C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
init_view.c
Go to the documentation of this file.
1/** @file init_view.c
2 @brief Initialize C-Menu View Screen IO and Input
3 @ingroup init_view
4 @author Bill Waller
5 Copyright (c) 2025
6 MIT License
7 billxwaller@gmail.com
8 @date 2026-02-09
9 */
10
11/**
12 @defgroup init_view Initializing View I/O
13 @brief Populate the C-Menu View Struct and Connect Input
14 */
15#include <common.h>
16#include <errno.h>
17#include <fcntl.h>
18#include <stddef.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/mman.h>
22#include <sys/stat.h>
23#include <sys/sysmacros.h>
24#include <unistd.h>
25#include <wait.h>
26
27/** @brief Initialize C-Menu View in full screen mode.
28 @ingroup init_view
29 @note This function sets up the view structure for full screen mode and
30 creates a new pad for the view.
31 @param init Pointer to the Init structure containing view settings.
32 @return 0 on success, -1 on failure.
33 @verbatim
34 The function creates the following windows:
35 1. view->win: Status or Command Line
36 2. view->ln_win: Line Number Window
37 3. view->pad: Main Content Pad
38 @endverbatim
39 */
40int init_view_full_screen(Init *init) {
41 int scr_lines, scr_cols;
42 view = init->view;
43
44 if (view->tab_stop <= 0)
45 view->tab_stop = TABSIZE;
46 set_tabsize(view->tab_stop);
48 getmaxyx(stdscr, scr_lines, scr_cols);
49 if (view->lines == 0 || view->lines > scr_lines)
50 view->lines = scr_lines;
51 if (view->cols == 0 || view->cols > scr_cols)
52 view->cols = scr_cols;
53 if (view->begy + view->lines > scr_lines)
54 view->begy = scr_lines - view->lines;
55 if (view->begx + view->cols > scr_cols)
56 view->begx = scr_cols - view->cols;
57 /** view->win: status or command line window */
59 keypad(view->win, true);
60 idlok(view->win, false);
61 idcok(view->win, false);
62 wbkgrnd(view->win, &CCC_WIN);
63 wbkgrndset(view->win, &CCC_WIN);
64 scrollok(view->win, false);
65#ifdef NCDEBUG
66 immedok(view->win, true);
67#endif
68
69 /** view->ln_win: line number window */
70 view->ln_win_lines = scr_lines;
73 keypad(view->ln_win, false);
74 idlok(view->ln_win, false);
75 idcok(view->ln_win, false);
76 wbkgrnd(view->ln_win, &CCC_LN);
77 wbkgrndset(view->ln_win, &CCC_LN);
78 scrollok(view->ln_win, true);
79 wsetscrreg(view->ln_win, 0, view->scroll_lines - 1);
80#ifdef NCDEBUG
81 immedok(view->ln_win, true);
82#endif
83
84 /** view->win: status or command line window */
85 view->pminrow = 0;
86 view->pmincol = 0;
87 view->sminrow = 0;
88 view->smincol = 0;
93 view->pad = newpad(view->lines - 1, PAD_COLS);
94 if (view->pad == nullptr) {
95 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
96 ssnprintf(em1, MAXLEN - 1, "newpad(%d, %d) failed", view->lines,
98 em2[0] = '\0';
100 abend(-1, "init_view_full_screen: newpad() failed");
101 }
102 keypad(view->pad, false);
103 idlok(view->pad, false);
104 idcok(view->pad, false);
105 wbkgrnd(view->pad, &CCC_WIN);
106 wbkgrndset(view->pad, &CCC_WIN);
107 scrollok(view->pad, true);
108 wsetscrreg(view->pad, 0, view->scroll_lines - 1);
109#ifdef NCDEBUG
110 immedok(view->pad, true);
111#endif
112 return 0;
113}
114/** @brief Initialize the C-Menu View in box window mode.
115 @ingroup init_view
116 @note sets up the view structure for box window mode, adjusts dimensions
117 based on screen size, and creates a new pad for the view. It also configures
118 various parameters such as scroll lines, command line position, and tab size.
119 @param init Pointer to the Init structure containing view settings.
120 @param title Title for the box window.
121 @return 0 on success, -1 on failure.
122 */
123int init_view_boxwin(Init *init, char *title) {
124 int scr_lines, scr_cols;
125 view = init->view;
126
127 if (view->tab_stop <= 0)
128 view->tab_stop = TABSIZE;
129 set_tabsize(view->tab_stop);
130 view->f_full_screen = false;
131 getmaxyx(stdscr, scr_lines, scr_cols);
132 if (view->lines > scr_lines)
133 view->lines = scr_lines;
134 if (view->cols > scr_cols)
135 view->cols = scr_cols;
136 if (view->begy + view->lines > scr_lines)
137 view->begy = scr_lines - view->lines - 2;
138 if (view->begx + view->cols > scr_cols)
139 view->begx = scr_cols - view->cols - 2;
140 if (title != nullptr && title[0] != '\0')
142 else {
143 if (view->argv != nullptr && view->argv[0] != nullptr &&
144 view->argv[0][0] != '\0')
146 }
148 F_VIEW)) {
149 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 1);
150 ssnprintf(em1, MAXLEN - 1, "win_new(%d, %d, %d, %d, %s, %b) failed",
152 F_VIEW);
153 em2[0] = '\0';
155 return (-1);
156 }
157
159 keypad(view->win, true);
160 idlok(view->win, false);
161 idcok(view->win, false);
162 wbkgrnd(view->win, &CCC_WIN);
163 wbkgrndset(view->win, &CCC_WIN);
164 scrollok(view->win, false);
165#ifdef NCDEBUG
166 immedok(view->win, true);
167#endif
168
169 /** view->ln_win: line number window */
173 view->begx + 1);
174 keypad(view->ln_win, false);
175 idlok(view->ln_win, false);
176 idcok(view->ln_win, false);
177 wbkgrnd(view->ln_win, &CCC_LN);
178 wbkgrndset(view->ln_win, &CCC_LN);
179 scrollok(view->ln_win, true);
180 wsetscrreg(view->ln_win, 0, view->scroll_lines - 1);
181#ifdef NCDEBUG
182 immedok(view->ln_win, true);
183#endif
184 /** pad for main content */
186 view->cmd_line = 0;
187 view->pminrow = 0;
188 view->pmincol = 0;
193 view->pad = newpad(view->lines - 1, PAD_COLS);
194 keypad(view->pad, true);
195 idlok(view->pad, false);
196 idcok(view->pad, false);
197 wbkgrnd(view->pad, &CCC_WIN);
198 wbkgrndset(view->pad, &CCC_WIN);
199 scrollok(view->pad, true);
200 wsetscrreg(view->pad, 0, view->scroll_lines - 1);
201#ifdef NCDEBUG
202 immedok(view->pad, true);
203#endif
204 return (0);
205}
206/** @brief Initialize the input for a C-Menu View.
207 @ingroup init_view
208 @details This function initializes the input for view, which can be a file,
209 standard input, or a provider command to be initiated by view. It handles
210 different input sources and sets up the necessary file descriptors and memory
211 mapping for efficient access.
212 @param view Pointer to the View structure to be initialized.
213 @param file_name Name of the input file or "-" for standard input.
214 @return true on success, false on failure.
215 @note if a provider command is specified, set up a pipe to read its output.
216 A child process is spawned, and view, the parent process, reads from the
217 pipe.
218 @note If input is from a pipe or standard input, clone it to a temporary
219 file. This allows for memory-mapping the input later. It does not support
220 real-time updates to the input, but it allows for efficient access to the
221 data.
222 */
223int view_init_input(View *view, char *file_name) {
224 struct stat sb;
225 int idx = 0;
226 pid_t pid = -1;
227 int pipe_fd[2];
228 int s_argc = 0;
229 char *s_argv[MAXARGS];
230 char tmp_str[MAXLEN];
231 view->f_in_pipe = false;
232 if (strcmp(file_name, "-") == 0) {
233 file_name = "/dev/stdin";
234 view->f_in_pipe = true;
235 }
236 if (view->provider_cmd[0] != '\0') {
237 s_argc = str_to_args(s_argv, view->provider_cmd, MAXARGS - 1);
238 if (pipe(pipe_fd) == -1) {
239 Perror("pipe(pipe_fd) failed in init_view");
240 return -1;
241 }
242 if ((pid = fork()) == -1) {
243 Perror("fork() failed in init_view");
244 return -1;
245 }
246 if (pid == 0) { // Child
247 close(pipe_fd[P_READ]);
248 dup2(pipe_fd[P_WRITE], STDOUT_FILENO);
249 close(pipe_fd[P_WRITE]);
250 execvp(s_argv[0], s_argv);
251 strnz__cpy(tmp_str, "Can't exec view start cmd: ", MAXLEN - 1);
252 strnz__cat(tmp_str, s_argv[0], MAXLEN - 1);
253 Perror(tmp_str);
254 exit(EXIT_FAILURE);
255 }
256 // Back to parent
257 destroy_argv(s_argc, s_argv);
258 close(pipe_fd[P_WRITE]);
259 dup2(pipe_fd[P_READ], STDIN_FILENO);
260 view->in_fd = dup(STDIN_FILENO);
261 view->f_in_pipe = true;
262 } else {
263 if (view->f_in_pipe)
264 view->in_fd = dup(STDIN_FILENO);
265 else {
266 /*----------------------------------------------------------------------*/
267 /** Open the input file for reading and get its size. */
268 expand_tilde(file_name, MAXLEN - 1);
269 view->in_fd = open(file_name, O_RDONLY);
270 if (view->in_fd == -1) {
271 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__,
272 __LINE__ - 2);
273 ssnprintf(em1, MAXLEN - 1, "open %s", file_name);
274 strerror_r(errno, em2, MAXLEN);
276 return -1;
277 }
278 if (fstat(view->in_fd, &sb) == -1) {
279 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__,
280 __LINE__ - 1);
281 ssnprintf(em1, MAXLEN - 1, "fstat %s", file_name);
282 strerror_r(errno, em2, MAXLEN);
284 close(view->in_fd);
285 return -1;
286 }
287 view->file_size = sb.st_size;
288 if (view->file_size == 0) {
289 close(view->in_fd);
290 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__,
291 __LINE__ - 1);
292 ssnprintf(em1, MAXLEN - 1, "file %s is empty", file_name);
293 strerror_r(errno, em2, MAXLEN);
295 return -1;
296 }
297 if (!S_ISREG(sb.st_mode))
298 view->f_in_pipe = true;
299 }
300 /*----------------------------------------------------------------------*/
301 }
302 if (view->f_in_pipe) {
303 char tmp_filename[] = "/tmp/view_XXXXXX";
304 char buf[VBUFSIZ];
305 ssize_t bytes_read = 0;
306 ssize_t bytes_written = 0;
307 close(view->in_fd);
308 view->in_fd = mkstemp(tmp_filename);
309 if (view->in_fd == -1) {
310 abend(-1, "failed to mkstemp");
311 exit(EXIT_FAILURE);
312 }
313 unlink(tmp_filename);
314 /*-----------------------------------------------------------------*/
315 bool f_wait = false;
316 int ready;
317 fd_set read_fds;
318 struct timeval timeout;
319 Chyron *wait_chyron = nullptr;
320 WINDOW *wait_win = nullptr;
321 int remaining;
322 FD_ZERO(&read_fds);
323 FD_SET(STDIN_FILENO, &read_fds);
324 timeout.tv_sec = 0;
325 timeout.tv_usec = 200000; /**< 200ms timeout to check for input */
326 ready = select(STDIN_FILENO + 1, &read_fds, nullptr, nullptr, &timeout);
327 if (ready == 0) {
328 f_wait = true;
329 remaining = wait_timeout;
330 wait_chyron = wait_mk_chyron();
331 wait_win = wait_mk_win(wait_chyron, "WAITING for VIEW INPUT");
332 }
333 cmd_key = 0;
334 while (ready == 0 && remaining > 0 && cmd_key != KEY_F(9)) {
335 cmd_key = wait_continue(wait_win, wait_chyron, remaining);
336 if (cmd_key == KEY_F(9))
337 break;
338 FD_ZERO(&read_fds);
339 FD_SET(STDIN_FILENO, &read_fds);
340 timeout.tv_sec = 0;
341 timeout.tv_usec = 0;
342 ready =
343 select(STDIN_FILENO + 1, &read_fds, nullptr, nullptr, &timeout);
344 remaining--;
345 }
346 if (f_wait) {
347 if (wait_chyron != nullptr)
348 wait_destroy(wait_chyron);
349 }
350 if (cmd_key == KEY_F(9)) {
351 if (view->f_in_pipe && pid > 0) {
352 /** If user cancels while waiting for view input, kill
353 * provider_cmd child process and close pipe */
354 kill(pid, SIGKILL);
355 waitpid(pid, nullptr, 0);
356 close(pipe_fd[P_READ]);
357 }
358 Perror("No view input available");
359 return -1;
360 }
361 if (ready == -1) {
362 Perror("Error waiting for view input");
363 if (view->f_in_pipe && pid > 0) {
364 /** If error occurs while waiting for view input, kill
365 * provider_cmd child process and close pipe */
366 kill(pid, SIGKILL);
367 waitpid(pid, nullptr, 0);
368 close(pipe_fd[P_READ]);
369 }
370 return -1;
371 }
372 if (ready == 0) {
373 Perror("Timeout waiting for view input");
374 if (view->f_in_pipe && pid > 0) {
375 /** If timeout occurs while waiting for view input, kill
376 * provider_cmd child process and close pipe */
377 kill(pid, SIGKILL);
378 waitpid(pid, nullptr, 0);
379 close(pipe_fd[P_READ]);
380 }
381 return -1;
382 }
383 if (ready == 1 && !FD_ISSET(STDIN_FILENO, &read_fds)) {
384 Perror("Unexpected error waiting for view input");
385 if (view->f_in_pipe && pid > 0) {
386 /** If unexpected error occurs while waiting for view input,
387 * kill provider_cmd child process and close pipe */
388 kill(pid, SIGKILL);
389 waitpid(pid, nullptr, 0);
390 close(pipe_fd[P_READ]);
391 }
392 return -1;
393 }
394 /*-----------------------------------------------------------------*/
395 while ((bytes_read = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
396 if (write(view->in_fd, buf, bytes_read) != bytes_read) {
397 abend(-1, "unable to write tmp");
398 exit(EXIT_FAILURE);
399 }
400 bytes_written += bytes_read;
401 }
402 if (bytes_written == 0) {
403 abend(-1, "unable to read stdin");
404 exit(EXIT_FAILURE);
405 }
406 if (fstat(view->in_fd, &sb) == -1) {
407 abend(-1, "fstat failed");
408 exit(EXIT_FAILURE);
409 }
410 view->file_size = sb.st_size;
411 if (view->file_size == 0) {
412 close(view->in_fd);
413 strnz__cpy(tmp_str, "no standard input", MAXLEN - 1);
414 abend(-1, tmp_str);
415 exit(EXIT_FAILURE);
416 }
418 // waitpid(-1, nullptr, 0);
419 }
420 // Memory-map the input file for efficient access.
421 view->buf =
422 mmap(nullptr, view->file_size, PROT_READ, MAP_PRIVATE, view->in_fd, 0);
423 if (view->buf == MAP_FAILED) {
424 ssnprintf(em0, MAXLEN - 1, "%s, line: %d", __FILE__, __LINE__ - 2);
425 ssnprintf(em1, MAXLEN - 1, "mmap %s", file_name);
426 strerror_r(errno, em2, MAXLEN);
428 close(view->in_fd);
429 return -1;
430 }
431 close(view->in_fd);
432 view->file_size = sb.st_size;
434 view->buf_curr_ptr = view->buf;
435 if (view->cmd_all[0] != '\0')
437 for (idx = 0; idx < NMARKS; idx++)
438 view->mark_tbl[idx] = NULL_POSITION;
439 strnz__cpy(view->cur_file_str, file_name, MAXLEN - 1);
441 return 0;
442}
#define P_READ
Definition common.h:50
#define F_VIEW
Definition common.h:49
#define P_WRITE
Definition common.h:51
#define MAXARGS
Definition cm.h:30
int wait_timeout
Definition futil.c:98
#define nullptr
Definition cm.h:23
#define NMARKS
Definition view.h:24
#define VBUFSIZ
Definition view.h:28
#define PAD_COLS
Definition view.h:30
#define NULL_POSITION
Definition view.h:27
View * view
Definition mem.c:48
#define MAXLEN
Definition curskeys.c:15
unsigned int cmd_key
Definition dwin.c:117
cchar_t CCC_LN
Definition dwin.c:151
bool waitpid_with_timeout(pid_t pid, int timeout)
Definition dwin.c:1431
char em1[MAXLEN]
Definition dwin.c:133
cchar_t CCC_WIN
Definition dwin.c:146
char em0[MAXLEN]
Definition dwin.c:132
char em2[MAXLEN]
Definition dwin.c:134
int win_new(int, int, int, int, char *, int)
Create a new window with optional box and title.
Definition dwin.c:783
bool wait_destroy(Chyron *)
Destroy the waiting message window and chyron.
Definition dwin.c:1203
int wait_continue(WINDOW *, Chyron *, int)
Update the waiting message with remaining time and check for user input.
Definition dwin.c:1215
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
void destroy_argv(int argc, char **argv)
Deallocates memory allocated for argument strings in argv.
Definition futil.c:221
size_t strnz__cpy(char *, const char *, size_t)
safer alternative to strncpy
Definition futil.c:269
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
bool expand_tilde(char *, int)
Replace Leading Tilde With Home Directory.
Definition futil.c:684
bool base_name(char *, char *)
Returns the base name of a file specification.
Definition futil.c:775
int str_to_args(char **, char *, int)
Converts a string into an array of argument strings.
Definition futil.c:167
int init_view_full_screen(Init *)
Initialize C-Menu View in full screen mode.
Definition init_view.c:40
int init_view_boxwin(Init *, char *)
Initialize the C-Menu View in box window mode.
Definition init_view.c:123
int view_init_input(View *, char *)
Initialize the input for a C-Menu View.
Definition init_view.c:223
View * view
Definition common.h:178
char provider_cmd[MAXLEN]
Definition view.h:52
char ** argv
Definition view.h:59
int smaxrow
Definition view.h:111
char * buf
Definition view.h:144
int in_fd
Definition view.h:137
WINDOW * pad
Definition view.h:99
int smincol
Definition view.h:109
off_t prev_file_pos
Definition view.h:130
WINDOW * win
Definition view.h:67
char cmd[MAXLEN]
Definition view.h:54
bool f_in_pipe
Definition view.h:136
char * buf_curr_ptr
Definition view.h:145
int smaxcol
Definition view.h:113
int cols
Definition view.h:49
bool f_full_screen
Definition view.h:86
int ln_win_cols
Definition view.h:154
off_t mark_tbl[NMARKS]
Definition view.h:135
WINDOW * ln_win
Definition view.h:152
int lines
Definition view.h:48
int pminrow
Definition view.h:105
int pmincol
Definition view.h:106
off_t file_size
Definition view.h:128
char cur_file_str[MAXLEN]
Definition view.h:91
int sminrow
Definition view.h:107
int cmd_line
Definition view.h:103
int tab_stop
Definition view.h:74
char title[MAXLEN]
Definition view.h:57
char cmd_all[MAXLEN]
Definition view.h:55
int begy
Definition view.h:50
int scroll_lines
Definition view.h:102
int ln_win_lines
Definition view.h:153
char file_name[MAXLEN]
Definition view.h:81
int begx
Definition view.h:51