diff --git a/src/runtime/plugins/xcb/kglobalaccel_x11.cpp b/src/runtime/plugins/xcb/kglobalaccel_x11.cpp index 9b37c7b..d76f1d5 100644 --- a/src/runtime/plugins/xcb/kglobalaccel_x11.cpp +++ b/src/runtime/plugins/xcb/kglobalaccel_x11.cpp @@ -1,314 +1,276 @@ /* This file is part of the KDE libraries Copyright (C) 2001,2002 Ellis Whitehead Copyright (C) 2013 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kglobalaccel_x11.h" #include "logging_p.h" #include "kkeyserver.h" #include #include #include #include #include #include // xcb #include #include // g_keyModMaskXAccel // mask of modifiers which can be used in shortcuts // (meta, alt, ctrl, shift) // g_keyModMaskXOnOrOff // mask of modifiers where we don't care whether they are on or off // (caps lock, num lock, scroll lock) static uint g_keyModMaskXAccel = 0; static uint g_keyModMaskXOnOrOff = 0; static void calculateGrabMasks() { g_keyModMaskXAccel = KKeyServer::accelModMaskX(); g_keyModMaskXOnOrOff = KKeyServer::modXLock() | KKeyServer::modXNumLock() | KKeyServer::modXScrollLock() | KKeyServer::modXModeSwitch(); //qCDebug(KGLOBALACCELD) << "g_keyModMaskXAccel = " << g_keyModMaskXAccel // << "g_keyModMaskXOnOrOff = " << g_keyModMaskXOnOrOff << endl; } //---------------------------------------------------- KGlobalAccelImpl::KGlobalAccelImpl(QObject *parent) : KGlobalAccelInterface(parent) , m_keySymbols(nullptr) { calculateGrabMasks(); if (QX11Info::isPlatformX11()) { m_keySymbols = xcb_key_symbols_alloc(QX11Info::connection()); } } KGlobalAccelImpl::~KGlobalAccelImpl() { if (m_keySymbols) { xcb_key_symbols_free(m_keySymbols); } } bool KGlobalAccelImpl::grabKey( int keyQt, bool grab ) { //grabKey is called during shutdown //shutdown might be due to the X server being killed //if so, fail immediately before trying to make other xcb calls if (!QX11Info::connection() || xcb_connection_has_error(QX11Info::connection())) { return false; } if (!m_keySymbols) { return false; } if( !keyQt ) { qCDebug(KGLOBALACCELD) << "Tried to grab key with null code."; return false; } uint keyModX; xcb_keysym_t keySymX; // Resolve the modifier if( !KKeyServer::keyQtToModX(keyQt, &keyModX) ) { qCDebug(KGLOBALACCELD) << "keyQt (0x" << hex << keyQt << ") failed to resolve to x11 modifier"; return false; } // Resolve the X symbol if( !KKeyServer::keyQtToSymX(keyQt, (int *)&keySymX) ) { qCDebug(KGLOBALACCELD) << "keyQt (0x" << hex << keyQt << ") failed to resolve to x11 keycode"; return false; } xcb_keycode_t *keyCodes = xcb_key_symbols_get_keycode(m_keySymbols, keySymX); if (!keyCodes) { return false; } int i = 0; bool success = !grab; while (keyCodes[i] != XCB_NO_SYMBOL) { xcb_keycode_t keyCodeX = keyCodes[i++]; // Check if shift needs to be added to the grab since KKeySequenceWidget // can remove shift for some keys. (all the %&* and such) if( !(keyQt & Qt::SHIFT) && !KKeyServer::isShiftAsModifierAllowed( keyQt ) && + !(keyQt & Qt::KeypadModifier) && keySymX != xcb_key_symbols_get_keysym(m_keySymbols, keyCodeX, 0) && keySymX == xcb_key_symbols_get_keysym(m_keySymbols, keyCodeX, 1) ) { qCDebug(KGLOBALACCELD) << "adding shift to the grab"; keyModX |= KKeyServer::modXShift(); } keyModX &= g_keyModMaskXAccel; // Get rid of any non-relevant bits in mod if( !keyCodeX ) { qCDebug(KGLOBALACCELD) << "keyQt (0x" << hex << keyQt << ") was resolved to x11 keycode 0"; continue; } // We'll have to grab 8 key modifier combinations in order to cover all // combinations of CapsLock, NumLock, ScrollLock. // Does anyone with more X-savvy know how to set a mask on QX11Info::appRootWindow so that // the irrelevant bits are always ignored and we can just make one XGrabKey // call per accelerator? -- ellis #ifndef NDEBUG QString sDebug = QString("\tcode: 0x%1 state: 0x%2 | ").arg(keyCodeX,0,16).arg(keyModX,0,16); #endif uint keyModMaskX = ~g_keyModMaskXOnOrOff; QVector cookies; for( uint irrelevantBitsMask = 0; irrelevantBitsMask <= 0xff; irrelevantBitsMask++ ) { if( (irrelevantBitsMask & keyModMaskX) == 0 ) { #ifndef NDEBUG sDebug += QString("0x%3, ").arg(irrelevantBitsMask, 0, 16); #endif if( grab ) cookies << xcb_grab_key_checked(QX11Info::connection(), true, QX11Info::appRootWindow(), keyModX | irrelevantBitsMask, keyCodeX, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_SYNC); else cookies << xcb_ungrab_key_checked(QX11Info::connection(), keyCodeX, QX11Info::appRootWindow(), keyModX | irrelevantBitsMask); } } bool failed = false; if( grab ) { for (int i = 0; i < cookies.size(); ++i) { QScopedPointer error(xcb_request_check(QX11Info::connection(), cookies.at(i))); if (!error.isNull()) { failed = true; } } if( failed ) { qCDebug(KGLOBALACCELD) << "grab failed!\n"; for( uint m = 0; m <= 0xff; m++ ) { if(( m & keyModMaskX ) == 0 ) xcb_ungrab_key(QX11Info::connection(), keyCodeX, QX11Info::appRootWindow(), keyModX | m); } } else { success = true; } } } free(keyCodes); return success; } bool KGlobalAccelImpl::nativeEventFilter(const QByteArray &eventType, void *message, long *) { if (eventType != "xcb_generic_event_t") { return false; } xcb_generic_event_t *event = reinterpret_cast(message); const uint8_t responseType = event->response_type & ~0x80; switch (responseType) { case XCB_MAPPING_NOTIFY: qCDebug(KGLOBALACCELD) << "Got XMappingNotify event"; xcb_refresh_keyboard_mapping(m_keySymbols, reinterpret_cast(event)); x11MappingNotify(); return true; case XCB_KEY_PRESS: #ifdef KDEDGLOBALACCEL_TRACE qCDebug(KGLOBALACCELD) << "Got XKeyPress event"; #endif return x11KeyPress(reinterpret_cast(event)); default: // We get all XEvents. Just ignore them. return false; } Q_ASSERT(false); return false; } void KGlobalAccelImpl::x11MappingNotify() { // Maybe the X modifier map has been changed. // uint oldKeyModMaskXAccel = g_keyModMaskXAccel; // uint oldKeyModMaskXOnOrOff = g_keyModMaskXOnOrOff; // First ungrab all currently grabbed keys. This is needed because we // store the keys as qt keycodes and use KKeyServer to map them to x11 key // codes. After calling KKeyServer::initializeMods() they could map to // different keycodes. ungrabKeys(); KKeyServer::initializeMods(); calculateGrabMasks(); grabKeys(); } bool KGlobalAccelImpl::x11KeyPress(xcb_key_press_event_t *pEvent) { if (QWidget::keyboardGrabber() || QApplication::activePopupWidget()) { qCWarning(KGLOBALACCELD) << "kglobalacceld should be popup and keyboard grabbing free!"; } // Keyboard needs to be ungrabed after XGrabKey() activates the grab, // otherwise it becomes frozen. xcb_connection_t *c = QX11Info::connection(); xcb_ungrab_keyboard(c, XCB_TIME_CURRENT_TIME); xcb_flush(c); - xcb_keycode_t keyCodeX = pEvent->detail; - uint16_t keyModX = pEvent->state & (g_keyModMaskXAccel | KKeyServer::MODE_SWITCH); - - xcb_keysym_t keySymX = xcb_key_press_lookup_keysym(m_keySymbols, pEvent, 0); - - // If numlock is active and a keypad key is pressed, XOR the SHIFT state. - // e.g., KP_4 => Shift+KP_Left, and Shift+KP_4 => KP_Left. - if (pEvent->state & KKeyServer::modXNumLock()) { - // If this is a keypad key, - if( keySymX >= XK_KP_Space && keySymX <= XK_KP_9 ) { - switch( keySymX ) { - - // Leave the following keys unaltered - // FIXME: The proper solution is to see which keysyms don't change when shifted. - case XK_KP_Multiply: - case XK_KP_Add: - case XK_KP_Subtract: - case XK_KP_Divide: - case XK_KP_Enter: - break; - - default: - keyModX ^= KKeyServer::modXShift(); - } - } - } - - int keyCodeQt; - int keyModQt; - KKeyServer::symXToKeyQt(keySymX, &keyCodeQt); - KKeyServer::modXToQt(keyModX, &keyModQt); - - if ((keyModQt & Qt::SHIFT) && !KKeyServer::isShiftAsModifierAllowed( keyCodeQt ) ) { -#ifdef KDEDGLOBALACCEL_TRACE - qCDebug(KGLOBALACCELD) << "removing shift modifier"; -#endif - if (keyCodeQt != Qt::Key_Tab) { // KKeySequenceWidget does not map shift+tab to backtab - static const int FirstLevelShift = 1; - keySymX = xcb_key_symbols_get_keysym(m_keySymbols, keyCodeX, FirstLevelShift); - KKeyServer::symXToKeyQt(keySymX, &keyCodeQt); - } - keyModQt &= ~Qt::SHIFT; - } - - int keyQt = keyCodeQt | keyModQt; + int keyQt; + if (!KKeyServer::xcbKeyPressEventToQt(pEvent, &keyQt)) { + qCWarning(KGLOBALACCELD) << "KKeyServer::xcbKeyPressEventToQt failed"; + return false; + } + //qDebug() << "keyQt=" << QString::number(keyQt, 16); // All that work for this hey... argh... if (NET::timestampCompare(pEvent->time, QX11Info::appTime()) > 0) { QX11Info::setAppTime(pEvent->time); } return keyPressed(keyQt); } void KGlobalAccelImpl::setEnabled( bool enable ) { if (enable && qApp->platformName() == QLatin1String("xcb")) { qApp->installNativeEventFilter(this); } else { qApp->removeNativeEventFilter(this); } } void KGlobalAccelImpl::syncX() { xcb_connection_t *c = QX11Info::connection(); auto *value = xcb_get_input_focus_reply(c, xcb_get_input_focus_unchecked(c), nullptr); free(value); }