Serenity Operating System
1/*
2 * Copyright (c) 2021, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <WindowServer/AppletManager.h>
8#include <WindowServer/ConnectionFromClient.h>
9#include <WindowServer/Screen.h>
10#include <WindowServer/WMConnectionFromClient.h>
11
12namespace WindowServer {
13
14HashMap<int, NonnullRefPtr<WMConnectionFromClient>> WMConnectionFromClient::s_connections {};
15
16WMConnectionFromClient::WMConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> client_socket, int client_id)
17 : IPC::ConnectionFromClient<WindowManagerClientEndpoint, WindowManagerServerEndpoint>(*this, move(client_socket), client_id)
18{
19 s_connections.set(client_id, *this);
20}
21
22WMConnectionFromClient::~WMConnectionFromClient()
23{
24 // The WM has gone away, so take away the applet manager (cause there's nowhere
25 // to draw it in).
26 AppletManager::the().set_position({});
27}
28
29void WMConnectionFromClient::die()
30{
31 deferred_invoke([this] {
32 s_connections.remove(client_id());
33 });
34}
35
36void WMConnectionFromClient::set_applet_area_position(Gfx::IntPoint position)
37{
38 if (m_window_id < 0) {
39 did_misbehave("SetAppletAreaPosition: WM didn't assign window as manager yet");
40 // FIXME: return ok boolean?
41 return;
42 }
43
44 AppletManager::the().set_position(position);
45
46 WindowServer::ConnectionFromClient::for_each_client([](auto& connection) {
47 if (auto result = connection.post_message(Messages::WindowClient::AppletAreaRectChanged(AppletManager::the().window()->rect())); result.is_error()) {
48 dbgln("WMConnectionFromClient::set_applet_area_position: {}", result.error());
49 }
50 });
51}
52
53void WMConnectionFromClient::set_active_window(i32 client_id, i32 window_id)
54{
55 auto* client = WindowServer::ConnectionFromClient::from_client_id(client_id);
56 if (!client) {
57 did_misbehave("SetActiveWindow: Bad client ID");
58 return;
59 }
60 auto it = client->m_windows.find(window_id);
61 if (it == client->m_windows.end()) {
62 did_misbehave("SetActiveWindow: Bad window ID");
63 return;
64 }
65 auto& window = *(*it).value;
66 WindowManager::the().move_to_front_and_make_active(window);
67}
68
69void WMConnectionFromClient::popup_window_menu(i32 client_id, i32 window_id, Gfx::IntPoint screen_position)
70{
71 auto* client = WindowServer::ConnectionFromClient::from_client_id(client_id);
72 if (!client) {
73 did_misbehave("PopupWindowMenu: Bad client ID");
74 return;
75 }
76 auto it = client->m_windows.find(window_id);
77 if (it == client->m_windows.end()) {
78 did_misbehave("PopupWindowMenu: Bad window ID");
79 return;
80 }
81 auto& window = *(*it).value;
82 if (auto* modal_window = window.blocking_modal_window()) {
83 modal_window->popup_window_menu(screen_position, WindowMenuDefaultAction::BasedOnWindowState);
84 } else {
85 window.popup_window_menu(screen_position, WindowMenuDefaultAction::BasedOnWindowState);
86 }
87}
88
89void WMConnectionFromClient::start_window_resize(i32 client_id, i32 window_id, i32 resize_direction)
90{
91 auto* client = WindowServer::ConnectionFromClient::from_client_id(client_id);
92 if (!client) {
93 did_misbehave("WM_StartWindowResize: Bad client ID");
94 return;
95 }
96 auto it = client->m_windows.find(window_id);
97 if (it == client->m_windows.end()) {
98 did_misbehave("WM_StartWindowResize: Bad window ID");
99 return;
100 }
101 auto& window = *(*it).value;
102 // FIXME: We are cheating a bit here by using the current cursor location and hard-coding the left button.
103 // Maybe the client should be allowed to specify what initiated this request?
104 WindowManager::the().start_window_resize(window, ScreenInput::the().cursor_location(), MouseButton::Primary, (ResizeDirection)resize_direction);
105}
106
107void WMConnectionFromClient::set_window_minimized(i32 client_id, i32 window_id, bool minimized)
108{
109 auto* client = WindowServer::ConnectionFromClient::from_client_id(client_id);
110 if (!client) {
111 did_misbehave("WM_SetWindowMinimized: Bad client ID");
112 return;
113 }
114 auto it = client->m_windows.find(window_id);
115 if (it == client->m_windows.end()) {
116 did_misbehave("WM_SetWindowMinimized: Bad window ID");
117 return;
118 }
119 auto& window = *(*it).value;
120 WindowManager::the().minimize_windows(window, minimized);
121}
122
123void WMConnectionFromClient::toggle_show_desktop()
124{
125 bool should_hide = false;
126 auto& current_window_stack = WindowManager::the().current_window_stack();
127 current_window_stack.for_each_window([&](auto& window) {
128 if (window.type() == WindowType::Normal && window.is_minimizable()) {
129 if (!window.is_hidden() && !window.is_minimized()) {
130 should_hide = true;
131 return IterationDecision::Break;
132 }
133 }
134 return IterationDecision::Continue;
135 });
136
137 current_window_stack.for_each_window([&](auto& window) {
138 if (window.type() == WindowType::Normal && window.is_minimizable()) {
139 auto state = window.minimized_state();
140 if (state == WindowMinimizedState::None || state == WindowMinimizedState::Hidden) {
141 WindowManager::the().hide_windows(window, should_hide);
142 }
143 }
144 return IterationDecision::Continue;
145 });
146}
147
148void WMConnectionFromClient::set_event_mask(u32 event_mask)
149{
150 m_event_mask = event_mask;
151}
152
153void WMConnectionFromClient::set_manager_window(i32 window_id)
154{
155 m_window_id = window_id;
156
157 // Let the window manager know that we obtained a manager window, and should
158 // receive information about other windows.
159 WindowManager::the().greet_window_manager(*this);
160}
161
162void WMConnectionFromClient::set_workspace(u32 row, u32 col)
163{
164 WindowManager::the().switch_to_window_stack(row, col);
165}
166
167void WMConnectionFromClient::set_window_taskbar_rect(i32 client_id, i32 window_id, Gfx::IntRect const& rect)
168{
169 // Because the Taskbar (which should be the only user of this API) does not own the
170 // window or the client id, there is a possibility that it may send this message for
171 // a window or client that may have been destroyed already. This is not an error,
172 // and we should not call did_misbehave() for either.
173 auto* client = WindowServer::ConnectionFromClient::from_client_id(client_id);
174 if (!client)
175 return;
176
177 auto it = client->m_windows.find(window_id);
178 if (it == client->m_windows.end())
179 return;
180
181 auto& window = *(*it).value;
182 window.set_taskbar_rect(rect);
183}
184
185void WMConnectionFromClient::set_keymap(DeprecatedString const& keymap)
186{
187 WindowManager::the().keymap_switcher()->set_keymap(keymap);
188}
189
190}