From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.71) id 1VJkJJ-0001BT-AY for mharc-grub-devel@gnu.org; Wed, 11 Sep 2013 09:18:21 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40561) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VJkJC-0000xW-Cp for grub-devel@gnu.org; Wed, 11 Sep 2013 09:18:18 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VJkJ7-00048y-7F for grub-devel@gnu.org; Wed, 11 Sep 2013 09:18:14 -0400 Received: from chiark.greenend.org.uk ([212.13.197.229]:56005) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VJkJ6-00048l-UF for grub-devel@gnu.org; Wed, 11 Sep 2013 09:18:09 -0400 Received: from [172.20.153.9] (helo=riva.pelham.vpn.ucam.org) by chiark.greenend.org.uk (Debian Exim 4.72 #1) with esmtps (return-path cjwatson@ubuntu.com) id 1VJkJ4-0006XI-SO; Wed, 11 Sep 2013 14:18:07 +0100 Received: from ns1.pelham.vpn.ucam.org ([172.20.153.2] helo=riva.ucam.org) by riva.pelham.vpn.ucam.org with esmtps (TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.80) (envelope-from ) id 1VJkJ3-0002vb-Om; Wed, 11 Sep 2013 14:18:05 +0100 Date: Wed, 11 Sep 2013 14:18:04 +0100 From: Colin Watson To: grub-devel@gnu.org Subject: [RFC][PATCH] Allow hotkeys to interrupt hidden menu Message-ID: <20130911131804.GA10979@riva.ucam.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-Received-From: 212.13.197.229 Cc: Franz Hsieh X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list Reply-To: The development of GNU GRUB List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 11 Sep 2013 13:18:18 -0000 One of my colleagues, Franz Hsieh (CCed) sent this patch to https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/1178618. It has a few minor implementation issues, and I'll follow up with my own review in a few minutes (it certainly also requires rebasing to apply properly to trunk), but I wanted to ask what people thought of the design, particularly since it adds new configuration options. The basic idea is to allow a hidden menu (sleep --interruptible) to be interrupted by a hotkey which is then passed on to the menu code, so that you can use menuentry --hotkey and still hotkey-select an item from the hidden menu. I suggested adding an "unput"-type feature to the terminal layer to support this, but the approach used here of stuffing the hotkey into an environment variable should work just as well and avoids the need to add more code to the kernel. Index: grub2-1.99/grub-core/commands/sleep.c =================================================================== --- grub2-1.99.orig/grub-core/commands/sleep.c 2013-06-13 10:15:08.574977456 +0800 +++ grub2-1.99/grub-core/commands/sleep.c 2013-06-13 10:15:09.366977489 +0800 @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -31,9 +32,31 @@ { {"verbose", 'v', 0, N_("Verbose countdown."), 0, 0}, {"interruptible", 'i', 0, N_("Interruptible with ESC."), 0, 0}, + {"function-key", 'f', 0, + N_("Interruptible with function key."), "KEY", ARG_TYPE_STRING}, {0, 0, 0, 0, 0, 0} }; +static struct +{ + char *name; + int key; +} function_key_aliases[] = + { + {"f1", GRUB_TERM_KEY_F1}, + {"f2", GRUB_TERM_KEY_F2}, + {"f3", GRUB_TERM_KEY_F3}, + {"f4", GRUB_TERM_KEY_F4}, + {"f5", GRUB_TERM_KEY_F5}, + {"f6", GRUB_TERM_KEY_F6}, + {"f7", GRUB_TERM_KEY_F7}, + {"f8", GRUB_TERM_KEY_F8}, + {"f9", GRUB_TERM_KEY_F9}, + {"f10", GRUB_TERM_KEY_F10}, + {"f11", GRUB_TERM_KEY_F11}, + {"f12", GRUB_TERM_KEY_F12}, + }; + static grub_uint16_t *pos; static void @@ -46,7 +69,21 @@ } static int -grub_check_keyboard (void) +grub_parse_function_key(const char* name) +{ + unsigned i; + if (!name) + return 0; + + for (i = 0; i < ARRAY_SIZE (function_key_aliases); i++) + if (grub_strcmp (name, function_key_aliases[i].name) == 0) + return function_key_aliases[i].key; + + return 0; +} + +static int +grub_check_keyboard (int fnkey) { int mods = 0; grub_term_input_t term; @@ -64,22 +101,34 @@ (mods & (GRUB_TERM_STATUS_LSHIFT | GRUB_TERM_STATUS_RSHIFT)) != 0) return 1; - if (grub_checkkey () >= 0 && grub_getkey () == GRUB_TERM_ESC) - return 1; + if (grub_checkkey () >= 0) + { + int key = grub_getkey(); + if (key == GRUB_TERM_ESC) + return 1; + + if (key == fnkey) + { + char hotkey[32] = {0}; + grub_snprintf(hotkey, 32, "%d", key); + grub_env_set("hotkey", (char*)&hotkey); + return 1; + } + } return 0; } /* Based on grub_millisleep() from kern/generic/millisleep.c. */ static int -grub_interruptible_millisleep (grub_uint32_t ms) +grub_interruptible_millisleep (grub_uint32_t ms, int key) { grub_uint64_t start; start = grub_get_time_ms (); while (grub_get_time_ms () - start < ms) - if (grub_check_keyboard ()) + if (grub_check_keyboard (key)) return 1; return 0; @@ -90,6 +139,7 @@ { struct grub_arg_list *state = ctxt->state; int n; + int key = 0; if (argc != 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing operand"); @@ -104,14 +154,21 @@ pos = grub_term_save_pos (); + if (state[2].set) + { + key = grub_parse_function_key(state[2].arg); + if (key == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid function key"); + } + for (; n; n--) { if (state[0].set) do_print (n); - if (state[1].set) + if (state[1].set || state[2].set) { - if (grub_interruptible_millisleep (1000)) + if (grub_interruptible_millisleep (1000, key)) return 1; } else Index: grub2-1.99/grub-core/normal/menu.c =================================================================== --- grub2-1.99.orig/grub-core/normal/menu.c 2013-06-13 10:15:08.618977458 +0800 +++ grub2-1.99/grub-core/normal/menu.c 2013-06-13 10:15:09.370977489 +0800 @@ -103,6 +103,33 @@ return timeout; } +int +grub_menu_get_hotkey (void) +{ + char *val; + int hotkey; + + val = grub_env_get ("hotkey"); + if (! val) + return -1; + + grub_error_push(); + + hotkey = (int) grub_strtoul (val, 0, 10); + + /* If the value is invalid, unset the variable. */ + if (grub_errno != GRUB_ERR_NONE) + { + grub_env_unset ("hotkey"); + grub_errno = GRUB_ERR_NONE; + hotkey = -1; + } + + grub_error_pop (); + + return hotkey; +} + /* Set current timeout in the variable "timeout". */ void grub_menu_set_timeout (int timeout) @@ -481,6 +508,21 @@ return entry; } +static int +get_menuentry_by_hotkey(grub_menu_t menu, int hotkey) +{ + grub_menu_entry_t entry; + int i; + for (i = 0, entry = menu->entry_list; i < menu->size; + i++, entry = entry->next) + if (entry->hotkey == hotkey) + { + menu_fini (); + return i; + } + return 0; +} + #define GRUB_MENU_PAGE_SIZE 10 /* Show the menu and handle menu entry selection. Returns the menu entry @@ -495,14 +537,23 @@ grub_uint64_t saved_time; int default_entry, current_entry; int timeout; + int hotkey = 0; default_entry = get_entry_number (menu, "default"); + hotkey = grub_menu_get_hotkey (); /* If DEFAULT_ENTRY is not within the menu entries, fall back to the first entry. */ if (default_entry < 0 || default_entry >= menu->size) default_entry = 0; + /* check if hotkey is set */ + if (hotkey > 0 && (current_entry = get_menuentry_by_hotkey(menu, hotkey)) > 0) + { + *auto_boot = 1; + return current_entry; + } + /* If timeout is 0, drawing is pointless (and ugly). */ if (grub_menu_get_timeout () == 0) { @@ -646,16 +697,12 @@ default: { - grub_menu_entry_t entry; - int i; - for (i = 0, entry = menu->entry_list; i < menu->size; - i++, entry = entry->next) - if (entry->hotkey == c) - { - menu_fini (); - *auto_boot = 0; - return i; - } + int entry = get_menuentry_by_hotkey(menu, c); + if (entry > 0) + { + *auto_boot = 0; + return entry; + } } break; } Index: grub2-1.99/util/grub-mkconfig.in =================================================================== --- grub2-1.99.orig/util/grub-mkconfig.in 2013-06-13 10:15:09.322977487 +0800 +++ grub2-1.99/util/grub-mkconfig.in 2013-06-13 10:16:33.954980977 +0800 @@ -251,7 +251,8 @@ GRUB_INIT_TUNE \ GRUB_SAVEDEFAULT \ GRUB_BADRAM \ - GRUB_RECORDFAIL_TIMEOUT + GRUB_RECORDFAIL_TIMEOUT \ + GRUB_HIDDEN_TIMEOUT_HOTKEY if test "x${grub_cfg}" != "x"; then rm -f ${grub_cfg}.new Index: grub2-1.99/util/grub.d/30_os-prober.in =================================================================== --- grub2-1.99.orig/util/grub.d/30_os-prober.in 2013-06-13 10:15:09.010977474 +0800 +++ grub2-1.99/util/grub.d/30_os-prober.in 2013-06-13 10:22:12.534994943 +0800 @@ -34,6 +34,12 @@ verbose=" --verbose" fi + if [ "x${GRUB_HIDDEN_TIMEOUT_HOTKEY}" = "x" ] ; then + hotkey= + else + hotkey=" --function-key ${GRUB_HIDDEN_TIMEOUT_HOTKEY}" + fi + if [ "x${1}" = "x0" ] ; then cat <