1-- 2-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3-- 4-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org> 5-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org> 6-- All rights reserved. 7-- 8-- Redistribution and use in source and binary forms, with or without 9-- modification, are permitted provided that the following conditions 10-- are met: 11-- 1. Redistributions of source code must retain the above copyright 12-- notice, this list of conditions and the following disclaimer. 13-- 2. Redistributions in binary form must reproduce the above copyright 14-- notice, this list of conditions and the following disclaimer in the 15-- documentation and/or other materials provided with the distribution. 16-- 17-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20-- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27-- SUCH DAMAGE. 28-- 29-- $FreeBSD$ 30-- 31 32local color = require("color") 33local config = require("config") 34local core = require("core") 35local screen = require("screen") 36 37local drawer = {} 38 39local fbsd_logo 40local beastie_color 41local beastie 42local fbsd_logo_v 43local orb_color 44local orb 45local none 46local none_shifted = false 47 48local function menu_entry_name(drawing_menu, entry) 49 local name_handler = drawer.menu_name_handlers[entry.entry_type] 50 51 if name_handler ~= nil then 52 return name_handler(drawing_menu, entry) 53 end 54 if type(entry.name) == "function" then 55 return entry.name() 56 end 57 return entry.name 58end 59 60local function shift_brand_text(shift) 61 drawer.brand_position.x = drawer.brand_position.x + shift.x 62 drawer.brand_position.y = drawer.brand_position.y + shift.y 63 drawer.menu_position.x = drawer.menu_position.x + shift.x 64 drawer.menu_position.y = drawer.menu_position.y + shift.y 65 drawer.box_pos_dim.x = drawer.box_pos_dim.x + shift.x 66 drawer.box_pos_dim.y = drawer.box_pos_dim.y + shift.y 67end 68 69fbsd_logo = { 70" ______ ____ _____ _____ ", 71" | ____| | _ \\ / ____| __ \\ ", 72" | |___ _ __ ___ ___ | |_) | (___ | | | |", 73" | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", 74" | | | | | __/ __/| |_) |____) | |__| |", 75" | | | | | | || | | |", 76" |_| |_| \\___|\\___||____/|_____/|_____/ " 77} 78 79beastie_color = { 80" \027[31m, ,", 81" /( )`", 82" \\ \\___ / |", 83" /- \027[37m_\027[31m `-/ '", 84" (\027[37m/\\/ \\\027[31m \\ /\\", 85" \027[37m/ / |\027[31m ` \\", 86" \027[34mO O \027[37m) \027[31m/ |", 87" \027[37m`-^--'\027[31m`< '", 88" (_.) _ ) /", 89" `.___/` /", 90" `-----' /", 91" \027[33m<----.\027[31m __ / __ \\", 92" \027[33m<----|====\027[31mO)))\027[33m==\027[31m) \\) /\027[33m====|", 93" \027[33m<----'\027[31m `--' `.__,' \\", 94" | |", 95" \\ / /\\", 96" \027[36m______\027[31m( (_ / \\______/", 97" \027[36m,' ,-----' |", 98" `--{__________)\027[37m" 99} 100 101beastie = { 102" , ,", 103" /( )`", 104" \\ \\___ / |", 105" /- _ `-/ '", 106" (/\\/ \\ \\ /\\", 107" / / | ` \\", 108" O O ) / |", 109" `-^--'`< '", 110" (_.) _ ) /", 111" `.___/` /", 112" `-----' /", 113" <----. __ / __ \\", 114" <----|====O)))==) \\) /====|", 115" <----' `--' `.__,' \\", 116" | |", 117" \\ / /\\", 118" ______( (_ / \\______/", 119" ,' ,-----' |", 120" `--{__________)" 121} 122 123fbsd_logo_v = { 124" ______", 125" | ____| __ ___ ___ ", 126" | |__ | '__/ _ \\/ _ \\", 127" | __|| | | __/ __/", 128" | | | | | | |", 129" |_| |_| \\___|\\___|", 130" ____ _____ _____", 131" | _ \\ / ____| __ \\", 132" | |_) | (___ | | | |", 133" | _ < \\___ \\| | | |", 134" | |_) |____) | |__| |", 135" | | | |", 136" |____/|_____/|_____/" 137} 138 139orb_color = { 140" \027[31m``` \027[31;1m`\027[31m", 141" s` `.....---...\027[31;1m....--.``` -/\027[31m", 142" +o .--` \027[31;1m/y:` +.\027[31m", 143" yo`:. \027[31;1m:o `+-\027[31m", 144" y/ \027[31;1m-/` -o/\027[31m", 145" .- \027[31;1m::/sy+:.\027[31m", 146" / \027[31;1m`-- /\027[31m", 147" `: \027[31;1m:`\027[31m", 148" `: \027[31;1m:`\027[31m", 149" / \027[31;1m/\027[31m", 150" .- \027[31;1m-.\027[31m", 151" -- \027[31;1m-.\027[31m", 152" `:` \027[31;1m`:`", 153" \027[31;1m.-- `--.", 154" .---.....----.\027[37m" 155} 156 157orb = { 158" ``` `", 159" s` `.....---.......--.``` -/", 160" +o .--` /y:` +.", 161" yo`:. :o `+-", 162" y/ -/` -o/", 163" .- ::/sy+:.", 164" / `-- /", 165" `: :`", 166" `: :`", 167" / /", 168" .- -.", 169" -- -.", 170" `:` `:`", 171" .-- `--.", 172" .---.....----." 173} 174 175none = {""} 176 177-- Module exports 178drawer.menu_name_handlers = { 179 -- Menu name handlers should take the menu being drawn and entry being 180 -- drawn as parameters, and return the name of the item. 181 -- This is designed so that everything, including menu separators, may 182 -- have their names derived differently. The default action for entry 183 -- types not specified here is to use entry.name directly. 184 [core.MENU_SEPARATOR] = function(_, entry) 185 if entry.name ~= nil then 186 if type(entry.name) == "function" then 187 return entry.name() 188 end 189 return entry.name 190 end 191 return "" 192 end, 193 [core.MENU_CAROUSEL_ENTRY] = function(_, entry) 194 local carid = entry.carousel_id 195 local caridx = config.getCarouselIndex(carid) 196 local choices = entry.items 197 if type(choices) == "function" then 198 choices = choices() 199 end 200 if #choices < caridx then 201 caridx = 1 202 end 203 return entry.name(caridx, choices[caridx], choices) 204 end, 205} 206 207drawer.brand_position = {x = 2, y = 1} 208drawer.logo_position = {x = 46, y = 1} 209drawer.menu_position = {x = 6, y = 11} 210drawer.box_pos_dim = {x = 3, y = 10, w = 41, h = 11} 211 212drawer.branddefs = { 213 -- Indexed by valid values for loader_brand in loader.conf(5). Valid 214 -- keys are: graphic (table depicting graphic) 215 ["fbsd"] = { 216 graphic = fbsd_logo, 217 }, 218 ["none"] = { 219 graphic = none, 220 }, 221} 222 223drawer.logodefs = { 224 -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys 225 -- are: requires_color (boolean), graphic (table depicting graphic), and 226 -- shift (table containing x and y). 227 ["beastie"] = { 228 requires_color = true, 229 graphic = beastie_color, 230 }, 231 ["beastiebw"] = { 232 graphic = beastie, 233 }, 234 ["fbsdbw"] = { 235 graphic = fbsd_logo_v, 236 shift = {x = 5, y = 4}, 237 }, 238 ["orb"] = { 239 requires_color = true, 240 graphic = orb_color, 241 shift = {x = 2, y = 4}, 242 }, 243 ["orbbw"] = { 244 graphic = orb, 245 shift = {x = 2, y = 4}, 246 }, 247 ["tribute"] = { 248 graphic = fbsd_logo, 249 }, 250 ["tributebw"] = { 251 graphic = fbsd_logo, 252 }, 253 ["none"] = { 254 graphic = none, 255 shift = {x = 17, y = 0}, 256 }, 257} 258 259function drawer.drawscreen(menu_opts) 260 -- drawlogo() must go first. 261 -- it determines the positions of other elements 262 drawer.drawlogo() 263 drawer.drawbrand() 264 drawer.drawbox() 265 return drawer.drawmenu(menu_opts) 266end 267 268function drawer.drawmenu(m) 269 local x = drawer.menu_position.x 270 local y = drawer.menu_position.y 271 272 -- print the menu and build the alias table 273 local alias_table = {} 274 local entry_num = 0 275 local menu_entries = m.entries 276 if type(menu_entries) == "function" then 277 menu_entries = menu_entries() 278 end 279 for line_num, e in ipairs(menu_entries) do 280 -- Allow menu items to be conditionally visible by specifying 281 -- a visible function. 282 if e.visible ~= nil and not e.visible() then 283 goto continue 284 end 285 if e.entry_type ~= core.MENU_SEPARATOR then 286 entry_num = entry_num + 1 287 screen.setcursor(x, y + line_num) 288 289 print(entry_num .. ". " .. menu_entry_name(m, e)) 290 291 -- fill the alias table 292 alias_table[tostring(entry_num)] = e 293 if e.alias ~= nil then 294 for _, a in ipairs(e.alias) do 295 alias_table[a] = e 296 end 297 end 298 else 299 screen.setcursor(x, y + line_num) 300 print(menu_entry_name(m, e)) 301 end 302 ::continue:: 303 end 304 return alias_table 305end 306 307 308function drawer.drawbox() 309 local x = drawer.box_pos_dim.x 310 local y = drawer.box_pos_dim.y 311 local w = drawer.box_pos_dim.w 312 local h = drawer.box_pos_dim.h 313 314 local hl = string.char(0xCD) 315 local vl = string.char(0xBA) 316 317 local tl = string.char(0xC9) 318 local bl = string.char(0xC8) 319 local tr = string.char(0xBB) 320 local br = string.char(0xBC) 321 322 screen.setcursor(x, y); print(tl) 323 screen.setcursor(x, y+h); print(bl) 324 screen.setcursor(x+w, y); print(tr) 325 screen.setcursor(x+w, y+h); print(br) 326 327 for i = 1, w-1 do 328 screen.setcursor(x+i, y) 329 print(hl) 330 screen.setcursor(x+i, y+h) 331 print(hl) 332 end 333 334 for i = 1, h-1 do 335 screen.setcursor(x, y+i) 336 print(vl) 337 screen.setcursor(x+w, y+i) 338 print(vl) 339 end 340 341 screen.setcursor(x+(w/2)-9, y) 342 print("Welcome to FreeBSD") 343end 344 345function drawer.draw(x, y, logo) 346 for i = 1, #logo do 347 screen.setcursor(x, y + i) 348 print(logo[i]) 349 end 350end 351 352function drawer.drawbrand() 353 local x = tonumber(loader.getenv("loader_brand_x")) or 354 drawer.brand_position.x 355 local y = tonumber(loader.getenv("loader_brand_y")) or 356 drawer.brand_position.y 357 358 local graphic = drawer.branddefs[loader.getenv("loader_brand")] 359 if graphic == nil then 360 graphic = fbsd_logo 361 end 362 drawer.draw(x, y, graphic) 363end 364 365function drawer.drawlogo() 366 local x = tonumber(loader.getenv("loader_logo_x")) or 367 drawer.logo_position.x 368 local y = tonumber(loader.getenv("loader_logo_y")) or 369 drawer.logo_position.y 370 371 local logo = loader.getenv("loader_logo") 372 local colored = color.isEnabled() 373 374 -- Lookup 375 local logodef = drawer.logodefs[logo] 376 377 if logodef ~= nil and logodef.graphic == none then 378 -- centre brand and text if no logo 379 if not none_shifted then 380 shift_brand_text(logodef.shift) 381 none_shifted = true 382 end 383 elseif logodef == nil or logodef.graphic == nil or 384 (not colored and logodef.requires_color) then 385 -- Choose a sensible default 386 if colored then 387 logodef = drawer.logodefs["orb"] 388 else 389 logodef = drawer.logodefs["orbbw"] 390 end 391 end 392 if logodef.shift ~= nil then 393 x = x + logodef.shift.x 394 y = y + logodef.shift.y 395 end 396 drawer.draw(x, y, logodef.graphic) 397end 398 399return drawer 400