Serenity Operating System
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}