[gnome-shell] Install a GDK event handler to catch events queued by IBus



commit 870be026d8f46c985484e62e32f217d6782d54f3
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Fri Sep 10 18:07:17 2010 -0400

    Install a GDK event handler to catch events queued by IBus
    
    With IBus, key events sometimes get sent to the server than
    redelivered via gdk_event_put(). Since we expect all key events to
    be delivered via the GDK event filter Mutter installs, this results
    in key input not working.
    
    To fix this, install an event handler with gdk_event_handler_set()
    to intercept events being set from GDK to GTK+, pull out the
    key events and send them back to Clutter.
    
    Partially based on a patch by Daiki Ueno
    
    https://bugzilla.gnome.org/show_bug.cgi?id=621659

 src/gnome-shell-plugin.c |   95 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 95 insertions(+), 0 deletions(-)
---
diff --git a/src/gnome-shell-plugin.c b/src/gnome-shell-plugin.c
index ad2c4e6..c77cbda 100644
--- a/src/gnome-shell-plugin.c
+++ b/src/gnome-shell-plugin.c
@@ -301,6 +301,99 @@ add_statistics (GnomeShellPlugin *shell_plugin)
                                           NULL, NULL);
 }
 
+/* This is an IBus workaround. The flow of events with IBus is that every time
+ * it gets gets a key event, it:
+ *
+ *  Sends it to the daemon via D-Bus asynchronously
+ *  When it gets an reply, synthesizes a new GdkEvent and puts it into the
+ *   GDK event queue with gdk_event_put(), including
+ *   IBUS_FORWARD_MASK = 1 << 25 in the state to prevent a loop.
+ *
+ * (Normally, IBus uses the GTK+ key snooper mechanism to get the key
+ * events early, but since our key events aren't visible to GTK+ key snoopers,
+ * IBus will instead get the events via the standard
+ * GtkIMContext.filter_keypress() mechanism.)
+ *
+ * There are a number of potential problems here; probably the worst
+ * problem is that IBus doesn't forward the timestamp with the event
+ * so that every key event that gets delivered ends up with
+ * GDK_CURRENT_TIME.  This creates some very subtle bugs; for example
+ * if you have IBus running and a keystroke is used to trigger
+ * launching an application, focus stealing prevention won't work
+ * right. http://code.google.com/p/ibus/issues/detail?id=1184
+ *
+ * In any case, our normal flow of key events is:
+ *
+ *  GDK filter function => clutter_x11_handle_event => clutter actor
+ *
+ * So, if we see a key event that gets delivered via the GDK event handler
+ * function - then we know it must be one of these synthesized events, and
+ * we should push it back to clutter.
+ *
+ * To summarize, the full key event flow with IBus is:
+ *
+ *   GDK filter function
+ *     => Mutter
+ *     => gnome_shell_plugin_xevent_filter()
+ *     => clutter_x11_handle_event()
+ *     => clutter event delivery to actor
+ *     => gtk_im_context_filter_event()
+ *     => sent to IBus daemon
+ *     => response received from IBus daemon
+ *     => gdk_event_put()
+ *     => GDK event handler
+ *     => <this function>
+ *     => clutter_event_put()
+ *     => clutter event delivery to actor
+ *
+ * Anything else we see here we just pass on to the normal GDK event handler
+ * gtk_main_do_event().
+ */
+static void
+gnome_shell_gdk_event_handler (GdkEvent *event_gdk,
+                               gpointer  data)
+{
+  if (event_gdk->type == GDK_KEY_PRESS || event_gdk->type == GDK_KEY_RELEASE)
+    {
+      ClutterActor *stage;
+      Window stage_xwindow;
+
+      stage = clutter_stage_get_default ();
+      stage_xwindow = clutter_x11_get_stage_window (CLUTTER_STAGE (stage));
+
+      if (GDK_WINDOW_XID (event_gdk->key.window) == stage_xwindow)
+        {
+          ClutterDeviceManager *device_manager = clutter_device_manager_get_default ();
+          ClutterInputDevice *keyboard = clutter_device_manager_get_core_device (device_manager,
+                                                                                 CLUTTER_KEYBOARD_DEVICE);
+
+          ClutterEvent *event_clutter = clutter_event_new ((event_gdk->type == GDK_KEY_RELEASE) ?
+                                                           CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE);
+          event_clutter->key.time = event_gdk->key.time;
+          event_clutter->key.flags = CLUTTER_EVENT_NONE;
+          event_clutter->key.stage = CLUTTER_STAGE (stage);
+          event_clutter->key.source = NULL;
+
+          /* This depends on ClutterModifierType and GdkModifierType being
+           * identical, which they are currently. (They both match the X
+           * modifier state in the low 16-bits and have the same extensions.) */
+          event_clutter->key.modifier_state = event_gdk->key.state;
+
+          event_clutter->key.keyval = event_gdk->key.keyval;
+          event_clutter->key.hardware_keycode = event_gdk->key.hardware_keycode;
+          event_clutter->key.unicode_value = gdk_keyval_to_unicode (event_clutter->key.keyval);
+          event_clutter->key.device = keyboard;
+
+          clutter_event_put (event_clutter);
+          clutter_event_free (event_clutter);
+
+          return;
+        }
+    }
+
+  gtk_main_do_event (event_gdk);
+}
+
 static void
 muted_log_handler (const char     *log_domain,
                    GLogLevelFlags  log_level,
@@ -340,6 +433,8 @@ gnome_shell_plugin_start (MetaPlugin *plugin)
                     NULL);
   update_font_options (settings);
 
+  gdk_event_handler_set (gnome_shell_gdk_event_handler, plugin, NULL);
+
   screen = meta_plugin_get_screen (plugin);
   display = meta_screen_get_display (screen);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]