From 31844fa8a1710c8f573f3f30b46baf9093cee83b Mon Sep 17 00:00:00 2001 From: Yuqian Yang Date: Sun, 14 Sep 2025 22:46:53 +0800 Subject: Implement visibility of xcb window. --- include/cru/platform/gui/xcb/UiApplication.h | 16 +++-- include/cru/platform/gui/xcb/Window.h | 10 ++- src/platform/gui/xcb/UiApplication.cpp | 16 ----- src/platform/gui/xcb/Window.cpp | 99 ++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 22 deletions(-) diff --git a/include/cru/platform/gui/xcb/UiApplication.h b/include/cru/platform/gui/xcb/UiApplication.h index cc38b3ed..6063a8ab 100644 --- a/include/cru/platform/gui/xcb/UiApplication.h +++ b/include/cru/platform/gui/xcb/UiApplication.h @@ -31,10 +31,18 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { xcb_screen_t* GetFirstXcbScreen(); xcb_atom_t GetOrCreateXcbAtom(std::string name); - xcb_atom_t GetXcbAtom_NET_WM_NAME(); - xcb_atom_t GetXcbAtom_NET_WM_WINDOW_TYPE(); - xcb_atom_t GetXcbAtom_NET_WM_WINDOW_TYPE_NORMAL(); - xcb_atom_t GetXcbAtom_NET_WM_WINDOW_TYPE_UTILITY(); + +#define CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(name) \ + xcb_atom_t GetXcbAtom##name() { return GetOrCreateXcbAtom(#name); } + + CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(WM_NAME) + CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(WM_STATE) + CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(_NET_WM_NAME) + CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(_NET_WM_WINDOW_TYPE) + CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(_NET_WM_WINDOW_TYPE_NORMAL) + CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(_NET_WM_WINDOW_TYPE_UTILITY) + +#undef CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM public: int Run() override; diff --git a/include/cru/platform/gui/xcb/Window.h b/include/cru/platform/gui/xcb/Window.h index f118ed66..6d923666 100644 --- a/include/cru/platform/gui/xcb/Window.h +++ b/include/cru/platform/gui/xcb/Window.h @@ -29,8 +29,8 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { String GetTitle() override; void SetTitle(String title) override; - virtual WindowVisibilityType GetVisibility() = 0; - virtual void SetVisibility(WindowVisibilityType visibility) = 0; + WindowVisibilityType GetVisibility() override; + void SetVisibility(WindowVisibilityType visibility) override; virtual Size GetClientSize() = 0; virtual void SetClientSize(const Size& size) = 0; @@ -92,6 +92,11 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { void DoSetStyleFlags(xcb_window_t window); void DoSetTitle(xcb_window_t window); + void* XcbGetProperty(xcb_window_t window, xcb_atom_t property, + xcb_atom_t type, std::uint32_t offset, + std::uint32_t length, + std::uint32_t* out_length = nullptr); + private: XcbUiApplication* application_; std::optional xcb_window_; @@ -99,6 +104,7 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { Size current_size_; WindowStyleFlag style_; std::string title_; + bool mapped_; XcbWindow* parent_; diff --git a/src/platform/gui/xcb/UiApplication.cpp b/src/platform/gui/xcb/UiApplication.cpp index e5cab19f..0a6ab07f 100644 --- a/src/platform/gui/xcb/UiApplication.cpp +++ b/src/platform/gui/xcb/UiApplication.cpp @@ -63,22 +63,6 @@ xcb_atom_t XcbUiApplication::GetOrCreateXcbAtom(std::string name) { return atom; } -xcb_atom_t XcbUiApplication::GetXcbAtom_NET_WM_NAME() { - return GetOrCreateXcbAtom("_NET_WM_NAME"); -} - -xcb_atom_t XcbUiApplication::GetXcbAtom_NET_WM_WINDOW_TYPE() { - return GetOrCreateXcbAtom("_NET_WM_WINDOW_TYPE"); -} - -xcb_atom_t XcbUiApplication::GetXcbAtom_NET_WM_WINDOW_TYPE_NORMAL() { - return GetOrCreateXcbAtom("_NET_WM_WINDOW_TYPE_NORMAL"); -} - -xcb_atom_t XcbUiApplication::GetXcbAtom_NET_WM_WINDOW_TYPE_UTILITY() { - return GetOrCreateXcbAtom("_NET_WM_WINDOW_TYPE_UTILITY"); -} - int XcbUiApplication::Run() { auto exit_code = event_loop_.Run(); diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp index b56b8fa9..188f62b8 100644 --- a/src/platform/gui/xcb/Window.cpp +++ b/src/platform/gui/xcb/Window.cpp @@ -1,4 +1,5 @@ #include "cru/platform/gui/xcb/Window.h" +#include "cru/base/Base.h" #include "cru/platform/Check.h" #include "cru/platform/graphics/Painter.h" #include "cru/platform/graphics/cairo/CairoPainter.h" @@ -98,6 +99,84 @@ void XcbWindow::SetTitle(String title) { } } +namespace { +constexpr int WithdrawnState = 0; +constexpr int NormalState = 1; +constexpr int IconicState = 3; +} // namespace + +WindowVisibilityType XcbWindow::GetVisibility() { + if (!xcb_window_) return WindowVisibilityType::Hide; + auto value = static_cast( + XcbGetProperty(*xcb_window_, application_->GetXcbAtomWM_STATE(), + application_->GetXcbAtomWM_STATE(), 0, 1)); + if (value != nullptr && *value == IconicState) { + return WindowVisibilityType::Minimize; + } + if (!mapped_) return WindowVisibilityType::Hide; + return WindowVisibilityType::Show; +} + +void XcbWindow::SetVisibility(WindowVisibilityType visibility) { + auto update_wm_state = [this, visibility] { + auto atom = application_->GetXcbAtomWM_STATE(); + auto window = *xcb_window_; + + std::uint32_t value[2]; + switch (visibility) { + case WindowVisibilityType::Show: + value[0] = NormalState; + break; + case WindowVisibilityType::Minimize: + value[0] = IconicState; + break; + case WindowVisibilityType::Hide: + value[0] = WithdrawnState; + break; + default: + UnreachableCode(); + } + + auto old_value = static_cast( + XcbGetProperty(*xcb_window_, atom, atom, 0, 2)); + if (old_value) value[1] = old_value[1]; + UnreachableCode(); + + xcb_change_property(application_->GetXcbConnection(), XCB_PROP_MODE_REPLACE, + window, atom, atom, 32, sizeof(value) / sizeof(*value), + value); + }; + + switch (visibility) { + case WindowVisibilityType::Show: { + if (!xcb_window_) { + DoCreateWindow(); + } + update_wm_state(); + xcb_map_window(application_->GetXcbConnection(), *xcb_window_); + break; + } + case WindowVisibilityType::Minimize: { + if (!xcb_window_) { + DoCreateWindow(); + } + update_wm_state(); + xcb_unmap_window(application_->GetXcbConnection(), *xcb_window_); + break; + } + case WindowVisibilityType::Hide: { + if (!xcb_window_) { + return; + } + update_wm_state(); + xcb_unmap_window(application_->GetXcbConnection(), *xcb_window_); + break; + } + default: + UnreachableCode(); + } +} + std::unique_ptr XcbWindow::BeginPaint() { assert(cairo_surface_); @@ -172,6 +251,8 @@ xcb_window_t XcbWindow::DoCreateWindow() { screen->root_visual, mask, values); current_size_ = Size(width, height); + xcb_window_ = window; + DoSetStyleFlags(window); DoSetParent(window); @@ -226,10 +307,12 @@ void XcbWindow::HandleEvent(xcb_generic_event_t *event) { } case XCB_MAP_NOTIFY: { visibility_change_event_.Raise(WindowVisibilityType::Show); + mapped_ = true; break; } case XCB_UNMAP_NOTIFY: { visibility_change_event_.Raise(WindowVisibilityType::Hide); + mapped_ = false; break; } case XCB_FOCUS_IN: { @@ -410,4 +493,20 @@ void XcbWindow::DoSetTitle(xcb_window_t window) { } } +void *XcbWindow::XcbGetProperty(xcb_window_t window, xcb_atom_t property, + xcb_atom_t type, std::uint32_t offset, + std::uint32_t length, + std::uint32_t *out_length) { + auto cookie = xcb_get_property(application_->GetXcbConnection(), false, + window, property, type, offset, length); + auto reply = + xcb_get_property_reply(application_->GetXcbConnection(), cookie, NULL); + if (reply->type == XCB_ATOM_NONE) { + return nullptr; + } + if (out_length != nullptr) { + *out_length = xcb_get_property_value_length(reply); + } + return xcb_get_property_value(reply); +} } // namespace cru::platform::gui::xcb -- cgit v1.2.3