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 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}