Adding upstream version 4.00~pre55+dfsg.
[syslinux-debian/hramrach.git] / com32 / menu / menumain.c
blobb04c70b0f1ab06a3cafd9741659c019c4d7a9f57
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
9 * Boston MA 02110-1301, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
15 * menumain.c
17 * Simple menu system which displays a list and allows the user to select
18 * a command line and/or edit it.
21 #include <ctype.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <consoles.h>
26 #include <getkey.h>
27 #include <minmax.h>
28 #include <setjmp.h>
29 #include <limits.h>
30 #include <com32.h>
31 #include <syslinux/adv.h>
33 #include "menu.h"
35 /* The symbol "cm" always refers to the current menu across this file... */
36 static struct menu *cm;
38 const struct menu_parameter mparm[NPARAMS] = {
39 [P_WIDTH] = {"width", 0},
40 [P_MARGIN] = {"margin", 10},
41 [P_PASSWD_MARGIN] = {"passwordmargin", 3},
42 [P_MENU_ROWS] = {"rows", 12},
43 [P_TABMSG_ROW] = {"tabmsgrow", 18},
44 [P_CMDLINE_ROW] = {"cmdlinerow", 18},
45 [P_END_ROW] = {"endrow", -1},
46 [P_PASSWD_ROW] = {"passwordrow", 11},
47 [P_TIMEOUT_ROW] = {"timeoutrow", 20},
48 [P_HELPMSG_ROW] = {"helpmsgrow", 22},
49 [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1},
50 [P_HSHIFT] = {"hshift", 0},
51 [P_VSHIFT] = {"vshift", 0},
52 [P_HIDDEN_ROW] = {"hiddenrow", -2},
55 /* These macros assume "cm" is a pointer to the current menu */
56 #define WIDTH (cm->mparm[P_WIDTH])
57 #define MARGIN (cm->mparm[P_MARGIN])
58 #define PASSWD_MARGIN (cm->mparm[P_PASSWD_MARGIN])
59 #define MENU_ROWS (cm->mparm[P_MENU_ROWS])
60 #define TABMSG_ROW (cm->mparm[P_TABMSG_ROW]+VSHIFT)
61 #define CMDLINE_ROW (cm->mparm[P_CMDLINE_ROW]+VSHIFT)
62 #define END_ROW (cm->mparm[P_END_ROW])
63 #define PASSWD_ROW (cm->mparm[P_PASSWD_ROW]+VSHIFT)
64 #define TIMEOUT_ROW (cm->mparm[P_TIMEOUT_ROW]+VSHIFT)
65 #define HELPMSG_ROW (cm->mparm[P_HELPMSG_ROW]+VSHIFT)
66 #define HELPMSGEND_ROW (cm->mparm[P_HELPMSGEND_ROW])
67 #define HSHIFT (cm->mparm[P_HSHIFT])
68 #define VSHIFT (cm->mparm[P_VSHIFT])
69 #define HIDDEN_ROW (cm->mparm[P_HIDDEN_ROW])
71 static char *pad_line(const char *text, int align, int width)
73 static char buffer[MAX_CMDLINE_LEN];
74 int n, p;
76 if (width >= (int)sizeof buffer)
77 return NULL; /* Can't do it */
79 n = strlen(text);
80 if (n >= width)
81 n = width;
83 memset(buffer, ' ', width);
84 buffer[width] = 0;
85 p = ((width - n) * align) >> 1;
86 memcpy(buffer + p, text, n);
88 return buffer;
91 /* Display an entry, with possible hotkey highlight. Assumes
92 that the current attribute is the non-hotkey one, and will
93 guarantee that as an exit condition as well. */
94 static void
95 display_entry(const struct menu_entry *entry, const char *attrib,
96 const char *hotattrib, int width)
98 const char *p = entry->displayname;
99 char marker;
101 if (!p)
102 p = "";
104 switch (entry->action) {
105 case MA_SUBMENU:
106 marker = '>';
107 break;
108 case MA_EXIT:
109 marker = '<';
110 break;
111 default:
112 marker = 0;
113 break;
116 if (marker)
117 width -= 2;
119 while (width) {
120 if (*p) {
121 if (*p == '^') {
122 p++;
123 if (*p && ((unsigned char)*p & ~0x20) == entry->hotkey) {
124 fputs(hotattrib, stdout);
125 putchar(*p++);
126 fputs(attrib, stdout);
127 width--;
129 } else {
130 putchar(*p++);
131 width--;
133 } else {
134 putchar(' ');
135 width--;
139 if (marker) {
140 putchar(' ');
141 putchar(marker);
145 static void draw_row(int y, int sel, int top, int sbtop, int sbbot)
147 int i = (y - 4 - VSHIFT) + top;
148 int dis = (i < cm->nentries) && is_disabled(cm->menu_entries[i]);
150 printf("\033[%d;%dH\1#1\016x\017%s ",
151 y, MARGIN + 1 + HSHIFT,
152 (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3");
154 if (i >= cm->nentries) {
155 fputs(pad_line("", 0, WIDTH - 2 * MARGIN - 4), stdout);
156 } else {
157 display_entry(cm->menu_entries[i],
158 (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3",
159 (i == sel) ? "\1#6" : dis ? "\2#17" : "\1#4",
160 WIDTH - 2 * MARGIN - 4);
163 if (cm->nentries <= MENU_ROWS) {
164 printf(" \1#1\016x\017");
165 } else if (sbtop > 0) {
166 if (y >= sbtop && y <= sbbot)
167 printf(" \1#7\016a\017");
168 else
169 printf(" \1#1\016x\017");
170 } else {
171 putchar(' '); /* Don't modify the scrollbar */
175 static jmp_buf timeout_jump;
177 int mygetkey(clock_t timeout)
179 clock_t t0, t;
180 clock_t tto, to;
181 int key;
183 if (!totaltimeout)
184 return get_key(stdin, timeout);
186 for (;;) {
187 tto = min(totaltimeout, INT_MAX);
188 to = timeout ? min(tto, timeout) : tto;
190 t0 = times(NULL);
191 key = get_key(stdin, to);
192 t = times(NULL) - t0;
194 if (totaltimeout <= t)
195 longjmp(timeout_jump, 1);
197 totaltimeout -= t;
199 if (key != KEY_NONE)
200 return key;
202 if (timeout) {
203 if (timeout <= t)
204 return KEY_NONE;
206 timeout -= t;
211 static int ask_passwd(const char *menu_entry)
213 char user_passwd[WIDTH], *p;
214 int done;
215 int key;
216 int x;
217 int rv;
219 printf("\033[%d;%dH\2#11\016l", PASSWD_ROW, PASSWD_MARGIN + 1);
220 for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++)
221 putchar('q');
223 printf("k\033[%d;%dHx", PASSWD_ROW + 1, PASSWD_MARGIN + 1);
224 for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++)
225 putchar(' ');
227 printf("x\033[%d;%dHm", PASSWD_ROW + 2, PASSWD_MARGIN + 1);
228 for (x = 2; x <= WIDTH - 2 * PASSWD_MARGIN - 1; x++)
229 putchar('q');
231 printf("j\017\033[%d;%dH\2#12 %s \033[%d;%dH\2#13",
232 PASSWD_ROW, (WIDTH - (strlen(cm->messages[MSG_PASSPROMPT]) + 2)) / 2,
233 cm->messages[MSG_PASSPROMPT], PASSWD_ROW + 1, PASSWD_MARGIN + 3);
235 drain_keyboard();
237 /* Actually allow user to type a password, then compare to the SHA1 */
238 done = 0;
239 p = user_passwd;
241 while (!done) {
242 key = mygetkey(0);
244 switch (key) {
245 case KEY_ENTER:
246 case KEY_CTRL('J'):
247 done = 1;
248 break;
250 case KEY_ESC:
251 case KEY_CTRL('C'):
252 p = user_passwd; /* No password entered */
253 done = 1;
254 break;
256 case KEY_BACKSPACE:
257 case KEY_DEL:
258 case KEY_DELETE:
259 if (p > user_passwd) {
260 printf("\b \b");
261 p--;
263 break;
265 case KEY_CTRL('U'):
266 while (p > user_passwd) {
267 printf("\b \b");
268 p--;
270 break;
272 default:
273 if (key >= ' ' && key <= 0xFF &&
274 (p - user_passwd) < WIDTH - 2 * PASSWD_MARGIN - 5) {
275 *p++ = key;
276 putchar('*');
278 break;
282 if (p == user_passwd)
283 return 0; /* No password entered */
285 *p = '\0';
287 rv = (cm->menu_master_passwd &&
288 passwd_compare(cm->menu_master_passwd, user_passwd))
289 || (menu_entry && passwd_compare(menu_entry, user_passwd));
291 /* Clean up */
292 memset(user_passwd, 0, WIDTH);
293 drain_keyboard();
295 return rv;
298 static void draw_menu(int sel, int top, int edit_line)
300 int x, y;
301 int sbtop = 0, sbbot = 0;
302 const char *tabmsg;
303 int tabmsg_len;
305 if (cm->nentries > MENU_ROWS) {
306 int sblen = max(MENU_ROWS * MENU_ROWS / cm->nentries, 1);
307 sbtop = (MENU_ROWS - sblen + 1) * top / (cm->nentries - MENU_ROWS + 1);
308 sbbot = sbtop + sblen - 1;
309 sbtop += 4;
310 sbbot += 4; /* Starting row of scrollbar */
313 printf("\033[%d;%dH\1#1\016l", VSHIFT + 1, HSHIFT + MARGIN + 1);
314 for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++)
315 putchar('q');
317 printf("k\033[%d;%dH\1#1x\017\1#2 %s \1#1\016x",
318 VSHIFT + 2,
319 HSHIFT + MARGIN + 1, pad_line(cm->title, 1, WIDTH - 2 * MARGIN - 4));
321 printf("\033[%d;%dH\1#1t", VSHIFT + 3, HSHIFT + MARGIN + 1);
322 for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++)
323 putchar('q');
324 fputs("u\017", stdout);
326 for (y = 4 + VSHIFT; y < 4 + VSHIFT + MENU_ROWS; y++)
327 draw_row(y, sel, top, sbtop, sbbot);
329 printf("\033[%d;%dH\1#1\016m", y, HSHIFT + MARGIN + 1);
330 for (x = 2 + HSHIFT; x <= (WIDTH - 2 * MARGIN - 1) + HSHIFT; x++)
331 putchar('q');
332 fputs("j\017", stdout);
334 if (edit_line && cm->allowedit && !cm->menu_master_passwd)
335 tabmsg = cm->messages[MSG_TAB];
336 else
337 tabmsg = cm->messages[MSG_NOTAB];
339 tabmsg_len = strlen(tabmsg);
341 printf("\1#8\033[%d;%dH%s",
342 TABMSG_ROW, 1 + HSHIFT + ((WIDTH - tabmsg_len) >> 1), tabmsg);
343 printf("\1#0\033[%d;1H", END_ROW);
346 static void clear_screen(void)
348 fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout);
351 static void display_help(const char *text)
353 int row;
354 const char *p;
356 if (!text) {
357 text = "";
358 printf("\1#0\033[%d;1H", HELPMSG_ROW);
359 } else {
360 printf("\2#16\033[%d;1H", HELPMSG_ROW);
363 for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) {
364 switch (*p) {
365 case '\r':
366 case '\f':
367 case '\v':
368 case '\033':
369 break;
370 case '\n':
371 printf("\033[K\033[%d;1H", ++row);
372 break;
373 default:
374 putchar(*p);
378 fputs("\033[K", stdout);
380 while (row <= HELPMSGEND_ROW) {
381 printf("\033[K\033[%d;1H", ++row);
385 static void show_fkey(int key)
387 int fkey;
389 while (1) {
390 switch (key) {
391 case KEY_F1:
392 fkey = 0;
393 break;
394 case KEY_F2:
395 fkey = 1;
396 break;
397 case KEY_F3:
398 fkey = 2;
399 break;
400 case KEY_F4:
401 fkey = 3;
402 break;
403 case KEY_F5:
404 fkey = 4;
405 break;
406 case KEY_F6:
407 fkey = 5;
408 break;
409 case KEY_F7:
410 fkey = 6;
411 break;
412 case KEY_F8:
413 fkey = 7;
414 break;
415 case KEY_F9:
416 fkey = 8;
417 break;
418 case KEY_F10:
419 fkey = 9;
420 break;
421 case KEY_F11:
422 fkey = 10;
423 break;
424 case KEY_F12:
425 fkey = 11;
426 break;
427 default:
428 fkey = -1;
429 break;
432 if (fkey == -1)
433 break;
435 if (cm->fkeyhelp[fkey].textname)
436 key = show_message_file(cm->fkeyhelp[fkey].textname,
437 cm->fkeyhelp[fkey].background);
438 else
439 break;
443 static const char *edit_cmdline(const char *input, int top)
445 static char cmdline[MAX_CMDLINE_LEN];
446 int key, len, prev_len, cursor;
447 int redraw = 1; /* We enter with the menu already drawn */
449 strncpy(cmdline, input, MAX_CMDLINE_LEN);
450 cmdline[MAX_CMDLINE_LEN - 1] = '\0';
452 len = cursor = strlen(cmdline);
453 prev_len = 0;
455 for (;;) {
456 if (redraw > 1) {
457 /* Clear and redraw whole screen */
458 /* Enable ASCII on G0 and DEC VT on G1; do it in this order
459 to avoid confusing the Linux console */
460 clear_screen();
461 draw_menu(-1, top, 1);
462 prev_len = 0;
465 if (redraw > 0) {
466 /* Redraw the command line */
467 printf("\033[?25l\033[%d;1H\1#9> \2#10%s",
468 CMDLINE_ROW, pad_line(cmdline, 0, max(len, prev_len)));
469 printf("\2#10\033[%d;3H%s\033[?25h",
470 CMDLINE_ROW, pad_line(cmdline, 0, cursor));
471 prev_len = len;
472 redraw = 0;
475 key = mygetkey(0);
477 switch (key) {
478 case KEY_CTRL('L'):
479 redraw = 2;
480 break;
482 case KEY_ENTER:
483 case KEY_CTRL('J'):
484 return cmdline;
486 case KEY_ESC:
487 case KEY_CTRL('C'):
488 return NULL;
490 case KEY_BACKSPACE:
491 case KEY_DEL:
492 if (cursor) {
493 memmove(cmdline + cursor - 1, cmdline + cursor,
494 len - cursor + 1);
495 len--;
496 cursor--;
497 redraw = 1;
499 break;
501 case KEY_CTRL('D'):
502 case KEY_DELETE:
503 if (cursor < len) {
504 memmove(cmdline + cursor, cmdline + cursor + 1, len - cursor);
505 len--;
506 redraw = 1;
508 break;
510 case KEY_CTRL('U'):
511 if (len) {
512 len = cursor = 0;
513 cmdline[len] = '\0';
514 redraw = 1;
516 break;
518 case KEY_CTRL('W'):
519 if (cursor) {
520 int prevcursor = cursor;
522 while (cursor && my_isspace(cmdline[cursor - 1]))
523 cursor--;
525 while (cursor && !my_isspace(cmdline[cursor - 1]))
526 cursor--;
528 memmove(cmdline + cursor, cmdline + prevcursor,
529 len - prevcursor + 1);
530 len -= (prevcursor - cursor);
531 redraw = 1;
533 break;
535 case KEY_LEFT:
536 case KEY_CTRL('B'):
537 if (cursor) {
538 cursor--;
539 redraw = 1;
541 break;
543 case KEY_RIGHT:
544 case KEY_CTRL('F'):
545 if (cursor < len) {
546 putchar(cmdline[cursor++]);
548 break;
550 case KEY_CTRL('K'):
551 if (cursor < len) {
552 cmdline[len = cursor] = '\0';
553 redraw = 1;
555 break;
557 case KEY_HOME:
558 case KEY_CTRL('A'):
559 if (cursor) {
560 cursor = 0;
561 redraw = 1;
563 break;
565 case KEY_END:
566 case KEY_CTRL('E'):
567 if (cursor != len) {
568 cursor = len;
569 redraw = 1;
571 break;
573 case KEY_F1:
574 case KEY_F2:
575 case KEY_F3:
576 case KEY_F4:
577 case KEY_F5:
578 case KEY_F6:
579 case KEY_F7:
580 case KEY_F8:
581 case KEY_F9:
582 case KEY_F10:
583 case KEY_F11:
584 case KEY_F12:
585 show_fkey(key);
586 redraw = 1;
587 break;
589 default:
590 if (key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN - 1) {
591 if (cursor == len) {
592 cmdline[len] = key;
593 cmdline[++len] = '\0';
594 cursor++;
595 putchar(key);
596 prev_len++;
597 } else {
598 memmove(cmdline + cursor + 1, cmdline + cursor,
599 len - cursor + 1);
600 cmdline[cursor++] = key;
601 len++;
602 redraw = 1;
605 break;
610 static inline int shift_is_held(void)
612 uint8_t shift_bits = *(uint8_t *) 0x417;
614 return !!(shift_bits & 0x5d); /* Caps/Scroll/Alt/Shift */
617 static void print_timeout_message(int tol, int row, const char *msg)
619 static int last_msg_len = 0;
620 char buf[256];
621 int nc = 0, nnc, padc;
622 const char *tp = msg;
623 char tc;
624 char *tq = buf;
626 while ((size_t) (tq - buf) < (sizeof buf - 16) && (tc = *tp)) {
627 tp++;
628 if (tc == '#') {
629 nnc = sprintf(tq, "\2#15%d\2#14", tol);
630 tq += nnc;
631 nc += nnc - 8; /* 8 formatting characters */
632 } else if (tc == '{') {
633 /* Deal with {singular[,dual],plural} constructs */
634 struct {
635 const char *s, *e;
636 } tx[3];
637 const char *tpp;
638 int n = 0;
640 memset(tx, 0, sizeof tx);
642 tx[0].s = tp;
644 while (*tp && *tp != '}') {
645 if (*tp == ',' && n < 2) {
646 tx[n].e = tp;
647 n++;
648 tx[n].s = tp + 1;
650 tp++;
652 tx[n].e = tp;
654 if (*tp)
655 tp++; /* Skip final bracket */
657 if (!tx[1].s)
658 tx[1] = tx[0];
659 if (!tx[2].s)
660 tx[2] = tx[1];
662 /* Now [0] is singular, [1] is dual, and [2] is plural,
663 even if the user only specified some of them. */
665 switch (tol) {
666 case 1:
667 n = 0;
668 break;
669 case 2:
670 n = 1;
671 break;
672 default:
673 n = 2;
674 break;
677 for (tpp = tx[n].s; tpp < tx[n].e; tpp++) {
678 if ((size_t) (tq - buf) < (sizeof buf)) {
679 *tq++ = *tpp;
680 nc++;
683 } else {
684 *tq++ = tc;
685 nc++;
688 *tq = '\0';
690 if (nc >= last_msg_len) {
691 padc = 0;
692 } else {
693 padc = (last_msg_len - nc + 1) >> 1;
696 printf("\033[%d;%dH\2#14%*s%s%*s", row,
697 HSHIFT + 1 + ((WIDTH - nc) >> 1) - padc,
698 padc, "", buf, padc, "");
700 last_msg_len = nc;
703 /* Set the background screen, etc. */
704 static void prepare_screen_for_menu(void)
706 console_color_table = cm->color_table;
707 console_color_table_size = menu_color_table_size;
708 set_background(cm->menu_background);
711 static const char *do_hidden_menu(void)
713 int key;
714 int timeout_left, this_timeout;
716 clear_screen();
718 if (!setjmp(timeout_jump)) {
719 timeout_left = cm->timeout;
721 while (!cm->timeout || timeout_left) {
722 int tol = timeout_left / CLK_TCK;
724 print_timeout_message(tol, HIDDEN_ROW, cm->messages[MSG_AUTOBOOT]);
726 this_timeout = min(timeout_left, CLK_TCK);
727 key = mygetkey(this_timeout);
729 if (key != KEY_NONE)
730 return NULL; /* Key pressed */
732 timeout_left -= this_timeout;
736 /* Clear the message from the screen */
737 print_timeout_message(0, HIDDEN_ROW, "");
739 if (cm->ontimeout)
740 return cm->ontimeout;
741 else
742 return cm->menu_entries[cm->defentry]->cmdline; /* Default entry */
745 static const char *run_menu(void)
747 int key;
748 int done = 0;
749 volatile int entry = cm->curentry;
750 int prev_entry = -1;
751 volatile int top = cm->curtop;
752 int prev_top = -1;
753 int clear = 1, to_clear;
754 const char *cmdline = NULL;
755 volatile clock_t key_timeout, timeout_left, this_timeout;
756 const struct menu_entry *me;
757 bool hotkey = false;
759 /* Note: for both key_timeout and timeout == 0 means no limit */
760 timeout_left = key_timeout = cm->timeout;
762 /* If we're in shiftkey mode, exit immediately unless a shift key
763 is pressed */
764 if (shiftkey && !shift_is_held()) {
765 return cm->menu_entries[cm->defentry]->cmdline;
766 } else {
767 shiftkey = 0;
770 /* Do this before hiddenmenu handling, so we show the background */
771 prepare_screen_for_menu();
773 /* Handle hiddenmenu */
774 if (hiddenmenu) {
775 cmdline = do_hidden_menu();
776 if (cmdline)
777 return cmdline;
779 /* Otherwise display the menu now; the timeout has already been
780 cancelled, since the user pressed a key. */
781 hiddenmenu = 0;
782 key_timeout = 0;
785 /* Handle both local and global timeout */
786 if (setjmp(timeout_jump)) {
787 entry = cm->defentry;
789 if (top < 0 || top < entry - MENU_ROWS + 1)
790 top = max(0, entry - MENU_ROWS + 1);
791 else if (top > entry || top > max(0, cm->nentries - MENU_ROWS))
792 top = min(entry, max(0, cm->nentries - MENU_ROWS));
794 draw_menu(cm->ontimeout ? -1 : entry, top, 1);
795 cmdline =
796 cm->ontimeout ? cm->ontimeout : cm->menu_entries[entry]->cmdline;
797 done = 1;
800 while (!done) {
801 if (entry <= 0) {
802 entry = 0;
803 while (entry < cm->nentries && is_disabled(cm->menu_entries[entry]))
804 entry++;
806 if (entry >= cm->nentries) {
807 entry = cm->nentries - 1;
808 while (entry > 0 && is_disabled(cm->menu_entries[entry]))
809 entry--;
812 me = cm->menu_entries[entry];
814 if (top < 0 || top < entry - MENU_ROWS + 1)
815 top = max(0, entry - MENU_ROWS + 1);
816 else if (top > entry || top > max(0, cm->nentries - MENU_ROWS))
817 top = min(entry, max(0, cm->nentries - MENU_ROWS));
819 /* Start with a clear screen */
820 if (clear) {
821 /* Clear and redraw whole screen */
822 /* Enable ASCII on G0 and DEC VT on G1; do it in this order
823 to avoid confusing the Linux console */
824 if (clear >= 2)
825 prepare_screen_for_menu();
826 clear_screen();
827 clear = 0;
828 prev_entry = prev_top = -1;
831 if (top != prev_top) {
832 draw_menu(entry, top, 1);
833 display_help(me->helptext);
834 } else if (entry != prev_entry) {
835 draw_row(prev_entry - top + 4 + VSHIFT, entry, top, 0, 0);
836 draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0);
837 display_help(me->helptext);
840 prev_entry = entry;
841 prev_top = top;
842 cm->curentry = entry;
843 cm->curtop = top;
845 /* Cursor movement cancels timeout */
846 if (entry != cm->defentry)
847 key_timeout = 0;
849 if (key_timeout) {
850 int tol = timeout_left / CLK_TCK;
851 print_timeout_message(tol, TIMEOUT_ROW, cm->messages[MSG_AUTOBOOT]);
852 to_clear = 1;
853 } else {
854 to_clear = 0;
857 if (hotkey && me->immediate) {
858 /* If the hotkey was flagged immediate, simulate pressing ENTER */
859 key = KEY_ENTER;
860 } else {
861 this_timeout = min(min(key_timeout, timeout_left),
862 (clock_t) CLK_TCK);
863 key = mygetkey(this_timeout);
865 if (key != KEY_NONE) {
866 timeout_left = key_timeout;
867 if (to_clear)
868 printf("\033[%d;1H\1#0\033[K", TIMEOUT_ROW);
872 hotkey = false;
874 switch (key) {
875 case KEY_NONE: /* Timeout */
876 /* This is somewhat hacky, but this at least lets the user
877 know what's going on, and still deals with "phantom inputs"
878 e.g. on serial ports.
880 Warning: a timeout will boot the default entry without any
881 password! */
882 if (key_timeout) {
883 if (timeout_left <= this_timeout)
884 longjmp(timeout_jump, 1);
886 timeout_left -= this_timeout;
888 break;
890 case KEY_CTRL('L'):
891 clear = 1;
892 break;
894 case KEY_ENTER:
895 case KEY_CTRL('J'):
896 key_timeout = 0; /* Cancels timeout */
897 if (me->passwd) {
898 clear = 1;
899 done = ask_passwd(me->passwd);
900 } else {
901 done = 1;
903 cmdline = NULL;
904 if (done) {
905 switch (me->action) {
906 case MA_CMD:
907 cmdline = me->cmdline;
908 break;
909 case MA_SUBMENU:
910 case MA_GOTO:
911 case MA_EXIT:
912 done = 0;
913 clear = 2;
914 cm = me->submenu;
915 entry = cm->curentry;
916 top = cm->curtop;
917 break;
918 case MA_QUIT:
919 /* Quit menu system */
920 done = 1;
921 clear = 1;
922 draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0);
923 break;
924 default:
925 done = 0;
926 break;
929 if (done && !me->passwd) {
930 /* Only save a new default if we don't have a password... */
931 if (me->save && me->label) {
932 syslinux_setadv(ADV_MENUSAVE, strlen(me->label), me->label);
933 syslinux_adv_write();
936 break;
938 case KEY_UP:
939 case KEY_CTRL('P'):
940 while (entry > 0) {
941 entry--;
942 if (entry < top)
943 top -= MENU_ROWS;
944 if (!is_disabled(cm->menu_entries[entry]))
945 break;
947 break;
949 case KEY_DOWN:
950 case KEY_CTRL('N'):
951 while (entry < cm->nentries - 1) {
952 entry++;
953 if (entry >= top + MENU_ROWS)
954 top += MENU_ROWS;
955 if (!is_disabled(cm->menu_entries[entry]))
956 break;
958 break;
960 case KEY_PGUP:
961 case KEY_LEFT:
962 case KEY_CTRL('B'):
963 case '<':
964 entry -= MENU_ROWS;
965 top -= MENU_ROWS;
966 while (entry > 0 && is_disabled(cm->menu_entries[entry])) {
967 entry--;
968 if (entry < top)
969 top -= MENU_ROWS;
971 break;
973 case KEY_PGDN:
974 case KEY_RIGHT:
975 case KEY_CTRL('F'):
976 case '>':
977 case ' ':
978 entry += MENU_ROWS;
979 top += MENU_ROWS;
980 while (entry < cm->nentries - 1
981 && is_disabled(cm->menu_entries[entry])) {
982 entry++;
983 if (entry >= top + MENU_ROWS)
984 top += MENU_ROWS;
986 break;
988 case '-':
989 while (entry > 0) {
990 entry--;
991 top--;
992 if (!is_disabled(cm->menu_entries[entry]))
993 break;
995 break;
997 case '+':
998 while (entry < cm->nentries - 1) {
999 entry++;
1000 top++;
1001 if (!is_disabled(cm->menu_entries[entry]))
1002 break;
1004 break;
1006 case KEY_CTRL('A'):
1007 case KEY_HOME:
1008 top = entry = 0;
1009 break;
1011 case KEY_CTRL('E'):
1012 case KEY_END:
1013 entry = cm->nentries - 1;
1014 top = max(0, cm->nentries - MENU_ROWS);
1015 break;
1017 case KEY_F1:
1018 case KEY_F2:
1019 case KEY_F3:
1020 case KEY_F4:
1021 case KEY_F5:
1022 case KEY_F6:
1023 case KEY_F7:
1024 case KEY_F8:
1025 case KEY_F9:
1026 case KEY_F10:
1027 case KEY_F11:
1028 case KEY_F12:
1029 show_fkey(key);
1030 clear = 1;
1031 break;
1033 case KEY_TAB:
1034 if (cm->allowedit && me->action == MA_CMD) {
1035 int ok = 1;
1037 key_timeout = 0; /* Cancels timeout */
1038 draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0);
1040 if (cm->menu_master_passwd) {
1041 ok = ask_passwd(NULL);
1042 clear_screen();
1043 draw_menu(-1, top, 0);
1044 } else {
1045 /* Erase [Tab] message and help text */
1046 printf("\033[%d;1H\1#0\033[K", TABMSG_ROW);
1047 display_help(NULL);
1050 if (ok) {
1051 cmdline = edit_cmdline(me->cmdline, top);
1052 done = !!cmdline;
1053 clear = 1; /* In case we hit [Esc] and done is null */
1054 } else {
1055 draw_row(entry - top + 4 + VSHIFT, entry, top, 0, 0);
1058 break;
1059 case KEY_CTRL('C'): /* Ctrl-C */
1060 case KEY_ESC: /* Esc */
1061 if (cm->parent) {
1062 cm = cm->parent;
1063 clear = 2;
1064 entry = cm->curentry;
1065 top = cm->curtop;
1066 } else if (cm->allowedit) {
1067 done = 1;
1068 clear = 1;
1069 key_timeout = 0;
1071 draw_row(entry - top + 4 + VSHIFT, -1, top, 0, 0);
1073 if (cm->menu_master_passwd)
1074 done = ask_passwd(NULL);
1076 break;
1077 default:
1078 if (key > 0 && key < 0xFF) {
1079 key &= ~0x20; /* Upper case */
1080 if (cm->menu_hotkeys[key]) {
1081 key_timeout = 0;
1082 entry = cm->menu_hotkeys[key]->entry;
1083 /* Should we commit at this point? */
1084 hotkey = true;
1087 break;
1091 printf("\033[?25h"); /* Show cursor */
1093 /* Return the label name so localboot and ipappend work */
1094 return cmdline;
1097 int main(int argc, char *argv[])
1099 const char *cmdline;
1100 struct menu *m;
1101 int rows, cols;
1102 int i;
1104 (void)argc;
1106 parse_configs(argv + 1);
1109 * We don't start the console until we have parsed the configuration
1110 * file, since the configuration file might impact the console
1111 * configuration, e.g. MENU RESOLUTION.
1113 start_console();
1114 if (getscreensize(1, &rows, &cols)) {
1115 /* Unknown screen size? */
1116 rows = 24;
1117 cols = 80;
1120 /* Some postprocessing for all menus */
1121 for (m = menu_list; m; m = m->next) {
1122 if (!m->mparm[P_WIDTH])
1123 m->mparm[P_WIDTH] = cols;
1125 /* If anyone has specified negative parameters, consider them
1126 relative to the bottom row of the screen. */
1127 for (i = 0; i < NPARAMS; i++)
1128 if (m->mparm[i] < 0)
1129 m->mparm[i] = max(m->mparm[i] + rows, 0);
1132 cm = start_menu;
1134 if (!cm->nentries) {
1135 fputs("Initial menu has no LABEL entries!\n", stdout);
1136 return 1; /* Error! */
1139 for (;;) {
1140 local_cursor_enable(true);
1141 cmdline = run_menu();
1143 if (clearmenu)
1144 clear_screen();
1146 local_cursor_enable(false);
1147 printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
1149 if (cmdline) {
1150 execute(cmdline, KT_NONE);
1151 if (cm->onerror)
1152 execute(cm->onerror, KT_NONE);
1153 } else {
1154 return 0; /* Exit */