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