Serenity Operating System
at hosted 737 lines 25 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/HashMap.h> 28#include <AK/JsonObject.h> 29#include <AK/NeverDestroyed.h> 30#include <AK/SharedBuffer.h> 31#include <LibCore/EventLoop.h> 32#include <LibCore/MimeData.h> 33#include <LibGUI/Action.h> 34#include <LibGUI/Application.h> 35#include <LibGUI/Event.h> 36#include <LibGUI/Painter.h> 37#include <LibGUI/Widget.h> 38#include <LibGUI/Window.h> 39#include <LibGUI/WindowServerConnection.h> 40#include <LibGfx/Bitmap.h> 41#include <serenity.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <unistd.h> 45 46//#define UPDATE_COALESCING_DEBUG 47 48namespace GUI { 49 50static NeverDestroyed<HashTable<Window*>> all_windows; 51static NeverDestroyed<HashMap<int, Window*>> reified_windows; 52 53Window* Window::from_window_id(int window_id) 54{ 55 auto it = reified_windows->find(window_id); 56 if (it != reified_windows->end()) 57 return (*it).value; 58 return nullptr; 59} 60 61Window::Window(Core::Object* parent) 62 : Core::Object(parent) 63{ 64 all_windows->set(this); 65 m_rect_when_windowless = { 100, 400, 140, 140 }; 66 m_title_when_windowless = "GUI::Window"; 67} 68 69Window::~Window() 70{ 71 all_windows->remove(this); 72 hide(); 73} 74 75void Window::close() 76{ 77 hide(); 78} 79 80void Window::move_to_front() 81{ 82 if (!is_visible()) 83 return; 84 85 WindowServerConnection::the().send_sync<Messages::WindowServer::MoveWindowToFront>(m_window_id); 86} 87 88void Window::show() 89{ 90 if (is_visible()) 91 return; 92 m_override_cursor = StandardCursor::None; 93 auto response = WindowServerConnection::the().send_sync<Messages::WindowServer::CreateWindow>( 94 m_rect_when_windowless, 95 m_has_alpha_channel, 96 m_modal, 97 m_minimizable, 98 m_resizable, 99 m_fullscreen, 100 m_show_titlebar, 101 m_opacity_when_windowless, 102 m_base_size, 103 m_size_increment, 104 (i32)m_window_type, 105 m_title_when_windowless); 106 m_window_id = response->window_id(); 107 m_visible = true; 108 109 apply_icon(); 110 111 reified_windows->set(m_window_id, this); 112 Application::the().did_create_window({}); 113 update(); 114} 115 116void Window::hide() 117{ 118 if (!is_visible()) 119 return; 120 reified_windows->remove(m_window_id); 121 WindowServerConnection::the().send_sync<Messages::WindowServer::DestroyWindow>(m_window_id); 122 m_window_id = 0; 123 m_visible = false; 124 m_pending_paint_event_rects.clear(); 125 m_back_bitmap = nullptr; 126 m_front_bitmap = nullptr; 127 m_override_cursor = StandardCursor::None; 128 129 bool app_has_visible_windows = false; 130 for (auto& window : *all_windows) { 131 if (window->is_visible()) { 132 app_has_visible_windows = true; 133 break; 134 } 135 } 136 if (!app_has_visible_windows) 137 Application::the().did_delete_last_window({}); 138} 139 140void Window::set_title(const StringView& title) 141{ 142 m_title_when_windowless = title; 143 if (!is_visible()) 144 return; 145 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowTitle>(m_window_id, title); 146} 147 148String Window::title() const 149{ 150 if (!is_visible()) 151 return m_title_when_windowless; 152 return WindowServerConnection::the().send_sync<Messages::WindowServer::GetWindowTitle>(m_window_id)->title(); 153} 154 155Gfx::Rect Window::rect() const 156{ 157 if (!is_visible()) 158 return m_rect_when_windowless; 159 return WindowServerConnection::the().send_sync<Messages::WindowServer::GetWindowRect>(m_window_id)->rect(); 160} 161 162void Window::set_rect(const Gfx::Rect& a_rect) 163{ 164 m_rect_when_windowless = a_rect; 165 if (!is_visible()) { 166 if (m_main_widget) 167 m_main_widget->resize(m_rect_when_windowless.size()); 168 return; 169 } 170 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowRect>(m_window_id, a_rect); 171 if (m_back_bitmap && m_back_bitmap->size() != a_rect.size()) 172 m_back_bitmap = nullptr; 173 if (m_front_bitmap && m_front_bitmap->size() != a_rect.size()) 174 m_front_bitmap = nullptr; 175 if (m_main_widget) 176 m_main_widget->resize(a_rect.size()); 177} 178 179void Window::set_window_type(WindowType window_type) 180{ 181 m_window_type = window_type; 182} 183 184void Window::set_override_cursor(StandardCursor cursor) 185{ 186 if (!is_visible()) 187 return; 188 if (m_override_cursor == cursor) 189 return; 190 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowOverrideCursor>(m_window_id, (u32)cursor); 191 m_override_cursor = cursor; 192} 193 194void Window::event(Core::Event& event) 195{ 196 if (event.type() == Event::Drop) { 197 auto& drop_event = static_cast<DropEvent&>(event); 198 if (!m_main_widget) 199 return; 200 auto result = m_main_widget->hit_test(drop_event.position()); 201 auto local_event = make<DropEvent>(result.local_position, drop_event.text(), drop_event.mime_data()); 202 ASSERT(result.widget); 203 return result.widget->dispatch_event(*local_event, this); 204 } 205 206 if (event.type() == Event::MouseUp || event.type() == Event::MouseDown || event.type() == Event::MouseDoubleClick || event.type() == Event::MouseMove || event.type() == Event::MouseWheel) { 207 auto& mouse_event = static_cast<MouseEvent&>(event); 208 if (m_global_cursor_tracking_widget) { 209 auto window_relative_rect = m_global_cursor_tracking_widget->window_relative_rect(); 210 Gfx::Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() }; 211 auto local_event = make<MouseEvent>((Event::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta()); 212 m_global_cursor_tracking_widget->dispatch_event(*local_event, this); 213 return; 214 } 215 if (m_automatic_cursor_tracking_widget) { 216 auto window_relative_rect = m_automatic_cursor_tracking_widget->window_relative_rect(); 217 Gfx::Point local_point { mouse_event.x() - window_relative_rect.x(), mouse_event.y() - window_relative_rect.y() }; 218 auto local_event = make<MouseEvent>((Event::Type)event.type(), local_point, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta()); 219 m_automatic_cursor_tracking_widget->dispatch_event(*local_event, this); 220 if (mouse_event.buttons() == 0) 221 m_automatic_cursor_tracking_widget = nullptr; 222 return; 223 } 224 if (!m_main_widget) 225 return; 226 auto result = m_main_widget->hit_test(mouse_event.position()); 227 auto local_event = make<MouseEvent>((Event::Type)event.type(), result.local_position, mouse_event.buttons(), mouse_event.button(), mouse_event.modifiers(), mouse_event.wheel_delta()); 228 ASSERT(result.widget); 229 set_hovered_widget(result.widget); 230 if (mouse_event.buttons() != 0 && !m_automatic_cursor_tracking_widget) 231 m_automatic_cursor_tracking_widget = result.widget->make_weak_ptr(); 232 if (result.widget != m_global_cursor_tracking_widget.ptr()) 233 return result.widget->dispatch_event(*local_event, this); 234 return; 235 } 236 237 if (event.type() == Event::MultiPaint) { 238 if (!is_visible()) 239 return; 240 if (!m_main_widget) 241 return; 242 auto& paint_event = static_cast<MultiPaintEvent&>(event); 243 auto rects = paint_event.rects(); 244 ASSERT(!rects.is_empty()); 245 if (m_back_bitmap && m_back_bitmap->size() != paint_event.window_size()) { 246 // Eagerly discard the backing store if we learn from this paint event that it needs to be bigger. 247 // Otherwise we would have to wait for a resize event to tell us. This way we don't waste the 248 // effort on painting into an undersized bitmap that will be thrown away anyway. 249 m_back_bitmap = nullptr; 250 } 251 bool created_new_backing_store = !m_back_bitmap; 252 if (!m_back_bitmap) { 253 m_back_bitmap = create_backing_bitmap(paint_event.window_size()); 254 } else if (m_double_buffering_enabled) { 255 bool still_has_pixels = m_back_bitmap->shared_buffer()->set_nonvolatile(); 256 if (!still_has_pixels) { 257 m_back_bitmap = create_backing_bitmap(paint_event.window_size()); 258 created_new_backing_store = true; 259 } 260 } 261 262 auto rect = rects.first(); 263 if (rect.is_empty() || created_new_backing_store) { 264 rects.clear(); 265 rects.append({ {}, paint_event.window_size() }); 266 } 267 268 for (auto& rect : rects) 269 m_main_widget->dispatch_event(*make<PaintEvent>(rect), this); 270 271 if (m_double_buffering_enabled) 272 flip(rects); 273 else if (created_new_backing_store) 274 set_current_backing_bitmap(*m_back_bitmap, true); 275 276 if (is_visible()) { 277 Vector<Gfx::Rect> rects_to_send; 278 for (auto& r : rects) 279 rects_to_send.append(r); 280 WindowServerConnection::the().post_message(Messages::WindowServer::DidFinishPainting(m_window_id, rects_to_send)); 281 } 282 return; 283 } 284 285 if (event.type() == Event::KeyUp || event.type() == Event::KeyDown) { 286 if (m_focused_widget) 287 return m_focused_widget->dispatch_event(event, this); 288 if (m_main_widget) 289 return m_main_widget->dispatch_event(event, this); 290 return; 291 } 292 293 if (event.type() == Event::WindowBecameActive || event.type() == Event::WindowBecameInactive) { 294 m_is_active = event.type() == Event::WindowBecameActive; 295 if (m_main_widget) 296 m_main_widget->dispatch_event(event, this); 297 if (m_focused_widget) 298 m_focused_widget->update(); 299 return; 300 } 301 302 if (event.type() == Event::WindowCloseRequest) { 303 if (on_close_request) { 304 if (on_close_request() == Window::CloseRequestDecision::StayOpen) 305 return; 306 } 307 close(); 308 return; 309 } 310 311 if (event.type() == Event::WindowLeft) { 312 set_hovered_widget(nullptr); 313 return; 314 } 315 316 if (event.type() == Event::Resize) { 317 auto new_size = static_cast<ResizeEvent&>(event).size(); 318 if (m_back_bitmap && m_back_bitmap->size() != new_size) 319 m_back_bitmap = nullptr; 320 if (!m_pending_paint_event_rects.is_empty()) { 321 m_pending_paint_event_rects.clear_with_capacity(); 322 m_pending_paint_event_rects.append({ {}, new_size }); 323 } 324 m_rect_when_windowless = { {}, new_size }; 325 m_main_widget->set_relative_rect({ {}, new_size }); 326 return; 327 } 328 329 if (event.type() > Event::__Begin_WM_Events && event.type() < Event::__End_WM_Events) 330 return wm_event(static_cast<WMEvent&>(event)); 331 332 if (event.type() == Event::DragMove) { 333 if (!m_main_widget) 334 return; 335 auto& drag_event = static_cast<DragEvent&>(event); 336 auto result = m_main_widget->hit_test(drag_event.position()); 337 auto local_event = make<DragEvent>(static_cast<Event::Type>(drag_event.type()), result.local_position, drag_event.data_type()); 338 ASSERT(result.widget); 339 return result.widget->dispatch_event(*local_event, this); 340 } 341 342 if (event.type() == Event::ThemeChange) { 343 if (!m_main_widget) 344 return; 345 auto theme_event = static_cast<ThemeChangeEvent&>(event); 346 auto dispatch_theme_change = [&](auto& widget, auto recursive) { 347 widget.dispatch_event(theme_event, this); 348 widget.for_each_child_widget([&](auto& widget) -> IterationDecision { 349 widget.dispatch_event(theme_event, this); 350 recursive(widget, recursive); 351 return IterationDecision::Continue; 352 }); 353 }; 354 dispatch_theme_change(*m_main_widget.ptr(), dispatch_theme_change); 355 return; 356 } 357 358 Core::Object::event(event); 359} 360 361bool Window::is_visible() const 362{ 363 return m_visible; 364} 365 366void Window::update() 367{ 368 auto rect = this->rect(); 369 update({ 0, 0, rect.width(), rect.height() }); 370} 371 372void Window::force_update() 373{ 374 if (!is_visible()) 375 return; 376 auto rect = this->rect(); 377 WindowServerConnection::the().post_message(Messages::WindowServer::InvalidateRect(m_window_id, { { 0, 0, rect.width(), rect.height() } }, true)); 378} 379 380void Window::update(const Gfx::Rect& a_rect) 381{ 382 if (!is_visible()) 383 return; 384 385 for (auto& pending_rect : m_pending_paint_event_rects) { 386 if (pending_rect.contains(a_rect)) { 387#ifdef UPDATE_COALESCING_DEBUG 388 dbgprintf("Ignoring %s since it's contained by pending rect %s\n", a_rect.to_string().characters(), pending_rect.to_string().characters()); 389#endif 390 return; 391 } 392 } 393 394 if (m_pending_paint_event_rects.is_empty()) { 395 deferred_invoke([this](auto&) { 396 auto rects = move(m_pending_paint_event_rects); 397 if (rects.is_empty()) 398 return; 399 Vector<Gfx::Rect> rects_to_send; 400 for (auto& r : rects) 401 rects_to_send.append(r); 402 WindowServerConnection::the().post_message(Messages::WindowServer::InvalidateRect(m_window_id, rects_to_send, false)); 403 }); 404 } 405 m_pending_paint_event_rects.append(a_rect); 406} 407 408void Window::set_main_widget(Widget* widget) 409{ 410 if (m_main_widget == widget) 411 return; 412 if (m_main_widget) 413 remove_child(*m_main_widget); 414 m_main_widget = widget; 415 if (m_main_widget) { 416 add_child(*widget); 417 auto new_window_rect = rect(); 418 if (m_main_widget->horizontal_size_policy() == SizePolicy::Fixed) 419 new_window_rect.set_width(m_main_widget->preferred_size().width()); 420 if (m_main_widget->vertical_size_policy() == SizePolicy::Fixed) 421 new_window_rect.set_height(m_main_widget->preferred_size().height()); 422 set_rect(new_window_rect); 423 m_main_widget->set_relative_rect({ {}, new_window_rect.size() }); 424 m_main_widget->set_window(this); 425 if (m_main_widget->accepts_focus()) 426 m_main_widget->set_focus(true); 427 } 428 update(); 429} 430 431void Window::set_focused_widget(Widget* widget) 432{ 433 if (m_focused_widget == widget) 434 return; 435 if (m_focused_widget) { 436 Core::EventLoop::current().post_event(*m_focused_widget, make<Event>(Event::FocusOut)); 437 m_focused_widget->update(); 438 } 439 m_focused_widget = widget ? widget->make_weak_ptr() : nullptr; 440 if (m_focused_widget) { 441 Core::EventLoop::current().post_event(*m_focused_widget, make<Event>(Event::FocusIn)); 442 m_focused_widget->update(); 443 } 444} 445 446void Window::set_global_cursor_tracking_widget(Widget* widget) 447{ 448 if (widget == m_global_cursor_tracking_widget) 449 return; 450 m_global_cursor_tracking_widget = widget ? widget->make_weak_ptr() : nullptr; 451} 452 453void Window::set_automatic_cursor_tracking_widget(Widget* widget) 454{ 455 if (widget == m_automatic_cursor_tracking_widget) 456 return; 457 m_automatic_cursor_tracking_widget = widget ? widget->make_weak_ptr() : nullptr; 458} 459 460void Window::set_has_alpha_channel(bool value) 461{ 462 if (m_has_alpha_channel == value) 463 return; 464 m_has_alpha_channel = value; 465 if (!is_visible()) 466 return; 467 468 m_pending_paint_event_rects.clear(); 469 m_back_bitmap = nullptr; 470 m_front_bitmap = nullptr; 471 472 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowHasAlphaChannel>(m_window_id, value); 473 update(); 474} 475 476void Window::set_double_buffering_enabled(bool value) 477{ 478 ASSERT(!is_visible()); 479 m_double_buffering_enabled = value; 480} 481 482void Window::set_opacity(float opacity) 483{ 484 m_opacity_when_windowless = opacity; 485 if (!is_visible()) 486 return; 487 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowOpacity>(m_window_id, opacity); 488} 489 490void Window::set_hovered_widget(Widget* widget) 491{ 492 if (widget == m_hovered_widget) 493 return; 494 495 if (m_hovered_widget) 496 Core::EventLoop::current().post_event(*m_hovered_widget, make<Event>(Event::Leave)); 497 498 m_hovered_widget = widget ? widget->make_weak_ptr() : nullptr; 499 500 if (m_hovered_widget) 501 Core::EventLoop::current().post_event(*m_hovered_widget, make<Event>(Event::Enter)); 502} 503 504void Window::set_current_backing_bitmap(Gfx::Bitmap& bitmap, bool flush_immediately) 505{ 506 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowBackingStore>(m_window_id, 32, bitmap.pitch(), bitmap.shbuf_id(), bitmap.has_alpha_channel(), bitmap.size(), flush_immediately); 507} 508 509void Window::flip(const Vector<Gfx::Rect, 32>& dirty_rects) 510{ 511 swap(m_front_bitmap, m_back_bitmap); 512 513 set_current_backing_bitmap(*m_front_bitmap); 514 515 if (!m_back_bitmap || m_back_bitmap->size() != m_front_bitmap->size()) { 516 m_back_bitmap = create_backing_bitmap(m_front_bitmap->size()); 517 memcpy(m_back_bitmap->scanline(0), m_front_bitmap->scanline(0), m_front_bitmap->size_in_bytes()); 518 m_back_bitmap->shared_buffer()->set_volatile(); 519 return; 520 } 521 522 // Copy whatever was painted from the front to the back. 523 Painter painter(*m_back_bitmap); 524 for (auto& dirty_rect : dirty_rects) 525 painter.blit(dirty_rect.location(), *m_front_bitmap, dirty_rect); 526 527 m_back_bitmap->shared_buffer()->set_volatile(); 528} 529 530NonnullRefPtr<Gfx::Bitmap> Window::create_shared_bitmap(Gfx::BitmapFormat format, const Gfx::Size& size) 531{ 532 ASSERT(WindowServerConnection::the().server_pid()); 533 ASSERT(!size.is_empty()); 534 size_t pitch = round_up_to_power_of_two(size.width() * sizeof(Gfx::RGBA32), 16); 535 size_t size_in_bytes = size.height() * pitch; 536 auto shared_buffer = SharedBuffer::create_with_size(size_in_bytes); 537 ASSERT(shared_buffer); 538 shared_buffer->share_with(WindowServerConnection::the().server_pid()); 539 return Gfx::Bitmap::create_with_shared_buffer(format, *shared_buffer, size); 540} 541 542NonnullRefPtr<Gfx::Bitmap> Window::create_backing_bitmap(const Gfx::Size& size) 543{ 544 auto format = m_has_alpha_channel ? Gfx::BitmapFormat::RGBA32 : Gfx::BitmapFormat::RGB32; 545 return create_shared_bitmap(format, size); 546} 547 548void Window::set_modal(bool modal) 549{ 550 ASSERT(!is_visible()); 551 m_modal = modal; 552} 553 554void Window::wm_event(WMEvent&) 555{ 556} 557 558void Window::set_icon(const Gfx::Bitmap* icon) 559{ 560 if (m_icon == icon) 561 return; 562 563 m_icon = create_shared_bitmap(Gfx::BitmapFormat::RGBA32, icon->size()); 564 { 565 Painter painter(*m_icon); 566 painter.blit({ 0, 0 }, *icon, icon->rect()); 567 } 568 569 apply_icon(); 570} 571 572void Window::apply_icon() 573{ 574 if (!m_icon) 575 return; 576 577 if (!is_visible()) 578 return; 579 580 int rc = shbuf_seal(m_icon->shbuf_id()); 581 ASSERT(rc == 0); 582 583 rc = shbuf_allow_all(m_icon->shbuf_id()); 584 ASSERT(rc == 0); 585 586 static bool has_set_process_icon; 587 if (!has_set_process_icon) 588 set_process_icon(m_icon->shbuf_id()); 589 590 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowIconBitmap>(m_window_id, m_icon->to_shareable_bitmap(WindowServerConnection::the().server_pid())); 591} 592 593void Window::start_wm_resize() 594{ 595 WindowServerConnection::the().post_message(Messages::WindowServer::WM_StartWindowResize(WindowServerConnection::the().my_client_id(), m_window_id)); 596} 597 598Vector<Widget*> Window::focusable_widgets() const 599{ 600 if (!m_main_widget) 601 return {}; 602 603 Vector<Widget*> collected_widgets; 604 605 Function<void(Widget&)> collect_focusable_widgets = [&](auto& widget) { 606 if (widget.accepts_focus()) 607 collected_widgets.append(&widget); 608 widget.for_each_child_widget([&](auto& child) { 609 if (!child.is_visible()) 610 return IterationDecision::Continue; 611 if (!child.is_enabled()) 612 return IterationDecision::Continue; 613 collect_focusable_widgets(child); 614 return IterationDecision::Continue; 615 }); 616 }; 617 618 collect_focusable_widgets(const_cast<Widget&>(*m_main_widget)); 619 return collected_widgets; 620} 621 622void Window::save_to(AK::JsonObject& json) 623{ 624 json.set("title", title()); 625 json.set("visible", is_visible()); 626 json.set("active", is_active()); 627 json.set("minimizable", is_minimizable()); 628 json.set("resizable", is_resizable()); 629 json.set("fullscreen", is_fullscreen()); 630 json.set("rect", rect().to_string()); 631 json.set("base_size", base_size().to_string()); 632 json.set("size_increment", size_increment().to_string()); 633 Core::Object::save_to(json); 634} 635 636void Window::set_fullscreen(bool fullscreen) 637{ 638 if (m_fullscreen == fullscreen) 639 return; 640 m_fullscreen = fullscreen; 641 if (!is_visible()) 642 return; 643 WindowServerConnection::the().send_sync<Messages::WindowServer::SetFullscreen>(m_window_id, fullscreen); 644} 645 646void Window::schedule_relayout() 647{ 648 if (m_layout_pending) 649 return; 650 m_layout_pending = true; 651 deferred_invoke([this](auto&) { 652 if (main_widget()) 653 main_widget()->do_layout(); 654 update(); 655 m_layout_pending = false; 656 }); 657} 658 659void Window::for_each_window(Badge<WindowServerConnection>, Function<void(Window&)> callback) 660{ 661 for (auto& e : *reified_windows) { 662 ASSERT(e.value); 663 callback(*e.value); 664 } 665} 666 667void Window::update_all_windows(Badge<WindowServerConnection>) 668{ 669 for (auto& e : *reified_windows) { 670 e.value->force_update(); 671 } 672} 673 674void Window::notify_state_changed(Badge<WindowServerConnection>, bool minimized, bool occluded) 675{ 676 m_visible_for_timer_purposes = !minimized && !occluded; 677 678 // When double buffering is enabled, minimization/occlusion means we can mark the front bitmap volatile (in addition to the back bitmap.) 679 // When double buffering is disabled, there is only the back bitmap (which we can now mark volatile!) 680 RefPtr<Gfx::Bitmap>& bitmap = m_double_buffering_enabled ? m_front_bitmap : m_back_bitmap; 681 if (!bitmap) 682 return; 683 if (minimized || occluded) { 684 bitmap->shared_buffer()->set_volatile(); 685 } else { 686 if (!bitmap->shared_buffer()->set_nonvolatile()) { 687 bitmap = nullptr; 688 update(); 689 } 690 } 691} 692 693Action* Window::action_for_key_event(const KeyEvent& event) 694{ 695 Shortcut shortcut(event.modifiers(), (KeyCode)event.key()); 696 Action* found_action = nullptr; 697 for_each_child_of_type<Action>([&](auto& action) { 698 if (action.shortcut() == shortcut) { 699 found_action = &action; 700 return IterationDecision::Break; 701 } 702 return IterationDecision::Continue; 703 }); 704 return found_action; 705} 706 707void Window::set_base_size(const Gfx::Size& base_size) 708{ 709 if (m_base_size == base_size) 710 return; 711 m_base_size = base_size; 712 if (is_visible()) 713 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowBaseSizeAndSizeIncrement>(m_window_id, m_base_size, m_size_increment); 714} 715 716void Window::set_size_increment(const Gfx::Size& size_increment) 717{ 718 if (m_size_increment == size_increment) 719 return; 720 m_size_increment = size_increment; 721 if (is_visible()) 722 WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowBaseSizeAndSizeIncrement>(m_window_id, m_base_size, m_size_increment); 723} 724 725void Window::did_remove_widget(Badge<Widget>, const Widget& widget) 726{ 727 if (m_focused_widget == &widget) 728 m_focused_widget = nullptr; 729 if (m_hovered_widget == &widget) 730 m_hovered_widget = nullptr; 731 if (m_global_cursor_tracking_widget == &widget) 732 m_global_cursor_tracking_widget = nullptr; 733 if (m_automatic_cursor_tracking_widget == &widget) 734 m_automatic_cursor_tracking_widget = nullptr; 735} 736 737}