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