]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/lua/drawer.lua
lualoader: Hide most of the internal drawing functions
[FreeBSD/FreeBSD.git] / stand / lua / drawer.lua
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
32 local color = require("color")
33 local config = require("config")
34 local core = require("core")
35 local screen = require("screen")
36
37 local drawer = {}
38
39 local fbsd_brand
40 local none
41
42 local function menuEntryName(drawing_menu, entry)
43         local name_handler = drawer.menu_name_handlers[entry.entry_type]
44
45         if name_handler ~= nil then
46                 return name_handler(drawing_menu, entry)
47         end
48         if type(entry.name) == "function" then
49                 return entry.name()
50         end
51         return entry.name
52 end
53
54 local function getBranddef(brand)
55         if brand == nil then
56                 return nil
57         end
58         -- Look it up
59         local branddef = drawer.branddefs[brand]
60
61         -- Try to pull it in
62         if branddef == nil then
63                 try_include('brand-' .. brand)
64                 branddef = drawer.branddefs[brand]
65         end
66
67         return branddef
68 end
69
70 local function getLogodef(logo)
71         if logo == nil then
72                 return nil
73         end
74         -- Look it up
75         local logodef = drawer.logodefs[logo]
76
77         -- Try to pull it in
78         if logodef == nil then
79                 try_include('logo-' .. logo)
80                 logodef = drawer.logodefs[logo]
81         end
82
83         return logodef
84 end
85
86 local function draw(x, y, logo)
87         for i = 1, #logo do
88                 screen.setcursor(x, y + i - 1)
89                 printc(logo[i])
90         end
91 end
92
93 local function drawmenu(menudef)
94         local x = drawer.menu_position.x
95         local y = drawer.menu_position.y
96
97         x = x + drawer.shift.x
98         y = y + drawer.shift.y
99
100         -- print the menu and build the alias table
101         local alias_table = {}
102         local entry_num = 0
103         local menu_entries = menudef.entries
104         local effective_line_num = 0
105         if type(menu_entries) == "function" then
106                 menu_entries = menu_entries()
107         end
108         for _, e in ipairs(menu_entries) do
109                 -- Allow menu items to be conditionally visible by specifying
110                 -- a visible function.
111                 if e.visible ~= nil and not e.visible() then
112                         goto continue
113                 end
114                 effective_line_num = effective_line_num + 1
115                 if e.entry_type ~= core.MENU_SEPARATOR then
116                         entry_num = entry_num + 1
117                         screen.setcursor(x, y + effective_line_num)
118
119                         printc(entry_num .. ". " .. menuEntryName(menudef, e))
120
121                         -- fill the alias table
122                         alias_table[tostring(entry_num)] = e
123                         if e.alias ~= nil then
124                                 for _, a in ipairs(e.alias) do
125                                         alias_table[a] = e
126                                 end
127                         end
128                 else
129                         screen.setcursor(x, y + effective_line_num)
130                         printc(menuEntryName(menudef, e))
131                 end
132                 ::continue::
133         end
134         return alias_table
135 end
136
137 local function drawbox()
138         local x = drawer.menu_position.x - 3
139         local y = drawer.menu_position.y - 1
140         local w = drawer.frame_size.w
141         local h = drawer.frame_size.h
142
143         local framestyle = loader.getenv("loader_menu_frame") or "double"
144         local framespec = drawer.frame_styles[framestyle]
145         -- If we don't have a framespec for the current frame style, just don't
146         -- draw a box.
147         if framespec == nil then
148                 return
149         end
150
151         local hl = framespec.horizontal
152         local vl = framespec.vertical
153
154         local tl = framespec.top_left
155         local bl = framespec.bottom_left
156         local tr = framespec.top_right
157         local br = framespec.bottom_right
158
159         x = x + drawer.shift.x
160         y = y + drawer.shift.y
161
162         screen.setcursor(x, y); printc(tl)
163         screen.setcursor(x, y + h); printc(bl)
164         screen.setcursor(x + w, y); printc(tr)
165         screen.setcursor(x + w, y + h); printc(br)
166
167         screen.setcursor(x + 1, y)
168         for _ = 1, w - 1 do
169                 printc(hl)
170         end
171
172         screen.setcursor(x + 1, y + h)
173         for _ = 1, w - 1 do
174                 printc(hl)
175         end
176
177         for i = 1, h - 1 do
178                 screen.setcursor(x, y + i)
179                 printc(vl)
180                 screen.setcursor(x + w, y + i)
181                 printc(vl)
182         end
183
184         local menu_header = loader.getenv("loader_menu_title") or
185             "Welcome to FreeBSD"
186         local menu_header_align = loader.getenv("loader_menu_title_align")
187         local menu_header_x
188
189         if menu_header_align ~= nil then
190                 menu_header_align = menu_header_align:lower()
191                 if menu_header_align == "left" then
192                         -- Just inside the left border on top
193                         menu_header_x = x + 1
194                 elseif menu_header_align == "right" then
195                         -- Just inside the right border on top
196                         menu_header_x = x + w - #menu_header
197                 end
198         end
199         if menu_header_x == nil then
200                 menu_header_x = x + (w / 2) - (#menu_header / 2)
201         end
202         screen.setcursor(menu_header_x, y)
203         printc(menu_header)
204 end
205
206 local function drawbrand()
207         local x = tonumber(loader.getenv("loader_brand_x")) or
208             drawer.brand_position.x
209         local y = tonumber(loader.getenv("loader_brand_y")) or
210             drawer.brand_position.y
211
212         local branddef = getBranddef(loader.getenv("loader_brand"))
213
214         if branddef == nil then
215                 branddef = getBranddef(drawer.default_brand)
216         end
217
218         local graphic = branddef.graphic
219
220         x = x + drawer.shift.x
221         y = y + drawer.shift.y
222         draw(x, y, graphic)
223 end
224
225 local function drawlogo()
226         local x = tonumber(loader.getenv("loader_logo_x")) or
227             drawer.logo_position.x
228         local y = tonumber(loader.getenv("loader_logo_y")) or
229             drawer.logo_position.y
230
231         local logo = loader.getenv("loader_logo")
232         local colored = color.isEnabled()
233
234         local logodef = getLogodef(logo)
235
236         if logodef == nil or logodef.graphic == nil or
237             (not colored and logodef.requires_color) then
238                 -- Choose a sensible default
239                 if colored then
240                         logodef = getLogodef("orb")
241                 else
242                         logodef = getLogodef("orbbw")
243                 end
244         end
245
246         if logodef ~= nil and logodef.graphic == none then
247                 drawer.shift = logodef.shift
248         else
249                 drawer.shift = drawer.default_shift
250         end
251
252         x = x + drawer.shift.x
253         y = y + drawer.shift.y
254
255         if logodef ~= nil and logodef.shift ~= nil then
256                 x = x + logodef.shift.x
257                 y = y + logodef.shift.y
258         end
259
260         draw(x, y, logodef.graphic)
261 end
262
263 fbsd_brand = {
264 "  ______               ____   _____ _____  ",
265 " |  ____|             |  _ \\ / ____|  __ \\ ",
266 " | |___ _ __ ___  ___ | |_) | (___ | |  | |",
267 " |  ___| '__/ _ \\/ _ \\|  _ < \\___ \\| |  | |",
268 " | |   | | |  __/  __/| |_) |____) | |__| |",
269 " | |   | | |    |    ||     |      |      |",
270 " |_|   |_|  \\___|\\___||____/|_____/|_____/ "
271 }
272 none = {""}
273
274 -- Module exports
275 drawer.default_brand = 'fbsd'
276
277 drawer.menu_name_handlers = {
278         -- Menu name handlers should take the menu being drawn and entry being
279         -- drawn as parameters, and return the name of the item.
280         -- This is designed so that everything, including menu separators, may
281         -- have their names derived differently. The default action for entry
282         -- types not specified here is to use entry.name directly.
283         [core.MENU_SEPARATOR] = function(_, entry)
284                 if entry.name ~= nil then
285                         if type(entry.name) == "function" then
286                                 return entry.name()
287                         end
288                         return entry.name
289                 end
290                 return ""
291         end,
292         [core.MENU_CAROUSEL_ENTRY] = function(_, entry)
293                 local carid = entry.carousel_id
294                 local caridx = config.getCarouselIndex(carid)
295                 local choices = entry.items
296                 if type(choices) == "function" then
297                         choices = choices()
298                 end
299                 if #choices < caridx then
300                         caridx = 1
301                 end
302                 return entry.name(caridx, choices[caridx], choices)
303         end,
304 }
305
306 drawer.brand_position = {x = 2, y = 1}
307 drawer.logo_position = {x = 46, y = 4}
308 drawer.menu_position = {x = 5, y = 10}
309 drawer.frame_size = {w = 42, h = 13}
310 drawer.default_shift = {x = 0, y = 0}
311 drawer.shift = drawer.default_shift
312
313 drawer.branddefs = {
314         -- Indexed by valid values for loader_brand in loader.conf(5). Valid
315         -- keys are: graphic (table depicting graphic)
316         ["fbsd"] = {
317                 graphic = fbsd_brand,
318         },
319         ["none"] = {
320                 graphic = none,
321         },
322 }
323
324 function drawer.addBrand(name, def)
325         drawer.branddefs[name] = def
326 end
327
328 function drawer.addLogo(name, def)
329         drawer.logodefs[name] = def
330 end
331
332 drawer.logodefs = {
333         -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys
334         -- are: requires_color (boolean), graphic (table depicting graphic), and
335         -- shift (table containing x and y).
336         ["tribute"] = {
337                 graphic = fbsd_brand,
338         },
339         ["tributebw"] = {
340                 graphic = fbsd_brand,
341         },
342         ["none"] = {
343                 graphic = none,
344                 shift = {x = 17, y = 0},
345         },
346 }
347
348 drawer.frame_styles = {
349         -- Indexed by valid values for loader_menu_frame in loader.conf(5).
350         -- All of the keys appearing below must be set for any menu frame style
351         -- added to drawer.frame_styles.
352         ["ascii"] = {
353                 horizontal      = "-",
354                 vertical        = "|",
355                 top_left        = "+",
356                 bottom_left     = "+",
357                 top_right       = "+",
358                 bottom_right    = "+",
359         },
360         ["single"] = {
361                 horizontal      = "\xC4",
362                 vertical        = "\xB3",
363                 top_left        = "\xDA",
364                 bottom_left     = "\xC0",
365                 top_right       = "\xBF",
366                 bottom_right    = "\xD9",
367         },
368         ["double"] = {
369                 horizontal      = "\xCD",
370                 vertical        = "\xBA",
371                 top_left        = "\xC9",
372                 bottom_left     = "\xC8",
373                 top_right       = "\xBB",
374                 bottom_right    = "\xBC",
375         },
376 }
377
378 function drawer.drawscreen(menudef)
379         -- drawlogo() must go first.
380         -- it determines the positions of other elements
381         drawlogo()
382         drawbrand()
383         drawbox()
384         return drawmenu(menudef)
385 end
386
387 return drawer