Notcurses 3.0.16
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
notcurses.c
Go to the documentation of this file.
1#include "linux.h"
2#include "version.h"
3#include "egcpool.h"
4#include "internal.h"
5#include <time.h>
6#include <fcntl.h>
7#include <errno.h>
8#include <stdio.h>
9#include <limits.h>
10#include <string.h>
11#include <unistd.h>
12#include <stdlib.h>
13#include <unistr.h>
14#include <locale.h>
15#include <uniwbrk.h>
16#include <inttypes.h>
17#include "compat/compat.h"
18#include "unixsig.h"
19#include "banner.h"
20
21#define ESC "\x1b"
22#define TABSTOP 8
23
24void notcurses_version_components(int* major, int* minor, int* patch, int* tweak){
25 *major = NOTCURSES_VERNUM_MAJOR;
26 *minor = NOTCURSES_VERNUM_MINOR;
27 *patch = NOTCURSES_VERNUM_PATCH;
28 *tweak = atoi(NOTCURSES_VERSION_TWEAK);
29}
30
32 if(nc->tcache.ttyfd < 0){
33 return -1;
34 }
36 return -1;
37 }
39 return 0;
40}
41
43 if(nc->tcache.ttyfd < 0){
44 return -1;
45 }
47 &nc->tcache, nc->flags & NCOPTION_DRAIN_INPUT)){
48 return -1;
49 }
50 // move to the end of our output
51 if(nc->rstate.logendy < 0){
52 return 0;
53 }
55 return 0;
56}
57
58// reset the current colors, styles, and palette. called on startup (to purge
59// any preexisting styling) and shutdown (to not affect further programs).
60int reset_term_attributes(const tinfo* ti, fbuf* f){
61 int ret = 0;
62 const char* esc;
63 if((esc = get_escape(ti, ESCAPE_OP)) && fbuf_emit(f, esc)){
64 ret = -1;
65 }
66 if((esc = get_escape(ti, ESCAPE_SGR0)) && fbuf_emit(f, esc)){
67 ret = -1;
68 }
69 return ret;
70}
71
72// attempt to restore the palette. if XT{PUSH,POP}COLORS is supported, use
73// XTPOPCOLORS. if we can program individual colors, and we read the palette,
74// reload it from our initial capture. otherwise, use "oc" if available; this
75// will blow away any preexisting palette in favor of the default. if we've
76// never touched the palette, don't bother trying to restore it (unless we're
77// using XTPOPCOLORS, since in that case we always used XTPUSHCOLORS).
78int reset_term_palette(const tinfo* ti, fbuf* f, unsigned touchedpalette){
79 int ret = 0;
80 const char* esc;
81 if((esc = get_escape(ti, ESCAPE_RESTORECOLORS))){
82 loginfo("restoring palette via xtpopcolors");
83 if(fbuf_emit(f, esc)){
84 ret = -1;
85 }
86 return ret;
87 }
88 if(!touchedpalette){
89 return 0;
90 }
91 if(ti->caps.can_change_colors && ti->maxpaletteread > -1){
92 loginfo("restoring saved palette (%d)", ti->maxpaletteread + 1);
93 esc = get_escape(ti, ESCAPE_INITC);
94 for(int z = 0 ; z < ti->maxpaletteread ; ++z){
95 unsigned r, g, b;
96 ncchannel_rgb8(ti->originalpalette.chans[z], &r, &g, &b);
97 // Need convert RGB values [0..256) to [0..1000], ugh
98 r = r * 1000 / 255;
99 g = g * 1000 / 255;
100 b = b * 1000 / 255;
101 if(fbuf_emit(f, tiparm(esc, z, r, g, b)) < 0){
102 return -1;
103 }
104 }
105 }else if((esc = get_escape(ti, ESCAPE_OC))){
106 loginfo("resetting palette");
107 if(fbuf_emit(f, esc)){
108 ret = -1;
109 }
110 }else{
111 logwarn("no method known to restore palette");
112 }
113 return ret;
114}
115
116// Do the minimum necessary stuff to restore the terminal, then return. This is
117// the end of the line for fatal signal handlers. notcurses_stop() will go on
118// to tear down and account for internal structures. note that we do lots of
119// shit here that is unsafe within a signal handler =[ FIXME.
120static int
121notcurses_stop_minimal(void* vnc, void** altstack, int errret){
122 notcurses* nc = vnc;
123 int ret = 0;
124 ret |= drop_signals(nc, altstack);
125 // collect output into the memstream buffer, and then dump it directly using
126 // blocking_write(), to avoid problems with unreliable fflush().
127 fbuf* f = &nc->rstate.f;
128 fbuf_reset(f);
129 // be sure to write the restoration sequences *prior* to running rmcup, as
130 // they apply to the screen (alternate or otherwise) we're actually using.
131 const char* esc;
132 ret |= reset_term_palette(&nc->tcache, f, nc->touched_palette);
133 ret |= reset_term_attributes(&nc->tcache, f);
134 if((esc = get_escape(&nc->tcache, ESCAPE_RMKX)) && fbuf_emit(f, esc)){
135 ret = -1;
136 }
137 const char* cnorm = get_escape(&nc->tcache, ESCAPE_CNORM);
138 if(cnorm && fbuf_emit(f, cnorm)){
139 ret = -1;
140 }
141 if(fbuf_flush(f, nc->ttyfp)){
142 ret = -1;
143 }
144 if(nc->tcache.ttyfd >= 0){
145 ret |= notcurses_mice_disable(nc);
146 if(nc->tcache.tpreserved){
147 ret |= tcsetattr(nc->tcache.ttyfd, TCSAFLUSH, nc->tcache.tpreserved);
148 }
149 // don't use leave_alternate_screen() here; we need pop the keyboard
150 // whether we're in regular or alternate screen, and we need it done
151 // before returning to the regular screen if we're in the alternate. if
152 // we drained input, we never sent a keyboard modifier; send none now.
153 if(!(nc->flags & NCOPTION_DRAIN_INPUT)){
154 if(nc->tcache.kbdlevel){
155 if(tty_emit(KKEYBOARD_POP, nc->tcache.ttyfd)){
156 ret = -1;
157 }
158 }else{
159 if(tty_emit(XTMODKEYSUNDO, nc->tcache.ttyfd)){
160 ret = -1;
161 }
162 }
163 }
164 if(nc->tcache.in_alt_screen){
165 if((esc = get_escape(&nc->tcache, ESCAPE_RMCUP))){
166 if(tty_emit(esc, nc->tcache.ttyfd)){
167 ret = -1;
168 }
169 nc->tcache.in_alt_screen = 0;
170 }
171 }
172 }
173 if(errret){
174 ret = errret;
175 }
176 logdebug("restored terminal, returning %d", ret);
177 return ret;
178}
179
180static const char NOTCURSES_VERSION[] =
181 NOTCURSES_VERSION_MAJOR "."
182 NOTCURSES_VERSION_MINOR "."
183 NOTCURSES_VERSION_PATCH;
184
185const char* notcurses_version(void){
186 return NOTCURSES_VERSION;
187}
188
189void* ncplane_set_userptr(ncplane* n, void* opaque){
190 void* ret = n->userptr;
191 n->userptr = opaque;
192 return ret;
193}
194
196 return n->userptr;
197}
198
199const void* ncplane_userptr_const(const ncplane* n){
200 return n->userptr;
201}
202
203// is the cursor in an invalid position? it never should be, but it's probably
204// better to make sure (it's cheap) than to read from/write to random crap.
205static bool
206cursor_invalid_p(const ncplane* n){
207 if(n->y >= n->leny || n->x >= n->lenx){
208 return true;
209 }
210 return false;
211}
212
213char* ncplane_at_cursor(const ncplane* n, uint16_t* stylemask, uint64_t* channels){
214 return ncplane_at_yx(n, n->y, n->x, stylemask, channels);
215}
216
217char* ncplane_at_yx(const ncplane* n, int y, int x, uint16_t* stylemask, uint64_t* channels){
218 if(y < 0){
219 if(y != -1){
220 logerror("invalid y: %d", y);
221 return NULL;
222 }
223 y = n->y;
224 }
225 if(x < 0){
226 if(x != -1){
227 logerror("invalid x: %d", x);
228 return NULL;
229 }
230 x = n->x;
231 }
232 if((unsigned)y >= n->leny || (unsigned)x >= n->lenx){
233 logerror("invalid coordinates: %d/%d", y, x);
234 return NULL;
235 }
236 if(n->sprite){
237 if(stylemask){
238 *stylemask = 0;
239 }
240 if(channels){
241 *channels = 0;
242 }
243 return strdup(n->sprite->glyph.buf);
244 }
245 const nccell* yx = &n->fb[nfbcellidx(n, y, x)];
246 // if we're the right side of a wide glyph, we return the main glyph
247 if(nccell_wide_right_p(yx)){
248 return ncplane_at_yx(n, y, x - 1, stylemask, channels);
249 }
250 char* ret = nccell_extract(n, yx, stylemask, channels);
251 if(ret == NULL){
252 return NULL;
253 }
254//fprintf(stderr, "GOT [%s]\n", ret);
255 if(strcmp(ret, "") == 0){
256 free(ret);
257 ret = nccell_strdup(n, &n->basecell);
258 if(ret == NULL){
259 return NULL;
260 }
261 if(stylemask){
262 *stylemask = n->basecell.stylemask;
263 }
264 }
265 // FIXME load basecell channels if appropriate
266 return ret;
267}
268
270 return ncplane_at_yx_cell(n, n->y, n->x, c);
271}
272
274 if(n->sprite){
275 logerror("invoked on a sprixel plane");
276 return -1;
277 }
278 if(y < 0){
279 if(y != -1){
280 logerror("invalid y: %d", y);
281 return -1;
282 }
283 y = n->y;
284 }
285 if(x < 0){
286 if(x != -1){
287 logerror("invalid x: %d", x);
288 return -1;
289 }
290 x = n->x;
291 }
292 if((unsigned)y >= n->leny || (unsigned)x >= n->lenx){
293 logerror("invalid coordinates: %d/%d", y, x);
294 return -1;
295 }
296 nccell* targ = ncplane_cell_ref_yx(n, y, x);
297 if(nccell_duplicate(n, c, targ)){
298 return -1;
299 }
300 // FIXME take base cell into account where necessary!
301 return strlen(nccell_extended_gcluster(n, targ));
302}
303
304void ncplane_dim_yx(const ncplane* n, unsigned* rows, unsigned* cols){
305 if(rows){
306 *rows = n->leny;
307 }
308 if(cols){
309 *cols = n->lenx;
310 }
311}
312
313// anyone calling this needs ensure the ncplane's framebuffer is updated
314// to reflect changes in geometry. also called at startup for standard plane.
315// sets |cgeo_changed| high iff the cell geometry changed (will happen on a
316// resize, and on a font resize if the pixel geometry does not change).
317// sets |pgeo_changed| high iff the cell-pixel geometry changed (will happen
318// on a font resize).
319int update_term_dimensions(unsigned* rows, unsigned* cols, tinfo* tcache,
320 int margin_b, unsigned* cgeo_changed, unsigned* pgeo_changed){
321 *pgeo_changed = 0;
322 *cgeo_changed = 0;
323 // if we're not a real tty, we presumably haven't changed geometry, return
324 if(tcache->ttyfd < 0){
325 if(rows){
326 *rows = tcache->default_rows;
327 }
328 if(cols){
329 *cols = tcache->default_cols;
330 }
331 tcache->cellpxy = 0;
332 tcache->cellpxx = 0;
333 return 0;
334 }
335 unsigned rowsafe, colsafe;
336 if(rows == NULL){
337 rows = &rowsafe;
338 rowsafe = tcache->dimy;
339 }
340 if(cols == NULL){
341 cols = &colsafe;
342 colsafe = tcache->dimx;
343 }
344#ifndef __MINGW32__
345 struct winsize ws;
346 if(tiocgwinsz(tcache->ttyfd, &ws)){
347 return -1;
348 }
349 *rows = ws.ws_row;
350 *cols = ws.ws_col;
351 unsigned cpixy;
352 unsigned cpixx;
353#ifdef __linux__
354 if(tcache->linux_fb_fd >= 0){
355 get_linux_fb_pixelgeom(tcache, &tcache->pixy, &tcache->pixx);
356 cpixy = tcache->pixy / *rows;
357 cpixx = tcache->pixx / *cols;
358 }else{
359#else
360 {
361#endif
362 // we might have the pixel geometry from CSI14t, so don't override a valid
363 // earlier response with 0s from the ioctl. we do want to fire off a fresh
364 // CSI14t in this case, though FIXME.
365 if(ws.ws_ypixel){
366 tcache->pixy = ws.ws_ypixel;
367 tcache->pixx = ws.ws_xpixel;
368 }
369 // update even if we didn't get values just now, because we need set
370 // cellpx{y,x} up from an initial CSI14n, which set only pix{y,x}.
371 cpixy = ws.ws_row ? tcache->pixy / ws.ws_row : 0;
372 cpixx = ws.ws_col ? tcache->pixx / ws.ws_col : 0;
373 }
374 if(tcache->cellpxy != cpixy){
375 tcache->cellpxy = cpixy;
376 *pgeo_changed = 1;
377 }
378 if(tcache->cellpxx != cpixx){
379 tcache->cellpxx = cpixx;
380 *pgeo_changed = 1;
381 }
382 if(tcache->cellpxy == 0 || tcache->cellpxx == 0){
383 tcache->pixel_draw = NULL; // disable support
384 }
385#else
386 CONSOLE_SCREEN_BUFFER_INFO csbi;
387 // There is the buffer itself, which is similar in function to the scrollback
388 // buffer in a Linux terminal, and there is the display window, which is the
389 // visible view of that buffer. The addressable area (from a VT point of
390 // view) spans the width of the buffer, but the height of the display window.
391 //
392 // +--------------------------+ ^
393 // | | |
394 // | | |
395 // +-----+--------------+-----+ ^ w | b
396 // |XXXXX|XXXXXXXXXXXXXX|XXXXX| | i | u
397 // |XXXXX|XXXXXXXXXXXXXX|XXXXX| | n | f
398 // |XXXXX|XXXXXXXXXXXXXX|XXXXX| | d | f
399 // |XXXXX|XXXXXXXXXXXXXX|XXXXX| | o | e
400 // +-----+--------------+-----+ v w | r
401 // | | |
402 // | | |
403 // +--------------------------+ v
404 //
405 // <--- window --->
406 //
407 //<--------- buffer --------->
408 //
409 // Because the buffer extends past the bottom of the display window, a user
410 // can potentially scroll down beyond what would normally be thought of as the
411 // end of the buffer. Because the buffer can be wider than the display
412 // window, the user can scroll horizontally to view parts of the addressable
413 // area that aren't currently visible.
414 if(GetConsoleScreenBufferInfo(tcache->outhandle, &csbi)){
415 *cols = csbi.dwSize.X;
416 *rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
417 }else{
418 *rows = tcache->default_rows;
419 *cols = tcache->default_cols;
420 }
421#endif
422 if(tcache->dimy != *rows){
423 tcache->dimy = *rows;
424 *cgeo_changed = 1;
425 }
426 if(tcache->dimx != *cols){
427 tcache->dimx = *cols;
428 *cgeo_changed = 1;
429 }
430 if(tcache->sixel_maxy_pristine){
431 int sixelrows = *rows - 1;
432 // if the bottom margin is at least one row, we can draw into the last
433 // row of our visible area. we must leave the true bottom row alone.
434 if(margin_b){
435 ++sixelrows;
436 }
437 tcache->sixel_maxy = sixelrows * tcache->cellpxy;
438 if(tcache->sixel_maxy > tcache->sixel_maxy_pristine){
439 tcache->sixel_maxy = tcache->sixel_maxy_pristine;
440 }
441 }
442 return 0;
443}
444
445// destroy the sprixels of an ncpile (this will not hide the sprixels)
446static void
447free_sprixels(ncpile* n){
448 while(n->sprixelcache){
449 sprixel* tmp = n->sprixelcache->next;
450 sprixel_free(n->sprixelcache);
451 n->sprixelcache = tmp;
452 }
453}
454
455// destroy an empty ncpile. only call with pilelock held.
456static void
457ncpile_destroy(ncpile* pile){
458 if(pile){
459 pile->prev->next = pile->next;
460 pile->next->prev = pile->prev;
461 free_sprixels(pile);
462 free(pile->crender);
463 free(pile);
464 }
465}
466
468 if(p){
469 // ncdirect fakes an ncplane with no ->pile
470 if(ncplane_pile(p)){
472 pthread_mutex_lock(&nc->stats.lock);
474 ncplane_notcurses(p)->stats.s.fbbytes -= sizeof(*p->fb) * p->leny * p->lenx;
475 pthread_mutex_unlock(&nc->stats.lock);
476 if(p->above == NULL && p->below == NULL){
477 pthread_mutex_lock(&nc->pilelock);
478 ncpile_destroy(ncplane_pile(p));
479 pthread_mutex_unlock(&nc->pilelock);
480 }
481 }
482 if(p->widget){
483 void* w = p->widget;
484 void (*wdestruct)(void*) = p->wdestruct;
485 p->widget = NULL;
486 p->wdestruct = NULL;
487 logdebug("calling widget destructor %p(%p)", wdestruct, w);
488 wdestruct(w);
489 logdebug("got the widget");
490 }
491 if(p->sprite){
493 }
494 destroy_tam(p);
495 egcpool_dump(&p->pool);
496 free(p->name);
497 free(p->fb);
498 free(p);
499 }
500}
501
502// create a new ncpile. only call with pilelock held. the return value
503// was assigned to n->pile.
505static ncpile*
506make_ncpile(notcurses* nc, ncplane* n){
507 ncpile* ret = malloc(sizeof(*ret));
508 if(ret){
509 ret->nc = nc;
510 ret->top = n;
511 ret->bottom = n;
512 ret->roots = n;
513 n->bprev = &ret->roots;
514 if(nc->stdplane){ // stdplane (and thus stdpile) has already been created
515 ret->prev = ncplane_pile(nc->stdplane)->prev;
516 ncplane_pile(nc->stdplane)->prev->next = ret;
517 ret->next = ncplane_pile(nc->stdplane);
518 ncplane_pile(nc->stdplane)->prev = ret;
519 }else{
520 ret->prev = ret;
521 ret->next = ret;
522 }
523 n->above = NULL;
524 n->below = NULL;
525 ret->dimy = nc->tcache.dimy;
526 ret->dimx = nc->tcache.dimx;
527 ret->cellpxy = nc->tcache.cellpxy;
528 ret->cellpxx = nc->tcache.cellpxx;
529 ret->crender = NULL;
530 ret->crenderlen = 0;
531 ret->sprixelcache = NULL;
532 ret->scrolls = 0;
533 }
534 n->pile = ret;
535 return ret;
536}
537
538
539static inline size_t ncplane_sizeof_cellarray( size_t rows, size_t cols)
540{
541 // check if multiplication would overflow
542 // calloc will deal with overflow due to sizeof(nccell)
543 if( ! rows || (cols > SIZE_MAX / rows))
544 return 0;
545 return rows * cols;
546}
547
548// create a new ncplane at the specified location (relative to the true screen,
549// having origin at 0,0), having the specified size, and put it at the top of
550// the planestack. its cursor starts at its origin; its style starts as null.
551// a plane may exceed the boundaries of the screen, but must have positive
552// size in both dimensions. bind the plane to 'n', which may be NULL to create
553// a new pile. if bound to a plane instead, this plane moves when that plane
554// moves, and coordinates to move to are relative to that plane.
555// there are two denormalized case we also must handle, that of the "fake"
556// isolated ncplane created by ncdirect for rendering visuals. in that case
557// (and only in that case), nc is NULL (as is n). there's also creation of the
558// initial standard plane, in which case nc is not NULL, but nc->stdplane *is*
559// (as once more is n).
561 const ncplane_options* nopts){
562 if(nopts->flags >= (NCPLANE_OPTION_FIXED << 1u)){
563 logwarn("provided unsupported flags %016" PRIx64, nopts->flags);
564 }
566 if(n == NULL){
567 logerror("alignment requires a parent plane");
568 return NULL;
569 }
570 }
572 if(nopts->rows != 0 || nopts->cols != 0){
573 logerror("geometry specified with margins (r=%u, c=%u)",
574 nopts->rows, nopts->cols);
575 return NULL;
576 }
577 }
578 ncplane* p = malloc(sizeof(*p));
579 if(p == NULL){
580 return NULL;
581 }
585 p->widget = NULL;
586 p->wdestruct = NULL;
588 p->margin_b = nopts->margin_b;
589 p->margin_r = nopts->margin_r;
590 if(n){ // use parent size
591 p->leny = ncplane_dim_y(n);
592 p->lenx = ncplane_dim_x(n);
593 }else{ // use pile size
594 notcurses_term_dim_yx(nc, &p->leny, &p->lenx);
595 }
596 if((p->leny -= p->margin_b) <= 0){
597 p->leny = 1;
598 }
599 if((p->lenx -= p->margin_r) <= 0){
600 p->lenx = 1;
601 }
602 }else{
603 p->leny = nopts->rows;
604 p->lenx = nopts->cols;
605 }
606
607 size_t fbsize = ncplane_sizeof_cellarray(p->leny, p->lenx);
608 if(!fbsize || (p->fb = calloc(fbsize, sizeof(struct nccell))) == NULL){
609 logerror("error allocating cellmatrix (r=%u, c=%u)",
610 p->leny, p->lenx);
611 free(p);
612 return NULL;
613 }
614 p->x = p->y = 0;
615 p->logrow = 0;
616 p->sprite = NULL;
617 p->blist = NULL;
618 p->name = strdup(nopts->name ? nopts->name : "");
621 p->tam = NULL;
622 if(!n){ // new root/standard plane
623 p->absy = nopts->y;
624 p->absx = nopts->x;
625 p->bnext = NULL;
626 p->bprev = NULL;
627 p->boundto = p;
628 }else{ // bound to preexisting pile
629 if(nopts->flags & NCPLANE_OPTION_HORALIGNED){
630 p->absx = ncplane_halign(n, nopts->x, nopts->cols);
631 p->halign = nopts->x;
632 }else{
633 p->absx = nopts->x;
634 }
635 p->absx += n->absx;
636 if(nopts->flags & NCPLANE_OPTION_VERALIGNED){
637 p->absy = ncplane_valign(n, nopts->y, nopts->rows);
638 p->valign = nopts->y;
639 }else{
640 p->absy = nopts->y;
641 }
642 p->absy += n->absy;
643 if( (p->bnext = n->blist) ){
644 n->blist->bprev = &p->bnext;
645 }
646 p->bprev = &n->blist;
647 *p->bprev = p;
648 p->boundto = n;
649 }
650 // FIXME handle top/left margins
651 p->resizecb = nopts->resizecb;
652 p->stylemask = 0;
653 p->channels = 0;
654 egcpool_init(&p->pool);
655 nccell_init(&p->basecell);
656 p->userptr = nopts->userptr;
657 if(nc == NULL){ // fake ncplane backing ncdirect object
658 p->above = NULL;
659 p->below = NULL;
660 p->pile = NULL;
661 }else{
662 pthread_mutex_lock(&nc->pilelock);
663 ncpile* pile = n ? ncplane_pile(n) : NULL;
664 if( (p->pile = pile) ){ // existing pile
665 p->above = NULL;
666 if( (p->below = pile->top) ){ // always happens save initial plane
667 pile->top->above = p;
668 }else{
669 pile->bottom = p;
670 }
671 pile->top = p;
672 }else{ // new pile
673 make_ncpile(nc, p);
674 }
675 pthread_mutex_lock(&nc->stats.lock);
676 nc->stats.s.fbbytes += fbsize;
677 ++nc->stats.s.planes;
678 pthread_mutex_unlock(&nc->stats.lock);
679 pthread_mutex_unlock(&nc->pilelock);
680 }
681 loginfo("created new %ux%u plane \"%s\" @ %dx%d",
682 p->leny, p->lenx, p->name ? p->name : "", p->absy, p->absx);
683 return p;
684}
685
686// create an ncplane of the specified dimensions, but do not yet place it in
687// the z-buffer. clear out all cells. this is for a wholly new context.
688static ncplane*
689create_initial_ncplane(notcurses* nc, int dimy, int dimx){
690 ncplane_options nopts = {
691 .y = 0, .x = 0,
692 .rows = dimy - (nc->margin_t + nc->margin_b),
693 .cols = dimx - (nc->margin_l + nc->margin_r),
694 .userptr = NULL,
695 .name = "std",
696 .resizecb = NULL,
697 .flags = 0,
698 };
699 return nc->stdplane = ncplane_new_internal(nc, NULL, &nopts);
700}
701
703 return nc->stdplane;
704}
705
707 return nc->stdplane;
708}
709
713
715 return ncplane_new_internal(nc, NULL, nopts);
716}
717
719 n->x = 0;
720 n->y = 0;
721}
722
724 if(x < 0){
725 if(x < -1){
726 logerror("negative target x %d", x);
727 return -1;
728 }
729 }else if((unsigned)x >= n->lenx){
730 logerror("target x %d >= width %u", x, n->lenx);
731 return -1;
732 }else{
733 n->x = x;
734 }
735 if(y < 0){
736 if(y < -1){
737 logerror("negative target y %d", y);
738 return -1;
739 }
740 }else if((unsigned)y >= n->leny){
741 logerror("target y %d >= height %u", y, n->leny);
742 return -1;
743 }else{
744 n->y = y;
745 }
746 if(cursor_invalid_p(n)){
747 logerror("invalid cursor following move (%d/%d)", n->y, n->x);
748 return -1;
749 }
750 return 0;
751}
752
754 if((int)n->y + y == -1){
755 logerror("invalid target y -1");
756 return -1;
757 }else if((int)n->x + x == -1){
758 logerror("invalid target x -1");
759 return -1;
760 }else return ncplane_cursor_move_yx(n, n->y + y, n->x + x);
761}
762
763ncplane* ncplane_dup(const ncplane* n, void* opaque){
764 int dimy = n->leny;
765 int dimx = n->lenx;
766 const int placey = n->absy;
767 const int placex = n->absx;
768 struct ncplane_options nopts = {
769 .y = placey,
770 .x = placex,
771 .rows = dimy,
772 .cols = dimx,
773 .userptr = opaque,
774 .name = n->name,
775 .resizecb = ncplane_resizecb(n),
776 .flags = 0,
777 };
778 ncplane* newn = ncplane_create(n->boundto, &nopts);
779 if(newn == NULL){
780 return NULL;
781 }
782 // we don't duplicate sprites...though i'm unsure why not
783 size_t fbsize = sizeof(*n->fb) * dimx * dimy;
784 if(egcpool_dup(&newn->pool, &n->pool)){
786 return NULL;
787 }
788 memmove(newn->fb, n->fb, fbsize);
789 // don't use ncplane_cursor_move_yx() here; the cursor could be in an
790 // invalid location, which will be disallowed, failing out.
791 newn->y = n->y;
792 newn->x = n->x;
793 newn->halign = n->halign;
794 newn->stylemask = ncplane_styles(n);
795 newn->channels = ncplane_channels(n);
796 // we dupd the egcpool, so just dup the goffset
797 newn->basecell = n->basecell;
798 return newn;
799}
800
801// call the resize callback for each bound child in turn. we only need to do
802// the first generation; if they resize, they'll invoke
803// ncplane_resize_internal(), leading to this function being called anew.
805 int ret = 0;
806 for(struct ncplane* child = n->blist ; child ; child = child->bnext){
807 if(child->resizecb){
808 ret |= child->resizecb(child);
809 }
810 }
811 return ret;
812}
813
814// basic consistency checks on resize requests
815static int
816ncplane_resize_internal_check(const ncplane* n, int keepy, int keepx,
817 unsigned keepleny, unsigned keeplenx,
818 int yoff, int xoff, unsigned ylen, unsigned xlen,
819 unsigned* rows, unsigned* cols){
820 if(keepy < 0 || keepx < 0){ // can't start at negative origin
821 logerror("can't retain negative offset %dx%d", keepy, keepx);
822 return -1;
823 }
824 if((!keepleny && keeplenx) || (keepleny && !keeplenx)){ // both must be 0
825 logerror("can't retain null dimension %ux%u", keepleny, keeplenx);
826 return -1;
827 }
828 // can't be smaller than keep length
829 if(ylen < keepleny){
830 logerror("can't map in y dimension: %u < %u", ylen, keepleny);
831 return -1;
832 }
833 if(xlen < keeplenx){
834 logerror("can't map in x dimension: %u < %u", xlen, keeplenx);
835 return -1;
836 }
837 if(ylen <= 0 || xlen <= 0){ // can't resize to trivial or negative size
838 logerror("can't achieve meaningless size %ux%u", ylen, xlen);
839 return -1;
840 }
842 if(keepleny + keepy > *rows){
843 logerror("can't keep %u@%d rows from %u", keepleny, keepy, *rows);
844 return -1;
845 }
846 if(keeplenx + keepx > *cols){
847 logerror("can't keep %u@%d cols from %u", keeplenx, keepx, *cols);
848 return -1;
849 }
850 loginfo("%ux%u @ %d/%d → %u/%u @ %d/%d (want %ux%u@%d/%d)", *rows, *cols,
851 n->absy, n->absx, ylen, xlen, n->absy + keepy + yoff, n->absx + keepx + xoff,
852 keepleny, keeplenx, keepy, keepx);
853 return 0;
854}
855
856// can be used on stdplane, unlike ncplane_resize() which prohibits it.
857int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
858 unsigned keepleny, unsigned keeplenx,
859 int yoff, int xoff,
860 unsigned ylen, unsigned xlen){
861 unsigned rows, cols;
862 if(ncplane_resize_internal_check(n, keepy, keepx, keepleny, keeplenx,
863 yoff, xoff, ylen, xlen,
864 &rows, &cols)){
865 return -1;
866 }
867 if(n->absy == n->absy + keepy && n->absx == n->absx + keepx &&
868 rows == ylen && cols == xlen){
869 return 0;
870 }
872 if(n->sprite){
873 sprixel_hide(n->sprite);
874 }
875 // we're good to resize. we'll need alloc up a new framebuffer, and copy in
876 // those elements we're retaining, zeroing out the rest. alternatively, if
877 // we've shrunk, we will be filling the new structure.
878 int oldarea = rows * cols;
879 int keptarea = keepleny * keeplenx;
880 int newarea = ylen * xlen;
881 size_t fbsize = sizeof(nccell) * newarea;
882 nccell* fb;
883 // there are two cases worth optimizing:
884 //
885 // * nothing is kept. we malloc() a new cellmatrix, dump the EGCpool in
886 // toto, and zero out the matrix. no copies, one memset.
887 // * old and new x dimensions match, and we're keeping the full width.
888 // we release any cells we're about to lose, realloc() the cellmatrix,
889 // and zero out any new cells. so long as the realloc() doesn't move
890 // us, there are no copies, one memset, one iteration (since this is
891 // most often due to autogrowth by a single line, the likelihood that
892 // we remain where we are is pretty high).
893 // * otherwise, we malloc() a new cellmatrix, zero out any new cells,
894 // copy over any reused cells, and release any lost cells. one
895 // gigantic iteration.
896 // we might realloc instead of mallocing, in which case we NULL out
897 // |preserved|. it must otherwise be free()d at the end.
898 nccell* preserved = n->fb;
899 if(cols == xlen && cols == keeplenx && keepleny && !keepy){
900 // we need release the cells that we're losing, lest we leak EGCpool
901 // memory. unfortunately, this means we mutate the plane on the error case.
902 // any solution would involve copying them out first. we only do this if
903 // we're keeping some, as we otherwise drop the EGCpool in toto.
904 if(n->leny > keepleny){
905 for(unsigned y = keepleny ; y < n->leny ; ++y){
906 for(unsigned x = 0 ; x < n->lenx ; ++x){
907 nccell_release(n, ncplane_cell_ref_yx(n, y, x));
908 }
909 }
910 }
911 if((fb = realloc(n->fb, fbsize)) == NULL){
912 return -1;
913 }
914 preserved = NULL;
915 }else{
916 if((fb = malloc(fbsize)) == NULL){
917 return -1;
918 }
919 }
920 if(n->tam){
921 loginfo("tam realloc to %d entries", newarea);
922 // FIXME first, free any disposed auxiliary vectors!
923 tament* tmptam = realloc(n->tam, sizeof(*tmptam) * newarea);
924 if(tmptam == NULL){
925 if(preserved){
926 free(fb);
927 }
928 return -1;
929 }
930 n->tam = tmptam;
931 if(newarea > oldarea){
932 memset(n->tam + oldarea, 0, sizeof(*n->tam) * (newarea - oldarea));
933 }
934 }
935 // update the cursor, if it would otherwise be off-plane
936 if(n->y >= ylen){
937 n->y = ylen - 1;
938 }
939 if(n->x >= xlen){
940 n->x = xlen - 1;
941 }
942 pthread_mutex_lock(&nc->stats.lock);
943 ncplane_notcurses(n)->stats.s.fbbytes -= sizeof(*fb) * (rows * cols);
944 ncplane_notcurses(n)->stats.s.fbbytes += fbsize;
945 pthread_mutex_unlock(&nc->stats.lock);
946 const int oldabsy = n->absy;
947 // go ahead and move. we can no longer fail at this point. but don't yet
948 // resize, because n->len[xy] are used in fbcellidx() in the loop below. we
949 // don't use ncplane_move_yx(), because we want to planebinding-invariant.
950 n->absy += keepy + yoff;
951 n->absx += keepx + xoff;
952//fprintf(stderr, "absx: %d keepx: %d xoff: %d\n", n->absx, keepx, xoff);
953 if(keptarea == 0){ // keep nothing, resize/move only.
954 // if we're keeping nothing, dump the old egcspool. otherwise, we go ahead
955 // and keep it. perhaps we ought compact it?
956 memset(fb, 0, sizeof(*fb) * newarea);
957 egcpool_dump(&n->pool);
958 }else if(!preserved){
959 // the x dimensions are equal, and we're keeping across the width. only the
960 // y dimension changed. if we grew, we need zero out the new cells (if we
961 // shrunk, we already released the old cells prior to the realloc).
962 unsigned tozorch = (ylen - keepleny) * xlen * sizeof(*fb);
963 if(tozorch){
964 unsigned zorchoff = keepleny * xlen;
965 memset(fb + zorchoff, 0, tozorch);
966 }
967 }else{
968 // we currently have maxy rows of maxx cells each. we will be keeping rows
969 // keepy..keepy + keepleny - 1 and columns keepx..keepx + keeplenx - 1.
970 // anything else is zerod out. itery is the row we're writing *to*, and we
971 // must write to each (and every cell in each).
972 for(unsigned itery = 0 ; itery < ylen ; ++itery){
973 int truey = itery + n->absy;
974 int sourceoffy = truey - oldabsy;
975//fprintf(stderr, "sourceoffy: %d keepy: %d ylen: %d\n", sourceoffy, keepy, ylen);
976 // if we have nothing copied to this line, zero it out in one go
977 if(sourceoffy < keepy || sourceoffy >= keepy + (int)keepleny){
978//fprintf(stderr, "writing 0s to line %d of %d\n", itery, ylen);
979 memset(fb + (itery * xlen), 0, sizeof(*fb) * xlen);
980 }else{
981 int copyoff = itery * xlen; // our target at any given time
982 // we do have something to copy, and zero, one, or two regions to zero out
983 unsigned copied = 0;
984 if(xoff < 0){
985 memset(fb + copyoff, 0, sizeof(*fb) * -xoff);
986 copyoff += -xoff;
987 copied += -xoff;
988 }
989 const int sourceidx = nfbcellidx(n, sourceoffy, keepx);
990//fprintf(stderr, "copying line %d (%d) to %d (%d)\n", sourceoffy, sourceidx, copyoff / xlen, copyoff);
991 memcpy(fb + copyoff, preserved + sourceidx, sizeof(*fb) * keeplenx);
992 copyoff += keeplenx;
993 copied += keeplenx;
994 unsigned perline = xlen - copied;
995 for(unsigned x = copyoff ; x < n->lenx ; ++x){
996 nccell_release(n, ncplane_cell_ref_yx(n, sourceoffy, x));
997 }
998 memset(fb + copyoff, 0, sizeof(*fb) * perline);
999 }
1000 }
1001 }
1002 n->fb = fb;
1003 n->lenx = xlen;
1004 n->leny = ylen;
1005 free(preserved);
1007}
1008
1009int ncplane_resize(ncplane* n, int keepy, int keepx,
1010 unsigned keepleny, unsigned keeplenx,
1011 int yoff, int xoff,
1012 unsigned ylen, unsigned xlen){
1013 if(n == ncplane_notcurses(n)->stdplane){
1014//fprintf(stderr, "Can't resize standard plane\n");
1015 return -1;
1016 }
1017 return ncplane_resize_internal(n, keepy, keepx, keepleny, keeplenx,
1018 yoff, xoff, ylen, xlen);
1019}
1020
1022 if(ncp == NULL){
1023 return 0;
1024 }
1025 if(ncplane_notcurses(ncp)->stdplane == ncp){
1026 logerror("won't destroy standard plane");
1027 return -1;
1028 }
1029//notcurses_debug(ncplane_notcurses(ncp), stderr);
1030 loginfo("destroying %dx%d plane \"%s\" @ %dx%d",
1031 ncp->leny, ncp->lenx, ncp->name ? ncp->name : NULL, ncp->absy, ncp->absx);
1032 int ret = 0;
1033 // dissolve our binding from behind (->bprev is either NULL, or its
1034 // predecessor on the bound list's ->bnext, or &ncp->boundto->blist)
1035 if(ncp->bprev){
1036 if( (*ncp->bprev = ncp->bnext) ){
1037 ncp->bnext->bprev = ncp->bprev;
1038 }
1039 }else if(ncp->bnext){
1040 //assert(ncp->boundto->blist == ncp);
1041 ncp->bnext->bprev = NULL;
1042 }
1043 // recursively reparent our children to the plane to which we are bound.
1044 // this will extract each one from the sibling list.
1045 struct ncplane* bound = ncp->blist;
1046 while(bound){
1047 struct ncplane* tmp = bound->bnext;
1048 ncplane* bindto = ((ncp == ncp->boundto) ? bound : ncp->boundto);
1049 if(ncplane_reparent_family(bound, bindto) == NULL){
1050 ret = -1;
1051 }
1052 bound = tmp;
1053 }
1054 // extract ourselves from the z-axis. do this *after* reparenting, in case
1055 // reparenting shifts up the z-axis somehow (though i don't think it can,
1056 // at least not within a pile?).
1057 if(ncp->above){
1058 ncp->above->below = ncp->below;
1059 }else{
1060 ncplane_pile(ncp)->top = ncp->below;
1061 }
1062 if(ncp->below){
1063 ncp->below->above = ncp->above;
1064 }else{
1065 ncplane_pile(ncp)->bottom = ncp->above;
1066 }
1067 free_plane(ncp);
1068 return ret;
1069}
1070
1072 if(ncp == NULL){
1073 return 0;
1074 }
1075 if(ncplane_notcurses(ncp)->stdplane == ncp){
1076 logerror("won't destroy standard plane");
1077 return -1;
1078 }
1079 int ret = 0;
1080 while(ncp->blist){
1081 ret |= ncplane_family_destroy(ncp->blist);
1082 }
1083 ret |= ncplane_destroy(ncp);
1084 return ret;
1085}
1086
1087// it's critical that we're using UTF-8 encoding if at all possible. since the
1088// client might not have called setlocale(2) (if they weren't reading the
1089// directions...), go ahead and try calling setlocale(LC_ALL, "") and then
1090// setlocale(LC_CTYPE, "C.UTF-8") ourselves *iff* we're not using UTF-8 *and*
1091// LANG is not explicitly set to "C" nor "POSIX". this still requires the user
1092// to have a proper locale generated and available on disk. either way, they're
1093// going to get a diagnostic (unless the user has explicitly configured a LANG
1094// of "C" or "POSIX"). recommended practice is for the client code to have
1095// called setlocale() themselves, and set the NCOPTION_INHIBIT_SETLOCALE flag.
1096// if that flag is set, we take the locale and encoding as we get them.
1097void init_lang(void){
1098#ifdef __MINGW32__
1099 if(setlocale(LC_ALL, ".UTF8") == NULL){
1100 logwarn("couldn't set LC_ALL to utf8");
1101 }
1102#endif
1103 const char* encoding = nl_langinfo(CODESET);
1104 if(encoding && encoding_is_utf8(encoding)){
1105 return; // already utf-8, great!
1106 }
1107 const char* lang = getenv("LANG");
1108 // if LANG was explicitly set to C/POSIX, life sucks, roll with it
1109 if(lang && (!strcmp(lang, "C") || !strcmp(lang, "POSIX"))){
1110 loginfo("LANG was explicitly set to %s, not changing locale", lang);
1111 return;
1112 }
1113#ifndef __MINGW32__
1114 if(setlocale(LC_ALL, "") == NULL){
1115 logwarn("setting locale based on LANG failed");
1116 }
1117#endif
1118 encoding = nl_langinfo(CODESET);
1119 if(encoding && encoding_is_utf8(encoding)){
1120 loginfo("set locale from LANG; client should call setlocale(2)!");
1121 return;
1122 }
1123 setlocale(LC_CTYPE, "C.UTF-8");
1124 encoding = nl_langinfo(CODESET);
1125 if(encoding && encoding_is_utf8(encoding)){
1126 loginfo("forced UTF-8 encoding; client should call setlocale(2)!");
1127 return;
1128 }
1129}
1130
1131// initialize a recursive mutex lock in a way that works on both glibc + musl
1132static int
1133recursive_lock_init(pthread_mutex_t *lock){
1134#ifndef __GLIBC__
1135#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE
1136#endif
1137 pthread_mutexattr_t attr;
1138 if(pthread_mutexattr_init(&attr)){
1139 return -1;
1140 }
1141 if(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP)){
1142 pthread_mutexattr_destroy(&attr);
1143 return -1;
1144 }
1145 if(pthread_mutex_init(lock, &attr)){
1146 pthread_mutexattr_destroy(&attr);
1147 return -1;
1148 }
1149 pthread_mutexattr_destroy(&attr);
1150 return 0;
1151#ifndef __GLIBC__
1152#undef PTHREAD_MUTEX_RECURSIVE_NP
1153#endif
1154}
1155
1157 if(nc->tcache.cellpxy == 0 || nc->tcache.cellpxx == 0){
1158 return NCPIXEL_NONE;
1159 }
1160 return nc->tcache.pixel_implementation;
1161}
1162
1163// the earliest stage of initialization. logging does not yet work; all
1164// diagostics should be emitted with fprintf(stderr).
1165// * validates |opts|, if not NULL
1166// * creates and zeroes out the notcurses struct
1167// * ensures that we're using ASCII or UTF8 and calls setlocale(3)
1168// * checks the environment for NOTCURSES_LOGLEVEL and sets ret->loglevel
1169// * writes TERM to the environment, if specified via opts->termtype
1170//
1171// iff we're using UTF8, |utf8| will be set to 1. it is otherwise set to 0.
1172__attribute__ ((nonnull (2))) static notcurses*
1173notcurses_early_init(const struct notcurses_options* opts, FILE* fp, unsigned* utf8){
1174 if(fwide(fp, 0) > 0){
1175 fprintf(stderr, "error: output stream is wide-oriented");
1176 return NULL;
1177 }
1178 notcurses* ret = malloc(sizeof(*ret));
1179 if(ret == NULL){
1180 return ret;
1181 }
1182 memset(ret, 0, sizeof(*ret));
1183 if(opts){
1184 if(opts->flags >= (NCOPTION_SCROLLING << 1u)){
1185 fprintf(stderr, "warning: unknown Notcurses options %016" PRIu64, opts->flags);
1186 }
1187 if(opts->termtype){
1188 if(putenv_term(opts->termtype)){
1189 free(ret);
1190 return NULL;
1191 }
1192 }
1193 ret->flags = opts->flags;
1194 ret->margin_t = opts->margin_t;
1195 ret->margin_b = opts->margin_b;
1196 ret->margin_l = opts->margin_l;
1197 ret->margin_r = opts->margin_r;
1198 ret->loglevel = opts->loglevel;
1199 }
1201 if(!(ret->flags & NCOPTION_INHIBIT_SETLOCALE)){
1202 init_lang();
1203 }
1204//fprintf(stderr, "getenv LC_ALL: %s LC_CTYPE: %s\n", getenv("LC_ALL"), getenv("LC_CTYPE"));
1205 const char* encoding = nl_langinfo(CODESET);
1206 if(encoding && encoding_is_utf8(encoding)){
1207 *utf8 = true;
1208 }else{
1209 *utf8 = false;
1210 if(encoding && (strcmp(encoding, "ANSI_X3.4-1968") &&
1211 strcmp(encoding, "US-ASCII") &&
1212 strcmp(encoding, "ASCII"))){
1213 fprintf(stderr, "encoding (\"%s\") was neither ANSI_X3.4-1968 nor UTF-8, refusing to start\n did you call setlocale()?\n",
1214 encoding ? encoding : "none found");
1215 free(ret);
1216 return NULL;
1217 }
1218 }
1219 ret->cursory = ret->cursorx = -1;
1220 reset_stats(&ret->stats.s);
1222 ret->ttyfp = fp;
1223 egcpool_init(&ret->pool);
1225 fprintf(stderr, "invalid loglevel %d", ret->loglevel);
1226 free(ret);
1227 return NULL;
1228 }
1229 if(recursive_lock_init(&ret->pilelock)){
1230 fprintf(stderr, "couldn't initialize pile mutex");
1231 free(ret);
1232 return NULL;
1233 }
1234 if(pthread_mutex_init(&ret->stats.lock, NULL)){
1235 pthread_mutex_destroy(&ret->pilelock);
1236 free(ret);
1237 return NULL;
1238 }
1239 if(*utf8){
1241 }
1242 return ret;
1243}
1244
1246 if(outfp == NULL){
1247 outfp = stdout;
1248 }
1249 unsigned utf8;
1250 // ret comes out entirely zero-initialized
1251 notcurses* ret = notcurses_early_init(opts, outfp, &utf8);
1252 if(ret == NULL){
1253 return NULL;
1254 }
1255 // the fbuf is needed by notcurses_stop_minimal, so this must be done
1256 // before registering fatal signal handlers.
1257 if(fbuf_init(&ret->rstate.f)){
1258 pthread_mutex_destroy(&ret->pilelock);
1259 pthread_mutex_destroy(&ret->stats.lock);
1260 free(ret);
1261 return NULL;
1262 }
1265 notcurses_stop_minimal)){
1266 fbuf_free(&ret->rstate.f);
1267 pthread_mutex_destroy(&ret->pilelock);
1268 pthread_mutex_destroy(&ret->stats.lock);
1269 free(ret);
1270 return NULL;
1271 }
1272 // don't set loglevel until we've acquired the signal handler, lest we
1273 // change the loglevel out from under a running instance
1274 loglevel = ret->loglevel;
1275 ret->rstate.logendy = -1;
1276 ret->rstate.logendx = -1;
1277 ret->rstate.x = ret->rstate.y = -1;
1278 int fakecursory = ret->rstate.logendy;
1279 int fakecursorx = ret->rstate.logendx;
1280 int* cursory = ret->flags & NCOPTION_PRESERVE_CURSOR ?
1281 &ret->rstate.logendy : &fakecursory;
1282 int* cursorx = ret->flags & NCOPTION_PRESERVE_CURSOR ?
1283 &ret->rstate.logendx : &fakecursorx;
1284 if(interrogate_terminfo(&ret->tcache, ret->ttyfp, utf8,
1287 cursory, cursorx, &ret->stats,
1288 ret->margin_l, ret->margin_t,
1289 ret->margin_r, ret->margin_b,
1290 ret->flags & NCOPTION_DRAIN_INPUT)){
1291 void* altstack;
1292 fbuf_free(&ret->rstate.f);
1293 pthread_mutex_destroy(&ret->pilelock);
1294 pthread_mutex_destroy(&ret->stats.lock);
1295 drop_signals(ret, &altstack);
1296 free(ret);
1297 free(altstack);
1298 return NULL;
1299 }
1300 if(ret->tcache.maxpaletteread > -1){
1301 memcpy(ret->palette.chans, ret->tcache.originalpalette.chans,
1302 sizeof(*ret->palette.chans) * (ret->tcache.maxpaletteread + 1));
1303 }
1304 if((ret->flags & NCOPTION_PRESERVE_CURSOR) ||
1305 (!(ret->flags & NCOPTION_SUPPRESS_BANNERS))){
1306 // the u7 led the queries so that we would get a cursor position
1307 // unaffected by any query spill (unconsumed control sequences). move
1308 // us back to that location, in case there was any such spillage.
1309 if(*cursory < 0 || *cursorx < 0){
1310 unsigned cy, cx;
1311 if(locate_cursor(&ret->tcache, &cy, &cx)){
1312 logwarn("couldn't preserve cursor");
1313 }else{
1314 *cursory = cy;
1315 *cursorx = cx;
1316 }
1317 }
1318 if(*cursory >= 0 && *cursorx >= 0){
1319 if(goto_location(ret, &ret->rstate.f, *cursory, *cursorx, NULL)){
1320 goto err;
1321 }
1322 }
1323 }
1324 unsigned dimy, dimx, cgeo, pgeo; // latter two are don't-cares
1325 if(update_term_dimensions(&dimy, &dimx, &ret->tcache, ret->margin_b, &cgeo, &pgeo)){
1326 goto err;
1327 }
1328 if(ncvisual_init(ret->loglevel)){
1329 goto err;
1330 }
1331 ret->stdplane = NULL;
1332 if((ret->stdplane = create_initial_ncplane(ret, dimy, dimx)) == NULL){
1333 logpanic("couldn't create the initial plane (bad margins?)");
1334 goto err;
1335 }
1336 if(ret->flags & NCOPTION_SCROLLING){
1337 ncplane_set_scrolling(ret->stdplane, true);
1338 }
1339 reset_term_attributes(&ret->tcache, &ret->rstate.f);
1340 const char* cinvis = get_escape(&ret->tcache, ESCAPE_CIVIS);
1341 if(cinvis && fbuf_emit(&ret->rstate.f, cinvis) < 0){
1342 free_plane(ret->stdplane);
1343 goto err;
1344 }
1345 const char* pushcolors = get_escape(&ret->tcache, ESCAPE_SAVECOLORS);
1346 if(pushcolors && fbuf_emit(&ret->rstate.f, pushcolors)){
1347 free_plane(ret->stdplane);
1348 goto err;
1349 }
1350 init_banner(ret, &ret->rstate.f);
1351 if(fbuf_flush(&ret->rstate.f, ret->ttyfp) < 0){
1352 free_plane(ret->stdplane);
1353 goto err;
1354 }
1355 if(ret->rstate.logendy >= 0){ // if either is set, both are
1356 if(!(ret->flags & NCOPTION_SUPPRESS_BANNERS) && ret->tcache.ttyfd >= 0){
1357 unsigned uendy, uendx;
1358 if(locate_cursor(&ret->tcache, &uendy, &uendx)){
1359 free_plane(ret->stdplane);
1360 goto err;
1361 }
1362 ret->rstate.logendy = uendy;
1363 ret->rstate.logendx = uendx;
1364 }
1367 }
1368 }
1369 if(!(ret->flags & NCOPTION_NO_ALTERNATE_SCREEN)){
1370 // perform an explicit clear since the alternate screen was requested
1371 // (smcup *might* clear, but who knows? and it might not have been
1372 // available in any case).
1373 if(clear_and_home(ret, &ret->tcache, &ret->rstate.f)){
1374 goto err;
1375 }
1376 // no need to reestablish a preserved cursor -- that only affects the
1377 // standard plane, not the physical cursor that was just disrupted.
1378 }
1379 // the sprite clear ought take place within the alternate screen, if it's
1380 // being used.
1381 if(!(ret->flags & NCOPTION_NO_CLEAR_BITMAPS)){
1382 if(sprite_clear_all(&ret->tcache, &ret->rstate.f)){
1383 goto err;
1384 }
1385 }
1386 if(ret->rstate.f.used){
1387 if(fbuf_flush(&ret->rstate.f, ret->ttyfp) < 0){
1388 goto err;
1389 }
1390 }
1391 return ret;
1392
1393err:{
1394 void* altstack;
1395 logpanic("alas, you will not be going to space today.");
1396 notcurses_stop_minimal(ret, &altstack, -1);
1397 fbuf_free(&ret->rstate.f);
1398 if(ret->tcache.ttyfd >= 0 && ret->tcache.tpreserved){
1399 (void)tcsetattr(ret->tcache.ttyfd, TCSAFLUSH, ret->tcache.tpreserved);
1400 free(ret->tcache.tpreserved);
1401 }
1402 del_curterm(cur_term);
1403 pthread_mutex_destroy(&ret->stats.lock);
1404 pthread_mutex_destroy(&ret->pilelock);
1405 free(ret);
1406 free(altstack);
1407 return NULL;
1408 }
1409}
1410
1411// updates *pile to point at (*pile)->next, frees all but standard pile/plane
1412static void
1413ncpile_drop(notcurses* nc, ncpile** pile){
1414 bool sawstdplane = false;
1415 ncpile* next = (*pile)->next;
1416 ncplane* p = (*pile)->top;
1417 while(p){
1418 ncplane* tmp = p->below;
1419 logdebug("killing plane %p, next is %p", p, tmp);
1420 if(nc->stdplane != p){
1421 free_plane(p);
1422 }else{
1423 sawstdplane = true;
1424 }
1425 p = tmp;
1426 }
1427 *pile = next;
1428 if(sawstdplane){
1429 ncplane_pile(nc->stdplane)->top = nc->stdplane;
1430 ncplane_pile(nc->stdplane)->bottom = nc->stdplane;
1431 nc->stdplane->above = nc->stdplane->below = NULL;
1432 nc->stdplane->blist = NULL;
1433 }
1434}
1435
1436// drop all piles and all planes, save the standard plane and its pile
1438 logdebug("we have some planes");
1439 pthread_mutex_lock(&nc->pilelock);
1440 ncpile* p = ncplane_pile(nc->stdplane);
1441 ncpile* p0 = p;
1442 do{
1443 ncpile_drop(nc, &p);
1444 }while(p0 != p);
1445 pthread_mutex_unlock(&nc->pilelock);
1446 logdebug("all planes dropped");
1447}
1448
1450 logdebug("stopping notcurses");
1451//notcurses_debug(nc, stderr);
1452 int ret = 0;
1453 if(nc){
1454 void* altstack;
1455 ret |= notcurses_stop_minimal(nc, &altstack, 0);
1456 // if we were not using the alternate screen, our cursor's wherever we last
1457 // wrote. move it to the furthest place to which it advanced.
1458 if(!get_escape(&nc->tcache, ESCAPE_SMCUP)){
1459 fbuf_reset(&nc->rstate.f);
1460//fprintf(stderr, "CLOSING TO %d/%d\n", nc->rstate.logendy, nc->rstate.logendx);
1461 goto_location(nc, &nc->rstate.f, nc->rstate.logendy, nc->rstate.logendx, NULL);
1462//fprintf(stderr, "***"); fflush(stderr);
1463 fbuf_finalize(&nc->rstate.f, stdout);
1464 }
1465 if(nc->stdplane){
1467 free_plane(nc->stdplane);
1468 }
1469 if(nc->tcache.ttyfd >= 0){
1470 ret |= close(nc->tcache.ttyfd);
1471 }
1472 egcpool_dump(&nc->pool);
1473 free(nc->lastframe);
1474 // perhaps surprisingly, this stops the input thread
1476 // get any current stats loaded into stash_stats
1478 if(!(nc->flags & NCOPTION_SUPPRESS_BANNERS)){
1479 summarize_stats(nc);
1480 }
1481#ifndef __MINGW32__
1482 del_curterm(cur_term);
1483#endif
1484 ret |= pthread_mutex_destroy(&nc->stats.lock);
1485 ret |= pthread_mutex_destroy(&nc->pilelock);
1486 fbuf_free(&nc->rstate.f);
1487 free(nc);
1488 free(altstack);
1489 }
1490 return ret;
1491}
1492
1493uint64_t ncplane_channels(const ncplane* n){
1494 return ncchannels_channels(n->channels);
1495}
1496
1498 ncchannels_set_channels(&n->channels, channels);
1499}
1500
1501uint16_t ncplane_styles(const ncplane* n){
1502 return n->stylemask;
1503}
1504
1506 ncchannels_set_fg_default(&n->channels);
1507}
1508
1510 ncchannels_set_bg_default(&n->channels);
1511}
1512
1513void ncplane_set_bg_rgb8_clipped(ncplane* n, int r, int g, int b){
1514 ncchannels_set_bg_rgb8_clipped(&n->channels, r, g, b);
1515}
1516
1517int ncplane_set_bg_rgb8(ncplane* n, unsigned r, unsigned g, unsigned b){
1518 return ncchannels_set_bg_rgb8(&n->channels, r, g, b);
1519}
1520
1521void ncplane_set_fg_rgb8_clipped(ncplane* n, int r, int g, int b){
1522 ncchannels_set_fg_rgb8_clipped(&n->channels, r, g, b);
1523}
1524
1525int ncplane_set_fg_rgb8(ncplane* n, unsigned r, unsigned g, unsigned b){
1526 return ncchannels_set_fg_rgb8(&n->channels, r, g, b);
1527}
1528
1529int ncplane_set_fg_rgb(ncplane* n, unsigned channel){
1530 return ncchannels_set_fg_rgb(&n->channels, channel);
1531}
1532
1533uint64_t ncplane_set_bchannel(ncplane* n, uint32_t channel){
1534 return ncchannels_set_bchannel(&n->channels, channel);
1535}
1536
1537uint64_t ncplane_set_fchannel(ncplane* n, uint32_t channel){
1538 return ncchannels_set_fchannel(&n->channels, channel);
1539}
1540
1541int ncplane_set_bg_rgb(ncplane* n, unsigned channel){
1542 return ncchannels_set_bg_rgb(&n->channels, channel);
1543}
1544
1546 return ncchannels_set_fg_alpha(&n->channels, alpha);
1547}
1548
1550 return ncchannels_set_bg_alpha(&n->channels, alpha);
1551}
1552
1554 return ncchannels_set_fg_palindex(&n->channels, idx);
1555}
1556
1558 return ncchannels_set_bg_palindex(&n->channels, idx);
1559}
1560
1562 if(nccell_wide_right_p(c)){
1563 return -1;
1564 }
1565 return nccell_duplicate(ncp, &ncp->basecell, c);
1566}
1567
1568int ncplane_set_base(ncplane* ncp, const char* egc, uint16_t stylemask, uint64_t channels){
1569 return nccell_prime(ncp, &ncp->basecell, egc, stylemask, channels);
1570}
1571
1573 return nccell_duplicate(ncp, c, &ncp->basecell);
1574}
1575
1576const char* nccell_extended_gcluster(const ncplane* n, const nccell* c){
1577 if(cell_simple_p(c)){
1578 return (const char*)&c->gcluster;
1579 }
1580 return egcpool_extended_gcluster(&n->pool, c);
1581}
1582
1583// 'n' ends up above 'above'
1584int ncplane_move_above(ncplane* restrict n, ncplane* restrict above){
1585 if(n == above){ // probably gets optimized out =/
1586 return -1;
1587 }
1588 ncpile* p = ncplane_pile(n);
1589 if(above == NULL){
1590 if(n->below){
1591 if( (n->below->above = n->above) ){
1592 n->above->below = n->below;
1593 }else{
1594 p->top = n->below;
1595 }
1596 n->below = NULL;
1597 if( (n->above = p->bottom) ){
1598 n->above->below = n;
1599 }
1600 p->bottom = n;
1601 }
1602 return 0;
1603 }
1604 if(n->below != above){
1605 if(p != ncplane_pile(above)){ // can't move among piles
1606 return -1;
1607 }
1608 // splice out 'n'
1609 if(n->below){
1610 n->below->above = n->above;
1611 }else{
1612 p->bottom = n->above;
1613 }
1614 if(n->above){
1615 n->above->below = n->below;
1616 }else{
1617 p->top = n->below;
1618 }
1619 if( (n->above = above->above) ){
1620 above->above->below = n;
1621 }else{
1622 p->top = n;
1623 }
1624 above->above = n;
1625 n->below = above;
1626 }
1627 return 0;
1628}
1629
1630// 'n' ends up below 'below', or on top if 'below' == NULL
1631int ncplane_move_below(ncplane* restrict n, ncplane* restrict below){
1632 if(n == below){ // probably gets optimized out =/
1633 return -1;
1634 }
1635 ncpile* p = ncplane_pile(n);
1636 if(below == NULL){
1637 if(n->above){
1638 if( (n->above->below = n->below) ){
1639 n->below->above = n->above;
1640 }else{
1641 p->bottom = n->above;
1642 }
1643 n->above = NULL;
1644 if( (n->below = p->top) ){
1645 n->below->above = n;
1646 }
1647 p->top = n;
1648 }
1649 return 0;
1650 }
1651 if(n->above != below){
1652 if(p != ncplane_pile(below)){ // can't move among piles
1653 return -1;
1654 }
1655 if(n->below){
1656 n->below->above = n->above;
1657 }else{
1658 p->bottom = n->above;
1659 }
1660 if(n->above){
1661 n->above->below = n->below;
1662 }else{
1663 p->top = n->below;
1664 }
1665 if( (n->below = below->below) ){
1666 below->below->above = n;
1667 }else{
1668 p->bottom = n;
1669 }
1670 below->below = n;
1671 n->above = below;
1672 }
1673 return 0;
1674}
1675
1676// if above is NULL, we're moving to the bottom
1677int ncplane_move_family_above(ncplane* restrict n, ncplane* restrict bpoint){
1680 if(ncplane_move_above(n, bpoint)){
1681 return -1;
1682 }
1683 // traverse the planes above n, until we hit NULL. do the planes above n
1684 // first, so that we know the topmost element of our new ensplicification.
1685 // at this point, n is the bottommost plane, and we're inserting above it.
1686 ncplane* targ = n;
1687 while(above && above != n){
1688 ncplane* tmp = ncplane_above(above);
1689 if(ncplane_descendant_p(above, n)){
1691 targ = above;
1692 }
1693 above = tmp;
1694 }
1695 // n remains the topmost plane, and we're inserting above it. we have to be
1696 // careful this time not to cross into any we moved below n.
1697 const ncplane* topmost = targ;
1698 targ = n;
1699 while(below && below != topmost){
1700 ncplane* tmp = ncplane_below(below);
1701 if(ncplane_descendant_p(below, n)){
1703 targ = below;
1704 }
1705 below = tmp;
1706 }
1707 return 0;
1708}
1709
1710// if below is NULL, we're moving to the top
1711int ncplane_move_family_below(ncplane* restrict n, ncplane* restrict bpoint){
1714 if(ncplane_move_below(n, bpoint)){
1715 return -1;
1716 }
1717 // traverse the planes below n, until we hit NULL. do the planes below n
1718 // first, so that we know the bottommost element of our new ensplicification.
1719 // we're inserting below n...
1720 ncplane* targ = n;
1721 while(below && below != n){
1722 ncplane* tmp = ncplane_below(below);
1723 if(ncplane_descendant_p(below, n)){
1725 targ = below;
1726 }
1727 below = tmp;
1728 }
1729 // n remains the topmost plane, and we're inserting above it. we have to be
1730 // careful this time not to cross into any we moved below n.
1731 const ncplane* bottommost = targ;
1732 targ = n;
1733 while(above && above != bottommost){
1734 ncplane* tmp = ncplane_above(above);
1735 if(ncplane_descendant_p(above, n)){
1737 targ = above;
1738 }
1739 above = tmp;
1740 }
1741 return 0;
1742}
1743
1744void ncplane_cursor_yx(const ncplane* n, unsigned* y, unsigned* x){
1745 if(y){
1746 *y = n->y;
1747 }
1748 if(x){
1749 *x = n->x;
1750 }
1751}
1752
1753static inline void
1754nccell_obliterate(ncplane* n, nccell* c){
1755 nccell_release(n, c);
1756 nccell_init(c);
1757}
1758
1759// increment y by 1 and rotate the framebuffer up one line. x moves to 0. any
1760// non-fixed bound planes move up 1 line if they intersect the plane.
1762//fprintf(stderr, "pre-scroll: %d/%d %d/%d log: %d scrolling: %u\n", n->y, n->x, n->leny, n->lenx, n->logrow, n->scrolling);
1763 n->x = 0;
1764 if(n->y == n->leny - 1){
1765 // we're on the last line of the plane
1766 if(n->autogrow){
1767 ncplane_resize_simple(n, n->leny + 1, n->lenx);
1768 ncplane_cursor_move_yx(n, n->leny - 1, 0);
1769 return;
1770 }
1771 // we'll actually be scrolling material up and out, and making a new line.
1772 // if this is the standard plane, that means a "physical" scroll event is
1773 // called for.
1775 ncplane_pile(n)->scrolls++;
1776 }
1777 n->logrow = (n->logrow + 1) % n->leny;
1778 nccell* row = n->fb + nfbcellidx(n, n->y, 0);
1779 for(unsigned clearx = 0 ; clearx < n->lenx ; ++clearx){
1780 nccell_release(n, &row[clearx]);
1781 }
1782 memset(row, 0, sizeof(*row) * n->lenx);
1783 for(struct ncplane* c = n->blist ; c ; c = c->bnext){
1784 if(!c->fixedbound){
1785 if(ncplanes_intersect_p(n, c)){
1786 ncplane_move_rel(c, -1, 0);
1787 }
1788 }
1789 }
1790 }else{
1791 ++n->y;
1792 }
1793}
1794
1796 if(!ncplane_scrolling_p(n)){
1797 logerror("can't scroll %d on non-scrolling plane", r);
1798 return -1;
1799 }
1800 if(r < 0){
1801 logerror("can't scroll %d lines", r);
1802 return -1;
1803 }
1804 while(r-- > 0){
1805 scroll_down(n);
1806 }
1808 notcurses_render(ncplane_notcurses(n));
1809 }
1810 return 0;
1811}
1812
1813// Scroll |n| up until |child| is no longer hidden beneath it. Returns an
1814// error if |child| is not a child of |n|, or |n| is not scrolling, or |child|
1815// is fixed. Returns the number of scrolling events otherwise (might be 0).
1817 if(!ncplane_descendant_p(child, n)){
1818 logerror("not a descendant of specified plane");
1819 return -1;
1820 }
1821 if(child->fixedbound){
1822 logerror("child plane is fixed");
1823 return -1;
1824 }
1825 int parend = ncplane_abs_y(n) + ncplane_dim_y(n) - 1; // where parent ends
1826 int chend = ncplane_abs_y(child) + ncplane_dim_y(child) - 1; // where child ends
1827 if(chend <= parend){
1828 return 0;
1829 }
1830 int r = chend - parend; // how many rows we need scroll parent
1831 ncplane_cursor_move_yx(n, ncplane_dim_y(n) - 1, 0);
1832 int ret = ncplane_scrollup(n, r);
1833 return ret;
1834}
1835
1836int nccell_load(ncplane* n, nccell* c, const char* gcluster){
1837 int cols;
1838 int bytes = utf8_egc_len(gcluster, &cols);
1839 return pool_load_direct(&n->pool, c, gcluster, bytes, cols);
1840}
1841
1842// where the magic happens. write the single EGC completely described by |egc|,
1843// occupying |cols| columns, to the ncplane |n| at the coordinate |y|, |x|. if
1844// either or both of |y|/|x| is -1, the current cursor location for that
1845// dimension will be used. if the glyph cannot fit on the current line, it is
1846// an error unless scrolling is enabled.
1847static int
1848ncplane_put(ncplane* n, int y, int x, const char* egc, int cols,
1849 uint16_t stylemask, uint64_t channels, int bytes){
1850 if(n->sprite){
1851 logerror("can't write [%s] to sprixelated plane", egc);
1852 return -1;
1853 }
1854 // reject any control character for output other than newline (and then only
1855 // on a scrolling plane) and tab.
1856 if(is_control_egc((const unsigned char*)egc, bytes)){
1857 if(*egc == '\n'){
1858 // if we're not scrolling, autogrow would be to the right (as opposed to
1859 // down), and thus it still wouldn't apply to the case of a newline.
1860 if(!n->scrolling){
1861 logerror("rejecting newline on non-scrolling plane");
1862 return -1;
1863 }
1864 }else if(*egc != '\t'){
1865 logerror("rejecting %dB control character", bytes);
1866 return -1;
1867 }
1868 }
1869 // check *before ncplane_cursor_move_yx()* whether we're past the end of the
1870 // line. if scrolling is enabled, move to the next line if so. if x or y are
1871 // specified, we must always try to print at exactly that location, and
1872 // there's no need to check the present location in that dimension.
1873 bool linesend = false;
1874 if(x < 0){
1875 // we checked x for all negatives, but only -1 is valid (our else clause is
1876 // predicated on a non-negative x).
1877 if(x == -1){
1878 if(n->x + cols - 1 >= n->lenx){
1879 linesend = true;
1880 }
1881 }
1882 }else{
1883 if((unsigned)x + cols - 1 >= n->lenx){
1884 linesend = true;
1885 }
1886 }
1887 bool scrolled = false;
1888 if(linesend){
1889 if(n->scrolling){
1890 scroll_down(n);
1891 scrolled = true;
1892 }else if(n->autogrow){
1893 ncplane_resize_simple(n, n->leny, n->lenx + cols);
1894 }else{
1895 logerror("target x %d [%.*s] > length %d", n->x, bytes, egc, n->lenx);
1896 return -1;
1897 }
1898 }
1899 // targets outside the plane will be rejected here.
1900 if(ncplane_cursor_move_yx(n, y, x)){
1901 return -1;
1902 }
1903 if(*egc == '\n'){
1904 scroll_down(n);
1905 scrolled = true;
1906 }
1907 // A wide character obliterates anything to its immediate right (and marks
1908 // that cell as wide). Any character placed atop one cell of a wide character
1909 // obliterates all cells. Note that a two-cell glyph can thus obliterate two
1910 // other two-cell glyphs, totalling four columns.
1911 nccell* targ = ncplane_cell_ref_yx(n, n->y, n->x);
1912 // we're always starting on the leftmost cell of our output glyph. check the
1913 // target, and find the leftmost cell of the glyph it will be displacing.
1914 // obliterate as we go along.
1915 int idx = n->x;
1916 nccell* lmc = targ;
1917 while(nccell_wide_right_p(lmc)){
1918 nccell_obliterate(n, &n->fb[nfbcellidx(n, n->y, idx)]);
1919 lmc = ncplane_cell_ref_yx(n, n->y, --idx);
1920 }
1921 // we're now on the leftmost cell of the target glyph.
1922 int twidth = nccell_cols(lmc);
1923 nccell_release(n, lmc);
1924 twidth -= n->x - idx;
1925 while(--twidth > 0){
1926 nccell_obliterate(n, &n->fb[nfbcellidx(n, n->y, n->x + twidth)]);
1927 }
1928 targ->stylemask = stylemask;
1929 targ->channels = channels;
1930 // tabs get replaced with spaces, up to the next tab stop. we always try to
1931 // write at least one. if there are no more tabstops on the current line, if
1932 // autogrowing to the right, extend as necessary. otherwise, if scrolling,
1933 // move to the next line. otherwise, simply fill any spaces we can. this has
1934 // already taken place by the time we get here, if it ought have happened.
1935 if(*egc == '\t'){
1936 cols = TABSTOP - (n->x % TABSTOP);
1937 if(n->x + 1 >= n->lenx){
1938 if(!n->scrolling && n->autogrow){
1939 ncplane_resize_simple(n, n->leny, n->lenx + (cols ? cols - 1 : TABSTOP - 1));
1940 // must refresh targ; resize invalidated it
1941 targ = ncplane_cell_ref_yx(n, n->y, n->x);
1942 }
1943 }
1944 if(cell_load_direct(n, targ, " ", bytes, 1) < 0){
1945 return -1;
1946 }
1947 }else{
1948 if(cell_load_direct(n, targ, egc, bytes, cols) < 0){
1949 return -1;
1950 }
1951 }
1952 //loginfo("%08x %016lx %c %d %d", targ->gcluster, targ->channels, nccell_double_wide_p(targ) ? 'D' : 'd', bytes, cols);
1953 if(*egc != '\n'){
1954 ++n->x;
1955 // if wide, set our right hand columns wide, and check for further damage
1956 for(int i = 1 ; i < cols ; ++i){
1957 nccell* candidate = &n->fb[nfbcellidx(n, n->y, n->x)];
1958 int off = nccell_cols(candidate);
1959 nccell_release(n, &n->fb[nfbcellidx(n, n->y, n->x)]);
1960 while(--off > 0){
1961 nccell_obliterate(n, &n->fb[nfbcellidx(n, n->y, n->x + off)]);
1962 }
1963 if(*egc != '\t'){
1964 candidate->channels = targ->channels;
1965 candidate->stylemask = targ->stylemask;
1966 candidate->width = targ->width;
1967 }else{
1968 if(cell_load_direct(n, candidate, " ", bytes, 1) < 0){
1969 return -1;
1970 }
1971 }
1972 // don't do any further expansion of the plane here; just break
1973 if(++n->x >= n->lenx){
1974 break;
1975 }
1976 }
1977 }
1978 if(scrolled){
1980 notcurses_render(ncplane_notcurses(n));
1981 }
1982 }
1983 return cols;
1984}
1985
1986int ncplane_putc_yx(ncplane* n, int y, int x, const nccell* c){
1987 const int cols = nccell_cols(c);
1988 // unfortunately, |c| comes from |n|. the act of writing |c| to |n| could
1989 // grow |n|'s egcpool, invalidating the reference represented by
1990 // nccell_extended_gcluster(). so we must copy and free it.
1991 char* egc = nccell_strdup(n, c);
1992 if(egc == NULL){
1993 logerror("couldn't duplicate cell");
1994 return -1;
1995 }
1996 int r = ncplane_put(n, y, x, egc, cols, c->stylemask, c->channels, strlen(egc));
1997 free(egc);
1998 return r;
1999}
2000
2001int ncplane_putegc_yx(ncplane* n, int y, int x, const char* gclust, size_t* sbytes){
2002 int cols;
2003 int bytes = utf8_egc_len(gclust, &cols);
2004 if(bytes < 0){
2005 return -1;
2006 }
2007 if(sbytes){
2008 *sbytes = bytes;
2009 }
2010//fprintf(stderr, "glust: %s cols: %d wcs: %d\n", gclust, cols, bytes);
2011 return ncplane_put(n, y, x, gclust, cols, n->stylemask, n->channels, bytes);
2012}
2013
2015 uint64_t channels = n->channels;
2016 uint16_t stylemask = n->stylemask;
2017 const nccell* targ = &n->fb[nfbcellidx(n, n->y, n->x)];
2018 n->channels = targ->channels;
2019 n->stylemask = targ->stylemask;
2020 int ret = ncplane_putchar(n, c);
2021 n->channels = channels;
2022 n->stylemask = stylemask;
2023 return ret;
2024}
2025
2026int ncplane_putwegc_stained(ncplane* n, const wchar_t* gclust, size_t* sbytes){
2027 uint64_t channels = n->channels;
2028 uint16_t stylemask = n->stylemask;
2029 const nccell* targ = &n->fb[nfbcellidx(n, n->y, n->x)];
2030 n->channels = targ->channels;
2031 n->stylemask = targ->stylemask;
2032 int ret = ncplane_putwegc(n, gclust, sbytes);
2033 n->channels = channels;
2034 n->stylemask = stylemask;
2035 return ret;
2036}
2037
2038int ncplane_putegc_stained(ncplane* n, const char* gclust, size_t* sbytes){
2039 uint64_t channels = n->channels;
2040 uint16_t stylemask = n->stylemask;
2041 const nccell* targ = &n->fb[nfbcellidx(n, n->y, n->x)];
2042 n->channels = targ->channels;
2043 n->stylemask = targ->stylemask;
2044 int ret = ncplane_putegc(n, gclust, sbytes);
2045 n->channels = channels;
2046 n->stylemask = stylemask;
2047 return ret;
2048}
2049
2050int ncplane_cursor_at(const ncplane* n, nccell* c, char** gclust){
2051 if(n->y >= n->leny || n->x >= n->lenx){
2052 return -1;
2053 }
2054 const nccell* src = &n->fb[nfbcellidx(n, n->y, n->x)];
2055 memcpy(c, src, sizeof(*src));
2056 if(cell_simple_p(c)){
2057 *gclust = NULL;
2058 }else if((*gclust = strdup(nccell_extended_gcluster(n, src))) == NULL){
2059 return -1;
2060 }
2061 return 0;
2062}
2063
2065 return term_supported_styles(&nc->tcache);
2066}
2067
2069 return nc->tcache.caps.colors;
2070}
2071
2073 return termdesc_longterm(&nc->tcache);
2074}
2075
2076// conform to the specified stylebits
2077void ncplane_set_styles(ncplane* n, unsigned stylebits){
2078 n->stylemask = (stylebits & NCSTYLE_MASK);
2079}
2080
2081// turn on any specified stylebits
2082void ncplane_on_styles(ncplane* n, unsigned stylebits){
2083 n->stylemask |= (stylebits & NCSTYLE_MASK);
2084}
2085
2086// turn off any specified stylebits
2087void ncplane_off_styles(ncplane* n, unsigned stylebits){
2088 n->stylemask &= ~(stylebits & NCSTYLE_MASK);
2089}
2090
2091// i hate the big allocation and two copies here, but eh what you gonna do?
2092// well, for one, we don't need the huge allocation FIXME
2093char* ncplane_vprintf_prep(const char* format, va_list ap){
2094 const size_t size = BUFSIZ; // healthy estimate, can embiggen below
2095 char* buf = malloc(size);
2096 if(buf == NULL){
2097 return NULL;
2098 }
2099 va_list vacopy;
2100 va_copy(vacopy, ap);
2101 int ret = vsnprintf(buf, size, format, ap);
2102 if(ret < 0){
2103 free(buf);
2104 va_end(vacopy);
2105 return NULL;
2106 }
2107 if((size_t)ret >= size){
2108 char* tmp = realloc(buf, ret + 1);
2109 if(tmp == NULL){
2110 free(buf);
2111 va_end(vacopy);
2112 return NULL;
2113 }
2114 buf = tmp;
2115 vsprintf(buf, format, vacopy);
2116 }
2117 va_end(vacopy);
2118 return buf;
2119}
2120
2121int ncplane_vprintf_yx(ncplane* n, int y, int x, const char* format, va_list ap){
2122 char* r = ncplane_vprintf_prep(format, ap);
2123 if(r == NULL){
2124 return -1;
2125 }
2126 int ret = ncplane_putstr_yx(n, y, x, r);
2127 free(r);
2128 return ret;
2129}
2130
2132 const char* format, va_list ap){
2133 char* r = ncplane_vprintf_prep(format, ap);
2134 if(r == NULL){
2135 return -1;
2136 }
2137 int ret = ncplane_putstr_aligned(n, y, align, r);
2138 free(r);
2139 return ret;
2140}
2141
2142int ncplane_vprintf_stained(struct ncplane* n, const char* format, va_list ap){
2143 char* r = ncplane_vprintf_prep(format, ap);
2144 if(r == NULL){
2145 return -1;
2146 }
2147 int ret = ncplane_putstr_stained(n, r);
2148 free(r);
2149 return ret;
2150}
2151
2152int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* str){
2153 char* chopped = strndup(str, s);
2154 int ret = ncplane_putstr_aligned(n, y, align, chopped);
2155 free(chopped);
2156 return ret;
2157}
2158
2159int ncplane_hline_interp(ncplane* n, const nccell* c, unsigned len,
2160 uint64_t c1, uint64_t c2){
2161 if(len <= 0){
2162 logerror("passed invalid length %u", len);
2163 return -1;
2164 }
2165 unsigned ur, ug, ub;
2166 int r1, g1, b1, r2, g2, b2;
2167 int br1, bg1, bb1, br2, bg2, bb2;
2168 ncchannels_fg_rgb8(c1, &ur, &ug, &ub);
2169 r1 = ur; g1 = ug; b1 = ub;
2170 ncchannels_fg_rgb8(c2, &ur, &ug, &ub);
2171 r2 = ur; g2 = ug; b2 = ub;
2172 ncchannels_bg_rgb8(c1, &ur, &ug, &ub);
2173 br1 = ur; bg1 = ug; bb1 = ub;
2174 ncchannels_bg_rgb8(c2, &ur, &ug, &ub);
2175 br2 = ur; bg2 = ug; bb2 = ub;
2176 int deltr = r2 - r1;
2177 int deltg = g2 - g1;
2178 int deltb = b2 - b1;
2179 int deltbr = br2 - br1;
2180 int deltbg = bg2 - bg1;
2181 int deltbb = bb2 - bb1;
2182 unsigned ret;
2184 if(nccell_duplicate(n, &dupc, c) < 0){
2185 return -1;
2186 }
2187 bool fgdef = false, bgdef = false;
2188 if(ncchannels_fg_default_p(c1) && ncchannels_fg_default_p(c2)){
2189 fgdef = true;
2190 }
2191 if(ncchannels_bg_default_p(c1) && ncchannels_bg_default_p(c2)){
2192 bgdef = true;
2193 }
2194 for(ret = 0 ; ret < len ; ++ret){
2195 int r = (deltr * (int)ret) / (int)len + r1;
2196 int g = (deltg * (int)ret) / (int)len + g1;
2197 int b = (deltb * (int)ret) / (int)len + b1;
2198 int br = (deltbr * (int)ret) / (int)len + br1;
2199 int bg = (deltbg * (int)ret) / (int)len + bg1;
2200 int bb = (deltbb * (int)ret) / (int)len + bb1;
2201 if(!fgdef){
2202 nccell_set_fg_rgb8(&dupc, r, g, b);
2203 }
2204 if(!bgdef){
2205 nccell_set_bg_rgb8(&dupc, br, bg, bb);
2206 }
2207 if(ncplane_putc(n, &dupc) <= 0){
2208 return -1;
2209 }
2210 }
2211 nccell_release(n, &dupc);
2212 return ret;
2213}
2214
2215int ncplane_vline_interp(ncplane* n, const nccell* c, unsigned len,
2216 uint64_t c1, uint64_t c2){
2217 if(len <= 0){
2218 logerror("passed invalid length %u", len);
2219 return -1;
2220 }
2221 unsigned ur, ug, ub;
2222 int r1, g1, b1, r2, g2, b2;
2223 int br1, bg1, bb1, br2, bg2, bb2;
2224 ncchannels_fg_rgb8(c1, &ur, &ug, &ub);
2225 r1 = ur; g1 = ug; b1 = ub;
2226 ncchannels_fg_rgb8(c2, &ur, &ug, &ub);
2227 r2 = ur; g2 = ug; b2 = ub;
2228 ncchannels_bg_rgb8(c1, &ur, &ug, &ub);
2229 br1 = ur; bg1 = ug; bb1 = ub;
2230 ncchannels_bg_rgb8(c2, &ur, &ug, &ub);
2231 br2 = ur; bg2 = ug; bb2 = ub;
2232 int deltr = (r2 - r1) / ((int)len + 1);
2233 int deltg = (g2 - g1) / ((int)len + 1);
2234 int deltb = (b2 - b1) / ((int)len + 1);
2235 int deltbr = (br2 - br1) / ((int)len + 1);
2236 int deltbg = (bg2 - bg1) / ((int)len + 1);
2237 int deltbb = (bb2 - bb1) / ((int)len + 1);
2238 unsigned ypos, xpos;
2239 unsigned ret;
2240 ncplane_cursor_yx(n, &ypos, &xpos);
2242 if(nccell_duplicate(n, &dupc, c) < 0){
2243 return -1;
2244 }
2245 bool fgdef = false, bgdef = false;
2246 if(ncchannels_fg_default_p(c1) && ncchannels_fg_default_p(c2)){
2247 fgdef = true;
2248 }
2249 if(ncchannels_bg_default_p(c1) && ncchannels_bg_default_p(c2)){
2250 bgdef = true;
2251 }
2252 for(ret = 0 ; ret < len ; ++ret){
2253 if(ncplane_cursor_move_yx(n, ypos + ret, xpos)){
2254 return -1;
2255 }
2256 r1 += deltr;
2257 g1 += deltg;
2258 b1 += deltb;
2259 br1 += deltbr;
2260 bg1 += deltbg;
2261 bb1 += deltbb;
2262 if(!fgdef){
2263 nccell_set_fg_rgb8(&dupc, r1, g1, b1);
2264 }
2265 if(!bgdef){
2266 nccell_set_bg_rgb8(&dupc, br1, bg1, bb1);
2267 }
2268 if(ncplane_putc(n, &dupc) <= 0){
2269 return -1;
2270 }
2271 }
2272 nccell_release(n, &dupc);
2273 return ret;
2274}
2275
2276// we must have at least 2x2, or it's an error
2277int ncplane_box(ncplane* n, const nccell* ul, const nccell* ur,
2278 const nccell* ll, const nccell* lr, const nccell* hl,
2279 const nccell* vl, unsigned ystop, unsigned xstop,
2280 unsigned ctlword){
2281 unsigned yoff, xoff;
2282 ncplane_cursor_yx(n, &yoff, &xoff);
2283 // must be at least 2x2, with its upper-left corner at the current cursor
2284 if(ystop < yoff + 1){
2285 logerror("ystop (%u) insufficient for yoff (%d)", ystop, yoff);
2286 return -1;
2287 }
2288 if(xstop < xoff + 1){
2289 logerror("xstop (%u) insufficient for xoff (%d)", xstop, xoff);
2290 return -1;
2291 }
2292 unsigned ymax, xmax;
2293 ncplane_dim_yx(n, &ymax, &xmax);
2294 // must be within the ncplane
2295 if(xstop >= xmax || ystop >= ymax){
2296 logerror("boundary (%ux%u) beyond plane (%dx%d)", ystop, xstop, ymax, xmax);
2297 return -1;
2298 }
2299 unsigned edges;
2300 edges = !(ctlword & NCBOXMASK_TOP) + !(ctlword & NCBOXMASK_LEFT);
2301 if(edges >= box_corner_needs(ctlword)){
2302 if(ncplane_putc(n, ul) < 0){
2303 return -1;
2304 }
2305 }
2306 if(!(ctlword & NCBOXMASK_TOP)){ // draw top border, if called for
2307 if(xstop - xoff >= 2){
2308 if(ncplane_cursor_move_yx(n, yoff, xoff + 1)){
2309 return -1;
2310 }
2311 if(!(ctlword & NCBOXGRAD_TOP)){ // cell styling, hl
2312 if(ncplane_hline(n, hl, xstop - xoff - 1) < 0){
2313 return -1;
2314 }
2315 }else{ // gradient, ul -> ur
2316 if(ncplane_hline_interp(n, hl, xstop - xoff - 1, ul->channels, ur->channels) < 0){
2317 return -1;
2318 }
2319 }
2320 }
2321 }
2322 edges = !(ctlword & NCBOXMASK_TOP) + !(ctlword & NCBOXMASK_RIGHT);
2323 if(edges >= box_corner_needs(ctlword)){
2324 if(ncplane_cursor_move_yx(n, yoff, xstop)){
2325 return -1;
2326 }
2327 if(ncplane_putc(n, ur) < 0){
2328 return -1;
2329 }
2330 }
2331 ++yoff;
2332 // middle rows (vertical lines)
2333 if(yoff < ystop){
2334 if(!(ctlword & NCBOXMASK_LEFT)){
2335 if(ncplane_cursor_move_yx(n, yoff, xoff)){
2336 return -1;
2337 }
2338 if((ctlword & NCBOXGRAD_LEFT)){ // grad styling, ul->ll
2339 if(ncplane_vline_interp(n, vl, ystop - yoff, ul->channels, ll->channels) < 0){
2340 return -1;
2341 }
2342 }else{
2343 if(ncplane_vline(n, vl, ystop - yoff) < 0){
2344 return -1;
2345 }
2346 }
2347 }
2348 if(!(ctlword & NCBOXMASK_RIGHT)){
2349 if(ncplane_cursor_move_yx(n, yoff, xstop)){
2350 return -1;
2351 }
2352 if((ctlword & NCBOXGRAD_RIGHT)){ // grad styling, ur->lr
2353 if(ncplane_vline_interp(n, vl, ystop - yoff, ur->channels, lr->channels) < 0){
2354 return -1;
2355 }
2356 }else{
2357 if(ncplane_vline(n, vl, ystop - yoff) < 0){
2358 return -1;
2359 }
2360 }
2361 }
2362 }
2363 // bottom line
2364 yoff = ystop;
2365 edges = !(ctlword & NCBOXMASK_BOTTOM) + !(ctlword & NCBOXMASK_LEFT);
2366 if(edges >= box_corner_needs(ctlword)){
2367 if(ncplane_cursor_move_yx(n, yoff, xoff)){
2368 return -1;
2369 }
2370 if(ncplane_putc(n, ll) < 0){
2371 return -1;
2372 }
2373 }
2374 if(!(ctlword & NCBOXMASK_BOTTOM)){
2375 if(xstop - xoff >= 2){
2376 if(ncplane_cursor_move_yx(n, yoff, xoff + 1)){
2377 return -1;
2378 }
2379 if(!(ctlword & NCBOXGRAD_BOTTOM)){ // cell styling, hl
2380 if(ncplane_hline(n, hl, xstop - xoff - 1) < 0){
2381 return -1;
2382 }
2383 }else{
2384 if(ncplane_hline_interp(n, hl, xstop - xoff - 1, ll->channels, lr->channels) < 0){
2385 return -1;
2386 }
2387 }
2388 }
2389 }
2390 edges = !(ctlword & NCBOXMASK_BOTTOM) + !(ctlword & NCBOXMASK_RIGHT);
2391 if(edges >= box_corner_needs(ctlword)){
2392 if(ncplane_cursor_move_yx(n, yoff, xstop)){
2393 return -1;
2394 }
2395 if(ncplane_putc(n, lr) < 0){
2396 return -1;
2397 }
2398 }
2399 return 0;
2400}
2401
2402// takes the head of a list of bound planes. performs a DFS on all planes bound
2403// to 'n', and all planes down-list from 'n', moving all *by* 'dy' and 'dx'.
2404static void
2405move_bound_planes(ncplane* n, int dy, int dx){
2406 while(n){
2407 if(n->sprite){
2408 sprixel_movefrom(n->sprite, n->absy, n->absx);
2409 }
2410 n->absy += dy;
2411 n->absx += dx;
2412 move_bound_planes(n->blist, dy, dx);
2413 n = n->bnext;
2414 }
2415}
2416
2417int ncplane_move_yx(ncplane* n, int y, int x){
2418 if(n == ncplane_notcurses(n)->stdplane){
2419 return -1;
2420 }
2421 int dy, dx; // amount moved
2422 if(n->boundto == n){
2423 dy = y - n->absy;
2424 dx = x - n->absx;
2425 }else{
2426 dy = (n->boundto->absy + y) - n->absy;
2427 dx = (n->boundto->absx + x) - n->absx;
2428 }
2429 if(dy || dx){ // don't want to trigger sprixel_movefrom() if unneeded
2430 if(n->sprite){
2431 sprixel_movefrom(n->sprite, n->absy, n->absx);
2432 }
2433 n->absx += dx;
2434 n->absy += dy;
2435 move_bound_planes(n->blist, dy, dx);
2436 }
2437 return 0;
2438}
2439
2440int ncplane_y(const ncplane* n){
2441 if(n->boundto == n){
2442 return n->absy;
2443 }
2444 return n->absy - n->boundto->absy;
2445}
2446
2447int ncplane_x(const ncplane* n){
2448 if(n->boundto == n){
2449 return n->absx;
2450 }
2451 return n->absx - n->boundto->absx;
2452}
2453
2454void ncplane_yx(const ncplane* n, int* y, int* x){
2455 if(y){
2456 *y = ncplane_y(n);
2457 }
2458 if(x){
2459 *x = ncplane_x(n);
2460 }
2461}
2462
2463// special case of ncplane_erase_region()
2465 loginfo("erasing %dx%d plane", n->leny, n->lenx);
2466 if(n->sprite){
2467 sprixel_hide(n->sprite);
2468 destroy_tam(n);
2469 }
2470 // we must preserve the background, but a pure nccell_duplicate() would be
2471 // wiped out by the egcpool_dump(). do a duplication (to get the stylemask
2472 // and channels), and then reload.
2473 char* egc = nccell_strdup(n, &n->basecell);
2474 memset(n->fb, 0, sizeof(*n->fb) * n->leny * n->lenx);
2475 egcpool_dump(&n->pool);
2476 // we need to zero out the EGC before handing this off to nccell_load, but
2477 // we don't want to lose the channels/attributes, so explicit gcluster load.
2478 n->basecell.gcluster = 0;
2479 nccell_load(n, &n->basecell, egc);
2480 free(egc);
2481 n->y = n->x = 0;
2482}
2483
2484int ncplane_erase_region(ncplane* n, int ystart, int xstart, int ylen, int xlen){
2485 if(ystart == -1){
2486 ystart = n->y;
2487 }
2488 if(xstart == -1){
2489 xstart = n->x;
2490 }
2491 if(ystart < 0 || xstart < 0){
2492 logerror("illegal start of erase (%d, %d)", ystart, xstart);
2493 return -1;
2494 }
2495 if(ystart >= (int)ncplane_dim_y(n) || xstart >= (int)ncplane_dim_x(n)){
2496 logerror("illegal start of erase (%d, %d)", ystart, xstart);
2497 return -1;
2498 }
2499 if(xlen < 0){
2500 if(xlen + 1 < -xstart){
2501 xlen = -xstart - 1;
2502 }
2503 xstart = xstart + xlen + 1;
2504 xlen = -xlen;
2505 }else if(xlen == 0){
2506 xstart = 0;
2507 xlen = ncplane_dim_x(n);
2508 }
2509 if(xlen > (int)ncplane_dim_x(n) || xstart + xlen > (int)ncplane_dim_x(n)){
2510 xlen = ncplane_dim_x(n) - xstart;
2511 }
2512 if(ylen < 0){
2513 if(ylen + 1 < -ystart){
2514 ylen = -ystart - 1;
2515 }
2516 ystart = ystart + ylen + 1;
2517 ylen = -ylen;
2518 }else if(ylen == 0){
2519 ystart = 0;
2520 ylen = ncplane_dim_y(n);
2521 }
2522 if(ylen > (int)ncplane_dim_y(n) || ystart + ylen > (int)ncplane_dim_y(n)){
2523 ylen = ncplane_dim_y(n) - ystart;
2524 }
2525 // special-case the full plane erasure, as it's powerfully optimized (O(1))
2526 if(ystart == 0 && xstart == 0 &&
2527 ylen == (int)ncplane_dim_y(n) && xlen == (int)ncplane_dim_x(n)){
2528 int tmpy = n->y; // preserve cursor location
2529 int tmpx = n->x;
2531 n->y = tmpy;
2532 n->x = tmpx;
2533 return 0;
2534 }
2535 loginfo("erasing %d/%d - %d/%d", ystart, xstart, ystart + ylen, xstart + xlen);
2536 for(int y = ystart ; y < ystart + ylen ; ++y){
2537 for(int x = xstart ; x < xstart + xlen ; ++x){
2538 nccell_release(n, &n->fb[nfbcellidx(n, y, x)]);
2539 nccell_init(&n->fb[nfbcellidx(n, y, x)]);
2540 }
2541 }
2542 return 0;
2543}
2544
2546 return ncplane_pile(n)->top;
2547}
2548
2550 return ncplane_pile(n)->bottom;
2551}
2552
2554 return n->below;
2555}
2556
2558 return n->above;
2559}
2560
2561int notcurses_mice_enable(notcurses* n, unsigned eventmask){
2562 if(mouse_setup(&n->tcache, eventmask)){
2563 return -1;
2564 }
2565 return 0;
2566}
2567
2569 ncpalette* p = malloc(sizeof(*p));
2570 if(p){
2571 memcpy(p, &nc->palette, sizeof(*p));
2572 }
2573 return p;
2574}
2575
2577 int ret = -1;
2578 if(!notcurses_canchangecolor(nc)){
2579 return -1;
2580 }
2581 for(size_t z = 0 ; z < sizeof(p->chans) / sizeof(*p->chans) ; ++z){
2582 if(nc->palette.chans[z] != p->chans[z]){
2583 nc->palette.chans[z] = p->chans[z];
2584 nc->palette_damage[z] = true;
2585 }
2586 }
2587 ret = 0;
2588 return ret;
2589}
2590
2592 free(p);
2593}
2594
2595bool ncplane_translate_abs(const ncplane* n, int* restrict y, int* restrict x){
2596 ncplane_translate(ncplane_stdplane_const(n), n, y, x);
2597 if(y){
2598 if(*y < 0){
2599 return false;
2600 }
2601 unsigned yval = *y;
2602 if(yval >= n->leny){
2603 return false;
2604 }
2605 }
2606 if(x){
2607 if(*x < 0){
2608 return false;
2609 }
2610 unsigned xval = *x;
2611 if(xval >= n->lenx){
2612 return false;
2613 }
2614 }
2615 return true;
2616}
2617
2618void ncplane_translate(const ncplane* src, const ncplane* dst,
2619 int* restrict y, int* restrict x){
2620 if(dst == NULL){
2621 dst = ncplane_stdplane_const(src);
2622 }
2623 if(y){
2624 *y = src->absy - dst->absy + *y;
2625 }
2626 if(x){
2627 *x = src->absx - dst->absx + *x;
2628 }
2629}
2630
2632 return ncplane_pile(n)->nc;
2633}
2634
2636 return ncplane_pile_const(n)->nc;
2637}
2638
2640 return n->absy;
2641}
2642
2644 return n->absx;
2645}
2646
2647void ncplane_abs_yx(const ncplane* n, int* RESTRICT y, int* RESTRICT x){
2648 if(y){
2649 *y = ncplane_abs_y(n);
2650 }
2651 if(x){
2652 *x = ncplane_abs_x(n);
2653 }
2654}
2655
2657 return n->boundto;
2658}
2659
2661 return n->boundto;
2662}
2663
2664int ncplane_set_name(ncplane* n, const char* name){
2665 char* copy = name ? strdup(name) : NULL;
2666 if(copy == NULL && name != NULL){
2667 return -1;
2668 }
2669 free(n->name);
2670 n->name = copy;
2671 return 0;
2672}
2673
2674char* ncplane_name(const ncplane* n){
2675 return n->name ? strdup(n->name) : NULL;
2676}
2677
2679 n->resizecb = resizecb;
2680}
2681
2683 return n->resizecb;
2684}
2685
2687 if(n->boundto == n){
2688 return 0;
2689 }
2690 int absy = ncplane_abs_y(n);
2691 int absx = ncplane_abs_x(n);
2692 int ret = 0;
2693 if(absy + ncplane_dim_y(n) > ncplane_dim_y(n->boundto)){
2694 const int dy = (absy + ncplane_dim_y(n)) - ncplane_dim_y(n->boundto);
2695 logdebug("moving up %d", dy);
2696 if(ncplane_move_rel(n, -dy, 0)){
2697 ret = -1;
2698 }
2699 absy = ncplane_abs_y(n);
2700 }
2701 if(absx + ncplane_dim_x(n) > ncplane_dim_x(n->boundto)){
2702 const int dx = ncplane_dim_x(n->boundto) - (absx + ncplane_dim_x(n));
2703 logdebug("moving left %d", dx);
2704 if(ncplane_move_rel(n, 0, dx)){
2705 ret = -1;
2706 }
2707 absx = ncplane_abs_x(n);
2708 }
2709 // this will prefer upper-left material if the child plane is larger than
2710 // the parent. we might want a smarter rule, one based on origin?
2711 if(absy < 0){
2712 logdebug("moving down %d", -absy);
2713 // we're at least partially above our parent
2714 if(ncplane_move_rel(n, -absy, 0)){
2715 ret = -1;
2716 }
2717 }
2718 if(absx < 0){
2719 logdebug("moving right %d", -absx);
2720 // we're at least partially to the left of our parent
2721 if(ncplane_move_rel(n, 0, -absx)){
2722 ret = -1;
2723 }
2724 }
2725 return ret;
2726}
2727
2729 const ncplane* parent = ncplane_parent_const(n);
2730 // a marginalized plane cannot be larger than its oppressor plane =]
2731 unsigned maxy, maxx;
2732 if(parent == n){ // root plane, need to use pile size
2733 ncpile* p = ncplane_pile(n);
2734 maxy = p->dimy;
2735 maxx = p->dimx;
2736 }else{
2737 ncplane_dim_yx(parent, &maxy, &maxx);
2738 }
2739 if((maxy -= (n->margin_b + (n->absy - n->boundto->absy))) < 1){
2740 maxy = 1;
2741 }
2742 if((maxx -= (n->margin_r + (n->absx - n->boundto->absx))) < 1){
2743 maxx = 1;
2744 }
2745 unsigned oldy, oldx;
2746 ncplane_dim_yx(n, &oldy, &oldx); // current dimensions of 'n'
2747 unsigned keepleny = oldy > maxy ? maxy : oldy;
2748 unsigned keeplenx = oldx > maxx ? maxx : oldx;
2749 if(ncplane_resize_internal(n, 0, 0, keepleny, keeplenx, 0, 0, maxy, maxx)){
2750 return -1;
2751 }
2752 int targy = maxy - n->margin_b;
2753 int targx = maxx - n->margin_b;
2754 loginfo("marg %d/%d, pdim %d/%d, move %d/%d", n->margin_b, n->margin_r, maxy, maxx, targy, targx);
2755 return ncplane_move_yx(n, targy, targx);
2756}
2757
2759 const ncpile* pile = ncplane_pile(n); // FIXME should be taken against parent
2760 const unsigned rows = pile->dimy;
2761 const unsigned cols = pile->dimx;
2762 unsigned oldy, oldx;
2763 ncplane_dim_yx(n, &oldy, &oldx); // current dimensions of 'n'
2764 unsigned keepleny = oldy > rows ? rows : oldy;
2765 unsigned keeplenx = oldx > cols ? cols : oldx;
2766 return ncplane_resize_internal(n, 0, 0, keepleny, keeplenx, 0, 0, rows, cols);
2767}
2768
2770 const ncplane* parent = ncplane_parent_const(n);
2771 if(parent == n){
2772 logerror("can't realign a root plane");
2773 return 0;
2774 }
2775 if(n->halign == NCALIGN_UNALIGNED && n->valign == NCALIGN_UNALIGNED){
2776 logerror("passed a non-aligned plane");
2777 return -1;
2778 }
2779 int xpos = ncplane_x(n);
2780 if(n->halign != NCALIGN_UNALIGNED){
2781 xpos = ncplane_halign(parent, n->halign, ncplane_dim_x(n));
2782 }
2783 int ypos = ncplane_y(n);
2784 if(n->valign != NCALIGN_UNALIGNED){
2785 ypos = ncplane_valign(parent, n->valign, ncplane_dim_y(n));
2786 }
2787 return ncplane_move_yx(n, ypos, xpos);
2788}
2789
2790// The standard plane cannot be reparented; we return NULL in that case.
2791// If provided |newparent|==|n|, we are moving |n| to its own pile. If |n|
2792// is already bound to |newparent|, this is a no-op, and we return |n|.
2793// This is essentially a wrapper around ncplane_reparent_family() that first
2794// reparents any children to the parent of 'n', or makes them root planes if
2795// 'n' is a root plane.
2797 const notcurses* nc = ncplane_notcurses_const(n);
2798 if(n == nc->stdplane){
2799 logerror("won't reparent standard plane");
2800 return NULL; // can't reparent standard plane
2801 }
2802 if(n->boundto == newparent){
2803 loginfo("won't reparent plane to itself");
2804 return n; // don't return error, just a no-op
2805 }
2806//notcurses_debug(ncplane_notcurses(n), stderr);
2807 if(n->blist){
2808 if(n->boundto == n){ // children become new root planes
2809 ncplane* lastlink;
2810 ncplane* child = n->blist;
2811 do{
2812 child->boundto = child;
2813 lastlink = child;
2814 child = child->bnext;
2815 }while(child); // n->blist != NULL -> lastlink != NULL
2816 if( (lastlink->bnext = ncplane_pile(n)->roots) ){
2817 lastlink->bnext->bprev = &lastlink->bnext;
2818 }
2819 n->blist->bprev = &ncplane_pile(n)->roots;
2820 ncplane_pile(n)->roots = n->blist;
2821 }else{ // children are rebound to current parent
2822 ncplane* lastlink;
2823 ncplane* child = n->blist;
2824 do{
2825 child->boundto = n->boundto;
2826 lastlink = child;
2827 child = child->bnext;
2828 }while(child); // n->blist != NULL -> lastlink != NULL
2829 if( (lastlink->bnext = n->boundto->blist) ){
2830 lastlink->bnext->bprev = &lastlink->bnext;
2831 }
2832 n->blist->bprev = &n->boundto->blist;
2833 n->boundto->blist = n->blist;
2834 }
2835 n->blist = NULL;
2836 }
2837 // FIXME would be nice to skip ncplane_descendant_p() on this call...:/
2838 return ncplane_reparent_family(n, newparent);
2839}
2840
2841// unsplice self from the z-axis, and then unsplice all children, recursively.
2842// to be called before unbinding 'n' from old pile.
2843static void
2844unsplice_zaxis_recursive(ncplane* n){
2845 // might already have been unspliced, in which case ->above/->below are NULL
2846 if(ncplane_pile(n)->top == n){
2847 ncplane_pile(n)->top = n->below;
2848 }else if(n->above){
2849 n->above->below = n->below;
2850 }
2851 if(ncplane_pile(n)->bottom == n){
2852 ncplane_pile(n)->bottom = n->above;
2853 }else if(n->below){
2854 n->below->above = n->above;
2855 }
2856 for(ncplane* child = n->blist ; child ; child = child->bnext){
2857 unsplice_zaxis_recursive(child);
2858 }
2859 n->below = n->above = NULL;
2860}
2861
2862// unsplice our sprixel from the pile's sprixellist, and then unsplice all
2863// children, recursively. call before unbinding. returns a doubly-linked
2864// list of any sprixels found.
2865static sprixel*
2866unsplice_sprixels_recursive(ncplane* n, sprixel* prev){
2867 sprixel* s = n->sprite;
2868 if(s){
2869 if(s->prev){
2870 s->prev->next = s->next;
2871 }else{
2872 ncplane_pile(n)->sprixelcache = s->next;
2873 }
2874 if(s->next){
2875 s->next->prev = s->prev;
2876 }
2877 if( (s->prev = prev) ){
2878 prev->next = s;
2879 }
2880 s->next = NULL;
2881 prev = s;
2882 }
2883 for(ncplane* child = n->blist ; child ; child = child->bnext){
2884 unsplice_sprixels_recursive(child, prev);
2885 while(prev && prev->next){ // FIXME lame
2886 prev = prev->next;
2887 }
2888 }
2889 return prev;
2890}
2891
2892// recursively splice 'n' and children into the z-axis, above 'n->boundto'.
2893// handles 'n' == 'n->boundto'. to be called after binding 'n' into new pile.
2894static void
2895splice_zaxis_recursive(ncplane* n, ncpile* p, unsigned ocellpxy, unsigned ocellpxx,
2896 unsigned ncellpxy, unsigned ncellpxx){
2897 n->pile = p;
2898 if(n != n->boundto){
2899 if((n->above = n->boundto->above) == NULL){
2900 n->pile->top = n;
2901 }else{
2902 n->boundto->above->below = n;
2903 }
2904 n->below = n->boundto;
2905 n->boundto->above = n;
2906 }
2907 if(n->sprite){
2908 if(ocellpxy != ncellpxy || ocellpxx != ncellpxx){
2909 sprixel_rescale(n->sprite, ncellpxy, ncellpxx);
2910 // FIXME do what on error?
2911 }
2912 }
2913 for(ncplane* child = n->blist ; child ; child = child->bnext){
2914 splice_zaxis_recursive(child, p, ocellpxy, ocellpxx, ncellpxy, ncellpxx);
2915 }
2916}
2917
2919 // ncplane_notcurses() goes through ncplane_pile(). since we're possibly
2920 // destroying piles below, get the notcurses reference early on.
2922 if(n == nc->stdplane){
2923 return NULL; // can't reparent standard plane
2924 }
2925 if(n->boundto == newparent){ // no-op
2926 return n;
2927 }
2928 if(ncplane_descendant_p(newparent, n)){
2929 return NULL;
2930 }
2931//notcurses_debug(ncplane_notcurses(n), stderr);
2932 if(n->bprev){ // extract from sibling list
2933 if( (*n->bprev = n->bnext) ){
2934 n->bnext->bprev = n->bprev;
2935 }
2936 }else if(n->bnext){
2937 n->bnext->bprev = NULL;
2938 }
2939 n->bprev = NULL;
2940 n->bnext = NULL;
2941 // if leaving a pile, extract n from the old zaxis, and also any sprixel
2942 sprixel* s = NULL;
2943 if(n == newparent || ncplane_pile(n) != ncplane_pile(newparent)){
2944 unsplice_zaxis_recursive(n);
2945 s = unsplice_sprixels_recursive(n, NULL);
2946 }
2947 const unsigned ocellpxy = ncplane_pile(n)->cellpxy;
2948 const unsigned ocellpxx = ncplane_pile(n)->cellpxx;
2949 n->boundto = newparent;
2950 if(n == n->boundto){ // we're a new root plane
2951 logdebug("reparenting new root plane %p", n);
2952 unsplice_zaxis_recursive(n);
2953 n->bnext = NULL;
2954 n->bprev = NULL;
2955 pthread_mutex_lock(&nc->pilelock);
2956 if(ncplane_pile(n)->top == NULL){ // did we just empty our pile?
2957 ncpile_destroy(ncplane_pile(n));
2958 }
2959 make_ncpile(nc, n);
2960 unsigned ncellpxy = ncplane_pile(n)->cellpxy;
2961 unsigned ncellpxx = ncplane_pile(n)->cellpxx;
2962 pthread_mutex_unlock(&nc->pilelock);
2963 if(ncplane_pile(n)){ // FIXME otherwise, we've got a problem...!
2964 splice_zaxis_recursive(n, ncplane_pile(n), ocellpxy, ocellpxx, ncellpxy, ncellpxx);
2965 }
2966 }else{ // establish ourselves as a sibling of new parent's children
2967 if( (n->bnext = newparent->blist) ){
2968 n->bnext->bprev = &n->bnext;
2969 }
2970 n->bprev = &newparent->blist;
2971 newparent->blist = n;
2972 // place it immediately above the new binding plane if crossing piles
2973 if(ncplane_pile(n) != ncplane_pile(n->boundto)){
2974 unsigned ncellpxy = ncplane_pile(n->boundto)->cellpxy;
2975 unsigned ncellpxx = ncplane_pile(n->boundto)->cellpxx;
2976 pthread_mutex_lock(&nc->pilelock);
2977 if(ncplane_pile(n)->top == NULL){ // did we just empty our pile?
2978 ncpile_destroy(ncplane_pile(n));
2979 }
2980 n->pile = ncplane_pile(n->boundto);
2981 pthread_mutex_unlock(&nc->pilelock);
2982 splice_zaxis_recursive(n, ncplane_pile(n), ocellpxy, ocellpxx, ncellpxy, ncellpxx);
2983 }
2984 }
2985 if(s){ // must be on new plane, with sprixels to donate
2986 sprixel* lame = s;
2987 while(lame->next){
2988 lame = lame->next;
2989 }
2990 if( (lame->next = n->pile->sprixelcache) ){
2991 n->pile->sprixelcache->prev = lame;
2992 }
2993 n->pile->sprixelcache = s;
2994 }
2995 return n;
2996}
2997
2998bool ncplane_set_scrolling(ncplane* n, unsigned scrollp){
2999 bool old = n->scrolling;
3000 n->scrolling = scrollp;
3001 return old;
3002}
3003
3005 return n->scrolling;
3006}
3007
3008bool ncplane_set_autogrow(ncplane* n, unsigned growp){
3010 logerror("can't set the standard plane autogrow");
3011 return false;
3012 }
3013 bool old = n->autogrow;
3014 n->autogrow = growp;
3015 return old;
3016}
3017
3019 return n->autogrow;
3020}
3021
3022// extract an integer, which must be non-negative, and followed by either a
3023// comma or a NUL terminator.
3024static int
3025lex_ulong(const char* op, unsigned* i, char** endptr){
3026 errno = 0;
3027 long l = strtol(op, endptr, 10);
3028 if(l < 0 || (l == LONG_MAX && errno == ERANGE) || (l > INT_MAX)){
3029 fprintf(stderr, "invalid margin: %s", op);
3030 return -1;
3031 }
3032 if((**endptr != ',' && **endptr) || *endptr == op){
3033 fprintf(stderr, "invalid margin: %s", op);
3034 return -1;
3035 }
3036 *i = l;
3037 return 0;
3038}
3039
3040int notcurses_lex_scalemode(const char* op, ncscale_e* scalemode){
3041 if(strcasecmp(op, "stretch") == 0){
3042 *scalemode = NCSCALE_STRETCH;
3043 }else if(strcasecmp(op, "scalehi") == 0){
3044 *scalemode = NCSCALE_SCALE_HIRES;
3045 }else if(strcasecmp(op, "hires") == 0){
3046 *scalemode = NCSCALE_NONE_HIRES;
3047 }else if(strcasecmp(op, "scale") == 0){
3048 *scalemode = NCSCALE_SCALE;
3049 }else if(strcasecmp(op, "none") == 0){
3050 *scalemode = NCSCALE_NONE;
3051 }else{
3052 return -1;
3053 }
3054 return 0;
3055}
3056
3057const char* notcurses_str_scalemode(ncscale_e scalemode){
3058 if(scalemode == NCSCALE_STRETCH){
3059 return "stretch";
3060 }else if(scalemode == NCSCALE_SCALE){
3061 return "scale";
3062 }else if(scalemode == NCSCALE_NONE){
3063 return "none";
3064 }else if(scalemode == NCSCALE_NONE_HIRES){
3065 return "hires";
3066 }else if(scalemode == NCSCALE_SCALE_HIRES){
3067 return "scalehi";
3068 }
3069 return NULL;
3070}
3071
3073 char* eptr;
3074 if(lex_ulong(op, &opts->margin_t, &eptr)){
3075 return -1;
3076 }
3077 if(!*eptr){ // allow a single value to be specified for all four margins
3078 opts->margin_r = opts->margin_l = opts->margin_b = opts->margin_t;
3079 return 0;
3080 }
3081 op = ++eptr; // once here, we require four values
3082 if(lex_ulong(op, &opts->margin_r, &eptr) || !*eptr){
3083 return -1;
3084 }
3085 op = ++eptr;
3086 if(lex_ulong(op, &opts->margin_b, &eptr) || !*eptr){
3087 return -1;
3088 }
3089 op = ++eptr;
3090 if(lex_ulong(op, &opts->margin_l, &eptr) || *eptr){ // must end in NUL
3091 return -1;
3092 }
3093 return 0;
3094}
3095
3097 return inputready_fd(n->tcache.ictx);
3098}
3099
3101 return inputready_fd(n->tcache.ictx);
3102}
3103
3104// FIXME speed this up, PoC
3105// given an egc, get its index in the blitter's EGC set
3106static int
3107get_blitter_egc_idx(const struct blitset* bset, const char* egc){
3108 wchar_t wc;
3109 mbstate_t mbs = {0};
3110 size_t sret = mbrtowc(&wc, egc, strlen(egc), &mbs);
3111 if(sret == (size_t)-1 || sret == (size_t)-2){
3112 return -1;
3113 }
3114 wchar_t* wptr = wcsrchr(bset->egcs, wc);
3115 if(wptr == NULL){
3116//fprintf(stderr, "FAILED TO FIND [%s] (%lc) in [%ls]\n", egc, wc, bset->egcs);
3117 return -1;
3118 }
3119//fprintf(stderr, "FOUND [%s] (%lc) in [%ls] (%zu)\n", egc, wc, bset->egcs, wptr - bset->egcs);
3120 return wptr - bset->egcs;
3121}
3122
3123static bool
3124is_bg_p(int idx, int py, int px, int width){
3125 // bit increases to the right, and down
3126 const int bpos = py * width + px; // bit corresponding to pixel, 0..|egcs|-1
3127 const unsigned mask = 1u << bpos;
3128 if(idx & mask){
3129 return false;
3130 }
3131 return true;
3132}
3133
3134static inline uint32_t*
3135ncplane_as_rgba_internal(const ncplane* nc, ncblitter_e blit,
3136 int begy, int begx, unsigned leny, unsigned lenx,
3137 unsigned* pxdimy, unsigned* pxdimx){
3138 const notcurses* ncur = ncplane_notcurses_const(nc);
3139 unsigned ystart, xstart;
3140 if(check_geometry_args(nc, begy, begx, &leny, &lenx, &ystart, &xstart)){
3141 return NULL;
3142 }
3143 if(blit == NCBLIT_PIXEL){ // FIXME extend this to support sprixels
3144 logerror("pixel blitter %d not yet supported", blit);
3145 return NULL;
3146 }
3147 if(blit == NCBLIT_DEFAULT){
3148 logerror("must specify exact blitter, not NCBLIT_DEFAULT");
3149 return NULL;
3150 }
3151 const struct blitset* bset = lookup_blitset(&ncur->tcache, blit, false);
3152 if(bset == NULL){
3153 logerror("blitter %d invalid in current environment", blit);
3154 return NULL;
3155 }
3156//fprintf(stderr, "ALLOCATING %u %d %d %p\n", 4u * lenx * leny * 2, leny, lenx, bset);
3157 if(pxdimy){
3158 *pxdimy = leny * bset->height;
3159 }
3160 if(pxdimx){
3161 *pxdimx = lenx * bset->width;
3162 }
3163 uint32_t* ret = malloc(sizeof(*ret) * lenx * bset->width * leny * bset->height);
3164//fprintf(stderr, "GEOM: %d/%d %d/%d ret: %p\n", bset->height, bset->width, *pxdimy, *pxdimx, ret);
3165 if(ret){
3166 for(unsigned y = ystart, targy = 0 ; y < ystart + leny ; ++y, targy += bset->height){
3167 for(unsigned x = xstart, targx = 0 ; x < xstart + lenx ; ++x, targx += bset->width){
3168 uint16_t stylemask;
3169 uint64_t channels;
3170 char* c = ncplane_at_yx(nc, y, x, &stylemask, &channels);
3171 if(c == NULL){
3172 free(ret);
3173 return NULL;
3174 }
3175 int idx = get_blitter_egc_idx(bset, c);
3176 if(idx < 0){
3177 free(ret);
3178 free(c);
3179 return NULL;
3180 }
3181 unsigned fr, fg, fb, br, bg, bb, fa, ba;
3182 ncchannels_fg_rgb8(channels, &fr, &fb, &fg);
3183 fa = ncchannels_fg_alpha(channels);
3184 ncchannels_bg_rgb8(channels, &br, &bb, &bg);
3185 ba = ncchannels_bg_alpha(channels);
3186 // handle each destination pixel from this cell
3187 for(unsigned py = 0 ; py < bset->height ; ++py){
3188 for(unsigned px = 0 ; px < bset->width ; ++px){
3189 uint32_t* p = &ret[(targy + py) * (lenx * bset->width) + (targx + px)];
3190 bool background = is_bg_p(idx, py, px, bset->width);
3191 *p = 0;
3192 if(background){
3193 if(!ba){
3194 ncpixel_set_a(p, 0xff);
3195 ncpixel_set_r(p, br);
3196 ncpixel_set_g(p, bb);
3197 ncpixel_set_b(p, bg);
3198 }
3199 }else{
3200 if(!fa){
3201 ncpixel_set_a(p, 0xff);
3202 ncpixel_set_r(p, fr);
3203 ncpixel_set_g(p, fb);
3204 ncpixel_set_b(p, fg);
3205 }
3206 }
3207 }
3208 }
3209 free(c);
3210 }
3211 }
3212 }
3213 return ret;
3214}
3215
3217 int begy, int begx, unsigned leny,
3218 unsigned lenx, unsigned* pxdimy, unsigned* pxdimx){
3219 unsigned px, py;
3220 if(!pxdimy){
3221 pxdimy = &py;
3222 }
3223 if(!pxdimx){
3224 pxdimx = &px;
3225 }
3226 return ncplane_as_rgba_internal(nc, blit, begy, begx, leny, lenx, pxdimy, pxdimx);
3227}
3228
3229// return a heap-allocated copy of the contents
3230char* ncplane_contents(ncplane* nc, int begy, int begx, unsigned leny, unsigned lenx){
3231 unsigned ystart, xstart;
3232 if(check_geometry_args(nc, begy, begx, &leny, &lenx, &ystart, &xstart)){
3233 return NULL;
3234 }
3235 size_t retlen = 1;
3236 char* ret = malloc(retlen);
3237 if(ret){
3238 for(unsigned y = ystart, targy = 0 ; y < ystart + leny ; ++y, targy += 2){
3239 for(unsigned x = xstart, targx = 0 ; x < xstart + lenx ; ++x, ++targx){
3241 // we need ncplane_at_yx_cell() here instead of ncplane_at_yx(),
3242 // because we should only have one copy of each wide EGC.
3243 int clen;
3244 if((clen = ncplane_at_yx_cell(nc, y, x, &ncl)) < 0){
3245 free(ret);
3246 return NULL;
3247 }
3248 const char* c = nccell_extended_gcluster(nc, &ncl);
3249 if(clen){
3250 char* tmp = realloc(ret, retlen + clen);
3251 if(!tmp){
3252 free(ret);
3253 return NULL;
3254 }
3255 ret = tmp;
3256 memcpy(ret + retlen - 1, c, clen);
3257 retlen += clen;
3258 }
3259 }
3260 }
3261 ret[retlen - 1] = '\0';
3262 }
3263 return ret;
3264}
3265
3266// find the center coordinate of a plane, preferring the top/left in the
3267// case of an even number of rows/columns (in such a case, there will be one
3268// more cell to the bottom/right of the center than the top/left). the
3269// center is then modified relative to the plane's origin.
3271 ncplane_center(n, y, x);
3272 if(y){
3273 *y += n->absy;
3274 }
3275 if(x){
3276 *x += n->absx;
3277 }
3278}
3279
3280int ncplane_putwstr_stained(ncplane* n, const wchar_t* gclustarr){
3281 mbstate_t ps = {0};
3282 const wchar_t** wset = &gclustarr;
3283 size_t mbytes = wcsrtombs(NULL, wset, 0, &ps);
3284 if(mbytes == (size_t)-1){
3285 logerror("error converting wide string");
3286 return -1;
3287 }
3288 ++mbytes;
3289 char* mbstr = malloc(mbytes);
3290 if(mbstr == NULL){
3291 return -1;
3292 }
3293 size_t s = wcsrtombs(mbstr, wset, mbytes, &ps);
3294 if(s == (size_t)-1){
3295 free(mbstr);
3296 return -1;
3297 }
3298 int r = ncplane_putstr_stained(n, mbstr);
3299 free(mbstr);
3300 return r;
3301}
3302
3303int notcurses_ucs32_to_utf8(const uint32_t* ucs32, unsigned ucs32count,
3304 unsigned char* resultbuf, size_t buflen){
3305 if(u32_to_u8(ucs32, ucs32count, resultbuf, &buflen) == NULL){
3306 return -1;
3307 }
3308 return buflen;
3309}
3310
3311int ncstrwidth(const char* egcs, int* validbytes, int* validwidth){
3312 int cols;
3313 if(validwidth == NULL){
3314 validwidth = &cols;
3315 }
3316 *validwidth = 0;
3317 int bytes;
3318 if(validbytes == NULL){
3319 validbytes = &bytes;
3320 }
3321 *validbytes = 0;
3322 do{
3323 int thesecols, thesebytes;
3324 thesebytes = utf8_egc_len(egcs, &thesecols);
3325 if(thesebytes < 0){
3326 return -1;
3327 }
3328 egcs += thesebytes;
3329 *validbytes += thesebytes;
3330 *validwidth += thesecols;
3331 }while(*egcs);
3332 return *validwidth;
3333}
3334
3336 unsigned* RESTRICT pxy, unsigned* RESTRICT pxx,
3337 unsigned* RESTRICT celldimy, unsigned* RESTRICT celldimx,
3338 unsigned* RESTRICT maxbmapy, unsigned* RESTRICT maxbmapx){
3339 const notcurses* nc = ncplane_notcurses_const(n);
3340 const ncpile* p = ncplane_pile_const(n);
3341 if(celldimy){
3342 *celldimy = p->cellpxy;
3343 }
3344 if(celldimx){
3345 *celldimx = p->cellpxx;
3346 }
3347 if(pxy){
3348 *pxy = p->cellpxy * ncplane_dim_y(n);
3349 }
3350 if(pxx){
3351 *pxx = p->cellpxx * ncplane_dim_x(n);
3352 }
3354 if(maxbmapy){
3355 *maxbmapy = p->cellpxy * ncplane_dim_y(n);
3356 if(*maxbmapy > nc->tcache.sixel_maxy && nc->tcache.sixel_maxy){
3357 *maxbmapy = nc->tcache.sixel_maxy;
3358 }
3359 }
3360 if(maxbmapx){
3361 *maxbmapx = p->cellpxx * ncplane_dim_x(n);
3362 if(*maxbmapx > nc->tcache.sixel_maxx && nc->tcache.sixel_maxx){
3363 *maxbmapx = nc->tcache.sixel_maxx;
3364 }
3365 }
3366 }else{
3367 if(maxbmapy){
3368 *maxbmapy = 0;
3369 }
3370 if(maxbmapx){
3371 *maxbmapx = 0;
3372 }
3373 }
3374}
3375
3377 return &n->tcache.caps;
3378}
int init_banner(const notcurses *nc, fbuf *f)
Definition banner.c:27
const struct blitset * lookup_blitset(const tinfo *tcache, ncblitter_e setid, bool may_degrade)
Definition blit.c:1297
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:162
const nccell * c
Definition egcpool.h:207
uint32_t idx
Definition egcpool.h:209
va_end(va)
int r
Definition fbuf.h:226
int inputready_fd(const inputctx *ictx)
Definition in.c:2672
int putenv_term(const char *termname) __attribute__((nonnull(1)))
Definition termdesc.c:1655
void sprixel_movefrom(sprixel *s, int y, int x)
Definition sprite.c:68
void sprixel_hide(sprixel *s)
Definition sprite.c:83
int mouse_setup(tinfo *ti, unsigned eventmask)
Definition mice.c:3
void reset_stats(ncstats *stats)
Definition stats.c:74
int ncvisual_init(int loglevel)
Definition visual.c:23
int set_loglevel_from_env(ncloglevel_e *loglevel) __attribute__((nonnull(1)))
Definition util.c:19
void summarize_stats(notcurses *nc)
Definition stats.c:166
void sprixel_free(sprixel *s)
Definition sprite.c:38
void ncmetric_use_utf8(void)
Definition metric.c:24
int sprite_clear_all(const tinfo *t, fbuf *f)
Definition sprite.c:204
int clear_and_home(notcurses *nc, tinfo *ti, fbuf *f)
Definition render.c:1399
int get_linux_fb_pixelgeom(tinfo *ti, unsigned *ypix, unsigned *xpix)
Definition linux.c:813
#define logerror(fmt,...)
Definition logging.h:32
#define loginfo(fmt,...)
Definition logging.h:42
#define logdebug(fmt,...)
Definition logging.h:52
#define logwarn(fmt,...)
Definition logging.h:37
#define logpanic(fmt,...)
Definition logging.h:22
ncplane * ncpile_top(ncplane *n)
Definition notcurses.c:2545
int ncplane_abs_x(const ncplane *n)
Definition notcurses.c:2643
int ncplane_putwstr_stained(ncplane *n, const wchar_t *gclustarr)
Definition notcurses.c:3280
__attribute__((malloc))
Definition notcurses.c:504
void ncplane_on_styles(ncplane *n, unsigned stylebits)
Definition notcurses.c:2082
void ncplane_set_fg_default(ncplane *n)
Definition notcurses.c:1505
void ncpalette_free(ncpalette *p)
Definition notcurses.c:2591
const char * notcurses_version(void)
Definition notcurses.c:185
ncplane * ncplane_new_internal(notcurses *nc, ncplane *n, const ncplane_options *nopts)
Definition notcurses.c:560
int ncplane_set_fg_rgb(ncplane *n, unsigned channel)
Definition notcurses.c:1529
int ncplane_cursor_move_yx(ncplane *n, int y, int x)
Definition notcurses.c:723
void ncplane_set_fg_rgb8_clipped(ncplane *n, int r, int g, int b)
Definition notcurses.c:1521
void ncplane_home(ncplane *n)
Definition notcurses.c:718
int ncplane_x(const ncplane *n)
Definition notcurses.c:2447
int ncplane_cursor_move_rel(ncplane *n, int y, int x)
Definition notcurses.c:753
void * ncplane_userptr(ncplane *n)
Definition notcurses.c:195
int ncplane_y(const ncplane *n)
Definition notcurses.c:2440
char * ncplane_contents(ncplane *nc, int begy, int begx, unsigned leny, unsigned lenx)
Definition notcurses.c:3230
void ncplane_cursor_yx(const ncplane *n, unsigned *y, unsigned *x)
Definition notcurses.c:1744
ncplane * notcurses_stdplane(notcurses *nc)
Definition notcurses.c:702
#define PTHREAD_MUTEX_RECURSIVE_NP
int ncplane_erase_region(ncplane *n, int ystart, int xstart, int ylen, int xlen)
Definition notcurses.c:2484
ncplane * ncpile_create(notcurses *nc, const struct ncplane_options *nopts)
Definition notcurses.c:714
char * notcurses_detected_terminal(const notcurses *nc)
Definition notcurses.c:2072
bool ncplane_autogrow_p(const ncplane *n)
Definition notcurses.c:3018
int update_term_dimensions(unsigned *rows, unsigned *cols, tinfo *tcache, int margin_b, unsigned *cgeo_changed, unsigned *pgeo_changed)
Definition notcurses.c:319
int ncplane_scrollup(ncplane *n, int r)
Definition notcurses.c:1795
void ncplane_set_channels(ncplane *n, uint64_t channels)
Definition notcurses.c:1497
int notcurses_ucs32_to_utf8(const uint32_t *ucs32, unsigned ucs32count, unsigned char *resultbuf, size_t buflen)
Definition notcurses.c:3303
void ncplane_abs_yx(const ncplane *n, int *RESTRICT y, int *RESTRICT x)
Definition notcurses.c:2647
ncplane * ncplane_dup(const ncplane *n, void *opaque)
Definition notcurses.c:763
void ncplane_set_bg_rgb8_clipped(ncplane *n, int r, int g, int b)
Definition notcurses.c:1513
int ncplane_putchar_stained(ncplane *n, char c)
Definition notcurses.c:2014
int ncplane_putnstr_aligned(struct ncplane *n, int y, ncalign_e align, size_t s, const char *str)
Definition notcurses.c:2152
int ncplane_vprintf_aligned(ncplane *n, int y, ncalign_e align, const char *format, va_list ap)
Definition notcurses.c:2131
int ncplane_set_bg_alpha(ncplane *n, int alpha)
Definition notcurses.c:1549
void ncplane_set_styles(ncplane *n, unsigned stylebits)
Definition notcurses.c:2077
const char * notcurses_str_scalemode(ncscale_e scalemode)
Definition notcurses.c:3057
int ncplane_destroy(ncplane *ncp)
Definition notcurses.c:1021
int ncplane_resize_marginalized(ncplane *n)
Definition notcurses.c:2728
int ncplane_scrollup_child(ncplane *n, const ncplane *child)
Definition notcurses.c:1816
void ncplane_pixel_geom(const ncplane *n, unsigned *RESTRICT pxy, unsigned *RESTRICT pxx, unsigned *RESTRICT celldimy, unsigned *RESTRICT celldimx, unsigned *RESTRICT maxbmapy, unsigned *RESTRICT maxbmapx)
Definition notcurses.c:3335
void notcurses_version_components(int *major, int *minor, int *patch, int *tweak)
Definition notcurses.c:24
const char * nccell_extended_gcluster(const ncplane *n, const nccell *c)
Definition notcurses.c:1576
void ncplane_off_styles(ncplane *n, unsigned stylebits)
Definition notcurses.c:2087
void * ncplane_set_userptr(ncplane *n, void *opaque)
Definition notcurses.c:189
char * ncplane_at_yx(const ncplane *n, int y, int x, uint16_t *stylemask, uint64_t *channels)
Definition notcurses.c:217
int ncplane_set_fg_palindex(ncplane *n, unsigned idx)
Definition notcurses.c:1553
int ncplane_putegc_stained(ncplane *n, const char *gclust, size_t *sbytes)
Definition notcurses.c:2038
int ncplane_abs_y(const ncplane *n)
Definition notcurses.c:2639
int ncplane_resize_placewithin(ncplane *n)
Definition notcurses.c:2686
uint16_t ncplane_styles(const ncplane *n)
Definition notcurses.c:1501
int ncplane_move_family_above(ncplane *restrict n, ncplane *restrict bpoint)
Definition notcurses.c:1677
int ncplane_set_fg_alpha(ncplane *n, int alpha)
Definition notcurses.c:1545
int ncpalette_use(notcurses *nc, const ncpalette *p)
Definition notcurses.c:2576
int ncplane_set_bg_rgb(ncplane *n, unsigned channel)
Definition notcurses.c:1541
const notcurses * ncplane_notcurses_const(const ncplane *n)
Definition notcurses.c:2635
const nccapabilities * notcurses_capabilities(const notcurses *n)
Definition notcurses.c:3376
ncplane * ncplane_reparent_family(ncplane *n, ncplane *newparent)
Definition notcurses.c:2918
uint64_t ncplane_set_fchannel(ncplane *n, uint32_t channel)
Definition notcurses.c:1537
int ncplane_move_yx(ncplane *n, int y, int x)
Definition notcurses.c:2417
int ncplane_base(ncplane *ncp, nccell *c)
Definition notcurses.c:1572
int reset_term_palette(const tinfo *ti, fbuf *f, unsigned touchedpalette)
Definition notcurses.c:78
void free_plane(ncplane *p)
Definition notcurses.c:467
int ncplane_resize_maximize(ncplane *n)
Definition notcurses.c:2758
int nccell_load(ncplane *n, nccell *c, const char *gcluster)
Definition notcurses.c:1836
int ncdirect_inputready_fd(ncdirect *n)
Definition notcurses.c:3100
int ncplane_putwegc_stained(ncplane *n, const wchar_t *gclust, size_t *sbytes)
Definition notcurses.c:2026
int notcurses_inputready_fd(notcurses *n)
Definition notcurses.c:3096
void ncplane_translate(const ncplane *src, const ncplane *dst, int *restrict y, int *restrict x)
Definition notcurses.c:2618
int ncplane_putegc_yx(ncplane *n, int y, int x, const char *gclust, size_t *sbytes)
Definition notcurses.c:2001
int ncplane_set_base(ncplane *ncp, const char *egc, uint16_t stylemask, uint64_t channels)
Definition notcurses.c:1568
int ncplane_set_bg_rgb8(ncplane *n, unsigned r, unsigned g, unsigned b)
Definition notcurses.c:1517
int ncplane_move_family_below(ncplane *restrict n, ncplane *restrict bpoint)
Definition notcurses.c:1711
int notcurses_mice_enable(notcurses *n, unsigned eventmask)
Definition notcurses.c:2561
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:857
const ncplane * notcurses_stdplane_const(const notcurses *nc)
Definition notcurses.c:706
uint64_t ncplane_set_bchannel(ncplane *n, uint32_t channel)
Definition notcurses.c:1533
int ncplane_vprintf_stained(struct ncplane *n, const char *format, va_list ap)
Definition notcurses.c:2142
const ncplane * ncplane_parent_const(const ncplane *n)
Definition notcurses.c:2660
int ncplane_vprintf_yx(ncplane *n, int y, int x, const char *format, va_list ap)
Definition notcurses.c:2121
ncplane * ncplane_above(ncplane *n)
Definition notcurses.c:2557
int(*)(ncplane *) ncplane_resizecb(const ncplane *n)
Definition notcurses.c:2682
notcurses * notcurses_core_init(const notcurses_options *opts, FILE *outfp)
Definition notcurses.c:1245
void ncplane_set_resizecb(ncplane *n, int(*resizecb)(ncplane *))
Definition notcurses.c:2678
bool ncplane_set_scrolling(ncplane *n, unsigned scrollp)
Definition notcurses.c:2998
ncplane * ncplane_below(ncplane *n)
Definition notcurses.c:2553
uint32_t * ncplane_as_rgba(const ncplane *nc, ncblitter_e blit, int begy, int begx, unsigned leny, unsigned lenx, unsigned *pxdimy, unsigned *pxdimx)
Definition notcurses.c:3216
bool ncplane_scrolling_p(const ncplane *n)
Definition notcurses.c:3004
int notcurses_stop(notcurses *nc)
Definition notcurses.c:1449
char * ncplane_at_cursor(const ncplane *n, uint16_t *stylemask, uint64_t *channels)
Definition notcurses.c:213
void ncplane_set_bg_default(ncplane *n)
Definition notcurses.c:1509
void ncplane_yx(const ncplane *n, int *y, int *x)
Definition notcurses.c:2454
int ncplane_set_bg_palindex(ncplane *n, unsigned idx)
Definition notcurses.c:1557
void init_lang(void)
Definition notcurses.c:1097
void scroll_down(ncplane *n)
Definition notcurses.c:1761
int ncplane_hline_interp(ncplane *n, const nccell *c, unsigned len, uint64_t c1, uint64_t c2)
Definition notcurses.c:2159
ncplane * ncplane_reparent(ncplane *n, ncplane *newparent)
Definition notcurses.c:2796
int ncplane_set_name(ncplane *n, const char *name)
Definition notcurses.c:2664
ncpixelimpl_e notcurses_check_pixel_support(const notcurses *nc)
Definition notcurses.c:1156
int ncplane_at_cursor_cell(ncplane *n, nccell *c)
Definition notcurses.c:269
int notcurses_lex_scalemode(const char *op, ncscale_e *scalemode)
Definition notcurses.c:3040
int ncplane_cursor_at(const ncplane *n, nccell *c, char **gclust)
Definition notcurses.c:2050
int resize_callbacks_children(ncplane *n)
Definition notcurses.c:804
int ncplane_resize_realign(ncplane *n)
Definition notcurses.c:2769
int reset_term_attributes(const tinfo *ti, fbuf *f)
Definition notcurses.c:60
char * ncplane_vprintf_prep(const char *format, va_list ap)
Definition notcurses.c:2093
unsigned notcurses_palette_size(const notcurses *nc)
Definition notcurses.c:2068
int notcurses_leave_alternate_screen(notcurses *nc)
Definition notcurses.c:42
ncplane * ncpile_bottom(ncplane *n)
Definition notcurses.c:2549
int ncplane_move_below(ncplane *restrict n, ncplane *restrict below)
Definition notcurses.c:1631
int ncplane_at_yx_cell(ncplane *n, int y, int x, nccell *c)
Definition notcurses.c:273
int ncstrwidth(const char *egcs, int *validbytes, int *validwidth)
Definition notcurses.c:3311
int ncplane_set_fg_rgb8(ncplane *n, unsigned r, unsigned g, unsigned b)
Definition notcurses.c:1525
int ncplane_resize(ncplane *n, int keepy, int keepx, unsigned keepleny, unsigned keeplenx, int yoff, int xoff, unsigned ylen, unsigned xlen)
Definition notcurses.c:1009
bool ncplane_translate_abs(const ncplane *n, int *restrict y, int *restrict x)
Definition notcurses.c:2595
int ncplane_vline_interp(ncplane *n, const nccell *c, unsigned len, uint64_t c1, uint64_t c2)
Definition notcurses.c:2215
uint64_t ncplane_channels(const ncplane *n)
Definition notcurses.c:1493
notcurses * ncplane_notcurses(const ncplane *n)
Definition notcurses.c:2631
ncplane * ncplane_create(ncplane *n, const ncplane_options *nopts)
Definition notcurses.c:710
const void * ncplane_userptr_const(const ncplane *n)
Definition notcurses.c:199
bool ncplane_set_autogrow(ncplane *n, unsigned growp)
Definition notcurses.c:3008
void ncplane_erase(ncplane *n)
Definition notcurses.c:2464
char * ncplane_name(const ncplane *n)
Definition notcurses.c:2674
ncplane * ncplane_parent(ncplane *n)
Definition notcurses.c:2656
uint16_t notcurses_supported_styles(const notcurses *nc)
Definition notcurses.c:2064
int ncplane_move_above(ncplane *restrict n, ncplane *restrict above)
Definition notcurses.c:1584
#define TABSTOP
Definition notcurses.c:22
int ncplane_family_destroy(ncplane *ncp)
Definition notcurses.c:1071
int ncplane_box(ncplane *n, const nccell *ul, const nccell *ur, const nccell *ll, const nccell *lr, const nccell *hl, const nccell *vl, unsigned ystop, unsigned xstop, unsigned ctlword)
Definition notcurses.c:2277
int notcurses_enter_alternate_screen(notcurses *nc)
Definition notcurses.c:31
void notcurses_drop_planes(notcurses *nc)
Definition notcurses.c:1437
int ncplane_putc_yx(ncplane *n, int y, int x, const nccell *c)
Definition notcurses.c:1986
int notcurses_lex_margins(const char *op, notcurses_options *opts)
Definition notcurses.c:3072
void ncplane_center_abs(const ncplane *n, int *RESTRICT y, int *RESTRICT x)
Definition notcurses.c:3270
int ncplane_set_base_cell(ncplane *ncp, const nccell *c)
Definition notcurses.c:1561
ncpalette * ncpalette_new(notcurses *nc)
Definition notcurses.c:2568
void ncplane_dim_yx(const ncplane *n, unsigned *rows, unsigned *cols)
Definition notcurses.c:304
ncscale_e
Definition notcurses.h:96
@ NCSCALE_SCALE_HIRES
Definition notcurses.h:101
@ NCSCALE_STRETCH
Definition notcurses.h:99
@ NCSCALE_SCALE
Definition notcurses.h:98
@ NCSCALE_NONE
Definition notcurses.h:97
@ NCSCALE_NONE_HIRES
Definition notcurses.h:100
int y
Definition notcurses.h:1905
#define NCBOXMASK_TOP
Definition notcurses.h:2605
#define NCBOXGRAD_RIGHT
Definition notcurses.h:2610
return newn
Definition notcurses.h:3512
#define NCOPTION_SUPPRESS_BANNERS
Definition notcurses.h:1015
#define NCOPTION_INHIBIT_SETLOCALE
Definition notcurses.h:990
#define NCPLANE_OPTION_HORALIGNED
Definition notcurses.h:1441
#define NCPLANE_OPTION_AUTOGROW
Definition notcurses.h:1458
#define NCOPTION_NO_CLEAR_BITMAPS
Definition notcurses.h:996
#define NCSTYLE_MASK
Definition notcurses.h:769
#define NCPLANE_OPTION_MARGINALIZED
Definition notcurses.h:1450
#define NCPLANE_OPTION_VSCROLL
Definition notcurses.h:1462
const struct ncplane_options * opts
Definition notcurses.h:3487
ncalign_e
Definition notcurses.h:80
@ NCALIGN_UNALIGNED
Definition notcurses.h:81
#define NCOPTION_DRAIN_INPUT
Definition notcurses.h:1030
#define NCPLANE_OPTION_VERALIGNED
Definition notcurses.h:1443
#define NCOPTION_PRESERVE_CURSOR
Definition notcurses.h:1011
#define NCOPTION_SCROLLING
Definition notcurses.h:1034
#define NCOPTION_NO_QUIT_SIGHANDLERS
Definition notcurses.h:1005
#define NCBOXMASK_LEFT
Definition notcurses.h:2608
ncblitter_e
Definition notcurses.h:65
@ NCBLIT_PIXEL
Definition notcurses.h:73
@ NCBLIT_DEFAULT
Definition notcurses.h:66
#define NCOPTION_NO_WINCH_SIGHANDLER
Definition notcurses.h:1000
#define NCBOXGRAD_BOTTOM
Definition notcurses.h:2611
vopts n
Definition notcurses.h:3506
#define NCBOXGRAD_LEFT
Definition notcurses.h:2612
#define RESTRICT
Definition notcurses.h:24
#define NCBOXMASK_BOTTOM
Definition notcurses.h:2607
@ NCLOGLEVEL_SILENT
Definition notcurses.h:969
@ NCLOGLEVEL_TRACE
Definition notcurses.h:977
int int x
Definition notcurses.h:1905
#define NCCELL_TRIVIAL_INITIALIZER
Definition notcurses.h:737
ncpixelimpl_e
Definition notcurses.h:1672
@ NCPIXEL_NONE
Definition notcurses.h:1673
#define NCBOXMASK_RIGHT
Definition notcurses.h:2606
#define NCPLANE_OPTION_FIXED
Definition notcurses.h:1454
#define NCOPTION_NO_ALTERNATE_SCREEN
Definition notcurses.h:1019
API int API int const nccell unsigned len
Definition notcurses.h:2592
#define NCBOXGRAD_TOP
Definition notcurses.h:2609
#define NCOPTION_NO_FONT_CHANGES
Definition notcurses.h:1025
void nccell_release(ncplane *n, nccell *c)
Definition render.c:128
int nccell_duplicate(ncplane *n, nccell *targ, const nccell *c)
Definition render.c:133
int sprixel_rescale(sprixel *spx, unsigned ncellpxy, unsigned ncellpxx)
Definition sprite.c:225
void notcurses_stats_reset(notcurses *nc, ncstats *stats)
Definition stats.c:100
ncblitter blit
Definition internal.h:410
unsigned width
Definition internal.h:400
unsigned height
Definition internal.h:401
const wchar_t * egcs
Definition internal.h:404
Definition fbuf.h:25
uint64_t used
Definition fbuf.h:27
unsigned colors
Definition notcurses.h:1637
bool can_change_colors
Definition notcurses.h:1640
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
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
unsigned margin_r
Definition notcurses.h:1473
const char * name
Definition notcurses.h:1470
int(* resizecb)(struct ncplane *)
Definition notcurses.h:1471
unsigned margin_b
Definition notcurses.h:1473
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
ncstats s
Definition internal.h:247
pthread_mutex_t lock
Definition internal.h:246
unsigned planes
Definition notcurses.h:1820
uint64_t fbbytes
Definition notcurses.h:1819
rasterstate rstate
Definition internal.h:336
ncstats stashed_stats
Definition internal.h:357
ncpalette palette
Definition internal.h:366
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
int cursory
Definition internal.h:353
bool touched_palette
Definition internal.h:368
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
struct sprixel * prev
Definition sprite.h:145
struct sprixel * next
Definition sprite.h:144
int maxpaletteread
Definition termdesc.h:193
ncpixelimpl_e pixel_implementation
Definition termdesc.h:134
unsigned cellpxx
Definition termdesc.h:117
unsigned pixx
Definition termdesc.h:113
unsigned pixy
Definition termdesc.h:112
unsigned kbdlevel
Definition termdesc.h:216
unsigned dimx
Definition termdesc.h:118
ncpalette originalpalette
Definition termdesc.h:192
nccapabilities caps
Definition termdesc.h:111
struct termios * tpreserved
Definition termdesc.h:180
bool in_alt_screen
Definition termdesc.h:219
unsigned sixel_maxx
Definition termdesc.h:165
int default_rows
Definition termdesc.h:189
int(* pixel_draw)(const struct tinfo *, const struct ncpile *p, struct sprixel *s, fbuf *f, int y, int x)
Definition termdesc.h:147
unsigned dimy
Definition termdesc.h:118
unsigned sixel_maxy
Definition termdesc.h:172
unsigned sixel_maxy_pristine
Definition termdesc.h:173
int default_cols
Definition termdesc.h:190
unsigned cellpxy
Definition termdesc.h:116
int ttyfd
Definition termdesc.h:109
int enter_alternate_screen(int fd, FILE *ttyfp, tinfo *ti, unsigned drain)
Definition termdesc.c:559
int locate_cursor(tinfo *ti, unsigned *cursor_y, unsigned *cursor_x)
Definition termdesc.c:1581
void free_terminfo_cache(tinfo *ti)
Definition termdesc.c:197
int interrogate_terminfo(tinfo *ti, FILE *out, unsigned utf8, unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts, int *cursor_y, int *cursor_x, ncsharedstats *stats, int lmargin, int tmargin, int rmargin, int bmargin, unsigned draininput)
Definition termdesc.c:1317
int leave_alternate_screen(int fd, FILE *fp, tinfo *ti, unsigned drain)
Definition termdesc.c:611
char * termdesc_longterm(const tinfo *ti)
Definition termdesc.c:1558
int tiocgwinsz(int fd, struct winsize *ws)
Definition termdesc.c:1599
return NULL
Definition termdesc.h:229
@ ESCAPE_RMKX
Definition termdesc.h:66
@ ESCAPE_CIVIS
Definition termdesc.h:54
@ ESCAPE_CNORM
Definition termdesc.h:55
@ ESCAPE_RESTORECOLORS
Definition termdesc.h:88
@ ESCAPE_OP
Definition termdesc.h:50
@ ESCAPE_OC
Definition termdesc.h:56
@ ESCAPE_RMCUP
Definition termdesc.h:69
@ ESCAPE_SMCUP
Definition termdesc.h:68
@ ESCAPE_SAVECOLORS
Definition termdesc.h:87
@ ESCAPE_INITC
Definition termdesc.h:81
@ ESCAPE_SGR0
Definition termdesc.h:53
#define XTMODKEYSUNDO
Definition termdesc.h:27
#define KKEYBOARD_POP
Definition termdesc.h:22
int setup_signals(void *vnc, bool no_quit_sigs, bool no_winch_sigs, int(*handler)(void *, void **, int))
Definition unixsig.c:195
int drop_signals(void *nc, void **altstack)
Definition unixsig.c:104