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 42a9edc01bSKyle Evanslocal menu_name_handlers 43a9edc01bSKyle Evanslocal branddefs 44a9edc01bSKyle Evanslocal logodefs 45a9edc01bSKyle Evanslocal brand_position 46a9edc01bSKyle Evanslocal logo_position 47a9edc01bSKyle Evanslocal menu_position 48a9edc01bSKyle Evanslocal frame_size 49a9edc01bSKyle Evanslocal default_shift 50a9edc01bSKyle Evanslocal shift 51a9edc01bSKyle Evans 52322a2dddSKyle Evanslocal function menuEntryName(drawing_menu, entry) 53a9edc01bSKyle Evans local name_handler = menu_name_handlers[entry.entry_type] 54e15abd1fSKyle Evans 559f71d421SKyle Evans if name_handler ~= nil then 56aedd6be5SKyle Evans return name_handler(drawing_menu, entry) 57e15abd1fSKyle Evans end 58a51f9f0cSKyle Evans if type(entry.name) == "function" then 59aedd6be5SKyle Evans return entry.name() 60e15abd1fSKyle Evans end 61a51f9f0cSKyle Evans return entry.name 62a51f9f0cSKyle Evansend 63e15abd1fSKyle Evans 64e21e1dbeSKyle Evanslocal function getBranddef(brand) 65e21e1dbeSKyle Evans if brand == nil then 66e21e1dbeSKyle Evans return nil 67e21e1dbeSKyle Evans end 68e21e1dbeSKyle Evans -- Look it up 69a9edc01bSKyle Evans local branddef = branddefs[brand] 70e21e1dbeSKyle Evans 71e21e1dbeSKyle Evans -- Try to pull it in 72e21e1dbeSKyle Evans if branddef == nil then 73e21e1dbeSKyle Evans try_include('brand-' .. brand) 74a9edc01bSKyle Evans branddef = branddefs[brand] 75e21e1dbeSKyle Evans end 76e21e1dbeSKyle Evans 77e21e1dbeSKyle Evans return branddef 78e21e1dbeSKyle Evansend 79e21e1dbeSKyle Evans 801091c8feSKyle Evanslocal function getLogodef(logo) 81bbb516aeSKyle Evans if logo == nil then 82bbb516aeSKyle Evans return nil 83bbb516aeSKyle Evans end 841091c8feSKyle Evans -- Look it up 85a9edc01bSKyle Evans local logodef = logodefs[logo] 861091c8feSKyle Evans 871091c8feSKyle Evans -- Try to pull it in 881091c8feSKyle Evans if logodef == nil then 891091c8feSKyle Evans try_include('logo-' .. logo) 90a9edc01bSKyle Evans logodef = logodefs[logo] 911091c8feSKyle Evans end 921091c8feSKyle Evans 931091c8feSKyle Evans return logodef 941091c8feSKyle Evansend 951091c8feSKyle Evans 966112ee09SKyle Evanslocal function draw(x, y, logo) 976112ee09SKyle Evans for i = 1, #logo do 986112ee09SKyle Evans screen.setcursor(x, y + i - 1) 996112ee09SKyle Evans printc(logo[i]) 1006112ee09SKyle Evans end 1016112ee09SKyle Evansend 1026112ee09SKyle Evans 10312eaa305SKyle Evanslocal function drawmenu(menudef) 104a9edc01bSKyle Evans local x = menu_position.x 105a9edc01bSKyle Evans local y = menu_position.y 10612eaa305SKyle Evans 107a9edc01bSKyle Evans x = x + shift.x 108a9edc01bSKyle Evans y = y + shift.y 10912eaa305SKyle Evans 11012eaa305SKyle Evans -- print the menu and build the alias table 11112eaa305SKyle Evans local alias_table = {} 11212eaa305SKyle Evans local entry_num = 0 11312eaa305SKyle Evans local menu_entries = menudef.entries 11412eaa305SKyle Evans local effective_line_num = 0 11512eaa305SKyle Evans if type(menu_entries) == "function" then 11612eaa305SKyle Evans menu_entries = menu_entries() 11712eaa305SKyle Evans end 11812eaa305SKyle Evans for _, e in ipairs(menu_entries) do 11912eaa305SKyle Evans -- Allow menu items to be conditionally visible by specifying 12012eaa305SKyle Evans -- a visible function. 12112eaa305SKyle Evans if e.visible ~= nil and not e.visible() then 12212eaa305SKyle Evans goto continue 12312eaa305SKyle Evans end 12412eaa305SKyle Evans effective_line_num = effective_line_num + 1 12512eaa305SKyle Evans if e.entry_type ~= core.MENU_SEPARATOR then 12612eaa305SKyle Evans entry_num = entry_num + 1 12712eaa305SKyle Evans screen.setcursor(x, y + effective_line_num) 12812eaa305SKyle Evans 12912eaa305SKyle Evans printc(entry_num .. ". " .. menuEntryName(menudef, e)) 13012eaa305SKyle Evans 13112eaa305SKyle Evans -- fill the alias table 13212eaa305SKyle Evans alias_table[tostring(entry_num)] = e 13312eaa305SKyle Evans if e.alias ~= nil then 13412eaa305SKyle Evans for _, a in ipairs(e.alias) do 13512eaa305SKyle Evans alias_table[a] = e 13612eaa305SKyle Evans end 13712eaa305SKyle Evans end 13812eaa305SKyle Evans else 13912eaa305SKyle Evans screen.setcursor(x, y + effective_line_num) 14012eaa305SKyle Evans printc(menuEntryName(menudef, e)) 14112eaa305SKyle Evans end 14212eaa305SKyle Evans ::continue:: 14312eaa305SKyle Evans end 14412eaa305SKyle Evans return alias_table 14512eaa305SKyle Evansend 14612eaa305SKyle Evans 14790a25417SKyle Evanslocal function defaultframe() 14890a25417SKyle Evans if core.isSerialConsole() then 14990a25417SKyle Evans return "ascii" 15090a25417SKyle Evans end 15190a25417SKyle Evans return "double" 15290a25417SKyle Evansend 15390a25417SKyle Evans 15412eaa305SKyle Evanslocal function drawbox() 155a9edc01bSKyle Evans local x = menu_position.x - 3 156a9edc01bSKyle Evans local y = menu_position.y - 1 157a9edc01bSKyle Evans local w = frame_size.w 158a9edc01bSKyle Evans local h = frame_size.h 15912eaa305SKyle Evans 16090a25417SKyle Evans local framestyle = loader.getenv("loader_menu_frame") or defaultframe() 16112eaa305SKyle Evans local framespec = drawer.frame_styles[framestyle] 16212eaa305SKyle Evans -- If we don't have a framespec for the current frame style, just don't 16312eaa305SKyle Evans -- draw a box. 16412eaa305SKyle Evans if framespec == nil then 16512eaa305SKyle Evans return 16612eaa305SKyle Evans end 16712eaa305SKyle Evans 16812eaa305SKyle Evans local hl = framespec.horizontal 16912eaa305SKyle Evans local vl = framespec.vertical 17012eaa305SKyle Evans 17112eaa305SKyle Evans local tl = framespec.top_left 17212eaa305SKyle Evans local bl = framespec.bottom_left 17312eaa305SKyle Evans local tr = framespec.top_right 17412eaa305SKyle Evans local br = framespec.bottom_right 17512eaa305SKyle Evans 176a9edc01bSKyle Evans x = x + shift.x 177a9edc01bSKyle Evans y = y + shift.y 17812eaa305SKyle Evans 17912eaa305SKyle Evans screen.setcursor(x, y); printc(tl) 18012eaa305SKyle Evans screen.setcursor(x, y + h); printc(bl) 18112eaa305SKyle Evans screen.setcursor(x + w, y); printc(tr) 18212eaa305SKyle Evans screen.setcursor(x + w, y + h); printc(br) 18312eaa305SKyle Evans 18412eaa305SKyle Evans screen.setcursor(x + 1, y) 18512eaa305SKyle Evans for _ = 1, w - 1 do 18612eaa305SKyle Evans printc(hl) 18712eaa305SKyle Evans end 18812eaa305SKyle Evans 18912eaa305SKyle Evans screen.setcursor(x + 1, y + h) 19012eaa305SKyle Evans for _ = 1, w - 1 do 19112eaa305SKyle Evans printc(hl) 19212eaa305SKyle Evans end 19312eaa305SKyle Evans 19412eaa305SKyle Evans for i = 1, h - 1 do 19512eaa305SKyle Evans screen.setcursor(x, y + i) 19612eaa305SKyle Evans printc(vl) 19712eaa305SKyle Evans screen.setcursor(x + w, y + i) 19812eaa305SKyle Evans printc(vl) 19912eaa305SKyle Evans end 20012eaa305SKyle Evans 20112eaa305SKyle Evans local menu_header = loader.getenv("loader_menu_title") or 20212eaa305SKyle Evans "Welcome to FreeBSD" 20312eaa305SKyle Evans local menu_header_align = loader.getenv("loader_menu_title_align") 20412eaa305SKyle Evans local menu_header_x 20512eaa305SKyle Evans 20612eaa305SKyle Evans if menu_header_align ~= nil then 20712eaa305SKyle Evans menu_header_align = menu_header_align:lower() 20812eaa305SKyle Evans if menu_header_align == "left" then 20912eaa305SKyle Evans -- Just inside the left border on top 21012eaa305SKyle Evans menu_header_x = x + 1 21112eaa305SKyle Evans elseif menu_header_align == "right" then 21212eaa305SKyle Evans -- Just inside the right border on top 21312eaa305SKyle Evans menu_header_x = x + w - #menu_header 21412eaa305SKyle Evans end 21512eaa305SKyle Evans end 21612eaa305SKyle Evans if menu_header_x == nil then 21712eaa305SKyle Evans menu_header_x = x + (w / 2) - (#menu_header / 2) 21812eaa305SKyle Evans end 21912eaa305SKyle Evans screen.setcursor(menu_header_x, y) 22012eaa305SKyle Evans printc(menu_header) 22112eaa305SKyle Evansend 22212eaa305SKyle Evans 22312eaa305SKyle Evanslocal function drawbrand() 22412eaa305SKyle Evans local x = tonumber(loader.getenv("loader_brand_x")) or 225a9edc01bSKyle Evans brand_position.x 22612eaa305SKyle Evans local y = tonumber(loader.getenv("loader_brand_y")) or 227a9edc01bSKyle Evans brand_position.y 22812eaa305SKyle Evans 22912eaa305SKyle Evans local branddef = getBranddef(loader.getenv("loader_brand")) 23012eaa305SKyle Evans 23112eaa305SKyle Evans if branddef == nil then 23212eaa305SKyle Evans branddef = getBranddef(drawer.default_brand) 23312eaa305SKyle Evans end 23412eaa305SKyle Evans 23512eaa305SKyle Evans local graphic = branddef.graphic 23612eaa305SKyle Evans 237a9edc01bSKyle Evans x = x + shift.x 238a9edc01bSKyle Evans y = y + shift.y 23912eaa305SKyle Evans draw(x, y, graphic) 24012eaa305SKyle Evansend 24112eaa305SKyle Evans 24212eaa305SKyle Evanslocal function drawlogo() 24312eaa305SKyle Evans local x = tonumber(loader.getenv("loader_logo_x")) or 244a9edc01bSKyle Evans logo_position.x 24512eaa305SKyle Evans local y = tonumber(loader.getenv("loader_logo_y")) or 246a9edc01bSKyle Evans logo_position.y 24712eaa305SKyle Evans 24812eaa305SKyle Evans local logo = loader.getenv("loader_logo") 24912eaa305SKyle Evans local colored = color.isEnabled() 25012eaa305SKyle Evans 25112eaa305SKyle Evans local logodef = getLogodef(logo) 25212eaa305SKyle Evans 25312eaa305SKyle Evans if logodef == nil or logodef.graphic == nil or 25412eaa305SKyle Evans (not colored and logodef.requires_color) then 25512eaa305SKyle Evans -- Choose a sensible default 25612eaa305SKyle Evans if colored then 2572c690e2aSKyle Evans logodef = getLogodef(drawer.default_color_logodef) 25812eaa305SKyle Evans else 2592c690e2aSKyle Evans logodef = getLogodef(drawer.default_bw_logodef) 26012eaa305SKyle Evans end 261*6697f577SKyle Evans 262*6697f577SKyle Evans -- Something has gone terribly wrong. 263*6697f577SKyle Evans if logodef == nil then 264*6697f577SKyle Evans logodef = getLogodef(drawer.default_fallback_logodef) 265*6697f577SKyle Evans end 26612eaa305SKyle Evans end 26712eaa305SKyle Evans 26812eaa305SKyle Evans if logodef ~= nil and logodef.graphic == none then 269a9edc01bSKyle Evans shift = logodef.shift 27012eaa305SKyle Evans else 271a9edc01bSKyle Evans shift = default_shift 27212eaa305SKyle Evans end 27312eaa305SKyle Evans 274a9edc01bSKyle Evans x = x + shift.x 275a9edc01bSKyle Evans y = y + shift.y 27612eaa305SKyle Evans 27712eaa305SKyle Evans if logodef ~= nil and logodef.shift ~= nil then 27812eaa305SKyle Evans x = x + logodef.shift.x 27912eaa305SKyle Evans y = y + logodef.shift.y 28012eaa305SKyle Evans end 28112eaa305SKyle Evans 28212eaa305SKyle Evans draw(x, y, logodef.graphic) 28312eaa305SKyle Evansend 28412eaa305SKyle Evans 2851091c8feSKyle Evansfbsd_brand = { 286088b4f5fSWarner Losh" ______ ____ _____ _____ ", 287088b4f5fSWarner Losh" | ____| | _ \\ / ____| __ \\ ", 288088b4f5fSWarner Losh" | |___ _ __ ___ ___ | |_) | (___ | | | |", 289088b4f5fSWarner Losh" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 290088b4f5fSWarner Losh" | | | | | __/ __/| |_) |____) | |__| |", 291088b4f5fSWarner Losh" | | | | | | || | | |", 292088b4f5fSWarner Losh" |_| |_| \\___|\\___||____/|_____/|_____/ " 293aedd6be5SKyle Evans} 294aedd6be5SKyle Evansnone = {""} 295088b4f5fSWarner Losh 296a9edc01bSKyle Evansmenu_name_handlers = { 297b5746545SKyle Evans -- Menu name handlers should take the menu being drawn and entry being 298b5746545SKyle Evans -- drawn as parameters, and return the name of the item. 299b5746545SKyle Evans -- This is designed so that everything, including menu separators, may 300b5746545SKyle Evans -- have their names derived differently. The default action for entry 301a51f9f0cSKyle Evans -- types not specified here is to use entry.name directly. 302e2df27e3SKyle Evans [core.MENU_SEPARATOR] = function(_, entry) 303dd65496aSKyle Evans if entry.name ~= nil then 304a51f9f0cSKyle Evans if type(entry.name) == "function" then 305dd65496aSKyle Evans return entry.name() 306dd65496aSKyle Evans end 307a51f9f0cSKyle Evans return entry.name 308a51f9f0cSKyle Evans end 309dd65496aSKyle Evans return "" 310dd65496aSKyle Evans end, 311e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 312aedd6be5SKyle Evans local carid = entry.carousel_id 313aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 3144f437f9eSKyle Evans local choices = entry.items 3154f437f9eSKyle Evans if type(choices) == "function" then 3164f437f9eSKyle Evans choices = choices() 3174f437f9eSKyle Evans end 3189f71d421SKyle Evans if #choices < caridx then 319aedd6be5SKyle Evans caridx = 1 320b5746545SKyle Evans end 321aedd6be5SKyle Evans return entry.name(caridx, choices[caridx], choices) 322b5746545SKyle Evans end, 323aedd6be5SKyle Evans} 324b5746545SKyle Evans 325a9edc01bSKyle Evansbranddefs = { 326699578a6SKyle Evans -- Indexed by valid values for loader_brand in loader.conf(5). Valid 327699578a6SKyle Evans -- keys are: graphic (table depicting graphic) 32829aa5794SKyle Evans ["fbsd"] = { 3291091c8feSKyle Evans graphic = fbsd_brand, 33029aa5794SKyle Evans }, 33129aa5794SKyle Evans ["none"] = { 33229aa5794SKyle Evans graphic = none, 33329aa5794SKyle Evans }, 334aedd6be5SKyle Evans} 33529aa5794SKyle Evans 336a9edc01bSKyle Evanslogodefs = { 337bb26c57dSKyle Evans -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 338752b2d40SKyle Evans -- are: requires_color (boolean), graphic (table depicting graphic), and 339bb26c57dSKyle Evans -- shift (table containing x and y). 340bb26c57dSKyle Evans ["tribute"] = { 3411091c8feSKyle Evans graphic = fbsd_brand, 342bb26c57dSKyle Evans }, 343bb26c57dSKyle Evans ["tributebw"] = { 3441091c8feSKyle Evans graphic = fbsd_brand, 345bb26c57dSKyle Evans }, 346bb26c57dSKyle Evans ["none"] = { 347752b2d40SKyle Evans graphic = none, 348bb26c57dSKyle Evans shift = {x = 17, y = 0}, 349bb26c57dSKyle Evans }, 350aedd6be5SKyle Evans} 351bb26c57dSKyle Evans 352a9edc01bSKyle Evansbrand_position = {x = 2, y = 1} 353a9edc01bSKyle Evanslogo_position = {x = 46, y = 4} 354a9edc01bSKyle Evansmenu_position = {x = 5, y = 10} 355a9edc01bSKyle Evansframe_size = {w = 42, h = 13} 356a9edc01bSKyle Evansdefault_shift = {x = 0, y = 0} 357a9edc01bSKyle Evansshift = default_shift 358a9edc01bSKyle Evans 359a9edc01bSKyle Evans-- Module exports 360a9edc01bSKyle Evansdrawer.default_brand = 'fbsd' 3612c690e2aSKyle Evansdrawer.default_color_logodef = 'orb' 3622c690e2aSKyle Evansdrawer.default_bw_logodef = 'orbbw' 363*6697f577SKyle Evans-- For when things go terribly wrong; this def should be present here in the 364*6697f577SKyle Evans-- drawer module in case it's a filesystem issue. 365*6697f577SKyle Evansdrawer.default_fallback_logodef = 'none' 366a9edc01bSKyle Evans 367a9edc01bSKyle Evansfunction drawer.addBrand(name, def) 368a9edc01bSKyle Evans branddefs[name] = def 369a9edc01bSKyle Evansend 370a9edc01bSKyle Evans 371a9edc01bSKyle Evansfunction drawer.addLogo(name, def) 372a9edc01bSKyle Evans logodefs[name] = def 373a9edc01bSKyle Evansend 374a9edc01bSKyle Evans 375379e652eSKyle Evansdrawer.frame_styles = { 376379e652eSKyle Evans -- Indexed by valid values for loader_menu_frame in loader.conf(5). 377379e652eSKyle Evans -- All of the keys appearing below must be set for any menu frame style 378379e652eSKyle Evans -- added to drawer.frame_styles. 379379e652eSKyle Evans ["ascii"] = { 380379e652eSKyle Evans horizontal = "-", 381379e652eSKyle Evans vertical = "|", 382379e652eSKyle Evans top_left = "+", 383379e652eSKyle Evans bottom_left = "+", 384379e652eSKyle Evans top_right = "+", 385379e652eSKyle Evans bottom_right = "+", 386379e652eSKyle Evans }, 387379e652eSKyle Evans ["single"] = { 38856758831SToomas Soome horizontal = "\xE2\x94\x80", 38956758831SToomas Soome vertical = "\xE2\x94\x82", 39056758831SToomas Soome top_left = "\xE2\x94\x8C", 39156758831SToomas Soome bottom_left = "\xE2\x94\x94", 39256758831SToomas Soome top_right = "\xE2\x94\x90", 39356758831SToomas Soome bottom_right = "\xE2\x94\x98", 394379e652eSKyle Evans }, 395379e652eSKyle Evans ["double"] = { 39656758831SToomas Soome horizontal = "\xE2\x95\x90", 39756758831SToomas Soome vertical = "\xE2\x95\x91", 39856758831SToomas Soome top_left = "\xE2\x95\x94", 39956758831SToomas Soome bottom_left = "\xE2\x95\x9A", 40056758831SToomas Soome top_right = "\xE2\x95\x97", 40156758831SToomas Soome bottom_right = "\xE2\x95\x9D", 402379e652eSKyle Evans }, 403379e652eSKyle Evans} 404379e652eSKyle Evans 40512eaa305SKyle Evansfunction drawer.drawscreen(menudef) 406088b4f5fSWarner Losh -- drawlogo() must go first. 407088b4f5fSWarner Losh -- it determines the positions of other elements 40812eaa305SKyle Evans drawlogo() 40912eaa305SKyle Evans drawbrand() 41012eaa305SKyle Evans drawbox() 41112eaa305SKyle Evans return drawmenu(menudef) 412088b4f5fSWarner Loshend 413088b4f5fSWarner Losh 414aedd6be5SKyle Evansreturn drawer 415