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 concept of subwindows which share memory with their parents.
* There is no tracing functionality ala `trace(3NCURSES)`. Superior external * There is no tracing functionality ala `trace(3NCURSES)`. Superior external
tracing solutions exist, such as `bpftrace`. 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 ## Environment notes

@ -6,6 +6,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <signal.h>
#include <stdbool.h> #include <stdbool.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -157,10 +158,27 @@ typedef enum {
// FIXME... // FIXME...
} ncspecial_key; } ncspecial_key;
API int notcurses_getc(const struct notcurses* n, cell* c, // See ppoll(2) for more detail. Provide a NULL 'ts' to block at lenghth, a 'ts'
ncspecial_key* special); // of 0 for non-blocking operation, and otherwise a timespec to bound blocking.
API int notcurses_getc_blocking(const struct notcurses* n, cell* c, // Signals in sigmask (less several we handle internally) will be atomically
ncspecial_key* special); // 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 // Refresh our idea of the terminal's dimensions, reshaping the standard plane
// if necessary. Without a call to this function following a terminal resize // 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)); fprintf(stderr, "Error polling on stdin/eventfd (%s)\n", strerror(errno));
}else{ }else{
if(fds[0].revents & POLLIN){ if(fds[0].revents & POLLIN){
key = notcurses_getc(nc, c, special); key = notcurses_getc_blocking(nc, c, special);
if(key < 0){ if(key < 0){
return -1; 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){ int main(void){
if(setlocale(LC_ALL, "") == nullptr){ if(setlocale(LC_ALL, "") == nullptr){
return EXIT_FAILURE; return EXIT_FAILURE;
@ -57,7 +65,8 @@ int main(void){
special, special, nckeystr(special)) < 0){ special, special, nckeystr(special)) < 0){
break; 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; break;
} }
}else{ }else{

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

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

Loading…
Cancel
Save