mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
documentation and declarations (#13)
ncplane: flesh out API ncplane: line and erase APIs README.md: document differences from ncurses notcurses_init(): print long term name CMake: link librt into notcurses cell: move functionality out to header
This commit is contained in:
parent
a8721df75a
commit
7e92c8bb82
@ -12,6 +12,7 @@ include(GNUInstallDirs)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(TERMINFO REQUIRED tinfo>=6.1)
|
||||
find_library(LIBRT rt)
|
||||
|
||||
file(GLOB LIBSRCS CONFIGURE_DEPENDS src/lib/*.c)
|
||||
add_library(notcurses SHARED ${LIBSRCS})
|
||||
@ -22,8 +23,9 @@ target_include_directories(notcurses
|
||||
"${TERMINFO_INCLUDE_DIR}"
|
||||
)
|
||||
target_link_libraries(notcurses
|
||||
INTERFACE
|
||||
PRIVATE
|
||||
"${TERMINFO_LIBRARIES}"
|
||||
"${LIBRT}"
|
||||
)
|
||||
set_target_properties(notcurses PROPERTIES
|
||||
PUBLIC_HEADER "include/notcurses.h"
|
||||
|
53
README.md
53
README.md
@ -25,7 +25,9 @@ complex TUIs*. I would argue that teletypes etc. are fundamentally unsuitable.
|
||||
Most operating systems seem reasonable targets, but I only have Linux and
|
||||
FreeBSD available for testing.
|
||||
|
||||
notcurses makes use of the Terminfo library shipped with NCURSES.
|
||||
notcurses makes use of the Terminfo library shipped with NCURSES. notcurses
|
||||
uses Terminfo wherever possible, benefiting greatly from its portability and
|
||||
thoroughness.
|
||||
|
||||
notcurses opens up advanced functionality for the interactive user on
|
||||
workstations, phones, laptops, and tablets, at the expense of e.g.
|
||||
@ -40,11 +42,16 @@ Why use this non-standard library?
|
||||
24-bit RGB color.
|
||||
|
||||
* Visual features not directly available via NCURSES, including images,
|
||||
fonts, and video.
|
||||
fonts, video, high-contrast text, and transparent regions.
|
||||
|
||||
* Thread safety, and use in parallel programs, has been a design consideration
|
||||
from the beginning.
|
||||
|
||||
* It's Apache2-licensed in its entirety, as opposed to the
|
||||
[drama in several acts](https://invisible-island.net/ncurses/ncurses-license.html])
|
||||
that is the NCURSES license (the latter is [summarized](https://invisible-island.net/ncurses/ncurses-license.html#issues_freer)
|
||||
as "a restatement of MIT-X11").
|
||||
|
||||
On the other hand, if you're targeting industrial or critical applications,
|
||||
or wish to benefit from the time-tested reliability and portability of Curses,
|
||||
you should by all means use that fine library.
|
||||
@ -127,3 +134,45 @@ from a lower `ncplane` from being seen. An `ncplane` corresponds loosely to an
|
||||
[NCURSES Panel](https://invisible-island.net/ncurses/ncurses-intro.html#panels),
|
||||
but is the primary drawing surface of notcurses—there is no object
|
||||
corresponding to a bare NCURSES `WINDOW`.
|
||||
|
||||
## Differences from NCURSES
|
||||
|
||||
The biggest difference, of course, is that notcurses is not an implementation
|
||||
of X/Open (aka XSI) Curses, nor part of SUS4-2018.
|
||||
|
||||
The detailed differences between notcurses and NCURSES probably can't be fully
|
||||
enumerated, and if they could, no one would want to read it. With that said,
|
||||
some design decisions might surprise NCURSES programmers:
|
||||
|
||||
* The screen is not cleared on entry.
|
||||
* There is no distinct `PANEL` type. The z-buffer is a fundamental property,
|
||||
and all drawable surfaces are ordered along the z axis. There is no
|
||||
equivalent to `update_panels()`.
|
||||
* Scrolling is disabled by default, and cannot be globally enabled.
|
||||
* The hardware cursor is disabled by default, when supported (`civis` capability).
|
||||
* Echoing of input is disabled by default, and `cbreak` mode is used by default.
|
||||
* Colors are always specified as 24 bits in 3 components (RGB). If necessary,
|
||||
these will be quantized for the actual terminal. There are no "color pairs".
|
||||
* There is no distinct "pad" concept (these are NCURSES `WINDOW`s created with
|
||||
the `newpad()` function). All drawable surfaces can exceed the display size.
|
||||
* Multiple threads can freely call into notcurses, so long as they're not
|
||||
accessing the same data. In particular, it is always safe to concurrently
|
||||
mutate different ncplanes in different threads.
|
||||
* NCURSES has thread-ignorant and thread-semi-safe versions, trace-enabled and
|
||||
traceless versions, and versions with and without support for wide characters.
|
||||
notcurses is one library: no tracing, wide characters, thread safety.
|
||||
|
||||
### Features missing relative to NCURSES
|
||||
|
||||
This isn't "features currently missing", but rather "features I do not intend
|
||||
to implement".
|
||||
|
||||
* There is no immediate-output mode (`immedok()`, `echochar()` etc.)
|
||||
With that said, `ncplane_putc()` followed by `notcurses_render()` ought
|
||||
be just as fast as `echochar()`.
|
||||
* There is no support for soft labels (`slk_init()`, etc.).
|
||||
* 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.
|
||||
|
@ -1,17 +1,68 @@
|
||||
#ifndef NOTCURSES_NOTCURSES
|
||||
#define NOTCURSES_NOTCURSES
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Get a human-readable string describing the running ncurses version.
|
||||
// Get a human-readable string describing the running notcurses version.
|
||||
const char* notcurses_version(void);
|
||||
|
||||
struct ncplane; // a drawable notcurses surface
|
||||
struct notcurses; // notcurses state for a given terminal
|
||||
struct cell; // a coordinate on an ncplane: wchar_t(s) and styling
|
||||
struct ncplane; // a drawable notcurses surface, composed of cells
|
||||
struct notcurses; // notcurses state for a given terminal, composed of ncplanes
|
||||
|
||||
// A cell corresponds to a single character cell on some plane. At any cell, we
|
||||
// can have a short array of wchar_t (L'\0'-terminated; we need support an
|
||||
// array due to the possibility of combining characters), a foreground color,
|
||||
// a background color, and an attribute set. The rules on the wchar_t array are
|
||||
// the same as those for an ncurses 6.1 cchar_t:
|
||||
//
|
||||
// FIXME i don't care for this (large) static array one whit. we're not bound
|
||||
// to X/Open, and owe cchar_t no fealty. i do like the attrs and colors being
|
||||
// bound up with it, though. this definition is almost certain to change. we
|
||||
// could overload some invalid UTF-8 construction (say a first byte greater
|
||||
// than 0x7f) to escape out to some attached storage pool, using the
|
||||
// difference as an index into the pool. we would have 25 bits, after all...
|
||||
//
|
||||
// * At most one spacing character, which must be the first if present.
|
||||
// * Up to NCCHARW_MAX-1 nonspacing characters follow. Extra spacing
|
||||
// characters are ignored. A nonspacing character is one for which wcwidth()
|
||||
// returns zero, and is not the wide NUL (L'\0').
|
||||
// * A single control character can be present, with no other characters (save
|
||||
// an immediate wide NUL (L'\0').
|
||||
// * If there are fewer than NCCHARW_MAX wide characters, they must be
|
||||
// terminated with a wide NUL (L'\0').
|
||||
//
|
||||
// Multi-column characters can only have a single attribute/color.
|
||||
// https://pubs.opengroup.org/onlinepubs/007908799/xcurses/intov.html
|
||||
//
|
||||
// Each cell occupies 16 bytes (128 bits). The surface is thus ~2MB for a
|
||||
// (pretty large) 500x200 terminal. At 80x43, it's less than 100KB.
|
||||
#define NCCHARW_MAX 1
|
||||
typedef struct cell {
|
||||
wchar_t cchar[NCCHARW_MAX]; // 1 * 4b -> 4b
|
||||
// The classic NCURSES WA_* attributes (16 bits), plus 16 bits of alpha.
|
||||
uint32_t attrword; // + 4b -> 8b
|
||||
// (channels & 0x8000000000000000ull): inherit styling from prior cell
|
||||
// (channels & 0x4000000000000000ull): foreground is *not* "default color"
|
||||
// (channels & 0x3f00000000000000ull): reserved, must be 0
|
||||
// (channels & 0x00ffffff00000000ull): foreground in 3x8 RGB (rrggbb)
|
||||
// (channels & 0x0000000080000000ull): in the middle of a multicolumn glyph
|
||||
// (channels & 0x0000000040000000ull): background is *not* "default color"
|
||||
// (channels & 0x000000003f000000ull): reserved, must be 0
|
||||
// (channels & 0x0000000000ffffffull): background in 3x8 RGB (rrggbb)
|
||||
// At render time, these 24-bit values are quantized down to terminal
|
||||
// capabilities, if necessary. There's a clear path to 10-bit support should
|
||||
// we one day need it, but keep things cagey for now. "default color" is
|
||||
// best explained by color(3NCURSES). ours is the same concept. until the
|
||||
// "not default color" bit is set, any color you load will be ignored.
|
||||
uint64_t channels; // + 8b == 16b
|
||||
} cell;
|
||||
|
||||
// FIXME we'll need to expose this definition for ncplane_getwc()
|
||||
struct cell; // the contents of a single cell on a single plane
|
||||
@ -98,6 +149,11 @@ void ncplane_yx(const struct ncplane* n, int* y, int* x);
|
||||
|
||||
// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'.
|
||||
void ncplane_move_above(struct ncplane* n, struct ncplane* above);
|
||||
// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'.
|
||||
void ncplane_move_below(struct ncplane* n, struct ncplane* below);
|
||||
// Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom.
|
||||
void ncplane_move_top(struct ncplane* n);
|
||||
void ncplane_move_bottom(struct ncplane* n);
|
||||
|
||||
// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'.
|
||||
void ncplane_move_below(struct ncplane* n, struct ncplane* below);
|
||||
@ -106,10 +162,38 @@ void ncplane_move_below(struct ncplane* n, struct ncplane* below);
|
||||
void ncplane_move_top(struct ncplane* n);
|
||||
void ncplane_move_bottom(struct ncplane* n);
|
||||
|
||||
// Set the current cell in the specified plane to the provided wchar_t array.
|
||||
// The array must not be more than one column worth of wchar_t's, among other
|
||||
// restrictions. Advances the cursor by one cell.
|
||||
int ncplane_putwc(struct ncplane* n, const wchar_t* wcs);
|
||||
// Replace the cell underneath the cursor with the provided cell 'c', and
|
||||
// advance the cursor by one cell *unless we are at the end of the plane*.
|
||||
// On success, returns 1 if the cursor was advanced, and 0 otherwise. On
|
||||
// failure, -1 is returned.
|
||||
int ncplane_putwc(struct ncplane* n, const cell* c);
|
||||
|
||||
// Retrieve the cell under the cursor, returning it in 'c'.
|
||||
void ncplane_getwc(const struct ncplane* n, cell* c);
|
||||
|
||||
// Write a series of wchar_ts to the current location. They will be interpreted
|
||||
// as a series of columns (according to the definition of ncplane_putwc()).
|
||||
// Advances the cursor by some positive number of cells; this number is returned
|
||||
// on success. On error, a non-positive number is returned, indicating the
|
||||
// number of cells which were written before the error.
|
||||
int ncplane_putwstr(struct ncplane* n, const wchar_t* wstr);
|
||||
|
||||
// The ncplane equivalent of wprintf(3) and vwprintf(3), themselves the
|
||||
// wide-character equivalents of printf(3) and vprintf(3).
|
||||
int ncplane_wprintf(struct ncplane* n, const wchar_t* format, ...);
|
||||
|
||||
// Draw horizontal or vertical lines using the specified cell of wchar_t's,
|
||||
// starting at the current cursor position. The cursor will end at the cell
|
||||
// following the last cell output (even, perhaps counter-intuitively, when
|
||||
// drawing vertical lines), just as if ncplane_putwc() was called at that spot.
|
||||
// Returns the number of cells drawn on success. On error, returns the negative
|
||||
// number of cells drawn.
|
||||
int ncplane_hline(struct ncplane* n, const wchar_t* wcs, int len);
|
||||
int ncplane_vline(struct ncplane* n, int yoff, const wchar_t* wcs, int len);
|
||||
|
||||
// Erase all content in the ncplane, resetting all attributes to normal, all
|
||||
// colors to -1, and all cells to undrawn.
|
||||
void ncplane_erase(struct ncplane* n);
|
||||
|
||||
// Retrieve the cell under the cursor, returning it in 'c'.
|
||||
void ncplane_getwc(const struct ncplane* n, struct cell* c);
|
||||
@ -134,6 +218,63 @@ int ncplane_wprintf(struct ncplane* n, const wchar_t* format, ...);
|
||||
int ncplane_fg_rgb8(struct ncplane* n, int r, int g, int b);
|
||||
int ncplane_bg_rgb8(struct ncplane* n, int r, int g, int b);
|
||||
|
||||
// Fine details about terminal
|
||||
|
||||
// FIXME bools for the various WA_* attributes
|
||||
// FIXME verify that they are both supported AND not part of no_color_video
|
||||
|
||||
// Returns the number of colors supported by the palette, or 0 if there is no
|
||||
// palette (DirectColor or no colors).
|
||||
int notcurses_palette_size(const struct notcurses* nc);
|
||||
|
||||
// Working with cells
|
||||
|
||||
// Copies as many wchar_ts out of 'wstr' and into 'c' as it can, according to
|
||||
// the rules of cell composition. If the leading part of wstr is not a valid
|
||||
// cell, -1 is returned. Returns the number of wchar_ts copied, not including
|
||||
// the terminating L'\0' (if 'wstr' is empty, zero is returned).
|
||||
int load_cell(cell* c, const wchar_t* wstr);
|
||||
|
||||
static inline uint32_t
|
||||
cell_fg_rgb(uint64_t channel){
|
||||
return (channel & 0x00ffffff00000000ull) >> 32u;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
cell_bg_rgb(uint64_t channel){
|
||||
return (channel & 0xffffffull);
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
cell_rgb_red(uint32_t rgb){
|
||||
return (rgb & 0xff0000ull) >> 16u;
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
cell_rgb_green(uint32_t rgb){
|
||||
return (rgb & 0xff00ull) >> 8u;
|
||||
}
|
||||
|
||||
static inline unsigned
|
||||
cell_rgb_blue(uint32_t rgb){
|
||||
return (rgb & 0xffull);
|
||||
}
|
||||
|
||||
static inline void
|
||||
cell_set_fg(cell* c, unsigned r, unsigned g, unsigned b){
|
||||
uint64_t rgb = (r & 0xffull) << 48u;
|
||||
rgb |= (g & 0xffull) << 40u;
|
||||
rgb |= (b & 0xffull) << 32u;
|
||||
c->channels = (c->channels & 0x00ffffff00000000ull) | rgb;
|
||||
}
|
||||
|
||||
static inline void
|
||||
cell_get_fb(const cell* c, unsigned* r, unsigned* g, unsigned* b){
|
||||
*r = cell_rgb_red(cell_fg_rgb(c->channels));
|
||||
*g = cell_rgb_green(cell_fg_rgb(c->channels));
|
||||
*b = cell_rgb_blue(cell_fg_rgb(c->channels));
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -24,17 +24,19 @@ int main(void){
|
||||
fprintf(stderr, "Couldn't get standard plane\n");
|
||||
goto err;
|
||||
}
|
||||
int x, cols;
|
||||
ncplane_dimyx(ncp, NULL, &cols);
|
||||
if(ncplane_cursor_move_yx(ncp, 1, 1)){
|
||||
int x, y, rows, cols;
|
||||
ncplane_dimyx(ncp, &rows, &cols);
|
||||
cell c;
|
||||
load_cell(&c, /*L"💣*/L"X");
|
||||
cell_set_fg(&c, 200, 0, 200);
|
||||
for(y = 1 ; y < rows - 1 ; ++y){
|
||||
if(ncplane_cursor_move_yx(ncp, y, 1)){
|
||||
goto err;
|
||||
}
|
||||
for(x = 1 ; x < cols - 1 ; ++x){
|
||||
if(ncplane_fg_rgb8(ncp, 200, 0, 200)){
|
||||
if(ncplane_putwc(ncp, &c)){
|
||||
goto err;
|
||||
}
|
||||
if(ncplane_putwc(ncp, L"X"/*💣*/)){
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if(notcurses_render(nc)){
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <ncurses.h> // needed for some definitions, see terminfo(3ncurses)
|
||||
#include <time.h>
|
||||
#include <term.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
@ -9,34 +10,6 @@
|
||||
#include "notcurses.h"
|
||||
#include "version.h"
|
||||
|
||||
// A cell represents a single character cell in the display. At any cell, we
|
||||
// can have a short array of wchar_t (L'\0'-terminated; we need support an
|
||||
// array due to the possibility of combining characters), a foreground color,
|
||||
// a background color, and an attribute set. The rules on the wchar_t array are
|
||||
// the same as those for an ncurses 6.1 cchar_t:
|
||||
//
|
||||
// * At most one spacing character, which must be the first if present.
|
||||
// * Up to CCHARW_MAX-1 nonspacing characters follow. Extra spacing characters
|
||||
// are ignored. A nonspacing character is one for which wcwidth() returns
|
||||
// zero, and is not the wide NUL (L'\0').
|
||||
// * A single control character can be present, with no other characters (save
|
||||
// an immediate wide NUL (L'\0').
|
||||
// * If there are fewer than CCHARW_MAX wide characters, they must be
|
||||
// terminated with a wide NUL (L'\0').
|
||||
//
|
||||
// Multi-column characters can only have a single attribute/color.
|
||||
// https://pubs.opengroup.org/onlinepubs/007908799/xcurses/intov.html
|
||||
//
|
||||
// Each cell occupies 32 bytes (256 bits). The surface is thus ~4MB for a
|
||||
// (pretty large) 500x200 terminal. At 80x43, it's less than 200KB.
|
||||
typedef struct cell {
|
||||
wchar_t cchar[CCHARW_MAX + 1]; // 6 * 4b == 24b
|
||||
// The attrword covers classic NCURSES attributes (16 bits), plus foreground
|
||||
// and background color, stored as 3x8bits of RGB. At render time, these
|
||||
// 24-bit values are quantized down to terminal capabilities, if necessary.
|
||||
uint64_t attrs;
|
||||
} cell;
|
||||
|
||||
// Some capabilities are so fundamental that we don't attempt to run without
|
||||
// them. Essentially, we require a two-dimensional, random-access terminal.
|
||||
static const char* required_caps[] = {
|
||||
@ -71,6 +44,7 @@ typedef struct ncplane {
|
||||
|
||||
typedef struct notcurses {
|
||||
int ttyfd; // file descriptor for controlling tty (takes stdin)
|
||||
timer_t timer; // CLOCK_MONOTONIC timer for benchmarking
|
||||
int colors; // number of colors usable for this screen
|
||||
// We verify that some capabilities exist (see required_caps). Those needn't
|
||||
// be checked before further use; just use tiparm() directly. These might be
|
||||
@ -268,10 +242,16 @@ notcurses* notcurses_init(const notcurses_options* opts){
|
||||
if(ret == NULL){
|
||||
return ret;
|
||||
}
|
||||
if(timer_create(CLOCK_MONOTONIC, NULL, &ret->timer)){
|
||||
fprintf(stderr, "Error initializing monotonic clock (%s)\n", strerror(errno));
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->ttyfd = opts->outfd;
|
||||
if(tcgetattr(ret->ttyfd, &ret->tpreserved)){
|
||||
fprintf(stderr, "Couldn't preserve terminal state for %d (%s)\n",
|
||||
ret->ttyfd, strerror(errno));
|
||||
timer_delete(ret->timer);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
@ -287,6 +267,8 @@ notcurses* notcurses_init(const notcurses_options* opts){
|
||||
fprintf(stderr, "Terminfo error %d (see terminfo(3ncurses))\n", termerr);
|
||||
goto err;
|
||||
}
|
||||
char* longname_term = longname();
|
||||
fprintf(stderr, "Term: %s\n", longname_term ? longname_term : "?");
|
||||
ret->RGBflag = tigetflag("RGB") == 1;
|
||||
if((ret->colors = tigetnum("colors")) <= 0){
|
||||
fprintf(stderr, "This terminal doesn't appear to support colors\n");
|
||||
@ -331,6 +313,7 @@ notcurses* notcurses_init(const notcurses_options* opts){
|
||||
|
||||
err:
|
||||
tcsetattr(ret->ttyfd, TCSANOW, &ret->tpreserved);
|
||||
timer_delete(ret->timer);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
@ -364,26 +347,6 @@ erpchar(int c){
|
||||
return EOF;
|
||||
}
|
||||
|
||||
#define CELL_RMASK 0x0000ff0000000000ull
|
||||
#define CELL_GMASK 0x000000ff00000000ull
|
||||
#define CELL_BMASK 0x00000000ff000000ull
|
||||
#define CELL_RGBMASK (CELL_RMASK | CELL_GMASK | CELL_BMASK)
|
||||
|
||||
static void
|
||||
cell_set_fg(cell* c, unsigned r, unsigned g, unsigned b){
|
||||
uint64_t rgb = (r & 0xffull) << 40u;
|
||||
rgb += (g & 0xffull) << 32u;
|
||||
rgb += (b & 0xffull) << 24u;
|
||||
c->attrs = (c->attrs & ~CELL_RGBMASK) | rgb;
|
||||
}
|
||||
|
||||
static void
|
||||
cell_get_fb(const cell* c, unsigned* r, unsigned* g, unsigned* b){
|
||||
*r = (c->attrs & CELL_RMASK) >> 40u;
|
||||
*g = (c->attrs & CELL_GMASK) >> 32;
|
||||
*b = (c->attrs & CELL_BMASK) >> 24u;
|
||||
}
|
||||
|
||||
int ncplane_fg_rgb8(ncplane* n, int r, int g, int b){
|
||||
if(r >= 256 || g >= 256 || b >= 256){
|
||||
return -1;
|
||||
@ -451,12 +414,13 @@ term_movyx(int y, int x){
|
||||
}
|
||||
|
||||
// Write the cchar (one cell's worth of wchar_t's) to the physical terminal
|
||||
// FIXME probably want to use a wmemstream
|
||||
static int
|
||||
term_putw(const notcurses* nc, const cell* c){
|
||||
ssize_t w;
|
||||
size_t len = wcslen(c->cchar);
|
||||
size_t len = wcsnlen(c->cchar, sizeof(c->cchar) / sizeof(*c->cchar));
|
||||
if(len == 0){
|
||||
if((w = write(nc->ttyfd, " ", 1)) < 0 || (size_t)w != 1){
|
||||
if((w = write(nc->ttyfd, " ", 1)) < 0 || (size_t)w != 1){ // FIXME
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@ -518,15 +482,6 @@ void ncplane_cursor_yx(const ncplane* n, int* y, int* x){
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
validate_wchar_cell(const cell* c, const wchar_t* wcs){
|
||||
if(wcslen(wcs) >= sizeof(c->cchar) / sizeof(*c->cchar)){
|
||||
return false;
|
||||
}
|
||||
// FIXME check other crap
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
advance_cursor(struct ncplane* n){
|
||||
if(++n->x == n->lenx){
|
||||
@ -537,12 +492,44 @@ advance_cursor(struct ncplane* n){
|
||||
}
|
||||
}
|
||||
|
||||
int ncplane_putwc(struct ncplane* n, const wchar_t* wcs){
|
||||
cell* c = &n->fb[fbcellidx(n, n->y, n->x)];
|
||||
if(!validate_wchar_cell(c, wcs)){
|
||||
return -1;
|
||||
}
|
||||
memcpy(c->cchar, wcs, wcslen(wcs) * sizeof(*wcs));
|
||||
int ncplane_putwc(struct ncplane* n, const cell* c){
|
||||
cell* targ = &n->fb[fbcellidx(n, n->y, n->x)];
|
||||
memcpy(targ, c, sizeof(*c));
|
||||
advance_cursor(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_cell(cell* c, const wchar_t* wstr){
|
||||
int copied = 0;
|
||||
do{
|
||||
if(copied == sizeof(c->cchar) / sizeof(*c->cchar)){
|
||||
if(!wcwidth(*wstr)){ // next one *must* be a spacing char
|
||||
return -1; // filled up the buffer
|
||||
}
|
||||
break; // no terminator on cells which fill the array [shrug]
|
||||
}
|
||||
if(copied && *wstr != L'\0' && wcwidth(*wstr)){
|
||||
break; // only nonspacing (zero-width) chars after first; throw it back
|
||||
}
|
||||
c->cchar[copied++] = *wstr;
|
||||
}while(*wstr++ != L'\0'); // did we just copy L'\0'? if so, we're always done
|
||||
return copied;
|
||||
}
|
||||
|
||||
int ncplane_putwstr(struct ncplane* n, const wchar_t* wstr){
|
||||
int ret = 0;
|
||||
// FIXME speed up this blissfully naive solution
|
||||
cell c;
|
||||
while(*wstr != L'\0'){
|
||||
int wcs = load_cell(&c, wstr);
|
||||
if(wcs <= 0){
|
||||
return -ret;
|
||||
}
|
||||
wstr += wcs;
|
||||
if(ncplane_putwc(n, &c)){
|
||||
return -ret;
|
||||
}
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -97,12 +97,15 @@ TEST_F(NcplaneTest, RejectBadRGB) {
|
||||
EXPECT_NE(0, ncplane_fg_rgb8(n_, 255, 256, 255));
|
||||
EXPECT_NE(0, ncplane_fg_rgb8(n_, 255, 255, 256));
|
||||
EXPECT_NE(0, ncplane_fg_rgb8(n_, 256, 256, 256));
|
||||
EXPECT_EQ(0, ncplane_fg_rgb8(n_, 255, 255, 255));
|
||||
}
|
||||
|
||||
// Verify we can emit a wide character, and it advances the cursor
|
||||
TEST_F(NcplaneTest, EmitWchar) {
|
||||
wchar_t cchar[] = L"✔";
|
||||
EXPECT_EQ(0, ncplane_putwc(n_, cchar));
|
||||
cell c;
|
||||
load_cell(&c, cchar);
|
||||
EXPECT_EQ(0, ncplane_putwc(n_, &c));
|
||||
int x, y;
|
||||
ncplane_cursor_yx(n_, &y, &x);
|
||||
EXPECT_EQ(y, 0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user