aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2024-08-07 15:20:34 +0200
committerPřemysl Eric Janouch <p@janouch.name>2024-08-07 17:01:35 +0200
commit180b16faee4b4fdba493d6ba7a5b4ba6e0475ead (patch)
treea05a8d9b750f7790961d457a184e6aabbbd6594c
parent674ea6d9a62d5d62218071e7017198013cdb7adc (diff)
downloaddesktop-tools-180b16faee4b4fdba493d6ba7a5b4ba6e0475ead.tar.gz
desktop-tools-180b16faee4b4fdba493d6ba7a5b4ba6e0475ead.tar.xz
desktop-tools-180b16faee4b4fdba493d6ba7a5b4ba6e0475ead.zip
wmstatus: add an option to import bindings to Sway
We still want to retain the ability to bind them on our own under X11. With this, the Wayland situation has considerably improved, but the activity watch and keyboard layout switching are still broken.
-rw-r--r--wmstatus.c154
1 files changed, 151 insertions, 3 deletions
diff --git a/wmstatus.c b/wmstatus.c
index 11cce10..426fb0d 100644
--- a/wmstatus.c
+++ b/wmstatus.c
@@ -70,6 +70,20 @@ log_message_custom (void *user_data, const char *quote, const char *fmt,
fputs ("\n", stream);
}
+static void
+shell_quote (const char *str, struct str *output)
+{
+ // See SUSv3 Shell and Utilities, 2.2.3 Double-Quotes
+ str_append_c (output, '"');
+ for (const char *p = str; *p; p++)
+ {
+ if (strchr ("`$\"\\", *p))
+ str_append_c (output, '\\');
+ str_append_c (output, *p);
+ }
+ str_append_c (output, '"');
+}
+
// --- NUT ---------------------------------------------------------------------
// More or less copied and pasted from the MPD client. This code doesn't even
@@ -2992,6 +3006,136 @@ app_save_configuration (struct app_context *ctx, const char *path_hint)
free (filename);
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static bool
+sway_command_argument_needs_quoting (const char *word)
+{
+ while (*word)
+ if (!isalnum_ascii (*word++))
+ return true;
+ return false;
+}
+
+static void
+sway_append_command_argument (struct str *out, const char *word)
+{
+ if (out->len)
+ str_append_c (out, ' ');
+
+ if (!sway_command_argument_needs_quoting (word))
+ {
+ str_append (out, word);
+ return;
+ }
+
+ str_append_c (out, '\'');
+ for (const char *p = word; *p; p++)
+ {
+ if (*p == '\'' || *p == '\\')
+ str_append_c (out, '\\');
+ str_append_c (out, *p);
+ }
+ str_append_c (out, '\'');
+}
+
+static const char *
+sway_bindsym (const char *combination, const char *action)
+{
+ const char *error = NULL;
+ struct strv keys = strv_make ();
+ struct strv args = strv_make ();
+ if (!parse_binding (combination, &keys))
+ {
+ error = "parsing key combination failed";
+ goto out;
+ }
+ if (!parse_binding (action, &args) || !args.len)
+ {
+ error = "parsing the binding failed";
+ goto out;
+ }
+
+ struct action handler = action_by_name (args.vector[0]);
+ if (!handler.name)
+ {
+ error = "unknown action";
+ goto out;
+ }
+
+ // The i3/Sway quoting is properly fucked up,
+ // and its exec command forwards to `sh -c`.
+ struct str shell_command = str_make ();
+ if (strcmp (handler.name, "exec"))
+ {
+ // argv[0] would need realpath() applied on it.
+ shell_quote (PROGRAM_NAME, &shell_command);
+ str_append (&shell_command, " -- ");
+ shell_quote (handler.name, &shell_command);
+ str_append_c (&shell_command, ' ');
+ }
+ for (size_t i = 1; i < args.len; i++)
+ {
+ shell_quote (args.vector[i], &shell_command);
+ str_append_c (&shell_command, ' ');
+ }
+ if (shell_command.len)
+ shell_command.str[--shell_command.len] = 0;
+
+ // This command name may not be quoted.
+ // Note that i3-msg doesn't accept bindsym at all, only swaymsg does.
+ struct str sway_command = str_make ();
+ sway_append_command_argument (&sway_command, "bindsym");
+ char *recombined = strv_join (&keys, "+");
+ sway_append_command_argument (&sway_command, recombined);
+ free (recombined);
+ sway_append_command_argument (&sway_command, "exec");
+ sway_append_command_argument (&sway_command, shell_command.str);
+ str_free (&shell_command);
+
+ struct strv argv = strv_make ();
+ strv_append (&argv, "swaymsg");
+ strv_append_owned (&argv, str_steal (&sway_command));
+
+ posix_spawn_file_actions_t actions;
+ posix_spawn_file_actions_init (&actions);
+ posix_spawnp (NULL, argv.vector[0], &actions, NULL, argv.vector, environ);
+ posix_spawn_file_actions_destroy (&actions);
+
+ strv_free (&argv);
+out:
+ strv_free (&keys);
+ strv_free (&args);
+ return error;
+}
+
+static void
+sway_forward_bindings (void)
+{
+ // app_context_init() has side-effects.
+ struct app_context ctx = { .config = app_make_config () };
+ app_load_configuration (&ctx);
+
+ struct str_map *keys =
+ &config_item_get (ctx.config.root, "keys", NULL)->value.object;
+ struct str_map_iter iter = str_map_iter_make (keys);
+
+ struct config_item *action;
+ while ((action = str_map_iter_next (&iter)))
+ {
+ const char *combination = iter.link->key, *err = NULL;
+ if (action->type != CONFIG_ITEM_NULL)
+ {
+ if (action->type != CONFIG_ITEM_STRING)
+ err = "expected a string";
+ else
+ err = sway_bindsym (combination, action->value.string.str);
+ }
+ if (err)
+ print_warning ("configuration: key `%s': %s", combination, err);
+ }
+}
+
// --- Signals -----------------------------------------------------------------
static int g_signal_pipe[2]; ///< A pipe used to signal... signals
@@ -3079,15 +3223,16 @@ main (int argc, char *argv[])
{ 'd', "debug", NULL, 0, "run in debug mode" },
{ 'h', "help", NULL, 0, "display this help and exit" },
{ 'V', "version", NULL, 0, "output version information and exit" },
- { '3', "i3bar", NULL, 0, "print output for i3bar/sway-bar instead" },
+ { '3', "i3bar", NULL, 0, "print output for i3-bar/swaybar instead" },
+ { 's', "bind-sway", NULL, 0, "import bindings over swaymsg" },
{ 'w', "write-default-cfg", "FILENAME",
OPT_OPTIONAL_ARG | OPT_LONG_ONLY,
"write a default configuration file and exit" },
{ 0, NULL, NULL, 0, NULL }
};
- struct opt_handler oh =
- opt_handler_make (argc, argv, opts, NULL, "Set root window name.");
+ struct opt_handler oh = opt_handler_make (argc, argv, opts, "[ACTION...]",
+ "Set root window name.");
bool i3bar = false;
int c;
@@ -3106,6 +3251,9 @@ main (int argc, char *argv[])
case '3':
i3bar = true;
break;
+ case 's':
+ sway_forward_bindings ();
+ exit (EXIT_SUCCESS);
case 'w':
{
// app_context_init() has side-effects.