Serenity Operating System
at master 1434 lines 51 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Badge.h> 8#include <LibGfx/Bitmap.h> 9#include <LibGfx/StandardCursor.h> 10#include <LibGfx/SystemTheme.h> 11#include <WindowServer/AppletManager.h> 12#include <WindowServer/Compositor.h> 13#include <WindowServer/ConnectionFromClient.h> 14#include <WindowServer/Menu.h> 15#include <WindowServer/MenuItem.h> 16#include <WindowServer/Screen.h> 17#include <WindowServer/Window.h> 18#include <WindowServer/WindowClientEndpoint.h> 19#include <WindowServer/WindowManager.h> 20#include <WindowServer/WindowSwitcher.h> 21#include <errno.h> 22#include <stdio.h> 23#include <unistd.h> 24 25namespace WindowServer { 26 27HashMap<int, NonnullRefPtr<ConnectionFromClient>>* s_connections; 28 29void ConnectionFromClient::for_each_client(Function<void(ConnectionFromClient&)> callback) 30{ 31 if (!s_connections) 32 return; 33 for (auto& it : *s_connections) { 34 callback(*it.value); 35 } 36} 37 38ConnectionFromClient* ConnectionFromClient::from_client_id(int client_id) 39{ 40 if (!s_connections) 41 return nullptr; 42 auto it = s_connections->find(client_id); 43 if (it == s_connections->end()) 44 return nullptr; 45 return (*it).value.ptr(); 46} 47 48ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr<Core::LocalSocket> client_socket, int client_id) 49 : IPC::ConnectionFromClient<WindowClientEndpoint, WindowServerEndpoint>(*this, move(client_socket), client_id) 50{ 51 if (!s_connections) 52 s_connections = new HashMap<int, NonnullRefPtr<ConnectionFromClient>>; 53 s_connections->set(client_id, *this); 54 55 auto& wm = WindowManager::the(); 56 async_fast_greet(Screen::rects(), Screen::main().index(), wm.window_stack_rows(), wm.window_stack_columns(), Gfx::current_system_theme_buffer(), Gfx::FontDatabase::default_font_query(), Gfx::FontDatabase::fixed_width_font_query(), Gfx::FontDatabase::window_title_font_query(), wm.system_effects().effects(), client_id); 57} 58 59ConnectionFromClient::~ConnectionFromClient() 60{ 61 auto& wm = WindowManager::the(); 62 if (wm.dnd_client() == this) 63 wm.end_dnd_drag(); 64 65 if (m_has_display_link) 66 Compositor::the().decrement_display_link_count({}); 67 68 MenuManager::the().close_all_menus_from_client({}, *this); 69 auto windows = move(m_windows); 70 for (auto& window : windows) { 71 window.value->detach_client({}); 72 if (window.value->type() == WindowType::Applet) 73 AppletManager::the().remove_applet(window.value); 74 } 75 76 if (m_show_screen_number) 77 Compositor::the().decrement_show_screen_number({}); 78} 79 80void ConnectionFromClient::die() 81{ 82 deferred_invoke([this] { 83 s_connections->remove(client_id()); 84 }); 85} 86 87void ConnectionFromClient::notify_about_new_screen_rects() 88{ 89 auto& wm = WindowManager::the(); 90 async_screen_rects_changed(Screen::rects(), Screen::main().index(), wm.window_stack_rows(), wm.window_stack_columns()); 91} 92 93void ConnectionFromClient::create_menu(i32 menu_id, DeprecatedString const& menu_title) 94{ 95 auto menu = Menu::construct(this, menu_id, menu_title); 96 m_menus.set(menu_id, move(menu)); 97} 98 99void ConnectionFromClient::destroy_menu(i32 menu_id) 100{ 101 auto it = m_menus.find(menu_id); 102 if (it == m_menus.end()) { 103 did_misbehave("DestroyMenu: Bad menu ID"); 104 return; 105 } 106 auto& menu = *(*it).value; 107 menu.close(); 108 m_menus.remove(it); 109 remove_child(menu); 110} 111 112void ConnectionFromClient::add_menu(i32 window_id, i32 menu_id) 113{ 114 auto it = m_windows.find(window_id); 115 auto jt = m_menus.find(menu_id); 116 if (it == m_windows.end()) { 117 did_misbehave("AddMenu: Bad window ID"); 118 return; 119 } 120 if (jt == m_menus.end()) { 121 did_misbehave("AddMenu: Bad menu ID"); 122 return; 123 } 124 auto& window = *(*it).value; 125 auto& menu = *(*jt).value; 126 window.add_menu(menu); 127} 128 129void ConnectionFromClient::add_menu_item(i32 menu_id, i32 identifier, i32 submenu_id, 130 DeprecatedString const& text, bool enabled, bool visible, bool checkable, bool checked, bool is_default, 131 DeprecatedString const& shortcut, Gfx::ShareableBitmap const& icon, bool exclusive) 132{ 133 auto it = m_menus.find(menu_id); 134 if (it == m_menus.end()) { 135 dbgln("AddMenuItem: Bad menu ID: {}", menu_id); 136 return; 137 } 138 auto& menu = *(*it).value; 139 auto menu_item = make<MenuItem>(menu, identifier, text, shortcut, enabled, visible, checkable, checked); 140 if (is_default) 141 menu_item->set_default(true); 142 menu_item->set_icon(icon.bitmap()); 143 menu_item->set_submenu_id(submenu_id); 144 menu_item->set_exclusive(exclusive); 145 menu.add_item(move(menu_item)); 146} 147 148void ConnectionFromClient::popup_menu(i32 menu_id, Gfx::IntPoint screen_position, Gfx::IntRect const& button_rect) 149{ 150 auto position = screen_position; 151 auto it = m_menus.find(menu_id); 152 if (it == m_menus.end()) { 153 did_misbehave("PopupMenu: Bad menu ID"); 154 return; 155 } 156 auto& menu = *(*it).value; 157 if (!button_rect.is_empty()) 158 menu.open_button_menu(position, button_rect); 159 else 160 menu.popup(position); 161} 162 163void ConnectionFromClient::dismiss_menu(i32 menu_id) 164{ 165 auto it = m_menus.find(menu_id); 166 if (it == m_menus.end()) { 167 did_misbehave("DismissMenu: Bad menu ID"); 168 return; 169 } 170 auto& menu = *(*it).value; 171 menu.close(); 172} 173 174void ConnectionFromClient::update_menu_item(i32 menu_id, i32 identifier, [[maybe_unused]] i32 submenu_id, 175 DeprecatedString const& text, bool enabled, bool visible, bool checkable, bool checked, bool is_default, 176 DeprecatedString const& shortcut, Gfx::ShareableBitmap const& icon) 177{ 178 auto it = m_menus.find(menu_id); 179 if (it == m_menus.end()) { 180 did_misbehave("UpdateMenuItem: Bad menu ID"); 181 return; 182 } 183 auto& menu = *(*it).value; 184 auto* menu_item = menu.item_with_identifier(identifier); 185 if (!menu_item) { 186 did_misbehave("UpdateMenuItem: Bad menu item identifier"); 187 return; 188 } 189 menu_item->set_icon(icon.bitmap()); 190 menu_item->set_text(text); 191 menu_item->set_shortcut_text(shortcut); 192 menu_item->set_enabled(enabled); 193 menu_item->set_visible(visible); 194 menu_item->set_checkable(checkable); 195 menu_item->set_default(is_default); 196 if (checkable) 197 menu_item->set_checked(checked); 198 199 menu.redraw(*menu_item); 200} 201 202void ConnectionFromClient::remove_menu_item(i32 menu_id, i32 identifier) 203{ 204 auto it = m_menus.find(menu_id); 205 if (it == m_menus.end()) { 206 did_misbehave("RemoveMenuItem: Bad menu ID"); 207 return; 208 } 209 auto& menu = *(*it).value; 210 if (!menu.remove_item_with_identifier(identifier)) 211 did_misbehave("RemoveMenuItem: Bad menu item identifier"); 212} 213 214void ConnectionFromClient::flash_menubar_menu(i32 window_id, i32 menu_id) 215{ 216 auto itw = m_windows.find(window_id); 217 if (itw == m_windows.end()) { 218 did_misbehave("FlashMenubarMenu: Bad window ID"); 219 return; 220 } 221 auto& window = *(*itw).value; 222 223 auto itm = m_menus.find(menu_id); 224 if (itm == m_menus.end()) { 225 did_misbehave("FlashMenubarMenu: Bad menu ID"); 226 return; 227 } 228 auto& menu = *(*itm).value; 229 230 if (window.menubar().flash_menu(&menu)) { 231 window.frame().invalidate_menubar(); 232 233 if (m_flashed_menu_timer && m_flashed_menu_timer->is_active()) { 234 m_flashed_menu_timer->on_timeout(); 235 m_flashed_menu_timer->stop(); 236 } 237 238 m_flashed_menu_timer = Core::Timer::create_single_shot(75, [weak_window = window.make_weak_ptr<Window>()] { 239 if (!weak_window) 240 return; 241 weak_window->menubar().flash_menu(nullptr); 242 weak_window->frame().invalidate_menubar(); 243 }).release_value_but_fixme_should_propagate_errors(); 244 m_flashed_menu_timer->start(); 245 } else if (m_flashed_menu_timer) { 246 m_flashed_menu_timer->restart(); 247 } 248} 249 250void ConnectionFromClient::add_menu_separator(i32 menu_id) 251{ 252 auto it = m_menus.find(menu_id); 253 if (it == m_menus.end()) { 254 did_misbehave("AddMenuSeparator: Bad menu ID"); 255 return; 256 } 257 auto& menu = *(*it).value; 258 menu.add_item(make<MenuItem>(menu, MenuItem::Separator)); 259} 260 261void ConnectionFromClient::move_window_to_front(i32 window_id) 262{ 263 auto it = m_windows.find(window_id); 264 if (it == m_windows.end()) { 265 did_misbehave("MoveWindowToFront: Bad window ID"); 266 return; 267 } 268 WindowManager::the().move_to_front_and_make_active(*(*it).value); 269} 270 271void ConnectionFromClient::set_fullscreen(i32 window_id, bool fullscreen) 272{ 273 auto it = m_windows.find(window_id); 274 if (it == m_windows.end()) { 275 did_misbehave("SetFullscreen: Bad window ID"); 276 return; 277 } 278 it->value->set_fullscreen(fullscreen); 279} 280 281void ConnectionFromClient::set_frameless(i32 window_id, bool frameless) 282{ 283 auto it = m_windows.find(window_id); 284 if (it == m_windows.end()) { 285 did_misbehave("SetFrameless: Bad window ID"); 286 return; 287 } 288 it->value->set_frameless(frameless); 289 WindowManager::the().tell_wms_window_state_changed(*it->value); 290} 291 292void ConnectionFromClient::set_forced_shadow(i32 window_id, bool shadow) 293{ 294 auto it = m_windows.find(window_id); 295 if (it == m_windows.end()) { 296 did_misbehave("SetForcedShadow: Bad window ID"); 297 return; 298 } 299 it->value->set_forced_shadow(shadow); 300 it->value->invalidate(); 301 Compositor::the().invalidate_occlusions(); 302} 303 304void ConnectionFromClient::set_window_opacity(i32 window_id, float opacity) 305{ 306 auto it = m_windows.find(window_id); 307 if (it == m_windows.end()) { 308 did_misbehave("SetWindowOpacity: Bad window ID"); 309 return; 310 } 311 it->value->set_opacity(opacity); 312} 313 314Messages::WindowServer::SetWallpaperResponse ConnectionFromClient::set_wallpaper(Gfx::ShareableBitmap const& bitmap) 315{ 316 return Compositor::the().set_wallpaper(bitmap.bitmap()); 317} 318 319void ConnectionFromClient::set_background_color(DeprecatedString const& background_color) 320{ 321 Compositor::the().set_background_color(background_color); 322} 323 324void ConnectionFromClient::set_wallpaper_mode(DeprecatedString const& mode) 325{ 326 Compositor::the().set_wallpaper_mode(mode); 327} 328 329Messages::WindowServer::GetWallpaperResponse ConnectionFromClient::get_wallpaper() 330{ 331 return Compositor::the().wallpaper_bitmap()->to_shareable_bitmap(); 332} 333 334Messages::WindowServer::SetScreenLayoutResponse ConnectionFromClient::set_screen_layout(ScreenLayout const& screen_layout, bool save) 335{ 336 DeprecatedString error_msg; 337 bool success = WindowManager::the().set_screen_layout(ScreenLayout(screen_layout), save, error_msg); 338 return { success, move(error_msg) }; 339} 340 341Messages::WindowServer::GetScreenLayoutResponse ConnectionFromClient::get_screen_layout() 342{ 343 return { WindowManager::the().get_screen_layout() }; 344} 345 346Messages::WindowServer::SaveScreenLayoutResponse ConnectionFromClient::save_screen_layout() 347{ 348 DeprecatedString error_msg; 349 bool success = WindowManager::the().save_screen_layout(error_msg); 350 return { success, move(error_msg) }; 351} 352 353Messages::WindowServer::ApplyWorkspaceSettingsResponse ConnectionFromClient::apply_workspace_settings(u32 rows, u32 columns, bool save) 354{ 355 if (rows == 0 || columns == 0 || rows > WindowManager::max_window_stack_rows || columns > WindowManager::max_window_stack_columns) 356 return { false }; 357 358 return { WindowManager::the().apply_workspace_settings(rows, columns, save) }; 359} 360 361Messages::WindowServer::GetWorkspaceSettingsResponse ConnectionFromClient::get_workspace_settings() 362{ 363 auto& wm = WindowManager::the(); 364 return { (unsigned)wm.window_stack_rows(), (unsigned)wm.window_stack_columns(), WindowManager::max_window_stack_rows, WindowManager::max_window_stack_columns }; 365} 366 367void ConnectionFromClient::show_screen_numbers(bool show) 368{ 369 if (m_show_screen_number == show) 370 return; 371 m_show_screen_number = show; 372 if (show) 373 Compositor::the().increment_show_screen_number({}); 374 else 375 Compositor::the().decrement_show_screen_number({}); 376} 377 378void ConnectionFromClient::set_window_title(i32 window_id, DeprecatedString const& title) 379{ 380 auto it = m_windows.find(window_id); 381 if (it == m_windows.end()) { 382 did_misbehave("SetWindowTitle: Bad window ID"); 383 return; 384 } 385 it->value->set_title(title); 386} 387 388Messages::WindowServer::GetWindowTitleResponse ConnectionFromClient::get_window_title(i32 window_id) 389{ 390 auto it = m_windows.find(window_id); 391 if (it == m_windows.end()) { 392 did_misbehave("GetWindowTitle: Bad window ID"); 393 return nullptr; 394 } 395 return it->value->title(); 396} 397 398Messages::WindowServer::IsMaximizedResponse ConnectionFromClient::is_maximized(i32 window_id) 399{ 400 auto it = m_windows.find(window_id); 401 if (it == m_windows.end()) { 402 did_misbehave("IsMaximized: Bad window ID"); 403 return nullptr; 404 } 405 return it->value->is_maximized(); 406} 407 408void ConnectionFromClient::set_maximized(i32 window_id, bool maximized) 409{ 410 auto it = m_windows.find(window_id); 411 if (it == m_windows.end()) { 412 did_misbehave("SetMaximized: Bad window ID"); 413 return; 414 } 415 it->value->set_maximized(maximized); 416} 417 418Messages::WindowServer::IsMinimizedResponse ConnectionFromClient::is_minimized(i32 window_id) 419{ 420 auto it = m_windows.find(window_id); 421 if (it == m_windows.end()) { 422 did_misbehave("IsMinimized: Bad window ID"); 423 return nullptr; 424 } 425 return it->value->is_minimized(); 426} 427 428void ConnectionFromClient::set_minimized(i32 window_id, bool minimized) 429{ 430 auto it = m_windows.find(window_id); 431 if (it == m_windows.end()) { 432 did_misbehave("SetMinimized: Bad window ID"); 433 return; 434 } 435 it->value->set_minimized(minimized); 436} 437 438void ConnectionFromClient::set_window_icon_bitmap(i32 window_id, Gfx::ShareableBitmap const& icon) 439{ 440 auto it = m_windows.find(window_id); 441 if (it == m_windows.end()) { 442 did_misbehave("SetWindowIconBitmap: Bad window ID"); 443 return; 444 } 445 auto& window = *(*it).value; 446 447 if (icon.is_valid()) { 448 window.set_icon(*icon.bitmap()); 449 } else { 450 window.set_default_icon(); 451 } 452 453 window.frame().invalidate_titlebar(); 454 WindowManager::the().tell_wms_window_icon_changed(window); 455} 456 457Messages::WindowServer::SetWindowRectResponse ConnectionFromClient::set_window_rect(i32 window_id, Gfx::IntRect const& rect) 458{ 459 auto it = m_windows.find(window_id); 460 if (it == m_windows.end()) { 461 did_misbehave("SetWindowRect: Bad window ID"); 462 return nullptr; 463 } 464 auto& window = *(*it).value; 465 if (window.is_fullscreen()) { 466 dbgln("ConnectionFromClient: Ignoring SetWindowRect request for fullscreen window"); 467 return nullptr; 468 } 469 if (rect.width() > INT16_MAX || rect.height() > INT16_MAX) { 470 did_misbehave(DeprecatedString::formatted("SetWindowRect: Bad window sizing(width={}, height={}), dimension exceeds INT16_MAX", rect.width(), rect.height()).characters()); 471 return nullptr; 472 } 473 474 if (rect.location() != window.rect().location()) { 475 window.set_default_positioned(false); 476 } 477 auto new_rect = rect; 478 window.apply_minimum_size(new_rect); 479 window.set_rect(new_rect); 480 window.request_update(window.rect()); 481 return window.rect(); 482} 483 484Messages::WindowServer::GetWindowRectResponse ConnectionFromClient::get_window_rect(i32 window_id) 485{ 486 auto it = m_windows.find(window_id); 487 if (it == m_windows.end()) { 488 did_misbehave("GetWindowRect: Bad window ID"); 489 return nullptr; 490 } 491 return it->value->rect(); 492} 493 494static Gfx::IntSize calculate_minimum_size_for_window(Window const& window) 495{ 496 if (window.is_frameless()) 497 return { 0, 0 }; 498 499 // NOTE: Windows with a title bar have a minimum size enforced by the system, 500 // because we want to always keep their title buttons accessible. 501 if (window.type() == WindowType::Normal) { 502 auto palette = WindowManager::the().palette(); 503 504 int required_width = 0; 505 // Padding on left and right of window title content. 506 // FIXME: This seems like it should be defined in the theme. 507 required_width += 2 + 2; 508 // App icon 509 required_width += 16; 510 // Padding between icon and buttons 511 required_width += 2; 512 // Close button 513 required_width += palette.window_title_button_width(); 514 // Maximize button 515 if (window.is_resizable()) 516 required_width += palette.window_title_button_width(); 517 // Minimize button 518 if (window.is_minimizable()) 519 required_width += palette.window_title_button_width(); 520 521 return { required_width, 0 }; 522 } 523 524 return { 0, 0 }; 525} 526 527void ConnectionFromClient::set_window_minimum_size(i32 window_id, Gfx::IntSize size) 528{ 529 auto it = m_windows.find(window_id); 530 if (it == m_windows.end()) { 531 did_misbehave("SetWindowMinimumSize: Bad window ID"); 532 return; 533 } 534 auto& window = *(*it).value; 535 if (window.is_fullscreen()) { 536 dbgln("ConnectionFromClient: Ignoring SetWindowMinimumSize request for fullscreen window"); 537 return; 538 } 539 540 auto system_window_minimum_size = calculate_minimum_size_for_window(window); 541 window.set_minimum_size({ max(size.width(), system_window_minimum_size.width()), 542 max(size.height(), system_window_minimum_size.height()) }); 543 544 if (window.width() < window.minimum_size().width() || window.height() < window.minimum_size().height()) { 545 // New minimum size is larger than the current window size, resize accordingly. 546 auto new_rect = window.rect(); 547 bool did_size_clamp = window.apply_minimum_size(new_rect); 548 window.set_rect(new_rect); 549 window.request_update(window.rect()); 550 551 if (did_size_clamp) 552 window.refresh_client_size(); 553 } 554} 555 556Messages::WindowServer::GetWindowMinimumSizeResponse ConnectionFromClient::get_window_minimum_size(i32 window_id) 557{ 558 auto it = m_windows.find(window_id); 559 if (it == m_windows.end()) { 560 did_misbehave("GetWindowMinimumSize: Bad window ID"); 561 return nullptr; 562 } 563 return it->value->minimum_size(); 564} 565 566Messages::WindowServer::GetAppletRectOnScreenResponse ConnectionFromClient::get_applet_rect_on_screen(i32 window_id) 567{ 568 auto it = m_windows.find(window_id); 569 if (it == m_windows.end()) { 570 did_misbehave("GetAppletRectOnScreen: Bad window ID"); 571 return nullptr; 572 } 573 574 Gfx::IntRect applet_area_rect; 575 if (auto* applet_area_window = AppletManager::the().window()) 576 applet_area_rect = applet_area_window->rect(); 577 578 return it->value->rect_in_applet_area().translated(applet_area_rect.location()); 579} 580 581Window* ConnectionFromClient::window_from_id(i32 window_id) 582{ 583 auto it = m_windows.find(window_id); 584 if (it == m_windows.end()) 585 return nullptr; 586 return it->value.ptr(); 587} 588 589void ConnectionFromClient::create_window(i32 window_id, Gfx::IntRect const& rect, 590 bool auto_position, bool has_alpha_channel, bool minimizable, bool closeable, bool resizable, 591 bool fullscreen, bool frameless, bool forced_shadow, float opacity, 592 float alpha_hit_threshold, Gfx::IntSize base_size, Gfx::IntSize size_increment, 593 Gfx::IntSize minimum_size, Optional<Gfx::IntSize> const& resize_aspect_ratio, i32 type, i32 mode, 594 DeprecatedString const& title, i32 parent_window_id, Gfx::IntRect const& launch_origin_rect) 595{ 596 Window* parent_window = nullptr; 597 if (parent_window_id) { 598 parent_window = window_from_id(parent_window_id); 599 if (!parent_window) { 600 did_misbehave("CreateWindow with bad parent_window_id"); 601 return; 602 } 603 604 if (auto* blocker = parent_window->blocking_modal_window(); blocker && mode == (i32)WindowMode::Blocking) { 605 did_misbehave("CreateWindow with illegal mode: reciprocally blocked"); 606 return; 607 } 608 } 609 610 if (type < 0 || type >= (i32)WindowType::_Count) { 611 did_misbehave("CreateWindow with a bad type"); 612 return; 613 } 614 615 if (mode < 0 || mode >= (i32)WindowMode::_Count) { 616 did_misbehave("CreateWindow with a bad mode"); 617 return; 618 } 619 620 if (m_windows.contains(window_id)) { 621 did_misbehave("CreateWindow with already-used window ID"); 622 return; 623 } 624 625 auto window = Window::construct(*this, (WindowType)type, (WindowMode)mode, window_id, minimizable, closeable, frameless, resizable, fullscreen, parent_window); 626 627 window->set_forced_shadow(forced_shadow); 628 629 if (!launch_origin_rect.is_empty()) 630 window->start_launch_animation(launch_origin_rect); 631 632 window->set_has_alpha_channel(has_alpha_channel); 633 window->set_title(title); 634 if (!fullscreen) { 635 Gfx::IntRect new_rect = rect; 636 if (auto_position && window->is_movable()) { 637 new_rect = { WindowManager::the().get_recommended_window_position({ 100, 100 }), rect.size() }; 638 window->set_default_positioned(true); 639 } 640 auto system_window_minimum_size = calculate_minimum_size_for_window(window); 641 window->set_minimum_size({ max(minimum_size.width(), system_window_minimum_size.width()), 642 max(minimum_size.height(), system_window_minimum_size.height()) }); 643 bool did_size_clamp = window->apply_minimum_size(new_rect); 644 window->set_rect(new_rect); 645 646 if (did_size_clamp) 647 window->refresh_client_size(); 648 } 649 if (window->type() == WindowType::Desktop) { 650 window->set_rect(Screen::bounding_rect()); 651 window->recalculate_rect(); 652 } 653 window->set_opacity(opacity); 654 window->set_alpha_hit_threshold(alpha_hit_threshold); 655 window->set_size_increment(size_increment); 656 window->set_base_size(base_size); 657 if (resize_aspect_ratio.has_value() && !resize_aspect_ratio.value().is_empty()) 658 window->set_resize_aspect_ratio(resize_aspect_ratio); 659 window->invalidate(true, true); 660 if (window->type() == WindowType::Applet) 661 AppletManager::the().add_applet(*window); 662 m_windows.set(window_id, move(window)); 663} 664 665void ConnectionFromClient::destroy_window(Window& window, Vector<i32>& destroyed_window_ids) 666{ 667 for (auto& child_window : window.child_windows()) { 668 if (!child_window) 669 continue; 670 VERIFY(child_window->window_id() != window.window_id()); 671 destroy_window(*child_window, destroyed_window_ids); 672 } 673 674 destroyed_window_ids.append(window.window_id()); 675 676 if (window.type() == WindowType::Applet) 677 AppletManager::the().remove_applet(window); 678 679 window.destroy(); 680 remove_child(window); 681 m_windows.remove(window.window_id()); 682} 683 684Messages::WindowServer::DestroyWindowResponse ConnectionFromClient::destroy_window(i32 window_id) 685{ 686 auto it = m_windows.find(window_id); 687 if (it == m_windows.end()) { 688 did_misbehave("DestroyWindow: Bad window ID"); 689 return nullptr; 690 } 691 auto& window = *(*it).value; 692 Vector<i32> destroyed_window_ids; 693 destroy_window(window, destroyed_window_ids); 694 return destroyed_window_ids; 695} 696 697void ConnectionFromClient::post_paint_message(Window& window, bool ignore_occlusion) 698{ 699 auto rect_set = window.take_pending_paint_rects(); 700 if (window.is_minimized() || (!ignore_occlusion && window.is_occluded())) 701 return; 702 703 async_paint(window.window_id(), window.size(), rect_set.rects()); 704} 705 706void ConnectionFromClient::invalidate_rect(i32 window_id, Vector<Gfx::IntRect> const& rects, bool ignore_occlusion) 707{ 708 auto it = m_windows.find(window_id); 709 if (it == m_windows.end()) { 710 did_misbehave("InvalidateRect: Bad window ID"); 711 return; 712 } 713 auto& window = *(*it).value; 714 for (size_t i = 0; i < rects.size(); ++i) 715 window.request_update(rects[i].intersected(Gfx::Rect { {}, window.size() }), ignore_occlusion); 716} 717 718void ConnectionFromClient::did_finish_painting(i32 window_id, Vector<Gfx::IntRect> const& rects) 719{ 720 auto it = m_windows.find(window_id); 721 if (it == m_windows.end()) { 722 did_misbehave("DidFinishPainting: Bad window ID"); 723 return; 724 } 725 auto& window = *(*it).value; 726 for (auto& rect : rects) 727 window.invalidate(rect); 728 if (window.has_alpha_channel() && window.alpha_hit_threshold() > 0.0f) 729 WindowManager::the().reevaluate_hover_state_for_window(&window); 730 731 WindowSwitcher::the().refresh_if_needed(); 732} 733 734void ConnectionFromClient::set_window_backing_store(i32 window_id, [[maybe_unused]] i32 bpp, 735 [[maybe_unused]] i32 pitch, IPC::File const& anon_file, i32 serial, bool has_alpha_channel, 736 Gfx::IntSize size, Gfx::IntSize visible_size, bool flush_immediately) 737{ 738 auto it = m_windows.find(window_id); 739 if (it == m_windows.end()) { 740 did_misbehave("SetWindowBackingStore: Bad window ID"); 741 return; 742 } 743 auto& window = *(*it).value; 744 if (window.last_backing_store() && window.last_backing_store_serial() == serial) { 745 window.swap_backing_stores(); 746 } else { 747 // FIXME: Plumb scale factor here eventually. 748 auto buffer_or_error = Core::AnonymousBuffer::create_from_anon_fd(anon_file.take_fd(), pitch * size.height()); 749 if (buffer_or_error.is_error()) { 750 did_misbehave("SetWindowBackingStore: Failed to create anonymous buffer for window backing store"); 751 return; 752 } 753 auto backing_store_or_error = Gfx::Bitmap::create_with_anonymous_buffer( 754 has_alpha_channel ? Gfx::BitmapFormat::BGRA8888 : Gfx::BitmapFormat::BGRx8888, 755 buffer_or_error.release_value(), 756 size, 757 1, 758 {}); 759 if (backing_store_or_error.is_error()) { 760 did_misbehave(""); 761 } 762 window.set_backing_store(backing_store_or_error.release_value(), serial); 763 } 764 window.set_backing_store_visible_size(visible_size); 765 766 if (flush_immediately) 767 window.invalidate(false); 768} 769 770void ConnectionFromClient::set_global_mouse_tracking(bool enabled) 771{ 772 m_does_global_mouse_tracking = enabled; 773} 774 775void ConnectionFromClient::set_window_cursor(i32 window_id, i32 cursor_type) 776{ 777 auto it = m_windows.find(window_id); 778 if (it == m_windows.end()) { 779 did_misbehave("SetWindowCursor: Bad window ID"); 780 return; 781 } 782 auto& window = *(*it).value; 783 if (cursor_type < 0 || cursor_type >= (i32)Gfx::StandardCursor::__Count) { 784 did_misbehave("SetWindowCursor: Bad cursor type"); 785 return; 786 } 787 window.set_cursor(Cursor::create((Gfx::StandardCursor)cursor_type)); 788 if (&window == WindowManager::the().hovered_window()) 789 Compositor::the().invalidate_cursor(); 790} 791 792void ConnectionFromClient::set_window_custom_cursor(i32 window_id, Gfx::ShareableBitmap const& cursor) 793{ 794 auto it = m_windows.find(window_id); 795 if (it == m_windows.end()) { 796 did_misbehave("SetWindowCustomCursor: Bad window ID"); 797 return; 798 } 799 800 auto& window = *(*it).value; 801 if (!cursor.is_valid()) { 802 did_misbehave("SetWindowCustomCursor: Bad cursor"); 803 return; 804 } 805 806 window.set_cursor(Cursor::create(*cursor.bitmap(), 1)); 807 Compositor::the().invalidate_cursor(); 808} 809 810void ConnectionFromClient::set_window_has_alpha_channel(i32 window_id, bool has_alpha_channel) 811{ 812 auto it = m_windows.find(window_id); 813 if (it == m_windows.end()) { 814 did_misbehave("SetWindowHasAlphaChannel: Bad window ID"); 815 return; 816 } 817 it->value->set_has_alpha_channel(has_alpha_channel); 818} 819 820void ConnectionFromClient::set_window_alpha_hit_threshold(i32 window_id, float threshold) 821{ 822 auto it = m_windows.find(window_id); 823 if (it == m_windows.end()) { 824 did_misbehave("SetWindowAlphaHitThreshold: Bad window ID"); 825 return; 826 } 827 it->value->set_alpha_hit_threshold(threshold); 828} 829 830void ConnectionFromClient::start_window_resize(i32 window_id, i32 resize_direction) 831{ 832 auto it = m_windows.find(window_id); 833 if (it == m_windows.end()) { 834 did_misbehave("WM_StartWindowResize: Bad window ID"); 835 return; 836 } 837 if (resize_direction < 0 || resize_direction >= (i32)ResizeDirection::__Count) { 838 did_misbehave("WM_StartWindowResize: Bad resize direction"); 839 return; 840 } 841 auto& window = *(*it).value; 842 if (!window.is_resizable()) { 843 dbgln("Client wants to start resizing a non-resizable window"); 844 return; 845 } 846 // FIXME: We are cheating a bit here by using the current cursor location and hard-coding the left button. 847 // Maybe the client should be allowed to specify what initiated this request? 848 WindowManager::the().start_window_resize(window, ScreenInput::the().cursor_location(), MouseButton::Primary, (ResizeDirection)resize_direction); 849} 850 851Messages::WindowServer::StartDragResponse ConnectionFromClient::start_drag(DeprecatedString const& text, HashMap<DeprecatedString, ByteBuffer> const& mime_data, Gfx::ShareableBitmap const& drag_bitmap) 852{ 853 auto& wm = WindowManager::the(); 854 if (wm.dnd_client() || !(wm.last_processed_buttons() & MouseButton::Primary)) 855 return false; 856 857 wm.start_dnd_drag(*this, text, drag_bitmap.bitmap(), Core::MimeData::construct(mime_data)); 858 return true; 859} 860 861void ConnectionFromClient::set_accepts_drag(bool accepts) 862{ 863 auto& wm = WindowManager::the(); 864 VERIFY(wm.dnd_client()); 865 wm.set_accepts_drag(accepts); 866} 867 868Messages::WindowServer::SetSystemThemeResponse ConnectionFromClient::set_system_theme(DeprecatedString const& theme_path, DeprecatedString const& theme_name, bool keep_desktop_background, Optional<DeprecatedString> const& color_scheme_path) 869{ 870 bool success = WindowManager::the().update_theme(theme_path, theme_name, keep_desktop_background, color_scheme_path); 871 return success; 872} 873 874Messages::WindowServer::GetSystemThemeResponse ConnectionFromClient::get_system_theme() 875{ 876 return g_config->read_entry("Theme", "Name"); 877} 878 879Messages::WindowServer::SetSystemThemeOverrideResponse ConnectionFromClient::set_system_theme_override(Core::AnonymousBuffer const& theme_override) 880{ 881 bool success = WindowManager::the().set_theme_override(theme_override); 882 return success; 883} 884 885Messages::WindowServer::GetSystemThemeOverrideResponse ConnectionFromClient::get_system_theme_override() 886{ 887 return WindowManager::the().get_theme_override(); 888} 889 890void ConnectionFromClient::clear_system_theme_override() 891{ 892 WindowManager::the().clear_theme_override(); 893} 894 895Messages::WindowServer::IsSystemThemeOverriddenResponse ConnectionFromClient::is_system_theme_overridden() 896{ 897 return WindowManager::the().is_theme_overridden(); 898} 899 900Messages::WindowServer::GetPreferredColorSchemeResponse ConnectionFromClient::get_preferred_color_scheme() 901{ 902 return WindowManager::the().get_preferred_color_scheme(); 903} 904 905void ConnectionFromClient::apply_cursor_theme(DeprecatedString const& name) 906{ 907 WindowManager::the().apply_cursor_theme(name); 908} 909 910void ConnectionFromClient::set_cursor_highlight_radius(int radius) 911{ 912 WindowManager::the().set_cursor_highlight_radius(radius); 913} 914 915Messages::WindowServer::GetCursorHighlightRadiusResponse ConnectionFromClient::get_cursor_highlight_radius() 916{ 917 return WindowManager::the().cursor_highlight_radius(); 918} 919 920void ConnectionFromClient::set_cursor_highlight_color(Gfx::Color color) 921{ 922 WindowManager::the().set_cursor_highlight_color(color); 923} 924 925Messages::WindowServer::GetCursorHighlightColorResponse ConnectionFromClient::get_cursor_highlight_color() 926{ 927 return WindowManager::the().cursor_highlight_color(); 928} 929 930Messages::WindowServer::GetCursorThemeResponse ConnectionFromClient::get_cursor_theme() 931{ 932 return g_config->read_entry("Mouse", "CursorTheme"); 933} 934 935Messages::WindowServer::SetSystemFontsResponse ConnectionFromClient::set_system_fonts(DeprecatedString const& default_font_query, DeprecatedString const& fixed_width_font_query, DeprecatedString const& window_title_font_query) 936{ 937 if (!Gfx::FontDatabase::the().get_by_name(default_font_query) 938 || !Gfx::FontDatabase::the().get_by_name(fixed_width_font_query)) { 939 dbgln("Received unusable font queries: '{}' and '{}'", default_font_query, fixed_width_font_query); 940 return false; 941 } 942 943 dbgln("Updating fonts: '{}' and '{}'", default_font_query, fixed_width_font_query); 944 945 Gfx::FontDatabase::set_default_font_query(default_font_query); 946 Gfx::FontDatabase::set_fixed_width_font_query(fixed_width_font_query); 947 Gfx::FontDatabase::set_window_title_font_query(window_title_font_query); 948 949 ConnectionFromClient::for_each_client([&](auto& client) { 950 client.async_update_system_fonts(default_font_query, fixed_width_font_query, window_title_font_query); 951 }); 952 953 WindowManager::the().invalidate_after_theme_or_font_change(); 954 955 g_config->write_entry("Fonts", "Default", default_font_query); 956 g_config->write_entry("Fonts", "FixedWidth", fixed_width_font_query); 957 g_config->write_entry("Fonts", "WindowTitle", window_title_font_query); 958 959 return !g_config->sync().is_error(); 960} 961 962void ConnectionFromClient::set_system_effects(Vector<bool> const& effects, u8 geometry) 963{ 964 WindowManager::the().apply_system_effects(effects, static_cast<ShowGeometry>(geometry)); 965 ConnectionFromClient::for_each_client([&](auto& client) { 966 client.async_update_system_effects(effects); 967 }); 968} 969 970void ConnectionFromClient::set_window_base_size_and_size_increment(i32 window_id, Gfx::IntSize base_size, Gfx::IntSize size_increment) 971{ 972 auto it = m_windows.find(window_id); 973 if (it == m_windows.end()) { 974 did_misbehave("SetWindowBaseSizeAndSizeIncrementResponse: Bad window ID"); 975 return; 976 } 977 978 auto& window = *it->value; 979 window.set_base_size(base_size); 980 window.set_size_increment(size_increment); 981} 982 983void ConnectionFromClient::set_window_resize_aspect_ratio(i32 window_id, Optional<Gfx::IntSize> const& resize_aspect_ratio) 984{ 985 auto it = m_windows.find(window_id); 986 if (it == m_windows.end()) { 987 did_misbehave("SetWindowResizeAspectRatioResponse: Bad window ID"); 988 return; 989 } 990 991 auto& window = *it->value; 992 window.set_resize_aspect_ratio(resize_aspect_ratio); 993} 994 995void ConnectionFromClient::enable_display_link() 996{ 997 if (m_has_display_link) 998 return; 999 m_has_display_link = true; 1000 Compositor::the().increment_display_link_count({}); 1001} 1002 1003void ConnectionFromClient::disable_display_link() 1004{ 1005 if (!m_has_display_link) 1006 return; 1007 m_has_display_link = false; 1008 Compositor::the().decrement_display_link_count({}); 1009} 1010 1011void ConnectionFromClient::notify_display_link(Badge<Compositor>) 1012{ 1013 if (!m_has_display_link) 1014 return; 1015 1016 async_display_link_notification(); 1017} 1018 1019void ConnectionFromClient::set_window_progress(i32 window_id, Optional<i32> const& progress) 1020{ 1021 auto it = m_windows.find(window_id); 1022 if (it == m_windows.end()) { 1023 did_misbehave("SetWindowProgress with bad window ID"); 1024 return; 1025 } 1026 it->value->set_progress(progress); 1027} 1028 1029void ConnectionFromClient::refresh_system_theme() 1030{ 1031 // Post the client an UpdateSystemTheme message to refresh its theme. 1032 async_update_system_theme(Gfx::current_system_theme_buffer()); 1033} 1034 1035void ConnectionFromClient::pong() 1036{ 1037 m_ping_timer = nullptr; 1038 set_unresponsive(false); 1039} 1040 1041void ConnectionFromClient::set_global_cursor_position(Gfx::IntPoint position) 1042{ 1043 if (!Screen::main().rect().contains(position)) { 1044 did_misbehave("SetGlobalCursorPosition with bad position"); 1045 return; 1046 } 1047 if (position != ScreenInput::the().cursor_location()) { 1048 ScreenInput::the().set_cursor_location(position); 1049 Compositor::the().invalidate_cursor(); 1050 } 1051} 1052 1053Messages::WindowServer::GetGlobalCursorPositionResponse ConnectionFromClient::get_global_cursor_position() 1054{ 1055 return ScreenInput::the().cursor_location(); 1056} 1057 1058void ConnectionFromClient::set_mouse_acceleration(float factor) 1059{ 1060 double dbl_factor = (double)factor; 1061 if (dbl_factor < mouse_accel_min || dbl_factor > mouse_accel_max) { 1062 did_misbehave("SetMouseAcceleration with bad acceleration factor"); 1063 return; 1064 } 1065 WindowManager::the().set_acceleration_factor(dbl_factor); 1066} 1067 1068Messages::WindowServer::GetMouseAccelerationResponse ConnectionFromClient::get_mouse_acceleration() 1069{ 1070 return ScreenInput::the().acceleration_factor(); 1071} 1072 1073void ConnectionFromClient::set_scroll_step_size(u32 step_size) 1074{ 1075 if (step_size < scroll_step_size_min) { 1076 did_misbehave("SetScrollStepSize with bad scroll step size"); 1077 return; 1078 } 1079 WindowManager::the().set_scroll_step_size(step_size); 1080} 1081 1082Messages::WindowServer::GetScrollStepSizeResponse ConnectionFromClient::get_scroll_step_size() 1083{ 1084 return ScreenInput::the().scroll_step_size(); 1085} 1086 1087void ConnectionFromClient::set_double_click_speed(i32 speed) 1088{ 1089 if (speed < double_click_speed_min || speed > double_click_speed_max) { 1090 did_misbehave("SetDoubleClickSpeed with bad speed"); 1091 return; 1092 } 1093 WindowManager::the().set_double_click_speed(speed); 1094} 1095 1096Messages::WindowServer::GetDoubleClickSpeedResponse ConnectionFromClient::get_double_click_speed() 1097{ 1098 return WindowManager::the().double_click_speed(); 1099} 1100 1101void ConnectionFromClient::set_mouse_buttons_switched(bool switched) 1102{ 1103 WindowManager::the().set_mouse_buttons_switched(switched); 1104} 1105 1106Messages::WindowServer::AreMouseButtonsSwitchedResponse ConnectionFromClient::are_mouse_buttons_switched() 1107{ 1108 return WindowManager::the().are_mouse_buttons_switched(); 1109} 1110 1111void ConnectionFromClient::set_natural_scroll(bool inverted) 1112{ 1113 WindowManager::the().set_natural_scroll(inverted); 1114} 1115 1116Messages::WindowServer::IsNaturalScrollResponse ConnectionFromClient::is_natural_scroll() 1117{ 1118 return WindowManager::the().is_natural_scroll(); 1119} 1120 1121void ConnectionFromClient::set_unresponsive(bool unresponsive) 1122{ 1123 if (m_unresponsive == unresponsive) 1124 return; 1125 m_unresponsive = unresponsive; 1126 for (auto& it : m_windows) { 1127 auto& window = *it.value; 1128 window.invalidate(true, true); 1129 if (unresponsive) { 1130 window.set_cursor_override(WindowManager::the().wait_cursor()); 1131 } else { 1132 window.remove_cursor_override(); 1133 } 1134 } 1135 Compositor::the().invalidate_cursor(); 1136} 1137 1138void ConnectionFromClient::may_have_become_unresponsive() 1139{ 1140 async_ping(); 1141 m_ping_timer = Core::Timer::create_single_shot(1000, [this] { 1142 set_unresponsive(true); 1143 }).release_value_but_fixme_should_propagate_errors(); 1144 m_ping_timer->start(); 1145} 1146 1147void ConnectionFromClient::did_become_responsive() 1148{ 1149 set_unresponsive(false); 1150} 1151 1152Messages::WindowServer::GetScreenBitmapResponse ConnectionFromClient::get_screen_bitmap(Optional<Gfx::IntRect> const& rect, Optional<u32> const& screen_index) 1153{ 1154 if (screen_index.has_value()) { 1155 auto* screen = Screen::find_by_index(screen_index.value()); 1156 if (!screen) { 1157 dbgln("get_screen_bitmap: Screen {} does not exist!", screen_index.value()); 1158 return { Gfx::ShareableBitmap() }; 1159 } 1160 if (rect.has_value()) { 1161 auto bitmap_or_error = Compositor::the().front_bitmap_for_screenshot({}, *screen).cropped(rect.value()); 1162 if (bitmap_or_error.is_error()) { 1163 dbgln("get_screen_bitmap: Failed to crop screenshot: {}", bitmap_or_error.error()); 1164 return { Gfx::ShareableBitmap() }; 1165 } 1166 return bitmap_or_error.release_value()->to_shareable_bitmap(); 1167 } 1168 auto& bitmap = Compositor::the().front_bitmap_for_screenshot({}, *screen); 1169 return bitmap.to_shareable_bitmap(); 1170 } 1171 // TODO: Mixed scale setups at what scale? Lowest? Highest? Configurable? 1172 auto bitmap_size = rect.value_or(Screen::bounding_rect()).size(); 1173 if (auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, bitmap_size, 1); !bitmap_or_error.is_error()) { 1174 auto bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors(); 1175 Gfx::Painter painter(*bitmap); 1176 Screen::for_each([&](auto& screen) { 1177 auto screen_rect = screen.rect(); 1178 if (rect.has_value() && !rect.value().intersects(screen_rect)) 1179 return IterationDecision::Continue; 1180 auto src_rect = rect.has_value() ? rect.value().intersected(screen_rect) : screen_rect; 1181 VERIFY(Screen::bounding_rect().contains(src_rect)); 1182 auto& screen_bitmap = Compositor::the().front_bitmap_for_screenshot({}, screen); 1183 // TODO: painter does *not* support down-sampling!!! 1184 painter.blit(screen_rect.location(), screen_bitmap, src_rect.translated(-screen_rect.location()), 1.0f, false); 1185 return IterationDecision::Continue; 1186 }); 1187 return bitmap->to_shareable_bitmap(); 1188 } 1189 return { Gfx::ShareableBitmap() }; 1190} 1191 1192Messages::WindowServer::GetScreenBitmapAroundCursorResponse ConnectionFromClient::get_screen_bitmap_around_cursor(Gfx::IntSize size) 1193{ 1194 return get_screen_bitmap_around_location(size, ScreenInput::the().cursor_location()).bitmap(); 1195} 1196 1197Messages::WindowServer::GetScreenBitmapAroundLocationResponse ConnectionFromClient::get_screen_bitmap_around_location(Gfx::IntSize size, Gfx::IntPoint location) 1198{ 1199 // TODO: Mixed scale setups at what scale? Lowest? Highest? Configurable? 1200 Gfx::Rect rect { location.x() - (size.width() / 2), location.y() - (size.height() / 2), size.width(), size.height() }; 1201 1202 // Recompose the screen to make sure the cursor is painted in the location we think it is. 1203 // FIXME: This is rather wasteful. We can probably think of a way to avoid this. 1204 Compositor::the().compose(); 1205 1206 // Check if we need to compose from multiple screens. If not we can take a fast path 1207 size_t intersecting_with_screens = 0; 1208 Screen::for_each([&](auto& screen) { 1209 if (rect.intersects(screen.rect())) 1210 intersecting_with_screens++; 1211 return IterationDecision::Continue; 1212 }); 1213 1214 if (intersecting_with_screens == 1) { 1215 auto& screen = Screen::closest_to_rect(rect); 1216 auto crop_rect = rect.translated(-screen.rect().location()); 1217 auto bitmap_or_error = Compositor::the().front_bitmap_for_screenshot({}, screen).cropped(crop_rect); 1218 if (bitmap_or_error.is_error()) { 1219 dbgln("get_screen_bitmap_around_cursor: Failed to crop screenshot: {}", bitmap_or_error.error()); 1220 return { {} }; 1221 } 1222 return bitmap_or_error.release_value()->to_shareable_bitmap(); 1223 } 1224 1225 if (auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, rect.size(), 1); !bitmap_or_error.is_error()) { 1226 auto bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors(); 1227 auto bounding_screen_src_rect = Screen::bounding_rect().intersected(rect); 1228 Gfx::Painter painter(*bitmap); 1229 auto& screen_with_cursor = ScreenInput::the().cursor_location_screen(); 1230 auto cursor_rect = Compositor::the().current_cursor_rect(); 1231 Screen::for_each([&](auto& screen) { 1232 auto screen_rect = screen.rect(); 1233 auto src_rect = screen_rect.intersected(bounding_screen_src_rect); 1234 if (src_rect.is_empty()) 1235 return IterationDecision ::Continue; 1236 auto& screen_bitmap = Compositor::the().front_bitmap_for_screenshot({}, screen); 1237 // TODO: Add scaling support for multiple screens 1238 auto from_rect = src_rect.translated(-screen_rect.location()); 1239 auto target_location = rect.intersected(screen_rect).location().translated(-rect.location()); 1240 // TODO: painter does *not* support down-sampling!!! 1241 painter.blit(target_location, screen_bitmap, from_rect, 1.0f, false); 1242 // Check if we are a screen that doesn't have the cursor but the cursor would 1243 // have normally been cut off (we don't draw portions of the cursor on a screen 1244 // that doesn't actually have the cursor). In that case we need to render the remaining 1245 // portion of the cursor on that screen's capture manually 1246 if (&screen != &screen_with_cursor) { 1247 auto screen_cursor_rect = cursor_rect.intersected(screen_rect); 1248 if (!screen_cursor_rect.is_empty()) { 1249 if (auto const* cursor_bitmap = Compositor::the().cursor_bitmap_for_screenshot({}, screen)) { 1250 auto src_rect = screen_cursor_rect.translated(-cursor_rect.location()); 1251 auto cursor_target = cursor_rect.intersected(screen_rect).location().translated(-rect.location()); 1252 // TODO: painter does *not* support down-sampling!!! 1253 painter.blit(cursor_target, *cursor_bitmap, src_rect); 1254 } 1255 } 1256 } 1257 return IterationDecision::Continue; 1258 }); 1259 return bitmap->to_shareable_bitmap(); 1260 } 1261 return { {} }; 1262} 1263 1264Messages::WindowServer::GetColorUnderCursorResponse ConnectionFromClient::get_color_under_cursor() 1265{ 1266 auto screen_scale_factor = ScreenInput::the().cursor_location_screen().scale_factor(); 1267 // FIXME: Add a mechanism to get screen bitmap without cursor, so we don't have to do this 1268 // manual translation to avoid sampling the color on the actual cursor itself. 1269 auto cursor_location = (ScreenInput::the().cursor_location() * screen_scale_factor).translated(-1, -1); 1270 auto& screen_with_cursor = ScreenInput::the().cursor_location_screen(); 1271 auto scaled_screen_rect = screen_with_cursor.rect() * screen_scale_factor; 1272 1273 if (!scaled_screen_rect.contains(cursor_location)) 1274 return Optional<Color> {}; 1275 1276 return { Compositor::the().color_at_position({}, screen_with_cursor, cursor_location) }; 1277} 1278 1279Messages::WindowServer::IsWindowModifiedResponse ConnectionFromClient::is_window_modified(i32 window_id) 1280{ 1281 auto it = m_windows.find(window_id); 1282 if (it == m_windows.end()) { 1283 did_misbehave("IsWindowModified: Bad window ID"); 1284 return nullptr; 1285 } 1286 auto& window = *it->value; 1287 return window.is_modified(); 1288} 1289 1290Messages::WindowServer::GetDesktopDisplayScaleResponse ConnectionFromClient::get_desktop_display_scale(u32 screen_index) 1291{ 1292 if (auto* screen = Screen::find_by_index(screen_index)) 1293 return screen->scale_factor(); 1294 dbgln("GetDesktopDisplayScale: Screen {} does not exist", screen_index); 1295 return 0; 1296} 1297 1298void ConnectionFromClient::set_window_modified(i32 window_id, bool modified) 1299{ 1300 auto it = m_windows.find(window_id); 1301 if (it == m_windows.end()) { 1302 did_misbehave("SetWindowModified: Bad window ID"); 1303 return; 1304 } 1305 auto& window = *it->value; 1306 window.set_modified(modified); 1307} 1308 1309void ConnectionFromClient::set_flash_flush(bool enabled) 1310{ 1311 Compositor::the().set_flash_flush(enabled); 1312} 1313 1314void ConnectionFromClient::set_window_parent_from_client(i32 client_id, i32 parent_id, i32 child_id) 1315{ 1316 auto* child_window = window_from_id(child_id); 1317 if (!child_window) { 1318 did_misbehave("SetWindowParentFromClient: Bad child window ID"); 1319 return; 1320 } 1321 1322 auto* client_connection = from_client_id(client_id); 1323 if (!client_connection) { 1324 did_misbehave("SetWindowParentFromClient: Bad client ID"); 1325 return; 1326 } 1327 1328 auto* parent_window = client_connection->window_from_id(parent_id); 1329 if (!parent_window) { 1330 did_misbehave("SetWindowParentFromClient: Bad parent window ID"); 1331 return; 1332 } 1333 1334 if (parent_window->is_stealable_by_client(this->client_id())) { 1335 child_window->set_parent_window(*parent_window); 1336 } else { 1337 did_misbehave("SetWindowParentFromClient: Window is not stealable"); 1338 } 1339} 1340 1341Messages::WindowServer::GetWindowRectFromClientResponse ConnectionFromClient::get_window_rect_from_client(i32 client_id, i32 window_id) 1342{ 1343 auto* client_connection = from_client_id(client_id); 1344 if (!client_connection) { 1345 did_misbehave("GetWindowRectFromClient: Bad client ID"); 1346 return { Gfx::IntRect() }; 1347 } 1348 1349 auto* window = client_connection->window_from_id(window_id); 1350 if (!window) { 1351 did_misbehave("GetWindowRectFromClient: Bad window ID"); 1352 return { Gfx::IntRect() }; 1353 } 1354 1355 return window->rect(); 1356} 1357 1358void ConnectionFromClient::add_window_stealing_for_client(i32 client_id, i32 window_id) 1359{ 1360 auto* window = window_from_id(window_id); 1361 if (!window) { 1362 did_misbehave("AddWindowStealingForClient: Bad window ID"); 1363 return; 1364 } 1365 1366 if (!from_client_id(client_id)) { 1367 did_misbehave("AddWindowStealingForClient: Bad client ID"); 1368 return; 1369 } 1370 1371 window->add_stealing_for_client(client_id); 1372} 1373 1374void ConnectionFromClient::remove_window_stealing_for_client(i32 client_id, i32 window_id) 1375{ 1376 auto* window = window_from_id(window_id); 1377 if (!window) { 1378 did_misbehave("RemoveWindowStealingForClient: Bad window ID"); 1379 return; 1380 } 1381 1382 // Don't check if the client exists, it may have died 1383 1384 window->remove_stealing_for_client(client_id); 1385} 1386 1387void ConnectionFromClient::remove_window_stealing(i32 window_id) 1388{ 1389 auto* window = window_from_id(window_id); 1390 if (!window) { 1391 did_misbehave("RemoveWindowStealing: Bad window ID"); 1392 return; 1393 } 1394 1395 window->remove_all_stealing(); 1396} 1397 1398void ConnectionFromClient::set_always_on_top(i32 window_id, bool always_on_top) 1399{ 1400 auto* window = window_from_id(window_id); 1401 if (!window) { 1402 did_misbehave("SetAlwaysOnTop: Bad window ID"); 1403 return; 1404 } 1405 1406 window->set_always_on_top(always_on_top); 1407} 1408 1409void ConnectionFromClient::notify_about_theme_change() 1410{ 1411 // Recalculate minimum size for each window, using the new theme metrics. 1412 // FIXME: We only ever increase the minimum size, which means that if you go from a theme with large buttons 1413 // (eg Basalt) to one with smaller buttons (eg Default) then the minimum size will remain large. This 1414 // only happens with pre-existing windows, and it's unlikely that you will ever have windows that are 1415 // so small, so it's probably fine, but it is technically a bug. :^) 1416 for_each_window([](auto& window) -> IterationDecision { 1417 auto system_window_minimum_size = calculate_minimum_size_for_window(window); 1418 1419 auto old_minimum_size = window.minimum_size(); 1420 auto new_rect = window.rect(); 1421 1422 window.set_minimum_size({ max(old_minimum_size.width(), system_window_minimum_size.width()), 1423 max(old_minimum_size.height(), system_window_minimum_size.height()) }); 1424 if (window.apply_minimum_size(new_rect)) { 1425 window.set_rect(new_rect); 1426 window.refresh_client_size(); 1427 } 1428 1429 return IterationDecision::Continue; 1430 }); 1431 async_update_system_theme(Gfx::current_system_theme_buffer()); 1432} 1433 1434}