Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(581)

Side by Side Diff: chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.cc

Issue 2449243002: Gtk3 ui: Add libgtk3ui as a separate build component (Closed)
Patch Set: Add theme_properties dep to //chrome/browser/ui Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.h"
6
7 #include <gdk/gdk.h>
8 #include <gdk/gdkkeysyms.h>
9 #include <gdk/gdkx.h>
10 #include <stddef.h>
11
12 #include <gtk/gtk.h>
13
14 #include <X11/X.h>
15 #include <X11/Xlib.h>
16
17 #include "base/message_loop/message_loop.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "ui/base/ime/composition_text.h"
20 #include "ui/base/ime/composition_text_util_pango.h"
21 #include "ui/base/ime/text_input_client.h"
22 #include "ui/events/event.h"
23 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
24 #include "ui/gfx/x/x11_types.h"
25
26 namespace libgtk2ui {
27
28 X11InputMethodContextImplGtk2::X11InputMethodContextImplGtk2(
29 ui::LinuxInputMethodContextDelegate* delegate,
30 bool is_simple)
31 : delegate_(delegate),
32 gtk_context_(NULL),
33 gdk_last_set_client_window_(NULL) {
34 CHECK(delegate_);
35
36 ResetXModifierKeycodesCache();
37
38 gtk_context_ =
39 is_simple ? gtk_im_context_simple_new() : gtk_im_multicontext_new();
40
41 g_signal_connect(gtk_context_, "commit", G_CALLBACK(OnCommitThunk), this);
42 g_signal_connect(gtk_context_, "preedit-changed",
43 G_CALLBACK(OnPreeditChangedThunk), this);
44 g_signal_connect(gtk_context_, "preedit-end", G_CALLBACK(OnPreeditEndThunk),
45 this);
46 g_signal_connect(gtk_context_, "preedit-start",
47 G_CALLBACK(OnPreeditStartThunk), this);
48 // TODO(shuchen): Handle operations on surrounding text.
49 // "delete-surrounding" and "retrieve-surrounding" signals should be
50 // handled.
51 }
52
53 X11InputMethodContextImplGtk2::~X11InputMethodContextImplGtk2() {
54 if (gtk_context_) {
55 g_object_unref(gtk_context_);
56 gtk_context_ = NULL;
57 }
58 }
59
60 // Overriden from ui::LinuxInputMethodContext
61
62 bool X11InputMethodContextImplGtk2::DispatchKeyEvent(
63 const ui::KeyEvent& key_event) {
64 if (!key_event.HasNativeEvent() || !gtk_context_)
65 return false;
66
67 // Translate a XKeyEvent to a GdkEventKey.
68 GdkEvent* event = GdkEventFromNativeEvent(key_event.native_event());
69 if (!event) {
70 LOG(ERROR) << "Cannot translate a XKeyEvent to a GdkEvent.";
71 return false;
72 }
73
74 if (event->key.window != gdk_last_set_client_window_) {
75 gtk_im_context_set_client_window(gtk_context_, event->key.window);
76 gdk_last_set_client_window_ = event->key.window;
77 }
78
79 // Convert the last known caret bounds relative to the screen coordinates
80 // to a GdkRectangle relative to the client window.
81 gint x = 0;
82 gint y = 0;
83 gdk_window_get_origin(event->key.window, &x, &y);
84 GdkRectangle rect = {last_caret_bounds_.x() - x,
85 last_caret_bounds_.y() - y,
86 last_caret_bounds_.width(),
87 last_caret_bounds_.height()};
88 gtk_im_context_set_cursor_location(gtk_context_, &rect);
89
90 const bool handled =
91 gtk_im_context_filter_keypress(gtk_context_, &event->key);
92 gdk_event_free(event);
93 return handled;
94 }
95
96 void X11InputMethodContextImplGtk2::Reset() {
97 gtk_im_context_reset(gtk_context_);
98 }
99
100 void X11InputMethodContextImplGtk2::Focus() {
101 gtk_im_context_focus_in(gtk_context_);
102 }
103
104 void X11InputMethodContextImplGtk2::Blur() {
105 gtk_im_context_focus_out(gtk_context_);
106 }
107
108 void X11InputMethodContextImplGtk2::SetCursorLocation(const gfx::Rect& rect) {
109 // Remember the caret bounds so that we can set the cursor location later.
110 // gtk_im_context_set_cursor_location() takes the location relative to the
111 // client window, which is unknown at this point. So we'll call
112 // gtk_im_context_set_cursor_location() later in ProcessKeyEvent() where
113 // (and only where) we know the client window.
114 last_caret_bounds_ = rect;
115 }
116
117 // private:
118
119 void X11InputMethodContextImplGtk2::ResetXModifierKeycodesCache() {
120 modifier_keycodes_.clear();
121 meta_keycodes_.clear();
122 super_keycodes_.clear();
123 hyper_keycodes_.clear();
124
125 Display* display = gfx::GetXDisplay();
126 gfx::XScopedPtr<XModifierKeymap,
127 gfx::XObjectDeleter<XModifierKeymap, int, XFreeModifiermap>>
128 modmap(XGetModifierMapping(display));
129 int min_keycode = 0;
130 int max_keycode = 0;
131 int keysyms_per_keycode = 1;
132 XDisplayKeycodes(display, &min_keycode, &max_keycode);
133 gfx::XScopedPtr<KeySym[]> keysyms(
134 XGetKeyboardMapping(display, min_keycode, max_keycode - min_keycode + 1,
135 &keysyms_per_keycode));
136 for (int i = 0; i < 8 * modmap->max_keypermod; ++i) {
137 const int keycode = modmap->modifiermap[i];
138 if (!keycode)
139 continue;
140 modifier_keycodes_.insert(keycode);
141
142 if (!keysyms)
143 continue;
144 for (int j = 0; j < keysyms_per_keycode; ++j) {
145 switch (keysyms[(keycode - min_keycode) * keysyms_per_keycode + j]) {
146 case XK_Meta_L:
147 case XK_Meta_R:
148 meta_keycodes_.push_back(keycode);
149 break;
150 case XK_Super_L:
151 case XK_Super_R:
152 super_keycodes_.push_back(keycode);
153 break;
154 case XK_Hyper_L:
155 case XK_Hyper_R:
156 hyper_keycodes_.push_back(keycode);
157 break;
158 }
159 }
160 }
161 }
162
163 GdkEvent* X11InputMethodContextImplGtk2::GdkEventFromNativeEvent(
164 const base::NativeEvent& native_event) {
165 XEvent xkeyevent;
166 if (native_event->type == GenericEvent) {
167 // If this is an XI2 key event, build a matching core X event, to avoid
168 // having two cases for every use.
169 ui::InitXKeyEventFromXIDeviceEvent(*native_event, &xkeyevent);
170 } else {
171 DCHECK(native_event->type == KeyPress || native_event->type == KeyRelease);
172 xkeyevent.xkey = native_event->xkey;
173 }
174 XKeyEvent& xkey = xkeyevent.xkey;
175
176 // Get a GdkDisplay.
177 GdkDisplay* display = gdk_x11_lookup_xdisplay(xkey.display);
178 if (!display) {
179 // Fall back to the default display.
180 display = gdk_display_get_default();
181 }
182 if (!display) {
183 LOG(ERROR) << "Cannot get a GdkDisplay for a key event.";
184 return NULL;
185 }
186 // Get a keysym and group.
187 KeySym keysym = NoSymbol;
188 guint8 keyboard_group = 0;
189 XLookupString(&xkey, NULL, 0, &keysym, NULL);
190 GdkKeymap* keymap = gdk_keymap_get_for_display(display);
191 GdkKeymapKey* keys = NULL;
192 guint* keyvals = NULL;
193 gint n_entries = 0;
194 if (keymap &&
195 gdk_keymap_get_entries_for_keycode(keymap, xkey.keycode,
196 &keys, &keyvals, &n_entries)) {
197 for (gint i = 0; i < n_entries; ++i) {
198 if (keyvals[i] == keysym) {
199 keyboard_group = keys[i].group;
200 break;
201 }
202 }
203 }
204 g_free(keys);
205 keys = NULL;
206 g_free(keyvals);
207 keyvals = NULL;
208 // Get a GdkWindow.
209 #if GTK_CHECK_VERSION(2,24,0)
210 GdkWindow* window = gdk_x11_window_lookup_for_display(display, xkey.window);
211 #else
212 GdkWindow* window = gdk_window_lookup_for_display(display, xkey.window);
213 #endif
214 if (window)
215 g_object_ref(window);
216 else
217 #if GTK_CHECK_VERSION(2,24,0)
218 window = gdk_x11_window_foreign_new_for_display(display, xkey.window);
219 #else
220 window = gdk_window_foreign_new_for_display(display, xkey.window);
221 #endif
222 if (!window) {
223 LOG(ERROR) << "Cannot get a GdkWindow for a key event.";
224 return NULL;
225 }
226
227 // Create a GdkEvent.
228 GdkEventType event_type = xkey.type == KeyPress ?
229 GDK_KEY_PRESS : GDK_KEY_RELEASE;
230 GdkEvent* event = gdk_event_new(event_type);
231 event->key.type = event_type;
232 event->key.window = window;
233 // GdkEventKey and XKeyEvent share the same definition for time and state.
234 event->key.send_event = xkey.send_event;
235 event->key.time = xkey.time;
236 event->key.state = xkey.state;
237 event->key.keyval = keysym;
238 event->key.length = 0;
239 event->key.string = NULL;
240 event->key.hardware_keycode = xkey.keycode;
241 event->key.group = keyboard_group;
242 event->key.is_modifier = IsKeycodeModifierKey(xkey.keycode);
243
244 char keybits[32] = {0};
245 XQueryKeymap(xkey.display, keybits);
246 if (IsAnyOfKeycodesPressed(meta_keycodes_, keybits, sizeof keybits * 8))
247 event->key.state |= GDK_META_MASK;
248 if (IsAnyOfKeycodesPressed(super_keycodes_, keybits, sizeof keybits * 8))
249 event->key.state |= GDK_SUPER_MASK;
250 if (IsAnyOfKeycodesPressed(hyper_keycodes_, keybits, sizeof keybits * 8))
251 event->key.state |= GDK_HYPER_MASK;
252
253 return event;
254 }
255
256 bool X11InputMethodContextImplGtk2::IsKeycodeModifierKey(
257 unsigned int keycode) const {
258 return modifier_keycodes_.find(keycode) != modifier_keycodes_.end();
259 }
260
261 bool X11InputMethodContextImplGtk2::IsAnyOfKeycodesPressed(
262 const std::vector<int>& keycodes,
263 const char* keybits,
264 int num_keys) const {
265 for (size_t i = 0; i < keycodes.size(); ++i) {
266 const int keycode = keycodes[i];
267 if (keycode < 0 || num_keys <= keycode)
268 continue;
269 if (keybits[keycode / 8] & 1 << (keycode % 8))
270 return true;
271 }
272 return false;
273 }
274
275 // GtkIMContext event handlers.
276
277 void X11InputMethodContextImplGtk2::OnCommit(GtkIMContext* context,
278 gchar* text) {
279 if (context != gtk_context_)
280 return;
281
282 delegate_->OnCommit(base::UTF8ToUTF16(text));
283 }
284
285 void X11InputMethodContextImplGtk2::OnPreeditChanged(GtkIMContext* context) {
286 if (context != gtk_context_)
287 return;
288
289 gchar* str = NULL;
290 PangoAttrList* attrs = NULL;
291 gint cursor_pos = 0;
292 gtk_im_context_get_preedit_string(context, &str, &attrs, &cursor_pos);
293 ui::CompositionText composition_text;
294 ui::ExtractCompositionTextFromGtkPreedit(str, attrs, cursor_pos,
295 &composition_text);
296 g_free(str);
297 pango_attr_list_unref(attrs);
298
299 delegate_->OnPreeditChanged(composition_text);
300 }
301
302 void X11InputMethodContextImplGtk2::OnPreeditEnd(GtkIMContext* context) {
303 if (context != gtk_context_)
304 return;
305
306 delegate_->OnPreeditEnd();
307 }
308
309 void X11InputMethodContextImplGtk2::OnPreeditStart(GtkIMContext* context) {
310 if (context != gtk_context_)
311 return;
312
313 delegate_->OnPreeditStart();
314 }
315
316 } // namespace libgtk2ui
OLDNEW
« no previous file with comments | « chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.h ('k') | chrome/browser/ui/libgtkui/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698