C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
ui_ncurses.c
Go to the documentation of this file.
1// src/ui/ui_ncurses.c
2#include "../include/cm.h"
4#include <stdlib.h>
5#include <string.h>
6
7/** @file ui_ncurses.c
8 * @ingroup ui_ncurses
9 * @brief Ncurses-based UI implementation.
10 *
11 * This file implements the UI runtime and surface management using the
12 * ncurses library. It provides functions for creating and managing surfaces,
13 * handling input, and rendering the UI. The implementation is designed to be
14 * modular and can be extended to support additional features as needed.
15 */
16
17/** @defgroup ui_ncurses Ncurses UI Implementation
18 * @brief UI implementation using the ncurses library.
19 *
20 * This module provides functions for creating and managing UI surfaces, handling
21 * input, and rendering the UI using ncurses. It is designed to be used as part
22 * of a larger UI framework and can be extended with additional features as
23 * needed.
24 */
25
29
30/** * @brief Create a new UI surface.
31 * @ingroup ui_ncurses
32 * @param ui The UI runtime instance.
33 * @param parent The parent surface, or NULL for a top-level surface.
34 * @param rect The rectangle defining the position and size of the surface.
35 * @return A pointer to the newly created UiSurface, or NULL on failure.
36 */
37UiSurface *ui_surface_new(UiRuntime *ui, UiSurface *parent, UiRect rect) {
38 UiSurface *s = calloc(1, sizeof(*s));
39 if (!s)
40 return NULL;
41
42 s->runtime = ui;
43 s->parent = parent;
44 s->y = rect.y;
45 s->x = rect.x;
46 s->rows = rect.rows;
47 s->cols = rect.cols;
48
49 if (parent && parent->win) {
50 s->win = derwin(parent->win, rect.rows, rect.cols, rect.y, rect.x);
51 } else {
52 s->win = newwin(rect.rows, rect.cols, rect.y, rect.x);
53 }
54
55 if (!s->win) {
56 free(s);
57 return NULL;
58 }
59
60 s->pan = new_panel(s->win);
61 if (!s->pan) {
62 delwin(s->win);
63 free(s);
64 return NULL;
65 }
66
67 return s;
68}
69
70/** @brief Destroy a UI surface and free its resources.
71 * @ingroup ui_ncurses
72 * @param s The UiSurface to destroy.
73 */
74void ui_surface_destroy(UiSurface *s) {
75 if (!s)
76 return;
77 if (s->pan)
78 del_panel(s->pan);
79 if (s->win)
80 delwin(s->win);
81 free(s);
82}
83
84/** @brief Move a UI surface to a new position.
85 * @ingroup ui_ncurses
86 * @param s The UiSurface to move.
87 * @param y The new y-coordinate of the surface.
88 * @param x The new x-coordinate of the surface.
89 * @return 0 on success, or -1 on failure.
90 */
91int ui_surface_move(UiSurface *s, int y, int x) {
92 if (!s)
93 return -1;
94 s->y = y;
95 s->x = x;
96 return move_panel(s->pan, y, x);
97}
98
99/** @brief Resize a UI surface to new dimensions.
100 * @ingroup ui_ncurses
101 * @param s The UiSurface to resize.
102 * @param rows The new number of rows for the surface.
103 * @param cols The new number of columns for the surface.
104 * @return 0 on success, or -1 on failure.
105 */
106int ui_surface_resize(UiSurface *s, int rows, int cols) {
107 if (!s)
108 return -1;
109 s->rows = rows;
110 s->cols = cols;
111 return wresize(s->win, rows, cols);
112}
113
114/** @brief Clear the contents of a UI surface.
115 * @ingroup ui_ncurses
116 * @param s The UiSurface to clear.
117 * @return 0 on success, or -1 on failure.
118 */
119int ui_surface_clear(UiSurface *s) {
120 if (!s)
121 return -1;
122 wclear(s->win);
123 return 0;
124}
125
126/** @brief Erase the contents of a UI surface.
127 * @ingroup ui_ncurses
128 * @param s The UiSurface to erase.
129 * @return 0 on success, or -1 on failure.
130 */
131int ui_surface_erase(UiSurface *s) {
132 if (!s)
133 return -1;
134 werase(s->win);
135 return 0;
136}
137
138/** @brief Show a UI surface.
139 * @ingroup ui_ncurses
140 * @param s The UiSurface to show.
141 * @return 0 on success, or -1 on failure.
142 */
143int ui_surface_show(UiSurface *s) {
144 if (!s)
145 return -1;
146 show_panel(s->pan);
147 s->hidden = false;
148 return 0;
149}
150/** @brief Hide a UI surface.
151 * @ingroup ui_ncurses
152 * @param s The UiSurface to hide.
153 * @return 0 on success, or -1 on failure.
154 */
155int ui_surface_hide(UiSurface *s) {
156 if (!s)
157 return -1;
158 hide_panel(s->pan);
159 s->hidden = true;
160 return 0;
161}
162
163/** @brief Move the cursor within a UI surface.
164 * @ingroup ui_ncurses
165 * @param s The UiSurface to move the cursor in.
166 * @param y The new y-coordinate of the cursor.
167 * @param x The new x-coordinate of the cursor.
168 * @return 0 on success, or -1 on failure.
169 */
170int ui_cursor_move(UiSurface *s, int y, int x) {
171 if (!s)
172 return -1;
173 return wmove(s->win, y, x);
174}
175/** @brief Initialize NCUrses
176 * @ingroup ui_ncurses
177 * @param cfg The UI runtime instance.
178 * @return 0 on success, or -1 on failure.
179 */
180UiRuntime *ui_init(const UiConfig *cfg) {
181 UiRuntime *ui = calloc(1, sizeof(*ui));
182 if (!ui)
183 return NULL;
184 char tty_name[XLEN];
185
186 // Get the name of the terminal device
187 if (ttyname_r(STDERR_FILENO, tty_name, sizeof(tty_name)) != 0) {
188 Perror("ui_init: ttyname_r() failed");
189 exit(EXIT_FAILURE);
190 }
191 // open the terminal device for NCurses input and output
192 tty_fp = fopen(tty_name, "r+");
193 if (tty_fp == nullptr) {
194 Perror("ui_init: fopen() failed");
195 exit(EXIT_FAILURE);
196 }
197 // newterm() allows us to specify a tty device for NCurses input and
198 // output. nullptr for the first argument means to use the default terminal type.
199 screen = newterm(nullptr, tty_fp, tty_fp);
200 if (screen == nullptr) {
201 Perror("ui_init: newterm() failed");
202 exit(EXIT_FAILURE);
203 }
204 set_term(screen);
205 f_curses_open = true;
206 if (!has_colors()) {
208 Perror("Terminal does not support colors");
209 exit(EXIT_FAILURE);
210 }
211 if (!can_change_color()) {
213 Perror("Terminal cannot change colors");
214 exit(EXIT_FAILURE);
215 }
216 start_color();
217 use_default_colors();
218 cbreak();
219 noecho();
220 keypad(stdscr, TRUE);
221 if (cfg) {
225 } else {
226 ui->cursor_visible = true;
227 }
228 if (ui->mouse_enabled)
229 mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
230 curs_set(ui->cursor_visible ? 1 : 0);
231 getmaxyx(stdscr, ui->rows, ui->cols);
232 win_ptr = -1;
233 return ui;
234}
235
236/** @brief Shutdown the UI runtime and free its resources.
237 * @ingroup ui_ncurses
238 * @param ui The UiRuntime instance to shutdown.
239 */
240void ui_shutdown(UiRuntime *ui) {
241 if (!ui)
242 return;
243 // ui_surface_destroy (ui->surface[0]);
244 endwin();
245 delscreen(screen);
246 fclose(tty_fp);
247 f_curses_open = false;
248 free(ui);
249}
250
251/** @brief Get the current screen size.
252 * @ingroup ui_ncurses
253 * @param ui The UiRuntime instance.
254 * @param rows Output parameter for the number of rows.
255 * @param cols Output parameter for the number of columns.
256 */
257void ui_get_screen_size(UiRuntime *ui, int *rows, int *cols) {
258 if (!ui)
259 return;
260 getmaxyx(stdscr, ui->rows, ui->cols);
261 if (rows)
262 *rows = ui->rows;
263 if (cols)
264 *cols = ui->cols;
265}
266
267/** @brief Render the UI by updating panels and refreshing the screen.
268 * @ingroup ui_ncurses
269 * @param ui The UiRuntime instance.
270 * @return 0 on success, or -1 on failure.
271 */
272int ui_render(UiRuntime *ui) {
273 (void)ui;
274 update_panels();
275 doupdate();
276 return 0;
277}
278
279/** @brief Clear the entire screen.
280 * @ingroup ui_ncurses
281 * @param ui The UiRuntime instance.
282 * @return 0 on success, or -1 on failure.
283 */
284int ui_clear_screen(UiRuntime *ui) {
285 (void)ui;
286 erase();
287 return 0;
288}
289
290/** @brief Suspend the UI, restoring the terminal to its normal state.
291 * @ingroup ui_ncurses
292 * @param ui The UiRuntime instance.
293 * @return 0 on success, or -1 on failure.
294 */
295int ui_suspend(UiRuntime *ui) {
296 (void)ui;
297 def_prog_mode();
298 endwin();
299 return 0;
300}
301
302/** @brief Resume the UI after being suspended, reinitializing the screen.
303 * @ingroup ui_ncurses
304 * @param ui The UiRuntime instance.
305 * @return 0 on success, or -1 on failure.
306 */
307int ui_resume(UiRuntime *ui) {
308 (void)ui;
309 reset_prog_mode();
310 refresh();
311 update_panels();
312 doupdate();
313 return 0;
314}
315
316/** @brief Enable or disable the cursor visibility.
317 * @ingroup ui_ncurses
318 * @param ui The UiRuntime instance.
319 * @param visible true to show the cursor, false to hide it.
320 * @return 0 on success, or -1 on failure.
321 */
322int ui_cursor_enable(UiRuntime *ui, bool visible) {
323 if (!ui)
324 return -1;
325 ui->cursor_visible = visible;
326 curs_set(visible ? 1 : 0);
327 return 0;
328}
329
330int ui_bkgrnd(UiSurface *s, const UiStyle *style, const char *c) {
331 if (!s)
332 return -1;
333 cchar_t cch = ui_style_to_cch(style, c);
334 wbkgrnd(s->win, &cch);
335 return 0;
336}
337
338int ui_bkgrnd_set(UiSurface *s, const UiStyle *style, const char *c) {
339 if (!s)
340 return -1;
341 cchar_t cch = ui_style_to_cch(style, c);
342 wbkgrndset(s->win, &cch);
343 return 0;
344}
345UiStyle *ui_style_from_cch(const cchar_t *cch) {
346 UiStyle *style = calloc(1, sizeof(*style));
347 if (!style)
348 return NULL;
349 wchar_t wc[2] = {L'\0', L'\0'};
350 attr_t attrs;
351 short cpx;
352 int fg, bg;
353 RGB rgb;
354 getcchar(cch, wc, &attrs, &cpx, nullptr);
355 style->bold = (attrs & WA_BOLD) != 0;
356 style->dim = (attrs & WA_DIM) != 0;
357 style->italic = (attrs & WA_ITALIC) != 0;
358 style->underline = (attrs & WA_UNDERLINE) != 0;
359 style->blink = (attrs & WA_BLINK) != 0;
360 style->reverse = (attrs & WA_REVERSE) != 0;
361 style->invis = (attrs & WA_INVIS) != 0;
362 extended_pair_content(cpx, &fg, &bg);
363 extended_color_content(fg, &rgb.r, &rgb.g, &rgb.b);
364 style->fg.r = (rgb.r * 255) / 1000;
365 style->fg.g = (rgb.g * 255) / 1000;
366 style->fg.b = (rgb.b * 255) / 1000;
367 extended_color_content(bg, &rgb.r, &rgb.g, &rgb.b);
368 style->bg.r = (rgb.r * 255) / 1000;
369 style->bg.g = (rgb.g * 255) / 1000;
370 style->bg.b = (rgb.b * 255) / 1000;
371 return style;
372}
373
374UiStyle *ui_style_new() {
375 UiStyle *style = calloc(1, sizeof(*style));
376 if (!style)
377 return NULL;
378 style->fg.r = 255;
379 style->fg.g = 255;
380 style->fg.b = 255;
381 style->bg.r = 0;
382 style->bg.g = 0;
383 style->bg.b = 0;
384 return style;
385}
386
387void ui_style_destroy(UiStyle *style) {
388 if (!style)
389 return;
390 free(style);
391}
392
393cchar_t ui_style_to_cch(const UiStyle *style, const char *c) {
394 int fg, bg;
395 attr_t attrs = 0;
396 uint32_t cpx = 0;
397 mbstate_t mbstate;
398 memset(&mbstate, 0, sizeof(mbstate));
399 attrs |= style->bold ? WA_BOLD : 0;
400 attrs |= style->dim ? WA_DIM : 0;
401 attrs |= style->italic ? WA_ITALIC : 0;
402 attrs |= style->underline ? WA_UNDERLINE : 0;
403 attrs |= style->blink ? WA_BLINK : 0;
404 attrs |= style->reverse ? WA_REVERSE : 0;
405 attrs |= style->invis ? WA_INVIS : 0;
406 RGB rgb = (RGB){style->fg.r, style->fg.g, style->fg.b};
407 fg = rgb_to_curses_clr(&rgb);
408 rgb = (RGB){style->bg.r, style->bg.g, style->bg.b};
409 bg = rgb_to_curses_clr(&rgb);
410 cpx = get_clr_pair(fg, bg);
411 const cchar_t cch = mkcc(cpx, attrs, c);
412 return cch;
413}
#define MAXWIN
Definition cm.h:37
bool f_curses_open
Definition sig.c:33
SCREEN * screen
Definition dwin.c:42
int win_ptr
Definition dwin.c:164
FILE * tty_fp
Definition dwin.c:43
UiSurface * ui_surface_win[MAXWIN]
Definition ui_ncurses.c:27
UiSurface * ui_surface_box[MAXWIN]
Definition ui_ncurses.c:26
UiSurface * ui_surface_win2[MAXWIN]
Definition ui_ncurses.c:28
int ui_bkgrnd(UiSurface *, const UiStyle *, const char *)
Definition ui_ncurses.c:330
int ui_bkgrnd_set(UiSurface *, const UiStyle *, const char *)
Definition ui_ncurses.c:338
void ui_style_destroy(UiStyle *)
Definition ui_ncurses.c:387
#define XLEN
UiStyle * ui_style_from_cch(const cchar_t *)
Definition ui_ncurses.c:345
UiStyle * ui_style_new()
Definition ui_ncurses.c:374
cchar_t ui_style_to_cch(const UiStyle *, const char *)
Definition ui_ncurses.c:393
#define TRUE
Definition iloan.c:19
int rgb_to_curses_clr(RGB *)
Get color index for RGB color.
Definition dwin.c:432
cchar_t mkcc(int, attr_t, const char *)
Create a cchar_t with the specified color pair index.
Definition dwin.c:748
int get_clr_pair(int, int)
Get color pair index for foreground and background colors.
Definition dwin.c:401
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1526
UiSurface * ui_surface_new(UiRuntime *ui, UiSurface *parent, UiRect rect)
Create a new UI surface.
Definition ui_ncurses.c:37
int ui_surface_hide(UiSurface *s)
Hide a UI surface.
Definition ui_ncurses.c:155
int ui_surface_clear(UiSurface *s)
Clear the contents of a UI surface.
Definition ui_ncurses.c:119
void ui_get_screen_size(UiRuntime *ui, int *rows, int *cols)
Get the current screen size.
Definition ui_ncurses.c:257
int ui_render(UiRuntime *ui)
Render the UI by updating panels and refreshing the screen.
Definition ui_ncurses.c:272
void ui_surface_destroy(UiSurface *s)
Destroy a UI surface and free its resources.
Definition ui_ncurses.c:74
int ui_surface_show(UiSurface *s)
Show a UI surface.
Definition ui_ncurses.c:143
void ui_shutdown(UiRuntime *ui)
Shutdown the UI runtime and free its resources.
Definition ui_ncurses.c:240
int ui_clear_screen(UiRuntime *ui)
Clear the entire screen.
Definition ui_ncurses.c:284
int ui_surface_erase(UiSurface *s)
Erase the contents of a UI surface.
Definition ui_ncurses.c:131
UiRuntime * ui_init(const UiConfig *cfg)
Initialize NCUrses.
Definition ui_ncurses.c:180
int ui_cursor_move(UiSurface *s, int y, int x)
Move the cursor within a UI surface.
Definition ui_ncurses.c:170
int ui_suspend(UiRuntime *ui)
Suspend the UI, restoring the terminal to its normal state.
Definition ui_ncurses.c:295
int ui_resume(UiRuntime *ui)
Resume the UI after being suspended, reinitializing the screen.
Definition ui_ncurses.c:307
int ui_surface_move(UiSurface *s, int y, int x)
Move a UI surface to a new position.
Definition ui_ncurses.c:91
int ui_surface_resize(UiSurface *s, int rows, int cols)
Resize a UI surface to new dimensions.
Definition ui_ncurses.c:106
int ui_cursor_enable(UiRuntime *ui, bool visible)
Enable or disable the cursor visibility.
Definition ui_ncurses.c:322
int r
Definition cm.h:382
int b
Definition cm.h:382
int g
Definition cm.h:382
bool dim
Definition ui_backend.h:162
UiColor bg
Definition ui_backend.h:160
bool underline
Definition ui_backend.h:164
bool blink
Definition ui_backend.h:165
UiColor fg
Definition ui_backend.h:159
bool invis
Definition ui_backend.h:167
bool italic
Definition ui_backend.h:163
bool reverse
Definition ui_backend.h:166
bool bold
Definition ui_backend.h:161
int cols
Definition ui_backend.h:200
int rows
Definition ui_backend.h:199
bool cursor_visible
Definition ui_backend.h:214
bool enable_mouse
Definition ui_backend.h:212
bool enable_alt_screen
Definition ui_backend.h:213
struct UiSurface * parent
struct UiRuntime * runtime