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