]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/lua/drawer.lua
cr_canseejailproc(): New privilege, no direct check for UID 0
[FreeBSD/FreeBSD.git] / stand / lua / drawer.lua
1 --
2 -- SPDX-License-Identifier: BSD-2-Clause
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
30 local color = require("color")
31 local config = require("config")
32 local core = require("core")
33 local screen = require("screen")
34
35 local drawer = {}
36
37 local fbsd_brand
38 local none
39
40 local menu_name_handlers
41 local branddefs
42 local logodefs
43 local brand_position
44 local logo_position
45 local menu_position
46 local frame_size
47 local default_shift
48 local shift
49
50 local function menuEntryName(drawing_menu, entry)
51         local name_handler = menu_name_handlers[entry.entry_type]
52
53         if name_handler ~= nil then
54                 return name_handler(drawing_menu, entry)
55         end
56         if type(entry.name) == "function" then
57                 return entry.name()
58         end
59         return entry.name
60 end
61
62 local function processFile(gfxname)
63         if gfxname == nil then
64                 return false, "Missing filename"
65         end
66
67         local ret = try_include('gfx-' .. gfxname)
68         if ret == nil then
69                 return false, "Failed to include gfx-" .. gfxname
70         end
71
72         -- Legacy format
73         if type(ret) ~= "table" then
74                 return true
75         end
76
77         for gfxtype, def in pairs(ret) do
78                 if gfxtype == "brand" then
79                         drawer.addBrand(gfxname, def)
80                 elseif gfxtype == "logo" then
81                         drawer.addLogo(gfxname, def)
82                 else
83                         return false, "Unknown graphics type '" .. gfxtype ..
84                             "'"
85                 end
86         end
87
88         return true
89 end
90
91 local function getBranddef(brand)
92         if brand == nil then
93                 return nil
94         end
95         -- Look it up
96         local branddef = branddefs[brand]
97
98         -- Try to pull it in
99         if branddef == nil then
100                 local res, err = processFile(brand)
101                 if not res then
102                         -- This fallback should go away after FreeBSD 13.
103                         try_include('brand-' .. brand)
104                         -- If the fallback also failed, print whatever error
105                         -- we encountered in the original processing.
106                         if branddefs[brand] == nil then
107                                 print(err)
108                                 return nil
109                         end
110                 end
111
112                 branddef = branddefs[brand]
113         end
114
115         return branddef
116 end
117
118 local function getLogodef(logo)
119         if logo == nil then
120                 return nil
121         end
122         -- Look it up
123         local logodef = logodefs[logo]
124
125         -- Try to pull it in
126         if logodef == nil then
127                 local res, err = processFile(logo)
128                 if not res then
129                         -- This fallback should go away after FreeBSD 13.
130                         try_include('logo-' .. logo)
131                         -- If the fallback also failed, print whatever error
132                         -- we encountered in the original processing.
133                         if logodefs[logo] == nil then
134                                 print(err)
135                                 return nil
136                         end
137                 end
138
139                 logodef = logodefs[logo]
140         end
141
142         return logodef
143 end
144
145 local function draw(x, y, logo)
146         for i = 1, #logo do
147                 screen.setcursor(x, y + i - 1)
148                 printc(logo[i])
149         end
150 end
151
152 local function drawmenu(menudef)
153         local x = menu_position.x
154         local y = menu_position.y
155
156         x = x + shift.x
157         y = y + shift.y
158
159         -- print the menu and build the alias table
160         local alias_table = {}
161         local entry_num = 0
162         local menu_entries = menudef.entries
163         local effective_line_num = 0
164         if type(menu_entries) == "function" then
165                 menu_entries = menu_entries()
166         end
167         for _, e in ipairs(menu_entries) do
168                 -- Allow menu items to be conditionally visible by specifying
169                 -- a visible function.
170                 if e.visible ~= nil and not e.visible() then
171                         goto continue
172                 end
173                 effective_line_num = effective_line_num + 1
174                 if e.entry_type ~= core.MENU_SEPARATOR then
175                         entry_num = entry_num + 1
176                         screen.setcursor(x, y + effective_line_num)
177
178                         printc(entry_num .. ". " .. menuEntryName(menudef, e))
179
180                         -- fill the alias table
181                         alias_table[tostring(entry_num)] = e
182                         if e.alias ~= nil then
183                                 for _, a in ipairs(e.alias) do
184                                         alias_table[a] = e
185                                 end
186                         end
187                 else
188                         screen.setcursor(x, y + effective_line_num)
189                         printc(menuEntryName(menudef, e))
190                 end
191                 ::continue::
192         end
193         return alias_table
194 end
195
196 local function defaultframe()
197         if core.isSerialConsole() then
198                 return "ascii"
199         end
200         return "double"
201 end
202
203 local function drawframe()
204         local x = menu_position.x - 3
205         local y = menu_position.y - 1
206         local w = frame_size.w
207         local h = frame_size.h
208
209         local framestyle = loader.getenv("loader_menu_frame") or defaultframe()
210         local framespec = drawer.frame_styles[framestyle]
211         -- If we don't have a framespec for the current frame style, just don't
212         -- draw a box.
213         if framespec == nil then
214                 return false
215         end
216
217         local hl = framespec.horizontal
218         local vl = framespec.vertical
219
220         local tl = framespec.top_left
221         local bl = framespec.bottom_left
222         local tr = framespec.top_right
223         local br = framespec.bottom_right
224
225         x = x + shift.x
226         y = y + shift.y
227
228         if core.isFramebufferConsole() and loader.term_drawrect ~= nil then
229                 loader.term_drawrect(x, y, x + w, y + h)
230                 return true
231         end
232
233         screen.setcursor(x, y); printc(tl)
234         screen.setcursor(x, y + h); printc(bl)
235         screen.setcursor(x + w, y); printc(tr)
236         screen.setcursor(x + w, y + h); printc(br)
237
238         screen.setcursor(x + 1, y)
239         for _ = 1, w - 1 do
240                 printc(hl)
241         end
242
243         screen.setcursor(x + 1, y + h)
244         for _ = 1, w - 1 do
245                 printc(hl)
246         end
247
248         for i = 1, h - 1 do
249                 screen.setcursor(x, y + i)
250                 printc(vl)
251                 screen.setcursor(x + w, y + i)
252                 printc(vl)
253         end
254         return true
255 end
256
257 local function drawbox()
258         local x = menu_position.x - 3
259         local y = menu_position.y - 1
260         local w = frame_size.w
261         local menu_header = loader.getenv("loader_menu_title") or
262             "Welcome to FreeBSD"
263         local menu_header_align = loader.getenv("loader_menu_title_align")
264         local menu_header_x
265
266         x = x + shift.x
267         y = y + shift.y
268
269         if drawframe(x, y, w) == false then
270                 return
271         end
272
273         if menu_header_align ~= nil then
274                 menu_header_align = menu_header_align:lower()
275                 if menu_header_align == "left" then
276                         -- Just inside the left border on top
277                         menu_header_x = x + 1
278                 elseif menu_header_align == "right" then
279                         -- Just inside the right border on top
280                         menu_header_x = x + w - #menu_header
281                 end
282         end
283         if menu_header_x == nil then
284                 menu_header_x = x + (w // 2) - (#menu_header // 2)
285         end
286         screen.setcursor(menu_header_x - 1, y)
287         if menu_header ~= "" then
288                 printc(" " .. menu_header .. " ")
289         end
290
291 end
292
293 local function drawbrand()
294         local x = tonumber(loader.getenv("loader_brand_x")) or
295             brand_position.x
296         local y = tonumber(loader.getenv("loader_brand_y")) or
297             brand_position.y
298
299         local branddef = getBranddef(loader.getenv("loader_brand"))
300
301         if branddef == nil then
302                 branddef = getBranddef(drawer.default_brand)
303         end
304
305         local graphic = branddef.graphic
306
307         x = x + shift.x
308         y = y + shift.y
309         if branddef.shift ~= nil then
310                 x = x + branddef.shift.x
311                 y = y + branddef.shift.y
312         end
313
314         if core.isFramebufferConsole() and
315             loader.term_putimage ~= nil and
316             branddef.image ~= nil then
317                 if loader.term_putimage(branddef.image, 1, 1, 0, 7, 0)
318                 then
319                         return true
320                 end
321         end
322         draw(x, y, graphic)
323 end
324
325 local function drawlogo()
326         local x = tonumber(loader.getenv("loader_logo_x")) or
327             logo_position.x
328         local y = tonumber(loader.getenv("loader_logo_y")) or
329             logo_position.y
330
331         local logo = loader.getenv("loader_logo")
332         local colored = color.isEnabled()
333
334         local logodef = getLogodef(logo)
335
336         if logodef == nil or logodef.graphic == nil or
337             (not colored and logodef.requires_color) then
338                 -- Choose a sensible default
339                 if colored then
340                         logodef = getLogodef(drawer.default_color_logodef)
341                 else
342                         logodef = getLogodef(drawer.default_bw_logodef)
343                 end
344
345                 -- Something has gone terribly wrong.
346                 if logodef == nil then
347                         logodef = getLogodef(drawer.default_fallback_logodef)
348                 end
349         end
350
351         if logodef ~= nil and logodef.graphic == none then
352                 shift = logodef.shift
353         else
354                 shift = default_shift
355         end
356
357         x = x + shift.x
358         y = y + shift.y
359
360         if logodef ~= nil and logodef.shift ~= nil then
361                 x = x + logodef.shift.x
362                 y = y + logodef.shift.y
363         end
364
365         if core.isFramebufferConsole() and
366             loader.term_putimage ~= nil and
367             logodef.image ~= nil then
368                 local y1 = 15
369
370                 if logodef.image_rl ~= nil then
371                         y1 = logodef.image_rl
372                 end
373                 if loader.term_putimage(logodef.image, x, y, 0, y + y1, 0)
374                 then
375                         return true
376                 end
377         end
378         draw(x, y, logodef.graphic)
379 end
380
381 local function drawitem(func)
382         local console = loader.getenv("console")
383
384         for c in string.gmatch(console, "%w+") do
385                 loader.setenv("console", c)
386                 func()
387         end
388         loader.setenv("console", console)
389 end
390
391 fbsd_brand = {
392 "  ______               ____   _____ _____  ",
393 " |  ____|             |  _ \\ / ____|  __ \\ ",
394 " | |___ _ __ ___  ___ | |_) | (___ | |  | |",
395 " |  ___| '__/ _ \\/ _ \\|  _ < \\___ \\| |  | |",
396 " | |   | | |  __/  __/| |_) |____) | |__| |",
397 " | |   | | |    |    ||     |      |      |",
398 " |_|   |_|  \\___|\\___||____/|_____/|_____/ "
399 }
400 none = {""}
401
402 menu_name_handlers = {
403         -- Menu name handlers should take the menu being drawn and entry being
404         -- drawn as parameters, and return the name of the item.
405         -- This is designed so that everything, including menu separators, may
406         -- have their names derived differently. The default action for entry
407         -- types not specified here is to use entry.name directly.
408         [core.MENU_SEPARATOR] = function(_, entry)
409                 if entry.name ~= nil then
410                         if type(entry.name) == "function" then
411                                 return entry.name()
412                         end
413                         return entry.name
414                 end
415                 return ""
416         end,
417         [core.MENU_CAROUSEL_ENTRY] = function(_, entry)
418                 local carid = entry.carousel_id
419                 local caridx = config.getCarouselIndex(carid)
420                 local choices = entry.items
421                 if type(choices) == "function" then
422                         choices = choices()
423                 end
424                 if #choices < caridx then
425                         caridx = 1
426                 end
427                 return entry.name(caridx, choices[caridx], choices)
428         end,
429 }
430
431 branddefs = {
432         -- Indexed by valid values for loader_brand in loader.conf(5). Valid
433         -- keys are: graphic (table depicting graphic)
434         ["fbsd"] = {
435                 graphic = fbsd_brand,
436                 image = "/boot/images/freebsd-brand-rev.png",
437         },
438         ["none"] = {
439                 graphic = none,
440         },
441 }
442
443 logodefs = {
444         -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys
445         -- are: requires_color (boolean), graphic (table depicting graphic), and
446         -- shift (table containing x and y).
447         ["tribute"] = {
448                 graphic = fbsd_brand,
449         },
450         ["tributebw"] = {
451                 graphic = fbsd_brand,
452         },
453         ["none"] = {
454                 graphic = none,
455                 shift = {x = 17, y = 0},
456         },
457 }
458
459 brand_position = {x = 2, y = 1}
460 logo_position = {x = 46, y = 4}
461 menu_position = {x = 5, y = 10}
462 frame_size = {w = 42, h = 13}
463 default_shift = {x = 0, y = 0}
464 shift = default_shift
465
466 -- Module exports
467 drawer.default_brand = 'fbsd'
468 drawer.default_color_logodef = 'orb'
469 drawer.default_bw_logodef = 'orbbw'
470 -- For when things go terribly wrong; this def should be present here in the
471 -- drawer module in case it's a filesystem issue.
472 drawer.default_fallback_logodef = 'none'
473
474 -- These should go away after FreeBSD 13; only available for backwards
475 -- compatibility with old logo- files.
476 function drawer.addBrand(name, def)
477         branddefs[name] = def
478 end
479
480 function drawer.addLogo(name, def)
481         logodefs[name] = def
482 end
483
484 drawer.frame_styles = {
485         -- Indexed by valid values for loader_menu_frame in loader.conf(5).
486         -- All of the keys appearing below must be set for any menu frame style
487         -- added to drawer.frame_styles.
488         ["ascii"] = {
489                 horizontal      = "-",
490                 vertical        = "|",
491                 top_left        = "+",
492                 bottom_left     = "+",
493                 top_right       = "+",
494                 bottom_right    = "+",
495         },
496         ["single"] = {
497                 horizontal      = "\xE2\x94\x80",
498                 vertical        = "\xE2\x94\x82",
499                 top_left        = "\xE2\x94\x8C",
500                 bottom_left     = "\xE2\x94\x94",
501                 top_right       = "\xE2\x94\x90",
502                 bottom_right    = "\xE2\x94\x98",
503         },
504         ["double"] = {
505                 horizontal      = "\xE2\x95\x90",
506                 vertical        = "\xE2\x95\x91",
507                 top_left        = "\xE2\x95\x94",
508                 bottom_left     = "\xE2\x95\x9A",
509                 top_right       = "\xE2\x95\x97",
510                 bottom_right    = "\xE2\x95\x9D",
511         },
512 }
513
514 function drawer.drawscreen(menudef)
515         -- drawlogo() must go first.
516         -- it determines the positions of other elements
517         drawitem(drawlogo)
518         drawitem(drawbrand)
519         drawitem(drawbox)
520         return drawmenu(menudef)
521 end
522
523 return drawer