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