mirror of
https://github.com/dankamongmen/notcurses.git
synced 2024-11-06 03:20:26 +00:00
[input] replay non-escapes as regular text #2100
This commit is contained in:
parent
fe34e797a3
commit
8f4fa30e7b
143
src/lib/in.c
143
src/lib/in.c
@ -163,6 +163,9 @@ typedef struct inputctx {
|
||||
tinfo* ti; // link back to tinfo
|
||||
pthread_t tid; // tid for input thread
|
||||
|
||||
unsigned midescape; // we're in the middle of a potential escape. we need
|
||||
// to do a nonblocking read and try to complete it.
|
||||
|
||||
unsigned linesigs; // are line discipline signals active?
|
||||
unsigned drain; // drain away bulk input?
|
||||
ncsharedstats *stats; // stats shared with notcurses context
|
||||
@ -448,6 +451,7 @@ create_inputctx(tinfo* ti, FILE* infp, int lmargin, int tmargin,
|
||||
// (as stored in tpreserved)
|
||||
i->linesigs = 1;
|
||||
i->tbufvalid = 0;
|
||||
i->midescape = 0;
|
||||
i->numeric = 0;
|
||||
i->stridx = 0;
|
||||
i->runstring[i->stridx] = '\0';
|
||||
@ -936,7 +940,8 @@ special_key(inputctx* ictx){
|
||||
pthread_cond_broadcast(&ictx->icond);
|
||||
}
|
||||
|
||||
// ictx->numeric and ictx->p2 have the two parameters
|
||||
// ictx->numeric and ictx->p2 have the two parameters, where ictx->numeric was
|
||||
// optional and indicates a special key with no modifiers.
|
||||
static void
|
||||
kitty_kbd(inputctx* ictx){
|
||||
enum { // synthesized events derived from keypresses
|
||||
@ -944,7 +949,7 @@ kitty_kbd(inputctx* ictx){
|
||||
SYNTH_SIGINT,
|
||||
SYNTH_SIGQUIT,
|
||||
} synth = SYNTH_NOTHING;
|
||||
assert(ictx->numeric > 0);
|
||||
assert(ictx->numeric >= 0);
|
||||
assert(ictx->p2 > 0);
|
||||
pthread_mutex_lock(&ictx->ilock);
|
||||
if(ictx->ivalid == ictx->isize){
|
||||
@ -1097,6 +1102,13 @@ pump_control_read(inputctx* ictx, unsigned char c){
|
||||
ictx->p2 = ictx->numeric;
|
||||
ictx->state = STATE_CURSOR_COL;
|
||||
ictx->numeric = 0;
|
||||
}else if(c == 'u'){
|
||||
// kitty keyboard protocol
|
||||
ictx->p2 = ictx->numeric;
|
||||
ictx->numeric = 0;
|
||||
kitty_kbd(ictx);
|
||||
ictx->state = STATE_NULL;
|
||||
return 2;
|
||||
}else if(c >= 0x40 && c <= 0x7E){
|
||||
ictx->state = STATE_NULL;
|
||||
}
|
||||
@ -1135,6 +1147,7 @@ pump_control_read(inputctx* ictx, unsigned char c){
|
||||
}else if(c == 'M'){
|
||||
mouse_click(ictx);
|
||||
ictx->state = STATE_NULL;
|
||||
return 2;
|
||||
}else{
|
||||
ictx->state = STATE_NULL;
|
||||
}
|
||||
@ -1179,6 +1192,7 @@ pump_control_read(inputctx* ictx, unsigned char c){
|
||||
// kitty keyboard protocol
|
||||
kitty_kbd(ictx);
|
||||
ictx->state = STATE_NULL;
|
||||
return 2;
|
||||
}else if(c == ';'){
|
||||
if(ictx->p2 == 4){
|
||||
if(ictx->initdata){
|
||||
@ -1503,56 +1517,112 @@ handoff_initial_responses(inputctx* ictx){
|
||||
pthread_cond_broadcast(&ictx->icond);
|
||||
}
|
||||
|
||||
// try to lex a control sequence off of buf. return the number of bytes
|
||||
// try to lex a single control sequence off of buf. return the number of bytes
|
||||
// consumed if we do so, and -1 otherwise. buf is *not* necessarily
|
||||
// NUL-terminated. precondition: buflen >= 1.
|
||||
// FIXME we ought play complete failures into the general input buffer?
|
||||
// NUL-terminated. if we are definitely *not* an escape, or we're unsure when
|
||||
// we run out of input, return the negated relevant number of bytes, setting
|
||||
// ictx->midescape if we're uncertain.
|
||||
// precondition: buflen >= 1. precondition: buf[0] == 0x1b.
|
||||
static int
|
||||
process_escape(inputctx* ictx, const unsigned char* buf, int buflen){
|
||||
assert(1 <= buflen);
|
||||
ictx->midescape = 0;
|
||||
// FIXME given that we keep our state across invocations, we want buf offset
|
||||
// by the amount we handled the last iteration, if any was left over...
|
||||
// until then, we reset the state on entry. remove that once we preserve!
|
||||
if(buf[0] != '\x1b'){
|
||||
return -1;
|
||||
}
|
||||
ictx->triepos = ictx->inputescapes;
|
||||
ictx->state = STATE_NULL;
|
||||
// FIXME end material eliminated by maintaining state + offset
|
||||
int used = 0;
|
||||
// FIXME we really want to unite these two automata, ugh
|
||||
while(used < buflen){
|
||||
int r = pump_control_read(ictx, buf[used]);
|
||||
if(r == 1){
|
||||
handoff_initial_responses(ictx);
|
||||
unsigned char candidate = buf[used++];
|
||||
int r = pump_control_read(ictx, candidate);
|
||||
if(r == 2){
|
||||
return used;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
if(r == 1){ // received DA1, ending initial responses
|
||||
// safe to call even if initdata == NULL
|
||||
handoff_initial_responses(ictx);
|
||||
return used;
|
||||
}
|
||||
if(ictx->initdata){
|
||||
continue; // FIXME drops any Alt+chars entwined within initial responses
|
||||
}
|
||||
// an escape always resets the trie, as does a NULL transition
|
||||
if(candidate == NCKEY_ESC){
|
||||
ictx->triepos = ictx->inputescapes;
|
||||
if(used > 1){ // we got reset; replay as input
|
||||
return -(used - 1);
|
||||
}
|
||||
// validated first byte as escape! keep going. otherwise, check trie.
|
||||
// we can safely check trie[candidate] above because we are either coming
|
||||
// off the initial node, which definitely has a valid ->trie, or we're
|
||||
// coming from a transition, where ictx->triepos->trie is checked below.
|
||||
}else if(ictx->triepos->trie[candidate] == NULL){
|
||||
ictx->triepos = ictx->inputescapes;
|
||||
if(ictx->state == STATE_ESC){
|
||||
if(candidate && candidate <= 0x80){ // FIXME what about supraASCII utf8?
|
||||
alt_key(ictx, candidate);
|
||||
return used;
|
||||
}
|
||||
}
|
||||
if(ictx->state == STATE_NULL){
|
||||
// we were an invalid sequence; replay as input. we know used > 1. the
|
||||
// current character is not yet excluded, so return negative "used - 1".
|
||||
return -(used - 1);
|
||||
}
|
||||
}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{
|
||||
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){
|
||||
return used;
|
||||
}else if(ictx->triepos->trie == NULL){
|
||||
if(ictx->state == STATE_NULL){
|
||||
ictx->triepos = ictx->inputescapes;
|
||||
// all inspected characters are invalid; return full negative "used"
|
||||
return -used;
|
||||
}
|
||||
}
|
||||
}
|
||||
++used;
|
||||
}
|
||||
return used;
|
||||
// we exhausted input without knowing whether or not this is a valid control
|
||||
// sequence; we're still on-trie, and need more (immediate) input.
|
||||
ictx->midescape = 1;
|
||||
return -used;
|
||||
}
|
||||
|
||||
// process as many control sequences from |buf|, having |bufused| bytes,
|
||||
// as we can. anything not a valid control sequence is dropped. this text
|
||||
// needn't be valid UTF-8.
|
||||
// needn't be valid UTF-8. this is always called on tbuf; if we find bulk data
|
||||
// here, we need replay it into ibuf (assuming that there's room).
|
||||
static void
|
||||
process_escapes(inputctx* ictx, unsigned char* buf, int* bufused){
|
||||
int offset = 0;
|
||||
while(*bufused){
|
||||
int consumed = process_escape(ictx, buf + offset, *bufused);
|
||||
// if we aren't certain, that's not a control sequence unless we're at
|
||||
// the end of the tbuf, in which case we really do try reading more. if
|
||||
// this was not a sequence, we'll catch it on the next read.
|
||||
if(consumed < 0){
|
||||
if(!ictx->midescape){
|
||||
consumed = -consumed;
|
||||
int available = sizeof(ictx->ibuf) - ictx->ibufvalid;
|
||||
if(available){
|
||||
if(available > consumed){
|
||||
available = consumed;
|
||||
}
|
||||
logwarn("replaying %dB of %dB to ibuf\n", available, consumed);
|
||||
memcpy(ictx->ibuf + ictx->ibufvalid, buf + offset, available);
|
||||
}
|
||||
*bufused -= consumed;
|
||||
offset += consumed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
*bufused -= consumed;
|
||||
@ -1669,7 +1739,19 @@ process_melange(inputctx* ictx, const unsigned char* buf, int* bufused){
|
||||
int consumed = 0;
|
||||
if(buf[offset] == '\x1b'){
|
||||
consumed = process_escape(ictx, buf + offset, *bufused);
|
||||
}else{
|
||||
if(consumed < 0){
|
||||
if(ictx->midescape){
|
||||
if(-consumed != *bufused){ // not at the end; treat it as input
|
||||
// no need to move between buffers; simply ensure we process it as
|
||||
// input, and don't mark anything as consumed.
|
||||
ictx->midescape = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// don't process as input only if we just read a valid control character,
|
||||
// or if we need to read more to determine what it is.
|
||||
if(consumed <= 0 && !ictx->midescape){
|
||||
consumed = process_ncinput(ictx, buf + offset, *bufused);
|
||||
}
|
||||
if(consumed < 0){
|
||||
@ -1714,6 +1796,7 @@ int ncinput_shovel(inputctx* ictx, const void* buf, int len){
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME if ictx->midescape is set, need a nonblocking read!
|
||||
static int
|
||||
block_on_input(inputctx* ictx){
|
||||
struct timespec* ts = NULL; // FIXME
|
||||
|
Loading…
Reference in New Issue
Block a user