aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-10-05 16:39:31 +0800
committerYuqian Yang <crupest@crupest.life>2025-10-05 16:39:31 +0800
commitf502391ceb2fc7fd441718d2152ef14e2ec24973 (patch)
treeb25b50c7eaaf3309723858098ea113a959eaa407
parent047e0eba34c669ff71535602c09edec587912059 (diff)
downloadcru-f502391ceb2fc7fd441718d2152ef14e2ec24973.tar.gz
cru-f502391ceb2fc7fd441718d2152ef14e2ec24973.tar.bz2
cru-f502391ceb2fc7fd441718d2152ef14e2ec24973.zip
Use xkb.
-rw-r--r--include/cru/platform/gui/xcb/InputMethod.h5
-rw-r--r--include/cru/platform/gui/xcb/Keyboard.h28
-rw-r--r--include/cru/platform/gui/xcb/UiApplication.h3
-rw-r--r--src/platform/gui/xcb/CMakeLists.txt4
-rw-r--r--src/platform/gui/xcb/InputMethod.cpp16
-rw-r--r--src/platform/gui/xcb/Keyboard.cpp91
-rw-r--r--src/platform/gui/xcb/UiApplication.cpp7
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();