Serenity Operating System
at master 580 lines 24 kB view raw
1/* 2 * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include "WindowStack.h" 10#include <AK/HashMap.h> 11#include <AK/HashTable.h> 12#include <AK/WeakPtr.h> 13#include <LibCore/ConfigFile.h> 14#include <LibCore/ElapsedTimer.h> 15#include <LibGfx/Color.h> 16#include <LibGfx/DisjointRectSet.h> 17#include <LibGfx/Painter.h> 18#include <LibGfx/Palette.h> 19#include <LibGfx/Rect.h> 20#include <WindowServer/Cursor.h> 21#include <WindowServer/Event.h> 22#include <WindowServer/KeymapSwitcher.h> 23#include <WindowServer/MenuManager.h> 24#include <WindowServer/ResizeDirection.h> 25#include <WindowServer/ScreenLayout.h> 26#include <WindowServer/SystemEffects.h> 27#include <WindowServer/WMConnectionFromClient.h> 28#include <WindowServer/WindowSwitcher.h> 29#include <WindowServer/WindowType.h> 30 31namespace WindowServer { 32 33int const double_click_speed_max = 900; 34int const double_click_speed_min = 100; 35 36extern RefPtr<Core::ConfigFile> g_config; 37 38class Screen; 39class MouseEvent; 40class Window; 41class ConnectionFromClient; 42class WindowSwitcher; 43class Button; 44class DndOverlay; 45class WindowGeometryOverlay; 46 47class WindowManager : public Core::Object { 48 C_OBJECT(WindowManager) 49 50 friend class Compositor; 51 friend class WindowFrame; 52 friend class WindowSwitcher; 53 54public: 55 static constexpr size_t default_window_stack_rows = 2; 56 static constexpr size_t default_window_stack_columns = 2; 57 static_assert(default_window_stack_rows >= 1); 58 static_assert(default_window_stack_columns >= 1); 59 static constexpr unsigned max_window_stack_rows = 16; 60 static constexpr unsigned max_window_stack_columns = 16; 61 62 static WindowManager& the(); 63 64 virtual ~WindowManager() override = default; 65 66 Palette palette() const { return Palette(*m_palette); } 67 68 RefPtr<Core::ConfigFile> config() const { return m_config; } 69 void reload_config(); 70 71 void add_window(Window&); 72 void remove_window(Window&); 73 74 void notify_title_changed(Window&); 75 void notify_rect_changed(Window&, Gfx::IntRect const& oldRect, Gfx::IntRect const& newRect); 76 void notify_minimization_state_changed(Window&); 77 void notify_opacity_changed(Window&); 78 void notify_occlusion_state_changed(Window&); 79 void notify_progress_changed(Window&); 80 void notify_modified_changed(Window&); 81 82 Gfx::IntRect tiled_window_rect(Window const&, WindowTileType tile_type = WindowTileType::Maximized, bool relative_to_window_screen = false) const; 83 84 ConnectionFromClient const* dnd_client() const { return m_dnd_client.ptr(); } 85 Core::MimeData const& dnd_mime_data() const { return *m_dnd_mime_data; } 86 87 void start_dnd_drag(ConnectionFromClient&, DeprecatedString const& text, Gfx::Bitmap const*, Core::MimeData const&); 88 void end_dnd_drag(); 89 90 void set_accepts_drag(bool); 91 92 Window* active_window() 93 { 94 VERIFY(m_current_window_stack); 95 return m_current_window_stack->active_window(); 96 } 97 Window const* active_window() const 98 { 99 VERIFY(m_current_window_stack); 100 return m_current_window_stack->active_window(); 101 } 102 103 Window* foremost_popup_window(WindowStack& stack = WindowManager::the().current_window_stack()); 104 void request_close_fragile_windows(WindowStack& stack = WindowManager::the().current_window_stack()); 105 106 ConnectionFromClient const* active_client() const; 107 108 Window* window_with_active_menu() { return m_window_with_active_menu; } 109 Window const* window_with_active_menu() const { return m_window_with_active_menu; } 110 void set_window_with_active_menu(Window*); 111 112 Window* highlight_window() { return m_highlight_window; } 113 Window const* highlight_window() const { return m_highlight_window; } 114 void set_highlight_window(Window*); 115 116 void move_to_front_and_make_active(Window&); 117 118 Gfx::IntRect desktop_rect(Screen&) const; 119 Gfx::IntRect arena_rect_for_type(Screen&, WindowType) const; 120 121 Cursor const& active_cursor() const; 122 Cursor const& hidden_cursor() const { return *m_hidden_cursor; } 123 Cursor const& arrow_cursor() const { return *m_arrow_cursor; } 124 Cursor const& crosshair_cursor() const { return *m_crosshair_cursor; } 125 Cursor const& hand_cursor() const { return *m_hand_cursor; } 126 Cursor const& help_cursor() const { return *m_help_cursor; } 127 Cursor const& resize_horizontally_cursor() const { return *m_resize_horizontally_cursor; } 128 Cursor const& resize_vertically_cursor() const { return *m_resize_vertically_cursor; } 129 Cursor const& resize_diagonally_tlbr_cursor() const { return *m_resize_diagonally_tlbr_cursor; } 130 Cursor const& resize_diagonally_bltr_cursor() const { return *m_resize_diagonally_bltr_cursor; } 131 Cursor const& resize_column_cursor() const { return *m_resize_column_cursor; } 132 Cursor const& resize_row_cursor() const { return *m_resize_row_cursor; } 133 Cursor const& i_beam_cursor() const { return *m_i_beam_cursor; } 134 Cursor const& disallowed_cursor() const { return *m_disallowed_cursor; } 135 Cursor const& move_cursor() const { return *m_move_cursor; } 136 Cursor const& drag_cursor() const { return *m_drag_cursor; } 137 Cursor const& drag_copy_cursor() const { return *m_drag_copy_cursor; } 138 Cursor const& wait_cursor() const { return *m_wait_cursor; } 139 Cursor const& eyedropper_cursor() const { return *m_eyedropper_cursor; } 140 Cursor const& zoom_cursor() const { return *m_zoom_cursor; } 141 142 int cursor_highlight_radius() const { return m_cursor_highlight_radius; } 143 Gfx::Color cursor_highlight_color() const { return m_cursor_highlight_color; } 144 145 Gfx::Font const& font() const; 146 Gfx::Font const& window_title_font() const; 147 148 bool set_screen_layout(ScreenLayout&&, bool, DeprecatedString&); 149 ScreenLayout get_screen_layout() const; 150 bool save_screen_layout(DeprecatedString&); 151 152 void set_acceleration_factor(double); 153 void set_scroll_step_size(unsigned); 154 void set_double_click_speed(int); 155 int double_click_speed() const; 156 void set_mouse_buttons_switched(bool); 157 bool are_mouse_buttons_switched() const; 158 void set_natural_scroll(bool); 159 bool is_natural_scroll() const; 160 161 void set_active_window(Window*); 162 void set_hovered_button(Button*); 163 164 Button const* cursor_tracking_button() const { return m_cursor_tracking_button.ptr(); } 165 void set_cursor_tracking_button(Button*); 166 167 void set_resize_candidate(Window&, ResizeDirection); 168 void clear_resize_candidate(); 169 ResizeDirection resize_direction_of_window(Window const&); 170 171 void greet_window_manager(WMConnectionFromClient&); 172 void tell_wms_window_state_changed(Window&); 173 void tell_wms_window_icon_changed(Window&); 174 void tell_wms_window_rect_changed(Window&); 175 void tell_wms_screen_rects_changed(); 176 void tell_wms_applet_area_size_changed(Gfx::IntSize); 177 void tell_wms_super_key_pressed(); 178 void tell_wms_super_space_key_pressed(); 179 void tell_wms_super_d_key_pressed(); 180 void tell_wms_super_digit_key_pressed(u8); 181 void tell_wms_current_window_stack_changed(); 182 183 void check_hide_geometry_overlay(Window&); 184 185 void start_window_resize(Window&, Gfx::IntPoint, MouseButton, ResizeDirection); 186 void start_window_resize(Window&, MouseEvent const&, ResizeDirection); 187 void start_window_move(Window&, MouseEvent const&); 188 void start_window_move(Window&, Gfx::IntPoint); 189 190 Window const* active_fullscreen_window() const 191 { 192 if (active_window() && active_window()->is_fullscreen()) 193 return active_window(); 194 return nullptr; 195 }; 196 197 Window* active_fullscreen_window() 198 { 199 if (active_window() && active_window()->is_fullscreen()) 200 return active_window(); 201 return nullptr; 202 } 203 204 bool update_theme(DeprecatedString theme_path, DeprecatedString theme_name, bool keep_desktop_background, Optional<DeprecatedString> const& color_scheme_path); 205 void invalidate_after_theme_or_font_change(); 206 207 bool set_theme_override(Core::AnonymousBuffer const& theme_override); 208 Optional<Core::AnonymousBuffer> get_theme_override() const; 209 void clear_theme_override(); 210 bool is_theme_overridden() { return m_theme_overridden; } 211 Optional<DeprecatedString> get_preferred_color_scheme() { return m_preferred_color_scheme; } 212 213 bool set_hovered_window(Window*); 214 void deliver_mouse_event(Window&, MouseEvent const&); 215 216 void did_popup_a_menu(Badge<Menu>); 217 218 void system_menu_doubleclick(Window& window, MouseEvent const& event); 219 bool is_menu_doubleclick(Window& window, MouseEvent const& event) const; 220 221 void minimize_windows(Window&, bool); 222 void hide_windows(Window&, bool); 223 void maximize_windows(Window&, bool); 224 void set_always_on_top(Window&, bool); 225 226 template<typename Callback> 227 Window* for_each_window_in_modal_chain(Window& window, Callback callback) 228 { 229 Function<Window*(Window&)> recurse = [&](Window& w) -> Window* { 230 if (!w.is_modal()) { 231 auto decision = callback(w); 232 if (decision == IterationDecision::Break) 233 return &w; 234 } 235 for (auto& child : w.child_windows()) { 236 if (!child || child->is_destroyed() || !child->is_modal()) 237 continue; 238 auto decision = callback(*child); 239 if (auto* result = recurse(*child)) 240 return result; 241 if (decision == IterationDecision::Break) 242 return child; 243 } 244 return nullptr; 245 }; 246 if (auto* modeless = window.modeless_ancestor()) 247 return recurse(*modeless); 248 return nullptr; 249 } 250 bool is_window_in_modal_chain(Window& chain_window, Window& other_window); 251 252 Gfx::IntPoint get_recommended_window_position(Gfx::IntPoint desired); 253 254 void reload_icon_bitmaps_after_scale_change(); 255 256 void reevaluate_hover_state_for_window(Window* = nullptr); 257 Window* hovered_window() const { return m_hovered_window.ptr(); } 258 259 void switch_to_window_stack(WindowStack&, Window* = nullptr, bool show_overlay = true); 260 void switch_to_window_stack(u32 row, u32 col, Window* carry = nullptr, bool show_overlay = true) 261 { 262 if (row < window_stack_rows() && col < window_stack_columns()) 263 switch_to_window_stack(*(*m_window_stacks[row])[col], carry, show_overlay); 264 } 265 266 size_t window_stack_rows() const { return m_window_stacks.size(); } 267 size_t window_stack_columns() const { return m_window_stacks[0]->size(); } 268 269 bool apply_workspace_settings(unsigned rows, unsigned columns, bool save); 270 271 WindowStack& current_window_stack() 272 { 273 VERIFY(m_current_window_stack); 274 return *m_current_window_stack; 275 } 276 277 template<typename F> 278 IterationDecision for_each_window_stack(F f) 279 { 280 for (auto& row : m_window_stacks) { 281 for (auto& stack : *row) { 282 IterationDecision decision = f(*stack); 283 if (decision != IterationDecision::Continue) 284 return decision; 285 } 286 } 287 return IterationDecision::Continue; 288 } 289 290 WindowStack& window_stack_for_window(Window&); 291 292 static constexpr bool is_stationary_window_type(WindowType window_type) 293 { 294 switch (window_type) { 295 case WindowType::Normal: 296 return false; 297 default: 298 return true; 299 } 300 } 301 302 static constexpr bool is_fragile_window_type(WindowType window_type) 303 { 304 switch (window_type) { 305 case WindowType::Autocomplete: 306 case WindowType::Popup: 307 case WindowType::Tooltip: 308 return true; 309 default: 310 return false; 311 } 312 } 313 314 void did_switch_window_stack(Badge<Compositor>, WindowStack&, WindowStack&); 315 316 template<typename Callback> 317 IterationDecision for_each_visible_window_from_back_to_front(Callback, WindowStack* = nullptr); 318 template<typename Callback> 319 IterationDecision for_each_visible_window_from_front_to_back(Callback, WindowStack* = nullptr); 320 321 MultiScaleBitmaps const* overlay_rect_shadow() const { return m_overlay_rect_shadow.ptr(); } 322 323 void apply_cursor_theme(DeprecatedString const& name); 324 325 void set_cursor_highlight_radius(int radius); 326 void set_cursor_highlight_color(Gfx::Color color); 327 328 bool is_cursor_highlight_enabled() const { return m_cursor_highlight_radius > 0 && m_cursor_highlight_enabled; } 329 330 void load_system_effects(); 331 void apply_system_effects(Vector<bool>, ShowGeometry); 332 SystemEffects& system_effects() { return m_system_effects; } 333 334 RefPtr<KeymapSwitcher> keymap_switcher() { return m_keymap_switcher; } 335 336 Window* automatic_cursor_tracking_window() { return m_automatic_cursor_tracking_window; } 337 Window const* automatic_cursor_tracking_window() const { return m_automatic_cursor_tracking_window; } 338 void set_automatic_cursor_tracking_window(Window* window) { m_automatic_cursor_tracking_window = window; } 339 340 u8 last_processed_buttons() { return m_last_processed_buttons; } 341 342private: 343 explicit WindowManager(Gfx::PaletteImpl&); 344 345 void notify_new_active_window(Window&); 346 void notify_previous_active_window(Window&); 347 void notify_active_window_input_preempted(); 348 void notify_active_window_input_restored(); 349 350 void process_mouse_event(MouseEvent&); 351 void process_event_for_doubleclick(Window& window, MouseEvent& event); 352 bool process_ongoing_window_resize(MouseEvent const&); 353 bool process_ongoing_window_move(MouseEvent&); 354 bool process_ongoing_drag(MouseEvent&); 355 bool process_ongoing_active_input_mouse_event(MouseEvent const&); 356 bool process_mouse_event_for_titlebar_buttons(MouseEvent const&); 357 void process_mouse_event_for_window(HitTestResult&, MouseEvent const&); 358 359 void process_key_event(KeyEvent&); 360 361 template<typename Callback> 362 void for_each_window_manager(Callback); 363 364 virtual void event(Core::Event&) override; 365 void tell_wm_about_window(WMConnectionFromClient& conn, Window&); 366 void tell_wm_about_window_icon(WMConnectionFromClient& conn, Window&); 367 void tell_wm_about_window_rect(WMConnectionFromClient& conn, Window&); 368 void tell_wm_about_current_window_stack(WMConnectionFromClient&); 369 void pick_new_active_window(Window*); 370 371 bool sync_config_to_disk(); 372 373 [[nodiscard]] static WindowStack& get_rendering_window_stacks(WindowStack*&); 374 375 RefPtr<Cursor const> m_hidden_cursor; 376 RefPtr<Cursor const> m_arrow_cursor; 377 RefPtr<Cursor const> m_hand_cursor; 378 RefPtr<Cursor const> m_help_cursor; 379 RefPtr<Cursor const> m_resize_horizontally_cursor; 380 RefPtr<Cursor const> m_resize_vertically_cursor; 381 RefPtr<Cursor const> m_resize_diagonally_tlbr_cursor; 382 RefPtr<Cursor const> m_resize_diagonally_bltr_cursor; 383 RefPtr<Cursor const> m_resize_column_cursor; 384 RefPtr<Cursor const> m_resize_row_cursor; 385 RefPtr<Cursor const> m_i_beam_cursor; 386 RefPtr<Cursor const> m_disallowed_cursor; 387 RefPtr<Cursor const> m_move_cursor; 388 RefPtr<Cursor const> m_drag_cursor; 389 RefPtr<Cursor const> m_drag_copy_cursor; 390 RefPtr<Cursor const> m_wait_cursor; 391 RefPtr<Cursor const> m_crosshair_cursor; 392 RefPtr<Cursor const> m_eyedropper_cursor; 393 RefPtr<Cursor const> m_zoom_cursor; 394 int m_cursor_highlight_radius { 0 }; 395 Gfx::Color m_cursor_highlight_color; 396 bool m_cursor_highlight_enabled { false }; 397 398 RefPtr<MultiScaleBitmaps> m_overlay_rect_shadow; 399 400 // Setup 2 rows 1 column by default 401 Vector<NonnullOwnPtr<Vector<NonnullOwnPtr<WindowStack>, default_window_stack_columns>>, default_window_stack_rows> m_window_stacks; 402 WindowStack* m_current_window_stack { nullptr }; 403 404 struct DoubleClickInfo { 405 struct ClickMetadata { 406 Core::ElapsedTimer clock; 407 Gfx::IntPoint last_position; 408 }; 409 410 ClickMetadata const& metadata_for_button(MouseButton) const; 411 ClickMetadata& metadata_for_button(MouseButton); 412 413 void reset() 414 { 415 m_primary = {}; 416 m_secondary = {}; 417 m_middle = {}; 418 m_backward = {}; 419 m_forward = {}; 420 } 421 422 WeakPtr<Window> m_clicked_window; 423 424 private: 425 ClickMetadata m_primary; 426 ClickMetadata m_secondary; 427 ClickMetadata m_middle; 428 ClickMetadata m_backward; 429 ClickMetadata m_forward; 430 }; 431 432 bool is_considered_doubleclick(MouseEvent const&, DoubleClickInfo::ClickMetadata const&) const; 433 434 Gfx::IntPoint to_floating_cursor_position(Gfx::IntPoint) const; 435 436 DoubleClickInfo m_double_click_info; 437 int m_double_click_speed { 0 }; 438 int m_max_distance_for_double_click { 4 }; 439 bool m_previous_event_was_super_keydown { false }; 440 bool m_mouse_buttons_switched { false }; 441 bool m_natural_scroll { false }; 442 bool m_theme_overridden { false }; 443 Optional<DeprecatedString> m_preferred_color_scheme { OptionalNone() }; 444 445 WeakPtr<Window> m_hovered_window; 446 WeakPtr<Window> m_highlight_window; 447 WeakPtr<Window> m_window_with_active_menu; 448 WeakPtr<Window> m_automatic_cursor_tracking_window; 449 450 OwnPtr<WindowGeometryOverlay> m_geometry_overlay; 451 WeakPtr<Window> m_move_window; 452 Gfx::IntPoint m_move_origin; 453 Gfx::IntPoint m_move_window_origin; 454 Gfx::IntPoint m_move_window_cursor_position; 455 Gfx::IntPoint m_mouse_down_origin; 456 457 WeakPtr<Window> m_resize_window; 458 WeakPtr<Window> m_resize_candidate; 459 MouseButton m_resizing_mouse_button { MouseButton::None }; 460 Gfx::IntRect m_resize_window_original_rect; 461 Gfx::IntPoint m_resize_origin; 462 ResizeDirection m_resize_direction { ResizeDirection::None }; 463 464 u8 m_keyboard_modifiers { 0 }; 465 u8 m_last_processed_buttons { MouseButton::None }; 466 467 NonnullRefPtr<WindowSwitcher> m_switcher; 468 NonnullRefPtr<KeymapSwitcher> m_keymap_switcher; 469 470 WeakPtr<Button> m_cursor_tracking_button; 471 WeakPtr<Button> m_hovered_button; 472 473 NonnullRefPtr<Gfx::PaletteImpl> m_palette; 474 475 RefPtr<Core::ConfigFile> m_config; 476 477 OwnPtr<DndOverlay> m_dnd_overlay; 478 WeakPtr<ConnectionFromClient> m_dnd_client; 479 DeprecatedString m_dnd_text; 480 bool m_dnd_accepts_drag { false }; 481 482 RefPtr<Core::MimeData const> m_dnd_mime_data; 483 484 WindowStack* m_switching_to_window_stack { nullptr }; 485 Vector<WeakPtr<Window>, 4> m_carry_window_to_new_stack; 486 487 SystemEffects m_system_effects; 488}; 489 490template<typename Callback> 491inline IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Callback callback, WindowStack* specific_stack) 492{ 493 auto* window_stack = specific_stack; 494 WindowStack* transitioning_to_window_stack = nullptr; 495 if (!window_stack) 496 window_stack = &get_rendering_window_stacks(transitioning_to_window_stack); 497 auto for_each_window = [&]<WindowType window_type>() { 498 if constexpr (is_stationary_window_type(window_type)) { 499 auto& stationary_stack = window_stack->stationary_window_stack(); 500 return stationary_stack.for_each_visible_window_of_type_from_back_to_front(window_type, callback); 501 } else { 502 auto decision = window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback); 503 if (decision == IterationDecision::Continue && transitioning_to_window_stack) 504 decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_back_to_front(window_type, callback); 505 return decision; 506 } 507 }; 508 if (for_each_window.template operator()<WindowType::Desktop>() == IterationDecision::Break) 509 return IterationDecision::Break; 510 if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break) 511 return IterationDecision::Break; 512 if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break) 513 return IterationDecision::Break; 514 if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break) 515 return IterationDecision::Break; 516 if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break) 517 return IterationDecision::Break; 518 if (for_each_window.template operator()<WindowType::Autocomplete>() == IterationDecision::Break) 519 return IterationDecision::Break; 520 if (for_each_window.template operator()<WindowType::Popup>() == IterationDecision::Break) 521 return IterationDecision::Break; 522 if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break) 523 return IterationDecision::Break; 524 if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break) 525 return IterationDecision::Break; 526 return for_each_window.template operator()<WindowType::WindowSwitcher>(); 527} 528 529template<typename Callback> 530inline IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Callback callback, WindowStack* specific_stack) 531{ 532 auto* window_stack = specific_stack; 533 WindowStack* transitioning_to_window_stack = nullptr; 534 if (!window_stack) 535 window_stack = &get_rendering_window_stacks(transitioning_to_window_stack); 536 auto for_each_window = [&]<WindowType window_type>() { 537 if constexpr (is_stationary_window_type(window_type)) { 538 auto& stationary_stack = window_stack->stationary_window_stack(); 539 return stationary_stack.for_each_visible_window_of_type_from_front_to_back(window_type, callback); 540 } else { 541 auto decision = window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback); 542 if (decision == IterationDecision::Continue && transitioning_to_window_stack) 543 decision = transitioning_to_window_stack->for_each_visible_window_of_type_from_front_to_back(window_type, callback); 544 return decision; 545 } 546 }; 547 if (for_each_window.template operator()<WindowType::WindowSwitcher>() == IterationDecision::Break) 548 return IterationDecision::Break; 549 if (for_each_window.template operator()<WindowType::Menu>() == IterationDecision::Break) 550 return IterationDecision::Break; 551 if (for_each_window.template operator()<WindowType::Tooltip>() == IterationDecision::Break) 552 return IterationDecision::Break; 553 if (for_each_window.template operator()<WindowType::Popup>() == IterationDecision::Break) 554 return IterationDecision::Break; 555 if (for_each_window.template operator()<WindowType::Autocomplete>() == IterationDecision::Break) 556 return IterationDecision::Break; 557 if (for_each_window.template operator()<WindowType::Notification>() == IterationDecision::Break) 558 return IterationDecision::Break; 559 if (for_each_window.template operator()<WindowType::AppletArea>() == IterationDecision::Break) 560 return IterationDecision::Break; 561 if (for_each_window.template operator()<WindowType::Taskbar>() == IterationDecision::Break) 562 return IterationDecision::Break; 563 if (for_each_window.template operator()<WindowType::Normal>() == IterationDecision::Break) 564 return IterationDecision::Break; 565 return for_each_window.template operator()<WindowType::Desktop>(); 566} 567 568template<typename Callback> 569void WindowManager::for_each_window_manager(Callback callback) 570{ 571 auto& connections = WMConnectionFromClient::s_connections; 572 573 // FIXME: this isn't really ordered... does it need to be? 574 for (auto it = connections.begin(); it != connections.end(); ++it) { 575 if (callback(*it->value) == IterationDecision::Break) 576 return; 577 } 578} 579 580}