From d19264eea8eb72c46c5432ee925f42d498b6d9f9 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 13 Nov 2024 17:30:12 +0200 Subject: [PATCH] WindowServer: Implement partial window resizing This patch adds support for client side resizing, so clients can request the server to resize their windows. WindowServer will respond with resize event when and if the resizing is complete. --- userspace/libraries/LibGUI/Window.cpp | 79 +++++++++----- .../libraries/LibGUI/include/LibGUI/Packet.h | 22 ++-- .../libraries/LibGUI/include/LibGUI/Window.h | 24 +++-- userspace/programs/WindowServer/Window.cpp | 102 ++++++++++++------ userspace/programs/WindowServer/Window.h | 18 +++- .../programs/WindowServer/WindowServer.cpp | 89 +++++++++------ .../programs/WindowServer/WindowServer.h | 1 + userspace/programs/WindowServer/main.cpp | 4 + 8 files changed, 232 insertions(+), 107 deletions(-) diff --git a/userspace/libraries/LibGUI/Window.cpp b/userspace/libraries/LibGUI/Window.cpp index 5d68d769..48a2fde1 100644 --- a/userspace/libraries/LibGUI/Window.cpp +++ b/userspace/libraries/LibGUI/Window.cpp @@ -59,7 +59,7 @@ namespace LibGUI Window::~Window() { - clear(); + cleanup(); } BAN::ErrorOr> Window::create(uint32_t width, uint32_t height, BAN::StringView title) @@ -101,27 +101,13 @@ namespace LibGUI TRY(create_packet.title.append(title)); TRY(create_packet.send_serialized(server_fd)); - const auto [response_type, response_data ] = TRY(recv_packet(server_fd)); - if (response_type != PacketType::WindowCreateResponse) - return BAN::Error::from_literal("Server responded with invalid packet"); + auto window = TRY(BAN::UniqPtr::create(server_fd)); - const auto create_response = TRY(WindowPacket::WindowCreateResponse::deserialize(response_data.span())); - void* framebuffer_addr = smo_map(create_response.smo_key); - if (framebuffer_addr == nullptr) - return BAN::Error::from_errno(errno); - width = create_response.width; - height = create_response.height; - - BAN::Vector framebuffer; - TRY(framebuffer.resize(width * height, 0xFFFFFFFF)); - - auto window = TRY(BAN::UniqPtr::create( - server_fd, - static_cast(framebuffer_addr), - BAN::move(framebuffer), - width, - height - )); + bool resized = false; + window->set_resize_window_event_callback([&]() { resized = true; }); + while (!resized) + window->poll_events(); + window->set_resize_window_event_callback({}); server_closer.disable(); @@ -291,6 +277,16 @@ namespace LibGUI m_attributes = attributes; } + void Window::request_resize(uint32_t width, uint32_t height) + { + WindowPacket::WindowSetSize packet; + packet.width = width; + packet.height = height; + + if (auto ret = packet.send_serialized(m_server_fd); ret.is_error()) + return on_socket_error(__FUNCTION__); + } + void Window::on_socket_error(BAN::StringView function) { if (m_handling_socket_error) @@ -303,14 +299,42 @@ namespace LibGUI exit(1); m_socket_error_callback(); - clear(); + cleanup(); } - void Window::clear() + void Window::cleanup() { munmap(m_framebuffer_smo, m_width * m_height * 4); close(m_server_fd); - m_server_fd = -1; + } + + BAN::ErrorOr Window::handle_resize_event(const EventPacket::ResizeWindowEvent& event) + { + if (m_framebuffer_smo) + munmap(m_framebuffer_smo, m_width * m_height * 4); + m_framebuffer_smo = nullptr; + + BAN::Vector framebuffer; + TRY(framebuffer.resize(event.width * event.height, 0xFFFFFFFF)); + + void* framebuffer_addr = smo_map(event.smo_key); + if (framebuffer_addr == nullptr) + return BAN::Error::from_errno(errno); + + const uint32_t min_x = BAN::Math::min(m_width, event.width); + const uint32_t min_y = BAN::Math::min(m_height, event.height); + for (uint32_t y = 0; y < min_y; y++) + for (uint32_t x = 0; x < min_x; x++) + framebuffer[y * event.width + x] = m_framebuffer[y * m_width + x]; + + m_framebuffer_smo = static_cast(framebuffer_addr); + m_framebuffer = BAN::move(framebuffer); + m_width = event.width; + m_height = event.height; + + invalidate(); + + return {}; } #define TRY_OR_BREAK(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) break; e.release_value(); }) @@ -343,6 +367,13 @@ namespace LibGUI else exit(0); break; + case PacketType::ResizeWindowEvent: + { + MUST(handle_resize_event(TRY_OR_BREAK(EventPacket::ResizeWindowEvent::deserialize(packet_data.span())))); + if (m_resize_window_event_callback) + m_resize_window_event_callback(); + break; + } case PacketType::KeyEvent: if (m_key_event_callback) m_key_event_callback(TRY_OR_BREAK(EventPacket::KeyEvent::deserialize(packet_data.span())).event); diff --git a/userspace/libraries/LibGUI/include/LibGUI/Packet.h b/userspace/libraries/LibGUI/include/LibGUI/Packet.h index 5dbe7be9..6807155e 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Packet.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Packet.h @@ -163,9 +163,11 @@ namespace LibGUI WindowSetPosition, WindowSetAttributes, WindowSetMouseCapture, + WindowSetSize, DestroyWindowEvent, CloseWindowEvent, + ResizeWindowEvent, KeyEvent, MouseButtonEvent, MouseMoveEvent, @@ -182,13 +184,6 @@ namespace LibGUI BAN::String, title ); - DEFINE_PACKET( - WindowCreateResponse, - uint32_t, width, - uint32_t, height, - long, smo_key - ); - DEFINE_PACKET( WindowInvalidate, uint32_t, x, @@ -220,6 +215,12 @@ namespace LibGUI bool, captured ); + DEFINE_PACKET( + WindowSetSize, + uint32_t, width, + uint32_t, height + ); + } namespace EventPacket @@ -233,6 +234,13 @@ namespace LibGUI CloseWindowEvent ); + DEFINE_PACKET( + ResizeWindowEvent, + uint32_t, width, + uint32_t, height, + long, smo_key + ); + DEFINE_PACKET_EXTRA( KeyEvent, using event_t = LibInput::KeyEvent, diff --git a/userspace/libraries/LibGUI/include/LibGUI/Window.h b/userspace/libraries/LibGUI/include/LibGUI/Window.h index 2e2bea8b..6c08cd09 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Window.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Window.h @@ -67,12 +67,17 @@ namespace LibGUI Attributes get_attributes() const { return m_attributes; } void set_attributes(Attributes attributes); + // send resize request to window server + // actual resize is only done after resize callback is called + void request_resize(uint32_t width, uint32_t height); + uint32_t width() const { return m_width; } uint32_t height() const { return m_height; } void poll_events(); void set_socket_error_callback(BAN::Function callback) { m_socket_error_callback = callback; } void set_close_window_event_callback(BAN::Function callback) { m_close_window_event_callback = callback; } + void set_resize_window_event_callback(BAN::Function callback) { m_resize_window_event_callback = callback; } void set_key_event_callback(BAN::Function callback) { m_key_event_callback = callback; } void set_mouse_button_event_callback(BAN::Function callback) { m_mouse_button_event_callback = callback; } void set_mouse_move_event_callback(BAN::Function callback) { m_mouse_move_event_callback = callback; } @@ -81,33 +86,32 @@ namespace LibGUI int server_fd() const { return m_server_fd; } private: - Window(int server_fd, uint32_t* framebuffer_smo, BAN::Vector&& framebuffer, uint32_t width, uint32_t height) + Window(int server_fd) : m_server_fd(server_fd) - , m_framebuffer(framebuffer) - , m_framebuffer_smo(framebuffer_smo) - , m_width(width) - , m_height(height) { } bool clamp_to_framebuffer(int32_t& x, int32_t& y, uint32_t& width, uint32_t& height) const; void on_socket_error(BAN::StringView function); - void clear(); + void cleanup(); + + BAN::ErrorOr handle_resize_event(const EventPacket::ResizeWindowEvent&); private: - int m_server_fd; + const int m_server_fd; bool m_handling_socket_error { false }; Attributes m_attributes; BAN::Vector m_framebuffer; - uint32_t* m_framebuffer_smo; - uint32_t m_width; - uint32_t m_height; + uint32_t* m_framebuffer_smo { nullptr }; + uint32_t m_width { 0 }; + uint32_t m_height { 0 }; BAN::Function m_socket_error_callback; BAN::Function m_close_window_event_callback; + BAN::Function m_resize_window_event_callback; BAN::Function m_key_event_callback; BAN::Function m_mouse_button_event_callback; BAN::Function m_mouse_move_event_callback; diff --git a/userspace/programs/WindowServer/Window.cpp b/userspace/programs/WindowServer/Window.cpp index e7c0fea0..78f20293 100644 --- a/userspace/programs/WindowServer/Window.cpp +++ b/userspace/programs/WindowServer/Window.cpp @@ -1,6 +1,7 @@ #include "Window.h" #include +#include #include @@ -9,20 +10,6 @@ #include #include -Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font) - : m_client_fd(fd) - , m_client_area(area) - , m_smo_key(smo_key) -{ - MUST(m_title.append(title)); - prepare_title_bar(font); - - m_fb_addr = static_cast(smo_map(smo_key)); - ASSERT(m_fb_addr); - - memset(m_fb_addr, 0xFF, client_width() * client_height() * 4); -} - Window::~Window() { munmap(m_fb_addr, client_width() * client_height() * 4); @@ -33,40 +20,91 @@ Window::~Window() close(m_client_fd); } -void Window::prepare_title_bar(const LibFont::Font& font) +BAN::ErrorOr Window::initialize(BAN::StringView title, uint32_t width, uint32_t height) { - const size_t title_bar_bytes = title_bar_width() * title_bar_height() * 4; - uint32_t* title_bar_data = new uint32_t[title_bar_bytes]; - ASSERT(title_bar_data); - for (size_t i = 0; i < title_bar_bytes; i++) - title_bar_data[i] = 0xFFFFFFFF; + m_title.clear(); + TRY(m_title.append(title)); + TRY(resize(width, height)); + return {}; +} + +BAN::ErrorOr Window::resize(uint32_t width, uint32_t height) +{ + const size_t fb_bytes = width * height * 4; + + long smo_key = smo_create(fb_bytes, PROT_READ | PROT_WRITE); + if (smo_key == -1) + return BAN::Error::from_errno(errno); + BAN::ScopeGuard smo_deleter([&]() { smo_delete(smo_key); }); + + uint32_t* fb_addr = static_cast(smo_map(smo_key)); + if (fb_addr == nullptr) + return BAN::Error::from_errno(errno); + memset(fb_addr, 0xFF, fb_bytes); + BAN::ScopeGuard smo_unmapper([&]() { munmap(fb_addr, fb_bytes); }); + + { + const auto old_area = m_client_area; + + m_client_area.width = width; + m_client_area.height = height; + auto title_bar_ret = prepare_title_bar(); + m_client_area = old_area; + + if (title_bar_ret.is_error()) + return title_bar_ret.release_error(); + } + + smo_deleter.disable(); + smo_unmapper.disable(); + + if (m_fb_addr) + munmap(m_fb_addr, client_width() * client_height() * 4); + if (m_smo_key) + smo_delete(m_smo_key); + + m_fb_addr = fb_addr; + m_smo_key = smo_key; + m_client_area.width = width; + m_client_area.height = height; + + return {}; +} + +BAN::ErrorOr Window::prepare_title_bar() +{ + const uint32_t font_w = m_font.width(); + const uint32_t font_h = m_font.height(); + const uint32_t font_p = m_font.pitch(); + + TRY(m_title_bar_data.resize(title_bar_width() * title_bar_height())); + for (auto& pixel : m_title_bar_data) + pixel = 0xFFFFFFFF; const auto text_area = title_text_area(); - for (size_t i = 0; i < m_title.size() && (i + 1) * font.width() < static_cast(text_area.width); i++) + for (size_t i = 0; i < m_title.size() && (i + 1) * font_w < static_cast(text_area.width); i++) { - const auto* glyph = font.glyph(m_title[i]); + const auto* glyph = m_font.glyph(m_title[i]); if (glyph == nullptr) continue; - const int32_t y_off = (font.height() < (uint32_t)title_bar_height()) ? (title_bar_height() - font.height()) / 2 : 0; - const int32_t x_off = y_off + i * font.width(); - for (int32_t y = 0; (uint32_t)y < font.height(); y++) + const int32_t y_off = (font_h < (uint32_t)title_bar_height()) ? (title_bar_height() - font_h) / 2 : 0; + const int32_t x_off = y_off + i * font_w; + for (int32_t y = 0; (uint32_t)y < font_h; y++) { if (y + y_off >= title_bar_height()) break; - for (int32_t x = 0; (uint32_t)x < font.width(); x++) + for (int32_t x = 0; (uint32_t)x < font_w; x++) { if (x + x_off >= text_area.width) break; - const uint8_t bitmask = 1 << (font.width() - x - 1); - if (glyph[y * font.pitch()] & bitmask) - title_bar_data[(y_off + y) * title_bar_width() + (x_off + x)] = 0xFF000000; + const uint8_t bitmask = 1 << (font_w - x - 1); + if (glyph[y * font_p] & bitmask) + m_title_bar_data[(y_off + y) * title_bar_width() + (x_off + x)] = 0xFF000000; } } } - if (m_title_bar_data) - delete[] m_title_bar_data; - m_title_bar_data = title_bar_data; + return {}; } diff --git a/userspace/programs/WindowServer/Window.h b/userspace/programs/WindowServer/Window.h index 8b45188e..2313173c 100644 --- a/userspace/programs/WindowServer/Window.h +++ b/userspace/programs/WindowServer/Window.h @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -11,7 +12,11 @@ class Window : public BAN::RefCounted { public: - Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font); + Window(int fd, const LibFont::Font& font) + : m_font(font) + , m_client_fd(fd) + { } + ~Window(); void set_position(Position position) @@ -21,6 +26,7 @@ public: } int client_fd() const { return m_client_fd; } + long smo_key() const { return m_smo_key; } int32_t client_x() const { return m_client_area.x; } int32_t client_y() const { return m_client_area.y; } @@ -63,19 +69,25 @@ public: Circle close_button_area() const { return { title_bar_x() + title_bar_width() - title_bar_height() / 2, title_bar_y() + title_bar_height() / 2, title_bar_height() * 3 / 8 }; } Rectangle title_text_area() const { return { title_bar_x(), title_bar_y(), title_bar_width() - title_bar_height(), title_bar_height() }; } + BAN::ErrorOr initialize(BAN::StringView title, uint32_t width, uint32_t height); + BAN::ErrorOr resize(uint32_t width, uint32_t height); + private: - void prepare_title_bar(const LibFont::Font& font); + BAN::ErrorOr prepare_title_bar(); private: static constexpr int32_t m_title_bar_height { 20 }; + const LibFont::Font& m_font; + const int m_client_fd { -1 }; Rectangle m_client_area { 0, 0, 0, 0 }; long m_smo_key { 0 }; uint32_t* m_fb_addr { nullptr }; - uint32_t* m_title_bar_data { nullptr }; BAN::String m_title; + BAN::Vector m_title_bar_data; + LibGUI::Window::Attributes m_attributes { LibGUI::Window::default_attributes }; friend class BAN::RefPtr; diff --git a/userspace/programs/WindowServer/WindowServer.cpp b/userspace/programs/WindowServer/WindowServer.cpp index 0d0d2ddf..35650273 100644 --- a/userspace/programs/WindowServer/WindowServer.cpp +++ b/userspace/programs/WindowServer/WindowServer.cpp @@ -43,34 +43,10 @@ void WindowServer::on_window_create(int fd, const LibGUI::WindowPacket::WindowCr return; } - const uint32_t width = packet.width ? packet.width : m_framebuffer.width; + const uint32_t width = packet.width ? packet.width : m_framebuffer.width; const uint32_t height = packet.height ? packet.height : m_framebuffer.height; - const size_t window_fb_bytes = width * height * 4; - - long smo_key = smo_create(window_fb_bytes, PROT_READ | PROT_WRITE); - if (smo_key == -1) - { - dwarnln("smo_create: {}", strerror(errno)); - return; - } - BAN::ScopeGuard smo_deleter([smo_key] { smo_delete(smo_key); }); - - Rectangle window_area { - static_cast((m_framebuffer.width - width) / 2), - static_cast((m_framebuffer.height - height) / 2), - static_cast(width), - static_cast(height) - }; - - // Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font) - auto window_or_error = (BAN::RefPtr::create( - fd, - window_area, - smo_key, - packet.title, - m_font - )); + auto window_or_error = BAN::RefPtr::create(fd, m_font); if (window_or_error.is_error()) { dwarnln("could not create window for client: {}", window_or_error.error()); @@ -85,17 +61,27 @@ void WindowServer::on_window_create(int fd, const LibGUI::WindowPacket::WindowCr } BAN::ScopeGuard window_popper([&] { m_client_windows.pop_back(); }); - LibGUI::WindowPacket::WindowCreateResponse response; - response.width = width; - response.height = height; - response.smo_key = smo_key; + if (auto ret = window->initialize(packet.title, width, height); ret.is_error()) + { + dwarnln("could not create window for client: {}", ret.error()); + return; + } + + window->set_position({ + static_cast((m_framebuffer.width - window->client_width()) / 2), + static_cast((m_framebuffer.height - window->client_height()) / 2), + }); + + LibGUI::EventPacket::ResizeWindowEvent response; + response.width = window->client_width(); + response.height = window->client_height(); + response.smo_key = window->smo_key(); if (auto ret = response.send_serialized(fd); ret.is_error()) { dwarnln("could not respond to window create request: {}", ret.error()); return; } - smo_deleter.disable(); window_popper.disable(); set_focused_window(window); @@ -232,6 +218,47 @@ void WindowServer::on_window_set_mouse_capture(int fd, const LibGUI::WindowPacke invalidate(cursor_area()); } +void WindowServer::on_window_set_size(int fd, const LibGUI::WindowPacket::WindowSetSize& packet) +{ + BAN::RefPtr target_window; + for (auto& window : m_client_windows) + { + if (window->client_fd() != fd) + continue; + target_window = window; + break; + } + + if (!target_window) + { + dwarnln("client tried to set window size while not owning a window"); + return; + } + + const auto old_area = target_window->full_area(); + + const uint32_t width = packet.width ? packet.width : m_framebuffer.width; + const uint32_t height = packet.height ? packet.height : m_framebuffer.height; + + if (auto ret = target_window->resize(width, height); ret.is_error()) + { + dwarnln("could not resize client window {}", ret.error()); + return; + } + + LibGUI::EventPacket::ResizeWindowEvent response; + response.width = target_window->client_width(); + response.height = target_window->client_height(); + response.smo_key = target_window->smo_key(); + if (auto ret = response.send_serialized(fd); ret.is_error()) + { + dwarnln("could not respond to window resize request: {}", ret.error()); + return; + } + + invalidate(target_window->full_area().get_bounding_box(old_area)); +} + void WindowServer::on_key_event(LibInput::KeyEvent event) { // Mod key is not passed to clients diff --git a/userspace/programs/WindowServer/WindowServer.h b/userspace/programs/WindowServer/WindowServer.h index 611b2ed2..21d45c9f 100644 --- a/userspace/programs/WindowServer/WindowServer.h +++ b/userspace/programs/WindowServer/WindowServer.h @@ -35,6 +35,7 @@ public: void on_window_set_position(int fd, const LibGUI::WindowPacket::WindowSetPosition&); void on_window_set_attributes(int fd, const LibGUI::WindowPacket::WindowSetAttributes&); void on_window_set_mouse_capture(int fd, const LibGUI::WindowPacket::WindowSetMouseCapture&); + void on_window_set_size(int fd, const LibGUI::WindowPacket::WindowSetSize&); void on_key_event(LibInput::KeyEvent event); void on_mouse_button(LibInput::MouseButtonEvent event); diff --git a/userspace/programs/WindowServer/main.cpp b/userspace/programs/WindowServer/main.cpp index 0fdf3e46..ca334cd9 100644 --- a/userspace/programs/WindowServer/main.cpp +++ b/userspace/programs/WindowServer/main.cpp @@ -354,6 +354,10 @@ int main() if (auto ret = LibGUI::WindowPacket::WindowSetMouseCapture::deserialize(client_data.packet_buffer.span()); !ret.is_error()) window_server.on_window_set_mouse_capture(fd, ret.release_value()); break; + case LibGUI::PacketType::WindowSetSize: + if (auto ret = LibGUI::WindowPacket::WindowSetSize::deserialize(client_data.packet_buffer.span()); !ret.is_error()) + window_server.on_window_set_size(fd, ret.release_value()); + break; default: dprintln("unhandled packet type: {}", *reinterpret_cast(client_data.packet_buffer.data())); }