From d165aced4eb6d2d0cd4d26ecc20ac35a736c17b1 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <ueno@unixuser.org>
Date: Tue, 13 Jul 2010 18:40:39 +0900
Subject: [PATCH] Make keyboard usable again with IBus.

Some GtkIMContext implementations may send GDK key events asynchronously with
gdk_event_put().  This patch creates GtkInvisible to dispatch these events.

Also, the return value of shell_get_event_state() needed to be changed to
guint from ClutterModifierType since enum may be signed and gjs throws type
error when passing it to say meta_display_get_keybinding_action().

Signed-off-by: Daiki Ueno <ueno@localhost.localdomain>
---
 src/shell-global.c  |    2 +-
 src/shell-global.h  |    2 +-
 src/st/st-im-text.c |   78 +++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/src/shell-global.c b/src/shell-global.c
index 94b57c5..0181f58 100644
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -1290,7 +1290,7 @@ shell_global_get_pointer (ShellGlobal         *global,
  *
  * Return value: the state from the event
  */
-ClutterModifierType
+guint
 shell_get_event_state (ClutterEvent *event)
 {
   ClutterModifierType state = clutter_event_get_state (event);
diff --git a/src/shell-global.h b/src/shell-global.h
index 1dd0be8..0b8cf32 100644
--- a/src/shell-global.h
+++ b/src/shell-global.h
@@ -91,7 +91,7 @@ void shell_global_get_pointer (ShellGlobal         *global,
 
 GSettings *shell_global_get_settings (ShellGlobal *global);
 
-ClutterModifierType shell_get_event_state (ClutterEvent *event);
+guint shell_get_event_state (ClutterEvent *event);
 
 void shell_popup_menu (GtkMenu *menu, int button, guint32 time,
                        int menu_x, int menu_y);
diff --git a/src/st/st-im-text.c b/src/st/st-im-text.c
index 29fd71e..eb3f9b1 100644
--- a/src/st/st-im-text.c
+++ b/src/st/st-im-text.c
@@ -61,6 +61,7 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
 #include <X11/extensions/XKB.h>
 
 #include "st-im-text.h"
@@ -71,7 +72,7 @@
 struct _StIMTextPrivate
 {
   GtkIMContext *im_context;
-  GdkWindow *window;
+  GtkWidget *invisible;
 
   guint need_im_reset : 1;
 };
@@ -202,13 +203,70 @@ window_for_actor (ClutterActor *actor)
   return window;
 }
 
+static ClutterKeyEvent *
+key_event_from_gdk (StIMText *self, GdkEventKey *event_gdk)
+{
+  ClutterKeyEvent *event_clutter;
+  ClutterActor *stage;
+
+  event_clutter = (ClutterKeyEvent *)clutter_event_new ((event_gdk->type == GDK_KEY_PRESS) ?
+                                                        CLUTTER_KEY_PRESS :
+                                                        CLUTTER_KEY_RELEASE);
+  event_clutter->time = event_gdk->time;
+  event_clutter->flags = CLUTTER_EVENT_NONE;
+  event_clutter->source = CLUTTER_ACTOR (self);
+  stage = clutter_actor_get_stage (event_clutter->source);
+  event_clutter->stage = CLUTTER_STAGE (stage);
+  /* 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->modifier_state = event_gdk->state;
+  event_clutter->keyval = event_gdk->keyval;
+  event_clutter->hardware_keycode = event_gdk->hardware_keycode;
+  event_clutter->unicode_value = gdk_keyval_to_unicode (event_gdk->keyval);
+  return event_clutter;
+}
+
+static gboolean
+forward_key_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
+{
+  ClutterKeyEvent *event_clutter;
+  StIMText *self = user_data;
+
+  event_clutter = key_event_from_gdk (self, event);
+  clutter_event_put ((ClutterEvent *)event_clutter);
+  clutter_event_free ((ClutterEvent *)event_clutter);
+  return TRUE;
+}
+
 static void
 st_im_text_realize (ClutterActor *actor)
 {
   StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
+  GdkScreen *screen;
+  GdkWindow *window;
+
+  window = window_for_actor (actor);
+  screen = gdk_drawable_get_screen (window);
+  g_object_unref (window);
 
-  priv->window = window_for_actor (actor);
-  gtk_im_context_set_client_window (priv->im_context, priv->window);
+  priv->invisible = gtk_invisible_new_for_screen (screen);
+  /* GtkIMContext, more specifically IBus, may send GDK key events
+   * asynchronously with gdk_event_put(), so we need to convert them
+   * to Clutter key events.  See
+   * https://bugzilla.gnome.org/show_bug.cgi?id=621659
+   */
+  g_signal_connect (priv->invisible,
+                    "key-press-event",
+                    G_CALLBACK(forward_key_event),
+                    actor);
+  g_signal_connect (priv->invisible,
+                    "key-release-event",
+                    G_CALLBACK(forward_key_event),
+                    actor);
+  gtk_widget_show_all (priv->invisible);
+  gtk_im_context_set_client_window (priv->im_context,
+                                    gtk_widget_get_window (priv->invisible));
 }
 
 static void
@@ -219,8 +277,8 @@ st_im_text_unrealize (ClutterActor *actor)
 
   reset_im_context (self);
   gtk_im_context_set_client_window (priv->im_context, NULL);
-  g_object_unref (priv->window);
-  priv->window = NULL;
+  g_object_unref (priv->invisible);
+  priv->invisible = NULL;
 }
 
 static gboolean
@@ -263,15 +321,17 @@ key_is_modifier (guint16 keyval)
 }
 
 static GdkEventKey *
-key_event_to_gdk (ClutterKeyEvent *event_clutter)
+key_event_to_gdk (StIMText *self, ClutterKeyEvent *event_clutter)
 {
   GdkDisplay *display = gdk_display_get_default ();
   GdkKeymap *keymap = gdk_keymap_get_for_display (display);
   GdkEventKey *event_gdk;
+  StIMTextPrivate *priv = self->priv;
   event_gdk = (GdkEventKey *)gdk_event_new ((event_clutter->type == CLUTTER_KEY_PRESS) ?
                                             GDK_KEY_PRESS : GDK_KEY_RELEASE);
 
-  event_gdk->window = window_for_actor ((ClutterActor *)event_clutter->stage);
+  event_gdk->window = gtk_widget_get_window (priv->invisible);
+  g_object_ref (event_gdk->window);
   event_gdk->send_event = FALSE;
   event_gdk->time = event_clutter->time;
   /* This depends on ClutterModifierType and GdkModifierType being
@@ -336,7 +396,7 @@ st_im_text_key_press_event (ClutterActor    *actor,
 
   if (clutter_text_get_editable (clutter_text))
     {
-      GdkEventKey *event_gdk = key_event_to_gdk (event);
+      GdkEventKey *event_gdk = key_event_to_gdk (self, event);
 
       if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
         {
@@ -379,7 +439,7 @@ st_im_text_key_release_event (ClutterActor    *actor,
 
   if (clutter_text_get_editable (clutter_text))
     {
-      event_gdk = key_event_to_gdk (event);
+      event_gdk = key_event_to_gdk (self, event);
 
       if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
         {
-- 
1.7.1.1

