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/Badge.h>
28#include <AK/SharedBuffer.h>
29#include <LibGfx/Bitmap.h>
30#include <LibGfx/SystemTheme.h>
31#include <WindowServer/AppletManager.h>
32#include <WindowServer/ClientConnection.h>
33#include <WindowServer/Clipboard.h>
34#include <WindowServer/Compositor.h>
35#include <WindowServer/EventLoop.h>
36#include <WindowServer/Menu.h>
37#include <WindowServer/MenuBar.h>
38#include <WindowServer/MenuItem.h>
39#include <WindowServer/Screen.h>
40#include <WindowServer/Window.h>
41#include <WindowServer/WindowClientEndpoint.h>
42#include <WindowServer/WindowManager.h>
43#include <WindowServer/WindowSwitcher.h>
44#include <errno.h>
45#include <serenity.h>
46#include <stdio.h>
47#include <unistd.h>
48
49namespace WindowServer {
50
51HashMap<int, NonnullRefPtr<ClientConnection>>* s_connections;
52
53void ClientConnection::for_each_client(Function<void(ClientConnection&)> callback)
54{
55 if (!s_connections)
56 return;
57 for (auto& it : *s_connections) {
58 callback(*it.value);
59 }
60}
61
62ClientConnection* ClientConnection::from_client_id(int client_id)
63{
64 if (!s_connections)
65 return nullptr;
66 auto it = s_connections->find(client_id);
67 if (it == s_connections->end())
68 return nullptr;
69 return (*it).value.ptr();
70}
71
72ClientConnection::ClientConnection(Core::LocalSocket& client_socket, int client_id)
73 : IPC::ClientConnection<WindowServerEndpoint>(*this, client_socket, client_id)
74{
75 if (!s_connections)
76 s_connections = new HashMap<int, NonnullRefPtr<ClientConnection>>;
77 s_connections->set(client_id, *this);
78}
79
80ClientConnection::~ClientConnection()
81{
82 MenuManager::the().close_all_menus_from_client({}, *this);
83 auto windows = move(m_windows);
84 for (auto& window : windows) {
85 window.value->detach_client({});
86 if (window.value->type() == WindowType::MenuApplet)
87 AppletManager::the().remove_applet(window.value);
88 }
89}
90
91void ClientConnection::die()
92{
93 deferred_invoke([this](auto&) {
94 s_connections->remove(client_id());
95 });
96}
97
98void ClientConnection::notify_about_new_screen_rect(const Gfx::Rect& rect)
99{
100 post_message(Messages::WindowClient::ScreenRectChanged(rect));
101}
102
103void ClientConnection::notify_about_clipboard_contents_changed()
104{
105 post_message(Messages::WindowClient::ClipboardContentsChanged(Clipboard::the().data_type()));
106}
107
108OwnPtr<Messages::WindowServer::CreateMenubarResponse> ClientConnection::handle(const Messages::WindowServer::CreateMenubar&)
109{
110 int menubar_id = m_next_menubar_id++;
111 auto menubar = make<MenuBar>(*this, menubar_id);
112 m_menubars.set(menubar_id, move(menubar));
113 return make<Messages::WindowServer::CreateMenubarResponse>(menubar_id);
114}
115
116OwnPtr<Messages::WindowServer::DestroyMenubarResponse> ClientConnection::handle(const Messages::WindowServer::DestroyMenubar& message)
117{
118 int menubar_id = message.menubar_id();
119 auto it = m_menubars.find(menubar_id);
120 if (it == m_menubars.end()) {
121 did_misbehave("DestroyMenubar: Bad menubar ID");
122 return nullptr;
123 }
124 auto& menubar = *(*it).value;
125 MenuManager::the().close_menubar(menubar);
126 m_menubars.remove(it);
127 return make<Messages::WindowServer::DestroyMenubarResponse>();
128}
129
130OwnPtr<Messages::WindowServer::CreateMenuResponse> ClientConnection::handle(const Messages::WindowServer::CreateMenu& message)
131{
132 int menu_id = m_next_menu_id++;
133 auto menu = Menu::construct(this, menu_id, message.menu_title());
134 m_menus.set(menu_id, move(menu));
135 return make<Messages::WindowServer::CreateMenuResponse>(menu_id);
136}
137
138OwnPtr<Messages::WindowServer::DestroyMenuResponse> ClientConnection::handle(const Messages::WindowServer::DestroyMenu& message)
139{
140 int menu_id = message.menu_id();
141 auto it = m_menus.find(menu_id);
142 if (it == m_menus.end()) {
143 did_misbehave("DestroyMenu: Bad menu ID");
144 return nullptr;
145 }
146 auto& menu = *(*it).value;
147 menu.close();
148 m_menus.remove(it);
149 remove_child(menu);
150 return make<Messages::WindowServer::DestroyMenuResponse>();
151}
152
153OwnPtr<Messages::WindowServer::SetApplicationMenubarResponse> ClientConnection::handle(const Messages::WindowServer::SetApplicationMenubar& message)
154{
155 int menubar_id = message.menubar_id();
156 auto it = m_menubars.find(menubar_id);
157 if (it == m_menubars.end()) {
158 did_misbehave("SetApplicationMenubar: Bad menubar ID");
159 return nullptr;
160 }
161 auto& menubar = *(*it).value;
162 m_app_menubar = menubar.make_weak_ptr();
163 WindowManager::the().notify_client_changed_app_menubar(*this);
164 return make<Messages::WindowServer::SetApplicationMenubarResponse>();
165}
166
167OwnPtr<Messages::WindowServer::AddMenuToMenubarResponse> ClientConnection::handle(const Messages::WindowServer::AddMenuToMenubar& message)
168{
169 int menubar_id = message.menubar_id();
170 int menu_id = message.menu_id();
171 auto it = m_menubars.find(menubar_id);
172 auto jt = m_menus.find(menu_id);
173 if (it == m_menubars.end()) {
174 did_misbehave("AddMenuToMenubar: Bad menubar ID");
175 return nullptr;
176 }
177 if (jt == m_menus.end()) {
178 did_misbehave("AddMenuToMenubar: Bad menu ID");
179 return nullptr;
180 }
181 auto& menubar = *(*it).value;
182 auto& menu = *(*jt).value;
183 menubar.add_menu(menu);
184 return make<Messages::WindowServer::AddMenuToMenubarResponse>();
185}
186
187OwnPtr<Messages::WindowServer::AddMenuItemResponse> ClientConnection::handle(const Messages::WindowServer::AddMenuItem& message)
188{
189 int menu_id = message.menu_id();
190 unsigned identifier = message.identifier();
191 auto it = m_menus.find(menu_id);
192 if (it == m_menus.end()) {
193 dbg() << "AddMenuItem: Bad menu ID: " << menu_id;
194 return nullptr;
195 }
196 auto& menu = *(*it).value;
197 auto menu_item = make<MenuItem>(menu, identifier, message.text(), message.shortcut(), message.enabled(), message.checkable(), message.checked());
198 if (message.icon_buffer_id() != -1) {
199 auto icon_buffer = SharedBuffer::create_from_shbuf_id(message.icon_buffer_id());
200 if (!icon_buffer)
201 return nullptr;
202 // FIXME: Verify that the icon buffer can accomodate a 16x16 bitmap view.
203 auto shared_icon = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, icon_buffer.release_nonnull(), { 16, 16 });
204 menu_item->set_icon(shared_icon);
205 }
206 menu_item->set_submenu_id(message.submenu_id());
207 menu_item->set_exclusive(message.exclusive());
208 menu.add_item(move(menu_item));
209 return make<Messages::WindowServer::AddMenuItemResponse>();
210}
211
212OwnPtr<Messages::WindowServer::PopupMenuResponse> ClientConnection::handle(const Messages::WindowServer::PopupMenu& message)
213{
214 int menu_id = message.menu_id();
215 auto position = message.screen_position();
216 auto it = m_menus.find(menu_id);
217 if (it == m_menus.end()) {
218 did_misbehave("PopupMenu: Bad menu ID");
219 return nullptr;
220 }
221 auto& menu = *(*it).value;
222 menu.popup(position);
223 return make<Messages::WindowServer::PopupMenuResponse>();
224}
225
226OwnPtr<Messages::WindowServer::DismissMenuResponse> ClientConnection::handle(const Messages::WindowServer::DismissMenu& message)
227{
228 int menu_id = message.menu_id();
229 auto it = m_menus.find(menu_id);
230 if (it == m_menus.end()) {
231 did_misbehave("DismissMenu: Bad menu ID");
232 return nullptr;
233 }
234 auto& menu = *(*it).value;
235 menu.close();
236 return make<Messages::WindowServer::DismissMenuResponse>();
237}
238
239OwnPtr<Messages::WindowServer::UpdateMenuItemResponse> ClientConnection::handle(const Messages::WindowServer::UpdateMenuItem& message)
240{
241 int menu_id = message.menu_id();
242 auto it = m_menus.find(menu_id);
243 if (it == m_menus.end()) {
244 did_misbehave("UpdateMenuItem: Bad menu ID");
245 return nullptr;
246 }
247 auto& menu = *(*it).value;
248 auto* menu_item = menu.item_with_identifier(message.identifier());
249 if (!menu_item) {
250 did_misbehave("UpdateMenuItem: Bad menu item identifier");
251 return nullptr;
252 }
253 menu_item->set_text(message.text());
254 menu_item->set_shortcut_text(message.shortcut());
255 menu_item->set_enabled(message.enabled());
256 menu_item->set_checkable(message.checkable());
257 if (message.checkable())
258 menu_item->set_checked(message.checked());
259 return make<Messages::WindowServer::UpdateMenuItemResponse>();
260}
261
262OwnPtr<Messages::WindowServer::AddMenuSeparatorResponse> ClientConnection::handle(const Messages::WindowServer::AddMenuSeparator& message)
263{
264 int menu_id = message.menu_id();
265 auto it = m_menus.find(menu_id);
266 if (it == m_menus.end()) {
267 did_misbehave("AddMenuSeparator: Bad menu ID");
268 return nullptr;
269 }
270 auto& menu = *(*it).value;
271 menu.add_item(make<MenuItem>(menu, MenuItem::Separator));
272 return make<Messages::WindowServer::AddMenuSeparatorResponse>();
273}
274
275OwnPtr<Messages::WindowServer::MoveWindowToFrontResponse> ClientConnection::handle(const Messages::WindowServer::MoveWindowToFront& message)
276{
277 auto it = m_windows.find(message.window_id());
278 if (it == m_windows.end()) {
279 did_misbehave("MoveWindowToFront: Bad window ID");
280 return nullptr;
281 }
282 WindowManager::the().move_to_front_and_make_active(*(*it).value);
283 return make<Messages::WindowServer::MoveWindowToFrontResponse>();
284}
285
286OwnPtr<Messages::WindowServer::SetFullscreenResponse> ClientConnection::handle(const Messages::WindowServer::SetFullscreen& message)
287{
288 auto it = m_windows.find(message.window_id());
289 if (it == m_windows.end()) {
290 did_misbehave("SetFullscreen: Bad window ID");
291 return nullptr;
292 }
293 it->value->set_fullscreen(message.fullscreen());
294 return make<Messages::WindowServer::SetFullscreenResponse>();
295}
296
297OwnPtr<Messages::WindowServer::SetWindowOpacityResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowOpacity& message)
298{
299 auto it = m_windows.find(message.window_id());
300 if (it == m_windows.end()) {
301 did_misbehave("SetWindowOpacity: Bad window ID");
302 return nullptr;
303 }
304 it->value->set_opacity(message.opacity());
305 return make<Messages::WindowServer::SetWindowOpacityResponse>();
306}
307
308void ClientConnection::handle(const Messages::WindowServer::AsyncSetWallpaper& message)
309{
310 Compositor::the().set_wallpaper(message.path(), [&](bool success) {
311 post_message(Messages::WindowClient::AsyncSetWallpaperFinished(success));
312 });
313}
314
315OwnPtr<Messages::WindowServer::SetBackgroundColorResponse> ClientConnection::handle(const Messages::WindowServer::SetBackgroundColor& message)
316{
317 Compositor::the().set_backgound_color(message.background_color());
318 return make<Messages::WindowServer::SetBackgroundColorResponse>();
319}
320
321OwnPtr<Messages::WindowServer::SetWallpaperModeResponse> ClientConnection::handle(const Messages::WindowServer::SetWallpaperMode& message)
322{
323 Compositor::the().set_wallpaper_mode(message.mode());
324 return make<Messages::WindowServer::SetWallpaperModeResponse>();
325}
326
327OwnPtr<Messages::WindowServer::GetWallpaperResponse> ClientConnection::handle(const Messages::WindowServer::GetWallpaper&)
328{
329 return make<Messages::WindowServer::GetWallpaperResponse>(Compositor::the().wallpaper_path());
330}
331
332OwnPtr<Messages::WindowServer::SetResolutionResponse> ClientConnection::handle(const Messages::WindowServer::SetResolution& message)
333{
334 return make<Messages::WindowServer::SetResolutionResponse>(WindowManager::the().set_resolution(message.resolution().width(), message.resolution().height()), WindowManager::the().resolution());
335}
336
337OwnPtr<Messages::WindowServer::SetWindowTitleResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowTitle& message)
338{
339 auto it = m_windows.find(message.window_id());
340 if (it == m_windows.end()) {
341 did_misbehave("SetWindowTitle: Bad window ID");
342 return nullptr;
343 }
344 it->value->set_title(message.title());
345 return make<Messages::WindowServer::SetWindowTitleResponse>();
346}
347
348OwnPtr<Messages::WindowServer::GetWindowTitleResponse> ClientConnection::handle(const Messages::WindowServer::GetWindowTitle& message)
349{
350 auto it = m_windows.find(message.window_id());
351 if (it == m_windows.end()) {
352 did_misbehave("GetWindowTitle: Bad window ID");
353 return nullptr;
354 }
355 return make<Messages::WindowServer::GetWindowTitleResponse>(it->value->title());
356}
357
358OwnPtr<Messages::WindowServer::SetWindowIconBitmapResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowIconBitmap& message)
359{
360 auto it = m_windows.find(message.window_id());
361 if (it == m_windows.end()) {
362 did_misbehave("SetWindowIconBitmap: Bad window ID");
363 return nullptr;
364 }
365 auto& window = *(*it).value;
366
367 if (message.icon().is_valid()) {
368 window.set_icon(*message.icon().bitmap());
369 } else {
370 window.set_default_icon();
371 }
372
373 window.frame().invalidate_title_bar();
374 WindowManager::the().tell_wm_listeners_window_icon_changed(window);
375 return make<Messages::WindowServer::SetWindowIconBitmapResponse>();
376}
377
378OwnPtr<Messages::WindowServer::SetWindowRectResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowRect& message)
379{
380 int window_id = message.window_id();
381 auto it = m_windows.find(window_id);
382 if (it == m_windows.end()) {
383 did_misbehave("SetWindowRect: Bad window ID");
384 return nullptr;
385 }
386 auto& window = *(*it).value;
387 if (window.is_fullscreen()) {
388 dbg() << "ClientConnection: Ignoring SetWindowRect request for fullscreen window";
389 return nullptr;
390 }
391 window.set_rect(message.rect());
392 window.request_update(message.rect());
393 return make<Messages::WindowServer::SetWindowRectResponse>();
394}
395
396OwnPtr<Messages::WindowServer::GetWindowRectResponse> ClientConnection::handle(const Messages::WindowServer::GetWindowRect& message)
397{
398 int window_id = message.window_id();
399 auto it = m_windows.find(window_id);
400 if (it == m_windows.end()) {
401 did_misbehave("GetWindowRect: Bad window ID");
402 return nullptr;
403 }
404 return make<Messages::WindowServer::GetWindowRectResponse>(it->value->rect());
405}
406
407OwnPtr<Messages::WindowServer::SetClipboardContentsResponse> ClientConnection::handle(const Messages::WindowServer::SetClipboardContents& message)
408{
409 auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.shbuf_id());
410 if (!shared_buffer) {
411 did_misbehave("SetClipboardContents: Bad shared buffer ID");
412 return nullptr;
413 }
414 Clipboard::the().set_data(*shared_buffer, message.content_size(), message.content_type());
415 return make<Messages::WindowServer::SetClipboardContentsResponse>();
416}
417
418OwnPtr<Messages::WindowServer::GetClipboardContentsResponse> ClientConnection::handle(const Messages::WindowServer::GetClipboardContents&)
419{
420 auto& clipboard = Clipboard::the();
421
422 i32 shbuf_id = -1;
423 if (clipboard.size()) {
424 // FIXME: Optimize case where an app is copy/pasting within itself.
425 // We can just reuse the SharedBuffer then, since it will have the same peer PID.
426 // It would be even nicer if a SharedBuffer could have an arbitrary number of clients..
427 RefPtr<SharedBuffer> shared_buffer = SharedBuffer::create_with_size(clipboard.size());
428 ASSERT(shared_buffer);
429 memcpy(shared_buffer->data(), clipboard.data(), clipboard.size());
430 shared_buffer->seal();
431 shared_buffer->share_with(client_pid());
432 shbuf_id = shared_buffer->shbuf_id();
433
434 // FIXME: This is a workaround for the fact that SharedBuffers will go away if neither side is retaining them.
435 // After we respond to GetClipboardContents, we have to wait for the client to ref the buffer on his side.
436 m_last_sent_clipboard_content = move(shared_buffer);
437 }
438 return make<Messages::WindowServer::GetClipboardContentsResponse>(shbuf_id, clipboard.size(), clipboard.data_type());
439}
440
441OwnPtr<Messages::WindowServer::CreateWindowResponse> ClientConnection::handle(const Messages::WindowServer::CreateWindow& message)
442{
443 int window_id = m_next_window_id++;
444 auto window = Window::construct(*this, (WindowType)message.type(), window_id, message.modal(), message.minimizable(), message.resizable(), message.fullscreen());
445 window->set_has_alpha_channel(message.has_alpha_channel());
446 window->set_title(message.title());
447 if (!message.fullscreen())
448 window->set_rect(message.rect());
449 window->set_show_titlebar(message.show_titlebar());
450 window->set_opacity(message.opacity());
451 window->set_size_increment(message.size_increment());
452 window->set_base_size(message.base_size());
453 window->invalidate();
454 if (window->type() == WindowType::MenuApplet)
455 AppletManager::the().add_applet(*window);
456 m_windows.set(window_id, move(window));
457 return make<Messages::WindowServer::CreateWindowResponse>(window_id);
458}
459
460OwnPtr<Messages::WindowServer::DestroyWindowResponse> ClientConnection::handle(const Messages::WindowServer::DestroyWindow& message)
461{
462 auto it = m_windows.find(message.window_id());
463 if (it == m_windows.end()) {
464 did_misbehave("DestroyWindow: Bad window ID");
465 return nullptr;
466 }
467 auto& window = *(*it).value;
468
469 if (window.type() == WindowType::MenuApplet)
470 AppletManager::the().remove_applet(window);
471
472 WindowManager::the().invalidate(window);
473 remove_child(window);
474 ASSERT(it->value.ptr() == &window);
475 m_windows.remove(message.window_id());
476
477 return make<Messages::WindowServer::DestroyWindowResponse>();
478}
479
480void ClientConnection::post_paint_message(Window& window, bool ignore_occlusion)
481{
482 auto rect_set = window.take_pending_paint_rects();
483 if (window.is_minimized() || (!ignore_occlusion && window.is_occluded()))
484 return;
485
486 post_message(Messages::WindowClient::Paint(window.window_id(), window.size(), rect_set.rects()));
487}
488
489void ClientConnection::handle(const Messages::WindowServer::InvalidateRect& message)
490{
491 auto it = m_windows.find(message.window_id());
492 if (it == m_windows.end()) {
493 did_misbehave("InvalidateRect: Bad window ID");
494 return;
495 }
496 auto& window = *(*it).value;
497 for (size_t i = 0; i < message.rects().size(); ++i)
498 window.request_update(message.rects()[i].intersected({ {}, window.size() }), message.ignore_occlusion());
499}
500
501void ClientConnection::handle(const Messages::WindowServer::DidFinishPainting& message)
502{
503 int window_id = message.window_id();
504 auto it = m_windows.find(window_id);
505 if (it == m_windows.end()) {
506 did_misbehave("DidFinishPainting: Bad window ID");
507 return;
508 }
509 auto& window = *(*it).value;
510 for (auto& rect : message.rects())
511 WindowManager::the().invalidate(window, rect);
512
513 WindowSwitcher::the().refresh_if_needed();
514}
515
516OwnPtr<Messages::WindowServer::SetWindowBackingStoreResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowBackingStore& message)
517{
518 int window_id = message.window_id();
519 auto it = m_windows.find(window_id);
520 if (it == m_windows.end()) {
521 did_misbehave("SetWindowBackingStore: Bad window ID");
522 return nullptr;
523 }
524 auto& window = *(*it).value;
525 if (window.last_backing_store() && window.last_backing_store()->shbuf_id() == message.shbuf_id()) {
526 window.swap_backing_stores();
527 } else {
528 auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.shbuf_id());
529 if (!shared_buffer)
530 return make<Messages::WindowServer::SetWindowBackingStoreResponse>();
531 auto backing_store = Gfx::Bitmap::create_with_shared_buffer(
532 message.has_alpha_channel() ? Gfx::BitmapFormat::RGBA32 : Gfx::BitmapFormat::RGB32,
533 *shared_buffer,
534 message.size());
535 window.set_backing_store(move(backing_store));
536 }
537
538 if (message.flush_immediately())
539 window.invalidate();
540
541 return make<Messages::WindowServer::SetWindowBackingStoreResponse>();
542}
543
544OwnPtr<Messages::WindowServer::SetGlobalCursorTrackingResponse> ClientConnection::handle(const Messages::WindowServer::SetGlobalCursorTracking& message)
545{
546 int window_id = message.window_id();
547 auto it = m_windows.find(window_id);
548 if (it == m_windows.end()) {
549 did_misbehave("SetGlobalCursorTracking: Bad window ID");
550 return nullptr;
551 }
552 it->value->set_global_cursor_tracking_enabled(message.enabled());
553 return make<Messages::WindowServer::SetGlobalCursorTrackingResponse>();
554}
555
556OwnPtr<Messages::WindowServer::SetWindowOverrideCursorResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowOverrideCursor& message)
557{
558 auto it = m_windows.find(message.window_id());
559 if (it == m_windows.end()) {
560 did_misbehave("SetWindowOverrideCursor: Bad window ID");
561 return nullptr;
562 }
563 auto& window = *(*it).value;
564 window.set_override_cursor(Cursor::create((StandardCursor)message.cursor_type()));
565 return make<Messages::WindowServer::SetWindowOverrideCursorResponse>();
566}
567
568OwnPtr<Messages::WindowServer::SetWindowHasAlphaChannelResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowHasAlphaChannel& message)
569{
570 auto it = m_windows.find(message.window_id());
571 if (it == m_windows.end()) {
572 did_misbehave("SetWindowHasAlphaChannel: Bad window ID");
573 return nullptr;
574 }
575 it->value->set_has_alpha_channel(message.has_alpha_channel());
576 return make<Messages::WindowServer::SetWindowHasAlphaChannelResponse>();
577}
578
579void ClientConnection::handle(const Messages::WindowServer::WM_SetActiveWindow& message)
580{
581 auto* client = ClientConnection::from_client_id(message.client_id());
582 if (!client) {
583 did_misbehave("WM_SetActiveWindow: Bad client ID");
584 return;
585 }
586 auto it = client->m_windows.find(message.window_id());
587 if (it == client->m_windows.end()) {
588 did_misbehave("WM_SetActiveWindow: Bad window ID");
589 return;
590 }
591 auto& window = *(*it).value;
592 window.set_minimized(false);
593 WindowManager::the().move_to_front_and_make_active(window);
594}
595
596void ClientConnection::handle(const Messages::WindowServer::WM_PopupWindowMenu& message)
597{
598 auto* client = ClientConnection::from_client_id(message.client_id());
599 if (!client) {
600 did_misbehave("WM_PopupWindowMenu: Bad client ID");
601 return;
602 }
603 auto it = client->m_windows.find(message.window_id());
604 if (it == client->m_windows.end()) {
605 did_misbehave("WM_PopupWindowMenu: Bad window ID");
606 return;
607 }
608 auto& window = *(*it).value;
609 window.popup_window_menu(message.screen_position());
610}
611
612void ClientConnection::handle(const Messages::WindowServer::WM_StartWindowResize& request)
613{
614 auto* client = ClientConnection::from_client_id(request.client_id());
615 if (!client) {
616 did_misbehave("WM_StartWindowResize: Bad client ID");
617 return;
618 }
619 auto it = client->m_windows.find(request.window_id());
620 if (it == client->m_windows.end()) {
621 did_misbehave("WM_StartWindowResize: Bad window ID");
622 return;
623 }
624 auto& window = *(*it).value;
625 // FIXME: We are cheating a bit here by using the current cursor location and hard-coding the left button.
626 // Maybe the client should be allowed to specify what initiated this request?
627 WindowManager::the().start_window_resize(window, Screen::the().cursor_location(), MouseButton::Left);
628}
629
630void ClientConnection::handle(const Messages::WindowServer::WM_SetWindowMinimized& message)
631{
632 auto* client = ClientConnection::from_client_id(message.client_id());
633 if (!client) {
634 did_misbehave("WM_SetWindowMinimized: Bad client ID");
635 return;
636 }
637 auto it = client->m_windows.find(message.window_id());
638 if (it == client->m_windows.end()) {
639 did_misbehave("WM_SetWindowMinimized: Bad window ID");
640 return;
641 }
642 auto& window = *(*it).value;
643 window.set_minimized(message.minimized());
644}
645
646OwnPtr<Messages::WindowServer::GreetResponse> ClientConnection::handle(const Messages::WindowServer::Greet&)
647{
648 return make<Messages::WindowServer::GreetResponse>(client_id(), Screen::the().rect(), Gfx::current_system_theme_buffer_id());
649}
650
651bool ClientConnection::is_showing_modal_window() const
652{
653 for (auto& it : m_windows) {
654 auto& window = *it.value;
655 if (window.is_visible() && window.is_modal())
656 return true;
657 }
658 return false;
659}
660
661void ClientConnection::handle(const Messages::WindowServer::WM_SetWindowTaskbarRect& message)
662{
663 auto* client = ClientConnection::from_client_id(message.client_id());
664 if (!client) {
665 did_misbehave("WM_SetWindowTaskbarRect: Bad client ID");
666 return;
667 }
668 auto it = client->m_windows.find(message.window_id());
669 if (it == client->m_windows.end()) {
670 did_misbehave("WM_SetWindowTaskbarRect: Bad window ID");
671 return;
672 }
673 auto& window = *(*it).value;
674 window.set_taskbar_rect(message.rect());
675}
676
677OwnPtr<Messages::WindowServer::StartDragResponse> ClientConnection::handle(const Messages::WindowServer::StartDrag& message)
678{
679 auto& wm = WindowManager::the();
680 if (wm.dnd_client())
681 return make<Messages::WindowServer::StartDragResponse>(false);
682
683 RefPtr<Gfx::Bitmap> bitmap;
684 if (message.bitmap_id() != -1) {
685 auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.bitmap_id());
686 ssize_t size_in_bytes = message.bitmap_size().area() * sizeof(Gfx::RGBA32);
687 if (size_in_bytes > shared_buffer->size()) {
688 did_misbehave("SetAppletBackingStore: Shared buffer is too small for applet size");
689 return nullptr;
690 }
691 bitmap = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *shared_buffer, message.bitmap_size());
692 }
693
694 wm.start_dnd_drag(*this, message.text(), bitmap, message.data_type(), message.data());
695 return make<Messages::WindowServer::StartDragResponse>(true);
696}
697
698OwnPtr<Messages::WindowServer::SetSystemMenuResponse> ClientConnection::handle(const Messages::WindowServer::SetSystemMenu& message)
699{
700 auto it = m_menus.find(message.menu_id());
701 if (it == m_menus.end()) {
702 did_misbehave("SetSystemMenu called with invalid menu ID");
703 return nullptr;
704 }
705
706 auto& menu = it->value;
707 MenuManager::the().set_system_menu(menu);
708 return make<Messages::WindowServer::SetSystemMenuResponse>();
709}
710
711OwnPtr<Messages::WindowServer::SetSystemThemeResponse> ClientConnection::handle(const Messages::WindowServer::SetSystemTheme& message)
712{
713 bool success = WindowManager::the().update_theme(message.theme_path(), message.theme_name());
714 return make<Messages::WindowServer::SetSystemThemeResponse>(success);
715}
716
717void ClientConnection::boost()
718{
719 // FIXME: Re-enable this when we have a solution for boosting.
720#if 0
721 if (set_process_boost(client_pid(), 10) < 0)
722 perror("boost: set_process_boost");
723#endif
724}
725
726void ClientConnection::deboost()
727{
728 // FIXME: Re-enable this when we have a solution for boosting.
729#if 0
730 if (set_process_boost(client_pid(), 0) < 0)
731 perror("deboost: set_process_boost");
732#endif
733}
734
735OwnPtr<Messages::WindowServer::SetWindowBaseSizeAndSizeIncrementResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowBaseSizeAndSizeIncrement& message)
736{
737 auto it = m_windows.find(message.window_id());
738 if (it == m_windows.end()) {
739 did_misbehave("SetWindowBaseSizeAndSizeIncrementResponse: Bad window ID");
740 return nullptr;
741 }
742
743 auto& window = *it->value;
744 window.set_base_size(message.base_size());
745 window.set_size_increment(message.size_increment());
746
747 return make<Messages::WindowServer::SetWindowBaseSizeAndSizeIncrementResponse>();
748}
749
750void ClientConnection::handle(const Messages::WindowServer::EnableDisplayLink&)
751{
752 m_has_display_link = true;
753}
754
755void ClientConnection::handle(const Messages::WindowServer::DisableDisplayLink&)
756{
757 m_has_display_link = false;
758}
759
760void ClientConnection::notify_display_link(Badge<Compositor>)
761{
762 if (!m_has_display_link)
763 return;
764
765 post_message(Messages::WindowClient::DisplayLinkNotification());
766}
767
768}