Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / global_shortcut_listener_x11.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/global_shortcut_listener_x11.h"
6
7 #include "content/public/browser/browser_thread.h"
8 #include "ui/base/accelerators/accelerator.h"
9 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
10 #include "ui/events/platform/x11/x11_event_source.h"
11 #include "ui/gfx/x/x11_error_tracker.h"
12 #include "ui/gfx/x/x11_types.h"
13
14 using content::BrowserThread;
15
16 namespace {
17
18 // The modifiers masks used for grabing keys. Due to XGrabKey only working on
19 // exact modifiers, we need to grab all key combination including zero or more
20 // of the following: Num lock, Caps lock and Scroll lock. So that we can make
21 // sure the behavior of global shortcuts is consistent on all platforms.
22 const unsigned int kModifiersMasks[] = {
23   0,                                // No additional modifier.
24   Mod2Mask,                         // Num lock
25   LockMask,                         // Caps lock
26   Mod5Mask,                         // Scroll lock
27   Mod2Mask | LockMask,
28   Mod2Mask | Mod5Mask,
29   LockMask | Mod5Mask,
30   Mod2Mask | LockMask | Mod5Mask
31 };
32
33 int GetNativeModifiers(const ui::Accelerator& accelerator) {
34   int modifiers = 0;
35   modifiers |= accelerator.IsShiftDown() ? ShiftMask : 0;
36   modifiers |= accelerator.IsCtrlDown() ? ControlMask : 0;
37   modifiers |= accelerator.IsAltDown() ? Mod1Mask : 0;
38
39   return modifiers;
40 }
41
42 }  // namespace
43
44 namespace extensions {
45
46 // static
47 GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
48   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
49   static GlobalShortcutListenerX11* instance =
50       new GlobalShortcutListenerX11();
51   return instance;
52 }
53
54 GlobalShortcutListenerX11::GlobalShortcutListenerX11()
55     : is_listening_(false),
56       x_display_(gfx::GetXDisplay()),
57       x_root_window_(DefaultRootWindow(x_display_)) {
58   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59 }
60
61 GlobalShortcutListenerX11::~GlobalShortcutListenerX11() {
62   if (is_listening_)
63     StopListening();
64 }
65
66 void GlobalShortcutListenerX11::StartListening() {
67   DCHECK(!is_listening_);  // Don't start twice.
68   DCHECK(!registered_hot_keys_.empty());  // Also don't start if no hotkey is
69                                           // registered.
70
71   ui::X11EventSource::GetInstance()->AddPlatformEventDispatcher(this);
72
73   is_listening_ = true;
74 }
75
76 void GlobalShortcutListenerX11::StopListening() {
77   DCHECK(is_listening_);  // No point if we are not already listening.
78   DCHECK(registered_hot_keys_.empty());  // Make sure the set is clean before
79                                          // ending.
80
81   ui::X11EventSource::GetInstance()->RemovePlatformEventDispatcher(this);
82
83   is_listening_ = false;
84 }
85
86 bool GlobalShortcutListenerX11::CanDispatchEvent(
87     const ui::PlatformEvent& event) {
88   return event->type == KeyPress;
89 }
90
91 uint32_t GlobalShortcutListenerX11::DispatchEvent(
92     const ui::PlatformEvent& event) {
93   CHECK_EQ(KeyPress, event->type);
94   OnXKeyPressEvent(event);
95
96   return ui::POST_DISPATCH_NONE;
97 }
98
99 bool GlobalShortcutListenerX11::RegisterAcceleratorImpl(
100     const ui::Accelerator& accelerator) {
101   DCHECK(registered_hot_keys_.find(accelerator) == registered_hot_keys_.end());
102
103   int modifiers = GetNativeModifiers(accelerator);
104   KeyCode keycode = XKeysymToKeycode(x_display_,
105       XKeysymForWindowsKeyCode(accelerator.key_code(), false));
106   gfx::X11ErrorTracker err_tracker;
107
108   // Because XGrabKey only works on the exact modifiers mask, we should register
109   // our hot keys with modifiers that we want to ignore, including Num lock,
110   // Caps lock, Scroll lock. See comment about |kModifiersMasks|.
111   for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
112     XGrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
113              x_root_window_, False, GrabModeAsync, GrabModeAsync);
114   }
115
116   if (err_tracker.FoundNewError()) {
117     // We may have part of the hotkeys registered, clean up.
118     for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
119       XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
120                  x_root_window_);
121     }
122
123     return false;
124   }
125
126   registered_hot_keys_.insert(accelerator);
127   return true;
128 }
129
130 void GlobalShortcutListenerX11::UnregisterAcceleratorImpl(
131     const ui::Accelerator& accelerator) {
132   DCHECK(registered_hot_keys_.find(accelerator) != registered_hot_keys_.end());
133
134   int modifiers = GetNativeModifiers(accelerator);
135   KeyCode keycode = XKeysymToKeycode(x_display_,
136       XKeysymForWindowsKeyCode(accelerator.key_code(), false));
137
138   for (size_t i = 0; i < arraysize(kModifiersMasks); ++i) {
139     XUngrabKey(x_display_, keycode, modifiers | kModifiersMasks[i],
140                x_root_window_);
141   }
142   registered_hot_keys_.erase(accelerator);
143 }
144
145 void GlobalShortcutListenerX11::OnXKeyPressEvent(::XEvent* x_event) {
146   DCHECK(x_event->type == KeyPress);
147   int modifiers = 0;
148   modifiers |= (x_event->xkey.state & ShiftMask) ? ui::EF_SHIFT_DOWN : 0;
149   modifiers |= (x_event->xkey.state & ControlMask) ? ui::EF_CONTROL_DOWN : 0;
150   modifiers |= (x_event->xkey.state & Mod1Mask) ? ui::EF_ALT_DOWN : 0;
151
152   ui::Accelerator accelerator(
153       ui::KeyboardCodeFromXKeyEvent(x_event), modifiers);
154   if (registered_hot_keys_.find(accelerator) != registered_hot_keys_.end())
155     NotifyKeyPressed(accelerator);
156 }
157
158 }  // namespace extensions