diff --git a/include/sway/commands.h b/include/sway/commands.h index bbbdfc80..3fde0893 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -288,6 +288,7 @@ sway_cmd seat_cmd_idle_inhibit; sway_cmd seat_cmd_idle_wake; sway_cmd seat_cmd_keyboard_grouping; sway_cmd seat_cmd_pointer_constraint; +sway_cmd seat_cmd_shortcuts_inhibitor; sway_cmd seat_cmd_xcursor_theme; sway_cmd cmd_ipc_cmd; diff --git a/include/sway/config.h b/include/sway/config.h index fdd65efd..359f9604 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -177,6 +177,12 @@ enum seat_config_allow_constrain { CONSTRAIN_DISABLE }; +enum seat_config_shortcuts_inhibit { + SHORTCUTS_INHIBIT_DEFAULT, // the default is currently enabled + SHORTCUTS_INHIBIT_ENABLE, + SHORTCUTS_INHIBIT_DISABLE +}; + enum seat_keyboard_grouping { KEYBOARD_GROUP_DEFAULT, // the default is currently smart KEYBOARD_GROUP_NONE, @@ -201,6 +207,7 @@ struct seat_config { list_t *attachments; // list of seat_attachment configs int hide_cursor_timeout; enum seat_config_allow_constrain allow_constrain; + enum seat_config_shortcuts_inhibit shortcuts_inhibit; enum seat_keyboard_grouping keyboard_grouping; uint32_t idle_inhibit_sources, idle_wake_sources; struct { diff --git a/sway/commands/seat.c b/sway/commands/seat.c index eba28cac..84c6ba53 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c @@ -22,6 +22,7 @@ static struct cmd_handler seat_handlers[] = { { "idle_wake", seat_cmd_idle_wake }, { "keyboard_grouping", seat_cmd_keyboard_grouping }, { "pointer_constraint", seat_cmd_pointer_constraint }, + { "shortcuts_inhibitor", seat_cmd_shortcuts_inhibitor }, { "xcursor_theme", seat_cmd_xcursor_theme }, }; diff --git a/sway/commands/seat/shortcuts_inhibitor.c b/sway/commands/seat/shortcuts_inhibitor.c new file mode 100644 index 00000000..7c7f99cf --- /dev/null +++ b/sway/commands/seat/shortcuts_inhibitor.c @@ -0,0 +1,96 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/input/seat.h" +#include "sway/input/input-manager.h" +#include "util.h" + +static struct cmd_results *handle_action(struct seat_config *sc, + struct sway_seat *seat, const char *action) { + struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = NULL; + if (strcmp(action, "disable") == 0) { + sc->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE; + + wl_list_for_each(sway_inhibitor, + &seat->keyboard_shortcuts_inhibitors, link) { + wlr_keyboard_shortcuts_inhibitor_v1_deactivate( + sway_inhibitor->inhibitor); + } + + sway_log(SWAY_DEBUG, "Deactivated all keyboard shortcuts inhibitors"); + } else { + sway_inhibitor = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); + if (!sway_inhibitor) { + return cmd_results_new(CMD_FAILURE, + "No inhibitor found for focused surface"); + } + + struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor = + sway_inhibitor->inhibitor; + bool inhibit; + if (strcmp(action, "activate") == 0) { + inhibit = true; + } else if (strcmp(action, "deactivate") == 0) { + inhibit = false; + } else if (strcmp(action, "toggle") == 0) { + inhibit = !inhibitor->active; + } else { + return cmd_results_new(CMD_INVALID, "Expected enable|" + "disable|activate|deactivate|toggle"); + } + + if (inhibit) { + wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor); + } else { + wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor); + } + + sway_log(SWAY_DEBUG, "%sctivated keyboard shortcuts inhibitor", + inhibit ? "A" : "Dea"); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +// shortcuts_inhibitor [enable|disable|activate|deactivate|toggle] +struct cmd_results *seat_cmd_shortcuts_inhibitor(int argc, char **argv) { + struct cmd_results *error = + checkarg(argc, "shortcuts_inhibitor", EXPECTED_EQUAL_TO, 1); + if (error) { + return error; + } + + struct seat_config *sc = config->handler_context.seat_config; + if (!sc) { + return cmd_results_new(CMD_FAILURE, "No seat defined"); + } + + if (strcmp(argv[0], "enable") == 0) { + sc->shortcuts_inhibit = SHORTCUTS_INHIBIT_ENABLE; + // at runtime disable is an action that also deactivates all active + // inhibitors handled in handle_action() + } else if (strcmp(argv[0], "disable") == 0 && !config->active) { + sc->shortcuts_inhibit = SHORTCUTS_INHIBIT_DISABLE; + } else if (!config->active) { + return cmd_results_new(CMD_INVALID, "only enable and disable " + "can be used in the config"); + } else { + if (strcmp(sc->name, "*") != 0) { + struct sway_seat *seat = input_manager_get_seat(sc->name, false); + if (!seat) { + return cmd_results_new(CMD_FAILURE, + "Seat %s does not exist", sc->name); + } + error = handle_action(sc, seat, argv[0]); + } else { + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + error = handle_action(sc, seat, argv[0]); + if (error && error->status != CMD_SUCCESS) { + break; + } + } + } + } + + return error ? error : cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/config/seat.c b/sway/config/seat.c index 6c916727..e2702de5 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -30,6 +30,7 @@ struct seat_config *new_seat_config(const char* name) { } seat->hide_cursor_timeout = -1; seat->allow_constrain = CONSTRAIN_DEFAULT; + seat->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; seat->keyboard_grouping = KEYBOARD_GROUP_DEFAULT; seat->xcursor_theme.name = NULL; seat->xcursor_theme.size = 24; @@ -154,6 +155,10 @@ void merge_seat_config(struct seat_config *dest, struct seat_config *source) { dest->allow_constrain = source->allow_constrain; } + if (source->shortcuts_inhibit != SHORTCUTS_INHIBIT_DEFAULT) { + dest->shortcuts_inhibit = source->shortcuts_inhibit; + } + if (source->keyboard_grouping != KEYBOARD_GROUP_DEFAULT) { dest->keyboard_grouping = source->keyboard_grouping; } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index af0f5afa..124d57dc 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -333,6 +333,26 @@ static void handle_keyboard_shortcuts_inhibit_new_inhibitor( struct sway_seat *seat = inhibitor->seat->data; wl_list_insert(&seat->keyboard_shortcuts_inhibitors, &sway_inhibitor->link); + struct seat_config *config = seat_get_config(seat); + if (!config) { + config = seat_get_config_by_name("*"); + } + + if (config && config->shortcuts_inhibit == SHORTCUTS_INHIBIT_DISABLE) { + /** + * Here we deny to honour the inhibitor by never sending the + * activate signal. We can not, however, destroy the inhibitor + * because the protocol doesn't allow for it. So it will linger + * until the client removes it im- or explicitly. But at least + * it can only be one inhibitor per surface and seat at a time. + * + * We also want to allow the user to activate the inhibitor + * manually later which is why we do this check here where the + * inhibitor is already attached to its seat and ready for use. + */ + return; + } + wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor); } diff --git a/sway/meson.build b/sway/meson.build index 6fdc4a7d..8a549108 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -94,6 +94,7 @@ sway_sources = files( 'commands/seat/idle.c', 'commands/seat/keyboard_grouping.c', 'commands/seat/pointer_constraint.c', + 'commands/seat/shortcuts_inhibitor.c', 'commands/seat/xcursor_theme.c', 'commands/set.c', 'commands/show_marks.c', diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 9edd9381..c0584241 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -243,6 +243,25 @@ correct seat. by default) for the seat. This is primarily useful for video games. The "escape" command can be used at runtime to escape from a captured client. +*seat* shortcuts_inhibitor enable|disable|activate|deactivate|toggle + Enables or disables the ability of clients to inhibit keyboard + shortcuts for the seat. This is primarily useful for virtualization and + remote desktop software. Subcommands _enable_ and _disable_ affect + whether future inhibitors are honoured by default, i.e. activated + automatically, the default being _enable_. When used at runtime, + _disable_ also disables any currently active inhibitors. _activate_, + _deactivate_ and _toggle_ are only useable at runtime and change the + state of a potentially existing inhibitor on the currently focused + window. This can be used with the current seat alias (_-_) to affect + only the currently focused window of the current seat. Subcommand + _deactivate_ is particularly useful in an _--inhibited_ *bindsym* to + escape a state where shortcuts are inhibited and the client becomes + uncooperative. It is worth noting that whether disabled or deactivated + inhibitors are removed is entirely up to the client. Depending on the + client it may therefore be possible to (re-)activate them later. Any + visual indication that an inhibitor is present is currently left to the + client as well. + *seat* xcursor_theme [] Override the system default XCursor theme. The default seat's (_seat0_) theme is also used as the default cursor theme in