Skip to content

Commit

Permalink
feature: implementing the remainder of the focus IPC (#298)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkae authored Nov 17, 2024
1 parent 514f39f commit 0297e0a
Show file tree
Hide file tree
Showing 19 changed files with 401 additions and 118 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
75 changes: 75 additions & 0 deletions src/compositor_state.cpp
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
**/

#include "compositor_state.h"

using namespace miracle;

[[nodiscard]] std::shared_ptr<Container> CompositorState::active() const
{
if (!focused.expired())
return focused.lock();

return nullptr;
}

void CompositorState::focus(std::shared_ptr<Container> 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<Container> const& container)
{
if (!focused.expired())
{
if (focused.lock() == container)
focused.reset();
}
}

void CompositorState::add(std::shared_ptr<Container> const& container)
{
CompositorState::focus_order.push_back(container);
}

void CompositorState::remove(std::shared_ptr<Container> 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<Container> 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;
}
23 changes: 19 additions & 4 deletions src/compositor_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef MIRACLE_WM_COMPOSITOR_STATE_H
#define MIRACLE_WM_COMPOSITOR_STATE_H

#include "container.h"

#include <algorithm>
#include <memory>
#include <mir/geometry/point.h>
#include <vector>

namespace miracle
{
class Container;
class Output;

enum class WindowManagerMode
Expand All @@ -40,14 +43,26 @@ enum class WindowManagerMode
selecting
};

struct CompositorState
class CompositorState
{
public:
WindowManagerMode mode = WindowManagerMode::normal;
std::shared_ptr<Output> active_output;
std::shared_ptr<Container> active;
std::shared_ptr<Output> active_output = nullptr;
mir::geometry::Point cursor_position;
uint32_t modifiers = 0;
bool has_clicked_floating_window = false;

[[nodiscard]] std::shared_ptr<Container> active() const;
void focus(std::shared_ptr<Container> const& container);
void unfocus(std::shared_ptr<Container> const& container);
void add(std::shared_ptr<Container> const& container);
void remove(std::shared_ptr<Container> const& container);
[[nodiscard]] std::shared_ptr<Container> get_first_with_type(ContainerType type) const;
[[nodiscard]] std::vector<std::weak_ptr<Container>> const& containers() const { return focus_order; }

private:
std::weak_ptr<Container> focused;
std::vector<std::weak_ptr<Container>> focus_order;
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/container_group_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/floating_window_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 13 additions & 3 deletions src/i3_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/

#define MIR_LOG_COMPONENT "miracle::i3_command"

#include "i3_command.h"
#include "jpcre2.h"
#include "string_extensions.h"
#include "window_controller.h"
#include "window_helpers.h"

#include <cstring>
#include <ranges>
#define MIR_LOG_COMPONENT "miracle::i3_command"
#include <mir/log.h>
#include <miral/application.h>
#include <miral/application_info.h>
#include <ranges>

using namespace miracle;

Expand Down Expand Up @@ -171,14 +174,21 @@ 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);
jp::Regex re(criteria.regex.value());
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;
}
Expand Down
1 change: 1 addition & 0 deletions src/i3_command.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum class I3CommandType
input
};

// https://i3wm.org/docs/userguide.html#command_criteria
enum class I3ScopeType
{
none,
Expand Down
78 changes: 47 additions & 31 deletions src/i3_command_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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;

Expand All @@ -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<std::string>(command.arguments.begin() + 1, command.arguments.end());
policy.try_select_output(names);
}
}
}

namespace
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down
5 changes: 3 additions & 2 deletions src/i3_command_executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef MIRACLEWM_I_3_COMMAND_EXECUTOR_H
#define MIRACLEWM_I_3_COMMAND_EXECUTOR_H

#include "compositor_state.h"
#include "i3_command.h"
#include <mir/glib_main_loop.h>

Expand All @@ -37,15 +38,15 @@ class I3CommandExecutor
I3CommandExecutor(
Policy&,
WorkspaceManager&,
miral::WindowManagerTools const&,
CompositorState const&,
AutoRestartingLauncher&,
WindowController&);
void process(I3ScopedCommandList const&);

private:
Policy& policy;
WorkspaceManager& workspace_manager;
miral::WindowManagerTools tools;
CompositorState const& state;
AutoRestartingLauncher& launcher;
WindowController& window_controller;

Expand Down
4 changes: 2 additions & 2 deletions src/leaf_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion src/parent_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Expand Down
Loading

0 comments on commit 0297e0a

Please sign in to comment.