From dcc58ef713cf84ff775daad7ddf965bea698f783 Mon Sep 17 00:00:00 2001 From: nick black Date: Sat, 10 Jul 2021 20:42:02 -0400 Subject: [PATCH] input: process CSI for cursor report #1692 --- include/notcurses/nckeys.h | 3 +++ src/lib/input.c | 43 ++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/include/notcurses/nckeys.h b/include/notcurses/nckeys.h index a5c3bce2f..ce0d2146f 100644 --- a/include/notcurses/nckeys.h +++ b/include/notcurses/nckeys.h @@ -113,6 +113,9 @@ extern "C" { #define NCKEY_BUTTON10 suppuabize(210) #define NCKEY_BUTTON11 suppuabize(211) #define NCKEY_RELEASE suppuabize(212) +// never returned to the user -- when a cursor location report shows up on +// the input, it's picked up by our internals, and used. +#define NCKEY_CURSOR_LOCATION_REPORT suppuabize(213) // Synonyms (so far as we're concerned) #define NCKEY_SCROLL_UP NCKEY_BUTTON4 diff --git a/src/lib/input.c b/src/lib/input.c index 1837a61d0..0ffb45cd9 100644 --- a/src/lib/input.c +++ b/src/lib/input.c @@ -177,9 +177,15 @@ ncinputlayer_add_input_escape(ncinputlayer* nc, const char* esc, char32_t specia return 0; } -// We received the CSI prefix. Extract the data payload. +// We received the CSI prefix. Extract the data payload. Right now, we handle +// mouse and cursor location reports. The former is three parameters ending +// with 'm' or 'M'; the latter is two ending with 'R'. Both use 1-biased +// coordinates, so a 0 can be safely rejected. static char32_t handle_csi(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ + // stash the first parameter away. it's encoded if the CSI ends up being a + // mouse event, and otherwise it's the cursor's column (x) coordinate. + int param1 = -1; enum { PARAM1, // reading first param (button + modifiers) plus delimiter PARAM2, // reading second param (x coordinate) plus delimiter @@ -191,6 +197,7 @@ handle_csi(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ int candidate = pop_input_keypress(nc); if(state == PARAM1){ if(candidate == ';'){ + param1 = param; state = PARAM2; // modifiers: 32 (motion) 16 (control) 8 (alt) 4 (shift) // buttons 4, 5, 6, 7: adds 64 @@ -219,14 +226,21 @@ handle_csi(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ break; } }else if(state == PARAM2){ - if(candidate == ';'){ + if(candidate == 'R'){ // cursor location report + if(param <= 0 || param1 <= 0){ + logwarn("Invalid cursor location param (%d/%d)\n", param, param1); + break; + } + ni->x = param1 - 1; + ni->y = param - 1; + return NCKEY_CURSOR_LOCATION_REPORT; + }else if(candidate == ';'){ state = PARAM3; if(param == 0){ + logwarn("Invalid mouse param (%d/%d)\n", param1, param); break; } - if(ni){ - ni->x = param - 1 - leftmargin; - } + ni->x = param - 1 - leftmargin; param = 0; }else if(isdigit(candidate)){ param *= 10; @@ -240,12 +254,11 @@ handle_csi(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ id = NCKEY_RELEASE; } if(param == 0){ + logwarn("Invalid mouse param (%d/%d)\n", param1, param); break; } - if(ni){ - ni->y = param - 1 - topmargin; - ni->id = id; - } + ni->y = param - 1 - topmargin; + ni->id = id; return id; }else if(isdigit(candidate)){ param *= 10; @@ -256,6 +269,8 @@ handle_csi(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ } } // FIXME ungetc on failure! walk trie backwards or something + // FIXME increment the input_error stat + logerror("Error processing CSI\n"); return (char32_t)-1; } @@ -313,9 +328,7 @@ handle_getc(ncinputlayer* nc, int kpress, ncinput* ni, int leftmargin, int topma // interpret it as alt + candidate FIXME broken for first char matching // trie, second char not -- will read as alt+second char... if(candidate > 0 && candidate < 0x80){ - if(ni){ - ni->alt = true; - } + ni->alt = true; return candidate; } // FIXME ungetc on failure! walk trie backwards or something @@ -400,13 +413,17 @@ input_queue_space(const ncinputlayer* nc){ static char32_t handle_queued_input(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ + ncinput nireal; + if(ni == NULL){ + ni = &nireal; + } // if there was some error in getc(), we still dole out the existing queue if(nc->inputbuf_occupied == 0){ return -1; } int r = pop_input_keypress(nc); char32_t ret = handle_getc(nc, r, ni, leftmargin, topmargin); - if(ret != (char32_t)-1 && ni){ + if(ret != (char32_t)-1){ ni->id = ret; } return ret;