aboutsummaryrefslogtreecommitdiff
path: root/src/platform
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform')
-rw-r--r--src/platform/bootstrap/Bootstrap.cpp6
-rw-r--r--src/platform/bootstrap/CMakeLists.txt10
-rw-r--r--src/platform/graphics/cairo/CairoPainter.cpp2
-rw-r--r--src/platform/graphics/cairo/PangoFont.cpp2
-rw-r--r--src/platform/graphics/cairo/PangoTextLayout.cpp52
-rw-r--r--src/platform/gui/CMakeLists.txt1
-rw-r--r--src/platform/gui/Window.cpp6
-rw-r--r--src/platform/gui/xcb/CMakeLists.txt8
-rw-r--r--src/platform/gui/xcb/Cursor.cpp54
-rw-r--r--src/platform/gui/xcb/Keyboard.cpp12
-rw-r--r--src/platform/gui/xcb/UiApplication.cpp38
-rw-r--r--src/platform/gui/xcb/Window.cpp227
12 files changed, 381 insertions, 37 deletions
diff --git a/src/platform/bootstrap/Bootstrap.cpp b/src/platform/bootstrap/Bootstrap.cpp
index e9183550..bcf12613 100644
--- a/src/platform/bootstrap/Bootstrap.cpp
+++ b/src/platform/bootstrap/Bootstrap.cpp
@@ -1,10 +1,11 @@
#include "cru/platform/bootstrap/Bootstrap.h"
-#include "cru/base/Base.h"
#if defined(_WIN32)
#include "cru/platform/gui/win/UiApplication.h"
#elif defined(__APPLE__)
#include "cru/platform/gui/osx/UiApplication.h"
+#elif defined(__unix)
+#include "cru/platform/gui/xcb/UiApplication.h"
#else
#endif
@@ -15,7 +16,8 @@ cru::platform::gui::IUiApplication* CreateUiApplication() {
#elif defined(__APPLE__)
return new cru::platform::gui::osx::OsxUiApplication();
#else
- NotImplemented();
+ return new cru::platform::gui::xcb::XcbUiApplication();
#endif
+ NotImplemented();
}
} // namespace cru::platform::bootstrap
diff --git a/src/platform/bootstrap/CMakeLists.txt b/src/platform/bootstrap/CMakeLists.txt
index 41f1e3d3..24cdff2b 100644
--- a/src/platform/bootstrap/CMakeLists.txt
+++ b/src/platform/bootstrap/CMakeLists.txt
@@ -17,10 +17,10 @@ if(WIN32)
elseif(APPLE)
target_link_libraries(CruPlatformGraphicsBootstrap PUBLIC CruPlatformGraphicsQuartz)
target_link_libraries(CruPlatformBootstrap PUBLIC CruPlatformGuiOsx)
-elseif(EMSCRIPTEN)
- target_link_libraries(CruPlatformGraphicsBootstrap PUBLIC CruBase)
- target_link_libraries(CruPlatformBootstrap PUBLIC CruBase) # TODO: Remember to change this.
-else()
+elseif(UNIX)
target_link_libraries(CruPlatformGraphicsBootstrap PUBLIC CruPlatformGraphicsCairo)
- target_link_libraries(CruPlatformBootstrap PUBLIC CruPlatformGraphicsCairo)
+ target_link_libraries(CruPlatformBootstrap PUBLIC CruPlatformGuiXcb)
+else()
+ target_link_libraries(CruPlatformGraphicsBootstrap PUBLIC CruBase)
+ target_link_libraries(CruPlatformBootstrap PUBLIC CruBase)
endif()
diff --git a/src/platform/graphics/cairo/CairoPainter.cpp b/src/platform/graphics/cairo/CairoPainter.cpp
index b9ab50c4..8dd214cc 100644
--- a/src/platform/graphics/cairo/CairoPainter.cpp
+++ b/src/platform/graphics/cairo/CairoPainter.cpp
@@ -186,6 +186,8 @@ void CairoPainter::DrawText(const Point& offset, ITextLayout* text_layout,
cairo_save(cairo_);
cairo_set_source(cairo_, cairo_pattern);
+ cairo_move_to(cairo_, offset.x, offset.y);
+ pango_cairo_update_layout(cairo_, pango_text_layout->GetPangoLayout());
pango_cairo_show_layout(cairo_, pango_text_layout->GetPangoLayout());
cairo_restore(cairo_);
}
diff --git a/src/platform/graphics/cairo/PangoFont.cpp b/src/platform/graphics/cairo/PangoFont.cpp
index 0de17add..d5c1ad0b 100644
--- a/src/platform/graphics/cairo/PangoFont.cpp
+++ b/src/platform/graphics/cairo/PangoFont.cpp
@@ -10,7 +10,7 @@ PangoFont::PangoFont(CairoGraphicsFactory* factory, String font_family,
auto font_family_str = font_family_.ToUtf8();
pango_font_description_set_family(pango_font_description_,
font_family_str.c_str());
- pango_font_description_set_size(pango_font_description_, font_size);
+ pango_font_description_set_size(pango_font_description_, font_size * PANGO_SCALE);
}
PangoFont::~PangoFont() {
diff --git a/src/platform/graphics/cairo/PangoTextLayout.cpp b/src/platform/graphics/cairo/PangoTextLayout.cpp
index 1033ce9e..0ba7c806 100644
--- a/src/platform/graphics/cairo/PangoTextLayout.cpp
+++ b/src/platform/graphics/cairo/PangoTextLayout.cpp
@@ -6,13 +6,26 @@
#include "cru/platform/graphics/cairo/CairoGraphicsFactory.h"
#include "cru/platform/graphics/cairo/PangoFont.h"
+#include <pango/pangocairo.h>
+
namespace cru::platform::graphics::cairo {
+namespace {
+Rect ConvertFromPango(const Rect& rect) {
+ auto result = rect;
+ result.left /= PANGO_SCALE;
+ result.top /= PANGO_SCALE;
+ result.width /= PANGO_SCALE;
+ result.height /= PANGO_SCALE;
+ return result;
+}
+} // namespace
+
PangoTextLayout::PangoTextLayout(CairoGraphicsFactory* factory,
std::shared_ptr<IFont> font)
: CairoResource(factory) {
Expects(font);
font_ = CheckPlatform<PangoFont>(font, GetPlatformId());
- pango_layout_ = pango_layout_new(factory->GetDefaultPangoContext());
+ pango_layout_ = pango_cairo_create_layout(factory->GetDefaultCairo());
pango_layout_set_font_description(pango_layout_,
font_->GetPangoFontDescription());
};
@@ -37,11 +50,11 @@ void PangoTextLayout::SetFont(std::shared_ptr<IFont> font) {
}
void PangoTextLayout::SetMaxWidth(float max_width) {
- return pango_layout_set_width(pango_layout_, max_width);
+ return pango_layout_set_width(pango_layout_, max_width * PANGO_SCALE);
}
void PangoTextLayout::SetMaxHeight(float max_height) {
- return pango_layout_set_height(pango_layout_, max_height);
+ return pango_layout_set_height(pango_layout_, max_height * PANGO_SCALE);
}
bool PangoTextLayout::IsEditMode() { return edit_mode_; }
@@ -64,7 +77,7 @@ float PangoTextLayout::GetLineHeight(Index line_index) {
auto line = pango_layout_get_line_readonly(pango_layout_, line_index);
int height;
pango_layout_line_get_height(line, &height);
- return height;
+ return static_cast<float>(height) / PANGO_SCALE;
}
Index PangoTextLayout::FromUtf8IndexToUtf16Index(Index index) {
@@ -90,8 +103,9 @@ Index PangoTextLayout::FromUtf16IndexToUtf8Index(Index index) {
Rect PangoTextLayout::GetTextBounds(bool includingTrailingSpace) {
PangoRectangle rectangle;
- pango_layout_get_extents(pango_layout_, nullptr, &rectangle);
- return Rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
+ pango_layout_get_extents(pango_layout_, &rectangle, nullptr);
+ return ConvertFromPango(
+ Rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height));
}
std::vector<Rect> PangoTextLayout::TextRangeRect(const TextRange& text_range) {
@@ -108,32 +122,36 @@ std::vector<Rect> PangoTextLayout::TextRangeRect(const TextRange& text_range) {
if (start_line_index == end_line_index) {
auto line = pango_layout_get_line(pango_layout_, start_line_index);
PangoRectangle rectangle;
- pango_layout_line_get_extents(line, nullptr, &rectangle);
- return {Rect(rectangle.x + start_x_pos, rectangle.y,
- end_x_pos - start_x_pos, rectangle.height)};
+ pango_layout_line_get_extents(line, &rectangle, nullptr);
+ return {ConvertFromPango(Rect(rectangle.x + start_x_pos, rectangle.y,
+ end_x_pos - start_x_pos, rectangle.height))};
} else {
std::vector<Rect> result;
PangoRectangle rectangle;
auto start_line = pango_layout_get_line(pango_layout_, start_line_index);
- pango_layout_line_get_extents(start_line, nullptr, &rectangle);
+ pango_layout_line_get_extents(start_line, &rectangle, nullptr);
result.push_back(Rect(rectangle.x + start_x_pos, rectangle.y,
rectangle.width - start_x_pos, rectangle.height));
for (int line_index = start_line_index + 1; line_index < end_line_index;
line_index++) {
auto line = pango_layout_get_line(pango_layout_, line_index);
- pango_layout_line_get_extents(line, nullptr, &rectangle);
+ pango_layout_line_get_extents(line, &rectangle, nullptr);
result.push_back(
Rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height));
}
auto end_line = pango_layout_get_line(pango_layout_, end_line_index);
- pango_layout_line_get_extents(end_line, nullptr, &rectangle);
+ pango_layout_line_get_extents(end_line, &rectangle, nullptr);
result.push_back(
Rect(rectangle.x, rectangle.y, end_x_pos, rectangle.height));
+ for (auto& r : result) {
+ r = ConvertFromPango(r);
+ }
+
return result;
}
}
@@ -146,15 +164,17 @@ Rect PangoTextLayout::TextSinglePoint(Index position, bool trailing) {
auto line = pango_layout_get_line(pango_layout_, line_index);
PangoRectangle rectangle;
- pango_layout_line_get_extents(line, nullptr, &rectangle);
+ pango_layout_line_get_extents(line, &rectangle, nullptr);
- return Rect(rectangle.x + x_pos, rectangle.y, 0, rectangle.height);
+ return ConvertFromPango(
+ Rect(rectangle.x + x_pos, rectangle.y, 0, rectangle.height));
}
TextHitTestResult PangoTextLayout::HitTest(const Point& point) {
int index, trailing;
- auto inside_text = pango_layout_xy_to_index(pango_layout_, point.x, point.y,
- &index, &trailing);
+ auto inside_text =
+ pango_layout_xy_to_index(pango_layout_, point.x * PANGO_SCALE,
+ point.y * PANGO_SCALE, &index, &trailing);
return TextHitTestResult{FromUtf8IndexToUtf16Index(index), trailing != 0,
inside_text != 0};
}
diff --git a/src/platform/gui/CMakeLists.txt b/src/platform/gui/CMakeLists.txt
index 808caf9c..b541428e 100644
--- a/src/platform/gui/CMakeLists.txt
+++ b/src/platform/gui/CMakeLists.txt
@@ -2,6 +2,7 @@ add_library(CruPlatformGui
Keyboard.cpp
Menu.cpp
UiApplication.cpp
+ Window.cpp
)
target_link_libraries(CruPlatformGui PUBLIC CruPlatformGraphics)
target_compile_definitions(CruPlatformGui PRIVATE CRU_PLATFORM_GUI_EXPORT_API)
diff --git a/src/platform/gui/Window.cpp b/src/platform/gui/Window.cpp
new file mode 100644
index 00000000..fdcfbae4
--- /dev/null
+++ b/src/platform/gui/Window.cpp
@@ -0,0 +1,6 @@
+#include "cru/platform/gui/Window.h"
+#include "cru/base/Base.h"
+
+namespace cru::platform::gui {
+bool INativeWindow::IsCreated() { NotImplemented(); }
+} // namespace cru::platform::gui
diff --git a/src/platform/gui/xcb/CMakeLists.txt b/src/platform/gui/xcb/CMakeLists.txt
index aa6116e4..fa5cc0bf 100644
--- a/src/platform/gui/xcb/CMakeLists.txt
+++ b/src/platform/gui/xcb/CMakeLists.txt
@@ -1,11 +1,13 @@
find_library(LIBRARY_CAIRO cairo REQUIRED)
find_library(LIBRARY_XCB xcb REQUIRED)
-add_library(CruPlatformGuiX11
+find_library(LIBRARY_XCB_CURSOR xcb-cursor REQUIRED)
+add_library(CruPlatformGuiXcb
+ Cursor.cpp
Keyboard.cpp
UiApplication.cpp
Window.cpp
)
-target_link_libraries(CruPlatformGuiX11 PUBLIC
+target_link_libraries(CruPlatformGuiXcb PUBLIC
CruPlatformGui CruPlatformGraphicsCairo
- ${LIBRARY_XCB} ${LIBRARY_CAIRO}
+ ${LIBRARY_XCB} ${LIBRARY_XCB_CURSOR} ${LIBRARY_CAIRO}
)
diff --git a/src/platform/gui/xcb/Cursor.cpp b/src/platform/gui/xcb/Cursor.cpp
new file mode 100644
index 00000000..5582c6a6
--- /dev/null
+++ b/src/platform/gui/xcb/Cursor.cpp
@@ -0,0 +1,54 @@
+#include "cru/platform/gui/xcb/Cursor.h"
+#include "cru/base/Exception.h"
+#include "cru/platform/gui/Cursor.h"
+#include "cru/platform/gui/xcb/UiApplication.h"
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_cursor.h>
+#include <memory>
+
+namespace cru::platform::gui::xcb {
+XcbCursor::XcbCursor(XcbUiApplication* application, xcb_cursor_t cursor,
+ bool auto_free)
+ : application_(application), cursor_(cursor), auto_free_(auto_free) {}
+
+XcbCursor::~XcbCursor() {
+ if (auto_free_) {
+ xcb_free_cursor(application_->GetXcbConnection(), cursor_);
+ }
+}
+
+xcb_cursor_t XcbCursor::GetXcbCursor() { return cursor_; }
+
+XcbCursorManager::XcbCursorManager(XcbUiApplication* application)
+ : application_(application) {
+ auto code = xcb_cursor_context_new(application->GetXcbConnection(),
+ application->GetFirstXcbScreen(),
+ &xcb_cursor_context_);
+ if (code != 0) {
+ throw PlatformException("Failed to call xcb_cursor_context_new.");
+ }
+
+ cursors_[SystemCursorType::Arrow] =
+ std::make_shared<XcbCursor>(application_, XCB_CURSOR_NONE, false);
+ cursors_[SystemCursorType::Hand] = LoadXCursor("pointer");
+ cursors_[SystemCursorType::IBeam] = LoadXCursor("ibeam");
+}
+
+XcbCursorManager::~XcbCursorManager() {
+ xcb_cursor_context_free(xcb_cursor_context_);
+}
+
+std::shared_ptr<ICursor> XcbCursorManager::GetSystemCursor(
+ SystemCursorType type) {
+ return cursors_[type];
+}
+
+std::shared_ptr<XcbCursor> XcbCursorManager::LoadXCursor(
+ std::string_view name) {
+ return std::make_shared<XcbCursor>(
+ application_, xcb_cursor_load_cursor(xcb_cursor_context_, name.data()),
+ true);
+}
+
+} // namespace cru::platform::gui::xcb
diff --git a/src/platform/gui/xcb/Keyboard.cpp b/src/platform/gui/xcb/Keyboard.cpp
index d7559062..e5f6da8e 100644
--- a/src/platform/gui/xcb/Keyboard.cpp
+++ b/src/platform/gui/xcb/Keyboard.cpp
@@ -1,4 +1,5 @@
#include "cru/platform/gui/xcb/Keyboard.h"
+#include "cru/base/Guard.h"
#include "cru/platform/gui/Keyboard.h"
#include "cru/platform/gui/xcb/UiApplication.h"
@@ -82,8 +83,8 @@ KeyCode XorgKeycodeToCruKeyCode(XcbUiApplication *application,
// Get keyboard mapping
auto mapping_cookie = xcb_get_keyboard_mapping(connection, keycode, 1);
- auto *mapping_reply =
- xcb_get_keyboard_mapping_reply(connection, mapping_cookie, NULL);
+ auto mapping_reply = FreeLater(
+ xcb_get_keyboard_mapping_reply(connection, mapping_cookie, NULL));
if (!mapping_reply) {
throw XcbException("Cannot get keyboard mapping.");
@@ -107,7 +108,8 @@ using KeymapBitset =
KeymapBitset GetXorgKeymap(xcb_connection_t *connection) {
auto keymap_cookie = xcb_query_keymap(connection);
- auto keymap_reply = xcb_query_keymap_reply(connection, keymap_cookie, NULL);
+ auto keymap_reply =
+ FreeLater(xcb_query_keymap_reply(connection, keymap_cookie, NULL));
if (!keymap_reply) {
throw XcbException("Cannot get keymap.");
@@ -136,8 +138,8 @@ std::unordered_map<KeyCode, bool> GetKeyboardState(
// Get keyboard mapping
auto mapping_cookie = xcb_get_keyboard_mapping(connection, min_keycode,
max_keycode - min_keycode + 1);
- auto *mapping_reply =
- xcb_get_keyboard_mapping_reply(connection, mapping_cookie, NULL);
+ auto mapping_reply = FreeLater(
+ xcb_get_keyboard_mapping_reply(connection, mapping_cookie, NULL));
if (!mapping_reply) {
throw XcbException("Cannot get keyboard mapping.");
diff --git a/src/platform/gui/xcb/UiApplication.cpp b/src/platform/gui/xcb/UiApplication.cpp
index 0a6ab07f..1e5613b4 100644
--- a/src/platform/gui/xcb/UiApplication.cpp
+++ b/src/platform/gui/xcb/UiApplication.cpp
@@ -1,10 +1,15 @@
#include "cru/platform/gui/xcb/UiApplication.h"
+#include "cru/base/Base.h"
+#include "cru/base/Guard.h"
#include "cru/platform/graphics/cairo/CairoGraphicsFactory.h"
+#include "cru/platform/gui/Window.h"
+#include "cru/platform/gui/xcb/Cursor.h"
#include "cru/platform/gui/xcb/Window.h"
#include <poll.h>
#include <xcb/xcb.h>
+#include <algorithm>
namespace cru::platform::gui::xcb {
XcbUiApplication::XcbUiApplication(
@@ -20,6 +25,7 @@ XcbUiApplication::XcbUiApplication(
int screen_num;
xcb_connection_t *connection = xcb_connect(NULL, &screen_num);
+ xcb_connection_ = connection;
this->CheckXcbConnectionError();
event_loop_.SetPoll(xcb_get_file_descriptor(connection), POLLIN,
@@ -28,15 +34,23 @@ XcbUiApplication::XcbUiApplication(
const xcb_setup_t *setup = xcb_get_setup(connection);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
this->screen_ = iter.data;
+
+ cursor_manager_ = new XcbCursorManager(this);
}
XcbUiApplication::~XcbUiApplication() {
+ delete cursor_manager_;
+
xcb_disconnect(this->xcb_connection_);
if (release_cairo_factory_) {
delete cairo_factory_;
}
}
+graphics::cairo::CairoGraphicsFactory *XcbUiApplication::GetCairoFactory() {
+ return cairo_factory_;
+}
+
void XcbUiApplication::CheckXcbConnectionError() {
if (xcb_connection_has_error(this->xcb_connection_)) {
throw XcbException("xcb_connection_has_error returned non-zero.");
@@ -47,6 +61,8 @@ xcb_connection_t *XcbUiApplication::GetXcbConnection() {
return xcb_connection_;
}
+void XcbUiApplication::XcbFlush() { xcb_flush(xcb_connection_); }
+
xcb_screen_t *XcbUiApplication::GetFirstXcbScreen() { return screen_; }
xcb_atom_t XcbUiApplication::GetOrCreateXcbAtom(std::string name) {
@@ -57,7 +73,8 @@ xcb_atom_t XcbUiApplication::GetOrCreateXcbAtom(std::string name) {
auto cookie =
xcb_intern_atom(xcb_connection_, false, name.size(), name.data());
- auto reply = xcb_intern_atom_reply(xcb_connection_, cookie, nullptr);
+ auto reply =
+ FreeLater(xcb_intern_atom_reply(xcb_connection_, cookie, nullptr));
auto atom = reply->atom;
xcb_atom_.emplace(std::move(name), atom);
return atom;
@@ -122,6 +139,25 @@ void XcbUiApplication::HandleXEvents() {
}
}
+std::vector<INativeWindow *> XcbUiApplication::GetAllWindow() {
+ std::vector<INativeWindow *> windows(windows_.size());
+ std::ranges::copy(windows_, windows.begin());
+ return windows;
+}
+
+INativeWindow *XcbUiApplication::CreateWindow() { return new XcbWindow(this); }
+
+cru::platform::graphics::IGraphicsFactory *
+XcbUiApplication::GetGraphicsFactory() {
+ return cairo_factory_;
+}
+
+ICursorManager *XcbUiApplication::GetCursorManager() { return cursor_manager_; }
+
+IClipboard *XcbUiApplication::GetClipboard() { NotImplemented(); }
+
+IMenu *XcbUiApplication::GetApplicationMenu() { return nullptr; }
+
void XcbUiApplication::RegisterWindow(XcbWindow *window) {
windows_.push_back(window);
}
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