mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-10 01:29:05 -04:00
Merge branch 'master' of github.com:dankamongmen/notcurses
This commit is contained in:
commit
94722c0f28
@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(notcurses VERSION 0.9.0
|
project(notcurses VERSION 1.0.0
|
||||||
DESCRIPTION "UI for modern terminal emulators"
|
DESCRIPTION "UI for modern terminal emulators"
|
||||||
HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses"
|
HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses"
|
||||||
LANGUAGES C CXX)
|
LANGUAGES C CXX)
|
||||||
@ -79,7 +79,7 @@ target_compile_definitions(notcurses-demo
|
|||||||
)
|
)
|
||||||
|
|
||||||
# tiny proofs of concept, one binary per source file
|
# tiny proofs of concept, one binary per source file
|
||||||
file(GLOB POCSRCS CONFIGURE_DEPENDS src/poc/*.c)
|
file(GLOB POCSRCS CONFIGURE_DEPENDS src/poc/*.c src/poc/*.cpp)
|
||||||
foreach(f ${POCSRCS})
|
foreach(f ${POCSRCS})
|
||||||
get_filename_component(fe "${f}" NAME_WE)
|
get_filename_component(fe "${f}" NAME_WE)
|
||||||
add_executable(${fe} ${f})
|
add_executable(${fe} ${f})
|
||||||
@ -202,11 +202,11 @@ file(GLOB MANPAGES1 CONFIGURE_DEPENDS doc/man/man1/*)
|
|||||||
file(GLOB MANPAGES3 CONFIGURE_DEPENDS doc/man/man3/*)
|
file(GLOB MANPAGES3 CONFIGURE_DEPENDS doc/man/man3/*)
|
||||||
install(FILES
|
install(FILES
|
||||||
${MANPAGES1}
|
${MANPAGES1}
|
||||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man1
|
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1
|
||||||
)
|
)
|
||||||
install(FILES
|
install(FILES
|
||||||
${MANPAGES3}
|
${MANPAGES3}
|
||||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man3
|
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man3
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS notcurses-demo DESTINATION bin)
|
install(TARGETS notcurses-demo DESTINATION bin)
|
||||||
|
118
README.md
118
README.md
@ -14,8 +14,8 @@ by [nick black](https://nick-black.com/dankwiki/index.php/Hack_on) (<nickblack@l
|
|||||||
* [Requirements](#requirements)
|
* [Requirements](#requirements)
|
||||||
* [Use](#use)
|
* [Use](#use)
|
||||||
* [Input](#input)
|
* [Input](#input)
|
||||||
* [Planes](#planes)
|
* [Planes](#planes) ([Plane Channels API](#plane-channels-api), [Wide chars](#wide-chars))
|
||||||
* [Cells](#cells)
|
* [Cells](#cells) ([Cell Channels API](#cell-channels-api))
|
||||||
* [Multimedia](#multimedia)
|
* [Multimedia](#multimedia)
|
||||||
* [Panelreels](#panelreels)
|
* [Panelreels](#panelreels)
|
||||||
* [Channels](#channels)
|
* [Channels](#channels)
|
||||||
@ -25,6 +25,7 @@ by [nick black](https://nick-black.com/dankwiki/index.php/Hack_on) (<nickblack@l
|
|||||||
* [Features missing relative to NCURSES](#features-missing-relative-to-ncurses)
|
* [Features missing relative to NCURSES](#features-missing-relative-to-ncurses)
|
||||||
* [Adapting NCURSES programs](#adapting-ncurses-programs)
|
* [Adapting NCURSES programs](#adapting-ncurses-programs)
|
||||||
* [Environment notes](#environment-notes)
|
* [Environment notes](#environment-notes)
|
||||||
|
* [DirectColor detection](#DirectColor-detection)
|
||||||
* [Fonts](#fonts)
|
* [Fonts](#fonts)
|
||||||
* [Supplemental material](#supplemental-material)
|
* [Supplemental material](#supplemental-material)
|
||||||
* [Useful links](#useful-links)
|
* [Useful links](#useful-links)
|
||||||
@ -41,7 +42,7 @@ by [nick black](https://nick-black.com/dankwiki/index.php/Hack_on) (<nickblack@l
|
|||||||
|
|
||||||
* **What it is not**: a source-compatible X/Open Curses implementation, nor a
|
* **What it is not**: a source-compatible X/Open Curses implementation, nor a
|
||||||
replacement for NCURSES on existing systems, nor a widely-ported and -tested
|
replacement for NCURSES on existing systems, nor a widely-ported and -tested
|
||||||
bedrock of Open Source, nor a battle-proven, veteran library.
|
bedrock of free software, nor a battle-proven, veteran library.
|
||||||
|
|
||||||
notcurses abandons the X/Open Curses API bundled as part of the Single UNIX
|
notcurses abandons the X/Open Curses API bundled as part of the Single UNIX
|
||||||
Specification. The latter shows its age, and seems not capable of making use of
|
Specification. The latter shows its age, and seems not capable of making use of
|
||||||
@ -202,6 +203,9 @@ you off guard.
|
|||||||
Utility functions operating on the toplevel `notcurses` object include:
|
Utility functions operating on the toplevel `notcurses` object include:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
|
// Return the topmost ncplane, of which there is always at least one.
|
||||||
|
struct ncplane* notcurses_top(struct notcurses* n);
|
||||||
|
|
||||||
// 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
|
||||||
// (as signaled via SIGWINCH), notcurses_render() might not function properly.
|
// (as signaled via SIGWINCH), notcurses_render() might not function properly.
|
||||||
@ -376,12 +380,11 @@ corresponding to a bare NCURSES `WINDOW`.
|
|||||||
// of the resized ncplane. Finally, 'ylen' and 'xlen' are the dimensions of the
|
// of the resized ncplane. Finally, 'ylen' and 'xlen' are the dimensions of the
|
||||||
// ncplane after resizing. 'ylen' must be greater than or equal to 'keepleny',
|
// ncplane after resizing. 'ylen' must be greater than or equal to 'keepleny',
|
||||||
// and 'xlen' must be greater than or equal to 'keeplenx'. It is an error to
|
// and 'xlen' must be greater than or equal to 'keeplenx'. It is an error to
|
||||||
// attempt to resize the standard plane. If either of 'keepy' or 'keepx' is
|
// attempt to resize the standard plane. If either of 'keepleny' or 'keeplenx'
|
||||||
// non-zero, both must be non-zero.
|
// is non-zero, both must be non-zero.
|
||||||
//
|
//
|
||||||
// Essentially, the kept material does not move. It serves to anchor the
|
// Essentially, the kept material does not move. It serves to anchor the
|
||||||
// resized plane. If there is no kept material, the plane can move freely:
|
// resized plane. If there is no kept material, the plane can move freely.
|
||||||
// it is possible to implement ncplane_move() in terms of ncplane_resize().
|
|
||||||
int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny,
|
int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny,
|
||||||
int keeplenx, int yoff, int xoff, int ylen, int xlen);
|
int keeplenx, int yoff, int xoff, int ylen, int xlen);
|
||||||
|
|
||||||
@ -420,6 +423,8 @@ void ncplane_styles_off(struct ncplane* n, unsigned stylebits);
|
|||||||
// Return the current styling for this ncplane.
|
// Return the current styling for this ncplane.
|
||||||
unsigned ncplane_styles(const struct ncplane* n);
|
unsigned ncplane_styles(const struct ncplane* n);
|
||||||
|
|
||||||
|
// Return the ncplane below this one, or NULL if this is at the stack's bottom.
|
||||||
|
struct ncplane* ncplane_below(struct ncplane* n);
|
||||||
```
|
```
|
||||||
|
|
||||||
If a given cell's glyph is zero, or its foreground channel is fully transparent,
|
If a given cell's glyph is zero, or its foreground channel is fully transparent,
|
||||||
@ -463,6 +468,10 @@ not necessarily reflect anything on the actual screen).
|
|||||||
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
|
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
|
||||||
int ncplane_at_cursor(struct ncplane* n, cell* c);
|
int ncplane_at_cursor(struct ncplane* n, cell* c);
|
||||||
|
|
||||||
|
// Retrieve the cell at the specified location on the specified plane, returning
|
||||||
|
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
|
||||||
|
int ncplane_at_yx(struct ncplane* n, int y, int x, cell* c);
|
||||||
|
|
||||||
// Manipulate the opaque user pointer associated with this plane.
|
// Manipulate the opaque user pointer associated with this plane.
|
||||||
// ncplane_set_userptr() returns the previous userptr after replacing
|
// ncplane_set_userptr() returns the previous userptr after replacing
|
||||||
// it with 'opaque'. the others simply return the userptr.
|
// it with 'opaque'. the others simply return the userptr.
|
||||||
@ -710,12 +719,9 @@ ncplane_rounded_box(struct ncplane* n, uint32_t attr, uint64_t channels,
|
|||||||
if((ret = cells_rounded_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
if((ret = cells_rounded_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
||||||
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
||||||
}
|
}
|
||||||
cell_release(n, &ul);
|
cell_release(n, &ul); cell_release(n, &ur);
|
||||||
cell_release(n, &ur);
|
cell_release(n, &ll); cell_release(n, &lr);
|
||||||
cell_release(n, &ll);
|
cell_release(n, &hl); cell_release(n, &vl);
|
||||||
cell_release(n, &lr);
|
|
||||||
cell_release(n, &hl);
|
|
||||||
cell_release(n, &vl);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,12 +744,9 @@ ncplane_double_box(struct ncplane* n, uint32_t attr, uint64_t channels,
|
|||||||
if((ret = cells_double_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
if((ret = cells_double_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
||||||
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
||||||
}
|
}
|
||||||
cell_release(n, &ul);
|
cell_release(n, &ul); cell_release(n, &ur);
|
||||||
cell_release(n, &ur);
|
cell_release(n, &ll); cell_release(n, &lr);
|
||||||
cell_release(n, &ll);
|
cell_release(n, &hl); cell_release(n, &vl);
|
||||||
cell_release(n, &lr);
|
|
||||||
cell_release(n, &hl);
|
|
||||||
cell_release(n, &vl);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,6 +873,32 @@ void ncplane_set_fg_default(struct ncplane* n);
|
|||||||
void ncplane_set_bg_default(struct ncplane* n);
|
void ncplane_set_bg_default(struct ncplane* n);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Wide chars
|
||||||
|
|
||||||
|
Notcurses assumes that all glyphs occupy widths which are an integral multiple
|
||||||
|
of the smallest possible glyph's cell width (aka a "fixed-width font"). Unicode
|
||||||
|
introduces characters which generally occupy two such cells, known as wide
|
||||||
|
characters (though in the end, width of a glyph is a property of the font). It
|
||||||
|
is not possible to print half of such a glyph, nor is it generally possible to
|
||||||
|
print a wide glyph on the last column of a terminal.
|
||||||
|
|
||||||
|
Notcurses does not consider it an error to place a wide character on the last
|
||||||
|
column of a line. It will obliterate any content which was in that cell, but
|
||||||
|
will not itself be rendered. The default content will not be reproduced in such
|
||||||
|
a cell, either. When any character is placed atop a wide character's left or
|
||||||
|
right half, the wide character is obliterated in its entirety. When a wide
|
||||||
|
character is placed, any character under its left or right side is annihilated,
|
||||||
|
including wide characters. It is thus possible for two wide characters to sit
|
||||||
|
at columns 0 and 2, and for both to be obliterated by a single wide character
|
||||||
|
placed at column 1.
|
||||||
|
|
||||||
|
Likewise, when rendering, a plane which would partially obstruct a wide glyph
|
||||||
|
prevents it from being rendered entirely. A pathological case would be that of
|
||||||
|
a terminal _n_ columns in width, containing _n-1_ planes, each 2 columns wide.
|
||||||
|
The planes are placed at offsets [0..n - 2]. Each plane is above the plane to
|
||||||
|
its left, and each plane contains a single wide character. Were this to be
|
||||||
|
rendered, only the rightmost plane (and its single glyph) would be rendered!
|
||||||
|
|
||||||
### Cells
|
### Cells
|
||||||
|
|
||||||
Unlike the `notcurses` or `ncplane` objects, the definition of `cell` is
|
Unlike the `notcurses` or `ncplane` objects, the definition of `cell` is
|
||||||
@ -934,12 +963,14 @@ typedef struct cell {
|
|||||||
#define CELL_ALPHA_OPAQUE 0
|
#define CELL_ALPHA_OPAQUE 0
|
||||||
```
|
```
|
||||||
|
|
||||||
`cell`s must be initialized with `CELL_TRIVIAL_INITIALIZER` or `cell_init()`
|
`cell`s must be initialized with an initialization macro or `cell_init()`
|
||||||
before any other use (both merely zero out the `cell`).
|
before any other use. `cell_init()` and `CELL_TRIVIAL_INITIALIZER` both
|
||||||
|
simply zero out the `cell`.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
#define CELL_TRIVIAL_INITIALIZER { .gcluster = '\0', .attrword = 0, .channels = 0, }
|
#define CELL_TRIVIAL_INITIALIZER { .gcluster = '\0', .attrword = 0, .channels = 0, }
|
||||||
#define CELL_SIMPLE_INITIALIZER(c) { .gcluster = c, .attrword = 0, .channels = 0, }
|
#define CELL_SIMPLE_INITIALIZER(c) { .gcluster = (c), .attrword = 0, .channels = 0, }
|
||||||
|
#define CELL_INITIALIZER(c, a, chan) { .gcluster = (c), .attrword = (a), .channels = (chan), }
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
cell_init(cell* c){
|
cell_init(cell* c){
|
||||||
@ -1038,6 +1069,17 @@ cell_simple_p(const cell* c){
|
|||||||
return c->gcluster < 0x80;
|
return c->gcluster < 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
cell_load_simple(struct ncplane* n, cell* c, char ch){
|
||||||
|
cell_release(n, c);
|
||||||
|
c->channels &= ~CELL_WIDEASIAN_MASK;
|
||||||
|
c->gcluster = ch;
|
||||||
|
if(cell_simple_p(c)){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// get the offset into the egcpool for this cell's EGC. returns meaningless and
|
// get the offset into the egcpool for this cell's EGC. returns meaningless and
|
||||||
// unsafe results if called on a simple cell.
|
// unsafe results if called on a simple cell.
|
||||||
static inline uint32_t
|
static inline uint32_t
|
||||||
@ -1745,6 +1787,7 @@ acquired using the `notcurses_stats()` function. This function cannot fail.
|
|||||||
```c
|
```c
|
||||||
typedef struct ncstats {
|
typedef struct ncstats {
|
||||||
uint64_t renders; // number of notcurses_render() runs
|
uint64_t renders; // number of notcurses_render() runs
|
||||||
|
uint64_t failed_renders; // number of aborted renders, should be 0
|
||||||
uint64_t render_bytes; // bytes emitted to ttyfp
|
uint64_t render_bytes; // bytes emitted to ttyfp
|
||||||
uint64_t render_max_bytes; // max bytes emitted for a frame
|
uint64_t render_max_bytes; // max bytes emitted for a frame
|
||||||
uint64_t render_min_bytes; // min bytes emitted for a frame
|
uint64_t render_min_bytes; // min bytes emitted for a frame
|
||||||
@ -1895,19 +1938,38 @@ These are pretty obvious, implementation-wise.
|
|||||||
|
|
||||||
* If your terminal has an option about default interpretation of "ambiguous-width
|
* If your terminal has an option about default interpretation of "ambiguous-width
|
||||||
characters" (this is actually a technical term from Unicode), ensure it is
|
characters" (this is actually a technical term from Unicode), ensure it is
|
||||||
set to **Wide**, not narrow.
|
set to **Wide**, not narrow. If that doesn't work, ensure it is set to
|
||||||
|
**Narrow**, heh.
|
||||||
|
|
||||||
* If you can disable BiDi in your terminal, do so while running notcurses
|
* If you can disable BiDi in your terminal, do so while running notcurses
|
||||||
applications, until I have that handled better. notcurses doesn't recognize
|
applications, until I have that handled better. notcurses doesn't recognize
|
||||||
the BiDi state machine transitions, and thus merrily continues writing
|
the BiDi state machine transitions, and thus merrily continues writing
|
||||||
left-to-right. ﷽!
|
left-to-right. Likewise, ultra-wide glyphs will have interesting effects.
|
||||||
|
﷽!
|
||||||
|
|
||||||
* The unit tests assume dimensions of at least 80x25. They might work in a
|
* The unit tests assume dimensions of at least 80x25. They might work in a
|
||||||
smaller terminal. They might not. Don't file bugs on it.
|
smaller terminal. They might not. Don't file bugs on it.
|
||||||
|
|
||||||
|
### DirectColor detection
|
||||||
|
|
||||||
|
notcurses aims to use only information found in the terminal's terminfo entry to detect capabilities, DirectColor
|
||||||
|
being one of them. Support for this is indicated by terminfo having a flag, added in NCURSES 6.1, named `RGB` set
|
||||||
|
to `true`. However, as of today there are few and far between terminfo entries which have the capability in their
|
||||||
|
database entry and so DirectColor won't be used in most cases. Terminal emulators have had for years a kludge to
|
||||||
|
work around this limitation of terminfo in the form of the `COLORTERM` environment variable which, if set to either
|
||||||
|
`truecolor` or `24bit` does the job of indicating the capability of sending the escapes 48 and 38 together with a
|
||||||
|
tripartite RGB (0 ≤ c ≤ 255 for all three components) to specify fore- and background colors.
|
||||||
|
Checking for `COLORTERM` admittedly goes against the goal stated at the top of this section but, for all practical
|
||||||
|
purposes, makes the detection work quite well **today**.
|
||||||
|
|
||||||
### Fonts
|
### Fonts
|
||||||
|
|
||||||
Fonts end up being a whole thing.
|
Fonts end up being a whole thing, little of which is pleasant. I'll write this
|
||||||
|
up someday **FIXME**.
|
||||||
|
|
||||||
|
### When all else fails...
|
||||||
|
|
||||||
|
...fuck wit' it harder, hax0r.
|
||||||
|
|
||||||
## Supplemental material
|
## Supplemental material
|
||||||
|
|
||||||
@ -1930,9 +1992,13 @@ Fonts end up being a whole thing.
|
|||||||
|
|
||||||
* [tui-rs](https://github.com/fdehau/tui-rs) (Rust)
|
* [tui-rs](https://github.com/fdehau/tui-rs) (Rust)
|
||||||
* [blessed-contrib](https://github.com/yaronn/blessed-contrib) (Javascript)
|
* [blessed-contrib](https://github.com/yaronn/blessed-contrib) (Javascript)
|
||||||
|
* [FINAL CUT](https://github.com/gansm/finalcut) (C++)
|
||||||
|
|
||||||
### History
|
### History
|
||||||
|
|
||||||
|
* 2019-12-18: notcurses [0.9.0 "You dig in! You dig out! You get out!"](https://github.com/dankamongmen/notcurses/releases/tag/v0.9.0),
|
||||||
|
and also the first contributor besides myself (@grendello). Last major
|
||||||
|
pre-GA release.
|
||||||
* 2019-12-05: notcurses [0.4.0 "TRAP MUSIC ALL NIGHT LONG"](https://github.com/dankamongmen/notcurses/releases/tag/v0.4.0),
|
* 2019-12-05: notcurses [0.4.0 "TRAP MUSIC ALL NIGHT LONG"](https://github.com/dankamongmen/notcurses/releases/tag/v0.4.0),
|
||||||
the first generally usable notcurses. I prepare a [demo](https://www.youtube.com/watch?v=eEv2YRyiEVM),
|
the first generally usable notcurses. I prepare a [demo](https://www.youtube.com/watch?v=eEv2YRyiEVM),
|
||||||
and release it on YouTube.
|
and release it on YouTube.
|
||||||
@ -1961,7 +2027,7 @@ Fonts end up being a whole thing.
|
|||||||
* Notcurses could never be what it is without decades of tireless, likely
|
* Notcurses could never be what it is without decades of tireless, likely
|
||||||
thankless work by Thomas E. Dickey on NCURSES. His FAQ is a model of
|
thankless work by Thomas E. Dickey on NCURSES. His FAQ is a model of
|
||||||
engineering history. He exemplifies documentation excellence and
|
engineering history. He exemplifies documentation excellence and
|
||||||
conservative, thoughtful stewardship. The Open Source community owes
|
conservative, thoughtful stewardship. The free software community owes
|
||||||
Mr. Dickey a great debt.
|
Mr. Dickey a great debt.
|
||||||
* Justine Tunney, one of my first friends at Google NYC, was always present
|
* Justine Tunney, one of my first friends at Google NYC, was always present
|
||||||
with support, and pointed out the useful memstream functionality of
|
with support, and pointed out the useful memstream functionality of
|
||||||
|
13
debian/changelog
vendored
13
debian/changelog
vendored
@ -1,8 +1,15 @@
|
|||||||
notcurses (0.9.0-1) UNRELEASED; urgency=medium
|
notcurses (1.0.0-1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
*
|
* Install binaries' man pages in notcurses-bin
|
||||||
|
* Install library man pages in libnotcurses-dev
|
||||||
|
|
||||||
-- Nick Black <dankamongmen@gmail.com> Thu, 05 Dec 2019 04:37:32 -0500
|
-- Nick Black <dankamongmen@gmail.com> Wed, 18 Dec 2019 06:47:44 -0500
|
||||||
|
|
||||||
|
notcurses (0.9.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* New upstream version
|
||||||
|
|
||||||
|
-- Nick Black <dankamongmen@gmail.com> Wed, 18 Dec 2019 05:32:55 -0500
|
||||||
|
|
||||||
notcurses (0.4.0-1) unstable; urgency=medium
|
notcurses (0.4.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
1
debian/libnotcurses-dev.install
vendored
1
debian/libnotcurses-dev.install
vendored
@ -2,3 +2,4 @@ usr/include
|
|||||||
usr/lib/*/*.so
|
usr/lib/*/*.so
|
||||||
usr/lib/*/pkgconfig/*.pc
|
usr/lib/*/pkgconfig/*.pc
|
||||||
usr/lib/*/cmake/*
|
usr/lib/*/cmake/*
|
||||||
|
usr/share/man/man3/*
|
||||||
|
1
debian/notcurses-bin.install
vendored
1
debian/notcurses-bin.install
vendored
@ -1,2 +1,3 @@
|
|||||||
usr/bin/*
|
usr/bin/*
|
||||||
usr/share/notcurses/*
|
usr/share/notcurses/*
|
||||||
|
usr/man/man1/*
|
||||||
|
@ -27,25 +27,25 @@ contains a set of text-based demonstrations of capabilities from the notcurses l
|
|||||||
.P
|
.P
|
||||||
(i)ntro—a setting of tone
|
(i)ntro—a setting of tone
|
||||||
.P
|
.P
|
||||||
(s)liders—a missing-piece puzzle made up of colorful blocks
|
|
||||||
.P
|
|
||||||
(u)niblocks—a series of blocks detailing Unicode pages
|
|
||||||
.P
|
|
||||||
(m)axcolors—smoothly changing colors
|
(m)axcolors—smoothly changing colors
|
||||||
.P
|
.P
|
||||||
(l)uigi-a dashing plumber of Apennine persuasion
|
(l)uigi-a dashing plumber of Apennine persuasion
|
||||||
.P
|
.P
|
||||||
|
(u)niblocks—a series of blocks detailing Unicode pages
|
||||||
|
.P
|
||||||
(b)oxes—pulsating boxes with a transparent center
|
(b)oxes—pulsating boxes with a transparent center
|
||||||
.P
|
.P
|
||||||
(g)rid—a gradient of color lain atop a great grid
|
(g)rid—a gradient of color lain atop a great grid
|
||||||
.P
|
.P
|
||||||
(w)idecolors—letters of many languages in many colors
|
(s)liders—a missing-piece puzzle made up of colorful blocks
|
||||||
.P
|
.P
|
||||||
(v)iew—in which PNGs are rendered as text, and a video, too
|
(w)idechomper—a gremlin feasts upon wide characters
|
||||||
|
.P
|
||||||
|
(v)iew—images and a video are rendered as text
|
||||||
.P
|
.P
|
||||||
(p)anelreels—demonstration of the panelreel high-level widget
|
(p)anelreels—demonstration of the panelreel high-level widget
|
||||||
.P
|
.P
|
||||||
(o)utro—a message of hope from the library author
|
(o)utro—a message of hope from the library's author
|
||||||
.SH NOTES
|
.SH NOTES
|
||||||
Proper display requires a terminal advertising the RGB terminfo(5) capability (necessary for specification of arbitrary 24bpp colors), and a monospaced font with good Unicode support.
|
Proper display requires a terminal advertising the RGB terminfo(5) capability (necessary for specification of arbitrary 24bpp colors), and a monospaced font with good Unicode support.
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
|
@ -133,6 +133,9 @@ API int notcurses_stop(struct notcurses* nc);
|
|||||||
// successful call to notcurses_render().
|
// successful call to notcurses_render().
|
||||||
API int notcurses_render(struct notcurses* nc);
|
API int notcurses_render(struct notcurses* nc);
|
||||||
|
|
||||||
|
// Return the topmost ncplane, of which there is always at least one.
|
||||||
|
API struct ncplane* notcurses_top(struct notcurses* n);
|
||||||
|
|
||||||
// All input is currently taken from stdin, though this will likely change. We
|
// All input is currently taken from stdin, though this will likely change. We
|
||||||
// attempt to read a single UTF8-encoded Unicode codepoint, *not* an entire
|
// attempt to read a single UTF8-encoded Unicode codepoint, *not* an entire
|
||||||
// Extended Grapheme Cluster. It is also possible that we will read a special
|
// Extended Grapheme Cluster. It is also possible that we will read a special
|
||||||
@ -279,7 +282,8 @@ API unsigned notcurses_supported_styles(const struct notcurses* nc);
|
|||||||
API int notcurses_palette_size(const struct notcurses* nc);
|
API int notcurses_palette_size(const struct notcurses* nc);
|
||||||
|
|
||||||
typedef struct ncstats {
|
typedef struct ncstats {
|
||||||
uint64_t renders; // number of notcurses_render() runs
|
uint64_t renders; // number of successful notcurses_render() runs
|
||||||
|
uint64_t failed_renders; // number of aborted renders, should be 0
|
||||||
uint64_t render_bytes; // bytes emitted to ttyfp
|
uint64_t render_bytes; // bytes emitted to ttyfp
|
||||||
int64_t render_max_bytes; // max bytes emitted for a frame
|
int64_t render_max_bytes; // max bytes emitted for a frame
|
||||||
int64_t render_min_bytes; // min bytes emitted for a frame
|
int64_t render_min_bytes; // min bytes emitted for a frame
|
||||||
@ -309,12 +313,11 @@ API void notcurses_stats(const struct notcurses* nc, ncstats* stats);
|
|||||||
// of the resized ncplane. Finally, 'ylen' and 'xlen' are the dimensions of the
|
// of the resized ncplane. Finally, 'ylen' and 'xlen' are the dimensions of the
|
||||||
// ncplane after resizing. 'ylen' must be greater than or equal to 'keepleny',
|
// ncplane after resizing. 'ylen' must be greater than or equal to 'keepleny',
|
||||||
// and 'xlen' must be greater than or equal to 'keeplenx'. It is an error to
|
// and 'xlen' must be greater than or equal to 'keeplenx'. It is an error to
|
||||||
// attempt to resize the standard plane. If either of 'keepy' or 'keepx' is
|
// attempt to resize the standard plane. If either of 'keepleny' or 'keeplenx'
|
||||||
// non-zero, both must be non-zero.
|
// is non-zero, both must be non-zero.
|
||||||
//
|
//
|
||||||
// Essentially, the kept material does not move. It serves to anchor the
|
// Essentially, the kept material does not move. It serves to anchor the
|
||||||
// resized plane. If there is no kept material, the plane can move freely:
|
// resized plane. If there is no kept material, the plane can move freely.
|
||||||
// it is possible to implement ncplane_move() in terms of ncplane_resize().
|
|
||||||
API int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny,
|
API int ncplane_resize(struct ncplane* n, int keepy, int keepx, int keepleny,
|
||||||
int keeplenx, int yoff, int xoff, int ylen, int xlen);
|
int keeplenx, int yoff, int xoff, int ylen, int xlen);
|
||||||
|
|
||||||
@ -342,16 +345,44 @@ API void ncplane_yx(const struct ncplane* n, int* RESTRICT y, int* RESTRICT x);
|
|||||||
API int ncplane_move_top(struct ncplane* n);
|
API int ncplane_move_top(struct ncplane* n);
|
||||||
API int ncplane_move_bottom(struct ncplane* n);
|
API int ncplane_move_bottom(struct ncplane* n);
|
||||||
|
|
||||||
// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'.
|
|
||||||
API int ncplane_move_below(struct ncplane* RESTRICT n, struct ncplane* RESTRICT below);
|
|
||||||
|
|
||||||
// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'.
|
// Splice ncplane 'n' out of the z-buffer, and reinsert it above 'above'.
|
||||||
API int ncplane_move_above(struct ncplane* RESTRICT n, struct ncplane* RESTRICT above);
|
API int ncplane_move_above_unsafe(struct ncplane* RESTRICT n,
|
||||||
|
struct ncplane* RESTRICT above);
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
ncplane_move_above(struct ncplane* n, struct ncplane* above){
|
||||||
|
if(n == above){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ncplane_move_above_unsafe(n, above);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Splice ncplane 'n' out of the z-buffer, and reinsert it below 'below'.
|
||||||
|
API int ncplane_move_below_unsafe(struct ncplane* RESTRICT n,
|
||||||
|
struct ncplane* RESTRICT below);
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
ncplane_move_below(struct ncplane* n, struct ncplane* below){
|
||||||
|
if(n == below){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ncplane_move_below_unsafe(n, below);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the plane above this one, or NULL if this is at the top.
|
||||||
|
API struct ncplane* ncplane_below(struct ncplane* n);
|
||||||
|
|
||||||
|
// Return the plane below this one, or NULL if this is at the bottom.
|
||||||
|
API struct ncplane* ncplane_below(struct ncplane* n);
|
||||||
|
|
||||||
// Retrieve the cell at the cursor location on the specified plane, returning
|
// Retrieve the cell at the cursor location on the specified plane, returning
|
||||||
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
|
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
|
||||||
API int ncplane_at_cursor(struct ncplane* n, cell* c);
|
API int ncplane_at_cursor(struct ncplane* n, cell* c);
|
||||||
|
|
||||||
|
// Retrieve the cell at the specified location on the specified plane, returning
|
||||||
|
// it in 'c'. This copy is safe to use until the ncplane is destroyed/erased.
|
||||||
|
API int ncplane_at_yx(struct ncplane* n, int y, int x, cell* c);
|
||||||
|
|
||||||
// Manipulate the opaque user pointer associated with this plane.
|
// Manipulate the opaque user pointer associated with this plane.
|
||||||
// ncplane_set_userptr() returns the previous userptr after replacing
|
// ncplane_set_userptr() returns the previous userptr after replacing
|
||||||
// it with 'opaque'. the others simply return the userptr.
|
// it with 'opaque'. the others simply return the userptr.
|
||||||
@ -1074,7 +1105,8 @@ API int ncplane_fadein(struct ncplane* n, const struct timespec* ts);
|
|||||||
// Working with cells
|
// Working with cells
|
||||||
|
|
||||||
#define CELL_TRIVIAL_INITIALIZER { .gcluster = '\0', .attrword = 0, .channels = 0, }
|
#define CELL_TRIVIAL_INITIALIZER { .gcluster = '\0', .attrword = 0, .channels = 0, }
|
||||||
#define CELL_SIMPLE_INITIALIZER(c) { .gcluster = c, .attrword = 0, .channels = 0, }
|
#define CELL_SIMPLE_INITIALIZER(c) { .gcluster = (c), .attrword = 0, .channels = 0, }
|
||||||
|
#define CELL_INITIALIZER(c, a, chan) { .gcluster = (c), .attrword = (a), .channels = (chan), }
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
cell_init(cell* c){
|
cell_init(cell* c){
|
||||||
@ -1174,6 +1206,17 @@ cell_simple_p(const cell* c){
|
|||||||
return c->gcluster < 0x80;
|
return c->gcluster < 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
cell_load_simple(struct ncplane* n, cell* c, char ch){
|
||||||
|
cell_release(n, c);
|
||||||
|
c->channels &= ~CELL_WIDEASIAN_MASK;
|
||||||
|
c->gcluster = ch;
|
||||||
|
if(cell_simple_p(c)){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// get the offset into the egcpool for this cell's EGC. returns meaningless and
|
// get the offset into the egcpool for this cell's EGC. returns meaningless and
|
||||||
// unsafe results if called on a simple cell.
|
// unsafe results if called on a simple cell.
|
||||||
static inline uint32_t
|
static inline uint32_t
|
||||||
@ -1231,12 +1274,9 @@ ncplane_rounded_box(struct ncplane* n, uint32_t attr, uint64_t channels,
|
|||||||
if((ret = cells_rounded_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
if((ret = cells_rounded_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
||||||
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
||||||
}
|
}
|
||||||
cell_release(n, &ul);
|
cell_release(n, &ul); cell_release(n, &ur);
|
||||||
cell_release(n, &ur);
|
cell_release(n, &ll); cell_release(n, &lr);
|
||||||
cell_release(n, &ll);
|
cell_release(n, &hl); cell_release(n, &vl);
|
||||||
cell_release(n, &lr);
|
|
||||||
cell_release(n, &hl);
|
|
||||||
cell_release(n, &vl);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1265,12 +1305,9 @@ ncplane_double_box(struct ncplane* n, uint32_t attr, uint64_t channels,
|
|||||||
if((ret = cells_double_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
if((ret = cells_double_box(n, attr, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
|
||||||
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ystop, xstop, ctlword);
|
||||||
}
|
}
|
||||||
cell_release(n, &ul);
|
cell_release(n, &ul); cell_release(n, &ur);
|
||||||
cell_release(n, &ur);
|
cell_release(n, &ll); cell_release(n, &lr);
|
||||||
cell_release(n, &ll);
|
cell_release(n, &hl); cell_release(n, &vl);
|
||||||
cell_release(n, &lr);
|
|
||||||
cell_release(n, &hl);
|
|
||||||
cell_release(n, &vl);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,28 +49,28 @@ int box_demo(struct notcurses* nc){
|
|||||||
if(ncplane_putstr_aligned(n, ytargbase++, "┗━━┻━━┛", NCALIGN_CENTER) < 0){
|
if(ncplane_putstr_aligned(n, ytargbase++, "┗━━┻━━┛", NCALIGN_CENTER) < 0){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ncplane_set_fg_rgb(n, 255, 255, 255);
|
|
||||||
ncplane_set_bg_rgb(n, 180, 40, 180);
|
|
||||||
do{
|
do{
|
||||||
int y = 0, x = 0;
|
int y = 0, x = 0;
|
||||||
ncplane_dim_yx(n, &ylen, &xlen);
|
ncplane_dim_yx(n, &ylen, &xlen);
|
||||||
while(ylen - y >= targy && xlen - x >= targx){
|
while(ylen - y >= targy && xlen - x >= targx){
|
||||||
cell_set_fg_rgb(&ul, 107 - (y * 2), zbonus, 107 + (y * 2));
|
cell_set_fg_rgb(&ul, 107 - (y * 2), zbonus, 107 + (y * 2));
|
||||||
cell_set_bg_rgb(&ul, zbonus, 20 + y, 20 + y);
|
cell_set_bg_rgb(&ul, 20, zbonus, 20);
|
||||||
cell_set_fg_rgb(&ur, 107 - (y * 2), zbonus, 107 + (y * 2));
|
cell_set_fg_rgb(&ur, 107 - (y * 2), zbonus, 107 + (y * 2));
|
||||||
cell_set_bg_rgb(&ur, zbonus, 20 + y, 20 + y);
|
cell_set_bg_rgb(&ur, 20, zbonus, 20);
|
||||||
cell_set_fg_rgb(&hl, 107 - (y * 2), zbonus, 107 + (y * 2));
|
cell_set_fg_rgb(&hl, 107 - (y * 2), zbonus, 107 + (y * 2));
|
||||||
cell_set_bg_rgb(&hl, 20, zbonus, 20);
|
cell_set_bg_rgb(&hl, 20, zbonus, 20);
|
||||||
cell_set_fg_rgb(&ll, 107 - (y * 2), zbonus, 107 + (y * 2));
|
cell_set_fg_rgb(&ll, 107 - (y * 2), zbonus, 107 + (y * 2));
|
||||||
cell_set_bg_rgb(&ll, zbonus, 20 + y, 20 + y);
|
cell_set_bg_rgb(&ll, 20, zbonus, 20);
|
||||||
cell_set_fg_rgb(&lr, 107 - (y * 2), zbonus, 107 + (y * 2));
|
cell_set_fg_rgb(&lr, 107 - (y * 2), zbonus, 107 + (y * 2));
|
||||||
cell_set_bg_rgb(&lr, zbonus, 20 + y, 20 + y);
|
cell_set_bg_rgb(&lr, 20, zbonus, 20);
|
||||||
cell_set_fg_rgb(&vl, 20, zbonus, 20);
|
cell_set_fg_rgb(&vl, 107 - (y * 2), zbonus, 107 + (y * 2));
|
||||||
cell_set_bg_rgb(&vl, 107 - (y * 2), zbonus, 107 + (y * 2));
|
cell_set_bg_rgb(&vl, 20, zbonus, 20);
|
||||||
if(ncplane_cursor_move_yx(n, y, x)){
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(ncplane_box_sized(n, &ul, &ur, &ll, &lr, &hl, &vl, ylen, xlen, 0)){
|
if(ncplane_box_sized(n, &ul, &ur, &ll, &lr, &hl, &vl, ylen, xlen,
|
||||||
|
NCBOXGRAD_LEFT | NCBOXGRAD_BOTTOM |
|
||||||
|
NCBOXGRAD_RIGHT | NCBOXGRAD_TOP)){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ylen -= 2;
|
ylen -= 2;
|
||||||
|
@ -52,7 +52,7 @@ usage(const char* exe, int status){
|
|||||||
fprintf(out, " s: run shuffle\n");
|
fprintf(out, " s: run shuffle\n");
|
||||||
fprintf(out, " u: run uniblock\n");
|
fprintf(out, " u: run uniblock\n");
|
||||||
fprintf(out, " v: run view\n");
|
fprintf(out, " v: run view\n");
|
||||||
fprintf(out, " w: run widecolors\n");
|
fprintf(out, " w: run widechomper\n");
|
||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,8 +63,29 @@ intro(struct notcurses* nc){
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ncplane_erase(ncp);
|
ncplane_erase(ncp);
|
||||||
|
if(ncplane_cursor_move_yx(ncp, 0, 0)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
int x, y, rows, cols;
|
int x, y, rows, cols;
|
||||||
ncplane_dim_yx(ncp, &rows, &cols);
|
ncplane_dim_yx(ncp, &rows, &cols);
|
||||||
|
cell ul = CELL_TRIVIAL_INITIALIZER, ur = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
cell ll = CELL_TRIVIAL_INITIALIZER, lr = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
cell hl = CELL_TRIVIAL_INITIALIZER, vl = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
if(cells_rounded_box(ncp, CELL_STYLE_BOLD, 0, &ul, &ur, &ll, &lr, &hl, &vl)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
channels_set_fg_rgb(&ul.channels, 0xff, 0, 0);
|
||||||
|
channels_set_fg_rgb(&ur.channels, 0, 0xff, 0);
|
||||||
|
channels_set_fg_rgb(&ll.channels, 0, 0, 0xff);
|
||||||
|
channels_set_fg_rgb(&lr.channels, 0xff, 0xff, 0xff);
|
||||||
|
if(ncplane_box_sized(ncp, &ul, &ur, &ll, &lr, &hl, &vl, rows, cols,
|
||||||
|
NCBOXGRAD_TOP | NCBOXGRAD_BOTTOM |
|
||||||
|
NCBOXGRAD_RIGHT | NCBOXGRAD_LEFT)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cell_release(ncp, &ul); cell_release(ncp, &ur);
|
||||||
|
cell_release(ncp, &ll); cell_release(ncp, &lr);
|
||||||
|
cell_release(ncp, &hl); cell_release(ncp, &vl);
|
||||||
cell c;
|
cell c;
|
||||||
cell_init(&c);
|
cell_init(&c);
|
||||||
const char* cstr = "Δ";
|
const char* cstr = "Δ";
|
||||||
@ -135,7 +156,7 @@ ext_demos(struct notcurses* nc, const char* demos){
|
|||||||
case 'g': ret = grid_demo(nc); break;
|
case 'g': ret = grid_demo(nc); break;
|
||||||
case 'l': ret = luigi_demo(nc); break;
|
case 'l': ret = luigi_demo(nc); break;
|
||||||
case 'v': ret = view_demo(nc); break;
|
case 'v': ret = view_demo(nc); break;
|
||||||
case 'w': ret = widecolor_demo(nc); break;
|
case 'w': ret = widechomper_demo(nc); break;
|
||||||
case 'p': ret = panelreel_demo(nc); break;
|
case 'p': ret = panelreel_demo(nc); break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Unknown demo specification: %c\n", *demos);
|
fprintf(stderr, "Unknown demo specification: %c\n", *demos);
|
||||||
|
@ -12,7 +12,7 @@ extern "C" {
|
|||||||
extern struct timespec demodelay;
|
extern struct timespec demodelay;
|
||||||
|
|
||||||
int unicodeblocks_demo(struct notcurses* nc);
|
int unicodeblocks_demo(struct notcurses* nc);
|
||||||
int widecolor_demo(struct notcurses* nc);
|
int widechomper_demo(struct notcurses* nc);
|
||||||
int box_demo(struct notcurses* nc);
|
int box_demo(struct notcurses* nc);
|
||||||
int maxcolor_demo(struct notcurses* nc);
|
int maxcolor_demo(struct notcurses* nc);
|
||||||
int grid_demo(struct notcurses* nc);
|
int grid_demo(struct notcurses* nc);
|
||||||
|
@ -102,6 +102,9 @@ gridswitch_demo(struct notcurses* nc, struct ncplane *n){
|
|||||||
// center
|
// center
|
||||||
for(y = 1 ; y < maxy - 1 ; ++y){
|
for(y = 1 ; y < maxy - 1 ; ++y){
|
||||||
x = 0;
|
x = 0;
|
||||||
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
cell_set_fg_rgb(&cl, 255 - rs * y, 255 - gs * (x + y), 255 - bs * x);
|
cell_set_fg_rgb(&cl, 255 - rs * y, 255 - gs * (x + y), 255 - bs * x);
|
||||||
cell_set_bg_rgb(&cl, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
cell_set_bg_rgb(&cl, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
||||||
ncplane_putc(n, &cl);
|
ncplane_putc(n, &cl);
|
||||||
@ -117,6 +120,9 @@ gridswitch_demo(struct notcurses* nc, struct ncplane *n){
|
|||||||
|
|
||||||
// bottom line
|
// bottom line
|
||||||
x = 0;
|
x = 0;
|
||||||
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
cell_set_fg_rgb(&ll, 255 - rs * y, 255 - gs * (x + y), 255 - bs * x);
|
cell_set_fg_rgb(&ll, 255 - rs * y, 255 - gs * (x + y), 255 - bs * x);
|
||||||
cell_set_bg_rgb(&ll, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
cell_set_bg_rgb(&ll, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
||||||
ncplane_putc(n, &ll);
|
ncplane_putc(n, &ll);
|
||||||
@ -167,6 +173,9 @@ gridinv_demo(struct notcurses* nc, struct ncplane *n){
|
|||||||
// center
|
// center
|
||||||
for(y = 1 ; y < maxy - 1 ; ++y){
|
for(y = 1 ; y < maxy - 1 ; ++y){
|
||||||
x = 0;
|
x = 0;
|
||||||
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
cell_set_fg_rgb(&cl, 0, 0, 0);
|
cell_set_fg_rgb(&cl, 0, 0, 0);
|
||||||
cell_set_bg_rgb(&cl, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
cell_set_bg_rgb(&cl, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
||||||
ncplane_putc(n, &cl);
|
ncplane_putc(n, &cl);
|
||||||
@ -182,6 +191,9 @@ gridinv_demo(struct notcurses* nc, struct ncplane *n){
|
|||||||
|
|
||||||
// bottom line
|
// bottom line
|
||||||
x = 0;
|
x = 0;
|
||||||
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
cell_set_fg_rgb(&ll, 0, 0, 0);
|
cell_set_fg_rgb(&ll, 0, 0, 0);
|
||||||
cell_set_bg_rgb(&ll, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
cell_set_bg_rgb(&ll, 255 - rs * x, 255 - gs * (x + y), 255 - bs * y);
|
||||||
ncplane_putc(n, &ll);
|
ncplane_putc(n, &ll);
|
||||||
@ -233,6 +245,9 @@ int grid_demo(struct notcurses* nc){
|
|||||||
// center
|
// center
|
||||||
for(y = 1 ; y < maxy - 1 ; ++y){
|
for(y = 1 ; y < maxy - 1 ; ++y){
|
||||||
x = 0;
|
x = 0;
|
||||||
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
cell_set_bg_rgb(&cl, y, y, y);
|
cell_set_bg_rgb(&cl, y, y, y);
|
||||||
cell_set_bg_rgb(&cc, y, y, y);
|
cell_set_bg_rgb(&cc, y, y, y);
|
||||||
cell_set_bg_rgb(&cr, y, y, y);
|
cell_set_bg_rgb(&cr, y, y, y);
|
||||||
@ -248,6 +263,9 @@ int grid_demo(struct notcurses* nc){
|
|||||||
|
|
||||||
// bottom line
|
// bottom line
|
||||||
x = 0;
|
x = 0;
|
||||||
|
if(ncplane_cursor_move_yx(n, y, x)){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
cell_set_bg_rgb(&ll, y, y, y);
|
cell_set_bg_rgb(&ll, y, y, y);
|
||||||
cell_set_bg_rgb(&lc, y, y, y);
|
cell_set_bg_rgb(&lc, y, y, y);
|
||||||
cell_set_bg_rgb(&lr, y, y, y);
|
cell_set_bg_rgb(&lr, y, y, y);
|
||||||
|
212
src/demo/luigi.c
212
src/demo/luigi.c
@ -5,104 +5,109 @@
|
|||||||
//2 = yellow
|
//2 = yellow
|
||||||
//3 = green
|
//3 = green
|
||||||
|
|
||||||
static const char luigi1[] = "0000000000000000"
|
static const char* luigis[] = {
|
||||||
"0000000000000000"
|
"0000000000000000"
|
||||||
"0000000111110000"
|
"0000000000000000"
|
||||||
"0000011111120000"
|
"0000000111110000"
|
||||||
"0000111111220000"
|
"0000011111120000"
|
||||||
"0000111111111110"
|
"0000111111220000"
|
||||||
"0000333223222000"
|
"0000111111111110"
|
||||||
"0003223223322220"
|
"0000333223222000"
|
||||||
"0003223322222222"
|
"0003223223322220"
|
||||||
"0033223322232222"
|
"0003223322222222"
|
||||||
"0033222223333330"
|
"0033223322232222"
|
||||||
"0003322222333330"
|
"0033222223333330"
|
||||||
"0000032222222200"
|
"0003322222333330"
|
||||||
"0000311122200000"
|
"0000032222222200"
|
||||||
"0003133313000000"
|
"0000311122200000"
|
||||||
"0003133331300000"
|
"0003133313000000"
|
||||||
"0033133333112200"
|
"0003133331300000"
|
||||||
"0031133333332222"
|
"0033133333112200"
|
||||||
"0031113333332222"
|
"0031133333332222"
|
||||||
"0001113333333222"
|
"0031113333332222"
|
||||||
"0001111333333222"
|
"0001113333333222"
|
||||||
"0001111113331000"
|
"0001111333333222"
|
||||||
"0001111111111000"
|
"0001111113331000"
|
||||||
"0001111111113000"
|
"0001111111111000"
|
||||||
"3333111111131100"
|
"0001111111113000"
|
||||||
"3333111113311100"
|
"3333111111131100"
|
||||||
"3333111131111000"
|
"3333111113311100"
|
||||||
"3333111001111000"
|
"3333111131111000"
|
||||||
"3333000003333000"
|
"3333111001111000"
|
||||||
"3300000003333000"
|
"3333000003333000"
|
||||||
"3000000003333330"
|
"3300000003333000"
|
||||||
"0000000003333330";
|
"3000000003333330"
|
||||||
|
"0000000003333330",
|
||||||
|
|
||||||
static const char luigi2[] = "0000000000000000"
|
"0000000000000000"
|
||||||
"0000001111100000"
|
"0000001111100000"
|
||||||
"0000111111200000"
|
"0000111111200000"
|
||||||
"0001111112200000"
|
"0001111112200000"
|
||||||
"0001111111111100"
|
"0001111111111100"
|
||||||
"0003332232220000"
|
"0003332232220000"
|
||||||
"0032232233222200"
|
"0032232233222200"
|
||||||
"0032233222222220"
|
"0032233222222220"
|
||||||
"0332233222322220"
|
"0332233222322220"
|
||||||
"0332222233333300"
|
"0332222233333300"
|
||||||
"0033222223333300"
|
"0033222223333300"
|
||||||
"0003322222222000"
|
"0003322222222000"
|
||||||
"0000111122000000"
|
"0000111122000000"
|
||||||
"0003133113300000"
|
"0003133113300000"
|
||||||
"0031333311300000"
|
"0031333311300000"
|
||||||
"0031333311330000"
|
"0031333311330000"
|
||||||
"0031333311130000"
|
"0031333311130000"
|
||||||
"0031333332230000"
|
"0031333332230000"
|
||||||
"0031333322220000"
|
"0031333322220000"
|
||||||
"0011133322221000"
|
"0011133322221000"
|
||||||
"0011133322221100"
|
"0011133322221100"
|
||||||
"0011113322211100"
|
"0011113322211100"
|
||||||
"0011111133111100"
|
"0011111133111100"
|
||||||
"0001111133311000"
|
"0001111133311000"
|
||||||
"0000111333333000"
|
"0000111333333000"
|
||||||
"0000113333330000"
|
"0000113333330000"
|
||||||
"0000011333300000"
|
"0000011333300000"
|
||||||
"0000031113330000"
|
"0000031113330000"
|
||||||
"0000033330330000"
|
"0000033330330000"
|
||||||
"0000333330000000"
|
"0000333330000000"
|
||||||
"0000333333300000"
|
"0000333333300000"
|
||||||
"0000003333300000";
|
"0000003333300000",
|
||||||
|
|
||||||
|
"0000001111100000"
|
||||||
|
"0000111111200000"
|
||||||
|
"0001111112200000"
|
||||||
|
"0001111111111100"
|
||||||
|
"0003332232220000"
|
||||||
|
"0032232233222200"
|
||||||
|
"0032233222222220"
|
||||||
|
"0332233222322220"
|
||||||
|
"0332222233333300"
|
||||||
|
"0333222223333300"
|
||||||
|
"0003322222222000"
|
||||||
|
"0000033322000000"
|
||||||
|
"0000111133100020"
|
||||||
|
"0003333113310222"
|
||||||
|
"0033333311313222"
|
||||||
|
"0333333311331222"
|
||||||
|
"0333333311331323"
|
||||||
|
"0333333111331330"
|
||||||
|
"3333331112132300"
|
||||||
|
"3333111111111000"
|
||||||
|
"2222211111111000"
|
||||||
|
"2222211111111003"
|
||||||
|
"2222111111111033"
|
||||||
|
"0222111111133333"
|
||||||
|
"0001311111133333"
|
||||||
|
"0031131111133333"
|
||||||
|
"3331113311133333"
|
||||||
|
"3333111100033333"
|
||||||
|
"3333310000000000"
|
||||||
|
"0333000000000000"
|
||||||
|
"0333000000000000"
|
||||||
|
"0033300000000000",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static const char luigi3[] = "0000001111100000"
|
|
||||||
"0000111111200000"
|
|
||||||
"0001111112200000"
|
|
||||||
"0001111111111100"
|
|
||||||
"0003332232220000"
|
|
||||||
"0032232233222200"
|
|
||||||
"0032233222222220"
|
|
||||||
"0332233222322220"
|
|
||||||
"0332222233333300"
|
|
||||||
"0333222223333300"
|
|
||||||
"0003322222222000"
|
|
||||||
"0000033322000000"
|
|
||||||
"0000111133100020"
|
|
||||||
"0003333113310222"
|
|
||||||
"0033333311313222"
|
|
||||||
"0333333311331222"
|
|
||||||
"0333333311331323"
|
|
||||||
"0333333111331330"
|
|
||||||
"3333331112132300"
|
|
||||||
"3333111111111000"
|
|
||||||
"2222211111111000"
|
|
||||||
"2222211111111003"
|
|
||||||
"2222111111111033"
|
|
||||||
"0222111111133333"
|
|
||||||
"0001311111133333"
|
|
||||||
"0031131111133333"
|
|
||||||
"3331113311133333"
|
|
||||||
"3333111100033333"
|
|
||||||
"3333310000000000"
|
|
||||||
"0333000000000000"
|
|
||||||
"0333000000000000"
|
|
||||||
"0033300000000000";
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
draw_luigi(struct ncplane* n, const char* sprite){
|
draw_luigi(struct ncplane* n, const char* sprite){
|
||||||
@ -159,26 +164,25 @@ int luigi_demo(struct notcurses* nc){
|
|||||||
int yoff = rows * 4 / 5 - height + 1; // tuned
|
int yoff = rows * 4 / 5 - height + 1; // tuned
|
||||||
struct ncplane* lns[3];
|
struct ncplane* lns[3];
|
||||||
int i;
|
int i;
|
||||||
|
struct ncplane* lastseen = NULL;
|
||||||
for(i = 0 ; i < 3 ; ++i){
|
for(i = 0 ; i < 3 ; ++i){
|
||||||
lns[i] = notcurses_newplane(nc, height, 16, yoff, -16, NULL);
|
lns[i] = notcurses_newplane(nc, height, 16, yoff, 0, NULL);
|
||||||
if(lns[i] == NULL){
|
if(lns[i] == NULL){
|
||||||
while(--i){
|
while(--i){
|
||||||
ncplane_destroy(lns[i]);
|
ncplane_destroy(lns[i]);
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
lastseen = lns[i];
|
||||||
|
draw_luigi(lns[i], luigis[i]);
|
||||||
|
ncplane_move_bottom(lastseen); // all start hidden underneath stdplane
|
||||||
}
|
}
|
||||||
draw_luigi(lns[0], luigi1);
|
|
||||||
draw_luigi(lns[1], luigi2);
|
|
||||||
draw_luigi(lns[2], luigi3);
|
|
||||||
struct ncplane* lastseen = NULL;
|
|
||||||
struct timespec stepdelay;
|
struct timespec stepdelay;
|
||||||
ns_to_timespec(timespec_to_ns(&demodelay) / (cols - 16 - 1), &stepdelay);
|
ns_to_timespec(timespec_to_ns(&demodelay) / (cols - 16 - 1), &stepdelay);
|
||||||
for(i = 0 ; i < cols - 16 - 1 ; ++i){
|
for(i = 0 ; i < cols - 16 - 1 ; ++i){
|
||||||
if(lastseen){ // hide the previous sprite
|
ncplane_move_bottom(lastseen); // hide the previous sprite
|
||||||
ncplane_move_yx(lastseen, yoff, -16);
|
|
||||||
}
|
|
||||||
lastseen = lns[i % 3];
|
lastseen = lns[i % 3];
|
||||||
|
ncplane_move_top(lastseen);
|
||||||
ncplane_move_yx(lastseen, yoff, i);
|
ncplane_move_yx(lastseen, yoff, i);
|
||||||
notcurses_render(nc);
|
notcurses_render(nc);
|
||||||
nanosleep(&stepdelay, NULL);
|
nanosleep(&stepdelay, NULL);
|
||||||
|
@ -22,7 +22,7 @@ view_video_demo(struct notcurses* nc){
|
|||||||
ncplane_dim_yx(ncp, &dimy, &dimx);
|
ncplane_dim_yx(ncp, &dimy, &dimx);
|
||||||
int averr;
|
int averr;
|
||||||
struct ncvisual* ncv;
|
struct ncvisual* ncv;
|
||||||
ncv = ncplane_visual_open(ncp, "../tests/bob.mkv", &averr);
|
ncv = ncplane_visual_open(ncp, "../tests/fm6.mkv", &averr);
|
||||||
if(!ncv){
|
if(!ncv){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "demo.h"
|
#include "demo.h"
|
||||||
|
|
||||||
|
// Fill up the screen with as much crazy Unicode as we can, and then set a
|
||||||
|
// gremlin loose, looking to eat up all the wide characters.
|
||||||
|
|
||||||
// FIXME throw this in there somehow
|
// FIXME throw this in there somehow
|
||||||
// ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫
|
// ∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫
|
||||||
// ⎪⎢⎜│a²+b³ ⎟⎥⎪
|
// ⎪⎢⎜│a²+b³ ⎟⎥⎪
|
||||||
@ -283,6 +286,7 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total,
|
|||||||
int bytes_out, int egs_out, int cols_out){
|
int bytes_out, int egs_out, int cols_out){
|
||||||
cell c = CELL_TRIVIAL_INITIALIZER;
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
cell_load(n, &c, " ");
|
cell_load(n, &c, " ");
|
||||||
|
cell_set_fg_alpha(&c, CELL_ALPHA_TRANS);
|
||||||
cell_set_bg_alpha(&c, CELL_ALPHA_TRANS);
|
cell_set_bg_alpha(&c, CELL_ALPHA_TRANS);
|
||||||
ncplane_set_default(n, &c);
|
ncplane_set_default(n, &c);
|
||||||
cell_release(n, &c);
|
cell_release(n, &c);
|
||||||
@ -297,7 +301,7 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total,
|
|||||||
}
|
}
|
||||||
// bottom handle
|
// bottom handle
|
||||||
ncplane_cursor_move_yx(n, 4, 17);
|
ncplane_cursor_move_yx(n, 4, 17);
|
||||||
ncplane_putegc(n, "┬", 0, 0, NULL);
|
ncplane_putegc(n, "┬", 0, channels, NULL);
|
||||||
ncplane_cursor_move_yx(n, 5, 17);
|
ncplane_cursor_move_yx(n, 5, 17);
|
||||||
ncplane_putegc(n, "│", 0, channels, NULL);
|
ncplane_putegc(n, "│", 0, channels, NULL);
|
||||||
ncplane_cursor_move_yx(n, 6, 17);
|
ncplane_cursor_move_yx(n, 6, 17);
|
||||||
@ -344,7 +348,7 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Much of this text comes from http://kermitproject.org/utf8.html
|
// Much of this text comes from http://kermitproject.org/utf8.html
|
||||||
int widecolor_demo(struct notcurses* nc){
|
int widechomper_demo(struct notcurses* nc){
|
||||||
static const char* strs[] = {
|
static const char* strs[] = {
|
||||||
"Война и мир",
|
"Война и мир",
|
||||||
"Бра́тья Карама́зовы",
|
"Бра́тья Карама́зовы",
|
||||||
@ -575,9 +579,8 @@ int widecolor_demo(struct notcurses* nc){
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
const char** s;
|
const char** s;
|
||||||
int count = notcurses_palette_size(nc);
|
const int steps[] = { 0x100, 0x100, 0x40000, 0x10001, };
|
||||||
const int steps[] = { 1, 0x100, 0x40000, 0x10001, };
|
const int starts[] = { 0x004000, 0x000040, 0x010101, 0x400040, };
|
||||||
const int starts[] = { 0x4000, 0x40, 0x10000, 0x400040, };
|
|
||||||
|
|
||||||
struct ncplane* n = notcurses_stdplane(nc);
|
struct ncplane* n = notcurses_stdplane(nc);
|
||||||
size_t i;
|
size_t i;
|
||||||
@ -588,13 +591,10 @@ int widecolor_demo(struct notcurses* nc){
|
|||||||
cell c;
|
cell c;
|
||||||
struct timespec screenend;
|
struct timespec screenend;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &screenend);
|
clock_gettime(CLOCK_MONOTONIC, &screenend);
|
||||||
ns_to_timespec(timespec_to_ns(&screenend) + timespec_to_ns(&demodelay), &screenend);
|
ns_to_timespec(timespec_to_ns(&screenend) + 2 * timespec_to_ns(&demodelay), &screenend);
|
||||||
do{ // (re)draw a screen
|
do{ // (re)draw a screen
|
||||||
const int start = starts[i];
|
const int start = starts[i];
|
||||||
int step = steps[i];
|
int step = steps[i];
|
||||||
const int rollover = 256 / ((step & 0xff) | ((step & 0xff00) >> 8u)
|
|
||||||
| ((step & 0xff0000) >> 16u));
|
|
||||||
int rollcount = 0; // number of times we've added this step
|
|
||||||
cell_init(&c);
|
cell_init(&c);
|
||||||
int y, x, maxy, maxx;
|
int y, x, maxy, maxx;
|
||||||
ncplane_dim_yx(n, &maxy, &maxx);
|
ncplane_dim_yx(n, &maxy, &maxx);
|
||||||
@ -634,11 +634,18 @@ int widecolor_demo(struct notcurses* nc){
|
|||||||
}
|
}
|
||||||
int ulen = 0;
|
int ulen = 0;
|
||||||
int r;
|
int r;
|
||||||
if((r = ncplane_putegc(n, &(*s)[idx], 0, channels, &ulen)) < 0){
|
if(wcwidth(wcs) <= maxx - x){
|
||||||
if(ulen < 0){
|
if((r = ncplane_putegc(n, &(*s)[idx], 0, channels, &ulen)) < 0){
|
||||||
|
if(ulen < 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
cell octo = CELL_INITIALIZER('#', 0, channels);
|
||||||
|
if((r = ncplane_putc(n, &octo)) < 1){
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
cell_release(n, &octo);
|
||||||
}
|
}
|
||||||
ncplane_cursor_yx(n, &y, &x);
|
ncplane_cursor_yx(n, &y, &x);
|
||||||
idx += ulen;
|
idx += ulen;
|
||||||
@ -646,19 +653,7 @@ int widecolor_demo(struct notcurses* nc){
|
|||||||
cols_out += r;
|
cols_out += r;
|
||||||
++egcs_out;
|
++egcs_out;
|
||||||
}
|
}
|
||||||
if(++rollcount % rollover == 0){
|
rgb += step;
|
||||||
step *= 256;
|
|
||||||
}
|
|
||||||
if((unsigned)step >= 1ul << 24){
|
|
||||||
step >>= 24u;
|
|
||||||
}
|
|
||||||
if(step == 0){
|
|
||||||
step = 1;
|
|
||||||
}
|
|
||||||
if((rgb += step) >= count){
|
|
||||||
rgb = 0;
|
|
||||||
step *= 256;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}while(y < maxy && x < maxx);
|
}while(y < maxy && x < maxx);
|
||||||
struct ncplane* mess = notcurses_newplane(nc, 7, 57, 1, 4, NULL);
|
struct ncplane* mess = notcurses_newplane(nc, 7, 57, 1, 4, NULL);
|
||||||
@ -697,7 +692,7 @@ int widecolor_demo(struct notcurses* nc){
|
|||||||
pthread_join(tid, NULL);
|
pthread_join(tid, NULL);
|
||||||
ncplane_destroy(mess);
|
ncplane_destroy(mess);
|
||||||
if(key == NCKEY_RESIZE){
|
if(key == NCKEY_RESIZE){
|
||||||
notcurses_resize(nc, NULL, NULL);
|
notcurses_resize(nc, &maxy, &maxx);
|
||||||
}
|
}
|
||||||
}while(key == NCKEY_RESIZE);
|
}while(key == NCKEY_RESIZE);
|
||||||
}
|
}
|
@ -84,11 +84,15 @@ typedef struct notcurses {
|
|||||||
FILE* ttyfp; // FILE* for controlling tty, from opts->ttyfp
|
FILE* ttyfp; // FILE* for controlling tty, from opts->ttyfp
|
||||||
FILE* ttyinfp; // FILE* for processing input
|
FILE* ttyinfp; // FILE* for processing input
|
||||||
unsigned char* damage; // damage map (row granularity)
|
unsigned char* damage; // damage map (row granularity)
|
||||||
|
char* mstream; // buffer for rendering memstream, see open_memstream(3)
|
||||||
|
FILE* mstreamfp;// FILE* for rendering memstream
|
||||||
|
size_t mstrsize;// size of rendering memstream
|
||||||
int colors; // number of colors usable for this screen
|
int colors; // number of colors usable for this screen
|
||||||
ncstats stats; // some statistics across the lifetime of the notcurses ctx
|
ncstats stats; // some statistics across the lifetime of the notcurses ctx
|
||||||
// We verify that some terminfo capabilities exist. These needn't be checked
|
// We verify that some terminfo capabilities exist. These needn't be checked
|
||||||
// before further use; just use tiparm() directly.
|
// before further use; just use tiparm() directly.
|
||||||
char* cup; // move cursor
|
char* cup; // move cursor
|
||||||
|
bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor
|
||||||
char* civis; // hide cursor
|
char* civis; // hide cursor
|
||||||
// These might be NULL, and we can more or less work without them. Check!
|
// These might be NULL, and we can more or less work without them. Check!
|
||||||
char* clearscr; // erase screen and home cursor
|
char* clearscr; // erase screen and home cursor
|
||||||
@ -112,9 +116,7 @@ typedef struct notcurses {
|
|||||||
char* italoff; // CELL_STYLE_ITALIC (disable)
|
char* italoff; // CELL_STYLE_ITALIC (disable)
|
||||||
char* smkx; // enter keypad transmit mode (keypad_xmit)
|
char* smkx; // enter keypad transmit mode (keypad_xmit)
|
||||||
char* rmkx; // leave keypad transmit mode (keypad_local)
|
char* rmkx; // leave keypad transmit mode (keypad_local)
|
||||||
|
|
||||||
struct termios tpreserved; // terminal state upon entry
|
struct termios tpreserved; // terminal state upon entry
|
||||||
bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor
|
|
||||||
bool CCCflag; // terminfo-reported "CCC" flag for palette set capability
|
bool CCCflag; // terminfo-reported "CCC" flag for palette set capability
|
||||||
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
|
||||||
@ -168,21 +170,26 @@ flash_damage_map(unsigned char* damage, int count, bool val){
|
|||||||
void ncplane_updamage(ncplane* n);
|
void ncplane_updamage(ncplane* n);
|
||||||
|
|
||||||
// For our first attempt, O(1) uniform conversion from 8-bit r/g/b down to
|
// For our first attempt, O(1) uniform conversion from 8-bit r/g/b down to
|
||||||
// ~2.4-bit 6x6x6 ANSI cube + greyscale (assumed on entry; I know no way to
|
// ~2.4-bit 6x6x6 cube + greyscale (assumed on entry; I know no way to
|
||||||
// even semi-portably recover the palette) proceeds via: map each 8-bit to
|
// even semi-portably recover the palette) proceeds via: map each 8-bit to
|
||||||
// a 5-bit target grey. if all 3 components match, select that grey.
|
// a 5-bit target grey. if all 3 components match, select that grey.
|
||||||
// otherwise, c / 42.7 to map to 6 values. this never generates pure black
|
// otherwise, c / 42.7 to map to 6 values. this never generates pure black
|
||||||
// nor white, though, lame...FIXME
|
// nor white, though, lame...FIXME
|
||||||
static inline int
|
static inline int
|
||||||
rgb_to_ansi256(unsigned r, unsigned g, unsigned b){
|
rgb_quantize_256(unsigned r, unsigned g, unsigned b){
|
||||||
const unsigned GREYMASK = 0xf8;
|
const unsigned GREYMASK = 0xf8;
|
||||||
r &= GREYMASK;
|
// if all 5 MSBs match, return grey from 24-member grey ramp or pure
|
||||||
g &= GREYMASK;
|
// black/white from original 16 (0 and 15, respectively)
|
||||||
b &= GREYMASK;
|
if((r & GREYMASK) == (g & GREYMASK) && (g & GREYMASK) == (b & GREYMASK)){
|
||||||
if(r == g && g == b){ // 5 MSBs match, return grey
|
// 256 / 26 == 9.846
|
||||||
r >>= 3u;
|
int gidx = r * 5 / 49 - 1;
|
||||||
r += 232;
|
if(gidx < 0){
|
||||||
return r > 255 ? 255: r;
|
return 0;
|
||||||
|
}
|
||||||
|
if(gidx >= 24){
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
return 232 + gidx;
|
||||||
}
|
}
|
||||||
r /= 43;
|
r /= 43;
|
||||||
g /= 43;
|
g /= 43;
|
||||||
@ -190,6 +197,31 @@ rgb_to_ansi256(unsigned r, unsigned g, unsigned b){
|
|||||||
return r * 36 + g * 6 + b + 16;
|
return r * 36 + g * 6 + b + 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
term_emit(const char* name __attribute__ ((unused)), const char* seq,
|
||||||
|
FILE* out, bool flush){
|
||||||
|
int ret = fprintf(out, "%s", seq);
|
||||||
|
if(ret < 0){
|
||||||
|
// fprintf(stderr, "Error emitting %zub %s escape (%s)\n", strlen(seq), name, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if((size_t)ret != strlen(seq)){
|
||||||
|
// fprintf(stderr, "Short write (%db) for %zub %s sequence\n", ret, strlen(seq), name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(flush && fflush(out)){
|
||||||
|
// fprintf(stderr, "Error flushing after %db %s sequence (%s)\n", ret, name, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char*
|
||||||
|
extended_gcluster(const ncplane* n, const cell* c){
|
||||||
|
uint32_t idx = cell_egc_idx(c);
|
||||||
|
return n->pool.pool + idx;
|
||||||
|
}
|
||||||
|
|
||||||
#define NANOSECS_IN_SEC 1000000000
|
#define NANOSECS_IN_SEC 1000000000
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
File diff suppressed because it is too large
Load Diff
532
src/lib/render.c
Normal file
532
src/lib/render.c
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include "internal.h"
|
||||||
|
|
||||||
|
static inline uint64_t
|
||||||
|
timespec_to_ns(const struct timespec* t){
|
||||||
|
return t->tv_sec * NANOSECS_IN_SEC + t->tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mutex_unlock(void* vlock){
|
||||||
|
pthread_mutex_unlock(vlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
blocking_write(int fd, const char* buf, size_t buflen){
|
||||||
|
size_t written = 0;
|
||||||
|
do{
|
||||||
|
ssize_t w = write(fd, buf + written, buflen - written);
|
||||||
|
if(w < 0){
|
||||||
|
if(errno != EAGAIN && errno != EWOULDBLOCK){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
written += w;
|
||||||
|
}
|
||||||
|
if(written < buflen){
|
||||||
|
struct pollfd pfd = {
|
||||||
|
.fd = fd,
|
||||||
|
.events = POLLOUT,
|
||||||
|
.revents = 0,
|
||||||
|
};
|
||||||
|
poll(&pfd, 1, -1);
|
||||||
|
}
|
||||||
|
}while(written < buflen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int notcurses_refresh(notcurses* nc){
|
||||||
|
int ret = 0;
|
||||||
|
pthread_mutex_lock(&nc->lock);
|
||||||
|
pthread_cleanup_push(mutex_unlock, &nc->lock);
|
||||||
|
if(nc->mstream == NULL){
|
||||||
|
ret = -1; // haven't rendered yet, and thus don't know what should be there
|
||||||
|
}else if(blocking_write(nc->ttyfd, nc->mstream, nc->mstrsize)){
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
pthread_cleanup_pop(1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_render_stats(const struct timespec* time1, const struct timespec* time0,
|
||||||
|
ncstats* stats, int bytes){
|
||||||
|
int64_t elapsed = timespec_to_ns(time1) - timespec_to_ns(time0);
|
||||||
|
//fprintf(stderr, "Rendering took %ld.%03lds\n", elapsed / NANOSECS_IN_SEC,
|
||||||
|
// (elapsed % NANOSECS_IN_SEC) / 1000000);
|
||||||
|
if(bytes >= 0){
|
||||||
|
stats->render_bytes += bytes;
|
||||||
|
if(bytes > stats->render_max_bytes){
|
||||||
|
stats->render_max_bytes = bytes;
|
||||||
|
}
|
||||||
|
if(bytes < stats->render_min_bytes){
|
||||||
|
stats->render_min_bytes = bytes;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
++stats->failed_renders;
|
||||||
|
}
|
||||||
|
if(elapsed > 0){ // don't count clearly incorrect information, egads
|
||||||
|
++stats->renders;
|
||||||
|
stats->render_ns += elapsed;
|
||||||
|
if(elapsed > stats->render_max_ns){
|
||||||
|
stats->render_max_ns = elapsed;
|
||||||
|
}
|
||||||
|
if(elapsed < stats->render_min_ns){
|
||||||
|
stats->render_min_ns = elapsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine the best palette for the current frame, and write the necessary
|
||||||
|
// escape sequences to 'out'. for now, we just assume the ANSI palettes. at
|
||||||
|
// 256 colors, this is the 16 normal ones, 6x6x6 color cubes, and 32 greys.
|
||||||
|
// it's probably better to sample the darker regions rather than cover so much
|
||||||
|
// chroma, but whatever....FIXME
|
||||||
|
static int
|
||||||
|
prep_optimized_palette(notcurses* nc, FILE* out __attribute__ ((unused))){
|
||||||
|
if(nc->RGBflag){
|
||||||
|
return 0; // DirectColor, no need to write palette
|
||||||
|
}
|
||||||
|
if(!nc->CCCflag){
|
||||||
|
return 0; // can't change palette
|
||||||
|
}
|
||||||
|
// FIXME
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// reshape the shadow framebuffer to match the stdplane's dimensions, throwing
|
||||||
|
// away the old one.
|
||||||
|
static int
|
||||||
|
reshape_shadow_fb(notcurses* nc){
|
||||||
|
const size_t size = sizeof(nc->shadowbuf) * nc->stdscr->leny * nc->stdscr->lenx;
|
||||||
|
cell* fb = malloc(size);
|
||||||
|
if(fb == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
free(nc->shadowbuf);
|
||||||
|
nc->shadowbuf = fb;
|
||||||
|
nc->shadowy = nc->stdscr->leny;
|
||||||
|
nc->shadowx = nc->stdscr->lenx;
|
||||||
|
memset(fb, 0, size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Find the topmost cell for this coordinate by walking down the z-buffer,
|
||||||
|
// looking for an intersecting ncplane. Once we've found one, check it for
|
||||||
|
// transparency in either the back- or foreground. If the alpha channel is
|
||||||
|
// active, keep descending and blending until we hit opacity, or bedrock. We
|
||||||
|
// recurse to find opacity, and blend the result into what we have. The
|
||||||
|
// 'findfore' and 'findback' bools control our recursion--there's no point in
|
||||||
|
// going further down when a color is locked in, so don't (for instance) recurse
|
||||||
|
// further when we have a transparent foreground and opaque background atop an
|
||||||
|
// opaque foreground and transparent background. The cell we ultimately return
|
||||||
|
// (a const ref to 'c') is backed by '*retp' via rawdog copy; the caller must
|
||||||
|
// not call cell_release() upon it, nor use it beyond the scope of the render.
|
||||||
|
//
|
||||||
|
// So, as we go down, we find planes which can have impact on the result. Once
|
||||||
|
// we've locked the result in (base case), write the deep values we have to 'c'.
|
||||||
|
// Then, as we come back up, blend them as appropriate. The actual glyph is
|
||||||
|
// whichever one occurs at the top with a non-transparent α (α < 3). To effect
|
||||||
|
// tail recursion, though, we instead write first, and then recurse, blending
|
||||||
|
// as we descend. α <= 0 is opaque. α >= 3 is fully transparent.
|
||||||
|
static ncplane*
|
||||||
|
dig_visible_cell(cell* c, int y, int x, ncplane* p, int falpha, int balpha,
|
||||||
|
bool* damage){
|
||||||
|
while(p){
|
||||||
|
// where in the plane this coordinate would be, based off absy/absx. the
|
||||||
|
// true origin is 0,0, so abs=2,2 means coordinate 3,3 would be 1,1, while
|
||||||
|
// abs=-2,-2 would make coordinate 3,3 relative 5, 5.
|
||||||
|
int poffx, poffy;
|
||||||
|
poffy = y - p->absy;
|
||||||
|
poffx = x - p->absx;
|
||||||
|
if(poffy < p->leny && poffy >= 0){
|
||||||
|
if(poffx < p->lenx && poffx >= 0){ // p is valid for this y, x
|
||||||
|
const cell* vis = &p->fb[fbcellidx(p, poffy, poffx)];
|
||||||
|
// if we never loaded any content into the cell (or obliterated it by
|
||||||
|
// writing in a zero), use the plane's background cell.
|
||||||
|
if(vis->gcluster == 0){
|
||||||
|
vis = &p->defcell;
|
||||||
|
}
|
||||||
|
bool lockedglyph = false;
|
||||||
|
int nalpha;
|
||||||
|
if(falpha > 0 && (nalpha = cell_get_fg_alpha(vis)) < CELL_ALPHA_TRANS){
|
||||||
|
if(c->gcluster == 0){ // never write fully trans glyphs, never replace
|
||||||
|
if( (c->gcluster = vis->gcluster) ){ // index copy only
|
||||||
|
lockedglyph = true; // must return this ncplane for this glyph
|
||||||
|
c->attrword = vis->attrword;
|
||||||
|
cell_set_fchannel(c, cell_get_fchannel(vis)); // FIXME blend it in
|
||||||
|
falpha -= (CELL_ALPHA_TRANS - nalpha); // FIXME blend it in
|
||||||
|
if(p->damage[poffy]){
|
||||||
|
*damage = true;
|
||||||
|
p->damage[poffy] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(balpha > 0 && (nalpha = cell_get_bg_alpha(vis)) < CELL_ALPHA_TRANS){
|
||||||
|
cell_set_bchannel(c, cell_get_bchannel(vis)); // FIXME blend it in
|
||||||
|
balpha -= (CELL_ALPHA_TRANS - nalpha);
|
||||||
|
if(p->damage[poffy]){
|
||||||
|
*damage = true;
|
||||||
|
p->damage[poffy] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if((falpha > 0 || balpha > 0) && p->z){ // we must go further!
|
||||||
|
ncplane* cand = dig_visible_cell(c, y, x, p->z, falpha, balpha, damage);
|
||||||
|
if(!lockedglyph && cand){
|
||||||
|
p = cand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = p->z;
|
||||||
|
}
|
||||||
|
// should never happen for valid y, x thanks to the stdplane. you fucked up!
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ncplane*
|
||||||
|
visible_cell(cell* c, int y, int x, ncplane* n, bool* damage){
|
||||||
|
cell_init(c);
|
||||||
|
return dig_visible_cell(c, y, x, n, CELL_ALPHA_TRANS, CELL_ALPHA_TRANS, damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the cell's UTF-8 grapheme cluster to the provided FILE*. returns the
|
||||||
|
// number of columns occupied by this EGC (only an approximation; it's actually
|
||||||
|
// a property of the font being used).
|
||||||
|
static int
|
||||||
|
term_putc(FILE* out, const ncplane* n, const cell* c){
|
||||||
|
if(cell_simple_p(c)){
|
||||||
|
if(c->gcluster == 0 || iscntrl(c->gcluster)){
|
||||||
|
// fprintf(stderr, "[ ]\n");
|
||||||
|
if(fputc_unlocked(' ', out) == EOF){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
// fprintf(stderr, "[%c]\n", c->gcluster);
|
||||||
|
if(fputc_unlocked(c->gcluster, out) == EOF){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
const char* ext = extended_gcluster(n, c);
|
||||||
|
// fprintf(stderr, "[%s]\n", ext);
|
||||||
|
if(fputs_unlocked(ext, out) < 0){ // FIXME check for short write?
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the current and target style bitmasks against the specified 'stylebit'.
|
||||||
|
// if they are different, and we have the necessary capability, write the
|
||||||
|
// applicable terminfo entry to 'out'. returns -1 only on a true error.
|
||||||
|
static int
|
||||||
|
term_setstyle(FILE* out, unsigned cur, unsigned targ, unsigned stylebit,
|
||||||
|
const char* ton, const char* toff){
|
||||||
|
int ret = 0;
|
||||||
|
unsigned curon = cur & stylebit;
|
||||||
|
unsigned targon = targ & stylebit;
|
||||||
|
if(curon != targon){
|
||||||
|
if(targon){
|
||||||
|
if(ton){
|
||||||
|
ret = term_emit("ton", ton, out, false);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(toff){ // how did this happen? we can turn it on, but not off?
|
||||||
|
ret = term_emit("toff", toff, out, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(ret < 0){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write any escape sequences necessary to set the desired style
|
||||||
|
static int
|
||||||
|
term_setstyles(const notcurses* nc, FILE* out, uint32_t* curattr, const cell* c,
|
||||||
|
bool* normalized){
|
||||||
|
*normalized = false;
|
||||||
|
uint32_t cellattr = cell_styles(c);
|
||||||
|
if(cellattr == *curattr){
|
||||||
|
return 0; // happy agreement, change nothing
|
||||||
|
}
|
||||||
|
int ret = 0;
|
||||||
|
// if only italics changed, don't emit any sgr escapes. xor of current and
|
||||||
|
// target ought have all 0s in the lower 8 bits if only italics changed.
|
||||||
|
if((cellattr ^ *curattr) & 0x00ff0000ul){
|
||||||
|
*normalized = true; // FIXME this is pretty conservative
|
||||||
|
// if everything's 0, emit the shorter sgr0
|
||||||
|
if(nc->sgr0 && ((cellattr & CELL_STYLE_MASK) == 0)){
|
||||||
|
if(term_emit("sgr0", nc->sgr0, out, false) < 0){
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}else if(term_emit("sgr", tiparm(nc->sgr, cellattr & CELL_STYLE_STANDOUT,
|
||||||
|
cellattr & CELL_STYLE_UNDERLINE,
|
||||||
|
cellattr & CELL_STYLE_REVERSE,
|
||||||
|
cellattr & CELL_STYLE_BLINK,
|
||||||
|
cellattr & CELL_STYLE_DIM,
|
||||||
|
cellattr & CELL_STYLE_BOLD,
|
||||||
|
cellattr & CELL_STYLE_INVIS,
|
||||||
|
cellattr & CELL_STYLE_PROTECT, 0),
|
||||||
|
out, false) < 0){
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sgr will blow away italics if they were set beforehand
|
||||||
|
ret |= term_setstyle(out, *curattr, cellattr, CELL_STYLE_ITALIC, nc->italics, nc->italoff);
|
||||||
|
*curattr = cellattr;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 for foreground, 4 for background, ugh FIXME
|
||||||
|
static int
|
||||||
|
term_esc_rgb(notcurses* nc __attribute__ ((unused)), FILE* out, int esc,
|
||||||
|
unsigned r, unsigned g, unsigned b){
|
||||||
|
// The correct way to do this is using tiparm+tputs, but doing so (at least
|
||||||
|
// as of terminfo 6.1.20191019) both emits ~3% more bytes for a run of 'rgb'
|
||||||
|
// and gives rise to some corrupted cells (possibly due to special handling of
|
||||||
|
// values < 256; I'm not at this time sure). So we just cons up our own.
|
||||||
|
/*if(esc == 4){
|
||||||
|
return term_emit("setab", tiparm(nc->setab, (int)((r << 16u) | (g << 8u) | b)), out, false);
|
||||||
|
}else if(esc == 3){
|
||||||
|
return term_emit("setaf", tiparm(nc->setaf, (int)((r << 16u) | (g << 8u) | b)), out, false);
|
||||||
|
}else{
|
||||||
|
return -1;
|
||||||
|
}*/
|
||||||
|
#define RGBESC1 "\x1b" "["
|
||||||
|
#define RGBESC2 "8;2;"
|
||||||
|
// rrr;ggg;bbbm
|
||||||
|
char rgbesc[] = RGBESC1 " " RGBESC2 " ";
|
||||||
|
int len = strlen(RGBESC1);
|
||||||
|
rgbesc[len++] = esc;
|
||||||
|
len += strlen(RGBESC2);
|
||||||
|
if(r > 99){
|
||||||
|
rgbesc[len++] = r / 100 + '0';
|
||||||
|
}
|
||||||
|
if(r > 9){
|
||||||
|
rgbesc[len++] = (r % 100) / 10 + '0';
|
||||||
|
}
|
||||||
|
rgbesc[len++] = (r % 10) + '0';
|
||||||
|
rgbesc[len++] = ';';
|
||||||
|
if(g > 99){ rgbesc[len++] = g / 100 + '0'; }
|
||||||
|
if(g > 9){ rgbesc[len++] = (g % 100) / 10 + '0'; }
|
||||||
|
rgbesc[len++] = g % 10 + '0';
|
||||||
|
rgbesc[len++] = ';';
|
||||||
|
if(b > 99){ rgbesc[len++] = b / 100 + '0'; }
|
||||||
|
if(b > 9){ rgbesc[len++] = (b % 100) / 10 + '0'; }
|
||||||
|
rgbesc[len++] = b % 10 + '0';
|
||||||
|
rgbesc[len++] = 'm';
|
||||||
|
rgbesc[len] = '\0';
|
||||||
|
int w;
|
||||||
|
if((w = fputs_unlocked(rgbesc, out)) < len){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
term_bg_rgb8(notcurses* nc, FILE* out, unsigned r, unsigned g, unsigned b){
|
||||||
|
// We typically want to use tputs() and tiperm() to acquire and write the
|
||||||
|
// escapes, as these take into account terminal-specific delays, padding,
|
||||||
|
// etc. For the case of DirectColor, there is no suitable terminfo entry, but
|
||||||
|
// we're also in that case working with hopefully more robust terminals.
|
||||||
|
// If it doesn't work, eh, it doesn't work. Fuck the world; save yourself.
|
||||||
|
if(nc->RGBflag){
|
||||||
|
return term_esc_rgb(nc, out, '4', r, g, b);
|
||||||
|
}else{
|
||||||
|
if(nc->setab == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// For 256-color indexed mode, start constructing a palette based off
|
||||||
|
// the inputs *if we can change the palette*. If more than 256 are used on
|
||||||
|
// a single screen, start... combining close ones? For 8-color mode, simple
|
||||||
|
// interpolation. I have no idea what to do for 88 colors. FIXME
|
||||||
|
if(nc->colors >= 256){
|
||||||
|
term_emit("setab", tiparm(nc->setab, rgb_quantize_256(r, g, b)), out, false);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
term_fg_rgb8(notcurses* nc, FILE* out, unsigned r, unsigned g, unsigned b){
|
||||||
|
// We typically want to use tputs() and tiperm() to acquire and write the
|
||||||
|
// escapes, as these take into account terminal-specific delays, padding,
|
||||||
|
// etc. For the case of DirectColor, there is no suitable terminfo entry, but
|
||||||
|
// we're also in that case working with hopefully more robust terminals.
|
||||||
|
// If it doesn't work, eh, it doesn't work. Fuck the world; save yourself.
|
||||||
|
if(nc->RGBflag){
|
||||||
|
return term_esc_rgb(nc, out, '3', r, g, b);
|
||||||
|
}else{
|
||||||
|
if(nc->setaf == NULL){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(nc->colors >= 256){
|
||||||
|
term_emit("setaf", tiparm(nc->setaf, rgb_quantize_256(r, g, b)), out, false);
|
||||||
|
}
|
||||||
|
// For 256-color indexed mode, start constructing a palette based off
|
||||||
|
// the inputs *if we can change the palette*. If more than 256 are used on
|
||||||
|
// a single screen, start... combining close ones? For 8-color mode, simple
|
||||||
|
// interpolation. I have no idea what to do for 88 colors. FIXME
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
notcurses_render_internal(notcurses* nc){
|
||||||
|
int ret = 0;
|
||||||
|
int y, x;
|
||||||
|
FILE* out = nc->mstreamfp;
|
||||||
|
fseeko(out, 0, SEEK_SET);
|
||||||
|
// don't write a clearscreen. we only update things that have been changed.
|
||||||
|
// we explicitly move the cursor at the beginning of each output line, so no
|
||||||
|
// need to home it expliticly.
|
||||||
|
prep_optimized_palette(nc, out); // FIXME do what on failure?
|
||||||
|
uint32_t curattr = 0; // current attributes set (does not include colors)
|
||||||
|
// FIXME as of at least gcc 9.2.1, we get a false -Wmaybe-uninitialized below
|
||||||
|
// when using these without explicit initializations. for the life of me, i
|
||||||
|
// can't see any such path, and valgrind is cool with it, so what ya gonna do?
|
||||||
|
unsigned lastr = 0, lastg = 0, lastb = 0;
|
||||||
|
unsigned lastbr = 0, lastbg = 0, lastbb = 0;
|
||||||
|
// we can elide a color escape iff the color has not changed between the two
|
||||||
|
// cells and the current cell uses no defaults, or if both the current and
|
||||||
|
// the last used both defaults.
|
||||||
|
bool fgelidable = false, bgelidable = false, defaultelidable = false;
|
||||||
|
/*if(nc->stdscr->leny != nc->shadowy || nc->stdscr->lenx != nc->shadowx){
|
||||||
|
reshape_shadow_fb(nc);
|
||||||
|
}*/
|
||||||
|
for(y = 0 ; y < nc->stdscr->leny ; ++y){
|
||||||
|
bool linedamaged = false; // have we repositioned the cursor to start line?
|
||||||
|
bool newdamage = nc->damage[y];
|
||||||
|
// fprintf(stderr, "nc->damage[%d] (%p) = %u\n", y, nc->damage + y, nc->damage[y]);
|
||||||
|
if(newdamage){
|
||||||
|
nc->damage[y] = 0;
|
||||||
|
}
|
||||||
|
// move to the beginning of the line, in case our accounting was befouled
|
||||||
|
// by wider- (or narrower-) than-reported characters
|
||||||
|
for(x = 0 ; x < nc->stdscr->lenx ; ++x){
|
||||||
|
unsigned r, g, b, br, bg, bb;
|
||||||
|
ncplane* p;
|
||||||
|
cell c; // no need to initialize
|
||||||
|
p = visible_cell(&c, y, x, nc->top, &newdamage);
|
||||||
|
assert(p);
|
||||||
|
// don't try to print a wide character on the last column; it'll instead
|
||||||
|
// be printed on the next line. they probably shouldn't be admitted, but
|
||||||
|
// we can end up with one due to a resize.
|
||||||
|
if((x + 1 >= nc->stdscr->lenx && cell_double_wide_p(&c))){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!linedamaged){
|
||||||
|
if(newdamage){
|
||||||
|
term_emit("cup", tiparm(nc->cup, y, x), out, false);
|
||||||
|
nc->stats.cellelisions += x;
|
||||||
|
nc->stats.cellemissions += (nc->stdscr->lenx - x);
|
||||||
|
linedamaged = true;
|
||||||
|
}else{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set the style. this can change the color back to the default; if it
|
||||||
|
// does, we need update our elision possibilities.
|
||||||
|
bool normalized;
|
||||||
|
term_setstyles(nc, out, &curattr, &c, &normalized);
|
||||||
|
if(normalized){
|
||||||
|
defaultelidable = true;
|
||||||
|
bgelidable = false;
|
||||||
|
fgelidable = false;
|
||||||
|
}
|
||||||
|
// we allow these to be set distinctly, but terminfo only supports using
|
||||||
|
// them both via the 'op' capability. unless we want to generate the 'op'
|
||||||
|
// escapes ourselves, if either is set to default, we first send op, and
|
||||||
|
// then a turnon for whichever aren't default.
|
||||||
|
|
||||||
|
// we can elide the default set iff the previous used both defaults
|
||||||
|
if(cell_fg_default_p(&c) || cell_bg_default_p(&c)){
|
||||||
|
if(!defaultelidable){
|
||||||
|
++nc->stats.defaultemissions;
|
||||||
|
term_emit("op", nc->op, out, false);
|
||||||
|
}else{
|
||||||
|
++nc->stats.defaultelisions;
|
||||||
|
}
|
||||||
|
// if either is not default, this will get turned off
|
||||||
|
defaultelidable = true;
|
||||||
|
fgelidable = false;
|
||||||
|
bgelidable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can elide the foreground set iff the previous used fg and matched
|
||||||
|
if(!cell_fg_default_p(&c)){
|
||||||
|
cell_get_fg_rgb(&c, &r, &g, &b);
|
||||||
|
if(fgelidable && lastr == r && lastg == g && lastb == b){
|
||||||
|
++nc->stats.fgelisions;
|
||||||
|
}else{
|
||||||
|
term_fg_rgb8(nc, out, r, g, b);
|
||||||
|
++nc->stats.fgemissions;
|
||||||
|
fgelidable = true;
|
||||||
|
}
|
||||||
|
lastr = r; lastg = g; lastb = b;
|
||||||
|
defaultelidable = false;
|
||||||
|
}
|
||||||
|
if(!cell_bg_default_p(&c)){
|
||||||
|
cell_get_bg_rgb(&c, &br, &bg, &bb);
|
||||||
|
if(bgelidable && lastbr == br && lastbg == bg && lastbb == bb){
|
||||||
|
++nc->stats.bgelisions;
|
||||||
|
}else{
|
||||||
|
term_bg_rgb8(nc, out, br, bg, bb);
|
||||||
|
++nc->stats.bgemissions;
|
||||||
|
bgelidable = true;
|
||||||
|
}
|
||||||
|
lastbr = br; lastbg = bg; lastbb = bb;
|
||||||
|
defaultelidable = false;
|
||||||
|
}
|
||||||
|
// fprintf(stderr, "[%02d/%02d] 0x%02x 0x%02x 0x%02x %p\n", y, x, r, g, b, p);
|
||||||
|
term_putc(out, p, &c);
|
||||||
|
if(cell_double_wide_p(&c)){
|
||||||
|
++x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(linedamaged == false){
|
||||||
|
nc->stats.cellelisions += x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret |= fflush(out);
|
||||||
|
fflush(nc->ttyfp);
|
||||||
|
if(blocking_write(nc->ttyfd, nc->mstream, nc->mstrsize)){
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
/*fprintf(stderr, "%lu/%lu %lu/%lu %lu/%lu\n", defaultelisions, defaultemissions,
|
||||||
|
fgelisions, fgemissions, bgelisions, bgemissions);*/
|
||||||
|
if(nc->renderfp){
|
||||||
|
fprintf(nc->renderfp, "%s\n", nc->mstream);
|
||||||
|
}
|
||||||
|
return nc->mstrsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int notcurses_render(notcurses* nc){
|
||||||
|
int ret = 0;
|
||||||
|
struct timespec start, done;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
||||||
|
pthread_mutex_lock(&nc->lock);
|
||||||
|
pthread_cleanup_push(mutex_unlock, &nc->lock);
|
||||||
|
int bytes = notcurses_render_internal(nc);
|
||||||
|
int dimy, dimx;
|
||||||
|
notcurses_resize(nc, &dimy, &dimx);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC_RAW, &done);
|
||||||
|
update_render_stats(&done, &start, &nc->stats, bytes);
|
||||||
|
if(bytes < 0){
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
pthread_cleanup_pop(1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
135
src/poc/sprite-enhalfen.c
Normal file
135
src/poc/sprite-enhalfen.c
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char* luigis[] = {
|
||||||
|
"0000000000000000"
|
||||||
|
"0000000000000000"
|
||||||
|
"0000000111110000"
|
||||||
|
"0000011111120000"
|
||||||
|
"0000111111220000"
|
||||||
|
"0000111111111110"
|
||||||
|
"0000333223222000"
|
||||||
|
"0003223223322220"
|
||||||
|
"0003223322222222"
|
||||||
|
"0033223322232222"
|
||||||
|
"0033222223333330"
|
||||||
|
"0003322222333330"
|
||||||
|
"0000032222222200"
|
||||||
|
"0000311122200000"
|
||||||
|
"0003133313000000"
|
||||||
|
"0003133331300000"
|
||||||
|
"0033133333112200"
|
||||||
|
"0031133333332222"
|
||||||
|
"0031113333332222"
|
||||||
|
"0001113333333222"
|
||||||
|
"0001111333333222"
|
||||||
|
"0001111113331000"
|
||||||
|
"0001111111111000"
|
||||||
|
"0001111111113000"
|
||||||
|
"3333111111131100"
|
||||||
|
"3333111113311100"
|
||||||
|
"3333111131111000"
|
||||||
|
"3333111001111000"
|
||||||
|
"3333000003333000"
|
||||||
|
"3300000003333000"
|
||||||
|
"3000000003333330"
|
||||||
|
"0000000003333330",
|
||||||
|
|
||||||
|
"0000000000000000"
|
||||||
|
"0000001111100000"
|
||||||
|
"0000111111200000"
|
||||||
|
"0001111112200000"
|
||||||
|
"0001111111111100"
|
||||||
|
"0003332232220000"
|
||||||
|
"0032232233222200"
|
||||||
|
"0032233222222220"
|
||||||
|
"0332233222322220"
|
||||||
|
"0332222233333300"
|
||||||
|
"0033222223333300"
|
||||||
|
"0003322222222000"
|
||||||
|
"0000111122000000"
|
||||||
|
"0003133113300000"
|
||||||
|
"0031333311300000"
|
||||||
|
"0031333311330000"
|
||||||
|
"0031333311130000"
|
||||||
|
"0031333332230000"
|
||||||
|
"0031333322220000"
|
||||||
|
"0011133322221000"
|
||||||
|
"0011133322221100"
|
||||||
|
"0011113322211100"
|
||||||
|
"0011111133111100"
|
||||||
|
"0001111133311000"
|
||||||
|
"0000111333333000"
|
||||||
|
"0000113333330000"
|
||||||
|
"0000011333300000"
|
||||||
|
"0000031113330000"
|
||||||
|
"0000033330330000"
|
||||||
|
"0000333330000000"
|
||||||
|
"0000333333300000"
|
||||||
|
"0000003333300000",
|
||||||
|
|
||||||
|
"0000001111100000"
|
||||||
|
"0000111111200000"
|
||||||
|
"0001111112200000"
|
||||||
|
"0001111111111100"
|
||||||
|
"0003332232220000"
|
||||||
|
"0032232233222200"
|
||||||
|
"0032233222222220"
|
||||||
|
"0332233222322220"
|
||||||
|
"0332222233333300"
|
||||||
|
"0333222223333300"
|
||||||
|
"0003322222222000"
|
||||||
|
"0000033322000000"
|
||||||
|
"0000111133100020"
|
||||||
|
"0003333113310222"
|
||||||
|
"0033333311313222"
|
||||||
|
"0333333311331222"
|
||||||
|
"0333333311331323"
|
||||||
|
"0333333111331330"
|
||||||
|
"3333331112132300"
|
||||||
|
"3333111111111000"
|
||||||
|
"2222211111111000"
|
||||||
|
"2222211111111003"
|
||||||
|
"2222111111111033"
|
||||||
|
"0222111111133333"
|
||||||
|
"0001311111133333"
|
||||||
|
"0031131111133333"
|
||||||
|
"3331113311133333"
|
||||||
|
"3333111100033333"
|
||||||
|
"3333310000000000"
|
||||||
|
"0333000000000000"
|
||||||
|
"0333000000000000"
|
||||||
|
"0033300000000000",
|
||||||
|
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
enhalfen(const char* s, int width){
|
||||||
|
if(strlen(s) % (width * 2)){
|
||||||
|
fprintf(stderr, "Bad length: %zu\n", strlen(s));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
size_t rowoff = 0;
|
||||||
|
size_t idx;
|
||||||
|
for(rowoff = 0 ; s[rowoff] ; rowoff += 2 * width){
|
||||||
|
for(idx = 0 ; idx < width ; ++idx){
|
||||||
|
int c = (s[rowoff + idx] - '0') * 4 + (s[rowoff + width + idx] - '0');
|
||||||
|
printf("%x", c);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void){
|
||||||
|
const char** sprite;
|
||||||
|
for(sprite = luigis ; *sprite ; ++sprite){
|
||||||
|
if(enhalfen(*sprite, 16)){
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
66
src/poc/unidamage.cpp
Normal file
66
src/poc/unidamage.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <clocale>
|
||||||
|
#include <cassert>
|
||||||
|
#include <notcurses.h>
|
||||||
|
|
||||||
|
// What happens when we print over half of a wide glyph?
|
||||||
|
|
||||||
|
int main(int argc, char** argv){
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
notcurses_options opts{};
|
||||||
|
opts.inhibit_alternate_screen = true;
|
||||||
|
struct notcurses* nc;
|
||||||
|
if((nc = notcurses_init(&opts, stdout)) == nullptr){
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
struct ncplane* n = notcurses_stdplane(nc);
|
||||||
|
int dimx, dimy;
|
||||||
|
ncplane_dim_yx(n, &dimy, &dimx);
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
cell_set_bg_rgb(&c, 0, 0x80, 0);
|
||||||
|
//ncplane_set_default(n, &c);
|
||||||
|
if(cell_load(n, &c, "🐳") < 0){
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if(dimy > 5){
|
||||||
|
dimy = 5;
|
||||||
|
}
|
||||||
|
for(int i = 0 ; i < dimy ; ++i){
|
||||||
|
for(int j = 8 ; j < dimx / 2 ; ++j){ // leave some empty spaces
|
||||||
|
if(ncplane_putc_yx(n, i, j * 2, &c) < 0){
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ncplane_putc_yx(n, dimy, dimx - 3, &c);
|
||||||
|
ncplane_putc_yx(n, dimy, dimx - 1, &c);
|
||||||
|
ncplane_putc_yx(n, dimy + 1, dimx - 2, &c);
|
||||||
|
ncplane_putc_yx(n, dimy + 1, dimx - 4, &c);
|
||||||
|
cell_release(n, &c);
|
||||||
|
// put these on the right side of the wide glyphs
|
||||||
|
for(int i = 0 ; i < dimy / 2 ; ++i){
|
||||||
|
for(int j = 5 ; j < dimx / 2 ; j += 2){
|
||||||
|
if(ncplane_putsimple_yx(n, i, j, (j % 10) + '0') < 0){
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// put these on the left side of the wide glyphs
|
||||||
|
for(int i = dimy / 2 ; i < dimy ; ++i){
|
||||||
|
for(int j = 4 ; j < dimx / 2 ; j += 2){
|
||||||
|
if(ncplane_putsimple_yx(n, i, j, (j % 10) + '0') < 0){
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(notcurses_render(nc)){
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
return notcurses_stop(nc) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
|
||||||
|
err:
|
||||||
|
notcurses_stop(nc);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
BIN
tests/bob.mkv
BIN
tests/bob.mkv
Binary file not shown.
BIN
tests/fm6.mkv
Normal file
BIN
tests/fm6.mkv
Normal file
Binary file not shown.
@ -8,17 +8,74 @@ class InternalsTest : public :: testing::Test {
|
|||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(InternalsTest, RGBtoANSIWhite) {
|
TEST_F(InternalsTest, RGBtoANSIWhite) {
|
||||||
unsigned r, g, b;
|
unsigned r, g, b;
|
||||||
r = g = b = 0xff;
|
for(r = 250 ; r < 256 ; ++r){
|
||||||
EXPECT_EQ(255, rgb_to_ansi256(r, g, b));
|
g = b = r;
|
||||||
|
EXPECT_EQ(15, rgb_quantize_256(r, g, b));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(InternalsTest, RGBtoANSIBlack) {
|
TEST_F(InternalsTest, RGBtoANSIBlack) {
|
||||||
unsigned r, g, b;
|
unsigned r, g, b;
|
||||||
r = g = b = 0x0;
|
for(r = 0 ; r < 10 ; ++r){
|
||||||
EXPECT_EQ(232, rgb_to_ansi256(r, g, b));
|
g = b = r;
|
||||||
|
EXPECT_EQ(0, rgb_quantize_256(r, g, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InternalsTest, RGBtoANSIGrey) {
|
||||||
|
unsigned r, g, b;
|
||||||
|
for(r = 10 ; r < 244 ; ++r){
|
||||||
|
g = b = r;
|
||||||
|
EXPECT_EQ(231 + (r * 5) / 49, rgb_quantize_256(r, g, b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pure reds are either 0 (black), or 16 plus 36 * [0..5].
|
||||||
|
TEST_F(InternalsTest, RGBtoANSIRed) {
|
||||||
|
unsigned r, g, b;
|
||||||
|
g = b = 0x0;
|
||||||
|
for(r = 0 ; r < 256 ; ++r){
|
||||||
|
int c256 = rgb_quantize_256(r, g, b);
|
||||||
|
if(r < 8){
|
||||||
|
EXPECT_EQ(0, c256);
|
||||||
|
}else{
|
||||||
|
EXPECT_LT(15, c256);
|
||||||
|
EXPECT_EQ(16, c256 % 36);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pure greens are either 0 (black), or 16 plus 6 * [0..5].
|
||||||
|
TEST_F(InternalsTest, RGBtoANSIGreen) {
|
||||||
|
unsigned r, g, b;
|
||||||
|
r = b = 0x0;
|
||||||
|
for(g = 0 ; g < 256 ; ++g){
|
||||||
|
int c256 = rgb_quantize_256(r, g, b);
|
||||||
|
EXPECT_GT(48, c256);
|
||||||
|
if(g < 8){
|
||||||
|
EXPECT_EQ(0, c256);
|
||||||
|
}else{
|
||||||
|
EXPECT_LT(15, c256);
|
||||||
|
EXPECT_EQ(4, c256 % 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pure blues are either 0 (black), or one of the first 6 colors [16..22].
|
||||||
|
TEST_F(InternalsTest, RGBtoANSIBlue) {
|
||||||
|
unsigned r, g, b;
|
||||||
|
r = g = 0x0;
|
||||||
|
for(b = 0 ; b < 256 ; ++b){
|
||||||
|
int c256 = rgb_quantize_256(r, g, b);
|
||||||
|
EXPECT_GT(22, c256);
|
||||||
|
if(b < 8){
|
||||||
|
EXPECT_EQ(0, c256);
|
||||||
|
}else{
|
||||||
|
EXPECT_LT(15, c256);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ TEST_F(LibavTest, LoadVideo) {
|
|||||||
int averr;
|
int averr;
|
||||||
int dimy, dimx;
|
int dimy, dimx;
|
||||||
ncplane_dim_yx(ncp_, &dimy, &dimx);
|
ncplane_dim_yx(ncp_, &dimy, &dimx);
|
||||||
auto ncv = ncplane_visual_open(ncp_, "../tests/bob.mkv", &averr);
|
auto ncv = ncplane_visual_open(ncp_, "../tests/fm6.mkv", &averr);
|
||||||
ASSERT_NE(nullptr, ncv);
|
ASSERT_NE(nullptr, ncv);
|
||||||
EXPECT_EQ(0, averr);
|
EXPECT_EQ(0, averr);
|
||||||
auto frame = ncvisual_decode(ncv, &averr);
|
auto frame = ncvisual_decode(ncv, &averr);
|
||||||
@ -74,7 +74,7 @@ TEST_F(LibavTest, LoadVideoCreatePlane) {
|
|||||||
int averr;
|
int averr;
|
||||||
int dimy, dimx;
|
int dimy, dimx;
|
||||||
ncplane_dim_yx(ncp_, &dimy, &dimx);
|
ncplane_dim_yx(ncp_, &dimy, &dimx);
|
||||||
auto ncv = ncvisual_open_plane(nc_, "../tests/bob.mkv", &averr, 0, 0, false);
|
auto ncv = ncvisual_open_plane(nc_, "../tests/fm6.mkv", &averr, 0, 0, false);
|
||||||
ASSERT_NE(nullptr, ncv);
|
ASSERT_NE(nullptr, ncv);
|
||||||
EXPECT_EQ(0, averr);
|
EXPECT_EQ(0, averr);
|
||||||
auto frame = ncvisual_decode(ncv, &averr);
|
auto frame = ncvisual_decode(ncv, &averr);
|
||||||
|
@ -762,3 +762,116 @@ TEST_F(NcplaneTest, MoveToLowerRight) {
|
|||||||
EXPECT_EQ(0, ncplane_destroy(ncp));
|
EXPECT_EQ(0, ncplane_destroy(ncp));
|
||||||
// FIXME verify with ncplane_at_cursor()
|
// FIXME verify with ncplane_at_cursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Placing a wide char to the immediate left of any other char ought obliterate
|
||||||
|
// that cell.
|
||||||
|
TEST_F(NcplaneTest, WideCharAnnihilatesRight) {
|
||||||
|
const wchar_t* w = L"🐸";
|
||||||
|
const wchar_t* wbashed = L"🦂";
|
||||||
|
const char bashed = 'X';
|
||||||
|
int sbytes = 0;
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 1, wbashed, 0, 0, &sbytes));
|
||||||
|
EXPECT_LT(0, ncplane_putsimple_yx(n_, 1, 1, bashed));
|
||||||
|
int x, y;
|
||||||
|
ncplane_cursor_yx(n_, &y, &x);
|
||||||
|
EXPECT_EQ(1, y);
|
||||||
|
EXPECT_EQ(2, x);
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 0, w, 0, 0, &sbytes));
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 1, 0, w, 0, 0, &sbytes));
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
ncplane_at_yx(n_, 0, 0, &c);
|
||||||
|
const char* wres = extended_gcluster(n_, &c);
|
||||||
|
EXPECT_EQ(0, strcmp(wres, "🐸")); // should be frog
|
||||||
|
ncplane_at_yx(n_, 0, 1, &c);
|
||||||
|
EXPECT_TRUE(cell_double_wide_p(&c)); // should be wide
|
||||||
|
ncplane_at_yx(n_, 0, 2, &c);
|
||||||
|
EXPECT_EQ(0, c.gcluster); // should be nothing
|
||||||
|
ncplane_at_yx(n_, 1, 0, &c);
|
||||||
|
wres = extended_gcluster(n_, &c);
|
||||||
|
EXPECT_EQ(0, strcmp(wres, "🐸")); // should be frog
|
||||||
|
ncplane_at_yx(n_, 1, 1, &c);
|
||||||
|
EXPECT_TRUE(cell_double_wide_p(&c)); //should be wide
|
||||||
|
ncplane_at_yx(n_, 0, 2, &c);
|
||||||
|
EXPECT_EQ(0, c.gcluster);
|
||||||
|
EXPECT_EQ(0, notcurses_render(nc_)); // should be nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placing a wide char on the right half of a wide char ought obliterate the
|
||||||
|
// original wide char.
|
||||||
|
TEST_F(NcplaneTest, WideCharAnnihilatesWideLeft) {
|
||||||
|
const wchar_t* w = L"🐍";
|
||||||
|
const wchar_t* wbashed = L"🦂";
|
||||||
|
int sbytes = 0;
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 0, wbashed, 0, 0, &sbytes));
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 1, w, 0, 0, &sbytes));
|
||||||
|
int x, y;
|
||||||
|
ncplane_cursor_yx(n_, &y, &x);
|
||||||
|
EXPECT_EQ(0, y);
|
||||||
|
EXPECT_EQ(3, x);
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
ncplane_at_yx(n_, 0, 0, &c);
|
||||||
|
EXPECT_EQ(0, c.gcluster); // should be nothing
|
||||||
|
ncplane_at_yx(n_, 0, 1, &c);
|
||||||
|
const char* wres = extended_gcluster(n_, &c);
|
||||||
|
EXPECT_EQ(0, strcmp(wres, "🐍")); // should be snake
|
||||||
|
ncplane_at_yx(n_, 0, 2, &c);
|
||||||
|
EXPECT_TRUE(cell_double_wide_p(&c)); // should be wide
|
||||||
|
EXPECT_EQ(0, notcurses_render(nc_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placing a normal char on either half of a wide char ought obliterate
|
||||||
|
// the original wide char.
|
||||||
|
TEST_F(NcplaneTest, WideCharsAnnihilated) {
|
||||||
|
const char cc = 'X';
|
||||||
|
const wchar_t* wbashedl = L"🐍";
|
||||||
|
const wchar_t* wbashedr = L"🦂";
|
||||||
|
int sbytes = 0;
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 0, wbashedl, 0, 0, &sbytes));
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 2, wbashedr, 0, 0, &sbytes));
|
||||||
|
EXPECT_EQ(1, ncplane_putsimple_yx(n_, 0, 1, cc));
|
||||||
|
EXPECT_EQ(1, ncplane_putsimple_yx(n_, 0, 2, cc));
|
||||||
|
int x, y;
|
||||||
|
ncplane_cursor_yx(n_, &y, &x);
|
||||||
|
EXPECT_EQ(0, y);
|
||||||
|
EXPECT_EQ(3, x);
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
ncplane_at_yx(n_, 0, 0, &c);
|
||||||
|
EXPECT_EQ(0, c.gcluster); // should be nothing
|
||||||
|
ncplane_at_yx(n_, 0, 1, &c);
|
||||||
|
EXPECT_EQ(cc, c.gcluster); // should be 'X'
|
||||||
|
ncplane_at_yx(n_, 0, 2, &c);
|
||||||
|
EXPECT_EQ(cc, c.gcluster); // should be 'X"
|
||||||
|
ncplane_at_yx(n_, 0, 3, &c);
|
||||||
|
EXPECT_EQ(0, c.gcluster); // should be nothing
|
||||||
|
EXPECT_EQ(0, notcurses_render(nc_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// But placing something to the immediate right of any glyph, that is not a
|
||||||
|
// problem. Ensure it is so.
|
||||||
|
TEST_F(NcplaneTest, AdjacentCharsSafe) {
|
||||||
|
const char cc = 'X';
|
||||||
|
const wchar_t* wsafel = L"🐍";
|
||||||
|
const wchar_t* wsafer = L"🦂";
|
||||||
|
int sbytes = 0;
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 0, wsafel, 0, 0, &sbytes));
|
||||||
|
EXPECT_LT(0, ncplane_putwegc_yx(n_, 0, 3, wsafer, 0, 0, &sbytes));
|
||||||
|
EXPECT_EQ(1, ncplane_putsimple_yx(n_, 0, 2, cc));
|
||||||
|
int x, y;
|
||||||
|
ncplane_cursor_yx(n_, &y, &x);
|
||||||
|
EXPECT_EQ(0, y);
|
||||||
|
EXPECT_EQ(3, x);
|
||||||
|
cell c = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
ncplane_at_yx(n_, 0, 0, &c);
|
||||||
|
const char* wres = extended_gcluster(n_, &c);
|
||||||
|
EXPECT_EQ(0, strcmp(wres, "🐍")); // should be snake
|
||||||
|
ncplane_at_yx(n_, 0, 1, &c);
|
||||||
|
EXPECT_TRUE(cell_double_wide_p(&c)); // should be snake
|
||||||
|
ncplane_at_yx(n_, 0, 2, &c);
|
||||||
|
EXPECT_EQ(cc, c.gcluster); // should be 'X'
|
||||||
|
ncplane_at_yx(n_, 0, 3, &c);
|
||||||
|
wres = extended_gcluster(n_, &c);
|
||||||
|
EXPECT_EQ(0, strcmp(wres, "🦂")); // should be scorpion
|
||||||
|
ncplane_at_yx(n_, 0, 4, &c);
|
||||||
|
EXPECT_TRUE(cell_double_wide_p(&c)); // should be scorpion
|
||||||
|
EXPECT_EQ(0, notcurses_render(nc_));
|
||||||
|
}
|
||||||
|
136
tests/zaxis.cpp
Normal file
136
tests/zaxis.cpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include "main.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class ZAxisTest : public :: testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
if(getenv("TERM") == nullptr){
|
||||||
|
GTEST_SKIP();
|
||||||
|
}
|
||||||
|
notcurses_options nopts{};
|
||||||
|
nopts.inhibit_alternate_screen = true;
|
||||||
|
outfp_ = fopen("/dev/tty", "wb");
|
||||||
|
ASSERT_NE(nullptr, outfp_);
|
||||||
|
nc_ = notcurses_init(&nopts, outfp_);
|
||||||
|
ASSERT_NE(nullptr, nc_);
|
||||||
|
n_ = notcurses_stdplane(nc_);
|
||||||
|
ASSERT_NE(nullptr, n_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
if(nc_){
|
||||||
|
EXPECT_EQ(0, notcurses_stop(nc_));
|
||||||
|
}
|
||||||
|
if(outfp_){
|
||||||
|
fclose(outfp_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct notcurses* nc_{};
|
||||||
|
struct ncplane* n_{};
|
||||||
|
FILE* outfp_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ZAxisTest, StdPlaneOnly) {
|
||||||
|
struct ncplane* top = notcurses_top(nc_);
|
||||||
|
EXPECT_EQ(n_, top);
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(top));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you want to move the plane which is already top+bottom to either, go ahead
|
||||||
|
TEST_F(ZAxisTest, StdPlaneOnanism) {
|
||||||
|
EXPECT_EQ(0, ncplane_move_top(n_));
|
||||||
|
struct ncplane* top = notcurses_top(nc_);
|
||||||
|
EXPECT_EQ(n_, top);
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(top));
|
||||||
|
EXPECT_EQ(0, ncplane_move_bottom(n_));
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(n_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// you can't place a plane above or below itself, stdplane or otherwise
|
||||||
|
TEST_F(ZAxisTest, NoMoveSelf) {
|
||||||
|
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
|
||||||
|
ASSERT_NE(nullptr, np);
|
||||||
|
EXPECT_NE(0, ncplane_move_below(n_, n_));
|
||||||
|
EXPECT_NE(0, ncplane_move_above(n_, n_));
|
||||||
|
EXPECT_NE(0, ncplane_move_below(np, np));
|
||||||
|
EXPECT_NE(0, ncplane_move_above(np, np));
|
||||||
|
}
|
||||||
|
|
||||||
|
// new planes ought be on the top
|
||||||
|
TEST_F(ZAxisTest, NewPlaneOnTop) {
|
||||||
|
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
|
||||||
|
ASSERT_NE(nullptr, np);
|
||||||
|
struct ncplane* top = notcurses_top(nc_);
|
||||||
|
EXPECT_EQ(np, top);
|
||||||
|
EXPECT_EQ(n_, ncplane_below(top));
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(n_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// "move" top plane to top. everything ought remain the same.
|
||||||
|
TEST_F(ZAxisTest, TopToTop) {
|
||||||
|
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
|
||||||
|
ASSERT_NE(nullptr, np);
|
||||||
|
struct ncplane* top = notcurses_top(nc_);
|
||||||
|
EXPECT_EQ(np, top);
|
||||||
|
EXPECT_EQ(n_, ncplane_below(top));
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(n_));
|
||||||
|
EXPECT_EQ(0, ncplane_move_top(np));
|
||||||
|
// verify it
|
||||||
|
top = notcurses_top(nc_);
|
||||||
|
EXPECT_EQ(np, top);
|
||||||
|
EXPECT_EQ(n_, ncplane_below(top));
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(n_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// move top plane to bottom, and verify enumeration
|
||||||
|
TEST_F(ZAxisTest, TopToBottom) {
|
||||||
|
struct ncplane* np = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
|
||||||
|
ASSERT_NE(nullptr, np);
|
||||||
|
struct ncplane* top = notcurses_top(nc_);
|
||||||
|
EXPECT_EQ(np, top);
|
||||||
|
EXPECT_EQ(n_, ncplane_below(top));
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(n_));
|
||||||
|
EXPECT_EQ(0, ncplane_move_bottom(np));
|
||||||
|
top = notcurses_top(nc_);
|
||||||
|
EXPECT_EQ(n_, top);
|
||||||
|
EXPECT_EQ(np, ncplane_below(top));
|
||||||
|
EXPECT_EQ(nullptr, ncplane_below(np));
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that moving one above another, with no other changes, is reflected at
|
||||||
|
// render time (requires explicit damage maintenance from move functionality).
|
||||||
|
TEST_F(ZAxisTest, ZAxisDamage) {
|
||||||
|
cell cat = CELL_TRIVIAL_INITIALIZER;
|
||||||
|
cell c = CELL_SIMPLE_INITIALIZER('x');
|
||||||
|
ASSERT_EQ(0, cell_set_fg_rgb(&c, 0xff, 0, 0));
|
||||||
|
ASSERT_EQ(1, ncplane_putc(n_, &c));
|
||||||
|
EXPECT_EQ(0, notcurses_render(nc_));
|
||||||
|
ASSERT_EQ(0, ncplane_cursor_move_yx(n_, 0, 0));
|
||||||
|
ASSERT_EQ(1, ncplane_at_cursor(n_, &cat));
|
||||||
|
ASSERT_TRUE(cell_simple_p(&cat));
|
||||||
|
ASSERT_EQ('x', cat.gcluster);
|
||||||
|
struct ncplane* n2 = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
|
||||||
|
ASSERT_EQ(1, cell_load(n2, &c, "y"));
|
||||||
|
ASSERT_EQ(0, cell_set_fg_rgb(&c, 0, 0xff, 0));
|
||||||
|
ASSERT_EQ(1, ncplane_putc(n2, &c));
|
||||||
|
EXPECT_EQ(0, notcurses_render(nc_));
|
||||||
|
ASSERT_EQ(0, ncplane_cursor_move_yx(n2, 0, 0));
|
||||||
|
ASSERT_EQ(1, ncplane_at_cursor(n2, &cat));
|
||||||
|
ASSERT_EQ('y', cat.gcluster);
|
||||||
|
struct ncplane* n3 = notcurses_newplane(nc_, 2, 2, 0, 0, nullptr);
|
||||||
|
ASSERT_EQ(1, cell_load(n3, &c, "z"));
|
||||||
|
ASSERT_EQ(0, cell_set_fg_rgb(&c, 0, 0, 0xff));
|
||||||
|
ASSERT_EQ(1, ncplane_putc(n3, &c));
|
||||||
|
EXPECT_EQ(0, notcurses_render(nc_));
|
||||||
|
ASSERT_EQ(0, ncplane_cursor_move_yx(n3, 0, 0));
|
||||||
|
ASSERT_EQ(1, ncplane_at_cursor(n3, &cat));
|
||||||
|
ASSERT_EQ('z', cat.gcluster);
|
||||||
|
// FIXME testing damage requires notcurses keeping a copy of the screen....
|
||||||
|
// FIXME move y atop z
|
||||||
|
// FIXME inspect
|
||||||
|
// FIXME move z atop y
|
||||||
|
// FIXME inspect
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user