feature: custom actions

This commit is contained in:
Matthew Kosarek 2024-02-09 16:05:45 -05:00
parent c6dbc2c402
commit d6abbe57e1
4 changed files with 165 additions and 7 deletions

View File

@ -1,7 +1,7 @@
> This user manual will keep up to date with the current state of the project.
> Please note that the information in here is likely to change with future release.
# Default Key Commands
# Built-in Key Commands
- `meta + enter`: Open new terminal
- `meta + h`: Switch current lane to horizontal layout mode
- `meta + v`: Switch current lane to vertical layout mode
@ -30,10 +30,12 @@
- Window CANNOT be resized or moved with the pointer
# Configuration File
## Location
The configuration file will be written blank the first time that you start the compositor. The file is named `miracle-wm.yaml`
and it is written to your config directory, most likely at `~/.config/miracle-wm.yaml`.
## Data Types
## Types
First, let's define some reoccurring data types in the configuration file:
- `ModifierKey`: represents a modifier to be used in conjunction with another key press (e.g. Ctrl + Alt + Delete; Ctrl and Alt would be modifiers)
@ -89,19 +91,40 @@ First, let's define some reoccurring data types in the configuration file:
};
```
## Configuration Definition
- `CustomAction`: defines a custom action to execute when the provided keybind is inputted.
```c++
struct CustomAction
{
// Action to execute
command: String
// Action will fire based on this key event
action: "up" | "down" | "repeat" | "modifiers";
// Modifiers required for the action to trigger
modifiers: Modifier[];
// Name of the keycode that the action should respond to.
// See https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
// for the list of available keycodes (e.g. KEY_ENTER, KEY_Z, etc.)
key: KeyCodeName;
};
```
## Definition
With those types defined, the following table defines the allowed key/value pairs:
| Key | Default | Type | Description |
|--------------------------|---------|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| action_key | `meta` | `Modifier` | The default key that is used to initate any action. |
| default_action_overrides | `[]` | `DefaultActionOverride[]` | A list overrides to apply to built-in actions. Actions may be overridden more than once and will respond to multiple key combinations as a result. Defining at least one override disables the default action defined in [Default Key Commands](#default-key-commands) |
| gap_size_x | 10 | `int` | Size of the gaps in pixels horizontally between windows | |
| gap_size_y | 10 | `int` | Size of the gaps in pixels vertically between windows | |
| startup_apps | [] | `String[]` | List of applications to be started when the compositor starts |
| custom_actions | [] | `CustomAction[]` | A list of custom applications that I user can execute. These actions always have precedence over built-in actions. |
| gap_size_x | 10 | `int` | Size of the gaps in pixels horizontally between windows | |
| gap_size_y | 10 | `int` | Size of the gaps in pixels vertically between windows | |
| startup_apps | [] | `String[]` | List of applications to be started when the compositor starts |
## Example Configuration
## Example
```yaml
action_key: alt # Set the primary action key to alt
default_action_overrides:
@ -111,6 +134,14 @@ default_action_overrides:
- ctrl
- shift
key: KEY_ENTER
custom_actions: # Set meta + D to open wofi
- command: wofi --show=drun
action: down
modifiers:
- primary
key: KEY_D
gap_size_x: 20
gap_size_y: 20
startup_apps:

View File

@ -377,6 +377,95 @@ MiracleConfig::MiracleConfig()
key_commands[i].push_back(default_key_commands[i]);
}
// Custom actions
if (config["custom_actions"])
{
auto const custom_actions = config["custom_actions"];
if (!custom_actions.IsSequence())
{
mir::log_error("custom_actions: value must be an array");
return;
}
for (auto i = 0; i < custom_actions.size(); i++)
{
auto sub_node = custom_actions[i];
if (!sub_node["command"])
{
mir::log_error("custom_actions: missing command");
continue;
}
if (!sub_node["action"])
{
mir::log_error("custom_actions: missing action");
continue;
}
if (!sub_node["modifiers"])
{
mir::log_error("custom_actions: missing modifiers");
continue;
}
if (!sub_node["key"])
{
mir::log_error("custom_actions: missing key");
continue;
}
// TODO: Copy & paste here
auto command = sub_node["command"].as<std::string>();
auto action = sub_node["action"].as<std::string>();
MirKeyboardAction keyboard_action;
if (action == "up")
keyboard_action = MirKeyboardAction::mir_keyboard_action_up;
else if (action == "down")
keyboard_action = MirKeyboardAction::mir_keyboard_action_down;
else if (action == "repeat")
keyboard_action = MirKeyboardAction::mir_keyboard_action_repeat;
else if (action == "modifiers")
keyboard_action = MirKeyboardAction::mir_keyboard_action_modifiers;
else {
mir::log_error("custom_actions: Unknown keyboard action: %s", action.c_str());
continue;
}
auto key = sub_node["key"].as<std::string>();
auto code = libevdev_event_code_from_name(EV_KEY,
key.c_str()); //https://stackoverflow.com/questions/32059363/is-there-a-way-to-get-the-evdev-keycode-from-a-string
if (code < 0)
{
mir::log_error(
"custom_actions: Unknown keyboard code in configuration: %s. See the linux kernel for allowed codes: https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h",
key.c_str());
continue;
}
auto modifiers_node = sub_node["modifiers"];
if (!modifiers_node.IsSequence())
{
mir::log_error("custom_actions: Provided modifiers is not an array");
continue;
}
uint modifiers = 0;
for (auto j = 0; j < modifiers_node.size(); j++)
{
auto modifier = modifiers_node[j].as<std::string>();
modifiers = modifiers | parse_modifier(modifier);
}
custom_key_commands.push_back({
keyboard_action,
modifiers,
code,
command
});
}
}
// Gap sizes
if (config["gap_size_x"])
{
@ -445,6 +534,29 @@ MirInputEventModifier MiracleConfig::get_input_event_modifier() const
return (MirInputEventModifier)primary_modifier;
}
CustomKeyCommand const*
MiracleConfig::matches_custom_key_command(MirKeyboardAction action, int scan_code, unsigned int modifiers) const
{
// TODO: Copy & paste
for (auto const& command : custom_key_commands)
{
if (action != command.action)
continue;
auto command_modifiers = command.modifiers;
if (command_modifiers & miracle_input_event_modifier_default)
command_modifiers = command_modifiers & ~miracle_input_event_modifier_default | get_input_event_modifier();
if (command_modifiers != modifiers)
continue;
if (scan_code == command.key)
return &command;
}
return nullptr;
}
DefaultKeyCommand MiracleConfig::matches_key_command(MirKeyboardAction action, int scan_code, unsigned int modifiers) const
{
for (int i = 0; i < DefaultKeyCommand::MAX; i++)

View File

@ -57,6 +57,11 @@ struct KeyCommand
int key;
};
struct CustomKeyCommand : KeyCommand
{
std::string command;
};
typedef std::vector<KeyCommand> KeyCommandList;
class MiracleConfig
@ -64,6 +69,7 @@ class MiracleConfig
public:
MiracleConfig();
[[nodiscard]] MirInputEventModifier get_input_event_modifier() const;
CustomKeyCommand const* matches_custom_key_command(MirKeyboardAction action, int scan_code, unsigned int modifiers) const;
[[nodiscard]] DefaultKeyCommand matches_key_command(MirKeyboardAction action, int scan_code, unsigned int modifiers) const;
[[nodiscard]] int get_gap_size_x() const;
[[nodiscard]] int get_gap_size_y() const;
@ -74,6 +80,8 @@ private:
static const uint miracle_input_event_modifier_default = 1 << 18;
uint primary_modifier = mir_input_event_modifier_meta;
std::vector<CustomKeyCommand> custom_key_commands;
KeyCommandList key_commands[DefaultKeyCommand::MAX];
int gap_size_x = 10;

View File

@ -92,6 +92,13 @@ bool MiracleWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const
auto const scan_code = miral::toolkit::mir_keyboard_event_scan_code(event);
auto const modifiers = miral::toolkit::mir_keyboard_event_modifiers(event) & MODIFIER_MASK;
auto custom_key_command = config.matches_custom_key_command(action, scan_code, modifiers);
if (custom_key_command != nullptr)
{
external_client_launcher.launch(custom_key_command->command);
return true;
}
auto key_command = config.matches_key_command(action, scan_code, modifiers);
if (key_command == DefaultKeyCommand::MAX)
return false;