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"