GTK eradication -- the build chain.
[sxemacs] / src / ui / Gtk / glyphs-gtk.c
1 /* X-specific Lisp objects.
2    Copyright (C) 1993, 1994 Free Software Foundation, Inc.
3    Copyright (C) 1995 Board of Trustees, University of Illinois.
4    Copyright (C) 1995 Tinker Systems
5    Copyright (C) 1995, 1996 Ben Wing
6    Copyright (C) 1995 Sun Microsystems
7
8 This file is part of SXEmacs
9
10 SXEmacs is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 SXEmacs is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
22
23
24 /* Synched up with: Not in FSF. */
25
26 /* Original author: Jamie Zawinski for 19.8
27    font-truename stuff added by Jamie Zawinski for 19.10
28    subwindow support added by Chuck Thompson
29    additional XPM support added by Chuck Thompson
30    initial X-Face support added by Stig
31    rewritten/restructured by Ben Wing for 19.12/19.13
32    GIF/JPEG support added by Ben Wing for 19.14
33    PNG support added by Bill Perry for 19.14
34    Improved GIF/JPEG support added by Bill Perry for 19.14
35    Cleanup/simplification of error handling by Ben Wing for 19.14
36    Pointer/icon overhaul, more restructuring by Ben Wing for 19.14
37    GIF support changed to external GIFlib 3.1 by Jareth Hein for 21.0
38    Many changes for color work and optimizations by Jareth Hein for 21.0
39    Switch of GIF/JPEG/PNG to new EImage intermediate code by Jareth Hein for 21.0
40    TIFF code by Jareth Hein for 21.0
41    GIF/JPEG/PNG/TIFF code moved to new glyph-eimage.c for 21.0
42    Gtk version by William Perry for 21.1
43
44    TODO:
45    Support the GrayScale, StaticColor and StaticGray visual classes.
46    Convert images.el to C and stick it in here?
47  */
48
49 #include <config.h>
50 #include "lisp.h"
51 #include "lstream.h"
52 #include "console-gtk.h"
53 #include "ui/glyphs.h"
54 #include "glyphs-gtk.h"
55 #include "objects-gtk.h"
56 #include "gui-gtk.h"
57 #include "ui-gtk.h"
58
59 #include "buffer.h"
60 #include "ui/window.h"
61 #include "ui/frame.h"
62 #include "ui/insdel.h"
63 #include "opaque.h"
64 #include "ui/faces.h"
65 #include "elhash.h"
66 #include "events/events.h"
67
68 #include "ui/imgproc.h"
69
70 #include "sysfile.h"
71
72 #include <setjmp.h>
73
74 #if defined (HAVE_XPM)
75 #include <X11/xpm.h>
76 #endif
77
78 #ifdef FILE_CODING
79 #include "mule/file-coding.h"
80 #endif
81
82 extern void enqueue_gtk_dispatch_event(Lisp_Object event);
83
84 /* Widget callback hash table callback slot. */
85 #define WIDGET_GLYPH_SLOT 0
86
87 #if SXE_INTBITS == 32
88 # define FOUR_BYTE_TYPE unsigned int
89 #elif SXE_LONGBITS == 32
90 # define FOUR_BYTE_TYPE unsigned long
91 #elif SXE_SHORTBITS == 32
92 # define FOUR_BYTE_TYPE unsigned short
93 #else
94 #error What kind of strange-ass system are we running on?
95 #endif
96
97 DECLARE_IMAGE_INSTANTIATOR_FORMAT(nothing);
98 DECLARE_IMAGE_INSTANTIATOR_FORMAT(string);
99 DECLARE_IMAGE_INSTANTIATOR_FORMAT(formatted_string);
100 DECLARE_IMAGE_INSTANTIATOR_FORMAT(inherit);
101 #ifdef HAVE_JPEG
102 DECLARE_IMAGE_INSTANTIATOR_FORMAT(jpeg);
103 #endif
104 #ifdef HAVE_TIFF
105 DECLARE_IMAGE_INSTANTIATOR_FORMAT(tiff);
106 #endif
107 #ifdef HAVE_PNG
108 DECLARE_IMAGE_INSTANTIATOR_FORMAT(png);
109 #endif
110 #ifdef HAVE_GIF
111 DECLARE_IMAGE_INSTANTIATOR_FORMAT(gif);
112 #endif
113
114 #if 1
115 DECLARE_IMAGE_INSTANTIATOR_FORMAT(rawrgb);
116 DECLARE_IMAGE_INSTANTIATOR_FORMAT(rawrgba);
117 #endif
118
119 #ifdef HAVE_XFACE
120 DEFINE_DEVICE_IIFORMAT(gtk, xface);
121 Lisp_Object Qxface;
122 #endif
123
124 #ifdef HAVE_XPM
125 DEFINE_DEVICE_IIFORMAT(gtk, xpm);
126 #endif
127
128 DEFINE_DEVICE_IIFORMAT(gtk, xbm);
129 DEFINE_DEVICE_IIFORMAT(gtk, subwindow);
130
131 DEFINE_IMAGE_INSTANTIATOR_FORMAT(cursor_font);
132 Lisp_Object Qcursor_font;
133
134 DEFINE_IMAGE_INSTANTIATOR_FORMAT(font);
135
136 DEFINE_IMAGE_INSTANTIATOR_FORMAT(autodetect);
137
138 #ifdef HAVE_WIDGETS
139 DECLARE_IMAGE_INSTANTIATOR_FORMAT(layout);
140 DEFINE_DEVICE_IIFORMAT(gtk, widget);
141 DEFINE_DEVICE_IIFORMAT(gtk, native_layout);
142 DEFINE_DEVICE_IIFORMAT(gtk, button);
143 DEFINE_DEVICE_IIFORMAT(gtk, progress_gauge);
144 DEFINE_DEVICE_IIFORMAT(gtk, edit_field);
145 DEFINE_DEVICE_IIFORMAT(gtk, combo_box);
146 DEFINE_DEVICE_IIFORMAT(gtk, tab_control);
147 DEFINE_DEVICE_IIFORMAT(gtk, label);
148 #endif
149
150 static void update_widget_face(GtkWidget * w, Lisp_Image_Instance * ii,
151                                Lisp_Object domain);
152 static void cursor_font_instantiate(Lisp_Object image_instance,
153                                     Lisp_Object instantiator,
154                                     Lisp_Object pointer_fg,
155                                     Lisp_Object pointer_bg,
156                                     int dest_mask, Lisp_Object domain);
157
158 static gint cursor_name_to_index(const char *name);
159
160 #ifndef BitmapSuccess
161 #define BitmapSuccess           0
162 #define BitmapOpenFailed        1
163 #define BitmapFileInvalid       2
164 #define BitmapNoMemory          3
165 #endif
166
167 #include "ui/bitmaps.h"
168
169 DEFINE_IMAGE_INSTANTIATOR_FORMAT(gtk_resource);
170 Lisp_Object Q_resource_type, Q_resource_id;
171 Lisp_Object Qgtk_resource;
172 #ifdef HAVE_WIDGETS
173 Lisp_Object Qgtk_widget_instantiate_internal, Qgtk_widget_property_internal;
174 Lisp_Object Qgtk_widget_redisplay_internal, Qgtk_widget_set_style;
175 #endif
176
177 #define CONST const
178 \f
179 /************************************************************************/
180 /*                      image instance methods                          */
181 /************************************************************************/
182
183 /************************************************************************/
184 /* convert from a series of RGB triples to an XImage formated for the   */
185 /* proper display                                                       */
186 /************************************************************************/
187 static GdkImage *convert_EImage_to_GDKImage(Lisp_Object device, int width,
188                                             int height, unsigned char *pic,
189                                             unsigned long **pixtbl,
190                                             int *npixels)
191 {
192         GdkColormap *cmap;
193         GdkVisual *vis;
194         GdkImage *outimg;
195         int depth, byte_cnt, i, j;
196         int rd, gr, bl, q;
197         unsigned char *data, *ip, *dp = NULL;
198         quant_table *qtable = NULL;
199         union {
200                 FOUR_BYTE_TYPE val;
201                 char cp[4];
202         } conv;
203
204         cmap = DEVICE_GTK_COLORMAP(XDEVICE(device));
205         vis = DEVICE_GTK_VISUAL(XDEVICE(device));
206         depth = DEVICE_GTK_DEPTH(XDEVICE(device));
207
208         if (vis->type == GDK_VISUAL_GRAYSCALE
209             || vis->type == GDK_VISUAL_STATIC_COLOR
210             || vis->type == GDK_VISUAL_STATIC_GRAY) {
211                 /* #### Implement me!!! */
212                 return NULL;
213         }
214
215         if (vis->type == GDK_VISUAL_PSEUDO_COLOR) {
216                 /* Quantize the image and get a histogram while we're at it.
217                    Do this first to save memory */
218                 qtable = build_EImage_quantable(pic, width, height, 256);
219                 if (qtable == NULL)
220                         return NULL;
221         }
222
223         /* The first parameter (GdkWindow *) is allowed to be NULL if we
224          ** specify the depth */
225         outimg = gdk_image_new(GDK_IMAGE_FASTEST, vis, width, height);
226
227         if (!outimg)
228                 return NULL;
229
230         byte_cnt = outimg->bpp;
231
232         data = (unsigned char *)outimg->mem;
233
234         if (!data) {
235                 gdk_image_destroy(outimg);
236                 return NULL;
237         }
238
239         if (vis->type == GDK_VISUAL_PSEUDO_COLOR) {
240                 unsigned long pixarray[256];
241                 int pixcount, n;
242                 /* use our quantize table to allocate the colors */
243                 pixcount = 32;
244                 *pixtbl = xnew_array(unsigned long, pixcount);
245                 *npixels = 0;
246
247                 /* ### should implement a sort by popularity to assure proper allocation */
248                 n = *npixels;
249                 for (i = 0; i < qtable->num_active_colors; i++) {
250                         GdkColor color;
251                         int res;
252
253                         color.red = qtable->rm[i] ? qtable->rm[i] << 8 : 0;
254                         color.green = qtable->gm[i] ? qtable->gm[i] << 8 : 0;
255                         color.blue = qtable->bm[i] ? qtable->bm[i] << 8 : 0;
256                         res = allocate_nearest_color(cmap, vis, &color);
257                         if (res > 0 && res < 3) {
258                                 DO_REALLOC_ATOMIC(*pixtbl, pixcount, n + 1,
259                                                   unsigned long);
260                                 (*pixtbl)[n] = color.pixel;
261                                 n++;
262                         }
263                         pixarray[i] = color.pixel;
264                 }
265                 *npixels = n;
266                 ip = pic;
267                 for (i = 0; i < height; i++) {
268                         dp = data + (i * outimg->bpl);
269                         for (j = 0; j < width; j++) {
270                                 rd = *ip++;
271                                 gr = *ip++;
272                                 bl = *ip++;
273                                 conv.val =
274                                     pixarray[QUANT_GET_COLOR
275                                              (qtable, rd, gr, bl)];
276 #if WORDS_BIGENDIAN
277                                 if (outimg->byte_order == GDK_MSB_FIRST)
278                                         for (q = 4 - byte_cnt; q < 4; q++)
279                                                 *dp++ = conv.cp[q];
280                                 else
281                                         for (q = 3; q >= 4 - byte_cnt; q--)
282                                                 *dp++ = conv.cp[q];
283 #else
284                                 if (outimg->byte_order == GDK_MSB_FIRST)
285                                         for (q = byte_cnt - 1; q >= 0; q--)
286                                                 *dp++ = conv.cp[q];
287                                 else
288                                         for (q = 0; q < byte_cnt; q++)
289                                                 *dp++ = conv.cp[q];
290 #endif
291                         }
292                 }
293                 xfree(qtable);
294         } else {
295                 unsigned long rshift, gshift, bshift, rbits, gbits, bbits, junk;
296                 junk = vis->red_mask;
297                 rshift = 0;
298                 while ((junk & 0x1) == 0) {
299                         junk = junk >> 1;
300                         rshift++;
301                 }
302                 rbits = 0;
303                 while (junk != 0) {
304                         junk = junk >> 1;
305                         rbits++;
306                 }
307                 junk = vis->green_mask;
308                 gshift = 0;
309                 while ((junk & 0x1) == 0) {
310                         junk = junk >> 1;
311                         gshift++;
312                 }
313                 gbits = 0;
314                 while (junk != 0) {
315                         junk = junk >> 1;
316                         gbits++;
317                 }
318                 junk = vis->blue_mask;
319                 bshift = 0;
320                 while ((junk & 0x1) == 0) {
321                         junk = junk >> 1;
322                         bshift++;
323                 }
324                 bbits = 0;
325                 while (junk != 0) {
326                         junk = junk >> 1;
327                         bbits++;
328                 }
329                 ip = pic;
330                 for (i = 0; i < height; i++) {
331                         dp = data + (i * outimg->bpl);
332                         for (j = 0; j < width; j++) {
333                                 if (rbits > 8)
334                                         rd = *ip++ << (rbits - 8);
335                                 else
336                                         rd = *ip++ >> (8 - rbits);
337                                 if (gbits > 8)
338                                         gr = *ip++ << (gbits - 8);
339                                 else
340                                         gr = *ip++ >> (8 - gbits);
341                                 if (bbits > 8)
342                                         bl = *ip++ << (bbits - 8);
343                                 else
344                                         bl = *ip++ >> (8 - bbits);
345
346                                 conv.val =
347                                     (rd << rshift) | (gr << gshift) | (bl <<
348                                                                        bshift);
349 #if WORDS_BIGENDIAN
350                                 if (outimg->byte_order == GDK_MSB_FIRST)
351                                         for (q = 4 - byte_cnt; q < 4; q++)
352                                                 *dp++ = conv.cp[q];
353                                 else
354                                         for (q = 3; q >= 4 - byte_cnt; q--)
355                                                 *dp++ = conv.cp[q];
356 #else
357                                 if (outimg->byte_order == GDK_MSB_FIRST)
358                                         for (q = byte_cnt - 1; q >= 0; q--)
359                                                 *dp++ = conv.cp[q];
360                                 else
361                                         for (q = 0; q < byte_cnt; q++)
362                                                 *dp++ = conv.cp[q];
363 #endif
364                         }
365                 }
366         }
367         return outimg;
368 }
369
370 static void
371 gtk_print_image_instance(struct Lisp_Image_Instance *p,
372                          Lisp_Object printcharfun, int escapeflag)
373 {
374         char buf[100];
375
376         switch (IMAGE_INSTANCE_TYPE(p)) {
377         case IMAGE_MONO_PIXMAP:
378         case IMAGE_COLOR_PIXMAP:
379         case IMAGE_POINTER:
380                 write_fmt_str(printcharfun, " (0x%lx",
381                               (unsigned long)IMAGE_INSTANCE_GTK_PIXMAP(p));
382                 if (IMAGE_INSTANCE_GTK_MASK(p)) {
383                         write_fmt_str(printcharfun, "/0x%lx",
384                                       (unsigned long)IMAGE_INSTANCE_GTK_MASK(p));
385                 }
386                 write_c_string(")", printcharfun);
387                 break;
388 #if HAVE_SUBWINDOWS
389         case IMAGE_SUBWINDOW:
390                 /* #### implement me */
391 #endif
392         default:
393                 break;
394         }
395 }
396
397 static void gtk_finalize_image_instance(struct Lisp_Image_Instance *p)
398 {
399         if (!p->data)
400                 return;
401
402         if (DEVICE_LIVE_P(XDEVICE(p->device))) {
403                 if (0) ;
404 #ifdef HAVE_WIDGETS
405                 if (IMAGE_INSTANCE_TYPE(p) == IMAGE_WIDGET) {
406                         if (IMAGE_INSTANCE_SUBWINDOW_ID(p)) {
407                                 gtk_widget_destroy(IMAGE_INSTANCE_SUBWINDOW_ID
408                                                    (p));
409
410                                 /* We can release the callbacks again. */
411                                 /* #### FIXME! */
412                                 /* ungcpro_popup_callbacks (...); */
413
414                                 /* IMAGE_INSTANCE_GTK_WIDGET_ID (p) = 0; */
415                                 IMAGE_INSTANCE_GTK_CLIPWIDGET(p) = 0;
416                         }
417                 }
418 #endif
419                 else if (IMAGE_INSTANCE_TYPE(p) == IMAGE_SUBWINDOW) {
420                         abort();
421                 } else {
422                         int i;
423                         if (IMAGE_INSTANCE_PIXMAP_TIMEOUT(p))
424                                 disable_glyph_animated_timeout
425                                     (IMAGE_INSTANCE_PIXMAP_TIMEOUT(p));
426
427                         if (IMAGE_INSTANCE_GTK_MASK(p) &&
428                             IMAGE_INSTANCE_GTK_MASK(p) !=
429                             IMAGE_INSTANCE_GTK_PIXMAP(p))
430                                 gdk_pixmap_unref(IMAGE_INSTANCE_GTK_MASK(p));
431                         IMAGE_INSTANCE_PIXMAP_MASK(p) = 0;
432
433                         if (IMAGE_INSTANCE_GTK_PIXMAP_SLICES(p)) {
434                                 for (i = 0;
435                                      i < IMAGE_INSTANCE_PIXMAP_MAXSLICE(p); i++)
436                                         if (IMAGE_INSTANCE_GTK_PIXMAP_SLICE
437                                             (p, i)) {
438                                                 gdk_pixmap_unref
439                                                     (IMAGE_INSTANCE_GTK_PIXMAP_SLICE
440                                                      (p, i));
441                                                 IMAGE_INSTANCE_GTK_PIXMAP_SLICE
442                                                     (p, i) = 0;
443                                         }
444                                 xfree(IMAGE_INSTANCE_GTK_PIXMAP_SLICES(p));
445                                 IMAGE_INSTANCE_GTK_PIXMAP_SLICES(p) = 0;
446                         }
447
448                         if (IMAGE_INSTANCE_GTK_CURSOR(p)) {
449                                 gdk_cursor_destroy(IMAGE_INSTANCE_GTK_CURSOR
450                                                    (p));
451                                 IMAGE_INSTANCE_GTK_CURSOR(p) = 0;
452                         }
453                 }
454
455 #if 0
456                 /* #### BILL!!! */
457                 if (IMAGE_INSTANCE_GTK_NPIXELS(p) != 0) {
458                         XFreeColors(dpy,
459                                     IMAGE_INSTANCE_GTK_COLORMAP(p),
460                                     IMAGE_INSTANCE_GTK_PIXELS(p),
461                                     IMAGE_INSTANCE_GTK_NPIXELS(p), 0);
462                         IMAGE_INSTANCE_GTK_NPIXELS(p) = 0;
463                 }
464 #endif
465         }
466
467         if (IMAGE_INSTANCE_TYPE(p) != IMAGE_WIDGET
468             && IMAGE_INSTANCE_TYPE(p) != IMAGE_SUBWINDOW
469             && IMAGE_INSTANCE_GTK_PIXELS(p)) {
470                 xfree(IMAGE_INSTANCE_GTK_PIXELS(p));
471                 IMAGE_INSTANCE_GTK_PIXELS(p) = 0;
472         }
473
474         xfree(p->data);
475         p->data = 0;
476 }
477
478 static int
479 gtk_image_instance_equal(struct Lisp_Image_Instance *p1,
480                          struct Lisp_Image_Instance *p2, int depth)
481 {
482         switch (IMAGE_INSTANCE_TYPE(p1)) {
483         case IMAGE_MONO_PIXMAP:
484         case IMAGE_COLOR_PIXMAP:
485         case IMAGE_POINTER:
486                 if (IMAGE_INSTANCE_GTK_COLORMAP(p1) !=
487                     IMAGE_INSTANCE_GTK_COLORMAP(p2)
488                     || IMAGE_INSTANCE_GTK_NPIXELS(p1) !=
489                     IMAGE_INSTANCE_GTK_NPIXELS(p2))
490                         return 0;
491 #if HAVE_SUBWINDOWS
492         case IMAGE_SUBWINDOW:
493                 /* #### implement me */
494 #endif
495                 break;
496         default:
497                 break;
498         }
499
500         return 1;
501 }
502
503 static unsigned long
504 gtk_image_instance_hash(struct Lisp_Image_Instance *p, int depth)
505 {
506         switch (IMAGE_INSTANCE_TYPE(p)) {
507         case IMAGE_MONO_PIXMAP:
508         case IMAGE_COLOR_PIXMAP:
509         case IMAGE_POINTER:
510                 return IMAGE_INSTANCE_GTK_NPIXELS(p);
511 #if HAVE_SUBWINDOWS
512         case IMAGE_SUBWINDOW:
513                 /* #### implement me */
514                 return 0;
515 #endif
516         default:
517                 return 0;
518         }
519 }
520
521 /* Set all the slots in an image instance structure to reasonable
522    default values.  This is used somewhere within an instantiate
523    method.  It is assumed that the device slot within the image
524    instance is already set -- this is the case when instantiate
525    methods are called. */
526
527 static void
528 gtk_initialize_pixmap_image_instance(struct Lisp_Image_Instance *ii,
529                                      int slices, enum image_instance_type type)
530 {
531         ii->data = xnew_and_zero(struct gtk_image_instance_data);
532         IMAGE_INSTANCE_PIXMAP_MAXSLICE(ii) = slices;
533         IMAGE_INSTANCE_GTK_PIXMAP_SLICES(ii) =
534             xnew_array_and_zero(GdkPixmap *, slices);
535         IMAGE_INSTANCE_TYPE(ii) = type;
536         IMAGE_INSTANCE_PIXMAP_FILENAME(ii) = Qnil;
537         IMAGE_INSTANCE_PIXMAP_MASK_FILENAME(ii) = Qnil;
538         IMAGE_INSTANCE_PIXMAP_HOTSPOT_X(ii) = Qnil;
539         IMAGE_INSTANCE_PIXMAP_HOTSPOT_Y(ii) = Qnil;
540         IMAGE_INSTANCE_PIXMAP_FG(ii) = Qnil;
541         IMAGE_INSTANCE_PIXMAP_BG(ii) = Qnil;
542 }
543 \f
544 /************************************************************************/
545 /*                        pixmap file functions                         */
546 /************************************************************************/
547
548 /* Where bitmaps are; initialized from resource database */
549 Lisp_Object Vgtk_bitmap_file_path;
550
551 #ifndef BITMAPDIR
552 #define BITMAPDIR "/usr/include/X11/bitmaps"
553 #endif
554
555 /* Given a pixmap filename, look through all of the "standard" places
556    where the file might be located.  Return a full pathname if found;
557    otherwise, return Qnil. */
558
559 static Lisp_Object gtk_locate_pixmap_file(Lisp_Object name)
560 {
561         /* This function can GC if IN_REDISPLAY is false */
562
563         /* Check non-absolute pathnames with a directory component relative to
564            the search path; that's the way Xt does it. */
565         /* #### Unix-specific */
566         if (XSTRING_BYTE(name, 0) == '/' ||
567             (XSTRING_BYTE(name, 0) == '.' &&
568              (XSTRING_BYTE(name, 1) == '/' ||
569               (XSTRING_BYTE(name, 1) == '.' &&
570                (XSTRING_BYTE(name, 2) == '/'))))) {
571                 if (!NILP(Ffile_readable_p(name)))
572                         return name;
573                 else
574                         return Qnil;
575         }
576
577         if (NILP(Vdefault_gtk_device))
578                 /* This may occur during intialization. */
579                 return Qnil;
580
581         if (NILP(Vgtk_bitmap_file_path)) {
582                 Vgtk_bitmap_file_path = nconc2(Vgtk_bitmap_file_path,
583                                                (decode_path(BITMAPDIR)));
584         }
585
586         {
587                 Lisp_Object found;
588                 if (locate_file(Vgtk_bitmap_file_path, name, Qnil, &found, R_OK)
589                     < 0) {
590                         Lisp_Object temp = list1(Vdata_directory);
591                         struct gcpro gcpro1;
592
593                         GCPRO1(temp);
594                         locate_file(temp, name, Qnil, &found, R_OK);
595                         UNGCPRO;
596                 }
597
598                 return found;
599         }
600 }
601
602 static Lisp_Object locate_pixmap_file(Lisp_Object name)
603 {
604         return gtk_locate_pixmap_file(name);
605 }
606 \f
607 /************************************************************************/
608 /*                           cursor functions                           */
609 /************************************************************************/
610
611 /* Check that this server supports cursors of size WIDTH * HEIGHT.  If
612    not, signal an error.  INSTANTIATOR is only used in the error
613    message. */
614
615 static void
616 check_pointer_sizes(unsigned int width, unsigned int height,
617                     Lisp_Object instantiator)
618 {
619         /* #### BILL!!! There is no way to call XQueryBestCursor from Gdk! */
620 #if 0
621         unsigned int best_width, best_height;
622         if (!XQueryBestCursor(DisplayOfScreen(xs), RootWindowOfScreen(xs),
623                               width, height, &best_width, &best_height))
624                 /* this means that an X error of some sort occurred (we trap
625                    these so they're not fatal). */
626                 signal_simple_error("XQueryBestCursor() failed?", instantiator);
627
628         if (width > best_width || height > best_height)
629                 error_with_frob(instantiator,
630                                 "pointer too large (%dx%d): "
631                                 "server requires %dx%d or smaller",
632                                 width, height, best_width, best_height);
633 #endif
634 }
635
636 static void
637 generate_cursor_fg_bg(Lisp_Object device, Lisp_Object * foreground,
638                       Lisp_Object * background, GdkColor * xfg, GdkColor * xbg)
639 {
640         if (!NILP(*foreground) && !COLOR_INSTANCEP(*foreground))
641                 *foreground =
642                     Fmake_color_instance(*foreground, device,
643                                          encode_error_behavior_flag(ERROR_ME));
644         if (COLOR_INSTANCEP(*foreground))
645                 *xfg = *COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE(*foreground));
646         else {
647                 xfg->pixel = 0;
648                 xfg->red = xfg->green = xfg->blue = 0;
649         }
650
651         if (!NILP(*background) && !COLOR_INSTANCEP(*background))
652                 *background =
653                     Fmake_color_instance(*background, device,
654                                          encode_error_behavior_flag(ERROR_ME));
655         if (COLOR_INSTANCEP(*background))
656                 *xbg = *COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE(*background));
657         else {
658                 xbg->pixel = 0;
659                 xbg->red = xbg->green = xbg->blue = ~0;
660         }
661 }
662
663 static void
664 maybe_recolor_cursor(Lisp_Object image_instance, Lisp_Object foreground,
665                      Lisp_Object background)
666 {
667 #if 0
668         /* #### BILL!!! */
669         Lisp_Object device = XIMAGE_INSTANCE_DEVICE(image_instance);
670         GdkColor xfg, xbg;
671
672         generate_cursor_fg_bg(device, &foreground, &background, &xfg, &xbg);
673         if (!NILP(foreground) || !NILP(background)) {
674                 XRecolorCursor(DEVICE_X_DISPLAY(XDEVICE(device)),
675                                XIMAGE_INSTANCE_GTK_CURSOR(image_instance),
676                                &xfg, &xbg);
677                 XIMAGE_INSTANCE_PIXMAP_FG(image_instance) = foreground;
678                 XIMAGE_INSTANCE_PIXMAP_BG(image_instance) = background;
679         }
680 #else
681         /* stderr_out ("Don't know how to recolor cursors in Gtk!\n"); */
682 #endif
683 }
684 \f
685 /************************************************************************/
686 /*                        color pixmap functions                        */
687 /************************************************************************/
688
689 /* Initialize an image instance from an XImage.
690
691    DEST_MASK specifies the mask of allowed image types.
692
693    PIXELS and NPIXELS specify an array of pixels that are used in
694    the image.  These need to be kept around for the duration of the
695    image.  When the image instance is freed, XFreeColors() will
696    automatically be called on all the pixels specified here; thus,
697    you should have allocated the pixels yourself using XAllocColor()
698    or the like.  The array passed in is used directly without
699    being copied, so it should be heap data created with xmalloc().
700    It will be freed using xfree() when the image instance is
701    destroyed.
702
703    If this fails, signal an error.  INSTANTIATOR is only used
704    in the error message.
705
706    #### This should be able to handle conversion into `pointer'.
707    Use the same code as for `xpm'. */
708
709 static void
710 init_image_instance_from_gdk_image(struct Lisp_Image_Instance *ii,
711                                    GdkImage * gdk_image,
712                                    int dest_mask,
713                                    GdkColormap * cmap,
714                                    unsigned long *pixels,
715                                    int npixels,
716                                    int slices, Lisp_Object instantiator)
717 {
718         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
719         GdkGC *gc;
720         GdkWindow *d;
721         GdkPixmap *pixmap;
722
723         if (!DEVICE_GTK_P(XDEVICE(device)))
724                 signal_simple_error("Not a Gtk device", device);
725
726         d = GET_GTK_WIDGET_WINDOW(DEVICE_GTK_APP_SHELL(XDEVICE(device)));
727
728         if (!(dest_mask & IMAGE_COLOR_PIXMAP_MASK))
729                 incompatible_image_types(instantiator, dest_mask,
730                                          IMAGE_COLOR_PIXMAP_MASK);
731
732         pixmap =
733             gdk_pixmap_new(d, gdk_image->width, gdk_image->height,
734                            gdk_image->depth);
735         if (!pixmap)
736                 signal_simple_error("Unable to create pixmap", instantiator);
737
738         gc = gdk_gc_new(pixmap);
739         if (!gc) {
740                 gdk_pixmap_unref(pixmap);
741                 signal_simple_error("Unable to create GC", instantiator);
742         }
743
744         gdk_draw_image(GDK_DRAWABLE(pixmap), gc, gdk_image,
745                        0, 0, 0, 0, gdk_image->width, gdk_image->height);
746
747         gdk_gc_destroy(gc);
748
749         gtk_initialize_pixmap_image_instance(ii, slices, IMAGE_COLOR_PIXMAP);
750
751         IMAGE_INSTANCE_PIXMAP_FILENAME(ii) =
752             find_keyword_in_vector(instantiator, Q_file);
753
754         IMAGE_INSTANCE_GTK_PIXMAP(ii) = pixmap;
755         IMAGE_INSTANCE_GTK_MASK(ii) = 0;
756         IMAGE_INSTANCE_PIXMAP_WIDTH(ii) = gdk_image->width;
757         IMAGE_INSTANCE_PIXMAP_HEIGHT(ii) = gdk_image->height;
758         IMAGE_INSTANCE_PIXMAP_DEPTH(ii) = gdk_image->depth;
759         IMAGE_INSTANCE_GTK_COLORMAP(ii) = cmap;
760         IMAGE_INSTANCE_GTK_PIXELS(ii) = pixels;
761         IMAGE_INSTANCE_GTK_NPIXELS(ii) = npixels;
762 }
763
764 #if 0
765 void init_image_instance_from_gdk_pixmap(struct Lisp_Image_Instance *ii,
766                                          struct device *device,
767                                          GdkPixmap * gdk_pixmap,
768                                          int dest_mask,
769                                          Lisp_Object instantiator)
770 {
771         GdkWindow *d;
772         gint width, height, depth;
773
774         if (!DEVICE_GTK_P(device))
775                 abort();
776
777         IMAGE_INSTANCE_DEVICE(ii) = device;
778         IMAGE_INSTANCE_TYPE(ii) = IMAGE_COLOR_PIXMAP;
779
780         d = GET_GTK_WIDGET_WINDOW(DEVICE_GTK_APP_SHELL(device));
781
782         if (!(dest_mask & IMAGE_COLOR_PIXMAP_MASK))
783                 incompatible_image_types(instantiator, dest_mask,
784                                          IMAGE_COLOR_PIXMAP_MASK);
785
786         gtk_initialize_pixmap_image_instance(ii, IMAGE_COLOR_PIXMAP);
787
788         gdk_window_get_geometry(gdk_pixmap, NULL, NULL, &width, &height,
789                                 &depth);
790
791         IMAGE_INSTANCE_PIXMAP_FILENAME(ii) = Qnil;
792         IMAGE_INSTANCE_GTK_PIXMAP(ii) = gdk_pixmap;
793         IMAGE_INSTANCE_GTK_MASK(ii) = 0;
794         IMAGE_INSTANCE_PIXMAP_WIDTH(ii) = width;
795         IMAGE_INSTANCE_PIXMAP_HEIGHT(ii) = height;
796         IMAGE_INSTANCE_PIXMAP_DEPTH(ii) = depth;
797         IMAGE_INSTANCE_GTK_COLORMAP(ii) = gdk_window_get_colormap(gdk_pixmap);
798         IMAGE_INSTANCE_GTK_PIXELS(ii) = 0;
799         IMAGE_INSTANCE_GTK_NPIXELS(ii) = 0;
800 }
801 #endif
802
803 static void
804 image_instance_add_gdk_image(Lisp_Image_Instance * ii,
805                              GdkImage * gdk_image,
806                              int slice, Lisp_Object instantiator)
807 {
808         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
809         GdkWindow *d;
810         GdkPixmap *pixmap;
811         GdkGC *gc;
812
813         d = GET_GTK_WIDGET_WINDOW(DEVICE_GTK_APP_SHELL(XDEVICE(device)));
814
815         pixmap =
816             gdk_pixmap_new(d, gdk_image->width, gdk_image->height,
817                            gdk_image->depth);
818
819         if (!pixmap)
820                 signal_simple_error("Unable to create pixmap", instantiator);
821
822         gc = gdk_gc_new(pixmap);
823
824         if (!gc) {
825                 gdk_pixmap_unref(pixmap);
826                 signal_simple_error("Unable to create GC", instantiator);
827         }
828
829         gdk_draw_image(GDK_DRAWABLE(pixmap), gc, gdk_image, 0, 0, 0, 0,
830                        gdk_image->width, gdk_image->height);
831
832         gdk_gc_destroy(gc);
833
834         IMAGE_INSTANCE_GTK_PIXMAP_SLICE(ii, slice) = pixmap;
835 }
836
837 static void
838 gtk_init_image_instance_from_eimage(struct Lisp_Image_Instance *ii,
839                                     int width, int height,
840                                     int slices,
841                                     unsigned char *eimage,
842                                     int dest_mask,
843                                     Lisp_Object instantiator,
844                                     Lisp_Object domain)
845 {
846         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
847         GdkColormap *cmap = DEVICE_GTK_COLORMAP(XDEVICE(device));
848         unsigned long *pixtbl = NULL;
849         int npixels = 0;
850         int slice;
851         GdkImage *gdk_image;
852
853         for (slice = 0; slice < slices; slice++) {
854                 gdk_image =
855                     convert_EImage_to_GDKImage(device, width, height, eimage,
856                                                &pixtbl, &npixels);
857                 if (!gdk_image) {
858                         if (pixtbl)
859                                 xfree(pixtbl);
860                         signal_image_error
861                             ("EImage to GdkImage conversion failed",
862                              instantiator);
863                 }
864
865                 if (slice == 0)
866                         /* Now create the pixmap and set up the image instance */
867                         init_image_instance_from_gdk_image(ii, gdk_image,
868                                                            dest_mask, cmap,
869                                                            pixtbl, npixels,
870                                                            slices,
871                                                            instantiator);
872                 else
873                         image_instance_add_gdk_image(ii, gdk_image, slice,
874                                                      instantiator);
875
876                 if (gdk_image) {
877                         gdk_image_destroy(gdk_image);
878                 }
879                 gdk_image = 0;
880         }
881 }
882
883 /* Given inline data for a mono pixmap, create and return the
884    corresponding X object. */
885
886 static GdkPixmap *pixmap_from_xbm_inline(Lisp_Object device, int width,
887                                          int height,
888                                          /* Note that data is in ext-format! */
889                                          CONST Extbyte * bits)
890 {
891         return (gdk_bitmap_create_from_data
892                 (GET_GTK_WIDGET_WINDOW(DEVICE_GTK_APP_SHELL(XDEVICE(device))),
893                  (char *)bits, width, height));
894 }
895
896 /* Given inline data for a mono pixmap, initialize the given
897    image instance accordingly. */
898
899 static void
900 init_image_instance_from_xbm_inline(struct Lisp_Image_Instance *ii,
901                                     int width, int height,
902                                     /* Note that data is in ext-format! */
903                                     CONST char *bits,
904                                     Lisp_Object instantiator,
905                                     Lisp_Object pointer_fg,
906                                     Lisp_Object pointer_bg,
907                                     int dest_mask,
908                                     GdkPixmap * mask, Lisp_Object mask_filename)
909 {
910         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
911         Lisp_Object foreground =
912             find_keyword_in_vector(instantiator, Q_foreground);
913         Lisp_Object background =
914             find_keyword_in_vector(instantiator, Q_background);
915         GdkColor fg;
916         GdkColor bg;
917         enum image_instance_type type;
918         GdkWindow *draw =
919             GET_GTK_WIDGET_WINDOW(DEVICE_GTK_APP_SHELL(XDEVICE(device)));
920         GdkColormap *cmap = DEVICE_GTK_COLORMAP(XDEVICE(device));
921         GdkColor black;
922         GdkColor white;
923
924         gdk_color_black(cmap, &black);
925         gdk_color_white(cmap, &white);
926
927         if (!DEVICE_GTK_P(XDEVICE(device)))
928                 signal_simple_error("Not a Gtk device", device);
929
930         if ((dest_mask & IMAGE_MONO_PIXMAP_MASK) &&
931             (dest_mask & IMAGE_COLOR_PIXMAP_MASK)) {
932                 if (!NILP(foreground) || !NILP(background))
933                         type = IMAGE_COLOR_PIXMAP;
934                 else
935                         type = IMAGE_MONO_PIXMAP;
936         } else if (dest_mask & IMAGE_MONO_PIXMAP_MASK)
937                 type = IMAGE_MONO_PIXMAP;
938         else if (dest_mask & IMAGE_COLOR_PIXMAP_MASK)
939                 type = IMAGE_COLOR_PIXMAP;
940         else if (dest_mask & IMAGE_POINTER_MASK)
941                 type = IMAGE_POINTER;
942         else
943                 incompatible_image_types(instantiator, dest_mask,
944                                          IMAGE_MONO_PIXMAP_MASK |
945                                          IMAGE_COLOR_PIXMAP_MASK |
946                                          IMAGE_POINTER_MASK);
947
948         gtk_initialize_pixmap_image_instance(ii, 1, type);
949         IMAGE_INSTANCE_PIXMAP_WIDTH(ii) = width;
950         IMAGE_INSTANCE_PIXMAP_HEIGHT(ii) = height;
951         IMAGE_INSTANCE_PIXMAP_FILENAME(ii) =
952             find_keyword_in_vector(instantiator, Q_file);
953
954         switch (type) {
955         case IMAGE_MONO_PIXMAP:
956                 {
957                         IMAGE_INSTANCE_GTK_PIXMAP(ii) =
958                             pixmap_from_xbm_inline(device, width, height,
959                                                    (Extbyte *) bits);
960                 }
961                 break;
962
963         case IMAGE_COLOR_PIXMAP:
964                 {
965                         gint d = DEVICE_GTK_DEPTH(XDEVICE(device));
966
967                         if (!NILP(foreground) && !COLOR_INSTANCEP(foreground))
968                                 foreground =
969                                     Fmake_color_instance(foreground, device,
970                                                          encode_error_behavior_flag
971                                                          (ERROR_ME));
972
973                         if (COLOR_INSTANCEP(foreground))
974                                 fg = *COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE
975                                                                (foreground));
976
977                         if (!NILP(background) && !COLOR_INSTANCEP(background))
978                                 background =
979                                     Fmake_color_instance(background, device,
980                                                          encode_error_behavior_flag
981                                                          (ERROR_ME));
982
983                         if (COLOR_INSTANCEP(background))
984                                 bg = *COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE
985                                                                (background));
986
987                         /* We used to duplicate the pixels using XAllocColor(), to protect
988                            against their getting freed.  Just as easy to just store the
989                            color instances here and GC-protect them, so this doesn't
990                            happen. */
991                         IMAGE_INSTANCE_PIXMAP_FG(ii) = foreground;
992                         IMAGE_INSTANCE_PIXMAP_BG(ii) = background;
993                         IMAGE_INSTANCE_GTK_PIXMAP(ii) =
994                             gdk_pixmap_create_from_data(draw, (char *)bits,
995                                                         width, height, d, &fg,
996                                                         &bg);
997                         IMAGE_INSTANCE_PIXMAP_DEPTH(ii) = d;
998                 }
999                 break;
1000
1001         case IMAGE_POINTER:
1002                 {
1003                         GdkColor fg_color, bg_color;
1004                         GdkPixmap *source;
1005
1006                         check_pointer_sizes(width, height, instantiator);
1007
1008                         source =
1009                             gdk_pixmap_create_from_data(draw, (char *)bits,
1010                                                         width, height, 1,
1011                                                         &black, &white);
1012
1013                         if (NILP(foreground))
1014                                 foreground = pointer_fg;
1015                         if (NILP(background))
1016                                 background = pointer_bg;
1017                         generate_cursor_fg_bg(device, &foreground, &background,
1018                                               &fg_color, &bg_color);
1019
1020                         IMAGE_INSTANCE_PIXMAP_FG(ii) = foreground;
1021                         IMAGE_INSTANCE_PIXMAP_BG(ii) = background;
1022                         IMAGE_INSTANCE_PIXMAP_HOTSPOT_X(ii) =
1023                             find_keyword_in_vector(instantiator, Q_hotspot_x);
1024                         IMAGE_INSTANCE_PIXMAP_HOTSPOT_Y(ii) =
1025                             find_keyword_in_vector(instantiator, Q_hotspot_y);
1026                         IMAGE_INSTANCE_GTK_CURSOR(ii) =
1027                             gdk_cursor_new_from_pixmap(source, mask, &fg_color,
1028                                                        &bg_color,
1029                                                        !NILP
1030                                                        (IMAGE_INSTANCE_PIXMAP_HOTSPOT_X
1031                                                         (ii)) ?
1032                                                        XINT
1033                                                        (IMAGE_INSTANCE_PIXMAP_HOTSPOT_X
1034                                                         (ii)) : 0,
1035                                                        !NILP
1036                                                        (IMAGE_INSTANCE_PIXMAP_HOTSPOT_Y
1037                                                         (ii)) ?
1038                                                        XINT
1039                                                        (IMAGE_INSTANCE_PIXMAP_HOTSPOT_Y
1040                                                         (ii)) : 0);
1041                 }
1042                 break;
1043
1044         default:
1045                 abort();
1046         }
1047 }
1048
1049 static void
1050 xbm_instantiate_1(Lisp_Object image_instance, Lisp_Object instantiator,
1051                   Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1052                   int dest_mask, int width, int height,
1053                   /* Note that data is in ext-format! */
1054                   CONST char *bits)
1055 {
1056         Lisp_Object mask_data =
1057             find_keyword_in_vector(instantiator, Q_mask_data);
1058         Lisp_Object mask_file =
1059             find_keyword_in_vector(instantiator, Q_mask_file);
1060         struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1061         GdkPixmap *mask = 0;
1062         CONST char *gcc_may_you_rot_in_hell;
1063
1064         if (!NILP(mask_data)) {
1065                 TO_EXTERNAL_FORMAT(LISP_STRING, XCAR(XCDR(XCDR(mask_data))),
1066                                    C_STRING_ALLOCA, gcc_may_you_rot_in_hell,
1067                                    Qfile_name);
1068                 mask =
1069                     pixmap_from_xbm_inline(IMAGE_INSTANCE_DEVICE(ii),
1070                                            XINT(XCAR(mask_data)),
1071                                            XINT(XCAR(XCDR(mask_data))),
1072                                            (CONST unsigned char *)
1073                                            gcc_may_you_rot_in_hell);
1074         }
1075
1076         init_image_instance_from_xbm_inline(ii, width, height, bits,
1077                                             instantiator, pointer_fg,
1078                                             pointer_bg, dest_mask, mask,
1079                                             mask_file);
1080 }
1081
1082 /* Instantiate method for XBM's. */
1083
1084 static void
1085 gtk_xbm_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1086                     Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1087                     int dest_mask, Lisp_Object domain)
1088 {
1089         Lisp_Object data = find_keyword_in_vector(instantiator, Q_data);
1090         CONST char *gcc_go_home;
1091
1092         assert(!NILP(data));
1093
1094         TO_EXTERNAL_FORMAT(LISP_STRING, XCAR(XCDR(XCDR(data))),
1095                            C_STRING_ALLOCA, gcc_go_home, Qbinary);
1096
1097         xbm_instantiate_1(image_instance, instantiator, pointer_fg,
1098                           pointer_bg, dest_mask, XINT(XCAR(data)),
1099                           XINT(XCAR(XCDR(data))), gcc_go_home);
1100 }
1101 \f
1102 #ifdef HAVE_XPM
1103 /**********************************************************************
1104  *                             XPM                                    *
1105  **********************************************************************/
1106
1107 /* strcasecmp() is not sufficiently portable or standard,
1108    and it's easier just to write our own. */
1109 static int ascii_strcasecmp(const char *s1, const char *s2)
1110 {
1111         while (1) {
1112                 char c1 = *s1++;
1113                 char c2 = *s2++;
1114                 if (c1 >= 'A' && c1 <= 'Z')
1115                         c1 += 'a' - 'A';
1116                 if (c2 >= 'A' && c2 <= 'Z')
1117                         c2 += 'a' - 'A';
1118                 if (c1 != c2)
1119                         return c1 - c2;
1120                 if (c1 == '\0')
1121                         return 0;
1122         }
1123 }
1124
1125 struct color_symbol {
1126         char *name;
1127         GdkColor color;
1128 };
1129
1130 static struct color_symbol *extract_xpm_color_names(Lisp_Object device,
1131                                                     Lisp_Object domain,
1132                                                     Lisp_Object
1133                                                     color_symbol_alist,
1134                                                     int *nsymbols)
1135 {
1136         /* This function can GC */
1137         Lisp_Object rest;
1138         Lisp_Object results = Qnil;
1139         int i, j;
1140         struct color_symbol *colortbl;
1141         struct gcpro gcpro1, gcpro2;
1142
1143         GCPRO2(results, device);
1144
1145         /* We built up results to be (("name" . #<color>) ...) so that if an
1146            error happens we don't lose any malloc()ed data, or more importantly,
1147            leave any pixels allocated in the server. */
1148         i = 0;
1149         LIST_LOOP(rest, color_symbol_alist) {
1150                 Lisp_Object cons = XCAR(rest);
1151                 Lisp_Object name = XCAR(cons);
1152                 Lisp_Object value = XCDR(cons);
1153                 if (NILP(value))
1154                         continue;
1155                 if (STRINGP(value))
1156                         value =
1157                             Fmake_color_instance
1158                             (value, device,
1159                              encode_error_behavior_flag(ERROR_ME_NOT));
1160                 else {
1161                         assert(COLOR_SPECIFIERP(value));
1162                         value = Fspecifier_instance(value, domain, Qnil, Qnil);
1163                 }
1164                 if (NILP(value))
1165                         continue;
1166                 results = noseeum_cons(noseeum_cons(name, value), results);
1167                 i++;
1168         }
1169         UNGCPRO;                /* no more evaluation */
1170
1171         *nsymbols = i;
1172         if (i == 0)
1173                 return 0;
1174
1175         colortbl = xnew_array_and_zero(struct color_symbol, i);
1176
1177         for (j = 0; j < i; j++) {
1178                 Lisp_Object cons = XCAR(results);
1179                 colortbl[j].color =
1180                     *COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE(XCDR(cons)));
1181
1182                 colortbl[j].name = (char *)XSTRING_DATA(XCAR(cons));
1183                 free_cons(XCONS(cons));
1184                 cons = results;
1185                 results = XCDR(results);
1186                 free_cons(XCONS(cons));
1187         }
1188         return colortbl;
1189 }
1190
1191 static void
1192 gtk_xpm_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1193                     Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1194                     int dest_mask, Lisp_Object domain)
1195 {
1196         /* This function can GC */
1197         char temp_file_name[1024];
1198         struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1199         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
1200         Lisp_Object data = find_keyword_in_vector(instantiator, Q_data);
1201         GdkColormap *cmap;
1202         int depth;
1203         GdkVisual *visual;
1204         GdkPixmap *pixmap;
1205         GdkPixmap *mask = 0;
1206         GdkWindow *window = 0;
1207         int nsymbols = 0, i = 0;
1208         struct color_symbol *color_symbols = NULL;
1209         Lisp_Object color_symbol_alist = find_keyword_in_vector(instantiator,
1210                                                                 Q_color_symbols);
1211         enum image_instance_type type;
1212         int force_mono;
1213         unsigned int w, h;
1214         const unsigned char *volatile dstring;
1215
1216         if (!DEVICE_GTK_P(XDEVICE(device)))
1217                 signal_simple_error("Not a Gtk device", device);
1218
1219         if (dest_mask & IMAGE_COLOR_PIXMAP_MASK)
1220                 type = IMAGE_COLOR_PIXMAP;
1221         else if (dest_mask & IMAGE_MONO_PIXMAP_MASK)
1222                 type = IMAGE_MONO_PIXMAP;
1223         else if (dest_mask & IMAGE_POINTER_MASK)
1224                 type = IMAGE_POINTER;
1225         else
1226                 incompatible_image_types(instantiator, dest_mask,
1227                                          IMAGE_MONO_PIXMAP_MASK |
1228                                          IMAGE_COLOR_PIXMAP_MASK |
1229                                          IMAGE_POINTER_MASK);
1230         force_mono = (type != IMAGE_COLOR_PIXMAP);
1231
1232         window = GET_GTK_WIDGET_WINDOW(DEVICE_GTK_APP_SHELL(XDEVICE(device)));
1233         cmap = DEVICE_GTK_COLORMAP(XDEVICE(device));
1234         depth = DEVICE_GTK_DEPTH(XDEVICE(device));
1235         visual = DEVICE_GTK_VISUAL(XDEVICE(device));
1236
1237         gtk_initialize_pixmap_image_instance(ii, 1, type);
1238
1239         assert(!NILP(data));
1240
1241         /* Extract all the entries from xpm-color-symbols */
1242         color_symbols =
1243             extract_xpm_color_names(device, domain, color_symbol_alist,
1244                                     &nsymbols);
1245
1246         assert(!NILP(data));
1247
1248         LISP_STRING_TO_EXTERNAL(data, dstring, Qbinary);
1249
1250         /*
1251          * GTK only uses the 'c' color entry of an XPM and doesn't use the symbolic
1252          * color names at all.  This is unfortunate because the way to change the
1253          * colors from lisp is by adding the symbolic names, and the new colors, to
1254          * the variable xpm-color-symbols.
1255          *
1256          * To get around this decode the XPM, add a 'c' entry of the desired color
1257          * for each matching symbolic color, recode the XPM and pass it to GTK.  The
1258          * decode and recode stages aren't too bad because this also performs the
1259          * external to internal format translation, which avoids contortions like
1260          * writing the XPM back to disk in order to get it processed.
1261          */
1262
1263         {
1264                 XpmImage image;
1265                 XpmInfo info;
1266                 char **data;
1267
1268                 XpmCreateXpmImageFromBuffer((char *)dstring, &image, &info);
1269
1270                 for (i = 0; i < nsymbols; i++) {
1271                         unsigned j;
1272
1273                         for (j = 0; j < image.ncolors; j++) {
1274                                 if (image.colorTable[j].symbolic != NULL &&
1275                                     !ascii_strcasecmp(color_symbols[i].name,
1276                                                       image.colorTable[j].
1277                                                       symbolic)) {
1278                                         int maxLen = 16, sz;
1279                                         image.colorTable[j].c_color =
1280                                             xmalloc(maxLen);
1281
1282                                         sz = snprintf(image.colorTable[j].c_color,
1283                                                       maxLen, "#%.4x%.4x%.4x",
1284                                                       color_symbols[i].color.red,
1285                                                       color_symbols[i].color.green,
1286                                                       color_symbols[i].color.blue);
1287                                         assert( sz >= 0 && sz < maxLen);
1288                                 }
1289                         }
1290                 }
1291
1292                 XpmCreateDataFromXpmImage(&data, &image, &info);
1293
1294                 pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, NULL,
1295                                                       data);
1296         }
1297
1298         if (color_symbols)
1299                 xfree(color_symbols);
1300
1301         if (!pixmap) {
1302                 signal_image_error("Error reading pixmap", data);
1303         }
1304
1305         gdk_window_get_geometry(pixmap, NULL, NULL, &w, &h, &depth);
1306
1307         IMAGE_INSTANCE_GTK_PIXMAP(ii) = pixmap;
1308         IMAGE_INSTANCE_PIXMAP_MASK(ii) = (void *)mask;
1309         IMAGE_INSTANCE_GTK_COLORMAP(ii) = cmap;
1310         IMAGE_INSTANCE_GTK_PIXELS(ii) = 0;
1311         IMAGE_INSTANCE_GTK_NPIXELS(ii) = 0;
1312         IMAGE_INSTANCE_PIXMAP_WIDTH(ii) = w;
1313         IMAGE_INSTANCE_PIXMAP_HEIGHT(ii) = h;
1314         IMAGE_INSTANCE_PIXMAP_FILENAME(ii) =
1315             find_keyword_in_vector(instantiator, Q_file);
1316
1317         switch (type) {
1318         case IMAGE_MONO_PIXMAP:
1319                 break;
1320
1321         case IMAGE_COLOR_PIXMAP:
1322                 {
1323                         IMAGE_INSTANCE_PIXMAP_DEPTH(ii) = depth;
1324                 }
1325                 break;
1326
1327         case IMAGE_POINTER:
1328                 {
1329                         GdkColor fg, bg;
1330                         unsigned int xhot, yhot;
1331
1332                         /* #### Gtk does not give us access to the hotspots of a pixmap */
1333                         xhot = yhot = 1;
1334                         XSETINT(IMAGE_INSTANCE_PIXMAP_HOTSPOT_X(ii), xhot);
1335                         XSETINT(IMAGE_INSTANCE_PIXMAP_HOTSPOT_Y(ii), yhot);
1336
1337                         check_pointer_sizes(w, h, instantiator);
1338
1339                         /* If the loaded pixmap has colors allocated (meaning it came from an
1340                            XPM file), then use those as the default colors for the cursor we
1341                            create.  Otherwise, default to pointer_fg and pointer_bg.
1342                          */
1343                         if (depth > 1) {
1344                                 warn_when_safe(Qunimplemented, Qnotice,
1345                                                "GTK does not support XPM cursors...\n");
1346                                 IMAGE_INSTANCE_GTK_CURSOR(ii) =
1347                                     gdk_cursor_new(GDK_COFFEE_MUG);
1348                         } else {
1349                                 generate_cursor_fg_bg(device, &pointer_fg,
1350                                                       &pointer_bg, &fg, &bg);
1351                                 IMAGE_INSTANCE_PIXMAP_FG(ii) = pointer_fg;
1352                                 IMAGE_INSTANCE_PIXMAP_BG(ii) = pointer_bg;
1353                                 IMAGE_INSTANCE_GTK_CURSOR(ii) =
1354                                     gdk_cursor_new_from_pixmap(pixmap, mask,
1355                                                                &fg, &bg, xhot,
1356                                                                yhot);
1357                         }
1358                 }
1359
1360                 break;
1361
1362         default:
1363                 abort();
1364         }
1365 }
1366 #endif                          /* HAVE_XPM */
1367 \f
1368 #ifdef HAVE_XFACE
1369
1370 /**********************************************************************
1371  *                             X-Face                                 *
1372  **********************************************************************/
1373 #if defined(EXTERN)
1374 /* This is about to get redefined! */
1375 #undef EXTERN
1376 #endif
1377 /* We have to define SYSV32 so that compface.h includes string.h
1378    instead of strings.h. */
1379 #define SYSV32
1380 #ifdef __cplusplus
1381 extern "C" {
1382 #endif
1383 #include <compface.h>
1384 #ifdef __cplusplus
1385 }
1386 #endif
1387 /* JMP_BUF cannot be used here because if it doesn't get defined
1388    to jmp_buf we end up with a conflicting type error with the
1389    definition in compface.h */ extern jmp_buf comp_env;
1390 #undef SYSV32
1391
1392 static void
1393 gtk_xface_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1394                       Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1395                       int dest_mask, Lisp_Object domain)
1396 {
1397         Lisp_Object data = find_keyword_in_vector(instantiator, Q_data);
1398         int i, stattis;
1399         char *p, *bits, *bp;
1400         CONST char *volatile emsg = 0;
1401         CONST char *volatile dstring;
1402
1403         assert(!NILP(data));
1404
1405         LISP_STRING_TO_EXTERNAL(data, dstring, Qbinary);
1406
1407         if ((p = strchr(dstring, ':'))) {
1408                 dstring = p + 1;
1409         }
1410
1411         /* Must use setjmp not SETJMP because we used jmp_buf above not JMP_BUF */
1412         if (!(stattis = setjmp(comp_env))) {
1413                 UnCompAll((char *)dstring);
1414                 UnGenFace();
1415         }
1416
1417         switch (stattis) {
1418         case -2:
1419                 emsg = "uncompface: internal error";
1420                 break;
1421         case -1:
1422                 emsg = "uncompface: insufficient or invalid data";
1423                 break;
1424         case 1:
1425                 emsg = "uncompface: excess data ignored";
1426                 break;
1427         }
1428
1429         if (emsg)
1430                 signal_simple_error_2(emsg, data, Qimage);
1431
1432         bp = bits = (char *)alloca(PIXELS / 8);
1433
1434         /* the compface library exports char F[], which uses a single byte per
1435            pixel to represent a 48x48 bitmap.  Yuck. */
1436         for (i = 0, p = F; i < (PIXELS / 8); ++i) {
1437                 int n, b;
1438                 /* reverse the bit order of each byte... */
1439                 for (b = n = 0; b < 8; ++b) {
1440                         n |= ((*p++) << b);
1441                 }
1442                 *bp++ = (char)n;
1443         }
1444
1445         xbm_instantiate_1(image_instance, instantiator, pointer_fg,
1446                           pointer_bg, dest_mask, 48, 48, bits);
1447 }
1448
1449 #endif                          /* HAVE_XFACE */
1450
1451 /**********************************************************************
1452  *                             RESOURCES                              *
1453  **********************************************************************/
1454
1455 static void gtk_resource_validate(Lisp_Object instantiator)
1456 {
1457         if ((NILP(find_keyword_in_vector(instantiator, Q_file))
1458              && NILP(find_keyword_in_vector(instantiator, Q_resource_id)))
1459             || NILP(find_keyword_in_vector(instantiator, Q_resource_type)))
1460                 signal_simple_error
1461                     ("Must supply :file, :resource-id and :resource-type",
1462                      instantiator);
1463 }
1464
1465 static Lisp_Object
1466 gtk_resource_normalize(Lisp_Object inst, Lisp_Object console_type,
1467                        Lisp_Object dest_mask)
1468 {
1469         /* This function can call lisp */
1470         Lisp_Object file = Qnil;
1471         struct gcpro gcpro1, gcpro2;
1472         Lisp_Object alist = Qnil;
1473
1474         GCPRO2(file, alist);
1475
1476         file = potential_pixmap_file_instantiator(inst, Q_file, Q_data,
1477                                                   console_type);
1478
1479         if (CONSP(file))        /* failure locating filename */
1480                 signal_double_file_error("Opening pixmap file",
1481                                          "no such file or directory",
1482                                          Fcar(file));
1483
1484         if (NILP(file))         /* no conversion necessary */
1485                 RETURN_UNGCPRO(inst);
1486
1487         alist = tagged_vector_to_alist(inst);
1488
1489         {
1490                 alist = remassq_no_quit(Q_file, alist);
1491                 alist = Fcons(Fcons(Q_file, file), alist);
1492         }
1493
1494         {
1495                 Lisp_Object result =
1496                     alist_to_tagged_vector(Qgtk_resource, alist);
1497                 free_alist(alist);
1498                 RETURN_UNGCPRO(result);
1499         }
1500 }
1501
1502 static int gtk_resource_possible_dest_types(void)
1503 {
1504         return IMAGE_POINTER_MASK | IMAGE_COLOR_PIXMAP_MASK;
1505 }
1506
1507 extern guint symbol_to_enum(Lisp_Object, GtkType);
1508
1509 static guint resource_name_to_resource(Lisp_Object name, int type)
1510 {
1511         if (type == IMAGE_POINTER)
1512                 return (symbol_to_enum(name, GTK_TYPE_GDK_CURSOR_TYPE));
1513         else
1514                 return (0);
1515 }
1516
1517 static int resource_symbol_to_type(Lisp_Object data)
1518 {
1519         if (EQ(data, Qcursor))
1520                 return IMAGE_POINTER;
1521 #if 0
1522         else if (EQ(data, Qicon))
1523                 return IMAGE_ICON;
1524         else if (EQ(data, Qbitmap))
1525                 return IMAGE_BITMAP;
1526 #endif
1527         else
1528                 return 0;
1529 }
1530
1531 static void
1532 gtk_resource_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1533                          Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1534                          int dest_mask, Lisp_Object domain)
1535 {
1536         struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1537         GdkCursor *c = NULL;
1538         unsigned int type = 0;
1539         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
1540         Lisp_Object resource_type =
1541             find_keyword_in_vector(instantiator, Q_resource_type);
1542         Lisp_Object resource_id =
1543             find_keyword_in_vector(instantiator, Q_resource_id);
1544
1545         if (!DEVICE_GTK_P(XDEVICE(device)))
1546                 signal_simple_error("Not a GTK device", device);
1547
1548         type = resource_symbol_to_type(resource_type);
1549
1550 #if 0
1551         if (dest_mask & IMAGE_POINTER_MASK && type == IMAGE_POINTER_MASK)
1552                 iitype = IMAGE_POINTER;
1553         else if (dest_mask & IMAGE_COLOR_PIXMAP_MASK)
1554                 iitype = IMAGE_COLOR_PIXMAP;
1555         else
1556                 incompatible_image_types(instantiator, dest_mask,
1557                                          IMAGE_COLOR_PIXMAP_MASK |
1558                                          IMAGE_POINTER_MASK);
1559 #endif
1560
1561         /* mess with the keyword info we were provided with */
1562         gtk_initialize_pixmap_image_instance(ii, 1, type);
1563         c = gdk_cursor_new(resource_name_to_resource(resource_id, type));
1564         IMAGE_INSTANCE_GTK_CURSOR(ii) = c;
1565         IMAGE_INSTANCE_PIXMAP_FILENAME(ii) = resource_id;
1566         IMAGE_INSTANCE_PIXMAP_WIDTH(ii) = 10;
1567         IMAGE_INSTANCE_PIXMAP_HEIGHT(ii) = 10;
1568         IMAGE_INSTANCE_PIXMAP_DEPTH(ii) = 1;
1569 }
1570
1571 static void check_valid_resource_symbol(Lisp_Object data)
1572 {
1573         CHECK_SYMBOL(data);
1574         if (!resource_symbol_to_type(data))
1575                 signal_simple_error("invalid resource type", data);
1576 }
1577
1578 static void check_valid_resource_id(Lisp_Object data)
1579 {
1580         if (!resource_name_to_resource(data, IMAGE_POINTER)
1581             && !resource_name_to_resource(data, IMAGE_COLOR_PIXMAP)
1582 #if 0
1583             && !resource_name_to_resource(data, IMAGE_BITMAP)
1584 #endif
1585             )
1586                 signal_simple_error("invalid resource identifier", data);
1587 }
1588
1589 #if 0
1590 void check_valid_string_or_int(Lisp_Object data)
1591 {
1592         if (!INTP(data))
1593                 CHECK_STRING(data);
1594         else
1595                 CHECK_INT(data);
1596 }
1597 #endif
1598 \f
1599 /**********************************************************************
1600  *                       Autodetect                                      *
1601  **********************************************************************/
1602
1603 static void autodetect_validate(Lisp_Object instantiator)
1604 {
1605         data_must_be_present(instantiator);
1606 }
1607
1608 static Lisp_Object
1609 autodetect_normalize(Lisp_Object instantiator,
1610                      Lisp_Object console_type, Lisp_Object dest_mask)
1611 {
1612         Lisp_Object file = find_keyword_in_vector(instantiator, Q_data);
1613         Lisp_Object filename = Qnil;
1614         Lisp_Object data = Qnil;
1615         struct gcpro gcpro1, gcpro2, gcpro3;
1616         Lisp_Object alist = Qnil;
1617
1618         GCPRO3(filename, data, alist);
1619
1620         if (NILP(file))         /* no conversion necessary */
1621                 RETURN_UNGCPRO(instantiator);
1622
1623         alist = tagged_vector_to_alist(instantiator);
1624
1625         filename = locate_pixmap_file(file);
1626         if (!NILP(filename)) {
1627                 int xhot, yhot;
1628                 /* #### Apparently some versions of XpmReadFileToData, which is
1629                    called by pixmap_to_lisp_data, don't return an error value
1630                    if the given file is not a valid XPM file.  Instead, they
1631                    just seg fault.  It is definitely caused by passing a
1632                    bitmap.  To try and avoid this we check for bitmaps first.  */
1633
1634                 data = bitmap_to_lisp_data(filename, &xhot, &yhot, 1);
1635
1636                 if (!EQ(data, Qt)) {
1637                         alist = remassq_no_quit(Q_data, alist);
1638                         alist = Fcons(Fcons(Q_file, filename),
1639                                       Fcons(Fcons(Q_data, data), alist));
1640                         if (xhot != -1)
1641                                 alist =
1642                                     Fcons(Fcons(Q_hotspot_x, make_int(xhot)),
1643                                           alist);
1644                         if (yhot != -1)
1645                                 alist =
1646                                     Fcons(Fcons(Q_hotspot_y, make_int(yhot)),
1647                                           alist);
1648
1649                         alist =
1650                             xbm_mask_file_munging(alist, filename, Qnil,
1651                                                   console_type);
1652
1653                         {
1654                                 Lisp_Object result =
1655                                     alist_to_tagged_vector(Qxbm, alist);
1656                                 free_alist(alist);
1657                                 RETURN_UNGCPRO(result);
1658                         }
1659                 }
1660 #ifdef HAVE_XPM
1661                 data = pixmap_to_lisp_data(filename, 1);
1662
1663                 if (!EQ(data, Qt)) {
1664                         alist = remassq_no_quit(Q_data, alist);
1665                         alist = Fcons(Fcons(Q_file, filename),
1666                                       Fcons(Fcons(Q_data, data), alist));
1667                         alist = Fcons(Fcons(Q_color_symbols,
1668                                             evaluate_xpm_color_symbols()),
1669                                       alist);
1670                         {
1671                                 Lisp_Object result =
1672                                     alist_to_tagged_vector(Qxpm, alist);
1673                                 free_alist(alist);
1674                                 RETURN_UNGCPRO(result);
1675                         }
1676                 }
1677 #endif
1678         }
1679
1680         /* If we couldn't convert it, just put it back as it is.
1681            We might try to further frob it later as a cursor-font
1682            specification. (We can't do that now because we don't know
1683            what dest-types it's going to be instantiated into.) */
1684         {
1685                 Lisp_Object result = alist_to_tagged_vector(Qautodetect, alist);
1686                 free_alist(alist);
1687                 RETURN_UNGCPRO(result);
1688         }
1689 }
1690
1691 static int autodetect_possible_dest_types(void)
1692 {
1693         return
1694             IMAGE_MONO_PIXMAP_MASK |
1695             IMAGE_COLOR_PIXMAP_MASK | IMAGE_POINTER_MASK | IMAGE_TEXT_MASK;
1696 }
1697
1698 static void
1699 autodetect_instantiate(Lisp_Object image_instance,
1700                        Lisp_Object instantiator,
1701                        Lisp_Object pointer_fg,
1702                        Lisp_Object pointer_bg,
1703                        int dest_mask, Lisp_Object domain)
1704 {
1705         Lisp_Object data = find_keyword_in_vector(instantiator, Q_data);
1706         struct gcpro gcpro1, gcpro2, gcpro3;
1707         Lisp_Object alist = Qnil;
1708         Lisp_Object result = Qnil;
1709         int is_cursor_font = 0;
1710
1711         GCPRO3(data, alist, result);
1712
1713         alist = tagged_vector_to_alist(instantiator);
1714         if (dest_mask & IMAGE_POINTER_MASK) {
1715                 CONST char *name_ext;
1716
1717                 TO_EXTERNAL_FORMAT(LISP_STRING, data,
1718                                    C_STRING_ALLOCA, name_ext, Qfile_name);
1719
1720                 if (cursor_name_to_index(name_ext) != -1) {
1721                         result = alist_to_tagged_vector(Qcursor_font, alist);
1722                         is_cursor_font = 1;
1723                 }
1724         }
1725
1726         if (!is_cursor_font)
1727                 result = alist_to_tagged_vector(Qstring, alist);
1728         free_alist(alist);
1729
1730         if (is_cursor_font)
1731                 cursor_font_instantiate(image_instance, result, pointer_fg,
1732                                         pointer_bg, dest_mask, domain);
1733         else
1734                 string_instantiate(image_instance, result, pointer_fg,
1735                                    pointer_bg, dest_mask, domain);
1736
1737         UNGCPRO;
1738 }
1739 \f
1740 /**********************************************************************
1741  *                              Font                                  *
1742  **********************************************************************/
1743
1744 static void font_validate(Lisp_Object instantiator)
1745 {
1746         data_must_be_present(instantiator);
1747 }
1748
1749 static int font_possible_dest_types(void)
1750 {
1751         return IMAGE_POINTER_MASK;
1752 }
1753
1754 static void
1755 font_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1756                  Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1757                  int dest_mask, Lisp_Object domain)
1758 {
1759         /* This function can GC */
1760         Lisp_Object data = find_keyword_in_vector(instantiator, Q_data);
1761         struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1762         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
1763         GdkColor fg, bg;
1764         GdkFont *source, *mask;
1765         char source_name[MAXPATHLEN], mask_name[MAXPATHLEN], dummy;
1766         int source_char, mask_char;
1767         int count;
1768         Lisp_Object foreground, background;
1769
1770         if (!DEVICE_GTK_P(XDEVICE(device)))
1771                 signal_simple_error("Not a Gtk device", device);
1772
1773         if (!STRINGP(data) || strncmp("FONT ", (char *)XSTRING_DATA(data), 5))
1774                 signal_simple_error("Invalid font-glyph instantiator",
1775                                     instantiator);
1776
1777         if (!(dest_mask & IMAGE_POINTER_MASK))
1778                 incompatible_image_types(instantiator, dest_mask,
1779                                          IMAGE_POINTER_MASK);
1780
1781         foreground = find_keyword_in_vector(instantiator, Q_foreground);
1782         if (NILP(foreground))
1783                 foreground = pointer_fg;
1784         background = find_keyword_in_vector(instantiator, Q_background);
1785         if (NILP(background))
1786                 background = pointer_bg;
1787
1788         generate_cursor_fg_bg(device, &foreground, &background, &fg, &bg);
1789
1790         count = sscanf((char *)XSTRING_DATA(data),
1791                        "FONT %s %d %s %d %c",
1792                        source_name, &source_char,
1793                        mask_name, &mask_char, &dummy);
1794         /* Allow "%s %d %d" as well... */
1795         if (count == 3 && (1 == sscanf(mask_name, "%d %c", &mask_char, &dummy)))
1796                 count = 4, mask_name[0] = 0;
1797
1798         if (count != 2 && count != 4)
1799                 signal_simple_error("invalid cursor specification", data);
1800         source = gdk_font_load(source_name);
1801         if (!source)
1802                 signal_simple_error_2("couldn't load font",
1803                                       build_string(source_name), data);
1804         if (count == 2)
1805                 mask = 0;
1806         else if (!mask_name[0])
1807                 mask = source;
1808         else {
1809                 mask = gdk_font_load(mask_name);
1810                 if (!mask)
1811                         /* continuable */
1812                         Fsignal(Qerror,
1813                                 list3(build_string("couldn't load font"),
1814                                       build_string(mask_name), data));
1815         }
1816         if (!mask)
1817                 mask_char = 0;
1818
1819         /* #### call XQueryTextExtents() and check_pointer_sizes() here. */
1820
1821         gtk_initialize_pixmap_image_instance(ii, 1, IMAGE_POINTER);
1822
1823         IMAGE_INSTANCE_GTK_CURSOR(ii) = NULL;
1824
1825 #if 0
1826         /* #### BILL!!! There is no way to call this function from Gdk */
1827         XCreateGlyphCursor(dpy, source, mask, source_char, mask_char, &fg, &bg);
1828 #endif
1829         XIMAGE_INSTANCE_PIXMAP_FG(image_instance) = foreground;
1830         XIMAGE_INSTANCE_PIXMAP_BG(image_instance) = background;
1831
1832         gdk_font_unref(source);
1833         if (mask && mask != source)
1834                 gdk_font_unref(mask);
1835 }
1836 \f
1837 /**********************************************************************
1838  *                           Cursor-Font                              *
1839  **********************************************************************/
1840
1841 static void cursor_font_validate(Lisp_Object instantiator)
1842 {
1843         data_must_be_present(instantiator);
1844 }
1845
1846 static int cursor_font_possible_dest_types(void)
1847 {
1848         return IMAGE_POINTER_MASK;
1849 }
1850
1851 static char *__downcase(const char *name)
1852 {
1853         char *converted = strdup(name);
1854         char *work = converted;
1855
1856         while (*work) {
1857                 *work = tolower(*work);
1858                 work++;
1859         }
1860         return (converted);
1861 }
1862
1863 /* This is basically the equivalent of XmuCursorNameToIndex */
1864 static gint cursor_name_to_index(const char *name)
1865 {
1866         int i;
1867         static char *the_gdk_cursors[GDK_NUM_GLYPHS];
1868
1869         if (!the_gdk_cursors[GDK_BASED_ARROW_UP]) {
1870                 /* Need to initialize the array */
1871                 /* Supposedly since this array is static it should be
1872                    initialized to NULLs for us, but I'm very paranoid. */
1873                 for (i = 0; i < GDK_NUM_GLYPHS; i++) {
1874                         the_gdk_cursors[i] = NULL;
1875                 }
1876
1877 #define FROB_CURSOR(x) the_gdk_cursors[GDK_##x] = __downcase(#x)
1878                 FROB_CURSOR(ARROW);
1879                 FROB_CURSOR(BASED_ARROW_DOWN);
1880                 FROB_CURSOR(BASED_ARROW_UP);
1881                 FROB_CURSOR(BOAT);
1882                 FROB_CURSOR(BOGOSITY);
1883                 FROB_CURSOR(BOTTOM_LEFT_CORNER);
1884                 FROB_CURSOR(BOTTOM_RIGHT_CORNER);
1885                 FROB_CURSOR(BOTTOM_SIDE);
1886                 FROB_CURSOR(BOTTOM_TEE);
1887                 FROB_CURSOR(BOX_SPIRAL);
1888                 FROB_CURSOR(CENTER_PTR);
1889                 FROB_CURSOR(CIRCLE);
1890                 FROB_CURSOR(CLOCK);
1891                 FROB_CURSOR(COFFEE_MUG);
1892                 FROB_CURSOR(CROSS);
1893                 FROB_CURSOR(CROSS_REVERSE);
1894                 FROB_CURSOR(CROSSHAIR);
1895                 FROB_CURSOR(DIAMOND_CROSS);
1896                 FROB_CURSOR(DOT);
1897                 FROB_CURSOR(DOTBOX);
1898                 FROB_CURSOR(DOUBLE_ARROW);
1899                 FROB_CURSOR(DRAFT_LARGE);
1900                 FROB_CURSOR(DRAFT_SMALL);
1901                 FROB_CURSOR(DRAPED_BOX);
1902                 FROB_CURSOR(EXCHANGE);
1903                 FROB_CURSOR(FLEUR);
1904                 FROB_CURSOR(GOBBLER);
1905                 FROB_CURSOR(GUMBY);
1906                 FROB_CURSOR(HAND1);
1907                 FROB_CURSOR(HAND2);
1908                 FROB_CURSOR(HEART);
1909                 FROB_CURSOR(ICON);
1910                 FROB_CURSOR(IRON_CROSS);
1911                 FROB_CURSOR(LEFT_PTR);
1912                 FROB_CURSOR(LEFT_SIDE);
1913                 FROB_CURSOR(LEFT_TEE);
1914                 FROB_CURSOR(LEFTBUTTON);
1915                 FROB_CURSOR(LL_ANGLE);
1916                 FROB_CURSOR(LR_ANGLE);
1917                 FROB_CURSOR(MAN);
1918                 FROB_CURSOR(MIDDLEBUTTON);
1919                 FROB_CURSOR(MOUSE);
1920                 FROB_CURSOR(PENCIL);
1921                 FROB_CURSOR(PIRATE);
1922                 FROB_CURSOR(PLUS);
1923                 FROB_CURSOR(QUESTION_ARROW);
1924                 FROB_CURSOR(RIGHT_PTR);
1925                 FROB_CURSOR(RIGHT_SIDE);
1926                 FROB_CURSOR(RIGHT_TEE);
1927                 FROB_CURSOR(RIGHTBUTTON);
1928                 FROB_CURSOR(RTL_LOGO);
1929                 FROB_CURSOR(SAILBOAT);
1930                 FROB_CURSOR(SB_DOWN_ARROW);
1931                 FROB_CURSOR(SB_H_DOUBLE_ARROW);
1932                 FROB_CURSOR(SB_LEFT_ARROW);
1933                 FROB_CURSOR(SB_RIGHT_ARROW);
1934                 FROB_CURSOR(SB_UP_ARROW);
1935                 FROB_CURSOR(SB_V_DOUBLE_ARROW);
1936                 FROB_CURSOR(SHUTTLE);
1937                 FROB_CURSOR(SIZING);
1938                 FROB_CURSOR(SPIDER);
1939                 FROB_CURSOR(SPRAYCAN);
1940                 FROB_CURSOR(STAR);
1941                 FROB_CURSOR(TARGET);
1942                 FROB_CURSOR(TCROSS);
1943                 FROB_CURSOR(TOP_LEFT_ARROW);
1944                 FROB_CURSOR(TOP_LEFT_CORNER);
1945                 FROB_CURSOR(TOP_RIGHT_CORNER);
1946                 FROB_CURSOR(TOP_SIDE);
1947                 FROB_CURSOR(TOP_TEE);
1948                 FROB_CURSOR(TREK);
1949                 FROB_CURSOR(UL_ANGLE);
1950                 FROB_CURSOR(UMBRELLA);
1951                 FROB_CURSOR(UR_ANGLE);
1952                 FROB_CURSOR(WATCH);
1953                 FROB_CURSOR(XTERM);
1954                 FROB_CURSOR(X_CURSOR);
1955 #undef FROB_CURSOR
1956         }
1957
1958         for (i = 0; i < GDK_NUM_GLYPHS; i++) {
1959                 if (!the_gdk_cursors[i])
1960                         continue;
1961                 if (!strcmp(the_gdk_cursors[i], name)) {
1962                         return (i);
1963                 }
1964         }
1965         return (-1);
1966 }
1967
1968 static void
1969 cursor_font_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
1970                         Lisp_Object pointer_fg, Lisp_Object pointer_bg,
1971                         int dest_mask, Lisp_Object domain)
1972 {
1973         /* This function can GC */
1974         Lisp_Object data = find_keyword_in_vector(instantiator, Q_data);
1975         struct Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
1976         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
1977         int i;
1978         CONST char *name_ext;
1979         Lisp_Object foreground, background;
1980
1981         if (!DEVICE_GTK_P(XDEVICE(device)))
1982                 signal_simple_error("Not a Gtk device", device);
1983
1984         if (!(dest_mask & IMAGE_POINTER_MASK))
1985                 incompatible_image_types(instantiator, dest_mask,
1986                                          IMAGE_POINTER_MASK);
1987
1988         TO_EXTERNAL_FORMAT(LISP_STRING, data,
1989                            C_STRING_ALLOCA, name_ext, Qfile_name);
1990
1991         if ((i = cursor_name_to_index(name_ext)) == -1)
1992                 signal_simple_error("Unrecognized cursor-font name", data);
1993
1994         gtk_initialize_pixmap_image_instance(ii, 1, IMAGE_POINTER);
1995         IMAGE_INSTANCE_GTK_CURSOR(ii) = gdk_cursor_new(i);
1996         foreground = find_keyword_in_vector(instantiator, Q_foreground);
1997         if (NILP(foreground))
1998                 foreground = pointer_fg;
1999         background = find_keyword_in_vector(instantiator, Q_background);
2000         if (NILP(background))
2001                 background = pointer_bg;
2002         maybe_recolor_cursor(image_instance, foreground, background);
2003 }
2004
2005 static int
2006 gtk_colorize_image_instance(Lisp_Object image_instance,
2007                             Lisp_Object foreground, Lisp_Object background);
2008 \f
2009 /************************************************************************/
2010 /*                      subwindow and widget support                      */
2011 /************************************************************************/
2012
2013 /* unmap the image if it is a widget. This is used by redisplay via
2014    redisplay_unmap_subwindows */
2015 static void gtk_unmap_subwindow(Lisp_Image_Instance * p)
2016 {
2017         if (IMAGE_INSTANCE_TYPE(p) == IMAGE_SUBWINDOW) {
2018                 /* We don't support subwindows, but we do support widgets... */
2019                 abort();
2020         } else {                /* must be a widget */
2021
2022                 /* Since we are being unmapped we want the enclosing frame to
2023                    get focus. The losing with simple scrolling but is the safest
2024                    thing to do. */
2025                 if (IMAGE_INSTANCE_GTK_CLIPWIDGET(p))
2026                         gtk_widget_unmap(IMAGE_INSTANCE_GTK_CLIPWIDGET(p));
2027         }
2028 }
2029
2030 /* map the subwindow. This is used by redisplay via
2031    redisplay_output_subwindow */
2032 static void
2033 gtk_map_subwindow(Lisp_Image_Instance * p, int x, int y,
2034                   struct display_glyph_area *dga)
2035 {
2036         assert(dga->width > 0 && dga->height > 0);
2037
2038         if (IMAGE_INSTANCE_TYPE(p) == IMAGE_SUBWINDOW) {
2039                 /* No subwindow support... */
2040                 abort();
2041         } else {                /* must be a widget */
2042
2043                 struct frame *f = XFRAME(IMAGE_INSTANCE_FRAME(p));
2044                 GtkWidget *wid = IMAGE_INSTANCE_GTK_CLIPWIDGET(p);
2045                 GtkAllocation a;
2046                 int moving;
2047
2048                 if (!wid)
2049                         return;
2050
2051                 a.x = x + IMAGE_INSTANCE_GTK_WIDGET_XOFFSET(p);
2052                 a.y = y + IMAGE_INSTANCE_GTK_WIDGET_YOFFSET(p);
2053                 a.width = dga->width;
2054                 a.height = dga->height;
2055
2056                 /* Is the widget cganging position? */
2057                 moving = (a.x != wid->allocation.x) ||
2058                     (a.y != wid->allocation.y);
2059
2060                 if ((a.width != wid->allocation.width) ||
2061                     (a.height != wid->allocation.height) || moving) {
2062                         gtk_widget_size_allocate(IMAGE_INSTANCE_GTK_CLIPWIDGET
2063                                                  (p), &a);
2064                 }
2065
2066                 if (moving) {
2067                         guint32 old_flags =
2068                             GTK_WIDGET_FLAGS(FRAME_GTK_TEXT_WIDGET(f));
2069
2070                         /* GtkFixed widget queues a resize when you add a widget.
2071                          ** But only if it is visible.
2072                          ** losers.
2073                          */
2074                         GTK_WIDGET_FLAGS(FRAME_GTK_TEXT_WIDGET(f)) &=
2075                             ~GTK_VISIBLE;
2076
2077                         if (IMAGE_INSTANCE_GTK_ALREADY_PUT(p)) {
2078                                 gtk_fixed_move(GTK_FIXED
2079                                                (FRAME_GTK_TEXT_WIDGET(f)), wid,
2080                                                a.x, a.y);
2081                         } else {
2082                                 IMAGE_INSTANCE_GTK_ALREADY_PUT(p) = TRUE;
2083                                 gtk_fixed_put(GTK_FIXED
2084                                               (FRAME_GTK_TEXT_WIDGET(f)), wid,
2085                                               a.x, a.y);
2086                         }
2087
2088                         GTK_WIDGET_FLAGS(FRAME_GTK_TEXT_WIDGET(f)) = old_flags;
2089                 } else {
2090                         if (IMAGE_INSTANCE_GTK_ALREADY_PUT(p)) {
2091                                 /* Do nothing... */
2092                         } else {
2093                                 /* Must make sure we have put the image at least once! */
2094                                 IMAGE_INSTANCE_GTK_ALREADY_PUT(p) = TRUE;
2095                                 gtk_fixed_put(GTK_FIXED
2096                                               (FRAME_GTK_TEXT_WIDGET(f)), wid,
2097                                               a.x, a.y);
2098                         }
2099                 }
2100
2101                 if (!IMAGE_INSTANCE_SUBWINDOW_DISPLAYEDP(p)) {
2102                         gtk_widget_map(wid);
2103                 }
2104
2105                 gtk_widget_draw(wid, NULL);
2106         }
2107 }
2108
2109 /* when you click on a widget you may activate another widget this
2110    needs to be checked and all appropriate widgets updated */
2111 static void gtk_redisplay_subwindow(Lisp_Image_Instance * p)
2112 {
2113         /* Update the subwindow size if necessary. */
2114         if (IMAGE_INSTANCE_SIZE_CHANGED(p)) {
2115 #if 0
2116                 XResizeWindow(IMAGE_INSTANCE_X_SUBWINDOW_DISPLAY(p),
2117                               IMAGE_INSTANCE_X_SUBWINDOW_ID(p),
2118                               IMAGE_INSTANCE_WIDTH(p),
2119                               IMAGE_INSTANCE_HEIGHT(p));
2120 #endif
2121         }
2122 }
2123
2124 /* Update all attributes that have changed. */
2125 static void gtk_redisplay_widget(Lisp_Image_Instance * p)
2126 {
2127         /* This function can GC if IN_REDISPLAY is false. */
2128
2129         if (!IMAGE_INSTANCE_GTK_CLIPWIDGET(p))
2130                 return;
2131
2132 #ifdef HAVE_WIDGETS
2133         /* First get the items if they have changed since this is a
2134            structural change. As such it will nuke all added values so we
2135            need to update most other things after the items have changed. */
2136         gtk_widget_show_all(IMAGE_INSTANCE_GTK_CLIPWIDGET(p));
2137         if (IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(p)) {
2138                 Lisp_Object image_instance;
2139
2140                 XSETIMAGE_INSTANCE(image_instance, p);
2141
2142                 /* Need to update GtkArgs that might have changed... */
2143                 /* #### FIXME!!! */
2144         } else {
2145                 /* #### FIXME!!! */
2146                 /* No items changed, so do nothing, right? */
2147         }
2148
2149         /* Possibly update the colors and font */
2150         if (IMAGE_INSTANCE_WIDGET_FACE_CHANGED(p)
2151             ||
2152             /* #### This is not sufficient because it will not cope with widgets
2153                that are not currently visible. Once redisplay has done the
2154                visible ones it will clear this flag so that when new ones
2155                become visible they will not be updated. */
2156             XFRAME(IMAGE_INSTANCE_FRAME(p))->faces_changed
2157             ||
2158             XFRAME(IMAGE_INSTANCE_FRAME(p))->frame_changed
2159             || IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(p)) {
2160                 /* #### Write this function BILL! */
2161                 update_widget_face(NULL, p, IMAGE_INSTANCE_FRAME(p));
2162         }
2163
2164         /* Possibly update the text. */
2165         if (IMAGE_INSTANCE_TEXT_CHANGED(p)) {
2166                 char *str;
2167                 Lisp_Object val = IMAGE_INSTANCE_WIDGET_TEXT(p);
2168                 LISP_STRING_TO_EXTERNAL(val, str, Qnative);
2169
2170                 /* #### Need to special case each type of GtkWidget here! */
2171         }
2172
2173         /* Possibly update the size. */
2174         if (IMAGE_INSTANCE_SIZE_CHANGED(p)
2175             || IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(p)
2176             || IMAGE_INSTANCE_TEXT_CHANGED(p)) {
2177                 GtkRequisition r;
2178                 GtkAllocation a = IMAGE_INSTANCE_GTK_CLIPWIDGET(p)->allocation;
2179
2180                 assert(IMAGE_INSTANCE_GTK_WIDGET_ID(p) &&
2181                        IMAGE_INSTANCE_GTK_CLIPWIDGET(p));
2182
2183                 a.width = r.width = IMAGE_INSTANCE_WIDTH(p);
2184                 a.height = r.height = IMAGE_INSTANCE_HEIGHT(p);
2185
2186                 /* Force the widget's preferred and actual size to what we say it shall
2187                    be. */
2188                 gtk_widget_size_request(IMAGE_INSTANCE_GTK_CLIPWIDGET(p), &r);
2189                 gtk_widget_size_allocate(IMAGE_INSTANCE_GTK_CLIPWIDGET(p), &a);
2190         }
2191
2192         /* Adjust offsets within the frame. */
2193         if (XFRAME(IMAGE_INSTANCE_FRAME(p))->size_changed) {
2194                 /* I don't think we need to do anything for Gtk here... */
2195         }
2196
2197         /* now modify the widget */
2198 #endif
2199 }
2200
2201 /* instantiate and gtk type subwindow */
2202 static void
2203 gtk_subwindow_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
2204                           Lisp_Object pointer_fg, Lisp_Object pointer_bg,
2205                           int dest_mask, Lisp_Object domain)
2206 {
2207         /* This function can GC */
2208         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
2209         Lisp_Object device = IMAGE_INSTANCE_DEVICE(ii);
2210         Lisp_Object frame = DOMAIN_FRAME(domain);
2211
2212         if (!DEVICE_GTK_P(XDEVICE(device)))
2213                 signal_simple_error("Not a GTK device", device);
2214
2215         IMAGE_INSTANCE_TYPE(ii) = IMAGE_SUBWINDOW;
2216
2217         ii->data = xnew_and_zero(struct gtk_subwindow_data);
2218
2219         /* Create a window for clipping */
2220         IMAGE_INSTANCE_GTK_CLIPWINDOW(ii) = NULL;
2221
2222         /* Now put the subwindow inside the clip window. */
2223         IMAGE_INSTANCE_SUBWINDOW_ID(ii) = (void *)NULL;
2224 }
2225
2226 #ifdef HAVE_WIDGETS
2227 \f
2228 /************************************************************************/
2229 /*                            widgets                            */
2230 /************************************************************************/
2231 static void
2232 update_widget_face(GtkWidget * w, Lisp_Image_Instance * ii, Lisp_Object domain)
2233 {
2234         if (0) {
2235                 GtkStyle *style = gtk_widget_get_style(w);
2236                 Lisp_Object pixel = Qnil;
2237                 GdkColor *fcolor, *bcolor;
2238
2239                 style = gtk_style_copy(style);
2240
2241                 /* Update the foreground. */
2242                 pixel = FACE_FOREGROUND(IMAGE_INSTANCE_WIDGET_FACE(ii), domain);
2243                 fcolor = COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE(pixel));
2244
2245                 /* Update the background. */
2246                 pixel = FACE_BACKGROUND(IMAGE_INSTANCE_WIDGET_FACE(ii), domain);
2247                 bcolor = COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE(pixel));
2248
2249                 /* Update the font */
2250                 /* #### FIXME!!! Need to copy the widgets style, dick with it, and
2251                  ** set the widgets style to the new style...
2252                  */
2253                 gtk_widget_set_style(w, style);
2254
2255                 /* #### Megahack - but its just getting too complicated to do this
2256                    in the right place. */
2257 #if 0
2258                 if (EQ(IMAGE_INSTANCE_WIDGET_TYPE(ii), Qtab_control))
2259                         update_tab_widget_face(wv, ii, domain);
2260 #endif
2261         }
2262 }
2263
2264 #if 0
2265 static void
2266 update_tab_widget_face(GtkWidget * w, Lisp_Image_Instance * ii,
2267                        Lisp_Object domain)
2268 {
2269         if (wv->contents) {
2270                 widget_value *val = wv->contents, *cur;
2271
2272                 /* Give each child label the correct foreground color. */
2273                 Lisp_Object pixel = FACE_FOREGROUND
2274                     (IMAGE_INSTANCE_WIDGET_FACE(ii),
2275                      domain);
2276                 XColor fcolor = COLOR_INSTANCE_X_COLOR(XCOLOR_INSTANCE(pixel));
2277                 lw_add_widget_value_arg(val, XtNtabForeground, fcolor.pixel);
2278                 wv->change = VISIBLE_CHANGE;
2279                 val->change = VISIBLE_CHANGE;
2280
2281                 for (cur = val->next; cur; cur = cur->next) {
2282                         cur->change = VISIBLE_CHANGE;
2283                         if (cur->value) {
2284                                 lw_copy_widget_value_args(val, cur);
2285                         }
2286                 }
2287         }
2288 }
2289 #endif
2290
2291 static Lisp_Object
2292 gtk_widget_instantiate_1(Lisp_Object image_instance, Lisp_Object instantiator,
2293                          Lisp_Object pointer_fg, Lisp_Object pointer_bg,
2294                          Lisp_Object domain)
2295 {
2296         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
2297         Lisp_Object widget = Qnil;
2298         char *nm = NULL;
2299         GtkWidget *w = NULL;
2300         struct gcpro gcpro1;
2301
2302         IMAGE_INSTANCE_TYPE(ii) = IMAGE_WIDGET;
2303
2304         if (!NILP(IMAGE_INSTANCE_WIDGET_TEXT(ii))) {
2305                 LISP_STRING_TO_EXTERNAL(IMAGE_INSTANCE_WIDGET_TEXT(ii), nm,
2306                                         Qnative);
2307         }
2308
2309         ii->data = xnew_and_zero(struct gtk_subwindow_data);
2310
2311         /* Create a clipping widget */
2312         IMAGE_INSTANCE_GTK_CLIPWIDGET(ii) = NULL;
2313         IMAGE_INSTANCE_GTK_ALREADY_PUT(ii) = FALSE;
2314
2315         /* Create the actual widget */
2316         GCPRO1(widget);
2317         widget = call5(Qgtk_widget_instantiate_internal,
2318                        image_instance, instantiator,
2319                        pointer_fg, pointer_bg, domain);
2320
2321         if (!NILP(widget)) {
2322                 CHECK_GTK_OBJECT(widget);
2323                 w = GTK_WIDGET(XGTK_OBJECT(widget)->object);
2324         } else {
2325                 stderr_out
2326                     ("Lisp-level creation of widget failed... falling back\n");
2327                 w = gtk_label_new("Widget Creation Failed...");
2328         }
2329
2330         UNGCPRO;
2331
2332         IMAGE_INSTANCE_SUBWINDOW_ID(ii) = (void *)w;
2333
2334         /* #### HACK!!!!  We should make this do the right thing if we
2335          ** really need a clip widget!
2336          */
2337         IMAGE_INSTANCE_GTK_CLIPWIDGET(ii) = w;
2338
2339         /* The current theme may produce a widget of a different size that what we
2340            expect so force reconsideration of the widget's size. */
2341         IMAGE_INSTANCE_LAYOUT_CHANGED(ii) = 1;
2342
2343         return (Qt);
2344 }
2345
2346 static void
2347 gtk_widget_instantiate(Lisp_Object image_instance, Lisp_Object instantiator,
2348                        Lisp_Object pointer_fg, Lisp_Object pointer_bg,
2349                        int dest_mask, Lisp_Object domain)
2350 {
2351         call_with_suspended_errors((lisp_fn_t) gtk_widget_instantiate_1,
2352                                    Qnil, Qimage,
2353                                    ERROR_ME_WARN, 5,
2354                                    image_instance, instantiator,
2355                                    pointer_fg, pointer_bg, domain);
2356 }
2357
2358 /* get properties of a control */
2359 static Lisp_Object
2360 gtk_widget_property(Lisp_Object image_instance, Lisp_Object prop)
2361 {
2362         /* Lisp_Image_Instance *ii = XIMAGE_INSTANCE (image_instance); */
2363
2364         /* get the text from a control */
2365         if (EQ(prop, Q_text)) {
2366                 return Qnil;
2367         }
2368         return Qunbound;
2369 }
2370
2371 #define FAKE_GTK_WIDGET_INSTANTIATOR(x)                                 \
2372 static void                                                             \
2373 gtk_##x##_instantiate (Lisp_Object image_instance,                      \
2374    Lisp_Object instantiator,                                            \
2375    Lisp_Object pointer_fg,                                              \
2376    Lisp_Object pointer_bg,                                              \
2377    int dest_mask, Lisp_Object domain)                                   \
2378 {                                                                       \
2379   gtk_widget_instantiate (image_instance, instantiator, pointer_fg,     \
2380                           pointer_bg, dest_mask, domain);               \
2381 }
2382
2383 FAKE_GTK_WIDGET_INSTANTIATOR(native_layout);
2384 FAKE_GTK_WIDGET_INSTANTIATOR(button);
2385 FAKE_GTK_WIDGET_INSTANTIATOR(progress_gauge);
2386 FAKE_GTK_WIDGET_INSTANTIATOR(edit_field);
2387 FAKE_GTK_WIDGET_INSTANTIATOR(combo_box);
2388 FAKE_GTK_WIDGET_INSTANTIATOR(label);
2389 /* Note: tab_control has a custom instantiator (see below) */
2390
2391 /*
2392   Ask the widget to return it's preferred size.  This device method must
2393   defined for all widgets that also have format specific version of
2394   query_geometry defined in glyphs-widget.c.  This is because those format
2395   specific versions return sizes that are appropriate for the X widgets.  For
2396   GTK, the size of a widget can change at runtime due to the user changing
2397   their theme.
2398
2399   This method can be called before the widget is instantiated.  This is
2400   because instantiate_image_instantiator() is tying to be helpful to other
2401   toolkits and supply sane geometry values to them.  This is not appropriate
2402   for GTK and can be ignored.
2403
2404   This method can be used by all widgets.
2405 */
2406 static void
2407 gtk_widget_query_geometry(Lisp_Object image_instance,
2408                           int *width, int *height,
2409                           enum image_instance_geometry disp, Lisp_Object domain)
2410 {
2411         Lisp_Image_Instance *p = XIMAGE_INSTANCE(image_instance);
2412
2413         if (p->data != NULL) {
2414                 GtkWidget *w = IMAGE_INSTANCE_GTK_CLIPWIDGET(p);
2415                 GtkRequisition r;
2416
2417                 gtk_widget_size_request(w, &r);
2418                 *height = r.height;
2419                 *width = r.width;
2420         }
2421 }
2422 \f
2423 /* Button functions. */
2424
2425 /* Update a button's clicked state. */
2426 static void gtk_button_redisplay(Lisp_Object image_instance)
2427 {
2428         /* This function can GC if IN_REDISPLAY is false. */
2429         Lisp_Image_Instance *p = XIMAGE_INSTANCE(image_instance);
2430         GtkWidget *w = IMAGE_INSTANCE_GTK_CLIPWIDGET(p);
2431
2432         if (GTK_WIDGET_TYPE(w) == gtk_button_get_type()) {
2433         } else if (GTK_WIDGET_TYPE(w) == gtk_check_button_get_type()) {
2434         } else if (GTK_WIDGET_TYPE(w) == gtk_radio_button_get_type()) {
2435         } else {
2436                 /* Unknown button type... */
2437                 abort();
2438         }
2439 }
2440
2441 /* get properties of a button */
2442 static Lisp_Object
2443 gtk_button_property(Lisp_Object image_instance, Lisp_Object prop)
2444 {
2445         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
2446
2447         /* check the state of a button */
2448         if (EQ(prop, Q_selected)) {
2449                 if (GTK_WIDGET_HAS_FOCUS(IMAGE_INSTANCE_SUBWINDOW_ID(ii)))
2450                         return Qt;
2451                 else
2452                         return Qnil;
2453         }
2454         return Qunbound;
2455 }
2456 \f
2457 /* Progress gauge functions. */
2458
2459 /* set the properties of a progress gauge */
2460 static void gtk_progress_gauge_redisplay(Lisp_Object image_instance)
2461 {
2462         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
2463
2464         if (IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii)) {
2465                 gfloat f;
2466                 Lisp_Object val;
2467
2468                 val = XGUI_ITEM(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii))->value;
2469                 f = XFLOATINT(val);
2470
2471                 gtk_progress_set_value(GTK_PROGRESS
2472                                        (IMAGE_INSTANCE_SUBWINDOW_ID(ii)), f);
2473         }
2474 }
2475 \f
2476 /* Tab Control functions. */
2477
2478 /*
2479   Register a widget's callbacks with the frame's hashtable.  The hashtable is
2480   weak so deregistration is handled automatically.  Tab controls have per-tab
2481   callback list functions and the GTK callback architecture is not
2482   sufficiently flexible to deal with this.  Instead, the functions are
2483   registered here and the id is passed through the callback loop.
2484  */
2485 static int
2486 gtk_register_gui_item(Lisp_Object image_instance, Lisp_Object gui,
2487                       Lisp_Object domain)
2488 {
2489         struct frame *f = XFRAME(DOMAIN_FRAME(domain));
2490         int id = gui_item_id_hash(FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE(f),
2491                                   gui, WIDGET_GLYPH_SLOT);
2492
2493         Fputhash(make_int(id), image_instance,
2494                  FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE(f));
2495         Fputhash(make_int(id), XGUI_ITEM(gui)->callback,
2496                  FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE(f));
2497         Fputhash(make_int(id), XGUI_ITEM(gui)->callback_ex,
2498                  FRAME_GTK_WIDGET_CALLBACK_EX_HASH_TABLE(f));
2499         return id;
2500 }
2501
2502 /*
2503   Append the given item as a tab to the notebook. Callbacks, etc are all
2504   setup.
2505  */
2506 static void
2507 gtk_add_tab_item(Lisp_Object image_instance,
2508                  GtkNotebook * nb, Lisp_Object item, Lisp_Object domain, int i)
2509 {
2510         Lisp_Object name;
2511         int hash_id = 0;
2512         char *c_name = NULL;
2513         GtkWidget *box;
2514
2515         if (GUI_ITEMP(item)) {
2516                 Lisp_Gui_Item *pgui = XGUI_ITEM(item);
2517
2518                 if (!STRINGP(pgui->name))
2519                         pgui->name = Feval(pgui->name);
2520
2521                 CHECK_STRING(pgui->name);
2522
2523                 hash_id = gtk_register_gui_item(image_instance, item, domain);
2524                 name = pgui->name;
2525         } else {
2526                 CHECK_STRING(item);
2527                 name = item;
2528         }
2529
2530         TO_EXTERNAL_FORMAT(LISP_STRING, name, C_STRING_ALLOCA, c_name, Qctext);
2531
2532         /* Dummy widget that the notbook wants to display when a tab is selected. */
2533         box = gtk_vbox_new(FALSE, 3);
2534
2535         /*
2536            Store the per-tab callback data id in the tab.  The callback functions
2537            themselves could have been stored in the widget but this avoids having to
2538            worry about the garbage collector running between here and the callback
2539            function.
2540          */
2541         gtk_object_set_data(GTK_OBJECT(box), GTK_DATA_TAB_HASHCODE_IDENTIFIER,
2542                             (gpointer) hash_id);
2543
2544         gtk_notebook_append_page(nb, box, gtk_label_new(c_name));
2545 }
2546
2547 /* Signal handler for the switch-page signal. */
2548 static void gtk_tab_control_callback(GtkNotebook * notebook,
2549                                      GtkNotebookPage * page,
2550                                      gint page_num, gpointer user_data)
2551 {
2552         /*
2553            This callback is called for every selection, not just user selection.
2554            We're only interested in user selection, which occurs outside of
2555            redisplay.
2556          */
2557
2558         if (!in_display) {
2559                 Lisp_Object image_instance, callback, callback_ex;
2560                 Lisp_Object frame, event;
2561                 int update_subwindows_p = 0;
2562                 struct frame *f = gtk_widget_to_frame(GTK_WIDGET(notebook));
2563                 int id;
2564
2565                 if (!f)
2566                         return;
2567                 frame = wrap_frame(f);
2568
2569                 id = (int)gtk_object_get_data(GTK_OBJECT(page->child),
2570                                               GTK_DATA_TAB_HASHCODE_IDENTIFIER);
2571                 image_instance = Fgethash(make_int(id),
2572                                           FRAME_GTK_WIDGET_INSTANCE_HASH_TABLE
2573                                           (f), Qnil);
2574                 callback =
2575                     Fgethash(make_int(id),
2576                              FRAME_GTK_WIDGET_CALLBACK_HASH_TABLE(f), Qnil);
2577                 callback_ex =
2578                     Fgethash(make_int(id),
2579                              FRAME_GTK_WIDGET_CALLBACK_EX_HASH_TABLE(f), Qnil);
2580                 update_subwindows_p = 1;
2581
2582                 /* It is possible for a widget action to cause it to get out of
2583                    sync with its instantiator. Thus it is necessary to signal
2584                    this possibility. */
2585                 if (IMAGE_INSTANCEP(image_instance))
2586                         XIMAGE_INSTANCE_WIDGET_ACTION_OCCURRED(image_instance) =
2587                             1;
2588
2589                 if (!NILP(callback_ex) && !UNBOUNDP(callback_ex)) {
2590                         event = Fmake_event(Qnil, Qnil);
2591
2592                         XEVENT(event)->event_type = misc_user_event;
2593                         XEVENT(event)->channel = frame;
2594                         XEVENT(event)->event.eval.function = Qeval;
2595                         XEVENT(event)->event.eval.object =
2596                             list4(Qfuncall, callback_ex, image_instance, event);
2597                 } else if (NILP(callback) || UNBOUNDP(callback))
2598                         event = Qnil;
2599                 else {
2600                         Lisp_Object fn, arg;
2601
2602                         event = Fmake_event(Qnil, Qnil);
2603
2604                         get_gui_callback(callback, &fn, &arg);
2605                         XEVENT(event)->event_type = misc_user_event;
2606                         XEVENT(event)->channel = frame;
2607                         XEVENT(event)->event.eval.function = fn;
2608                         XEVENT(event)->event.eval.object = arg;
2609                 }
2610
2611                 if (!NILP(event))
2612                         enqueue_gtk_dispatch_event(event);
2613
2614                 /* The result of this evaluation could cause other instances to change so
2615                    enqueue an update callback to check this. */
2616                 if (update_subwindows_p && !NILP(event))
2617                         enqueue_magic_eval_event(update_widget_instances,
2618                                                  frame);
2619         }
2620 }
2621
2622 /* Create a tab_control widget.  The special handling of the individual tabs
2623    means that the normal instantiation code cannot be used. */
2624 static void
2625 gtk_tab_control_instantiate(Lisp_Object image_instance,
2626                             Lisp_Object instantiator,
2627                             Lisp_Object pointer_fg,
2628                             Lisp_Object pointer_bg,
2629                             int dest_mask, Lisp_Object domain)
2630 {
2631         Lisp_Object rest;
2632         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
2633         int i = 0;
2634         int selected = 0;
2635         GtkNotebook *nb;
2636
2637         /* The normal instantiation is still needed. */
2638         gtk_widget_instantiate(image_instance, instantiator, pointer_fg,
2639                                pointer_bg, dest_mask, domain);
2640
2641         nb = GTK_NOTEBOOK(IMAGE_INSTANCE_GTK_CLIPWIDGET(ii));
2642
2643         /* Add items to the tab, find the current selection */
2644         LIST_LOOP(rest, XCDR(IMAGE_INSTANCE_WIDGET_ITEMS(ii))) {
2645                 gtk_add_tab_item(image_instance, nb, XCAR(rest), domain, i);
2646
2647                 if (gui_item_selected_p(XCAR(rest)))
2648                         selected = i;
2649
2650                 i++;
2651         }
2652
2653         gtk_notebook_set_page(nb, selected);
2654
2655         /* Call per-tab lisp callback when a tab is pressed. */
2656         gtk_signal_connect(GTK_OBJECT(nb), "switch-page",
2657                            GTK_SIGNAL_FUNC(gtk_tab_control_callback), NULL);
2658 }
2659
2660 /* Set the properties of a tab control */
2661 static void gtk_tab_control_redisplay(Lisp_Object image_instance)
2662 {
2663         /* #### Convert this to GTK baby! */
2664         Lisp_Image_Instance *ii = XIMAGE_INSTANCE(image_instance);
2665
2666         if (IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii) ||
2667             IMAGE_INSTANCE_WIDGET_ACTION_OCCURRED(ii)) {
2668                 /* If only the order has changed then simply select the first
2669                    one of the pending set. This stops horrendous rebuilding -
2670                    and hence flicker - of the tabs each time you click on
2671                    one. */
2672                 if (tab_control_order_only_changed(image_instance)) {
2673                         int i = 0;
2674                         Lisp_Object rest, selected =
2675                             gui_item_list_find_selected
2676                             (NILP(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)) ?
2677                              XCDR(IMAGE_INSTANCE_WIDGET_ITEMS(ii)) :
2678                              XCDR(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)));
2679
2680                         LIST_LOOP(rest, XCDR(IMAGE_INSTANCE_WIDGET_ITEMS(ii))) {
2681                                 if (gui_item_equal_sans_selected
2682                                     (XCAR(rest), selected, 0)) {
2683                                         Lisp_Object old_selected =
2684                                             gui_item_list_find_selected(XCDR
2685                                                                         (IMAGE_INSTANCE_WIDGET_ITEMS
2686                                                                          (ii)));
2687
2688                                         /* Pick up the new selected item. */
2689                                         XGUI_ITEM(old_selected)->selected =
2690                                             XGUI_ITEM(XCAR(rest))->selected;
2691                                         XGUI_ITEM(XCAR(rest))->selected =
2692                                             XGUI_ITEM(selected)->selected;
2693                                         /* We're not actually changing the items anymore. */
2694                                         IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii)
2695                                             = 0;
2696                                         IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)
2697                                             = Qnil;
2698
2699                                         gtk_notebook_set_page(GTK_NOTEBOOK
2700                                                               (IMAGE_INSTANCE_GTK_CLIPWIDGET
2701                                                                (ii)), i);
2702
2703                                         break;
2704                                 }
2705
2706                                 i++;
2707                         }
2708                 } else {
2709                         /* More than just the order has changed... let's get busy! */
2710                         GtkNotebook *nb =
2711                             GTK_NOTEBOOK(IMAGE_INSTANCE_GTK_CLIPWIDGET(ii));
2712                         guint num_pages = g_list_length(nb->children);
2713                         Lisp_Object rest;
2714                         int i;
2715
2716                         /* Why is there no API to remove everything from a notebook? */
2717                         if (num_pages >= 0) {
2718                                 for (i = num_pages; i >= 0; --i) {
2719                                         gtk_notebook_remove_page(nb, i);
2720                                 }
2721                         }
2722
2723                         i = 0;
2724
2725                         LIST_LOOP(rest,
2726                                   XCDR(IMAGE_INSTANCE_WIDGET_PENDING_ITEMS(ii)))
2727                         {
2728                                 gtk_add_tab_item(image_instance, nb, XCAR(rest),
2729                                                  IMAGE_INSTANCE_FRAME(ii), i);
2730                         }
2731
2732                         /* Show all the new widgets we just added... */
2733                         gtk_widget_show_all(GTK_WIDGET(nb));
2734                 }
2735         }
2736
2737         /* Possibly update the face. */
2738 #if 0
2739         if (IMAGE_INSTANCE_WIDGET_FACE_CHANGED(ii)
2740             ||
2741             XFRAME(IMAGE_INSTANCE_FRAME(ii))->faces_changed
2742             || IMAGE_INSTANCE_WIDGET_ITEMS_CHANGED(ii)) {
2743                 update_tab_widget_face(wv, ii, IMAGE_INSTANCE_FRAME(ii));
2744         }
2745 #endif
2746 }
2747 #endif                          /* HAVE_WIDGETS */
2748 \f
2749 /************************************************************************/
2750 /*                            initialization                            */
2751 /************************************************************************/
2752 void syms_of_glyphs_gtk(void)
2753 {
2754         defkeyword(&Q_resource_id, ":resource-id");
2755         defkeyword(&Q_resource_type, ":resource-type");
2756 #ifdef HAVE_WIDGETS
2757         defsymbol(&Qgtk_widget_instantiate_internal,
2758                   "gtk-widget-instantiate-internal");
2759         defsymbol(&Qgtk_widget_property_internal,
2760                   "gtk-widget-property-internal");
2761         defsymbol(&Qgtk_widget_redisplay_internal,
2762                   "gtk-widget-redisplay-internal");
2763         defsymbol(&Qgtk_widget_set_style, "gtk-widget-set-style");
2764 #endif
2765 }
2766
2767 void console_type_create_glyphs_gtk(void)
2768 {
2769         /* image methods */
2770         CONSOLE_HAS_METHOD(gtk, print_image_instance);
2771         CONSOLE_HAS_METHOD(gtk, finalize_image_instance);
2772         CONSOLE_HAS_METHOD(gtk, image_instance_equal);
2773         CONSOLE_HAS_METHOD(gtk, image_instance_hash);
2774         CONSOLE_HAS_METHOD(gtk, colorize_image_instance);
2775         CONSOLE_HAS_METHOD(gtk, init_image_instance_from_eimage);
2776         CONSOLE_HAS_METHOD(gtk, locate_pixmap_file);
2777         CONSOLE_HAS_METHOD(gtk, unmap_subwindow);
2778         CONSOLE_HAS_METHOD(gtk, map_subwindow);
2779         CONSOLE_HAS_METHOD(gtk, redisplay_widget);
2780         CONSOLE_HAS_METHOD(gtk, redisplay_subwindow);
2781 }
2782
2783 void image_instantiator_format_create_glyphs_gtk(void)
2784 {
2785         IIFORMAT_VALID_CONSOLE(gtk, nothing);
2786         IIFORMAT_VALID_CONSOLE(gtk, string);
2787 #ifdef HAVE_WIDGETS
2788         IIFORMAT_VALID_CONSOLE(gtk, layout);
2789 #endif
2790         IIFORMAT_VALID_CONSOLE(gtk, formatted_string);
2791         IIFORMAT_VALID_CONSOLE(gtk, inherit);
2792 #ifdef HAVE_XPM
2793         INITIALIZE_DEVICE_IIFORMAT(gtk, xpm);
2794         IIFORMAT_HAS_DEVMETHOD(gtk, xpm, instantiate);
2795 #endif
2796 #ifdef HAVE_JPEG
2797         IIFORMAT_VALID_CONSOLE(gtk, jpeg);
2798 #endif
2799 #ifdef HAVE_TIFF
2800         IIFORMAT_VALID_CONSOLE(gtk, tiff);
2801 #endif
2802 #ifdef HAVE_PNG
2803         IIFORMAT_VALID_CONSOLE(gtk, png);
2804 #endif
2805 #ifdef HAVE_GIF
2806         IIFORMAT_VALID_CONSOLE(gtk, gif);
2807 #endif
2808 #if 1
2809         IIFORMAT_VALID_CONSOLE(gtk, rawrgb);
2810         IIFORMAT_VALID_CONSOLE(gtk, rawrgba);
2811 #endif
2812
2813         INITIALIZE_DEVICE_IIFORMAT(gtk, subwindow);
2814         IIFORMAT_HAS_DEVMETHOD(gtk, subwindow, instantiate);
2815
2816 #ifdef HAVE_WIDGETS
2817         /* layout widget */
2818         INITIALIZE_DEVICE_IIFORMAT(gtk, native_layout);
2819         IIFORMAT_HAS_DEVMETHOD(gtk, native_layout, instantiate);
2820
2821         /* button widget */
2822         INITIALIZE_DEVICE_IIFORMAT(gtk, button);
2823         IIFORMAT_HAS_DEVMETHOD(gtk, button, property);
2824         IIFORMAT_HAS_DEVMETHOD(gtk, button, instantiate);
2825         IIFORMAT_HAS_DEVMETHOD(gtk, button, redisplay);
2826         IIFORMAT_HAS_SHARED_DEVMETHOD(gtk, button, query_geometry, widget);
2827         /* general widget methods. */
2828         INITIALIZE_DEVICE_IIFORMAT(gtk, widget);
2829         IIFORMAT_HAS_DEVMETHOD(gtk, widget, property);
2830         IIFORMAT_HAS_DEVMETHOD(gtk, widget, query_geometry);
2831
2832         /* progress gauge */
2833         INITIALIZE_DEVICE_IIFORMAT(gtk, progress_gauge);
2834         IIFORMAT_HAS_DEVMETHOD(gtk, progress_gauge, redisplay);
2835         IIFORMAT_HAS_DEVMETHOD(gtk, progress_gauge, instantiate);
2836         IIFORMAT_HAS_SHARED_DEVMETHOD(gtk, progress_gauge, query_geometry,
2837                                       widget);
2838         /* text field */
2839         INITIALIZE_DEVICE_IIFORMAT(gtk, edit_field);
2840         IIFORMAT_HAS_DEVMETHOD(gtk, edit_field, instantiate);
2841         INITIALIZE_DEVICE_IIFORMAT(gtk, combo_box);
2842         IIFORMAT_HAS_DEVMETHOD(gtk, combo_box, instantiate);
2843         IIFORMAT_HAS_SHARED_DEVMETHOD(gtk, combo_box, redisplay, tab_control);
2844         /* tab control widget */
2845         INITIALIZE_DEVICE_IIFORMAT(gtk, tab_control);
2846         IIFORMAT_HAS_DEVMETHOD(gtk, tab_control, instantiate);
2847         IIFORMAT_HAS_DEVMETHOD(gtk, tab_control, redisplay);
2848         IIFORMAT_HAS_SHARED_DEVMETHOD(gtk, tab_control, query_geometry, widget);
2849         /* label */
2850         INITIALIZE_DEVICE_IIFORMAT(gtk, label);
2851         IIFORMAT_HAS_DEVMETHOD(gtk, label, instantiate);
2852 #endif
2853
2854         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(cursor_font, "cursor-font");
2855         IIFORMAT_VALID_CONSOLE(gtk, cursor_font);
2856
2857         IIFORMAT_HAS_METHOD(cursor_font, validate);
2858         IIFORMAT_HAS_METHOD(cursor_font, possible_dest_types);
2859         IIFORMAT_HAS_METHOD(cursor_font, instantiate);
2860
2861         IIFORMAT_VALID_KEYWORD(cursor_font, Q_data, check_valid_string);
2862         IIFORMAT_VALID_KEYWORD(cursor_font, Q_foreground, check_valid_string);
2863         IIFORMAT_VALID_KEYWORD(cursor_font, Q_background, check_valid_string);
2864
2865         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(font, "font");
2866         IIFORMAT_VALID_CONSOLE(gtk, font);
2867
2868         IIFORMAT_HAS_METHOD(font, validate);
2869         IIFORMAT_HAS_METHOD(font, possible_dest_types);
2870         IIFORMAT_HAS_METHOD(font, instantiate);
2871
2872         IIFORMAT_VALID_KEYWORD(font, Q_data, check_valid_string);
2873         IIFORMAT_VALID_KEYWORD(font, Q_foreground, check_valid_string);
2874         IIFORMAT_VALID_KEYWORD(font, Q_background, check_valid_string);
2875
2876 #ifdef HAVE_XPM
2877         INITIALIZE_DEVICE_IIFORMAT(gtk, xpm);
2878         IIFORMAT_HAS_DEVMETHOD(gtk, xpm, instantiate);
2879 #endif
2880
2881 #ifdef HAVE_XFACE
2882         INITIALIZE_DEVICE_IIFORMAT(gtk, xface);
2883         IIFORMAT_HAS_DEVMETHOD(gtk, xface, instantiate);
2884 #endif
2885
2886         INITIALIZE_DEVICE_IIFORMAT(gtk, xbm);
2887         IIFORMAT_HAS_DEVMETHOD(gtk, xbm, instantiate);
2888         IIFORMAT_VALID_CONSOLE(gtk, xbm);
2889
2890         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(gtk_resource, "gtk-resource");
2891         IIFORMAT_VALID_CONSOLE(gtk, gtk_resource);
2892
2893         IIFORMAT_HAS_METHOD(gtk_resource, validate);
2894         IIFORMAT_HAS_METHOD(gtk_resource, normalize);
2895         IIFORMAT_HAS_METHOD(gtk_resource, possible_dest_types);
2896         IIFORMAT_HAS_METHOD(gtk_resource, instantiate);
2897
2898         IIFORMAT_VALID_KEYWORD(gtk_resource, Q_resource_type,
2899                                check_valid_resource_symbol);
2900         IIFORMAT_VALID_KEYWORD(gtk_resource, Q_resource_id,
2901                                check_valid_resource_id);
2902         IIFORMAT_VALID_KEYWORD(gtk_resource, Q_file, check_valid_string);
2903
2904         INITIALIZE_IMAGE_INSTANTIATOR_FORMAT(autodetect, "autodetect");
2905         IIFORMAT_VALID_CONSOLE(gtk, autodetect);
2906
2907         IIFORMAT_HAS_METHOD(autodetect, validate);
2908         IIFORMAT_HAS_METHOD(autodetect, normalize);
2909         IIFORMAT_HAS_METHOD(autodetect, possible_dest_types);
2910         IIFORMAT_HAS_METHOD(autodetect, instantiate);
2911
2912         IIFORMAT_VALID_KEYWORD(autodetect, Q_data, check_valid_string);
2913 }
2914
2915 void vars_of_glyphs_gtk(void)
2916 {
2917 #ifdef HAVE_XFACE
2918         Fprovide(Qxface);
2919 #endif
2920
2921         DEFVAR_LISP("gtk-bitmap-file-path", &Vgtk_bitmap_file_path      /*
2922 A list of the directories in which X bitmap files may be found.
2923 If nil, this is initialized from the "*bitmapFilePath" resource.
2924 This is used by the `make-image-instance' function (however, note that if
2925 the environment variable XBMLANGPATH is set, it is consulted first).
2926                                                                          */ );
2927         Vgtk_bitmap_file_path = Qnil;
2928 }
2929
2930 void complex_vars_of_glyphs_gtk(void)
2931 {
2932 #define BUILD_GLYPH_INST(variable, name)                        \
2933   Fadd_spec_to_specifier                                        \
2934     (GLYPH_IMAGE (XGLYPH (variable)),                           \
2935      vector3 (Qxbm, Q_data,                                     \
2936               list3 (make_int (name##_width),                   \
2937                      make_int (name##_height),                  \
2938                      make_ext_string (name##_bits,              \
2939                                       sizeof (name##_bits),     \
2940                                       Qbinary))),               \
2941      Qglobal, Qgtk, Qnil)
2942
2943         BUILD_GLYPH_INST(Vtruncation_glyph, truncator);
2944         BUILD_GLYPH_INST(Vcontinuation_glyph, continuer);
2945         BUILD_GLYPH_INST(Vsxemacs_logo, sxemacs);
2946         BUILD_GLYPH_INST(Vhscroll_glyph, hscroll);
2947
2948 #undef BUILD_GLYPH_INST
2949 }
2950 \f
2951 /* Ripped off from glyphs-msw.c */
2952 /*
2953  * The data returned by the following routine is always in left-most byte
2954  * first and left-most bit first.  If it doesn't return BitmapSuccess then
2955  * its arguments won't have been touched.  This routine should look as much
2956  * like the Xlib routine XReadBitmapfile as possible.
2957  */
2958 #define MAX_SIZE 1024
2959
2960 /* shared data for the image read/parse logic */
2961 static short hexTable[256];     /* conversion value */
2962 static int gtk_glyphs_initialized = FALSE;      /* easier to fill in at run time */
2963
2964 /*
2965  *      Table index for the hex values. Initialized once, first time.
2966  *      Used for translation value or delimiter significance lookup.
2967  */
2968 static void initHexTable()
2969 {
2970         /*
2971          * We build the table at run time for several reasons:
2972          *
2973          *     1.  portable to non-ASCII machines.
2974          *     2.  still reentrant since we set the init flag after setting table.
2975          *     3.  easier to extend.
2976          *     4.  less prone to bugs.
2977          */
2978         hexTable['0'] = 0;
2979         hexTable['1'] = 1;
2980         hexTable['2'] = 2;
2981         hexTable['3'] = 3;
2982         hexTable['4'] = 4;
2983         hexTable['5'] = 5;
2984         hexTable['6'] = 6;
2985         hexTable['7'] = 7;
2986         hexTable['8'] = 8;
2987         hexTable['9'] = 9;
2988         hexTable['A'] = 10;
2989         hexTable['B'] = 11;
2990         hexTable['C'] = 12;
2991         hexTable['D'] = 13;
2992         hexTable['E'] = 14;
2993         hexTable['F'] = 15;
2994         hexTable['a'] = 10;
2995         hexTable['b'] = 11;
2996         hexTable['c'] = 12;
2997         hexTable['d'] = 13;
2998         hexTable['e'] = 14;
2999         hexTable['f'] = 15;
3000
3001         /* delimiters of significance are flagged w/ negative value */
3002         hexTable[' '] = -1;
3003         hexTable[','] = -1;
3004         hexTable['}'] = -1;
3005         hexTable['\n'] = -1;
3006         hexTable['\t'] = -1;
3007
3008         gtk_glyphs_initialized = TRUE;
3009 }
3010
3011 /*
3012  *      read next hex value in the input stream, return -1 if EOF
3013  */
3014 static int NextInt(FILE * fstream)
3015 {
3016         int ch;
3017         int value = 0;
3018         int gotone = 0;
3019         int done = 0;
3020
3021         /* loop, accumulate hex value until find delimiter  */
3022         /* skip any initial delimiters found in read stream */
3023
3024         while (!done) {
3025                 ch = getc(fstream);
3026                 if (ch == EOF) {
3027                         value = -1;
3028                         done++;
3029                 } else {
3030                         /* trim high bits, check type and accumulate */
3031                         ch &= 0xff;
3032                         if (isascii(ch) && isxdigit(ch)) {
3033                                 value = (value << 4) + hexTable[ch];
3034                                 gotone++;
3035                         } else if ((hexTable[ch]) < 0 && gotone)
3036                                 done++;
3037                 }
3038         }
3039         return value;
3040 }
3041
3042 int read_bitmap_data(fstream, width, height, datap, x_hot, y_hot)
3043 FILE *fstream;                  /* handle on file  */
3044 unsigned int *width, *height;   /* RETURNED */
3045 unsigned char **datap;          /* RETURNED */
3046 int *x_hot, *y_hot;             /* RETURNED */
3047 {
3048         unsigned char *data = NULL;     /* working variable */
3049         char line[MAX_SIZE];    /* input line from file */
3050         int size;               /* number of bytes of data */
3051         char name_and_type[MAX_SIZE];   /* an input line */
3052         char *type;             /* for parsing */
3053         int value;              /* from an input line */
3054         int version10p;         /* boolean, old format */
3055         int padding;            /* to handle alignment */
3056         int bytes_per_line;     /* per scanline of data */
3057         unsigned int ww = 0;    /* width */
3058         unsigned int hh = 0;    /* height */
3059         int hx = -1;            /* x hotspot */
3060         int hy = -1;            /* y hotspot */
3061
3062 #define Xmalloc(size) malloc(size)
3063
3064         /* first time initialization */
3065         if (gtk_glyphs_initialized == FALSE)
3066                 initHexTable();
3067
3068         /* error cleanup and return macro   */
3069 #define RETURN(code) { if (data) free (data); return code; }
3070
3071         while (fgets(line, MAX_SIZE, fstream)) {
3072                 if (strlen(line) == MAX_SIZE - 1) {
3073                         RETURN(BitmapFileInvalid);
3074                 }
3075                 if (sscanf(line, "#define %s %d", name_and_type, &value) == 2) {
3076                         if (!(type = strrchr(name_and_type, '_')))
3077                                 type = name_and_type;
3078                         else
3079                                 type++;
3080
3081                         if (!strcmp("width", type))
3082                                 ww = (unsigned int)value;
3083                         if (!strcmp("height", type))
3084                                 hh = (unsigned int)value;
3085                         if (!strcmp("hot", type)) {
3086                                 if (type-- == name_and_type
3087                                     || type-- == name_and_type)
3088                                         continue;
3089                                 if (!strcmp("x_hot", type))
3090                                         hx = value;
3091                                 if (!strcmp("y_hot", type))
3092                                         hy = value;
3093                         }
3094                         continue;
3095                 }
3096
3097                 if (sscanf(line, "static short %s = {", name_and_type) == 1)
3098                         version10p = 1;
3099                 else if (sscanf
3100                          (line, "static unsigned char %s = {",
3101                           name_and_type) == 1)
3102                         version10p = 0;
3103                 else if (sscanf(line, "static char %s = {", name_and_type) == 1)
3104                         version10p = 0;
3105                 else
3106                         continue;
3107
3108                 if (!(type = strrchr(name_and_type, '_')))
3109                         type = name_and_type;
3110                 else
3111                         type++;
3112
3113                 if (strcmp("bits[]", type))
3114                         continue;
3115
3116                 if (!ww || !hh)
3117                         RETURN(BitmapFileInvalid);
3118
3119                 if ((ww % 16) && ((ww % 16) < 9) && version10p)
3120                         padding = 1;
3121                 else
3122                         padding = 0;
3123
3124                 bytes_per_line = (ww + 7) / 8 + padding;
3125
3126                 size = bytes_per_line * hh;
3127                 data = (unsigned char *)Xmalloc((unsigned int)size);
3128                 if (!data)
3129                         RETURN(BitmapNoMemory);
3130
3131                 if (version10p) {
3132                         unsigned char *ptr;
3133                         int bytes;
3134
3135                         for (bytes = 0, ptr = data; bytes < size; (bytes += 2)) {
3136                                 if ((value = NextInt(fstream)) < 0)
3137                                         RETURN(BitmapFileInvalid);
3138                                 *(ptr++) = value;
3139                                 if (!padding || ((bytes + 2) % bytes_per_line))
3140                                         *(ptr++) = value >> 8;
3141                         }
3142                 } else {
3143                         unsigned char *ptr;
3144                         int bytes;
3145
3146                         for (bytes = 0, ptr = data; bytes < size;
3147                              bytes++, ptr++) {
3148                                 if ((value = NextInt(fstream)) < 0)
3149                                         RETURN(BitmapFileInvalid);
3150                                 *ptr = value;
3151                         }
3152                 }
3153                 break;
3154         }                       /* end while */
3155
3156         if (data == NULL) {
3157                 RETURN(BitmapFileInvalid);
3158         }
3159
3160         *datap = data;
3161         data = NULL;
3162         *width = ww;
3163         *height = hh;
3164         if (x_hot)
3165                 *x_hot = hx;
3166         if (y_hot)
3167                 *y_hot = hy;
3168
3169         RETURN(BitmapSuccess);
3170 }
3171
3172 int read_bitmap_data_from_file(CONST char *filename, unsigned int *width,
3173                                unsigned int *height, unsigned char **datap,
3174                                int *x_hot, int *y_hot)
3175 {
3176         FILE *fstream;
3177         int rval;
3178
3179         if ((fstream = fopen(filename, "r")) == NULL) {
3180                 return BitmapOpenFailed;
3181         }
3182         rval = read_bitmap_data(fstream, width, height, datap, x_hot, y_hot);
3183         fclose(fstream);
3184         return rval;
3185 }
3186
3187 /* X specific crap */
3188 #include <gdk/gdkx.h>
3189 /* #### Should remove all this X specific stuff when GTK/GDK matures a
3190    bit more and provides an abstraction for it. */
3191 static int
3192 gtk_colorize_image_instance(Lisp_Object image_instance,
3193                             Lisp_Object foreground, Lisp_Object background)
3194 {
3195         struct Lisp_Image_Instance *p;
3196
3197         p = XIMAGE_INSTANCE(image_instance);
3198
3199         switch (IMAGE_INSTANCE_TYPE(p)) {
3200         case IMAGE_MONO_PIXMAP:
3201                 IMAGE_INSTANCE_TYPE(p) = IMAGE_COLOR_PIXMAP;
3202                 /* Make sure there aren't two pointers to the same mask, causing
3203                    it to get freed twice. */
3204                 IMAGE_INSTANCE_GTK_MASK(p) = 0;
3205                 break;
3206
3207         default:
3208                 return 0;
3209         }
3210
3211         {
3212                 GdkWindow *draw =
3213                     GET_GTK_WIDGET_WINDOW(DEVICE_GTK_APP_SHELL
3214                                           (XDEVICE(IMAGE_INSTANCE_DEVICE(p))));
3215                 GdkPixmap *new_pxmp = gdk_pixmap_new(draw,
3216                                                      IMAGE_INSTANCE_PIXMAP_WIDTH
3217                                                      (p),
3218                                                      IMAGE_INSTANCE_PIXMAP_HEIGHT
3219                                                      (p),
3220                                                      DEVICE_GTK_DEPTH(XDEVICE
3221                                                                       (IMAGE_INSTANCE_DEVICE
3222                                                                        (p))));
3223                 GdkGCValues gcv;
3224                 GdkGC *gc;
3225
3226                 gcv.foreground =
3227                     *COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE(foreground));
3228                 gcv.background =
3229                     *COLOR_INSTANCE_GTK_COLOR(XCOLOR_INSTANCE(background));
3230                 gc = gdk_gc_new_with_values(new_pxmp, &gcv,
3231                                             GDK_GC_BACKGROUND |
3232                                             GDK_GC_FOREGROUND);
3233
3234                 XCopyPlane(GDK_WINDOW_XDISPLAY(draw),
3235                            GDK_WINDOW_XWINDOW(IMAGE_INSTANCE_GTK_PIXMAP(p)),
3236                            GDK_WINDOW_XWINDOW(new_pxmp),
3237                            GDK_GC_XGC(gc), 0, 0,
3238                            IMAGE_INSTANCE_PIXMAP_WIDTH(p),
3239                            IMAGE_INSTANCE_PIXMAP_HEIGHT(p), 0, 0, 1);
3240
3241                 gdk_gc_destroy(gc);
3242                 IMAGE_INSTANCE_GTK_PIXMAP(p) = new_pxmp;
3243                 IMAGE_INSTANCE_PIXMAP_DEPTH(p) =
3244                     DEVICE_GTK_DEPTH(XDEVICE(IMAGE_INSTANCE_DEVICE(p)));
3245                 IMAGE_INSTANCE_PIXMAP_FG(p) = foreground;
3246                 IMAGE_INSTANCE_PIXMAP_BG(p) = background;
3247                 return 1;
3248         }
3249 }