1 | /*
|
---|
2 | * globalaccelman_x11.cpp - manager for global hotkeys
|
---|
3 | * Copyright (C) 2003 Justin Karneges
|
---|
4 | *
|
---|
5 | * This library is free software; you can redistribute it and/or
|
---|
6 | * modify it under the terms of the GNU Lesser General Public
|
---|
7 | * License as published by the Free Software Foundation; either
|
---|
8 | * version 2.1 of the License, or (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This library 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 GNU
|
---|
13 | * Lesser General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU Lesser General Public
|
---|
16 | * License along with this library; if not, write to the Free Software
|
---|
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
---|
18 | *
|
---|
19 | */
|
---|
20 |
|
---|
21 | #include"globalaccelman.h"
|
---|
22 |
|
---|
23 | #include<qkeysequence.h>
|
---|
24 | #include<qwidget.h>
|
---|
25 | #include<qapplication.h>
|
---|
26 |
|
---|
27 | #include<X11/X.h>
|
---|
28 | #include<X11/Xlib.h>
|
---|
29 | #include<X11/keysym.h>
|
---|
30 |
|
---|
31 | #ifdef KeyPress
|
---|
32 | // defined by X11 headers
|
---|
33 | const int XKeyPress = KeyPress;
|
---|
34 | const int XKeyRelease = KeyRelease;
|
---|
35 | #undef KeyPress
|
---|
36 | #endif
|
---|
37 |
|
---|
38 | struct QT_XK_KEYGROUP
|
---|
39 | {
|
---|
40 | char num;
|
---|
41 | int sym[3];
|
---|
42 | };
|
---|
43 |
|
---|
44 | struct QT_XK_KEYMAP
|
---|
45 | {
|
---|
46 | int key;
|
---|
47 | QT_XK_KEYGROUP xk;
|
---|
48 | } qt_xk_table[] = {
|
---|
49 | { Qt::Key_Escape, {1, { XK_Escape }}},
|
---|
50 | { Qt::Key_Tab, {2, { XK_Tab, XK_KP_Tab }}},
|
---|
51 | { Qt::Key_Backtab, {1, { XK_ISO_Left_Tab }}},
|
---|
52 | { Qt::Key_Backspace, {1, { XK_BackSpace }}},
|
---|
53 | { Qt::Key_Return, {1, { XK_Return }}},
|
---|
54 | { Qt::Key_Enter, {1, { XK_KP_Enter }}},
|
---|
55 | { Qt::Key_Insert, {2, { XK_Insert, XK_KP_Insert }}},
|
---|
56 | { Qt::Key_Delete, {3, { XK_Delete, XK_KP_Delete, XK_Clear }}},
|
---|
57 | { Qt::Key_Pause, {1, { XK_Pause }}},
|
---|
58 | { Qt::Key_Print, {1, { XK_Print }}},
|
---|
59 | { Qt::Key_SysReq, {1, { XK_Sys_Req }}},
|
---|
60 | { Qt::Key_Clear, {1, { XK_KP_Begin }}},
|
---|
61 | { Qt::Key_Home, {2, { XK_Home, XK_KP_Home }}},
|
---|
62 | { Qt::Key_End, {2, { XK_End, XK_KP_End }}},
|
---|
63 | { Qt::Key_Left, {2, { XK_Left, XK_KP_Left }}},
|
---|
64 | { Qt::Key_Up, {2, { XK_Up, XK_KP_Up }}},
|
---|
65 | { Qt::Key_Right, {2, { XK_Right, XK_KP_Right }}},
|
---|
66 | { Qt::Key_Down, {2, { XK_Down, XK_KP_Down }}},
|
---|
67 | { Qt::Key_Prior, {2, { XK_Prior, XK_KP_Prior }}},
|
---|
68 | { Qt::Key_Next, {2, { XK_Next, XK_KP_Next }}},
|
---|
69 | { Qt::Key_Shift, {3, { XK_Shift_L, XK_Shift_R, XK_Shift_Lock }}},
|
---|
70 | { Qt::Key_Control, {2, { XK_Control_L, XK_Control_R }}},
|
---|
71 | { Qt::Key_Meta, {2, { XK_Meta_L, XK_Meta_R }}},
|
---|
72 | { Qt::Key_Alt, {2, { XK_Alt_L, XK_Alt_R }}},
|
---|
73 | { Qt::Key_CapsLock, {1, { XK_Caps_Lock }}},
|
---|
74 | { Qt::Key_NumLock, {1, { XK_Num_Lock }}},
|
---|
75 | { Qt::Key_ScrollLock, {1, { XK_Scroll_Lock }}},
|
---|
76 | { Qt::Key_Space, {2, { XK_space, XK_KP_Space }}},
|
---|
77 | { Qt::Key_Equal, {2, { XK_equal, XK_KP_Equal }}},
|
---|
78 | { Qt::Key_Asterisk, {2, { XK_asterisk, XK_KP_Multiply }}},
|
---|
79 | { Qt::Key_Plus, {2, { XK_plus, XK_KP_Add }}},
|
---|
80 | { Qt::Key_Comma, {2, { XK_comma, XK_KP_Separator }}},
|
---|
81 | { Qt::Key_Minus, {2, { XK_minus, XK_KP_Subtract }}},
|
---|
82 | { Qt::Key_Period, {2, { XK_period, XK_KP_Decimal }}},
|
---|
83 | { Qt::Key_Slash, {2, { XK_slash, XK_KP_Divide }}},
|
---|
84 | { Qt::Key_F1, {1, { XK_F1 }}},
|
---|
85 | { Qt::Key_F2, {1, { XK_F2 }}},
|
---|
86 | { Qt::Key_F3, {1, { XK_F3 }}},
|
---|
87 | { Qt::Key_F4, {1, { XK_F4 }}},
|
---|
88 | { Qt::Key_F5, {1, { XK_F5 }}},
|
---|
89 | { Qt::Key_F6, {1, { XK_F6 }}},
|
---|
90 | { Qt::Key_F7, {1, { XK_F7 }}},
|
---|
91 | { Qt::Key_F8, {1, { XK_F8 }}},
|
---|
92 | { Qt::Key_F9, {1, { XK_F9 }}},
|
---|
93 | { Qt::Key_F10, {1, { XK_F10 }}},
|
---|
94 | { Qt::Key_F11, {1, { XK_F11 }}},
|
---|
95 | { Qt::Key_F12, {1, { XK_F12 }}},
|
---|
96 | { Qt::Key_F13, {1, { XK_F13 }}},
|
---|
97 | { Qt::Key_F14, {1, { XK_F14 }}},
|
---|
98 | { Qt::Key_F15, {1, { XK_F15 }}},
|
---|
99 | { Qt::Key_F16, {1, { XK_F16 }}},
|
---|
100 | { Qt::Key_F17, {1, { XK_F17 }}},
|
---|
101 | { Qt::Key_F18, {1, { XK_F18 }}},
|
---|
102 | { Qt::Key_F19, {1, { XK_F19 }}},
|
---|
103 | { Qt::Key_F20, {1, { XK_F20 }}},
|
---|
104 | { Qt::Key_F21, {1, { XK_F21 }}},
|
---|
105 | { Qt::Key_F22, {1, { XK_F22 }}},
|
---|
106 | { Qt::Key_F23, {1, { XK_F23 }}},
|
---|
107 | { Qt::Key_F24, {1, { XK_F24 }}},
|
---|
108 | { Qt::Key_F25, {1, { XK_F25 }}},
|
---|
109 | { Qt::Key_F26, {1, { XK_F26 }}},
|
---|
110 | { Qt::Key_F27, {1, { XK_F27 }}},
|
---|
111 | { Qt::Key_F28, {1, { XK_F28 }}},
|
---|
112 | { Qt::Key_F29, {1, { XK_F29 }}},
|
---|
113 | { Qt::Key_F30, {1, { XK_F30 }}},
|
---|
114 | { Qt::Key_F31, {1, { XK_F31 }}},
|
---|
115 | { Qt::Key_F32, {1, { XK_F32 }}},
|
---|
116 | { Qt::Key_F33, {1, { XK_F33 }}},
|
---|
117 | { Qt::Key_F34, {1, { XK_F34 }}},
|
---|
118 | { Qt::Key_F35, {1, { XK_F35 }}},
|
---|
119 | { Qt::Key_Super_L, {1, { XK_Super_L }}},
|
---|
120 | { Qt::Key_Super_R, {1, { XK_Super_R }}},
|
---|
121 | { Qt::Key_Menu, {1, { XK_Menu }}},
|
---|
122 | { Qt::Key_Hyper_L, {1, { XK_Hyper_L }}},
|
---|
123 | { Qt::Key_Hyper_R, {1, { XK_Hyper_R }}},
|
---|
124 | { Qt::Key_Help, {1, { XK_Help }}},
|
---|
125 | { Qt::Key_Direction_L,{0, { 0 }}},
|
---|
126 | { Qt::Key_Direction_R,{0, { 0 }}},
|
---|
127 |
|
---|
128 | { Qt::Key_unknown, {0, { 0 }}},
|
---|
129 | };
|
---|
130 |
|
---|
131 | static long alt_mask = 0;
|
---|
132 | static long meta_mask = 0;
|
---|
133 | static bool haveMods = false;
|
---|
134 |
|
---|
135 | // adapted from qapplication_x11.cpp
|
---|
136 | static void ensureModifiers()
|
---|
137 | {
|
---|
138 | if(haveMods)
|
---|
139 | return;
|
---|
140 |
|
---|
141 | Display *appDpy = qt_xdisplay();
|
---|
142 | XModifierKeymap *map = XGetModifierMapping(appDpy);
|
---|
143 | if(map) {
|
---|
144 | int i, maskIndex = 0, mapIndex = 0;
|
---|
145 | for (maskIndex = 0; maskIndex < 8; maskIndex++) {
|
---|
146 | for (i = 0; i < map->max_keypermod; i++) {
|
---|
147 | if (map->modifiermap[mapIndex]) {
|
---|
148 | KeySym sym = XKeycodeToKeysym(appDpy, map->modifiermap[mapIndex], 0);
|
---|
149 | if ( alt_mask == 0 && ( sym == XK_Alt_L || sym == XK_Alt_R ) ) {
|
---|
150 | alt_mask = 1 << maskIndex;
|
---|
151 | }
|
---|
152 | if ( meta_mask == 0 && (sym == XK_Meta_L || sym == XK_Meta_R ) ) {
|
---|
153 | meta_mask = 1 << maskIndex;
|
---|
154 | }
|
---|
155 | }
|
---|
156 | mapIndex++;
|
---|
157 | }
|
---|
158 | }
|
---|
159 |
|
---|
160 | XFreeModifiermap(map);
|
---|
161 | }
|
---|
162 | else {
|
---|
163 | // assume defaults
|
---|
164 | alt_mask = Mod1Mask;
|
---|
165 | meta_mask = Mod4Mask;
|
---|
166 | }
|
---|
167 |
|
---|
168 | haveMods = true;
|
---|
169 | }
|
---|
170 |
|
---|
171 | static bool convertKeySequence(const QKeySequence &ks, unsigned int *_mod, QT_XK_KEYGROUP *_kg)
|
---|
172 | {
|
---|
173 | int code = ks;
|
---|
174 | ensureModifiers();
|
---|
175 |
|
---|
176 | unsigned int mod = 0;
|
---|
177 | if(code & Qt::META)
|
---|
178 | mod |= meta_mask;
|
---|
179 | if(code & Qt::SHIFT)
|
---|
180 | mod |= ShiftMask;
|
---|
181 | if(code & Qt::CTRL)
|
---|
182 | mod |= ControlMask;
|
---|
183 | if(code & Qt::ALT)
|
---|
184 | mod |= alt_mask;
|
---|
185 |
|
---|
186 | QT_XK_KEYGROUP kg;
|
---|
187 | kg.num = 0;
|
---|
188 | code &= 0xffff;
|
---|
189 | // see if it is in our table
|
---|
190 | bool found = false;
|
---|
191 | for(int n = 0; qt_xk_table[n].key != Qt::Key_unknown; ++n) {
|
---|
192 | if(qt_xk_table[n].key == code) {
|
---|
193 | kg = qt_xk_table[n].xk;
|
---|
194 | found = true;
|
---|
195 | break;
|
---|
196 | }
|
---|
197 | }
|
---|
198 | // not found?
|
---|
199 | if(!found) {
|
---|
200 | // try latin1
|
---|
201 | if(code >= 0x20 && code <= 0x7f) {
|
---|
202 | kg.num = 1;
|
---|
203 | kg.sym[0] = code;
|
---|
204 | }
|
---|
205 | }
|
---|
206 | if(!kg.num)
|
---|
207 | return false;
|
---|
208 |
|
---|
209 | if(_mod)
|
---|
210 | *_mod = mod;
|
---|
211 | if(_kg)
|
---|
212 | *_kg = kg;
|
---|
213 |
|
---|
214 | return true;
|
---|
215 | }
|
---|
216 |
|
---|
217 | class X11HotKey
|
---|
218 | {
|
---|
219 | public:
|
---|
220 | X11HotKey() {}
|
---|
221 | ~X11HotKey()
|
---|
222 | {
|
---|
223 | XUngrabKey(dsp, code, mod, grab);
|
---|
224 | }
|
---|
225 |
|
---|
226 | static bool failed;
|
---|
227 | static int XGrabErrorHandler(Display *, XErrorEvent *)
|
---|
228 | {
|
---|
229 | failed = true;
|
---|
230 | return 0;
|
---|
231 | }
|
---|
232 |
|
---|
233 | static X11HotKey * bind(int keysym, unsigned int mod)
|
---|
234 | {
|
---|
235 | Display *dsp = qt_xdisplay();
|
---|
236 | Window grab = qt_xrootwin();
|
---|
237 | int code = XKeysymToKeycode(dsp, keysym);
|
---|
238 |
|
---|
239 | // don't grab keys with empty code (because it means just the modifier key)
|
---|
240 | if ( keysym && !code )
|
---|
241 | return 0;
|
---|
242 |
|
---|
243 | failed = false;
|
---|
244 | XErrorHandler savedErrorHandler = XSetErrorHandler(XGrabErrorHandler);
|
---|
245 | XGrabKey(dsp, code, mod, grab, False, GrabModeAsync, GrabModeAsync);
|
---|
246 | XSync(dsp, False);
|
---|
247 | XSetErrorHandler(savedErrorHandler);
|
---|
248 | if(failed)
|
---|
249 | return 0;
|
---|
250 | X11HotKey *k = new X11HotKey;
|
---|
251 | k->dsp = dsp;
|
---|
252 | k->grab = grab;
|
---|
253 | k->code = code;
|
---|
254 | k->mod = mod;
|
---|
255 | return k;
|
---|
256 | }
|
---|
257 |
|
---|
258 | Display *dsp; // X11 display
|
---|
259 | Window grab; // Window (root window, in our case)
|
---|
260 | int code; // Keycode
|
---|
261 | unsigned int mod; // modifiers (shift/alt/etc)
|
---|
262 | };
|
---|
263 |
|
---|
264 | bool X11HotKey::failed;
|
---|
265 |
|
---|
266 | class X11HotKeyGroup
|
---|
267 | {
|
---|
268 | public:
|
---|
269 | X11HotKeyGroup()
|
---|
270 | {
|
---|
271 | list.setAutoDelete(true);
|
---|
272 | }
|
---|
273 |
|
---|
274 | ~X11HotKeyGroup()
|
---|
275 | {
|
---|
276 | }
|
---|
277 |
|
---|
278 | static X11HotKeyGroup * bind(const QT_XK_KEYGROUP &kg, unsigned int mod, int qkey, int id)
|
---|
279 | {
|
---|
280 | QPtrList<X11HotKey> list;
|
---|
281 | for(int n = 0; n < kg.num; ++n) {
|
---|
282 | X11HotKey *k = X11HotKey::bind(kg.sym[n], mod);
|
---|
283 | if(k)
|
---|
284 | list.append(k);
|
---|
285 | }
|
---|
286 | if(list.isEmpty())
|
---|
287 | return 0;
|
---|
288 | X11HotKeyGroup *h = new X11HotKeyGroup;
|
---|
289 | h->list = list;
|
---|
290 | h->qkey = qkey;
|
---|
291 | h->id = id;
|
---|
292 | return h;
|
---|
293 | }
|
---|
294 |
|
---|
295 | QPtrList<X11HotKey> list; // Associated X11HotKeys
|
---|
296 | int qkey; // Qt keycode (with modifiers)
|
---|
297 | int id; // GlobalAccelManager unique ID for this hotkey
|
---|
298 | };
|
---|
299 |
|
---|
300 | class GlobalAccelManager::Private
|
---|
301 | {
|
---|
302 | public:
|
---|
303 | Private(GlobalAccelManager *_man)
|
---|
304 | {
|
---|
305 | man = _man;
|
---|
306 | id_at = 1;
|
---|
307 | list.setAutoDelete(true);
|
---|
308 | f = new Filter(this);
|
---|
309 | qApp->installEventFilter(f);
|
---|
310 | }
|
---|
311 |
|
---|
312 | ~Private()
|
---|
313 | {
|
---|
314 | qApp->removeEventFilter(f);
|
---|
315 | delete f;
|
---|
316 | }
|
---|
317 |
|
---|
318 | bool isThisYours(int qkey)
|
---|
319 | {
|
---|
320 | QPtrListIterator<X11HotKeyGroup> it(list);
|
---|
321 | for(X11HotKeyGroup *k; (k = it.current()); ++it) {
|
---|
322 | if(k->qkey == qkey) {
|
---|
323 | man->triggerActivated(k->id);
|
---|
324 | return true;
|
---|
325 | }
|
---|
326 | }
|
---|
327 | return false;
|
---|
328 | }
|
---|
329 |
|
---|
330 | class Filter : public QObject
|
---|
331 | {
|
---|
332 | public:
|
---|
333 | Filter(Private *_p)
|
---|
334 | {
|
---|
335 | p = _p;
|
---|
336 | }
|
---|
337 |
|
---|
338 | protected:
|
---|
339 | bool eventFilter(QObject *o, QEvent *e)
|
---|
340 | {
|
---|
341 | if(e->type() == QEvent::KeyPress) {
|
---|
342 | QKeyEvent *k = (QKeyEvent *)e;
|
---|
343 | int qkey = k->key();
|
---|
344 | if(k->state() & ShiftButton)
|
---|
345 | qkey |= Qt::SHIFT;
|
---|
346 | if(k->state() & ControlButton)
|
---|
347 | qkey |= Qt::CTRL;
|
---|
348 | if(k->state() & AltButton)
|
---|
349 | qkey |= Qt::ALT;
|
---|
350 | if(k->state() & MetaButton)
|
---|
351 | qkey |= Qt::META;
|
---|
352 |
|
---|
353 | if(p->isThisYours(qkey))
|
---|
354 | return true;
|
---|
355 | }
|
---|
356 | return QObject::eventFilter(o, e);
|
---|
357 | }
|
---|
358 |
|
---|
359 | private:
|
---|
360 | Private *p;
|
---|
361 | };
|
---|
362 |
|
---|
363 | GlobalAccelManager *man;
|
---|
364 | int id_at;
|
---|
365 | Filter *f;
|
---|
366 | QPtrList<X11HotKeyGroup> list;
|
---|
367 | };
|
---|
368 |
|
---|
369 | GlobalAccelManager::GlobalAccelManager()
|
---|
370 | {
|
---|
371 | d = new Private(this);
|
---|
372 | }
|
---|
373 |
|
---|
374 | GlobalAccelManager::~GlobalAccelManager()
|
---|
375 | {
|
---|
376 | delete d;
|
---|
377 | }
|
---|
378 |
|
---|
379 | int GlobalAccelManager::setAccel(const QKeySequence &ks)
|
---|
380 | {
|
---|
381 | QT_XK_KEYGROUP kg;
|
---|
382 | unsigned int mod;
|
---|
383 | if(!convertKeySequence(ks, &mod, &kg))
|
---|
384 | return 0;
|
---|
385 |
|
---|
386 | X11HotKeyGroup *h = X11HotKeyGroup::bind(kg, mod, ks, d->id_at++);
|
---|
387 | if(!h)
|
---|
388 | return 0;
|
---|
389 | d->list.append(h);
|
---|
390 |
|
---|
391 | return h->id;
|
---|
392 | }
|
---|
393 |
|
---|
394 | void GlobalAccelManager::removeAccel(int id)
|
---|
395 | {
|
---|
396 | QPtrListIterator<X11HotKeyGroup> it(d->list);
|
---|
397 | for(X11HotKeyGroup *hk = 0; (hk = it.current()); ++it) {
|
---|
398 | if(hk->id == id) {
|
---|
399 | d->list.removeRef(hk);
|
---|
400 | return;
|
---|
401 | }
|
---|
402 | }
|
---|
403 | }
|
---|