mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-29 06:22:53 +00:00
WindowServer: Re-use existing Screen instances and improve fallbacks
If a screen layout cannot be applied, instead of failing to start WindowServer try to fall back to an auto-generated screen layout with the devices that are detected. Also, be a bit smarter about changing the current screen layout. Instead of closing all framebuffers and bringing them back up, keep what we can and only change resolution on those that we need to change them on. To make this work we also need to move away from using an array of structures to hold compositor related per-screen data to attaching it to the Screen itself, which makes re-using a screen much simpler.
This commit is contained in:
parent
1ecb725357
commit
dbb9f891fb
Notes:
sideshowbarker
2024-07-18 08:39:04 +09:00
Author: https://github.com/tomuta
Commit: dbb9f891fb
Pull-request: https://github.com/SerenityOS/serenity/pull/8859
7 changed files with 483 additions and 190 deletions
|
@ -75,33 +75,11 @@ const Gfx::Bitmap* Compositor::cursor_bitmap_for_screenshot(Badge<ClientConnecti
|
|||
|
||||
const Gfx::Bitmap& Compositor::front_bitmap_for_screenshot(Badge<ClientConnection>, Screen& screen) const
|
||||
{
|
||||
return *m_screen_data[screen.index()].m_front_bitmap;
|
||||
return *screen.compositor_screen_data().m_front_bitmap;
|
||||
}
|
||||
|
||||
void Compositor::ScreenData::init_bitmaps(Compositor& compositor, Screen& screen)
|
||||
void CompositorScreenData::init_bitmaps(Compositor& compositor, Screen& screen)
|
||||
{
|
||||
m_has_flipped = false;
|
||||
m_have_flush_rects = false;
|
||||
m_buffers_are_flipped = false;
|
||||
m_screen_can_set_buffer = screen.can_set_buffer();
|
||||
|
||||
auto size = screen.size();
|
||||
|
||||
m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(0, 0));
|
||||
m_front_painter = make<Gfx::Painter>(*m_front_bitmap);
|
||||
m_front_painter->translate(-screen.rect().location());
|
||||
|
||||
if (m_screen_can_set_buffer)
|
||||
m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(1, 0));
|
||||
else
|
||||
m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor());
|
||||
m_back_painter = make<Gfx::Painter>(*m_back_bitmap);
|
||||
m_back_painter->translate(-screen.rect().location());
|
||||
|
||||
m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor());
|
||||
m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap);
|
||||
m_temp_painter->translate(-screen.rect().location());
|
||||
|
||||
// Recreate the screen-number overlay as the Screen instances may have changed, or get rid of it if we no longer need it
|
||||
if (compositor.showing_screen_numbers()) {
|
||||
m_screen_number_overlay = compositor.create_overlay<ScreenNumberOverlay>(screen);
|
||||
|
@ -109,13 +87,40 @@ void Compositor::ScreenData::init_bitmaps(Compositor& compositor, Screen& screen
|
|||
} else {
|
||||
m_screen_number_overlay = nullptr;
|
||||
}
|
||||
|
||||
m_has_flipped = false;
|
||||
m_have_flush_rects = false;
|
||||
m_buffers_are_flipped = false;
|
||||
m_screen_can_set_buffer = screen.can_set_buffer();
|
||||
|
||||
m_flush_rects.clear_with_capacity();
|
||||
m_flush_transparent_rects.clear_with_capacity();
|
||||
m_flush_special_rects.clear_with_capacity();
|
||||
|
||||
auto size = screen.size();
|
||||
m_front_bitmap = nullptr;
|
||||
m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(0, 0));
|
||||
m_front_painter = make<Gfx::Painter>(*m_front_bitmap);
|
||||
m_front_painter->translate(-screen.rect().location());
|
||||
|
||||
m_back_bitmap = nullptr;
|
||||
if (m_screen_can_set_buffer)
|
||||
m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(1, 0));
|
||||
else
|
||||
m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor());
|
||||
m_back_painter = make<Gfx::Painter>(*m_back_bitmap);
|
||||
m_back_painter->translate(-screen.rect().location());
|
||||
|
||||
m_temp_bitmap = nullptr;
|
||||
m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor());
|
||||
m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap);
|
||||
m_temp_painter->translate(-screen.rect().location());
|
||||
}
|
||||
|
||||
void Compositor::init_bitmaps()
|
||||
{
|
||||
m_screen_data.resize(Screen::count());
|
||||
Screen::for_each([&](auto& screen) {
|
||||
m_screen_data[screen.index()].init_bitmaps(*this, screen);
|
||||
screen.compositor_screen_data().init_bitmaps(*this, screen);
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
|
@ -231,12 +236,14 @@ void Compositor::compose()
|
|||
|
||||
auto& cursor_screen = ScreenInput::the().cursor_location_screen();
|
||||
|
||||
for (auto& screen_data : m_screen_data) {
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
screen_data.m_have_flush_rects = false;
|
||||
screen_data.m_flush_rects.clear_with_capacity();
|
||||
screen_data.m_flush_transparent_rects.clear_with_capacity();
|
||||
screen_data.m_flush_special_rects.clear_with_capacity();
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
auto cursor_rect = current_cursor_rect();
|
||||
|
||||
|
@ -247,8 +254,7 @@ void Compositor::compose()
|
|||
if (&screen == &cursor_screen && !previous_cursor_screen && !need_to_draw_cursor && rect.intersects(cursor_rect)) {
|
||||
// Restore what's behind the cursor if anything touches the area of the cursor
|
||||
need_to_draw_cursor = true;
|
||||
auto& screen_data = m_screen_data[cursor_screen.index()];
|
||||
if (screen_data.restore_cursor_back(cursor_screen, previous_cursor_rect))
|
||||
if (cursor_screen.compositor_screen_data().restore_cursor_back(cursor_screen, previous_cursor_rect))
|
||||
previous_cursor_screen = &screen;
|
||||
}
|
||||
};
|
||||
|
@ -257,15 +263,14 @@ void Compositor::compose()
|
|||
// Cursor moved to another screen, restore on the cursor's background on the previous screen
|
||||
need_to_draw_cursor = true;
|
||||
if (m_current_cursor_screen) {
|
||||
auto& screen_data = m_screen_data[m_current_cursor_screen->index()];
|
||||
if (screen_data.restore_cursor_back(*m_current_cursor_screen, previous_cursor_rect))
|
||||
if (m_current_cursor_screen->compositor_screen_data().restore_cursor_back(*m_current_cursor_screen, previous_cursor_rect))
|
||||
previous_cursor_screen = m_current_cursor_screen;
|
||||
}
|
||||
m_current_cursor_screen = &cursor_screen;
|
||||
}
|
||||
|
||||
auto prepare_rect = [&](Screen& screen, const Gfx::IntRect& rect) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
dbgln_if(COMPOSE_DEBUG, " -> flush opaque: {}", rect);
|
||||
VERIFY(!screen_data.m_flush_rects.intersects(rect));
|
||||
VERIFY(!screen_data.m_flush_transparent_rects.intersects(rect));
|
||||
|
@ -275,7 +280,7 @@ void Compositor::compose()
|
|||
};
|
||||
|
||||
auto prepare_transparency_rect = [&](Screen& screen, const Gfx::IntRect& rect) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
dbgln_if(COMPOSE_DEBUG, " -> flush transparent: {}", rect);
|
||||
VERIFY(!screen_data.m_flush_rects.intersects(rect));
|
||||
for (auto& r : screen_data.m_flush_transparent_rects.rects()) {
|
||||
|
@ -288,7 +293,7 @@ void Compositor::compose()
|
|||
check_restore_cursor_back(screen, rect);
|
||||
};
|
||||
|
||||
if (!m_screen_data[cursor_screen.index()].m_cursor_back_bitmap || m_invalidated_cursor)
|
||||
if (!cursor_screen.compositor_screen_data().m_cursor_back_bitmap || m_invalidated_cursor)
|
||||
check_restore_cursor_back(cursor_screen, cursor_rect);
|
||||
|
||||
auto paint_wallpaper = [&](Screen& screen, Gfx::Painter& painter, const Gfx::IntRect& rect, const Gfx::IntRect& screen_rect) {
|
||||
|
@ -319,7 +324,7 @@ void Compositor::compose()
|
|||
auto screen_rect = screen.rect();
|
||||
auto screen_render_rect = screen_rect.intersected(render_rect);
|
||||
if (!screen_render_rect.is_empty()) {
|
||||
auto& back_painter = *m_screen_data[screen.index()].m_back_painter;
|
||||
auto& back_painter = *screen.compositor_screen_data().m_back_painter;
|
||||
dbgln_if(COMPOSE_DEBUG, " render wallpaper opaque: {} on screen #{}", screen_render_rect, screen.index());
|
||||
prepare_rect(screen, render_rect);
|
||||
paint_wallpaper(screen, back_painter, render_rect, screen_rect);
|
||||
|
@ -450,7 +455,7 @@ void Compositor::compose()
|
|||
dbgln_if(COMPOSE_DEBUG, " render opaque: {} on screen #{}", screen_render_rect, screen->index());
|
||||
|
||||
prepare_rect(*screen, screen_render_rect);
|
||||
auto& back_painter = *m_screen_data[screen->index()].m_back_painter;
|
||||
auto& back_painter = *screen->compositor_screen_data().m_back_painter;
|
||||
Gfx::PainterStateSaver saver(back_painter);
|
||||
back_painter.add_clip_rect(screen_render_rect);
|
||||
compose_window_rect(*screen, back_painter, screen_render_rect);
|
||||
|
@ -471,7 +476,7 @@ void Compositor::compose()
|
|||
continue;
|
||||
dbgln_if(COMPOSE_DEBUG, " render wallpaper: {} on screen #{}", screen_render_rect, screen->index());
|
||||
|
||||
auto& temp_painter = *m_screen_data[screen->index()].m_temp_painter;
|
||||
auto& temp_painter = *screen->compositor_screen_data().m_temp_painter;
|
||||
prepare_transparency_rect(*screen, screen_render_rect);
|
||||
paint_wallpaper(*screen, temp_painter, screen_render_rect, screen_rect);
|
||||
}
|
||||
|
@ -489,7 +494,7 @@ void Compositor::compose()
|
|||
dbgln_if(COMPOSE_DEBUG, " render transparent: {} on screen #{}", screen_render_rect, screen->index());
|
||||
|
||||
prepare_transparency_rect(*screen, screen_render_rect);
|
||||
auto& temp_painter = *m_screen_data[screen->index()].m_temp_painter;
|
||||
auto& temp_painter = *screen->compositor_screen_data().m_temp_painter;
|
||||
Gfx::PainterStateSaver saver(temp_painter);
|
||||
temp_painter.add_clip_rect(screen_render_rect);
|
||||
compose_window_rect(*screen, temp_painter, screen_render_rect);
|
||||
|
@ -518,7 +523,7 @@ void Compositor::compose()
|
|||
VERIFY(![&]() {
|
||||
bool is_overlapping = false;
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
auto& flush_transparent_rects = screen_data.m_flush_transparent_rects;
|
||||
auto& flush_rects = screen_data.m_flush_rects;
|
||||
for (auto& rect_transparent : flush_transparent_rects.rects()) {
|
||||
|
@ -543,7 +548,7 @@ void Compositor::compose()
|
|||
// Copy anything rendered to the temporary buffer to the back buffer
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto screen_rect = screen.rect();
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
for (auto& rect : screen_data.m_flush_transparent_rects.rects())
|
||||
screen_data.m_back_painter->blit(rect.location(), *screen_data.m_temp_bitmap, rect.translated(-screen_rect.location()));
|
||||
return IterationDecision::Continue;
|
||||
|
@ -556,7 +561,7 @@ void Compositor::compose()
|
|||
|
||||
if (!m_animations.is_empty()) {
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
update_animations(screen, screen_data.m_flush_special_rects);
|
||||
if (!screen_data.m_flush_special_rects.is_empty())
|
||||
screen_data.m_have_flush_rects = true;
|
||||
|
@ -568,7 +573,7 @@ void Compositor::compose()
|
|||
}
|
||||
|
||||
if (need_to_draw_cursor) {
|
||||
auto& screen_data = m_screen_data[cursor_screen.index()];
|
||||
auto& screen_data = cursor_screen.compositor_screen_data();
|
||||
screen_data.draw_cursor(cursor_screen, cursor_rect);
|
||||
}
|
||||
|
||||
|
@ -580,7 +585,7 @@ void Compositor::compose()
|
|||
|
||||
void Compositor::flush(Screen& screen)
|
||||
{
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
|
||||
bool device_can_flush_buffers = screen.can_device_flush_buffers();
|
||||
if (!screen_data.m_have_flush_rects && (!screen_data.m_screen_can_set_buffer || screen_data.m_has_flipped)) {
|
||||
|
@ -793,7 +798,7 @@ bool Compositor::set_wallpaper(const String& path, Function<void(bool)>&& callba
|
|||
return true;
|
||||
}
|
||||
|
||||
void Compositor::ScreenData::flip_buffers(Screen& screen)
|
||||
void CompositorScreenData::flip_buffers(Screen& screen)
|
||||
{
|
||||
VERIFY(m_screen_can_set_buffer);
|
||||
swap(m_front_bitmap, m_back_bitmap);
|
||||
|
@ -861,7 +866,7 @@ void Compositor::render_overlays()
|
|||
// NOTE: overlays should always be rendered to the temporary buffer!
|
||||
for (auto& overlay : m_overlay_list) {
|
||||
for (auto* screen : overlay.m_screens) {
|
||||
auto& screen_data = m_screen_data[screen->index()];
|
||||
auto& screen_data = screen->compositor_screen_data();
|
||||
auto& painter = screen_data.overlay_painter();
|
||||
screen_data.for_each_intersected_flushing_rect(overlay.current_render_rect(), [&](auto& intersected_overlay_rect) {
|
||||
Gfx::PainterStateSaver saver(painter);
|
||||
|
@ -905,7 +910,7 @@ void Compositor::remove_overlay(Overlay& overlay)
|
|||
overlay_rects_changed();
|
||||
}
|
||||
|
||||
void Compositor::ScreenData::draw_cursor(Screen& screen, const Gfx::IntRect& cursor_rect)
|
||||
void CompositorScreenData::draw_cursor(Screen& screen, const Gfx::IntRect& cursor_rect)
|
||||
{
|
||||
auto& wm = WindowManager::the();
|
||||
|
||||
|
@ -927,7 +932,7 @@ void Compositor::ScreenData::draw_cursor(Screen& screen, const Gfx::IntRect& cur
|
|||
m_cursor_back_is_valid = true;
|
||||
}
|
||||
|
||||
bool Compositor::ScreenData::restore_cursor_back(Screen& screen, Gfx::IntRect& last_cursor_rect)
|
||||
bool CompositorScreenData::restore_cursor_back(Screen& screen, Gfx::IntRect& last_cursor_rect)
|
||||
{
|
||||
if (!m_cursor_back_is_valid || !m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale())
|
||||
return false;
|
||||
|
@ -969,17 +974,19 @@ void Compositor::decrement_display_link_count(Badge<ClientConnection>)
|
|||
|
||||
void Compositor::invalidate_current_screen_number_rects()
|
||||
{
|
||||
for (auto& screen_data : m_screen_data) {
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
if (screen_data.m_screen_number_overlay)
|
||||
screen_data.m_screen_number_overlay->invalidate();
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void Compositor::increment_show_screen_number(Badge<ClientConnection>)
|
||||
{
|
||||
if (m_show_screen_number_count++ == 0) {
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
VERIFY(!screen_data.m_screen_number_overlay);
|
||||
screen_data.m_screen_number_overlay = create_overlay<ScreenNumberOverlay>(screen);
|
||||
screen_data.m_screen_number_overlay->set_enabled(true);
|
||||
|
@ -991,8 +998,10 @@ void Compositor::decrement_show_screen_number(Badge<ClientConnection>)
|
|||
{
|
||||
if (--m_show_screen_number_count == 0) {
|
||||
invalidate_current_screen_number_rects();
|
||||
for (auto& screen_data : m_screen_data)
|
||||
screen_data.m_screen_number_overlay = nullptr;
|
||||
Screen::for_each([&](auto& screen) {
|
||||
screen.compositor_screen_data().m_screen_number_overlay = nullptr;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1412,7 +1421,7 @@ void Compositor::unregister_animation(Badge<Animation>, Animation& animation)
|
|||
|
||||
void Compositor::update_animations(Screen& screen, Gfx::DisjointRectSet& flush_rects)
|
||||
{
|
||||
auto& painter = *m_screen_data[screen.index()].m_back_painter;
|
||||
auto& painter = *screen.compositor_screen_data().m_back_painter;
|
||||
for (RefPtr<Animation> animation : m_animations) {
|
||||
animation->update({}, painter, screen, flush_rects);
|
||||
}
|
||||
|
@ -1422,7 +1431,7 @@ void Compositor::create_window_stack_switch_overlay(WindowStack& target_stack)
|
|||
{
|
||||
stop_window_stack_switch_overlay_timer();
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
auto& screen_data = screen.compositor_screen_data();
|
||||
screen_data.m_window_stack_switch_overlay = nullptr; // delete it first
|
||||
screen_data.m_window_stack_switch_overlay = create_overlay<WindowStackSwitchOverlay>(screen, target_stack);
|
||||
screen_data.m_window_stack_switch_overlay->set_enabled(true);
|
||||
|
@ -1433,8 +1442,7 @@ void Compositor::create_window_stack_switch_overlay(WindowStack& target_stack)
|
|||
void Compositor::remove_window_stack_switch_overlays()
|
||||
{
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
screen_data.m_window_stack_switch_overlay = nullptr;
|
||||
screen.compositor_screen_data().m_window_stack_switch_overlay = nullptr;
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
}
|
||||
|
@ -1456,8 +1464,7 @@ void Compositor::start_window_stack_switch_overlay_timer()
|
|||
}
|
||||
bool have_overlay = false;
|
||||
Screen::for_each([&](auto& screen) {
|
||||
auto& screen_data = m_screen_data[screen.index()];
|
||||
if (screen_data.m_window_stack_switch_overlay) {
|
||||
if (screen.compositor_screen_data().m_window_stack_switch_overlay) {
|
||||
have_overlay = true;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace WindowServer {
|
|||
|
||||
class Animation;
|
||||
class ClientConnection;
|
||||
class Compositor;
|
||||
class Cursor;
|
||||
class MultiScaleBitmaps;
|
||||
class Window;
|
||||
|
@ -31,8 +32,62 @@ enum class WallpaperMode {
|
|||
Unchecked
|
||||
};
|
||||
|
||||
struct CompositorScreenData {
|
||||
RefPtr<Gfx::Bitmap> m_front_bitmap;
|
||||
RefPtr<Gfx::Bitmap> m_back_bitmap;
|
||||
RefPtr<Gfx::Bitmap> m_temp_bitmap;
|
||||
OwnPtr<Gfx::Painter> m_back_painter;
|
||||
OwnPtr<Gfx::Painter> m_front_painter;
|
||||
OwnPtr<Gfx::Painter> m_temp_painter;
|
||||
RefPtr<Gfx::Bitmap> m_cursor_back_bitmap;
|
||||
OwnPtr<Gfx::Painter> m_cursor_back_painter;
|
||||
Gfx::IntRect m_last_cursor_rect;
|
||||
OwnPtr<ScreenNumberOverlay> m_screen_number_overlay;
|
||||
OwnPtr<WindowStackSwitchOverlay> m_window_stack_switch_overlay;
|
||||
bool m_buffers_are_flipped { false };
|
||||
bool m_screen_can_set_buffer { false };
|
||||
bool m_has_flipped { false };
|
||||
bool m_cursor_back_is_valid { false };
|
||||
bool m_have_flush_rects { false };
|
||||
|
||||
Gfx::DisjointRectSet m_flush_rects;
|
||||
Gfx::DisjointRectSet m_flush_transparent_rects;
|
||||
Gfx::DisjointRectSet m_flush_special_rects;
|
||||
|
||||
Gfx::Painter& overlay_painter() { return *m_temp_painter; }
|
||||
|
||||
void init_bitmaps(Compositor&, Screen&);
|
||||
void flip_buffers(Screen&);
|
||||
void draw_cursor(Screen&, const Gfx::IntRect&);
|
||||
bool restore_cursor_back(Screen&, Gfx::IntRect&);
|
||||
|
||||
template<typename F>
|
||||
IterationDecision for_each_intersected_flushing_rect(Gfx::IntRect const& intersecting_rect, F f)
|
||||
{
|
||||
auto iterate_flush_rects = [&](Gfx::DisjointRectSet const& flush_rects) {
|
||||
for (auto& rect : flush_rects.rects()) {
|
||||
auto intersection = intersecting_rect.intersected(rect);
|
||||
if (intersection.is_empty())
|
||||
continue;
|
||||
IterationDecision decision = f(intersection);
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
};
|
||||
auto decision = iterate_flush_rects(m_flush_rects);
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
// We do not have to iterate m_flush_special_rects here as these
|
||||
// technically should be removed anyway. m_flush_rects and
|
||||
// m_flush_transparent_rects should cover everything already
|
||||
return iterate_flush_rects(m_flush_transparent_rects);
|
||||
}
|
||||
};
|
||||
|
||||
class Compositor final : public Core::Object {
|
||||
C_OBJECT(Compositor)
|
||||
friend struct CompositorScreenData;
|
||||
friend class Overlay;
|
||||
|
||||
public:
|
||||
|
@ -127,6 +182,11 @@ public:
|
|||
|
||||
void set_flash_flush(bool b) { m_flash_flush = b; }
|
||||
|
||||
static NonnullOwnPtr<CompositorScreenData> create_screen_data(Badge<Screen>)
|
||||
{
|
||||
return adopt_own(*new CompositorScreenData());
|
||||
}
|
||||
|
||||
private:
|
||||
Compositor();
|
||||
void init_bitmaps();
|
||||
|
@ -160,61 +220,6 @@ private:
|
|||
bool m_invalidated_cursor { false };
|
||||
bool m_overlay_rects_changed { false };
|
||||
|
||||
struct ScreenData {
|
||||
RefPtr<Gfx::Bitmap> m_front_bitmap;
|
||||
RefPtr<Gfx::Bitmap> m_back_bitmap;
|
||||
RefPtr<Gfx::Bitmap> m_temp_bitmap;
|
||||
OwnPtr<Gfx::Painter> m_back_painter;
|
||||
OwnPtr<Gfx::Painter> m_front_painter;
|
||||
OwnPtr<Gfx::Painter> m_temp_painter;
|
||||
RefPtr<Gfx::Bitmap> m_cursor_back_bitmap;
|
||||
OwnPtr<Gfx::Painter> m_cursor_back_painter;
|
||||
Gfx::IntRect m_last_cursor_rect;
|
||||
OwnPtr<ScreenNumberOverlay> m_screen_number_overlay;
|
||||
OwnPtr<WindowStackSwitchOverlay> m_window_stack_switch_overlay;
|
||||
bool m_buffers_are_flipped { false };
|
||||
bool m_screen_can_set_buffer { false };
|
||||
bool m_has_flipped { false };
|
||||
bool m_cursor_back_is_valid { false };
|
||||
bool m_have_flush_rects { false };
|
||||
|
||||
Gfx::DisjointRectSet m_flush_rects;
|
||||
Gfx::DisjointRectSet m_flush_transparent_rects;
|
||||
Gfx::DisjointRectSet m_flush_special_rects;
|
||||
|
||||
Gfx::Painter& overlay_painter() { return *m_temp_painter; }
|
||||
|
||||
void init_bitmaps(Compositor&, Screen&);
|
||||
void flip_buffers(Screen&);
|
||||
void draw_cursor(Screen&, const Gfx::IntRect&);
|
||||
bool restore_cursor_back(Screen&, Gfx::IntRect&);
|
||||
|
||||
template<typename F>
|
||||
IterationDecision for_each_intersected_flushing_rect(Gfx::IntRect const& intersecting_rect, F f)
|
||||
{
|
||||
auto iterate_flush_rects = [&](Gfx::DisjointRectSet const& flush_rects) {
|
||||
for (auto& rect : flush_rects.rects()) {
|
||||
auto intersection = intersecting_rect.intersected(rect);
|
||||
if (intersection.is_empty())
|
||||
continue;
|
||||
IterationDecision decision = f(intersection);
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
};
|
||||
auto decision = iterate_flush_rects(m_flush_rects);
|
||||
if (decision != IterationDecision::Continue)
|
||||
return decision;
|
||||
// We do not have to iterate m_flush_special_rects here as these
|
||||
// technically should be removed anyway. m_flush_rects and
|
||||
// m_flush_transparent_rects should cover everything already
|
||||
return iterate_flush_rects(m_flush_transparent_rects);
|
||||
}
|
||||
};
|
||||
friend struct ScreenData;
|
||||
Vector<ScreenData, default_screen_count> m_screen_data;
|
||||
|
||||
IntrusiveList<Overlay, RawPtr<Overlay>, &Overlay::m_list_node> m_overlay_list;
|
||||
Gfx::DisjointRectSet m_overlay_rects;
|
||||
Gfx::DisjointRectSet m_dirty_screen_rects;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
namespace WindowServer {
|
||||
|
||||
NonnullOwnPtrVector<Screen, default_screen_count> Screen::s_screens;
|
||||
NonnullRefPtrVector<Screen, default_screen_count> Screen::s_screens;
|
||||
Screen* Screen::s_main_screen { nullptr };
|
||||
Gfx::IntRect Screen::s_bounding_screens_rect {};
|
||||
ScreenLayout Screen::s_layout;
|
||||
|
@ -55,39 +55,137 @@ bool Screen::apply_layout(ScreenLayout&& screen_layout, String& error_msg)
|
|||
if (!screen_layout.is_valid(&error_msg))
|
||||
return false;
|
||||
|
||||
if (screen_layout == s_layout)
|
||||
return true;
|
||||
|
||||
bool place_cursor_on_main_screen = find_by_location(ScreenInput::the().cursor_location()) == nullptr;
|
||||
|
||||
HashMap<size_t, size_t> current_to_new_indices_map;
|
||||
HashMap<size_t, size_t> new_to_current_indices_map;
|
||||
HashMap<size_t, NonnullRefPtr<Screen>> devices_no_longer_used;
|
||||
for (size_t i = 0; i < s_layout.screens.size(); i++) {
|
||||
auto& screen = s_layout.screens[i];
|
||||
bool found = false;
|
||||
for (size_t j = 0; j < screen_layout.screens.size(); j++) {
|
||||
auto& new_screen = screen_layout.screens[j];
|
||||
if (new_screen.device == screen.device) {
|
||||
current_to_new_indices_map.set(i, j);
|
||||
new_to_current_indices_map.set(j, i);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
devices_no_longer_used.set(i, s_screens[i]);
|
||||
}
|
||||
HashMap<Screen*, size_t> screens_with_resolution_change;
|
||||
HashMap<Screen*, size_t> screens_with_scale_change;
|
||||
for (auto& it : current_to_new_indices_map) {
|
||||
auto& screen = s_layout.screens[it.key];
|
||||
auto& new_screen = screen_layout.screens[it.value];
|
||||
if (screen.resolution != new_screen.resolution)
|
||||
screens_with_resolution_change.set(&s_screens[it.key], it.value);
|
||||
if (screen.scale_factor != new_screen.scale_factor)
|
||||
screens_with_scale_change.set(&s_screens[it.key], it.value);
|
||||
}
|
||||
|
||||
auto screens_backup = move(s_screens);
|
||||
auto layout_backup = move(s_layout);
|
||||
for (auto& old_screen : screens_backup)
|
||||
old_screen.close_device();
|
||||
|
||||
for (auto& it : screens_with_resolution_change) {
|
||||
auto& existing_screen = *it.key;
|
||||
dbgln("Closing device {} in preparation for resolution change", layout_backup.screens[existing_screen.index()].device);
|
||||
existing_screen.close_device();
|
||||
}
|
||||
|
||||
AK::ArmedScopeGuard rollback([&] {
|
||||
for (auto& screen : s_screens)
|
||||
screen.close_device();
|
||||
s_screens = move(screens_backup);
|
||||
s_layout = move(layout_backup);
|
||||
for (auto& old_screen : screens_backup) {
|
||||
if (!old_screen.open_device()) {
|
||||
for (size_t i = 0; i < s_screens.size(); i++) {
|
||||
auto& old_screen = s_screens[i];
|
||||
// Restore the original screen index in case it changed
|
||||
old_screen.set_index(i);
|
||||
if (i == s_layout.main_screen_index)
|
||||
old_screen.make_main_screen();
|
||||
bool changed_scale = screens_with_scale_change.contains(&old_screen);
|
||||
if (screens_with_resolution_change.contains(&old_screen)) {
|
||||
if (old_screen.open_device()) {
|
||||
// The resolution was changed, so we also implicitly applied the new scale factor
|
||||
changed_scale = false;
|
||||
} else {
|
||||
// Don't set error_msg here, it should already be set
|
||||
dbgln("Rolling back screen layout failed: could not open device");
|
||||
}
|
||||
}
|
||||
|
||||
old_screen.update_virtual_rect();
|
||||
if (changed_scale)
|
||||
old_screen.scale_factor_changed();
|
||||
}
|
||||
update_bounding_rect();
|
||||
});
|
||||
s_layout = move(screen_layout);
|
||||
for (size_t index = 0; index < s_layout.screens.size(); index++) {
|
||||
auto* screen = WindowServer::Screen::create(s_layout.screens[index]);
|
||||
Screen* screen;
|
||||
bool need_to_open_device;
|
||||
if (auto it = new_to_current_indices_map.find(index); it != new_to_current_indices_map.end()) {
|
||||
// Re-use the existing screen instance
|
||||
screen = &screens_backup[it->value];
|
||||
s_screens.append(*screen);
|
||||
screen->set_index(index);
|
||||
|
||||
need_to_open_device = screens_with_resolution_change.contains(screen);
|
||||
} else {
|
||||
screen = WindowServer::Screen::create(index);
|
||||
if (!screen) {
|
||||
error_msg = String::formatted("Error creating screen #{}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
need_to_open_device = true;
|
||||
}
|
||||
|
||||
if (need_to_open_device && !screen->open_device()) {
|
||||
error_msg = String::formatted("Error opening device for screen #{}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->update_virtual_rect();
|
||||
if (!need_to_open_device && screens_with_scale_change.contains(screen))
|
||||
screen->scale_factor_changed();
|
||||
|
||||
VERIFY(screen);
|
||||
VERIFY(index == screen->index());
|
||||
|
||||
if (s_layout.main_screen_index == index)
|
||||
screen->make_main_screen();
|
||||
|
||||
screen->open_device();
|
||||
}
|
||||
|
||||
rollback.disarm();
|
||||
|
||||
if (place_cursor_on_main_screen) {
|
||||
ScreenInput::the().set_cursor_location(Screen::main().rect().center());
|
||||
} else {
|
||||
auto cursor_location = ScreenInput::the().cursor_location();
|
||||
if (!find_by_location(cursor_location)) {
|
||||
// Cursor is off screen, try to find the closest location on another screen
|
||||
float closest_distance = 0;
|
||||
Optional<Gfx::IntPoint> closest_point;
|
||||
for (auto& screen : s_screens) {
|
||||
auto closest_point_on_screen_rect = screen.rect().closest_to(cursor_location);
|
||||
auto distance = closest_point_on_screen_rect.distance_from(cursor_location);
|
||||
if (!closest_point.has_value() || distance < closest_distance) {
|
||||
closest_distance = distance;
|
||||
closest_point = closest_point_on_screen_rect;
|
||||
}
|
||||
}
|
||||
ScreenInput::the().set_cursor_location(closest_point.value()); // We should always have one
|
||||
}
|
||||
}
|
||||
|
||||
update_bounding_rect();
|
||||
update_scale_factors_in_use();
|
||||
return true;
|
||||
|
@ -108,17 +206,13 @@ void Screen::update_scale_factors_in_use()
|
|||
});
|
||||
}
|
||||
|
||||
Screen::Screen(ScreenLayout::Screen& screen_info)
|
||||
: m_virtual_rect(screen_info.location, { screen_info.resolution.width() / screen_info.scale_factor, screen_info.resolution.height() / screen_info.scale_factor })
|
||||
Screen::Screen(size_t screen_index)
|
||||
: m_index(screen_index)
|
||||
, m_framebuffer_data(adopt_own(*new ScreenFBData()))
|
||||
, m_info(screen_info)
|
||||
, m_compositor_screen_data(Compositor::create_screen_data({}))
|
||||
{
|
||||
update_virtual_rect();
|
||||
open_device();
|
||||
|
||||
// If the cursor is not in a valid screen (yet), force it into one
|
||||
dbgln("Screen() current physical cursor location: {} rect: {}", ScreenInput::the().cursor_location(), rect());
|
||||
if (!find_by_location(ScreenInput::the().cursor_location()))
|
||||
ScreenInput::the().set_cursor_location(rect().center());
|
||||
}
|
||||
|
||||
Screen::~Screen()
|
||||
|
@ -129,9 +223,10 @@ Screen::~Screen()
|
|||
bool Screen::open_device()
|
||||
{
|
||||
close_device();
|
||||
m_framebuffer_fd = open(m_info.device.characters(), O_RDWR | O_CLOEXEC);
|
||||
auto& info = screen_layout_info();
|
||||
m_framebuffer_fd = open(info.device.characters(), O_RDWR | O_CLOEXEC);
|
||||
if (m_framebuffer_fd < 0) {
|
||||
perror(String::formatted("failed to open {}", m_info.device).characters());
|
||||
perror(String::formatted("failed to open {}", info.device).characters());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -156,6 +251,19 @@ void Screen::close_device()
|
|||
}
|
||||
}
|
||||
|
||||
void Screen::update_virtual_rect()
|
||||
{
|
||||
auto& screen_info = screen_layout_info();
|
||||
m_virtual_rect = { screen_info.location, { screen_info.resolution.width() / screen_info.scale_factor, screen_info.resolution.height() / screen_info.scale_factor } };
|
||||
dbgln("update_virtual_rect for screen #{}: {}", index(), m_virtual_rect);
|
||||
}
|
||||
|
||||
void Screen::scale_factor_changed()
|
||||
{
|
||||
// Flush rects are affected by the screen factor
|
||||
constrain_pending_flush_rects();
|
||||
}
|
||||
|
||||
void Screen::init()
|
||||
{
|
||||
set_resolution(true);
|
||||
|
@ -208,12 +316,13 @@ bool Screen::set_resolution(bool initial)
|
|||
if (!initial)
|
||||
screen_with_cursor = &ScreenInput::the().cursor_location_screen();
|
||||
|
||||
FBResolution physical_resolution { 0, (unsigned)m_info.resolution.width(), (unsigned)m_info.resolution.height() };
|
||||
auto& info = screen_layout_info();
|
||||
FBResolution physical_resolution { 0, (unsigned)info.resolution.width(), (unsigned)info.resolution.height() };
|
||||
int rc = fb_set_resolution(m_framebuffer_fd, &physical_resolution);
|
||||
dbgln_if(WSSCREEN_DEBUG, "Screen #{}: fb_set_resolution() - return code {}", index(), rc);
|
||||
|
||||
auto on_change_resolution = [&]() {
|
||||
if (initial || physical_resolution.width != (unsigned)m_info.resolution.width() || physical_resolution.height != (unsigned)m_info.resolution.height()) {
|
||||
if (initial || physical_resolution.width != (unsigned)info.resolution.width() || physical_resolution.height != (unsigned)info.resolution.height()) {
|
||||
if (m_framebuffer) {
|
||||
size_t previous_size_in_bytes = m_size_in_bytes;
|
||||
int rc = munmap(m_framebuffer, previous_size_in_bytes);
|
||||
|
@ -240,8 +349,15 @@ bool Screen::set_resolution(bool initial)
|
|||
}
|
||||
}
|
||||
|
||||
m_info.resolution = { physical_resolution.width, physical_resolution.height };
|
||||
info.resolution = { physical_resolution.width, physical_resolution.height };
|
||||
m_pitch = physical_resolution.pitch;
|
||||
|
||||
update_virtual_rect();
|
||||
|
||||
// Since pending flush rects are affected by the scale factor
|
||||
// update even if only the scale factor changed
|
||||
constrain_pending_flush_rects();
|
||||
|
||||
if (this == screen_with_cursor) {
|
||||
auto& screen_input = ScreenInput::the();
|
||||
screen_input.set_cursor_location(screen_input.cursor_location().constrained(rect()));
|
||||
|
@ -254,7 +370,7 @@ bool Screen::set_resolution(bool initial)
|
|||
}
|
||||
if (rc == -1) {
|
||||
int err = errno;
|
||||
dbgln("Screen #{}: Failed to set resolution {}: {}", index(), m_info.resolution, strerror(err));
|
||||
dbgln("Screen #{}: Failed to set resolution {}: {}", index(), info.resolution, strerror(err));
|
||||
on_change_resolution();
|
||||
return false;
|
||||
}
|
||||
|
@ -345,6 +461,30 @@ void ScreenInput::on_receive_keyboard_data(::KeyEvent kernel_event)
|
|||
Core::EventLoop::current().post_event(WindowManager::the(), move(message));
|
||||
}
|
||||
|
||||
void Screen::constrain_pending_flush_rects()
|
||||
{
|
||||
auto& fb_data = *m_framebuffer_data;
|
||||
if (fb_data.pending_flush_rects.is_empty())
|
||||
return;
|
||||
Gfx::IntRect screen_rect({}, rect().size());
|
||||
Gfx::DisjointRectSet rects;
|
||||
for (auto& fb_rect : fb_data.pending_flush_rects) {
|
||||
Gfx::IntRect rect { (int)fb_rect.x, (int)fb_rect.y, (int)fb_rect.width, (int)fb_rect.height };
|
||||
auto intersected_rect = rect.intersected(screen_rect);
|
||||
if (!intersected_rect.is_empty())
|
||||
rects.add(intersected_rect);
|
||||
}
|
||||
fb_data.pending_flush_rects.clear_with_capacity();
|
||||
for (auto& rect : rects.rects()) {
|
||||
fb_data.pending_flush_rects.append({
|
||||
x : (unsigned)rect.x(),
|
||||
y : (unsigned)rect.y(),
|
||||
width : (unsigned)rect.width(),
|
||||
height : (unsigned)rect.height()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::queue_flush_display_rect(Gfx::IntRect const& flush_region)
|
||||
{
|
||||
// NOTE: we don't scale until in Screen::flush_display so that when
|
||||
|
@ -396,6 +536,7 @@ void Screen::flush_display(int buffer_index)
|
|||
// Now that we have a final set of rects, apply the scale factor
|
||||
auto scale_factor = this->scale_factor();
|
||||
for (auto& flush_rect : fb_data.pending_flush_rects) {
|
||||
VERIFY(Gfx::IntRect({}, m_virtual_rect.size()).contains({ (int)flush_rect.x, (int)flush_rect.y, (int)flush_rect.width, (int)flush_rect.height }));
|
||||
flush_rect.x *= scale_factor;
|
||||
flush_rect.y *= scale_factor;
|
||||
flush_rect.width *= scale_factor;
|
||||
|
@ -425,6 +566,7 @@ void Screen::flush_display_front_buffer(int front_buffer_index, Gfx::IntRect& re
|
|||
.height = (unsigned)(rect.height() * scale_factor)
|
||||
};
|
||||
|
||||
VERIFY(Gfx::IntRect({}, m_virtual_rect.size()).contains(rect));
|
||||
if (fb_flush_buffers(m_framebuffer_fd, front_buffer_index, &flush_rect, 1) < 0) {
|
||||
int err = errno;
|
||||
if (err == ENOTSUP)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "ScreenLayout.h"
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Kernel/API/KeyCode.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
|
@ -58,14 +58,15 @@ private:
|
|||
unsigned m_scroll_step_size { 1 };
|
||||
};
|
||||
|
||||
struct CompositorScreenData;
|
||||
struct ScreenFBData;
|
||||
|
||||
class Screen {
|
||||
class Screen : public RefCounted<Screen> {
|
||||
public:
|
||||
template<typename... Args>
|
||||
static Screen* create(Args&&... args)
|
||||
{
|
||||
auto screen = adopt_own(*new Screen(forward<Args>(args)...));
|
||||
auto screen = adopt_ref(*new Screen(forward<Args>(args)...));
|
||||
if (!screen->is_opened())
|
||||
return nullptr;
|
||||
auto* screen_ptr = screen.ptr();
|
||||
|
@ -155,7 +156,7 @@ public:
|
|||
|
||||
int width() const { return m_virtual_rect.width(); }
|
||||
int height() const { return m_virtual_rect.height(); }
|
||||
int scale_factor() const { return m_info.scale_factor; }
|
||||
int scale_factor() const { return screen_layout_info().scale_factor; }
|
||||
|
||||
Gfx::RGBA32* scanline(int buffer_index, int y);
|
||||
|
||||
|
@ -169,12 +170,16 @@ public:
|
|||
void flush_display(int buffer_index);
|
||||
void flush_display_front_buffer(int front_buffer_index, Gfx::IntRect&);
|
||||
|
||||
CompositorScreenData& compositor_screen_data() { return *m_compositor_screen_data; }
|
||||
|
||||
private:
|
||||
Screen(ScreenLayout::Screen&);
|
||||
Screen(size_t);
|
||||
bool open_device();
|
||||
void close_device();
|
||||
void init();
|
||||
void scale_factor_changed();
|
||||
bool set_resolution(bool initial);
|
||||
void constrain_pending_flush_rects();
|
||||
static void update_indices()
|
||||
{
|
||||
for (size_t i = 0; i < s_screens.size(); i++)
|
||||
|
@ -185,7 +190,12 @@ private:
|
|||
|
||||
bool is_opened() const { return m_framebuffer_fd >= 0; }
|
||||
|
||||
static NonnullOwnPtrVector<Screen, default_screen_count> s_screens;
|
||||
void set_index(size_t index) { m_index = index; }
|
||||
void update_virtual_rect();
|
||||
ScreenLayout::Screen& screen_layout_info() { return s_layout.screens[m_index]; }
|
||||
ScreenLayout::Screen const& screen_layout_info() const { return s_layout.screens[m_index]; }
|
||||
|
||||
static NonnullRefPtrVector<Screen, default_screen_count> s_screens;
|
||||
static Screen* s_main_screen;
|
||||
static Gfx::IntRect s_bounding_screens_rect;
|
||||
static ScreenLayout s_layout;
|
||||
|
@ -203,8 +213,7 @@ private:
|
|||
Gfx::IntRect m_virtual_rect;
|
||||
int m_framebuffer_fd { -1 };
|
||||
NonnullOwnPtr<ScreenFBData> m_framebuffer_data;
|
||||
|
||||
ScreenLayout::Screen& m_info;
|
||||
NonnullOwnPtr<CompositorScreenData> m_compositor_screen_data;
|
||||
};
|
||||
|
||||
inline Gfx::RGBA32* Screen::scanline(int buffer_index, int y)
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
unsigned main_screen_index { 0 };
|
||||
|
||||
bool is_valid(String* error_msg = nullptr) const;
|
||||
void normalize();
|
||||
bool normalize();
|
||||
bool load_config(const Core::ConfigFile& config_file, String* error_msg = nullptr);
|
||||
bool save_config(Core::ConfigFile& config_file, bool sync = true) const;
|
||||
bool try_auto_add_framebuffer(String const&);
|
||||
|
|
|
@ -105,21 +105,124 @@ bool ScreenLayout::is_valid(String* error_msg) const
|
|||
return true;
|
||||
}
|
||||
|
||||
void ScreenLayout::normalize()
|
||||
bool ScreenLayout::normalize()
|
||||
{
|
||||
// Check for any overlaps and try to move screens
|
||||
Vector<Gfx::IntRect, 8> screen_virtual_rects;
|
||||
for (auto& screen : screens)
|
||||
screen_virtual_rects.append(screen.virtual_rect());
|
||||
|
||||
bool did_change = false;
|
||||
for (;;) {
|
||||
// Separate any overlapping screens
|
||||
if (Gfx::IntRect::disperse(screen_virtual_rects)) {
|
||||
did_change = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if all screens are still reachable
|
||||
Vector<Gfx::IntRect*, 8> reachable_rects;
|
||||
|
||||
auto recalculate_reachable = [&]() {
|
||||
reachable_rects = { &screen_virtual_rects[main_screen_index] };
|
||||
bool did_reach_another;
|
||||
do {
|
||||
did_reach_another = false;
|
||||
auto& latest_reachable_rect = *reachable_rects[reachable_rects.size() - 1];
|
||||
for (auto& rect : screen_virtual_rects) {
|
||||
if (&rect == &latest_reachable_rect || reachable_rects.contains_slow(&rect))
|
||||
continue;
|
||||
if (rect.is_adjacent(latest_reachable_rect)) {
|
||||
reachable_rects.append(&rect);
|
||||
did_reach_another = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (did_reach_another);
|
||||
};
|
||||
|
||||
recalculate_reachable();
|
||||
if (reachable_rects.size() != screen_virtual_rects.size()) {
|
||||
// Some screens were not reachable, try to move one somewhere closer
|
||||
for (auto& screen_rect : screen_virtual_rects) {
|
||||
if (reachable_rects.contains_slow(&screen_rect))
|
||||
continue;
|
||||
|
||||
float closest_distance = 0;
|
||||
Gfx::IntRect* closest_rect = nullptr;
|
||||
for (auto& screen_rect2 : screen_virtual_rects) {
|
||||
if (&screen_rect2 == &screen_rect)
|
||||
continue;
|
||||
if (!reachable_rects.contains_slow(&screen_rect2))
|
||||
continue;
|
||||
auto distance = screen_rect.outside_center_point_distance_to(screen_rect2);
|
||||
if (!closest_rect || distance < closest_distance) {
|
||||
closest_distance = distance;
|
||||
closest_rect = &screen_rect2;
|
||||
}
|
||||
}
|
||||
VERIFY(closest_rect); // We should always have one!
|
||||
VERIFY(closest_rect != &screen_rect);
|
||||
|
||||
// Move the screen_rect closer to closest_rect
|
||||
auto is_adjacent_to_reachable = [&]() {
|
||||
for (auto* rect : reachable_rects) {
|
||||
if (rect == &screen_rect)
|
||||
continue;
|
||||
if (screen_rect.is_adjacent(*rect))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Move it until we're touching a reachable screen
|
||||
do {
|
||||
auto outside_center_points = screen_rect.closest_outside_center_points(*closest_rect);
|
||||
int delta_x = 0;
|
||||
if (outside_center_points[0].x() < outside_center_points[1].x())
|
||||
delta_x = 1;
|
||||
else if (outside_center_points[0].x() > outside_center_points[1].x())
|
||||
delta_x = -1;
|
||||
int delta_y = 0;
|
||||
if (outside_center_points[0].y() < outside_center_points[1].y())
|
||||
delta_y = 1;
|
||||
else if (outside_center_points[0].y() > outside_center_points[1].y())
|
||||
delta_y = -1;
|
||||
VERIFY(delta_x != 0 || delta_y != 0);
|
||||
screen_rect.translate_by(delta_x, delta_y);
|
||||
} while (!is_adjacent_to_reachable());
|
||||
|
||||
recalculate_reachable();
|
||||
did_change = true;
|
||||
break; // We only try to move one at at time
|
||||
}
|
||||
|
||||
// Moved the screen, re-evaluate
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int smallest_x = 0;
|
||||
int smallest_y = 0;
|
||||
for (size_t i = 0; i < screens.size(); i++) {
|
||||
auto& screen = screens[i];
|
||||
if (i == 0 || screen.location.x() < smallest_x)
|
||||
smallest_x = screen.location.x();
|
||||
if (i == 0 || screen.location.y() < smallest_y)
|
||||
smallest_y = screen.location.y();
|
||||
for (size_t i = 0; i < screen_virtual_rects.size(); i++) {
|
||||
auto& rect = screen_virtual_rects[i];
|
||||
if (i == 0 || rect.x() < smallest_x)
|
||||
smallest_x = rect.x();
|
||||
if (i == 0 || rect.y() < smallest_y)
|
||||
smallest_y = rect.y();
|
||||
}
|
||||
if (smallest_x != 0 || smallest_y != 0) {
|
||||
for (auto& screen : screens)
|
||||
screen.location.translate_by(-smallest_x, -smallest_y);
|
||||
for (auto& rect : screen_virtual_rects)
|
||||
rect.translate_by(-smallest_x, -smallest_y);
|
||||
did_change = true;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < screens.size(); i++)
|
||||
screens[i].location = screen_virtual_rects[i].location();
|
||||
|
||||
VERIFY(is_valid());
|
||||
return did_change;
|
||||
}
|
||||
|
||||
bool ScreenLayout::load_config(const Core::ConfigFile& config_file, String* error_msg)
|
||||
|
@ -213,11 +316,24 @@ bool ScreenLayout::try_auto_add_framebuffer(String const& device_path)
|
|||
resolution.height = main_screen.resolution.height();
|
||||
}
|
||||
|
||||
auto append_screen = [&](Gfx::IntRect const& new_screen_rect) {
|
||||
screens.append({ .device = device_path,
|
||||
.location = new_screen_rect.location(),
|
||||
.resolution = new_screen_rect.size(),
|
||||
.scale_factor = 1 });
|
||||
};
|
||||
|
||||
if (screens.is_empty()) {
|
||||
append_screen({ 0, 0, (int)resolution.width, (int)resolution.height });
|
||||
return true;
|
||||
}
|
||||
|
||||
auto original_screens = move(screens);
|
||||
screens = original_screens;
|
||||
ArmedScopeGuard screens_guard([&] {
|
||||
screens = move(original_screens);
|
||||
});
|
||||
|
||||
// Now that we know the current resolution, try to find a location that we can add onto
|
||||
// TODO: make this a little more sophisticated in case a more complex layout is already configured
|
||||
for (auto& screen : screens) {
|
||||
|
@ -240,13 +356,7 @@ bool ScreenLayout::try_auto_add_framebuffer(String const& device_path)
|
|||
}
|
||||
|
||||
if (!collision) {
|
||||
screens.append({
|
||||
.device = device_path,
|
||||
.location = new_screen_rect.location(),
|
||||
.resolution = new_screen_rect.size(),
|
||||
.scale_factor = 1
|
||||
});
|
||||
|
||||
append_screen(new_screen_rect);
|
||||
if (is_valid()) {
|
||||
// We got lucky!
|
||||
screens_guard.disarm();
|
||||
|
|
|
@ -82,14 +82,8 @@ int main(int, char**)
|
|||
AK::HashTable<String> fb_devices_configured;
|
||||
WindowServer::ScreenLayout screen_layout;
|
||||
String error_msg;
|
||||
if (!screen_layout.load_config(*wm_config, &error_msg)) {
|
||||
dbgln("Error loading screen configuration: {}", error_msg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (auto& screen_info : screen_layout.screens)
|
||||
fb_devices_configured.set(screen_info.device);
|
||||
|
||||
auto add_unconfigured_devices = [&]() {
|
||||
// Enumerate the /dev/fbX devices and try to set up any ones we find that we haven't already used
|
||||
Core::DirIterator di("/dev", Core::DirIterator::SkipParentAndBaseDir);
|
||||
while (di.has_next()) {
|
||||
|
@ -104,9 +98,35 @@ int main(int, char**)
|
|||
if (!screen_layout.try_auto_add_framebuffer(full_path))
|
||||
dbgln("Could not auto-add framebuffer device {} to screen layout", full_path);
|
||||
}
|
||||
};
|
||||
|
||||
auto apply_and_generate_generic_screen_layout = [&]() {
|
||||
screen_layout = {};
|
||||
fb_devices_configured = {};
|
||||
add_unconfigured_devices();
|
||||
if (!WindowServer::Screen::apply_layout(move(screen_layout), error_msg)) {
|
||||
dbgln("Failed to apply generated fallback screen layout: {}", error_msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
dbgln("Applied generated fallback screen layout!");
|
||||
return true;
|
||||
};
|
||||
|
||||
if (screen_layout.load_config(*wm_config, &error_msg)) {
|
||||
for (auto& screen_info : screen_layout.screens)
|
||||
fb_devices_configured.set(screen_info.device);
|
||||
|
||||
add_unconfigured_devices();
|
||||
|
||||
if (!WindowServer::Screen::apply_layout(move(screen_layout), error_msg)) {
|
||||
dbgln("Error applying screen layout: {}", error_msg);
|
||||
if (!apply_and_generate_generic_screen_layout())
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
dbgln("Error loading screen configuration: {}", error_msg);
|
||||
if (!apply_and_generate_generic_screen_layout())
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue