]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
2f3d58b073af7929b9f40f02d78b940aad8e85bc
[~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 const 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   g_message ("gtk_timeout_add_interp() is deprecated");
1095   return gtk_timeout_add_full (interval, NULL, function, data, destroy);
1096 }
1097
1098 /* Search for the specified tag in a list of timeouts. If it
1099  * is found, destroy the timeout, and either remove the link
1100  * or set link->data to NULL depending on remove_link
1101  */
1102 static gint
1103 gtk_timeout_remove_from_list (GList **list, guint tag, gint remove_link)
1104 {
1105   GList *tmp_list;
1106   GtkTimeoutFunction *timeoutf;
1107
1108   tmp_list = *list;
1109   while (tmp_list)
1110     {
1111       timeoutf = tmp_list->data;
1112       
1113       if (timeoutf->tag == tag)
1114         {
1115           if (remove_link)
1116             {
1117               *list = g_list_remove_link (*list, tmp_list);
1118               g_list_free (tmp_list);
1119             }
1120           else
1121             tmp_list->data = NULL;
1122
1123           gtk_timeout_destroy (timeoutf);
1124           
1125           return TRUE;
1126         }
1127       
1128       tmp_list = tmp_list->next;
1129     }
1130   return FALSE;
1131 }
1132
1133 void
1134 gtk_timeout_remove (guint tag)
1135 {
1136   
1137   /* Remove a timeout function.
1138    * (Which, basically, involves searching the
1139    *  list for the tag).
1140    */
1141
1142   if (gtk_timeout_remove_from_list (&timeout_functions, tag, TRUE))
1143     return;
1144   if (gtk_timeout_remove_from_list (&current_timeouts, tag, TRUE))
1145     return;
1146   if (gtk_timeout_remove_from_list (&running_timeouts, tag, FALSE))
1147     return;
1148 }
1149
1150 guint
1151 gtk_quit_add_full (guint                main_level,
1152                    GtkFunction          function,
1153                    GtkCallbackMarshal   marshal,
1154                    gpointer             data,
1155                    GtkDestroyNotify     destroy)
1156 {
1157   static guint quit_id = 1;
1158   GtkQuitFunction *quitf;
1159   
1160   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
1161
1162   if (!quit_mem_chunk)
1163     quit_mem_chunk = g_mem_chunk_new ("quit mem chunk", sizeof (GtkQuitFunction),
1164                                       512, G_ALLOC_AND_FREE);
1165   
1166   quitf = g_chunk_new (GtkQuitFunction, quit_mem_chunk);
1167   
1168   quitf->id = quit_id++;
1169   quitf->main_level = main_level;
1170   quitf->function = function;
1171   quitf->marshal = marshal;
1172   quitf->data = data;
1173   quitf->destroy = destroy;
1174
1175   quit_functions = g_list_prepend (quit_functions, quitf);
1176   
1177   return quitf->id;
1178 }
1179
1180 gint
1181 gtk_idle_compare (GHook *new_g_hook,
1182                   GHook *g_sibling)
1183 {
1184   GtkIdleHook *new_hook = GTK_IDLE_HOOK (new_g_hook);
1185   GtkIdleHook *sibling = GTK_IDLE_HOOK (g_sibling);
1186   
1187   /* We add an extra +1 to the comparision result to make sure
1188    * that we get inserted at the end of the list of hooks with
1189    * the same priority.
1190    */
1191   return new_hook->priority - sibling->priority + 1;
1192 }
1193
1194 guint
1195 gtk_idle_add_full (gint                 priority,
1196                    GtkFunction          function,
1197                    GtkCallbackMarshal   marshal,
1198                    gpointer             data,
1199                    GtkDestroyNotify     destroy)
1200 {
1201   GHook *hook;
1202   GtkIdleHook *ihook;
1203
1204   if (function)
1205     g_return_val_if_fail (marshal == NULL, 0);
1206   else
1207     g_return_val_if_fail (marshal != NULL, 0);
1208
1209   if (!idle_hooks.seq_id)
1210     g_hook_list_init (&idle_hooks, sizeof (GtkIdleHook));
1211
1212   hook = g_hook_alloc (&idle_hooks);
1213   ihook = GTK_IDLE_HOOK (hook);
1214   hook->data = data;
1215   if (marshal)
1216     {
1217       hook->flags |= GTK_HOOK_MARSHAL;
1218       hook->func = marshal;
1219     }
1220   else
1221     hook->func = function;
1222   hook->destroy = destroy;
1223   ihook->priority = priority;
1224
1225   /* If we are adding the first idle function, possibly wake up
1226    * the main thread out of its select().
1227    */
1228   if (!g_hook_first_valid (&idle_hooks, TRUE))
1229     gdk_threads_wake ();
1230
1231   g_hook_insert_sorted (&idle_hooks, hook, gtk_idle_compare);
1232   
1233   return hook->hook_id;
1234 }
1235
1236 guint
1237 gtk_idle_add (GtkFunction function,
1238               gpointer    data)
1239 {
1240   return gtk_idle_add_full (GTK_PRIORITY_DEFAULT, function, NULL, data, NULL);
1241 }
1242
1243 guint       
1244 gtk_idle_add_priority   (gint               priority,
1245                          GtkFunction        function,
1246                          gpointer           data)
1247 {
1248   return gtk_idle_add_full (priority, function, NULL, data, NULL);
1249 }
1250
1251 guint
1252 gtk_idle_add_interp  (GtkCallbackMarshal   marshal,
1253                       gpointer             data,
1254                       GtkDestroyNotify     destroy)
1255 {
1256   g_message ("gtk_idle_add_interp() is deprecated");
1257   return gtk_idle_add_full (GTK_PRIORITY_DEFAULT, NULL, marshal, data, destroy);
1258 }
1259
1260 void
1261 gtk_idle_remove (guint tag)
1262 {
1263   g_return_if_fail (tag > 0);
1264
1265   if (!g_hook_destroy (&idle_hooks, tag))
1266     g_warning ("gtk_idle_remove(%d): no such idle function", tag);
1267 }
1268
1269 void
1270 gtk_idle_remove_by_data (gpointer data)
1271 {
1272   GHook *hook;
1273
1274   hook = g_hook_find_data (&idle_hooks, TRUE, data);
1275   if (hook)
1276     g_hook_destroy_link (&idle_hooks, hook);
1277   else
1278     g_warning ("gtk_idle_remove_by_data(%p): no such idle function", data);
1279 }
1280
1281 static gboolean
1282 gtk_finish_idles (void)
1283 {
1284   gboolean idles_called;
1285
1286   idles_called = FALSE;
1287   while (last_idle)
1288     {
1289       GHook *hook;
1290
1291       hook = g_hook_next_valid (last_idle, FALSE);
1292
1293       if (!hook || GTK_IDLE_HOOK (hook)->priority != GTK_IDLE_HOOK (last_idle)->priority)
1294         {
1295           g_hook_unref (&idle_hooks, last_idle);
1296           last_idle = NULL;
1297         }
1298       else
1299         {
1300           g_hook_unref (&idle_hooks, last_idle);
1301           last_idle = hook;
1302           g_hook_ref (&idle_hooks, last_idle);
1303
1304           idles_called = TRUE;
1305           gtk_invoke_hook (&idle_hooks, last_idle);
1306         }
1307     }
1308
1309   return idles_called;
1310 }
1311
1312 static void
1313 gtk_handle_idles (void)
1314 {
1315   GHook *hook;
1316
1317   /* Caller must already have called gtk_finish_idles() if necessary
1318    */
1319   g_assert (last_idle == NULL);
1320
1321   hook = g_hook_first_valid (&idle_hooks, FALSE);
1322
1323   if (hook)
1324     {
1325       last_idle = hook;
1326       g_hook_ref (&idle_hooks, last_idle);
1327
1328       gtk_invoke_hook (&idle_hooks, last_idle);
1329
1330       gtk_finish_idles ();
1331     }
1332 }
1333
1334 static void
1335 gtk_invoke_hook (GHookList *hook_list,
1336                  GHook     *hook)
1337 {
1338   gboolean keep_alive;
1339
1340   if (hook->flags & GTK_HOOK_MARSHAL)
1341     {
1342       GtkArg args[1];
1343       register GtkCallbackMarshal marshal;
1344
1345       keep_alive = FALSE;
1346       args[0].name = NULL;
1347       args[0].type = GTK_TYPE_BOOL;
1348       GTK_VALUE_POINTER (args[0]) = &keep_alive;
1349       marshal = hook->func;
1350       marshal (NULL, hook->data, 0, args);
1351     }
1352   else
1353     {
1354       register GtkFunction func;
1355
1356       func = hook->func;
1357       keep_alive = func (hook->data);
1358     }
1359   if (!keep_alive)
1360     g_hook_destroy_link (hook_list, hook);
1361 }
1362
1363 static gint
1364 gtk_invoke_timeout_function (GtkTimeoutFunction *timeoutf)
1365 {
1366   if (!timeoutf->marshal)
1367     return timeoutf->function (timeoutf->data);
1368   else
1369     {
1370       GtkArg args[1];
1371       gint ret_val = FALSE;
1372       args[0].name = NULL;
1373       args[0].type = GTK_TYPE_BOOL;
1374       args[0].d.pointer_data = &ret_val;
1375       timeoutf->marshal (NULL, timeoutf->data,  0, args);
1376       return ret_val;
1377     }
1378 }
1379
1380 static void
1381 gtk_handle_current_timeouts (guint32 the_time)
1382 {
1383   gint result;
1384   GList *tmp_list;
1385   GtkTimeoutFunction *timeoutf;
1386   
1387   while (current_timeouts)
1388     {
1389       tmp_list = current_timeouts;
1390       timeoutf = tmp_list->data;
1391       
1392       current_timeouts = g_list_remove_link (current_timeouts, tmp_list);
1393       if (running_timeouts)
1394         {
1395           running_timeouts->prev = tmp_list;
1396           tmp_list->next = running_timeouts;
1397         }
1398       running_timeouts = tmp_list;
1399       
1400       result = gtk_invoke_timeout_function (timeoutf);
1401
1402       running_timeouts = g_list_remove_link (running_timeouts, tmp_list);
1403       timeoutf = tmp_list->data;
1404       
1405       g_list_free_1 (tmp_list);
1406
1407       if (timeoutf)
1408         {
1409           if (!result)
1410             {
1411               gtk_timeout_destroy (timeoutf);
1412             }
1413           else
1414             {
1415               timeoutf->interval = timeoutf->originterval;
1416               timeoutf->start = the_time;
1417               gtk_timeout_insert (timeoutf);
1418             }
1419         }
1420     }
1421 }
1422
1423 static void
1424 gtk_handle_timeouts (void)
1425 {
1426   guint32 the_time;
1427   GList *tmp_list;
1428   GList *tmp_list2;
1429   GtkTimeoutFunction *timeoutf;
1430   
1431   /* Caller must already have called gtk_handle_current_timeouts if
1432    * necessary */
1433   g_assert (current_timeouts == NULL);
1434   
1435   if (timeout_functions)
1436     {
1437       the_time = gdk_time_get ();
1438       
1439       tmp_list = timeout_functions;
1440       while (tmp_list)
1441         {
1442           timeoutf = tmp_list->data;
1443           
1444           if (timeoutf->interval <= (the_time - timeoutf->start))
1445             {
1446               tmp_list2 = tmp_list;
1447               tmp_list = tmp_list->next;
1448               
1449               timeout_functions = g_list_remove_link (timeout_functions, tmp_list2);
1450               current_timeouts = g_list_concat (current_timeouts, tmp_list2);
1451             }
1452           else
1453             {
1454               timeoutf->interval -= (the_time - timeoutf->start);
1455               timeoutf->start = the_time;
1456               tmp_list = tmp_list->next;
1457             }
1458         }
1459       
1460       if (current_timeouts)
1461         gtk_handle_current_timeouts (the_time);
1462     }
1463 }
1464
1465 static void
1466 gtk_quit_destroy (GtkQuitFunction *quitf)
1467 {
1468   if (quitf->destroy)
1469     quitf->destroy (quitf->data);
1470   g_mem_chunk_free (quit_mem_chunk, quitf);
1471 }
1472
1473 static gint
1474 gtk_quit_destructor (GtkObject **object_p)
1475 {
1476   if (*object_p)
1477     gtk_object_destroy (*object_p);
1478   g_free (object_p);
1479
1480   return FALSE;
1481 }
1482
1483 void
1484 gtk_quit_add_destroy (guint              main_level,
1485                       GtkObject         *object)
1486 {
1487   GtkObject **object_p;
1488
1489   g_return_if_fail (main_level > 0);
1490   g_return_if_fail (object != NULL);
1491   g_return_if_fail (GTK_IS_OBJECT (object));
1492
1493   object_p = g_new (GtkObject*, 1);
1494   *object_p = object;
1495   gtk_signal_connect (object,
1496                       "destroy",
1497                       GTK_SIGNAL_FUNC (gtk_widget_destroyed),
1498                       object_p);
1499   gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p);
1500 }
1501
1502 guint
1503 gtk_quit_add (guint       main_level,
1504               GtkFunction function,
1505               gpointer    data)
1506 {
1507   return gtk_quit_add_full (main_level, function, NULL, data, NULL);
1508 }
1509
1510 void
1511 gtk_quit_remove (guint id)
1512 {
1513   GtkQuitFunction *quitf;
1514   GList *tmp_list;
1515   
1516   tmp_list = quit_functions;
1517   while (tmp_list)
1518     {
1519       quitf = tmp_list->data;
1520       
1521       if (quitf->id == id)
1522         {
1523           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1524           g_list_free (tmp_list);
1525           gtk_quit_destroy (quitf);
1526           
1527           return;
1528         }
1529       
1530       tmp_list = tmp_list->next;
1531     }
1532 }
1533
1534 void
1535 gtk_quit_remove_by_data (gpointer data)
1536 {
1537   GtkQuitFunction *quitf;
1538   GList *tmp_list;
1539   
1540   tmp_list = quit_functions;
1541   while (tmp_list)
1542     {
1543       quitf = tmp_list->data;
1544       
1545       if (quitf->data == data)
1546         {
1547           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1548           g_list_free (tmp_list);
1549           gtk_quit_destroy (quitf);
1550
1551           return;
1552         }
1553       
1554       tmp_list = tmp_list->next;
1555     }
1556 }
1557
1558 static void
1559 gtk_invoke_input_function (GtkInputFunction *input,
1560                            gint source,
1561                            GdkInputCondition condition)
1562 {
1563   GtkArg args[3];
1564   args[0].type = GTK_TYPE_INT;
1565   args[0].name = NULL;
1566   GTK_VALUE_INT(args[0]) = source;
1567   args[1].type = GTK_TYPE_GDK_INPUT_CONDITION;
1568   args[1].name = NULL;
1569   GTK_VALUE_FLAGS(args[1]) = condition;
1570   args[2].type = GTK_TYPE_NONE;
1571   args[2].name = NULL;
1572
1573   input->callback (NULL, input->data, 2, args);
1574 }
1575
1576 static void
1577 gtk_destroy_input_function (GtkInputFunction *input)
1578 {
1579   if (input->destroy)
1580     (input->destroy) (input->data);
1581   g_free (input);
1582 }
1583
1584 guint
1585 gtk_input_add_full (gint source,
1586                     GdkInputCondition condition,
1587                     GdkInputFunction function,
1588                     GtkCallbackMarshal marshal,
1589                     gpointer data,
1590                     GtkDestroyNotify destroy)
1591 {
1592   if (marshal)
1593     {
1594       GtkInputFunction *input;
1595
1596       input = g_new (GtkInputFunction, 1);
1597       input->callback = marshal;
1598       input->data = data;
1599       input->destroy = destroy;
1600
1601       return gdk_input_add_full (source,
1602                                  condition,
1603                                  (GdkInputFunction) gtk_invoke_input_function,
1604                                  input,
1605                                  (GdkDestroyNotify) gtk_destroy_input_function);
1606     }
1607   else
1608     return gdk_input_add_full (source, condition, function, data, destroy);
1609 }
1610
1611 void
1612 gtk_input_remove (guint tag)
1613 {
1614   gdk_input_remove (tag);
1615 }
1616
1617 GdkEvent *
1618 gtk_get_current_event (void)
1619 {
1620   if (current_events)
1621     return gdk_event_copy ((GdkEvent *) current_events->data);
1622   else
1623     return NULL;
1624 }
1625
1626 GtkWidget*
1627 gtk_get_event_widget (GdkEvent *event)
1628 {
1629   GtkWidget *widget;
1630
1631   widget = NULL;
1632   if (event && event->any.window)
1633     gdk_window_get_user_data (event->any.window, (void**) &widget);
1634   
1635   return widget;
1636 }
1637
1638 static void
1639 gtk_exit_func (void)
1640 {
1641   if (gtk_initialized)
1642     {
1643       gtk_initialized = FALSE;
1644       gtk_preview_uninit ();
1645     }
1646 }
1647
1648
1649 /* We rely on some knowledge of how g_list_insert_sorted works to make
1650  * sure that we insert after timeouts of equal interval
1651  */
1652 static gint
1653 gtk_timeout_compare (gconstpointer a, gconstpointer b)
1654 {
1655   return (((const GtkTimeoutFunction *)a)->interval < 
1656           ((const GtkTimeoutFunction *)b)->interval)
1657     ? -1 : 1;
1658 }
1659
1660 static void
1661 gtk_timeout_insert (GtkTimeoutFunction *timeoutf)
1662 {
1663   g_assert (timeoutf != NULL);
1664   
1665   /* Insert the timeout function appropriately.
1666    * Appropriately meaning sort it into the list
1667    *  of timeout functions.
1668    */
1669   timeout_functions = g_list_insert_sorted (timeout_functions, timeoutf,
1670                                             gtk_timeout_compare);
1671 }
1672
1673 static gint
1674 gtk_quit_invoke_function (GtkQuitFunction *quitf)
1675 {
1676   if (!quitf->marshal)
1677     return quitf->function (quitf->data);
1678   else
1679     {
1680       GtkArg args[1];
1681       gint ret_val = FALSE;
1682
1683       args[0].name = NULL;
1684       args[0].type = GTK_TYPE_BOOL;
1685       args[0].d.pointer_data = &ret_val;
1686       ((GtkCallbackMarshal) quitf->marshal) (NULL,
1687                                              quitf->data,
1688                                              0, args);
1689       return ret_val;
1690     }
1691 }
1692
1693 static void
1694 gtk_handle_timer (void)
1695 {
1696   GtkTimeoutFunction *timeoutf;
1697   
1698   if (g_hook_first_valid (&idle_hooks, FALSE))
1699     {
1700       gdk_timer_set (0);
1701       gdk_timer_enable ();
1702     }
1703   else if (timeout_functions)
1704     {
1705       timeoutf = timeout_functions->data;
1706       gdk_timer_set (timeoutf->interval);
1707       gdk_timer_enable ();
1708     }
1709   else
1710     {
1711       gdk_timer_set (0);
1712       gdk_timer_disable ();
1713     }
1714 }
1715
1716 static void
1717 gtk_propagate_event (GtkWidget *widget,
1718                      GdkEvent  *event)
1719 {
1720   gint handled_event;
1721   
1722   g_return_if_fail (widget != NULL);
1723   g_return_if_fail (event != NULL);
1724   
1725   handled_event = FALSE;
1726
1727   if ((event->type == GDK_KEY_PRESS) ||
1728       (event->type == GDK_KEY_RELEASE))
1729     {
1730       /* Only send key events within Window widgets to the Window
1731        *  The Window widget will in turn pass the
1732        *  key event on to the currently focused widget
1733        *  for that window.
1734        */
1735       GtkWidget *window;
1736
1737       window = gtk_widget_get_ancestor (widget, gtk_window_get_type ());
1738       if (window)
1739         {
1740           if (GTK_WIDGET_IS_SENSITIVE (window))
1741             gtk_widget_event (window, event);
1742
1743           handled_event = TRUE; /* don't send to widget */
1744         }
1745     }
1746   
1747   /* Other events get propagated up the widget tree
1748    *  so that parents can see the button and motion
1749    *  events of the children.
1750    */
1751   while (!handled_event && widget)
1752     {
1753       GtkWidget *tmp;
1754
1755       gtk_widget_ref (widget);
1756       handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
1757       tmp = widget->parent;
1758       gtk_widget_unref (widget);
1759       widget  = tmp;
1760     }
1761 }
1762
1763
1764 #if 0
1765 static void
1766 gtk_error (gchar *str)
1767 {
1768   gtk_print (str);
1769 }
1770
1771 static void
1772 gtk_warning (gchar *str)
1773 {
1774   gtk_print (str);
1775 }
1776
1777 static void
1778 gtk_message (gchar *str)
1779 {
1780   gtk_print (str);
1781 }
1782
1783 static void
1784 gtk_print (gchar *str)
1785 {
1786   static GtkWidget *window = NULL;
1787   static GtkWidget *text;
1788   static int level = 0;
1789   GtkWidget *box1;
1790   GtkWidget *box2;
1791   GtkWidget *table;
1792   GtkWidget *hscrollbar;
1793   GtkWidget *vscrollbar;
1794   GtkWidget *separator;
1795   GtkWidget *button;
1796   
1797   if (level > 0)
1798     {
1799       fputs (str, stdout);
1800       fflush (stdout);
1801       return;
1802     }
1803   
1804   if (!window)
1805     {
1806       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1807       
1808       gtk_signal_connect (GTK_OBJECT (window), "destroy",
1809                           (GtkSignalFunc) gtk_widget_destroyed,
1810                           &window);
1811       
1812       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1813       
1814       box1 = gtk_vbox_new (FALSE, 0);
1815       gtk_container_add (GTK_CONTAINER (window), box1);
1816       gtk_widget_show (box1);
1817       
1818       
1819       box2 = gtk_vbox_new (FALSE, 10);
1820       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1821       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1822       gtk_widget_show (box2);
1823       
1824       
1825       table = gtk_table_new (2, 2, FALSE);
1826       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1827       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1828       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1829       gtk_widget_show (table);
1830       
1831       text = gtk_text_new (NULL, NULL);
1832       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1833       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1834       gtk_widget_show (text);
1835       gtk_widget_realize (text);
1836       
1837       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1838       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1839                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1840       gtk_widget_show (hscrollbar);
1841       
1842       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1843       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1844                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1845       gtk_widget_show (vscrollbar);
1846       
1847       separator = gtk_hseparator_new ();
1848       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1849       gtk_widget_show (separator);
1850       
1851       
1852       box2 = gtk_vbox_new (FALSE, 10);
1853       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1854       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1855       gtk_widget_show (box2);
1856       
1857       
1858       button = gtk_button_new_with_label ("close");
1859       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1860                                  (GtkSignalFunc) gtk_widget_hide,
1861                                  GTK_OBJECT (window));
1862       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1863       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1864       gtk_widget_grab_default (button);
1865       gtk_widget_show (button);
1866     }
1867   
1868   level += 1;
1869   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1870   level -= 1;
1871   
1872   if (!GTK_WIDGET_VISIBLE (window))
1873     gtk_widget_show (window);
1874 }
1875 #endif