aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cru/platform/GraphicsBase.h16
-rw-r--r--include/cru/platform/gui/xcb/UiApplication.h1
-rw-r--r--include/cru/platform/gui/xcb/Window.h15
-rw-r--r--src/platform/gui/xcb/Window.cpp89
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