Serenity Operating System
at hosted 407 lines 16 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#pragma once 28 29#include <AK/HashMap.h> 30#include <AK/HashTable.h> 31#include <AK/InlineLinkedList.h> 32#include <AK/WeakPtr.h> 33#include <LibCore/ConfigFile.h> 34#include <LibCore/ElapsedTimer.h> 35#include <LibGfx/Color.h> 36#include <LibGfx/DisjointRectSet.h> 37#include <LibGfx/Painter.h> 38#include <LibGfx/Palette.h> 39#include <LibGfx/Rect.h> 40#include <WindowServer/Cursor.h> 41#include <WindowServer/Event.h> 42#include <WindowServer/MenuBar.h> 43#include <WindowServer/MenuManager.h> 44#include <WindowServer/Window.h> 45#include <WindowServer/WindowSwitcher.h> 46#include <WindowServer/WindowType.h> 47 48namespace WindowServer { 49 50class Screen; 51class MouseEvent; 52class Window; 53class ClientConnection; 54class WindowSwitcher; 55class Button; 56 57enum class ResizeDirection { 58 None, 59 Left, 60 UpLeft, 61 Up, 62 UpRight, 63 Right, 64 DownRight, 65 Down, 66 DownLeft 67}; 68 69class WindowManager : public Core::Object { 70 C_OBJECT(WindowManager) 71 72 friend class Compositor; 73 friend class WindowFrame; 74 friend class WindowSwitcher; 75 76public: 77 static WindowManager& the(); 78 79 explicit WindowManager(const Gfx::PaletteImpl&); 80 virtual ~WindowManager() override; 81 82 Palette palette() const { return Palette(*m_palette); } 83 84 RefPtr<Core::ConfigFile> wm_config() const 85 { 86 return m_wm_config; 87 } 88 void reload_config(bool); 89 90 void add_window(Window&); 91 void remove_window(Window&); 92 93 void notify_title_changed(Window&); 94 void notify_rect_changed(Window&, const Gfx::Rect& oldRect, const Gfx::Rect& newRect); 95 void notify_minimization_state_changed(Window&); 96 void notify_opacity_changed(Window&); 97 void notify_occlusion_state_changed(Window&); 98 void notify_client_changed_app_menubar(ClientConnection&); 99 100 Gfx::Rect maximized_window_rect(const Window&) const; 101 102 ClientConnection* dnd_client() { return m_dnd_client.ptr(); } 103 const String& dnd_text() const { return m_dnd_text; } 104 const String& dnd_data_type() const { return m_dnd_data_type; } 105 const String& dnd_data() const { return m_dnd_data; } 106 const Gfx::Bitmap* dnd_bitmap() const { return m_dnd_bitmap; } 107 Gfx::Rect dnd_rect() const; 108 109 void start_dnd_drag(ClientConnection&, const String& text, Gfx::Bitmap*, const String& data_type, const String& data); 110 void end_dnd_drag(); 111 112 Window* active_window() { return m_active_window.ptr(); } 113 const ClientConnection* active_client() const; 114 bool active_window_is_modal() const { return m_active_window && m_active_window->is_modal(); } 115 116 Window* highlight_window() { return m_highlight_window.ptr(); } 117 void set_highlight_window(Window*); 118 119 void move_to_front_and_make_active(Window&); 120 121 Gfx::Rect menubar_rect() const; 122 123 const Cursor& active_cursor() const; 124 const Cursor& arrow_cursor() const { return *m_arrow_cursor; } 125 const Cursor& hand_cursor() const { return *m_hand_cursor; } 126 const Cursor& resize_horizontally_cursor() const { return *m_resize_horizontally_cursor; } 127 const Cursor& resize_vertically_cursor() const { return *m_resize_vertically_cursor; } 128 const Cursor& resize_diagonally_tlbr_cursor() const { return *m_resize_diagonally_tlbr_cursor; } 129 const Cursor& resize_diagonally_bltr_cursor() const { return *m_resize_diagonally_bltr_cursor; } 130 const Cursor& i_beam_cursor() const { return *m_i_beam_cursor; } 131 const Cursor& disallowed_cursor() const { return *m_disallowed_cursor; } 132 const Cursor& move_cursor() const { return *m_move_cursor; } 133 const Cursor& drag_cursor() const { return *m_drag_cursor; } 134 135 void invalidate(const Window&); 136 void invalidate(const Window&, const Gfx::Rect&); 137 void invalidate(const Gfx::Rect&); 138 void invalidate(); 139 void flush(const Gfx::Rect&); 140 141 const Gfx::Font& font() const; 142 const Gfx::Font& window_title_font() const; 143 144 bool set_resolution(int width, int height); 145 Gfx::Size resolution() const; 146 147 void set_active_window(Window*); 148 void set_hovered_button(Button*); 149 150 Button* cursor_tracking_button() { return m_cursor_tracking_button.ptr(); } 151 void set_cursor_tracking_button(Button*); 152 153 void set_resize_candidate(Window&, ResizeDirection); 154 void clear_resize_candidate(); 155 ResizeDirection resize_direction_of_window(const Window&); 156 157 bool any_opaque_window_contains_rect(const Gfx::Rect&); 158 bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::Rect&); 159 160 void tell_wm_listeners_window_state_changed(Window&); 161 void tell_wm_listeners_window_icon_changed(Window&); 162 void tell_wm_listeners_window_rect_changed(Window&); 163 164 void start_window_resize(Window&, const Gfx::Point&, MouseButton); 165 void start_window_resize(Window&, const MouseEvent&); 166 167 const Window* active_fullscreen_window() const { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; } 168 Window* active_fullscreen_window() { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; } 169 170 bool update_theme(String theme_path, String theme_name); 171 172 void set_hovered_window(Window*); 173 void deliver_mouse_event(Window& window, MouseEvent& event); 174 175private: 176 NonnullRefPtr<Cursor> get_cursor(const String& name); 177 NonnullRefPtr<Cursor> get_cursor(const String& name, const Gfx::Point& hotspot); 178 179 void process_mouse_event(MouseEvent&, Window*& hovered_window); 180 void process_event_for_doubleclick(Window& window, MouseEvent& event); 181 bool process_ongoing_window_resize(const MouseEvent&, Window*& hovered_window); 182 bool process_ongoing_window_move(MouseEvent&, Window*& hovered_window); 183 bool process_ongoing_drag(MouseEvent&, Window*& hovered_window); 184 void start_window_move(Window&, const MouseEvent&); 185 template<typename Callback> 186 IterationDecision for_each_visible_window_of_type_from_back_to_front(WindowType, Callback, bool ignore_highlight = false); 187 template<typename Callback> 188 IterationDecision for_each_visible_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); 189 template<typename Callback> 190 IterationDecision for_each_visible_window_from_front_to_back(Callback); 191 template<typename Callback> 192 IterationDecision for_each_visible_window_from_back_to_front(Callback); 193 template<typename Callback> 194 void for_each_window_listening_to_wm_events(Callback); 195 template<typename Callback> 196 void for_each_window(Callback); 197 template<typename Callback> 198 IterationDecision for_each_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); 199 200 virtual void event(Core::Event&) override; 201 void paint_window_frame(const Window&); 202 void tell_wm_listener_about_window(Window& listener, Window&); 203 void tell_wm_listener_about_window_icon(Window& listener, Window&); 204 void tell_wm_listener_about_window_rect(Window& listener, Window&); 205 void pick_new_active_window(); 206 207 void recompute_occlusions(); 208 209 RefPtr<Cursor> m_arrow_cursor; 210 RefPtr<Cursor> m_hand_cursor; 211 RefPtr<Cursor> m_resize_horizontally_cursor; 212 RefPtr<Cursor> m_resize_vertically_cursor; 213 RefPtr<Cursor> m_resize_diagonally_tlbr_cursor; 214 RefPtr<Cursor> m_resize_diagonally_bltr_cursor; 215 RefPtr<Cursor> m_i_beam_cursor; 216 RefPtr<Cursor> m_disallowed_cursor; 217 RefPtr<Cursor> m_move_cursor; 218 RefPtr<Cursor> m_drag_cursor; 219 220 InlineLinkedList<Window> m_windows_in_order; 221 222 struct DoubleClickInfo { 223 struct ClickMetadata { 224 Core::ElapsedTimer clock; 225 Gfx::Point last_position; 226 }; 227 228 ClickMetadata& metadata_for_button(MouseButton); 229 230 void reset() 231 { 232 m_left = {}; 233 m_right = {}; 234 m_middle = {}; 235 } 236 237 WeakPtr<Window> m_clicked_window; 238 239 private: 240 ClickMetadata m_left; 241 ClickMetadata m_right; 242 ClickMetadata m_middle; 243 }; 244 DoubleClickInfo m_double_click_info; 245 int m_double_click_speed { 0 }; 246 int m_max_distance_for_double_click { 4 }; 247 248 WeakPtr<Window> m_active_window; 249 WeakPtr<Window> m_hovered_window; 250 WeakPtr<Window> m_highlight_window; 251 WeakPtr<Window> m_active_input_window; 252 253 WeakPtr<Window> m_move_window; 254 Gfx::Point m_move_origin; 255 Gfx::Point m_move_window_origin; 256 257 WeakPtr<Window> m_resize_window; 258 WeakPtr<Window> m_resize_candidate; 259 MouseButton m_resizing_mouse_button { MouseButton::None }; 260 Gfx::Rect m_resize_window_original_rect; 261 Gfx::Point m_resize_origin; 262 ResizeDirection m_resize_direction { ResizeDirection::None }; 263 264 bool m_moved_or_resized_since_logo_keydown { false }; 265 266 u8 m_keyboard_modifiers { 0 }; 267 268 WindowSwitcher m_switcher; 269 270 WeakPtr<Button> m_cursor_tracking_button; 271 WeakPtr<Button> m_hovered_button; 272 273 NonnullRefPtr<Gfx::PaletteImpl> m_palette; 274 275 RefPtr<Core::ConfigFile> m_wm_config; 276 277 WeakPtr<ClientConnection> m_dnd_client; 278 String m_dnd_text; 279 String m_dnd_data_type; 280 String m_dnd_data; 281 RefPtr<Gfx::Bitmap> m_dnd_bitmap; 282}; 283 284template<typename Callback> 285IterationDecision WindowManager::for_each_visible_window_of_type_from_back_to_front(WindowType type, Callback callback, bool ignore_highlight) 286{ 287 bool do_highlight_window_at_end = false; 288 for (auto& window : m_windows_in_order) { 289 if (!window.is_visible()) 290 continue; 291 if (window.is_minimized()) 292 continue; 293 if (window.type() != type) 294 continue; 295 if (!ignore_highlight && m_highlight_window == &window) { 296 do_highlight_window_at_end = true; 297 continue; 298 } 299 if (callback(window) == IterationDecision::Break) 300 return IterationDecision::Break; 301 } 302 if (do_highlight_window_at_end) { 303 if (callback(*m_highlight_window) == IterationDecision::Break) 304 return IterationDecision::Break; 305 } 306 return IterationDecision::Continue; 307} 308 309template<typename Callback> 310IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Callback callback) 311{ 312 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Normal, callback) == IterationDecision::Break) 313 return IterationDecision::Break; 314 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, callback) == IterationDecision::Break) 315 return IterationDecision::Break; 316 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Tooltip, callback) == IterationDecision::Break) 317 return IterationDecision::Break; 318 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break) 319 return IterationDecision::Break; 320 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menubar, callback) == IterationDecision::Break) 321 return IterationDecision::Break; 322 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menu, callback) == IterationDecision::Break) 323 return IterationDecision::Break; 324 return for_each_visible_window_of_type_from_back_to_front(WindowType::WindowSwitcher, callback); 325} 326 327template<typename Callback> 328IterationDecision WindowManager::for_each_visible_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight) 329{ 330 if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { 331 if (callback(*m_highlight_window) == IterationDecision::Break) 332 return IterationDecision::Break; 333 } 334 335 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 336 if (!window->is_visible()) 337 continue; 338 if (window->is_minimized()) 339 continue; 340 if (window->type() != type) 341 continue; 342 if (!ignore_highlight && window == m_highlight_window) 343 continue; 344 if (callback(*window) == IterationDecision::Break) 345 return IterationDecision::Break; 346 } 347 return IterationDecision::Continue; 348} 349 350template<typename Callback> 351IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Callback callback) 352{ 353 if (for_each_visible_window_of_type_from_front_to_back(WindowType::WindowSwitcher, callback) == IterationDecision::Break) 354 return IterationDecision::Break; 355 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menu, callback) == IterationDecision::Break) 356 return IterationDecision::Break; 357 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menubar, callback) == IterationDecision::Break) 358 return IterationDecision::Break; 359 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break) 360 return IterationDecision::Break; 361 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Tooltip, callback) == IterationDecision::Break) 362 return IterationDecision::Break; 363 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break) 364 return IterationDecision::Break; 365 return for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, callback); 366} 367 368template<typename Callback> 369void WindowManager::for_each_window_listening_to_wm_events(Callback callback) 370{ 371 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 372 if (!window->listens_to_wm_events()) 373 continue; 374 if (callback(*window) == IterationDecision::Break) 375 return; 376 } 377} 378 379template<typename Callback> 380void WindowManager::for_each_window(Callback callback) 381{ 382 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 383 if (callback(*window) == IterationDecision::Break) 384 return; 385 } 386} 387 388template<typename Callback> 389IterationDecision WindowManager::for_each_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight) 390{ 391 if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { 392 if (callback(*m_highlight_window) == IterationDecision::Break) 393 return IterationDecision::Break; 394 } 395 396 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 397 if (window->type() != type) 398 continue; 399 if (!ignore_highlight && window == m_highlight_window) 400 continue; 401 if (callback(*window) == IterationDecision::Break) 402 return IterationDecision::Break; 403 } 404 return IterationDecision::Continue; 405} 406 407}