xfns.c 214 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-2016 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

Paul Eggert's avatar
Paul Eggert committed
276
      if (had_errors || wm_window == rootw)
277
        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
#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
453 454 455
                  && prop->format == 32
                  && (xcb_get_property_value_length (prop)
		      == 4 * sizeof (int32_t)))
456
                {
457
                  int32_t *fe = xcb_get_property_value (prop);
458 459 460 461 462 463 464 465 466 467 468

                  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
469 470 471 472 473 474 475 476
      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)
        {
477
          long *fe = (long *)tmp_data;
478 479 480 481 482 483 484

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

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

489 490
  if (right_offset_x || bottom_offset_y)
    {
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
#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
510 511 512 513 514 515
      int xy_ign;
      unsigned int ign;
      Window rootw;

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

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

523
  unblock_input ();
524 525

  if (had_errors) return;
526

527 528 529 530
  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;
531
  if (top_offset_y) *top_offset_y = -outer_y;
532

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

536 537
  if (outer_border) *outer_border = bw;

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

542 543 544
/* 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.  */
545

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

553

554
/* Get the mouse position in frame relative coordinates.  */
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 584
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);

585
  XTranslateCoordinates (FRAME_X_DISPLAY (f),
586

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

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

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

  unblock_input ();
598
}
599 600 601 602

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

void
603
gamma_correct (struct frame *f, XColor *color)
604 605 606 607 608 609 610 611 612 613
{
  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;
    }
}


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

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

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

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

639 640 641 642

/* 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
643

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

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

Paul Eggert's avatar
Paul Eggert committed
651 652 653
#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.  */
654
  if (strcmp (SDATA (color_name), "black") == 0)
655
    return BLACK_PIX_DEFAULT (f);
656
  else if (strcmp (SDATA (color_name), "white") == 0)
657
    return WHITE_PIX_DEFAULT (f);
658
#endif
Jim Blandy's avatar
Jim Blandy committed
659

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

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

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


Jim Blandy's avatar
Jim Blandy committed
673

Gerd Moellmann's avatar
Gerd Moellmann committed
674 675 676 677 678
/* 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
679
x_set_wait_for_wm (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
Gerd Moellmann's avatar
Gerd Moellmann committed
680 681 682 683
{
  f->output_data.x->wait_for_wm = !NILP (new_value);
}

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

691 692
  if (!NILP (Fmemq (new_value, choice)))
    {
693
#ifdef USE_GTK
694 695 696 697 698 699 700 701
      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");
702
#endif
703 704 705
    }
  else
    wrong_choice (choice, new_value);
706 707
}

708 709
#ifdef USE_GTK

710 711
/* 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.  */
712

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

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

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

      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
736
	  result = true;
737 738 739 740
	}
      else
	g_error_free (err);

741
      unblock_input ();
742 743 744
    }

  return result;
745
}
746

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

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

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

Gerd Moellmann's avatar
Gerd Moellmann committed
761

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

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

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

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

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

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

788 789 790 791 792 793
      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);
	}
794

795
      unblock_input ();
796

797
      update_face_from_frame_parameter (f, Qforeground_color, arg);
798

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

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

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

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

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

820
      block_input ();
821 822 823 824 825
      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
826 827 828 829
#ifdef USE_GTK
      xg_set_background_color (f, bg);
#endif

830 831 832 833 834 835 836 837
#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)
	  {
838
	    Window window = XSCROLL_BAR (bar)->x_window;
839 840 841 842
	    XSetWindowBackground (dpy, window, bg);
	  }
      }
#endif /* USE_TOOLKIT_SCROLL_BARS */
Jim Blandy's avatar
Jim Blandy committed
843

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

847
      if (FRAME_VISIBLE_P (f))
Jim Blandy's avatar
Jim Blandy committed
848
        redraw_frame (f);
Jim Blandy's avatar
Jim Blandy committed
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 931
/* 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;
}

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

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

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

952
  for (i = 0; i < mouse_cursor_max; i++)
953
    {
954 955 956 957 958 959 960 961
      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;
962
    }
963

964
  block_input ();
965

966 967
  /* 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
968

969
  for (i = 0; i < mouse_cursor_max; i++)
970
    {
971 972 973 974
      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]);
975 976
    }

977 978 979
  /* Now sync up and process all received errors from cursor
     creation.  */
  if (x_had_errors_p (dpy))
980
    {
981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
      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);
1002 1003
    }

1004
  x_uncatch_errors_after_check ();
1005

Jim Blandy's avatar
Jim Blandy committed
1006
  {
1007 1008 1009 1010 1011 1012 1013 1014
    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
1015 1016
  }

1017
  if (FRAME_X_WINDOW (f) != 0)
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
    {
      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
1039

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

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

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

1053 1054 1055 1056
  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
1057
      fore_pixel_allocated_p = true;
1058
    }
Jim Blandy's avatar
Jim Blandy committed
1059
  else
Karoly Lorentey's avatar
Karoly Lorentey committed
1060
    fore_pixel = FRAME_BACKGROUND_PIXEL (f);
1061

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

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

1074
      pixel = x->mouse_pixel;
1075
      if (pixel == fore_pixel)
1076 1077 1078 1079
	{
	  if (fore_pixel_allocated_p)
	    {
	      x_free_colors (f, &fore_pixel, 1);
Paul Eggert's avatar
Paul Eggert committed
1080
	      fore_pixel_allocated_p = false;
1081
	    }