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