mirror of
https://github.com/dankamongmen/notcurses.git
synced 2024-11-04 06:00:30 +00:00
[input] walk specials automaton
This commit is contained in:
parent
fd5708a7cd
commit
716a37f917
@ -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);
|
||||
|
@ -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;
|
||||
|
287
src/lib/in.c
287
src/lib/in.c
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user