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