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