Serenity Operating System
at master 1766 lines 76 kB view raw
1/* 2 * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Compositor.h" 8#include "Animation.h" 9#include "ConnectionFromClient.h" 10#include "Event.h" 11#include "EventLoop.h" 12#include "MultiScaleBitmaps.h" 13#include "Screen.h" 14#include "Window.h" 15#include "WindowManager.h" 16#include "WindowSwitcher.h" 17#include <AK/Debug.h> 18#include <AK/Memory.h> 19#include <AK/ScopeGuard.h> 20#include <LibCore/Timer.h> 21#include <LibGfx/AntiAliasingPainter.h> 22#include <LibGfx/Font/Font.h> 23#include <LibGfx/Painter.h> 24#include <LibGfx/StylePainter.h> 25#include <LibThreading/BackgroundAction.h> 26 27namespace WindowServer { 28 29Compositor& Compositor::the() 30{ 31 static Compositor s_the; 32 return s_the; 33} 34 35static WallpaperMode mode_to_enum(DeprecatedString const& name) 36{ 37 if (name == "Tile") 38 return WallpaperMode::Tile; 39 if (name == "Stretch") 40 return WallpaperMode::Stretch; 41 if (name == "Center") 42 return WallpaperMode::Center; 43 return WallpaperMode::Center; 44} 45 46Compositor::Compositor() 47{ 48 m_display_link_notify_timer = add<Core::Timer>( 49 1000 / 60, [this] { 50 notify_display_links(); 51 }); 52 53 m_compose_timer = Core::Timer::create_single_shot( 54 1000 / 60, 55 [this] { 56 compose(); 57 }, 58 this) 59 .release_value_but_fixme_should_propagate_errors(); 60 m_compose_timer->start(); 61 62 m_immediate_compose_timer = Core::Timer::create_single_shot( 63 0, 64 [this] { 65 compose(); 66 }, 67 this) 68 .release_value_but_fixme_should_propagate_errors(); 69 m_compose_timer->start(); 70 71 init_bitmaps(); 72} 73 74Gfx::Bitmap const* Compositor::cursor_bitmap_for_screenshot(Badge<ConnectionFromClient>, Screen& screen) const 75{ 76 if (!m_current_cursor) 77 return nullptr; 78 return &m_current_cursor->bitmap(screen.scale_factor()); 79} 80 81Gfx::Bitmap const& Compositor::front_bitmap_for_screenshot(Badge<ConnectionFromClient>, Screen& screen) const 82{ 83 return *screen.compositor_screen_data().m_front_bitmap; 84} 85 86Gfx::Color Compositor::color_at_position(Badge<ConnectionFromClient>, Screen& screen, Gfx::IntPoint position) const 87{ 88 return screen.compositor_screen_data().m_front_bitmap->get_pixel(position); 89} 90 91void CompositorScreenData::init_bitmaps(Compositor& compositor, Screen& screen) 92{ 93 // Recreate the screen-number overlay as the Screen instances may have changed, or get rid of it if we no longer need it 94 if (compositor.showing_screen_numbers()) { 95 m_screen_number_overlay = compositor.create_overlay<ScreenNumberOverlay>(screen); 96 m_screen_number_overlay->set_enabled(true); 97 } else { 98 m_screen_number_overlay = nullptr; 99 } 100 101 m_has_flipped = false; 102 m_have_flush_rects = false; 103 m_buffers_are_flipped = false; 104 m_screen_can_set_buffer = screen.can_set_buffer(); 105 106 m_flush_rects.clear_with_capacity(); 107 m_flush_transparent_rects.clear_with_capacity(); 108 m_flush_special_rects.clear_with_capacity(); 109 110 auto size = screen.size(); 111 m_front_bitmap = nullptr; 112 m_front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(0, 0)).release_value_but_fixme_should_propagate_errors(); 113 m_front_painter = make<Gfx::Painter>(*m_front_bitmap); 114 m_front_painter->translate(-screen.rect().location()); 115 116 m_back_bitmap = nullptr; 117 if (m_screen_can_set_buffer) 118 m_back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor(), screen.pitch(), screen.scanline(1, 0)).release_value_but_fixme_should_propagate_errors(); 119 else 120 m_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor()).release_value_but_fixme_should_propagate_errors(); 121 m_back_painter = make<Gfx::Painter>(*m_back_bitmap); 122 m_back_painter->translate(-screen.rect().location()); 123 124 m_temp_bitmap = nullptr; 125 m_temp_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, size, screen.scale_factor()).release_value_but_fixme_should_propagate_errors(); 126 m_temp_painter = make<Gfx::Painter>(*m_temp_bitmap); 127 m_temp_painter->translate(-screen.rect().location()); 128 129 clear_wallpaper_bitmap(); 130} 131 132void Compositor::init_bitmaps() 133{ 134 Screen::for_each([&](auto& screen) { 135 screen.compositor_screen_data().init_bitmaps(*this, screen); 136 return IterationDecision::Continue; 137 }); 138 139 invalidate_screen(); 140} 141 142void Compositor::did_construct_window_manager(Badge<WindowManager>) 143{ 144 auto& wm = WindowManager::the(); 145 146 m_current_window_stack = &wm.current_window_stack(); 147 148 m_wallpaper_mode = mode_to_enum(g_config->read_entry("Background", "Mode", "Center")); 149 m_custom_background_color = Color::from_string(g_config->read_entry("Background", "Color", "")); 150 151 invalidate_screen(); 152 invalidate_occlusions(); 153 compose(); 154} 155 156Gfx::IntPoint Compositor::window_transition_offset(Window& window) 157{ 158 if (WindowManager::is_stationary_window_type(window.type())) 159 return {}; 160 161 if (window.is_moving_to_another_stack()) 162 return {}; 163 164 return window.window_stack().transition_offset(); 165} 166 167void Compositor::compose() 168{ 169 auto& wm = WindowManager::the(); 170 171 { 172 auto& current_cursor = wm.active_cursor(); 173 if (m_current_cursor != &current_cursor) { 174 change_cursor(&current_cursor); 175 m_invalidated_cursor = m_invalidated_any = true; 176 } 177 } 178 179 if (!m_invalidated_any) { 180 // nothing dirtied since the last compose pass. 181 return; 182 } 183 184 if (m_occlusions_dirty) { 185 m_occlusions_dirty = false; 186 recompute_occlusions(); 187 } 188 189 // We should have recomputed occlusions if any overlay rects were changed 190 VERIFY(!m_overlay_rects_changed); 191 192 auto dirty_screen_rects = move(m_dirty_screen_rects); 193 194 bool window_stack_transition_in_progress = m_transitioning_to_window_stack != nullptr; 195 196 // Mark window regions as dirty that need to be re-rendered 197 wm.for_each_visible_window_from_back_to_front([&](Window& window) { 198 auto transition_offset = window_transition_offset(window); 199 auto frame_rect = window.frame().render_rect(); 200 auto frame_rect_on_screen = frame_rect.translated(transition_offset); 201 for (auto& dirty_rect : dirty_screen_rects.rects()) { 202 auto invalidate_rect = dirty_rect.intersected(frame_rect_on_screen); 203 if (!invalidate_rect.is_empty()) { 204 auto inner_rect_offset = window.rect().location() - frame_rect.location(); 205 invalidate_rect.translate_by(-(frame_rect.location() + inner_rect_offset + transition_offset)); 206 window.invalidate_no_notify(invalidate_rect); 207 m_invalidated_window = true; 208 } 209 } 210 window.prepare_dirty_rects(); 211 if (window_stack_transition_in_progress) 212 window.dirty_rects().translate_by(transition_offset); 213 return IterationDecision::Continue; 214 }); 215 216 // Any dirty rects in transparency areas may require windows above or below 217 // to also be marked dirty in these areas 218 wm.for_each_visible_window_from_back_to_front([&](Window& window) { 219 auto& dirty_rects = window.dirty_rects(); // dirty rects have already been adjusted for transition offset! 220 if (dirty_rects.is_empty()) 221 return IterationDecision::Continue; 222 auto& affected_transparency_rects = window.affected_transparency_rects(); 223 if (affected_transparency_rects.is_empty()) 224 return IterationDecision::Continue; 225 // If we have transparency rects that affect others, we better have transparency rects ourselves... 226 auto& transparency_rects = window.transparency_rects(); 227 VERIFY(!transparency_rects.is_empty()); 228 for (auto& it : affected_transparency_rects) { 229 auto& affected_window_dirty_rects = it.key->dirty_rects(); 230 auto& affected_rects = it.value; 231 affected_rects.for_each_intersected(dirty_rects, [&](auto& dirty_rect) { 232 affected_window_dirty_rects.add(dirty_rect); 233 return IterationDecision::Continue; 234 }); 235 } 236 return IterationDecision::Continue; 237 }); 238 239 Color background_color = wm.palette().desktop_background(); 240 if (m_custom_background_color.has_value()) 241 background_color = m_custom_background_color.value(); 242 243 if constexpr (COMPOSE_DEBUG) { 244 dbgln("COMPOSE: invalidated: window: {} cursor: {}, any: {}", m_invalidated_window, m_invalidated_cursor, m_invalidated_any); 245 for (auto& r : dirty_screen_rects.rects()) 246 dbgln("dirty screen: {}", r); 247 } 248 249 auto& cursor_screen = ScreenInput::the().cursor_location_screen(); 250 251 Screen::for_each([&](auto& screen) { 252 auto& screen_data = screen.compositor_screen_data(); 253 screen_data.m_have_flush_rects = false; 254 screen_data.m_flush_rects.clear_with_capacity(); 255 screen_data.m_flush_transparent_rects.clear_with_capacity(); 256 screen_data.m_flush_special_rects.clear_with_capacity(); 257 return IterationDecision::Continue; 258 }); 259 260 auto cursor_rect = current_cursor_rect(); 261 262 bool need_to_draw_cursor = false; 263 Gfx::IntRect previous_cursor_rect; 264 Screen* previous_cursor_screen = nullptr; 265 auto check_restore_cursor_back = [&](Screen& screen, Gfx::IntRect const& rect) { 266 if (&screen == &cursor_screen && !previous_cursor_screen && !need_to_draw_cursor && rect.intersects(cursor_rect)) { 267 // Restore what's behind the cursor if anything touches the area of the cursor 268 need_to_draw_cursor = true; 269 if (cursor_screen.compositor_screen_data().restore_cursor_back(cursor_screen, previous_cursor_rect)) 270 previous_cursor_screen = &screen; 271 } 272 }; 273 274 if (&cursor_screen != m_current_cursor_screen) { 275 // Cursor moved to another screen, restore on the cursor's background on the previous screen 276 need_to_draw_cursor = true; 277 if (m_current_cursor_screen) { 278 if (m_current_cursor_screen->compositor_screen_data().restore_cursor_back(*m_current_cursor_screen, previous_cursor_rect)) 279 previous_cursor_screen = m_current_cursor_screen; 280 } 281 m_current_cursor_screen = &cursor_screen; 282 } 283 284 auto prepare_rect = [&](Screen& screen, Gfx::IntRect const& rect) { 285 auto& screen_data = screen.compositor_screen_data(); 286 dbgln_if(COMPOSE_DEBUG, " -> flush opaque: {}", rect); 287 VERIFY(!screen_data.m_flush_rects.intersects(rect)); 288 VERIFY(!screen_data.m_flush_transparent_rects.intersects(rect)); 289 screen_data.m_have_flush_rects = true; 290 screen_data.m_flush_rects.add(rect); 291 check_restore_cursor_back(screen, rect); 292 }; 293 294 auto prepare_transparency_rect = [&](Screen& screen, Gfx::IntRect const& rect) { 295 auto& screen_data = screen.compositor_screen_data(); 296 dbgln_if(COMPOSE_DEBUG, " -> flush transparent: {}", rect); 297 VERIFY(!screen_data.m_flush_rects.intersects(rect)); 298 for (auto& r : screen_data.m_flush_transparent_rects.rects()) { 299 if (r == rect) 300 return; 301 } 302 303 screen_data.m_have_flush_rects = true; 304 screen_data.m_flush_transparent_rects.add(rect); 305 check_restore_cursor_back(screen, rect); 306 }; 307 308 if (!cursor_screen.compositor_screen_data().m_cursor_back_bitmap || m_invalidated_cursor) 309 check_restore_cursor_back(cursor_screen, cursor_rect); 310 311 auto paint_wallpaper = [&](Screen& screen, Gfx::Painter& painter, Gfx::IntRect const& rect, Gfx::IntRect const& screen_rect) { 312 if (m_wallpaper) { 313 if (m_wallpaper_mode == WallpaperMode::Center) { 314 Gfx::IntPoint offset { (screen.width() - m_wallpaper->width()) / 2, (screen.height() - m_wallpaper->height()) / 2 }; 315 316 // FIXME: If the wallpaper is opaque and covers the whole rect, no need to fill with color! 317 painter.fill_rect(rect, background_color); 318 painter.blit_offset(rect.location(), *m_wallpaper, rect.translated(-screen_rect.location()), offset); 319 } else if (m_wallpaper_mode == WallpaperMode::Tile) { 320 painter.draw_tiled_bitmap(rect, *m_wallpaper); 321 } else if (m_wallpaper_mode == WallpaperMode::Stretch) { 322 VERIFY(screen.compositor_screen_data().m_wallpaper_bitmap); 323 painter.blit(rect.location(), *screen.compositor_screen_data().m_wallpaper_bitmap, rect.translated(-screen.location())); 324 } else { 325 VERIFY_NOT_REACHED(); 326 } 327 } else { 328 painter.fill_rect(rect, background_color); 329 } 330 }; 331 332 { 333 // Paint any desktop wallpaper rects that are not somehow underneath any window transparency 334 // rects and outside of any opaque window areas 335 m_opaque_wallpaper_rects.for_each_intersected(dirty_screen_rects, [&](auto& render_rect) { 336 Screen::for_each([&](auto& screen) { 337 auto screen_rect = screen.rect(); 338 auto screen_render_rect = screen_rect.intersected(render_rect); 339 if (!screen_render_rect.is_empty()) { 340 dbgln_if(COMPOSE_DEBUG, " render wallpaper opaque: {} on screen #{}", screen_render_rect, screen.index()); 341 prepare_rect(screen, render_rect); 342 auto& back_painter = *screen.compositor_screen_data().m_back_painter; 343 paint_wallpaper(screen, back_painter, render_rect, screen_rect); 344 } 345 return IterationDecision::Continue; 346 }); 347 return IterationDecision::Continue; 348 }); 349 m_transparent_wallpaper_rects.for_each_intersected(dirty_screen_rects, [&](auto& render_rect) { 350 Screen::for_each([&](auto& screen) { 351 auto screen_rect = screen.rect(); 352 auto screen_render_rect = screen_rect.intersected(render_rect); 353 if (!screen_render_rect.is_empty()) { 354 dbgln_if(COMPOSE_DEBUG, " render wallpaper transparent: {} on screen #{}", screen_render_rect, screen.index()); 355 prepare_transparency_rect(screen, render_rect); 356 auto& temp_painter = *screen.compositor_screen_data().m_temp_painter; 357 paint_wallpaper(screen, temp_painter, render_rect, screen_rect); 358 } 359 return IterationDecision::Continue; 360 }); 361 return IterationDecision::Continue; 362 }); 363 } 364 365 auto compose_window = [&](Window& window) -> IterationDecision { 366 if (window.screens().is_empty()) { 367 // This window doesn't intersect with any screens, so there's nothing to render 368 return IterationDecision::Continue; 369 } 370 auto transition_offset = window_transition_offset(window); 371 auto frame_rect = window.frame().render_rect().translated(transition_offset); 372 auto window_rect = window.rect().translated(transition_offset); 373 auto frame_rects = frame_rect.shatter(window_rect); 374 375 dbgln_if(COMPOSE_DEBUG, " window {} frame rect: {}", window.title(), frame_rect); 376 377 RefPtr<Gfx::Bitmap> backing_store = window.backing_store(); 378 auto compose_window_rect = [&](Screen& screen, Gfx::Painter& painter, const Gfx::IntRect& rect) { 379 if (!window.is_fullscreen()) { 380 rect.for_each_intersected(frame_rects, [&](const Gfx::IntRect& intersected_rect) { 381 Gfx::PainterStateSaver saver(painter); 382 painter.add_clip_rect(intersected_rect); 383 painter.translate(transition_offset); 384 dbgln_if(COMPOSE_DEBUG, " render frame: {}", intersected_rect); 385 window.frame().paint(screen, painter, intersected_rect.translated(-transition_offset)); 386 return IterationDecision::Continue; 387 }); 388 } 389 390 auto update_window_rect = window_rect.intersected(rect); 391 if (update_window_rect.is_empty()) 392 return; 393 394 auto clear_window_rect = [&](const Gfx::IntRect& clear_rect) { 395 auto fill_color = wm.palette().window(); 396 if (!window.is_opaque()) 397 fill_color.set_alpha(255 * window.opacity()); 398 painter.fill_rect(clear_rect, fill_color); 399 }; 400 401 if (!backing_store) { 402 clear_window_rect(update_window_rect); 403 return; 404 } 405 406 // Decide where we would paint this window's backing store. 407 // This is subtly different from widow.rect(), because window 408 // size may be different from its backing store size. This 409 // happens when the window has been resized and the client 410 // has not yet attached a new backing store. In this case, 411 // we want to try to blit the backing store at the same place 412 // it was previously, and fill the rest of the window with its 413 // background color. 414 Gfx::IntRect backing_rect; 415 backing_rect.set_size(window.backing_store_visible_size()); 416 switch (WindowManager::the().resize_direction_of_window(window)) { 417 case ResizeDirection::None: 418 case ResizeDirection::Right: 419 case ResizeDirection::Down: 420 case ResizeDirection::DownRight: 421 backing_rect.set_location(window_rect.location()); 422 break; 423 case ResizeDirection::Left: 424 case ResizeDirection::Up: 425 case ResizeDirection::UpLeft: 426 backing_rect.set_right_without_resize(window_rect.right()); 427 backing_rect.set_bottom_without_resize(window_rect.bottom()); 428 break; 429 case ResizeDirection::UpRight: 430 backing_rect.set_left(window.rect().left()); 431 backing_rect.set_bottom_without_resize(window_rect.bottom()); 432 break; 433 case ResizeDirection::DownLeft: 434 backing_rect.set_right_without_resize(window_rect.right()); 435 backing_rect.set_top(window_rect.top()); 436 break; 437 default: 438 VERIFY_NOT_REACHED(); 439 break; 440 } 441 442 Gfx::IntRect dirty_rect_in_backing_coordinates = update_window_rect.intersected(backing_rect) 443 .translated(-backing_rect.location()); 444 445 if (!dirty_rect_in_backing_coordinates.is_empty()) { 446 auto dst = backing_rect.location().translated(dirty_rect_in_backing_coordinates.location()); 447 448 if (window.client() && window.client()->is_unresponsive()) { 449 if (window.is_opaque()) { 450 painter.blit_filtered(dst, *backing_store, dirty_rect_in_backing_coordinates, [](Color src) { 451 return src.to_grayscale().darkened(0.75f); 452 }); 453 } else { 454 u8 alpha = 255 * window.opacity(); 455 painter.blit_filtered(dst, *backing_store, dirty_rect_in_backing_coordinates, [&](Color src) { 456 auto color = src.to_grayscale().darkened(0.75f); 457 color.set_alpha(alpha); 458 return color; 459 }); 460 } 461 } else { 462 painter.blit(dst, *backing_store, dirty_rect_in_backing_coordinates, window.opacity()); 463 } 464 } 465 466 for (auto background_rect : update_window_rect.shatter(backing_rect)) 467 clear_window_rect(background_rect); 468 }; 469 470 auto& dirty_rects = window.dirty_rects(); 471 472 if constexpr (COMPOSE_DEBUG) { 473 for (auto& dirty_rect : dirty_rects.rects()) 474 dbgln(" dirty: {}", dirty_rect); 475 for (auto& r : window.opaque_rects().rects()) 476 dbgln(" opaque: {}", r); 477 for (auto& r : window.transparency_rects().rects()) 478 dbgln(" transparent: {}", r); 479 } 480 481 // Render opaque portions directly to the back buffer 482 auto& opaque_rects = window.opaque_rects(); 483 if (!opaque_rects.is_empty()) { 484 opaque_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) { 485 for (auto* screen : window.screens()) { 486 auto screen_render_rect = render_rect.intersected(screen->rect()); 487 if (screen_render_rect.is_empty()) 488 continue; 489 dbgln_if(COMPOSE_DEBUG, " render opaque: {} on screen #{}", screen_render_rect, screen->index()); 490 491 prepare_rect(*screen, screen_render_rect); 492 auto& back_painter = *screen->compositor_screen_data().m_back_painter; 493 Gfx::PainterStateSaver saver(back_painter); 494 back_painter.add_clip_rect(screen_render_rect); 495 compose_window_rect(*screen, back_painter, screen_render_rect); 496 } 497 return IterationDecision::Continue; 498 }); 499 } 500 501 // Render the wallpaper for any transparency directly covering 502 // the wallpaper 503 auto& transparency_wallpaper_rects = window.transparency_wallpaper_rects(); 504 if (!transparency_wallpaper_rects.is_empty()) { 505 transparency_wallpaper_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) { 506 for (auto* screen : window.screens()) { 507 auto screen_rect = screen->rect(); 508 auto screen_render_rect = render_rect.intersected(screen_rect); 509 if (screen_render_rect.is_empty()) 510 continue; 511 dbgln_if(COMPOSE_DEBUG, " render wallpaper: {} on screen #{}", screen_render_rect, screen->index()); 512 513 auto& temp_painter = *screen->compositor_screen_data().m_temp_painter; 514 prepare_transparency_rect(*screen, screen_render_rect); 515 paint_wallpaper(*screen, temp_painter, screen_render_rect, screen_rect); 516 } 517 return IterationDecision::Continue; 518 }); 519 } 520 auto& transparency_rects = window.transparency_rects(); 521 if (!transparency_rects.is_empty()) { 522 transparency_rects.for_each_intersected(dirty_rects, [&](const Gfx::IntRect& render_rect) { 523 for (auto* screen : window.screens()) { 524 auto screen_rect = screen->rect(); 525 auto screen_render_rect = render_rect.intersected(screen_rect); 526 if (screen_render_rect.is_empty()) 527 continue; 528 dbgln_if(COMPOSE_DEBUG, " render transparent: {} on screen #{}", screen_render_rect, screen->index()); 529 530 prepare_transparency_rect(*screen, screen_render_rect); 531 auto& temp_painter = *screen->compositor_screen_data().m_temp_painter; 532 Gfx::PainterStateSaver saver(temp_painter); 533 temp_painter.add_clip_rect(screen_render_rect); 534 compose_window_rect(*screen, temp_painter, screen_render_rect); 535 } 536 return IterationDecision::Continue; 537 }); 538 } 539 return IterationDecision::Continue; 540 }; 541 542 // Paint the window stack. 543 if (m_invalidated_window) { 544 auto* fullscreen_window = wm.active_fullscreen_window(); 545 // FIXME: Remove the !WindowSwitcher::the().is_visible() check when WindowSwitcher is an overlay 546 if (fullscreen_window && fullscreen_window->is_opaque() && !WindowSwitcher::the().is_visible()) { 547 compose_window(*fullscreen_window); 548 fullscreen_window->clear_dirty_rects(); 549 } else { 550 wm.for_each_visible_window_from_back_to_front([&](Window& window) { 551 compose_window(window); 552 window.clear_dirty_rects(); 553 return IterationDecision::Continue; 554 }); 555 } 556 557 // Check that there are no overlapping transparent and opaque flush rectangles 558 VERIFY(![&]() { 559 bool is_overlapping = false; 560 Screen::for_each([&](auto& screen) { 561 auto& screen_data = screen.compositor_screen_data(); 562 auto& flush_transparent_rects = screen_data.m_flush_transparent_rects; 563 auto& flush_rects = screen_data.m_flush_rects; 564 for (auto& rect_transparent : flush_transparent_rects.rects()) { 565 for (auto& rect_opaque : flush_rects.rects()) { 566 if (rect_opaque.intersects(rect_transparent)) { 567 dbgln("Transparent rect {} overlaps opaque rect: {}: {}", rect_transparent, rect_opaque, rect_opaque.intersected(rect_transparent)); 568 is_overlapping = true; 569 return IterationDecision::Break; 570 } 571 } 572 } 573 return IterationDecision::Continue; 574 }); 575 return is_overlapping; 576 }()); 577 578 if (!m_overlay_list.is_empty()) { 579 // Render everything to the temporary buffer before we copy it back 580 render_overlays(); 581 } 582 583 // Copy anything rendered to the temporary buffer to the back buffer 584 Screen::for_each([&](auto& screen) { 585 auto screen_rect = screen.rect(); 586 auto& screen_data = screen.compositor_screen_data(); 587 for (auto& rect : screen_data.m_flush_transparent_rects.rects()) 588 screen_data.m_back_painter->blit(rect.location(), *screen_data.m_temp_bitmap, rect.translated(-screen_rect.location())); 589 return IterationDecision::Continue; 590 }); 591 } 592 593 m_invalidated_any = false; 594 m_invalidated_window = false; 595 m_invalidated_cursor = false; 596 597 if (!m_animations.is_empty()) { 598 Screen::for_each([&](auto& screen) { 599 auto& screen_data = screen.compositor_screen_data(); 600 update_animations(screen, screen_data.m_flush_special_rects); 601 if (!screen_data.m_flush_special_rects.is_empty()) 602 screen_data.m_have_flush_rects = true; 603 return IterationDecision::Continue; 604 }); 605 // As long as animations are running make sure we keep rendering frames 606 m_invalidated_any = true; 607 start_compose_async_timer(); 608 } 609 610 if (need_to_draw_cursor) { 611 auto& screen_data = cursor_screen.compositor_screen_data(); 612 screen_data.draw_cursor(cursor_screen, cursor_rect); 613 } 614 615 Screen::for_each([&](auto& screen) { 616 flush(screen); 617 return IterationDecision::Continue; 618 }); 619} 620 621void Compositor::flush(Screen& screen) 622{ 623 auto& screen_data = screen.compositor_screen_data(); 624 625 bool device_can_flush_buffers = screen.can_device_flush_buffers(); 626 if (!screen_data.m_have_flush_rects && (!screen_data.m_screen_can_set_buffer || screen_data.m_has_flipped)) { 627 dbgln_if(COMPOSE_DEBUG, "Nothing to flush on screen #{} {}", screen.index(), screen_data.m_have_flush_rects); 628 return; 629 } 630 screen_data.m_have_flush_rects = false; 631 632 auto screen_rect = screen.rect(); 633 if (m_flash_flush) { 634 Gfx::IntRect bounding_flash; 635 for (auto& rect : screen_data.m_flush_rects.rects()) { 636 screen_data.m_front_painter->fill_rect(rect, Color::Yellow); 637 bounding_flash = bounding_flash.united(rect); 638 } 639 for (auto& rect : screen_data.m_flush_transparent_rects.rects()) { 640 screen_data.m_front_painter->fill_rect(rect, Color::Green); 641 bounding_flash = bounding_flash.united(rect); 642 } 643 if (!bounding_flash.is_empty()) { 644 if (screen.can_device_flush_entire_buffer()) { 645 screen.flush_display_entire_framebuffer(); 646 } else if (device_can_flush_buffers) { 647 // If the device needs a flush we need to let it know that we 648 // modified the front buffer! 649 bounding_flash.translate_by(-screen_rect.location()); 650 screen.flush_display_front_buffer((!screen_data.m_screen_can_set_buffer || !screen_data.m_buffers_are_flipped) ? 0 : 1, bounding_flash); 651 } 652 usleep(10000); 653 } 654 } 655 656 if (device_can_flush_buffers && screen_data.m_screen_can_set_buffer) { 657 if (!screen_data.m_has_flipped) { 658 // If we have not flipped any buffers before, we should be flushing 659 // the entire buffer to make sure that the device has all the bits we wrote 660 screen_data.m_flush_rects = { screen.rect() }; 661 } 662 663 // If we also support buffer flipping we need to make sure we transfer all 664 // updated areas to the device before we flip. We already modified the framebuffer 665 // memory, but the device needs to know what areas we actually did update. 666 for (auto& rect : screen_data.m_flush_rects.rects()) 667 screen.queue_flush_display_rect(rect.translated(-screen_rect.location())); 668 for (auto& rect : screen_data.m_flush_transparent_rects.rects()) 669 screen.queue_flush_display_rect(rect.translated(-screen_rect.location())); 670 for (auto& rect : screen_data.m_flush_special_rects.rects()) 671 screen.queue_flush_display_rect(rect.translated(-screen_rect.location())); 672 673 screen.flush_display((!screen_data.m_screen_can_set_buffer || screen_data.m_buffers_are_flipped) ? 0 : 1); 674 } 675 676 if (screen_data.m_screen_can_set_buffer) { 677 screen_data.flip_buffers(screen); 678 screen_data.m_has_flipped = true; 679 } 680 681 auto do_flush = [&](Gfx::IntRect rect) { 682 VERIFY(screen_rect.contains(rect)); 683 rect.translate_by(-screen_rect.location()); 684 685 // Almost everything in Compositor is in logical coordinates, with the painters having 686 // a scale applied. But this routine accesses the backbuffer pixels directly, so it 687 // must work in physical coordinates. 688 auto scaled_rect = rect * screen.scale_factor(); 689 Gfx::ARGB32* front_ptr = screen_data.m_front_bitmap->scanline(scaled_rect.y()) + scaled_rect.x(); 690 Gfx::ARGB32* back_ptr = screen_data.m_back_bitmap->scanline(scaled_rect.y()) + scaled_rect.x(); 691 size_t pitch = screen_data.m_back_bitmap->pitch(); 692 693 // NOTE: The meaning of a flush depends on whether we can flip buffers or not. 694 // 695 // If flipping is supported, flushing means that we've flipped, and now we 696 // copy the changed bits from the front buffer to the back buffer, to keep 697 // them in sync. 698 // 699 // If flipping is not supported, flushing means that we copy the changed 700 // rects from the backing bitmap to the display framebuffer. 701 702 Gfx::ARGB32* to_ptr; 703 const Gfx::ARGB32* from_ptr; 704 705 if (screen_data.m_screen_can_set_buffer) { 706 to_ptr = back_ptr; 707 from_ptr = front_ptr; 708 } else { 709 to_ptr = front_ptr; 710 from_ptr = back_ptr; 711 } 712 713 for (int y = 0; y < scaled_rect.height(); ++y) { 714 fast_u32_copy(to_ptr, from_ptr, scaled_rect.width()); 715 from_ptr = (const Gfx::ARGB32*)((const u8*)from_ptr + pitch); 716 to_ptr = (Gfx::ARGB32*)((u8*)to_ptr + pitch); 717 } 718 if (device_can_flush_buffers) { 719 // Whether or not we need to flush buffers, we need to at least track what we modified 720 // so that we can flush these areas next time before we flip buffers. Or, if we don't 721 // support buffer flipping then we will flush them shortly. 722 screen.queue_flush_display_rect(rect); 723 } 724 }; 725 for (auto& rect : screen_data.m_flush_rects.rects()) 726 do_flush(rect); 727 for (auto& rect : screen_data.m_flush_transparent_rects.rects()) 728 do_flush(rect); 729 for (auto& rect : screen_data.m_flush_special_rects.rects()) 730 do_flush(rect); 731 if (device_can_flush_buffers && !screen_data.m_screen_can_set_buffer) { 732 // If we also support flipping buffers we don't really need to flush these areas right now. 733 // Instead, we skip this step and just keep track of them until shortly before the next flip. 734 // If we however don't support flipping buffers then we need to flush the changed areas right 735 // now so that they can be sent to the device. 736 screen.flush_display(screen_data.m_buffers_are_flipped ? 1 : 0); 737 } 738} 739 740void Compositor::invalidate_screen() 741{ 742 invalidate_screen(Screen::bounding_rect()); 743} 744 745void Compositor::invalidate_screen(Gfx::IntRect const& screen_rect) 746{ 747 m_dirty_screen_rects.add(screen_rect.intersected(Screen::bounding_rect())); 748 749 if (m_invalidated_any) 750 return; 751 752 m_invalidated_any = true; 753 m_invalidated_window = true; 754 start_compose_async_timer(); 755} 756 757void Compositor::invalidate_screen(Gfx::DisjointIntRectSet const& rects) 758{ 759 m_dirty_screen_rects.add(rects.intersected(Screen::bounding_rect())); 760 761 if (m_invalidated_any) 762 return; 763 764 m_invalidated_any = true; 765 m_invalidated_window = true; 766 start_compose_async_timer(); 767} 768 769void Compositor::invalidate_window() 770{ 771 if (m_invalidated_window) 772 return; 773 m_invalidated_window = true; 774 m_invalidated_any = true; 775 776 start_compose_async_timer(); 777} 778 779void Compositor::start_compose_async_timer() 780{ 781 // We delay composition by a timer interval, but to not affect latency too 782 // much, if a pending compose is not already scheduled, we also schedule an 783 // immediate compose the next spin of the event loop. 784 if (!m_compose_timer->is_active()) { 785 m_compose_timer->start(); 786 m_immediate_compose_timer->start(); 787 } 788} 789 790bool Compositor::set_background_color(DeprecatedString const& background_color) 791{ 792 auto color = Color::from_string(background_color); 793 if (!color.has_value()) 794 return false; 795 796 m_custom_background_color = color; 797 798 g_config->write_entry("Background", "Color", background_color); 799 bool succeeded = !g_config->sync().is_error(); 800 801 if (succeeded) { 802 update_wallpaper_bitmap(); 803 Compositor::invalidate_screen(); 804 } 805 806 return succeeded; 807} 808 809bool Compositor::set_wallpaper_mode(DeprecatedString const& mode) 810{ 811 g_config->write_entry("Background", "Mode", mode); 812 bool succeeded = !g_config->sync().is_error(); 813 814 if (succeeded) { 815 m_wallpaper_mode = mode_to_enum(mode); 816 update_wallpaper_bitmap(); 817 Compositor::invalidate_screen(); 818 } 819 820 return succeeded; 821} 822 823bool Compositor::set_wallpaper(RefPtr<Gfx::Bitmap const> bitmap) 824{ 825 if (!bitmap) 826 m_wallpaper = nullptr; 827 else 828 m_wallpaper = bitmap; 829 update_wallpaper_bitmap(); 830 invalidate_screen(); 831 832 return true; 833} 834 835void Compositor::update_wallpaper_bitmap() 836{ 837 Screen::for_each([&](Screen& screen) { 838 auto& screen_data = screen.compositor_screen_data(); 839 if (m_wallpaper_mode != WallpaperMode::Stretch || !m_wallpaper) { 840 screen_data.clear_wallpaper_bitmap(); 841 return IterationDecision::Continue; 842 } 843 844 // See if there is another screen with the same resolution and scale. 845 // If so, we can use the same bitmap. 846 bool share_bitmap_with_other_screen = false; 847 Screen::for_each([&](Screen& screen2) { 848 if (&screen == &screen2) { 849 // Stop iterating here, we haven't updated wallpaper bitmaps for 850 // this screen and the following screens. 851 return IterationDecision::Break; 852 } 853 854 if (screen.size() == screen2.size() && screen.scale_factor() == screen2.scale_factor()) { 855 auto& screen2_data = screen2.compositor_screen_data(); 856 857 // Use the same bitmap as the other screen 858 screen_data.m_wallpaper_bitmap = screen2_data.m_wallpaper_bitmap; 859 share_bitmap_with_other_screen = true; 860 return IterationDecision::Break; 861 } 862 return IterationDecision::Continue; 863 }); 864 865 if (share_bitmap_with_other_screen) 866 return IterationDecision::Continue; 867 868 if (screen.size() == m_wallpaper->size() && screen.scale_factor() == m_wallpaper->scale()) { 869 // If the screen size is equal to the wallpaper size, we don't actually need to scale it 870 screen_data.m_wallpaper_bitmap = m_wallpaper; 871 } else { 872 auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, screen.size(), screen.scale_factor()).release_value_but_fixme_should_propagate_errors(); 873 874 Gfx::Painter painter(*bitmap); 875 painter.draw_scaled_bitmap(bitmap->rect(), *m_wallpaper, m_wallpaper->rect()); 876 877 screen_data.m_wallpaper_bitmap = move(bitmap); 878 } 879 return IterationDecision::Continue; 880 }); 881} 882 883void CompositorScreenData::clear_wallpaper_bitmap() 884{ 885 m_wallpaper_bitmap = nullptr; 886} 887 888void CompositorScreenData::flip_buffers(Screen& screen) 889{ 890 VERIFY(m_screen_can_set_buffer); 891 swap(m_front_bitmap, m_back_bitmap); 892 swap(m_front_painter, m_back_painter); 893 screen.set_buffer(m_buffers_are_flipped ? 0 : 1); 894 m_buffers_are_flipped = !m_buffers_are_flipped; 895} 896 897void Compositor::screen_resolution_changed() 898{ 899 // Screens may be gone now, invalidate any references to them 900 m_current_cursor_screen = nullptr; 901 902 init_bitmaps(); 903 invalidate_occlusions(); 904 overlay_rects_changed(); 905 update_wallpaper_bitmap(); 906 compose(); 907} 908 909Gfx::IntRect Compositor::current_cursor_rect() const 910{ 911 auto& wm = WindowManager::the(); 912 auto& current_cursor = m_current_cursor ? *m_current_cursor : wm.active_cursor(); 913 Gfx::IntRect cursor_rect { ScreenInput::the().cursor_location().translated(-current_cursor.params().hotspot()), current_cursor.size() }; 914 if (wm.is_cursor_highlight_enabled()) { 915 auto highlight_diameter = wm.cursor_highlight_radius() * 2; 916 auto inflate_w = highlight_diameter - cursor_rect.width(); 917 auto inflate_h = highlight_diameter - cursor_rect.height(); 918 cursor_rect.inflate(inflate_w, inflate_h); 919 // Ensures cursor stays in the same location when highlighting is enabled. 920 cursor_rect.translate_by(-(inflate_w % 2), -(inflate_h % 2)); 921 } 922 return cursor_rect; 923} 924 925void Compositor::invalidate_cursor(bool compose_immediately) 926{ 927 if (m_invalidated_cursor && !compose_immediately) 928 return; 929 m_invalidated_cursor = true; 930 m_invalidated_any = true; 931 932 if (compose_immediately) 933 compose(); 934 else 935 start_compose_async_timer(); 936} 937 938void Compositor::change_cursor(Cursor const* cursor) 939{ 940 if (m_current_cursor == cursor) 941 return; 942 m_current_cursor = cursor; 943 m_current_cursor_frame = 0; 944 if (m_cursor_timer) { 945 m_cursor_timer->stop(); 946 m_cursor_timer = nullptr; 947 } 948 if (cursor && cursor->params().frames() > 1 && cursor->params().frame_ms() != 0) { 949 m_cursor_timer = add<Core::Timer>( 950 cursor->params().frame_ms(), [this, cursor] { 951 if (m_current_cursor != cursor) 952 return; 953 auto frames = cursor->params().frames(); 954 if (++m_current_cursor_frame >= frames) 955 m_current_cursor_frame = 0; 956 invalidate_cursor(true); 957 }); 958 m_cursor_timer->start(); 959 } 960} 961 962void Compositor::render_overlays() 963{ 964 // NOTE: overlays should always be rendered to the temporary buffer! 965 for (auto& overlay : m_overlay_list) { 966 for (auto* screen : overlay.m_screens) { 967 auto& screen_data = screen->compositor_screen_data(); 968 auto& painter = screen_data.overlay_painter(); 969 970 auto render_overlay_rect = [&](auto& intersected_overlay_rect) { 971 Gfx::PainterStateSaver saver(painter); 972 painter.add_clip_rect(intersected_overlay_rect); 973 painter.translate(overlay.m_current_rect.location()); 974 overlay.render(painter, *screen); 975 return IterationDecision::Continue; 976 }; 977 978 auto const& render_rect = overlay.current_render_rect(); 979 screen_data.for_each_intersected_flushing_rect(render_rect, render_overlay_rect); 980 981 // Now render any areas that are not somehow underneath any window or transparency area 982 m_transparent_wallpaper_rects.for_each_intersected(render_rect, render_overlay_rect); 983 } 984 } 985} 986 987void Compositor::add_overlay(Overlay& overlay) 988{ 989 VERIFY(!overlay.m_list_node.is_in_list()); 990 auto zorder = overlay.zorder(); 991 bool did_insert = false; 992 for (auto& other_overlay : m_overlay_list) { 993 if (other_overlay.zorder() > zorder) { 994 m_overlay_list.insert_before(other_overlay, overlay); 995 did_insert = true; 996 break; 997 } 998 } 999 if (!did_insert) 1000 m_overlay_list.append(overlay); 1001 1002 overlay.clear_invalidated(); 1003 overlay_rects_changed(); 1004 auto& rect = overlay.rect(); 1005 if (!rect.is_empty()) 1006 invalidate_screen(rect); 1007} 1008 1009void Compositor::remove_overlay(Overlay& overlay) 1010{ 1011 auto& current_render_rect = overlay.current_render_rect(); 1012 if (!current_render_rect.is_empty()) 1013 invalidate_screen(current_render_rect); 1014 m_overlay_list.remove(overlay); 1015 overlay_rects_changed(); 1016} 1017 1018void CompositorScreenData::draw_cursor(Screen& screen, Gfx::IntRect const& cursor_rect) 1019{ 1020 auto& wm = WindowManager::the(); 1021 1022 if (!m_cursor_back_bitmap || m_cursor_back_bitmap->size() != cursor_rect.size() || m_cursor_back_bitmap->scale() != screen.scale_factor()) { 1023 m_cursor_back_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, cursor_rect.size(), screen.scale_factor()).release_value_but_fixme_should_propagate_errors(); 1024 m_cursor_back_painter = make<Gfx::Painter>(*m_cursor_back_bitmap); 1025 } 1026 1027 auto& compositor = Compositor::the(); 1028 auto& current_cursor = compositor.m_current_cursor ? *compositor.m_current_cursor : wm.active_cursor(); 1029 auto screen_rect = screen.rect(); 1030 m_cursor_back_painter->blit({ 0, 0 }, *m_back_bitmap, cursor_rect.intersected(screen_rect).translated(-screen_rect.location())); 1031 auto cursor_src_rect = current_cursor.source_rect(compositor.m_current_cursor_frame); 1032 auto cursor_blit_pos = current_cursor.rect().centered_within(cursor_rect).location(); 1033 1034 if (wm.is_cursor_highlight_enabled()) { 1035 Gfx::AntiAliasingPainter aa_back_painter { *m_back_painter }; 1036 aa_back_painter.fill_ellipse(cursor_rect, wm.cursor_highlight_color()); 1037 } 1038 m_back_painter->blit(cursor_blit_pos, current_cursor.bitmap(screen.scale_factor()), cursor_src_rect); 1039 1040 m_flush_special_rects.add(Gfx::IntRect(cursor_rect.location(), cursor_rect.size()).intersected(screen.rect())); 1041 m_have_flush_rects = true; 1042 m_last_cursor_rect = cursor_rect; 1043 VERIFY(compositor.m_current_cursor_screen == &screen); 1044 m_cursor_back_is_valid = true; 1045} 1046 1047bool CompositorScreenData::restore_cursor_back(Screen& screen, Gfx::IntRect& last_cursor_rect) 1048{ 1049 if (!m_cursor_back_is_valid || !m_cursor_back_bitmap || m_cursor_back_bitmap->scale() != m_back_bitmap->scale()) 1050 return false; 1051 1052 last_cursor_rect = m_last_cursor_rect.intersected(screen.rect()); 1053 m_back_painter->blit(last_cursor_rect.location(), *m_cursor_back_bitmap, { { 0, 0 }, last_cursor_rect.size() }); 1054 m_flush_special_rects.add(last_cursor_rect.intersected(screen.rect())); 1055 m_have_flush_rects = true; 1056 m_cursor_back_is_valid = false; 1057 return true; 1058} 1059 1060void Compositor::update_fonts() 1061{ 1062 ScreenNumberOverlay::pick_font(); 1063} 1064 1065void Compositor::notify_display_links() 1066{ 1067 ConnectionFromClient::for_each_client([](auto& client) { 1068 client.notify_display_link({}); 1069 }); 1070} 1071 1072void Compositor::increment_display_link_count(Badge<ConnectionFromClient>) 1073{ 1074 ++m_display_link_count; 1075 if (m_display_link_count == 1) 1076 m_display_link_notify_timer->start(); 1077} 1078 1079void Compositor::decrement_display_link_count(Badge<ConnectionFromClient>) 1080{ 1081 VERIFY(m_display_link_count); 1082 --m_display_link_count; 1083 if (!m_display_link_count) 1084 m_display_link_notify_timer->stop(); 1085} 1086 1087void Compositor::invalidate_current_screen_number_rects() 1088{ 1089 Screen::for_each([&](auto& screen) { 1090 auto& screen_data = screen.compositor_screen_data(); 1091 if (screen_data.m_screen_number_overlay) 1092 screen_data.m_screen_number_overlay->invalidate(); 1093 return IterationDecision::Continue; 1094 }); 1095} 1096 1097void Compositor::increment_show_screen_number(Badge<ConnectionFromClient>) 1098{ 1099 if (m_show_screen_number_count++ == 0) { 1100 Screen::for_each([&](auto& screen) { 1101 auto& screen_data = screen.compositor_screen_data(); 1102 VERIFY(!screen_data.m_screen_number_overlay); 1103 screen_data.m_screen_number_overlay = create_overlay<ScreenNumberOverlay>(screen); 1104 screen_data.m_screen_number_overlay->set_enabled(true); 1105 return IterationDecision::Continue; 1106 }); 1107 } 1108} 1109void Compositor::decrement_show_screen_number(Badge<ConnectionFromClient>) 1110{ 1111 if (--m_show_screen_number_count == 0) { 1112 invalidate_current_screen_number_rects(); 1113 Screen::for_each([&](auto& screen) { 1114 screen.compositor_screen_data().m_screen_number_overlay = nullptr; 1115 return IterationDecision::Continue; 1116 }); 1117 } 1118} 1119 1120void Compositor::overlays_theme_changed() 1121{ 1122 for (auto& overlay : m_overlay_list) 1123 overlay.theme_changed(); 1124 overlay_rects_changed(); 1125} 1126 1127void Compositor::overlay_rects_changed() 1128{ 1129 if (m_overlay_rects_changed) 1130 return; 1131 1132 m_overlay_rects_changed = true; 1133 m_invalidated_any = true; 1134 invalidate_occlusions(); 1135 for (auto& rect : m_overlay_rects.rects()) 1136 invalidate_screen(rect); 1137 start_compose_async_timer(); 1138} 1139 1140void Compositor::recompute_overlay_rects() 1141{ 1142 // The purpose of this is to gather all areas that we will render over 1143 // regular window contents. This effectively just forces those areas to 1144 // be rendered as transparency areas, which allows us to render these 1145 // flicker-free. 1146 m_overlay_rects.clear_with_capacity(); 1147 for (auto& overlay : m_overlay_list) { 1148 auto& render_rect = overlay.rect(); 1149 m_overlay_rects.add(render_rect); 1150 1151 // Save the rectangle we are using for rendering from now on 1152 overlay.did_recompute_occlusions(); 1153 1154 // Cache which screens this overlay are rendered on 1155 overlay.m_screens.clear_with_capacity(); 1156 Screen::for_each([&](auto& screen) { 1157 if (render_rect.intersects(screen.rect())) 1158 overlay.m_screens.append(&screen); 1159 return IterationDecision::Continue; 1160 }); 1161 1162 invalidate_screen(render_rect); 1163 } 1164} 1165 1166void Compositor::recompute_occlusions() 1167{ 1168 auto& wm = WindowManager::the(); 1169 bool is_switcher_visible = wm.m_switcher->is_visible(); 1170 auto never_occlude = [&](WindowStack& window_stack) { 1171 if (is_switcher_visible) { 1172 switch (wm.m_switcher->mode()) { 1173 case WindowSwitcher::Mode::ShowCurrentDesktop: 1174 // Any window on the currently rendered desktop should not be occluded, even if it's behind 1175 // another window entirely. 1176 return &window_stack == m_current_window_stack || &window_stack == m_transitioning_to_window_stack; 1177 case WindowSwitcher::Mode::ShowAllWindows: 1178 // The window switcher wants to know about all windows, even those on other desktops 1179 return true; 1180 } 1181 } 1182 return false; 1183 }; 1184 1185 wm.for_each_window_stack([&](WindowStack& window_stack) { 1186 if (&window_stack == m_current_window_stack || &window_stack == m_transitioning_to_window_stack) { 1187 // We'll calculate precise occlusions for these further down. Changing occlusions right now 1188 // may trigger an additional unnecessary notification 1189 } else { 1190 window_stack.set_all_occluded(!never_occlude(window_stack)); 1191 } 1192 return IterationDecision::Continue; 1193 }); 1194 1195 if (m_overlay_rects_changed) { 1196 m_overlay_rects_changed = false; 1197 recompute_overlay_rects(); 1198 } 1199 1200 if constexpr (OCCLUSIONS_DEBUG) { 1201 dbgln("OCCLUSIONS:"); 1202 for (auto& rect : m_overlay_rects.rects()) 1203 dbgln(" overlay: {}", rect); 1204 } 1205 1206 bool window_stack_transition_in_progress = m_transitioning_to_window_stack != nullptr; 1207 auto& main_screen = Screen::main(); 1208 auto* fullscreen_window = wm.active_fullscreen_window(); 1209 // FIXME: Remove the !WindowSwitcher::the().is_visible() check when WindowSwitcher is an overlay 1210 if (fullscreen_window && !WindowSwitcher::the().is_visible()) { 1211 // TODO: support fullscreen windows on all screens 1212 auto screen_rect = main_screen.rect(); 1213 wm.for_each_visible_window_from_front_to_back([&](Window& w) { 1214 auto& visible_opaque = w.opaque_rects(); 1215 auto& transparency_rects = w.transparency_rects(); 1216 auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects(); 1217 w.affected_transparency_rects().clear(); 1218 w.screens().clear_with_capacity(); 1219 if (&w == fullscreen_window) { 1220 w.screens().append(&main_screen); 1221 if (w.is_opaque()) { 1222 visible_opaque = screen_rect; 1223 transparency_rects.clear(); 1224 transparency_wallpaper_rects.clear(); 1225 } else { 1226 visible_opaque.clear(); 1227 transparency_rects = screen_rect; 1228 transparency_wallpaper_rects = screen_rect; 1229 } 1230 } else { 1231 visible_opaque.clear(); 1232 transparency_rects.clear(); 1233 transparency_wallpaper_rects.clear(); 1234 } 1235 return IterationDecision::Continue; 1236 }); 1237 1238 m_opaque_wallpaper_rects.clear(); 1239 } 1240 // FIXME: Remove the WindowSwitcher::the().is_visible() check when WindowSwitcher is an overlay 1241 if (!fullscreen_window || WindowSwitcher::the().is_visible() || (fullscreen_window && !fullscreen_window->is_opaque())) { 1242 Gfx::DisjointIntRectSet remaining_visible_screen_rects; 1243 remaining_visible_screen_rects.add_many(Screen::rects()); 1244 bool have_transparent = false; 1245 wm.for_each_visible_window_from_front_to_back([&](Window& w) { 1246 VERIFY(!w.is_minimized()); 1247 w.transparency_wallpaper_rects().clear(); 1248 auto previous_visible_opaque = move(w.opaque_rects()); 1249 auto previous_visible_transparency = move(w.transparency_rects()); 1250 1251 auto invalidate_previous_render_rects = [&](Gfx::IntRect const& new_render_rect) { 1252 if (!previous_visible_opaque.is_empty()) { 1253 if (new_render_rect.is_empty()) 1254 invalidate_screen(previous_visible_opaque); 1255 else 1256 invalidate_screen(previous_visible_opaque.shatter(new_render_rect)); 1257 } 1258 if (!previous_visible_transparency.is_empty()) { 1259 if (new_render_rect.is_empty()) 1260 invalidate_screen(previous_visible_transparency); 1261 else 1262 invalidate_screen(previous_visible_transparency.shatter(new_render_rect)); 1263 } 1264 }; 1265 1266 auto& visible_opaque = w.opaque_rects(); 1267 auto& transparency_rects = w.transparency_rects(); 1268 bool should_invalidate_old = w.should_invalidate_last_rendered_screen_rects(); 1269 1270 auto& affected_transparency_rects = w.affected_transparency_rects(); 1271 affected_transparency_rects.clear(); 1272 1273 w.screens().clear_with_capacity(); 1274 1275 auto transition_offset = window_transition_offset(w); 1276 auto transparent_frame_render_rects = w.frame().transparent_render_rects(); 1277 auto opaque_frame_render_rects = w.frame().opaque_render_rects(); 1278 if (window_stack_transition_in_progress) { 1279 transparent_frame_render_rects.translate_by(transition_offset); 1280 opaque_frame_render_rects.translate_by(transition_offset); 1281 } 1282 if (should_invalidate_old) { 1283 for (auto& rect : opaque_frame_render_rects.rects()) 1284 invalidate_previous_render_rects(rect); 1285 for (auto& rect : transparent_frame_render_rects.rects()) 1286 invalidate_previous_render_rects(rect); 1287 } 1288 1289 if (auto transparent_render_rects = transparent_frame_render_rects.intersected(remaining_visible_screen_rects); !transparent_render_rects.is_empty()) 1290 transparency_rects = move(transparent_render_rects); 1291 if (auto opaque_render_rects = opaque_frame_render_rects.intersected(remaining_visible_screen_rects); !opaque_render_rects.is_empty()) 1292 visible_opaque = move(opaque_render_rects); 1293 1294 auto render_rect_on_screen = w.frame().render_rect().translated(transition_offset); 1295 auto visible_window_rects = remaining_visible_screen_rects.intersected(w.rect().translated(transition_offset)); 1296 Gfx::DisjointIntRectSet opaque_covering; 1297 Gfx::DisjointIntRectSet transparent_covering; 1298 bool found_this_window = false; 1299 wm.for_each_visible_window_from_back_to_front([&](Window& w2) { 1300 if (!found_this_window) { 1301 if (&w == &w2) 1302 found_this_window = true; 1303 return IterationDecision::Continue; 1304 } 1305 1306 VERIFY(!w2.is_minimized()); 1307 1308 auto w2_render_rect = w2.frame().render_rect(); 1309 auto w2_render_rect_on_screen = w2_render_rect; 1310 auto w2_transition_offset = window_transition_offset(w2); 1311 if (window_stack_transition_in_progress) 1312 w2_render_rect_on_screen.translate_by(w2_transition_offset); 1313 if (!render_rect_on_screen.intersects(w2_render_rect_on_screen)) 1314 return IterationDecision::Continue; 1315 1316 auto opaque_rects = w2.frame().opaque_render_rects(); 1317 auto transparent_rects = w2.frame().transparent_render_rects(); 1318 if (window_stack_transition_in_progress) { 1319 auto transition_offset_2 = window_transition_offset(w2); 1320 opaque_rects.translate_by(transition_offset_2); 1321 transparent_rects.translate_by(transition_offset_2); 1322 } 1323 opaque_rects = opaque_rects.intersected(render_rect_on_screen); 1324 transparent_rects = transparent_rects.intersected(render_rect_on_screen); 1325 if (opaque_rects.is_empty() && transparent_rects.is_empty()) 1326 return IterationDecision::Continue; 1327 VERIFY(!opaque_rects.intersects(transparent_rects)); 1328 for (auto& covering : opaque_rects.rects()) { 1329 opaque_covering.add(covering); 1330 if (!visible_window_rects.is_empty()) 1331 visible_window_rects = visible_window_rects.shatter(covering); 1332 if (!visible_opaque.is_empty()) { 1333 auto uncovered_opaque = visible_opaque.shatter(covering); 1334 visible_opaque = move(uncovered_opaque); 1335 } 1336 if (!transparency_rects.is_empty()) { 1337 auto uncovered_transparency = transparency_rects.shatter(covering); 1338 transparency_rects = move(uncovered_transparency); 1339 } 1340 if (!transparent_covering.is_empty()) { 1341 auto uncovered_transparency = transparent_covering.shatter(covering); 1342 transparent_covering = move(uncovered_transparency); 1343 } 1344 } 1345 if (!transparent_rects.is_empty()) 1346 transparent_covering.add(transparent_rects.shatter(opaque_covering)); 1347 VERIFY(!transparent_covering.intersects(opaque_covering)); 1348 return IterationDecision::Continue; 1349 }); 1350 VERIFY(opaque_covering.is_empty() || render_rect_on_screen.contains(opaque_covering.rects())); 1351 if (!m_overlay_rects.is_empty() && m_overlay_rects.intersects(visible_opaque)) { 1352 // In order to render overlays flicker-free we need to force this area into the 1353 // temporary transparency rendering buffer 1354 transparent_covering.add(m_overlay_rects.intersected(visible_opaque)); 1355 } 1356 if (!transparent_covering.is_empty()) { 1357 VERIFY(!transparent_covering.intersects(opaque_covering)); 1358 transparency_rects.add(transparent_covering); 1359 if (!visible_opaque.is_empty()) { 1360 auto uncovered_opaque = visible_opaque.shatter(transparent_covering); 1361 visible_opaque = move(uncovered_opaque); 1362 } 1363 1364 // Now that we know what transparency rectangles are immediately covering our window 1365 // figure out what windows they belong to and add them to the affected transparency rects. 1366 // We can't do the same with the windows below as we haven't gotten to those yet. These 1367 // will be determined after we're done with this pass. 1368 found_this_window = false; 1369 wm.for_each_visible_window_from_back_to_front([&](Window& w2) { 1370 if (!found_this_window) { 1371 if (&w == &w2) 1372 found_this_window = true; 1373 return IterationDecision::Continue; 1374 } 1375 1376 auto affected_transparency = transparent_covering.intersected(w2.transparency_rects()); 1377 if (!affected_transparency.is_empty()) { 1378 auto result = affected_transparency_rects.set(&w2, move(affected_transparency)); 1379 VERIFY(result == AK::HashSetResult::InsertedNewEntry); 1380 } 1381 return IterationDecision::Continue; 1382 }); 1383 } 1384 1385 // This window should not be occluded while the window switcher is interested in it (depending 1386 // on the mode it's in). If it isn't then determine occlusions based on whether the window 1387 // rect has any visible areas at all. 1388 w.set_occluded(never_occlude(w.window_stack()) ? false : visible_window_rects.is_empty()); 1389 1390 bool have_opaque = !visible_opaque.is_empty(); 1391 if (!transparency_rects.is_empty()) 1392 have_transparent = true; 1393 if (have_transparent || have_opaque) { 1394 // Figure out what screens this window is rendered on 1395 // We gather this information so we can more quickly 1396 // render the window on each of the screens that it 1397 // needs to be rendered on. 1398 Screen::for_each([&](auto& screen) { 1399 auto screen_rect = screen.rect(); 1400 for (auto& r : visible_opaque.rects()) { 1401 if (r.intersects(screen_rect)) { 1402 w.screens().append(&screen); 1403 return IterationDecision::Continue; 1404 } 1405 } 1406 for (auto& r : transparency_rects.rects()) { 1407 if (r.intersects(screen_rect)) { 1408 w.screens().append(&screen); 1409 return IterationDecision::Continue; 1410 } 1411 } 1412 return IterationDecision::Continue; 1413 }); 1414 } 1415 1416 if (!visible_opaque.is_empty()) { 1417 VERIFY(!visible_opaque.intersects(transparency_rects)); 1418 1419 // Determine visible area for the window below 1420 remaining_visible_screen_rects = remaining_visible_screen_rects.shatter(visible_opaque); 1421 } 1422 return IterationDecision::Continue; 1423 }); 1424 1425 if (have_transparent) { 1426 // Also, now that we have completed the first pass we can determine the affected 1427 // transparency rects below a given window 1428 wm.for_each_visible_window_from_back_to_front([&](Window& w) { 1429 // Any area left in remaining_visible_screen_rects will need to be rendered with the wallpaper first 1430 auto& transparency_rects = w.transparency_rects(); 1431 auto& transparency_wallpaper_rects = w.transparency_wallpaper_rects(); 1432 if (transparency_rects.is_empty()) { 1433 VERIFY(transparency_wallpaper_rects.is_empty()); // Should have been cleared in the first pass 1434 } else { 1435 transparency_wallpaper_rects = remaining_visible_screen_rects.intersected(transparency_rects); 1436 1437 if (!transparency_wallpaper_rects.is_empty()) { 1438 auto remaining_visible = remaining_visible_screen_rects.shatter(transparency_wallpaper_rects); 1439 remaining_visible_screen_rects = move(remaining_visible); 1440 } 1441 } 1442 1443 // Figure out the affected transparency rects underneath. First figure out if any transparency is visible at all 1444 Gfx::DisjointIntRectSet transparent_underneath; 1445 wm.for_each_visible_window_from_back_to_front([&](Window& w2) { 1446 if (&w == &w2) 1447 return IterationDecision::Break; 1448 auto& opaque_rects2 = w2.opaque_rects(); 1449 if (!opaque_rects2.is_empty()) { 1450 auto uncovered_transparency = transparent_underneath.shatter(opaque_rects2); 1451 transparent_underneath = move(uncovered_transparency); 1452 } 1453 w2.transparency_rects().for_each_intersected(transparency_rects, [&](auto& rect) { 1454 transparent_underneath.add(rect); 1455 return IterationDecision::Continue; 1456 }); 1457 1458 return IterationDecision::Continue; 1459 }); 1460 if (!transparent_underneath.is_empty()) { 1461 // Now that we know there are some transparency rects underneath that are visible 1462 // figure out what windows they belong to 1463 auto& affected_transparency_rects = w.affected_transparency_rects(); 1464 wm.for_each_visible_window_from_back_to_front([&](Window& w2) { 1465 if (&w == &w2) 1466 return IterationDecision::Break; 1467 auto& transparency_rects2 = w2.transparency_rects(); 1468 if (transparency_rects2.is_empty()) 1469 return IterationDecision::Continue; 1470 1471 auto affected_transparency = transparent_underneath.intersected(transparency_rects2); 1472 if (!affected_transparency.is_empty()) { 1473 auto result = affected_transparency_rects.set(&w2, move(affected_transparency)); 1474 VERIFY(result == AK::HashSetResult::InsertedNewEntry); 1475 } 1476 return IterationDecision::Continue; 1477 }); 1478 } 1479 return IterationDecision::Continue; 1480 }); 1481 } 1482 1483 m_transparent_wallpaper_rects.clear_with_capacity(); 1484 if (!m_overlay_rects.is_empty() && m_overlay_rects.intersects(remaining_visible_screen_rects)) { 1485 // Check if any overlay rects are remaining that are not somehow above any windows 1486 m_transparent_wallpaper_rects = m_overlay_rects.intersected(remaining_visible_screen_rects); 1487 auto remaining_visible_not_covered = remaining_visible_screen_rects.shatter(m_overlay_rects); 1488 remaining_visible_screen_rects = move(remaining_visible_not_covered); 1489 } 1490 1491 m_opaque_wallpaper_rects = move(remaining_visible_screen_rects); 1492 } 1493 1494 if constexpr (OCCLUSIONS_DEBUG) { 1495 for (auto& r : m_opaque_wallpaper_rects.rects()) 1496 dbgln(" wallpaper opaque: {}", r); 1497 } 1498 1499 wm.for_each_visible_window_from_back_to_front([&](Window& w) { 1500 auto window_frame_rect = w.frame().render_rect(); 1501 if (w.is_minimized() || window_frame_rect.is_empty() || w.screens().is_empty()) 1502 return IterationDecision::Continue; 1503 1504 if constexpr (OCCLUSIONS_DEBUG) { 1505 dbgln(" Window {} frame rect: {} rendered on screens: {}", w.title(), window_frame_rect, w.screens().size()); 1506 for (auto& s : w.screens()) 1507 dbgln(" screen: #{}", s->index()); 1508 for (auto& r : w.opaque_rects().rects()) 1509 dbgln(" opaque: {}", r); 1510 for (auto& r : w.transparency_wallpaper_rects().rects()) 1511 dbgln(" transparent wallpaper: {}", r); 1512 for (auto& r : w.transparency_rects().rects()) 1513 dbgln(" transparent: {}", r); 1514 for (auto& it : w.affected_transparency_rects()) { 1515 dbgln(" affects {}:", it.key->title()); 1516 for (auto& r : it.value.rects()) 1517 dbgln(" transparent: {}", r); 1518 } 1519 } 1520 1521 VERIFY(!w.opaque_rects().intersects(m_opaque_wallpaper_rects)); 1522 VERIFY(!w.transparency_rects().intersects(m_opaque_wallpaper_rects)); 1523 VERIFY(!w.transparency_wallpaper_rects().intersects(m_opaque_wallpaper_rects)); 1524 return IterationDecision::Continue; 1525 }); 1526} 1527 1528void Compositor::register_animation(Badge<Animation>, Animation& animation) 1529{ 1530 bool was_empty = m_animations.is_empty(); 1531 auto result = m_animations.set(&animation); 1532 VERIFY(result == AK::HashSetResult::InsertedNewEntry); 1533 if (was_empty) 1534 start_compose_async_timer(); 1535} 1536 1537void Compositor::animation_started(Badge<Animation>) 1538{ 1539 m_invalidated_any = true; 1540 start_compose_async_timer(); 1541} 1542 1543void Compositor::unregister_animation(Badge<Animation>, Animation& animation) 1544{ 1545 bool was_removed = m_animations.remove(&animation); 1546 VERIFY(was_removed); 1547} 1548 1549void Compositor::update_animations(Screen& screen, Gfx::DisjointIntRectSet& flush_rects) 1550{ 1551 auto& painter = *screen.compositor_screen_data().m_back_painter; 1552 // Iterating over the animations using remove_all_matching we can iterate 1553 // and immediately remove finished animations without having to keep track 1554 // of them in a separate container. 1555 m_animations.remove_all_matching([&](auto* animation) { 1556 if (!animation->update({}, painter, screen, flush_rects)) { 1557 // Mark it as removed so that the Animation::on_stop handler doesn't 1558 // trigger the Animation object from being destroyed, causing it to 1559 // unregister while we still loop over them. 1560 animation->was_removed({}); 1561 1562 // Temporarily bump the ref count so that if the Animation::on_stop 1563 // handler clears its own reference, it doesn't immediately destroy 1564 // itself while we're still in the Function<> call 1565 NonnullRefPtr<Animation> protect_animation(*animation); 1566 animation->stop(); 1567 return true; 1568 } 1569 return false; 1570 }); 1571} 1572 1573void Compositor::create_window_stack_switch_overlay(WindowStack& target_stack) 1574{ 1575 stop_window_stack_switch_overlay_timer(); 1576 Screen::for_each([&](auto& screen) { 1577 auto& screen_data = screen.compositor_screen_data(); 1578 screen_data.m_window_stack_switch_overlay = nullptr; // delete it first 1579 screen_data.m_window_stack_switch_overlay = create_overlay<WindowStackSwitchOverlay>(screen, target_stack); 1580 screen_data.m_window_stack_switch_overlay->set_enabled(true); 1581 return IterationDecision::Continue; 1582 }); 1583} 1584 1585void Compositor::remove_window_stack_switch_overlays() 1586{ 1587 Screen::for_each([&](auto& screen) { 1588 screen.compositor_screen_data().m_window_stack_switch_overlay = nullptr; 1589 return IterationDecision::Continue; 1590 }); 1591} 1592 1593void Compositor::stop_window_stack_switch_overlay_timer() 1594{ 1595 if (m_stack_switch_overlay_timer) { 1596 // Cancel any timer, we're going to delete the overlay 1597 m_stack_switch_overlay_timer->stop(); 1598 m_stack_switch_overlay_timer = nullptr; 1599 } 1600} 1601 1602void Compositor::start_window_stack_switch_overlay_timer() 1603{ 1604 if (m_stack_switch_overlay_timer) { 1605 m_stack_switch_overlay_timer->stop(); 1606 m_stack_switch_overlay_timer = nullptr; 1607 } 1608 bool have_overlay = false; 1609 Screen::for_each([&](auto& screen) { 1610 if (screen.compositor_screen_data().m_window_stack_switch_overlay) { 1611 have_overlay = true; 1612 return IterationDecision::Break; 1613 } 1614 return IterationDecision::Continue; 1615 }); 1616 if (!have_overlay) 1617 return; 1618 m_stack_switch_overlay_timer = Core::Timer::create_single_shot( 1619 500, 1620 [this] { 1621 remove_window_stack_switch_overlays(); 1622 }, 1623 this) 1624 .release_value_but_fixme_should_propagate_errors(); 1625 m_stack_switch_overlay_timer->start(); 1626} 1627 1628void Compositor::finish_window_stack_switch() 1629{ 1630 VERIFY(m_transitioning_to_window_stack); 1631 VERIFY(m_current_window_stack); 1632 VERIFY(m_transitioning_to_window_stack != m_current_window_stack); 1633 1634 m_current_window_stack->set_transition_offset({}, {}); 1635 m_transitioning_to_window_stack->set_transition_offset({}, {}); 1636 1637 auto* previous_window_stack = m_current_window_stack; 1638 m_current_window_stack = m_transitioning_to_window_stack; 1639 m_transitioning_to_window_stack = nullptr; 1640 1641 m_window_stack_transition_animation = nullptr; 1642 1643 auto& wm = WindowManager::the(); 1644 if (!wm.m_switcher->is_visible()) 1645 previous_window_stack->set_all_occluded(true); 1646 wm.did_switch_window_stack({}, *previous_window_stack, *m_current_window_stack); 1647 1648 invalidate_occlusions(); 1649 1650 // Rather than invalidating the entire we could invalidate all render rectangles 1651 // that are affected by the transition offset before and after changing it. 1652 invalidate_screen(); 1653 1654 start_window_stack_switch_overlay_timer(); 1655} 1656 1657void Compositor::set_current_window_stack_no_transition(WindowStack& new_window_stack) 1658{ 1659 if (m_transitioning_to_window_stack) { 1660 finish_window_stack_switch(); 1661 VERIFY(!m_window_stack_transition_animation); 1662 VERIFY(!m_transitioning_to_window_stack); 1663 } 1664 if (m_current_window_stack == &new_window_stack) 1665 return; 1666 m_current_window_stack = &new_window_stack; 1667 invalidate_for_window_stack_merge_or_change(); 1668} 1669 1670void Compositor::invalidate_for_window_stack_merge_or_change() 1671{ 1672 invalidate_occlusions(); 1673 invalidate_screen(); 1674} 1675 1676void Compositor::switch_to_window_stack(WindowStack& new_window_stack, bool show_overlay) 1677{ 1678 if (m_transitioning_to_window_stack) { 1679 if (m_transitioning_to_window_stack == &new_window_stack) 1680 return; 1681 // A switch is in progress, but the user is impatient. Finish the transition instantly 1682 finish_window_stack_switch(); 1683 VERIFY(!m_window_stack_transition_animation); 1684 // Now switch to the next target as usual 1685 } 1686 VERIFY(m_current_window_stack); 1687 1688 if (&new_window_stack == m_current_window_stack) { 1689 // So that the user knows which stack they're on, show the overlay briefly 1690 if (show_overlay) { 1691 create_window_stack_switch_overlay(*m_current_window_stack); 1692 start_window_stack_switch_overlay_timer(); 1693 } else { 1694 stop_window_stack_switch_overlay_timer(); 1695 remove_window_stack_switch_overlays(); 1696 } 1697 return; 1698 } 1699 VERIFY(!m_transitioning_to_window_stack); 1700 m_transitioning_to_window_stack = &new_window_stack; 1701 1702 auto window_stack_size = Screen::bounding_rect().size(); 1703 1704 int delta_x = 0; 1705 if (new_window_stack.column() < m_current_window_stack->column()) 1706 delta_x = window_stack_size.width(); 1707 else if (new_window_stack.column() > m_current_window_stack->column()) 1708 delta_x = -window_stack_size.width(); 1709 int delta_y = 0; 1710 if (new_window_stack.row() < m_current_window_stack->row()) 1711 delta_y = window_stack_size.height(); 1712 else if (new_window_stack.row() > m_current_window_stack->row()) { 1713 delta_y = -window_stack_size.height(); 1714 } 1715 1716 m_transitioning_to_window_stack->set_transition_offset({}, { -delta_x, -delta_y }); 1717 m_current_window_stack->set_transition_offset({}, {}); 1718 1719 if (show_overlay) { 1720 // We start the timer when the animation ends! 1721 create_window_stack_switch_overlay(*m_transitioning_to_window_stack); 1722 } else { 1723 stop_window_stack_switch_overlay_timer(); 1724 remove_window_stack_switch_overlays(); 1725 } 1726 1727 VERIFY(!m_window_stack_transition_animation); 1728 m_window_stack_transition_animation = Animation::create(); 1729 m_window_stack_transition_animation->set_duration(250); 1730 m_window_stack_transition_animation->on_update = [this, delta_x, delta_y](float progress, Gfx::Painter&, Screen&, Gfx::DisjointIntRectSet&) { 1731 VERIFY(m_transitioning_to_window_stack); 1732 VERIFY(m_current_window_stack); 1733 1734 // Set transition offset for the window stack we're transitioning out of 1735 auto previous_transition_offset_from = m_current_window_stack->transition_offset(); 1736 Gfx::IntPoint transition_offset_from { (float)delta_x * progress, (float)delta_y * progress }; 1737 if (previous_transition_offset_from == transition_offset_from) 1738 return; 1739 1740 { 1741 // we need to render both, the existing dirty rectangles as well as where we're shifting to 1742 auto translated_dirty_rects = m_dirty_screen_rects.clone(); 1743 auto transition_delta = transition_offset_from - previous_transition_offset_from; 1744 translated_dirty_rects.translate_by(transition_delta); 1745 m_dirty_screen_rects.add(translated_dirty_rects.intersected(Screen::bounding_rect())); 1746 } 1747 m_current_window_stack->set_transition_offset({}, transition_offset_from); 1748 1749 // Set transition offset for the window stack we're transitioning to 1750 Gfx::IntPoint transition_offset_to { (float)-delta_x * (1.0f - progress), (float)-delta_y * (1.0f - progress) }; 1751 m_transitioning_to_window_stack->set_transition_offset({}, transition_offset_to); 1752 1753 invalidate_occlusions(); 1754 1755 // Rather than invalidating the entire we could invalidate all render rectangles 1756 // that are affected by the transition offset before and after changing it. 1757 invalidate_screen(); 1758 }; 1759 1760 m_window_stack_transition_animation->on_stop = [this] { 1761 finish_window_stack_switch(); 1762 }; 1763 m_window_stack_transition_animation->start(); 1764} 1765 1766}