==> src/buffer.c <==1/* Cells and the buffer. */
2#include "tui/buffer.h"
3
4#include "tui/grapheme.h"
5#include "tui/unicode_width.h"
6#include "tui/utf8.h"
7
8#include <stdlib.h>
9#include <string.h>
10
11const tui_cell TUI_CELL_EMPTY = {.none = true};
12
13const char *tui_cell_symbol(const tui_cell *c) { return c->none ? " " : c->sym; }
14
15static size_t fit(const char *s, size_t len, size_t room) {
16 if (len <= room) return len;
17 /* Cut at a character boundary. */
18 while (room > 0 && ((unsigned char)s[room] & 0xC0) == 0x80) room--;
19 return room;
20}
21
22void tui_cell_set_symbol(tui_cell *c, const char *s, size_t len) {
23 len = fit(s, len, TUI_SYM_MAX - 1);
24 memcpy(c->sym, s, len);
25 c->sym[len] = 0;
26 c->none = false;
27}
28
29void tui_cell_set_str(tui_cell *c, const char *s) { tui_cell_set_symbol(c, s, strlen(s)); }
30
31void tui_cell_append_symbol(tui_cell *c, const char *s, size_t len) {
32 if (c->none) {
33 c->sym[0] = 0;
34 c->none = false;
35 }
36 size_t have = strlen(c->sym);
37 len = fit(s, len, TUI_SYM_MAX - 1 - have);
38 memcpy(c->sym + have, s, len);
39 c->sym[have + len] = 0;
40}
41
42void tui_cell_set_style(tui_cell *c, tui_style st) {
43 if (st.has_fg) c->fg = st.fg;
44 if (st.has_bg) c->bg = st.bg;
45 c->mod = (uint16_t)(c->mod | st.add);
46 c->mod = (uint16_t)(c->mod & ~st.sub);
47}
48
49void tui_cell_reset(tui_cell *c) { *c = TUI_CELL_EMPTY; }
50
51tui_style tui_cell_style(const tui_cell *c) {
52 return (tui_style){.fg = c->fg, .bg = c->bg, .has_fg = true, .has_bg = true, .add = c->mod};
53}
54
55uint16_t tui_symbol_width(const char *s, size_t len) {
56 if (len == 1) return 1;
57 size_t w = tui_str_width(s, len);
58 for (size_t i = 0; i < len;) {
59 uint32_t cp = tui_utf8_next(s, len, &i);
60 if (cp == 0xFF9E || cp == 0xFF9F) w++;
61 }
62 return (uint16_t)(w > 0xFFFF ? 0xFFFF : w);
63}
64
65bool tui_symbol_has_control(const char *s, size_t len) {
66 for (size_t i = 0; i < len;) {
67 uint32_t cp = tui_utf8_next(s, len, &i);
68 if (cp < 0x20 || (cp >= 0x7F && cp <= 0x9F)) return true;
69 }
70 return false;
71}
72
73void tui_buffer_init(tui_buffer *b, tui_rect area) {
74 size_t n = tui_rect_area(area);
75 b->area = area;
76 b->cells = malloc((n ? n : 1) * sizeof *b->cells);
77 if (!b->cells) abort();
78 for (size_t i = 0; i < n; i++) b->cells[i] = TUI_CELL_EMPTY;
79}
80
81void tui_buffer_free(tui_buffer *b) {
82 free(b->cells);
83 b->cells = NULL;
84}
85
86tui_cell *tui_buffer_cell(tui_buffer *b, uint16_t x, uint16_t y) {
87 if (!tui_rect_contains(b->area, x, y)) return NULL;
88 return &b->cells[(size_t)(y - b->area.y) * b->area.width + (x - b->area.x)];
89}
90
91const tui_cell *tui_buffer_cell_c(const tui_buffer *b, uint16_t x, uint16_t y) {
92 return tui_buffer_cell((tui_buffer *)b, x, y);
93}
94
95uint16_t tui_buffer_set_stringn(tui_buffer *b, uint16_t x, uint16_t y, const char *s, size_t len, size_t max_width,
96 tui_style st) {
97 uint16_t maxw = max_width > 0xFFFF ? 0xFFFF : (uint16_t)max_width;
98 uint16_t remaining = tui_min16(tui_sat_sub(tui_rect_right(b->area), x), maxw);
99 for (size_t i = 0; i < len;) {
100 size_t end = tui_grapheme_next(s, len, i);
101 const char *g = s + i;
102 size_t glen = end - i;
103 i = end;
104 if (tui_symbol_has_control(g, glen)) continue;
105 uint16_t w = tui_symbol_width(g, glen);
106 if (w == 0) continue;
107 if (w > remaining) break;
108 remaining = (uint16_t)(remaining - w);
109 tui_cell *c = tui_buffer_cell(b, x, y);
110 if (c) {
111 tui_cell_set_symbol(c, g, glen);
112 tui_cell_set_style(c, st);
113 }
114 uint16_t next = (uint16_t)(x + w);
115 x++;
116 while (x < next) {
117 tui_cell *h = tui_buffer_cell(b, x, y);
118 if (h) tui_cell_reset(h);
119 x++;
120 }
121 }
122 return x;
123}
124
125uint16_t tui_buffer_set_string(tui_buffer *b, uint16_t x, uint16_t y, const char *s, tui_style st) {
126 return tui_buffer_set_stringn(b, x, y, s, strlen(s), (size_t)-1, st);
127}
128
129void tui_buffer_set_style(tui_buffer *b, tui_rect area, tui_style st) {
130 tui_rect r = tui_rect_intersection(b->area, area);
131 for (uint16_t y = r.y; y < tui_rect_bottom(r); y++)
132 for (uint16_t x = r.x; x < tui_rect_right(r); x++) tui_cell_set_style(tui_buffer_cell(b, x, y), st);
133}
134
135void tui_buffer_clear(tui_buffer *b, tui_rect area) {
136 tui_rect r = tui_rect_intersection(area, b->area);
137 if (tui_rect_empty(r)) return;
138 for (uint16_t x = r.x; x < tui_rect_right(r); x++)
139 for (uint16_t y = r.y; y < tui_rect_bottom(r); y++) tui_cell_reset(tui_buffer_cell(b, x, y));
140}