From da8170c5b61fd62f06ee437c86c8a7573668ee7b Mon Sep 17 00:00:00 2001 From: Bananymous Date: Fri, 8 Nov 2024 02:54:27 +0200 Subject: [PATCH] LibGUI: Add support for focusable windows and mouse capturing These are essential parts of a window server! This allows making TaskBar non-focusable. --- userspace/libraries/LibGUI/Window.cpp | 19 ++- .../libraries/LibGUI/include/LibGUI/Packet.h | 33 +++-- .../libraries/LibGUI/include/LibGUI/Window.h | 16 ++- userspace/programs/TaskBar/main.cpp | 4 +- userspace/programs/Terminal/Terminal.cpp | 2 +- userspace/programs/WindowServer/Window.h | 2 +- .../programs/WindowServer/WindowServer.cpp | 127 ++++++++++++++---- .../programs/WindowServer/WindowServer.h | 3 + userspace/programs/WindowServer/main.cpp | 4 + 9 files changed, 164 insertions(+), 46 deletions(-) diff --git a/userspace/libraries/LibGUI/Window.cpp b/userspace/libraries/LibGUI/Window.cpp index 888ffd16..783fdcaf 100644 --- a/userspace/libraries/LibGUI/Window.cpp +++ b/userspace/libraries/LibGUI/Window.cpp @@ -267,6 +267,20 @@ namespace LibGUI return true; } + bool Window::set_mouse_capture(bool captured) + { + WindowPacket::WindowSetMouseCapture packet; + packet.captured = captured; + + if (auto ret = packet.send_serialized(m_server_fd); ret.is_error()) + { + dprintln("failed to set mouse capture: {}", ret.error()); + return false; + } + + return true; + } + bool Window::set_position(int32_t x, int32_t y) { WindowPacket::WindowSetPosition packet; @@ -285,10 +299,7 @@ namespace LibGUI bool Window::set_attributes(Attributes attributes) { WindowPacket::WindowSetAttributes packet; - packet.title_bar = attributes.title_bar; - packet.movable = attributes.movable; - packet.rounded_corners = attributes.rounded_corners; - packet.alpha_channel = attributes.alpha_channel; + packet.attributes = attributes; if (auto ret = packet.send_serialized(m_server_fd); ret.is_error()) { diff --git a/userspace/libraries/LibGUI/include/LibGUI/Packet.h b/userspace/libraries/LibGUI/include/LibGUI/Packet.h index ac1f8b7d..5dbe7be9 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Packet.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Packet.h @@ -162,6 +162,7 @@ namespace LibGUI WindowInvalidate, WindowSetPosition, WindowSetAttributes, + WindowSetMouseCapture, DestroyWindowEvent, CloseWindowEvent, @@ -174,35 +175,49 @@ namespace LibGUI namespace WindowPacket { - DEFINE_PACKET(WindowCreate, + DEFINE_PACKET( + WindowCreate, uint32_t, width, uint32_t, height, BAN::String, title ); - DEFINE_PACKET(WindowCreateResponse, + DEFINE_PACKET( + WindowCreateResponse, uint32_t, width, uint32_t, height, long, smo_key ); - DEFINE_PACKET(WindowInvalidate, + DEFINE_PACKET( + WindowInvalidate, uint32_t, x, uint32_t, y, uint32_t, width, uint32_t, height ); - DEFINE_PACKET(WindowSetPosition, + DEFINE_PACKET( + WindowSetPosition, int32_t, x, int32_t, y ); - DEFINE_PACKET(WindowSetAttributes, - bool, title_bar, - bool, rounded_corners, - bool, movable, - bool, alpha_channel + DEFINE_PACKET_EXTRA( + WindowSetAttributes, + struct Attributes { + bool title_bar; + bool movable; + bool focusable; + bool rounded_corners; + bool alpha_channel; + }, + Attributes, attributes + ); + + DEFINE_PACKET( + WindowSetMouseCapture, + bool, captured ); } diff --git a/userspace/libraries/LibGUI/include/LibGUI/Window.h b/userspace/libraries/LibGUI/include/LibGUI/Window.h index c5afbd89..756b7ac6 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Window.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Window.h @@ -14,12 +14,14 @@ namespace LibGUI class Window { public: - struct Attributes - { - bool title_bar { true }; - bool movable { true }; - bool rounded_corners { true }; - bool alpha_channel { false }; + using Attributes = WindowPacket::WindowSetAttributes::Attributes; + + static constexpr Attributes default_attributes = { + .title_bar = true, + .movable = true, + .focusable = true, + .rounded_corners = true, + .alpha_channel = false, }; public: @@ -58,6 +60,8 @@ namespace LibGUI bool invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height); bool invalidate() { return invalidate(0, 0, width(), height()); } + bool set_mouse_capture(bool captured); + bool set_position(int32_t x, int32_t y); Attributes get_attributes() const { return m_attributes; } diff --git a/userspace/programs/TaskBar/main.cpp b/userspace/programs/TaskBar/main.cpp index e4d66155..d96e247c 100644 --- a/userspace/programs/TaskBar/main.cpp +++ b/userspace/programs/TaskBar/main.cpp @@ -13,12 +13,14 @@ int main() auto window = MUST(LibGUI::Window::create(0, font.height() + 2 * padding, "TaskBar")); - auto attributes = window->get_attributes(); + auto attributes = LibGUI::Window::default_attributes; attributes.title_bar = false; attributes.movable = false; + attributes.focusable = false; attributes.alpha_channel = false; attributes.rounded_corners = false; window->set_attributes(attributes); + window->set_close_window_event_callback([]() {}); window->set_position(0, 0); diff --git a/userspace/programs/Terminal/Terminal.cpp b/userspace/programs/Terminal/Terminal.cpp index a3bf5e7c..c87bf579 100644 --- a/userspace/programs/Terminal/Terminal.cpp +++ b/userspace/programs/Terminal/Terminal.cpp @@ -112,7 +112,7 @@ void Terminal::run() m_window = MUST(LibGUI::Window::create(600, 400, "Terminal"_sv)); - auto attributes = m_window->get_attributes(); + auto attributes = LibGUI::Window::default_attributes; attributes.alpha_channel = true; m_window->set_attributes(attributes); diff --git a/userspace/programs/WindowServer/Window.h b/userspace/programs/WindowServer/Window.h index a33a00a2..8b45188e 100644 --- a/userspace/programs/WindowServer/Window.h +++ b/userspace/programs/WindowServer/Window.h @@ -76,7 +76,7 @@ private: uint32_t* m_title_bar_data { nullptr }; BAN::String m_title; - LibGUI::Window::Attributes m_attributes; + 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 349d6b09..0d0d2ddf 100644 --- a/userspace/programs/WindowServer/WindowServer.cpp +++ b/userspace/programs/WindowServer/WindowServer.cpp @@ -181,14 +181,55 @@ void WindowServer::on_window_set_attributes(int fd, const LibGUI::WindowPacket:: } const auto old_client_area = target_window->full_area(); - target_window->set_attributes({ - .title_bar = packet.title_bar, - .movable = packet.movable, - .rounded_corners = packet.rounded_corners, - .alpha_channel = packet.alpha_channel, - }); + target_window->set_attributes(packet.attributes); const auto new_client_area = target_window->full_area(); invalidate(new_client_area.get_bounding_box(old_client_area)); + + if (!packet.attributes.focusable && m_focused_window == target_window) + { + m_focused_window = nullptr; + for (size_t i = m_client_windows.size(); i > 0; i--) + { + if (auto& window = m_client_windows[i - 1]; window->get_attributes().focusable) + { + set_focused_window(window); + break; + } + } + } +} + +void WindowServer::on_window_set_mouse_capture(int fd, const LibGUI::WindowPacket::WindowSetMouseCapture& packet) +{ + if (m_is_mouse_captured && packet.captured) + { + ASSERT(m_focused_window); + if (fd != m_focused_window->client_fd()) + dwarnln("client tried to set mouse capture while other window has it already captured"); + 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 mouse capture while not owning a window"); + return; + } + + if (packet.captured == m_is_mouse_captured) + return; + + set_focused_window(target_window); + m_is_mouse_captured = packet.captured; + invalidate(cursor_area()); } void WindowServer::on_key_event(LibInput::KeyEvent event) @@ -247,6 +288,20 @@ void WindowServer::on_key_event(LibInput::KeyEvent event) void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) { + if (m_is_mouse_captured) + { + ASSERT(m_focused_window); + + LibGUI::EventPacket::MouseButtonEvent packet; + packet.event.button = event.button; + packet.event.pressed = event.pressed; + packet.event.x = 0; + packet.event.y = 0; + if (auto ret = packet.send_serialized(m_focused_window->client_fd()); ret.is_error()) + dwarnln("could not send mouse button event: {}", ret.error()); + return; + } + BAN::RefPtr target_window; for (size_t i = m_client_windows.size(); i > 0; i--) { @@ -261,7 +316,8 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) if (!target_window) return; - set_focused_window(target_window); + if (target_window->get_attributes().focusable) + set_focused_window(target_window); // Handle window moving when mod key is held or mouse press on title bar const bool can_start_move = m_is_mod_key_held || target_window->title_text_area().contains(m_cursor); @@ -289,7 +345,7 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) packet.event.y = m_cursor.y - m_focused_window->client_y(); if (auto ret = packet.send_serialized(m_focused_window->client_fd()); ret.is_error()) { - dwarnln("could not send mouse button event event: {}", ret.error()); + dwarnln("could not send mouse button event: {}", ret.error()); return; } } @@ -297,6 +353,18 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event) { + if (m_is_mouse_captured) + { + ASSERT(m_focused_window); + + LibGUI::EventPacket::MouseMoveEvent packet; + packet.event.x = event.rel_x; + packet.event.y = -event.rel_y; + if (auto ret = packet.send_serialized(m_focused_window->client_fd()); ret.is_error()) + dwarnln("could not send mouse move event: {}", ret.error()); + return; + } + const int32_t new_x = BAN::Math::clamp(m_cursor.x + event.rel_x, 0, m_framebuffer.width); const int32_t new_y = BAN::Math::clamp(m_cursor.y - event.rel_y, 0, m_framebuffer.height); @@ -341,7 +409,7 @@ void WindowServer::on_mouse_move(LibInput::MouseMoveEvent event) packet.event.y = m_cursor.y - m_focused_window->client_y(); if (auto ret = packet.send_serialized(m_focused_window->client_fd()); ret.is_error()) { - dwarnln("could not send mouse move event event: {}", ret.error()); + dwarnln("could not send mouse move event: {}", ret.error()); return; } } @@ -355,7 +423,7 @@ void WindowServer::on_mouse_scroll(LibInput::MouseScrollEvent event) packet.event.scroll = event.scroll; if (auto ret = packet.send_serialized(m_focused_window->client_fd()); ret.is_error()) { - dwarnln("could not send mouse scroll event event: {}", ret.error()); + dwarnln("could not send mouse scroll event: {}", ret.error()); return; } } @@ -366,6 +434,12 @@ void WindowServer::set_focused_window(BAN::RefPtr window) if (m_focused_window == window) return; + if (m_is_mouse_captured) + { + m_is_mouse_captured = false; + invalidate(cursor_area()); + } + for (size_t i = m_client_windows.size(); i > 0; i--) { if (m_client_windows[i - 1] == window) @@ -589,22 +663,25 @@ void WindowServer::invalidate(Rectangle area) } } - auto cursor = cursor_area(); - if (auto overlap = cursor.get_overlap(area); overlap.has_value()) + if (!m_is_mouse_captured) { - for (int32_t y_off = 0; y_off < overlap->height; y_off++) + auto cursor = cursor_area(); + if (auto overlap = cursor.get_overlap(area); overlap.has_value()) { - for (int32_t x_off = 0; x_off < overlap->width; x_off++) + for (int32_t y_off = 0; y_off < overlap->height; y_off++) { - const int32_t rel_x = overlap->x - m_cursor.x + x_off; - const int32_t rel_y = overlap->y - m_cursor.y + y_off; - const uint32_t offset = (rel_y * s_cursor_width + rel_x) * 4; - uint32_t r = (((s_cursor_data[offset + 0] - 33) << 2) | ((s_cursor_data[offset + 1] - 33) >> 4)); - uint32_t g = ((((s_cursor_data[offset + 1] - 33) & 0xF) << 4) | ((s_cursor_data[offset + 2] - 33) >> 2)); - uint32_t b = ((((s_cursor_data[offset + 2] - 33) & 0x3) << 6) | ((s_cursor_data[offset + 3] - 33))); - uint32_t color = (r << 16) | (g << 8) | b; - if (color != 0xFF00FF) - m_framebuffer.mmap[(overlap->y + y_off) * m_framebuffer.width + (overlap->x + x_off)] = color; + for (int32_t x_off = 0; x_off < overlap->width; x_off++) + { + const int32_t rel_x = overlap->x - m_cursor.x + x_off; + const int32_t rel_y = overlap->y - m_cursor.y + y_off; + const uint32_t offset = (rel_y * s_cursor_width + rel_x) * 4; + uint32_t r = (((s_cursor_data[offset + 0] - 33) << 2) | ((s_cursor_data[offset + 1] - 33) >> 4)); + uint32_t g = ((((s_cursor_data[offset + 1] - 33) & 0xF) << 4) | ((s_cursor_data[offset + 2] - 33) >> 2)); + uint32_t b = ((((s_cursor_data[offset + 2] - 33) & 0x3) << 6) | ((s_cursor_data[offset + 3] - 33))); + uint32_t color = (r << 16) | (g << 8) | b; + if (color != 0xFF00FF) + m_framebuffer.mmap[(overlap->y + y_off) * m_framebuffer.width + (overlap->x + x_off)] = color; + } } } } @@ -618,7 +695,9 @@ void WindowServer::invalidate(Rectangle area) size_t index = (mmap_addr - reinterpret_cast(m_framebuffer.mmap)) / 4096; size_t byte = index / 8; size_t bit = index % 8; - m_pages_to_sync_bitmap[byte] |= 1 << bit; + //dprintln("{}/{}", byte, m_pages_to_sync_bitmap.size()); + if (byte < m_pages_to_sync_bitmap.size()) + m_pages_to_sync_bitmap[byte] |= 1 << bit; mmap_addr += 4096; } } diff --git a/userspace/programs/WindowServer/WindowServer.h b/userspace/programs/WindowServer/WindowServer.h index dc66e3f0..611b2ed2 100644 --- a/userspace/programs/WindowServer/WindowServer.h +++ b/userspace/programs/WindowServer/WindowServer.h @@ -34,6 +34,7 @@ public: void on_window_invalidate(int fd, const LibGUI::WindowPacket::WindowInvalidate&); 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_key_event(LibInput::KeyEvent event); void on_mouse_button(LibInput::MouseButtonEvent event); @@ -70,6 +71,8 @@ private: BAN::RefPtr m_focused_window; Position m_cursor; + bool m_is_mouse_captured { false }; + bool m_deleted_window { false }; bool m_is_stopped { false }; bool m_is_bouncing_window = false; diff --git a/userspace/programs/WindowServer/main.cpp b/userspace/programs/WindowServer/main.cpp index ed478366..0fdf3e46 100644 --- a/userspace/programs/WindowServer/main.cpp +++ b/userspace/programs/WindowServer/main.cpp @@ -350,6 +350,10 @@ int main() if (auto ret = LibGUI::WindowPacket::WindowSetAttributes::deserialize(client_data.packet_buffer.span()); !ret.is_error()) window_server.on_window_set_attributes(fd, ret.release_value()); break; + case LibGUI::PacketType::WindowSetMouseCapture: + 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; default: dprintln("unhandled packet type: {}", *reinterpret_cast(client_data.packet_buffer.data())); }