diff --git a/AK/Retained.h b/AK/Retained.h index 10756d6440..f583576c42 100644 --- a/AK/Retained.h +++ b/AK/Retained.h @@ -1,5 +1,6 @@ #pragma once +#include #include #ifdef __clang__ diff --git a/Servers/WindowServer/Makefile b/Servers/WindowServer/Makefile index 6cfd85e28f..2756f391a9 100644 --- a/Servers/WindowServer/Makefile +++ b/Servers/WindowServer/Makefile @@ -26,6 +26,7 @@ WINDOWSERVER_OBJS = \ WSWindowFrame.o \ WSButton.o \ WSCPUMonitor.o \ + WSCompositor.o \ main.o APP = WindowServer diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index b9a85f6902..42353a4a68 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -1,20 +1,21 @@ +#include +#include #include +#include +#include #include -#include #include +#include #include +#include #include #include -#include -#include -#include #include -#include +#include +#include #include #include #include -#include -#include HashMap* 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)); diff --git a/Servers/WindowServer/WSCompositor.cpp b/Servers/WindowServer/WSCompositor.cpp new file mode 100644 index 0000000000..713b828d8d --- /dev/null +++ b/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 +#include +#include + +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(*m_front_bitmap); + m_back_painter = make(*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 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::WM_DeferredCompose)); + m_pending_compose_event = true; + } +} + +bool WSCompositor::set_wallpaper(const String& path, Function&& callback) +{ + struct Context { + String path; + RetainPtr bitmap; + Function callback; + }; + auto context = make(); + context->path = path; + context->callback = move(callback); + + int rc = create_thread([] (void* ctx) -> int { + OwnPtr 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&& 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(*m_front_bitmap); + m_back_painter = make(*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); +} diff --git a/Servers/WindowServer/WSCompositor.h b/Servers/WindowServer/WSCompositor.h new file mode 100644 index 0000000000..eed376286e --- /dev/null +++ b/Servers/WindowServer/WSCompositor.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include + +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&& 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&&); + + 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 m_front_bitmap; + RetainPtr m_back_bitmap; + OwnPtr m_back_painter; + OwnPtr m_front_painter; + + DisjointRectSet m_dirty_rects; + + Rect m_last_cursor_rect; + Rect m_last_geometry_label_rect; + + String m_wallpaper_path; + RetainPtr m_wallpaper; +}; diff --git a/Servers/WindowServer/WSScreen.cpp b/Servers/WindowServer/WSScreen.cpp index 03ef728abb..bfb23e46fd 100644 --- a/Servers/WindowServer/WSScreen.cpp +++ b/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 #include #include #include +#include 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) diff --git a/Servers/WindowServer/WSWindowFrame.cpp b/Servers/WindowServer/WSWindowFrame.cpp index 5e8c79ab26..c4747e8b85 100644 --- a/Servers/WindowServer/WSWindowFrame.cpp +++ b/Servers/WindowServer/WSWindowFrame.cpp @@ -1,12 +1,13 @@ +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include 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; } diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index ab5a22e1fb..db9528934b 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -1,26 +1,27 @@ -#include "WSWindowManager.h" -#include "WSWindow.h" -#include "WSScreen.h" +#include "WSCompositor.h" #include "WSEventLoop.h" -#include -#include -#include -#include -#include -#include #include "WSMenu.h" #include "WSMenuBar.h" #include "WSMenuItem.h" +#include "WSScreen.h" +#include "WSWindow.h" +#include "WSWindowManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include +#include //#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(*m_front_bitmap); - m_back_painter = make(*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&& callback) -{ - struct Context { - String path; - RetainPtr bitmap; - Function callback; - }; - auto context = make(); - context->path = path; - context->callback = move(callback); - - int rc = create_thread([] (void* ctx) -> int { - OwnPtr 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&& 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(*m_front_bitmap); - m_back_painter = make(*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 -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(window.rect(), m_screen_rect)); - window.set_rect(m_screen_rect); + WSEventLoop::the().post_event(window, make(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 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(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::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()); diff --git a/Servers/WindowServer/WSWindowManager.h b/Servers/WindowServer/WSWindowManager.h index f352d83056..b80d831bba 100644 --- a/Servers/WindowServer/WSWindowManager.h +++ b/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&& 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 IterationDecision for_each_visible_window_from_back_to_front(Callback); template void for_each_window_listening_to_wm_events(Callback); template void for_each_window(Callback); - template void for_each_active_menubar_menu(Callback); + + template + 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&&); - WSScreen& m_screen; - Rect m_screen_rect; + RetainPtr m_arrow_cursor; + RetainPtr m_resize_horizontally_cursor; + RetainPtr m_resize_vertically_cursor; + RetainPtr m_resize_diagonally_tlbr_cursor; + RetainPtr m_resize_diagonally_bltr_cursor; + RetainPtr m_i_beam_cursor; + RetainPtr m_disallowed_cursor; + RetainPtr 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 m_front_bitmap; - RetainPtr m_back_bitmap; - - DisjointRectSet m_dirty_rects; - - bool m_pending_compose_event { false }; - - RetainPtr m_arrow_cursor; - RetainPtr m_resize_horizontally_cursor; - RetainPtr m_resize_vertically_cursor; - RetainPtr m_resize_diagonally_tlbr_cursor; - RetainPtr m_resize_diagonally_bltr_cursor; - RetainPtr m_i_beam_cursor; - RetainPtr m_disallowed_cursor; - RetainPtr m_move_cursor; - - OwnPtr m_back_painter; - OwnPtr m_front_painter; - - String m_wallpaper_path; - RetainPtr m_wallpaper; - - bool m_flash_flush { false }; - bool m_buffers_are_flipped { false }; - byte m_keyboard_modifiers { 0 }; OwnPtr m_system_menu; diff --git a/Servers/WindowServer/WSWindowSwitcher.cpp b/Servers/WindowServer/WSWindowSwitcher.cpp index e9a2d27084..69a84ec81a 100644 --- a/Servers/WindowServer/WSWindowSwitcher.cpp +++ b/Servers/WindowServer/WSWindowSwitcher.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -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(*this, WSWindowType::WindowSwitcher); m_switcher_window->set_rect(m_rect); diff --git a/Servers/WindowServer/main.cpp b/Servers/WindowServer/main.cpp index 7a42e2482e..b8320b1858 100644 --- a/Servers/WindowServer/main.cpp +++ b/Servers/WindowServer/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -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");