From: Michael D. Lowis Date: Wed, 24 May 2017 12:54:52 +0000 (-0400) Subject: Implement mouse chording like acme X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=7514a017754b0647fb00ee7fb5b4ee0eb65536eb;p=projs%2Ftide.git Implement mouse chording like acme --- diff --git a/Makefile b/Makefile index 4714a19..1416b86 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,9 @@ tests/xedit: tests/xedit.o libedit.a tests/xpick: tests/xpick.o libedit.a tests/term: tests/term.o libedit.a +%: %.o + $(LD) -o $@ $^ $(LDFLAGS) + # load generate dependencies -include *.d lib/*.d tests/*.d tests/lib/*.d diff --git a/TODO.md b/TODO.md index d639ff9..beb9d2b 100644 --- a/TODO.md +++ b/TODO.md @@ -2,37 +2,23 @@ Up Next: -* Run commands in the background and don't block the main thread. -* refactor selection handling to buf.c to prepare for multiple selections. * Make Fn keys execute nth command in the tags buffers -* 100% coverage with unit and unit-integration tests -* Status line should omit characters from beginning of path to make file path fit -* right click to fetch file or line - -Straight-up Bugs: - -* tab inserts dont coalesce like one would expect +* arrow keys with selection should clear the selection instead of moving. +* move by words is inconsistent. Example: + var infoId = 'readerinfo'+reader.id; The Future: +* refactor selection handling to buf.c to prepare for multiple selections. +* 100% coverage with unit and unit-integration tests +* right click to fetch file or line +* tab inserts dont coalesce like one would expect +* Run commands in the background and don't block the main thread. * shortcut to repeat previous operation * add command line flags to toggle options (Tabs, Indent, etc..) * add command env vars to set options (Tabs, Indent, etc..) * implement command diffing logic to optimize the undo/redo log - -# Mouse Chords - -Mouse Sequence Description --------------------------------------------------------------------------------- -Pl Rl Null Selection -Pl Rl Pl Rl Select by context -Pl Rl Pl Rl Pl Rl Select big word -Pl Pm Cut line or selection -Pl Pr Paste -Pm Rm Execute text -Pm Pr Cancel the execution that would occur -Pr Rr Search -Pr Pm Cancel the search that would occur +* Status line should omit characters from beginning of path to make file path fit # Auxillary Programs diff --git a/inc/win.h b/inc/win.h index a2a8bba..77b464a 100644 --- a/inc/win.h +++ b/inc/win.h @@ -1,3 +1,11 @@ +enum { + MouseLeft = 1, + MouseMiddle = 2, + MouseRight = 3, + MouseWheelUp = 4, + MouseWheelDn = 5 +}; + typedef enum { STATUS = 0, TAGS = 1, @@ -43,7 +51,7 @@ void win_warpptr(WinRegion id); View* win_view(WinRegion id); Buf* win_buf(WinRegion id); Sel* win_sel(WinRegion id); -bool win_btnpressed(MouseBtn btn); +bool win_btnpressed(int btn); WinRegion win_getregion(void); bool win_setregion(WinRegion id); void win_setscroll(double offset, double visible); @@ -55,7 +63,7 @@ void onfocus(bool focused); void onupdate(void); void onlayout(void); void onscroll(double percent); -void onmouseleft(WinRegion id, size_t count, size_t row, size_t col); -void onmousemiddle(WinRegion id, size_t count, size_t row, size_t col); -void onmouseright(WinRegion id, size_t count, size_t row, size_t col); +void onmouseleft(WinRegion id, bool pressed, size_t row, size_t col); +void onmousemiddle(WinRegion id, bool pressed, size_t row, size_t col); +void onmouseright(WinRegion id, bool pressed, size_t row, size_t col); diff --git a/inc/x11.h b/inc/x11.h index 4b60c18..a758842 100644 --- a/inc/x11.h +++ b/inc/x11.h @@ -1,25 +1,10 @@ -typedef enum { - MOUSE_ACT_UP, - MOUSE_ACT_DOWN, - MOUSE_ACT_MOVE -} MouseAct; - -typedef enum { - MOUSE_BTN_LEFT = 0, - MOUSE_BTN_MIDDLE = 1, - MOUSE_BTN_RIGHT = 2, - MOUSE_BTN_WHEELUP = 3, - MOUSE_BTN_WHEELDOWN = 4, - MOUSE_BTN_NONE = 5, - MOUSE_BTN_COUNT = 6 -} MouseBtn; - typedef struct { void (*redraw)(int width, int height); void (*handle_key)(int mods, uint32_t rune); - void (*handle_mouse)(MouseAct act, MouseBtn btn, int x, int y); void (*shutdown)(void); void (*set_focus)(bool focus); + void (*mouse_drag)(int state, int x, int y); + void (*mouse_btn)(int state, bool pressed, int x, int y); uint32_t palette[16]; } XConfig; @@ -130,7 +115,7 @@ enum { void x11_init(XConfig* cfg); void x11_deinit(void); -int x11_keymods(void); +int x11_keybtnstate(void); bool x11_keymodsset(int mask); void x11_window(char* name, int width, int height); void x11_dialog(char* name, int height, int width); diff --git a/lib/win.c b/lib/win.c index 9982ccc..0f2ac7b 100644 --- a/lib/win.c +++ b/lib/win.c @@ -5,20 +5,14 @@ #include #include -typedef struct { - uint64_t time; - uint8_t count; - bool pressed; - int region; -} ButtonState; - -static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols); -static WinRegion getregion(size_t x, size_t y); static void onredraw(int height, int width); static void oninput(int mods, Rune key); -static void onmouse(MouseAct act, MouseBtn btn, int x, int y); -static void onwheelup(WinRegion id, size_t count, size_t row, size_t col); -static void onwheeldn(WinRegion id, size_t count, size_t row, size_t col); +static void onmousedrag(int state, int x, int y); +static void onmousebtn(int btn, bool pressed, int x, int y); +static void onwheelup(WinRegion id, bool pressed, size_t row, size_t col); +static void onwheeldn(WinRegion id, bool pressed, size_t row, size_t col); +static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols); +static WinRegion getregion(size_t x, size_t y); static size_t Ruler = 0; static double ScrollOffset = 0.0; @@ -27,23 +21,14 @@ static XFont Font; static XConfig Config = { .redraw = onredraw, .handle_key = oninput, - .handle_mouse = onmouse, .shutdown = onshutdown, .set_focus = onfocus, + .mouse_drag = onmousedrag, + .mouse_btn = onmousebtn, .palette = COLOR_PALETTE }; - -void (*MouseActs[MOUSE_BTN_COUNT])(WinRegion id, size_t count, size_t row, size_t col) = { - [MOUSE_BTN_LEFT] = onmouseleft, - [MOUSE_BTN_MIDDLE] = onmousemiddle, - [MOUSE_BTN_RIGHT] = onmouseright, - [MOUSE_BTN_WHEELUP] = onwheelup, - [MOUSE_BTN_WHEELDOWN] = onwheeldn, -}; - static WinRegion Focused = EDIT; static Region Regions[NREGIONS] = {0}; -static ButtonState MouseBtns[MOUSE_BTN_COUNT] = {0}; KeyBinding* Keys = NULL; static void win_init(void (*errfn)(char*)) { @@ -67,10 +52,14 @@ static bool update_focus(void) { static int prev_x = 0, prev_y = 0; int ptr_x, ptr_y; bool changed = false; - x11_mouse_get(&ptr_x, &ptr_y); - if (prev_x != ptr_x || prev_y != ptr_y) - changed = win_setregion(getregion(ptr_x, ptr_y)); - prev_x = ptr_x, prev_y = ptr_y; + /* dont change focus if any mouse buttons are pressed */ + if ((x11_keybtnstate() & 0x1f00) == 0) + { + x11_mouse_get(&ptr_x, &ptr_y); + if (prev_x != ptr_x || prev_y != ptr_y) + changed = win_setregion(getregion(ptr_x, ptr_y)); + prev_x = ptr_x, prev_y = ptr_y; + } return changed; } @@ -108,8 +97,9 @@ void win_setkeys(KeyBinding* bindings) { Keys = bindings; } -bool win_btnpressed(MouseBtn btn) { - return MouseBtns[btn].pressed; +bool win_btnpressed(int btn) { + int btnmask = (1 << (btn + 7)); + return ((x11_keybtnstate() & btnmask) == btnmask); } WinRegion win_getregion(void) { @@ -262,79 +252,68 @@ static void oninput(int mods, Rune key) { } } -static void onclick(MouseAct act, MouseBtn btn, int x, int y) { - WinRegion id = getregion(x, y); - size_t row = (y-Regions[id].y) / x11_font_height(Font); - size_t col = (x-Regions[id].x) / x11_font_width(Font); - if (id == SCROLL) { - id = EDIT; - switch (btn) { - case MOUSE_BTN_LEFT: +static void scroll_actions(int btn, bool pressed, int x, int y) { + size_t row = (y-Regions[SCROLL].y) / x11_font_height(Font); + size_t col = (x-Regions[SCROLL].x) / x11_font_width(Font); + switch (btn) { + case MouseLeft: + if (pressed) view_scroll(win_view(EDIT), -row); - break; - case MOUSE_BTN_MIDDLE: + break; + case MouseMiddle: + if (pressed) onscroll((double)(y - Regions[SCROLL].y) / (double)(Regions[SCROLL].height - Regions[SCROLL].y)); - break; - case MOUSE_BTN_RIGHT: + break; + case MouseRight: + if (pressed) view_scroll(win_view(EDIT), +row); - break; - case MOUSE_BTN_WHEELUP: - view_scroll(win_view(id), -ScrollLines); - break; - case MOUSE_BTN_WHEELDOWN: - view_scroll(win_view(id), +ScrollLines); - break; - default: - break; - } - } else if (MouseActs[btn]) { - MouseActs[btn](MouseBtns[btn].region, MouseBtns[btn].count, row, col); + break; + case MouseWheelUp: + view_scroll(win_view(EDIT), -ScrollLines); + break; + case MouseWheelDn: + view_scroll(win_view(EDIT), +ScrollLines); + break; } } -static void onmouse(MouseAct act, MouseBtn btn, int x, int y) { +static void onmousedrag(int state, int x, int y) { WinRegion id = getregion(x, y); size_t row = (y-Regions[id].y) / x11_font_height(Font); size_t col = (x-Regions[id].x) / x11_font_width(Font); - if (act == MOUSE_ACT_MOVE) { - if (MouseBtns[MOUSE_BTN_LEFT].pressed) { - WinRegion selid = MouseBtns[MOUSE_BTN_LEFT].region; - if (MouseBtns[MOUSE_BTN_LEFT].count == 1) { - view_setcursor(win_view(selid), row, col); - MouseBtns[MOUSE_BTN_LEFT].count = 0; - } else { - view_selext(win_view(selid), row, col); - } - } + if (id == Focused && win_btnpressed(MouseLeft)) + view_selext(win_view(id), row, col); +} + +static void onmousebtn(int btn, bool pressed, int x, int y) { + WinRegion id = getregion(x, y); + size_t row = (y-Regions[id].y) / x11_font_height(Font); + size_t col = (x-Regions[id].x) / x11_font_width(Font); + + if (id == SCROLL) { + scroll_actions(btn, pressed, x, y); } else { - MouseBtns[btn].pressed = (act == MOUSE_ACT_DOWN); - if (MouseBtns[btn].pressed) { - /* update the number of clicks and click time */ - uint32_t now = getmillis(); - uint32_t elapsed = now - MouseBtns[btn].time; - MouseBtns[btn].time = now; - MouseBtns[btn].region = id; - if (elapsed <= 250) - MouseBtns[btn].count++; - else - MouseBtns[btn].count = 1; - } else if (MouseBtns[btn].count > 0) { - onclick(act, btn, x, y); + switch(btn) { + case MouseLeft: onmouseleft(id, pressed, row, col); break; + case MouseMiddle: onmousemiddle(id, pressed, row, col); break; + case MouseRight: onmouseright(id, pressed, row, col); break; + case MouseWheelUp: onwheelup(id, pressed, row, col); break; + case MouseWheelDn: onwheeldn(id, pressed, row, col); break; } } } -static void onwheelup(WinRegion id, size_t count, size_t row, size_t col) { +static void onwheelup(WinRegion id, bool pressed, size_t row, size_t col) { + if (!pressed) return; view_scroll(win_view(id), -ScrollLines); } -static void onwheeldn(WinRegion id, size_t count, size_t row, size_t col) { +static void onwheeldn(WinRegion id, bool pressed, size_t row, size_t col) { + if (!pressed) return; view_scroll(win_view(id), +ScrollLines); } -/*****************************************************************************/ - static void draw_glyphs(size_t x, size_t y, UGlyph* glyphs, size_t rlen, size_t ncols) { XGlyphSpec specs[rlen]; size_t i = 0; @@ -369,4 +348,3 @@ static WinRegion getregion(size_t x, size_t y) { } return NREGIONS; } - diff --git a/lib/x11.c b/lib/x11.c index 2292c14..0278464 100644 --- a/lib/x11.c +++ b/lib/x11.c @@ -50,7 +50,7 @@ static struct { GC gc; } X; static XConfig* Config; -static int Mods; +static int KeyBtnState; static Atom SelTarget; static struct XSel { char* name; @@ -98,12 +98,12 @@ void x11_init(XConfig* cfg) { SelTarget = XInternAtom(X.display, "STRING", 0); } -int x11_keymods(void) { - return Mods; +int x11_keybtnstate(void) { + return KeyBtnState; } bool x11_keymodsset(int mask) { - return ((Mods & mask) == mask); + return ((KeyBtnState & mask) == mask); } void x11_window(char* name, int width, int height) { @@ -274,37 +274,24 @@ static uint32_t getkey(XEvent* e) { static void handle_key(XEvent* event) { uint32_t key = getkey(event); - Mods = event->xkey.state & ModAny; + KeyBtnState = event->xkey.state; if (key == RUNE_ERR) return; - Config->handle_key(Mods, key); + Config->handle_key(KeyBtnState, key); } static void handle_mouse(XEvent* e) { - MouseAct action; - MouseBtn button; int x = 0, y = 0; if (e->type == MotionNotify) { - action = MOUSE_ACT_MOVE; - button = MOUSE_BTN_LEFT; + KeyBtnState = e->xmotion.state; x = e->xmotion.x; y = e->xmotion.y; + Config->mouse_drag(KeyBtnState, x, y); } else { - Mods = e->xbutton.state & ModAny; - action = (e->type == ButtonPress ? MOUSE_ACT_DOWN : MOUSE_ACT_UP); - /* set the button id */ - switch (e->xbutton.button) { - case Button1: button = MOUSE_BTN_LEFT; break; - case Button2: button = MOUSE_BTN_MIDDLE; break; - case Button3: button = MOUSE_BTN_RIGHT; break; - case Button4: button = MOUSE_BTN_WHEELUP; break; - case Button5: button = MOUSE_BTN_WHEELDOWN; break; - default: button = MOUSE_BTN_NONE; break; - } + KeyBtnState = e->xbutton.state; x = e->xbutton.x; y = e->xbutton.y; + Config->mouse_btn(e->xbutton.button, (e->type == ButtonPress), x, y); } - /* pass the data to the app */ - Config->handle_mouse(action, button, x, y); } static void set_focus(bool focused) { diff --git a/term.c b/term.c index 33d589b..a8ab016 100644 --- a/term.c +++ b/term.c @@ -5,20 +5,16 @@ #include #include -void onmouseleft(WinRegion id, size_t count, size_t row, size_t col) { - +void onmouseleft(WinRegion id, bool pressed, size_t row, size_t col) { } -void onmousemiddle(WinRegion id, size_t count, size_t row, size_t col) { - +void onmousemiddle(WinRegion id, bool pressed, size_t row, size_t col) { } -void onmouseright(WinRegion id, size_t count, size_t row, size_t col) { - +void onmouseright(WinRegion id, bool pressed, size_t row, size_t col) { } void onscroll(double percent) { - } void onfocus(bool focused) { @@ -35,7 +31,6 @@ void onshutdown(void) { } void onerror(char* msg) { - } #ifndef TEST diff --git a/xedit.c b/xedit.c index 2d92983..0897ea9 100644 --- a/xedit.c +++ b/xedit.c @@ -174,7 +174,14 @@ static void reload(void) { /* Mouse Handling ******************************************************************************/ -void onmouseleft(WinRegion id, size_t count, size_t row, size_t col) { +void onmouseleft(WinRegion id, bool pressed, size_t row, size_t col) { + static int count = 0; + static uint64_t before = 0; + if (!pressed) return; + uint64_t now = getmillis(); + count = ((now-before) <= 250 ? count+1 : 1); + before = now; + if (count == 1) { if (x11_keymodsset(ModShift)) view_selext(win_view(id), row, col); @@ -187,8 +194,9 @@ void onmouseleft(WinRegion id, size_t count, size_t row, size_t col) { } } -void onmousemiddle(WinRegion id, size_t count, size_t row, size_t col) { - if (win_btnpressed(MOUSE_BTN_LEFT)) { +void onmousemiddle(WinRegion id, bool pressed, size_t row, size_t col) { + if (pressed) return; + if (win_btnpressed(MouseLeft)) { cut(); } else { char* str = view_fetch(win_view(id), row, col); @@ -197,8 +205,9 @@ void onmousemiddle(WinRegion id, size_t count, size_t row, size_t col) { } } -void onmouseright(WinRegion id, size_t count, size_t row, size_t col) { - if (win_btnpressed(MOUSE_BTN_LEFT)) { +void onmouseright(WinRegion id, bool pressed, size_t row, size_t col) { + if (pressed) return; + if (win_btnpressed(MouseLeft)) { paste(); } else { SearchDir *= (x11_keymodsset(ModShift) ? -1 : +1); diff --git a/xpick.c b/xpick.c index cf117ae..9d7b309 100644 --- a/xpick.c +++ b/xpick.c @@ -112,13 +112,13 @@ static void score(void) { vec_sort(&Choices, by_score); } -void onmouseleft(WinRegion id, size_t count, size_t row, size_t col) { +void onmouseleft(WinRegion id, bool pressed, size_t row, size_t col) { } -void onmousemiddle(WinRegion id, size_t count, size_t row, size_t col) { +void onmousemiddle(WinRegion id, bool pressed, size_t row, size_t col) { } -void onmouseright(WinRegion id, size_t count, size_t row, size_t col) { +void onmouseright(WinRegion id, bool pressed, size_t row, size_t col) { } void onscroll(double percent) {