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