]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
Implement a search path for GTK+ modules.
[~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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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-2000.  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 #include "gdkconfig.h"
28
29 #include <locale.h>
30
31 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
32 #include <libintl.h>
33 #endif
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <gmodule.h>
39 #ifdef G_OS_UNIX
40 #include <unistd.h>
41 #endif
42
43 #include <pango/pango-utils.h>  /* For pango_split_file_list */
44
45 #include "gtkdnd.h"
46 #include "gtkversion.h"
47 #include "gtkmain.h"
48 #include "gtkrc.h"
49 #include "gtkselection.h"
50 #include "gtksettings.h"
51 #include "gtksignal.h"
52 #include "gtkwidget.h"
53 #include "gtkwindow.h"
54 #include "gtkprivate.h"
55 #include "gdk/gdki18n.h"
56 #include "config.h"
57 #include "gtkdebug.h"
58 #include "gtkintl.h"
59
60 /* Private type definitions
61  */
62 typedef struct _GtkInitFunction          GtkInitFunction;
63 typedef struct _GtkQuitFunction          GtkQuitFunction;
64 typedef struct _GtkClosure               GtkClosure;
65 typedef struct _GtkKeySnooperData        GtkKeySnooperData;
66
67 struct _GtkInitFunction
68 {
69   GtkFunction function;
70   gpointer data;
71 };
72
73 struct _GtkQuitFunction
74 {
75   guint id;
76   guint main_level;
77   GtkCallbackMarshal marshal;
78   GtkFunction function;
79   gpointer data;
80   GtkDestroyNotify destroy;
81 };
82
83 struct _GtkClosure
84 {
85   GtkCallbackMarshal marshal;
86   gpointer data;
87   GtkDestroyNotify destroy;
88 };
89
90 struct _GtkKeySnooperData
91 {
92   GtkKeySnoopFunc func;
93   gpointer func_data;
94   guint id;
95 };
96
97 static void  gtk_exit_func               (void);
98 static gint  gtk_quit_invoke_function    (GtkQuitFunction    *quitf);
99 static void  gtk_quit_destroy            (GtkQuitFunction    *quitf);
100 static gint  gtk_invoke_key_snoopers     (GtkWidget          *grab_widget,
101                                           GdkEvent           *event);
102
103 static void     gtk_destroy_closure      (gpointer            data);
104 static gboolean gtk_invoke_idle_timeout  (gpointer            data);
105 static void     gtk_invoke_input         (gpointer            data,
106                                           gint                source,
107                                           GdkInputCondition   condition);
108
109 #if 0
110 static void  gtk_error                   (gchar              *str);
111 static void  gtk_warning                 (gchar              *str);
112 static void  gtk_message                 (gchar              *str);
113 static void  gtk_print                   (gchar              *str);
114 #endif
115
116 static GtkWindowGroup *gtk_main_get_window_group (GtkWidget   *widget);
117
118 const guint gtk_major_version = GTK_MAJOR_VERSION;
119 const guint gtk_minor_version = GTK_MINOR_VERSION;
120 const guint gtk_micro_version = GTK_MICRO_VERSION;
121 const guint gtk_binary_age = GTK_BINARY_AGE;
122 const guint gtk_interface_age = GTK_INTERFACE_AGE;
123
124 static guint gtk_main_loop_level = 0;
125 static gint gtk_initialized = FALSE;
126 static GList *current_events = NULL;
127
128 static GSList *main_loops = NULL;      /* stack of currently executing main loops */
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   {"misc", GTK_DEBUG_MISC},
150   {"plugsocket", GTK_DEBUG_PLUGSOCKET},
151   {"text", GTK_DEBUG_TEXT},
152   {"tree", GTK_DEBUG_TREE},
153   {"updates", GTK_DEBUG_UPDATES}
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 #undef gtk_init_check
181
182 /* This checks to see if the process is running suid or sgid
183  * at the current time. If so, we don't allow GTK+ to be initialized.
184  * This is meant to be a mild check - we only error out if we
185  * can prove the programmer is doing something wrong, not if
186  * they could be doing something wrong. For this reason, we
187  * don't use issetugid() on BSD or prctl (PR_GET_DUMPABLE).
188  */
189 static gboolean
190 check_setugid (void)
191 {
192 /* this isn't at all relevant on MS Windows and doesn't compile ... --hb */
193 #ifndef G_OS_WIN32
194   uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
195   gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
196   
197 #ifdef HAVE_GETRESUID
198   /* These aren't in the header files, so we prototype them here.
199    */
200   int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
201   int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
202
203   if (getresuid (&ruid, &euid, &suid) != 0 ||
204       getresgid (&rgid, &egid, &sgid) != 0)
205 #endif /* HAVE_GETRESUID */
206     {
207       suid = ruid = getuid ();
208       sgid = rgid = getgid ();
209       euid = geteuid ();
210       egid = getegid ();
211     }
212
213   if (ruid != euid || ruid != suid ||
214       rgid != egid || rgid != sgid)
215     {
216       g_warning ("This process is currently running setuid or setgid.\n"
217                  "This is not a supported use of GTK+. You must create a helper\n"
218                  "program instead. For further details, see:\n\n"
219                  "    http://www.gtk.org/setuid.html\n\n"
220                  "Refusing to initialize GTK+.");
221       exit (1);
222     }
223 #endif
224   return TRUE;
225 }
226
227 static gchar **
228 get_module_path (void)
229 {
230   gchar *module_path = g_getenv ("GTK_MODULE_PATH");
231   gchar *exe_prefix = g_getenv("GTK_EXE_PREFIX");
232   gchar **result;
233   gchar *default_dir;
234
235   if (exe_prefix)
236     default_dir = g_build_filename (exe_prefix, "lib", "gtk-2.0", "modules", NULL);
237   else
238     default_dir = g_build_filename (GTK_LIBDIR, "gtk-2.0", "modules", NULL);
239
240   module_path = g_strconcat (module_path ? module_path : "",
241                              module_path ? G_SEARCHPATH_SEPARATOR_S : "",
242                              default_dir, NULL);
243
244   result = pango_split_file_list (module_path);
245
246   g_free (default_dir);
247   g_free (module_path);
248
249   return result;
250 }
251
252 static GModule *
253 find_module (gchar      **module_path,
254              const gchar *name)
255 {
256   GModule *module;
257   gchar *module_name;
258   gint i;
259
260   if (g_path_is_absolute (name))
261     return g_module_open (name, G_MODULE_BIND_LAZY);
262
263   for (i = 0; module_path[i]; i++)
264     {
265       gchar *version_directory;
266
267       version_directory = g_build_filename (module_path[i], GTK_BINARY_VERSION, NULL);
268       module_name = g_module_build_path (version_directory, name);
269       g_free (version_directory);
270       
271       if (g_file_test (module_name, G_FILE_TEST_EXISTS))
272         {
273           g_free (module_name);
274           return g_module_open (module_name, G_MODULE_BIND_LAZY);
275         }
276       
277       g_free (module_name);
278
279       module_name = g_module_build_path (module_path[i], name);
280       
281       if (g_file_test (module_name, G_FILE_TEST_EXISTS))
282         {
283           g_free (module_name);
284           return g_module_open (module_name, G_MODULE_BIND_LAZY);
285         }
286
287       g_free (module_name);
288     }
289
290   /* As last resort, try loading without an absolute path (using system
291    * library path)
292    */
293   module_name = g_module_build_path (NULL, name);
294   module = g_module_open (module_name, G_MODULE_BIND_LAZY);
295   g_free(module_name);
296
297   return module;
298 }
299
300 static GSList *
301 load_module (GSList      *gtk_modules,
302              gchar      **module_path,
303              const gchar *name)
304 {
305   GtkModuleInitFunc modinit_func = NULL;
306   GModule *module = NULL;
307   
308   if (g_module_supported ())
309     {
310       module = find_module (module_path, name);
311       if (module &&
312           g_module_symbol (module, "gtk_module_init", (gpointer*) &modinit_func) &&
313           modinit_func)
314         {
315           if (!g_slist_find (gtk_modules, modinit_func))
316             {
317               g_module_make_resident (module);
318               gtk_modules = g_slist_prepend (gtk_modules, modinit_func);
319             }
320           else
321             {
322               g_module_close (module);
323               module = NULL;
324             }
325         }
326     }
327   if (!modinit_func)
328     {
329       g_message ("Failed to load module \"%s\": %s",
330                  module ? g_module_name (module) : name,
331                  g_module_error ());
332       if (module)
333         g_module_close (module);
334     }
335   
336   return gtk_modules;
337 }
338
339 static GSList *
340 load_modules (const char *module_str)
341 {
342   gchar **module_path = get_module_path ();
343   gchar **module_names = pango_split_file_list (module_str);
344   GSList *gtk_modules = NULL;
345   gint i;
346   
347   for (i = 0; module_names[i]; i++)
348     gtk_modules = load_module (gtk_modules, module_path, module_names[i]);
349   
350   gtk_modules = g_slist_reverse (gtk_modules);
351   
352   g_strfreev (module_names);
353   g_strfreev (module_path);
354
355   return gtk_modules;
356 }
357
358 gboolean
359 gtk_init_check (int      *argc,
360                 char   ***argv)
361 {
362   GString *gtk_modules_string = NULL;
363   GSList *gtk_modules = NULL;
364   GSList *slist;
365   gchar *env_string;
366
367   if (gtk_initialized)
368     return TRUE;
369
370   if (!check_setugid ())
371     return FALSE;
372   
373 #if     0
374   g_set_error_handler (gtk_error);
375   g_set_warning_handler (gtk_warning);
376   g_set_message_handler (gtk_message);
377   g_set_print_handler (gtk_print);
378 #endif
379   
380   /* Initialize "gdk". We pass along the 'argc' and 'argv'
381    *  parameters as they contain information that GDK uses
382    */
383   if (!gdk_init_check (argc, argv))
384     return FALSE;
385
386   gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
387   
388 #ifdef G_ENABLE_DEBUG
389   env_string = getenv ("GTK_DEBUG");
390   if (env_string != NULL)
391     {
392       gtk_debug_flags = g_parse_debug_string (env_string,
393                                               gtk_debug_keys,
394                                               gtk_ndebug_keys);
395       env_string = NULL;
396     }
397 #endif  /* G_ENABLE_DEBUG */
398
399   env_string = getenv ("GTK_MODULES");
400   if (env_string)
401     gtk_modules_string = g_string_new (env_string);
402
403   if (argc && argv)
404     {
405       gint i, j, k;
406       
407       for (i = 1; i < *argc;)
408         {
409           if (strcmp ("--gtk-module", (*argv)[i]) == 0 ||
410               strncmp ("--gtk-module=", (*argv)[i], 13) == 0)
411             {
412               gchar *module_name = (*argv)[i] + 12;
413               
414               if (*module_name == '=')
415                 module_name++;
416               else if (i + 1 < *argc)
417                 {
418                   (*argv)[i] = NULL;
419                   i += 1;
420                   module_name = (*argv)[i];
421                 }
422               (*argv)[i] = NULL;
423
424               if (module_name && *module_name)
425                 {
426                   if (gtk_modules_string)
427                     g_string_append_c (gtk_modules_string, G_SEARCHPATH_SEPARATOR);
428                   else
429                     gtk_modules_string = g_string_new (NULL);
430
431                   g_string_append (gtk_modules_string, module_name);
432                 }
433             }
434           else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0)
435             {
436               GLogLevelFlags fatal_mask;
437               
438               fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
439               fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
440               g_log_set_always_fatal (fatal_mask);
441               (*argv)[i] = NULL;
442             }
443 #ifdef G_ENABLE_DEBUG
444           else if ((strcmp ("--gtk-debug", (*argv)[i]) == 0) ||
445                    (strncmp ("--gtk-debug=", (*argv)[i], 12) == 0))
446             {
447               gchar *equal_pos = strchr ((*argv)[i], '=');
448               
449               if (equal_pos != NULL)
450                 {
451                   gtk_debug_flags |= g_parse_debug_string (equal_pos+1,
452                                                            gtk_debug_keys,
453                                                            gtk_ndebug_keys);
454                 }
455               else if ((i + 1) < *argc && (*argv)[i + 1])
456                 {
457                   gtk_debug_flags |= g_parse_debug_string ((*argv)[i+1],
458                                                            gtk_debug_keys,
459                                                            gtk_ndebug_keys);
460                   (*argv)[i] = NULL;
461                   i += 1;
462                 }
463               (*argv)[i] = NULL;
464             }
465           else if ((strcmp ("--gtk-no-debug", (*argv)[i]) == 0) ||
466                    (strncmp ("--gtk-no-debug=", (*argv)[i], 15) == 0))
467             {
468               gchar *equal_pos = strchr ((*argv)[i], '=');
469               
470               if (equal_pos != NULL)
471                 {
472                   gtk_debug_flags &= ~g_parse_debug_string (equal_pos+1,
473                                                             gtk_debug_keys,
474                                                             gtk_ndebug_keys);
475                 }
476               else if ((i + 1) < *argc && (*argv)[i + 1])
477                 {
478                   gtk_debug_flags &= ~g_parse_debug_string ((*argv)[i+1],
479                                                             gtk_debug_keys,
480                                                             gtk_ndebug_keys);
481                   (*argv)[i] = NULL;
482                   i += 1;
483                 }
484               (*argv)[i] = NULL;
485             }
486 #endif /* G_ENABLE_DEBUG */
487           i += 1;
488         }
489       
490       for (i = 1; i < *argc; i++)
491         {
492           for (k = i; k < *argc; k++)
493             if ((*argv)[k] != NULL)
494               break;
495           
496           if (k > i)
497             {
498               k -= i;
499               for (j = i + k; j < *argc; j++)
500                 (*argv)[j-k] = (*argv)[j];
501               *argc -= k;
502             }
503         }
504     }
505
506   if (gtk_debug_flags & GTK_DEBUG_UPDATES)
507     gdk_window_set_debug_updates (TRUE);
508
509   /* load gtk modules */
510   if (gtk_modules_string)
511     {
512       gtk_modules = load_modules (gtk_modules_string->str);
513       g_string_free (gtk_modules_string, TRUE);
514     }
515
516 #ifdef ENABLE_NLS
517 #  ifndef G_OS_WIN32
518   bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
519 #    ifdef HAVE_BIND_TEXTDOMAIN_CODESET
520   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
521 #    endif
522 #  else /* !G_OS_WIN32 */
523   {
524     bindtextdomain (GETTEXT_PACKAGE,
525                     g_win32_get_package_installation_subdirectory (GETTEXT_PACKAGE,
526                                                                    g_strdup_printf ("gtk-%d.%d.dll", GTK_MAJOR_VERSION, GTK_MINOR_VERSION),
527                                                                    "locale"));
528   }
529 #endif
530 #endif  
531
532   {
533   /* Translate to default:RTL if you want your widgets
534    * to be RTL, otherwise translate to default:LTR.
535    * Do *not* translate it to "predefinito:LTR", if it
536    * it isn't default:LTR or default:RTL it will not work 
537    */
538     char *e = _("default:LTR");
539     if (strcmp (e, "default:RTL")==0) {
540       gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
541     } else if (strcmp (e, "default:LTR")) {
542       g_warning ("Whoever translated default:LTR did so wrongly.\n");
543     }
544   }
545
546   /* Initialize the default visual and colormap to be
547    *  used in creating widgets. (We want to use the system
548    *  defaults so as to be nice to the colormap).
549    */
550   gtk_visual = gdk_visual_get_system ();
551   gtk_colormap = gdk_colormap_get_system ();
552
553   gtk_type_init (0);
554   _gtk_rc_init ();
555   
556   
557   /* Register an exit function to make sure we are able to cleanup.
558    */
559   g_atexit (gtk_exit_func);
560   
561   /* Set the 'initialized' flag.
562    */
563   gtk_initialized = TRUE;
564
565   /* initialize gtk modules
566    */
567   for (slist = gtk_modules; slist; slist = slist->next)
568     {
569       if (slist->data)
570         {
571           GtkModuleInitFunc modinit;
572           
573           modinit = slist->data;
574           modinit (argc, argv);
575         }
576     }
577   g_slist_free (gtk_modules);
578   
579 #ifndef G_OS_WIN32
580   /* No use warning on Win32, there aren't any non-devel versions anyhow... */
581   g_message (""              "YOU ARE USING THE DEVEL BRANCH 1.3.x OF GTK+ WHICH IS CURRENTLY\n"
582              "                UNDER HEAVY DEVELOPMENT AND FREQUENTLY INTRODUCES INSTABILITIES.\n"
583              "                if you don't know why you are getting this, you probably want to\n"
584              "                use the stable branch which can be retrived from\n"
585              "                ftp://ftp.gtk.org/pub/gtk/v1.2/ or via CVS with\n"
586              "                cvs checkout -r glib-1-2 glib; cvs checkout -r gtk-1-2 gtk+");
587 #endif
588
589   return TRUE;
590 }
591
592 #undef gtk_init
593
594 void
595 gtk_init (int *argc, char ***argv)
596 {
597   if (!gtk_init_check (argc, argv))
598     {
599       g_warning ("cannot open display: %s", gdk_get_display ());
600       exit (1);
601     }
602 }
603
604 #ifdef G_OS_WIN32
605
606 static void
607 check_sizeof_GtkWindow (size_t sizeof_GtkWindow)
608 {
609   if (sizeof_GtkWindow != sizeof (GtkWindow))
610     g_error ("Incompatible build!\n"
611              "The code using GTK+ thinks GtkWindow is of different\n"
612              "size than it actually is in this build of GTK+.\n"
613              "On Windows, this probably means that you have compiled\n"
614              "your code with gcc without the -fnative-struct switch.");
615 }
616
617 /* These two functions might get more checks added later, thus pass
618  * in the number of extra args.
619  */
620 void
621 gtk_init_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow)
622 {
623   check_sizeof_GtkWindow (sizeof_GtkWindow);
624   gtk_init (argc, argv);
625 }
626
627 gboolean
628 gtk_init_check_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow)
629 {
630   check_sizeof_GtkWindow (sizeof_GtkWindow);
631   return gtk_init_check (argc, argv);
632 }
633
634 #endif
635
636 void
637 gtk_exit (gint errorcode)
638 {
639   /* Only if "gtk" has been initialized should we de-initialize.
640    */
641   /* de-initialisation is done by the gtk_exit_funct(),
642    * no need to do this here (Alex J.)
643    */
644   gdk_exit (errorcode);
645 }
646
647
648 /**
649  * gtk_set_locale:
650  *
651  *
652  * Initializes internationalization support for GTK+.  You
653  * should call this function before gtk_init() if your application
654  * supports internationalization.
655  * 
656  *  (In gory detail - sets the current locale according to the
657  * program environment. This is the same as calling the libc function
658  * setlocale (LC_ALL, "") but also takes care of the locale specific
659  * setup of the windowing system used by GDK.)
660  * 
661  * Return value: a string corresponding to the locale set, as with the C library function setlocale()
662  **/
663 gchar*
664 gtk_set_locale (void)
665 {
666   return gdk_set_locale ();
667 }
668
669 /**
670  * gtk_get_default_language:
671  *
672  * Returns the ISO language code for the default language currently in
673  * effect. (Note that this can change over the life of an
674  * application.)  The default language is derived from the current
675  * locale. It determines, for example, whether GTK+ uses the
676  * right-to-left or left-to-right text direction.
677  * 
678  * Return value: the default language as an allocated string, must be freed
679  **/
680 PangoLanguage *
681 gtk_get_default_language (void)
682 {
683   gchar *lang;
684   PangoLanguage *result;
685   gchar *p;
686   
687   lang = g_strdup (setlocale (LC_CTYPE, NULL));
688   p = strchr (lang, '.');
689   if (p)
690     *p = '\0';
691   p = strchr (lang, '@');
692   if (p)
693     *p = '\0';
694
695   result = pango_language_from_string (lang);
696   g_free (lang);
697   
698   return result;
699 }
700
701 void
702 gtk_main (void)
703 {
704   GList *tmp_list;
705   GList *functions;
706   GtkInitFunction *init;
707   GMainLoop *loop;
708
709   gtk_main_loop_level++;
710   
711   loop = g_main_new (TRUE);
712   main_loops = g_slist_prepend (main_loops, loop);
713
714   tmp_list = functions = init_functions;
715   init_functions = NULL;
716   
717   while (tmp_list)
718     {
719       init = tmp_list->data;
720       tmp_list = tmp_list->next;
721       
722       (* init->function) (init->data);
723       g_free (init);
724     }
725   g_list_free (functions);
726
727   if (g_main_is_running (main_loops->data))
728     {
729       GDK_THREADS_LEAVE ();
730       g_main_run (loop);
731       GDK_THREADS_ENTER ();
732       gdk_flush ();
733     }
734
735   if (quit_functions)
736     {
737       GList *reinvoke_list = NULL;
738       GtkQuitFunction *quitf;
739
740       while (quit_functions)
741         {
742           quitf = quit_functions->data;
743
744           tmp_list = quit_functions;
745           quit_functions = g_list_remove_link (quit_functions, quit_functions);
746           g_list_free_1 (tmp_list);
747
748           if ((quitf->main_level && quitf->main_level != gtk_main_loop_level) ||
749               gtk_quit_invoke_function (quitf))
750             {
751               reinvoke_list = g_list_prepend (reinvoke_list, quitf);
752             }
753           else
754             {
755               gtk_quit_destroy (quitf);
756             }
757         }
758       if (reinvoke_list)
759         {
760           GList *work;
761           
762           work = g_list_last (reinvoke_list);
763           if (quit_functions)
764             quit_functions->prev = work;
765           work->next = quit_functions;
766           quit_functions = work;
767         }
768
769       gdk_flush ();
770     }
771               
772   main_loops = g_slist_remove (main_loops, loop);
773
774   g_main_destroy (loop);
775
776   gtk_main_loop_level--;
777 }
778
779 guint
780 gtk_main_level (void)
781 {
782   return gtk_main_loop_level;
783 }
784
785 void
786 gtk_main_quit (void)
787 {
788   g_return_if_fail (main_loops != NULL);
789
790   g_main_quit (main_loops->data);
791 }
792
793 gint
794 gtk_events_pending (void)
795 {
796   gboolean result;
797   
798   GDK_THREADS_LEAVE ();  
799   result = g_main_pending ();
800   GDK_THREADS_ENTER ();
801
802   return result;
803 }
804
805 gboolean
806 gtk_main_iteration (void)
807 {
808   GDK_THREADS_LEAVE ();
809   g_main_iteration (TRUE);
810   GDK_THREADS_ENTER ();
811
812   if (main_loops)
813     return !g_main_is_running (main_loops->data);
814   else
815     return TRUE;
816 }
817
818 gboolean
819 gtk_main_iteration_do (gboolean blocking)
820 {
821   GDK_THREADS_LEAVE ();
822   g_main_iteration (blocking);
823   GDK_THREADS_ENTER ();
824
825   if (main_loops)
826     return !g_main_is_running (main_loops->data);
827   else
828     return TRUE;
829 }
830
831 void 
832 gtk_main_do_event (GdkEvent *event)
833 {
834   GtkWidget *event_widget;
835   GtkWidget *grab_widget;
836   GtkWindowGroup *window_group;
837   GdkEvent *next_event;
838   GList *tmp_list;
839
840   /* If there are any events pending then get the next one.
841    */
842   next_event = gdk_event_peek ();
843   
844   /* Try to compress enter/leave notify events. These event
845    *  pairs occur when the mouse is dragged quickly across
846    *  a window with many buttons (or through a menu). Instead
847    *  of highlighting and de-highlighting each widget that
848    *  is crossed it is better to simply de-highlight the widget
849    *  which contained the mouse initially and highlight the
850    *  widget which ends up containing the mouse.
851    */
852   if (next_event)
853     if (((event->type == GDK_ENTER_NOTIFY) ||
854          (event->type == GDK_LEAVE_NOTIFY)) &&
855         ((next_event->type == GDK_ENTER_NOTIFY) ||
856          (next_event->type == GDK_LEAVE_NOTIFY)) &&
857         (next_event->type != event->type) &&
858         (next_event->any.window == event->any.window))
859       {
860         /* Throw both the peeked copy and the queued copy away 
861          */
862         gdk_event_free (next_event);
863         next_event = gdk_event_get ();
864         gdk_event_free (next_event);
865         
866         return;
867       }
868
869   if (next_event)
870     gdk_event_free (next_event);
871
872   /* Find the widget which got the event. We store the widget
873    *  in the user_data field of GdkWindow's.
874    *  Ignore the event if we don't have a widget for it, except
875    *  for GDK_PROPERTY_NOTIFY events which are handled specialy.
876    *  Though this happens rarely, bogus events can occour
877    *  for e.g. destroyed GdkWindows. 
878    */
879   event_widget = gtk_get_event_widget (event);
880   if (!event_widget)
881     {
882       /* To handle selection INCR transactions, we select
883        * PropertyNotify events on the requestor window and create
884        * a corresponding (fake) GdkWindow so that events get
885        * here. There won't be a widget though, so we have to handle
886            * them specially
887            */
888       if (event->type == GDK_PROPERTY_NOTIFY)
889         gtk_selection_incr_event (event->any.window,
890                                   &event->property);
891       else if (event->type == GDK_SETTING)
892         _gtk_settings_handle_event (&event->setting);
893
894       return;
895     }
896   
897   /* Push the event onto a stack of current events for
898    * gtk_current_event_get().
899    */
900   current_events = g_list_prepend (current_events, event);
901
902   window_group = gtk_main_get_window_group (event_widget);
903   
904   /* If there is a grab in effect...
905    */
906   if (window_group->grabs)
907     {
908       grab_widget = window_group->grabs->data;
909       
910       /* If the grab widget is an ancestor of the event widget
911        *  then we send the event to the original event widget.
912        *  This is the key to implementing modality.
913        */
914       if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
915           gtk_widget_is_ancestor (event_widget, grab_widget))
916         grab_widget = event_widget;
917     }
918   else
919     {
920       grab_widget = event_widget;
921     }
922
923   /* Not all events get sent to the grabbing widget.
924    * The delete, destroy, expose, focus change and resize
925    *  events still get sent to the event widget because
926    *  1) these events have no meaning for the grabbing widget
927    *  and 2) redirecting these events to the grabbing widget
928    *  could cause the display to be messed up.
929    * 
930    * Drag events are also not redirected, since it isn't
931    *  clear what the semantics of that would be.
932    */
933   switch (event->type)
934     {
935     case GDK_NOTHING:
936       break;
937       
938     case GDK_DELETE:
939       gtk_widget_ref (event_widget);
940       if ((!window_group->grabs || gtk_widget_get_toplevel (window_group->grabs->data) == event_widget) &&
941           !gtk_widget_event (event_widget, event))
942         gtk_widget_destroy (event_widget);
943       gtk_widget_unref (event_widget);
944       break;
945       
946     case GDK_DESTROY:
947       /* Unexpected GDK_DESTROY from the outside, ignore for
948        * child windows, handle like a GDK_DELETE for toplevels
949        */
950       if (!event_widget->parent)
951         {
952           gtk_widget_ref (event_widget);
953           if (!gtk_widget_event (event_widget, event) &&
954               GTK_WIDGET_REALIZED (event_widget))
955             gtk_widget_destroy (event_widget);
956           gtk_widget_unref (event_widget);
957         }
958       break;
959       
960     case GDK_EXPOSE:
961       if (event->any.window && GTK_WIDGET_DOUBLE_BUFFERED (event_widget))
962         {
963           gdk_window_begin_paint_region (event->any.window, event->expose.region);
964           gtk_widget_send_expose (event_widget, event);
965           gdk_window_end_paint (event->any.window);
966         }
967       else
968         gtk_widget_send_expose (event_widget, event);
969       break;
970
971     case GDK_PROPERTY_NOTIFY:
972     case GDK_NO_EXPOSE:
973     case GDK_FOCUS_CHANGE:
974     case GDK_CONFIGURE:
975     case GDK_MAP:
976     case GDK_UNMAP:
977     case GDK_SELECTION_CLEAR:
978     case GDK_SELECTION_REQUEST:
979     case GDK_SELECTION_NOTIFY:
980     case GDK_CLIENT_EVENT:
981     case GDK_VISIBILITY_NOTIFY:
982     case GDK_WINDOW_STATE:
983       gtk_widget_event (event_widget, event);
984       break;
985
986     case GDK_SCROLL:
987     case GDK_BUTTON_PRESS:
988     case GDK_2BUTTON_PRESS:
989     case GDK_3BUTTON_PRESS:
990       gtk_propagate_event (grab_widget, event);
991       break;
992
993     case GDK_KEY_PRESS:
994     case GDK_KEY_RELEASE:
995       if (key_snoopers)
996         {
997           if (gtk_invoke_key_snoopers (grab_widget, event))
998             break;
999         }
1000       /* else fall through */
1001     case GDK_MOTION_NOTIFY:
1002     case GDK_BUTTON_RELEASE:
1003     case GDK_PROXIMITY_IN:
1004     case GDK_PROXIMITY_OUT:
1005       gtk_propagate_event (grab_widget, event);
1006       break;
1007       
1008     case GDK_ENTER_NOTIFY:
1009       if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
1010         {
1011           gtk_widget_event (grab_widget, event);
1012           if (event_widget == grab_widget)
1013             GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
1014         }
1015       break;
1016       
1017     case GDK_LEAVE_NOTIFY:
1018       if (GTK_WIDGET_LEAVE_PENDING (event_widget))
1019         {
1020           GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
1021           gtk_widget_event (event_widget, event);
1022         }
1023       else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
1024         gtk_widget_event (grab_widget, event);
1025       break;
1026       
1027     case GDK_DRAG_STATUS:
1028     case GDK_DROP_FINISHED:
1029       _gtk_drag_source_handle_event (event_widget, event);
1030       break;
1031     case GDK_DRAG_ENTER:
1032     case GDK_DRAG_LEAVE:
1033     case GDK_DRAG_MOTION:
1034     case GDK_DROP_START:
1035       _gtk_drag_dest_handle_event (event_widget, event);
1036       break;
1037     default:
1038       g_assert_not_reached ();
1039       break;
1040     }
1041   
1042   tmp_list = current_events;
1043   current_events = g_list_remove_link (current_events, tmp_list);
1044   g_list_free_1 (tmp_list);
1045 }
1046
1047 gboolean
1048 gtk_true (void)
1049 {
1050   return TRUE;
1051 }
1052
1053 gboolean
1054 gtk_false (void)
1055 {
1056   return FALSE;
1057 }
1058
1059 static GtkWindowGroup *
1060 gtk_main_get_window_group (GtkWidget   *widget)
1061 {
1062   GtkWidget *toplevel = NULL;
1063
1064   if (widget)
1065     toplevel = gtk_widget_get_toplevel (widget);
1066
1067   if (toplevel && GTK_IS_WINDOW (toplevel))
1068     return _gtk_window_get_group (GTK_WINDOW (toplevel));
1069   else
1070     return _gtk_window_get_group (NULL);
1071 }
1072
1073 typedef struct
1074 {
1075   gboolean was_grabbed;
1076   GtkWidget *grab_widget;
1077 } GrabNotifyInfo;
1078
1079 static void
1080 gtk_grab_notify_foreach (GtkWidget *child,
1081                          gpointer   data)
1082                         
1083 {
1084   GrabNotifyInfo *info = data;
1085
1086   if (child != info->grab_widget)
1087     {
1088       g_object_ref (G_OBJECT (child));
1089
1090       gtk_signal_emit_by_name (GTK_OBJECT (child), "grab_notify", info->was_grabbed);
1091
1092       if (GTK_IS_CONTAINER (child))
1093        gtk_container_foreach (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
1094       
1095       g_object_unref (G_OBJECT (child));
1096     }
1097 }
1098
1099 static void
1100 gtk_grab_notify (GtkWindowGroup *group,
1101                  GtkWidget      *grab_widget,
1102                  gboolean        was_grabbed)
1103 {
1104   GList *toplevels;
1105   GrabNotifyInfo info;
1106
1107   info.grab_widget = grab_widget;
1108   info.was_grabbed = was_grabbed;
1109
1110   g_object_ref (group);
1111   g_object_ref (grab_widget);
1112
1113   toplevels = gtk_window_list_toplevels ();
1114   g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
1115                             
1116   while (toplevels)
1117     {
1118       GtkWindow *toplevel = toplevels->data;
1119       toplevels = g_list_delete_link (toplevels, toplevels);
1120
1121       if (group == toplevel->group)
1122         gtk_container_foreach (GTK_CONTAINER (toplevel), gtk_grab_notify_foreach, &info);
1123       g_object_unref (toplevel);
1124     }
1125
1126   g_object_unref (group);
1127   g_object_unref (grab_widget);
1128 }
1129
1130 void
1131 gtk_grab_add (GtkWidget *widget)
1132 {
1133   GtkWindowGroup *group;
1134   gboolean was_grabbed;
1135   
1136   g_return_if_fail (widget != NULL);
1137   
1138   if (!GTK_WIDGET_HAS_GRAB (widget) && GTK_WIDGET_IS_SENSITIVE (widget))
1139     {
1140       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
1141       
1142       group = gtk_main_get_window_group (widget);
1143
1144       was_grabbed = (group->grabs != NULL);
1145       
1146       gtk_widget_ref (widget);
1147       group->grabs = g_slist_prepend (group->grabs, widget);
1148
1149       if (!was_grabbed)
1150         gtk_grab_notify (group, widget, FALSE);
1151     }
1152 }
1153
1154 GtkWidget*
1155 gtk_grab_get_current (void)
1156 {
1157   GtkWindowGroup *group;
1158
1159   group = gtk_main_get_window_group (NULL);
1160
1161   if (group->grabs)
1162     return GTK_WIDGET (group->grabs->data);
1163   return NULL;
1164 }
1165
1166 void
1167 gtk_grab_remove (GtkWidget *widget)
1168 {
1169   GtkWindowGroup *group;
1170   
1171   g_return_if_fail (widget != NULL);
1172   
1173   if (GTK_WIDGET_HAS_GRAB (widget))
1174     {
1175       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
1176
1177       group = gtk_main_get_window_group (widget);
1178       group->grabs = g_slist_remove (group->grabs, widget);
1179       
1180       gtk_widget_unref (widget);
1181
1182       if (!group->grabs)
1183         gtk_grab_notify (group, widget, TRUE);
1184     }
1185 }
1186
1187 void
1188 gtk_init_add (GtkFunction function,
1189               gpointer    data)
1190 {
1191   GtkInitFunction *init;
1192   
1193   init = g_new (GtkInitFunction, 1);
1194   init->function = function;
1195   init->data = data;
1196   
1197   init_functions = g_list_prepend (init_functions, init);
1198 }
1199
1200 guint
1201 gtk_key_snooper_install (GtkKeySnoopFunc snooper,
1202                          gpointer        func_data)
1203 {
1204   GtkKeySnooperData *data;
1205   static guint snooper_id = 1;
1206
1207   g_return_val_if_fail (snooper != NULL, 0);
1208
1209   data = g_new (GtkKeySnooperData, 1);
1210   data->func = snooper;
1211   data->func_data = func_data;
1212   data->id = snooper_id++;
1213   key_snoopers = g_slist_prepend (key_snoopers, data);
1214
1215   return data->id;
1216 }
1217
1218 void
1219 gtk_key_snooper_remove (guint            snooper_id)
1220 {
1221   GtkKeySnooperData *data = NULL;
1222   GSList *slist;
1223
1224   slist = key_snoopers;
1225   while (slist)
1226     {
1227       data = slist->data;
1228       if (data->id == snooper_id)
1229         break;
1230
1231       slist = slist->next;
1232       data = NULL;
1233     }
1234   if (data)
1235     key_snoopers = g_slist_remove (key_snoopers, data);
1236 }
1237
1238 static gint
1239 gtk_invoke_key_snoopers (GtkWidget *grab_widget,
1240                          GdkEvent  *event)
1241 {
1242   GSList *slist;
1243   gint return_val = FALSE;
1244
1245   slist = key_snoopers;
1246   while (slist && !return_val)
1247     {
1248       GtkKeySnooperData *data;
1249
1250       data = slist->data;
1251       slist = slist->next;
1252       return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data);
1253     }
1254
1255   return return_val;
1256 }
1257
1258 guint
1259 gtk_quit_add_full (guint                main_level,
1260                    GtkFunction          function,
1261                    GtkCallbackMarshal   marshal,
1262                    gpointer             data,
1263                    GtkDestroyNotify     destroy)
1264 {
1265   static guint quit_id = 1;
1266   GtkQuitFunction *quitf;
1267   
1268   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
1269
1270   if (!quit_mem_chunk)
1271     quit_mem_chunk = g_mem_chunk_new ("quit mem chunk", sizeof (GtkQuitFunction),
1272                                       512, G_ALLOC_AND_FREE);
1273   
1274   quitf = g_chunk_new (GtkQuitFunction, quit_mem_chunk);
1275   
1276   quitf->id = quit_id++;
1277   quitf->main_level = main_level;
1278   quitf->function = function;
1279   quitf->marshal = marshal;
1280   quitf->data = data;
1281   quitf->destroy = destroy;
1282
1283   quit_functions = g_list_prepend (quit_functions, quitf);
1284   
1285   return quitf->id;
1286 }
1287
1288 static void
1289 gtk_quit_destroy (GtkQuitFunction *quitf)
1290 {
1291   if (quitf->destroy)
1292     quitf->destroy (quitf->data);
1293   g_mem_chunk_free (quit_mem_chunk, quitf);
1294 }
1295
1296 static gint
1297 gtk_quit_destructor (GtkObject **object_p)
1298 {
1299   if (*object_p)
1300     gtk_object_destroy (*object_p);
1301   g_free (object_p);
1302
1303   return FALSE;
1304 }
1305
1306 void
1307 gtk_quit_add_destroy (guint              main_level,
1308                       GtkObject         *object)
1309 {
1310   GtkObject **object_p;
1311
1312   g_return_if_fail (main_level > 0);
1313   g_return_if_fail (GTK_IS_OBJECT (object));
1314
1315   object_p = g_new (GtkObject*, 1);
1316   *object_p = object;
1317   gtk_signal_connect (object,
1318                       "destroy",
1319                       GTK_SIGNAL_FUNC (gtk_widget_destroyed),
1320                       object_p);
1321   gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p);
1322 }
1323
1324 guint
1325 gtk_quit_add (guint       main_level,
1326               GtkFunction function,
1327               gpointer    data)
1328 {
1329   return gtk_quit_add_full (main_level, function, NULL, data, NULL);
1330 }
1331
1332 void
1333 gtk_quit_remove (guint id)
1334 {
1335   GtkQuitFunction *quitf;
1336   GList *tmp_list;
1337   
1338   tmp_list = quit_functions;
1339   while (tmp_list)
1340     {
1341       quitf = tmp_list->data;
1342       
1343       if (quitf->id == id)
1344         {
1345           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1346           g_list_free (tmp_list);
1347           gtk_quit_destroy (quitf);
1348           
1349           return;
1350         }
1351       
1352       tmp_list = tmp_list->next;
1353     }
1354 }
1355
1356 void
1357 gtk_quit_remove_by_data (gpointer data)
1358 {
1359   GtkQuitFunction *quitf;
1360   GList *tmp_list;
1361   
1362   tmp_list = quit_functions;
1363   while (tmp_list)
1364     {
1365       quitf = tmp_list->data;
1366       
1367       if (quitf->data == data)
1368         {
1369           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1370           g_list_free (tmp_list);
1371           gtk_quit_destroy (quitf);
1372
1373           return;
1374         }
1375       
1376       tmp_list = tmp_list->next;
1377     }
1378 }
1379
1380 guint
1381 gtk_timeout_add_full (guint32            interval,
1382                       GtkFunction        function,
1383                       GtkCallbackMarshal marshal,
1384                       gpointer           data,
1385                       GtkDestroyNotify   destroy)
1386 {
1387   if (marshal)
1388     {
1389       GtkClosure *closure;
1390
1391       closure = g_new (GtkClosure, 1);
1392       closure->marshal = marshal;
1393       closure->data = data;
1394       closure->destroy = destroy;
1395
1396       return g_timeout_add_full (0, interval, 
1397                                  gtk_invoke_idle_timeout,
1398                                  closure,
1399                                  gtk_destroy_closure);
1400     }
1401   else
1402     return g_timeout_add_full (0, interval, function, data, destroy);
1403 }
1404
1405 guint
1406 gtk_timeout_add (guint32     interval,
1407                  GtkFunction function,
1408                  gpointer    data)
1409 {
1410   return g_timeout_add_full (0, interval, function, data, NULL);
1411 }
1412
1413 void
1414 gtk_timeout_remove (guint tag)
1415 {
1416   g_source_remove (tag);
1417 }
1418
1419 guint
1420 gtk_idle_add_full (gint                 priority,
1421                    GtkFunction          function,
1422                    GtkCallbackMarshal   marshal,
1423                    gpointer             data,
1424                    GtkDestroyNotify     destroy)
1425 {
1426   if (marshal)
1427     {
1428       GtkClosure *closure;
1429
1430       closure = g_new (GtkClosure, 1);
1431       closure->marshal = marshal;
1432       closure->data = data;
1433       closure->destroy = destroy;
1434
1435       return g_idle_add_full (priority,
1436                               gtk_invoke_idle_timeout,
1437                               closure,
1438                               gtk_destroy_closure);
1439     }
1440   else
1441     return g_idle_add_full (priority, function, data, destroy);
1442 }
1443
1444 guint
1445 gtk_idle_add (GtkFunction function,
1446               gpointer    data)
1447 {
1448   return g_idle_add_full (GTK_PRIORITY_DEFAULT, function, data, NULL);
1449 }
1450
1451 guint       
1452 gtk_idle_add_priority (gint        priority,
1453                        GtkFunction function,
1454                        gpointer    data)
1455 {
1456   return g_idle_add_full (priority, function, data, NULL);
1457 }
1458
1459 void
1460 gtk_idle_remove (guint tag)
1461 {
1462   g_source_remove (tag);
1463 }
1464
1465 void
1466 gtk_idle_remove_by_data (gpointer data)
1467 {
1468   if (!g_idle_remove_by_data (data))
1469     g_warning ("gtk_idle_remove_by_data(%p): no such idle", data);
1470 }
1471
1472 guint
1473 gtk_input_add_full (gint                source,
1474                     GdkInputCondition   condition,
1475                     GdkInputFunction    function,
1476                     GtkCallbackMarshal  marshal,
1477                     gpointer            data,
1478                     GtkDestroyNotify    destroy)
1479 {
1480   if (marshal)
1481     {
1482       GtkClosure *closure;
1483
1484       closure = g_new (GtkClosure, 1);
1485       closure->marshal = marshal;
1486       closure->data = data;
1487       closure->destroy = destroy;
1488
1489       return gdk_input_add_full (source,
1490                                  condition,
1491                                  (GdkInputFunction) gtk_invoke_input,
1492                                  closure,
1493                                  (GdkDestroyNotify) gtk_destroy_closure);
1494     }
1495   else
1496     return gdk_input_add_full (source, condition, function, data, destroy);
1497 }
1498
1499 void
1500 gtk_input_remove (guint tag)
1501 {
1502   g_source_remove (tag);
1503 }
1504
1505 static void
1506 gtk_destroy_closure (gpointer data)
1507 {
1508   GtkClosure *closure = data;
1509
1510   if (closure->destroy)
1511     (closure->destroy) (closure->data);
1512   g_free (closure);
1513 }
1514
1515 static gboolean
1516 gtk_invoke_idle_timeout (gpointer data)
1517 {
1518   GtkClosure *closure = data;
1519
1520   GtkArg args[1];
1521   gint ret_val = FALSE;
1522   args[0].name = NULL;
1523   args[0].type = GTK_TYPE_BOOL;
1524   args[0].d.pointer_data = &ret_val;
1525   closure->marshal (NULL, closure->data,  0, args);
1526   return ret_val;
1527 }
1528
1529 static void
1530 gtk_invoke_input (gpointer          data,
1531                   gint              source,
1532                   GdkInputCondition condition)
1533 {
1534   GtkClosure *closure = data;
1535
1536   GtkArg args[3];
1537   args[0].type = GTK_TYPE_INT;
1538   args[0].name = NULL;
1539   GTK_VALUE_INT (args[0]) = source;
1540   args[1].type = GDK_TYPE_INPUT_CONDITION;
1541   args[1].name = NULL;
1542   GTK_VALUE_FLAGS (args[1]) = condition;
1543   args[2].type = GTK_TYPE_NONE;
1544   args[2].name = NULL;
1545
1546   closure->marshal (NULL, closure->data, 2, args);
1547 }
1548
1549 /**
1550  * gtk_get_current_event:
1551  * 
1552  * Obtains a copy of the event currently being processed by GTK+.  For
1553  * example, if you get a "clicked" signal from #GtkButton, the current
1554  * event will be the #GdkEventButton that triggered the "clicked"
1555  * signal. The returned event must be freed with gdk_event_free().
1556  * If there is no current event, the function returns %NULL.
1557  * 
1558  * Return value: a copy of the current event, or %NULL if no current event.
1559  **/
1560 GdkEvent*
1561 gtk_get_current_event (void)
1562 {
1563   if (current_events)
1564     return gdk_event_copy (current_events->data);
1565   else
1566     return NULL;
1567 }
1568
1569 /**
1570  * gtk_get_current_event_time:
1571  * 
1572  * If there is a current event and it has a timestamp, return that
1573  * timestamp, otherwise return %GDK_CURRENT_TIME.
1574  * 
1575  * Return value: the timestamp from the current event, or %GDK_CURRENT_TIME.
1576  **/
1577 guint32
1578 gtk_get_current_event_time (void)
1579 {
1580   if (current_events)
1581     return gdk_event_get_time (current_events->data);
1582   else
1583     return GDK_CURRENT_TIME;
1584 }
1585
1586 /**
1587  * gtk_get_current_event_state:
1588  * @state: a location to store the state of the current event
1589  * 
1590  * If there is a current event and it has a state field, place
1591  * that state field in @state and return %TRUE, otherwise return
1592  * %FALSE.
1593  * 
1594  * Return value: %TRUE if there was a current event and it had a state field
1595  **/
1596 gboolean
1597 gtk_get_current_event_state (GdkModifierType *state)
1598 {
1599   g_return_val_if_fail (state != NULL, FALSE);
1600   
1601   if (current_events)
1602     return gdk_event_get_state (current_events->data, state);
1603   else
1604     {
1605       *state = 0;
1606       return FALSE;
1607     }
1608 }
1609
1610 /**
1611  * gtk_get_event_widget:
1612  * @event: a #GdkEvent
1613  *
1614  * If @event is %NULL or the event was not associated with any widget,
1615  * returns %NULL, otherwise returns the widget that received the event
1616  * originally.
1617  * 
1618  * Return value: the widget that originally received @event, or %NULL
1619  **/
1620 GtkWidget*
1621 gtk_get_event_widget (GdkEvent *event)
1622 {
1623   GtkWidget *widget;
1624
1625   widget = NULL;
1626   if (event && event->any.window)
1627     gdk_window_get_user_data (event->any.window, (void**) &widget);
1628   
1629   return widget;
1630 }
1631
1632 static void
1633 gtk_exit_func (void)
1634 {
1635   if (gtk_initialized)
1636     {
1637       gtk_initialized = FALSE;
1638     }
1639 }
1640
1641
1642 static gint
1643 gtk_quit_invoke_function (GtkQuitFunction *quitf)
1644 {
1645   if (!quitf->marshal)
1646     return quitf->function (quitf->data);
1647   else
1648     {
1649       GtkArg args[1];
1650       gint ret_val = FALSE;
1651
1652       args[0].name = NULL;
1653       args[0].type = GTK_TYPE_BOOL;
1654       args[0].d.pointer_data = &ret_val;
1655       ((GtkCallbackMarshal) quitf->marshal) (NULL,
1656                                              quitf->data,
1657                                              0, args);
1658       return ret_val;
1659     }
1660 }
1661
1662 /**
1663  * gtk_propagate_event:
1664  * @widget: a #GtkWidget
1665  * @event: an event
1666  *
1667  * Sends an event to a widget, propagating the event to parent widgets
1668  * if the event remains unhandled. Events received by GTK+ from GDK
1669  * normally begin in gtk_main_do_event(). Depending on the type of
1670  * event, existence of modal dialogs, grabs, etc., the event may be
1671  * propagated; if so, this function is used. gtk_propagate_event()
1672  * calls gtk_widget_event() on each widget it decides to send the
1673  * event to.  So gtk_widget_event() is the lowest-level function; it
1674  * simply emits the "event" and possibly an event-specific signal on a
1675  * widget.  gtk_propagate_event() is a bit higher-level, and
1676  * gtk_main_do_event() is the highest level.
1677  *
1678  * All that said, you most likely don't want to use any of these
1679  * functions; synthesizing events is rarely needed. Consider asking on
1680  * the mailing list for better ways to achieve your goals. For
1681  * example, use gdk_window_invalidate_rect() or
1682  * gtk_widget_queue_draw() instead of making up expose events.
1683  * 
1684  **/
1685 void
1686 gtk_propagate_event (GtkWidget *widget,
1687                      GdkEvent  *event)
1688 {
1689   gint handled_event;
1690   
1691   g_return_if_fail (GTK_IS_WIDGET (widget));
1692   g_return_if_fail (event != NULL);
1693   
1694   handled_event = FALSE;
1695
1696   gtk_widget_ref (widget);
1697       
1698   if ((event->type == GDK_KEY_PRESS) ||
1699       (event->type == GDK_KEY_RELEASE))
1700     {
1701       /* Only send key events within Window widgets to the Window
1702        *  The Window widget will in turn pass the
1703        *  key event on to the currently focused widget
1704        *  for that window.
1705        */
1706       GtkWidget *window;
1707
1708       window = gtk_widget_get_toplevel (widget);
1709       if (window && GTK_IS_WINDOW (window))
1710         {
1711           /* If there is a grab within the window, give the grab widget
1712            * a first crack at the key event
1713            */
1714           if (widget != window && GTK_WIDGET_HAS_GRAB (widget))
1715             handled_event = gtk_widget_event (widget, event);
1716           
1717           if (!handled_event)
1718             {
1719               window = gtk_widget_get_toplevel (widget);
1720               if (window && GTK_IS_WINDOW (window))
1721                 {
1722                   if (GTK_WIDGET_IS_SENSITIVE (window))
1723                     gtk_widget_event (window, event);
1724                 }
1725             }
1726                   
1727           handled_event = TRUE; /* don't send to widget */
1728         }
1729     }
1730   
1731   /* Other events get propagated up the widget tree
1732    *  so that parents can see the button and motion
1733    *  events of the children.
1734    */
1735   if (!handled_event)
1736     {
1737       while (TRUE)
1738         {
1739           GtkWidget *tmp;
1740           
1741           handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
1742           tmp = widget->parent;
1743           gtk_widget_unref (widget);
1744
1745           widget = tmp;
1746           
1747           if (!handled_event && widget)
1748             gtk_widget_ref (widget);
1749           else
1750             break;
1751         }
1752     }
1753   else
1754     gtk_widget_unref (widget);
1755 }
1756
1757 #if 0
1758 static void
1759 gtk_error (gchar *str)
1760 {
1761   gtk_print (str);
1762 }
1763
1764 static void
1765 gtk_warning (gchar *str)
1766 {
1767   gtk_print (str);
1768 }
1769
1770 static void
1771 gtk_message (gchar *str)
1772 {
1773   gtk_print (str);
1774 }
1775
1776 static void
1777 gtk_print (gchar *str)
1778 {
1779   static GtkWidget *window = NULL;
1780   static GtkWidget *text;
1781   static int level = 0;
1782   GtkWidget *box1;
1783   GtkWidget *box2;
1784   GtkWidget *table;
1785   GtkWidget *hscrollbar;
1786   GtkWidget *vscrollbar;
1787   GtkWidget *separator;
1788   GtkWidget *button;
1789   
1790   if (level > 0)
1791     {
1792       fputs (str, stdout);
1793       fflush (stdout);
1794       return;
1795     }
1796   
1797   if (!window)
1798     {
1799       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1800       
1801       gtk_signal_connect (GTK_OBJECT (window), "destroy",
1802                           (GtkSignalFunc) gtk_widget_destroyed,
1803                           &window);
1804       
1805       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1806       
1807       box1 = gtk_vbox_new (FALSE, 0);
1808       gtk_container_add (GTK_CONTAINER (window), box1);
1809       gtk_widget_show (box1);
1810       
1811       
1812       box2 = gtk_vbox_new (FALSE, 10);
1813       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1814       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1815       gtk_widget_show (box2);
1816       
1817       
1818       table = gtk_table_new (2, 2, FALSE);
1819       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1820       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1821       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1822       gtk_widget_show (table);
1823       
1824       text = gtk_text_new (NULL, NULL);
1825       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1826       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1827       gtk_widget_show (text);
1828       gtk_widget_realize (text);
1829       
1830       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1831       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1832                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1833       gtk_widget_show (hscrollbar);
1834       
1835       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1836       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1837                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1838       gtk_widget_show (vscrollbar);
1839       
1840       separator = gtk_hseparator_new ();
1841       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1842       gtk_widget_show (separator);
1843       
1844       
1845       box2 = gtk_vbox_new (FALSE, 10);
1846       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1847       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1848       gtk_widget_show (box2);
1849       
1850       
1851       button = gtk_button_new_with_label ("close");
1852       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1853                                  (GtkSignalFunc) gtk_widget_hide,
1854                                  GTK_OBJECT (window));
1855       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1856       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1857       gtk_widget_grab_default (button);
1858       gtk_widget_show (button);
1859     }
1860   
1861   level += 1;
1862   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1863   level -= 1;
1864   
1865   if (!GTK_WIDGET_VISIBLE (window))
1866     gtk_widget_show (window);
1867 }
1868 #endif
1869
1870 gboolean
1871 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
1872                                   GValue                *return_accu,
1873                                   const GValue          *handler_return,
1874                                   gpointer               dummy)
1875 {
1876   gboolean continue_emission;
1877   gboolean signal_handled;
1878   
1879   signal_handled = g_value_get_boolean (handler_return);
1880   g_value_set_boolean (return_accu, signal_handled);
1881   continue_emission = !signal_handled;
1882   
1883   return continue_emission;
1884 }