123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- #include "WSCompositor.h"
- #include "WSEvent.h"
- #include "WSEventLoop.h"
- #include "WSScreen.h"
- #include "WSWindow.h"
- #include "WSWindowManager.h"
- #include <LibDraw/Font.h>
- #include <LibDraw/PNGLoader.h>
- #include <LibDraw/Painter.h>
- #include <LibThread/BackgroundAction.h>
- // #define COMPOSITOR_DEBUG
- WSCompositor& WSCompositor::the()
- {
- static WSCompositor s_the;
- return s_the;
- }
- WallpaperMode mode_to_enum(const String& name)
- {
- if (name == "simple")
- return WallpaperMode::Simple;
- if (name == "tile")
- return WallpaperMode::Tile;
- if (name == "center")
- return WallpaperMode::Center;
- if (name == "scaled")
- return WallpaperMode::Scaled;
- return WallpaperMode::Simple;
- }
- WSCompositor::WSCompositor()
- {
- m_compose_timer = CTimer::construct(this);
- m_immediate_compose_timer = CTimer::construct(this);
- m_screen_can_set_buffer = WSScreen::the().can_set_buffer();
- init_bitmaps();
- m_compose_timer->on_timeout = [&]() {
- #if defined(COMPOSITOR_DEBUG)
- dbgprintf("WSCompositor: delayed frame callback: %d rects\n", m_dirty_rects.size());
- #endif
- compose();
- };
- m_compose_timer->set_single_shot(true);
- m_compose_timer->set_interval(1000 / 60);
- m_immediate_compose_timer->on_timeout = [=]() {
- #if defined(COMPOSITOR_DEBUG)
- dbgprintf("WSCompositor: immediate frame callback: %d rects\n", m_dirty_rects.size());
- #endif
- compose();
- };
- m_immediate_compose_timer->set_single_shot(true);
- m_immediate_compose_timer->set_interval(0);
- }
- void WSCompositor::init_bitmaps()
- {
- auto& screen = WSScreen::the();
- auto size = screen.size();
- m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, screen.pitch(), screen.scanline(0));
- if (m_screen_can_set_buffer)
- m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, screen.pitch(), screen.scanline(size.height()));
- else
- m_back_bitmap = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, size);
- m_front_painter = make<Painter>(*m_front_bitmap);
- m_back_painter = make<Painter>(*m_back_bitmap);
- m_buffers_are_flipped = false;
- invalidate();
- }
- void WSCompositor::compose()
- {
- auto& wm = WSWindowManager::the();
- if (m_wallpaper_mode == WallpaperMode::Unchecked)
- m_wallpaper_mode = mode_to_enum(wm.wm_config()->read_entry("Background", "Mode", "simple"));
- auto& ws = WSScreen::the();
- auto dirty_rects = move(m_dirty_rects);
- if (dirty_rects.size() == 0) {
- // nothing dirtied since the last compose pass.
- return;
- }
- dirty_rects.add(Rect::intersection(m_last_geometry_label_rect, WSScreen::the().rect()));
- dirty_rects.add(Rect::intersection(m_last_cursor_rect, WSScreen::the().rect()));
- dirty_rects.add(Rect::intersection(current_cursor_rect(), WSScreen::the().rect()));
- #ifdef DEBUG_COUNTERS
- dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.rects().size());
- #endif
- auto any_dirty_rect_intersects_window = [&dirty_rects](const WSWindow& window) {
- auto window_frame_rect = window.frame().rect();
- for (auto& dirty_rect : dirty_rects.rects()) {
- if (dirty_rect.intersects(window_frame_rect))
- return true;
- }
- return false;
- };
- for (auto& dirty_rect : dirty_rects.rects()) {
- if (wm.any_opaque_window_contains_rect(dirty_rect))
- continue;
- m_back_painter->fill_rect(dirty_rect, wm.m_background_color);
- if (m_wallpaper) {
- if (m_wallpaper_mode == WallpaperMode::Simple) {
- m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);
- } else if (m_wallpaper_mode == WallpaperMode::Center) {
- Point offset { ws.size().width() / 2 - m_wallpaper->size().width() / 2,
- ws.size().height() / 2 - m_wallpaper->size().height() / 2 };
- m_back_painter->blit_offset(dirty_rect.location(), *m_wallpaper,
- dirty_rect, offset);
- } else if (m_wallpaper_mode == WallpaperMode::Tile) {
- m_back_painter->blit_tiled(dirty_rect.location(), *m_wallpaper, dirty_rect);
- } else {
- float hscale = (float)m_wallpaper->size().width() / (float)ws.size().width();
- float vscale = (float)m_wallpaper->size().height() / (float)ws.size().height();
- m_back_painter->blit_scaled(dirty_rect, *m_wallpaper, dirty_rect, hscale, vscale);
- }
- }
- }
- auto compose_window = [&](WSWindow& window) -> IterationDecision {
- if (!any_dirty_rect_intersects_window(window))
- return IterationDecision::Continue;
- PainterStateSaver saver(*m_back_painter);
- m_back_painter->add_clip_rect(window.frame().rect());
- RefPtr<GraphicsBitmap> backing_store = window.backing_store();
- for (auto& dirty_rect : dirty_rects.rects()) {
- if (wm.any_opaque_window_above_this_one_contains_rect(window, dirty_rect))
- continue;
- PainterStateSaver saver(*m_back_painter);
- m_back_painter->add_clip_rect(dirty_rect);
- if (!backing_store)
- m_back_painter->fill_rect(dirty_rect, window.background_color());
- if (!window.is_fullscreen())
- window.frame().paint(*m_back_painter);
- if (!backing_store)
- continue;
- Rect dirty_rect_in_window_coordinates = Rect::intersection(dirty_rect, window.rect());
- if (dirty_rect_in_window_coordinates.is_empty())
- continue;
- dirty_rect_in_window_coordinates.move_by(-window.position());
- auto dst = window.position();
- dst.move_by(dirty_rect_in_window_coordinates.location());
- m_back_painter->blit(dst, *backing_store, dirty_rect_in_window_coordinates, window.opacity());
- if (backing_store->width() < window.width()) {
- Rect right_fill_rect { window.x() + backing_store->width(), window.y(), window.width() - backing_store->width(), window.height() };
- m_back_painter->fill_rect(right_fill_rect, window.background_color());
- }
- if (backing_store->height() < window.height()) {
- Rect bottom_fill_rect { window.x(), window.y() + backing_store->height(), window.width(), window.height() - backing_store->height() };
- m_back_painter->fill_rect(bottom_fill_rect, window.background_color());
- }
- }
- return IterationDecision::Continue;
- };
- if (auto* fullscreen_window = wm.active_fullscreen_window()) {
- compose_window(*fullscreen_window);
- } else {
- wm.for_each_visible_window_from_back_to_front([&](WSWindow& window) {
- return compose_window(window);
- });
- draw_geometry_label();
- }
- draw_cursor();
- if (m_flash_flush) {
- for (auto& rect : dirty_rects.rects())
- m_front_painter->fill_rect(rect, Color::Yellow);
- }
- if (m_screen_can_set_buffer)
- flip_buffers();
- for (auto& r : dirty_rects.rects())
- flush(r);
- }
- void WSCompositor::flush(const Rect& a_rect)
- {
- auto rect = Rect::intersection(a_rect, WSScreen::the().rect());
- #ifdef DEBUG_COUNTERS
- dbgprintf("[WM] flush #%u (%d,%d %dx%d)\n", ++m_flush_count, rect.x(), rect.y(), rect.width(), rect.height());
- #endif
- RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x();
- 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.
- RGBA32* to_ptr;
- const 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;
- }
- for (int y = 0; y < rect.height(); ++y) {
- fast_u32_copy(to_ptr, from_ptr, rect.width());
- from_ptr = (const RGBA32*)((const u8*)from_ptr + pitch);
- to_ptr = (RGBA32*)((u8*)to_ptr + pitch);
- }
- }
- void WSCompositor::invalidate()
- {
- m_dirty_rects.clear_with_capacity();
- invalidate(WSScreen::the().rect());
- }
- void WSCompositor::invalidate(const Rect& a_rect)
- {
- auto rect = Rect::intersection(a_rect, WSScreen::the().rect());
- if (rect.is_empty())
- return;
- m_dirty_rects.add(rect);
- // We delay composition by a timer interval, but to not affect latency too
- // much, if a pending compose is not already scheduled, we also schedule an
- // immediate compose the next spin of the event loop.
- if (!m_compose_timer->is_active()) {
- #if defined(COMPOSITOR_DEBUG)
- dbgprintf("Invalidated (starting immediate frame): %dx%d %dx%d\n", a_rect.x(), a_rect.y(), a_rect.width(), a_rect.height());
- #endif
- m_compose_timer->start();
- m_immediate_compose_timer->start();
- } else {
- #if defined(COMPOSITOR_DEBUG)
- dbgprintf("Invalidated (frame callback pending): %dx%d %dx%d\n", a_rect.x(), a_rect.y(), a_rect.width(), a_rect.height());
- #endif
- }
- }
- bool WSCompositor::set_wallpaper(const String& path, Function<void(bool)>&& callback)
- {
- LibThread::BackgroundAction<RefPtr<GraphicsBitmap>>::create(
- [path] {
- return load_png(path);
- },
- [this, path, callback = move(callback)](RefPtr<GraphicsBitmap> bitmap) {
- if (!bitmap) {
- callback(false);
- return;
- }
- m_wallpaper_path = path;
- m_wallpaper = move(bitmap);
- invalidate();
- callback(true);
- });
- return true;
- }
- void WSCompositor::flip_buffers()
- {
- ASSERT(m_screen_can_set_buffer);
- swap(m_front_bitmap, m_back_bitmap);
- swap(m_front_painter, m_back_painter);
- WSScreen::the().set_buffer(m_buffers_are_flipped ? 0 : 1);
- m_buffers_are_flipped = !m_buffers_are_flipped;
- }
- void WSCompositor::set_resolution(int desired_width, int desired_height)
- {
- auto screen_rect = WSScreen::the().rect();
- if (screen_rect.width() == desired_width && screen_rect.height() == desired_height)
- return;
- m_wallpaper_path = {};
- m_wallpaper = nullptr;
- // Make sure it's impossible to set an invalid resolution
- ASSERT(desired_width >= 640 && desired_height >= 480);
- WSScreen::the().set_resolution(desired_width, desired_height);
- init_bitmaps();
- compose();
- }
- Rect WSCompositor::current_cursor_rect() const
- {
- auto& wm = WSWindowManager::the();
- return { WSScreen::the().cursor_location().translated(-wm.active_cursor().hotspot()), wm.active_cursor().size() };
- }
- void WSCompositor::invalidate_cursor()
- {
- invalidate(current_cursor_rect());
- }
- void WSCompositor::draw_geometry_label()
- {
- auto& wm = WSWindowManager::the();
- auto* window_being_moved_or_resized = wm.m_drag_window ? wm.m_drag_window.ptr() : (wm.m_resize_window ? wm.m_resize_window.ptr() : nullptr);
- if (!window_being_moved_or_resized) {
- m_last_geometry_label_rect = {};
- return;
- }
- auto geometry_string = window_being_moved_or_resized->rect().to_string();
- if (!window_being_moved_or_resized->size_increment().is_null()) {
- int width_steps = (window_being_moved_or_resized->width() - window_being_moved_or_resized->base_size().width()) / window_being_moved_or_resized->size_increment().width();
- int height_steps = (window_being_moved_or_resized->height() - window_being_moved_or_resized->base_size().height()) / window_being_moved_or_resized->size_increment().height();
- geometry_string = String::format("%s (%dx%d)", geometry_string.characters(), width_steps, height_steps);
- }
- auto geometry_label_rect = Rect { 0, 0, wm.font().width(geometry_string) + 16, wm.font().glyph_height() + 10 };
- geometry_label_rect.center_within(window_being_moved_or_resized->rect());
- m_back_painter->fill_rect(geometry_label_rect, Color::WarmGray);
- m_back_painter->draw_rect(geometry_label_rect, Color::DarkGray);
- m_back_painter->draw_text(geometry_label_rect, geometry_string, TextAlignment::Center);
- m_last_geometry_label_rect = geometry_label_rect;
- }
- void WSCompositor::draw_cursor()
- {
- auto& wm = WSWindowManager::the();
- Rect cursor_rect = current_cursor_rect();
- Color inner_color = Color::White;
- Color outer_color = Color::Black;
- if (WSScreen::the().mouse_button_state() & (unsigned)MouseButton::Left)
- swap(inner_color, outer_color);
- m_back_painter->blit(cursor_rect.location(), wm.active_cursor().bitmap(), wm.active_cursor().rect());
- m_last_cursor_rect = cursor_rect;
- }
|