]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmain.c
Robustify tracking of pointer grab window.
[~andy/gtk] / gtk / gtkmain.c
index 4b934b6b84e138c31b0bc1bad2521a4f3cc90152..c38607a39cc66b9c588f0da5291268224d4f9b3d 100644 (file)
@@ -2,58 +2,63 @@
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
  * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
+ * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
- * Library General Public License for more details.
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU Library General Public
+ * You should have received a copy of the GNU Lesser General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
 
 /*
- * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
+ * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
  * file for a list of people on the GTK+ Team.  See the ChangeLog
  * files for a list of changes.  These files are distributed with
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
-#include "gdkx.h"              /* For GDK_WINDOWING */
+#include "gdkconfig.h"
 
-#if GDK_WINDOWING == GDK_WINDOWING_X11
-#include <X11/Xlocale.h>       /* so we get the right setlocale */
-#else
 #include <locale.h>
+
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+#include <libintl.h>
 #endif
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <gmodule.h>
-#include "gtkbutton.h"
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#include <sys/types.h>         /* For uid_t, gid_t */
+#endif
+#ifdef G_OS_WIN32
+#define STRICT
+#include <windows.h>
+#undef STRICT
+#endif
+
+#include <pango/pango-utils.h> /* For pango_split_file_list */
+
+#include "gtkaccelmap.h"
 #include "gtkdnd.h"
-#include "gtkcompat.h"
-#include "gtkhscrollbar.h"
-#include "gtkhseparator.h"
+#include "gtkversion.h"
 #include "gtkmain.h"
-#include "gtkpreview.h"
 #include "gtkrc.h"
-#include "gtkscrolledwindow.h"
 #include "gtkselection.h"
+#include "gtksettings.h"
 #include "gtksignal.h"
-#include "gtktable.h"
-#include "gtktext.h"
-#include "gtkvbox.h"
-#include "gtkvscrollbar.h"
 #include "gtkwidget.h"
 #include "gtkwindow.h"
 #include "gtkprivate.h"
-#include "gdk/gdki18n.h"
 #include "config.h"
 #include "gtkdebug.h"
 #include "gtkintl.h"
@@ -95,7 +100,6 @@ struct _GtkKeySnooperData
   guint id;
 };
 
-static void  gtk_exit_func              (void);
 static gint  gtk_quit_invoke_function   (GtkQuitFunction    *quitf);
 static void  gtk_quit_destroy           (GtkQuitFunction    *quitf);
 static gint  gtk_invoke_key_snoopers    (GtkWidget          *grab_widget,
@@ -114,6 +118,8 @@ static void  gtk_message             (gchar              *str);
 static void  gtk_print                  (gchar              *str);
 #endif
 
+static GtkWindowGroup *gtk_main_get_window_group (GtkWidget   *widget);
+
 const guint gtk_major_version = GTK_MAJOR_VERSION;
 const guint gtk_minor_version = GTK_MINOR_VERSION;
 const guint gtk_micro_version = GTK_MICRO_VERSION;
@@ -126,9 +132,6 @@ static GList *current_events = NULL;
 
 static GSList *main_loops = NULL;      /* stack of currently executing main loops */
 
-static GSList *grabs = NULL;              /* A stack of unique grabs. The grabbing
-                                           *  widget is the first one on the list.
-                                           */
 static GList *init_functions = NULL;      /* A list of init functions.
                                            */
 static GList *quit_functions = NULL;      /* A list of quit functions.
@@ -148,11 +151,12 @@ guint gtk_debug_flags = 0;                   /* Global GTK debug flag */
 
 #ifdef G_ENABLE_DEBUG
 static const GDebugKey gtk_debug_keys[] = {
-  {"objects", GTK_DEBUG_OBJECTS},
   {"misc", GTK_DEBUG_MISC},
-  {"signals", GTK_DEBUG_SIGNALS},
-  {"dnd", GTK_DEBUG_DND},
-  {"plugsocket", GTK_DEBUG_PLUGSOCKET}
+  {"plugsocket", GTK_DEBUG_PLUGSOCKET},
+  {"text", GTK_DEBUG_TEXT},
+  {"tree", GTK_DEBUG_TREE},
+  {"updates", GTK_DEBUG_UPDATES},
+  {"keybindings", GTK_DEBUG_KEYBINDINGS}
 };
 
 static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
@@ -179,40 +183,371 @@ gtk_check_version (guint required_major,
   return NULL;
 }
 
-#ifdef __EMX__
-static gchar *add_dll_suffix(gchar *module_name)
+#undef gtk_init_check
+
+/* This checks to see if the process is running suid or sgid
+ * at the current time. If so, we don't allow GTK+ to be initialized.
+ * This is meant to be a mild check - we only error out if we
+ * can prove the programmer is doing something wrong, not if
+ * they could be doing something wrong. For this reason, we
+ * don't use issetugid() on BSD or prctl (PR_GET_DUMPABLE).
+ */
+static gboolean
+check_setugid (void)
 {
-    gchar *suffix = strrchr(module_name, '.');
-    
-    if (!suffix || stricmp(suffix, ".dll"))
+/* this isn't at all relevant on MS Windows and doesn't compile ... --hb */
+#ifndef G_OS_WIN32
+  uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
+  gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
+  
+#ifdef HAVE_GETRESUID
+  /* These aren't in the header files, so we prototype them here.
+   */
+  int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
+  int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
+
+  if (getresuid (&ruid, &euid, &suid) != 0 ||
+      getresgid (&rgid, &egid, &sgid) != 0)
+#endif /* HAVE_GETRESUID */
     {
-       gchar *old = module_name;
-         
-       module_name = g_strconcat (module_name, ".dll", NULL);
-       g_free (old);
+      suid = ruid = getuid ();
+      sgid = rgid = getgid ();
+      euid = geteuid ();
+      egid = getegid ();
+    }
+
+  if (ruid != euid || ruid != suid ||
+      rgid != egid || rgid != sgid)
+    {
+      g_warning ("This process is currently running setuid or setgid.\n"
+                "This is not a supported use of GTK+. You must create a helper\n"
+                "program instead. For further details, see:\n\n"
+                "    http://www.gtk.org/setuid.html\n\n"
+                "Refusing to initialize GTK+.");
+      exit (1);
     }
-    return (module_name);
-}
 #endif
+  return TRUE;
+}
+
+#ifdef G_OS_WIN32
+
+G_WIN32_DLLMAIN_FOR_DLL_NAME(static, dll_name)
+
+const gchar *
+_gtk_get_libdir (void)
+{
+  static char *gtk_libdir = NULL;
+  if (gtk_libdir == NULL)
+    gtk_libdir = g_win32_get_package_installation_subdirectory
+      (GETTEXT_PACKAGE, dll_name, "lib");
+
+  return gtk_libdir;
+}
+
+const gchar *
+_gtk_get_localedir (void)
+{
+  static char *gtk_localedir = NULL;
+  if (gtk_localedir == NULL)
+    gtk_localedir = g_win32_get_package_installation_subdirectory
+      (GETTEXT_PACKAGE, dll_name, "lib\\locale");
+
+  return gtk_localedir;
+}
+
+const gchar *
+_gtk_get_sysconfdir (void)
+{
+  static char *gtk_sysconfdir = NULL;
+  if (gtk_sysconfdir == NULL)
+    gtk_sysconfdir = g_win32_get_package_installation_subdirectory
+      (GETTEXT_PACKAGE, dll_name, "etc");
+
+  return gtk_sysconfdir;
+}
+
+const gchar *
+_gtk_get_data_prefix (void)
+{
+  static char *gtk_data_prefix = NULL;
+  if (gtk_data_prefix == NULL)
+    gtk_data_prefix = g_win32_get_package_installation_directory
+      (GETTEXT_PACKAGE, dll_name);
+
+  return gtk_data_prefix;
+}
+
+#endif /* G_OS_WIN32 */
+
+static gchar **
+get_module_path (void)
+{
+  const gchar *module_path_env;
+  const gchar *exe_prefix;
+  const gchar *home_dir;
+  gchar *home_gtk_dir = NULL;
+  gchar *module_path;
+  gchar *default_dir;
+  static gchar **result = NULL;
+
+  if (result)
+    return result;
+
+  home_dir = g_get_home_dir();
+  if (home_dir)
+    home_gtk_dir = g_build_filename (home_dir, ".gtk-2.0", NULL);
+
+  module_path_env = g_getenv ("GTK_PATH");
+  exe_prefix = g_getenv ("GTK_EXE_PREFIX");
+
+  if (exe_prefix)
+    default_dir = g_build_filename (exe_prefix, "lib", "gtk-2.0", NULL);
+  else
+    default_dir = g_build_filename (GTK_LIBDIR, "gtk-2.0", NULL);
+
+  if (module_path_env && home_gtk_dir)
+    module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S,
+                               module_path_env, home_gtk_dir, default_dir, NULL);
+  else if (module_path_env)
+    module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S,
+                               module_path_env, default_dir, NULL);
+  else if (home_gtk_dir)
+    module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S,
+                               home_gtk_dir, default_dir, NULL);
+  else
+    module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S,
+                               default_dir, NULL);
+
+  g_free (home_gtk_dir);
+  g_free (default_dir);
+
+  result = pango_split_file_list (module_path);
+  g_free (module_path);
+
+  return result;
+}
+
+/**
+ * _gtk_get_module_path:
+ * @type: the type of the module, for instance 'modules', 'engines', immodules'
+ * 
+ * Determines the search path for a particular type of module.
+ * 
+ * Return value: the search path for the module type. Free with g_strfreev().
+ **/
+gchar **
+_gtk_get_module_path (const gchar *type)
+{
+  gchar **paths = get_module_path();
+  gchar **path;
+  gchar **result;
+  gint count = 0;
+
+  for (path = paths; *path; path++)
+    count++;
+
+  result = g_new (gchar *, count * 4 + 1);
+
+  count = 0;
+  for (path = get_module_path (); *path; path++)
+    {
+      gint use_version, use_host;
+      
+      for (use_version = TRUE; use_version >= FALSE; use_version--)
+       for (use_host = TRUE; use_host >= FALSE; use_host--)
+         {
+           gchar *tmp_dir;
+           
+           if (use_version && use_host)
+             tmp_dir = g_build_filename (*path, GTK_BINARY_VERSION, GTK_HOST, type, NULL);
+           else if (use_version)
+             tmp_dir = g_build_filename (*path, GTK_BINARY_VERSION, type, NULL);
+           else if (use_host)
+             tmp_dir = g_build_filename (*path, GTK_HOST, type, NULL);
+           else
+             tmp_dir = g_build_filename (*path, type, NULL);
+
+           result[count++] = tmp_dir;
+         }
+    }
+
+  result[count++] = NULL;
+
+  return result;
+}
+
+/**
+ * _gtk_find_module:
+ * @name: the name of the module
+ * @type: the type of the module, for instance 'modules', 'engines', immodules'
+ * 
+ * Looks for a dynamically module named @name of type @type in the standard GTK+
+ *  module search path.
+ * 
+ * Return value: the pathname to the found module, or %NULL if it wasn't found.
+ *  Free with g_free().
+ **/
+gchar *
+_gtk_find_module (const gchar *name,
+                 const gchar *type)
+{
+  gchar **paths;
+  gchar **path;
+  gchar *module_name = NULL;
+
+  if (g_path_is_absolute (name))
+    return g_strdup (name);
+
+  paths = _gtk_get_module_path (type);
+  for (path = paths; *path; path++)
+    {
+      gchar *tmp_name = g_module_build_path (*path, name);
+           
+      if (g_file_test (tmp_name, G_FILE_TEST_EXISTS))
+       {
+         module_name = tmp_name;
+         goto found;
+       }
+      else
+       g_free(tmp_name);
+    }
+
+  g_strfreev (paths);
+
+ found:
+  return module_name;
+}
+
+static GModule *
+find_module (gchar      **module_path,
+            const gchar *name)
+{
+  GModule *module;
+  gchar *module_name;
+
+  module_name = _gtk_find_module (name, "modules");
+  if (!module_name)
+    {
+      /* As last resort, try loading without an absolute path (using system
+       * library path)
+       */
+      module_name = g_module_build_path (NULL, name);
+    }
+  
+  module = g_module_open (module_name, G_MODULE_BIND_LAZY);
+  g_free(module_name);
+
+  return module;
+}
+
+static GSList *
+load_module (GSList      *gtk_modules,
+            gchar      **module_path,
+            const gchar *name)
+{
+  GtkModuleInitFunc modinit_func = NULL;
+  GModule *module = NULL;
+  
+  if (g_module_supported ())
+    {
+      module = find_module (module_path, name);
+      if (module &&
+         g_module_symbol (module, "gtk_module_init", (gpointer *) &modinit_func) &&
+         modinit_func)
+       {
+         if (!g_slist_find (gtk_modules, modinit_func))
+           {
+             g_module_make_resident (module);
+             gtk_modules = g_slist_prepend (gtk_modules, modinit_func);
+           }
+         else
+           {
+             g_module_close (module);
+             module = NULL;
+           }
+       }
+    }
+  if (!modinit_func)
+    {
+      g_message ("Failed to load module \"%s\": %s",
+                module ? g_module_name (module) : name,
+                g_module_error ());
+      if (module)
+       g_module_close (module);
+    }
+  
+  return gtk_modules;
+}
+
+static GSList *
+load_modules (const char *module_str)
+{
+  gchar **module_path = get_module_path ();
+  gchar **module_names = pango_split_file_list (module_str);
+  GSList *gtk_modules = NULL;
+  gint i;
+  
+  for (i = 0; module_names[i]; i++)
+    gtk_modules = load_module (gtk_modules, module_path, module_names[i]);
+  
+  gtk_modules = g_slist_reverse (gtk_modules);
+  
+  g_strfreev (module_names);
+  g_strfreev (module_path);
+
+  return gtk_modules;
+}
+
+static gboolean do_setlocale = TRUE;
+
+/**
+ * gtk_disable_setlocale:
+ * 
+ * Prevents gtk_init() and gtk_init_check() from automatically
+ * calling <literal>setlocale (LC_ALL, "")</literal>. You would 
+ * want to use this function if you wanted to set the locale for 
+ * your program to something other than the user's locale, or if 
+ * you wanted to set different values for different locale categories.
+ *
+ * Most programs should not need to call this function.
+ **/
+void
+gtk_disable_setlocale (void)
+{
+  if (gtk_initialized)
+    g_warning ("gtk_disable_setlocale() must be called before gtk_init()");
+    
+  do_setlocale = FALSE;
+}
 
 gboolean
 gtk_init_check (int     *argc,
                char   ***argv)
 {
-  extern void gtk_object_post_arg_parsing_init (void);
+  GString *gtk_modules_string = NULL;
   GSList *gtk_modules = NULL;
   GSList *slist;
-  gchar *env_string = NULL;
+  const gchar *env_string;
 
   if (gtk_initialized)
     return TRUE;
 
+  if (!check_setugid ())
+    return FALSE;
+  
 #if    0
   g_set_error_handler (gtk_error);
   g_set_warning_handler (gtk_warning);
   g_set_message_handler (gtk_message);
   g_set_print_handler (gtk_print);
 #endif
+
+  if (do_setlocale)
+    {
+      if (!setlocale (LC_ALL, ""))
+       g_warning ("Locale not supported by C library.\n\tUsing the fallback 'C' locale.");
+    }
   
   /* Initialize "gdk". We pass along the 'argc' and 'argv'
    *  parameters as they contain information that GDK uses
@@ -223,7 +558,7 @@ gtk_init_check (int  *argc,
   gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
   
 #ifdef G_ENABLE_DEBUG
-  env_string = getenv ("GTK_DEBUG");
+  env_string = g_getenv ("GTK_DEBUG");
   if (env_string != NULL)
     {
       gtk_debug_flags = g_parse_debug_string (env_string,
@@ -233,26 +568,9 @@ gtk_init_check (int         *argc,
     }
 #endif /* G_ENABLE_DEBUG */
 
-  env_string = getenv ("GTK_MODULES");
+  env_string = g_getenv ("GTK_MODULES");
   if (env_string)
-    {
-      gchar **modules, **as;
-
-#ifndef __EMX__
-      modules = g_strsplit (env_string, G_SEARCHPATH_SEPARATOR_S, -1);
-#else
-      modules = g_strsplit (env_string, ";", -1);
-#endif
-      for (as = modules; *as; as++)
-       {
-         if (**as)
-           gtk_modules = g_slist_prepend (gtk_modules, *as);
-         else
-           g_free (*as);
-       }
-      g_free (modules);
-      env_string = NULL;
-    }
+    gtk_modules_string = g_string_new (env_string);
 
   if (argc && argv)
     {
@@ -267,7 +585,7 @@ gtk_init_check (int  *argc,
              
              if (*module_name == '=')
                module_name++;
-             else
+             else if (i + 1 < *argc)
                {
                  (*argv)[i] = NULL;
                  i += 1;
@@ -275,7 +593,15 @@ gtk_init_check (int         *argc,
                }
              (*argv)[i] = NULL;
 
-             gtk_modules = g_slist_prepend (gtk_modules, g_strdup (module_name));
+             if (module_name && *module_name)
+               {
+                 if (gtk_modules_string)
+                   g_string_append_c (gtk_modules_string, G_SEARCHPATH_SEPARATOR);
+                 else
+                   gtk_modules_string = g_string_new (NULL);
+
+                 g_string_append (gtk_modules_string, module_name);
+               }
            }
          else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0)
            {
@@ -348,72 +674,37 @@ gtk_init_check (int        *argc,
            }
        }
     }
-  
+
+  if (gtk_debug_flags & GTK_DEBUG_UPDATES)
+    gdk_window_set_debug_updates (TRUE);
+
   /* load gtk modules */
-  gtk_modules = g_slist_reverse (gtk_modules);
-  for (slist = gtk_modules; slist; slist = slist->next)
+  if (gtk_modules_string)
     {
-      gchar *module_name;
-      GModule *module = NULL;
-      GtkModuleInitFunc modinit_func = NULL;
-      
-      module_name = slist->data;
-      slist->data = NULL;
-#ifndef __EMX__
-      if (!g_path_is_absolute (module_name))
-       {
-         gchar *old = module_name;
-         
-         module_name = g_module_build_path (NULL, module_name);
-         g_free (old);
-       }
-#else
-      module_name = add_dll_suffix(module_name);
-#endif
-      if (g_module_supported ())
-       {
-         module = g_module_open (module_name, G_MODULE_BIND_LAZY);
-         if (module &&
-             g_module_symbol (module, "gtk_module_init", (gpointer*) &modinit_func) &&
-             modinit_func)
-           {
-             if (!g_slist_find (gtk_modules, modinit_func))
-               {
-                 g_module_make_resident (module);
-                 slist->data = modinit_func;
-               }
-             else
-               {
-                 g_module_close (module);
-                 module = NULL;
-               }
-           }
-       }
-      if (!modinit_func)
-       {
-         g_warning ("Failed to load module \"%s\": %s",
-                    module ? g_module_name (module) : module_name,
-                    g_module_error ());
-         if (module)
-           g_module_close (module);
-       }
-      g_free (module_name);
+      gtk_modules = load_modules (gtk_modules_string->str);
+      g_string_free (gtk_modules_string, TRUE);
     }
 
 #ifdef ENABLE_NLS
-#ifndef NATIVE_WIN32
-  bindtextdomain("gtk+", GTK_LOCALEDIR);
-#else
+  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+#    ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#    endif
+#endif  
+
   {
-    /* GTk+ locale dir is %WinDir%\gtk\locale */
-    extern char *get_gtk_sysconf_directory ();
-    bindtextdomain ("gtk+", g_strconcat (get_gtk_sysconf_directory (),
-                                        G_DIR_SEPARATOR_S,
-                                        "locale",
-                                        NULL));
+  /* Translate to default:RTL if you want your widgets
+   * to be RTL, otherwise translate to default:LTR.
+   * Do *not* translate it to "predefinito:LTR", if it
+   * it isn't default:LTR or default:RTL it will not work 
+   */
+    char *e = _("default:LTR");
+    if (strcmp (e, "default:RTL")==0) {
+      gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
+    } else if (strcmp (e, "default:LTR")) {
+      g_warning ("Whoever translated default:LTR did so wrongly.\n");
+    }
   }
-#endif
-#endif  
 
   /* Initialize the default visual and colormap to be
    *  used in creating widgets. (We want to use the system
@@ -422,15 +713,9 @@ gtk_init_check (int         *argc,
   gtk_visual = gdk_visual_get_system ();
   gtk_colormap = gdk_colormap_get_system ();
 
-  gtk_type_init ();
-  gtk_object_post_arg_parsing_init ();
-  gtk_signal_init ();
-  gtk_rc_init ();
-  
-  
-  /* Register an exit function to make sure we are able to cleanup.
-   */
-  g_atexit (gtk_exit_func);
+  gtk_type_init (0);
+  _gtk_accel_map_init ();  
+  _gtk_rc_init ();
   
   /* Set the 'initialized' flag.
    */
@@ -450,45 +735,121 @@ gtk_init_check (int       *argc,
     }
   g_slist_free (gtk_modules);
   
-#ifndef NATIVE_WIN32
-  /* No use warning on Win32, there aren't any non-devel versions anyhow... */
-  g_warning (""              "YOU ARE USING THE DEVEL BRANCH 1.3.x OF GTK+ WHICH IS CURRENTLY\n"
-            "                UNDER HEAVY DEVELOPMENT AND FREQUENTLY INTRODUCES INSTABILITIES.\n"
-            "                if you don't know why you are getting this, you probably want to\n"
-            "                use the stable branch which can be retrived from\n"
-            "                ftp://ftp.gtk.org/pub/gtk/v1.2/ or via CVS with\n"
-            "                cvs checkout -r glib-1-2 glib; cvs checkout -r gtk-1-2 gtk+");
-#endif
-
   return TRUE;
 }
+
+#undef gtk_init
+
 void
 gtk_init (int *argc, char ***argv)
 {
   if (!gtk_init_check (argc, argv))
     {
       g_warning ("cannot open display: %s", gdk_get_display ());
-      exit(1);
+      exit (1);
     }
 }
 
+#ifdef G_OS_WIN32
+
+static void
+check_sizeof_GtkWindow (size_t sizeof_GtkWindow)
+{
+  if (sizeof_GtkWindow != sizeof (GtkWindow))
+    g_error ("Incompatible build!\n"
+            "The code using GTK+ thinks GtkWindow is of different\n"
+             "size than it actually is in this build of GTK+.\n"
+            "On Windows, this probably means that you have compiled\n"
+            "your code with gcc without the -fnative-struct switch.");
+}
+
+/* These two functions might get more checks added later, thus pass
+ * in the number of extra args.
+ */
 void
-gtk_exit (int errorcode)
+gtk_init_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow)
+{
+  check_sizeof_GtkWindow (sizeof_GtkWindow);
+  gtk_init (argc, argv);
+}
+
+gboolean
+gtk_init_check_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow)
+{
+  check_sizeof_GtkWindow (sizeof_GtkWindow);
+  return gtk_init_check (argc, argv);
+}
+
+#endif
+
+void
+gtk_exit (gint errorcode)
 {
   /* Only if "gtk" has been initialized should we de-initialize.
    */
-  /* de-initialisation is done by the gtk_exit_funct(),
-   * no need to do this here (Alex J.)
-   */
-  gdk_exit(errorcode);
+  gdk_exit (errorcode);
 }
 
+
+/**
+ * gtk_set_locale:
+ *
+ * Initializes internationalization support for GTK+. gtk_init()
+ * automatically does this, so there is typically no point
+ * in calling this function.
+ *
+ * If you are calling this function because you changed the locale
+ * after GTK+ is was initialized, then calling this function
+ * may help a bit. (Note, however, that changing the locale
+ * after GTK+ is initialized may produce inconsistent results and
+ * is not really supported.)
+ * 
+ * In detail - sets the current locale according to the
+ * program environment. This is the same as calling the C library function
+ * <literal>setlocale (LC_ALL, "")</literal> but also takes care of the 
+ * locale specific setup of the windowing system used by GDK.
+ * 
+ * Return value: a string corresponding to the locale set, as with the
+ * C library function <function>setlocale()</function>.
+ **/
 gchar*
 gtk_set_locale (void)
 {
   return gdk_set_locale ();
 }
 
+/**
+ * gtk_get_default_language:
+ *
+ * Returns the ISO language code for the default language currently in
+ * effect. (Note that this can change over the life of an
+ * application.)  The default language is derived from the current
+ * locale. It determines, for example, whether GTK+ uses the
+ * right-to-left or left-to-right text direction.
+ * 
+ * Return value: the default language as an allocated string, must be freed
+ **/
+PangoLanguage *
+gtk_get_default_language (void)
+{
+  gchar *lang;
+  PangoLanguage *result;
+  gchar *p;
+  
+  lang = g_strdup (setlocale (LC_CTYPE, NULL));
+  p = strchr (lang, '.');
+  if (p)
+    *p = '\0';
+  p = strchr (lang, '@');
+  if (p)
+    *p = '\0';
+
+  result = pango_language_from_string (lang);
+  g_free (lang);
+  
+  return result;
+}
+
 void
 gtk_main (void)
 {
@@ -532,7 +893,9 @@ gtk_main (void)
        {
          quitf = quit_functions->data;
 
+         tmp_list = quit_functions;
          quit_functions = g_list_remove_link (quit_functions, quit_functions);
+         g_list_free_1 (tmp_list);
 
          if ((quitf->main_level && quitf->main_level != gtk_main_loop_level) ||
              gtk_quit_invoke_function (quitf))
@@ -541,7 +904,6 @@ gtk_main (void)
            }
          else
            {
-             g_list_free (tmp_list);
              gtk_quit_destroy (quitf);
            }
        }
@@ -583,13 +945,21 @@ gtk_main_quit (void)
 gint
 gtk_events_pending (void)
 {
-  return g_main_pending();
+  gboolean result;
+  
+  GDK_THREADS_LEAVE ();  
+  result = g_main_pending ();
+  GDK_THREADS_ENTER ();
+
+  return result;
 }
 
-gint 
+gboolean
 gtk_main_iteration (void)
 {
+  GDK_THREADS_LEAVE ();
   g_main_iteration (TRUE);
+  GDK_THREADS_ENTER ();
 
   if (main_loops)
     return !g_main_is_running (main_loops->data);
@@ -597,10 +967,12 @@ gtk_main_iteration (void)
     return TRUE;
 }
 
-gint 
+gboolean
 gtk_main_iteration_do (gboolean blocking)
 {
+  GDK_THREADS_LEAVE ();
   g_main_iteration (blocking);
+  GDK_THREADS_ENTER ();
 
   if (main_loops)
     return !g_main_is_running (main_loops->data);
@@ -608,12 +980,129 @@ gtk_main_iteration_do (gboolean blocking)
     return TRUE;
 }
 
+/* private libgtk to libgdk interfaces
+ */
+gboolean gdk_pointer_grab_info_libgtk_only  (GdkWindow **grab_window,
+                                            gboolean   *owner_events);
+gboolean gdk_keyboard_grab_info_libgtk_only (GdkWindow **grab_window,
+                                            gboolean   *owner_events);
+
+static void
+rewrite_events_translate (GdkWindow *old_window,
+                         GdkWindow *new_window,
+                         gdouble   *x,
+                         gdouble   *y)
+{
+  gint old_origin_x, old_origin_y;
+  gint new_origin_x, new_origin_y;
+
+  gdk_window_get_origin        (old_window, &old_origin_x, &old_origin_y);
+  gdk_window_get_origin        (new_window, &new_origin_x, &new_origin_y);
+
+  *x += new_origin_x - old_origin_x;
+  *y += new_origin_y - old_origin_y;
+}
+
+GdkEvent *
+rewrite_event_for_window (GdkEvent  *event,
+                         GdkWindow *new_window)
+{
+  event = gdk_event_copy (event);
+
+  switch (event->type)
+    {
+    case GDK_SCROLL:
+      rewrite_events_translate (event->any.window,
+                               new_window,
+                               &event->scroll.x, &event->scroll.y);
+      break;
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      rewrite_events_translate (event->any.window,
+                               new_window,
+                               &event->button.x, &event->button.y);
+      break;
+    case GDK_MOTION_NOTIFY:
+      rewrite_events_translate (event->any.window,
+                               new_window,
+                               &event->motion.x, &event->motion.y);
+      break;
+    case GDK_KEY_PRESS:
+    case GDK_KEY_RELEASE:
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+      break;
+
+    default:
+      return event;
+    }
+
+  g_object_unref (event->any.window);
+  event->any.window = g_object_ref (new_window);
+
+  return event;
+}
+
+/* If there is a pointer or keyboard grab in effect with owner_events = TRUE,
+ * then what X11 does is deliver the event normally if it was going to this
+ * client, otherwise, delivers it in terms of the grab window. This function
+ * rewrites events to the effect that events going to the same window group
+ * are delivered normally, otherwise, the event is delivered in terms of the
+ * grab window.
+ */
+static GdkEvent *
+rewrite_event_for_grabs (GdkEvent *event)
+{
+  GdkWindow *grab_window;
+  GtkWidget *event_widget, *grab_widget;;
+  gboolean owner_events;
+
+  switch (event->type)
+    {
+    case GDK_SCROLL:
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+    case GDK_MOTION_NOTIFY:
+    case GDK_PROXIMITY_IN:
+    case GDK_PROXIMITY_OUT:
+      if (!gdk_pointer_grab_info_libgtk_only (&grab_window, &owner_events) ||
+         !owner_events)
+       return NULL;
+      break;
+
+    case GDK_KEY_PRESS:
+    case GDK_KEY_RELEASE:
+      if (!gdk_keyboard_grab_info_libgtk_only (&grab_window, &owner_events) ||
+         !owner_events)
+       return NULL;
+      break;
+
+    default:
+      return NULL;
+    }
+
+  event_widget = gtk_get_event_widget (event);
+  gdk_window_get_user_data (grab_window, (void**) &grab_widget);
+
+  if (grab_widget &&
+      gtk_main_get_window_group (grab_widget) != gtk_main_get_window_group (event_widget))
+    return rewrite_event_for_window (event, grab_window);
+  else
+    return NULL;
+}
+
 void 
 gtk_main_do_event (GdkEvent *event)
 {
   GtkWidget *event_widget;
   GtkWidget *grab_widget;
+  GtkWindowGroup *window_group;
   GdkEvent *next_event;
+  GdkEvent *rewritten_event = NULL;
   GList *tmp_list;
 
   /* If there are any events pending then get the next one.
@@ -667,20 +1156,34 @@ gtk_main_do_event (GdkEvent *event)
       if (event->type == GDK_PROPERTY_NOTIFY)
        gtk_selection_incr_event (event->any.window,
                                  &event->property);
-      
+      else if (event->type == GDK_SETTING)
+       _gtk_settings_handle_event (&event->setting);
+
       return;
     }
+
+  /* If pointer or keyboard grabs are in effect, munge the events
+   * so that each window group looks like a separate app.
+   */
+  rewritten_event = rewrite_event_for_grabs (event);
+  if (rewritten_event)
+    {
+      event = rewritten_event;
+      event_widget = gtk_get_event_widget (event);
+    }
   
+  window_group = gtk_main_get_window_group (event_widget);
+
   /* Push the event onto a stack of current events for
    * gtk_current_event_get().
    */
   current_events = g_list_prepend (current_events, event);
-  
+
   /* If there is a grab in effect...
    */
-  if (grabs)
+  if (window_group->grabs)
     {
-      grab_widget = grabs->data;
+      grab_widget = window_group->grabs->data;
       
       /* If the grab widget is an ancestor of the event widget
        *  then we send the event to the original event widget.
@@ -712,22 +1215,38 @@ gtk_main_do_event (GdkEvent *event)
       
     case GDK_DELETE:
       gtk_widget_ref (event_widget);
-      if (!gtk_widget_event (event_widget, event) &&
-         !GTK_OBJECT_DESTROYED (event_widget))
+      if ((!window_group->grabs || gtk_widget_get_toplevel (window_group->grabs->data) == event_widget) &&
+         !gtk_widget_event (event_widget, event))
        gtk_widget_destroy (event_widget);
       gtk_widget_unref (event_widget);
       break;
       
     case GDK_DESTROY:
-      gtk_widget_ref (event_widget);
-      gtk_widget_event (event_widget, event);
-      if (!GTK_OBJECT_DESTROYED (event_widget))
-       gtk_widget_destroy (event_widget);
-      gtk_widget_unref (event_widget);
+      /* Unexpected GDK_DESTROY from the outside, ignore for
+       * child windows, handle like a GDK_DELETE for toplevels
+       */
+      if (!event_widget->parent)
+       {
+         gtk_widget_ref (event_widget);
+         if (!gtk_widget_event (event_widget, event) &&
+             GTK_WIDGET_REALIZED (event_widget))
+           gtk_widget_destroy (event_widget);
+         gtk_widget_unref (event_widget);
+       }
       break;
       
-    case GDK_PROPERTY_NOTIFY:
     case GDK_EXPOSE:
+      if (event->any.window && GTK_WIDGET_DOUBLE_BUFFERED (event_widget))
+       {
+         gdk_window_begin_paint_region (event->any.window, event->expose.region);
+         gtk_widget_send_expose (event_widget, event);
+         gdk_window_end_paint (event->any.window);
+       }
+      else
+       gtk_widget_send_expose (event_widget, event);
+      break;
+
+    case GDK_PROPERTY_NOTIFY:
     case GDK_NO_EXPOSE:
     case GDK_FOCUS_CHANGE:
     case GDK_CONFIGURE:
@@ -738,48 +1257,14 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_SELECTION_NOTIFY:
     case GDK_CLIENT_EVENT:
     case GDK_VISIBILITY_NOTIFY:
+    case GDK_WINDOW_STATE:
       gtk_widget_event (event_widget, event);
       break;
 
+    case GDK_SCROLL:
     case GDK_BUTTON_PRESS:
     case GDK_2BUTTON_PRESS:
     case GDK_3BUTTON_PRESS:
-    /* We treat button 4-5 specially, assume we have
-     * a MS-style scrollwheel mouse, and try to find
-     * a plausible widget to scroll. We also trap
-     * button 4-5 double and triple clicks here, since
-     * they will be generated if the user scrolls quickly.
-     */
-      if ((grab_widget == event_widget) &&
-         (event->button.button == 4 || event->button.button == 5))
-       {
-         GtkWidget *range = NULL;
-         GtkWidget *scrollwin;
-         
-         if (GTK_IS_RANGE (event_widget))
-           range = event_widget;
-         else
-           {
-             scrollwin = gtk_widget_get_ancestor (event_widget,
-                                                  GTK_TYPE_SCROLLED_WINDOW);
-             if (scrollwin)
-               range = GTK_SCROLLED_WINDOW (scrollwin)->vscrollbar;
-           }
-         
-         if (range && GTK_WIDGET_VISIBLE (range))
-           {
-             if (event->type == GDK_BUTTON_PRESS)
-               {
-                 GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
-                 gfloat new_value = adj->value + ((event->button.button == 4) ? 
-                                                  -adj->page_increment / 2: 
-                                                   adj->page_increment / 2);
-                 new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
-                 gtk_adjustment_set_value (adj, new_value);
-               }
-             break;
-           }
-       }
       gtk_propagate_event (grab_widget, event);
       break;
 
@@ -801,9 +1286,13 @@ gtk_main_do_event (GdkEvent *event)
     case GDK_ENTER_NOTIFY:
       if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
        {
+         g_object_ref (event_widget);
+         
          gtk_widget_event (grab_widget, event);
          if (event_widget == grab_widget)
            GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
+         
+         g_object_unref (event_widget);
        }
       break;
       
@@ -819,66 +1308,182 @@ gtk_main_do_event (GdkEvent *event)
       
     case GDK_DRAG_STATUS:
     case GDK_DROP_FINISHED:
-      gtk_drag_source_handle_event (event_widget, event);
+      _gtk_drag_source_handle_event (event_widget, event);
       break;
     case GDK_DRAG_ENTER:
     case GDK_DRAG_LEAVE:
     case GDK_DRAG_MOTION:
     case GDK_DROP_START:
-      gtk_drag_dest_handle_event (event_widget, event);
+      _gtk_drag_dest_handle_event (event_widget, event);
+      break;
+    default:
+      g_assert_not_reached ();
       break;
     }
   
   tmp_list = current_events;
   current_events = g_list_remove_link (current_events, tmp_list);
   g_list_free_1 (tmp_list);
+
+  if (rewritten_event)
+    gdk_event_free (rewritten_event);
 }
 
-gint
+gboolean
 gtk_true (void)
 {
   return TRUE;
 }
 
-gint
+gboolean
 gtk_false (void)
 {
   return FALSE;
 }
 
+static GtkWindowGroup *
+gtk_main_get_window_group (GtkWidget   *widget)
+{
+  GtkWidget *toplevel = NULL;
+
+  if (widget)
+    toplevel = gtk_widget_get_toplevel (widget);
+
+  if (toplevel && GTK_IS_WINDOW (toplevel))
+    return _gtk_window_get_group (GTK_WINDOW (toplevel));
+  else
+    return _gtk_window_get_group (NULL);
+}
+
+typedef struct
+{
+  GtkWidget *old_grab_widget;
+  GtkWidget *new_grab_widget;
+} GrabNotifyInfo;
+
+static gboolean
+check_is_grabbed (GtkWidget *widget,
+                 GtkWidget *grab_widget)
+{
+  if (grab_widget)
+    return !(widget == grab_widget || gtk_widget_is_ancestor (widget, grab_widget));
+  else
+    return FALSE;
+}
+
+static void
+gtk_grab_notify_foreach (GtkWidget *child,
+                        gpointer   data)
+                        
+{
+  GrabNotifyInfo *info = data;
+  gboolean was_grabbed = check_is_grabbed (child, info->old_grab_widget);
+  gboolean is_grabbed = check_is_grabbed (child, info->new_grab_widget);
+
+  if (was_grabbed != is_grabbed)
+    {
+      g_object_ref (G_OBJECT (child));
+      
+      gtk_signal_emit_by_name (GTK_OBJECT (child), "grab_notify", was_grabbed);
+      
+      if (GTK_IS_CONTAINER (child))
+       gtk_container_foreach (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
+      
+      g_object_unref (G_OBJECT (child));
+    }
+}
+
+static void
+gtk_grab_notify (GtkWindowGroup *group,
+                GtkWidget      *grab_widget,
+                gboolean        was_grabbed)
+{
+  GList *toplevels;
+  GrabNotifyInfo info;
+
+  if (was_grabbed)
+    {
+      info.old_grab_widget = grab_widget;
+      info.new_grab_widget = group->grabs ? group->grabs->data : NULL;
+    }
+  else
+    {
+      info.old_grab_widget = (group->grabs && group->grabs->next) ? group->grabs->next->data : NULL;
+      info.new_grab_widget = grab_widget;
+    }
+
+  g_object_ref (group);
+  g_object_ref (grab_widget);
+
+  toplevels = gtk_window_list_toplevels ();
+  g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
+                           
+  while (toplevels)
+    {
+      GtkWindow *toplevel = toplevels->data;
+      toplevels = g_list_delete_link (toplevels, toplevels);
+
+      if (group == _gtk_window_get_group (toplevel))
+       gtk_container_foreach (GTK_CONTAINER (toplevel), gtk_grab_notify_foreach, &info);
+      g_object_unref (toplevel);
+    }
+
+  g_object_unref (group);
+  g_object_unref (grab_widget);
+}
+
 void
 gtk_grab_add (GtkWidget *widget)
 {
+  GtkWindowGroup *group;
+  gboolean was_grabbed;
+  
   g_return_if_fail (widget != NULL);
   
-  if (!GTK_WIDGET_HAS_GRAB (widget))
+  if (!GTK_WIDGET_HAS_GRAB (widget) && GTK_WIDGET_IS_SENSITIVE (widget))
     {
       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
       
-      grabs = g_slist_prepend (grabs, widget);
+      group = gtk_main_get_window_group (widget);
+
+      was_grabbed = (group->grabs != NULL);
+      
       gtk_widget_ref (widget);
+      group->grabs = g_slist_prepend (group->grabs, widget);
+
+      gtk_grab_notify (group, widget, FALSE);
     }
 }
 
 GtkWidget*
 gtk_grab_get_current (void)
 {
-  if (grabs)
-    return GTK_WIDGET (grabs->data);
+  GtkWindowGroup *group;
+
+  group = gtk_main_get_window_group (NULL);
+
+  if (group->grabs)
+    return GTK_WIDGET (group->grabs->data);
   return NULL;
 }
 
 void
 gtk_grab_remove (GtkWidget *widget)
 {
+  GtkWindowGroup *group;
+  
   g_return_if_fail (widget != NULL);
   
   if (GTK_WIDGET_HAS_GRAB (widget))
     {
       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
+
+      group = gtk_main_get_window_group (widget);
+      group->grabs = g_slist_remove (group->grabs, widget);
       
-      grabs = g_slist_remove (grabs, widget);
       gtk_widget_unref (widget);
+
+      gtk_grab_notify (group, widget, TRUE);
     }
 }
 
@@ -1008,7 +1613,6 @@ gtk_quit_add_destroy (guint              main_level,
   GtkObject **object_p;
 
   g_return_if_fail (main_level > 0);
-  g_return_if_fail (object != NULL);
   g_return_if_fail (GTK_IS_OBJECT (object));
 
   object_p = g_new (GtkObject*, 1);
@@ -1148,9 +1752,9 @@ gtk_idle_add (GtkFunction function,
 }
 
 guint      
-gtk_idle_add_priority  (gint               priority,
-                        GtkFunction        function,
-                        gpointer           data)
+gtk_idle_add_priority (gint        priority,
+                      GtkFunction function,
+                      gpointer    data)
 {
   return g_idle_add_full (priority, function, data, NULL);
 }
@@ -1235,25 +1839,87 @@ gtk_invoke_input (gpointer          data,
   GtkArg args[3];
   args[0].type = GTK_TYPE_INT;
   args[0].name = NULL;
-  GTK_VALUE_INT(args[0]) = source;
-  args[1].type = GTK_TYPE_GDK_INPUT_CONDITION;
+  GTK_VALUE_INT (args[0]) = source;
+  args[1].type = GDK_TYPE_INPUT_CONDITION;
   args[1].name = NULL;
-  GTK_VALUE_FLAGS(args[1]) = condition;
+  GTK_VALUE_FLAGS (args[1]) = condition;
   args[2].type = GTK_TYPE_NONE;
   args[2].name = NULL;
 
   closure->marshal (NULL, closure->data, 2, args);
 }
 
+/**
+ * gtk_get_current_event:
+ * 
+ * Obtains a copy of the event currently being processed by GTK+.  For
+ * example, if you get a "clicked" signal from #GtkButton, the current
+ * event will be the #GdkEventButton that triggered the "clicked"
+ * signal. The returned event must be freed with gdk_event_free().
+ * If there is no current event, the function returns %NULL.
+ * 
+ * Return value: a copy of the current event, or %NULL if no current event.
+ **/
 GdkEvent*
 gtk_get_current_event (void)
 {
   if (current_events)
-    return gdk_event_copy ((GdkEvent *) current_events->data);
+    return gdk_event_copy (current_events->data);
   else
     return NULL;
 }
 
+/**
+ * gtk_get_current_event_time:
+ * 
+ * If there is a current event and it has a timestamp, return that
+ * timestamp, otherwise return %GDK_CURRENT_TIME.
+ * 
+ * Return value: the timestamp from the current event, or %GDK_CURRENT_TIME.
+ **/
+guint32
+gtk_get_current_event_time (void)
+{
+  if (current_events)
+    return gdk_event_get_time (current_events->data);
+  else
+    return GDK_CURRENT_TIME;
+}
+
+/**
+ * gtk_get_current_event_state:
+ * @state: a location to store the state of the current event
+ * 
+ * If there is a current event and it has a state field, place
+ * that state field in @state and return %TRUE, otherwise return
+ * %FALSE.
+ * 
+ * Return value: %TRUE if there was a current event and it had a state field
+ **/
+gboolean
+gtk_get_current_event_state (GdkModifierType *state)
+{
+  g_return_val_if_fail (state != NULL, FALSE);
+  
+  if (current_events)
+    return gdk_event_get_state (current_events->data, state);
+  else
+    {
+      *state = 0;
+      return FALSE;
+    }
+}
+
+/**
+ * gtk_get_event_widget:
+ * @event: a #GdkEvent
+ *
+ * If @event is %NULL or the event was not associated with any widget,
+ * returns %NULL, otherwise returns the widget that received the event
+ * originally.
+ * 
+ * Return value: the widget that originally received @event, or %NULL
+ **/
 GtkWidget*
 gtk_get_event_widget (GdkEvent *event)
 {
@@ -1266,17 +1932,6 @@ gtk_get_event_widget (GdkEvent *event)
   return widget;
 }
 
-static void
-gtk_exit_func (void)
-{
-  if (gtk_initialized)
-    {
-      gtk_initialized = FALSE;
-      gtk_preview_uninit ();
-    }
-}
-
-
 static gint
 gtk_quit_invoke_function (GtkQuitFunction *quitf)
 {
@@ -1297,18 +1952,42 @@ gtk_quit_invoke_function (GtkQuitFunction *quitf)
     }
 }
 
+/**
+ * gtk_propagate_event:
+ * @widget: a #GtkWidget
+ * @event: an event
+ *
+ * Sends an event to a widget, propagating the event to parent widgets
+ * if the event remains unhandled. Events received by GTK+ from GDK
+ * normally begin in gtk_main_do_event(). Depending on the type of
+ * event, existence of modal dialogs, grabs, etc., the event may be
+ * propagated; if so, this function is used. gtk_propagate_event()
+ * calls gtk_widget_event() on each widget it decides to send the
+ * event to.  So gtk_widget_event() is the lowest-level function; it
+ * simply emits the "event" and possibly an event-specific signal on a
+ * widget.  gtk_propagate_event() is a bit higher-level, and
+ * gtk_main_do_event() is the highest level.
+ *
+ * All that said, you most likely don't want to use any of these
+ * functions; synthesizing events is rarely needed. Consider asking on
+ * the mailing list for better ways to achieve your goals. For
+ * example, use gdk_window_invalidate_rect() or
+ * gtk_widget_queue_draw() instead of making up expose events.
+ * 
+ **/
 void
 gtk_propagate_event (GtkWidget *widget,
                     GdkEvent  *event)
 {
   gint handled_event;
   
-  g_return_if_fail (widget != NULL);
   g_return_if_fail (GTK_IS_WIDGET (widget));
   g_return_if_fail (event != NULL);
   
   handled_event = FALSE;
 
+  gtk_widget_ref (widget);
+      
   if ((event->type == GDK_KEY_PRESS) ||
       (event->type == GDK_KEY_RELEASE))
     {
@@ -1319,30 +1998,53 @@ gtk_propagate_event (GtkWidget *widget,
        */
       GtkWidget *window;
 
-      window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
-      if (window)
-        {
-         if (GTK_WIDGET_IS_SENSITIVE (window))
-           gtk_widget_event (window, event);
-
-          handled_event = TRUE; /* don't send to widget */
-        }
+      window = gtk_widget_get_toplevel (widget);
+      if (window && GTK_IS_WINDOW (window))
+       {
+         /* If there is a grab within the window, give the grab widget
+          * a first crack at the key event
+          */
+         if (widget != window && GTK_WIDGET_HAS_GRAB (widget))
+           handled_event = gtk_widget_event (widget, event);
+         
+         if (!handled_event)
+           {
+             window = gtk_widget_get_toplevel (widget);
+             if (window && GTK_IS_WINDOW (window))
+               {
+                 if (GTK_WIDGET_IS_SENSITIVE (window))
+                   gtk_widget_event (window, event);
+               }
+           }
+                 
+         handled_event = TRUE; /* don't send to widget */
+       }
     }
   
   /* Other events get propagated up the widget tree
    *  so that parents can see the button and motion
    *  events of the children.
    */
-  while (!handled_event && widget)
+  if (!handled_event)
     {
-      GtkWidget *tmp;
+      while (TRUE)
+       {
+         GtkWidget *tmp;
+         
+         handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
+         tmp = widget->parent;
+         gtk_widget_unref (widget);
 
-      gtk_widget_ref (widget);
-      handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
-      tmp = widget->parent;
-      gtk_widget_unref (widget);
-      widget  = tmp;
+         widget = tmp;
+         
+         if (!handled_event && widget)
+           gtk_widget_ref (widget);
+         else
+           break;
+       }
     }
+  else
+    gtk_widget_unref (widget);
 }
 
 #if 0
@@ -1457,3 +2159,19 @@ gtk_print (gchar *str)
     gtk_widget_show (window);
 }
 #endif
+
+gboolean
+_gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
+                                 GValue                *return_accu,
+                                 const GValue          *handler_return,
+                                 gpointer               dummy)
+{
+  gboolean continue_emission;
+  gboolean signal_handled;
+  
+  signal_handled = g_value_get_boolean (handler_return);
+  g_value_set_boolean (return_accu, signal_handled);
+  continue_emission = !signal_handled;
+  
+  return continue_emission;
+}