]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
f78d2beefcbcca193fde40327e0b48f7caae3f1d
[~andy/gtk] / gtk / gtkmain.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include <X11/Xlocale.h>        /* so we get the right setlocale */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <gmodule.h>
24 #include "gtkbutton.h"
25 #include "gtkdnd.h"
26 #include "gtkfeatures.h"
27 #include "gtkhscrollbar.h"
28 #include "gtkhseparator.h"
29 #include "gtkmain.h"
30 #include "gtkpreview.h"
31 #include "gtkrc.h"
32 #include "gtkselection.h"
33 #include "gtksignal.h"
34 #include "gtktable.h"
35 #include "gtktext.h"
36 #include "gtkvbox.h"
37 #include "gtkvscrollbar.h"
38 #include "gtkwidget.h"
39 #include "gtkwindow.h"
40 #include "gtkprivate.h"
41 #include "gdk/gdki18n.h"
42 #include "../config.h"
43 #include "gtkdebug.h"
44
45
46 /* Private type definitions
47  */
48 typedef struct _GtkInitFunction          GtkInitFunction;
49 typedef struct _GtkQuitFunction          GtkQuitFunction;
50 typedef struct _GtkTimeoutFunction       GtkTimeoutFunction;
51 typedef struct _GtkIdleHook              GtkIdleHook;
52 typedef struct _GtkInputFunction         GtkInputFunction;
53 typedef struct _GtkKeySnooperData        GtkKeySnooperData;
54
55 struct _GtkInitFunction
56 {
57   GtkFunction function;
58   gpointer data;
59 };
60
61 struct _GtkQuitFunction
62 {
63   guint id;
64   guint main_level;
65   GtkCallbackMarshal marshal;
66   GtkFunction function;
67   gpointer data;
68   GtkDestroyNotify destroy;
69 };
70
71 struct _GtkTimeoutFunction
72 {
73   guint tag;
74   guint32 start;
75   guint32 interval;
76   guint32 originterval;
77   GtkFunction function;
78   GtkCallbackMarshal marshal;
79   gpointer data;
80   GtkDestroyNotify destroy;
81 };
82
83 enum
84 {
85   GTK_HOOK_MARSHAL      = 1 << (G_HOOK_FLAG_USER_SHIFT)
86 };
87
88 struct _GtkIdleHook
89 {
90   GHook hook;
91   gint  priority;
92 };
93 #define GTK_IDLE_HOOK(hook)     ((GtkIdleHook*) hook)
94
95 struct _GtkInputFunction
96 {
97   GtkCallbackMarshal callback;
98   gpointer data;
99   GtkDestroyNotify destroy;
100 };
101
102 struct _GtkKeySnooperData
103 {
104   GtkKeySnoopFunc func;
105   gpointer func_data;
106   guint id;
107 };
108
109 static void     gtk_exit_func            (void);
110 static void     gtk_invoke_hook          (GHookList *hook_list,
111                                           GHook     *hook);
112 static void     gtk_handle_idles         (void);
113 static gboolean gtk_finish_idles         (void);
114 static gint  gtk_quit_invoke_function    (GtkQuitFunction    *quitf);
115 static void  gtk_quit_destroy            (GtkQuitFunction    *quitf);
116 static void  gtk_timeout_insert          (GtkTimeoutFunction *timeoutf);
117 static void  gtk_handle_current_timeouts (guint32             the_time);
118 static gint  gtk_invoke_key_snoopers     (GtkWidget          *grab_widget,
119                                           GdkEvent           *event);
120 static void  gtk_handle_timeouts         (void);
121 static void  gtk_handle_timer            (void);
122 static void  gtk_propagate_event         (GtkWidget          *widget,
123                                           GdkEvent           *event);
124 #if 0
125 static void  gtk_error                   (gchar              *str);
126 static void  gtk_warning                 (gchar              *str);
127 static void  gtk_message                 (gchar              *str);
128 static void  gtk_print                   (gchar              *str);
129 #endif
130
131 static gint  gtk_timeout_remove_from_list (GList               **list, 
132                                            guint                 tag, 
133                                            gint                  remove_link);
134
135 static gint  gtk_timeout_compare         (gconstpointer      a, 
136                                           gconstpointer      b);
137
138
139 const guint gtk_major_version = GTK_MAJOR_VERSION;
140 const guint gtk_minor_version = GTK_MINOR_VERSION;
141 const guint gtk_micro_version = GTK_MICRO_VERSION;
142 const guint gtk_binary_age = GTK_BINARY_AGE;
143 const guint gtk_interface_age = GTK_INTERFACE_AGE;
144
145 static gboolean iteration_done = FALSE;
146 static guint gtk_main_loop_level = 0;
147 static gint gtk_initialized = FALSE;
148 static GdkEvent *next_event = NULL;
149 static GList *current_events = NULL;
150
151 static GSList *grabs = NULL;               /* A stack of unique grabs. The grabbing
152                                             *  widget is the first one on the list.
153                                             */
154 static GList *init_functions = NULL;       /* A list of init functions.
155                                             */
156 static GList *quit_functions = NULL;       /* A list of quit functions.
157                                             */
158
159
160 /* When handling timeouts, the algorithm is to move all of the expired
161  * timeouts from timeout_functions to current_timeouts then process
162  * them as a batch. If one of the timeouts recursively calls the main
163  * loop, then the remainder of the timeouts in current_timeouts will
164  * be processed before anything else happens.
165  * 
166  * Each timeout is procesed as follows:
167  *
168  * - The timeout is removed from current_timeouts
169  * - The timeout is pushed on the running_timeouts stack
170  * - The timeout is executed
171  * - The timeout stack is popped
172  * - If the timeout function wasn't removed from the stack while executing,
173  *   and it returned TRUE, it is added back to timeout_functions, otherwise
174  *   it is destroyed if necessary.
175  *
176  * gtk_timeout_remove() works by checking for the timeout in
177  * timeout_functions current_timeouts and running_timeouts. If it is
178  * found in one of the first two, it is removed and destroyed. If it
179  * is found in running_timeouts, it is destroyed and ->data is set to
180  * NULL for the stack entry.
181  *
182  * Idle functions work pretty much identically.  
183  */
184
185 static GList *timeout_functions = NULL;    /* A list of timeout functions sorted by
186                                             *  when the length of the time interval
187                                             *  remaining. Therefore, the first timeout
188                                             *  function to expire is at the head of
189                                             *  the list and the last to expire is at
190                                             *  the tail of the list.  */
191 /* Prioritized idle callbacks */
192 static GHookList        idle_hooks = { 0 };
193 static GHook            *last_idle = NULL;
194
195 /* The timeouts that are currently being processed 
196  *  by gtk_handle_current_timeouts
197  */
198 static GList *current_timeouts = NULL;
199
200 /* A stack of timeouts that is currently being executed
201  */
202 static GList *running_timeouts = NULL;
203
204 static GMemChunk *timeout_mem_chunk = NULL;
205 static GMemChunk *quit_mem_chunk = NULL;
206
207 static GSList *key_snoopers = NULL;
208
209 static GdkVisual *gtk_visual;              /* The visual to be used in creating new
210                                             *  widgets.
211                                             */
212 static GdkColormap *gtk_colormap;          /* The colormap to be used in creating new
213                                             *  widgets.
214                                             */
215
216 guint gtk_debug_flags = 0;                 /* Global GTK debug flag */
217
218 #ifdef G_ENABLE_DEBUG
219 static GDebugKey gtk_debug_keys[] = {
220   {"objects", GTK_DEBUG_OBJECTS},
221   {"misc", GTK_DEBUG_MISC},
222   {"signals", GTK_DEBUG_SIGNALS},
223   {"dnd", GTK_DEBUG_DND}
224 };
225
226 static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
227
228 #endif /* G_ENABLE_DEBUG */
229
230 gchar*
231 gtk_check_version (guint required_major,
232                    guint required_minor,
233                    guint required_micro)
234 {
235   if (required_major > GTK_MAJOR_VERSION)
236     return "Gtk+ version to old (major mismatch)";
237   if (required_major < GTK_MAJOR_VERSION)
238     return "Gtk+ version to new (major mismatch)";
239   if (required_minor > GTK_MINOR_VERSION)
240     return "Gtk+ version to old (minor mismatch)";
241   if (required_minor < GTK_MINOR_VERSION)
242     return "Gtk+ version to new (minor mismatch)";
243   if (required_micro < GTK_MICRO_VERSION - GTK_BINARY_AGE)
244     return "Gtk+ version to new (micro mismatch)";
245   if (required_micro > GTK_MICRO_VERSION)
246     return "Gtk+ version to old (micro mismatch)";
247   return NULL;
248 }
249
250
251 gint gtk_use_mb = -1;
252
253 void
254 gtk_init (int    *argc,
255           char ***argv)
256 {
257   GSList *gtk_modules = NULL;
258   GSList *slist;
259   gchar *current_locale;
260   gchar *env_string = NULL;
261
262   if (gtk_initialized)
263     return;
264
265 #if     0
266   g_set_error_handler (gtk_error);
267   g_set_warning_handler (gtk_warning);
268   g_set_message_handler (gtk_message);
269   g_set_print_handler (gtk_print);
270 #endif
271   
272   /* Initialize "gdk". We pass along the 'argc' and 'argv'
273    *  parameters as they contain information that GDK uses
274    */
275   gdk_init (argc, argv);
276   
277 #ifdef G_ENABLE_DEBUG
278   env_string = getenv ("GTK_DEBUG");
279   if (env_string != NULL)
280     {
281       gtk_debug_flags = g_parse_debug_string (env_string,
282                                               gtk_debug_keys,
283                                               gtk_ndebug_keys);
284       env_string = NULL;
285     }
286 #endif  /* G_ENABLE_DEBUG */
287
288   env_string = getenv ("GTK_MODULES");
289   if (env_string)
290     {
291       gchar **modules, **as;
292
293       modules = g_strsplit (env_string, ":", -1);
294       for (as = modules; *as; as++)
295         {
296           if (**as)
297             gtk_modules = g_slist_prepend (gtk_modules, *as);
298           else
299             g_free (*as);
300         }
301       g_free (modules);
302       env_string = NULL;
303     }
304
305   if (argc && argv)
306     {
307       gint i, j, k;
308       
309       for (i = 1; i < *argc;)
310         {
311           if (strcmp ("--gtk-module", (*argv)[i]) == 0 ||
312               strncmp ("--gtk-module=", (*argv)[i], 13) == 0)
313             {
314               gchar *module_name = (*argv)[i] + 12;
315               
316               if (*module_name == '=')
317                 module_name++;
318               else
319                 {
320                   (*argv)[i] = NULL;
321                   i += 1;
322                   module_name = (*argv)[i];
323                 }
324               (*argv)[i] = NULL;
325
326               gtk_modules = g_slist_prepend (gtk_modules, g_strdup (module_name));
327             }
328           else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0)
329             {
330               GLogLevelFlags fatal_mask;
331               
332               fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
333               fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
334               g_log_set_always_fatal (fatal_mask);
335               (*argv)[i] = NULL;
336             }
337 #ifdef G_ENABLE_DEBUG
338           else if ((strcmp ("--gtk-debug", (*argv)[i]) == 0) ||
339                    (strncmp ("--gtk-debug=", (*argv)[i], 12) == 0))
340             {
341               gchar *equal_pos = strchr ((*argv)[i], '=');
342               
343               if (equal_pos != NULL)
344                 {
345                   gtk_debug_flags |= g_parse_debug_string (equal_pos+1,
346                                                            gtk_debug_keys,
347                                                            gtk_ndebug_keys);
348                 }
349               else if ((i + 1) < *argc && (*argv)[i + 1])
350                 {
351                   gtk_debug_flags |= g_parse_debug_string ((*argv)[i+1],
352                                                            gtk_debug_keys,
353                                                            gtk_ndebug_keys);
354                   (*argv)[i] = NULL;
355                   i += 1;
356                 }
357               (*argv)[i] = NULL;
358             }
359           else if ((strcmp ("--gtk-no-debug", (*argv)[i]) == 0) ||
360                    (strncmp ("--gtk-no-debug=", (*argv)[i], 15) == 0))
361             {
362               gchar *equal_pos = strchr ((*argv)[i], '=');
363               
364               if (equal_pos != NULL)
365                 {
366                   gtk_debug_flags &= ~g_parse_debug_string (equal_pos+1,
367                                                             gtk_debug_keys,
368                                                             gtk_ndebug_keys);
369                 }
370               else if ((i + 1) < *argc && (*argv)[i + 1])
371                 {
372                   gtk_debug_flags &= ~g_parse_debug_string ((*argv)[i+1],
373                                                             gtk_debug_keys,
374                                                             gtk_ndebug_keys);
375                   (*argv)[i] = NULL;
376                   i += 1;
377                 }
378               (*argv)[i] = NULL;
379             }
380 #endif /* G_ENABLE_DEBUG */
381           i += 1;
382         }
383       
384       for (i = 1; i < *argc; i++)
385         {
386           for (k = i; k < *argc; k++)
387             if ((*argv)[k] != NULL)
388               break;
389           
390           if (k > i)
391             {
392               k -= i;
393               for (j = i + k; j < *argc; j++)
394                 (*argv)[j-k] = (*argv)[j];
395               *argc -= k;
396             }
397         }
398     }
399   
400   /* Check if there is a good chance the mb functions will handle things
401    * correctly - set if either mblen("\xc0", MB_CUR_MAX) == 1 in the
402    * C locale, or we're using X's mb functions. (-DX_LOCALE && locale != C)
403    */
404  
405   current_locale = setlocale (LC_CTYPE, NULL);
406
407 #ifdef X_LOCALE
408   if ((strcmp (current_locale, "C")) && (strcmp (current_locale, "POSIX")))
409     gtk_use_mb = TRUE;
410   else
411 #endif /* X_LOCALE */
412     {
413       /* Detect GNU libc, where mb == UTF8. Not useful unless it's
414        * really a UTF8 locale. The below still probably will
415        * screw up on Greek, Cyrillic, etc, encoded as UTF8.
416        */
417       
418       wchar_t result;
419       gtk_use_mb = TRUE;
420       
421       if ((MB_CUR_MAX == 2) &&
422           (mbstowcs (&result, "\xdd\xa5", 1) > 0) &&
423           result == 0x765)
424         {
425           if ((strlen (current_locale) < 4) ||
426               g_strcasecmp (current_locale + strlen(current_locale) - 4, "utf8"))
427             gtk_use_mb = FALSE;
428         }
429     }
430
431   GTK_NOTE (MISC,
432             g_message ("%s multi-byte string functions.", 
433                        gtk_use_mb ? "Using" : "Not using"));
434
435
436   /* load gtk modules */
437   gtk_modules = g_slist_reverse (gtk_modules);
438   for (slist = gtk_modules; slist; slist = slist->next)
439     {
440       gchar *module_name;
441       GModule *module = NULL;
442       GtkModuleInitFunc modinit_func = NULL;
443       
444       module_name = slist->data;
445       slist->data = NULL;
446       if (!(module_name[0] == '/' ||
447             (module_name[0] == 'l' &&
448              module_name[1] == 'i' &&
449              module_name[2] == 'b')))
450         {
451           gchar *old = module_name;
452           
453           module_name = g_strconcat ("lib", module_name, ".so", NULL);
454           g_free (old);
455         }
456       if (g_module_supported ())
457         {
458           module = g_module_open (module_name, G_MODULE_BIND_LAZY);
459           if (module &&
460               g_module_symbol (module, "gtk_module_init", (gpointer*) &modinit_func) &&
461               modinit_func)
462             {
463               if (!g_slist_find (gtk_modules, modinit_func))
464                 {
465                   g_module_make_resident (module);
466                   slist->data = modinit_func;
467                 }
468               else
469                 {
470                   g_module_close (module);
471                   module = NULL;
472                 }
473             }
474         }
475       if (!modinit_func)
476         {
477           g_warning ("Failed to load module \"%s\": %s",
478                      module ? g_module_name (module) : module_name,
479                      g_module_error ());
480           if (module)
481             g_module_close (module);
482         }
483       g_free (module_name);
484     }
485
486   /* Initialize the default visual and colormap to be
487    *  used in creating widgets. (We want to use the system
488    *  defaults so as to be nice to the colormap).
489    */
490   gtk_visual = gdk_visual_get_system ();
491   gtk_colormap = gdk_colormap_get_system ();
492
493   gtk_type_init ();
494   gtk_signal_init ();
495   gtk_rc_init ();
496   
497   
498   /* Register an exit function to make sure we are able to cleanup.
499    */
500   g_atexit (gtk_exit_func);
501   
502   /* Set the 'initialized' flag.
503    */
504   gtk_initialized = TRUE;
505
506   /* initialize gtk modules
507    */
508   for (slist = gtk_modules; slist; slist = slist->next)
509     {
510       if (slist->data)
511         {
512           GtkModuleInitFunc modinit;
513           
514           modinit = slist->data;
515           modinit (argc, argv);
516         }
517     }
518   g_slist_free (gtk_modules);
519 }
520
521 void
522 gtk_exit (int errorcode)
523 {
524   /* Only if "gtk" has been initialized should we de-initialize.
525    */
526   /* de-initialisation is done by the gtk_exit_funct(),
527    * no need to do this here (Alex J.)
528    */
529   gdk_exit(errorcode);
530 }
531
532 gchar*
533 gtk_set_locale (void)
534 {
535   return gdk_set_locale ();
536 }
537
538 void
539 gtk_main (void)
540 {
541   GList *tmp_list;
542   GList *functions;
543   GtkInitFunction *init;
544   int old_done;
545   
546   gtk_main_loop_level++;
547   
548   tmp_list = functions = init_functions;
549   init_functions = NULL;
550   
551   while (tmp_list)
552     {
553       init = tmp_list->data;
554       tmp_list = tmp_list->next;
555       
556       (* init->function) (init->data);
557       g_free (init);
558     }
559   g_list_free (functions);
560   
561   old_done = iteration_done;
562   while (!gtk_main_iteration ())
563     ;
564   iteration_done = old_done;
565
566   if (quit_functions)
567     {
568       GList *reinvoke_list = NULL;
569       GtkQuitFunction *quitf;
570
571       while (quit_functions)
572         {
573           quitf = quit_functions->data;
574
575           quit_functions = g_list_remove_link (quit_functions, quit_functions);
576
577           if ((quitf->main_level && quitf->main_level != gtk_main_loop_level) ||
578               gtk_quit_invoke_function (quitf))
579             {
580               reinvoke_list = g_list_prepend (reinvoke_list, quitf);
581             }
582           else
583             {
584               g_list_free (tmp_list);
585               gtk_quit_destroy (quitf);
586             }
587         }
588       if (reinvoke_list)
589         {
590           GList *work;
591           
592           work = g_list_last (reinvoke_list);
593           if (quit_functions)
594             quit_functions->prev = work;
595           work->next = quit_functions;
596           quit_functions = work;
597         }
598     }
599               
600   gtk_main_loop_level--;
601 }
602
603 guint
604 gtk_main_level (void)
605 {
606   return gtk_main_loop_level;
607 }
608
609 void
610 gtk_main_quit (void)
611 {
612   iteration_done = TRUE;
613 }
614
615 gint
616 gtk_events_pending (void)
617 {
618   gint result = 0;
619   
620   /* if this function is called from a timeout which will only return
621    * if gtk needs processor time, we need to take iteration_done==TRUE
622    * into account as well.
623    */
624   result = iteration_done;
625   result += next_event != NULL;
626   result += gdk_events_pending();
627
628   result += last_idle != NULL;
629   result += current_timeouts != NULL;
630
631   if (!result)
632     {
633       GHook *hook;
634
635       hook = g_hook_first_valid (&idle_hooks, FALSE);
636
637       result += hook && GTK_IDLE_HOOK (hook)->priority <= GTK_PRIORITY_INTERNAL;
638     }
639   
640   if (!result && timeout_functions)
641     {
642       guint32 the_time;
643       GtkTimeoutFunction *timeoutf;
644       
645       the_time = gdk_time_get ();
646       
647       timeoutf = timeout_functions->data;
648       
649       result += timeoutf->interval <= (the_time - timeoutf->start);
650     }
651   
652   return result;
653 }
654
655 gint 
656 gtk_main_iteration (void)
657 {
658   return gtk_main_iteration_do (TRUE);
659 }
660
661 gint
662 gtk_main_iteration_do (gboolean blocking)
663 {
664   GtkWidget *event_widget;
665   GtkWidget *grab_widget;
666   GdkEvent *event = NULL;
667   GList *tmp_list;
668   
669   iteration_done = FALSE;
670   
671   /* If this is a recursive call, and there are pending timeouts or
672    * idles, finish them, then return immediately to avoid blocking
673    * in gdk_event_get()
674    */
675   if (current_timeouts)
676     {
677       gtk_handle_current_timeouts( gdk_time_get());
678
679       if (iteration_done)
680         gdk_flush ();
681
682       return iteration_done;
683     }
684   if (last_idle && gtk_finish_idles ())
685     {
686       if (iteration_done)
687         gdk_flush ();
688
689       return iteration_done;
690     }
691   
692   /* If there is a valid event in 'next_event' then move it to 'event'
693    */
694   if (next_event)
695     {
696       event = next_event;
697       next_event = NULL;
698     }
699   
700   /* If we don't have an event then get one.
701    */
702   if (!event)
703     {
704       /* Handle setting of the "gdk" timer. If there are no
705        *  timeout functions, then the timer is turned off.
706        *  If there are timeout functions, then the timer is
707        *  set to the shortest timeout interval (which is
708        *  the first timeout function).
709        */
710       gtk_handle_timer ();
711       
712       if (blocking)
713         event = gdk_event_get ();
714     }
715   
716   /* "gdk_event_get" can return FALSE if the timer goes off
717    *  and no events are pending. Therefore, we should make
718    *  sure that we got an event before continuing.
719    */
720   if (event)
721     {
722       /* If there are any events pending then get the next one.
723        */
724       if (gdk_events_pending () > 0)
725         next_event = gdk_event_get ();
726       
727       /* Try to compress enter/leave notify events. These event
728        *  pairs occur when the mouse is dragged quickly across
729        *  a window with many buttons (or through a menu). Instead
730        *  of highlighting and de-highlighting each widget that
731        *  is crossed it is better to simply de-highlight the widget
732        *  which contained the mouse initially and highlight the
733        *  widget which ends up containing the mouse.
734        */
735       if (next_event)
736         if (((event->type == GDK_ENTER_NOTIFY) ||
737              (event->type == GDK_LEAVE_NOTIFY)) &&
738             ((next_event->type == GDK_ENTER_NOTIFY) ||
739              (next_event->type == GDK_LEAVE_NOTIFY)) &&
740             (next_event->type != event->type) &&
741             (next_event->any.window == event->any.window))
742           {
743             gdk_event_free (event);
744             gdk_event_free (next_event);
745             next_event = NULL;
746             
747             goto event_handling_done;
748           }
749       
750       /* Find the widget which got the event. We store the widget
751        *  in the user_data field of GdkWindow's.
752        *  Ignore the event if we don't have a widget for it, except
753        *  for GDK_PROPERTY_NOTIFY events which are handled specialy.
754        *  Though this happens rarely, bogus events can occour
755        *  for e.g. destroyed GdkWindows. 
756        */
757       event_widget = gtk_get_event_widget (event);
758       if (!event_widget)
759         {
760           /* To handle selection INCR transactions, we select
761            * PropertyNotify events on the requestor window and create
762            * a corresponding (fake) GdkWindow so that events get
763            * here. There won't be a widget though, so we have to handle
764            * them specially
765            */
766           if (event->type == GDK_PROPERTY_NOTIFY)
767             gtk_selection_incr_event (event->any.window,
768                                       &event->property);
769           
770           gdk_event_free (event);
771           
772           goto event_handling_done;
773         }
774       
775       /* Push the event onto a stack of current events for
776        * gtk_current_event_get().
777        */
778       current_events = g_list_prepend (current_events, event);
779       
780       /* If there is a grab in effect...
781        */
782       if (grabs)
783         {
784           grab_widget = grabs->data;
785           
786           /* If the grab widget is an ancestor of the event widget
787            *  then we send the event to the original event widget.
788            *  This is the key to implementing modality.
789            */
790           if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
791               gtk_widget_is_ancestor (event_widget, grab_widget))
792             grab_widget = event_widget;
793         }
794       else
795         {
796           grab_widget = event_widget;
797         }
798
799       /* Not all events get sent to the grabbing widget.
800        * The delete, destroy, expose, focus change and resize
801        *  events still get sent to the event widget because
802        *  1) these events have no meaning for the grabbing widget
803        *  and 2) redirecting these events to the grabbing widget
804        *  could cause the display to be messed up.
805        * 
806        * Drag events are also not redirected, since it isn't
807        *  clear what the semantics of that would be.
808        */
809       switch (event->type)
810         {
811         case GDK_NOTHING:
812           break;
813           
814         case GDK_DELETE:
815           gtk_widget_ref (event_widget);
816           if (!gtk_widget_event (event_widget, event) &&
817               !GTK_OBJECT_DESTROYED (event_widget))
818             gtk_widget_destroy (event_widget);
819           gtk_widget_unref (event_widget);
820           break;
821           
822         case GDK_DESTROY:
823           gtk_widget_ref (event_widget);
824           gtk_widget_event (event_widget, event);
825           if (!GTK_OBJECT_DESTROYED (event_widget))
826             gtk_widget_destroy (event_widget);
827           gtk_widget_unref (event_widget);
828           break;
829           
830         case GDK_PROPERTY_NOTIFY:
831         case GDK_EXPOSE:
832         case GDK_NO_EXPOSE:
833         case GDK_FOCUS_CHANGE:
834         case GDK_CONFIGURE:
835         case GDK_MAP:
836         case GDK_UNMAP:
837         case GDK_SELECTION_CLEAR:
838         case GDK_SELECTION_REQUEST:
839         case GDK_SELECTION_NOTIFY:
840         case GDK_CLIENT_EVENT:
841         case GDK_VISIBILITY_NOTIFY:
842           gtk_widget_event (event_widget, event);
843           break;
844           
845         case GDK_KEY_PRESS:
846         case GDK_KEY_RELEASE:
847           if (key_snoopers)
848             {
849               if (gtk_invoke_key_snoopers (grab_widget, event))
850                 break;
851             }
852           /* else fall through */
853         case GDK_MOTION_NOTIFY:
854         case GDK_BUTTON_PRESS:
855         case GDK_2BUTTON_PRESS:
856         case GDK_3BUTTON_PRESS:
857         case GDK_BUTTON_RELEASE:
858         case GDK_PROXIMITY_IN:
859         case GDK_PROXIMITY_OUT:
860           gtk_propagate_event (grab_widget, event);
861           break;
862           
863         case GDK_ENTER_NOTIFY:
864           if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
865             {
866               gtk_widget_event (grab_widget, event);
867               if (event_widget == grab_widget)
868                 GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
869             }
870           break;
871           
872         case GDK_LEAVE_NOTIFY:
873           if (GTK_WIDGET_LEAVE_PENDING (event_widget))
874             {
875               GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
876               gtk_widget_event (event_widget, event);
877             }
878           else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
879             gtk_widget_event (grab_widget, event);
880           break;
881
882         case GDK_DRAG_STATUS:
883         case GDK_DROP_FINISHED:
884           gtk_drag_source_handle_event (event_widget, event);
885           break;
886         case GDK_DRAG_ENTER:
887         case GDK_DRAG_LEAVE:
888         case GDK_DRAG_MOTION:
889         case GDK_DROP_START:
890           gtk_drag_dest_handle_event (event_widget, event);
891           break;
892         }
893       
894       tmp_list = current_events;
895       current_events = g_list_remove_link (current_events, tmp_list);
896       g_list_free_1 (tmp_list);
897       
898       gdk_event_free (event);
899     }
900   else
901     {
902       if (gdk_events_pending() == 0)
903         gtk_handle_idles ();
904     }
905   
906 event_handling_done:
907   
908   /* Handle timeout functions that may have expired.
909    */
910   gtk_handle_timeouts ();
911   
912   if (iteration_done)
913     gdk_flush ();
914   
915   return iteration_done;
916 }
917
918 gint
919 gtk_true (void)
920 {
921   return TRUE;
922 }
923
924 gint
925 gtk_false (void)
926 {
927   return FALSE;
928 }
929
930 void
931 gtk_grab_add (GtkWidget *widget)
932 {
933   g_return_if_fail (widget != NULL);
934   
935   if (!GTK_WIDGET_HAS_GRAB (widget))
936     {
937       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
938       
939       grabs = g_slist_prepend (grabs, widget);
940       gtk_widget_ref (widget);
941     }
942 }
943
944 GtkWidget*
945 gtk_grab_get_current (void)
946 {
947   if (grabs)
948     return GTK_WIDGET (grabs->data);
949   return NULL;
950 }
951
952 void
953 gtk_grab_remove (GtkWidget *widget)
954 {
955   g_return_if_fail (widget != NULL);
956   
957   if (GTK_WIDGET_HAS_GRAB (widget))
958     {
959       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
960       
961       grabs = g_slist_remove (grabs, widget);
962       gtk_widget_unref (widget);
963     }
964 }
965
966 void
967 gtk_init_add (GtkFunction function,
968               gpointer    data)
969 {
970   GtkInitFunction *init;
971   
972   init = g_new (GtkInitFunction, 1);
973   init->function = function;
974   init->data = data;
975   
976   init_functions = g_list_prepend (init_functions, init);
977 }
978
979 guint
980 gtk_key_snooper_install (GtkKeySnoopFunc snooper,
981                          gpointer        func_data)
982 {
983   GtkKeySnooperData *data;
984   static guint snooper_id = 1;
985
986   g_return_val_if_fail (snooper != NULL, 0);
987
988   data = g_new (GtkKeySnooperData, 1);
989   data->func = snooper;
990   data->func_data = func_data;
991   data->id = snooper_id++;
992   key_snoopers = g_slist_prepend (key_snoopers, data);
993
994   return data->id;
995 }
996
997 void
998 gtk_key_snooper_remove (guint            snooper_id)
999 {
1000   GtkKeySnooperData *data = NULL;
1001   GSList *slist;
1002
1003   slist = key_snoopers;
1004   while (slist)
1005     {
1006       data = slist->data;
1007       if (data->id == snooper_id)
1008         break;
1009
1010       slist = slist->next;
1011       data = NULL;
1012     }
1013   if (data)
1014     key_snoopers = g_slist_remove (key_snoopers, data);
1015 }
1016
1017 static gint
1018 gtk_invoke_key_snoopers (GtkWidget *grab_widget,
1019                          GdkEvent  *event)
1020 {
1021   GSList *slist;
1022   gint return_val = FALSE;
1023
1024   slist = key_snoopers;
1025   while (slist && !return_val)
1026     {
1027       GtkKeySnooperData *data;
1028
1029       data = slist->data;
1030       slist = slist->next;
1031       return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data);
1032     }
1033
1034   return return_val;
1035 }
1036
1037 guint
1038 gtk_timeout_add_full (guint32            interval,
1039                       GtkFunction        function,
1040                       GtkCallbackMarshal marshal,
1041                       gpointer           data,
1042                       GtkDestroyNotify   destroy)
1043 {
1044   static guint timeout_tag = 1;
1045   GtkTimeoutFunction *timeoutf;
1046   
1047   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
1048
1049   /* Create a new timeout function structure.
1050    * The start time is the current time.
1051    */
1052   if (!timeout_mem_chunk)
1053     timeout_mem_chunk = g_mem_chunk_new ("timeout mem chunk", sizeof (GtkTimeoutFunction),
1054                                          1024, G_ALLOC_AND_FREE);
1055   
1056   timeoutf = g_chunk_new (GtkTimeoutFunction, timeout_mem_chunk);
1057   
1058   timeoutf->tag = timeout_tag++;
1059   timeoutf->start = gdk_time_get ();
1060   timeoutf->interval = interval;
1061   timeoutf->originterval = interval;
1062   timeoutf->function = function;
1063   timeoutf->marshal = marshal;
1064   timeoutf->data = data;
1065   timeoutf->destroy = destroy;
1066   
1067   gtk_timeout_insert (timeoutf);
1068   
1069   return timeoutf->tag;
1070 }
1071
1072 static void
1073 gtk_timeout_destroy (GtkTimeoutFunction *timeoutf)
1074 {
1075   if (timeoutf->destroy)
1076     (timeoutf->destroy) (timeoutf->data);
1077   g_mem_chunk_free (timeout_mem_chunk, timeoutf);
1078 }
1079
1080 guint
1081 gtk_timeout_add (guint32     interval,
1082                  GtkFunction function,
1083                  gpointer    data)
1084 {
1085   return gtk_timeout_add_full (interval, function, FALSE, data, NULL);
1086 }
1087
1088 guint
1089 gtk_timeout_add_interp (guint32            interval,
1090                         GtkCallbackMarshal function,
1091                         gpointer           data,
1092                         GtkDestroyNotify   destroy)
1093 {
1094   return gtk_timeout_add_full (interval, NULL, function, data, destroy);
1095 }
1096
1097 /* Search for the specified tag in a list of timeouts. If it
1098  * is found, destroy the timeout, and either remove the link
1099  * or set link->data to NULL depending on remove_link
1100  */
1101 static gint
1102 gtk_timeout_remove_from_list (GList **list, guint tag, gint remove_link)
1103 {
1104   GList *tmp_list;
1105   GtkTimeoutFunction *timeoutf;
1106
1107   tmp_list = *list;
1108   while (tmp_list)
1109     {
1110       timeoutf = tmp_list->data;
1111       
1112       if (timeoutf->tag == tag)
1113         {
1114           if (remove_link)
1115             {
1116               *list = g_list_remove_link (*list, tmp_list);
1117               g_list_free (tmp_list);
1118             }
1119           else
1120             tmp_list->data = NULL;
1121
1122           gtk_timeout_destroy (timeoutf);
1123           
1124           return TRUE;
1125         }
1126       
1127       tmp_list = tmp_list->next;
1128     }
1129   return FALSE;
1130 }
1131
1132 void
1133 gtk_timeout_remove (guint tag)
1134 {
1135   
1136   /* Remove a timeout function.
1137    * (Which, basically, involves searching the
1138    *  list for the tag).
1139    */
1140
1141   if (gtk_timeout_remove_from_list (&timeout_functions, tag, TRUE))
1142     return;
1143   if (gtk_timeout_remove_from_list (&current_timeouts, tag, TRUE))
1144     return;
1145   if (gtk_timeout_remove_from_list (&running_timeouts, tag, FALSE))
1146     return;
1147 }
1148
1149 guint
1150 gtk_quit_add_full (guint                main_level,
1151                    GtkFunction          function,
1152                    GtkCallbackMarshal   marshal,
1153                    gpointer             data,
1154                    GtkDestroyNotify     destroy)
1155 {
1156   static guint quit_id = 1;
1157   GtkQuitFunction *quitf;
1158   
1159   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
1160
1161   if (!quit_mem_chunk)
1162     quit_mem_chunk = g_mem_chunk_new ("quit mem chunk", sizeof (GtkQuitFunction),
1163                                       512, G_ALLOC_AND_FREE);
1164   
1165   quitf = g_chunk_new (GtkQuitFunction, quit_mem_chunk);
1166   
1167   quitf->id = quit_id++;
1168   quitf->main_level = main_level;
1169   quitf->function = function;
1170   quitf->marshal = marshal;
1171   quitf->data = data;
1172   quitf->destroy = destroy;
1173
1174   quit_functions = g_list_prepend (quit_functions, quitf);
1175   
1176   return quitf->id;
1177 }
1178
1179 gint
1180 gtk_idle_compare (GHook *new_g_hook,
1181                   GHook *g_sibling)
1182 {
1183   GtkIdleHook *new_hook = GTK_IDLE_HOOK (new_g_hook);
1184   GtkIdleHook *sibling = GTK_IDLE_HOOK (g_sibling);
1185   
1186   /* We add an extra +1 to the comparision result to make sure
1187    * that we get inserted at the end of the list of hooks with
1188    * the same priority.
1189    */
1190   return new_hook->priority - sibling->priority + 1;
1191 }
1192
1193 guint
1194 gtk_idle_add_full (gint                 priority,
1195                    GtkFunction          function,
1196                    GtkCallbackMarshal   marshal,
1197                    gpointer             data,
1198                    GtkDestroyNotify     destroy)
1199 {
1200   GHook *hook;
1201   GtkIdleHook *ihook;
1202
1203   if (function)
1204     g_return_val_if_fail (marshal == NULL, 0);
1205   else
1206     g_return_val_if_fail (marshal != NULL, 0);
1207
1208   if (!idle_hooks.seq_id)
1209     g_hook_list_init (&idle_hooks, sizeof (GtkIdleHook));
1210
1211   hook = g_hook_alloc (&idle_hooks);
1212   ihook = GTK_IDLE_HOOK (hook);
1213   hook->data = data;
1214   if (marshal)
1215     {
1216       hook->flags |= GTK_HOOK_MARSHAL;
1217       hook->func = marshal;
1218     }
1219   else
1220     hook->func = function;
1221   hook->destroy = destroy;
1222   ihook->priority = priority;
1223
1224   /* If we are adding the first idle function, possibly wake up
1225    * the main thread out of its select().
1226    */
1227   if (!g_hook_first_valid (&idle_hooks, TRUE))
1228     gdk_threads_wake ();
1229
1230   g_hook_insert_sorted (&idle_hooks, hook, gtk_idle_compare);
1231   
1232   return hook->hook_id;
1233 }
1234
1235 guint
1236 gtk_idle_add (GtkFunction function,
1237               gpointer    data)
1238 {
1239   return gtk_idle_add_full (GTK_PRIORITY_DEFAULT, function, NULL, data, NULL);
1240 }
1241
1242 guint       
1243 gtk_idle_add_priority   (gint               priority,
1244                          GtkFunction        function,
1245                          gpointer           data)
1246 {
1247   return gtk_idle_add_full (priority, function, NULL, data, NULL);
1248 }
1249
1250 guint
1251 gtk_idle_add_interp  (GtkCallbackMarshal   marshal,
1252                       gpointer             data,
1253                       GtkDestroyNotify     destroy)
1254 {
1255   return gtk_idle_add_full (GTK_PRIORITY_DEFAULT, NULL, marshal, data, destroy);
1256 }
1257
1258 void
1259 gtk_idle_remove (guint tag)
1260 {
1261   g_return_if_fail (tag > 0);
1262
1263   if (!g_hook_destroy (&idle_hooks, tag))
1264     g_warning ("gtk_idle_remove(%d): no such idle function", tag);
1265 }
1266
1267 void
1268 gtk_idle_remove_by_data (gpointer data)
1269 {
1270   GHook *hook;
1271
1272   hook = g_hook_find_data (&idle_hooks, TRUE, data);
1273   if (hook)
1274     g_hook_destroy_link (&idle_hooks, hook);
1275   else
1276     g_warning ("gtk_idle_remove_by_data(%p): no such idle function", data);
1277 }
1278
1279 static gboolean
1280 gtk_finish_idles (void)
1281 {
1282   gboolean idles_called;
1283
1284   idles_called = FALSE;
1285   while (last_idle)
1286     {
1287       GHook *hook;
1288
1289       hook = g_hook_next_valid (last_idle, FALSE);
1290
1291       if (!hook || GTK_IDLE_HOOK (hook)->priority != GTK_IDLE_HOOK (last_idle)->priority)
1292         {
1293           g_hook_unref (&idle_hooks, last_idle);
1294           last_idle = NULL;
1295         }
1296       else
1297         {
1298           g_hook_unref (&idle_hooks, last_idle);
1299           last_idle = hook;
1300           g_hook_ref (&idle_hooks, last_idle);
1301
1302           idles_called = TRUE;
1303           gtk_invoke_hook (&idle_hooks, last_idle);
1304         }
1305     }
1306
1307   return idles_called;
1308 }
1309
1310 static void
1311 gtk_handle_idles (void)
1312 {
1313   GHook *hook;
1314
1315   /* Caller must already have called gtk_finish_idles() if necessary
1316    */
1317   g_assert (last_idle == NULL);
1318
1319   hook = g_hook_first_valid (&idle_hooks, FALSE);
1320
1321   if (hook)
1322     {
1323       last_idle = hook;
1324       g_hook_ref (&idle_hooks, last_idle);
1325
1326       gtk_invoke_hook (&idle_hooks, last_idle);
1327
1328       gtk_finish_idles ();
1329     }
1330 }
1331
1332 static void
1333 gtk_invoke_hook (GHookList *hook_list,
1334                  GHook     *hook)
1335 {
1336   gboolean keep_alive;
1337
1338   if (hook->flags & GTK_HOOK_MARSHAL)
1339     {
1340       GtkArg args[1];
1341       register GtkCallbackMarshal marshal;
1342
1343       keep_alive = FALSE;
1344       args[0].name = NULL;
1345       args[0].type = GTK_TYPE_BOOL;
1346       GTK_VALUE_POINTER (args[0]) = &keep_alive;
1347       marshal = hook->func;
1348       marshal (NULL, hook->data, 0, args);
1349     }
1350   else
1351     {
1352       register GtkFunction func;
1353
1354       func = hook->func;
1355       keep_alive = func (hook->data);
1356     }
1357   if (!keep_alive)
1358     g_hook_destroy_link (hook_list, hook);
1359 }
1360
1361 static gint
1362 gtk_invoke_timeout_function (GtkTimeoutFunction *timeoutf)
1363 {
1364   if (!timeoutf->marshal)
1365     return timeoutf->function (timeoutf->data);
1366   else
1367     {
1368       GtkArg args[1];
1369       gint ret_val = FALSE;
1370       args[0].name = NULL;
1371       args[0].type = GTK_TYPE_BOOL;
1372       args[0].d.pointer_data = &ret_val;
1373       timeoutf->marshal (NULL, timeoutf->data,  0, args);
1374       return ret_val;
1375     }
1376 }
1377
1378 static void
1379 gtk_handle_current_timeouts (guint32 the_time)
1380 {
1381   gint result;
1382   GList *tmp_list;
1383   GtkTimeoutFunction *timeoutf;
1384   
1385   while (current_timeouts)
1386     {
1387       tmp_list = current_timeouts;
1388       timeoutf = tmp_list->data;
1389       
1390       current_timeouts = g_list_remove_link (current_timeouts, tmp_list);
1391       if (running_timeouts)
1392         {
1393           running_timeouts->prev = tmp_list;
1394           tmp_list->next = running_timeouts;
1395         }
1396       running_timeouts = tmp_list;
1397       
1398       result = gtk_invoke_timeout_function (timeoutf);
1399
1400       running_timeouts = g_list_remove_link (running_timeouts, tmp_list);
1401       timeoutf = tmp_list->data;
1402       
1403       g_list_free_1 (tmp_list);
1404
1405       if (timeoutf)
1406         {
1407           if (!result)
1408             {
1409               gtk_timeout_destroy (timeoutf);
1410             }
1411           else
1412             {
1413               timeoutf->interval = timeoutf->originterval;
1414               timeoutf->start = the_time;
1415               gtk_timeout_insert (timeoutf);
1416             }
1417         }
1418     }
1419 }
1420
1421 static void
1422 gtk_handle_timeouts (void)
1423 {
1424   guint32 the_time;
1425   GList *tmp_list;
1426   GList *tmp_list2;
1427   GtkTimeoutFunction *timeoutf;
1428   
1429   /* Caller must already have called gtk_handle_current_timeouts if
1430    * necessary */
1431   g_assert (current_timeouts == NULL);
1432   
1433   if (timeout_functions)
1434     {
1435       the_time = gdk_time_get ();
1436       
1437       tmp_list = timeout_functions;
1438       while (tmp_list)
1439         {
1440           timeoutf = tmp_list->data;
1441           
1442           if (timeoutf->interval <= (the_time - timeoutf->start))
1443             {
1444               tmp_list2 = tmp_list;
1445               tmp_list = tmp_list->next;
1446               
1447               timeout_functions = g_list_remove_link (timeout_functions, tmp_list2);
1448               current_timeouts = g_list_concat (current_timeouts, tmp_list2);
1449             }
1450           else
1451             {
1452               timeoutf->interval -= (the_time - timeoutf->start);
1453               timeoutf->start = the_time;
1454               tmp_list = tmp_list->next;
1455             }
1456         }
1457       
1458       if (current_timeouts)
1459         gtk_handle_current_timeouts (the_time);
1460     }
1461 }
1462
1463 static void
1464 gtk_quit_destroy (GtkQuitFunction *quitf)
1465 {
1466   if (quitf->destroy)
1467     quitf->destroy (quitf->data);
1468   g_mem_chunk_free (quit_mem_chunk, quitf);
1469 }
1470
1471 static gint
1472 gtk_quit_destructor (GtkObject **object_p)
1473 {
1474   if (*object_p)
1475     gtk_object_destroy (*object_p);
1476   g_free (object_p);
1477
1478   return FALSE;
1479 }
1480
1481 void
1482 gtk_quit_add_destroy (guint              main_level,
1483                       GtkObject         *object)
1484 {
1485   GtkObject **object_p;
1486
1487   g_return_if_fail (main_level > 0);
1488   g_return_if_fail (object != NULL);
1489   g_return_if_fail (GTK_IS_OBJECT (object));
1490
1491   object_p = g_new (GtkObject*, 1);
1492   *object_p = object;
1493   gtk_signal_connect (object,
1494                       "destroy",
1495                       GTK_SIGNAL_FUNC (gtk_widget_destroyed),
1496                       object_p);
1497   gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p);
1498 }
1499
1500 guint
1501 gtk_quit_add (guint       main_level,
1502               GtkFunction function,
1503               gpointer    data)
1504 {
1505   return gtk_quit_add_full (main_level, function, NULL, data, NULL);
1506 }
1507
1508 void
1509 gtk_quit_remove (guint id)
1510 {
1511   GtkQuitFunction *quitf;
1512   GList *tmp_list;
1513   
1514   tmp_list = quit_functions;
1515   while (tmp_list)
1516     {
1517       quitf = tmp_list->data;
1518       
1519       if (quitf->id == id)
1520         {
1521           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1522           g_list_free (tmp_list);
1523           gtk_quit_destroy (quitf);
1524           
1525           return;
1526         }
1527       
1528       tmp_list = tmp_list->next;
1529     }
1530 }
1531
1532 void
1533 gtk_quit_remove_by_data (gpointer data)
1534 {
1535   GtkQuitFunction *quitf;
1536   GList *tmp_list;
1537   
1538   tmp_list = quit_functions;
1539   while (tmp_list)
1540     {
1541       quitf = tmp_list->data;
1542       
1543       if (quitf->data == data)
1544         {
1545           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1546           g_list_free (tmp_list);
1547           gtk_quit_destroy (quitf);
1548
1549           return;
1550         }
1551       
1552       tmp_list = tmp_list->next;
1553     }
1554 }
1555
1556 static void
1557 gtk_invoke_input_function (GtkInputFunction *input,
1558                            gint source,
1559                            GdkInputCondition condition)
1560 {
1561   GtkArg args[3];
1562   args[0].type = GTK_TYPE_INT;
1563   args[0].name = NULL;
1564   GTK_VALUE_INT(args[0]) = source;
1565   args[1].type = GTK_TYPE_GDK_INPUT_CONDITION;
1566   args[1].name = NULL;
1567   GTK_VALUE_FLAGS(args[1]) = condition;
1568   args[2].type = GTK_TYPE_NONE;
1569   args[2].name = NULL;
1570
1571   input->callback (NULL, input->data, 2, args);
1572 }
1573
1574 static void
1575 gtk_destroy_input_function (GtkInputFunction *input)
1576 {
1577   if (input->destroy)
1578     (input->destroy) (input->data);
1579   g_free (input);
1580 }
1581
1582 guint
1583 gtk_input_add_full (gint source,
1584                     GdkInputCondition condition,
1585                     GdkInputFunction function,
1586                     GtkCallbackMarshal marshal,
1587                     gpointer data,
1588                     GtkDestroyNotify destroy)
1589 {
1590   if (marshal)
1591     {
1592       GtkInputFunction *input;
1593
1594       input = g_new (GtkInputFunction, 1);
1595       input->callback = marshal;
1596       input->data = data;
1597       input->destroy = destroy;
1598
1599       return gdk_input_add_full (source,
1600                                  condition,
1601                                  (GdkInputFunction) gtk_invoke_input_function,
1602                                  input,
1603                                  (GdkDestroyNotify) gtk_destroy_input_function);
1604     }
1605   else
1606     return gdk_input_add_full (source, condition, function, data, destroy);
1607 }
1608
1609 void
1610 gtk_input_remove (guint tag)
1611 {
1612   gdk_input_remove (tag);
1613 }
1614
1615 GdkEvent *
1616 gtk_get_current_event (void)
1617 {
1618   if (current_events)
1619     return gdk_event_copy ((GdkEvent *) current_events->data);
1620   else
1621     return NULL;
1622 }
1623
1624 GtkWidget*
1625 gtk_get_event_widget (GdkEvent *event)
1626 {
1627   GtkWidget *widget;
1628
1629   widget = NULL;
1630   if (event && event->any.window)
1631     gdk_window_get_user_data (event->any.window, (void**) &widget);
1632   
1633   return widget;
1634 }
1635
1636 static void
1637 gtk_exit_func (void)
1638 {
1639   if (gtk_initialized)
1640     {
1641       gtk_initialized = FALSE;
1642       gtk_preview_uninit ();
1643     }
1644 }
1645
1646
1647 /* We rely on some knowledge of how g_list_insert_sorted works to make
1648  * sure that we insert after timeouts of equal interval
1649  */
1650 static gint
1651 gtk_timeout_compare (gconstpointer a, gconstpointer b)
1652 {
1653   return (((const GtkTimeoutFunction *)a)->interval < 
1654           ((const GtkTimeoutFunction *)b)->interval)
1655     ? -1 : 1;
1656 }
1657
1658 static void
1659 gtk_timeout_insert (GtkTimeoutFunction *timeoutf)
1660 {
1661   g_assert (timeoutf != NULL);
1662   
1663   /* Insert the timeout function appropriately.
1664    * Appropriately meaning sort it into the list
1665    *  of timeout functions.
1666    */
1667   timeout_functions = g_list_insert_sorted (timeout_functions, timeoutf,
1668                                             gtk_timeout_compare);
1669 }
1670
1671 static gint
1672 gtk_quit_invoke_function (GtkQuitFunction *quitf)
1673 {
1674   if (!quitf->marshal)
1675     return quitf->function (quitf->data);
1676   else
1677     {
1678       GtkArg args[1];
1679       gint ret_val = FALSE;
1680
1681       args[0].name = NULL;
1682       args[0].type = GTK_TYPE_BOOL;
1683       args[0].d.pointer_data = &ret_val;
1684       ((GtkCallbackMarshal) quitf->marshal) (NULL,
1685                                              quitf->data,
1686                                              0, args);
1687       return ret_val;
1688     }
1689 }
1690
1691 static void
1692 gtk_handle_timer (void)
1693 {
1694   GtkTimeoutFunction *timeoutf;
1695   
1696   if (g_hook_first_valid (&idle_hooks, FALSE))
1697     {
1698       gdk_timer_set (0);
1699       gdk_timer_enable ();
1700     }
1701   else if (timeout_functions)
1702     {
1703       timeoutf = timeout_functions->data;
1704       gdk_timer_set (timeoutf->interval);
1705       gdk_timer_enable ();
1706     }
1707   else
1708     {
1709       gdk_timer_set (0);
1710       gdk_timer_disable ();
1711     }
1712 }
1713
1714 static void
1715 gtk_propagate_event (GtkWidget *widget,
1716                      GdkEvent  *event)
1717 {
1718   gint handled_event;
1719   
1720   g_return_if_fail (widget != NULL);
1721   g_return_if_fail (event != NULL);
1722   
1723   handled_event = FALSE;
1724
1725   if ((event->type == GDK_KEY_PRESS) ||
1726       (event->type == GDK_KEY_RELEASE))
1727     {
1728       /* Only send key events within Window widgets to the Window
1729        *  The Window widget will in turn pass the
1730        *  key event on to the currently focused widget
1731        *  for that window.
1732        */
1733       GtkWidget *window;
1734
1735       window = gtk_widget_get_ancestor (widget, gtk_window_get_type ());
1736       if (window)
1737         {
1738           if (GTK_WIDGET_IS_SENSITIVE (window))
1739             gtk_widget_event (window, event);
1740
1741           handled_event = TRUE; /* don't send to widget */
1742         }
1743     }
1744   
1745   /* Other events get propagated up the widget tree
1746    *  so that parents can see the button and motion
1747    *  events of the children.
1748    */
1749   while (!handled_event && widget)
1750     {
1751       GtkWidget *tmp;
1752
1753       gtk_widget_ref (widget);
1754       handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
1755       tmp = widget->parent;
1756       gtk_widget_unref (widget);
1757       widget  = tmp;
1758     }
1759 }
1760
1761
1762 #if 0
1763 static void
1764 gtk_error (gchar *str)
1765 {
1766   gtk_print (str);
1767 }
1768
1769 static void
1770 gtk_warning (gchar *str)
1771 {
1772   gtk_print (str);
1773 }
1774
1775 static void
1776 gtk_message (gchar *str)
1777 {
1778   gtk_print (str);
1779 }
1780
1781 static void
1782 gtk_print (gchar *str)
1783 {
1784   static GtkWidget *window = NULL;
1785   static GtkWidget *text;
1786   static int level = 0;
1787   GtkWidget *box1;
1788   GtkWidget *box2;
1789   GtkWidget *table;
1790   GtkWidget *hscrollbar;
1791   GtkWidget *vscrollbar;
1792   GtkWidget *separator;
1793   GtkWidget *button;
1794   
1795   if (level > 0)
1796     {
1797       fputs (str, stdout);
1798       fflush (stdout);
1799       return;
1800     }
1801   
1802   if (!window)
1803     {
1804       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1805       
1806       gtk_signal_connect (GTK_OBJECT (window), "destroy",
1807                           (GtkSignalFunc) gtk_widget_destroyed,
1808                           &window);
1809       
1810       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1811       
1812       box1 = gtk_vbox_new (FALSE, 0);
1813       gtk_container_add (GTK_CONTAINER (window), box1);
1814       gtk_widget_show (box1);
1815       
1816       
1817       box2 = gtk_vbox_new (FALSE, 10);
1818       gtk_container_border_width (GTK_CONTAINER (box2), 10);
1819       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1820       gtk_widget_show (box2);
1821       
1822       
1823       table = gtk_table_new (2, 2, FALSE);
1824       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1825       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1826       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1827       gtk_widget_show (table);
1828       
1829       text = gtk_text_new (NULL, NULL);
1830       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1831       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1832       gtk_widget_show (text);
1833       gtk_widget_realize (text);
1834       
1835       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1836       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1837                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1838       gtk_widget_show (hscrollbar);
1839       
1840       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1841       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1842                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1843       gtk_widget_show (vscrollbar);
1844       
1845       separator = gtk_hseparator_new ();
1846       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1847       gtk_widget_show (separator);
1848       
1849       
1850       box2 = gtk_vbox_new (FALSE, 10);
1851       gtk_container_border_width (GTK_CONTAINER (box2), 10);
1852       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1853       gtk_widget_show (box2);
1854       
1855       
1856       button = gtk_button_new_with_label ("close");
1857       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1858                                  (GtkSignalFunc) gtk_widget_hide,
1859                                  GTK_OBJECT (window));
1860       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1861       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1862       gtk_widget_grab_default (button);
1863       gtk_widget_show (button);
1864     }
1865   
1866   level += 1;
1867   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1868   level -= 1;
1869   
1870   if (!GTK_WIDGET_VISIBLE (window))
1871     gtk_widget_show (window);
1872 }
1873 #endif