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())); }