xfns.c 213 KB
Newer Older
Jim Blandy's avatar
Jim Blandy committed
1
/* Functions for the X window system.
Glenn Morris's avatar
Glenn Morris committed
2

Paul Eggert's avatar
Paul Eggert committed
3
Copyright (C) 1989, 1992-2015 Free Software Foundation, Inc.
Jim Blandy's avatar
Jim Blandy committed
4 5 6

This file is part of GNU Emacs.

7
GNU Emacs is free software: you can redistribute it and/or modify
Jim Blandy's avatar
Jim Blandy committed
8
it under the terms of the GNU General Public License as published by
9 10
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jim Blandy's avatar
Jim Blandy committed
11 12 13 14 15 16 17

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
18
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
Jim Blandy's avatar
Jim Blandy committed
19

Richard M. Stallman's avatar
Richard M. Stallman committed
20
#include <config.h>
21
#include <stdio.h>
22
#include <math.h>
23 24
#include <unistd.h>

Jim Blandy's avatar
Jim Blandy committed
25 26
#include "lisp.h"
#include "xterm.h"
Jim Blandy's avatar
Jim Blandy committed
27
#include "frame.h"
Jim Blandy's avatar
Jim Blandy committed
28 29 30
#include "window.h"
#include "buffer.h"
#include "dispextern.h"
Jim Blandy's avatar
Jim Blandy committed
31
#include "keyboard.h"
32
#include "blockinput.h"
Karl Heuer's avatar
Karl Heuer committed
33
#include "charset.h"
Kenichi Handa's avatar
Kenichi Handa committed
34
#include "coding.h"
35
#include "termhooks.h"
Kenichi Handa's avatar
Kenichi Handa committed
36 37
#include "font.h"

38 39
#include <sys/types.h>
#include <sys/stat.h>
Jim Blandy's avatar
Jim Blandy committed
40

41
#include "bitmaps/gray.xbm"
42 43
#include "xsettings.h"

44 45 46 47 48 49 50
#ifdef HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
#endif
#ifdef HAVE_XINERAMA
#include <X11/extensions/Xinerama.h>
#endif

Jan Djärv's avatar
Jan Djärv committed
51 52 53 54
#ifdef USE_GTK
#include "gtkutil.h"
#endif

55 56 57
#ifdef USE_X_TOOLKIT
#include <X11/Shell.h>

58
#ifndef USE_MOTIF
Chong Yidong's avatar
Chong Yidong committed
59 60
#ifdef HAVE_XAW3D
#include <X11/Xaw3d/Paned.h>
Chong Yidong's avatar
Chong Yidong committed
61
#include <X11/Xaw3d/Label.h>
Chong Yidong's avatar
Chong Yidong committed
62
#else /* !HAVE_XAW3D */
63 64
#include <X11/Xaw/Paned.h>
#include <X11/Xaw/Label.h>
Chong Yidong's avatar
Chong Yidong committed
65
#endif /* HAVE_XAW3D */
66
#endif /* USE_MOTIF */
67 68 69 70 71

#ifdef USG
#undef USG	/* ####KLUDGE for Solaris 2.2 and up */
#include <X11/Xos.h>
#define USG
72 73
#ifdef USG /* Pacify gcc -Wunused-macros.  */
#endif
74 75 76 77 78 79 80 81
#else
#include <X11/Xos.h>
#endif

#include "widget.h"

#include "../lwlib/lwlib.h"

82 83 84 85
#ifdef USE_MOTIF
#include <Xm/Xm.h>
#include <Xm/DialogS.h>
#include <Xm/FileSB.h>
Jan D's avatar
Jan D committed
86 87
#include <Xm/List.h>
#include <Xm/TextF.h>
88 89
#endif

90 91 92 93
#ifdef USE_LUCID
#include "../lwlib/xlwmenu.h"
#endif

Juanma Barranquero's avatar
Juanma Barranquero committed
94
#if !defined (NO_EDITRES)
95
#define HACK_EDITRES
96
extern void _XEditResCheckMessages (Widget, XtPointer, XEvent *, Boolean *);
Dan Nicolaescu's avatar
Dan Nicolaescu committed
97
#endif /* not defined NO_EDITRES */
98

99 100
/* Unique id counter for widgets created by the Lucid Widget Library.  */

101 102
extern LWLIB_ID widget_id_tick;

103 104 105 106
#ifdef USE_MOTIF

#endif /* USE_MOTIF */

107 108
#endif /* USE_X_TOOLKIT */

Kenichi Handa's avatar
Kenichi Handa committed
109 110 111 112
#ifdef USE_GTK

#endif /* USE_GTK */

Richard M. Stallman's avatar
Richard M. Stallman committed
113 114
#define MAXREQUEST(dpy) (XMaxRequestSize (dpy))

115
static ptrdiff_t image_cache_refcount;
116
#ifdef GLYPH_DEBUG
117
static int dpyinfo_refcount;
118 119
#endif

120 121
static struct x_display_info *x_display_info_for_name (Lisp_Object);

122
/* Let the user specify an X display with a Lisp object.
123
   OBJECT may be nil, a frame or a terminal object.
124 125 126
   nil stands for the selected frame--or, if that is not an X frame,
   the first X display on the list.  */

127
struct x_display_info *
128
check_x_display_info (Lisp_Object object)
129
{
130
  struct x_display_info *dpyinfo = NULL;
131

132
  if (NILP (object))
133
    {
Gerd Moellmann's avatar
Gerd Moellmann committed
134
      struct frame *sf = XFRAME (selected_frame);
135

Gerd Moellmann's avatar
Gerd Moellmann committed
136
      if (FRAME_X_P (sf) && FRAME_LIVE_P (sf))
137
	dpyinfo = FRAME_DISPLAY_INFO (sf);
138
      else if (x_display_list != 0)
139
	dpyinfo = x_display_list;
140 141 142
      else
	error ("X windows are not in use or not initialized");
    }
143
  else if (TERMINALP (object))
144
    {
145
      struct terminal *t = decode_live_terminal (object);
146

147
      if (t->type != output_x_window)
148
        error ("Terminal %d is not an X display", t->id);
149

150
      dpyinfo = t->display_info.x;
151
    }
152 153
  else if (STRINGP (object))
    dpyinfo = x_display_info_for_name (object);
154 155
  else
    {
Dmitry Antipov's avatar
Dmitry Antipov committed
156
      struct frame *f = decode_window_system_frame (object);
157
      dpyinfo = FRAME_DISPLAY_INFO (f);
158
    }
159 160

  return dpyinfo;
161
}
162

163 164 165 166 167 168 169
/* Return the screen positions and offsets of frame F.
   Store the offsets between FRAME_OUTER_WINDOW and the containing
   window manager window into LEFT_OFFSET_X, RIGHT_OFFSET_X,
   TOP_OFFSET_Y and BOTTOM_OFFSET_Y.
   Store the offsets between FRAME_X_WINDOW and the containing
   window manager window into X_PIXELS_DIFF and Y_PIXELS_DIFF.
   Store the screen positions of frame F into XPTR and YPTR.
170 171 172
   These are the positions of the containing window manager window,
   not Emacs's own window.  */
void
173 174 175 176 177 178 179 180
x_real_pos_and_offsets (struct frame *f,
                        int *left_offset_x,
                        int *right_offset_x,
                        int *top_offset_y,
                        int *bottom_offset_y,
                        int *x_pixels_diff,
                        int *y_pixels_diff,
                        int *xptr,
181 182
                        int *yptr,
                        int *outer_border)
183
{
184
  int win_x = 0, win_y = 0, outer_x = 0, outer_y = 0;
185
  int real_x = 0, real_y = 0;
Paul Eggert's avatar
Paul Eggert committed
186
  bool had_errors = false;
187
  Window win = f->output_data.x->parent_desc;
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
  struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
  long max_len = 400;
  Atom target_type = XA_CARDINAL;
  unsigned int ow = 0, oh = 0;
  unsigned int fw = 0, fh = 0;
  unsigned int bw = 0;
  /* We resort to XCB if possible because there are several X calls
     here which require responses from the server but do not have data
     dependencies between them.  Using XCB lets us pipeline requests,
     whereas with Xlib we must wait for each answer before sending the
     next request.

     For a non-local display, the round-trip time could be a few tens
     of milliseconds, depending on the network distance.  It doesn't
     take a lot of those to add up to a noticeable hesitation in
     responding to user actions.  */
#ifdef USE_XCB
  xcb_connection_t *xcb_conn = dpyinfo->xcb_connection;
  xcb_get_property_cookie_t prop_cookie;
  xcb_get_geometry_cookie_t outer_geom_cookie;
  bool sent_requests = false;
#else
210 211
  Atom actual_type;
  unsigned long actual_size, bytes_remaining;
212
  int rc, actual_format;
213 214
  Display *dpy = FRAME_X_DISPLAY (f);
  unsigned char *tmp_data = NULL;
215
#endif
216

217 218 219 220 221 222 223 224 225 226
  if (x_pixels_diff) *x_pixels_diff = 0;
  if (y_pixels_diff) *y_pixels_diff = 0;
  if (left_offset_x) *left_offset_x = 0;
  if (top_offset_y) *top_offset_y = 0;
  if (right_offset_x) *right_offset_x = 0;
  if (bottom_offset_y) *bottom_offset_y = 0;
  if (xptr) *xptr = 0;
  if (yptr) *yptr = 0;
  if (outer_border) *outer_border = 0;

227
  if (win == dpyinfo->root_window)
228 229
    win = FRAME_OUTER_WINDOW (f);

230 231 232 233 234 235 236
  block_input ();

#ifndef USE_XCB
  /* If we're using XCB, all errors are checked for on each call.  */
  x_catch_errors (dpy);
#endif

237 238 239 240 241
  /* This loop traverses up the containment tree until we hit the root
     window.  Window managers may intersect many windows between our window
     and the root window.  The window we find just before the root window
     should be the outer WM window. */
  for (;;)
242
    {
243
      Window wm_window, rootw;
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

#ifdef USE_XCB
      xcb_query_tree_cookie_t query_tree_cookie;
      xcb_query_tree_reply_t *query_tree;

      query_tree_cookie = xcb_query_tree (xcb_conn, win);
      query_tree = xcb_query_tree_reply (xcb_conn, query_tree_cookie, NULL);
      if (query_tree == NULL)
	had_errors = true;
      else
	{
	  wm_window = query_tree->parent;
	  rootw = query_tree->root;
	  free (query_tree);
	}
#else
260 261
      Window *tmp_children;
      unsigned int tmp_nchildren;
262
      int success;
263

264
      success = XQueryTree (dpy, win, &rootw,
265
			    &wm_window, &tmp_children, &tmp_nchildren);
266

267
      had_errors = x_had_errors_p (dpy);
268

269 270 271 272
      /* Don't free tmp_children if XQueryTree failed.  */
      if (! success)
	break;

273
      XFree (tmp_children);
274
#endif
275

276 277
      if (wm_window == rootw || had_errors)
        break;
278

279 280
      win = wm_window;
    }
281

282 283
  if (! had_errors)
    {
284 285 286 287 288 289 290 291
#ifdef USE_XCB
      xcb_get_geometry_cookie_t geom_cookie;
      xcb_translate_coordinates_cookie_t trans_cookie;
      xcb_translate_coordinates_cookie_t outer_trans_cookie;

      xcb_translate_coordinates_reply_t *trans;
      xcb_get_geometry_reply_t *geom;
#else
292
      Window child, rootw;
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
      unsigned int ign;
#endif

#ifdef USE_XCB
      /* Fire off the requests that don't have data dependencies.

         Once we've done this, we must collect the results for each
         one before returning, even if other errors are detected,
         making the other responses moot.  */
      geom_cookie = xcb_get_geometry (xcb_conn, win);

      trans_cookie =
        xcb_translate_coordinates (xcb_conn,
                                   /* From-window, to-window.  */
                                   FRAME_DISPLAY_INFO (f)->root_window,
                                   FRAME_X_WINDOW (f),

                                   /* From-position.  */
                                   0, 0);
      if (FRAME_X_WINDOW (f) != FRAME_OUTER_WINDOW (f))
        outer_trans_cookie =
          xcb_translate_coordinates (xcb_conn,
                                     /* From-window, to-window.  */
                                     FRAME_DISPLAY_INFO (f)->root_window,
                                     FRAME_OUTER_WINDOW (f),

                                     /* From-position.  */
                                     0, 0);
      if (right_offset_x || bottom_offset_y)
	outer_geom_cookie = xcb_get_geometry (xcb_conn,
					      FRAME_OUTER_WINDOW (f));

      if (dpyinfo->root_window == f->output_data.x->parent_desc)
	/* Try _NET_FRAME_EXTENTS if our parent is the root window.  */
	prop_cookie = xcb_get_property (xcb_conn, 0, win,
					dpyinfo->Xatom_net_frame_extents,
					target_type, 0, max_len);

      sent_requests = true;
#endif
333

334
      /* Get the real coordinates for the WM window upper left corner */
335 336 337 338 339 340 341 342 343 344 345 346 347 348
#ifdef USE_XCB
      geom = xcb_get_geometry_reply (xcb_conn, geom_cookie, NULL);
      if (geom)
	{
	  real_x = geom->x;
	  real_y = geom->y;
	  ow = geom->width;
	  oh = geom->height;
	  bw = geom->border_width;
	  free (geom);
	}
      else
	had_errors = true;
#else
349
      XGetGeometry (dpy, win,
350 351
		    &rootw, &real_x, &real_y, &ow, &oh, &bw, &ign);
#endif
352 353 354 355 356 357 358 359 360 361

      /* Translate real coordinates to coordinates relative to our
         window.  For our window, the upper left corner is 0, 0.
         Since the upper left corner of the WM window is outside
         our window, win_x and win_y will be negative:

         ------------------          ---> x
         |      title                |
         | -----------------         v y
         | |  our window
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378

         Since we don't care about the child window corresponding to
         the actual coordinates, we can send zero to get the offsets
         and compute the resulting coordinates below.  This reduces
         the data dependencies between calls and lets us pipeline the
         requests better in the XCB case.  */
#ifdef USE_XCB
      trans = xcb_translate_coordinates_reply (xcb_conn, trans_cookie, NULL);
      if (trans)
	{
	  win_x = trans->dst_x;
	  win_y = trans->dst_y;
	  free (trans);
	}
      else
	had_errors = true;
#else
379
      XTranslateCoordinates (dpy,
380

381
			     /* From-window, to-window.  */
382
			     FRAME_DISPLAY_INFO (f)->root_window,
383
                             FRAME_X_WINDOW (f),
384

385
			     /* From-position, to-position.  */
386
                             0, 0, &win_x, &win_y,
387

388 389
			     /* Child of win.  */
			     &child);
390
#endif
391

392 393 394
      win_x += real_x;
      win_y += real_y;

395
      if (FRAME_X_WINDOW (f) == FRAME_OUTER_WINDOW (f))
396
	{
397 398
          outer_x = win_x;
          outer_y = win_y;
399
	}
400 401
      else
        {
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
#ifdef USE_XCB
          xcb_translate_coordinates_reply_t *outer_trans;

          outer_trans = xcb_translate_coordinates_reply (xcb_conn,
                                                         outer_trans_cookie,
                                                         NULL);
          if (outer_trans)
            {
              outer_x = outer_trans->dst_x;
              outer_y = outer_trans->dst_y;
              free (outer_trans);
            }
          else
	    had_errors = true;
#else
417
          XTranslateCoordinates (dpy,
418

419
                                 /* From-window, to-window.  */
420
                                 FRAME_DISPLAY_INFO (f)->root_window,
421
                                 FRAME_OUTER_WINDOW (f),
422

423
                                 /* From-position, to-position.  */
424
                                 0, 0, &outer_x, &outer_y,
425

426 427
                                 /* Child of win.  */
                                 &child);
428
#endif
429 430 431

	  outer_x += real_x;
	  outer_y += real_y;
432
	}
433

434
#ifndef USE_XCB
435
      had_errors = x_had_errors_p (dpy);
436
#endif
437
    }
438

439
  if (dpyinfo->root_window == f->output_data.x->parent_desc)
440 441
    {
      /* Try _NET_FRAME_EXTENTS if our parent is the root window.  */
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
#ifdef USE_XCB
      /* Make sure we didn't get an X error early and skip sending the
         request.  */
      if (sent_requests)
        {
          xcb_get_property_reply_t *prop;

          prop = xcb_get_property_reply (xcb_conn, prop_cookie, NULL);
          if (prop)
            {
              if (prop->type == target_type
                  && xcb_get_property_value_length (prop) == 4
                  && prop->format == 32)
                {
                  long *fe = xcb_get_property_value (prop);

                  outer_x = -fe[0];
                  outer_y = -fe[2];
                  real_x -= fe[0];
                  real_y -= fe[2];
                }
              free (prop);
            }
          /* Xlib version doesn't set had_errors here.  Intentional or bug?  */
        }
#else
468 469 470 471 472 473 474 475
      rc = XGetWindowProperty (dpy, win, dpyinfo->Xatom_net_frame_extents,
                               0, max_len, False, target_type,
                               &actual_type, &actual_format, &actual_size,
                               &bytes_remaining, &tmp_data);

      if (rc == Success && actual_type == target_type && !x_had_errors_p (dpy)
          && actual_size == 4 && actual_format == 32)
        {
476
          long *fe = (long *)tmp_data;
477 478 479 480 481 482 483

          outer_x = -fe[0];
          outer_y = -fe[2];
          real_x -= fe[0];
          real_y -= fe[2];
        }

Ken Raeburn's avatar
Ken Raeburn committed
484
      if (tmp_data) XFree (tmp_data);
485
#endif
Ken Raeburn's avatar
Ken Raeburn committed
486
    }
487

488 489
  if (right_offset_x || bottom_offset_y)
    {
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
#ifdef USE_XCB
      /* Make sure we didn't get an X error early and skip sending the
         request.  */
      if (sent_requests)
        {
          xcb_get_geometry_reply_t *outer_geom;

          outer_geom = xcb_get_geometry_reply (xcb_conn, outer_geom_cookie,
                                               NULL);
          if (outer_geom)
            {
              fw = outer_geom->width;
              fh = outer_geom->height;
              free (outer_geom);
            }
          else
	    had_errors = true;
        }
#else
509 510 511 512 513 514
      int xy_ign;
      unsigned int ign;
      Window rootw;

      XGetGeometry (dpy, FRAME_OUTER_WINDOW (f),
		    &rootw, &xy_ign, &xy_ign, &fw, &fh, &ign, &ign);
515
#endif
516 517
    }

518
#ifndef USE_XCB
519
  x_uncatch_errors ();
520
#endif
521

522
  unblock_input ();
523 524

  if (had_errors) return;
525

526 527 528 529
  if (x_pixels_diff) *x_pixels_diff = -win_x;
  if (y_pixels_diff) *y_pixels_diff = -win_y;

  if (left_offset_x) *left_offset_x = -outer_x;
530
  if (top_offset_y) *top_offset_y = -outer_y;
531

532 533
  if (xptr) *xptr = real_x;
  if (yptr) *yptr = real_y;
534

535 536
  if (outer_border) *outer_border = bw;

537 538
  if (right_offset_x) *right_offset_x = ow - fw + outer_x;
  if (bottom_offset_y) *bottom_offset_y = oh - fh + outer_y;
539 540
}

541 542 543
/* Store the screen positions of frame F into XPTR and YPTR.
   These are the positions of the containing window manager window,
   not Emacs's own window.  */
544

545 546 547
void
x_real_positions (struct frame *f, int *xptr, int *yptr)
{
548 549
  x_real_pos_and_offsets (f, NULL, NULL, NULL, NULL, NULL, NULL, xptr, yptr,
                          NULL);
550 551
}

552

553
/* Get the mouse position in frame relative coordinates.  */
554

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583
void
x_relative_mouse_position (struct frame *f, int *x, int *y)
{
  Window root, dummy_window;
  int dummy;

  eassert (FRAME_X_P (f));

  block_input ();

  XQueryPointer (FRAME_X_DISPLAY (f),
                 DefaultRootWindow (FRAME_X_DISPLAY (f)),

                 /* The root window which contains the pointer.  */
                 &root,

                 /* Window pointer is on, not used  */
                 &dummy_window,

                 /* The position on that root window.  */
                 x, y,

                 /* x/y in dummy_window coordinates, not used.  */
                 &dummy, &dummy,

                 /* Modifier keys and pointer buttons, about which
                    we don't care.  */
                 (unsigned int *) &dummy);

584
  XTranslateCoordinates (FRAME_X_DISPLAY (f),
585

586 587 588
                         /* From-window, to-window.  */
                         FRAME_DISPLAY_INFO (f)->root_window,
                         FRAME_X_WINDOW (f),
589

590 591 592 593 594 595 596
                         /* From-position, to-position.  */
                         *x, *y, x, y,

                         /* Child of win.  */
                         &dummy_window);

  unblock_input ();
597
}
598 599 600 601

/* Gamma-correct COLOR on frame F.  */

void
602
gamma_correct (struct frame *f, XColor *color)
603 604 605 606 607 608 609 610 611 612
{
  if (f->gamma)
    {
      color->red = pow (color->red / 65535.0, f->gamma) * 65535.0 + 0.5;
      color->green = pow (color->green / 65535.0, f->gamma) * 65535.0 + 0.5;
      color->blue = pow (color->blue / 65535.0, f->gamma) * 65535.0 + 0.5;
    }
}


613
/* Decide if color named COLOR_NAME is valid for use on frame F.  If
614 615
   so, return the RGB values in COLOR.  If ALLOC_P,
   allocate the color.  Value is false if COLOR_NAME is invalid, or
616
   no color could be allocated.  */
617

618
bool
619
x_defined_color (struct frame *f, const char *color_name,
620
		 XColor *color, bool alloc_p)
Jim Blandy's avatar
Jim Blandy committed
621
{
Paul Eggert's avatar
Paul Eggert committed
622
  bool success_p = false;
623
  Colormap cmap = FRAME_X_COLORMAP (f);
Jim Blandy's avatar
Jim Blandy committed
624

625
  block_input ();
626 627 628 629
#ifdef USE_GTK
  success_p = xg_check_special_colors (f, color_name, color);
#endif
  if (!success_p)
630
    success_p = x_parse_color (f, color_name, color) != 0;
631 632
  if (success_p && alloc_p)
    success_p = x_alloc_nearest_color (f, cmap, color);
633
  unblock_input ();
Jim Blandy's avatar
Jim Blandy committed
634

635
  return success_p;
Jim Blandy's avatar
Jim Blandy committed
636 637
}

638 639 640 641

/* Return the pixel color value for color COLOR_NAME on frame F.  If F
   is a monochrome frame, return MONO_COLOR regardless of what ARG says.
   Signal an error if color can't be allocated.  */
Jim Blandy's avatar
Jim Blandy committed
642

643
static int
Dmitry Antipov's avatar
Dmitry Antipov committed
644
x_decode_color (struct frame *f, Lisp_Object color_name, int mono_color)
Jim Blandy's avatar
Jim Blandy committed
645
{
646
  XColor cdef;
Jim Blandy's avatar
Jim Blandy committed
647

648
  CHECK_STRING (color_name);
Jim Blandy's avatar
Jim Blandy committed
649

Paul Eggert's avatar
Paul Eggert committed
650 651 652
#if false /* Don't do this.  It's wrong when we're not using the default
	     colormap, it makes freeing difficult, and it's probably not
	     an important optimization.  */
653
  if (strcmp (SDATA (color_name), "black") == 0)
654
    return BLACK_PIX_DEFAULT (f);
655
  else if (strcmp (SDATA (color_name), "white") == 0)
656
    return WHITE_PIX_DEFAULT (f);
657
#endif
Jim Blandy's avatar
Jim Blandy committed
658

659
  /* Return MONO_COLOR for monochrome frames.  */
660
  if (FRAME_DISPLAY_INFO (f)->n_planes == 1)
661
    return mono_color;
Jim Blandy's avatar
Jim Blandy committed
662

663
  /* x_defined_color is responsible for coping with failures
664
     by looking for a near-miss.  */
Paul Eggert's avatar
Paul Eggert committed
665
  if (x_defined_color (f, SSDATA (color_name), &cdef, true))
666 667
    return cdef.pixel;

668
  signal_error ("Undefined color", color_name);
Jim Blandy's avatar
Jim Blandy committed
669
}
670 671


Jim Blandy's avatar
Jim Blandy committed
672

Gerd Moellmann's avatar
Gerd Moellmann committed
673 674 675 676 677
/* Change the `wait-for-wm' frame parameter of frame F.  OLD_VALUE is
   the previous value of that parameter, NEW_VALUE is the new value.
   See also the comment of wait_for_wm in struct x_output.  */

static void
678
x_set_wait_for_wm (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
Gerd Moellmann's avatar
Gerd Moellmann committed
679 680 681 682
{
  f->output_data.x->wait_for_wm = !NILP (new_value);
}

683 684 685 686 687
static void
x_set_tool_bar_position (struct frame *f,
                         Lisp_Object new_value,
                         Lisp_Object old_value)
{
688
  Lisp_Object choice = list4 (Qleft, Qright, Qtop, Qbottom);
689

690 691
  if (!NILP (Fmemq (new_value, choice)))
    {
692
#ifdef USE_GTK
693 694 695 696 697 698 699 700
      if (!EQ (new_value, old_value))
	{
	  xg_change_toolbar_position (f, new_value);
	  fset_tool_bar_position (f, new_value);
	}
#else
      if (!EQ (new_value, Qtop))
	error ("The only supported tool bar position is top");
701
#endif
702 703 704
    }
  else
    wrong_choice (choice, new_value);
705 706
}

707 708
#ifdef USE_GTK

709 710
/* Set icon from FILE for frame F.  By using GTK functions the icon
   may be any format that GdkPixbuf knows about, i.e. not just bitmaps.  */
711

Paul Eggert's avatar
Paul Eggert committed
712
bool
Dmitry Antipov's avatar
Dmitry Antipov committed
713
xg_set_icon (struct frame *f, Lisp_Object file)
714
{
Paul Eggert's avatar
Paul Eggert committed
715
  bool result = false;
716
  Lisp_Object found;
717

718 719 720 721 722 723
  found = x_find_image_file (file);

  if (! NILP (found))
    {
      GdkPixbuf *pixbuf;
      GError *err = NULL;
724
      char *filename = SSDATA (ENCODE_FILE (found));
725
      block_input ();
726 727 728 729 730 731 732 733 734

      pixbuf = gdk_pixbuf_new_from_file (filename, &err);

      if (pixbuf)
	{
	  gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
			       pixbuf);
	  g_object_unref (pixbuf);

Paul Eggert's avatar
Paul Eggert committed
735
	  result = true;
736 737 738 739
	}
      else
	g_error_free (err);

740
      unblock_input ();
741 742 743
    }

  return result;
744
}
745

Paul Eggert's avatar
Paul Eggert committed
746
bool
Dmitry Antipov's avatar
Dmitry Antipov committed
747
xg_set_icon_from_xpm_data (struct frame *f, const char **data)
748
{
749
  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_xpm_data (data);
750 751

  if (!pixbuf)
Paul Eggert's avatar
Paul Eggert committed
752
    return false;
753

754
  gtk_window_set_icon (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)), pixbuf);
755
  g_object_unref (pixbuf);
Paul Eggert's avatar
Paul Eggert committed
756
  return true;
757
}
758 759
#endif /* USE_GTK */

Gerd Moellmann's avatar
Gerd Moellmann committed
760

Jim Blandy's avatar
Jim Blandy committed
761
/* Functions called only from `x_set_frame_param'
Jim Blandy's avatar
Jim Blandy committed
762 763
   to set individual parameters.

764
   If FRAME_X_WINDOW (f) is 0,
Jim Blandy's avatar
Jim Blandy committed
765
   the frame is being created and its X-window does not exist yet.
Jim Blandy's avatar
Jim Blandy committed
766 767 768
   In that case, just record the parameter's new value
   in the standard place; do not attempt to change the window.  */

769
static void
770
x_set_foreground_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
Jim Blandy's avatar
Jim Blandy committed
771
{
772 773
  struct x_output *x = f->output_data.x;
  unsigned long fg, old_fg;
774

775
  fg = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
Karoly Lorentey's avatar
Karoly Lorentey committed
776 777
  old_fg = FRAME_FOREGROUND_PIXEL (f);
  FRAME_FOREGROUND_PIXEL (f) = fg;
778

779
  if (FRAME_X_WINDOW (f) != 0)
Jim Blandy's avatar
Jim Blandy committed
780
    {
781
      Display *dpy = FRAME_X_DISPLAY (f);
782

783
      block_input ();
784 785
      XSetForeground (dpy, x->normal_gc, fg);
      XSetBackground (dpy, x->reverse_gc, fg);
786

787 788 789 790 791 792
      if (x->cursor_pixel == old_fg)
	{
	  unload_color (f, x->cursor_pixel);
	  x->cursor_pixel = x_copy_color (f, fg);
	  XSetBackground (dpy, x->cursor_gc, x->cursor_pixel);
	}
793

794
      unblock_input ();
795

796
      update_face_from_frame_parameter (f, Qforeground_color, arg);
797

798
      if (FRAME_VISIBLE_P (f))
Jim Blandy's avatar
Jim Blandy committed
799
        redraw_frame (f);
Jim Blandy's avatar
Jim Blandy committed
800
    }
801

802
  unload_color (f, old_fg);
Jim Blandy's avatar
Jim Blandy committed
803 804
}

805
static void
806
x_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
Jim Blandy's avatar
Jim Blandy committed
807
{
808 809
  struct x_output *x = f->output_data.x;
  unsigned long bg;
Jim Blandy's avatar
Jim Blandy committed
810

811
  bg = x_decode_color (f, arg, WHITE_PIX_DEFAULT (f));
Karoly Lorentey's avatar
Karoly Lorentey committed
812 813
  unload_color (f, FRAME_BACKGROUND_PIXEL (f));
  FRAME_BACKGROUND_PIXEL (f) = bg;
814

815
  if (FRAME_X_WINDOW (f) != 0)
Jim Blandy's avatar
Jim Blandy committed
816
    {
817
      Display *dpy = FRAME_X_DISPLAY (f);
818

819
      block_input ();
820 821 822 823 824
      XSetBackground (dpy, x->normal_gc, bg);
      XSetForeground (dpy, x->reverse_gc, bg);
      XSetWindowBackground (dpy, FRAME_X_WINDOW (f), bg);
      XSetForeground (dpy, x->cursor_gc, bg);

Jan Djärv's avatar
Jan Djärv committed
825 826 827 828
#ifdef USE_GTK
      xg_set_background_color (f, bg);
#endif

829 830 831 832 833 834 835 836
#ifndef USE_TOOLKIT_SCROLL_BARS /* Turns out to be annoying with
				   toolkit scroll bars.  */
      {
	Lisp_Object bar;
	for (bar = FRAME_SCROLL_BARS (f);
	     !NILP (bar);
	     bar = XSCROLL_BAR (bar)->next)
	  {
837
	    Window window = XSCROLL_BAR (bar)->x_window;
838 839 840 841
	    XSetWindowBackground (dpy, window, bg);
	  }
      }
#endif /* USE_TOOLKIT_SCROLL_BARS */
Jim Blandy's avatar
Jim Blandy committed
842

843
      unblock_input ();
844
      update_face_from_frame_parameter (f, Qbackground_color, arg);
845

846
      if (FRAME_VISIBLE_P (f))
Jim Blandy's avatar
Jim Blandy committed
847
        redraw_frame (f);
Jim Blandy's avatar
Jim Blandy committed
848 849 850
    }
}

851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
/* This array must stay in sync with the mouse_cursor_types array below!  */
enum mouse_cursor {
  mouse_cursor_text,
  mouse_cursor_nontext,
  mouse_cursor_hourglass,
  mouse_cursor_mode,
  mouse_cursor_hand,
  mouse_cursor_horizontal_drag,
  mouse_cursor_vertical_drag,
  mouse_cursor_max
};

struct mouse_cursor_types {
  /* Printable name for error messages (optional).  */
  const char *name;

  /* Lisp variable controlling the cursor shape.  */
  /* FIXME: A couple of these variables are defined in the C code but
     are not actually accessible from Lisp.  They should probably be
     made accessible or removed.  */
  Lisp_Object *shape_var_ptr;

  /* The default shape.  */
  int default_shape;
};

/* This array must stay in sync with enum mouse_cursor above!  */
static const struct mouse_cursor_types mouse_cursor_types[] = {
  { "text",      &Vx_pointer_shape,                XC_xterm             },
  { "nontext",   &Vx_nontext_pointer_shape,        XC_left_ptr          },
  { "hourglass", &Vx_hourglass_pointer_shape,      XC_watch             },
  { "modeline",  &Vx_mode_pointer_shape,           XC_xterm             },
  { NULL,        &Vx_sensitive_text_pointer_shape, XC_hand2             },
  { NULL,        &Vx_window_horizontal_drag_shape, XC_sb_h_double_arrow },
  { NULL,        &Vx_window_vertical_drag_shape,   XC_sb_v_double_arrow },
};

struct mouse_cursor_data {
  /* Last index for which XCreateFontCursor has been called, and thus
     the last index for which x_request_serial[] is valid.  */
  int last_cursor_create_request;

  /* Last index for which an X error event was received in response to
     attempting to create the cursor.  */
  int error_cursor;

  /* Cursor numbers chosen.  */
  unsigned int cursor_num[mouse_cursor_max];

  /* Allocated Cursor values, or zero for failed attempts.  */
  Cursor cursor[mouse_cursor_max];

  /* X serial numbers for the first request sent by XCreateFontCursor.
     Note that there may be more than one request sent.  */
  unsigned long x_request_serial[mouse_cursor_max];

  /* If an error has been received, a pointer to where the current
     error-message text is stored.  */
  char *error_string;
};

static void
x_set_mouse_color_handler (Display *dpy, XErrorEvent *event,
			   char *error_string, void *data)
{
  struct mouse_cursor_data *cursor_data = data;
  int i;

  cursor_data->error_cursor = -1;
  cursor_data->error_string = error_string;
  for (i = 0; i < cursor_data->last_cursor_create_request; i++)
    {
      if (event->serial >= cursor_data->x_request_serial[i])
	cursor_data->error_cursor = i;
    }
  if (cursor_data->error_cursor >= 0)
    /* If we failed to allocate it, don't try to free it.  */
    cursor_data->cursor[cursor_data->error_cursor] = 0;
}

931
static void
932
x_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
Jim Blandy's avatar
Jim Blandy committed
933
{
934 935
  struct x_output *x = f->output_data.x;
  Display *dpy = FRAME_X_DISPLAY (f);
936
  struct mouse_cursor_data cursor_data = { -1, -1 };
937
  unsigned long pixel = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
Karoly Lorentey's avatar
Karoly Lorentey committed
938
  unsigned long mask_color = FRAME_BACKGROUND_PIXEL (f);
939
  int i;
940

941
  /* Don't let pointers be invisible.  */
942
  if (mask_color == pixel)
943 944
    {
      x_free_colors (f, &pixel, 1);
Karoly Lorentey's avatar
Karoly Lorentey committed
945
      pixel = x_copy_color (f, FRAME_FOREGROUND_PIXEL (f));
946
    }
947

948 949
  unload_color (f, x->mouse_pixel);
  x->mouse_pixel = pixel;
Jim Blandy's avatar
Jim Blandy committed
950

951
  for (i = 0; i < mouse_cursor_max; i++)
952
    {
953 954 955 956 957 958 959 960
      Lisp_Object shape_var = *mouse_cursor_types[i].shape_var_ptr;
      if (!NILP (shape_var))
	{
	  CHECK_TYPE_RANGED_INTEGER (unsigned, shape_var);
	  cursor_data.cursor_num[i] = XINT (shape_var);
	}
      else
	cursor_data.cursor_num[i] = mouse_cursor_types[i].default_shape;
961
    }
962

963
  block_input ();
964

965 966
  /* It's not okay to crash if the user selects a screwy cursor.  */
  x_catch_errors_with_handler (dpy, x_set_mouse_color_handler, &cursor_data);
Jim Blandy's avatar
Jim Blandy committed
967

968
  for (i = 0; i < mouse_cursor_max; i++)
969
    {
970 971 972 973
      cursor_data.x_request_serial[i] = XNextRequest (dpy);
      cursor_data.last_cursor_create_request = i;
      cursor_data.cursor[i] = XCreateFontCursor (dpy,
						 cursor_data.cursor_num[i]);
974 975
    }

976 977 978
  /* Now sync up and process all received errors from cursor
     creation.  */
  if (x_had_errors_p (dpy))
979
    {
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
      const char *bad_cursor_name = NULL;
      /* Bounded by X_ERROR_MESSAGE_SIZE in xterm.c.  */
      size_t message_length = strlen (cursor_data.error_string);
      char *xmessage = alloca (1 + message_length);
      memcpy (xmessage, cursor_data.error_string, message_length);

      x_uncatch_errors ();

      /* Free any successfully created cursors.  */
      for (i = 0; i < mouse_cursor_max; i++)
	if (cursor_data.cursor[i] != 0)
	  XFreeCursor (dpy, cursor_data.cursor[i]);

      /* This should only be able to fail if the server's serial
	 number tracking is broken.  */
      if (cursor_data.error_cursor >= 0)
	bad_cursor_name = mouse_cursor_types[cursor_data.error_cursor].name;
      if (bad_cursor_name)
	error ("bad %s pointer cursor: %s", bad_cursor_name, xmessage);
      else
	error ("can't set cursor shape: %s", xmessage);
1001 1002
    }

1003
  x_uncatch_errors_after_check ();
1004

Jim Blandy's avatar
Jim Blandy committed
1005
  {
1006 1007 1008 1009 1010 1011 1012 1013
    XColor colors[2]; /* 0=foreground, 1=background */

    colors[0].pixel = x->mouse_pixel;
    colors[1].pixel = mask_color;
    x_query_colors (f, colors, 2);

    for (i = 0; i < mouse_cursor_max; i++)
      XRecolorCursor (dpy, cursor_data.cursor[i], &colors[0], &colors[1]);
Jim Blandy's avatar
Jim Blandy committed
1014 1015
  }

1016
  if (FRAME_X_WINDOW (f) != 0)
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
    {
      f->output_data.x->current_cursor = cursor_data.cursor[mouse_cursor_text];
      XDefineCursor (dpy, FRAME_X_WINDOW (f),
		     f->output_data.x->current_cursor);
    }

#define INSTALL_CURSOR(FIELD, SHORT_INDEX)				\
  eassert (x->FIELD != cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]); \
  if (x->FIELD != 0)							\
    XFreeCursor (dpy, x->FIELD);					\
  x->FIELD = cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX];

  INSTALL_CURSOR (text_cursor, text);
  INSTALL_CURSOR (nontext_cursor, nontext);
  INSTALL_CURSOR (hourglass_cursor, hourglass);
  INSTALL_CURSOR (modeline_cursor, mode);
  INSTALL_CURSOR (hand_cursor, hand);
  INSTALL_CURSOR (horizontal_drag_cursor, horizontal_drag);
  INSTALL_CURSOR (vertical_drag_cursor, vertical_drag);

#undef INSTALL_CURSOR
1038

1039
  XFlush (dpy);
1040
  unblock_input ();
1041 1042

  update_face_from_frame_parameter (f, Qmouse_color, arg);
Jim Blandy's avatar
Jim Blandy committed
1043 1044
}

1045
static void
1046
x_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
Jim Blandy's avatar
Jim Blandy committed
1047
{
1048
  unsigned long fore_pixel, pixel;
Paul Eggert's avatar
Paul Eggert committed
1049
  bool fore_pixel_allocated_p = false, pixel_allocated_p = false;
1050
  struct x_output *x = f->output_data.x;
Jim Blandy's avatar
Jim Blandy committed
1051

1052 1053 1054 1055
  if (!NILP (Vx_cursor_fore_pixel))
    {
      fore_pixel = x_decode_color (f, Vx_cursor_fore_pixel,
				   WHITE_PIX_DEFAULT (f));
Paul Eggert's avatar
Paul Eggert committed
1056
      fore_pixel_allocated_p = true;
1057
    }
Jim Blandy's avatar
Jim Blandy committed
1058
  else
Karoly Lorentey's avatar
Karoly Lorentey committed
1059
    fore_pixel = FRAME_BACKGROUND_PIXEL (f);
1060

1061
  pixel = x_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
Paul Eggert's avatar
Paul Eggert committed
1062
  pixel_allocated_p = true;
1063

1064
  /* Make sure that the cursor color differs from the background color.  */
Karoly Lorentey's avatar
Karoly Lorentey committed
1065
  if (pixel == FRAME_BACKGROUND_PIXEL (f))
Jim Blandy's avatar
Jim Blandy committed
1066
    {
1067 1068 1069
      if (pixel_allocated_p)
	{
	  x_free_colors (f, &pixel, 1);
Paul Eggert's avatar
Paul Eggert committed
1070
	  pixel_allocated_p = false;
1071
	}
1072

1073
      pixel = x->mouse_pixel;
1074
      if (pixel == fore_pixel)
1075 1076 1077 1078
	{
	  if (fore_pixel_allocated_p)
	    {
	      x_free_colors (f, &fore_pixel, 1);
Paul Eggert's avatar
Paul Eggert committed
1079
	      fore_pixel_allocated_p = false;
1080
	    }
Karoly Lorentey's avatar
Karoly Lorentey committed
1081
	  fore_pixel = FRAME_BACKGROUND_PIXEL (f);
1082
	}
Jim Blandy's avatar
Jim Blandy committed
1083
    }
1084

Gerd Moellmann's avatar