refactor: establish the OutputContent as a source of truth for an Output's layout in preparation for floating windows (#44)

* Establish the concept of `WindowMetadata` which every window must have. This data is used to associate a `Window` to its `OutputContent` and `Tree`
* The `Policy` now defers to the `OutputContent` for decisions on how a window should behave. The `Policy` is more of a metadata-resolver than anything, as it leaves the heavy-lifting to the `OutputContent`
* Prepares for the integration of a `FloatingWindowManager` that the `OutputContent` will be able to defer to when a window is of type `WindowType::floating`
This commit is contained in:
Matthew Kosarek 2024-03-02 07:47:11 -05:00 committed by GitHub
parent 9dca0468be
commit 0601a51be3
24 changed files with 841 additions and 539 deletions

View File

@ -26,19 +26,20 @@ include(GNUInstallDirs)
add_executable(miracle-wm
src/main.cpp
src/tiling_window_management_policy.cpp
src/policy.cpp
src/tree.cpp
src/node.cpp
src/window_helpers.h
src/window_helpers.cpp
src/miracle_config.cpp
src/miracle_config.h
src/screen.cpp
src/output_content.cpp
src/workspace_manager.cpp
src/ipc.cpp
src/auto_restarting_launcher.cpp
src/workspace_observer.cpp
src/window_metadata.cpp
src/workspace_content.cpp
)
target_include_directories(miracle-wm PUBLIC SYSTEM ${MIRAL_INCLUDE_DIRS})

View File

@ -1,7 +1,7 @@
#define MIR_LOG_COMPONENT "miracle_ipc"
#include "ipc.h"
#include "screen.h"
#include "output_content.h"
#include <fcntl.h>
#include <sys/socket.h>
@ -47,9 +47,9 @@ struct sockaddr_un *ipc_user_sockaddr() {
return ipc_sockaddr;
}
json workspace_to_json(std::shared_ptr<Screen> const& screen, int key)
json workspace_to_json(std::shared_ptr<OutputContent> const& screen, int key)
{
bool is_focused = screen->get_active_workspace() == key;
bool is_focused = screen->get_active_workspace_num() == key;
auto area = screen->get_area();
return {
@ -199,7 +199,7 @@ Ipc::Ipc(miral::MirRunner& runner, miracle::WorkspaceManager& workspace_manager)
});
}
void Ipc::on_created(std::shared_ptr<Screen> const& info, int key)
void Ipc::on_created(std::shared_ptr<OutputContent> const& info, int key)
{
json j = {
{"change", "init"},
@ -218,7 +218,7 @@ void Ipc::on_created(std::shared_ptr<Screen> const& info, int key)
}
}
void Ipc::on_removed(std::shared_ptr<Screen> const& screen, int key)
void Ipc::on_removed(std::shared_ptr<OutputContent> const& screen, int key)
{
json j = {
{"change", "empty"},
@ -237,9 +237,9 @@ void Ipc::on_removed(std::shared_ptr<Screen> const& screen, int key)
}
void Ipc::on_focused(
std::shared_ptr<Screen> const& previous,
std::shared_ptr<OutputContent> const& previous,
int previous_key,
std::shared_ptr<Screen> const& current,
std::shared_ptr<OutputContent> const& current,
int current_key)
{
json j = {

View File

@ -57,9 +57,9 @@ class Ipc : public WorkspaceObserver
public:
Ipc(miral::MirRunner& runner, WorkspaceManager&);
void on_created(std::shared_ptr<Screen> const& info, int key) override;
void on_removed(std::shared_ptr<Screen> const& info, int key) override;
void on_focused(std::shared_ptr<Screen> const& previous, int, std::shared_ptr<Screen> const& current, int) override;
void on_created(std::shared_ptr<OutputContent> const& info, int key) override;
void on_removed(std::shared_ptr<OutputContent> const& info, int key) override;
void on_focused(std::shared_ptr<OutputContent> const& previous, int, std::shared_ptr<OutputContent> const& current, int) override;
void disconnect_all();
private:
struct IpcClient

View File

@ -1,6 +1,6 @@
#define MIR_LOG_COMPONENT "miracle-main"
#include "tiling_window_management_policy.h"
#include "policy.h"
#include "miracle_config.h"
#include "auto_restarting_launcher.h"
@ -24,14 +24,13 @@ int main(int argc, char const* argv[])
std::function<void()> shutdown_hook{[]{}};
runner.add_stop_callback([&] { shutdown_hook(); });
InternalClientLauncher internal_client_launcher;
ExternalClientLauncher external_client_launcher;
miracle::AutoRestartingLauncher auto_restarting_launcher(runner, external_client_launcher);
auto config = std::make_shared<miracle::MiracleConfig>(runner);
WindowManagerOptions window_managers
{
add_window_manager_policy<miracle::TilingWindowManagementPolicy>(
"tiling", external_client_launcher, internal_client_launcher, runner, config)
add_window_manager_policy<miracle::Policy>(
"tiling", external_client_launcher, runner, config)
};
Keymap config_keymap;
@ -65,7 +64,6 @@ int main(int argc, char const* argv[])
X11Support{}.default_to_enabled(),
config_keymap,
external_client_launcher,
internal_client_launcher,
display_configuration_options,
AddInitCallback(run_startup_apps)
});

View File

@ -1,3 +1,5 @@
#include "window_metadata.h"
#include <memory>
#define MIR_LOG_COMPONENT "node"
#include "node.h"
@ -13,7 +15,7 @@ Node::Node(
miral::WindowManagerTools const& tools_,
geom::Rectangle const& area,
std::shared_ptr<MiracleConfig> const& config,
Tree const* tree)
Tree* tree)
: tools{tools_},
state{NodeState::lane},
logical_area{area},
@ -26,20 +28,17 @@ Node::Node(
miral::WindowManagerTools const& tools_,
geom::Rectangle const& area,
std::shared_ptr<Node> parent,
std::shared_ptr<WindowMetadata> const& metadata,
miral::Window const& window,
std::shared_ptr<MiracleConfig> const& config,
Tree const* tree)
Tree* tree)
: tools{tools_},
parent{std::move(parent)},
metadata{metadata},
window{window},
state{NodeState::window},
logical_area{area},
config{config},
tree{tree}
{
miral::WindowSpecification spec;
spec.userdata() = metadata;
tools.modify_window(metadata->get_window(), spec);
}
geom::Rectangle Node::get_logical_area_internal(geom::Rectangle const& rectangle)
@ -217,26 +216,25 @@ geom::Rectangle Node::create_new_node_position(int index)
}
}
void Node::add_window(miral::Window& new_window)
std::shared_ptr<Node> Node::add_window(miral::Window& new_window)
{
if (pending_index < 0)
{
mir::fatal_error("Unable to add the window to the scene. Was create_new_node_position called?");
return;
return nullptr;
}
auto node_metadata = std::make_shared<WindowMetadata>(WindowType::tiled, new_window);
auto node = std::make_shared<Node>(
tools,
pending_logical_rect,
shared_from_this(),
node_metadata,
new_window,
config,
tree);
node_metadata->associate_to_node(node);
sub_nodes.insert(sub_nodes.begin() + pending_index, node);
pending_index = -1;
return node;
}
void Node::_refit_node_to_area()
@ -286,7 +284,7 @@ void Node::set_logical_area(geom::Rectangle const& target_rect)
{
if (is_window())
{
auto& info = tools.info_for(metadata->get_window());
auto& info = tools.info_for(get_window());
if (!window_helpers::is_window_fullscreen(info.state()))
{
_set_window_rectangle(target_rect);
@ -389,12 +387,13 @@ std::shared_ptr<Node> Node::to_lane()
tools,
logical_area,
shared_from_this(),
metadata,
get_window(),
config,
tree);
metadata->associate_to_node(window_node);
sub_nodes.push_back(window_node);
metadata = nullptr;
auto metadata = window_helpers::get_metadata(window, tools);
metadata->associate_to_node(window_node);
window = miral::Window();
return window_node;
}
@ -584,7 +583,6 @@ int Node::get_min_height() const
void Node::_set_window_rectangle(geom::Rectangle area)
{
auto visible_rect = _get_visible_from_logical(area, config);
auto window = metadata->get_window();
window.move_to(visible_rect.top_left);
window.resize(visible_rect.size);
auto& window_info = tools.info_for(window);
@ -599,7 +597,7 @@ void Node::constrain()
{
if (is_window())
{
auto& info = tools.info_for(metadata->get_window());
auto& info = tools.info_for(window);
if (window_helpers::is_window_fullscreen(info.state()))
info.clip_area(mir::optional_value<geom::Rectangle>());
else

View File

@ -35,14 +35,14 @@ public:
Node(miral::WindowManagerTools const& tools,
geom::Rectangle const& area,
std::shared_ptr<MiracleConfig> const& config,
Tree const* tree);
Tree* tree);
Node(miral::WindowManagerTools const& tools,
geom::Rectangle const& area,
std::shared_ptr<Node> parent,
std::shared_ptr<WindowMetadata> const& metadata,
miral::Window const& window,
std::shared_ptr<MiracleConfig> const& config,
Tree const* tree);
Tree* tree);
/// Area taken up by the node including gaps.
geom::Rectangle get_logical_area();
@ -54,7 +54,7 @@ public:
geom::Rectangle create_new_node_position(int index = -1);
/// Append the node to the lane
void add_window(miral::Window&);
std::shared_ptr<Node> add_window(miral::Window&);
/// Updates the node's logical area (including gaps)
void set_logical_area(geom::Rectangle const& target_rect);
@ -97,16 +97,16 @@ public:
bool is_window() const { return state == NodeState::window; }
bool is_lane() const { return state == NodeState::lane; }
NodeLayoutDirection get_direction() const { return direction; }
miral::Window& get_window() { return metadata->get_window(); }
miral::Window& get_window() { return window; }
std::shared_ptr<Node> get_parent() const { return parent; }
std::vector<std::shared_ptr<Node>> const& get_sub_nodes() const { return sub_nodes; }
Tree const* get_tree() { return tree; }
Tree* get_tree() { return tree; }
private:
std::shared_ptr<Node> parent;
miral::WindowManagerTools tools;
Tree const* tree;
std::shared_ptr<WindowMetadata> metadata;
Tree* tree;
miral::Window window;
std::vector<std::shared_ptr<Node>> sub_nodes;
std::vector<std::shared_ptr<Node>> hidden_nodes;
NodeState state;

374
src/output_content.cpp Normal file
View File

@ -0,0 +1,374 @@
#define MIR_LOG_COMPONENT "output_content"
#include "output_content.h"
#include "window_helpers.h"
#include "workspace_manager.h"
#include <miral/window_info.h>
#include <mir/log.h>
using namespace miracle;
OutputContent::OutputContent(
miral::Output const& output,
WorkspaceManager& workspace_manager,
geom::Rectangle const& area,
miral::WindowManagerTools const& tools,
std::shared_ptr<MiracleConfig> const& config)
: output{output},
workspace_manager{workspace_manager},
area{area},
tools{tools},
config{config}
{
}
std::shared_ptr<Tree> OutputContent::get_active_tree() const
{
return get_active_workspace()->get_tree();
}
std::shared_ptr<WorkspaceContent> OutputContent::get_active_workspace() const
{
for (auto& info : workspaces)
{
if (info->get_workspace() == active_workspace)
return info;
}
throw std::runtime_error("get_active_workspace: unable to find the active workspace. We shouldn't be here!");
return nullptr;
}
WindowType OutputContent::allocate_position(miral::WindowSpecification& requested_specification)
{
if (!window_helpers::is_tileable(requested_specification))
return WindowType::other;
requested_specification = get_active_tree()->allocate_position(requested_specification);
return WindowType::tiled;
}
void OutputContent::advise_new_window(miral::WindowInfo const& window_info, WindowType type)
{
std::shared_ptr<WindowMetadata> metadata = nullptr;
switch (type)
{
case WindowType::tiled:
{
auto node = get_active_tree()->advise_new_window(window_info);
metadata = std::make_shared<WindowMetadata>(WindowType::tiled, window_info.window(), this);
metadata->associate_to_node(node);
break;
}
case WindowType::other:
if (window_info.state() == MirWindowState::mir_window_state_attached)
{
tools.select_active_window(window_info.window());
}
metadata = std::make_shared<WindowMetadata>(WindowType::other, window_info.window(), this);
break;
default:
mir::log_error("Unsupported window type: %d", (int)type);
break;
}
if (metadata)
{
miral::WindowSpecification spec;
spec.userdata() = metadata;
tools.modify_window(window_info.window(), spec);
}
else
{
mir::log_error("Window failed to set metadata");
}
}
void OutputContent::handle_window_ready(miral::WindowInfo &window_info, std::shared_ptr<miracle::WindowMetadata> const& metadata)
{
switch (metadata->get_type())
{
case WindowType::tiled:
{
metadata->get_tiling_node()->get_tree()->handle_window_ready(window_info);
break;
}
default:
mir::log_error("Unsupported window type: %d", (int)metadata->get_type());
return;
}
}
void OutputContent::advise_focus_gained(const std::shared_ptr<miracle::WindowMetadata> &metadata)
{
switch (metadata->get_type())
{
case WindowType::tiled:
{
metadata->get_tiling_node()->get_tree()->advise_focus_gained(metadata->get_window());
tools.raise_tree(metadata->get_window());
break;
}
default:
mir::log_error("Unsupported window type: %d", (int)metadata->get_type());
return;
}
}
void OutputContent::advise_focus_lost(const std::shared_ptr<miracle::WindowMetadata> &metadata)
{
switch (metadata->get_type())
{
case WindowType::tiled:
{
metadata->get_tiling_node()->get_tree()->advise_focus_lost(metadata->get_window());
tools.raise_tree(metadata->get_window());
break;
}
default:
mir::log_error("Unsupported window type: %d", (int)metadata->get_type());
return;
}
}
void OutputContent::advise_delete_window(const std::shared_ptr<miracle::WindowMetadata> &metadata)
{
switch (metadata->get_type())
{
case WindowType::tiled:
{
metadata->get_tiling_node()->get_tree()->advise_delete_window(metadata->get_window());
break;
}
default:
mir::log_error("Unsupported window type: %d", (int)metadata->get_type());
return;
}
}
void OutputContent::advise_state_change(const std::shared_ptr<miracle::WindowMetadata> &metadata, MirWindowState state)
{
switch (metadata->get_type())
{
case WindowType::tiled:
{
if (get_active_tree().get() != metadata->get_tiling_node()->get_tree())
break;
metadata->get_tiling_node()->get_tree()->advise_state_change(metadata->get_window(), state);
break;
}
default:
mir::log_error("Unsupported window type: %d", (int)metadata->get_type());
return;
}
}
void OutputContent::handle_modify_window(const std::shared_ptr<miracle::WindowMetadata> &metadata,
const miral::WindowSpecification &modifications)
{
switch (metadata->get_type())
{
case WindowType::tiled:
{
if (get_active_tree().get() != metadata->get_tiling_node()->get_tree())
break;
if (modifications.state().is_set())
{
if (modifications.state().value() == mir_window_state_fullscreen || modifications.state().value() == mir_window_state_maximized)
metadata->get_tiling_node()->get_tree()->advise_fullscreen_window(metadata->get_window());
else if (modifications.state().value() == mir_window_state_restored)
metadata->get_tiling_node()->get_tree()->advise_restored_window(metadata->get_window());
}
metadata->get_tiling_node()->get_tree()->constrain(metadata->get_window());
tools.modify_window(metadata->get_window(), modifications);
break;
}
default:
mir::log_error("Unsupported window type: %d", (int)metadata->get_type());
return;
}
}
mir::geometry::Rectangle
OutputContent::confirm_placement_on_display(
const std::shared_ptr<miracle::WindowMetadata> &metadata,
MirWindowState new_state,
const mir::geometry::Rectangle &new_placement)
{
mir::geometry::Rectangle modified_placement = new_placement;
switch (metadata->get_type())
{
case WindowType::tiled:
{
metadata->get_tiling_node()->get_tree()->confirm_placement_on_display(
metadata->get_window(), new_state, modified_placement);
break;
}
default:
mir::log_error("Unsupported window type: %d", (int)metadata->get_type());
break;
}
return new_placement;
}
void OutputContent::select_window_from_point(int x, int y)
{
get_active_tree()->select_window_from_point(x, y);
}
void OutputContent::advise_new_workspace(int workspace)
{
workspaces.push_back(
std::make_shared<WorkspaceContent>(this, tools, workspace, config));
}
void OutputContent::advise_workspace_deleted(int workspace)
{
for (auto it = workspaces.begin(); it != workspaces.end(); it++)
{
if (it->get()->get_workspace() == workspace)
{
workspaces.erase(it);
return;
}
}
}
bool OutputContent::advise_workspace_active(int key)
{
for (auto& workspace : workspaces)
{
if (workspace->get_workspace() == key)
{
std::shared_ptr<WorkspaceContent> previous_workspace = nullptr;
for (auto& other : workspaces)
{
if (other->get_workspace() == active_workspace)
{
previous_workspace = other;
other->hide();
break;
}
}
active_workspace = key;
workspace->show();
// Important: Delete the workspace only after we have shown the new one because we may want
// to move a node to the new workspace.
if (previous_workspace != nullptr)
{
auto active_tree = previous_workspace->get_tree();
if (active_tree->is_empty())
workspace_manager.delete_workspace(previous_workspace->get_workspace());
}
return true;
}
}
return false;
}
void OutputContent::advise_application_zone_create(miral::Zone const& application_zone)
{
if (application_zone.extents().contains(area))
{
application_zone_list.push_back(application_zone);
for (auto& workspace : workspaces)
workspace->get_tree()->recalculate_root_node_area();
}
}
void OutputContent::advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original)
{
for (auto& zone : application_zone_list)
if (zone == original)
{
zone = updated;
for (auto& workspace : workspaces)
workspace->get_tree()->recalculate_root_node_area();
break;
}
}
void OutputContent::advise_application_zone_delete(miral::Zone const& application_zone)
{
if (std::remove(application_zone_list.begin(), application_zone_list.end(), application_zone) != application_zone_list.end())
{
for (auto& workspace : workspaces)
workspace->get_tree()->recalculate_root_node_area();
}
}
bool OutputContent::point_is_in_output(int x, int y)
{
return area.contains(geom::Point(x, y));
}
void OutputContent::close_active_window()
{
get_active_tree()->close_active_window();
}
bool OutputContent::resize_active_window(miracle::Direction direction)
{
return get_active_tree()->try_resize_active_window(direction);
}
bool OutputContent::select(miracle::Direction direction)
{
return get_active_tree()->try_select_next(direction);
}
bool OutputContent::move_active_window(miracle::Direction direction)
{
return get_active_tree()->try_move_active_window(direction);
}
void OutputContent::request_vertical()
{
get_active_tree()->request_vertical();
}
void OutputContent::request_horizontal()
{
get_active_tree()->request_horizontal();
}
void OutputContent::toggle_resize_mode()
{
get_active_tree()->toggle_resize_mode();
}
void OutputContent::toggle_fullscreen()
{
get_active_tree()->try_toggle_active_fullscreen();
}
void OutputContent::update_area(geom::Rectangle const& new_area)
{
area = new_area;
for (auto& workspace : workspaces)
{
workspace->get_tree()->set_output_area(area);
}
}
std::vector<miral::Window> OutputContent::collect_all_windows() const
{
std::vector<miral::Window> windows;
for (auto& workspace : get_workspaces())
{
workspace->get_tree()->foreach_node([&](auto node)
{
if (node->is_window())
{
windows.push_back(node->get_window());
}
});
}
return windows;
}

80
src/output_content.h Normal file
View File

@ -0,0 +1,80 @@
#ifndef MIRACLE_SCREEN_H
#define MIRACLE_SCREEN_H
#include "tree.h"
#include "workspace_content.h"
#include <memory>
#include <miral/output.h>
namespace miracle
{
class WorkspaceManager;
class MiracleConfig;
class OutputContent
{
public:
OutputContent(
miral::Output const& output,
WorkspaceManager& workspace_manager,
geom::Rectangle const& area,
miral::WindowManagerTools const& tools,
std::shared_ptr<MiracleConfig> const& options);
~OutputContent() = default;
[[nodiscard]] std::shared_ptr<Tree> get_active_tree() const;
[[nodiscard]] int get_active_workspace_num() const { return active_workspace; }
[[nodiscard]] std::shared_ptr<WorkspaceContent> get_active_workspace() const;
WindowType allocate_position(miral::WindowSpecification& requested_specification);
void advise_new_window(miral::WindowInfo const& window_info, WindowType type);
void handle_window_ready(miral::WindowInfo &window_info, std::shared_ptr<miracle::WindowMetadata> const& metadata);
void advise_focus_gained(std::shared_ptr<miracle::WindowMetadata> const& metadata);
void advise_focus_lost(std::shared_ptr<miracle::WindowMetadata> const& metadata);
void advise_delete_window(std::shared_ptr<miracle::WindowMetadata> const& metadata);
void advise_state_change(std::shared_ptr<miracle::WindowMetadata> const& metadata, MirWindowState state);
void handle_modify_window(std::shared_ptr<miracle::WindowMetadata> const& metadata, const miral::WindowSpecification &modifications);
mir::geometry::Rectangle confirm_placement_on_display(
std::shared_ptr<miracle::WindowMetadata> const& metadata,
MirWindowState new_state,
const mir::geometry::Rectangle &new_placement);
void select_window_from_point(int x, int y);
void advise_new_workspace(int workspace);
void advise_workspace_deleted(int workspace);
bool advise_workspace_active(int workspace);
std::vector<std::shared_ptr<WorkspaceContent>> const& get_workspaces() const { return workspaces; }
void advise_application_zone_create(miral::Zone const& application_zone);
void advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original);
void advise_application_zone_delete(miral::Zone const& application_zone);
bool point_is_in_output(int x, int y);
void close_active_window();
bool resize_active_window(Direction direction);
bool select(Direction direction);
bool move_active_window(Direction direction);
void request_vertical();
void request_horizontal();
void toggle_resize_mode();
void toggle_fullscreen();
void update_area(geom::Rectangle const& area);
std::vector<miral::Window> collect_all_windows() const;
geom::Rectangle const& get_area() { return area; }
std::vector<miral::Zone> const& get_app_zones() { return application_zone_list; }
miral::Output const& get_output() { return output; }
[[nodiscard]] bool is_active() const { return is_active_; }
void set_is_active(bool new_is_active) { is_active_ = new_is_active; }
private:
miral::Output output;
WorkspaceManager& workspace_manager;
miral::WindowManagerTools tools;
geom::Rectangle area;
std::shared_ptr<MiracleConfig> config;
int active_workspace = -1;
std::vector<std::shared_ptr<WorkspaceContent>> workspaces;
std::vector<miral::Zone> application_zone_list;
bool is_active_ = false;
};
}
#endif

View File

@ -1,6 +1,8 @@
#include "miral/window_specification.h"
#include "window_metadata.h"
#define MIR_LOG_COMPONENT "miracle"
#include "tiling_window_management_policy.h"
#include "policy.h"
#include "window_helpers.h"
#include "miracle_config.h"
#include "workspace_manager.h"
@ -27,15 +29,13 @@ const int MODIFIER_MASK =
mir_input_event_modifier_meta;
}
TilingWindowManagementPolicy::TilingWindowManagementPolicy(
Policy::Policy(
miral::WindowManagerTools const& tools,
miral::ExternalClientLauncher const& external_client_launcher,
miral::InternalClientLauncher const& internal_client_launcher,
miral::MirRunner& runner,
std::shared_ptr<MiracleConfig> const& config)
: window_manager_tools{tools},
external_client_launcher{external_client_launcher},
internal_client_launcher{internal_client_launcher},
runner{runner},
config{config},
workspace_manager{WorkspaceManager(
@ -51,12 +51,12 @@ TilingWindowManagementPolicy::TilingWindowManagementPolicy(
}, 1);
}
TilingWindowManagementPolicy::~TilingWindowManagementPolicy()
Policy::~Policy()
{
workspace_observer_registrar.unregister_interest(*ipc);
}
bool TilingWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
bool Policy::handle_keyboard_event(MirKeyboardEvent const* event)
{
auto const action = miral::toolkit::mir_keyboard_event_action(event);
auto const scan_code = miral::toolkit::mir_keyboard_event_scan_code(event);
@ -83,58 +83,58 @@ bool TilingWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const*
return true;
}
case RequestVertical:
if(active_output) active_output->get_active_tree().request_vertical();
if(active_output) active_output->request_vertical();
return true;
case RequestHorizontal:
if(active_output) active_output->get_active_tree().request_horizontal();
if(active_output) active_output->request_horizontal();
return true;
case ToggleResize:
if(active_output) active_output->get_active_tree().toggle_resize_mode();
if(active_output) active_output->toggle_resize_mode();
return true;
case MoveUp:
if (active_output && active_output->get_active_tree().try_move_active_window(Direction::up))
if (active_output && active_output->move_active_window(Direction::up))
return true;
return false;
case MoveDown:
if (active_output && active_output->get_active_tree().try_move_active_window(Direction::down))
if (active_output && active_output->move_active_window(Direction::down))
return true;
return false;
case MoveLeft:
if (active_output && active_output->get_active_tree().try_move_active_window(Direction::left))
if (active_output && active_output->move_active_window(Direction::left))
return true;
return false;
case MoveRight:
if (active_output && active_output->get_active_tree().try_move_active_window(Direction::right))
if (active_output && active_output->move_active_window(Direction::right))
return true;
return false;
case SelectUp:
if (active_output && (active_output->get_active_tree().try_resize_active_window(Direction::up)
|| active_output->get_active_tree().try_select_next(Direction::up)))
if (active_output && (active_output->resize_active_window(Direction::up)
|| active_output->select(Direction::up)))
return true;
return false;
case SelectDown:
if (active_output && (active_output->get_active_tree().try_resize_active_window(Direction::down)
|| active_output->get_active_tree().try_select_next(Direction::down)))
if (active_output && (active_output->resize_active_window(Direction::down)
|| active_output->select(Direction::down)))
return true;
return false;
case SelectLeft:
if (active_output && (active_output->get_active_tree().try_resize_active_window(Direction::left)
|| active_output->get_active_tree().try_select_next(Direction::left)))
if (active_output && (active_output->resize_active_window(Direction::left)
|| active_output->select(Direction::left)))
return true;
return false;
case SelectRight:
if (active_output && (active_output->get_active_tree().try_resize_active_window(Direction::right)
|| active_output->get_active_tree().try_select_next(Direction::right)))
if (active_output && (active_output->resize_active_window(Direction::right)
|| active_output->select(Direction::right)))
return true;
return false;
case QuitActiveWindow:
if (active_output) active_output->get_active_tree().close_active_window();
if (active_output) active_output->close_active_window();
return true;
case QuitCompositor:
runner.stop();
return true;
case Fullscreen:
if (active_output) active_output->get_active_tree().try_toggle_active_fullscreen();
if (active_output) active_output->toggle_fullscreen();
return true;
case SelectWorkspace1:
if (active_output) workspace_manager.request_workspace(active_output, 1);
@ -203,7 +203,7 @@ bool TilingWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const*
return false;
}
bool TilingWindowManagementPolicy::handle_pointer_event(MirPointerEvent const* event)
bool Policy::handle_pointer_event(MirPointerEvent const* event)
{
auto x = miral::toolkit::mir_pointer_event_axis_value(event, MirPointerAxis::mir_pointer_axis_x);
auto y = miral::toolkit::mir_pointer_event_axis_value(event, MirPointerAxis::mir_pointer_axis_y);
@ -217,12 +217,12 @@ bool TilingWindowManagementPolicy::handle_pointer_event(MirPointerEvent const* e
if (active_output) active_output->set_is_active(false);
active_output = output;
active_output->set_is_active(true);
workspace_manager.request_focus(output->get_active_workspace());
workspace_manager.request_focus(output->get_active_workspace_num());
}
if (output->get_active_workspace() >= 0)
if (output->get_active_workspace_num() >= 0)
{
active_output->get_active_tree().select_window_from_point(static_cast<int>(x), static_cast<int>(y));
active_output->select_window_from_point(static_cast<int>(x), static_cast<int>(y));
}
break;
}
@ -231,7 +231,7 @@ bool TilingWindowManagementPolicy::handle_pointer_event(MirPointerEvent const* e
return false;
}
auto TilingWindowManagementPolicy::place_new_window(
auto Policy::place_new_window(
const miral::ApplicationInfo &app_info,
const miral::WindowSpecification &requested_specification) -> miral::WindowSpecification
{
@ -241,19 +241,22 @@ auto TilingWindowManagementPolicy::place_new_window(
return requested_specification;
}
auto new_spec = requested_specification;
pending_output = active_output;
return active_output->get_active_tree().allocate_position(requested_specification);
pending_type = active_output->allocate_position(new_spec);
return new_spec;
}
void TilingWindowManagementPolicy::_add_to_output_immediately(miral::Window& window, std::shared_ptr<Screen>& output)
void Policy::_add_to_output_immediately(miral::Window& window, std::shared_ptr<OutputContent>& output)
{
miral::WindowSpecification spec;
spec = output->get_active_tree().allocate_position(spec);
pending_output = output;
pending_type = output->allocate_position(spec);
window_manager_tools.modify_window(window, spec);
output->get_active_tree().advise_new_window(window_manager_tools.info_for(window));
handle_window_ready(window_manager_tools.info_for(window));
}
void TilingWindowManagementPolicy::advise_new_window(miral::WindowInfo const& window_info)
void Policy::advise_new_window(miral::WindowInfo const& window_info)
{
auto shared_output = pending_output.lock();
if (!shared_output)
@ -275,53 +278,74 @@ void TilingWindowManagementPolicy::advise_new_window(miral::WindowInfo const& wi
return;
}
shared_output->get_active_tree().advise_new_window(window_info);
shared_output->advise_new_window(window_info, pending_type);
pending_type = WindowType::none;
pending_output.reset();
}
void TilingWindowManagementPolicy::handle_window_ready(miral::WindowInfo &window_info)
void Policy::handle_window_ready(miral::WindowInfo &window_info)
{
for (auto const& output : output_list)
{
if (output->get_active_tree().handle_window_ready(window_info))
break;
}
auto metadata = window_helpers::get_metadata(window_info);
if (!metadata)
return;
metadata->get_output()->handle_window_ready(window_info, metadata);
}
void TilingWindowManagementPolicy::advise_focus_gained(const miral::WindowInfo &window_info)
void Policy::advise_focus_gained(const miral::WindowInfo &window_info)
{
for (auto const& output : output_list)
output->get_active_tree().advise_focus_gained(window_info.window());
window_manager_tools.raise_tree(window_info.window());
auto metadata = window_helpers::get_metadata(window_info);
if (!metadata)
{
mir::fatal_error("advise_focus_gained: metadata is not provided");
return;
}
void TilingWindowManagementPolicy::advise_focus_lost(const miral::WindowInfo &window_info)
{
for (auto const& output : output_list)
output->get_active_tree().advise_focus_lost(window_info.window());
metadata->get_output()->advise_focus_gained(metadata);
}
void TilingWindowManagementPolicy::advise_delete_window(const miral::WindowInfo &window_info)
void Policy::advise_focus_lost(const miral::WindowInfo &window_info)
{
auto metadata = window_helpers::get_metadata(window_info);
if (!metadata)
{
mir::fatal_error("advise_focus_lost: metadata is not provided");
return;
}
metadata->get_output()->advise_focus_lost(metadata);
}
void Policy::advise_delete_window(const miral::WindowInfo &window_info)
{
for (auto it = orphaned_window_list.begin(); it != orphaned_window_list.end();)
{
if (*it == window_info.window())
it = orphaned_window_list.erase(it);
{
orphaned_window_list.erase(it);
return;
}
else
it++;
}
for (auto const& output : output_list)
output->get_active_tree().advise_delete_window(window_info.window());
auto metadata = window_helpers::get_metadata(window_info);
if (!metadata)
{
mir::fatal_error("advise_delete_window: metadata is not provided");
return;
}
void TilingWindowManagementPolicy::advise_move_to(miral::WindowInfo const& window_info, geom::Point top_left)
metadata->get_output()->advise_delete_window(metadata);
}
void Policy::advise_move_to(miral::WindowInfo const& window_info, geom::Point top_left)
{
}
void TilingWindowManagementPolicy::advise_output_create(miral::Output const& output)
void Policy::advise_output_create(miral::Output const& output)
{
auto new_tree = std::make_shared<Screen>(
auto new_tree = std::make_shared<OutputContent>(
output, workspace_manager, output.extents(), window_manager_tools, config);
workspace_manager.request_first_available_workspace(new_tree);
output_list.push_back(new_tree);
@ -339,54 +363,45 @@ void TilingWindowManagementPolicy::advise_output_create(miral::Output const& out
}
}
void TilingWindowManagementPolicy::advise_output_update(miral::Output const& updated, miral::Output const& original)
void Policy::advise_output_update(miral::Output const& updated, miral::Output const& original)
{
for (auto& output : output_list)
{
if (output->get_output().is_same_output(original))
{
for (auto& workspace : output->get_workspaces())
{
workspace.tree->set_output_area(updated.extents());
}
output->update_area(updated.extents());
break;
}
}
}
void TilingWindowManagementPolicy::advise_output_delete(miral::Output const& output)
void Policy::advise_output_delete(miral::Output const& output)
{
for (auto it = output_list.begin(); it != output_list.end();)
{
auto other_output = *it;
if (other_output->get_output().is_same_output(output))
{
it = output_list.erase(it);
if (other_output == active_output)
{
output_list.erase(it);
if (output_list.empty())
{
// All nodes should become orphaned
for (auto& workspace : other_output->get_workspaces())
for (auto& window : other_output->collect_all_windows())
{
workspace.tree->foreach_node([&](auto node)
{
if (node->is_window())
{
orphaned_window_list.push_back(node->get_window());
}
});
orphaned_window_list.push_back(window);
WindowSpecification spec;
spec.userdata() = nullptr;
window_manager_tools.modify_window(window, spec);
}
active_output = nullptr;
}
else
{
active_output = output_list[0];
for (auto& workspace : other_output->get_workspaces())
active_output = output_list.front();
for (auto& window : other_output->collect_all_windows())
{
active_output->get_active_tree().add_tree(workspace.tree);
}
_add_to_output_immediately(window, active_output);
}
}
break;
@ -394,107 +409,66 @@ void TilingWindowManagementPolicy::advise_output_delete(miral::Output const& out
}
}
void TilingWindowManagementPolicy::advise_state_change(miral::WindowInfo const& window_info, MirWindowState state)
void Policy::advise_state_change(miral::WindowInfo const& window_info, MirWindowState state)
{
for (auto const& output : output_list)
auto metadata = window_helpers::get_metadata(window_info);
if (!metadata)
{
if (output->get_active_tree().advise_state_change(window_info, state))
{
break;
}
}
mir::fatal_error("advise_state_changed: metadata is not provided");
return;
}
void TilingWindowManagementPolicy::handle_modify_window(
metadata->get_output()->advise_state_change(metadata, state);
}
void Policy::handle_modify_window(
miral::WindowInfo &window_info,
const miral::WindowSpecification &modifications)
{
if (modifications.state().is_set())
auto metadata = window_helpers::get_metadata(window_info);
if (!metadata)
{
if (modifications.state().value() == mir_window_state_fullscreen || modifications.state().value() == mir_window_state_maximized)
{
for (auto const& output : output_list)
{
bool found = false;
for (auto& workspace : output->get_workspaces())
{
if (workspace.tree->advise_fullscreen_window(window_info))
{
found = true;
break;
}
mir::fatal_error("handle_modify_window: metadata is not provided");
return;
}
if (found) break;
}
}
else if (modifications.state().value() == mir_window_state_restored)
{
for (auto const& output : output_list)
{
bool found = false;
for (auto& workspace : output->get_workspaces())
{
if (workspace.tree->advise_restored_window(window_info))
{
found = true;
break;
}
metadata->get_output()->handle_modify_window(metadata, modifications);
}
if (found) break;
}
}
}
for (auto const& output :output_list)
{
bool found = false;
for (auto& workspace : output->get_workspaces())
{
if (workspace.tree->constrain(window_info))
{
found = true;
window_manager_tools.modify_window(window_info.window(), modifications);
break;
}
}
if (found) break;
}
}
void TilingWindowManagementPolicy::handle_raise_window(miral::WindowInfo &window_info)
void Policy::handle_raise_window(miral::WindowInfo &window_info)
{
window_manager_tools.select_active_window(window_info.window());
}
mir::geometry::Rectangle
TilingWindowManagementPolicy::confirm_placement_on_display(
Policy::confirm_placement_on_display(
const miral::WindowInfo &window_info,
MirWindowState new_state,
const mir::geometry::Rectangle &new_placement)
{
mir::geometry::Rectangle modified_placement = new_placement;
for (auto const& output : output_list)
auto metadata = window_helpers::get_metadata(window_info);
if (!metadata)
{
if (output->get_active_tree().confirm_placement_on_display(window_info, new_state, modified_placement))
break;
mir::log_error("confirm_placement_on_display: window lacks metadata");
return new_placement;
}
mir::geometry::Rectangle modified_placement = metadata->get_output()->confirm_placement_on_display(
metadata, new_state, new_placement);
return modified_placement;
}
bool TilingWindowManagementPolicy::handle_touch_event(const MirTouchEvent *event)
bool Policy::handle_touch_event(const MirTouchEvent *event)
{
return false;
}
void TilingWindowManagementPolicy::handle_request_move(miral::WindowInfo &window_info, const MirInputEvent *input_event)
void Policy::handle_request_move(miral::WindowInfo &window_info, const MirInputEvent *input_event)
{
}
void TilingWindowManagementPolicy::handle_request_resize(
void Policy::handle_request_resize(
miral::WindowInfo &window_info,
const MirInputEvent *input_event,
MirResizeEdge edge)
@ -502,14 +476,14 @@ void TilingWindowManagementPolicy::handle_request_resize(
}
mir::geometry::Rectangle TilingWindowManagementPolicy::confirm_inherited_move(
mir::geometry::Rectangle Policy::confirm_inherited_move(
const miral::WindowInfo &window_info,
mir::geometry::Displacement movement)
{
return { window_info.window().top_left() + movement, window_info.window().size() };
}
void TilingWindowManagementPolicy::advise_application_zone_create(miral::Zone const& application_zone)
void Policy::advise_application_zone_create(miral::Zone const& application_zone)
{
for (auto const& output : output_list)
{
@ -517,7 +491,7 @@ void TilingWindowManagementPolicy::advise_application_zone_create(miral::Zone co
}
}
void TilingWindowManagementPolicy::advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original)
void Policy::advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original)
{
for (auto const& output : output_list)
{
@ -525,7 +499,7 @@ void TilingWindowManagementPolicy::advise_application_zone_update(miral::Zone co
}
}
void TilingWindowManagementPolicy::advise_application_zone_delete(miral::Zone const& application_zone)
void Policy::advise_application_zone_delete(miral::Zone const& application_zone)
{
for (auto const& output : output_list)
{

View File

@ -1,7 +1,7 @@
#ifndef MIRACLE_TILING_WINDOW_MANAGEMENT_POLICY_H
#define MIRACLE_TILING_WINDOW_MANAGEMENT_POLICY_H
#ifndef MIRACLE_POLICY_H
#define MIRACLE_POLICY_H
#include "screen.h"
#include "output_content.h"
#include "miracle_config.h"
#include "workspace_manager.h"
#include "ipc.h"
@ -22,16 +22,15 @@ class MirRunner;
namespace miracle
{
class TilingWindowManagementPolicy : public miral::WindowManagementPolicy
class Policy : public miral::WindowManagementPolicy
{
public:
TilingWindowManagementPolicy(
Policy(
miral::WindowManagerTools const&,
miral::ExternalClientLauncher const&,
miral::InternalClientLauncher const&,
miral::MirRunner&,
std::shared_ptr<MiracleConfig> const&);
~TilingWindowManagementPolicy() override;
~Policy() override;
bool handle_keyboard_event(MirKeyboardEvent const* event) override;
bool handle_pointer_event(MirPointerEvent const* event) override;
@ -75,24 +74,24 @@ public:
void advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original) override;
void advise_application_zone_delete(miral::Zone const& application_zone) override;
std::shared_ptr<Screen> const& get_active_output() { return active_output; }
std::shared_ptr<OutputContent> const& get_active_output() { return active_output; }
private:
std::shared_ptr<Screen> active_output;
std::vector<std::shared_ptr<Screen>> output_list;
std::weak_ptr<Screen> pending_output;
std::shared_ptr<OutputContent> active_output;
std::vector<std::shared_ptr<OutputContent>> output_list;
std::weak_ptr<OutputContent> pending_output;
WindowType pending_type;
std::vector<Window> orphaned_window_list;
miral::WindowManagerTools window_manager_tools;
miral::ExternalClientLauncher const external_client_launcher;
miral::InternalClientLauncher const internal_client_launcher;
miral::MirRunner& runner;
std::shared_ptr<MiracleConfig> config;
WorkspaceObserverRegistrar workspace_observer_registrar;
WorkspaceManager workspace_manager;
std::shared_ptr<Ipc> ipc;
void _add_to_output_immediately(Window&, std::shared_ptr<Screen>&);
void _add_to_output_immediately(Window&, std::shared_ptr<OutputContent>&);
};
}
#endif //MIRACLE_TILING_WINDOW_MANAGEMENT_POLICY_H
#endif //MIRACLE_POLICY_H

View File

@ -1,139 +0,0 @@
#include "screen.h"
#include "workspace_manager.h"
#include <miral/window_info.h>
using namespace miracle;
Screen::Screen(
miral::Output const& output,
WorkspaceManager& workspace_manager,
geom::Rectangle const& area,
miral::WindowManagerTools const& tools,
std::shared_ptr<MiracleConfig> const& config)
: output{output},
workspace_manager{workspace_manager},
area{area},
tools{tools},
config{config}
{
}
Tree &Screen::get_active_tree()
{
for (auto& info : workspaces)
{
if (info.workspace == active_workspace)
return *info.tree;
}
throw std::runtime_error("Unable to find the active tree. We shouldn't be here");
}
void Screen::advise_new_workspace(int workspace)
{
workspaces.push_back({workspace, std::make_shared<Tree>(this, tools, config)});
}
void Screen::advise_workspace_deleted(int workspace)
{
for (auto it = workspaces.begin(); it != workspaces.end(); it++)
{
if (it->workspace == workspace)
{
workspaces.erase(it);
return;
}
}
}
bool Screen::advise_workspace_active(int key)
{
for (auto& workspace : workspaces)
{
if (workspace.workspace == key)
{
ScreenWorkspaceInfo* previous_workspace = nullptr;
for (auto& other : workspaces)
{
if (other.workspace == active_workspace)
{
previous_workspace = &other;
hide(other);
break;
}
}
active_workspace = key;
show(workspace);
// Important: Delete the workspace only after we have shown the new one because we may want
// to move a node to the new workspace.
if (previous_workspace != nullptr)
{
auto& active_tree = previous_workspace->tree;
if (active_tree->is_empty())
workspace_manager.delete_workspace(previous_workspace->workspace);
}
return true;
}
}
return false;
}
void Screen::hide(ScreenWorkspaceInfo& info)
{
info.tree->hide();
}
void Screen::show(ScreenWorkspaceInfo& info)
{
info.tree->show();
}
const ScreenWorkspaceInfo &Screen::get_workspace(int key)
{
for (auto const& workspace : workspaces)
{
if (workspace.workspace == key)
return workspace;
}
mir::fatal_error("Cannot find workspace with key: %c", key);
}
void Screen::advise_application_zone_create(miral::Zone const& application_zone)
{
if (application_zone.extents().contains(area))
{
application_zone_list.push_back(application_zone);
for (auto& workspace : workspaces)
workspace.tree->recalculate_root_node_area();
}
}
void Screen::advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original)
{
for (auto& zone : application_zone_list)
if (zone == original)
{
zone = updated;
for (auto& workspace : workspaces)
workspace.tree->recalculate_root_node_area();
break;
}
}
void Screen::advise_application_zone_delete(miral::Zone const& application_zone)
{
if (std::remove(application_zone_list.begin(), application_zone_list.end(), application_zone) != application_zone_list.end())
{
for (auto& workspace : workspaces)
workspace.tree->recalculate_root_node_area();
}
}
bool Screen::point_is_in_output(int x, int y)
{
return area.contains(geom::Point(x, y));
}

View File

@ -1,76 +0,0 @@
#ifndef MIRACLE_SCREEN_H
#define MIRACLE_SCREEN_H
#include "tree.h"
#include <memory>
#include <miral/output.h>
namespace miracle
{
struct WorkspaceManager;
class MiracleConfig;
struct NodeResurrection
{
std::shared_ptr<Node> node;
MirWindowState state;
};
struct ScreenWorkspaceInfo
{
int workspace;
std::shared_ptr<Tree> tree;
std::vector<NodeResurrection> nodes_to_resurrect;
};
/// A screen is comprised of a map of workspaces, each having their own tree.
// Workspaces are shared across screens such that screens a workspace with a
// particular index can ONLY ever live on one screen at a time.
class Screen
{
public:
Screen(
miral::Output const& output,
WorkspaceManager& workspace_manager,
geom::Rectangle const& area,
miral::WindowManagerTools const& tools,
std::shared_ptr<MiracleConfig> const& options);
~Screen() = default;
Tree& get_active_tree();
int get_active_workspace() const { return active_workspace; }
void advise_new_workspace(int workspace);
void advise_workspace_deleted(int workspace);
bool advise_workspace_active(int workspace);
std::vector<ScreenWorkspaceInfo>& get_workspaces() { return workspaces; }
ScreenWorkspaceInfo const& get_workspace(int key);
void advise_application_zone_create(miral::Zone const& application_zone);
void advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original);
void advise_application_zone_delete(miral::Zone const& application_zone);
bool point_is_in_output(int x, int y);
geom::Rectangle const& get_area() { return area; }
std::vector<miral::Zone> const& get_app_zones() { return application_zone_list; }
miral::Output const& get_output() { return output; }
bool is_active() const { return is_active_; }
void set_is_active(bool new_is_active) { is_active_ = new_is_active; }
private:
miral::Output output;
WorkspaceManager& workspace_manager;
miral::WindowManagerTools tools;
geom::Rectangle area;
std::shared_ptr<MiracleConfig> config;
int active_workspace = -1;
std::vector<ScreenWorkspaceInfo> workspaces;
std::vector<miral::Zone> application_zone_list;
bool is_active_ = false;
void hide(ScreenWorkspaceInfo&);
void show(ScreenWorkspaceInfo&);
};
}
#endif

View File

@ -1,8 +1,9 @@
#include "window_metadata.h"
#define MIR_LOG_COMPONENT "window_tree"
#include "tree.h"
#include "window_helpers.h"
#include "screen.h"
#include "output_content.h"
#include "miracle_config.h"
#include <memory>
@ -13,7 +14,7 @@
using namespace miracle;
Tree::Tree(
Screen* screen,
OutputContent* screen,
miral::WindowManagerTools const& tools,
std::shared_ptr<MiracleConfig> const& config)
: screen{screen},
@ -40,9 +41,6 @@ Tree::~Tree()
miral::WindowSpecification Tree::allocate_position(const miral::WindowSpecification &requested_specification)
{
miral::WindowSpecification new_spec = requested_specification;
if (!window_helpers::is_tileable(requested_specification))
return new_spec;
new_spec.server_side_decorated() = false;
new_spec.min_width() = geom::Width{0};
new_spec.max_width() = geom::Width{std::numeric_limits<int>::max()};
@ -60,23 +58,16 @@ miral::WindowSpecification Tree::allocate_position(const miral::WindowSpecificat
return new_spec;
}
void Tree::advise_new_window(miral::WindowInfo const& window_info)
std::shared_ptr<Node> Tree::advise_new_window(miral::WindowInfo const& window_info)
{
if (!window_helpers::is_tileable(window_info))
{
if (window_info.state() == MirWindowState::mir_window_state_attached)
{
tools.select_active_window(window_info.window());
}
return;
}
_get_active_lane()->add_window(window_info.window());
auto node = _get_active_lane()->add_window(window_info.window());
if (window_helpers::is_window_fullscreen(window_info.state()))
{
tools.select_active_window(window_info.window());
advise_fullscreen_window(window_info);
advise_fullscreen_window(window_info.window());
}
return node;
}
void Tree::toggle_resize_mode()
@ -164,9 +155,9 @@ bool Tree::try_toggle_active_fullscreen()
tools.place_and_size_for_state(spec, window_info);
tools.modify_window(active_window->get_window(), spec);
if (is_active_window_fullscreen)
advise_restored_window(window_info);
advise_restored_window(window_info.window());
else
advise_fullscreen_window(window_info);
advise_fullscreen_window(window_info.window());
return true;
}
@ -634,9 +625,9 @@ void Tree::recalculate_root_node_area()
}
}
bool Tree::advise_fullscreen_window(miral::WindowInfo const& window_info)
bool Tree::advise_fullscreen_window(miral::Window& window)
{
auto node = window_helpers::get_node_for_window_by_tree(window_info.window(), tools, this);
auto node = window_helpers::get_node_for_window_by_tree(window, tools, this);
if (!node)
return false;
@ -646,9 +637,9 @@ bool Tree::advise_fullscreen_window(miral::WindowInfo const& window_info)
return true;
}
bool Tree::advise_restored_window(miral::WindowInfo const& window_info)
bool Tree::advise_restored_window(miral::Window& window)
{
auto node = window_helpers::get_node_for_window_by_tree(window_info.window(), tools, this);
auto node = window_helpers::get_node_for_window_by_tree(window, tools, this);
if (!node)
return false;
@ -670,13 +661,13 @@ bool Tree::handle_window_ready(miral::WindowInfo &window_info)
if (window_info.can_be_active())
tools.select_active_window(window_info.window());
constrain(window_info);
constrain(window_info.window());
return true;
}
bool Tree::advise_state_change(const miral::WindowInfo &window_info, MirWindowState state)
bool Tree::advise_state_change(miral::Window const& window, MirWindowState state)
{
auto node = window_helpers::get_node_for_window_by_tree(window_info.window(), tools, this);
auto node = window_helpers::get_node_for_window_by_tree(window, tools, this);
if (!node)
return false;
@ -706,11 +697,11 @@ bool Tree::advise_state_change(const miral::WindowInfo &window_info, MirWindowSt
}
bool Tree::confirm_placement_on_display(
const miral::WindowInfo &window_info,
miral::Window const& window,
MirWindowState new_state,
mir::geometry::Rectangle &new_placement)
{
auto node = window_helpers::get_node_for_window_by_tree(window_info.window(), tools, this);
auto node = window_helpers::get_node_for_window_by_tree(window, tools, this);
if (!node)
return false;
@ -727,9 +718,9 @@ bool Tree::confirm_placement_on_display(
return true;
}
bool Tree::constrain(miral::WindowInfo &window_info)
bool Tree::constrain(miral::Window& window)
{
auto node = window_helpers::get_node_for_window_by_tree(window_info.window(), tools, this);
auto node = window_helpers::get_node_for_window_by_tree(window, tools, this);
if (!node)
return false;
@ -746,19 +737,6 @@ bool Tree::constrain(miral::WindowInfo &window_info)
return true;
}
void Tree::add_tree(std::shared_ptr<Tree> const& other_tree)
{
other_tree->foreach_node([&](auto node)
{
if (node->is_window())
{
auto new_node_position = root_lane->create_new_node_position();
node->set_logical_area(new_node_position);
root_lane->add_window(node->get_window());
}
});
}
namespace
{
void foreach_node_internal(std::function<void(std::shared_ptr<Node>)> const& f, std::shared_ptr<Node> const& parent)

View File

@ -1,5 +1,5 @@
#ifndef WINDOW_TREE_H
#define WINDOW_TREE_H
#ifndef MIRACLE_TREE_H
#define MIRACLE_TREE_H
#include "node.h"
#include <memory>
@ -16,7 +16,7 @@ namespace geom = mir::geometry;
namespace miracle
{
class Screen;
class OutputContent;
class MiracleConfig;
enum class Direction
@ -30,14 +30,14 @@ enum class Direction
class Tree
{
public:
Tree(Screen* parent, miral::WindowManagerTools const& tools, std::shared_ptr<MiracleConfig> const& options);
Tree(OutputContent* parent, miral::WindowManagerTools const& tools, std::shared_ptr<MiracleConfig> const& options);
~Tree();
/// Makes space for the new window and returns its specified spot in the grid. Note that the returned
/// position is the position WITH GAPS.
miral::WindowSpecification allocate_position(const miral::WindowSpecification &requested_specification);
void advise_new_window(miral::WindowInfo const&);
std::shared_ptr<Node> advise_new_window(miral::WindowInfo const&);
/// Places us into resize mode. Other operations are prohibited while we are in resize mode.
void toggle_resize_mode();
@ -74,20 +74,18 @@ public:
bool select_window_from_point(int x, int y);
bool advise_fullscreen_window(miral::WindowInfo const&);
bool advise_restored_window(miral::WindowInfo const &window_info);
bool advise_fullscreen_window(miral::Window&);
bool advise_restored_window(miral::Window&);
bool handle_window_ready(miral::WindowInfo& window_info);
bool advise_state_change(miral::WindowInfo const& window_info, MirWindowState state);
bool advise_state_change(miral::Window const& window, MirWindowState state);
bool confirm_placement_on_display(
const miral::WindowInfo &window_info,
miral::Window const& window,
MirWindowState new_state,
mir::geometry::Rectangle &new_placement);
/// Constrains the window to its tile if it is in this tree.
bool constrain(miral::WindowInfo& window_info);
void add_tree(std::shared_ptr<Tree> const&);
bool constrain(miral::Window& window);
void foreach_node(std::function<void(std::shared_ptr<Node>)> const&);
void close_active_window();
@ -120,7 +118,7 @@ private:
MirWindowState state;
};
Screen* screen;
OutputContent* screen;
miral::WindowManagerTools tools;
std::shared_ptr<MiracleConfig> config;
std::shared_ptr<Node> root_lane;
@ -144,4 +142,4 @@ private:
}
#endif //MIRCOMPOSITOR_WINDOW_TREE_H
#endif //MIRACLE_TREE_H

View File

@ -13,16 +13,33 @@ bool miracle::window_helpers::is_window_fullscreen(MirWindowState state)
|| state == mir_window_state_vertmaximized;
}
std::shared_ptr<miracle::WindowMetadata> miracle::window_helpers::get_metadata(const miral::WindowInfo &info)
{
if (info.userdata())
return static_pointer_cast<WindowMetadata>(info.userdata());
mir::log_error("Unable to find metadata for window");
return nullptr;
}
std::shared_ptr<miracle::WindowMetadata>
miracle::window_helpers::get_metadata(const miral::Window &window, const miral::WindowManagerTools &tools)
{
auto& info = tools.info_for(window);
if (info.userdata())
return static_pointer_cast<WindowMetadata>(info.userdata());
mir::log_error("Unable to find metadata for window");
return nullptr;
}
std::shared_ptr<miracle::Node> miracle::window_helpers::get_node_for_window(
miral::Window const& window,
miral::WindowManagerTools const& tools)
{
auto& info = tools.info_for(window);
if (info.userdata())
{
std::shared_ptr<WindowMetadata> data = static_pointer_cast<WindowMetadata>(info.userdata());
return data->get_tiling_node();
}
auto metadata = get_metadata(window, tools);
if (metadata)
return metadata->get_tiling_node();
mir::log_error("Unable to find node for window");
return nullptr;
@ -35,9 +52,7 @@ std::shared_ptr<miracle::Node> miracle::window_helpers::get_node_for_window_by_t
{
auto node = get_node_for_window(window, tools);
if (node && node->get_tree() == tree)
{
return node;
}
return nullptr;
}

View File

@ -8,6 +8,7 @@ namespace miracle
{
class Node;
class Tree;
class WindowMetadata;
namespace window_helpers
{
@ -24,6 +25,13 @@ bool is_tileable(T const& requested_specification)
&& !has_exclusive_rect;
}
std::shared_ptr<WindowMetadata> get_metadata(
miral::WindowInfo const& info);
std::shared_ptr<WindowMetadata> get_metadata(
miral::Window const& window,
miral::WindowManagerTools const& tools);
std::shared_ptr<Node> get_node_for_window(
miral::Window const& window,
miral::WindowManagerTools const& tools);

View File

@ -2,9 +2,13 @@
using namespace miracle;
WindowMetadata::WindowMetadata(miracle::WindowType type, miral::Window const& window)
WindowMetadata::WindowMetadata(
miracle::WindowType type,
miral::Window const& window,
OutputContent* output)
: type{type},
window{window}
window{window},
output{output}
{}
void WindowMetadata::associate_to_node(std::shared_ptr<Node> const& node)

View File

@ -7,20 +7,22 @@
namespace miracle
{
class OutputContent;
class Node;
enum class WindowType
{
none,
tiled,
floating
floating,
other
};
/// Applied to WindowInfo to enable
class WindowMetadata
{
public:
WindowMetadata(WindowType type, miral::Window const& window);
WindowMetadata(WindowType type, miral::Window const& window, OutputContent* output);
void associate_to_node(std::shared_ptr<Node> const&);
miral::Window& get_window() { return window; }
std::shared_ptr<Node> get_tiling_node() {
@ -28,11 +30,14 @@ public:
return tiling_node;
return nullptr;
}
WindowType get_type() { return type; }
OutputContent* get_output() { return output; }
private:
WindowType type;
miral::Window window;
OutputContent* output;
std::shared_ptr<Node> tiling_node;
};

34
src/workspace_content.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "workspace_content.h"
#include "tree.h"
using namespace miracle;
WorkspaceContent::WorkspaceContent(
miracle::OutputContent *screen,
miral::WindowManagerTools const& tools,
int workspace,
std::shared_ptr<MiracleConfig> const& config)
: tree(std::make_shared<Tree>(screen, tools, config)),
workspace{workspace}
{
}
int WorkspaceContent::get_workspace() const
{
return workspace;
}
std::shared_ptr<Tree> WorkspaceContent::get_tree() const
{
return tree;
}
void WorkspaceContent::show()
{
tree->show();
}
void WorkspaceContent::hide()
{
tree->hide();
}

33
src/workspace_content.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef MIRACLEWM_WORKSPACE_CONTENT_H
#define MIRACLEWM_WORKSPACE_CONTENT_H
#include <miral/window_manager_tools.h>
namespace miracle
{
class OutputContent;
class MiracleConfig;
class Tree;
class WorkspaceContent
{
public:
WorkspaceContent(
OutputContent* screen,
miral::WindowManagerTools const& tools,
int workspace,
std::shared_ptr<MiracleConfig> const& config);
[[nodiscard]] int get_workspace() const;
[[nodiscard]] std::shared_ptr<Tree> get_tree() const;
void show();
void hide();
private:
std::shared_ptr<Tree> tree;
int workspace;
};
} // miracle
#endif //MIRACLEWM_WORKSPACE_CONTENT_H

View File

@ -1,6 +1,6 @@
#define MIR_LOG_COMPONENT "workspace_manager"
#include "workspace_manager.h"
#include "screen.h"
#include "output_content.h"
#include "window_helpers.h"
#include <mir/log.h>
@ -11,19 +11,19 @@ using namespace miracle;
WorkspaceManager::WorkspaceManager(
WindowManagerTools const& tools,
WorkspaceObserverRegistrar& registry,
std::function<std::shared_ptr<Screen> const()> const& get_active_screen) :
std::function<std::shared_ptr<OutputContent> const()> const& get_active_screen) :
tools_{tools},
registry{registry},
get_active_screen{get_active_screen}
{
}
std::shared_ptr<Screen> WorkspaceManager::request_workspace(std::shared_ptr<Screen> screen, int key)
std::shared_ptr<OutputContent> WorkspaceManager::request_workspace(std::shared_ptr<OutputContent> screen, int key)
{
if (workspaces[key] != nullptr)
{
auto workspace = workspaces[key];
auto active_workspace = workspace->get_active_workspace();
auto active_workspace = workspace->get_active_workspace_num();
if (active_workspace == key)
{
mir::log_warning("Same workspace selected twice in a row");
@ -42,7 +42,7 @@ std::shared_ptr<Screen> WorkspaceManager::request_workspace(std::shared_ptr<Scre
return screen;
}
bool WorkspaceManager::request_first_available_workspace(std::shared_ptr<Screen> screen)
bool WorkspaceManager::request_first_available_workspace(std::shared_ptr<OutputContent> screen)
{
for (int i = 1; i < NUM_WORKSPACES; i++)
{
@ -62,27 +62,45 @@ bool WorkspaceManager::request_first_available_workspace(std::shared_ptr<Screen>
return false;
}
bool WorkspaceManager::move_active_to_workspace(std::shared_ptr<Screen> screen, int workspace)
bool WorkspaceManager::move_active_to_workspace(std::shared_ptr<OutputContent> screen, int workspace)
{
auto window = tools_.active_window();
if (!window)
return false;
auto& original_tree = screen->get_active_tree();
auto metadata = window_helpers::get_metadata(window, tools_);
switch (metadata->get_type())
{
case WindowType::tiled:
{
auto original_tree = screen->get_active_tree();
auto window_node = window_helpers::get_node_for_window(window, tools_);
original_tree.advise_delete_window(window);
original_tree->advise_delete_window(window);
auto screen_to_move_to = request_workspace(screen, workspace);
auto& prev_info = tools_.info_for(window);
// TODO: These need to be set so that the window is correctly seen as tileable
// WARNING: These need to be set so that the window is correctly seen as tileable
miral::WindowSpecification spec;
spec.type() = prev_info.type();
spec.state() = prev_info.state();
spec = screen_to_move_to->get_active_tree().allocate_position(spec);
spec = screen_to_move_to->get_active_tree()->allocate_position(spec);
tools_.modify_window(window, spec);
screen_to_move_to->get_active_tree().advise_new_window(prev_info);
screen_to_move_to->get_active_tree().handle_window_ready(prev_info);
auto new_node = screen_to_move_to->get_active_tree()->advise_new_window(prev_info);
metadata->associate_to_node(new_node);
miral::WindowSpecification next_spec;
next_spec.userdata() = metadata;
tools_.modify_window(window, next_spec);
screen_to_move_to->get_active_tree()->handle_window_ready(prev_info);
break;
}
default:
mir::log_error("Cannot move window of type %d to a new workspace", (int)metadata->get_type());
return false;
}
return true;
}
@ -109,7 +127,7 @@ void WorkspaceManager::request_focus(int key)
if (active_screen != nullptr)
{
auto active_workspace = active_screen->get_active_workspace();
auto active_workspace = active_screen->get_active_workspace_num();
registry.advise_focused(active_screen, active_workspace, workspaces[key], key);
}
else

View File

@ -19,7 +19,7 @@ using miral::WindowManagerTools;
using miral::WindowSpecification;
using miral::Workspace;
class Screen;
class OutputContent;
class WorkspaceManager
{
@ -27,30 +27,30 @@ public:
explicit WorkspaceManager(
WindowManagerTools const& tools,
WorkspaceObserverRegistrar& registry,
std::function<std::shared_ptr<Screen> const()> const& get_active_screen);
std::function<std::shared_ptr<OutputContent> const()> const& get_active_screen);
virtual ~WorkspaceManager() = default;
/// Request the workspace. If it does not yet exist, then one
/// is created on the current Screen. If it does exist, we navigate
/// to the screen containing that workspace and show it if it
/// isn't already shown.
std::shared_ptr<Screen> request_workspace(std::shared_ptr<Screen> screen, int workspace);
std::shared_ptr<OutputContent> request_workspace(std::shared_ptr<OutputContent> screen, int workspace);
bool request_first_available_workspace(std::shared_ptr<Screen> screen);
bool request_first_available_workspace(std::shared_ptr<OutputContent> screen);
bool move_active_to_workspace(std::shared_ptr<Screen> screen, int workspace);
bool move_active_to_workspace(std::shared_ptr<OutputContent> screen, int workspace);
bool delete_workspace(int workspace);
void request_focus(int workspace);
static int constexpr NUM_WORKSPACES = 10;
std::array<std::shared_ptr<Screen>, NUM_WORKSPACES> const& get_workspaces() { return workspaces; }
std::array<std::shared_ptr<OutputContent>, NUM_WORKSPACES> const& get_workspaces() { return workspaces; }
private:
WindowManagerTools tools_;
WorkspaceObserverRegistrar& registry;
std::function<std::shared_ptr<Screen> const()> get_active_screen;
std::array<std::shared_ptr<Screen>, NUM_WORKSPACES> workspaces;
std::function<std::shared_ptr<OutputContent> const()> get_active_screen;
std::array<std::shared_ptr<OutputContent>, NUM_WORKSPACES> workspaces;
};
}

View File

@ -32,7 +32,7 @@ void WorkspaceObserverRegistrar::unregister_interest(miracle::WorkspaceObserver&
}));
}
void WorkspaceObserverRegistrar::advise_created(std::shared_ptr<Screen> const& info, int key)
void WorkspaceObserverRegistrar::advise_created(std::shared_ptr<OutputContent> const& info, int key)
{
for (auto& observer : observers)
{
@ -41,7 +41,7 @@ void WorkspaceObserverRegistrar::advise_created(std::shared_ptr<Screen> const& i
}
}
void WorkspaceObserverRegistrar::advise_removed(std::shared_ptr<Screen> const& info, int key)
void WorkspaceObserverRegistrar::advise_removed(std::shared_ptr<OutputContent> const& info, int key)
{
for (auto& observer : observers)
{
@ -51,9 +51,9 @@ void WorkspaceObserverRegistrar::advise_removed(std::shared_ptr<Screen> const& i
}
void WorkspaceObserverRegistrar::advise_focused(
std::shared_ptr<Screen> const& previous,
std::shared_ptr<OutputContent> const& previous,
int previous_key,
std::shared_ptr<Screen> const& current,
std::shared_ptr<OutputContent> const& current,
int current_key)
{
for (auto& observer : observers)

View File

@ -1,22 +1,22 @@
#ifndef MIRACLEWM_WORKSPACE_OBSERVER_H
#define MIRACLEWM_WORKSPACE_OBSERVER_H
#include "screen.h"
#include "output_content.h"
#include <mir/executor.h>
#include <memory>
namespace miracle
{
class Screen;
class OutputContent;
class WorkspaceObserver
{
public:
virtual ~WorkspaceObserver() = default;
virtual void on_created(std::shared_ptr<Screen> const&, int) = 0;
virtual void on_removed(std::shared_ptr<Screen> const&, int) = 0;
virtual void on_focused(std::shared_ptr<Screen> const& previous, int, std::shared_ptr<Screen> const& current, int) = 0;
virtual void on_created(std::shared_ptr<OutputContent> const&, int) = 0;
virtual void on_removed(std::shared_ptr<OutputContent> const&, int) = 0;
virtual void on_focused(std::shared_ptr<OutputContent> const& previous, int, std::shared_ptr<OutputContent> const& current, int) = 0;
int get_id() const;
protected:
@ -32,9 +32,9 @@ public:
WorkspaceObserverRegistrar() = default;
void register_interest(std::weak_ptr<WorkspaceObserver>);
void unregister_interest(WorkspaceObserver&);
void advise_created(std::shared_ptr<Screen> const&, int);
void advise_removed(std::shared_ptr<Screen> const&, int);
void advise_focused(std::shared_ptr<Screen> const& previous, int, std::shared_ptr<Screen> const& current, int);
void advise_created(std::shared_ptr<OutputContent> const&, int);
void advise_removed(std::shared_ptr<OutputContent> const&, int);
void advise_focused(std::shared_ptr<OutputContent> const& previous, int, std::shared_ptr<OutputContent> const& current, int);
private:
std::vector<std::weak_ptr<WorkspaceObserver>> observers;