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.
This commit is contained in:
Bananymous 2024-11-13 19:10:15 +02:00
parent 5e041e6e5a
commit bda2c663da
6 changed files with 125 additions and 1 deletions

View File

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

View File

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

View File

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

View File

@ -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<Window> 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<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
@ -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<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;
@ -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];

View File

@ -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<BAN::RefPtr<Window>> 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<Window> m_focused_window;
Position m_cursor;

View File

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