diff options
-rw-r--r-- | include/cru/platform/gui/xcb/InputMethod.h | 5 | ||||
-rw-r--r-- | include/cru/platform/gui/xcb/Keyboard.h | 28 | ||||
-rw-r--r-- | include/cru/platform/gui/xcb/UiApplication.h | 3 | ||||
-rw-r--r-- | src/platform/gui/xcb/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/platform/gui/xcb/InputMethod.cpp | 16 | ||||
-rw-r--r-- | src/platform/gui/xcb/Keyboard.cpp | 91 | ||||
-rw-r--r-- | src/platform/gui/xcb/UiApplication.cpp | 7 |
7 files changed, 121 insertions, 33 deletions
diff --git a/include/cru/platform/gui/xcb/InputMethod.h b/include/cru/platform/gui/xcb/InputMethod.h index a325c1da..0c928b24 100644 --- a/include/cru/platform/gui/xcb/InputMethod.h +++ b/include/cru/platform/gui/xcb/InputMethod.h @@ -1,8 +1,8 @@ #pragma once +#include <cru/base/Base.h> #include "../InputMethod.h" #include "Base.h" -#include <cru/base/Base.h> #include <xcb-imdkit/imclient.h> #include <xcb/xcb.h> @@ -28,10 +28,13 @@ class XcbXimInputMethodManager : public XcbResource { void DispatchComposition(xcb_xim_t* im, xcb_xic_t ic, CompositionText text); bool HandleXEvent(xcb_generic_event_t* event); + void SetXimServerUnprocessedXEventCallback( + std::function<void(xcb_key_press_event_t* event)> callback); private: XcbUiApplication* application_; xcb_xim_t* im_; + std::function<void(xcb_key_press_event_t* event)> forward_event_callback_; }; class XcbXimInputMethodContext : public XcbResource, diff --git a/include/cru/platform/gui/xcb/Keyboard.h b/include/cru/platform/gui/xcb/Keyboard.h index a3e66e2b..e1fc7fec 100644 --- a/include/cru/platform/gui/xcb/Keyboard.h +++ b/include/cru/platform/gui/xcb/Keyboard.h @@ -3,15 +3,33 @@ #include <cru/platform/gui/Keyboard.h> #include <xcb/xcb.h> +#include <xkbcommon/xkbcommon.h> #include <unordered_map> namespace cru::platform::gui::xcb { struct XcbUiApplication; -KeyCode XorgKeysymToKeyCode(xcb_keysym_t keysym); -std::vector<xcb_keysym_t> XorgKeycodeToKeysyms(XcbUiApplication* application, xcb_keycode_t keycode); -KeyCode XorgKeycodeToCruKeyCode(XcbUiApplication* application, xcb_keycode_t keycode); -std::string XorgKeysymToUtf8(xcb_keysym_t keysym, bool upper = false); -std::unordered_map<KeyCode, bool> GetKeyboardState(XcbUiApplication* application); +KeyCode XorgKeysymToCruKeyCode(xcb_keysym_t keysym); +std::vector<xcb_keysym_t> XorgKeycodeToKeysyms(XcbUiApplication* application, + xcb_keycode_t keycode); +KeyCode XorgKeycodeToCruKeyCode(XcbUiApplication* application, + xcb_keycode_t keycode); +std::unordered_map<KeyCode, bool> GetKeyboardState( + XcbUiApplication* application); KeyModifier GetCurrentKeyModifiers(XcbUiApplication* application); + +class XcbKeyboardManager { + public: + explicit XcbKeyboardManager(XcbUiApplication* application); + ~XcbKeyboardManager(); + + std::string KeysymToUtf8(xcb_keysym_t keysym); + std::string KeycodeToUtf8(xcb_keycode_t keycode); + + private: + XcbUiApplication* application_; + xkb_context* xkb_context_; + xkb_keymap* xkb_keymap_; + xkb_state* xkb_state_; +}; } // namespace cru::platform::gui::xcb diff --git a/include/cru/platform/gui/xcb/UiApplication.h b/include/cru/platform/gui/xcb/UiApplication.h index d6971099..72dd542a 100644 --- a/include/cru/platform/gui/xcb/UiApplication.h +++ b/include/cru/platform/gui/xcb/UiApplication.h @@ -14,6 +14,7 @@ namespace cru::platform::gui::xcb { class XcbWindow; class XcbCursorManager; class XcbXimInputMethodManager; +class XcbKeyboardManager; class XcbUiApplication : public XcbResource, public virtual IUiApplication { friend XcbWindow; @@ -51,6 +52,7 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { #undef CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM XcbXimInputMethodManager* GetXcbXimInputMethodManager(); + XcbKeyboardManager* GetXcbKeyboardManager(); public: int Run() override; @@ -104,5 +106,6 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { XcbCursorManager* cursor_manager_; XcbXimInputMethodManager* input_method_manager_; + XcbKeyboardManager* keyboard_manager_; }; } // namespace cru::platform::gui::xcb diff --git a/src/platform/gui/xcb/CMakeLists.txt b/src/platform/gui/xcb/CMakeLists.txt index 5adeb923..9ae1019d 100644 --- a/src/platform/gui/xcb/CMakeLists.txt +++ b/src/platform/gui/xcb/CMakeLists.txt @@ -2,6 +2,7 @@ find_library(LIBRARY_XCB xcb REQUIRED) find_library(LIBRARY_XCB_CURSOR xcb-cursor REQUIRED) find_library(LIBRARY_XCB_IMDKIT xcb-imdkit REQUIRED) find_library(LIBRARY_XKBCOMMON xkbcommon REQUIRED) +find_library(LIBRARY_XKBCOMMON_X11 xkbcommon-x11 REQUIRED) add_library(CruPlatformGuiXcb Cursor.cpp InputMethod.cpp @@ -11,5 +12,6 @@ add_library(CruPlatformGuiXcb ) target_link_libraries(CruPlatformGuiXcb PUBLIC CruPlatformGui CruPlatformGraphicsCairo - ${LIBRARY_XCB} ${LIBRARY_XCB_CURSOR} ${LIBRARY_XCB_IMDKIT} ${LIBRARY_XCB_XKBCOMMON} + "${LIBRARY_XCB}" "${LIBRARY_XCB_CURSOR}" "${LIBRARY_XCB_IMDKIT}" + "${LIBRARY_XKBCOMMON}" "${LIBRARY_XKBCOMMON_X11}" ) diff --git a/src/platform/gui/xcb/InputMethod.cpp b/src/platform/gui/xcb/InputMethod.cpp index 61753bf3..90bc8f66 100644 --- a/src/platform/gui/xcb/InputMethod.cpp +++ b/src/platform/gui/xcb/InputMethod.cpp @@ -28,8 +28,11 @@ XcbXimInputMethodManager::XcbXimInputMethodManager( if ((event->response_type & ~0x80) == XCB_KEY_PRESS) { manager->DispatchCommit( im, ic, - XorgKeysymToUtf8(XorgKeycodeToKeysyms(manager->application_, - event->detail)[0])); + manager->application_->GetXcbKeyboardManager()->KeycodeToUtf8( + event->detail)); + } else { + if (manager->forward_event_callback_) + manager->forward_event_callback_(event); } }, .commit_string = @@ -41,7 +44,8 @@ XcbXimInputMethodManager::XcbXimInputMethodManager( if (flag & XCB_XIM_LOOKUP_KEYSYM) { std::string text; for (int i = 0; i < nKeySym; i++) { - text += XorgKeysymToUtf8(keysym[i]); + text += manager->application_->GetXcbKeyboardManager() + ->KeysymToUtf8(keysym[i]); } manager->DispatchCommit(im, ic, std::move(text)); } @@ -113,11 +117,17 @@ bool XcbXimInputMethodManager::HandleXEvent(xcb_generic_event_t *event) { if (context->ic_ && (((event->response_type & ~0x80) == XCB_KEY_PRESS) || ((event->response_type & ~0x80) == XCB_KEY_RELEASE))) { xcb_xim_forward_event(im_, *context->ic_, (xcb_key_press_event_t *)event); + return true; } } return false; } +void XcbXimInputMethodManager::SetXimServerUnprocessedXEventCallback( + std::function<void(xcb_key_press_event_t *event)> callback) { + forward_event_callback_ = std::move(callback); +} + XcbXimInputMethodContext::XcbXimInputMethodContext( XcbXimInputMethodManager *manager, XcbWindow *window) : manager_(manager), window_(window), enabled_(false) { diff --git a/src/platform/gui/xcb/Keyboard.cpp b/src/platform/gui/xcb/Keyboard.cpp index f49cac51..2e02bcd1 100644 --- a/src/platform/gui/xcb/Keyboard.cpp +++ b/src/platform/gui/xcb/Keyboard.cpp @@ -1,13 +1,13 @@ #include "cru/platform/gui/xcb/Keyboard.h" +#include "cru/base/Exception.h" #include "cru/base/Guard.h" -#include "cru/base/StringUtil.h" #include "cru/platform/gui/Keyboard.h" #include "cru/platform/gui/xcb/UiApplication.h" #include <xcb/xcb.h> -#include <xcb/xproto.h> +#include <xkbcommon/xkbcommon-x11.h> +#include <xkbcommon/xkbcommon.h> #include <bitset> -#include <cctype> #include <climits> #include <unordered_map> #include <utility> @@ -15,7 +15,7 @@ namespace cru::platform::gui::xcb { // Refer to // https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#keysym_encoding -KeyCode XorgKeysymToKeyCode(xcb_keysym_t keysym) { +KeyCode XorgKeysymToCruKeyCode(xcb_keysym_t keysym) { if (keysym >= 'A' && keysym <= 'Z') { return KeyCode(static_cast<int>(KeyCode::A) + (keysym - 'A')); } @@ -108,30 +108,13 @@ KeyCode XorgKeycodeToCruKeyCode(XcbUiApplication *application, auto keysyms = XorgKeycodeToKeysyms(application, keycode); for (auto keysym : keysyms) { - auto result = XorgKeysymToKeyCode(keysym); + auto result = XorgKeysymToCruKeyCode(keysym); if (result != KeyCode::Unknown) return result; } return KeyCode::Unknown; } -std::string XorgKeysymToUtf8(xcb_keysym_t keysym, bool upper) { - if (0x20 <= keysym && keysym <= 0x7e || 0xa0 <= keysym && keysym <= 0xff) { - return std::string{ - static_cast<char>(upper ? std::toupper(keysym) : keysym)}; - } - - if (0x01000100 <= keysym && keysym <= 0x0110FFFF) { - auto code_point = keysym - 0x01000000; - std::string result; - Utf8EncodeCodePointAppend(keysym, - [&result](char c) { result.push_back(c); }); - return result; - } - - return {}; -} - namespace { using KeymapBitset = std::bitset<sizeof(std::declval<xcb_query_keymap_reply_t>().keys) * @@ -186,7 +169,7 @@ std::unordered_map<KeyCode, bool> GetKeyboardState( for (xcb_keycode_t i = min_keycode; i <= max_keycode; i++) { auto keysyms_for_this = keysyms + (i - min_keycode) * keysyms_per_keycode; for (int j = 0; j < keysyms_per_keycode; j++) { - auto keycode = XorgKeysymToKeyCode(keysyms_for_this[j]); + auto keycode = XorgKeysymToCruKeyCode(keysyms_for_this[j]); if (keycode != KeyCode::Unknown) { result[keycode] = keymap[i]; } @@ -211,4 +194,66 @@ KeyModifier GetCurrentKeyModifiers(XcbUiApplication *application) { } return result; } + +XcbKeyboardManager::XcbKeyboardManager(XcbUiApplication *application) + : application_(application) { + xkb_x11_setup_xkb_extension( + application->GetXcbConnection(), XKB_X11_MIN_MAJOR_XKB_VERSION, + XKB_X11_MIN_MINOR_XKB_VERSION, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, + nullptr, nullptr, nullptr, nullptr); + + xkb_context_ = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!xkb_context_) { + throw PlatformException("Failed to call xkb_context_new."); + } + + auto device_id = + xkb_x11_get_core_keyboard_device_id(application->GetXcbConnection()); + if (device_id == -1) { + throw PlatformException( + "Failed to call xkb_x11_get_core_keyboard_device_id."); + } + + xkb_keymap_ = xkb_x11_keymap_new_from_device( + xkb_context_, application->GetXcbConnection(), device_id, + XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!xkb_keymap_) { + throw PlatformException("Failed to call xkb_x11_keymap_new_from_device."); + } + + xkb_state_ = xkb_x11_state_new_from_device( + xkb_keymap_, application->GetXcbConnection(), device_id); + if (!xkb_state_) { + throw PlatformException("Failed to call xkb_x11_state_new_from_device."); + } +} + +XcbKeyboardManager::~XcbKeyboardManager() { + xkb_state_unref(xkb_state_); + xkb_keymap_unref(xkb_keymap_); + xkb_context_unref(xkb_context_); +} + +std::string XcbKeyboardManager::KeycodeToUtf8(xcb_keycode_t keycode) { + auto size = xkb_state_key_get_utf8(xkb_state_, keycode, NULL, 0); + if (size <= 0) { + return {}; + } + std::string buffer(size + 1, 0); + xkb_state_key_get_utf8(xkb_state_, keycode, buffer.data(), size + 1); + buffer.resize(size); + return buffer; +} + +std::string XcbKeyboardManager::KeysymToUtf8(xcb_keysym_t keysym) { + auto size = xkb_keysym_to_utf8(keysym, NULL, 0); + if (size <= 0) { + return {}; + } + std::string buffer(size + 1, 0); + xkb_keysym_to_utf8(keysym, buffer.data(), size + 1); + buffer.resize(size); + return buffer; +} + } // namespace cru::platform::gui::xcb diff --git a/src/platform/gui/xcb/UiApplication.cpp b/src/platform/gui/xcb/UiApplication.cpp index 03e39985..a943fe1f 100644 --- a/src/platform/gui/xcb/UiApplication.cpp +++ b/src/platform/gui/xcb/UiApplication.cpp @@ -6,6 +6,7 @@ #include "cru/platform/gui/Window.h" #include "cru/platform/gui/xcb/Cursor.h" #include "cru/platform/gui/xcb/InputMethod.h" +#include "cru/platform/gui/xcb/Keyboard.h" #include "cru/platform/gui/xcb/Window.h" #include <poll.h> @@ -38,9 +39,11 @@ XcbUiApplication::XcbUiApplication( cursor_manager_ = new XcbCursorManager(this); input_method_manager_ = new XcbXimInputMethodManager(this); + keyboard_manager_ = new XcbKeyboardManager(this); } XcbUiApplication::~XcbUiApplication() { + delete keyboard_manager_; delete input_method_manager_; delete cursor_manager_; @@ -87,6 +90,10 @@ XcbXimInputMethodManager *XcbUiApplication::GetXcbXimInputMethodManager() { return input_method_manager_; } +XcbKeyboardManager *XcbUiApplication::GetXcbKeyboardManager() { + return keyboard_manager_; +} + int XcbUiApplication::Run() { auto exit_code = event_loop_.Run(); |