]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/lua/menu.lua
stand/lua: Correct usage and acceptance of BACKSPACE/DELETE keys
[FreeBSD/FreeBSD.git] / stand / lua / menu.lua
1 --
2 -- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3 -- All rights reserved.
4 --
5 -- Redistribution and use in source and binary forms, with or without
6 -- modification, are permitted provided that the following conditions
7 -- are met:
8 -- 1. Redistributions of source code must retain the above copyright
9 --    notice, this list of conditions and the following disclaimer.
10 -- 2. Redistributions in binary form must reproduce the above copyright
11 --    notice, this list of conditions and the following disclaimer in the
12 --    documentation and/or other materials provided with the distribution.
13 --
14 -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 -- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 -- SUCH DAMAGE.
25 --
26 -- $FreeBSD$
27 --
28
29
30 local menu = {};
31
32 local core = require("core");
33 local color = require("color");
34 local config = require("config");
35 local screen = require("screen");
36 local drawer = require("drawer");
37
38 local OnOff;
39 local skip;
40 local run;
41 local autoboot;
42 local carousel_choices = {};
43
44 --loader menu tree:
45 --rooted at menu.welcome
46 --submenu declarations:
47 local boot_options;
48 local welcome;
49
50 menu.boot_options = {
51         -- return to welcome menu
52         {
53                 entry_type = core.MENU_RETURN,
54                 name = function()
55                         return "Back to main menu"..color.highlight(" [Backspace]");
56                 end
57         },
58
59         -- load defaults
60         {
61                 entry_type = core.MENU_ENTRY,
62                 name = function()
63                         return "Load System "..color.highlight("D").."efaults";
64                 end,
65                 func = function()
66                         core.setDefaults()
67                 end,
68                 alias = {"d", "D"}
69         },
70
71         {
72                 entry_type = core.MENU_SEPARATOR,
73                 name = function()
74                         return "";
75                 end
76         },
77
78         {
79                 entry_type = core.MENU_SEPARATOR,
80                 name = function()
81                         return "Boot Options:";
82                 end
83         },
84
85         -- acpi
86         {
87                 entry_type = core.MENU_ENTRY,
88                 name = function()
89                         return OnOff(color.highlight("A").."CPI       :", core.acpi);
90                 end,
91                 func = function()
92                         core.setACPI();
93                 end,
94                 alias = {"a", "A"}
95         },
96         -- safe mode
97         {
98                 entry_type = core.MENU_ENTRY,
99                 name = function()
100                         return OnOff("Safe "..color.highlight("M").."ode  :", core.sm);
101                 end,
102                 func = function()
103                         core.setSafeMode();
104                 end,
105                 alias = {"m", "M"}
106         },
107         -- single user
108         {
109                 entry_type = core.MENU_ENTRY,
110                 name = function()
111                         return OnOff(color.highlight("S").."ingle user:", core.su);
112                 end,
113                 func = function()
114                         core.setSingleUser();
115                 end,
116                 alias = {"s", "S"}
117         },
118         -- verbose boot
119         {
120                 entry_type = core.MENU_ENTRY,
121                 name = function()
122                         return OnOff(color.highlight("V").."erbose    :", core.verbose);
123                 end,
124                 func = function()
125                         core.setVerbose();
126                 end,
127                 alias = {"v", "V"}
128         },
129 };
130
131 menu.welcome = {
132         -- boot multi user
133         {
134                 entry_type = core.MENU_ENTRY,
135                 name = function()
136                         return color.highlight("B").."oot Multi user "..color.highlight("[Enter]");
137                 end,
138                 func = function()
139                         core.setSingleUser(false);
140                         core.boot();
141                 end,
142                 alias = {"b", "B"}
143         },
144
145         -- boot single user
146         {
147                 entry_type = core.MENU_ENTRY,
148                 name = function()
149                         return "Boot "..color.highlight("S").."ingle user";
150                 end,
151                 func = function()
152                         core.setSingleUser(true);
153                         core.boot();
154                 end,
155                 alias = {"s", "S"}
156         },
157
158         -- escape to interpreter
159         {
160                 entry_type = core.MENU_RETURN,
161                 name = function()
162                         return color.highlight("Esc").."ape to loader prompt";
163                 end,
164                 alias = {core.KEYSTR_ESCAPE}
165         },
166
167         -- reboot
168         {
169                 entry_type = core.MENU_ENTRY,
170                 name = function()
171                         return color.highlight("R").."eboot";
172                 end,
173                 func = function()
174                         loader.perform("reboot");
175                 end,
176                 alias = {"r", "R"}
177         },
178
179
180         {
181                 entry_type = core.MENU_SEPARATOR,
182                 name = function()
183                         return "";
184                 end
185         },
186
187         {
188                 entry_type = core.MENU_SEPARATOR,
189                 name = function()
190                         return "Options:";
191                 end
192         },
193
194         -- kernel options
195         {
196                 entry_type = core.MENU_CAROUSEL_ENTRY,
197                 carousel_id = "kernel",
198                 items = core.kernelList,
199                 name = function(idx, choice, all_choices)
200                         if #all_choices == 0 then
201                                 return "Kernel: ";
202                         end
203
204                         local kernel_name = color.escapef(color.GREEN) ..
205                             choice .. color.default();
206                         if (idx == 1) then
207                                 kernel_name = "default/" .. kernel_name;
208                         end
209                         return color.highlight("K").."ernel: " .. kernel_name ..
210                             " (" .. idx ..
211                             " of " .. #all_choices .. ")";
212                 end,
213                 func = function(choice)
214                         config.reload(choice);
215                 end,
216                 alias = {"k", "K"}
217         },
218
219         -- boot options
220         {
221                 entry_type = core.MENU_SUBMENU,
222                 name = function()
223                         return "Boot "..color.highlight("O").."ptions";
224                 end,
225                 submenu = function()
226                         return menu.boot_options;
227                 end,
228                 alias = {"o", "O"}
229         }
230
231 };
232
233 -- The first item in every carousel is always the default item.
234 function menu.getCarouselIndex(id)
235         local val = carousel_choices[id];
236         if (val == nil) then
237                 return 1;
238         end
239         return val;
240 end
241
242 function menu.setCarouselIndex(id, idx)
243         carousel_choices[id] = idx;
244 end
245
246 function menu.run(m)
247
248         if (menu.skip()) then
249                 core.autoboot();
250                 return false;
251         end
252
253         if (m == nil) then
254                 m = menu.welcome;
255         end
256
257         -- redraw screen
258         screen.clear();
259         screen.defcursor();
260         local alias_table = drawer.drawscreen(m);
261
262 --      menu.autoboot();
263
264         cont = true;
265         while cont do
266                 local key = io.getchar();
267
268                 -- Special key behaviors
269                 if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
270                     (m ~= menu.welcome) then
271                         break
272                 elseif (key == core.KEY_ENTER) then
273                         core.boot();
274                         -- Should not return
275                 end
276
277                 key = string.char(key)
278                 -- check to see if key is an alias
279                 local sel_entry = nil;
280                 for k, v in pairs(alias_table) do
281                         if (key == k) then
282                                 sel_entry = v;
283                         end
284                 end
285
286                 -- if we have an alias do the assigned action:
287                 if(sel_entry ~= nil) then
288                         if (sel_entry.entry_type == core.MENU_ENTRY) then
289                                 -- run function
290                                 sel_entry.func();
291                         elseif (sel_entry.entry_type == core.MENU_CAROUSEL_ENTRY) then
292                                 -- carousel (rotating) functionality
293                                 local carid = sel_entry.carousel_id;
294                                 local caridx = menu.getCarouselIndex(carid);
295                                 local choices = sel_entry.items();
296
297                                 caridx = (caridx % #choices) + 1;
298                                 menu.setCarouselIndex(carid, caridx);
299                                 sel_entry.func(choices[caridx]);
300                         elseif (sel_entry.entry_type == core.MENU_SUBMENU) then
301                                 -- recurse
302                                 cont = menu.run(sel_entry.submenu());
303                         elseif (sel_entry.entry_type == core.MENU_RETURN) then
304                                 -- break recurse
305                                 cont = false;
306                         end
307                         -- if we got an alias key the screen is out of date:
308                         screen.clear();
309                         screen.defcursor();
310                         alias_table = drawer.drawscreen(m);
311                 end
312         end
313
314         if (m == menu.welcome) then
315                 screen.defcursor();
316                 print("Exiting menu!");
317                 return false;
318         end
319
320         return true;
321 end
322
323 function menu.skip()
324         if core.bootserial() then
325                 return true;
326         end
327         local c = string.lower(loader.getenv("console") or "");
328         if (c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil then
329                 return true;
330         end
331
332         c = string.lower(loader.getenv("beastie_disable") or "");
333         print("beastie_disable", c);
334         return c == "yes";
335 end
336
337 function menu.autoboot()
338         if menu.already_autoboot == true then
339                 return;
340         end
341         menu.already_autoboot = true;
342
343         local ab = loader.getenv("autoboot_delay");
344         if ab == "NO" or ab == "no" then
345                 core.boot();
346         end
347         ab = tonumber(ab) or 10;
348
349         local x = loader.getenv("loader_menu_timeout_x") or 5;
350         local y = loader.getenv("loader_menu_timeout_y") or 22;
351
352         local endtime = loader.time() + ab;
353         local time;
354
355         repeat
356                 time = endtime - loader.time();
357                 screen.setcursor(x, y);
358                 print("Autoboot in "..time.." seconds, hit [Enter] to boot"
359                               .." or any other key to stop     ");
360                 screen.defcursor();
361                 if io.ischar() then
362                         local ch = io.getchar();
363                         if ch == core.KEY_ENTER then
364                                 break;
365                         else
366                                 -- prevent autoboot when escaping to interpreter
367                                 loader.setenv("autoboot_delay", "NO");
368                                 -- erase autoboot msg
369                                 screen.setcursor(0, y);
370                                 print("                                        "
371                                               .."                                        ");
372                                 screen.defcursor();
373                                 return;
374                         end
375                 end
376
377                 loader.delay(50000);
378         until time <= 0
379         core.boot();
380
381 end
382
383 function OnOff(str, b)
384         if (b) then
385                 return str .. color.escapef(color.GREEN).."On"..color.escapef(color.WHITE);
386         else
387                 return str .. color.escapef(color.RED).."off"..color.escapef(color.WHITE);
388         end
389 end
390
391 return menu