feature: rudimentary workspace foundation (#18)

* workspace data structure in place

* feature: rudimentary workspace support, but I expect some bugs

* bugfix: unfocusable nodes
This commit is contained in:
Matthew Kosarek 2024-02-07 08:05:09 -05:00 committed by GitHub
parent b8ebe4ab8c
commit b9405e62d9
13 changed files with 811 additions and 79 deletions

View File

@ -29,6 +29,8 @@ add_executable(miracle-wm
src/window_helpers.cpp
src/miracle_config.cpp
src/miracle_config.h
src/screen.cpp
src/workspace_manager.cpp
)
target_include_directories(miracle-wm PUBLIC SYSTEM ${MIRAL_INCLUDE_DIRS})

View File

@ -6,7 +6,6 @@
#include <miral/keymap.h>
#include <miral/x11_support.h>
#include <miral/wayland_extensions.h>
#include <miral/command_line_option.h>
#include <miral/display_configuration_option.h>
#include <miral/add_init_callback.h>
#include "miracle_window_management_policy.h"

View File

@ -101,6 +101,26 @@ MiracleConfig::MiracleConfig()
key_command = DefaultKeyCommand::QuitCompositor;
else if (name == "fullscreen")
key_command = DefaultKeyCommand::Fullscreen;
else if (name == "select_workspace_1")
key_command = DefaultKeyCommand::SelectWorkspace1;
else if (name == "select_workspace_2")
key_command = DefaultKeyCommand::SelectWorkspace2;
else if (name == "select_workspace_3")
key_command = DefaultKeyCommand::SelectWorkspace3;
else if (name == "select_workspace_4")
key_command = DefaultKeyCommand::SelectWorkspace4;
else if (name == "select_workspace_5")
key_command = DefaultKeyCommand::SelectWorkspace5;
else if (name == "select_workspace_6")
key_command = DefaultKeyCommand::SelectWorkspace6;
else if (name == "select_workspace_7")
key_command = DefaultKeyCommand::SelectWorkspace7;
else if (name == "select_workspace_8")
key_command = DefaultKeyCommand::SelectWorkspace8;
else if (name == "select_workspace_9")
key_command = DefaultKeyCommand::SelectWorkspace9;
else if (name == "select_workspace_0")
key_command = DefaultKeyCommand::SelectWorkspace0;
else {
mir::log_error("default_action_overrides: Unknown key command override: %s", name.c_str());
continue;
@ -229,6 +249,56 @@ MiracleConfig::MiracleConfig()
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_F
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_1
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_2
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_3
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_4
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_5
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_6
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_7
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_8
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_9
},
{
MirKeyboardAction ::mir_keyboard_action_down,
miracle_input_event_modifier_default,
KEY_0
}
};
for (int i = 0; i < DefaultKeyCommand::MAX; i++)

View File

@ -26,6 +26,16 @@ enum DefaultKeyCommand
QuitActiveWindow,
QuitCompositor,
Fullscreen,
SelectWorkspace1,
SelectWorkspace2,
SelectWorkspace3,
SelectWorkspace4,
SelectWorkspace5,
SelectWorkspace6,
SelectWorkspace7,
SelectWorkspace8,
SelectWorkspace9,
SelectWorkspace0,
MAX
};

View File

@ -3,6 +3,7 @@
#include "miracle_window_management_policy.h"
#include "window_helpers.h"
#include "miracle_config.h"
#include "workspace_manager.h"
#include <mir_toolkit/events/enums.h>
#include <miral/toolkit_event.h>
@ -80,7 +81,8 @@ MiracleWindowManagementPolicy::MiracleWindowManagementPolicy(
external_client_launcher{external_client_launcher},
internal_client_launcher{internal_client_launcher},
runner{runner},
config{config}
config{config},
workspace_manager{WorkspaceManager(tools)}
{
}
@ -104,59 +106,89 @@ bool MiracleWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const
}
return true;
case RequestVertical:
active_tree->tree.request_vertical();
active_output->screen->get_active_tree().request_vertical();
return true;
case RequestHorizontal:
active_tree->tree.request_horizontal();
active_output->screen->get_active_tree().request_horizontal();
return true;
case ToggleResize:
active_tree->tree.toggle_resize_mode();
active_output->screen->get_active_tree().toggle_resize_mode();
return true;
case MoveUp:
if (active_tree->tree.try_move_active_window(Direction::up))
if (active_output->screen->get_active_tree().try_move_active_window(Direction::up))
return true;
return false;
case MoveDown:
if (active_tree->tree.try_move_active_window(Direction::down))
if (active_output->screen->get_active_tree().try_move_active_window(Direction::down))
return true;
return false;
case MoveLeft:
if (active_tree->tree.try_move_active_window(Direction::left))
if (active_output->screen->get_active_tree().try_move_active_window(Direction::left))
return true;
return false;
case MoveRight:
if (active_tree->tree.try_move_active_window(Direction::right))
if (active_output->screen->get_active_tree().try_move_active_window(Direction::right))
return true;
return false;
case SelectUp:
if (active_tree->tree.try_resize_active_window(Direction::up)
|| active_tree->tree.try_select_next(Direction::up))
if (active_output->screen->get_active_tree().try_resize_active_window(Direction::up)
|| active_output->screen->get_active_tree().try_select_next(Direction::up))
return true;
return false;
case SelectDown:
if (active_tree->tree.try_resize_active_window(Direction::down)
|| active_tree->tree.try_select_next(Direction::down))
if (active_output->screen->get_active_tree().try_resize_active_window(Direction::down)
|| active_output->screen->get_active_tree().try_select_next(Direction::down))
return true;
return false;
case SelectLeft:
if (active_tree->tree.try_resize_active_window(Direction::left)
|| active_tree->tree.try_select_next(Direction::left))
if (active_output->screen->get_active_tree().try_resize_active_window(Direction::left)
|| active_output->screen->get_active_tree().try_select_next(Direction::left))
return true;
return false;
case SelectRight:
if (active_tree->tree.try_resize_active_window(Direction::right)
|| active_tree->tree.try_select_next(Direction::right))
if (active_output->screen->get_active_tree().try_resize_active_window(Direction::right)
|| active_output->screen->get_active_tree().try_select_next(Direction::right))
return true;
return false;
case QuitActiveWindow:
active_tree->tree.close_active_window();
active_output->screen->get_active_tree().close_active_window();
return true;
case QuitCompositor:
runner.stop();
return true;
case Fullscreen:
active_tree->tree.try_toggle_active_fullscreen();
active_output->screen->get_active_tree().try_toggle_active_fullscreen();
return true;
case SelectWorkspace1:
workspace_manager.request_workspace(active_output->screen, '1');
break;
case SelectWorkspace2:
workspace_manager.request_workspace(active_output->screen, '2');
break;
case SelectWorkspace3:
workspace_manager.request_workspace(active_output->screen, '3');
break;
case SelectWorkspace4:
workspace_manager.request_workspace(active_output->screen, '4');
break;
case SelectWorkspace5:
workspace_manager.request_workspace(active_output->screen, '5');
break;
case SelectWorkspace6:
workspace_manager.request_workspace(active_output->screen, '6');
break;
case SelectWorkspace7:
workspace_manager.request_workspace(active_output->screen, '7');
break;
case SelectWorkspace8:
workspace_manager.request_workspace(active_output->screen, '8');
break;
case SelectWorkspace9:
workspace_manager.request_workspace(active_output->screen, '9');
break;
case SelectWorkspace0:
workspace_manager.request_workspace(active_output->screen, '0');
break;
default:
std::cerr << "Unknown key_command: " << key_command << std::endl;
break;
@ -169,12 +201,12 @@ bool MiracleWindowManagementPolicy::handle_pointer_event(MirPointerEvent const*
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);
for (auto const& pair : tree_list)
for (auto const& pair : output_list)
{
if (pair->tree.point_is_in_output(static_cast<int>(x), static_cast<int>(y)))
if (active_output->screen->get_active_tree().point_is_in_output(static_cast<int>(x), static_cast<int>(y)))
{
active_tree = pair;
active_tree->tree.select_window_from_point(static_cast<int>(x), static_cast<int>(y));
active_output = pair;
active_output->screen->get_active_tree().select_window_from_point(static_cast<int>(x), static_cast<int>(y));
break;
}
}
@ -190,7 +222,7 @@ auto MiracleWindowManagementPolicy::place_new_window(
{
// In this step, we'll ask the WindowTree where we should place the window on the display
// We will also resize the adjacent windows accordingly in this step.
return active_tree->tree.allocate_position(requested_specification);
return active_output->screen->get_active_tree().allocate_position(requested_specification);
}
return requested_specification;
@ -200,7 +232,7 @@ void MiracleWindowManagementPolicy::advise_new_window(miral::WindowInfo const& w
{
miral::WindowManagementPolicy::advise_new_window(window_info);
if (is_tileable(window_info))
active_tree->tree.advise_new_window(window_info);
active_output->screen->get_active_tree().advise_new_window(window_info);
}
void MiracleWindowManagementPolicy::handle_window_ready(miral::WindowInfo &window_info)
@ -210,30 +242,30 @@ void MiracleWindowManagementPolicy::handle_window_ready(miral::WindowInfo &windo
return;
}
for (auto const& tree : tree_list)
for (auto const& output : output_list)
{
if (tree->tree.handle_window_ready(window_info))
if (output->screen->get_active_tree().handle_window_ready(window_info))
break;
}
}
void MiracleWindowManagementPolicy::advise_focus_gained(const miral::WindowInfo &window_info)
{
for (auto const& tree : tree_list)
tree->tree.advise_focus_gained(window_info.window());
for (auto const& output : output_list)
output->screen->get_active_tree().advise_focus_gained(window_info.window());
window_manager_tools.raise_tree(window_info.window());
}
void MiracleWindowManagementPolicy::advise_focus_lost(const miral::WindowInfo &window_info)
{
for (auto const& tree : tree_list)
tree->tree.advise_focus_lost(window_info.window());
for (auto const& output : output_list)
output->screen->get_active_tree().advise_focus_lost(window_info.window());
}
void MiracleWindowManagementPolicy::advise_delete_window(const miral::WindowInfo &window_info)
{
for (auto const& tree : tree_list)
tree->tree.advise_delete_window(window_info.window());
for (auto const& output : output_list)
output->screen->get_active_tree().advise_delete_window(window_info.window());
}
void MiracleWindowManagementPolicy::advise_move_to(miral::WindowInfo const& window_info, geom::Point top_left)
@ -244,21 +276,25 @@ void MiracleWindowManagementPolicy::advise_move_to(miral::WindowInfo const& wind
void MiracleWindowManagementPolicy::advise_output_create(miral::Output const& output)
{
WindowTreeOptions options = { config.get_gap_size_x(), config.get_gap_size_y() };
auto new_tree = std::make_shared<OutputTreePair>(
auto new_tree = std::make_shared<OutputInfo>(
output,
WindowTree(output.extents(), window_manager_tools, options));
tree_list.push_back(new_tree);
if (active_tree == nullptr)
active_tree = new_tree;
std::make_shared<Screen>(workspace_manager, output.extents(), window_manager_tools, options));
workspace_manager.request_first_available_workspace(new_tree->screen);
output_list.push_back(new_tree);
if (active_output == nullptr)
active_output = new_tree;
}
void MiracleWindowManagementPolicy::advise_output_update(miral::Output const& updated, miral::Output const& original)
{
for (auto& pair : tree_list)
for (auto& output : output_list)
{
if (pair->output.is_same_output(original))
if (output->output.is_same_output(original))
{
pair->tree.set_output_area(updated.extents());
for (auto workspace : output->screen->get_workspaces())
{
workspace.tree.set_output_area(updated.extents());
}
break;
}
}
@ -266,20 +302,21 @@ void MiracleWindowManagementPolicy::advise_output_update(miral::Output const& up
void MiracleWindowManagementPolicy::advise_output_delete(miral::Output const& output)
{
for (auto const& it : tree_list)
for (auto const& it : output_list)
{
if (it->output.is_same_output(output))
{
// TODO: Move windows open on the dying output to the other output
auto did_remove = std::remove(tree_list.begin(), tree_list.end(), it);
if (did_remove != tree_list.end() && it == active_tree)
auto did_remove = std::remove(output_list.begin(), output_list.end(), it);
if (did_remove != output_list.end() && it == active_output)
{
if (tree_list.empty())
active_tree = nullptr;
if (output_list.empty())
active_output = nullptr;
else
{
active_tree = tree_list[0];
active_tree->tree.add_tree(it->tree);
// TODO: Add ALL Trees
active_output = output_list[0];
active_output->screen->get_active_tree().add_tree(it->screen->get_active_tree());
}
}
break;
@ -289,9 +326,9 @@ void MiracleWindowManagementPolicy::advise_output_delete(miral::Output const& ou
void MiracleWindowManagementPolicy::advise_state_change(miral::WindowInfo const& window_info, MirWindowState state)
{
for (auto const& tree : tree_list)
for (auto const& output : output_list)
{
if (tree->tree.advise_state_change(window_info, state))
if (active_output->screen->get_active_tree().advise_state_change(window_info, state))
{
break;
}
@ -306,27 +343,55 @@ void MiracleWindowManagementPolicy::handle_modify_window(
{
if (modifications.state().value() == mir_window_state_fullscreen || modifications.state().value() == mir_window_state_maximized)
{
for (auto const& tree : tree_list)
if (tree->tree.advise_fullscreen_window(window_info))
break;
for (auto const& output : output_list)
{
bool found = false;
for (auto workspace : output->screen->get_workspaces())
{
if (workspace.tree.advise_fullscreen_window(window_info))
{
found = true;
break;
}
}
if (found) break;
}
}
else if (modifications.state().value() == mir_window_state_restored)
{
for (auto const& tree : tree_list)
for (auto const& output : output_list)
{
if (tree->tree.advise_restored_window(window_info))
break;
bool found = false;
for (auto workspace : output->screen->get_workspaces())
{
if (workspace.tree.advise_restored_window(window_info))
{
found = true;
break;
}
}
if (found) break;
}
}
}
for (auto const& tree :tree_list)
for (auto const& output :output_list)
{
if (tree->tree.constrain(window_info))
break;
}
bool found = false;
for (auto workspace : output->screen->get_workspaces())
{
if (workspace.tree.constrain(window_info))
{
found = true;
window_manager_tools.modify_window(window_info.window(), modifications);
break;
}
}
window_manager_tools.modify_window(window_info.window(), modifications);
if (found) break;
}
}
void MiracleWindowManagementPolicy::handle_raise_window(miral::WindowInfo &window_info)
@ -341,12 +406,19 @@ MiracleWindowManagementPolicy::confirm_placement_on_display(
const mir::geometry::Rectangle &new_placement)
{
mir::geometry::Rectangle modified_placement = new_placement;
for (auto const& tree : tree_list) {
if (tree->tree.confirm_placement_on_display(window_info, new_state, modified_placement))
for (auto const& output : output_list)\
{
bool found = false;
for (auto workspace : output->screen->get_workspaces())
{
break;
if (workspace.tree.confirm_placement_on_display(window_info, new_state, modified_placement))
{
found = true;
break;
}
}
if (found) break;
}
return modified_placement;
}
@ -378,18 +450,27 @@ mir::geometry::Rectangle MiracleWindowManagementPolicy::confirm_inherited_move(
void MiracleWindowManagementPolicy::advise_application_zone_create(miral::Zone const& application_zone)
{
for (auto const& tree : tree_list)
tree->tree.advise_application_zone_create(application_zone);
for (auto const& output : output_list)
{
for (auto workspace : output->screen->get_workspaces())
workspace.tree.advise_application_zone_create(application_zone);
}
}
void MiracleWindowManagementPolicy::advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original)
{
for (auto const& tree : tree_list)
tree->tree.advise_application_zone_update(updated, original);
for (auto const& output : output_list)
{
for (auto workspace : output->screen->get_workspaces())
workspace.tree.advise_application_zone_update(updated, original);
}
}
void MiracleWindowManagementPolicy::advise_application_zone_delete(miral::Zone const& application_zone)
{
for (auto const& tree : tree_list)
tree->tree.advise_application_zone_delete(application_zone);
for (auto const& output : output_list)
{
for (auto workspace : output->screen->get_workspaces())
workspace.tree.advise_application_zone_delete(application_zone);
}
}

View File

@ -1,8 +1,9 @@
#ifndef MIRIE_WINDOW_MANAGEMENT_POLICY_H
#define MIRIE_WINDOW_MANAGEMENT_POLICY_H
#include "window_tree.h"
#include "screen.h"
#include "miracle_config.h"
#include "workspace_manager.h"
#include <miral/window_manager_tools.h>
#include <miral/window_management_policy.h>
@ -20,10 +21,10 @@ class MirRunner;
namespace miracle
{
struct OutputTreePair
struct OutputInfo
{
miral::Output output;
WindowTree tree;
std::shared_ptr<Screen> screen;
};
class MiracleWindowManagementPolicy : public miral::WindowManagementPolicy
@ -80,13 +81,14 @@ public:
void advise_application_zone_delete(miral::Zone const& application_zone) override;
private:
std::shared_ptr<OutputTreePair> active_tree;
std::vector<std::shared_ptr<OutputTreePair>> tree_list;
std::shared_ptr<OutputInfo> active_output;
std::vector<std::shared_ptr<OutputInfo>> output_list;
miral::WindowManagerTools window_manager_tools;
miral::ExternalClientLauncher const external_client_launcher;
miral::InternalClientLauncher const internal_client_launcher;
miral::MirRunner& runner;
MiracleConfig const& config;
WorkspaceManager workspace_manager;
};
}

View File

@ -47,9 +47,9 @@ InsertNodeInternalResult insert_node_internal(
int lane_pos,
int index,
int node_count,
std::function<int(int)> get_node_size,
std::function<int(int)> get_node_position,
std::function<void(int, int, int)> set_node_size_position)
std::function<int(int)> const& get_node_size,
std::function<int(int)> const& get_node_position,
std::function<void(int, int, int)> const& set_node_size_position)
{
int new_item_size = floorf64((double)lane_size / (double)(node_count + 1));
int new_item_position = lane_pos + index * new_item_size;

75
src/screen.cpp Normal file
View File

@ -0,0 +1,75 @@
#include "screen.h"
#include "workspace_manager.h"
#include <miral/window_info.h>
using namespace miracle;
Screen::Screen(
WorkspaceManager& workspace_manager,
geom::Rectangle const& area,
miral::WindowManagerTools const& tools,
WindowTreeOptions const& options)
: workspace_manager{workspace_manager},
area{area},
tools{tools},
options{options}
{
}
WindowTree &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(char workspace)
{
workspaces.push_back({
workspace,
WindowTree(area, tools, options)
});
make_workspace_active(workspace);
}
bool Screen::make_workspace_active(char key)
{
for (auto& workspace : workspaces)
{
if (workspace.workspace == key)
{
// Deactivate current workspace
for (auto& other : workspaces)
{
if (other.workspace == active_workspace)
{
hide(other);
break;
}
}
// Active new workspace
show(workspace);
active_workspace = key;
return true;
}
}
active_workspace = key;
return false;
}
void Screen::hide(ScreenWorkspaceInfo& info)
{
info.tree.hide();
}
void Screen::show(ScreenWorkspaceInfo& info)
{
info.tree.show();
}

57
src/screen.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef MIRACLE_SCREEN_H
#define MIRACLE_SCREEN_H
#include "window_tree.h"
#include <memory>
namespace miracle
{
struct WorkspaceManager;
struct NodeResurrection
{
std::shared_ptr<Node> node;
MirWindowState state;
};
struct ScreenWorkspaceInfo
{
char workspace;
WindowTree 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(
WorkspaceManager& workspace_manager,
geom::Rectangle const& area,
miral::WindowManagerTools const& tools,
WindowTreeOptions const& options);
~Screen() = default;
WindowTree& get_active_tree();
void advise_new_workspace(char workspace);
bool make_workspace_active(char workspace);
std::vector<ScreenWorkspaceInfo>& get_workspaces() { return workspaces; }
private:
WorkspaceManager& workspace_manager;
miral::WindowManagerTools tools;
geom::Rectangle area;
WindowTreeOptions options;
char active_workspace;
std::vector<ScreenWorkspaceInfo> workspaces;
void hide(ScreenWorkspaceInfo&);
void show(ScreenWorkspaceInfo&);
};
}
#endif

View File

@ -655,6 +655,9 @@ bool WindowTree::advise_state_change(const miral::WindowInfo &window_info, MirWi
if (!node)
return false;
if (is_hidden)
return true;
switch (state)
{
case mir_window_state_restored:
@ -705,6 +708,9 @@ bool WindowTree::constrain(miral::WindowInfo &window_info)
if (!node)
return false;
if (is_hidden)
return false;
if (!node->get_parent())
{
std::cerr << "Unable to constrain node without parent\n";
@ -752,4 +758,55 @@ void WindowTree::close_active_window()
{
tools.ask_client_to_close(active_window->get_window());
}
}
void WindowTree::hide()
{
if (is_hidden)
{
mir::log_warning("Tree is already hidden");
return;
}
is_hidden = true;
foreach_node([&](auto node)
{
if (node->is_window())
{
miral::WindowInfo& window_info = tools.info_for(node->get_window());
nodes_to_resurrect.push_back({
node,
window_info.state()
});
}
});
for (auto& node : nodes_to_resurrect)
{
miral::WindowInfo& window_info = tools.info_for(node.node->get_window());
miral::WindowSpecification modifications;
modifications.state() = mir_window_state_hidden;
tools.place_and_size_for_state(modifications, window_info);
tools.modify_window(window_info.window(), modifications);
}
}
void WindowTree::show()
{
if (!is_hidden)
{
mir::log_warning("Tree is already shown");
return;
}
is_hidden = false;
for (auto other_node : nodes_to_resurrect)
{
auto& window_info = tools.info_for(other_node.node->get_window());
miral::WindowSpecification modifications;
modifications.state() = other_node.state;
tools.place_and_size_for_state(modifications, window_info);
tools.modify_window(window_info.window(), modifications);
}
nodes_to_resurrect.clear();
}

View File

@ -102,6 +102,12 @@ public:
void foreach_node(std::function<void(std::shared_ptr<Node>)>);
void close_active_window();
/// Hides the entire tree
void hide();
/// Shows the entire tree
void show();
private:
struct MoveResult
{
@ -114,6 +120,12 @@ private:
std::shared_ptr<Node> node = nullptr;
};
struct NodeResurrection
{
std::shared_ptr<Node> node;
MirWindowState state;
};
miral::WindowManagerTools tools;
WindowTreeOptions options;
std::shared_ptr<Node> root_lane;
@ -122,6 +134,8 @@ private:
bool is_resizing = false;
std::vector<miral::Zone> application_zone_list;
bool is_active_window_fullscreen = false;
bool is_hidden = false;
std::vector<NodeResurrection> nodes_to_resurrect;
std::shared_ptr<Node> _get_active_lane();
void _handle_direction_request(NodeLayoutDirection direction);

275
src/workspace_manager.cpp Normal file
View File

@ -0,0 +1,275 @@
#include "workspace_manager.h"
#include "screen.h"
using namespace mir::geometry;
using namespace miral;
using namespace miracle;
namespace
{
char DEFAULT_WORKSPACES[] = {'1','2','3','4','5','6','7','8','9', '0'};
}
WorkspaceManager::WorkspaceManager(WindowManagerTools const& tools) :
tools_{tools}
{
}
bool WorkspaceManager::request_workspace(std::shared_ptr<Screen> screen, char key)
{
for (auto workspace : workspaces)
{
if (workspace.key == key)
{
workspace.screen->make_workspace_active(key);
return true;
}
}
workspaces.push_back({
key,
screen
});
screen->advise_new_workspace(key);
return true;
}
bool WorkspaceManager::request_first_available_workspace(std::shared_ptr<Screen> screen)
{
for (int i = 0; i < 10; i++)
{
bool can_use = true;
for (auto workspace : workspaces)
{
if (workspace.key == DEFAULT_WORKSPACES[i])
{
can_use = false;
}
}
if (can_use)
return request_workspace(screen, DEFAULT_WORKSPACES[i]);
}
return false;
}
//
//void miracle::WorkspaceManager::jump_to_workspace(bool take_active, int index)
//{
// tools_.invoke_under_lock(
// [this, take_active, index]
// {
// if (active_workspace_index != index)
// {
// auto const old_workspace = active_workspace_index;
// auto const& window = take_active ? tools_.active_window() : Window{};
// auto const& old_active = workspaces[active_workspace_index];
//
// while (workspaces.size() - 1 < index)
// workspaces.push_back(tools_.create_workspace());
//
// auto const& new_active = workspaces[index];
// change_active_workspace(new_active, old_active, window);
// erase_if_empty(std::next(workspaces.begin(), old_workspace));
// active_workspace_index = index;
// }
// });
//}
//
//void miracle::WorkspaceManager::workspace_begin(bool take_active)
//{
// jump_to_workspace(take_active, 0);
//}
//
//void miracle::WorkspaceManager::workspace_end(bool take_active)
//{
// jump_to_workspace(take_active, workspaces.size() - 1);
//}
//
//void miracle::WorkspaceManager::workspace_up(bool take_active)
//{
// if (active_workspace_index != 0)
// jump_to_workspace(take_active, active_workspace_index - 1);
//}
//
//void miracle::WorkspaceManager::workspace_down(bool take_active)
//{
// if (active_workspace_index != workspaces.size() - 1)
// jump_to_workspace(active_workspace_index + 1, take_active);
//}
//
//void miracle::WorkspaceManager::erase_if_empty(workspace_list::iterator const& old_workspace)
//{
// bool empty = true;
// tools_.for_each_window_in_workspace(*old_workspace, [&](auto ww)
// {
// if (is_application(tools_.info_for(ww).depth_layer()))
// empty = false;
// });
// if (empty)
// {
// workspace_to_active.erase(*old_workspace);
// workspaces.erase(old_workspace);
// }
//}
//
//void miracle::WorkspaceManager::apply_workspace_hidden_to(Window const& window)
//{
// auto const& window_info = tools_.info_for(window);
// auto& workspace_info = workspace_info_for(window_info);
// if (!workspace_info.in_hidden_workspace)
// {
// workspace_info.in_hidden_workspace = true;
// workspace_info.old_state = window_info.state();
//
// WindowSpecification modifications;
// modifications.state() = mir_window_state_hidden;
// tools_.place_and_size_for_state(modifications, window_info);
// tools_.modify_window(window_info.window(), modifications);
// }
//}
//
//void miracle::WorkspaceManager::apply_workspace_visible_to(Window const& window)
//{
// auto const& window_info = tools_.info_for(window);
// auto& workspace_info = workspace_info_for(window_info);
// if (workspace_info.in_hidden_workspace)
// {
// workspace_info.in_hidden_workspace = false;
// WindowSpecification modifications;
// modifications.state() = workspace_info.old_state;
// tools_.place_and_size_for_state(modifications, window_info);
// tools_.modify_window(window_info.window(), modifications);
// }
//}
//
//void miracle::WorkspaceManager::change_active_workspace(
// std::shared_ptr<Workspace> const& new_active,
// std::shared_ptr<Workspace> const& old_active,
// Window const& window)
//{
// if (new_active == old_active) return;
//
// auto const old_active_window = tools_.active_window();
// auto const old_active_window_shell = old_active_window &&
// !is_application(tools_.info_for(old_active_window).depth_layer());
//
// if (!old_active_window || old_active_window_shell)
// {
// // If there's no active window, the first shown grabs focus: get the right one
// if (auto const ww = workspace_to_active[new_active])
// {
// tools_.for_each_workspace_containing(ww, [&](std::shared_ptr<Workspace> const& ws)
// {
// if (ws == new_active)
// {
// apply_workspace_visible_to(ww);
// }
// });
//
// // If focus was on a shell window, put it on an app
// if (old_active_window_shell)
// tools_.select_active_window(ww);
// }
// }
//
// tools_.remove_tree_from_workspace(window, old_active);
// tools_.add_tree_to_workspace(window, new_active);
//
// tools_.for_each_window_in_workspace(new_active, [&](Window const& ww)
// {
// if (is_application(tools_.info_for(ww).depth_layer()))
// {
// apply_workspace_visible_to(ww);
// }
// });
//
// bool hide_old_active = false;
// tools_.for_each_window_in_workspace(old_active, [&](Window const& ww)
// {
// if (is_application(tools_.info_for(ww).depth_layer()))
// {
// if (ww == old_active_window)
// {
// // If we hide the active window focus will shift: do that last
// hide_old_active = true;
// return;
// }
//
// apply_workspace_hidden_to(ww);
// return;
// }
// });
//
// if (hide_old_active)
// {
// apply_workspace_hidden_to(old_active_window);
//
// // Remember the old active_window when we switch away
// workspace_to_active[old_active] = old_active_window;
// }
//}
//
//void miracle::WorkspaceManager::advise_adding_to_workspace(std::shared_ptr<Workspace> const& workspace,
// std::vector<Window> const& windows)
//{
// if (windows.empty())
// return;
//
// for (auto const& window : windows)
// {
// if (workspace == workspaces[active_workspace_index])
// {
// apply_workspace_visible_to(window);
// }
// else
// {
// apply_workspace_hidden_to(window);
// }
// }
//}
//
//auto miracle::WorkspaceManager::active_workspace() const -> std::shared_ptr<Workspace>
//{
// return workspaces[active_workspace_index];
//}
//
//bool miracle::WorkspaceManager::is_application(MirDepthLayer layer)
//{
// switch (layer)
// {
// case mir_depth_layer_application:
// case mir_depth_layer_always_on_top:
// return true;
//
// default:;
// return false;
// }
//}
//
//bool miracle::WorkspaceManager::in_hidden_workspace(WindowInfo const& info) const
//{
// auto& workspace_info = workspace_info_for(info);
//
// return workspace_info.in_hidden_workspace;
//}
//
//void miracle::WorkspaceManager::advise_new_window(WindowInfo const& window_info)
//{
// if (auto const& parent = window_info.parent())
// {
// if (workspace_info_for(tools_.info_for(parent)).in_hidden_workspace)
// apply_workspace_hidden_to(window_info.window());
// }
// else
// {
// tools_.add_tree_to_workspace(window_info.window(), active_workspace());
// }
//}
//
//auto miracle::WorkspaceManager::make_workspace_info() -> std::shared_ptr<WorkspaceInfo>
//{
// return std::make_shared<WorkspaceInfo>();
//}

90
src/workspace_manager.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef WORKSPACE_MANAGER_H
#define WORKSPACE_MANAGER_H
#include <memory>
#include <vector>
#include <miral/window_manager_tools.h>
#include <list>
#include <map>
namespace miracle
{
using miral::Window;
using miral::WindowInfo;
using miral::WindowManagerTools;
using miral::WindowSpecification;
using miral::Workspace;
class Screen;
// TODO:
// As it stands, this isn't exactly what I want. What I do want
// is a workspace manager that is output-aware. The data structure
// should be a global WorkspaceManager who holds a mapping of Screens -> Workspaces.
// Workspaces should have a name and be organized according to that name. This
// goes to mean that we should ignore the idea of a vector of workspaces, and settle
// for char-encoded workspaces instead.
struct WorkspaceInfo
{
char key;
std::shared_ptr<Screen> screen;
};
class WorkspaceManager
{
public:
explicit WorkspaceManager(WindowManagerTools const& tools);
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.
bool request_workspace(std::shared_ptr<Screen> screen, char workspace);
bool request_first_available_workspace(std::shared_ptr<Screen> screen);
// void workspace_begin(bool take_active);
//
// void workspace_end(bool take_active);
//
// void workspace_up(bool take_active);
//
// void workspace_down(bool take_active);
//
// void jump_to_workspace(bool take_active, int index);
//
// void apply_workspace_hidden_to(Window const& window);
//
// void apply_workspace_visible_to(Window const& window);
//
// void change_active_workspace(
// std::shared_ptr<Workspace> const& ww,
// std::shared_ptr<Workspace> const& old_active,
// miral::Window const& window);
//
// void advise_new_window(const WindowInfo &window_info);
//
// void advise_adding_to_workspace(
// std::shared_ptr<Workspace> const& workspace,
// std::vector<Window> const& windows);
//
// auto active_workspace() const -> std::shared_ptr<Workspace>;
//
// bool in_hidden_workspace(WindowInfo const& info) const;
//
// static bool is_application(MirDepthLayer layer);
private:
WindowManagerTools tools_;
std::vector<WorkspaceInfo> workspaces;
void erase_if_empty(std::vector<std::shared_ptr<Workspace>>::iterator const& old_workspace);
};
}
#endif