From 2ef2435953720698f0af0adf2bf7e1e06f3cb999 Mon Sep 17 00:00:00 2001 From: nick black Date: Wed, 26 Aug 2020 21:21:02 -0400 Subject: [PATCH] input: move to inputlayer abstraction --- src/lib/input.c | 46 +++++++++++++++++++++++---------------------- src/lib/internal.h | 39 ++++++++++++++++++++------------------ src/lib/notcurses.c | 20 ++++++++++---------- 3 files changed, 55 insertions(+), 50 deletions(-) diff --git a/src/lib/input.c b/src/lib/input.c index 4a976e168..7964de168 100644 --- a/src/lib/input.c +++ b/src/lib/input.c @@ -20,7 +20,7 @@ void sigwinch_handler(int signo){ } static inline int -pop_input_keypress(notcurses* nc){ +pop_input_keypress(ncinputlayer* nc){ int candidate = nc->inputbuf[nc->inputbuf_valid_starts]; // fprintf(stderr, "DEOCCUPY: %u@%u read: %d\n", nc->inputbuf_occupied, nc->inputbuf_valid_starts, nc->inputbuf[nc->inputbuf_valid_starts]); if(++nc->inputbuf_valid_starts == sizeof(nc->inputbuf) / sizeof(*nc->inputbuf)){ @@ -32,7 +32,7 @@ pop_input_keypress(notcurses* nc){ // assumes there is space, as you presumably just popped it static inline void -unpop_keypress(notcurses* nc, int kpress){ +unpop_keypress(ncinputlayer* nc, int kpress){ ++nc->inputbuf_occupied; if(nc->inputbuf_valid_starts-- == 0){ nc->inputbuf_valid_starts = sizeof(nc->inputbuf) / sizeof(*nc->inputbuf) - 1; @@ -73,7 +73,7 @@ void input_free_esctrie(esctrie** eptr){ } static int -notcurses_add_input_escape(notcurses* nc, const char* esc, char32_t special){ +ncinputlayer_add_input_escape(ncinputlayer* nc, const char* esc, char32_t special){ if(esc[0] != ESC || strlen(esc) < 2){ // assume ESC prefix + content fprintf(stderr, "Not an escape: %s (0x%x)\n", esc, special); return -1; @@ -116,7 +116,7 @@ notcurses_add_input_escape(notcurses* nc, const char* esc, char32_t special){ // We received the CSI prefix. Extract the data payload. static char32_t -handle_csi(notcurses* nc, ncinput* ni){ +handle_csi(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ enum { PARAM1, // reading first param (button + modifiers) plus delimiter PARAM2, // reading second param (x coordinate) plus delimiter @@ -162,7 +162,7 @@ handle_csi(notcurses* nc, ncinput* ni){ break; } if(ni){ - ni->x = param - 1 - nc->margin_l; + ni->x = param - 1 - leftmargin; } param = 0; }else if(isdigit(candidate)){ @@ -180,7 +180,7 @@ handle_csi(notcurses* nc, ncinput* ni){ break; } if(ni){ - ni->y = param - 1 - nc->margin_t; + ni->y = param - 1 - topmargin; ni->id = id; } return id; @@ -200,7 +200,7 @@ handle_csi(notcurses* nc, ncinput* ni){ // if there is a full UTF8 codepoint or keystroke (composed or otherwise), // return it, and pop it from the queue. static char32_t -handle_getc(notcurses* nc, int kpress, ncinput* ni){ +handle_getc(ncinputlayer* nc, int kpress, ncinput* ni, int leftmargin, int topmargin){ //fprintf(stderr, "KEYPRESS: %d\n", kpress); if(kpress < 0){ return -1; @@ -220,7 +220,7 @@ handle_getc(notcurses* nc, int kpress, ncinput* ni){ } if(esc && esc->special != NCKEY_INVALID){ if(esc->special == NCKEY_CSI){ - return handle_csi(nc, ni); + return handle_csi(nc, ni, leftmargin, topmargin); } return esc->special; } @@ -291,12 +291,12 @@ block_on_input(FILE* fp, const struct timespec* ts, sigset_t* sigmask){ } static bool -input_queue_full(const notcurses* nc){ +input_queue_full(const ncinputlayer* nc){ return nc->inputbuf_occupied == sizeof(nc->inputbuf) / sizeof(*nc->inputbuf); } static char32_t -handle_input(notcurses* nc, ncinput* ni){ +handle_input(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ int r; // getc() returns unsigned chars cast to ints while(!input_queue_full(nc) && (r = getc(nc->ttyinfp)) >= 0){ @@ -317,15 +317,15 @@ handle_input(notcurses* nc, ncinput* ni){ return -1; } r = pop_input_keypress(nc); - return handle_getc(nc, r, ni); + return handle_getc(nc, r, ni, leftmargin, topmargin); } static char32_t -handle_ncinput(notcurses* nc, ncinput* ni){ +handle_ncinput(ncinputlayer* nc, ncinput* ni, int leftmargin, int topmargin){ if(ni){ memset(ni, 0, sizeof(*ni)); } - char32_t r = handle_input(nc, ni); + char32_t r = handle_input(nc, ni, leftmargin, topmargin); // ctrl (*without* alt) + letter maps to [1..26], and is independent of shift // FIXME need to distinguish between: // - Enter and ^J @@ -353,14 +353,15 @@ handle_ncinput(notcurses* nc, ncinput* ni){ // helper so we can do counter increment at a single location static inline char32_t -notcurses_prestamp(notcurses* nc, const struct timespec *ts, - sigset_t* sigmask, ncinput* ni){ +ncinputlayer_prestamp(ncinputlayer* nc, const struct timespec *ts, + sigset_t* sigmask, ncinput* ni, int leftmargin, + int topmargin){ errno = 0; - char32_t r = handle_ncinput(nc, ni); + char32_t r = handle_ncinput(nc, ni, leftmargin, topmargin); if(r == (char32_t)-1){ if(errno == EAGAIN || errno == EWOULDBLOCK){ block_on_input(nc->ttyinfp, ts, sigmask); - r = handle_ncinput(nc, ni); + r = handle_ncinput(nc, ni, leftmargin, topmargin); } return r; } @@ -370,9 +371,10 @@ notcurses_prestamp(notcurses* nc, const struct timespec *ts, // infp has already been set non-blocking char32_t notcurses_getc(notcurses* nc, const struct timespec *ts, sigset_t* sigmask, ncinput* ni){ - char32_t r = notcurses_prestamp(nc, ts, sigmask, ni); + char32_t r = ncinputlayer_prestamp(&nc->input, ts, sigmask, ni, + nc->margin_l, nc->margin_t); if(r != (char32_t)-1){ - uint64_t stamp = nc->input_events++; // need increment even if !ni + uint64_t stamp = nc->input.input_events++; // need increment even if !ni if(ni){ ni->seqnum = stamp; } @@ -380,7 +382,7 @@ char32_t notcurses_getc(notcurses* nc, const struct timespec *ts, return r; } -int prep_special_keys(notcurses* nc){ +int prep_special_keys(ncinputlayer* nc){ static const struct { const char* tinfo; char32_t key; @@ -484,12 +486,12 @@ int prep_special_keys(notcurses* nc){ continue; } //fprintf(stderr, "support for terminfo's %s: %s\n", k->tinfo, seq); - if(notcurses_add_input_escape(nc, seq, k->key)){ + if(ncinputlayer_add_input_escape(nc, seq, k->key)){ fprintf(stderr, "Couldn't add support for %s\n", k->tinfo); return -1; } } - if(notcurses_add_input_escape(nc, CSIPREFIX, NCKEY_CSI)){ + if(ncinputlayer_add_input_escape(nc, CSIPREFIX, NCKEY_CSI)){ fprintf(stderr, "Couldn't add support for %s\n", k->tinfo); return -1; } diff --git a/src/lib/internal.h b/src/lib/internal.h index daaf55c06..f6b7a3f4a 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -252,6 +252,23 @@ typedef struct tinfo { char* rmcup; // restore primary mode } tinfo; +typedef struct ncinputlayer { + FILE* ttyinfp; // FILE* for processing input + unsigned char inputbuf[BUFSIZ]; + // we keep a wee ringbuffer of input queued up for delivery. if + // inputbuf_occupied == sizeof(inputbuf), there is no room. otherwise, data + // can be read to inputbuf_write_at until we fill up. the first datum + // available for the app is at inputbuf_valid_starts iff inputbuf_occupied is + // not 0. the main purpose is working around bad predictions of escapes. + unsigned inputbuf_occupied; + unsigned inputbuf_valid_starts; + unsigned inputbuf_write_at; + // number of input events seen. does not belong in ncstats, since it must not + // be reset (semantics are relied upon by widgets for mouse click detection). + uint64_t input_events; + struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys +} ncinputlayer; + typedef struct ncdirect { palette256 palette; // 256-indexed palette can be used instead of/with RGB FILE* ttyfp; // FILE* for output tty @@ -259,6 +276,7 @@ typedef struct ncdirect { tinfo tcache; // terminfo cache unsigned fgrgb, bgrgb; // last RGB values of foreground/background uint16_t stylemask; // current styles + ncinputlayer input; // input layer; we're in cbreak mode bool fgdefault, bgdefault; // are FG/BG currently using default colors? bool utf8; // are we using utf-8 encoding, as hoped? struct termios tpreserved; // terminal state upon entry @@ -288,34 +306,19 @@ typedef struct notcurses { int truecols; // true number of columns in the physical rendering area. // used only to see if output motion takes us to the next // line thanks to terminal action alone. - - tinfo tcache; // terminfo cache - FILE* ttyfp; // FILE* for writing rasterized data int ttyfd; // file descriptor for controlling tty - FILE* ttyinfp; // FILE* for processing input + ncinputlayer input; // input layer; we're in cbreak mode FILE* renderfp; // debugging FILE* to which renderings are written + tinfo tcache; // terminfo cache struct termios tpreserved; // terminal state upon entry bool suppress_banner; // from notcurses_options - unsigned char inputbuf[BUFSIZ]; - // we keep a wee ringbuffer of input queued up for delivery. if - // inputbuf_occupied == sizeof(inputbuf), there is no room. otherwise, data - // can be read to inputbuf_write_at until we fill up. the first datum - // available for the app is at inputbuf_valid_starts iff inputbuf_occupied is - // not 0. the main purpose is working around bad predictions of escapes. - unsigned inputbuf_occupied; - unsigned inputbuf_valid_starts; - unsigned inputbuf_write_at; - // number of input events seen. does not belong in ncstats, since it must not - // be reset (semantics are relied upon by widgets for mouse click detection). - uint64_t input_events; // desired margins (best-effort only), copied in from notcurses_options int margin_t, margin_b, margin_r, margin_l; int loglevel; palette256 palette; // 256-indexed palette can be used instead of/with RGB bool palette_damage[NCPALETTESIZE]; - struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys bool utf8; // are we using utf-8 encoding, as hoped? bool libsixel; // do we have Sixel support? } notcurses; @@ -362,7 +365,7 @@ ncplane_stdplane_const(const ncplane* n){ } // load all known special keys from terminfo, and build the input sequence trie -int prep_special_keys(notcurses* nc); +int prep_special_keys(ncinputlayer* nc); // free up the input escapes trie void input_free_esctrie(struct esctrie** trie); diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 8fbf252af..75da092de 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -888,8 +888,8 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ reset_stats(&ret->stashstats); ret->ttyfp = outfp; ret->renderfp = opts->renderfp; - ret->inputescapes = NULL; - ret->ttyinfp = stdin; // FIXME + ret->input.inputescapes = NULL; + ret->input.ttyinfp = stdin; // FIXME memset(&ret->rstate, 0, sizeof(ret->rstate)); memset(&ret->palette_damage, 0, sizeof(ret->palette_damage)); memset(&ret->palette, 0, sizeof(ret->palette)); @@ -898,14 +898,14 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ ret->lfdimx = 0; ret->libsixel = false; egcpool_init(&ret->pool); - if(make_nonblocking(ret->ttyinfp)){ + if(make_nonblocking(ret->input.ttyinfp)){ free(ret); return NULL; } - ret->inputbuf_occupied = 0; - ret->inputbuf_valid_starts = 0; - ret->inputbuf_write_at = 0; - ret->input_events = 0; + ret->input.inputbuf_occupied = 0; + ret->input.inputbuf_valid_starts = 0; + ret->input.inputbuf_write_at = 0; + ret->input.input_events = 0; if((ret->loglevel = opts->loglevel) > NCLOGLEVEL_TRACE || ret->loglevel < 0){ fprintf(stderr, "Invalid loglevel %d\n", ret->loglevel); free(ret); @@ -951,7 +951,7 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ if(interrogate_terminfo(&ret->tcache)){ goto err; } - if(prep_special_keys(ret)){ + if(prep_special_keys(&ret->input)){ goto err; } // Neither of these is supported on e.g. the "linux" virtual console. @@ -1039,7 +1039,7 @@ int notcurses_stop(notcurses* nc){ egcpool_dump(&nc->pool); free(nc->lastframe); free(nc->rstate.mstream); - input_free_esctrie(&nc->inputescapes); + input_free_esctrie(&nc->input.inputescapes); stash_stats(nc); if(!nc->suppress_banner){ if(nc->stashstats.renders){ @@ -2134,7 +2134,7 @@ int notcurses_lex_margins(const char* op, notcurses_options* opts){ } int notcurses_inputready_fd(notcurses* n){ - return fileno(n->ttyinfp); + return fileno(n->input.ttyinfp); } uint32_t* ncplane_rgba(const ncplane* nc, ncblitter_e blit,