OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/ash/event_rewriter.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "ash/shell.h" | |
10 #include "base/logging.h" | |
11 #include "base/prefs/pref_service.h" | |
12 #include "base/strings/string_util.h" | |
13 #include "chrome/browser/profiles/profile_manager.h" | |
14 #include "ui/aura/root_window.h" | |
15 #include "ui/events/event.h" | |
16 #include "ui/events/event_utils.h" | |
17 #include "ui/events/keycodes/keyboard_code_conversion.h" | |
18 | |
19 #if defined(OS_CHROMEOS) | |
20 #include <X11/extensions/XInput2.h> | |
21 #include <X11/keysym.h> | |
22 #include <X11/XF86keysym.h> | |
23 #include <X11/Xlib.h> | |
24 | |
25 // Get rid of a macro from Xlib.h that conflicts with OwnershipService class. | |
26 #undef Status | |
27 | |
28 #include "ash/wm/window_state.h" | |
29 #include "base/command_line.h" | |
30 #include "base/sys_info.h" | |
31 #include "chrome/browser/chromeos/keyboard_driven_event_rewriter.h" | |
32 #include "chrome/browser/chromeos/login/login_display_host_impl.h" | |
33 #include "chrome/browser/chromeos/login/user_manager.h" | |
34 #include "chrome/browser/chromeos/xinput_hierarchy_changed_event_listener.h" | |
35 #include "chrome/common/pref_names.h" | |
36 #include "chromeos/chromeos_switches.h" | |
37 #include "chromeos/ime/input_method_manager.h" | |
38 #include "chromeos/ime/xkeyboard.h" | |
39 #include "ui/base/x/x11_util.h" | |
40 #include "ui/events/keycodes/keyboard_code_conversion_x.h" | |
41 #include "ui/views/corewm/window_util.h" | |
42 #endif | |
43 | |
44 namespace { | |
45 | |
46 const int kBadDeviceId = -1; | |
47 | |
48 #if defined(OS_CHROMEOS) | |
49 const char kNeo2LayoutId[] = "xkb:de:neo:ger"; | |
50 const char kCaMultixLayoutId[] = "xkb:ca:multix:fra"; | |
51 | |
52 // A key code and a flag we should use when a key is remapped to |remap_to|. | |
53 const struct ModifierRemapping { | |
54 int remap_to; | |
55 int flag; | |
56 unsigned int native_modifier; | |
57 ui::KeyboardCode keycode; | |
58 KeySym native_keysyms[4]; // left, right, shift+left, shift+right. | |
59 } kModifierRemappings[] = { | |
60 { chromeos::input_method::kSearchKey, 0, Mod4Mask, ui::VKEY_LWIN, | |
61 { XK_Super_L, XK_Super_L, XK_Super_L, XK_Super_L }}, | |
62 { chromeos::input_method::kControlKey, ui::EF_CONTROL_DOWN, ControlMask, | |
63 ui::VKEY_CONTROL, | |
64 { XK_Control_L, XK_Control_R, XK_Control_L, XK_Control_R }}, | |
65 { chromeos::input_method::kAltKey, ui::EF_ALT_DOWN, Mod1Mask, | |
66 ui::VKEY_MENU, { XK_Alt_L, XK_Alt_R, XK_Meta_L, XK_Meta_R }}, | |
67 { chromeos::input_method::kVoidKey, 0, 0U, ui::VKEY_UNKNOWN, | |
68 { XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol, XK_VoidSymbol }}, | |
69 { chromeos::input_method::kCapsLockKey, 0, 0U, ui::VKEY_CAPITAL, | |
70 { XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock, XK_Caps_Lock }}, | |
71 { chromeos::input_method::kEscapeKey, 0, 0U, ui::VKEY_ESCAPE, | |
72 { XK_Escape, XK_Escape, XK_Escape, XK_Escape }}, | |
73 }; | |
74 | |
75 const ModifierRemapping* kModifierRemappingCtrl = &kModifierRemappings[1]; | |
76 | |
77 // A structure for converting |native_modifier| to a pair of |flag| and | |
78 // |pref_name|. | |
79 const struct ModifierFlagToPrefName { | |
80 unsigned int native_modifier; | |
81 int flag; | |
82 const char* pref_name; | |
83 } kModifierFlagToPrefName[] = { | |
84 // TODO(yusukes): When the device has a Chrome keyboard (i.e. the one without | |
85 // Caps Lock), we should not check kLanguageRemapCapsLockKeyTo. | |
86 { Mod3Mask, 0, prefs::kLanguageRemapCapsLockKeyTo }, | |
87 { Mod4Mask, 0, prefs::kLanguageRemapSearchKeyTo }, | |
88 { ControlMask, ui::EF_CONTROL_DOWN, prefs::kLanguageRemapControlKeyTo }, | |
89 { Mod1Mask, ui::EF_ALT_DOWN, prefs::kLanguageRemapAltKeyTo }, | |
90 { Mod2Mask, 0, prefs::kLanguageRemapDiamondKeyTo }, | |
91 }; | |
92 | |
93 // Gets a remapped key for |pref_name| key. For example, to find out which | |
94 // key Search is currently remapped to, call the function with | |
95 // prefs::kLanguageRemapSearchKeyTo. | |
96 const ModifierRemapping* GetRemappedKey(const std::string& pref_name, | |
97 const PrefService& pref_service) { | |
98 if (!pref_service.FindPreference(pref_name.c_str())) | |
99 return NULL; // The |pref_name| hasn't been registered. On login screen? | |
100 const int value = pref_service.GetInteger(pref_name.c_str()); | |
101 for (size_t i = 0; i < arraysize(kModifierRemappings); ++i) { | |
102 if (value == kModifierRemappings[i].remap_to) | |
103 return &kModifierRemappings[i]; | |
104 } | |
105 return NULL; | |
106 } | |
107 | |
108 bool IsRight(KeySym native_keysym) { | |
109 switch (native_keysym) { | |
110 case XK_Alt_R: | |
111 case XK_Control_R: | |
112 case XK_Hyper_R: | |
113 case XK_Meta_R: | |
114 case XK_Shift_R: | |
115 case XK_Super_R: | |
116 return true; | |
117 default: | |
118 break; | |
119 } | |
120 return false; | |
121 } | |
122 | |
123 bool HasDiamondKey() { | |
124 return CommandLine::ForCurrentProcess()->HasSwitch( | |
125 chromeos::switches::kHasChromeOSDiamondKey); | |
126 } | |
127 | |
128 bool IsMod3UsedByCurrentInputMethod() { | |
129 // Since both German Neo2 XKB layout and Caps Lock depend on Mod3Mask, | |
130 // it's not possible to make both features work. For now, we don't remap | |
131 // Mod3Mask when Neo2 is in use. | |
132 // TODO(yusukes): Remove the restriction. | |
133 chromeos::input_method::InputMethodManager* manager = | |
134 chromeos::input_method::InputMethodManager::Get(); | |
135 return manager->GetCurrentInputMethod().id() == kNeo2LayoutId || | |
136 manager->GetCurrentInputMethod().id() == kCaMultixLayoutId; | |
137 } | |
138 | |
139 #endif // defined(OS_CHROMEOS) | |
140 | |
141 } // namespace | |
142 | |
143 EventRewriter::EventRewriter() | |
144 : last_device_id_(kBadDeviceId), | |
145 #if defined(OS_CHROMEOS) | |
146 xkeyboard_for_testing_(NULL), | |
147 keyboard_driven_event_rewriter_( | |
148 new chromeos::KeyboardDrivenEventRewriter), | |
149 #endif | |
150 pref_service_for_testing_(NULL) { | |
151 // The ash shell isn't instantiated for our unit tests. | |
152 if (ash::Shell::HasInstance()) { | |
153 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()-> | |
154 AddRootWindowObserver(this); | |
155 } | |
156 #if defined(OS_CHROMEOS) | |
157 if (base::SysInfo::IsRunningOnChromeOS()) { | |
158 chromeos::XInputHierarchyChangedEventListener::GetInstance() | |
159 ->AddObserver(this); | |
160 } | |
161 RefreshKeycodes(); | |
162 #endif | |
163 } | |
164 | |
165 EventRewriter::~EventRewriter() { | |
166 if (ash::Shell::HasInstance()) { | |
167 ash::Shell::GetPrimaryRootWindow()->GetDispatcher()-> | |
168 RemoveRootWindowObserver(this); | |
169 } | |
170 #if defined(OS_CHROMEOS) | |
171 if (base::SysInfo::IsRunningOnChromeOS()) { | |
172 chromeos::XInputHierarchyChangedEventListener::GetInstance() | |
173 ->RemoveObserver(this); | |
174 } | |
175 #endif | |
176 } | |
177 | |
178 EventRewriter::DeviceType EventRewriter::DeviceAddedForTesting( | |
179 int device_id, | |
180 const std::string& device_name) { | |
181 return DeviceAddedInternal(device_id, device_name); | |
182 } | |
183 | |
184 // static | |
185 EventRewriter::DeviceType EventRewriter::GetDeviceType( | |
186 const std::string& device_name) { | |
187 std::vector<std::string> tokens; | |
188 Tokenize(device_name, " .", &tokens); | |
189 | |
190 // If the |device_name| contains the two words, "apple" and "keyboard", treat | |
191 // it as an Apple keyboard. | |
192 bool found_apple = false; | |
193 bool found_keyboard = false; | |
194 for (size_t i = 0; i < tokens.size(); ++i) { | |
195 if (!found_apple && LowerCaseEqualsASCII(tokens[i], "apple")) | |
196 found_apple = true; | |
197 if (!found_keyboard && LowerCaseEqualsASCII(tokens[i], "keyboard")) | |
198 found_keyboard = true; | |
199 if (found_apple && found_keyboard) | |
200 return kDeviceAppleKeyboard; | |
201 } | |
202 | |
203 return kDeviceUnknown; | |
204 } | |
205 | |
206 void EventRewriter::RewriteForTesting(ui::KeyEvent* event) { | |
207 Rewrite(event); | |
208 } | |
209 | |
210 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterKeyEvent( | |
211 ui::KeyEvent* event) { | |
212 if (event->HasNativeEvent()) | |
213 Rewrite(event); | |
214 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; | |
215 } | |
216 | |
217 ash::EventRewriterDelegate::Action EventRewriter::RewriteOrFilterLocatedEvent( | |
218 ui::LocatedEvent* event) { | |
219 if (event->HasNativeEvent()) | |
220 RewriteLocatedEvent(event); | |
221 return ash::EventRewriterDelegate::ACTION_REWRITE_EVENT; | |
222 } | |
223 | |
224 void EventRewriter::OnKeyboardMappingChanged(const aura::RootWindow* root) { | |
225 #if defined(OS_CHROMEOS) | |
226 RefreshKeycodes(); | |
227 #endif | |
228 } | |
229 | |
230 #if defined(OS_CHROMEOS) | |
231 void EventRewriter::DeviceAdded(int device_id) { | |
232 DCHECK_NE(XIAllDevices, device_id); | |
233 DCHECK_NE(XIAllMasterDevices, device_id); | |
234 if (device_id == XIAllDevices || device_id == XIAllMasterDevices) { | |
235 LOG(ERROR) << "Unexpected device_id passed: " << device_id; | |
236 return; | |
237 } | |
238 | |
239 int ndevices_return = 0; | |
240 XIDeviceInfo* device_info = XIQueryDevice(gfx::GetXDisplay(), | |
241 device_id, | |
242 &ndevices_return); | |
243 | |
244 // Since |device_id| is neither XIAllDevices nor XIAllMasterDevices, | |
245 // the number of devices found should be either 0 (not found) or 1. | |
246 if (!device_info) { | |
247 LOG(ERROR) << "XIQueryDevice: Device ID " << device_id << " is unknown."; | |
248 return; | |
249 } | |
250 | |
251 DCHECK_EQ(1, ndevices_return); | |
252 for (int i = 0; i < ndevices_return; ++i) { | |
253 DCHECK_EQ(device_id, device_info[i].deviceid); // see the comment above. | |
254 DCHECK(device_info[i].name); | |
255 DeviceAddedInternal(device_info[i].deviceid, device_info[i].name); | |
256 } | |
257 | |
258 XIFreeDeviceInfo(device_info); | |
259 } | |
260 | |
261 void EventRewriter::DeviceRemoved(int device_id) { | |
262 device_id_to_type_.erase(device_id); | |
263 } | |
264 | |
265 void EventRewriter::DeviceKeyPressedOrReleased(int device_id) { | |
266 std::map<int, DeviceType>::const_iterator iter = | |
267 device_id_to_type_.find(device_id); | |
268 if (iter == device_id_to_type_.end()) { | |
269 // |device_id| is unknown. This means the device was connected before | |
270 // booting the OS. Query the name of the device and add it to the map. | |
271 DeviceAdded(device_id); | |
272 } | |
273 | |
274 last_device_id_ = device_id; | |
275 } | |
276 | |
277 void EventRewriter::RefreshKeycodes() { | |
278 keysym_to_keycode_map_.clear(); | |
279 } | |
280 | |
281 KeyCode EventRewriter::NativeKeySymToNativeKeycode(KeySym keysym) { | |
282 if (keysym_to_keycode_map_.count(keysym)) | |
283 return keysym_to_keycode_map_[keysym]; | |
284 | |
285 XDisplay* display = gfx::GetXDisplay(); | |
286 KeyCode keycode = XKeysymToKeycode(display, keysym); | |
287 keysym_to_keycode_map_[keysym] = keycode; | |
288 return keycode; | |
289 } | |
290 | |
291 bool EventRewriter::TopRowKeysAreFunctionKeys(ui::KeyEvent* event) const { | |
292 const PrefService* prefs = GetPrefService(); | |
293 if (prefs && | |
294 prefs->FindPreference(prefs::kLanguageSendFunctionKeys) && | |
295 prefs->GetBoolean(prefs::kLanguageSendFunctionKeys)) | |
296 return true; | |
297 | |
298 aura::Window* target = static_cast<aura::Window*>(event->target()); | |
299 if (!target) | |
300 return false; | |
301 aura::Window* top_level = views::corewm::GetToplevelWindow(target); | |
302 return top_level && | |
303 ash::wm::GetWindowState(top_level)->top_row_keys_are_function_keys(); | |
304 } | |
305 | |
306 bool EventRewriter::RewriteWithKeyboardRemappingsByKeySym( | |
307 const KeyboardRemapping* remappings, | |
308 size_t num_remappings, | |
309 KeySym keysym, | |
310 unsigned int native_mods, | |
311 unsigned int mods, | |
312 KeySym* remapped_native_keysym, | |
313 unsigned int* remapped_native_mods, | |
314 ui::KeyboardCode* remapped_keycode, | |
315 unsigned int* remapped_mods) { | |
316 for (size_t i = 0; i < num_remappings; ++i) { | |
317 const KeyboardRemapping& map = remappings[i]; | |
318 | |
319 if (keysym != map.input_keysym) | |
320 continue; | |
321 unsigned int matched_mods = native_mods & map.input_native_mods; | |
322 if (matched_mods != map.input_native_mods) | |
323 continue; | |
324 | |
325 *remapped_native_keysym = map.output_keysym; | |
326 *remapped_keycode = map.output_keycode; | |
327 *remapped_native_mods = (native_mods & ~map.input_native_mods) | | |
328 map.output_native_mods; | |
329 *remapped_mods = (mods & ~map.input_mods) | map.output_mods; | |
330 return true; | |
331 } | |
332 | |
333 return false; | |
334 } | |
335 | |
336 bool EventRewriter::RewriteWithKeyboardRemappingsByKeyCode( | |
337 const KeyboardRemapping* remappings, | |
338 size_t num_remappings, | |
339 KeyCode keycode, | |
340 unsigned int native_mods, | |
341 unsigned int mods, | |
342 KeySym* remapped_native_keysym, | |
343 unsigned int* remapped_native_mods, | |
344 ui::KeyboardCode* remapped_keycode, | |
345 unsigned int* remapped_mods) { | |
346 for (size_t i = 0; i < num_remappings; ++i) { | |
347 const KeyboardRemapping& map = remappings[i]; | |
348 | |
349 KeyCode input_keycode = NativeKeySymToNativeKeycode(map.input_keysym); | |
350 if (keycode != input_keycode) | |
351 continue; | |
352 unsigned int matched_mods = native_mods & map.input_native_mods; | |
353 if (matched_mods != map.input_native_mods) | |
354 continue; | |
355 | |
356 *remapped_native_keysym = map.output_keysym; | |
357 *remapped_keycode = map.output_keycode; | |
358 *remapped_native_mods = (native_mods & ~map.input_native_mods) | | |
359 map.output_native_mods; | |
360 *remapped_mods = (mods & ~map.input_mods) | map.output_mods; | |
361 return true; | |
362 } | |
363 | |
364 return false; | |
365 } | |
366 #endif // defined(OS_CHROMEOS) | |
367 | |
368 const PrefService* EventRewriter::GetPrefService() const { | |
369 if (pref_service_for_testing_) | |
370 return pref_service_for_testing_; | |
371 Profile* profile = ProfileManager::GetActiveUserProfile(); | |
372 return profile ? profile->GetPrefs() : NULL; | |
373 } | |
374 | |
375 void EventRewriter::Rewrite(ui::KeyEvent* event) { | |
376 #if defined(OS_CHROMEOS) | |
377 // Do not rewrite an event sent by ui_controls::SendKeyPress(). See | |
378 // crbug.com/136465. | |
379 if (event->native_event()->xkey.send_event) | |
380 return; | |
381 | |
382 // Keyboard driven rewriting happen first. Skip further processing if event is | |
383 // changed. | |
384 if (keyboard_driven_event_rewriter_->RewriteIfKeyboardDrivenOnLoginScreen( | |
385 event)) { | |
386 return; | |
387 } | |
388 #endif | |
389 RewriteModifiers(event); | |
390 RewriteNumPadKeys(event); | |
391 RewriteExtendedKeys(event); | |
392 RewriteFunctionKeys(event); | |
393 } | |
394 | |
395 bool EventRewriter::IsAppleKeyboard() const { | |
396 if (last_device_id_ == kBadDeviceId) | |
397 return false; | |
398 | |
399 // Check which device generated |event|. | |
400 std::map<int, DeviceType>::const_iterator iter = | |
401 device_id_to_type_.find(last_device_id_); | |
402 if (iter == device_id_to_type_.end()) { | |
403 LOG(ERROR) << "Device ID " << last_device_id_ << " is unknown."; | |
404 return false; | |
405 } | |
406 | |
407 const DeviceType type = iter->second; | |
408 return type == kDeviceAppleKeyboard; | |
409 } | |
410 | |
411 void EventRewriter::GetRemappedModifierMasks( | |
412 int original_flags, | |
413 unsigned int original_native_modifiers, | |
414 int* remapped_flags, | |
415 unsigned int* remapped_native_modifiers) const { | |
416 #if defined(OS_CHROMEOS) | |
417 // TODO(glotov): remove the following condition when we do not restart chrome | |
418 // when user logs in as guest. See Rewrite() for details. | |
419 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() && | |
420 chromeos::LoginDisplayHostImpl::default_host()) { | |
421 return; | |
422 } | |
423 | |
424 const PrefService* pref_service = GetPrefService(); | |
425 if (!pref_service) | |
426 return; | |
427 | |
428 // When a diamond key is not available, a Mod2Mask should not treated as a | |
429 // configurable modifier because Mod2Mask may be worked as NumLock mask. | |
430 // (cf. http://crbug.com/173956) | |
431 const bool skip_mod2 = !HasDiamondKey(); | |
432 // If Mod3 is used by the current input method, don't allow the CapsLock | |
433 // pref to remap it, or the keyboard behavior will be broken. | |
434 const bool skip_mod3 = IsMod3UsedByCurrentInputMethod(); | |
435 | |
436 for (size_t i = 0; i < arraysize(kModifierFlagToPrefName); ++i) { | |
437 if ((skip_mod2 && kModifierFlagToPrefName[i].native_modifier == Mod2Mask) || | |
438 (skip_mod3 && kModifierFlagToPrefName[i].native_modifier == Mod3Mask)) { | |
439 continue; | |
440 } | |
441 if (original_native_modifiers & | |
442 kModifierFlagToPrefName[i].native_modifier) { | |
443 const ModifierRemapping* remapped_key = | |
444 GetRemappedKey(kModifierFlagToPrefName[i].pref_name, *pref_service); | |
445 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. | |
446 if (IsAppleKeyboard() && | |
447 (kModifierFlagToPrefName[i].native_modifier == Mod4Mask)) { | |
448 remapped_key = kModifierRemappingCtrl; | |
449 } | |
450 if (remapped_key) { | |
451 *remapped_flags |= remapped_key->flag; | |
452 *remapped_native_modifiers |= remapped_key->native_modifier; | |
453 } else { | |
454 *remapped_flags |= kModifierFlagToPrefName[i].flag; | |
455 *remapped_native_modifiers |= | |
456 kModifierFlagToPrefName[i].native_modifier; | |
457 } | |
458 } | |
459 } | |
460 | |
461 *remapped_flags = | |
462 (original_flags & ~(ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)) | | |
463 *remapped_flags; | |
464 | |
465 unsigned int native_mask = Mod4Mask | ControlMask | Mod1Mask; | |
466 if (!skip_mod2) | |
467 native_mask |= Mod2Mask; | |
468 if (!skip_mod3) | |
469 native_mask |= Mod3Mask; | |
470 *remapped_native_modifiers = | |
471 (original_native_modifiers & ~native_mask) | | |
472 *remapped_native_modifiers; | |
473 #endif | |
474 } | |
475 | |
476 bool EventRewriter::RewriteModifiers(ui::KeyEvent* event) { | |
477 #if defined(OS_CHROMEOS) | |
478 // Do nothing if we have just logged in as guest but have not restarted chrome | |
479 // process yet (so we are still on the login screen). In this situations we | |
480 // have no user profile so can not do anything useful. | |
481 // Note that currently, unlike other accounts, when user logs in as guest, we | |
482 // restart chrome process. In future this is to be changed. | |
483 // TODO(glotov): remove the following condition when we do not restart chrome | |
484 // when user logs in as guest. | |
485 if (chromeos::UserManager::Get()->IsLoggedInAsGuest() && | |
486 chromeos::LoginDisplayHostImpl::default_host()) | |
487 return false; | |
488 | |
489 const PrefService* pref_service = GetPrefService(); | |
490 if (!pref_service) | |
491 return false; | |
492 | |
493 DCHECK_EQ(chromeos::input_method::kControlKey, | |
494 kModifierRemappingCtrl->remap_to); | |
495 | |
496 XEvent* xev = event->native_event(); | |
497 XKeyEvent* xkey = &(xev->xkey); | |
498 KeySym keysym = XLookupKeysym(xkey, 0); | |
499 | |
500 ui::KeyboardCode remapped_keycode = event->key_code(); | |
501 KeyCode remapped_native_keycode = xkey->keycode; | |
502 | |
503 // First, remap |keysym|. | |
504 const ModifierRemapping* remapped_key = NULL; | |
505 switch (keysym) { | |
506 // On Chrome OS, XF86XK_Launch6 (F15) with Mod2Mask is sent when Diamond | |
507 // key is pressed. | |
508 case XF86XK_Launch6: | |
509 // When diamond key is not available, the configuration UI for Diamond | |
510 // key is not shown. Therefore, ignore the kLanguageRemapDiamondKeyTo | |
511 // syncable pref. | |
512 if (HasDiamondKey()) | |
513 remapped_key = | |
514 GetRemappedKey(prefs::kLanguageRemapDiamondKeyTo, *pref_service); | |
515 // Default behavior is Ctrl key. | |
516 if (!remapped_key) | |
517 remapped_key = kModifierRemappingCtrl; | |
518 break; | |
519 // On Chrome OS, XF86XK_Launch7 (F16) with Mod3Mask is sent when Caps Lock | |
520 // is pressed (with one exception: when IsMod3UsedByCurrentInputMethod() is | |
521 // true, the key generates XK_ISO_Level3_Shift with Mod3Mask, not | |
522 // XF86XK_Launch7). | |
523 case XF86XK_Launch7: | |
524 remapped_key = | |
525 GetRemappedKey(prefs::kLanguageRemapCapsLockKeyTo, *pref_service); | |
526 break; | |
527 case XK_Super_L: | |
528 case XK_Super_R: | |
529 // Rewrite Command-L/R key presses on an Apple keyboard to Control-L/R. | |
530 if (IsAppleKeyboard()) | |
531 remapped_key = kModifierRemappingCtrl; | |
532 else | |
533 remapped_key = | |
534 GetRemappedKey(prefs::kLanguageRemapSearchKeyTo, *pref_service); | |
535 // Default behavior is Super key, hence don't remap the event if the pref | |
536 // is unavailable. | |
537 break; | |
538 case XK_Control_L: | |
539 case XK_Control_R: | |
540 remapped_key = | |
541 GetRemappedKey(prefs::kLanguageRemapControlKeyTo, *pref_service); | |
542 break; | |
543 case XK_Alt_L: | |
544 case XK_Alt_R: | |
545 case XK_Meta_L: | |
546 case XK_Meta_R: | |
547 remapped_key = | |
548 GetRemappedKey(prefs::kLanguageRemapAltKeyTo, *pref_service); | |
549 break; | |
550 default: | |
551 break; | |
552 } | |
553 | |
554 if (remapped_key) { | |
555 remapped_keycode = remapped_key->keycode; | |
556 const size_t level = (event->IsShiftDown() ? (1 << 1) : 0) + | |
557 (IsRight(keysym) ? (1 << 0) : 0); | |
558 const KeySym native_keysym = remapped_key->native_keysyms[level]; | |
559 remapped_native_keycode = NativeKeySymToNativeKeycode(native_keysym); | |
560 } | |
561 | |
562 // Next, remap modifier bits. | |
563 int remapped_flags = 0; | |
564 unsigned int remapped_native_modifiers = 0U; | |
565 GetRemappedModifierMasks(event->flags(), xkey->state, | |
566 &remapped_flags, &remapped_native_modifiers); | |
567 | |
568 // Toggle Caps Lock if the remapped key is ui::VKEY_CAPITAL, but do nothing if | |
569 // the original key is ui::VKEY_CAPITAL (i.e. a Caps Lock key on an external | |
570 // keyboard is pressed) since X can handle that case. | |
571 if ((event->type() == ui::ET_KEY_PRESSED) && | |
572 (event->key_code() != ui::VKEY_CAPITAL) && | |
573 (remapped_keycode == ui::VKEY_CAPITAL)) { | |
574 chromeos::input_method::XKeyboard* xkeyboard = xkeyboard_for_testing_ ? | |
575 xkeyboard_for_testing_ : | |
576 chromeos::input_method::InputMethodManager::Get()->GetXKeyboard(); | |
577 xkeyboard->SetCapsLockEnabled(!xkeyboard->CapsLockIsEnabled()); | |
578 } | |
579 | |
580 OverwriteEvent(event, | |
581 remapped_native_keycode, remapped_native_modifiers, | |
582 remapped_keycode, remapped_flags); | |
583 return true; | |
584 #else | |
585 // TODO(yusukes): Support Ash on other platforms if needed. | |
586 return false; | |
587 #endif | |
588 } | |
589 | |
590 bool EventRewriter::RewriteNumPadKeys(ui::KeyEvent* event) { | |
591 bool rewritten = false; | |
592 #if defined(OS_CHROMEOS) | |
593 XEvent* xev = event->native_event(); | |
594 XKeyEvent* xkey = &(xev->xkey); | |
595 | |
596 const KeySym keysym = XLookupKeysym(xkey, 0); | |
597 switch (keysym) { | |
598 case XK_KP_Insert: | |
599 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_0), | |
600 xkey->state | Mod2Mask, | |
601 ui::VKEY_NUMPAD0, event->flags()); | |
602 rewritten = true; | |
603 break; | |
604 case XK_KP_Delete: | |
605 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_Decimal), | |
606 xkey->state | Mod2Mask, | |
607 ui::VKEY_DECIMAL, event->flags()); | |
608 rewritten = true; | |
609 break; | |
610 case XK_KP_End: | |
611 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_1), | |
612 xkey->state | Mod2Mask, | |
613 ui::VKEY_NUMPAD1, event->flags()); | |
614 rewritten = true; | |
615 break; | |
616 case XK_KP_Down: | |
617 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_2), | |
618 xkey->state | Mod2Mask, | |
619 ui::VKEY_NUMPAD2, event->flags()); | |
620 rewritten = true; | |
621 break; | |
622 case XK_KP_Next: | |
623 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_3), | |
624 xkey->state | Mod2Mask, | |
625 ui::VKEY_NUMPAD3, event->flags()); | |
626 rewritten = true; | |
627 break; | |
628 case XK_KP_Left: | |
629 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_4), | |
630 xkey->state | Mod2Mask, | |
631 ui::VKEY_NUMPAD4, event->flags()); | |
632 rewritten = true; | |
633 break; | |
634 case XK_KP_Begin: | |
635 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_5), | |
636 xkey->state | Mod2Mask, | |
637 ui::VKEY_NUMPAD5, event->flags()); | |
638 rewritten = true; | |
639 break; | |
640 case XK_KP_Right: | |
641 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_6), | |
642 xkey->state | Mod2Mask, | |
643 ui::VKEY_NUMPAD6, event->flags()); | |
644 rewritten = true; | |
645 break; | |
646 case XK_KP_Home: | |
647 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_7), | |
648 xkey->state | Mod2Mask, | |
649 ui::VKEY_NUMPAD7, event->flags()); | |
650 rewritten = true; | |
651 break; | |
652 case XK_KP_Up: | |
653 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_8), | |
654 xkey->state | Mod2Mask, | |
655 ui::VKEY_NUMPAD8, event->flags()); | |
656 rewritten = true; | |
657 break; | |
658 case XK_KP_Prior: | |
659 OverwriteEvent(event, NativeKeySymToNativeKeycode(XK_KP_9), | |
660 xkey->state | Mod2Mask, | |
661 ui::VKEY_NUMPAD9, event->flags()); | |
662 rewritten = true; | |
663 break; | |
664 case XK_KP_Divide: | |
665 case XK_KP_Multiply: | |
666 case XK_KP_Subtract: | |
667 case XK_KP_Add: | |
668 case XK_KP_Enter: | |
669 // Add Mod2Mask for consistency. | |
670 OverwriteEvent(event, xkey->keycode, xkey->state | Mod2Mask, | |
671 event->key_code(), event->flags()); | |
672 rewritten = true; | |
673 break; | |
674 default: | |
675 break; | |
676 } | |
677 #else | |
678 // TODO(yusukes): Support Ash on other platforms if needed. | |
679 #endif | |
680 return rewritten; | |
681 } | |
682 | |
683 bool EventRewriter::RewriteExtendedKeys(ui::KeyEvent* event) { | |
684 #if defined(OS_CHROMEOS) | |
685 XEvent* xev = event->native_event(); | |
686 XKeyEvent* xkey = &(xev->xkey); | |
687 const KeySym keysym = XLookupKeysym(xkey, 0); | |
688 | |
689 KeySym remapped_native_keysym = 0; | |
690 unsigned int remapped_native_mods = 0; | |
691 ui::KeyboardCode remapped_keycode = ui::VKEY_UNKNOWN; | |
692 unsigned int remapped_mods = 0; | |
693 | |
694 if (xkey->state & Mod4Mask) { | |
695 // Allow Search to avoid rewriting extended keys. | |
696 static const KeyboardRemapping kAvoidRemappings[] = { | |
697 { // Alt+Backspace | |
698 XK_BackSpace, | |
699 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask, | |
700 XK_BackSpace, ui::VKEY_BACK, | |
701 ui::EF_ALT_DOWN, Mod1Mask, | |
702 }, | |
703 { // Control+Alt+Up | |
704 XK_Up, | |
705 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
706 Mod1Mask | ControlMask | Mod4Mask, | |
707 XK_Up, ui::VKEY_UP, | |
708 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, | |
709 }, | |
710 { // Alt+Up | |
711 XK_Up, | |
712 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask, | |
713 XK_Up, ui::VKEY_UP, | |
714 ui::EF_ALT_DOWN, Mod1Mask, | |
715 }, | |
716 { // Control+Alt+Down | |
717 XK_Down, | |
718 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, | |
719 Mod1Mask | ControlMask | Mod4Mask, | |
720 XK_Down, ui::VKEY_DOWN, | |
721 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, | |
722 }, | |
723 { // Alt+Down | |
724 XK_Down, | |
725 ui::EF_ALT_DOWN, Mod1Mask | Mod4Mask, | |
726 XK_Down, ui::VKEY_DOWN, | |
727 ui::EF_ALT_DOWN, Mod1Mask, | |
728 } | |
729 }; | |
730 | |
731 RewriteWithKeyboardRemappingsByKeySym(kAvoidRemappings, | |
732 arraysize(kAvoidRemappings), | |
733 keysym, | |
734 xkey->state, | |
735 event->flags(), | |
736 &remapped_native_keysym, | |
737 &remapped_native_mods, | |
738 &remapped_keycode, | |
739 &remapped_mods); | |
740 } | |
741 | |
742 if (remapped_keycode == ui::VKEY_UNKNOWN) { | |
743 static const KeyboardRemapping kSearchRemappings[] = { | |
744 { // Search+BackSpace -> Delete | |
745 XK_BackSpace, | |
746 0, Mod4Mask, | |
747 XK_Delete, ui::VKEY_DELETE, | |
748 0, 0 | |
749 }, | |
750 { // Search+Left -> Home | |
751 XK_Left, | |
752 0, Mod4Mask, | |
753 XK_Home, ui::VKEY_HOME, | |
754 0, 0 | |
755 }, | |
756 { // Search+Up -> Prior (aka PageUp) | |
757 XK_Up, | |
758 0, Mod4Mask, | |
759 XK_Prior, ui::VKEY_PRIOR, | |
760 0, 0 | |
761 }, | |
762 { // Search+Right -> End | |
763 XK_Right, | |
764 0, Mod4Mask, | |
765 XK_End, ui::VKEY_END, | |
766 0, 0 | |
767 }, | |
768 { // Search+Down -> Next (aka PageDown) | |
769 XK_Down, | |
770 0, Mod4Mask, | |
771 XK_Next, ui::VKEY_NEXT, | |
772 0, 0 | |
773 }, | |
774 { // Search+Period -> Insert | |
775 XK_period, | |
776 0, Mod4Mask, | |
777 XK_Insert, ui::VKEY_INSERT, | |
778 0, 0 | |
779 } | |
780 }; | |
781 | |
782 RewriteWithKeyboardRemappingsByKeySym(kSearchRemappings, | |
783 arraysize(kSearchRemappings), | |
784 keysym, | |
785 xkey->state, | |
786 event->flags(), | |
787 &remapped_native_keysym, | |
788 &remapped_native_mods, | |
789 &remapped_keycode, | |
790 &remapped_mods); | |
791 } | |
792 | |
793 if (remapped_keycode == ui::VKEY_UNKNOWN) { | |
794 static const KeyboardRemapping kNonSearchRemappings[] = { | |
795 { // Alt+BackSpace -> Delete | |
796 XK_BackSpace, | |
797 ui::EF_ALT_DOWN, Mod1Mask, | |
798 XK_Delete, ui::VKEY_DELETE, | |
799 0, 0 | |
800 }, | |
801 { // Control+Alt+Up -> Home | |
802 XK_Up, | |
803 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, | |
804 XK_Home, ui::VKEY_HOME, | |
805 0, 0 | |
806 }, | |
807 { // Alt+Up -> Prior (aka PageUp) | |
808 XK_Up, | |
809 ui::EF_ALT_DOWN, Mod1Mask, | |
810 XK_Prior, ui::VKEY_PRIOR, | |
811 0, 0 | |
812 }, | |
813 { // Control+Alt+Down -> End | |
814 XK_Down, | |
815 ui::EF_ALT_DOWN | ui::EF_CONTROL_DOWN, Mod1Mask | ControlMask, | |
816 XK_End, ui::VKEY_END, | |
817 0, 0 | |
818 }, | |
819 { // Alt+Down -> Next (aka PageDown) | |
820 XK_Down, | |
821 ui::EF_ALT_DOWN, Mod1Mask, | |
822 XK_Next, ui::VKEY_NEXT, | |
823 0, 0 | |
824 } | |
825 }; | |
826 | |
827 RewriteWithKeyboardRemappingsByKeySym(kNonSearchRemappings, | |
828 arraysize(kNonSearchRemappings), | |
829 keysym, | |
830 xkey->state, | |
831 event->flags(), | |
832 &remapped_native_keysym, | |
833 &remapped_native_mods, | |
834 &remapped_keycode, | |
835 &remapped_mods); | |
836 } | |
837 | |
838 if (remapped_keycode == ui::VKEY_UNKNOWN) | |
839 return false; | |
840 | |
841 OverwriteEvent(event, | |
842 NativeKeySymToNativeKeycode(remapped_native_keysym), | |
843 remapped_native_mods, | |
844 remapped_keycode, | |
845 remapped_mods); | |
846 return true; | |
847 #else | |
848 // TODO(yusukes): Support Ash on other platforms if needed. | |
849 return false; | |
850 #endif | |
851 } | |
852 | |
853 bool EventRewriter::RewriteFunctionKeys(ui::KeyEvent* event) { | |
854 #if defined(OS_CHROMEOS) | |
855 XEvent* xev = event->native_event(); | |
856 XKeyEvent* xkey = &(xev->xkey); | |
857 const KeySym keysym = XLookupKeysym(xkey, 0); | |
858 | |
859 KeySym remapped_native_keysym = 0; | |
860 unsigned int remapped_native_mods = 0; | |
861 ui::KeyboardCode remapped_keycode = ui::VKEY_UNKNOWN; | |
862 unsigned int remapped_mods = 0; | |
863 | |
864 // By default the top row (F1-F12) keys are special keys for back, forward, | |
865 // brightness, volume, etc. However, windows for v2 apps can optionally | |
866 // request raw function keys for these keys. | |
867 bool top_row_keys_are_special_keys = !TopRowKeysAreFunctionKeys(event); | |
868 | |
869 if ((xkey->state & Mod4Mask) && top_row_keys_are_special_keys) { | |
870 // Allow Search to avoid rewriting F1-F12. | |
871 static const KeyboardRemapping kFkeysToFkeys[] = { | |
872 { XK_F1, 0, Mod4Mask, XK_F1, ui::VKEY_F1, }, | |
873 { XK_F2, 0, Mod4Mask, XK_F2, ui::VKEY_F2, }, | |
874 { XK_F3, 0, Mod4Mask, XK_F3, ui::VKEY_F3, }, | |
875 { XK_F4, 0, Mod4Mask, XK_F4, ui::VKEY_F4, }, | |
876 { XK_F5, 0, Mod4Mask, XK_F5, ui::VKEY_F5, }, | |
877 { XK_F6, 0, Mod4Mask, XK_F6, ui::VKEY_F6, }, | |
878 { XK_F7, 0, Mod4Mask, XK_F7, ui::VKEY_F7, }, | |
879 { XK_F8, 0, Mod4Mask, XK_F8, ui::VKEY_F8, }, | |
880 { XK_F9, 0, Mod4Mask, XK_F9, ui::VKEY_F9, }, | |
881 { XK_F10, 0, Mod4Mask, XK_F10, ui::VKEY_F10, }, | |
882 { XK_F11, 0, Mod4Mask, XK_F11, ui::VKEY_F11, }, | |
883 { XK_F12, 0, Mod4Mask, XK_F12, ui::VKEY_F12, }, | |
884 }; | |
885 | |
886 RewriteWithKeyboardRemappingsByKeySym(kFkeysToFkeys, | |
887 arraysize(kFkeysToFkeys), | |
888 keysym, | |
889 xkey->state, | |
890 event->flags(), | |
891 &remapped_native_keysym, | |
892 &remapped_native_mods, | |
893 &remapped_keycode, | |
894 &remapped_mods); | |
895 } | |
896 | |
897 if (remapped_keycode == ui::VKEY_UNKNOWN) { | |
898 static const KeyboardRemapping kFkeysToSpecialKeys[] = { | |
899 { XK_F1, 0, 0, XF86XK_Back, ui::VKEY_BROWSER_BACK, 0, 0 }, | |
900 { XK_F2, 0, 0, XF86XK_Forward, ui::VKEY_BROWSER_FORWARD, 0, 0 }, | |
901 { XK_F3, 0, 0, XF86XK_Reload, ui::VKEY_BROWSER_REFRESH, 0, 0 }, | |
902 { XK_F4, 0, 0, XF86XK_LaunchB, ui::VKEY_MEDIA_LAUNCH_APP2, 0, 0 }, | |
903 { XK_F5, 0, 0, XF86XK_LaunchA, ui::VKEY_MEDIA_LAUNCH_APP1, 0, 0 }, | |
904 { XK_F6, 0, 0, XF86XK_MonBrightnessDown, ui::VKEY_BRIGHTNESS_DOWN, 0, 0 }, | |
905 { XK_F7, 0, 0, XF86XK_MonBrightnessUp, ui::VKEY_BRIGHTNESS_UP, 0, 0 }, | |
906 { XK_F8, 0, 0, XF86XK_AudioMute, ui::VKEY_VOLUME_MUTE, 0, 0 }, | |
907 { XK_F9, 0, 0, XF86XK_AudioLowerVolume, ui::VKEY_VOLUME_DOWN, 0, 0 }, | |
908 { XK_F10, 0, 0, XF86XK_AudioRaiseVolume, ui::VKEY_VOLUME_UP, 0, 0 }, | |
909 }; | |
910 | |
911 if (top_row_keys_are_special_keys) { | |
912 // Rewrite the F1-F12 keys on a Chromebook keyboard to special keys. | |
913 RewriteWithKeyboardRemappingsByKeySym(kFkeysToSpecialKeys, | |
914 arraysize(kFkeysToSpecialKeys), | |
915 keysym, | |
916 xkey->state, | |
917 event->flags(), | |
918 &remapped_native_keysym, | |
919 &remapped_native_mods, | |
920 &remapped_keycode, | |
921 &remapped_mods); | |
922 } else if (xkey->state & Mod4Mask) { | |
923 // Use Search + F1-F12 for the special keys. | |
924 RewriteWithKeyboardRemappingsByKeySym(kFkeysToSpecialKeys, | |
925 arraysize(kFkeysToSpecialKeys), | |
926 keysym, | |
927 xkey->state & !Mod4Mask, | |
928 event->flags(), | |
929 &remapped_native_keysym, | |
930 &remapped_native_mods, | |
931 &remapped_keycode, | |
932 &remapped_mods); | |
933 } | |
934 } | |
935 | |
936 if (remapped_keycode == ui::VKEY_UNKNOWN && xkey->state & Mod4Mask) { | |
937 // Remap Search+<number> to F<number>. | |
938 // We check the keycode here instead of the keysym, as these keys have | |
939 // different keysyms when modifiers are pressed, such as shift. | |
940 | |
941 // TODO(danakj): On some i18n keyboards, these choices will be bad and we | |
942 // should make layout-specific choices here. For eg. on a french keyboard | |
943 // "-" and "6" are the same key, so F11 will not be accessible. | |
944 static const KeyboardRemapping kNumberKeysToFkeys[] = { | |
945 { XK_1, 0, Mod4Mask, XK_F1, ui::VKEY_F1, 0, 0 }, | |
946 { XK_2, 0, Mod4Mask, XK_F2, ui::VKEY_F2, 0, 0 }, | |
947 { XK_3, 0, Mod4Mask, XK_F3, ui::VKEY_F3, 0, 0 }, | |
948 { XK_4, 0, Mod4Mask, XK_F4, ui::VKEY_F4, 0, 0 }, | |
949 { XK_5, 0, Mod4Mask, XK_F5, ui::VKEY_F5, 0, 0 }, | |
950 { XK_6, 0, Mod4Mask, XK_F6, ui::VKEY_F6, 0, 0 }, | |
951 { XK_7, 0, Mod4Mask, XK_F7, ui::VKEY_F7, 0, 0 }, | |
952 { XK_8, 0, Mod4Mask, XK_F8, ui::VKEY_F8, 0, 0 }, | |
953 { XK_9, 0, Mod4Mask, XK_F9, ui::VKEY_F9, 0, 0 }, | |
954 { XK_0, 0, Mod4Mask, XK_F10, ui::VKEY_F10, 0, 0 }, | |
955 { XK_minus, 0, Mod4Mask, XK_F11, ui::VKEY_F11, 0, 0 }, | |
956 { XK_equal, 0, Mod4Mask, XK_F12, ui::VKEY_F12, 0, 0 } | |
957 }; | |
958 | |
959 RewriteWithKeyboardRemappingsByKeyCode(kNumberKeysToFkeys, | |
960 arraysize(kNumberKeysToFkeys), | |
961 xkey->keycode, | |
962 xkey->state, | |
963 event->flags(), | |
964 &remapped_native_keysym, | |
965 &remapped_native_mods, | |
966 &remapped_keycode, | |
967 &remapped_mods); | |
968 } | |
969 | |
970 if (remapped_keycode == ui::VKEY_UNKNOWN) | |
971 return false; | |
972 | |
973 OverwriteEvent(event, | |
974 NativeKeySymToNativeKeycode(remapped_native_keysym), | |
975 remapped_native_mods, | |
976 remapped_keycode, | |
977 remapped_mods); | |
978 return true; | |
979 #else | |
980 // TODO(danakj): Support Ash on other platforms if needed. | |
981 return false; | |
982 #endif | |
983 } | |
984 | |
985 void EventRewriter::RewriteLocatedEvent(ui::LocatedEvent* event) { | |
986 #if defined(OS_CHROMEOS) | |
987 if (event->flags() & ui::EF_IS_SYNTHESIZED) | |
988 return; | |
989 | |
990 XEvent* xevent = event->native_event(); | |
991 if (!xevent || xevent->type != GenericEvent) | |
992 return; | |
993 | |
994 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); | |
995 if (xievent->evtype != XI_ButtonPress && xievent->evtype != XI_ButtonRelease) | |
996 return; | |
997 | |
998 // First, remap modifier masks. | |
999 int remapped_flags = 0; | |
1000 unsigned int remapped_native_modifiers = 0U; | |
1001 GetRemappedModifierMasks(event->flags(), xievent->mods.effective, | |
1002 &remapped_flags, &remapped_native_modifiers); | |
1003 xievent->mods.effective = remapped_native_modifiers; | |
1004 | |
1005 // Then, remap Alt+Button1 to Button3. | |
1006 if ((xievent->evtype == XI_ButtonPress || | |
1007 pressed_device_ids_.count(xievent->sourceid)) && | |
1008 (xievent->mods.effective & Mod1Mask) && xievent->detail == Button1) { | |
1009 xievent->mods.effective &= ~Mod1Mask; | |
1010 xievent->detail = Button3; | |
1011 if (xievent->evtype == XI_ButtonRelease) { | |
1012 // On the release clear the left button from the existing state and the | |
1013 // mods, and set the right button. | |
1014 XISetMask(xievent->buttons.mask, Button3); | |
1015 XIClearMask(xievent->buttons.mask, Button1); | |
1016 xievent->mods.effective &= ~Button1Mask; | |
1017 pressed_device_ids_.erase(xievent->sourceid); | |
1018 } else { | |
1019 pressed_device_ids_.insert(xievent->sourceid); | |
1020 } | |
1021 } | |
1022 | |
1023 const int mouse_event_flags = event->flags() & | |
1024 (ui::EF_IS_DOUBLE_CLICK | ui::EF_IS_TRIPLE_CLICK | ui::EF_IS_NON_CLIENT | | |
1025 ui::EF_IS_SYNTHESIZED | ui::EF_FROM_TOUCH); | |
1026 event->set_flags(mouse_event_flags | ui::EventFlagsFromNative(xevent)); | |
1027 #else | |
1028 // TODO(yusukes): Support Ash on other platforms if needed. | |
1029 #endif | |
1030 } | |
1031 | |
1032 void EventRewriter::OverwriteEvent(ui::KeyEvent* event, | |
1033 unsigned int new_native_keycode, | |
1034 unsigned int new_native_state, | |
1035 ui::KeyboardCode new_keycode, | |
1036 int new_flags) { | |
1037 #if defined(OS_CHROMEOS) | |
1038 XEvent* xev = event->native_event(); | |
1039 XKeyEvent* xkey = &(xev->xkey); | |
1040 xkey->keycode = new_native_keycode; | |
1041 xkey->state = new_native_state; | |
1042 event->set_key_code(new_keycode); | |
1043 event->set_character(ui::GetCharacterFromKeyCode(event->key_code(), | |
1044 new_flags)); | |
1045 event->set_flags(new_flags); | |
1046 event->NormalizeFlags(); | |
1047 #else | |
1048 // TODO(yusukes): Support Ash on other platforms if needed. | |
1049 #endif | |
1050 } | |
1051 | |
1052 EventRewriter::DeviceType EventRewriter::DeviceAddedInternal( | |
1053 int device_id, | |
1054 const std::string& device_name) { | |
1055 const DeviceType type = EventRewriter::GetDeviceType(device_name); | |
1056 if (type == kDeviceAppleKeyboard) { | |
1057 VLOG(1) << "Apple keyboard '" << device_name << "' connected: " | |
1058 << "id=" << device_id; | |
1059 } | |
1060 // Always overwrite the existing device_id since the X server may reuse a | |
1061 // device id for an unattached device. | |
1062 device_id_to_type_[device_id] = type; | |
1063 return type; | |
1064 } | |
OLD | NEW |