]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmain.c
Merge libgdk and libgtk
[~andy/gtk] / gtk / gtkmain.c
index 497a99f671a33c31c711cdc0a7d82bbaa1ad5beb..dc272600fe2cc10c9e958f6d43c424b3eb341af1 100644 (file)
 
 #include "config.h"
 
+#include "gtkmain.h"
+
 #include <glib.h>
-#include "gdkconfig.h"
+#include "gdk/gdk.h"
 
 #include <locale.h>
 
 #include "gtkclipboard.h"
 #include "gtkdnd.h"
 #include "gtkversion.h"
-#include "gtkmain.h"
 #include "gtkmodules.h"
 #include "gtkrc.h"
 #include "gtkrecentmanager.h"
 #include "gtkselection.h"
 #include "gtksettings.h"
-#include "gtkwidget.h"
-#include "gtkwindow.h"
+#include "gtkwidgetprivate.h"
+#include "gtkwindowprivate.h"
 #include "gtktooltip.h"
 #include "gtkdebug.h"
-#include "gtkalias.h"
 #include "gtkmenu.h"
-#include "gdk/gdkkeysyms.h"
-
-#include "gdk/gdkprivate.h" /* for GDK_WINDOW_DESTROYED */
 
 #ifdef G_OS_WIN32
 
@@ -146,17 +143,9 @@ _gtk_get_localedir (void)
 
 /* Private type definitions
  */
-typedef struct _GtkInitFunction                 GtkInitFunction;
 typedef struct _GtkQuitFunction                 GtkQuitFunction;
-typedef struct _GtkClosure              GtkClosure;
 typedef struct _GtkKeySnooperData       GtkKeySnooperData;
 
-struct _GtkInitFunction
-{
-  GtkFunction function;
-  gpointer data;
-};
-
 struct _GtkQuitFunction
 {
   guint id;
@@ -167,13 +156,6 @@ struct _GtkQuitFunction
   GDestroyNotify destroy;
 };
 
-struct _GtkClosure
-{
-  GtkCallbackMarshal marshal;
-  gpointer data;
-  GDestroyNotify destroy;
-};
-
 struct _GtkKeySnooperData
 {
   GtkKeySnoopFunc func;
@@ -186,27 +168,8 @@ static void  gtk_quit_destroy               (GtkQuitFunction    *quitf);
 static gint  gtk_invoke_key_snoopers    (GtkWidget          *grab_widget,
                                          GdkEvent           *event);
 
-static void     gtk_destroy_closure      (gpointer            data);
-static gboolean gtk_invoke_idle_timeout  (gpointer            data);
-static void     gtk_invoke_input         (gpointer            data,
-                                         gint                source,
-                                         GdkInputCondition   condition);
-
-#if 0
-static void  gtk_error                  (gchar              *str);
-static void  gtk_warning                (gchar              *str);
-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;
-const guint gtk_binary_age = GTK_BINARY_AGE;
-const guint gtk_interface_age = GTK_INTERFACE_AGE;
-
 static guint gtk_main_loop_level = 0;
 static gint pre_initialized = FALSE;
 static gint gtk_initialized = FALSE;
@@ -214,13 +177,11 @@ static GList *current_events = NULL;
 
 static GSList *main_loops = NULL;      /* stack of currently executing main loops */
 
-static GList *init_functions = NULL;      /* A list of init functions.
-                                           */
 static GList *quit_functions = NULL;      /* A list of quit functions.
                                            */
 static GSList *key_snoopers = NULL;
 
-guint gtk_debug_flags = 0;                /* Global GTK debug flag */
+static guint debug_flags = 0;             /* Global GTK debug flag */
 
 #ifdef G_ENABLE_DEBUG
 static const GDebugKey gtk_debug_keys[] = {
@@ -235,10 +196,112 @@ static const GDebugKey gtk_debug_keys[] = {
   {"geometry", GTK_DEBUG_GEOMETRY},
   {"icontheme", GTK_DEBUG_ICONTHEME},
   {"printing", GTK_DEBUG_PRINTING},
-  {"builder", GTK_DEBUG_BUILDER}
+  {"builder", GTK_DEBUG_BUILDER},
+  {"size-request", GTK_DEBUG_SIZE_REQUEST},
 };
 #endif /* G_ENABLE_DEBUG */
 
+/**
+ * gtk_get_major_version:
+ *
+ * Returns the major version number of the GTK+ library.  (e.g. in GTK+ version
+ * 3.1.5 this is 3.) 
+ *
+ * This function is in the library, so it represents the GTK+ library
+ * your code is running against. Contrast with the #GTK_MAJOR_VERSION
+ * macro, which represents the major version of the GTK+ headers you
+ * have included when compiling your code.
+ *
+ * Returns: the major version number of the GTK+ library.
+ *
+ * Since: 3.0
+ */
+guint
+gtk_get_major_version (void)
+{
+  return GTK_MAJOR_VERSION;
+}
+
+/**
+ * gtk_get_minor_version:
+ *
+ * Returns the minor version number of the GTK+ library.  (e.g. in GTK+ version
+ * 3.1.5 this is 1.) 
+ *
+ * This function is in the library, so it represents the GTK+ library
+ * your code is are running against. Contrast with the
+ * #GTK_MINOR_VERSION macro, which represents the minor version of the
+ * GTK+ headers you have included when compiling your code.
+ *
+ * Returns: the minor version number of the GTK+ library.
+ *
+ * Since: 3.0
+ */
+guint
+gtk_get_minor_version (void)
+{
+  return GTK_MINOR_VERSION;
+}
+
+/**
+ * gtk_get_micro_version:
+ *
+ * Returns the micro version number of the GTK+ library.  (e.g. in GTK+ version
+ * 3.1.5 this is 5.) 
+ *
+ * This function is in the library, so it represents the GTK+ library
+ * your code is are running against. Contrast with the
+ * #GTK_MICRO_VERSION macro, which represents the micro version of the
+ * GTK+ headers you have included when compiling your code.
+ *
+ * Returns: the micro version number of the GTK+ library.
+ *
+ * Since: 3.0
+ */
+guint
+gtk_get_micro_version (void)
+{
+  return GTK_MICRO_VERSION;
+}
+
+/**
+ * gtk_get_binary_age:
+ *
+ * Returns the binary age as passed to
+ * <application>libtool</application> when building the GTK+ library
+ * the process is running against. If
+ * <application>libtool</application> means nothing to you, don't
+ * worry about it.
+ *
+ * Returns: the binary age of the GTK+ library.
+ *
+ * Since: 3.0
+ */
+guint
+gtk_get_binary_age (void)
+{
+  return GTK_BINARY_AGE;
+}
+
+/**
+ * gtk_get_interface_age:
+ *
+ * Returns the interface age as passed to
+ * <application>libtool</application> when building the GTK+ library
+ * the process is running against. If
+ * <application>libtool</application> means nothing to you, don't
+ * worry about it.
+ *
+ * Returns: the interface age of the GTK+ library.
+ *
+ * Since: 3.0
+ */
+guint
+gtk_get_interface_age (void)
+{
+  return GTK_INTERFACE_AGE;
+}
+
 /**
  * gtk_check_version:
  * @required_major: the required major version.
@@ -263,7 +326,7 @@ static const GDebugKey gtk_debug_keys[] = {
  * This function is primarily for GTK+ modules; the module
  * can call this function to check that it wasn't loaded
  * into an incompatible version of GTK+. However, such a
- * check isn't completely reliable, since the module may be
+ * check isn't completely reliable, since the module may be
  * linked against an old version of GTK+ and calling the
  * old version of gtk_check_version(), but still get loaded
  * into an application using a newer version of GTK+.
@@ -413,9 +476,9 @@ static gboolean g_fatal_warnings = FALSE;
 static gboolean
 gtk_arg_debug_cb (const char *key, const char *value, gpointer user_data)
 {
-  gtk_debug_flags |= g_parse_debug_string (value,
-                                          gtk_debug_keys,
-                                          G_N_ELEMENTS (gtk_debug_keys));
+  debug_flags |= g_parse_debug_string (value,
+                                      gtk_debug_keys,
+                                      G_N_ELEMENTS (gtk_debug_keys));
 
   return TRUE;
 }
@@ -423,9 +486,9 @@ gtk_arg_debug_cb (const char *key, const char *value, gpointer user_data)
 static gboolean
 gtk_arg_no_debug_cb (const char *key, const char *value, gpointer user_data)
 {
-  gtk_debug_flags &= ~g_parse_debug_string (value,
-                                           gtk_debug_keys,
-                                           G_N_ELEMENTS (gtk_debug_keys));
+  debug_flags &= ~g_parse_debug_string (value,
+                                       gtk_debug_keys,
+                                       G_N_ELEMENTS (gtk_debug_keys));
 
   return TRUE;
 }
@@ -627,24 +690,35 @@ setlocale_initialization (void)
     }
 }
 
+static void
+check_mixed_deps (void)
+{
+  GModule *module;
+  gpointer func;
+
+  module = g_module_open (NULL, 0);
+
+  if (g_module_symbol (module, "gtk_progress_get_type", &func))
+    {
+      g_error ("GTK+ 2.x symbols detected. Using GTK+ 2.x and GTK+ 3 in the same process is not supported");
+    }
+
+  g_module_close (module);
+}
+
 static void
 do_pre_parse_initialization (int    *argc,
                             char ***argv)
 {
   const gchar *env_string;
   
-#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 (pre_initialized)
     return;
 
   pre_initialized = TRUE;
 
+  check_mixed_deps ();
+
   gdk_pre_parse_libgtk_only ();
   gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
   
@@ -652,9 +726,9 @@ do_pre_parse_initialization (int    *argc,
   env_string = g_getenv ("GTK_DEBUG");
   if (env_string != NULL)
     {
-      gtk_debug_flags = g_parse_debug_string (env_string,
-                                             gtk_debug_keys,
-                                             G_N_ELEMENTS (gtk_debug_keys));
+      debug_flags = g_parse_debug_string (env_string,
+                                         gtk_debug_keys,
+                                         G_N_ELEMENTS (gtk_debug_keys));
       env_string = NULL;
     }
 #endif /* G_ENABLE_DEBUG */
@@ -701,7 +775,7 @@ do_post_parse_initialization (int    *argc,
       g_log_set_always_fatal (fatal_mask);
     }
 
-  if (gtk_debug_flags & GTK_DEBUG_UPDATES)
+  if (debug_flags & GTK_DEBUG_UPDATES)
     gdk_window_set_debug_updates (TRUE);
 
   {
@@ -721,7 +795,6 @@ do_post_parse_initialization (int    *argc,
   g_type_init ();
 
   _gtk_accel_map_init ();
-  _gtk_rc_init ();
 
   /* Set the 'initialized' flag.
    */
@@ -786,6 +859,33 @@ post_parse_hook (GOptionContext *context,
 }
 
 
+/**
+ * gtk_get_debug_flags:
+ *
+ * Returns the GTK+ debug flags.
+ *
+ * This function is intended for GTK+ modules that want
+ * to adjust their debug output based on GTK+ debug flags.
+ *
+ * Returns: the GTK+ debug flags.
+ */
+guint
+gtk_get_debug_flags (void)
+{
+  return debug_flags;
+}
+
+/**
+ * gtk_set_debug_flags:
+ *
+ * Sets the GTK+ debug flags.
+ */
+void
+gtk_set_debug_flags (guint flags)
+{
+  debug_flags = flags;
+}
+
 /**
  * gtk_get_option_group:
  * @open_default_display: whether to open the default display 
@@ -827,33 +927,33 @@ gtk_get_option_group (gboolean open_default_display)
  * @argc: a pointer to the number of command line arguments.
  * @argv: a pointer to the array of command line arguments.
  * @parameter_string: a string which is displayed in
- *    the first line of <option>--help</option> output, after 
+ *    the first line of <option>--help</option> output, after
  *    <literal><replaceable>programname</replaceable> [OPTION...]</literal>
  * @entries: a %NULL-terminated array of #GOptionEntry<!-- -->s
  *    describing the options of your program
  * @translation_domain: a translation domain to use for translating
  *    the <option>--help</option> output for the options in @entries
- *    with gettext(), or %NULL
- * @error: a return location for errors 
+ *    and the @parameter_string with gettext(), or %NULL
+ * @error: a return location for errors
  *
- * This function does the same work as gtk_init_check(). 
- * Additionally, it allows you to add your own commandline options, 
- * and it automatically generates nicely formatted 
+ * This function does the same work as gtk_init_check().
+ * Additionally, it allows you to add your own commandline options,
+ * and it automatically generates nicely formatted
  * <option>--help</option> output. Note that your program will
  * be terminated after writing out the help output.
  *
- * Returns: %TRUE if the GUI has been successfully initialized, 
+ * Returns: %TRUE if the GUI has been successfully initialized,
  *               %FALSE otherwise.
- * 
+ *
  * Since: 2.6
  */
 gboolean
-gtk_init_with_args (int            *argc,
-                   char         ***argv,
-                   const char     *parameter_string,
-                   GOptionEntry   *entries,
-                   const char     *translation_domain,
-                   GError        **error)
+gtk_init_with_args (gint                 *argc,
+                    gchar              ***argv,
+                    const gchar          *parameter_string,
+                    const GOptionEntry   *entries,
+                    const gchar          *translation_domain,
+                    GError              **error)
 {
   GOptionContext *context;
   GOptionGroup *gtk_group;
@@ -868,14 +968,15 @@ gtk_init_with_args (int            *argc,
     return FALSE;
 
   gtk_group = gtk_get_option_group (TRUE);
-  
+
   context = g_option_context_new (parameter_string);
   g_option_context_add_group (context, gtk_group);
-  
+  g_option_context_set_translation_domain (context, translation_domain);
+
   if (entries)
     g_option_context_add_main_entries (context, entries, translation_domain);
   retval = g_option_context_parse (context, argc, argv, error);
-  
+
   g_option_context_free (context);
 
   return retval;
@@ -1068,13 +1169,6 @@ gtk_init_check_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof
 
 #endif
 
-void
-gtk_exit (gint errorcode)
-{
-  exit (errorcode);
-}
-
-
 /**
  * gtk_set_locale:
  *
@@ -1191,8 +1285,6 @@ void
 gtk_main (void)
 {
   GList *tmp_list;
-  GList *functions;
-  GtkInitFunction *init;
   GMainLoop *loop;
 
   gtk_main_loop_level++;
@@ -1200,19 +1292,6 @@ gtk_main (void)
   loop = g_main_loop_new (NULL, TRUE);
   main_loops = g_slist_prepend (main_loops, loop);
 
-  tmp_list = functions = init_functions;
-  init_functions = NULL;
-  
-  while (tmp_list)
-    {
-      init = tmp_list->data;
-      tmp_list = tmp_list->next;
-      
-      (* init->function) (init->data);
-      g_free (init);
-    }
-  g_list_free (functions);
-
   if (g_main_loop_is_running (main_loops->data))
     {
       GDK_THREADS_LEAVE ();
@@ -1328,12 +1407,10 @@ gtk_main_iteration_do (gboolean blocking)
 
 /* private libgtk to libgdk interfaces
  */
-gboolean gdk_pointer_grab_info_libgtk_only  (GdkDisplay *display,
-                                            GdkWindow **grab_window,
-                                            gboolean   *owner_events);
-gboolean gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display,
-                                            GdkWindow **grab_window,
-                                            gboolean   *owner_events);
+gboolean gdk_device_grab_info_libgtk_only (GdkDisplay  *display,
+                                           GdkDevice   *device,
+                                           GdkWindow  **grab_window,
+                                           gboolean    *owner_events);
 
 static void
 rewrite_events_translate (GdkWindow *old_window,
@@ -1408,6 +1485,7 @@ rewrite_event_for_grabs (GdkEvent *event)
   gpointer grab_widget_ptr;
   gboolean owner_events;
   GdkDisplay *display;
+  GdkDevice *device;
 
   switch (event->type)
     {
@@ -1419,20 +1497,15 @@ rewrite_event_for_grabs (GdkEvent *event)
     case GDK_MOTION_NOTIFY:
     case GDK_PROXIMITY_IN:
     case GDK_PROXIMITY_OUT:
-      display = gdk_drawable_get_display (event->proximity.window);
-      if (!gdk_pointer_grab_info_libgtk_only (display, &grab_window, &owner_events) ||
-         !owner_events)
-       return NULL;
-      break;
-
     case GDK_KEY_PRESS:
     case GDK_KEY_RELEASE:
-      display = gdk_drawable_get_display (event->key.window);
-      if (!gdk_keyboard_grab_info_libgtk_only (display, &grab_window, &owner_events) ||
+      display = gdk_window_get_display (event->any.window);
+      device = gdk_event_get_device (event);
+
+      if (!gdk_device_grab_info_libgtk_only (display, device, &grab_window, &owner_events) ||
          !owner_events)
-       return NULL;
+        return NULL;
       break;
-
     default:
       return NULL;
     }
@@ -1452,9 +1525,10 @@ void
 gtk_main_do_event (GdkEvent *event)
 {
   GtkWidget *event_widget;
-  GtkWidget *grab_widget;
+  GtkWidget *grab_widget = NULL;
   GtkWindowGroup *window_group;
   GdkEvent *rewritten_event = NULL;
+  GdkDevice *device;
   GList *tmp_list;
 
   if (event->type == GDK_SETTING)
@@ -1501,33 +1575,42 @@ gtk_main_do_event (GdkEvent *event)
       event = rewritten_event;
       event_widget = gtk_get_event_widget (event);
     }
-  
+
   window_group = gtk_main_get_window_group (event_widget);
+  device = gdk_event_get_device (event);
 
-  /* Push the event onto a stack of current events for
-   * gtk_current_event_get().
+  /* check whether there is a (device) grab in effect...
    */
-  current_events = g_list_prepend (current_events, event);
+  if (device)
+    grab_widget = gtk_window_group_get_current_device_grab (window_group, device);
 
-  /* If there is a grab in effect...
+  if (!grab_widget)
+    grab_widget = gtk_window_group_get_current_grab (window_group);
+
+  /* If the grab widget is an ancestor of the event widget
+   *  then we send the event to the original event widget.
+   *  This is the key to implementing modality.
    */
-  if (window_group->grabs)
-    {
-      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.
-       *  This is the key to implementing modality.
-       */
-      if (gtk_widget_is_sensitive (event_widget) &&
-         gtk_widget_is_ancestor (event_widget, grab_widget))
-       grab_widget = event_widget;
-    }
-  else
+  if (!grab_widget ||
+      (gtk_widget_is_sensitive (event_widget) &&
+       gtk_widget_is_ancestor (event_widget, grab_widget)))
+    grab_widget = event_widget;
+
+  /* If the widget receiving events is actually blocked by another device GTK+ grab */
+  if (device &&
+      _gtk_window_group_widget_is_blocked_for_device (window_group, grab_widget, device))
     {
-      grab_widget = event_widget;
+      if (rewritten_event)
+        gdk_event_free (rewritten_event);
+
+      return;
     }
 
+  /* Push the event onto a stack of current events for
+   * gtk_current_event_get().
+   */
+  current_events = g_list_prepend (current_events, event);
+
   /* Not all events get sent to the grabbing widget.
    * The delete, destroy, expose, focus change and resize
    *  events still get sent to the event widget because
@@ -1545,7 +1628,7 @@ gtk_main_do_event (GdkEvent *event)
       
     case GDK_DELETE:
       g_object_ref (event_widget);
-      if ((!window_group->grabs || gtk_widget_get_toplevel (window_group->grabs->data) == event_widget) &&
+      if ((!gtk_window_group_get_current_grab (window_group) || gtk_widget_get_toplevel (gtk_window_group_get_current_grab (window_group)) == event_widget) &&
          !gtk_widget_event (event_widget, event))
        gtk_widget_destroy (event_widget);
       g_object_unref (event_widget);
@@ -1555,11 +1638,11 @@ gtk_main_do_event (GdkEvent *event)
       /* Unexpected GDK_DESTROY from the outside, ignore for
        * child windows, handle like a GDK_DELETE for toplevels
        */
-      if (!event_widget->parent)
+      if (!gtk_widget_get_parent (event_widget))
        {
          g_object_ref (event_widget);
          if (!gtk_widget_event (event_widget, event) &&
-             GTK_WIDGET_REALIZED (event_widget))
+             gtk_widget_get_realized (event_widget))
            gtk_widget_destroy (event_widget);
          g_object_unref (event_widget);
        }
@@ -1576,7 +1659,7 @@ gtk_main_do_event (GdkEvent *event)
        {
          /* The app may paint with a previously allocated cairo_t,
             which will draw directly to the window. We can't catch cairo
-            drap operatoins to automatically flush the window, thus we
+            draw operations to automatically flush the window, thus we
             need to explicitly flush any outstanding moves or double
             buffering */
          gdk_window_flush (event->any.window);
@@ -1585,7 +1668,6 @@ gtk_main_do_event (GdkEvent *event)
       break;
 
     case GDK_PROPERTY_NOTIFY:
-    case GDK_NO_EXPOSE:
     case GDK_FOCUS_CHANGE:
     case GDK_CONFIGURE:
     case GDK_MAP:
@@ -1618,7 +1700,7 @@ gtk_main_do_event (GdkEvent *event)
       /* Catch alt press to enable auto-mnemonics;
        * menus are handled elsewhere
        */
-      if ((event->key.keyval == GDK_Alt_L || event->key.keyval == GDK_Alt_R) &&
+      if ((event->key.keyval == GDK_KEY_Alt_L || event->key.keyval == GDK_KEY_Alt_R) &&
           !GTK_IS_MENU_SHELL (grab_widget))
         {
           gboolean auto_mnemonics;
@@ -1648,14 +1730,17 @@ gtk_main_do_event (GdkEvent *event)
       break;
       
     case GDK_ENTER_NOTIFY:
-      GTK_PRIVATE_SET_FLAG (event_widget, GTK_HAS_POINTER);
-      _gtk_widget_set_pointer_window (event_widget, event->any.window);
+      _gtk_widget_set_device_window (event_widget,
+                                     gdk_event_get_device (event),
+                                     event->any.window);
       if (gtk_widget_is_sensitive (grab_widget))
        gtk_widget_event (grab_widget, event);
       break;
       
     case GDK_LEAVE_NOTIFY:
-      GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_HAS_POINTER);
+      _gtk_widget_set_device_window (event_widget,
+                                     gdk_event_get_device (event),
+                                     NULL);
       if (gtk_widget_is_sensitive (grab_widget))
        gtk_widget_event (grab_widget, event);
       break;
@@ -1730,16 +1815,73 @@ typedef struct
   gboolean   was_grabbed;
   gboolean   is_grabbed;
   gboolean   from_grab;
+  GList     *notified_windows;
+  GdkDevice *device;
 } GrabNotifyInfo;
 
+static void
+synth_crossing_for_grab_notify (GtkWidget       *from,
+                                GtkWidget       *to,
+                                GrabNotifyInfo  *info,
+                                GList           *devices,
+                                GdkCrossingMode  mode)
+{
+  while (devices)
+    {
+      GdkDevice *device = devices->data;
+      GdkWindow *from_window, *to_window;
+
+      /* Do not propagate events more than once to
+       * the same windows if non-multidevice aware.
+       */
+      if (!from)
+        from_window = NULL;
+      else
+        {
+          from_window = _gtk_widget_get_device_window (from, device);
+
+          if (from_window &&
+              !gdk_window_get_support_multidevice (from_window) &&
+              g_list_find (info->notified_windows, from_window))
+            from_window = NULL;
+        }
+
+      if (!to)
+        to_window = NULL;
+      else
+        {
+          to_window = _gtk_widget_get_device_window (to, device);
+
+          if (to_window &&
+              !gdk_window_get_support_multidevice (to_window) &&
+              g_list_find (info->notified_windows, to_window))
+            to_window = NULL;
+        }
+
+      if (from_window || to_window)
+        {
+          _gtk_widget_synthesize_crossing ((from_window) ? from : NULL,
+                                           (to_window) ? to : NULL,
+                                           device, mode);
+
+          if (from_window)
+            info->notified_windows = g_list_prepend (info->notified_windows, from_window);
+
+          if (to_window)
+            info->notified_windows = g_list_prepend (info->notified_windows, to_window);
+        }
+
+      devices = devices->next;
+    }
+}
+
 static void
 gtk_grab_notify_foreach (GtkWidget *child,
                         gpointer   data)
-                        
 {
   GrabNotifyInfo *info = data;
   gboolean was_grabbed, is_grabbed, was_shadowed, is_shadowed;
+  GList *devices;
 
   was_grabbed = info->was_grabbed;
   is_grabbed = info->is_grabbed;
@@ -1754,42 +1896,55 @@ gtk_grab_notify_foreach (GtkWidget *child,
 
   if ((was_shadowed || is_shadowed) && GTK_IS_CONTAINER (child))
     gtk_container_forall (GTK_CONTAINER (child), gtk_grab_notify_foreach, info);
-  
+
+  if (info->device &&
+      _gtk_widget_get_device_window (child, info->device))
+    {
+      /* Device specified and is on widget */
+      devices = g_list_prepend (NULL, info->device);
+    }
+  else
+    devices = _gtk_widget_list_devices (child);
+
   if (is_shadowed)
     {
-      GTK_PRIVATE_SET_FLAG (child, GTK_SHADOWED);
-      if (!was_shadowed && GTK_WIDGET_HAS_POINTER (child)
-         && gtk_widget_is_sensitive (child))
-       _gtk_widget_synthesize_crossing (child, info->new_grab_widget,
-                                        GDK_CROSSING_GTK_GRAB);
+      _gtk_widget_set_shadowed (child, TRUE);
+      if (!was_shadowed && devices &&
+         gtk_widget_is_sensitive (child))
+        synth_crossing_for_grab_notify (child, info->new_grab_widget,
+                                        info, devices,
+                                        GDK_CROSSING_GTK_GRAB);
     }
   else
     {
-      GTK_PRIVATE_UNSET_FLAG (child, GTK_SHADOWED);
-      if (was_shadowed && GTK_WIDGET_HAS_POINTER (child)
-         && gtk_widget_is_sensitive (child))
-       _gtk_widget_synthesize_crossing (info->old_grab_widget, child,
-                                        info->from_grab ? GDK_CROSSING_GTK_GRAB
-                                        : GDK_CROSSING_GTK_UNGRAB);
+      _gtk_widget_set_shadowed (child, FALSE);
+      if (was_shadowed && devices &&
+          gtk_widget_is_sensitive (child))
+        synth_crossing_for_grab_notify (info->old_grab_widget, child,
+                                        info, devices,
+                                        info->from_grab ? GDK_CROSSING_GTK_GRAB :
+                                        GDK_CROSSING_GTK_UNGRAB);
     }
 
   if (was_shadowed != is_shadowed)
     _gtk_widget_grab_notify (child, was_shadowed);
-  
+
   g_object_unref (child);
-  
+  g_list_free (devices);
+
   info->was_grabbed = was_grabbed;
   info->is_grabbed = is_grabbed;
 }
 
 static void
 gtk_grab_notify (GtkWindowGroup *group,
+                 GdkDevice      *device,
                 GtkWidget      *old_grab_widget,
                 GtkWidget      *new_grab_widget,
                 gboolean        from_grab)
 {
   GList *toplevels;
-  GrabNotifyInfo info;
+  GrabNotifyInfo info = { 0 };
 
   if (old_grab_widget == new_grab_widget)
     return;
@@ -1797,12 +1952,13 @@ gtk_grab_notify (GtkWindowGroup *group,
   info.old_grab_widget = old_grab_widget;
   info.new_grab_widget = new_grab_widget;
   info.from_grab = from_grab;
+  info.device = device;
 
   g_object_ref (group);
 
   toplevels = gtk_window_list_toplevels ();
   g_list_foreach (toplevels, (GFunc)g_object_ref, NULL);
-                           
+
   while (toplevels)
     {
       GtkWindow *toplevel = toplevels->data;
@@ -1816,6 +1972,7 @@ gtk_grab_notify (GtkWindowGroup *group,
       g_object_unref (toplevel);
     }
 
+  g_list_free (info.notified_windows);
   g_object_unref (group);
 }
 
@@ -1829,22 +1986,27 @@ gtk_grab_add (GtkWidget *widget)
   
   if (!gtk_widget_has_grab (widget) && gtk_widget_is_sensitive (widget))
     {
-      GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
-      
+      _gtk_widget_set_has_grab (widget, TRUE);
+
       group = gtk_main_get_window_group (widget);
 
-      if (group->grabs)
-       old_grab_widget = (GtkWidget *)group->grabs->data;
-      else
-       old_grab_widget = NULL;
+      old_grab_widget = gtk_window_group_get_current_grab (group);
 
       g_object_ref (widget);
-      group->grabs = g_slist_prepend (group->grabs, widget);
+      _gtk_window_group_add_grab (group, widget);
 
-      gtk_grab_notify (group, old_grab_widget, widget, TRUE);
+      gtk_grab_notify (group, NULL, old_grab_widget, widget, TRUE);
     }
 }
 
+/**
+ * gtk_grab_get_current:
+ *
+ * Queries the current grab of the default window group.
+ *
+ * Return value: (transfer none): The widget which currently
+ *     has the grab or %NULL if no grab is active
+ */
 GtkWidget*
 gtk_grab_get_current (void)
 {
@@ -1852,9 +2014,7 @@ gtk_grab_get_current (void)
 
   group = gtk_main_get_window_group (NULL);
 
-  if (group->grabs)
-    return GTK_WIDGET (group->grabs->data);
-  return NULL;
+  return gtk_window_group_get_current_grab (group);
 }
 
 void
@@ -1867,33 +2027,76 @@ gtk_grab_remove (GtkWidget *widget)
   
   if (gtk_widget_has_grab (widget))
     {
-      GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
+      _gtk_widget_set_has_grab (widget, FALSE);
 
       group = gtk_main_get_window_group (widget);
-      group->grabs = g_slist_remove (group->grabs, widget);
-      
-      if (group->grabs)
-       new_grab_widget = (GtkWidget *)group->grabs->data;
-      else
-       new_grab_widget = NULL;
+      _gtk_window_group_remove_grab (group, widget);
+      new_grab_widget = gtk_window_group_get_current_grab (group);
+
+      gtk_grab_notify (group, NULL, widget, new_grab_widget, FALSE);
 
-      gtk_grab_notify (group, widget, new_grab_widget, FALSE);
-      
       g_object_unref (widget);
     }
 }
 
+/**
+ * gtk_device_grab_add:
+ * @widget: a #GtkWidget
+ * @device: a #GtkDevice to grab on.
+ * @block_others: %TRUE to prevent other devices to interact with @widget.
+ *
+ * Adds a GTK+ grab on @device, so all the events on @device and its
+ * associated pointer or keyboard (if any) are delivered to @widget.
+ * If the @block_others parameter is %TRUE, any other devices will be
+ * unable to interact with @widget during the grab.
+ *
+ * Since: 3.0
+ **/
 void
-gtk_init_add (GtkFunction function,
-             gpointer    data)
+gtk_device_grab_add (GtkWidget        *widget,
+                     GdkDevice        *device,
+                     gboolean          block_others)
 {
-  GtkInitFunction *init;
-  
-  init = g_new (GtkInitFunction, 1);
-  init->function = function;
-  init->data = data;
-  
-  init_functions = g_list_prepend (init_functions, init);
+  GtkWindowGroup *group;
+  GtkWidget *old_grab_widget;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  group = gtk_main_get_window_group (widget);
+  old_grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+  if (old_grab_widget != widget)
+    _gtk_window_group_add_device_grab (group, widget, device, block_others);
+
+  gtk_grab_notify (group, device, old_grab_widget, widget, TRUE);
+}
+
+/**
+ * gtk_device_grab_remove:
+ * @widget: a #GtkWidget
+ * @device: a #GdkDevice
+ *
+ * Removes a device grab from the given widget. You have to pair calls
+ * to gtk_device_grab_add() and gtk_device_grab_remove().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_device_grab_remove (GtkWidget *widget,
+                        GdkDevice *device)
+{
+  GtkWindowGroup *group;
+  GtkWidget *new_grab_widget;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (GDK_IS_DEVICE (device));
+
+  group = gtk_main_get_window_group (widget);
+  _gtk_window_group_remove_device_grab (group, widget, device);
+  new_grab_widget = gtk_window_group_get_current_device_grab (group, device);
+
+  gtk_grab_notify (group, device, widget, new_grab_widget, FALSE);
 }
 
 guint
@@ -1992,25 +2195,25 @@ gtk_quit_destroy (GtkQuitFunction *quitf)
 }
 
 static gint
-gtk_quit_destructor (GtkObject **object_p)
+gtk_quit_destructor (GtkWidget **object_p)
 {
   if (*object_p)
-    gtk_object_destroy (*object_p);
+    gtk_widget_destroy (*object_p);
   g_free (object_p);
 
   return FALSE;
 }
 
 void
-gtk_quit_add_destroy (guint              main_level,
-                     GtkObject         *object)
+gtk_quit_add_destroy (guint      main_level,
+                     GtkWidget *object)
 {
-  GtkObject **object_p;
+  GtkWidget **object_p;
 
   g_return_if_fail (main_level > 0);
-  g_return_if_fail (GTK_IS_OBJECT (object));
+  g_return_if_fail (GTK_IS_WIDGET (object));
 
-  object_p = g_new (GtkObject*, 1);
+  object_p = g_new (GtkWidget*, 1);
   *object_p = object;
   g_signal_connect (object,
                    "destroy",
@@ -2075,175 +2278,6 @@ gtk_quit_remove_by_data (gpointer data)
     }
 }
 
-guint
-gtk_timeout_add_full (guint32           interval,
-                     GtkFunction        function,
-                     GtkCallbackMarshal marshal,
-                     gpointer           data,
-                     GDestroyNotify     destroy)
-{
-  if (marshal)
-    {
-      GtkClosure *closure;
-
-      closure = g_new (GtkClosure, 1);
-      closure->marshal = marshal;
-      closure->data = data;
-      closure->destroy = destroy;
-
-      return g_timeout_add_full (0, interval, 
-                                gtk_invoke_idle_timeout,
-                                closure,
-                                gtk_destroy_closure);
-    }
-  else
-    return g_timeout_add_full (0, interval, function, data, destroy);
-}
-
-guint
-gtk_timeout_add (guint32     interval,
-                GtkFunction function,
-                gpointer    data)
-{
-  return g_timeout_add_full (0, interval, function, data, NULL);
-}
-
-void
-gtk_timeout_remove (guint tag)
-{
-  g_source_remove (tag);
-}
-
-guint
-gtk_idle_add_full (gint                        priority,
-                  GtkFunction          function,
-                  GtkCallbackMarshal   marshal,
-                  gpointer             data,
-                  GDestroyNotify       destroy)
-{
-  if (marshal)
-    {
-      GtkClosure *closure;
-
-      closure = g_new (GtkClosure, 1);
-      closure->marshal = marshal;
-      closure->data = data;
-      closure->destroy = destroy;
-
-      return g_idle_add_full (priority,
-                             gtk_invoke_idle_timeout,
-                             closure,
-                             gtk_destroy_closure);
-    }
-  else
-    return g_idle_add_full (priority, function, data, destroy);
-}
-
-guint
-gtk_idle_add (GtkFunction function,
-             gpointer    data)
-{
-  return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, function, data, NULL);
-}
-
-guint      
-gtk_idle_add_priority (gint        priority,
-                      GtkFunction function,
-                      gpointer    data)
-{
-  return g_idle_add_full (priority, function, data, NULL);
-}
-
-void
-gtk_idle_remove (guint tag)
-{
-  g_source_remove (tag);
-}
-
-void
-gtk_idle_remove_by_data (gpointer data)
-{
-  if (!g_idle_remove_by_data (data))
-    g_warning ("gtk_idle_remove_by_data(%p): no such idle", data);
-}
-
-guint
-gtk_input_add_full (gint               source,
-                   GdkInputCondition   condition,
-                   GdkInputFunction    function,
-                   GtkCallbackMarshal  marshal,
-                   gpointer            data,
-                   GDestroyNotify      destroy)
-{
-  if (marshal)
-    {
-      GtkClosure *closure;
-
-      closure = g_new (GtkClosure, 1);
-      closure->marshal = marshal;
-      closure->data = data;
-      closure->destroy = destroy;
-
-      return gdk_input_add_full (source,
-                                condition,
-                                (GdkInputFunction) gtk_invoke_input,
-                                closure,
-                                (GDestroyNotify) gtk_destroy_closure);
-    }
-  else
-    return gdk_input_add_full (source, condition, function, data, destroy);
-}
-
-void
-gtk_input_remove (guint tag)
-{
-  g_source_remove (tag);
-}
-
-static void
-gtk_destroy_closure (gpointer data)
-{
-  GtkClosure *closure = data;
-
-  if (closure->destroy)
-    (closure->destroy) (closure->data);
-  g_free (closure);
-}
-
-static gboolean
-gtk_invoke_idle_timeout (gpointer data)
-{
-  GtkClosure *closure = data;
-
-  GtkArg args[1];
-  gint ret_val = FALSE;
-  args[0].name = NULL;
-  args[0].type = G_TYPE_BOOLEAN;
-  args[0].d.pointer_data = &ret_val;
-  closure->marshal (NULL, closure->data,  0, args);
-  return ret_val;
-}
-
-static void
-gtk_invoke_input (gpointer         data,
-                 gint              source,
-                 GdkInputCondition condition)
-{
-  GtkClosure *closure = data;
-
-  GtkArg args[3];
-  args[0].type = G_TYPE_INT;
-  args[0].name = NULL;
-  GTK_VALUE_INT (args[0]) = source;
-  args[1].type = GDK_TYPE_INPUT_CONDITION;
-  args[1].name = NULL;
-  GTK_VALUE_FLAGS (args[1]) = condition;
-  args[2].type = G_TYPE_NONE;
-  args[2].name = NULL;
-
-  closure->marshal (NULL, closure->data, 2, args);
-}
-
 /**
  * gtk_get_current_event:
  * 
@@ -2305,6 +2339,23 @@ gtk_get_current_event_state (GdkModifierType *state)
     }
 }
 
+/**
+ * gtk_get_current_event_device:
+ *
+ * If there is a current event and it has a device, return that
+ * device, otherwise return %NULL.
+ *
+ * Returns: (transfer none): a #GdkDevice, or %NULL
+ **/
+GdkDevice *
+gtk_get_current_event_device (void)
+{
+  if (current_events)
+    return gdk_event_get_device (current_events->data);
+  else
+    return NULL;
+}
+
 /**
  * gtk_get_event_widget:
  * @event: a #GdkEvent
@@ -2313,7 +2364,8 @@ gtk_get_current_event_state (GdkModifierType *state)
  * returns %NULL, otherwise returns the widget that received the event
  * originally.
  * 
- * Return value: the widget that originally received @event, or %NULL
+ * Return value: (transfer none): the widget that originally
+ *     received @event, or %NULL
  **/
 GtkWidget*
 gtk_get_event_widget (GdkEvent *event)
@@ -2323,7 +2375,7 @@ gtk_get_event_widget (GdkEvent *event)
 
   widget = NULL;
   if (event && event->any.window && 
-      (event->type == GDK_DESTROY || !GDK_WINDOW_DESTROYED (event->any.window)))
+      (event->type == GDK_DESTROY || !gdk_window_is_destroyed (event->any.window)))
     {
       gdk_window_get_user_data (event->any.window, &widget_ptr);
       widget = widget_ptr;
@@ -2440,8 +2492,8 @@ gtk_propagate_event (GtkWidget *widget,
            handled_event = event->type != GDK_SCROLL;
          else
            handled_event = gtk_widget_event (widget, event);
-             
-         tmp = widget->parent;
+
+          tmp = gtk_widget_get_parent (widget);
          g_object_unref (widget);
 
          widget = tmp;
@@ -2456,119 +2508,6 @@ gtk_propagate_event (GtkWidget *widget,
     g_object_unref (widget);
 }
 
-#if 0
-static void
-gtk_error (gchar *str)
-{
-  gtk_print (str);
-}
-
-static void
-gtk_warning (gchar *str)
-{
-  gtk_print (str);
-}
-
-static void
-gtk_message (gchar *str)
-{
-  gtk_print (str);
-}
-
-static void
-gtk_print (gchar *str)
-{
-  static GtkWidget *window = NULL;
-  static GtkWidget *text;
-  static int level = 0;
-  GtkWidget *box1;
-  GtkWidget *box2;
-  GtkWidget *table;
-  GtkWidget *hscrollbar;
-  GtkWidget *vscrollbar;
-  GtkWidget *separator;
-  GtkWidget *button;
-  
-  if (level > 0)
-    {
-      fputs (str, stdout);
-      fflush (stdout);
-      return;
-    }
-  
-  if (!window)
-    {
-      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-      
-      gtk_signal_connect (GTK_OBJECT (window), "destroy",
-                         G_CALLBACK (gtk_widget_destroyed),
-                         &window);
-      
-      gtk_window_set_title (GTK_WINDOW (window), "Messages");
-      
-      box1 = gtk_vbox_new (FALSE, 0);
-      gtk_container_add (GTK_CONTAINER (window), box1);
-      gtk_widget_show (box1);
-      
-      
-      box2 = gtk_vbox_new (FALSE, 10);
-      gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
-      gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
-      gtk_widget_show (box2);
-      
-      
-      table = gtk_table_new (2, 2, FALSE);
-      gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
-      gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
-      gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
-      gtk_widget_show (table);
-      
-      text = gtk_text_new (NULL, NULL);
-      gtk_text_set_editable (GTK_TEXT (text), FALSE);
-      gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
-      gtk_widget_show (text);
-      gtk_widget_realize (text);
-      
-      hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
-      gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
-                       GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
-      gtk_widget_show (hscrollbar);
-      
-      vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
-      gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
-                       GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
-      gtk_widget_show (vscrollbar);
-      
-      separator = gtk_hseparator_new ();
-      gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
-      gtk_widget_show (separator);
-      
-      
-      box2 = gtk_vbox_new (FALSE, 10);
-      gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
-      gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
-      gtk_widget_show (box2);
-      
-      
-      button = gtk_button_new_with_label ("close");
-      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
-                                G_CALLBACK (gtk_widget_hide),
-                                GTK_OBJECT (window));
-      gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
-      gtk_widget_set_can_default (button, TRUE);
-      gtk_widget_grab_default (button);
-      gtk_widget_show (button);
-    }
-  
-  level += 1;
-  gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
-  level -= 1;
-  
-  if (!gtk_widget_get_visible (window))
-    gtk_widget_show (window);
-}
-#endif
-
 gboolean
 _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
                                  GValue                *return_accu,
@@ -2584,6 +2523,3 @@ _gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint,
   
   return continue_emission;
 }
-
-#define __GTK_MAIN_C__
-#include "gtkaliasdef.c"