menu.lua (f0cb3b6b258df56324123c8732a9b5470a88383c) menu.lua (aedd6be5c7c3096828fafa6c1528f3966b9e3aa5)
1--
2-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3-- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
4-- All rights reserved.
5--
6-- Redistribution and use in source and binary forms, with or without
7-- modification, are permitted provided that the following conditions
8-- are met:

--- 14 unchanged lines hidden (view full) ---

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
25-- SUCH DAMAGE.
26--
27-- $FreeBSD$
28--
29
30
1--
2-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3-- Copyright (C) 2018 Kyle Evans <kevans@FreeBSD.org>
4-- All rights reserved.
5--
6-- Redistribution and use in source and binary forms, with or without
7-- modification, are permitted provided that the following conditions
8-- are met:

--- 14 unchanged lines hidden (view full) ---

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
25-- SUCH DAMAGE.
26--
27-- $FreeBSD$
28--
29
30
31local core = require("core");
32local color = require("color");
33local config = require("config");
34local screen = require("screen");
35local drawer = require("drawer");
31local core = require("core")
32local color = require("color")
33local config = require("config")
34local screen = require("screen")
35local drawer = require("drawer")
36
36
37local menu = {};
37local menu = {}
38
38
39local skip;
40local run;
41local autoboot;
39local skip
40local run
41local autoboot
42
43local OnOff = function(str, b)
44 if (b) then
45 return str .. color.escapef(color.GREEN) .. "On" ..
42
43local OnOff = function(str, b)
44 if (b) then
45 return str .. color.escapef(color.GREEN) .. "On" ..
46 color.escapef(color.WHITE);
46 color.escapef(color.WHITE)
47 else
48 return str .. color.escapef(color.RED) .. "off" ..
47 else
48 return str .. color.escapef(color.RED) .. "off" ..
49 color.escapef(color.WHITE);
49 color.escapef(color.WHITE)
50 end
51end
52
53-- Module exports
54menu.handlers = {
55 -- Menu handlers take the current menu and selected entry as parameters,
56 -- and should return a boolean indicating whether execution should
57 -- continue or not. The return value may be omitted if this entry should
58 -- have no bearing on whether we continue or not, indicating that we
59 -- should just continue after execution.
60 [core.MENU_ENTRY] = function(current_menu, entry)
61 -- run function
50 end
51end
52
53-- Module exports
54menu.handlers = {
55 -- Menu handlers take the current menu and selected entry as parameters,
56 -- and should return a boolean indicating whether execution should
57 -- continue or not. The return value may be omitted if this entry should
58 -- have no bearing on whether we continue or not, indicating that we
59 -- should just continue after execution.
60 [core.MENU_ENTRY] = function(current_menu, entry)
61 -- run function
62 entry.func();
62 entry.func()
63 end,
64 [core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry)
65 -- carousel (rotating) functionality
63 end,
64 [core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry)
65 -- carousel (rotating) functionality
66 local carid = entry.carousel_id;
67 local caridx = config.getCarouselIndex(carid);
68 local choices = entry.items();
66 local carid = entry.carousel_id
67 local caridx = config.getCarouselIndex(carid)
68 local choices = entry.items()
69
70 if (#choices > 0) then
69
70 if (#choices > 0) then
71 caridx = (caridx % #choices) + 1;
72 config.setCarouselIndex(carid, caridx);
73 entry.func(caridx, choices[caridx], choices);
71 caridx = (caridx % #choices) + 1
72 config.setCarouselIndex(carid, caridx)
73 entry.func(caridx, choices[caridx], choices)
74 end
75 end,
76 [core.MENU_SUBMENU] = function(current_menu, entry)
77 -- recurse
74 end
75 end,
76 [core.MENU_SUBMENU] = function(current_menu, entry)
77 -- recurse
78 return menu.run(entry.submenu());
78 return menu.run(entry.submenu())
79 end,
80 [core.MENU_RETURN] = function(current_menu, entry)
81 -- allow entry to have a function/side effect
82 if (entry.func ~= nil) then
79 end,
80 [core.MENU_RETURN] = function(current_menu, entry)
81 -- allow entry to have a function/side effect
82 if (entry.func ~= nil) then
83 entry.func();
83 entry.func()
84 end
84 end
85 return false;
85 return false
86 end,
86 end,
87};
87}
88-- loader menu tree is rooted at menu.welcome
89
90menu.boot_options = {
91 entries = {
92 -- return to welcome menu
93 {
94 entry_type = core.MENU_RETURN,
95 name = function()
96 return "Back to main menu" ..
88-- loader menu tree is rooted at menu.welcome
89
90menu.boot_options = {
91 entries = {
92 -- return to welcome menu
93 {
94 entry_type = core.MENU_RETURN,
95 name = function()
96 return "Back to main menu" ..
97 color.highlight(" [Backspace]");
97 color.highlight(" [Backspace]")
98 end
99 },
100
101 -- load defaults
102 {
103 entry_type = core.MENU_ENTRY,
104 name = function()
105 return "Load System " .. color.highlight("D") ..
98 end
99 },
100
101 -- load defaults
102 {
103 entry_type = core.MENU_ENTRY,
104 name = function()
105 return "Load System " .. color.highlight("D") ..
106 "efaults";
106 "efaults"
107 end,
108 func = function()
107 end,
108 func = function()
109 core.setDefaults();
109 core.setDefaults()
110 end,
111 alias = {"d", "D"}
112 },
113
114 {
115 entry_type = core.MENU_SEPARATOR,
116 name = function()
110 end,
111 alias = {"d", "D"}
112 },
113
114 {
115 entry_type = core.MENU_SEPARATOR,
116 name = function()
117 return "";
117 return ""
118 end
119 },
120
121 {
122 entry_type = core.MENU_SEPARATOR,
123 name = function()
118 end
119 },
120
121 {
122 entry_type = core.MENU_SEPARATOR,
123 name = function()
124 return "Boot Options:";
124 return "Boot Options:"
125 end
126 },
127
128 -- acpi
129 {
130 entry_type = core.MENU_ENTRY,
131 visible = core.isSystem386,
132 name = function()
133 return OnOff(color.highlight("A") ..
125 end
126 },
127
128 -- acpi
129 {
130 entry_type = core.MENU_ENTRY,
131 visible = core.isSystem386,
132 name = function()
133 return OnOff(color.highlight("A") ..
134 "CPI :", core.acpi);
134 "CPI :", core.acpi)
135 end,
136 func = function()
135 end,
136 func = function()
137 core.setACPI();
137 core.setACPI()
138 end,
139 alias = {"a", "A"}
140 },
141 -- safe mode
142 {
143 entry_type = core.MENU_ENTRY,
144 name = function()
145 return OnOff("Safe " .. color.highlight("M") ..
138 end,
139 alias = {"a", "A"}
140 },
141 -- safe mode
142 {
143 entry_type = core.MENU_ENTRY,
144 name = function()
145 return OnOff("Safe " .. color.highlight("M") ..
146 "ode :", core.sm);
146 "ode :", core.sm)
147 end,
148 func = function()
147 end,
148 func = function()
149 core.setSafeMode();
149 core.setSafeMode()
150 end,
151 alias = {"m", "M"}
152 },
153 -- single user
154 {
155 entry_type = core.MENU_ENTRY,
156 name = function()
157 return OnOff(color.highlight("S") ..
150 end,
151 alias = {"m", "M"}
152 },
153 -- single user
154 {
155 entry_type = core.MENU_ENTRY,
156 name = function()
157 return OnOff(color.highlight("S") ..
158 "ingle user:", core.su);
158 "ingle user:", core.su)
159 end,
160 func = function()
159 end,
160 func = function()
161 core.setSingleUser();
161 core.setSingleUser()
162 end,
163 alias = {"s", "S"}
164 },
165 -- verbose boot
166 {
167 entry_type = core.MENU_ENTRY,
168 name = function()
169 return OnOff(color.highlight("V") ..
162 end,
163 alias = {"s", "S"}
164 },
165 -- verbose boot
166 {
167 entry_type = core.MENU_ENTRY,
168 name = function()
169 return OnOff(color.highlight("V") ..
170 "erbose :", core.verbose);
170 "erbose :", core.verbose)
171 end,
172 func = function()
171 end,
172 func = function()
173 core.setVerbose();
173 core.setVerbose()
174 end,
175 alias = {"v", "V"}
176 },
177 },
174 end,
175 alias = {"v", "V"}
176 },
177 },
178};
178}
179
180menu.welcome = {
181 entries = function()
179
180menu.welcome = {
181 entries = function()
182 local menu_entries = menu.welcome.all_entries;
182 local menu_entries = menu.welcome.all_entries
183 -- Swap the first two menu items on single user boot
184 if (core.isSingleUserBoot()) then
185 -- We'll cache the swapped menu, for performance
186 if (menu.welcome.swapped_menu ~= nil) then
183 -- Swap the first two menu items on single user boot
184 if (core.isSingleUserBoot()) then
185 -- We'll cache the swapped menu, for performance
186 if (menu.welcome.swapped_menu ~= nil) then
187 return menu.welcome.swapped_menu;
187 return menu.welcome.swapped_menu
188 end
189 -- Shallow copy the table
188 end
189 -- Shallow copy the table
190 menu_entries = core.shallowCopyTable(menu_entries);
190 menu_entries = core.shallowCopyTable(menu_entries)
191
192 -- Swap the first two menu entries
193 menu_entries[1], menu_entries[2] =
191
192 -- Swap the first two menu entries
193 menu_entries[1], menu_entries[2] =
194 menu_entries[2], menu_entries[1];
194 menu_entries[2], menu_entries[1]
195
196 -- Then set their names to their alternate names
197 menu_entries[1].name, menu_entries[2].name =
198 menu_entries[1].alternate_name,
195
196 -- Then set their names to their alternate names
197 menu_entries[1].name, menu_entries[2].name =
198 menu_entries[1].alternate_name,
199 menu_entries[2].alternate_name;
200 menu.welcome.swapped_menu = menu_entries;
199 menu_entries[2].alternate_name
200 menu.welcome.swapped_menu = menu_entries
201 end
201 end
202 return menu_entries;
202 return menu_entries
203 end,
204 all_entries = {
205 -- boot multi user
206 {
207 entry_type = core.MENU_ENTRY,
208 name = function()
209 return color.highlight("B") ..
210 "oot Multi user " ..
203 end,
204 all_entries = {
205 -- boot multi user
206 {
207 entry_type = core.MENU_ENTRY,
208 name = function()
209 return color.highlight("B") ..
210 "oot Multi user " ..
211 color.highlight("[Enter]");
211 color.highlight("[Enter]")
212 end,
213 -- Not a standard menu entry function!
214 alternate_name = function()
215 return color.highlight("B") ..
212 end,
213 -- Not a standard menu entry function!
214 alternate_name = function()
215 return color.highlight("B") ..
216 "oot Multi user";
216 "oot Multi user"
217 end,
218 func = function()
217 end,
218 func = function()
219 core.setSingleUser(false);
220 core.boot();
219 core.setSingleUser(false)
220 core.boot()
221 end,
222 alias = {"b", "B"}
223 },
224
225 -- boot single user
226 {
227 entry_type = core.MENU_ENTRY,
228 name = function()
229 return "Boot " .. color.highlight("S") ..
221 end,
222 alias = {"b", "B"}
223 },
224
225 -- boot single user
226 {
227 entry_type = core.MENU_ENTRY,
228 name = function()
229 return "Boot " .. color.highlight("S") ..
230 "ingle user";
230 "ingle user"
231 end,
232 -- Not a standard menu entry function!
233 alternate_name = function()
234 return "Boot " .. color.highlight("S") ..
231 end,
232 -- Not a standard menu entry function!
233 alternate_name = function()
234 return "Boot " .. color.highlight("S") ..
235 "ingle user " .. color.highlight("[Enter]");
235 "ingle user " .. color.highlight("[Enter]")
236 end,
237 func = function()
236 end,
237 func = function()
238 core.setSingleUser(true);
239 core.boot();
238 core.setSingleUser(true)
239 core.boot()
240 end,
241 alias = {"s", "S"}
242 },
243
244 -- escape to interpreter
245 {
246 entry_type = core.MENU_RETURN,
247 name = function()
248 return color.highlight("Esc") ..
240 end,
241 alias = {"s", "S"}
242 },
243
244 -- escape to interpreter
245 {
246 entry_type = core.MENU_RETURN,
247 name = function()
248 return color.highlight("Esc") ..
249 "ape to loader prompt";
249 "ape to loader prompt"
250 end,
251 func = function()
250 end,
251 func = function()
252 loader.setenv("autoboot_delay", "NO");
252 loader.setenv("autoboot_delay", "NO")
253 end,
254 alias = {core.KEYSTR_ESCAPE}
255 },
256
257 -- reboot
258 {
259 entry_type = core.MENU_ENTRY,
260 name = function()
253 end,
254 alias = {core.KEYSTR_ESCAPE}
255 },
256
257 -- reboot
258 {
259 entry_type = core.MENU_ENTRY,
260 name = function()
261 return color.highlight("R") .. "eboot";
261 return color.highlight("R") .. "eboot"
262 end,
263 func = function()
262 end,
263 func = function()
264 loader.perform("reboot");
264 loader.perform("reboot")
265 end,
266 alias = {"r", "R"}
267 },
268
269
270 {
271 entry_type = core.MENU_SEPARATOR,
272 name = function()
265 end,
266 alias = {"r", "R"}
267 },
268
269
270 {
271 entry_type = core.MENU_SEPARATOR,
272 name = function()
273 return "";
273 return ""
274 end
275 },
276
277 {
278 entry_type = core.MENU_SEPARATOR,
279 name = function()
274 end
275 },
276
277 {
278 entry_type = core.MENU_SEPARATOR,
279 name = function()
280 return "Options:";
280 return "Options:"
281 end
282 },
283
284 -- kernel options
285 {
286 entry_type = core.MENU_CAROUSEL_ENTRY,
287 carousel_id = "kernel",
288 items = core.kernelList,
289 name = function(idx, choice, all_choices)
290 if (#all_choices == 0) then
281 end
282 },
283
284 -- kernel options
285 {
286 entry_type = core.MENU_CAROUSEL_ENTRY,
287 carousel_id = "kernel",
288 items = core.kernelList,
289 name = function(idx, choice, all_choices)
290 if (#all_choices == 0) then
291 return "Kernel: ";
291 return "Kernel: "
292 end
293
292 end
293
294 local is_default = (idx == 1);
295 local kernel_name = "";
296 local name_color;
294 local is_default = (idx == 1)
295 local kernel_name = ""
296 local name_color
297 if (is_default) then
297 if (is_default) then
298 name_color = color.escapef(color.GREEN);
299 kernel_name = "default/";
298 name_color = color.escapef(color.GREEN)
299 kernel_name = "default/"
300 else
300 else
301 name_color = color.escapef(color.BLUE);
301 name_color = color.escapef(color.BLUE)
302 end
303 kernel_name = kernel_name .. name_color ..
302 end
303 kernel_name = kernel_name .. name_color ..
304 choice .. color.default();
304 choice .. color.default()
305 return color.highlight("K") .. "ernel: " ..
306 kernel_name .. " (" .. idx .. " of " ..
305 return color.highlight("K") .. "ernel: " ..
306 kernel_name .. " (" .. idx .. " of " ..
307 #all_choices .. ")";
307 #all_choices .. ")"
308 end,
309 func = function(idx, choice, all_choices)
308 end,
309 func = function(idx, choice, all_choices)
310 config.selectkernel(choice);
310 config.selectkernel(choice)
311 end,
312 alias = {"k", "K"}
313 },
314
315 -- boot options
316 {
317 entry_type = core.MENU_SUBMENU,
318 name = function()
319 return "Boot " .. color.highlight("O") ..
311 end,
312 alias = {"k", "K"}
313 },
314
315 -- boot options
316 {
317 entry_type = core.MENU_SUBMENU,
318 name = function()
319 return "Boot " .. color.highlight("O") ..
320 "ptions";
320 "ptions"
321 end,
322 submenu = function()
321 end,
322 submenu = function()
323 return menu.boot_options;
323 return menu.boot_options
324 end,
325 alias = {"o", "O"}
326 },
327 },
324 end,
325 alias = {"o", "O"}
326 },
327 },
328};
328}
329
330function menu.run(m)
331
332 if (menu.skip()) then
329
330function menu.run(m)
331
332 if (menu.skip()) then
333 core.autoboot();
334 return false;
333 core.autoboot()
334 return false
335 end
336
337 if (m == nil) then
335 end
336
337 if (m == nil) then
338 m = menu.welcome;
338 m = menu.welcome
339 end
340
341 -- redraw screen
339 end
340
341 -- redraw screen
342 screen.clear();
343 screen.defcursor();
344 local alias_table = drawer.drawscreen(m);
342 screen.clear()
343 screen.defcursor()
344 local alias_table = drawer.drawscreen(m)
345
345
346 menu.autoboot();
346 menu.autoboot()
347
347
348 cont = true;
348 cont = true
349 while (cont) do
349 while (cont) do
350 local key = io.getchar();
350 local key = io.getchar()
351
352 -- Special key behaviors
353 if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
354 (m ~= menu.welcome) then
351
352 -- Special key behaviors
353 if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
354 (m ~= menu.welcome) then
355 break;
355 break
356 elseif (key == core.KEY_ENTER) then
356 elseif (key == core.KEY_ENTER) then
357 core.boot();
357 core.boot()
358 -- Should not return
359 end
360
361 key = string.char(key)
362 -- check to see if key is an alias
358 -- Should not return
359 end
360
361 key = string.char(key)
362 -- check to see if key is an alias
363 local sel_entry = nil;
363 local sel_entry = nil
364 for k, v in pairs(alias_table) do
365 if (key == k) then
364 for k, v in pairs(alias_table) do
365 if (key == k) then
366 sel_entry = v;
366 sel_entry = v
367 end
368 end
369
370 -- if we have an alias do the assigned action:
371 if (sel_entry ~= nil) then
372 -- Get menu handler
367 end
368 end
369
370 -- if we have an alias do the assigned action:
371 if (sel_entry ~= nil) then
372 -- Get menu handler
373 local handler = menu.handlers[sel_entry.entry_type];
373 local handler = menu.handlers[sel_entry.entry_type]
374 if (handler ~= nil) then
375 -- The handler's return value indicates whether
376 -- we need to exit this menu. An omitted return
377 -- value means "continue" by default.
374 if (handler ~= nil) then
375 -- The handler's return value indicates whether
376 -- we need to exit this menu. An omitted return
377 -- value means "continue" by default.
378 cont = handler(m, sel_entry);
378 cont = handler(m, sel_entry)
379 if (cont == nil) then
379 if (cont == nil) then
380 cont = true;
380 cont = true
381 end
382 end
383 -- if we got an alias key the screen is out of date:
381 end
382 end
383 -- if we got an alias key the screen is out of date:
384 screen.clear();
385 screen.defcursor();
386 alias_table = drawer.drawscreen(m);
384 screen.clear()
385 screen.defcursor()
386 alias_table = drawer.drawscreen(m)
387 end
388 end
389
390 if (m == menu.welcome) then
387 end
388 end
389
390 if (m == menu.welcome) then
391 screen.defcursor();
392 print("Exiting menu!");
393 return false;
391 screen.defcursor()
392 print("Exiting menu!")
393 return false
394 end
395
394 end
395
396 return true;
396 return true
397end
398
399function menu.skip()
400 if (core.isSerialBoot()) then
397end
398
399function menu.skip()
400 if (core.isSerialBoot()) then
401 return true;
401 return true
402 end
402 end
403 local c = string.lower(loader.getenv("console") or "");
403 local c = string.lower(loader.getenv("console") or "")
404 if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
404 if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
405 return true;
405 return true
406 end
407
406 end
407
408 c = string.lower(loader.getenv("beastie_disable") or "");
409 print("beastie_disable", c);
410 return c == "yes";
408 c = string.lower(loader.getenv("beastie_disable") or "")
409 print("beastie_disable", c)
410 return c == "yes"
411end
412
413function menu.autoboot()
414 if (menu.already_autoboot == true) then
411end
412
413function menu.autoboot()
414 if (menu.already_autoboot == true) then
415 return;
415 return
416 end
416 end
417 menu.already_autoboot = true;
417 menu.already_autoboot = true
418
418
419 local ab = loader.getenv("autoboot_delay");
419 local ab = loader.getenv("autoboot_delay")
420 if (ab ~= nil) and (ab:lower() == "no") then
420 if (ab ~= nil) and (ab:lower() == "no") then
421 return;
421 return
422 elseif (tonumber(ab) == -1) then
422 elseif (tonumber(ab) == -1) then
423 core.boot();
423 core.boot()
424 end
424 end
425 ab = tonumber(ab) or 10;
425 ab = tonumber(ab) or 10
426
426
427 local x = loader.getenv("loader_menu_timeout_x") or 5;
428 local y = loader.getenv("loader_menu_timeout_y") or 22;
427 local x = loader.getenv("loader_menu_timeout_x") or 5
428 local y = loader.getenv("loader_menu_timeout_y") or 22
429
429
430 local endtime = loader.time() + ab;
431 local time;
430 local endtime = loader.time() + ab
431 local time
432
433 repeat
432
433 repeat
434 time = endtime - loader.time();
435 screen.setcursor(x, y);
434 time = endtime - loader.time()
435 screen.setcursor(x, y)
436 print("Autoboot in " .. time ..
437 " seconds, hit [Enter] to boot" ..
436 print("Autoboot in " .. time ..
437 " seconds, hit [Enter] to boot" ..
438 " or any other key to stop ");
439 screen.defcursor();
438 " or any other key to stop ")
439 screen.defcursor()
440 if (io.ischar()) then
440 if (io.ischar()) then
441 local ch = io.getchar();
441 local ch = io.getchar()
442 if (ch == core.KEY_ENTER) then
442 if (ch == core.KEY_ENTER) then
443 break;
443 break
444 else
445 -- erase autoboot msg
444 else
445 -- erase autoboot msg
446 screen.setcursor(0, y);
446 screen.setcursor(0, y)
447 print(" "
447 print(" "
448 .. " ");
449 screen.defcursor();
450 return;
448 .. " ")
449 screen.defcursor()
450 return
451 end
452 end
453
451 end
452 end
453
454 loader.delay(50000);
455 until time <= 0;
456 core.boot();
454 loader.delay(50000)
455 until time <= 0
456 core.boot()
457
458end
459
457
458end
459
460return menu;
460return menu