From 18e330f9f4a673da78e6503c697619ad99b4bdbf Mon Sep 17 00:00:00 2001 From: Yuqian Yang Date: Wed, 10 Sep 2025 01:01:47 +0800 Subject: xcb window painter. --- include/cru/platform/gui/xcb/UiApplication.h | 9 +++- include/cru/platform/gui/xcb/Window.h | 5 +- src/platform/gui/xcb/CMakeLists.txt | 6 ++- src/platform/gui/xcb/UiApplication.cpp | 18 ++++++- src/platform/gui/xcb/Window.cpp | 72 +++++++++++++++++++++------- 5 files changed, 87 insertions(+), 23 deletions(-) diff --git a/include/cru/platform/gui/xcb/UiApplication.h b/include/cru/platform/gui/xcb/UiApplication.h index 3fce8efa..2a9ea517 100644 --- a/include/cru/platform/gui/xcb/UiApplication.h +++ b/include/cru/platform/gui/xcb/UiApplication.h @@ -3,6 +3,7 @@ #include "Base.h" #include +#include #include #include @@ -14,10 +15,13 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { friend XcbWindow; public: - XcbUiApplication(); + explicit XcbUiApplication( + graphics::cairo::CairoGraphicsFactory* cairo_factory = nullptr); ~XcbUiApplication(); public: + graphics::cairo::CairoGraphicsFactory* GetCairoFactory(); + void CheckXcbConnectionError(); xcb_connection_t* GetXcbConnection(); @@ -72,6 +76,9 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { void UnregisterWindow(XcbWindow* window); private: + graphics::cairo::CairoGraphicsFactory* cairo_factory_; + bool release_cairo_factory_; + xcb_connection_t* xcb_connection_; xcb_screen_t* screen_; diff --git a/include/cru/platform/gui/xcb/Window.h b/include/cru/platform/gui/xcb/Window.h index 62351645..e09fdbfb 100644 --- a/include/cru/platform/gui/xcb/Window.h +++ b/include/cru/platform/gui/xcb/Window.h @@ -3,6 +3,7 @@ #include "../Window.h" #include "Base.h" +#include #include #include #include @@ -59,8 +60,7 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { virtual void RequestRepaint() = 0; - // Remember to call EndDraw on return value and destroy it. - virtual std::unique_ptr BeginPaint() = 0; + std::unique_ptr BeginPaint() override; IEvent* CreateEvent() override; IEvent* DestroyEvent() override; @@ -91,6 +91,7 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { private: XcbUiApplication* application_; std::optional xcb_window_; + cairo_surface_t *cairo_surface_; Size current_size_; Event create_event_; diff --git a/src/platform/gui/xcb/CMakeLists.txt b/src/platform/gui/xcb/CMakeLists.txt index 8bd64279..aa6116e4 100644 --- a/src/platform/gui/xcb/CMakeLists.txt +++ b/src/platform/gui/xcb/CMakeLists.txt @@ -5,5 +5,7 @@ add_library(CruPlatformGuiX11 UiApplication.cpp Window.cpp ) -target_link_libraries(CruPlatformGuiX11 PUBLIC CruPlatformGui ${LIBRARY_XCB} ${LIBRARY_CAIRO}) - +target_link_libraries(CruPlatformGuiX11 PUBLIC + CruPlatformGui CruPlatformGraphicsCairo + ${LIBRARY_XCB} ${LIBRARY_CAIRO} +) diff --git a/src/platform/gui/xcb/UiApplication.cpp b/src/platform/gui/xcb/UiApplication.cpp index 619e71ef..462e2fde 100644 --- a/src/platform/gui/xcb/UiApplication.cpp +++ b/src/platform/gui/xcb/UiApplication.cpp @@ -1,12 +1,21 @@ #include "cru/platform/gui/xcb/UiApplication.h" +#include "cru/platform/graphics/cairo/CairoGraphicsFactory.h" #include "cru/platform/gui/xcb/Window.h" #include #include namespace cru::platform::gui::xcb { -XcbUiApplication::XcbUiApplication() { +XcbUiApplication::XcbUiApplication( + graphics::cairo::CairoGraphicsFactory *cairo_factory) { + release_cairo_factory_ = false; + if (cairo_factory == nullptr) { + cairo_factory = new graphics::cairo::CairoGraphicsFactory(); + release_cairo_factory_ = true; + } + cairo_factory_ = cairo_factory; + is_quit_on_all_window_closed_ = false; int screen_num; @@ -21,7 +30,12 @@ XcbUiApplication::XcbUiApplication() { this->screen_ = iter.data; } -XcbUiApplication::~XcbUiApplication() { xcb_disconnect(this->xcb_connection_); } +XcbUiApplication::~XcbUiApplication() { + xcb_disconnect(this->xcb_connection_); + if (release_cairo_factory_) { + delete cairo_factory_; + } +} void XcbUiApplication::CheckXcbConnectionError() { if (xcb_connection_has_error(this->xcb_connection_)) { diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp index 3f22e6b0..29c31527 100644 --- a/src/platform/gui/xcb/Window.cpp +++ b/src/platform/gui/xcb/Window.cpp @@ -1,13 +1,19 @@ #include "cru/platform/gui/xcb/Window.h" +#include "cru/platform/graphics/Painter.h" +#include "cru/platform/graphics/cairo/CairoPainter.h" #include "cru/platform/gui/Base.h" #include "cru/platform/gui/Keyboard.h" #include "cru/platform/gui/Window.h" #include "cru/platform/gui/xcb/Keyboard.h" #include "cru/platform/gui/xcb/UiApplication.h" +#include +#include #include +#include #include #include +#include #include namespace cru::platform::gui::xcb { @@ -49,12 +55,24 @@ KeyModifier ConvertModifiers(uint32_t mask) { } // namespace XcbWindow::XcbWindow(XcbUiApplication *application) - : application_(application) { + : application_(application), + xcb_window_(std::nullopt), + cairo_surface_(nullptr) { application->RegisterWindow(this); } XcbWindow::~XcbWindow() { application_->UnregisterWindow(this); } +std::unique_ptr XcbWindow::BeginPaint() { + assert(cairo_surface_); + + auto factory = application_->GetCairoFactory(); + cairo_t *cairo = cairo_create(cairo_surface_); + auto painter = + new graphics::cairo::CairoPainter(factory, cairo, true, cairo_surface_); + return std::unique_ptr(painter); +} + IEvent *XcbWindow::CreateEvent() { return &create_event_; } IEvent *XcbWindow::DestroyEvent() { return &destroy_event_; } @@ -96,10 +114,13 @@ IEvent *XcbWindow::KeyUpEvent() { return &key_up_event_; } std::optional XcbWindow::GetXcbWindow() { return xcb_window_; } xcb_window_t XcbWindow::DoCreateWindow() { + assert(xcb_window_ == std::nullopt); + assert(cairo_surface_ == nullptr); + auto connection = application_->GetXcbConnection(); auto screen = application_->GetFirstXcbScreen(); - auto xcb_window = xcb_generate_id(connection); + auto window = xcb_generate_id(connection); uint32_t mask = XCB_CW_EVENT_MASK; uint32_t values[1] = { @@ -109,39 +130,58 @@ xcb_window_t XcbWindow::DoCreateWindow() { 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, + int width = 400, height = 200; + + xcb_create_window(connection, XCB_COPY_FROM_PARENT, window, screen->root, 100, + 100, width, height, 10, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); - current_size_ = {400, 200}; + current_size_ = Size(width, height); + + xcb_visualtype_t *visual_type; + + for (xcb_depth_iterator_t depth_iter = + xcb_screen_allowed_depths_iterator(screen); + depth_iter.rem; xcb_depth_next(&depth_iter)) { + for (xcb_visualtype_iterator_t visual_iter = + xcb_depth_visuals_iterator(depth_iter.data); + visual_iter.rem; xcb_visualtype_next(&visual_iter)) { + if (screen->root_visual == visual_iter.data->visual_id) { + visual_type = visual_iter.data; + break; + } + } + } + + cairo_surface_ = + cairo_xcb_surface_create(connection, window, visual_type, width, height); create_event_.Raise(nullptr); - return xcb_window; + return 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); + paint_event_.Raise(nullptr); break; } case XCB_DESTROY_NOTIFY: { destroy_event_.Raise(nullptr); + + cairo_surface_destroy(cairo_surface_); + cairo_surface_ = nullptr; xcb_window_ = std::nullopt; break; } case XCB_CONFIGURE_NOTIFY: { xcb_configure_notify_event_t *configure = (xcb_configure_notify_event_t *)event; - if (configure->width != current_size_.width || - configure->height != current_size_.height) { - current_size_ = Size(configure->width, configure->height); + auto width = configure->width, height = configure->height; + if (width != current_size_.width || height != current_size_.height) { + current_size_ = Size(width, height); + assert(cairo_surface_); + cairo_xcb_surface_set_size(cairo_surface_, width, height); resize_event_.Raise(current_size_); } break; -- cgit v1.2.3