/* * globalaccelman_x11.cpp - manager for global hotkeys * Copyright (C) 2003 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include"globalaccelman.h" #include #include #include #include #include #include #ifdef KeyPress // defined by X11 headers const int XKeyPress = KeyPress; const int XKeyRelease = KeyRelease; #undef KeyPress #endif struct QT_XK_KEYGROUP { char num; int sym[3]; }; struct QT_XK_KEYMAP { int key; QT_XK_KEYGROUP xk; } qt_xk_table[] = { { Qt::Key_Escape, {1, { XK_Escape }}}, { Qt::Key_Tab, {2, { XK_Tab, XK_KP_Tab }}}, { Qt::Key_Backtab, {1, { XK_ISO_Left_Tab }}}, { Qt::Key_Backspace, {1, { XK_BackSpace }}}, { Qt::Key_Return, {1, { XK_Return }}}, { Qt::Key_Enter, {1, { XK_KP_Enter }}}, { Qt::Key_Insert, {2, { XK_Insert, XK_KP_Insert }}}, { Qt::Key_Delete, {3, { XK_Delete, XK_KP_Delete, XK_Clear }}}, { Qt::Key_Pause, {1, { XK_Pause }}}, { Qt::Key_Print, {1, { XK_Print }}}, { Qt::Key_SysReq, {1, { XK_Sys_Req }}}, { Qt::Key_Clear, {1, { XK_KP_Begin }}}, { Qt::Key_Home, {2, { XK_Home, XK_KP_Home }}}, { Qt::Key_End, {2, { XK_End, XK_KP_End }}}, { Qt::Key_Left, {2, { XK_Left, XK_KP_Left }}}, { Qt::Key_Up, {2, { XK_Up, XK_KP_Up }}}, { Qt::Key_Right, {2, { XK_Right, XK_KP_Right }}}, { Qt::Key_Down, {2, { XK_Down, XK_KP_Down }}}, { Qt::Key_Prior, {2, { XK_Prior, XK_KP_Prior }}}, { Qt::Key_Next, {2, { XK_Next, XK_KP_Next }}}, { Qt::Key_Shift, {3, { XK_Shift_L, XK_Shift_R, XK_Shift_Lock }}}, { Qt::Key_Control, {2, { XK_Control_L, XK_Control_R }}}, { Qt::Key_Meta, {2, { XK_Meta_L, XK_Meta_R }}}, { Qt::Key_Alt, {2, { XK_Alt_L, XK_Alt_R }}}, { Qt::Key_CapsLock, {1, { XK_Caps_Lock }}}, { Qt::Key_NumLock, {1, { XK_Num_Lock }}}, { Qt::Key_ScrollLock, {1, { XK_Scroll_Lock }}}, { Qt::Key_Space, {2, { XK_space, XK_KP_Space }}}, { Qt::Key_Equal, {2, { XK_equal, XK_KP_Equal }}}, { Qt::Key_Asterisk, {2, { XK_asterisk, XK_KP_Multiply }}}, { Qt::Key_Plus, {2, { XK_plus, XK_KP_Add }}}, { Qt::Key_Comma, {2, { XK_comma, XK_KP_Separator }}}, { Qt::Key_Minus, {2, { XK_minus, XK_KP_Subtract }}}, { Qt::Key_Period, {2, { XK_period, XK_KP_Decimal }}}, { Qt::Key_Slash, {2, { XK_slash, XK_KP_Divide }}}, { Qt::Key_F1, {1, { XK_F1 }}}, { Qt::Key_F2, {1, { XK_F2 }}}, { Qt::Key_F3, {1, { XK_F3 }}}, { Qt::Key_F4, {1, { XK_F4 }}}, { Qt::Key_F5, {1, { XK_F5 }}}, { Qt::Key_F6, {1, { XK_F6 }}}, { Qt::Key_F7, {1, { XK_F7 }}}, { Qt::Key_F8, {1, { XK_F8 }}}, { Qt::Key_F9, {1, { XK_F9 }}}, { Qt::Key_F10, {1, { XK_F10 }}}, { Qt::Key_F11, {1, { XK_F11 }}}, { Qt::Key_F12, {1, { XK_F12 }}}, { Qt::Key_F13, {1, { XK_F13 }}}, { Qt::Key_F14, {1, { XK_F14 }}}, { Qt::Key_F15, {1, { XK_F15 }}}, { Qt::Key_F16, {1, { XK_F16 }}}, { Qt::Key_F17, {1, { XK_F17 }}}, { Qt::Key_F18, {1, { XK_F18 }}}, { Qt::Key_F19, {1, { XK_F19 }}}, { Qt::Key_F20, {1, { XK_F20 }}}, { Qt::Key_F21, {1, { XK_F21 }}}, { Qt::Key_F22, {1, { XK_F22 }}}, { Qt::Key_F23, {1, { XK_F23 }}}, { Qt::Key_F24, {1, { XK_F24 }}}, { Qt::Key_F25, {1, { XK_F25 }}}, { Qt::Key_F26, {1, { XK_F26 }}}, { Qt::Key_F27, {1, { XK_F27 }}}, { Qt::Key_F28, {1, { XK_F28 }}}, { Qt::Key_F29, {1, { XK_F29 }}}, { Qt::Key_F30, {1, { XK_F30 }}}, { Qt::Key_F31, {1, { XK_F31 }}}, { Qt::Key_F32, {1, { XK_F32 }}}, { Qt::Key_F33, {1, { XK_F33 }}}, { Qt::Key_F34, {1, { XK_F34 }}}, { Qt::Key_F35, {1, { XK_F35 }}}, { Qt::Key_Super_L, {1, { XK_Super_L }}}, { Qt::Key_Super_R, {1, { XK_Super_R }}}, { Qt::Key_Menu, {1, { XK_Menu }}}, { Qt::Key_Hyper_L, {1, { XK_Hyper_L }}}, { Qt::Key_Hyper_R, {1, { XK_Hyper_R }}}, { Qt::Key_Help, {1, { XK_Help }}}, { Qt::Key_Direction_L,{0, { 0 }}}, { Qt::Key_Direction_R,{0, { 0 }}}, { Qt::Key_unknown, {0, { 0 }}}, }; static long alt_mask = 0; static long meta_mask = 0; static bool haveMods = false; // adapted from qapplication_x11.cpp static void ensureModifiers() { if(haveMods) return; Display *appDpy = qt_xdisplay(); XModifierKeymap *map = XGetModifierMapping(appDpy); if(map) { int i, maskIndex = 0, mapIndex = 0; for (maskIndex = 0; maskIndex < 8; maskIndex++) { for (i = 0; i < map->max_keypermod; i++) { if (map->modifiermap[mapIndex]) { KeySym sym = XKeycodeToKeysym(appDpy, map->modifiermap[mapIndex], 0); if ( alt_mask == 0 && ( sym == XK_Alt_L || sym == XK_Alt_R ) ) { alt_mask = 1 << maskIndex; } if ( meta_mask == 0 && (sym == XK_Meta_L || sym == XK_Meta_R ) ) { meta_mask = 1 << maskIndex; } } mapIndex++; } } XFreeModifiermap(map); } else { // assume defaults alt_mask = Mod1Mask; meta_mask = Mod4Mask; } haveMods = true; } static bool convertKeySequence(const QKeySequence &ks, unsigned int *_mod, QT_XK_KEYGROUP *_kg) { int code = ks; ensureModifiers(); unsigned int mod = 0; if(code & Qt::META) mod |= meta_mask; if(code & Qt::SHIFT) mod |= ShiftMask; if(code & Qt::CTRL) mod |= ControlMask; if(code & Qt::ALT) mod |= alt_mask; QT_XK_KEYGROUP kg; kg.num = 0; code &= 0xffff; // see if it is in our table bool found = false; for(int n = 0; qt_xk_table[n].key != Qt::Key_unknown; ++n) { if(qt_xk_table[n].key == code) { kg = qt_xk_table[n].xk; found = true; break; } } // not found? if(!found) { // try latin1 if(code >= 0x20 && code <= 0x7f) { kg.num = 1; kg.sym[0] = code; } } if(!kg.num) return false; if(_mod) *_mod = mod; if(_kg) *_kg = kg; return true; } class X11HotKey { public: X11HotKey() {} ~X11HotKey() { XUngrabKey(dsp, code, mod, grab); } static bool failed; static int XGrabErrorHandler(Display *, XErrorEvent *) { failed = true; return 0; } static X11HotKey * bind(int keysym, unsigned int mod) { Display *dsp = qt_xdisplay(); Window grab = qt_xrootwin(); int code = XKeysymToKeycode(dsp, keysym); // don't grab keys with empty code (because it means just the modifier key) if ( keysym && !code ) return 0; failed = false; XErrorHandler savedErrorHandler = XSetErrorHandler(XGrabErrorHandler); XGrabKey(dsp, code, mod, grab, False, GrabModeAsync, GrabModeAsync); XSync(dsp, False); XSetErrorHandler(savedErrorHandler); if(failed) return 0; X11HotKey *k = new X11HotKey; k->dsp = dsp; k->grab = grab; k->code = code; k->mod = mod; return k; } Display *dsp; // X11 display Window grab; // Window (root window, in our case) int code; // Keycode unsigned int mod; // modifiers (shift/alt/etc) }; bool X11HotKey::failed; class X11HotKeyGroup { public: X11HotKeyGroup() { list.setAutoDelete(true); } ~X11HotKeyGroup() { } static X11HotKeyGroup * bind(const QT_XK_KEYGROUP &kg, unsigned int mod, int qkey, int id) { QPtrList list; for(int n = 0; n < kg.num; ++n) { X11HotKey *k = X11HotKey::bind(kg.sym[n], mod); if(k) list.append(k); } if(list.isEmpty()) return 0; X11HotKeyGroup *h = new X11HotKeyGroup; h->list = list; h->qkey = qkey; h->id = id; return h; } QPtrList list; // Associated X11HotKeys int qkey; // Qt keycode (with modifiers) int id; // GlobalAccelManager unique ID for this hotkey }; class GlobalAccelManager::Private { public: Private(GlobalAccelManager *_man) { man = _man; id_at = 1; list.setAutoDelete(true); f = new Filter(this); qApp->installEventFilter(f); } ~Private() { qApp->removeEventFilter(f); delete f; } bool isThisYours(int qkey) { QPtrListIterator it(list); for(X11HotKeyGroup *k; (k = it.current()); ++it) { if(k->qkey == qkey) { man->triggerActivated(k->id); return true; } } return false; } class Filter : public QObject { public: Filter(Private *_p) { p = _p; } protected: bool eventFilter(QObject *o, QEvent *e) { if(e->type() == QEvent::KeyPress) { QKeyEvent *k = (QKeyEvent *)e; int qkey = k->key(); if(k->state() & ShiftButton) qkey |= Qt::SHIFT; if(k->state() & ControlButton) qkey |= Qt::CTRL; if(k->state() & AltButton) qkey |= Qt::ALT; if(k->state() & MetaButton) qkey |= Qt::META; if(p->isThisYours(qkey)) return true; } return QObject::eventFilter(o, e); } private: Private *p; }; GlobalAccelManager *man; int id_at; Filter *f; QPtrList list; }; GlobalAccelManager::GlobalAccelManager() { d = new Private(this); } GlobalAccelManager::~GlobalAccelManager() { delete d; } int GlobalAccelManager::setAccel(const QKeySequence &ks) { QT_XK_KEYGROUP kg; unsigned int mod; if(!convertKeySequence(ks, &mod, &kg)) return 0; X11HotKeyGroup *h = X11HotKeyGroup::bind(kg, mod, ks, d->id_at++); if(!h) return 0; d->list.append(h); return h->id; } void GlobalAccelManager::removeAccel(int id) { QPtrListIterator it(d->list); for(X11HotKeyGroup *hk = 0; (hk = it.current()); ++it) { if(hk->id == id) { d->list.removeRef(hk); return; } } }