(linenum→info "unix/slp.c:2238")

emacs/22.1/src/gtkutil.c

    1: /* Functions for creating and updating GTK widgets.
    2:    Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
    3: 
    4: This file is part of GNU Emacs.
    5: 
    6: GNU Emacs is free software; you can redistribute it and/or modify
    7: it under the terms of the GNU General Public License as published by
    8: the Free Software Foundation; either version 2, or (at your option)
    9: any later version.
   10: 
   11: GNU Emacs is distributed in the hope that it will be useful,
   12: but WITHOUT ANY WARRANTY; without even the implied warranty of
   13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14: GNU General Public License for more details.
   15: 
   16: You should have received a copy of the GNU General Public License
   17: along with GNU Emacs; see the file COPYING.  If not, write to
   18: the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   19: Boston, MA 02110-1301, USA.  */
   20: 
   21: #include "config.h"
   22: 
   23: #ifdef USE_GTK
   24: #include <string.h>
   25: #include <signal.h>
   26: #include <stdio.h>
   27: #include "lisp.h"
   28: #include "xterm.h"
   29: #include "blockinput.h"
   30: #include "syssignal.h"
   31: #include "window.h"
   32: #include "atimer.h"
   33: #include "gtkutil.h"
   34: #include "termhooks.h"
   35: #include "keyboard.h"
   36: #include "charset.h"
   37: #include "coding.h"
   38: #include <gdk/gdkkeysyms.h>
   39: 
   40: 
   41: #define FRAME_TOTAL_PIXEL_HEIGHT(f) \
   42:   (FRAME_PIXEL_HEIGHT (f) + FRAME_MENUBAR_HEIGHT (f) + FRAME_TOOLBAR_HEIGHT (f))
   43: 
   44: /* Avoid "differ in sign" warnings */
   45: #define SSDATA(x)  ((char *) SDATA (x))
   46: 
   47: ^L
   48: /***********************************************************************
   49:                       Display handling functions
   50:  ***********************************************************************/
   51: 
   52: #ifdef HAVE_GTK_MULTIDISPLAY
   53: 
   54: /* Return the GdkDisplay that corresponds to the X display DPY.  */
   55: 
   56: static GdkDisplay *
   57: xg_get_gdk_display (dpy)
   58:      Display *dpy;
   59: {
   60:   return gdk_x11_lookup_xdisplay (dpy);
   61: }
   62: 
   63: /* When the GTK widget W is to be created on a display for F that
   64:    is not the default display, set the display for W.
   65:    W can be a GtkMenu or a GtkWindow widget.  */
   66: 
   67: static void
   68: xg_set_screen (w, f)
   69:      GtkWidget *w;
   70:      FRAME_PTR f;
   71: {
   72:   if (FRAME_X_DISPLAY (f) != GDK_DISPLAY ())
   73:     {
   74:       GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
   75:       GdkScreen *gscreen = gdk_display_get_default_screen (gdpy);
   76: 
   77:       if (GTK_IS_MENU (w))
   78:         gtk_menu_set_screen (GTK_MENU (w), gscreen);
   79:       else
   80:         gtk_window_set_screen (GTK_WINDOW (w), gscreen);
   81:     }
   82: }
   83: 
   84: 
   85: #else /* not HAVE_GTK_MULTIDISPLAY */
   86: 
   87: /* Make some defines so we can use the GTK 2.2 functions when
   88:    compiling with GTK 2.0.  */
   89: 
   90: #define xg_set_screen(w, f)
   91: #define gdk_xid_table_lookup_for_display(dpy, w)    gdk_xid_table_lookup (w)
   92: #define gdk_pixmap_foreign_new_for_display(dpy, p)  gdk_pixmap_foreign_new (p)
   93: #define gdk_cursor_new_for_display(dpy, c)          gdk_cursor_new (c)
   94: #define gdk_x11_lookup_xdisplay(dpy)                0
   95: #define GdkDisplay                                  void
   96: 
   97: #endif /* not HAVE_GTK_MULTIDISPLAY */
   98: 
   99: /* Open a display named by DISPLAY_NAME.  The display is returned in *DPY.
  100:    *DPY is set to NULL if the display can't be opened.
  101: 
  102:    Returns non-zero if display could be opened, zero if display could not
  103:    be opened, and less than zero if the GTK version doesn't support
  104:    multipe displays.  */
  105: 
  106: int
  107: xg_display_open (display_name, dpy)
  108:      char *display_name;
  109:      Display **dpy;
  110: {
  111: #ifdef HAVE_GTK_MULTIDISPLAY
  112:   GdkDisplay *gdpy;
  113: 
  114:   gdpy = gdk_display_open (display_name);
  115:   *dpy = gdpy ? GDK_DISPLAY_XDISPLAY (gdpy) : NULL;
  116: 
  117:   return gdpy != NULL;
  118: 
  119: #else /* not HAVE_GTK_MULTIDISPLAY */
  120: 
  121:   return -1;
  122: #endif /* not HAVE_GTK_MULTIDISPLAY */
  123: }
  124: 
  125: 
  126: /* Close display DPY.  */
  127: 
  128: void
  129: xg_display_close (Display *dpy)
  130: {
  131: #ifdef HAVE_GTK_MULTIDISPLAY
  132:   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
  133: 
  134:   /* If this is the default display, we must change it before calling
  135:      dispose, otherwise it will crash on some Gtk+ versions.  */
  136:   if (gdk_display_get_default () == gdpy)
  137:     {
  138:       struct x_display_info *dpyinfo;
  139:       Display *new_dpy = 0;
  140:       GdkDisplay *gdpy_new;
  141: 
  142:       /* Find another display.  */
  143:       for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
  144:         if (dpyinfo->display != dpy)
  145:           {
  146:             new_dpy = dpyinfo->display;
  147:             break;
  148:           }
  149: 
  150:       if (! new_dpy) return; /* Emacs will exit anyway.  */
  151: 
  152:       gdpy_new = gdk_x11_lookup_xdisplay (new_dpy);
  153:       gdk_display_manager_set_default_display (gdk_display_manager_get (),
  154:                                                gdpy_new);
  155:     }
  156: 
  157:   /* GTK 2.2-2.8 has a bug that makes gdk_display_close crash (bug
  158:      http://bugzilla.gnome.org/show_bug.cgi?id=85715).  This way
  159:      we can continue running, but there will be memory leaks.  */
  160: 
  161: #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 10
  162:   g_object_run_dispose (G_OBJECT (gdpy));
  163: #else
  164:   /* This seems to be fixed in GTK 2.10. */
  165:   gdk_display_close (gdpy);
  166: #endif
  167: #endif /* HAVE_GTK_MULTIDISPLAY */
  168: }
  169: 
  170: ^L
  171: /***********************************************************************
  172:                       Utility functions
  173:  ***********************************************************************/
  174: /* The timer for scroll bar repetition and menu bar timeouts.
  175:    NULL if no timer is started.  */
  176: static struct atimer *xg_timer;
  177: 
  178: 
  179: /* The next two variables and functions are taken from lwlib.  */
  180: static widget_value *widget_value_free_list;
  181: static int malloc_cpt;
  182: 
  183: /* Allocate a widget_value structure, either by taking one from the
  184:    widget_value_free_list or by malloc:ing a new one.
  185: 
  186:    Return a pointer to the allocated structure.  */
  187: 
  188: widget_value *
  189: malloc_widget_value ()
  190: {
  191:   widget_value *wv;
  192:   if (widget_value_free_list)
  193:     {
  194:       wv = widget_value_free_list;
  195:       widget_value_free_list = wv->free_list;
  196:       wv->free_list = 0;
  197:     }
  198:   else
  199:     {
  200:       wv = (widget_value *) xmalloc (sizeof (widget_value));
  201:       malloc_cpt++;
  202:     }
  203:   memset (wv, 0, sizeof (widget_value));
  204:   return wv;
  205: }
  206: 
  207: /* This is analogous to free.  It frees only what was allocated
  208:    by malloc_widget_value, and no substructures.  */
  209: 
  210: void
  211: free_widget_value (wv)
  212:      widget_value *wv;
  213: {
  214:   if (wv->free_list)
  215:     abort ();
  216: 
  217:   if (malloc_cpt > 25)
  218:     {
  219:       /* When the number of already allocated cells is too big,
  220:          We free it.  */
  221:       free (wv);
  222:       malloc_cpt--;
  223:     }
  224:   else
  225:     {
  226:       wv->free_list = widget_value_free_list;
  227:       widget_value_free_list = wv;
  228:     }
  229: }
  230: 
  231: 
  232: /* Create and return the cursor to be used for popup menus and
  233:    scroll bars on display DPY.  */
  234: 
  235: GdkCursor *
  236: xg_create_default_cursor (dpy)
  237:      Display *dpy;
  238: {
  239:   GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (dpy);
  240:   return gdk_cursor_new_for_display (gdpy, GDK_LEFT_PTR);
  241: }
  242: 
  243: /* Apply GMASK to GPIX and return a GdkPixbuf with an alpha channel.  */
  244: 
  245: static GdkPixbuf *
  246: xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap)
  247:      GdkPixmap *gpix;
  248:      GdkPixmap *gmask;
  249:      GdkColormap *cmap;
  250: {
  251:   int x, y, width, height, rowstride, mask_rowstride;
  252:   GdkPixbuf *icon_buf, *tmp_buf;
  253:   guchar *pixels;
  254:   guchar *mask_pixels;
  255: 
  256:   gdk_drawable_get_size (gpix, &width, &height);
  257:   tmp_buf = gdk_pixbuf_get_from_drawable (NULL, gpix, cmap,
  258:                                           0, 0, 0, 0, width, height);
  259:   icon_buf = gdk_pixbuf_add_alpha (tmp_buf, FALSE, 0, 0, 0);
  260:   g_object_unref (G_OBJECT (tmp_buf));
  261: 
  262:   if (gmask)
  263:     {
  264:       GdkPixbuf *mask_buf = gdk_pixbuf_get_from_drawable (NULL,
  265:                                                           gmask,
  266:                                                           NULL,
  267:                                                           0, 0, 0, 0,
  268:                                                           width, height);
  269:       guchar *pixels = gdk_pixbuf_get_pixels (icon_buf);
  270:       guchar *mask_pixels = gdk_pixbuf_get_pixels (mask_buf);
  271:       int rowstride = gdk_pixbuf_get_rowstride (icon_buf);
  272:       int mask_rowstride = gdk_pixbuf_get_rowstride (mask_buf);
  273:       int y;
  274: 
  275:       for (y = 0; y < height; ++y)
  276:         {
  277:           guchar *iconptr, *maskptr;
  278:           int x;
  279: 
  280:           iconptr = pixels + y * rowstride;
  281:           maskptr = mask_pixels + y * mask_rowstride;
  282: 
  283:           for (x = 0; x < width; ++x)
  284:             {
  285:               /* In a bitmap, RGB is either 255/255/255 or 0/0/0.  Checking
  286:                  just R is sufficient.  */
  287:               if (maskptr[0] == 0)
  288:                 iconptr[3] = 0; /* 0, 1, 2 is R, G, B.  3 is alpha.  */
  289: 
  290:               iconptr += rowstride/width;
  291:               maskptr += mask_rowstride/width;
  292:             }
  293:         }
  294: 
  295:       g_object_unref (G_OBJECT (mask_buf));
  296:     }
  297: 
  298:   return icon_buf;
  299: }
  300: 
  301: /* For the image defined in IMG, make and return a GtkImage.  For displays with
  302:    8 planes or less we must make a GdkPixbuf and apply the mask manually.
  303:    Otherwise the highlightning and dimming the tool bar code in GTK does
  304:    will look bad.  For display with more than 8 planes we just use the
  305:    pixmap and mask directly.  For monochrome displays, GTK doesn't seem
  306:    able to use external pixmaps, it looks bad whatever we do.
  307:    The image is defined on the display where frame F is.
  308:    WIDGET is used to find the GdkColormap to use for the GdkPixbuf.
  309:    If OLD_WIDGET is NULL, a new widget is constructed and returned.
  310:    If OLD_WIDGET is not NULL, that widget is modified.  */
  311: 
  312: static GtkWidget *
  313: xg_get_image_for_pixmap (f, img, widget, old_widget)
  314:      FRAME_PTR f;
  315:      struct image *img;
  316:      GtkWidget *widget;
  317:      GtkImage *old_widget;
  318: {
  319:   GdkPixmap *gpix;
  320:   GdkPixmap *gmask;
  321:   GdkDisplay *gdpy;
  322: 
  323:   /* If we have a file, let GTK do all the image handling.
  324:      This seems to be the only way to make insensitive and activated icons
  325:      look good in all cases.  */
  326:   Lisp_Object specified_file = Qnil;
  327:   Lisp_Object tail;
  328:   Lisp_Object file;
  329:   extern Lisp_Object QCfile;
  330: 
  331:   for (tail = XCDR (img->spec);
  332:        NILP (specified_file) && CONSP (tail) && CONSP (XCDR (tail));
  333:        tail = XCDR (XCDR (tail)))
  334:     if (EQ (XCAR (tail), QCfile))
  335:       specified_file = XCAR (XCDR (tail));
  336: 
  337:   /* We already loaded the image once before calling this
  338:      function, so this only fails if the image file has been removed.
  339:      In that case, use the pixmap already loaded.  */
  340: 
  341:   if (STRINGP (specified_file)
  342:       && STRINGP (file = x_find_image_file (specified_file)))
  343:     {
  344:       if (! old_widget)
  345:         old_widget = GTK_IMAGE (gtk_image_new_from_file (SSDATA (file)));
  346:       else
  347:         gtk_image_set_from_file (old_widget, SSDATA (file));
  348: 
  349:       return GTK_WIDGET (old_widget);
  350:     }
  351: 
  352:   /* No file, do the image handling ourselves.  This will look very bad
  353:      on a monochrome display, and sometimes bad on all displays with
  354:      certain themes.  */
  355: 
  356:   gdpy = gdk_x11_lookup_xdisplay (FRAME_X_DISPLAY (f));
  357:   gpix = gdk_pixmap_foreign_new_for_display (gdpy, img->pixmap);
  358:   gmask = img->mask ? gdk_pixmap_foreign_new_for_display (gdpy, img->mask) : 0;
  359: 
  360:   if (x_screen_planes (f) > 8 || x_screen_planes (f) == 1)
  361:     {
  362:       if (! old_widget)
  363:         old_widget = GTK_IMAGE (gtk_image_new_from_pixmap (gpix, gmask));
  364:       else
  365:         gtk_image_set_from_pixmap (old_widget, gpix, gmask);
  366:     }
  367:   else
  368:     {
  369: 
  370:       /* This is a workaround to make icons look good on pseudo color
  371:          displays.  Apparently GTK expects the images to have an alpha
  372:          channel.  If they don't, insensitive and activated icons will
  373:          look bad.  This workaround does not work on monochrome displays,
  374:          and is not needed on true color/static color displays (i.e.
  375:          16 bits and higher).  */
  376:       GdkColormap *cmap = gtk_widget_get_colormap (widget);
  377:       GdkPixbuf *icon_buf = xg_get_pixbuf_from_pix_and_mask (gpix, gmask, cmap);
  378: 
  379:       if (! old_widget)
  380:         old_widget = GTK_IMAGE (gtk_image_new_from_pixbuf (icon_buf));
  381:       else
  382:         gtk_image_set_from_pixbuf (old_widget, icon_buf);
  383: 
  384:       g_object_unref (G_OBJECT (icon_buf));
  385:     }
  386: 
  387:   g_object_unref (G_OBJECT (gpix));
  388:   if (gmask) g_object_unref (G_OBJECT (gmask));
  389: 
  390:   return GTK_WIDGET (old_widget);
  391: }
  392: 
  393: 
  394: /* Set CURSOR on W and all widgets W contain.  We must do like this
  395:    for scroll bars and menu because they create widgets internally,
  396:    and it is those widgets that are visible.  */
  397: 
  398: static void
  399: xg_set_cursor (w, cursor)
  400:      GtkWidget *w;
  401:      GdkCursor *cursor;
  402: {
  403:   GList *children = gdk_window_peek_children (w->window);
  404: 
  405:   gdk_window_set_cursor (w->window, cursor);
  406: 
  407:   /* The scroll bar widget has more than one GDK window (had to look at
  408:      the source to figure this out), and there is no way to set cursor
  409:      on widgets in GTK.  So we must set the cursor for all GDK windows.
  410:      Ditto for menus.  */
  411: 
  412:   for ( ; children; children = g_list_next (children))
  413:     gdk_window_set_cursor (GDK_WINDOW (children->data), cursor);
  414: }
  415: 
  416: /*  Timer function called when a timeout occurs for xg_timer.
  417:     This function processes all GTK events in a recursive event loop.
  418:     This is done because GTK timer events are not seen by Emacs event
  419:     detection, Emacs only looks for X events.  When a scroll bar has the
  420:     pointer (detected by button press/release events below) an Emacs
  421:     timer is started, and this function can then check if the GTK timer
  422:     has expired by calling the GTK event loop.
  423:     Also, when a menu is active, it has a small timeout before it
  424:     pops down the sub menu under it.  */
  425: 
  426: static void
  427: xg_process_timeouts (timer)
  428:      struct atimer *timer;
  429: {
  430:   BLOCK_INPUT;
  431:   /* Ideally we would like to just handle timer events, like the Xt version
  432:      of this does in xterm.c, but there is no such feature in GTK.  */
  433:   while (gtk_events_pending ())
  434:     gtk_main_iteration ();
  435:   UNBLOCK_INPUT;
  436: }
  437: 
  438: /* Start the xg_timer with an interval of 0.1 seconds, if not already started.
  439:    xg_process_timeouts is called when the timer expires.  The timer
  440:    started is continuous, i.e. runs until xg_stop_timer is called.  */
  441: 
  442: static void
  443: xg_start_timer ()
  444: {
  445:   if (! xg_timer)
  446:     {
  447:       EMACS_TIME interval;
  448:       EMACS_SET_SECS_USECS (interval, 0, 100000);
  449:       xg_timer = start_atimer (ATIMER_CONTINUOUS,
  450:                                interval,
  451:                                xg_process_timeouts,
  452:                                0);
  453:     }
  454: }
  455: 
  456: /* Stop the xg_timer if started.  */
  457: 
  458: static void
  459: xg_stop_timer ()
  460: {
  461:   if (xg_timer)
  462:     {
  463:       cancel_atimer (xg_timer);
  464:       xg_timer = 0;
  465:     }
  466: }
  467: 
  468: /* Insert NODE into linked LIST.  */
  469: 
  470: static void
  471: xg_list_insert (xg_list_node *list, xg_list_node *node)
  472: {
  473:   xg_list_node *list_start = list->next;
  474: 
  475:   if (list_start) list_start->prev = node;
  476:   node->next = list_start;
  477:   node->prev = 0;
  478:   list->next = node;
  479: }
  480: 
  481: /* Remove NODE from linked LIST.  */
  482: 
  483: static void
  484: xg_list_remove (xg_list_node *list, xg_list_node *node)
  485: {
  486:   xg_list_node *list_start = list->next;
  487:   if (node == list_start)
  488:     {
  489:       list->next = node->next;
  490:       if (list->next) list->next->prev = 0;
  491:     }
  492:   else
  493:     {
  494:       node->prev->next = node->next;
  495:       if (node->next) node->next->prev = node->prev;
  496:     }
  497: }
  498: 
  499: /* Allocate and return a utf8 version of STR.  If STR is already
  500:    utf8 or NULL, just return STR.
  501:    If not, a new string is allocated and the caller must free the result
  502:    with g_free.  */
  503: 
  504: static char *
  505: get_utf8_string (str)
  506:      char *str;
  507: {
  508:   char *utf8_str = str;
  509: 
  510:   if (!str) return NULL;
  511: 
  512:   /* If not UTF-8, try current locale.  */
  513:   if (!g_utf8_validate (str, -1, NULL))
  514:     utf8_str = g_locale_to_utf8 (str, -1, 0, 0, 0);
  515: 
  516:   if (!utf8_str) 
  517:     {
  518:       /* Probably some control characters in str.  Escape them. */
  519:       size_t nr_bad = 0;
  520:       gsize bytes_read;
  521:       gsize bytes_written;
  522:       unsigned char *p = (unsigned char *)str;
  523:       char *cp, *up;
  524:       GError *error = NULL;
  525: 
  526:       while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
  527:                                        &bytes_written, &error))
  528:              && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
  529:         {
  530:           ++nr_bad;
  531:           p += bytes_written+1;
  532:           g_error_free (error);
  533:           error = NULL;
  534:         }
  535: 
  536:       if (error) 
  537:         {
  538:           g_error_free (error);
  539:           error = NULL;
  540:         }
  541:       if (cp) g_free (cp);
  542: 
  543:       up = utf8_str = xmalloc (strlen (str) + nr_bad * 4 + 1);
  544:       p = (unsigned char *)str;
  545: 
  546:       while (! (cp = g_locale_to_utf8 ((char *)p, -1, &bytes_read,
  547:                                        &bytes_written, &error))
  548:              && error->code == G_CONVERT_ERROR_ILLEGAL_SEQUENCE)
  549:         {
  550:           strncpy (up, (char *)p, bytes_written);
  551:           sprintf (up + bytes_written, "\\%03o", p[bytes_written]);
  552:           up[bytes_written+4] = '\0';
  553:           up += bytes_written+4;
  554:           p += bytes_written+1;
  555:           g_error_free (error);
  556:           error = NULL;
  557:         }
  558: 
  559:       if (cp) 
  560:         {
  561:           strcat (utf8_str, cp);
  562:           g_free (cp);
  563:         }
  564:       if (error) 
  565:         {
  566:           g_error_free (error);
  567:           error = NULL;
  568:         }
  569:     }
  570:   return utf8_str;
  571: }
  572: 
  573: 
  574: ^L
  575: /***********************************************************************
  576:     General functions for creating widgets, resizing, events, e.t.c.
  577:  ***********************************************************************/
  578: 
  579: /* Make a geometry string and pass that to GTK.  It seems this is the
  580:    only way to get geometry position right if the user explicitly
  581:    asked for a position when starting Emacs.
  582:    F is the frame we shall set geometry for.  */
  583: 
  584: static void
  585: xg_set_geometry (f)
  586:      FRAME_PTR f;
  587: {
  588:   if (f->size_hint_flags & USPosition)
  589:   {
  590:     int left = f->left_pos;
  591:     int xneg = f->size_hint_flags & XNegative;
  592:     int top = f->top_pos;
  593:     int yneg = f->size_hint_flags & YNegative;
  594:     char geom_str[32];
  595: 
  596:     if (xneg)
  597:       left = -left;
  598:     if (yneg)
  599:       top = -top;
  600: 
  601:     sprintf (geom_str, "=%dx%d%c%d%c%d",
  602:              FRAME_PIXEL_WIDTH (f),
  603:              FRAME_TOTAL_PIXEL_HEIGHT (f),
  604:              (xneg ? '-' : '+'), left,
  605:              (yneg ? '-' : '+'), top);
  606: 
  607:     if (!gtk_window_parse_geometry (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
  608:                                     geom_str))
  609:       fprintf (stderr, "Failed to parse: '%s'\n", geom_str);
  610:   } else if (f->size_hint_flags & PPosition) {
  611:     gtk_window_move (GTK_WINDOW (FRAME_GTK_OUTER_WIDGET (f)),
  612:                      f->left_pos, f->top_pos);
  613:   }
  614: }
  615: 
  616: 
  617: /* Resize the outer wi