C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
ui_ncurses_draw.c
Go to the documentation of this file.
1/** @file ui_ncurses_draw.c
2 @ingroup ui_ncurses
3 @brief NCurses UI backend — drawing functions.
4
5 Implements the drawing operations declared in ui_backend.h using the
6 NCurses wide-character API.
7*/
8
9#define _XOPEN_SOURCE_EXTENDED 1
10
11#include "cm.h"
12#include "ui_backend.h"
14#include <stdlib.h>
15#include <string.h>
16#include <wchar.h>
17
18int mbstr_to_cc(char *in_str, UiCell *cmplx_buf_s);
19void parse_ansi(char *ansi_str, attr_t *attr, uint *cpx);
20
21/* -------------------------------------------------------------------------
22 Surface style
23 ------------------------------------------------------------------------- */
24
25int ui_wclrtobot(UiSurface *s, uint w) {
26 if (!s)
27 return -1;
28 wclrtobot(s->mwin[w]);
29 return 0;
30}
31int ui_wclrtoeol(UiSurface *s, uint w) {
32 if (!s)
33 return -1;
34 wclrtoeol(s->mwin[w]);
35 return 0;
36}
37/* -------------------------------------------------------------------------
38 Text with UiStyle
39 ------------------------------------------------------------------------- */
40int ui_draw_ch_yx(UiSurface *s, uint w, uint y, uint x, const char c) {
41 if (!s)
42 return -1;
43 mvwaddch(s->mwin[w], y, x, c);
44 return 0;
45}
46// text string
47int ui_draw_text(UiSurface *s, uint w, uint y, uint x, const char *text) {
48 if (!s || !text)
49 return -1;
50 mvwaddstr(s->mwin[w], y, x, text);
51 return 0;
52}
53// text - limit length
54int ui_draw_text_n(UiSurface *s, uint w, uint y, uint x, const char *text, int n) {
55 if (!s || !text)
56 return -1;
57 mvwaddnstr(s->mwin[w], y, x, text, (int)n);
58 return 0;
59}
60// text string - pad length
61int ui_draw_text_fill(UiSurface *s, uint w, uint y, uint x, const char *text, int n) {
62 if (!s || !text)
63 return -1;
64 uint l = strlen(text);
65 if (l < (uint)n) {
66 char *tmp_str = (char *)malloc(n + 1);
67 if (!tmp_str)
68 return -1;
69 strcpy(tmp_str, text);
70 for (int i = l; i < (int)n; i++) {
71 tmp_str[i] = ' ';
72 }
73 tmp_str[n] = '\0';
74 mvwaddstr(s->mwin[w], y, x, tmp_str);
75 free(tmp_str);
76 } else {
77 mvwaddnstr(s->mwin[w], y, x, text, (int)n);
78 }
79 return 0;
80}
81// -------------------------------------------------------------------------
82// Wide Character Strings (wstr)
83// ---------------------------------------------------------------------------
84int ui_waddwstr(UiSurface *s, uint w, const wchar_t *wstr) {
85 if (!s || !wstr)
86 return -1;
87 waddwstr(s->mwin[w], wstr);
88 return 0;
89}
90int ui_mvwaddwstr(UiSurface *s, uint w, uint y, uint x, const wchar_t *wstr) {
91 if (!s || !wstr)
92 return -1;
93 mvwaddwstr(s->mwin[w], y, x, wstr);
94 return 0;
95}
96int ui_waddnwstr(UiSurface *s, uint w, const wchar_t *wstr, int m) {
97 if (!s || !wstr)
98 return -1;
99 waddnwstr(s->mwin[w], wstr, m);
100 return 0;
101}
102int ui_mvwaddnwstr(UiSurface *s, uint w, uint y, uint x, const wchar_t *wstr, int m) {
103 if (!s || !wstr)
104 return -1;
105 mvwaddnwstr(s->mwin[w], y, x, wstr, m);
106 return 0;
107}
108// -------------------------------------------------------------------------
109// Text - Sanstyle - These should probably be implemented as macros
110// -------------------------------------------------------------------------
111int ui_waddstr(UiSurface *s, uint w, const char *text) {
112 if (!s || !text)
113 return -1;
114 waddstr(s->mwin[w], text);
115 return 0;
116}
117// text string
118int ui_mvaddstr(UiSurface *s, uint w, uint y, uint x, const char *text) {
119 if (!s || !text)
120 return -1;
121 mvwaddstr(s->mwin[w], y, x, text);
122 return 0;
123}
124// text character single
125int ui_mvwaddch(UiSurface *s, uint w, uint y, uint x, const char c) {
126 if (!s || !c)
127 return -1;
128 mvwaddch(s->mwin[w], y, x, c);
129 return 0;
130}
131// text string
132int ui_mvwaddstr(UiSurface *s, uint w, uint y, uint x, const char *text) {
133 if (!s || !text)
134 return -1;
135 mvwaddstr(s->mwin[w], y, x, text);
136 return 0;
137}
138int ui_mvwaddnstr(UiSurface *s, uint w, uint y, uint x, const char *text, int m) {
139 if (!s || !text)
140 return -1;
141 mvwaddnstr(s->mwin[w], y, x, text, m);
142 return 0;
143}
144// text string - pad length
145int ui_mvwaddstr_fill(UiSurface *s, uint w, uint y, uint x, const char *str, int m) {
146 char *d, *e;
147 uint maxy, maxx;
148 char tmp_str[MAXLEN];
149 getmaxyx(s->mwin[w], maxy, maxx);
150 y = min(y, maxy);
151 m = min(m, maxx);
152 m = min(m, (uint)MAXLEN - 1);
153 e = d = tmp_str;
154 e += m;
155 while (d < e) {
156 if (*str == '\0' || *str == '\n')
157 *d++ = ' ';
158 else
159 *d++ = *str++;
160 }
161 *d = '\0';
162 m = strlen(tmp_str);
163 mvwaddnstr(s->mwin[w], y, x, tmp_str, m);
164 return 0;
165}
166// ---------------------------------------------------------------------------
167// Complex Characters (cc)
168// ---------------------------------------------------------------------------
169int ui_wadd_wch(UiSurface *s, uint w, const UiCell *cell) {
170 if (!s)
171 return -1;
172 wadd_wchnstr(s->mwin[w], cell, 1);
173 return 0;
174}
175int ui_mvwadd_wch(UiSurface *s, uint w, uint y, uint x, const UiCell *cell) {
176 if (!s)
177 return -1;
178 mvwadd_wch(s->mwin[w], y, x, cell);
179 return 0;
180}
181int ui_wadd_wchstr(UiSurface *s, uint w, const UiCell *cmplx_buf) {
182 if (!s || !cmplx_buf)
183 return -1;
184 wadd_wchstr(s->mwin[w], cmplx_buf);
185 return 0;
186}
187// cc string
188int ui_mvwadd_wchstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cmplx_buf) {
189 if (!s || !cmplx_buf)
190 return -1;
191 mvwadd_wchstr(s->mwin[w], y, x, cmplx_buf);
192 return 0;
193}
194// cc string - limit length
195int ui_wadd_wchnstr(UiSurface *s, uint w, const UiCell *cmplx_buf, uint n) {
196 if (!s || !cmplx_buf)
197 return -1;
198 wadd_wchnstr(s->mwin[w], cmplx_buf, n);
199 return 0;
200}
201int ui_mvwadd_wchnstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cmplx_buf, uint n) {
202 if (!s || !cmplx_buf)
203 return -1;
204 mvwadd_wchnstr(s->mwin[w], y, x, cmplx_buf, n);
205 return 0;
206}
207/* -------------------------------------------------------------------------
208 Screen management
209 ------------------------------------------------------------------------- */
211 touchwin(stdscr);
212 for (int s = 0; s <= sfc_ptr; s++) {
213 for (uint w = 0; w < SUB_SFC_MAX; w++)
214 if (ui_surface[s]->mwin[w] != nullptr)
215 touchwin(ui_surface[s]->mwin[w]);
216 }
218}
219/* -------------------------------------------------------------------------
220 Formatting
221 ------------------------------------------------------------------------- */
222
223/** @brief Convert a multibyte string to a complex character array. */
224int mbstr_to_cc(char *in_str, UiCell *cmplx_buf_s) {
225 char ansi_tok[MAXLEN];
226 uint i = 0, j = 0, x = 0;
227 uint len = 0;
228 uint tab_stop = 4;
229 uint tab_spaces = 0;
230 uint char_width;
231 attr_t attr = WA_NORMAL;
232 uint cpx = cp_nt;
233 UiCell cc = {0};
234 wchar_t wstr[2] = {L'\0', L'\0'};
235 UiCell *cmplx_buf = cmplx_buf_s;
236 mbstate_t mbstate;
237 memset(&mbstate, 0, sizeof(mbstate));
238 while (in_str[i] != '\0') { // line
239 while (1) { // ANSI SGR, Character, and Word
240 if (in_str[i] == '\033') { // ANSI SGR
241 if (in_str[i + 1] == '[') {
242 len = strcspn(&in_str[i], "mK ") + 1;
243 memcpy(ansi_tok, &in_str[i], len + 1);
244 ansi_tok[len] = '\0';
245 if (ansi_tok[0] == '\0') {
246 if (i + 2 < MAXLEN)
247 i += 2;
248 continue;
249 }
250 if (len == 0 || in_str[i + len - 1] == ' ') {
251 i += 2;
252 continue;
253 } else if (in_str[i + len - 1] == 'K') {
254 i += len;
255 continue;
256 }
257 parse_ansi(ansi_tok, &attr, &cpx);
258 i += len;
259 } else {
260 i++;
261 continue;
262 }
263 } else { // Characters
264 if (in_str[i] == ' ') {
265 wstr[0] = L' ';
266 wstr[1] = L'\0';
267 setcchar(&cc, wstr, attr, cpx, nullptr);
268 cmplx_buf[j++] = cc;
269 x++;
270 i++;
271 continue;
272 }
273 if (in_str[i] == '\0') {
274 break;
275 }
276 if (in_str[i] == '\t') {
277 tab_spaces = tab_stop - j % tab_stop;
278 wstr[0] = L' ';
279 wstr[1] = L'\0';
280 setcchar(&cc, wstr, attr, cpx, nullptr);
281 for (uint z = 0; z < tab_spaces; z++) {
282 cmplx_buf[j++] = cc;
283 x++;
284 }
285 i++;
286 continue;
287 }
288 wstr[1] = L'\0';
289 len = mbrtowc(wstr, &in_str[i], MB_CUR_MAX, &mbstate);
290 if (len <= 0) {
291 wstr[0] = L'?';
292 wstr[1] = L'\0';
293 len = 1;
294 }
295 char_width = wcwidth(wstr[0]);
296 x += char_width;
297 setcchar(&cc, wstr, attr, cpx, nullptr);
298 cmplx_buf[j++] = cc;
299 i += len;
300 continue;
301 } // END Character and Word
302 } // END WHILE - ANSI SGR, Character, and Word
303 } // END WHILE - line
304 wstr[0] = '\0';
305 wstr[1] = '\0';
306 setcchar(&cc, wstr, WA_NORMAL, cpx, nullptr);
307 cmplx_buf[j] = cc;
308 return x;
309}
310/** @brief Parse an ANSI SGR sequence and update attributes and color pair index. */
311void parse_ansi(char *ansi_str, attr_t *attr, uint *cpx) {
312 char *tok;
313 char t0, t1;
314 char tstr[3];
315 uint len, x_idx;
316 uint fg, bg;
317 int _fg_clr, _bg_clr;
318 uint fg_clr, bg_clr;
319 char *ansi_p = ansi_str + 2;
320 extended_pair_content(*cpx, &_fg_clr, &_bg_clr);
321 fg = fg_clr = (uint)_fg_clr;
322 bg = fg_clr = (uint)_bg_clr;
323 RGB rgb;
324 tok = strtok((char *)ansi_p, ";m");
325 bool a_toi_error = false;
326 while (1) {
327 if (tok == nullptr || *tok == '\0')
328 break;
329 len = strlen(tok);
330 if (len == 2) {
331 t0 = tok[0];
332 t1 = tok[1];
333 if (t0 == '3' || t0 == '4') {
334 if (t1 == '8') {
335 tok = strtok(nullptr, ";m");
336 if (tok != nullptr) {
337 if (*tok == '5') {
338 tok = strtok(nullptr, ";m");
339 if (tok != nullptr) {
340 x_idx = a_toi(tok, &a_toi_error);
341 rgb = xterm256_idx_to_rgb(x_idx);
342 }
343 } else if (*tok == '2') {
344 tok = strtok(nullptr, ";m");
345 rgb.r = a_toi(tok, &a_toi_error);
346 tok = strtok(nullptr, ";m");
347 rgb.g = a_toi(tok, &a_toi_error);
348 tok = strtok(nullptr, ";m");
349 rgb.b = a_toi(tok, &a_toi_error);
350 }
351 }
352 if (t0 == '3')
353 fg_clr = ui_add_color_rgb(&rgb);
354 else if (t0 == '4')
355 bg_clr = ui_add_color_rgb(&rgb);
356 } else if (t1 == '9') {
357 if (t0 == '3')
358 fg_clr = CLR_NT_FG;
359 else if (t0 == '4')
360 bg_clr = CLR_NT_BG;
361 } else if (t1 >= '0' && t1 <= '7') {
362 if (t0 == '3') {
363 tstr[0] = t1;
364 tstr[1] = '\0';
365 x_idx = a_toi(tstr, &a_toi_error);
366 rgb = xterm256_idx_to_rgb(x_idx);
367 fg_clr = ui_add_color_rgb(&rgb);
368 } else if (t0 == '4') {
369 tstr[0] = t1;
370 tstr[1] = '\0';
371 x_idx = a_toi(tstr, &a_toi_error);
372 rgb = xterm256_idx_to_rgb(x_idx);
373 bg_clr = ui_add_color_rgb(&rgb);
374 }
375 }
376 } else if (t0 == '0') {
377 *tok = t1;
378 len = 1;
379 }
380 }
381 if (len == 1) {
382 if (*tok == '0') {
383 *attr = WA_NORMAL;
384 fg_clr = CLR_NT_FG;
385 bg_clr = CLR_NT_BG;
386 } else {
387 switch (a_toi(tok, &a_toi_error)) {
388 case 1:
389 *attr |= WA_BOLD;
390 break;
391 case 2:
392 *attr |= WA_DIM;
393 break;
394 case 3:
395 *attr |= WA_ITALIC;
396 break;
397 case 4:
398 *attr |= WA_UNDERLINE;
399 break;
400 case 5:
401 *attr |= WA_BLINK;
402 break;
403 case 7:
404 *attr |= WA_REVERSE;
405 break;
406 case 8:
407 *attr |= WA_INVIS;
408 break;
409 default:
410 break;
411 }
412 }
413 } else if (len == 0) {
414 *attr = WA_NORMAL;
415 fg_clr = CLR_NT_FG;
416 bg_clr = CLR_NT_BG;
417 }
418 tok = strtok(nullptr, ";m");
419 }
420 if (!a_toi_error && (fg_clr != fg || bg_clr != bg))
421 *cpx = ui_add_pair(fg_clr, bg_clr);
422 return;
423}
@ CLR_NT_FG
Definition cm.h:198
@ CLR_NT_BG
Definition cm.h:199
#define min(x, y)
min macro evaluates two expressions, returning least result
Definition cm.h:91
void ui_render()
Definition ui_ncurses.c:800
int ui_wadd_wch(UiSurface *s, uint w, const UiCell *cell)
int ui_draw_ch_yx(UiSurface *s, uint w, uint y, uint x, const char c)
int ui_wclrtobot(UiSurface *s, uint w)
int ui_mvwadd_wchstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cell)
int ui_mvwaddnstr(UiSurface *s, uint w, uint y, uint x, const char *text, int m)
int ui_mvwaddstr(UiSurface *s, uint w, uint y, uint x, const char *text)
int ui_wadd_wchnstr(UiSurface *s, uint w, const UiCell *cell, uint m)
int ui_add_color_rgb(RGB *rgb)
Definition ui_ncurses.c:138
int ui_mvwaddnwstr(UiSurface *s, uint w, uint y, uint x, const wchar_t *wstr, int m)
int ui_draw_text_n(UiSurface *s, uint w, uint y, uint x, const char *text, int m)
int ui_mvwadd_wchnstr(UiSurface *s, uint w, uint y, uint x, const UiCell *cell, uint m)
int ui_mvwaddch(UiSurface *s, uint w, uint y, uint x, const char c)
uint16_t attr_t
Definition ui_backend.h:256
int ui_draw_text(UiSurface *s, uint w, uint y, uint x, const char *text)
int ui_waddnwstr(UiSurface *s, uint w, const wchar_t *wstr, int m)
void ui_restore_wins()
int ui_mvaddstr(UiSurface *s, uint w, uint y, uint x, const char *text)
UiSurface * ui_surface[UI_SFC_MAX]
Definition ui_ncurses.c:28
int ui_draw_text_fill(UiSurface *s, uint w, uint y, uint x, const char *text, int m)
int ui_waddstr(UiSurface *s, uint w, const char *text)
int ui_wclrtoeol(UiSurface *s, uint w)
int sfc_ptr
Definition nckeys.c:47
struct nccell UiCell
Definition ui_backend.h:250
int ui_wadd_wchstr(UiSurface *s, uint w, const UiCell *cell)
int ui_mvwadd_wch(UiSurface *s, uint w, uint y, uint x, const UiCell *cell)
int ui_mvwaddwstr(UiSurface *s, uint w, uint y, uint x, const wchar_t *wstr)
int ui_mvwaddstr_fill(UiSurface *s, uint w, uint y, uint x, const char *str, int m)
int ui_waddwstr(UiSurface *s, uint w, const wchar_t *wstr)
#define WA_NORMAL
#define WA_BLINK
#define WA_ITALIC
#define WA_DIM
#define WA_INVIS
#define WA_BOLD
#define WA_REVERSE
#define WA_UNDERLINE
uint ui_add_pair(uint fg, uint bg)
Definition ui_ncurses.c:105
int mbstr_to_cc(char *in_str, UiCell *cmplx_buf_s)
Convert a multibyte string to a complex character array.
void parse_ansi(char *ansi_str, attr_t *attr, uint *cpx)
Parse an ANSI SGR sequence and update attributes and color pair index.
#define MAXLEN
Definition curskeys.c:15
ushort cp_nt
Definition dwin.c:151
RGB xterm256_idx_to_rgb(uint idx)
Convert XTerm 256 color index to RGB.
Definition dwin.c:274
int a_toi(char *, bool *)
a safer alternative to atoi() for converting ASCII strings to integers.
Definition futil.c:762