1-- 2-- Copyright (c) 2015 Pedro Souza <[email protected]> 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 30local menu = {}; 31 32local core = require("core"); 33local color = require("color"); 34local config = require("config"); 35local screen = require("screen"); 36local drawer = require("drawer"); 37 38local OnOff; 39local skip; 40local run; 41local autoboot; 42local carousel_choices = {}; 43 44--loader menu tree: 45--rooted at menu.welcome 46--submenu declarations: 47local boot_options; 48local welcome; 49 50menu.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 131menu.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. 234function menu.getCarouselIndex(id) 235 local val = carousel_choices[id]; 236 if (val == nil) then 237 return 1; 238 end 239 return val; 240end 241 242function menu.setCarouselIndex(id, idx) 243 carousel_choices[id] = idx; 244end 245 246function 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) and (m ~= menu.welcome) then 270 break 271 elseif (key == core.KEY_ENTER) then 272 core.boot(); 273 -- Should not return 274 end 275 276 key = string.char(key) 277 -- check to see if key is an alias 278 local sel_entry = nil; 279 for k, v in pairs(alias_table) do 280 if (key == k) then 281 sel_entry = v; 282 end 283 end 284 285 -- if we have an alias do the assigned action: 286 if(sel_entry ~= nil) then 287 if (sel_entry.entry_type == core.MENU_ENTRY) then 288 -- run function 289 sel_entry.func(); 290 elseif (sel_entry.entry_type == core.MENU_CAROUSEL_ENTRY) then 291 -- carousel (rotating) functionality 292 local carid = sel_entry.carousel_id; 293 local caridx = menu.getCarouselIndex(carid); 294 local choices = sel_entry.items(); 295 296 caridx = (caridx % #choices) + 1; 297 menu.setCarouselIndex(carid, caridx); 298 sel_entry.func(choices[caridx]); 299 elseif (sel_entry.entry_type == core.MENU_SUBMENU) then 300 -- recurse 301 cont = menu.run(sel_entry.submenu()); 302 elseif (sel_entry.entry_type == core.MENU_RETURN) then 303 -- break recurse 304 cont = false; 305 end 306 -- if we got an alias key the screen is out of date: 307 screen.clear(); 308 screen.defcursor(); 309 alias_table = drawer.drawscreen(m); 310 end 311 end 312 313 if (m == menu.welcome) then 314 screen.defcursor(); 315 print("Exiting menu!"); 316 return false; 317 end 318 319 return true; 320end 321 322function menu.skip() 323 if core.bootserial() then 324 return true; 325 end 326 local c = string.lower(loader.getenv("console") or ""); 327 if (c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil then 328 return true; 329 end 330 331 c = string.lower(loader.getenv("beastie_disable") or ""); 332 print("beastie_disable", c); 333 return c == "yes"; 334end 335 336function menu.autoboot() 337 if menu.already_autoboot == true then 338 return; 339 end 340 menu.already_autoboot = true; 341 342 local ab = loader.getenv("autoboot_delay"); 343 if ab == "NO" or ab == "no" then 344 core.boot(); 345 end 346 ab = tonumber(ab) or 10; 347 348 local x = loader.getenv("loader_menu_timeout_x") or 5; 349 local y = loader.getenv("loader_menu_timeout_y") or 22; 350 351 local endtime = loader.time() + ab; 352 local time; 353 354 repeat 355 time = endtime - loader.time(); 356 screen.setcursor(x, y); 357 print("Autoboot in "..time.." seconds, hit [Enter] to boot" 358 .." or any other key to stop "); 359 screen.defcursor(); 360 if io.ischar() then 361 local ch = io.getchar(); 362 if ch == core.KEY_ENTER then 363 break; 364 else 365 -- prevent autoboot when escaping to interpreter 366 loader.setenv("autoboot_delay", "NO"); 367 -- erase autoboot msg 368 screen.setcursor(0, y); 369 print(" " 370 .." "); 371 screen.defcursor(); 372 return; 373 end 374 end 375 376 loader.delay(50000); 377 until time <= 0 378 core.boot(); 379 380end 381 382function OnOff(str, b) 383 if (b) then 384 return str .. color.escapef(color.GREEN).."On"..color.escapef(color.WHITE); 385 else 386 return str .. color.escapef(color.RED).."off"..color.escapef(color.WHITE); 387 end 388end 389 390return menu 391