Implement multiple display handling for GTK.
[bpt/emacs.git] / src / xfns.c
index fe9ca5a..cd50cbb 100644 (file)
@@ -125,6 +125,14 @@ static Lisp_Object Vmotif_version_string;
 
 #endif /* USE_X_TOOLKIT */
 
+#ifdef USE_GTK
+
+/* GTK+ version info */
+
+static Lisp_Object Vgtk_version_string;
+
+#endif /* USE_GTK */
+
 #ifdef HAVE_X11R4
 #define MAXREQUEST(dpy) (XMaxRequestSize (dpy))
 #else
@@ -300,7 +308,7 @@ x_window_to_frame (dpyinfo, wdesc)
 #ifdef USE_GTK
       if (f->output_data.x->edit_widget)
       {
-        GtkWidget *gwdesc = xg_win_to_widget (wdesc);
+        GtkWidget *gwdesc = xg_win_to_widget (dpyinfo->display, wdesc);
         struct x_output *x = f->output_data.x;
         if (gwdesc != 0 && gwdesc == x->edit_widget)
           return f;
@@ -344,7 +352,7 @@ x_any_window_to_frame (dpyinfo, wdesc)
          else if (x->widget)
            {
 #ifdef USE_GTK
-              GtkWidget *gwdesc = xg_win_to_widget (wdesc);
+              GtkWidget *gwdesc = xg_win_to_widget (dpyinfo->display, wdesc);
               if (gwdesc != 0
                   && (gwdesc == x->widget
                       || gwdesc == x->edit_widget
@@ -396,7 +404,7 @@ x_non_menubar_window_to_frame (dpyinfo, wdesc)
       else if (x->widget)
        {
 #ifdef USE_GTK
-          GtkWidget *gwdesc = xg_win_to_widget (wdesc);
+          GtkWidget *gwdesc = xg_win_to_widget (dpyinfo->display, wdesc);
           if (gwdesc != 0
               && (gwdesc == x->widget
                   || gwdesc == x->edit_widget
@@ -440,7 +448,7 @@ x_menubar_window_to_frame (dpyinfo, wdesc)
 #ifdef USE_GTK
       if (x->menubar_widget)
         {
-          GtkWidget *gwdesc = xg_win_to_widget (wdesc);
+          GtkWidget *gwdesc = xg_win_to_widget (dpyinfo->display, wdesc);
           int found = 0;
 
           BLOCK_INPUT;
@@ -486,7 +494,7 @@ x_top_window_to_frame (dpyinfo, wdesc)
        {
          /* This frame matches if the window is its topmost widget.  */
 #ifdef USE_GTK
-          GtkWidget *gwdesc = xg_win_to_widget (wdesc);
+          GtkWidget *gwdesc = xg_win_to_widget (dpyinfo->display, wdesc);
           if (gwdesc == x->widget)
             return f;
 #else
@@ -623,6 +631,7 @@ x_create_bitmap_from_data (f, bits, width, height)
 
   id = x_allocate_bitmap_record (f);
   dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+  dpyinfo->bitmaps[id - 1].have_mask = 0;
   dpyinfo->bitmaps[id - 1].file = NULL;
   dpyinfo->bitmaps[id - 1].refcount = 1;
   dpyinfo->bitmaps[id - 1].depth = 1;
@@ -674,6 +683,7 @@ x_create_bitmap_from_file (f, file)
 
   id = x_allocate_bitmap_record (f);
   dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+  dpyinfo->bitmaps[id - 1].have_mask = 0;
   dpyinfo->bitmaps[id - 1].refcount = 1;
   dpyinfo->bitmaps[id - 1].file
     = (char *) xmalloc (SBYTES (file) + 1);
@@ -701,7 +711,8 @@ x_destroy_bitmap (f, id)
        {
          BLOCK_INPUT;
          XFreePixmap (FRAME_X_DISPLAY (f), dpyinfo->bitmaps[id - 1].pixmap);
-         XFreePixmap (FRAME_X_DISPLAY (f), dpyinfo->bitmaps[id - 1].mask);
+         if (dpyinfo->bitmaps[id - 1].have_mask)
+           XFreePixmap (FRAME_X_DISPLAY (f), dpyinfo->bitmaps[id - 1].mask);
          if (dpyinfo->bitmaps[id - 1].file)
            {
              xfree (dpyinfo->bitmaps[id - 1].file);
@@ -723,7 +734,8 @@ x_destroy_all_bitmaps (dpyinfo)
     if (dpyinfo->bitmaps[i].refcount > 0)
       {
        XFreePixmap (dpyinfo->display, dpyinfo->bitmaps[i].pixmap);
-       XFreePixmap (dpyinfo->display, dpyinfo->bitmaps[i].mask);
+       if (dpyinfo->bitmaps[i].have_mask)
+         XFreePixmap (dpyinfo->display, dpyinfo->bitmaps[i].mask);
        if (dpyinfo->bitmaps[i].file)
          xfree (dpyinfo->bitmaps[i].file);
       }
@@ -750,7 +762,7 @@ static void x_destroy_x_image P_ ((XImage *ximg));
    It's nicer with some borders in this context */
 
 int
-x_create_bitmap_mask(f, id)
+x_create_bitmap_mask (f, id)
      struct frame *f;
      int id;
 {
@@ -768,9 +780,9 @@ x_create_bitmap_mask(f, id)
   if (!(id > 0))
     return -1;
 
-  pixmap = x_bitmap_pixmap(f, id);
-  width = x_bitmap_width(f, id);
-  height = x_bitmap_height(f, id);
+  pixmap = x_bitmap_pixmap (f, id);
+  width = x_bitmap_width (f, id);
+  height = x_bitmap_height (f, id);
 
   BLOCK_INPUT;
   ximg = XGetImage (FRAME_X_DISPLAY (f), pixmap, 0, 0, width, height,
@@ -787,7 +799,7 @@ x_create_bitmap_mask(f, id)
   UNBLOCK_INPUT;
   if (!result)
     {
-      XDestroyImage(ximg);
+      XDestroyImage (ximg);
       return -1;
     }
 
@@ -822,10 +834,11 @@ x_create_bitmap_mask(f, id)
             width, height);
   XFreeGC (FRAME_X_DISPLAY (f), gc);
 
+  dpyinfo->bitmaps[id - 1].have_mask = 1;
   dpyinfo->bitmaps[id - 1].mask = mask;
 
   XDestroyImage (ximg);
-  x_destroy_x_image(mask_img);
+  x_destroy_x_image (mask_img);
 
   return 0;
 }
@@ -1087,36 +1100,51 @@ x_set_wait_for_wm (f, new_value, old_value)
 
 #ifdef USE_GTK
 
-/* Wrapper for gtk_window_icon_from_file() */
+static Lisp_Object x_find_image_file P_ ((Lisp_Object file));
+
+/* 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.  */
 
 int
-xg_set_icon(f, file)
-    struct frame *f;
+xg_set_icon (f, file)
+    FRAME_PTR f;
     Lisp_Object file;
 {
-    struct gcpro gcpro1, gcpro2, gcpro3;
-    int fd;
-    int result = 1;
-    Lisp_Object found, search_path;
-    char *filename;
+  struct gcpro gcpro1;
+  int result = 0;
+  Lisp_Object found;
 
-    search_path = Fcons (Vdata_directory, Vx_bitmap_file_path);
+  GCPRO1 (found);
 
-    GCPRO3 (found, search_path, file);
-    fd = openp (search_path, file, Qnil, &found, Qnil);
-    if (fd > 0)
-      {
-       filename = (char *) SDATA (found);
-       BLOCK_INPUT;
-       result =
-         gtk_window_set_icon_from_file (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
-                                        filename,
-                                        NULL);
-       UNBLOCK_INPUT;
-      }
-    emacs_close (fd);
-    UNGCPRO;
-    return result;
+  found = x_find_image_file (file);
+
+  if (! NILP (found))
+    {
+      GdkPixbuf *pixbuf;
+      GError *err = NULL;
+      char *filename;
+
+      filename = SDATA (found);
+      BLOCK_INPUT;
+
+      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);
+
+         result = 1;
+       }
+      else
+       g_error_free (err);
+
+      UNBLOCK_INPUT;
+    }
+
+  UNGCPRO;
+  return result;
 }
 #endif /* USE_GTK */
 
@@ -1935,11 +1963,11 @@ x_set_name (f, name, explicit)
           managers which don't support that encoding.  So, if NAME
           contains only ASCII and 8859-1 characters, encode it by
           iso-latin-1, and use "STRING" in text.encoding hoping that
-          such window manager at least analize this format correctly,
+          such window managers at least analyze this format correctly,
           i.e. treat 8-bit bytes as 8859-1 characters.
 
           We may also be able to use "UTF8_STRING" in text.encoding
-          in the feature which can encode all Unicode characters.
+          in the future which can encode all Unicode characters.
           But, for the moment, there's no way to know that the
           current window manager supports it or not.  */
        coding_system = Qcompound_text;
@@ -2430,8 +2458,8 @@ create_frame_xic (f)
 
       xic = XCreateIC (xim,
                       XNInputStyle, xic_style,
-                      XNClientWindow, FRAME_X_WINDOW(f),
-                      XNFocusWindow, FRAME_X_WINDOW(f),
+                      XNClientWindow, FRAME_X_WINDOW (f),
+                      XNFocusWindow, FRAME_X_WINDOW (f),
                       XNStatusAttributes, status_attr,
                       XNPreeditAttributes, preedit_attr,
                       NULL);
@@ -2475,7 +2503,7 @@ xic_set_preeditarea (w, x, y)
   XVaNestedList attr;
   XPoint spot;
 
-  spot.x = WINDOW_TO_FRAME_PIXEL_X (w, x);
+  spot.x = WINDOW_TO_FRAME_PIXEL_X (w, x) + WINDOW_LEFT_FRINGE_WIDTH (w);
   spot.y = WINDOW_TO_FRAME_PIXEL_Y (w, y) + FONT_BASE (FRAME_FONT (f));
   attr = XVaCreateNestedList (0, XNSpotLocation, &spot, NULL);
   XSetICValues (FRAME_XIC (f), XNPreeditAttributes, attr, NULL);
@@ -2522,7 +2550,7 @@ xic_set_statusarea (f)
   XFree (needed);
 
   attr = XVaCreateNestedList (0, XNArea, &area, NULL);
-  XSetICValues(xic, XNStatusAttributes, attr, NULL);
+  XSetICValues (xic, XNStatusAttributes, attr, NULL);
   XFree (attr);
 }
 
@@ -2642,7 +2670,7 @@ x_window (f, window_prompting, minibuffer_only)
   {
     int len;
     char *tem, shell_position[32];
-    Arg al[2];
+    Arg al[10];
     int ac = 0;
     int extra_borders = 0;
     int menubar_size
@@ -2694,9 +2722,19 @@ x_window (f, window_prompting, minibuffer_only)
                 (xneg ? '-' : '+'), left,
                 (yneg ? '-' : '+'), top);
       else
-       sprintf (shell_position, "=%dx%d",
-                FRAME_PIXEL_WIDTH (f) + extra_borders,
-                FRAME_PIXEL_HEIGHT (f) + menubar_size + extra_borders);
+        {
+          sprintf (shell_position, "=%dx%d",
+                   FRAME_PIXEL_WIDTH (f) + extra_borders,
+                   FRAME_PIXEL_HEIGHT (f) + menubar_size + extra_borders);
+
+          /* Setting x and y when the position is not specified in
+             the geometry string will set program position in the WM hints.
+             If Emacs had just one program position, we could set it in
+             fallback resources, but since each make-frame call can specify
+             different program positions, this is easier.  */
+          XtSetArg (al[ac], XtNx, left); ac++;
+          XtSetArg (al[ac], XtNy, top); ac++;
+        }
     }
 
     len = strlen (shell_position) + 1;
@@ -2755,7 +2793,7 @@ x_window (f, window_prompting, minibuffer_only)
     {
       /* XIM server might require some X events. */
       unsigned long fevent = NoEventMask;
-      XGetICValues(FRAME_XIC (f), XNFilterEvents, &fevent, NULL);
+      XGetICValues (FRAME_XIC (f), XNFilterEvents, &fevent, NULL);
       attributes.event_mask |= fevent;
     }
 #endif /* HAVE_X_I18N */
@@ -2809,7 +2847,7 @@ if (use_xim)
       {
        /* XIM server might require some X events. */
        unsigned long fevent = NoEventMask;
-       XGetICValues(FRAME_XIC (f), XNFilterEvents, &fevent, NULL);
+       XGetICValues (FRAME_XIC (f), XNFilterEvents, &fevent, NULL);
 
        if (fevent != NoEventMask)
          {
@@ -2873,7 +2911,7 @@ x_window (f)
        {
          /* XIM server might require some X events. */
          unsigned long fevent = NoEventMask;
-         XGetICValues(FRAME_XIC (f), XNFilterEvents, &fevent, NULL);
+         XGetICValues (FRAME_XIC (f), XNFilterEvents, &fevent, NULL);
          attributes.event_mask |= fevent;
          attribute_mask = CWEventMask;
          XChangeWindowAttributes (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
@@ -3506,6 +3544,19 @@ This function is an internal primitive--use `make-frame' instead.  */)
        ;
     }
 
+  /* Set the WM leader property.  GTK does this itself, so this is not
+     needed when using GTK.  */
+  if (dpyinfo->client_leader_window != 0)
+    {
+      BLOCK_INPUT;
+      XChangeProperty (FRAME_X_DISPLAY (f),
+                       FRAME_OUTER_WINDOW (f),
+                       dpyinfo->Xatom_wm_client_leader,
+                       XA_WINDOW, 32, PropModeReplace,
+                       (char *) &dpyinfo->client_leader_window, 1);
+      UNBLOCK_INPUT;
+    }
+
   UNGCPRO;
 
   /* Make sure windows on this frame appear in calls to next-window
@@ -4171,9 +4222,6 @@ If DISPLAY is nil, that stands for the selected frame's display.  */)
   for (i = 0; i < dpyinfo->n_fonts; i++)
     if (dpyinfo->font_table[i].name)
       {
-       if (dpyinfo->font_table[i].name != dpyinfo->font_table[i].full_name)
-         xfree (dpyinfo->font_table[i].full_name);
-       xfree (dpyinfo->font_table[i].name);
        XFreeFont (dpyinfo->display, dpyinfo->font_table[i].font);
       }
 
@@ -4236,6 +4284,125 @@ x_sync (f)
 }
 
 \f
+/***********************************************************************
+                General X functions exposed to Elisp.
+ ***********************************************************************/
+
+DEFUN ("x-send-client-message", Fx_send_client_event,
+       Sx_send_client_message, 6, 6, 0,
+       doc: /* Send a client message of MESSAGE-TYPE to window DEST on DISPLAY.
+
+For DISPLAY, specify either a frame or a display name (a string).
+If DISPLAY is nil, that stands for the selected frame's display.
+DEST may be an integer, in which case it is a Window id.  The value 0 may
+be used to send to the root window of the DISPLAY.
+If DEST is a frame the event is sent to the outer window of that frame.
+Nil means the currently selected frame.
+If DEST is the string "PointerWindow" the event is sent to the window that
+contains the pointer.  If DEST is the string "InputFocus" the event is
+sent to the window that has the input focus.
+FROM is the frame sending the event.  Use nil for currently selected frame.
+MESSAGE-TYPE is the name of an Atom as a string.
+FORMAT must be one of 8, 16 or 32 and determines the size of the values in
+bits.  VALUES is a list of integer and/or strings containing the values to
+send.  If a value is a string, it is converted to an Atom and the value of
+the Atom is sent.  If more values than fits into the event is given,
+the excessive values are ignored.  */)
+     (display, dest, from, message_type, format, values)
+     Lisp_Object display, dest, from, message_type, format, values;
+{
+  struct x_display_info *dpyinfo = check_x_display_info (display);
+  Window wdest;
+  XEvent event;
+  Lisp_Object cons;
+  int i;
+  int max_nr_values = (int) sizeof (event.xclient.data.b);
+  struct frame *f = check_x_frame (from);
+  
+  CHECK_STRING (message_type);
+  CHECK_NUMBER (format);
+  CHECK_CONS (values);
+
+  for (cons = values; CONSP (cons); cons = XCDR (cons))
+    {
+      Lisp_Object o = XCAR (cons);
+
+      if (! INTEGERP (o) && ! STRINGP (o))
+        error ("Bad data in VALUES, must be integer or string");
+    }
+
+  event.xclient.type = ClientMessage;
+  event.xclient.format = XFASTINT (format);
+
+  if (event.xclient.format != 8 && event.xclient.format != 16
+      && event.xclient.format != 32)
+    error ("FORMAT must be one of 8, 16 or 32");
+  if (event.xclient.format == 16) max_nr_values /= 2;
+  if (event.xclient.format == 32) max_nr_values /= 4;
+  
+  if (FRAMEP (dest) || NILP (dest))
+    {
+      struct frame *fdest = check_x_frame (dest);
+      wdest = FRAME_OUTER_WINDOW (fdest);
+    }
+  else if (STRINGP (dest))
+    {
+      if (strcmp (SDATA (dest), "PointerWindow") == 0)
+        wdest = PointerWindow;
+      else if (strcmp (SDATA (dest), "InputFocus") == 0)
+        wdest = InputFocus;
+      else
+        error ("DEST as a string must be one of PointerWindow or InputFocus");
+    }
+  else
+    {
+      CHECK_NUMBER (dest);
+      wdest = (Window) XFASTINT (dest);
+      if (wdest == 0) wdest = dpyinfo->root_window;
+    }
+
+  BLOCK_INPUT;
+  for (cons = values, i = 0;
+       CONSP (cons) && i < max_nr_values;
+       cons = XCDR (cons), ++i)
+    {
+      Lisp_Object o = XCAR (cons);
+      long val;
+
+      if (INTEGERP (o))
+        val = XINT (o);
+      else if (STRINGP (o))
+          val = XInternAtom (dpyinfo->display, SDATA (o), False);
+
+      if (event.xclient.format == 8)
+        event.xclient.data.b[i] = (char) val;
+      else if (event.xclient.format == 16)
+        event.xclient.data.s[i] = (short) val;
+      else
+        event.xclient.data.l[i] = val;
+    }
+
+  for ( ; i < max_nr_values; ++i)
+    if (event.xclient.format == 8)
+      event.xclient.data.b[i] = 0;
+    else if (event.xclient.format == 16)
+      event.xclient.data.s[i] = 0;
+    else
+      event.xclient.data.l[i] = 0;
+
+  event.xclient.message_type
+    = XInternAtom (dpyinfo->display, SDATA (message_type), False);
+  event.xclient.display = dpyinfo->display;
+  event.xclient.window = FRAME_OUTER_WINDOW (f);
+
+  XSendEvent (dpyinfo->display, wdest, False, 0xffff, &event);
+
+  XFlush (dpyinfo->display);
+  UNBLOCK_INPUT;
+
+  return Qnil;
+}
+\f
 /***********************************************************************
                            Image types
  ***********************************************************************/
@@ -6714,7 +6881,37 @@ lookup_rgb_color (f, r, g, b)
   unsigned hash = CT_HASH_RGB (r, g, b);
   int i = hash % CT_SIZE;
   struct ct_color *p;
+  struct x_display_info *dpyinfo;
+
+  /* Handle TrueColor visuals specially, which improves performance by
+     two orders of magnitude.  Freeing colors on TrueColor visuals is
+     a nop, and pixel colors specify RGB values directly.  See also
+     the Xlib spec, chapter 3.1.  */
+  dpyinfo = FRAME_X_DISPLAY_INFO (f);
+  if (dpyinfo->red_bits > 0)
+    {
+      unsigned long pr, pg, pb;
+
+      /* Apply gamma-correction like normal color allocation does.  */
+      if (f->gamma)
+       {
+         XColor color;
+         color.red = r, color.green = g, color.blue = b;
+         gamma_correct (f, &color);
+         r = color.red, g = color.green, b = color.blue;
+       }
 
+      /* Scale down RGB values to the visual's bits per RGB, and shift
+        them to the right position in the pixel color.  Note that the
+        original RGB values are 16-bit values, as usual in X.  */
+      pr = (r >> (16 - dpyinfo->red_bits))   << dpyinfo->red_offset;
+      pg = (g >> (16 - dpyinfo->green_bits)) << dpyinfo->green_offset;
+      pb = (b >> (16 - dpyinfo->blue_bits))  << dpyinfo->blue_offset;
+
+      /* Assemble the pixel color.  */
+      return pr | pg | pb;
+    }
+  
   for (p = ct_table[i]; p; p = p->next)
     if (p->r == r && p->g == g && p->b == b)
       break;
@@ -7565,7 +7762,11 @@ pbm_load (f, img)
 
 #if HAVE_PNG
 
-#include <libpng/png.h>
+#if defined HAVE_LIBPNG_PNG_H
+# include <libpng/png.h>
+#else
+# include <png.h>
+#endif
 
 /* Function prototypes.  */
 
@@ -8965,7 +9166,7 @@ gif_load (f, img)
       memsrc.len = SBYTES (specified_data);
       memsrc.index = 0;
 
-      gif = DGifOpen(&memsrc, gif_read_from_memory);
+      gif = DGifOpen (&memsrc, gif_read_from_memory);
       if (!gif)
        {
          image_error ("Cannot open memory source `%s'", img->spec, Qnil);
@@ -10862,6 +11063,19 @@ meaning don't clear the cache.  */);
 #endif /* USE_MOTIF */
 #endif /* USE_X_TOOLKIT */
 
+#ifdef USE_GTK
+  Fprovide (intern ("gtk"), Qnil);
+
+  DEFVAR_LISP ("gtk-version-string", &Vgtk_version_string,
+               doc: /* Version info for GTK+.  */);
+  {
+    char gtk_version[40];
+    g_snprintf (gtk_version, sizeof (gtk_version), "%u.%u.%u",
+                GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
+    Vgtk_version_string = build_string (gtk_version);
+  }
+#endif /* USE_GTK */
+
   /* X window properties.  */
   defsubr (&Sx_change_window_property);
   defsubr (&Sx_delete_window_property);
@@ -10889,6 +11103,7 @@ meaning don't clear the cache.  */);
   defsubr (&Sx_close_connection);
   defsubr (&Sx_display_list);
   defsubr (&Sx_synchronize);
+  defsubr (&Sx_send_client_message);
   defsubr (&Sx_focus_frame);
   defsubr (&Sx_backspace_delete_keys_p);
 
@@ -11016,3 +11231,6 @@ init_xfns ()
 }
 
 #endif /* HAVE_X_WINDOWS */
+
+/* arch-tag: 55040d02-5485-4d58-8b22-95a7a05f3288
+   (do not change this comment) */