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