get everything prepped for reliable keyboard escapes #78

pull/124/head
nick black 5 years ago committed by Nick Black
parent 04e067c202
commit 37b0e416c0

@ -783,8 +783,6 @@ to implement".
* There is no concept of subwindows which share memory with their parents.
* There is no tracing functionality ala `trace(3NCURSES)`. Superior external
tracing solutions exist, such as `bpftrace`.
* There is no timeout functionality for input (`timeout()`, `halfdelay()`, etc.).
Roll your own with any of the four thousand ways to do it.
## Environment notes

@ -6,6 +6,7 @@
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <stdbool.h>
#ifdef __cplusplus
@ -157,10 +158,27 @@ typedef enum {
// FIXME...
} ncspecial_key;
API int notcurses_getc(const struct notcurses* n, cell* c,
ncspecial_key* special);
API int notcurses_getc_blocking(const struct notcurses* n, cell* c,
ncspecial_key* special);
// See ppoll(2) for more detail. Provide a NULL 'ts' to block at lenghth, a 'ts'
// of 0 for non-blocking operation, and otherwise a timespec to bound blocking.
// Signals in sigmask (less several we handle internally) will be atomically
// masked and unmasked per ppoll(2). It should generally contain all signals.
API int notcurses_getc(struct notcurses* n, cell* c, ncspecial_key* special,
const struct timespec* ts, sigset_t* sigmask);
static inline int
notcurses_getc_nblock(struct notcurses* n, cell* c, ncspecial_key* nkey){
sigset_t sigmask;
sigfillset(&sigmask);
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
return notcurses_getc(n, c, nkey, &ts, &sigmask);
}
static inline int
notcurses_getc_blocking(struct notcurses* n, cell* c, ncspecial_key* nkey){
sigset_t sigmask;
sigemptyset(&sigmask);
return notcurses_getc(n, c, nkey, NULL, &sigmask);
}
// Refresh our idea of the terminal's dimensions, reshaping the standard plane
// if necessary. Without a call to this function following a terminal resize

@ -219,7 +219,7 @@ handle_input(struct notcurses* nc, struct panelreel* pr, int efd,
fprintf(stderr, "Error polling on stdin/eventfd (%s)\n", strerror(errno));
}else{
if(fds[0].revents & POLLIN){
key = notcurses_getc(nc, c, special);
key = notcurses_getc_blocking(nc, c, special);
if(key < 0){
return -1;
}

@ -23,6 +23,14 @@ const char* nckeystr(ncspecial_key key){
}
}
// print the utf8 Control Pictures for otherwise unprintable chars
wchar_t printutf8(int kp){
if(kp <= 27 && kp >= 0){
return 0x2400 + kp;
}
return kp;
}
int main(void){
if(setlocale(LC_ALL, "") == nullptr){
return EXIT_FAILURE;
@ -57,7 +65,8 @@ int main(void){
special, special, nckeystr(special)) < 0){
break;
}
}else if(ncplane_printf(n, "Got UTF-8: [0x%04x (%04d)] '%c'\n", kp, kp, kp) < 0){
}else if(ncplane_printf(n, "Got ASCII: [0x%02x (%03d)] '%lc'\n",
kp, kp, isprint(kp) ? kp : printutf8(kp)) < 0){
break;
}
}else{

@ -1,79 +1,110 @@
#include <sys/poll.h>
#include "internal.h"
static const unsigned char ESC = 0x1b; // 27
sig_atomic_t resize_seen = 0;
static inline int
pop_input_keypress(notcurses* nc){
int candidate = nc->inputbuf[nc->inputbuf_valid_starts];
fprintf(stderr, "DEOCCUPY: %u@%u read: %d\n", nc->inputbuf_occupied, nc->inputbuf_valid_starts, nc->inputbuf[nc->inputbuf_valid_starts]);
if(++nc->inputbuf_valid_starts == sizeof(nc->inputbuf) / sizeof(*nc->inputbuf)){
nc->inputbuf_valid_starts = 0;
}
--nc->inputbuf_occupied;
return candidate;
}
// add the keypress we just read to our input queue (assuming there is room).
// if there is a full UTF8 codepoint or keystroke (composed or otherwise),
// return it, and pop it from the queue.
static int
handle_getc(const notcurses* nc __attribute__ ((unused)), cell* c, int kpress,
ncspecial_key* special){
handle_getc(notcurses* nc, cell* c, int kpress, ncspecial_key* special){
fprintf(stderr, "KEYPRESS: %d\n", kpress);
if(kpress < 0){
return -1;
}
// FIXME add it to the queue, then consult queue
if(kpress == ESC){
fprintf(stderr, "ESCAPE OH SHIT\n");
// FIXME delay a little waiting for more?
while(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);
}
}
*special = 0;
if(kpress == 0x04){ // ctrl-d
if(kpress == 0x04){ // ctrl-d, treated as EOF
return -1;
}
// FIXME look for keypad
if(kpress < 0x80){
c->gcluster = kpress;
}else{
// FIXME
// FIXME load up zee utf8
}
return 1;
}
int notcurses_getc(const notcurses* nc, cell* c, ncspecial_key* special){
// blocks up through ts (infinite with NULL ts), returning number of events
// (0 on timeout) or -1 on error/interruption.
static int
block_on_input(FILE* fp, const struct timespec* ts, sigset_t* sigmask){
struct pollfd pfd = {
.fd = fileno(fp),
.events = POLLIN | POLLRDHUP,
.revents = 0,
};
sigdelset(sigmask, SIGWINCH);
sigdelset(sigmask, SIGINT);
sigdelset(sigmask, SIGQUIT);
sigdelset(sigmask, SIGSEGV);
return ppoll(&pfd, 1, ts, sigmask);
}
static bool
input_queue_full(const notcurses* nc){
return nc->inputbuf_occupied == sizeof(nc->inputbuf) / sizeof(*nc->inputbuf);
}
static int
handle_input(notcurses* nc, cell* c, ncspecial_key* special){
int r;
c->gcluster = 0;
// 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]);
if(++nc->inputbuf_write_at == sizeof(nc->inputbuf) / sizeof(*nc->inputbuf)){
nc->inputbuf_write_at = 0;
}
++nc->inputbuf_occupied;
}
// highest priority is resize notifications, since they don't queue
if(resize_seen){
resize_seen = 0;
c->gcluster = 0;
*special = NCKEY_RESIZE;
return 1;
}
// FIXME check queue
int r = getc(nc->ttyinfp);
if(r < 0){
return r;
// if there was some error in getc(), we still dole out the existing queue
if(nc->inputbuf_occupied == 0){
return -1;
}
r = pop_input_keypress(nc);
return handle_getc(nc, c, r, special);
}
// we set our infd to non-blocking on entry, so to do a blocking call (without
// burning cpu) we'll need to set up a poll().
int notcurses_getc_blocking(const notcurses* nc, cell* c, ncspecial_key* special){
struct pollfd pfd = {
.fd = fileno(nc->ttyinfp),
.events = POLLIN | POLLRDHUP,
.revents = 0,
};
int pret, r;
sigset_t smask;
sigfillset(&smask);
sigdelset(&smask, SIGWINCH);
sigdelset(&smask, SIGINT);
sigdelset(&smask, SIGQUIT);
sigdelset(&smask, SIGSEGV);
while((pret = ppoll(&pfd, 1, NULL, &smask)) >= 0){
if(pret == 0){
continue;
}
r = notcurses_getc(nc, c, special);
if(r < 0){
break; // want EINTR handling below
}
// infp has always been set non-blocking
int notcurses_getc(notcurses* nc, cell* c, ncspecial_key* special,
const struct timespec *ts, sigset_t* sigmask){
errno = 0;
int r = handle_input(nc, c, special);
if(r > 0){
return r;
}
if(errno == EINTR){
if(resize_seen){
resize_seen = 0;
c->gcluster = 0;
*special = NCKEY_RESIZE;
return 1;
}
if(errno == EAGAIN || errno == EWOULDBLOCK){
block_on_input(nc->ttyinfp, ts, sigmask);
r = handle_input(nc, c, special);
}
return -1;
return r;
}

@ -112,7 +112,7 @@ typedef struct notcurses {
ncplane* top; // the contents of our topmost plane (initially entire screen)
ncplane* stdscr;// aliases some plane from the z-buffer, covers screen
FILE* renderfp; // debugging FILE* to which renderings are written
char inputbuf[BUFSIZ];
unsigned char inputbuf[BUFSIZ];
// we keep a wee ringbuffer of input queued up for delivery. if
// inputbuf_occupied == sizeof(inputbuf), there is no room. otherwise, data
// can be read to inputbuf_write_at until we fill up. the first datum

Loading…
Cancel
Save