aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.