From c4850c5bded85fa41091207432c62dfbc718d7c3 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Tue, 29 Nov 2016 09:37:51 -0500 Subject: [PATCH] Added real modifier support to keybindings and added keybindings for common non-modal editing shortcuts --- inc/edit.h | 5 +- inc/x11.h | 15 +++++- libedit/utils.c | 6 ++- libx/x11.c | 53 ++++++++++++++-------- xedit.c | 118 ++++++++++++++++++++++++++++++++++++------------ xpick.c | 4 +- 6 files changed, 145 insertions(+), 56 deletions(-) diff --git a/inc/edit.h b/inc/edit.h index 562f593..c3a67d4 100644 --- a/inc/edit.h +++ b/inc/edit.h @@ -7,7 +7,7 @@ typedef struct { FMap fmap(char* path); void funmap(FMap file); -uint32_t getmillis(void); +uint64_t getmillis(void); bool risword(Rune r); bool risblank(Rune r); char* stringdup(const char* str); @@ -214,12 +214,13 @@ enum ColorId { /* Global State *****************************************************************************/ typedef struct { + int mods; Rune key; void (*action)(void); } KeyBinding; typedef struct { - uint32_t time; + uint64_t time; uint8_t count; bool pressed; int region; diff --git a/inc/x11.h b/inc/x11.h index a3ee126..3d3e788 100644 --- a/inc/x11.h +++ b/inc/x11.h @@ -16,7 +16,7 @@ typedef enum { typedef struct { void (*redraw)(int width, int height); - void (*handle_key)(uint32_t rune); + void (*handle_key)(int mods, uint32_t rune); void (*handle_mouse)(MouseAct act, MouseBtn btn, int x, int y); uint32_t palette[16]; } XConfig; @@ -107,6 +107,19 @@ enum Keys { KEY_CTRL_UNDERSCORE = 0x1F, }; +/* Key modifier masks */ +enum { + ModNone = 0, + ModShift = (1 << 0), + ModCapsLock = (1 << 1), + ModCtrl = (1 << 2), + ModAlt = (1 << 3), + ModNumLock = (1 << 4), + ModScrollLock = (1 << 5), + ModWindows = (1 << 6), + ModAny = ModWindows-1 +}; + void x11_init(XConfig* cfg); void x11_deinit(void); void x11_window(char* name, int width, int height); diff --git a/libedit/utils.c b/libedit/utils.c index 154b34b..674c704 100644 --- a/libedit/utils.c +++ b/libedit/utils.c @@ -10,10 +10,12 @@ #include #include -uint32_t getmillis(void) { +uint64_t getmillis(void) { struct timespec time; clock_gettime(CLOCK_MONOTONIC, &time); - return ((time.tv_sec * 1000) + (time.tv_nsec / 1000000)); + uint64_t ms = ((uint64_t)time.tv_sec * (uint64_t)1000); + ms += ((uint64_t)time.tv_nsec / (uint64_t)1000000); + return ms; } void die(const char* msgfmt, ...) { diff --git a/libx/x11.c b/libx/x11.c index d700c7b..f0fffb0 100644 --- a/libx/x11.c +++ b/libx/x11.c @@ -133,23 +133,7 @@ void x11_show(void) { XMapWindow(X.display, X.window); } -static uint32_t getkey(XEvent* e) { - uint32_t rune = 0xFFFD; - size_t len = 0; - char buf[8]; - KeySym key; - Status status; - /* Read the key string */ - if (X.xic) - len = Xutf8LookupString(X.xic, &e->xkey, buf, sizeof(buf), &key, &status); - else - len = XLookupString(&e->xkey, buf, sizeof(buf), &key, 0); - /* decode it */ - if (len > 0) { - len = 0; - for(int i = 0; i < 8 && !utf8decode(&rune, &len, buf[i]); i++); - } - /* translate the key code into a unicode codepoint */ +static uint32_t special_keys(uint32_t key) { switch (key) { case XK_F1: return KEY_F1; case XK_F2: return KEY_F2; @@ -173,8 +157,39 @@ static uint32_t getkey(XEvent* e) { case XK_Down: return KEY_DOWN; case XK_Left: return KEY_LEFT; case XK_Right: return KEY_RIGHT; - default: return rune; + default: return key; + } +} + +static uint32_t getkey(XEvent* e) { + uint32_t rune = RUNE_ERR; + size_t len = 0; + char buf[8]; + KeySym key; + Status status; + /* Read the key string */ + if (X.xic) + len = Xutf8LookupString(X.xic, &e->xkey, buf, sizeof(buf), &key, &status); + else + len = XLookupString(&e->xkey, buf, sizeof(buf), &key, 0); + /* if it's ascii, just return it */ + if (key >= 0x20 && key <= 0x7F) + return (uint32_t)key; + /* decode it */ + if (len > 0) { + len = 0; + for (int i = 0; i < 8 && !utf8decode(&rune, &len, buf[i]); i++); } + /* translate special key codes into unicode codepoints */ + key = special_keys(key); + return rune; +} + +static void handle_key(XEvent* event) { + uint32_t key = getkey(event); + int mods = event->xkey.state & ModAny; + if (key == RUNE_ERR) return; + Config->handle_key(mods, key); } static void handle_mouse(XEvent* e) { @@ -214,7 +229,7 @@ void x11_loop(void) { switch (e.type) { case FocusIn: if (X.xic) XSetICFocus(X.xic); break; case FocusOut: if (X.xic) XUnsetICFocus(X.xic); break; - case KeyPress: Config->handle_key(getkey(&e)); break; + case KeyPress: handle_key(&e); break; case ButtonRelease: handle_mouse(&e); break; case ButtonPress: handle_mouse(&e); break; case MotionNotify: handle_mouse(&e); break; diff --git a/xedit.c b/xedit.c index f702f0d..c6cb28b 100644 --- a/xedit.c +++ b/xedit.c @@ -14,7 +14,7 @@ enum RegionId { // Input Handlers static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y); static void tag_handler(char* cmd, char* arg); -static void key_handler(Rune key); +static void key_handler(int mods, Rune key); // Drawing Routines static void draw_runes(size_t x, size_t y, int fg, int bg, UGlyph* glyphs, size_t rlen); @@ -47,6 +47,24 @@ static void search(void); static void execute(void); static void find(char* arg); static void open_file(void); +static void delrune_right(void); +static void delword_right(void); +static void delrune_left(void); +static void delword_left(void); +static void cursor_bof(void); +static void select_bol(void); +static void select_bof(void); +static void cursor_eof(void); +static void select_eol(void); +static void select_eof(void); +static void select_up(void); +static void select_dn(void); +static void select_left(void); +static void word_left(void); +static void selword_left(void); +static void select_right(void); +static void word_right(void); +static void selword_right(void); // Tag/Cmd Execution static Tag* tag_lookup(char* cmd); @@ -111,41 +129,60 @@ static KeyBinding Bindings[] = { //{ KEY_CTRL_U, del_to_bol }, //{ KEY_CTRL_W, del_to_bow }, //{ KEY_CTRL_H, del_prev_char}, - { KEY_CTRL_A, cursor_bol }, - { KEY_CTRL_E, cursor_eol }, + { ModCtrl, 'a', cursor_bol }, + { ModCtrl, 'e', cursor_eol }, /* Standard Text Editing Shortcuts */ - { KEY_CTRL_S, save }, - { KEY_CTRL_Z, undo }, - { KEY_CTRL_Y, redo }, - { KEY_CTRL_X, cut }, - { KEY_CTRL_C, copy }, - { KEY_CTRL_V, paste }, + { ModCtrl, 's', save }, + { ModCtrl, 'z', undo }, + { ModCtrl, 'y', redo }, + { ModCtrl, 'x', cut }, + { ModCtrl, 'c', copy }, + { ModCtrl, 'v', paste }, /* Common Special Keys */ - { KEY_DELETE, delete }, - { KEY_HOME, cursor_bol }, - { KEY_END, cursor_eol }, - { KEY_PGUP, page_up }, - { KEY_PGDN, page_dn }, - { KEY_UP, cursor_up }, - { KEY_DOWN, cursor_dn }, - { KEY_LEFT, cursor_left }, - { KEY_RIGHT, cursor_right }, + { ModNone, KEY_PGUP, page_up }, + { ModNone, KEY_PGDN, page_dn }, + { ModNone, KEY_DELETE, delrune_right }, + { ModCtrl, KEY_DELETE, delword_right }, + { ModNone, KEY_BACKSPACE, delrune_left }, + { ModCtrl, KEY_BACKSPACE, delword_left }, + + /* Cursor Movements */ + { ModNone, KEY_HOME, cursor_bol }, + { ModCtrl, KEY_HOME, cursor_bof }, + { ModShift, KEY_HOME, select_bol }, + { ModCtrl|ModShift, KEY_HOME, select_bof }, + { ModNone, KEY_END, cursor_eol }, + { ModCtrl, KEY_END, cursor_eof }, + { ModShift, KEY_END, select_eol }, + { ModCtrl|ModShift, KEY_END, select_eof }, + { ModNone, KEY_UP, cursor_up }, + { ModShift, KEY_UP, select_up }, + { ModNone, KEY_DOWN, cursor_dn }, + { ModShift, KEY_DOWN, select_dn }, + { ModNone, KEY_LEFT, cursor_left }, + { ModShift, KEY_LEFT, select_left }, + { ModCtrl, KEY_LEFT, word_left }, + { ModCtrl|ModShift, KEY_LEFT, selword_left }, + { ModNone, KEY_RIGHT, cursor_right }, + { ModShift, KEY_RIGHT, select_right }, + { ModCtrl, KEY_RIGHT, word_right }, + { ModCtrl|ModShift, KEY_RIGHT, selword_right }, /* Implementation Specific */ - { KEY_ESCAPE, select_prev }, - { KEY_CTRL_T, change_focus }, - { KEY_CTRL_Q, quit }, - { KEY_CTRL_F, search }, - { KEY_CTRL_D, execute }, + { ModAny, KEY_ESCAPE, select_prev }, + { ModCtrl, 't', change_focus }, + { ModCtrl, 'q', quit }, + { ModCtrl, 'f', search }, + { ModCtrl, 'd', execute }, /* Picker Shortcuts */ - { KEY_CTRL_O, open_file }, + { ModCtrl, 'o', open_file }, //{ KEY_CTRL_P, find_ctag }, //{ KEY_CTRL_G, goto_ctag }, - { 0, NULL } + { 0, 0, NULL } // End the table }; /* External Commands @@ -214,7 +251,7 @@ static void mouse_handler(MouseAct act, MouseBtn btn, int x, int y) { } } -static void key_handler(Rune key) { +static void key_handler(int mods, Rune key) { /* ignore invalid keys */ if (key == RUNE_ERR) return; /* handle the proper line endings */ @@ -222,8 +259,10 @@ static void key_handler(Rune key) { if (key == '\n' && currview()->buffer.crlf) key = RUNE_CRLF; /* handle the key */ KeyBinding* bindings = Bindings; + uint32_t mkey = tolower(key); while (bindings->key) { - if (key == bindings->key) { + int keymods = bindings->mods; + if ((mkey == bindings->key) && (keymods == ModAny || keymods == mods)) { bindings->action(); return; } @@ -395,9 +434,9 @@ static void change_focus(void) { } static void quit(void) { - static uint32_t num_clicks = 0; - static uint32_t prevtime = 0; - uint32_t now = getmillis(); + static uint64_t num_clicks = 0; + static uint64_t prevtime = 0; + uint64_t now = getmillis(); num_clicks = (now - prevtime < 250 ? num_clicks+1 : 1); prevtime = now; if (!getbuf(EDIT)->modified || num_clicks >= 2) @@ -472,6 +511,25 @@ static void open_file(void) { free(file); } +static void delrune_right(void){} +static void delword_right(void){} +static void delrune_left(void){} +static void delword_left(void){} +static void cursor_bof(void){} +static void select_bol(void){} +static void select_bof(void){} +static void cursor_eof(void){} +static void select_eol(void){} +static void select_eof(void){} +static void select_up(void){} +static void select_dn(void){} +static void select_left(void){} +static void word_left(void){} +static void selword_left(void){} +static void select_right(void){} +static void word_right(void){} +static void selword_right(void){} + /* Tag/Cmd Execution *****************************************************************************/ static Tag* tag_lookup(char* cmd) { diff --git a/xpick.c b/xpick.c index d77ec44..4a8d070 100644 --- a/xpick.c +++ b/xpick.c @@ -7,7 +7,7 @@ static void redraw(int width, int height); static void mouse_input(MouseAct act, MouseBtn btn, int x, int y); -static void keyboard_input(uint32_t key); +static void keyboard_input(int mods, uint32_t key); typedef struct { float score; @@ -163,7 +163,7 @@ static void mouse_input(MouseAct act, MouseBtn btn, int x, int y) { (void)y; } -static void keyboard_input(uint32_t key) { +static void keyboard_input(int mods, uint32_t key) { switch (key) { case KEY_LEFT: break; case KEY_RIGHT: break; -- 2.52.0