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 541091c8feSKyle Evanslocal function getLogodef(logo) 55*bbb516aeSKyle Evans if logo == nil then 56*bbb516aeSKyle Evans return nil 57*bbb516aeSKyle Evans end 581091c8feSKyle Evans -- Look it up 591091c8feSKyle Evans local logodef = drawer.logodefs[logo] 601091c8feSKyle Evans 611091c8feSKyle Evans -- Try to pull it in 621091c8feSKyle Evans if logodef == nil then 631091c8feSKyle Evans try_include('logo-' .. logo) 641091c8feSKyle Evans logodef = drawer.logodefs[logo] 651091c8feSKyle Evans end 661091c8feSKyle Evans 671091c8feSKyle Evans return logodef 681091c8feSKyle Evansend 691091c8feSKyle Evans 701091c8feSKyle Evansfbsd_brand = { 71088b4f5fSWarner Losh" ______ ____ _____ _____ ", 72088b4f5fSWarner Losh" | ____| | _ \\ / ____| __ \\ ", 73088b4f5fSWarner Losh" | |___ _ __ ___ ___ | |_) | (___ | | | |", 74088b4f5fSWarner Losh" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 75088b4f5fSWarner Losh" | | | | | __/ __/| |_) |____) | |__| |", 76088b4f5fSWarner Losh" | | | | | | || | | |", 77088b4f5fSWarner Losh" |_| |_| \\___|\\___||____/|_____/|_____/ " 78aedd6be5SKyle Evans} 79aedd6be5SKyle Evansnone = {""} 80088b4f5fSWarner Losh 81b5746545SKyle Evans-- Module exports 82b5746545SKyle Evansdrawer.menu_name_handlers = { 83b5746545SKyle Evans -- Menu name handlers should take the menu being drawn and entry being 84b5746545SKyle Evans -- drawn as parameters, and return the name of the item. 85b5746545SKyle Evans -- This is designed so that everything, including menu separators, may 86b5746545SKyle Evans -- have their names derived differently. The default action for entry 87a51f9f0cSKyle Evans -- types not specified here is to use entry.name directly. 88e2df27e3SKyle Evans [core.MENU_SEPARATOR] = function(_, entry) 89dd65496aSKyle Evans if entry.name ~= nil then 90a51f9f0cSKyle Evans if type(entry.name) == "function" then 91dd65496aSKyle Evans return entry.name() 92dd65496aSKyle Evans end 93a51f9f0cSKyle Evans return entry.name 94a51f9f0cSKyle Evans end 95dd65496aSKyle Evans return "" 96dd65496aSKyle Evans end, 97e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 98aedd6be5SKyle Evans local carid = entry.carousel_id 99aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 1004f437f9eSKyle Evans local choices = entry.items 1014f437f9eSKyle Evans if type(choices) == "function" then 1024f437f9eSKyle Evans choices = choices() 1034f437f9eSKyle Evans end 1049f71d421SKyle Evans if #choices < caridx then 105aedd6be5SKyle Evans caridx = 1 106b5746545SKyle Evans end 107aedd6be5SKyle Evans return entry.name(caridx, choices[caridx], choices) 108b5746545SKyle Evans end, 109aedd6be5SKyle Evans} 110b5746545SKyle Evans 111aedd6be5SKyle Evansdrawer.brand_position = {x = 2, y = 1} 1121495c98fSKyle Evansdrawer.logo_position = {x = 46, y = 4} 1131495c98fSKyle Evansdrawer.menu_position = {x = 5, y = 10} 1141495c98fSKyle Evansdrawer.frame_size = {w = 42, h = 13} 1152d36799aSKyle Evansdrawer.default_shift = {x = 0, y = 0} 1162d36799aSKyle Evansdrawer.shift = drawer.default_shift 117b5746545SKyle Evans 11829aa5794SKyle Evansdrawer.branddefs = { 119699578a6SKyle Evans -- Indexed by valid values for loader_brand in loader.conf(5). Valid 120699578a6SKyle Evans -- keys are: graphic (table depicting graphic) 12129aa5794SKyle Evans ["fbsd"] = { 1221091c8feSKyle Evans graphic = fbsd_brand, 12329aa5794SKyle Evans }, 12429aa5794SKyle Evans ["none"] = { 12529aa5794SKyle Evans graphic = none, 12629aa5794SKyle Evans }, 127aedd6be5SKyle Evans} 12829aa5794SKyle Evans 1291091c8feSKyle Evansfunction drawer.addBrand(name, def) 1301091c8feSKyle Evans drawer.branddefs[name] = def 1311091c8feSKyle Evansend 1321091c8feSKyle Evans 1331091c8feSKyle Evansfunction drawer.addLogo(name, def) 1341091c8feSKyle Evans drawer.logodefs[name] = def 1351091c8feSKyle Evansend 1361091c8feSKyle Evans 137bb26c57dSKyle Evansdrawer.logodefs = { 138bb26c57dSKyle Evans -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 139752b2d40SKyle Evans -- are: requires_color (boolean), graphic (table depicting graphic), and 140bb26c57dSKyle Evans -- shift (table containing x and y). 141bb26c57dSKyle Evans ["tribute"] = { 1421091c8feSKyle Evans graphic = fbsd_brand, 143bb26c57dSKyle Evans }, 144bb26c57dSKyle Evans ["tributebw"] = { 1451091c8feSKyle Evans graphic = fbsd_brand, 146bb26c57dSKyle Evans }, 147bb26c57dSKyle Evans ["none"] = { 148752b2d40SKyle Evans graphic = none, 149bb26c57dSKyle Evans shift = {x = 17, y = 0}, 150bb26c57dSKyle Evans }, 151aedd6be5SKyle Evans} 152bb26c57dSKyle Evans 153379e652eSKyle Evansdrawer.frame_styles = { 154379e652eSKyle Evans -- Indexed by valid values for loader_menu_frame in loader.conf(5). 155379e652eSKyle Evans -- All of the keys appearing below must be set for any menu frame style 156379e652eSKyle Evans -- added to drawer.frame_styles. 157379e652eSKyle Evans ["ascii"] = { 158379e652eSKyle Evans horizontal = "-", 159379e652eSKyle Evans vertical = "|", 160379e652eSKyle Evans top_left = "+", 161379e652eSKyle Evans bottom_left = "+", 162379e652eSKyle Evans top_right = "+", 163379e652eSKyle Evans bottom_right = "+", 164379e652eSKyle Evans }, 165379e652eSKyle Evans ["single"] = { 166379e652eSKyle Evans horizontal = "\xC4", 167379e652eSKyle Evans vertical = "\xB3", 168379e652eSKyle Evans top_left = "\xDA", 169379e652eSKyle Evans bottom_left = "\xC0", 170379e652eSKyle Evans top_right = "\xBF", 171379e652eSKyle Evans bottom_right = "\xD9", 172379e652eSKyle Evans }, 173379e652eSKyle Evans ["double"] = { 174379e652eSKyle Evans horizontal = "\xCD", 175379e652eSKyle Evans vertical = "\xBA", 176379e652eSKyle Evans top_left = "\xC9", 177379e652eSKyle Evans bottom_left = "\xC8", 178379e652eSKyle Evans top_right = "\xBB", 179379e652eSKyle Evans bottom_right = "\xBC", 180379e652eSKyle Evans }, 181379e652eSKyle Evans} 182379e652eSKyle Evans 183088b4f5fSWarner Loshfunction drawer.drawscreen(menu_opts) 184088b4f5fSWarner Losh -- drawlogo() must go first. 185088b4f5fSWarner Losh -- it determines the positions of other elements 186aedd6be5SKyle Evans drawer.drawlogo() 187aedd6be5SKyle Evans drawer.drawbrand() 188aedd6be5SKyle Evans drawer.drawbox() 189aedd6be5SKyle Evans return drawer.drawmenu(menu_opts) 190088b4f5fSWarner Loshend 191088b4f5fSWarner Losh 19204af4229SKyle Evansfunction drawer.drawmenu(menudef) 193e2df27e3SKyle Evans local x = drawer.menu_position.x 194e2df27e3SKyle Evans local y = drawer.menu_position.y 195088b4f5fSWarner Losh 1962d36799aSKyle Evans x = x + drawer.shift.x 1972d36799aSKyle Evans y = y + drawer.shift.y 1982d36799aSKyle Evans 199088b4f5fSWarner Losh -- print the menu and build the alias table 200aedd6be5SKyle Evans local alias_table = {} 201aedd6be5SKyle Evans local entry_num = 0 20204af4229SKyle Evans local menu_entries = menudef.entries 2031afbc37aSKyle Evans local effective_line_num = 0 2049f71d421SKyle Evans if type(menu_entries) == "function" then 205aedd6be5SKyle Evans menu_entries = menu_entries() 2062e716cecSKyle Evans end 207d709f254SKyle Evans for _, e in ipairs(menu_entries) do 2084a4fb4f8SKyle Evans -- Allow menu items to be conditionally visible by specifying 2094a4fb4f8SKyle Evans -- a visible function. 2109f71d421SKyle Evans if e.visible ~= nil and not e.visible() then 211aedd6be5SKyle Evans goto continue 2124a4fb4f8SKyle Evans end 2131afbc37aSKyle Evans effective_line_num = effective_line_num + 1 2149f71d421SKyle Evans if e.entry_type ~= core.MENU_SEPARATOR then 215aedd6be5SKyle Evans entry_num = entry_num + 1 2161afbc37aSKyle Evans screen.setcursor(x, y + effective_line_num) 217ada26c4aSKyle Evans 2189895e5d4SKyle Evans printc(entry_num .. ". " .. menuEntryName(menudef, e)) 219088b4f5fSWarner Losh 220088b4f5fSWarner Losh -- fill the alias table 221aedd6be5SKyle Evans alias_table[tostring(entry_num)] = e 2229f71d421SKyle Evans if e.alias ~= nil then 223e2df27e3SKyle Evans for _, a in ipairs(e.alias) do 224aedd6be5SKyle Evans alias_table[a] = e 225088b4f5fSWarner Losh end 226196ba166SKyle Evans end 227088b4f5fSWarner Losh else 2281afbc37aSKyle Evans screen.setcursor(x, y + effective_line_num) 2299895e5d4SKyle Evans printc(menuEntryName(menudef, e)) 230088b4f5fSWarner Losh end 2314a4fb4f8SKyle Evans ::continue:: 232088b4f5fSWarner Losh end 233aedd6be5SKyle Evans return alias_table 234088b4f5fSWarner Loshend 235088b4f5fSWarner Losh 236088b4f5fSWarner Loshfunction drawer.drawbox() 2371495c98fSKyle Evans local x = drawer.menu_position.x - 3 2381495c98fSKyle Evans local y = drawer.menu_position.y - 1 2391495c98fSKyle Evans local w = drawer.frame_size.w 2401495c98fSKyle Evans local h = drawer.frame_size.h 241088b4f5fSWarner Losh 242379e652eSKyle Evans local framestyle = loader.getenv("loader_menu_frame") or "double" 243379e652eSKyle Evans local framespec = drawer.frame_styles[framestyle] 244379e652eSKyle Evans -- If we don't have a framespec for the current frame style, just don't 245379e652eSKyle Evans -- draw a box. 246379e652eSKyle Evans if framespec == nil then 247379e652eSKyle Evans return 248379e652eSKyle Evans end 249088b4f5fSWarner Losh 250379e652eSKyle Evans local hl = framespec.horizontal 251379e652eSKyle Evans local vl = framespec.vertical 252088b4f5fSWarner Losh 253379e652eSKyle Evans local tl = framespec.top_left 254379e652eSKyle Evans local bl = framespec.bottom_left 255379e652eSKyle Evans local tr = framespec.top_right 256379e652eSKyle Evans local br = framespec.bottom_right 257088b4f5fSWarner Losh 2582d36799aSKyle Evans x = x + drawer.shift.x 2592d36799aSKyle Evans y = y + drawer.shift.y 2602d36799aSKyle Evans 261223e9874SKyle Evans screen.setcursor(x, y); printc(tl) 262223e9874SKyle Evans screen.setcursor(x, y + h); printc(bl) 263223e9874SKyle Evans screen.setcursor(x + w, y); printc(tr) 264223e9874SKyle Evans screen.setcursor(x + w, y + h); printc(br) 265379e652eSKyle Evans 266379e652eSKyle Evans screen.setcursor(x + 1, y) 267379e652eSKyle Evans for _ = 1, w - 1 do 268223e9874SKyle Evans printc(hl) 269379e652eSKyle Evans end 270379e652eSKyle Evans 271379e652eSKyle Evans screen.setcursor(x + 1, y + h) 272379e652eSKyle Evans for _ = 1, w - 1 do 273223e9874SKyle Evans printc(hl) 274088b4f5fSWarner Losh end 275088b4f5fSWarner Losh 276088b4f5fSWarner Losh for i = 1, h - 1 do 277aedd6be5SKyle Evans screen.setcursor(x, y + i) 278223e9874SKyle Evans printc(vl) 279aedd6be5SKyle Evans screen.setcursor(x + w, y + i) 280223e9874SKyle Evans printc(vl) 281088b4f5fSWarner Losh end 282088b4f5fSWarner Losh 283953d8937SKyle Evans local menu_header = loader.getenv("loader_menu_title") or 284953d8937SKyle Evans "Welcome to FreeBSD" 285b4353326SKyle Evans local menu_header_align = loader.getenv("loader_menu_title_align") 286953d8937SKyle Evans local menu_header_x 287953d8937SKyle Evans 288b4353326SKyle Evans if menu_header_align ~= nil then 289b4353326SKyle Evans menu_header_align = menu_header_align:lower() 290b4353326SKyle Evans if menu_header_align == "left" then 291b4353326SKyle Evans -- Just inside the left border on top 292b4353326SKyle Evans menu_header_x = x + 1 293b4353326SKyle Evans elseif menu_header_align == "right" then 294b4353326SKyle Evans -- Just inside the right border on top 295b4353326SKyle Evans menu_header_x = x + w - #menu_header 296b4353326SKyle Evans end 297b4353326SKyle Evans end 298b4353326SKyle Evans if menu_header_x == nil then 299953d8937SKyle Evans menu_header_x = x + (w / 2) - (#menu_header / 2) 300b4353326SKyle Evans end 301953d8937SKyle Evans screen.setcursor(menu_header_x, y) 302953d8937SKyle Evans printc(menu_header) 303088b4f5fSWarner Loshend 304088b4f5fSWarner Losh 305088b4f5fSWarner Loshfunction drawer.draw(x, y, logo) 306088b4f5fSWarner Losh for i = 1, #logo do 3071495c98fSKyle Evans screen.setcursor(x, y + i - 1) 3081495c98fSKyle Evans printc(logo[i]) 309088b4f5fSWarner Losh end 310088b4f5fSWarner Loshend 311088b4f5fSWarner Losh 312088b4f5fSWarner Loshfunction drawer.drawbrand() 31324a1bd54SKyle Evans local x = tonumber(loader.getenv("loader_brand_x")) or 314aedd6be5SKyle Evans drawer.brand_position.x 31524a1bd54SKyle Evans local y = tonumber(loader.getenv("loader_brand_y")) or 316aedd6be5SKyle Evans drawer.brand_position.y 317088b4f5fSWarner Losh 3181091c8feSKyle Evans local graphic = drawer.branddefs[loader.getenv("loader_brand")] or 3191091c8feSKyle Evans fbsd_brand 3202d36799aSKyle Evans 3212d36799aSKyle Evans x = x + drawer.shift.x 3222d36799aSKyle Evans y = y + drawer.shift.y 323aedd6be5SKyle Evans drawer.draw(x, y, graphic) 324088b4f5fSWarner Loshend 325088b4f5fSWarner Losh 326088b4f5fSWarner Loshfunction drawer.drawlogo() 32724a1bd54SKyle Evans local x = tonumber(loader.getenv("loader_logo_x")) or 328aedd6be5SKyle Evans drawer.logo_position.x 32924a1bd54SKyle Evans local y = tonumber(loader.getenv("loader_logo_y")) or 330aedd6be5SKyle Evans drawer.logo_position.y 331088b4f5fSWarner Losh 332aedd6be5SKyle Evans local logo = loader.getenv("loader_logo") 333aedd6be5SKyle Evans local colored = color.isEnabled() 334088b4f5fSWarner Losh 3351091c8feSKyle Evans local logodef = getLogodef(logo) 336bb26c57dSKyle Evans 3372d36799aSKyle Evans if logodef == nil or logodef.graphic == nil or 3389f71d421SKyle Evans (not colored and logodef.requires_color) then 339bb26c57dSKyle Evans -- Choose a sensible default 3409f71d421SKyle Evans if colored then 3411091c8feSKyle Evans logodef = getLogodef("orb") 342088b4f5fSWarner Losh else 3431091c8feSKyle Evans logodef = getLogodef("orbbw") 344088b4f5fSWarner Losh end 345088b4f5fSWarner Losh end 3462d36799aSKyle Evans 3472d36799aSKyle Evans if logodef ~= nil and logodef.graphic == none then 3482d36799aSKyle Evans drawer.shift = logodef.shift 3492d36799aSKyle Evans else 3502d36799aSKyle Evans drawer.shift = drawer.default_shift 3512d36799aSKyle Evans end 3522d36799aSKyle Evans 3532d36799aSKyle Evans x = x + drawer.shift.x 3542d36799aSKyle Evans y = y + drawer.shift.y 3552d36799aSKyle Evans 3562ed9eb5dSKyle Evans if logodef ~= nil and logodef.shift ~= nil then 357aedd6be5SKyle Evans x = x + logodef.shift.x 358aedd6be5SKyle Evans y = y + logodef.shift.y 359bb26c57dSKyle Evans end 3602d36799aSKyle Evans 361aedd6be5SKyle Evans drawer.draw(x, y, logodef.graphic) 362088b4f5fSWarner Loshend 363088b4f5fSWarner Losh 364aedd6be5SKyle Evansreturn drawer 365