2 -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3 -- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
4 -- All rights reserved.
6 -- Redistribution and use in source and binary forms, with or without
7 -- modification, are permitted provided that the following conditions
9 -- 1. Redistributions of source code must retain the above copyright
10 -- notice, this list of conditions and the following disclaimer.
11 -- 2. Redistributions in binary form must reproduce the above copyright
12 -- notice, this list of conditions and the following disclaimer in the
13 -- documentation and/or other materials provided with the distribution.
15 -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 local core = require("core");
32 local color = require("color");
33 local config = require("config");
34 local screen = require("screen");
35 local drawer = require("drawer");
43 local carousel_choices = {};
46 -- Menu handlers take the current menu and selected entry as parameters,
47 -- and should return a boolean indicating whether execution should
48 -- continue or not. The return value may be omitted if this entry should
49 -- have no bearing on whether we continue or not, indicating that we
50 -- should just continue after execution.
51 [core.MENU_ENTRY] = function(current_menu, entry)
55 [core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry)
56 -- carousel (rotating) functionality
57 local carid = entry.carousel_id;
58 local caridx = menu.getCarouselIndex(carid);
59 local choices = entry.items();
61 if (#choices > 0) then
62 caridx = (caridx % #choices) + 1;
63 menu.setCarouselIndex(carid, caridx);
64 entry.func(caridx, choices[caridx], choices);
67 [core.MENU_SUBMENU] = function(current_menu, entry)
69 return menu.run(entry.submenu());
71 [core.MENU_RETURN] = function(current_menu, entry)
72 -- allow entry to have a function/side effect
73 if (entry.func ~= nil) then
79 -- loader menu tree is rooted at menu.welcome
83 -- return to welcome menu
85 entry_type = core.MENU_RETURN,
87 return "Back to main menu" ..
88 color.highlight(" [Backspace]");
94 entry_type = core.MENU_ENTRY,
96 return "Load System " .. color.highlight("D") ..
106 entry_type = core.MENU_SEPARATOR,
113 entry_type = core.MENU_SEPARATOR,
115 return "Boot Options:";
121 entry_type = core.MENU_ENTRY,
123 return OnOff(color.highlight("A") ..
133 entry_type = core.MENU_ENTRY,
135 return OnOff("Safe " .. color.highlight("M") ..
145 entry_type = core.MENU_ENTRY,
147 return OnOff(color.highlight("S") ..
148 "ingle user:", core.su);
151 core.setSingleUser();
157 entry_type = core.MENU_ENTRY,
159 return OnOff(color.highlight("V") ..
160 "erbose :", core.verbose);
172 local menu_entries = menu.welcome.all_entries;
173 -- Swap the first two menu items on single user boot
174 if (core.isSingleUserBoot()) then
175 -- We'll cache the swapped menu, for performance
176 if (menu.welcome.swapped_menu ~= nil) then
177 return menu.welcome.swapped_menu;
179 -- Shallow copy the table
180 menu_entries = core.shallowCopyTable(menu_entries);
182 -- Swap the first two menu entries
183 menu_entries[1], menu_entries[2] =
184 menu_entries[2], menu_entries[1];
186 -- Then set their names to their alternate names
187 menu_entries[1].name, menu_entries[2].name =
188 menu_entries[1].alternate_name,
189 menu_entries[2].alternate_name;
190 menu.welcome.swapped_menu = menu_entries;
197 entry_type = core.MENU_ENTRY,
199 return color.highlight("B") ..
201 color.highlight("[Enter]");
203 -- Not a standard menu entry function!
204 alternate_name = function()
205 return color.highlight("B") ..
209 core.setSingleUser(false);
217 entry_type = core.MENU_ENTRY,
219 return "Boot " .. color.highlight("S") ..
222 -- Not a standard menu entry function!
223 alternate_name = function()
224 return "Boot " .. color.highlight("S") ..
225 "ingle user " .. color.highlight("[Enter]");
228 core.setSingleUser(true);
234 -- escape to interpreter
236 entry_type = core.MENU_RETURN,
238 return color.highlight("Esc") ..
239 "ape to loader prompt";
242 loader.setenv("autoboot_delay", "NO");
244 alias = {core.KEYSTR_ESCAPE}
249 entry_type = core.MENU_ENTRY,
251 return color.highlight("R") .. "eboot";
254 loader.perform("reboot");
261 entry_type = core.MENU_SEPARATOR,
268 entry_type = core.MENU_SEPARATOR,
276 entry_type = core.MENU_CAROUSEL_ENTRY,
277 carousel_id = "kernel",
278 items = core.kernelList,
279 name = function(idx, choice, all_choices)
280 if (#all_choices == 0) then
284 local is_default = (idx == 1);
285 local kernel_name = "";
288 name_color = color.escapef(color.GREEN);
289 kernel_name = "default/";
291 name_color = color.escapef(color.BLUE);
293 kernel_name = kernel_name .. name_color ..
294 choice .. color.default();
295 return color.highlight("K") .. "ernel: " ..
296 kernel_name .. " (" .. idx .. " of " ..
299 func = function(idx, choice, all_choices)
300 config.selectkernel(choice);
307 entry_type = core.MENU_SUBMENU,
309 return "Boot " .. color.highlight("O") ..
313 return menu.boot_options;
320 -- The first item in every carousel is always the default item.
321 function menu.getCarouselIndex(id)
322 local val = carousel_choices[id];
329 function menu.setCarouselIndex(id, idx)
330 carousel_choices[id] = idx;
335 if (menu.skip()) then
347 local alias_table = drawer.drawscreen(m);
353 local key = io.getchar();
355 -- Special key behaviors
356 if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
357 (m ~= menu.welcome) then
359 elseif (key == core.KEY_ENTER) then
364 key = string.char(key)
365 -- check to see if key is an alias
366 local sel_entry = nil;
367 for k, v in pairs(alias_table) do
373 -- if we have an alias do the assigned action:
374 if (sel_entry ~= nil) then
376 local handler = menu.handlers[sel_entry.entry_type];
377 if (handler ~= nil) then
378 -- The handler's return value indicates whether
379 -- we need to exit this menu. An omitted return
380 -- value means "continue" by default.
381 cont = handler(m, sel_entry);
382 if (cont == nil) then
386 -- if we got an alias key the screen is out of date:
389 alias_table = drawer.drawscreen(m);
393 if (m == menu.welcome) then
395 print("Exiting menu!");
404 if (core.isSerialBoot()) then
407 local c = string.lower(loader.getenv("console") or "");
408 if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
412 c = string.lower(loader.getenv("beastie_disable") or "");
413 print("beastie_disable", c);
417 function menu.autoboot()
418 if (menu.already_autoboot == true) then
421 menu.already_autoboot = true;
423 local ab = loader.getenv("autoboot_delay");
424 if (ab ~= nil) and (ab:lower() == "no") then
426 elseif (tonumber(ab) == -1) then
429 ab = tonumber(ab) or 10;
431 local x = loader.getenv("loader_menu_timeout_x") or 5;
432 local y = loader.getenv("loader_menu_timeout_y") or 22;
434 local endtime = loader.time() + ab;
438 time = endtime - loader.time();
439 screen.setcursor(x, y);
440 print("Autoboot in " .. time ..
441 " seconds, hit [Enter] to boot" ..
442 " or any other key to stop ");
444 if (io.ischar()) then
445 local ch = io.getchar();
446 if (ch == core.KEY_ENTER) then
449 -- erase autoboot msg
450 screen.setcursor(0, y);
464 function OnOff(str, b)
466 return str .. color.escapef(color.GREEN) .. "On" ..
467 color.escapef(color.WHITE);
469 return str .. color.escapef(color.RED) .. "off" ..
470 color.escapef(color.WHITE);