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