diff options
-rw-r--r-- | include/cru/platform/gui/xcb/InputMethod.h | 5 | ||||
-rw-r--r-- | include/cru/platform/gui/xcb/Keyboard.h | 2 | ||||
-rw-r--r-- | include/cru/platform/gui/xcb/Window.h | 3 | ||||
-rw-r--r-- | src/base/log/Logger.cpp | 2 | ||||
-rw-r--r-- | src/platform/gui/xcb/InputMethod.cpp | 54 | ||||
-rw-r--r-- | src/platform/gui/xcb/Keyboard.cpp | 35 | ||||
-rw-r--r-- | src/platform/gui/xcb/Window.cpp | 3 |
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() { |