1088b4f5fSWarner Losh-- 272e39d71SKyle Evans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD 372e39d71SKyle Evans-- 4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 5df74a61fSKyle Evans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 6088b4f5fSWarner Losh-- All rights reserved. 7088b4f5fSWarner Losh-- 8088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without 9088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions 10088b4f5fSWarner Losh-- are met: 11088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright 12088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer. 13088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright 14088b4f5fSWarner Losh-- notice, this list of conditions and the following disclaimer in the 15088b4f5fSWarner Losh-- documentation and/or other materials provided with the distribution. 16088b4f5fSWarner Losh-- 17088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20088b4f5fSWarner Losh-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27088b4f5fSWarner Losh-- SUCH DAMAGE. 28088b4f5fSWarner Losh-- 29088b4f5fSWarner Losh-- $FreeBSD$ 30088b4f5fSWarner Losh-- 31088b4f5fSWarner Losh 32aedd6be5SKyle Evanslocal color = require("color") 33aedd6be5SKyle Evanslocal config = require("config") 34aedd6be5SKyle Evanslocal core = require("core") 35aedd6be5SKyle Evanslocal screen = require("screen") 36088b4f5fSWarner Losh 37aedd6be5SKyle Evanslocal drawer = {} 38c8518398SKyle Evans 391091c8feSKyle Evanslocal fbsd_brand 40aedd6be5SKyle Evanslocal none 4102122e53SKyle Evans 42322a2dddSKyle Evanslocal function menuEntryName(drawing_menu, entry) 43aedd6be5SKyle Evans local name_handler = drawer.menu_name_handlers[entry.entry_type] 44e15abd1fSKyle Evans 459f71d421SKyle Evans if name_handler ~= nil then 46aedd6be5SKyle Evans return name_handler(drawing_menu, entry) 47e15abd1fSKyle Evans end 48a51f9f0cSKyle Evans if type(entry.name) == "function" then 49aedd6be5SKyle Evans return entry.name() 50e15abd1fSKyle Evans end 51a51f9f0cSKyle Evans return entry.name 52a51f9f0cSKyle Evansend 53e15abd1fSKyle Evans 54e21e1dbeSKyle Evanslocal function getBranddef(brand) 55e21e1dbeSKyle Evans if brand == nil then 56e21e1dbeSKyle Evans return nil 57e21e1dbeSKyle Evans end 58e21e1dbeSKyle Evans -- Look it up 59e21e1dbeSKyle Evans local branddef = drawer.branddefs[brand] 60e21e1dbeSKyle Evans 61e21e1dbeSKyle Evans -- Try to pull it in 62e21e1dbeSKyle Evans if branddef == nil then 63e21e1dbeSKyle Evans try_include('brand-' .. brand) 64e21e1dbeSKyle Evans branddef = drawer.branddefs[brand] 65e21e1dbeSKyle Evans end 66e21e1dbeSKyle Evans 67e21e1dbeSKyle Evans return branddef 68e21e1dbeSKyle Evansend 69e21e1dbeSKyle Evans 701091c8feSKyle Evanslocal function getLogodef(logo) 71bbb516aeSKyle Evans if logo == nil then 72bbb516aeSKyle Evans return nil 73bbb516aeSKyle Evans end 741091c8feSKyle Evans -- Look it up 751091c8feSKyle Evans local logodef = drawer.logodefs[logo] 761091c8feSKyle Evans 771091c8feSKyle Evans -- Try to pull it in 781091c8feSKyle Evans if logodef == nil then 791091c8feSKyle Evans try_include('logo-' .. logo) 801091c8feSKyle Evans logodef = drawer.logodefs[logo] 811091c8feSKyle Evans end 821091c8feSKyle Evans 831091c8feSKyle Evans return logodef 841091c8feSKyle Evansend 851091c8feSKyle Evans 866112ee09SKyle Evanslocal function draw(x, y, logo) 876112ee09SKyle Evans for i = 1, #logo do 886112ee09SKyle Evans screen.setcursor(x, y + i - 1) 896112ee09SKyle Evans printc(logo[i]) 906112ee09SKyle Evans end 916112ee09SKyle Evansend 926112ee09SKyle Evans 93*12eaa305SKyle Evanslocal function drawmenu(menudef) 94*12eaa305SKyle Evans local x = drawer.menu_position.x 95*12eaa305SKyle Evans local y = drawer.menu_position.y 96*12eaa305SKyle Evans 97*12eaa305SKyle Evans x = x + drawer.shift.x 98*12eaa305SKyle Evans y = y + drawer.shift.y 99*12eaa305SKyle Evans 100*12eaa305SKyle Evans -- print the menu and build the alias table 101*12eaa305SKyle Evans local alias_table = {} 102*12eaa305SKyle Evans local entry_num = 0 103*12eaa305SKyle Evans local menu_entries = menudef.entries 104*12eaa305SKyle Evans local effective_line_num = 0 105*12eaa305SKyle Evans if type(menu_entries) == "function" then 106*12eaa305SKyle Evans menu_entries = menu_entries() 107*12eaa305SKyle Evans end 108*12eaa305SKyle Evans for _, e in ipairs(menu_entries) do 109*12eaa305SKyle Evans -- Allow menu items to be conditionally visible by specifying 110*12eaa305SKyle Evans -- a visible function. 111*12eaa305SKyle Evans if e.visible ~= nil and not e.visible() then 112*12eaa305SKyle Evans goto continue 113*12eaa305SKyle Evans end 114*12eaa305SKyle Evans effective_line_num = effective_line_num + 1 115*12eaa305SKyle Evans if e.entry_type ~= core.MENU_SEPARATOR then 116*12eaa305SKyle Evans entry_num = entry_num + 1 117*12eaa305SKyle Evans screen.setcursor(x, y + effective_line_num) 118*12eaa305SKyle Evans 119*12eaa305SKyle Evans printc(entry_num .. ". " .. menuEntryName(menudef, e)) 120*12eaa305SKyle Evans 121*12eaa305SKyle Evans -- fill the alias table 122*12eaa305SKyle Evans alias_table[tostring(entry_num)] = e 123*12eaa305SKyle Evans if e.alias ~= nil then 124*12eaa305SKyle Evans for _, a in ipairs(e.alias) do 125*12eaa305SKyle Evans alias_table[a] = e 126*12eaa305SKyle Evans end 127*12eaa305SKyle Evans end 128*12eaa305SKyle Evans else 129*12eaa305SKyle Evans screen.setcursor(x, y + effective_line_num) 130*12eaa305SKyle Evans printc(menuEntryName(menudef, e)) 131*12eaa305SKyle Evans end 132*12eaa305SKyle Evans ::continue:: 133*12eaa305SKyle Evans end 134*12eaa305SKyle Evans return alias_table 135*12eaa305SKyle Evansend 136*12eaa305SKyle Evans 137*12eaa305SKyle Evanslocal function drawbox() 138*12eaa305SKyle Evans local x = drawer.menu_position.x - 3 139*12eaa305SKyle Evans local y = drawer.menu_position.y - 1 140*12eaa305SKyle Evans local w = drawer.frame_size.w 141*12eaa305SKyle Evans local h = drawer.frame_size.h 142*12eaa305SKyle Evans 143*12eaa305SKyle Evans local framestyle = loader.getenv("loader_menu_frame") or "double" 144*12eaa305SKyle Evans local framespec = drawer.frame_styles[framestyle] 145*12eaa305SKyle Evans -- If we don't have a framespec for the current frame style, just don't 146*12eaa305SKyle Evans -- draw a box. 147*12eaa305SKyle Evans if framespec == nil then 148*12eaa305SKyle Evans return 149*12eaa305SKyle Evans end 150*12eaa305SKyle Evans 151*12eaa305SKyle Evans local hl = framespec.horizontal 152*12eaa305SKyle Evans local vl = framespec.vertical 153*12eaa305SKyle Evans 154*12eaa305SKyle Evans local tl = framespec.top_left 155*12eaa305SKyle Evans local bl = framespec.bottom_left 156*12eaa305SKyle Evans local tr = framespec.top_right 157*12eaa305SKyle Evans local br = framespec.bottom_right 158*12eaa305SKyle Evans 159*12eaa305SKyle Evans x = x + drawer.shift.x 160*12eaa305SKyle Evans y = y + drawer.shift.y 161*12eaa305SKyle Evans 162*12eaa305SKyle Evans screen.setcursor(x, y); printc(tl) 163*12eaa305SKyle Evans screen.setcursor(x, y + h); printc(bl) 164*12eaa305SKyle Evans screen.setcursor(x + w, y); printc(tr) 165*12eaa305SKyle Evans screen.setcursor(x + w, y + h); printc(br) 166*12eaa305SKyle Evans 167*12eaa305SKyle Evans screen.setcursor(x + 1, y) 168*12eaa305SKyle Evans for _ = 1, w - 1 do 169*12eaa305SKyle Evans printc(hl) 170*12eaa305SKyle Evans end 171*12eaa305SKyle Evans 172*12eaa305SKyle Evans screen.setcursor(x + 1, y + h) 173*12eaa305SKyle Evans for _ = 1, w - 1 do 174*12eaa305SKyle Evans printc(hl) 175*12eaa305SKyle Evans end 176*12eaa305SKyle Evans 177*12eaa305SKyle Evans for i = 1, h - 1 do 178*12eaa305SKyle Evans screen.setcursor(x, y + i) 179*12eaa305SKyle Evans printc(vl) 180*12eaa305SKyle Evans screen.setcursor(x + w, y + i) 181*12eaa305SKyle Evans printc(vl) 182*12eaa305SKyle Evans end 183*12eaa305SKyle Evans 184*12eaa305SKyle Evans local menu_header = loader.getenv("loader_menu_title") or 185*12eaa305SKyle Evans "Welcome to FreeBSD" 186*12eaa305SKyle Evans local menu_header_align = loader.getenv("loader_menu_title_align") 187*12eaa305SKyle Evans local menu_header_x 188*12eaa305SKyle Evans 189*12eaa305SKyle Evans if menu_header_align ~= nil then 190*12eaa305SKyle Evans menu_header_align = menu_header_align:lower() 191*12eaa305SKyle Evans if menu_header_align == "left" then 192*12eaa305SKyle Evans -- Just inside the left border on top 193*12eaa305SKyle Evans menu_header_x = x + 1 194*12eaa305SKyle Evans elseif menu_header_align == "right" then 195*12eaa305SKyle Evans -- Just inside the right border on top 196*12eaa305SKyle Evans menu_header_x = x + w - #menu_header 197*12eaa305SKyle Evans end 198*12eaa305SKyle Evans end 199*12eaa305SKyle Evans if menu_header_x == nil then 200*12eaa305SKyle Evans menu_header_x = x + (w / 2) - (#menu_header / 2) 201*12eaa305SKyle Evans end 202*12eaa305SKyle Evans screen.setcursor(menu_header_x, y) 203*12eaa305SKyle Evans printc(menu_header) 204*12eaa305SKyle Evansend 205*12eaa305SKyle Evans 206*12eaa305SKyle Evanslocal function drawbrand() 207*12eaa305SKyle Evans local x = tonumber(loader.getenv("loader_brand_x")) or 208*12eaa305SKyle Evans drawer.brand_position.x 209*12eaa305SKyle Evans local y = tonumber(loader.getenv("loader_brand_y")) or 210*12eaa305SKyle Evans drawer.brand_position.y 211*12eaa305SKyle Evans 212*12eaa305SKyle Evans local branddef = getBranddef(loader.getenv("loader_brand")) 213*12eaa305SKyle Evans 214*12eaa305SKyle Evans if branddef == nil then 215*12eaa305SKyle Evans branddef = getBranddef(drawer.default_brand) 216*12eaa305SKyle Evans end 217*12eaa305SKyle Evans 218*12eaa305SKyle Evans local graphic = branddef.graphic 219*12eaa305SKyle Evans 220*12eaa305SKyle Evans x = x + drawer.shift.x 221*12eaa305SKyle Evans y = y + drawer.shift.y 222*12eaa305SKyle Evans draw(x, y, graphic) 223*12eaa305SKyle Evansend 224*12eaa305SKyle Evans 225*12eaa305SKyle Evanslocal function drawlogo() 226*12eaa305SKyle Evans local x = tonumber(loader.getenv("loader_logo_x")) or 227*12eaa305SKyle Evans drawer.logo_position.x 228*12eaa305SKyle Evans local y = tonumber(loader.getenv("loader_logo_y")) or 229*12eaa305SKyle Evans drawer.logo_position.y 230*12eaa305SKyle Evans 231*12eaa305SKyle Evans local logo = loader.getenv("loader_logo") 232*12eaa305SKyle Evans local colored = color.isEnabled() 233*12eaa305SKyle Evans 234*12eaa305SKyle Evans local logodef = getLogodef(logo) 235*12eaa305SKyle Evans 236*12eaa305SKyle Evans if logodef == nil or logodef.graphic == nil or 237*12eaa305SKyle Evans (not colored and logodef.requires_color) then 238*12eaa305SKyle Evans -- Choose a sensible default 239*12eaa305SKyle Evans if colored then 240*12eaa305SKyle Evans logodef = getLogodef("orb") 241*12eaa305SKyle Evans else 242*12eaa305SKyle Evans logodef = getLogodef("orbbw") 243*12eaa305SKyle Evans end 244*12eaa305SKyle Evans end 245*12eaa305SKyle Evans 246*12eaa305SKyle Evans if logodef ~= nil and logodef.graphic == none then 247*12eaa305SKyle Evans drawer.shift = logodef.shift 248*12eaa305SKyle Evans else 249*12eaa305SKyle Evans drawer.shift = drawer.default_shift 250*12eaa305SKyle Evans end 251*12eaa305SKyle Evans 252*12eaa305SKyle Evans x = x + drawer.shift.x 253*12eaa305SKyle Evans y = y + drawer.shift.y 254*12eaa305SKyle Evans 255*12eaa305SKyle Evans if logodef ~= nil and logodef.shift ~= nil then 256*12eaa305SKyle Evans x = x + logodef.shift.x 257*12eaa305SKyle Evans y = y + logodef.shift.y 258*12eaa305SKyle Evans end 259*12eaa305SKyle Evans 260*12eaa305SKyle Evans draw(x, y, logodef.graphic) 261*12eaa305SKyle Evansend 262*12eaa305SKyle Evans 2631091c8feSKyle Evansfbsd_brand = { 264088b4f5fSWarner Losh" ______ ____ _____ _____ ", 265088b4f5fSWarner Losh" | ____| | _ \\ / ____| __ \\ ", 266088b4f5fSWarner Losh" | |___ _ __ ___ ___ | |_) | (___ | | | |", 267088b4f5fSWarner Losh" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 268088b4f5fSWarner Losh" | | | | | __/ __/| |_) |____) | |__| |", 269088b4f5fSWarner Losh" | | | | | | || | | |", 270088b4f5fSWarner Losh" |_| |_| \\___|\\___||____/|_____/|_____/ " 271aedd6be5SKyle Evans} 272aedd6be5SKyle Evansnone = {""} 273088b4f5fSWarner Losh 274b5746545SKyle Evans-- Module exports 275e21e1dbeSKyle Evansdrawer.default_brand = 'fbsd' 276e21e1dbeSKyle Evans 277b5746545SKyle Evansdrawer.menu_name_handlers = { 278b5746545SKyle Evans -- Menu name handlers should take the menu being drawn and entry being 279b5746545SKyle Evans -- drawn as parameters, and return the name of the item. 280b5746545SKyle Evans -- This is designed so that everything, including menu separators, may 281b5746545SKyle Evans -- have their names derived differently. The default action for entry 282a51f9f0cSKyle Evans -- types not specified here is to use entry.name directly. 283e2df27e3SKyle Evans [core.MENU_SEPARATOR] = function(_, entry) 284dd65496aSKyle Evans if entry.name ~= nil then 285a51f9f0cSKyle Evans if type(entry.name) == "function" then 286dd65496aSKyle Evans return entry.name() 287dd65496aSKyle Evans end 288a51f9f0cSKyle Evans return entry.name 289a51f9f0cSKyle Evans end 290dd65496aSKyle Evans return "" 291dd65496aSKyle Evans end, 292e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 293aedd6be5SKyle Evans local carid = entry.carousel_id 294aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 2954f437f9eSKyle Evans local choices = entry.items 2964f437f9eSKyle Evans if type(choices) == "function" then 2974f437f9eSKyle Evans choices = choices() 2984f437f9eSKyle Evans end 2999f71d421SKyle Evans if #choices < caridx then 300aedd6be5SKyle Evans caridx = 1 301b5746545SKyle Evans end 302aedd6be5SKyle Evans return entry.name(caridx, choices[caridx], choices) 303b5746545SKyle Evans end, 304aedd6be5SKyle Evans} 305b5746545SKyle Evans 306aedd6be5SKyle Evansdrawer.brand_position = {x = 2, y = 1} 3071495c98fSKyle Evansdrawer.logo_position = {x = 46, y = 4} 3081495c98fSKyle Evansdrawer.menu_position = {x = 5, y = 10} 3091495c98fSKyle Evansdrawer.frame_size = {w = 42, h = 13} 3102d36799aSKyle Evansdrawer.default_shift = {x = 0, y = 0} 3112d36799aSKyle Evansdrawer.shift = drawer.default_shift 312b5746545SKyle Evans 31329aa5794SKyle Evansdrawer.branddefs = { 314699578a6SKyle Evans -- Indexed by valid values for loader_brand in loader.conf(5). Valid 315699578a6SKyle Evans -- keys are: graphic (table depicting graphic) 31629aa5794SKyle Evans ["fbsd"] = { 3171091c8feSKyle Evans graphic = fbsd_brand, 31829aa5794SKyle Evans }, 31929aa5794SKyle Evans ["none"] = { 32029aa5794SKyle Evans graphic = none, 32129aa5794SKyle Evans }, 322aedd6be5SKyle Evans} 32329aa5794SKyle Evans 3241091c8feSKyle Evansfunction drawer.addBrand(name, def) 3251091c8feSKyle Evans drawer.branddefs[name] = def 3261091c8feSKyle Evansend 3271091c8feSKyle Evans 3281091c8feSKyle Evansfunction drawer.addLogo(name, def) 3291091c8feSKyle Evans drawer.logodefs[name] = def 3301091c8feSKyle Evansend 3311091c8feSKyle Evans 332bb26c57dSKyle Evansdrawer.logodefs = { 333bb26c57dSKyle Evans -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 334752b2d40SKyle Evans -- are: requires_color (boolean), graphic (table depicting graphic), and 335bb26c57dSKyle Evans -- shift (table containing x and y). 336bb26c57dSKyle Evans ["tribute"] = { 3371091c8feSKyle Evans graphic = fbsd_brand, 338bb26c57dSKyle Evans }, 339bb26c57dSKyle Evans ["tributebw"] = { 3401091c8feSKyle Evans graphic = fbsd_brand, 341bb26c57dSKyle Evans }, 342bb26c57dSKyle Evans ["none"] = { 343752b2d40SKyle Evans graphic = none, 344bb26c57dSKyle Evans shift = {x = 17, y = 0}, 345bb26c57dSKyle Evans }, 346aedd6be5SKyle Evans} 347bb26c57dSKyle Evans 348379e652eSKyle Evansdrawer.frame_styles = { 349379e652eSKyle Evans -- Indexed by valid values for loader_menu_frame in loader.conf(5). 350379e652eSKyle Evans -- All of the keys appearing below must be set for any menu frame style 351379e652eSKyle Evans -- added to drawer.frame_styles. 352379e652eSKyle Evans ["ascii"] = { 353379e652eSKyle Evans horizontal = "-", 354379e652eSKyle Evans vertical = "|", 355379e652eSKyle Evans top_left = "+", 356379e652eSKyle Evans bottom_left = "+", 357379e652eSKyle Evans top_right = "+", 358379e652eSKyle Evans bottom_right = "+", 359379e652eSKyle Evans }, 360379e652eSKyle Evans ["single"] = { 361379e652eSKyle Evans horizontal = "\xC4", 362379e652eSKyle Evans vertical = "\xB3", 363379e652eSKyle Evans top_left = "\xDA", 364379e652eSKyle Evans bottom_left = "\xC0", 365379e652eSKyle Evans top_right = "\xBF", 366379e652eSKyle Evans bottom_right = "\xD9", 367379e652eSKyle Evans }, 368379e652eSKyle Evans ["double"] = { 369379e652eSKyle Evans horizontal = "\xCD", 370379e652eSKyle Evans vertical = "\xBA", 371379e652eSKyle Evans top_left = "\xC9", 372379e652eSKyle Evans bottom_left = "\xC8", 373379e652eSKyle Evans top_right = "\xBB", 374379e652eSKyle Evans bottom_right = "\xBC", 375379e652eSKyle Evans }, 376379e652eSKyle Evans} 377379e652eSKyle Evans 378*12eaa305SKyle Evansfunction drawer.drawscreen(menudef) 379088b4f5fSWarner Losh -- drawlogo() must go first. 380088b4f5fSWarner Losh -- it determines the positions of other elements 381*12eaa305SKyle Evans drawlogo() 382*12eaa305SKyle Evans drawbrand() 383*12eaa305SKyle Evans drawbox() 384*12eaa305SKyle Evans return drawmenu(menudef) 385088b4f5fSWarner Loshend 386088b4f5fSWarner Losh 387aedd6be5SKyle Evansreturn drawer 388