Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/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}