Files
tmux/cmd-list-keys.c
nicm d29aa12117 Replace the notification system with events. Events can carry a payload of
additional payload (to reduce problems with lifetime of objects) and are
delivered to one or more event sinks. This is more powerful and reduces the
complex dependencies between control mode and hooks. Events are now used for
hooks, control mode notifications and for monitors (set-hook -B).

wait-for can now wait for an event to fire (-E flag, with -F to for filter),
with -v to print the payload, as well as listing (-l) waiting clients on an
event and forcing one to wake (-w).

A few additional hooks are also now available (pane-created, pane-resized, etc)
and some of the existing ones have additional format variables available.
2026-07-10 13:38:45 +00:00

259 lines
6.9 KiB
C

/* $OpenBSD: cmd-list-keys.c,v 1.78 2026/07/01 13:12:17 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* List key bindings.
*/
#define LIST_KEYS_TEMPLATE \
"#{?notes_only," \
"#{key_prefix} " \
"#{p|#{key_string_width}:key_string} " \
"#{?key_note,#{key_note},#{key_command}}" \
"," \
"bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \
"-T #{p|#{key_table_width}:key_table} " \
"#{p|#{key_string_width}:#{q|a:key_string}} " \
"#{key_command}}"
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
const struct cmd_entry cmd_list_keys_entry = {
.name = "list-keys",
.alias = "lsk",
.args = { "1aF:NO:P:rT:", 0, 1, NULL },
.usage = "[-1aNr] [-F format] [-O order] [-P prefix-string]"
"[-T key-table] [key]",
.flags = CMD_STARTSERVER|CMD_AFTERHOOK,
.exec = cmd_list_keys_exec
};
static char *
cmd_list_keys_get_prefix(struct args *args)
{
key_code prefix;
if (args_has(args, 'P'))
return (xstrdup(args_get(args, 'P')));
prefix = options_get_number(global_s_options, "prefix");
if (prefix == KEYC_NONE)
return (xstrdup(""));
return (xstrdup(key_string_lookup_key(prefix, 0)));
}
static u_int
cmd_list_keys_get_width(struct key_binding **l, u_int n)
{
u_int i, width, keywidth = 0;
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0));
if (width > keywidth)
keywidth = width;
}
return (keywidth);
}
static u_int
cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
{
u_int i, width, tablewidth = 0;
for (i = 0; i < n; i++) {
width = utf8_cstrwidth(l[i]->tablename);
if (width > tablewidth)
tablewidth = width;
}
return (tablewidth);
}
static struct key_binding **
cmd_list_keys_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
{
const char *tables[] = { "prefix", "root" };
struct key_table *t;
struct key_binding **lt;
u_int i, ltsz, len = 0, offset = 0;
static struct key_binding **l = NULL;
static u_int lsz = 0;
for (i = 0; i < nitems(tables); i++) {
t = key_bindings_get_table(tables[i], 0);
lt = sort_get_key_bindings_table(t, &ltsz, sort_crit);
len += ltsz;
if (lsz <= len) {
lsz = len + 100;
l = xreallocarray(l, lsz, sizeof *l);
}
memcpy(l + offset, lt, ltsz * sizeof *l);
offset += ltsz;
}
*n = len;
return (l);
}
static void
cmd_list_keys_filter_key_list(int filter_notes, int filter_key, key_code only,
struct key_binding **l, u_int *n)
{
key_code key;
u_int i, j = 0;
for (i = 0; i < *n; i++) {
key = l[i]->key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
if (filter_key && only != key)
continue;
if (filter_notes && l[i]->note == NULL)
continue;
l[j++] = l[i];
}
*n = j;
}
static void
cmd_list_keys_format_add_key_binding(struct format_tree *ft,
const struct key_binding *bd, const char *prefix)
{
char *s;
if (bd->flags & KEY_BINDING_REPEAT)
format_add(ft, "key_repeat", "1");
else
format_add(ft, "key_repeat", "0");
if (bd->note != NULL)
format_add(ft, "key_note", "%s", bd->note);
else
format_add(ft, "key_note", "%s", "");
format_add(ft, "key_prefix", "%s", prefix);
format_add(ft, "key_table", "%s", bd->tablename);
format_add(ft, "key_string", "%s", key_string_lookup_key(bd->key, 0));
s = cmd_list_print(bd->cmdlist, CMD_LIST_PRINT_ESCAPED|
CMD_LIST_PRINT_NO_GROUPS);
format_add(ft, "key_command", "%s", s);
free(s);
}
static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct format_tree *ft;
struct key_table *table = NULL;
struct key_binding **l;
key_code only = KEYC_UNKNOWN;
const char *template, *tablename, *keystr;
char *line;
char *prefix = NULL;
u_int i, n;
int single, notes_only, filter_notes, filter_key;
struct sort_criteria sort_crit;
if ((keystr = args_string(args, 0)) != NULL) {
only = key_string_lookup_string(keystr);
if (only == KEYC_UNKNOWN) {
cmdq_error(item, "invalid key: %s", keystr);
return (CMD_RETURN_ERROR);
}
only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS);
}
sort_crit.order = sort_order_from_string(args_get(args, 'O'));
if (sort_crit.order == SORT_END && args_has(args, 'O')) {
cmdq_error(item, "invalid sort order");
return (CMD_RETURN_ERROR);
}
sort_crit.reversed = args_has(args, 'r');
if ((tablename = args_get(args, 'T')) != NULL) {
table = key_bindings_get_table(tablename, 0);
if (table == NULL) {
cmdq_error(item, "table %s doesn't exist", tablename);
return (CMD_RETURN_ERROR);
}
}
prefix = cmd_list_keys_get_prefix(args);
single = args_has(args, '1');
notes_only = args_has(args, 'N');
if ((template = args_get(args, 'F')) == NULL)
template = LIST_KEYS_TEMPLATE;
if (table)
l = sort_get_key_bindings_table(table, &n, &sort_crit);
else if (notes_only)
l = cmd_list_keys_get_root_and_prefix(&n, &sort_crit);
else
l = sort_get_key_bindings(&n, &sort_crit);
filter_notes = notes_only && !args_has(args, 'a');
filter_key = only != KEYC_UNKNOWN;
if (filter_notes || filter_key) {
cmd_list_keys_filter_key_list(filter_notes, filter_key, only, l,
&n);
}
if (filter_key && n == 0) {
cmdq_error(item, "unknown key: %s", keystr);
free(prefix);
return (CMD_RETURN_ERROR);
}
if (single && n > 1)
n = 1;
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
format_defaults(ft, tc, NULL, NULL, NULL);
format_add(ft, "notes_only", "%d", notes_only);
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
format_add(ft, "key_table_width", "%u",
cmd_list_keys_get_table_width(l, n));
for (i = 0; i < n; i++) {
cmd_list_keys_format_add_key_binding(ft, l[i], prefix);
line = format_expand(ft, template);
if (single && tc != NULL && (~tc->flags & CLIENT_CONTROL))
status_message_set(tc, -1, 1, 0, 0, "%s", line);
else if (*line != '\0')
cmdq_print(item, "%s", line);
free(line);
if (single)
break;
}
format_free(ft);
free(prefix);
return (CMD_RETURN_NORMAL);
}