From 96104b55249ad0201b1be805e1874c759fdd0ac9 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 4 Apr 2019 01:44:35 +0200 Subject: [PATCH] Taskbar: More bringup work. We now see a basic window list. --- Applications/Taskbar/Makefile | 1 + Applications/Taskbar/TaskbarWidget.cpp | 7 ++- Applications/Taskbar/TaskbarWidget.h | 5 +- Applications/Taskbar/TaskbarWindow.cpp | 52 +++++++++++++-- Applications/Taskbar/TaskbarWindow.h | 5 +- Applications/Taskbar/WindowList.cpp | 17 +++++ Applications/Taskbar/WindowList.h | 80 ++++++++++++++++++++++++ LibC/SharedBuffer.cpp | 2 +- LibGUI/GBoxLayout.cpp | 5 ++ LibGUI/GEventLoop.cpp | 16 +++++ LibGUI/GEventLoop.h | 1 + LibGUI/GLayout.cpp | 9 +++ LibGUI/GLayout.h | 2 + LibGUI/GWidget.cpp | 4 ++ Servers/WindowServer/WSAPITypes.h | 8 +++ Servers/WindowServer/WSMessage.h | 63 +++++++++++++++++++ Servers/WindowServer/WSWindow.cpp | 33 ++++++++++ Servers/WindowServer/WSWindow.h | 3 + Servers/WindowServer/WSWindowManager.cpp | 26 +++++++- Servers/WindowServer/WSWindowManager.h | 12 ++++ 20 files changed, 339 insertions(+), 12 deletions(-) create mode 100644 Applications/Taskbar/WindowList.cpp create mode 100644 Applications/Taskbar/WindowList.h diff --git a/Applications/Taskbar/Makefile b/Applications/Taskbar/Makefile index a766c0aa94c..9b86e8dea62 100644 --- a/Applications/Taskbar/Makefile +++ b/Applications/Taskbar/Makefile @@ -1,6 +1,7 @@ OBJS = \ TaskbarWindow.o \ TaskbarWidget.o \ + WindowList.o \ main.o APP = Taskbar diff --git a/Applications/Taskbar/TaskbarWidget.cpp b/Applications/Taskbar/TaskbarWidget.cpp index 114600ecb1e..9e25d969285 100644 --- a/Applications/Taskbar/TaskbarWidget.cpp +++ b/Applications/Taskbar/TaskbarWidget.cpp @@ -5,13 +5,14 @@ #include #include -TaskbarWidget::TaskbarWidget(GWidget* parent) +TaskbarWidget::TaskbarWidget(WindowList& window_list, GWidget* parent) : GFrame(parent) + , m_window_list(window_list) { set_fill_with_background_color(true); set_layout(make(Orientation::Horizontal)); - layout()->set_margins({ 0, 8, 0, 8 }); - layout()->set_spacing(8); + layout()->set_margins({ 0, 3, 0, 3 }); + layout()->set_spacing(3); set_frame_thickness(1); set_frame_shape(GFrame::Shape::Panel); diff --git a/Applications/Taskbar/TaskbarWidget.h b/Applications/Taskbar/TaskbarWidget.h index 196120405a1..1226f3df833 100644 --- a/Applications/Taskbar/TaskbarWidget.h +++ b/Applications/Taskbar/TaskbarWidget.h @@ -1,8 +1,10 @@ #include +class WindowList; + class TaskbarWidget final : public GFrame { public: - TaskbarWidget(GWidget* parent = nullptr); + TaskbarWidget(WindowList&, GWidget* parent = nullptr); virtual ~TaskbarWidget() override; virtual const char* class_name() const override { return "TaskbarWidget"; } @@ -10,4 +12,5 @@ public: private: virtual void paint_event(GPaintEvent&) override; + WindowList& m_window_list; }; diff --git a/Applications/Taskbar/TaskbarWindow.cpp b/Applications/Taskbar/TaskbarWindow.cpp index 4fd3abbddc5..b139c1fed38 100644 --- a/Applications/Taskbar/TaskbarWindow.cpp +++ b/Applications/Taskbar/TaskbarWindow.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -16,7 +17,7 @@ TaskbarWindow::TaskbarWindow() GDesktop::the().on_rect_change = [this] (const Rect& rect) { on_screen_rect_change(rect); }; - auto* widget = new TaskbarWidget; + auto* widget = new TaskbarWidget(m_window_list); set_main_widget(widget); } @@ -32,10 +33,51 @@ void TaskbarWindow::on_screen_rect_change(const Rect& rect) void TaskbarWindow::wm_event(GWMEvent& event) { -#if 0 + WindowIdentifier identifier { event.client_id(), event.window_id() }; switch (event.type()) { - case GEvent::WM_WindowAdded: - m_window_list.append({}) + case GEvent::WM_WindowAdded: { + auto& added_event = static_cast(event); + printf("WM_WindowAdded: client_id=%d, window_id=%d, title=%s, rect=%s\n", + added_event.client_id(), + added_event.window_id(), + added_event.title().characters(), + added_event.rect().to_string().characters() + ); + auto& window = m_window_list.ensure_window(identifier); + window.set_title(added_event.title()); + window.set_rect(added_event.rect()); + window.set_button(new GButton(main_widget())); + window.button()->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed); + window.button()->set_preferred_size({ 100, 22 }); + window.button()->set_caption(window.title()); + update(); + break; + } + case GEvent::WM_WindowRemoved: { + auto& removed_event = static_cast(event); + printf("WM_WindowRemoved: client_id=%d, window_id=%d\n", + removed_event.client_id(), + removed_event.window_id() + ); + m_window_list.remove_window(identifier); + update(); + break; + } + case GEvent::WM_WindowStateChanged: { + auto& changed_event = static_cast(event); + printf("WM_WindowStateChanged: client_id=%d, window_id=%d, title=%s, rect=%s\n", + changed_event.client_id(), + changed_event.window_id(), + changed_event.title().characters(), + changed_event.rect().to_string().characters() + ); + auto& window = m_window_list.ensure_window(identifier); + window.set_title(changed_event.title()); + window.set_rect(changed_event.rect()); + window.button()->set_caption(changed_event.title()); + break; + } + default: + break; } -#endif } diff --git a/Applications/Taskbar/TaskbarWindow.h b/Applications/Taskbar/TaskbarWindow.h index d6eff7eafa9..c8f5f052d99 100644 --- a/Applications/Taskbar/TaskbarWindow.h +++ b/Applications/Taskbar/TaskbarWindow.h @@ -1,12 +1,13 @@ #include #include +#include "WindowList.h" class TaskbarWindow final : public GWindow { public: TaskbarWindow(); virtual ~TaskbarWindow() override; - int taskbar_height() const { return 20; } + int taskbar_height() const { return 28; } virtual const char* class_name() const override { return "TaskbarWindow"; } @@ -14,4 +15,6 @@ private: void on_screen_rect_change(const Rect&); virtual void wm_event(GWMEvent&) override; + + WindowList m_window_list; }; diff --git a/Applications/Taskbar/WindowList.cpp b/Applications/Taskbar/WindowList.cpp new file mode 100644 index 00000000000..d0b9ac3a40a --- /dev/null +++ b/Applications/Taskbar/WindowList.cpp @@ -0,0 +1,17 @@ +#include "WindowList.h" + +Window& WindowList::ensure_window(const WindowIdentifier& identifier) +{ + auto it = m_windows.find(identifier); + if (it != m_windows.end()) + return *it->value; + auto window = make(identifier); + auto& window_ref = *window; + m_windows.set(identifier, move(window)); + return window_ref; +} + +void WindowList::remove_window(const WindowIdentifier& identifier) +{ + m_windows.remove(identifier); +} diff --git a/Applications/Taskbar/WindowList.h b/Applications/Taskbar/WindowList.h new file mode 100644 index 00000000000..7f5484376b7 --- /dev/null +++ b/Applications/Taskbar/WindowList.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include +#include + +class WindowIdentifier { +public: + WindowIdentifier(int client_id, int window_id) + : m_client_id(client_id) + , m_window_id(window_id) + { + } + + int client_id() const { return m_client_id; } + int window_id() const { return m_window_id; } + + bool operator==(const WindowIdentifier& other) const + { + return m_client_id == other.m_client_id && m_window_id == other.m_window_id; + } +private: + int m_client_id { -1 }; + int m_window_id { -1 }; +}; + +namespace AK { +template<> +struct Traits { + static unsigned hash(const WindowIdentifier& w) { return pair_int_hash(w.client_id(), w.window_id()); } + static void dump(const WindowIdentifier& w) { kprintf("WindowIdentifier(%d, %d)", w.client_id(), w.window_id()); } +}; +} + +class Window { +public: + explicit Window(const WindowIdentifier& identifier) + : m_identifier(identifier) + { + } + + ~Window() + { + delete m_button; + } + + WindowIdentifier identifier() const { return m_identifier; } + + String title() const { return m_title; } + void set_title(const String& title) { m_title = title; } + + Rect rect() const { return m_rect; } + void set_rect(const Rect& rect) { m_rect = rect; } + + GButton* button() { return m_button; } + void set_button(GButton* button) { m_button = button; } + +private: + WindowIdentifier m_identifier; + String m_title; + Rect m_rect; + GButton* m_button { nullptr }; +}; + +class WindowList { +public: + template void for_each_window(Callback callback) + { + for (auto& it : m_windows) + callback(*it.value); + } + + Window& ensure_window(const WindowIdentifier&); + void remove_window(const WindowIdentifier&); + +private: + HashMap> m_windows; +}; diff --git a/LibC/SharedBuffer.cpp b/LibC/SharedBuffer.cpp index 3df9a3b2399..ef8c2620755 100644 --- a/LibC/SharedBuffer.cpp +++ b/LibC/SharedBuffer.cpp @@ -51,6 +51,6 @@ void SharedBuffer::seal() int rc = seal_shared_buffer(m_shared_buffer_id); if (rc < 0) { perror("seal_shared_buffer"); - exit(1); + ASSERT_NOT_REACHED(); } } diff --git a/LibGUI/GBoxLayout.cpp b/LibGUI/GBoxLayout.cpp index cee646c4911..7296bac0be4 100644 --- a/LibGUI/GBoxLayout.cpp +++ b/LibGUI/GBoxLayout.cpp @@ -34,6 +34,9 @@ void GBoxLayout::run(GWidget& widget) dbgprintf("GBoxLayout: Starting with available size: %s\n", available_size.to_string().characters()); for (auto& entry : m_entries) { + if (!entry.widget) + continue; + if (!entry.widget->is_visible()) continue; ++number_of_visible_entries; @@ -81,6 +84,8 @@ void GBoxLayout::run(GWidget& widget) int current_y = margins().top(); for (auto& entry : m_entries) { + if (!entry.widget) + continue; if (!entry.widget->is_visible()) continue; Rect rect(current_x, current_y, 0, 0); diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index 9bd68bee1f3..a7c02b8bb6b 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -268,6 +268,17 @@ void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event) ASSERT_NOT_REACHED(); } +void GEventLoop::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& window) +{ + if (event.type == WSAPI_ServerMessage::WM_WindowAdded) + return post_event(window, make(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length), event.wm.rect)); + if (event.type == WSAPI_ServerMessage::WM_WindowStateChanged) + return post_event(window, make(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length), event.wm.rect)); + if (event.type == WSAPI_ServerMessage::WM_WindowRemoved) + return post_event(window, make(event.wm.client_id, event.wm.window_id)); + ASSERT_NOT_REACHED(); +} + void GEventLoop::wait_for_event() { fd_set rfds; @@ -397,6 +408,11 @@ void GEventLoop::process_unprocessed_messages() case WSAPI_ServerMessage::Type::WindowResized: handle_resize_event(event, *window); break; + case WSAPI_ServerMessage::Type::WM_WindowAdded: + case WSAPI_ServerMessage::Type::WM_WindowRemoved: + case WSAPI_ServerMessage::Type::WM_WindowStateChanged: + handle_wm_event(event, *window); + break; default: break; } diff --git a/LibGUI/GEventLoop.h b/LibGUI/GEventLoop.h index 51214215eff..f32bba887f3 100644 --- a/LibGUI/GEventLoop.h +++ b/LibGUI/GEventLoop.h @@ -60,6 +60,7 @@ private: void handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow&); void handle_menu_event(const WSAPI_ServerMessage&); void handle_window_entered_or_left_event(const WSAPI_ServerMessage&, GWindow&); + void handle_wm_event(const WSAPI_ServerMessage&, GWindow&); void get_next_timer_expiration(timeval&); void connect_to_server(); diff --git a/LibGUI/GLayout.cpp b/LibGUI/GLayout.cpp index 70b637a1e03..59d3690d1ff 100644 --- a/LibGUI/GLayout.cpp +++ b/LibGUI/GLayout.cpp @@ -40,6 +40,15 @@ void GLayout::add_widget(GWidget& widget) m_owner->notify_layout_changed(Badge()); } +void GLayout::remove_widget(GWidget& widget) +{ + m_entries.remove_first_matching([&] (auto& entry) { + return entry.widget.ptr() == &widget; + }); + if (m_owner) + m_owner->notify_layout_changed(Badge()); +} + void GLayout::set_spacing(int spacing) { if (m_spacing == spacing) diff --git a/LibGUI/GLayout.h b/LibGUI/GLayout.h index 78ff71a17b1..599ad6248c8 100644 --- a/LibGUI/GLayout.h +++ b/LibGUI/GLayout.h @@ -16,6 +16,8 @@ public: void add_widget(GWidget&); void add_layout(OwnPtr&&); + void remove_widget(GWidget&); + virtual void run(GWidget&) = 0; void notify_adopted(Badge, GWidget&); diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 553dfa6cd8a..738d6538282 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -27,6 +27,10 @@ void GWidget::child_event(GChildEvent& event) if (event.child() && event.child()->is_widget() && layout()) layout()->add_widget(static_cast(*event.child())); } + if (event.type() == GEvent::ChildRemoved) { + if (event.child() && event.child()->is_widget() && layout()) + layout()->remove_widget(static_cast(*event.child())); + } return GObject::child_event(event); } diff --git a/Servers/WindowServer/WSAPITypes.h b/Servers/WindowServer/WSAPITypes.h index 06bbc56885a..209b0f851cc 100644 --- a/Servers/WindowServer/WSAPITypes.h +++ b/Servers/WindowServer/WSAPITypes.h @@ -91,6 +91,9 @@ struct WSAPI_ServerMessage { DidSetWallpaper, DidGetWallpaper, ScreenRectChanged, + WM_WindowAdded, + WM_WindowRemoved, + WM_WindowStateChanged, }; Type type { Invalid }; int window_id { -1 }; @@ -103,6 +106,11 @@ struct WSAPI_ServerMessage { int server_pid; WSAPI_Rect screen_rect; } greeting; + struct { + int client_id; + int window_id; + WSAPI_Rect rect; + } wm; struct { WSAPI_Rect rect; } screen; diff --git a/Servers/WindowServer/WSMessage.h b/Servers/WindowServer/WSMessage.h index 6b7428169e2..5d63a893597 100644 --- a/Servers/WindowServer/WSMessage.h +++ b/Servers/WindowServer/WSMessage.h @@ -26,6 +26,10 @@ public: WindowCloseRequest, WindowResized, + WM_WindowAdded, + WM_WindowRemoved, + WM_WindowStateChanged, + __Begin_API_Client_Requests, APICreateMenubarRequest, APIDestroyMenubarRequest, @@ -581,3 +585,62 @@ private: Rect m_old_rect; Rect m_rect; }; + +class WSWMEvent : public WSMessage { +public: + WSWMEvent(Type type, int client_id, int window_id) + : WSMessage(type) + , m_client_id(client_id) + , m_window_id(window_id) + { + } + + int client_id() const { return m_client_id; } + int window_id() const { return m_window_id; } + +private: + int m_client_id; + int m_window_id; +}; + +class WSWMWindowAddedEvent : public WSWMEvent { +public: + WSWMWindowAddedEvent(int client_id, int window_id, const String& title, const Rect& rect) + : WSWMEvent(WSMessage::WM_WindowAdded, client_id, window_id) + , m_title(title) + , m_rect(rect) + { + } + + String title() const { return m_title; } + Rect rect() const { return m_rect; } + +private: + String m_title; + Rect m_rect; +}; + +class WSWMWindowRemovedEvent : public WSWMEvent { +public: + WSWMWindowRemovedEvent(int client_id, int window_id) + : WSWMEvent(WSMessage::WM_WindowRemoved, client_id, window_id) + { + } +}; + +class WSWMWindowStateChangedEvent : public WSWMEvent { +public: + WSWMWindowStateChangedEvent(int client_id, int window_id, const String& title, const Rect& rect) + : WSWMEvent(WSMessage::WM_WindowStateChanged, client_id, window_id) + , m_title(title) + , m_rect(rect) + { + } + + String title() const { return m_title; } + Rect rect() const { return m_rect; } + +private: + String m_title; + Rect m_rect; +}; diff --git a/Servers/WindowServer/WSWindow.cpp b/Servers/WindowServer/WSWindow.cpp index 778973ce801..b648b2503a5 100644 --- a/Servers/WindowServer/WSWindow.cpp +++ b/Servers/WindowServer/WSWindow.cpp @@ -28,6 +28,9 @@ WSWindow::WSWindow(WSClientConnection& client, WSWindowType window_type, int win , m_window_id(window_id) , m_icon(default_window_icon()) { + // FIXME: This should not be hard-coded here. + if (m_type == WSWindowType::Taskbar) + m_listens_to_wm_events = true; WSWindowManager::the().add_window(*this); } @@ -138,6 +141,36 @@ void WSWindow::on_message(const WSMessage& message) server_message.window.old_rect = static_cast(message).old_rect(); server_message.window.rect = static_cast(message).rect(); break; + case WSMessage::WM_WindowAdded: { + auto& added_event = static_cast(message); + server_message.type = WSAPI_ServerMessage::Type::WM_WindowAdded; + server_message.wm.client_id = added_event.client_id(); + server_message.wm.window_id = added_event.window_id(); + ASSERT(added_event.title().length() < sizeof(server_message.text)); + memcpy(server_message.text, added_event.title().characters(), added_event.title().length()); + server_message.text_length = added_event.title().length(); + server_message.wm.rect = added_event.rect(); + break; + } + case WSMessage::WM_WindowRemoved: { + auto& removed_event = static_cast(message); + server_message.type = WSAPI_ServerMessage::Type::WM_WindowRemoved; + server_message.wm.client_id = removed_event.client_id(); + server_message.wm.window_id = removed_event.window_id(); + break; + } + case WSMessage::WM_WindowStateChanged: { + auto& changed_event = static_cast(message); + server_message.type = WSAPI_ServerMessage::Type::WM_WindowStateChanged; + server_message.wm.client_id = changed_event.client_id(); + server_message.wm.window_id = changed_event.window_id(); + ASSERT(changed_event.title().length() < sizeof(server_message.text)); + memcpy(server_message.text, changed_event.title().characters(), changed_event.title().length()); + server_message.text_length = changed_event.title().length(); + server_message.wm.rect = changed_event.rect(); + break; + } + default: break; } diff --git a/Servers/WindowServer/WSWindow.h b/Servers/WindowServer/WSWindow.h index 19cb9699922..165c32cef01 100644 --- a/Servers/WindowServer/WSWindow.h +++ b/Servers/WindowServer/WSWindow.h @@ -20,6 +20,8 @@ public: bool is_blocked_by_modal_window() const; + bool listens_to_wm_events() const { return m_listens_to_wm_events; } + WSClientConnection* client() { return m_client; } const WSClientConnection* client() const { return m_client; } @@ -124,6 +126,7 @@ private: bool m_has_painted_since_last_resize { false }; bool m_modal { false }; bool m_resizable { false }; + bool m_listens_to_wm_events { false }; RetainPtr m_backing_store; RetainPtr m_last_backing_store; int m_window_id { -1 }; diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index 3875e8d5910..2400e854aff 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -294,7 +294,7 @@ void get_cpu_usage(unsigned& busy, unsigned& idle) FILE* fp = fopen("/proc/all", "r"); if (!fp) { perror("failed to open /proc/all"); - exit(1); + ASSERT_NOT_REACHED(); } for (;;) { char buf[BUFSIZ]; @@ -507,6 +507,12 @@ void WSWindowManager::add_window(WSWindow& window) set_active_window(&window); if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) m_switcher.refresh(); + + for_each_window_listening_to_wm_events([&window] (WSWindow& listener) { + if (window.client()) + WSMessageLoop::the().post_message(listener, make(window.client()->client_id(), window.window_id(), window.title(), window.rect())); + return IterationDecision::Continue; + }); } void WSWindowManager::move_to_front_and_make_active(WSWindow& window) @@ -534,6 +540,12 @@ void WSWindowManager::remove_window(WSWindow& window) set_active_window(*m_windows.begin()); if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) m_switcher.refresh(); + + for_each_window_listening_to_wm_events([&window] (WSWindow& listener) { + if (window.client()) + WSMessageLoop::the().post_message(listener, make(window.client()->client_id(), window.window_id())); + return IterationDecision::Continue; + }); } void WSWindowManager::notify_title_changed(WSWindow& window) @@ -542,6 +554,12 @@ void WSWindowManager::notify_title_changed(WSWindow& window) invalidate(outer_window_rect(window)); if (m_switcher.is_visible()) m_switcher.refresh(); + + for_each_window_listening_to_wm_events([&window] (WSWindow& listener) { + if (window.client()) + WSMessageLoop::the().post_message(listener, make(window.client()->client_id(), window.window_id(), window.title(), window.rect())); + return IterationDecision::Continue; + }); } void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect) @@ -553,6 +571,12 @@ void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect invalidate(outer_window_rect(new_rect)); if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher) m_switcher.refresh(); + + for_each_window_listening_to_wm_events([&window] (WSWindow& listener) { + if (window.client()) + WSMessageLoop::the().post_message(listener, make(window.client()->client_id(), window.window_id(), window.title(), window.rect())); + return IterationDecision::Continue; + }); } void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, const WSMouseEvent& event) diff --git a/Servers/WindowServer/WSWindowManager.h b/Servers/WindowServer/WSWindowManager.h index 328f2d7f6e3..8cc58c7b094 100644 --- a/Servers/WindowServer/WSWindowManager.h +++ b/Servers/WindowServer/WSWindowManager.h @@ -112,6 +112,7 @@ private: template IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback); template IterationDecision for_each_visible_window_from_front_to_back(Callback); template IterationDecision for_each_visible_window_from_back_to_front(Callback); + template void for_each_window_listening_to_wm_events(Callback); template void for_each_active_menubar_menu(Callback); void close_current_menu(); virtual void on_message(const WSMessage&) override; @@ -265,3 +266,14 @@ IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Ca return IterationDecision::Abort; return for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback); } + +template +void WSWindowManager::for_each_window_listening_to_wm_events(Callback callback) +{ + for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) { + if (!window->listens_to_wm_events()) + continue; + if (callback(*window) == IterationDecision::Abort) + return; + } +}