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

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