]> git.mdlowis.com Git - projs/tide.git/commitdiff
Implement mouse chording like acme
authorMichael D. Lowis <mike.lowis@gentex.com>
Wed, 24 May 2017 12:54:52 +0000 (08:54 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Wed, 24 May 2017 12:54:52 +0000 (08:54 -0400)
Makefile
TODO.md
inc/win.h
inc/x11.h
lib/win.c
lib/x11.c
term.c
xedit.c
xpick.c

index 4714a198ee41ef5c54b3110d4e54977ca0ceaedf..1416b86d81ae8f9180ef4647c834da8de53d8c0b 100644 (file)
--- 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 d639ff9b0c9031b26e750683832bfd82663f2fda..beb9d2b714adfff08596e638401e14dbfecf0ee6 100644 (file)
--- 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
 
index a2a8bbac46841b32a9fdee2d0e357bec0270d78c..77b464ac028376b42490c2c596454dc8447cdc83 100644 (file)
--- 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);
 
index 4b60c18336637aa2822e90f8e4f11e2d53d5bbd7..a7588427777242895a240da3121acbd263dd08f5 100644 (file)
--- 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);
index 9982cccf8cec3e57b6969f055dc49a57cbd9593e..0f2ac7b8450431dc549eac54df129922452f224d 100644 (file)
--- a/lib/win.c
+++ b/lib/win.c
@@ -5,20 +5,14 @@
 #include <win.h>
 #include <ctype.h>
 
-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;
 }
-
index 2292c14d9be0d188c3015d25a7ea336cc22703c6..027846485624c2575d5a556ebcb67065a815375e 100644 (file)
--- 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 33d589b113d78549ac5d941aaec8a31cb34dc067..a8ab0162ee444985d5cb1efbfdc8dcfc0378f779 100644 (file)
--- a/term.c
+++ b/term.c
@@ -5,20 +5,16 @@
 #include <ctype.h>
 #include <win.h>
 
-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 2d92983a09d2d5fb1d1ecc6440cd3d6cba4fd137..0897ea92f2f0333868ff398dd1ba02be3d19418d 100644 (file)
--- 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 cf117ae59452a9ad59180750b2543930b65cecee..9d7b3096bfe1e6e28515e0b6d2a866bc1c712f8a 100644 (file)
--- 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) {