Decode input escape sequences to special keys #78

Add the necessary input buffer, non-blocking reads, escape trie,
and unit tests to support extended keys, including arrow keys.
Update notcurses-input to print Unicode Control Glyphs instead of
a blank space for control chars.
pull/124/head
nick black 5 years ago committed by Nick Black
parent cbbc18c3a3
commit af7ca02f36

@ -154,7 +154,11 @@ typedef enum {
NCKEY_RIGHT,
NCKEY_DOWN,
NCKEY_LEFT,
NCKEY_DC, // delete
NCKEY_DEL,
NCKEY_PGDOWN,
NCKEY_PGUP,
NCKEY_HOME,
NCKEY_END,
// FIXME...
} ncspecial_key;
@ -180,6 +184,11 @@ notcurses_getc_blocking(struct notcurses* n, cell* c, ncspecial_key* nkey){
return notcurses_getc(n, c, nkey, NULL, &sigmask);
}
// Add this escape sequence to the trie, resolving to the specified specialkey.
// Exported mainly for the benefit of unit tests.
API int notcurses_add_input_escape(struct notcurses* nc, const char* esc,
ncspecial_key special);
// Refresh our idea of the terminal's dimensions, reshaping the standard plane
// if necessary. Without a call to this function following a terminal resize
// (as signaled via SIGWINCH), notcurses_render() might not function properly.

@ -315,7 +315,7 @@ panelreel_demo_core(struct notcurses* nc, int efd, tabletctx** tctxs){
case NCKEY_RIGHT: ++x; if(panelreel_move(pr, x, y)){ --x; } break;
case NCKEY_UP: panelreel_prev(pr); break;
case NCKEY_DOWN: panelreel_next(pr); break;
case NCKEY_DC: kill_active_tablet(pr, tctxs); break;
case NCKEY_DEL: kill_active_tablet(pr, tctxs); break;
default:
ncplane_cursor_move_yx(w, 3, 2);
ncplane_printf(w, "Unknown special (%d)\n", special);

@ -48,21 +48,24 @@ void input_free_esctrie(esctrie** eptr){
}
}
int input_add_escape(notcurses* nc, const char* esc, ncspecial_key special){
esctrie** cur;
fprintf(stderr, "ADDING: %s for %d\n", esc, special);
int notcurses_add_input_escape(notcurses* nc, const char* esc, ncspecial_key special){
if(esc[0] != ESC || strlen(esc) < 2){ // assume ESC prefix + content
return -1;
}
esctrie** cur = &nc->inputescapes;
do{
//fprintf(stderr, "ADDING: %s (%zu) for %d\n", esc, strlen(esc), special);
++esc;
int validate = *esc;
if(validate < 0 || validate >= 0x80){
return -1;
}
if(nc->inputescapes == NULL){
cur = &nc->inputescapes;
}else if(validate){
if(*cur == NULL){
if((*cur = create_esctrie_node(NCKEY_INVALID)) == NULL){
return -1;
}
}
if(validate){
if((*cur)->trie == NULL){
const size_t tsize = sizeof((*cur)->trie) * 0x80;
(*cur)->trie = malloc(tsize);
@ -70,11 +73,6 @@ int input_add_escape(notcurses* nc, const char* esc, ncspecial_key special){
}
cur = &(*cur)->trie[validate];
}
if(*cur == NULL){
if((*cur = create_esctrie_node(NCKEY_INVALID)) == NULL){
return -1;
}
}
}while(*esc);
if((*cur)->special){ // already had one here!
return -1;
@ -94,13 +92,24 @@ handle_getc(notcurses* nc, cell* c, int kpress, ncspecial_key* special){
}
if(kpress == ESC){
// FIXME delay a little waiting for more?
while(nc->inputbuf_occupied){
const esctrie* esc = nc->inputescapes;
while(esc && esc->special == NCKEY_INVALID && nc->inputbuf_occupied){
int candidate = pop_input_keypress(nc);
// FIXME walk trie via candidate, exiting (and ungetc()ing) on failure
fprintf(stderr, "CANDIDATE: %c\n", candidate);
//fprintf(stderr, "CANDIDATE: %c\n", candidate);
if(esc->trie == NULL){
esc = NULL;
}else if(candidate >= 0x80 || candidate < 0){
esc = NULL;
}else{
esc = esc->trie[candidate];
}
}
if(esc && esc->special != NCKEY_INVALID){
*special = esc->special;
return 1;
}
// FIXME ungetc on failure! walk trie backwards or something
}
*special = 0;
if(kpress == 0x04){ // ctrl-d, treated as EOF
return -1;
}
@ -137,10 +146,11 @@ static int
handle_input(notcurses* nc, cell* c, ncspecial_key* special){
int r;
c->gcluster = 0;
*special = NCKEY_INVALID;
// getc() returns unsigned chars cast to ints
while(!input_queue_full(nc) && (r = getc(nc->ttyinfp)) >= 0){
nc->inputbuf[nc->inputbuf_write_at] = (unsigned char)r;
fprintf(stderr, "OCCUPY: %u@%u read: %d\n", nc->inputbuf_occupied, nc->inputbuf_write_at, nc->inputbuf[nc->inputbuf_write_at]);
//fprintf(stderr, "OCCUPY: %u@%u read: %d\n", nc->inputbuf_occupied, nc->inputbuf_write_at, nc->inputbuf[nc->inputbuf_write_at]);
if(++nc->inputbuf_write_at == sizeof(nc->inputbuf) / sizeof(*nc->inputbuf)){
nc->inputbuf_write_at = 0;
}

@ -104,6 +104,8 @@ typedef struct notcurses {
char* right; // kcuf1
char* up; // kcuu1
char* down; // kcud1
char* del; // delete
char* home; // home
char* npage; // knp
char* ppage; // kpp
@ -127,9 +129,6 @@ typedef struct notcurses {
extern sig_atomic_t resize_seen;
// add this escape sequence to the trie, resolving to the specified specialkey
int input_add_escape(notcurses* nc, const char* esc, ncspecial_key special);
// free up the input escapes trie
void input_free_esctrie(struct esctrie** trie);

@ -520,12 +520,33 @@ interrogate_terminfo(notcurses* nc, const notcurses_options* opts){
term_verify_seq(&nc->italoff, "ritm");
term_verify_seq(&nc->op, "op");
term_verify_seq(&nc->clear, "clear");
term_verify_seq(&nc->left, "kcub1");
term_verify_seq(&nc->right, "kcuf1");
term_verify_seq(&nc->up, "kcuu1");
term_verify_seq(&nc->down, "kcud1");
term_verify_seq(&nc->npage, "knp");
term_verify_seq(&nc->ppage, "kpp");
if(term_verify_seq(&nc->left, "kcub1") == 0){
notcurses_add_input_escape(nc, nc->left, NCKEY_LEFT);
}
if(term_verify_seq(&nc->right, "kcuf1") == 0){
notcurses_add_input_escape(nc, nc->right, NCKEY_RIGHT);
}
if(term_verify_seq(&nc->up, "kcuu1") == 0){
notcurses_add_input_escape(nc, nc->up, NCKEY_UP);
}
if(term_verify_seq(&nc->down, "kcud1") == 0){
notcurses_add_input_escape(nc, nc->down, NCKEY_DOWN);
}
if(term_verify_seq(&nc->del, "kdch1") == 0){
notcurses_add_input_escape(nc, nc->down, NCKEY_DEL);
}
if(term_verify_seq(&nc->home, "kend") == 0){
notcurses_add_input_escape(nc, nc->down, NCKEY_END);
}
if(term_verify_seq(&nc->home, "khome") == 0){
notcurses_add_input_escape(nc, nc->down, NCKEY_HOME);
}
if(term_verify_seq(&nc->npage, "knp") == 0){
notcurses_add_input_escape(nc, nc->down, NCKEY_PGDOWN);
}
if(term_verify_seq(&nc->ppage, "kpp") == 0){
notcurses_add_input_escape(nc, nc->down, NCKEY_PGUP);
}
// Some terminals cannot combine certain styles with colors. Don't advertise
// support for the style in that case.
int nocolor_stylemask = tigetnum("ncv");

@ -1,6 +1,7 @@
#include <string>
#include <cstdlib>
#include <notcurses.h>
#include "internal.h"
#include "main.h"
class NotcursesTest : public :: testing::Test {
@ -103,3 +104,10 @@ TEST_F(NotcursesTest, TileScreenWithPlanes) {
delete[] planes;
ASSERT_EQ(0, notcurses_render(nc_));
}
// build a trie up from terminfo(5)-style escape sequences
TEST_F(NotcursesTest, InputEscapesTrie) {
const char* khome = "\x1bOH";
ASSERT_EQ(0, notcurses_add_input_escape(nc_, khome, NCKEY_HOME));
// FIXME need be able to lookup
}

Loading…
Cancel
Save