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