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#include <AK/SharedBuffer.h>
28#include <LibCore/EventLoop.h>
29#include <LibCore/MimeData.h>
30#include <LibGUI/Action.h>
31#include <LibGUI/Application.h>
32#include <LibGUI/Clipboard.h>
33#include <LibGUI/Desktop.h>
34#include <LibGUI/DisplayLink.h>
35#include <LibGUI/DragOperation.h>
36#include <LibGUI/Event.h>
37#include <LibGUI/Menu.h>
38#include <LibGUI/Widget.h>
39#include <LibGUI/Window.h>
40#include <LibGUI/WindowServerConnection.h>
41#include <LibGfx/Bitmap.h>
42#include <LibGfx/Palette.h>
43#include <LibGfx/SystemTheme.h>
44
45//#define GEVENTLOOP_DEBUG
46
47namespace GUI {
48
49WindowServerConnection& WindowServerConnection::the()
50{
51 static WindowServerConnection* s_connection = nullptr;
52 if (!s_connection)
53 s_connection = new WindowServerConnection;
54 return *s_connection;
55}
56
57static void set_system_theme_from_shbuf_id(int id)
58{
59 auto system_theme = SharedBuffer::create_from_shbuf_id(id);
60 ASSERT(system_theme);
61 Gfx::set_system_theme(*system_theme);
62 Application::the().set_system_palette(*system_theme);
63}
64
65void WindowServerConnection::handshake()
66{
67 auto response = send_sync<Messages::WindowServer::Greet>();
68 set_my_client_id(response->client_id());
69 set_system_theme_from_shbuf_id(response->system_theme_buffer_id());
70 Desktop::the().did_receive_screen_rect({}, response->screen_rect());
71}
72
73void WindowServerConnection::handle(const Messages::WindowClient::UpdateSystemTheme& message)
74{
75 set_system_theme_from_shbuf_id(message.system_theme_buffer_id());
76 Window::update_all_windows({});
77 Window::for_each_window({}, [](auto& window) {
78 Core::EventLoop::current().post_event(window, make<ThemeChangeEvent>());
79 });
80}
81
82void WindowServerConnection::handle(const Messages::WindowClient::Paint& message)
83{
84#ifdef GEVENTLOOP_DEBUG
85 dbgprintf("WID=%d Paint\n", message.window_id());
86#endif
87 if (auto* window = Window::from_window_id(message.window_id()))
88 Core::EventLoop::current().post_event(*window, make<MultiPaintEvent>(message.rects(), message.window_size()));
89}
90
91void WindowServerConnection::handle(const Messages::WindowClient::WindowResized& message)
92{
93 if (auto* window = Window::from_window_id(message.window_id())) {
94 Core::EventLoop::current().post_event(*window, make<ResizeEvent>(message.old_rect().size(), message.new_rect().size()));
95 }
96}
97
98void WindowServerConnection::handle(const Messages::WindowClient::WindowActivated& message)
99{
100#ifdef GEVENTLOOP_DEBUG
101 dbgprintf("(%d) WID=%d WindowActivated\n", getpid(), message.window_id());
102#endif
103 if (auto* window = Window::from_window_id(message.window_id()))
104 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowBecameActive));
105}
106
107void WindowServerConnection::handle(const Messages::WindowClient::WindowDeactivated& message)
108{
109#ifdef GEVENTLOOP_DEBUG
110 dbgprintf("(%d) WID=%d WindowDeactivated\n", getpid(), message.window_id());
111#endif
112 if (auto* window = Window::from_window_id(message.window_id()))
113 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowBecameInactive));
114}
115
116void WindowServerConnection::handle(const Messages::WindowClient::WindowCloseRequest& message)
117{
118 if (auto* window = Window::from_window_id(message.window_id()))
119 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowCloseRequest));
120}
121
122void WindowServerConnection::handle(const Messages::WindowClient::WindowEntered& message)
123{
124 if (auto* window = Window::from_window_id(message.window_id()))
125 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowEntered));
126}
127
128void WindowServerConnection::handle(const Messages::WindowClient::WindowLeft& message)
129{
130 if (auto* window = Window::from_window_id(message.window_id()))
131 Core::EventLoop::current().post_event(*window, make<Event>(Event::WindowLeft));
132}
133
134void WindowServerConnection::handle(const Messages::WindowClient::KeyDown& message)
135{
136#ifdef GEVENTLOOP_DEBUG
137 dbgprintf("WID=%d KeyDown character=0x%02x\n", message.window_id(), message.character());
138#endif
139 auto* window = Window::from_window_id(message.window_id());
140 if (!window)
141 return;
142
143 auto key_event = make<KeyEvent>(Event::KeyDown, message.key(), message.modifiers());
144 if (message.character() != '\0') {
145 char ch = message.character();
146 key_event->m_text = String(&ch, 1);
147 }
148
149 Action* action = nullptr;
150
151 if (auto* focused_widget = window->focused_widget())
152 action = focused_widget->action_for_key_event(*key_event);
153
154 if (!action)
155 action = window->action_for_key_event(*key_event);
156
157 if (!action)
158 action = Application::the().action_for_key_event(*key_event);
159
160 if (action && action->is_enabled()) {
161 action->activate();
162 return;
163 }
164 Core::EventLoop::current().post_event(*window, move(key_event));
165}
166
167void WindowServerConnection::handle(const Messages::WindowClient::KeyUp& message)
168{
169#ifdef GEVENTLOOP_DEBUG
170 dbgprintf("WID=%d KeyUp character=0x%02x\n", message.window_id(), message.character());
171#endif
172 auto* window = Window::from_window_id(message.window_id());
173 if (!window)
174 return;
175
176 auto key_event = make<KeyEvent>(Event::KeyUp, message.key(), message.modifiers());
177 if (message.character() != '\0') {
178 char ch = message.character();
179 key_event->m_text = String(&ch, 1);
180 }
181
182 Core::EventLoop::current().post_event(*window, move(key_event));
183}
184
185MouseButton to_gmousebutton(u32 button)
186{
187 switch (button) {
188 case 0:
189 return MouseButton::None;
190 case 1:
191 return MouseButton::Left;
192 case 2:
193 return MouseButton::Right;
194 case 4:
195 return MouseButton::Middle;
196 default:
197 ASSERT_NOT_REACHED();
198 break;
199 }
200}
201
202void WindowServerConnection::handle(const Messages::WindowClient::MouseDown& message)
203{
204#ifdef GEVENTLOOP_DEBUG
205 dbgprintf("WID=%d MouseDown %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
206#endif
207
208 if (auto* window = Window::from_window_id(message.window_id()))
209 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseDown, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
210}
211
212void WindowServerConnection::handle(const Messages::WindowClient::MouseUp& message)
213{
214#ifdef GEVENTLOOP_DEBUG
215 dbgprintf("WID=%d MouseUp %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
216#endif
217
218 if (auto* window = Window::from_window_id(message.window_id()))
219 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseUp, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
220}
221
222void WindowServerConnection::handle(const Messages::WindowClient::MouseMove& message)
223{
224#ifdef GEVENTLOOP_DEBUG
225 dbgprintf("WID=%d MouseMove %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
226#endif
227
228 if (auto* window = Window::from_window_id(message.window_id())) {
229 if (message.is_drag())
230 Core::EventLoop::current().post_event(*window, make<DragEvent>(Event::DragMove, message.mouse_position(), message.drag_data_type()));
231 else
232 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseMove, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
233 }
234}
235
236void WindowServerConnection::handle(const Messages::WindowClient::MouseDoubleClick& message)
237{
238#ifdef GEVENTLOOP_DEBUG
239 dbgprintf("WID=%d MouseDoubleClick %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
240#endif
241
242 if (auto* window = Window::from_window_id(message.window_id()))
243 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseDoubleClick, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
244}
245
246void WindowServerConnection::handle(const Messages::WindowClient::MouseWheel& message)
247{
248#ifdef GEVENTLOOP_DEBUG
249 dbgprintf("WID=%d MouseWheel %d,%d,%d\n", message.window_id(), message.mouse_position().x(), message.mouse_position().y(), message.wheel_delta();
250#endif
251
252 if (auto* window = Window::from_window_id(message.window_id()))
253 Core::EventLoop::current().post_event(*window, make<MouseEvent>(Event::MouseWheel, message.mouse_position(), message.buttons(), to_gmousebutton(message.button()), message.modifiers(), message.wheel_delta()));
254}
255
256void WindowServerConnection::handle(const Messages::WindowClient::MenuItemActivated& message)
257{
258 auto* menu = Menu::from_menu_id(message.menu_id());
259 if (!menu) {
260 dbgprintf("EventLoop received event for invalid menu ID %d\n", message.menu_id());
261 return;
262 }
263 if (auto* action = menu->action_at(message.identifier()))
264 action->activate(menu);
265}
266
267void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowStateChanged& message)
268{
269#ifdef GEVENTLOOP_DEBUG
270 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type);
271#endif
272 if (auto* window = Window::from_window_id(message.wm_id()))
273 Core::EventLoop::current().post_event(*window, make<WMWindowStateChangedEvent>(message.client_id(), message.window_id(), message.title(), message.rect(), message.is_active(), static_cast<WindowType>(message.window_type()), message.is_minimized()));
274}
275
276void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRectChanged& message)
277{
278#ifdef GEVENTLOOP_DEBUG
279 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type);
280#endif
281 if (auto* window = Window::from_window_id(message.wm_id()))
282 Core::EventLoop::current().post_event(*window, make<WMWindowRectChangedEvent>(message.client_id(), message.window_id(), message.rect()));
283}
284
285void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowIconBitmapChanged& message)
286{
287#ifdef GEVENTLOOP_DEBUG
288 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type);
289#endif
290 if (auto* window = Window::from_window_id(message.wm_id()))
291 Core::EventLoop::current().post_event(*window, make<WMWindowIconBitmapChangedEvent>(message.client_id(), message.window_id(), message.icon_buffer_id(), message.icon_size()));
292}
293
294void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRemoved& message)
295{
296#ifdef GEVENTLOOP_DEBUG
297 dbgprintf("EventLoop: handle_wm_event: %d\n", (int)event.type);
298#endif
299 if (auto* window = Window::from_window_id(message.wm_id()))
300 Core::EventLoop::current().post_event(*window, make<WMWindowRemovedEvent>(message.client_id(), message.window_id()));
301}
302
303void WindowServerConnection::handle(const Messages::WindowClient::ScreenRectChanged& message)
304{
305 Desktop::the().did_receive_screen_rect({}, message.rect());
306}
307
308void WindowServerConnection::handle(const Messages::WindowClient::ClipboardContentsChanged& message)
309{
310 Clipboard::the().did_receive_clipboard_contents_changed({}, message.content_type());
311}
312
313void WindowServerConnection::handle(const Messages::WindowClient::AsyncSetWallpaperFinished&)
314{
315 // This is handled manually by Desktop::set_wallpaper().
316}
317
318void WindowServerConnection::handle(const Messages::WindowClient::DragDropped& message)
319{
320 if (auto* window = Window::from_window_id(message.window_id())) {
321 auto mime_data = Core::MimeData::construct();
322 mime_data->set_data(message.data_type(), message.data().to_byte_buffer());
323 Core::EventLoop::current().post_event(*window, make<DropEvent>(message.mouse_position(), message.text(), mime_data));
324 }
325}
326
327void WindowServerConnection::handle(const Messages::WindowClient::DragAccepted&)
328{
329 DragOperation::notify_accepted({});
330}
331
332void WindowServerConnection::handle(const Messages::WindowClient::DragCancelled&)
333{
334 DragOperation::notify_cancelled({});
335}
336
337void WindowServerConnection::handle(const Messages::WindowClient::WindowStateChanged& message)
338{
339 if (auto* window = Window::from_window_id(message.window_id()))
340 window->notify_state_changed({}, message.minimized(), message.occluded());
341}
342
343void WindowServerConnection::handle(const Messages::WindowClient::DisplayLinkNotification&)
344{
345 DisplayLink::notify({});
346}
347
348}