diff --git a/userspace/libraries/LibGUI/Window.cpp b/userspace/libraries/LibGUI/Window.cpp index 5983e1455b..888ffd1632 100644 --- a/userspace/libraries/LibGUI/Window.cpp +++ b/userspace/libraries/LibGUI/Window.cpp @@ -65,9 +65,6 @@ namespace LibGUI BAN::ErrorOr> Window::create(uint32_t width, uint32_t height, BAN::StringView title) { - BAN::Vector framebuffer; - TRY(framebuffer.resize(width * height, 0xFF000000)); - int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (server_fd == -1) return BAN::Error::from_errno(errno); @@ -113,6 +110,11 @@ namespace LibGUI 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, @@ -258,13 +260,46 @@ namespace LibGUI if (auto ret = packet.send_serialized(m_server_fd); ret.is_error()) { - dprintln("Failed to send packet: {}", ret.error().get_message()); + dprintln("failed to invalidate window: {}", ret.error()); return false; } return true; } + bool Window::set_position(int32_t x, int32_t y) + { + WindowPacket::WindowSetPosition packet; + packet.x = x; + packet.y = y; + + if (auto ret = packet.send_serialized(m_server_fd); ret.is_error()) + { + dprintln("failed to set window position: {}", ret.error()); + return false; + } + + return true; + } + + 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; + + if (auto ret = packet.send_serialized(m_server_fd); ret.is_error()) + { + dprintln("failed to set window attributes: {}", ret.error()); + return false; + } + + m_attributes = attributes; + return true; + } + #define TRY_OR_BREAK(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) break; e.release_value(); }) void Window::poll_events() diff --git a/userspace/libraries/LibGUI/include/LibGUI/Packet.h b/userspace/libraries/LibGUI/include/LibGUI/Packet.h index 728703a550..ac1f8b7d84 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Packet.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Packet.h @@ -160,6 +160,8 @@ namespace LibGUI WindowCreate, WindowCreateResponse, WindowInvalidate, + WindowSetPosition, + WindowSetAttributes, DestroyWindowEvent, CloseWindowEvent, @@ -179,6 +181,8 @@ namespace LibGUI ); DEFINE_PACKET(WindowCreateResponse, + uint32_t, width, + uint32_t, height, long, smo_key ); @@ -189,6 +193,18 @@ namespace LibGUI uint32_t, height ); + DEFINE_PACKET(WindowSetPosition, + int32_t, x, + int32_t, y + ); + + DEFINE_PACKET(WindowSetAttributes, + bool, title_bar, + bool, rounded_corners, + bool, movable, + bool, alpha_channel + ); + } namespace EventPacket diff --git a/userspace/libraries/LibGUI/include/LibGUI/Window.h b/userspace/libraries/LibGUI/include/LibGUI/Window.h index 8a1c19d872..c5afbd898e 100644 --- a/userspace/libraries/LibGUI/include/LibGUI/Window.h +++ b/userspace/libraries/LibGUI/include/LibGUI/Window.h @@ -13,6 +13,15 @@ namespace LibGUI class Window { + public: + struct Attributes + { + bool title_bar { true }; + bool movable { true }; + bool rounded_corners { true }; + bool alpha_channel { false }; + }; + public: ~Window(); @@ -49,6 +58,11 @@ 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_position(int32_t x, int32_t y); + + Attributes get_attributes() const { return m_attributes; } + bool set_attributes(Attributes attributes); + uint32_t width() const { return m_width; } uint32_t height() const { return m_height; } @@ -75,6 +89,8 @@ namespace LibGUI private: int m_server_fd; + Attributes m_attributes; + BAN::Vector m_framebuffer; uint32_t* m_framebuffer_smo; uint32_t m_width; diff --git a/userspace/programs/Terminal/Terminal.cpp b/userspace/programs/Terminal/Terminal.cpp index d276632707..a3bf5e7cfd 100644 --- a/userspace/programs/Terminal/Terminal.cpp +++ b/userspace/programs/Terminal/Terminal.cpp @@ -111,6 +111,11 @@ void Terminal::run() m_fg_color = s_colors_bright[7]; m_window = MUST(LibGUI::Window::create(600, 400, "Terminal"_sv)); + + auto attributes = m_window->get_attributes(); + attributes.alpha_channel = true; + m_window->set_attributes(attributes); + m_window->fill(m_bg_color); m_window->invalidate(); diff --git a/userspace/programs/WindowServer/Utils.h b/userspace/programs/WindowServer/Utils.h index f5f9206c34..914caf3251 100644 --- a/userspace/programs/WindowServer/Utils.h +++ b/userspace/programs/WindowServer/Utils.h @@ -29,6 +29,8 @@ struct Rectangle BAN::Optional get_overlap(Rectangle other) const { + if (height == 0 || width == 0 || other.width == 0 || other.height == 0) + return {}; const auto min_x = BAN::Math::max(x, other.x); const auto min_y = BAN::Math::max(y, other.y); const auto max_x = BAN::Math::min(x + width, other.x + other.width); diff --git a/userspace/programs/WindowServer/Window.cpp b/userspace/programs/WindowServer/Window.cpp index ff73ba643c..e7c0fea078 100644 --- a/userspace/programs/WindowServer/Window.cpp +++ b/userspace/programs/WindowServer/Window.cpp @@ -19,7 +19,8 @@ Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, cons m_fb_addr = static_cast(smo_map(smo_key)); ASSERT(m_fb_addr); - memset(m_fb_addr, 0, client_width() * client_height() * 4); + + memset(m_fb_addr, 0xFF, client_width() * client_height() * 4); } Window::~Window() diff --git a/userspace/programs/WindowServer/Window.h b/userspace/programs/WindowServer/Window.h index 89be7425d6..a33a00a2b9 100644 --- a/userspace/programs/WindowServer/Window.h +++ b/userspace/programs/WindowServer/Window.h @@ -6,6 +6,7 @@ #include #include +#include class Window : public BAN::RefCounted { @@ -29,9 +30,9 @@ public: Rectangle client_area() const { return m_client_area; } int32_t title_bar_x() const { return client_x(); } - int32_t title_bar_y() const { return client_y() - title_bar_height(); } + int32_t title_bar_y() const { return m_attributes.title_bar ? client_y() - title_bar_height() : client_y(); } int32_t title_bar_width() const { return client_width(); } - int32_t title_bar_height() const { return m_title_bar_height; } + int32_t title_bar_height() const { return m_attributes.title_bar ? m_title_bar_height : 0; } Rectangle title_bar_size() const { return { 0, 0, title_bar_width(), title_bar_height() }; } Rectangle title_bar_area() const { return { title_bar_x(), title_bar_y(), title_bar_width(), title_bar_height() }; } @@ -42,6 +43,9 @@ public: Rectangle full_size() const { return { 0, 0, full_width(), full_height() }; } Rectangle full_area() const { return { full_x(), full_y(), full_width(), full_height() }; } + LibGUI::Window::Attributes get_attributes() const { return m_attributes; }; + void set_attributes(LibGUI::Window::Attributes attributes) { m_attributes = attributes; }; + const uint32_t* framebuffer() const { return m_fb_addr; } uint32_t title_bar_pixel(int32_t abs_x, int32_t abs_y, Position cursor) const @@ -65,12 +69,14 @@ private: private: static constexpr int32_t m_title_bar_height { 20 }; - 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 }; + 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; + LibGUI::Window::Attributes m_attributes; + friend class BAN::RefPtr; }; diff --git a/userspace/programs/WindowServer/WindowServer.cpp b/userspace/programs/WindowServer/WindowServer.cpp index 15558cfb3c..e89ebc9920 100644 --- a/userspace/programs/WindowServer/WindowServer.cpp +++ b/userspace/programs/WindowServer/WindowServer.cpp @@ -27,7 +27,7 @@ WindowServer::WindowServer(Framebuffer& framebuffer, int32_t corner_radius) BAN::ErrorOr WindowServer::set_background_image(BAN::UniqPtr image) { if (image->width() != (uint64_t)m_framebuffer.width || image->height() != (uint64_t)m_framebuffer.height) - image = TRY(image->resize(m_framebuffer.width, m_framebuffer.height, LibImage::Image::ResizeAlgorithm::Linear)); + image = TRY(image->resize(m_framebuffer.width, m_framebuffer.height)); m_background_image = BAN::move(image); invalidate(m_framebuffer.area()); return {}; @@ -43,7 +43,10 @@ void WindowServer::on_window_create(int fd, const LibGUI::WindowPacket::WindowCr return; } - const size_t window_fb_bytes = packet.width * packet.height * 4; + 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) @@ -54,10 +57,10 @@ void WindowServer::on_window_create(int fd, const LibGUI::WindowPacket::WindowCr BAN::ScopeGuard smo_deleter([smo_key] { smo_delete(smo_key); }); Rectangle window_area { - static_cast((m_framebuffer.width - packet.width) / 2), - static_cast((m_framebuffer.height - packet.height) / 2), - static_cast(packet.width), - static_cast(packet.height) + 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) @@ -83,6 +86,8 @@ 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 = response.send_serialized(fd); ret.is_error()) { @@ -132,6 +137,60 @@ void WindowServer::on_window_invalidate(int fd, const LibGUI::WindowPacket::Wind }); } +void WindowServer::on_window_set_position(int fd, const LibGUI::WindowPacket::WindowSetPosition& 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 position while not owning a window"); + return; + } + + const auto old_client_area = target_window->full_area(); + target_window->set_position({ + .x = packet.x, + .y = packet.y, + }); + const auto new_client_area = target_window->full_area(); + invalidate(new_client_area.get_bounding_box(old_client_area)); +} + +void WindowServer::on_window_set_attributes(int fd, const LibGUI::WindowPacket::WindowSetAttributes& 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 attributes while not owning a window"); + return; + } + + 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, + }); + const auto new_client_area = target_window->full_area(); + invalidate(new_client_area.get_bounding_box(old_client_area)); +} + void WindowServer::on_key_event(LibInput::KeyEvent event) { // Mod key is not passed to clients @@ -204,7 +263,7 @@ void WindowServer::on_mouse_button(LibInput::MouseButtonEvent event) // 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); if (event.pressed && event.button == LibInput::MouseButton::Left && !m_is_moving_window && can_start_move) - m_is_moving_window = true; + m_is_moving_window = target_window->get_attributes().movable; else if (m_is_moving_window && !event.pressed) m_is_moving_window = false; else if (!event.pressed && event.button == LibInput::MouseButton::Left && target_window->close_button_area().contains(m_cursor)) @@ -354,6 +413,8 @@ void WindowServer::invalidate(Rectangle area) m_framebuffer.mmap[y * m_framebuffer.width + x] = 0xFF101010; } + // FIXME: this loop should be inverse order and terminate + // after window without alpha channel is found for (auto& pwindow : m_client_windows) { auto& window = *pwindow; @@ -426,8 +487,10 @@ void WindowServer::invalidate(Rectangle area) }; const auto is_rounded_off = - [&](Position pos) -> bool + [&](const Window& window, Position pos) -> bool { + if (!window.get_attributes().rounded_corners) + return false; for (int32_t i = 0; i < 4; i++) { if (!corner_areas[i].contains(pos)) @@ -451,7 +514,7 @@ void WindowServer::invalidate(Rectangle area) { const int32_t abs_x = title_overlap->x + x_off; const int32_t abs_y = title_overlap->y + y_off; - if (is_rounded_off({ abs_x, abs_y })) + if (is_rounded_off(window, { abs_x, abs_y })) continue; const uint32_t color = window.title_bar_pixel(abs_x, abs_y, m_cursor); @@ -479,12 +542,14 @@ void WindowServer::invalidate(Rectangle area) auto* window_row = &window.framebuffer()[src_row_y * window.client_width() + src_row_x]; auto* frameb_row = &m_framebuffer.mmap[ abs_row_y * m_framebuffer.width + abs_row_x]; + const bool should_alpha_blend = window.get_attributes().alpha_channel; for (int32_t i = 0; i < fast_overlap->width; i++) { const uint32_t color_a = *window_row; const uint32_t color_b = *frameb_row; - *frameb_row = alpha_blend(color_a, color_b); - + *frameb_row = should_alpha_blend + ? alpha_blend(color_a, color_b) + : color_a; window_row++; frameb_row++; } @@ -502,7 +567,7 @@ void WindowServer::invalidate(Rectangle area) { const int32_t abs_x = corner_overlap->x + x_off; const int32_t abs_y = corner_overlap->y + y_off; - if (is_rounded_off({ abs_x, abs_y })) + if (is_rounded_off(window, { abs_x, abs_y })) continue; const int32_t src_x = abs_x - window.client_x(); @@ -511,7 +576,10 @@ void WindowServer::invalidate(Rectangle area) const uint32_t color_a = window.framebuffer()[src_y * window.client_width() + src_x]; const uint32_t color_b = m_framebuffer.mmap[abs_y * m_framebuffer.width + abs_x]; - m_framebuffer.mmap[abs_y * m_framebuffer.width + abs_x] = alpha_blend(color_a, color_b); + const bool should_alpha_blend = window.get_attributes().alpha_channel; + m_framebuffer.mmap[abs_y * m_framebuffer.width + abs_x] = should_alpha_blend + ? alpha_blend(color_a, color_b) + : color_a; } } } diff --git a/userspace/programs/WindowServer/WindowServer.h b/userspace/programs/WindowServer/WindowServer.h index 1b8c0dcdb6..dc66e3f085 100644 --- a/userspace/programs/WindowServer/WindowServer.h +++ b/userspace/programs/WindowServer/WindowServer.h @@ -32,6 +32,8 @@ public: void on_window_create(int fd, const LibGUI::WindowPacket::WindowCreate&); 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_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 35dde3e43a..ed47836623 100644 --- a/userspace/programs/WindowServer/main.cpp +++ b/userspace/programs/WindowServer/main.cpp @@ -335,15 +335,21 @@ int main() switch (*reinterpret_cast(client_data.packet_buffer.data())) { case LibGUI::PacketType::WindowCreate: - { if (auto ret = LibGUI::WindowPacket::WindowCreate::deserialize(client_data.packet_buffer.span()); !ret.is_error()) window_server.on_window_create(fd, ret.release_value()); break; - } case LibGUI::PacketType::WindowInvalidate: if (auto ret = LibGUI::WindowPacket::WindowInvalidate::deserialize(client_data.packet_buffer.span()); !ret.is_error()) window_server.on_window_invalidate(fd, ret.release_value()); break; + case LibGUI::PacketType::WindowSetPosition: + if (auto ret = LibGUI::WindowPacket::WindowSetPosition::deserialize(client_data.packet_buffer.span()); !ret.is_error()) + window_server.on_window_set_position(fd, ret.release_value()); + break; + case LibGUI::PacketType::WindowSetAttributes: + 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; default: dprintln("unhandled packet type: {}", *reinterpret_cast(client_data.packet_buffer.data())); }