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

Side by Side Diff: chrome/browser/chromeos/events/event_rewriter.cc

Issue 248343002: Convert chromeos::EventRewriter to a ui::EventRewriter (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@x354034-kder
Patch Set: Bug fix: non-native event under X11 Created 6 years, 7 months 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/chromeos/events/event_rewriter.h" 5 #include "chrome/browser/chromeos/events/event_rewriter.h"
6 6
7 #include <X11/extensions/XInput2.h>
8 #include <X11/keysym.h>
9 #include <X11/XF86keysym.h>
10 #include <X11/Xlib.h>
11 // Get rid of macros from Xlib.h that conflicts with other parts of the code.
12 #undef RootWindow
13 #undef Status
14
15 #include <vector> 7 #include <vector>
16 8
17 #include "ash/wm/window_state.h" 9 #include "ash/wm/window_state.h"
18 #include "ash/wm/window_util.h" 10 #include "ash/wm/window_util.h"
19 #include "base/command_line.h" 11 #include "base/command_line.h"
20 #include "base/logging.h" 12 #include "base/logging.h"
21 #include "base/prefs/pref_service.h" 13 #include "base/prefs/pref_service.h"
22 #include "base/strings/string_util.h" 14 #include "base/strings/string_util.h"
23 #include "base/sys_info.h" 15 #include "base/sys_info.h"
24 #include "chrome/browser/chromeos/events/xinput_hierarchy_changed_event_listener .h"
25 #include "chrome/browser/chromeos/login/login_display_host_impl.h" 16 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
26 #include "chrome/browser/chromeos/login/user_manager.h" 17 #include "chrome/browser/chromeos/login/user_manager.h"
27 #include "chrome/browser/profiles/profile_manager.h" 18 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/common/pref_names.h" 19 #include "chrome/common/pref_names.h"
29 #include "chromeos/chromeos_switches.h" 20 #include "chromeos/chromeos_switches.h"
30 #include "chromeos/ime/ime_keyboard.h" 21 #include "chromeos/ime/ime_keyboard.h"
31 #include "chromeos/ime/input_method_manager.h" 22 #include "chromeos/ime/input_method_manager.h"
32 #include "ui/base/x/x11_util.h"
33 #include "ui/events/event.h" 23 #include "ui/events/event.h"
34 #include "ui/events/event_utils.h" 24 #include "ui/events/event_utils.h"
35 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
36 #include "ui/events/platform/platform_event_source.h" 25 #include "ui/events/platform/platform_event_source.h"
37 #include "ui/wm/core/window_util.h" 26 #include "ui/wm/core/window_util.h"
38 27
28 #if defined(USE_X11)
29 #include <X11/extensions/XInput2.h>
30 #include <X11/Xlib.h>
31 // Get rid of macros from Xlib.h that conflicts with other parts of the code.
32 #undef RootWindow
33 #undef Status
34
35 #include "chrome/browser/chromeos/events/xinput_hierarchy_changed_event_listener .h"
36 #include "ui/base/x/x11_util.h"
37 #endif
38
39 namespace chromeos {
40
39 namespace { 41 namespace {
40 42
41 const int kBadDeviceId = -1; 43 const int kBadDeviceId = -1;
42 44
43 // A key code and a flag we should use when a key is remapped to |remap_to|. 45 // Table of key properties of remappable keys and/or remapping targets.
46 // This is searched in two distinct ways:
47 // - |remap_to| is an |input_method::ModifierKey|, which is the form
48 // held in user preferences. |GetRemappedKey()| maps this to the
49 // corresponding |key_code| and characterstic event |flag|.
50 // - |flag| is a |ui::EventFlags|. |GetRemappedModifierMasks()| maps this
51 // to the corresponding user preference |pref_name| for that flag's
52 // key, so that it can then be remapped as above.
53 // In addition |kModifierRemappingCtrl| is a direct reference to the
54 // Control key entry in the table, used in handling Chromebook Diamond
55 // and Apple Command keys.
44 const struct ModifierRemapping { 56 const struct ModifierRemapping {
45 int remap_to; 57 int remap_to;
46 int flag; 58 int flag;
47 unsigned int native_modifier; 59 ui::KeyboardCode key_code;
48 ui::KeyboardCode keycode; 60 const char* pref_name;
49 KeySym native_keysyms[4]; // left, right, shift+left, shift+right.
50 } kModifierRemappings[] = { 61 } kModifierRemappings[] = {
51 { chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN, 62 {input_method::kSearchKey, ui::EF_COMMAND_DOWN, ui::VKEY_LWIN,
52 { XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L }}, 63 prefs::kLanguageRemapSearchKeyTo},
53 { chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask, 64 {input_method::kControlKey, ui::EF_CONTROL_DOWN, ui::VKEY_CONTROL,
54 ui::VKEY_CONTROL, 65 prefs::kLanguageRemapControlKeyTo},
55 { XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }}, 66 {input_method::kAltKey, ui::EF_ALT_DOWN, ui::VKEY_MENU,
56 { chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask, 67 prefs::kLanguageRemapAltKeyTo},
57 ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }}, 68 {input_method::kVoidKey, 0, ui::VKEY_UNKNOWN, NULL},
58 { chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN, 69 {input_method::kCapsLockKey, ui::EF_CAPS_LOCK_DOWN, ui::VKEY_CAPITAL,
59 { XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }}, 70 prefs::kLanguageRemapCapsLockKeyTo},
60 { chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL, 71 {input_method::kEscapeKey, 0, ui::VKEY_ESCAPE, NULL},
61 { XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }}, 72 {0, 0, ui::VKEY_F15, prefs::kLanguageRemapDiamondKeyTo},
62 { chromeos::input_method::kEscapeKey, 0, 0U, ui::VKEY_ESCAPE,
63 { XK_Escape, XK_Escape, XK_Escape, XK_Escape }},
64 }; 73 };
65 74
66 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; 75 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1];
67 76
68 // A structure for converting |native_modifier| to a pair of |flag| and
69 // |pref_name|.
70 const struct ModifierFlagToPrefName {
71 unsigned int native_modifier;
72 int flag;
73 const char* pref_name;
74 } kModifierFlagToPrefName[] = {
75 // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without
76 // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo.
77 { Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo },
78 { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo },
79 { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo },
80 { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo },
81 { Mod2Mask, 0, prefs::kLanguageRemapDiamondKeyTo },
82 };
83
84 // Gets a remapped key for |pref_name| key. For example, to find out which 77 // Gets a remapped key for |pref_name| key. For example, to find out which
85 // key Search is currently remapped to, call the function with 78 // key Search is currently remapped to, call the function with
86 // prefs::kLanguageRemapSearchKeyTo. 79 // prefs::kLanguageRemapSearchKeyTo.
87 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, 80 const ModifierRemapping* GetRemappedKey(const std::string& pref_name,
88 const PrefService& pref_service) { 81 const PrefService& pref_service) {
89 if (!pref_service.FindPreference(pref_name.c_str())) 82 if (!pref_service.FindPreference(pref_name.c_str()))
90 return NULL; // The |pref_name| hasn't been registered. On login screen? 83 return NULL; // The |pref_name| hasn't been registered. On login screen?
91 const int value = pref_service.GetInteger(pref_name.c_str()); 84 const int value = pref_service.GetInteger(pref_name.c_str());
92 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { 85 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) {
93 if (value == kModifierRemappings[i].remap_to) 86 if (value == kModifierRemappings[i].remap_to)
94 return &kModifierRemappings[i]; 87 return &kModifierRemappings[i];
95 } 88 }
96 return NULL; 89 return NULL;
97 } 90 }
98 91
99 bool IsRight(KeySym native_keysym) {
100 switch (native_keysym) {
101 case XK_Alt_R:
102 case XK_Control_R:
103 case XK_Hyper_R:
104 case XK_Meta_R:
105 case XK_Shift_R:
106 case XK_Super_R:
107 return true;
108 default:
109 break;
110 }
111 return false;
112 }
113
114 bool HasDiamondKey() { 92 bool HasDiamondKey() {
115 return CommandLine::ForCurrentProcess()->HasSwitch( 93 return CommandLine::ForCurrentProcess()->HasSwitch(
116 chromeos::switches::kHasChromeOSDiamondKey); 94 chromeos::switches::kHasChromeOSDiamondKey);
117 } 95 }
118 96
119 bool IsISOLevel5ShiftUsedByCurrentInputMethod() { 97 bool IsISOLevel5ShiftUsedByCurrentInputMethod() {
120 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, 98 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask,
121 // it's not possible to make both features work. For now, we don't remap 99 // it's not possible to make both features work. For now, we don't remap
122 // Mod3Mask when Neo2 is in use. 100 // Mod3Mask when Neo2 is in use.
123 // TODO(yusukes): Remove the restriction. 101 // TODO(yusukes): Remove the restriction.
124 chromeos::input_method::InputMethodManager* manager = 102 input_method::InputMethodManager* manager =
125 chromeos::input_method::InputMethodManager::Get(); 103 input_method::InputMethodManager::Get();
126 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod(); 104 return manager->IsISOLevel5ShiftUsedByCurrentInputMethod();
127 } 105 }
128 106
107 EventRewriter::DeviceType GetDeviceType(const std::string& device_name) {
108 std::vector<std::string> tokens;
109 Tokenize(device_name, " .", &tokens);
110
111 // If the |device_name| contains the two words, "apple" and "keyboard", treat
112 // it as an Apple keyboard.
113 bool found_apple = false;
114 bool found_keyboard = false;
115 for (size_t i = 0; i < tokens.size(); ++i) {
116 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple"))
117 found_apple = true;
118 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard"))
119 found_keyboard = true;
120 if (found_apple && found_keyboard)
121 return EventRewriter::kDeviceAppleKeyboard;
122 }
123
124 return EventRewriter::kDeviceUnknown;
125 }
126
129 } // namespace 127 } // namespace
130 128
131 namespace chromeos {
132
133 EventRewriter::EventRewriter() 129 EventRewriter::EventRewriter()
134 : last_device_id_(kBadDeviceId), 130 : last_device_id_(kBadDeviceId),
135 keyboard_for_testing_(NULL), 131 ime_keyboard_for_testing_(NULL),
136 pref_service_for_testing_(NULL) { 132 pref_service_for_testing_(NULL) {
133 #if defined(USE_X11)
137 ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); 134 ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
138 if (base::SysInfo::IsRunningOnChromeOS()) { 135 if (base::SysInfo::IsRunningOnChromeOS()) {
139 XInputHierarchyChangedEventListener::GetInstance()->AddObserver(this); 136 XInputHierarchyChangedEventListener::GetInstance()->AddObserver(this);
140 } 137 }
141 RefreshKeycodes(); 138 #endif
142 } 139 }
143 140
144 EventRewriter::~EventRewriter() { 141 EventRewriter::~EventRewriter() {
142 #if defined(USE_X11)
145 ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this); 143 ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
146 if (base::SysInfo::IsRunningOnChromeOS()) { 144 if (base::SysInfo::IsRunningOnChromeOS()) {
147 XInputHierarchyChangedEventListener::GetInstance()->RemoveObserver(this); 145 XInputHierarchyChangedEventListener::GetInstance()->RemoveObserver(this);
148 } 146 }
147 #endif
149 } 148 }
150 149
151 EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting( 150 EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting(
152 int device_id, 151 int device_id,
153 const std::string& device_name) { 152 const std::string& device_name) {
154 return DeviceAddedInternal(device_id, device_name); 153 return DeviceAddedInternal(device_id, device_name);
155 } 154 }
156 155
157 // static 156 void EventRewriter::RewriteLocatedEventForTesting(const ui::Event& event,
158 EventRewriter::DeviceType EventRewriter::GetDeviceType( 157 int* flags) {
159 const std::string& device_name) { 158 MutableKeyState state = {*flags, ui::VKEY_UNKNOWN};
160 std::vector<std::string> tokens; 159 RewriteLocatedEvent(event, &state);
161 Tokenize(device_name, " .", &tokens); 160 *flags = state.flags;
162
163 // If the |device_name| contains the two words, "apple" and "keyboard", treat
164 // it as an Apple keyboard.
165 bool found_apple = false;
166 bool found_keyboard = false;
167 for (size_t i = 0; i < tokens.size(); ++i) {
168 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple"))
169 found_apple = true;
170 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard"))
171 found_keyboard = true;
172 if (found_apple && found_keyboard)
173 return kDeviceAppleKeyboard;
174 }
175
176 return kDeviceUnknown;
177 } 161 }
178 162
179 void EventRewriter::RewriteForTesting(XEvent* event) { 163 ui::EventRewriteStatus EventRewriter::RewriteEvent(
180 Rewrite(event); 164 const ui::Event& event,
165 scoped_ptr<ui::Event>* rewritten_event) {
166 #if defined(USE_X11)
167 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
168 // crbug.com/136465.
169 XEvent* xev = event.native_event();
170 if (xev && xev->xany.send_event)
171 return ui::EVENT_REWRITE_CONTINUE;
172 #endif
173 switch (event.type()) {
174 case ui::ET_KEY_PRESSED:
175 case ui::ET_KEY_RELEASED: {
176 const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
177 MutableKeyState state = {key_event.flags(), key_event.key_code()};
178 RewriteModifierKeys(key_event, &state);
179 RewriteNumPadKeys(key_event, &state);
180 RewriteExtendedKeys(key_event, &state);
181 RewriteFunctionKeys(key_event, &state);
182 if ((key_event.flags() != state.flags) ||
183 (key_event.key_code() != state.key_code)) {
184 ui::KeyEvent* rewritten_key_event = new ui::KeyEvent(key_event);
185 rewritten_event->reset(rewritten_key_event);
186 rewritten_key_event->set_flags(state.flags);
187 rewritten_key_event->set_key_code(state.key_code);
188 return ui::EVENT_REWRITE_REWRITTEN;
189 }
190 return ui::EVENT_REWRITE_CONTINUE;
191 }
192 case ui::ET_MOUSE_PRESSED:
193 case ui::ET_MOUSE_RELEASED:
194 case ui::ET_TOUCH_PRESSED:
195 case ui::ET_TOUCH_RELEASED: {
196 MutableKeyState state = {event.flags(), ui::VKEY_UNKNOWN};
197 RewriteLocatedEvent(event, &state);
198 if (event.flags() != state.flags) {
199 if (event.IsMouseEvent()) {
200 rewritten_event->reset(
201 new ui::MouseEvent(static_cast<const ui::MouseEvent&>(event)));
202 } else {
203 rewritten_event->reset(
204 new ui::TouchEvent(static_cast<const ui::TouchEvent&>(event)));
205 }
206 rewritten_event->get()->set_flags(state.flags);
207 return ui::EVENT_REWRITE_REWRITTEN;
208 }
209 return ui::EVENT_REWRITE_CONTINUE;
210 }
211 default:
212 return ui::EVENT_REWRITE_CONTINUE;
213 }
214 NOTREACHED();
181 } 215 }
182 216
217 ui::EventRewriteStatus EventRewriter::NextDispatchEvent(
218 const ui::Event& last_event,
219 scoped_ptr<ui::Event>* new_event) {
220 NOTREACHED();
221 return ui::EVENT_REWRITE_CONTINUE;
222 }
223
224 #if defined(USE_X11)
183 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) { 225 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) {
184 std::map<int, DeviceType>::const_iterator iter = 226 if (!device_id_to_type_.count(device_id)) {
185 device_id_to_type_.find(device_id);
186 if (iter == device_id_to_type_.end()) {
187 // |device_id| is unknown. This means the device was connected before 227 // |device_id| is unknown. This means the device was connected before
188 // booting the OS. Query the name of the device and add it to the map. 228 // booting the OS. Query the name of the device and add it to the map.
189 DeviceAdded(device_id); 229 DeviceAdded(device_id);
190 } 230 }
191
192 last_device_id_ = device_id; 231 last_device_id_ = device_id;
193 } 232 }
194 233 #endif
195 void EventRewriter::WillProcessEvent(const ui::PlatformEvent& event) {
196 XEvent* xevent = event;
197 if (xevent->type == KeyPress || xevent->type == KeyRelease) {
198 Rewrite(xevent);
199 } else if (xevent->type == GenericEvent) {
200 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
201 if (xievent->evtype == XI_KeyPress || xievent->evtype == XI_KeyRelease) {
202 if (xievent->deviceid == xievent->sourceid)
203 DeviceKeyPressedOrReleased(xievent->deviceid);
204 } else {
205 RewriteLocatedEvent(xevent);
206 }
207 } else if (xevent->type == MappingNotify) {
208 if (xevent->xmapping.request == MappingModifier ||
209 xevent->xmapping.request == MappingKeyboard) {
210 RefreshKeycodes();
211 }
212 }
213 }
214
215 void EventRewriter::DidProcessEvent(const ui::PlatformEvent& event) {
216 }
217
218 void EventRewriter::DeviceHierarchyChanged() {}
219
220 void EventRewriter::DeviceAdded(int device_id) {
221 DCHECK_NE(XIAllDevices, device_id);
222 DCHECK_NE(XIAllMasterDevices, device_id);
223 if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
224 LOG(ERROR) << "Unexpected device_id passed: " << device_id;
225 return;
226 }
227
228 int ndevices_return = 0;
229 XIDeviceInfo* device_info = XIQueryDevice(gfx::GetXDisplay(),
230 device_id,
231 &ndevices_return);
232
233 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
234 // the number of devices found should be either 0 (not found) or 1.
235 if (!device_info) {
236 LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
237 return;
238 }
239
240 DCHECK_EQ(1, ndevices_return);
241 for (int i = 0; i < ndevices_return; ++i) {
242 DCHECK_EQ(device_id, device_info[i].deviceid); // see the comment above.
243 DCHECK(device_info[i].name);
244 DeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
245 }
246
247 XIFreeDeviceInfo(device_info);
248 }
249
250 void EventRewriter::DeviceRemoved(int device_id) {
251 device_id_to_type_.erase(device_id);
252 }
253
254 void EventRewriter::RefreshKeycodes() {
255 keysym_to_keycode_map_.clear();
256 }
257
258 KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) {
259 if (keysym_to_keycode_map_.count(keysym))
260 return keysym_to_keycode_map_[keysym];
261
262 XDisplay* display = gfx::GetXDisplay();
263 KeyCode keycode = XKeysymToKeycode(display, keysym);
264 keysym_to_keycode_map_[keysym] = keycode;
265 return keycode;
266 }
267
268 bool EventRewriter::TopRowKeysAreFunctionKeys(XEvent* event) const {
269 const PrefService* prefs = GetPrefService();
270 if (prefs &&
271 prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
272 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
273 return true;
274
275 ash::wm::WindowState* state = ash::wm::GetActiveWindowState();
276 return state ? state->top_row_keys_are_function_keys() : false;
277 }
278
279 bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym(
280 const KeyboardRemapping* remappings,
281 size_t num_remappings,
282 KeySym keysym,
283 unsigned int native_mods,
284 KeySym* remapped_native_keysym,
285 unsigned int* remapped_native_mods) {
286 for (size_t i = 0; i < num_remappings; ++i) {
287 const KeyboardRemapping& map = remappings[i];
288
289 if (keysym != map.input_keysym)
290 continue;
291 unsigned int matched_mods = native_mods & map.input_native_mods;
292 if (matched_mods != map.input_native_mods)
293 continue;
294
295 *remapped_native_keysym = map.output_keysym;
296 *remapped_native_mods = (native_mods & ~map.input_native_mods) |
297 map.output_native_mods;
298 return true;
299 }
300
301 return false;
302 }
303
304 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
305 const KeyboardRemapping* remappings,
306 size_t num_remappings,
307 KeyCode keycode,
308 unsigned int native_mods,
309 KeySym* remapped_native_keysym,
310 unsigned int* remapped_native_mods) {
311 for (size_t i = 0; i < num_remappings; ++i) {
312 const KeyboardRemapping& map = remappings[i];
313
314 KeyCode input_keycode = NativeKeySymToNativeKeycode(map.input_keysym);
315 if (keycode != input_keycode)
316 continue;
317 unsigned int matched_mods = native_mods & map.input_native_mods;
318 if (matched_mods != map.input_native_mods)
319 continue;
320
321 *remapped_native_keysym = map.output_keysym;
322 *remapped_native_mods = (native_mods & ~map.input_native_mods) |
323 map.output_native_mods;
324 return true;
325 }
326
327 return false;
328 }
329 234
330 const PrefService* EventRewriter::GetPrefService() const { 235 const PrefService* EventRewriter::GetPrefService() const {
331 if (pref_service_for_testing_) 236 if (pref_service_for_testing_)
332 return pref_service_for_testing_; 237 return pref_service_for_testing_;
333 Profile* profile = ProfileManager::GetActiveUserProfile(); 238 Profile* profile = ProfileManager::GetActiveUserProfile();
334 return profile ? profile->GetPrefs() : NULL; 239 return profile ? profile->GetPrefs() : NULL;
335 } 240 }
336 241
337 void EventRewriter::Rewrite(XEvent* event) {
338 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See
339 // crbug.com/136465.
340 if (event->xkey.send_event)
341 return;
342
343 RewriteModifiers(event);
344 RewriteNumPadKeys(event);
345 RewriteExtendedKeys(event);
346 RewriteFunctionKeys(event);
347 }
348
349 bool EventRewriter::IsAppleKeyboard() const { 242 bool EventRewriter::IsAppleKeyboard() const {
350 if (last_device_id_ == kBadDeviceId) 243 if (last_device_id_ == kBadDeviceId)
351 return false; 244 return false;
352 245
353 // Check which device generated |event|. 246 // Check which device generated |event|.
354 std::map<int, DeviceType>::const_iterator iter = 247 std::map<int, DeviceType>::const_iterator iter =
355 device_id_to_type_.find(last_device_id_); 248 device_id_to_type_.find(last_device_id_);
356 if (iter == device_id_to_type_.end()) { 249 if (iter == device_id_to_type_.end()) {
357 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown."; 250 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown.";
358 return false; 251 return false;
359 } 252 }
360 253
361 const DeviceType type = iter->second; 254 const DeviceType type = iter->second;
362 return type == kDeviceAppleKeyboard; 255 return type == kDeviceAppleKeyboard;
363 } 256 }
364 257
365 void EventRewriter::GetRemappedModifierMasks( 258 bool EventRewriter::TopRowKeysAreFunctionKeys(const ui::KeyEvent& event) const {
366 unsigned int original_native_modifiers, 259 const PrefService* prefs = GetPrefService();
367 unsigned int* remapped_native_modifiers) const { 260 if (prefs && prefs->FindPreference(prefs::kLanguageSendFunctionKeys) &&
368 // TODO(glotov): remove the following condition when we do not restart chrome 261 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys))
369 // when user logs in as guest. See Rewrite() for details. 262 return true;
370 if (UserManager::Get()->IsLoggedInAsGuest() &&
371 LoginDisplayHostImpl::default_host()) {
372 return;
373 }
374 263
375 const PrefService* pref_service = GetPrefService(); 264 ash::wm::WindowState* state = ash::wm::GetActiveWindowState();
376 if (!pref_service) 265 return state ? state->top_row_keys_are_function_keys() : false;
377 return; 266 }
378 267
379 // When a diamond key is not available, a Mod2Mask should not treated as a 268 int EventRewriter::GetRemappedModifierMasks(const PrefService& pref_service,
380 // configurable modifier because Mod2Mask may be worked as NumLock mask. 269 const ui::Event& event,
381 // (cf. http://crbug.com/173956) 270 int original_flags) const {
382 const bool skip_mod2 = !HasDiamondKey(); 271 int unmodified_flags = original_flags;
383 // If Mod3 is used by the current input method, don't allow the CapsLock 272 int rewritten_flags = 0;
384 // pref to remap it, or the keyboard behavior will be broken. 273 for (size_t i = 0; unmodified_flags && (i < arraysize(kModifierRemappings));
385 const bool skip_mod3 = IsISOLevel5ShiftUsedByCurrentInputMethod(); 274 ++i) {
386 275 const ModifierRemapping* remapped_key = 0;
387 for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) { 276 if (!(unmodified_flags & kModifierRemappings[i].flag))
388 if ((skip_mod2 && kModifierFlagToPrefName[i].native_modifier == Mod2Mask) ||
389 (skip_mod3 && kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) {
390 continue; 277 continue;
278 switch (kModifierRemappings[i].flag) {
279 case ui::EF_COMMAND_DOWN:
280 // Rewrite Command key presses on an Apple keyboard to Control.
281 if (IsAppleKeyboard()) {
282 DCHECK_EQ(ui::EF_CONTROL_DOWN, kModifierRemappingCtrl->flag);
283 remapped_key = kModifierRemappingCtrl;
284 }
285 break;
286 case ui::EF_CAPS_LOCK_DOWN:
287 // If CapsLock is used by the current input method, don't allow the
288 // CapsLock pref to remap it, or the keyboard behavior will be broken.
289 if (IsISOLevel5ShiftUsedByCurrentInputMethod())
290 continue;
291 break;
292 default:
293 break;
391 } 294 }
392 if (original_native_modifiers & 295 if (!remapped_key && kModifierRemappings[i].pref_name) {
393 kModifierFlagToPrefName[i].native_modifier) { 296 remapped_key =
394 const ModifierRemapping* remapped_key = 297 GetRemappedKey(kModifierRemappings[i].pref_name, pref_service);
395 GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service); 298 }
396 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. 299 if (remapped_key) {
397 if (IsAppleKeyboard() && 300 unmodified_flags &= ~kModifierRemappings[i].flag;
398 (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) { 301 rewritten_flags |= remapped_key->flag;
399 remapped_key = kModifierRemappingCtrl;
400 }
401 if (remapped_key) {
402 *remapped_native_modifiers |= remapped_key->native_modifier;
403 } else {
404 *remapped_native_modifiers |=
405 kModifierFlagToPrefName[i].native_modifier;
406 }
407 } 302 }
408 } 303 }
409 304 return rewritten_flags | unmodified_flags;
410 unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask;
411 if (!skip_mod2)
412 native_mask |= Mod2Mask;
413 if (!skip_mod3)
414 native_mask |= Mod3Mask;
415 *remapped_native_modifiers =
416 (original_native_modifiers & ~native_mask) |
417 *remapped_native_modifiers;
418 } 305 }
419 306
420 bool EventRewriter::RewriteModifiers(XEvent* event) { 307 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode(
421 DCHECK(event->type == KeyPress || event->type == KeyRelease); 308 const KeyboardRemapping* remappings,
309 size_t num_remappings,
310 const MutableKeyState& input,
311 MutableKeyState* remapped_state) {
312 for (size_t i = 0; i < num_remappings; ++i) {
313 const KeyboardRemapping& map = remappings[i];
314 if (input.key_code != map.input_key_code)
315 continue;
316 if ((input.flags & map.input_flags) != map.input_flags)
317 continue;
318 remapped_state->key_code = map.output_key_code;
319 remapped_state->flags = (input.flags & ~map.input_flags) | map.output_flags;
320 return true;
321 }
322 return false;
323 }
324
325 void EventRewriter::RewriteModifierKeys(const ui::KeyEvent& key_event,
326 MutableKeyState* state) {
327 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
328 key_event.type() == ui::ET_KEY_RELEASED);
329
422 // Do nothing if we have just logged in as guest but have not restarted chrome 330 // Do nothing if we have just logged in as guest but have not restarted chrome
423 // process yet (so we are still on the login screen). In this situations we 331 // process yet (so we are still on the login screen). In this situations we
424 // have no user profile so can not do anything useful. 332 // have no user profile so can not do anything useful.
425 // Note that currently, unlike other accounts, when user logs in as guest, we 333 // Note that currently, unlike other accounts, when user logs in as guest, we
426 // restart chrome process. In future this is to be changed. 334 // restart chrome process. In future this is to be changed.
427 // TODO(glotov): remove the following condition when we do not restart chrome 335 // TODO(glotov): remove the following condition when we do not restart chrome
428 // when user logs in as guest. 336 // when user logs in as guest.
337 // TODO(kpschoedel): check whether this is still necessary.
429 if (UserManager::Get()->IsLoggedInAsGuest() && 338 if (UserManager::Get()->IsLoggedInAsGuest() &&
430 LoginDisplayHostImpl::default_host()) 339 LoginDisplayHostImpl::default_host())
431 return false; 340 return;
432 341
433 const PrefService* pref_service = GetPrefService(); 342 const PrefService* pref_service = GetPrefService();
434 if (!pref_service) 343 if (!pref_service)
435 return false; 344 return;
436 345
437 DCHECK_EQ(input_method::kControlKey, kModifierRemappingCtrl->remap_to); 346 MutableKeyState incoming = *state;
347 state->flags = ui::EF_NONE;
348 int characteristic_flag = ui::EF_NONE;
438 349
439 XKeyEvent* xkey = &event->xkey; 350 // First, remap the key code.
440 KeySym keysym = XLookupKeysym(xkey, 0);
441 ui::KeyboardCode original_keycode = ui::KeyboardCodeFromNative(event);
442 ui::KeyboardCode remapped_keycode = original_keycode;
443 KeyCode remapped_native_keycode = xkey->keycode;
444
445 // First, remap |keysym|.
446 const ModifierRemapping* remapped_key = NULL; 351 const ModifierRemapping* remapped_key = NULL;
447 switch (keysym) { 352 switch (incoming.key_code) {
448 // On Chrome OS, XF86XK_Launch6 (F15) with Mod2Mask is sent when Diamond 353 // On Chrome OS, F15 (XF86XK_Launch6) with NumLock (Mod2Mask) is sent
449 // key is pressed. 354 // when Diamond key is pressed.
450 case XF86XK_Launch6: 355 case ui::VKEY_F15:
451 // When diamond key is not available, the configuration UI for Diamond 356 // When diamond key is not available, the configuration UI for Diamond
452 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo 357 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo
453 // syncable pref. 358 // syncable pref.
454 if (HasDiamondKey()) 359 if (HasDiamondKey())
455 remapped_key = 360 remapped_key =
456 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service); 361 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service);
457 // Default behavior is Ctrl key. 362 // Default behavior is Ctrl key.
458 if (!remapped_key) 363 if (!remapped_key) {
364 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
459 remapped_key = kModifierRemappingCtrl; 365 remapped_key = kModifierRemappingCtrl;
366 characteristic_flag = ui::EF_CONTROL_DOWN;
367 }
460 break; 368 break;
461 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock 369 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock
462 // is pressed (with one exception: when 370 // is pressed (with one exception: when
463 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates 371 // IsISOLevel5ShiftUsedByCurrentInputMethod() is true, the key generates
464 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7). 372 // XK_ISO_Level3_Shift with Mod3Mask, not XF86XK_Launch7).
465 case XF86XK_Launch7: 373 case ui::VKEY_F16:
374 characteristic_flag = ui::EF_CAPS_LOCK_DOWN;
466 remapped_key = 375 remapped_key =
467 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); 376 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service);
468 break; 377 break;
469 case XK_Super_L: 378 case ui::VKEY_LWIN:
470 case XK_Super_R: 379 case ui::VKEY_RWIN:
471 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. 380 characteristic_flag = ui::EF_COMMAND_DOWN;
472 if (IsAppleKeyboard()) 381 // Rewrite Command-L/R key presses on an Apple keyboard to Control.
382 if (IsAppleKeyboard()) {
383 DCHECK_EQ(ui::VKEY_CONTROL, kModifierRemappingCtrl->key_code);
473 remapped_key = kModifierRemappingCtrl; 384 remapped_key = kModifierRemappingCtrl;
474 else 385 } else {
475 remapped_key = 386 remapped_key =
476 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); 387 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service);
388 }
477 // Default behavior is Super key, hence don't remap the event if the pref 389 // Default behavior is Super key, hence don't remap the event if the pref
478 // is unavailable. 390 // is unavailable.
479 break; 391 break;
480 case XK_Control_L: 392 case ui::VKEY_CONTROL:
481 case XK_Control_R: 393 characteristic_flag = ui::EF_CONTROL_DOWN;
482 remapped_key = 394 remapped_key =
483 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service); 395 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service);
484 break; 396 break;
485 case XK_Alt_L: 397 case ui::VKEY_MENU:
486 case XK_Alt_R: 398 // ALT key
487 case XK_Meta_L: 399 characteristic_flag = ui::EF_ALT_DOWN;
488 case XK_Meta_R:
489 remapped_key = 400 remapped_key =
490 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service); 401 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service);
491 break; 402 break;
492 default: 403 default:
493 break; 404 break;
494 } 405 }
495 406
496 if (remapped_key) { 407 if (remapped_key) {
497 int flags = ui::EventFlagsFromNative(event); 408 state->key_code = remapped_key->key_code;
498 remapped_keycode = remapped_key->keycode; 409 incoming.flags |= characteristic_flag;
499 const size_t level = ((flags & ui::EF_SHIFT_DOWN) ? (1 << 1) : 0) + 410 characteristic_flag = remapped_key->flag;
500 (IsRight(keysym) ? (1 << 0) : 0);
501 const KeySym native_keysym = remapped_key->native_keysyms[level];
502 remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym);
503 } 411 }
504 412
505 // Next, remap modifier bits. 413 // Next, remap modifier bits.
506 unsigned int remapped_native_modifiers = 0U; 414 state->flags |=
507 GetRemappedModifierMasks(xkey->state, &remapped_native_modifiers); 415 GetRemappedModifierMasks(*pref_service, key_event, incoming.flags);
416 if (key_event.type() == ui::ET_KEY_PRESSED)
417 state->flags |= characteristic_flag;
418 else
419 state->flags &= ~characteristic_flag;
508 420
509 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if 421 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if
510 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external 422 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external
511 // keyboard is pressed) since X can handle that case. 423 // keyboard is pressed) since X can handle that case.
512 if (event->type == KeyPress && 424 if (key_event.type() == ui::ET_KEY_PRESSED &&
513 original_keycode != ui::VKEY_CAPITAL && 425 incoming.key_code != ui::VKEY_CAPITAL &&
514 remapped_keycode == ui::VKEY_CAPITAL) { 426 state->key_code == ui::VKEY_CAPITAL) {
515 input_method::ImeKeyboard* keyboard = keyboard_for_testing_ ? 427 chromeos::input_method::ImeKeyboard* ime_keyboard =
516 keyboard_for_testing_ : 428 ime_keyboard_for_testing_
517 input_method::InputMethodManager::Get()->GetImeKeyboard(); 429 ? ime_keyboard_for_testing_
518 keyboard->SetCapsLockEnabled(!keyboard->CapsLockIsEnabled()); 430 : chromeos::input_method::InputMethodManager::Get()
431 ->GetImeKeyboard();
432 ime_keyboard->SetCapsLockEnabled(!ime_keyboard->CapsLockIsEnabled());
433 }
434 }
435
436 void EventRewriter::RewriteNumPadKeys(const ui::KeyEvent& key_event,
437 MutableKeyState* state) {
438 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
439 key_event.type() == ui::ET_KEY_RELEASED);
440 if (!(state->flags & ui::EF_NUMPAD_KEY))
441 return;
442 MutableKeyState incoming = *state;
443
444 static const KeyboardRemapping kNumPadRemappings[] = {
445 {ui::VKEY_INSERT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD0, ui::EF_NUMPAD_KEY},
446 {ui::VKEY_DELETE, ui::EF_NUMPAD_KEY, ui::VKEY_DECIMAL, ui::EF_NUMPAD_KEY},
447 {ui::VKEY_END, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD1, ui::EF_NUMPAD_KEY},
448 {ui::VKEY_DOWN, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD2, ui::EF_NUMPAD_KEY},
449 {ui::VKEY_NEXT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD3, ui::EF_NUMPAD_KEY},
450 {ui::VKEY_LEFT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD4, ui::EF_NUMPAD_KEY},
451 {ui::VKEY_CLEAR, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD5, ui::EF_NUMPAD_KEY},
452 {ui::VKEY_RIGHT, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD6, ui::EF_NUMPAD_KEY},
453 {ui::VKEY_HOME, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD7, ui::EF_NUMPAD_KEY},
454 {ui::VKEY_UP, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD8, ui::EF_NUMPAD_KEY},
455 {ui::VKEY_PRIOR, ui::EF_NUMPAD_KEY, ui::VKEY_NUMPAD9, ui::EF_NUMPAD_KEY},
456 };
457
458 RewriteWithKeyboardRemappingsByKeyCode(
459 kNumPadRemappings, arraysize(kNumPadRemappings), incoming, state);
460 }
461
462 void EventRewriter::RewriteExtendedKeys(const ui::KeyEvent& key_event,
463 MutableKeyState* state) {
464 DCHECK(key_event.type() == ui::ET_KEY_PRESSED ||
465 key_event.type() == ui::ET_KEY_RELEASED);
466
467 MutableKeyState incoming = *state;
468 bool rewritten = false;
469
470 if ((incoming.flags & (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) ==
471 (ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN)) {
472 // Allow Search to avoid rewriting extended keys.
473 static const KeyboardRemapping kAvoidRemappings[] = {
474 { // Alt+Backspace
475 ui::VKEY_BACK, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_BACK,
476 ui::EF_ALT_DOWN,
477 },
478 { // Control+Alt+Up
479 ui::VKEY_UP,
480 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
481 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
482 },
483 { // Alt+Up
484 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_UP,
485 ui::EF_ALT_DOWN,
486 },
487 { // Control+Alt+Down
488 ui::VKEY_DOWN,
489 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_COMMAND_DOWN,
490 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN,
491 },
492 { // Alt+Down
493 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN, ui::VKEY_DOWN,
494 ui::EF_ALT_DOWN,
495 }};
496
497 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
498 kAvoidRemappings, arraysize(kAvoidRemappings), incoming, state);
519 } 499 }
520 500
521 OverwriteEvent(event, remapped_native_keycode, remapped_native_modifiers); 501 if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
522 return true; 502 static const KeyboardRemapping kSearchRemappings[] = {
503 { // Search+BackSpace -> Delete
504 ui::VKEY_BACK, ui::EF_COMMAND_DOWN, ui::VKEY_DELETE, 0},
505 { // Search+Left -> Home
506 ui::VKEY_LEFT, ui::EF_COMMAND_DOWN, ui::VKEY_HOME, 0},
507 { // Search+Up -> Prior (aka PageUp)
508 ui::VKEY_UP, ui::EF_COMMAND_DOWN, ui::VKEY_PRIOR, 0},
509 { // Search+Right -> End
510 ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN, ui::VKEY_END, 0},
511 { // Search+Down -> Next (aka PageDown)
512 ui::VKEY_DOWN, ui::EF_COMMAND_DOWN, ui::VKEY_NEXT, 0},
513 { // Search+Period -> Insert
514 ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN, ui::VKEY_INSERT, 0}};
515
516 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
517 kSearchRemappings, arraysize(kSearchRemappings), incoming, state);
518 }
519
520 if (!rewritten && (incoming.flags & ui::EF_ALT_DOWN)) {
521 static const KeyboardRemapping kNonSearchRemappings[] = {
522 { // Alt+BackSpace -> Delete
523 ui::VKEY_BACK, ui::EF_ALT_DOWN, ui::VKEY_DELETE, 0},
524 { // Control+Alt+Up -> Home
525 ui::VKEY_UP, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_HOME, 0},
526 { // Alt+Up -> Prior (aka PageUp)
527 ui::VKEY_UP, ui::EF_ALT_DOWN, ui::VKEY_PRIOR, 0},
528 { // Control+Alt+Down -> End
529 ui::VKEY_DOWN, ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, ui::VKEY_END, 0},
530 { // Alt+Down -> Next (aka PageDown)
531 ui::VKEY_DOWN, ui::EF_ALT_DOWN, ui::VKEY_NEXT, 0}};
532
533 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
534 kNonSearchRemappings, arraysize(kNonSearchRemappings), incoming, state);
535 }
523 } 536 }
524 537
525 bool EventRewriter::RewriteNumPadKeys(XEvent* event) { 538 void EventRewriter::RewriteFunctionKeys(const ui::KeyEvent& key_event,
526 DCHECK(event->type == KeyPress || event->type == KeyRelease); 539 MutableKeyState* state) {
527 bool rewritten = false; 540 CHECK(key_event.type() == ui::ET_KEY_PRESSED ||
528 XKeyEvent* xkey = &event->xkey; 541 key_event.type() == ui::ET_KEY_RELEASED);
529 const KeySym keysym = XLookupKeysym(xkey, 0); 542 MutableKeyState incoming = *state;
530 switch (keysym) {
531 case XK_KP_Insert:
532 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_0),
533 xkey->state | Mod2Mask);
534 rewritten = true;
535 break;
536 case XK_KP_Delete:
537 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_Decimal),
538 xkey->state | Mod2Mask);
539 rewritten = true;
540 break;
541 case XK_KP_End:
542 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_1),
543 xkey->state | Mod2Mask);
544 rewritten = true;
545 break;
546 case XK_KP_Down:
547 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_2),
548 xkey->state | Mod2Mask);
549 rewritten = true;
550 break;
551 case XK_KP_Next:
552 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_3),
553 xkey->state | Mod2Mask);
554 rewritten = true;
555 break;
556 case XK_KP_Left:
557 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_4),
558 xkey->state | Mod2Mask);
559 rewritten = true;
560 break;
561 case XK_KP_Begin:
562 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_5),
563 xkey->state | Mod2Mask);
564 rewritten = true;
565 break;
566 case XK_KP_Right:
567 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_6),
568 xkey->state | Mod2Mask);
569 rewritten = true;
570 break;
571 case XK_KP_Home:
572 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_7),
573 xkey->state | Mod2Mask);
574 rewritten = true;
575 break;
576 case XK_KP_Up:
577 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_8),
578 xkey->state | Mod2Mask);
579 rewritten = true;
580 break;
581 case XK_KP_Prior:
582 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_9),
583 xkey->state | Mod2Mask);
584 rewritten = true;
585 break;
586 case XK_KP_Divide:
587 case XK_KP_Multiply:
588 case XK_KP_Subtract:
589 case XK_KP_Add:
590 case XK_KP_Enter:
591 // Add Mod2Mask for consistency.
592 OverwriteEvent(event, xkey->keycode, xkey->state | Mod2Mask);
593 rewritten = true;
594 break;
595 default:
596 break;
597 }
598 return rewritten;
599 }
600
601 bool EventRewriter::RewriteExtendedKeys(XEvent* event) {
602 DCHECK(event->type == KeyPress || event->type == KeyRelease);
603 XKeyEvent* xkey = &event->xkey;
604 const KeySym keysym = XLookupKeysym(xkey, 0);
605
606 KeySym remapped_native_keysym = 0;
607 unsigned int remapped_native_mods = 0;
608 bool rewritten = false; 543 bool rewritten = false;
609 544
610 if (xkey->state & Mod4Mask) { 545 if ((incoming.key_code >= ui::VKEY_F1) &&
611 // Allow Search to avoid rewriting extended keys. 546 (incoming.key_code <= ui::VKEY_F24)) {
612 static const KeyboardRemapping kAvoidRemappings[] = { 547 // By default the top row (F1-F12) keys are system keys for back, forward,
613 { // Alt+Backspace 548 // brightness, volume, etc. However, windows for v2 apps can optionally
614 XK_BackSpace, Mod1Mask | Mod4Mask, 549 // request raw function keys for these keys.
615 XK_BackSpace, Mod1Mask, 550 bool top_row_keys_are_function_keys = TopRowKeysAreFunctionKeys(key_event);
616 }, 551 bool search_is_pressed = (incoming.flags & ui::EF_COMMAND_DOWN) != 0;
617 { // Control+Alt+Up
618 XK_Up, Mod1Mask | ControlMask | Mod4Mask,
619 XK_Up, Mod1Mask | ControlMask,
620 },
621 { // Alt+Up
622 XK_Up, Mod1Mask | Mod4Mask,
623 XK_Up, Mod1Mask,
624 },
625 { // Control+Alt+Down
626 XK_Down, Mod1Mask | ControlMask | Mod4Mask,
627 XK_Down, Mod1Mask | ControlMask,
628 },
629 { // Alt+Down
630 XK_Down, Mod1Mask | Mod4Mask,
631 XK_Down, Mod1Mask,
632 }
633 };
634 552
635 rewritten = RewriteWithKeyboardRemappingsByKeySym( 553 // Search? Top Row Result
636 kAvoidRemappings, 554 // ------- -------- ------
637 arraysize(kAvoidRemappings), 555 // No Fn Unchanged
638 keysym, 556 // No System Fn -> System
639 xkey->state, 557 // Yes Fn Fn -> System
640 &remapped_native_keysym, 558 // Yes System Search+Fn -> Fn
641 &remapped_native_mods); 559 if (top_row_keys_are_function_keys == search_is_pressed) {
642 } 560 // Rewrite the F1-F12 keys on a Chromebook keyboard to system keys.
643 561 static const KeyboardRemapping kFkeysToSystemKeys[] = {
644 if (!rewritten) { 562 {ui::VKEY_F1, 0, ui::VKEY_BROWSER_BACK, 0},
645 static const KeyboardRemapping kSearchRemappings[] = { 563 {ui::VKEY_F2, 0, ui::VKEY_BROWSER_FORWARD, 0},
646 { // Search+BackSpace -> Delete 564 {ui::VKEY_F3, 0, ui::VKEY_BROWSER_REFRESH, 0},
647 XK_BackSpace, Mod4Mask, 565 {ui::VKEY_F4, 0, ui::VKEY_MEDIA_LAUNCH_APP2, 0},
648 XK_Delete, 0 566 {ui::VKEY_F5, 0, ui::VKEY_MEDIA_LAUNCH_APP1, 0},
649 }, 567 {ui::VKEY_F6, 0, ui::VKEY_BRIGHTNESS_DOWN, 0},
650 { // Search+Left -> Home 568 {ui::VKEY_F7, 0, ui::VKEY_BRIGHTNESS_UP, 0},
651 XK_Left, Mod4Mask, 569 {ui::VKEY_F8, 0, ui::VKEY_VOLUME_MUTE, 0},
652 XK_Home, 0 570 {ui::VKEY_F9, 0, ui::VKEY_VOLUME_DOWN, 0},
653 }, 571 {ui::VKEY_F10, 0, ui::VKEY_VOLUME_UP, 0},
654 { // Search+Up -> Prior (aka PageUp) 572 };
655 XK_Up, Mod4Mask, 573 MutableKeyState incoming_without_command = incoming;
656 XK_Prior, 0 574 incoming_without_command.flags &= ~ui::EF_COMMAND_DOWN;
657 }, 575 rewritten =
658 { // Search+Right -> End 576 RewriteWithKeyboardRemappingsByKeyCode(kFkeysToSystemKeys,
659 XK_Right, Mod4Mask, 577 arraysize(kFkeysToSystemKeys),
660 XK_End, 0 578 incoming_without_command,
661 }, 579 state);
662 { // Search+Down -> Next (aka PageDown) 580 } else if (search_is_pressed) {
663 XK_Down, Mod4Mask, 581 // Allow Search to avoid rewriting F1-F12.
664 XK_Next, 0 582 state->flags &= ~ui::EF_COMMAND_DOWN;
665 }, 583 rewritten = true;
666 { // Search+Period -> Insert
667 XK_period, Mod4Mask,
668 XK_Insert, 0
669 }
670 };
671
672 rewritten = RewriteWithKeyboardRemappingsByKeySym(
673 kSearchRemappings,
674 arraysize(kSearchRemappings),
675 keysym,
676 xkey->state,
677 &remapped_native_keysym,
678 &remapped_native_mods);
679 }
680
681 if (!rewritten) {
682 static const KeyboardRemapping kNonSearchRemappings[] = {
683 { // Alt+BackSpace -> Delete
684 XK_BackSpace, Mod1Mask,
685 XK_Delete, 0
686 },
687 { // Control+Alt+Up -> Home
688 XK_Up, Mod1Mask | ControlMask,
689 XK_Home, 0
690 },
691 { // Alt+Up -> Prior (aka PageUp)
692 XK_Up, Mod1Mask,
693 XK_Prior, 0
694 },
695 { // Control+Alt+Down -> End
696 XK_Down, Mod1Mask | ControlMask,
697 XK_End, 0
698 },
699 { // Alt+Down -> Next (aka PageDown)
700 XK_Down, Mod1Mask,
701 XK_Next, 0
702 }
703 };
704
705 rewritten = RewriteWithKeyboardRemappingsByKeySym(
706 kNonSearchRemappings,
707 arraysize(kNonSearchRemappings),
708 keysym,
709 xkey->state,
710 &remapped_native_keysym,
711 &remapped_native_mods);
712 }
713
714 if (!rewritten)
715 return false;
716
717 OverwriteEvent(event,
718 NativeKeySymToNativeKeycode(remapped_native_keysym),
719 remapped_native_mods);
720 return true;
721 }
722
723 bool EventRewriter::RewriteFunctionKeys(XEvent* event) {
724 DCHECK(event->type == KeyPress || event->type == KeyRelease);
725 XKeyEvent* xkey = &event->xkey;
726 const KeySym keysym = XLookupKeysym(xkey, 0);
727
728 KeySym remapped_native_keysym = 0;
729 unsigned int remapped_native_mods = 0;
730 bool rewritten = false;
731
732 // By default the top row (F1-F12) keys are special keys for back, forward,
733 // brightness, volume, etc. However, windows for v2 apps can optionally
734 // request raw function keys for these keys.
735 bool top_row_keys_are_special_keys = !TopRowKeysAreFunctionKeys(event);
736
737 if ((xkey->state & Mod4Mask) && top_row_keys_are_special_keys) {
738 // Allow Search to avoid rewriting F1-F12.
739 static const KeyboardRemapping kFkeysToFkeys[] = {
740 { XK_F1, Mod4Mask, XK_F1, },
741 { XK_F2, Mod4Mask, XK_F2, },
742 { XK_F3, Mod4Mask, XK_F3, },
743 { XK_F4, Mod4Mask, XK_F4, },
744 { XK_F5, Mod4Mask, XK_F5, },
745 { XK_F6, Mod4Mask, XK_F6, },
746 { XK_F7, Mod4Mask, XK_F7, },
747 { XK_F8, Mod4Mask, XK_F8, },
748 { XK_F9, Mod4Mask, XK_F9, },
749 { XK_F10, Mod4Mask, XK_F10, },
750 { XK_F11, Mod4Mask, XK_F11, },
751 { XK_F12, Mod4Mask, XK_F12, },
752 };
753
754 rewritten = RewriteWithKeyboardRemappingsByKeySym(
755 kFkeysToFkeys,
756 arraysize(kFkeysToFkeys),
757 keysym,
758 xkey->state,
759 &remapped_native_keysym,
760 &remapped_native_mods);
761 }
762
763 if (!rewritten) {
764 static const KeyboardRemapping kFkeysToSpecialKeys[] = {
765 { XK_F1, 0, XF86XK_Back, 0 },
766 { XK_F2, 0, XF86XK_Forward, 0 },
767 { XK_F3, 0, XF86XK_Reload, 0 },
768 { XK_F4, 0, XF86XK_LaunchB, 0 },
769 { XK_F5, 0, XF86XK_LaunchA, 0 },
770 { XK_F6, 0, XF86XK_MonBrightnessDown, 0 },
771 { XK_F7, 0, XF86XK_MonBrightnessUp, 0 },
772 { XK_F8, 0, XF86XK_AudioMute, 0 },
773 { XK_F9, 0, XF86XK_AudioLowerVolume, 0 },
774 { XK_F10, 0, XF86XK_AudioRaiseVolume, 0 },
775 };
776
777 if (top_row_keys_are_special_keys) {
778 // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys.
779 rewritten = RewriteWithKeyboardRemappingsByKeySym(
780 kFkeysToSpecialKeys,
781 arraysize(kFkeysToSpecialKeys),
782 keysym,
783 xkey->state,
784 &remapped_native_keysym,
785 &remapped_native_mods);
786 } else if (xkey->state & Mod4Mask) {
787 // Use Search + F1-F12 for the special keys.
788 rewritten = RewriteWithKeyboardRemappingsByKeySym(
789 kFkeysToSpecialKeys,
790 arraysize(kFkeysToSpecialKeys),
791 keysym,
792 xkey->state & ~Mod4Mask,
793 &remapped_native_keysym,
794 &remapped_native_mods);
795 } 584 }
796 } 585 }
797 586
798 if (!rewritten && (xkey->state & Mod4Mask)) { 587 if (!rewritten && (incoming.flags & ui::EF_COMMAND_DOWN)) {
799 // Remap Search+<number> to F<number>. 588 // Remap Search+<number> to F<number>.
800 // We check the keycode here instead of the keysym, as these keys have 589 // We check the keycode here instead of the keysym, as these keys have
801 // different keysyms when modifiers are pressed, such as shift. 590 // different keysyms when modifiers are pressed, such as shift.
802 591
803 // TODO(danakj): On some i18n keyboards, these choices will be bad and we 592 // TODO(danakj): On some i18n keyboards, these choices will be bad and we
804 // should make layout-specific choices here. For eg. on a french keyboard 593 // should make layout-specific choices here. For eg. on a french keyboard
805 // "-" and "6" are the same key, so F11 will not be accessible. 594 // "-" and "6" are the same key, so F11 will not be accessible.
806 static const KeyboardRemapping kNumberKeysToFkeys[] = { 595 static const KeyboardRemapping kNumberKeysToFkeys[] = {
807 { XK_1, Mod4Mask, XK_F1, 0 }, 596 {ui::VKEY_1, ui::EF_COMMAND_DOWN, ui::VKEY_F1, 0},
808 { XK_2, Mod4Mask, XK_F2, 0 }, 597 {ui::VKEY_2, ui::EF_COMMAND_DOWN, ui::VKEY_F2, 0},
809 { XK_3, Mod4Mask, XK_F3, 0 }, 598 {ui::VKEY_3, ui::EF_COMMAND_DOWN, ui::VKEY_F3, 0},
810 { XK_4, Mod4Mask, XK_F4, 0 }, 599 {ui::VKEY_4, ui::EF_COMMAND_DOWN, ui::VKEY_F4, 0},
811 { XK_5, Mod4Mask, XK_F5, 0 }, 600 {ui::VKEY_5, ui::EF_COMMAND_DOWN, ui::VKEY_F5, 0},
812 { XK_6, Mod4Mask, XK_F6, 0 }, 601 {ui::VKEY_6, ui::EF_COMMAND_DOWN, ui::VKEY_F6, 0},
813 { XK_7, Mod4Mask, XK_F7, 0 }, 602 {ui::VKEY_7, ui::EF_COMMAND_DOWN, ui::VKEY_F7, 0},
814 { XK_8, Mod4Mask, XK_F8, 0 }, 603 {ui::VKEY_8, ui::EF_COMMAND_DOWN, ui::VKEY_F8, 0},
815 { XK_9, Mod4Mask, XK_F9, 0 }, 604 {ui::VKEY_9, ui::EF_COMMAND_DOWN, ui::VKEY_F9, 0},
816 { XK_0, Mod4Mask, XK_F10, 0 }, 605 {ui::VKEY_0, ui::EF_COMMAND_DOWN, ui::VKEY_F10, 0},
817 { XK_minus, Mod4Mask, XK_F11, 0 }, 606 {ui::VKEY_OEM_MINUS, ui::EF_COMMAND_DOWN, ui::VKEY_F11, 0},
818 { XK_equal, Mod4Mask, XK_F12, 0 } 607 {ui::VKEY_OEM_PLUS, ui::EF_COMMAND_DOWN, ui::VKEY_F12, 0}};
819 };
820
821 rewritten = RewriteWithKeyboardRemappingsByKeyCode( 608 rewritten = RewriteWithKeyboardRemappingsByKeyCode(
822 kNumberKeysToFkeys, 609 kNumberKeysToFkeys, arraysize(kNumberKeysToFkeys), incoming, state);
823 arraysize(kNumberKeysToFkeys),
824 xkey->keycode,
825 xkey->state,
826 &remapped_native_keysym,
827 &remapped_native_mods);
828 } 610 }
829
830 if (!rewritten)
831 return false;
832
833 OverwriteEvent(event,
834 NativeKeySymToNativeKeycode(remapped_native_keysym),
835 remapped_native_mods);
836 return true;
837 } 611 }
838 612
839 void EventRewriter::RewriteLocatedEvent(XEvent* event) { 613 void EventRewriter::RewriteLocatedEvent(const ui::Event& event,
840 DCHECK_EQ(GenericEvent, event->type); 614 MutableKeyState* state) {
841 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data); 615 const PrefService* pref_service = GetPrefService();
842 if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease) 616 if (!pref_service)
843 return; 617 return;
844 618
845 // First, remap modifier masks. 619 // First, remap modifier masks.
846 unsigned int remapped_native_modifiers = 0U; 620 state->flags = GetRemappedModifierMasks(*pref_service, event, state->flags);
847 GetRemappedModifierMasks(xievent->mods.effective, &remapped_native_modifiers); 621
848 xievent->mods.effective = remapped_native_modifiers; 622 #if defined(USE_X11)
623 // TODO(kpschoedel): de-X11 with unified device ids from crbug.com/360377
624 XEvent* xevent = event.native_event();
625 if (!xevent || xevent->type != GenericEvent)
626 return;
627 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
628 if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease)
629 return;
849 630
850 // Then, remap Alt+Button1 to Button3. 631 // Then, remap Alt+Button1 to Button3.
851 if ((xievent->evtype == XI_ButtonPress || 632 if ((xievent->evtype == XI_ButtonPress ||
852 pressed_device_ids_.count(xievent->sourceid)) && 633 pressed_device_ids_.count(xievent->sourceid)) &&
853 (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) { 634 (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) {
635 state->flags &= ~(ui::EF_ALT_DOWN | ui::EF_LEFT_MOUSE_BUTTON);
636 state->flags |= ui::EF_RIGHT_MOUSE_BUTTON;
854 xievent->mods.effective &= ~Mod1Mask; 637 xievent->mods.effective &= ~Mod1Mask;
855 xievent->detail = Button3; 638 xievent->detail = Button3;
856 if (xievent->evtype == XI_ButtonRelease) { 639 if (xievent->evtype == XI_ButtonRelease) {
857 // On the release clear the left button from the existing state and the 640 // On the release clear the left button from the existing state and the
858 // mods, and set the right button. 641 // mods, and set the right button.
859 XISetMask(xievent->buttons.mask, Button3); 642 XISetMask(xievent->buttons.mask, Button3);
860 XIClearMask(xievent->buttons.mask, Button1); 643 XIClearMask(xievent->buttons.mask, Button1);
861 xievent->mods.effective &= ~Button1Mask; 644 xievent->mods.effective &= ~Button1Mask;
862 pressed_device_ids_.erase(xievent->sourceid); 645 pressed_device_ids_.erase(xievent->sourceid);
863 } else { 646 } else {
864 pressed_device_ids_.insert(xievent->sourceid); 647 pressed_device_ids_.insert(xievent->sourceid);
865 } 648 }
866 } 649 }
867 } 650 #endif // defined(USE_X11)
868
869 void EventRewriter::OverwriteEvent(XEvent* event,
870 unsigned int new_native_keycode,
871 unsigned int new_native_state) {
872 DCHECK(event->type == KeyPress || event->type == KeyRelease);
873 XKeyEvent* xkey = &event->xkey;
874 xkey->keycode = new_native_keycode;
875 xkey->state = new_native_state;
876 } 651 }
877 652
878 EventRewriter::DeviceType EventRewriter::DeviceAddedInternal( 653 EventRewriter::DeviceType EventRewriter::DeviceAddedInternal(
879 int device_id, 654 int device_id,
880 const std::string& device_name) { 655 const std::string& device_name) {
881 const DeviceType type = EventRewriter::GetDeviceType(device_name); 656 const DeviceType type = GetDeviceType(device_name);
882 if (type == kDeviceAppleKeyboard) { 657 if (type == kDeviceAppleKeyboard) {
883 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " 658 VLOG(1) << "Apple keyboard '" << device_name << "' connected: "
884 << "id=" << device_id; 659 << "id=" << device_id;
885 } 660 }
886 // Always overwrite the existing device_id since the X server may reuse a 661 // Always overwrite the existing device_id since the X server may reuse a
887 // device id for an unattached device. 662 // device id for an unattached device.
888 device_id_to_type_[device_id] = type; 663 device_id_to_type_[device_id] = type;
889 return type; 664 return type;
890 } 665 }
891 666
667 #if defined(USE_X11)
668 void EventRewriter::WillProcessEvent(const ui::PlatformEvent& event) {
669 XEvent* xevent = event;
670 if (xevent->type == GenericEvent) {
671 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
672 if (xievent->evtype == XI_KeyPress || xievent->evtype == XI_KeyRelease) {
673 if (xievent->deviceid == xievent->sourceid)
674 DeviceKeyPressedOrReleased(xievent->deviceid);
675 }
676 }
677 }
678
679 void EventRewriter::DidProcessEvent(const ui::PlatformEvent& event) {
680 }
681
682 void EventRewriter::DeviceHierarchyChanged() {
683 }
684
685 void EventRewriter::DeviceAdded(int device_id) {
686 DCHECK_NE(XIAllDevices, device_id);
687 DCHECK_NE(XIAllMasterDevices, device_id);
688 if (device_id == XIAllDevices || device_id == XIAllMasterDevices) {
689 LOG(ERROR) << "Unexpected device_id passed: " << device_id;
690 return;
691 }
692
693 int ndevices_return = 0;
694 XIDeviceInfo* device_info =
695 XIQueryDevice(gfx::GetXDisplay(), device_id, &ndevices_return);
696
697 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices,
698 // the number of devices found should be either 0 (not found) or 1.
699 if (!device_info) {
700 LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown.";
701 return;
702 }
703
704 DCHECK_EQ(1, ndevices_return);
705 for (int i = 0; i < ndevices_return; ++i) {
706 DCHECK_EQ(device_id, device_info[i].deviceid); // see the comment above.
707 DCHECK(device_info[i].name);
708 DeviceAddedInternal(device_info[i].deviceid, device_info[i].name);
709 }
710
711 XIFreeDeviceInfo(device_info);
712 }
713
714 void EventRewriter::DeviceRemoved(int device_id) {
715 device_id_to_type_.erase(device_id);
716 }
717 #endif
718
892 } // namespace chromeos 719 } // namespace chromeos
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/events/event_rewriter.h ('k') | chrome/browser/chromeos/events/event_rewriter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698