mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 11:12:45 +00:00 
			
		
		
		
	WindowServer: Let the window switcher render itself as a WSWindow.
Much better than drawing directly into the back buffer.
This commit is contained in:
		
							parent
							
								
									f3ff402cf3
								
							
						
					
					
						commit
						b85fe0bd07
					
				
					 8 changed files with 89 additions and 59 deletions
				
			
		|  | @ -69,7 +69,7 @@ WSWindow& WSMenu::ensure_menu_window() | |||
|             next_item_location.move_by(0, height); | ||||
|         } | ||||
| 
 | ||||
|         auto window = make<WSWindow>(*this); | ||||
|         auto window = make<WSWindow>(*this, WSWindowType::Menu); | ||||
|         window->set_opacity(0.95f); | ||||
|         window->set_rect(0, 0, width(), height()); | ||||
|         m_menu_window = move(window); | ||||
|  | @ -107,7 +107,7 @@ void WSMenu::draw() | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void WSMenu::on_window_message(WSMessage& message) | ||||
| void WSMenu::on_message(WSMessage& message) | ||||
| { | ||||
|     ASSERT(menu_window()); | ||||
|     if (message.type() == WSMessage::MouseMove) { | ||||
|  |  | |||
|  | @ -4,7 +4,8 @@ | |||
| #include <AK/Vector.h> | ||||
| #include <AK/WeakPtr.h> | ||||
| #include <SharedGraphics/Rect.h> | ||||
| #include "WSMenuItem.h" | ||||
| #include <WindowServer/WSMenuItem.h> | ||||
| #include <WindowServer/WSMessageReceiver.h> | ||||
| 
 | ||||
| class WSClientConnection; | ||||
| class WSMenuBar; | ||||
|  | @ -12,10 +13,10 @@ class WSMessage; | |||
| class WSWindow; | ||||
| class Font; | ||||
| 
 | ||||
| class WSMenu : public Weakable<WSMenu> { | ||||
| class WSMenu final : public WSMessageReceiver { | ||||
| public: | ||||
|     WSMenu(WSClientConnection*, int menu_id, String&& name); | ||||
|     ~WSMenu(); | ||||
|     virtual ~WSMenu() override; | ||||
| 
 | ||||
|     WSClientConnection* client() { return m_client; } | ||||
|     const WSClientConnection* client() const { return m_client; } | ||||
|  | @ -58,7 +59,6 @@ public: | |||
|     int left_padding() const { return 14; } | ||||
|     int right_padding() const { return 14; } | ||||
| 
 | ||||
|     void on_window_message(WSMessage&); | ||||
|     void draw(); | ||||
|     const Font& font() const; | ||||
| 
 | ||||
|  | @ -73,6 +73,8 @@ public: | |||
|     void close(); | ||||
| 
 | ||||
| private: | ||||
|     virtual void on_message(WSMessage&) override; | ||||
| 
 | ||||
|     int padding_between_text_and_shortcut() const { return 50; } | ||||
|     void did_activate(WSMenuItem&); | ||||
|     WSClientConnection* m_client { nullptr }; | ||||
|  |  | |||
|  | @ -5,9 +5,9 @@ | |||
| #include <WindowServer/WSAPITypes.h> | ||||
| #include <WindowServer/WSClientConnection.h> | ||||
| 
 | ||||
| WSWindow::WSWindow(WSMenu& menu) | ||||
|     : m_type(WSWindowType::Menu) | ||||
|     , m_menu(&menu) | ||||
| WSWindow::WSWindow(WSMessageReceiver& internal_owner, WSWindowType type) | ||||
|     : m_internal_owner(&internal_owner) | ||||
|     , m_type(type) | ||||
| { | ||||
|     WSWindowManager::the().add_window(*this); | ||||
| } | ||||
|  | @ -36,12 +36,11 @@ void WSWindow::set_title(String&& title) | |||
| void WSWindow::set_rect(const Rect& rect) | ||||
| { | ||||
|     Rect old_rect; | ||||
|     ASSERT(m_client || m_menu); | ||||
|     if (m_rect == rect) | ||||
|         return; | ||||
|     old_rect = m_rect; | ||||
|     m_rect = rect; | ||||
|     if (m_menu && (!m_backing_store || old_rect.size() != rect.size())) { | ||||
|     if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) { | ||||
|         m_backing_store = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, m_rect.size()); | ||||
|     } | ||||
|     WSWindowManager::the().notify_rect_changed(*this, old_rect, rect); | ||||
|  | @ -61,10 +60,8 @@ static WSAPI_MouseButton to_api(MouseButton button) | |||
| 
 | ||||
| void WSWindow::on_message(WSMessage& message) | ||||
| { | ||||
|     if (m_menu) { | ||||
|         m_menu->on_window_message(message); | ||||
|         return; | ||||
|     } | ||||
|     if (m_internal_owner) | ||||
|         return m_internal_owner->on_message(message); | ||||
| 
 | ||||
|     WSAPI_ServerMessage server_message; | ||||
|     server_message.window_id = window_id(); | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ class WSMenu; | |||
| class WSWindow final : public WSMessageReceiver, public InlineLinkedListNode<WSWindow> { | ||||
| public: | ||||
|     WSWindow(WSClientConnection&, int window_id); | ||||
|     explicit WSWindow(WSMenu&); | ||||
|     WSWindow(WSMessageReceiver&, WSWindowType); | ||||
|     virtual ~WSWindow() override; | ||||
| 
 | ||||
|     WSClientConnection* client() { return m_client; } | ||||
|  | @ -85,6 +85,7 @@ public: | |||
| 
 | ||||
| private: | ||||
|     WSClientConnection* m_client { nullptr }; | ||||
|     WSMessageReceiver* m_internal_owner { nullptr }; | ||||
|     String m_title; | ||||
|     Rect m_rect; | ||||
|     WSWindowType m_type { WSWindowType::Normal }; | ||||
|  | @ -92,7 +93,6 @@ private: | |||
|     bool m_visible { true }; | ||||
|     bool m_has_alpha_channel { false }; | ||||
|     bool m_has_painted_since_last_resize { false }; | ||||
|     WSMenu* m_menu { nullptr }; | ||||
|     RetainPtr<GraphicsBitmap> m_backing_store; | ||||
|     int m_window_id { -1 }; | ||||
|     float m_opacity { 1 }; | ||||
|  |  | |||
|  | @ -82,6 +82,16 @@ static inline Rect outer_window_rect(const Rect& window) | |||
|     return rect; | ||||
| } | ||||
| 
 | ||||
| static inline Rect outer_window_rect(const WSWindow& window) | ||||
| { | ||||
|     if (window.type() == WSWindowType::Menu) | ||||
|         return menu_window_rect(window.rect()); | ||||
|     if (window.type() == WSWindowType::WindowSwitcher) | ||||
|         return window.rect(); | ||||
|     ASSERT(window.type() == WSWindowType::Normal); | ||||
|     return outer_window_rect(window.rect()); | ||||
| } | ||||
| 
 | ||||
| static WSWindowManager* s_the; | ||||
| 
 | ||||
| WSWindowManager& WSWindowManager::the() | ||||
|  | @ -384,9 +394,12 @@ void WSWindowManager::paint_window_frame(WSWindow& window) | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (window.type() == WSWindowType::WindowSwitcher) | ||||
|         return; | ||||
| 
 | ||||
|     auto titlebar_rect = title_bar_rect(window.rect()); | ||||
|     auto titlebar_inner_rect = title_bar_text_rect(window.rect()); | ||||
|     auto outer_rect = outer_window_rect(window.rect()); | ||||
|     auto outer_rect = outer_window_rect(window); | ||||
|     auto border_rect = border_window_rect(window.rect()); | ||||
|     auto close_button_rect = close_button_rect_for_window(window.rect()); | ||||
| 
 | ||||
|  | @ -466,8 +479,8 @@ void WSWindowManager::add_window(WSWindow& window) | |||
|     m_windows_in_order.append(&window); | ||||
|     if (!active_window()) | ||||
|         set_active_window(&window); | ||||
|     if (m_switcher.is_visible()) | ||||
|         m_switcher.invalidate(); | ||||
|     if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) | ||||
|         m_switcher.refresh(); | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::move_to_front(WSWindow& window) | ||||
|  | @ -488,25 +501,25 @@ void WSWindowManager::remove_window(WSWindow& window) | |||
|     m_windows_in_order.remove(&window); | ||||
|     if (!active_window() && !m_windows.is_empty()) | ||||
|         set_active_window(*m_windows.begin()); | ||||
|     if (m_switcher.is_visible()) | ||||
|         m_switcher.invalidate(); | ||||
|     if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) | ||||
|         m_switcher.refresh(); | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::notify_title_changed(WSWindow& window) | ||||
| { | ||||
|     printf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters()); | ||||
|     invalidate(outer_window_rect(window.rect())); | ||||
|     dbgprintf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters()); | ||||
|     invalidate(outer_window_rect(window)); | ||||
|     if (m_switcher.is_visible()) | ||||
|         m_switcher.invalidate(); | ||||
|         m_switcher.refresh(); | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect) | ||||
| { | ||||
|     printf("[WM] WSWindow %p rect changed (%d,%d %dx%d) -> (%d,%d %dx%d)\n", &window, old_rect.x(), old_rect.y(), old_rect.width(), old_rect.height(), new_rect.x(), new_rect.y(), new_rect.width(), new_rect.height()); | ||||
|     dbgprintf("[WM] WSWindow %p rect changed (%d,%d %dx%d) -> (%d,%d %dx%d)\n", &window, old_rect.x(), old_rect.y(), old_rect.width(), old_rect.height(), new_rect.x(), new_rect.y(), new_rect.width(), new_rect.height()); | ||||
|     invalidate(outer_window_rect(old_rect)); | ||||
|     invalidate(outer_window_rect(new_rect)); | ||||
|     if (m_switcher.is_visible()) | ||||
|         m_switcher.invalidate(); | ||||
|     if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) | ||||
|         m_switcher.refresh(); | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, WSMouseEvent& event) | ||||
|  | @ -578,7 +591,7 @@ void WSWindowManager::start_window_resize(WSWindow& window, WSMouseEvent& event) | |||
|         { ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right }, | ||||
|         { ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight }, | ||||
|     }; | ||||
|     Rect outer_rect = outer_window_rect(window.rect()); | ||||
|     Rect outer_rect = outer_window_rect(window); | ||||
|     ASSERT(outer_rect.contains(event.position())); | ||||
|     int window_relative_x = event.x() - outer_rect.x(); | ||||
|     int window_relative_y = event.y() - outer_rect.y(); | ||||
|  | @ -752,7 +765,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_ | |||
|     } | ||||
| 
 | ||||
|     for_each_visible_window_from_front_to_back([&] (WSWindow& window) { | ||||
|         if (window.type() != WSWindowType::Menu && outer_window_rect(window.rect()).contains(event.position())) { | ||||
|         if (window.type() == WSWindowType::Normal && outer_window_rect(window).contains(event.position())) { | ||||
|             if (m_keyboard_modifiers == Mod_Logo && event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) { | ||||
|                 start_window_drag(window, event); | ||||
|                 return IterationDecision::Abort; | ||||
|  | @ -762,7 +775,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_ | |||
|                 return IterationDecision::Abort; | ||||
|             } | ||||
|         } | ||||
|         if (window.type() != WSWindowType::Menu && title_bar_rect(window.rect()).contains(event.position())) { | ||||
|         if (window.type() == WSWindowType::Normal && title_bar_rect(window.rect()).contains(event.position())) { | ||||
|             if (event.type() == WSMessage::MouseDown) { | ||||
|                 move_to_front(window); | ||||
|                 set_active_window(&window); | ||||
|  | @ -777,7 +790,7 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& event_ | |||
|         } | ||||
| 
 | ||||
|         if (window.rect().contains(event.position())) { | ||||
|             if (window.type() != WSWindowType::Menu && event.type() == WSMessage::MouseDown) { | ||||
|             if (window.type() == WSWindowType::Normal && event.type() == WSMessage::MouseDown) { | ||||
|                 move_to_front(window); | ||||
|                 set_active_window(&window); | ||||
|             } | ||||
|  | @ -815,14 +828,14 @@ void WSWindowManager::compose() | |||
|                 //        Maybe there's some way we could know this?
 | ||||
|                 continue; | ||||
|             } | ||||
|             if (outer_window_rect(window->rect()).contains(r)) | ||||
|             if (outer_window_rect(*window).contains(r)) | ||||
|                 return true; | ||||
|         } | ||||
|         return false; | ||||
|     }; | ||||
| 
 | ||||
|     auto any_dirty_rect_intersects_window = [&dirty_rects] (const WSWindow& window) { | ||||
|         auto window_rect = outer_window_rect(window.rect()); | ||||
|         auto window_rect = outer_window_rect(window); | ||||
|         for (auto& dirty_rect : dirty_rects.rects()) { | ||||
|             if (dirty_rect.intersects(window_rect)) | ||||
|                 return true; | ||||
|  | @ -876,7 +889,9 @@ void WSWindowManager::compose() | |||
|         compose_window(*m_highlight_window); | ||||
| 
 | ||||
|     draw_menubar(); | ||||
|     draw_window_switcher(); | ||||
|     if (m_switcher.is_visible()) | ||||
|         compose_window(*m_switcher.switcher_window()); | ||||
| 
 | ||||
|     draw_cursor(); | ||||
| 
 | ||||
|     if (m_flash_flush) { | ||||
|  | @ -949,7 +964,7 @@ void WSWindowManager::draw_menubar() | |||
| void WSWindowManager::draw_window_switcher() | ||||
| { | ||||
|     if (m_switcher.is_visible()) | ||||
|         m_switcher.draw(*m_back_painter); | ||||
|         m_switcher.draw(); | ||||
| } | ||||
| 
 | ||||
| void WSWindowManager::draw_cursor() | ||||
|  | @ -1009,8 +1024,8 @@ void WSWindowManager::set_highlight_window(WSWindow* window) | |||
| 
 | ||||
| void WSWindowManager::set_active_window(WSWindow* window) | ||||
| { | ||||
|     if (window->type() == WSWindowType::Menu) { | ||||
|         dbgprintf("WSWindowManager: Attempted to make a menu window active.\n"); | ||||
|     if (window->type() != WSWindowType::Normal) { | ||||
|         dbgprintf("WSWindowManager: Attempted to make a non-normal window active.\n"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | @ -1079,7 +1094,11 @@ void WSWindowManager::invalidate(const WSWindow& window) | |||
|         return; | ||||
|     } | ||||
|     if (window.type() == WSWindowType::Normal) { | ||||
|         invalidate(outer_window_rect(window.rect())); | ||||
|         invalidate(outer_window_rect(window)); | ||||
|         return; | ||||
|     } | ||||
|     if (window.type() == WSWindowType::WindowSwitcher) { | ||||
|         invalidate(window.rect()); | ||||
|         return; | ||||
|     } | ||||
|     ASSERT_NOT_REACHED(); | ||||
|  | @ -1091,7 +1110,7 @@ void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect) | |||
|         invalidate(window); | ||||
|         return; | ||||
|     } | ||||
|     auto outer_rect = outer_window_rect(window.rect()); | ||||
|     auto outer_rect = outer_window_rect(window); | ||||
|     auto inner_rect = rect; | ||||
|     inner_rect.move_by(window.position()); | ||||
|     // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
 | ||||
|  | @ -1107,13 +1126,13 @@ void WSWindowManager::flush(const Rect& a_rect) | |||
|     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(); | ||||
|     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 = (RGBA32*)((byte*)front_ptr + pitch); | ||||
|         front_ptr = (const RGBA32*)((const byte*)front_ptr + pitch); | ||||
|         back_ptr = (RGBA32*)((byte*)back_ptr + pitch); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -16,11 +16,11 @@ void WSWindowSwitcher::set_visible(bool visible) | |||
|     if (m_visible == visible) | ||||
|         return; | ||||
|     m_visible = visible; | ||||
|     if (!m_visible) { | ||||
|         WSWindowManager::the().invalidate(m_rect); | ||||
|     if (m_switcher_window) | ||||
|         m_switcher_window->set_visible(visible); | ||||
|     if (!m_visible) | ||||
|         return; | ||||
|     } | ||||
|     invalidate(); | ||||
|     refresh(); | ||||
| } | ||||
| 
 | ||||
| WSWindow* WSWindowSwitcher::selected_window() | ||||
|  | @ -53,15 +53,15 @@ void WSWindowSwitcher::on_key_event(const WSKeyEvent& event) | |||
|     auto* highlight_window = m_windows.at(m_selected_index).ptr(); | ||||
|     ASSERT(highlight_window); | ||||
|     WSWindowManager::the().set_highlight_window(highlight_window); | ||||
|     draw(); | ||||
|     WSWindowManager::the().invalidate(m_rect); | ||||
| } | ||||
| 
 | ||||
| void WSWindowSwitcher::draw(Painter& painter) | ||||
| void WSWindowSwitcher::draw() | ||||
| { | ||||
|     painter.translate(m_rect.location()); | ||||
|     Painter painter(*m_switcher_window->backing_store()); | ||||
|     painter.fill_rect({ { }, m_rect.size() }, Color::LightGray); | ||||
|     painter.draw_rect({ { }, m_rect.size() }, Color::DarkGray); | ||||
| 
 | ||||
|     for (int index = 0; index < m_windows.size(); ++index) { | ||||
|         auto& window = *m_windows.at(index); | ||||
|         Rect item_rect { | ||||
|  | @ -80,16 +80,14 @@ void WSWindowSwitcher::draw(Painter& painter) | |||
|             text_color = Color::Black; | ||||
|             rect_text_color = Color::DarkGray; | ||||
|         } | ||||
| 
 | ||||
|         painter.set_font(Font::default_bold_font()); | ||||
|         painter.draw_text(item_rect, window.title(), TextAlignment::CenterLeft, text_color); | ||||
|         painter.set_font(WSWindowManager::the().font()); | ||||
|         painter.draw_text(item_rect, window.rect().to_string(), TextAlignment::CenterRight, rect_text_color); | ||||
|     } | ||||
|     painter.translate(-m_rect.x(), -m_rect.y()); | ||||
| } | ||||
| 
 | ||||
| void WSWindowSwitcher::invalidate() | ||||
| void WSWindowSwitcher::refresh() | ||||
| { | ||||
|     WSWindow* selected_window = nullptr; | ||||
|     if (m_selected_index > 0 && m_windows[m_selected_index]) | ||||
|  | @ -114,5 +112,13 @@ void WSWindowSwitcher::invalidate() | |||
|     m_rect.set_width(longest_title * WSWindowManager::the().font().glyph_width() + space_for_window_rect + padding() * 2); | ||||
|     m_rect.set_height(window_count * item_height() + padding() * 2); | ||||
|     m_rect.center_within(WSWindowManager::the().m_screen_rect); | ||||
|     WSWindowManager::the().invalidate(m_rect); | ||||
|     if (!m_switcher_window) | ||||
|         m_switcher_window = make<WSWindow>(*this, WSWindowType::WindowSwitcher); | ||||
|     m_switcher_window->set_rect(m_rect); | ||||
|     draw(); | ||||
| } | ||||
| 
 | ||||
| void WSWindowSwitcher::on_message(WSMessage& message) | ||||
| { | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -3,15 +3,16 @@ | |||
| #include <SharedGraphics/Rect.h> | ||||
| #include <AK/Vector.h> | ||||
| #include <AK/WeakPtr.h> | ||||
| #include <WindowServer/WSMessageReceiver.h> | ||||
| 
 | ||||
| class Painter; | ||||
| class WSKeyEvent; | ||||
| class WSWindow; | ||||
| 
 | ||||
| class WSWindowSwitcher { | ||||
| class WSWindowSwitcher : public WSMessageReceiver { | ||||
| public: | ||||
|     WSWindowSwitcher(); | ||||
|     ~WSWindowSwitcher(); | ||||
|     virtual ~WSWindowSwitcher() override; | ||||
| 
 | ||||
|     bool is_visible() const { return m_visible; } | ||||
|     void set_visible(bool); | ||||
|  | @ -20,17 +21,21 @@ public: | |||
|     void hide() { set_visible(false); } | ||||
| 
 | ||||
|     void on_key_event(const WSKeyEvent&); | ||||
|     void invalidate(); | ||||
|     void refresh(); | ||||
| 
 | ||||
|     void draw(Painter&); | ||||
|     void draw(); | ||||
| 
 | ||||
|     int item_height() { return 20; } | ||||
|     int padding() { return 8; } | ||||
| 
 | ||||
|     WSWindow* selected_window(); | ||||
| 
 | ||||
| private: | ||||
|     WSWindow* switcher_window() { return m_switcher_window.ptr(); } | ||||
| 
 | ||||
| private: | ||||
|     virtual void on_message(WSMessage&) override; | ||||
| 
 | ||||
|     OwnPtr<WSWindow> m_switcher_window; | ||||
|     Rect m_rect; | ||||
|     bool m_visible { false }; | ||||
|     Vector<WeakPtr<WSWindow>> m_windows; | ||||
|  |  | |||
|  | @ -3,5 +3,6 @@ | |||
| enum class WSWindowType { | ||||
|     Invalid = 0, | ||||
|     Normal, | ||||
|     Menu | ||||
|     Menu, | ||||
|     WindowSwitcher, | ||||
| }; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling