Files
bshell/bshell.runtime/line-editor/keybind.c
T

224 lines
5.1 KiB
C
Raw Normal View History

#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)