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