Serenity Operating System
at portability 416 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 void set_resolution(int width, int height); 145 146 void set_active_window(Window*); 147 void set_hovered_button(Button*); 148 149 Button* cursor_tracking_button() { return m_cursor_tracking_button.ptr(); } 150 void set_cursor_tracking_button(Button*); 151 152 void set_resize_candidate(Window&, ResizeDirection); 153 void clear_resize_candidate(); 154 ResizeDirection resize_direction_of_window(const Window&); 155 156 bool any_opaque_window_contains_rect(const Gfx::Rect&); 157 bool any_opaque_window_above_this_one_contains_rect(const Window&, const Gfx::Rect&); 158 159 void tell_wm_listeners_window_state_changed(Window&); 160 void tell_wm_listeners_window_icon_changed(Window&); 161 void tell_wm_listeners_window_rect_changed(Window&); 162 163 void start_window_resize(Window&, const Gfx::Point&, MouseButton); 164 void start_window_resize(Window&, const MouseEvent&); 165 166 const Window* active_fullscreen_window() const { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; } 167 Window* active_fullscreen_window() { return (m_active_window && m_active_window->is_fullscreen()) ? m_active_window : nullptr; } 168 169 bool update_theme(String theme_path, String theme_name); 170 171 void set_hovered_window(Window*); 172 void deliver_mouse_event(Window& window, MouseEvent& event); 173 174private: 175 NonnullRefPtr<Cursor> get_cursor(const String& name); 176 NonnullRefPtr<Cursor> get_cursor(const String& name, const Gfx::Point& hotspot); 177 178 void process_mouse_event(MouseEvent&, Window*& hovered_window); 179 void process_event_for_doubleclick(Window& window, MouseEvent& event); 180 bool process_ongoing_window_resize(const MouseEvent&, Window*& hovered_window); 181 bool process_ongoing_window_move(MouseEvent&, Window*& hovered_window); 182 bool process_ongoing_drag(MouseEvent&, Window*& hovered_window); 183 void start_window_move(Window&, const MouseEvent&); 184 template<typename Callback> 185 IterationDecision for_each_visible_window_of_type_from_back_to_front(WindowType, Callback, bool ignore_highlight = false); 186 template<typename Callback> 187 IterationDecision for_each_visible_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); 188 template<typename Callback> 189 IterationDecision for_each_visible_window_from_front_to_back(Callback); 190 template<typename Callback> 191 IterationDecision for_each_visible_window_from_back_to_front(Callback); 192 template<typename Callback> 193 void for_each_window_listening_to_wm_events(Callback); 194 template<typename Callback> 195 void for_each_window(Callback); 196 template<typename Callback> 197 IterationDecision for_each_window_of_type_from_front_to_back(WindowType, Callback, bool ignore_highlight = false); 198 199 virtual void event(Core::Event&) override; 200 void paint_window_frame(const Window&); 201 void tell_wm_listener_about_window(Window& listener, Window&); 202 void tell_wm_listener_about_window_icon(Window& listener, Window&); 203 void tell_wm_listener_about_window_rect(Window& listener, Window&); 204 void pick_new_active_window(); 205 206 void recompute_occlusions(); 207 208 RefPtr<Cursor> m_arrow_cursor; 209 RefPtr<Cursor> m_hand_cursor; 210 RefPtr<Cursor> m_resize_horizontally_cursor; 211 RefPtr<Cursor> m_resize_vertically_cursor; 212 RefPtr<Cursor> m_resize_diagonally_tlbr_cursor; 213 RefPtr<Cursor> m_resize_diagonally_bltr_cursor; 214 RefPtr<Cursor> m_i_beam_cursor; 215 RefPtr<Cursor> m_disallowed_cursor; 216 RefPtr<Cursor> m_move_cursor; 217 RefPtr<Cursor> m_drag_cursor; 218 219 Color m_background_color; 220 Color m_active_window_border_color; 221 Color m_active_window_border_color2; 222 Color m_active_window_title_color; 223 Color m_inactive_window_border_color; 224 Color m_inactive_window_border_color2; 225 Color m_inactive_window_title_color; 226 Color m_moving_window_border_color; 227 Color m_moving_window_border_color2; 228 Color m_moving_window_title_color; 229 Color m_highlight_window_border_color; 230 Color m_highlight_window_border_color2; 231 Color m_highlight_window_title_color; 232 233 InlineLinkedList<Window> m_windows_in_order; 234 235 struct DoubleClickInfo { 236 struct ClickMetadata { 237 Core::ElapsedTimer clock; 238 Gfx::Point last_position; 239 }; 240 241 ClickMetadata& metadata_for_button(MouseButton); 242 243 void reset() 244 { 245 m_left = {}; 246 m_right = {}; 247 m_middle = {}; 248 } 249 250 WeakPtr<Window> m_clicked_window; 251 252 private: 253 ClickMetadata m_left; 254 ClickMetadata m_right; 255 ClickMetadata m_middle; 256 }; 257 DoubleClickInfo m_double_click_info; 258 int m_double_click_speed { 0 }; 259 int m_max_distance_for_double_click { 4 }; 260 261 WeakPtr<Window> m_active_window; 262 WeakPtr<Window> m_hovered_window; 263 WeakPtr<Window> m_highlight_window; 264 WeakPtr<Window> m_active_input_window; 265 266 WeakPtr<Window> m_move_window; 267 Gfx::Point m_move_origin; 268 Gfx::Point m_move_window_origin; 269 270 WeakPtr<Window> m_resize_window; 271 WeakPtr<Window> m_resize_candidate; 272 MouseButton m_resizing_mouse_button { MouseButton::None }; 273 Gfx::Rect m_resize_window_original_rect; 274 Gfx::Point m_resize_origin; 275 ResizeDirection m_resize_direction { ResizeDirection::None }; 276 277 bool m_moved_or_resized_since_logo_keydown { false }; 278 279 u8 m_keyboard_modifiers { 0 }; 280 281 WindowSwitcher m_switcher; 282 283 WeakPtr<Button> m_cursor_tracking_button; 284 WeakPtr<Button> m_hovered_button; 285 286 NonnullRefPtr<Gfx::PaletteImpl> m_palette; 287 288 RefPtr<Core::ConfigFile> m_wm_config; 289 290 WeakPtr<ClientConnection> m_dnd_client; 291 String m_dnd_text; 292 String m_dnd_data_type; 293 String m_dnd_data; 294 RefPtr<Gfx::Bitmap> m_dnd_bitmap; 295}; 296 297template<typename Callback> 298IterationDecision WindowManager::for_each_visible_window_of_type_from_back_to_front(WindowType type, Callback callback, bool ignore_highlight) 299{ 300 bool do_highlight_window_at_end = false; 301 for (auto& window : m_windows_in_order) { 302 if (!window.is_visible()) 303 continue; 304 if (window.is_minimized()) 305 continue; 306 if (window.type() != type) 307 continue; 308 if (!ignore_highlight && m_highlight_window == &window) { 309 do_highlight_window_at_end = true; 310 continue; 311 } 312 if (callback(window) == IterationDecision::Break) 313 return IterationDecision::Break; 314 } 315 if (do_highlight_window_at_end) { 316 if (callback(*m_highlight_window) == IterationDecision::Break) 317 return IterationDecision::Break; 318 } 319 return IterationDecision::Continue; 320} 321 322template<typename Callback> 323IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Callback callback) 324{ 325 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Normal, callback) == IterationDecision::Break) 326 return IterationDecision::Break; 327 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, callback) == IterationDecision::Break) 328 return IterationDecision::Break; 329 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Tooltip, callback) == IterationDecision::Break) 330 return IterationDecision::Break; 331 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menubar, callback) == IterationDecision::Break) 332 return IterationDecision::Break; 333 if (for_each_visible_window_of_type_from_back_to_front(WindowType::Menu, callback) == IterationDecision::Break) 334 return IterationDecision::Break; 335 return for_each_visible_window_of_type_from_back_to_front(WindowType::WindowSwitcher, callback); 336} 337 338template<typename Callback> 339IterationDecision WindowManager::for_each_visible_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight) 340{ 341 if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { 342 if (callback(*m_highlight_window) == IterationDecision::Break) 343 return IterationDecision::Break; 344 } 345 346 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 347 if (!window->is_visible()) 348 continue; 349 if (window->is_minimized()) 350 continue; 351 if (window->type() != type) 352 continue; 353 if (!ignore_highlight && window == m_highlight_window) 354 continue; 355 if (callback(*window) == IterationDecision::Break) 356 return IterationDecision::Break; 357 } 358 return IterationDecision::Continue; 359} 360 361template<typename Callback> 362IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Callback callback) 363{ 364 if (for_each_visible_window_of_type_from_front_to_back(WindowType::WindowSwitcher, callback) == IterationDecision::Break) 365 return IterationDecision::Break; 366 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menu, callback) == IterationDecision::Break) 367 return IterationDecision::Break; 368 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Menubar, callback) == IterationDecision::Break) 369 return IterationDecision::Break; 370 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break) 371 return IterationDecision::Break; 372 if (for_each_visible_window_of_type_from_front_to_back(WindowType::Tooltip, callback) == IterationDecision::Break) 373 return IterationDecision::Break; 374 return for_each_visible_window_of_type_from_front_to_back(WindowType::Normal, callback); 375} 376 377template<typename Callback> 378void WindowManager::for_each_window_listening_to_wm_events(Callback callback) 379{ 380 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 381 if (!window->listens_to_wm_events()) 382 continue; 383 if (callback(*window) == IterationDecision::Break) 384 return; 385 } 386} 387 388template<typename Callback> 389void WindowManager::for_each_window(Callback callback) 390{ 391 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 392 if (callback(*window) == IterationDecision::Break) 393 return; 394 } 395} 396 397template<typename Callback> 398IterationDecision WindowManager::for_each_window_of_type_from_front_to_back(WindowType type, Callback callback, bool ignore_highlight) 399{ 400 if (!ignore_highlight && m_highlight_window && m_highlight_window->type() == type && m_highlight_window->is_visible()) { 401 if (callback(*m_highlight_window) == IterationDecision::Break) 402 return IterationDecision::Break; 403 } 404 405 for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { 406 if (window->type() != type) 407 continue; 408 if (!ignore_highlight && window == m_highlight_window) 409 continue; 410 if (callback(*window) == IterationDecision::Break) 411 return IterationDecision::Break; 412 } 413 return IterationDecision::Continue; 414} 415 416}