-
-
Notifications
You must be signed in to change notification settings - Fork 113
/
notcurses.h
4671 lines (4070 loc) · 199 KB
/
notcurses.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#ifndef NOTCURSES_NOTCURSES
#define NOTCURSES_NOTCURSES
#include <time.h>
#include <ctype.h>
#include <wchar.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <limits.h>
#include <stdbool.h>
#include <notcurses/ncport.h>
#include <notcurses/nckeys.h>
#include <notcurses/ncseqs.h>
#ifdef __cplusplus
extern "C" {
#define RESTRICT
#define _Static_assert(...)
#else
#define RESTRICT restrict
#endif
#ifdef NOTCURSES_FFI
#define static API
#endif
#ifndef __MINGW32__
#define API __attribute__((visibility("default")))
#else
#define API __declspec(dllexport)
#endif
#define ALLOC __attribute__((malloc)) __attribute__((warn_unused_result))
// Get a human-readable string describing the running Notcurses version.
API const char* notcurses_version(void);
// Cannot be inline, as we want to get the versions of the actual Notcurses
// library we loaded, not what we compile against.
API void notcurses_version_components(int* major, int* minor, int* patch, int* tweak);
struct notcurses; // Notcurses state for a given terminal, composed of ncplanes
struct ncplane; // a drawable Notcurses surface, composed of cells
struct ncvisual; // a visual bit of multimedia opened with LibAV|OIIO
struct ncuplot; // uint64_t histogram
struct ncdplot; // double histogram
struct ncprogbar; // progress bar
struct ncfdplane; // i/o wrapper to dump file descriptor to plane
struct ncsubproc; // ncfdplane wrapper with subprocess management
struct ncselector;// widget supporting selecting 1 from a list of options
struct ncmultiselector; // widget supporting selecting 0..n from n options
struct ncreader; // widget supporting free string input ala readline
struct ncfadectx; // context for a palette fade operation
struct nctablet; // grouped item within an ncreel
struct ncreel; // hierarchical block-based data browser
struct nctab; // grouped item within an nctabbed
struct nctabbed; // widget with one tab visible at a time
struct ncdirect; // direct mode context
// we never blit full blocks, but instead spaces (more efficient) with the
// background set to the desired foreground. these need be kept in the same
// order as the blitters[] definition in lib/blit.c.
typedef enum {
NCBLIT_DEFAULT, // let the ncvisual pick
NCBLIT_1x1, // space, compatible with ASCII
NCBLIT_2x1, // halves + 1x1 (space) ▄▀
NCBLIT_2x2, // quadrants + 2x1 ▗▐ ▖▀▟▌▙
NCBLIT_3x2, // sextants (*NOT* 2x2) 🬀🬁🬂🬃🬄🬅🬆🬇🬈🬉🬊🬋🬌🬍🬎🬏🬐🬑🬒🬓🬔🬕🬖🬗🬘🬙🬚🬛🬜🬝🬞
NCBLIT_BRAILLE, // 4 rows, 2 cols (braille) ⡀⡄⡆⡇⢀⣀⣄⣆⣇⢠⣠⣤⣦⣧⢰⣰⣴⣶⣷⢸⣸⣼⣾⣿
NCBLIT_PIXEL, // pixel graphics
// these blitters are suitable only for plots, not general media
NCBLIT_4x1, // four vertical levels █▆▄▂
NCBLIT_8x1, // eight vertical levels █▇▆▅▄▃▂▁
} ncblitter_e;
// Alignment within a plane or terminal. Left/right-justified, or centered.
typedef enum {
NCALIGN_UNALIGNED,
NCALIGN_LEFT,
NCALIGN_CENTER,
NCALIGN_RIGHT,
} ncalign_e;
#define NCALIGN_TOP NCALIGN_LEFT
#define NCALIGN_BOTTOM NCALIGN_RIGHT
// How to scale an ncvisual during rendering. NCSCALE_NONE will apply no
// scaling. NCSCALE_SCALE scales a visual to the plane's size, maintaining
// aspect ratio. NCSCALE_STRETCH stretches and scales the image in an attempt
// to fill the entirety of the plane. NCSCALE_NONE_HIRES and
// NCSCALE_SCALE_HIRES behave like their counterparts, but admit blitters
// which don't preserve aspect ratio.
typedef enum {
NCSCALE_NONE,
NCSCALE_SCALE,
NCSCALE_STRETCH,
NCSCALE_NONE_HIRES,
NCSCALE_SCALE_HIRES,
} ncscale_e;
// background cannot be highcontrast, only foreground
#define NCALPHA_HIGHCONTRAST 0x30000000ull
#define NCALPHA_TRANSPARENT 0x20000000ull
#define NCALPHA_BLEND 0x10000000ull
#define NCALPHA_OPAQUE 0x00000000ull
// we support palette-indexed color up to 8 bits.
#define NCPALETTESIZE 256
// Does this glyph completely obscure the background? If so, there's no need
// to emit a background when rasterizing, a small optimization. These are
// also used to track regions into which we must not cellblit.
#define NC_NOBACKGROUND_MASK 0x8700000000000000ull
// if this bit is set, we are *not* using the default background color
#define NC_BGDEFAULT_MASK 0x0000000040000000ull
// extract these bits to get the background RGB value
#define NC_BG_RGB_MASK 0x0000000000ffffffull
// if this bit *and* NC_BGDEFAULT_MASK are set, we're using a
// palette-indexed background color
#define NC_BG_PALETTE 0x0000000008000000ull
// extract these bits to get the background alpha mask
#define NC_BG_ALPHA_MASK 0x30000000ull
// initialize a 32-bit channel pair with specified RGB
#define NCCHANNEL_INITIALIZER(r, g, b) \
(((uint32_t)(r) << 16u) + ((uint32_t)(g) << 8u) + (b) + NC_BGDEFAULT_MASK)
// initialize a 64-bit channel pair with specified RGB fg/bg
#define NCCHANNELS_INITIALIZER(fr, fg, fb, br, bg, bb) \
((NCCHANNEL_INITIALIZER((fr), (fg), (fb)) << 32ull) + \
(NCCHANNEL_INITIALIZER((br), (bg), (bb))))
// These lowest-level functions manipulate a channel encodings directly. Users
// will typically manipulate ncplanes' and nccells' channels through their
// APIs, rather than calling these explicitly.
// Extract the 2-bit alpha component from a 32-bit channel. It is not
// shifted down, and can be directly compared to NCALPHA_* values.
static inline unsigned
ncchannel_alpha(uint32_t channel){
return channel & NC_BG_ALPHA_MASK;
}
// Set the 2-bit alpha component of the 32-bit channel. Background channels
// must not be set to NCALPHA_HIGHCONTRAST. It is an error if alpha contains
// any bits other than NCALPHA_*.
static inline int
ncchannel_set_alpha(uint32_t* channel, unsigned alpha){
if(alpha & ~NC_BG_ALPHA_MASK){
return -1;
}
*channel = (uint32_t)alpha | (*channel & (uint32_t)~NC_BG_ALPHA_MASK);
if(alpha != NCALPHA_OPAQUE){
*channel |= NC_BGDEFAULT_MASK;
}
return 0;
}
// Is this channel using the "default color" rather than RGB/palette-indexed?
static inline bool
ncchannel_default_p(uint32_t channel){
return !(channel & NC_BGDEFAULT_MASK);
}
// Mark the channel as using its default color. Alpha is set opaque.
static inline uint32_t
ncchannel_set_default(uint32_t* channel){
*channel &= (uint32_t)~NC_BGDEFAULT_MASK; // turn off not-default bit
ncchannel_set_alpha(channel, NCALPHA_OPAQUE);
return *channel;
}
// Is this channel using palette-indexed color?
static inline bool
ncchannel_palindex_p(uint32_t channel){
return !ncchannel_default_p(channel) && (channel & NC_BG_PALETTE);
}
// Extract the palette index from a channel. Only valid if
// ncchannel_palindex_p() would return true for the channel.
static inline unsigned
ncchannel_palindex(uint32_t channel){
return channel & 0xff;
}
// Mark the channel as using the specified palette color. It is an error if
// the index is greater than NCPALETTESIZE. Alpha is set opaque.
static inline int
ncchannel_set_palindex(uint32_t* channel, unsigned idx){
if(idx >= NCPALETTESIZE){
return -1;
}
ncchannel_set_alpha(channel, NCALPHA_OPAQUE);
*channel &= 0xff000000ull;
*channel |= NC_BGDEFAULT_MASK | NC_BG_PALETTE | idx;
return 0;
}
// Is this channel using RGB color?
static inline bool
ncchannel_rgb_p(uint32_t channel){
// bitwise or is intentional (allows compiler more freedom)
return !(ncchannel_default_p(channel) | ncchannel_palindex_p(channel));
}
// Extract the 8-bit red component from a 32-bit channel. Only valid if
// ncchannel_rgb_p() would return true for the channel.
static inline unsigned
ncchannel_r(uint32_t channel){
return (channel & 0xff0000u) >> 16u;
}
// Extract the 8-bit green component from a 32-bit channel. Only valid if
// ncchannel_rgb_p() would return true for the channel.
static inline unsigned
ncchannel_g(uint32_t channel){
return (channel & 0x00ff00u) >> 8u;
}
// Extract the 8-bit blue component from a 32-bit channel. Only valid if
// ncchannel_rgb_p() would return true for the channel.
static inline unsigned
ncchannel_b(uint32_t channel){
return (channel & 0x0000ffu);
}
// Extract the 24-bit RGB value from a 32-bit channel.
// Only valid if ncchannel_rgb_p() would return true for the channel.
static inline uint32_t
ncchannel_rgb(uint32_t channel){
return channel & NC_BG_RGB_MASK;
}
// Extract the three 8-bit R/G/B components from a 32-bit channel.
// Only valid if ncchannel_rgb_p() would return true for the channel.
static inline uint32_t
ncchannel_rgb8(uint32_t channel, unsigned* RESTRICT r, unsigned* RESTRICT g,
unsigned* RESTRICT b){
*r = ncchannel_r(channel);
*g = ncchannel_g(channel);
*b = ncchannel_b(channel);
return channel;
}
// Set the three 8-bit components of a 32-bit channel, and mark it as not using
// the default color. Retain the other bits unchanged. Any value greater than
// 255 will result in a return of -1 and no change to the channel.
static inline int
ncchannel_set_rgb8(uint32_t* channel, unsigned r, unsigned g, unsigned b){
if(r >= 256 || g >= 256 || b >= 256){
return -1;
}
uint32_t c = (r << 16u) | (g << 8u) | b;
// clear the existing rgb bits, clear the palette index indicator, set
// the not-default bit, and or in the new rgb.
*channel = (uint32_t)((*channel & ~(NC_BG_RGB_MASK | NC_BG_PALETTE)) | NC_BGDEFAULT_MASK | c);
return 0;
}
// Same, but provide an assembled, packed 24 bits of rgb.
static inline int
ncchannel_set(uint32_t* channel, uint32_t rgb){
if(rgb > 0xffffffu){
return -1;
}
*channel = (uint32_t)((*channel & ~(NC_BG_RGB_MASK | NC_BG_PALETTE)) | NC_BGDEFAULT_MASK | rgb);
return 0;
}
// Set the three 8-bit components of a 32-bit channel, and mark it as not using
// the default color. Retain the other bits unchanged. r, g, and b will be
// clipped to the range [0..255].
static inline void
ncchannel_set_rgb8_clipped(uint32_t* channel, int r, int g, int b){
if(r >= 256){
r = 255;
}
if(g >= 256){
g = 255;
}
if(b >= 256){
b = 255;
}
if(r <= -1){
r = 0;
}
if(g <= -1){
g = 0;
}
if(b <= -1){
b = 0;
}
uint32_t c = (uint32_t)((r << 16u) | (g << 8u) | b);
*channel = (uint32_t)((*channel & ~(NC_BG_RGB_MASK | NC_BG_PALETTE)) | NC_BGDEFAULT_MASK | c);
}
// Extract the background alpha and coloring bits from a 64-bit channel
// pair as a single 32-bit value.
static inline uint32_t
ncchannels_bchannel(uint64_t channels){
return channels & (NC_BG_RGB_MASK | NC_BG_PALETTE |
NC_BGDEFAULT_MASK | NC_BG_ALPHA_MASK);
}
// Extract the foreground alpha and coloring bits from a 64-bit channel
// pair as a single 32-bit value.
static inline uint32_t
ncchannels_fchannel(uint64_t channels){
return ncchannels_bchannel(channels >> 32u);
}
// Extract the background alpha and coloring bits from a 64-bit channel pair.
static inline uint64_t
ncchannels_channels(uint64_t channels){
return ncchannels_bchannel(channels) |
((uint64_t)ncchannels_fchannel(channels) << 32u);
}
static inline bool
ncchannels_bg_rgb_p(uint64_t channels){
return ncchannel_rgb_p(ncchannels_bchannel(channels));
}
static inline bool
ncchannels_fg_rgb_p(uint64_t channels){
return ncchannel_rgb_p(ncchannels_fchannel(channels));
}
// Extract 2 bits of background alpha from 'channels', shifted to LSBs.
static inline unsigned
ncchannels_bg_alpha(uint64_t channels){
return ncchannel_alpha(ncchannels_bchannel(channels));
}
// Set the background alpha and coloring bits of the 64-bit channel pair
// from a single 32-bit value.
static inline uint64_t
ncchannels_set_bchannel(uint64_t* channels, uint32_t channel){
// drop the background color and alpha bit
*channels &= ((0xffffffffllu << 32u) | NC_NOBACKGROUND_MASK);
*channels |= (uint32_t)(channel & ~NC_NOBACKGROUND_MASK);
return *channels;
}
// Set the foreground alpha and coloring bits of the 64-bit channel pair
// from a single 32-bit value.
static inline uint64_t
ncchannels_set_fchannel(uint64_t* channels, uint32_t channel){
// drop the foreground color and alpha bit
*channels &= (0xffffffffllu | ((uint64_t)NC_NOBACKGROUND_MASK << 32u));
*channels |= (uint64_t)(channel & ~NC_NOBACKGROUND_MASK) << 32u;
return *channels;
}
// Set the alpha and coloring bits of a channel pair from another channel pair.
static inline uint64_t
ncchannels_set_channels(uint64_t* dst, uint64_t channels){
ncchannels_set_bchannel(dst, channels & 0xffffffffull);
ncchannels_set_fchannel(dst, (uint32_t)((channels >> 32u) & 0xffffffffull));
return *dst;
}
// Set the 2-bit alpha component of the background channel.
static inline int
ncchannels_set_bg_alpha(uint64_t* channels, unsigned alpha){
if(alpha == NCALPHA_HIGHCONTRAST){ // forbidden for background alpha
return -1;
}
uint32_t channel = ncchannels_bchannel(*channels);
if(ncchannel_set_alpha(&channel, alpha) < 0){
return -1;
}
ncchannels_set_bchannel(channels, channel);
return 0;
}
// Extract 2 bits of foreground alpha from 'channels', shifted to LSBs.
static inline unsigned
ncchannels_fg_alpha(uint64_t channels){
return ncchannel_alpha(ncchannels_fchannel(channels));
}
// Set the 2-bit alpha component of the foreground channel.
static inline int
ncchannels_set_fg_alpha(uint64_t* channels, unsigned alpha){
uint32_t channel = ncchannels_fchannel(*channels);
if(ncchannel_set_alpha(&channel, alpha) < 0){
return -1;
}
*channels = ((uint64_t)channel << 32llu) | (*channels & 0xffffffffllu);
return 0;
}
// Returns the channels with the fore- and background's color information
// swapped, but without touching housekeeping bits. Alpha is retained unless
// it would lead to an illegal state: HIGHCONTRAST, TRANSPARENT, and BLEND
// are taken to OPAQUE unless the new value is RGB.
static inline uint64_t
ncchannels_reverse(uint64_t channels){
const uint64_t raw = ((uint64_t)ncchannels_bchannel(channels) << 32u) +
ncchannels_fchannel(channels);
const uint64_t statemask = ((NC_NOBACKGROUND_MASK | NC_BG_ALPHA_MASK) << 32u) |
NC_NOBACKGROUND_MASK | NC_BG_ALPHA_MASK;
uint64_t ret = raw & ~statemask;
ret |= channels & statemask;
if(ncchannels_bg_alpha(ret) != NCALPHA_OPAQUE){
if(!ncchannels_bg_rgb_p(ret)){
ncchannels_set_bg_alpha(&ret, NCALPHA_OPAQUE);
}
}
if(ncchannels_fg_alpha(ret) != NCALPHA_OPAQUE){
if(!ncchannels_fg_rgb_p(ret)){
ncchannels_set_fg_alpha(&ret, NCALPHA_OPAQUE);
}
}
return ret;
}
// Creates a new channel pair using 'fchan' as the foreground channel
// and 'bchan' as the background channel.
static inline uint64_t
ncchannels_combine(uint32_t fchan, uint32_t bchan){
uint64_t channels = 0;
ncchannels_set_fchannel(&channels, fchan);
ncchannels_set_bchannel(&channels, bchan);
return channels;
}
static inline unsigned
ncchannels_fg_palindex(uint64_t channels){
return ncchannel_palindex(ncchannels_fchannel(channels));
}
static inline unsigned
ncchannels_bg_palindex(uint64_t channels){
return ncchannel_palindex(ncchannels_bchannel(channels));
}
// Extract 24 bits of foreground RGB from 'channels', shifted to LSBs.
static inline uint32_t
ncchannels_fg_rgb(uint64_t channels){
return ncchannel_rgb(ncchannels_fchannel(channels));
}
// Extract 24 bits of background RGB from 'channels', shifted to LSBs.
static inline uint32_t
ncchannels_bg_rgb(uint64_t channels){
return ncchannel_rgb(ncchannels_bchannel(channels));
}
// Extract 24 bits of foreground RGB from 'channels', split into subchannels.
static inline uint32_t
ncchannels_fg_rgb8(uint64_t channels, unsigned* r, unsigned* g, unsigned* b){
return ncchannel_rgb8(ncchannels_fchannel(channels), r, g, b);
}
// Extract 24 bits of background RGB from 'channels', split into subchannels.
static inline uint32_t
ncchannels_bg_rgb8(uint64_t channels, unsigned* r, unsigned* g, unsigned* b){
return ncchannel_rgb8(ncchannels_bchannel(channels), r, g, b);
}
// Set the r, g, and b channels for the foreground component of this 64-bit
// 'channels' variable, and mark it as not using the default color.
static inline int
ncchannels_set_fg_rgb8(uint64_t* channels, unsigned r, unsigned g, unsigned b){
uint32_t channel = ncchannels_fchannel(*channels);
if(ncchannel_set_rgb8(&channel, r, g, b) < 0){
return -1;
}
*channels = ((uint64_t)channel << 32llu) | (*channels & 0xffffffffllu);
return 0;
}
// Same, but clips to [0..255].
static inline void
ncchannels_set_fg_rgb8_clipped(uint64_t* channels, int r, int g, int b){
uint32_t channel = ncchannels_fchannel(*channels);
ncchannel_set_rgb8_clipped(&channel, r, g, b);
*channels = ((uint64_t)channel << 32llu) | (*channels & 0xffffffffllu);
}
static inline int
ncchannels_set_fg_palindex(uint64_t* channels, unsigned idx){
uint32_t channel = ncchannels_fchannel(*channels);
if(ncchannel_set_palindex(&channel, idx) < 0){
return -1;
}
*channels = ((uint64_t)channel << 32llu) | (*channels & 0xffffffffllu);
return 0;
}
// Same, but set an assembled 24 bit channel at once.
static inline int
ncchannels_set_fg_rgb(uint64_t* channels, unsigned rgb){
uint32_t channel = ncchannels_fchannel(*channels);
if(ncchannel_set(&channel, rgb) < 0){
return -1;
}
*channels = ((uint64_t)channel << 32llu) | (*channels & 0xffffffffllu);
return 0;
}
// Set the r, g, and b channels for the background component of this 64-bit
// 'channels' variable, and mark it as not using the default color.
static inline int
ncchannels_set_bg_rgb8(uint64_t* channels, unsigned r, unsigned g, unsigned b){
uint32_t channel = ncchannels_bchannel(*channels);
if(ncchannel_set_rgb8(&channel, r, g, b) < 0){
return -1;
}
ncchannels_set_bchannel(channels, channel);
return 0;
}
// Same, but clips to [0..255].
static inline void
ncchannels_set_bg_rgb8_clipped(uint64_t* channels, int r, int g, int b){
uint32_t channel = ncchannels_bchannel(*channels);
ncchannel_set_rgb8_clipped(&channel, r, g, b);
ncchannels_set_bchannel(channels, channel);
}
// Set the cell's background palette index, set the background palette index
// bit, set it background-opaque, and clear the background default color bit.
static inline int
ncchannels_set_bg_palindex(uint64_t* channels, unsigned idx){
uint32_t channel = ncchannels_bchannel(*channels);
if(ncchannel_set_palindex(&channel, idx) < 0){
return -1;
}
ncchannels_set_bchannel(channels, channel);
return 0;
}
// Same, but set an assembled 24 bit channel at once.
static inline int
ncchannels_set_bg_rgb(uint64_t* channels, unsigned rgb){
uint32_t channel = ncchannels_bchannel(*channels);
if(ncchannel_set(&channel, rgb) < 0){
return -1;
}
ncchannels_set_bchannel(channels, channel);
return 0;
}
// Is the foreground using the "default foreground color"?
static inline bool
ncchannels_fg_default_p(uint64_t channels){
return ncchannel_default_p(ncchannels_fchannel(channels));
}
// Is the foreground using indexed palette color?
static inline bool
ncchannels_fg_palindex_p(uint64_t channels){
return ncchannel_palindex_p(ncchannels_fchannel(channels));
}
// Is the background using the "default background color"? The "default
// background color" must generally be used to take advantage of
// terminal-effected transparency.
static inline bool
ncchannels_bg_default_p(uint64_t channels){
return ncchannel_default_p(ncchannels_bchannel(channels));
}
// Is the background using indexed palette color?
static inline bool
ncchannels_bg_palindex_p(uint64_t channels){
return ncchannel_palindex_p(ncchannels_bchannel(channels));
}
// Mark the foreground channel as using its default color.
static inline uint64_t
ncchannels_set_fg_default(uint64_t* channels){
uint32_t channel = ncchannels_fchannel(*channels);
ncchannel_set_default(&channel);
ncchannels_set_fchannel(channels, channel);
return *channels;
}
// Mark the background channel as using its default color.
static inline uint64_t
ncchannels_set_bg_default(uint64_t* channels){
uint32_t channel = ncchannels_bchannel(*channels);
ncchannel_set_default(&channel);
ncchannels_set_bchannel(channels, channel);
return *channels;
}
// 0x0--0x10ffff can be UTF-8-encoded with only 4 bytes
#define WCHAR_MAX_UTF8BYTES 4
// Returns the number of columns occupied by the longest valid prefix of a
// multibyte (UTF-8) string. If an invalid character is encountered, -1 will be
// returned, and the number of valid bytes and columns will be written into
// *|validbytes| and *|validwidth| (assuming them non-NULL). If the entire
// string is valid, *|validbytes| and *|validwidth| reflect the entire string.
API int ncstrwidth(const char* egcs, int* validbytes, int* validwidth)
__attribute__ ((nonnull (1)));
// input functions like notcurses_get() return ucs32-encoded uint32_t. convert
// a series of uint32_t to utf8. result must be at least 4 bytes per input
// uint32_t (6 bytes per uint32_t will future-proof against Unicode expansion).
// the number of bytes used is returned, or -1 if passed illegal ucs32, or too
// small of a buffer.
API int notcurses_ucs32_to_utf8(const uint32_t* ucs32, unsigned ucs32count,
unsigned char* resultbuf, size_t buflen)
__attribute__ ((nonnull (1, 3)));
// An nccell corresponds to a single character cell on some plane, which can be
// occupied by a single grapheme cluster (some root spacing glyph, along with
// possible combining characters, which might span multiple columns). At any
// cell, we can have a theoretically arbitrarily long UTF-8 EGC, a foreground
// color, a background color, and an attribute set. Valid grapheme cluster
// contents include:
//
// * A NUL terminator,
// * A single control character, followed by a NUL terminator,
// * At most one spacing character, followed by zero or more nonspacing
// characters, followed by a NUL terminator.
//
// Multi-column characters can only have a single style/color throughout.
// Existence is suffering, and thus wcwidth() is not reliable. It's just
// quoting whether or not the EGC contains a "Wide Asian" double-width
// character. This is set for some things, like most emoji, and not set for
// other things, like cuneiform. True display width is a *function of the
// font and terminal*. Among the longest Unicode codepoints is
//
// U+FDFD ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM ﷽
//
// wcwidth() rather optimistically claims this most exalted glyph to occupy
// a single column. BiDi text is too complicated for me to even get into here.
// Be assured there are no easy answers; ours is indeed a disturbing Universe.
//
// Each nccell occupies 16 static bytes (128 bits). The surface is thus ~1.6MB
// for a (pretty large) 500x200 terminal. At 80x43, it's less than 64KB.
// Dynamic requirements (the egcpool) can add up to 16MB to an ncplane, but
// such large pools are unlikely in common use.
//
// We implement some small alpha compositing. Foreground and background both
// have two bits of inverted alpha. The actual grapheme written to a cell is
// the topmost non-zero grapheme. If its alpha is 00, its foreground color is
// used unchanged. If its alpha is 10, its foreground color is derived entirely
// from cells underneath it. Otherwise, the result will be a composite.
// Likewise for the background. If the bottom of a coordinate's zbuffer is
// reached with a cumulative alpha of zero, the default is used. In this way,
// a terminal configured with transparent background can be supported through
// multiple occluding ncplanes. A foreground alpha of 11 requests high-contrast
// text (relative to the computed background). A background alpha of 11 is
// currently forbidden.
//
// Default color takes precedence over palette or RGB, and cannot be used with
// transparency. Indexed palette takes precedence over RGB. It cannot
// meaningfully set transparency, but it can be mixed into a cascading color.
// RGB is used if neither default terminal colors nor palette indexing are in
// play, and fully supports all transparency options.
//
// This structure is exposed only so that most functions can be inlined. Do not
// directly modify or access the fields of this structure; use the API.
typedef struct nccell {
// These 32 bits, together with the associated plane's associated egcpool,
// completely define this cell's EGC. Unless the EGC requires more than four
// bytes to encode as UTF-8, it will be inlined here. If more than four bytes
// are required, it will be spilled into the egcpool. In either case, there's
// a NUL-terminated string available without copying, because (1) the egcpool
// is all NUL-terminated sequences and (2) the fifth byte of this struct (the
// gcluster_backstop field, see below) is guaranteed to be zero, as are any
// unused bytes in gcluster.
//
// The gcluster + gcluster_backstop thus form a valid C string of between 0
// and 4 non-NUL bytes. Interpreting them in this fashion requires that
// gcluster be stored as a little-endian number (strings have no byte order).
// This gives rise to three simple rules:
//
// * when storing to gcluster from a numeric, always use htole()
// * when loading from gcluster for numeric use, always use htole()
// * when referencing gcluster as a string, always use a pointer cast
//
// Uses of gcluster ought thus always have exactly one htole() or pointer
// cast associated with them, and we otherwise always work as host-endian.
//
// A spilled EGC is indicated by the value 0x01XXXXXX. This cannot alias a
// true supra-ASCII EGC, because UTF-8 only encodes bytes <= 0x80 when they
// are single-byte ASCII-derived values. The XXXXXX is interpreted as a 24-bit
// index into the egcpool. These pools may thus be up to 16MB.
//
// The cost of this scheme is that the character 0x01 (SOH) cannot be encoded
// in a nccell, which we want anyway. It must not be allowed through the API,
// or havoc will result.
uint32_t gcluster; // 4B → 4B little endian EGC
uint8_t gcluster_backstop; // 1B → 5B (8 bits of zero)
// we store the column width in this field. for a multicolumn EGC of N
// columns, there will be N nccells, and each has a width of N...for now.
// eventually, such an EGC will set more than one subsequent cell to
// WIDE_RIGHT, and this won't be necessary. it can then be used as a
// bytecount. see #1203. FIXME iff width >= 2, the cell is part of a
// multicolumn glyph. whether a cell is the left or right side of the glyph
// can be determined by checking whether ->gcluster is zero.
uint8_t width; // 1B → 6B (8 bits of EGC column width)
uint16_t stylemask; // 2B → 8B (16 bits of NCSTYLE_* attributes)
// (channels & 0x8000000000000000ull): blitted to upper-left quadrant
// (channels & 0x4000000000000000ull): foreground is *not* "default color"
// (channels & 0x3000000000000000ull): foreground alpha (2 bits)
// (channels & 0x0800000000000000ull): foreground uses palette index
// (channels & 0x0400000000000000ull): blitted to upper-right quadrant
// (channels & 0x0200000000000000ull): blitted to lower-left quadrant
// (channels & 0x0100000000000000ull): blitted to lower-right quadrant
// (channels & 0x00ffffff00000000ull): foreground in 3x8 RGB (rrggbb) / pindex
// (channels & 0x0000000080000000ull): reserved, must be 0
// (channels & 0x0000000040000000ull): background is *not* "default color"
// (channels & 0x0000000030000000ull): background alpha (2 bits)
// (channels & 0x0000000008000000ull): background uses palette index
// (channels & 0x0000000007000000ull): reserved, must be 0
// (channels & 0x0000000000ffffffull): background in 3x8 RGB (rrggbb) / pindex
// At render time, these 24-bit values are quantized down to terminal
// capabilities, if necessary. There's a clear path to 10-bit support should
// we one day need it, but keep things cagey for now. "default color" is
// best explained by color(3NCURSES). ours is the same concept. until the
// "not default color" bit is set, any color you load will be ignored.
uint64_t channels; // + 8B == 16B
} nccell;
// do *not* load invalid EGCs using these macros! there is no way for us to
// protect against such misuse here. problems *will* ensue. similarly, do not
// set channel flags other than colors/alpha. we assign non-printing glyphs
// a width of 1 to match utf8_egc_len()'s behavior for whitespace/NUL.
// FIXME can we enforce this with static_assert?
#define NCCELL_INITIALIZER(c, s, chan) { .gcluster = (htole(c)), .gcluster_backstop = 0,\
.width = (uint8_t)((wcwidth(c) < 0 || !c) ? 1 : wcwidth(c)), .stylemask = (s), .channels = (chan), }
// python fails on #define CELL_CHAR_INITIALIZER(c) CELL_INITIALIZER(c, 0, 0)
#define NCCELL_CHAR_INITIALIZER(c) { .gcluster = (htole(c)), .gcluster_backstop = 0,\
.width = (uint8_t)((wcwidth(c) < 0 || !c) ? 1 : wcwidth(c)), .stylemask = 0, .channels = 0, }
// python fails on #define CELL_TRIVIAL_INITIALIZER CELL_CHAR_INITIALIZER(0)
#define NCCELL_TRIVIAL_INITIALIZER { .gcluster = 0, .gcluster_backstop = 0,\
.width = 1, .stylemask = 0, .channels = 0, }
static inline void
nccell_init(nccell* c){
memset(c, 0, sizeof(*c));
}
// Breaks the UTF-8 string in 'gcluster' down, setting up the nccell 'c'.
// Returns the number of bytes copied out of 'gcluster', or -1 on failure. The
// styling of the cell is left untouched, but any resources are released.
API int nccell_load(struct ncplane* n, nccell* c, const char* gcluster);
// nccell_load(), plus blast the styling with 'attr' and 'channels'.
static inline int
nccell_prime(struct ncplane* n, nccell* c, const char* gcluster,
uint16_t stylemask, uint64_t channels){
c->stylemask = stylemask;
c->channels = channels;
int ret = nccell_load(n, c, gcluster);
return ret;
}
// Duplicate 'c' into 'targ'; both must be/will be bound to 'n'. Returns -1 on
// failure, and 0 on success.
API int nccell_duplicate(struct ncplane* n, nccell* targ, const nccell* c);
// Release resources held by the nccell 'c'.
API void nccell_release(struct ncplane* n, nccell* c);
// if you want reverse video, try ncchannels_reverse(). if you want blink, try
// ncplane_pulse(). if you want protection, put things on a different plane.
#define NCSTYLE_MASK 0xffffu
#define NCSTYLE_ITALIC 0x0010u
#define NCSTYLE_UNDERLINE 0x0008u
#define NCSTYLE_UNDERCURL 0x0004u
#define NCSTYLE_BOLD 0x0002u
#define NCSTYLE_STRUCK 0x0001u
#define NCSTYLE_NONE 0
// Set the specified style bits for the nccell 'c', whether they're actively
// supported or not. Only the lower 16 bits are meaningful.
static inline void
nccell_set_styles(nccell* c, unsigned stylebits){
c->stylemask = stylebits & NCSTYLE_MASK;
}
// Extract the style bits from the nccell.
static inline uint16_t
nccell_styles(const nccell* c){
return c->stylemask;
}
// Add the specified styles (in the LSBs) to the nccell's existing spec,
// whether they're actively supported or not.
static inline void
nccell_on_styles(nccell* c, unsigned stylebits){
c->stylemask |= (uint16_t)(stylebits & NCSTYLE_MASK);
}
// Remove the specified styles (in the LSBs) from the nccell's existing spec.
static inline void
nccell_off_styles(nccell* c, unsigned stylebits){
c->stylemask &= (uint16_t)~(stylebits & NCSTYLE_MASK);
}
// Use the default color for the foreground.
static inline void
nccell_set_fg_default(nccell* c){
ncchannels_set_fg_default(&c->channels);
}
// Use the default color for the background.
static inline void
nccell_set_bg_default(nccell* c){
ncchannels_set_bg_default(&c->channels);
}
static inline int
nccell_set_fg_alpha(nccell* c, unsigned alpha){
return ncchannels_set_fg_alpha(&c->channels, alpha);
}
static inline int
nccell_set_bg_alpha(nccell* c, unsigned alpha){
return ncchannels_set_bg_alpha(&c->channels, alpha);
}
static inline uint64_t
nccell_set_bchannel(nccell* c, uint32_t channel){
return ncchannels_set_bchannel(&c->channels, channel);
}
static inline uint64_t
nccell_set_fchannel(nccell* c, uint32_t channel){
return ncchannels_set_fchannel(&c->channels, channel);
}
static inline uint64_t
nccell_set_channels(nccell* c, uint64_t channels){
return ncchannels_set_channels(&c->channels, channels);
}
// Is the cell part of a multicolumn element?
static inline bool
nccell_double_wide_p(const nccell* c){
return (c->width >= 2);
}
// Is this the right half of a wide character?
static inline bool
nccell_wide_right_p(const nccell* c){
return nccell_double_wide_p(c) && c->gcluster == 0;
}
// Is this the left half of a wide character?
static inline bool
nccell_wide_left_p(const nccell* c){
return nccell_double_wide_p(c) && c->gcluster;
}
// return a pointer to the NUL-terminated EGC referenced by 'c'. this pointer
// can be invalidated by any further operation on the plane 'n', so...watch out!
API __attribute__ ((returns_nonnull)) const char*
nccell_extended_gcluster(const struct ncplane* n, const nccell* c);
static inline uint64_t
nccell_channels(const nccell* c){
return ncchannels_channels(c->channels);
}
// Extract the background alpha and coloring bits from a cell's channels
// as a single 32-bit value.
static inline uint32_t
nccell_bchannel(const nccell* cl){
return ncchannels_bchannel(cl->channels);
}
// Extract the foreground alpha and coloring bits from a cell's channels
// as a single 32-bit value.
static inline uint32_t
nccell_fchannel(const nccell* cl){
return ncchannels_fchannel(cl->channels);
}
// return the number of columns occupied by 'c'. see ncstrwidth() for an
// equivalent for multiple EGCs.
static inline unsigned
nccell_cols(const nccell* c){
return c->width ? c->width : 1;
}
// copy the UTF8-encoded EGC out of the nccell. the result is not tied to any
// ncplane, and persists across erases / destruction.
ALLOC static inline char*
nccell_strdup(const struct ncplane* n, const nccell* c){
return strdup(nccell_extended_gcluster(n, c));
}
// Extract the three elements of a nccell.
static inline char*
nccell_extract(const struct ncplane* n, const nccell* c,
uint16_t* stylemask, uint64_t* channels){
if(stylemask){
*stylemask = c->stylemask;
}
if(channels){
*channels = c->channels;
}
return nccell_strdup(n, c);
}
// Returns true if the two nccells are distinct EGCs, attributes, or channels.
// The actual egcpool index needn't be the same--indeed, the planes needn't even
// be the same. Only the expanded EGC must be equal. The EGC must be bit-equal;
// it would probably be better to test whether they're Unicode-equal FIXME.
// probably needs be fixed up for sprixels FIXME.
static inline bool
nccellcmp(const struct ncplane* n1, const nccell* RESTRICT c1,
const struct ncplane* n2, const nccell* RESTRICT c2){
if(c1->stylemask != c2->stylemask){
return true;
}
if(c1->channels != c2->channels){
return true;
}
return strcmp(nccell_extended_gcluster(n1, c1), nccell_extended_gcluster(n2, c2));
}
// Load a 7-bit char 'ch' into the nccell 'c'. Returns the number of bytes
// used, or -1 on error.
static inline int
nccell_load_char(struct ncplane* n, nccell* c, char ch){
char gcluster[2];
gcluster[0] = ch;
gcluster[1] = '\0';
return nccell_load(n, c, gcluster);
}
// Load a UTF-8 encoded EGC of up to 4 bytes into the nccell 'c'. Returns the
// number of bytes used, or -1 on error.
static inline int
nccell_load_egc32(struct ncplane* n, nccell* c, uint32_t egc){
char gcluster[sizeof(egc) + 1];
egc = htole(egc);
memcpy(gcluster, &egc, sizeof(egc));
gcluster[4] = '\0';
return nccell_load(n, c, gcluster);
}
// Load a UCS-32 codepoint into the nccell 'c'. Returns the number of bytes
// used, or -1 on error.
static inline int
nccell_load_ucs32(struct ncplane* n, nccell* c, uint32_t u){
unsigned char utf8[WCHAR_MAX_UTF8BYTES];
if(notcurses_ucs32_to_utf8(&u, 1, utf8, sizeof(utf8)) < 0){
return -1;
}
uint32_t utf8asegc;
_Static_assert(WCHAR_MAX_UTF8BYTES == sizeof(utf8asegc),
"WCHAR_MAX_UTF8BYTES didn't equal sizeof(uint32_t)");
memcpy(&utf8asegc, utf8, sizeof(utf8));
return nccell_load_egc32(n, c, utf8asegc);
}
// These log levels consciously map cleanly to those of libav; Notcurses itself
// does not use this full granularity. The log level does not affect the opening
// and closing banners, which can be disabled via the notcurses_option struct's
// 'suppress_banner'. Note that if stderr is connected to the same terminal on
// which we're rendering, any kind of logging will disrupt the output (which is
// undesirable). The "default" zero value is NCLOGLEVEL_PANIC.
typedef enum {
NCLOGLEVEL_SILENT = -1,// default. print nothing once fullscreen service begins
NCLOGLEVEL_PANIC = 0, // print diagnostics related to catastrophic failure
NCLOGLEVEL_FATAL = 1, // we're hanging around, but we've had a horrible fault
NCLOGLEVEL_ERROR = 2, // we can't keep doing this, but we can do other things
NCLOGLEVEL_WARNING = 3,// you probably don't want what's happening to happen
NCLOGLEVEL_INFO = 4, // "standard information"
NCLOGLEVEL_VERBOSE = 5,// "detailed information"
NCLOGLEVEL_DEBUG = 6, // this is honestly a bit much
NCLOGLEVEL_TRACE = 7, // there's probably a better way to do what you want
} ncloglevel_e;
// Bits for notcurses_options->flags.
// notcurses_init() will call setlocale() to inspect the current locale. If
// that locale is "C" or "POSIX", it will call setlocale(LC_ALL, "") to set
// the locale according to the LANG environment variable. Ideally, this will
// result in UTF8 being enabled, even if the client app didn't call
// setlocale() itself. Unless you're certain that you're invoking setlocale()
// prior to notcurses_init(), you should not set this bit. Even if you are
// invoking setlocale(), this behavior shouldn't be an issue unless you're
// doing something weird (setting a locale not based on LANG).
#define NCOPTION_INHIBIT_SETLOCALE 0x0001ull
// We typically try to clear any preexisting bitmaps. If we ought *not* try
// to do this, pass NCOPTION_NO_CLEAR_BITMAPS. Note that they might still
// get cleared even if this is set, and they might not get cleared even if
// this is not set. It's a tough world out there.
#define NCOPTION_NO_CLEAR_BITMAPS 0x0002ull
// We typically install a signal handler for SIGWINCH that generates a resize
// event in the notcurses_get() queue. Set to inhibit this handler.
#define NCOPTION_NO_WINCH_SIGHANDLER 0x0004ull