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 <ctype.h>
20#include <ncursesw/ncurses.h>
21#include <stdlib.h>
22#include <string.h>
23#include <termios.h>
24#include <unistd.h>
25
26unsigned int menu_engine(Init *);
27unsigned int menu_cmd_processor(Init *);
28
29/** @brief The main loop of the menu system.
30 @ingroup menu_engine
31 @param init A pointer to an Init structure containing initialization data for
32 the menu system.
33 @returns an integer indicating the action taken by the user, such as
34 returning to the main menu or exiting the menu system.
35 @details displays the menu and processes user input until the user exits the
36 menu or returns to the main menu.
37 */
38unsigned int menu_engine(Init *init) {
39 int action;
40 int i;
41 char tmp_str[MAXLEN];
42 Menu *menu = init->menu;
43 if (menu == nullptr) {
44 Perror("menu_engine: menu is nullptr");
45 return (1);
46 }
47 menu->lines = 0;
48 menu->cols = 0;
49 menu->line_idx = 0;
50 menu->item_count = 0;
51 menu->title[0] = '\0';
52 menu->choice_max_len = 0;
53 menu->text_max_len = 0;
55 if (box_new(menu->lines, menu->cols, menu->begy, menu->begx, menu->title)) {
56 ssnprintf(tmp_str, MAXLEN - 1, "box_new(%d, %d, %d, %d, %s) failed",
57 menu->lines, menu->cols, menu->begy, menu->begx, menu->title);
58 Perror(tmp_str);
59 return 1;
60 }
61
62 scrollok(win_win[win_ptr], false);
63 int len;
64 mbstate_t mbstate;
65 memset(&mbstate, 0, sizeof(mbstate));
66 cchar_t cc = {0};
67
68 action = MA_DISPLAY_MENU;
69 while (action) {
70 switch (action) {
71 case MA_RETURN:
73 if (win_ptr < 0) {
75 return 0;
76 }
78 return 0;
79 case MA_DISPLAY_MENU:
80 for (menu->line_idx = 0; menu->line_idx < menu->item_count;
81 menu->line_idx++) {
82 // mvwaddstr(win_win[win_ptr], menu->line_idx, 0, menu->line[menu->line_idx]->choice_text);
84 menu->line_idx,
85 0,
87 menu->cols - 2);
88 // Highlight the letter of the menu choice
89 wchar_t wstr[2] = {L'\0', L'\0'};
90 len = mbrtowc(wstr, &menu->line[menu->line_idx]->choice_letter,
91 MB_CUR_MAX, &mbstate);
92 if (len < 0) {
93 wstr[0] = L'?';
94 wstr[1] = L'\0';
95 }
96 setcchar(&cc, wstr, WA_NORMAL, cp_nt_hl, nullptr);
97 mvwadd_wch(win_win[win_ptr], menu->line_idx,
98 menu->line[menu->line_idx]->letter_pos, &cc);
99 }
100 action = MA_RESET_MENU;
101 break;
102 case MA_RESET_MENU:
103 menu->line_idx = 0;
104 for (i = 0; i < menu->item_count; i++) {
105 if (menu->line[i]->type == MT_CHOICE) {
106 menu->line_idx = i;
107 break;
108 }
109 }
110 action = MA_CONTINUE;
111 break;
112 case MA_CONTINUE:
113 action = menu_cmd_processor(init);
114 break;
115 default:
116 break;
117 }
118 }
119 destroy_menu(init);
120 return 0;
121}
122/** @brief Processes user input for the menu system.
123 @ingroup menu_engine
124 @param init A pointer to an Init structure containing initialization data
125 for the menu system.
126 @returns an integer indicating the action taken by the user, such as
127 returning to the main menu, displaying a submenu, or executing a command
128 associated with a menu choice.
129 @details handles navigation through the menu options, executing commands
130 associated with menu choices, and responding to special keys such as
131 function keys and mouse clicks.
132 */
133unsigned int menu_cmd_processor(Init *init) {
134 int i, c;
135 int len;
136 char *d;
137 int in_key;
138 char tmp_str[MAXLEN];
139 char earg_str[MAXLEN];
140 char *eargv[MAXARGS];
141 int eargc;
142 mbstate_t mbstate;
143 memset(&mbstate, 0, sizeof(mbstate));
144 cchar_t cc = {0};
145 Menu *menu = init->menu;
146 keypad(win_win[win_ptr], TRUE);
147 mousemask(BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED, nullptr);
148 MEVENT event;
149 scrollok(win_win[win_ptr], FALSE);
150
151 // Highlight the currently selected menu choice
152
153 while (1) {
154 wbkgrndset(win_win[win_ptr], &CC_NT_REV);
157 wbkgrndset(win_win[win_ptr], &CC_NT);
158 // Highlight the letter of the currently selected menu choice
159 wchar_t wstr[2] = {L'\0', L'\0'};
160 len = mbrtowc(wstr, &menu->line[menu->line_idx]->choice_letter,
161 MB_CUR_MAX, &mbstate);
162 if (len < 0) {
163 wstr[0] = L'?';
164 wstr[1] = L'\0';
165 }
166 setcchar(&cc, wstr, WA_NORMAL, cp_nt_hl_rev, nullptr);
167 mvwadd_wch(win_win[win_ptr], menu->line_idx,
168 menu->line[menu->line_idx]->letter_pos, &cc);
169
170 // Wait for user input and process it
171 event.y = event.x = -1;
172 update_panels();
173 doupdate();
174 wmove(win_win[win_ptr], menu->line_idx, 1);
175 in_key = xwgetch(win_win[win_ptr], nullptr, -1);
176
177 // Remove the highlight from the currently selected menu choice
180 len = mbrtowc(wstr, &menu->line[menu->line_idx]->choice_letter,
181 MB_CUR_MAX, &mbstate);
182 if (len < 0) {
183 wstr[0] = L'?';
184 wstr[1] = L'\0';
185 }
186 setcchar(&cc, wstr, WA_NORMAL, cp_nt_hl, nullptr);
187 mvwadd_wch(win_win[win_ptr], menu->line_idx,
188 menu->line[menu->line_idx]->letter_pos, &cc);
189 // Initialize the mouse event coordinates to -1 to indicate no mouse
190 // event
191 event.y = event.x = -1;
192 switch (in_key) {
193 /** Move up to the previous menu choice */
194 case KEY_UP:
195 i = menu->line_idx;
196 while (i > 0) {
197 i--;
198 if (menu->line[i]->type == MT_CHOICE) {
199 menu->line_idx = i;
200 break;
201 }
202 }
203 return (MA_CONTINUE);
204 /** Move down to the next menu choice */
205 case KEY_DOWN:
206 i = menu->line_idx;
207 while (i < menu->item_count - 1) {
208 i++;
209 if (menu->line[i]->type == MT_CHOICE) {
210 menu->line_idx = i;
211 break;
212 }
213 }
214 return (MA_CONTINUE);
215 /** Select the current menu choice and execute its associated
216 * command */
217 case '\n':
218 case KEY_ENTER:
219 break;
220 /** @brief Display help information for the menu system */
221 case '?':
222 case KEY_F(1):
223 if (menu->f_help_spec && menu->help_spec[0] != '\0')
224 strnz__cpy(tmp_str, menu->help_spec, MAXLEN - 1);
225 else {
226 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
227 strnz__cat(tmp_str, "/", MAXLEN - 1);
229 }
230 eargc = 0;
231 eargv[eargc++] = strdup("view");
232 eargv[eargc++] = strdup("-Nf");
233 eargv[eargc++] = strdup(tmp_str);
234 eargv[eargc] = nullptr;
235 init->lines = 51;
236 init->cols = 86;
237 init->begy = menu->begy + 1;
238 init->begx = menu->begx + 1;
239 strnz__cpy(init->title, "Menu Help", MAXLEN - 1);
240 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
241 init->begx);
242 destroy_argv(eargc, eargv);
243 return (MA_DISPLAY_MENU);
244 /** Exit the menu and return to the previous menu or exit if at top
245 */
246 case KEY_F(9):
247 case KEY_BREAK:
248 return (MA_RETURN);
249 /** @brief send default printer output file to printer */
250 case KEY_ALTF(9):
251 d = getenv("PRTCMD");
252 if (d == nullptr || *d == '\0')
253 strnz__cpy(earg_str, PRINTCMD, MAXLEN - 1);
254 else
255 strnz__cpy(earg_str, d, MAXLEN - 1);
256 strnz__cat(earg_str, " ", MAXLEN - 1);
257 d = getenv("PRTFILE");
258 if (d == nullptr || *d == '\0') {
259 d = getenv("HOME");
260 if (d == nullptr || *d == '\0')
262 else {
263 strnz__cat(earg_str, d, MAXLEN - 1);
264 strnz__cat(earg_str, "/", MAXLEN - 1);
266 }
267 } else
268 strnz__cat(earg_str, d, MAXLEN - 1);
269 full_screen_shell(earg_str);
270 return (MA_RESET_MENU);
271 /** @brief open the default editor */
272 case KEY_ALTF(10):
274 return (MA_DISPLAY_MENU);
275 /** @brief process mouse event */
276 case KEY_MOUSE:
277 if (click_y == -1 || click_x == -1)
278 return (MA_CONTINUE);
279 if (click_y < 0 || click_y >= menu->item_count)
280 return (MA_CONTINUE);
281 menu->line_idx = click_y;
282 break;
283 default:
284 /** @brief If the user presses a key that corresponds to a menu
285 * choice's letter, select that menu choice */
286 if (in_key == 'q')
287 return (MA_RETURN);
288 for (i = 0; i < menu->item_count; i++) {
289 if (menu->line[i]->raw_text[0] == '_') {
290 if (menu->line[i]->choice_letter == (char)in_key) {
291 menu->line_idx = i;
292 break;
293 }
294 } else {
295 if (menu->line[i]->choice_letter == (char)in_key) {
296 menu->line_idx = i;
297 break;
298 }
299 }
300 }
301 if (i >= menu->item_count)
302 return (MA_CONTINUE);
303 break;
304 }
305 char *s;
306 c = (int)menu->line[menu->line_idx]->command_type;
307 switch (c) {
308 /** @brief Execute the command associated with the selected menu
309 * choice
310 */
311 case CT_DEXE:
312 s = strpbrk(menu->line[menu->line_idx]->command_str, " \t\f\v");
313 strnz__cpy(earg_str, s, MAXLEN - 1);
314 trim(earg_str);
315 eargc = str_to_args(eargv, s, MAX_ARGS);
317 destroy_argv(eargc, eargv);
318 return (MA_RESET_MENU);
319 case CT_EXEC:
320 s = strpbrk(menu->line[menu->line_idx]->command_str, " \t\f\v");
321 strnz__cpy(earg_str, s, MAXLEN - 1);
322 trim(earg_str);
323 eargc = str_to_args(eargv, s, MAX_ARGS);
324 stdio_names(stdio_names_str, "menu_engine.c:328");
326 destroy_argv(eargc, eargv);
327 return (MA_RESET_MENU);
328 /** @brief Display help information for the menu system */
329 case CT_HELP:
330 if (menu->f_help_spec && menu->help_spec[0] != '\0')
331 strnz__cpy(tmp_str, menu->help_spec, MAXLEN - 1);
332 else {
333 strnz__cpy(tmp_str, init->mapp_help, MAXLEN - 1);
334 strnz__cat(tmp_str, "/", MAXLEN - 1);
336 }
337 eargc = 0;
338 eargv[eargc++] = strdup("view");
339 eargv[eargc++] = strdup("-N");
340 eargv[eargc++] = strdup("f");
341 eargv[eargc++] = strdup(tmp_str);
342 eargv[eargc] = nullptr;
343 init->lines = 40;
344 init->cols = 60;
345 init->begy = menu->begy + 1;
346 init->begx = menu->begx + 1;
347 strnz__cpy(init->title, "Menu Help", MAXLEN - 1);
348 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
349 init->begx);
350 destroy_argv(eargc, eargv);
351 return (MA_DISPLAY_MENU);
352 /** @brief Display a submenu or perform an action associated with
353 * the selected menu choice */
354 case CT_MENU:
356 MAXLEN - 1);
357 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
358 if (eargc == 0)
359 return (MA_DISPLAY_MENU);
360 popup_menu(init, eargc, eargv, menu->begy + 1, menu->begx + 1);
361 destroy_argv(eargc, eargv);
362 return (MA_RESET_MENU);
363 /** @brief Display a pick list or form associated with the selected
364 * menu choice */
365 case CT_PICK:
367 MAXLEN - 1);
368 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
369 popup_pick(init, eargc, eargv, menu->begy + 1, menu->begx + 1);
370 destroy_argv(eargc, eargv);
371 return (MA_DISPLAY_MENU);
372 /** @brief Display a form associated with the selected menu choice
373 */
374 case CT_FORM:
376 MAXLEN - 1);
377 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
378 popup_form(init, eargc, eargv, menu->begy + 1, menu->begx + 1);
379 destroy_argv(eargc, eargv);
380 return (MA_RESET_MENU);
381 /** @brief Display a view associated with the selected menu choice
382 */
383 case CT_VIEW:
385 MAXLEN - 1);
386 eargc = str_to_args(eargv, earg_str, MAX_ARGS);
387 init->lines = 66;
388 init->cols = 80;
389 init->begy = menu->begy + 1;
390 init->begx = menu->begx + 1;
392 MAXLEN - 1);
393 popup_view(init, eargc, eargv, init->lines, init->cols, init->begy,
394 init->begx);
395 destroy_argv(eargc, eargv);
396 return (MA_DISPLAY_MENU);
397 /** @brief open ckeys (test curses keys) */
398 case CT_CKEYS:
400 return (MA_DISPLAY_MENU);
401 /** @brief return to calling program */
402 case CT_RETURN:
403 return (MA_RETURN);
404 /** @brief write the current menu configuration to a file */
405 case CT_WRITE_CONFIG:
406 write_config(init);
407 return (MA_CONTINUE);
408 default:
409 return (MA_CONTINUE);
410 }
411 }
412 return 0;
413}
#define PRINTCMD
Definition common.h:47
#define VIEW_PRT_FILE
Definition common.h:36
int popup_ckeys()
Display Curses Keys Responds to curses keys and mouse events, displaying the key code and description...
Definition curskeys.c:23
int popup_form(Init *, int, char **, int, int)
instantiate a form popup window
Definition popups.c:112
#define MENU_HELP_FILE
Definition common.h:32
int popup_menu(Init *, int, char **, int, int)
instantiate a menu popup window
Definition popups.c:53
int popup_view(Init *, int, char **, int, int, int, int)
instantiate a view popup window
Definition popups.c:144
int popup_pick(Init *, int, char **, int, int)
instantiate a pick popup window
Definition popups.c:83
int cp_nt_hl
Definition dwin.c:185
WINDOW * win_win[MAXWIN]
Definition dwin.c:52
#define MAX_ARGS
Definition cm.h:46
#define MAXARGS
Definition cm.h:48
cchar_t CC_NT
Definition dwin.c:204
char * stdio_names(char *, char *)
Definition futil.c:1064
int click_x
Definition dwin.c:81
int cp_nt_hl_rev
Definition dwin.c:186
char stdio_names_str[MAXLEN]
Definition futil.c:134
int win_ptr
Definition dwin.c:164
int click_y
Definition dwin.c:80
#define KEY_ALTF(n)
Definition cm.h:503
cchar_t CC_NT_REV
Definition dwin.c:205
@ MT_CHOICE
Definition menu.h:24
@ CT_PICK
Definition menu.h:46
@ CT_FORM
Definition menu.h:42
@ CT_DEXE
Definition menu.h:38
@ CT_MENU
Definition menu.h:45
@ CT_RETURN
Definition menu.h:49
@ CT_HELP
Definition menu.h:40
@ CT_WRITE_CONFIG
Definition menu.h:51
@ CT_EXEC
Definition menu.h:39
@ CT_VIEW
Definition menu.h:47
@ CT_CKEYS
Definition menu.h:48
@ MA_RETURN
Definition menu.h:29
@ MA_DISPLAY_MENU
Definition menu.h:30
@ MA_RESET_MENU
Definition menu.h:31
@ MA_CONTINUE
Definition menu.h:32
#define TRUE
Definition iloan.c:19
#define FALSE
Definition iloan.c:18
#define MAXLEN
Definition curskeys.c:15
int xwgetch(WINDOW *, Chyron *, int)
Wrapper for wgetch that handles signals, mouse events, checks for clicks on the chyron line,...
Definition dwin.c:2094
void restore_wins()
Restore all windows after a screen resize.
Definition dwin.c:1225
void win_del()
Delete the current window and its associated box window.
Definition dwin.c:1179
void mvwaddstr_fill(WINDOW *, int, int, char *, int)
For lines shorter than their display area, fill the rest with spaces.
Definition dwin.c:1941
int box_new(int, int, int, int, char *)
Create a new window with optional box and title.
Definition dwin.c:967
int Perror(char *)
Display a simple error message window or print to stderr.
Definition dwin.c:1526
int full_screen_fork_exec(char **)
Execute a command in full screen mode.
Definition exec.c:46
int full_screen_shell(char *)
Execute a shell command in full screen mode.
Definition exec.c:62
int fork_detach_execvp(char **)
Fork, set new session ID, close files, and execute detached command.
Definition detach.c:29
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:544
int destroy_argv(int argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:494
size_t trim(char *)
Trims leading and trailing spaces from string s in place.
Definition futil.c:391
size_t ssnprintf(char *, size_t, const char *,...)
ssnprintf was designed to be a safer alternative to snprintf.
Definition futil.c:420
size_t strnz__cat(char *, const char *, size_t)
safer alternative to strncat
Definition futil.c:573
int str_to_args(char **, char *, int)
Converts a string into an array of argument strings.
Definition futil.c:440
int write_config(Init *)
Write the current configuration to a file specified in init->minitrc.
Definition init.c:1049
Menu * destroy_menu(Init *init)
Destroy Menu structure.
Definition mem.c:166
unsigned int menu_engine(Init *)
The main loop of the menu system.
Definition menu_engine.c:38
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.
char title[MAXLEN]
Definition common.h:129
char mapp_help[MAXLEN]
Definition common.h:148
int begx
Definition common.h:118
int cols
Definition common.h:116
int begy
Definition common.h:117
Menu * menu
Definition common.h:182
int lines
Definition common.h:115
char * choice_text
Definition menu.h:79
char * raw_text
Definition menu.h:76
char * command_str
Definition menu.h:95
unsigned int type
Definition menu.h:74
int letter_pos
Definition menu.h:85
unsigned int command_type
Definition menu.h:89
char choice_letter
Definition menu.h:82
int cols
Definition menu.h:117
Line * line[MAX_MENU_LINES]
Definition menu.h:219
bool f_help_spec
Definition menu.h:178
int begy
Definition menu.h:119
int text_max_len
Definition menu.h:206
int item_count
Definition menu.h:212
int choice_max_len
Definition menu.h:200
int line_idx
Definition menu.h:215
char help_spec[MAXLEN]
Definition menu.h:146
int lines
Definition menu.h:115
char title[MAXLEN]
Definition menu.h:131
int begx
Definition menu.h:122