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 147*90a25417SKyle Evanslocal function defaultframe() 148*90a25417SKyle Evans if core.isSerialConsole() then 149*90a25417SKyle Evans return "ascii" 150*90a25417SKyle Evans end 151*90a25417SKyle Evans return "double" 152*90a25417SKyle Evansend 153*90a25417SKyle 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 160*90a25417SKyle 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 26112eaa305SKyle Evans end 26212eaa305SKyle Evans 26312eaa305SKyle Evans if logodef ~= nil and logodef.graphic == none then 264a9edc01bSKyle Evans shift = logodef.shift 26512eaa305SKyle Evans else 266a9edc01bSKyle Evans shift = default_shift 26712eaa305SKyle Evans end 26812eaa305SKyle Evans 269a9edc01bSKyle Evans x = x + shift.x 270a9edc01bSKyle Evans y = y + shift.y 27112eaa305SKyle Evans 27212eaa305SKyle Evans if logodef ~= nil and logodef.shift ~= nil then 27312eaa305SKyle Evans x = x + logodef.shift.x 27412eaa305SKyle Evans y = y + logodef.shift.y 27512eaa305SKyle Evans end 27612eaa305SKyle Evans 27712eaa305SKyle Evans draw(x, y, logodef.graphic) 27812eaa305SKyle Evansend 27912eaa305SKyle Evans 2801091c8feSKyle Evansfbsd_brand = { 281088b4f5fSWarner Losh" ______ ____ _____ _____ ", 282088b4f5fSWarner Losh" | ____| | _ \\ / ____| __ \\ ", 283088b4f5fSWarner Losh" | |___ _ __ ___ ___ | |_) | (___ | | | |", 284088b4f5fSWarner Losh" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 285088b4f5fSWarner Losh" | | | | | __/ __/| |_) |____) | |__| |", 286088b4f5fSWarner Losh" | | | | | | || | | |", 287088b4f5fSWarner Losh" |_| |_| \\___|\\___||____/|_____/|_____/ " 288aedd6be5SKyle Evans} 289aedd6be5SKyle Evansnone = {""} 290088b4f5fSWarner Losh 291a9edc01bSKyle Evansmenu_name_handlers = { 292b5746545SKyle Evans -- Menu name handlers should take the menu being drawn and entry being 293b5746545SKyle Evans -- drawn as parameters, and return the name of the item. 294b5746545SKyle Evans -- This is designed so that everything, including menu separators, may 295b5746545SKyle Evans -- have their names derived differently. The default action for entry 296a51f9f0cSKyle Evans -- types not specified here is to use entry.name directly. 297e2df27e3SKyle Evans [core.MENU_SEPARATOR] = function(_, entry) 298dd65496aSKyle Evans if entry.name ~= nil then 299a51f9f0cSKyle Evans if type(entry.name) == "function" then 300dd65496aSKyle Evans return entry.name() 301dd65496aSKyle Evans end 302a51f9f0cSKyle Evans return entry.name 303a51f9f0cSKyle Evans end 304dd65496aSKyle Evans return "" 305dd65496aSKyle Evans end, 306e2df27e3SKyle Evans [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 307aedd6be5SKyle Evans local carid = entry.carousel_id 308aedd6be5SKyle Evans local caridx = config.getCarouselIndex(carid) 3094f437f9eSKyle Evans local choices = entry.items 3104f437f9eSKyle Evans if type(choices) == "function" then 3114f437f9eSKyle Evans choices = choices() 3124f437f9eSKyle Evans end 3139f71d421SKyle Evans if #choices < caridx then 314aedd6be5SKyle Evans caridx = 1 315b5746545SKyle Evans end 316aedd6be5SKyle Evans return entry.name(caridx, choices[caridx], choices) 317b5746545SKyle Evans end, 318aedd6be5SKyle Evans} 319b5746545SKyle Evans 320a9edc01bSKyle Evansbranddefs = { 321699578a6SKyle Evans -- Indexed by valid values for loader_brand in loader.conf(5). Valid 322699578a6SKyle Evans -- keys are: graphic (table depicting graphic) 32329aa5794SKyle Evans ["fbsd"] = { 3241091c8feSKyle Evans graphic = fbsd_brand, 32529aa5794SKyle Evans }, 32629aa5794SKyle Evans ["none"] = { 32729aa5794SKyle Evans graphic = none, 32829aa5794SKyle Evans }, 329aedd6be5SKyle Evans} 33029aa5794SKyle Evans 331a9edc01bSKyle Evanslogodefs = { 332bb26c57dSKyle Evans -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 333752b2d40SKyle Evans -- are: requires_color (boolean), graphic (table depicting graphic), and 334bb26c57dSKyle Evans -- shift (table containing x and y). 335bb26c57dSKyle Evans ["tribute"] = { 3361091c8feSKyle Evans graphic = fbsd_brand, 337bb26c57dSKyle Evans }, 338bb26c57dSKyle Evans ["tributebw"] = { 3391091c8feSKyle Evans graphic = fbsd_brand, 340bb26c57dSKyle Evans }, 341bb26c57dSKyle Evans ["none"] = { 342752b2d40SKyle Evans graphic = none, 343bb26c57dSKyle Evans shift = {x = 17, y = 0}, 344bb26c57dSKyle Evans }, 345aedd6be5SKyle Evans} 346bb26c57dSKyle Evans 347a9edc01bSKyle Evansbrand_position = {x = 2, y = 1} 348a9edc01bSKyle Evanslogo_position = {x = 46, y = 4} 349a9edc01bSKyle Evansmenu_position = {x = 5, y = 10} 350a9edc01bSKyle Evansframe_size = {w = 42, h = 13} 351a9edc01bSKyle Evansdefault_shift = {x = 0, y = 0} 352a9edc01bSKyle Evansshift = default_shift 353a9edc01bSKyle Evans 354a9edc01bSKyle Evans-- Module exports 355a9edc01bSKyle Evansdrawer.default_brand = 'fbsd' 3562c690e2aSKyle Evansdrawer.default_color_logodef = 'orb' 3572c690e2aSKyle Evansdrawer.default_bw_logodef = 'orbbw' 358a9edc01bSKyle Evans 359a9edc01bSKyle Evansfunction drawer.addBrand(name, def) 360a9edc01bSKyle Evans branddefs[name] = def 361a9edc01bSKyle Evansend 362a9edc01bSKyle Evans 363a9edc01bSKyle Evansfunction drawer.addLogo(name, def) 364a9edc01bSKyle Evans logodefs[name] = def 365a9edc01bSKyle Evansend 366a9edc01bSKyle Evans 367379e652eSKyle Evansdrawer.frame_styles = { 368379e652eSKyle Evans -- Indexed by valid values for loader_menu_frame in loader.conf(5). 369379e652eSKyle Evans -- All of the keys appearing below must be set for any menu frame style 370379e652eSKyle Evans -- added to drawer.frame_styles. 371379e652eSKyle Evans ["ascii"] = { 372379e652eSKyle Evans horizontal = "-", 373379e652eSKyle Evans vertical = "|", 374379e652eSKyle Evans top_left = "+", 375379e652eSKyle Evans bottom_left = "+", 376379e652eSKyle Evans top_right = "+", 377379e652eSKyle Evans bottom_right = "+", 378379e652eSKyle Evans }, 379379e652eSKyle Evans ["single"] = { 38056758831SToomas Soome horizontal = "\xE2\x94\x80", 38156758831SToomas Soome vertical = "\xE2\x94\x82", 38256758831SToomas Soome top_left = "\xE2\x94\x8C", 38356758831SToomas Soome bottom_left = "\xE2\x94\x94", 38456758831SToomas Soome top_right = "\xE2\x94\x90", 38556758831SToomas Soome bottom_right = "\xE2\x94\x98", 386379e652eSKyle Evans }, 387379e652eSKyle Evans ["double"] = { 38856758831SToomas Soome horizontal = "\xE2\x95\x90", 38956758831SToomas Soome vertical = "\xE2\x95\x91", 39056758831SToomas Soome top_left = "\xE2\x95\x94", 39156758831SToomas Soome bottom_left = "\xE2\x95\x9A", 39256758831SToomas Soome top_right = "\xE2\x95\x97", 39356758831SToomas Soome bottom_right = "\xE2\x95\x9D", 394379e652eSKyle Evans }, 395379e652eSKyle Evans} 396379e652eSKyle Evans 39712eaa305SKyle Evansfunction drawer.drawscreen(menudef) 398088b4f5fSWarner Losh -- drawlogo() must go first. 399088b4f5fSWarner Losh -- it determines the positions of other elements 40012eaa305SKyle Evans drawlogo() 40112eaa305SKyle Evans drawbrand() 40212eaa305SKyle Evans drawbox() 40312eaa305SKyle Evans return drawmenu(menudef) 404088b4f5fSWarner Loshend 405088b4f5fSWarner Losh 406aedd6be5SKyle Evansreturn drawer 407