From dfeb19b0a83b8ce57d28bf94a4f8d129993d1064 Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Mon, 12 Jan 2026 23:06:20 +0100 Subject: Initial commit --- src/xcb_xkb.cc | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 src/xcb_xkb.cc (limited to 'src/xcb_xkb.cc') diff --git a/src/xcb_xkb.cc b/src/xcb_xkb.cc new file mode 100644 index 0000000..3d00537 --- /dev/null +++ b/src/xcb_xkb.cc @@ -0,0 +1,167 @@ +#include "xcb_xkb.hh" + +#include +#include +#include +#include +#include +#define explicit dont_use_cxx_explicit +#include +#undef explicit +#include +#include +#include +#include + +namespace xcb { + +namespace { + +struct KeymapDeleter { + void operator()(xkb_keymap* keymap) const { + if (keymap) + xkb_keymap_unref(keymap); + } +}; + +struct StateDeleter { + void operator()(xkb_state* state) const { + if (state) + xkb_state_unref(state); + } +}; + +struct ContextDeleter { + void operator()(xkb_context* context) const { + if (context) + xkb_context_unref(context); + } +}; + +class KeyboardImpl : public Keyboard { + public: + KeyboardImpl() = default; + + bool init(xcb_connection_t* conn) { + if (!xkb_x11_setup_xkb_extension( + conn, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nullptr, nullptr, + &first_xkb_event_, nullptr)) + return false; + + ctx_.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); + if (!ctx_) + return false; + device_id_ = xkb_x11_get_core_keyboard_device_id(conn); + if (device_id_ == -1) + return false; + if (!update_keymap(conn)) + return false; + select_events(conn); + return true; + } + + bool handle_event(xcb_connection_t* conn, + xcb_generic_event_t* event) override { + if (XCB_EVENT_RESPONSE_TYPE(event) == first_xkb_event_) { + auto* xkb_event = reinterpret_cast(event); + if (std::cmp_equal(xkb_event->deviceID, device_id_)) { + switch (xkb_event->xkbType) { + case XCB_XKB_NEW_KEYBOARD_NOTIFY: { + auto* e = + reinterpret_cast(event); + if (e->changed & XCB_XKB_NKN_DETAIL_KEYCODES) + update_keymap(conn); + break; + } + case XCB_XKB_MAP_NOTIFY: + update_keymap(conn); + break; + case XCB_XKB_STATE_NOTIFY: { + auto* e = reinterpret_cast(event); + xkb_state_update_mask(state_.get(), e->baseMods, e->latchedMods, + e->lockedMods, e->baseGroup, e->latchedGroup, + e->lockedGroup); + break; + } + default: + break; + } + } + return true; + } + return false; + } + + std::string get_utf8(xcb_key_press_event_t* event) override { + char tmp[16]; + xkb_state_key_get_utf8(state_.get(), event->detail, tmp, sizeof(tmp)); + return {tmp}; + } + + private: + struct xkb_generic_event_t { + uint8_t response_type; + uint8_t xkbType; + uint16_t sequence; + xcb_timestamp_t time; + uint8_t deviceID; + }; + + bool update_keymap(xcb_connection_t* conn) { + auto* keymap = xkb_x11_keymap_new_from_device(ctx_.get(), conn, device_id_, + XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!keymap) + return false; + auto* state = xkb_x11_state_new_from_device(keymap, conn, device_id_); + if (!state) { + xkb_keymap_unref(keymap); + return false; + } + keymap_.reset(keymap); + state_.reset(state); + return true; + } + + void select_events(xcb_connection_t* conn) const { + static const uint16_t new_keyboard_details = XCB_XKB_NKN_DETAIL_KEYCODES; + static const uint16_t map_parts = + XCB_XKB_MAP_PART_KEY_TYPES | XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP; + static const uint16_t state_details = + XCB_XKB_STATE_PART_MODIFIER_BASE | XCB_XKB_STATE_PART_MODIFIER_LATCH | + XCB_XKB_STATE_PART_MODIFIER_LOCK | XCB_XKB_STATE_PART_GROUP_BASE | + XCB_XKB_STATE_PART_GROUP_LATCH | XCB_XKB_STATE_PART_GROUP_LOCK; + + xcb_xkb_select_events_details_t details = {}; + details.affectNewKeyboard = new_keyboard_details; + details.newKeyboardDetails = new_keyboard_details; + details.affectState = state_details; + details.stateDetails = state_details; + + xcb_xkb_select_events_aux(conn, device_id_, + XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | + XCB_XKB_EVENT_TYPE_MAP_NOTIFY | + XCB_XKB_EVENT_TYPE_STATE_NOTIFY, + 0, 0, map_parts, map_parts, &details); + } + + std::unique_ptr ctx_; + std::unique_ptr keymap_; + std::unique_ptr state_; + uint8_t first_xkb_event_; + int32_t device_id_; +}; + +} // namespace + +std::unique_ptr Keyboard::create(xcb_connection_t* conn) { + auto ret = std::make_unique(); + if (ret->init(conn)) + return ret; + return nullptr; +} + +} // namespace xcb -- cgit v1.2.3-70-g09d2