1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#pragma once
10#include "../../shared/Connections/WSConnection.h"
11#include "../Components/ControlBar/ControlBar.h"
12#include "../Components/MenuBar/MenuBar.h"
13#include "../Components/Security/Violations.h"
14#include "../Devices/Screen.h"
15#include "../IPC/Connection.h"
16#include "../IPC/Event.h"
17#include "../IPC/ServerDecoder.h"
18#include "../Managers/Compositor.h"
19#include "../SystemApps/SystemApp.h"
20#include "../Target/Generic/Window.h"
21#include <algorithm>
22#include <libfoundation/EventLoop.h>
23#include <libfoundation/EventReceiver.h>
24#include <libipc/ServerConnection.h>
25#include <list>
26#include <vector>
27
28namespace WinServer {
29
30class WindowManager : public LFoundation::EventReceiver {
31#ifdef TARGET_DESKTOP
32 using Window = WinServer::Desktop::Window;
33#elif TARGET_MOBILE
34 using Window = WinServer::Mobile::Window;
35#endif
36
37public:
38 inline static WindowManager& the()
39 {
40 extern WindowManager* s_WinServer_WindowManager_the;
41 return *s_WinServer_WindowManager_the;
42 }
43
44 WindowManager();
45
46 void add_window(Window* window);
47 void remove_window(Window* window);
48
49 void resize_window(Window& window, const LG::Size& size);
50 void close_window(Window& window) { send_event(new WindowCloseRequestMessage(window.connection_id(), window.id())); }
51 void minimize_window(Window& window);
52 void maximize_window(Window& window);
53
54 template <typename Callback>
55 void minimize_windows(Callback callback)
56 {
57 std::vector<Window*> hided;
58 for (auto* window : m_windows) {
59 if (window && window->type() == WindowType::Standard && callback(window)) {
60 window->set_visible(false);
61 hided.push_back(window);
62 on_window_became_invisible(window);
63 remove_attention_from_window(window);
64 }
65 }
66
67 for (auto* window : hided) {
68 m_windows.erase(std::find(m_windows.begin(), m_windows.end(), window));
69 m_windows.push_back(window);
70 }
71 }
72
73 Window* top_window_in_view(WindowType type) const;
74 inline Window* window(int id)
75 {
76 for (auto* window : m_windows) {
77 if (window->id() == id) {
78 return window;
79 }
80 }
81 return nullptr;
82 }
83
84 inline void move_window(Window* window, int x_offset, int y_offset)
85 {
86 y_offset = std::max(y_offset, (int)visible_area().min_y() - (int)Desktop::WindowFrame::std_top_border_frame_size() - window->bounds().min_y());
87 if (m_dock.has_value()) [[likely]] {
88 y_offset = std::min(y_offset, (int)(visible_area().max_y() - window->content_bounds().min_y()));
89 }
90 window->bounds().offset_by(x_offset, y_offset);
91 window->content_bounds().offset_by(x_offset, y_offset);
92 }
93
94 inline void do_bring_to_front(Window& window)
95 {
96 auto* window_ptr = &window;
97 m_windows.erase(std::find(m_windows.begin(), m_windows.end(), window_ptr));
98 m_windows.push_front(window_ptr);
99 }
100 void bring_to_front(Window& window);
101
102 inline std::list<Window*>& windows() { return m_windows; }
103 inline const std::list<Window*>& windows() const { return m_windows; }
104 inline int next_win_id() { return ++m_next_win_id; }
105
106 const LG::Rect& visible_area() const { return m_visible_area; }
107 void shrink_visible_area(int top, int bottom) { m_visible_area.set_y(m_visible_area.min_y() + top), m_visible_area.set_height(m_visible_area.height() - top - bottom); }
108
109 void receive_event(std::unique_ptr<LFoundation::Event> event) override;
110
111 // Notifiers
112 bool notify_listner_about_window_creation(const Window& window, int changed_window_id);
113 bool notify_listner_about_window_status(const Window& window, int changed_window_id, WindowStatusUpdateType type);
114 bool notify_listner_about_changed_icon(const Window&, int changed_window_id);
115 bool notify_listner_about_changed_title(const Window&, int changed_window_id);
116
117 void notify_window_creation(int changed_window_id);
118 void notify_window_status_changed(int changed_window_id, WindowStatusUpdateType type);
119 void notify_window_icon_changed(int changed_window_id);
120 void notify_window_title_changed(int changed_window_id);
121
122 inline void ask_to_set_active_window(Window* win) { set_active_window(win); }
123 inline void ask_to_set_active_window(Window& win) { set_active_window(win); }
124
125 void on_window_misbehave(Window& window, ViolationClass);
126 void on_window_style_change(Window& win);
127 void on_window_menubar_change(Window& window);
128
129 // Popup & Menubar
130 inline Popup& popup() { return m_compositor.popup(); }
131 inline const Popup& popup() const { return m_compositor.popup(); }
132 inline MenuBar& menu_bar() { return m_compositor.menu_bar(); }
133 inline const MenuBar& menu_bar() const { return m_compositor.menu_bar(); }
134
135private:
136 void add_system_window(Window* window);
137 void bring_system_windows_to_front();
138 void setup_dock(Window* window);
139 void setup_applist(Window* window);
140
141 void remove_attention_from_window(Window* window);
142
143 void start_window_move(Window& window);
144 bool continue_window_move();
145
146 void update_mouse_position(std::unique_ptr<LFoundation::Event> mouse_event);
147 void receive_mouse_event(std::unique_ptr<LFoundation::Event> event);
148 void receive_keyboard_event(std::unique_ptr<LFoundation::Event> event);
149
150 inline Window* movable_window() { return m_movable_window; }
151 inline Window* hovered_window() { return m_hovered_window; }
152 inline void set_hovered_window(Window* win) { m_hovered_window = win; }
153
154 inline Window* active_window() { return m_active_window; }
155 inline void set_active_window(Window* win) { on_active_window_will_change(), bring_to_front(*win), m_active_window = win, on_active_window_did_change(); }
156 inline void set_active_window(Window& win) { on_active_window_will_change(), bring_to_front(win), m_active_window = &win, on_active_window_did_change(); }
157 inline void set_active_window(std::nullptr_t) { on_active_window_will_change(), m_active_window = nullptr, on_active_window_did_change(); }
158
159 void on_window_became_invisible(Window* window);
160 void on_active_window_will_change();
161 void on_active_window_did_change();
162
163 inline void send_event(Message* msg) { m_event_loop.add(m_connection, new SendEvent(msg)); }
164
165 std::list<Window*> m_windows;
166
167 Screen& m_screen;
168 Connection& m_connection;
169 Compositor& m_compositor;
170 CursorManager& m_cursor_manager;
171 LFoundation::EventLoop& m_event_loop;
172 std::vector<MenuDir> m_std_menubar_content;
173
174 LG::Rect m_visible_area;
175
176 SystemApp m_dock;
177 SystemApp m_applist;
178
179 // TODO: implement with std::weak_ptr.
180 Window* m_movable_window {};
181 Window* m_active_window {};
182 Window* m_hovered_window {};
183 int m_next_win_id { 0 };
184};
185
186} // namespace WinServer