1329167Simp-- 2344220Skevans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3344220Skevans-- 4329167Simp-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 5344220Skevans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 6329167Simp-- All rights reserved. 7329167Simp-- 8329167Simp-- Redistribution and use in source and binary forms, with or without 9329167Simp-- modification, are permitted provided that the following conditions 10329167Simp-- are met: 11329167Simp-- 1. Redistributions of source code must retain the above copyright 12329167Simp-- notice, this list of conditions and the following disclaimer. 13329167Simp-- 2. Redistributions in binary form must reproduce the above copyright 14329167Simp-- notice, this list of conditions and the following disclaimer in the 15329167Simp-- documentation and/or other materials provided with the distribution. 16329167Simp-- 17329167Simp-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18329167Simp-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19329167Simp-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20329167Simp-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21329167Simp-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22329167Simp-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23329167Simp-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24329167Simp-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25329167Simp-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26329167Simp-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27329167Simp-- SUCH DAMAGE. 28329167Simp-- 29329167Simp-- $FreeBSD: stable/11/stand/lua/drawer.lua 361817 2020-06-05 02:52:07Z kevans $ 30329167Simp-- 31329167Simp 32344220Skevanslocal color = require("color") 33344220Skevanslocal config = require("config") 34344220Skevanslocal core = require("core") 35344220Skevanslocal screen = require("screen") 36329167Simp 37344220Skevanslocal drawer = {} 38329167Simp 39344220Skevanslocal fbsd_brand 40344220Skevanslocal none 41329167Simp 42344220Skevanslocal menu_name_handlers 43344220Skevanslocal branddefs 44344220Skevanslocal logodefs 45344220Skevanslocal brand_position 46344220Skevanslocal logo_position 47344220Skevanslocal menu_position 48344220Skevanslocal frame_size 49344220Skevanslocal default_shift 50344220Skevanslocal shift 51329167Simp 52344220Skevanslocal function menuEntryName(drawing_menu, entry) 53344220Skevans local name_handler = menu_name_handlers[entry.entry_type] 54329167Simp 55344220Skevans if name_handler ~= nil then 56344220Skevans return name_handler(drawing_menu, entry) 57344220Skevans end 58344220Skevans if type(entry.name) == "function" then 59344220Skevans return entry.name() 60344220Skevans end 61344220Skevans return entry.name 62344220Skevansend 63329167Simp 64344220Skevanslocal function getBranddef(brand) 65344220Skevans if brand == nil then 66344220Skevans return nil 67344220Skevans end 68344220Skevans -- Look it up 69344220Skevans local branddef = branddefs[brand] 70329167Simp 71344220Skevans -- Try to pull it in 72344220Skevans if branddef == nil then 73344220Skevans try_include('brand-' .. brand) 74344220Skevans branddef = branddefs[brand] 75344220Skevans end 76329167Simp 77344220Skevans return branddef 78344220Skevansend 79329167Simp 80344220Skevanslocal function getLogodef(logo) 81344220Skevans if logo == nil then 82344220Skevans return nil 83344220Skevans end 84344220Skevans -- Look it up 85344220Skevans local logodef = logodefs[logo] 86329167Simp 87344220Skevans -- Try to pull it in 88344220Skevans if logodef == nil then 89344220Skevans try_include('logo-' .. logo) 90344220Skevans logodef = logodefs[logo] 91344220Skevans end 92329167Simp 93344220Skevans return logodef 94344220Skevansend 95329167Simp 96344220Skevanslocal function draw(x, y, logo) 97344220Skevans for i = 1, #logo do 98344220Skevans screen.setcursor(x, y + i - 1) 99344220Skevans printc(logo[i]) 100344220Skevans end 101329167Simpend 102329167Simp 103344220Skevanslocal function drawmenu(menudef) 104344220Skevans local x = menu_position.x 105344220Skevans local y = menu_position.y 106329167Simp 107344220Skevans x = x + shift.x 108344220Skevans y = y + shift.y 109344220Skevans 110329167Simp -- print the menu and build the alias table 111344220Skevans local alias_table = {} 112344220Skevans local entry_num = 0 113344220Skevans local menu_entries = menudef.entries 114344220Skevans local effective_line_num = 0 115344220Skevans if type(menu_entries) == "function" then 116344220Skevans menu_entries = menu_entries() 117344220Skevans end 118344220Skevans for _, e in ipairs(menu_entries) do 119344220Skevans -- Allow menu items to be conditionally visible by specifying 120344220Skevans -- a visible function. 121344220Skevans if e.visible ~= nil and not e.visible() then 122344220Skevans goto continue 123344220Skevans end 124344220Skevans effective_line_num = effective_line_num + 1 125344220Skevans if e.entry_type ~= core.MENU_SEPARATOR then 126344220Skevans entry_num = entry_num + 1 127344220Skevans screen.setcursor(x, y + effective_line_num) 128329167Simp 129344220Skevans printc(entry_num .. ". " .. menuEntryName(menudef, e)) 130344220Skevans 131329167Simp -- fill the alias table 132344220Skevans alias_table[tostring(entry_num)] = e 133344220Skevans if e.alias ~= nil then 134344220Skevans for _, a in ipairs(e.alias) do 135344220Skevans alias_table[a] = e 136344220Skevans end 137329167Simp end 138329167Simp else 139344220Skevans screen.setcursor(x, y + effective_line_num) 140344220Skevans printc(menuEntryName(menudef, e)) 141329167Simp end 142344220Skevans ::continue:: 143329167Simp end 144344220Skevans return alias_table 145329167Simpend 146329167Simp 147352349Skevanslocal function defaultframe() 148352349Skevans if core.isSerialConsole() then 149352349Skevans return "ascii" 150352349Skevans end 151352349Skevans return "double" 152352349Skevansend 153352349Skevans 154344220Skevanslocal function drawbox() 155344220Skevans local x = menu_position.x - 3 156344220Skevans local y = menu_position.y - 1 157344220Skevans local w = frame_size.w 158344220Skevans local h = frame_size.h 159329167Simp 160352349Skevans local framestyle = loader.getenv("loader_menu_frame") or defaultframe() 161344220Skevans local framespec = drawer.frame_styles[framestyle] 162344220Skevans -- If we don't have a framespec for the current frame style, just don't 163344220Skevans -- draw a box. 164344220Skevans if framespec == nil then 165344220Skevans return 166344220Skevans end 167329167Simp 168344220Skevans local hl = framespec.horizontal 169344220Skevans local vl = framespec.vertical 170329167Simp 171344220Skevans local tl = framespec.top_left 172344220Skevans local bl = framespec.bottom_left 173344220Skevans local tr = framespec.top_right 174344220Skevans local br = framespec.bottom_right 175329167Simp 176344220Skevans x = x + shift.x 177344220Skevans y = y + shift.y 178329167Simp 179344220Skevans screen.setcursor(x, y); printc(tl) 180344220Skevans screen.setcursor(x, y + h); printc(bl) 181344220Skevans screen.setcursor(x + w, y); printc(tr) 182344220Skevans screen.setcursor(x + w, y + h); printc(br) 183344220Skevans 184344220Skevans screen.setcursor(x + 1, y) 185344220Skevans for _ = 1, w - 1 do 186344220Skevans printc(hl) 187329167Simp end 188329167Simp 189344220Skevans screen.setcursor(x + 1, y + h) 190344220Skevans for _ = 1, w - 1 do 191344220Skevans printc(hl) 192329167Simp end 193329167Simp 194344220Skevans for i = 1, h - 1 do 195344220Skevans screen.setcursor(x, y + i) 196344220Skevans printc(vl) 197344220Skevans screen.setcursor(x + w, y + i) 198344220Skevans printc(vl) 199344220Skevans end 200329167Simp 201344220Skevans local menu_header = loader.getenv("loader_menu_title") or 202344220Skevans "Welcome to FreeBSD" 203344220Skevans local menu_header_align = loader.getenv("loader_menu_title_align") 204344220Skevans local menu_header_x 205344220Skevans 206344220Skevans if menu_header_align ~= nil then 207344220Skevans menu_header_align = menu_header_align:lower() 208344220Skevans if menu_header_align == "left" then 209344220Skevans -- Just inside the left border on top 210344220Skevans menu_header_x = x + 1 211344220Skevans elseif menu_header_align == "right" then 212344220Skevans -- Just inside the right border on top 213344220Skevans menu_header_x = x + w - #menu_header 214344220Skevans end 215329167Simp end 216344220Skevans if menu_header_x == nil then 217344220Skevans menu_header_x = x + (w / 2) - (#menu_header / 2) 218344220Skevans end 219344220Skevans screen.setcursor(menu_header_x, y) 220344220Skevans printc(menu_header) 221329167Simpend 222329167Simp 223344220Skevanslocal function drawbrand() 224344220Skevans local x = tonumber(loader.getenv("loader_brand_x")) or 225344220Skevans brand_position.x 226344220Skevans local y = tonumber(loader.getenv("loader_brand_y")) or 227344220Skevans brand_position.y 228329167Simp 229344220Skevans local branddef = getBranddef(loader.getenv("loader_brand")) 230344220Skevans 231344220Skevans if branddef == nil then 232344220Skevans branddef = getBranddef(drawer.default_brand) 233329167Simp end 234329167Simp 235344220Skevans local graphic = branddef.graphic 236344220Skevans 237344220Skevans x = x + shift.x 238344220Skevans y = y + shift.y 239344220Skevans draw(x, y, graphic) 240329167Simpend 241329167Simp 242344220Skevanslocal function drawlogo() 243344220Skevans local x = tonumber(loader.getenv("loader_logo_x")) or 244344220Skevans logo_position.x 245344220Skevans local y = tonumber(loader.getenv("loader_logo_y")) or 246344220Skevans logo_position.y 247329167Simp 248344220Skevans local logo = loader.getenv("loader_logo") 249344220Skevans local colored = color.isEnabled() 250344220Skevans 251344220Skevans local logodef = getLogodef(logo) 252344220Skevans 253344220Skevans if logodef == nil or logodef.graphic == nil or 254344220Skevans (not colored and logodef.requires_color) then 255344220Skevans -- Choose a sensible default 256344220Skevans if colored then 257344220Skevans logodef = getLogodef(drawer.default_color_logodef) 258344220Skevans else 259344220Skevans logodef = getLogodef(drawer.default_bw_logodef) 260344220Skevans end 261361817Skevans 262361817Skevans -- Something has gone terribly wrong. 263361817Skevans if logodef == nil then 264361817Skevans logodef = getLogodef(drawer.default_fallback_logodef) 265361817Skevans end 266329167Simp end 267344220Skevans 268344220Skevans if logodef ~= nil and logodef.graphic == none then 269344220Skevans shift = logodef.shift 270344220Skevans else 271344220Skevans shift = default_shift 272329167Simp end 273329167Simp 274344220Skevans x = x + shift.x 275344220Skevans y = y + shift.y 276329167Simp 277344220Skevans if logodef ~= nil and logodef.shift ~= nil then 278344220Skevans x = x + logodef.shift.x 279344220Skevans y = y + logodef.shift.y 280344220Skevans end 281344220Skevans 282344220Skevans draw(x, y, logodef.graphic) 283344220Skevansend 284344220Skevans 285344220Skevansfbsd_brand = { 286344220Skevans" ______ ____ _____ _____ ", 287344220Skevans" | ____| | _ \\ / ____| __ \\ ", 288344220Skevans" | |___ _ __ ___ ___ | |_) | (___ | | | |", 289344220Skevans" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 290344220Skevans" | | | | | __/ __/| |_) |____) | |__| |", 291344220Skevans" | | | | | | || | | |", 292344220Skevans" |_| |_| \\___|\\___||____/|_____/|_____/ " 293344220Skevans} 294344220Skevansnone = {""} 295344220Skevans 296344220Skevansmenu_name_handlers = { 297344220Skevans -- Menu name handlers should take the menu being drawn and entry being 298344220Skevans -- drawn as parameters, and return the name of the item. 299344220Skevans -- This is designed so that everything, including menu separators, may 300344220Skevans -- have their names derived differently. The default action for entry 301344220Skevans -- types not specified here is to use entry.name directly. 302344220Skevans [core.MENU_SEPARATOR] = function(_, entry) 303344220Skevans if entry.name ~= nil then 304344220Skevans if type(entry.name) == "function" then 305344220Skevans return entry.name() 306344220Skevans end 307344220Skevans return entry.name 308329167Simp end 309344220Skevans return "" 310344220Skevans end, 311344220Skevans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 312344220Skevans local carid = entry.carousel_id 313344220Skevans local caridx = config.getCarouselIndex(carid) 314344220Skevans local choices = entry.items 315344220Skevans if type(choices) == "function" then 316344220Skevans choices = choices() 317329167Simp end 318344220Skevans if #choices < caridx then 319344220Skevans caridx = 1 320329167Simp end 321344220Skevans return entry.name(caridx, choices[caridx], choices) 322344220Skevans end, 323344220Skevans} 324344220Skevans 325344220Skevansbranddefs = { 326344220Skevans -- Indexed by valid values for loader_brand in loader.conf(5). Valid 327344220Skevans -- keys are: graphic (table depicting graphic) 328344220Skevans ["fbsd"] = { 329344220Skevans graphic = fbsd_brand, 330344220Skevans }, 331344220Skevans ["none"] = { 332344220Skevans graphic = none, 333344220Skevans }, 334344220Skevans} 335344220Skevans 336344220Skevanslogodefs = { 337344220Skevans -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 338344220Skevans -- are: requires_color (boolean), graphic (table depicting graphic), and 339344220Skevans -- shift (table containing x and y). 340344220Skevans ["tribute"] = { 341344220Skevans graphic = fbsd_brand, 342344220Skevans }, 343344220Skevans ["tributebw"] = { 344344220Skevans graphic = fbsd_brand, 345344220Skevans }, 346344220Skevans ["none"] = { 347344220Skevans graphic = none, 348344220Skevans shift = {x = 17, y = 0}, 349344220Skevans }, 350344220Skevans} 351344220Skevans 352344220Skevansbrand_position = {x = 2, y = 1} 353344220Skevanslogo_position = {x = 46, y = 4} 354344220Skevansmenu_position = {x = 5, y = 10} 355344220Skevansframe_size = {w = 42, h = 13} 356344220Skevansdefault_shift = {x = 0, y = 0} 357344220Skevansshift = default_shift 358344220Skevans 359344220Skevans-- Module exports 360344220Skevansdrawer.default_brand = 'fbsd' 361344220Skevansdrawer.default_color_logodef = 'orb' 362344220Skevansdrawer.default_bw_logodef = 'orbbw' 363361817Skevans-- For when things go terribly wrong; this def should be present here in the 364361817Skevans-- drawer module in case it's a filesystem issue. 365361817Skevansdrawer.default_fallback_logodef = 'none' 366344220Skevans 367344220Skevansfunction drawer.addBrand(name, def) 368344220Skevans branddefs[name] = def 369329167Simpend 370329167Simp 371344220Skevansfunction drawer.addLogo(name, def) 372344220Skevans logodefs[name] = def 373344220Skevansend 374344220Skevans 375344220Skevansdrawer.frame_styles = { 376344220Skevans -- Indexed by valid values for loader_menu_frame in loader.conf(5). 377344220Skevans -- All of the keys appearing below must be set for any menu frame style 378344220Skevans -- added to drawer.frame_styles. 379344220Skevans ["ascii"] = { 380344220Skevans horizontal = "-", 381344220Skevans vertical = "|", 382344220Skevans top_left = "+", 383344220Skevans bottom_left = "+", 384344220Skevans top_right = "+", 385344220Skevans bottom_right = "+", 386344220Skevans }, 387344220Skevans ["single"] = { 388344220Skevans horizontal = "\xC4", 389344220Skevans vertical = "\xB3", 390344220Skevans top_left = "\xDA", 391344220Skevans bottom_left = "\xC0", 392344220Skevans top_right = "\xBF", 393344220Skevans bottom_right = "\xD9", 394344220Skevans }, 395344220Skevans ["double"] = { 396344220Skevans horizontal = "\xCD", 397344220Skevans vertical = "\xBA", 398344220Skevans top_left = "\xC9", 399344220Skevans bottom_left = "\xC8", 400344220Skevans top_right = "\xBB", 401344220Skevans bottom_right = "\xBC", 402344220Skevans }, 403344220Skevans} 404344220Skevans 405344220Skevansfunction drawer.drawscreen(menudef) 406344220Skevans -- drawlogo() must go first. 407344220Skevans -- it determines the positions of other elements 408344220Skevans drawlogo() 409344220Skevans drawbrand() 410344220Skevans drawbox() 411344220Skevans return drawmenu(menudef) 412344220Skevansend 413344220Skevans 414329167Simpreturn drawer 415