diff options
-rw-r--r-- | include/cru/platform/GraphicsBase.h | 16 | ||||
-rw-r--r-- | include/cru/platform/gui/xcb/UiApplication.h | 1 | ||||
-rw-r--r-- | include/cru/platform/gui/xcb/Window.h | 15 | ||||
-rw-r--r-- | src/platform/gui/xcb/Window.cpp | 89 |
4 files changed, 104 insertions, 17 deletions
diff --git a/include/cru/platform/GraphicsBase.h b/include/cru/platform/GraphicsBase.h index b0f653ef..d5936476 100644 --- a/include/cru/platform/GraphicsBase.h +++ b/include/cru/platform/GraphicsBase.h @@ -206,10 +206,22 @@ struct Rect final { height + thickness.GetVerticalTotal()); } - constexpr Rect Shrink(const Thickness& thickness) const { - return Rect(left + thickness.left, top + thickness.top, + constexpr Rect Shrink(const Thickness& thickness, + bool normalize = false) const { + Rect result(left + thickness.left, top + thickness.top, width - thickness.GetHorizontalTotal(), height - thickness.GetVerticalTotal()); + + if (normalize) { + if (result.width < 0) { + result.width = 0; + } + if (result.height < 0) { + result.height = 0; + } + } + + return result; } constexpr bool IsPointInside(const Point& point) const { diff --git a/include/cru/platform/gui/xcb/UiApplication.h b/include/cru/platform/gui/xcb/UiApplication.h index 6063a8ab..591ac33f 100644 --- a/include/cru/platform/gui/xcb/UiApplication.h +++ b/include/cru/platform/gui/xcb/UiApplication.h @@ -41,6 +41,7 @@ class XcbUiApplication : public XcbResource, public virtual IUiApplication { 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) + CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM(_NET_FRAME_EXTENTS) #undef CRU_XCB_UI_APPLICATION_DEFINE_XCB_ATOM diff --git a/include/cru/platform/gui/xcb/Window.h b/include/cru/platform/gui/xcb/Window.h index 927c3db9..ba4798a5 100644 --- a/include/cru/platform/gui/xcb/Window.h +++ b/include/cru/platform/gui/xcb/Window.h @@ -1,5 +1,6 @@ #pragma once +#include "../../GraphicsBase.h" #include "../Window.h" #include "Base.h" @@ -38,13 +39,8 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { Rect GetClientRect() override; void SetClientRect(const Rect& rect) override; - // Get the rect of the window containing frame. - // The lefttop of the rect is relative to screen lefttop. - virtual Rect GetWindowRect() = 0; - - // Set the rect of the window containing frame. - // The lefttop of the rect is relative to screen lefttop. - virtual void SetWindowRect(const Rect& rect) = 0; + Rect GetWindowRect() override; + void SetWindowRect(const Rect& rect) override; virtual bool RequestFocus() = 0; @@ -98,6 +94,11 @@ class XcbWindow : public XcbResource, public virtual INativeWindow { std::uint32_t length, std::uint32_t* out_length = nullptr); + // Relative to screen lefttop. + Point GetXcbWindowPosition(xcb_window_t window); + + std::optional<Thickness> Get_NET_FRAME_EXTENTS(xcb_window_t window); + private: XcbUiApplication* application_; std::optional<xcb_window_t> xcb_window_; diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp index 1dbef489..9f2448d1 100644 --- a/src/platform/gui/xcb/Window.cpp +++ b/src/platform/gui/xcb/Window.cpp @@ -1,6 +1,7 @@ #include "cru/platform/gui/xcb/Window.h" #include "cru/base/Base.h" #include "cru/platform/Check.h" +#include "cru/platform/GraphicsBase.h" #include "cru/platform/graphics/Painter.h" #include "cru/platform/graphics/cairo/CairoPainter.h" #include "cru/platform/gui/Base.h" @@ -188,12 +189,15 @@ Rect XcbWindow::GetClientRect() { if (!xcb_window_) { return Rect{}; } - auto cookie = - xcb_get_geometry(application_->GetXcbConnection(), *xcb_window_); + + auto window = *xcb_window_; + + auto cookie = xcb_get_geometry(application_->GetXcbConnection(), window); auto reply = xcb_get_geometry_reply(application_->GetXcbConnection(), cookie, nullptr); - return Rect(reply->x + reply->border_width, reply->y + reply->border_width, - reply->width, reply->height); + auto position = GetXcbWindowPosition(window); + + return Rect(position.x, position.y, reply->width, reply->height); } void XcbWindow::SetClientRect(const Rect &rect) { @@ -201,6 +205,32 @@ void XcbWindow::SetClientRect(const Rect &rect) { DoSetClientRect(*xcb_window_, rect); } +Rect XcbWindow::GetWindowRect() { + if (!xcb_window_) return {}; + + auto client_rect = GetClientRect(); + auto frame_properties = Get_NET_FRAME_EXTENTS(*xcb_window_); + + if (frame_properties.has_value()) { + return client_rect.Expand(*frame_properties); + } + + return client_rect; +} + +void XcbWindow::SetWindowRect(const Rect &rect) { + if (!xcb_window_) return; + + auto real_rect = rect; + auto frame_properties = Get_NET_FRAME_EXTENTS(*xcb_window_); + + if (frame_properties.has_value()) { + real_rect = real_rect.Shrink(*frame_properties, true); + } + + SetClientRect(real_rect); +} + std::unique_ptr<graphics::IPainter> XcbWindow::BeginPaint() { assert(cairo_surface_); @@ -518,10 +548,16 @@ void XcbWindow::DoSetTitle(xcb_window_t window) { } void XcbWindow::DoSetClientRect(xcb_window_t window, const Rect &rect) { - std::uint32_t values[4]{static_cast<std::uint32_t>(rect.left), - static_cast<std::uint32_t>(rect.top), - static_cast<std::uint32_t>(rect.width), - static_cast<std::uint32_t>(rect.height)}; + auto tree_cookie = xcb_query_tree(application_->GetXcbConnection(), window); + auto tree_reply = xcb_query_tree_reply(application_->GetXcbConnection(), + tree_cookie, nullptr); + auto parent_position = GetXcbWindowPosition(tree_reply->parent); + + std::uint32_t values[4]{ + static_cast<std::uint32_t>(rect.left - parent_position.x), + static_cast<std::uint32_t>(rect.top - parent_position.y), + static_cast<std::uint32_t>(rect.width), + static_cast<std::uint32_t>(rect.height)}; xcb_configure_window(application_->GetXcbConnection(), window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, @@ -544,4 +580,41 @@ void *XcbWindow::XcbGetProperty(xcb_window_t window, xcb_atom_t property, } return xcb_get_property_value(reply); } + +std::optional<Thickness> XcbWindow::Get_NET_FRAME_EXTENTS(xcb_window_t window) { + auto frame_properties = static_cast<std::uint32_t *>( + XcbGetProperty(window, application_->GetXcbAtom_NET_FRAME_EXTENTS(), + XCB_ATOM_CARDINAL, 0, 4)); + + if (frame_properties == nullptr) { + return std::nullopt; + } + + return Thickness(frame_properties[0], frame_properties[2], + frame_properties[1], frame_properties[3]); +} + +Point XcbWindow::GetXcbWindowPosition(xcb_window_t window) { + Point result; + + while (true) { + auto cookie = xcb_get_geometry(application_->GetXcbConnection(), window); + auto reply = xcb_get_geometry_reply(application_->GetXcbConnection(), + cookie, nullptr); + result.x += reply->x; + result.y += reply->y; + + auto tree_cookie = xcb_query_tree(application_->GetXcbConnection(), window); + auto tree_reply = xcb_query_tree_reply(application_->GetXcbConnection(), + tree_cookie, nullptr); + window = tree_reply->parent; + // TODO: Multi-screen offset? + if (tree_reply->root == window || window == XCB_WINDOW_NONE) { + break; + } + } + + return result; +} + } // namespace cru::platform::gui::xcb |