1// Aseprite UI Library
2// Copyright (C) 2018-2022 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8// #define REPORT_EVENTS
9// #define REPORT_FOCUS_MOVEMENT
10// #define DEBUG_PAINT_EVENTS
11// #define LIMIT_DISPATCH_TIME
12// #define DEBUG_UI_THREADS
13#define GARBAGE_TRACE(...)
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include "ui/manager.h"
20
21#include "base/concurrent_queue.h"
22#include "base/scoped_value.h"
23#include "base/time.h"
24#include "os/event.h"
25#include "os/event_queue.h"
26#include "os/surface.h"
27#include "os/system.h"
28#include "os/window.h"
29#include "os/window_spec.h"
30#include "ui/intern.h"
31#include "ui/ui.h"
32
33#if defined(DEBUG_PAINT_EVENTS) || defined(DEBUG_UI_THREADS)
34#include "base/thread.h"
35#endif
36
37#include <algorithm>
38#include <limits>
39#include <list>
40#include <memory>
41#include <utility>
42#include <vector>
43
44#if defined(_WIN32) && defined(DEBUG_PAINT_EVENTS)
45 #define WIN32_LEAN_AND_MEAN
46 #include <windows.h>
47 #undef min
48 #undef max
49#endif
50
51namespace ui {
52
53namespace {
54
55// The redraw state is used to avoid drawing the manager when a window
56// has been just closed by the user, so we delay the redrawing (the
57// kPaintMessages generation) for the next generateMessages() round.
58enum class RedrawState {
59 Normal,
60 AWindowHasJustBeenClosed,
61 RedrawDelayed,
62 ClosingApp,
63};
64RedrawState redrawState = RedrawState::Normal;
65
66} // anonymous namespace
67
68static const int NFILTERS = (int)(kFirstRegisteredMessage+1);
69
70struct Filter {
71 int message;
72 Widget* widget;
73
74 Filter(int message, Widget* widget)
75 : message(message)
76 , widget(widget) { }
77};
78
79typedef std::list<Message*> Messages;
80typedef std::list<Filter*> Filters;
81
82Manager* Manager::m_defaultManager = nullptr;
83
84#ifdef DEBUG_UI_THREADS
85static base::thread::native_id_type manager_thread = 0;
86#endif
87
88static WidgetsList mouse_widgets_list; // List of widgets to send mouse events
89static Messages msg_queue; // Messages queue
90static Messages used_msg_queue; // Messages queue
91static base::concurrent_queue<Message*> concurrent_msg_queue;
92static Filters msg_filters[NFILTERS]; // Filters for every enqueued message
93static int filter_locks = 0;
94
95// Current display with the mouse, used to avoid processing a
96// os::Event::MouseLeave of the non-current display/window as when we
97// move the mouse between two windows we can receive:
98//
99// 1. A os::Event::MouseEnter of the new window
100// 2. A os::Event::MouseLeave of the old window
101//
102// Instead of the MouseLeave event of the old window first.
103static Display* mouse_display = nullptr;
104
105static Widget* focus_widget; // The widget with the focus
106static Widget* mouse_widget; // The widget with the mouse
107static Widget* capture_widget; // The widget that captures the mouse
108
109static bool first_time = true; // true when we don't enter in poll yet
110
111// Don't adjust window positions automatically when it's false. Used
112// when Screen/UI scaling is changed to avoid adjusting windows as
113// when the os::Display is resized by the user.
114static bool auto_window_adjustment = true;
115
116// Keyboard focus movement stuff
117inline bool does_accept_focus(Widget* widget)
118{
119 return ((((widget)->flags() & (FOCUS_STOP |
120 DISABLED |
121 HIDDEN |
122 DECORATIVE)) == FOCUS_STOP) &&
123 ((widget)->isVisible()));
124}
125
126static int count_widgets_accept_focus(Widget* widget);
127static bool child_accept_focus(Widget* widget, bool first);
128static Widget* next_widget(Widget* widget);
129static int cmp_left(Widget* widget, int x, int y);
130static int cmp_right(Widget* widget, int x, int y);
131static int cmp_up(Widget* widget, int x, int y);
132static int cmp_down(Widget* widget, int x, int y);
133
134namespace {
135
136class LockFilters {
137public:
138 LockFilters() {
139 ++filter_locks;
140 }
141 ~LockFilters() {
142 ASSERT(filter_locks > 0);
143 --filter_locks;
144
145 if (filter_locks == 0) {
146 // Clear empty filters
147 for (Filters& msg_filter : msg_filters) {
148 for (auto it = msg_filter.begin(); it != msg_filter.end(); ) {
149 Filter* filter = *it;
150 if (filter->widget == nullptr) {
151 delete filter;
152 it = msg_filter.erase(it);
153 }
154 else {
155 ++it;
156 }
157 }
158 }
159 }
160 }
161};
162
163os::Hit handle_native_hittest(os::Window* osWindow,
164 const gfx::Point& pos)
165{
166 Display* display = Manager::getDisplayFromNativeWindow(osWindow);
167 if (display) {
168 auto window = static_cast<Window*>(display->containedWidget());
169 switch (window->hitTest(pos)) {
170 case HitTestNowhere: return os::Hit::Content;
171 case HitTestCaption: return os::Hit::TitleBar;
172 case HitTestClient: return os::Hit::Content;
173 case HitTestBorderNW: return os::Hit::TopLeft;
174 case HitTestBorderN: return os::Hit::Top;
175 case HitTestBorderNE: return os::Hit::TopRight;
176 case HitTestBorderE: return os::Hit::Right;
177 case HitTestBorderSE: return os::Hit::BottomRight;
178 case HitTestBorderS: return os::Hit::Bottom;
179 case HitTestBorderSW: return os::Hit::BottomLeft;
180 case HitTestBorderW: return os::Hit::Left;
181 }
182 }
183 return os::Hit::None;
184}
185
186} // anonymous namespace
187
188// static
189bool Manager::widgetAssociatedToManager(Widget* widget)
190{
191 return (focus_widget == widget ||
192 mouse_widget == widget ||
193 capture_widget == widget ||
194 std::find(mouse_widgets_list.begin(),
195 mouse_widgets_list.end(),
196 widget) != mouse_widgets_list.end());
197}
198
199Manager::Manager(const os::WindowRef& nativeWindow)
200 : Widget(kManagerWidget)
201 , m_display(nullptr, nativeWindow, this)
202 , m_eventQueue(os::instance()->eventQueue())
203 , m_lockedWindow(nullptr)
204 , m_mouseButton(kButtonNone)
205{
206 // The native window can be nullptr when running tests
207 if (nativeWindow)
208 nativeWindow->setUserData(&m_display);
209
210#ifdef DEBUG_UI_THREADS
211 ASSERT(!manager_thread);
212 manager_thread = base::this_thread::native_id();
213#endif
214
215 if (!m_defaultManager) {
216 // Empty lists
217 ASSERT(msg_queue.empty());
218 mouse_widgets_list.clear();
219
220 // Reset variables
221 focus_widget = nullptr;
222 mouse_widget = nullptr;
223 capture_widget = nullptr;
224 }
225
226 setBounds(m_display.bounds());
227 setVisible(true);
228
229 // Default manager is the first one (and is always visible).
230 if (!m_defaultManager)
231 m_defaultManager = this;
232
233 // TODO check if this is needed
234 onNewDisplayConfiguration(&m_display);
235}
236
237Manager::~Manager()
238{
239#ifdef DEBUG_UI_THREADS
240 ASSERT(manager_thread == base::this_thread::native_id());
241#endif
242
243 // There are some messages in queue? Dispatch everything.
244 dispatchMessages();
245 collectGarbage();
246
247 // Finish the main manager.
248 if (m_defaultManager == this) {
249 // No more cursor
250 set_mouse_cursor(kNoCursor);
251
252 // Check timers & filters
253#ifdef _DEBUG
254 if (get_app_state() != AppState::kClosingWithException) {
255 ASSERT(!Timer::haveTimers());
256 for (Filters& msg_filter : msg_filters)
257 ASSERT(msg_filter.empty());
258 }
259 ASSERT(msg_queue.empty());
260#endif
261
262 // No more default manager
263 m_defaultManager = nullptr;
264
265 // Shutdown system
266 mouse_widgets_list.clear();
267 }
268}
269
270// static
271Display* Manager::getDisplayFromNativeWindow(os::Window* window)
272{
273 if (window)
274 return window->userData<Display>();
275 else
276 return nullptr;
277}
278
279void Manager::run()
280{
281 MessageLoop loop(this);
282
283 if (first_time) {
284 first_time = false;
285
286 invalidate();
287 set_mouse_cursor(kArrowCursor);
288 }
289
290 while (!children().empty())
291 loop.pumpMessages();
292}
293
294void Manager::flipAllDisplays()
295{
296 OverlayManager* overlays = OverlayManager::instance();
297
298 update_cursor_overlay();
299
300 // Draw overlays.
301 overlays->drawOverlays();
302
303 m_display.flipDisplay();
304 if (get_multiple_displays()) {
305 for (auto child : children()) {
306 auto window = static_cast<Window*>(child);
307 if (window->ownDisplay())
308 window->display()->flipDisplay();
309 }
310 }
311}
312
313void Manager::updateAllDisplaysWithNewScale(int scale)
314{
315 os::Window* nativeWindow = m_display.nativeWindow();
316 nativeWindow->setScale(scale);
317
318 if (get_multiple_displays()) {
319 for (auto child : children()) {
320 auto window = static_cast<Window*>(child);
321 if (window->ownDisplay()) {
322 Display* display = static_cast<Window*>(child)->display();
323 display->nativeWindow()->setScale(scale);
324 onNewDisplayConfiguration(display);
325 }
326 }
327 }
328
329 onNewDisplayConfiguration(&m_display);
330}
331
332bool Manager::generateMessages()
333{
334#ifdef DEBUG_UI_THREADS
335 ASSERT(manager_thread == base::this_thread::native_id());
336#endif
337
338 // First check: there are windows to manage?
339 if (children().empty())
340 return false;
341
342 // Generate messages from other threads
343 if (!concurrent_msg_queue.empty()) {
344 Message* msg = nullptr;
345 while (concurrent_msg_queue.try_pop(msg))
346 msg_queue.push_back(msg);
347 }
348
349 // Generate messages from OS input
350 generateMessagesFromOSEvents();
351
352 // Generate messages for timers
353 Timer::pollTimers();
354
355 // Returns true if we have to dispatch messages (if the redraw was
356 // delayed, we have to pump messages because there is where paint
357 // messages are flushed)
358 if (!msg_queue.empty() || redrawState != RedrawState::Normal)
359 return true;
360 else
361 return false;
362}
363
364void Manager::generateSetCursorMessage(Display* display,
365 const gfx::Point& mousePos,
366 KeyModifiers modifiers,
367 PointerType pointerType)
368{
369 if (get_mouse_cursor() == kOutsideDisplay)
370 return;
371
372 Widget* dst = (capture_widget ? capture_widget: mouse_widget);
373 if (dst)
374 enqueueMessage(
375 newMouseMessage(
376 kSetCursorMessage,
377 display, dst,
378 mousePos,
379 pointerType,
380 m_mouseButton,
381 modifiers));
382 else
383 set_mouse_cursor(kArrowCursor);
384}
385
386static MouseButton mouse_button_from_os_to_ui(const os::Event& osEvent)
387{
388 static_assert((int)os::Event::NoneButton == (int)ui::kButtonNone &&
389 (int)os::Event::LeftButton == (int)ui::kButtonLeft &&
390 (int)os::Event::RightButton == (int)ui::kButtonRight &&
391 (int)os::Event::MiddleButton == (int)ui::kButtonMiddle &&
392 (int)os::Event::X1Button == (int)ui::kButtonX1 &&
393 (int)os::Event::X2Button == (int)ui::kButtonX2,
394 "Mouse button constants do not match");
395 return (MouseButton)osEvent.button();
396}
397
398void Manager::generateMessagesFromOSEvents()
399{
400#ifdef DEBUG_UI_THREADS
401 ASSERT(manager_thread == base::this_thread::native_id());
402#endif
403
404 os::Event lastMouseMoveEvent;
405
406 // Events from laf-os
407 os::Event osEvent;
408 for (;;) {
409 // Calculate how much time we can wait for the next message in the
410 // event queue.
411 double timeout = 0.0;
412 if (msg_queue.empty() && redrawState == RedrawState::Normal) {
413 if (!Timer::getNextTimeout(timeout))
414 timeout = os::EventQueue::kWithoutTimeout;
415 }
416
417 if (timeout == os::EventQueue::kWithoutTimeout && used_msg_queue.empty())
418 collectGarbage();
419#if _DEBUG
420 else if (!m_garbage.empty()) {
421 GARBAGE_TRACE("collectGarbage() wasn't called #objects=%d"
422 " (msg_queue=%d used_msg_queue=%d redrawState=%d timeout=%.16g)\n",
423 int(m_garbage.size()),
424 msg_queue.size(),
425 used_msg_queue.size(),
426 int(redrawState),
427 timeout);
428 }
429#endif
430
431 m_eventQueue->getEvent(osEvent, timeout);
432 if (osEvent.type() == os::Event::None)
433 break;
434
435 Display* display = getDisplayFromNativeWindow(osEvent.window().get());
436 if (!display)
437 display = this->display();
438
439 switch (osEvent.type()) {
440
441 case os::Event::CloseApp: {
442 Message* msg = new Message(kCloseDisplayMessage);
443 msg->setDisplay(display);
444 msg->setRecipient(this);
445 msg->setPropagateToChildren(true);
446 enqueueMessage(msg);
447 break;
448 }
449
450 case os::Event::CloseWindow: {
451 Message* msg = new Message(kCloseDisplayMessage);
452 msg->setDisplay(display);
453 msg->setRecipient(this);
454 msg->setPropagateToChildren(true);
455 enqueueMessage(msg);
456 break;
457 }
458
459 case os::Event::ResizeWindow: {
460 Message* msg = new Message(kResizeDisplayMessage);
461 msg->setDisplay(display);
462 msg->setRecipient(this);
463 msg->setPropagateToChildren(false);
464 enqueueMessage(msg);
465 break;
466 }
467
468 case os::Event::DropFiles: {
469 Message* msg = new DropFilesMessage(osEvent.files());
470 msg->setDisplay(display);
471 msg->setRecipient(this);
472 enqueueMessage(msg);
473 break;
474 }
475
476 case os::Event::KeyDown:
477 case os::Event::KeyUp: {
478 Message* msg = new KeyMessage(
479 (osEvent.type() == os::Event::KeyDown ?
480 kKeyDownMessage:
481 kKeyUpMessage),
482 osEvent.scancode(),
483 osEvent.modifiers(),
484 osEvent.unicodeChar(),
485 osEvent.repeat());
486
487 msg->setDisplay(display);
488
489 if (osEvent.isDeadKey())
490 static_cast<KeyMessage*>(msg)->setDeadKey(true);
491
492 broadcastKeyMsg(msg);
493 enqueueMessage(msg);
494 break;
495 }
496
497 case os::Event::MouseEnter: {
498 if (get_multiple_displays()) {
499 if (osEvent.window()) {
500 ASSERT(display != nullptr);
501 _internal_set_mouse_display(display);
502 }
503 }
504 set_mouse_cursor(kArrowCursor);
505 lastMouseMoveEvent = osEvent;
506 mouse_display = display;
507 break;
508 }
509
510 case os::Event::MouseLeave: {
511 if (mouse_display == display) {
512 set_mouse_cursor(kOutsideDisplay);
513 setMouse(nullptr);
514
515 _internal_no_mouse_position();
516 mouse_display = nullptr;
517
518 // To avoid calling kSetCursorMessage when the mouse leaves
519 // the window.
520 lastMouseMoveEvent = os::Event();
521 }
522 break;
523 }
524
525 case os::Event::MouseMove: {
526 handleMouseMove(
527 display,
528 osEvent.position(),
529 osEvent.modifiers(),
530 osEvent.pointerType(),
531 osEvent.pressure());
532 lastMouseMoveEvent = osEvent;
533 break;
534 }
535
536 case os::Event::MouseDown: {
537 handleMouseDown(
538 display,
539 osEvent.position(),
540 m_mouseButton = mouse_button_from_os_to_ui(osEvent),
541 osEvent.modifiers(),
542 osEvent.pointerType(),
543 osEvent.pressure());
544 break;
545 }
546
547 case os::Event::MouseUp: {
548 handleMouseUp(
549 display,
550 osEvent.position(),
551 mouse_button_from_os_to_ui(osEvent),
552 osEvent.modifiers(),
553 osEvent.pointerType());
554 m_mouseButton = kButtonNone;
555 break;
556 }
557
558 case os::Event::MouseDoubleClick: {
559 handleMouseDoubleClick(
560 display,
561 osEvent.position(),
562 m_mouseButton = mouse_button_from_os_to_ui(osEvent),
563 osEvent.modifiers(),
564 osEvent.pointerType(),
565 osEvent.pressure());
566 break;
567 }
568
569 case os::Event::MouseWheel: {
570 handleMouseWheel(display,
571 osEvent.position(),
572 osEvent.modifiers(),
573 osEvent.pointerType(),
574 osEvent.wheelDelta(),
575 osEvent.preciseWheel());
576 break;
577 }
578
579 case os::Event::TouchMagnify: {
580 handleTouchMagnify(display,
581 osEvent.position(),
582 osEvent.modifiers(),
583 osEvent.magnification());
584 break;
585 }
586
587 case os::Event::Callback: {
588 // Call from the UI thread
589 osEvent.execCallback();
590 break;
591 }
592
593 }
594 }
595
596 // Generate just one kSetCursorMessage for the last mouse position
597 if (lastMouseMoveEvent.type() != os::Event::None) {
598 osEvent = lastMouseMoveEvent;
599
600 Display* display = getDisplayFromNativeWindow(osEvent.window().get());
601 if (!display)
602 display = this->display();
603
604 generateSetCursorMessage(display,
605 osEvent.position(),
606 osEvent.modifiers(),
607 osEvent.pointerType());
608 }
609}
610
611void Manager::handleMouseMove(Display* display,
612 const gfx::Point& mousePos,
613 const KeyModifiers modifiers,
614 const PointerType pointerType,
615 const float pressure)
616{
617 updateMouseWidgets(mousePos, display);
618
619 // Send the mouse movement message
620 Widget* dst = (capture_widget ? capture_widget: mouse_widget);
621 enqueueMessage(
622 newMouseMessage(
623 kMouseMoveMessage,
624 display, dst,
625 mousePos,
626 pointerType,
627 m_mouseButton,
628 modifiers,
629 gfx::Point(0, 0),
630 false,
631 pressure));
632}
633
634void Manager::handleMouseDown(Display* display,
635 const gfx::Point& mousePos,
636 MouseButton mouseButton,
637 KeyModifiers modifiers,
638 PointerType pointerType,
639 const float pressure)
640{
641 handleWindowZOrder();
642
643 enqueueMessage(
644 newMouseMessage(
645 kMouseDownMessage,
646 display,
647 (capture_widget ? capture_widget: mouse_widget),
648 mousePos,
649 pointerType,
650 mouseButton,
651 modifiers,
652 gfx::Point(0, 0),
653 false,
654 pressure));
655}
656
657void Manager::handleMouseUp(Display* display,
658 const gfx::Point& mousePos,
659 MouseButton mouseButton,
660 KeyModifiers modifiers,
661 PointerType pointerType)
662{
663 enqueueMessage(
664 newMouseMessage(
665 kMouseUpMessage,
666 display,
667 (capture_widget ? capture_widget: mouse_widget),
668 mousePos,
669 pointerType,
670 mouseButton,
671 modifiers));
672}
673
674void Manager::handleMouseDoubleClick(Display* display,
675 const gfx::Point& mousePos,
676 MouseButton mouseButton,
677 KeyModifiers modifiers,
678 PointerType pointerType,
679 const float pressure)
680{
681 Widget* dst = (capture_widget ? capture_widget: mouse_widget);
682 if (dst) {
683 enqueueMessage(
684 newMouseMessage(
685 kDoubleClickMessage,
686 display, dst, mousePos, pointerType,
687 mouseButton, modifiers,
688 gfx::Point(0, 0), false,
689 pressure));
690 }
691}
692
693void Manager::handleMouseWheel(Display* display,
694 const gfx::Point& mousePos,
695 KeyModifiers modifiers,
696 PointerType pointerType,
697 const gfx::Point& wheelDelta,
698 bool preciseWheel)
699{
700 enqueueMessage(newMouseMessage(
701 kMouseWheelMessage,
702 display,
703 (capture_widget ? capture_widget: mouse_widget),
704 mousePos, pointerType, m_mouseButton, modifiers,
705 wheelDelta, preciseWheel));
706}
707
708void Manager::handleTouchMagnify(Display* display,
709 const gfx::Point& mousePos,
710 const KeyModifiers modifiers,
711 const double magnification)
712{
713 Widget* widget = (capture_widget ? capture_widget: mouse_widget);
714 if (widget) {
715 Message* msg = new TouchMessage(
716 kTouchMagnifyMessage,
717 modifiers,
718 mousePos,
719 magnification);
720
721 msg->setDisplay(display);
722 msg->setRecipient(widget);
723
724 enqueueMessage(msg);
725 }
726}
727
728// Handles Z order: Send the window to top (only when you click in a
729// window that aren't the desktop).
730//
731// TODO code similar to Display::handleWindowZOrder()
732void Manager::handleWindowZOrder()
733{
734 if (capture_widget || !mouse_widget)
735 return;
736
737 // The clicked window
738 Window* window = mouse_widget->window();
739 Manager* win_manager = (window ? window->manager(): nullptr);
740
741 if ((window) &&
742 // We cannot change Z-order of desktop windows
743 (!window->isDesktop()) &&
744 // We cannot change Z order of foreground windows because a
745 // foreground window can launch other background windows
746 // which should be kept on top of the foreground one.
747 (!window->isForeground()) &&
748 // If the window is not already the top window of the manager.
749 (window != win_manager->getTopWindow())) {
750 base::ScopedValue<Widget*> scoped(m_lockedWindow, window, nullptr);
751
752 window->display()->handleWindowZOrder(window);
753
754 // Put it in the top of the list
755 win_manager->removeChild(window);
756
757 if (window->isOnTop())
758 win_manager->insertChild(0, window);
759 else {
760 int pos = (int)win_manager->children().size();
761
762 for (auto it=win_manager->children().rbegin(),
763 end=win_manager->children().rend();
764 it != end; ++it) {
765 if (static_cast<Window*>(*it)->isOnTop())
766 break;
767
768 --pos;
769 }
770 win_manager->insertChild(pos, window);
771 }
772
773 if (!window->ownDisplay())
774 window->invalidate();
775 }
776
777 // Put the focus
778 setFocus(mouse_widget);
779}
780
781// If display is nullptr, mousePos is in screen coordinates, if not,
782// it's relative to the display content rect.
783void Manager::updateMouseWidgets(const gfx::Point& mousePos,
784 Display* display)
785{
786 gfx::Point screenPos = (display ? display->nativeWindow()->pointToScreen(mousePos):
787 mousePos);
788
789 // Get the list of widgets to send mouse messages.
790 mouse_widgets_list.clear();
791 broadcastMouseMessage(screenPos,
792 mouse_widgets_list);
793
794 // Get the widget under the mouse
795 Widget* widget = nullptr;
796 for (auto mouseWidget : mouse_widgets_list) {
797 if (get_multiple_displays()) {
798 if (display) {
799 if (display != mouseWidget->display()) {
800 widget = mouseWidget->display()->containedWidget()->pickFromScreenPos(screenPos);
801 }
802 else {
803 widget = mouseWidget->pick(mousePos);
804 }
805 }
806 else {
807 widget = mouseWidget->display()->containedWidget()->pickFromScreenPos(screenPos);
808 }
809 }
810 else {
811 if (display)
812 widget = mouseWidget->pick(mousePos);
813 else
814 widget = mouseWidget->pickFromScreenPos(screenPos);
815 }
816 if (widget) {
817 // Get the first ancestor of the picked widget that doesn't
818 // ignore mouse events.
819 while (widget && widget->hasFlags(IGNORE_MOUSE))
820 widget = widget->parent();
821 break;
822 }
823 }
824
825 // Fixup "mouse" flag
826 if (widget != mouse_widget) {
827 if (!widget) {
828 freeMouse();
829 }
830 else {
831 setMouse(widget);
832 }
833 }
834}
835
836void Manager::dispatchMessages()
837{
838 // Send messages in the queue (mouse/key/timer/etc. events) This
839 // might change the state of widgets, etc. In case pumpQueue()
840 // returns a number greater than 0, it means that we've processed
841 // some messages, so we've to redraw the screen.
842 if (pumpQueue() > 0 || redrawState == RedrawState::RedrawDelayed) {
843 if (redrawState == RedrawState::ClosingApp) {
844 // Do nothing, we don't flush nor process paint messages
845 }
846 // If a window has just been closed with Manager::_closeWindow()
847 // after processing messages, we'll wait the next event generation
848 // to process painting events (so the manager doesn't lost the
849 // DIRTY flag right now).
850 else if (redrawState == RedrawState::AWindowHasJustBeenClosed) {
851 redrawState = RedrawState::RedrawDelayed;
852 }
853 else {
854 if (redrawState == RedrawState::RedrawDelayed)
855 redrawState = RedrawState::Normal;
856
857 // Generate and send just kPaintMessages with the latest UI state.
858 flushRedraw();
859 pumpQueue();
860
861 // Flip back-buffers to real displays.
862 flipAllDisplays();
863 }
864 }
865}
866
867void Manager::addToGarbage(Widget* widget)
868{
869 ASSERT(widget);
870 m_garbage.push_back(widget);
871}
872
873void Manager::enqueueMessage(Message* msg)
874{
875 ASSERT(msg);
876
877 if (is_ui_thread())
878 msg_queue.push_back(msg);
879 else
880 concurrent_msg_queue.push(msg);
881}
882
883Window* Manager::getTopWindow()
884{
885 return static_cast<Window*>(UI_FIRST_WIDGET(children()));
886}
887
888Window* Manager::getDesktopWindow()
889{
890 for (auto child : children()) {
891 Window* window = static_cast<Window*>(child);
892 if (window->isDesktop())
893 return window;
894 }
895 return nullptr;
896}
897
898Window* Manager::getForegroundWindow()
899{
900 for (auto child : children()) {
901 Window* window = static_cast<Window*>(child);
902 if (window->isForeground() ||
903 window->isDesktop())
904 return window;
905 }
906 return nullptr;
907}
908
909Display* Manager::getForegroundDisplay()
910{
911 if (get_multiple_displays()) {
912 Window* window = getForegroundWindow();
913 if (window)
914 return window->display();
915 }
916 return &m_display;
917}
918
919Widget* Manager::getFocus()
920{
921 return focus_widget;
922}
923
924Widget* Manager::getMouse()
925{
926 return mouse_widget;
927}
928
929Widget* Manager::getCapture()
930{
931 return capture_widget;
932}
933
934void Manager::setFocus(Widget* widget)
935{
936 if ((focus_widget != widget)
937 && (!(widget)
938 || (!(widget->hasFlags(DISABLED))
939 && !(widget->hasFlags(HIDDEN))
940 && !(widget->hasFlags(DECORATIVE))
941 && someParentIsFocusStop(widget)))) {
942 Widget* commonAncestor = findLowestCommonAncestor(focus_widget, widget);
943
944 // Fetch the focus
945 if (focus_widget && focus_widget != commonAncestor) {
946 auto msg = new Message(kFocusLeaveMessage);
947 msg->setRecipient(focus_widget);
948 msg->setPropagateToParent(true);
949 msg->setCommonAncestor(commonAncestor);
950 enqueueMessage(msg);
951
952 // Remove HAS_FOCUS from all hierarchy
953 auto a = focus_widget;
954 while (a && a != commonAncestor) {
955 a->disableFlags(HAS_FOCUS);
956 a = a->parent();
957 }
958 }
959
960 // Put the focus
961 focus_widget = widget;
962 if (widget) {
963 auto msg = new Message(kFocusEnterMessage);
964 msg->setRecipient(widget);
965 msg->setPropagateToParent(true);
966 msg->setCommonAncestor(commonAncestor);
967 enqueueMessage(msg);
968
969 // Add HAS_FOCUS to all hierarchy
970 auto a = focus_widget;
971 while (a && a != commonAncestor) {
972 if (a->hasFlags(FOCUS_STOP))
973 a->enableFlags(HAS_FOCUS);
974 a = a->parent();
975 }
976 }
977 }
978}
979
980void Manager::setMouse(Widget* widget)
981{
982#ifdef REPORT_EVENTS
983 TRACEARGS("Manager::setMouse ",
984 (widget ? typeid(*widget).name(): "null"),
985 (widget ? widget->id(): ""));
986#endif
987
988 if ((mouse_widget != widget) && (!capture_widget)) {
989 Widget* commonAncestor = findLowestCommonAncestor(mouse_widget, widget);
990
991 // Fetch the mouse
992 if (mouse_widget && mouse_widget != commonAncestor) {
993 auto msg = new Message(kMouseLeaveMessage);
994 msg->setRecipient(mouse_widget);
995 msg->setPropagateToParent(true);
996 msg->setCommonAncestor(commonAncestor);
997 enqueueMessage(msg);
998
999 // Remove HAS_MOUSE from all the hierarchy
1000 auto a = mouse_widget;
1001 while (a && a != commonAncestor) {
1002 a->disableFlags(HAS_MOUSE);
1003 a = a->parent();
1004 }
1005 }
1006
1007 // Put the mouse
1008 mouse_widget = widget;
1009 if (widget) {
1010 Display* display = mouse_widget->display();
1011 gfx::Point mousePos = display->nativeWindow()->pointFromScreen(get_mouse_position());
1012
1013 auto msg = newMouseMessage(
1014 kMouseEnterMessage,
1015 display, nullptr,
1016 mousePos,
1017 PointerType::Unknown,
1018 m_mouseButton,
1019 kKeyUninitializedModifier);
1020
1021 msg->setRecipient(widget);
1022 msg->setPropagateToParent(true);
1023 msg->setCommonAncestor(commonAncestor);
1024 enqueueMessage(msg);
1025 generateSetCursorMessage(display,
1026 mousePos,
1027 kKeyUninitializedModifier,
1028 PointerType::Unknown);
1029
1030 // Add HAS_MOUSE to all the hierarchy
1031 auto a = mouse_widget;
1032 while (a && a != commonAncestor) {
1033 a->enableFlags(HAS_MOUSE);
1034 a = a->parent();
1035 }
1036 }
1037 }
1038}
1039
1040void Manager::setCapture(Widget* widget)
1041{
1042 // To set the capture, we set first the mouse_widget (because
1043 // mouse_widget shouldn't be != capture_widget)
1044 setMouse(widget);
1045
1046 widget->enableFlags(HAS_CAPTURE);
1047 capture_widget = widget;
1048
1049 Display* display = (widget ? widget->display(): &m_display);
1050 ASSERT(display && display->nativeWindow());
1051 if (display && display->nativeWindow())
1052 display->nativeWindow()->captureMouse();
1053}
1054
1055// Sets the focus to the "magnetic" widget inside the window
1056void Manager::attractFocus(Widget* widget)
1057{
1058 // Get the magnetic widget
1059 Widget* magnet = findMagneticWidget(widget->window());
1060
1061 // If magnetic widget exists and it doesn't have the focus
1062 if (magnet && !magnet->hasFocus())
1063 setFocus(magnet);
1064}
1065
1066void Manager::focusFirstChild(Widget* widget)
1067{
1068 for (Widget* it=widget->window(); it; it=next_widget(it)) {
1069 if (does_accept_focus(it) && !(child_accept_focus(it, true))) {
1070 setFocus(it);
1071 break;
1072 }
1073 }
1074}
1075
1076void Manager::freeFocus()
1077{
1078 setFocus(nullptr);
1079}
1080
1081void Manager::freeMouse()
1082{
1083 setMouse(nullptr);
1084}
1085
1086void Manager::freeCapture()
1087{
1088 if (capture_widget) {
1089 Display* display = capture_widget->display();
1090
1091 capture_widget->disableFlags(HAS_CAPTURE);
1092 capture_widget = nullptr;
1093
1094 ASSERT(display && display->nativeWindow());
1095 if (display && display->nativeWindow())
1096 display->nativeWindow()->releaseMouse();
1097 }
1098}
1099
1100void Manager::freeWidget(Widget* widget)
1101{
1102#ifdef DEBUG_UI_THREADS
1103 ASSERT(manager_thread == base::this_thread::native_id());
1104#endif
1105
1106 if (widget->hasFocus() || (widget == focus_widget))
1107 freeFocus();
1108
1109 // We shouldn't free widgets that are locked, it means, widgets that
1110 // will be re-added soon (e.g. when the stack of windows is
1111 // temporarily modified).
1112 if (m_lockedWindow == widget)
1113 return;
1114
1115 // Break any relationship with the GUI manager
1116 if (widget->hasCapture() || (widget == capture_widget))
1117 freeCapture();
1118
1119 if (widget->hasMouse() || (widget == mouse_widget))
1120 freeMouse();
1121
1122 auto it = std::find(mouse_widgets_list.begin(),
1123 mouse_widgets_list.end(),
1124 widget);
1125 if (it != mouse_widgets_list.end())
1126 mouse_widgets_list.erase(it);
1127
1128 ASSERT(!Manager::widgetAssociatedToManager(widget));
1129}
1130
1131void Manager::removeMessagesFor(Widget* widget)
1132{
1133#ifdef DEBUG_UI_THREADS
1134 ASSERT(manager_thread == base::this_thread::native_id());
1135#endif
1136
1137 for (Message* msg : msg_queue)
1138 msg->removeRecipient(widget);
1139
1140 for (Message* msg : used_msg_queue)
1141 msg->removeRecipient(widget);
1142}
1143
1144void Manager::removeMessagesFor(Widget* widget, MessageType type)
1145{
1146#ifdef DEBUG_UI_THREADS
1147 ASSERT(manager_thread == base::this_thread::native_id());
1148#endif
1149
1150 for (Message* msg : msg_queue)
1151 if (msg->type() == type)
1152 msg->removeRecipient(widget);
1153
1154 for (Message* msg : used_msg_queue)
1155 if (msg->type() == type)
1156 msg->removeRecipient(widget);
1157}
1158
1159void Manager::removeMessagesForTimer(Timer* timer)
1160{
1161#ifdef DEBUG_UI_THREADS
1162 ASSERT(manager_thread == base::this_thread::native_id());
1163#endif
1164
1165 for (Message* msg : msg_queue) {
1166 if (msg->type() == kTimerMessage &&
1167 static_cast<TimerMessage*>(msg)->timer() == timer) {
1168 msg->removeRecipient(msg->recipient());
1169 static_cast<TimerMessage*>(msg)->_resetTimer();
1170 }
1171 }
1172
1173 for (Message* msg : used_msg_queue) {
1174 if (msg->type() == kTimerMessage &&
1175 static_cast<TimerMessage*>(msg)->timer() == timer) {
1176 msg->removeRecipient(msg->recipient());
1177 static_cast<TimerMessage*>(msg)->_resetTimer();
1178 }
1179 }
1180}
1181
1182void Manager::removeMessagesForDisplay(Display* display)
1183{
1184#ifdef DEBUG_UI_THREADS
1185 ASSERT(manager_thread == base::this_thread::native_id());
1186#endif
1187
1188 for (Message* msg : msg_queue) {
1189 if (msg->display() == display) {
1190 msg->removeRecipient(msg->recipient());
1191 msg->setDisplay(nullptr);
1192 }
1193 }
1194
1195 for (Message* msg : used_msg_queue) {
1196 if (msg->display() == display) {
1197 msg->removeRecipient(msg->recipient());
1198 msg->setDisplay(nullptr);
1199 }
1200 }
1201}
1202
1203void Manager::removePaintMessagesForDisplay(Display* display)
1204{
1205#ifdef DEBUG_UI_THREADS
1206 ASSERT(manager_thread == base::this_thread::native_id());
1207#endif
1208
1209 for (auto it=msg_queue.begin(); it != msg_queue.end(); ) {
1210 Message* msg = *it;
1211 if (msg->type() == kPaintMessage &&
1212 msg->display() == display) {
1213 delete msg;
1214 it = msg_queue.erase(it);
1215 }
1216 else
1217 ++it;
1218 }
1219}
1220
1221void Manager::addMessageFilter(int message, Widget* widget)
1222{
1223#ifdef DEBUG_UI_THREADS
1224 ASSERT(manager_thread == base::this_thread::native_id());
1225#endif
1226
1227 LockFilters lock;
1228 int c = message;
1229 if (c >= kFirstRegisteredMessage)
1230 c = kFirstRegisteredMessage;
1231
1232 msg_filters[c].push_back(new Filter(message, widget));
1233}
1234
1235void Manager::removeMessageFilter(int message, Widget* widget)
1236{
1237#ifdef DEBUG_UI_THREADS
1238 ASSERT(manager_thread == base::this_thread::native_id());
1239#endif
1240
1241 LockFilters lock;
1242 int c = message;
1243 if (c >= kFirstRegisteredMessage)
1244 c = kFirstRegisteredMessage;
1245
1246 Filters& msg_filter = msg_filters[c];
1247 for (Filter* filter : msg_filter) {
1248 if (filter->widget == widget)
1249 filter->widget = nullptr;
1250 }
1251}
1252
1253void Manager::removeMessageFilterFor(Widget* widget)
1254{
1255#ifdef DEBUG_UI_THREADS
1256 ASSERT(manager_thread == base::this_thread::native_id());
1257#endif
1258
1259 LockFilters lock;
1260 for (Filters& msg_filter : msg_filters) {
1261 for (Filter* filter : msg_filter) {
1262 if (filter->widget == widget)
1263 filter->widget = nullptr;
1264 }
1265 }
1266}
1267
1268bool Manager::isFocusMovementMessage(Message* msg)
1269{
1270 if (msg->type() != kKeyDownMessage &&
1271 msg->type() != kKeyUpMessage)
1272 return false;
1273
1274 switch (static_cast<KeyMessage*>(msg)->scancode()) {
1275 case kKeyTab:
1276 case kKeyLeft:
1277 case kKeyRight:
1278 case kKeyUp:
1279 case kKeyDown:
1280 return true;
1281 }
1282 return false;
1283}
1284
1285Widget* Manager::pickFromScreenPos(const gfx::Point& screenPos) const
1286{
1287 Display* mainDisplay = display();
1288
1289 if (get_multiple_displays()) {
1290 for (auto child : children()) {
1291 auto window = static_cast<Window*>(child);
1292 if (window->ownDisplay() ||
1293 window->display() != mainDisplay) {
1294 os::Window* nativeWindow = window->display()->nativeWindow();
1295 if (nativeWindow->frame().contains(screenPos))
1296 return window->pick(nativeWindow->pointFromScreen(screenPos));
1297 }
1298 }
1299
1300 gfx::Point displayPos = display()->nativeWindow()->pointFromScreen(screenPos);
1301 for (auto child : children()) {
1302 auto window = static_cast<Window*>(child);
1303 if (window->display() == mainDisplay) {
1304 if (auto picked = window->pick(displayPos))
1305 return picked;
1306 }
1307 }
1308 }
1309 return Widget::pickFromScreenPos(screenPos);
1310}
1311
1312void Manager::_closingAppWithException()
1313{
1314 redrawState = RedrawState::ClosingApp;
1315}
1316
1317// Configures the window for begin the loop
1318void Manager::_openWindow(Window* window, bool center)
1319{
1320 Display* parentDisplay = getForegroundDisplay();
1321 ASSERT(parentDisplay);
1322
1323 // Opening other window in the "close app" state, ok, let's back to normal.
1324 if (redrawState == RedrawState::ClosingApp)
1325 redrawState = RedrawState::Normal;
1326
1327 // Free all widgets of special states.
1328 if (window->isWantFocus()) {
1329 freeCapture();
1330 freeMouse();
1331 freeFocus();
1332 }
1333
1334 // Add the window to manager.
1335 insertChild(0, window);
1336
1337 // Broadcast the open message.
1338 {
1339 Message msg(kOpenMessage);
1340 window->sendMessage(&msg);
1341 }
1342
1343 // Relayout
1344 if (center)
1345 window->centerWindow(parentDisplay);
1346 else
1347 window->layout();
1348
1349 // If the window already was set a display, we don't setup it
1350 // (i.e. in the case of combobox popup/window the display field is
1351 // set to the same display where the ComboBox widget is located)
1352 if (!window->hasDisplaySet()) {
1353 // In other case, we can try to create a display/native window for
1354 // the UI window.
1355 if (get_multiple_displays()
1356 && window->shouldCreateNativeWindow()) {
1357 const int scale = parentDisplay->nativeWindow()->scale();
1358
1359 os::WindowSpec spec;
1360 gfx::Rect frame;
1361 bool changeFrame;
1362 if (!window->lastNativeFrame().isEmpty()) {
1363 frame = window->lastNativeFrame();
1364 changeFrame = true;
1365 }
1366 else {
1367 gfx::Rect relativeToFrame = parentDisplay->nativeWindow()->contentRect();
1368 frame = window->bounds();
1369 frame *= scale;
1370 frame.offset(relativeToFrame.origin());
1371 changeFrame = false;
1372 }
1373
1374 limit_with_workarea(parentDisplay, frame);
1375
1376 spec.position(os::WindowSpec::Position::Frame);
1377 spec.frame(frame);
1378 spec.scale(scale);
1379 // Only desktop will have the real native window title bar
1380 // TODO in the future other windows could use the native title bar
1381 // when there are no special decorators (or we could just add
1382 // the possibility to create new buttons in the native window
1383 // title bar)
1384 spec.titled(window->isDesktop());
1385 spec.floating(!window->isDesktop());
1386 spec.resizable(window->isDesktop() || window->isSizeable());
1387 spec.maximizable(spec.resizable());
1388 spec.minimizable(window->isDesktop());
1389 spec.borderless(!window->isDesktop());
1390 spec.transparent(window->isTransparent());
1391
1392 if (!window->isDesktop()) {
1393 spec.parent(parentDisplay->nativeWindow());
1394 }
1395
1396 os::WindowRef newNativeWindow = os::instance()->makeWindow(spec);
1397 ui::Display* newDisplay = new ui::Display(parentDisplay, newNativeWindow, window);
1398
1399 newNativeWindow->setUserData(newDisplay);
1400 window->setDisplay(newDisplay, true);
1401
1402 // Set native title bar text
1403 newNativeWindow->setTitle(window->text());
1404
1405 // Activate only non-floating windows
1406 if (!spec.floating())
1407 newNativeWindow->activate();
1408 else
1409 m_display.nativeWindow()->activate();
1410
1411 // Move all widgets to the os::Display origin (0,0)
1412 if (changeFrame) {
1413 window->setBounds(newNativeWindow->bounds() / scale);
1414 }
1415 else {
1416 window->offsetWidgets(-window->origin().x, -window->origin().y);
1417 }
1418
1419 // Handle native hit testing. Required to be able to move/resize
1420 // a window with a non-mouse pointer (e.g. stylus) on Windows.
1421 newNativeWindow->handleHitTest = handle_native_hittest;
1422 }
1423 else {
1424 // Same display for desktop window or when multiple displays is
1425 // disabled.
1426 window->setDisplay(this->display(), false);
1427 }
1428 }
1429
1430 // Dirty the entire window and show it
1431 window->setVisible(true);
1432 window->invalidate();
1433
1434 // Attract the focus to the magnetic widget...
1435 // 1) get the magnetic widget
1436 Widget* magnet = findMagneticWidget(window);
1437 // 2) if magnetic widget exists and it doesn't have the focus
1438 if (magnet && !magnet->hasFocus())
1439 setFocus(magnet);
1440 // 3) if not, put the focus in the first child
1441 else if (window->isWantFocus())
1442 focusFirstChild(window);
1443
1444 // Update mouse widget (as it can be a widget below the
1445 // recently opened window).
1446 updateMouseWidgets(ui::get_mouse_position(), nullptr);
1447}
1448
1449void Manager::_closeWindow(Window* window, bool redraw_background)
1450{
1451 if (!hasChild(window))
1452 return;
1453
1454 gfx::Region reg1;
1455 if (!window->ownDisplay()) {
1456 if (redraw_background)
1457 window->getRegion(reg1);
1458 }
1459
1460 // Close all windows to this desktop
1461 if (window->isDesktop()) {
1462 while (!children().empty()) {
1463 Window* child = static_cast<Window*>(children().front());
1464 if (child == window)
1465 break;
1466 else {
1467 gfx::Region reg2;
1468 window->getRegion(reg2);
1469 reg1 |= reg2;
1470
1471 _closeWindow(child, false);
1472 }
1473 }
1474 }
1475
1476 // Free all widgets of special states.
1477 if (capture_widget && capture_widget->window() == window)
1478 freeCapture();
1479
1480 if (mouse_widget && mouse_widget->window() == window)
1481 freeMouse();
1482
1483 if (focus_widget && focus_widget->window() == window)
1484 freeFocus();
1485
1486 // Hide window.
1487 window->setVisible(false);
1488
1489 // Close message.
1490 {
1491 Message msg(kCloseMessage);
1492 window->sendMessage(&msg);
1493 }
1494
1495 // Destroy native window associated with this window's display if needed
1496 Display* windowDisplay = window->display();
1497 Display* parentDisplay;
1498 if (// The display can be nullptr if the window was not opened or
1499 // was closed before.
1500 window->ownDisplay()) {
1501 parentDisplay = (windowDisplay ? windowDisplay->parentDisplay(): nullptr);
1502 ASSERT(parentDisplay);
1503 ASSERT(windowDisplay);
1504 ASSERT(windowDisplay != this->display());
1505
1506 // We are receiving several crashes from Windows users where
1507 // parentDisplay != nullptr and parentDisplay->nativeWindow() ==
1508 // nullptr, so we have to do some extra checks in these places
1509 // (anyway this might produce some crashes in other places)
1510 os::Window* nativeWindow = (windowDisplay ? windowDisplay->nativeWindow(): nullptr);
1511 os::Window* parentNativeWindow = (parentDisplay ? parentDisplay->nativeWindow(): nullptr);
1512 ASSERT(nativeWindow);
1513 ASSERT(parentNativeWindow);
1514
1515 // Just as we've set the origin of the window bounds to (0, 0)
1516 // when we created the native window, we have to restore the
1517 // ui::Window bounds' origin now that we are going to remove/close
1518 // the native window.
1519 if (parentNativeWindow && nativeWindow) {
1520 const int scale = parentNativeWindow->scale();
1521 const gfx::Point parentOrigin = parentNativeWindow->contentRect().origin();
1522 const gfx::Point origin = nativeWindow->contentRect().origin();
1523 const gfx::Rect newBounds((origin - parentOrigin) / scale,
1524 window->bounds().size());
1525 window->setBounds(newBounds);
1526 }
1527
1528 // Set the native window user data to nullptr so any other queued
1529 // native message is not processed.
1530 window->setDisplay(nullptr, false);
1531 if (nativeWindow)
1532 nativeWindow->setUserData<void*>(nullptr);
1533
1534 // Remove all messages for this display.
1535 removeMessagesForDisplay(windowDisplay);
1536
1537 // Remove the mouse cursor from the display that we are going to
1538 // delete.
1539 _internal_set_mouse_display(parentDisplay);
1540
1541 // Remove the display that we're going to delete (windowDisplay)
1542 // as parent of any other existent display.
1543 for (auto otherChild : children()) {
1544 if (auto otherWindow = static_cast<Window*>(otherChild)) {
1545 if (otherWindow != window &&
1546 otherWindow->display() &&
1547 otherWindow->display()->parentDisplay() == windowDisplay) {
1548 otherWindow->display()->_setParentDisplay(parentDisplay);
1549 }
1550 }
1551 }
1552
1553 // The ui::Display should destroy the os::Window
1554 delete windowDisplay;
1555
1556 // Activate main windows
1557 if (parentNativeWindow)
1558 parentNativeWindow->activate();
1559 }
1560 else {
1561 parentDisplay = windowDisplay;
1562 window->setDisplay(nullptr, false);
1563 }
1564
1565 // Update manager list stuff.
1566 removeChild(window);
1567
1568 // Redraw background.
1569 parentDisplay->containedWidget()->invalidateRegion(reg1);
1570
1571 // Update mouse widget (as it can be a widget below the
1572 // recently closed window).
1573 updateMouseWidgets(ui::get_mouse_position(), nullptr);
1574
1575 if (redrawState != RedrawState::ClosingApp) {
1576 if (children().empty()) {
1577 // All windows were closed...
1578 redrawState = RedrawState::ClosingApp;
1579 }
1580 else {
1581 redrawState = RedrawState::AWindowHasJustBeenClosed;
1582 }
1583 }
1584}
1585
1586void Manager::_runModalWindow(Window* window)
1587{
1588 MessageLoop loop(manager());
1589 while (!window->hasFlags(HIDDEN))
1590 loop.pumpMessages();
1591}
1592
1593void Manager::_updateMouseWidgets()
1594{
1595 // Update mouse widget.
1596 updateMouseWidgets(ui::get_mouse_position(), nullptr);
1597}
1598
1599bool Manager::onProcessMessage(Message* msg)
1600{
1601 switch (msg->type()) {
1602
1603 case kPaintMessage:
1604 // Draw nothing (the manager should be invisible). On Windows,
1605 // after closing the main window, the manager will not refresh
1606 // the os::Display content, so we'll avoid a gray background
1607 // (the last main window content is kept until the Display is
1608 // finally closed.)
1609 return true;
1610
1611 case kCloseDisplayMessage: {
1612 if (msg->display() != &m_display) {
1613 if (Window* window = dynamic_cast<Window*>(msg->display()->containedWidget())) {
1614 window->closeWindow(this);
1615 }
1616 }
1617 break;
1618 }
1619
1620 case kResizeDisplayMessage:
1621 onNewDisplayConfiguration(msg->display());
1622 break;
1623
1624 case kKeyDownMessage:
1625 case kKeyUpMessage: {
1626 KeyMessage* keymsg = static_cast<KeyMessage*>(msg);
1627 keymsg->setPropagateToChildren(true);
1628 keymsg->setPropagateToParent(false);
1629
1630 // Continue sending the message to the children of all windows
1631 // (until a desktop or foreground window).
1632 Window* win = nullptr;
1633 for (auto manchild : children()) {
1634 win = static_cast<Window*>(manchild);
1635
1636 // Send to the window.
1637 for (auto winchild : win->children())
1638 if (winchild->sendMessage(msg))
1639 return true;
1640
1641 if (win->isForeground() ||
1642 win->isDesktop())
1643 break;
1644 }
1645
1646 // Check the focus movement for foreground (non-desktop) windows.
1647 if (win && win->isForeground()) {
1648 if (msg->type() == kKeyDownMessage)
1649 processFocusMovementMessage(msg);
1650 return true;
1651 }
1652 else
1653 return false;
1654 }
1655
1656 }
1657
1658 return Widget::onProcessMessage(msg);
1659}
1660
1661void Manager::onResize(ResizeEvent& ev)
1662{
1663 gfx::Rect old_pos = bounds();
1664 gfx::Rect new_pos = ev.bounds();
1665 setBoundsQuietly(new_pos);
1666
1667 // The whole manager area is invalid now.
1668 m_display.setInvalidRegion(gfx::Region(new_pos));
1669
1670 const int dx = new_pos.x - old_pos.x;
1671 const int dy = new_pos.y - old_pos.y;
1672 const int dw = new_pos.w - old_pos.w;
1673 const int dh = new_pos.h - old_pos.h;
1674
1675 for (auto child : children()) {
1676 Window* window = static_cast<Window*>(child);
1677 if (window->ownDisplay())
1678 continue;
1679
1680 if (window->isDesktop()) {
1681 window->setBounds(new_pos);
1682 break;
1683 }
1684
1685 gfx::Rect bounds = window->bounds();
1686 const int cx = bounds.x+bounds.w/2;
1687 const int cy = bounds.y+bounds.h/2;
1688
1689 if (auto_window_adjustment) {
1690 if (cx > old_pos.x+old_pos.w*3/5) {
1691 bounds.x += dw;
1692 }
1693 else if (cx > old_pos.x+old_pos.w*2/5) {
1694 bounds.x += dw / 2;
1695 }
1696
1697 if (cy > old_pos.y+old_pos.h*3/5) {
1698 bounds.y += dh;
1699 }
1700 else if (cy > old_pos.y+old_pos.h*2/5) {
1701 bounds.y += dh / 2;
1702 }
1703
1704 bounds.offset(dx, dy);
1705 }
1706 else {
1707 if (bounds.x2() > new_pos.x2()) {
1708 bounds.x = new_pos.x2() - bounds.w;
1709 }
1710 if (bounds.y2() > new_pos.y2()) {
1711 bounds.y = new_pos.y2() - bounds.h;
1712 }
1713 }
1714 window->setBounds(bounds);
1715 }
1716}
1717
1718void Manager::onBroadcastMouseMessage(const gfx::Point& screenPos,
1719 WidgetsList& targets)
1720{
1721 // Ask to the first window in the "children" list to know how to
1722 // propagate mouse messages.
1723 Widget* widget = UI_FIRST_WIDGET(children());
1724 if (widget)
1725 widget->broadcastMouseMessage(screenPos, targets);
1726}
1727
1728void Manager::onInitTheme(InitThemeEvent& ev)
1729{
1730 Widget::onInitTheme(ev);
1731
1732 // Remap the windows
1733 const int oldUIScale = ui::details::old_guiscale();
1734 const int newUIScale = ui::guiscale();
1735 for (auto widget : children()) {
1736 if (widget->type() == kWindowWidget) {
1737 auto window = static_cast<Window*>(widget);
1738 if (window->isDesktop()) {
1739 window->layout();
1740 }
1741 else {
1742 gfx::Size displaySize = m_display.size();
1743 gfx::Rect bounds = window->bounds();
1744 bounds *= newUIScale;
1745 bounds /= oldUIScale;
1746 bounds.x = std::clamp(bounds.x, 0, displaySize.w - bounds.w);
1747 bounds.y = std::clamp(bounds.y, 0, displaySize.h - bounds.h);
1748 window->setBounds(bounds);
1749 }
1750 }
1751 }
1752}
1753
1754LayoutIO* Manager::onGetLayoutIO()
1755{
1756 return nullptr;
1757}
1758
1759void Manager::onNewDisplayConfiguration(Display* display)
1760{
1761 ASSERT(display);
1762 Widget* container = display->containedWidget();
1763
1764 gfx::Size displaySize = display->size();
1765 if ((bounds().w != displaySize.w ||
1766 bounds().h != displaySize.h)) {
1767 container->setBounds(gfx::Rect(displaySize));
1768 }
1769
1770 // The native window can be nullptr when running tests.
1771 if (!display->nativeWindow())
1772 return;
1773
1774 _internal_set_mouse_display(display);
1775 container->invalidate();
1776 container->flushRedraw();
1777}
1778
1779void Manager::onSizeHint(SizeHintEvent& ev)
1780{
1781 int w = 0, h = 0;
1782
1783 if (!parent()) { // hasn' parent?
1784 w = bounds().w;
1785 h = bounds().h;
1786 }
1787 else {
1788 gfx::Rect pos = parent()->childrenBounds();
1789
1790 for (auto child : children()) {
1791 gfx::Rect cpos = child->bounds();
1792 pos = pos.createUnion(cpos);
1793 }
1794
1795 w = pos.w;
1796 h = pos.h;
1797 }
1798
1799 ev.setSizeHint(gfx::Size(w, h));
1800}
1801
1802int Manager::pumpQueue()
1803{
1804#ifdef DEBUG_UI_THREADS
1805 ASSERT(manager_thread == base::this_thread::native_id());
1806#endif
1807
1808#ifdef LIMIT_DISPATCH_TIME
1809 base::tick_t t = base::current_tick();
1810#endif
1811
1812 int count = 0; // Number of processed messages
1813 while (!msg_queue.empty()) {
1814#ifdef LIMIT_DISPATCH_TIME
1815 if (base::current_tick()-t > 250)
1816 break;
1817#endif
1818
1819 // The message to process
1820 auto it = msg_queue.begin();
1821 Message* msg = *it;
1822 ASSERT(msg);
1823
1824 // Move the message from msg_queue to used_msg_queue
1825 msg_queue.erase(it);
1826 auto eraseIt = used_msg_queue.insert(used_msg_queue.end(), msg);
1827
1828 // Call Timer::tick() if this is a tick message.
1829 if (msg->type() == kTimerMessage) {
1830 // The timer can be nullptr if it was removed with removeMessagesForTimer()
1831 if (auto timer = static_cast<TimerMessage*>(msg)->timer())
1832 timer->tick();
1833 }
1834
1835 bool done = false;
1836
1837 // Send this message to filters
1838 {
1839 Filters& msg_filter = msg_filters[std::min(msg->type(), kFirstRegisteredMessage)];
1840 if (!msg_filter.empty()) {
1841 LockFilters lock;
1842 for (Filter* filter : msg_filter) {
1843 // The widget can be nullptr in case that the filter was
1844 // "pre-removed" (it'll finally erased from the
1845 // msg_filter list from ~LockFilters()).
1846 if (filter->widget != nullptr &&
1847 msg->type() == filter->message) {
1848 msg->setFromFilter(true);
1849 done = sendMessageToWidget(msg, filter->widget);
1850 msg->setFromFilter(false);
1851
1852 if (done)
1853 break;
1854 }
1855 }
1856 }
1857 }
1858
1859 if (!done) {
1860 // Then send the message to its recipient
1861 if (Widget* widget = msg->recipient())
1862 done = sendMessageToWidget(msg, widget);
1863 }
1864
1865 // Remove the message from the used_msg_queue
1866 used_msg_queue.erase(eraseIt);
1867
1868 // Destroy the message
1869 delete msg;
1870 ++count;
1871 }
1872
1873 return count;
1874}
1875
1876bool Manager::sendMessageToWidget(Message* msg, Widget* widget)
1877{
1878#ifdef DEBUG_UI_THREADS
1879 ASSERT(manager_thread == base::this_thread::native_id());
1880#endif
1881
1882 if (!widget)
1883 return false;
1884
1885#ifdef REPORT_EVENTS
1886 {
1887 static const char* msg_name[] = {
1888 "kOpenMessage",
1889 "kCloseMessage",
1890 "kCloseDisplayMessage",
1891 "kResizeDisplayMessage",
1892 "kPaintMessage",
1893 "kTimerMessage",
1894 "kDropFilesMessage",
1895 "kWinMoveMessage",
1896
1897 "kKeyDownMessage",
1898 "kKeyUpMessage",
1899 "kFocusEnterMessage",
1900 "kFocusLeaveMessage",
1901
1902 "kMouseDownMessage",
1903 "kMouseUpMessage",
1904 "kDoubleClickMessage",
1905 "kMouseEnterMessage",
1906 "kMouseLeaveMessage",
1907 "kMouseMoveMessage",
1908 "kSetCursorMessage",
1909 "kMouseWheelMessage",
1910 "kTouchMagnifyMessage",
1911 };
1912 static_assert(kOpenMessage == 0 &&
1913 kTouchMagnifyMessage == sizeof(msg_name)/sizeof(const char*)-1,
1914 "MessageType enum has changed");
1915 const char* string =
1916 (msg->type() >= 0 &&
1917 msg->type() < sizeof(msg_name)/sizeof(const char*)) ?
1918 msg_name[msg->type()]: "Unknown";
1919
1920 TRACEARGS("Event", msg->type(), "(", string, ")",
1921 "for", ((void*)widget),
1922 typeid(*widget).name(),
1923 widget->id().empty());
1924 }
1925#endif
1926
1927 bool used = false;
1928
1929 // We need to configure the clip region for paint messages
1930 // before we call Widget::sendMessage().
1931 if (msg->type() == kPaintMessage) {
1932 if (widget->hasFlags(HIDDEN))
1933 return false;
1934
1935 // Ignore all paint messages when we are closing the app
1936 if (redrawState == RedrawState::ClosingApp)
1937 return false;
1938
1939 PaintMessage* paintMsg = static_cast<PaintMessage*>(msg);
1940 Display* display = paintMsg->display();
1941
1942 // TODO use paintMsg->display() here
1943 // Restore overlays in the region that we're going to paint.
1944 OverlayManager::instance()->restoreOverlappedAreas(paintMsg->rect());
1945
1946 os::SurfaceRef surface(base::AddRef(display->surface()));
1947 surface->saveClip();
1948
1949 if (surface->clipRect(paintMsg->rect())) {
1950#ifdef REPORT_EVENTS
1951 TRACEARGS(" - clipRect ", paintMsg->rect());
1952#endif
1953
1954#ifdef DEBUG_PAINT_EVENTS
1955 {
1956 os::SurfaceLock lock(surface);
1957 os::Paint p;
1958 p.color(gfx::rgba(0, 0, 255));
1959 p.style(os::Paint::Fill);
1960 surface->drawRect(paintMsg->rect(), p);
1961
1962 display->nativeWindow()
1963 ->invalidateRegion(gfx::Region(paintMsg->rect()));
1964
1965#ifdef _WIN32 // TODO add a display->nativeWindow()->updateNow() method ??
1966 HWND hwnd = (HWND)display->nativeWindow()->nativeHandle();
1967 UpdateWindow(hwnd);
1968#else
1969 base::this_thread::sleep_for(0.002);
1970#endif
1971 }
1972#endif
1973
1974 // Call the message handler
1975 used = widget->sendMessage(msg);
1976 }
1977
1978 // Restore clip region for paint messages.
1979 surface->restoreClip();
1980
1981 // As this kPaintMessage's rectangle was updated, we can
1982 // remove it from "m_invalidRegion".
1983 display->subtractInvalidRegion(gfx::Region(paintMsg->rect()));
1984 }
1985 else {
1986 // Call the message handler
1987 used = widget->sendMessage(msg);
1988 }
1989
1990 return used;
1991}
1992
1993// It's like Widget::onInvalidateRegion() but optimized for the
1994// Manager (as we know that all children in a Manager will be windows,
1995// we can use this knowledge to avoid some calculations).
1996//
1997// TODO similar to Window::onInvalidateRegion
1998void Manager::onInvalidateRegion(const gfx::Region& region)
1999{
2000 if (!isVisible() || region.contains(bounds()) == gfx::Region::Out)
2001 return;
2002
2003 // Intersect only with manager bounds, we don't need to use
2004 // getDrawableRegion() because each window will be processed in the
2005 // following for() loop (and it's highly probable that a desktop
2006 // Window will use the whole manager portion anyway).
2007 gfx::Region reg1;
2008 reg1.createIntersection(region, gfx::Region(bounds()));
2009
2010 // Redraw windows from top to background.
2011 bool withDesktop = false;
2012 for (auto child : children()) {
2013 ASSERT(dynamic_cast<Window*>(child));
2014 ASSERT(child->type() == kWindowWidget);
2015 Window* window = static_cast<Window*>(child);
2016
2017 // Invalidating the manager only works for the main display, to
2018 // invalidate windows you have to invalidate them.
2019 if (window->ownDisplay())
2020 continue;
2021
2022 // Invalidate regions of this window
2023 window->invalidateRegion(reg1);
2024
2025 // There is desktop?
2026 if (window->isDesktop()) {
2027 withDesktop = true;
2028 break; // Work done
2029 }
2030
2031 // Clip this window area for the next window.
2032 gfx::Region reg2;
2033 window->getRegion(reg2);
2034 reg1.createSubtraction(reg1, reg2);
2035 }
2036
2037 // Invalidate areas outside windows (only when there are not a
2038 // desktop window).
2039 if (!withDesktop) {
2040 // TODO we should be able to modify m_updateRegion directly here,
2041 // so we avoid the getDrawableRegion() call from
2042 // Widget::onInvalidateRegion().
2043 Widget::onInvalidateRegion(reg1);
2044 }
2045}
2046
2047LayoutIO* Manager::getLayoutIO()
2048{
2049 return onGetLayoutIO();
2050}
2051
2052void Manager::collectGarbage()
2053{
2054 if (m_garbage.empty())
2055 return;
2056
2057 GARBAGE_TRACE("Manager::collectGarbage() #objects=%d\n", int(m_garbage.size()));
2058
2059 for (auto widget : m_garbage) {
2060 GARBAGE_TRACE(" -> deleting %s %s ---\n",
2061 typeid(*widget).name(),
2062 widget->id().c_str());
2063 delete widget;
2064 }
2065 m_garbage.clear();
2066}
2067
2068/**********************************************************************
2069 Internal routines
2070 **********************************************************************/
2071
2072// static
2073Widget* Manager::findLowestCommonAncestor(Widget* a, Widget* b)
2074{
2075 if (!a || !b)
2076 return nullptr;
2077
2078 Widget* u = a;
2079 Widget* v = b;
2080 int aDepth = 0;
2081 int bDepth = 0;
2082 while (u) {
2083 ++aDepth;
2084 u = u->parent();
2085 }
2086 while (v) {
2087 ++bDepth;
2088 v = v->parent();
2089 }
2090
2091 while (aDepth > bDepth) {
2092 --aDepth;
2093 a = a->parent();
2094 }
2095 while (bDepth > aDepth) {
2096 --bDepth;
2097 b = b->parent();
2098 }
2099
2100 while (a && b) {
2101 if (a == b)
2102 break;
2103 a = a->parent();
2104 b = b->parent();
2105 }
2106 return a;
2107}
2108
2109// static
2110bool Manager::someParentIsFocusStop(Widget* widget)
2111{
2112 if (widget->isFocusStop())
2113 return true;
2114
2115 if (widget->parent())
2116 return someParentIsFocusStop(widget->parent());
2117 else
2118 return false;
2119}
2120
2121// static
2122Widget* Manager::findMagneticWidget(Widget* widget)
2123{
2124 Widget* found;
2125
2126 for (auto child : widget->children()) {
2127 found = findMagneticWidget(child);
2128 if (found)
2129 return found;
2130 }
2131
2132 if (widget->isFocusMagnet())
2133 return widget;
2134 else
2135 return nullptr;
2136}
2137
2138// static
2139Message* Manager::newMouseMessage(
2140 MessageType type,
2141 Display* display,
2142 Widget* widget,
2143 const gfx::Point& mousePos,
2144 PointerType pointerType,
2145 MouseButton button,
2146 KeyModifiers modifiers,
2147 const gfx::Point& wheelDelta,
2148 bool preciseWheel,
2149 float pressure)
2150{
2151#ifdef __APPLE__
2152 // Convert Ctrl+left click -> right-click
2153 if (widget &&
2154 widget->isVisible() &&
2155 widget->isEnabled() &&
2156 widget->hasFlags(CTRL_RIGHT_CLICK) &&
2157 (modifiers & kKeyCtrlModifier) &&
2158 (button == kButtonLeft)) {
2159 modifiers = KeyModifiers(int(modifiers) & ~int(kKeyCtrlModifier));
2160 button = kButtonRight;
2161 }
2162#endif
2163
2164 Message* msg = new MouseMessage(
2165 type, pointerType, button, modifiers, mousePos,
2166 wheelDelta, preciseWheel, pressure);
2167
2168 if (display)
2169 msg->setDisplay(display);
2170
2171 if (widget)
2172 msg->setRecipient(widget);
2173
2174 return msg;
2175}
2176
2177// static
2178void Manager::broadcastKeyMsg(Message* msg)
2179{
2180 // Send the message to the widget with capture
2181 if (capture_widget) {
2182 msg->setRecipient(capture_widget);
2183 }
2184 // Send the msg to the focused widget
2185 else if (focus_widget) {
2186 msg->setRecipient(focus_widget);
2187 }
2188 // Finally, send the message to the manager, it'll know what to do
2189 else {
2190 msg->setRecipient(this);
2191 }
2192}
2193
2194/***********************************************************************
2195 Focus Movement
2196 ***********************************************************************/
2197
2198// TODO rewrite this function, it is based in an old code from the
2199// Allegro library GUI code
2200
2201bool Manager::processFocusMovementMessage(Message* msg)
2202{
2203 int (*cmp)(Widget*, int, int) = nullptr;
2204 Widget* focus = nullptr;
2205 Widget* it;
2206 bool ret = false;
2207 Window* window = nullptr;
2208 int c, count;
2209
2210 // Who have the focus
2211 if (focus_widget) {
2212 window = focus_widget->window();
2213 }
2214 else if (!this->children().empty()) {
2215 window = this->getTopWindow();
2216 }
2217
2218 if (!window)
2219 return false;
2220
2221 // How many children want the focus in this window?
2222 count = count_widgets_accept_focus(window);
2223
2224 // One at least
2225 if (count > 0) {
2226 std::vector<Widget*> list(count);
2227
2228 c = 0;
2229
2230 // Create a list of possible candidates to receive the focus
2231 for (it=focus_widget; it; it=next_widget(it)) {
2232 if (does_accept_focus(it) && !(child_accept_focus(it, true)))
2233 list[c++] = it;
2234 }
2235 for (it=window; it != focus_widget; it=next_widget(it)) {
2236 if (does_accept_focus(it) && !(child_accept_focus(it, true)))
2237 list[c++] = it;
2238 }
2239
2240 // Depending on the pressed key...
2241 switch (static_cast<KeyMessage*>(msg)->scancode()) {
2242
2243 case kKeyTab:
2244 // Reverse tab
2245 if ((msg->modifiers() & (kKeyShiftModifier | kKeyCtrlModifier | kKeyAltModifier)) != 0) {
2246 focus = list[count-1];
2247 }
2248 // Normal tab
2249 else if (count > 1) {
2250 focus = list[1];
2251 }
2252 ret = true;
2253 break;
2254
2255 // Arrow keys
2256 case kKeyLeft: if (!cmp) cmp = cmp_left; [[fallthrough]];
2257 case kKeyRight: if (!cmp) cmp = cmp_right; [[fallthrough]];
2258 case kKeyUp: if (!cmp) cmp = cmp_up; [[fallthrough]];
2259 case kKeyDown: if (!cmp) cmp = cmp_down;
2260 // More than one widget
2261 if (count > 1) {
2262 // Position where the focus come
2263 gfx::Point pt = (focus_widget ? focus_widget->bounds().center():
2264 window->bounds().center());
2265
2266 c = (focus_widget ? 1: 0);
2267
2268 // Rearrange the list
2269 for (int i=c; i<count-1; ++i) {
2270 for (int j=i+1; j<count; ++j) {
2271 // Sort the list in ascending order
2272 if ((*cmp)(list[i], pt.x, pt.y) > (*cmp)(list[j], pt.x, pt.y))
2273 std::swap(list[i], list[j]);
2274 }
2275 }
2276
2277#ifdef REPORT_FOCUS_MOVEMENT
2278 // Print list of widgets
2279 for (int i=c; i<count-1; ++i) {
2280 TRACE("list[%d] = %d (%s)\n",
2281 i, (*cmp)(list[i], pt.x, pt.y),
2282 typeid(*list[i]).name());
2283 }
2284#endif
2285
2286 // Check if the new widget to put the focus is not in the wrong way.
2287 if ((*cmp)(list[c], pt.x, pt.y) < std::numeric_limits<int>::max())
2288 focus = list[c];
2289 }
2290 // If only there are one widget, put the focus in this
2291 else
2292 focus = list[0];
2293
2294 ret = true;
2295 break;
2296 }
2297
2298 if ((focus) && (focus != focus_widget))
2299 setFocus(focus);
2300 }
2301
2302 return ret;
2303}
2304
2305static int count_widgets_accept_focus(Widget* widget)
2306{
2307 int count = 0;
2308
2309 for (auto child : widget->children())
2310 count += count_widgets_accept_focus(child);
2311
2312 if ((count == 0) && (does_accept_focus(widget)))
2313 count++;
2314
2315 return count;
2316}
2317
2318static bool child_accept_focus(Widget* widget, bool first)
2319{
2320 for (auto child : widget->children())
2321 if (child_accept_focus(child, false))
2322 return true;
2323
2324 return (first ? false: does_accept_focus(widget));
2325}
2326
2327static Widget* next_widget(Widget* widget)
2328{
2329 if (!widget->children().empty())
2330 return UI_FIRST_WIDGET(widget->children());
2331
2332 while (widget->parent() &&
2333 widget->parent()->type() != kManagerWidget) {
2334 WidgetsList::const_iterator begin = widget->parent()->children().begin();
2335 WidgetsList::const_iterator end = widget->parent()->children().end();
2336 WidgetsList::const_iterator it = std::find(begin, end, widget);
2337
2338 ASSERT(it != end);
2339
2340 if ((it+1) != end)
2341 return *(it+1);
2342 else
2343 widget = widget->parent();
2344 }
2345
2346 return nullptr;
2347}
2348
2349static int cmp_left(Widget* widget, int x, int y)
2350{
2351 int z = x - (widget->bounds().x+widget->bounds().w/2);
2352 if (z <= 0)
2353 return std::numeric_limits<int>::max();
2354 return z + ABS((widget->bounds().y+widget->bounds().h/2) - y) * 8;
2355}
2356
2357static int cmp_right(Widget* widget, int x, int y)
2358{
2359 int z = (widget->bounds().x+widget->bounds().w/2) - x;
2360 if (z <= 0)
2361 return std::numeric_limits<int>::max();
2362 return z + ABS((widget->bounds().y+widget->bounds().h/2) - y) * 8;
2363}
2364
2365static int cmp_up(Widget* widget, int x, int y)
2366{
2367 int z = y - (widget->bounds().y+widget->bounds().h/2);
2368 if (z <= 0)
2369 return std::numeric_limits<int>::max();
2370 return z + ABS((widget->bounds().x+widget->bounds().w/2) - x) * 8;
2371}
2372
2373static int cmp_down(Widget* widget, int x, int y)
2374{
2375 int z = (widget->bounds().y+widget->bounds().h/2) - y;
2376 if (z <= 0)
2377 return std::numeric_limits<int>::max();
2378 return z + ABS((widget->bounds().x+widget->bounds().w/2) - x) * 8;
2379}
2380
2381} // namespace ui
2382