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