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