224 lines
5.1 KiB
C
224 lines
5.1 KiB
C
|
|
#include "keybind.h"
|
||
|
|
|
||
|
|
#include "line-editor.h"
|
||
|
|
|
||
|
|
#include <fx/term/tty.h>
|
||
|
|
|
||
|
|
#define KEYCODE_WILDCARD FX_KEY_RESERVED_START
|
||
|
|
|
||
|
|
static FX_BST_DEFINE_SIMPLE_INSERT(
|
||
|
|
struct keymap_entry,
|
||
|
|
e_node,
|
||
|
|
e_code,
|
||
|
|
put_keymap_entry);
|
||
|
|
static FX_BST_DEFINE_SIMPLE_GET(
|
||
|
|
struct keymap_entry,
|
||
|
|
fx_keycode,
|
||
|
|
e_node,
|
||
|
|
e_code,
|
||
|
|
get_keymap_entry);
|
||
|
|
static FX_BST_DEFINE_SIMPLE_INSERT(
|
||
|
|
struct keybind,
|
||
|
|
b_node,
|
||
|
|
b_priority,
|
||
|
|
put_keybind);
|
||
|
|
|
||
|
|
struct bshell_line_ed_keybind_p {
|
||
|
|
bshell_line_ed_keybind *kb_self;
|
||
|
|
struct keybind kb_bind;
|
||
|
|
};
|
||
|
|
|
||
|
|
static void keymap_entry_destroy(struct keymap_entry *entry)
|
||
|
|
{
|
||
|
|
fx_bst_node *cur_bind = fx_bst_first(&entry->e_binds);
|
||
|
|
while (cur_bind) {
|
||
|
|
fx_bst_node *next_bind = fx_bst_next(cur_bind);
|
||
|
|
fx_bst_delete(&entry->e_binds, cur_bind);
|
||
|
|
|
||
|
|
struct keybind *keybind
|
||
|
|
= fx_unbox(struct keybind, cur_bind, b_node);
|
||
|
|
struct bshell_line_ed_keybind_p *keybind_object_p = fx_unbox(
|
||
|
|
struct bshell_line_ed_keybind_p,
|
||
|
|
keybind,
|
||
|
|
kb_bind);
|
||
|
|
bshell_line_ed_keybind_unref(keybind_object_p->kb_self);
|
||
|
|
|
||
|
|
cur_bind = next_bind;
|
||
|
|
}
|
||
|
|
|
||
|
|
free(entry);
|
||
|
|
}
|
||
|
|
|
||
|
|
void line_ed_cleanup_keymap(fx_bst *keymap)
|
||
|
|
{
|
||
|
|
fx_bst_node *cur_entry = fx_bst_first(keymap);
|
||
|
|
while (cur_entry) {
|
||
|
|
fx_bst_node *next_entry = fx_bst_next(cur_entry);
|
||
|
|
fx_bst_delete(keymap, cur_entry);
|
||
|
|
|
||
|
|
struct keymap_entry *keymap_entry
|
||
|
|
= fx_unbox(struct keymap_entry, cur_entry, e_node);
|
||
|
|
|
||
|
|
keymap_entry_destroy(keymap_entry);
|
||
|
|
|
||
|
|
cur_entry = next_entry;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static fx_keycode key_string_to_code(const char *key)
|
||
|
|
{
|
||
|
|
const char *tmp = key;
|
||
|
|
fx_keycode result = 0;
|
||
|
|
|
||
|
|
if (*key == '^') {
|
||
|
|
result |= FX_MOD_CTRL;
|
||
|
|
key++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!strcmp(key, "*")) {
|
||
|
|
result |= KEYCODE_WILDCARD;
|
||
|
|
} else if (!strcmp(key, "left")) {
|
||
|
|
result |= FX_KEY_ARROW_LEFT;
|
||
|
|
} else if (!strcmp(key, "right")) {
|
||
|
|
result |= FX_KEY_ARROW_RIGHT;
|
||
|
|
} else if (!strcmp(key, "up")) {
|
||
|
|
result |= FX_KEY_ARROW_UP;
|
||
|
|
} else if (!strcmp(key, "down")) {
|
||
|
|
result |= FX_KEY_ARROW_DOWN;
|
||
|
|
} else if (!strcmp(key, "backspace")) {
|
||
|
|
result |= FX_KEY_BACKSPACE;
|
||
|
|
} else if (!strcmp(key, "return")) {
|
||
|
|
result |= FX_KEY_RETURN;
|
||
|
|
} else {
|
||
|
|
result |= fx_wchar_utf8_codepoint_decode(key);
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bshell_line_ed_keybind *line_ed_bind_key(
|
||
|
|
struct bshell_line_ed_p *ed,
|
||
|
|
const char *key,
|
||
|
|
bshell_line_ed_keybind_callback_t callback,
|
||
|
|
unsigned long priority)
|
||
|
|
{
|
||
|
|
fx_keycode keycode = key_string_to_code(key);
|
||
|
|
if (!keycode) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
bshell_line_ed_keybind *bind = NULL;
|
||
|
|
bind = bshell_line_ed_keybind_create();
|
||
|
|
|
||
|
|
if (!bind) {
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct bshell_line_ed_keybind_p *bind_p
|
||
|
|
= fx_object_get_private(bind, BSHELL_TYPE_LINE_EDITOR_KEYBIND);
|
||
|
|
|
||
|
|
struct keymap_entry *keymap_entry
|
||
|
|
= get_keymap_entry(&ed->ed_keymap, keycode);
|
||
|
|
if (!keymap_entry) {
|
||
|
|
keymap_entry = calloc(1, sizeof *keymap_entry);
|
||
|
|
if (!keymap_entry) {
|
||
|
|
bshell_line_ed_keybind_unref(bind);
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
keymap_entry->e_code = keycode;
|
||
|
|
put_keymap_entry(&ed->ed_keymap, keymap_entry);
|
||
|
|
}
|
||
|
|
|
||
|
|
bind_p->kb_self = bind;
|
||
|
|
bind_p->kb_bind.b_priority = priority;
|
||
|
|
bind_p->kb_bind.b_callback = callback;
|
||
|
|
put_keybind(&keymap_entry->e_binds, &bind_p->kb_bind);
|
||
|
|
|
||
|
|
return bind;
|
||
|
|
}
|
||
|
|
|
||
|
|
bshell_line_ed_keybind *bshell_line_ed_bind_key(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
const char *key,
|
||
|
|
bshell_line_ed_keybind_callback_t callback,
|
||
|
|
unsigned long priority)
|
||
|
|
{
|
||
|
|
FX_CLASS_DISPATCH_STATIC(
|
||
|
|
BSHELL_TYPE_LINE_EDITOR,
|
||
|
|
line_ed_bind_key,
|
||
|
|
ed,
|
||
|
|
key,
|
||
|
|
callback,
|
||
|
|
priority);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result do_handle_key(
|
||
|
|
struct bshell_line_ed_p *ed,
|
||
|
|
fx_keycode match_code,
|
||
|
|
fx_keycode real_code)
|
||
|
|
{
|
||
|
|
struct bshell_line_ed_keybind_result result
|
||
|
|
= BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
|
||
|
|
struct keymap_entry *keymap_entry
|
||
|
|
= get_keymap_entry(&ed->ed_keymap, match_code);
|
||
|
|
if (!keymap_entry) {
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
fx_bst_node *cur = fx_bst_last(&keymap_entry->e_binds);
|
||
|
|
while (cur) {
|
||
|
|
struct keybind *bind = fx_unbox(struct keybind, cur, b_node);
|
||
|
|
result = bind->b_callback(ed->ed_self, real_code);
|
||
|
|
|
||
|
|
if (result.r_status != BSHELL_SUCCESS
|
||
|
|
|| result.r_action != BSHELL_LINE_EDITOR_KEYBIND_CONTINUE) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
cur = fx_bst_prev(cur);
|
||
|
|
}
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
void line_ed_handle_key(struct bshell_line_ed_p *ed, fx_keycode code)
|
||
|
|
{
|
||
|
|
struct bshell_line_ed_keybind_result result
|
||
|
|
= do_handle_key(ed, code, code);
|
||
|
|
if (result.r_status != BSHELL_SUCCESS
|
||
|
|
|| result.r_action != BSHELL_LINE_EDITOR_KEYBIND_CONTINUE) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (code & FX_MOD_CTRL) {
|
||
|
|
result = do_handle_key(
|
||
|
|
ed,
|
||
|
|
FX_MOD_CTRL | KEYCODE_WILDCARD,
|
||
|
|
code);
|
||
|
|
} else {
|
||
|
|
result = do_handle_key(ed, KEYCODE_WILDCARD, code);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void fini(fx_object *obj, void *priv)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
FX_TYPE_CLASS_BEGIN(bshell_line_ed_keybind)
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
|
||
|
|
FX_INTERFACE_ENTRY(to_string) = NULL;
|
||
|
|
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
|
||
|
|
FX_TYPE_CLASS_END(bshell_line_ed_keybind)
|
||
|
|
|
||
|
|
FX_TYPE_DEFINITION_BEGIN(bshell_line_ed_keybind)
|
||
|
|
FX_TYPE_ID(0x5d842371, 0x40a3, 0x4683, 0xaff2, 0x05daa53d36b7);
|
||
|
|
FX_TYPE_NAME("bshell.line_editor.keybind");
|
||
|
|
FX_TYPE_CLASS(bshell_line_ed_keybind_class);
|
||
|
|
FX_TYPE_INSTANCE_PRIVATE(struct bshell_line_ed_keybind_p);
|
||
|
|
FX_TYPE_INSTANCE_FINI(fini);
|
||
|
|
FX_TYPE_DEFINITION_END(bshell_line_ed_keybind)
|