File indexing completed on 2024-11-10 04:56:36
0001 /* 0002 SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "x11_standalone_keyboard.h" 0008 0009 #include "main.h" 0010 0011 #include <QDebug> 0012 0013 #define explicit dont_use_cxx_explicit 0014 #include <xcb/xkb.h> 0015 #undef explicit 0016 #include <xkbcommon/xkbcommon-x11.h> 0017 0018 namespace KWin 0019 { 0020 0021 class X11KeyboardFilter : public X11EventFilter 0022 { 0023 public: 0024 X11KeyboardFilter(X11Keyboard *kbd, int eventType) 0025 : X11EventFilter(eventType) 0026 , m_kbd(kbd) 0027 { 0028 } 0029 0030 bool event(xcb_generic_event_t *event) override 0031 { 0032 return m_kbd->event(event); 0033 } 0034 0035 private: 0036 X11Keyboard *m_kbd; 0037 }; 0038 0039 X11Keyboard::X11Keyboard() 0040 : m_xkbContext(xkb_context_new(XKB_CONTEXT_NO_FLAGS)) 0041 { 0042 const xcb_query_extension_reply_t *reply = xcb_get_extension_data(kwinApp()->x11Connection(), &xcb_xkb_id); 0043 if (!reply || !reply->present) { 0044 qWarning() << "XKeyboard extension is unavailable"; 0045 return; 0046 } 0047 0048 m_deviceId = xkb_x11_get_core_keyboard_device_id(kwinApp()->x11Connection()); 0049 if (m_deviceId == -1) { 0050 qWarning() << "xkb_x11_get_core_keyboard_device_id() failed"; 0051 return; 0052 } 0053 0054 enum { 0055 requiredEvents = 0056 (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY 0057 | XCB_XKB_EVENT_TYPE_MAP_NOTIFY 0058 | XCB_XKB_EVENT_TYPE_STATE_NOTIFY), 0059 0060 requiredNknDetails = 0061 (XCB_XKB_NKN_DETAIL_KEYCODES), 0062 0063 requiredMapParts = 0064 (XCB_XKB_MAP_PART_KEY_TYPES 0065 | XCB_XKB_MAP_PART_KEY_SYMS 0066 | XCB_XKB_MAP_PART_MODIFIER_MAP 0067 | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS 0068 | XCB_XKB_MAP_PART_KEY_ACTIONS 0069 | XCB_XKB_MAP_PART_VIRTUAL_MODS 0070 | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP), 0071 0072 requiredStateDetails = 0073 (XCB_XKB_STATE_PART_MODIFIER_BASE 0074 | XCB_XKB_STATE_PART_MODIFIER_LATCH 0075 | XCB_XKB_STATE_PART_MODIFIER_LOCK 0076 | XCB_XKB_STATE_PART_GROUP_BASE 0077 | XCB_XKB_STATE_PART_GROUP_LATCH 0078 | XCB_XKB_STATE_PART_GROUP_LOCK), 0079 }; 0080 0081 static const xcb_xkb_select_events_details_t details = { 0082 .affectNewKeyboard = requiredNknDetails, 0083 .newKeyboardDetails = requiredNknDetails, 0084 .affectState = requiredStateDetails, 0085 .stateDetails = requiredStateDetails, 0086 }; 0087 0088 xcb_void_cookie_t cookie = 0089 xcb_xkb_select_events_aux_checked(kwinApp()->x11Connection(), 0090 m_deviceId, 0091 requiredEvents, 0092 0, 0093 0, 0094 requiredMapParts, 0095 requiredMapParts, 0096 &details); 0097 0098 xcb_generic_error_t *error = xcb_request_check(kwinApp()->x11Connection(), cookie); 0099 if (error) { 0100 free(error); 0101 return; 0102 } 0103 0104 updateKeymap(); 0105 0106 m_filter = std::make_unique<X11KeyboardFilter>(this, reply->first_event); 0107 } 0108 0109 X11Keyboard::~X11Keyboard() 0110 { 0111 if (m_xkbState) { 0112 xkb_state_unref(m_xkbState); 0113 m_xkbState = nullptr; 0114 } 0115 if (m_xkbKeymap) { 0116 xkb_keymap_unref(m_xkbKeymap); 0117 m_xkbKeymap = nullptr; 0118 } 0119 if (m_xkbContext) { 0120 xkb_context_unref(m_xkbContext); 0121 m_xkbContext = nullptr; 0122 } 0123 } 0124 0125 bool X11Keyboard::event(xcb_generic_event_t *gevent) 0126 { 0127 union xkb_event { 0128 struct 0129 { 0130 uint8_t response_type; 0131 uint8_t xkbType; 0132 uint16_t sequence; 0133 xcb_timestamp_t time; 0134 uint8_t deviceID; 0135 } any; 0136 xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify; 0137 xcb_xkb_map_notify_event_t map_notify; 0138 xcb_xkb_state_notify_event_t state_notify; 0139 } *event = reinterpret_cast<union xkb_event *>(gevent); 0140 0141 if (event->any.deviceID == m_deviceId) { 0142 switch (event->any.xkbType) { 0143 case XCB_XKB_NEW_KEYBOARD_NOTIFY: 0144 if (event->new_keyboard_notify.changed & XCB_XKB_NKN_DETAIL_KEYCODES) { 0145 updateKeymap(); 0146 } 0147 break; 0148 0149 case XCB_XKB_MAP_NOTIFY: 0150 updateKeymap(); 0151 break; 0152 0153 case XCB_XKB_STATE_NOTIFY: 0154 xkb_state_update_mask(m_xkbState, 0155 event->state_notify.baseMods, 0156 event->state_notify.latchedMods, 0157 event->state_notify.lockedMods, 0158 event->state_notify.baseGroup, 0159 event->state_notify.latchedGroup, 0160 event->state_notify.lockedGroup); 0161 break; 0162 } 0163 } 0164 0165 return false; 0166 } 0167 0168 void X11Keyboard::updateKeymap() 0169 { 0170 xkb_keymap *keymap = xkb_x11_keymap_new_from_device(m_xkbContext, kwinApp()->x11Connection(), m_deviceId, XKB_KEYMAP_COMPILE_NO_FLAGS); 0171 if (!keymap) { 0172 qWarning() << "xkb_x11_keymap_new_from_device() failed"; 0173 return; 0174 } 0175 0176 xkb_state *state = xkb_x11_state_new_from_device(keymap, kwinApp()->x11Connection(), m_deviceId); 0177 if (!state) { 0178 xkb_keymap_unref(keymap); 0179 qWarning() << "xkb_x11_state_new_from_device() failed"; 0180 return; 0181 } 0182 0183 if (m_xkbState) { 0184 xkb_state_unref(m_xkbState); 0185 } 0186 if (m_xkbKeymap) { 0187 xkb_keymap_unref(m_xkbKeymap); 0188 } 0189 0190 m_xkbState = state; 0191 m_xkbKeymap = keymap; 0192 } 0193 0194 xkb_keymap *X11Keyboard::xkbKeymap() const 0195 { 0196 return m_xkbKeymap; 0197 } 0198 0199 xkb_state *X11Keyboard::xkbState() const 0200 { 0201 return m_xkbState; 0202 } 0203 0204 Qt::KeyboardModifiers X11Keyboard::modifiers() const 0205 { 0206 Qt::KeyboardModifiers mods; 0207 0208 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) == 1 || xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_EFFECTIVE) == 1) { 0209 mods |= Qt::ShiftModifier; 0210 } 0211 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) == 1) { 0212 mods |= Qt::AltModifier; 0213 } 0214 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) == 1) { 0215 mods |= Qt::ControlModifier; 0216 } 0217 if (xkb_state_mod_name_is_active(m_xkbState, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) == 1) { 0218 mods |= Qt::MetaModifier; 0219 } 0220 0221 return mods; 0222 } 0223 0224 } // namespace KWin