|
@@ -61,28 +61,44 @@ Compositor::Compositor()
|
|
|
},
|
|
|
this);
|
|
|
|
|
|
- m_screen_can_set_buffer = Screen::the().can_set_buffer();
|
|
|
init_bitmaps();
|
|
|
}
|
|
|
|
|
|
-void Compositor::init_bitmaps()
|
|
|
+const Gfx::Bitmap& Compositor::front_bitmap_for_screenshot(Badge<ClientConnection>, Screen& screen) const
|
|
|
+{
|
|
|
+ return *m_screen_data[screen.index()].m_front_bitmap;
|
|
|
+}
|
|
|
+
|
|
|
+void Compositor::ScreenData::init_bitmaps(Screen& screen)
|
|
|
{
|
|
|
- auto& screen = Screen::the();
|
|
|
auto size = screen.size();
|
|
|
|
|
|
m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(0));
|
|
|
m_front_painter = make<Gfx::Painter>(*m_front_bitmap);
|
|
|
+ m_front_painter->translate(-screen.rect().location());
|
|
|
|
|
|
- if (m_screen_can_set_buffer)
|
|
|
+ if (screen.can_set_buffer())
|
|
|
m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(screen.physical_height()));
|
|
|
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());
|
|
|
|
|
|
m_buffers_are_flipped = false;
|
|
|
+ m_screen_can_set_buffer = screen.can_set_buffer();
|
|
|
+}
|
|
|
+
|
|
|
+void Compositor::init_bitmaps()
|
|
|
+{
|
|
|
+ m_screen_data.resize(Screen::count());
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ m_screen_data[screen.index()].init_bitmaps(screen);
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
|
|
|
invalidate_screen();
|
|
|
}
|
|
@@ -101,7 +117,6 @@ void Compositor::did_construct_window_manager(Badge<WindowManager>)
|
|
|
void Compositor::compose()
|
|
|
{
|
|
|
auto& wm = WindowManager::the();
|
|
|
- auto& ws = Screen::the();
|
|
|
|
|
|
{
|
|
|
auto& current_cursor = wm.active_cursor();
|
|
@@ -122,11 +137,26 @@ void Compositor::compose()
|
|
|
}
|
|
|
|
|
|
auto dirty_screen_rects = move(m_dirty_screen_rects);
|
|
|
- dirty_screen_rects.add(m_last_geometry_label_damage_rect.intersected(ws.rect()));
|
|
|
- dirty_screen_rects.add(m_last_dnd_rect.intersected(ws.rect()));
|
|
|
- if (m_invalidated_cursor) {
|
|
|
- if (wm.dnd_client())
|
|
|
- dirty_screen_rects.add(wm.dnd_rect().intersected(ws.rect()));
|
|
|
+ auto* dnd_client = wm.dnd_client();
|
|
|
+ if (!m_last_geometry_label_damage_rect.is_empty() || !m_last_dnd_rect.is_empty() || (m_invalidated_cursor && dnd_client)) {
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ if (!m_last_geometry_label_damage_rect.is_empty()) {
|
|
|
+ auto rect = m_last_geometry_label_damage_rect.intersected(screen.rect());
|
|
|
+ if (!rect.is_empty())
|
|
|
+ dirty_screen_rects.add(rect);
|
|
|
+ }
|
|
|
+ if (!m_last_dnd_rect.is_empty()) {
|
|
|
+ auto rect = m_last_dnd_rect.intersected(screen.rect());
|
|
|
+ if (!rect.is_empty())
|
|
|
+ dirty_screen_rects.add(rect);
|
|
|
+ }
|
|
|
+ if (m_invalidated_cursor && dnd_client) {
|
|
|
+ auto rect = wm.dnd_rect().intersected(screen.rect());
|
|
|
+ if (!rect.is_empty())
|
|
|
+ dirty_screen_rects.add(rect);
|
|
|
+ }
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
// Mark window regions as dirty that need to be re-rendered
|
|
@@ -180,61 +210,81 @@ void Compositor::compose()
|
|
|
dbgln("dirty screen: {}", r);
|
|
|
}
|
|
|
|
|
|
- Gfx::DisjointRectSet flush_rects;
|
|
|
- Gfx::DisjointRectSet flush_transparent_rects;
|
|
|
- Gfx::DisjointRectSet flush_special_rects;
|
|
|
- auto cursor_rect = current_cursor_rect();
|
|
|
- bool need_to_draw_cursor = false;
|
|
|
+ auto& cursor_screen = ScreenInput::the().cursor_location_screen();
|
|
|
|
|
|
- auto back_painter = *m_back_painter;
|
|
|
- auto temp_painter = *m_temp_painter;
|
|
|
+ for (auto& screen_data : m_screen_data) {
|
|
|
+ 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();
|
|
|
+ }
|
|
|
|
|
|
- auto check_restore_cursor_back = [&](const Gfx::IntRect& rect) {
|
|
|
- if (!need_to_draw_cursor && rect.intersects(cursor_rect)) {
|
|
|
+ auto cursor_rect = current_cursor_rect();
|
|
|
+
|
|
|
+ bool need_to_draw_cursor = false;
|
|
|
+ Gfx::IntRect previous_cursor_rect;
|
|
|
+ Screen* previous_cursor_screen = nullptr;
|
|
|
+ auto check_restore_cursor_back = [&](Screen& screen, const Gfx::IntRect& rect) {
|
|
|
+ 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;
|
|
|
- restore_cursor_back();
|
|
|
+ auto& screen_data = m_screen_data[cursor_screen.index()];
|
|
|
+ if (screen_data.restore_cursor_back(cursor_screen, previous_cursor_rect))
|
|
|
+ previous_cursor_screen = &screen;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- auto prepare_rect = [&](const Gfx::IntRect& rect) {
|
|
|
+ if (&cursor_screen != m_current_cursor_screen) {
|
|
|
+ // 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))
|
|
|
+ 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()];
|
|
|
dbgln_if(COMPOSE_DEBUG, " -> flush opaque: {}", rect);
|
|
|
- VERIFY(!flush_rects.intersects(rect));
|
|
|
- VERIFY(!flush_transparent_rects.intersects(rect));
|
|
|
- flush_rects.add(rect);
|
|
|
- check_restore_cursor_back(rect);
|
|
|
+ VERIFY(!screen_data.m_flush_rects.intersects(rect));
|
|
|
+ VERIFY(!screen_data.m_flush_transparent_rects.intersects(rect));
|
|
|
+ screen_data.m_flush_rects.add(rect);
|
|
|
+ check_restore_cursor_back(screen, rect);
|
|
|
};
|
|
|
|
|
|
- auto prepare_transparency_rect = [&](const Gfx::IntRect& rect) {
|
|
|
+ auto prepare_transparency_rect = [&](Screen& screen, const Gfx::IntRect& rect) {
|
|
|
+ auto& screen_data = m_screen_data[screen.index()];
|
|
|
dbgln_if(COMPOSE_DEBUG, " -> flush transparent: {}", rect);
|
|
|
- VERIFY(!flush_rects.intersects(rect));
|
|
|
- for (auto& r : flush_transparent_rects.rects()) {
|
|
|
+ VERIFY(!screen_data.m_flush_rects.intersects(rect));
|
|
|
+ for (auto& r : screen_data.m_flush_transparent_rects.rects()) {
|
|
|
if (r == rect)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- flush_transparent_rects.add(rect);
|
|
|
- check_restore_cursor_back(rect);
|
|
|
+ screen_data.m_flush_transparent_rects.add(rect);
|
|
|
+ check_restore_cursor_back(screen, rect);
|
|
|
};
|
|
|
|
|
|
- if (!m_cursor_back_bitmap || m_invalidated_cursor)
|
|
|
- check_restore_cursor_back(cursor_rect);
|
|
|
+ if (!m_screen_data[cursor_screen.index()].m_cursor_back_bitmap || m_invalidated_cursor)
|
|
|
+ check_restore_cursor_back(cursor_screen, cursor_rect);
|
|
|
|
|
|
- auto paint_wallpaper = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
|
|
+ auto paint_wallpaper = [&](Screen& screen, Gfx::Painter& painter, const Gfx::IntRect& rect, const Gfx::IntRect& screen_rect) {
|
|
|
// FIXME: If the wallpaper is opaque and covers the whole rect, no need to fill with color!
|
|
|
painter.fill_rect(rect, background_color);
|
|
|
if (m_wallpaper) {
|
|
|
if (m_wallpaper_mode == WallpaperMode::Center) {
|
|
|
- Gfx::IntPoint offset { (ws.width() - m_wallpaper->width()) / 2, (ws.height() - m_wallpaper->height()) / 2 };
|
|
|
- painter.blit_offset(rect.location(), *m_wallpaper, rect, offset);
|
|
|
+ Gfx::IntPoint offset { (screen.width() - m_wallpaper->width()) / 2, (screen.height() - m_wallpaper->height()) / 2 };
|
|
|
+ painter.blit_offset(rect.location(), *m_wallpaper, rect.translated(-screen_rect.location()), offset);
|
|
|
} else if (m_wallpaper_mode == WallpaperMode::Tile) {
|
|
|
painter.draw_tiled_bitmap(rect, *m_wallpaper);
|
|
|
} else if (m_wallpaper_mode == WallpaperMode::Stretch) {
|
|
|
- float hscale = (float)m_wallpaper->width() / (float)ws.width();
|
|
|
- float vscale = (float)m_wallpaper->height() / (float)ws.height();
|
|
|
+ float hscale = (float)m_wallpaper->width() / (float)screen.width();
|
|
|
+ float vscale = (float)m_wallpaper->height() / (float)screen.height();
|
|
|
|
|
|
// TODO: this may look ugly, we should scale to a backing bitmap and then blit
|
|
|
- auto src_rect = Gfx::FloatRect { rect.x() * hscale, rect.y() * vscale, rect.width() * hscale, rect.height() * vscale };
|
|
|
+ auto relative_rect = rect.translated(-screen_rect.location());
|
|
|
+ auto src_rect = Gfx::FloatRect { relative_rect.x() * hscale, relative_rect.y() * vscale, relative_rect.width() * hscale, relative_rect.height() * vscale };
|
|
|
painter.draw_scaled_bitmap(rect, *m_wallpaper, src_rect);
|
|
|
} else {
|
|
|
VERIFY_NOT_REACHED();
|
|
@@ -243,29 +293,39 @@ void Compositor::compose()
|
|
|
};
|
|
|
|
|
|
m_opaque_wallpaper_rects.for_each_intersected(dirty_screen_rects, [&](const Gfx::IntRect& render_rect) {
|
|
|
- dbgln_if(COMPOSE_DEBUG, " render wallpaper opaque: {}", render_rect);
|
|
|
- prepare_rect(render_rect);
|
|
|
- paint_wallpaper(back_painter, render_rect);
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ 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;
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
return IterationDecision::Continue;
|
|
|
});
|
|
|
|
|
|
auto compose_window = [&](Window& window) -> IterationDecision {
|
|
|
- auto frame_rect = window.frame().render_rect();
|
|
|
- if (!frame_rect.intersects(ws.rect()))
|
|
|
+ if (window.screens().is_empty()) {
|
|
|
+ // This window doesn't intersect with any screens, so there's nothing to render
|
|
|
return IterationDecision::Continue;
|
|
|
+ }
|
|
|
+ auto frame_rect = window.frame().render_rect();
|
|
|
auto window_rect = window.rect();
|
|
|
auto frame_rects = frame_rect.shatter(window_rect);
|
|
|
|
|
|
dbgln_if(COMPOSE_DEBUG, " window {} frame rect: {}", window.title(), frame_rect);
|
|
|
|
|
|
RefPtr<Gfx::Bitmap> backing_store = window.backing_store();
|
|
|
- auto compose_window_rect = [&](Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
|
|
+ auto compose_window_rect = [&](Screen& screen, Gfx::Painter& painter, const Gfx::IntRect& rect) {
|
|
|
if (!window.is_fullscreen()) {
|
|
|
rect.for_each_intersected(frame_rects, [&](const Gfx::IntRect& intersected_rect) {
|
|
|
Gfx::PainterStateSaver saver(painter);
|
|
|
painter.add_clip_rect(intersected_rect);
|
|
|
dbgln_if(COMPOSE_DEBUG, " render frame: {}", intersected_rect);
|
|
|
- window.frame().paint(painter, intersected_rect);
|
|
|
+ window.frame().paint(screen, painter, intersected_rect);
|
|
|
return IterationDecision::Continue;
|
|
|
});
|
|
|
}
|
|
@@ -359,12 +419,18 @@ void Compositor::compose()
|
|
|
auto& opaque_rects = window.opaque_rects();
|
|
|
if (!opaque_rects.is_empty()) {
|
|
|
opaque_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
|
|
- dbgln_if(COMPOSE_DEBUG, " render opaque: {}", render_rect);
|
|
|
-
|
|
|
- prepare_rect(render_rect);
|
|
|
- Gfx::PainterStateSaver saver(back_painter);
|
|
|
- back_painter.add_clip_rect(render_rect);
|
|
|
- compose_window_rect(back_painter, render_rect);
|
|
|
+ for (auto* screen : window.screens()) {
|
|
|
+ auto screen_render_rect = render_rect.intersected(screen->rect());
|
|
|
+ if (screen_render_rect.is_empty())
|
|
|
+ continue;
|
|
|
+ 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;
|
|
|
+ Gfx::PainterStateSaver saver(back_painter);
|
|
|
+ back_painter.add_clip_rect(screen_render_rect);
|
|
|
+ compose_window_rect(*screen, back_painter, screen_render_rect);
|
|
|
+ }
|
|
|
return IterationDecision::Continue;
|
|
|
});
|
|
|
}
|
|
@@ -374,22 +440,36 @@ void Compositor::compose()
|
|
|
auto& transparency_wallpaper_rects = window.transparency_wallpaper_rects();
|
|
|
if (!transparency_wallpaper_rects.is_empty()) {
|
|
|
transparency_wallpaper_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
|
|
- dbgln_if(COMPOSE_DEBUG, " render wallpaper: {}", render_rect);
|
|
|
-
|
|
|
- prepare_transparency_rect(render_rect);
|
|
|
- paint_wallpaper(temp_painter, render_rect);
|
|
|
+ for (auto* screen : window.screens()) {
|
|
|
+ auto screen_rect = screen->rect();
|
|
|
+ auto screen_render_rect = render_rect.intersected(screen_rect);
|
|
|
+ if (screen_render_rect.is_empty())
|
|
|
+ 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;
|
|
|
+ prepare_transparency_rect(*screen, screen_render_rect);
|
|
|
+ paint_wallpaper(*screen, temp_painter, screen_render_rect, screen_rect);
|
|
|
+ }
|
|
|
return IterationDecision::Continue;
|
|
|
});
|
|
|
}
|
|
|
auto& transparency_rects = window.transparency_rects();
|
|
|
if (!transparency_rects.is_empty()) {
|
|
|
transparency_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) {
|
|
|
- dbgln_if(COMPOSE_DEBUG, " render transparent: {}", render_rect);
|
|
|
-
|
|
|
- prepare_transparency_rect(render_rect);
|
|
|
- Gfx::PainterStateSaver saver(temp_painter);
|
|
|
- temp_painter.add_clip_rect(render_rect);
|
|
|
- compose_window_rect(temp_painter, render_rect);
|
|
|
+ for (auto* screen : window.screens()) {
|
|
|
+ auto screen_rect = screen->rect();
|
|
|
+ auto screen_render_rect = render_rect.intersected(screen_rect);
|
|
|
+ if (screen_render_rect.is_empty())
|
|
|
+ continue;
|
|
|
+ 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;
|
|
|
+ Gfx::PainterStateSaver saver(temp_painter);
|
|
|
+ temp_painter.add_clip_rect(screen_render_rect);
|
|
|
+ compose_window_rect(*screen, temp_painter, screen_render_rect);
|
|
|
+ }
|
|
|
return IterationDecision::Continue;
|
|
|
});
|
|
|
}
|
|
@@ -411,24 +491,35 @@ void Compositor::compose()
|
|
|
|
|
|
// Check that there are no overlapping transparent and opaque flush rectangles
|
|
|
VERIFY(![&]() {
|
|
|
- for (auto& rect_transparent : flush_transparent_rects.rects()) {
|
|
|
- for (auto& rect_opaque : flush_rects.rects()) {
|
|
|
- if (rect_opaque.intersects(rect_transparent)) {
|
|
|
- dbgln("Transparent rect {} overlaps opaque rect: {}: {}", rect_transparent, rect_opaque, rect_opaque.intersected(rect_transparent));
|
|
|
- return true;
|
|
|
+ bool is_overlapping = false;
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ auto& screen_data = m_screen_data[screen.index()];
|
|
|
+ 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()) {
|
|
|
+ for (auto& rect_opaque : flush_rects.rects()) {
|
|
|
+ if (rect_opaque.intersects(rect_transparent)) {
|
|
|
+ dbgln("Transparent rect {} overlaps opaque rect: {}: {}", rect_transparent, rect_opaque, rect_opaque.intersected(rect_transparent));
|
|
|
+ is_overlapping = true;
|
|
|
+ return IterationDecision::Break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- return false;
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
+ return is_overlapping;
|
|
|
}());
|
|
|
|
|
|
// Copy anything rendered to the temporary buffer to the back buffer
|
|
|
- for (auto& rect : flush_transparent_rects.rects())
|
|
|
- back_painter.blit(rect.location(), *m_temp_bitmap, rect);
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ auto screen_rect = screen.rect();
|
|
|
+ auto& screen_data = m_screen_data[screen.index()];
|
|
|
+ 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;
|
|
|
+ });
|
|
|
|
|
|
- Gfx::IntRect geometry_label_damage_rect;
|
|
|
- if (draw_geometry_label(geometry_label_damage_rect))
|
|
|
- flush_special_rects.add(geometry_label_damage_rect);
|
|
|
+ draw_geometry_label(cursor_screen);
|
|
|
}
|
|
|
|
|
|
m_invalidated_any = false;
|
|
@@ -438,34 +529,50 @@ void Compositor::compose()
|
|
|
if (wm.dnd_client()) {
|
|
|
auto dnd_rect = wm.dnd_rect();
|
|
|
|
|
|
- // TODO: render once into a backing bitmap, then just blit...
|
|
|
- auto render_dnd = [&]() {
|
|
|
- back_painter.fill_rect(dnd_rect, wm.palette().selection().with_alpha(200));
|
|
|
- back_painter.draw_rect(dnd_rect, wm.palette().selection());
|
|
|
- if (!wm.dnd_text().is_empty()) {
|
|
|
- auto text_rect = dnd_rect;
|
|
|
- if (wm.dnd_bitmap())
|
|
|
- text_rect.translate_by(wm.dnd_bitmap()->width() + 8, 0);
|
|
|
- back_painter.draw_text(text_rect, wm.dnd_text(), Gfx::TextAlignment::CenterLeft, wm.palette().selection_text());
|
|
|
- }
|
|
|
- if (wm.dnd_bitmap()) {
|
|
|
- back_painter.blit(dnd_rect.top_left().translated(4, 4), *wm.dnd_bitmap(), wm.dnd_bitmap()->rect());
|
|
|
- }
|
|
|
- };
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ auto screen_rect = screen.rect();
|
|
|
+ auto render_dnd_rect = screen_rect.intersected(dnd_rect);
|
|
|
+ if (render_dnd_rect.is_empty())
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ auto& screen_data = m_screen_data[screen.index()];
|
|
|
+ auto& back_painter = *screen_data.m_back_painter;
|
|
|
+
|
|
|
+ // TODO: render once into a backing bitmap, then just blit...
|
|
|
+ auto render_dnd = [&]() {
|
|
|
+ back_painter.fill_rect(dnd_rect, wm.palette().selection().with_alpha(200));
|
|
|
+ back_painter.draw_rect(dnd_rect, wm.palette().selection());
|
|
|
+ if (!wm.dnd_text().is_empty()) {
|
|
|
+ auto text_rect = dnd_rect;
|
|
|
+ if (wm.dnd_bitmap())
|
|
|
+ text_rect.translate_by(wm.dnd_bitmap()->width() + 8, 0);
|
|
|
+ back_painter.draw_text(text_rect, wm.dnd_text(), Gfx::TextAlignment::CenterLeft, wm.palette().selection_text());
|
|
|
+ }
|
|
|
+ if (wm.dnd_bitmap()) {
|
|
|
+ back_painter.blit(dnd_rect.top_left().translated(4, 4), *wm.dnd_bitmap(), wm.dnd_bitmap()->rect());
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- dirty_screen_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
|
|
- Gfx::PainterStateSaver saver(back_painter);
|
|
|
- back_painter.add_clip_rect(render_rect);
|
|
|
- render_dnd();
|
|
|
- return IterationDecision::Continue;
|
|
|
- });
|
|
|
- flush_transparent_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
|
|
- Gfx::PainterStateSaver saver(back_painter);
|
|
|
- back_painter.add_clip_rect(render_rect);
|
|
|
- render_dnd();
|
|
|
+ dirty_screen_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
|
|
+ auto screen_render_rect = render_rect.intersected(screen_rect);
|
|
|
+ if (screen_render_rect.is_empty())
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ Gfx::PainterStateSaver saver(back_painter);
|
|
|
+ back_painter.add_clip_rect(screen_render_rect);
|
|
|
+ render_dnd();
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
+ screen_data.m_flush_transparent_rects.for_each_intersected(dnd_rect, [&](const Gfx::IntRect& render_rect) {
|
|
|
+ auto screen_render_rect = render_rect.intersected(screen_rect);
|
|
|
+ if (screen_render_rect.is_empty())
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ Gfx::PainterStateSaver saver(back_painter);
|
|
|
+ back_painter.add_clip_rect(screen_render_rect);
|
|
|
+ render_dnd();
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
+ m_last_dnd_rect = dnd_rect;
|
|
|
return IterationDecision::Continue;
|
|
|
});
|
|
|
- m_last_dnd_rect = dnd_rect;
|
|
|
} else {
|
|
|
if (!m_last_dnd_rect.is_empty()) {
|
|
|
invalidate_screen(m_last_dnd_rect);
|
|
@@ -473,78 +580,98 @@ void Compositor::compose()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- run_animations(flush_special_rects);
|
|
|
+ bool did_render_animation = false;
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ auto& screen_data = m_screen_data[screen.index()];
|
|
|
+ did_render_animation |= render_animation_frame(screen, screen_data.m_flush_special_rects);
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
|
|
|
if (need_to_draw_cursor) {
|
|
|
- flush_rects.add(cursor_rect);
|
|
|
- if (cursor_rect != m_last_cursor_rect)
|
|
|
- flush_rects.add(m_last_cursor_rect);
|
|
|
- draw_cursor(cursor_rect);
|
|
|
+ auto& screen_data = m_screen_data[cursor_screen.index()];
|
|
|
+ screen_data.draw_cursor(cursor_screen, cursor_rect);
|
|
|
+ screen_data.m_flush_rects.add(cursor_rect);
|
|
|
+ if (previous_cursor_screen && cursor_rect != previous_cursor_rect)
|
|
|
+ m_screen_data[previous_cursor_screen->index()].m_flush_rects.add(previous_cursor_rect);
|
|
|
}
|
|
|
|
|
|
- if (m_flash_flush) {
|
|
|
- for (auto& rect : flush_rects.rects())
|
|
|
- m_front_painter->fill_rect(rect, Color::Yellow);
|
|
|
- }
|
|
|
-
|
|
|
- if (m_screen_can_set_buffer)
|
|
|
- flip_buffers();
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ flush(screen);
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
|
|
|
- for (auto& rect : flush_rects.rects())
|
|
|
- flush(rect);
|
|
|
- for (auto& rect : flush_transparent_rects.rects())
|
|
|
- flush(rect);
|
|
|
- for (auto& rect : flush_special_rects.rects())
|
|
|
- flush(rect);
|
|
|
+ if (did_render_animation)
|
|
|
+ step_animations();
|
|
|
}
|
|
|
|
|
|
-void Compositor::flush(const Gfx::IntRect& a_rect)
|
|
|
+void Compositor::flush(Screen& screen)
|
|
|
{
|
|
|
- auto rect = Gfx::IntRect::intersection(a_rect, Screen::the().rect());
|
|
|
-
|
|
|
- // Almost everything in Compositor is in logical coordinates, with the painters having
|
|
|
- // a scale applied. But this routine accesses the backbuffer pixels directly, so it
|
|
|
- // must work in physical coordinates.
|
|
|
- rect = rect * Screen::the().scale_factor();
|
|
|
- Gfx::RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x();
|
|
|
- Gfx::RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x();
|
|
|
- size_t pitch = m_back_bitmap->pitch();
|
|
|
-
|
|
|
- // NOTE: The meaning of a flush depends on whether we can flip buffers or not.
|
|
|
- //
|
|
|
- // If flipping is supported, flushing means that we've flipped, and now we
|
|
|
- // copy the changed bits from the front buffer to the back buffer, to keep
|
|
|
- // them in sync.
|
|
|
- //
|
|
|
- // If flipping is not supported, flushing means that we copy the changed
|
|
|
- // rects from the backing bitmap to the display framebuffer.
|
|
|
-
|
|
|
- Gfx::RGBA32* to_ptr;
|
|
|
- const Gfx::RGBA32* from_ptr;
|
|
|
-
|
|
|
- if (m_screen_can_set_buffer) {
|
|
|
- to_ptr = back_ptr;
|
|
|
- from_ptr = front_ptr;
|
|
|
- } else {
|
|
|
- to_ptr = front_ptr;
|
|
|
- from_ptr = back_ptr;
|
|
|
+ auto& screen_data = m_screen_data[screen.index()];
|
|
|
+ if (m_flash_flush) {
|
|
|
+ for (auto& rect : screen_data.m_flush_rects.rects())
|
|
|
+ screen_data.m_front_painter->fill_rect(rect, Color::Yellow);
|
|
|
}
|
|
|
|
|
|
- for (int y = 0; y < rect.height(); ++y) {
|
|
|
- fast_u32_copy(to_ptr, from_ptr, rect.width());
|
|
|
- from_ptr = (const Gfx::RGBA32*)((const u8*)from_ptr + pitch);
|
|
|
- to_ptr = (Gfx::RGBA32*)((u8*)to_ptr + pitch);
|
|
|
- }
|
|
|
+ if (screen_data.m_screen_can_set_buffer)
|
|
|
+ screen_data.flip_buffers(screen);
|
|
|
+
|
|
|
+ auto screen_rect = screen.rect();
|
|
|
+ auto do_flush = [&](const Gfx::IntRect& a_rect) {
|
|
|
+ auto rect = Gfx::IntRect::intersection(a_rect, screen_rect);
|
|
|
+ if (rect.is_empty())
|
|
|
+ return;
|
|
|
+ rect.translate_by(-screen_rect.location());
|
|
|
+
|
|
|
+ // Almost everything in Compositor is in logical coordinates, with the painters having
|
|
|
+ // a scale applied. But this routine accesses the backbuffer pixels directly, so it
|
|
|
+ // must work in physical coordinates.
|
|
|
+ rect = rect * screen.scale_factor();
|
|
|
+ Gfx::RGBA32* front_ptr = screen_data.m_front_bitmap->scanline(rect.y()) + rect.x();
|
|
|
+ Gfx::RGBA32* back_ptr = screen_data.m_back_bitmap->scanline(rect.y()) + rect.x();
|
|
|
+ size_t pitch = screen_data.m_back_bitmap->pitch();
|
|
|
+
|
|
|
+ // NOTE: The meaning of a flush depends on whether we can flip buffers or not.
|
|
|
+ //
|
|
|
+ // If flipping is supported, flushing means that we've flipped, and now we
|
|
|
+ // copy the changed bits from the front buffer to the back buffer, to keep
|
|
|
+ // them in sync.
|
|
|
+ //
|
|
|
+ // If flipping is not supported, flushing means that we copy the changed
|
|
|
+ // rects from the backing bitmap to the display framebuffer.
|
|
|
+
|
|
|
+ Gfx::RGBA32* to_ptr;
|
|
|
+ const Gfx::RGBA32* from_ptr;
|
|
|
+
|
|
|
+ if (screen_data.m_screen_can_set_buffer) {
|
|
|
+ to_ptr = back_ptr;
|
|
|
+ from_ptr = front_ptr;
|
|
|
+ } else {
|
|
|
+ to_ptr = front_ptr;
|
|
|
+ from_ptr = back_ptr;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int y = 0; y < rect.height(); ++y) {
|
|
|
+ fast_u32_copy(to_ptr, from_ptr, rect.width());
|
|
|
+ from_ptr = (const Gfx::RGBA32*)((const u8*)from_ptr + pitch);
|
|
|
+ to_ptr = (Gfx::RGBA32*)((u8*)to_ptr + pitch);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ for (auto& rect : screen_data.m_flush_rects.rects())
|
|
|
+ do_flush(rect);
|
|
|
+ for (auto& rect : screen_data.m_flush_transparent_rects.rects())
|
|
|
+ do_flush(rect);
|
|
|
+ for (auto& rect : screen_data.m_flush_special_rects.rects())
|
|
|
+ do_flush(rect);
|
|
|
}
|
|
|
|
|
|
void Compositor::invalidate_screen()
|
|
|
{
|
|
|
- invalidate_screen(Screen::the().rect());
|
|
|
+ invalidate_screen(Screen::bounding_rect());
|
|
|
}
|
|
|
|
|
|
void Compositor::invalidate_screen(const Gfx::IntRect& screen_rect)
|
|
|
{
|
|
|
- m_dirty_screen_rects.add(screen_rect.intersected(Screen::the().rect()));
|
|
|
+ m_dirty_screen_rects.add(screen_rect.intersected(Screen::bounding_rect()));
|
|
|
|
|
|
if (m_invalidated_any)
|
|
|
return;
|
|
@@ -623,19 +750,21 @@ bool Compositor::set_wallpaper(const String& path, Function<void(bool)>&& callba
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-void Compositor::flip_buffers()
|
|
|
+void Compositor::ScreenData::flip_buffers(Screen& screen)
|
|
|
{
|
|
|
VERIFY(m_screen_can_set_buffer);
|
|
|
swap(m_front_bitmap, m_back_bitmap);
|
|
|
swap(m_front_painter, m_back_painter);
|
|
|
- Screen::the().set_buffer(m_buffers_are_flipped ? 0 : 1);
|
|
|
+ screen.set_buffer(m_buffers_are_flipped ? 0 : 1);
|
|
|
m_buffers_are_flipped = !m_buffers_are_flipped;
|
|
|
}
|
|
|
|
|
|
-void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects)
|
|
|
+static const int minimize_animation_steps = 10;
|
|
|
+
|
|
|
+bool Compositor::render_animation_frame(Screen& screen, Gfx::DisjointRectSet& flush_rects)
|
|
|
{
|
|
|
- static const int minimize_animation_steps = 10;
|
|
|
- auto& painter = *m_back_painter;
|
|
|
+ bool did_render_any = false;
|
|
|
+ auto& painter = *m_screen_data[screen.index()].m_back_painter;
|
|
|
Gfx::PainterStateSaver saver(painter);
|
|
|
painter.set_draw_op(Gfx::Painter::DrawOp::Invert);
|
|
|
|
|
@@ -658,12 +787,24 @@ void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects)
|
|
|
from_rect.height() - (int)(height_delta_per_step * animation_index)
|
|
|
};
|
|
|
|
|
|
- dbgln_if(MINIMIZE_ANIMATION_DEBUG, "Minimize animation from {} to {} frame# {} {}", from_rect, to_rect, animation_index, rect);
|
|
|
+ dbgln_if(MINIMIZE_ANIMATION_DEBUG, "Minimize animation from {} to {} frame# {} {} on screen #{}", from_rect, to_rect, animation_index, rect, screen.index());
|
|
|
|
|
|
painter.draw_rect(rect, Color::Transparent); // Color doesn't matter, we draw inverted
|
|
|
flush_rects.add(rect);
|
|
|
invalidate_screen(rect);
|
|
|
|
|
|
+ did_render_any = true;
|
|
|
+ }
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
+
|
|
|
+ return did_render_any;
|
|
|
+}
|
|
|
+
|
|
|
+void Compositor::step_animations()
|
|
|
+{
|
|
|
+ WindowManager::the().window_stack().for_each_window([&](Window& window) {
|
|
|
+ if (window.in_minimize_animation()) {
|
|
|
window.step_minimize_animation();
|
|
|
if (window.minimize_animation_index() >= minimize_animation_steps)
|
|
|
window.end_minimize_animation();
|
|
@@ -672,33 +813,18 @@ void Compositor::run_animations(Gfx::DisjointRectSet& flush_rects)
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-bool Compositor::set_resolution(int desired_width, int desired_height, int scale_factor)
|
|
|
+void Compositor::screen_resolution_changed()
|
|
|
{
|
|
|
- auto screen_rect = Screen::the().rect();
|
|
|
- if (screen_rect.width() == desired_width && screen_rect.height() == desired_height && Screen::the().scale_factor() == scale_factor)
|
|
|
- return true;
|
|
|
-
|
|
|
- // Make sure it's impossible to set an invalid resolution
|
|
|
- if (!(desired_width >= 640 && desired_height >= 480 && scale_factor >= 1)) {
|
|
|
- dbgln("Compositor: Tried to set invalid resolution: {}x{}", desired_width, desired_height);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- int old_scale_factor = Screen::the().scale_factor();
|
|
|
- bool success = Screen::the().set_resolution(desired_width, desired_height, scale_factor);
|
|
|
- if (success && old_scale_factor != scale_factor)
|
|
|
- WindowManager::the().reload_icon_bitmaps_after_scale_change();
|
|
|
init_bitmaps();
|
|
|
invalidate_occlusions();
|
|
|
compose();
|
|
|
- return success;
|
|
|
}
|
|
|
|
|
|
Gfx::IntRect Compositor::current_cursor_rect() const
|
|
|
{
|
|
|
auto& wm = WindowManager::the();
|
|
|
auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor();
|
|
|
- return { Screen::the().cursor_location().translated(-current_cursor.params().hotspot()), current_cursor.size() };
|
|
|
+ return { ScreenInput::the().cursor_location().translated(-current_cursor.params().hotspot()), current_cursor.size() };
|
|
|
}
|
|
|
|
|
|
void Compositor::invalidate_cursor(bool compose_immediately)
|
|
@@ -714,13 +840,13 @@ void Compositor::invalidate_cursor(bool compose_immediately)
|
|
|
start_compose_async_timer();
|
|
|
}
|
|
|
|
|
|
-bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_damage_rect)
|
|
|
+void Compositor::draw_geometry_label(Screen& screen)
|
|
|
{
|
|
|
auto& wm = WindowManager::the();
|
|
|
auto* window_being_moved_or_resized = wm.m_move_window ? wm.m_move_window.ptr() : (wm.m_resize_window ? wm.m_resize_window.ptr() : nullptr);
|
|
|
if (!window_being_moved_or_resized) {
|
|
|
m_last_geometry_label_damage_rect = {};
|
|
|
- return false;
|
|
|
+ return;
|
|
|
}
|
|
|
auto geometry_string = window_being_moved_or_resized->rect().to_string();
|
|
|
if (!window_being_moved_or_resized->size_increment().is_null()) {
|
|
@@ -731,7 +857,7 @@ bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_damage_rect)
|
|
|
|
|
|
auto geometry_label_rect = Gfx::IntRect { 0, 0, wm.font().width(geometry_string) + 16, wm.font().glyph_height() + 10 };
|
|
|
geometry_label_rect.center_within(window_being_moved_or_resized->rect());
|
|
|
- auto desktop_rect = wm.desktop_rect();
|
|
|
+ auto desktop_rect = wm.desktop_rect(screen);
|
|
|
if (geometry_label_rect.left() < desktop_rect.left())
|
|
|
geometry_label_rect.set_left(desktop_rect.left());
|
|
|
if (geometry_label_rect.top() < desktop_rect.top())
|
|
@@ -741,14 +867,17 @@ bool Compositor::draw_geometry_label(Gfx::IntRect& geometry_label_damage_rect)
|
|
|
if (geometry_label_rect.bottom() > desktop_rect.bottom())
|
|
|
geometry_label_rect.set_bottom_without_resize(desktop_rect.bottom());
|
|
|
|
|
|
- auto& back_painter = *m_back_painter;
|
|
|
+ auto& screen_data = m_screen_data[screen.index()];
|
|
|
+ auto& back_painter = *screen_data.m_back_painter;
|
|
|
+ auto geometry_label_damage_rect = geometry_label_rect.inflated(2, 2);
|
|
|
+ Gfx::PainterStateSaver saver(back_painter);
|
|
|
+ back_painter.add_clip_rect(geometry_label_damage_rect);
|
|
|
+
|
|
|
back_painter.fill_rect(geometry_label_rect.translated(1, 1), Color(Color::Black).with_alpha(80));
|
|
|
Gfx::StylePainter::paint_button(back_painter, geometry_label_rect.translated(-1, -1), wm.palette(), Gfx::ButtonStyle::Normal, false);
|
|
|
back_painter.draw_text(geometry_label_rect.translated(-1, -1), geometry_string, Gfx::TextAlignment::Center, wm.palette().window_text());
|
|
|
|
|
|
- geometry_label_damage_rect = geometry_label_rect.inflated(2, 2);
|
|
|
m_last_geometry_label_damage_rect = geometry_label_damage_rect;
|
|
|
- return true;
|
|
|
}
|
|
|
|
|
|
void Compositor::change_cursor(const Cursor* cursor)
|
|
@@ -774,28 +903,34 @@ void Compositor::change_cursor(const Cursor* cursor)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void Compositor::draw_cursor(const Gfx::IntRect& cursor_rect)
|
|
|
+void Compositor::ScreenData::draw_cursor(Screen& screen, const Gfx::IntRect& cursor_rect)
|
|
|
{
|
|
|
auto& wm = WindowManager::the();
|
|
|
|
|
|
- if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size() || m_cursor_back_bitmap->scale() != Screen::the().scale_factor()) {
|
|
|
- m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, cursor_rect.size(), Screen::the().scale_factor());
|
|
|
+ if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size() || m_cursor_back_bitmap->scale() != screen.scale_factor()) {
|
|
|
+ m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, cursor_rect.size(), screen.scale_factor());
|
|
|
m_cursor_back_painter = make<Gfx::Painter>(*m_cursor_back_bitmap);
|
|
|
}
|
|
|
|
|
|
- auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor();
|
|
|
- m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(Screen::the().rect()));
|
|
|
- m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(m_current_cursor_frame));
|
|
|
+ auto& compositor = Compositor::the();
|
|
|
+ auto& current_cursor = compositor.m_current_cursor ? *compositor.m_current_cursor : wm.active_cursor();
|
|
|
+ auto screen_rect = screen.rect();
|
|
|
+ m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, current_cursor.rect().translated(cursor_rect.location()).intersected(screen_rect).translated(-screen_rect.location()));
|
|
|
+ m_back_painter->blit(cursor_rect.location(), current_cursor.bitmap(), current_cursor.source_rect(compositor.m_current_cursor_frame));
|
|
|
m_last_cursor_rect = cursor_rect;
|
|
|
+ VERIFY(compositor.m_current_cursor_screen == &screen);
|
|
|
+ m_cursor_back_is_valid = true;
|
|
|
}
|
|
|
|
|
|
-void Compositor::restore_cursor_back()
|
|
|
+bool Compositor::ScreenData::restore_cursor_back(Screen& screen, Gfx::IntRect& last_cursor_rect)
|
|
|
{
|
|
|
- if (!m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale())
|
|
|
- return;
|
|
|
+ if (!m_cursor_back_is_valid || !m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale())
|
|
|
+ return false;
|
|
|
|
|
|
- auto last_cursor_rect = m_last_cursor_rect.intersected(Screen::the().rect());
|
|
|
+ last_cursor_rect = m_last_cursor_rect.intersected(screen.rect());
|
|
|
m_back_painter->blit(last_cursor_rect.location(), *m_cursor_back_bitmap, { { 0, 0 }, last_cursor_rect.size() });
|
|
|
+ m_cursor_back_is_valid = false;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
void Compositor::notify_display_links()
|
|
@@ -863,14 +998,17 @@ void Compositor::recompute_occlusions()
|
|
|
|
|
|
dbgln_if(OCCLUSIONS_DEBUG, "OCCLUSIONS:");
|
|
|
|
|
|
- auto screen_rect = Screen::the().rect();
|
|
|
-
|
|
|
+ auto& main_screen = Screen::main();
|
|
|
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
|
|
|
+ // TODO: support fullscreen windows on all screens
|
|
|
+ auto screen_rect = main_screen.rect();
|
|
|
WindowManager::the().window_stack().for_each_visible_window_from_front_to_back([&](Window& w) {
|
|
|
auto& visible_opaque = w.opaque_rects();
|
|
|
auto& transparency_rects = w.transparency_rects();
|
|
|
auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects();
|
|
|
+ w.screens().clear_with_capacity();
|
|
|
if (&w == fullscreen_window) {
|
|
|
+ w.screens().append(&main_screen);
|
|
|
if (w.is_opaque()) {
|
|
|
visible_opaque = screen_rect;
|
|
|
transparency_rects.clear();
|
|
@@ -890,28 +1028,39 @@ void Compositor::recompute_occlusions()
|
|
|
|
|
|
m_opaque_wallpaper_rects.clear();
|
|
|
} else {
|
|
|
- Gfx::DisjointRectSet visible_rects(screen_rect);
|
|
|
+ Gfx::DisjointRectSet visible_rects;
|
|
|
+ visible_rects.add_many(Screen::rects());
|
|
|
bool have_transparent = false;
|
|
|
WindowManager::the().window_stack().for_each_visible_window_from_front_to_back([&](Window& w) {
|
|
|
w.transparency_wallpaper_rects().clear();
|
|
|
auto& visible_opaque = w.opaque_rects();
|
|
|
+ visible_opaque.clear();
|
|
|
auto& transparency_rects = w.transparency_rects();
|
|
|
- if (w.is_minimized()) {
|
|
|
- visible_opaque.clear();
|
|
|
- transparency_rects.clear();
|
|
|
+ transparency_rects.clear();
|
|
|
+ w.screens().clear_with_capacity();
|
|
|
+ if (w.is_minimized())
|
|
|
return IterationDecision::Continue;
|
|
|
- }
|
|
|
|
|
|
- auto transparent_render_rects = w.frame().transparent_render_rects().intersected(screen_rect);
|
|
|
- auto opaque_render_rects = w.frame().opaque_render_rects().intersected(screen_rect);
|
|
|
- if (transparent_render_rects.is_empty() && opaque_render_rects.is_empty()) {
|
|
|
- visible_opaque.clear();
|
|
|
- transparency_rects.clear();
|
|
|
+ auto transparent_frame_render_rects = w.frame().transparent_render_rects();
|
|
|
+ auto opaque_frame_render_rects = w.frame().opaque_render_rects();
|
|
|
+ Gfx::DisjointRectSet visible_opaque_rects;
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ auto screen_rect = screen.rect();
|
|
|
+ if (auto transparent_render_rects = transparent_frame_render_rects.intersected(screen_rect); !transparent_render_rects.is_empty()) {
|
|
|
+ if (transparency_rects.is_empty())
|
|
|
+ transparency_rects = move(transparent_render_rects);
|
|
|
+ else
|
|
|
+ transparency_rects.add(transparent_render_rects);
|
|
|
+ }
|
|
|
+ if (auto opaque_render_rects = opaque_frame_render_rects.intersected(screen_rect); !opaque_render_rects.is_empty()) {
|
|
|
+ if (visible_opaque_rects.is_empty())
|
|
|
+ visible_opaque_rects = move(opaque_render_rects);
|
|
|
+ else
|
|
|
+ visible_opaque_rects.add(opaque_render_rects);
|
|
|
+ }
|
|
|
return IterationDecision::Continue;
|
|
|
- }
|
|
|
-
|
|
|
- visible_opaque = visible_rects.intersected(opaque_render_rects);
|
|
|
- transparency_rects = move(transparent_render_rects);
|
|
|
+ });
|
|
|
+ visible_opaque = visible_rects.intersected(visible_opaque_rects);
|
|
|
|
|
|
auto render_rect = w.frame().render_rect();
|
|
|
|
|
@@ -967,8 +1116,31 @@ void Compositor::recompute_occlusions()
|
|
|
return IterationDecision::Continue;
|
|
|
});
|
|
|
|
|
|
+ bool have_opaque = !visible_opaque.is_empty();
|
|
|
if (!transparency_rects.is_empty())
|
|
|
have_transparent = true;
|
|
|
+ if (have_transparent || have_opaque) {
|
|
|
+ // Figure out what screens this window is rendered on
|
|
|
+ // We gather this information so we can more quickly
|
|
|
+ // render the window on each of the screens that it
|
|
|
+ // needs to be rendered on.
|
|
|
+ Screen::for_each([&](auto& screen) {
|
|
|
+ auto screen_rect = screen.rect();
|
|
|
+ for (auto& r : visible_opaque.rects()) {
|
|
|
+ if (r.intersects(screen_rect)) {
|
|
|
+ w.screens().append(&screen);
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (auto& r : transparency_rects.rects()) {
|
|
|
+ if (r.intersects(screen_rect)) {
|
|
|
+ w.screens().append(&screen);
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return IterationDecision::Continue;
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
VERIFY(!visible_opaque.intersects(transparency_rects));
|
|
|
|
|
@@ -1008,12 +1180,14 @@ void Compositor::recompute_occlusions()
|
|
|
}
|
|
|
|
|
|
wm.window_stack().for_each_visible_window_from_back_to_front([&](Window& w) {
|
|
|
- auto window_frame_rect = w.frame().render_rect().intersected(screen_rect);
|
|
|
- if (w.is_minimized() || window_frame_rect.is_empty())
|
|
|
+ auto window_frame_rect = w.frame().render_rect();
|
|
|
+ if (w.is_minimized() || window_frame_rect.is_empty() || w.screens().is_empty())
|
|
|
return IterationDecision::Continue;
|
|
|
|
|
|
if constexpr (OCCLUSIONS_DEBUG) {
|
|
|
- dbgln(" Window {} frame rect: {}", w.title(), window_frame_rect);
|
|
|
+ dbgln(" Window {} frame rect: {} rendered on screens: {}", w.title(), window_frame_rect, w.screens().size());
|
|
|
+ for (auto& s : w.screens())
|
|
|
+ dbgln(" screen: #{}", s->index());
|
|
|
for (auto& r : w.opaque_rects().rects())
|
|
|
dbgln(" opaque: {}", r);
|
|
|
for (auto& r : w.transparency_wallpaper_rects().rects())
|