aboutsummaryrefslogtreecommitdiff
path: root/src/platform/gui/xcb/Window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform/gui/xcb/Window.cpp')
-rw-r--r--src/platform/gui/xcb/Window.cpp227
1 files changed, 223 insertions, 4 deletions
diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp
index 188f62b8..e6e92fbc 100644
--- a/src/platform/gui/xcb/Window.cpp
+++ b/src/platform/gui/xcb/Window.cpp
@@ -1,17 +1,23 @@
#include "cru/platform/gui/xcb/Window.h"
#include "cru/base/Base.h"
+#include "cru/base/Guard.h"
#include "cru/platform/Check.h"
+#include "cru/platform/GraphicsBase.h"
+#include "cru/platform/graphics/NullPainter.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/Cursor.h"
#include "cru/platform/gui/xcb/Keyboard.h"
#include "cru/platform/gui/xcb/UiApplication.h"
#include <cairo-xcb.h>
#include <cairo.h>
#include <xcb/xcb.h>
+#include <xcb/xproto.h>
+#include <algorithm>
#include <cassert>
#include <cinttypes>
#include <cstdint>
@@ -66,9 +72,12 @@ XcbWindow::XcbWindow(XcbUiApplication *application)
XcbWindow::~XcbWindow() { application_->UnregisterWindow(this); }
+bool XcbWindow::IsCreated() { return xcb_window_.has_value(); }
+
void XcbWindow::Close() {
if (xcb_window_) {
xcb_destroy_window(application_->GetXcbConnection(), *xcb_window_);
+ application_->XcbFlush();
}
}
@@ -79,6 +88,7 @@ void XcbWindow::SetParent(INativeWindow *parent) {
if (xcb_window_) {
DoSetParent(*xcb_window_);
}
+ application_->XcbFlush();
}
WindowStyleFlag XcbWindow::GetStyleFlag() { return style_; }
@@ -88,6 +98,7 @@ void XcbWindow::SetStyleFlag(WindowStyleFlag flag) {
if (xcb_window_) {
DoSetStyleFlags(*xcb_window_);
}
+ application_->XcbFlush();
}
String XcbWindow::GetTitle() { return String::FromUtf8(title_); }
@@ -97,6 +108,7 @@ void XcbWindow::SetTitle(String title) {
if (xcb_window_) {
DoSetTitle(*xcb_window_);
}
+ application_->XcbFlush();
}
namespace {
@@ -140,7 +152,6 @@ void XcbWindow::SetVisibility(WindowVisibilityType visibility) {
auto old_value = static_cast<std::uint32_t *>(
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),
@@ -175,10 +186,125 @@ void XcbWindow::SetVisibility(WindowVisibilityType visibility) {
default:
UnreachableCode();
}
+
+ application_->XcbFlush();
+}
+
+Size XcbWindow::GetClientSize() { return GetClientRect().GetSize(); }
+
+void XcbWindow::SetClientSize(const Size &size) {
+ auto rect = GetClientRect();
+ SetClientRect(Rect(rect.GetLeftTop(), size));
+}
+
+Rect XcbWindow::GetClientRect() {
+ if (!xcb_window_) {
+ return Rect{};
+ }
+
+ auto window = *xcb_window_;
+
+ auto cookie = xcb_get_geometry(application_->GetXcbConnection(), window);
+ auto reply = FreeLater(xcb_get_geometry_reply(
+ application_->GetXcbConnection(), cookie, nullptr));
+ auto position = GetXcbWindowPosition(window);
+
+ return Rect(position.x, position.y, reply->width, reply->height);
+}
+
+void XcbWindow::SetClientRect(const Rect &rect) {
+ if (!xcb_window_) return;
+ DoSetClientRect(*xcb_window_, rect);
+ application_->XcbFlush();
+}
+
+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);
+}
+
+bool XcbWindow::RequestFocus() {
+ if (!xcb_window_) return false;
+ xcb_set_input_focus(application_->GetXcbConnection(), XCB_NONE, *xcb_window_,
+ XCB_CURRENT_TIME);
+ application_->XcbFlush();
+ return true;
+}
+
+Point XcbWindow::GetMousePosition() {
+ auto window = xcb_window_.value_or(application_->GetFirstXcbScreen()->root);
+ auto cookie = xcb_query_pointer(application_->GetXcbConnection(), window);
+ auto reply = FreeLater(xcb_query_pointer_reply(
+ application_->GetXcbConnection(), cookie, nullptr));
+ return Point(reply->win_x, reply->win_y);
+}
+
+bool XcbWindow::CaptureMouse() {
+ if (!xcb_window_) return false;
+ // KDE handles grabbing automatically.
+ //
+ // xcb_grab_pointer(application_->GetXcbConnection(), TRUE, *xcb_window_, 0,
+ // XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE,
+ // XCB_CURSOR_NONE, XCB_CURRENT_TIME);
+ // application_->XcbFlush();
+ return true;
+}
+
+bool XcbWindow::ReleaseMouse() {
+ if (!xcb_window_) return false;
+ // KDE handles grabbing automatically.
+ //
+ // xcb_ungrab_pointer(application_->xcb_connection_, XCB_CURRENT_TIME);
+ // application_->XcbFlush();
+ return true;
+}
+
+void XcbWindow::SetCursor(std::shared_ptr<ICursor> cursor) {
+ if (!xcb_window_) return;
+ auto xcb_cursor = CheckPlatform<XcbCursor>(cursor, GetPlatformId());
+ cursor_ = xcb_cursor;
+ DoSetCursor(*xcb_window_, xcb_cursor.get());
+}
+
+void XcbWindow::SetToForeground() {
+ SetVisibility(WindowVisibilityType::Show);
+ assert(xcb_window_.has_value());
+ const static uint32_t values[] = {XCB_STACK_MODE_ABOVE};
+ xcb_configure_window(application_->GetXcbConnection(), *xcb_window_,
+ XCB_CONFIG_WINDOW_STACK_MODE, values);
+ application_->XcbFlush();
+}
+
+void XcbWindow::RequestRepaint() {
+ // TODO: true throttle
+ paint_event_.Raise(nullptr);
}
std::unique_ptr<graphics::IPainter> XcbWindow::BeginPaint() {
- assert(cairo_surface_);
+ if (!xcb_window_.has_value()) {
+ return std::make_unique<graphics::NullPainter>();
+ }
auto factory = application_->GetCairoFactory();
cairo_t *cairo = cairo_create(cairo_surface_);
@@ -225,6 +351,8 @@ IEvent<NativeKeyEventArgs> *XcbWindow::KeyDownEvent() {
IEvent<NativeKeyEventArgs> *XcbWindow::KeyUpEvent() { return &key_up_event_; }
+IInputMethodContext *XcbWindow::GetInputMethodContext() { return nullptr; }
+
std::optional<xcb_window_t> XcbWindow::GetXcbWindow() { return xcb_window_; }
xcb_window_t XcbWindow::DoCreateWindow() {
@@ -253,8 +381,16 @@ xcb_window_t XcbWindow::DoCreateWindow() {
xcb_window_ = window;
+ std::vector<xcb_atom_t> wm_protocols{
+ application_->GetXcbAtomWM_DELETE_WINDOW()};
+ xcb_change_property(application_->GetXcbConnection(), XCB_PROP_MODE_REPLACE,
+ window, application_->GetXcbAtomWM_PROTOCOLS(),
+ XCB_ATOM_ATOM, 32, wm_protocols.size(),
+ wm_protocols.data());
+
DoSetStyleFlags(window);
DoSetParent(window);
+ DoSetCursor(window, cursor_.get());
xcb_visualtype_t *visual_type;
@@ -291,6 +427,14 @@ void XcbWindow::HandleEvent(xcb_generic_event_t *event) {
cairo_surface_destroy(cairo_surface_);
cairo_surface_ = nullptr;
xcb_window_ = std::nullopt;
+
+ if (application_->IsQuitOnAllWindowClosed() &&
+ std::ranges::none_of(
+ application_->GetAllWindow(),
+ [](INativeWindow *window) { return window->IsCreated(); })) {
+ application_->RequestQuit(0);
+ }
+
break;
}
case XCB_CONFIGURE_NOTIFY: {
@@ -388,6 +532,16 @@ void XcbWindow::HandleEvent(xcb_generic_event_t *event) {
key_up_event_.Raise(std::move(args));
break;
}
+ case XCB_CLIENT_MESSAGE: {
+ xcb_client_message_event_t *cm = (xcb_client_message_event_t *)event;
+ if (cm->data.data32[0] == application_->GetXcbAtomWM_DELETE_WINDOW() &&
+ xcb_window_.has_value()) {
+ xcb_destroy_window(application_->GetXcbConnection(),
+ xcb_window_.value());
+ xcb_flush(application_->GetXcbConnection());
+ }
+ break;
+ }
default:
/* Unknown event type, ignore it */
printf("Unknown event: %" PRIu8 "\n", event->response_type);
@@ -455,6 +609,10 @@ std::optional<xcb_window_t> XcbWindow::GetEventWindow(
xcb_key_release_event_t *kr = (xcb_key_release_event_t *)event;
return kr->event;
}
+ case XCB_CLIENT_MESSAGE: {
+ xcb_client_message_event_t *cm = (xcb_client_message_event_t *)event;
+ return cm->window;
+ }
default:
return std::nullopt;
}
@@ -493,14 +651,38 @@ void XcbWindow::DoSetTitle(xcb_window_t window) {
}
}
+void XcbWindow::DoSetClientRect(xcb_window_t window, const Rect &rect) {
+ auto tree_cookie = xcb_query_tree(application_->GetXcbConnection(), window);
+ auto tree_reply = FreeLater(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,
+ values);
+}
+
+void XcbWindow::DoSetCursor(xcb_window_t window, XcbCursor *cursor) {
+ std::uint32_t values[]{cursor ? cursor->GetXcbCursor() : XCB_CURSOR_NONE};
+ xcb_change_window_attributes(application_->GetXcbConnection(), window,
+ XCB_CW_CURSOR, values);
+ application_->XcbFlush();
+}
+
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);
+ auto reply = FreeLater(
+ xcb_get_property_reply(application_->GetXcbConnection(), cookie, NULL));
if (reply->type == XCB_ATOM_NONE) {
return nullptr;
}
@@ -509,4 +691,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 = FreeLater(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 = FreeLater(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