Index: head/stand/lua/drawer.lua =================================================================== --- head/stand/lua/drawer.lua (revision 329698) +++ head/stand/lua/drawer.lua (revision 329699) @@ -1,388 +1,394 @@ -- -- Copyright (c) 2015 Pedro Souza -- Copyright (c) 2018 Kyle Evans -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -- SUCH DAMAGE. -- -- $FreeBSD$ -- local color = require("color") local config = require("config") local core = require("core") local screen = require("screen") local drawer = {} local fbsd_logo local beastie_color local beastie local fbsd_logo_v local orb local none local none_shifted = false local menu_entry_name = function(drawing_menu, entry) local name_handler = drawer.menu_name_handlers[entry.entry_type] if name_handler ~= nil then return name_handler(drawing_menu, entry) end - return entry.name() + if type(entry.name) == "function" then + return entry.name() + end + return entry.name end local shift_brand_text = function(shift) drawer.brand_position.x = drawer.brand_position.x + shift.x drawer.brand_position.y = drawer.brand_position.y + shift.y drawer.menu_position.x = drawer.menu_position.x + shift.x drawer.menu_position.y = drawer.menu_position.y + shift.y drawer.box_pos_dim.x = drawer.box_pos_dim.x + shift.x drawer.box_pos_dim.y = drawer.box_pos_dim.y + shift.y end fbsd_logo = { " ______ ____ _____ _____ ", " | ____| | _ \\ / ____| __ \\ ", " | |___ _ __ ___ ___ | |_) | (___ | | | |", " | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", " | | | | | __/ __/| |_) |____) | |__| |", " | | | | | | || | | |", " |_| |_| \\___|\\___||____/|_____/|_____/ " } beastie_color = { " \027[31m, ,", " /( )`", " \\ \\___ / |", " /- \027[37m_\027[31m `-/ '", " (\027[37m/\\/ \\\027[31m \\ /\\", " \027[37m/ / |\027[31m ` \\", " \027[34mO O \027[37m) \027[31m/ |", " \027[37m`-^--'\027[31m`< '", " (_.) _ ) /", " `.___/` /", " `-----' /", " \027[33m<----.\027[31m __ / __ \\", " \027[33m<----|====\027[31mO)))\027[33m==\027[31m) \\) /\027[33m====|", " \027[33m<----'\027[31m `--' `.__,' \\", " | |", " \\ / /\\", " \027[36m______\027[31m( (_ / \\______/", " \027[36m,' ,-----' |", " `--{__________)\027[37m" } beastie = { " , ,", " /( )`", " \\ \\___ / |", " /- _ `-/ '", " (/\\/ \\ \\ /\\", " / / | ` \\", " O O ) / |", " `-^--'`< '", " (_.) _ ) /", " `.___/` /", " `-----' /", " <----. __ / __ \\", " <----|====O)))==) \\) /====|", " <----' `--' `.__,' \\", " | |", " \\ / /\\", " ______( (_ / \\______/", " ,' ,-----' |", " `--{__________)" } fbsd_logo_v = { " ______", " | ____| __ ___ ___ ", " | |__ | '__/ _ \\/ _ \\", " | __|| | | __/ __/", " | | | | | | |", " |_| |_| \\___|\\___|", " ____ _____ _____", " | _ \\ / ____| __ \\", " | |_) | (___ | | | |", " | _ < \\___ \\| | | |", " | |_) |____) | |__| |", " | | | |", " |____/|_____/|_____/" } orb_color = { " \027[31m``` \027[31;1m`\027[31m", " s` `.....---...\027[31;1m....--.``` -/\027[31m", " +o .--` \027[31;1m/y:` +.\027[31m", " yo`:. \027[31;1m:o `+-\027[31m", " y/ \027[31;1m-/` -o/\027[31m", " .- \027[31;1m::/sy+:.\027[31m", " / \027[31;1m`-- /\027[31m", " `: \027[31;1m:`\027[31m", " `: \027[31;1m:`\027[31m", " / \027[31;1m/\027[31m", " .- \027[31;1m-.\027[31m", " -- \027[31;1m-.\027[31m", " `:` \027[31;1m`:`", " \027[31;1m.-- `--.", " .---.....----.\027[37m" } orb = { " ``` `", " s` `.....---.......--.``` -/", " +o .--` /y:` +.", " yo`:. :o `+-", " y/ -/` -o/", " .- ::/sy+:.", " / `-- /", " `: :`", " `: :`", " / /", " .- -.", " -- -.", " `:` `:`", " .-- `--.", " .---.....----." } none = {""} -- Module exports drawer.menu_name_handlers = { -- Menu name handlers should take the menu being drawn and entry being -- drawn as parameters, and return the name of the item. -- This is designed so that everything, including menu separators, may -- have their names derived differently. The default action for entry - -- types not specified here is to call and use entry.name(). + -- types not specified here is to use entry.name directly. [core.MENU_SEPARATOR] = function(drawing_menu, entry) if entry.name ~= nil then - return entry.name() + if type(entry.name) == "function" then + return entry.name() + end + return entry.name end return "" end, [core.MENU_CAROUSEL_ENTRY] = function(drawing_menu, entry) local carid = entry.carousel_id local caridx = config.getCarouselIndex(carid) local choices = entry.items() if #choices < caridx then caridx = 1 end return entry.name(caridx, choices[caridx], choices) end, } drawer.brand_position = {x = 2, y = 1} drawer.logo_position = {x = 46, y = 1} drawer.menu_position = {x = 6, y = 11} drawer.box_pos_dim = {x = 3, y = 10, w = 41, h = 11} drawer.branddefs = { -- Indexed by valid values for loader_brand in loader.conf(5). Valid -- keys are: graphic (table depicting graphic) ["fbsd"] = { graphic = fbsd_logo, }, ["none"] = { graphic = none, }, } drawer.logodefs = { -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys -- are: requires_color (boolean), graphic (table depicting graphic), and -- shift (table containing x and y). ["beastie"] = { requires_color = true, graphic = beastie_color, }, ["beastiebw"] = { graphic = beastie, }, ["fbsdbw"] = { graphic = fbsd_logo_v, shift = {x = 5, y = 4}, }, ["orb"] = { requires_color = true, graphic = orb_color, shift = {x = 2, y = 4}, }, ["orbbw"] = { graphic = orb, shift = {x = 2, y = 4}, }, ["tribute"] = { graphic = fbsd_logo, }, ["tributebw"] = { graphic = fbsd_logo, }, ["none"] = { graphic = none, shift = {x = 17, y = 0}, }, } function drawer.drawscreen(menu_opts) -- drawlogo() must go first. -- it determines the positions of other elements drawer.drawlogo() drawer.drawbrand() drawer.drawbox() return drawer.drawmenu(menu_opts) end function drawer.drawmenu(m) x = drawer.menu_position.x y = drawer.menu_position.y -- print the menu and build the alias table local alias_table = {} local entry_num = 0 local menu_entries = m.entries if type(menu_entries) == "function" then menu_entries = menu_entries() end for line_num, e in ipairs(menu_entries) do -- Allow menu items to be conditionally visible by specifying -- a visible function. if e.visible ~= nil and not e.visible() then goto continue end if e.entry_type ~= core.MENU_SEPARATOR then entry_num = entry_num + 1 screen.setcursor(x, y + line_num) print(entry_num .. ". " .. menu_entry_name(m, e)) -- fill the alias table alias_table[tostring(entry_num)] = e if e.alias ~= nil then for n, a in ipairs(e.alias) do alias_table[a] = e end end else screen.setcursor(x, y + line_num) print(menu_entry_name(m, e)) end ::continue:: end return alias_table end function drawer.drawbox() x = drawer.box_pos_dim.x y = drawer.box_pos_dim.y w = drawer.box_pos_dim.w h = drawer.box_pos_dim.h local hl = string.char(0xCD) local vl = string.char(0xBA) local tl = string.char(0xC9) local bl = string.char(0xC8) local tr = string.char(0xBB) local br = string.char(0xBC) screen.setcursor(x, y); print(tl) screen.setcursor(x, y+h); print(bl) screen.setcursor(x+w, y); print(tr) screen.setcursor(x+w, y+h); print(br) for i = 1, w-1 do screen.setcursor(x+i, y) print(hl) screen.setcursor(x+i, y+h) print(hl) end for i = 1, h-1 do screen.setcursor(x, y+i) print(vl) screen.setcursor(x+w, y+i) print(vl) end screen.setcursor(x+(w/2)-9, y) print("Welcome to FreeBSD") end function drawer.draw(x, y, logo) for i = 1, #logo do screen.setcursor(x, y + i) print(logo[i]) end end function drawer.drawbrand() local x = tonumber(loader.getenv("loader_brand_x")) or drawer.brand_position.x local y = tonumber(loader.getenv("loader_brand_y")) or drawer.brand_position.y local graphic = drawer.branddefs[loader.getenv("loader_brand")] if graphic == nil then graphic = fbsd_logo end drawer.draw(x, y, graphic) end function drawer.drawlogo() local x = tonumber(loader.getenv("loader_logo_x")) or drawer.logo_position.x local y = tonumber(loader.getenv("loader_logo_y")) or drawer.logo_position.y local logo = loader.getenv("loader_logo") local colored = color.isEnabled() -- Lookup local logodef = drawer.logodefs[logo] if logodef ~= nil and logodef.graphic == none then -- centre brand and text if no logo if not none_shifted then shift_brand_text(logodef.shift) none_shifted = true end elseif logodef == nil or logodef.graphic == nil or (not colored and logodef.requires_color) then -- Choose a sensible default if colored then logodef = drawer.logodefs["orb"] else logodef = drawer.logodefs["orbbw"] end end if logodef.shift ~= nil then x = x + logodef.shift.x y = y + logodef.shift.y end drawer.draw(x, y, logodef.graphic) end return drawer Index: head/stand/lua/menu.lua =================================================================== --- head/stand/lua/menu.lua (revision 329698) +++ head/stand/lua/menu.lua (revision 329699) @@ -1,454 +1,406 @@ -- -- Copyright (c) 2015 Pedro Souza -- Copyright (C) 2018 Kyle Evans -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -- SUCH DAMAGE. -- -- $FreeBSD$ -- local core = require("core") local color = require("color") local config = require("config") local screen = require("screen") local drawer = require("drawer") local menu = {} local skip local run local autoboot local OnOff = function(str, b) if b then return str .. color.escapef(color.GREEN) .. "On" .. color.escapef(color.WHITE) else return str .. color.escapef(color.RED) .. "off" .. color.escapef(color.WHITE) end end -- Module exports menu.handlers = { -- Menu handlers take the current menu and selected entry as parameters, -- and should return a boolean indicating whether execution should -- continue or not. The return value may be omitted if this entry should -- have no bearing on whether we continue or not, indicating that we -- should just continue after execution. [core.MENU_ENTRY] = function(current_menu, entry) -- run function entry.func() end, [core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry) -- carousel (rotating) functionality local carid = entry.carousel_id local caridx = config.getCarouselIndex(carid) local choices = entry.items() if #choices > 0 then caridx = (caridx % #choices) + 1 config.setCarouselIndex(carid, caridx) entry.func(caridx, choices[caridx], choices) end end, [core.MENU_SUBMENU] = function(current_menu, entry) -- recurse return menu.run(entry.submenu) end, [core.MENU_RETURN] = function(current_menu, entry) -- allow entry to have a function/side effect if entry.func ~= nil then entry.func() end return false end, } -- loader menu tree is rooted at menu.welcome menu.boot_options = { entries = { -- return to welcome menu { entry_type = core.MENU_RETURN, - name = function() - return "Back to main menu" .. - color.highlight(" [Backspace]") - end + name = "Back to main menu" .. + color.highlight(" [Backspace]"), }, - -- load defaults { entry_type = core.MENU_ENTRY, - name = function() - return "Load System " .. color.highlight("D") .. - "efaults" - end, - func = function() - core.setDefaults() - end, + name = "Load System " .. color.highlight("D") .. + "efaults", + func = core.setDefaults, alias = {"d", "D"} }, - { entry_type = core.MENU_SEPARATOR, }, - { entry_type = core.MENU_SEPARATOR, - name = function() - return "Boot Options:" - end + name = "Boot Options:", }, - -- acpi { entry_type = core.MENU_ENTRY, visible = core.isSystem386, name = function() return OnOff(color.highlight("A") .. "CPI :", core.acpi) end, - func = function() - core.setACPI() - end, + func = core.setACPI, alias = {"a", "A"} }, -- safe mode { entry_type = core.MENU_ENTRY, name = function() return OnOff("Safe " .. color.highlight("M") .. "ode :", core.sm) end, - func = function() - core.setSafeMode() - end, + func = core.setSafeMode, alias = {"m", "M"} }, -- single user { entry_type = core.MENU_ENTRY, name = function() return OnOff(color.highlight("S") .. "ingle user:", core.su) end, - func = function() - core.setSingleUser() - end, + func = core.setSingleUser, alias = {"s", "S"} }, -- verbose boot { entry_type = core.MENU_ENTRY, name = function() return OnOff(color.highlight("V") .. "erbose :", core.verbose) end, - func = function() - core.setVerbose() - end, + func = core.setVerbose, alias = {"v", "V"} }, }, } menu.welcome = { entries = function() local menu_entries = menu.welcome.all_entries -- Swap the first two menu items on single user boot if core.isSingleUserBoot() then -- We'll cache the swapped menu, for performance if menu.welcome.swapped_menu ~= nil then return menu.welcome.swapped_menu end -- Shallow copy the table menu_entries = core.shallowCopyTable(menu_entries) -- Swap the first two menu entries menu_entries[1], menu_entries[2] = menu_entries[2], menu_entries[1] -- Then set their names to their alternate names menu_entries[1].name, menu_entries[2].name = menu_entries[1].alternate_name, menu_entries[2].alternate_name menu.welcome.swapped_menu = menu_entries end return menu_entries end, all_entries = { -- boot multi user { entry_type = core.MENU_ENTRY, - name = function() - return color.highlight("B") .. - "oot Multi user " .. - color.highlight("[Enter]") - end, + name = color.highlight("B") .. "oot Multi user " .. + color.highlight("[Enter]"), -- Not a standard menu entry function! - alternate_name = function() - return color.highlight("B") .. - "oot Multi user" - end, + alternate_name = color.highlight("B") .. + "oot Multi user", func = function() core.setSingleUser(false) core.boot() end, alias = {"b", "B"} }, - -- boot single user { entry_type = core.MENU_ENTRY, - name = function() - return "Boot " .. color.highlight("S") .. - "ingle user" - end, + name = "Boot " .. color.highlight("S") .. "ingle user", -- Not a standard menu entry function! - alternate_name = function() - return "Boot " .. color.highlight("S") .. - "ingle user " .. color.highlight("[Enter]") - end, + alternate_name = "Boot " .. color.highlight("S") .. + "ingle user " .. color.highlight("[Enter]"), func = function() core.setSingleUser(true) core.boot() end, alias = {"s", "S"} }, - -- escape to interpreter { entry_type = core.MENU_RETURN, - name = function() - return color.highlight("Esc") .. - "ape to loader prompt" - end, + name = color.highlight("Esc") .. "ape to loader prompt", func = function() loader.setenv("autoboot_delay", "NO") end, alias = {core.KEYSTR_ESCAPE} }, - -- reboot { entry_type = core.MENU_ENTRY, - name = function() - return color.highlight("R") .. "eboot" - end, + name = color.highlight("R") .. "eboot", func = function() loader.perform("reboot") end, alias = {"r", "R"} }, - - { entry_type = core.MENU_SEPARATOR, }, - { entry_type = core.MENU_SEPARATOR, - name = function() - return "Options:" - end + name = "Options:", }, - -- kernel options { entry_type = core.MENU_CAROUSEL_ENTRY, carousel_id = "kernel", items = core.kernelList, name = function(idx, choice, all_choices) if #all_choices == 0 then return "Kernel: " end local is_default = (idx == 1) local kernel_name = "" local name_color if is_default then name_color = color.escapef(color.GREEN) kernel_name = "default/" else name_color = color.escapef(color.BLUE) end kernel_name = kernel_name .. name_color .. choice .. color.default() return color.highlight("K") .. "ernel: " .. kernel_name .. " (" .. idx .. " of " .. #all_choices .. ")" end, func = function(idx, choice, all_choices) config.selectkernel(choice) end, alias = {"k", "K"} }, - -- boot options { entry_type = core.MENU_SUBMENU, - name = function() - return "Boot " .. color.highlight("O") .. - "ptions" - end, + name = "Boot " .. color.highlight("O") .. "ptions", submenu = menu.boot_options, alias = {"o", "O"} }, }, } menu.default = menu.welcome function menu.run(m) if menu.skip() then core.autoboot() return false end if m == nil then m = menu.default end -- redraw screen screen.clear() screen.defcursor() local alias_table = drawer.drawscreen(m) menu.autoboot() cont = true while cont do local key = io.getchar() -- Special key behaviors if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and m ~= menu.default then break elseif key == core.KEY_ENTER then core.boot() -- Should not return end key = string.char(key) -- check to see if key is an alias local sel_entry = nil for k, v in pairs(alias_table) do if key == k then sel_entry = v end end -- if we have an alias do the assigned action: if sel_entry ~= nil then -- Get menu handler local handler = menu.handlers[sel_entry.entry_type] if handler ~= nil then -- The handler's return value indicates whether -- we need to exit this menu. An omitted return -- value means "continue" by default. cont = handler(m, sel_entry) if cont == nil then cont = true end end -- if we got an alias key the screen is out of date: screen.clear() screen.defcursor() alias_table = drawer.drawscreen(m) end end if m == menu.default then screen.defcursor() print("Exiting menu!") return false end return true end function menu.skip() if core.isSerialBoot() then return true end local c = string.lower(loader.getenv("console") or "") if c:match("^efi[ ;]") ~= nil or c:match("[ ;]efi[ ;]") ~= nil then return true end c = string.lower(loader.getenv("beastie_disable") or "") print("beastie_disable", c) return c == "yes" end function menu.autoboot() if menu.already_autoboot then return end menu.already_autoboot = true local ab = loader.getenv("autoboot_delay") if ab ~= nil and ab:lower() == "no" then return elseif tonumber(ab) == -1 then core.boot() end ab = tonumber(ab) or 10 local x = loader.getenv("loader_menu_timeout_x") or 5 local y = loader.getenv("loader_menu_timeout_y") or 22 local endtime = loader.time() + ab local time repeat time = endtime - loader.time() screen.setcursor(x, y) print("Autoboot in " .. time .. " seconds, hit [Enter] to boot" .. " or any other key to stop ") screen.defcursor() if io.ischar() then local ch = io.getchar() if ch == core.KEY_ENTER then break else -- erase autoboot msg screen.setcursor(0, y) print(" " .. " ") screen.defcursor() return end end loader.delay(50000) until time <= 0 core.boot() end return menu