C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
dwin.c
Go to the documentation of this file.
1/** @file dwin.c
2 @brief Window support for C-Menu - EXPERIMENTAL
3 @note This file contains functions for managing NCurses windows and color
4 settings for the Chyron structure for function key labels and mouse click
5 handling. This file is a work in progress and may be subject to change as the
6 C-Menu project evolves. Generally, don't try to use it yet unless you want
7 complete the half-done code modifications.
8 @author Bill Waller
9 Copyright (c) 2025
10 MIT License
11 billxwaller@gmail.com
12 @date 2026-02-09
13 */
14
15/** @defgroup window_support Window Support
16 @brief Manage NCurses windows and color settings
17 */
18
19#include <cm.h>
20#include <errno.h>
21#include <math.h>
22#include <stdbool.h>
23#include <stdlib.h>
24#include <string.h>
25#include <termios.h>
26#include <unistd.h>
27#include <wait.h>
28#include <wchar.h>
29#define NC true
30
31bool open_curses(SIO *);
32void destroy_curses();
33int win_new(int, int, int, int, char *, int);
34void win_redraw(WINDOW *);
35void win_resize(int, int, char *);
36WINDOW *win_del();
37void restore_wins();
38void cbox(WINDOW *);
39void win_init_attrs();
40int Perror(char *);
41void mvwaddstr_fill(WINDOW *, int, int, char *, int);
42void abend(int, char *);
43int nf_error(int, char *);
46void list_colors();
47int clr_name_to_idx(char *);
48void init_hex_clr(int, char *);
50RGB hex_clr_str_to_rgb(char *);
51RGB xterm256_idx_to_rgb(int);
52int rgb_to_curses_clr(RGB *);
53int rgb_to_xterm256_idx(RGB *);
54void apply_gamma(RGB *);
55
56Chyron *new_chyron();
57void set_chyron_key(Chyron *, int, char *, int);
58void set_chyron_key_cp(Chyron *, int, char *, int, int);
59bool is_set_chyron_key(Chyron *, int);
60void unset_chyron_key(Chyron *, int);
61void compile_chyron(Chyron *);
62int get_chyron_key(Chyron *, int);
63Chyron *destroy_chyron(Chyron *chyron);
64int mb_to_cc(cchar_t *, char *, attr_t, int, int *, int);
65
66Chyron *wait_mk_chyron();
67WINDOW *wait_mk_win(Chyron *, char *);
68int wait_continue(WINDOW *, Chyron *, int);
69bool wait_destroy(Chyron *);
70
71int xwgetch(WINDOW *, Chyron *, int);
72
73bool init_clr_palette(SIO *);
74cchar_t mkccc(int);
75SCREEN *screen;
76
77SIO *sio; /**< Global pointer to SIO struct for terminal and color settings */
78
79/** StdColors
80 @note Standard 16 colors for xterm256 color conversions
81 @note These colors can be overridden in ".minitrc" */
82RGB StdColors[16] = {
83 {0, 0, 0}, {128, 0, 0}, {0, 128, 0}, {128, 128, 0},
84 {0, 0, 128}, {128, 0, 128}, {0, 128, 128}, {192, 192, 192},
85 {128, 128, 128}, {255, 0, 0}, {0, 255, 0}, {255, 255, 0},
86 {0, 0, 255}, {255, 0, 255}, {0, 255, 255}, {255, 255, 255}};
87/** colors_text
88 @note Color names for .minitrc overrides
89 @note These names are used in .minitrc to specify color overrides
90 @note The order of these names corresponds to the colors_enum values */
91char const colors_text[][10] = {
92 "black", "red", "green", "yellow", "blue", "magenta", "cyan",
93 "white", "orange", "bg", "abg", "bblack", "bred", "bgreen",
94 "byellow", "bblue", "bcyan", "bmagenta", "bwhite", "borange", ""};
95const wchar_t bw_ho = BW_HO; /**< horizontal line */
96const wchar_t bw_ve = BW_VE; /**< vertical line */
97const wchar_t bw_tl = BW_RTL; /**< top left corner */
98const wchar_t bw_tr = BW_RTR; /**< top right corner */
99const wchar_t bw_bl = BW_RBL; /**< bottom left corner */
100const wchar_t bw_br = BW_RBR; /**< bottom right corner */
101const wchar_t bw_lt = BW_LT; /**< left tee */
102const wchar_t bw_rt = BW_RT; /**< right tee */
103const wchar_t bw_sp = BW_SP; /**< tee space */
105 1.2; /**< Gamma correction value for gray colors. Set in .minitrc */
106double RED_GAMMA =
107 1.2; /**< Gamma correction value for red colors. Set in .minitrc */
109 1.2; /**< Gamma correction value for green colors. Set in .minitrc */
111 1.2; /**< Gamma correction value for blue colors. Set in .minitrc */
112
113WINDOW *win;
117unsigned int cmd_key;
118bool f_sigwench = false;
124int m_begy = -1;
125int m_begx = -1;
131char fn[MAXLEN];
142int clr_cnt = 0;
145cchar_t CCC_NORM;
146cchar_t CCC_WIN;
147cchar_t CCC_BG;
148cchar_t CCC_BOX;
151cchar_t CCC_LN;
152/** Global file/pipe numbers */
155
156/** @brief Initialize window attributes
157 @ingroup window_support
158 @note This function initializes color pairs for the window
159 @note cp_norm, cp_win, and cp_box are global variables
160 @see get_clr_pair
161 */
162void win_init_attrs() { return; }
163
164/** @defgroup Chyron Chyron Management
165 @brief Create and manage the Chyron
166 */
167
168/** @brief Create and initialize Chyron structure
169 @ingroup Chyron
170 @return pointer to new Chyron structure
171 @details This function allocates memory for a new Chyron structure and
172 initializes the key pointers. Each key pointer is allocated memory for a
173 ChyronKey structure. The Chyron structure is used to manage function key
174 labels and their associated keycodes for mouse click handling in the chyron
175 area of the interface.
176 @note The use of calloc ensures that the allocated memory is initialized to
177 zero, which means that the text for each key will be initialized to an empty
178 string and the keycodes will be initialized to zero. This allows the
179 is_set_chyron_key function to check if a key is set by checking if the first
180 character of the text is not '\0'. If any memory allocation fails, the
181 function will call abend to handle the error and return nullptr.
182 */
183Chyron *new_chyron() {
184 Chyron *chyron = (Chyron *)calloc(1, sizeof(Chyron));
185 if (!chyron) {
186 abend(-1, "calloc chyron failed");
187 return nullptr;
188 }
189 for (int i = 0; i < CHYRON_KEYS; i++)
190 chyron->key[i] = (ChyronKey *)calloc(1, sizeof(ChyronKey));
191 return chyron;
192}
193/** @brief Destroy Chyron structure
194 @ingroup Chyron
195 @param chyron pointer to Chyron structure
196 @return nullptr
197 */
198Chyron *destroy_chyron(Chyron *chyron) {
199 int i;
200
201 if (!chyron)
202 return nullptr;
203 for (i = 0; i < CHYRON_KEYS; i++) {
204 if (chyron->key[i])
205 free(chyron->key[i]);
206 chyron->key[i] = nullptr;
207 }
208 free(chyron);
209 chyron = nullptr;
210 return chyron;
211}
212/** @brief Check if function key label is set
213 @ingroup Chyron
214 @param chyron structure
215 @param k Function key index (0-19)
216 @return true if set, false if not set */
217bool is_set_chyron_key(Chyron *chyron, int k) {
218 if (chyron->key[k]->text[0] != '\0')
219 return true;
220 else
221 return false;
222}
223/** @brief Set chyron key
224 @ingroup Chyron
225 @param chyron structure
226 @param k chyron key index (0-19)
227 @param s chyron key label
228 @param kc chyron key code
229 @param cp color pair index for the key label
230 @details This function sets the label and keycode for a chyron key. The
231 label is copied into the chyron structure, and the keycode is stored for
232 later retrieval when theire is a mouse click. The compile_chyron function
233 uses the keycode values to determine which key was clicked based on the mouse
234 X position. If the label string is empty, the key is unset by setting the
235 first character of the text to '\0'.
236*/
237void set_chyron_key_cp(Chyron *chyron, int k, char *s, int kc, int cp) {
238 if (*s != '\0')
240 else
241 chyron->key[k]->text[0] = '\0';
242 chyron->key[k]->keycode = kc;
243 chyron->key[k]->cp = cp;
244}
245void set_chyron_key(Chyron *chyron, int k, char *s, int kc) {
246 if (*s != '\0')
248 else
249 chyron->key[k]->text[0] = '\0';
250 chyron->key[k]->keycode = kc;
251 chyron->key[k]->cp = cp_reverse;
252}
253/** @brief Unset chyron key
254 @ingroup Chyron
255 @param chyron structure
256 @param k chyron_key index
257*/
258void unset_chyron_key(Chyron *chyron, int k) { chyron->key[k]->text[0] = '\0'; }
259/** @brief construct the chyron string from the chyron structure
260 @ingroup Chyron
261 @param chyron
262 @details The chyron string is constructed by concatenating the labels of the
263 set keys, separated by " | ". The end_pos values for each key are set to
264 determine the zones for mouse clicks. When a mouse click occurs, the
265 get_chyron_key function uses the end_pos values to determine which key was
266 clicked based on the X position of the click.
267*/
268void compile_chyron(Chyron *chyron) {
269 int end_pos = 0;
270 int k = 0;
271 int pos = 0;
272 int cp = cp_reverse;
273 cchar_t *cx;
274 while (k < CHYRON_KEYS) {
275 if (chyron->key[k]->text[0] == '\0') {
276 k++;
277 continue;
278 }
279 if (end_pos == 0) {
280 cx = chyron->cmplx_buf;
281 mb_to_cc(cx, " ", WA_NORMAL, cp_reverse, &pos, MAXLEN - 1);
282 } else {
283 mb_to_cc(chyron->cmplx_buf, "|", WA_NORMAL, cp_reverse, &pos,
284 MAXLEN - 1);
285 }
286 cx = chyron->cmplx_buf;
287 cp = chyron->key[k]->cp;
288 mb_to_cc(cx, chyron->key[k]->text, WA_NORMAL, cp, &pos, MAXLEN - 1);
289 end_pos = pos;
290 chyron->l = end_pos;
291 chyron->key[k]->end_pos = end_pos;
292 k++;
293 }
294 mb_to_cc(chyron->cmplx_buf, " ", WA_NORMAL, cp, &pos, MAXLEN - 1);
295 chyron->l = end_pos;
296}
297void display_chyron(WINDOW *win, Chyron *chyron, int line, int col) {
298 wmove(win, line, 0);
299 wclrtoeol(win);
300 wmove(win, line, 0);
301 wadd_wchstr(win, chyron->cmplx_buf);
302 wmove(win, line, col);
303 return;
304}
305/** @brief Convert multibyte string to complex character array
306 @ingroup Chyron
307 @param cmplx_buf Output buffer for complex characters
308 @param str Input multibyte string
309 @param attr Attributes to apply to the complex characters
310 @param cpx Color pair index for the complex characters
311 @param pos Pointer to current position in the output buffer, updated as
312 characters are added
313 @param maxlen Maximum length of the output buffer
314 @return Number of bytes processed from the input string
315 @details This function converts a multibyte string to an array of complex
316 characters (cchar_t) that can be used with NCurses functions. It handles
317 multibyte characters and applies the specified color pair to each character.
318 The pos parameter is updated to reflect the current position in the output
319 buffer, and the function ensures that it does not exceed the maximum length.
320*/
321int mb_to_cc(cchar_t *cmplx_buf, char *str, attr_t attr, int cpx, int *pos,
322 int maxlen) {
323 int i = 0, len = 0;
324 const char *s;
325 cchar_t cc = {0};
326 wchar_t wstr[2] = {L'\0', L'\0'};
327 mbstate_t mbstate;
328 memset(&mbstate, 0, sizeof(mbstate));
329 attr = WA_NORMAL;
330 if (*pos >= maxlen - 1)
331 return 0;
332 while (str[i] != '\0') {
333 s = &str[i];
334 len = mbrtowc(wstr, s, MB_CUR_MAX, &mbstate);
335 if (len <= 0) {
336 wstr[0] = L'?';
337 wstr[1] = L'\0';
338 len = 1;
339 }
340 wstr[1] = L'\0';
341 if (*pos >= maxlen - 1)
342 break;
343 if (setcchar(&cc, wstr, attr, cpx, nullptr) != ERR) {
344 if (len > 0 && (*pos + len) < MAXLEN - 1)
345 cmplx_buf[(*pos)++] = cc;
346 }
347 i += len;
348 }
349 wstr[0] = L'\0';
350 wstr[1] = L'\0';
351 setcchar(&cc, wstr, attr, cpx, nullptr);
352 cmplx_buf[*pos] = cc;
353 return *pos;
354}
355/** @brief Get keycode from chyron
356 @ingroup Chyron
357 @param chyron structure
358 @param x Mouse X position
359 @return Keycode
360 @note This function uses the end_pos values set in compile_chyron
361 to determine which key was clicked
362 @note The chyron functions provide xwgetch() with a mechanism to translate
363 mouse click positions into key codes based on the labels set in the chyron
364 structure. When a mouse click occurs, xwgetch() can call get_chyron_key()
365 with the X position of the click to determine which function key was clicked,
366 allowing for dynamic and customizable function key behavior in the chyron
367 area of the interface.
368*/
369int get_chyron_key(Chyron *chyron, int x) {
370 int i;
371 for (i = 0; chyron->key[i]->end_pos != -1; i++)
372 if (x < chyron->key[i]->end_pos)
373 break;
374 return chyron->key[i]->keycode;
375}
376/** @brief Create complex character buffer from multibyte string
377 @ingroup Chyron
378 @param s Input multibyte string
379 @return Pointer to complex character buffer
380 @details This function creates a complex character buffer from a multibyte
381 string. It allocates memory for the buffer and converts the multibyte string
382 to complex characters using the mb_to_cc function. The color pair used for
383 the complex characters is cp_norm. The caller is responsible for freeing the
384 allocated buffer when it is no longer needed.
385*/
386cchar_t *mk_cmplx_buf(const char *s) {
387 cchar_t *cmplx_buf = (cchar_t *)calloc(MAXLEN, sizeof(cchar_t));
388 int j = 0;
389 int len;
390 attr_t attr = WA_NORMAL;
391 int cpx = cp_win;
392 wchar_t wc = L'\0';
393 cchar_t cc = {0};
394 mbstate_t mbstate;
395 memset(&mbstate, 0, sizeof(mbstate));
396 len = mbrtowc(&wc, s, MB_CUR_MAX, &mbstate);
397 if (len <= 0) {
398 wc = L'?';
399 len = 1;
400 }
401 if (setcchar(&cc, &wc, attr, cpx, nullptr) != ERR) {
402 if (len > 0 && (j + len) < MAXLEN - 1) {
403 cmplx_buf[j++] = cc;
404 }
405 }
406 return cmplx_buf;
407}
408
409/** @brief Initialize NCurses and color settings
410 @ingroup window_support
411 @param sio Pointer to SIO struct with terminal and color settings
412 @return true if successful, false if error
413 note This function initializes NCurses and sets up color pairs based on the
414 settings in the SIO struct. It also applies gamma correction to colors.
415 Use this function to initialize NCurses if you don't want NCurses to receive
416 data from the stdin pipe
417 @code
418 1. saves stdin and stdout file descriptors in SIO
419 2. opens a terminal device for NCurses screen IO
420 3. replaces STDERR_FILENO with terminal file descriptor
421 @endcode */
422
423bool open_curses(SIO *sio) {
424 char tmp_str[MAXLEN];
425 char emsg0[MAXLEN];
426
427 if (ttyname_r(STDERR_FILENO, sio->tty_name, sizeof(sio->tty_name)) != 0) {
428 strerror_r(errno, tmp_str, MAXLEN - 1);
429 strnz__cpy(emsg0, "ttyname_r failed ", MAXLEN - 1);
430 strnz__cat(emsg0, tmp_str, MAXLEN - 1);
431 fprintf(stderr, "%s\n", tmp_str);
432 exit(0);
433 }
434 /** open the terminal device for reading and writing */
435 ncurses_fp = fopen(sio->tty_name, "r+");
436 if (ncurses_fp == nullptr) {
437 strerror_r(errno, tmp_str, MAXLEN - 1);
438 strnz__cpy(emsg0, "fopen(sio->tty_name) failed ", MAXLEN - 1);
439 strnz__cat(emsg0, tmp_str, MAXLEN - 1);
440 fprintf(stderr, "%s\n", tmp_str);
441 exit(0);
442 }
443 /** We use SCREEN and newterm because this allows us to */
444 /** specify the terminal FILE */
445 screen = newterm(nullptr, ncurses_fp, ncurses_fp);
446 if (screen == nullptr) {
447 strerror_r(errno, tmp_str, MAXLEN - 1);
448 strnz__cpy(emsg0, "newterm failed ", MAXLEN - 1);
449 strnz__cat(emsg0, tmp_str, MAXLEN - 1);
450 fprintf(stderr, "%s\n", tmp_str);
451 exit(0);
452 }
453 set_term(screen);
454 f_curses_open = true;
455 if (!has_colors()) {
457 abend(-1, "Terminal color support required");
458 }
459 start_color();
460 if (!can_change_color()) {
462 fprintf(stderr, "Terminal cannot change colors\n");
463 fprintf(stderr, "Check TERM environment variable\n");
464 fprintf(stderr, "Check terminfo for missing \"ccc\"\n");
465 abend(-1, "fatal error");
466 }
468 /** Set gamma correction values */
469 /** These are read from ~/.minitrc */
470 /** We need these values when initializing colors */
475
482
489 noecho();
490 keypad(stdscr, true);
491 idlok(stdscr, false);
492 idcok(stdscr, false);
493 wbkgrnd(stdscr, &CCC_NORM);
494 wbkgrndset(stdscr, &CCC_NORM);
495#ifdef NCDEBUG
496 immedok(stdscr, true);
497#endif
498 win_ptr = -1;
499 return sio;
500}
501/** @defgroup color_management Color Management
502 @brief Conversion of Color Data Types and Management of Colors and Color
503 Pairs
504 */
505/** @brief Get color pair index for foreground and background colors
506 @ingroup color_management
507 @param fg Foreground color index
508 @param bg Background color index
509 @return Color pair index */
510int get_clr_pair(int fg, int bg) {
511 int rc, i, pfg, pbg;
512 for (i = 1; i < clr_pair_cnt; i++) {
513 extended_pair_content(i, &pfg, &pbg);
514 if (pfg == fg && pbg == bg)
515 return i;
516 }
517 if (i >= COLOR_PAIRS) {
518 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
519 ssnprintf(em1, MAXLEN - 1, "NCurses COLOR_PAIRS (%d) exceeded (%d)",
520 COLOR_PAIRS, i);
521 strerror_r(errno, em2, MAXLEN);
523 return (EXIT_FAILURE);
524 }
525 if (i < COLOR_PAIRS) {
526 rc = init_extended_pair(i, fg, bg);
527 if (rc == ERR)
528 return ERR;
529 }
530 if (i < COLOR_PAIRS)
531 clr_pair_cnt++;
532 return i;
533}
534/** @brief Get color index for RGB color
535 @ingroup color_management
536 @param rgb RGB color
537 @return NCurses color index
538 @note Curses uses 0-1000 for RGB values
539 @note If the color does not exist, it is created along with a new color
540 index */
541int rgb_to_curses_clr(RGB *rgb) {
542 int i;
543 int r, g, b;
544 apply_gamma(rgb);
545 rgb->r = (rgb->r * 1000) / 255;
546 rgb->g = (rgb->g * 1000) / 255;
547 rgb->b = (rgb->b * 1000) / 255;
548 for (i = 0; i < clr_cnt; i++) {
549 extended_color_content(i, &r, &g, &b);
550 if (rgb->r == r && rgb->g == g && rgb->b == b) {
551 return i;
552 }
553 }
554 if (i < COLORS) {
555 init_extended_color(i, rgb->r, rgb->g, rgb->b);
556 clr_cnt++;
557 return clr_cnt - 1;
558 }
559 return ERR;
560}
561/** @brief Convert RGB color to XTerm 256 color index
562 @ingroup color_management
563 @param rgb RGB color
564 @return XTerm 256 color index
565 note This function converts an RGB color to the nearest XTerm 256 color
566 index. It first checks if the color is a shade of gray, and if so, it uses
567 the gray ramp. Otherwise, it calculates the nearest color in the 6x6x6 color
568 cube. */
569int rgb_to_xterm256_idx(RGB *rgb) {
570 if (rgb->r == rgb->g && rgb->g == rgb->b) {
571 if (rgb->r < 8)
572 return 16;
573 if (rgb->r > 248)
574 return 231;
575 return ((rgb->r - 8) / 10) + 231;
576 } else {
577 int r_index = (rgb->r < 45) ? 0 : (rgb->r - 60) / 40 + 1;
578 int g_index = (rgb->g < 45) ? 0 : (rgb->g - 60) / 40 + 1;
579 int b_index = (rgb->b < 45) ? 0 : (rgb->b - 60) / 40 + 1;
580 return 16 + (36 * r_index) + (6 * g_index) + b_index;
581 }
582}
583/** @brief Convert XTerm 256 color index to RGB color
584 @ingroup color_management
585 @param idx XTerm 256 color index
586 @return RGB color
587 note This function converts an XTerm 256 color index to an RGB color. It
588 first checks if the index is in the standard 16 colors, then checks if it's
589 in the 6x6x6 color cube, and finally checks if it's in the gray ramp. */
591 /** Convert XTerm 256 color index to RGB
592 @param idx - XTerm 256 color index
593 @return RGB struct */
594 RGB rgb;
595 if (idx > 255)
596 idx = 255;
597 if (idx < 0)
598 idx = 0;
599 rgb.r = rgb.g = rgb.b = 0;
600 if (idx < 16) {
601 rgb.r = StdColors[idx].r;
602 rgb.g = StdColors[idx].g;
603 rgb.b = StdColors[idx].b;
604 } else if (idx >= 16 && idx <= 231) {
605 idx -= 16;
606 rgb.r = (idx / 36) % 6 * 51;
607 rgb.g = (idx / 6) % 6 * 51;
608 rgb.b = (idx % 6) * 51;
609 } else if (idx >= 232 && idx <= 255) {
610 int gray = (idx - 232) * 11;
611 rgb.r = rgb.g = rgb.b = gray;
612 }
613 return rgb;
614}
615
616/** @brief Apply gamma correction to RGB color
617 @ingroup color_management
618 @param rgb Pointer to RGB color
619 @note This function modifies the RGB color in place. It applies gamma
620 correction to the RGB color based on the gamma values set in the SIO struct.
621 If the color is a shade of gray, it applies the gray gamma correction.
622 Otherwise, it applies the individual red, green, and blue gamma corrections.
623 */
624void apply_gamma(RGB *rgb) {
625 if (rgb->r == rgb->g && rgb->r == rgb->b) {
626 if (GRAY_GAMMA > 0.0f && GRAY_GAMMA != 1.0f) {
627 rgb->r = (int)(pow((rgb->r / 255.0f), 1.0f / GRAY_GAMMA) * 255.0f);
628 rgb->g = rgb->r;
629 rgb->b = rgb->r;
630 }
631 return;
632 }
633 if (rgb->r != 0 && RED_GAMMA > 0.0f && RED_GAMMA != 1.0f)
634 rgb->r = (int)(pow((rgb->r / 255.0f), 1.0f / RED_GAMMA) * 255.0f);
635 if (rgb->g != 0 && GREEN_GAMMA > 0.0f && GREEN_GAMMA != 1.0f)
636 rgb->g = (int)(pow((rgb->g / 255.0f), 1.0f / GREEN_GAMMA) * 255.0f);
637 if (rgb->b != 0 && BLUE_GAMMA > 0.0f && BLUE_GAMMA != 1.0f)
638 rgb->b = (int)(pow((rgb->b / 255.0f), 1.0f / BLUE_GAMMA) * 255.0f);
639}
640/** @brief Initialize color palette based on SIO settings
641 @ingroup color_management
642 @param sio Pointer to SIO struct with color settings
643 @return true if successful, false if error
644 @note This function initializes the xterm256 color cube and applies any
645 color overrides specified in the SIO struct. The color strings in the SIO
646 struct are expected to be six-digit HTML style hex color codes (e.g.,
647 "#RRGGBB"). If a color override is specified for any of the standard colors,
648 it is applied using the init_hex_clr function. After processing all colors,
649 the clr_cnt variable is set to 16 to indicate that the standard colors have
650 been initialized. */
651bool init_clr_palette(SIO *sio) {
652 if (sio->black[0])
654 if (sio->red[0])
656 if (sio->green[0])
658 if (sio->yellow[0])
660 if (sio->blue[0])
662 if (sio->magenta[0])
664 if (sio->cyan[0])
666 if (sio->white[0])
668 if (sio->bblack[0])
670 if (sio->bred[0])
672 if (sio->bgreen[0])
674 if (sio->byellow[0])
676 if (sio->bblue[0])
678 if (sio->bmagenta[0])
680 if (sio->bcyan[0])
682 if (sio->bwhite[0])
684 if (sio->borange[0])
686 if (sio->fg_clr_x[0])
688 if (sio->bg_clr_x[0])
690 if (sio->bo_clr_x[0])
692 if (sio->ln_clr_x[0])
694 if (sio->ln_bg_clr_x[0])
697 return true;
698}
699/** @brief Initialize extended ncurses color from HTML style hex string
700 @ingroup color_management
701 @param idx Color index
702 @param s Hex color string
703 @note NCurses uses 0-1000 for RGB values, so the RGB values from the hex
704 string are converted to this range before initializing the color. If the
705 color index is less than 16, the RGB values are also stored in the StdColors
706 array for reference.
707 */
708void init_hex_clr(int idx, char *s) {
709 RGB rgb;
711 apply_gamma(&rgb);
712 if (idx < 16) {
713 StdColors[idx].r = rgb.r;
714 StdColors[idx].g = rgb.g;
715 StdColors[idx].b = rgb.b;
716 }
717 rgb.r = (rgb.r * 1000) / 255;
718 rgb.g = (rgb.g * 1000) / 255;
719 rgb.b = (rgb.b * 1000) / 255;
720 init_extended_color(idx, rgb.r, rgb.g, rgb.b);
721}
722/** @brief Convert six-digit HTML style hex color code to RGB struct
723 @ingroup color_management
724 @param s six-digit HTML style hex color code */
725RGB hex_clr_str_to_rgb(char *s) {
726 RGB rgb;
727 sscanf(s, "#%02x%02x%02x", &rgb.r, &rgb.g, &rgb.b);
728 return rgb;
729}
730/** @brief Gracefully shut down NCurses and restore terminal settings
731 @ingroup window_support
732 @note This function should be called before exiting the program to ensure
733 that the terminal is left in a usable state. It checks if NCurses was
734 initialized and, if so, it erases the screen, refreshes it, and ends the
735 NCurses session. It also restores the original terminal settings using
736 restore_shell_tioctl and resets signal handlers to their default state with
737 sig_dfl_mode. */
739 if (!f_curses_open)
740 return;
741 while (win_ptr > 0) {
742 if (win_win[win_ptr])
743 delwin(win_win[win_ptr]);
744 if (win_box[win_ptr])
745 delwin(win_box[win_ptr]);
748 win_ptr--;
749 }
750 werase(stdscr);
751 wrefresh(stdscr);
752 endwin();
753 delscreen(screen);
754 // screen = nullptr;
755 fclose(ncurses_fp);
756 f_curses_open = false;
759 return;
760}
761/** @brief Create a cchar_t with the specified color pair index
762 @ingroup color_management
763 @param cp Color pair index
764 @return cchar_t with the specified color pair index and a space character
765 as the wide character */
766cchar_t mkccc(int cp) {
767 cchar_t cc = {0};
768 wchar_t wc = L' ';
769 setcchar(&cc, &wc, WA_NORMAL, cp, nullptr);
770 return cc;
771}
772/** @brief Create a new window with optional box and title
773 @ingroup window_support
774 @param wlines Number of lines
775 @param wcols Number of columns
776 @param wbegy Beginning Y position
777 @param wbegx Beginning X position
778 @param wtitle Window title
779 @param flag Window flags
780 @note if flag set to W_BOX, Only create win_box. This is for View which uses
781 the box window for display and doesn't need a separate win_win
782 @return 0 if successful, 1 if error */
783int win_new(int wlines, int wcols, int wbegy, int wbegx, char *wtitle,
784 int flag) {
785 int maxx;
786 if (win_ptr < MAXWIN) {
787 win_ptr++;
788 if (wbegy != 0 || wbegx != 0 || wlines < LINES - 2 ||
789 wcols < COLS - 2) {
790 win_box[win_ptr] = newwin(wlines + 2, wcols + 2, wbegy, wbegx);
791 if (win_box[win_ptr] == nullptr) {
792 win_ptr--;
793 return (1);
794 }
795#ifdef NCDEBUG
796 immedok(win_box[win_ptr], true);
797#endif
798 wbkgrnd(win_box[win_ptr], &CCC_BOX);
799 wbkgrndset(win_box[win_ptr], &CCC_BOX);
801 mvwaddnwstr(win_box[win_ptr], 0, 1, &bw_rt, 1);
802 mvwaddnwstr(win_box[win_ptr], 0, 2, &bw_sp, 1);
803 mvwaddstr(win_box[win_ptr], 0, 3, wtitle);
804 maxx = getmaxx(win_box[win_ptr]);
805 int s = strlen(wtitle);
806 if ((s + 3) < maxx)
807 mvwaddch(win_box[win_ptr], 0, (s + 3), ' ');
808 if ((s + 4) < maxx)
809 mvwaddnwstr(win_box[win_ptr], 0, (s + 4), &bw_lt, 1);
810 wnoutrefresh(win_box[win_ptr]);
811 wbegy += 1;
812 wbegx += 1;
813 } else {
814 win_box[win_ptr] = newwin(wlines, wcols, wbegy, wbegx);
815 if (win_box[win_ptr] == nullptr)
816 win_ptr--;
817 return (1);
818#ifdef NCDEBUG
819 immedok(win_box[win_ptr], true);
820#endif
821 wbkgrnd(win_box[win_ptr], &CCC_BOX);
822 wbkgrndset(win_box[win_ptr], &CCC_BOX);
823 }
824 if (!(flag & W_BOX)) {
825 win_win[win_ptr] = newwin(wlines, wcols, wbegy, wbegx);
826 if (win_win[win_ptr] == nullptr) {
827 delwin(win_box[win_ptr]);
828 return (1);
829 }
830#ifdef NCDEBUG
831 immedok(win_win[win_ptr], true);
832#endif
833 wbkgrnd(win_win[win_ptr], &CCC_WIN);
834 wbkgrndset(win_win[win_ptr], &CCC_WIN);
835 keypad(win_win[win_ptr], true);
836 idlok(win_win[win_ptr], false);
837 idcok(win_win[win_ptr], false);
838 }
839 }
840 return (0);
841}
842/** @brief Resize the current window and its box, and update the title
843 @ingroup window_support
844 @param wlines Number of lines
845 @param wcols Number of columns
846 @param title Window title
847 @note This function resizes the current window and its associated box window
848 to the specified number of lines and columns. It also updates the title of
849 the box window if a title is provided. After resizing, it refreshes the
850 windows to apply the changes. */
851void win_resize(int wlines, int wcols, char *title) {
852 int maxx;
853 wrefresh(stdscr);
854 wresize(win_box[win_ptr], wlines + 2, wcols + 2);
855 wbkgrnd(win_box[win_ptr], &CCC_BOX);
856 wbkgrndset(win_box[win_ptr], &CCC_BOX);
858 if (title != nullptr && *title != '\0') {
859 wmove(win_box[win_ptr], 0, 1);
860 waddnstr(win_box[win_ptr], (const char *)&bw_rt, 1);
861 wmove(win_box[win_ptr], 0, 2);
862 waddnstr(win_box[win_ptr], (const char *)&bw_sp, 1);
863 mvwaddnwstr(win_box[win_ptr], 0, 1, &bw_rt, 1);
864 mvwaddnwstr(win_box[win_ptr], 0, 2, &bw_sp, 1);
865 mvwaddstr(win_box[win_ptr], 0, 3, title);
866 maxx = getmaxx(win_box[win_ptr]);
867 int s = strlen(title);
868 if ((s + 3) < maxx)
869 mvwaddch(win_box[win_ptr], 0, (s + 3), ' ');
870 if ((s + 4) < maxx)
871 mvwaddnwstr(win_box[win_ptr], 0, (s + 4), &bw_lt, 1);
872 }
873 wnoutrefresh(win_box[win_ptr]);
874 wresize(win_win[win_ptr], wlines, wcols);
875 wbkgrnd(win_win[win_ptr], &CCC_WIN);
876 wbkgrndset(win_win[win_ptr], &CCC_WIN);
877 wsetscrreg(win_win[win_ptr], 0, wlines - 1);
878 keypad(win_win[win_ptr], TRUE);
879 idlok(win_win[win_ptr], false);
880 idcok(win_win[win_ptr], false);
881#ifdef NCDEBUG
882 immedok(win_win[win_ptr], true);
883#endif
884}
885/** @brief Redraw the specified window
886 @ingroup window_support
887 @param win Pointer to the window to redraw
888 @note This function erases the contents of the specified window and then
889 refreshes it to update the display. Use this function when you need to clear
890 and redraw a window, such as after resizing or when updating its contents. */
891void win_redraw(WINDOW *win) {
892 werase(win);
893 wnoutrefresh(win);
894}
895/** @brief Delete the current window and its associated box window
896 @ingroup window_support
897 @return nullptr
898 @note This function deletes the current window and its associated box
899 window, if they exist. It also refreshes the remaining windows to ensure the
900 display is updated correctly. After calling this function, the global win_ptr
901 variable is decremented to point to the previous window in the stack. */
902WINDOW *win_del() {
903 int i;
904 curs_set(0);
905 if (win_ptr >= 0) {
906 touchwin(win_win[win_ptr]);
907 wbkgrnd(win_win[win_ptr], &CCC_NORM);
908 wbkgrndset(win_win[win_ptr], &CCC_NORM);
909 werase(win_win[win_ptr]);
910 wnoutrefresh(win_win[win_ptr]);
911 delwin(win_win[win_ptr]);
912
913 touchwin(win_box[win_ptr]);
914 wbkgrnd(win_box[win_ptr], &CCC_NORM);
915 wbkgrndset(win_box[win_ptr], &CCC_NORM);
916 werase(win_box[win_ptr]);
917 wnoutrefresh(win_box[win_ptr]);
918 delwin(win_box[win_ptr]);
919
920 for (i = 0; i < win_ptr; i++) {
921 touchwin(win_box[i]);
922 wnoutrefresh(win_box[i]);
923 touchwin(win_win[i]);
924 wnoutrefresh(win_win[i]);
925 }
926 win_ptr--;
927 }
928 curs_set(1);
929 return (0);
930}
931/** @brief Restore all windows after a screen resize
932 @ingroup window_support
933 @note This function is used to restore the display of all windows after a
934 screen resize event. It clears the standard screen and then iterates through
935 all existing windows, touching and refreshing them to ensure they are redrawn
936 correctly on the resized screen. Use this function in response to a SIGWINCH
937 signal to handle terminal resizing gracefully. */
939 int i;
940 touchwin(stdscr);
941 wnoutrefresh(stdscr);
942 wrefresh(stdscr);
943 for (i = 0; i <= win_ptr; i++) {
944 touchwin(win_box[i]);
945 wnoutrefresh(win_box[i]);
946 wrefresh(win_box[i]);
947 touchwin(win_win[i]);
948 wnoutrefresh(win_win[i]);
949 wrefresh(win_win[i]);
950 }
951}
952/** @brief Draw a box around the specified window
953 @ingroup window_support
954 @param box Pointer to the window to draw the box around
955 @note This function uses NCurses functions to draw a box around the
956 specified window. It adds the appropriate characters for the corners and
957 edges of the box based on the current character set. Use this function when
958 you want to visually separate a window from the rest of the screen with a
959 border. */
960void cbox(WINDOW *box) {
961 int x, y;
962 int maxx;
963 int maxy;
964
965 maxx = getmaxx(box);
966 maxx--;
967 mvwaddnwstr(box, 0, 0, &bw_tl, 1);
968 for (x = 1; x < maxx; x++)
969 waddnwstr(box, &bw_ho, 1);
970 waddnwstr(box, &bw_tr, 1);
971 maxy = getmaxy(box);
972 maxy--;
973 for (y = 1; y < maxy; y++) {
974 mvwaddnwstr(box, y, 0, &bw_ve, 1);
975 mvwaddnwstr(box, y, maxx, &bw_ve, 1);
976 }
977 mvwaddnwstr(box, maxy, 0, &bw_bl, 1);
978 for (x = 1; x < maxx; x++)
979 waddnwstr(box, &bw_ho, 1);
980 waddnwstr(box, &bw_br, 1);
981}
982
983/** @defgroup error_handling Error Handling
984 @brief Display Error messages
985 */
986
987/** @brief Accept a single letter answer
988 @ingroup error_handling
989 @param em0 First error message line
990 @param em1 Second error message line
991 @param em2 Third error message line
992 @param em3 Fourth error message line
993 @return Key code of user command */
994int answer_yn(char *em0, char *em1, char *em2, char *em3) {
995 char title[MAXLEN];
996 int line, pos, em_l, em0_l, em1_l, em2_l, em3_l;
997 WINDOW *error_win;
998
999 if (!f_curses_open) {
1000 fprintf(stderr, "\n\n%s\n%s\n%s\n%s\n\n", em0, em1, em2, em3);
1001 return (1);
1002 }
1003
1004 Chyron *chyron = new_chyron();
1005 set_chyron_key(chyron, 1, "F1 Help", KEY_F(1));
1006 set_chyron_key(chyron, 2, "N - No", 'n');
1007 set_chyron_key(chyron, 3, "Y - Yes", 'y');
1008 compile_chyron(chyron);
1009
1010 em0_l = strnz(em0, COLS - 4);
1011 em1_l = strnz(em1, COLS - 4);
1012 em2_l = strnz(em2, COLS - 4);
1013 em3_l = strnz(em1, COLS - 4);
1014 em_l = max(em0_l, em1_l);
1015 em_l = max(em_l, em2_l);
1016 em_l = max(em_l, em3_l);
1017 em_l = max(em_l, chyron->l);
1018 em_l = min(em_l, COLS - 4);
1019
1020 pos = ((COLS - em_l) - 4) / 2;
1021 line = (LINES - 6) / 2;
1022 strnz__cpy(title, "Notification", MAXLEN - 1);
1023 if (win_new(5, em_l + 2, line, pos, title, 0)) {
1024 ssnprintf(title, MAXLEN - 1, "win_new(%d, %d, %d, %d, %s, %b) failed",
1025 5, em_l + 2, line, pos, title, 0);
1026 destroy_chyron(chyron);
1027 abend(-1, title);
1028 }
1029 error_win = win_win[win_ptr];
1030 mvwaddstr(error_win, 0, 1, em0);
1031 mvwaddstr(error_win, 1, 1, em1);
1032 mvwaddstr(error_win, 2, 1, em2);
1033 mvwaddstr(error_win, 3, 1, em3);
1034 display_chyron(error_win, chyron, 4, chyron->l + 1);
1035 do {
1036 curs_set(1);
1037 cmd_key = xwgetch(error_win, chyron, -1);
1038 curs_set(0);
1039 if (cmd_key == KEY_F(1) || cmd_key == 'N' || cmd_key == 'n' ||
1040 cmd_key == 'Y' || cmd_key == 'y')
1041 break;
1042 } while (1);
1043 win_del();
1044 destroy_chyron(chyron);
1045 return (cmd_key);
1046}
1047/** @brief Display an error message window or print to stderr
1048 @ingroup error_handling
1049 @param em0 First error message line
1050 @param em1 Second error message line
1051 @param em2 Third error message line
1052 @param em3 Fourth error message line
1053 @return Key code of user command */
1054int display_error(char *em0, char *em1, char *em2, char *em3) {
1055 char title[MAXLEN];
1056 int line, pos, em_l, em0_l, em1_l, em2_l, em3_l;
1057 WINDOW *error_win;
1058
1059 if (!f_curses_open) {
1060 fprintf(stderr, "\n\n%s\n%s\n%s\n%s\n\n", em0, em1, em2, em3);
1061 return (1);
1062 }
1063
1064 Chyron *chyron = new_chyron();
1065 set_chyron_key(chyron, 1, "F1 Help", KEY_F(1));
1066 set_chyron_key(chyron, 9, "F9 Cancel", KEY_F(9));
1067 set_chyron_key(chyron, 10, "F10 Continue", KEY_F(10));
1068 compile_chyron(chyron);
1069
1070 em0_l = strnz(em0, COLS - 4);
1071 em1_l = strnz(em1, COLS - 4);
1072 em2_l = strnz(em2, COLS - 4);
1073 em3_l = strnz(em1, COLS - 4);
1074 em_l = max(em0_l, em1_l);
1075 em_l = max(em_l, em2_l);
1076 em_l = max(em_l, em3_l);
1077 em_l = max(em_l, chyron->l);
1078 em_l = min(em_l, COLS - 4);
1079
1080 pos = ((COLS - em_l) - 4) / 2;
1081 line = (LINES - 6) / 2;
1082 strnz__cpy(title, "Notification", MAXLEN - 1);
1083 if (win_new(5, em_l + 2, line, pos, title, 0)) {
1084 ssnprintf(title, MAXLEN - 1, "win_new(%d, %d, %d, %d, %s, %b) failed",
1085 5, em_l + 2, line, pos, title, 0);
1086 destroy_chyron(chyron);
1087 abend(-1, title);
1088 }
1089 error_win = win_win[win_ptr];
1090 mvwaddstr(error_win, 0, 1, em0);
1091 mvwaddstr(error_win, 1, 1, em1);
1092 mvwaddstr(error_win, 2, 1, em2);
1093 mvwaddstr(error_win, 3, 1, em3);
1094 display_chyron(error_win, chyron, 4, chyron->l + 1);
1095 do {
1096 cmd_key = xwgetch(error_win, chyron, -1);
1097 if (cmd_key == KEY_F(9) || cmd_key == KEY_F(10) || cmd_key == 'q' ||
1098 cmd_key == 'Q')
1099 break;
1100 } while (1);
1101 win_del();
1102 destroy_chyron(chyron);
1103 return (cmd_key);
1104}
1105
1106/** @brief Display a simple error message window or print to stderr
1107 @ingroup error_handling
1108 @param emsg_str Error message string
1109 @return Key code of user command */
1110int Perror(char *emsg_str) {
1111 char emsg[80];
1112 int emsg_max_len = 80;
1113 unsigned cmd_key;
1114 WINDOW *error_win;
1115 int len, line, pos;
1116 char title[MAXLEN];
1117 bool f_xwgetch = true;
1118 if (emsg_str[0] == '␛' && emsg_str[1] == 'w') {
1119 emsg_str += 2;
1120 f_xwgetch = false;
1121 }
1122 strnz__cpy(emsg, emsg_str, emsg_max_len - 1);
1123 if (!f_curses_open) {
1124 fprintf(stderr, "\n%s\n", emsg);
1125 return (1);
1126 }
1127 Chyron *chyron = new_chyron();
1128 set_chyron_key(chyron, 1, "F1 Help", KEY_F(1));
1129 set_chyron_key(chyron, 9, "F9 Cancel", KEY_F(9));
1130 set_chyron_key(chyron, 10, "F10 Continue", KEY_F(10));
1131 compile_chyron(chyron);
1132 len = max(strlen(title), strlen(emsg));
1133 len = max(len, chyron->l);
1134 len = max(len, 40);
1135 pos = (COLS - len - 4) / 2;
1136 line = (LINES - 4) / 2;
1137 strnz__cpy(title, "Notification", MAXLEN - 1);
1138 if (win_new(2, len + 2, line, pos, title, 0)) {
1139 ssnprintf(title, MAXLEN - 1, "win_new(%d, %d, %d, %d, %s, %b) failed",
1140 4, line, line, pos, title, 0);
1141 destroy_chyron(chyron);
1142 abend(-1, title);
1143 }
1144 error_win = win_win[win_ptr];
1145 mvwaddstr(error_win, 0, 1, emsg);
1146 display_chyron(error_win, chyron, 1, chyron->l + 1);
1147 if (f_xwgetch) {
1148 curs_set(1);
1149 cmd_key = xwgetch(error_win, chyron, -1);
1150 curs_set(0);
1151 win_del();
1152 } else {
1153 cmd_key = KEY_F(10);
1154 }
1155 destroy_chyron(chyron);
1156 return (cmd_key);
1157}
1158/** @brief Create a Chyron struct for the waiting message
1159 @ingroup error_handling
1160 @return Pointer to the chyron struct */
1162 Chyron *chyron = new_chyron();
1163 set_chyron_key(chyron, 9, "F9 Cancel", KEY_F(9));
1164 compile_chyron(chyron);
1165 return chyron;
1166}
1167/** @brief Display a popup waiting message
1168 @ingroup error_handling
1169 @param chyron Pointer to Chyron struct for displaying key options
1170 @param title window title
1171 @return WINDOW * struct */
1172WINDOW *wait_mk_win(Chyron *chyron, char *title) {
1173 char wm1[] = "Seconds remaining:";
1174 int len;
1175 int line, col;
1176 WINDOW *wait_win;
1177
1178 if (!f_curses_open) {
1179 fprintf(stderr, "\n%s\n", title);
1180 fprintf(stderr, "%s\n", wm1);
1181 return NULL;
1182 }
1183 len = max(strlen(title), strlen(wm1));
1184 len = max(len, chyron->l);
1185 len = max(len, 40);
1186 col = (COLS - len - 4) / 2;
1187 line = (LINES - 4) / 2;
1188 if (win_new(2, len + 2, line, col, title, 0)) {
1189 ssnprintf(title, MAXLEN - 1, "win_new(%d, %d, %d, %d, %s, %b) failed",
1190 4, line, line, col, title, 0);
1191 abend(-1, title);
1192 }
1193 wait_win = win_win[win_ptr];
1194 mvwaddstr(wait_win, 0, 1, wm1);
1195 display_chyron(wait_win, chyron, 1, 0);
1196 wmove(wait_win, 1, chyron->l);
1197 return wait_win;
1198}
1199/** @brief Destroy the waiting message window and chyron
1200 @ingroup error_handling
1201 @param chyron Pointer to Chyron struct for displaying key options
1202 @return true if successful */
1203bool wait_destroy(Chyron *chyron) {
1204 win_del();
1205 destroy_chyron(chyron);
1206 return true;
1207}
1208/** @brief Update the waiting message with remaining time and check for user
1209 input
1210 @ingroup error_handling
1211 @param chyron Pointer to Chyron struct for displaying key options
1212 @param wait_win Pointer to the waiting message window
1213 @param remaining Time remaining for the wait in seconds
1214 @return true if the wait should continue, false if it should be cancelled */
1215int wait_continue(WINDOW *wait_win, Chyron *chyron, int remaining) {
1216 char time_str[10];
1217 ssnprintf(time_str, 9, "%-4d", remaining);
1218 mvwaddstr(wait_win, 0, 21, time_str);
1219 display_chyron(wait_win, chyron, 1, 0);
1220 wmove(wait_win, 1, chyron->l);
1221 cmd_key = xwgetch(wait_win, chyron, 1);
1222 return cmd_key;
1223}
1224bool action_disposition(char *title, char *action_str) {
1225 int len;
1226 int line, col;
1227 WINDOW *action_disposition_win;
1228
1229 if (!f_curses_open) {
1230 fprintf(stderr, "\n%s\n", title);
1231 fprintf(stderr, "%s\n", action_str);
1232 return true;
1233 }
1234 Chyron *chyron = new_chyron();
1235 set_chyron_key(chyron, 10, "F10 Continue", KEY_F(10));
1236 compile_chyron(chyron);
1237 len = max(strlen(title), strlen(action_str));
1238 col = (COLS - len - 4) / 2;
1239 line = (LINES - 4) / 2;
1240 if (win_new(2, len + 2, line, col, title, 0)) {
1241 ssnprintf(em0, MAXLEN - 1, "win_new(%d, %d, %d, %d, %s, %b) failed", 4,
1242 line, line, col, title, 0);
1244 }
1245 action_disposition_win = win_win[win_ptr];
1246 mvwaddstr(action_disposition_win, 0, 1, action_str);
1247 display_chyron(action_disposition_win, chyron, 1, 0);
1248 wmove(action_disposition_win, 1, chyron->l);
1249 cmd_key = xwgetch(action_disposition_win, chyron, 1);
1250 win_del();
1251 destroy_chyron(chyron);
1252 return true;
1253}
1254
1255/** @brief For lines shorter than their display area, fill the rest with spaces
1256 @ingroup window_support
1257 @param w Pointer to window
1258 @param y Y coordinate
1259 @param x X coordinate
1260 @param s String to display
1261 @param l Length of display area */
1262void mvwaddstr_fill(WINDOW *w, int y, int x, char *s, int l) {
1263 char *d, *e;
1264 char tmp_str[MAXLEN];
1265
1266 d = tmp_str;
1267 if (l > MAXLEN - 1)
1268 l = MAXLEN - 1;
1269 e = tmp_str + l;
1270 while (d < e)
1271 if (*s == '\0' || *s == '\n')
1272 *d++ = ' ';
1273 else
1274 *d++ = *s++;
1275 *d++ = '\0';
1276 mvwaddstr(w, y, x, tmp_str);
1277}
1278/** @brief Get color index from color name
1279 @ingroup color_management
1280 @param s Color name
1281 @return Color index or -1 if not found */
1282int clr_name_to_idx(char *s) {
1283 int i = 0;
1284 int n = 16;
1285
1287 while (i < n) {
1288 if (!strcmp(colors_text[i], s))
1289 break;
1290 i++;
1291 }
1292 if (i >= n)
1293 return (-1);
1294 return (i);
1295}
1296/** @brief list colors to stderr
1297 @ingroup color_management
1298 @note only lists the first 16, since that's how many we let the
1299 user redefine */
1301 int i, col;
1302
1303 for (i = 0, col = 0; i < 16; i++, col++) {
1304 if (i < 8) {
1305 fprintf(stderr, " ");
1306 }
1307 if (i == 8) {
1308 col = 0;
1309 fprintf(stderr, "\n");
1310 } else if (col > 0)
1311 fprintf(stderr, " ");
1312 fprintf(stderr, "%s", colors_text[i]);
1313 }
1314 fprintf(stderr, "\n");
1315}
1316/** @brief Display error message and wait for key press
1317 @ingroup error_handling
1318 @param ec Error code
1319 @param s Error message */
1320int nf_error(int ec, char *s) {
1321 fprintf(stderr, "ERROR: %s code: %d\n", s, ec);
1322 fprintf(stderr, "Press a key to continue");
1323 di_getch();
1324 fprintf(stderr, "\n");
1325 return ec;
1326}
1327/** @brief Abnormal program termination
1328 @ingroup error_handling
1329 @param ec Exit code
1330 @param s Error message */
1331void abend(int ec, char *s) {
1335 fprintf(stderr, "\n\nABEND: %s (code: %d)\n", s, ec);
1336 exit(EXIT_FAILURE);
1337}
1338/** @brief Wrapper for wgetch that handles signals, mouse events, checks for
1339 clicks on the chyron line, and accepts a sinigle character answer
1340 @ingroup window_support
1341 @param win Pointer to window
1342 @param chyron Pointer to chyron struct
1343 @param n Number of seconds to wait before timing out
1344 @verbatim
1345
1346 0: Wait indefinitely for user input (raw mode)
1347 accept a single character answer, and don't wait for Enter key
1348 1: Wait for 1 decisecond
1349 n > 1: Wait for n/10 seconds
1350
1351 @endverbatim
1352 @return Key code or ERR if interrupted by signal
1353 @note This, of course, will be expanded into an event loop for message
1354 queuing
1355 @details Get mouse event and check if it's a left click or double click. If
1356 the click is outside the window, ignore it. If it's on the chyron line, get
1357 the corresponding key command. Otherwise, store the click coordinates as
1358 click_y and click_x for later use. */
1359int xwgetch(WINDOW *win, Chyron *chyron, int n) {
1360 int c;
1361 MEVENT event;
1362 mousemask(BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED | BUTTON4_PRESSED |
1363 BUTTON5_PRESSED,
1364 nullptr);
1365 event.y = event.x = -1;
1366 click_y = click_x = -1;
1367
1368 if (n == -1) {
1369 struct termios raw_tioctl;
1370 raw_tioctl = curses_tioctl;
1371 mk_raw_tioctl(&raw_tioctl);
1372 } else if (n == 0)
1373 halfdelay(1);
1374 else
1375 halfdelay(min(255, max(0, n * 10)));
1376 tcflush(2, TCIFLUSH);
1377 curs_set(1);
1378 do {
1379 c = wgetch(win);
1380 if (sig_received != 0) {
1383 if (c == 'q' || c == 'Q' || c == KEY_F(9))
1384 exit(EXIT_FAILURE);
1385 }
1386 if (n > 0 && c == ERR) {
1387 c = 0;
1388 break;
1389 }
1390 if (c == ERR)
1391 continue;
1392 if (c == KEY_MOUSE) {
1393 if (getmouse(&event) != OK) {
1394 c = 0;
1395 continue;
1396 }
1397 if (event.bstate & BUTTON4_PRESSED) {
1398 curs_set(0);
1399 return KEY_UP;
1400 } else if (event.bstate & BUTTON5_PRESSED) {
1401 curs_set(0);
1402 return KEY_DOWN;
1403 }
1404 if (event.bstate & BUTTON1_CLICKED ||
1405 event.bstate & BUTTON1_DOUBLE_CLICKED) {
1406 if (!wenclose(win, event.y, event.x)) {
1407 c = 0;
1408 continue;
1409 }
1410 wmouse_trafo(win, &event.y, &event.x, false);
1411 if (event.y < 0 || event.x < 0 || event.x >= getmaxx(win) ||
1412 event.y >= getmaxy(win)) {
1413 c = 0;
1414 continue;
1415 }
1416 click_y = event.y;
1417 click_x = event.x;
1418 if (chyron && event.y == getmaxy(win) - 1) {
1419 c = get_chyron_key(chyron, event.x);
1420 break;
1421 } else
1422 break;
1423 }
1424 }
1425 } while (c == ERR);
1426 curs_set(0);
1428 return c;
1429}
1430
1431bool waitpid_with_timeout(pid_t pid, int timeout) {
1432 int status;
1433 int rc;
1434 Chyron *wait_chyron;
1435 WINDOW *wait_win;
1436 int remaining = timeout;
1437
1438 usleep(100000); // Sleep for 200ms */
1439 rc = waitpid(pid, &status, WNOHANG);
1440 if (rc == pid)
1441 return true;
1442 if (rc == -1) {
1443 ssnprintf(em0, MAXLEN - 1, "Error waiting for process %d", pid);
1445 return false;
1446 }
1447 wait_chyron = wait_mk_chyron();
1448 ssnprintf(em0, MAXLEN - 1, "Waiting for process %d to finish...", pid);
1449 wait_win = wait_mk_win(wait_chyron, em0);
1450 cmd_key = 0;
1451 while (rc == 0 && remaining > 0 && cmd_key != KEY_F(9)) {
1452 cmd_key = wait_continue(wait_win, wait_chyron, remaining);
1453 if (cmd_key == KEY_F(9))
1454 break;
1455 remaining--;
1456 }
1457 wait_destroy(wait_chyron);
1458 if (rc == pid)
1459 return true;
1460 return false;
1461}
volatile sig_atomic_t sig_received
Definition sig.c:31
bool handle_signal(sig_atomic_t)
#define BW_RBL
Definition cm.h:431
#define MAXWIN
Definition cm.h:468
@ CLR_FG
Definition cm.h:140
@ CLR_RED
Definition cm.h:124
@ CLR_YELLOW
Definition cm.h:126
@ CLR_BCYAN
Definition cm.h:137
@ CLR_WHITE
Definition cm.h:130
@ CLR_MAGENTA
Definition cm.h:128
@ CLR_BLACK
Definition cm.h:123
@ CLR_BWHITE
Definition cm.h:138
@ CLR_LN_BG
Definition cm.h:144
@ CLR_BO
Definition cm.h:142
@ CLR_BBLACK
Definition cm.h:131
@ CLR_BBLUE
Definition cm.h:135
@ CLR_LN
Definition cm.h:143
@ CLR_BGREEN
Definition cm.h:133
@ CLR_BYELLOW
Definition cm.h:134
@ CLR_BMAGENTA
Definition cm.h:136
@ CLR_BORANGE
Definition cm.h:139
@ CLR_BG
Definition cm.h:141
@ CLR_BRED
Definition cm.h:132
@ CLR_NCOLORS
Definition cm.h:145
@ CLR_BLUE
Definition cm.h:127
@ CLR_GREEN
Definition cm.h:125
@ CLR_CYAN
Definition cm.h:129
#define BW_HO
Definition cm.h:423
#define BW_VE
Definition cm.h:424
bool f_curses_open
Definition sig.c:33
#define BW_LT
Definition cm.h:433
#define BW_RTR
Definition cm.h:430
#define CHYRON_KEYS
Definition cm.h:230
struct termios shell_tioctl curses_tioctl
Definition scriou.c:34
#define BW_RT
Definition cm.h:435
#define CHYRON_KEY_MAXLEN
Definition cm.h:227
#define BW_SP
Definition cm.h:438
#define BW_RBR
Definition cm.h:432
#define nullptr
Definition cm.h:23
#define min(x, y)
min macro evaluates two expressions, returning least result
Definition cm.h:55
#define BW_RTL
Definition cm.h:429
#define W_BOX
Definition cm.h:179
#define max(a, b)
max macro evaluates two expressions, returning greatest result.
Definition cm.h:48
#define TRUE
Definition iloan.c:19
#define MAXLEN
Definition curskeys.c:15
int box_attr
Definition dwin.c:120
int cp_box
Definition dwin.c:138
unsigned int cmd_key
Definition dwin.c:117
WINDOW * win
Definition dwin.c:113
const wchar_t bw_rt
Definition dwin.c:102
const wchar_t bw_ho
Definition dwin.c:95
cchar_t CCC_LN
Definition dwin.c:151
int cp_win
Definition dwin.c:137
int clr_pair_cnt
Definition dwin.c:144
WINDOW * win_win[MAXWIN]
Definition dwin.c:114
bool waitpid_with_timeout(pid_t pid, int timeout)
Definition dwin.c:1431
int pipe_in
Definition dwin.c:153
bool action_disposition(char *title, char *action_str)
Definition dwin.c:1224
const wchar_t bw_tl
Definition dwin.c:97
bool f_sigwench
Definition dwin.c:118
char em1[MAXLEN]
Definition dwin.c:133
int m_cols
Definition dwin.c:123
int tty_fd
Definition dwin.c:153
void display_chyron(WINDOW *win, Chyron *chyron, int line, int col)
Definition dwin.c:297
RGB StdColors[16]
Definition dwin.c:82
cchar_t CCC_WIN
Definition dwin.c:146
int clr_cnt
Definition dwin.c:142
int m_begy
Definition dwin.c:124
int m_begx
Definition dwin.c:125
int stdout_fd
Definition dwin.c:128
const wchar_t bw_tr
Definition dwin.c:98
double BLUE_GAMMA
Definition dwin.c:110
int clr_pair_idx
Definition dwin.c:143
const wchar_t bw_sp
Definition dwin.c:103
cchar_t CCC_REVERSE_HIGHLIGHT
Definition dwin.c:150
cchar_t CCC_BG
Definition dwin.c:147
int mouse_support
Definition dwin.c:126
int cp_ln
Definition dwin.c:141
int m_lines
Definition dwin.c:122
const wchar_t bw_lt
Definition dwin.c:101
SCREEN * screen
Definition dwin.c:75
int click_x
Definition dwin.c:45
int src_line
Definition dwin.c:129
char * src_name
Definition dwin.c:130
cchar_t CCC_BOX
Definition dwin.c:148
SIO * sio
Definition dwin.c:77
const wchar_t bw_bl
Definition dwin.c:99
int win_ptr
Definition dwin.c:121
FILE * ncurses_fp
Definition dwin.c:154
int exit_code
Definition dwin.c:116
int click_y
Definition dwin.c:44
int stdin_fd
Definition dwin.c:127
char em0[MAXLEN]
Definition dwin.c:132
double GRAY_GAMMA
Definition dwin.c:104
char em3[MAXLEN]
Definition dwin.c:135
double RED_GAMMA
Definition dwin.c:106
int cp_reverse_highlight
Definition dwin.c:140
char const colors_text[][10]
Definition dwin.c:91
int rgb_clr_to_cube(int)
void set_chyron_key(Chyron *, int, char *, int)
Definition dwin.c:245
const wchar_t bw_ve
Definition dwin.c:96
cchar_t CCC_REVERSE
Definition dwin.c:149
int pipe_out
Definition dwin.c:153
double GREEN_GAMMA
Definition dwin.c:108
cchar_t CCC_NORM
Definition dwin.c:145
int cp_reverse
Definition dwin.c:139
int win_attr
Definition dwin.c:119
WINDOW * win_box[MAXWIN]
Definition dwin.c:115
char fn[MAXLEN]
Definition dwin.c:131
char em2[MAXLEN]
Definition dwin.c:134
int cp_norm
Definition dwin.c:136
const wchar_t bw_br
Definition dwin.c:100
bool open_curses(SIO *)
Initialize NCurses and color settings.
Definition dwin.c:423
int xwgetch(WINDOW *, Chyron *, int)
Wrapper for wgetch that handles signals, mouse events, checks for clicks on the chyron line,...
Definition dwin.c:1359
void restore_wins()
Restore all windows after a screen resize.
Definition dwin.c:938
int win_new(int, int, int, int, char *, int)
Create a new window with optional box and title.
Definition dwin.c:783
void win_init_attrs()
Initialize window attributes.
Definition dwin.c:162
WINDOW * win_del()
Delete the current window and its associated box window.
Definition dwin.c:902
void mvwaddstr_fill(WINDOW *, int, int, char *, int)
For lines shorter than their display area, fill the rest with spaces.
Definition dwin.c:1262
void cbox(WINDOW *)
Draw a box around the specified window.
Definition dwin.c:960
void win_resize(int, int, char *)
Resize the current window and its box, and update the title.
Definition dwin.c:851
void destroy_curses()
Gracefully shut down NCurses and restore terminal settings.
Definition dwin.c:738
void win_redraw(WINDOW *)
Redraw the specified window.
Definition dwin.c:891
bool is_set_chyron_key(Chyron *, int)
Check if function key label is set.
Definition dwin.c:217
Chyron * destroy_chyron(Chyron *chyron)
Destroy Chyron structure.
Definition dwin.c:198
int get_chyron_key(Chyron *, int)
Get keycode from chyron.
Definition dwin.c:369
void set_chyron_key_cp(Chyron *, int, char *, int, int)
Set chyron key.
Definition dwin.c:237
void compile_chyron(Chyron *)
construct the chyron string from the chyron structure
Definition dwin.c:268
void unset_chyron_key(Chyron *, int)
Unset chyron key.
Definition dwin.c:258
int mb_to_cc(cchar_t *, char *, attr_t, int, int *, int)
Convert multibyte string to complex character array.
Definition dwin.c:321
Chyron * new_chyron()
Create and initialize Chyron structure.
Definition dwin.c:183
cchar_t * mk_cmplx_buf(const char *s)
Create complex character buffer from multibyte string.
Definition dwin.c:386
cchar_t mkccc(int)
Create a cchar_t with the specified color pair index.
Definition dwin.c:766
RGB xterm256_idx_to_rgb(int)
Convert XTerm 256 color index to RGB color.
Definition dwin.c:590
int clr_name_to_idx(char *)
Get color index from color name.
Definition dwin.c:1282
void list_colors()
list colors to stderr
Definition dwin.c:1300
int rgb_to_curses_clr(RGB *)
Get color index for RGB color.
Definition dwin.c:541
void init_hex_clr(int, char *)
Initialize extended ncurses color from HTML style hex string.
Definition dwin.c:708
bool init_clr_palette(SIO *)
Initialize color palette based on SIO settings.
Definition dwin.c:651
RGB hex_clr_str_to_rgb(char *)
Convert six-digit HTML style hex color code to RGB struct.
Definition dwin.c:725
void apply_gamma(RGB *)
Apply gamma correction to RGB color.
Definition dwin.c:624
int rgb_to_xterm256_idx(RGB *)
Convert RGB color to XTerm 256 color index.
Definition dwin.c:569
int get_clr_pair(int fg, int bg)
Get color pair index for foreground and background colors.
Definition dwin.c:510
bool wait_destroy(Chyron *)
Destroy the waiting message window and chyron.
Definition dwin.c:1203
int nf_error(int, char *)
Display error message and wait for key press.
Definition dwin.c:1320
int wait_continue(WINDOW *, Chyron *, int)
Update the waiting message with remaining time and check for user input.
Definition dwin.c:1215
int answer_yn(char *em0, char *em1, char *em2, char *em3)
Accept a single letter answer.
Definition dwin.c:994
WINDOW * wait_mk_win(Chyron *, char *)
Display a popup waiting message.
Definition dwin.c:1172
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1110
int display_error(char *em0, char *em1, char *em2, char *em3)
Display an error message window or print to stderr.
Definition dwin.c:1054
void abend(int, char *)
Abnormal program termination.
Definition dwin.c:1331
Chyron * wait_mk_chyron()
Create a Chyron struct for the waiting message.
Definition dwin.c:1161
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:269
bool str_to_lower(char *)
Converts a string to lowercase.
Definition futil.c:233
size_t strnz(char *, size_t)
terminates string at New Line, Carriage Return, or max_len
Definition futil.c:340
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:147
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition futil.c:298
char di_getch()
sget single character from terminal in raw mode
Definition scriou.c:139
bool restore_curses_tioctl()
restore_curses_tioctl() - restore curses terminal settings
Definition scriou.c:81
bool mk_raw_tioctl(struct termios *)
mk_raw_tioctl() - set terminal to raw mode
Definition scriou.c:126
bool restore_shell_tioctl()
restore_shell_tioctl() - restore shell terminal settings
Definition scriou.c:56
void sig_dfl_mode()
Set signal handlers to default behavior.
Definition sig.c:42
char text[CHYRON_KEY_MAXLEN]
Definition cm.h:233
int end_pos
Definition cm.h:236
int cp
Definition cm.h:237
int keycode
Definition cm.h:235
int l
Definition cm.h:244
cchar_t cmplx_buf[MAXLEN]
Definition cm.h:243
ChyronKey * key[CHYRON_KEYS]
Definition cm.h:241
int r
Definition cm.h:266
int b
Definition cm.h:268
int g
Definition cm.h:267
double green_gamma
Definition cm.h:628
char black[COLOR_LEN]
Definition cm.h:631
char bg_clr_x[COLOR_LEN]
Definition cm.h:652
char fg_clr_x[COLOR_LEN]
Definition cm.h:651
double blue_gamma
Definition cm.h:629
char bred[COLOR_LEN]
Definition cm.h:641
char yellow[COLOR_LEN]
Definition cm.h:634
char bo_clr_x[COLOR_LEN]
Definition cm.h:653
char bcyan[COLOR_LEN]
Definition cm.h:646
char borange[COLOR_LEN]
Definition cm.h:648
double red_gamma
Definition cm.h:627
char red[COLOR_LEN]
Definition cm.h:632
char magenta[COLOR_LEN]
Definition cm.h:636
char bgreen[COLOR_LEN]
Definition cm.h:642
char ln_clr_x[COLOR_LEN]
Definition cm.h:654
char byellow[COLOR_LEN]
Definition cm.h:643
char bwhite[COLOR_LEN]
Definition cm.h:647
char ln_bg_clr_x[COLOR_LEN]
Definition cm.h:655
char cyan[COLOR_LEN]
Definition cm.h:637
char tty_name[MAXLEN]
Definition cm.h:656
char green[COLOR_LEN]
Definition cm.h:633
char white[COLOR_LEN]
Definition cm.h:638
char bblue[COLOR_LEN]
Definition cm.h:644
char bmagenta[COLOR_LEN]
Definition cm.h:645
char blue[COLOR_LEN]
Definition cm.h:635
double gray_gamma
Definition cm.h:630
char bblack[COLOR_LEN]
Definition cm.h:640