diff --git a/NEWS.md b/NEWS.md index 940d3e05f..4ede43de3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -13,7 +13,7 @@ rearrangements of Notcurses. continue reading). Likewise added `NCDIRECT_OPTION_DRAIN_INPUT`. * Removed a bunch of deprecated `static inline` functions from the headers. * A new field, `evtype`, has been added to `ncinput`. It takes a value - from among `EVTYPE_{UNKNOWN, PRESS, REPEAT, RELEASE}.`. Where possible, + from among `NCTYPE_{UNKNOWN, PRESS, REPEAT, RELEASE}.`. Where possible, Notcurses will distinguish between a press, repeat, and release. This cannot be done in all environments, nor with all inputs. The `NCKEY_RELEASE` definition is no longer returned; instead, the diff --git a/USAGE.md b/USAGE.md index 0882bceeb..2d2d9257c 100644 --- a/USAGE.md +++ b/USAGE.md @@ -685,10 +685,10 @@ typedef struct ncinput { bool shift; // was shift held? bool ctrl; // was ctrl held? enum { - EVTYPE_UNKNOWN, - EVTYPE_PRESS, - EVTYPE_REPEAT, - EVTYPE_RELEASE, + NCTYPE_UNKNOWN, + NCTYPE_PRESS, + NCTYPE_REPEAT, + NCTYPE_RELEASE, } evtype; } ncinput; diff --git a/doc/man/man1/notcurses-input.1.md b/doc/man/man1/notcurses-input.1.md index 03a196d54..7012db511 100644 --- a/doc/man/man1/notcurses-input.1.md +++ b/doc/man/man1/notcurses-input.1.md @@ -15,14 +15,22 @@ notcurses-input - Read and display input events **notcurses-input** reads from stdin and decodes the input to stdout, including synthesized events and mouse events. To exit, generate EOF (usually Ctrl+'d'). +Each event will be printed on a single line. Leading that line is a series +of modifier indicators: + +* 'A'/'a': Alt was or was not pressed. +* 'C'/'c': Ctrl was or was not pressed. +* 'S'/'s': Shift was or was not pressed. +* 'L'/'R'/'P'/'u': Key was a release, repeat, press, or of unknown type. + # OPTIONS **-v**: Increase verbosity. # NOTES -Mouse events are only generated for button presses, and for movement while a -button is held down. +Mouse events are only generated for button presses and releases, and for +movement while a button is held down. # SEE ALSO diff --git a/doc/man/man3/notcurses_input.3.md b/doc/man/man3/notcurses_input.3.md index b5a6e59c6..9b257f310 100644 --- a/doc/man/man3/notcurses_input.3.md +++ b/doc/man/man3/notcurses_input.3.md @@ -92,13 +92,13 @@ action, but signals in the interim are permanently lost. ## Mice -For mouse events, the additional fields **y** and **x** are set. These fields -are not meaningful for keypress events. Mouse events can be distinguished using -the **nckey_mouse_p** predicate. Once enabled, mouse button presses are -detected, as are mouse motions made while a button is held down. If no button -is depressed, mouse movement _does not result in events_. This is known as -"button-event tracking" mode in the nomenclature of [Xterm Control -Sequences](https://www.xfree86.org/current/ctlseqs.html). +For mouse events, the additional fields **y** and **x** are set. These +fields are not meaningful for keypress events. Mouse events can be +distinguished using the **nckey_mouse_p** predicate. Once enabled, mouse +button presses and releases are detected, as are mouse motions made while a +button is held down. If no button is depressed, mouse movement _does not +result in events_. This is known as "button-event tracking" mode in the +nomenclature of [Xterm Control Sequences](https://www.xfree86.org/current/ctlseqs.html). ## Synthesized keypresses @@ -169,19 +169,20 @@ Failed escape sequences are not yet played back in their entirety; only an ESC (ASCII 0x1b) will be seen by the application. The Shift key is only indicated in conjunction with mouse button presses. If -e.g. Shift is used to generate a capital letter 'A', **id** will equal 'A', and -**shift** will be **false**. This should be fixed in the future. +e.g. Shift is used to generate a capital letter 'A', **id** will equal 'A', +and **shift** will be **false**. This should be fixed in the future. When Ctrl is pressed along with a letter, the letter will currently always be reported in its uppercase form. E.g., if Shift, Ctrl, and 'a' are all pressed, this is indistinguishable from Ctrl and 'a' without Shift. This should be fixed in the future. -Ctrl pressed along with 'J' or 'M', whether Shift is pressed or not, currently -registers as **NCKEY_ENTER**. This will likely change in the future. +Ctrl pressed along with 'J' or 'M', whether Shift is pressed or not, +currently registers as **NCKEY_ENTER**. This will likely change in the +future. -When the Kitty keyboard disambiguation protocol is used, most of these issues -are resolved. +When the Kitty keyboard disambiguation protocol is used, most of these +issues are resolved. # SEE ALSO diff --git a/include/ncpp/NCKey.hh b/include/ncpp/NCKey.hh index 95fc58a8b..030e5a92f 100644 --- a/include/ncpp/NCKey.hh +++ b/include/ncpp/NCKey.hh @@ -108,11 +108,15 @@ namespace ncpp static constexpr char32_t Button9 = NCKEY_BUTTON9; static constexpr char32_t Button10 = NCKEY_BUTTON10; static constexpr char32_t Button11 = NCKEY_BUTTON11; - static constexpr char32_t Release = NCKEY_RELEASE; static constexpr char32_t ScrollUp = NCKEY_SCROLL_UP; static constexpr char32_t ScrollDown = NCKEY_SCROLL_DOWN; static constexpr char32_t Return = NCKEY_RETURN; + static constexpr int TypeUnknown = ncinput::NCTYPE_UNKNOWN; + static constexpr int TypePress = ncinput::NCTYPE_PRESS; + static constexpr int TypeRepeat = ncinput::NCTYPE_REPEAT; + static constexpr int TypeRelease = ncinput::NCTYPE_RELEASE; + static bool IsMouse (char32_t ch) noexcept { return nckey_mouse_p (ch); diff --git a/include/notcurses/nckeys.h b/include/notcurses/nckeys.h index ce0d2146f..abf3dd8bd 100644 --- a/include/notcurses/nckeys.h +++ b/include/notcurses/nckeys.h @@ -112,10 +112,6 @@ extern "C" { #define NCKEY_BUTTON9 suppuabize(209) #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/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index c4bb09f0e..802f852a9 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1034,7 +1034,7 @@ nckey_supppuab_p(uint32_t w){ // Is the event a synthesized mouse event? static inline bool nckey_mouse_p(uint32_t r){ - return r >= NCKEY_BUTTON1 && r <= NCKEY_RELEASE; + return r >= NCKEY_BUTTON1 && r <= NCKEY_BUTTON11; } // An input event. Cell coordinates are currently defined only for mouse @@ -1048,10 +1048,10 @@ typedef struct ncinput { bool shift; // was shift held? bool ctrl; // was ctrl held? enum { - EVTYPE_UNKNOWN, - EVTYPE_PRESS, - EVTYPE_REPEAT, - EVTYPE_RELEASE, + NCTYPE_UNKNOWN, + NCTYPE_PRESS, + NCTYPE_REPEAT, + NCTYPE_RELEASE, } evtype; } ncinput; @@ -1067,7 +1067,7 @@ ncinput_equal_p(const ncinput* n1, const ncinput* n2){ if(n1->alt != n2->alt || n1->shift != n2->shift || n1->ctrl != n2->ctrl){ return false; } - if(n1->keytype != n2->keytype){ + if(n1->evtype != n2->evtype){ return false; } return true; diff --git a/src/demo/hud.c b/src/demo/hud.c index f9848752e..f48e7983d 100644 --- a/src/demo/hud.c +++ b/src/demo/hud.c @@ -244,7 +244,7 @@ bool menu_or_hud_key(struct notcurses *nc, const struct ncinput *ni){ if(sel == NULL){ return false; } - }else if(menu && ni->id == NCKEY_RELEASE){ + }else if(menu && ni->id == NCKEY_BUTTON1 && ni->evtype == NCTYPE_RELEASE){ const char* sel = ncmenu_mouse_selected(menu, ni, &tmpni); if(sel == NULL){ memcpy(&tmpni, ni, sizeof(tmpni)); diff --git a/src/demo/input.c b/src/demo/input.c index 6fb442896..879f217be 100644 --- a/src/demo/input.c +++ b/src/demo/input.c @@ -22,11 +22,11 @@ static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static int handle_mouse(const ncinput* ni){ - if(ni->id != NCKEY_BUTTON1 && ni->id != NCKEY_RELEASE){ + if(ni->id != NCKEY_BUTTON1){ return 0; } int ret; - if(ni->id == NCKEY_RELEASE){ + if(ni->evtype == NCTYPE_RELEASE){ ret = hud_release(); if(ret < 0){ ret = fpsplot_release(); diff --git a/src/input/input.cpp b/src/input/input.cpp index 41d3b994d..2f9d0359b 100644 --- a/src/input/input.cpp +++ b/src/input/input.cpp @@ -125,18 +125,17 @@ const char* nckeystr(char32_t spkey){ case NCKEY_EXIT: return "exit"; case NCKEY_PRINT: return "print"; case NCKEY_REFRESH: return "refresh"; - case NCKEY_BUTTON1: return "mouse (button 1 pressed)"; - case NCKEY_BUTTON2: return "mouse (button 2 pressed)"; - case NCKEY_BUTTON3: return "mouse (button 3 pressed)"; - case NCKEY_BUTTON4: return "mouse (button 4 pressed)"; - case NCKEY_BUTTON5: return "mouse (button 5 pressed)"; - case NCKEY_BUTTON6: return "mouse (button 6 pressed)"; - case NCKEY_BUTTON7: return "mouse (button 7 pressed)"; - case NCKEY_BUTTON8: return "mouse (button 8 pressed)"; - case NCKEY_BUTTON9: return "mouse (button 9 pressed)"; - case NCKEY_BUTTON10: return "mouse (button 10 pressed)"; - case NCKEY_BUTTON11: return "mouse (button 11 pressed)"; - case NCKEY_RELEASE: return "mouse (button released)"; + case NCKEY_BUTTON1: return "mouse (button 1)"; + case NCKEY_BUTTON2: return "mouse (button 2)"; + case NCKEY_BUTTON3: return "mouse (button 3)"; + case NCKEY_BUTTON4: return "mouse (button 4)"; + case NCKEY_BUTTON5: return "mouse (button 5)"; + case NCKEY_BUTTON6: return "mouse (button 6)"; + case NCKEY_BUTTON7: return "mouse (button 7)"; + case NCKEY_BUTTON8: return "mouse (button 8)"; + case NCKEY_BUTTON9: return "mouse (button 9)"; + case NCKEY_BUTTON10: return "mouse (button 10)"; + case NCKEY_BUTTON11: return "mouse (button 11)"; default: return "unknown"; } } @@ -204,6 +203,20 @@ void Ticker(ncpp::NotCurses* nc) { }while(!done); } +char evtype_to_char(ncinput* ni){ + switch(ni->evtype){ + case NCKey::TypeUnknown: + return 'u'; + case NCKey::TypePress: + return 'P'; + case NCKey::TypeRepeat: + return 'R'; + case NCKey::TypeRelease: + return 'L'; + } + return 'X'; +} + int input_demo(ncpp::NotCurses* nc) { constexpr auto PLOTHEIGHT = 6; auto n = nc->get_stdplane(&dimy, &dimx); @@ -285,8 +298,8 @@ int input_demo(ncpp::NotCurses* nc) { break; } n->set_fg_rgb8(0xd0, 0xd0, 0xd0); - n->printf("%c%c%c ", ni.alt ? 'A' : 'a', ni.ctrl ? 'C' : 'c', - ni.shift ? 'S' : 's'); + n->printf("%c%c%c%c ", ni.alt ? 'A' : 'a', ni.ctrl ? 'C' : 'c', + ni.shift ? 'S' : 's', evtype_to_char(&ni)); if(r < 0x80){ n->set_fg_rgb8(128, 250, 64); if(n->printf("ASCII: [0x%02x (%03d)] '%lc'", r, r, diff --git a/src/lib/in.c b/src/lib/in.c index 606e788db..d8d4903ef 100644 --- a/src/lib/in.c +++ b/src/lib/in.c @@ -847,9 +847,7 @@ mouse_click(inputctx* ictx, unsigned release){ return; } ncinput* ni = ictx->inputs + ictx->iwrite; - if(release){ - ni->id = NCKEY_RELEASE; - }else if(ictx->p2 >= 0 && ictx->p2 < 64){ + if(ictx->p2 >= 0 && ictx->p2 < 64){ ni->id = NCKEY_BUTTON1 + (ictx->p2 % 4); }else if(ictx->p2 >= 64 && ictx->p2 < 128){ ni->id = NCKEY_BUTTON4 + (ictx->p2 % 4); @@ -859,6 +857,12 @@ mouse_click(inputctx* ictx, unsigned release){ ni->ctrl = ictx->p2 & 0x10; ni->alt = ictx->p2 & 0x08; ni->shift = ictx->p2 & 0x04; + // mice don't send repeat events, so we know it's either release or press + if(release){ + ni->evtype = NCTYPE_RELEASE; + }else{ + ni->evtype = NCTYPE_PRESS; + } ni->x = x; ni->y = y; if(++ictx->iwrite == ictx->isize){ diff --git a/src/lib/menu.c b/src/lib/menu.c index 641d942b9..9886f14ec 100644 --- a/src/lib/menu.c +++ b/src/lib/menu.c @@ -587,7 +587,10 @@ const char* ncmenu_selected(const ncmenu* n, ncinput* ni){ const char* ncmenu_mouse_selected(const ncmenu* n, const ncinput* click, ncinput* ni){ - if(click->id != NCKEY_RELEASE){ + if(click->id != NCKEY_BUTTON1){ + return NULL; + } + if(click->evtype != NCTYPE_RELEASE){ return NULL; } int y, x, dimy, dimx; @@ -620,7 +623,7 @@ const char* ncmenu_mouse_selected(const ncmenu* n, const ncinput* click, bool ncmenu_offer_input(ncmenu* n, const ncinput* nc){ // we can't actually select menu items in this function, since we need to // invoke an arbitrary function as a result. - if(nc->id == NCKEY_RELEASE){ + if(nc->id == NCKEY_BUTTON1 && nc->evtype == NCTYPE_RELEASE){ int y, x, dimy, dimx; y = nc->y; x = nc->x; diff --git a/src/lib/selector.c b/src/lib/selector.c index f11814de0..87e24b1e9 100644 --- a/src/lib/selector.c +++ b/src/lib/selector.c @@ -483,7 +483,7 @@ bool ncselector_offer_input(ncselector* n, const ncinput* nc){ } } return true; - }else if(nc->id == NCKEY_RELEASE){ + }else if(nc->id == NCKEY_BUTTON1 && nc->evtype == NCTYPE_RELEASE){ int y = nc->y, x = nc->x; if(!ncplane_translate_abs(n->ncp, &y, &x)){ return false; @@ -762,7 +762,7 @@ bool ncmultiselector_offer_input(ncmultiselector* n, const ncinput* nc){ }else if(nc->id == NCKEY_SCROLL_DOWN){ ncmultiselector_nextitem(n); return true; - }else if(nc->id == NCKEY_RELEASE){ + }else if(nc->id == NCKEY_BUTTON1 && nc->evtype == NCTYPE_RELEASE){ int y = nc->y, x = nc->x; if(!ncplane_translate_abs(n->ncp, &y, &x)){ return false; diff --git a/src/tests/plane.cpp b/src/tests/plane.cpp index be52be002..474f2054d 100644 --- a/src/tests/plane.cpp +++ b/src/tests/plane.cpp @@ -872,7 +872,8 @@ TEST_CASE("Plane") { struct ncplane* n = ncplane_create(n_, &nopts); REQUIRE(n); ncinput ni{}; - ni.id = NCKEY_RELEASE; + ni.id = NCKEY_BUTTON1; + ni.evtype = ncinput::NCTYPE_RELEASE; int total = 0; for(ni.y = 0 ; ni.y < 5 ; ++ni.y){ for(ni.x = 0 ; ni.x < 5 ; ++ni.x){