mirror of
https://github.com/RGBCube/serenity
synced 2025-06-01 07:38:10 +00:00
WindowServer: Add initial support for rendering on multiple screens
This allows WindowServer to use multiple framebuffer devices and compose the desktop with any arbitrary layout. Currently, it is assumed that it is configured contiguous and non-overlapping, but this should eventually be enforced. To make rendering efficient, each window now also tracks on which screens it needs to be rendered. This way we don't have to iterate all the windows for each screen but instead use the same rendering loop and then only render to the screen (or screens) that the window actually uses.
This commit is contained in:
parent
499c33ae0c
commit
4392da970a
42 changed files with 1127 additions and 563 deletions
|
@ -93,11 +93,27 @@ Gfx::Font const& WindowManager::window_title_font() const
|
|||
return Gfx::FontDatabase::default_font().bold_variant();
|
||||
}
|
||||
|
||||
bool WindowManager::set_resolution(int width, int height, int scale)
|
||||
bool WindowManager::set_resolution(Screen& screen, int width, int height, int scale)
|
||||
{
|
||||
bool success = Compositor::the().set_resolution(width, height, scale);
|
||||
auto screen_rect = screen.rect();
|
||||
if (screen_rect.width() == width && screen_rect.height() == height && screen.scale_factor() == scale)
|
||||
return true;
|
||||
|
||||
// Make sure it's impossible to set an invalid resolution
|
||||
if (!(width >= 640 && height >= 480 && scale >= 1)) {
|
||||
dbgln("Compositor: Tried to set invalid resolution: {}x{}", width, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto old_scale_factor = screen.scale_factor();
|
||||
bool success = screen.set_resolution(width, height, scale);
|
||||
if (success && old_scale_factor != scale)
|
||||
reload_icon_bitmaps_after_scale_change();
|
||||
|
||||
Compositor::the().screen_resolution_changed();
|
||||
|
||||
ClientConnection::for_each_client([&](ClientConnection& client) {
|
||||
client.notify_about_new_screen_rect(Screen::the().rect());
|
||||
client.notify_about_new_screen_rects(Screen::rects(), Screen::main().index());
|
||||
});
|
||||
if (success) {
|
||||
m_window_stack.for_each_window([](Window& window) {
|
||||
|
@ -113,9 +129,9 @@ bool WindowManager::set_resolution(int width, int height, int scale)
|
|||
m_config->write_num_entry("Screen", "ScaleFactor", scale);
|
||||
m_config->sync();
|
||||
} else {
|
||||
dbgln("Saving fallback resolution: {} @1x to config file at {}", resolution(), m_config->filename());
|
||||
m_config->write_num_entry("Screen", "Width", resolution().width());
|
||||
m_config->write_num_entry("Screen", "Height", resolution().height());
|
||||
dbgln("Saving fallback resolution: {} @1x to config file at {}", screen.size(), m_config->filename());
|
||||
m_config->write_num_entry("Screen", "Width", screen.size().width());
|
||||
m_config->write_num_entry("Screen", "Height", screen.size().height());
|
||||
m_config->write_num_entry("Screen", "ScaleFactor", 1);
|
||||
m_config->sync();
|
||||
}
|
||||
|
@ -123,14 +139,9 @@ bool WindowManager::set_resolution(int width, int height, int scale)
|
|||
return success;
|
||||
}
|
||||
|
||||
Gfx::IntSize WindowManager::resolution() const
|
||||
{
|
||||
return Screen::the().size();
|
||||
}
|
||||
|
||||
void WindowManager::set_acceleration_factor(double factor)
|
||||
{
|
||||
Screen::the().set_acceleration_factor(factor);
|
||||
ScreenInput::the().set_acceleration_factor(factor);
|
||||
dbgln("Saving acceleration factor {} to config file at {}", factor, m_config->filename());
|
||||
m_config->write_entry("Mouse", "AccelerationFactor", String::formatted("{}", factor));
|
||||
m_config->sync();
|
||||
|
@ -138,7 +149,7 @@ void WindowManager::set_acceleration_factor(double factor)
|
|||
|
||||
void WindowManager::set_scroll_step_size(unsigned step_size)
|
||||
{
|
||||
Screen::the().set_scroll_step_size(step_size);
|
||||
ScreenInput::the().set_scroll_step_size(step_size);
|
||||
dbgln("Saving scroll step size {} to config file at {}", step_size, m_config->filename());
|
||||
m_config->write_entry("Mouse", "ScrollStepSize", String::number(step_size));
|
||||
m_config->sync();
|
||||
|
@ -158,11 +169,6 @@ int WindowManager::double_click_speed() const
|
|||
return m_double_click_speed;
|
||||
}
|
||||
|
||||
int WindowManager::scale_factor() const
|
||||
{
|
||||
return Screen::the().scale_factor();
|
||||
}
|
||||
|
||||
void WindowManager::add_window(Window& window)
|
||||
{
|
||||
bool is_first_window = m_window_stack.is_empty();
|
||||
|
@ -170,8 +176,9 @@ void WindowManager::add_window(Window& window)
|
|||
m_window_stack.add(window);
|
||||
|
||||
if (window.is_fullscreen()) {
|
||||
Core::EventLoop::current().post_event(window, make<ResizeEvent>(Screen::the().rect()));
|
||||
window.set_rect(Screen::the().rect());
|
||||
auto& screen = Screen::main(); // TODO: support fullscreen windows on other screens!
|
||||
Core::EventLoop::current().post_event(window, make<ResizeEvent>(screen.rect()));
|
||||
window.set_rect(screen.rect());
|
||||
}
|
||||
|
||||
if (window.type() != WindowType::Desktop || is_first_window)
|
||||
|
@ -558,8 +565,9 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event)
|
|||
|
||||
const int tiling_deadzone = 10;
|
||||
const int secondary_deadzone = 2;
|
||||
auto desktop = desktop_rect();
|
||||
|
||||
auto& cursor_screen = Screen::closest_to_location(event.position());
|
||||
auto desktop = desktop_rect(cursor_screen);
|
||||
auto desktop_relative_to_screen = desktop.translated(-cursor_screen.rect().location());
|
||||
if (m_move_window->is_maximized()) {
|
||||
auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin);
|
||||
if (pixels_moved_from_start > 5) {
|
||||
|
@ -574,31 +582,32 @@ bool WindowManager::process_ongoing_window_move(MouseEvent& event)
|
|||
bool is_resizable = m_move_window->is_resizable();
|
||||
auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin);
|
||||
|
||||
if (is_resizable && event.x() <= tiling_deadzone) {
|
||||
if (event.y() <= tiling_deadzone + desktop.top())
|
||||
m_move_window->set_tiled(WindowTileType::TopLeft);
|
||||
else if (event.y() >= desktop.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(WindowTileType::BottomLeft);
|
||||
auto event_location_relative_to_screen = event.position().translated(-cursor_screen.rect().location());
|
||||
if (is_resizable && event_location_relative_to_screen.x() <= tiling_deadzone) {
|
||||
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop_relative_to_screen.top())
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::TopLeft);
|
||||
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::BottomLeft);
|
||||
else
|
||||
m_move_window->set_tiled(WindowTileType::Left);
|
||||
} else if (is_resizable && event.x() >= Screen::the().width() - tiling_deadzone) {
|
||||
if (event.y() <= tiling_deadzone + desktop.top())
|
||||
m_move_window->set_tiled(WindowTileType::TopRight);
|
||||
else if (event.y() >= desktop.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(WindowTileType::BottomRight);
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Left);
|
||||
} else if (is_resizable && event_location_relative_to_screen.x() >= cursor_screen.width() - tiling_deadzone) {
|
||||
if (event_location_relative_to_screen.y() <= tiling_deadzone + desktop.top())
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::TopRight);
|
||||
else if (event_location_relative_to_screen.y() >= desktop_relative_to_screen.height() - tiling_deadzone)
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::BottomRight);
|
||||
else
|
||||
m_move_window->set_tiled(WindowTileType::Right);
|
||||
} else if (is_resizable && event.y() <= secondary_deadzone + desktop.top()) {
|
||||
m_move_window->set_tiled(WindowTileType::Top);
|
||||
} else if (is_resizable && event.y() >= desktop.bottom() - secondary_deadzone) {
|
||||
m_move_window->set_tiled(WindowTileType::Bottom);
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Right);
|
||||
} else if (is_resizable && event_location_relative_to_screen.y() <= secondary_deadzone + desktop_relative_to_screen.top()) {
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Top);
|
||||
} else if (is_resizable && event_location_relative_to_screen.y() >= desktop_relative_to_screen.bottom() - secondary_deadzone) {
|
||||
m_move_window->set_tiled(&cursor_screen, WindowTileType::Bottom);
|
||||
} else if (m_move_window->tiled() == WindowTileType::None) {
|
||||
Gfx::IntPoint pos = m_move_window_origin.translated(event.position() - m_move_origin);
|
||||
m_move_window->set_position_without_repaint(pos);
|
||||
// "Bounce back" the window if it would end up too far outside the screen.
|
||||
// If the user has let go of Mod_Super, maybe they didn't intentionally press it to begin with. Therefore, refuse to go into a state where knowledge about super-drags is necessary.
|
||||
bool force_titlebar_visible = !(m_keyboard_modifiers & Mod_Super);
|
||||
m_move_window->nudge_into_desktop(force_titlebar_visible);
|
||||
m_move_window->nudge_into_desktop(&cursor_screen, force_titlebar_visible);
|
||||
} else if (pixels_moved_from_start > 5) {
|
||||
m_move_window->set_untiled(event.position());
|
||||
m_move_origin = event.position();
|
||||
|
@ -1067,7 +1076,7 @@ void WindowManager::reevaluate_hovered_window(Window* updated_window)
|
|||
if (m_dnd_client || m_resize_window || m_move_window || m_cursor_tracking_button || MenuManager::the().has_open_menu())
|
||||
return;
|
||||
|
||||
auto cursor_location = Screen::the().cursor_location();
|
||||
auto cursor_location = ScreenInput::the().cursor_location();
|
||||
auto* currently_hovered = hovered_window();
|
||||
if (updated_window) {
|
||||
if (!(updated_window == currently_hovered || updated_window->frame().rect().contains(cursor_location) || (currently_hovered && currently_hovered->frame().rect().contains(cursor_location))))
|
||||
|
@ -1110,32 +1119,31 @@ void WindowManager::clear_resize_candidate()
|
|||
m_resize_candidate = nullptr;
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowManager::desktop_rect() const
|
||||
Gfx::IntRect WindowManager::desktop_rect(Screen& screen) const
|
||||
{
|
||||
if (active_fullscreen_window())
|
||||
return Screen::the().rect();
|
||||
return {
|
||||
0,
|
||||
0,
|
||||
Screen::the().width(),
|
||||
Screen::the().height() - 28
|
||||
};
|
||||
return Screen::main().rect(); // TODO: we should support fullscreen windows on any screen
|
||||
auto screen_rect = screen.rect();
|
||||
if (screen.is_main_screen())
|
||||
screen_rect.set_height(screen.height() - 28);
|
||||
return screen_rect;
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowManager::arena_rect_for_type(WindowType type) const
|
||||
Gfx::IntRect WindowManager::arena_rect_for_type(Screen& screen, WindowType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case WindowType::Desktop:
|
||||
return Screen::bounding_rect();
|
||||
case WindowType::Normal:
|
||||
case WindowType::ToolWindow:
|
||||
return desktop_rect();
|
||||
return desktop_rect(screen);
|
||||
case WindowType::Menu:
|
||||
case WindowType::WindowSwitcher:
|
||||
case WindowType::Taskbar:
|
||||
case WindowType::Tooltip:
|
||||
case WindowType::Applet:
|
||||
case WindowType::Notification:
|
||||
return Screen::the().rect();
|
||||
return screen.rect();
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
@ -1233,7 +1241,7 @@ void WindowManager::process_key_event(KeyEvent& event)
|
|||
}
|
||||
if (m_active_input_window->is_maximized())
|
||||
maximize_windows(*m_active_input_window, false);
|
||||
m_active_input_window->set_tiled(WindowTileType::Left);
|
||||
m_active_input_window->set_tiled(nullptr, WindowTileType::Left);
|
||||
return;
|
||||
}
|
||||
if (event.key() == Key_Right) {
|
||||
|
@ -1245,7 +1253,7 @@ void WindowManager::process_key_event(KeyEvent& event)
|
|||
}
|
||||
if (m_active_input_window->is_maximized())
|
||||
maximize_windows(*m_active_input_window, false);
|
||||
m_active_input_window->set_tiled(WindowTileType::Right);
|
||||
m_active_input_window->set_tiled(nullptr, WindowTileType::Right);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1448,24 +1456,29 @@ ResizeDirection WindowManager::resize_direction_of_window(Window const& window)
|
|||
return m_resize_direction;
|
||||
}
|
||||
|
||||
Gfx::IntRect WindowManager::maximized_window_rect(Window const& window) const
|
||||
Gfx::IntRect WindowManager::maximized_window_rect(Window const& window, bool relative_to_window_screen) const
|
||||
{
|
||||
Gfx::IntRect rect = Screen::the().rect();
|
||||
auto& screen = Screen::closest_to_rect(window.frame().rect());
|
||||
Gfx::IntRect rect = screen.rect();
|
||||
|
||||
// Subtract window title bar (leaving the border)
|
||||
rect.set_y(rect.y() + window.frame().titlebar_rect().height() + window.frame().menubar_rect().height());
|
||||
rect.set_height(rect.height() - window.frame().titlebar_rect().height() - window.frame().menubar_rect().height());
|
||||
|
||||
// Subtract taskbar window height if present
|
||||
const_cast<WindowManager*>(this)->m_window_stack.for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, [&rect](Window& taskbar_window) {
|
||||
rect.set_height(rect.height() - taskbar_window.height());
|
||||
return IterationDecision::Break;
|
||||
});
|
||||
if (screen.is_main_screen()) {
|
||||
// Subtract taskbar window height if present
|
||||
const_cast<WindowManager*>(this)->m_window_stack.for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, [&rect](Window& taskbar_window) {
|
||||
rect.set_height(rect.height() - taskbar_window.height());
|
||||
return IterationDecision::Break;
|
||||
});
|
||||
}
|
||||
|
||||
constexpr int tasteful_space_above_maximized_window = 1;
|
||||
rect.set_y(rect.y() + tasteful_space_above_maximized_window);
|
||||
rect.set_height(rect.height() - tasteful_space_above_maximized_window);
|
||||
|
||||
if (relative_to_window_screen)
|
||||
rect.translate_by(-screen.rect().location());
|
||||
return rect;
|
||||
}
|
||||
|
||||
|
@ -1577,9 +1590,10 @@ Gfx::IntPoint WindowManager::get_recommended_window_position(Gfx::IntPoint const
|
|||
|
||||
Gfx::IntPoint point;
|
||||
if (overlap_window) {
|
||||
auto& screen = Screen::closest_to_location(desired);
|
||||
point = overlap_window->position() + shift;
|
||||
point = { point.x() % Screen::the().width(),
|
||||
(point.y() >= (Screen::the().height() - taskbar_height))
|
||||
point = { point.x() % screen.width(),
|
||||
(point.y() >= (screen.height() - (screen.is_main_screen() ? taskbar_height : 0)))
|
||||
? Gfx::WindowTheme::current().titlebar_height(Gfx::WindowTheme::WindowType::Normal, palette())
|
||||
: point.y() };
|
||||
} else {
|
||||
|
@ -1593,7 +1607,7 @@ int WindowManager::compositor_icon_scale() const
|
|||
{
|
||||
if (!m_allow_hidpi_icons)
|
||||
return 1;
|
||||
return scale_factor();
|
||||
return Screen::main().scale_factor(); // TODO: There is no *one* scale factor...
|
||||
}
|
||||
|
||||
void WindowManager::reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icons)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue