aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--demos/CMakeLists.txt15
-rw-r--r--demos/InputMethod/main.cpp1
-rw-r--r--include/cru/base/Bitmask.h4
-rw-r--r--include/cru/base/StringToNumberConverter.h10
-rw-r--r--include/cru/platform/gui/Keyboard.h10
-rw-r--r--include/cru/platform/gui/xcb/InputMethod.h77
-rw-r--r--include/cru/platform/gui/xcb/UiApplication.h4
-rw-r--r--include/cru/platform/gui/xcb/Window.h4
-rw-r--r--src/platform/gui/xcb/CMakeLists.txt4
-rw-r--r--src/platform/gui/xcb/InputMethod.cpp219
-rw-r--r--src/platform/gui/xcb/UiApplication.cpp19
-rw-r--r--src/platform/gui/xcb/Window.cpp24
13 files changed, 360 insertions, 33 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c2a624e2..6552f2dc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -88,7 +88,7 @@ jobs:
- name: Install Libraries
run: |
sudo apt update
- sudo apt install -y libpng-dev libcairo2-dev libpango1.0-dev libxcb1-dev libxcb-cursor-dev
+ sudo apt install -y libpng-dev libcairo2-dev libpango1.0-dev libxcb1-dev libxcb-cursor-dev libxcb-imdkit-dev
- name: Build
run: |
diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt
index 46eda9c4..42335013 100644
--- a/demos/CMakeLists.txt
+++ b/demos/CMakeLists.txt
@@ -3,18 +3,11 @@ add_library(CruDemoBase INTERFACE)
target_link_libraries(CruDemoBase INTERFACE CruPlatformBootstrap)
add_subdirectory(platform)
+add_subdirectory(main)
+add_subdirectory(ScrollView)
+add_subdirectory(InputMethod)
-if(WIN32)
- add_subdirectory(main)
- add_subdirectory(ScrollView)
- add_subdirectory(InputMethod)
-elseif(APPLE)
- add_subdirectory(main)
- add_subdirectory(ScrollView)
- add_subdirectory(InputMethod)
-elseif(UNIX)
- add_subdirectory(main)
- add_subdirectory(ScrollView)
+if(UNIX)
add_subdirectory(xcb)
endif()
diff --git a/demos/InputMethod/main.cpp b/demos/InputMethod/main.cpp
index cf42d3c2..430075f4 100644
--- a/demos/InputMethod/main.cpp
+++ b/demos/InputMethod/main.cpp
@@ -23,6 +23,7 @@ struct InputMethodState {
int main() {
IUiApplication* application = bootstrap::CreateUiApplication();
+ application->SetQuitOnAllWindowClosed(true);
auto graphics_factory = application->GetGraphicsFactory();
diff --git a/include/cru/base/Bitmask.h b/include/cru/base/Bitmask.h
index 9b6b8957..7606f784 100644
--- a/include/cru/base/Bitmask.h
+++ b/include/cru/base/Bitmask.h
@@ -10,8 +10,10 @@ struct Bitmask final {
constexpr Bitmask() : value(0) {}
constexpr explicit Bitmask(TUnderlying value) : value(value) {}
+ // Start from 1.
static constexpr Bitmask FromOffset(int offset) {
- return Bitmask(static_cast<TUnderlying>(1u << offset));
+ if (offset == 0) return {};
+ return Bitmask(static_cast<TUnderlying>(1u << (offset - 1)));
}
constexpr bool Has(Bitmask rhs) const { return (value & rhs.value) != 0; }
diff --git a/include/cru/base/StringToNumberConverter.h b/include/cru/base/StringToNumberConverter.h
index 051f44d0..64c29971 100644
--- a/include/cru/base/StringToNumberConverter.h
+++ b/include/cru/base/StringToNumberConverter.h
@@ -14,15 +14,15 @@ using StringToNumberFlag = Bitmask<details::StringToNumberFlagTag>;
struct StringToNumberFlags {
constexpr static StringToNumberFlag kAllowLeadingSpaces =
- StringToNumberFlag::FromOffset(0);
- constexpr static StringToNumberFlag kAllowTrailingSpaces =
StringToNumberFlag::FromOffset(1);
- constexpr static StringToNumberFlag kAllowTrailingJunk =
+ constexpr static StringToNumberFlag kAllowTrailingSpaces =
StringToNumberFlag::FromOffset(2);
- constexpr static StringToNumberFlag kAllowLeadingZeroForInteger =
+ constexpr static StringToNumberFlag kAllowTrailingJunk =
StringToNumberFlag::FromOffset(3);
- constexpr static StringToNumberFlag kThrowOnError =
+ constexpr static StringToNumberFlag kAllowLeadingZeroForInteger =
StringToNumberFlag::FromOffset(4);
+ constexpr static StringToNumberFlag kThrowOnError =
+ StringToNumberFlag::FromOffset(5);
};
template <typename TResult>
diff --git a/include/cru/platform/gui/Keyboard.h b/include/cru/platform/gui/Keyboard.h
index eb1cc76e..97665e41 100644
--- a/include/cru/platform/gui/Keyboard.h
+++ b/include/cru/platform/gui/Keyboard.h
@@ -118,16 +118,16 @@ struct TagKeyModifier {};
using KeyModifier = Bitmask<details::TagKeyModifier>;
struct KeyModifiers {
- static constexpr KeyModifier none{0};
- static constexpr KeyModifier shift{0b1};
- static constexpr KeyModifier ctrl{0b10};
- static constexpr KeyModifier alt{0b100};
- static constexpr KeyModifier command{0b1000};
static constexpr KeyModifier None = KeyModifier::FromOffset(0);
static constexpr KeyModifier Shift = KeyModifier::FromOffset(1);
static constexpr KeyModifier Ctrl = KeyModifier::FromOffset(2);
static constexpr KeyModifier Alt = KeyModifier::FromOffset(3);
static constexpr KeyModifier Command = KeyModifier::FromOffset(4);
+ static constexpr KeyModifier none = None;
+ static constexpr KeyModifier shift = Shift;
+ static constexpr KeyModifier ctrl = Ctrl;
+ static constexpr KeyModifier alt = Alt;
+ static constexpr KeyModifier command = Command;
};
#ifdef CRU_PLATFORM_OSX
diff --git a/include/cru/platform/gui/xcb/InputMethod.h b/include/cru/platform/gui/xcb/InputMethod.h
new file mode 100644
index 00000000..286f3158
--- /dev/null
+++ b/include/cru/platform/gui/xcb/InputMethod.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include "../InputMethod.h"
+#include "Base.h"
+
+#include <xcb-imdkit/imclient.h>
+#include <xcb/xcb.h>
+#include <optional>
+
+namespace cru::platform::gui::xcb {
+class XcbUiApplication;
+class XcbWindow;
+
+class XcbXimInputMethodManager : public XcbResource {
+ friend XcbUiApplication;
+
+ public:
+ XcbXimInputMethodManager(XcbUiApplication* application);
+ ~XcbXimInputMethodManager() override;
+
+ xcb_xim_t* GetXcbXim();
+
+ private:
+ void DispatchCommit(xcb_xim_t* im, xcb_xic_t ic, std::string text);
+ void DispatchComposition(xcb_xim_t* im, xcb_xic_t ic, CompositionText text);
+
+ bool HandleXEvent(xcb_generic_event_t* event);
+
+ private:
+ XcbUiApplication* application_;
+ xcb_xim_t* im_;
+};
+
+class XcbXimInputMethodContext : public XcbResource,
+ public virtual IInputMethodContext {
+ friend XcbXimInputMethodManager;
+
+ public:
+ XcbXimInputMethodContext(XcbXimInputMethodManager* manager,
+ XcbWindow* window);
+ ~XcbXimInputMethodContext() override;
+
+ bool ShouldManuallyDrawCompositionText() override;
+
+ void EnableIME() override;
+ void DisableIME() override;
+
+ void CompleteComposition() override;
+ void CancelComposition() override;
+ CompositionText GetCompositionText() override;
+
+ // Set the candidate window left-top. Relative to window left-top. Use this
+ // method to prepare typing.
+ void SetCandidateWindowPosition(const Point& point) override;
+
+ IEvent<std::nullptr_t>* CompositionStartEvent() override;
+ IEvent<std::nullptr_t>* CompositionEndEvent() override;
+ IEvent<std::nullptr_t>* CompositionEvent() override;
+ IEvent<StringView>* TextEvent() override;
+
+ private:
+ void CreateIc(xcb_window_t window);
+ void DestroyIc();
+
+ private:
+ XcbXimInputMethodManager* manager_;
+ XcbWindow* window_;
+ bool enabled_;
+ std::optional<xcb_xic_t> ic_;
+ CompositionText composition_text_;
+
+ Event<std::nullptr_t> composition_start_event_;
+ Event<std::nullptr_t> composition_end_event_;
+ Event<std::nullptr_t> composition_event_;
+ Event<StringView> text_event_;
+};
+} // namespace cru::platform::gui::xcb
diff --git a/include/cru/platform/gui/xcb/UiApplication.h b/include/cru/platform/gui/xcb/UiApplication.h
index b8de86f2..d6971099 100644
--- a/include/cru/platform/gui/xcb/UiApplication.h
+++ b/include/cru/platform/gui/xcb/UiApplication.h
@@ -13,6 +13,7 @@
namespace cru::platform::gui::xcb {
class XcbWindow;
class XcbCursorManager;
+class XcbXimInputMethodManager;
class XcbUiApplication : public XcbResource, public virtual IUiApplication {
friend XcbWindow;
@@ -49,6 +50,8 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication {
#undef CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM
+ XcbXimInputMethodManager* GetXcbXimInputMethodManager();
+
public:
int Run() override;
@@ -100,5 +103,6 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication {
std::vector<XcbWindow*> windows_;
XcbCursorManager* cursor_manager_;
+ XcbXimInputMethodManager* input_method_manager_;
};
} // namespace cru::platform::gui::xcb
diff --git a/include/cru/platform/gui/xcb/Window.h b/include/cru/platform/gui/xcb/Window.h
index 61e4b616..e330ba7c 100644
--- a/include/cru/platform/gui/xcb/Window.h
+++ b/include/cru/platform/gui/xcb/Window.h
@@ -12,6 +12,7 @@
namespace cru::platform::gui::xcb {
class XcbUiApplication;
class XcbCursor;
+class XcbXimInputMethodContext;
class XcbWindow : public XcbResource, public virtual INativeWindow {
friend XcbUiApplication;
@@ -79,6 +80,8 @@ class XcbWindow : public XcbResource, public virtual INativeWindow {
public:
std::optional<xcb_window_t> GetXcbWindow();
+ XcbUiApplication* GetXcbUiApplication();
+ bool HasFocus();
private:
xcb_window_t DoCreateWindow();
@@ -112,6 +115,7 @@ class XcbWindow : public XcbResource, public virtual INativeWindow {
std::shared_ptr<XcbCursor> cursor_;
XcbWindow* parent_;
+ XcbXimInputMethodContext* input_method_;
Event<std::nullptr_t> create_event_;
Event<std::nullptr_t> destroy_event_;
diff --git a/src/platform/gui/xcb/CMakeLists.txt b/src/platform/gui/xcb/CMakeLists.txt
index fa5cc0bf..35feb8bc 100644
--- a/src/platform/gui/xcb/CMakeLists.txt
+++ b/src/platform/gui/xcb/CMakeLists.txt
@@ -1,13 +1,15 @@
find_library(LIBRARY_CAIRO cairo REQUIRED)
find_library(LIBRARY_XCB xcb REQUIRED)
find_library(LIBRARY_XCB_CURSOR xcb-cursor REQUIRED)
+find_library(LIBRARY_XCB_IMDKIT xcb-imdkit REQUIRED)
add_library(CruPlatformGuiXcb
Cursor.cpp
+ InputMethod.cpp
Keyboard.cpp
UiApplication.cpp
Window.cpp
)
target_link_libraries(CruPlatformGuiXcb PUBLIC
CruPlatformGui CruPlatformGraphicsCairo
- ${LIBRARY_XCB} ${LIBRARY_XCB_CURSOR} ${LIBRARY_CAIRO}
+ ${LIBRARY_XCB} ${LIBRARY_XCB_CURSOR} ${LIBRARY_XCB_IMDKIT} ${LIBRARY_CAIRO}
)
diff --git a/src/platform/gui/xcb/InputMethod.cpp b/src/platform/gui/xcb/InputMethod.cpp
new file mode 100644
index 00000000..0cd079b4
--- /dev/null
+++ b/src/platform/gui/xcb/InputMethod.cpp
@@ -0,0 +1,219 @@
+#include "cru/platform/gui/xcb/InputMethod.h"
+#include "cru/platform/Check.h"
+#include "cru/platform/gui/InputMethod.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>
+
+namespace cru::platform::gui::xcb {
+namespace {
+void XimLogger(const char *, ...) {}
+} // namespace
+
+XcbXimInputMethodManager::XcbXimInputMethodManager(
+ XcbUiApplication *application)
+ : application_(application) {
+ auto XimOpenCallback = [](xcb_xim_t *im, void *user_data) {};
+
+ xcb_xim_im_callback kXimCallbacks = {
+ .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));
+
+ } 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;
+ manager->DispatchCommit(im, ic, std::string(str, length));
+ }
+ }
+ },
+ .preedit_draw =
+ [](xcb_xim_t *im, xcb_xic_t ic, xcb_im_preedit_draw_fr_t *frame,
+ void *user_data) {
+ auto manager = static_cast<XcbXimInputMethodManager *>(user_data);
+ CompositionText text;
+ if (!(frame->status & 1)) {
+ text.text = String::FromUtf8(
+ reinterpret_cast<const std::byte *>(frame->preedit_string),
+ frame->length_of_preedit_string);
+ text.selection = frame->caret;
+ }
+ manager->DispatchComposition(im, ic, std::move(text));
+ }};
+
+ xcb_compound_text_init();
+ im_ = xcb_xim_create(application->GetXcbConnection(), 0, "@im=fcitx");
+ xcb_xim_set_im_callback(im_, &kXimCallbacks, static_cast<void *>(this));
+ xcb_xim_set_log_handler(im_, XimLogger);
+ xcb_xim_set_use_compound_text(im_, true);
+ xcb_xim_set_use_utf8_string(im_, true);
+ xcb_xim_open(im_, XimOpenCallback, true, this);
+}
+
+XcbXimInputMethodManager::~XcbXimInputMethodManager() {
+ xcb_xim_close(im_);
+ xcb_xim_destroy(im_);
+}
+
+xcb_xim_t *XcbXimInputMethodManager::GetXcbXim() { return im_; }
+
+void XcbXimInputMethodManager::DispatchCommit(xcb_xim_t *im, xcb_xic_t ic,
+ std::string 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->text_event_.Raise(String::FromUtf8(text));
+ }
+ }
+}
+
+void XcbXimInputMethodManager::DispatchComposition(xcb_xim_t *im, xcb_xic_t ic,
+ CompositionText 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_text_ = std::move(text);
+ context->composition_event_.Raise(nullptr);
+ }
+ }
+}
+
+bool XcbXimInputMethodManager::HandleXEvent(xcb_generic_event_t *event) {
+ if (xcb_xim_filter_event(im_, event)) return true;
+ for (auto window : application_->GetAllWindow()) {
+ auto input_method_context = window->GetInputMethodContext();
+ auto context = CheckPlatform<XcbXimInputMethodContext>(input_method_context,
+ GetPlatformIdUtf8());
+ // Forward event to input method if IC is created.
+ 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;
+}
+
+XcbXimInputMethodContext::XcbXimInputMethodContext(
+ XcbXimInputMethodManager *manager, XcbWindow *window)
+ : manager_(manager), window_(window), enabled_(false) {
+ window->CreateEvent()->AddSpyOnlyHandler([this, window] {
+ if (enabled_) {
+ CreateIc(*window->GetXcbWindow());
+ }
+ });
+
+ window->DestroyEvent()->AddSpyOnlyHandler([this] { DestroyIc(); });
+
+ window->FocusEvent()->AddSpyOnlyHandler([this, window] {
+ auto input_method_context = window->GetInputMethodContext();
+ auto context = CheckPlatform<XcbXimInputMethodContext>(input_method_context,
+ GetPlatformIdUtf8());
+ if (context->enabled_ && context->ic_) {
+ xcb_xim_set_ic_focus(context->manager_->GetXcbXim(), *context->ic_);
+ }
+ });
+}
+
+XcbXimInputMethodContext::~XcbXimInputMethodContext() {}
+
+bool XcbXimInputMethodContext::ShouldManuallyDrawCompositionText() {
+ return false;
+}
+
+void XcbXimInputMethodContext::EnableIME() {
+ if (enabled_) return;
+ enabled_ = true;
+ if (!ic_ && window_->GetXcbWindow()) {
+ CreateIc(*window_->GetXcbWindow());
+ }
+}
+
+void XcbXimInputMethodContext::DisableIME() {
+ if (!enabled_) return;
+ enabled_ = false;
+ DestroyIc();
+}
+
+void XcbXimInputMethodContext::CompleteComposition() { CancelComposition(); }
+
+void XcbXimInputMethodContext::CancelComposition() {
+ if (!ic_) return;
+ auto XimResetIcCallback = [](xcb_xim_t *im, xcb_xic_t ic,
+ xcb_im_reset_ic_reply_fr_t *reply,
+ void *user_data) {};
+ xcb_xim_reset_ic(manager_->GetXcbXim(), *ic_, XimResetIcCallback, this);
+}
+
+static void EmptyXimDestroyIcCallback(xcb_xim_t *im, xcb_xic_t ic,
+ void *user_data) {}
+
+void XcbXimInputMethodContext::SetCandidateWindowPosition(const Point &point) {
+ if (!ic_) return;
+
+ xcb_point_t spot;
+ spot.x = point.x;
+ spot.y = point.y;
+ xcb_xim_nested_list nested = xcb_xim_create_nested_list(
+ manager_->GetXcbXim(), XCB_XIM_XNSpotLocation, &spot, NULL);
+ xcb_xim_set_ic_values(manager_->GetXcbXim(), *ic_, EmptyXimDestroyIcCallback,
+ this, XCB_XIM_XNPreeditAttributes, &nested, NULL);
+ ::free(nested.data);
+}
+
+CompositionText XcbXimInputMethodContext::GetCompositionText() {
+ return composition_text_;
+}
+
+IEvent<std::nullptr_t> *XcbXimInputMethodContext::CompositionStartEvent() {
+ return &composition_start_event_;
+}
+
+IEvent<std::nullptr_t> *XcbXimInputMethodContext::CompositionEndEvent() {
+ return &composition_end_event_;
+}
+
+IEvent<std::nullptr_t> *XcbXimInputMethodContext::CompositionEvent() {
+ return &composition_event_;
+}
+
+IEvent<StringView> *XcbXimInputMethodContext::TextEvent() {
+ return &text_event_;
+}
+
+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;
+ if (context->window_->HasFocus()) {
+ xcb_xim_set_ic_focus(context->manager_->GetXcbXim(), ic);
+ }
+ };
+
+ uint32_t input_style = XCB_IM_PreeditPosition | XCB_IM_StatusArea;
+ xcb_xim_create_ic(manager_->GetXcbXim(), XimCreateIcCallback, this,
+ XCB_XIM_XNInputStyle, &input_style, XCB_XIM_XNClientWindow,
+ &window, XCB_XIM_XNFocusWindow, &window, NULL);
+}
+
+void XcbXimInputMethodContext::DestroyIc() {
+ if (!ic_) return;
+ xcb_xim_destroy_ic(manager_->GetXcbXim(), *ic_, EmptyXimDestroyIcCallback,
+ this);
+ ic_ = std::nullopt;
+}
+
+} // namespace cru::platform::gui::xcb
diff --git a/src/platform/gui/xcb/UiApplication.cpp b/src/platform/gui/xcb/UiApplication.cpp
index 1e5613b4..03e39985 100644
--- a/src/platform/gui/xcb/UiApplication.cpp
+++ b/src/platform/gui/xcb/UiApplication.cpp
@@ -5,6 +5,7 @@
#include "cru/platform/graphics/cairo/CairoGraphicsFactory.h"
#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/Window.h"
#include <poll.h>
@@ -36,9 +37,11 @@ XcbUiApplication::XcbUiApplication(
this->screen_ = iter.data;
cursor_manager_ = new XcbCursorManager(this);
+ input_method_manager_ = new XcbXimInputMethodManager(this);
}
XcbUiApplication::~XcbUiApplication() {
+ delete input_method_manager_;
delete cursor_manager_;
xcb_disconnect(this->xcb_connection_);
@@ -80,6 +83,10 @@ xcb_atom_t XcbUiApplication::GetOrCreateXcbAtom(std::string name) {
return atom;
}
+XcbXimInputMethodManager *XcbUiApplication::GetXcbXimInputMethodManager() {
+ return input_method_manager_;
+}
+
int XcbUiApplication::Run() {
auto exit_code = event_loop_.Run();
@@ -128,11 +135,13 @@ void XcbUiApplication::CancelTimer(long long id) {
void XcbUiApplication::HandleXEvents() {
xcb_generic_event_t *event;
while ((event = xcb_poll_for_event(xcb_connection_))) {
- auto event_xcb_window = XcbWindow::GetEventWindow(event);
- for (auto window : windows_) {
- if (window->GetXcbWindow() == event_xcb_window) {
- window->HandleEvent(event);
- break;
+ if (!input_method_manager_->HandleXEvent(event)) {
+ auto event_xcb_window = XcbWindow::GetEventWindow(event);
+ for (auto window : windows_) {
+ if (window->GetXcbWindow() == event_xcb_window) {
+ window->HandleEvent(event);
+ break;
+ }
}
}
::free(event);
diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp
index d45d68b2..2b4d57d9 100644
--- a/src/platform/gui/xcb/Window.cpp
+++ b/src/platform/gui/xcb/Window.cpp
@@ -10,16 +10,15 @@
#include "cru/platform/gui/Keyboard.h"
#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/UiApplication.h"
#include <cairo-xcb.h>
#include <cairo.h>
#include <xcb/xcb.h>
-#include <xcb/xproto.h>
#include <algorithm>
#include <cassert>
-#include <cinttypes>
#include <cstdint>
#include <memory>
#include <optional>
@@ -68,9 +67,14 @@ XcbWindow::XcbWindow(XcbUiApplication *application)
cairo_surface_(nullptr),
parent_(nullptr) {
application->RegisterWindow(this);
+ input_method_ = new XcbXimInputMethodContext(
+ application->GetXcbXimInputMethodManager(), this);
}
-XcbWindow::~XcbWindow() { application_->UnregisterWindow(this); }
+XcbWindow::~XcbWindow() {
+ delete input_method_;
+ application_->UnregisterWindow(this);
+}
bool XcbWindow::IsCreated() { return xcb_window_.has_value(); }
@@ -351,10 +355,22 @@ IEvent<NativeKeyEventArgs> *XcbWindow::KeyDownEvent() {
IEvent<NativeKeyEventArgs> *XcbWindow::KeyUpEvent() { return &key_up_event_; }
-IInputMethodContext *XcbWindow::GetInputMethodContext() { return nullptr; }
+IInputMethodContext *XcbWindow::GetInputMethodContext() {
+ return input_method_;
+}
std::optional<xcb_window_t> XcbWindow::GetXcbWindow() { return xcb_window_; }
+XcbUiApplication *XcbWindow::GetXcbUiApplication() { return application_; }
+
+bool XcbWindow::HasFocus() {
+ if (!xcb_window_) return false;
+ auto cookie = xcb_get_input_focus(application_->GetXcbConnection());
+ auto focus = xcb_get_input_focus_reply(application_->GetXcbConnection(),
+ cookie, nullptr);
+ return focus->focus == *xcb_window_;
+}
+
xcb_window_t XcbWindow::DoCreateWindow() {
assert(xcb_window_ == std::nullopt);
assert(cairo_surface_ == nullptr);