mirror of
https://github.com/tmux/tmux.git
synced 2025-09-02 21:56:57 +00:00
Support for multiple key tables to commands to be bound to sequences of
keys. The default key bindings become the "prefix" table and -n the "root" table. Keys may be bound in new tables with bind -T and switch-client -T used to specify the table in which the next key should be looked up. Based on a diff from Keith Amling.
This commit is contained in:
112
key-bindings.c
112
key-bindings.c
@ -25,60 +25,120 @@
|
||||
#include "tmux.h"
|
||||
|
||||
RB_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
|
||||
RB_GENERATE(key_tables, key_table, entry, key_table_cmp);
|
||||
struct key_tables key_tables = RB_INITIALIZER(&key_tables);
|
||||
|
||||
struct key_bindings key_bindings;
|
||||
int
|
||||
key_table_cmp(struct key_table *e1, struct key_table *e2)
|
||||
{
|
||||
return (strcmp(e1->name, e2->name));
|
||||
}
|
||||
|
||||
int
|
||||
key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
|
||||
{
|
||||
int key1, key2;
|
||||
|
||||
key1 = bd1->key & ~KEYC_PREFIX;
|
||||
key2 = bd2->key & ~KEYC_PREFIX;
|
||||
if (key1 != key2)
|
||||
return (key1 - key2);
|
||||
|
||||
if (bd1->key & KEYC_PREFIX && !(bd2->key & KEYC_PREFIX))
|
||||
return (-1);
|
||||
if (bd2->key & KEYC_PREFIX && !(bd1->key & KEYC_PREFIX))
|
||||
return (1);
|
||||
return (0);
|
||||
return (bd1->key - bd2->key);
|
||||
}
|
||||
|
||||
struct key_binding *
|
||||
key_bindings_lookup(int key)
|
||||
struct key_table *
|
||||
key_bindings_get_table(const char *name, int create)
|
||||
{
|
||||
struct key_binding bd;
|
||||
struct key_table table_find, *table;
|
||||
|
||||
bd.key = key;
|
||||
return (RB_FIND(key_bindings, &key_bindings, &bd));
|
||||
table_find.name = name;
|
||||
table = RB_FIND(key_tables, &key_tables, &table_find);
|
||||
if (table != NULL || !create)
|
||||
return (table);
|
||||
|
||||
table = xmalloc(sizeof *table);
|
||||
table->name = xstrdup(name);
|
||||
RB_INIT(&table->key_bindings);
|
||||
|
||||
table->references = 1; /* one reference in key_tables */
|
||||
RB_INSERT(key_tables, &key_tables, table);
|
||||
|
||||
return (table);
|
||||
}
|
||||
|
||||
void
|
||||
key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist)
|
||||
key_bindings_unref_table(struct key_table *table)
|
||||
{
|
||||
struct key_binding *bd;
|
||||
|
||||
key_bindings_remove(key);
|
||||
if (--table->references != 0)
|
||||
return;
|
||||
|
||||
while (!RB_EMPTY(&table->key_bindings)) {
|
||||
bd = RB_ROOT(&table->key_bindings);
|
||||
RB_REMOVE(key_bindings, &table->key_bindings, bd);
|
||||
cmd_list_free(bd->cmdlist);
|
||||
free(bd);
|
||||
}
|
||||
|
||||
free((void *)table->name);
|
||||
free(table);
|
||||
}
|
||||
|
||||
void
|
||||
key_bindings_add(const char *name, int key, int can_repeat,
|
||||
struct cmd_list *cmdlist)
|
||||
{
|
||||
struct key_table *table;
|
||||
struct key_binding bd_find, *bd;
|
||||
|
||||
table = key_bindings_get_table(name, 1);
|
||||
|
||||
bd_find.key = key;
|
||||
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
|
||||
if (bd != NULL) {
|
||||
RB_REMOVE(key_bindings, &table->key_bindings, bd);
|
||||
cmd_list_free(bd->cmdlist);
|
||||
free(bd);
|
||||
}
|
||||
|
||||
bd = xmalloc(sizeof *bd);
|
||||
bd->key = key;
|
||||
RB_INSERT(key_bindings, &key_bindings, bd);
|
||||
RB_INSERT(key_bindings, &table->key_bindings, bd);
|
||||
|
||||
bd->can_repeat = can_repeat;
|
||||
bd->cmdlist = cmdlist;
|
||||
}
|
||||
|
||||
void
|
||||
key_bindings_remove(int key)
|
||||
key_bindings_remove(const char *name, int key)
|
||||
{
|
||||
struct key_binding *bd;
|
||||
struct key_table *table;
|
||||
struct key_binding bd_find, *bd;
|
||||
|
||||
if ((bd = key_bindings_lookup(key)) == NULL)
|
||||
table = key_bindings_get_table(name, 0);
|
||||
if (table == NULL)
|
||||
return;
|
||||
RB_REMOVE(key_bindings, &key_bindings, bd);
|
||||
|
||||
bd_find.key = key;
|
||||
bd = RB_FIND(key_bindings, &table->key_bindings, &bd_find);
|
||||
if (bd == NULL)
|
||||
return;
|
||||
|
||||
RB_REMOVE(key_bindings, &table->key_bindings, bd);
|
||||
cmd_list_free(bd->cmdlist);
|
||||
free(bd);
|
||||
|
||||
if (RB_EMPTY(&table->key_bindings)) {
|
||||
RB_REMOVE(key_tables, &key_tables, table);
|
||||
key_bindings_unref_table(table);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
key_bindings_remove_table(const char *name)
|
||||
{
|
||||
struct key_table *table;
|
||||
|
||||
table = key_bindings_get_table(name, 0);
|
||||
if (table != NULL) {
|
||||
RB_REMOVE(key_tables, &key_tables, table);
|
||||
key_bindings_unref_table(table);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -169,8 +229,6 @@ key_bindings_init(void)
|
||||
int error;
|
||||
struct cmd_q *cmdq;
|
||||
|
||||
RB_INIT(&key_bindings);
|
||||
|
||||
cmdq = cmdq_new(NULL);
|
||||
for (i = 0; i < nitems(defaults); i++) {
|
||||
error = cmd_string_parse(defaults[i], &cmdlist,
|
||||
|
Reference in New Issue
Block a user