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