File indexing completed on 2024-12-08 06:39:07

0001 /*
0002     SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
0003     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "kmodifierkeyinfoprovider_xcb.h"
0009 #include "kmodifierkeyinfo.h"
0010 
0011 #include <QGuiApplication>
0012 
0013 #define XK_MISCELLANY
0014 #define XK_XKB_KEYS
0015 #include <X11/XKBlib.h>
0016 #include <X11/keysymdef.h>
0017 #include <xcb/xcb.h>
0018 
0019 struct ModifierDefinition {
0020     ModifierDefinition(Qt::Key _key, unsigned int _mask, const char *_name, KeySym _keysym)
0021         : key(_key)
0022         , mask(_mask)
0023         , name(_name)
0024         , keysym(_keysym)
0025     {
0026     }
0027     Qt::Key key;
0028     unsigned int mask;
0029     const char *name; // virtual modifier name
0030     KeySym keysym;
0031 };
0032 
0033 /*
0034  * Get the real modifiers related to a virtual modifier.
0035  */
0036 unsigned int xkbVirtualModifier(XkbDescPtr xkb, const char *name)
0037 {
0038     Q_ASSERT(xkb != nullptr);
0039 
0040     unsigned int mask = 0;
0041     bool nameEqual;
0042     for (int i = 0; i < XkbNumVirtualMods; ++i) {
0043         char *modStr = XGetAtomName(xkb->dpy, xkb->names->vmods[i]);
0044         if (modStr != nullptr) {
0045             nameEqual = (strcmp(name, modStr) == 0);
0046             XFree(modStr);
0047             if (nameEqual) {
0048                 XkbVirtualModsToReal(xkb, 1 << i, &mask);
0049                 break;
0050             }
0051         }
0052     }
0053     return mask;
0054 }
0055 
0056 static Display *display()
0057 {
0058     return qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
0059 }
0060 
0061 KModifierKeyInfoProviderXcb::KModifierKeyInfoProviderXcb()
0062     : KModifierKeyInfoProvider()
0063     , m_xkbEv(0)
0064     , m_xkbAvailable(false)
0065 {
0066     if (qGuiApp) {
0067         if (qGuiApp->platformName() == QLatin1String("xcb")) {
0068             int code;
0069             int xkberr;
0070             int maj;
0071             int min;
0072             m_xkbAvailable = XkbQueryExtension(display(), &code, &m_xkbEv, &xkberr, &maj, &min);
0073         }
0074     }
0075     if (m_xkbAvailable) {
0076         /* clang-format off */
0077         XkbSelectEvents(display(),
0078                         XkbUseCoreKbd,
0079                         XkbStateNotifyMask | XkbMapNotifyMask,
0080                         XkbStateNotifyMask | XkbMapNotifyMask);
0081 
0082         unsigned long int stateMask = XkbModifierStateMask
0083                                       | XkbModifierBaseMask
0084                                       | XkbModifierLatchMask
0085                                       | XkbModifierLockMask
0086                                       | XkbPointerButtonMask;
0087         /* clang-format on */
0088 
0089         XkbSelectEventDetails(display(), XkbUseCoreKbd, XkbStateNotifyMask, stateMask, stateMask);
0090     }
0091 
0092     xkbUpdateModifierMapping();
0093 
0094     // add known pointer buttons
0095     m_xkbButtons.insert(Qt::LeftButton, Button1Mask);
0096     m_xkbButtons.insert(Qt::MiddleButton, Button2Mask);
0097     m_xkbButtons.insert(Qt::RightButton, Button3Mask);
0098     m_xkbButtons.insert(Qt::XButton1, Button4Mask);
0099     m_xkbButtons.insert(Qt::XButton2, Button5Mask);
0100 
0101     // get the initial state
0102     if (m_xkbAvailable) {
0103         XkbStateRec state;
0104         XkbGetState(display(), XkbUseCoreKbd, &state);
0105         xkbModifierStateChanged(state.mods, state.latched_mods, state.locked_mods);
0106         xkbButtonStateChanged(state.ptr_buttons);
0107 
0108         QCoreApplication::instance()->installNativeEventFilter(this);
0109     }
0110 }
0111 
0112 KModifierKeyInfoProviderXcb::~KModifierKeyInfoProviderXcb()
0113 {
0114     if (m_xkbAvailable) {
0115         QCoreApplication::instance()->removeNativeEventFilter(this);
0116     }
0117 }
0118 
0119 bool KModifierKeyInfoProviderXcb::setKeyLatched(Qt::Key key, bool latched)
0120 {
0121     if (!m_xkbModifiers.contains(key)) {
0122         return false;
0123     }
0124 
0125     return XkbLatchModifiers(display(), XkbUseCoreKbd, m_xkbModifiers[key], latched ? m_xkbModifiers[key] : 0);
0126 }
0127 
0128 bool KModifierKeyInfoProviderXcb::setKeyLocked(Qt::Key key, bool locked)
0129 {
0130     if (!m_xkbModifiers.contains(key)) {
0131         return false;
0132     }
0133 
0134     return XkbLockModifiers(display(), XkbUseCoreKbd, m_xkbModifiers[key], locked ? m_xkbModifiers[key] : 0);
0135 }
0136 
0137 // HACK: xcb-xkb is not yet a public part of xcb. Because of that we have to include the event structure.
0138 namespace
0139 {
0140 typedef struct _xcb_xkb_map_notify_event_t {
0141     uint8_t response_type;
0142     uint8_t xkbType;
0143     uint16_t sequence;
0144     xcb_timestamp_t time;
0145     uint8_t deviceID;
0146     uint8_t ptrBtnActions;
0147     uint16_t changed;
0148     xcb_keycode_t minKeyCode;
0149     xcb_keycode_t maxKeyCode;
0150     uint8_t firstType;
0151     uint8_t nTypes;
0152     xcb_keycode_t firstKeySym;
0153     uint8_t nKeySyms;
0154     xcb_keycode_t firstKeyAct;
0155     uint8_t nKeyActs;
0156     xcb_keycode_t firstKeyBehavior;
0157     uint8_t nKeyBehavior;
0158     xcb_keycode_t firstKeyExplicit;
0159     uint8_t nKeyExplicit;
0160     xcb_keycode_t firstModMapKey;
0161     uint8_t nModMapKeys;
0162     xcb_keycode_t firstVModMapKey;
0163     uint8_t nVModMapKeys;
0164     uint16_t virtualMods;
0165     uint8_t pad0[2];
0166 } _xcb_xkb_map_notify_event_t;
0167 typedef struct _xcb_xkb_state_notify_event_t {
0168     uint8_t response_type;
0169     uint8_t xkbType;
0170     uint16_t sequence;
0171     xcb_timestamp_t time;
0172     uint8_t deviceID;
0173     uint8_t mods;
0174     uint8_t baseMods;
0175     uint8_t latchedMods;
0176     uint8_t lockedMods;
0177     uint8_t group;
0178     int16_t baseGroup;
0179     int16_t latchedGroup;
0180     uint8_t lockedGroup;
0181     uint8_t compatState;
0182     uint8_t grabMods;
0183     uint8_t compatGrabMods;
0184     uint8_t lookupMods;
0185     uint8_t compatLoockupMods;
0186     uint16_t ptrBtnState;
0187     uint16_t changed;
0188     xcb_keycode_t keycode;
0189     uint8_t eventType;
0190     uint8_t requestMajor;
0191     uint8_t requestMinor;
0192 } _xcb_xkb_state_notify_event_t;
0193 typedef union {
0194     /* All XKB events share these fields. */
0195     struct {
0196         uint8_t response_type;
0197         uint8_t xkbType;
0198         uint16_t sequence;
0199         xcb_timestamp_t time;
0200         uint8_t deviceID;
0201     } any;
0202     _xcb_xkb_map_notify_event_t map_notify;
0203     _xcb_xkb_state_notify_event_t state_notify;
0204 } _xkb_event;
0205 }
0206 
0207 bool KModifierKeyInfoProviderXcb::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *)
0208 {
0209     if (!m_xkbAvailable || eventType != "xcb_generic_event_t") {
0210         return false;
0211     }
0212     xcb_generic_event_t *event = static_cast<xcb_generic_event_t *>(message);
0213     if ((event->response_type & ~0x80) == m_xkbEv + XkbEventCode) {
0214         _xkb_event *kbevt = reinterpret_cast<_xkb_event *>(event);
0215         unsigned int stateMask = XkbModifierStateMask | XkbModifierBaseMask | XkbModifierLatchMask | XkbModifierLockMask;
0216         if (kbevt->any.xkbType == XkbMapNotify) {
0217             xkbUpdateModifierMapping();
0218         } else if (kbevt->any.xkbType == XkbStateNotify) {
0219             if (kbevt->state_notify.changed & stateMask) {
0220                 xkbModifierStateChanged(kbevt->state_notify.mods, kbevt->state_notify.latchedMods, kbevt->state_notify.lockedMods);
0221             } else if (kbevt->state_notify.changed & XkbPointerButtonMask) {
0222                 xkbButtonStateChanged(kbevt->state_notify.ptrBtnState);
0223             }
0224         }
0225     }
0226     return false;
0227 }
0228 
0229 void KModifierKeyInfoProviderXcb::xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, unsigned char locked_mods)
0230 {
0231     // detect keyboard modifiers
0232     ModifierStates newState;
0233 
0234     QHash<Qt::Key, unsigned int>::const_iterator it;
0235     QHash<Qt::Key, unsigned int>::const_iterator end = m_xkbModifiers.constEnd();
0236     for (it = m_xkbModifiers.constBegin(); it != end; ++it) {
0237         if (!m_modifierStates.contains(it.key())) {
0238             continue;
0239         }
0240         newState = Nothing;
0241 
0242         // determine the new state
0243         if (mods & it.value()) {
0244             newState |= Pressed;
0245         }
0246         if (latched_mods & it.value()) {
0247             newState |= Latched;
0248         }
0249         if (locked_mods & it.value()) {
0250             newState |= Locked;
0251         }
0252 
0253         stateUpdated(it.key(), newState);
0254     }
0255 }
0256 
0257 void KModifierKeyInfoProviderXcb::xkbButtonStateChanged(unsigned short ptr_buttons)
0258 {
0259     // detect mouse button states
0260     bool newButtonState;
0261 
0262     QHash<Qt::MouseButton, unsigned short>::const_iterator it;
0263     QHash<Qt::MouseButton, unsigned short>::const_iterator end = m_xkbButtons.constEnd();
0264     for (it = m_xkbButtons.constBegin(); it != end; ++it) {
0265         newButtonState = (ptr_buttons & it.value());
0266         if (newButtonState != m_buttonStates[it.key()]) {
0267             m_buttonStates[it.key()] = newButtonState;
0268             Q_EMIT buttonPressed(it.key(), newButtonState);
0269         }
0270     }
0271 }
0272 
0273 void KModifierKeyInfoProviderXcb::xkbUpdateModifierMapping()
0274 {
0275     if (!m_xkbAvailable) {
0276         return;
0277     }
0278     m_xkbModifiers.clear();
0279 
0280     QList<ModifierDefinition> srcModifiers;
0281     /* clang-format off */
0282     srcModifiers << ModifierDefinition(Qt::Key_Shift, ShiftMask, nullptr, 0)
0283                  << ModifierDefinition(Qt::Key_Control, ControlMask, nullptr, 0)
0284                  << ModifierDefinition(Qt::Key_Alt, 0, "Alt", XK_Alt_L)
0285                  // << { 0, 0, I18N_NOOP("Win"), "superkey", "" }
0286                  << ModifierDefinition(Qt::Key_Meta, 0, "Meta", XK_Meta_L)
0287                  << ModifierDefinition(Qt::Key_Super_L, 0, "Super", XK_Super_L)
0288                  << ModifierDefinition(Qt::Key_Hyper_L, 0, "Hyper", XK_Hyper_L)
0289                  << ModifierDefinition(Qt::Key_AltGr, 0, "AltGr", 0)
0290                  << ModifierDefinition(Qt::Key_NumLock, 0, "NumLock", XK_Num_Lock)
0291                  << ModifierDefinition(Qt::Key_CapsLock, LockMask, nullptr, 0)
0292                  << ModifierDefinition(Qt::Key_ScrollLock, 0, "ScrollLock", XK_Scroll_Lock);
0293     /* clang-format on */
0294 
0295     XkbDescPtr xkb = XkbGetKeyboard(display(), XkbAllComponentsMask, XkbUseCoreKbd);
0296 
0297     QList<ModifierDefinition>::const_iterator it;
0298     QList<ModifierDefinition>::const_iterator end = srcModifiers.constEnd();
0299     for (it = srcModifiers.constBegin(); it != end; ++it) {
0300         unsigned int mask = it->mask;
0301         if (mask == 0 && xkb != nullptr) {
0302             // try virtual modifier first
0303             if (it->name != nullptr) {
0304                 mask = xkbVirtualModifier(xkb, it->name);
0305             }
0306             if (mask == 0 && it->keysym != 0) {
0307                 mask = XkbKeysymToModifiers(display(), it->keysym);
0308             } else if (mask == 0) {
0309                 // special case for AltGr
0310                 /* clang-format off */
0311                 mask = XkbKeysymToModifiers(display(), XK_Mode_switch)
0312                        | XkbKeysymToModifiers(display(), XK_ISO_Level3_Shift)
0313                        | XkbKeysymToModifiers(display(), XK_ISO_Level3_Latch)
0314                        | XkbKeysymToModifiers(display(), XK_ISO_Level3_Lock);
0315                 /* clang-format on */
0316             }
0317         }
0318 
0319         if (mask != 0) {
0320             m_xkbModifiers.insert(it->key, mask);
0321             // previously unknown modifier
0322             if (!m_modifierStates.contains(it->key)) {
0323                 m_modifierStates.insert(it->key, Nothing);
0324                 Q_EMIT keyAdded(it->key);
0325             }
0326         }
0327     }
0328 
0329     // remove modifiers which are no longer available
0330     QMutableHashIterator<Qt::Key, ModifierStates> i(m_modifierStates);
0331     while (i.hasNext()) {
0332         i.next();
0333         if (!m_xkbModifiers.contains(i.key())) {
0334             Qt::Key key = i.key();
0335             i.remove();
0336             Q_EMIT keyRemoved(key);
0337         }
0338     }
0339 
0340     if (xkb != nullptr) {
0341         XkbFreeKeyboard(xkb, 0, true);
0342     }
0343 }
0344 
0345 #include "moc_kmodifierkeyinfoprovider_xcb.cpp"