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