Minor package-get cleanup + bldchain tweak
[sxemacs] / src / ui / X11 / gui-x.c
1 /* General GUI code -- X-specific. (menubars, scrollbars, toolbars, dialogs)
2    Copyright (C) 1995 Board of Trustees, University of Illinois.
3    Copyright (C) 1995, 1996, 2000 Ben Wing.
4    Copyright (C) 1995 Sun Microsystems, Inc.
5    Copyright (C) 1998 Free Software Foundation, Inc.
6
7 This file is part of SXEmacs
8
9 SXEmacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 SXEmacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
21
22
23 /* Synched up with: Not in FSF. */
24
25 /* This file Mule-ized by Ben Wing, 7-8-00. */
26
27 #include <config.h>
28 #include "lisp.h"
29
30 #include "console-x.h"
31 #ifdef LWLIB_USES_MOTIF
32 #include <Xm/Xm.h>              /* for XmVersion */
33 #endif  /* LWLIB_USES_MOTIF */
34 #include "gui-x.h"
35 #include "buffer.h"
36 #include "ui/device.h"
37 #define INCLUDE_EVENTS_H_PRIVATE_SPHERE
38 #include "events/events.h"
39 #include "ui/frame.h"
40 #include "ui/gui.h"
41 #include "ui/glyphs.h"
42 #include "ui/redisplay.h"
43 #include "opaque.h"
44
45 /* we need a unique id for each popup menu, dialog box, and scrollbar */
46 static unsigned int lwlib_id_tick;
47
48 LWLIB_ID new_lwlib_id(void)
49 {
50         return ++lwlib_id_tick;
51 }
52
53 widget_value *xmalloc_widget_value(void)
54 {
55         widget_value *tmp = malloc_widget_value();
56         if (!tmp)
57                 memory_full();
58         return tmp;
59 }
60 \f
61 static int mark_widget_value_mapper(widget_value * val, void *closure)
62 {
63         Lisp_Object markee;
64         if (val->call_data) {
65                 VOID_TO_LISP(markee, val->call_data);
66                 mark_object(markee);
67         }
68
69         if (val->accel) {
70                 VOID_TO_LISP(markee, val->accel);
71                 mark_object(markee);
72         }
73         return 0;
74 }
75
76 static Lisp_Object mark_popup_data(Lisp_Object obj)
77 {
78         struct popup_data *data = (struct popup_data *)XPOPUP_DATA(obj);
79
80         /* Now mark the callbacks and such that are hidden in the lwlib
81            call-data */
82
83         if (data->id)
84                 lw_map_widget_values(data->id, mark_widget_value_mapper, 0);
85
86         return data->last_menubar_buffer;
87 }
88
89 DEFINE_LRECORD_IMPLEMENTATION("popup-data", popup_data,
90                               mark_popup_data, internal_object_printer,
91                               0, 0, 0, 0, struct popup_data);
92 \f
93 /* This is like FRAME_MENUBAR_DATA (f), but contains an alist of
94    (id . popup-data) for GCPRO'ing the callbacks of the popup menus
95    and dialog boxes. */
96 static Lisp_Object Vpopup_callbacks;
97
98 void gcpro_popup_callbacks(LWLIB_ID id)
99 {
100         struct popup_data *pdata;
101         Lisp_Object lid = make_int(id);
102         Lisp_Object lpdata;
103
104         assert(NILP(assq_no_quit(lid, Vpopup_callbacks)));
105         pdata = alloc_lcrecord_type(struct popup_data, &lrecord_popup_data);
106         pdata->id = id;
107         pdata->last_menubar_buffer = Qnil;
108         pdata->menubar_contents_up_to_date = 0;
109         XSETPOPUP_DATA(lpdata, pdata);
110         Vpopup_callbacks = Fcons(Fcons(lid, lpdata), Vpopup_callbacks);
111 }
112
113 void ungcpro_popup_callbacks(LWLIB_ID id)
114 {
115         Lisp_Object lid = make_int(id);
116         Lisp_Object this = assq_no_quit(lid, Vpopup_callbacks);
117         assert(!NILP(this));
118         Vpopup_callbacks = delq_no_quit(this, Vpopup_callbacks);
119 }
120
121 int popup_handled_p(LWLIB_ID id)
122 {
123         return NILP(assq_no_quit(make_int(id), Vpopup_callbacks));
124 }
125
126 /* menu_item_descriptor_to_widget_value() et al. mallocs a
127    widget_value, but then may signal lisp errors.  If an error does
128    not occur, the opaque ptr we have here has had its pointer set to 0
129    to tell us not to do anything.  Otherwise we free the widget value.
130    (This has nothing to do with GC, it's just about not dropping
131    pointers to malloc'd data when errors happen.) */
132
133 Lisp_Object widget_value_unwind(Lisp_Object closure)
134 {
135         widget_value *wv = (widget_value *) get_opaque_ptr(closure);
136         free_opaque_ptr(closure);
137         if (wv)
138                 free_widget_value_tree(wv);
139         return Qnil;
140 }
141
142 #if 0
143 static void print_widget_value(widget_value * wv, int depth)
144 {
145         /* strings in wv are in external format; use printf not stdout_out
146            because the latter takes internal-format strings */
147         Extbyte d[200];
148         int i;
149         for (i = 0; i < depth; i++)
150                 d[i] = ' ';
151         d[depth] = 0;
152         /* #### - print type field */
153         printf("%sname:    %s\n", d, (wv->name ? wv->name : "(null)"));
154         if (wv->value)
155                 printf("%svalue:   %s\n", d, wv->value);
156         if (wv->key)
157                 printf("%skey:     %s\n", d, wv->key);
158         printf("%senabled: %d\n", d, wv->enabled);
159         if (wv->contents) {
160                 printf("\n%scontents: \n", d);
161                 print_widget_value(wv->contents, depth + 5);
162         }
163         if (wv->next) {
164                 printf("\n");
165                 print_widget_value(wv->next, depth);
166         }
167 }
168 #endif
169
170 /* This recursively calls free_widget_value() on the tree of widgets.
171    It must free all data that was malloc'ed for these widget_values.
172
173    It used to be that emacs only allocated new storage for the `key' slot.
174    All other slots are pointers into the data of Lisp_Strings, and must be
175    left alone.  */
176 void free_popup_widget_value_tree(widget_value * wv)
177 {
178         if (!wv) {
179                 return;
180         }
181         if (wv->key) {
182                 /* we mustnt free this? */
183 #if !defined HAVE_BDWGC || !defined EF_USE_BDWGC
184                 xfree(wv->key);
185 #endif  /* !BDWGC */
186         }
187         if (wv->value) {
188                 /* we mustnt free this? */
189 #if !defined HAVE_BDWGC || !defined EF_USE_BDWGC
190                 xfree(wv->value);
191 #endif  /* !BDWGC */
192         }
193         if (wv->name) {
194                 /* we mustnt free this? */
195 #if !defined HAVE_BDWGC || !defined EF_USE_BDWGC
196                 xfree(wv->name);
197 #endif  /* !BDWGC */
198         }
199
200 #if defined HAVE_BDWGC && defined EF_USE_BDWGC
201         wv->name = wv->value = wv->key = NULL;
202 #else  /* !BDWGC */
203         wv->name = wv->value = wv->key = (char *)0xDEADBEEF;
204 #endif  /* BDWGC */
205
206         if (wv->contents && (wv->contents != (widget_value *) 1)) {
207                 free_popup_widget_value_tree(wv->contents);
208 #if defined HAVE_BDWGC && defined EF_USE_BDWGC
209                 wv->contents = NULL;
210 #else  /* !BDWGC */
211                 wv->contents = (widget_value *) 0xDEADBEEF;
212 #endif  /* BDWGC */
213         }
214         if (wv->next) {
215                 free_popup_widget_value_tree(wv->next);
216 #if defined HAVE_BDWGC && defined EF_USE_BDWGC
217                 wv->next = NULL;
218 #else  /* !BDWGC */
219                 wv->next = (widget_value *) 0xDEADBEEF;
220 #endif  /* BDWGC */
221         }
222         free_widget_value(wv);
223         return;
224 }
225
226 /* The following is actually called from somewhere within XtDispatchEvent(),
227    called from XtAppProcessEvent() in event-Xt.c
228
229    Callback function for widgets and menus.
230  */
231
232 void
233 popup_selection_callback(Widget widget, LWLIB_ID ignored_id,
234                          XtPointer client_data)
235 {
236         Lisp_Object data, image_instance, callback, callback_ex;
237         Lisp_Object frame, event;
238         int update_subwindows_p = 0;
239         struct device *d = get_device_from_display(XtDisplay(widget));
240         struct frame *f = x_any_widget_or_parent_to_frame(d, widget);
241
242         /* set in lwlib to the time stamp associated with the most recent menu
243            operation */
244         extern Time x_focus_timestamp_really_sucks_fix_me_better;
245
246         if (!f)
247                 return;
248         if (((EMACS_INT) client_data) == 0)
249                 return;
250         VOID_TO_LISP(data, client_data);
251         XSETFRAME(frame, f);
252
253 #if 0
254         /* #### What the hell?  I can't understand why this call is here,
255            and doing it is really courting disaster in the new event
256            model, since popup_selection_callback is called from
257            within next_event_internal() and Faccept_process_output()
258            itself calls next_event_internal().  --Ben */
259
260         /* Flush the X and process input */
261         Faccept_process_output(Qnil, Qnil, Qnil);
262 #endif
263
264         if (((EMACS_INT) client_data) == -1) {
265                 event = Fmake_event(Qnil, Qnil);
266
267                 XEVENT(event)->event_type = misc_user_event;
268                 XEVENT(event)->channel = frame;
269                 XEVENT(event)->event.eval.function = Qrun_hooks;
270                 XEVENT(event)->event.eval.object = Qmenu_no_selection_hook;
271         } else {
272                 image_instance = XCAR(data);
273                 callback = XCAR(XCDR(data));
274                 callback_ex = XCDR(XCDR(data));
275                 update_subwindows_p = 1;
276                 /* It is possible for a widget action to cause it to get out of
277                    sync with its instantiator. Thus it is necessary to signal
278                    this possibility. */
279                 if (IMAGE_INSTANCEP(image_instance))
280                         XIMAGE_INSTANCE_WIDGET_ACTION_OCCURRED(image_instance) =
281                             1;
282
283                 if (!NILP(callback_ex) && !UNBOUNDP(callback_ex)) {
284                         event = Fmake_event(Qnil, Qnil);
285
286                         XEVENT(event)->event_type = misc_user_event;
287                         XEVENT(event)->channel = frame;
288                         XEVENT(event)->event.eval.function = Qeval;
289                         XEVENT(event)->event.eval.object =
290                             list4(Qfuncall, callback_ex, image_instance, event);
291                 } else if (NILP(callback) || UNBOUNDP(callback))
292                         event = Qnil;
293                 else {
294                         Lisp_Object fn, arg;
295
296                         event = Fmake_event(Qnil, Qnil);
297
298                         get_gui_callback(callback, &fn, &arg);
299                         XEVENT(event)->event_type = misc_user_event;
300                         XEVENT(event)->channel = frame;
301                         XEVENT(event)->event.eval.function = fn;
302                         XEVENT(event)->event.eval.object = arg;
303                 }
304         }
305
306         /* This is the timestamp used for asserting focus so we need to get an
307            up-to-date value event if no events have been dispatched to emacs
308          */
309 #if defined(HAVE_MENUBARS)
310         DEVICE_X_MOUSE_TIMESTAMP(d) =
311             x_focus_timestamp_really_sucks_fix_me_better;
312 #else
313         DEVICE_X_MOUSE_TIMESTAMP(d) = DEVICE_X_GLOBAL_MOUSE_TIMESTAMP(d);
314 #endif
315         if (!NILP(event))
316                 enqueue_Xt_dispatch_event(event);
317         /* The result of this evaluation could cause other instances to change so
318            enqueue an update callback to check this. */
319         if (update_subwindows_p && !NILP(event))
320                 enqueue_magic_eval_event(update_widget_instances, frame);
321 }
322
323 #if 1
324   /* Eval the activep slot of the menu item */
325 # define wv_set_evalable_slot(slot,form) do {   \
326   Lisp_Object wses_form = (form);               \
327   (slot) = (NILP (wses_form) ? 0 :              \
328             EQ (wses_form, Qt) ? 1 :            \
329             !NILP (Feval (wses_form)));         \
330 } while (0)
331 #else
332   /* Treat the activep slot of the menu item as a boolean */
333 # define wv_set_evalable_slot(slot,form)        \
334       ((void) (slot = (!NILP (form))))
335 #endif
336
337 Extbyte *menu_separator_style_and_to_external(const Bufbyte * s)
338 {
339         const Bufbyte *p;
340         Bufbyte first;
341
342         if (!s || s[0] == '\0')
343                 return NULL;
344         first = s[0];
345         if (first != '-' && first != '=')
346                 return NULL;
347         for (p = s; *p == first; p++)
348                 DO_NOTHING;
349
350         /* #### - cannot currently specify a separator tag "--!tag" and a
351            separator style "--:style" at the same time. */
352         /* #### - Also, the motif menubar code doesn't deal with the
353            double etched style yet, so it's not good to get into the habit of
354            using "===" in menubars to get double-etched lines */
355         if (*p == '!' || *p == '\0')
356                 return ((first == '-')
357                         ? NULL  /* single etched is the default */
358                         : xstrdup("shadowDoubleEtchedIn"));
359         else if (*p == ':') {
360                 Extbyte *retval;
361
362                 C_STRING_TO_EXTERNAL_MALLOC(p + 1, retval, Qlwlib_encoding);
363                 return retval;
364         }
365
366         return NULL;
367 }
368
369 Extbyte *add_accel_and_to_external(Lisp_Object string)
370 {
371         int i;
372         int found_accel = 0;
373         Extbyte *retval;
374         Bufbyte *name = XSTRING_DATA(string);
375
376         for (i = 0; name[i]; ++i)
377                 if (name[i] == '%' && name[i + 1] == '_') {
378                         found_accel = 1;
379                         break;
380                 }
381
382         if (found_accel)
383                 LISP_STRING_TO_EXTERNAL_MALLOC(string, retval, Qlwlib_encoding);
384         else {
385                 size_t namelen = XSTRING_LENGTH(string);
386                 Bufbyte *chars = (Bufbyte *) alloca(namelen + 3);
387                 chars[0] = '%';
388                 chars[1] = '_';
389                 memcpy(chars + 2, name, namelen + 1);
390                 C_STRING_TO_EXTERNAL_MALLOC(chars, retval, Qlwlib_encoding);
391         }
392
393         return retval;
394 }
395
396 /* This does the dirty work.  gc_currently_forbidden is 1 when this is called.
397  */
398 int
399 button_item_to_widget_value(Lisp_Object gui_object_instance,
400                             Lisp_Object gui_item, widget_value * wv,
401                             int allow_text_field_p, int no_keys_p,
402                             int menu_entry_p, int accel_p)
403 {
404         /* This function cannot GC because gc_currently_forbidden is set when
405            it's called */
406         Lisp_Gui_Item *pgui = 0;
407
408         /* degenerate case */
409         if (STRINGP(gui_item)) {
410                 wv->type = TEXT_TYPE;
411                 if (accel_p)
412                         wv->name = add_accel_and_to_external(gui_item);
413                 else
414                         LISP_STRING_TO_EXTERNAL_MALLOC(gui_item, wv->name,
415                                                        Qlwlib_encoding);
416                 return 1;
417         } else if (!GUI_ITEMP(gui_item))
418                 syntax_error("need a string or a gui_item here", gui_item);
419
420         pgui = XGUI_ITEM(gui_item);
421
422         if (!NILP(pgui->filter))
423                 syntax_error(":filter keyword not permitted on leaf nodes",
424                              gui_item);
425
426 #ifdef HAVE_MENUBARS
427         if (menu_entry_p
428             && !gui_item_included_p(gui_item, Vmenubar_configuration)) {
429                 /* the include specification says to ignore this item. */
430                 return 0;
431         }
432 #endif                          /* HAVE_MENUBARS */
433
434         if (!STRINGP(pgui->name))
435                 pgui->name = Feval(pgui->name);
436
437         CHECK_STRING(pgui->name);
438         if (accel_p) {
439                 Lisp_Object tmp = gui_item_accelerator(gui_item);
440                 wv->name = add_accel_and_to_external(pgui->name);
441                 wv->accel = LISP_TO_VOID(tmp);
442         } else {
443                 LISP_STRING_TO_EXTERNAL_MALLOC(pgui->name, wv->name,
444                                                Qlwlib_encoding);
445                 wv->accel = LISP_TO_VOID(Qnil);
446         }
447
448         if (!NILP(pgui->suffix)) {
449                 Lisp_Object suffix2;
450
451                 /* Shortcut to avoid evaluating suffix each time */
452                 if (STRINGP(pgui->suffix))
453                         suffix2 = pgui->suffix;
454                 else {
455                         suffix2 = Feval(pgui->suffix);
456                         CHECK_STRING(suffix2);
457                 }
458
459                 LISP_STRING_TO_EXTERNAL_MALLOC(suffix2, wv->value,
460                                                Qlwlib_encoding);
461         }
462
463         wv_set_evalable_slot(wv->enabled, pgui->active);
464         wv_set_evalable_slot(wv->selected, pgui->selected);
465
466         if (!NILP(pgui->callback) || !NILP(pgui->callback_ex)) {
467                 Lisp_Object tmp = cons3(gui_object_instance,
468                                         pgui->callback, pgui->callback_ex);
469                 wv->call_data = LISP_TO_VOID(tmp);
470         }
471
472         if (no_keys_p
473 #ifdef HAVE_MENUBARS
474             || (menu_entry_p && !menubar_show_keybindings)
475 #endif
476                 ) {
477                 wv->key = 0;
478         } else if (!NILP(pgui->keys)) {
479                 /* Use this string to generate key bindings */
480                 CHECK_STRING(pgui->keys);
481                 pgui->keys = Fsubstitute_command_keys(pgui->keys);
482                 if (XSTRING_LENGTH(pgui->keys) > 0)
483                         LISP_STRING_TO_EXTERNAL_MALLOC(pgui->keys, wv->key,
484                                                        Qlwlib_encoding);
485                 else
486                         wv->key = 0;
487         } else if (SYMBOLP(pgui->callback)) {   /* Show the binding of this command. */
488                 char buf[1024]; /* #### */
489                 /* #### Warning, dependency here on current_buffer and point */
490                 where_is_to_char(pgui->callback, buf);
491                 if (buf[0])
492                         C_STRING_TO_EXTERNAL_MALLOC(buf, wv->key,
493                                                     Qlwlib_encoding);
494                 else
495                         wv->key = 0;
496         }
497
498         CHECK_SYMBOL(pgui->style);
499         if (NILP(pgui->style)) {
500                 Bufbyte *intname = NULL;
501                 Bytecount intlen;
502                 /* If the callback is nil, treat this item like unselectable text.
503                    This way, dashes will show up as a separator. */
504                 if (!wv->enabled)
505                         wv->type = BUTTON_TYPE;
506                 TO_INTERNAL_FORMAT(C_STRING, wv->name,
507                                    ALLOCA, (intname, intlen), Qlwlib_encoding);
508                 if (intname != NULL && separator_string_p(intname)) {
509                         wv->type = SEPARATOR_TYPE;
510                         wv->value =
511                             menu_separator_style_and_to_external(intname);
512                 } else {
513 #if 0
514                         /* #### - this is generally desirable for menubars, but it breaks
515                            a package that uses dialog boxes and next_command_event magic
516                            to use the callback slot in dialog buttons for data instead of
517                            a real callback.
518
519                            Code is data, right?  The beauty of LISP abuse.   --Stig */
520                         if (NILP(callback))
521                                 wv->type = TEXT_TYPE;
522                         else
523 #endif
524                                 wv->type = BUTTON_TYPE;
525                 }
526         } else if (EQ(pgui->style, Qbutton))
527                 wv->type = BUTTON_TYPE;
528         else if (EQ(pgui->style, Qtoggle))
529                 wv->type = TOGGLE_TYPE;
530         else if (EQ(pgui->style, Qradio))
531                 wv->type = RADIO_TYPE;
532         else if (EQ(pgui->style, Qtext)) {
533                 wv->type = TEXT_TYPE;
534 #if 0
535                 wv->value = wv->name;
536                 wv->name = "value";
537 #endif
538         } else
539                 syntax_error_2("Unknown style", pgui->style, gui_item);
540
541         if (!allow_text_field_p && (wv->type == TEXT_TYPE))
542                 syntax_error("Text field not allowed in this context",
543                              gui_item);
544
545         if (!NILP(pgui->selected) && EQ(pgui->style, Qtext))
546                 syntax_error
547                     (":selected only makes sense with :style toggle, radio or button",
548                      gui_item);
549         return 1;
550 }
551
552 /* parse tree's of gui items into widget_value hierarchies */
553 static void gui_item_children_to_widget_values(Lisp_Object
554                                                gui_object_instance,
555                                                Lisp_Object items,
556                                                widget_value * parent,
557                                                int accel_p);
558
559 static widget_value *gui_items_to_widget_values_1(Lisp_Object
560                                                   gui_object_instance,
561                                                   Lisp_Object items,
562                                                   widget_value * parent,
563                                                   widget_value * prev,
564                                                   int accel_p)
565 {
566         widget_value *wv = 0;
567
568         assert((parent || prev) && !(parent && prev));
569         /* now walk the tree creating widget_values as appropriate */
570         if (!CONSP(items)) {
571                 wv = xmalloc_widget_value();
572                 if (parent)
573                         parent->contents = wv;
574                 else
575                         prev->next = wv;
576                 if (!button_item_to_widget_value(gui_object_instance,
577                                                  items, wv, 0, 1, 0, accel_p)) {
578                         free_widget_value_tree(wv);
579                         if (parent)
580                                 parent->contents = 0;
581                         else
582                                 prev->next = 0;
583                 } else
584                         wv->value = xstrdup(wv->name);  /* what a mess... */
585         } else {
586                 /* first one is the parent */
587                 if (CONSP(XCAR(items)))
588                         syntax_error("parent item must not be a list",
589                                      XCAR(items));
590
591                 if (parent)
592                         wv = gui_items_to_widget_values_1(gui_object_instance,
593                                                           XCAR(items), parent,
594                                                           0, accel_p);
595                 else
596                         wv = gui_items_to_widget_values_1(gui_object_instance,
597                                                           XCAR(items), 0, prev,
598                                                           accel_p);
599                 /* the rest are the children */
600                 gui_item_children_to_widget_values(gui_object_instance,
601                                                    XCDR(items), wv, accel_p);
602         }
603         return wv;
604 }
605
606 static void
607 gui_item_children_to_widget_values(Lisp_Object gui_object_instance,
608                                    Lisp_Object items, widget_value * parent,
609                                    int accel_p)
610 {
611         widget_value *wv = 0, *prev = 0;
612         Lisp_Object rest;
613         CHECK_CONS(items);
614
615         /* first one is master */
616         prev = gui_items_to_widget_values_1(gui_object_instance, XCAR(items),
617                                             parent, 0, accel_p);
618         /* the rest are the children */
619         LIST_LOOP(rest, XCDR(items)) {
620                 Lisp_Object tab = XCAR(rest);
621                 wv = gui_items_to_widget_values_1(gui_object_instance, tab, 0,
622                                                   prev, accel_p);
623                 prev = wv;
624         }
625 }
626
627 widget_value *gui_items_to_widget_values(Lisp_Object gui_object_instance,
628                                          Lisp_Object items, int accel_p)
629 {
630         /* This function can GC */
631         widget_value *control = 0, *tmp = 0;
632         int count = specpdl_depth();
633         Lisp_Object wv_closure;
634
635         if (NILP(items))
636                 syntax_error("must have some items", items);
637
638         /* Inhibit GC during this conversion.  The reasons for this are
639            the same as in menu_item_descriptor_to_widget_value(); see
640            the large comment above that function. */
641         record_unwind_protect(restore_gc_inhibit,
642                               make_int(gc_currently_forbidden));
643         gc_currently_forbidden = 1;
644
645         /* Also make sure that we free the partially-created widget_value
646            tree on Lisp error. */
647         control = xmalloc_widget_value();
648         wv_closure = make_opaque_ptr(control);
649         record_unwind_protect(widget_value_unwind, wv_closure);
650
651         gui_items_to_widget_values_1(gui_object_instance, items, control, 0,
652                                      accel_p);
653
654         /* mess about getting the data we really want */
655         tmp = control;
656         control = control->contents;
657         tmp->next = 0;
658         tmp->contents = 0;
659         free_widget_value_tree(tmp);
660
661         /* No more need to free the half-filled-in structures. */
662         set_opaque_ptr(wv_closure, 0);
663         unbind_to(count, Qnil);
664
665         return control;
666 }
667
668 /* This is a kludge to make sure emacs can only link against a version of
669    lwlib that was compiled in the right way.  Emacs references symbols which
670    correspond to the way it thinks lwlib was compiled, and if lwlib wasn't
671    compiled in that way, then somewhat meaningful link errors will result.
672    The alternatives to this range from obscure link errors, to obscure
673    runtime errors that look a lot like bugs.
674  */
675
676 static void sanity_check_lwlib(void)
677 {
678 #define MACROLET(v) { extern int v; v = 1; }
679
680 #if (XlibSpecificationRelease == 4)
681         MACROLET(lwlib_uses_x11r4);
682 #elif (XlibSpecificationRelease == 5)
683         MACROLET(lwlib_uses_x11r5);
684 #elif (XlibSpecificationRelease == 6)
685         MACROLET(lwlib_uses_x11r6);
686 #else
687         MACROLET(lwlib_uses_unknown_x11);
688 #endif
689 #ifdef LWLIB_USES_MOTIF
690         MACROLET(lwlib_uses_motif);
691 #if (XmVersion >= 1002)
692         MACROLET(lwlib_uses_motif_1_2);
693 #else
694         MACROLET(lwlib_does_not_use_motif_1_2);
695 #endif
696 #else
697         MACROLET(lwlib_does_not_use_motif);
698 #endif  /* LWLIB_USES_MOTIF */
699 #ifdef LWLIB_MENUBARS_LUCID
700         MACROLET(lwlib_menubars_lucid);
701 #elif defined (HAVE_MENUBARS)
702         MACROLET(lwlib_menubars_motif);
703 #endif
704 #ifdef LWLIB_SCROLLBARS_LUCID
705         MACROLET(lwlib_scrollbars_lucid);
706 #elif defined (LWLIB_SCROLLBARS_MOTIF)
707         MACROLET(lwlib_scrollbars_motif);
708 #elif defined (HAVE_SCROLLBARS)
709         MACROLET(lwlib_scrollbars_athena);
710 #endif
711 #ifdef LWLIB_DIALOGS_MOTIF
712         MACROLET(lwlib_dialogs_motif);
713 #elif defined (HAVE_DIALOGS)
714         MACROLET(lwlib_dialogs_athena);
715 #endif
716 #ifdef LWLIB_WIDGETS_MOTIF
717         MACROLET(lwlib_widgets_motif);
718 #elif defined (HAVE_WIDGETS)
719         MACROLET(lwlib_widgets_athena);
720 #endif
721
722 #undef MACROLET
723 }
724
725 void syms_of_gui_x(void)
726 {
727         INIT_LRECORD_IMPLEMENTATION(popup_data);
728 }
729
730 void reinit_vars_of_gui_x(void)
731 {
732         lwlib_id_tick = (1 << 16);      /* start big, to not conflict with Energize */
733 #ifdef HAVE_POPUPS
734         popup_up_p = 0;
735 #endif
736
737         /* this makes only safe calls as in emacs.c */
738         sanity_check_lwlib();
739 }
740
741 void vars_of_gui_x(void)
742 {
743         reinit_vars_of_gui_x();
744
745         Vpopup_callbacks = Qnil;
746         staticpro(&Vpopup_callbacks);
747 }