Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
internal.h
Go to the documentation of this file.
1#ifndef NOTCURSES_INTERNAL
2#define NOTCURSES_INTERNAL
3
4#ifdef __cplusplus
5extern "C" {
6#endif
7
8#include "version.h"
9#include "builddef.h"
10#include "compat/compat.h"
11#include "notcurses/ncport.h"
12#include "notcurses/notcurses.h"
13#include "notcurses/direct.h"
14
15#ifndef __MINGW32__
16#define API __attribute__((visibility("default")))
17#else
18#define API __declspec(dllexport)
19#endif
20#define ALLOC __attribute__((malloc)) __attribute__((warn_unused_result))
21
22// KEY_EVENT is defined by both ncurses.h (prior to 6.3) and wincon.h. since we
23// don't use either definition, kill it before inclusion of ncurses.h.
24#undef KEY_EVENT
25#include <term.h>
26#include <time.h>
27#include <term.h>
28#include <stdio.h>
29#include <stdint.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <string.h>
34#include <signal.h>
35#include <wctype.h>
36#include <pthread.h>
37#include <stdbool.h>
38#ifndef __STDC_FORMAT_MACROS
39#define __STDC_FORMAT_MACROS
40#endif
41#include <inttypes.h>
42#include <unictype.h>
43#ifndef __MINGW32__
44#include <langinfo.h>
45#endif
46#include "lib/termdesc.h"
47#include "lib/egcpool.h"
48#include "lib/sprite.h"
49#include "lib/fbuf.h"
50#include "lib/gpm.h"
51
52struct sixelmap;
53struct ncvisual_details;
54
55// Was this glyph drawn as part of an ncvisual? If so, we need to honor
56// blitter stacking rather than the standard trichannel solver.
57#define NC_BLITTERSTACK_MASK NC_NOBACKGROUND_MASK
58
59// we can't define multipart ncvisual here, because OIIO requires C++ syntax,
60// and we can't go throwing C++ syntax into this header. so it goes.
61
62// A plane is memory for some rectilinear virtual window, plus current cursor
63// state for that window, and part of a pile. Each pile has a total order along
64// its z-axis. Functions update these virtual planes over a series of API
65// calls. Eventually, notcurses_render() is called. We then do a depth buffer
66// blit of updated cells. A cell is updated if the topmost plane including that
67// cell updates it, not simply if any plane updates it.
68//
69// A plane may be partially or wholly offscreen--this might occur if the
70// screen is resized, for example. Offscreen portions will not be rendered.
71// Accesses beyond the borders of a panel, however, are errors.
72//
73// The framebuffer 'fb' is a set of rows. For scrolling, we interpret it as a
74// circular buffer of rows. 'logrow' is the index of the row at the logical top
75// of the plane. It only changes from 0 if the plane is scrollable.
76typedef struct ncplane {
77 nccell* fb; // "framebuffer" of character cells
78 int logrow; // logical top row, starts at 0, add one for each scroll
79 unsigned x, y; // current cursor location within this plane
80 // ncplane_yx() etc. use coordinates relative to the plane to which this
81 // plane is bound, but absx/absy are always relative to the terminal origin.
82 // they must thus be translated by any function which moves a parent plane.
83 int absx, absy; // origin of the plane relative to the pile's origin
84 // also used as left and top margin on resize by
85 // ncplane_resize_marginalized()
86 unsigned lenx, leny; // size of the plane, [0..len{x,y}) is addressable
87 egcpool pool; // attached storage pool for UTF-8 EGCs
88 uint64_t channels; // works the same way as cells
89
90 // a notcurses context is made up of piles, each rooted by one or more root
91 // planes. each pile has its own (totally ordered) z-axis.
92 struct ncpile* pile; // pile of which we are a part
93 struct ncplane* above; // plane above us, NULL if we're on top
94 struct ncplane* below; // plane below us, NULL if we're on bottom
95
96 // every plane is bound to some other plane, unless it is a root plane of a
97 // pile. a pile has a set of one or more root planes, all of them siblings.
98 // root planes are bound to themselves. the standard plane is always a root
99 // plane (since it cannot be reparented). a path exists to a root plane.
100 struct ncplane* bnext; // next in the blist
101 struct ncplane** bprev; // blist link back to us
102 struct ncplane* blist; // head of list of bound planes
103 struct ncplane* boundto;// plane to which we are bound (ourself for roots)
104
105 sprixel* sprite; // pointer into the sprixel cache
106 tament* tam; // transparency-annihilation sprite matrix
107
108 void* userptr; // slot for the user to stick some opaque pointer
109 int (*resizecb)(struct ncplane*); // callback after parent is resized
110 nccell basecell; // cell written anywhere that fb[i].gcluster == 0
111 char* name; // used only for debugging
112 ncalign_e halign; // relative to parent plane, for automatic realignment
113 ncalign_e valign; // relative to parent plane, for automatic realignment
114 uint16_t stylemask; // same deal as in a cell
115 int margin_b, margin_r;// bottom and right margins, stored for resize
116 bool scrolling; // is scrolling enabled? always disabled by default
117 bool fixedbound; // are we fixed relative to the parent's scrolling?
118 bool autogrow; // do we grow to accommodate output?
119
120 // we need to track any widget to which we are bound, so that (1) we don't
121 // end up bound to two widgets and (2) we can clean them up on shutdown
122 // (assuming they weren't explicitly cleaned up by the client).
123 void* widget; // widget to which we are bound, can be NULL
124 void(*wdestruct)(void*); // widget destructor, NULL iff widget is NULL
126
127// current presentation state of the terminal. it is carried across render
128// instances. initialize everything to 0 on a terminal reset / startup.
129typedef struct rasterstate {
130 // we assemble the encoded (rasterized) output in an fbuf (a portable POSIX
131 // memstream, basically), and keep it around between uses. this could be a
132 // problem if it ever tremendously spiked, but that seems unlikely?
133 fbuf f; // buffer for preparing raster glyph/escape stream
134
135 // the current cursor position. this is independent of whether the cursor is
136 // visible. it is the cell at which the next write will take place. this is
137 // modified by: output, cursor moves, clearing the screen (during refresh).
138 int y, x;
139
140 const ncplane* lastsrcp; // last source plane (we emit hpa on plane changes)
141
142 unsigned lastr; // foreground rgb, overloaded for palindexed fg
143 unsigned lastg;
144 unsigned lastb;
145 unsigned lastbr; // background rgb, overloaded for palindexed bg
146 unsigned lastbg;
147 unsigned lastbb;
148
149 // used in CLI mode, these track the end of logical output, to place the
150 // cursor following each rasterization. they are tracked thusly:
151 // * initialized to the initial physical cursor position
152 // * when we write to a physical row greater than logendy, update both
153 // * when we scroll, subtract one from logendy
154 // * if logendy reaches -1, reset both to 0
156
157 uint16_t curattr; // current attributes set (does not include colors)
158 // we elide a color escape iff the color has not changed between two cells
166
167// Tablets are the toplevel entitites within an ncreel. Each corresponds to
168// a single, distinct ncplane.
169typedef struct nctablet {
170 ncplane* p; // border plane, NULL when offscreen
171 ncplane* cbp; // data plane, NULL when offscreen
172 struct nctablet* next;
173 struct nctablet* prev;
174 tabletcb cbfxn; // application callback to draw cbp
175 void* curry; // application data provided to cbfxn
177
178typedef struct ncreel {
179 ncplane* p; // ncplane this ncreel occupies, under tablets
180 // doubly-linked list, a circular one when infinity scrolling is in effect.
181 // points at the focused tablet (when at least one tablet exists, one must be
182 // focused). it will be visibly focused following the next redraw.
184 nctablet* vft; // the visibly-focused tablet
185 enum {
188 } direction; // last direction of travel
189 int tabletcount; // could be derived, but we keep it o(1)
190 ncreel_options ropts; // copied in ncreel_create()
192
193typedef struct ncfdplane {
194 ncfdplane_callback cb; // invoked with fresh hot data
195 ncfdplane_done_cb donecb; // invoked on EOF (if !follow) or error
196 void* curry; // passed to the callbacks
197 int fd; // we take ownership of the fd, and close it
198 bool follow; // keep trying to read past the end (event-based)
199 ncplane* ncp; // bound ncplane
200 pthread_t tid; // thread servicing this i/o
201 bool destroyed; // set in ncfdplane_destroy() in our own context
203
204typedef struct ncsubproc {
206 pid_t pid; // subprocess
207 int pidfd; // for signalling/watching the subprocess
208 pthread_t waittid; // wait()ing thread if pidfd is not available
209 pthread_mutex_t lock; // guards waited
210 bool waited; // we've wait()ed on it, don't kill/wait further
212
213typedef struct ncreader {
214 ncplane* ncp; // always owned by ncreader
215 uint64_t tchannels; // channels for input text
216 uint32_t tattrs; // attributes for input text
217 ncplane* textarea; // grows as needed iff scrolling is enabled
218 int xproject; // virtual x location of ncp origin on textarea
219 bool horscroll; // is there horizontal panning?
220 bool no_cmd_keys; // are shortcuts disabled?
221 bool manage_cursor; // enable and place a virtual cursor
223
224typedef struct ncprogbar {
226 double progress; // on the range [0, 1]
230
231typedef struct nctab {
232 struct nctabbed* nt; // The nctabbed this belongs to
233 tabcb cb; // tab callback
234 char* name; // tab name
235 int namecols; // tab name width in columns
236 void* curry; // user pointer
237 struct nctab* prev;
238 struct nctab* next;
240
241// various moving parts within a notcurses context (and the user) might need to
242// access the stats object, so throw a lock on it. we don't want the lock in
243// the actual structure since (a) it's usually unnecessary and (b) it breaks
244// memset() and memcpy().
245typedef struct ncsharedstats {
246 pthread_mutex_t lock;
249
250typedef struct ncdirect {
251 ncpalette palette; // 256-indexed palette can be used instead of/with RGB
252 FILE* ttyfp; // FILE* for output tty
253 tinfo tcache; // terminfo cache
254 uint64_t channels; // current channels
255 uint16_t stylemask; // current styles
256 uint64_t flags; // copied in ncdirect_init() from param
257 ncsharedstats stats; // stats! not as broadly used as in notcurses
258 unsigned eof; // have we seen EOF on stdin?
260
261// Extracellular state for a cell during the render process. There is one
262// crender per rendered cell, and they are initialized to all zeroes. Be
263// careful with order here; padding can easily enlarge this struct from 40
264// to 48 bytes.
265struct crender {
266 nccell c; // solution cell
267 const ncplane *p; // source of glyph for this cell
268 sprixel* sprixel; // bitmap encountered during traversal
269 uint32_t hcfg; // fg channel prior to HIGHCONTRAST (need full channel)
270 struct {
271 // If the glyph we render is from an ncvisual, and has a transparent or
272 // blended background, blitter stacking is in effect. This is a complicated
273 // issue, but essentially, imagine a bottom block is rendered with a green
274 // bottom and transparent top. on a lower plane, a top block is rendered
275 // with a red foreground and blue background. Normally, this would result
276 // in a blue top and green bottom, but that's not what we ever wanted --
277 // what makes sense is a red top and green bottom. So ncvisual rendering
278 // sets bits from CELL_BLITTERSTACK_MASK when rendering a cell with a
279 // transparent background. When paint() selects a glyph, it checks for these
280 // bits. If they are set, any lower planes with CELL_BLITTERSTACK_MASK set
281 // take this into account when solving the background color.
282 unsigned blittedquads: 4;
283 unsigned damaged: 1; // only used in rasterization
284 // if NCALPHA_HIGHCONTRAST is in play, we apply the HSV flip once the
285 // background is locked in. set highcontrast to indicate this.
286 unsigned highcontrast: 1;
287 unsigned fgblends: 8;
288 unsigned bgblends: 8;
289 // we'll need recalculate the foreground relative to the solved background,
290 // and then reapply any foreground shading from above the highcontrast
291 // declaration. save the foreground state when we go highcontrast.
292 unsigned hcfgblends: 8; // number of foreground blends prior to HIGHCONTRAST
293 unsigned sprixeled: 1; // have we passed through a sprixel?
294 unsigned p_beats_sprixel: 1; // did we solve for our glyph above the bitmap?
295 } s;
296};
297
298// a pile is a collection of planes which will be rendered together. piles are
299// completely distinct with regards to thread-safety; one can always operate
300// concurrently on distinct piles (save rasterizing, of course). material from
301// other piles will be blown away whenever a pile is rasterized. no ordering
302// is meaningful between planes. when a new one is added, or one is destroyed,
303// the pilelock (in struct notcurses) is taken. a pile is destroyed whenever
304// its last plane is destroyed or reparented away. a pile contains a totally-
305// ordered list of piles (ordered on the z axis) and a directed acyclic forest
306// of bound planes, with each root plane rooting a DAG.
307//
308// the geometries are updated each time the pile is rendered. until a pile is
309// rendered, its geometries might be out-of-date with regards to the terminal
310// description in tcache. when it is rendered again, the geometries will be
311// updated, sprixels will have their TAMs rebuilt (if necessary), and each
312// root plane will have its resize callback invoked (possibly invoking its
313// bound planes' resize callbacks in turn).
314//
315// at context start, there is one pile (the standard pile), containing one
316// plane (the standard plane). each ncplane holds a pointer to its pile.
317typedef struct ncpile {
318 ncplane* top; // topmost plane, never NULL
319 ncplane* bottom; // bottommost plane, never NULL
320 ncplane* roots; // head of root plane list
321 struct crender* crender; // array (rows * cols crender objects)
322 struct notcurses* nc; // notcurses context
323 struct ncpile *prev, *next; // circular list pointers
324 size_t crenderlen; // size of crender vector
325 unsigned dimy, dimx; // rows and cols at last render/creation
326 unsigned cellpxx, cellpxy; // cell-pixel geometry at last render/creation
327 int scrolls; // how many real lines need be scrolled at raster
328 sprixel* sprixelcache; // sorted list of sprixels, assembled during paint
330
331// the standard pile can be reached through ->stdplane.
332typedef struct notcurses {
333 ncplane* stdplane; // standard plane, covers screen
334
335 // the style state of the terminal is carried across render runs
337
338 // we keep a copy of the last rendered frame. this facilitates O(1)
339 // notcurses_at_yx() and O(1) damage detection (at the cost of some memory).
340 // FIXME why isn't this just an ncplane rather than ~10 different members?
341 nccell* lastframe;// last rasterized framebuffer, NULL until first raster
342 // the last pile we rasterized. NULL until we've rasterized once. might
343 // be invalid due to the pile being destroyed; you are only allowed to
344 // evaluate it for equality to the pile being currently rasterized. when
345 // we switch piles, we need to clear all displayed sprixels, and
346 // invalidate the new pile's, pursuant to their display.
348 egcpool pool; // egcpool for lastframe
349
350 unsigned lfdimx; // dimensions of lastframe, unchanged by screen resize
351 unsigned lfdimy; // lfdimx/lfdimy are 0 until first rasterization
352
353 int cursory; // desired cursor placement according to user.
354 int cursorx; // -1 is don't-care, otherwise moved here after each render.
355
356 ncsharedstats stats; // some statistics across the lifetime of the context
357 ncstats stashed_stats; // retain across a context reset, for closing banner
358
359 FILE* ttyfp; // FILE* for writing rasterized data
360 tinfo tcache; // terminfo cache
361 pthread_mutex_t pilelock; // guards pile list, locks resize in render
362
363 // desired margins (best-effort only), copied in from notcurses_options
366 ncpalette palette; // 256-indexed palette can be used instead of/with RGB
368 bool touched_palette; // have we ever changed a palette entry?
369 uint64_t flags; // copied from notcurses_options
371
372typedef struct blitterargs {
373 // FIXME begy/begx are really only of interest to scaling; they ought be
374 // consumed there, and blitters ought always work with the scaled output.
375 int begy; // upper left start within visual
376 int begx;
377 int leny; // number of source pixels to use
378 int lenx;
379 uint64_t flags; // flags (as selected from ncvisual_options->flags)
380 uint32_t transcolor; // if non-zero, treat the lower 24 bits as a transparent color
381 union { // cell vs pixel-specific arguments
382 struct {
383 int placey; // placement within ncplane
385 } cell; // for cells
386 struct {
387 int colorregs; // number of color registers
388 sprixel* spx; // sprixel object
389 int pxoffy; // pixel y-offset within origin cell
390 int pxoffx; // pixel x-offset within origin cell
391 int cellpxy; // targeted cell-pixel geometry. this ought come from the
392 int cellpxx; // target ncpile, or tcache in Direct Mode
393 } pixel; // for pixels
394 } u;
396
397// a system for rendering RGBA pixels as text glyphs or sixel/kitty bitmaps
398struct blitset {
400 unsigned width; // number of input pixels per output cell, width
401 unsigned height; // number of input pixels per output cell, height
402 // the EGCs which form the blitter. bits grow left to right, and then top to
403 // bottom. the first character is always a space, the last a full block.
404 const wchar_t* egcs;
405 // the EGCs which form the various levels of a given plotset. if the geometry
406 // is wide, things are arranged with the rightmost side increasing most
407 // quickly, i.e. it can be indexed as height arrays of 1 + height glyphs. i.e.
408 // the first five braille EGCs are all 0 on the left, [0..4] on the right.
409 const wchar_t* plotegcs;
411 const char* name;
412 bool fill;
413};
414
415#include "blitset.h"
416
417void reset_stats(ncstats* stats);
418void summarize_stats(notcurses* nc);
419
420void update_raster_stats(const struct timespec* time1, const struct timespec* time0, ncstats* stats);
421void update_render_stats(const struct timespec* time1, const struct timespec* time0, ncstats* stats);
422void update_raster_bytes(ncstats* stats, int bytes);
423void update_write_stats(const struct timespec* time1, const struct timespec* time0, ncstats* stats, int bytes);
424
425void sigwinch_handler(int signo);
426
427void init_lang(void);
428
429int reset_term_attributes(const tinfo* ti, fbuf* f);
430int reset_term_palette(const tinfo* ti, fbuf* f, unsigned touchedpalette);
431
432// if there were missing elements we wanted from terminfo, bitch about them here
433void warn_terminfo(const notcurses* nc, const tinfo* ti);
434
436
437static inline ncpile*
438ncplane_pile(const ncplane* n){
439 return n->pile;
440}
441
442static inline const ncpile*
443ncplane_pile_const(const ncplane* n){
444 return n->pile;
445}
446
447static inline ncplane*
448ncplane_stdplane(ncplane* n){
450}
451
452static inline const ncplane*
453ncplane_stdplane_const(const ncplane* n){
455}
456
457// set the plane's widget and wdestruct fields, returning non-zero if they're
458// already non-NULL (i.e. if the plane is already bound), unless we pass NULL
459// (which ought be done from the widget destructor, to avoid corecursion).
460static inline int
461ncplane_set_widget(ncplane* n, void* w, void(*wdestruct)(void*)){
462 if(n->widget){
463 if(w){
464 logerror("plane is already bound to a widget");
465 return -1;
466 }
467 }else if(w == NULL){
468 return -1;
469 }
470 n->widget = w;
471 n->wdestruct = wdestruct;
472 return 0;
473}
474
475// initialize visualization backend (ffmpeg/oiio)
476int ncvisual_init(int loglevel);
477
478static inline int
479fbcellidx(int row, int rowlen, int col){
480//fprintf(stderr, "row: %d rowlen: %d col: %d\n", row, rowlen, col);
481 return row * rowlen + col;
482}
483
484// take a logical 'y' and convert it to the virtual 'y'. see HACKING.md.
485static inline int
486logical_to_virtual(const ncplane* n, int y){
487//fprintf(stderr, "y: %d n->logrow: %d n->leny: %d\n", y, n->logrow, n->leny);
488 return (y + n->logrow) % n->leny;
489}
490
491int clear_and_home(notcurses* nc, tinfo* ti, fbuf* f);
492
493static inline int
494nfbcellidx(const ncplane* n, int row, int col){
495 return fbcellidx(logical_to_virtual(n, row), n->lenx, col);
496}
497
498// is the rgb value greyish? note that pure white and pure black are both
499// considered greyish according to the definition of this function =].
500static inline bool
501rgb_greyish_p(unsigned r, unsigned g, unsigned b){
502 const unsigned GREYMASK = 0xf8;
503 if((r & GREYMASK) == (g & GREYMASK) && (g & GREYMASK) == (b & GREYMASK)){
504 return true;
505 }
506 return false;
507}
508
509// For our first attempt, O(1) uniform conversion from 8-bit r/g/b down to
510// ~2.4-bit 6x6x6 cube + greyscale (assumed on entry; I know no way to
511// even semi-portably recover the palette) proceeds via: map each 8-bit to
512// a 5-bit target grey. if all 3 components match, select that grey.
513// otherwise, c / 42.7 to map to 6 values.
514static inline int
515rgb_quantize_256(unsigned r, unsigned g, unsigned b){
516 // if all 5 MSBs match, return grey from 24-member grey ramp or pure
517 // black/white from original 16 (0 and 15, respectively)
518 if(rgb_greyish_p(r, g, b)){
519 // 8 and 238 come from https://terminalguide.namepad.de/attr/fgcol256/,
520 // which suggests 10-nit intervals otherwise.
521 if(r < 8){
522 return 0;
523 }else if(r > 238){
524 return 15;
525 }
526 return 232 + (r - 8) / 10;
527 }
528 r /= 43;
529 g /= 43;
530 b /= 43;
531 return r * 36 + g * 6 + b + 16;
532}
533
534// We get black, red, green, yellow, blue, magenta, cyan, and white. Ugh. Under
535// an additive model: G + R -> Y, G + B -> C, R + B -> M, R + G + B -> W
536static inline int
537rgb_quantize_8(unsigned r, unsigned g, unsigned b){
538 static const int BLACK = 0;
539 static const int RED = 1;
540 static const int GREEN = 2;
541 static const int YELLOW = 3;
542 static const int BLUE = 4;
543 static const int MAGENTA = 5;
544 static const int CYAN = 6;
545 static const int WHITE = 7;
546 if(rgb_greyish_p(r, g, b)){
547 if(r < 64){
548 return BLACK;
549 }
550 return WHITE;
551 }
552 if(r < 128){ // we have no red
553 if(g < 128){ // we have no green
554 if(b < 128){
555 return BLACK;
556 }
557 return BLUE;
558 } // we have green
559 if(b < 128){
560 return GREEN;
561 }
562 return CYAN;
563 }else if(g < 128){ // we have red, but not green
564 if(b < 128){
565 return RED;
566 }
567 return MAGENTA;
568 }else if(b < 128){ // we have red and green
569 return YELLOW;
570 }
571 return WHITE;
572}
573
574// Given r, g, and b values 0..255, do a weighted average per Rec. 601, and
575// return the 8-bit greyscale value (this value will be the r, g, and b value
576// for the new color).
577static inline int
578rgb_greyscale(int r, int g, int b){
579 if(r < 0 || r > 255){
580 return -1;
581 }
582 if(g < 0 || g > 255){
583 return -1;
584 }
585 if(b < 0 || b > 255){
586 return -1;
587 }
588 // Use Rec. 601 scaling plus linear approximation of gamma decompression
589 float fg = (0.299 * (r / 255.0) + 0.587 * (g / 255.0) + 0.114 * (b / 255.0));
590 return fg * 255;
591}
592
593static inline const char*
594pool_extended_gcluster(const egcpool* pool, const nccell* c){
595 if(cell_simple_p(c)){
596 return (const char*)&c->gcluster;
597 }
598 return egcpool_extended_gcluster(pool, c);
599}
600
601static inline nccell*
602ncplane_cell_ref_yx(const ncplane* n, unsigned y, unsigned x){
603 return &n->fb[nfbcellidx(n, y, x)];
604}
605
606static inline void
607cell_debug(const egcpool* p, const nccell* c){
608 fprintf(stderr, "gcluster: %08x %s style: 0x%04x chan: 0x%016" PRIx64 "\n",
609 c->gcluster, egcpool_extended_gcluster(p, c), c->stylemask,
610 c->channels);
611}
612
613static inline void
614plane_debug(const ncplane* n, bool details){
615 unsigned dimy, dimx;
616 ncplane_dim_yx(n, &dimy, &dimx);
617 fprintf(stderr, "p: %p dim: %d/%d poolsize: %d\n", n, dimy, dimx, n->pool.poolsize);
618 if(details){
619 for(unsigned y = 0 ; y < 1 ; ++y){
620 for(unsigned x = 0 ; x < 10 ; ++x){
621 const nccell* c = &n->fb[fbcellidx(y, dimx, x)];
622 fprintf(stderr, "[%03d/%03d] ", y, x);
623 cell_debug(&n->pool, c);
624 }
625 }
626 }
627}
628
629static inline notcurses*
630ncpile_notcurses(ncpile* p){
631 return p->nc;
632}
633
634static inline const notcurses*
635ncpile_notcurses_const(const ncpile* p){
636 return p->nc;
637}
638
639static inline void
640ncpile_debug(const ncpile* p, fbuf* f){
641 fbuf_printf(f, " ************************* %16p pile ****************************\n", p);
642 const ncplane* n = p->top;
643 const ncplane* prev = NULL;
644 int planeidx = 0;
645 while(n){
646 fbuf_printf(f, "%04d off y: %3d x: %3d geom y: %3u x: %3u curs y: %3u x: %3u %p %.4s\n",
647 planeidx, n->absy, n->absx, n->leny, n->lenx, n->y, n->x, n, n->name);
648 if(n->boundto || n->bnext || n->bprev || n->blist){
649 fbuf_printf(f, " bound %p %s %p %s %p binds %p\n",
650 n->boundto, notcurses_canutf8(ncpile_notcurses_const(p)) ? "←" : "<",
651 n->bprev, notcurses_canutf8(ncpile_notcurses_const(p)) ? "→" : ">",
652 n->bnext, n->blist);
653 }
654 if(n->bprev && (*n->bprev != n)){
655 fbuf_printf(f, " WARNING: expected *->bprev %p, got %p\n", n, *n->bprev);
656 }
657 if(n->above != prev){
658 fbuf_printf(f, " WARNING: expected ->above %p, got %p\n", prev, n->above);
659 }
660 if(ncplane_pile_const(n) != p){
661 fbuf_printf(f, " WARNING: expected pile %p, got %p\n", p, ncplane_pile_const(n));
662 }
663 prev = n;
664 n = n->below;
665 ++planeidx;
666 }
667 if(p->bottom != prev){
668 fbuf_printf(f, " WARNING: expected ->bottom %p, got %p\n", prev, p->bottom);
669 }
670}
671
672static inline void
673notcurses_debug_fbuf(const notcurses* nc, fbuf* f){
674 const ncpile* p = ncplane_pile(nc->stdplane);
675 fbuf_printf(f, " -------------------------- notcurses debug state -----------------------------\n");
676 const ncpile* p0 = p;
677 do{
678 ncpile_debug(p0, f);
679 const ncpile* prev = p0;
680 p0 = p0->next;
681 if(p0->prev != prev){
682 fbuf_printf(f, "WARNING: expected ->prev %p, got %p\n", prev, p0->prev);
683 }
684 }while(p != p0);
685 fbuf_printf(f, " ______________________________________________________________________________\n");
686}
687
688// cell coordinates *within the sprixel*, not absolute
689int sprite_wipe(const notcurses* nc, sprixel* s, int y, int x);
690void sprixel_free(sprixel* s);
691void sprixel_hide(sprixel* s);
692
693// dimy and dimx are cell geometry, not pixel.
694sprixel* sprixel_alloc(ncplane* n, int dimy, int dimx);
696int sprite_clear_all(const tinfo* t, fbuf* f);
697// these three all use absolute coordinates
698void sprixel_invalidate(sprixel* s, int y, int x);
699void sprixel_movefrom(sprixel* s, int y, int x);
700void sprixel_debug(const sprixel* s, FILE* out);
701void sixelmap_free(struct sixelmap *s);
702
703// update any necessary cells underneath the sprixel pursuant to its removal.
704// for sixel, this *achieves* the removal, and is performed on every cell.
705// returns 1 if the graphic can be immediately freed (which is equivalent to
706// asking whether it was sixel and there were no errors).
707static inline int
708sprite_scrub(const notcurses* n, const ncpile* p, sprixel* s){
709//sprixel_debug(s, stderr);
710 logdebug("sprixel %u state %d", s->id, s->invalidated);
711 return n->tcache.pixel_scrub(p, s);
712}
713
714// precondition: s->invalidated is SPRIXEL_INVALIDATED or SPRIXEL_MOVED.
715// returns -1 on error, or the number of bytes written.
716static inline int
717sprite_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
718 int yoff, int xoff){
719 if(!ti->pixel_draw){
720 return 0;
721 }
722//sprixel_debug(s, stderr);
723 logdebug("sprixel %u state %d", s->id, s->invalidated);
724 return ti->pixel_draw(ti, p, s, f, yoff, xoff);
725}
726
727// precondition: s->invalidated is SPRIXEL_MOVED or SPRIXEL_INVALIDATED
728// returns -1 on error, or the number of bytes written.
729static inline int
730sprite_redraw(notcurses* nc, const ncpile* p, sprixel* s, fbuf* f, int y, int x){
731//sprixel_debug(s, stderr);
732 const tinfo* ti = &nc->tcache;
733 logdebug("sprixel %u state %d", s->id, s->invalidated);
734 if(s->invalidated == SPRIXEL_MOVED && ti->pixel_move){
735 // if we are kitty prior to 0.20.0, C=1 isn't available to us, and we must
736 // not emit it. we use sixel_maxy_pristine as a side channel to encode
737 // this version information.
738 bool noscroll = !ti->sixel_maxy_pristine;
739 return ti->pixel_move(s, f, noscroll, y, x);
740 }else{
741 if(!ti->pixel_draw){
742 return 0;
743 }
744 int r = ti->pixel_draw(ti, p, s, f, y, x);
745 // different terminals put the cursor at different places following
746 // emission of a bitmap graphic. just reset y/x.
747 nc->rstate.y = -1;
748 nc->rstate.x = -1;
749 return r;
750 }
751}
752
753// present a loaded graphic. only defined for kitty.
754static inline int
755sprite_commit(tinfo* ti, fbuf* f, sprixel* s, unsigned forcescroll){
756 if(ti->pixel_commit){
757 // if we are kitty prior to 0.20.0, C=1 isn't available to us, and we must
758 // not emit it. we use sixel_maxy_pristine as a side channel to encode
759 // this version information. direct mode, meanwhile, sets forcescroll.
760 bool noscroll = !ti->sixel_maxy_pristine && !forcescroll;
761 if(ti->pixel_commit(f, s, noscroll) < 0){
762 return -1;
763 }
764 }
765 return 0;
766}
767
768static inline void
769cleanup_tam(tament* tam, int ydim, int xdim){
770 for(int y = 0 ; y < ydim ; ++y){
771 for(int x = 0 ; x < xdim ; ++x){
772 free(tam[y * xdim + x].auxvector);
773 tam[y * xdim + x].auxvector = NULL;
774 }
775 }
776}
777
778static inline void
779destroy_tam(ncplane* p){
780 if(p->tam){
781 cleanup_tam(p->tam, p->leny, p->lenx);
782 free(p->tam);
783 p->tam = NULL;
784 }
785}
786
787static inline int
788sprite_rebuild(const notcurses* nc, sprixel* s, int ycell, int xcell){
789 logdebug("rebuilding %d %d/%d", s->id, ycell, xcell);
790 const int idx = s->dimx * ycell + xcell;
791 int ret = 0;
792 // special case the transition back to SPRIXCELL_TRANSPARENT; this can be
793 // done in O(1), since the actual glyph needn't change.
796 }else if(s->n->tam[idx].state == SPRIXCELL_ANNIHILATED){
797 uint8_t* auxvec = (uint8_t*)s->n->tam[idx].auxvector;
798 assert(auxvec);
799 // sets the new state itself
800 ret = nc->tcache.pixel_rebuild(s, ycell, xcell, auxvec);
801 if(ret > 0){
802 free(auxvec);
803 s->n->tam[idx].auxvector = NULL;
804 }
805 }else{
806 return 0;
807 }
808 // don't upset SPRIXEL_MOVED
810 if(s->n->tam[idx].state != SPRIXCELL_TRANSPARENT &&
814 }
815 }
816 return ret;
817}
818
819// |y| and |x| are scaled geometry on input, and clamped scaled geometry on
820// output. |outy| is output geometry on output, and unused on input. output
821// geometry is derived from scaled geometry and output requirements (that Sixel
822// must be a multiple of six pixels tall). output width is always equal to
823// scaled width. all are pixels.
824// happy fact: common reported values for maximum sixel height are 256, 1024,
825// and 4096...not a single goddamn one of which is divisible by six. augh.
826static inline void
827clamp_to_sixelmax(const tinfo* t, unsigned* y, unsigned* x, unsigned* outy, ncscale_e scaling){
828 if(t->sixel_maxy && *y > t->sixel_maxy){
829 *y = t->sixel_maxy;
830 }
831 *outy = *y;
832 if(*outy % t->sprixel_scale_height){
833 *outy += t->sprixel_scale_height - (*outy % t->sprixel_scale_height);
834 // FIXME use closed form
835 while(t->sixel_maxy && *outy > t->sixel_maxy){
836 *outy -= t->sprixel_scale_height;
837 }
838 if(scaling == NCSCALE_STRETCH || *y > *outy){
839 *y = *outy;
840 }
841 }
842 if(t->sixel_maxx && *x > t->sixel_maxx){
843 *x = t->sixel_maxx;
844 }
845}
846
847// any sprixcell which does not cover the entirety of the underlying cell
848// cannot be SPRIXCELL_OPAQUE. this postprocesses the TAM, flipping any
849// such sprixcells to SPRIXCELL_MIXED. |leny| and |lenx| are output geometry
850// in pixels. |cdimy| and |cdimx| are output coverage in cells.
851static inline void
852scrub_tam_boundaries(tament* tam, int leny, int lenx, int cdimy, int cdimx){
853 // any sprixcells which don't cover the full cell underneath them cannot
854 // be SPRIXCELL_OPAQUE
855 const int cols = (lenx + cdimx - 1) / cdimx;
856 const int rows = (leny + cdimy - 1) / cdimy;
857 if(lenx % cdimx){
858 for(int y = 0 ; y < rows ; ++y){
859 if(tam[y * cols + cols - 1].state == SPRIXCELL_OPAQUE_KITTY){
860 tam[y * cols + cols - 1].state = SPRIXCELL_MIXED_KITTY;
861 }else if(tam[y * cols + cols - 1].state == SPRIXCELL_OPAQUE_SIXEL){
862 tam[y * cols + cols - 1].state = SPRIXCELL_MIXED_SIXEL;
863 }
864 }
865 }
866 if(leny % cdimy){
867 const int y = rows - 1;
868 for(int x = 0 ; x < cols ; ++x){
869 if(tam[y * cols + x].state == SPRIXCELL_OPAQUE_KITTY){
870 tam[y * cols + x].state = SPRIXCELL_MIXED_KITTY;
871 }else if(tam[y * cols + x].state == SPRIXCELL_OPAQUE_SIXEL){
872 tam[y * cols + x].state = SPRIXCELL_MIXED_SIXEL;
873 }
874 }
875 }
876}
877
878// get the TAM entry for these (absolute) coordinates
879static inline sprixcell_e
880sprixel_state(const sprixel* s, int y, int x){
882 int localy = y - (s->n->absy - stdn->absy);
883 int localx = x - (s->n->absx - stdn->absx);
884//fprintf(stderr, "TAM %%d at %d/%d (%d/%d, %d/%d)\n", /*s->n->tam[localy * s->dimx + localx].state,*/ localy, localx, y, x, s->dimy, s->dimx);
885 assert(localy >= 0);
886 assert(localy < (int)s->dimy);
887 assert(localx >= 0);
888 assert(localx < (int)s->dimx);
889 return s->n->tam[localy * s->dimx + localx].state;
890}
891
892static inline void
893pool_release(egcpool* pool, nccell* c){
894 if(cell_extended_p(c)){
895 egcpool_release(pool, cell_egc_idx(c));
896 }
897 c->gcluster = 0; // don't subject ourselves to double-release problems
898 c->width = 0; // don't subject ourselves to geometric ambiguities
899}
900
901// set the nccell 'c' to point into the egcpool at location 'eoffset'
902static inline void
903set_gcluster_egc(nccell* c, int eoffset){
904 c->gcluster = htole(0x01000000ul) + htole(eoffset);
905}
906
907// Duplicate one nccell onto another, possibly crossing ncplanes.
908static inline int
909cell_duplicate_far(egcpool* tpool, nccell* targ, const ncplane* splane, const nccell* c){
910 pool_release(tpool, targ);
911 targ->stylemask = c->stylemask;
912 targ->channels = c->channels;
913 targ->width = c->width;
914 if(!cell_extended_p(c)){
915 targ->gcluster = c->gcluster;
916 return 0;
917 }
918 const char* egc = nccell_extended_gcluster(splane, c);
919 size_t ulen = strlen(egc);
920 int eoffset = egcpool_stash(tpool, egc, ulen);
921 if(eoffset < 0){
922 return -1;
923 }
924 set_gcluster_egc(targ, eoffset);
925 return 0;
926}
927
928int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
929 unsigned keepleny, unsigned keeplenx,
930 int yoff, int xoff,
931 unsigned ylen, unsigned xlen);
932
933int update_term_dimensions(unsigned* rows, unsigned* cols, tinfo* tcache, int margin_b,
934 unsigned* cgeo_changed, unsigned* pgeo_changed)
935 __attribute__ ((nonnull (3, 5, 6)));
936
937ALLOC static inline void*
938memdup(const void* src, size_t len){
939 void* ret = malloc(len);
940 if(ret){
941 memcpy(ret, src, len);
942 }
943 return ret;
944}
945
946ALLOC void* bgra_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha);
947ALLOC void* rgb_loose_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha);
948ALLOC void* rgb_packed_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha);
949
950// find the "center" cell of two lengths. in the case of even rows/columns, we
951// place the center on the top/left. in such a case there will be one more
952// cell to the bottom/right of the center.
953static inline void
954center_box(int* RESTRICT y, int* RESTRICT x){
955 if(y){
956 *y = (*y - 1) / 2;
957 }
958 if(x){
959 *x = (*x - 1) / 2;
960 }
961}
962
963// find the "center" cell of a plane. in the case of even rows/columns, we
964// place the center on the top/left. in such a case there will be one more
965// cell to the bottom/right of the center.
966static inline void
967ncplane_center(const ncplane* n, int* RESTRICT y, int* RESTRICT x){
968 *y = n->leny;
969 *x = n->lenx;
970 center_box(y, x);
971}
972
973int ncvisual_bounding_box(const struct ncvisual* ncv, int* leny, int* lenx,
974 int* offy, int* offx);
975
976// Our gradient is a 2d lerp among the four corners of the region. We start
977// with the observation that each corner ought be its exact specified corner,
978// and the middle ought be the exact average of all four corners' components.
979// Another observation is that if all four corners are the same, every cell
980// ought be the exact same color. From this arises the observation that a
981// perimeter element is not affected by the other three sides:
982//
983// a corner element is defined by itself
984// a perimeter element is defined by the two points on its side
985// an internal element is defined by all four points
986//
987// 2D equation of state: solve for each quadrant's contribution (min 2x2):
988//
989// X' = (xlen - 1) - X
990// Y' = (ylen - 1) - Y
991// TLC: X' * Y' * TL
992// TRC: X * Y' * TR
993// BLC: X' * Y * BL
994// BRC: X * Y * BR
995// steps: (xlen - 1) * (ylen - 1) [maximum steps away from origin]
996//
997// Then add TLC + TRC + BLC + BRC + steps / 2, and divide by steps (the
998// steps / 2 is to work around truncate-towards-zero).
999static int
1000calc_gradient_component(unsigned tl, unsigned tr, unsigned bl, unsigned br,
1001 unsigned y, unsigned x, unsigned ylen, unsigned xlen){
1002 const int avm = (ylen - 1) - y;
1003 const int ahm = (xlen - 1) - x;
1004 if(xlen < 2){
1005 if(ylen < 2){
1006 return tl;
1007 }
1008 return (tl * avm + bl * y) / (ylen - 1);
1009 }
1010 if(ylen < 2){
1011 return (tl * ahm + tr * x) / (xlen - 1);
1012 }
1013 const int tlc = ahm * avm * tl;
1014 const int blc = ahm * y * bl;
1015 const int trc = x * avm * tr;
1016 const int brc = y * x * br;
1017 const int divisor = (ylen - 1) * (xlen - 1);
1018 return ((tlc + blc + trc + brc) + divisor / 2) / divisor;
1019}
1020
1021// calculate one of the channels of a gradient at a particular point.
1022static inline uint32_t
1023calc_gradient_channel(uint32_t ul, uint32_t ur, uint32_t ll, uint32_t lr,
1024 unsigned y, unsigned x, unsigned ylen, unsigned xlen){
1025 uint32_t chan = 0;
1026 ncchannel_set_rgb8_clipped(&chan,
1027 calc_gradient_component(ncchannel_r(ul), ncchannel_r(ur),
1028 ncchannel_r(ll), ncchannel_r(lr),
1029 y, x, ylen, xlen),
1030 calc_gradient_component(ncchannel_g(ul), ncchannel_g(ur),
1031 ncchannel_g(ll), ncchannel_g(lr),
1032 y, x, ylen, xlen),
1033 calc_gradient_component(ncchannel_b(ul), ncchannel_b(ur),
1034 ncchannel_b(ll), ncchannel_b(lr),
1035 y, x, ylen, xlen));
1036 ncchannel_set_alpha(&chan, ncchannel_alpha(ul)); // precondition: all αs are equal
1037 return chan;
1038}
1039
1040// calculate both channels of a gradient at a particular point, storing them
1041// into `channels'. x and y ought be the location within the gradient.
1042static inline void
1043calc_gradient_channels(uint64_t* channels, uint64_t ul, uint64_t ur,
1044 uint64_t ll, uint64_t lr, unsigned y, unsigned x,
1045 unsigned ylen, unsigned xlen){
1046 if(!ncchannels_fg_default_p(ul)){
1047 ncchannels_set_fchannel(channels,
1048 calc_gradient_channel(ncchannels_fchannel(ul),
1049 ncchannels_fchannel(ur),
1050 ncchannels_fchannel(ll),
1051 ncchannels_fchannel(lr),
1052 y, x, ylen, xlen));
1053 }else{
1054 ncchannels_set_fg_default(channels);
1055 }
1056 if(!ncchannels_bg_default_p(ul)){
1057 ncchannels_set_bchannel(channels,
1058 calc_gradient_channel(ncchannels_bchannel(ul),
1059 ncchannels_bchannel(ur),
1060 ncchannels_bchannel(ll),
1061 ncchannels_bchannel(lr),
1062 y, x, ylen, xlen));
1063 }else{
1064 ncchannels_set_bg_default(channels);
1065 }
1066}
1067
1068// ncdirect needs to "fake" an isolated ncplane as a drawing surface for
1069// ncvisual_blit(), and thus calls these low-level internal functions.
1070// they are not for general use -- check ncplane_new() and ncplane_destroy().
1072
1073void free_plane(ncplane* p);
1074
1075// heap-allocated formatted output
1076ALLOC char* ncplane_vprintf_prep(const char* format, va_list ap);
1077
1078// Resize the provided ncvisual to the specified 'rows' x 'cols', but do not
1079// change the internals of the ncvisual. Uses oframe.
1080int ncvisual_blit_internal(const struct ncvisual* ncv, int rows, int cols,
1081 ncplane* n, const struct blitset* bset,
1082 const blitterargs* bargs);
1083
1084// if fd < 0, blocking_write() is going to emit an EBADF, so we don't
1085// bother checking it here explicitly.
1086static inline int
1087tty_emit(const char* seq, int fd){
1088 if(!seq){
1089 return -1;
1090 }
1091 size_t slen = strlen(seq);
1092 if(blocking_write(fd, seq, slen)){
1093 return -1;
1094 }
1095 return 0;
1096}
1097
1098int set_fd_nonblocking(int fd, unsigned state, unsigned* oldstate);
1099
1100static inline int
1101term_bg_palindex(const notcurses* nc, fbuf* f, unsigned pal){
1102 const char* setab = get_escape(&nc->tcache, ESCAPE_SETAB);
1103 if(setab){
1104 return fbuf_emit(f, tiparm(setab, pal));
1105 }
1106 return 0;
1107}
1108
1109static inline int
1110term_fg_palindex(const notcurses* nc, fbuf* f, unsigned pal){
1111 const char* setaf = get_escape(&nc->tcache, ESCAPE_SETAF);
1112 if(setaf){
1113 return fbuf_emit(f, tiparm(setaf, pal));
1114 }
1115 return 0;
1116}
1117
1118// check the current and target style bitmasks against the specified 'stylebit'.
1119// if they are different, and we have the necessary capability, write the
1120// applicable terminfo entry to 'out'. returns -1 only on a true error.
1121static int
1122term_setstyle(fbuf* f, unsigned cur, unsigned targ, unsigned stylebit,
1123 const char* ton, const char* toff){
1124 int ret = 0;
1125 unsigned curon = cur & stylebit;
1126 unsigned targon = targ & stylebit;
1127 if(curon != targon){
1128 if(targon){
1129 if(ton){
1130 ret = fbuf_emit(f, ton);
1131 }
1132 }else{
1133 if(toff){ // how did this happen? we can turn it on, but not off?
1134 ret = fbuf_emit(f, toff);
1135 }
1136 }
1137 }
1138 if(ret < 0){
1139 return -1;
1140 }
1141 return 0;
1142}
1143
1144// emit escapes such that the current style is equal to newstyle. if this
1145// required an sgr0 (which resets colors), normalized will be non-zero upon
1146// a successful return.
1147static inline int
1148coerce_styles(fbuf* f, const tinfo* ti, uint16_t* curstyle,
1149 uint16_t newstyle, unsigned* normalized){
1150 *normalized = 0; // we never currently use sgr0
1151 int ret = 0;
1152 ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_BOLD,
1153 get_escape(ti, ESCAPE_BOLD), get_escape(ti, ESCAPE_NOBOLD));
1154 ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_ITALIC,
1155 get_escape(ti, ESCAPE_SITM), get_escape(ti, ESCAPE_RITM));
1156 ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_STRUCK,
1157 get_escape(ti, ESCAPE_SMXX), get_escape(ti, ESCAPE_RMXX));
1158 // underline and undercurl are exclusive. if we set one, don't go unsetting
1159 // the other.
1160 if(newstyle & NCSTYLE_UNDERLINE){ // turn on underline, or do nothing
1161 ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_UNDERLINE,
1162 get_escape(ti, ESCAPE_SMUL), get_escape(ti, ESCAPE_RMUL));
1163 }else if(newstyle & NCSTYLE_UNDERCURL){ // turn on undercurl, or do nothing
1164 ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_UNDERCURL,
1165 get_escape(ti, ESCAPE_SMULX), get_escape(ti, ESCAPE_SMULNOX));
1166 }else{ // turn off any underlining
1167 ret |= term_setstyle(f, *curstyle, newstyle, NCSTYLE_UNDERCURL | NCSTYLE_UNDERLINE,
1168 NULL, get_escape(ti, ESCAPE_RMUL));
1169 }
1170 *curstyle = newstyle;
1171 return ret;
1172}
1173
1174// DEC private mode set (DECSET) parameters (and corresponding XTerm resources)
1175#define SET_X10_MOUSE_PROT "9" // outdated, do not use, use x11
1176// we can combine 1000--1004, and then use 1005/1006/1015 for extended coordinates
1177#define SET_X11_MOUSE_PROT "1000" // for button events
1178#define SET_HILITE_MOUSE_PROT "1001" // for highlight tracking
1179#define SET_BTN_EVENT_MOUSE "1002" // for motion events with buttons
1180#define SET_ALL_EVENT_MOUSE "1003" // for motion events without buttons
1181#define SET_FOCUS_EVENT_MOUSE "1004" // for focus events
1182#define SET_UTF8_MOUSE_PROT "1005" // utf8-style extended coordinates
1183#define SET_SGR_MOUSE_PROT "1006" // sgr-style extended coordinates
1184#define SET_ALTERNATE_SCROLL "1007" // scroll in alternate screen (alternateScroll)
1185#define SET_TTYOUTPUT_SCROLL "1010" // scroll on tty output (scrollTtyOutput)
1186#define SET_KEYPRESS_SCROLL "1011" // scroll on keypress (scrollKey)
1187#define SET_URXVT_MOUSE_PROT "1015" // urxvt-style extended coordinates
1188#define SET_PIXEL_MOUSE_PROT "1016" // sgr-style, using pixles rather than cells
1189#define SET_ENABLE_ALTSCREEN "1046" // enable alternate screen (*sets* titeInhibit)
1190#define SET_ALTERNATE_SCREEN "1047" // replaces 47 (conflict w/DECGRPM) (titeInhibit)
1191#define SET_SAVE_CURSOR "1048" // save cursor ala DECSC (titeInhibit)
1192#define SET_SMCUP "1049" // 1047+1048 (titeInhibit)
1193// DECSET/DECRSTs can be chained with semicolons; can we generalize this? FIXME
1194#define DECSET(p) "\x1b[?" p "h"
1195#define DECRST(p) "\x1b[?" p "l"
1196
1197int mouse_setup(tinfo* ti, unsigned eventmask);
1198
1199// sync the drawing position to the specified location with as little overhead
1200// as possible (with nothing, if already at the right location). we prefer
1201// absolute horizontal moves (hpa) to relative ones, in the rare event that
1202// our understanding of our horizontal location is faulty. if we're moving from
1203// one plane to another, we emit an hpa no matter what.
1204// FIXME fall back to synthesized moves in the absence of capabilities (i.e.
1205// textronix lacks cup; fake it with horiz+vert moves)
1206// if hardcursorpos is non-zero, we always perform a cup. this is done when we
1207// don't know where the cursor currently is =].
1208static inline int
1209goto_location(notcurses* nc, fbuf* f, int y, int x, const ncplane* srcp){
1210//fprintf(stderr, "going to %d/%d from %d/%d hard: %u\n", y, x, nc->rstate.y, nc->rstate.x, nc->rstate.hardcursorpos);
1211 int ret = 0;
1212 // if we don't have hpa, force a cup even if we're only 1 char away. the only
1213 // TERM i know supporting cup sans hpa is vt100, and vt100 can suck it.
1214 // you can't use cuf for backwards moves anyway; again, vt100 can suck it.
1215 const char* hpa = get_escape(&nc->tcache, ESCAPE_HPA);
1216 if(nc->rstate.y == y && hpa){ // only need move x
1217 if(nc->rstate.x == x){
1218 if(nc->rstate.lastsrcp == srcp || !nc->tcache.gratuitous_hpa){
1219 return 0; // needn't move shit
1220 }
1221 ++nc->stats.s.hpa_gratuitous;
1222 }
1223 if(fbuf_emit(f, tiparm(hpa, x))){
1224 return -1;
1225 }
1226 }else{
1227 // cup is required, no need to verify existence
1228 const char* cup = get_escape(&nc->tcache, ESCAPE_CUP);
1229 if(fbuf_emit(f, tiparm(cup, y, x))){
1230 return -1;
1231 }
1232 }
1233 nc->rstate.x = x;
1234 nc->rstate.y = y;
1235 nc->rstate.lastsrcp = srcp;
1236 return ret;
1237}
1238
1239// how many edges need touch a corner for it to be printed?
1240static inline unsigned
1241box_corner_needs(unsigned ctlword){
1243}
1244
1245// True if the cell does not generate background pixels (i.e., the cell is a
1246// solid or shaded block, or certain emoji).
1247static inline bool
1248nccell_nobackground_p(const nccell* c){
1249 // needs to match all four bits of NC_NOBACKGROUND_MASK, not just one
1251}
1252
1253// True iff the foreground and background color are both RGB, and equal.
1254static inline bool
1255nccell_rgbequal_p(const nccell* c){
1256 if(nccell_fg_default_p(c) || nccell_fg_palindex_p(c)){
1257 return false;
1258 }
1259 if(nccell_bg_default_p(c) || nccell_bg_palindex_p(c)){
1260 return false;
1261 }
1262 return nccell_fg_rgb(c) == nccell_bg_rgb(c);
1263}
1264
1265// Returns a number 0 <= n <= 15 representing the four quadrants, and which (if
1266// any) are occupied due to blitting with a transparent background. The mapping
1267// is {tl, tr, bl, br}.
1268static inline unsigned
1269cell_blittedquadrants(const nccell* c){
1270 return ((c->channels & 0x8000000000000000ull) ? 1 : 0) |
1271 ((c->channels & 0x0400000000000000ull) ? 2 : 0) |
1272 ((c->channels & 0x0200000000000000ull) ? 4 : 0) |
1273 ((c->channels & 0x0100000000000000ull) ? 8 : 0);
1274}
1275
1276// Set this whenever blitting an ncvisual, when we have a transparent
1277// background. In such cases, ncvisuals underneath the cell must be rendered
1278// slightly differently.
1279static inline void
1280cell_set_blitquadrants(nccell* c, unsigned tl, unsigned tr, unsigned bl, unsigned br){
1281 // FIXME want a static assert that these four constants OR together to
1282 // equal CELL_BLITTERSTACK_MASK, bah
1283 uint64_t newval = (tl ? 0x8000000000000000ull : 0) |
1284 (tr ? 0x0400000000000000ull : 0) |
1285 (bl ? 0x0200000000000000ull : 0) |
1286 (br ? 0x0100000000000000ull : 0);
1287 c->channels = ((c->channels & ~NC_BLITTERSTACK_MASK) | newval);
1288}
1289
1290// Destroy a plane and all its bound descendants.
1292
1293// Extract the 32-bit background channel from a cell.
1294static inline uint32_t
1295cell_bchannel(const nccell* cl){
1296 return ncchannels_bchannel(cl->channels);
1297}
1298
1299// Extract those elements of the channel which are common to both foreground
1300// and background channel representations.
1301static inline uint32_t
1302channel_common(uint32_t channel){
1303 return channel & (NC_BGDEFAULT_MASK | NC_BG_RGB_MASK |
1305}
1306
1307// Extract those elements of the background channel which may be freely swapped
1308// with the foreground channel (alpha and coloring info).
1309static inline uint32_t
1310cell_bchannel_common(const nccell* cl){
1311 return channel_common(cell_bchannel(cl));
1312}
1313
1314// Extract the 32-bit foreground channel from a cell.
1315static inline uint32_t
1316cell_fchannel(const nccell* cl){
1317 return ncchannels_fchannel(cl->channels);
1318}
1319
1320// Extract those elements of the foreground channel which may be freely swapped
1321// with the background channel (alpha and coloring info).
1322static inline uint32_t
1323cell_fchannel_common(const nccell* cl){
1324 return channel_common(cell_fchannel(cl));
1325}
1326
1327// Set the 32-bit background channel of an nccell.
1328static inline uint64_t
1329cell_set_bchannel(nccell* cl, uint32_t channel){
1330 return ncchannels_set_bchannel(&cl->channels, channel);
1331}
1332
1333// Set the 32-bit foreground channel of an nccell.
1334static inline uint64_t
1335cell_set_fchannel(nccell* cl, uint32_t channel){
1336 return ncchannels_set_fchannel(&cl->channels, channel);
1337}
1338
1339// Returns the result of blending two channels. 'blends' indicates how heavily
1340// 'c1' ought be weighed. If 'blends' is 0 (indicating that 'c1' has not yet
1341// been set), 'c1' will be entirely determined by 'c2'. Otherwise, the default
1342// color is preserved if both are the default, palette color is preserved if
1343// both are the same palette index, and the result is otherwise RGB.
1344static inline unsigned
1345channels_blend(notcurses* nc, unsigned c1, unsigned c2, unsigned* blends,
1346 uint32_t defchan){
1347 if(ncchannel_alpha(c2) == NCALPHA_TRANSPARENT){
1348 return c1; // do *not* increment *blends
1349 }
1350 if(*blends == 0){
1351 // don't just return c2, or you set wide status and all kinds of crap
1352 if(ncchannel_default_p(c2)){
1353 ncchannel_set_default(&c1);
1354 }else if(ncchannel_palindex_p(c2)){
1355 ncchannel_set_palindex(&c1, ncchannel_palindex(c2));
1356 }else{
1357 ncchannel_set(&c1, ncchannel_rgb(c2));
1358 }
1359 }else if(ncchannel_default_p(c1) && ncchannel_default_p(c2)){
1360 // do nothing, leave as default
1361 }else if((ncchannel_palindex_p(c1) && ncchannel_palindex_p(c2)) &&
1362 ncchannel_palindex(c1) == ncchannel_palindex(c2)){
1363 // do nothing, leave as palette
1364 }else{
1365 unsigned r1, g1, b1;
1366 unsigned r2, g2, b2;
1367 if(ncchannel_default_p(c2)){
1368 ncchannel_rgb8(defchan, &r2, &g2, &b2);
1369 }else if(ncchannel_palindex_p(c2)){
1370 ncchannel_rgb8(nc->palette.chans[ncchannel_palindex(c2)],
1371 &r2, &g2, &b2);
1372 }else{
1373 ncchannel_rgb8(c2, &r2, &g2, &b2);
1374 }
1375 if(ncchannel_default_p(c1)){
1376 ncchannel_rgb8(defchan, &r1, &g1, &b1);
1377 }else if(ncchannel_palindex_p(c1)){
1378 ncchannel_rgb8(nc->palette.chans[ncchannel_palindex(c1)],
1379 &r1, &g1, &b1);
1380 }else{
1381 ncchannel_rgb8(c1, &r1, &g1, &b1);
1382 }
1383 unsigned r = (r1 * *blends + r2) / (*blends + 1);
1384 unsigned g = (g1 * *blends + g2) / (*blends + 1);
1385 unsigned b = (b1 * *blends + b2) / (*blends + 1);
1386 ncchannel_set_rgb8(&c1, r, g, b);
1387 }
1388 ncchannel_set_alpha(&c1, ncchannel_alpha(c2));
1389 ++*blends;
1390 return c1;
1391}
1392
1393// do not pass palette-indexed channels!
1394static inline uint64_t
1395cell_blend_fchannel(notcurses* nc, nccell* cl, unsigned channel, unsigned* blends){
1396 return cell_set_fchannel(cl, channels_blend(nc, cell_fchannel(cl),
1397 channel, blends, nc->tcache.fg_default));
1398}
1399
1400static inline uint64_t
1401cell_blend_bchannel(notcurses* nc, nccell* cl, unsigned channel, unsigned* blends){
1402 return cell_set_bchannel(cl, channels_blend(nc, cell_bchannel(cl),
1403 channel, blends, nc->tcache.bg_collides_default));
1404}
1405
1406// a sprixel occupies the entirety of its associated plane, usually an entirely
1407// new, purpose-specific plane. |leny| and |lenx| are output geometry in pixels.
1408static inline int
1409plane_blit_sixel(sprixel* spx, fbuf* f, int leny, int lenx,
1410 int parse_start, tament* tam, sprixel_e state){
1411 if(sprixel_load(spx, f, leny, lenx, parse_start, state)){
1412 return -1;
1413 }
1414 ncplane* n = spx->n;
1415 if(n){
1416//fprintf(stderr, "TAM WAS: %p NOW: %p\n", n->tam, tam);
1417 if(n->tam != tam){
1418 destroy_tam(n);
1419 }
1420 n->tam = tam;
1421 n->sprite = spx;
1422 }
1423 return 0;
1424}
1425
1426// is it a control character? check C0 and C1, but don't count empty strings,
1427// nor single-byte strings containing only a NUL character.
1428static inline bool
1429is_control_egc(const unsigned char* egc, int bytes){
1430 if(bytes == 1){
1431 if(*egc < 0x20 || *egc == 0x7f){ // includes NUL terminator
1432 return true;
1433 }
1434 }else if(bytes == 2){
1435 // 0xc2 followed by 0x80--0x9f are controls. 0xc2 followed by <0x80 is
1436 // simply invalid utf8.
1437 if(egc[0] == 0xc2){
1438 if(egc[1] < 0xa0){
1439 return true;
1440 }
1441 }
1442 }
1443 return false;
1444}
1445
1446// lowest level of cell+pool setup. if the EGC changes the output to RTL, it
1447// must be suffixed with a LTR-forcing character by now. The four bits of
1448// NC_BLITTERSTACK_MASK ought already be initialized. If gcluster is four
1449// bytes or fewer, this function cannot fail.
1450static inline int
1451pool_blit_direct(egcpool* pool, nccell* c, const char* gcluster, int bytes, int cols){
1452 pool_release(pool, c);
1453 if(bytes < 0 || cols < 0){
1454 return -1;
1455 }
1456 // we allow newlines and tabs to be blitted into the pool, as they're picked
1457 // up and given special semantics by output, paint(), and rasterization.
1458 if(is_control_egc((const unsigned char*)gcluster, bytes) && *gcluster != '\n' && *gcluster != '\t'){
1459 logerror("not loading control character %u", *(const unsigned char*)gcluster);
1460 return -1;
1461 }
1462 c->width = cols;
1463 if(bytes <= 4){
1464 c->gcluster = 0;
1465 memcpy(&c->gcluster, gcluster, bytes);
1466 }else{
1467 int eoffset = egcpool_stash(pool, gcluster, bytes);
1468 if(eoffset < 0){
1469 return -1;
1470 }
1471 set_gcluster_egc(c, eoffset);
1472 }
1473 return bytes;
1474}
1475
1476// Reset the quadrant occupancy bits, and pass the cell down to
1477// pool_blit_direct(). Returns the number of bytes loaded.
1478static inline int
1479pool_load_direct(egcpool* pool, nccell* c, const char* gcluster, int bytes, int cols){
1480 c->channels &= ~NC_NOBACKGROUND_MASK;
1481 return pool_blit_direct(pool, c, gcluster, bytes, cols);
1482}
1483
1484static inline int
1485cell_load_direct(ncplane* n, nccell* c, const char* gcluster, int bytes, int cols){
1486 return pool_load_direct(&n->pool, c, gcluster, bytes, cols);
1487}
1488
1489// increment y by 1 and rotate the framebuffer up one line. x moves to 0.
1490void scroll_down(ncplane* n);
1491
1492static inline bool
1493islinebreak(wchar_t wchar){
1494 // UC_LINE_SEPARATOR + UC_PARAGRAPH_SEPARATOR
1495 if(wchar == L'\n' || wchar == L'\v' || wchar == L'\f'){
1496 return true;
1497 }
1498 const uint32_t mask = UC_CATEGORY_MASK_Zl | UC_CATEGORY_MASK_Zp;
1499 return uc_is_general_category_withtable(wchar, mask);
1500}
1501
1502static inline bool
1503iswordbreak(wchar_t wchar){
1504 const uint32_t mask = UC_CATEGORY_MASK_Z |
1505 UC_CATEGORY_MASK_Zs;
1506 return uc_is_general_category_withtable(wchar, mask);
1507}
1508
1509// the heart of damage detection. compare two nccells (from two different
1510// planes) for equality. if they are equal, return 0. otherwise, dup the second
1511// onto the first and return non-zero.
1512static inline int
1513cellcmp_and_dupfar(egcpool* dampool, nccell* damcell,
1514 const ncplane* srcplane, const nccell* srccell){
1515 if(damcell->stylemask == srccell->stylemask){
1516 if(damcell->channels == srccell->channels){
1517 const char* srcegc = nccell_extended_gcluster(srcplane, srccell);
1518 const char* damegc = pool_extended_gcluster(dampool, damcell);
1519 if(strcmp(damegc, srcegc) == 0){
1520 return 0; // EGC match
1521 }
1522 }
1523 }
1524 cell_duplicate_far(dampool, damcell, srcplane, srccell);
1525 return 1;
1526}
1527
1528int get_tty_fd(FILE* ttyfp);
1529
1530// Given the four channels arguments, verify that:
1531//
1532// - if any is default foreground, all are default foreground
1533// - if any is default background, all are default background
1534// - all foregrounds must have the same alpha
1535// - all backgrounds must have the same alpha
1536// - palette-indexed color must not be used
1537//
1538// If you only want to check n < 4 channels, just duplicate one.
1539bool check_gradient_args(uint64_t ul, uint64_t ur, uint64_t bl, uint64_t br);
1540
1541// takes a signed starting coordinate (where -1 indicates the cursor's
1542// position), and an unsigned vector (where 0 indicates "everything
1543// remaining", i.e. to the right and below). returns 0 iff everything
1544// is valid and on the plane, filling in 'ystart'/'xstart' with the
1545// (non-negative) starting coordinates and 'ylen'/'xlen with the
1546// (positive) dimensions of the affected area.
1547static inline int
1548check_geometry_args(const ncplane* n, int y, int x,
1549 unsigned* ylen, unsigned* xlen,
1550 unsigned* ystart, unsigned* xstart){
1551 // handle the special -1 case for y/x, and reject other negatives
1552 if(y < 0){
1553 if(y != -1){
1554 logerror("invalid y: %d", y);
1555 return -1;
1556 }
1557 y = n->y;
1558 }
1559 if(x < 0){
1560 if(x != -1){
1561 logerror("invalid x: %d", x);
1562 return -1;
1563 }
1564 x = n->x;
1565 }
1566 // y and x are both now definitely positive, but might be off-plane.
1567 // lock in y and x as ystart and xstart for unsigned comparisons.
1568 *ystart = y;
1569 *xstart = x;
1570 unsigned ymax, xmax;
1571 ncplane_dim_yx(n, &ymax, &xmax);
1572 if(*ystart >= ymax || *xstart >= xmax){
1573 logerror("invalid starting coordinates: %u/%u", *ystart, *xstart);
1574 return -1;
1575 }
1576 // handle the special 0 case for ylen/xlen
1577 if(*ylen == 0){
1578 *ylen = ymax - *ystart;
1579 }
1580 if(*xlen == 0){
1581 *xlen = xmax - *xstart;
1582 }
1583 // ensure ylen/xlen are on-plane
1584 if(*ylen > ymax){
1585 logerror("ylen > dimy %u > %u", *ylen, ymax);
1586 return -1;
1587 }
1588 if(*xlen > xmax){
1589 logerror("xlen > dimx %u > %u", *xlen, xmax);
1590 return -1;
1591 }
1592 // ensure x + xlen and y + ylen are on-plane, without overflow
1593 if(ymax - *ylen < *ystart){
1594 logerror("y + ylen > ymax %u + %u > %u", *ystart, *ylen, ymax);
1595 return -1;
1596 }
1597 if(xmax - *xlen < *xstart){
1598 logerror("x + xlen > xmax %u + %u > %u", *xstart, *xlen, xmax);
1599 return -1;
1600 }
1601 return 0;
1602}
1603
1604void ncvisual_printbanner(fbuf* f);
1605
1606// alpha comes to us 0--255, but we have only 3 alpha values to map them to
1607// (opaque, blended, and transparent). it's necessary that we display
1608// something for any non-zero alpha (see #1540), so the threshold is 1.
1609// we might want to map this to blended, but we only use opaque and
1610// transparent for now. if |transcolor| is non-zero, match its lower 24
1611// bits against each pixel's RGB value, and treat a match as transparent.
1612static inline bool
1613rgba_trans_p(uint32_t p, uint32_t transcolor){
1614 if(ncpixel_a(p) < 192){
1615 return true;
1616 }
1617 if(transcolor &&
1618 (ncpixel_r(p) == (transcolor & 0xff0000ull) >> 16) &&
1619 (ncpixel_g(p) == (transcolor & 0xff00ull) >> 8) &&
1620 (ncpixel_b(p) == (transcolor & 0xffull))){
1621 return true;
1622 }
1623 return false;
1624}
1625
1626// get a non-negative "manhattan distance" between two rgb values
1627static inline uint32_t
1628rgb_diff(unsigned r1, unsigned g1, unsigned b1, unsigned r2, unsigned g2, unsigned b2){
1629 uint32_t distance = 0;
1630 distance += r1 > r2 ? r1 - r2 : r2 - r1;
1631 distance += g1 > g2 ? g1 - g2 : g2 - g1;
1632 distance += b1 > b2 ? b1 - b2 : b2 - b1;
1633//fprintf(stderr, "RGBDIFF %u %u %u %u %u %u: %u\n", r1, g1, b1, r2, g2, b2, distance);
1634 return distance;
1635}
1636
1637// returns non-zero iff the two planes intersect
1638static inline unsigned
1639ncplanes_intersect_p(const ncplane* p1, const ncplane* p2){
1640 int y1, x1, y2, x2;
1641 int b1, r1, b2, r2;
1642 ncplane_abs_yx(p1, &y1, &x1);
1643 b1 = y1 + ncplane_dim_y(p1) - 1;
1644 r1 = x1 + ncplane_dim_x(p1) - 1;
1645 ncplane_abs_yx(p2, &y2, &x2);
1646 b2 = y2 + ncplane_dim_y(p2) - 1;
1647 r2 = x2 + ncplane_dim_x(p2) - 1;
1648 if(b1 < y2){ // p1 is above p2, no intersection
1649 return 0;
1650 }
1651 if(b2 < y1){ // p2 is above p1, no intersection
1652 return 0;
1653 }
1654 if(r1 < x2){ // p1 is to the left of p2, no intersection
1655 return 0;
1656 }
1657 if(r2 < x1){ // p2 is to the left of p1, no intersection
1658 return 0;
1659 }
1660 return 1;
1661}
1662
1663static inline uint64_t
1664ncdirect_channels(const ncdirect* nc){
1665 return nc->channels;
1666}
1667
1668static inline bool
1669ncdirect_fg_default_p(const ncdirect* nc){
1670 return ncchannels_fg_default_p(ncdirect_channels(nc));
1671}
1672
1673static inline bool
1674ncdirect_bg_default_p(const ncdirect* nc){
1675 return ncchannels_bg_default_p(ncdirect_channels(nc));
1676}
1677
1678static inline bool
1679ncdirect_fg_palindex_p(const ncdirect* nc){
1680 return ncchannels_fg_palindex_p(ncdirect_channels(nc));
1681}
1682
1683static inline bool
1684ncdirect_bg_palindex_p(const ncdirect* nc){
1685 return ncchannels_bg_palindex_p(ncdirect_channels(nc));
1686}
1687
1688int ncdirect_set_fg_rgb_f(ncdirect* nc, unsigned rgb, fbuf* f);
1689int ncdirect_set_bg_rgb_f(ncdirect* nc, unsigned rgb, fbuf* f);
1690int term_fg_rgb8(const tinfo* ti, fbuf* f, unsigned r, unsigned g, unsigned b);
1691
1692const struct blitset* lookup_blitset(const tinfo* tcache, ncblitter_e setid, bool may_degrade);
1693
1694static inline int
1695rgba_blit_dispatch(ncplane* nc, const struct blitset* bset,
1696 int linesize, const void* data,
1697 int leny, int lenx, const blitterargs* bargs){
1698 return bset->blit(nc, linesize, data, leny, lenx, bargs);
1699}
1700
1701int ncvisual_geom_inner(const tinfo* ti, const struct ncvisual* n,
1702 const struct ncvisual_options* vopts, ncvgeom* geom,
1703 const struct blitset** bset,
1704 unsigned* disppxy, unsigned* disppxx,
1705 unsigned* outy, unsigned* outx,
1706 int* placey, int* placex);
1707
1708static inline const struct blitset*
1709rgba_blitter_low(const tinfo* tcache, ncscale_e scale, bool maydegrade,
1710 ncblitter_e blitrec) {
1711 if(blitrec == NCBLIT_DEFAULT){
1712 blitrec = rgba_blitter_default(tcache, scale);
1713 }
1714 return lookup_blitset(tcache, blitrec, maydegrade);
1715}
1716
1717// RGBA visuals all use NCBLIT_2x1 by default (or NCBLIT_1x1 if not in
1718// UTF-8 mode), but an alternative can be specified.
1719static inline const struct blitset*
1720rgba_blitter(const struct tinfo* tcache, const struct ncvisual_options* opts) {
1721 const bool maydegrade = !(opts && (opts->flags & NCVISUAL_OPTION_NODEGRADE));
1722 const ncscale_e scale = opts ? opts->scaling : NCSCALE_NONE;
1723 return rgba_blitter_low(tcache, scale, maydegrade, opts ? opts->blitter : NCBLIT_DEFAULT);
1724}
1725
1726// naive resize of |bmap| from |srows|x|scols| -> |drows|x|dcols|, suitable for
1727// pixel art. we either select at a constant interval (for shrinking) or duplicate
1728// at a constant ratio (for inflation). in the absence of a multimedia engine, this
1729// is the only kind of resizing we support.
1730static inline uint32_t*
1731resize_bitmap(const uint32_t* bmap, int srows, int scols, size_t sstride,
1732 int drows, int dcols, size_t dstride){
1733 if(sstride < scols * sizeof(*bmap)){
1734 return NULL;
1735 }
1736 if(dstride < dcols * sizeof(*bmap)){
1737 return NULL;
1738 }
1739 // FIXME if parameters match current setup, do nothing, and return bmap
1740 size_t size = drows * dstride;
1741 uint32_t* ret = (uint32_t*)malloc(size);
1742 if(ret == NULL){
1743 return NULL;
1744 }
1745 float xrat = (float)dcols / scols;
1746 float yrat = (float)drows / srows;
1747 int dy = 0;
1748 for(int y = 0 ; y < srows ; ++y){
1749 float ytarg = (y + 1) * yrat;
1750 if(ytarg > drows){
1751 ytarg = drows;
1752 }
1753 while(ytarg > dy){
1754 int dx = 0;
1755 for(int x = 0 ; x < scols ; ++x){
1756 float xtarg = (x + 1) * xrat;
1757 if(xtarg > dcols){
1758 xtarg = dcols;
1759 }
1760 while(xtarg > dx){
1761 ret[dy * dstride / sizeof(*ret) + dx] = bmap[y * sstride / sizeof(*ret) + x];
1762 ++dx;
1763 }
1764 }
1765 ++dy;
1766 }
1767 }
1768 return ret;
1769}
1770
1771// a neighbor on which to polyfill. by the time we get to it, it might or
1772// might not have been filled in. if so, discard immediately. otherwise,
1773// check self, and if valid, push all neighbors.
1775 int y, x;
1777};
1778
1779static inline struct topolyfill*
1780create_polyfill_op(int y, int x, struct topolyfill** stck){
1781 // cast for the benefit of c++ callers
1782 struct topolyfill* n = (struct topolyfill*)malloc(sizeof(*n));
1783 if(n){
1784 n->y = y;
1785 n->x = x;
1786 n->next = *stck;
1787 *stck = n;
1788 }
1789 return n;
1790}
1791
1792// implemented by a multimedia backend (ffmpeg or oiio), and installed
1793// prior to calling notcurses_core_init() (by notcurses_init()).
1797 int (*visual_blit)(const struct ncvisual* ncv, unsigned rows, unsigned cols,
1798 ncplane* n, const struct blitset* bset, const blitterargs* barg);
1799 struct ncvisual* (*visual_create)(void);
1800 struct ncvisual* (*visual_from_file)(const char* fname);
1801 // ncv constructors other than ncvisual_from_file() need to set up the
1802 // AVFrame* 'frame' according to their own data, which is assumed to
1803 // have been prepared already in 'ncv'.
1805 int (*visual_decode)(struct ncvisual* nc);
1806 int (*visual_decode_loop)(struct ncvisual* nc);
1807 int (*visual_stream)(notcurses* nc, struct ncvisual* ncv, float timescale,
1808 ncstreamcb streamer, const struct ncvisual_options* vopts, void* curry);
1809 ncplane* (*visual_subtitle)(ncplane* parent, const struct ncvisual* ncv);
1810 int rowalign; // rowstride base, can be 0 for no padding
1811 // do a persistent resize, changing the ncv itself
1812 int (*visual_resize)(struct ncvisual* ncv, unsigned rows, unsigned cols);
1813 void (*visual_destroy)(struct ncvisual* ncv);
1817
1818// populated by libnotcurses.so if linked with multimedia
1820
1821// within unix, we can just use isatty(3). on windows, things work
1822// differently. for a true Windows Terminal, we'll have HANDLE pointers
1823// rather than file descriptors. in cygwin/msys2, isatty(3) always fails.
1824// so for __MINGW32__, always return true. otherwise return isatty(fd).
1825static inline int
1826tty_check(int fd){
1827#ifdef __MINGW32__
1828 return _isatty(fd);
1829#endif
1830 return isatty(fd);
1831}
1832
1833// attempt to cancel the specified thread (not an error if we can't; it might
1834// have already exited), and then join it (an error here is propagated).
1835static inline int
1836cancel_and_join(const char* name, pthread_t tid, void** res){
1837 if(pthread_cancel(tid)){
1838 logerror("couldn't cancel %s thread", name); // tid might have died
1839 }
1840 if(pthread_join(tid, res)){
1841 logerror("error joining %s thread", name);
1842 return -1;
1843 }
1844 return 0;
1845}
1846
1847static inline int
1848emit_scrolls(const tinfo* ti, int count, fbuf* f){
1849 logdebug("emitting %d scrolls", count);
1850 if(count > 1){
1851 const char* indn = get_escape(ti, ESCAPE_INDN);
1852 if(indn){
1853 if(fbuf_emit(f, tiparm(indn, count)) < 0){
1854 return -1;
1855 }
1856 return 0;
1857 }
1858 }
1859 const char* ind = get_escape(ti, ESCAPE_IND);
1860 if(ind == NULL){
1861 ind = "\v";
1862 }
1863 // fell through if we had no indn
1864 while(count > 0){
1865 if(fbuf_emit(f, ind) < 0){
1866 return -1;
1867 }
1868 --count;
1869 }
1870 return 0;
1871}
1872
1873// both emit the |count > 0| scroll ops to |f|, and update the cursor
1874// tracking in |nc|
1875static inline int
1876emit_scrolls_track(notcurses* nc, int count, fbuf* f){
1877 if(emit_scrolls(&nc->tcache, count, f)){
1878 return -1;
1879 }
1880 nc->rstate.y -= count;
1881 nc->rstate.x = 0;
1882 return 0;
1883}
1884
1885// replace or populate the TERM environment variable with 'termname'
1886int putenv_term(const char* termname) __attribute__ ((nonnull (1)));
1887
1888// check environment for NOTCURSES_LOGLEVEL, and use it if defined.
1890 __attribute__ ((nonnull (1)));
1891
1892// glibc's _nl_normalize_charset() converts to lowercase, removing everything
1893// but alnums. furthermore, "cs" is a valid prefix meaning "character set".
1894static inline bool
1895encoding_is_utf8(const char *enc){
1896 if(tolower(enc[0]) == 'c' && tolower(enc[1]) == 's'){ // strncasecmp() isn't ansi/iso
1897 enc += 2; // skip initial "cs" if present.
1898 }
1899 const char utfstr[] = "utf8";
1900 const char* match = utfstr;
1901 while(*enc){
1902 if(isalnum(*enc)){ // we only care about alnums
1903 if(tolower(*enc) != tolower(*match)){
1904 return false;
1905 }
1906 ++match;
1907 }
1908 ++enc;
1909 }
1910 if(*match){
1911 return false;
1912 }
1913 return true;
1914}
1915
1916// tell ncmetric that utf8 is available. should be per-context, but isn't.
1917void ncmetric_use_utf8(void);
1918
1919#undef API
1920#undef ALLOC
1921
1922#ifdef __cplusplus
1923}
1924#endif
1925
1926#endif
int(* ncblitter)(struct ncplane *n, int linesize, const void *data, int scaledy, int scaledx, const struct blitterargs *bargs)
Definition blit.h:14
ncloglevel_e loglevel
Definition debug.c:3
API int API int API int uint64_t uint64_t uint64_t uint64_t lr
Definition direct.h:216
API int API int API int uint64_t uint64_t uint64_t uint64_t unsigned unsigned unsigned ctlword
Definition direct.h:217
API int API int API int uint64_t uint64_t uint64_t ll
Definition direct.h:216
API int API int API int uint64_t uint64_t uint64_t uint64_t unsigned unsigned xlen
Definition direct.h:217
API int API int API int uint64_t uint64_t uint64_t uint64_t unsigned ylen
Definition direct.h:217
API int API int API int uint64_t ul
Definition direct.h:215
API int API int API int uint64_t uint64_t ur
Definition direct.h:215
const char * egc
Definition egcpool.h:173
const char size_t ulen
Definition egcpool.h:173
assert(false)
const nccell * c
Definition egcpool.h:296
uint32_t idx
Definition egcpool.h:298
free(duplicated)
__attribute__((nonnull(1, 2))) static inline int egcpool_stash(egcpool *pool
int r
Definition fbuf.h:226
void update_render_stats(const struct timespec *time1, const struct timespec *time0, ncstats *stats)
Definition stats.c:40
int ncdirect_set_bg_rgb_f(ncdirect *nc, unsigned rgb, fbuf *f)
Definition render.c:1652
ncplane * ncplane_new_internal(notcurses *nc, ncplane *n, const ncplane_options *nopts)
Definition notcurses.c:557
int update_term_dimensions(unsigned *rows, unsigned *cols, tinfo *tcache, int margin_b, unsigned *cgeo_changed, unsigned *pgeo_changed) __attribute__((nonnull(3
#define ALLOC
Definition internal.h:20
sprixel * sprixel_recycle(ncplane *n)
Definition sprite.c:51
int ncdirect_set_fg_rgb_f(ncdirect *nc, unsigned rgb, fbuf *f)
Definition render.c:1682
void update_write_stats(const struct timespec *time1, const struct timespec *time0, ncstats *stats, int bytes)
Definition stats.c:5
int putenv_term(const char *termname) __attribute__((nonnull(1)))
Definition termdesc.c:1632
int term_fg_rgb8(const tinfo *ti, fbuf *f, unsigned r, unsigned g, unsigned b)
Definition render.c:743
ALLOC void * rgb_loose_to_rgba(const void *data, int rows, int *rowstride, int cols, int alpha)
Definition visual.c:461
void sprixel_movefrom(sprixel *s, int y, int x)
Definition sprite.c:68
void sprixel_hide(sprixel *s)
Definition sprite.c:83
void ncvisual_printbanner(fbuf *f)
Definition visual.c:30
int mouse_setup(tinfo *ti, unsigned eventmask)
Definition mice.c:3
int sprite_wipe(const notcurses *nc, sprixel *s, int y, int x)
Definition sprite.c:170
void reset_stats(ncstats *stats)
Definition stats.c:74
int ncvisual_init(int loglevel)
Definition visual.c:23
int ncplane_destroy_family(ncplane *ncp)
Definition notcurses.c:1068
void warn_terminfo(const notcurses *nc, const tinfo *ti)
int reset_term_palette(const tinfo *ti, fbuf *f, unsigned touchedpalette)
Definition notcurses.c:78
void free_plane(ncplane *p)
Definition notcurses.c:464
int set_loglevel_from_env(ncloglevel_e *loglevel) __attribute__((nonnull(1)))
Definition util.c:19
const struct blitset * lookup_blitset(const tinfo *tcache, ncblitter_e setid, bool may_degrade)
Definition blit.c:1297
int ncplane_resize_internal(ncplane *n, int keepy, int keepx, unsigned keepleny, unsigned keeplenx, int yoff, int xoff, unsigned ylen, unsigned xlen)
Definition notcurses.c:854
int ncvisual_bounding_box(const struct ncvisual *ncv, int *leny, int *lenx, int *offy, int *offx)
int set_fd_nonblocking(int fd, unsigned state, unsigned *oldstate)
ALLOC void * bgra_to_rgba(const void *data, int rows, int *rowstride, int cols, int alpha)
Definition visual.c:506
bool check_gradient_args(uint64_t ul, uint64_t ur, uint64_t bl, uint64_t br)
Definition fill.c:150
int ncvisual_blit_internal(const struct ncvisual *ncv, int rows, int cols, ncplane *n, const struct blitset *bset, const blitterargs *bargs)
void summarize_stats(notcurses *nc)
Definition stats.c:166
void sigwinch_handler(int signo)
Definition in.c:31
int get_tty_fd(FILE *ttyfp)
Definition fd.c:455
void sprixel_invalidate(sprixel *s, int y, int x)
Definition sprite.c:103
void init_lang(void)
Definition notcurses.c:1094
void scroll_down(ncplane *n)
Definition notcurses.c:1758
void update_raster_stats(const struct timespec *time1, const struct timespec *time0, ncstats *stats)
Definition stats.c:58
void sprixel_free(sprixel *s)
Definition sprite.c:38
int resize_callbacks_children(ncplane *n)
Definition notcurses.c:801
API ncvisual_implementation * visual_implementation
Definition visual.c:20
int reset_term_attributes(const tinfo *ti, fbuf *f)
Definition notcurses.c:60
void ncmetric_use_utf8(void)
Definition metric.c:24
int sprite_clear_all(const tinfo *t, fbuf *f)
Definition sprite.c:204
void sprixel_debug(const sprixel *s, FILE *out)
Definition sprite.c:7
#define API
Definition internal.h:16
sprixel * sprixel_alloc(ncplane *n, int dimy, int dimx)
Definition sprite.c:117
int ncvisual_geom_inner(const tinfo *ti, const struct ncvisual *n, const struct ncvisual_options *vopts, ncvgeom *geom, const struct blitset **bset, unsigned *disppxy, unsigned *disppxx, unsigned *outy, unsigned *outx, int *placey, int *placex)
ALLOC char * ncplane_vprintf_prep(const char *format, va_list ap)
Definition notcurses.c:2087
void sixelmap_free(struct sixelmap *s)
Definition sixel.c:216
void update_raster_bytes(ncstats *stats, int bytes)
Definition stats.c:27
int clear_and_home(notcurses *nc, tinfo *ti, fbuf *f)
Definition render.c:1399
ALLOC void * rgb_packed_to_rgba(const void *data, int rows, int *rowstride, int cols, int alpha)
Definition visual.c:485
#define logerror(fmt,...)
Definition logging.h:32
#define logdebug(fmt,...)
Definition logging.h:52
#define htole(x)
Definition ncport.h:36
ncplane * notcurses_stdplane(notcurses *nc)
Definition notcurses.c:699
void ncplane_abs_yx(const ncplane *n, int *RESTRICT y, int *RESTRICT x)
Definition notcurses.c:2642
const char * nccell_extended_gcluster(const ncplane *n, const nccell *c)
Definition notcurses.c:1573
const notcurses * ncplane_notcurses_const(const ncplane *n)
Definition notcurses.c:2630
const ncplane * notcurses_stdplane_const(const notcurses *nc)
Definition notcurses.c:703
notcurses * ncplane_notcurses(const ncplane *n)
Definition notcurses.c:2626
void ncplane_dim_yx(const ncplane *n, unsigned *rows, unsigned *cols)
Definition notcurses.c:301
#define NCSTYLE_UNDERCURL
Definition notcurses.h:772
ncscale_e
Definition notcurses.h:96
@ NCSCALE_STRETCH
Definition notcurses.h:99
@ NCSCALE_NONE
Definition notcurses.h:97
int y
Definition notcurses.h:1905
const struct ncplane_options struct ncvisual * ncv
Definition notcurses.h:3484
void(* tabcb)(struct nctab *t, struct ncplane *ncp, void *curry)
Definition notcurses.h:4250
#define NC_BG_PALETTE
Definition notcurses.h:123
#define NCBOXCORNER_SHIFT
Definition notcurses.h:2610
const struct ncplane_options struct ncvisual struct ncvisual_options * vopts
Definition notcurses.h:3484
#define NCSTYLE_UNDERLINE
Definition notcurses.h:771
#define NC_BGDEFAULT_MASK
Definition notcurses.h:118
const struct ncplane_options * opts
Definition notcurses.h:3483
ncalign_e
Definition notcurses.h:80
int(* tabletcb)(struct nctablet *t, bool drawfromtop)
Definition notcurses.h:3728
#define NC_NOBACKGROUND_MASK
Definition notcurses.h:116
#define NC_BG_ALPHA_MASK
Definition notcurses.h:125
ncblitter_e
Definition notcurses.h:65
@ NCBLIT_DEFAULT
Definition notcurses.h:66
#define NCALPHA_TRANSPARENT
Definition notcurses.h:106
#define NCSTYLE_ITALIC
Definition notcurses.h:770
vopts n
Definition notcurses.h:3502
#define RESTRICT
Definition notcurses.h:24
#define NCPALETTESIZE
Definition notcurses.h:111
#define NCSTYLE_BOLD
Definition notcurses.h:773
int(* ncfdplane_done_cb)(struct ncfdplane *n, int fderrno, void *curry)
Definition notcurses.h:4547
ncloglevel_e
Definition notcurses.h:968
int int x
Definition notcurses.h:1905
#define NCBOXCORNER_MASK
Definition notcurses.h:2609
#define NCSTYLE_STRUCK
Definition notcurses.h:774
int(* ncfdplane_callback)(struct ncfdplane *n, const void *buf, size_t s, void *curry)
Definition notcurses.h:4546
int(* ncstreamcb)(struct ncvisual *, struct ncvisual_options *, const struct timespec *, void *)
Definition notcurses.h:3533
API int API int const nccell unsigned len
Definition notcurses.h:2588
#define NCVISUAL_OPTION_NODEGRADE
Definition notcurses.h:3337
#define NC_BG_RGB_MASK
Definition notcurses.h:120
int sprixel_load(sprixel *spx, fbuf *f, unsigned pixy, unsigned pixx, int parse_start, sprixel_e state)
Definition sprite.c:154
sprixcell_e
Definition sprite.h:114
@ SPRIXCELL_ANNIHILATED
Definition sprite.h:120
@ SPRIXCELL_TRANSPARENT
Definition sprite.h:115
@ SPRIXCELL_ANNIHILATED_TRANS
Definition sprite.h:121
@ SPRIXCELL_MIXED_SIXEL
Definition sprite.h:118
@ SPRIXCELL_MIXED_KITTY
Definition sprite.h:119
@ SPRIXCELL_OPAQUE_SIXEL
Definition sprite.h:116
@ SPRIXCELL_OPAQUE_KITTY
Definition sprite.h:117
sprixel_e
Definition sprite.h:20
@ SPRIXEL_QUIESCENT
Definition sprite.h:21
@ SPRIXEL_INVALIDATED
Definition sprite.h:24
@ SPRIXEL_MOVED
Definition sprite.h:26
ncblitter_e geom
Definition internal.h:399
ncblitter blit
Definition internal.h:410
unsigned width
Definition internal.h:400
bool fill
Definition internal.h:412
const char * name
Definition internal.h:411
const wchar_t * plotegcs
Definition internal.h:409
unsigned height
Definition internal.h:401
const wchar_t * egcs
Definition internal.h:404
struct blitterargs::@3::@5 pixel
struct blitterargs::@3::@4 cell
uint64_t flags
Definition internal.h:379
sprixel * spx
Definition internal.h:388
union blitterargs::@3 u
uint32_t transcolor
Definition internal.h:380
int colorregs
Definition internal.h:387
struct crender::@2 s
unsigned damaged
Definition internal.h:283
nccell c
Definition internal.h:266
unsigned fgblends
Definition internal.h:287
unsigned bgblends
Definition internal.h:288
sprixel * sprixel
Definition internal.h:268
const ncplane * p
Definition internal.h:267
unsigned hcfgblends
Definition internal.h:292
unsigned highcontrast
Definition internal.h:286
unsigned p_beats_sprixel
Definition internal.h:294
unsigned blittedquads
Definition internal.h:282
unsigned sprixeled
Definition internal.h:293
uint32_t hcfg
Definition internal.h:269
Definition fbuf.h:25
uint8_t width
Definition notcurses.h:702
uint64_t channels
Definition notcurses.h:723
uint16_t stylemask
Definition notcurses.h:703
uint32_t gcluster
Definition notcurses.h:693
ncpalette palette
Definition internal.h:251
uint64_t channels
Definition internal.h:254
uint64_t flags
Definition internal.h:256
ncsharedstats stats
Definition internal.h:257
tinfo tcache
Definition internal.h:253
uint16_t stylemask
Definition internal.h:255
FILE * ttyfp
Definition internal.h:252
unsigned eof
Definition internal.h:258
ncplane * ncp
Definition internal.h:199
pthread_t tid
Definition internal.h:200
void * curry
Definition internal.h:196
bool destroyed
Definition internal.h:201
bool follow
Definition internal.h:198
ncfdplane_callback cb
Definition internal.h:194
ncfdplane_done_cb donecb
Definition internal.h:195
uint32_t chans[NCPALETTESIZE]
Definition notcurses.h:1585
unsigned cellpxx
Definition internal.h:326
struct ncpile * next
Definition internal.h:323
ncplane * bottom
Definition internal.h:319
sprixel * sprixelcache
Definition internal.h:328
unsigned dimx
Definition internal.h:325
struct ncpile * prev
Definition internal.h:323
size_t crenderlen
Definition internal.h:324
struct notcurses * nc
Definition internal.h:322
ncplane * roots
Definition internal.h:320
unsigned dimy
Definition internal.h:325
struct crender * crender
Definition internal.h:321
int scrolls
Definition internal.h:327
unsigned cellpxy
Definition internal.h:326
ncplane * top
Definition internal.h:318
int logrow
Definition internal.h:78
struct ncplane * below
Definition internal.h:94
ncalign_e valign
Definition internal.h:113
sprixel * sprite
Definition internal.h:105
bool fixedbound
Definition internal.h:117
unsigned lenx
Definition internal.h:86
bool scrolling
Definition internal.h:116
nccell basecell
Definition internal.h:110
bool autogrow
Definition internal.h:118
struct ncplane * above
Definition internal.h:93
struct ncpile * pile
Definition internal.h:92
struct ncplane * blist
Definition internal.h:102
char * name
Definition internal.h:111
uint64_t channels
Definition internal.h:88
ncalign_e halign
Definition internal.h:112
struct ncplane * boundto
Definition internal.h:103
struct ncplane * bnext
Definition internal.h:100
unsigned x
Definition internal.h:79
struct ncplane ** bprev
Definition internal.h:101
void(* wdestruct)(void *)
Definition internal.h:124
int absy
Definition internal.h:83
unsigned leny
Definition internal.h:86
uint16_t stylemask
Definition internal.h:114
void * userptr
Definition internal.h:108
tament * tam
Definition internal.h:106
int(* resizecb)(struct ncplane *)
Definition internal.h:109
unsigned y
Definition internal.h:79
int absx
Definition internal.h:83
int margin_b
Definition internal.h:115
void * widget
Definition internal.h:123
egcpool pool
Definition internal.h:87
int margin_r
Definition internal.h:115
nccell * fb
Definition internal.h:77
ncplane * ncp
Definition internal.h:225
double progress
Definition internal.h:226
uint32_t ulchannel
Definition internal.h:227
uint32_t blchannel
Definition internal.h:227
uint32_t urchannel
Definition internal.h:227
uint32_t brchannel
Definition internal.h:227
bool retrograde
Definition internal.h:228
uint64_t tchannels
Definition internal.h:215
bool no_cmd_keys
Definition internal.h:220
bool horscroll
Definition internal.h:219
ncplane * ncp
Definition internal.h:214
ncplane * textarea
Definition internal.h:217
int xproject
Definition internal.h:218
uint32_t tattrs
Definition internal.h:216
bool manage_cursor
Definition internal.h:221
ncreel_options ropts
Definition internal.h:190
int tabletcount
Definition internal.h:189
nctablet * tablets
Definition internal.h:183
ncplane * p
Definition internal.h:179
@ LASTDIRECTION_UP
Definition internal.h:186
@ LASTDIRECTION_DOWN
Definition internal.h:187
nctablet * vft
Definition internal.h:184
enum ncreel::@1 direction
ncstats s
Definition internal.h:247
pthread_mutex_t lock
Definition internal.h:246
uint64_t hpa_gratuitous
Definition notcurses.h:1814
pthread_mutex_t lock
Definition internal.h:209
ncfdplane * nfp
Definition internal.h:205
bool waited
Definition internal.h:210
pthread_t waittid
Definition internal.h:208
int pidfd
Definition internal.h:207
pid_t pid
Definition internal.h:206
char * name
Definition internal.h:234
struct nctabbed * nt
Definition internal.h:232
tabcb cb
Definition internal.h:233
struct nctab * prev
Definition internal.h:237
void * curry
Definition internal.h:236
int namecols
Definition internal.h:235
struct nctab * next
Definition internal.h:238
ncplane * cbp
Definition internal.h:171
struct nctablet * prev
Definition internal.h:173
void * curry
Definition internal.h:175
ncplane * p
Definition internal.h:170
tabletcb cbfxn
Definition internal.h:174
struct nctablet * next
Definition internal.h:172
int(* visual_resize)(struct ncvisual *ncv, unsigned rows, unsigned cols)
Definition internal.h:1812
int(* visual_stream)(notcurses *nc, struct ncvisual *ncv, float timescale, ncstreamcb streamer, const struct ncvisual_options *vopts, void *curry)
Definition internal.h:1807
int(* visual_decode)(struct ncvisual *nc)
Definition internal.h:1805
void(* visual_printbanner)(fbuf *f)
Definition internal.h:1796
int(* visual_decode_loop)(struct ncvisual *nc)
Definition internal.h:1806
void(* visual_details_seed)(struct ncvisual *ncv)
Definition internal.h:1804
int(* visual_blit)(const struct ncvisual *ncv, unsigned rows, unsigned cols, ncplane *n, const struct blitset *bset, const blitterargs *barg)
Definition internal.h:1797
void(* visual_destroy)(struct ncvisual *ncv)
Definition internal.h:1813
int(* visual_init)(int loglevel)
Definition internal.h:1795
rasterstate rstate
Definition internal.h:336
ncstats stashed_stats
Definition internal.h:357
ncpalette palette
Definition internal.h:366
ncpile * last_pile
Definition internal.h:347
bool palette_damage[NCPALETTESIZE]
Definition internal.h:367
pthread_mutex_t pilelock
Definition internal.h:361
nccell * lastframe
Definition internal.h:341
int cursorx
Definition internal.h:354
uint64_t flags
Definition internal.h:369
int margin_l
Definition internal.h:364
ncsharedstats stats
Definition internal.h:356
tinfo tcache
Definition internal.h:360
unsigned lfdimx
Definition internal.h:350
int cursory
Definition internal.h:353
bool touched_palette
Definition internal.h:368
unsigned lfdimy
Definition internal.h:351
FILE * ttyfp
Definition internal.h:359
ncplane * stdplane
Definition internal.h:333
int margin_t
Definition internal.h:364
int margin_b
Definition internal.h:364
egcpool pool
Definition internal.h:348
int margin_r
Definition internal.h:364
int loglevel
Definition internal.h:365
uint16_t curattr
Definition internal.h:157
bool fgelidable
Definition internal.h:159
unsigned lastbb
Definition internal.h:147
bool fgpalelidable
Definition internal.h:161
unsigned lastb
Definition internal.h:144
unsigned lastg
Definition internal.h:143
bool bgpalelidable
Definition internal.h:162
unsigned lastbg
Definition internal.h:146
unsigned lastbr
Definition internal.h:145
bool bgelidable
Definition internal.h:160
unsigned lastr
Definition internal.h:142
bool fgdefelidable
Definition internal.h:163
bool bgdefelidable
Definition internal.h:164
const ncplane * lastsrcp
Definition internal.h:140
unsigned dimx
Definition sprite.h:146
unsigned dimy
Definition sprite.h:146
struct ncplane * n
Definition sprite.h:142
uint32_t id
Definition sprite.h:139
sprixel_e invalidated
Definition sprite.h:143
void * auxvector
Definition sprite.h:128
sprixcell_e state
Definition sprite.h:127
unsigned gratuitous_hpa
Definition termdesc.h:184
int(* pixel_rebuild)(struct sprixel *s, int y, int x, uint8_t *auxvec)
Definition termdesc.h:140
uint32_t bg_collides_default
Definition termdesc.h:127
int(* pixel_commit)(fbuf *f, struct sprixel *s, unsigned noscroll)
Definition termdesc.h:155
unsigned sprixel_scale_height
Definition termdesc.h:174
unsigned sixel_maxx
Definition termdesc.h:165
uint32_t fg_default
Definition termdesc.h:130
int(* pixel_draw)(const struct tinfo *, const struct ncpile *p, struct sprixel *s, fbuf *f, int y, int x)
Definition termdesc.h:147
unsigned sixel_maxy
Definition termdesc.h:172
unsigned sixel_maxy_pristine
Definition termdesc.h:173
int(* pixel_move)(struct sprixel *s, fbuf *f, unsigned noscroll, int yoff, int xoff)
Definition termdesc.h:151
struct topolyfill * next
Definition internal.h:1776
return NULL
Definition termdesc.h:229
@ ESCAPE_BOLD
Definition termdesc.h:62
@ ESCAPE_CUP
Definition termdesc.h:45
@ ESCAPE_SMULX
Definition termdesc.h:73
@ ESCAPE_SETAB
Definition termdesc.h:49
@ ESCAPE_IND
Definition termdesc.h:76
@ ESCAPE_NOBOLD
Definition termdesc.h:63
@ ESCAPE_SMXX
Definition termdesc.h:70
@ ESCAPE_SITM
Definition termdesc.h:57
@ ESCAPE_RMXX
Definition termdesc.h:75
@ ESCAPE_INDN
Definition termdesc.h:77
@ ESCAPE_SMUL
Definition termdesc.h:71
@ ESCAPE_RITM
Definition termdesc.h:58
@ ESCAPE_SETAF
Definition termdesc.h:48
@ ESCAPE_HPA
Definition termdesc.h:46
@ ESCAPE_RMUL
Definition termdesc.h:72
@ ESCAPE_SMULNOX
Definition termdesc.h:74