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 86*6112ee09SKyle Evanslocal function draw(x, y, logo) 87*6112ee09SKyle Evans for i = 1, #logo do 88*6112ee09SKyle Evans screen.setcursor(x, y + i - 1) 89*6112ee09SKyle Evans printc(logo[i]) 90*6112ee09SKyle Evans end 91*6112ee09SKyle Evansend 92*6112ee09SKyle Evans 931091c8feSKyle Evansfbsd_brand = { 94088b4f5fSWarner Losh" ______ ____ _____ _____ ", 95088b4f5fSWarner Losh" | ____| | _ \\ / ____| __ \\ ", 96088b4f5fSWarner Losh" | |___ _ __ ___ ___ | |_) | (___ | | | |", 97088b4f5fSWarner Losh" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 98088b4f5fSWarner Losh" | | | | | __/ __/| |_) |____) | |__| |", 99088b4f5fSWarner Losh" | | | | | | || | | |", 100088b4f5fSWarner Losh" |_| |_| \\___|\\___||____/|_____/|_____/ " 101aedd6be5SKyle Evans} 102aedd6be5SKyle Evansnone = {""} 103088b4f5fSWarner Losh 104b5746545SKyle Evans-- Module exports 105e21e1dbeSKyle Evansdrawer.default_brand = 'fbsd' 106e21e1dbeSKyle Evans 107b5746545SKyle Evansdrawer.menu_name_handlers = { 108b5746545SKyle Evans -- Menu name handlers should take the menu being drawn and entry being 109b5746545SKyle Evans -- drawn as parameters, and return the name of the item. 110b5746545SKyle Evans -- This is designed so that everything, including menu separators, may 111b5746545SKyle Evans -- have their names derived differently. The default action for entry 112a51f9f0cSKyle Evans -- types not specified here is to use entry.name directly. 113e2df27e3SKyle Evans [core.MENU_SEPARATOR] = function(_, entry) 114dd65496aSKyle Evans if entry.name ~= nil then 115a51f9f0cSKyle Evans if type(entry.name) == "function" then 116dd65496aSKyle Evans return entry.name() 117dd65496aSKyle Evans end 118a51f9f0cSKyle Evans return entry.name 119a51f9f0cSKyle Evans end 120dd65496aSKyle Evans return "" 121dd65496aSKyle Evans end, 122e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 123aedd6be5SKyle Evans local carid = entry.carousel_id 124aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 1254f437f9eSKyle Evans local choices = entry.items 1264f437f9eSKyle Evans if type(choices) == "function" then 1274f437f9eSKyle Evans choices = choices() 1284f437f9eSKyle Evans end 1299f71d421SKyle Evans if #choices < caridx then 130aedd6be5SKyle Evans caridx = 1 131b5746545SKyle Evans end 132aedd6be5SKyle Evans return entry.name(caridx, choices[caridx], choices) 133b5746545SKyle Evans end, 134aedd6be5SKyle Evans} 135b5746545SKyle Evans 136aedd6be5SKyle Evansdrawer.brand_position = {x = 2, y = 1} 1371495c98fSKyle Evansdrawer.logo_position = {x = 46, y = 4} 1381495c98fSKyle Evansdrawer.menu_position = {x = 5, y = 10} 1391495c98fSKyle Evansdrawer.frame_size = {w = 42, h = 13} 1402d36799aSKyle Evansdrawer.default_shift = {x = 0, y = 0} 1412d36799aSKyle Evansdrawer.shift = drawer.default_shift 142b5746545SKyle Evans 14329aa5794SKyle Evansdrawer.branddefs = { 144699578a6SKyle Evans -- Indexed by valid values for loader_brand in loader.conf(5). Valid 145699578a6SKyle Evans -- keys are: graphic (table depicting graphic) 14629aa5794SKyle Evans ["fbsd"] = { 1471091c8feSKyle Evans graphic = fbsd_brand, 14829aa5794SKyle Evans }, 14929aa5794SKyle Evans ["none"] = { 15029aa5794SKyle Evans graphic = none, 15129aa5794SKyle Evans }, 152aedd6be5SKyle Evans} 15329aa5794SKyle Evans 1541091c8feSKyle Evansfunction drawer.addBrand(name, def) 1551091c8feSKyle Evans drawer.branddefs[name] = def 1561091c8feSKyle Evansend 1571091c8feSKyle Evans 1581091c8feSKyle Evansfunction drawer.addLogo(name, def) 1591091c8feSKyle Evans drawer.logodefs[name] = def 1601091c8feSKyle Evansend 1611091c8feSKyle Evans 162bb26c57dSKyle Evansdrawer.logodefs = { 163bb26c57dSKyle Evans -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 164752b2d40SKyle Evans -- are: requires_color (boolean), graphic (table depicting graphic), and 165bb26c57dSKyle Evans -- shift (table containing x and y). 166bb26c57dSKyle Evans ["tribute"] = { 1671091c8feSKyle Evans graphic = fbsd_brand, 168bb26c57dSKyle Evans }, 169bb26c57dSKyle Evans ["tributebw"] = { 1701091c8feSKyle Evans graphic = fbsd_brand, 171bb26c57dSKyle Evans }, 172bb26c57dSKyle Evans ["none"] = { 173752b2d40SKyle Evans graphic = none, 174bb26c57dSKyle Evans shift = {x = 17, y = 0}, 175bb26c57dSKyle Evans }, 176aedd6be5SKyle Evans} 177bb26c57dSKyle Evans 178379e652eSKyle Evansdrawer.frame_styles = { 179379e652eSKyle Evans -- Indexed by valid values for loader_menu_frame in loader.conf(5). 180379e652eSKyle Evans -- All of the keys appearing below must be set for any menu frame style 181379e652eSKyle Evans -- added to drawer.frame_styles. 182379e652eSKyle Evans ["ascii"] = { 183379e652eSKyle Evans horizontal = "-", 184379e652eSKyle Evans vertical = "|", 185379e652eSKyle Evans top_left = "+", 186379e652eSKyle Evans bottom_left = "+", 187379e652eSKyle Evans top_right = "+", 188379e652eSKyle Evans bottom_right = "+", 189379e652eSKyle Evans }, 190379e652eSKyle Evans ["single"] = { 191379e652eSKyle Evans horizontal = "\xC4", 192379e652eSKyle Evans vertical = "\xB3", 193379e652eSKyle Evans top_left = "\xDA", 194379e652eSKyle Evans bottom_left = "\xC0", 195379e652eSKyle Evans top_right = "\xBF", 196379e652eSKyle Evans bottom_right = "\xD9", 197379e652eSKyle Evans }, 198379e652eSKyle Evans ["double"] = { 199379e652eSKyle Evans horizontal = "\xCD", 200379e652eSKyle Evans vertical = "\xBA", 201379e652eSKyle Evans top_left = "\xC9", 202379e652eSKyle Evans bottom_left = "\xC8", 203379e652eSKyle Evans top_right = "\xBB", 204379e652eSKyle Evans bottom_right = "\xBC", 205379e652eSKyle Evans }, 206379e652eSKyle Evans} 207379e652eSKyle Evans 208088b4f5fSWarner Loshfunction drawer.drawscreen(menu_opts) 209088b4f5fSWarner Losh -- drawlogo() must go first. 210088b4f5fSWarner Losh -- it determines the positions of other elements 211aedd6be5SKyle Evans drawer.drawlogo() 212aedd6be5SKyle Evans drawer.drawbrand() 213aedd6be5SKyle Evans drawer.drawbox() 214aedd6be5SKyle Evans return drawer.drawmenu(menu_opts) 215088b4f5fSWarner Loshend 216088b4f5fSWarner Losh 21704af4229SKyle Evansfunction drawer.drawmenu(menudef) 218e2df27e3SKyle Evans local x = drawer.menu_position.x 219e2df27e3SKyle Evans local y = drawer.menu_position.y 220088b4f5fSWarner Losh 2212d36799aSKyle Evans x = x + drawer.shift.x 2222d36799aSKyle Evans y = y + drawer.shift.y 2232d36799aSKyle Evans 224088b4f5fSWarner Losh -- print the menu and build the alias table 225aedd6be5SKyle Evans local alias_table = {} 226aedd6be5SKyle Evans local entry_num = 0 22704af4229SKyle Evans local menu_entries = menudef.entries 2281afbc37aSKyle Evans local effective_line_num = 0 2299f71d421SKyle Evans if type(menu_entries) == "function" then 230aedd6be5SKyle Evans menu_entries = menu_entries() 2312e716cecSKyle Evans end 232d709f254SKyle Evans for _, e in ipairs(menu_entries) do 2334a4fb4f8SKyle Evans -- Allow menu items to be conditionally visible by specifying 2344a4fb4f8SKyle Evans -- a visible function. 2359f71d421SKyle Evans if e.visible ~= nil and not e.visible() then 236aedd6be5SKyle Evans goto continue 2374a4fb4f8SKyle Evans end 2381afbc37aSKyle Evans effective_line_num = effective_line_num + 1 2399f71d421SKyle Evans if e.entry_type ~= core.MENU_SEPARATOR then 240aedd6be5SKyle Evans entry_num = entry_num + 1 2411afbc37aSKyle Evans screen.setcursor(x, y + effective_line_num) 242ada26c4aSKyle Evans 2439895e5d4SKyle Evans printc(entry_num .. ". " .. menuEntryName(menudef, e)) 244088b4f5fSWarner Losh 245088b4f5fSWarner Losh -- fill the alias table 246aedd6be5SKyle Evans alias_table[tostring(entry_num)] = e 2479f71d421SKyle Evans if e.alias ~= nil then 248e2df27e3SKyle Evans for _, a in ipairs(e.alias) do 249aedd6be5SKyle Evans alias_table[a] = e 250088b4f5fSWarner Losh end 251196ba166SKyle Evans end 252088b4f5fSWarner Losh else 2531afbc37aSKyle Evans screen.setcursor(x, y + effective_line_num) 2549895e5d4SKyle Evans printc(menuEntryName(menudef, e)) 255088b4f5fSWarner Losh end 2564a4fb4f8SKyle Evans ::continue:: 257088b4f5fSWarner Losh end 258aedd6be5SKyle Evans return alias_table 259088b4f5fSWarner Loshend 260088b4f5fSWarner Losh 261088b4f5fSWarner Loshfunction drawer.drawbox() 2621495c98fSKyle Evans local x = drawer.menu_position.x - 3 2631495c98fSKyle Evans local y = drawer.menu_position.y - 1 2641495c98fSKyle Evans local w = drawer.frame_size.w 2651495c98fSKyle Evans local h = drawer.frame_size.h 266088b4f5fSWarner Losh 267379e652eSKyle Evans local framestyle = loader.getenv("loader_menu_frame") or "double" 268379e652eSKyle Evans local framespec = drawer.frame_styles[framestyle] 269379e652eSKyle Evans -- If we don't have a framespec for the current frame style, just don't 270379e652eSKyle Evans -- draw a box. 271379e652eSKyle Evans if framespec == nil then 272379e652eSKyle Evans return 273379e652eSKyle Evans end 274088b4f5fSWarner Losh 275379e652eSKyle Evans local hl = framespec.horizontal 276379e652eSKyle Evans local vl = framespec.vertical 277088b4f5fSWarner Losh 278379e652eSKyle Evans local tl = framespec.top_left 279379e652eSKyle Evans local bl = framespec.bottom_left 280379e652eSKyle Evans local tr = framespec.top_right 281379e652eSKyle Evans local br = framespec.bottom_right 282088b4f5fSWarner Losh 2832d36799aSKyle Evans x = x + drawer.shift.x 2842d36799aSKyle Evans y = y + drawer.shift.y 2852d36799aSKyle Evans 286223e9874SKyle Evans screen.setcursor(x, y); printc(tl) 287223e9874SKyle Evans screen.setcursor(x, y + h); printc(bl) 288223e9874SKyle Evans screen.setcursor(x + w, y); printc(tr) 289223e9874SKyle Evans screen.setcursor(x + w, y + h); printc(br) 290379e652eSKyle Evans 291379e652eSKyle Evans screen.setcursor(x + 1, y) 292379e652eSKyle Evans for _ = 1, w - 1 do 293223e9874SKyle Evans printc(hl) 294379e652eSKyle Evans end 295379e652eSKyle Evans 296379e652eSKyle Evans screen.setcursor(x + 1, y + h) 297379e652eSKyle Evans for _ = 1, w - 1 do 298223e9874SKyle Evans printc(hl) 299088b4f5fSWarner Losh end 300088b4f5fSWarner Losh 301088b4f5fSWarner Losh for i = 1, h - 1 do 302aedd6be5SKyle Evans screen.setcursor(x, y + i) 303223e9874SKyle Evans printc(vl) 304aedd6be5SKyle Evans screen.setcursor(x + w, y + i) 305223e9874SKyle Evans printc(vl) 306088b4f5fSWarner Losh end 307088b4f5fSWarner Losh 308953d8937SKyle Evans local menu_header = loader.getenv("loader_menu_title") or 309953d8937SKyle Evans "Welcome to FreeBSD" 310b4353326SKyle Evans local menu_header_align = loader.getenv("loader_menu_title_align") 311953d8937SKyle Evans local menu_header_x 312953d8937SKyle Evans 313b4353326SKyle Evans if menu_header_align ~= nil then 314b4353326SKyle Evans menu_header_align = menu_header_align:lower() 315b4353326SKyle Evans if menu_header_align == "left" then 316b4353326SKyle Evans -- Just inside the left border on top 317b4353326SKyle Evans menu_header_x = x + 1 318b4353326SKyle Evans elseif menu_header_align == "right" then 319b4353326SKyle Evans -- Just inside the right border on top 320b4353326SKyle Evans menu_header_x = x + w - #menu_header 321b4353326SKyle Evans end 322b4353326SKyle Evans end 323b4353326SKyle Evans if menu_header_x == nil then 324953d8937SKyle Evans menu_header_x = x + (w / 2) - (#menu_header / 2) 325b4353326SKyle Evans end 326953d8937SKyle Evans screen.setcursor(menu_header_x, y) 327953d8937SKyle Evans printc(menu_header) 328088b4f5fSWarner Loshend 329088b4f5fSWarner Losh 330088b4f5fSWarner Loshfunction drawer.drawbrand() 33124a1bd54SKyle Evans local x = tonumber(loader.getenv("loader_brand_x")) or 332aedd6be5SKyle Evans drawer.brand_position.x 33324a1bd54SKyle Evans local y = tonumber(loader.getenv("loader_brand_y")) or 334aedd6be5SKyle Evans drawer.brand_position.y 335088b4f5fSWarner Losh 336e21e1dbeSKyle Evans local branddef = getBranddef(loader.getenv("loader_brand")) 337e21e1dbeSKyle Evans 338e21e1dbeSKyle Evans if branddef == nil then 339e21e1dbeSKyle Evans branddef = getBranddef(drawer.default_brand) 340e21e1dbeSKyle Evans end 341e21e1dbeSKyle Evans 342e21e1dbeSKyle Evans local graphic = branddef.graphic 3432d36799aSKyle Evans 3442d36799aSKyle Evans x = x + drawer.shift.x 3452d36799aSKyle Evans y = y + drawer.shift.y 346*6112ee09SKyle Evans draw(x, y, graphic) 347088b4f5fSWarner Loshend 348088b4f5fSWarner Losh 349088b4f5fSWarner Loshfunction drawer.drawlogo() 35024a1bd54SKyle Evans local x = tonumber(loader.getenv("loader_logo_x")) or 351aedd6be5SKyle Evans drawer.logo_position.x 35224a1bd54SKyle Evans local y = tonumber(loader.getenv("loader_logo_y")) or 353aedd6be5SKyle Evans drawer.logo_position.y 354088b4f5fSWarner Losh 355aedd6be5SKyle Evans local logo = loader.getenv("loader_logo") 356aedd6be5SKyle Evans local colored = color.isEnabled() 357088b4f5fSWarner Losh 3581091c8feSKyle Evans local logodef = getLogodef(logo) 359bb26c57dSKyle Evans 3602d36799aSKyle Evans if logodef == nil or logodef.graphic == nil or 3619f71d421SKyle Evans (not colored and logodef.requires_color) then 362bb26c57dSKyle Evans -- Choose a sensible default 3639f71d421SKyle Evans if colored then 3641091c8feSKyle Evans logodef = getLogodef("orb") 365088b4f5fSWarner Losh else 3661091c8feSKyle Evans logodef = getLogodef("orbbw") 367088b4f5fSWarner Losh end 368088b4f5fSWarner Losh end 3692d36799aSKyle Evans 3702d36799aSKyle Evans if logodef ~= nil and logodef.graphic == none then 3712d36799aSKyle Evans drawer.shift = logodef.shift 3722d36799aSKyle Evans else 3732d36799aSKyle Evans drawer.shift = drawer.default_shift 3742d36799aSKyle Evans end 3752d36799aSKyle Evans 3762d36799aSKyle Evans x = x + drawer.shift.x 3772d36799aSKyle Evans y = y + drawer.shift.y 3782d36799aSKyle Evans 3792ed9eb5dSKyle Evans if logodef ~= nil and logodef.shift ~= nil then 380aedd6be5SKyle Evans x = x + logodef.shift.x 381aedd6be5SKyle Evans y = y + logodef.shift.y 382bb26c57dSKyle Evans end 3832d36799aSKyle Evans 384*6112ee09SKyle Evans draw(x, y, logodef.graphic) 385088b4f5fSWarner Loshend 386088b4f5fSWarner Losh 387aedd6be5SKyle Evansreturn drawer 388