From f761d4ef25ab0e695c79ae5c2ede1f850f1607af Mon Sep 17 00:00:00 2001 From: Yuqian Yang Date: Tue, 9 Sep 2025 19:10:14 +0800 Subject: Develop some xcb window. --- include/cru/platform/gui/xcb/UiApplication.h | 8 +- include/cru/platform/gui/xcb/Window.h | 20 ++- src/platform/gui/xcb/UiApplication.cpp | 26 +++- src/platform/gui/xcb/Window.cpp | 180 ++++++++++++++++++++++++++- 4 files changed, 223 insertions(+), 11 deletions(-) diff --git a/include/cru/platform/gui/xcb/UiApplication.h b/include/cru/platform/gui/xcb/UiApplication.h index 42d63609..3fce8efa 100644 --- a/include/cru/platform/gui/xcb/UiApplication.h +++ b/include/cru/platform/gui/xcb/UiApplication.h @@ -17,8 +17,14 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { XcbUiApplication(); ~XcbUiApplication(); + public: void CheckXcbConnectionError(); + xcb_connection_t* GetXcbConnection(); + + // This API is weird, but before we have correct screen API, we still use it. + xcb_screen_t* GetFirstXcbScreen(); + public: int Run() override; void RequestQuit(int quit_code) override; @@ -66,7 +72,7 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { void UnregisterWindow(XcbWindow* window); private: - xcb_connection_t* xcb_; + xcb_connection_t* xcb_connection_; xcb_screen_t* screen_; cru::platform::unix::UnixEventLoop event_loop_; diff --git a/include/cru/platform/gui/xcb/Window.h b/include/cru/platform/gui/xcb/Window.h index 2d5651e7..1676dced 100644 --- a/include/cru/platform/gui/xcb/Window.h +++ b/include/cru/platform/gui/xcb/Window.h @@ -4,11 +4,14 @@ #include "Base.h" #include +#include namespace cru::platform::gui::xcb { class XcbUiApplication; class XcbWindow : public XcbResource, public virtual INativeWindow { + friend XcbUiApplication; + public: explicit XcbWindow(XcbUiApplication* application); ~XcbWindow() override; @@ -66,8 +69,8 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { virtual IEvent* ResizeEvent() = 0; virtual IEvent* FocusEvent() = 0; - virtual IEvent* MouseEnterLeaveEvent() = 0; - virtual IEvent* MouseMoveEvent() = 0; + IEvent* MouseEnterLeaveEvent() override; + IEvent* MouseMoveEvent() override; virtual IEvent* MouseDownEvent() = 0; virtual IEvent* MouseUpEvent() = 0; virtual IEvent* MouseWheelEvent() = 0; @@ -76,8 +79,19 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { virtual IInputMethodContext* GetInputMethodContext() = 0; + public: + std::optional GetXcbWindow(); + + private: + xcb_window_t DoCreateWindow(); + void HandleEvent(xcb_generic_event_t* event); + static std::optional GetEventWindow(xcb_generic_event_t* event); + private: XcbUiApplication* application_; - xcb_window_t xcb_window_; + std::optional xcb_window_; + + Event mouse_enter_leave_event_; + Event mouse_move_event_; }; } // namespace cru::platform::gui::xcb diff --git a/src/platform/gui/xcb/UiApplication.cpp b/src/platform/gui/xcb/UiApplication.cpp index 139a5422..619e71ef 100644 --- a/src/platform/gui/xcb/UiApplication.cpp +++ b/src/platform/gui/xcb/UiApplication.cpp @@ -1,8 +1,9 @@ -#include +#include "cru/platform/gui/xcb/UiApplication.h" + +#include "cru/platform/gui/xcb/Window.h" #include #include -#include "cru/base/Base.h" namespace cru::platform::gui::xcb { XcbUiApplication::XcbUiApplication() { @@ -20,14 +21,20 @@ XcbUiApplication::XcbUiApplication() { this->screen_ = iter.data; } -XcbUiApplication::~XcbUiApplication() { xcb_disconnect(this->xcb_); } +XcbUiApplication::~XcbUiApplication() { xcb_disconnect(this->xcb_connection_); } void XcbUiApplication::CheckXcbConnectionError() { - if (xcb_connection_has_error(this->xcb_)) { + if (xcb_connection_has_error(this->xcb_connection_)) { throw XcbException("xcb_connection_has_error returned non-zero."); } } +xcb_connection_t *XcbUiApplication::GetXcbConnection() { + return xcb_connection_; +} + +xcb_screen_t *XcbUiApplication::GetFirstXcbScreen() { return screen_; } + int XcbUiApplication::Run() { auto exit_code = event_loop_.Run(); @@ -75,8 +82,15 @@ void XcbUiApplication::CancelTimer(long long id) { void XcbUiApplication::HandleXEvents() { xcb_generic_event_t *event; - while ((event = xcb_poll_for_event(xcb_))) { - NotImplemented(); + 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; + } + } + ::free(event); } } diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp index cf4e059d..3494bc7d 100644 --- a/src/platform/gui/xcb/Window.cpp +++ b/src/platform/gui/xcb/Window.cpp @@ -1,11 +1,189 @@ #include "cru/platform/gui/xcb/Window.h" +#include "cru/platform/gui/Window.h" #include "cru/platform/gui/xcb/UiApplication.h" +#include +#include +#include + +namespace { +// TODO: Remove this demo function. +/* print names of modifiers present in mask */ +void print_modifiers(uint32_t mask) { + const char *MODIFIERS[] = { + "Shift", "Lock", "Ctrl", "Alt", "Mod2", "Mod3", "Mod4", + "Mod5", "Button1", "Button2", "Button3", "Button4", "Button5"}; + + printf("Modifier mask: "); + for (const char **modifier = MODIFIERS; mask; mask >>= 1, ++modifier) { + if (mask & 1) { + printf("%s", *modifier); + } + } + printf("\n"); +} + +} // namespace + namespace cru::platform::gui::xcb { -XcbWindow::XcbWindow(XcbUiApplication* application) +XcbWindow::XcbWindow(XcbUiApplication *application) : application_(application) { application->RegisterWindow(this); } XcbWindow::~XcbWindow() { application_->UnregisterWindow(this); } + +IEvent *XcbWindow::MouseEnterLeaveEvent() { + return &mouse_enter_leave_event_; +} + +IEvent *XcbWindow::MouseMoveEvent() { return &mouse_move_event_; } + +std::optional XcbWindow::GetXcbWindow() { return xcb_window_; } + +xcb_window_t XcbWindow::DoCreateWindow() { + auto connection = application_->GetXcbConnection(); + auto screen = application_->GetFirstXcbScreen(); + + auto xcb_window = xcb_generate_id(connection); + + uint32_t mask = XCB_CW_EVENT_MASK; + uint32_t values[1] = { + XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS | + XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW | + XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE}; + + xcb_create_window(connection, XCB_COPY_FROM_PARENT, xcb_window, screen->root, + 100, 100, 400, 200, 10, XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, mask, values); + + return xcb_window; +} + +void XcbWindow::HandleEvent(xcb_generic_event_t *event) { + switch (event->response_type & ~0x80) { + case XCB_EXPOSE: { + xcb_expose_event_t *expose = (xcb_expose_event_t *)event; + + printf("Window %" PRIu32 + " exposed. Region to be redrawn at location (%" PRIu16 ",%" PRIu16 + "), with dimension (%" PRIu16 ",%" PRIu16 ")\n", + expose->window, expose->x, expose->y, expose->width, + expose->height); + break; + } + case XCB_BUTTON_PRESS: { + xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event; + print_modifiers(bp->state); + + switch (bp->detail) { + case 4: + printf("Wheel Button up in window %" PRIu32 + ", at coordinates (%" PRIi16 ",%" PRIi16 ")\n", + bp->event, bp->event_x, bp->event_y); + break; + case 5: + printf("Wheel Button down in window %" PRIu32 + ", at coordinates (%" PRIi16 ",%" PRIi16 ")\n", + bp->event, bp->event_x, bp->event_y); + break; + default: + printf("Button %" PRIu8 " pressed in window %" PRIu32 + ", at coordinates (%" PRIi16 ",%" PRIi16 ")\n", + bp->detail, bp->event, bp->event_x, bp->event_y); + break; + } + break; + } + case XCB_BUTTON_RELEASE: { + xcb_button_release_event_t *br = (xcb_button_release_event_t *)event; + print_modifiers(br->state); + + printf("Button %" PRIu8 " released in window %" PRIu32 + ", at coordinates (%" PRIi16 ",%" PRIi16 ")\n", + br->detail, br->event, br->event_x, br->event_y); + break; + } + case XCB_MOTION_NOTIFY: { + xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; + Point point(motion->event_x, motion->event_y); + mouse_move_event_.Raise(point); + break; + } + case XCB_ENTER_NOTIFY: { + xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)event; + mouse_enter_leave_event_.Raise(MouseEnterLeaveType::Enter); + Point point(enter->event_x, enter->event_y); + mouse_move_event_.Raise(point); + break; + } + case XCB_LEAVE_NOTIFY: { + xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *)event; + // Should we do this? + // Point point(leave->event_x, leave->event_y); + // mouse_move_event_.Raise(point); + mouse_enter_leave_event_.Raise(MouseEnterLeaveType::Leave); + break; + } + case XCB_KEY_PRESS: { + xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event; + print_modifiers(kp->state); + + printf("Key pressed in window %" PRIu32 "\n", kp->event); + break; + } + case XCB_KEY_RELEASE: { + xcb_key_release_event_t *kr = (xcb_key_release_event_t *)event; + print_modifiers(kr->state); + + printf("Key released in window %" PRIu32 "\n", kr->event); + break; + } + default: + /* Unknown event type, ignore it */ + printf("Unknown event: %" PRIu8 "\n", event->response_type); + break; + } +} + +std::optional XcbWindow::GetEventWindow( + xcb_generic_event_t *event) { + switch (event->response_type & ~0x80) { + case XCB_EXPOSE: { + xcb_expose_event_t *expose = (xcb_expose_event_t *)event; + return expose->window; + } + case XCB_BUTTON_PRESS: { + xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event; + return bp->event; + } + case XCB_BUTTON_RELEASE: { + xcb_button_release_event_t *br = (xcb_button_release_event_t *)event; + return br->event; + } + case XCB_MOTION_NOTIFY: { + xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)event; + return motion->event; + } + case XCB_ENTER_NOTIFY: { + xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)event; + return enter->event; + } + case XCB_LEAVE_NOTIFY: { + xcb_leave_notify_event_t *leave = (xcb_leave_notify_event_t *)event; + return leave->event; + } + case XCB_KEY_PRESS: { + xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event; + return kp->event; + } + case XCB_KEY_RELEASE: { + xcb_key_release_event_t *kr = (xcb_key_release_event_t *)event; + return kr->event; + } + default: + return std::nullopt; + } +} } // namespace cru::platform::gui::xcb -- cgit v1.2.3