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.
This commit is contained in:
2024-11-13 17:30:12 +02:00
parent 64c52012df
commit d19264eea8
8 changed files with 232 additions and 107 deletions

View File

@@ -1,6 +1,7 @@
#include "Window.h"
#include <BAN/Debug.h>
#include <BAN/ScopeGuard.h>
#include <LibGUI/Window.h>
@@ -9,20 +10,6 @@
#include <sys/socket.h>
#include <unistd.h>
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<uint32_t*>(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<void> 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<void> 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<uint32_t*>(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<void> 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<uint32_t>(text_area.width); i++)
for (size_t i = 0; i < m_title.size() && (i + 1) * font_w < static_cast<uint32_t>(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 {};
}

View File

@@ -4,6 +4,7 @@
#include <BAN/RefPtr.h>
#include <BAN/String.h>
#include <BAN/Vector.h>
#include <LibFont/Font.h>
#include <LibGUI/Window.h>
@@ -11,7 +12,11 @@
class Window : public BAN::RefCounted<Window>
{
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<void> initialize(BAN::StringView title, uint32_t width, uint32_t height);
BAN::ErrorOr<void> resize(uint32_t width, uint32_t height);
private:
void prepare_title_bar(const LibFont::Font& font);
BAN::ErrorOr<void> 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<uint32_t> m_title_bar_data;
LibGUI::Window::Attributes m_attributes { LibGUI::Window::default_attributes };
friend class BAN::RefPtr<Window>;

View File

@@ -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<int32_t>((m_framebuffer.width - width) / 2),
static_cast<int32_t>((m_framebuffer.height - height) / 2),
static_cast<int32_t>(width),
static_cast<int32_t>(height)
};
// Window::Window(int fd, Rectangle area, long smo_key, BAN::StringView title, const LibFont::Font& font)
auto window_or_error = (BAN::RefPtr<Window>::create(
fd,
window_area,
smo_key,
packet.title,
m_font
));
auto window_or_error = BAN::RefPtr<Window>::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<int32_t>((m_framebuffer.width - window->client_width()) / 2),
static_cast<int32_t>((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<Window> 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

View File

@@ -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);

View File

@@ -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<uint32_t*>(client_data.packet_buffer.data()));
}