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:
parent
5e041e6e5a
commit
bda2c663da
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue