| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.h" | 5 #include "chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.h" |
| 6 | 6 |
| 7 #include <gdk/gdk.h> | 7 #include <gdk/gdk.h> |
| 8 #include <gdk/gdkkeysyms.h> | 8 #include <gdk/gdkkeysyms.h> |
| 9 #include <gdk/gdkx.h> | 9 #include <gdk/gdkx.h> |
| 10 | 10 |
| 11 #include <gtk/gtk.h> | 11 #include <gtk/gtk.h> |
| 12 | 12 |
| 13 #include <X11/X.h> | 13 #include <X11/X.h> |
| 14 #include <X11/Xlib.h> | 14 #include <X11/Xlib.h> |
| 15 | 15 |
| 16 #include "base/event_types.h" | |
| 17 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
| 18 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 19 #include "ui/base/ime/composition_text.h" | 18 #include "ui/base/ime/composition_text.h" |
| 20 #include "ui/base/ime/composition_text_util_pango.h" | 19 #include "ui/base/ime/composition_text_util_pango.h" |
| 21 #include "ui/base/ime/text_input_client.h" | 20 #include "ui/base/ime/text_input_client.h" |
| 22 #include "ui/events/event.h" | 21 #include "ui/events/event.h" |
| 23 #include "ui/gfx/x/x11_types.h" | 22 #include "ui/gfx/x/x11_types.h" |
| 24 | 23 |
| 25 namespace { | |
| 26 | |
| 27 // Constructs a GdkEventKey from a XKeyEvent and returns it. Otherwise, | |
| 28 // returns NULL. The returned GdkEvent must be freed by gdk_event_free. | |
| 29 GdkEvent* GdkEventFromXKeyEvent(XKeyEvent& xkey, bool is_modifier) { | |
| 30 DCHECK(xkey.type == KeyPress || xkey.type == KeyRelease); | |
| 31 | |
| 32 // Get a GdkDisplay. | |
| 33 GdkDisplay* display = gdk_x11_lookup_xdisplay(xkey.display); | |
| 34 if (!display) { | |
| 35 // Fall back to the default display. | |
| 36 display = gdk_display_get_default(); | |
| 37 } | |
| 38 if (!display) { | |
| 39 LOG(ERROR) << "Cannot get a GdkDisplay for a key event."; | |
| 40 return NULL; | |
| 41 } | |
| 42 // Get a keysym and group. | |
| 43 KeySym keysym = NoSymbol; | |
| 44 guint8 keyboard_group = 0; | |
| 45 XLookupString(&xkey, NULL, 0, &keysym, NULL); | |
| 46 GdkKeymap* keymap = gdk_keymap_get_for_display(display); | |
| 47 GdkKeymapKey* keys = NULL; | |
| 48 guint* keyvals = NULL; | |
| 49 gint n_entries = 0; | |
| 50 if (keymap && | |
| 51 gdk_keymap_get_entries_for_keycode(keymap, xkey.keycode, | |
| 52 &keys, &keyvals, &n_entries)) { | |
| 53 for (gint i = 0; i < n_entries; ++i) { | |
| 54 if (keyvals[i] == keysym) { | |
| 55 keyboard_group = keys[i].group; | |
| 56 break; | |
| 57 } | |
| 58 } | |
| 59 } | |
| 60 g_free(keys); | |
| 61 keys = NULL; | |
| 62 g_free(keyvals); | |
| 63 keyvals = NULL; | |
| 64 // Get a GdkWindow. | |
| 65 GdkWindow* window = gdk_x11_window_lookup_for_display(display, xkey.window); | |
| 66 if (window) | |
| 67 g_object_ref(window); | |
| 68 else | |
| 69 window = gdk_x11_window_foreign_new_for_display(display, xkey.window); | |
| 70 if (!window) { | |
| 71 LOG(ERROR) << "Cannot get a GdkWindow for a key event."; | |
| 72 return NULL; | |
| 73 } | |
| 74 | |
| 75 // Create a GdkEvent. | |
| 76 GdkEventType event_type = xkey.type == KeyPress ? | |
| 77 GDK_KEY_PRESS : GDK_KEY_RELEASE; | |
| 78 GdkEvent* event = gdk_event_new(event_type); | |
| 79 event->key.type = event_type; | |
| 80 event->key.window = window; | |
| 81 // GdkEventKey and XKeyEvent share the same definition for time and state. | |
| 82 event->key.send_event = xkey.send_event; | |
| 83 event->key.time = xkey.time; | |
| 84 event->key.state = xkey.state; | |
| 85 event->key.keyval = keysym; | |
| 86 event->key.length = 0; | |
| 87 event->key.string = NULL; | |
| 88 event->key.hardware_keycode = xkey.keycode; | |
| 89 event->key.group = keyboard_group; | |
| 90 event->key.is_modifier = is_modifier; | |
| 91 return event; | |
| 92 } | |
| 93 | |
| 94 } // namespace | |
| 95 | |
| 96 namespace libgtk2ui { | 24 namespace libgtk2ui { |
| 97 | 25 |
| 98 X11InputMethodContextImplGtk2::X11InputMethodContextImplGtk2( | 26 X11InputMethodContextImplGtk2::X11InputMethodContextImplGtk2( |
| 99 ui::LinuxInputMethodContextDelegate* delegate) | 27 ui::LinuxInputMethodContextDelegate* delegate) |
| 100 : delegate_(delegate), | 28 : delegate_(delegate), |
| 101 gtk_context_simple_(NULL), | 29 gtk_context_simple_(NULL), |
| 102 gtk_multicontext_(NULL), | 30 gtk_multicontext_(NULL), |
| 103 gtk_context_(NULL), | 31 gtk_context_(NULL), |
| 104 gdk_last_set_client_window_(NULL) { | 32 gdk_last_set_client_window_(NULL) { |
| 105 CHECK(delegate_); | 33 CHECK(delegate_); |
| 106 | 34 |
| 107 { | 35 ResetXModifierKeycodesCache(); |
| 108 XModifierKeymap* keymap = XGetModifierMapping(gfx::GetXDisplay()); | |
| 109 for (int i = 0; i < 8 * keymap->max_keypermod; ++i) { | |
| 110 if (keymap->modifiermap[i]) | |
| 111 modifier_keycodes_.insert(keymap->modifiermap[i]); | |
| 112 } | |
| 113 XFreeModifiermap(keymap); | |
| 114 } | |
| 115 | 36 |
| 116 gtk_context_simple_ = gtk_im_context_simple_new(); | 37 gtk_context_simple_ = gtk_im_context_simple_new(); |
| 117 gtk_multicontext_ = gtk_im_multicontext_new(); | 38 gtk_multicontext_ = gtk_im_multicontext_new(); |
| 118 | 39 |
| 119 GtkIMContext* contexts[] = {gtk_context_simple_, gtk_multicontext_}; | 40 GtkIMContext* contexts[] = {gtk_context_simple_, gtk_multicontext_}; |
| 120 for (size_t i = 0; i < arraysize(contexts); ++i) { | 41 for (size_t i = 0; i < arraysize(contexts); ++i) { |
| 121 g_signal_connect(contexts[i], "commit", | 42 g_signal_connect(contexts[i], "commit", |
| 122 G_CALLBACK(OnCommitThunk), this); | 43 G_CALLBACK(OnCommitThunk), this); |
| 123 g_signal_connect(contexts[i], "preedit-changed", | 44 g_signal_connect(contexts[i], "preedit-changed", |
| 124 G_CALLBACK(OnPreeditChangedThunk), this); | 45 G_CALLBACK(OnPreeditChangedThunk), this); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 149 bool X11InputMethodContextImplGtk2::DispatchKeyEvent( | 70 bool X11InputMethodContextImplGtk2::DispatchKeyEvent( |
| 150 const ui::KeyEvent& key_event) { | 71 const ui::KeyEvent& key_event) { |
| 151 if (!key_event.HasNativeEvent()) | 72 if (!key_event.HasNativeEvent()) |
| 152 return false; | 73 return false; |
| 153 | 74 |
| 154 // The caller must call Focus() first. | 75 // The caller must call Focus() first. |
| 155 if (!gtk_context_) | 76 if (!gtk_context_) |
| 156 return false; | 77 return false; |
| 157 | 78 |
| 158 // Translate a XKeyEvent to a GdkEventKey. | 79 // Translate a XKeyEvent to a GdkEventKey. |
| 159 const base::NativeEvent& native_key_event = key_event.native_event(); | 80 GdkEvent* event = GdkEventFromNativeEvent(key_event.native_event()); |
| 160 GdkEvent* event = GdkEventFromXKeyEvent( | |
| 161 native_key_event->xkey, | |
| 162 IsKeycodeModifierKey(native_key_event->xkey.keycode)); | |
| 163 if (!event) { | 81 if (!event) { |
| 164 LOG(ERROR) << "Cannot translate a XKeyEvent to a GdkEvent."; | 82 LOG(ERROR) << "Cannot translate a XKeyEvent to a GdkEvent."; |
| 165 return false; | 83 return false; |
| 166 } | 84 } |
| 167 | 85 |
| 168 // Set the client window and cursor location. | 86 // Set the client window and cursor location. |
| 169 if (event->key.window != gdk_last_set_client_window_) { | 87 if (event->key.window != gdk_last_set_client_window_) { |
| 170 gtk_im_context_set_client_window(gtk_context_, event->key.window); | 88 gtk_im_context_set_client_window(gtk_context_, event->key.window); |
| 171 gdk_last_set_client_window_ = event->key.window; | 89 gdk_last_set_client_window_ = event->key.window; |
| 172 } | 90 } |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 // Remember the caret bounds so that we can set the cursor location later. | 138 // Remember the caret bounds so that we can set the cursor location later. |
| 221 // gtk_im_context_set_cursor_location() takes the location relative to the | 139 // gtk_im_context_set_cursor_location() takes the location relative to the |
| 222 // client window, which is unknown at this point. So we'll call | 140 // client window, which is unknown at this point. So we'll call |
| 223 // gtk_im_context_set_cursor_location() later in ProcessKeyEvent() where | 141 // gtk_im_context_set_cursor_location() later in ProcessKeyEvent() where |
| 224 // (and only where) we know the client window. | 142 // (and only where) we know the client window. |
| 225 last_caret_bounds_ = caret_bounds; | 143 last_caret_bounds_ = caret_bounds; |
| 226 } | 144 } |
| 227 | 145 |
| 228 // private: | 146 // private: |
| 229 | 147 |
| 148 void X11InputMethodContextImplGtk2::ResetXModifierKeycodesCache() { |
| 149 modifier_keycodes_.clear(); |
| 150 meta_keycodes_.clear(); |
| 151 super_keycodes_.clear(); |
| 152 hyper_keycodes_.clear(); |
| 153 |
| 154 Display* display = gfx::GetXDisplay(); |
| 155 const XModifierKeymap* modmap = XGetModifierMapping(display); |
| 156 int min_keycode = 0; |
| 157 int max_keycode = 0; |
| 158 int keysyms_per_keycode = 1; |
| 159 XDisplayKeycodes(display, &min_keycode, &max_keycode); |
| 160 const KeySym* keysyms = XGetKeyboardMapping( |
| 161 display, min_keycode, max_keycode - min_keycode + 1, |
| 162 &keysyms_per_keycode); |
| 163 for (int i = 0; i < 8 * modmap->max_keypermod; ++i) { |
| 164 const int keycode = modmap->modifiermap[i]; |
| 165 if (!keycode) |
| 166 continue; |
| 167 modifier_keycodes_.insert(keycode); |
| 168 |
| 169 if (!keysyms) |
| 170 continue; |
| 171 for (int j = 0; j < keysyms_per_keycode; ++j) { |
| 172 switch (keysyms[(keycode - min_keycode) * keysyms_per_keycode + j]) { |
| 173 case XK_Meta_L: |
| 174 case XK_Meta_R: |
| 175 meta_keycodes_.push_back(keycode); |
| 176 break; |
| 177 case XK_Super_L: |
| 178 case XK_Super_R: |
| 179 super_keycodes_.push_back(keycode); |
| 180 break; |
| 181 case XK_Hyper_L: |
| 182 case XK_Hyper_R: |
| 183 hyper_keycodes_.push_back(keycode); |
| 184 break; |
| 185 } |
| 186 } |
| 187 } |
| 188 XFree(const_cast<KeySym*>(keysyms)); |
| 189 XFreeModifiermap(const_cast<XModifierKeymap*>(modmap)); |
| 190 } |
| 191 |
| 192 GdkEvent* X11InputMethodContextImplGtk2::GdkEventFromNativeEvent( |
| 193 const base::NativeEvent& native_event) { |
| 194 const XKeyEvent& xkey = native_event->xkey; |
| 195 DCHECK(xkey.type == KeyPress || xkey.type == KeyRelease); |
| 196 |
| 197 // Get a GdkDisplay. |
| 198 GdkDisplay* display = gdk_x11_lookup_xdisplay(xkey.display); |
| 199 if (!display) { |
| 200 // Fall back to the default display. |
| 201 display = gdk_display_get_default(); |
| 202 } |
| 203 if (!display) { |
| 204 LOG(ERROR) << "Cannot get a GdkDisplay for a key event."; |
| 205 return NULL; |
| 206 } |
| 207 // Get a keysym and group. |
| 208 KeySym keysym = NoSymbol; |
| 209 guint8 keyboard_group = 0; |
| 210 XLookupString(&native_event->xkey, NULL, 0, &keysym, NULL); |
| 211 GdkKeymap* keymap = gdk_keymap_get_for_display(display); |
| 212 GdkKeymapKey* keys = NULL; |
| 213 guint* keyvals = NULL; |
| 214 gint n_entries = 0; |
| 215 if (keymap && |
| 216 gdk_keymap_get_entries_for_keycode(keymap, xkey.keycode, |
| 217 &keys, &keyvals, &n_entries)) { |
| 218 for (gint i = 0; i < n_entries; ++i) { |
| 219 if (keyvals[i] == keysym) { |
| 220 keyboard_group = keys[i].group; |
| 221 break; |
| 222 } |
| 223 } |
| 224 } |
| 225 g_free(keys); |
| 226 keys = NULL; |
| 227 g_free(keyvals); |
| 228 keyvals = NULL; |
| 229 // Get a GdkWindow. |
| 230 GdkWindow* window = gdk_x11_window_lookup_for_display(display, xkey.window); |
| 231 if (window) |
| 232 g_object_ref(window); |
| 233 else |
| 234 window = gdk_x11_window_foreign_new_for_display(display, xkey.window); |
| 235 if (!window) { |
| 236 LOG(ERROR) << "Cannot get a GdkWindow for a key event."; |
| 237 return NULL; |
| 238 } |
| 239 |
| 240 // Create a GdkEvent. |
| 241 GdkEventType event_type = xkey.type == KeyPress ? |
| 242 GDK_KEY_PRESS : GDK_KEY_RELEASE; |
| 243 GdkEvent* event = gdk_event_new(event_type); |
| 244 event->key.type = event_type; |
| 245 event->key.window = window; |
| 246 // GdkEventKey and XKeyEvent share the same definition for time and state. |
| 247 event->key.send_event = xkey.send_event; |
| 248 event->key.time = xkey.time; |
| 249 event->key.state = xkey.state; |
| 250 event->key.keyval = keysym; |
| 251 event->key.length = 0; |
| 252 event->key.string = NULL; |
| 253 event->key.hardware_keycode = xkey.keycode; |
| 254 event->key.group = keyboard_group; |
| 255 event->key.is_modifier = IsKeycodeModifierKey(xkey.keycode); |
| 256 |
| 257 char keybits[32] = {0}; |
| 258 XQueryKeymap(xkey.display, keybits); |
| 259 if (IsAnyOfKeycodesPressed(meta_keycodes_, keybits, sizeof keybits * 8)) |
| 260 event->key.state |= GDK_META_MASK; |
| 261 if (IsAnyOfKeycodesPressed(super_keycodes_, keybits, sizeof keybits * 8)) |
| 262 event->key.state |= GDK_SUPER_MASK; |
| 263 if (IsAnyOfKeycodesPressed(hyper_keycodes_, keybits, sizeof keybits * 8)) |
| 264 event->key.state |= GDK_HYPER_MASK; |
| 265 |
| 266 return event; |
| 267 } |
| 268 |
| 230 bool X11InputMethodContextImplGtk2::IsKeycodeModifierKey( | 269 bool X11InputMethodContextImplGtk2::IsKeycodeModifierKey( |
| 231 unsigned int keycode) const { | 270 unsigned int keycode) const { |
| 232 return modifier_keycodes_.find(keycode) != modifier_keycodes_.end(); | 271 return modifier_keycodes_.find(keycode) != modifier_keycodes_.end(); |
| 233 } | 272 } |
| 234 | 273 |
| 274 bool X11InputMethodContextImplGtk2::IsAnyOfKeycodesPressed( |
| 275 const std::vector<int>& keycodes, |
| 276 const char* keybits, |
| 277 int num_keys) const { |
| 278 for (size_t i = 0; i < keycodes.size(); ++i) { |
| 279 const int keycode = keycodes[i]; |
| 280 if (keycode < 0 || num_keys <= keycode) |
| 281 continue; |
| 282 if (keybits[keycode / 8] & 1 << (keycode % 8)) |
| 283 return true; |
| 284 } |
| 285 return false; |
| 286 } |
| 287 |
| 235 // GtkIMContext event handlers. | 288 // GtkIMContext event handlers. |
| 236 | 289 |
| 237 void X11InputMethodContextImplGtk2::OnCommit(GtkIMContext* context, | 290 void X11InputMethodContextImplGtk2::OnCommit(GtkIMContext* context, |
| 238 gchar* text) { | 291 gchar* text) { |
| 239 if (context != gtk_context_) | 292 if (context != gtk_context_) |
| 240 return; | 293 return; |
| 241 | 294 |
| 242 const base::string16& text_in_utf16 = base::UTF8ToUTF16(text); | 295 const base::string16& text_in_utf16 = base::UTF8ToUTF16(text); |
| 243 // If an underlying IME is emitting the "commit" signal to insert a character | 296 // If an underlying IME is emitting the "commit" signal to insert a character |
| 244 // for a direct input key event, ignores the insertion of the character at | 297 // for a direct input key event, ignores the insertion of the character at |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 text.length() == 1 && | 360 text.length() == 1 && |
| 308 text[0] == gdk_keyval_to_unicode(gdk_event_key_keyval_)) { | 361 text[0] == gdk_keyval_to_unicode(gdk_event_key_keyval_)) { |
| 309 is_signal_caught_ = true; | 362 is_signal_caught_ = true; |
| 310 return true; | 363 return true; |
| 311 } else { | 364 } else { |
| 312 return false; | 365 return false; |
| 313 } | 366 } |
| 314 } | 367 } |
| 315 | 368 |
| 316 } // namespace libgtk2ui | 369 } // namespace libgtk2ui |
| OLD | NEW |