]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
5eca47c7b8d1d150d8f6c5533c83a242498c43d5
[~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
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #ifdef GDK_WINDOWING_X11
28 #include <X11/Xlocale.h>        /* so we get the right setlocale */
29 #else
30 #include <locale.h>
31 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <gmodule.h>
36 #include "gtkbutton.h"
37 #include "gtkdnd.h"
38 #include "gtkcompat.h"
39 #include "gtkhscrollbar.h"
40 #include "gtkhseparator.h"
41 #include "gtkmain.h"
42 #include "gtkpreview.h"
43 #include "gtkrc.h"
44 #include "gtkscrolledwindow.h"
45 #include "gtkselection.h"
46 #include "gtksignal.h"
47 #include "gtktable.h"
48 #include "gtktext.h"
49 #include "gtkvbox.h"
50 #include "gtkvscrollbar.h"
51 #include "gtkwidget.h"
52 #include "gtkwindow.h"
53 #include "gtkprivate.h"
54 #include "gdk/gdki18n.h"
55 #include "config.h"
56 #include "gtkdebug.h"
57 #include "gtkintl.h"
58
59 /* Private type definitions
60  */
61 typedef struct _GtkInitFunction          GtkInitFunction;
62 typedef struct _GtkQuitFunction          GtkQuitFunction;
63 typedef struct _GtkClosure               GtkClosure;
64 typedef struct _GtkKeySnooperData        GtkKeySnooperData;
65
66 struct _GtkInitFunction
67 {
68   GtkFunction function;
69   gpointer data;
70 };
71
72 struct _GtkQuitFunction
73 {
74   guint id;
75   guint main_level;
76   GtkCallbackMarshal marshal;
77   GtkFunction function;
78   gpointer data;
79   GtkDestroyNotify destroy;
80 };
81
82 struct _GtkClosure
83 {
84   GtkCallbackMarshal marshal;
85   gpointer data;
86   GtkDestroyNotify destroy;
87 };
88
89 struct _GtkKeySnooperData
90 {
91   GtkKeySnoopFunc func;
92   gpointer func_data;
93   guint id;
94 };
95
96 static void  gtk_exit_func               (void);
97 static gint  gtk_quit_invoke_function    (GtkQuitFunction    *quitf);
98 static void  gtk_quit_destroy            (GtkQuitFunction    *quitf);
99 static gint  gtk_invoke_key_snoopers     (GtkWidget          *grab_widget,
100                                           GdkEvent           *event);
101
102 static void     gtk_destroy_closure      (gpointer            data);
103 static gboolean gtk_invoke_idle_timeout  (gpointer            data);
104 static void     gtk_invoke_input         (gpointer            data,
105                                           gint                source,
106                                           GdkInputCondition   condition);
107
108 #if 0
109 static void  gtk_error                   (gchar              *str);
110 static void  gtk_warning                 (gchar              *str);
111 static void  gtk_message                 (gchar              *str);
112 static void  gtk_print                   (gchar              *str);
113 #endif
114
115 const guint gtk_major_version = GTK_MAJOR_VERSION;
116 const guint gtk_minor_version = GTK_MINOR_VERSION;
117 const guint gtk_micro_version = GTK_MICRO_VERSION;
118 const guint gtk_binary_age = GTK_BINARY_AGE;
119 const guint gtk_interface_age = GTK_INTERFACE_AGE;
120
121 static guint gtk_main_loop_level = 0;
122 static gint gtk_initialized = FALSE;
123 static GList *current_events = NULL;
124
125 static GSList *main_loops = NULL;      /* stack of currently executing main loops */
126
127 static GSList *grabs = NULL;               /* A stack of unique grabs. The grabbing
128                                             *  widget is the first one on the list.
129                                             */
130 static GList *init_functions = NULL;       /* A list of init functions.
131                                             */
132 static GList *quit_functions = NULL;       /* A list of quit functions.
133                                             */
134 static GMemChunk *quit_mem_chunk = NULL;
135
136 static GSList *key_snoopers = NULL;
137
138 static GdkVisual *gtk_visual;              /* The visual to be used in creating new
139                                             *  widgets.
140                                             */
141 static GdkColormap *gtk_colormap;          /* The colormap to be used in creating new
142                                             *  widgets.
143                                             */
144
145 guint gtk_debug_flags = 0;                 /* Global GTK debug flag */
146
147 #ifdef G_ENABLE_DEBUG
148 static const GDebugKey gtk_debug_keys[] = {
149   {"objects", GTK_DEBUG_OBJECTS},
150   {"misc", GTK_DEBUG_MISC},
151   {"signals", GTK_DEBUG_SIGNALS},
152   {"dnd", GTK_DEBUG_DND},
153   {"plugsocket", GTK_DEBUG_PLUGSOCKET}
154 };
155
156 static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
157
158 #endif /* G_ENABLE_DEBUG */
159
160 gchar*
161 gtk_check_version (guint required_major,
162                    guint required_minor,
163                    guint required_micro)
164 {
165   if (required_major > GTK_MAJOR_VERSION)
166     return "Gtk+ version too old (major mismatch)";
167   if (required_major < GTK_MAJOR_VERSION)
168     return "Gtk+ version too new (major mismatch)";
169   if (required_minor > GTK_MINOR_VERSION)
170     return "Gtk+ version too old (minor mismatch)";
171   if (required_minor < GTK_MINOR_VERSION)
172     return "Gtk+ version too new (minor mismatch)";
173   if (required_micro < GTK_MICRO_VERSION - GTK_BINARY_AGE)
174     return "Gtk+ version too new (micro mismatch)";
175   if (required_micro > GTK_MICRO_VERSION)
176     return "Gtk+ version too old (micro mismatch)";
177   return NULL;
178 }
179
180 #ifdef __EMX__
181 static gchar *add_dll_suffix(gchar *module_name)
182 {
183     gchar *suffix = strrchr(module_name, '.');
184     
185     if (!suffix || stricmp(suffix, ".dll"))
186     {
187         gchar *old = module_name;
188           
189         module_name = g_strconcat (module_name, ".dll", NULL);
190         g_free (old);
191     }
192     return (module_name);
193 }
194 #endif
195
196 gboolean
197 gtk_init_check (int      *argc,
198                 char   ***argv)
199 {
200   GSList *gtk_modules = NULL;
201   GSList *slist;
202   gchar *env_string = NULL;
203
204   if (gtk_initialized)
205     return TRUE;
206
207 #if     0
208   g_set_error_handler (gtk_error);
209   g_set_warning_handler (gtk_warning);
210   g_set_message_handler (gtk_message);
211   g_set_print_handler (gtk_print);
212 #endif
213   
214   /* Initialize "gdk". We pass along the 'argc' and 'argv'
215    *  parameters as they contain information that GDK uses
216    */
217   if (!gdk_init_check (argc, argv))
218     return FALSE;
219
220   gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
221   
222 #ifdef G_ENABLE_DEBUG
223   env_string = getenv ("GTK_DEBUG");
224   if (env_string != NULL)
225     {
226       gtk_debug_flags = g_parse_debug_string (env_string,
227                                               gtk_debug_keys,
228                                               gtk_ndebug_keys);
229       env_string = NULL;
230     }
231 #endif  /* G_ENABLE_DEBUG */
232
233   env_string = getenv ("GTK_MODULES");
234   if (env_string)
235     {
236       gchar **modules, **as;
237
238 #ifndef __EMX__
239       modules = g_strsplit (env_string, G_SEARCHPATH_SEPARATOR_S, -1);
240 #else
241       modules = g_strsplit (env_string, ";", -1);
242 #endif
243       for (as = modules; *as; as++)
244         {
245           if (**as)
246             gtk_modules = g_slist_prepend (gtk_modules, *as);
247           else
248             g_free (*as);
249         }
250       g_free (modules);
251       env_string = NULL;
252     }
253
254   if (argc && argv)
255     {
256       gint i, j, k;
257       
258       for (i = 1; i < *argc;)
259         {
260           if (strcmp ("--gtk-module", (*argv)[i]) == 0 ||
261               strncmp ("--gtk-module=", (*argv)[i], 13) == 0)
262             {
263               gchar *module_name = (*argv)[i] + 12;
264               
265               if (*module_name == '=')
266                 module_name++;
267               else if (i + 1 < *argc)
268                 {
269                   (*argv)[i] = NULL;
270                   i += 1;
271                   module_name = (*argv)[i];
272                 }
273               (*argv)[i] = NULL;
274
275               if (module_name && *module_name)
276                 gtk_modules = g_slist_prepend (gtk_modules, g_strdup (module_name));
277             }
278           else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0)
279             {
280               GLogLevelFlags fatal_mask;
281               
282               fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
283               fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
284               g_log_set_always_fatal (fatal_mask);
285               (*argv)[i] = NULL;
286             }
287 #ifdef G_ENABLE_DEBUG
288           else if ((strcmp ("--gtk-debug", (*argv)[i]) == 0) ||
289                    (strncmp ("--gtk-debug=", (*argv)[i], 12) == 0))
290             {
291               gchar *equal_pos = strchr ((*argv)[i], '=');
292               
293               if (equal_pos != NULL)
294                 {
295                   gtk_debug_flags |= g_parse_debug_string (equal_pos+1,
296                                                            gtk_debug_keys,
297                                                            gtk_ndebug_keys);
298                 }
299               else if ((i + 1) < *argc && (*argv)[i + 1])
300                 {
301                   gtk_debug_flags |= g_parse_debug_string ((*argv)[i+1],
302                                                            gtk_debug_keys,
303                                                            gtk_ndebug_keys);
304                   (*argv)[i] = NULL;
305                   i += 1;
306                 }
307               (*argv)[i] = NULL;
308             }
309           else if ((strcmp ("--gtk-no-debug", (*argv)[i]) == 0) ||
310                    (strncmp ("--gtk-no-debug=", (*argv)[i], 15) == 0))
311             {
312               gchar *equal_pos = strchr ((*argv)[i], '=');
313               
314               if (equal_pos != NULL)
315                 {
316                   gtk_debug_flags &= ~g_parse_debug_string (equal_pos+1,
317                                                             gtk_debug_keys,
318                                                             gtk_ndebug_keys);
319                 }
320               else if ((i + 1) < *argc && (*argv)[i + 1])
321                 {
322                   gtk_debug_flags &= ~g_parse_debug_string ((*argv)[i+1],
323                                                             gtk_debug_keys,
324                                                             gtk_ndebug_keys);
325                   (*argv)[i] = NULL;
326                   i += 1;
327                 }
328               (*argv)[i] = NULL;
329             }
330 #endif /* G_ENABLE_DEBUG */
331           i += 1;
332         }
333       
334       for (i = 1; i < *argc; i++)
335         {
336           for (k = i; k < *argc; k++)
337             if ((*argv)[k] != NULL)
338               break;
339           
340           if (k > i)
341             {
342               k -= i;
343               for (j = i + k; j < *argc; j++)
344                 (*argv)[j-k] = (*argv)[j];
345               *argc -= k;
346             }
347         }
348     }
349   
350   /* load gtk modules */
351   gtk_modules = g_slist_reverse (gtk_modules);
352   for (slist = gtk_modules; slist; slist = slist->next)
353     {
354       gchar *module_name;
355       GModule *module = NULL;
356       GtkModuleInitFunc modinit_func = NULL;
357       
358       module_name = slist->data;
359       slist->data = NULL;
360 #ifndef __EMX__
361       if (!g_path_is_absolute (module_name))
362         {
363           gchar *old = module_name;
364           
365           module_name = g_module_build_path (NULL, module_name);
366           g_free (old);
367         }
368 #else
369       module_name = add_dll_suffix(module_name);
370 #endif
371       if (g_module_supported ())
372         {
373           module = g_module_open (module_name, G_MODULE_BIND_LAZY);
374           if (module &&
375               g_module_symbol (module, "gtk_module_init", (gpointer*) &modinit_func) &&
376               modinit_func)
377             {
378               if (!g_slist_find (gtk_modules, modinit_func))
379                 {
380                   g_module_make_resident (module);
381                   slist->data = modinit_func;
382                 }
383               else
384                 {
385                   g_module_close (module);
386                   module = NULL;
387                 }
388             }
389         }
390       if (!modinit_func)
391         {
392           g_warning ("Failed to load module \"%s\": %s",
393                      module ? g_module_name (module) : module_name,
394                      g_module_error ());
395           if (module)
396             g_module_close (module);
397         }
398       g_free (module_name);
399     }
400
401 #ifdef ENABLE_NLS
402 #ifndef G_OS_WIN32
403   bindtextdomain("gtk+", GTK_LOCALEDIR);
404 #else
405   {
406     /* GTk+ locale dir is %WinDir%\gtk+\locale */
407     extern char *get_gtk_sysconf_directory ();
408     bindtextdomain ("gtk+", g_strconcat (get_gtk_sysconf_directory (),
409                                          G_DIR_SEPARATOR_S,
410                                          "locale",
411                                          NULL));
412   }
413 #endif
414 #endif  
415
416   /* Initialize the default visual and colormap to be
417    *  used in creating widgets. (We want to use the system
418    *  defaults so as to be nice to the colormap).
419    */
420   gtk_visual = gdk_visual_get_system ();
421   gtk_colormap = gdk_colormap_get_system ();
422
423   gtk_type_init ();
424   gtk_signal_init ();
425   gtk_rc_init ();
426   
427   
428   /* Register an exit function to make sure we are able to cleanup.
429    */
430   g_atexit (gtk_exit_func);
431   
432   /* Set the 'initialized' flag.
433    */
434   gtk_initialized = TRUE;
435
436   /* initialize gtk modules
437    */
438   for (slist = gtk_modules; slist; slist = slist->next)
439     {
440       if (slist->data)
441         {
442           GtkModuleInitFunc modinit;
443           
444           modinit = slist->data;
445           modinit (argc, argv);
446         }
447     }
448   g_slist_free (gtk_modules);
449   
450 #ifndef G_OS_WIN32
451   /* No use warning on Win32, there aren't any non-devel versions anyhow... */
452   g_warning (""              "YOU ARE USING THE DEVEL BRANCH 1.3.x OF GTK+ WHICH IS CURRENTLY\n"
453              "                UNDER HEAVY DEVELOPMENT AND FREQUENTLY INTRODUCES INSTABILITIES.\n"
454              "                if you don't know why you are getting this, you probably want to\n"
455              "                use the stable branch which can be retrived from\n"
456              "                ftp://ftp.gtk.org/pub/gtk/v1.2/ or via CVS with\n"
457              "                cvs checkout -r glib-1-2 glib; cvs checkout -r gtk-1-2 gtk+");
458 #endif
459
460   return TRUE;
461 }
462 void
463 gtk_init (int *argc, char ***argv)
464 {
465   if (!gtk_init_check (argc, argv))
466     {
467       g_warning ("cannot open display: %s", gdk_get_display ());
468       exit(1);
469     }
470 }
471
472 void
473 gtk_exit (gint errorcode)
474 {
475   /* Only if "gtk" has been initialized should we de-initialize.
476    */
477   /* de-initialisation is done by the gtk_exit_funct(),
478    * no need to do this here (Alex J.)
479    */
480   gdk_exit(errorcode);
481 }
482
483 gchar*
484 gtk_set_locale (void)
485 {
486   return gdk_set_locale ();
487 }
488
489 void
490 gtk_main (void)
491 {
492   GList *tmp_list;
493   GList *functions;
494   GtkInitFunction *init;
495   GMainLoop *loop;
496
497   gtk_main_loop_level++;
498   
499   loop = g_main_new (TRUE);
500   main_loops = g_slist_prepend (main_loops, loop);
501
502   tmp_list = functions = init_functions;
503   init_functions = NULL;
504   
505   while (tmp_list)
506     {
507       init = tmp_list->data;
508       tmp_list = tmp_list->next;
509       
510       (* init->function) (init->data);
511       g_free (init);
512     }
513   g_list_free (functions);
514
515   if (g_main_is_running (main_loops->data))
516     {
517       GDK_THREADS_LEAVE ();
518       g_main_run (loop);
519       GDK_THREADS_ENTER ();
520       gdk_flush ();
521     }
522
523   if (quit_functions)
524     {
525       GList *reinvoke_list = NULL;
526       GtkQuitFunction *quitf;
527
528       while (quit_functions)
529         {
530           quitf = quit_functions->data;
531
532           tmp_list = quit_functions;
533           quit_functions = g_list_remove_link (quit_functions, quit_functions);
534           g_list_free_1 (tmp_list);
535
536           if ((quitf->main_level && quitf->main_level != gtk_main_loop_level) ||
537               gtk_quit_invoke_function (quitf))
538             {
539               reinvoke_list = g_list_prepend (reinvoke_list, quitf);
540             }
541           else
542             {
543               gtk_quit_destroy (quitf);
544             }
545         }
546       if (reinvoke_list)
547         {
548           GList *work;
549           
550           work = g_list_last (reinvoke_list);
551           if (quit_functions)
552             quit_functions->prev = work;
553           work->next = quit_functions;
554           quit_functions = work;
555         }
556
557       gdk_flush ();
558     }
559               
560   main_loops = g_slist_remove (main_loops, loop);
561
562   g_main_destroy (loop);
563
564   gtk_main_loop_level--;
565 }
566
567 guint
568 gtk_main_level (void)
569 {
570   return gtk_main_loop_level;
571 }
572
573 void
574 gtk_main_quit (void)
575 {
576   g_return_if_fail (main_loops != NULL);
577
578   g_main_quit (main_loops->data);
579 }
580
581 gint
582 gtk_events_pending (void)
583 {
584   gboolean result;
585   
586   GDK_THREADS_LEAVE ();  
587   result = g_main_pending();
588   GDK_THREADS_ENTER ();
589
590   return result;
591 }
592
593 gint 
594 gtk_main_iteration (void)
595 {
596   GDK_THREADS_LEAVE ();
597   g_main_iteration (TRUE);
598   GDK_THREADS_ENTER ();
599
600   if (main_loops)
601     return !g_main_is_running (main_loops->data);
602   else
603     return TRUE;
604 }
605
606 gint 
607 gtk_main_iteration_do (gboolean blocking)
608 {
609   GDK_THREADS_LEAVE ();
610   g_main_iteration (blocking);
611   GDK_THREADS_ENTER ();
612
613   if (main_loops)
614     return !g_main_is_running (main_loops->data);
615   else
616     return TRUE;
617 }
618
619 void 
620 gtk_main_do_event (GdkEvent *event)
621 {
622   GtkWidget *event_widget;
623   GtkWidget *grab_widget;
624   GdkEvent *next_event;
625   GList *tmp_list;
626
627   /* If there are any events pending then get the next one.
628    */
629   next_event = gdk_event_peek ();
630   
631   /* Try to compress enter/leave notify events. These event
632    *  pairs occur when the mouse is dragged quickly across
633    *  a window with many buttons (or through a menu). Instead
634    *  of highlighting and de-highlighting each widget that
635    *  is crossed it is better to simply de-highlight the widget
636    *  which contained the mouse initially and highlight the
637    *  widget which ends up containing the mouse.
638    */
639   if (next_event)
640     if (((event->type == GDK_ENTER_NOTIFY) ||
641          (event->type == GDK_LEAVE_NOTIFY)) &&
642         ((next_event->type == GDK_ENTER_NOTIFY) ||
643          (next_event->type == GDK_LEAVE_NOTIFY)) &&
644         (next_event->type != event->type) &&
645         (next_event->any.window == event->any.window))
646       {
647         /* Throw both the peeked copy and the queued copy away 
648          */
649         gdk_event_free (next_event);
650         next_event = gdk_event_get ();
651         gdk_event_free (next_event);
652         
653         return;
654       }
655
656   if (next_event)
657     gdk_event_free (next_event);
658
659   /* Find the widget which got the event. We store the widget
660    *  in the user_data field of GdkWindow's.
661    *  Ignore the event if we don't have a widget for it, except
662    *  for GDK_PROPERTY_NOTIFY events which are handled specialy.
663    *  Though this happens rarely, bogus events can occour
664    *  for e.g. destroyed GdkWindows. 
665    */
666   event_widget = gtk_get_event_widget (event);
667   if (!event_widget)
668     {
669       /* To handle selection INCR transactions, we select
670        * PropertyNotify events on the requestor window and create
671        * a corresponding (fake) GdkWindow so that events get
672        * here. There won't be a widget though, so we have to handle
673            * them specially
674            */
675       if (event->type == GDK_PROPERTY_NOTIFY)
676         gtk_selection_incr_event (event->any.window,
677                                   &event->property);
678       
679       return;
680     }
681   
682   /* Push the event onto a stack of current events for
683    * gtk_current_event_get().
684    */
685   current_events = g_list_prepend (current_events, event);
686   
687   /* If there is a grab in effect...
688    */
689   if (grabs)
690     {
691       grab_widget = grabs->data;
692       
693       /* If the grab widget is an ancestor of the event widget
694        *  then we send the event to the original event widget.
695        *  This is the key to implementing modality.
696        */
697       if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
698           gtk_widget_is_ancestor (event_widget, grab_widget))
699         grab_widget = event_widget;
700     }
701   else
702     {
703       grab_widget = event_widget;
704     }
705
706   /* Not all events get sent to the grabbing widget.
707    * The delete, destroy, expose, focus change and resize
708    *  events still get sent to the event widget because
709    *  1) these events have no meaning for the grabbing widget
710    *  and 2) redirecting these events to the grabbing widget
711    *  could cause the display to be messed up.
712    * 
713    * Drag events are also not redirected, since it isn't
714    *  clear what the semantics of that would be.
715    */
716   switch (event->type)
717     {
718     case GDK_NOTHING:
719       break;
720       
721     case GDK_DELETE:
722       gtk_widget_ref (event_widget);
723       if ((!grabs || gtk_widget_get_toplevel (grabs->data) == event_widget) &&
724           !gtk_widget_event (event_widget, event))
725         gtk_widget_destroy (event_widget);
726       gtk_widget_unref (event_widget);
727       break;
728       
729     case GDK_DESTROY:
730       /* Unexpected GDK_DESTROY from the outside, ignore for
731        * child windows, handle like a GDK_DELETE for toplevels
732        */
733       if (!event_widget->parent)
734         {
735           gtk_widget_ref (event_widget);
736           if (!gtk_widget_event (event_widget, event) &&
737               !GTK_OBJECT_DESTROYED (event_widget))
738             gtk_widget_destroy (event_widget);
739           gtk_widget_unref (event_widget);
740         }
741       break;
742       
743     case GDK_EXPOSE:
744       if (event->any.window)
745         gdk_window_begin_paint_rect (event->any.window, &event->expose.area);
746
747       gtk_widget_event (event_widget, event);
748
749       if (event->any.window)
750         gdk_window_end_paint (event->any.window);
751       break;
752
753     case GDK_PROPERTY_NOTIFY:
754     case GDK_NO_EXPOSE:
755     case GDK_FOCUS_CHANGE:
756     case GDK_CONFIGURE:
757     case GDK_MAP:
758     case GDK_UNMAP:
759     case GDK_SELECTION_CLEAR:
760     case GDK_SELECTION_REQUEST:
761     case GDK_SELECTION_NOTIFY:
762     case GDK_CLIENT_EVENT:
763     case GDK_VISIBILITY_NOTIFY:
764       gtk_widget_event (event_widget, event);
765       break;
766
767     case GDK_SCROLL:
768     case GDK_BUTTON_PRESS:
769     case GDK_2BUTTON_PRESS:
770     case GDK_3BUTTON_PRESS:
771       gtk_propagate_event (grab_widget, event);
772       break;
773
774     case GDK_KEY_PRESS:
775     case GDK_KEY_RELEASE:
776       if (key_snoopers)
777         {
778           if (gtk_invoke_key_snoopers (grab_widget, event))
779             break;
780         }
781       /* else fall through */
782     case GDK_MOTION_NOTIFY:
783     case GDK_BUTTON_RELEASE:
784     case GDK_PROXIMITY_IN:
785     case GDK_PROXIMITY_OUT:
786       gtk_propagate_event (grab_widget, event);
787       break;
788       
789     case GDK_ENTER_NOTIFY:
790       if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
791         {
792           gtk_widget_event (grab_widget, event);
793           if (event_widget == grab_widget)
794             GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
795         }
796       break;
797       
798     case GDK_LEAVE_NOTIFY:
799       if (GTK_WIDGET_LEAVE_PENDING (event_widget))
800         {
801           GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
802           gtk_widget_event (event_widget, event);
803         }
804       else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
805         gtk_widget_event (grab_widget, event);
806       break;
807       
808     case GDK_DRAG_STATUS:
809     case GDK_DROP_FINISHED:
810       gtk_drag_source_handle_event (event_widget, event);
811       break;
812     case GDK_DRAG_ENTER:
813     case GDK_DRAG_LEAVE:
814     case GDK_DRAG_MOTION:
815     case GDK_DROP_START:
816       gtk_drag_dest_handle_event (event_widget, event);
817       break;
818     }
819   
820   tmp_list = current_events;
821   current_events = g_list_remove_link (current_events, tmp_list);
822   g_list_free_1 (tmp_list);
823 }
824
825 gint
826 gtk_true (void)
827 {
828   return TRUE;
829 }
830
831 gint
832 gtk_false (void)
833 {
834   return FALSE;
835 }
836
837 void
838 gtk_grab_add (GtkWidget *widget)
839 {
840   g_return_if_fail (widget != NULL);
841   
842   if (!GTK_WIDGET_HAS_GRAB (widget))
843     {
844       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
845       
846       grabs = g_slist_prepend (grabs, widget);
847       gtk_widget_ref (widget);
848     }
849 }
850
851 GtkWidget*
852 gtk_grab_get_current (void)
853 {
854   if (grabs)
855     return GTK_WIDGET (grabs->data);
856   return NULL;
857 }
858
859 void
860 gtk_grab_remove (GtkWidget *widget)
861 {
862   g_return_if_fail (widget != NULL);
863   
864   if (GTK_WIDGET_HAS_GRAB (widget))
865     {
866       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
867       
868       grabs = g_slist_remove (grabs, widget);
869       gtk_widget_unref (widget);
870     }
871 }
872
873 void
874 gtk_init_add (GtkFunction function,
875               gpointer    data)
876 {
877   GtkInitFunction *init;
878   
879   init = g_new (GtkInitFunction, 1);
880   init->function = function;
881   init->data = data;
882   
883   init_functions = g_list_prepend (init_functions, init);
884 }
885
886 guint
887 gtk_key_snooper_install (GtkKeySnoopFunc snooper,
888                          gpointer        func_data)
889 {
890   GtkKeySnooperData *data;
891   static guint snooper_id = 1;
892
893   g_return_val_if_fail (snooper != NULL, 0);
894
895   data = g_new (GtkKeySnooperData, 1);
896   data->func = snooper;
897   data->func_data = func_data;
898   data->id = snooper_id++;
899   key_snoopers = g_slist_prepend (key_snoopers, data);
900
901   return data->id;
902 }
903
904 void
905 gtk_key_snooper_remove (guint            snooper_id)
906 {
907   GtkKeySnooperData *data = NULL;
908   GSList *slist;
909
910   slist = key_snoopers;
911   while (slist)
912     {
913       data = slist->data;
914       if (data->id == snooper_id)
915         break;
916
917       slist = slist->next;
918       data = NULL;
919     }
920   if (data)
921     key_snoopers = g_slist_remove (key_snoopers, data);
922 }
923
924 static gint
925 gtk_invoke_key_snoopers (GtkWidget *grab_widget,
926                          GdkEvent  *event)
927 {
928   GSList *slist;
929   gint return_val = FALSE;
930
931   slist = key_snoopers;
932   while (slist && !return_val)
933     {
934       GtkKeySnooperData *data;
935
936       data = slist->data;
937       slist = slist->next;
938       return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data);
939     }
940
941   return return_val;
942 }
943
944 guint
945 gtk_quit_add_full (guint                main_level,
946                    GtkFunction          function,
947                    GtkCallbackMarshal   marshal,
948                    gpointer             data,
949                    GtkDestroyNotify     destroy)
950 {
951   static guint quit_id = 1;
952   GtkQuitFunction *quitf;
953   
954   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
955
956   if (!quit_mem_chunk)
957     quit_mem_chunk = g_mem_chunk_new ("quit mem chunk", sizeof (GtkQuitFunction),
958                                       512, G_ALLOC_AND_FREE);
959   
960   quitf = g_chunk_new (GtkQuitFunction, quit_mem_chunk);
961   
962   quitf->id = quit_id++;
963   quitf->main_level = main_level;
964   quitf->function = function;
965   quitf->marshal = marshal;
966   quitf->data = data;
967   quitf->destroy = destroy;
968
969   quit_functions = g_list_prepend (quit_functions, quitf);
970   
971   return quitf->id;
972 }
973
974 static void
975 gtk_quit_destroy (GtkQuitFunction *quitf)
976 {
977   if (quitf->destroy)
978     quitf->destroy (quitf->data);
979   g_mem_chunk_free (quit_mem_chunk, quitf);
980 }
981
982 static gint
983 gtk_quit_destructor (GtkObject **object_p)
984 {
985   if (*object_p)
986     gtk_object_destroy (*object_p);
987   g_free (object_p);
988
989   return FALSE;
990 }
991
992 void
993 gtk_quit_add_destroy (guint              main_level,
994                       GtkObject         *object)
995 {
996   GtkObject **object_p;
997
998   g_return_if_fail (main_level > 0);
999   g_return_if_fail (object != NULL);
1000   g_return_if_fail (GTK_IS_OBJECT (object));
1001
1002   object_p = g_new (GtkObject*, 1);
1003   *object_p = object;
1004   gtk_signal_connect (object,
1005                       "destroy",
1006                       GTK_SIGNAL_FUNC (gtk_widget_destroyed),
1007                       object_p);
1008   gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p);
1009 }
1010
1011 guint
1012 gtk_quit_add (guint       main_level,
1013               GtkFunction function,
1014               gpointer    data)
1015 {
1016   return gtk_quit_add_full (main_level, function, NULL, data, NULL);
1017 }
1018
1019 void
1020 gtk_quit_remove (guint id)
1021 {
1022   GtkQuitFunction *quitf;
1023   GList *tmp_list;
1024   
1025   tmp_list = quit_functions;
1026   while (tmp_list)
1027     {
1028       quitf = tmp_list->data;
1029       
1030       if (quitf->id == id)
1031         {
1032           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1033           g_list_free (tmp_list);
1034           gtk_quit_destroy (quitf);
1035           
1036           return;
1037         }
1038       
1039       tmp_list = tmp_list->next;
1040     }
1041 }
1042
1043 void
1044 gtk_quit_remove_by_data (gpointer data)
1045 {
1046   GtkQuitFunction *quitf;
1047   GList *tmp_list;
1048   
1049   tmp_list = quit_functions;
1050   while (tmp_list)
1051     {
1052       quitf = tmp_list->data;
1053       
1054       if (quitf->data == data)
1055         {
1056           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1057           g_list_free (tmp_list);
1058           gtk_quit_destroy (quitf);
1059
1060           return;
1061         }
1062       
1063       tmp_list = tmp_list->next;
1064     }
1065 }
1066
1067 guint
1068 gtk_timeout_add_full (guint32            interval,
1069                       GtkFunction        function,
1070                       GtkCallbackMarshal marshal,
1071                       gpointer           data,
1072                       GtkDestroyNotify   destroy)
1073 {
1074   if (marshal)
1075     {
1076       GtkClosure *closure;
1077
1078       closure = g_new (GtkClosure, 1);
1079       closure->marshal = marshal;
1080       closure->data = data;
1081       closure->destroy = destroy;
1082
1083       return g_timeout_add_full (0, interval, 
1084                                  gtk_invoke_idle_timeout,
1085                                  closure,
1086                                  gtk_destroy_closure);
1087     }
1088   else
1089     return g_timeout_add_full (0, interval, function, data, destroy);
1090 }
1091
1092 guint
1093 gtk_timeout_add (guint32     interval,
1094                  GtkFunction function,
1095                  gpointer    data)
1096 {
1097   return g_timeout_add_full (0, interval, function, data, NULL);
1098 }
1099
1100 void
1101 gtk_timeout_remove (guint tag)
1102 {
1103   g_source_remove (tag);
1104 }
1105
1106 guint
1107 gtk_idle_add_full (gint                 priority,
1108                    GtkFunction          function,
1109                    GtkCallbackMarshal   marshal,
1110                    gpointer             data,
1111                    GtkDestroyNotify     destroy)
1112 {
1113   if (marshal)
1114     {
1115       GtkClosure *closure;
1116
1117       closure = g_new (GtkClosure, 1);
1118       closure->marshal = marshal;
1119       closure->data = data;
1120       closure->destroy = destroy;
1121
1122       return g_idle_add_full (priority,
1123                               gtk_invoke_idle_timeout,
1124                               closure,
1125                               gtk_destroy_closure);
1126     }
1127   else
1128     return g_idle_add_full (priority, function, data, destroy);
1129 }
1130
1131 guint
1132 gtk_idle_add (GtkFunction function,
1133               gpointer    data)
1134 {
1135   return g_idle_add_full (GTK_PRIORITY_DEFAULT, function, data, NULL);
1136 }
1137
1138 guint       
1139 gtk_idle_add_priority   (gint               priority,
1140                          GtkFunction        function,
1141                          gpointer           data)
1142 {
1143   return g_idle_add_full (priority, function, data, NULL);
1144 }
1145
1146 void
1147 gtk_idle_remove (guint tag)
1148 {
1149   g_source_remove (tag);
1150 }
1151
1152 void
1153 gtk_idle_remove_by_data (gpointer data)
1154 {
1155   if (!g_idle_remove_by_data (data))
1156     g_warning ("gtk_idle_remove_by_data(%p): no such idle", data);
1157 }
1158
1159 guint
1160 gtk_input_add_full (gint                source,
1161                     GdkInputCondition   condition,
1162                     GdkInputFunction    function,
1163                     GtkCallbackMarshal  marshal,
1164                     gpointer            data,
1165                     GtkDestroyNotify    destroy)
1166 {
1167   if (marshal)
1168     {
1169       GtkClosure *closure;
1170
1171       closure = g_new (GtkClosure, 1);
1172       closure->marshal = marshal;
1173       closure->data = data;
1174       closure->destroy = destroy;
1175
1176       return gdk_input_add_full (source,
1177                                  condition,
1178                                  (GdkInputFunction) gtk_invoke_input,
1179                                  closure,
1180                                  (GdkDestroyNotify) gtk_destroy_closure);
1181     }
1182   else
1183     return gdk_input_add_full (source, condition, function, data, destroy);
1184 }
1185
1186 void
1187 gtk_input_remove (guint tag)
1188 {
1189   g_source_remove (tag);
1190 }
1191
1192 static void
1193 gtk_destroy_closure (gpointer data)
1194 {
1195   GtkClosure *closure = data;
1196
1197   if (closure->destroy)
1198     (closure->destroy) (closure->data);
1199   g_free (closure);
1200 }
1201
1202 static gboolean
1203 gtk_invoke_idle_timeout (gpointer data)
1204 {
1205   GtkClosure *closure = data;
1206
1207   GtkArg args[1];
1208   gint ret_val = FALSE;
1209   args[0].name = NULL;
1210   args[0].type = GTK_TYPE_BOOL;
1211   args[0].d.pointer_data = &ret_val;
1212   closure->marshal (NULL, closure->data,  0, args);
1213   return ret_val;
1214 }
1215
1216 static void
1217 gtk_invoke_input (gpointer          data,
1218                   gint              source,
1219                   GdkInputCondition condition)
1220 {
1221   GtkClosure *closure = data;
1222
1223   GtkArg args[3];
1224   args[0].type = GTK_TYPE_INT;
1225   args[0].name = NULL;
1226   GTK_VALUE_INT(args[0]) = source;
1227   args[1].type = GTK_TYPE_GDK_INPUT_CONDITION;
1228   args[1].name = NULL;
1229   GTK_VALUE_FLAGS(args[1]) = condition;
1230   args[2].type = GTK_TYPE_NONE;
1231   args[2].name = NULL;
1232
1233   closure->marshal (NULL, closure->data, 2, args);
1234 }
1235
1236 GdkEvent*
1237 gtk_get_current_event (void)
1238 {
1239   if (current_events)
1240     return gdk_event_copy ((GdkEvent *) current_events->data);
1241   else
1242     return NULL;
1243 }
1244
1245 GtkWidget*
1246 gtk_get_event_widget (GdkEvent *event)
1247 {
1248   GtkWidget *widget;
1249
1250   widget = NULL;
1251   if (event && event->any.window)
1252     gdk_window_get_user_data (event->any.window, (void**) &widget);
1253   
1254   return widget;
1255 }
1256
1257 static void
1258 gtk_exit_func (void)
1259 {
1260   if (gtk_initialized)
1261     {
1262       gtk_initialized = FALSE;
1263       gtk_preview_uninit ();
1264     }
1265 }
1266
1267
1268 static gint
1269 gtk_quit_invoke_function (GtkQuitFunction *quitf)
1270 {
1271   if (!quitf->marshal)
1272     return quitf->function (quitf->data);
1273   else
1274     {
1275       GtkArg args[1];
1276       gint ret_val = FALSE;
1277
1278       args[0].name = NULL;
1279       args[0].type = GTK_TYPE_BOOL;
1280       args[0].d.pointer_data = &ret_val;
1281       ((GtkCallbackMarshal) quitf->marshal) (NULL,
1282                                              quitf->data,
1283                                              0, args);
1284       return ret_val;
1285     }
1286 }
1287
1288 void
1289 gtk_propagate_event (GtkWidget *widget,
1290                      GdkEvent  *event)
1291 {
1292   gint handled_event;
1293   
1294   g_return_if_fail (widget != NULL);
1295   g_return_if_fail (GTK_IS_WIDGET (widget));
1296   g_return_if_fail (event != NULL);
1297   
1298   handled_event = FALSE;
1299
1300   if ((event->type == GDK_KEY_PRESS) ||
1301       (event->type == GDK_KEY_RELEASE))
1302     {
1303       /* Only send key events within Window widgets to the Window
1304        *  The Window widget will in turn pass the
1305        *  key event on to the currently focused widget
1306        *  for that window.
1307        */
1308       GtkWidget *window;
1309
1310       window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
1311       if (window)
1312         {
1313           if (GTK_WIDGET_IS_SENSITIVE (window))
1314             gtk_widget_event (window, event);
1315
1316           handled_event = TRUE; /* don't send to widget */
1317         }
1318     }
1319   
1320   /* Other events get propagated up the widget tree
1321    *  so that parents can see the button and motion
1322    *  events of the children.
1323    */
1324   while (!handled_event && widget)
1325     {
1326       GtkWidget *tmp;
1327
1328       gtk_widget_ref (widget);
1329       handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
1330       tmp = widget->parent;
1331       gtk_widget_unref (widget);
1332       widget  = tmp;
1333     }
1334 }
1335
1336 #if 0
1337 static void
1338 gtk_error (gchar *str)
1339 {
1340   gtk_print (str);
1341 }
1342
1343 static void
1344 gtk_warning (gchar *str)
1345 {
1346   gtk_print (str);
1347 }
1348
1349 static void
1350 gtk_message (gchar *str)
1351 {
1352   gtk_print (str);
1353 }
1354
1355 static void
1356 gtk_print (gchar *str)
1357 {
1358   static GtkWidget *window = NULL;
1359   static GtkWidget *text;
1360   static int level = 0;
1361   GtkWidget *box1;
1362   GtkWidget *box2;
1363   GtkWidget *table;
1364   GtkWidget *hscrollbar;
1365   GtkWidget *vscrollbar;
1366   GtkWidget *separator;
1367   GtkWidget *button;
1368   
1369   if (level > 0)
1370     {
1371       fputs (str, stdout);
1372       fflush (stdout);
1373       return;
1374     }
1375   
1376   if (!window)
1377     {
1378       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1379       
1380       gtk_signal_connect (GTK_OBJECT (window), "destroy",
1381                           (GtkSignalFunc) gtk_widget_destroyed,
1382                           &window);
1383       
1384       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1385       
1386       box1 = gtk_vbox_new (FALSE, 0);
1387       gtk_container_add (GTK_CONTAINER (window), box1);
1388       gtk_widget_show (box1);
1389       
1390       
1391       box2 = gtk_vbox_new (FALSE, 10);
1392       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1393       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1394       gtk_widget_show (box2);
1395       
1396       
1397       table = gtk_table_new (2, 2, FALSE);
1398       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1399       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1400       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1401       gtk_widget_show (table);
1402       
1403       text = gtk_text_new (NULL, NULL);
1404       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1405       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1406       gtk_widget_show (text);
1407       gtk_widget_realize (text);
1408       
1409       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1410       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1411                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1412       gtk_widget_show (hscrollbar);
1413       
1414       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1415       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1416                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1417       gtk_widget_show (vscrollbar);
1418       
1419       separator = gtk_hseparator_new ();
1420       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1421       gtk_widget_show (separator);
1422       
1423       
1424       box2 = gtk_vbox_new (FALSE, 10);
1425       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1426       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1427       gtk_widget_show (box2);
1428       
1429       
1430       button = gtk_button_new_with_label ("close");
1431       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1432                                  (GtkSignalFunc) gtk_widget_hide,
1433                                  GTK_OBJECT (window));
1434       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1435       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1436       gtk_widget_grab_default (button);
1437       gtk_widget_show (button);
1438     }
1439   
1440   level += 1;
1441   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1442   level -= 1;
1443   
1444   if (!GTK_WIDGET_VISIBLE (window))
1445     gtk_widget_show (window);
1446 }
1447 #endif