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 menuEntryName(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 shiftBrandText(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 259drawer.frame_styles = { 260 -- Indexed by valid values for loader_menu_frame in loader.conf(5). 261 -- All of the keys appearing below must be set for any menu frame style 262 -- added to drawer.frame_styles. 263 ["ascii"] = { 264 horizontal = "-", 265 vertical = "|", 266 top_left = "+", 267 bottom_left = "+", 268 top_right = "+", 269 bottom_right = "+", 270 }, 271 ["single"] = { 272 horizontal = "\xC4", 273 vertical = "\xB3", 274 top_left = "\xDA", 275 bottom_left = "\xC0", 276 top_right = "\xBF", 277 bottom_right = "\xD9", 278 }, 279 ["double"] = { 280 horizontal = "\xCD", 281 vertical = "\xBA", 282 top_left = "\xC9", 283 bottom_left = "\xC8", 284 top_right = "\xBB", 285 bottom_right = "\xBC", 286 }, 287} 288 289function drawer.drawscreen(menu_opts) 290 -- drawlogo() must go first. 291 -- it determines the positions of other elements 292 drawer.drawlogo() 293 drawer.drawbrand() 294 drawer.drawbox() 295 return drawer.drawmenu(menu_opts) 296end 297 298function drawer.drawmenu(menudef) 299 local x = drawer.menu_position.x 300 local y = drawer.menu_position.y 301 302 -- print the menu and build the alias table 303 local alias_table = {} 304 local entry_num = 0 305 local menu_entries = menudef.entries 306 local effective_line_num = 0 307 if type(menu_entries) == "function" then 308 menu_entries = menu_entries() 309 end 310 for _, e in ipairs(menu_entries) do 311 -- Allow menu items to be conditionally visible by specifying 312 -- a visible function. 313 if e.visible ~= nil and not e.visible() then 314 goto continue 315 end 316 effective_line_num = effective_line_num + 1 317 if e.entry_type ~= core.MENU_SEPARATOR then 318 entry_num = entry_num + 1 319 screen.setcursor(x, y + effective_line_num) 320 321 print(entry_num .. ". " .. menuEntryName(menudef, e)) 322 323 -- fill the alias table 324 alias_table[tostring(entry_num)] = e 325 if e.alias ~= nil then 326 for _, a in ipairs(e.alias) do 327 alias_table[a] = e 328 end 329 end 330 else 331 screen.setcursor(x, y + effective_line_num) 332 print(menuEntryName(menudef, e)) 333 end 334 ::continue:: 335 end 336 return alias_table 337end 338 339function drawer.drawbox() 340 local x = drawer.box_pos_dim.x 341 local y = drawer.box_pos_dim.y 342 local w = drawer.box_pos_dim.w 343 local h = drawer.box_pos_dim.h 344 345 local framestyle = loader.getenv("loader_menu_frame") or "double" 346 local framespec = drawer.frame_styles[framestyle] 347 -- If we don't have a framespec for the current frame style, just don't 348 -- draw a box. 349 if framespec == nil then 350 return 351 end 352 353 local hl = framespec.horizontal 354 local vl = framespec.vertical 355 356 local tl = framespec.top_left 357 local bl = framespec.bottom_left 358 local tr = framespec.top_right 359 local br = framespec.bottom_right 360 361 screen.setcursor(x, y); printc(tl) 362 screen.setcursor(x, y + h); printc(bl) 363 screen.setcursor(x + w, y); printc(tr) 364 screen.setcursor(x + w, y + h); printc(br) 365 366 screen.setcursor(x + 1, y) 367 for _ = 1, w - 1 do 368 printc(hl) 369 end 370 371 screen.setcursor(x + 1, y + h) 372 for _ = 1, w - 1 do 373 printc(hl) 374 end 375 376 for i = 1, h - 1 do 377 screen.setcursor(x, y + i) 378 printc(vl) 379 screen.setcursor(x + w, y + i) 380 printc(vl) 381 end 382 383 screen.setcursor(x + (w / 2) - 9, y) 384 printc("Welcome to FreeBSD") 385end 386 387function drawer.draw(x, y, logo) 388 for i = 1, #logo do 389 screen.setcursor(x, y + i) 390 print(logo[i]) 391 end 392end 393 394function drawer.drawbrand() 395 local x = tonumber(loader.getenv("loader_brand_x")) or 396 drawer.brand_position.x 397 local y = tonumber(loader.getenv("loader_brand_y")) or 398 drawer.brand_position.y 399 400 local graphic = drawer.branddefs[loader.getenv("loader_brand")] 401 if graphic == nil then 402 graphic = fbsd_logo 403 end 404 drawer.draw(x, y, graphic) 405end 406 407function drawer.drawlogo() 408 local x = tonumber(loader.getenv("loader_logo_x")) or 409 drawer.logo_position.x 410 local y = tonumber(loader.getenv("loader_logo_y")) or 411 drawer.logo_position.y 412 413 local logo = loader.getenv("loader_logo") 414 local colored = color.isEnabled() 415 416 -- Lookup 417 local logodef = drawer.logodefs[logo] 418 419 if logodef ~= nil and logodef.graphic == none then 420 -- centre brand and text if no logo 421 if not none_shifted then 422 shiftBrandText(logodef.shift) 423 none_shifted = true 424 end 425 elseif logodef == nil or logodef.graphic == nil or 426 (not colored and logodef.requires_color) then 427 -- Choose a sensible default 428 if colored then 429 logodef = drawer.logodefs["orb"] 430 else 431 logodef = drawer.logodefs["orbbw"] 432 end 433 end 434 if logodef.shift ~= nil then 435 x = x + logodef.shift.x 436 y = y + logodef.shift.y 437 end 438 drawer.draw(x, y, logodef.graphic) 439end 440 441return drawer 442