Przeglądaj źródła

WindowServer: Factor out compositing from WSWindowManager into WSCompositor.

This is far from finished and the two classes are awkwardly grabbing at each
other's innards, but here's a first step in the right direction.
Andreas Kling 6 lat temu
rodzic
commit
ad908f1395

+ 1 - 0
AK/Retained.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <AK/Assertions.h>
 #include <AK/Types.h>
 
 #ifdef __clang__

+ 1 - 0
Servers/WindowServer/Makefile

@@ -26,6 +26,7 @@ WINDOWSERVER_OBJS = \
     WSWindowFrame.o \
     WSButton.o \
     WSCPUMonitor.o \
+    WSCompositor.o \
     main.o
 
 APP = WindowServer

+ 10 - 9
Servers/WindowServer/WSClientConnection.cpp

@@ -1,20 +1,21 @@
+#include <SharedBuffer.h>
+#include <WindowServer/WSAPITypes.h>
 #include <WindowServer/WSClientConnection.h>
+#include <WindowServer/WSClipboard.h>
+#include <WindowServer/WSCompositor.h>
 #include <WindowServer/WSEventLoop.h>
-#include <WindowServer/WSMenuBar.h>
 #include <WindowServer/WSMenu.h>
+#include <WindowServer/WSMenuBar.h>
 #include <WindowServer/WSMenuItem.h>
+#include <WindowServer/WSScreen.h>
 #include <WindowServer/WSWindow.h>
 #include <WindowServer/WSWindowManager.h>
-#include <WindowServer/WSAPITypes.h>
-#include <WindowServer/WSClipboard.h>
-#include <WindowServer/WSScreen.h>
 #include <WindowServer/WSWindowSwitcher.h>
-#include <SharedBuffer.h>
+#include <errno.h>
+#include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/uio.h>
 #include <unistd.h>
-#include <errno.h>
-#include <stdio.h>
 
 HashMap<int, WSClientConnection*>* s_connections;
 
@@ -342,7 +343,7 @@ void WSClientConnection::handle_request(const WSAPISetWindowOpacityRequest& requ
 
 void WSClientConnection::handle_request(const WSAPISetWallpaperRequest& request)
 {
-    WSWindowManager::the().set_wallpaper(request.wallpaper(), [&] (bool success) {
+    WSCompositor::the().set_wallpaper(request.wallpaper(), [&] (bool success) {
         WSAPI_ServerMessage response;
         response.type = WSAPI_ServerMessage::Type::DidSetWallpaper;
         response.value = success;
@@ -352,7 +353,7 @@ void WSClientConnection::handle_request(const WSAPISetWallpaperRequest& request)
 
 void WSClientConnection::handle_request(const WSAPIGetWallpaperRequest&)
 {
-    auto path = WSWindowManager::the().wallpaper_path();
+    auto path = WSCompositor::the().wallpaper_path();
     WSAPI_ServerMessage response;
     response.type = WSAPI_ServerMessage::Type::DidGetWallpaper;
     ASSERT(path.length() < (int)sizeof(response.text));

+ 334 - 0
Servers/WindowServer/WSCompositor.cpp

@@ -0,0 +1,334 @@
+#include "WSCompositor.h"
+#include "WSEvent.h"
+#include "WSEventLoop.h"
+#include "WSScreen.h"
+#include "WSWindow.h"
+#include "WSWindowManager.h"
+#include <SharedGraphics/Font.h>
+#include <SharedGraphics/PNGLoader.h>
+#include <SharedGraphics/Painter.h>
+
+WSCompositor& WSCompositor::the()
+{
+    static WSCompositor s_the;
+    return s_the;
+}
+
+WSCompositor::WSCompositor()
+{
+    auto size = WSScreen::the().size();
+    m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, WSScreen::the().scanline(0));
+    m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, WSScreen::the().scanline(size.height()));
+
+    m_front_painter = make<Painter>(*m_front_bitmap);
+    m_back_painter = make<Painter>(*m_back_bitmap);
+
+    m_wallpaper_path = "/res/wallpapers/retro.rgb";
+    m_wallpaper = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, m_wallpaper_path, { 1024, 768 });
+}
+
+void WSCompositor::event(CEvent& event)
+{
+    if (event.type() == WSEvent::WM_DeferredCompose) {
+        m_pending_compose_event = false;
+        compose();
+        return;
+    }
+}
+
+void WSCompositor::compose()
+{
+    auto& wm = WSWindowManager::the();
+
+    auto dirty_rects = move(m_dirty_rects);
+    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;
+        if (!m_wallpaper)
+            m_back_painter->fill_rect(dirty_rect, wm.m_background_color);
+        else
+            m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);
+    }
+
+    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());
+        RetainPtr<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_menubar();
+    }
+
+    draw_cursor();
+
+    if (m_flash_flush) {
+        for (auto& rect : dirty_rects.rects())
+            m_front_painter->fill_rect(rect, Color::Yellow);
+    }
+
+    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
+
+    const 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();
+
+    for (int y = 0; y < rect.height(); ++y) {
+        fast_dword_copy(back_ptr, front_ptr, rect.width());
+        front_ptr = (const RGBA32*)((const byte*)front_ptr + pitch);
+        back_ptr = (RGBA32*)((byte*)back_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);
+
+    if (!m_pending_compose_event) {
+        WSEventLoop::the().post_event(*this, make<WSEvent>(WSEvent::WM_DeferredCompose));
+        m_pending_compose_event = true;
+    }
+}
+
+bool WSCompositor::set_wallpaper(const String& path, Function<void(bool)>&& callback)
+{
+    struct Context {
+        String path;
+        RetainPtr<GraphicsBitmap> bitmap;
+        Function<void(bool)> callback;
+    };
+    auto context = make<Context>();
+    context->path = path;
+    context->callback = move(callback);
+
+    int rc = create_thread([] (void* ctx) -> int {
+        OwnPtr<Context> context((Context*)ctx);
+        context->bitmap = load_png(context->path);
+        if (!context->bitmap) {
+            context->callback(false);
+            exit_thread(0);
+            return 0;
+        }
+        the().deferred_invoke([context = move(context)] (auto&) {
+            the().finish_setting_wallpaper(context->path, *context->bitmap);
+            context->callback(true);
+        });
+        exit_thread(0);
+        return 0;
+    }, context.leak_ptr());
+    ASSERT(rc == 0);
+
+    return true;
+}
+
+void WSCompositor::finish_setting_wallpaper(const String& path, Retained<GraphicsBitmap>&& bitmap)
+{
+    m_wallpaper_path = path;
+    m_wallpaper = move(bitmap);
+    invalidate();
+}
+
+void WSCompositor::flip_buffers()
+{
+    swap(m_front_bitmap, m_back_bitmap);
+    swap(m_front_painter, m_back_painter);
+    int new_y_offset = m_buffers_are_flipped ? 0 : WSScreen::the().height();
+    WSScreen::the().set_y_offset(new_y_offset);
+    m_buffers_are_flipped = !m_buffers_are_flipped;
+}
+
+void WSCompositor::set_resolution(int width, int height)
+{
+    auto screen_rect = WSScreen::the().rect();
+    if (screen_rect.width() == width && screen_rect.height() == height)
+        return;
+    m_wallpaper_path = { };
+    m_wallpaper = nullptr;
+    WSScreen::the().set_resolution(width, height);
+    m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, WSScreen::the().scanline(0));
+    m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, WSScreen::the().scanline(height));
+    m_front_painter = make<Painter>(*m_front_bitmap);
+    m_back_painter = make<Painter>(*m_back_bitmap);
+    m_buffers_are_flipped = false;
+    invalidate();
+    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::LightGray);
+    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;
+}
+
+void WSCompositor::draw_menubar()
+{
+    auto& wm = WSWindowManager::the();
+    auto menubar_rect = wm.menubar_rect();
+
+    m_back_painter->fill_rect(menubar_rect, Color::LightGray);
+    m_back_painter->draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, Color::MidGray);
+    int index = 0;
+    wm.for_each_active_menubar_menu([&] (WSMenu& menu) {
+        Color text_color = Color::Black;
+        if (&menu == wm.current_menu()) {
+            m_back_painter->fill_rect(menu.rect_in_menubar(), wm.menu_selection_color());
+            text_color = Color::White;
+        }
+        m_back_painter->draw_text(
+            menu.text_rect_in_menubar(),
+            menu.name(),
+            index == 1 ? wm.app_menu_font() : wm.menu_font(),
+            TextAlignment::CenterLeft,
+            text_color
+        );
+        ++index;
+        return true;
+    });
+
+    int username_width = Font::default_bold_font().width(wm.m_username);
+    Rect username_rect {
+        menubar_rect.right() - wm.menubar_menu_margin() / 2 - Font::default_bold_font().width(wm.m_username),
+        menubar_rect.y(),
+        username_width,
+        menubar_rect.height()
+    };
+    m_back_painter->draw_text(username_rect, wm.m_username, Font::default_bold_font(), TextAlignment::CenterRight, Color::Black);
+
+    time_t now = time(nullptr);
+    auto* tm = localtime(&now);
+    auto time_text = String::format("%4u-%02u-%02u %02u:%02u:%02u",
+        tm->tm_year + 1900,
+        tm->tm_mon + 1,
+        tm->tm_mday,
+        tm->tm_hour,
+        tm->tm_min,
+        tm->tm_sec);
+    int time_width = wm.font().width(time_text);
+    Rect time_rect {
+        username_rect.left() - wm.menubar_menu_margin() / 2 - time_width,
+        menubar_rect.y(),
+        time_width,
+        menubar_rect.height()
+    };
+
+    m_back_painter->draw_text(time_rect, time_text, wm.font(), TextAlignment::CenterRight, Color::Black);
+
+    Rect cpu_rect { time_rect.right() - wm.font().width(time_text) - wm.m_cpu_monitor.capacity() - 10, time_rect.y() + 1, wm.m_cpu_monitor.capacity(), time_rect.height() - 2 };
+    wm.m_cpu_monitor.paint(*m_back_painter, cpu_rect);
+}

+ 58 - 0
Servers/WindowServer/WSCompositor.h

@@ -0,0 +1,58 @@
+#pragma once
+
+#include <AK/OwnPtr.h>
+#include <AK/RetainPtr.h>
+#include <LibCore/CObject.h>
+#include <SharedGraphics/DisjointRectSet.h>
+#include <SharedGraphics/GraphicsBitmap.h>
+
+class Painter;
+class WSCursor;
+
+class WSCompositor final : public CObject {
+public:
+    static WSCompositor& the();
+
+    void compose();
+    void invalidate();
+    void invalidate(const Rect&);
+
+    void set_resolution(int width, int height);
+
+    bool set_wallpaper(const String& path, Function<void(bool)>&& callback);
+    String wallpaper_path() const { return m_wallpaper_path; }
+
+    void invalidate_cursor();
+    Rect current_cursor_rect() const;
+
+private:
+    virtual void event(CEvent&) override;
+    virtual const char* class_name() const override { return "WSCompositor"; }
+
+    WSCompositor();
+    void flip_buffers();
+    void flush(const Rect&);
+    void draw_cursor();
+    void draw_geometry_label();
+    void draw_menubar();
+    void finish_setting_wallpaper(const String& path, Retained<GraphicsBitmap>&&);
+
+    unsigned m_compose_count { 0 };
+    unsigned m_flush_count { 0 };
+    bool m_pending_compose_event { false };
+    bool m_flash_flush { false };
+    bool m_buffers_are_flipped { false };
+
+    RetainPtr<GraphicsBitmap> m_front_bitmap;
+    RetainPtr<GraphicsBitmap> m_back_bitmap;
+    OwnPtr<Painter> m_back_painter;
+    OwnPtr<Painter> m_front_painter;
+
+    DisjointRectSet m_dirty_rects;
+
+    Rect m_last_cursor_rect;
+    Rect m_last_geometry_label_rect;
+
+    String m_wallpaper_path;
+    RetainPtr<GraphicsBitmap> m_wallpaper;
+};

+ 5 - 4
Servers/WindowServer/WSScreen.cpp

@@ -1,11 +1,12 @@
-#include "WSScreen.h"
-#include "WSEventLoop.h"
+#include "WSCompositor.h"
 #include "WSEvent.h"
+#include "WSEventLoop.h"
+#include "WSScreen.h"
 #include "WSWindowManager.h"
-#include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
+#include <unistd.h>
 
 static WSScreen* s_the;
 
@@ -86,7 +87,7 @@ void WSScreen::on_receive_mouse_data(int dx, int dy, int dz, unsigned buttons)
     }
 
     if (m_cursor_location != prev_location)
-        WSWindowManager::the().invalidate_cursor();
+        WSCompositor::the().invalidate_cursor();
 }
 
 void WSScreen::on_receive_keyboard_data(KeyEvent kernel_event)

+ 8 - 7
Servers/WindowServer/WSWindowFrame.cpp

@@ -1,12 +1,13 @@
-#include <WindowServer/WSWindowFrame.h>
-#include <WindowServer/WSWindowManager.h>
-#include <WindowServer/WSWindow.h>
-#include <WindowServer/WSEvent.h>
-#include <WindowServer/WSButton.h>
 #include <SharedGraphics/CharacterBitmap.h>
-#include <SharedGraphics/Painter.h>
 #include <SharedGraphics/Font.h>
+#include <SharedGraphics/Painter.h>
 #include <SharedGraphics/StylePainter.h>
+#include <WindowServer/WSButton.h>
+#include <WindowServer/WSCompositor.h>
+#include <WindowServer/WSEvent.h>
+#include <WindowServer/WSWindow.h>
+#include <WindowServer/WSWindowFrame.h>
+#include <WindowServer/WSWindowManager.h>
 
 static const int window_titlebar_height = 19;
 
@@ -294,7 +295,7 @@ void WSWindowFrame::on_mouse_event(const WSMouseEvent& event)
         int hot_area_row = min(2, window_relative_y / (outer_rect.height() / 3));
         int hot_area_column = min(2, window_relative_x / (outer_rect.width() / 3));
         wm.set_resize_candidate(m_window, direction_for_hot_area[hot_area_row][hot_area_column]);
-        wm.invalidate_cursor();
+        WSCompositor::the().invalidate_cursor();
         return;
     }
 

+ 36 - 351
Servers/WindowServer/WSWindowManager.cpp

@@ -1,26 +1,27 @@
-#include "WSWindowManager.h"
-#include "WSWindow.h"
-#include "WSScreen.h"
+#include "WSCompositor.h"
 #include "WSEventLoop.h"
-#include <SharedGraphics/Font.h>
-#include <SharedGraphics/Painter.h>
-#include <SharedGraphics/CharacterBitmap.h>
-#include <AK/StdLibExtras.h>
-#include <AK/Vector.h>
-#include <errno.h>
 #include "WSMenu.h"
 #include "WSMenuBar.h"
 #include "WSMenuItem.h"
+#include "WSScreen.h"
+#include "WSWindow.h"
+#include "WSWindowManager.h"
+#include <AK/StdLibExtras.h>
+#include <AK/Vector.h>
+#include <LibCore/CTimer.h>
+#include <SharedGraphics/CharacterBitmap.h>
+#include <SharedGraphics/Font.h>
+#include <SharedGraphics/PNGLoader.h>
+#include <SharedGraphics/Painter.h>
+#include <SharedGraphics/StylePainter.h>
+#include <WindowServer/WSAPITypes.h>
+#include <WindowServer/WSButton.h>
 #include <WindowServer/WSClientConnection.h>
-#include <unistd.h>
+#include <WindowServer/WSCursor.h>
+#include <errno.h>
 #include <stdio.h>
 #include <time.h>
-#include <SharedGraphics/StylePainter.h>
-#include <SharedGraphics/PNGLoader.h>
-#include <WindowServer/WSCursor.h>
-#include <WindowServer/WSButton.h>
-#include <LibCore/CTimer.h>
-#include <WindowServer/WSAPITypes.h>
+#include <unistd.h>
 
 //#define DEBUG_COUNTERS
 //#define RESIZE_DEBUG
@@ -33,35 +34,18 @@ WSWindowManager& WSWindowManager::the()
     return *s_the;
 }
 
-void WSWindowManager::flip_buffers()
-{
-    swap(m_front_bitmap, m_back_bitmap);
-    swap(m_front_painter, m_back_painter);
-    int new_y_offset = m_buffers_are_flipped ? 0 : m_screen_rect.height();
-    WSScreen::the().set_y_offset(new_y_offset);
-    m_buffers_are_flipped = !m_buffers_are_flipped;
-}
-
 WSWindowManager::WSWindowManager()
-    : m_screen(WSScreen::the())
-    , m_screen_rect(m_screen.rect())
-    , m_flash_flush(false)
 {
     s_the = this;
 
-#ifndef DEBUG_COUNTERS
-    (void)m_compose_count;
-    (void)m_flush_count;
-#endif
-    auto size = m_screen_rect.size();
-    m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, m_screen.scanline(0));
-    m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, m_screen.scanline(size.height()));
-
-    m_front_painter = make<Painter>(*m_front_bitmap);
-    m_back_painter = make<Painter>(*m_back_bitmap);
-
-    m_front_painter->set_font(font());
-    m_back_painter->set_font(font());
+    m_arrow_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/arrow.png"), { 2, 2 });
+    m_resize_horizontally_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-horizontal.png"));
+    m_resize_vertically_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-vertical.png"));
+    m_resize_diagonally_tlbr_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-diagonal-tlbr.png"));
+    m_resize_diagonally_bltr_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-diagonal-bltr.png"));
+    m_i_beam_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/i-beam.png"));
+    m_disallowed_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/disallowed.png"));
+    m_move_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/move.png"));
 
     m_background_color = Color(50, 50, 50);
     m_active_window_border_color = Color(110, 34, 9);
@@ -77,18 +61,6 @@ WSWindowManager::WSWindowManager()
     m_highlight_window_border_color2 = Color::from_rgb(0xfabbbb);
     m_highlight_window_title_color = Color::White;
 
-    m_arrow_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/arrow.png"), { 2, 2 });
-    m_resize_horizontally_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-horizontal.png"));
-    m_resize_vertically_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-vertical.png"));
-    m_resize_diagonally_tlbr_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-diagonal-tlbr.png"));
-    m_resize_diagonally_bltr_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/resize-diagonal-bltr.png"));
-    m_i_beam_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/i-beam.png"));
-    m_disallowed_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/disallowed.png"));
-    m_move_cursor = WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/move.png"));
-
-    m_wallpaper_path = "/res/wallpapers/retro.rgb";
-    m_wallpaper = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, m_wallpaper_path, { 1024, 768 });
-
     m_username = getlogin();
 
     m_menu_selection_color = Color::from_rgb(0x84351a);
@@ -173,7 +145,7 @@ WSWindowManager::WSWindowManager()
     });
 
     invalidate();
-    compose();
+    WSCompositor::the().compose();
 }
 
 WSWindowManager::~WSWindowManager()
@@ -205,72 +177,14 @@ void WSWindowManager::tick_clock()
     invalidate(menubar_rect());
 }
 
-bool WSWindowManager::set_wallpaper(const String& path, Function<void(bool)>&& callback)
-{
-    struct Context {
-        String path;
-        RetainPtr<GraphicsBitmap> bitmap;
-        Function<void(bool)> callback;
-    };
-    auto context = make<Context>();
-    context->path = path;
-    context->callback = move(callback);
-
-    int rc = create_thread([] (void* ctx) -> int {
-        OwnPtr<Context> context((Context*)ctx);
-        context->bitmap = load_png(context->path);
-        if (!context->bitmap) {
-            context->callback(false);
-            exit_thread(0);
-            return 0;
-        }
-        the().deferred_invoke([context = move(context)] (auto&) {
-            the().finish_setting_wallpaper(context->path, *context->bitmap);
-            context->callback(true);
-        });
-        exit_thread(0);
-        return 0;
-    }, context.leak_ptr());
-    ASSERT(rc == 0);
-
-    return true;
-}
-
-void WSWindowManager::finish_setting_wallpaper(const String& path, Retained<GraphicsBitmap>&& bitmap)
-{
-    m_wallpaper_path = path;
-    m_wallpaper = move(bitmap);
-    invalidate();
-}
-
 void WSWindowManager::set_resolution(int width, int height)
 {
-    if (m_screen_rect.width() == width && m_screen_rect.height() == height)
-        return;
-    m_wallpaper_path = { };
-    m_wallpaper = nullptr;
-    m_screen.set_resolution(width, height);
-    m_screen_rect = m_screen.rect();
-    m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, m_screen.scanline(0));
-    m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, m_screen.scanline(height));
-    m_front_painter = make<Painter>(*m_front_bitmap);
-    m_back_painter = make<Painter>(*m_back_bitmap);
-    m_buffers_are_flipped = false;
-    invalidate();
-    compose();
-
+    WSCompositor::the().set_resolution(width, height);
     WSClientConnection::for_each_client([&] (WSClientConnection& client) {
-        client.notify_about_new_screen_rect(m_screen_rect);
+        client.notify_about_new_screen_rect(WSScreen::the().rect());
     });
 }
 
-template<typename Callback>
-void WSWindowManager::for_each_active_menubar_menu(Callback callback)
-{
-    callback(*m_system_menu);
-    if (m_current_menubar)
-        m_current_menubar->for_each_menu(callback);
-}
 
 int WSWindowManager::menubar_menu_margin() const
 {
@@ -315,8 +229,8 @@ void WSWindowManager::add_window(WSWindow& window)
     m_windows_in_order.append(&window);
 
     if (window.is_fullscreen()) {
-        WSEventLoop::the().post_event(window, make<WSResizeEvent>(window.rect(), m_screen_rect));
-        window.set_rect(m_screen_rect);
+        WSEventLoop::the().post_event(window, make<WSResizeEvent>(window.rect(), WSScreen::the().rect()));
+        window.set_rect(WSScreen::the().rect());
     }
 
     set_active_window(&window);
@@ -859,7 +773,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& hovere
 void WSWindowManager::clear_resize_candidate()
 {
     if (m_resize_candidate)
-        invalidate_cursor();
+        WSCompositor::the().invalidate_cursor();
     m_resize_candidate = nullptr;
 }
 
@@ -915,167 +829,11 @@ bool WSWindowManager::any_opaque_window_above_this_one_contains_rect(const WSWin
     return found_containing_window;
 };
 
-void WSWindowManager::compose()
-{
-    auto dirty_rects = move(m_dirty_rects);
-    dirty_rects.add(Rect::intersection(m_last_geometry_label_rect, m_screen_rect));
-    dirty_rects.add(Rect::intersection(m_last_cursor_rect, m_screen_rect));
-    dirty_rects.add(Rect::intersection(current_cursor_rect(), m_screen_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 (any_opaque_window_contains_rect(dirty_rect))
-            continue;
-        if (!m_wallpaper)
-            m_back_painter->fill_rect(dirty_rect, m_background_color);
-        else
-            m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);
-    }
-
-    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());
-        RetainPtr<GraphicsBitmap> backing_store = window.backing_store();
-        for (auto& dirty_rect : dirty_rects.rects()) {
-            if (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 = active_fullscreen_window()) {
-        compose_window(*fullscreen_window);
-    } else {
-        for_each_visible_window_from_back_to_front([&] (WSWindow& window) {
-            return compose_window(window);
-        });
-
-        draw_geometry_label();
-        draw_menubar();
-    }
-
-    draw_cursor();
-
-    if (m_flash_flush) {
-        for (auto& rect : dirty_rects.rects())
-            m_front_painter->fill_rect(rect, Color::Yellow);
-    }
-
-    flip_buffers();
-    for (auto& r : dirty_rects.rects())
-        flush(r);
-}
-
-Rect WSWindowManager::current_cursor_rect() const
-{
-    return { m_screen.cursor_location().translated(-active_cursor().hotspot()), active_cursor().size() };
-}
-
-void WSWindowManager::invalidate_cursor()
-{
-    invalidate(current_cursor_rect());
-}
-
 Rect WSWindowManager::menubar_rect() const
 {
     if (active_fullscreen_window())
         return { };
-    return { 0, 0, m_screen_rect.width(), 18 };
-}
-
-void WSWindowManager::draw_menubar()
-{
-    auto menubar_rect = this->menubar_rect();
-
-    m_back_painter->fill_rect(menubar_rect, Color::LightGray);
-    m_back_painter->draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, Color::MidGray);
-    int index = 0;
-    for_each_active_menubar_menu([&] (WSMenu& menu) {
-        Color text_color = Color::Black;
-        if (&menu == current_menu()) {
-            m_back_painter->fill_rect(menu.rect_in_menubar(), menu_selection_color());
-            text_color = Color::White;
-        }
-        m_back_painter->draw_text(
-            menu.text_rect_in_menubar(),
-            menu.name(),
-            index == 1 ? app_menu_font() : menu_font(),
-            TextAlignment::CenterLeft,
-            text_color
-        );
-        ++index;
-        return true;
-    });
-
-    int username_width = Font::default_bold_font().width(m_username);
-    Rect username_rect {
-        menubar_rect.right() - menubar_menu_margin() / 2 - Font::default_bold_font().width(m_username),
-        menubar_rect.y(),
-        username_width,
-        menubar_rect.height()
-    };
-    m_back_painter->draw_text(username_rect, m_username, Font::default_bold_font(), TextAlignment::CenterRight, Color::Black);
-
-    time_t now = time(nullptr);
-    auto* tm = localtime(&now);
-    auto time_text = String::format("%4u-%02u-%02u %02u:%02u:%02u",
-        tm->tm_year + 1900,
-        tm->tm_mon + 1,
-        tm->tm_mday,
-        tm->tm_hour,
-        tm->tm_min,
-        tm->tm_sec);
-    int time_width = font().width(time_text);
-    Rect time_rect {
-        username_rect.left() - menubar_menu_margin() / 2 - time_width,
-        menubar_rect.y(),
-        time_width,
-        menubar_rect.height()
-    };
-
-    m_back_painter->draw_text(time_rect, time_text, font(), TextAlignment::CenterRight, Color::Black);
-
-    Rect cpu_rect { time_rect.right() - font().width(time_text) - m_cpu_monitor.capacity() - 10, time_rect.y() + 1, m_cpu_monitor.capacity(), time_rect.height() - 2 };
-    m_cpu_monitor.paint(*m_back_painter, cpu_rect);
+    return { 0, 0, WSScreen::the().rect().width(), 18 };
 }
 
 void WSWindowManager::draw_window_switcher()
@@ -1084,38 +842,6 @@ void WSWindowManager::draw_window_switcher()
         m_switcher.draw();
 }
 
-void WSWindowManager::draw_geometry_label()
-{
-    auto* window_being_moved_or_resized = m_drag_window ? m_drag_window.ptr() : (m_resize_window ? 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, font().width(geometry_string) + 16, font().glyph_height() + 10 };
-    geometry_label_rect.center_within(window_being_moved_or_resized->rect());
-    m_back_painter->fill_rect(geometry_label_rect, Color::LightGray);
-    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 WSWindowManager::draw_cursor()
-{
-    Rect cursor_rect = current_cursor_rect();
-    Color inner_color = Color::White;
-    Color outer_color = Color::Black;
-    if (m_screen.mouse_button_state() & (unsigned)MouseButton::Left)
-        swap(inner_color, outer_color);
-    m_back_painter->blit(cursor_rect.location(), active_cursor().bitmap(), active_cursor().rect());
-    m_last_cursor_rect = cursor_rect;
-}
-
 void WSWindowManager::event(CEvent& event)
 {
     if (static_cast<WSEvent&>(event).is_mouse_event()) {
@@ -1140,12 +866,6 @@ void WSWindowManager::event(CEvent& event)
         return;
     }
 
-    if (event.type() == WSEvent::WM_DeferredCompose) {
-        m_pending_compose_event = false;
-        compose();
-        return;
-    }
-
     CObject::event(event);
 }
 
@@ -1206,28 +926,12 @@ void WSWindowManager::set_hovered_window(WSWindow* window)
 
 void WSWindowManager::invalidate()
 {
-    m_dirty_rects.clear_with_capacity();
-    invalidate(m_screen_rect);
+    WSCompositor::the().invalidate();
 }
 
-void WSWindowManager::recompose_immediately()
+void WSWindowManager::invalidate(const Rect& rect)
 {
-    m_dirty_rects.clear_with_capacity();
-    invalidate(m_screen_rect, false);
-}
-
-void WSWindowManager::invalidate(const Rect& a_rect, bool should_schedule_compose_event)
-{
-    auto rect = Rect::intersection(a_rect, m_screen_rect);
-    if (rect.is_empty())
-        return;
-
-    m_dirty_rects.add(rect);
-
-    if (should_schedule_compose_event && !m_pending_compose_event) {
-        WSEventLoop::the().post_event(*this, make<WSEvent>(WSEvent::WM_DeferredCompose));
-        m_pending_compose_event = true;
-    }
+    WSCompositor::the().invalidate(rect);
 }
 
 void WSWindowManager::invalidate(const WSWindow& window)
@@ -1249,25 +953,6 @@ void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
     invalidate(inner_rect);
 }
 
-void WSWindowManager::flush(const Rect& a_rect)
-{
-    auto rect = Rect::intersection(a_rect, m_screen_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
-
-    const 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();
-
-    for (int y = 0; y < rect.height(); ++y) {
-        fast_dword_copy(back_ptr, front_ptr, rect.width());
-        front_ptr = (const RGBA32*)((const byte*)front_ptr + pitch);
-        back_ptr = (RGBA32*)((byte*)back_ptr + pitch);
-    }
-}
-
 void WSWindowManager::close_menu(WSMenu& menu)
 {
     if (current_menu() == &menu)
@@ -1337,7 +1022,7 @@ void WSWindowManager::set_resize_candidate(WSWindow& window, ResizeDirection dir
 
 Rect WSWindowManager::maximized_window_rect(const WSWindow& window) const
 {
-    Rect rect = m_screen_rect;
+    Rect rect = WSScreen::the().rect();
 
     // Subtract window title bar (leaving the border)
     rect.set_y(rect.y() + window.frame().title_bar_rect().height());

+ 29 - 53
Servers/WindowServer/WSWindowManager.h

@@ -31,6 +31,7 @@ class WSButton;
 enum class ResizeDirection { None, Left, UpLeft, Up, UpRight, Right, DownRight, Down, DownLeft };
 
 class WSWindowManager : public CObject {
+    friend class WSCompositor;
     friend class WSWindowFrame;
     friend class WSWindowSwitcher;
 public:
@@ -57,7 +58,6 @@ public:
 
     void move_to_front_and_make_active(WSWindow&);
 
-    void invalidate_cursor();
     void draw_cursor();
     void draw_menubar();
     void draw_window_switcher();
@@ -69,11 +69,20 @@ public:
     WSMenu* current_menu() { return m_current_menu.ptr(); }
     void set_current_menu(WSMenu*);
 
+    const WSCursor& active_cursor() const;
+    const WSCursor& arrow_cursor() const { return *m_arrow_cursor; }
+    const WSCursor& resize_horizontally_cursor() const { return *m_resize_horizontally_cursor; }
+    const WSCursor& resize_vertically_cursor() const { return *m_resize_vertically_cursor; }
+    const WSCursor& resize_diagonally_tlbr_cursor() const { return *m_resize_diagonally_tlbr_cursor; }
+    const WSCursor& resize_diagonally_bltr_cursor() const { return *m_resize_diagonally_bltr_cursor; }
+    const WSCursor& i_beam_cursor() const { return *m_i_beam_cursor; }
+    const WSCursor& disallowed_cursor() const { return *m_disallowed_cursor; }
+    const WSCursor& move_cursor() const { return *m_move_cursor; }
+
     void invalidate(const WSWindow&);
     void invalidate(const WSWindow&, const Rect&);
-    void invalidate(const Rect&, bool should_schedule_compose_event = true);
+    void invalidate(const Rect&);
     void invalidate();
-    void recompose_immediately();
     void flush(const Rect&);
 
     const Font& font() const;
@@ -88,21 +97,6 @@ public:
 
     void set_resolution(int width, int height);
 
-    bool set_wallpaper(const String& path, Function<void(bool)>&& callback);
-    String wallpaper_path() const { return m_wallpaper_path; }
-
-    const WSCursor& active_cursor() const;
-    Rect current_cursor_rect() const;
-
-    const WSCursor& arrow_cursor() const { return *m_arrow_cursor; }
-    const WSCursor& resize_horizontally_cursor() const { return *m_resize_horizontally_cursor; }
-    const WSCursor& resize_vertically_cursor() const { return *m_resize_vertically_cursor; }
-    const WSCursor& resize_diagonally_tlbr_cursor() const { return *m_resize_diagonally_tlbr_cursor; }
-    const WSCursor& resize_diagonally_bltr_cursor() const { return *m_resize_diagonally_bltr_cursor; }
-    const WSCursor& i_beam_cursor() const { return *m_i_beam_cursor; }
-    const WSCursor& disallowed_cursor() const { return *m_disallowed_cursor; }
-    const WSCursor& move_cursor() const { return *m_move_cursor; }
-
     void set_active_window(WSWindow*);
     void set_hovered_button(WSButton*);
 
@@ -143,7 +137,15 @@ private:
     template<typename Callback> IterationDecision for_each_visible_window_from_back_to_front(Callback);
     template<typename Callback> void for_each_window_listening_to_wm_events(Callback);
     template<typename Callback> void for_each_window(Callback);
-    template<typename Callback> void for_each_active_menubar_menu(Callback);
+
+    template<typename Callback>
+    void for_each_active_menubar_menu(Callback callback)
+    {
+        callback(*m_system_menu);
+        if (m_current_menubar)
+            m_current_menubar->for_each_menu(callback);
+    }
+
     void close_current_menu();
     virtual void event(CEvent&) override;
     void compose();
@@ -154,10 +156,15 @@ private:
     void tell_wm_listener_about_window_icon(WSWindow& listener, WSWindow&);
     void tell_wm_listener_about_window_rect(WSWindow& listener, WSWindow&);
     void pick_new_active_window();
-    void finish_setting_wallpaper(const String& path, Retained<GraphicsBitmap>&&);
 
-    WSScreen& m_screen;
-    Rect m_screen_rect;
+    RetainPtr<WSCursor> m_arrow_cursor;
+    RetainPtr<WSCursor> m_resize_horizontally_cursor;
+    RetainPtr<WSCursor> m_resize_vertically_cursor;
+    RetainPtr<WSCursor> m_resize_diagonally_tlbr_cursor;
+    RetainPtr<WSCursor> m_resize_diagonally_bltr_cursor;
+    RetainPtr<WSCursor> m_i_beam_cursor;
+    RetainPtr<WSCursor> m_disallowed_cursor;
+    RetainPtr<WSCursor> m_move_cursor;
 
     Color m_background_color;
     Color m_active_window_border_color;
@@ -208,37 +215,6 @@ private:
     Point m_resize_origin;
     ResizeDirection m_resize_direction { ResizeDirection::None };
 
-    Rect m_last_cursor_rect;
-    Rect m_last_geometry_label_rect;
-
-    unsigned m_compose_count { 0 };
-    unsigned m_flush_count { 0 };
-
-    RetainPtr<GraphicsBitmap> m_front_bitmap;
-    RetainPtr<GraphicsBitmap> m_back_bitmap;
-
-    DisjointRectSet m_dirty_rects;
-
-    bool m_pending_compose_event { false };
-
-    RetainPtr<WSCursor> m_arrow_cursor;
-    RetainPtr<WSCursor> m_resize_horizontally_cursor;
-    RetainPtr<WSCursor> m_resize_vertically_cursor;
-    RetainPtr<WSCursor> m_resize_diagonally_tlbr_cursor;
-    RetainPtr<WSCursor> m_resize_diagonally_bltr_cursor;
-    RetainPtr<WSCursor> m_i_beam_cursor;
-    RetainPtr<WSCursor> m_disallowed_cursor;
-    RetainPtr<WSCursor> m_move_cursor;
-
-    OwnPtr<Painter> m_back_painter;
-    OwnPtr<Painter> m_front_painter;
-
-    String m_wallpaper_path;
-    RetainPtr<GraphicsBitmap> m_wallpaper;
-
-    bool m_flash_flush { false };
-    bool m_buffers_are_flipped { false };
-
     byte m_keyboard_modifiers { 0 };
 
     OwnPtr<WSMenu> m_system_menu;

+ 2 - 1
Servers/WindowServer/WSWindowSwitcher.cpp

@@ -1,6 +1,7 @@
 #include <WindowServer/WSWindowSwitcher.h>
 #include <WindowServer/WSWindowManager.h>
 #include <WindowServer/WSEvent.h>
+#include <WindowServer/WSScreen.h>
 #include <SharedGraphics/Font.h>
 #include <SharedGraphics/StylePainter.h>
 
@@ -130,7 +131,7 @@ void WSWindowSwitcher::refresh()
     int space_for_window_rect = 180;
     m_rect.set_width(thumbnail_width() + longest_title_width + space_for_window_rect + padding() * 2 + item_padding() * 2);
     m_rect.set_height(window_count * item_height() + padding() * 2);
-    m_rect.center_within(wm.m_screen_rect);
+    m_rect.center_within(WSScreen::the().rect());
     if (!m_switcher_window)
         m_switcher_window = make<WSWindow>(*this, WSWindowType::WindowSwitcher);
     m_switcher_window->set_rect(m_rect);

+ 2 - 0
Servers/WindowServer/main.cpp

@@ -1,6 +1,7 @@
 #include <WindowServer/WSScreen.h>
 #include <WindowServer/WSWindowManager.h>
 #include <WindowServer/WSEventLoop.h>
+#include <WindowServer/WSCompositor.h>
 #include <signal.h>
 #include <stdio.h>
 
@@ -18,6 +19,7 @@ int main(int, char**)
 
     WSEventLoop loop;
     WSScreen screen(1024, 768);
+    WSCompositor::the();
     WSWindowManager window_manager;
 
     dbgprintf("Entering WindowServer main loop.\n");