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