#include "keybind.h" #include "line-editor.h" #include #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)