From bda2c663dae94c82ce3f3b098e50c233d2782a20 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Wed, 13 Nov 2024 19:10:15 +0200 Subject: [PATCH] WindowServer: Implement fullscreen windows If window size does not match framebuffer size, window data will be scaled to framebuffer using nearest sampling for best performance. --- userspace/libraries/LibGUI/Window.cpp | 9 ++ .../libraries/LibGUI/include/LibGUI/Packet.h | 6 ++ .../libraries/LibGUI/include/LibGUI/Window.h | 1 + .../programs/WindowServer/WindowServer.cpp | 101 +++++++++++++++++- .../programs/WindowServer/WindowServer.h | 5 + userspace/programs/WindowServer/main.cpp | 4 + 6 files changed, 125 insertions(+), 1 deletion(-) diff --git a/userspace/libraries/LibGUI/Window.cpp b/userspace/libraries/LibGUI/Window.cpp index 48a2fde1..3277a5d1 100644 --- a/userspace/libraries/LibGUI/Window.cpp +++ b/userspace/libraries/LibGUI/Window.cpp @@ -256,6 +256,15 @@ namespace LibGUI return on_socket_error(__FUNCTION__); } + void Window::set_fullscreen(bool fullscreen) + { + WindowPacket::WindowSetFullscreen packet; + packet.fullscreen = fullscreen; + + if (auto ret = packet.send_serialized(m_server_fd); ret.is_error()) + return on_socket_error(__FUNCTION__); + } + void Window::set_position(int32_t x, int32_t y) { WindowPacket::WindowSetPosition packet; diff --git a/userspace/libraries/LibGUI/include/LibGUI/Packet.h b/userspace/libraries/LibGUI/include/LibGUI/Packet.h index 6807155e..f8290cf7 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Packet.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Packet.h @@ -164,6 +164,7 @@ namespace LibGUI WindowSetAttributes, WindowSetMouseCapture, WindowSetSize, + WindowSetFullscreen, DestroyWindowEvent, CloseWindowEvent, @@ -221,6 +222,11 @@ namespace LibGUI uint32_t, height ); + DEFINE_PACKET( + WindowSetFullscreen, + bool, fullscreen + ); + } namespace EventPacket diff --git a/userspace/libraries/LibGUI/include/LibGUI/Window.h b/userspace/libraries/LibGUI/include/LibGUI/Window.h index 6c08cd09..e8f40d10 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Window.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Window.h @@ -61,6 +61,7 @@ namespace LibGUI void invalidate() { return invalidate(0, 0, width(), height()); } void set_mouse_capture(bool captured); + void set_fullscreen(bool fullscreen); void set_position(int32_t x, int32_t y); diff --git a/userspace/programs/WindowServer/WindowServer.cpp b/userspace/programs/WindowServer/WindowServer.cpp index 35650273..34180048 100644 --- a/userspace/programs/WindowServer/WindowServer.cpp +++ b/userspace/programs/WindowServer/WindowServer.cpp @@ -125,6 +125,9 @@ void WindowServer::on_window_invalidate(int fd, const LibGUI::WindowPacket::Wind void WindowServer::on_window_set_position(int fd, const LibGUI::WindowPacket::WindowSetPosition& packet) { + if (m_is_fullscreen_window && m_focused_window->client_fd() == fd) + return; + BAN::RefPtr target_window; for (auto& window : m_client_windows) { @@ -259,6 +262,45 @@ void WindowServer::on_window_set_size(int fd, const LibGUI::WindowPacket::Window invalidate(target_window->full_area().get_bounding_box(old_area)); } +void WindowServer::on_window_set_fullscreen(int fd, const LibGUI::WindowPacket::WindowSetFullscreen& packet) +{ + if (m_is_fullscreen_window) + { + ASSERT(m_focused_window); + if (m_focused_window->client_fd() != fd) + dwarnln("client tried to set fullscreen window size while another window is already fullscreen"); + else if (!packet.fullscreen) + { + m_is_fullscreen_window = false; + invalidate(m_framebuffer.area()); + } + return; + } + + if (!packet.fullscreen) + return; + + 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; + } + + m_is_fullscreen_window = true; + set_focused_window(target_window); + target_window->set_position({ 0, 0 }); + invalidate(m_framebuffer.area()); +} + void WindowServer::on_key_event(LibInput::KeyEvent event) { // Mod key is not passed to clients @@ -301,7 +343,7 @@ void WindowServer::on_key_event(LibInput::KeyEvent event) } // Toggle window bounce with F2 - if (event.pressed() && event.key == LibInput::Key::F2) + if (!m_is_fullscreen_window && event.pressed() && event.key == LibInput::Key::F2) m_is_bouncing_window = !m_is_bouncing_window; if (m_focused_window) @@ -376,6 +418,9 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) return; } } + + if (m_is_fullscreen_window) + m_is_moving_window = false; } void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event) @@ -497,6 +542,47 @@ static uint32_t alpha_blend(uint32_t color_a, uint32_t color_b) void WindowServer::invalidate(Rectangle area) { + if (m_is_fullscreen_window) + { + ASSERT(m_focused_window); + + auto focused_overlap = area.get_overlap(m_focused_window->client_area()); + if (!focused_overlap.has_value()) + return; + area = focused_overlap.release_value(); + + if (m_focused_window->client_area() == m_framebuffer.area()) + { + for (int32_t y = area.y; y < area.y + area.height; y++) + for (int32_t x = area.x; x < area.x + area.width; x++) + m_framebuffer.mmap[y * m_framebuffer.width + x] = m_focused_window->framebuffer()[y * m_focused_window->client_width() + x]; + mark_pending_sync(area); + } + else + { + const Rectangle dst_area { + .x = area.x * m_framebuffer.width / m_focused_window->client_width(), + .y = area.y * m_framebuffer.height / m_focused_window->client_height(), + .width = BAN::Math::div_round_up(area.width * m_framebuffer.width, m_focused_window->client_width()), + .height = BAN::Math::div_round_up(area.height * m_framebuffer.height, m_focused_window->client_height()) + }; + + for (int32_t dst_y = dst_area.y; dst_y < dst_area.y + dst_area.height; dst_y++) + { + for (int32_t dst_x = dst_area.x; dst_x < dst_area.x + dst_area.width; dst_x++) + { + const int32_t src_x = dst_x * m_focused_window->client_width() / m_framebuffer.width; + const int32_t src_y = dst_y * m_focused_window->client_height() / m_framebuffer.height; + m_framebuffer.mmap[dst_y * m_framebuffer.width + dst_x] = m_focused_window->framebuffer()[src_y * m_focused_window->client_width() + src_x]; + } + } + + mark_pending_sync(dst_area); + } + + return; + } + auto fb_overlap = area.get_overlap(m_framebuffer.area()); if (!fb_overlap.has_value()) return; @@ -713,6 +799,13 @@ void WindowServer::invalidate(Rectangle area) } } + mark_pending_sync(area); +} + +void WindowServer::mark_pending_sync(Rectangle area) +{ + // FIXME: this marks too many pages + const uintptr_t mmap_start = reinterpret_cast(m_framebuffer.mmap) + area.y * m_framebuffer.width * 4; const uintptr_t mmap_end = mmap_start + (area.height + 1) * m_framebuffer.width * 4; @@ -801,6 +894,12 @@ void WindowServer::remove_client_fd(int fd) return; m_client_data.remove(it); + if (m_is_fullscreen_window && m_focused_window->client_fd() == fd) + { + m_is_fullscreen_window = false; + invalidate(m_framebuffer.area()); + } + for (size_t i = 0; i < m_client_windows.size(); i++) { auto window = m_client_windows[i]; diff --git a/userspace/programs/WindowServer/WindowServer.h b/userspace/programs/WindowServer/WindowServer.h index 21d45c9f..5c3b7ca5 100644 --- a/userspace/programs/WindowServer/WindowServer.h +++ b/userspace/programs/WindowServer/WindowServer.h @@ -36,6 +36,7 @@ public: 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_window_set_fullscreen(int fd, const LibGUI::WindowPacket::WindowSetFullscreen&); void on_key_event(LibInput::KeyEvent event); void on_mouse_button(LibInput::MouseButtonEvent event); @@ -55,6 +56,9 @@ public: bool is_stopped() const { return m_is_stopped; } +private: + void mark_pending_sync(Rectangle area); + private: Framebuffer& m_framebuffer; BAN::Vector> m_client_windows; @@ -69,6 +73,7 @@ private: bool m_is_mod_key_held { false }; bool m_is_moving_window { false }; + bool m_is_fullscreen_window { false }; BAN::RefPtr m_focused_window; Position m_cursor; diff --git a/userspace/programs/WindowServer/main.cpp b/userspace/programs/WindowServer/main.cpp index 34f8e711..64b897ac 100644 --- a/userspace/programs/WindowServer/main.cpp +++ b/userspace/programs/WindowServer/main.cpp @@ -371,6 +371,10 @@ int main() 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; + case LibGUI::PacketType::WindowSetFullscreen: + if (auto ret = LibGUI::WindowPacket::WindowSetFullscreen::deserialize(client_data.packet_buffer.span()); !ret.is_error()) + window_server.on_window_set_fullscreen(fd, ret.release_value()); + break; default: dprintln("unhandled packet type: {}", *reinterpret_cast(client_data.packet_buffer.data())); }