aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-09-30 00:16:13 +0800
committerYuqian Yang <crupest@crupest.life>2025-10-03 00:13:54 +0800
commite903083732b0d4638ac066fc85b030b121d8e7e6 (patch)
treee9a871e71705fa83242e75cca6d9431390b8f755
parent44487b61e5c2b89318f9a4a85c67df9aad7884e1 (diff)
downloadcru-e903083732b0d4638ac066fc85b030b121d8e7e6.tar.gz
cru-e903083732b0d4638ac066fc85b030b121d8e7e6.tar.bz2
cru-e903083732b0d4638ac066fc85b030b121d8e7e6.zip
Impl input method for xim 2.
-rw-r--r--include/cru/platform/gui/xcb/InputMethod.h5
-rw-r--r--include/cru/platform/gui/xcb/Keyboard.h2
-rw-r--r--include/cru/platform/gui/xcb/Window.h3
-rw-r--r--src/base/log/Logger.cpp2
-rw-r--r--src/platform/gui/xcb/InputMethod.cpp54
-rw-r--r--src/platform/gui/xcb/Keyboard.cpp35
-rw-r--r--src/platform/gui/xcb/Window.cpp3
7 files changed, 90 insertions, 14 deletions
diff --git a/include/cru/platform/gui/xcb/InputMethod.h b/include/cru/platform/gui/xcb/InputMethod.h
index 286f3158..a325c1da 100644
--- a/include/cru/platform/gui/xcb/InputMethod.h
+++ b/include/cru/platform/gui/xcb/InputMethod.h
@@ -2,6 +2,7 @@
#include "../InputMethod.h"
#include "Base.h"
+#include <cru/base/Base.h>
#include <xcb-imdkit/imclient.h>
#include <xcb/xcb.h>
@@ -12,6 +13,8 @@ class XcbUiApplication;
class XcbWindow;
class XcbXimInputMethodManager : public XcbResource {
+ CRU_DEFINE_CLASS_LOG_TAG("cru::platform::gui::xcb::XcbXimInputMethodManager")
+
friend XcbUiApplication;
public:
@@ -33,6 +36,8 @@ class XcbXimInputMethodManager : public XcbResource {
class XcbXimInputMethodContext : public XcbResource,
public virtual IInputMethodContext {
+ CRU_DEFINE_CLASS_LOG_TAG("cru::platform::gui::xcb::XcbXimInputMethodContext")
+
friend XcbXimInputMethodManager;
public:
diff --git a/include/cru/platform/gui/xcb/Keyboard.h b/include/cru/platform/gui/xcb/Keyboard.h
index 41b4abee..f3650900 100644
--- a/include/cru/platform/gui/xcb/Keyboard.h
+++ b/include/cru/platform/gui/xcb/Keyboard.h
@@ -9,7 +9,9 @@ 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);
std::unordered_map<KeyCode, bool> GetKeyboardState(XcbUiApplication* application);
KeyModifier GetCurrentKeyModifiers(XcbUiApplication* application);
} // namespace cru::platform::gui::xcb
diff --git a/include/cru/platform/gui/xcb/Window.h b/include/cru/platform/gui/xcb/Window.h
index e330ba7c..d9bef0c3 100644
--- a/include/cru/platform/gui/xcb/Window.h
+++ b/include/cru/platform/gui/xcb/Window.h
@@ -2,6 +2,7 @@
#pragma once
#include "../../GraphicsBase.h"
#include "../Window.h"
+#include "../TimerHelper.h"
#include "Base.h"
#include <cairo.h>
@@ -117,6 +118,8 @@ class XcbWindow : public XcbResource, public virtual INativeWindow {
XcbWindow* parent_;
XcbXimInputMethodContext* input_method_;
+ TimerAutoCanceler repaint_canceler_;
+
Event<std::nullptr_t> create_event_;
Event<std::nullptr_t> destroy_event_;
Event<std::nullptr_t> paint_event_;
diff --git a/src/base/log/Logger.cpp b/src/base/log/Logger.cpp
index 4332ba75..87a56cd6 100644
--- a/src/base/log/Logger.cpp
+++ b/src/base/log/Logger.cpp
@@ -90,6 +90,7 @@ void Logger::Log(LogInfo log_info) {
log_queue_.push_back(std::move(log_info));
log_queue_condition_variable_.notify_one();
}
+
void Logger::LogThreadRun() {
while (true) {
std::list<LogInfo> queue;
@@ -115,6 +116,7 @@ void Logger::LogThreadRun() {
for (auto &log_info : queue) {
target->Write(log_info.level, MakeLogFinalMessage(log_info));
}
+ queue.clear();
}
// TODO: Should still wait for queue to be cleared.
diff --git a/src/platform/gui/xcb/InputMethod.cpp b/src/platform/gui/xcb/InputMethod.cpp
index 0cd079b4..49dadbf1 100644
--- a/src/platform/gui/xcb/InputMethod.cpp
+++ b/src/platform/gui/xcb/InputMethod.cpp
@@ -1,11 +1,14 @@
#include "cru/platform/gui/xcb/InputMethod.h"
+#include "cru/base/log/Logger.h"
#include "cru/platform/Check.h"
#include "cru/platform/gui/InputMethod.h"
+#include "cru/platform/gui/xcb/Keyboard.h"
#include "cru/platform/gui/xcb/UiApplication.h"
#include "cru/platform/gui/xcb/Window.h"
#include <xcb-imdkit/encoding.h>
#include <xcb-imdkit/imclient.h>
+#include <xcb-imdkit/ximproto.h>
namespace cru::platform::gui::xcb {
namespace {
@@ -18,24 +21,47 @@ XcbXimInputMethodManager::XcbXimInputMethodManager(
auto XimOpenCallback = [](xcb_xim_t *im, void *user_data) {};
xcb_xim_im_callback kXimCallbacks = {
+ .forward_event =
+ [](xcb_xim_t *im, xcb_xic_t ic, xcb_key_press_event_t *event,
+ void *user_data) {
+ auto manager = static_cast<XcbXimInputMethodManager *>(user_data);
+ if ((event->response_type & ~0x80) == XCB_KEY_PRESS) {
+ manager->DispatchCommit(
+ im, ic,
+ XorgKeysymToUtf8(XorgKeycodeToKeysyms(manager->application_,
+ event->detail)[0]));
+ }
+ },
.commit_string =
[](xcb_xim_t *im, xcb_xic_t ic, uint32_t flag, char *str,
uint32_t length, uint32_t *keysym, size_t nKeySym,
void *user_data) {
auto manager = static_cast<XcbXimInputMethodManager *>(user_data);
- if (xcb_xim_get_encoding(im) == XCB_XIM_UTF8_STRING) {
- manager->DispatchCommit(im, ic, std::string(str, length));
+ if (flag & XCB_XIM_LOOKUP_KEYSYM) {
+ std::string text;
+ for (int i = 0; i < nKeySym; i++) {
+ text += XorgKeysymToUtf8(keysym[i]);
+ }
+ manager->DispatchCommit(im, ic, std::move(text));
+ }
- } else if (xcb_xim_get_encoding(im) == XCB_XIM_COMPOUND_TEXT) {
- size_t newLength = 0;
- char *utf8 = xcb_compound_text_to_utf8(str, length, &newLength);
- if (utf8) {
- int l = newLength;
+ if (flag & XCB_XIM_LOOKUP_CHARS) {
+ if (xcb_xim_get_encoding(im) == XCB_XIM_UTF8_STRING) {
manager->DispatchCommit(im, ic, std::string(str, length));
+ } else if (xcb_xim_get_encoding(im) == XCB_XIM_COMPOUND_TEXT) {
+ size_t newLength = 0;
+ char *utf8 = xcb_compound_text_to_utf8(str, length, &newLength);
+ if (utf8) {
+ manager->DispatchCommit(im, ic, std::string(utf8, newLength));
+ }
}
}
},
+ .preedit_start =
+ [](xcb_xim_t *im, xcb_xic_t ic, void *user_data) {
+
+ },
.preedit_draw =
[](xcb_xim_t *im, xcb_xic_t ic, xcb_im_preedit_draw_fr_t *frame,
void *user_data) {
@@ -68,11 +94,14 @@ xcb_xim_t *XcbXimInputMethodManager::GetXcbXim() { return im_; }
void XcbXimInputMethodManager::DispatchCommit(xcb_xim_t *im, xcb_xic_t ic,
std::string text) {
+ CRU_LOG_TAG_INFO("IC {} dispatch commit string: {}", ic, text);
for (auto window : application_->GetAllWindow()) {
auto input_method_context = window->GetInputMethodContext();
auto context = CheckPlatform<XcbXimInputMethodContext>(input_method_context,
GetPlatformIdUtf8());
if (context->ic_ == ic) {
+ context->composition_event_.Raise(nullptr);
+ context->composition_end_event_.Raise(nullptr);
context->text_event_.Raise(String::FromUtf8(text));
}
}
@@ -101,7 +130,6 @@ 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;
@@ -131,7 +159,7 @@ XcbXimInputMethodContext::XcbXimInputMethodContext(
XcbXimInputMethodContext::~XcbXimInputMethodContext() {}
bool XcbXimInputMethodContext::ShouldManuallyDrawCompositionText() {
- return false;
+ return true;
}
void XcbXimInputMethodContext::EnableIME() {
@@ -164,6 +192,8 @@ static void EmptyXimDestroyIcCallback(xcb_xim_t *im, xcb_xic_t ic,
void XcbXimInputMethodContext::SetCandidateWindowPosition(const Point &point) {
if (!ic_) return;
+ CRU_LOG_TAG_INFO("IC {} set candidate window position: {}", *ic_, point);
+
xcb_point_t spot;
spot.x = point.x;
spot.y = point.y;
@@ -198,21 +228,25 @@ void XcbXimInputMethodContext::CreateIc(xcb_window_t window) {
auto XimCreateIcCallback = [](xcb_xim_t *im, xcb_xic_t ic, void *user_data) {
auto context = static_cast<XcbXimInputMethodContext *>(user_data);
context->ic_ = ic;
+ CRU_LOG_TAG_INFO("IC {} is created.", ic);
if (context->window_->HasFocus()) {
xcb_xim_set_ic_focus(context->manager_->GetXcbXim(), ic);
}
};
- uint32_t input_style = XCB_IM_PreeditPosition | XCB_IM_StatusArea;
+ uint32_t input_style =
+ XCB_IM_PreeditArea | XCB_IM_PreeditCallbacks | XCB_IM_StatusNothing;
xcb_xim_create_ic(manager_->GetXcbXim(), XimCreateIcCallback, this,
XCB_XIM_XNInputStyle, &input_style, XCB_XIM_XNClientWindow,
&window, XCB_XIM_XNFocusWindow, &window, NULL);
+ CRU_LOG_TAG_INFO("Create XIM IC.");
}
void XcbXimInputMethodContext::DestroyIc() {
if (!ic_) return;
xcb_xim_destroy_ic(manager_->GetXcbXim(), *ic_, EmptyXimDestroyIcCallback,
this);
+ CRU_LOG_TAG_INFO("Destroy XIM IC.");
ic_ = std::nullopt;
}
diff --git a/src/platform/gui/xcb/Keyboard.cpp b/src/platform/gui/xcb/Keyboard.cpp
index e5f6da8e..c52f7cc1 100644
--- a/src/platform/gui/xcb/Keyboard.cpp
+++ b/src/platform/gui/xcb/Keyboard.cpp
@@ -1,9 +1,11 @@
#include "cru/platform/gui/xcb/Keyboard.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 <bitset>
#include <climits>
#include <unordered_map>
@@ -74,8 +76,8 @@ KeyCode XorgKeysymToKeyCode(xcb_keysym_t keysym) {
}
}
-KeyCode XorgKeycodeToCruKeyCode(XcbUiApplication *application,
- xcb_keycode_t keycode) {
+std::vector<xcb_keysym_t> XorgKeycodeToKeysyms(XcbUiApplication *application,
+ xcb_keycode_t keycode) {
auto connection = application->GetXcbConnection();
auto setup = xcb_get_setup(connection);
auto min_keycode = setup->min_keycode;
@@ -93,14 +95,41 @@ KeyCode XorgKeycodeToCruKeyCode(XcbUiApplication *application,
auto keysyms_per_keycode = mapping_reply->keysyms_per_keycode;
auto *keysyms = xcb_get_keyboard_mapping_keysyms(mapping_reply);
+ std::vector<xcb_keysym_t> result;
for (int i = 0; i < keysyms_per_keycode; i++) {
- auto result = XorgKeysymToKeyCode(keysyms[i]);
+ result.push_back(keysyms[i]);
+ }
+ return result;
+}
+
+KeyCode XorgKeycodeToCruKeyCode(XcbUiApplication *application,
+ xcb_keycode_t keycode) {
+ auto keysyms = XorgKeycodeToKeysyms(application, keycode);
+
+ for (auto keysym : keysyms) {
+ auto result = XorgKeysymToKeyCode(keysym);
if (result != KeyCode::Unknown) return result;
}
return KeyCode::Unknown;
}
+std::string XorgKeysymToUtf8(xcb_keysym_t keysym) {
+ if (0x20 <= keysym && keysym <= 0x7e || 0xa0 <= keysym && keysym <= 0xff) {
+ return std::string{static_cast<char>(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) *
diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp
index 2b4d57d9..0085dae7 100644
--- a/src/platform/gui/xcb/Window.cpp
+++ b/src/platform/gui/xcb/Window.cpp
@@ -302,7 +302,8 @@ void XcbWindow::SetToForeground() {
void XcbWindow::RequestRepaint() {
// TODO: true throttle
- paint_event_.Raise(nullptr);
+ repaint_canceler_.Reset(
+ application_->SetImmediate([this] { paint_event_.Raise(nullptr); }));
}
std::unique_ptr<graphics::IPainter> XcbWindow::BeginPaint() {