]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkmain.c
Robustify tracking of pointer grab window.
[~andy/gtk] / gtk / gtkmain.c
index a9c0dae3c2486ad3d6fef24ece27a1db72e6cb8c..c38607a39cc66b9c588f0da5291268224d4f9b3d 100644 (file)
@@ -59,7 +59,6 @@
 #include "gtkwidget.h"
 #include "gtkwindow.h"
 #include "gtkprivate.h"
-#include "gdk/gdki18n.h"
 #include "config.h"
 #include "gtkdebug.h"
 #include "gtkintl.h"
@@ -101,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,
@@ -157,7 +155,8 @@ static const GDebugKey gtk_debug_keys[] = {
   {"plugsocket", GTK_DEBUG_PLUGSOCKET},
   {"text", GTK_DEBUG_TEXT},
   {"tree", GTK_DEBUG_TREE},
-  {"updates", GTK_DEBUG_UPDATES}
+  {"updates", GTK_DEBUG_UPDATES},
+  {"keybindings", GTK_DEBUG_KEYBINDINGS}
 };
 
 static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
@@ -284,73 +283,158 @@ _gtk_get_data_prefix (void)
 static gchar **
 get_module_path (void)
 {
-  const gchar *module_path_env = g_getenv ("GTK_MODULE_PATH");
-  const gchar *exe_prefix = g_getenv ("GTK_EXE_PREFIX");
-  gchar **result;
+  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", "modules", NULL);
+    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
-    default_dir = g_build_filename (GTK_LIBDIR, "gtk-2.0", "modules", NULL);
+    module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S,
+                               default_dir, NULL);
 
-  module_path = g_strconcat (module_path_env ? module_path_env : "",
-                            module_path_env ? 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 (default_dir);
   g_free (module_path);
 
   return result;
 }
 
-static GModule *
-find_module (gchar      **module_path,
-            const gchar *name)
+/**
+ * _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)
 {
-  GModule *module;
-  gchar *module_name;
-  gint i;
+  gchar **paths = get_module_path();
+  gchar **path;
+  gchar **result;
+  gint count = 0;
 
-  if (g_path_is_absolute (name))
-    return g_module_open (name, G_MODULE_BIND_LAZY);
+  for (path = paths; *path; path++)
+    count++;
 
-  for (i = 0; module_path[i]; i++)
-    {
-      gchar *version_directory;
+  result = g_new (gchar *, count * 4 + 1);
 
-      version_directory = g_build_filename (module_path[i], GTK_BINARY_VERSION, NULL);
-      module_name = g_module_build_path (version_directory, name);
-      g_free (version_directory);
-      
-      if (g_file_test (module_name, G_FILE_TEST_EXISTS))
-       {
-         module = g_module_open (module_name, G_MODULE_BIND_LAZY);
-         g_free (module_name);
-         return module;
-       }
+  count = 0;
+  for (path = get_module_path (); *path; path++)
+    {
+      gint use_version, use_host;
       
-      g_free (module_name);
+      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;
+         }
+    }
 
-      module_name = g_module_build_path (module_path[i], name);
-      
-      if (g_file_test (module_name, G_FILE_TEST_EXISTS))
+  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 = g_module_open (module_name, G_MODULE_BIND_LAZY);
-         g_free (module_name);
-         return module;
+         module_name = tmp_name;
+         goto found;
        }
-
-      g_free (module_name);
+      else
+       g_free(tmp_name);
     }
 
-  /* As last resort, try loading without an absolute path (using system
-   * library path)
-   */
-  module_name = g_module_build_path (NULL, 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);
 
@@ -444,7 +528,7 @@ gtk_init_check (int  *argc,
   GString *gtk_modules_string = NULL;
   GSList *gtk_modules = NULL;
   GSList *slist;
-  gchar *env_string;
+  const gchar *env_string;
 
   if (gtk_initialized)
     return TRUE;
@@ -460,7 +544,10 @@ gtk_init_check (int         *argc,
 #endif
 
   if (do_setlocale)
-    setlocale (LC_ALL, "");
+    {
+      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
@@ -630,10 +717,6 @@ gtk_init_check (int         *argc,
   _gtk_accel_map_init ();  
   _gtk_rc_init ();
   
-  /* Register an exit function to make sure we are able to cleanup.
-   */
-  g_atexit (gtk_exit_func);
-  
   /* Set the 'initialized' flag.
    */
   gtk_initialized = TRUE;
@@ -652,16 +735,6 @@ gtk_init_check (int         *argc,
     }
   g_slist_free (gtk_modules);
   
-#ifndef G_OS_WIN32
-  /* No use warning on Win32, there aren't any non-devel versions anyhow... */
-  g_message (""              "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 retrieved 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;
 }
 
@@ -714,9 +787,6 @@ 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);
 }
 
@@ -910,6 +980,121 @@ 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)
 {
@@ -917,6 +1102,7 @@ gtk_main_do_event (GdkEvent *event)
   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.
@@ -975,14 +1161,24 @@ gtk_main_do_event (GdkEvent *event)
 
       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);
 
-  window_group = gtk_main_get_window_group (event_widget);
-  
   /* If there is a grab in effect...
    */
   if (window_group->grabs)
@@ -1128,6 +1324,9 @@ gtk_main_do_event (GdkEvent *event)
   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);
 }
 
 gboolean
@@ -1733,16 +1932,6 @@ gtk_get_event_widget (GdkEvent *event)
   return widget;
 }
 
-static void
-gtk_exit_func (void)
-{
-  if (gtk_initialized)
-    {
-      gtk_initialized = FALSE;
-    }
-}
-
-
 static gint
 gtk_quit_invoke_function (GtkQuitFunction *quitf)
 {