1 /*
2 * globalshortcutmanager_x11.cpp - X11 implementation of global shortcuts
3 * Copyright (C) 2003-2007 Justin Karneges, Michail Pishchagin
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include "globalshortcutmanager.h"
22 #include "globalshortcuttrigger.h"
23
24 #include <QWidget>
25 #include <QKeyEvent>
26 #include <QCoreApplication>
27
28 #include <X11/X.h>
29 #include <X11/Xlib.h>
30 #include <X11/keysym.h>
31
32 #include <QX11Info>
33
34 #ifdef KeyPress
35 // defined by X11 headers
36 const int XKeyPress = KeyPress;
37 const int XKeyRelease = KeyRelease;
38 #undef KeyPress
39 #endif
40
41 class X11KeyTrigger
42 {
43 public:
~X11KeyTrigger()44 virtual ~X11KeyTrigger() {}
45 virtual void activate() = 0;
46 virtual bool isAccepted(const QKeySequence &qkey) const = 0;
47 };
48
49 class X11KeyTriggerManager : public QObject
50 {
51 public:
instance()52 static X11KeyTriggerManager* instance()
53 {
54 if(!instance_)
55 instance_ = new X11KeyTriggerManager();
56 return instance_;
57 }
58
addTrigger(X11KeyTrigger * trigger)59 void addTrigger(X11KeyTrigger* trigger)
60 {
61 triggers_ << trigger;
62 }
63
removeTrigger(X11KeyTrigger * trigger)64 void removeTrigger(X11KeyTrigger* trigger)
65 {
66 triggers_.removeAll(trigger);
67 }
68
69 struct Qt_XK_Keygroup
70 {
71 char num;
72 int sym[3];
73 };
74
75 protected:
76 // reimplemented
eventFilter(QObject * o,QEvent * e)77 bool eventFilter(QObject* o, QEvent* e)
78 {
79 if(e->type() == QEvent::KeyPress) {
80 QKeyEvent* k = static_cast<QKeyEvent*>(e);
81 int qkey = k->key();
82 if (k->modifiers() & Qt::ShiftModifier)
83 qkey |= Qt::SHIFT;
84 if (k->modifiers() & Qt::ControlModifier)
85 qkey |= Qt::CTRL;
86 if (k->modifiers() & Qt::AltModifier)
87 qkey |= Qt::ALT;
88 if (k->modifiers() & Qt::MetaModifier)
89 qkey |= Qt::META;
90
91 foreach(X11KeyTrigger* trigger, triggers_) {
92 if (trigger->isAccepted(QKeySequence(qkey))) {
93 trigger->activate();
94 return true;
95 }
96 }
97 }
98
99 return QObject::eventFilter(o, e);
100 }
101
102 private:
X11KeyTriggerManager()103 X11KeyTriggerManager()
104 : QObject(QCoreApplication::instance())
105 {
106 QCoreApplication::instance()->installEventFilter(this);
107 }
108
109 static X11KeyTriggerManager* instance_;
110 QList<X11KeyTrigger*> triggers_;
111
112 private:
113 struct Qt_XK_Keymap
114 {
115 int key;
116 Qt_XK_Keygroup xk;
117 };
118
119 static Qt_XK_Keymap qt_xk_table[];
120 static long alt_mask;
121 static long meta_mask;
122 static long super_mask;
123 static long hyper_mask;
124 static long numlock_mask;
125 static bool haveMods;
126
127 // adapted from qapplication_x11.cpp
ensureModifiers()128 static void ensureModifiers()
129 {
130 if (haveMods)
131 return;
132
133 Display* appDpy = QX11Info::display();
134 XModifierKeymap* map = XGetModifierMapping(appDpy);
135 if (map) {
136 // XKeycodeToKeysym helper code adapeted from xmodmap
137 int min_keycode, max_keycode, keysyms_per_keycode_return, keysyms_per_keycode = 1;
138 XDisplayKeycodes (appDpy, &min_keycode, &max_keycode);
139 XFree(XGetKeyboardMapping (appDpy, min_keycode, (max_keycode - min_keycode + 1), &keysyms_per_keycode));
140
141 int i, maskIndex = 0, mapIndex = 0;
142 for (maskIndex = 0; maskIndex < 8; maskIndex++) {
143 for (i = 0; i < map->max_keypermod; i++) {
144 if (map->modifiermap[mapIndex]) {
145 KeySym *sym = 0;
146 int symIndex = 0;
147 do {
148 sym = XGetKeyboardMapping(appDpy,map->modifiermap[mapIndex], 1, &keysyms_per_keycode_return);
149 symIndex++;
150 } while ( !sym[0] && symIndex < keysyms_per_keycode);
151 if (alt_mask == 0 && (sym[0] == XK_Alt_L || sym[0] == XK_Alt_R)) {
152 alt_mask = 1 << maskIndex;
153 }
154 if (meta_mask == 0 && (sym[0] == XK_Meta_L || sym[0] == XK_Meta_R)) {
155 meta_mask = 1 << maskIndex;
156 }
157 if (super_mask == 0 && (sym[0] == XK_Super_L || sym[0] == XK_Super_R)) {
158 super_mask = 1 << maskIndex;
159 }
160 if (hyper_mask == 0 && (sym[0] == XK_Hyper_L || sym[0] == XK_Hyper_R)) {
161 hyper_mask = 1 << maskIndex;
162 }
163 if (numlock_mask == 0 && (sym[0] == XK_Num_Lock)) {
164 numlock_mask = 1 << maskIndex;
165 }
166 XFree(sym);
167 }
168 mapIndex++;
169 }
170 }
171
172 XFreeModifiermap(map);
173
174 // logic from qt source see gui/kernel/qkeymapper_x11.cpp
175 if (meta_mask == 0 || meta_mask == alt_mask) {
176 // no meta keys... s,meta,super,
177 meta_mask = super_mask;
178 if (meta_mask == 0 || meta_mask == alt_mask) {
179 // no super keys either? guess we'll use hyper then
180 meta_mask = hyper_mask;
181 }
182 }
183 }
184 else {
185 // assume defaults
186 alt_mask = Mod1Mask;
187 meta_mask = Mod4Mask;
188 }
189
190 haveMods = true;
191 }
192
193 public:
convertKeySequence(const QKeySequence & ks,unsigned int * _mod,Qt_XK_Keygroup * _kg)194 static bool convertKeySequence(const QKeySequence& ks, unsigned int* _mod, Qt_XK_Keygroup* _kg)
195 {
196 int code = ks[0];
197 Qt_XK_Keygroup kg;
198 kg.num = 0;
199 kg.sym[0] = 0;
200
201 ensureModifiers();
202
203 unsigned int mod = 0;
204 if (code & Qt::META)
205 mod |= meta_mask;
206 if (code & Qt::SHIFT)
207 mod |= ShiftMask;
208 if (code & Qt::CTRL)
209 mod |= ControlMask;
210 if (code & Qt::ALT)
211 mod |= alt_mask;
212
213 code &= ~Qt::KeyboardModifierMask;
214
215 bool found = false;
216 for (int n = 0; qt_xk_table[n].key != Qt::Key_unknown; ++n) {
217 if (qt_xk_table[n].key == code) {
218 kg = qt_xk_table[n].xk;
219 found = true;
220 break;
221 }
222 }
223
224 if (!found) {
225 // try latin1
226 if (code >= 0x20 && code <= 0x7f) {
227 kg.num = 1;
228 kg.sym[0] = code;
229 }
230 }
231
232 if (!kg.num)
233 return false;
234
235 if (_mod)
236 *_mod = mod;
237 if (_kg)
238 *_kg = kg;
239
240 return true;
241 }
242
ignModifiersList()243 static QList<long> ignModifiersList()
244 {
245 QList<long> ret;
246 if (numlock_mask) {
247 ret << 0 << LockMask << numlock_mask << (LockMask | numlock_mask);
248 }
249 else {
250 ret << 0 << LockMask;
251 }
252 return ret;
253 }
254 };
255
256 X11KeyTriggerManager* X11KeyTriggerManager::instance_ = NULL;
257
258 class GlobalShortcutManager::KeyTrigger::Impl : public X11KeyTrigger
259 {
260 private:
261 KeyTrigger* trigger_;
262 QKeySequence qkey_;
263
264 struct GrabbedKey {
265 int code;
266 uint mod;
267 };
268 QList<GrabbedKey> grabbedKeys_;
269
270 static bool failed;
XGrabErrorHandler(Display *,XErrorEvent *)271 static int XGrabErrorHandler(Display *, XErrorEvent *)
272 {
273 qWarning("failed to grab key");
274 failed = true;
275 return 0;
276 }
277
bind(int keysym,unsigned int mod)278 void bind(int keysym, unsigned int mod)
279 {
280 int code = XKeysymToKeycode(QX11Info::display(), keysym);
281
282 // don't grab keys with empty code (because it means just the modifier key)
283 if (keysym && !code)
284 return;
285
286 failed = false;
287 XErrorHandler savedErrorHandler = XSetErrorHandler(XGrabErrorHandler);
288 WId w = QX11Info::appRootWindow();
289 foreach(long mask_mod, X11KeyTriggerManager::ignModifiersList()) {
290 XGrabKey(QX11Info::display(), code, mod | mask_mod, w, False, GrabModeAsync, GrabModeAsync);
291 GrabbedKey grabbedKey;
292 grabbedKey.code = code;
293 grabbedKey.mod = mod | mask_mod;
294 grabbedKeys_ << grabbedKey;
295 }
296 XSync(QX11Info::display(), False);
297 XSetErrorHandler(savedErrorHandler);
298 }
299
300 public:
301 /**
302 * Constructor registers the hotkey.
303 */
Impl(GlobalShortcutManager::KeyTrigger * t,const QKeySequence & ks)304 Impl(GlobalShortcutManager::KeyTrigger* t, const QKeySequence& ks)
305 : trigger_(t)
306 , qkey_(ks)
307 {
308 X11KeyTriggerManager::instance()->addTrigger(this);
309
310 X11KeyTriggerManager::Qt_XK_Keygroup kg;
311 unsigned int mod;
312 if (X11KeyTriggerManager::convertKeySequence(ks, &mod, &kg))
313 for (int n = 0; n < kg.num; ++n)
314 bind(kg.sym[n], mod);
315 }
316
317 /**
318 * Destructor unregisters the hotkey.
319 */
~Impl()320 ~Impl()
321 {
322 X11KeyTriggerManager::instance()->removeTrigger(this);
323
324 foreach(GrabbedKey key, grabbedKeys_)
325 XUngrabKey(QX11Info::display(), key.code, key.mod, QX11Info::appRootWindow());
326 }
327
activate()328 void activate()
329 {
330 emit trigger_->triggered();
331 }
332
isAccepted(const QKeySequence & qkey) const333 bool isAccepted(const QKeySequence &qkey) const
334 {
335 return qkey_ == qkey;
336 }
337 };
338
339 bool GlobalShortcutManager::KeyTrigger::Impl::failed;
340 long X11KeyTriggerManager::alt_mask = 0;
341 long X11KeyTriggerManager::meta_mask = 0;
342 long X11KeyTriggerManager::super_mask = 0;
343 long X11KeyTriggerManager::hyper_mask = 0;
344 long X11KeyTriggerManager::numlock_mask = 0;
345 bool X11KeyTriggerManager::haveMods = false;
346
347 X11KeyTriggerManager::Qt_XK_Keymap
348 X11KeyTriggerManager::qt_xk_table[] = {
349 { Qt::Key_Escape, {1, { XK_Escape }}},
350 { Qt::Key_Tab, {2, { XK_Tab, XK_KP_Tab }}},
351 { Qt::Key_Backtab, {1, { XK_ISO_Left_Tab }}},
352 { Qt::Key_Backspace, {1, { XK_BackSpace }}},
353 { Qt::Key_Return, {1, { XK_Return }}},
354 { Qt::Key_Enter, {1, { XK_KP_Enter }}},
355 { Qt::Key_Insert, {2, { XK_Insert, XK_KP_Insert }}},
356 { Qt::Key_Delete, {3, { XK_Delete, XK_KP_Delete, XK_Clear }}},
357 { Qt::Key_Pause, {1, { XK_Pause }}},
358 { Qt::Key_Print, {1, { XK_Print }}},
359 { Qt::Key_SysReq, {1, { XK_Sys_Req }}},
360 { Qt::Key_Clear, {1, { XK_KP_Begin }}},
361 { Qt::Key_Home, {2, { XK_Home, XK_KP_Home }}},
362 { Qt::Key_End, {2, { XK_End, XK_KP_End }}},
363 { Qt::Key_Left, {2, { XK_Left, XK_KP_Left }}},
364 { Qt::Key_Up, {2, { XK_Up, XK_KP_Up }}},
365 { Qt::Key_Right, {2, { XK_Right, XK_KP_Right }}},
366 { Qt::Key_Down, {2, { XK_Down, XK_KP_Down }}},
367 { Qt::Key_PageUp, {2, { XK_Prior, XK_KP_Prior }}},
368 { Qt::Key_PageDown, {2, { XK_Next, XK_KP_Next }}},
369 { Qt::Key_Shift, {3, { XK_Shift_L, XK_Shift_R, XK_Shift_Lock }}},
370 { Qt::Key_Control, {2, { XK_Control_L, XK_Control_R }}},
371 { Qt::Key_Meta, {2, { XK_Meta_L, XK_Meta_R }}},
372 { Qt::Key_Alt, {2, { XK_Alt_L, XK_Alt_R }}},
373 { Qt::Key_CapsLock, {1, { XK_Caps_Lock }}},
374 { Qt::Key_NumLock, {1, { XK_Num_Lock }}},
375 { Qt::Key_ScrollLock, {1, { XK_Scroll_Lock }}},
376 { Qt::Key_Space, {2, { XK_space, XK_KP_Space }}},
377 { Qt::Key_Equal, {2, { XK_equal, XK_KP_Equal }}},
378 { Qt::Key_Asterisk, {2, { XK_asterisk, XK_KP_Multiply }}},
379 { Qt::Key_Plus, {2, { XK_plus, XK_KP_Add }}},
380 { Qt::Key_Comma, {2, { XK_comma, XK_KP_Separator }}},
381 { Qt::Key_Minus, {2, { XK_minus, XK_KP_Subtract }}},
382 { Qt::Key_Period, {2, { XK_period, XK_KP_Decimal }}},
383 { Qt::Key_Slash, {2, { XK_slash, XK_KP_Divide }}},
384 { Qt::Key_F1, {1, { XK_F1 }}},
385 { Qt::Key_F2, {1, { XK_F2 }}},
386 { Qt::Key_F3, {1, { XK_F3 }}},
387 { Qt::Key_F4, {1, { XK_F4 }}},
388 { Qt::Key_F5, {1, { XK_F5 }}},
389 { Qt::Key_F6, {1, { XK_F6 }}},
390 { Qt::Key_F7, {1, { XK_F7 }}},
391 { Qt::Key_F8, {1, { XK_F8 }}},
392 { Qt::Key_F9, {1, { XK_F9 }}},
393 { Qt::Key_F10, {1, { XK_F10 }}},
394 { Qt::Key_F11, {1, { XK_F11 }}},
395 { Qt::Key_F12, {1, { XK_F12 }}},
396 { Qt::Key_F13, {1, { XK_F13 }}},
397 { Qt::Key_F14, {1, { XK_F14 }}},
398 { Qt::Key_F15, {1, { XK_F15 }}},
399 { Qt::Key_F16, {1, { XK_F16 }}},
400 { Qt::Key_F17, {1, { XK_F17 }}},
401 { Qt::Key_F18, {1, { XK_F18 }}},
402 { Qt::Key_F19, {1, { XK_F19 }}},
403 { Qt::Key_F20, {1, { XK_F20 }}},
404 { Qt::Key_F21, {1, { XK_F21 }}},
405 { Qt::Key_F22, {1, { XK_F22 }}},
406 { Qt::Key_F23, {1, { XK_F23 }}},
407 { Qt::Key_F24, {1, { XK_F24 }}},
408 { Qt::Key_F25, {1, { XK_F25 }}},
409 { Qt::Key_F26, {1, { XK_F26 }}},
410 { Qt::Key_F27, {1, { XK_F27 }}},
411 { Qt::Key_F28, {1, { XK_F28 }}},
412 { Qt::Key_F29, {1, { XK_F29 }}},
413 { Qt::Key_F30, {1, { XK_F30 }}},
414 { Qt::Key_F31, {1, { XK_F31 }}},
415 { Qt::Key_F32, {1, { XK_F32 }}},
416 { Qt::Key_F33, {1, { XK_F33 }}},
417 { Qt::Key_F34, {1, { XK_F34 }}},
418 { Qt::Key_F35, {1, { XK_F35 }}},
419 { Qt::Key_Super_L, {1, { XK_Super_L }}},
420 { Qt::Key_Super_R, {1, { XK_Super_R }}},
421 { Qt::Key_Menu, {1, { XK_Menu }}},
422 { Qt::Key_Hyper_L, {1, { XK_Hyper_L }}},
423 { Qt::Key_Hyper_R, {1, { XK_Hyper_R }}},
424 { Qt::Key_Help, {1, { XK_Help }}},
425 { Qt::Key_Direction_L, {0, { 0 }}},
426 { Qt::Key_Direction_R, {0, { 0 }}},
427
428 { Qt::Key_unknown, {0, { 0 }}},
429 };
430
KeyTrigger(const QKeySequence & key)431 GlobalShortcutManager::KeyTrigger::KeyTrigger(const QKeySequence& key)
432 {
433 d = new Impl(this, key);
434 }
435
~KeyTrigger()436 GlobalShortcutManager::KeyTrigger::~KeyTrigger()
437 {
438 delete d;
439 d = 0;
440 }
441