mirror of
https://github.com/miracle-wm-org/miracle-wm.git
synced 2025-01-07 19:13:50 +03:00
feature: ipc commands will not return errors with useful messages upon failure (#317)
- ipc commands will not return errors with useful messages upon failure - created a CommandController interface so that the executor will be easier to test later on
This commit is contained in:
parent
e879c66ece
commit
f8554ecb58
@ -77,6 +77,7 @@ add_library(miracle-wm-implementation
|
||||
src/scratchpad.h src/scratchpad.cpp
|
||||
src/compositor_state.h src/compositor_state.cpp
|
||||
src/math_helpers.h
|
||||
src/command_controller.h
|
||||
)
|
||||
|
||||
add_executable(miracle-wm
|
||||
|
86
src/command_controller.h
Normal file
86
src/command_controller.h
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
Copyright (C) 2024 Matthew Kosarek
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
#ifndef MIRACLE_WM_COMMAND_CONTROLLER_H
|
||||
#define MIRACLE_WM_COMMAND_CONTROLLER_H
|
||||
|
||||
#include "direction.h"
|
||||
#include "output.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace miracle
|
||||
{
|
||||
|
||||
/// Abstract interface used to send commands to miracle.
|
||||
class CommandController
|
||||
{
|
||||
public:
|
||||
virtual bool try_request_horizontal() = 0;
|
||||
virtual bool try_request_vertical() = 0;
|
||||
virtual bool try_toggle_layout(bool cycle_through_all) = 0;
|
||||
virtual void try_toggle_resize_mode() = 0;
|
||||
virtual bool try_resize(Direction direction, int pixels) = 0;
|
||||
virtual bool try_set_size(std::optional<int> const& width, std::optional<int> const& height) = 0;
|
||||
virtual bool try_move(Direction direction) = 0;
|
||||
virtual bool try_move_by(Direction direction, int pixels) = 0;
|
||||
virtual bool try_move_to(int x, int y) = 0;
|
||||
virtual bool try_select(Direction direction) = 0;
|
||||
virtual bool try_select_parent() = 0;
|
||||
virtual bool try_select_child() = 0;
|
||||
virtual bool try_select_floating() = 0;
|
||||
virtual bool try_select_tiling() = 0;
|
||||
virtual bool try_select_toggle() = 0;
|
||||
virtual bool try_close_window() = 0;
|
||||
virtual bool quit() = 0;
|
||||
virtual bool try_toggle_fullscreen() = 0;
|
||||
virtual bool select_workspace(int number, bool back_and_forth = true) = 0;
|
||||
virtual bool select_workspace(std::string const& name, bool back_and_forth) = 0;
|
||||
virtual bool next_workspace() = 0;
|
||||
virtual bool prev_workspace() = 0;
|
||||
virtual bool back_and_forth_workspace() = 0;
|
||||
virtual bool next_workspace_on_output(Output const&) = 0;
|
||||
virtual bool prev_workspace_on_output(Output const&) = 0;
|
||||
virtual bool move_active_to_workspace(int number, bool back_and_forth = true) = 0;
|
||||
virtual bool move_active_to_workspace_named(std::string const&, bool back_and_forth) = 0;
|
||||
virtual bool move_active_to_next_workspace() = 0;
|
||||
virtual bool move_active_to_prev_workspace() = 0;
|
||||
virtual bool move_active_to_back_and_forth() = 0;
|
||||
virtual bool move_to_scratchpad() = 0;
|
||||
virtual bool show_scratchpad() = 0;
|
||||
virtual bool toggle_floating() = 0;
|
||||
virtual bool toggle_pinned_to_workspace() = 0;
|
||||
virtual bool set_is_pinned(bool) = 0;
|
||||
virtual bool toggle_tabbing() = 0;
|
||||
virtual bool toggle_stacking() = 0;
|
||||
virtual bool set_layout(LayoutScheme scheme) = 0;
|
||||
virtual bool set_layout_default() = 0;
|
||||
virtual void move_cursor_to_output(Output const&) = 0;
|
||||
virtual bool try_select_next_output() = 0;
|
||||
virtual bool try_select_prev_output() = 0;
|
||||
virtual bool try_select_output(Direction direction) = 0;
|
||||
virtual bool try_select_output(std::vector<std::string> const& names) = 0;
|
||||
virtual bool try_move_active_to_output(Direction direction) = 0;
|
||||
virtual bool try_move_active_to_current() = 0;
|
||||
virtual bool try_move_active_to_primary() = 0;
|
||||
virtual bool try_move_active_to_nonprimary() = 0;
|
||||
virtual bool try_move_active_to_next() = 0;
|
||||
virtual bool try_move_active(std::vector<std::string> const& names) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MIRACLE_WM_COMMAND_CONTROLLER_H
|
@ -51,6 +51,7 @@ public:
|
||||
mir::geometry::Point cursor_position;
|
||||
uint32_t modifiers = 0;
|
||||
bool has_clicked_floating_window = false;
|
||||
std::vector<std::shared_ptr<Output>> output_list;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Container> active() const;
|
||||
|
||||
|
37
src/ipc.cpp
37
src/ipc.cpp
@ -225,12 +225,10 @@ json mode_event_to_json(WindowManagerMode mode)
|
||||
Ipc::Ipc(miral::MirRunner& runner,
|
||||
miracle::WorkspaceManager& workspace_manager,
|
||||
Policy& policy,
|
||||
std::shared_ptr<mir::ServerActionQueue> const& queue,
|
||||
IpcCommandExecutor& executor,
|
||||
std::shared_ptr<Config> const& config) :
|
||||
workspace_manager { workspace_manager },
|
||||
policy { policy },
|
||||
queue { queue },
|
||||
executor { executor },
|
||||
config { config }
|
||||
{
|
||||
@ -528,14 +526,20 @@ void Ipc::handle_command(miracle::Ipc::IpcClient& client, uint32_t payload_lengt
|
||||
{
|
||||
mir::log_debug("Processing i3_command: %s", buf);
|
||||
auto result = parse_i3_command(buf);
|
||||
if (result)
|
||||
if (result.success)
|
||||
{
|
||||
const std::string msg = "[{\"success\": true}]";
|
||||
send_reply(client, payload_type, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string msg = "[{\"success\": false, \"parse_error\": true}]";
|
||||
json j = json::array();
|
||||
j.push_back({
|
||||
{ "success", false },
|
||||
{ "parse_error", result.parse_error },
|
||||
{ "error", result.error },
|
||||
});
|
||||
const std::string msg = to_string(j);
|
||||
send_reply(client, payload_type, msg);
|
||||
}
|
||||
break;
|
||||
@ -759,26 +763,9 @@ void Ipc::handle_writeable(miracle::Ipc::IpcClient& client)
|
||||
client.write_buffer_len = 0;
|
||||
}
|
||||
|
||||
bool Ipc::parse_i3_command(const char* command)
|
||||
IpcValidationResult Ipc::parse_i3_command(const char* command)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(pending_commands_mutex);
|
||||
IpcCommandParser parser(command);
|
||||
pending_commands.push_back(parser.parse());
|
||||
}
|
||||
|
||||
queue->enqueue(this, [&]()
|
||||
{
|
||||
size_t num_processed = 0;
|
||||
{
|
||||
std::shared_lock lock(pending_commands_mutex);
|
||||
for (auto const& c : pending_commands)
|
||||
executor.process(c);
|
||||
num_processed = pending_commands.size();
|
||||
}
|
||||
|
||||
std::unique_lock lock(pending_commands_mutex);
|
||||
pending_commands.erase(pending_commands.begin(), pending_commands.begin() + num_processed);
|
||||
});
|
||||
return true;
|
||||
IpcCommandParser parser(command);
|
||||
auto const pending_commands = parser.parse();
|
||||
return executor.process(pending_commands);
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "workspace_manager.h"
|
||||
#include "workspace_observer.h"
|
||||
#include <mir/fd.h>
|
||||
#include <mir/server_action_queue.h>
|
||||
#include <miral/runner.h>
|
||||
#include <shared_mutex>
|
||||
#include <vector>
|
||||
@ -84,7 +83,6 @@ public:
|
||||
Ipc(miral::MirRunner& runner,
|
||||
WorkspaceManager&,
|
||||
Policy& policy,
|
||||
std::shared_ptr<mir::ServerActionQueue> const&,
|
||||
IpcCommandExecutor&,
|
||||
std::shared_ptr<Config> const&);
|
||||
~Ipc();
|
||||
@ -113,9 +111,6 @@ private:
|
||||
std::unique_ptr<miral::FdHandle> socket_handle;
|
||||
sockaddr_un* ipc_sockaddr = nullptr;
|
||||
std::vector<IpcClient> clients;
|
||||
std::vector<IpcParseResult> pending_commands;
|
||||
mutable std::shared_mutex pending_commands_mutex;
|
||||
std::shared_ptr<mir::ServerActionQueue> queue;
|
||||
IpcCommandExecutor& executor;
|
||||
std::shared_ptr<Config> config;
|
||||
|
||||
@ -124,7 +119,7 @@ private:
|
||||
void handle_command(IpcClient& client, uint32_t payload_length, IpcType payload_type);
|
||||
void send_reply(IpcClient& client, IpcType command_type, std::string const& payload);
|
||||
void handle_writeable(IpcClient& client);
|
||||
bool parse_i3_command(const char* command);
|
||||
IpcValidationResult parse_i3_command(const char* command);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "window_helpers.h"
|
||||
|
||||
#define MIR_LOG_COMPONENT "miracle"
|
||||
#include <format>
|
||||
#include <mir/log.h>
|
||||
#include <miral/application_info.h>
|
||||
|
||||
@ -100,7 +101,7 @@ protected:
|
||||
}
|
||||
|
||||
IpcCommandExecutor::IpcCommandExecutor(
|
||||
miracle::Policy& policy,
|
||||
CommandController& policy,
|
||||
WorkspaceManager& workspace_manager,
|
||||
CompositorState const& state,
|
||||
AutoRestartingLauncher& launcher,
|
||||
@ -113,49 +114,57 @@ IpcCommandExecutor::IpcCommandExecutor(
|
||||
{
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process(miracle::IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process(miracle::IpcParseResult const& command_list)
|
||||
{
|
||||
IpcValidationResult result;
|
||||
for (auto const& command : command_list.commands)
|
||||
{
|
||||
switch (command.type)
|
||||
{
|
||||
case IpcCommandType::exec:
|
||||
process_exec(command, command_list);
|
||||
result = process_exec(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::split:
|
||||
process_split(command, command_list);
|
||||
result = process_split(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::focus:
|
||||
process_focus(command, command_list);
|
||||
result = process_focus(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::move:
|
||||
process_move(command, command_list);
|
||||
result = process_move(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::sticky:
|
||||
process_sticky(command, command_list);
|
||||
result = process_sticky(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::exit:
|
||||
policy.quit();
|
||||
result = {};
|
||||
break;
|
||||
case IpcCommandType::input:
|
||||
process_input(command, command_list);
|
||||
result = process_input(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::workspace:
|
||||
process_workspace(command, command_list);
|
||||
result = process_workspace(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::layout:
|
||||
process_layout(command, command_list);
|
||||
result = process_layout(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::scratchpad:
|
||||
process_scratchpad(command, command_list);
|
||||
result = process_scratchpad(command, command_list);
|
||||
break;
|
||||
case IpcCommandType::resize:
|
||||
process_resize(command, command_list);
|
||||
result = process_resize(command, command_list);
|
||||
break;
|
||||
default:
|
||||
result = parse_error(std::format("Unsupported command type: %d", (int)command.type));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!result.success)
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
miral::Window IpcCommandExecutor::get_window_meeting_criteria(IpcParseResult const& command_list)
|
||||
@ -176,23 +185,27 @@ miral::Window IpcCommandExecutor::get_window_meeting_criteria(IpcParseResult con
|
||||
return miral::Window {};
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_exec(miracle::IpcCommand const& command, miracle::IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::parse_error(std::string error)
|
||||
{
|
||||
mir::log_error("Parse Error: %s", error.c_str());
|
||||
return {
|
||||
.success = false,
|
||||
.parse_error = true,
|
||||
.error = std::move(error)
|
||||
};
|
||||
}
|
||||
|
||||
IpcValidationResult IpcCommandExecutor::process_exec(miracle::IpcCommand const& command, miracle::IpcParseResult const& command_list)
|
||||
{
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_warning("process_exec: no arguments were supplied");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_exec: no arguments were supplied");
|
||||
|
||||
bool no_startup_id = false;
|
||||
if (!command.options.empty() && command.options[0] == "--no-startup-id")
|
||||
no_startup_id = true;
|
||||
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_warning("process_exec: argument does not have a command to run");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_exec: argument does not have a command to run");
|
||||
|
||||
std::string exec_cmd;
|
||||
for (auto const& arg : command.arguments)
|
||||
@ -202,15 +215,13 @@ void IpcCommandExecutor::process_exec(miracle::IpcCommand const& command, miracl
|
||||
|
||||
StartupApp app { exec_cmd, false, no_startup_id };
|
||||
launcher.launch(app);
|
||||
return {};
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_split(miracle::IpcCommand const& command, miracle::IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_split(miracle::IpcCommand const& command, miracle::IpcParseResult const& command_list)
|
||||
{
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_warning("process_split: no arguments were supplied");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_split: no arguments were supplied");
|
||||
|
||||
if (command.arguments.front() == "vertical")
|
||||
{
|
||||
@ -226,27 +237,27 @@ void IpcCommandExecutor::process_split(miracle::IpcCommand const& command, mirac
|
||||
}
|
||||
else
|
||||
{
|
||||
mir::log_warning("process_split: unknown argument %s", command.arguments.front().c_str());
|
||||
return;
|
||||
return parse_error(std::format("process_split: unknown argument {}", command.arguments.front().c_str()));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_focus(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_focus(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
// https://i3wm.org/docs/userguide.html#_focusing_moving_containers
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
if (command_list.scope.empty())
|
||||
{
|
||||
mir::log_warning("Focus command expected scope but none was provided");
|
||||
return;
|
||||
return parse_error("Focus command expected scope but none was provided");
|
||||
}
|
||||
|
||||
auto window = get_window_meeting_criteria(command_list);
|
||||
if (window)
|
||||
window_controller.select_active_window(window);
|
||||
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const& arg = command.arguments.front();
|
||||
@ -254,8 +265,7 @@ void IpcCommandExecutor::process_focus(IpcCommand const& command, IpcParseResult
|
||||
{
|
||||
if (command_list.scope.empty())
|
||||
{
|
||||
mir::log_warning("Focus 'workspace' command expected scope but none was provided");
|
||||
return;
|
||||
return parse_error("Focus 'workspace' command expected scope but none was provided");
|
||||
}
|
||||
|
||||
auto window = get_window_meeting_criteria(command_list);
|
||||
@ -279,13 +289,10 @@ void IpcCommandExecutor::process_focus(IpcCommand const& command, IpcParseResult
|
||||
{
|
||||
auto container = state.active();
|
||||
if (!container)
|
||||
return;
|
||||
return parse_error("Active container does nto exist");
|
||||
|
||||
if (container->get_type() != ContainerType::leaf)
|
||||
{
|
||||
mir::log_warning("Cannot focus prev when a tiling window is not selected");
|
||||
return;
|
||||
}
|
||||
return parse_error("Cannot focus prev when a tiling window is not selected");
|
||||
|
||||
if (auto parent = Container::as_parent(container->get_parent().lock()))
|
||||
{
|
||||
@ -301,13 +308,10 @@ void IpcCommandExecutor::process_focus(IpcCommand const& command, IpcParseResult
|
||||
{
|
||||
auto container = state.active();
|
||||
if (!container)
|
||||
return;
|
||||
return parse_error("No container is selected");
|
||||
|
||||
if (container->get_type() != ContainerType::leaf)
|
||||
{
|
||||
mir::log_warning("Cannot focus prev when a tiling window is not selected");
|
||||
return;
|
||||
}
|
||||
return parse_error("Cannot focus prev when a tiling window is not selected");
|
||||
|
||||
if (auto parent = Container::as_parent(container->get_parent().lock()))
|
||||
{
|
||||
@ -328,10 +332,7 @@ void IpcCommandExecutor::process_focus(IpcCommand const& command, IpcParseResult
|
||||
else if (arg == "output")
|
||||
{
|
||||
if (command.arguments.size() < 2)
|
||||
{
|
||||
mir::log_error("process_focus: 'focus output' must have more than two arguments");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_focus: 'focus output' must have more than two arguments");
|
||||
|
||||
auto const& arg1 = command.arguments[1];
|
||||
if (arg1 == "next")
|
||||
@ -352,6 +353,8 @@ void IpcCommandExecutor::process_focus(IpcCommand const& command, IpcParseResult
|
||||
policy.try_select_output(names);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
namespace
|
||||
@ -385,21 +388,15 @@ bool parse_move_distance(std::vector<std::string> const& arguments, int& index,
|
||||
}
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
auto active_output = policy.get_active_output();
|
||||
auto const& active_output = state.active_output;
|
||||
if (!active_output)
|
||||
{
|
||||
mir::log_warning("process_move: output is not set");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: output is not set");
|
||||
|
||||
// https://i3wm.org/docs/userguide.html#_focusing_moving_containers
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_warning("process_move: move command expects arguments");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: move command expects arguments");
|
||||
|
||||
int index = 0;
|
||||
auto const& arg0 = command.arguments[index++];
|
||||
@ -428,15 +425,12 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
else if (arg0 == "position")
|
||||
{
|
||||
if (command.arguments.size() < 2)
|
||||
{
|
||||
mir::log_error("process_move: move position expected a third argument");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: move position expected a third argument");
|
||||
|
||||
auto const& arg1 = command.arguments[index++];
|
||||
if (arg1 == "center")
|
||||
{
|
||||
auto active = policy.get_state().active().get();
|
||||
auto active = state.active().get();
|
||||
auto area = active_output->get_area();
|
||||
float x = (float)area.size.width.as_int() / 2.f - (float)active->get_visible_area().size.width.as_int() / 2.f;
|
||||
float y = (float)area.size.height.as_int() / 2.f - (float)active->get_visible_area().size.height.as_int() / 2.f;
|
||||
@ -444,7 +438,7 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
}
|
||||
else if (arg1 == "mouse")
|
||||
{
|
||||
auto const& position = policy.get_cursor_position();
|
||||
auto const& position = state.cursor_position;
|
||||
policy.try_move_to((int)position.x.as_int(), (int)position.y.as_int());
|
||||
}
|
||||
else
|
||||
@ -453,39 +447,28 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
int move_distance_y;
|
||||
|
||||
if (!parse_move_distance(command.arguments, index, total_size, move_distance_x))
|
||||
{
|
||||
mir::log_error("process_move: move position <x> <y>: unable to parse x");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: move position <x> <y>: unable to parse x");
|
||||
|
||||
if (!parse_move_distance(command.arguments, index, total_size, move_distance_y))
|
||||
{
|
||||
mir::log_error("process_move: move position <x> <y>: unable to parse y");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: move position <x> <y>: unable to parse y");
|
||||
|
||||
policy.try_move_to(move_distance_x, move_distance_y);
|
||||
}
|
||||
return;
|
||||
|
||||
return {};
|
||||
}
|
||||
else if (arg0 == "absolute")
|
||||
{
|
||||
auto const& arg1 = command.arguments[index++];
|
||||
auto const& arg2 = command.arguments[index++];
|
||||
if (arg1 != "position")
|
||||
{
|
||||
mir::log_error("process_move: move [absolute] ... expected 'position' as the third argument");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: move [absolute] ... expected 'position' as the third argument");
|
||||
|
||||
if (arg2 != "center")
|
||||
{
|
||||
mir::log_error("process_move: move absolute position ... expected 'center' as the third argument");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: move absolute position ... expected 'center' as the third argument");
|
||||
|
||||
float x = 0, y = 0;
|
||||
for (auto const& output : policy.get_output_list())
|
||||
for (auto const& output : state.output_list)
|
||||
{
|
||||
auto area = output->get_area();
|
||||
float end_x = (float)area.size.width.as_int() + (float)area.top_left.x.as_int();
|
||||
@ -496,30 +479,24 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
y = end_y;
|
||||
}
|
||||
|
||||
auto active = policy.get_state().active();
|
||||
auto active = state.active();
|
||||
float x_pos = x / 2.f - (float)active->get_visible_area().size.width.as_int() / 2.f;
|
||||
float y_pos = y / 2.f - (float)active->get_visible_area().size.height.as_int() / 2.f;
|
||||
policy.try_move_to((int)x_pos, (int)y_pos);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
else if (arg0 == "window" || arg0 == "container")
|
||||
{
|
||||
auto const back_and_forth = std::find(command.options.begin(), command.options.end(), "--no-auto-back-and-forth") == command.options.end();
|
||||
auto const& arg1 = command.arguments[index++];
|
||||
if (arg1 != "to")
|
||||
{
|
||||
mir::log_error("process_move: expected 'to' after 'move window/container ...'");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: expected 'to' after 'move window/container ...'");
|
||||
|
||||
auto const& arg2 = command.arguments[index++];
|
||||
if (arg2 == "workspace")
|
||||
{
|
||||
if (command.arguments.size() <= 3)
|
||||
{
|
||||
mir::log_error("process_move: expected another argument after 'move container/window to output...'");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_move: expected another argument after 'move container/window to output...'");
|
||||
|
||||
auto const& arg3 = command.arguments[index++];
|
||||
int number = -1;
|
||||
@ -527,17 +504,17 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
{
|
||||
// TODO: Do we need to care about the name here?
|
||||
policy.move_active_to_workspace(number, back_and_forth);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
else if (arg3 == "next")
|
||||
{
|
||||
policy.move_active_to_next_workspace();
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
else if (arg3 == "prev")
|
||||
{
|
||||
policy.move_active_to_prev_workspace();
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
else if (arg3 == "current")
|
||||
{
|
||||
@ -546,12 +523,12 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
else if (arg3 == "back_and_forth")
|
||||
{
|
||||
policy.move_active_to_back_and_forth();
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
policy.move_active_to_workspace_named(arg3, back_and_forth);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else if (arg2 == "output")
|
||||
@ -559,7 +536,7 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
if (command.arguments.size() <= 3)
|
||||
{
|
||||
mir::log_error("process_move: expected another argument after 'move container/window to output...'");
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const& arg3 = command.arguments[index++];
|
||||
@ -589,7 +566,7 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
else if (arg0 == "scratchpad")
|
||||
{
|
||||
policy.move_to_scratchpad();
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (direction < Direction::MAX)
|
||||
@ -600,15 +577,14 @@ void IpcCommandExecutor::process_move(IpcCommand const& command, IpcParseResult
|
||||
else
|
||||
policy.try_move(direction);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_sticky(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_sticky(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_warning("process_sticky: expects arguments");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_sticky: expects arguments");
|
||||
|
||||
auto const& arg0 = command.arguments[0];
|
||||
if (arg0 == "enable")
|
||||
@ -619,10 +595,11 @@ void IpcCommandExecutor::process_sticky(IpcCommand const& command, IpcParseResul
|
||||
policy.toggle_pinned_to_workspace();
|
||||
else
|
||||
mir::log_warning("process_sticky: unknown arguments: %s", arg0.c_str());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// This command will be
|
||||
void IpcCommandExecutor::process_input(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_input(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
// Payloads appear in the following format:
|
||||
// [type:X, xkb_Y, Z]
|
||||
@ -630,19 +607,13 @@ void IpcCommandExecutor::process_input(IpcCommand const& command, IpcParseResult
|
||||
// and Z is the value of that variable. Z may not be included at all, in which
|
||||
// case the variable is set to the default.
|
||||
if (command.arguments.size() < 2)
|
||||
{
|
||||
mir::log_warning("process_input: expects at least 2 arguments");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_input: expects at least 2 arguments");
|
||||
|
||||
const char* const TYPE_PREFIX = "type:";
|
||||
const size_t TYPE_PREFIX_LEN = strlen(TYPE_PREFIX);
|
||||
std::string_view type_str = command.arguments[0];
|
||||
if (!type_str.starts_with("type:"))
|
||||
{
|
||||
mir::log_warning("process_input: 'type' string is misformatted: %s", command.arguments[0].c_str());
|
||||
return;
|
||||
}
|
||||
return parse_error(std::format("process_input: 'type' string is misformatted: {}", command.arguments[0].c_str()));
|
||||
|
||||
std::string_view type = type_str.substr(TYPE_PREFIX_LEN);
|
||||
assert(type == "keyboard");
|
||||
@ -651,10 +622,7 @@ void IpcCommandExecutor::process_input(IpcCommand const& command, IpcParseResult
|
||||
const char* const XKB_PREFIX = "xkb_";
|
||||
const size_t XKB_PREFIX_LEN = strlen(XKB_PREFIX);
|
||||
if (!xkb_str.starts_with(XKB_PREFIX))
|
||||
{
|
||||
mir::log_warning("process_input: 'xkb' string is misformatted: %s", command.arguments[1].c_str());
|
||||
return;
|
||||
}
|
||||
return parse_error(std::format("process_input: 'xkb' string is misformatted: {}", command.arguments[1].c_str()));
|
||||
|
||||
std::string_view xkb_variable_name = xkb_str.substr(XKB_PREFIX_LEN);
|
||||
assert(xkb_variable_name == "model"
|
||||
@ -674,18 +642,16 @@ void IpcCommandExecutor::process_input(IpcCommand const& command, IpcParseResult
|
||||
}
|
||||
else
|
||||
{
|
||||
mir::log_warning("process_input: > 3 arguments were provided but only <= 3 are expected");
|
||||
return;
|
||||
return parse_error("process_input: > 3 arguments were provided but only <= 3 are expected");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_workspace(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_workspace(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_error("process_workspace: no arguments provided");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_workspace: no arguments provided");
|
||||
|
||||
std::string const& arg0 = command.arguments[0];
|
||||
if (arg0 == "next")
|
||||
@ -694,14 +660,14 @@ void IpcCommandExecutor::process_workspace(IpcCommand const& command, IpcParseRe
|
||||
policy.prev_workspace();
|
||||
else if (arg0 == "next_on_output")
|
||||
{
|
||||
if (auto const* output = policy.get_active_output())
|
||||
if (auto const& output = state.active_output)
|
||||
policy.next_workspace_on_output(*output);
|
||||
else
|
||||
mir::log_error("process_workspace: next_on_output has no output to go next on");
|
||||
}
|
||||
else if (arg0 == "prev_on_output")
|
||||
{
|
||||
if (auto const* output = policy.get_active_output())
|
||||
if (auto const& output = state.active_output)
|
||||
policy.prev_workspace_on_output(*output);
|
||||
else
|
||||
mir::log_error("process_workspace: prev_on_output has no output to go prev on");
|
||||
@ -722,7 +688,7 @@ void IpcCommandExecutor::process_workspace(IpcCommand const& command, IpcParseRe
|
||||
if (command.arguments.size() < 3)
|
||||
{
|
||||
policy.select_workspace(number, back_and_forth);
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
// We have "workspace number <name>"
|
||||
@ -735,9 +701,11 @@ void IpcCommandExecutor::process_workspace(IpcCommand const& command, IpcParseRe
|
||||
policy.select_workspace(*arg1, back_and_forth);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_layout(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_layout(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
// https://i3wm.org/docs/userguide.html#manipulating_layout
|
||||
std::string const& arg0 = command.arguments[0];
|
||||
@ -754,10 +722,7 @@ void IpcCommandExecutor::process_layout(IpcCommand const& command, IpcParseResul
|
||||
else if (arg0 == "toggle")
|
||||
{
|
||||
if (command.arguments.size() == 1)
|
||||
{
|
||||
mir::log_error("process_layout: expected argument after 'layout toggle ...'");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_layout: expected argument after 'layout toggle ...'");
|
||||
|
||||
if (command.arguments.size() == 2)
|
||||
{
|
||||
@ -767,18 +732,15 @@ void IpcCommandExecutor::process_layout(IpcCommand const& command, IpcParseResul
|
||||
else if (arg1 == "all")
|
||||
policy.try_toggle_layout(true);
|
||||
else
|
||||
mir::log_error("process_layout: expected split/all after 'layout toggle X'");
|
||||
return parse_error("process_layout: expected split/all after 'layout toggle X'");
|
||||
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
else
|
||||
{
|
||||
auto container = policy.get_state().active();
|
||||
auto container = state.active();
|
||||
if (!container)
|
||||
{
|
||||
mir::log_error("process_layout: container unavailable");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_layout: container unavailable");
|
||||
|
||||
auto current_type = container->get_layout();
|
||||
size_t index = 0;
|
||||
@ -844,24 +806,21 @@ void IpcCommandExecutor::process_layout(IpcCommand const& command, IpcParseResul
|
||||
policy.set_layout(LayoutScheme::horizontal);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_scratchpad(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_scratchpad(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_error("process_scratchpad: no arguments provided");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_scratchpad: no arguments provided");
|
||||
|
||||
std::string const& arg0 = command.arguments[0];
|
||||
if (arg0 != "show")
|
||||
{
|
||||
mir::log_error("process_scratchpad: all scratchpad commands must be 'scratchpad show'");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_scratchpad: all scratchpad commands must be 'scratchpad show'");
|
||||
|
||||
policy.show_scratchpad();
|
||||
return {};
|
||||
}
|
||||
|
||||
namespace
|
||||
@ -869,6 +828,7 @@ namespace
|
||||
struct ResizeAdjust
|
||||
{
|
||||
bool success = true;
|
||||
std::string error;
|
||||
Direction direction = Direction::MAX;
|
||||
int first = 0;
|
||||
int second = 0;
|
||||
@ -877,14 +837,11 @@ struct ResizeAdjust
|
||||
ResizeAdjust parse_resize(CompositorState const& state, ArgumentsIndexer& indexer, int multiplier)
|
||||
{
|
||||
if (!indexer.next())
|
||||
{
|
||||
mir::log_error("process_resize: expected argument after 'resize grow'");
|
||||
return { false };
|
||||
}
|
||||
return { .success = false, .error = "process_resize: expected argument after 'resize grow'" };
|
||||
|
||||
auto const& container = state.active();
|
||||
if (!container)
|
||||
return { .success = false };
|
||||
return { .success = false, .error = "No container is selcted" };
|
||||
|
||||
ResizeAdjust result;
|
||||
if (indexer.current() == "width" || indexer.current() == "horizontal")
|
||||
@ -913,9 +870,7 @@ ResizeAdjust parse_resize(CompositorState const& state, ArgumentsIndexer& indexe
|
||||
}
|
||||
else
|
||||
{
|
||||
mir::log_error("Unknown direction value: %s", indexer.current().c_str());
|
||||
result.success = false;
|
||||
return result;
|
||||
return { .success = false, .error = std::format("Unknown direction value: {}", indexer.current().c_str()) };
|
||||
}
|
||||
|
||||
int available_space = 0;
|
||||
@ -932,19 +887,12 @@ ResizeAdjust parse_resize(CompositorState const& state, ArgumentsIndexer& indexe
|
||||
|
||||
int first = 0;
|
||||
if (!indexer.parse_move_distance(available_space, first))
|
||||
{
|
||||
result.success = false;
|
||||
return result;
|
||||
}
|
||||
return { .success = false, .error = "cannot parse the first value" };
|
||||
|
||||
if (indexer.next())
|
||||
{
|
||||
if (indexer.current() != "or")
|
||||
{
|
||||
mir::log_error("parse_resize: expected 'or'");
|
||||
result.success = false;
|
||||
return result;
|
||||
}
|
||||
return { .success = false, .error = "expected 'or' after first value" };
|
||||
}
|
||||
|
||||
int second = 0;
|
||||
@ -957,6 +905,7 @@ ResizeAdjust parse_resize(CompositorState const& state, ArgumentsIndexer& indexe
|
||||
struct SetResizeResult
|
||||
{
|
||||
bool success = true;
|
||||
std::string error;
|
||||
std::optional<int> width;
|
||||
std::optional<int> height;
|
||||
};
|
||||
@ -965,21 +914,15 @@ SetResizeResult parse_set_resize(CompositorState const& state, ArgumentsIndexer&
|
||||
{
|
||||
auto const& container = state.active();
|
||||
if (!container)
|
||||
return { .success = false };
|
||||
return { .success = false, .error = "Container is not selected" };
|
||||
|
||||
SetResizeResult result;
|
||||
int width = 0, height = 0;
|
||||
if (!indexer.parse_move_distance(container->get_output()->get_area().size.width.as_value(), width))
|
||||
{
|
||||
mir::log_error("parse_set_resize: invalid width");
|
||||
return { .success = false };
|
||||
}
|
||||
return { .success = false, .error = "invalid width" };
|
||||
|
||||
if (!indexer.parse_move_distance(container->get_output()->get_area().size.height.as_value(), height))
|
||||
{
|
||||
mir::log_error("parse_set_resize: invalid height");
|
||||
return { .success = false };
|
||||
}
|
||||
return { .success = false, .error = "invalid height" };
|
||||
|
||||
if (width != 0)
|
||||
result.width = width;
|
||||
@ -990,13 +933,10 @@ SetResizeResult parse_set_resize(CompositorState const& state, ArgumentsIndexer&
|
||||
}
|
||||
}
|
||||
|
||||
void IpcCommandExecutor::process_resize(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
IpcValidationResult IpcCommandExecutor::process_resize(IpcCommand const& command, IpcParseResult const& command_list)
|
||||
{
|
||||
if (command.arguments.empty())
|
||||
{
|
||||
mir::log_error("process_resize: no arguments provided");
|
||||
return;
|
||||
}
|
||||
return parse_error("process_resize: no arguments provided");
|
||||
|
||||
ArgumentsIndexer indexer(command);
|
||||
auto const& arg0 = indexer.current();
|
||||
@ -1004,7 +944,7 @@ void IpcCommandExecutor::process_resize(IpcCommand const& command, IpcParseResul
|
||||
{
|
||||
auto adjust = parse_resize(state, indexer, 1);
|
||||
if (!adjust.success)
|
||||
return;
|
||||
return parse_error(adjust.error);
|
||||
|
||||
policy.try_resize(adjust.direction, adjust.first);
|
||||
}
|
||||
@ -1012,7 +952,7 @@ void IpcCommandExecutor::process_resize(IpcCommand const& command, IpcParseResul
|
||||
{
|
||||
auto adjust = parse_resize(state, indexer, -1);
|
||||
if (!adjust.success)
|
||||
return;
|
||||
return parse_error(adjust.error);
|
||||
|
||||
policy.try_resize(adjust.direction, adjust.first);
|
||||
}
|
||||
@ -1020,13 +960,12 @@ void IpcCommandExecutor::process_resize(IpcCommand const& command, IpcParseResul
|
||||
{
|
||||
auto result = parse_set_resize(state, indexer);
|
||||
if (!result.success)
|
||||
return;
|
||||
return parse_error(result.error);
|
||||
|
||||
policy.try_set_size(result.width, result.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
mir::log_error("process_resize: unexpected argument: %s", arg0.c_str());
|
||||
return;
|
||||
}
|
||||
return parse_error(std::format("process_resize: unexpected argument: {}", arg0.c_str()));
|
||||
|
||||
return {};
|
||||
}
|
@ -25,42 +25,51 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
namespace miracle
|
||||
{
|
||||
|
||||
class Policy;
|
||||
class CommandController;
|
||||
class WorkspaceManager;
|
||||
class AutoRestartingLauncher;
|
||||
class WindowController;
|
||||
|
||||
struct IpcValidationResult
|
||||
{
|
||||
bool success = true;
|
||||
bool parse_error = false;
|
||||
std::string error;
|
||||
};
|
||||
|
||||
/// Processes all commands coming from i3 IPC. This class is mostly for organizational
|
||||
/// purposes, as a lot of logic is associated with processing these operations.
|
||||
class IpcCommandExecutor
|
||||
{
|
||||
public:
|
||||
IpcCommandExecutor(
|
||||
Policy&,
|
||||
CommandController&,
|
||||
WorkspaceManager&,
|
||||
CompositorState const&,
|
||||
AutoRestartingLauncher&,
|
||||
WindowController&);
|
||||
void process(IpcParseResult const&);
|
||||
IpcValidationResult process(IpcParseResult const&);
|
||||
|
||||
private:
|
||||
Policy& policy;
|
||||
CommandController& policy;
|
||||
WorkspaceManager& workspace_manager;
|
||||
CompositorState const& state;
|
||||
AutoRestartingLauncher& launcher;
|
||||
WindowController& window_controller;
|
||||
|
||||
miral::Window get_window_meeting_criteria(IpcParseResult const&);
|
||||
void process_exec(IpcCommand const&, IpcParseResult const&);
|
||||
void process_split(IpcCommand const&, IpcParseResult const&);
|
||||
void process_focus(IpcCommand const&, IpcParseResult const&);
|
||||
void process_move(IpcCommand const&, IpcParseResult const&);
|
||||
void process_sticky(IpcCommand const&, IpcParseResult const&);
|
||||
void process_input(IpcCommand const&, IpcParseResult const&);
|
||||
void process_workspace(IpcCommand const&, IpcParseResult const&);
|
||||
void process_layout(IpcCommand const&, IpcParseResult const&);
|
||||
void process_scratchpad(IpcCommand const&, IpcParseResult const&);
|
||||
void process_resize(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_exec(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_split(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_focus(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_move(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_sticky(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_input(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_workspace(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_layout(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_scratchpad(IpcCommand const&, IpcParseResult const&);
|
||||
IpcValidationResult process_resize(IpcCommand const&, IpcParseResult const&);
|
||||
|
||||
IpcValidationResult parse_error(std::string error);
|
||||
};
|
||||
|
||||
} // miracle
|
||||
|
@ -110,7 +110,7 @@ Policy::Policy(
|
||||
window_controller(tools, animator, state),
|
||||
i3_command_executor(*this, workspace_manager, compositor_state, external_client_launcher, window_controller),
|
||||
surface_tracker { surface_tracker },
|
||||
ipc { std::make_shared<Ipc>(runner, workspace_manager, *this, server.the_main_loop(), i3_command_executor, config) },
|
||||
ipc { std::make_shared<Ipc>(runner, workspace_manager, *this, i3_command_executor, config) },
|
||||
scratchpad_(window_controller, state),
|
||||
self { std::make_shared<Self>(*this) }
|
||||
{
|
||||
@ -257,7 +257,7 @@ bool Policy::handle_pointer_event(MirPointerEvent const* event)
|
||||
state.cursor_position = { x, y };
|
||||
|
||||
// Select the output first
|
||||
for (auto const& output : output_list)
|
||||
for (auto const& output : state.output_list)
|
||||
{
|
||||
if (output->point_is_in_output(static_cast<int>(x), static_cast<int>(y)))
|
||||
{
|
||||
@ -360,10 +360,10 @@ void Policy::advise_new_window(miral::WindowInfo const& window_info)
|
||||
{
|
||||
mir::log_warning("create_container: output unavailable");
|
||||
auto window = window_info.window();
|
||||
if (!output_list.empty())
|
||||
if (!state.output_list.empty())
|
||||
{
|
||||
// Our output is gone! Let's try to add it to a different output
|
||||
output_list.front()->add_immediately(window);
|
||||
state.output_list.front()->add_immediately(window);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -505,7 +505,7 @@ void Policy::advise_output_create(miral::Output const& output)
|
||||
auto output_content = std::make_shared<Output>(
|
||||
output, workspace_manager, output.extents(), window_manager_tools,
|
||||
floating_window_manager, state, config, window_controller, animator);
|
||||
output_list.push_back(output_content);
|
||||
state.output_list.push_back(output_content);
|
||||
workspace_manager.request_first_available_workspace(output_content.get());
|
||||
if (state.active_output == nullptr)
|
||||
{
|
||||
@ -527,7 +527,7 @@ void Policy::advise_output_create(miral::Output const& output)
|
||||
|
||||
void Policy::advise_output_update(miral::Output const& updated, miral::Output const& original)
|
||||
{
|
||||
for (auto& output : output_list)
|
||||
for (auto& output : state.output_list)
|
||||
{
|
||||
if (output->get_output().is_same_output(original))
|
||||
{
|
||||
@ -539,7 +539,7 @@ void Policy::advise_output_update(miral::Output const& updated, miral::Output co
|
||||
|
||||
void Policy::advise_output_delete(miral::Output const& output)
|
||||
{
|
||||
for (auto it = output_list.begin(); it != output_list.end(); it++)
|
||||
for (auto it = state.output_list.begin(); it != state.output_list.end(); it++)
|
||||
{
|
||||
auto other_output = *it;
|
||||
if (other_output->get_output().is_same_output(output))
|
||||
@ -556,8 +556,8 @@ void Policy::advise_output_delete(miral::Output const& output)
|
||||
workspace_manager.delete_workspace(w);
|
||||
};
|
||||
|
||||
output_list.erase(it);
|
||||
if (output_list.empty())
|
||||
state.output_list.erase(it);
|
||||
if (state.output_list.empty())
|
||||
{
|
||||
// All nodes should become orphaned
|
||||
for (auto& window : other_output->collect_all_windows())
|
||||
@ -573,7 +573,7 @@ void Policy::advise_output_delete(miral::Output const& output)
|
||||
}
|
||||
else
|
||||
{
|
||||
state.active_output = output_list.front();
|
||||
state.active_output = state.output_list.front();
|
||||
state.active_output->set_is_active(true);
|
||||
for (auto& window : other_output->collect_all_windows())
|
||||
{
|
||||
@ -663,7 +663,7 @@ mir::geometry::Rectangle Policy::confirm_inherited_move(
|
||||
|
||||
void Policy::advise_application_zone_create(miral::Zone const& application_zone)
|
||||
{
|
||||
for (auto const& output : output_list)
|
||||
for (auto const& output : state.output_list)
|
||||
{
|
||||
output->advise_application_zone_create(application_zone);
|
||||
}
|
||||
@ -671,7 +671,7 @@ void Policy::advise_application_zone_create(miral::Zone const& application_zone)
|
||||
|
||||
void Policy::advise_application_zone_update(miral::Zone const& updated, miral::Zone const& original)
|
||||
{
|
||||
for (auto const& output : output_list)
|
||||
for (auto const& output : state.output_list)
|
||||
{
|
||||
output->advise_application_zone_update(updated, original);
|
||||
}
|
||||
@ -679,7 +679,7 @@ void Policy::advise_application_zone_update(miral::Zone const& updated, miral::Z
|
||||
|
||||
void Policy::advise_application_zone_delete(miral::Zone const& application_zone)
|
||||
{
|
||||
for (auto const& output : output_list)
|
||||
for (auto const& output : state.output_list)
|
||||
{
|
||||
output->advise_application_zone_delete(application_zone);
|
||||
}
|
||||
@ -1315,15 +1315,15 @@ void Policy::move_cursor_to_output(Output const& output)
|
||||
|
||||
bool Policy::try_select_next_output()
|
||||
{
|
||||
for (size_t i = 0; i < output_list.size(); i++)
|
||||
for (size_t i = 0; i < state.output_list.size(); i++)
|
||||
{
|
||||
if (output_list[i] == state.active_output)
|
||||
if (state.output_list[i] == state.active_output)
|
||||
{
|
||||
size_t j = i + 1;
|
||||
if (j == output_list.size())
|
||||
if (j == state.output_list.size())
|
||||
j = 0;
|
||||
|
||||
move_cursor_to_output(*output_list[j]);
|
||||
move_cursor_to_output(*state.output_list[j]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1333,15 +1333,15 @@ bool Policy::try_select_next_output()
|
||||
|
||||
bool Policy::try_select_prev_output()
|
||||
{
|
||||
for (int i = output_list.size() - 1; i >= 0; i++)
|
||||
for (int i = state.output_list.size() - 1; i >= 0; i++)
|
||||
{
|
||||
if (output_list[i] == state.active_output)
|
||||
if (state.output_list[i] == state.active_output)
|
||||
{
|
||||
size_t j = i - 1;
|
||||
if (j < 0)
|
||||
j = output_list.size() - 1;
|
||||
j = state.output_list.size() - 1;
|
||||
|
||||
move_cursor_to_output(*output_list[j]);
|
||||
move_cursor_to_output(*state.output_list[j]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1353,7 +1353,7 @@ std::shared_ptr<Output> const& Policy::_next_output_in_direction(Direction direc
|
||||
{
|
||||
auto const& active = state.active_output;
|
||||
auto const& active_area = active->get_area();
|
||||
for (auto const& output : output_list)
|
||||
for (auto const& output : state.output_list)
|
||||
{
|
||||
if (output == state.active_output)
|
||||
continue;
|
||||
@ -1432,7 +1432,7 @@ std::shared_ptr<Output> const& Policy::_next_output_in_list(std::vector<std::str
|
||||
if (next == names.size())
|
||||
next = 0;
|
||||
|
||||
for (auto const& output : output_list)
|
||||
for (auto const& output : state.output_list)
|
||||
{
|
||||
if (output->get_output().name() == names[next])
|
||||
return output;
|
||||
@ -1499,20 +1499,20 @@ bool Policy::try_move_active_to_current()
|
||||
|
||||
bool Policy::try_move_active_to_primary()
|
||||
{
|
||||
if (output_list.empty())
|
||||
if (state.output_list.empty())
|
||||
return false;
|
||||
|
||||
if (!can_move_container())
|
||||
return false;
|
||||
|
||||
if (state.active()->get_output() == output_list[0].get())
|
||||
if (state.active()->get_output() == state.output_list[0].get())
|
||||
return false;
|
||||
|
||||
auto container = state.active();
|
||||
container->get_output()->delete_container(container);
|
||||
state.unfocus(container);
|
||||
|
||||
output_list[0]->graft(container);
|
||||
state.output_list[0]->graft(container);
|
||||
if (container->window().value())
|
||||
window_controller.select_active_window(container->window().value());
|
||||
return true;
|
||||
@ -1521,20 +1521,20 @@ bool Policy::try_move_active_to_primary()
|
||||
bool Policy::try_move_active_to_nonprimary()
|
||||
{
|
||||
constexpr int MIN_SIZE_TO_HAVE_NONPRIMARY_OUTPUT = 2;
|
||||
if (output_list.size() < MIN_SIZE_TO_HAVE_NONPRIMARY_OUTPUT)
|
||||
if (state.output_list.size() < MIN_SIZE_TO_HAVE_NONPRIMARY_OUTPUT)
|
||||
return false;
|
||||
|
||||
if (!can_move_container())
|
||||
return false;
|
||||
|
||||
if (state.active_output != output_list[0])
|
||||
if (state.active_output != state.output_list[0])
|
||||
return false;
|
||||
|
||||
auto container = state.active();
|
||||
container->get_output()->delete_container(container);
|
||||
state.unfocus(container);
|
||||
|
||||
output_list[1]->graft(container);
|
||||
state.output_list[1]->graft(container);
|
||||
if (container->window().value())
|
||||
window_controller.select_active_window(container->window().value());
|
||||
return true;
|
||||
@ -1545,16 +1545,16 @@ bool Policy::try_move_active_to_next()
|
||||
if (!can_move_container())
|
||||
return false;
|
||||
|
||||
auto it = std::find(output_list.begin(), output_list.end(), state.active_output);
|
||||
if (it == output_list.end())
|
||||
auto it = std::find(state.output_list.begin(), state.output_list.end(), state.active_output);
|
||||
if (it == state.output_list.end())
|
||||
{
|
||||
mir::log_error("Policy::try_move_active_to_next: cannot find active output in list");
|
||||
return false;
|
||||
}
|
||||
|
||||
it++;
|
||||
if (it == output_list.end())
|
||||
it = output_list.begin();
|
||||
if (it == state.output_list.end())
|
||||
it = state.output_list.begin();
|
||||
|
||||
if (*it == state.active_output)
|
||||
return false;
|
||||
|
106
src/policy.h
106
src/policy.h
@ -20,6 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "animator.h"
|
||||
#include "auto_restarting_launcher.h"
|
||||
#include "command_controller.h"
|
||||
#include "compositor_state.h"
|
||||
#include "config.h"
|
||||
#include "ipc.h"
|
||||
@ -49,7 +50,7 @@ class Container;
|
||||
class ContainerGroupContainer;
|
||||
class WindowToolsAccessor;
|
||||
|
||||
class Policy : public miral::WindowManagementPolicy
|
||||
class Policy : public miral::WindowManagementPolicy, public CommandController
|
||||
{
|
||||
public:
|
||||
Policy(
|
||||
@ -101,61 +102,61 @@ public:
|
||||
|
||||
// Requests
|
||||
|
||||
bool try_request_horizontal();
|
||||
bool try_request_vertical();
|
||||
bool try_toggle_layout(bool cycle_through_all);
|
||||
void try_toggle_resize_mode();
|
||||
bool try_resize(Direction direction, int pixels);
|
||||
bool try_set_size(std::optional<int> const& width, std::optional<int> const& height);
|
||||
bool try_move(Direction direction);
|
||||
bool try_move_by(Direction direction, int pixels);
|
||||
bool try_move_to(int x, int y);
|
||||
bool try_select(Direction direction);
|
||||
bool try_select_parent();
|
||||
bool try_select_child();
|
||||
bool try_select_floating();
|
||||
bool try_select_tiling();
|
||||
bool try_select_toggle();
|
||||
bool try_close_window();
|
||||
bool quit();
|
||||
bool try_toggle_fullscreen();
|
||||
bool select_workspace(int number, bool back_and_forth = true);
|
||||
bool select_workspace(std::string const& name, bool back_and_forth);
|
||||
bool next_workspace();
|
||||
bool prev_workspace();
|
||||
bool back_and_forth_workspace();
|
||||
bool next_workspace_on_output(Output const&);
|
||||
bool prev_workspace_on_output(Output const&);
|
||||
bool move_active_to_workspace(int number, bool back_and_forth = true);
|
||||
bool move_active_to_workspace_named(std::string const&, bool back_and_forth);
|
||||
bool move_active_to_next_workspace();
|
||||
bool move_active_to_prev_workspace();
|
||||
bool move_active_to_back_and_forth();
|
||||
bool move_to_scratchpad();
|
||||
bool show_scratchpad();
|
||||
bool toggle_floating();
|
||||
bool toggle_pinned_to_workspace();
|
||||
bool set_is_pinned(bool);
|
||||
bool toggle_tabbing();
|
||||
bool toggle_stacking();
|
||||
bool set_layout(LayoutScheme scheme);
|
||||
bool set_layout_default();
|
||||
void move_cursor_to_output(Output const&);
|
||||
bool try_select_next_output();
|
||||
bool try_select_prev_output();
|
||||
bool try_select_output(Direction direction);
|
||||
bool try_select_output(std::vector<std::string> const& names);
|
||||
bool try_move_active_to_output(Direction direction);
|
||||
bool try_move_active_to_current();
|
||||
bool try_move_active_to_primary();
|
||||
bool try_move_active_to_nonprimary();
|
||||
bool try_move_active_to_next();
|
||||
bool try_move_active(std::vector<std::string> const& names);
|
||||
bool try_request_horizontal() override;
|
||||
bool try_request_vertical() override;
|
||||
bool try_toggle_layout(bool cycle_through_all) override;
|
||||
void try_toggle_resize_mode() override;
|
||||
bool try_resize(Direction direction, int pixels) override;
|
||||
bool try_set_size(std::optional<int> const& width, std::optional<int> const& height) override;
|
||||
bool try_move(Direction direction) override;
|
||||
bool try_move_by(Direction direction, int pixels) override;
|
||||
bool try_move_to(int x, int y) override;
|
||||
bool try_select(Direction direction) override;
|
||||
bool try_select_parent() override;
|
||||
bool try_select_child() override;
|
||||
bool try_select_floating() override;
|
||||
bool try_select_tiling() override;
|
||||
bool try_select_toggle() override;
|
||||
bool try_close_window() override;
|
||||
bool quit() override;
|
||||
bool try_toggle_fullscreen() override;
|
||||
bool select_workspace(int number, bool back_and_forth = true) override;
|
||||
bool select_workspace(std::string const& name, bool back_and_forth) override;
|
||||
bool next_workspace() override;
|
||||
bool prev_workspace() override;
|
||||
bool back_and_forth_workspace() override;
|
||||
bool next_workspace_on_output(Output const&) override;
|
||||
bool prev_workspace_on_output(Output const&) override;
|
||||
bool move_active_to_workspace(int number, bool back_and_forth = true) override;
|
||||
bool move_active_to_workspace_named(std::string const&, bool back_and_forth) override;
|
||||
bool move_active_to_next_workspace() override;
|
||||
bool move_active_to_prev_workspace() override;
|
||||
bool move_active_to_back_and_forth() override;
|
||||
bool move_to_scratchpad() override;
|
||||
bool show_scratchpad() override;
|
||||
bool toggle_floating() override;
|
||||
bool toggle_pinned_to_workspace() override;
|
||||
bool set_is_pinned(bool) override;
|
||||
bool toggle_tabbing() override;
|
||||
bool toggle_stacking() override;
|
||||
bool set_layout(LayoutScheme scheme) override;
|
||||
bool set_layout_default() override;
|
||||
void move_cursor_to_output(Output const&) override;
|
||||
bool try_select_next_output() override;
|
||||
bool try_select_prev_output() override;
|
||||
bool try_select_output(Direction direction) override;
|
||||
bool try_select_output(std::vector<std::string> const& names) override;
|
||||
bool try_move_active_to_output(Direction direction) override;
|
||||
bool try_move_active_to_current() override;
|
||||
bool try_move_active_to_primary() override;
|
||||
bool try_move_active_to_nonprimary() override;
|
||||
bool try_move_active_to_next() override;
|
||||
bool try_move_active(std::vector<std::string> const& names) override;
|
||||
|
||||
// Getters
|
||||
|
||||
[[nodiscard]] Output const* get_active_output() const { return state.active_output.get(); }
|
||||
[[nodiscard]] std::vector<std::shared_ptr<Output>> const& get_output_list() const { return output_list; }
|
||||
[[nodiscard]] std::vector<std::shared_ptr<Output>> const& get_output_list() const { return state.output_list; }
|
||||
[[nodiscard]] geom::Point const& get_cursor_position() const { return state.cursor_position; }
|
||||
[[nodiscard]] CompositorState const& get_state() const { return state; }
|
||||
|
||||
@ -173,7 +174,6 @@ private:
|
||||
|
||||
bool is_starting_ = true;
|
||||
CompositorState& state;
|
||||
std::vector<std::shared_ptr<Output>> output_list;
|
||||
AllocationHint pending_allocation;
|
||||
std::vector<miral::Window> orphaned_window_list;
|
||||
miral::WindowManagerTools window_manager_tools;
|
||||
|
Loading…
Reference in New Issue
Block a user