input thread #2136

pull/2166/head
nick black 3 years ago
parent c286c01d16
commit 3cf5a67b84
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

@ -5,6 +5,14 @@ rearrangements of Notcurses.
* `notcurses_check_pixel_support()` still returns 0 if there is no support
for bitmap graphics, but now returns an `ncpixelimple_e` to differentiate
the pixel backend otherwise. This result is strictly informative.
* Added `NCOPTION_DRAIN_INPUT`. Notcurses now launches a thread to process
input, so that it can respond to terminal messages with minimal latency.
Input read from `stdin` intended for the client is buffered until
retrieved. If your client never intends to read this input, provide this
flag to eliminate unnecessary processing, and ensure Notcurses can always
retrieve terminal messages (if buffers are full, Notcurses cannot
continue reading). Likewise added `NCDIRECT_OPTION_DRAIN_INPUT`.
* Removed a bunch of deprecated `static inline` functions from the headers.
* 2.4.0 (2021-09-06)
* Mouse events in the Linux console are now reported from GPM when built

@ -248,6 +248,25 @@ If things break or seem otherwise lackluster, **please** consult the
more importantly, it will link against minimal Notcurses installations.
</details>
<details>
<summary>Does it work with hardware terminals?</summary>
With the correct `TERM` value, many hardware terminals are supported. The VT100
is sadly unsupported due to its extensive need for delays. In general, if the
terminfo database entry indicates mandatory delays, Notcurses will not currently
support that terminal properly. It's known that Notcurses can drive the VT320
and VT340, including Sixel graphics on the latter.
</details>
<details>
<summary>What happens if I try blitting bitmap graphics on a terminal which
doesn't support them?</summary>
Notcurses will not make use of bitmap protocols unless the terminal positively
indicates support for them, even if `NCBLIT_PIXEL` has been requested. Likewise,
sextants (`NCBLIT_3x2`) won't be used without Unicode 13 support, etc.
`ncvisual_render()` will use the best blitter available, unless
`NCVISUAL_OPTION_NODEGRADE` is provided (in which case it will fail).
</details>
<details>
<summary>Notcurses looks like absolute crap in <code>screen</code>.</summary>
<code>screen</code> doesn't support RGB colors (at least as of 4.08.00);

@ -95,7 +95,7 @@ typedef enum {
// to do this, pass NCOPTION_NO_CLEAR_BITMAPS. Note that they might still
// get cleared even if this is set, and they might not get cleared even if
// this is not set. It's a tough world out there.
#define NCOPTION_NO_CLEAR_BITMAPS 0x0002ull
#define NCOPTION_NO_CLEAR_BITMAPS 0x0002
// We typically install a signal handler for SIGWINCH that generates a resize
// event in the notcurses_get() queue. Set to inhibit this handler.
@ -110,7 +110,7 @@ typedef enum {
// at context creation time. Together with NCOPTION_NO_ALTERNATE_SCREEN and a
// scrolling standard plane, this facilitates easy scrolling-style programs in
// rendered mode.
#define NCOPTION_PRESERVE_CURSOR 0x0010ull
#define NCOPTION_PRESERVE_CURSOR 0x0010
// Notcurses typically prints version info in notcurses_init() and performance
// info in notcurses_stop(). This inhibits that output.
@ -120,6 +120,17 @@ typedef enum {
// of the "alternate screen". This flag inhibits use of smcup/rmcup.
#define NCOPTION_NO_ALTERNATE_SCREEN 0x0040
// Do not modify the font. Notcurses might attempt to change the font slightly,
// to support certain glyphs (especially on the Linux console). If this is set,
// no such modifications will be made. Note that font changes will not affect
// anything but the virtual console/terminal in which Notcurses is running.
#define NCOPTION_NO_FONT_CHANGES 0x0080
// Input may be freely dropped. This ought be provided when the program does not
// intend to handle input. Otherwise, input can accumulate in internal buffers,
// eventually preventing Notcurses from processing terminal messages.
#define NCOPTION_DRAIN_INPUT 0x0100
// Configuration for notcurses_init().
typedef struct notcurses_options {
// The name of the terminfo database entry describing this terminal. If NULL,
@ -378,11 +389,23 @@ struct ncdirect* ncdirect_core_init(const char* termtype, FILE* fp, uint64_t fla
// echo and line buffering are turned off.
#define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull
// Input may be freely dropped. This ought be provided when the program does not
// intend to handle input. Otherwise, input can accumulate in internal buffers,
// eventually preventing Notcurses from processing terminal messages.
#define NCDIRECT_OPTION_DRAIN_INPUT 0x0004ull
// We typically install a signal handler for SIG{INT, SEGV, ABRT, QUIT} that
// restores the screen, and then calls the old signal handler. Set to inhibit
// registration of these signal handlers. Chosen to match fullscreen mode.
#define NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS 0x0008ull
// Enable logging (to stderr) at the NCLOGLEVEL_WARNING level.
#define NCDIRECT_OPTION_VERBOSE 0x0010ull
// Enable logging (to stderr) at the NCLOGLEVEL_TRACE level. This will enable
// all diagnostics, a superset of NCDIRECT_OPTION_VERBOSE (which this implies).
#define NCDIRECT_OPTION_VERY_VERBOSE 0x0020ull
// Release 'nc' and any associated resources. 0 on success, non-0 on failure.
int ncdirect_stop(struct ncdirect* nc);
```
@ -671,7 +694,12 @@ typedef struct ncinput {
// event is processed, the return value is the 'id' field from that event.
// 'ni' may be NULL.
uint32_t notcurses_get(struct notcurses* n, const struct timespec* ts,
ncinput* ni)
ncinput* ni);
// Acquire up to 'vcount' ncinputs at the vector 'ni'. The number read will be
// returned, or -1 on error without any reads, 0 on timeout.
int notcurses_getvec(struct notcurses* n, const struct timespec* ts,
ncinput* ni, int vcount);
// 'ni' may be NULL if the caller is uninterested in event details. If no event
// is ready, returns 0.

@ -169,6 +169,11 @@ The following flags are defined:
will place the terminal into cbreak mode (i.e. disabling echo and line
buffering; see **tcgetattr(3)**).
* **NCDIRECT_OPTION_DRAIN_INPUT**: Standard input may be freely discarded. If
you do not intend to process input, pass this flag. Otherwise, input can
buffer up, eventually preventing Notcurses from processing terminal
messages. It will furthermore avoid wasting time processing useless input.
* **NCDIRECT_OPTION_NO_QUIT_SIGHANDLERS**: A signal handler will usually be installed
for **SIGABRT**, **SIGFPE**, **SIGILL**, **SIGINT**, **SIGQUIT**,
**SIGSEGV**, and **SIGTERM**, cleaning up the terminal on such exceptions.

@ -19,6 +19,7 @@ notcurses_init - initialize a notcurses instance
#define NCOPTION_SUPPRESS_BANNERS 0x0020ull
#define NCOPTION_NO_ALTERNATE_SCREEN 0x0040ull
#define NCOPTION_NO_FONT_CHANGES 0x0080ull
#define NCOPTION_DRAIN_INPUT 0x0100ull
typedef enum {
NCLOGLEVEL_SILENT, // print nothing once fullscreen service begins
@ -139,6 +140,11 @@ zero. The following flags are defined:
* **NCOPTION_NO_FONT_CHANGES**: Do not touch the font. Notcurses might
otherwise attempt to extend the font, especially in the Linux console.
* **NCOPTION_DRAIN_INPUT**: Standard input may be freely discarded. If you do not
intend to process input, pass this flag. Otherwise, input can buffer up, and
eventually prevent Notcurses from processing messages from the terminal. It
will furthermore avoid wasting time processing useless input.
## Fatal signals
It is important to reset the terminal before exiting, whether terminating due

@ -31,6 +31,8 @@ typedef struct ncinput {
**uint32_t notcurses_get(struct notcurses* ***n***, const struct timespec* ***ts***, ncinput* ***ni***);**
**int notcurses_getvec(struct notcurses* ***n***, const struct timespec* ***ts***, ncinput* ***ni***, int vcount);**
**uint32_t notcurses_getc_nblock(struct notcurses* ***n***, ncinput* ***ni***);**
**uint32_t notcurses_getc_blocking(struct notcurses* ***n***, ncinput* ***ni***);**
@ -128,6 +130,10 @@ temporary one (especially e.g. **EINTR**), **notcurses_get** probably cannot
be usefully called forthwith. On a timeout, 0 is returned. Otherwise, the
UCS-32 value of a Unicode codepoint, or a synthesized event, is returned.
If an error is encountered before **notcurses_getvec** has read any input,
it will return -1. If it times out before reading any input, it will return
0. Otherwise, it returns the number of **ncinput** objects written back.
**notcurses_mouse_enable** returns 0 on success, and non-zero on failure, as
does **notcurses_mouse_disable**.

@ -24,6 +24,11 @@ extern "C" {
// echo and input's line buffering are turned off.
#define NCDIRECT_OPTION_INHIBIT_CBREAK 0x0002ull
// Input may be freely dropped. This ought be provided when the program does not
// intend to handle input. Otherwise, input can accumulate in internal buffers,
// eventually preventing Notcurses from processing terminal messages.
#define NCDIRECT_OPTION_DRAIN_INPUT 0x0004ull
// We typically install a signal handler for SIG{INT, SEGV, ABRT, QUIT} that
// restores the screen, and then calls the old signal handler. Set to inhibit
// registration of these signal handlers. Chosen to match fullscreen mode.
@ -489,11 +494,6 @@ ncdirect_canbraille(const struct ncdirect* nc){
API bool ncdirect_canget_cursor(const struct ncdirect* nc)
__attribute__ ((nonnull (1)));
// Deprecated, to be removed for ABI3. Use ncdirect_get() in new code.
API uint32_t ncdirect_getc(struct ncdirect* n, const struct timespec* ts,
const void* unused, ncinput* ni)
__attribute__ ((deprecated)) __attribute__ ((nonnull (1)));
#undef ALLOC
#undef API

@ -900,6 +900,11 @@ typedef enum {
// anything but the virtual console/terminal in which Notcurses is running.
#define NCOPTION_NO_FONT_CHANGES 0x0080ull
// Input may be freely dropped. This ought be provided when the program does not
// intend to handle input. Otherwise, input can accumulate in internal buffers,
// eventually preventing Notcurses from processing terminal messages.
#define NCOPTION_DRAIN_INPUT 0x0100ull
// Configuration for notcurses_init().
typedef struct notcurses_options {
// The name of the terminfo database entry describing this terminal. If NULL,
@ -1066,6 +1071,12 @@ API uint32_t notcurses_get(struct notcurses* n, const struct timespec* ts,
ncinput* ni)
__attribute__ ((nonnull (1)));
// Acquire up to 'vcount' ncinputs at the vector 'ni'. The number read will be
// returned, or -1 on error without any reads, 0 on timeout.
API int notcurses_getvec(struct notcurses* n, const struct timespec* ts,
ncinput* ni, int vcount)
__attribute__ ((nonnull (1, 3)));
// Get a file descriptor suitable for input event poll()ing. When this
// descriptor becomes available, you can call notcurses_getc_nblock(),
// and input ought be ready. This file descriptor is *not* necessarily
@ -4244,233 +4255,6 @@ palette256_get_rgb8(const ncpalette* p, int idx, unsigned* RESTRICT r, unsigned*
API void palette256_free(ncpalette* p) __attribute__ ((deprecated));
__attribute__ ((deprecated)) static inline unsigned
channel_r(uint32_t channel){
return ncchannel_r(channel);
}
// Extract the 8-bit green component from a 32-bit channel.
__attribute__ ((deprecated)) static inline unsigned
channel_g(uint32_t channel){
return ncchannel_g(channel);
}
// Extract the 8-bit blue component from a 32-bit channel.
__attribute__ ((deprecated)) static inline unsigned
channel_b(uint32_t channel){
return ncchannel_b(channel);
}
// Extract the three 8-bit R/G/B components from a 32-bit channel.
__attribute__ ((deprecated)) static inline unsigned
channel_rgb8(uint32_t channel, unsigned* RESTRICT r, unsigned* RESTRICT g,
unsigned* RESTRICT b){
return ncchannel_rgb8(channel, r, g, b);
}
// Set the three 8-bit components of a 32-bit channel, and mark it as not using
// the default color. Retain the other bits unchanged.
__attribute__ ((deprecated)) static inline int
channel_set_rgb8(uint32_t* channel, int r, int g, int b){
return ncchannel_set_rgb8(channel, r, g, b);
}
// Set the three 8-bit components of a 32-bit channel, and mark it as not using
// the default color. Retain the other bits unchanged. r, g, and b will be
// clipped to the range [0..255].
__attribute__ ((deprecated)) static inline void
channel_set_rgb8_clipped(unsigned* channel, int r, int g, int b){
return ncchannel_set_rgb8_clipped(channel, r, g, b);
}
// Same, but provide an assembled, packed 24 bits of rgb.
__attribute__ ((deprecated)) static inline int
channel_set(unsigned* channel, unsigned rgb){
return ncchannel_set(channel, rgb);
}
// Extract the 2-bit alpha component from a 32-bit channel.
__attribute__ ((deprecated)) static inline unsigned
channel_alpha(unsigned channel){
return ncchannel_alpha(channel);
}
__attribute__ ((deprecated)) static inline unsigned
channel_palindex(uint32_t channel){
return ncchannel_palindex(channel);
}
// Set the 2-bit alpha component of the 32-bit channel.
__attribute__ ((deprecated)) static inline int
channel_set_alpha(unsigned* channel, unsigned alpha){
return ncchannel_set_alpha(channel, alpha);
}
__attribute__ ((deprecated)) static inline int
channel_set_palindex(uint32_t* channel, int idx){
return ncchannel_set_palindex(channel, idx);
}
__attribute__ ((deprecated)) static inline bool
channel_default_p(unsigned channel){
return ncchannel_default_p(channel);
}
__attribute__ ((deprecated)) static inline bool
channel_palindex_p(unsigned channel){
return ncchannel_palindex_p(channel);
}
__attribute__ ((deprecated)) static inline unsigned
channel_set_default(unsigned* channel){
return ncchannel_set_default(channel);
}
__attribute__ ((deprecated)) static inline uint32_t
channels_bchannel(uint64_t channels){
return ncchannels_bchannel(channels);
}
__attribute__ ((deprecated)) static inline uint32_t
channels_fchannel(uint64_t channels){
return ncchannels_fchannel(channels);
}
__attribute__ ((deprecated)) static inline uint64_t
channels_set_bchannel(uint64_t* channels, uint32_t channel){
return ncchannels_set_bchannel(channels, channel);
}
__attribute__ ((deprecated)) static inline uint64_t
channels_set_fchannel(uint64_t* channels, uint32_t channel){
return ncchannels_set_fchannel(channels, channel);
}
__attribute__ ((deprecated)) static inline uint64_t
channels_combine(uint32_t fchan, uint32_t bchan){
return ncchannels_combine(fchan, bchan);
}
__attribute__ ((deprecated)) static inline unsigned
channels_fg_palindex(uint64_t channels){
return ncchannels_fg_palindex(channels);
}
__attribute__ ((deprecated)) static inline unsigned
channels_bg_palindex(uint64_t channels){
return ncchannels_bg_palindex(channels);
}
__attribute__ ((deprecated)) static inline unsigned
channels_fg_rgb(uint64_t channels){
return ncchannels_fg_rgb(channels);
}
__attribute__ ((deprecated)) static inline unsigned
channels_bg_rgb(uint64_t channels){
return ncchannels_bg_rgb(channels);
}
__attribute__ ((deprecated)) static inline unsigned
channels_fg_alpha(uint64_t channels){
return ncchannels_fg_alpha(channels);
}
__attribute__ ((deprecated)) static inline unsigned
channels_bg_alpha(uint64_t channels){
return ncchannels_bg_alpha(channels);
}
__attribute__ ((deprecated)) static inline unsigned
channels_fg_rgb8(uint64_t channels, unsigned* r, unsigned* g, unsigned* b){
return ncchannels_fg_rgb8(channels, r, g, b);
}
__attribute__ ((deprecated)) static inline unsigned
channels_bg_rgb8(uint64_t channels, unsigned* r, unsigned* g, unsigned* b){
return ncchannels_bg_rgb8(channels, r, g, b);
}
__attribute__ ((deprecated)) static inline int
channels_set_fg_rgb8(uint64_t* channels, int r, int g, int b){
return ncchannels_set_fg_rgb8(channels, r, g, b);
}
__attribute__ ((deprecated)) static inline void
channels_set_fg_rgb8_clipped(uint64_t* channels, int r, int g, int b){
ncchannels_set_fg_rgb8_clipped(channels, r, g, b);
}
__attribute__ ((deprecated)) static inline int
channels_set_fg_alpha(uint64_t* channels, unsigned alpha){
return ncchannels_set_fg_alpha(channels, alpha);
}
__attribute__ ((deprecated)) static inline int
channels_set_fg_palindex(uint64_t* channels, int idx){
return ncchannels_set_bg_palindex(channels, idx);
}
__attribute__ ((deprecated)) static inline int
channels_set_fg_rgb(uint64_t* channels, unsigned rgb){
return ncchannels_set_fg_rgb(channels, rgb);
}
__attribute__ ((deprecated)) static inline int
channels_set_bg_rgb8(uint64_t* channels, int r, int g, int b){
return ncchannels_set_bg_rgb8(channels, r, g, b);
}
__attribute__ ((deprecated)) static inline void
channels_set_bg_rgb8_clipped(uint64_t* channels, int r, int g, int b){
ncchannels_set_bg_rgb8_clipped(channels, r, g, b);
}
__attribute__ ((deprecated)) static inline int
channels_set_bg_alpha(uint64_t* channels, unsigned alpha){
return ncchannels_set_bg_alpha(channels, alpha);
}
__attribute__ ((deprecated)) static inline int
channels_set_bg_palindex(uint64_t* channels, int idx){
return ncchannels_set_bg_palindex(channels, idx);
}
__attribute__ ((deprecated)) static inline int
channels_set_bg_rgb(uint64_t* channels, unsigned rgb){
return ncchannels_set_bg_rgb(channels, rgb);
}
__attribute__ ((deprecated)) static inline bool
channels_fg_default_p(uint64_t channels){
return ncchannels_fg_default_p(channels);
}
__attribute__ ((deprecated)) static inline bool
channels_fg_palindex_p(uint64_t channels){
return ncchannels_fg_palindex_p(channels);
}
__attribute__ ((deprecated)) static inline bool
channels_bg_default_p(uint64_t channels){
return ncchannels_bg_default_p(channels);
}
__attribute__ ((deprecated)) static inline bool
channels_bg_palindex_p(uint64_t channels){
return ncchannels_bg_palindex_p(channels);
}
__attribute__ ((deprecated)) static inline uint64_t
channels_set_fg_default(uint64_t* channels){
return ncchannels_set_fg_default(channels);
}
__attribute__ ((deprecated)) static inline uint64_t
channels_set_bg_default(uint64_t* channels){
return ncchannels_set_bg_default(channels);
}
// Inflate each pixel in the image to 'scale'x'scale' pixels. It is an error
// if 'scale' is less than 1. The original color is retained.
// Deprecated; use ncvisual_resize_noninterpolative(), which this now wraps.
@ -4488,12 +4272,6 @@ typedef nccell cell; // FIXME backwards-compat, remove in ABI3
API void notcurses_debug_caps(const struct notcurses* nc, FILE* debugfp)
__attribute__ ((deprecated)) __attribute__ ((nonnull (1, 2)));
// Backwards-compatibility wrapper; this will be removed for ABI3.
// Use notcurses_get() in new code.
API uint32_t notcurses_getc(struct notcurses* n, const struct timespec* ts,
const void* unused, ncinput* ni)
__attribute__ ((deprecated)) __attribute__ ((nonnull (1)));
__attribute__ ((deprecated)) API int nccell_width(const struct ncplane* n, const nccell* c);
API ALLOC char* ncvisual_subtitle(const struct ncvisual* ncv)

@ -864,9 +864,7 @@ ncdirect_stop_minimal(void* vnc){
ret |= fbuf_finalize(&f, stdout);
}
if(nc->tcache.ttyfd >= 0){
if(nc->tcache.kittykbd){
ret |= tty_emit("\x1b[<u", nc->tcache.ttyfd);
}
ret |= tty_emit("\x1b[<u", nc->tcache.ttyfd);
const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM);
if(cnorm && tty_emit(cnorm, nc->tcache.ttyfd)){
ret = -1;
@ -883,7 +881,7 @@ ncdirect* ncdirect_core_init(const char* termtype, FILE* outfp, uint64_t flags){
if(outfp == NULL){
outfp = stdout;
}
if(flags > (NCDIRECT_OPTION_VERY_VERBOSE << 1)){ // allow them through with warning
if(flags > (NCDIRECT_OPTION_DRAIN_INPUT << 1)){ // allow them through with warning
logwarn("Passed unsupported flags 0x%016jx\n", (uintmax_t)flags);
}
ncdirect* ret = malloc(sizeof(ncdirect));

@ -32,7 +32,7 @@ gpmwatcher(void* vti){
logwarn("input overflowed %hd %hd\n", gev.x, gev.y);
continue;
}
ncinput_shovel(&ti->input, cmdbuf, strlen(cmdbuf));
ncinput_shovel(ti->ictx, cmdbuf, strlen(cmdbuf));
}
return NULL;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,82 @@
#ifndef NOTCURSES_IN
#define NOTCURSES_IN
#ifdef __cplusplus
extern "C" {
#endif
// internal header, not installed
#include <stdio.h>
struct tinfo;
struct inputctx;
int init_inputlayer(struct tinfo* ti, FILE* infp)
__attribute__ ((nonnull (1, 2)));
int stop_inputlayer(struct tinfo* ti);
int inputready_fd(const struct inputctx* ictx)
__attribute__ ((nonnull (1)));
// allow another source provide raw input for distribution to client code.
// drops input if there is no room in appropriate output queue.
int ncinput_shovel(struct inputctx* ictx, const void* buf, int len)
__attribute__ ((nonnull (1, 2)));
typedef enum {
TERMINAL_UNKNOWN, // no useful information from queries; use termname
// the very limited linux VGA/serial console, or possibly the (deprecated,
// pixel-drawable, RGBA8888) linux framebuffer console. *not* fbterm.
TERMINAL_LINUX, // ioctl()s
// the linux KMS/DRM console, *not* kmscon, but DRM direct dumb buffers
TERMINAL_LINUXDRM, // ioctl()s
TERMINAL_XTERM, // XTVERSION == 'XTerm(ver)'
TERMINAL_VTE, // TDA: "~VTE"
TERMINAL_KITTY, // XTGETTCAP['TN'] == 'xterm-kitty'
TERMINAL_FOOT, // TDA: "\EP!|464f4f54\E\\"
TERMINAL_MLTERM, // XTGETTCAP['TN'] == 'mlterm'
TERMINAL_TMUX, // XTVERSION == "tmux ver"
TERMINAL_WEZTERM, // XTVERSION == 'WezTerm *'
TERMINAL_ALACRITTY, // can't be detected; match TERM+DA2
TERMINAL_CONTOUR, // XTVERSION == 'contour ver'
TERMINAL_ITERM, // XTVERSION == 'iTerm2 [ver]'
TERMINAL_TERMINOLOGY, // TDA: "~~TY"
TERMINAL_APPLE, // Terminal.App, determined by TERM_PROGRAM + macOS
TERMINAL_MSTERMINAL, // Microsoft Windows Terminal
TERMINAL_MINTTY, // XTVERSION == 'mintty ver' MinTTY (Cygwin, MSYS2)
} queried_terminals_e;
// after spawning the input layer, send initial queries to the terminal. its
// responses will be built up herein. it's dangerous to go alone! take this!
struct initial_responses {
int cursory; // cursor location
int cursorx; // cursor location
unsigned appsync_supported; // is application-synchronized mode supported?
queried_terminals_e qterm; // determined terminal
unsigned kitty_graphics; // kitty graphics supported
uint32_t bg; // default background
int pixx; // screen geometry in pixels
int pixy; // screen geometry in pixels
int dimx; // screen geometry in cells
int dimy; // screen geometry in cells
int color_registers; // sixel color registers
int sixely; // maximum sixel height
int sixelx; // maximum sixel width
char* version; // version string, heap-allocated
};
// Blocking call. Waits until the input thread has processed all responses to
// our initial queries, and returns them.
struct initial_responses* inputlayer_get_responses(struct inputctx* ictx)
__attribute__ ((nonnull (1)));
int get_cursor_location(struct inputctx* ictx, int* y, int* x)
__attribute__ ((nonnull (1, 2, 3)));
#ifdef __cplusplus
}
#endif
#endif

@ -1,69 +0,0 @@
#ifndef NOTCURSES_INPUT
#define NOTCURSES_INPUT
#ifdef __cplusplus
extern "C" {
#endif
// internal header, not installed
#include <stdio.h>
struct tinfo;
struct termios;
struct ncinputlayer;
struct ncsharedstats;
typedef enum {
TERMINAL_UNKNOWN, // no useful information from queries; use termname
// the very limited linux VGA/serial console, or possibly the (deprecated,
// pixel-drawable, RGBA8888) linux framebuffer console. *not* fbterm.
TERMINAL_LINUX, // ioctl()s
// the linux KMS/DRM console, *not* kmscon, but DRM direct dumb buffers
TERMINAL_LINUXDRM, // ioctl()s
TERMINAL_XTERM, // XTVERSION == 'XTerm(ver)'
TERMINAL_VTE, // TDA: "~VTE"
TERMINAL_KITTY, // XTGETTCAP['TN'] == 'xterm-kitty'
TERMINAL_FOOT, // TDA: "\EP!|464f4f54\E\\"
TERMINAL_MLTERM, // XTGETTCAP['TN'] == 'mlterm'
TERMINAL_TMUX, // XTVERSION == "tmux ver"
TERMINAL_WEZTERM, // XTVERSION == 'WezTerm *'
TERMINAL_ALACRITTY, // can't be detected; match TERM+DA2
TERMINAL_CONTOUR, // XTVERSION == 'contour ver'
TERMINAL_ITERM, // XTVERSION == 'iTerm2 [ver]'
TERMINAL_TERMINOLOGY, // TDA: "~~TY"
TERMINAL_APPLE, // Terminal.App, determined by TERM_PROGRAM + macOS
TERMINAL_MSTERMINAL, // Microsoft Windows Terminal
TERMINAL_MINTTY, // XTVERSION == 'mintty ver' MinTTY (Cygwin, MSYS2)
} queried_terminals_e;
// sets up the input layer, building a trie of escape sequences and their
// nckey equivalents. if we are connected to a tty, this also completes the
// terminal detection sequence (we ought have already written our initial
// queries, ideally as early as possible). if we are able to determine the
// terminal conclusively, it will be written to |detected|. if the terminal
// advertised support for application-sychronized updates, |appsync| will be
// non-zero.
int ncinputlayer_init(struct tinfo* tcache, FILE* infp,
queried_terminals_e* detected, unsigned* appsync,
int* cursor_y, int* cursor_x,
struct ncsharedstats* stats,
unsigned* kittygraphs);
void ncinputlayer_stop(struct ncinputlayer* nilayer);
// FIXME absorb into ncinputlayer_init()
int cbreak_mode(struct tinfo* ti);
// assuming the user context is not active, go through current data looking
// for a cursor location report. if we find none, block on input, and read if
// appropriate. we can be interrupted by a new user context.
void ncinput_extract_clrs(struct tinfo* ti);
int ncinput_shovel(struct ncinputlayer* ni, const char* buf, size_t len);
#ifdef __cplusplus
}
#endif
#endif

@ -9,6 +9,7 @@ extern "C" {
#include "builddef.h"
#include "compat/compat.h"
#include "notcurses/notcurses.h"
#include "notcurses/direct.h"
// KEY_EVENT is defined by both ncurses.h and wincon.h. since we don't use
// either definition, kill it before inclusion of ncurses.h.

@ -406,7 +406,7 @@ program_line_drawing_chars(int fd, struct unimapdesc* map){
return 0;
}
if(ioctl(fd, PIO_UNIMAP, map)){
logwarn("Error setting kernel unicode map (%s)\n", strerror(errno));
logwarn("error setting kernel unicode map (%s)\n", strerror(errno));
return -1;
}
loginfo("Successfully added %d kernel unicode mapping%s\n",
@ -533,12 +533,12 @@ program_block_drawing_chars(tinfo* ti, int fd, struct console_font_op* cfo,
}
}
if(candidate == 0){
logwarn("Ran out of replaceable glyphs for U+%04lx\n", (long)half[s].w);
logwarn("ran out of replaceable glyphs for U+%04lx\n", (long)half[s].w);
// FIXME maybe don't want to error out here?
return -1;
}
if(shim_quad_block(cfo, candidate, half[s].qbits)){
logwarn("Error replacing glyph for U+%04lx at %u\n", (long)half[s].w, candidate);
logwarn("error replacing glyph for U+%04lx at %u\n", (long)half[s].w, candidate);
return -1;
}
if(add_to_map(map, half[s].w, candidate)){
@ -555,12 +555,12 @@ program_block_drawing_chars(tinfo* ti, int fd, struct console_font_op* cfo,
}
}
if(candidate == 0){
logwarn("Ran out of replaceable glyphs for U+%04lx\n", (long)quads[s].w);
logwarn("ran out of replaceable glyphs for U+%04lx\n", (long)quads[s].w);
// FIXME maybe don't want to error out here?
return -1;
}
if(shim_quad_block(cfo, candidate, quads[s].qbits)){
logwarn("Error replacing glyph for U+%04lx at %u\n", (long)quads[s].w, candidate);
logwarn("error replacing glyph for U+%04lx at %u\n", (long)quads[s].w, candidate);
return -1;
}
if(add_to_map(map, quads[s].w, candidate)){
@ -577,11 +577,11 @@ program_block_drawing_chars(tinfo* ti, int fd, struct console_font_op* cfo,
}
}
if(candidate == 0){
logwarn("Ran out of replaceable glyphs for U+%04lx\n", (long)eighths[s].w);
logwarn("ran out of replaceable glyphs for U+%04lx\n", (long)eighths[s].w);
return -1;
}
if(shim_lower_eighths(cfo, candidate, eighths[s].qbits)){
logwarn("Error replacing glyph for U+%04lx at %u\n", (long)eighths[s].w, candidate);
logwarn("error replacing glyph for U+%04lx at %u\n", (long)eighths[s].w, candidate);
return -1;
}
if(add_to_map(map, eighths[s].w, candidate)){
@ -600,12 +600,12 @@ program_block_drawing_chars(tinfo* ti, int fd, struct console_font_op* cfo,
}
cfo->op = KD_FONT_OP_SET;
if(ioctl(fd, KDFONTOP, cfo)){
logwarn("Error programming kernel font (%s)\n", strerror(errno));
logwarn("error programming kernel font (%s)\n", strerror(errno));
kill_fbcopy(&fbdup);
return -1;
}
if(ioctl(fd, PIO_UNIMAP, map)){
logwarn("Error setting kernel unicode map (%s)\n", strerror(errno));
logwarn("error setting kernel unicode map (%s)\n", strerror(errno));
kill_fbcopy(&fbdup);
return -1;
}
@ -636,7 +636,7 @@ reprogram_linux_font(tinfo* ti, int fd, struct console_font_op* cfo,
struct unimapdesc* map, unsigned no_font_changes,
bool* halfblocks, bool* quadrants){
if(ioctl(fd, KDFONTOP, cfo)){
logwarn("Error reading Linux kernelfont (%s)\n", strerror(errno));
logwarn("error reading Linux kernelfont (%s)\n", strerror(errno));
return -1;
}
loginfo("Kernel font size (glyphcount): %hu\n", cfo->charcount);
@ -646,7 +646,7 @@ reprogram_linux_font(tinfo* ti, int fd, struct console_font_op* cfo,
return -1;
}
if(ioctl(fd, GIO_UNIMAP, map)){
logwarn("Error reading Linux unimap (%s)\n", strerror(errno));
logwarn("error reading Linux unimap (%s)\n", strerror(errno));
return -1;
}
loginfo("Kernel Unimap size: %hu/%hu\n", map->entry_ct, USHRT_MAX);
@ -676,7 +676,7 @@ int reprogram_console_font(tinfo* ti, unsigned no_font_changes,
size_t totsize = 128 * cfo.charcount; // FIXME enough?
cfo.data = malloc(totsize);
if(cfo.data == NULL){
logwarn("Error acquiring %zub for font descriptors (%s)\n", totsize, strerror(errno));
logwarn("error acquiring %zub for font descriptors (%s)\n", totsize, strerror(errno));
return -1;
}
struct unimapdesc map = {};
@ -684,7 +684,7 @@ int reprogram_console_font(tinfo* ti, unsigned no_font_changes,
totsize = map.entry_ct * sizeof(struct unipair);
map.entries = malloc(totsize);
if(map.entries == NULL){
logwarn("Error acquiring %zub for Unicode font map (%s)\n", totsize, strerror(errno));
logwarn("error acquiring %zub for Unicode font map (%s)\n", totsize, strerror(errno));
free(cfo.data);
return -1;
}
@ -704,10 +704,10 @@ bool is_linux_console(int fd){
}
int mode;
if(ioctl(fd, KDGETMODE, &mode)){
logdebug("Not a Linux console, KDGETMODE failed\n");
logdebug("not a Linux console, KDGETMODE failed\n");
return false;
}
loginfo("Verified Linux console, mode %d\n", mode);
loginfo("verified Linux console, mode %d\n", mode);
return true;
}
@ -721,7 +721,7 @@ int get_linux_fb_pixelgeom(tinfo* ti, unsigned* ypix, unsigned *xpix){
}
struct fb_var_screeninfo fbi = {};
if(ioctl(ti->linux_fb_fd, FBIOGET_VSCREENINFO, &fbi)){
logwarn("No framebuffer info from %s (%s?)\n", ti->linux_fb_dev, strerror(errno));
logwarn("no framebuffer info from %s (%s?)\n", ti->linux_fb_dev, strerror(errno));
return -1;
}
loginfo("Linux %s geometry: %dx%d\n", ti->linux_fb_dev, fbi.yres, fbi.xres);

@ -1,4 +1,3 @@
#include "input.h"
#include "linux.h"
#include "version.h"
#include "egcpool.h"
@ -107,10 +106,8 @@ notcurses_stop_minimal(void* vnc){
if(nc->tcache.tpreserved){
ret |= tcsetattr(nc->tcache.ttyfd, TCSAFLUSH, nc->tcache.tpreserved);
}
if(nc->tcache.kittykbd){
if(tty_emit("\x1b[<u", nc->tcache.ttyfd)){
ret = -1;
}
if(tty_emit("\x1b[<u", nc->tcache.ttyfd)){
ret = -1;
}
if((esc = get_escape(&nc->tcache, ESCAPE_RMCUP))){
if(sprite_clear_all(&nc->tcache, f)){ // send this to f
@ -1001,7 +998,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
fprintf(stderr, "Provided an illegal negative margin, refusing to start\n");
return NULL;
}
if(opts->flags >= (NCOPTION_NO_FONT_CHANGES << 1u)){
if(opts->flags >= (NCOPTION_DRAIN_INPUT << 1u)){
fprintf(stderr, "Warning: unknown Notcurses options %016" PRIu64 "\n", opts->flags);
}
notcurses* ret = malloc(sizeof(*ret));

@ -5,7 +5,6 @@
#endif
#include "internal.h"
#include "windows.h"
#include "input.h"
#include "linux.h"
// there does not exist any true standard terminal size. with that said, we
@ -780,6 +779,9 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
goto err;
}
}
if(init_inputlayer(ti, stdin)){
goto err;
}
#ifndef __MINGW64__
// windows doesn't really have a concept of terminfo. you might ssh into other
// machines, but they'll use the terminfo installed thereon (putty, etc.).
@ -916,19 +918,51 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
goto err;
}
}
unsigned appsync_advertised = 0;
unsigned kittygraphs = 0;
if(init_inputlayer(ti, stdin)){
goto err;
}
/*
if(ncinputlayer_init(ti, stdin, &ti->qterm, &appsync_advertised,
cursor_y, cursor_x, stats, &kittygraphs)){
goto err;
unsigned kitty_graphics = 0;
if(ti->ttyfd >= 0){
struct initial_responses* iresp;
if((iresp = inputlayer_get_responses(ti->ictx)) == NULL){
goto err;
}
if(iresp->appsync_supported){
if(add_appsync_escapes_sm(ti, &tablelen, &tableused)){
free(iresp->version);
free(iresp);
goto err;
}
}
if(iresp->qterm != TERMINAL_UNKNOWN){
ti->qterm = iresp->qterm;
}
*cursor_y = iresp->cursory;
*cursor_x = iresp->cursorx;
ti->termversion = iresp->version;
if(iresp->dimy && iresp->dimx){
// FIXME probably oughtn't be setting the defaults, as this is just some
// random transient measurement?
ti->default_rows = iresp->dimy;
ti->default_cols = iresp->dimx;
}
if(iresp->pixy && iresp->pixx){
ti->pixy = iresp->pixy;
ti->pixx = iresp->pixx;
}
if(ti->default_rows && ti->default_cols){
ti->cellpixy = ti->pixy / ti->default_rows;
ti->cellpixx = ti->pixx / ti->default_cols;
}
ti->bg_collides_default = iresp->bg;
// kitty trumps sixel, when both are available
if((kitty_graphics = iresp->kitty_graphics) == 0){
ti->color_registers = iresp->color_registers;
ti->sixel_maxy = iresp->sixely;
ti->sixel_maxx = iresp->sixelx;
}
free(iresp);
}
*/
if(nocbreak){
if(ti->ttyfd >= 0){
// FIXME do this in input later, upon signaling completion?
if(tcsetattr(ti->ttyfd, TCSANOW, ti->tpreserved)){
goto err;
}
@ -939,11 +973,6 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
goto err;
}
}
if(appsync_advertised){
if(add_appsync_escapes_sm(ti, &tablelen, &tableused)){
goto err;
}
}
bool invertsixel = false;
if(apply_term_heuristics(ti, tname, ti->qterm, &tablelen, &tableused,
&invertsixel, nonewfonts)){
@ -951,8 +980,8 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
}
build_supported_styles(ti);
if(ti->pixel_draw == NULL && ti->pixel_draw_late == NULL){
if(kittygraphs){
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_SELFREF);
if(kitty_graphics){
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_ANIMATION);
}
// our current sixel quantization algorithm requires at least 64 color
// registers. we make use of no more than 256. this needs to happen
@ -1028,18 +1057,13 @@ int locate_cursor(tinfo* ti, int* cursor_y, int* cursor_x){
if(tty_emit(u7, fd)){
return -1;
}
// FIXME get that report
/*
loginfo("Got a report from %d %d/%d\n", fd, clr->y, clr->x);
*cursor_y = clr->y;
*cursor_x = clr->x;
get_cursor_location(ti->ictx, cursor_y, cursor_x);
loginfo("got a report from %d %d/%d\n", fd, *cursor_y, *cursor_x);
if(ti->inverted_cursor){
int tmp = *cursor_y;
*cursor_y = *cursor_x;
*cursor_x = tmp;
}
free(clr);
*/
return 0;
}

@ -13,7 +13,6 @@ extern "C" {
#include <pthread.h>
#include <stdbool.h>
#include <notcurses/notcurses.h>
#include "input.h"
#include "fbuf.h"
#include "in.h"
@ -89,43 +88,6 @@ typedef struct cursorreport {
struct cursorreport* next;
} cursorreport;
// we read input from one or two places. if stdin is connected to our
// controlling tty, we read only from that file descriptor. if it is
// connected to something else, and we have a controlling tty, we will
// read data only from stdin and control only from the tty. if we have
// no connected tty, only data is available.
typedef struct ncinputlayer {
// only allow one reader at a time, whether it's the user trying to do so,
// or our desire for a cursor report competing with the user.
pthread_mutex_t lock;
// must be held to operate on the cursor report queue shared between pure
// input and the control layer.
pthread_cond_t creport_cond;
// ttyfd is only valid if we are connected to a tty, *and* stdin is not
// connected to that tty (this usually means stdin was redirected). in that
// case, we read control sequences only from ttyfd.
int ttyfd; // file descriptor for connected tty
int infd; // file descriptor for processing input, from stdin
unsigned char inputbuf[BUFSIZ];
unsigned char csibuf[BUFSIZ]; // running buffer while parsing CSIs
// 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
// available for the app is at inputbuf_valid_starts iff inputbuf_occupied is
// not 0. the main purpose is working around bad predictions of escapes.
unsigned inputbuf_occupied;
unsigned inputbuf_valid_starts;
unsigned inputbuf_write_at;
// number of input events seen. does not belong in ncstats, since it must not
// be reset (semantics are relied upon by widgets for mouse click detection).
uint64_t input_events;
struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys
cursorreport* creport_queue; // queue of cursor reports
bool user_wants_data; // a user context is active
bool inner_wants_data; // if we're blocking on input
struct ncsharedstats* stats; // notcurses sharedstats object
} ncinputlayer;
// terminal interface description. most of these are acquired from terminfo(5)
// (using a database entry specified by TERM). some are determined via
// heuristics based off terminal interrogation or the TERM environment
@ -208,8 +170,6 @@ typedef struct tinfo {
int default_rows; // LINES environment var / lines terminfo / 24
int default_cols; // COLUMNS environment var / cols terminfo / 80
unsigned kittykbd; // kitty keyboard support level
int gpmfd; // connection to GPM daemon
pthread_t gpmthread; // thread handle for GPM watcher
#ifdef __linux__
@ -384,6 +344,8 @@ leave_alternate_screen(FILE* fp, tinfo* ti){
return 0;
}
int cbreak_mode(tinfo* ti);
#ifdef __cplusplus
}
#endif

Loading…
Cancel
Save