Serenity Operating System
at master 1369 lines 44 kB view raw
1/* 2 * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Debug.h> 8#include <AK/HashMap.h> 9#include <AK/IDAllocator.h> 10#include <AK/JsonObject.h> 11#include <AK/NeverDestroyed.h> 12#include <AK/ScopeGuard.h> 13#include <LibCore/EventLoop.h> 14#include <LibCore/MimeData.h> 15#include <LibGUI/Action.h> 16#include <LibGUI/Application.h> 17#include <LibGUI/ConnectionToWindowManagerServer.h> 18#include <LibGUI/ConnectionToWindowServer.h> 19#include <LibGUI/Desktop.h> 20#include <LibGUI/Event.h> 21#include <LibGUI/MenuItem.h> 22#include <LibGUI/Menubar.h> 23#include <LibGUI/Painter.h> 24#include <LibGUI/Widget.h> 25#include <LibGUI/Window.h> 26#include <LibGfx/Bitmap.h> 27#include <fcntl.h> 28#include <stdio.h> 29#include <stdlib.h> 30#include <unistd.h> 31 32namespace GUI { 33 34static i32 s_next_backing_store_serial; 35static IDAllocator s_window_id_allocator; 36 37class WindowBackingStore { 38public: 39 explicit WindowBackingStore(NonnullRefPtr<Gfx::Bitmap> bitmap) 40 : m_bitmap(move(bitmap)) 41 , m_serial(++s_next_backing_store_serial) 42 , m_visible_size(m_bitmap->size()) 43 { 44 } 45 46 Gfx::Bitmap& bitmap() { return *m_bitmap; } 47 Gfx::Bitmap const& bitmap() const { return *m_bitmap; } 48 49 Gfx::IntSize size() const { return m_bitmap->size(); } 50 51 i32 serial() const { return m_serial; } 52 53 Gfx::IntSize visible_size() const { return m_visible_size; } 54 void set_visible_size(Gfx::IntSize visible_size) { m_visible_size = visible_size; } 55 56private: 57 NonnullRefPtr<Gfx::Bitmap> m_bitmap; 58 const i32 m_serial; 59 Gfx::IntSize m_visible_size; 60}; 61 62static NeverDestroyed<HashTable<Window*>> all_windows; 63static NeverDestroyed<HashMap<int, Window*>> reified_windows; 64 65Window* Window::from_window_id(int window_id) 66{ 67 auto it = reified_windows->find(window_id); 68 if (it != reified_windows->end()) 69 return (*it).value; 70 return nullptr; 71} 72 73Window::Window(Core::Object* parent) 74 : Core::Object(parent) 75 , m_menubar(Menubar::construct()) 76{ 77 if (parent) 78 set_window_mode(WindowMode::Passive); 79 80 all_windows->set(this); 81 m_rect_when_windowless = { -5000, -5000, 0, 0 }; 82 m_title_when_windowless = "GUI::Window"; 83 84 register_property( 85 "title", 86 [this] { return title(); }, 87 [this](auto& value) { 88 set_title(value.to_deprecated_string()); 89 return true; 90 }); 91 92 register_property("visible", [this] { return is_visible(); }); 93 register_property("active", [this] { return is_active(); }); 94 95 REGISTER_BOOL_PROPERTY("minimizable", is_minimizable, set_minimizable); 96 REGISTER_BOOL_PROPERTY("resizable", is_resizable, set_resizable); 97 REGISTER_BOOL_PROPERTY("fullscreen", is_fullscreen, set_fullscreen); 98 REGISTER_RECT_PROPERTY("rect", rect, set_rect); 99 REGISTER_SIZE_PROPERTY("base_size", base_size, set_base_size); 100 REGISTER_SIZE_PROPERTY("size_increment", size_increment, set_size_increment); 101 REGISTER_BOOL_PROPERTY("obey_widget_min_size", is_obeying_widget_min_size, set_obey_widget_min_size); 102} 103 104Window::~Window() 105{ 106 all_windows->remove(this); 107 hide(); 108} 109 110void Window::close() 111{ 112 hide(); 113 if (on_close) 114 on_close(); 115} 116 117void Window::move_to_front() 118{ 119 if (!is_visible()) 120 return; 121 122 ConnectionToWindowServer::the().async_move_window_to_front(m_window_id); 123} 124 125void Window::show() 126{ 127 if (is_visible()) 128 return; 129 130 auto* parent_window = find_parent_window(); 131 132 m_window_id = s_window_id_allocator.allocate(); 133 134 Gfx::IntRect launch_origin_rect; 135 if (auto* launch_origin_rect_string = getenv("__libgui_launch_origin_rect")) { 136 auto parts = StringView { launch_origin_rect_string, strlen(launch_origin_rect_string) }.split_view(','); 137 if (parts.size() == 4) { 138 launch_origin_rect = Gfx::IntRect { 139 parts[0].to_int().value_or(0), 140 parts[1].to_int().value_or(0), 141 parts[2].to_int().value_or(0), 142 parts[3].to_int().value_or(0), 143 }; 144 } 145 unsetenv("__libgui_launch_origin_rect"); 146 } 147 148 update_min_size(); 149 150 ConnectionToWindowServer::the().async_create_window( 151 m_window_id, 152 m_rect_when_windowless, 153 !m_moved_by_client, 154 m_has_alpha_channel, 155 m_minimizable, 156 m_closeable, 157 m_resizable, 158 m_fullscreen, 159 m_frameless, 160 m_forced_shadow, 161 m_opacity_when_windowless, 162 m_alpha_hit_threshold, 163 m_base_size, 164 m_size_increment, 165 m_minimum_size_when_windowless, 166 m_resize_aspect_ratio, 167 (i32)m_window_type, 168 (i32)m_window_mode, 169 m_title_when_windowless, 170 parent_window ? parent_window->window_id() : 0, 171 launch_origin_rect); 172 m_visible = true; 173 m_visible_for_timer_purposes = true; 174 175 apply_icon(); 176 177 m_menubar->for_each_menu([&](Menu& menu) { 178 menu.realize_menu_if_needed(); 179 ConnectionToWindowServer::the().async_add_menu(m_window_id, menu.menu_id()); 180 return IterationDecision::Continue; 181 }); 182 183 set_maximized(m_maximized); 184 reified_windows->set(m_window_id, this); 185 Application::the()->did_create_window({}); 186 update(); 187} 188 189Window* Window::find_parent_window() 190{ 191 for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { 192 if (is<Window>(ancestor)) 193 return static_cast<Window*>(ancestor); 194 } 195 return nullptr; 196} 197 198void Window::server_did_destroy() 199{ 200 reified_windows->remove(m_window_id); 201 m_window_id = 0; 202 m_visible = false; 203 m_pending_paint_event_rects.clear(); 204 m_back_store = nullptr; 205 m_front_store = nullptr; 206 m_cursor = Gfx::StandardCursor::None; 207} 208 209void Window::hide() 210{ 211 if (!is_visible()) 212 return; 213 214 // NOTE: Don't bother asking WindowServer to destroy windows during application teardown. 215 // All our windows will be automatically garbage-collected by WindowServer anyway. 216 if (GUI::Application::in_teardown()) 217 return; 218 219 m_rect_when_windowless = rect(); 220 221 auto destroyed_window_ids = ConnectionToWindowServer::the().destroy_window(m_window_id); 222 server_did_destroy(); 223 224 for (auto child_window_id : destroyed_window_ids) { 225 if (auto* window = Window::from_window_id(child_window_id)) { 226 window->server_did_destroy(); 227 } 228 } 229 230 if (auto* app = Application::the()) { 231 bool app_has_visible_windows = false; 232 for (auto& window : *all_windows) { 233 if (window->is_visible()) { 234 app_has_visible_windows = true; 235 break; 236 } 237 } 238 if (!app_has_visible_windows) 239 app->did_delete_last_window({}); 240 } 241} 242 243void Window::set_title(DeprecatedString title) 244{ 245 m_title_when_windowless = move(title); 246 if (!is_visible()) 247 return; 248 ConnectionToWindowServer::the().async_set_window_title(m_window_id, m_title_when_windowless); 249} 250 251DeprecatedString Window::title() const 252{ 253 if (!is_visible()) 254 return m_title_when_windowless; 255 return ConnectionToWindowServer::the().get_window_title(m_window_id); 256} 257 258Gfx::IntRect Window::applet_rect_on_screen() const 259{ 260 VERIFY(m_window_type == WindowType::Applet); 261 return ConnectionToWindowServer::the().get_applet_rect_on_screen(m_window_id); 262} 263 264Gfx::IntRect Window::rect() const 265{ 266 if (!is_visible()) 267 return m_rect_when_windowless; 268 return ConnectionToWindowServer::the().get_window_rect(m_window_id); 269} 270 271void Window::set_rect(Gfx::IntRect const& a_rect) 272{ 273 if (a_rect.location() != m_rect_when_windowless.location()) { 274 m_moved_by_client = true; 275 } 276 277 m_rect_when_windowless = a_rect; 278 if (!is_visible()) { 279 if (m_main_widget) 280 m_main_widget->resize(m_rect_when_windowless.size()); 281 return; 282 } 283 auto window_rect = ConnectionToWindowServer::the().set_window_rect(m_window_id, a_rect); 284 if (m_back_store && m_back_store->size() != window_rect.size()) 285 m_back_store = nullptr; 286 if (m_front_store && m_front_store->size() != window_rect.size()) 287 m_front_store = nullptr; 288 if (m_main_widget) 289 m_main_widget->resize(window_rect.size()); 290} 291 292Gfx::IntSize Window::minimum_size() const 293{ 294 if (!is_visible()) 295 return m_minimum_size_when_windowless; 296 297 return ConnectionToWindowServer::the().get_window_minimum_size(m_window_id); 298} 299 300void Window::set_minimum_size(Gfx::IntSize size) 301{ 302 VERIFY(size.width() >= 0 && size.height() >= 0); 303 VERIFY(!is_obeying_widget_min_size()); 304 m_minimum_size_when_windowless = size; 305 306 if (is_visible()) 307 ConnectionToWindowServer::the().async_set_window_minimum_size(m_window_id, size); 308} 309 310void Window::center_on_screen() 311{ 312 set_rect(rect().centered_within(Desktop::the().rect())); 313} 314 315void Window::center_within(Window const& other) 316{ 317 if (this == &other) 318 return; 319 set_rect(rect().centered_within(other.rect())); 320} 321 322void Window::set_window_type(WindowType window_type) 323{ 324 m_window_type = window_type; 325} 326 327void Window::set_window_mode(WindowMode mode) 328{ 329 VERIFY(!is_visible()); 330 m_window_mode = mode; 331} 332 333void Window::make_window_manager(unsigned event_mask) 334{ 335 GUI::ConnectionToWindowManagerServer::the().async_set_event_mask(event_mask); 336 GUI::ConnectionToWindowManagerServer::the().async_set_manager_window(m_window_id); 337} 338 339bool Window::are_cursors_the_same(AK::Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> const& left, AK::Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> const& right) const 340{ 341 if (left.has<Gfx::StandardCursor>() != right.has<Gfx::StandardCursor>()) 342 return false; 343 if (left.has<Gfx::StandardCursor>()) 344 return left.get<Gfx::StandardCursor>() == right.get<Gfx::StandardCursor>(); 345 return left.get<NonnullRefPtr<Gfx::Bitmap const>>().ptr() == right.get<NonnullRefPtr<Gfx::Bitmap const>>().ptr(); 346} 347 348void Window::set_cursor(Gfx::StandardCursor cursor) 349{ 350 if (are_cursors_the_same(m_cursor, cursor)) 351 return; 352 m_cursor = cursor; 353 update_cursor(); 354} 355 356void Window::set_cursor(NonnullRefPtr<Gfx::Bitmap const> cursor) 357{ 358 if (are_cursors_the_same(m_cursor, cursor)) 359 return; 360 m_cursor = cursor; 361 update_cursor(); 362} 363 364void Window::handle_drop_event(DropEvent& event) 365{ 366 if (!m_main_widget) 367 return; 368 auto result = m_main_widget->hit_test(event.position()); 369 auto local_event = make<DropEvent>(result.local_position, event.text(), event.mime_data()); 370 VERIFY(result.widget); 371 result.widget->dispatch_event(*local_event, this); 372 373 Application::the()->set_drag_hovered_widget({}, nullptr); 374} 375 376void Window::handle_mouse_event(MouseEvent& event) 377{ 378 if (!m_main_widget) 379 return; 380 auto result = m_main_widget->hit_test(event.position()); 381 VERIFY(result.widget); 382 383 if (m_automatic_cursor_tracking_widget) { 384 auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect(); 385 Gfx::IntPoint local_point { event.x() - window_relative_rect.x(), event.y() - window_relative_rect.y() }; 386 auto local_event = MouseEvent((Event::Type)event.type(), local_point, event.buttons(), event.button(), event.modifiers(), event.wheel_delta_x(), event.wheel_delta_y(), event.wheel_raw_delta_x(), event.wheel_raw_delta_y()); 387 m_automatic_cursor_tracking_widget->dispatch_event(local_event, this); 388 if (event.buttons() == 0) { 389 m_automatic_cursor_tracking_widget = nullptr; 390 } else { 391 auto is_hovered = m_automatic_cursor_tracking_widget.ptr() == result.widget.ptr(); 392 set_hovered_widget(is_hovered ? m_automatic_cursor_tracking_widget.ptr() : nullptr); 393 } 394 return; 395 } 396 set_hovered_widget(result.widget); 397 if (event.buttons() != 0 && !m_automatic_cursor_tracking_widget) 398 m_automatic_cursor_tracking_widget = *result.widget; 399 auto local_event = MouseEvent((Event::Type)event.type(), result.local_position, event.buttons(), event.button(), event.modifiers(), event.wheel_delta_x(), event.wheel_delta_y(), event.wheel_raw_delta_x(), event.wheel_raw_delta_y()); 400 result.widget->dispatch_event(local_event, this); 401} 402 403Gfx::IntSize Window::backing_store_size(Gfx::IntSize window_size) const 404{ 405 if (!m_resizing) 406 return window_size; 407 408 int const backing_margin_during_resize = 64; 409 return { window_size.width() + backing_margin_during_resize, window_size.height() + backing_margin_during_resize }; 410} 411 412void Window::handle_multi_paint_event(MultiPaintEvent& event) 413{ 414 if (!is_visible()) 415 return; 416 if (!m_main_widget) 417 return; 418 auto rects = event.rects(); 419 if (!m_pending_paint_event_rects.is_empty()) { 420 // It's possible that there had been some calls to update() that 421 // haven't been flushed. We can handle these right now, avoiding 422 // another round trip. 423 rects.extend(move(m_pending_paint_event_rects)); 424 } 425 VERIFY(!rects.is_empty()); 426 427 // Throw away our backing store if its size is different, and we've stopped resizing or double buffering is disabled. 428 // This ensures that we shrink the backing store after a resize, and that we do not get flickering artifacts when 429 // directly painting into a shared active backing store. 430 if (m_back_store && (!m_resizing || !m_double_buffering_enabled) && m_back_store->size() != event.window_size()) 431 m_back_store = nullptr; 432 433 // Discard our backing store if it's unable to contain the new window size. Smaller is fine though, that prevents 434 // lots of backing store allocations during a resize. 435 if (m_back_store && !m_back_store->size().contains(event.window_size())) 436 m_back_store = nullptr; 437 438 bool created_new_backing_store = false; 439 if (!m_back_store) { 440 m_back_store = create_backing_store(backing_store_size(event.window_size())).release_value_but_fixme_should_propagate_errors(); 441 created_new_backing_store = true; 442 } else if (m_double_buffering_enabled) { 443 bool was_purged = false; 444 bool bitmap_has_memory = m_back_store->bitmap().set_nonvolatile(was_purged); 445 if (!bitmap_has_memory) { 446 // We didn't have enough memory to make the bitmap non-volatile! 447 // Fall back to single-buffered mode for this window. 448 // FIXME: Once we have a way to listen for system memory pressure notifications, 449 // it would be cool to transition back into double-buffered mode once 450 // the coast is clear. 451 dbgln("Not enough memory to make backing store non-volatile. Falling back to single-buffered mode."); 452 m_double_buffering_enabled = false; 453 m_back_store = move(m_front_store); 454 created_new_backing_store = true; 455 } else if (was_purged) { 456 // The backing store bitmap was cleared, but it does have memory. 457 // Act as if it's a new backing store so the entire window gets repainted. 458 created_new_backing_store = true; 459 } 460 } 461 462 if (created_new_backing_store) { 463 rects.clear(); 464 rects.append({ {}, event.window_size() }); 465 } 466 467 for (auto& rect : rects) { 468 PaintEvent paint_event(rect); 469 m_main_widget->dispatch_event(paint_event, this); 470 } 471 m_back_store->set_visible_size(event.window_size()); 472 473 if (m_double_buffering_enabled) 474 flip(rects); 475 else if (created_new_backing_store) 476 set_current_backing_store(*m_back_store, true); 477 478 if (is_visible()) 479 ConnectionToWindowServer::the().async_did_finish_painting(m_window_id, rects); 480} 481 482void Window::propagate_shortcuts_up_to_application(KeyEvent& event, Widget* widget) 483{ 484 VERIFY(event.type() == Event::KeyDown); 485 auto shortcut = Shortcut(event.modifiers(), event.key()); 486 Action* action = nullptr; 487 488 if (widget) { 489 VERIFY(widget->window() == this); 490 491 do { 492 action = widget->action_for_shortcut(shortcut); 493 if (action) 494 break; 495 496 widget = widget->parent_widget(); 497 } while (widget); 498 } 499 500 if (!action) 501 action = action_for_shortcut(shortcut); 502 if (!action) 503 action = Application::the()->action_for_shortcut(shortcut); 504 505 if (action) { 506 action->process_event(*this, event); 507 return; 508 } 509 510 event.ignore(); 511} 512 513void Window::handle_key_event(KeyEvent& event) 514{ 515 if (!m_focused_widget && event.type() == Event::KeyDown && event.key() == Key_Tab && !event.ctrl() && !event.alt() && !event.super()) { 516 focus_a_widget_if_possible(FocusSource::Keyboard); 517 } 518 519 if (m_default_return_key_widget && event.key() == Key_Return) 520 if (!m_focused_widget || !is<Button>(m_focused_widget.ptr())) 521 return default_return_key_widget()->dispatch_event(event, this); 522 523 if (m_focused_widget) 524 m_focused_widget->dispatch_event(event, this); 525 else if (m_main_widget) 526 m_main_widget->dispatch_event(event, this); 527 528 if (event.is_accepted()) 529 return; 530 531 if (is_blocking() || is_popup()) 532 return; 533 534 // Only process shortcuts if this is a keydown event. 535 if (event.type() == Event::KeyDown) 536 propagate_shortcuts_up_to_application(event, nullptr); 537} 538 539void Window::handle_resize_event(ResizeEvent& event) 540{ 541 auto new_size = event.size(); 542 543 // When the user is done resizing, we receive a last resize event with our actual size. 544 m_resizing = new_size != m_rect_when_windowless.size(); 545 546 if (!m_pending_paint_event_rects.is_empty()) { 547 m_pending_paint_event_rects.clear_with_capacity(); 548 m_pending_paint_event_rects.append({ {}, new_size }); 549 } 550 m_rect_when_windowless.set_size(new_size); 551 if (m_main_widget) 552 m_main_widget->set_relative_rect({ {}, new_size }); 553} 554 555void Window::handle_input_preemption_event(Core::Event& event) 556{ 557 if (on_input_preemption_change) 558 on_input_preemption_change(event.type() == Event::WindowInputPreempted); 559 if (!m_focused_widget) 560 return; 561 m_focused_widget->set_focus_preempted(event.type() == Event::WindowInputPreempted); 562 m_focused_widget->update(); 563} 564 565void Window::handle_became_active_or_inactive_event(Core::Event& event) 566{ 567 if (event.type() == Event::WindowBecameActive) 568 Application::the()->window_did_become_active({}, *this); 569 else 570 Application::the()->window_did_become_inactive({}, *this); 571 if (on_active_window_change) 572 on_active_window_change(event.type() == Event::WindowBecameActive); 573 if (m_main_widget) 574 m_main_widget->dispatch_event(event, this); 575 if (m_focused_widget) { 576 if (event.type() == Event::WindowBecameActive) 577 m_focused_widget->set_focus_preempted(false); 578 m_focused_widget->update(); 579 } 580} 581 582void Window::handle_close_request() 583{ 584 if (on_close_request) { 585 if (on_close_request() == Window::CloseRequestDecision::StayOpen) 586 return; 587 } 588 close(); 589} 590 591void Window::handle_theme_change_event(ThemeChangeEvent& event) 592{ 593 if (!m_main_widget) 594 return; 595 auto dispatch_theme_change = [&](auto& widget, auto recursive) { 596 widget.dispatch_event(event, this); 597 widget.for_each_child_widget([&](auto& widget) -> IterationDecision { 598 widget.dispatch_event(event, this); 599 recursive(widget, recursive); 600 return IterationDecision::Continue; 601 }); 602 }; 603 dispatch_theme_change(*m_main_widget.ptr(), dispatch_theme_change); 604} 605 606void Window::handle_fonts_change_event(FontsChangeEvent& event) 607{ 608 if (!m_main_widget) 609 return; 610 auto dispatch_fonts_change = [&](auto& widget, auto recursive) { 611 widget.dispatch_event(event, this); 612 widget.for_each_child_widget([&](auto& widget) -> IterationDecision { 613 widget.dispatch_event(event, this); 614 recursive(widget, recursive); 615 return IterationDecision::Continue; 616 }); 617 }; 618 dispatch_fonts_change(*m_main_widget.ptr(), dispatch_fonts_change); 619} 620 621void Window::handle_screen_rects_change_event(ScreenRectsChangeEvent& event) 622{ 623 if (!m_main_widget) 624 return; 625 auto dispatch_screen_rects_change = [&](auto& widget, auto recursive) { 626 widget.dispatch_event(event, this); 627 widget.for_each_child_widget([&](auto& widget) -> IterationDecision { 628 widget.dispatch_event(event, this); 629 recursive(widget, recursive); 630 return IterationDecision::Continue; 631 }); 632 }; 633 dispatch_screen_rects_change(*m_main_widget.ptr(), dispatch_screen_rects_change); 634 screen_rects_change_event(event); 635} 636 637void Window::handle_applet_area_rect_change_event(AppletAreaRectChangeEvent& event) 638{ 639 if (!m_main_widget) 640 return; 641 auto dispatch_applet_area_rect_change = [&](auto& widget, auto recursive) { 642 widget.dispatch_event(event, this); 643 widget.for_each_child_widget([&](auto& widget) -> IterationDecision { 644 widget.dispatch_event(event, this); 645 recursive(widget, recursive); 646 return IterationDecision::Continue; 647 }); 648 }; 649 dispatch_applet_area_rect_change(*m_main_widget.ptr(), dispatch_applet_area_rect_change); 650 applet_area_rect_change_event(event); 651} 652 653void Window::handle_drag_move_event(DragEvent& event) 654{ 655 if (!m_main_widget) 656 return; 657 auto result = m_main_widget->hit_test(event.position()); 658 VERIFY(result.widget); 659 660 Application::the()->set_drag_hovered_widget({}, result.widget, result.local_position, event.mime_types()); 661 662 // NOTE: Setting the drag hovered widget may have executed arbitrary code, so re-check that the widget is still there. 663 if (!result.widget) 664 return; 665 666 if (result.widget->has_pending_drop()) { 667 DragEvent drag_move_event(static_cast<Event::Type>(event.type()), result.local_position, event.mime_types()); 668 result.widget->dispatch_event(drag_move_event, this); 669 } 670} 671 672void Window::enter_event(Core::Event&) 673{ 674} 675 676void Window::leave_event(Core::Event&) 677{ 678} 679 680void Window::handle_entered_event(Core::Event& event) 681{ 682 enter_event(event); 683} 684 685void Window::handle_left_event(Core::Event& event) 686{ 687 set_hovered_widget(nullptr); 688 Application::the()->set_drag_hovered_widget({}, nullptr); 689 leave_event(event); 690} 691 692void Window::event(Core::Event& event) 693{ 694 ScopeGuard guard([&] { 695 // Accept the event so it doesn't bubble up to parent windows! 696 event.accept(); 697 }); 698 if (event.type() == Event::Drop) 699 return handle_drop_event(static_cast<DropEvent&>(event)); 700 701 if (event.type() == Event::MouseUp || event.type() == Event::MouseDown || event.type() == Event::MouseDoubleClick || event.type() == Event::MouseMove || event.type() == Event::MouseWheel) 702 return handle_mouse_event(static_cast<MouseEvent&>(event)); 703 704 if (event.type() == Event::MultiPaint) 705 return handle_multi_paint_event(static_cast<MultiPaintEvent&>(event)); 706 707 if (event.type() == Event::KeyUp || event.type() == Event::KeyDown) 708 return handle_key_event(static_cast<KeyEvent&>(event)); 709 710 if (event.type() == Event::WindowBecameActive || event.type() == Event::WindowBecameInactive) 711 return handle_became_active_or_inactive_event(event); 712 713 if (event.type() == Event::WindowInputPreempted || event.type() == Event::WindowInputRestored) 714 return handle_input_preemption_event(event); 715 716 if (event.type() == Event::WindowCloseRequest) 717 return handle_close_request(); 718 719 if (event.type() == Event::WindowEntered) 720 return handle_entered_event(event); 721 722 if (event.type() == Event::WindowLeft) 723 return handle_left_event(event); 724 725 if (event.type() == Event::Resize) 726 return handle_resize_event(static_cast<ResizeEvent&>(event)); 727 728 if (event.type() > Event::__Begin_WM_Events && event.type() < Event::__End_WM_Events) 729 return wm_event(static_cast<WMEvent&>(event)); 730 731 if (event.type() == Event::DragMove) 732 return handle_drag_move_event(static_cast<DragEvent&>(event)); 733 734 if (event.type() == Event::ThemeChange) 735 return handle_theme_change_event(static_cast<ThemeChangeEvent&>(event)); 736 737 if (event.type() == Event::FontsChange) 738 return handle_fonts_change_event(static_cast<FontsChangeEvent&>(event)); 739 740 if (event.type() == Event::ScreenRectsChange) 741 return handle_screen_rects_change_event(static_cast<ScreenRectsChangeEvent&>(event)); 742 743 if (event.type() == Event::AppletAreaRectChange) 744 return handle_applet_area_rect_change_event(static_cast<AppletAreaRectChangeEvent&>(event)); 745 746 Core::Object::event(event); 747} 748 749bool Window::is_visible() const 750{ 751 return m_visible; 752} 753 754void Window::update() 755{ 756 auto rect = this->rect(); 757 update({ 0, 0, rect.width(), rect.height() }); 758} 759 760void Window::force_update() 761{ 762 if (!is_visible()) 763 return; 764 auto rect = this->rect(); 765 ConnectionToWindowServer::the().async_invalidate_rect(m_window_id, { { 0, 0, rect.width(), rect.height() } }, true); 766} 767 768void Window::update(Gfx::IntRect const& a_rect) 769{ 770 if (!is_visible()) 771 return; 772 773 for (auto& pending_rect : m_pending_paint_event_rects) { 774 if (pending_rect.contains(a_rect)) { 775 dbgln_if(UPDATE_COALESCING_DEBUG, "Ignoring {} since it's contained by pending rect {}", a_rect, pending_rect); 776 return; 777 } 778 } 779 780 if (m_pending_paint_event_rects.is_empty()) { 781 deferred_invoke([this] { 782 auto rects = move(m_pending_paint_event_rects); 783 if (rects.is_empty()) 784 return; 785 ConnectionToWindowServer::the().async_invalidate_rect(m_window_id, rects, false); 786 }); 787 } 788 m_pending_paint_event_rects.append(a_rect); 789} 790 791void Window::set_main_widget(Widget* widget) 792{ 793 if (m_main_widget == widget) 794 return; 795 if (m_main_widget) { 796 m_main_widget->set_window(nullptr); 797 remove_child(*m_main_widget); 798 } 799 m_main_widget = widget; 800 if (m_main_widget) { 801 add_child(*widget); 802 auto new_window_rect = rect(); 803 auto new_widget_min_size = m_main_widget->effective_min_size(); 804 new_window_rect.set_width(max(new_window_rect.width(), MUST(new_widget_min_size.width().shrink_value()))); 805 new_window_rect.set_height(max(new_window_rect.height(), MUST(new_widget_min_size.height().shrink_value()))); 806 set_rect(new_window_rect); 807 m_main_widget->set_relative_rect({ {}, new_window_rect.size() }); 808 m_main_widget->set_window(this); 809 if (m_main_widget->focus_policy() != FocusPolicy::NoFocus) 810 m_main_widget->set_focus(true); 811 } 812 update(); 813} 814 815void Window::set_default_return_key_widget(Widget* widget) 816{ 817 if (m_default_return_key_widget == widget) 818 return; 819 m_default_return_key_widget = widget; 820} 821 822void Window::set_focused_widget(Widget* widget, FocusSource source) 823{ 824 if (m_focused_widget == widget) 825 return; 826 827 WeakPtr<Widget> previously_focused_widget = m_focused_widget; 828 m_focused_widget = widget; 829 830 if (!m_focused_widget && m_previously_focused_widget) 831 m_focused_widget = m_previously_focused_widget; 832 833 if (m_default_return_key_widget && m_default_return_key_widget->on_focus_change) 834 m_default_return_key_widget->on_focus_change(m_default_return_key_widget->is_focused(), source); 835 836 if (previously_focused_widget) { 837 Core::EventLoop::current().post_event(*previously_focused_widget, make<FocusEvent>(Event::FocusOut, source)); 838 previously_focused_widget->update(); 839 if (previously_focused_widget && previously_focused_widget->on_focus_change) 840 previously_focused_widget->on_focus_change(previously_focused_widget->is_focused(), source); 841 m_previously_focused_widget = previously_focused_widget; 842 } 843 if (m_focused_widget) { 844 Core::EventLoop::current().post_event(*m_focused_widget, make<FocusEvent>(Event::FocusIn, source)); 845 m_focused_widget->update(); 846 if (m_focused_widget && m_focused_widget->on_focus_change) 847 m_focused_widget->on_focus_change(m_focused_widget->is_focused(), source); 848 } 849} 850 851void Window::set_automatic_cursor_tracking_widget(Widget* widget) 852{ 853 if (widget == m_automatic_cursor_tracking_widget) 854 return; 855 m_automatic_cursor_tracking_widget = widget; 856} 857 858void Window::set_has_alpha_channel(bool value) 859{ 860 if (m_has_alpha_channel == value) 861 return; 862 m_has_alpha_channel = value; 863 if (!is_visible()) 864 return; 865 866 m_pending_paint_event_rects.clear(); 867 m_back_store = nullptr; 868 m_front_store = nullptr; 869 870 ConnectionToWindowServer::the().async_set_window_has_alpha_channel(m_window_id, value); 871 update(); 872} 873 874void Window::set_double_buffering_enabled(bool value) 875{ 876 VERIFY(!is_visible()); 877 m_double_buffering_enabled = value; 878} 879 880void Window::set_opacity(float opacity) 881{ 882 m_opacity_when_windowless = opacity; 883 if (!is_visible()) 884 return; 885 ConnectionToWindowServer::the().async_set_window_opacity(m_window_id, opacity); 886} 887 888void Window::set_alpha_hit_threshold(float threshold) 889{ 890 if (threshold < 0.0f) 891 threshold = 0.0f; 892 else if (threshold > 1.0f) 893 threshold = 1.0f; 894 if (m_alpha_hit_threshold == threshold) 895 return; 896 m_alpha_hit_threshold = threshold; 897 if (!is_visible()) 898 return; 899 ConnectionToWindowServer::the().async_set_window_alpha_hit_threshold(m_window_id, threshold); 900} 901 902void Window::set_hovered_widget(Widget* widget) 903{ 904 if (widget == m_hovered_widget) 905 return; 906 907 if (m_hovered_widget) 908 Core::EventLoop::current().post_event(*m_hovered_widget, make<Event>(Event::Leave)); 909 910 m_hovered_widget = widget; 911 912 if (m_hovered_widget) 913 Core::EventLoop::current().post_event(*m_hovered_widget, make<Event>(Event::Enter)); 914 915 auto* app = Application::the(); 916 if (app && app->hover_debugging_enabled()) 917 update(); 918} 919 920void Window::set_current_backing_store(WindowBackingStore& backing_store, bool flush_immediately) const 921{ 922 auto& bitmap = backing_store.bitmap(); 923 ConnectionToWindowServer::the().set_window_backing_store( 924 m_window_id, 925 32, 926 bitmap.pitch(), 927 bitmap.anonymous_buffer().fd(), 928 backing_store.serial(), 929 bitmap.has_alpha_channel(), 930 bitmap.size(), 931 backing_store.visible_size(), 932 flush_immediately); 933} 934 935void Window::flip(Vector<Gfx::IntRect, 32> const& dirty_rects) 936{ 937 swap(m_front_store, m_back_store); 938 939 set_current_backing_store(*m_front_store); 940 941 if (!m_back_store || m_back_store->size() != m_front_store->size()) { 942 m_back_store = create_backing_store(m_front_store->size()).release_value_but_fixme_should_propagate_errors(); 943 memcpy(m_back_store->bitmap().scanline(0), m_front_store->bitmap().scanline(0), m_front_store->bitmap().size_in_bytes()); 944 m_back_store->bitmap().set_volatile(); 945 return; 946 } 947 948 // Copy whatever was painted from the front to the back. 949 Painter painter(m_back_store->bitmap()); 950 for (auto& dirty_rect : dirty_rects) 951 painter.blit(dirty_rect.location(), m_front_store->bitmap(), dirty_rect, 1.0f, false); 952 953 m_back_store->bitmap().set_volatile(); 954} 955 956ErrorOr<NonnullOwnPtr<WindowBackingStore>> Window::create_backing_store(Gfx::IntSize size) 957{ 958 auto format = m_has_alpha_channel ? Gfx::BitmapFormat::BGRA8888 : Gfx::BitmapFormat::BGRx8888; 959 960 VERIFY(!size.is_empty()); 961 size_t pitch = Gfx::Bitmap::minimum_pitch(size.width(), format); 962 size_t size_in_bytes = size.height() * pitch; 963 964 auto buffer = TRY(Core::AnonymousBuffer::create_with_size(round_up_to_power_of_two(size_in_bytes, PAGE_SIZE))); 965 966 // FIXME: Plumb scale factor here eventually. 967 auto bitmap = TRY(Gfx::Bitmap::create_with_anonymous_buffer(format, buffer, size, 1, {})); 968 return make<WindowBackingStore>(bitmap); 969} 970 971void Window::wm_event(WMEvent&) 972{ 973} 974 975void Window::screen_rects_change_event(ScreenRectsChangeEvent&) 976{ 977} 978 979void Window::applet_area_rect_change_event(AppletAreaRectChangeEvent&) 980{ 981} 982 983void Window::set_icon(Gfx::Bitmap const* icon) 984{ 985 if (m_icon == icon) 986 return; 987 988 Gfx::IntSize icon_size = icon ? icon->size() : Gfx::IntSize(16, 16); 989 990 auto new_icon = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, icon_size).release_value_but_fixme_should_propagate_errors(); 991 if (icon) { 992 Painter painter(*new_icon); 993 painter.blit({ 0, 0 }, *icon, icon->rect()); 994 } 995 m_icon = move(new_icon); 996 997 apply_icon(); 998} 999 1000void Window::apply_icon() 1001{ 1002 if (!m_icon) 1003 return; 1004 1005 if (!is_visible()) 1006 return; 1007 1008 ConnectionToWindowServer::the().async_set_window_icon_bitmap(m_window_id, m_icon->to_shareable_bitmap()); 1009} 1010 1011void Window::start_interactive_resize(ResizeDirection resize_direction) 1012{ 1013 ConnectionToWindowServer::the().async_start_window_resize(m_window_id, (i32)resize_direction); 1014} 1015 1016Vector<Widget&> Window::focusable_widgets(FocusSource source) const 1017{ 1018 if (!m_main_widget) 1019 return {}; 1020 1021 HashTable<Widget*> seen_widgets; 1022 Vector<Widget&> collected_widgets; 1023 1024 Function<void(Widget&)> collect_focusable_widgets = [&](auto& widget) { 1025 bool widget_accepts_focus = false; 1026 switch (source) { 1027 case FocusSource::Keyboard: 1028 widget_accepts_focus = has_flag(widget.focus_policy(), FocusPolicy::TabFocus); 1029 break; 1030 case FocusSource::Mouse: 1031 widget_accepts_focus = has_flag(widget.focus_policy(), FocusPolicy::ClickFocus); 1032 break; 1033 case FocusSource::Programmatic: 1034 widget_accepts_focus = widget.focus_policy() != FocusPolicy::NoFocus; 1035 break; 1036 } 1037 1038 if (widget_accepts_focus) { 1039 auto& effective_focus_widget = widget.focus_proxy() ? *widget.focus_proxy() : widget; 1040 if (seen_widgets.set(&effective_focus_widget) == AK::HashSetResult::InsertedNewEntry) 1041 collected_widgets.append(effective_focus_widget); 1042 } 1043 widget.for_each_child_widget([&](auto& child) { 1044 if (!child.is_visible()) 1045 return IterationDecision::Continue; 1046 if (!child.is_enabled()) 1047 return IterationDecision::Continue; 1048 if (!child.is_auto_focusable()) 1049 return IterationDecision::Continue; 1050 collect_focusable_widgets(child); 1051 return IterationDecision::Continue; 1052 }); 1053 }; 1054 1055 collect_focusable_widgets(const_cast<Widget&>(*m_main_widget)); 1056 return collected_widgets; 1057} 1058 1059void Window::set_fullscreen(bool fullscreen) 1060{ 1061 if (m_fullscreen == fullscreen) 1062 return; 1063 m_fullscreen = fullscreen; 1064 if (!is_visible()) 1065 return; 1066 ConnectionToWindowServer::the().async_set_fullscreen(m_window_id, fullscreen); 1067} 1068 1069void Window::set_frameless(bool frameless) 1070{ 1071 if (m_frameless == frameless) 1072 return; 1073 m_frameless = frameless; 1074 if (!is_visible()) 1075 return; 1076 ConnectionToWindowServer::the().async_set_frameless(m_window_id, frameless); 1077 1078 if (!frameless) 1079 apply_icon(); 1080} 1081 1082void Window::set_forced_shadow(bool shadow) 1083{ 1084 if (m_forced_shadow == shadow) 1085 return; 1086 m_forced_shadow = shadow; 1087 if (!is_visible()) 1088 return; 1089 ConnectionToWindowServer::the().async_set_forced_shadow(m_window_id, shadow); 1090} 1091 1092void Window::set_obey_widget_min_size(bool obey_widget_min_size) 1093{ 1094 if (m_obey_widget_min_size != obey_widget_min_size) { 1095 m_obey_widget_min_size = obey_widget_min_size; 1096 schedule_relayout(); 1097 } 1098} 1099 1100void Window::set_maximized(bool maximized) 1101{ 1102 m_maximized = maximized; 1103 if (!is_visible()) 1104 return; 1105 1106 ConnectionToWindowServer::the().async_set_maximized(m_window_id, maximized); 1107} 1108 1109void Window::set_minimized(bool minimized) 1110{ 1111 if (!is_minimizable()) 1112 return; 1113 1114 m_minimized = minimized; 1115 if (!is_visible()) 1116 return; 1117 1118 ConnectionToWindowServer::the().async_set_minimized(m_window_id, minimized); 1119} 1120 1121void Window::update_min_size() 1122{ 1123 if (main_widget()) { 1124 main_widget()->do_layout(); 1125 if (m_obey_widget_min_size) { 1126 auto min_size = main_widget()->effective_min_size(); 1127 Gfx::IntSize size = { MUST(min_size.width().shrink_value()), MUST(min_size.height().shrink_value()) }; 1128 m_minimum_size_when_windowless = size; 1129 if (is_visible()) 1130 ConnectionToWindowServer::the().async_set_window_minimum_size(m_window_id, size); 1131 } 1132 } 1133} 1134 1135void Window::schedule_relayout() 1136{ 1137 if (m_layout_pending || !is_visible()) 1138 return; 1139 m_layout_pending = true; 1140 deferred_invoke([this] { 1141 update_min_size(); 1142 update(); 1143 m_layout_pending = false; 1144 }); 1145} 1146 1147void Window::refresh_system_theme() 1148{ 1149 ConnectionToWindowServer::the().async_refresh_system_theme(); 1150} 1151 1152void Window::for_each_window(Badge<ConnectionToWindowServer>, Function<void(Window&)> callback) 1153{ 1154 for (auto& e : *reified_windows) { 1155 VERIFY(e.value); 1156 callback(*e.value); 1157 } 1158} 1159 1160void Window::update_all_windows(Badge<ConnectionToWindowServer>) 1161{ 1162 for (auto& e : *reified_windows) { 1163 e.value->force_update(); 1164 } 1165} 1166 1167void Window::notify_state_changed(Badge<ConnectionToWindowServer>, bool minimized, bool maximized, bool occluded) 1168{ 1169 m_visible_for_timer_purposes = !minimized && !occluded; 1170 1171 m_maximized = maximized; 1172 1173 // When double buffering is enabled, minimization/occlusion means we can mark the front bitmap volatile (in addition to the back bitmap.) 1174 // When double buffering is disabled, there is only the back bitmap (which we can now mark volatile!) 1175 auto& store = m_double_buffering_enabled ? m_front_store : m_back_store; 1176 if (!store) 1177 return; 1178 if (minimized || occluded) { 1179 store->bitmap().set_volatile(); 1180 } else { 1181 bool was_purged = false; 1182 bool bitmap_has_memory = store->bitmap().set_nonvolatile(was_purged); 1183 if (!bitmap_has_memory) { 1184 // Not enough memory to make the bitmap non-volatile. Lose the bitmap and schedule an update. 1185 // Let the paint system figure out what to do. 1186 store = nullptr; 1187 update(); 1188 } else if (was_purged) { 1189 // The bitmap memory was purged by the kernel, but we have all-new zero-filled pages. 1190 // Schedule an update to regenerate the bitmap. 1191 update(); 1192 } 1193 } 1194} 1195 1196Action* Window::action_for_shortcut(Shortcut const& shortcut) 1197{ 1198 return Action::find_action_for_shortcut(*this, shortcut); 1199} 1200 1201void Window::set_base_size(Gfx::IntSize base_size) 1202{ 1203 if (m_base_size == base_size) 1204 return; 1205 m_base_size = base_size; 1206 if (is_visible()) 1207 ConnectionToWindowServer::the().async_set_window_base_size_and_size_increment(m_window_id, m_base_size, m_size_increment); 1208} 1209 1210void Window::set_size_increment(Gfx::IntSize size_increment) 1211{ 1212 if (m_size_increment == size_increment) 1213 return; 1214 m_size_increment = size_increment; 1215 if (is_visible()) 1216 ConnectionToWindowServer::the().async_set_window_base_size_and_size_increment(m_window_id, m_base_size, m_size_increment); 1217} 1218 1219void Window::set_resize_aspect_ratio(Optional<Gfx::IntSize> const& ratio) 1220{ 1221 if (m_resize_aspect_ratio == ratio) 1222 return; 1223 1224 m_resize_aspect_ratio = ratio; 1225 if (is_visible()) 1226 ConnectionToWindowServer::the().async_set_window_resize_aspect_ratio(m_window_id, m_resize_aspect_ratio); 1227} 1228 1229void Window::did_add_widget(Badge<Widget>, Widget&) 1230{ 1231 if (!m_focused_widget) 1232 focus_a_widget_if_possible(FocusSource::Mouse); 1233} 1234 1235void Window::did_remove_widget(Badge<Widget>, Widget& widget) 1236{ 1237 if (m_focused_widget == &widget) 1238 m_focused_widget = nullptr; 1239 if (m_hovered_widget == &widget) 1240 m_hovered_widget = nullptr; 1241 if (m_automatic_cursor_tracking_widget == &widget) 1242 m_automatic_cursor_tracking_widget = nullptr; 1243} 1244 1245void Window::set_progress(Optional<int> progress) 1246{ 1247 VERIFY(m_window_id); 1248 ConnectionToWindowServer::the().async_set_window_progress(m_window_id, progress); 1249} 1250 1251void Window::update_cursor() 1252{ 1253 auto new_cursor = m_cursor; 1254 1255 auto is_usable_cursor = [](auto& cursor) { 1256 return cursor.template has<NonnullRefPtr<Gfx::Bitmap const>>() || cursor.template get<Gfx::StandardCursor>() != Gfx::StandardCursor::None; 1257 }; 1258 1259 // NOTE: If there's an automatic cursor tracking widget, we retain its cursor until tracking stops. 1260 if (auto widget = m_automatic_cursor_tracking_widget) { 1261 if (is_usable_cursor(widget->override_cursor())) 1262 new_cursor = widget->override_cursor(); 1263 } else if (auto widget = m_hovered_widget) { 1264 if (is_usable_cursor(widget->override_cursor())) 1265 new_cursor = widget->override_cursor(); 1266 } 1267 1268 if (are_cursors_the_same(m_effective_cursor, new_cursor)) 1269 return; 1270 m_effective_cursor = new_cursor; 1271 1272 if (new_cursor.has<NonnullRefPtr<Gfx::Bitmap const>>()) 1273 ConnectionToWindowServer::the().async_set_window_custom_cursor(m_window_id, new_cursor.get<NonnullRefPtr<Gfx::Bitmap const>>()->to_shareable_bitmap()); 1274 else 1275 ConnectionToWindowServer::the().async_set_window_cursor(m_window_id, (u32)new_cursor.get<Gfx::StandardCursor>()); 1276} 1277 1278void Window::focus_a_widget_if_possible(FocusSource source) 1279{ 1280 auto focusable_widgets = this->focusable_widgets(source); 1281 if (!focusable_widgets.is_empty()) 1282 set_focused_widget(&focusable_widgets[0], source); 1283} 1284 1285void Window::did_disable_focused_widget(Badge<Widget>) 1286{ 1287 focus_a_widget_if_possible(FocusSource::Mouse); 1288} 1289 1290bool Window::is_active() const 1291{ 1292 VERIFY(Application::the()); 1293 return this == Application::the()->active_window(); 1294} 1295 1296Gfx::Bitmap* Window::back_bitmap() 1297{ 1298 return m_back_store ? &m_back_store->bitmap() : nullptr; 1299} 1300 1301ErrorOr<void> Window::try_add_menu(NonnullRefPtr<Menu> menu) 1302{ 1303 TRY(m_menubar->try_add_menu({}, move(menu))); 1304 if (m_window_id) { 1305 menu->realize_menu_if_needed(); 1306 ConnectionToWindowServer::the().async_add_menu(m_window_id, menu->menu_id()); 1307 } 1308 return {}; 1309} 1310 1311ErrorOr<NonnullRefPtr<Menu>> Window::try_add_menu(DeprecatedString name) 1312{ 1313 auto menu = TRY(m_menubar->try_add_menu({}, move(name))); 1314 if (m_window_id) { 1315 menu->realize_menu_if_needed(); 1316 ConnectionToWindowServer::the().async_add_menu(m_window_id, menu->menu_id()); 1317 } 1318 return menu; 1319} 1320 1321Menu& Window::add_menu(DeprecatedString name) 1322{ 1323 auto menu = MUST(try_add_menu(move(name))); 1324 return *menu; 1325} 1326 1327void Window::flash_menubar_menu_for(MenuItem const& menu_item) 1328{ 1329 if (!Desktop::the().system_effects().flash_menus()) 1330 return; 1331 auto menu_id = menu_item.menu_id(); 1332 if (menu_id < 0) 1333 return; 1334 1335 ConnectionToWindowServer::the().async_flash_menubar_menu(m_window_id, menu_id); 1336} 1337 1338bool Window::is_modified() const 1339{ 1340 if (!m_window_id) 1341 return false; 1342 return ConnectionToWindowServer::the().is_window_modified(m_window_id); 1343} 1344 1345void Window::set_modified(bool modified) 1346{ 1347 if (!m_window_id) 1348 return; 1349 ConnectionToWindowServer::the().async_set_window_modified(m_window_id, modified); 1350} 1351 1352void Window::flush_pending_paints_immediately() 1353{ 1354 if (!m_window_id) 1355 return; 1356 if (m_pending_paint_event_rects.is_empty()) 1357 return; 1358 MultiPaintEvent paint_event(move(m_pending_paint_event_rects), size()); 1359 handle_multi_paint_event(paint_event); 1360} 1361 1362void Window::set_always_on_top(bool always_on_top) 1363{ 1364 if (!m_window_id) 1365 return; 1366 ConnectionToWindowServer::the().set_always_on_top(m_window_id, always_on_top); 1367} 1368 1369}