Compare commits

...

6 Commits

Author SHA1 Message Date
Bananymous bda2c663da 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.
2024-11-13 19:10:15 +02:00
Bananymous 5e041e6e5a WindowServer: Make all terminating signals exit cleanly
This allows window server to restore input handling to TTY if window
server crashes on page fault or some other unexpected exception.
2024-11-13 17:32:13 +02:00
Bananymous d19264eea8 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.
2024-11-13 17:30:12 +02:00
Bananymous 64c52012df Kernel: Fix MemoryRegion overlap comparison
This made some calls to `munmap` unmap extra regions.
2024-11-13 17:26:42 +02:00
Bananymous 7542e55cb2 LibC: Add option to debug userspace malloc family calls 2024-11-10 14:03:25 +02:00
Bananymous 6bd51ac345 LibGUI: Client window now exits if it loses connection from server
This can be prevented by installing callback for socket errors.
2024-11-10 14:02:23 +02:00
11 changed files with 424 additions and 142 deletions

View File

@ -40,7 +40,7 @@ namespace Kernel
bool MemoryRegion::overlaps(vaddr_t address, size_t size) const
{
if (address + size < m_vaddr)
if (address + size <= m_vaddr)
return false;
if (address >= m_vaddr + m_size)
return false;

View File

@ -1,3 +1,5 @@
#include <BAN/Debug.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
@ -6,6 +8,8 @@
#include <sys/syscall.h>
#include <unistd.h>
#define DEBUG_MALLOC 0
static consteval size_t log_size_t(size_t value, size_t base)
{
size_t result = 0;
@ -174,6 +178,8 @@ static malloc_pool_t& pool_from_node(malloc_node_t* node)
void* malloc(size_t size)
{
dprintln_if(DEBUG_MALLOC, "malloc({})", size);
// align size to s_malloc_default_align boundary
if (size_t ret = size % s_malloc_default_align)
size += s_malloc_default_align - ret;
@ -207,6 +213,8 @@ void* malloc(size_t size)
void* realloc(void* ptr, size_t size)
{
dprintln_if(DEBUG_MALLOC, "realloc({}, {})", ptr, size);
if (ptr == nullptr)
return malloc(size);
@ -237,6 +245,8 @@ void* realloc(void* ptr, size_t size)
void free(void* ptr)
{
dprintln_if(DEBUG_MALLOC, "free({})", ptr);
if (ptr == nullptr)
return;
@ -265,6 +275,8 @@ void free(void* ptr)
void* calloc(size_t nmemb, size_t size)
{
dprintln_if(DEBUG_MALLOC, "calloc({}, {})", nmemb, size);
size_t total = nmemb * size;
if (size != 0 && total / size != nmemb)
{

View File

@ -59,8 +59,7 @@ namespace LibGUI
Window::~Window()
{
munmap(m_framebuffer_smo, m_width * m_height * 4);
close(m_server_fd);
cleanup();
}
BAN::ErrorOr<BAN::UniqPtr<Window>> Window::create(uint32_t width, uint32_t height, BAN::StringView title)
@ -102,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<Window>::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<uint32_t> framebuffer;
TRY(framebuffer.resize(width * height, 0xFFFFFFFF));
auto window = TRY(BAN::UniqPtr<Window>::create(
server_fd,
static_cast<uint32_t*>(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();
@ -244,10 +229,10 @@ namespace LibGUI
return true;
}
bool Window::invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height)
void Window::invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height)
{
if (!clamp_to_framebuffer(x, y, width, height))
return true;
return;
for (uint32_t i = 0; i < height; i++)
memcpy(&m_framebuffer_smo[(y + i) * m_width + x], &m_framebuffer[(y + i) * m_width + x], width * sizeof(uint32_t));
@ -259,56 +244,106 @@ namespace LibGUI
packet.height = height;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
{
dprintln("failed to invalidate window: {}", ret.error());
return false;
}
return true;
return on_socket_error(__FUNCTION__);
}
bool Window::set_mouse_capture(bool captured)
void 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;
return on_socket_error(__FUNCTION__);
}
bool Window::set_position(int32_t x, int32_t y)
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;
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;
return on_socket_error(__FUNCTION__);
}
bool Window::set_attributes(Attributes attributes)
void Window::set_attributes(Attributes attributes)
{
WindowPacket::WindowSetAttributes packet;
packet.attributes = attributes;
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
{
dprintln("failed to set window attributes: {}", ret.error());
return false;
}
return on_socket_error(__FUNCTION__);
m_attributes = attributes;
return true;
}
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)
return;
m_handling_socket_error = true;
dprintln("Socket error while running Window::{}", function);
if (!m_socket_error_callback)
exit(1);
m_socket_error_callback();
cleanup();
}
void Window::cleanup()
{
munmap(m_framebuffer_smo, m_width * m_height * 4);
close(m_server_fd);
}
BAN::ErrorOr<void> 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<uint32_t> 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<uint32_t*>(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(); })
@ -328,7 +363,7 @@ namespace LibGUI
auto packet_or_error = recv_packet(m_server_fd);
if (packet_or_error.is_error())
break;
return on_socket_error(__FUNCTION__);
const auto [packet_type, packet_data] = packet_or_error.release_value();
switch (packet_type)
@ -341,6 +376,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);

View File

@ -163,9 +163,12 @@ namespace LibGUI
WindowSetPosition,
WindowSetAttributes,
WindowSetMouseCapture,
WindowSetSize,
WindowSetFullscreen,
DestroyWindowEvent,
CloseWindowEvent,
ResizeWindowEvent,
KeyEvent,
MouseButtonEvent,
MouseMoveEvent,
@ -182,13 +185,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 +216,17 @@ namespace LibGUI
bool, captured
);
DEFINE_PACKET(
WindowSetSize,
uint32_t, width,
uint32_t, height
);
DEFINE_PACKET(
WindowSetFullscreen,
bool, fullscreen
);
}
namespace EventPacket
@ -233,6 +240,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,

View File

@ -57,21 +57,28 @@ namespace LibGUI
// fill_color is used when copying data outside of window bounds
void copy_horizontal_slice(int32_t dst_y, int32_t src_y, uint32_t amount, uint32_t fill_color);
bool invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height);
bool invalidate() { return invalidate(0, 0, width(), height()); }
void invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height);
void invalidate() { return invalidate(0, 0, width(), height()); }
bool set_mouse_capture(bool captured);
void set_mouse_capture(bool captured);
void set_fullscreen(bool fullscreen);
bool set_position(int32_t x, int32_t y);
void set_position(int32_t x, int32_t y);
Attributes get_attributes() const { return m_attributes; }
bool set_attributes(Attributes 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<void()> callback) { m_socket_error_callback = callback; }
void set_close_window_event_callback(BAN::Function<void()> callback) { m_close_window_event_callback = callback; }
void set_resize_window_event_callback(BAN::Function<void()> callback) { m_resize_window_event_callback = callback; }
void set_key_event_callback(BAN::Function<void(EventPacket::KeyEvent::event_t)> callback) { m_key_event_callback = callback; }
void set_mouse_button_event_callback(BAN::Function<void(EventPacket::MouseButtonEvent::event_t)> callback) { m_mouse_button_event_callback = callback; }
void set_mouse_move_event_callback(BAN::Function<void(EventPacket::MouseMoveEvent::event_t)> callback) { m_mouse_move_event_callback = callback; }
@ -80,27 +87,32 @@ namespace LibGUI
int server_fd() const { return m_server_fd; }
private:
Window(int server_fd, uint32_t* framebuffer_smo, BAN::Vector<uint32_t>&& 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 cleanup();
BAN::ErrorOr<void> 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<uint32_t> 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<void()> m_socket_error_callback;
BAN::Function<void()> m_close_window_event_callback;
BAN::Function<void()> m_resize_window_event_callback;
BAN::Function<void(EventPacket::KeyEvent::event_t)> m_key_event_callback;
BAN::Function<void(EventPacket::MouseButtonEvent::event_t)> m_mouse_button_event_callback;
BAN::Function<void(EventPacket::MouseMoveEvent::event_t)> m_mouse_move_event_callback;

View File

@ -53,8 +53,7 @@ int main()
window->fill_rect(text_x, text_y, text_w, text_h, bg_color);
window->draw_text(text, font, text_x, text_y, fg_color);
if (!window->invalidate(text_x, text_y, text_w, text_h))
is_running = false;
window->invalidate(text_x, text_y, text_w, text_h);
};
while (is_running)

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);
@ -139,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<Window> target_window;
for (auto& window : m_client_windows)
{
@ -232,6 +221,86 @@ 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_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<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;
}
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
@ -274,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)
@ -349,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)
@ -470,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;
@ -686,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<uintptr_t>(m_framebuffer.mmap) + area.y * m_framebuffer.width * 4;
const uintptr_t mmap_end = mmap_start + (area.height + 1) * m_framebuffer.width * 4;
@ -774,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];

View File

@ -35,6 +35,8 @@ 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_window_set_fullscreen(int fd, const LibGUI::WindowPacket::WindowSetFullscreen&);
void on_key_event(LibInput::KeyEvent event);
void on_mouse_button(LibInput::MouseButtonEvent event);
@ -54,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<BAN::RefPtr<Window>> m_client_windows;
@ -68,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<Window> m_focused_window;
Position m_cursor;

View File

@ -165,6 +165,19 @@ int main()
atexit([]() { tty_ctrl(STDIN_FILENO, TTY_CMD_SET, TTY_FLAG_ENABLE_INPUT); });
constexpr int non_terminating_signals[] {
SIGCHLD,
SIGCONT,
SIGSTOP,
SIGTSTP,
SIGTTIN,
SIGTTOU,
};
for (int sig = _SIGMIN; sig <= _SIGMAX; sig++)
signal(sig, exit);
for (int sig : non_terminating_signals)
signal(sig, SIG_DFL);
MUST(LibInput::KeyboardLayout::initialize());
MUST(LibInput::KeyboardLayout::get().load_from_file("/usr/share/keymaps/us.keymap"_sv));
@ -354,6 +367,14 @@ 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;
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<uint32_t*>(client_data.packet_buffer.data()));
}