diff --git a/CMakeLists.txt b/CMakeLists.txt index 290d950..abb71f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ add_library(miracle-wm-implementation src/minimal_window_manager.cpp src/minimal_window_manager.h src/config_error_handler.cpp src/config_error_handler.h src/scratchpad.h src/scratchpad.cpp + src/compositor_state.h src/compositor_state.cpp ) add_executable(miracle-wm diff --git a/src/compositor_state.cpp b/src/compositor_state.cpp new file mode 100644 index 0000000..1d4bf58 --- /dev/null +++ b/src/compositor_state.cpp @@ -0,0 +1,75 @@ +/** +Copyright (C) 2024 Matthew Kosarek + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +**/ + +#include "compositor_state.h" + +using namespace miracle; + +[[nodiscard]] std::shared_ptr CompositorState::active() const +{ + if (!focused.expired()) + return focused.lock(); + + return nullptr; +} + +void CompositorState::focus(std::shared_ptr const& container) +{ + auto it = std::find_if(focus_order.begin(), focus_order.end(), [&](auto const& element) + { + return !element.expired() && element.lock() == container; + }); + + if (it != focus_order.end()) + { + std::rotate(focus_order.begin(), it, it + 1); + focused = container; + } +} + +void CompositorState::unfocus(std::shared_ptr const& container) +{ + if (!focused.expired()) + { + if (focused.lock() == container) + focused.reset(); + } +} + +void CompositorState::add(std::shared_ptr const& container) +{ + CompositorState::focus_order.push_back(container); +} + +void CompositorState::remove(std::shared_ptr const& container) +{ + focus_order.erase(std::remove_if(focus_order.begin(), focus_order.end(), [&](auto const& element) + { + return !element.expired() && element.lock() == container; + })); +} + +std::shared_ptr CompositorState::get_first_with_type(ContainerType type) const +{ + for (auto const& container : focus_order) + { + if (!container.expired() && container.lock()->get_type() == type) + return container.lock(); + } + + return nullptr; +} \ No newline at end of file diff --git a/src/compositor_state.h b/src/compositor_state.h index eda327d..d39a0f0 100644 --- a/src/compositor_state.h +++ b/src/compositor_state.h @@ -18,12 +18,15 @@ along with this program. If not, see . #ifndef MIRACLE_WM_COMPOSITOR_STATE_H #define MIRACLE_WM_COMPOSITOR_STATE_H +#include "container.h" + +#include #include #include +#include namespace miracle { -class Container; class Output; enum class WindowManagerMode @@ -40,14 +43,26 @@ enum class WindowManagerMode selecting }; -struct CompositorState +class CompositorState { +public: WindowManagerMode mode = WindowManagerMode::normal; - std::shared_ptr active_output; - std::shared_ptr active; + std::shared_ptr active_output = nullptr; mir::geometry::Point cursor_position; uint32_t modifiers = 0; bool has_clicked_floating_window = false; + + [[nodiscard]] std::shared_ptr active() const; + void focus(std::shared_ptr const& container); + void unfocus(std::shared_ptr const& container); + void add(std::shared_ptr const& container); + void remove(std::shared_ptr const& container); + [[nodiscard]] std::shared_ptr get_first_with_type(ContainerType type) const; + [[nodiscard]] std::vector> const& containers() const { return focus_order; } + +private: + std::weak_ptr focused; + std::vector> focus_order; }; } diff --git a/src/container_group_container.cpp b/src/container_group_container.cpp index ff879a3..22becd6 100644 --- a/src/container_group_container.cpp +++ b/src/container_group_container.cpp @@ -282,7 +282,7 @@ void ContainerGroupContainer::animation_handle(uint32_t uint_32) bool ContainerGroupContainer::is_focused() const { - return state.active.get() == this; + return state.active().get() == this; } bool ContainerGroupContainer::is_fullscreen() const diff --git a/src/floating_window_container.cpp b/src/floating_window_container.cpp index 08d17a6..6d53bf6 100644 --- a/src/floating_window_container.cpp +++ b/src/floating_window_container.cpp @@ -274,7 +274,7 @@ void FloatingWindowContainer::animation_handle(uint32_t handle) bool FloatingWindowContainer::is_focused() const { - return state.active.get() == this; + return state.active().get() == this; } bool FloatingWindowContainer::is_fullscreen() const diff --git a/src/i3_command.cpp b/src/i3_command.cpp index 4d9aa56..d7ce775 100644 --- a/src/i3_command.cpp +++ b/src/i3_command.cpp @@ -15,6 +15,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . **/ +#define MIR_LOG_COMPONENT "miracle::i3_command" + #include "i3_command.h" #include "jpcre2.h" #include "string_extensions.h" @@ -22,9 +24,10 @@ along with this program. If not, see . #include "window_helpers.h" #include -#include -#define MIR_LOG_COMPONENT "miracle::i3_command" #include +#include +#include +#include using namespace miracle; @@ -171,7 +174,7 @@ bool I3ScopedCommandList::meets_criteria(miral::Window const& window, WindowCont switch (criteria.type) { case I3ScopeType::all: - break; + return true; case I3ScopeType::title: { auto& info = window_controller.info_for(window); @@ -179,6 +182,13 @@ bool I3ScopedCommandList::meets_criteria(miral::Window const& window, WindowCont auto const& name = info.name(); return re.match(name); } + case I3ScopeType::class_: + { + auto& info = window_controller.app_info(window); + jp::Regex re(criteria.regex.value()); + auto const& name = miral::name_of(info.application()); + return re.match(name); + } default: break; } diff --git a/src/i3_command.h b/src/i3_command.h index 027d07f..bce77aa 100644 --- a/src/i3_command.h +++ b/src/i3_command.h @@ -55,6 +55,7 @@ enum class I3CommandType input }; +// https://i3wm.org/docs/userguide.html#command_criteria enum class I3ScopeType { none, diff --git a/src/i3_command_executor.cpp b/src/i3_command_executor.cpp index 0568d63..73dc187 100644 --- a/src/i3_command_executor.cpp +++ b/src/i3_command_executor.cpp @@ -35,12 +35,12 @@ using namespace miracle; I3CommandExecutor::I3CommandExecutor( miracle::Policy& policy, WorkspaceManager& workspace_manager, - miral::WindowManagerTools const& tools, + CompositorState const& state, AutoRestartingLauncher& launcher, WindowController& window_controller) : policy { policy }, workspace_manager { workspace_manager }, - tools { tools }, + state { state }, launcher { launcher }, window_controller { window_controller } { @@ -90,21 +90,20 @@ void I3CommandExecutor::process(miracle::I3ScopedCommandList const& command_list miral::Window I3CommandExecutor::get_window_meeting_criteria(I3ScopedCommandList const& command_list) { - miral::Window result; - tools.find_application([&](miral::ApplicationInfo const& info) + for (auto const& container : state.containers()) { - for (auto const& window : info.windows()) + if (container.expired()) + continue; + + auto window = container.lock()->window(); + if (auto const& w = window.value()) { - if (command_list.meets_criteria(window, window_controller)) - { - result = window; - return true; - } + if (command_list.meets_criteria(w, window_controller)) + return window.value(); } + } - return false; - }); - return result; + return miral::Window {}; } void I3CommandExecutor::process_exec(miracle::I3Command const& command, miracle::I3ScopedCommandList const& command_list) @@ -203,16 +202,12 @@ void I3CommandExecutor::process_focus(I3Command const& command, I3ScopedCommandL else if (arg == "down") policy.try_select(Direction::down); else if (arg == "parent") - mir::log_warning("'focus parent' is not supported, see https://github.com/mattkae/miracle-wm/issues/117"); // TODO + policy.try_select_parent(); else if (arg == "child") mir::log_warning("'focus child' is not supported, see https://github.com/mattkae/miracle-wm/issues/117"); // TODO else if (arg == "prev") { - auto active_window = tools.active_window(); - if (!active_window) - return; - - auto container = window_controller.get_container(active_window); + auto container = state.active(); if (!container) return; @@ -234,11 +229,7 @@ void I3CommandExecutor::process_focus(I3Command const& command, I3ScopedCommandL } else if (arg == "next") { - auto active_window = tools.active_window(); - if (!active_window) - return; - - auto container = window_controller.get_container(active_window); + auto container = state.active(); if (!container) return; @@ -259,13 +250,38 @@ void I3CommandExecutor::process_focus(I3Command const& command, I3ScopedCommandL } } else if (arg == "floating") - mir::log_warning("'focus floating' is not supported, see https://github.com/mattkae/miracle-wm/issues/117"); // TODO + policy.try_select_floating(); else if (arg == "tiling") - mir::log_warning("'focus tiling' is not supported, see https://github.com/mattkae/miracle-wm/issues/117"); // TODO + policy.try_select_tiling(); else if (arg == "mode_toggle") - mir::log_warning("'focus mode_toggle' is not supported, see https://github.com/mattkae/miracle-wm/issues/117"); // TODO + policy.try_select_toggle(); else if (arg == "output") - mir::log_warning("'focus output' is not supported, see https://github.com/canonical/mir/issues/3357"); // TODO + { + if (command.arguments.size() < 2) + { + mir::log_error("process_focus: 'focus output' must have more than two arguments"); + return; + } + + auto const& arg1 = command.arguments[1]; + if (arg1 == "next") + policy.try_select_next_output(); + else if (arg1 == "prev") + policy.try_select_prev_output(); + else if (arg1 == "left") + policy.try_select_output(Direction::left); + else if (arg1 == "right") + policy.try_select_output(Direction::right); + else if (arg1 == "up") + policy.try_select_output(Direction::up); + else if (arg1 == "down") + policy.try_select_output(Direction::down); + else + { + auto names = std::vector(command.arguments.begin() + 1, command.arguments.end()); + policy.try_select_output(names); + } + } } namespace @@ -350,7 +366,7 @@ void I3CommandExecutor::process_move(I3Command const& command, I3ScopedCommandLi auto const& arg1 = command.arguments[index++]; if (arg1 == "center") { - auto active = policy.get_state().active.get(); + auto active = policy.get_state().active().get(); auto area = active_output->get_area(); float x = (float)area.size.width.as_int() / 2.f - (float)active->get_visible_area().size.width.as_int() / 2.f; float y = (float)area.size.height.as_int() / 2.f - (float)active->get_visible_area().size.height.as_int() / 2.f; @@ -410,7 +426,7 @@ void I3CommandExecutor::process_move(I3Command const& command, I3ScopedCommandLi y = end_y; } - auto active = policy.get_state().active; + auto active = policy.get_state().active(); float x_pos = x / 2.f - (float)active->get_visible_area().size.width.as_int() / 2.f; float y_pos = y / 2.f - (float)active->get_visible_area().size.height.as_int() / 2.f; policy.try_move_to((int)x_pos, (int)y_pos); @@ -653,7 +669,7 @@ void I3CommandExecutor::process_layout(I3Command const& command, I3ScopedCommand } else { - auto const& container = policy.get_state().active; + auto container = policy.get_state().active(); if (!container) { mir::log_error("process_layout: container unavailable"); diff --git a/src/i3_command_executor.h b/src/i3_command_executor.h index 3e1748e..07992f1 100644 --- a/src/i3_command_executor.h +++ b/src/i3_command_executor.h @@ -18,6 +18,7 @@ along with this program. If not, see . #ifndef MIRACLEWM_I_3_COMMAND_EXECUTOR_H #define MIRACLEWM_I_3_COMMAND_EXECUTOR_H +#include "compositor_state.h" #include "i3_command.h" #include @@ -37,7 +38,7 @@ class I3CommandExecutor I3CommandExecutor( Policy&, WorkspaceManager&, - miral::WindowManagerTools const&, + CompositorState const&, AutoRestartingLauncher&, WindowController&); void process(I3ScopedCommandList const&); @@ -45,7 +46,7 @@ class I3CommandExecutor private: Policy& policy; WorkspaceManager& workspace_manager; - miral::WindowManagerTools tools; + CompositorState const& state; AutoRestartingLauncher& launcher; WindowController& window_controller; diff --git a/src/leaf_container.cpp b/src/leaf_container.cpp index d815f83..b730d67 100644 --- a/src/leaf_container.cpp +++ b/src/leaf_container.cpp @@ -323,10 +323,10 @@ void LeafContainer::animation_handle(uint32_t handle) bool LeafContainer::is_focused() const { - if (state.active.get() == this || parent.lock()->is_focused()) + if (state.active().get() == this || parent.lock()->is_focused()) return true; - auto group = Container::as_group(state.active); + auto group = Container::as_group(state.active()); if (!group) return false; diff --git a/src/parent_container.cpp b/src/parent_container.cpp index 5f9e4fd..173dfe2 100644 --- a/src/parent_container.cpp +++ b/src/parent_container.cpp @@ -640,7 +640,7 @@ void ParentContainer::on_focus_gained() { for (auto const& container : sub_nodes) { - if (container != state.active && container->window()) + if (container != state.active() && container->window()) node_interface.send_to_back(container->window().value()); } } diff --git a/src/policy.cpp b/src/policy.cpp index a7adf96..a414b71 100644 --- a/src/policy.cpp +++ b/src/policy.cpp @@ -22,6 +22,7 @@ along with this program. If not, see . #include "container_group_container.h" #include "feature_flags.h" #include "floating_tree_container.h" +#include "parent_container.h" #include "shell_component_container.h" #include "window_helpers.h" #include "window_tools_accessor.h" @@ -70,7 +71,7 @@ Policy::Policy( { return get_output_list(); }) }, animator(server.the_main_loop(), config), window_controller(tools, animator, state), - i3_command_executor(*this, workspace_manager, tools, external_client_launcher, window_controller), + i3_command_executor(*this, workspace_manager, compositor_state, external_client_launcher, window_controller), surface_tracker { surface_tracker }, ipc { std::make_shared(runner, workspace_manager, *this, server.the_main_loop(), i3_command_executor, config) }, scratchpad_(window_controller, state) @@ -244,7 +245,7 @@ bool Policy::handle_pointer_event(MirPointerEvent const* event) { state.mode = WindowManagerMode::selecting; group_selection = std::make_shared(state); - state.active = group_selection; + state.add(group_selection); mode_observer_registrar.advise_changed(state.mode); } } @@ -268,14 +269,12 @@ bool Policy::handle_pointer_event(MirPointerEvent const* event) { if (auto window = intersected->window().value()) { - if (state.active != intersected) - { + if (state.active() != intersected) window_controller.select_active_window(window); - } } } - if (state.has_clicked_floating_window || (state.active && state.active->get_type() == ContainerType::floating_window)) + if (state.has_clicked_floating_window || (state.active() && state.active()->get_type() == ContainerType::floating_window)) { if (action == mir_pointer_action_button_down) state.has_clicked_floating_window = true; @@ -311,15 +310,13 @@ auto Policy::place_new_window( } auto new_spec = requested_specification; - pending_output = state.active_output; pending_allocation = state.active_output->allocate_position(app_info, new_spec, {}); return new_spec; } void Policy::advise_new_window(miral::WindowInfo const& window_info) { - auto shared_output = pending_output.lock(); - if (!shared_output) + if (!state.active_output) { mir::log_warning("create_container: output unavailable"); auto window = window_info.window(); @@ -340,14 +337,12 @@ void Policy::advise_new_window(miral::WindowInfo const& window_info) return; } - auto container = shared_output->create_container(window_info, pending_allocation); - + auto container = state.active_output->create_container(window_info, pending_allocation); container->animation_handle(animator.register_animateable()); container->on_open(); + state.add(container); pending_allocation.container_type = ContainerType::none; - pending_output.reset(); - surface_tracker.add(window_info.window()); } @@ -400,7 +395,7 @@ void Policy::advise_focus_gained(const miral::WindowInfo& window_info) if (workspace && workspace != state.active_output->active()) return; - state.active = container; + state.focus(container); container->on_focus_gained(); break; } @@ -416,8 +411,7 @@ void Policy::advise_focus_lost(const miral::WindowInfo& window_info) return; } - if (container == state.active) - state.active = nullptr; + state.unfocus(container); container->on_focus_lost(); } @@ -446,9 +440,7 @@ void Policy::advise_delete_window(const miral::WindowInfo& window_info) scratchpad_.remove(container); surface_tracker.remove(window_info.window()); - - if (state.active == container) - state.active = nullptr; + state.unfocus(container); } void Policy::advise_move_to(miral::WindowInfo const& window_info, geom::Point top_left) @@ -662,13 +654,13 @@ void Policy::advise_end() void Policy::try_toggle_resize_mode() { - if (!state.active) + if (!state.active()) { state.mode = WindowManagerMode::normal; return; } - if (state.active->get_type() != ContainerType::leaf) + if (state.active()->get_type() != ContainerType::leaf) { state.mode = WindowManagerMode::normal; return; @@ -687,10 +679,10 @@ bool Policy::try_request_vertical() if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - state.active->request_vertical_layout(); + state.active()->request_vertical_layout(); return true; } @@ -699,10 +691,10 @@ bool Policy::try_toggle_layout(bool cycle_thru_all) if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - state.active->toggle_layout(cycle_thru_all); + state.active()->toggle_layout(cycle_thru_all); return true; } @@ -711,10 +703,10 @@ bool Policy::try_request_horizontal() if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - state.active->request_horizontal_layout(); + state.active()->request_horizontal_layout(); return true; } @@ -723,10 +715,10 @@ bool Policy::try_resize(miracle::Direction direction) if (state.mode != WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - return state.active->resize(direction); + return state.active()->resize(direction); } bool Policy::try_move(miracle::Direction direction) @@ -734,10 +726,10 @@ bool Policy::try_move(miracle::Direction direction) if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - return state.active->move(direction); + return state.active()->move(direction); } bool Policy::try_move_by(miracle::Direction direction, int pixels) @@ -745,10 +737,10 @@ bool Policy::try_move_by(miracle::Direction direction, int pixels) if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - return state.active->move_by(direction, pixels); + return state.active()->move_by(direction, pixels); } bool Policy::try_move_to(int x, int y) @@ -756,10 +748,10 @@ bool Policy::try_move_to(int x, int y) if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - return state.active->move_to(x, y); + return state.active()->move_to(x, y); } bool Policy::try_select(miracle::Direction direction) @@ -767,18 +759,88 @@ bool Policy::try_select(miracle::Direction direction) if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) + return false; + + return state.active()->select_next(direction); +} + +bool Policy::try_select_parent() +{ + if (state.mode != WindowManagerMode::normal) + return false; + + if (!state.active()) + return false; + + if (!state.active()->get_parent().expired()) + { + state.focus(state.active()->get_parent().lock()); + return true; + } + else + { + mir::log_error("try_select_parent: no parent to select"); + return false; + } +} + +bool Policy::try_select_floating() +{ + if (state.mode != WindowManagerMode::normal) + return false; + + if (auto to_select = state.get_first_with_type(ContainerType::floating_window)) + { + if (auto const& window = to_select->window()) + { + window_controller.select_active_window(window.value()); + return true; + } + } + + return false; +} + +bool Policy::try_select_tiling() +{ + if (state.mode != WindowManagerMode::normal) + return false; + + if (auto to_select = state.get_first_with_type(ContainerType::leaf)) + { + if (auto const& window = to_select->window()) + { + window_controller.select_active_window(window.value()); + return true; + } + } + + return false; +} + +bool Policy::try_select_toggle() +{ + if (state.mode != WindowManagerMode::normal) return false; - return state.active->select_next(direction); + if (auto const active = state.active()) + { + if (active->get_type() == ContainerType::leaf) + return try_select_floating(); + else if (active->get_type() == ContainerType::floating_window) + return try_select_tiling(); + } + + return false; } bool Policy::try_close_window() { - if (!state.active) + if (!state.active()) return false; - auto window = state.active->window(); + auto window = state.active()->window(); if (!window) return false; @@ -798,10 +860,10 @@ bool Policy::try_toggle_fullscreen() if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - return state.active->toggle_fullscreen(); + return state.active()->toggle_fullscreen(); } bool Policy::select_workspace(int number, bool back_and_forth) @@ -873,9 +935,9 @@ bool Policy::move_active_to_workspace(int number, bool back_and_forth) if (!can_move_container()) return false; - auto container = state.active; + auto container = state.active(); container->get_output()->delete_container(container); - state.active = nullptr; + state.unfocus(container); if (workspace_manager.request_workspace( state.active_output.get(), number, back_and_forth)) @@ -892,9 +954,9 @@ bool Policy::move_active_to_workspace_named(std::string const& name, bool back_a if (!can_move_container()) return false; - auto container = state.active; + auto container = state.active(); container->get_output()->delete_container(container); - state.active = nullptr; + state.unfocus(nullptr); if (workspace_manager.request_workspace(state.active_output.get(), name, back_and_forth)) { @@ -910,9 +972,9 @@ bool Policy::move_active_to_next() if (!can_move_container()) return false; - auto container = state.active; + auto container = state.active(); container->get_output()->delete_container(container); - state.active = nullptr; + state.unfocus(container); if (workspace_manager.request_next(state.active_output)) { @@ -928,9 +990,9 @@ bool Policy::move_active_to_prev() if (!can_move_container()) return false; - auto container = state.active; + auto container = state.active(); container->get_output()->delete_container(container); - state.active = nullptr; + state.unfocus(container); if (workspace_manager.request_prev(state.active_output)) { @@ -946,9 +1008,9 @@ bool Policy::move_active_to_back_and_forth() if (!can_move_container()) return false; - auto container = state.active; + auto container = state.active(); container->get_output()->delete_container(container); - state.active = nullptr; + state.unfocus(container); if (workspace_manager.request_back_and_forth()) { @@ -965,7 +1027,7 @@ bool Policy::move_to_scratchpad() return false; // Only floating or tiled windows can be moved to the scratchpad - auto container = state.active; + auto container = state.active(); if (container->get_type() != ContainerType::floating_window && container->get_type() != ContainerType::leaf) { @@ -1000,10 +1062,10 @@ bool Policy::can_move_container() const if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - if (state.active->is_fullscreen()) + if (state.active()->is_fullscreen()) return false; return true; @@ -1018,7 +1080,8 @@ std::shared_ptr Policy::toggle_floating_internal(std::shared_ptrcreate_container(info, result); new_container->handle_ready(); - window_controller.select_active_window(state.active->window().value()); + state.add(new_container); + window_controller.select_active_window(state.active()->window().value()); return new_container; }; @@ -1041,6 +1104,8 @@ std::shared_ptr Policy::toggle_floating_internal(std::shared_ptrallocate_position(window_manager_tools.info_for(window->application()), spec, { ContainerType::floating_window }); window_controller.modify(*window, spec); + state.remove(container); + // Finally, declare it ready return handle_ready(*window, result); } @@ -1062,6 +1127,8 @@ std::shared_ptr Policy::toggle_floating_internal(std::shared_ptrallocate_position(window_manager_tools.info_for(window->application()), spec, { ContainerType::leaf }); window_controller.modify(*window, spec); + state.remove(container); + // Finally, declare it ready return handle_ready(*window, result); } @@ -1076,10 +1143,10 @@ bool Policy::toggle_floating() if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - toggle_floating_internal(state.active); + toggle_floating_internal(state.active()); return true; } @@ -1088,10 +1155,10 @@ bool Policy::toggle_pinned_to_workspace() if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - return state.active->pinned(!state.active->pinned()); + return state.active()->pinned(!state.active()->pinned()); } bool Policy::set_is_pinned(bool pinned) @@ -1099,10 +1166,10 @@ bool Policy::set_is_pinned(bool pinned) if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; - return state.active->pinned(pinned); + return state.active()->pinned(pinned); } bool Policy::toggle_tabbing() @@ -1110,7 +1177,7 @@ bool Policy::toggle_tabbing() if (!can_set_layout()) return false; - return state.active->toggle_tabbing(); + return state.active()->toggle_tabbing(); } bool Policy::toggle_stacking() @@ -1118,7 +1185,7 @@ bool Policy::toggle_stacking() if (!can_set_layout()) return false; - return state.active->toggle_stacking(); + return state.active()->toggle_stacking(); } bool Policy::set_layout(LayoutScheme scheme) @@ -1126,7 +1193,7 @@ bool Policy::set_layout(LayoutScheme scheme) if (!can_set_layout()) return false; - return state.active->set_layout(scheme); + return state.active()->set_layout(scheme); } bool Policy::set_layout_default() @@ -1134,15 +1201,94 @@ bool Policy::set_layout_default() if (!can_set_layout()) return false; - return state.active->set_layout(config->get_default_layout_scheme()); -}; + return state.active()->set_layout(config->get_default_layout_scheme()); +} + +void Policy::move_cursor_to_output(std::shared_ptr const& output) +{ + auto const& extents = output->get_output().extents(); + window_manager_tools.move_cursor_to({ extents.top_left.x.as_int() + extents.size.width.as_int() / 2.f, + extents.top_left.y.as_int() + extents.size.height.as_int() / 2.f }); +} + +bool Policy::try_select_next_output() +{ + for (size_t i = 0; i < output_list.size(); i++) + { + if (output_list[i] == state.active_output) + { + size_t j = i + 1; + if (j == output_list.size()) + j = 0; + + move_cursor_to_output(output_list[j]); + return true; + } + } + + return false; +} + +bool Policy::try_select_prev_output() +{ + for (int i = output_list.size() - 1; i >= 0; i++) + { + if (output_list[i] == state.active_output) + { + size_t j = i - 1; + if (j < 0) + j = output_list.size() - 1; + + move_cursor_to_output(output_list[j]); + return true; + } + } + + return false; +} + +bool Policy::try_select_output(Direction direction) +{ + return false; +} + +bool Policy::try_select_output(std::vector const& names) +{ + if (!state.active_output) + return false; + + auto current_name = state.active_output->get_output().name(); + size_t next = 0; + for (size_t i = 0; i < names.size(); i++) + { + if (names[i] == current_name) + { + next = i + 1; + break; + } + } + + if (next == names.size()) + next = 0; + + for (auto const& output : output_list) + { + if (output->get_output().name() == names[next]) + { + move_cursor_to_output(output); + return true; + } + } + + return false; +} bool Policy::can_set_layout() const { if (state.mode == WindowManagerMode::resizing) return false; - if (!state.active) + if (!state.active()) return false; return true; diff --git a/src/policy.h b/src/policy.h index 75fcc6f..5454335 100644 --- a/src/policy.h +++ b/src/policy.h @@ -110,6 +110,10 @@ class Policy : public miral::WindowManagementPolicy bool try_move_by(Direction direction, int pixels); bool try_move_to(int x, int y); bool try_select(Direction direction); + bool try_select_parent(); + bool try_select_floating(); + bool try_select_tiling(); + bool try_select_toggle(); bool try_close_window(); bool quit(); bool try_toggle_fullscreen(); @@ -134,6 +138,11 @@ class Policy : public miral::WindowManagementPolicy bool toggle_stacking(); bool set_layout(LayoutScheme scheme); bool set_layout_default(); + void move_cursor_to_output(std::shared_ptr const&); + bool try_select_next_output(); + bool try_select_prev_output(); + bool try_select_output(Direction direction); + bool try_select_output(std::vector const& names); // Getters @@ -150,7 +159,6 @@ class Policy : public miral::WindowManagementPolicy bool is_starting_ = true; CompositorState& state; std::vector> output_list; - std::weak_ptr pending_output; AllocationHint pending_allocation; std::vector orphaned_window_list; miral::WindowManagerTools window_manager_tools; diff --git a/src/tiling_window_tree.cpp b/src/tiling_window_tree.cpp index c641f48..9c857f7 100644 --- a/src/tiling_window_tree.cpp +++ b/src/tiling_window_tree.cpp @@ -790,5 +790,5 @@ Workspace* TilingWindowTree::get_workspace() const std::shared_ptr TilingWindowTree::active_container() const { - return Container::as_leaf(state.active); + return Container::as_leaf(state.active()); } diff --git a/src/window_controller.h b/src/window_controller.h index 632570a..d8d0347 100644 --- a/src/window_controller.h +++ b/src/window_controller.h @@ -18,6 +18,7 @@ along with this program. If not, see . #ifndef MIRACLEWM_TILING_INTERFACE_H #define MIRACLEWM_TILING_INTERFACE_H +#include #include #include @@ -52,6 +53,7 @@ class WindowController virtual void set_user_data(miral::Window const&, std::shared_ptr const&) = 0; virtual void modify(miral::Window const&, miral::WindowSpecification const&) = 0; virtual miral::WindowInfo& info_for(miral::Window const&) = 0; + virtual miral::ApplicationInfo& app_info(miral::Window const&) = 0; }; } diff --git a/src/window_manager_tools_window_controller.cpp b/src/window_manager_tools_window_controller.cpp index c959699..f313f55 100644 --- a/src/window_manager_tools_window_controller.cpp +++ b/src/window_manager_tools_window_controller.cpp @@ -236,6 +236,11 @@ miral::WindowInfo& WindowManagerToolsWindowController::info_for(miral::Window co return tools.info_for(window); } +miral::ApplicationInfo& WindowManagerToolsWindowController::app_info(miral::Window const& window) +{ + return tools.info_for(window.application()); +} + void WindowManagerToolsWindowController::close(miral::Window const& window) { tools.ask_client_to_close(window); diff --git a/src/window_manager_tools_window_controller.h b/src/window_manager_tools_window_controller.h index 06ec9fb..936413b 100644 --- a/src/window_manager_tools_window_controller.h +++ b/src/window_manager_tools_window_controller.h @@ -48,6 +48,7 @@ class WindowManagerToolsWindowController : public WindowController void set_user_data(miral::Window const&, std::shared_ptr const&) override; void modify(miral::Window const&, miral::WindowSpecification const&) override; miral::WindowInfo& info_for(miral::Window const&) override; + miral::ApplicationInfo& app_info(miral::Window const&) override; void close(miral::Window const& window) override; private: diff --git a/src/workspace.cpp b/src/workspace.cpp index 24aed4d..36512fb 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -178,7 +178,7 @@ void Workspace::handle_ready_hack(LeafContainer& container) window_controller.raise(window->window().value()); if (tree->has_fullscreen_window()) - window_controller.raise(state.active->window().value()); + window_controller.raise(state.active()->window().value()); } void Workspace::delete_container(std::shared_ptr const& container) @@ -347,10 +347,10 @@ void Workspace::graft(std::shared_ptr const& container) std::shared_ptr Workspace::get_layout_container() { - if (!state.active) + if (!state.active()) return nullptr; - auto parent = state.active->get_parent().lock(); + auto parent = state.active()->get_parent().lock(); if (!parent) return nullptr; diff --git a/tests/tiling_window_tree_test.cpp b/tests/tiling_window_tree_test.cpp index 50a65a7..37c3c52 100644 --- a/tests/tiling_window_tree_test.cpp +++ b/tests/tiling_window_tree_test.cpp @@ -91,6 +91,7 @@ class StubWindowController : public miracle::WindowController void set_user_data(miral::Window const&, std::shared_ptr const&) override { } void modify(miral::Window const&, miral::WindowSpecification const&) override { } miral::WindowInfo& info_for(miral::Window const&) override { } + miral::ApplicationInfo& app_info(miral::Window const&) override { } private: std::vector>>& pairs; @@ -125,8 +126,9 @@ class TilingWindowTreeTest : public testing::Test auto leaf = tree.confirm_window(info, nullptr); pairs.push_back({ window, leaf }); - state.active = leaf; + state.add(leaf); tree.advise_focus_gained(*leaf); + state.focus(leaf); return leaf; }