]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
new gtk_main_level(). fixes to gtk_window_show() to prevent -1x-1 size on
[~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
35
36 /* Private type definitions
37  */
38 typedef struct _GtkInitFunction     GtkInitFunction;
39 typedef struct _GtkTimeoutFunction  GtkTimeoutFunction;
40 typedef struct _GtkIdleFunction     GtkIdleFunction;
41
42 struct _GtkInitFunction
43 {
44   GtkFunction function;
45   gpointer data;
46 };
47
48 struct _GtkTimeoutFunction
49 {
50   gint tag;
51   guint32 start;
52   guint32 interval;
53   guint32 originterval;
54   gint interp;
55   GtkFunction function;
56   gpointer data;
57   GtkDestroyNotify destroy;
58 };
59
60 struct _GtkIdleFunction
61 {
62   gint tag;
63   gint interp;
64   GtkFunction function;
65   gpointer data;
66   GtkDestroyNotify destroy;
67 };
68
69
70 static void  gtk_exit_func               (void);
71 static void  gtk_timeout_insert          (GtkTimeoutFunction *timeoutf);
72 static void  gtk_handle_current_timeouts (guint32             the_time);
73 static void  gtk_handle_current_idles    (void);
74 static void  gtk_handle_timeouts         (void);
75 static void  gtk_handle_idle             (void);
76 static void  gtk_handle_timer            (void);
77 static void  gtk_propagate_event         (GtkWidget          *widget,
78                                           GdkEvent           *event);
79 static void  gtk_error                   (gchar              *str);
80 static void  gtk_warning                 (gchar              *str);
81 static void  gtk_message                 (gchar              *str);
82 static void  gtk_print                   (gchar              *str);
83
84
85 static gint done;
86 static guint main_level = 0;
87 static gint initialized = FALSE;
88 static GdkEvent next_event;
89 static GdkEvent current_event;
90 static gint have_event = FALSE;
91 static gint have_next_event = FALSE;
92
93 static GList *grabs = NULL;                /* A list of grabs. The grabbing widget
94                                             *  is the first one on the list.
95                                             */
96 static GList *init_functions = NULL;       /* A list of init functions.
97                                             */
98 static GList *timeout_functions = NULL;    /* A list of timeout functions sorted by
99                                             *  when the length of the time interval
100                                             *  remaining. Therefore, the first timeout
101                                             *  function to expire is at the head of
102                                             *  the list and the last to expire is at
103                                             *  the tail of the list.
104                                             */
105 static GList *idle_functions = NULL;       /* A list of idle functions.
106                                             */
107
108 static GList *current_idles = NULL;
109 static GList *current_timeouts = NULL;
110 static GMemChunk *timeout_mem_chunk = NULL;
111 static GMemChunk *idle_mem_chunk = NULL;
112
113 static GdkVisual *gtk_visual;              /* The visual to be used in creating new
114                                             *  widgets.
115                                             */
116 static GdkColormap *gtk_colormap;          /* The colormap to be used in creating new
117                                             *  widgets.
118                                             */
119
120
121 void
122 gtk_init (int    *argc,
123           char ***argv)
124 {
125   if (0)
126     {
127       g_set_error_handler (gtk_error);
128       g_set_warning_handler (gtk_warning);
129       g_set_message_handler (gtk_message);
130       g_set_print_handler (gtk_print);
131     }
132   
133   /* Initialize "gdk". We simply pass along the 'argc' and 'argv'
134    *  parameters as they contain information that
135    */
136   gdk_init (argc, argv);
137   
138   /* Initialize the default visual and colormap to be
139    *  used in creating widgets. (We want to use the system
140    *  defaults so as to be nice to the colormap).
141    */
142   gtk_visual = gdk_visual_get_system ();
143   gtk_colormap = gdk_colormap_get_system ();
144   gtk_rc_init ();
145   
146   gtk_type_init ();
147   
148   /* Register an exit function to make sure we are able to cleanup.
149    */
150   if (ATEXIT (gtk_exit_func))
151     g_warning ("unable to register exit function");
152   
153   /* Set the 'initialized' flag.
154    */
155   initialized = TRUE;
156 }
157
158 void
159 gtk_exit (int errorcode)
160 {
161   /* Only if "gtk" has been initialized should we de-initialize.
162    */
163   /* de-initialisation is done by the gtk_exit_funct(),
164    * no need to do this here (Alex J.)
165    */
166   gdk_exit(errorcode);
167 }
168
169 gchar*
170 gtk_set_locale ()
171 {
172   return gdk_set_locale ();
173 }
174
175 void
176 gtk_main ()
177 {
178   GList *tmp_list;
179   GList *functions;
180   GtkInitFunction *init;
181   int old_done;
182   
183   main_level++;
184   
185   tmp_list = functions = init_functions;
186   init_functions = NULL;
187   
188   while (tmp_list)
189     {
190       init = tmp_list->data;
191       tmp_list = tmp_list->next;
192       
193       (* init->function) (init->data);
194       g_free (init);
195     }
196   
197   g_list_free (functions);
198   
199   old_done = done;
200   while (!gtk_main_iteration ())
201     ;
202   done = old_done;
203   
204   main_level--;
205 }
206
207 guint
208 gtk_main_level (void)
209 {
210   return main_level;
211 }
212
213 void
214 gtk_main_quit ()
215 {
216   done = TRUE;
217 }
218
219 gint
220 gtk_main_iteration ()
221 {
222   GdkEvent event_copy;
223   GtkWidget *event_widget;
224   GtkWidget *grab_widget;
225   
226   done = FALSE;
227   
228   /* If this is a recursive call, and there are pending timeouts or
229    * idles, finish them, then return immediately to avoid blocking
230    * in gdk_event_get()
231    */
232   if (current_timeouts)
233     {
234       gtk_handle_current_timeouts( gdk_time_get());
235       return done;
236     }
237   if (current_idles)
238     {
239       gtk_handle_current_idles();
240       return done;
241     }
242   
243   /* If there is a valid event in 'next_event' then copy
244    *  it to 'event' and unset the flag.
245    */
246   if (have_next_event)
247     {
248       have_next_event = FALSE;
249       have_event = TRUE;
250       current_event = next_event;
251     }
252   
253   /* If we don't have an event then get one.
254    */
255   if (!have_event)
256     {
257       /* Handle setting of the "gdk" timer. If there are no
258        *  timeout functions, then the timer is turned off.
259        *  If there are timeout functions, then the timer is
260        *  set to the shortest timeout interval (which is
261        *  the first timeout function).
262        */
263       gtk_handle_timer ();
264       
265       have_event = gdk_event_get (&current_event, NULL, NULL);
266     }
267   
268   /* "gdk_event_get" can return FALSE if the timer goes off
269    *  and no events are pending. Therefore, we should make
270    *  sure that we got an event before continuing.
271    */
272   if (have_event)
273     {
274       have_event = FALSE;
275       
276       /* If there are any events pending then get the next one.
277        */
278       if (gdk_events_pending () > 0)
279         have_next_event = gdk_event_get (&next_event, NULL, NULL);
280       
281       /* Try to compress enter/leave notify events. These event
282        *  pairs occur when the mouse is dragged quickly across
283        *  a window with many buttons (or through a menu). Instead
284        *  of highlighting and de-highlighting each widget that
285        *  is crossed it is better to simply de-highlight the widget
286        *  which contained the mouse initially and highlight the
287        *  widget which ends up containing the mouse.
288        */
289       if (have_next_event)
290         if (((current_event.type == GDK_ENTER_NOTIFY) ||
291              (current_event.type == GDK_LEAVE_NOTIFY)) &&
292             ((next_event.type == GDK_ENTER_NOTIFY) ||
293              (next_event.type == GDK_LEAVE_NOTIFY)) &&
294             (next_event.type != current_event.type) &&
295             (next_event.any.window == current_event.any.window))
296           return done;
297       
298       /* Find the widget which got the event. We store the widget
299        *  in the user_data field of GdkWindow's.
300        */
301       event_widget = gtk_get_event_widget (&current_event);
302       
303       /* If there is a grab in effect...
304        */
305       if (grabs)
306         {
307           grab_widget = grabs->data;
308           
309           /* If the grab widget is an ancestor of the event widget
310            *  then we send the event to the original event widget.
311            *  This is the key to implementing modality.
312            */
313           if (gtk_widget_is_ancestor (event_widget, grab_widget))
314             grab_widget = event_widget;
315         }
316       else
317         {
318           grab_widget = event_widget;
319         }
320       
321       /* Not all events get sent to the grabbing widget.
322        * The delete, destroy, expose, focus change and resize
323        *  events still get sent to the event widget because
324        *  1) these events have no meaning for the grabbing widget
325        *  and 2) redirecting these events to the grabbing widget
326        *  could cause the display to be messed up.
327        */
328       event_copy = current_event;
329       switch (event_copy.type)
330         {
331         case GDK_NOTHING:
332           break;
333           
334         case GDK_DELETE:
335           if (gtk_widget_event (event_widget, &event_copy))
336             gtk_widget_destroy (event_widget);
337           break;
338           
339         case GDK_DESTROY:
340           gtk_widget_event (event_widget, &event_copy);
341           gtk_widget_destroy (event_widget);
342           break;
343           
344         case GDK_PROPERTY_NOTIFY:
345           /* To handle selection INCR transactions, we select
346            * PropertyNotify events on the requestor window and create
347            * a corresponding (fake) GdkWindow so that events get
348            * here. There won't be a widget though, so we have to handle
349            * them specially
350            */
351           
352           if (event_widget == NULL)
353             {
354               gtk_selection_incr_event (event_copy.any.window,
355                                         &event_copy.property);
356               break;
357             }
358           /* otherwise fall through */
359           
360         case GDK_EXPOSE:
361         case GDK_FOCUS_CHANGE:
362         case GDK_CONFIGURE:
363         case GDK_MAP:
364         case GDK_UNMAP:
365         case GDK_SELECTION_CLEAR:
366         case GDK_SELECTION_REQUEST:
367         case GDK_SELECTION_NOTIFY:
368         case GDK_CLIENT_EVENT:
369           gtk_widget_event (event_widget, &event_copy);
370           break;
371           
372         case GDK_MOTION_NOTIFY:
373         case GDK_BUTTON_PRESS:
374         case GDK_2BUTTON_PRESS:
375         case GDK_3BUTTON_PRESS:
376         case GDK_BUTTON_RELEASE:
377         case GDK_KEY_PRESS:
378         case GDK_KEY_RELEASE:
379         case GDK_PROXIMITY_IN:
380         case GDK_PROXIMITY_OUT:
381         case GDK_OTHER_EVENT:
382         case GDK_DRAG_BEGIN:
383         case GDK_DRAG_REQUEST:
384         case GDK_DROP_ENTER:
385         case GDK_DROP_LEAVE:
386         case GDK_DROP_DATA_AVAIL:
387           gtk_propagate_event (grab_widget, &event_copy);
388           break;
389           
390         case GDK_ENTER_NOTIFY:
391         case GDK_LEAVE_NOTIFY:
392           if (grab_widget && GTK_WIDGET_IS_SENSITIVE (grab_widget))
393             gtk_widget_event (grab_widget, &event_copy);
394           break;
395         }
396     }
397   else
398     {
399       if (gdk_events_pending() == 0)
400         gtk_handle_idle ();
401     }
402   
403   /* Handle a timeout functions that may have expired.
404    */
405   gtk_handle_timeouts ();
406   
407   return done;
408 }
409
410 gint
411 gtk_true (void)
412 {
413   return TRUE;
414 }
415
416 gint
417 gtk_false (void)
418 {
419   return FALSE;
420 }
421
422 void
423 gtk_grab_add (GtkWidget *widget)
424 {
425   /* Place the grab on the front of the list of grabs.
426    */
427   grabs = g_list_prepend (grabs, widget);
428 }
429
430 void
431 gtk_grab_remove (GtkWidget *widget)
432 {
433   /* Remove the grab from the list of grabs.
434    * Note: the grab being removed may be in
435    *  the middle of the list.
436    */
437   grabs = g_list_remove (grabs, widget);
438 }
439
440 void
441 gtk_init_add (GtkFunction function,
442               gpointer    data)
443 {
444   GtkInitFunction *init;
445   
446   init = g_new (GtkInitFunction, 1);
447   init->function = function;
448   init->data = data;
449   
450   init_functions = g_list_prepend (init_functions, init);
451 }
452
453 static gint
454 gtk_timeout_add_internal (guint32     interval,
455                           gint        interp,
456                           GtkFunction function,
457                           gpointer    data,
458                           GtkDestroyNotify destroy)
459 {
460   static gint timeout_tag = 1;
461   GtkTimeoutFunction *timeoutf;
462   
463   /* Create a new timeout function structure.
464    * The start time is the current time.
465    */
466   if (!timeout_mem_chunk)
467     timeout_mem_chunk = g_mem_chunk_new ("timeout mem chunk", sizeof (GtkTimeoutFunction),
468                                          1024, G_ALLOC_AND_FREE);
469   
470   timeoutf = g_chunk_new (GtkTimeoutFunction, timeout_mem_chunk);
471   
472   timeoutf->tag = timeout_tag++;
473   timeoutf->start = gdk_time_get ();
474   timeoutf->interval = interval;
475   timeoutf->originterval = interval;
476   timeoutf->interp = interp;
477   timeoutf->function = function;
478   timeoutf->data = data;
479   timeoutf->destroy = destroy;
480   
481   gtk_timeout_insert (timeoutf);
482   
483   return timeoutf->tag;
484 }
485
486 static void
487 gtk_timeout_destroy (GtkTimeoutFunction *timeoutf)
488 {
489   if (timeoutf->destroy)
490     (timeoutf->destroy) (timeoutf->data);
491   g_mem_chunk_free (timeout_mem_chunk, timeoutf);
492 }
493
494 gint
495 gtk_timeout_add (guint32     interval,
496                  GtkFunction function,
497                  gpointer    data)
498 {
499   return gtk_timeout_add_internal (interval, FALSE, function, data, NULL);
500 }
501
502 gint
503 gtk_timeout_add_interp (guint32            interval,
504                         GtkCallbackMarshal function,
505                         gpointer           data,
506                         GtkDestroyNotify   destroy)
507 {
508   return gtk_timeout_add_internal (interval, TRUE,
509                                    (GtkFunction) function,
510                                    data, destroy);
511 }
512
513 void
514 gtk_timeout_remove (gint tag)
515 {
516   GtkTimeoutFunction *timeoutf;
517   GList *tmp_list;
518   
519   /* Remove a timeout function.
520    * (Which, basically, involves searching the
521    *  list for the tag).
522    */
523   tmp_list = timeout_functions;
524   while (tmp_list)
525     {
526       timeoutf = tmp_list->data;
527       
528       if (timeoutf->tag == tag)
529         {
530           timeout_functions = g_list_remove_link (timeout_functions, tmp_list);
531           g_list_free (tmp_list);
532           gtk_timeout_destroy (timeoutf);
533           
534           return;
535         }
536       
537       tmp_list = tmp_list->next;
538     }
539   
540   tmp_list = current_timeouts;
541   while (tmp_list)
542     {
543       timeoutf = tmp_list->data;
544       
545       if (timeoutf->tag == tag)
546         {
547           current_timeouts = g_list_remove_link (current_timeouts, tmp_list);
548           g_list_free (tmp_list);
549           gtk_timeout_destroy (timeoutf);
550           
551           return;
552         }
553       
554       tmp_list = tmp_list->next;
555     }
556 }
557
558 static gint
559 gtk_idle_add_internal (gint             interp,
560                        GtkFunction      function,
561                        gpointer         data,
562                        GtkDestroyNotify destroy)
563 {
564   static gint idle_tag = 1;
565   GtkIdleFunction *idlef;
566   
567   if (!idle_mem_chunk)
568     idle_mem_chunk = g_mem_chunk_new ("idle mem chunk", sizeof (GtkIdleFunction),
569                                       1024, G_ALLOC_AND_FREE);
570   
571   idlef = g_chunk_new (GtkIdleFunction, idle_mem_chunk);
572   
573   idlef->tag = idle_tag++;
574   idlef->interp = interp;
575   idlef->function = function;
576   idlef->data = data;
577   idlef->destroy = destroy;
578   
579   idle_functions = g_list_append (idle_functions, idlef);
580   
581   return idlef->tag;
582 }
583
584 static void
585 gtk_idle_destroy (GtkIdleFunction *idlef)
586 {
587   if (idlef->destroy)
588     idlef->destroy (idlef->data);
589   g_mem_chunk_free (idle_mem_chunk, idlef);
590 }
591
592 gint
593 gtk_idle_add (GtkFunction function,
594               gpointer    data)
595 {
596   return gtk_idle_add_internal (FALSE, function, data, NULL);
597 }
598
599 gint
600 gtk_idle_add_interp (GtkCallbackMarshal function,
601                      gpointer           data,
602                      GtkDestroyNotify   destroy)
603 {
604   return gtk_idle_add_internal (TRUE, (GtkFunction)function, data, destroy);
605 }
606
607 void
608 gtk_idle_remove (gint tag)
609 {
610   GtkIdleFunction *idlef;
611   GList *tmp_list;
612   
613   tmp_list = idle_functions;
614   while (tmp_list)
615     {
616       idlef = tmp_list->data;
617       
618       if (idlef->tag == tag)
619         {
620           idle_functions = g_list_remove_link (idle_functions, tmp_list);
621           g_list_free (tmp_list);
622           gtk_idle_destroy (idlef);
623           
624           return;
625         }
626       
627       tmp_list = tmp_list->next;
628     }
629   
630   tmp_list = current_idles;
631   while (tmp_list)
632     {
633       idlef = tmp_list->data;
634       
635       if (idlef->tag == tag)
636         {
637           current_idles = g_list_remove_link (current_idles, tmp_list);
638           g_list_free (tmp_list);
639           gtk_idle_destroy (idlef);
640           
641           return;
642         }
643       
644       tmp_list = tmp_list->next;
645     }
646 }
647
648 void
649 gtk_idle_remove_by_data (gpointer data)
650 {
651   GtkIdleFunction *idlef;
652   GList *tmp_list;
653   
654   tmp_list = idle_functions;
655   while (tmp_list)
656     {
657       idlef = tmp_list->data;
658       
659       if (idlef->data == data)
660         {
661           idle_functions = g_list_remove_link (idle_functions, tmp_list);
662           g_list_free (tmp_list);
663           g_mem_chunk_free (idle_mem_chunk, idlef);
664           
665           return;
666         }
667       
668       tmp_list = tmp_list->next;
669     }
670   
671   tmp_list = current_idles;
672   while (tmp_list)
673     {
674       idlef = tmp_list->data;
675       
676       if (idlef->data == data)
677         {
678           current_idles = g_list_remove_link (current_idles, tmp_list);
679           g_list_free (tmp_list);
680           g_mem_chunk_free (idle_mem_chunk, idlef);
681           
682           return;
683         }
684       
685       tmp_list = tmp_list->next;
686     }
687 }
688
689 void
690 gtk_get_current_event (GdkEvent *event)
691 {
692   g_assert (event != NULL);
693   
694   *event = current_event;
695 }
696
697 GtkWidget*
698 gtk_get_event_widget (GdkEvent *event)
699 {
700   GtkWidget *widget;
701   gdk_window_get_user_data (event->any.window, (void**) &widget);
702   
703   return widget;
704 }
705
706 static void
707 gtk_exit_func ()
708 {
709   if (initialized)
710     {
711       initialized = FALSE;
712       gtk_preview_uninit ();
713     }
714 }
715
716 static void
717 gtk_timeout_insert (GtkTimeoutFunction *timeoutf)
718 {
719   GtkTimeoutFunction *temp;
720   GList *temp_list;
721   GList *new_list;
722   
723   g_assert (timeoutf != NULL);
724   
725   /* Insert the timeout function appropriately.
726    * Appropriately meaning sort it into the list
727    *  of timeout functions.
728    */
729   temp_list = timeout_functions;
730   while (temp_list)
731     {
732       temp = temp_list->data;
733       if (timeoutf->interval < temp->interval)
734         {
735           new_list = g_list_alloc ();
736           new_list->data = timeoutf;
737           new_list->next = temp_list;
738           new_list->prev = temp_list->prev;
739           if (temp_list->prev)
740             temp_list->prev->next = new_list;
741           temp_list->prev = new_list;
742           
743           if (temp_list == timeout_functions)
744             timeout_functions = new_list;
745           
746           return;
747         }
748       
749       temp_list = temp_list->next;
750     }
751   
752   timeout_functions = g_list_append (timeout_functions, timeoutf);
753 }
754
755 static gint
756 gtk_invoke_timeout_function (GtkTimeoutFunction *timeoutf)
757 {
758   if (!timeoutf->interp)
759     return timeoutf->function (timeoutf->data);
760   else
761     {
762       GtkArg args[1];
763       gint ret_val = FALSE;
764       args[0].name = NULL;
765       args[0].type = GTK_TYPE_BOOL;
766       args[0].d.pointer_data = &ret_val;
767       ((GtkCallbackMarshal)timeoutf->function) (NULL,
768                                                 timeoutf->data,
769                                                 0, args);
770       return ret_val;
771     }
772 }
773
774 static void
775 gtk_handle_current_timeouts (guint32 the_time)
776 {
777   GList *tmp_list;
778   GtkTimeoutFunction *timeoutf;
779   
780   while (current_timeouts)
781     {
782       tmp_list = current_timeouts;
783       timeoutf = tmp_list->data;
784       
785       current_timeouts = g_list_remove_link (current_timeouts, tmp_list);
786       g_list_free (tmp_list);
787       
788       if (gtk_invoke_timeout_function (timeoutf) == FALSE)
789         {
790           gtk_timeout_destroy (timeoutf);
791         }
792       else
793         {
794           timeoutf->interval = timeoutf->originterval;
795           timeoutf->start = the_time;
796           gtk_timeout_insert (timeoutf);
797         }
798     }
799 }
800
801 static void
802 gtk_handle_timeouts ()
803 {
804   guint32 the_time;
805   GList *tmp_list;
806   GList *tmp_list2;
807   GList *tmp_list3;
808   GtkTimeoutFunction *timeoutf;
809   
810   /* Caller must already have called gtk_handle_current_timeouts if
811    * necessary */
812   g_assert (current_timeouts == NULL);
813   
814   if (timeout_functions)
815     {
816       the_time = gdk_time_get ();
817       
818       tmp_list = timeout_functions;
819       while (tmp_list)
820         {
821           timeoutf = tmp_list->data;
822           
823           if (timeoutf->interval <= (the_time - timeoutf->start))
824             {
825               tmp_list2 = tmp_list;
826               tmp_list = tmp_list->next;
827               
828               timeout_functions = g_list_remove_link (timeout_functions, tmp_list2);
829               current_timeouts = g_list_concat (current_timeouts, tmp_list2);
830             }
831           else
832             {
833               timeoutf->interval -= (the_time - timeoutf->start);
834               timeoutf->start = the_time;
835               tmp_list = tmp_list->next;
836             }
837         }
838       
839       if (current_timeouts)
840         gtk_handle_current_timeouts(the_time);
841     }
842 }
843
844 static gint
845 gtk_idle_invoke_function (GtkIdleFunction *idlef)
846 {
847   if (!idlef->interp)
848     return idlef->function (idlef->data);
849   else
850     {
851       GtkArg args[1];
852       gint ret_val = FALSE;
853       args[0].name = NULL;
854       args[0].type = GTK_TYPE_BOOL;
855       args[0].d.pointer_data = &ret_val;
856       ((GtkCallbackMarshal)idlef->function) (NULL,
857                                              idlef->data,
858                                              0, args);
859       return ret_val;
860     }
861 }
862
863 static void
864 gtk_handle_current_idles ()
865 {
866   GList *tmp_list;
867   GtkIdleFunction *idlef;
868   
869   while (current_idles)
870     {
871       tmp_list = current_idles;
872       idlef = tmp_list->data;
873       
874       current_idles = g_list_remove_link (current_idles, tmp_list);
875       
876       if (gtk_idle_invoke_function (idlef) == FALSE)
877         {
878           g_list_free (tmp_list);
879           gtk_idle_destroy (idlef);
880         }
881       else
882         {
883           idle_functions = g_list_concat (idle_functions, tmp_list);
884         }
885     }
886 }
887
888 static void
889 gtk_handle_idle ()
890 {
891   GtkIdleFunction *idlef;
892   GList *tmp_list;
893   GList *tmp_list2;
894   
895   /* Caller must already have called gtk_handle_current_idles if
896      necessary */
897   g_assert (current_idles == NULL);
898   
899   if (idle_functions)
900     {
901       current_idles = idle_functions;
902       idle_functions = NULL;
903       
904       gtk_handle_current_idles();
905     }
906 }
907
908 static void
909 gtk_handle_timer ()
910 {
911   GtkTimeoutFunction *timeoutf;
912   
913   if (idle_functions)
914     {
915       gdk_timer_set (0);
916       gdk_timer_enable ();
917     }
918   else if (timeout_functions)
919     {
920       timeoutf = timeout_functions->data;
921       gdk_timer_set (timeoutf->interval);
922       gdk_timer_enable ();
923     }
924   else
925     {
926       gdk_timer_set (0);
927       gdk_timer_disable ();
928     }
929 }
930
931 static void
932 gtk_propagate_event (GtkWidget *widget,
933                      GdkEvent  *event)
934 {
935   GtkWidget *parent;
936   gint handled_event;
937   gint parent_old_value;
938   gint old_value;
939   
940   g_return_if_fail (widget != NULL);
941   g_return_if_fail (event != NULL);
942   
943   handled_event = FALSE;
944   
945   if ((event->type == GDK_KEY_PRESS) ||
946       (event->type == GDK_KEY_RELEASE))
947     {
948       /* Only send key events to window widgets.
949        *  The window widget will in turn pass the
950        *  key event on to the currently focused widget
951        *  for that window.
952        */
953       parent = gtk_widget_get_ancestor (widget, gtk_window_get_type ());
954       if (parent && GTK_WIDGET_IS_SENSITIVE (parent))
955         {
956           parent_old_value = GTK_OBJECT_IN_CALL (parent);
957           GTK_OBJECT_SET_FLAGS (parent, GTK_IN_CALL);
958           
959           handled_event = gtk_widget_event (parent, event);
960           
961           if (!parent_old_value)
962             GTK_OBJECT_UNSET_FLAGS (parent, GTK_IN_CALL);
963           
964           if (GTK_OBJECT_NEED_DESTROY (parent) && !GTK_OBJECT_IN_CALL (parent))
965             {
966               gtk_object_destroy (GTK_OBJECT (parent));
967               return;
968             }
969         }
970     }
971   
972   if (!handled_event)
973     {
974       old_value = GTK_OBJECT_IN_CALL (widget);
975       GTK_OBJECT_SET_FLAGS (widget, GTK_IN_CALL);
976       
977       /* Other events get propagated up the widget tree
978        *  so that parents can see the button and motion
979        *  events of the children.
980        */
981       parent = widget;
982       while (parent)
983         {
984           parent_old_value = GTK_OBJECT_IN_CALL (parent);
985           GTK_OBJECT_SET_FLAGS (parent, GTK_IN_CALL);
986           
987           handled_event = (!GTK_WIDGET_IS_SENSITIVE (parent) ||
988                            gtk_widget_event (parent, event));
989           
990           if (!parent_old_value)
991             GTK_OBJECT_UNSET_FLAGS (parent, GTK_IN_CALL);
992           
993           if (handled_event)
994             break;
995           
996           if (GTK_OBJECT_NEED_DESTROY (parent) && !GTK_OBJECT_IN_CALL (parent))
997             {
998               gtk_object_destroy (GTK_OBJECT (parent));
999               break;
1000             }
1001           
1002           parent = parent->parent;
1003         }
1004       
1005       if (!old_value)
1006         GTK_OBJECT_UNSET_FLAGS (widget, GTK_IN_CALL);
1007       
1008       if (GTK_OBJECT_NEED_DESTROY (widget) && !GTK_OBJECT_IN_CALL (widget))
1009         gtk_object_destroy (GTK_OBJECT (widget));
1010     }
1011 }
1012
1013
1014 static void
1015 gtk_error (gchar *str)
1016 {
1017   gtk_print (str);
1018 }
1019
1020 static void
1021 gtk_warning (gchar *str)
1022 {
1023   gtk_print (str);
1024 }
1025
1026 static void
1027 gtk_message (gchar *str)
1028 {
1029   gtk_print (str);
1030 }
1031
1032 static void
1033 gtk_print (gchar *str)
1034 {
1035   static GtkWidget *window = NULL;
1036   static GtkWidget *text;
1037   static int level = 0;
1038   GtkWidget *box1;
1039   GtkWidget *box2;
1040   GtkWidget *table;
1041   GtkWidget *hscrollbar;
1042   GtkWidget *vscrollbar;
1043   GtkWidget *separator;
1044   GtkWidget *button;
1045   
1046   if (level > 0)
1047     {
1048       fputs (str, stdout);
1049       fflush (stdout);
1050       return;
1051     }
1052   
1053   if (!window)
1054     {
1055       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1056       /*
1057        * gtk_signal_connect (GTK_OBJECT (window), "destroy",
1058        *                     (GtkSignalFunc) gtk_widget_destroyed,
1059        *                     &window);
1060        */
1061       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1062       
1063       box1 = gtk_vbox_new (FALSE, 0);
1064       gtk_container_add (GTK_CONTAINER (window), box1);
1065       gtk_widget_show (box1);
1066       
1067       
1068       box2 = gtk_vbox_new (FALSE, 10);
1069       gtk_container_border_width (GTK_CONTAINER (box2), 10);
1070       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1071       gtk_widget_show (box2);
1072       
1073       
1074       table = gtk_table_new (2, 2, FALSE);
1075       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1076       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1077       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1078       gtk_widget_show (table);
1079       
1080       text = gtk_text_new (NULL, NULL);
1081       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1082       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1083       gtk_widget_show (text);
1084       gtk_widget_realize (text);
1085       
1086       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1087       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1088                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1089       gtk_widget_show (hscrollbar);
1090       
1091       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1092       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1093                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1094       gtk_widget_show (vscrollbar);
1095       
1096       separator = gtk_hseparator_new ();
1097       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1098       gtk_widget_show (separator);
1099       
1100       
1101       box2 = gtk_vbox_new (FALSE, 10);
1102       gtk_container_border_width (GTK_CONTAINER (box2), 10);
1103       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1104       gtk_widget_show (box2);
1105       
1106       
1107       button = gtk_button_new_with_label ("close");
1108       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1109                                  (GtkSignalFunc) gtk_widget_hide,
1110                                  GTK_OBJECT (window));
1111       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1112       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1113       gtk_widget_grab_default (button);
1114       gtk_widget_show (button);
1115     }
1116   
1117   level += 1;
1118   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1119   level -= 1;
1120   
1121   if (!GTK_WIDGET_VISIBLE (window))
1122     gtk_widget_show (window);
1123 }