Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
4 *
5 * Derived from menuconfig.
6 */
7#include <xalloc.h>
8#include "nconf.h"
9#include "lkc.h"
10
11int attr_normal;
12int attr_main_heading;
13int attr_main_menu_box;
14int attr_main_menu_fore;
15int attr_main_menu_back;
16int attr_main_menu_grey;
17int attr_main_menu_heading;
18int attr_scrollwin_text;
19int attr_scrollwin_heading;
20int attr_scrollwin_box;
21int attr_dialog_text;
22int attr_dialog_menu_fore;
23int attr_dialog_menu_back;
24int attr_dialog_box;
25int attr_input_box;
26int attr_input_heading;
27int attr_input_text;
28int attr_input_field;
29int attr_function_text;
30int attr_function_highlight;
31
32#define COLOR_ATTR(_at, _fg, _bg, _hl) \
33 { .attr = &(_at), .has_color = true, .color_fg = _fg, .color_bg = _bg, .highlight = _hl }
34#define NO_COLOR_ATTR(_at, _hl) \
35 { .attr = &(_at), .has_color = false, .highlight = _hl }
36#define COLOR_DEFAULT -1
37
38struct nconf_attr_param {
39 int *attr;
40 bool has_color;
41 int color_fg;
42 int color_bg;
43 int highlight;
44};
45
46static const struct nconf_attr_param color_theme_params[] = {
47 COLOR_ATTR(attr_normal, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
48 COLOR_ATTR(attr_main_heading, COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD | A_UNDERLINE),
49 COLOR_ATTR(attr_main_menu_box, COLOR_YELLOW, COLOR_DEFAULT, A_NORMAL),
50 COLOR_ATTR(attr_main_menu_fore, COLOR_DEFAULT, COLOR_DEFAULT, A_REVERSE),
51 COLOR_ATTR(attr_main_menu_back, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
52 COLOR_ATTR(attr_main_menu_grey, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
53 COLOR_ATTR(attr_main_menu_heading, COLOR_GREEN, COLOR_DEFAULT, A_BOLD),
54 COLOR_ATTR(attr_scrollwin_text, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
55 COLOR_ATTR(attr_scrollwin_heading, COLOR_GREEN, COLOR_DEFAULT, A_BOLD),
56 COLOR_ATTR(attr_scrollwin_box, COLOR_YELLOW, COLOR_DEFAULT, A_BOLD),
57 COLOR_ATTR(attr_dialog_text, COLOR_DEFAULT, COLOR_DEFAULT, A_BOLD),
58 COLOR_ATTR(attr_dialog_menu_fore, COLOR_RED, COLOR_DEFAULT, A_STANDOUT),
59 COLOR_ATTR(attr_dialog_menu_back, COLOR_YELLOW, COLOR_DEFAULT, A_NORMAL),
60 COLOR_ATTR(attr_dialog_box, COLOR_YELLOW, COLOR_DEFAULT, A_BOLD),
61 COLOR_ATTR(attr_input_box, COLOR_YELLOW, COLOR_DEFAULT, A_NORMAL),
62 COLOR_ATTR(attr_input_heading, COLOR_GREEN, COLOR_DEFAULT, A_BOLD),
63 COLOR_ATTR(attr_input_text, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
64 COLOR_ATTR(attr_input_field, COLOR_DEFAULT, COLOR_DEFAULT, A_UNDERLINE),
65 COLOR_ATTR(attr_function_text, COLOR_YELLOW, COLOR_DEFAULT, A_REVERSE),
66 COLOR_ATTR(attr_function_highlight, COLOR_DEFAULT, COLOR_DEFAULT, A_BOLD),
67 { /* sentinel */ }
68};
69
70static const struct nconf_attr_param no_color_theme_params[] = {
71 NO_COLOR_ATTR(attr_normal, A_NORMAL),
72 NO_COLOR_ATTR(attr_main_heading, A_BOLD | A_UNDERLINE),
73 NO_COLOR_ATTR(attr_main_menu_box, A_NORMAL),
74 NO_COLOR_ATTR(attr_main_menu_fore, A_STANDOUT),
75 NO_COLOR_ATTR(attr_main_menu_back, A_NORMAL),
76 NO_COLOR_ATTR(attr_main_menu_grey, A_NORMAL),
77 NO_COLOR_ATTR(attr_main_menu_heading, A_BOLD),
78 NO_COLOR_ATTR(attr_scrollwin_text, A_NORMAL),
79 NO_COLOR_ATTR(attr_scrollwin_heading, A_BOLD),
80 NO_COLOR_ATTR(attr_scrollwin_box, A_BOLD),
81 NO_COLOR_ATTR(attr_dialog_text, A_NORMAL),
82 NO_COLOR_ATTR(attr_dialog_menu_fore, A_STANDOUT),
83 NO_COLOR_ATTR(attr_dialog_menu_back, A_NORMAL),
84 NO_COLOR_ATTR(attr_dialog_box, A_BOLD),
85 NO_COLOR_ATTR(attr_input_box, A_BOLD),
86 NO_COLOR_ATTR(attr_input_heading, A_BOLD),
87 NO_COLOR_ATTR(attr_input_text, A_NORMAL),
88 NO_COLOR_ATTR(attr_input_field, A_UNDERLINE),
89 NO_COLOR_ATTR(attr_function_text, A_REVERSE),
90 NO_COLOR_ATTR(attr_function_highlight, A_BOLD),
91 { /* sentinel */ }
92};
93
94void set_colors(void)
95{
96 const struct nconf_attr_param *p;
97 int pair = 0;
98
99 if (has_colors()) {
100 start_color();
101 use_default_colors();
102 p = color_theme_params;
103 } else {
104 p = no_color_theme_params;
105 }
106
107 for (; p->attr; p++) {
108 int attr = p->highlight;
109
110 if (p->has_color) {
111 pair++;
112 init_pair(pair, p->color_fg, p->color_bg);
113 attr |= COLOR_PAIR(pair);
114 }
115
116 *p->attr = attr;
117 }
118}
119
120/* this changes the windows attributes !!! */
121void print_in_middle(WINDOW *win, int y, int width, const char *str, int attrs)
122{
123 wattrset(win, attrs);
124 mvwprintw(win, y, (width - strlen(str)) / 2, "%s", str);
125}
126
127int get_line_no(const char *text)
128{
129 int i;
130 int total = 1;
131
132 if (!text)
133 return 0;
134
135 for (i = 0; text[i] != '\0'; i++)
136 if (text[i] == '\n')
137 total++;
138 return total;
139}
140
141const char *get_line(const char *text, int line_no)
142{
143 int i;
144 int lines = 0;
145
146 if (!text)
147 return NULL;
148
149 for (i = 0; text[i] != '\0' && lines < line_no; i++)
150 if (text[i] == '\n')
151 lines++;
152 return text+i;
153}
154
155int get_line_length(const char *line)
156{
157 int res = 0;
158 while (*line != '\0' && *line != '\n') {
159 line++;
160 res++;
161 }
162 return res;
163}
164
165/* print all lines to the window. */
166void fill_window(WINDOW *win, const char *text)
167{
168 int x, y;
169 int total_lines = get_line_no(text);
170 int i;
171
172 getmaxyx(win, y, x);
173 /* do not go over end of line */
174 total_lines = min(total_lines, y);
175 for (i = 0; i < total_lines; i++) {
176 const char *line = get_line(text, i);
177 int len = min(get_line_length(line), x);
178
179 mvwprintw(win, i, 0, "%.*s", len, line);
180 }
181}
182
183/* get the message, and buttons.
184 * each button must be a char*
185 * return the selected button
186 *
187 * this dialog is used for 2 different things:
188 * 1) show a text box, no buttons.
189 * 2) show a dialog, with horizontal buttons
190 */
191int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...)
192{
193 va_list ap;
194 char *btn;
195 int btns_width = 0;
196 int msg_lines = 0;
197 int msg_width = 0;
198 int total_width;
199 int win_rows = 0;
200 WINDOW *win;
201 WINDOW *msg_win;
202 WINDOW *menu_win;
203 MENU *menu;
204 ITEM *btns[btn_num+1];
205 int i, x, y;
206 int res = -1;
207
208
209 va_start(ap, btn_num);
210 for (i = 0; i < btn_num; i++) {
211 btn = va_arg(ap, char *);
212 btns[i] = new_item(btn, "");
213 btns_width += strlen(btn)+1;
214 }
215 va_end(ap);
216 btns[btn_num] = NULL;
217
218 /* find the widest line of msg: */
219 msg_lines = get_line_no(msg);
220 for (i = 0; i < msg_lines; i++) {
221 const char *line = get_line(msg, i);
222 int len = get_line_length(line);
223 if (msg_width < len)
224 msg_width = len;
225 }
226
227 total_width = max(msg_width, btns_width);
228 /* place dialog in middle of screen */
229 y = (getmaxy(stdscr)-(msg_lines+4))/2;
230 x = (getmaxx(stdscr)-(total_width+4))/2;
231
232
233 /* create the windows */
234 if (btn_num > 0)
235 win_rows = msg_lines+4;
236 else
237 win_rows = msg_lines+2;
238
239 win = newwin(win_rows, total_width+4, y, x);
240 keypad(win, TRUE);
241 menu_win = derwin(win, 1, btns_width, win_rows-2,
242 1+(total_width+2-btns_width)/2);
243 menu = new_menu(btns);
244 msg_win = derwin(win, win_rows-2, msg_width, 1,
245 1+(total_width+2-msg_width)/2);
246
247 set_menu_fore(menu, attr_dialog_menu_fore);
248 set_menu_back(menu, attr_dialog_menu_back);
249
250 wattrset(win, attr_dialog_box);
251 box(win, 0, 0);
252
253 /* print message */
254 wattrset(msg_win, attr_dialog_text);
255 fill_window(msg_win, msg);
256
257 set_menu_win(menu, win);
258 set_menu_sub(menu, menu_win);
259 set_menu_format(menu, 1, btn_num);
260 menu_opts_off(menu, O_SHOWDESC);
261 menu_opts_off(menu, O_SHOWMATCH);
262 menu_opts_on(menu, O_ONEVALUE);
263 menu_opts_on(menu, O_NONCYCLIC);
264 set_menu_mark(menu, "");
265 post_menu(menu);
266
267
268 touchwin(win);
269 refresh_all_windows(main_window);
270 while ((res = wgetch(win))) {
271 switch (res) {
272 case KEY_LEFT:
273 menu_driver(menu, REQ_LEFT_ITEM);
274 break;
275 case KEY_RIGHT:
276 menu_driver(menu, REQ_RIGHT_ITEM);
277 break;
278 case 9: /* TAB */
279 if (btn_num > 1) {
280 /* cycle through buttons */
281 if (item_index(current_item(menu)) == btn_num - 1)
282 menu_driver(menu, REQ_FIRST_ITEM);
283 else
284 menu_driver(menu, REQ_NEXT_ITEM);
285 }
286 break;
287 case 10: /* ENTER */
288 case 27: /* ESCAPE */
289 case ' ':
290 case KEY_F(F_BACK):
291 case KEY_F(F_EXIT):
292 break;
293 }
294 touchwin(win);
295 refresh_all_windows(main_window);
296
297 if (res == 10 || res == ' ') {
298 res = item_index(current_item(menu));
299 break;
300 } else if (res == 27 || res == KEY_F(F_BACK) ||
301 res == KEY_F(F_EXIT)) {
302 res = KEY_EXIT;
303 break;
304 }
305 }
306
307 unpost_menu(menu);
308 free_menu(menu);
309 for (i = 0; i < btn_num; i++)
310 free_item(btns[i]);
311
312 delwin(win);
313 return res;
314}
315
316int dialog_inputbox(WINDOW *main_window,
317 const char *title, const char *prompt,
318 const char *init, char **resultp, int *result_len)
319{
320 int prompt_lines = 0;
321 int prompt_width = 0;
322 WINDOW *win;
323 WINDOW *prompt_win;
324 WINDOW *form_win;
325 PANEL *panel;
326 int i, x, y, lines, columns, win_lines, win_cols;
327 int res = -1;
328 int cursor_position = strlen(init);
329 int cursor_form_win;
330 char *result = *resultp;
331
332 getmaxyx(stdscr, lines, columns);
333
334 if (strlen(init)+1 > *result_len) {
335 *result_len = strlen(init)+1;
336 *resultp = result = xrealloc(result, *result_len);
337 }
338
339 /* find the widest line of msg: */
340 prompt_lines = get_line_no(prompt);
341 for (i = 0; i < prompt_lines; i++) {
342 const char *line = get_line(prompt, i);
343 int len = get_line_length(line);
344 prompt_width = max(prompt_width, len);
345 }
346
347 if (title)
348 prompt_width = max(prompt_width, strlen(title));
349
350 win_lines = min(prompt_lines+6, lines-2);
351 win_cols = min(prompt_width+7, columns-2);
352 prompt_lines = max(win_lines-6, 0);
353 prompt_width = max(win_cols-7, 0);
354
355 /* place dialog in middle of screen */
356 y = (lines-win_lines)/2;
357 x = (columns-win_cols)/2;
358
359 strncpy(result, init, *result_len);
360 result[*result_len - 1] = '\0';
361
362 /* create the windows */
363 win = newwin(win_lines, win_cols, y, x);
364 prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2);
365 form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2);
366 keypad(form_win, TRUE);
367
368 wattrset(form_win, attr_input_field);
369
370 wattrset(win, attr_input_box);
371 box(win, 0, 0);
372 wattrset(win, attr_input_heading);
373 if (title)
374 mvwprintw(win, 0, 3, "%s", title);
375
376 /* print message */
377 wattrset(prompt_win, attr_input_text);
378 fill_window(prompt_win, prompt);
379
380 mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
381 cursor_form_win = min(cursor_position, prompt_width-1);
382 mvwprintw(form_win, 0, 0, "%s",
383 result + cursor_position-cursor_form_win);
384
385 /* create panels */
386 panel = new_panel(win);
387
388 /* show the cursor */
389 curs_set(1);
390
391 touchwin(win);
392 refresh_all_windows(main_window);
393 while ((res = wgetch(form_win))) {
394 int len = strlen(result);
395 switch (res) {
396 case 10: /* ENTER */
397 case 27: /* ESCAPE */
398 case KEY_F(F_HELP):
399 case KEY_F(F_EXIT):
400 case KEY_F(F_BACK):
401 break;
402 case 8: /* ^H */
403 case 127: /* ^? */
404 case KEY_BACKSPACE:
405 if (cursor_position > 0) {
406 memmove(&result[cursor_position-1],
407 &result[cursor_position],
408 len-cursor_position+1);
409 cursor_position--;
410 cursor_form_win--;
411 len--;
412 }
413 break;
414 case KEY_DC:
415 if (cursor_position >= 0 && cursor_position < len) {
416 memmove(&result[cursor_position],
417 &result[cursor_position+1],
418 len-cursor_position+1);
419 len--;
420 }
421 break;
422 case KEY_UP:
423 case KEY_RIGHT:
424 if (cursor_position < len) {
425 cursor_position++;
426 cursor_form_win++;
427 }
428 break;
429 case KEY_DOWN:
430 case KEY_LEFT:
431 if (cursor_position > 0) {
432 cursor_position--;
433 cursor_form_win--;
434 }
435 break;
436 case KEY_HOME:
437 cursor_position = 0;
438 cursor_form_win = 0;
439 break;
440 case KEY_END:
441 cursor_position = len;
442 cursor_form_win = min(cursor_position, prompt_width-1);
443 break;
444 default:
445 if ((isgraph(res) || isspace(res))) {
446 /* one for new char, one for '\0' */
447 if (len+2 > *result_len) {
448 *result_len = len+2;
449 *resultp = result = realloc(result,
450 *result_len);
451 }
452 /* insert the char at the proper position */
453 memmove(&result[cursor_position+1],
454 &result[cursor_position],
455 len-cursor_position+1);
456 result[cursor_position] = res;
457 cursor_position++;
458 cursor_form_win++;
459 len++;
460 } else {
461 mvprintw(0, 0, "unknown key: %d\n", res);
462 }
463 break;
464 }
465 if (cursor_form_win < 0)
466 cursor_form_win = 0;
467 else if (cursor_form_win > prompt_width-1)
468 cursor_form_win = prompt_width-1;
469
470 wmove(form_win, 0, 0);
471 wclrtoeol(form_win);
472 mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
473 mvwprintw(form_win, 0, 0, "%s",
474 result + cursor_position-cursor_form_win);
475 wmove(form_win, 0, cursor_form_win);
476 touchwin(win);
477 refresh_all_windows(main_window);
478
479 if (res == 10) {
480 res = 0;
481 break;
482 } else if (res == 27 || res == KEY_F(F_BACK) ||
483 res == KEY_F(F_EXIT)) {
484 res = KEY_EXIT;
485 break;
486 } else if (res == KEY_F(F_HELP)) {
487 res = 1;
488 break;
489 }
490 }
491
492 /* hide the cursor */
493 curs_set(0);
494 del_panel(panel);
495 delwin(prompt_win);
496 delwin(form_win);
497 delwin(win);
498 return res;
499}
500
501/* refresh all windows in the correct order */
502void refresh_all_windows(WINDOW *main_window)
503{
504 update_panels();
505 touchwin(main_window);
506 refresh();
507}
508
509void show_scroll_win(WINDOW *main_window,
510 const char *title,
511 const char *text)
512{
513 (void)show_scroll_win_ext(main_window, title, (char *)text, NULL, NULL, NULL, NULL);
514}
515
516/* layman's scrollable window... */
517int show_scroll_win_ext(WINDOW *main_window, const char *title, char *text,
518 int *vscroll, int *hscroll,
519 extra_key_cb_fn extra_key_cb, void *data)
520{
521 int res;
522 int total_lines = get_line_no(text);
523 int x, y, lines, columns;
524 int start_x = 0, start_y = 0;
525 int text_lines = 0, text_cols = 0;
526 int total_cols = 0;
527 int win_cols = 0;
528 int win_lines = 0;
529 int i = 0;
530 WINDOW *win;
531 WINDOW *pad;
532 PANEL *panel;
533 bool done = false;
534
535 if (hscroll)
536 start_x = *hscroll;
537 if (vscroll)
538 start_y = *vscroll;
539
540 getmaxyx(stdscr, lines, columns);
541
542 /* find the widest line of msg: */
543 total_lines = get_line_no(text);
544 for (i = 0; i < total_lines; i++) {
545 const char *line = get_line(text, i);
546 int len = get_line_length(line);
547 total_cols = max(total_cols, len+2);
548 }
549
550 /* create the pad */
551 pad = newpad(total_lines+10, total_cols+10);
552 wattrset(pad, attr_scrollwin_text);
553 fill_window(pad, text);
554
555 win_lines = min(total_lines+4, lines-2);
556 win_cols = min(total_cols+2, columns-2);
557 text_lines = max(win_lines-4, 0);
558 text_cols = max(win_cols-2, 0);
559
560 /* place window in middle of screen */
561 y = (lines-win_lines)/2;
562 x = (columns-win_cols)/2;
563
564 win = newwin(win_lines, win_cols, y, x);
565 keypad(win, TRUE);
566 /* show the help in the help window, and show the help panel */
567 wattrset(win, attr_scrollwin_box);
568 box(win, 0, 0);
569 wattrset(win, attr_scrollwin_heading);
570 mvwprintw(win, 0, 3, " %s ", title);
571 panel = new_panel(win);
572
573 /* handle scrolling */
574 while (!done) {
575 copywin(pad, win, start_y, start_x, 2, 2, text_lines,
576 text_cols, 0);
577 print_in_middle(win,
578 text_lines+2,
579 text_cols,
580 "<OK>",
581 attr_dialog_menu_fore);
582 wrefresh(win);
583
584 res = wgetch(win);
585 switch (res) {
586 case KEY_NPAGE:
587 case ' ':
588 case 'd':
589 start_y += text_lines-2;
590 break;
591 case KEY_PPAGE:
592 case 'u':
593 start_y -= text_lines+2;
594 break;
595 case KEY_HOME:
596 start_y = 0;
597 break;
598 case KEY_END:
599 start_y = total_lines-text_lines;
600 break;
601 case KEY_DOWN:
602 case 'j':
603 start_y++;
604 break;
605 case KEY_UP:
606 case 'k':
607 start_y--;
608 break;
609 case KEY_LEFT:
610 case 'h':
611 start_x--;
612 break;
613 case KEY_RIGHT:
614 case 'l':
615 start_x++;
616 break;
617 default:
618 if (extra_key_cb) {
619 size_t start = (get_line(text, start_y) - text);
620 size_t end = (get_line(text, start_y + text_lines) - text);
621
622 if (extra_key_cb(res, start, end, data)) {
623 done = true;
624 break;
625 }
626 }
627 }
628 if (res == 0 || res == 10 || res == 27 || res == 'q' ||
629 res == KEY_F(F_HELP) || res == KEY_F(F_BACK) ||
630 res == KEY_F(F_EXIT))
631 break;
632 if (start_y < 0)
633 start_y = 0;
634 if (start_y >= total_lines-text_lines)
635 start_y = total_lines-text_lines;
636 if (start_x < 0)
637 start_x = 0;
638 if (start_x >= total_cols-text_cols)
639 start_x = total_cols-text_cols;
640 }
641
642 if (hscroll)
643 *hscroll = start_x;
644 if (vscroll)
645 *vscroll = start_y;
646 del_panel(panel);
647 delwin(win);
648 refresh_all_windows(main_window);
649 return res;
650}