[input] walk specials automaton

This commit is contained in:
nick black 2021-09-14 20:17:52 -04:00
parent fd5708a7cd
commit 716a37f917
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
9 changed files with 290 additions and 142 deletions

View File

@ -216,13 +216,13 @@ int input_demo(ncpp::NotCurses* nc) {
}
struct ncplane_options nopts = {
.y = dimy - PLOTHEIGHT - 1,
.x = 0,
.x = NCALIGN_CENTER,
.rows = PLOTHEIGHT,
.cols = cols,
.cols = cols / 2,
.userptr = nullptr,
.name = "plot",
.resizecb = nullptr, // FIXME
.flags = 0,
.flags = NCPLANE_OPTION_HORALIGNED,
.margin_b = 0,
.margin_r = 0,
};
@ -354,6 +354,10 @@ int main(int argc, char** argv){
return EXIT_FAILURE;
}
notcurses_options nopts{};
nopts.margin_t = 2;
nopts.margin_l = 2;
nopts.margin_r = 2;
nopts.margin_b = 2;
nopts.loglevel = NCLOGLEVEL_ERROR;
if(argc > 2){
usage(argv[0], stderr);

View File

@ -179,7 +179,11 @@ int ncdirect_cursor_disable(ncdirect* nc){
}
static int
cursor_yx_get(struct inputctx* ictx, int ttyfd, const char* u7, int* y, int* x){
cursor_yx_get(ncdirect* n, int ttyfd, const char* u7, int* y, int* x){
struct inputctx* ictx = n->tcache.ictx;
if(ncdirect_flush(n)){
return -1;
}
if(tty_emit(u7, ttyfd)){
return -1;
}
@ -201,7 +205,7 @@ int ncdirect_cursor_move_yx(ncdirect* n, int y, int x){
if(hpa){
return term_emit(tiparm(hpa, x), n->ttyfp, false);
}else if(n->tcache.ttyfd >= 0 && u7){
if(cursor_yx_get(n->tcache.ictx, n->tcache.ttyfd, u7, &y, NULL)){
if(cursor_yx_get(n, n->tcache.ttyfd, u7, &y, NULL)){
return -1;
}
}else{
@ -211,7 +215,7 @@ int ncdirect_cursor_move_yx(ncdirect* n, int y, int x){
if(!vpa){
return term_emit(tiparm(vpa, y), n->ttyfp, false);
}else if(n->tcache.ttyfd >= 0 && u7){
if(cursor_yx_get(n->tcache.ictx, n->tcache.ttyfd, u7, NULL, &x)){
if(cursor_yx_get(n, n->tcache.ttyfd, u7, NULL, &x)){
return -1;
}
}else{
@ -381,7 +385,7 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
if(!x){
x = &xval;
}
ret = cursor_yx_get(n->tcache.ictx, n->tcache.ttyfd, u7, y, x);
ret = cursor_yx_get(n, n->tcache.ttyfd, u7, y, x);
if(tcsetattr(n->tcache.ttyfd, TCSANOW, &oldtermios)){
fprintf(stderr, "Couldn't restore terminal mode on %d (%s)\n",
n->tcache.ttyfd, strerror(errno)); // don't return error for this
@ -430,7 +434,7 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){
if(np->sprite){
int y;
const char* u7 = get_escape(&n->tcache, ESCAPE_U7);
if(cursor_yx_get(n->tcache.ictx, n->tcache.ttyfd, u7, &y, NULL)){
if(cursor_yx_get(n, n->tcache.ttyfd, u7, &y, NULL)){
return -1;
}
if(ncdirect_cursor_move_yx(n, y, xoff)){
@ -442,10 +446,10 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){
return -1;
}
fbuf f = {};
if(fbuf_init(&f)){
if(cursor_yx_get(n, n->tcache.ttyfd, u7, &y, NULL)){
return -1;
}
if(cursor_yx_get(n->tcache.ictx, n->tcache.ttyfd, u7, &y, NULL)){
if(fbuf_init(&f)){
return -1;
}
if(toty - dimy < y){
@ -458,21 +462,16 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){
// perform our scrolling outside of the fbuf framework, as we need it
// to happen immediately for fbdcon
if(ncdirect_cursor_move_yx(n, y, xoff)){
fbuf_free(&f);
return -1;
}
const char* indn = get_escape(&n->tcache, ESCAPE_IND);
if(scrolls > 1 && indn){
if(term_emit(tiparm(indn, scrolls), stdout, true) < 0){
return -1;
}
}
while(scrolls--){
if(ncfputc('\v', stdout) < 0){
return -1;
}
if(emit_scrolls(&n->tcache, scrolls, &f) < 0){
fbuf_free(&f);
return -1;
}
}
if(sprite_draw(&n->tcache, NULL, np->sprite, &f, y, xoff) < 0){
fbuf_free(&f);
return -1;
}
if(sprite_commit(&n->tcache, &f, np->sprite, true)){
@ -790,10 +789,8 @@ ncdirect_stop_minimal(void* vnc){
ret = -1;
}
ret |= tcsetattr(nc->tcache.ttyfd, TCSANOW, nc->tcache.tpreserved);
ret |= close(nc->tcache.ttyfd);
}
ret |= ncdirect_flush(nc);
free_terminfo_cache(&nc->tcache);
return ret;
}
@ -809,6 +806,10 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){
return ret;
}
memset(ret, 0, sizeof(*ret));
if(pthread_mutex_init(&ret->stats.lock, NULL)){
free(ret);
return NULL;
}
ret->flags = flags;
ret->ttyfp = outfp;
if(!(flags & NCDIRECT_OPTION_INHIBIT_SETLOCALE)){
@ -821,6 +822,7 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){
}
if(setup_signals(ret, (flags & NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS),
true, ncdirect_stop_minimal)){
pthread_mutex_destroy(&ret->stats.lock);
free(ret);
return NULL;
}
@ -835,9 +837,10 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){
}
int cursor_y = -1;
int cursor_x = -1;
if(interrogate_terminfo(&ret->tcache, termtype, ret->ttyfp, utf8,
1, flags & NCDIRECT_OPTION_INHIBIT_CBREAK,
TERMINAL_UNKNOWN, &cursor_y, &cursor_x, NULL)){
if(interrogate_terminfo(&ret->tcache, termtype, ret->ttyfp, utf8, 1,
flags & NCDIRECT_OPTION_INHIBIT_CBREAK,
TERMINAL_UNKNOWN, &cursor_y, &cursor_x,
&ret->stats, 0, 0)){
goto err;
}
if(cursor_y >= 0){
@ -845,6 +848,7 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){
// unaffected by any query spill (unconsumed control sequences). move
// us back to that location, in case there was any such spillage.
if(ncdirect_cursor_move_yx(ret, cursor_y, cursor_x)){
free_terminfo_cache(&ret->tcache);
goto err;
}
}
@ -860,6 +864,7 @@ err:
(void)tcsetattr(ret->tcache.ttyfd, TCSANOW, ret->tcache.tpreserved);
}
drop_signals(ret);
pthread_mutex_destroy(&ret->stats.lock);
free(ret);
return NULL;
}
@ -868,6 +873,11 @@ int ncdirect_stop(ncdirect* nc){
int ret = 0;
if(nc){
ret |= ncdirect_stop_minimal(nc);
free_terminfo_cache(&nc->tcache);
if(nc->tcache.ttyfd){
ret |= close(nc->tcache.ttyfd);
}
pthread_mutex_destroy(&nc->stats.lock);
free(nc);
}
return ret;

View File

@ -21,15 +21,14 @@
// FIXME still need to:
// read specials from terminfo
// integrate main specials trie with automaton, or match it alongside
// the main automaton
// integrate main specials trie with automaton, enable input_errors
// wake up input thread when space becomes available
// restore stats
// modifiers for non-kitty, non-mouse input
// (needs pipes/eventfds)
// handle timeouts
static sig_atomic_t resize_seen;
// called for SIGWINCH and SIGCONT
// called for SIGWINCH and SIGCONT, and causes block_on_input to return
void sigwinch_handler(int signo){
resize_seen = signo;
}
@ -104,6 +103,19 @@ typedef struct esctrie {
bool shift, ctrl, alt;
} esctrie;
static esctrie*
create_esctrie_node(int special){
esctrie* e = malloc(sizeof(*e));
if(e){
e->trie = NULL;
e->special = special;
e->shift = 0;
e->ctrl = 0;
e->alt = 0;
}
return e;
}
// local state for the input thread. don't put this large struct on the stack.
typedef struct inputctx {
int stdinfd; // bulk in fd. always >= 0 (almost always 0). we do not
@ -114,6 +126,9 @@ typedef struct inputctx {
HANDLE stdinhandle; // handle to input terminal for MSFT Terminal
#endif
uint64_t seqnum; // process-scope sequence number
int lmargin, tmargin; // margins in use at left and top
struct esctrie* inputescapes;
// these two are not ringbuffers; we always move any leftover materia to the
@ -131,6 +146,7 @@ typedef struct inputctx {
char runstring[BUFSIZ]; // running string (when stringstate != STATE_NULL)
int stridx; // length of runstring
int p2, p3, p4; // holders for numeric params
struct esctrie* triepos;// position in escapes automaton
// ringbuffers for processed, structured input
cursorloc* csrs; // cursor reports are dumped here
@ -148,23 +164,12 @@ typedef struct inputctx {
tinfo* ti; // link back to tinfo
pthread_t tid; // tid for input thread
ncsharedstats *stats; // stats shared with notcurses context
struct initial_responses* initdata;
struct initial_responses* initdata_complete;
} inputctx;
static esctrie*
create_esctrie_node(int special){
esctrie* e = malloc(sizeof(*e));
if(e){
e->special = special;
e->trie = NULL;
e->shift = 0;
e->ctrl = 0;
e->alt = 0;
}
return e;
}
static void
input_free_esctrie(esctrie** eptr){
esctrie* e;
@ -183,7 +188,8 @@ input_free_esctrie(esctrie** eptr){
}
static inline inputctx*
create_inputctx(tinfo* ti, FILE* infp){
create_inputctx(tinfo* ti, FILE* infp, int lmargin, int tmargin,
ncsharedstats* stats){
inputctx* i = malloc(sizeof(*i));
if(i){
i->csize = 64;
@ -197,19 +203,23 @@ create_inputctx(tinfo* ti, FILE* infp){
if((i->stdinfd = fileno(infp)) >= 0){
if( (i->initdata = malloc(sizeof(*i->initdata))) ){
if(set_fd_nonblocking(i->stdinfd, 1, &ti->stdio_blocking_save) == 0){
memset(i->initdata, 0, sizeof(*i->initdata));
i->termfd = tty_check(i->stdinfd) ? -1 : get_tty_fd(infp);
memset(i->initdata, 0, sizeof(*i->initdata));
i->state = i->stringstate = STATE_NULL;
i->iread = i->iwrite = i->ivalid = 0;
i->cread = i->cwrite = i->cvalid = 0;
i->initdata_complete = NULL;
i->inputescapes = NULL;
i->stats = stats;
i->ti = ti;
i->cvalid = i->ivalid = 0;
i->cwrite = i->iwrite = 0;
i->cread = i->iread = 0;
i->ibufvalid = 0;
i->tbufvalid = 0;
i->state = i->stringstate = STATE_NULL;
i->numeric = 0;
i->stridx = 0;
i->initdata_complete = NULL;
i->runstring[i->stridx] = '\0';
i->lmargin = lmargin;
i->tmargin = tmargin;
i->seqnum = 0;
logdebug("input descriptors: %d/%d\n", i->stdinfd, i->termfd);
return i;
}
@ -268,41 +278,45 @@ inputctx_add_input_escape(inputctx* ictx, const char* esc, uint32_t special,
logerror("not an escape (0x%x)\n", special);
return -1;
}
esctrie** cur = &ictx->inputescapes;
do{
//fprintf(stderr, "ADDING: %s (%zu) for %d\n", esc, strlen(esc), special);
++esc;
int validate = *esc;
if(validate < 0 || validate >= 0x80){
if(ictx->inputescapes == NULL){
if((ictx->inputescapes = create_esctrie_node(NCKEY_INVALID)) == NULL){
return -1;
}
if(*cur == NULL){
if((*cur = create_esctrie_node(NCKEY_INVALID)) == NULL){
}
esctrie* cur = ictx->inputescapes;
++esc; // don't encode initial escape as a transition
do{
int valid = *esc;
if(valid <= 0 || valid >= 0x80 || valid == NCKEY_ESC){
logerror("invalid character %d in escape\n", valid);
return -1;
}
if(cur->trie == NULL){
const size_t tsize = sizeof(cur->trie) * 0x80;
if((cur->trie = malloc(tsize)) == NULL){
return -1;
}
memset(cur->trie, 0, tsize);
}
if(cur->trie[valid] == NULL){
if((cur->trie[valid] = create_esctrie_node(NCKEY_INVALID)) == NULL){
return -1;
}
}
if(validate){
if((*cur)->trie == NULL){
const size_t tsize = sizeof((*cur)->trie) * 0x80;
if(((*cur)->trie = malloc(tsize)) == NULL){
return -1;
}
memset((*cur)->trie, 0, tsize);
}
cur = &(*cur)->trie[validate];
}
cur = cur->trie[valid];
++esc;
}while(*esc);
// it appears that multiple keys can be mapped to the same escape string. as
// an example, see "kend" and "kc1" in st ("simple term" from suckless) :/.
if((*cur)->special != NCKEY_INVALID){ // already had one here!
if((*cur)->special != special){
logwarn("already added escape (got 0x%x, wanted 0x%x)\n", (*cur)->special, special);
if(cur->special != NCKEY_INVALID){ // already had one here!
if(cur->special != special){
logwarn("already added escape (got 0x%x, wanted 0x%x)\n", cur->special, special);
}
}else{
(*cur)->special = special;
(*cur)->shift = shift;
(*cur)->ctrl = ctrl;
(*cur)->alt = alt;
cur->special = special;
cur->shift = shift;
cur->ctrl = ctrl;
cur->alt = alt;
}
return 0;
}
@ -310,6 +324,7 @@ inputctx_add_input_escape(inputctx* ictx, const char* esc, uint32_t special,
// https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions
static int
prep_kitty_special_keys(inputctx* nc){
// we do not list here those already handled by prep_windows_special_keys()
static const struct {
const char* esc;
uint32_t key;
@ -389,6 +404,7 @@ prep_all_keys(inputctx* ictx){
input_free_esctrie(&ictx->inputescapes);
return -1;
}
ictx->triepos = ictx->inputescapes;
return 0;
}
@ -625,8 +641,88 @@ mouse_click(inputctx* ictx){
ni->ctrl = ictx->p2 & 0x10;
ni->alt = ictx->p2 & 0x08;
ni->shift = ictx->p2 & 0x04;
ni->x = ictx->p3;
ni->y = ictx->numeric;
// convert from 1- to 0-indexing and account for margins
ni->x = ictx->p3 - 1 - ictx->lmargin;
ni->y = ictx->numeric - 1 - ictx->tmargin;
if(++ictx->iwrite == ictx->isize){
ictx->iwrite = 0;
}
++ictx->ivalid;
pthread_mutex_unlock(&ictx->ilock);
pthread_cond_broadcast(&ictx->icond);
}
static inline void
inc_input_events(inputctx* ictx){
pthread_mutex_lock(&ictx->stats->lock);
++ictx->stats->s.input_events;
pthread_mutex_unlock(&ictx->stats->lock);
}
// add a decoded, valid Unicode to the bulk output buffer, or drop it if no
// space is available.
static void
add_unicode(inputctx* ictx, uint32_t id){
pthread_mutex_lock(&ictx->ilock);
if(ictx->ivalid == ictx->isize){
pthread_mutex_unlock(&ictx->ilock);
logerror("dropping input 0x%08xx\n", id);
return;
}
ncinput* ni = ictx->inputs + ictx->iwrite;
ni->id = id;
ni->alt = false;
ni->ctrl = false;
ni->shift = false;
ni->x = ni->y = 0;
if(++ictx->iwrite == ictx->isize){
ictx->iwrite = 0;
}
++ictx->ivalid;
inc_input_events(ictx);
pthread_mutex_unlock(&ictx->ilock);
pthread_cond_broadcast(&ictx->icond);
}
static void
alt_key(inputctx* ictx, unsigned id){
pthread_mutex_lock(&ictx->ilock);
if(ictx->ivalid == ictx->isize){
pthread_mutex_unlock(&ictx->ilock);
logerror("dropping input 0x%08xx\n", ictx->triepos->special);
return;
}
ncinput* ni = ictx->inputs + ictx->iwrite;
ni->id = id;
ni->alt = true;
ni->ctrl = false;
ni->shift = false;
ni->x = ni->y = 0;
if(++ictx->iwrite == ictx->isize){
ictx->iwrite = 0;
}
++ictx->ivalid;
inc_input_events(ictx);
pthread_mutex_unlock(&ictx->ilock);
pthread_cond_broadcast(&ictx->icond);
}
static void
special_key(inputctx* ictx){
assert(ictx->triepos);
assert(NCKEY_INVALID != ictx->triepos->special);
pthread_mutex_lock(&ictx->ilock);
if(ictx->ivalid == ictx->isize){
pthread_mutex_unlock(&ictx->ilock);
logerror("dropping input 0x%08xx\n", ictx->triepos->special);
return;
}
ncinput* ni = ictx->inputs + ictx->iwrite;
ni->id = ictx->triepos->special;
ni->alt = ictx->triepos->alt;
ni->ctrl = ictx->triepos->ctrl;
ni->shift = ictx->triepos->shift;
ni->x = ni->y = 0;
if(++ictx->iwrite == ictx->isize){
ictx->iwrite = 0;
}
@ -660,7 +756,7 @@ kitty_kbd(inputctx* ictx){
ni->ctrl = !!((ictx->numeric - 1) & 0x4);
// FIXME decode remaining modifiers through 128
// standard keyboard protocol reports ctrl+ascii as the capital form,
// so (for now) conform with kitty protocol...
// so (for now) conform when using kitty protocol...
if(ni->id < 128 && islower(ni->id) && ni->ctrl){
ni->id = toupper(ni->id);
}
@ -744,7 +840,7 @@ pump_control_read(inputctx* ictx, unsigned char c){
if(c == '1'){
ictx->state = STATE_BG2;
}else{
// FIXME
ictx->state = STATE_NULL;
}
break;
case STATE_BG2:
@ -753,7 +849,7 @@ pump_control_read(inputctx* ictx, unsigned char c){
ictx->stridx = 0;
ictx->runstring[0] = '\0';
}else{
// FIXME
ictx->state = STATE_NULL;
}
break;
case STATE_BGSEMI: // drain string
@ -844,8 +940,9 @@ pump_control_read(inputctx* ictx, unsigned char c){
}else if(c == 'R'){
//fprintf(stderr, "CURSOR X: %d\n", ictx->numeric);
if(ictx->initdata){
ictx->initdata->cursorx = ictx->numeric - 1;
ictx->initdata->cursory = ictx->p2 - 1;
// convert from 1- to 0-indexing, and account for margins
ictx->initdata->cursorx = ictx->numeric - 1 - ictx->lmargin;
ictx->initdata->cursory = ictx->p2 - 1 - ictx->tmargin;
}else{
pthread_mutex_lock(&ictx->clock);
if(ictx->cvalid == ictx->csize){
@ -951,7 +1048,7 @@ pump_control_read(inputctx* ictx, unsigned char c){
ictx->stridx = 0;
ictx->runstring[0] = '\0';
}else{
// FIXME error?
ictx->state = STATE_NULL;
}
break;
case STATE_XTVERSION2:
@ -964,14 +1061,14 @@ pump_control_read(inputctx* ictx, unsigned char c){
if(c == '+'){
ictx->state = STATE_XTGETTCAP2;
}else{
// FIXME malformed
ictx->state = STATE_NULL;
}
break;
case STATE_XTGETTCAP2:
if(c == 'r'){
ictx->state = STATE_XTGETTCAP3;
}else{
// FIXME malformed
ictx->state = STATE_NULL;
}
break;
case STATE_XTGETTCAP3:
@ -1010,7 +1107,7 @@ pump_control_read(inputctx* ictx, unsigned char c){
ictx->stridx = 0;
ictx->runstring[0] = '\0';
}else{
// FIXME
ictx->state = STATE_NULL;
}
break;
case STATE_TDA2:
@ -1211,6 +1308,30 @@ process_escape(inputctx* ictx, const unsigned char* buf, int buflen){
if(r == 1){
handoff_initial_responses(ictx);
}
// FIXME this drops alt+ characters intermingled with responses
if(ictx->initdata == NULL){
// an escape always resets the trie, as does a NULL transition
unsigned char candidate = buf[used];
if(candidate == NCKEY_ESC){
ictx->triepos = ictx->inputescapes;
}else if(ictx->triepos->trie[candidate] == NULL){
if(ictx->state == STATE_ESC){
if(candidate && candidate <= 0x80){ // FIXME what about supraASCII utf8?
alt_key(ictx, candidate);
}
}
ictx->triepos = ictx->inputescapes;
}else{
ictx->triepos = ictx->triepos->trie[candidate];
logtrace("triepos: %p in: %u special: 0x%08x\n", ictx->triepos, candidate, ictx->triepos->special);
if(ictx->triepos->special != NCKEY_INVALID){ // match! mark and reset
special_key(ictx);
ictx->triepos = ictx->inputescapes;
}else if(ictx->triepos->trie == NULL){
ictx->triepos = ictx->inputescapes;
}
}
}
++used;
}
return used;
@ -1245,12 +1366,20 @@ process_input(const unsigned char* buf, int buflen, ncinput* ni){
return 1;
}
if(buf[0] < 0x80){ // pure ascii can't show up mid-utf8
ni->id = buf[0];
if(buf[0] == '\n' || buf[0] == '\r'){
ni->id = NCKEY_ENTER;
}else if(buf[0] > 0 && buf[0] <= 26 && buf[0] != '\t'){
ni->id = buf[0] + 'A' - 1;
ni->ctrl = true;
}else{
ni->id = buf[0];
}
return 1;
}
int cpointlen = 0;
wchar_t w;
mbstate_t mbstate;
// FIXME verify that we have enough length based off first char
while(++cpointlen <= (int)MB_CUR_MAX && cpointlen < buflen){
//fprintf(stderr, "CANDIDATE: %d cpointlen: %zu cpoint: %d\n", candidate, cpointlen, cpoint[cpointlen]);
// FIXME how the hell does this work with 16-bit wchar_t?
@ -1263,7 +1392,6 @@ process_input(const unsigned char* buf, int buflen, ncinput* ni){
}
}
// FIXME input error stat
// FIXME extract modifiers
return 0;
}
@ -1280,6 +1408,7 @@ process_ncinput(inputctx* ictx, const unsigned char* buf, int buflen){
ncinput* ni = ictx->inputs + ictx->iwrite;
int r = process_input(buf, buflen, ni);
if(r > 0){
inc_input_events(ictx);
if(++ictx->iwrite == ictx->isize){
ictx->iwrite = 0;
}
@ -1335,6 +1464,10 @@ process_melange(inputctx* ictx, const unsigned char* buf, int* bufused){
// walk the matching automaton from wherever we were.
static void
process_ibuf(inputctx* ictx){
if(resize_seen){
add_unicode(ictx, NCKEY_RESIZE);
resize_seen = 0;
}
if(ictx->tbufvalid){
// we could theoretically do this in parallel with process_bulk, but it
// hardly seems worthwhile without breaking apart the fetches of input.
@ -1406,10 +1539,12 @@ block_on_input(inputctx* ictx){
sigdelset(&smask, SIGWINCH);
while((events = ppoll(pfds, pfdcount, ts, &smask)) < 0){
#endif
fprintf(stderr, "GOT EVENTS: %d!\n", events);
if(errno != EINTR && errno != EAGAIN && errno != EBUSY && errno != EWOULDBLOCK){
return -1;
}
if(resize_seen){
fprintf(stderr, "SAW A RESIZE!\n");
return 1;
}
}
@ -1445,8 +1580,9 @@ input_thread(void* vmarshall){
return NULL;
}
int init_inputlayer(tinfo* ti, FILE* infp){
inputctx* ictx = create_inputctx(ti, infp);
int init_inputlayer(tinfo* ti, FILE* infp, int lmargin, int tmargin,
ncsharedstats* stats){
inputctx* ictx = create_inputctx(ti, infp, lmargin, tmargin, stats);
if(ictx == NULL){
return -1;
}
@ -1478,8 +1614,7 @@ int inputready_fd(const inputctx* ictx){
}
static inline uint32_t
internal_get(inputctx* ictx, const struct timespec* ts, ncinput* ni,
int lmargin, int tmargin){
internal_get(inputctx* ictx, const struct timespec* ts, ncinput* ni){
pthread_mutex_lock(&ictx->ilock);
while(!ictx->ivalid){
pthread_cond_wait(&ictx->icond, &ictx->ilock);
@ -1489,20 +1624,9 @@ internal_get(inputctx* ictx, const struct timespec* ts, ncinput* ni,
ictx->iread = 0;
}
--ictx->ivalid;
ni->seqnum = ++ictx->seqnum;
pthread_mutex_unlock(&ictx->ilock);
// FIXME adjust mouse coordinates for margins
return ni->id;
/*
uint32_t r = inputctx_prestamp(&nc->tcache, ts, ni,
nc->margin_l, nc->margin_t);
if(r != (uint32_t)-1){
uint64_t stamp = nc->tcache.input.input_events++; // need increment even if !ni
if(ni){
ni->seqnum = stamp;
}
}
return r;
*/
}
struct initial_responses* inputlayer_get_responses(inputctx* ictx){
@ -1519,8 +1643,7 @@ struct initial_responses* inputlayer_get_responses(inputctx* ictx){
// infp has already been set non-blocking
uint32_t notcurses_get(notcurses* nc, const struct timespec* ts, ncinput* ni){
uint32_t r = internal_get(nc->tcache.ictx, ts, ni,
nc->margin_l, nc->margin_t);
uint32_t r = internal_get(nc->tcache.ictx, ts, ni);
if(r != (uint32_t)-1){
++nc->stats.s.input_events;
}
@ -1546,7 +1669,7 @@ int notcurses_getvec(notcurses* n, const struct timespec* ts,
}
uint32_t ncdirect_get(ncdirect* n, const struct timespec* ts, ncinput* ni){
return internal_get(n->tcache.ictx, ts, ni, 0, 0);
return internal_get(n->tcache.ictx, ts, ni);
}
uint32_t notcurses_getc(notcurses* nc, const struct timespec* ts,

View File

@ -11,9 +11,11 @@ extern "C" {
struct tinfo;
struct inputctx;
struct ncsharedstats;
int init_inputlayer(struct tinfo* ti, FILE* infp)
__attribute__ ((nonnull (1, 2)));
int init_inputlayer(struct tinfo* ti, FILE* infp, int lmargin, int tmargin,
struct ncsharedstats* stats)
__attribute__ ((nonnull (1, 2, 5)));
int stop_inputlayer(struct tinfo* ti);

View File

@ -272,6 +272,15 @@ typedef struct nctabbed {
nctabbed_options opts; // copied in nctabbed_create()
} nctabbed;
// various moving parts within a notcurses context (and the user) might need to
// access the stats object, so throw a lock on it. we don't want the lock in
// the actual structure since (a) it's usually unnecessary and (b) it breaks
// memset() and memcpy().
typedef struct ncsharedstats {
pthread_mutex_t lock;
ncstats s;
} ncsharedstats;
typedef struct ncdirect {
ncpalette palette; // 256-indexed palette can be used instead of/with RGB
FILE* ttyfp; // FILE* for output tty
@ -280,6 +289,7 @@ typedef struct ncdirect {
uint16_t stylemask; // current styles
bool initialized_readline; // have we initialized Readline?
uint64_t flags; // copied in ncdirect_init() from param
ncsharedstats stats; // stats! not as broadly used as in notcurses
} ncdirect;
// Extracellular state for a cell during the render process. There is one
@ -330,15 +340,6 @@ typedef struct ncpile {
sprixel* sprixelcache; // list of sprixels
} ncpile;
// various moving parts within a notcurses context (and the user) might need to
// access the stats object, so throw a lock on it. we don't want the lock in
// the actual structure since (a) it's usually unnecessary and (b) it breaks
// memset() and memcpy().
typedef struct ncsharedstats {
pthread_mutex_t lock;
ncstats s;
} ncsharedstats;
// the standard pile can be reached through ->stdplane.
typedef struct notcurses {
ncplane* stdplane; // standard plane, covers screen
@ -1759,6 +1760,30 @@ cancel_and_join(const char* name, pthread_t tid, void** res){
return 0;
}
static inline int
emit_scrolls(const tinfo* ti, int count, fbuf* f){
if(count > 1){
const char* indn = get_escape(ti, ESCAPE_INDN);
if(indn){
if(fbuf_emit(f, tiparm(indn, count)) < 0){
return -1;
}
return 0;
}
}
const char* ind = get_escape(ti, ESCAPE_IND);
if(ind == NULL){
ind = "\v";
}
while(count > 0){
if(fbuf_emit(f, ind) < 0){
return -1;
}
--count;
}
return 0;
}
#undef ALLOC
#undef API

View File

@ -1089,7 +1089,8 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
if(interrogate_terminfo(&ret->tcache, opts->termtype, ret->ttyfp, utf8,
opts->flags & NCOPTION_NO_ALTERNATE_SCREEN, 0,
opts->flags & NCOPTION_NO_FONT_CHANGES,
cursory, cursorx, &ret->stats)){
cursory, cursorx, &ret->stats,
ret->margin_l, ret->margin_t)){
fbuf_free(&ret->rstate.f);
pthread_mutex_destroy(&ret->pilelock);
pthread_mutex_destroy(&ret->stats.lock);

View File

@ -983,26 +983,7 @@ rasterize_scrolls(const ncpile* p, fbuf* f){
return -1;
}
}
if(scrolls > 1){
const char* indn = get_escape(&p->nc->tcache, ESCAPE_INDN);
if(indn){
if(fbuf_emit(f, tiparm(indn, scrolls)) < 0){
return -1;
}
return 0;
}
}
const char* ind = get_escape(&p->nc->tcache, ESCAPE_IND);
if(ind == NULL){
ind = "\v";
}
while(scrolls > 0){
if(fbuf_emit(f, ind) < 0){
return -1;
}
--scrolls;
}
return 0;
return emit_scrolls(&p->nc->tcache, scrolls, f);
}
// second sprixel pass in rasterization. by this time, all sixels are handled

View File

@ -719,7 +719,8 @@ macos_early_matches(void){
// full round trip before getting the reply, which is likely to pace init.
int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned utf8,
unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts,
int* cursor_y, int* cursor_x, ncsharedstats* stats){
int* cursor_y, int* cursor_x, ncsharedstats* stats,
int lmargin, int tmargin){
int foolcursor_x, foolcursor_y;
if(!cursor_x){
cursor_x = &foolcursor_x;
@ -779,7 +780,7 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
goto err;
}
}
if(init_inputlayer(ti, stdin)){
if(init_inputlayer(ti, stdin, lmargin, tmargin, stats)){
goto err;
}
#ifndef __MINGW64__

View File

@ -210,7 +210,8 @@ term_supported_styles(const tinfo* ti){
int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out,
unsigned utf8, unsigned noaltscreen, unsigned nocbreak,
unsigned nonewfonts, int* cursor_y, int* cursor_x,
struct ncsharedstats* stats);
struct ncsharedstats* stats, int lmargin, int tmargin)
__attribute__ ((nonnull (1, 2, 3, 10)));
void free_terminfo_cache(tinfo* ti);