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 39*1091c8feSKyle 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 54*1091c8feSKyle Evanslocal function getLogodef(logo) 55*1091c8feSKyle Evans -- Look it up 56*1091c8feSKyle Evans local logodef = drawer.logodefs[logo] 57*1091c8feSKyle Evans 58*1091c8feSKyle Evans -- Try to pull it in 59*1091c8feSKyle Evans if logodef == nil then 60*1091c8feSKyle Evans try_include('logo-' .. logo) 61*1091c8feSKyle Evans logodef = drawer.logodefs[logo] 62*1091c8feSKyle Evans end 63*1091c8feSKyle Evans 64*1091c8feSKyle Evans return logodef 65*1091c8feSKyle Evansend 66*1091c8feSKyle Evans 67*1091c8feSKyle Evansfbsd_brand = { 68088b4f5fSWarner Losh" ______ ____ _____ _____ ", 69088b4f5fSWarner Losh" | ____| | _ \\ / ____| __ \\ ", 70088b4f5fSWarner Losh" | |___ _ __ ___ ___ | |_) | (___ | | | |", 71088b4f5fSWarner Losh" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 72088b4f5fSWarner Losh" | | | | | __/ __/| |_) |____) | |__| |", 73088b4f5fSWarner Losh" | | | | | | || | | |", 74088b4f5fSWarner Losh" |_| |_| \\___|\\___||____/|_____/|_____/ " 75aedd6be5SKyle Evans} 76aedd6be5SKyle Evansnone = {""} 77088b4f5fSWarner Losh 78b5746545SKyle Evans-- Module exports 79b5746545SKyle Evansdrawer.menu_name_handlers = { 80b5746545SKyle Evans -- Menu name handlers should take the menu being drawn and entry being 81b5746545SKyle Evans -- drawn as parameters, and return the name of the item. 82b5746545SKyle Evans -- This is designed so that everything, including menu separators, may 83b5746545SKyle Evans -- have their names derived differently. The default action for entry 84a51f9f0cSKyle Evans -- types not specified here is to use entry.name directly. 85e2df27e3SKyle Evans [core.MENU_SEPARATOR] = function(_, entry) 86dd65496aSKyle Evans if entry.name ~= nil then 87a51f9f0cSKyle Evans if type(entry.name) == "function" then 88dd65496aSKyle Evans return entry.name() 89dd65496aSKyle Evans end 90a51f9f0cSKyle Evans return entry.name 91a51f9f0cSKyle Evans end 92dd65496aSKyle Evans return "" 93dd65496aSKyle Evans end, 94e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 95aedd6be5SKyle Evans local carid = entry.carousel_id 96aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 974f437f9eSKyle Evans local choices = entry.items 984f437f9eSKyle Evans if type(choices) == "function" then 994f437f9eSKyle Evans choices = choices() 1004f437f9eSKyle Evans end 1019f71d421SKyle Evans if #choices < caridx then 102aedd6be5SKyle Evans caridx = 1 103b5746545SKyle Evans end 104aedd6be5SKyle Evans return entry.name(caridx, choices[caridx], choices) 105b5746545SKyle Evans end, 106aedd6be5SKyle Evans} 107b5746545SKyle Evans 108aedd6be5SKyle Evansdrawer.brand_position = {x = 2, y = 1} 1091495c98fSKyle Evansdrawer.logo_position = {x = 46, y = 4} 1101495c98fSKyle Evansdrawer.menu_position = {x = 5, y = 10} 1111495c98fSKyle Evansdrawer.frame_size = {w = 42, h = 13} 1122d36799aSKyle Evansdrawer.default_shift = {x = 0, y = 0} 1132d36799aSKyle Evansdrawer.shift = drawer.default_shift 114b5746545SKyle Evans 11529aa5794SKyle Evansdrawer.branddefs = { 116699578a6SKyle Evans -- Indexed by valid values for loader_brand in loader.conf(5). Valid 117699578a6SKyle Evans -- keys are: graphic (table depicting graphic) 11829aa5794SKyle Evans ["fbsd"] = { 119*1091c8feSKyle Evans graphic = fbsd_brand, 12029aa5794SKyle Evans }, 12129aa5794SKyle Evans ["none"] = { 12229aa5794SKyle Evans graphic = none, 12329aa5794SKyle Evans }, 124aedd6be5SKyle Evans} 12529aa5794SKyle Evans 126*1091c8feSKyle Evansfunction drawer.addBrand(name, def) 127*1091c8feSKyle Evans drawer.branddefs[name] = def 128*1091c8feSKyle Evansend 129*1091c8feSKyle Evans 130*1091c8feSKyle Evansfunction drawer.addLogo(name, def) 131*1091c8feSKyle Evans drawer.logodefs[name] = def 132*1091c8feSKyle Evansend 133*1091c8feSKyle Evans 134bb26c57dSKyle Evansdrawer.logodefs = { 135bb26c57dSKyle Evans -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 136752b2d40SKyle Evans -- are: requires_color (boolean), graphic (table depicting graphic), and 137bb26c57dSKyle Evans -- shift (table containing x and y). 138bb26c57dSKyle Evans ["tribute"] = { 139*1091c8feSKyle Evans graphic = fbsd_brand, 140bb26c57dSKyle Evans }, 141bb26c57dSKyle Evans ["tributebw"] = { 142*1091c8feSKyle Evans graphic = fbsd_brand, 143bb26c57dSKyle Evans }, 144bb26c57dSKyle Evans ["none"] = { 145752b2d40SKyle Evans graphic = none, 146bb26c57dSKyle Evans shift = {x = 17, y = 0}, 147bb26c57dSKyle Evans }, 148aedd6be5SKyle Evans} 149bb26c57dSKyle Evans 150379e652eSKyle Evansdrawer.frame_styles = { 151379e652eSKyle Evans -- Indexed by valid values for loader_menu_frame in loader.conf(5). 152379e652eSKyle Evans -- All of the keys appearing below must be set for any menu frame style 153379e652eSKyle Evans -- added to drawer.frame_styles. 154379e652eSKyle Evans ["ascii"] = { 155379e652eSKyle Evans horizontal = "-", 156379e652eSKyle Evans vertical = "|", 157379e652eSKyle Evans top_left = "+", 158379e652eSKyle Evans bottom_left = "+", 159379e652eSKyle Evans top_right = "+", 160379e652eSKyle Evans bottom_right = "+", 161379e652eSKyle Evans }, 162379e652eSKyle Evans ["single"] = { 163379e652eSKyle Evans horizontal = "\xC4", 164379e652eSKyle Evans vertical = "\xB3", 165379e652eSKyle Evans top_left = "\xDA", 166379e652eSKyle Evans bottom_left = "\xC0", 167379e652eSKyle Evans top_right = "\xBF", 168379e652eSKyle Evans bottom_right = "\xD9", 169379e652eSKyle Evans }, 170379e652eSKyle Evans ["double"] = { 171379e652eSKyle Evans horizontal = "\xCD", 172379e652eSKyle Evans vertical = "\xBA", 173379e652eSKyle Evans top_left = "\xC9", 174379e652eSKyle Evans bottom_left = "\xC8", 175379e652eSKyle Evans top_right = "\xBB", 176379e652eSKyle Evans bottom_right = "\xBC", 177379e652eSKyle Evans }, 178379e652eSKyle Evans} 179379e652eSKyle Evans 180088b4f5fSWarner Loshfunction drawer.drawscreen(menu_opts) 181088b4f5fSWarner Losh -- drawlogo() must go first. 182088b4f5fSWarner Losh -- it determines the positions of other elements 183aedd6be5SKyle Evans drawer.drawlogo() 184aedd6be5SKyle Evans drawer.drawbrand() 185aedd6be5SKyle Evans drawer.drawbox() 186aedd6be5SKyle Evans return drawer.drawmenu(menu_opts) 187088b4f5fSWarner Loshend 188088b4f5fSWarner Losh 18904af4229SKyle Evansfunction drawer.drawmenu(menudef) 190e2df27e3SKyle Evans local x = drawer.menu_position.x 191e2df27e3SKyle Evans local y = drawer.menu_position.y 192088b4f5fSWarner Losh 1932d36799aSKyle Evans x = x + drawer.shift.x 1942d36799aSKyle Evans y = y + drawer.shift.y 1952d36799aSKyle Evans 196088b4f5fSWarner Losh -- print the menu and build the alias table 197aedd6be5SKyle Evans local alias_table = {} 198aedd6be5SKyle Evans local entry_num = 0 19904af4229SKyle Evans local menu_entries = menudef.entries 2001afbc37aSKyle Evans local effective_line_num = 0 2019f71d421SKyle Evans if type(menu_entries) == "function" then 202aedd6be5SKyle Evans menu_entries = menu_entries() 2032e716cecSKyle Evans end 204d709f254SKyle Evans for _, e in ipairs(menu_entries) do 2054a4fb4f8SKyle Evans -- Allow menu items to be conditionally visible by specifying 2064a4fb4f8SKyle Evans -- a visible function. 2079f71d421SKyle Evans if e.visible ~= nil and not e.visible() then 208aedd6be5SKyle Evans goto continue 2094a4fb4f8SKyle Evans end 2101afbc37aSKyle Evans effective_line_num = effective_line_num + 1 2119f71d421SKyle Evans if e.entry_type ~= core.MENU_SEPARATOR then 212aedd6be5SKyle Evans entry_num = entry_num + 1 2131afbc37aSKyle Evans screen.setcursor(x, y + effective_line_num) 214ada26c4aSKyle Evans 2159895e5d4SKyle Evans printc(entry_num .. ". " .. menuEntryName(menudef, e)) 216088b4f5fSWarner Losh 217088b4f5fSWarner Losh -- fill the alias table 218aedd6be5SKyle Evans alias_table[tostring(entry_num)] = e 2199f71d421SKyle Evans if e.alias ~= nil then 220e2df27e3SKyle Evans for _, a in ipairs(e.alias) do 221aedd6be5SKyle Evans alias_table[a] = e 222088b4f5fSWarner Losh end 223196ba166SKyle Evans end 224088b4f5fSWarner Losh else 2251afbc37aSKyle Evans screen.setcursor(x, y + effective_line_num) 2269895e5d4SKyle Evans printc(menuEntryName(menudef, e)) 227088b4f5fSWarner Losh end 2284a4fb4f8SKyle Evans ::continue:: 229088b4f5fSWarner Losh end 230aedd6be5SKyle Evans return alias_table 231088b4f5fSWarner Loshend 232088b4f5fSWarner Losh 233088b4f5fSWarner Loshfunction drawer.drawbox() 2341495c98fSKyle Evans local x = drawer.menu_position.x - 3 2351495c98fSKyle Evans local y = drawer.menu_position.y - 1 2361495c98fSKyle Evans local w = drawer.frame_size.w 2371495c98fSKyle Evans local h = drawer.frame_size.h 238088b4f5fSWarner Losh 239379e652eSKyle Evans local framestyle = loader.getenv("loader_menu_frame") or "double" 240379e652eSKyle Evans local framespec = drawer.frame_styles[framestyle] 241379e652eSKyle Evans -- If we don't have a framespec for the current frame style, just don't 242379e652eSKyle Evans -- draw a box. 243379e652eSKyle Evans if framespec == nil then 244379e652eSKyle Evans return 245379e652eSKyle Evans end 246088b4f5fSWarner Losh 247379e652eSKyle Evans local hl = framespec.horizontal 248379e652eSKyle Evans local vl = framespec.vertical 249088b4f5fSWarner Losh 250379e652eSKyle Evans local tl = framespec.top_left 251379e652eSKyle Evans local bl = framespec.bottom_left 252379e652eSKyle Evans local tr = framespec.top_right 253379e652eSKyle Evans local br = framespec.bottom_right 254088b4f5fSWarner Losh 2552d36799aSKyle Evans x = x + drawer.shift.x 2562d36799aSKyle Evans y = y + drawer.shift.y 2572d36799aSKyle Evans 258223e9874SKyle Evans screen.setcursor(x, y); printc(tl) 259223e9874SKyle Evans screen.setcursor(x, y + h); printc(bl) 260223e9874SKyle Evans screen.setcursor(x + w, y); printc(tr) 261223e9874SKyle Evans screen.setcursor(x + w, y + h); printc(br) 262379e652eSKyle Evans 263379e652eSKyle Evans screen.setcursor(x + 1, y) 264379e652eSKyle Evans for _ = 1, w - 1 do 265223e9874SKyle Evans printc(hl) 266379e652eSKyle Evans end 267379e652eSKyle Evans 268379e652eSKyle Evans screen.setcursor(x + 1, y + h) 269379e652eSKyle Evans for _ = 1, w - 1 do 270223e9874SKyle Evans printc(hl) 271088b4f5fSWarner Losh end 272088b4f5fSWarner Losh 273088b4f5fSWarner Losh for i = 1, h - 1 do 274aedd6be5SKyle Evans screen.setcursor(x, y + i) 275223e9874SKyle Evans printc(vl) 276aedd6be5SKyle Evans screen.setcursor(x + w, y + i) 277223e9874SKyle Evans printc(vl) 278088b4f5fSWarner Losh end 279088b4f5fSWarner Losh 280953d8937SKyle Evans local menu_header = loader.getenv("loader_menu_title") or 281953d8937SKyle Evans "Welcome to FreeBSD" 282b4353326SKyle Evans local menu_header_align = loader.getenv("loader_menu_title_align") 283953d8937SKyle Evans local menu_header_x 284953d8937SKyle Evans 285b4353326SKyle Evans if menu_header_align ~= nil then 286b4353326SKyle Evans menu_header_align = menu_header_align:lower() 287b4353326SKyle Evans if menu_header_align == "left" then 288b4353326SKyle Evans -- Just inside the left border on top 289b4353326SKyle Evans menu_header_x = x + 1 290b4353326SKyle Evans elseif menu_header_align == "right" then 291b4353326SKyle Evans -- Just inside the right border on top 292b4353326SKyle Evans menu_header_x = x + w - #menu_header 293b4353326SKyle Evans end 294b4353326SKyle Evans end 295b4353326SKyle Evans if menu_header_x == nil then 296953d8937SKyle Evans menu_header_x = x + (w / 2) - (#menu_header / 2) 297b4353326SKyle Evans end 298953d8937SKyle Evans screen.setcursor(menu_header_x, y) 299953d8937SKyle Evans printc(menu_header) 300088b4f5fSWarner Loshend 301088b4f5fSWarner Losh 302088b4f5fSWarner Loshfunction drawer.draw(x, y, logo) 303088b4f5fSWarner Losh for i = 1, #logo do 3041495c98fSKyle Evans screen.setcursor(x, y + i - 1) 3051495c98fSKyle Evans printc(logo[i]) 306088b4f5fSWarner Losh end 307088b4f5fSWarner Loshend 308088b4f5fSWarner Losh 309088b4f5fSWarner Loshfunction drawer.drawbrand() 31024a1bd54SKyle Evans local x = tonumber(loader.getenv("loader_brand_x")) or 311aedd6be5SKyle Evans drawer.brand_position.x 31224a1bd54SKyle Evans local y = tonumber(loader.getenv("loader_brand_y")) or 313aedd6be5SKyle Evans drawer.brand_position.y 314088b4f5fSWarner Losh 315*1091c8feSKyle Evans local graphic = drawer.branddefs[loader.getenv("loader_brand")] or 316*1091c8feSKyle Evans fbsd_brand 3172d36799aSKyle Evans 3182d36799aSKyle Evans x = x + drawer.shift.x 3192d36799aSKyle Evans y = y + drawer.shift.y 320aedd6be5SKyle Evans drawer.draw(x, y, graphic) 321088b4f5fSWarner Loshend 322088b4f5fSWarner Losh 323088b4f5fSWarner Loshfunction drawer.drawlogo() 32424a1bd54SKyle Evans local x = tonumber(loader.getenv("loader_logo_x")) or 325aedd6be5SKyle Evans drawer.logo_position.x 32624a1bd54SKyle Evans local y = tonumber(loader.getenv("loader_logo_y")) or 327aedd6be5SKyle Evans drawer.logo_position.y 328088b4f5fSWarner Losh 329aedd6be5SKyle Evans local logo = loader.getenv("loader_logo") 330aedd6be5SKyle Evans local colored = color.isEnabled() 331088b4f5fSWarner Losh 332*1091c8feSKyle Evans local logodef = getLogodef(logo) 333bb26c57dSKyle Evans 3342d36799aSKyle Evans if logodef == nil or logodef.graphic == nil or 3359f71d421SKyle Evans (not colored and logodef.requires_color) then 336bb26c57dSKyle Evans -- Choose a sensible default 3379f71d421SKyle Evans if colored then 338*1091c8feSKyle Evans logodef = getLogodef("orb") 339088b4f5fSWarner Losh else 340*1091c8feSKyle Evans logodef = getLogodef("orbbw") 341088b4f5fSWarner Losh end 342088b4f5fSWarner Losh end 3432d36799aSKyle Evans 3442d36799aSKyle Evans if logodef ~= nil and logodef.graphic == none then 3452d36799aSKyle Evans drawer.shift = logodef.shift 3462d36799aSKyle Evans else 3472d36799aSKyle Evans drawer.shift = drawer.default_shift 3482d36799aSKyle Evans end 3492d36799aSKyle Evans 3502d36799aSKyle Evans x = x + drawer.shift.x 3512d36799aSKyle Evans y = y + drawer.shift.y 3522d36799aSKyle Evans 3532ed9eb5dSKyle Evans if logodef ~= nil and logodef.shift ~= nil then 354aedd6be5SKyle Evans x = x + logodef.shift.x 355aedd6be5SKyle Evans y = y + logodef.shift.y 356bb26c57dSKyle Evans end 3572d36799aSKyle Evans 358aedd6be5SKyle Evans drawer.draw(x, y, logodef.graphic) 359088b4f5fSWarner Loshend 360088b4f5fSWarner Losh 361aedd6be5SKyle Evansreturn drawer 362