1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/input_method/hotkey_manager.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include <X11/X.h> // ShiftMask, ControlMask, etc.
#include <X11/Xlib.h> // KeySym, XLookupString.
#include <X11/Xutil.h> // XK_* macros.
namespace {
uint32 KeySymToModifier(KeySym keysym) {
switch (keysym) {
case XK_Control_L:
return ControlMask;
case XK_Control_R:
return ControlMask;
case XK_Shift_L:
return ShiftMask;
case XK_Shift_R:
return ShiftMask;
case XK_Alt_L:
return Mod1Mask;
case XK_Alt_R:
return Mod1Mask;
case XK_Meta_L:
return Mod1Mask;
case XK_Meta_R:
return Mod1Mask;
default:
break;
}
// Note for Mod[2-5]Mask: Usually, Mod2Mask corresponds to the Num Lock key,
// and Mod4Mask does the Windows key. However, to be exact, the assignment is
// dynamic, and could be changed in the future, I believe. FYI, xkeyboard.cc
// has code which dynamically obtains a mask for Num Lock.
return 0;
}
} // namespace
namespace chromeos {
namespace input_method {
const int HotkeyManager::kNoEvent = -1;
const int HotkeyManager::kFiltered = -2;
const uint32 HotkeyManager::kKeyReleaseMask = 1U << 31;
// Make sure kKeyReleaseMask is not the same value as XXXMask in X11/X.h.
COMPILE_ASSERT(ControlMask != (1U << 31), CheckMaskValue);
COMPILE_ASSERT(ShiftMask != (1U << 31), CheckMaskValue);
COMPILE_ASSERT(LockMask != (1U << 31), CheckMaskValue);
COMPILE_ASSERT(Mod1Mask != (1U << 31), CheckMaskValue);
COMPILE_ASSERT(Mod2Mask != (1U << 31), CheckMaskValue);
COMPILE_ASSERT(Mod3Mask != (1U << 31), CheckMaskValue);
COMPILE_ASSERT(Mod4Mask != (1U << 31), CheckMaskValue);
COMPILE_ASSERT(Mod5Mask != (1U << 31), CheckMaskValue);
HotkeyManager::HotkeyManager()
: previous_keysym_(NoSymbol),
previous_modifiers_(0x0U),
filter_release_events_(false) {
}
HotkeyManager::~HotkeyManager() {
}
void HotkeyManager::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void HotkeyManager::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
bool HotkeyManager::AddHotkey(int event_id,
uint32 keysym,
uint32 modifiers,
bool trigger_on_key_press) {
if (event_id < 0) {
LOG(ERROR) << "Bad hotkey event id: " << event_id;
return false;
}
modifiers = NormalizeModifiers(keysym, modifiers, trigger_on_key_press);
return hotkeys_.insert(
std::make_pair(std::make_pair(keysym, modifiers), event_id)).second;
}
bool HotkeyManager::RemoveHotkey(int event_id) {
bool result = false;
std::map<std::pair<uint32, uint32>, int>::iterator iter;
for (iter = hotkeys_.begin(); iter != hotkeys_.end();) {
if (iter->second == event_id) {
hotkeys_.erase(iter++);
result = true;
} else {
++iter;
}
}
return result;
}
bool HotkeyManager::FilterKeyEvent(const XEvent& key_event) {
bool is_key_press = true;
switch (key_event.type) {
case KeyRelease:
is_key_press = false;
/* fall through */
case KeyPress:
break;
default:
LOG(ERROR) << "Unknown event: " << key_event.type;
return false;
}
KeySym keysym = NoSymbol;
::XLookupString(
const_cast<XKeyEvent*>(&key_event.xkey), NULL, 0, &keysym, NULL);
const int event_id =
FilterKeyEventInternal(keysym, key_event.xkey.state, is_key_press);
if (event_id >= 0) {
FOR_EACH_OBSERVER(Observer, observers_, HotkeyPressed(this, event_id));
return true; // The key event should be consumed since it's an IME hotkey.
} else if (event_id == kFiltered) {
return true;
}
return false;
}
uint32 HotkeyManager::NormalizeModifiers(
uint32 keysym, uint32 modifiers, bool is_key_press) const {
modifiers &= (ControlMask | ShiftMask | Mod1Mask);
modifiers |= KeySymToModifier(keysym);
if (!is_key_press) {
modifiers |= kKeyReleaseMask;
}
return modifiers;
}
bool HotkeyManager::IsModifier(uint32 keysym) const {
return KeySymToModifier(keysym) != 0;
}
int HotkeyManager::FilterKeyEventInternal(
uint32 keysym, uint32 modifiers, bool is_key_press) {
modifiers = NormalizeModifiers(keysym, modifiers, is_key_press);
// This logic is the same as bus_input_context_filter_keyboard_shortcuts in
// ibus-daemon.
if (filter_release_events_) {
if (modifiers & kKeyReleaseMask) {
// This release event should not be passed to the application.
return kFiltered;
}
filter_release_events_ = false;
}
int event_id = kNoEvent;
// This logic is the same as ibus_hotkey_profile_filter_key_event in libibus.
// See also http://crosbug.com/6225.
do {
if (modifiers & kKeyReleaseMask) {
if (previous_modifiers_ & kKeyReleaseMask) {
// The previous event has to be a key press event. This is necessary not
// to switch IME when e.g. X is released after Shift+Alt+X is pressed.
break;
}
if ((keysym != previous_keysym_) &&
(!IsModifier(keysym) || !IsModifier(previous_keysym_))) {
// This check is useful when e.g. Alt is released after Shift+Alt+X is
// pressed, and the release event of X is intercepted by the window
// manager. In this case, we should not switch IME, but the first check
// does not work.
// The IsModifier() checks are necessary for the 'press Alt, press
// Shift, release Alt, release Shift' case to work.
break;
}
}
std::map<std::pair<uint32, uint32>, int>::const_iterator iter =
hotkeys_.find(std::make_pair(keysym, modifiers));
if (iter != hotkeys_.end()) {
event_id = iter->second;
}
} while (false);
// This logic is the same as bus_input_context_filter_keyboard_shortcuts in
// ibus-daemon.
previous_keysym_ = keysym;
previous_modifiers_ = modifiers;
// Start filtering release events if a hotkey is detected.
filter_release_events_ = (event_id != kNoEvent);
return event_id;
}
} // namespace input_method
} // namespace chromeos
|