more work on input thread

pull/2166/head
nick black 3 years ago
parent e54661f425
commit 79610b2efa
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -2,6 +2,13 @@
#include "internal.h"
#include "in.h"
static sig_atomic_t resize_seen;
// called for SIGWINCH and SIGCONT
void sigwinch_handler(int signo){
resize_seen = signo;
}
// data collected from responses to our terminal queries.
typedef struct termqueries {
int celly, cellx; // cell geometry on startup
@ -19,10 +26,10 @@ typedef struct cursorloc {
// local state for the input thread
typedef struct inputctx {
// we do not close any of these file descriptors; we don't own them!
int termfd; // terminal fd: -1 with no controlling terminal, or
// if stdin is a terminal, and on Windows Terminal.
int stdinfd; // stdin fd. always 0.
int stdinfd; // bulk in fd. always >= 0 (almost always 0). we do not
// own this descriptor, and must not close() it.
#ifdef __MINGW64__
HANDLE stdinhandle; // handle to input terminal
#endif
@ -45,23 +52,27 @@ typedef struct inputctx {
} inputctx;
static inline inputctx*
create_inputctx(tinfo* ti){
create_inputctx(tinfo* ti, FILE* infp){
inputctx* i = malloc(sizeof(*i));
if(i){
i->csize = 64;
if( (i->csrs = malloc(sizeof(*i->csrs) * i->csize)) ){
i->isize = BUFSIZ;
if( (i->inputs = malloc(sizeof(*i->inputs) * i->isize)) ){
// FIXME set up infd/handle
i->termfd = -1;
i->stdinfd = -1;
i->ti = ti;
i->cvalid = i->ivalid = 0;
i->cwrite = i->iwrite = 0;
i->cread = i->iread = 0;
i->ibufvalid = i->ibufwrite = 0;
i->ibufread = 0;
return i;
if((i->stdinfd = fileno(infp)) >= 0){
if(set_fd_nonblocking(i->stdinfd, 1, &ti->stdio_blocking_save) == 0){
i->termfd = tty_check(i->stdinfd) ? -1 : get_tty_fd(infp);
i->ti = ti;
i->cvalid = i->ivalid = 0;
i->cwrite = i->iwrite = 0;
i->cread = i->iread = 0;
i->ibufvalid = i->ibufwrite = 0;
i->ibufread = 0;
logdebug("input descriptors: %d/%d\n", i->stdinfd, i->termfd);
return i;
}
}
free(i->inputs);
}
free(i->csrs);
}
@ -73,7 +84,10 @@ create_inputctx(tinfo* ti){
static inline void
free_inputctx(inputctx* i){
if(i){
// we *do not* own any file descriptors or handles; don't close them!
// we *do not* own stdinfd; don't close() it! we do own termfd.
if(i->termfd >= 0){
close(i->termfd);
}
// do not kill the thread here, either.
free(i->inputs);
free(i->csrs);
@ -160,8 +174,8 @@ input_thread(void* vmarshall){
return NULL;
}
int init_inputlayer(tinfo* ti){
inputctx* ictx = create_inputctx(ti);
int init_inputlayer(tinfo* ti, FILE* infp){
inputctx* ictx = create_inputctx(ti, infp);
if(ictx == NULL){
return -1;
}
@ -180,9 +194,58 @@ int stop_inputlayer(tinfo* ti){
if(ti->ictx){
loginfo("tearing down input thread\n");
ret |= cancel_and_join("input", ti->ictx->tid, NULL);
ret |= set_fd_nonblocking(ti->ictx->stdinfd, ti->stdio_blocking_save, NULL);
free_inputctx(ti->ictx);
ti->ictx = NULL;
}
}
return ret;
}
int inputready_fd(const inputctx* ictx){
return ictx->stdinfd;
}
// infp has already been set non-blocking
uint32_t notcurses_get(notcurses* nc, const struct timespec* ts, ncinput* ni){
/*
uint32_t r = ncinputlayer_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;
}
++nc->stats.s.input_events;
}
return r;
*/
return -1;
}
uint32_t notcurses_getc(notcurses* nc, const struct timespec* ts,
const void* unused, ncinput* ni){
(void)unused; // FIXME remove for abi3
return notcurses_get(nc, ts, ni);
}
uint32_t ncdirect_get(struct ncdirect* n, const struct timespec* ts, ncinput* ni){
/*
uint32_t r = ncinputlayer_prestamp(&n->tcache, ts, ni, 0, 0);
if(r != (uint32_t)-1){
uint64_t stamp = n->tcache.input.input_events++; // need increment even if !ni
if(ni){
ni->seqnum = stamp;
}
}
return r;
*/
return -1;
}
uint32_t ncdirect_getc(ncdirect* nc, const struct timespec *ts,
const void* unused, ncinput* ni){
(void)unused; // FIXME remove for abi3
return ncdirect_get(nc, ts, ni);
}

File diff suppressed because it is too large Load Diff

@ -375,7 +375,6 @@ typedef struct notcurses {
int loglevel;
ncpalette palette; // 256-indexed palette can be used instead of/with RGB
bool palette_damage[NCPALETTESIZE];
unsigned stdio_blocking_save; // was stdio blocking at entry? restore on stop.
uint64_t flags; // copied from notcurses_options
} notcurses;

@ -1150,9 +1150,6 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
ncplane_cursor_move_yx(ret->stdplane, ret->rstate.logendy, ret->rstate.logendx);
}
}
if(set_fd_nonblocking(ret->tcache.input.infd, 1, &ret->stdio_blocking_save)){
goto err;
}
if(!(opts->flags & NCOPTION_NO_ALTERNATE_SCREEN)){
// perform an explicit clear since the alternate screen was requested
// (smcup *might* clear, but who knows? and it might not have been
@ -1247,7 +1244,6 @@ int notcurses_stop(notcurses* nc){
goto_location(nc, &nc->rstate.f, targy, 0);
fbuf_finalize(&nc->rstate.f, stdout);
}
ret |= set_fd_nonblocking(nc->tcache.input.infd, nc->stdio_blocking_save, NULL);
if(nc->stdplane){
notcurses_drop_planes(nc);
free_plane(nc->stdplane);
@ -2682,11 +2678,11 @@ int notcurses_lex_margins(const char* op, notcurses_options* opts){
}
int notcurses_inputready_fd(notcurses* n){
return n->tcache.input.infd;
return inputready_fd(n->tcache.ictx);
}
int ncdirect_inputready_fd(ncdirect* n){
return n->tcache.input.infd;
return inputready_fd(n->tcache.ictx);
}
// FIXME speed this up, PoC

@ -168,7 +168,6 @@ match_termname(const char* termname, queried_terminals_e* qterm){
void free_terminfo_cache(tinfo* ti){
stop_inputlayer(ti);
ncinputlayer_stop(&ti->input);
free(ti->termversion);
free(ti->esctable);
#ifdef __linux__
@ -919,17 +918,18 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
}
unsigned appsync_advertised = 0;
unsigned kittygraphs = 0;
if(init_inputlayer(ti)){
if(init_inputlayer(ti, stdin)){
goto err;
}
/*
if(ncinputlayer_init(ti, stdin, &ti->qterm, &appsync_advertised,
cursor_y, cursor_x, stats, &kittygraphs)){
goto err;
}
*/
if(nocbreak){
if(ti->ttyfd >= 0){
if(tcsetattr(ti->ttyfd, TCSANOW, ti->tpreserved)){
ncinputlayer_stop(&ti->input);
goto err;
}
}
@ -947,7 +947,6 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
bool invertsixel = false;
if(apply_term_heuristics(ti, tname, ti->qterm, &tablelen, &tableused,
&invertsixel, nonewfonts)){
ncinputlayer_stop(&ti->input);
goto err;
}
build_supported_styles(ti);
@ -997,48 +996,6 @@ char* termdesc_longterm(const tinfo* ti){
return ret;
}
// when we have input->ttyfd, everything's simple -- we're reading from a
// different source than the user is, so we can just write the query, and block
// on the response, easy peasy.
// FIXME still, we ought reuse buffer, and pass on any excess reads...
static int
locate_cursor_simple(tinfo* ti, const char* u7, int* cursor_y, int* cursor_x){
if(ti->qterm == TERMINAL_MSTERMINAL){
return locate_cursor(ti, cursor_y, cursor_x);
}
char* buf = malloc(BUFSIZ);
if(buf == NULL){
return -1;
}
loginfo("sending cursor report request\n");
if(tty_emit(u7, ti->ttyfd)){
free(buf);
return -1;
}
ssize_t r;
do{
if((r = read(ti->input.infd, buf, BUFSIZ - 1)) > 0){
buf[r] = '\0';
if(sscanf(buf, "\e[%d;%dR", cursor_y, cursor_x) != 2){
loginfo("not a cursor location report: %s\n", buf);
free(buf);
return -1;
}
--*cursor_y;
--*cursor_x;
break;
}
}while(errno == EAGAIN || errno == EWOULDBLOCK || errno == EBUSY || errno == EINTR);
if(r < 0){
logerror("error reading cursor location from %d (%s)\n", ti->input.infd, strerror(errno));
free(buf);
return -1;
}
free(buf);
loginfo("located cursor with %d: %d/%d\n", ti->ttyfd, *cursor_y, *cursor_x);
return 0;
}
// send a u7 request, and wait until we have a cursor report. if input's ttyfd
// is valid, we can just camp there. otherwise, we need dance with potential
// user input looking at infd.
@ -1063,39 +1020,16 @@ int locate_cursor(tinfo* ti, int* cursor_y, int* cursor_x){
logwarn("No support in terminfo\n");
return -1;
}
if(ti->ttyfd >= 0){
return locate_cursor_simple(ti, u7, cursor_y, cursor_x);
}
int fd = ti->input.infd;
if(fd < 0){
if(ti->ttyfd < 0){
logwarn("No valid path for cursor report\n");
return -1;
}
bool emitted_u7 = false; // only want to send one max
cursorreport* clr;
loginfo("Acquiring input lock\n");
pthread_mutex_lock(&ti->input.lock);
while((clr = ti->input.creport_queue) == NULL){
logdebug("No report yet\n");
if(!emitted_u7){
logdebug("Emitting u7\n");
// FIXME i'd rather not do this while holding the lock =[
if(tty_emit(u7, fd)){
pthread_mutex_unlock(&ti->input.lock);
return -1;
}
emitted_u7 = true;
}
// this can block. we must enter holding the input lock, and it will
// return to us holding the input lock.
ncinput_extract_clrs(ti);
if( (clr = ti->input.creport_queue) ){
break;
}
pthread_cond_wait(&ti->input.creport_cond, &ti->input.lock);
int fd = ti->ttyfd;
if(tty_emit(u7, fd)){
return -1;
}
ti->input.creport_queue = clr->next;
pthread_mutex_unlock(&ti->input.lock);
// FIXME get that report
/*
loginfo("Got a report from %d %d/%d\n", fd, clr->y, clr->x);
*cursor_y = clr->y;
*cursor_x = clr->x;
@ -1105,5 +1039,45 @@ int locate_cursor(tinfo* ti, int* cursor_y, int* cursor_x){
*cursor_x = tmp;
}
free(clr);
*/
return 0;
}
int cbreak_mode(tinfo* ti){
#ifndef __MINGW64__
int ttyfd = ti->ttyfd;
if(ttyfd < 0){
return 0;
}
// assume it's not a true terminal (e.g. we might be redirected to a file)
struct termios modtermios;
memcpy(&modtermios, ti->tpreserved, sizeof(modtermios));
// see termios(3). disabling ECHO and ICANON means input will not be echoed
// to the screen, input is made available without enter-based buffering, and
// line editing is disabled. since we have not gone into raw mode, ctrl+c
// etc. still have their typical effects. ICRNL maps return to 13 (Ctrl+M)
// instead of 10 (Ctrl+J).
modtermios.c_lflag &= (~ECHO & ~ICANON);
modtermios.c_iflag &= ~ICRNL;
if(tcsetattr(ttyfd, TCSANOW, &modtermios)){
logerror("Error disabling echo / canonical on %d (%s)\n", ttyfd, strerror(errno));
return -1;
}
#else
// we don't yet have a way to take Cygwin/MSYS2 out of canonical mode. we'll
// hit this stanza in MSYS2; allow the GetConsoleMode() to fail for now. this
// means we'll need enter pressed after the query response, obviously an
// unacceptable state of affairs...FIXME
DWORD mode;
if(!GetConsoleMode(ti->inhandle, &mode)){
logerror("error acquiring input mode\n");
return 0; // FIXME is it safe?
}
mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
if(!SetConsoleMode(ti->inhandle, mode)){
logerror("error setting input mode\n");
return -1;
}
#endif
return 0;
}

@ -199,8 +199,8 @@ typedef struct tinfo {
queried_terminals_e qterm; // detected terminal class
// we heap-allocate this one (if we use it), as it's not fully defined on Windows
struct termios *tpreserved;// terminal state upon entry
ncinputlayer input; // input layer
struct inputctx* ictx; // new input layer
unsigned stdio_blocking_save; // was stdio blocking at entry? restore on stop.
// if we get a reply to our initial \e[18t cell geometry query, it will
// replace these values. note that LINES/COLUMNS cannot be used to limit

Loading…
Cancel
Save