C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
menu_engine.c
Go to the documentation of this file.
1/** @file menu_engine.c
2 @brief The working part of C-Menu Menu
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10/** @defgroup menu_engine Menu Engine
11 @brief Main loop and command processor for the C-Menu menu system
12 @details This module contains the core functionality for displaying menus,
13 processing user input, and executing commands associated with menu choices.
14 It handles navigation through menu options, responding to special keys,
15 and managing the display of submenus, pick lists, forms, and views.
16 */
17
18#include "common.h"
19#include <stdlib.h>
20#include <string.h>
21#include <termios.h>
22#include <unistd.h>
23
24unsigned int menu_engine(Init *);
25unsigned int menu_cmd_processor(Init *);
26
27/** @brief The main loop of the menu system.
28 @ingroup menu_engine
29 @param init A pointer to an Init structure containing initialization data for
30 the menu system.
31 @returns an integer indicating the action taken by the user, such as
32 returning to the main menu or exiting the menu system.
33 @details displays the menu and processes user input until the user exits the
34 menu or returns to the main menu.
35 */
36unsigned int menu_engine(Init *init) {
37 uint action;
38 uint i;
39 char tmp_str[MAXLEN];
40 Menu *menu = init->menu;
41 if (menu == nullptr) {
42 ui_perror(_("menu_engine: menu is nullptr"));
43 return (1);
44 }
45 menu->lines = 0;
46 menu->cols = 0;
47 menu->line_idx = 0;
48 menu->item_count = 0;
49 menu->title[0] = '\0';
50 menu->choice_max_len = 0;
51 menu->text_max_len = 0;
54 UiSurface *sfc = ui_surface[sfc_ptr];
55 if (sfc == nullptr) {
56 ssnprintf(tmp_str, MAXLEN - 1, "ui_tracked_sfc_box(%d, %d, %d, %d, %s) failed",
57 menu->lines, menu->cols, menu->begy, menu->begx, menu->title);
58 ui_perror(tmp_str);
59 return 1;
60 }
61 mbstate_t mbstate;
62 memset(&mbstate, 0, sizeof(mbstate));
63 action = MA_DISPLAY_MENU;
64 while (action) {
65 switch (action) {
66 case MA_RETURN:
68 if (sfc_ptr < 0) {
70 return 0;
71 }
73 return 0;
74 case MA_DISPLAY_MENU:
75 for (menu->line_idx = 0; menu->line_idx < menu->item_count;
76 menu->line_idx++) {
77 ui_bkgdset(sfc, WIN, &cell_nt);
80 menu->cols);
85 ui_bkgdset(sfc, WIN, &cell_nt);
87 }
88 action = MA_RESET_MENU;
89 break;
90 case MA_RESET_MENU:
91 menu->line_idx = 0;
92 for (i = 0; i < menu->item_count; i++) {
93 if (menu->line[i]->type == MT_CHOICE) {
94 menu->line_idx = i;
95 break;
96 }
97 }
98 action = MA_CONTINUE;
99 break;
100 case MA_CONTINUE:
101 action = menu_cmd_processor(init);
102 break;
103 default:
104 break;
105 }
106 }
107 destroy_menu(init);
108 return 0;
109}
110/** @brief Processes user input for the menu system.
111 @ingroup menu_engine
112 @param init A pointer to an Init structure containing initialization data
113 for the menu system.
114 @returns an integer indicating the action taken by the user, such as
115 returning to the main menu, displaying a submenu, or executing a command
116 associated with a menu choice.
117 @details handles navigation through the menu options, executing commands
118 associated with menu choices, and responding to special keys such as
119 function keys and mouse clicks.
120 */
121unsigned int menu_cmd_processor(Init *init) {
122 uint i;
123 uint c;
124 char *d;
125 int in_key;
126 char tmp_str[MAXLEN];
127 char earg_str[MAXLEN];
128 char *eargv[MAXARGS];
129 int eargc;
130 UiSurface *sfc = ui_surface[sfc_ptr];
131 mbstate_t mbstate;
132 memset(&mbstate, 0, sizeof(mbstate));
133 Menu *menu = init->menu;
134 ui_keypad(sfc, WIN, true);
135 ui_scrollok(sfc, WIN, false);
136#ifdef UAL_UI
137 ui_mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION);
138#else
139 ui_mice_enable(NCMICE_ALL_EVENTS);
140#endif
141 UiEvent event;
142 // Highlight the currently selected menu choice
143
144 while (1) {
148 menu->cols);
153 ui_bkgdset(sfc, WIN, &cell_nt);
154 // Wait for user input and process it
155 event.y = event.x = -1;
156 ui_wmove(sfc, WIN, menu->line_idx, 1);
158 in_key = ui_get_event(sfc, WIN, NULL, &event, -1);
160 if (event.y < menu->item_count)
161 in_key = menu->line[event.y]->choice_letter;
162 ui_bkgdset(sfc, WIN, &cell_nt);
165 menu->cols);
171 // Initialize the mouse event coordinates to -1 to indicate no mouse
172 // event
173 switch (in_key) {
174 /** Move up to the previous menu choice */
175 case UIKEY_UP:
176 i = menu->line_idx;
177 while (i > 0) {
178 i--;
179 if (menu->line[i]->type == MT_CHOICE) {
180 menu->line_idx = i;
181 break;
182 }
183 }
184 return (MA_CONTINUE);
185 /** Move down to the next menu choice */
186 case UIKEY_DOWN:
187 i = menu->line_idx;
188 while (i < menu->item_count - 1) {
189 i++;
190 if (menu->line[i]->type == MT_CHOICE) {
191 menu->line_idx = i;
192 break;
193 }
194 }
195 return (MA_CONTINUE);
196 /** Select the current menu choice and execute its associated
197 * command */
198 case '\n':
199 case UIKEY_ENTER:
200 break;
201 /** @brief Display help information for the menu system */
202 case '?':
203 case UIKEY_F01:
204 if (menu->f_help_spec && menu->help_spec[0] != '\0')
205 strnz__cpy(tmp_str, menu->help_spec, MAXLEN - 1);
206 else {
207 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
208 strnz__cat(tmp_str, "/", MAXLEN - 1);
210 }
211 eargc = 0;
212 eargv[eargc++] = strdup("view");
213 eargv[eargc++] = strdup("-Nf");
214 eargv[eargc++] = strdup(tmp_str);
215 eargv[eargc] = nullptr;
216 init->lines = 51;
217 init->cols = 86;
218 init->begy = menu->begy + 1;
219 init->begx = menu->begx + 1;
220 strnz__cpy(init->title, "Menu Help", MAXLEN - 1);
221 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
222 init->begx);
223 destroy_argv(eargc, eargv);
224 return (MA_DISPLAY_MENU);
225 /** Exit the menu and return to the previous menu or exit if at top
226 */
227 case UIKEY_F09:
228 return (MA_RETURN);
229 /** @brief send default printer output file to printer */
230 case UIKEY_F07:
231 d = getenv("PRTCMD");
232 if (d == nullptr || *d == '\0')
233 strnz__cpy(earg_str, PRINTCMD, MAXLEN - 1);
234 else
235 strnz__cpy(earg_str, d, MAXLEN - 1);
236 strnz__cat(earg_str, " ", MAXLEN - 1);
237 d = getenv("PRTFILE");
238 if (d == nullptr || *d == '\0') {
239 d = getenv("HOME");
240 if (d == nullptr || *d == '\0')
242 else {
243 strnz__cat(earg_str, d, MAXLEN - 1);
244 strnz__cat(earg_str, "/", MAXLEN - 1);
246 }
247 } else
248 strnz__cat(earg_str, d, MAXLEN - 1);
249 full_screen_shell(earg_str);
250 return (MA_RESET_MENU);
251 /** @brief open the default editor */
252 case UIKEY_F08:
254 return (MA_DISPLAY_MENU);
255 /** @brief process mouse event */
257 if (event.y >= menu->item_count)
258 return (MA_CONTINUE);
259 menu->line_idx = event.y;
260 break;
261 case 'q':
262 return (MA_RETURN);
263 break;
264 default:
265 /** @brief If the user presses a key that corresponds to a menu
266 * choice's letter, select that menu choice */
267 for (i = 0; i < menu->item_count; i++) {
268 if (menu->line[i]->raw_text[0] == '_') {
269 if (menu->line[i]->choice_letter == (char)in_key) {
270 menu->line_idx = i;
271 break;
272 }
273 } else {
274 if (menu->line[i]->choice_letter == (char)in_key) {
275 menu->line_idx = i;
276 break;
277 }
278 }
279 }
280 if (i >= menu->item_count)
281 return (MA_CONTINUE);
282 break;
283 }
284 char *s;
285 c = (int)menu->line[menu->line_idx]->command_type;
286 switch (c) {
287 /** @brief Execute the command associated with the selected menu
288 * choice
289 */
290 case CT_DEXE:
291 s = strpbrk(menu->line[menu->line_idx]->command_str, " \t\f\v");
292 strnz__cpy(earg_str, s, MAXLEN - 1);
293 trim(earg_str);
294 eargc = str_to_args(eargv, s, MAX_ARGS);
296 destroy_argv(eargc, eargv);
297 return (MA_RESET_MENU);
298 case CT_EXEC:
299 s = strpbrk(menu->line[menu->line_idx]->command_str, " \t\f\v");
300 strnz__cpy(earg_str, s, MAXLEN - 1);
301 trim(earg_str);
302 eargc = str_to_args(eargv, s, MAX_ARGS);
303 stdio_names(stdio_names_str, "menu_engine.c:328");
305 destroy_argv(eargc, eargv);
306 return (MA_RESET_MENU);
307 /** @brief Display help information for the menu system */
308 case CT_HELP:
309 if (menu->f_help_spec && menu->help_spec[0] != '\0')
310 strnz__cpy(tmp_str, menu->help_spec, MAXLEN - 1);
311 else {
312 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
313 strnz__cat(tmp_str, "/", MAXLEN - 1);
315 }
316 eargc = 0;
317 eargv[eargc++] = strdup("view");
318 eargv[eargc++] = strdup("-N");
319 eargv[eargc++] = strdup("f");
320 eargv[eargc++] = strdup(tmp_str);
321 eargv[eargc] = nullptr;
322 init->lines = 40;
323 init->cols = 60;
324 init->begy = menu->begy + 1;
325 init->begx = menu->begx + 1;
326 strnz__cpy(init->title, "Menu Help", MAXLEN - 1);
327 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
328 init->begx);
329 destroy_argv(eargc, eargv);
330 return (MA_DISPLAY_MENU);
331 /** @brief Display a submenu or perform an action associated with
332 * the selected menu choice */
333 case CT_MENU:
335 MAXLEN - 1);
336 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
337 if (eargc == 0)
338 return (MA_DISPLAY_MENU);
339 popup_menu(init, eargc, eargv, menu->begy + 1, menu->begx + 1);
340 destroy_argv(eargc, eargv);
341 return (MA_RESET_MENU);
342 /** @brief Display a pick list or form associated with the selected
343 * menu choice */
344 case CT_PICK:
346 MAXLEN - 1);
347 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
348 popup_pick(init, eargc, eargv, menu->begy + 1, menu->begx + 1);
349 destroy_argv(eargc, eargv);
350 return (MA_DISPLAY_MENU);
351 /** @brief Display a form associated with the selected menu choice
352 */
353 case CT_FORM:
355 MAXLEN - 1);
356 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
357 popup_form(init, eargc, eargv, menu->begy + 1, menu->begx + 1);
358 destroy_argv(eargc, eargv);
359 return (MA_RESET_MENU);
360 /** @brief Display a view associated with the selected menu choice
361 */
362 case CT_VIEW:
364 MAXLEN - 1);
365 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
366 init->lines = 66;
367 init->cols = 80;
368 init->begy = menu->begy + 1;
369 init->begx = menu->begx + 1;
371 MAXLEN - 1);
372 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
373 init->begx);
374 destroy_argv(eargc, eargv);
375 return (MA_DISPLAY_MENU);
376 /** @brief open ckeys (test curses keys) */
377 case CT_CKEYS:
379 return (MA_DISPLAY_MENU);
380 /** @brief return to calling program */
381 case CT_RETURN:
382 return (MA_RETURN);
383 /** @brief write the current menu configuration to a file */
384 case CT_WRITE_CONFIG:
385 write_config(init);
386 return (MA_CONTINUE);
387 default:
388 return (MA_CONTINUE);
389 }
390 }
391 return 0;
392}
#define PRINTCMD
Definition common.h:50
#define VIEW_PRT_FILE
Definition common.h:39
int popup_form(Init *, int, char **, uint, uint)
instantiate a form popup window
Definition popups.c:113
int popup_menu(Init *, int, char **, uint, uint)
instantiate a menu popup window
Definition popups.c:53
#define MENU_HELP_FILE
Definition common.h:35
int popup_pick(Init *, int, char **, uint, uint)
instantiate a pick popup window
Definition popups.c:83
int popup_view(Init *, int, char **, uint, uint, uint, uint)
instantiate a view popup window
Definition popups.c:145
#define MAX_ARGS
Definition cm.h:62
char stdio_names_str[4096]
Definition futil.c:122
#define MAXARGS
Definition cm.h:64
char * stdio_names(char *, char *)
Definition futil.c:1083
@ MT_CHOICE
Definition menu.h:23
@ CT_PICK
Definition menu.h:45
@ CT_FORM
Definition menu.h:41
@ CT_DEXE
Definition menu.h:37
@ CT_MENU
Definition menu.h:44
@ CT_RETURN
Definition menu.h:48
@ CT_HELP
Definition menu.h:39
@ CT_WRITE_CONFIG
Definition menu.h:50
@ CT_EXEC
Definition menu.h:38
@ CT_VIEW
Definition menu.h:46
@ CT_CKEYS
Definition menu.h:47
@ MA_RETURN
Definition menu.h:28
@ MA_DISPLAY_MENU
Definition menu.h:29
@ MA_RESET_MENU
Definition menu.h:30
@ MA_CONTINUE
Definition menu.h:31
int ui_draw_ch_yx(UiSurface *s, ss_t w, uint y, uint x, const char c)
Draw a character at a specific position in a surface window.
void ui_render()
Render the UI by updating panels and refreshing the display.
Definition ui_ncurses.c:884
int ui_draw_text_fill(UiSurface *s, ss_t w, uint y, uint x, const char *text, int m)
Draw a text string at a specific position in a surface window, filling the remaining space with space...
UiCell cell_nt
Definition ui_common.c:106
UiCell cell_nt_hl_rev
Definition ui_common.c:109
int ui_wmove(UiSurface *s, ss_t w, uint y, uint x)
Move the cursor to a specified position within a UiSurface.
Definition ui_ncurses.c:653
int ui_cm_surface_destroy(UiSurface *sfc)
Destroy the most recently created surface.
Definition ui_common.c:466
void ui_restore_wins()
Restore the state of all windows in the UI. This function refreshes the standard screen and all surfa...
UiCell cell_nt_hl
Definition ui_common.c:108
int ui_mousemask(int mask)
Enable or disable mouse support.
int sfc_ptr
Definition ui_ncurses.c:42
int ui_tracked_sfc_box(uint wlines, uint wcols, uint wbegy, uint wbegx, const char *wtitle)
Create a new surface with a box window.
Definition ui_common.c:387
int ui_scrollok(UiSurface *s, ss_t w, bool enable)
Enable or disable scrolling for a UiSurface.
Definition ui_ncurses.c:755
int ui_bkgdset(UiSurface *s, ss_t w, const UiCell *cell)
Set the background for new content to be written to a specific window within a UiSurface.
Definition ui_ncurses.c:941
UiCell cell_nt_rev
Definition ui_common.c:107
int ui_keypad(UiSurface *s, ss_t w, bool enable)
Enable or disable the keypad for a UiSurface.
Definition ui_ncurses.c:771
#define UIKEY_F09
#define UIKEY_F07
#define UIKEY_F01
#define UIKEY_UP
#define UIKEY_BUTTON1_CLICKED
#define UIKEY_ENTER
#define UIKEY_F08
#define UIKEY_DOWN
int ui_get_event(UiSurface *s, ss_t w, UiChyron *chyron, UiEvent *ev, int timeout_ms)
Read input events.
UiSurface * ui_surface[UI_SFC_MAX]
Definition ui_ncurses.c:40
int ui_mvwaddch(UiSurface *s, ss_t w, uint y, uint x, const char c)
Draw a character in a surface window.
int popup_ckeys()
Definition curskeys.c:31
#define MAXLEN
Definition curskeys.c:15
#define _(String)
Definition amort.c:23
int full_screen_fork_exec(char **)
Execute a command in full screen mode.
Definition exec.c:45
int full_screen_shell(char *)
Execute a shell command in full screen mode.
Definition exec.c:58
int fork_detach_execvp(char **eargv)
Fork, set new session ID, close files, and execute detached command.
Definition detach.c:41
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition whence.c:291
size_t trim(char *)
Trims leading and trailing spaces from string s in place.
Definition futil.c:361
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:390
int str_to_args(char **, char *, uint)
Converts a string into an array of argument strings.
Definition futil.c:412
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition whence.c:320
int destroy_argv(uint argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:468
int write_config(Init *init)
Write the current configuration to a file specified in init->minitrc.
Definition init.c:1160
Menu * destroy_menu(Init *init)
Destroy Menu structure.
Definition mem.c:147
unsigned int menu_engine(Init *)
The main loop of the menu system.
Definition menu_engine.c:36
unsigned int menu_cmd_processor(Init *)
Processes user input for the menu system.
unsigned int parse_menu_description(Init *)
Parse menu description file and create Menu.
int ui_perror(char *emsg_str)
Display a simple error message window or print to stderr.
Definition ui_common.c:1257
UiMouseAction mouse_action
Definition ui_backend.h:114
char title[MAXLEN]
Definition common.h:132
char mapp_help[MAXLEN]
Definition common.h:152
int begx
Definition common.h:121
int cols
Definition common.h:119
int begy
Definition common.h:120
Menu * menu
Definition common.h:187
int lines
Definition common.h:118
char * choice_text
Definition menu.h:78
char * raw_text
Definition menu.h:75
char * command_str
Definition menu.h:93
unsigned int type
Definition menu.h:73
uint letter_pos
Definition menu.h:84
char choice_letter
Definition menu.h:81
uint command_type
Definition menu.h:87
Line * line[MAX_MENU_LINES]
Definition menu.h:222
bool f_help_spec
Definition menu.h:181
uint text_max_len
Definition menu.h:209
uint begy
Definition menu.h:117
uint cols
Definition menu.h:115
uint choice_max_len
Definition menu.h:203
char help_spec[MAXLEN]
Definition menu.h:149
uint begx
Definition menu.h:120
uint line_idx
Definition menu.h:218
uint lines
Definition menu.h:113
uint item_count
Definition menu.h:215
char title[MAXLEN]
Definition menu.h:134