ncdirect_cursor_yx, ncdirect_cursor_pop, ncdirect_cursor_push #401 (#492)

* ncdirect_cursor_{push, pop, yx} declarations #401
* direct PoC: invoke ncdirect_cursor_yx() #401
* direct PoC: move to top of screen #401
* OTHERS.md: mention blessings
* ncdirect_cursor_yx working #401
* CHANGELOG: mention ncdirect_cursor_*() #401
pull/508/head
Nick Black 4 years ago committed by GitHub
parent 1a670ba443
commit 1c7796a2b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,13 @@
This document attempts to list user-visible changes and any major internal
rearrangements of Notcurses.
* 1.3.2 (not yet released)
* `ncdirect_cursor_push()`, `notcurses_cursor_pop()`, and
`ncdirect_cursor_yx()` have been added. These are not supported on all
terminals. `ncdirect_cursor_yx()` ought be considered experimental; it
must read a response from the terminal, and this can interact poorly with
other uses of standard input.
* 1.3.1 (2020-04-18)
* `ncplane_at_yx()` and `ncplane_at_cursor()` have been changed to return a
heap-allocated EGC, and write the attributes and channels to value-result

@ -0,0 +1,23 @@
# Other TUI libraries of note
* [NCURSES](https://invisible-island.net/ncurses/) (C)
* [tui-rs](https://github.com/fdehau/tui-rs) (Rust)
* [blessed-contrib](https://github.com/yaronn/blessed-contrib) (Javascript)
* [FINAL CUT](https://github.com/gansm/finalcut) (C++)
* [crossterm](https://github.com/crossterm-rs/crossterm) (Rust)
* [tty-cursor](https://github.com/piotrmurach/tty-cursor) (Ruby)
* [caca](http://caca.zoy.org/wiki/libcaca) (C)
* [Newt](https://pagure.io/newt) (C)
* [vty](http://hackage.haskell.org/package/vty) (Haskell)
* [hscharm](https://hackage.haskell.org/package/hscharm) (Haskell)
* [S-Lang](http://www.jedsoft.org/slang/) (S-Lang)
* [termbox-go](https://github.com/nsf/termbox-go) (Go)
* [tcell](https://github.com/gdamore/tcell) (Go)
* [termui](https://github.com/gizak/termui) (Go)
* [gocui](https://github.com/jroimartin/gocui) (Go)
* [tview](https://github.com/rivo/tview) (Go)
* [blessings](https://github.com/erikrose/blessings) [Python]
## Declarative
* [brick](https://github.com/jtdaugherty/brick) (Haskell, atop vty)

@ -2647,12 +2647,6 @@ up someday **FIXME**.
* Portable: [terminfo(5)](http://man7.org/linux/man-pages/man5/terminfo.5.html)
* Portable: [user_caps(5)](http://man7.org/linux/man-pages/man5/user_caps.5.html)
### Other TUI libraries of note
* [tui-rs](https://github.com/fdehau/tui-rs) (Rust)
* [blessed-contrib](https://github.com/yaronn/blessed-contrib) (Javascript)
* [FINAL CUT](https://github.com/gansm/finalcut) (C++)
### History
* 2020-04-12: notcurses [1.3.0 "hypnotize"](https://github.com/dankamongmen/notcurses/releases/tag/v1.3.0).

@ -83,6 +83,16 @@ API int ncdirect_cursor_left(struct ncdirect* nc, int num);
API int ncdirect_cursor_right(struct ncdirect* nc, int num);
API int ncdirect_cursor_down(struct ncdirect* nc, int num);
// Get the cursor position, when supported. This requires writing to the
// terminal, and then reading from it. If the terminal doesn't reply, or
// doesn't reply in a way we understand, the results might be deleterious.
API int ncdirect_cursor_yx(struct ncdirect* n, int* y, int* x);
// Push or pop the cursor location to the terminal's stack. The depth of this
// stack, and indeed its existence, is terminal-dependent.
API int ncdirect_cursor_push(struct ncdirect* n);
API int ncdirect_cursor_pop(struct ncdirect* n);
// Clear the screen.
API int ncdirect_clear(struct ncdirect* nc);

@ -1,3 +1,7 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include "internal.h"
int ncdirect_cursor_up(ncdirect* nc, int num){
@ -100,6 +104,112 @@ int ncdirect_cursor_move_yx(ncdirect* n, int y, int x){
return -1;
}
static int
cursor_yx_get(FILE* outfp, FILE* infp, int* y, int* x){
if(fprintf(outfp, "\033[6n") != 4){
return -1;
}
int in;
bool done = false;
enum { // what we expect now
CURSOR_ESC, // 27 (0x1b)
CURSOR_LSQUARE,
CURSOR_ROW, // delimited by a semicolon
CURSOR_COLUMN,
CURSOR_R,
} state = CURSOR_ESC;
int row = 0, column = 0;
while((in = getc(infp)) != EOF){
bool valid = false;
switch(state){
case CURSOR_ESC: valid = (in == '\x1b'); ++state; break;
case CURSOR_LSQUARE: valid = (in == '['); ++state; break;
case CURSOR_ROW:
if(isdigit(in)){
row *= 10;
row += in - '0';
valid = true;
}else if(in == ';'){
++state;
valid = true;
}
break;
case CURSOR_COLUMN:
if(isdigit(in)){
column *= 10;
column += in - '0';
valid = true;
}else if(in == 'R'){
++state;
valid = true;
}
break;
case CURSOR_R: default: // logical error, whoops
break;
}
if(!valid){
fprintf(stderr, "Unexpected result from terminal: %d\n", in);
break;
}
if(state == CURSOR_R){
done = true;
break;
}
}
if(!done){
return -1;
}
if(y){
*y = row;
}
if(x){
*x = column;
}
return 0;
}
// no terminfo capability for this. dangerous!
int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){
int infd = fileno(stdin); // FIXME n->ttyfp?
if(infd < 0){
fprintf(stderr, "Couldn't get file descriptor from stdin\n");
return -1;
}
// do *not* close infd!
struct termios termio, oldtermios;
if(tcgetattr(infd, &termio)){
fprintf(stderr, "Couldn't get terminal info from %d (%s)\n", infd, strerror(errno));
return -1;
}
memcpy(&oldtermios, &termio, sizeof(termio));
termio.c_lflag &= ~(ICANON | ECHO);
if(tcsetattr(infd, TCSAFLUSH, &termio)){
fprintf(stderr, "Couldn't put terminal into cbreak mode via %d (%s)\n",
infd, strerror(errno));
return -1;
}
int ret = cursor_yx_get(n->ttyfp, stdin, y, x);
if(tcsetattr(infd, TCSANOW, &oldtermios)){
fprintf(stderr, "Couldn't restore terminal mode on %d (%s)\n",
infd, strerror(errno)); // don't return error for this
}
return ret;
}
int ncdirect_cursor_push(ncdirect* n){
if(n->sc == NULL){
return -1;
}
return term_emit("sc", n->sc, n->ttyfp, false);
}
int ncdirect_cursor_pop(ncdirect* n){
if(n->rc == NULL){
return -1;
}
return term_emit("rc", n->rc, n->ttyfp, false);
}
int ncdirect_stop(ncdirect* nc){
int ret = 0;
if(nc){
@ -116,4 +226,3 @@ int ncdirect_stop(ncdirect* nc){
}
return ret;
}

@ -273,9 +273,11 @@ typedef struct ncdirect {
char* initc; // set a palette entry's RGB value
char* oc; // restore original colors
char* clear; // clear the screen
FILE* ttyfp; // FILE* for controlling tty, from opts->ttyfp
char* sc; // push the cursor location onto the stack
char* rc; // pop the cursor location off the stack
bool RGBflag; // terminfo-reported "RGB" flag for 24bpc truecolor
bool CCCflag; // terminfo-reported "CCC" flag for palette set capability
FILE* ttyfp; // FILE* for controlling tty, from opts->ttyfp
palette256 palette; // 256-indexed palette can be used instead of/with RGB
uint16_t fgrgb, bgrgb; // last RGB values of foreground/background
bool fgdefault, bgdefault; // are FG/BG currently using default colors?

@ -24,30 +24,6 @@ int main(void){
}
}
fflush(stdout);
notcurses_options opts;
memset(&opts, 0, sizeof(opts));
opts.inhibit_alternate_screen = true;
struct notcurses* nc = notcurses_init(&opts, stdout);
if(!nc){
fprintf(stderr, "Couldn't initialize notcurses\n");
return EXIT_FAILURE;
}
ncplane_set_fg(notcurses_stdplane(nc), 0x00ff00);
if(ncplane_putstr_aligned(notcurses_stdplane(nc), geom.ws_row - 2, NCALIGN_CENTER, " erperperp ") <= 0){
notcurses_stop(nc);
fprintf(stderr, "Error printing\n");
return EXIT_FAILURE;
}
if(notcurses_render(nc)){
notcurses_stop(nc);
fprintf(stderr, "Error rendering\n");
return EXIT_FAILURE;
}
sleep(2);
if(notcurses_stop(nc)){
fprintf(stderr, "Error stopping notcurses\n");
return EXIT_FAILURE;
}
struct ncdirect* n; // see bug #391
if((n = ncdirect_init(NULL, stdout)) == NULL){
return EXIT_FAILURE;
@ -65,5 +41,30 @@ int main(void){
ret |= ncdirect_cursor_right(n, geom.ws_col / 2);
ret |= ncdirect_cursor_up(n, geom.ws_row / 2);
printf(" erperperp! \n");
int y = -420, x = -420;
// FIXME try a push/pop
if(ncdirect_cursor_yx(n, &y, &x) == 0){
printf("\n\tRead cursor position: y: %d x: %d\n", y, x);
y += 2; // we just went down two lines
while(y > 3){
const int up = y >= 3 ? 3 : y;
ret |= ncdirect_cursor_up(n, up);
fflush(stdout);
y -= up;
int newy;
if(ncdirect_cursor_yx(n, &newy, NULL)){
ret = -1;
return EXIT_FAILURE;
}
if(newy != y){
fprintf(stderr, "Expected %d, got %d\n", y, newy);
return EXIT_FAILURE;
}
printf("\n\tRead cursor position: y: %d x: %d\n", newy, x);
y += 2;
}
}else{
ret = -1;
}
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
}

Loading…
Cancel
Save