Index: head/stand/lua/drawer.lua =================================================================== --- head/stand/lua/drawer.lua (revision 329639) +++ head/stand/lua/drawer.lua (revision 329640) @@ -1,380 +1,380 @@ -- -- Copyright (c) 2015 Pedro Souza -- Copyright (c) 2018 Kyle Evans -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -- SUCH DAMAGE. -- -- $FreeBSD$ -- local color = require("color"); local core = require("core"); local screen = require("screen"); local drawer = {}; local fbsd_logo; local beastie_color; local beastie; local fbsd_logo_v; local orb; local none; local none_shifted = false; +local menu_entry_name = function(drawing_menu, entry) + local name_handler = drawer.menu_name_handlers[entry.entry_type]; + + if (name_handler ~= nil) then + return name_handler(drawing_menu, entry); + end + return entry.name(); +end + +local shift_brand_text = function(shift) + drawer.brand_position.x = drawer.brand_position.x + shift.x; + drawer.brand_position.y = drawer.brand_position.y + shift.y; + drawer.menu_position.x = drawer.menu_position.x + shift.x; + drawer.menu_position.y = drawer.menu_position.y + shift.y; + drawer.box_pos_dim.x = drawer.box_pos_dim.x + shift.x; + drawer.box_pos_dim.y = drawer.box_pos_dim.y + shift.y; +end + drawer.menu_name_handlers = { -- Menu name handlers should take the menu being drawn and entry being -- drawn as parameters, and return the name of the item. -- This is designed so that everything, including menu separators, may -- have their names derived differently. The default action for entry -- types not specified here is to call and use entry.name(). [core.MENU_CAROUSEL_ENTRY] = function(drawing_menu, entry) local carid = entry.carousel_id; local caridx = menu.getCarouselIndex(carid); local choices = entry.items(); if (#choices < caridx) then caridx = 1; end return entry.name(caridx, choices[caridx], choices); end, }; drawer.brand_position = {x = 2, y = 1}; drawer.logo_position = {x = 46, y = 1}; drawer.menu_position = {x = 6, y = 11}; drawer.box_pos_dim = {x = 3, y = 10, w = 41, h = 11}; fbsd_logo = { " ______ ____ _____ _____ ", " | ____| | _ \\ / ____| __ \\ ", " | |___ _ __ ___ ___ | |_) | (___ | | | |", " | ___| '__/ _ \\/ _ \\| _ < \\___ \\| | | |", " | | | | | __/ __/| |_) |____) | |__| |", " | | | | | | || | | |", " |_| |_| \\___|\\___||____/|_____/|_____/ " }; beastie_color = { " \027[31m, ,", " /( )`", " \\ \\___ / |", " /- \027[37m_\027[31m `-/ '", " (\027[37m/\\/ \\\027[31m \\ /\\", " \027[37m/ / |\027[31m ` \\", " \027[34mO O \027[37m) \027[31m/ |", " \027[37m`-^--'\027[31m`< '", " (_.) _ ) /", " `.___/` /", " `-----' /", " \027[33m<----.\027[31m __ / __ \\", " \027[33m<----|====\027[31mO)))\027[33m==\027[31m) \\) /\027[33m====|", " \027[33m<----'\027[31m `--' `.__,' \\", " | |", " \\ / /\\", " \027[36m______\027[31m( (_ / \\______/", " \027[36m,' ,-----' |", " `--{__________)\027[37m" }; beastie = { " , ,", " /( )`", " \\ \\___ / |", " /- _ `-/ '", " (/\\/ \\ \\ /\\", " / / | ` \\", " O O ) / |", " `-^--'`< '", " (_.) _ ) /", " `.___/` /", " `-----' /", " <----. __ / __ \\", " <----|====O)))==) \\) /====|", " <----' `--' `.__,' \\", " | |", " \\ / /\\", " ______( (_ / \\______/", " ,' ,-----' |", " `--{__________)" }; fbsd_logo_v = { " ______", " | ____| __ ___ ___ ", " | |__ | '__/ _ \\/ _ \\", " | __|| | | __/ __/", " | | | | | | |", " |_| |_| \\___|\\___|", " ____ _____ _____", " | _ \\ / ____| __ \\", " | |_) | (___ | | | |", " | _ < \\___ \\| | | |", " | |_) |____) | |__| |", " | | | |", " |____/|_____/|_____/" }; orb_color = { " \027[31m``` \027[31;1m`\027[31m", " s` `.....---...\027[31;1m....--.``` -/\027[31m", " +o .--` \027[31;1m/y:` +.\027[31m", " yo`:. \027[31;1m:o `+-\027[31m", " y/ \027[31;1m-/` -o/\027[31m", " .- \027[31;1m::/sy+:.\027[31m", " / \027[31;1m`-- /\027[31m", " `: \027[31;1m:`\027[31m", " `: \027[31;1m:`\027[31m", " / \027[31;1m/\027[31m", " .- \027[31;1m-.\027[31m", " -- \027[31;1m-.\027[31m", " `:` \027[31;1m`:`", " \027[31;1m.-- `--.", " .---.....----.\027[37m" }; orb = { " ``` `", " s` `.....---.......--.``` -/", " +o .--` /y:` +.", " yo`:. :o `+-", " y/ -/` -o/", " .- ::/sy+:.", " / `-- /", " `: :`", " `: :`", " / /", " .- -.", " -- -.", " `:` `:`", " .-- `--.", " .---.....----." }; none = {""}; drawer.branddefs = { -- Indexed by valid values for loader_brand in loader.conf(5). Valid -- keys are: graphic (table depicting graphic) ["fbsd"] = { graphic = fbsd_logo, }, ["none"] = { graphic = none, }, }; drawer.logodefs = { -- Indexed by valid values for loader_logo in loader.conf(5). Valid keys -- are: requires_color (boolean), graphic (table depicting graphic), and -- shift (table containing x and y). ["beastie"] = { requires_color = true, graphic = beastie_color, }, ["beastiebw"] = { graphic = beastie, }, ["fbsdbw"] = { graphic = fbsd_logo_v, shift = {x = 5, y = 4}, }, ["orb"] = { requires_color = true, graphic = orb_color, shift = {x = 2, y = 4}, }, ["orbbw"] = { graphic = orb, shift = {x = 2, y = 4}, }, ["tribute"] = { graphic = fbsd_logo, }, ["tributebw"] = { graphic = fbsd_logo, }, ["none"] = { graphic = none, shift = {x = 17, y = 0}, }, }; function drawer.drawscreen(menu_opts) -- drawlogo() must go first. -- it determines the positions of other elements drawer.drawlogo(); drawer.drawbrand(); drawer.drawbox(); return drawer.drawmenu(menu_opts); end -function menu_entry_name(drawing_menu, entry) - local name_handler = drawer.menu_name_handlers[entry.entry_type]; - - if (name_handler ~= nil) then - return name_handler(drawing_menu, entry); - end - return entry.name(); -end - function drawer.drawmenu(m) x = drawer.menu_position.x; y = drawer.menu_position.y; -- print the menu and build the alias table local alias_table = {}; local entry_num = 0; local menu_entries = m.entries; if (type(menu_entries) == "function") then menu_entries = menu_entries(); end for line_num, e in ipairs(menu_entries) do -- Allow menu items to be conditionally visible by specifying -- a visible function. if (e.visible ~= nil) and (not e.visible()) then goto continue; end if (e.entry_type ~= core.MENU_SEPARATOR) then entry_num = entry_num + 1; screen.setcursor(x, y + line_num); print(entry_num .. ". " .. menu_entry_name(m, e)); -- fill the alias table alias_table[tostring(entry_num)] = e; if (e.alias ~= nil) then for n, a in ipairs(e.alias) do alias_table[a] = e; end end else screen.setcursor(x, y + line_num); print(menu_entry_name(m, e)); end ::continue:: end return alias_table; end function drawer.drawbox() x = drawer.box_pos_dim.x; y = drawer.box_pos_dim.y; w = drawer.box_pos_dim.w; h = drawer.box_pos_dim.h; local hl = string.char(0xCD); local vl = string.char(0xBA); local tl = string.char(0xC9); local bl = string.char(0xC8); local tr = string.char(0xBB); local br = string.char(0xBC); screen.setcursor(x, y); print(tl); screen.setcursor(x, y+h); print(bl); screen.setcursor(x+w, y); print(tr); screen.setcursor(x+w, y+h); print(br); for i = 1, w-1 do screen.setcursor(x+i, y); print(hl); screen.setcursor(x+i, y+h); print(hl); end for i = 1, h-1 do screen.setcursor(x, y+i); print(vl); screen.setcursor(x+w, y+i); print(vl); end screen.setcursor(x+(w/2)-9, y); print("Welcome to FreeBSD"); end function drawer.draw(x, y, logo) for i = 1, #logo do screen.setcursor(x, y + i); print(logo[i]); end end function drawer.drawbrand() local x = tonumber(loader.getenv("loader_brand_x")) or drawer.brand_position.x; local y = tonumber(loader.getenv("loader_brand_y")) or drawer.brand_position.y; local graphic = drawer.branddefs[loader.getenv("loader_brand")]; if (graphic == nil) then graphic = fbsd_logo; end drawer.draw(x, y, graphic); -end - -function shift_brand_text(shift) - drawer.brand_position.x = drawer.brand_position.x + shift.x; - drawer.brand_position.y = drawer.brand_position.y + shift.y; - drawer.menu_position.x = drawer.menu_position.x + shift.x; - drawer.menu_position.y = drawer.menu_position.y + shift.y; - drawer.box_pos_dim.x = drawer.box_pos_dim.x + shift.x; - drawer.box_pos_dim.y = drawer.box_pos_dim.y + shift.y; end function drawer.drawlogo() local x = tonumber(loader.getenv("loader_logo_x")) or drawer.logo_position.x; local y = tonumber(loader.getenv("loader_logo_y")) or drawer.logo_position.y; local logo = loader.getenv("loader_logo"); local colored = color.isEnabled(); -- Lookup local logodef = drawer.logodefs[logo]; if (logodef ~= nil) and (logodef.graphic == none) then -- centre brand and text if no logo if (not none_shifted) then shift_brand_text(logodef.shift); none_shifted = true; end elseif (logodef == nil) or (logodef.graphic == nil) or ((not colored) and logodef.requires_color) then -- Choose a sensible default if (colored) then logodef = drawer.logodefs["orb"]; else logodef = drawer.logodefs["orbbw"]; end end if (logodef.shift ~= nil) then x = x + logodef.shift.x; y = y + logodef.shift.y; end drawer.draw(x, y, logodef.graphic); end return drawer; Index: head/stand/lua/menu.lua =================================================================== --- head/stand/lua/menu.lua (revision 329639) +++ head/stand/lua/menu.lua (revision 329640) @@ -1,474 +1,474 @@ -- -- Copyright (c) 2015 Pedro Souza -- Copyright (C) 2018 Kyle Evans -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -- SUCH DAMAGE. -- -- $FreeBSD$ -- local core = require("core"); local color = require("color"); local config = require("config"); local screen = require("screen"); local drawer = require("drawer"); local menu = {}; -local OnOff; local skip; local run; local autoboot; local carousel_choices = {}; +local OnOff = function(str, b) + if (b) then + return str .. color.escapef(color.GREEN) .. "On" .. + color.escapef(color.WHITE); + else + return str .. color.escapef(color.RED) .. "off" .. + color.escapef(color.WHITE); + end +end + + menu.handlers = { -- Menu handlers take the current menu and selected entry as parameters, -- and should return a boolean indicating whether execution should -- continue or not. The return value may be omitted if this entry should -- have no bearing on whether we continue or not, indicating that we -- should just continue after execution. [core.MENU_ENTRY] = function(current_menu, entry) -- run function entry.func(); end, [core.MENU_CAROUSEL_ENTRY] = function(current_menu, entry) -- carousel (rotating) functionality local carid = entry.carousel_id; local caridx = menu.getCarouselIndex(carid); local choices = entry.items(); if (#choices > 0) then caridx = (caridx % #choices) + 1; menu.setCarouselIndex(carid, caridx); entry.func(caridx, choices[caridx], choices); end end, [core.MENU_SUBMENU] = function(current_menu, entry) -- recurse return menu.run(entry.submenu()); end, [core.MENU_RETURN] = function(current_menu, entry) -- allow entry to have a function/side effect if (entry.func ~= nil) then entry.func(); end return false; end, }; -- loader menu tree is rooted at menu.welcome menu.boot_options = { entries = { -- return to welcome menu { entry_type = core.MENU_RETURN, name = function() return "Back to main menu" .. color.highlight(" [Backspace]"); end }, -- load defaults { entry_type = core.MENU_ENTRY, name = function() return "Load System " .. color.highlight("D") .. "efaults"; end, func = function() core.setDefaults(); end, alias = {"d", "D"} }, { entry_type = core.MENU_SEPARATOR, name = function() return ""; end }, { entry_type = core.MENU_SEPARATOR, name = function() return "Boot Options:"; end }, -- acpi { entry_type = core.MENU_ENTRY, name = function() return OnOff(color.highlight("A") .. "CPI :", core.acpi); end, func = function() core.setACPI(); end, alias = {"a", "A"} }, -- safe mode { entry_type = core.MENU_ENTRY, name = function() return OnOff("Safe " .. color.highlight("M") .. "ode :", core.sm); end, func = function() core.setSafeMode(); end, alias = {"m", "M"} }, -- single user { entry_type = core.MENU_ENTRY, name = function() return OnOff(color.highlight("S") .. "ingle user:", core.su); end, func = function() core.setSingleUser(); end, alias = {"s", "S"} }, -- verbose boot { entry_type = core.MENU_ENTRY, name = function() return OnOff(color.highlight("V") .. "erbose :", core.verbose); end, func = function() core.setVerbose(); end, alias = {"v", "V"} }, }, }; menu.welcome = { entries = function() local menu_entries = menu.welcome.all_entries; -- Swap the first two menu items on single user boot if (core.isSingleUserBoot()) then -- We'll cache the swapped menu, for performance if (menu.welcome.swapped_menu ~= nil) then return menu.welcome.swapped_menu; end -- Shallow copy the table menu_entries = core.shallowCopyTable(menu_entries); -- Swap the first two menu entries menu_entries[1], menu_entries[2] = menu_entries[2], menu_entries[1]; -- Then set their names to their alternate names menu_entries[1].name, menu_entries[2].name = menu_entries[1].alternate_name, menu_entries[2].alternate_name; menu.welcome.swapped_menu = menu_entries; end return menu_entries; end, all_entries = { -- boot multi user { entry_type = core.MENU_ENTRY, name = function() return color.highlight("B") .. "oot Multi user " .. color.highlight("[Enter]"); end, -- Not a standard menu entry function! alternate_name = function() return color.highlight("B") .. "oot Multi user"; end, func = function() core.setSingleUser(false); core.boot(); end, alias = {"b", "B"} }, -- boot single user { entry_type = core.MENU_ENTRY, name = function() return "Boot " .. color.highlight("S") .. "ingle user"; end, -- Not a standard menu entry function! alternate_name = function() return "Boot " .. color.highlight("S") .. "ingle user " .. color.highlight("[Enter]"); end, func = function() core.setSingleUser(true); core.boot(); end, alias = {"s", "S"} }, -- escape to interpreter { entry_type = core.MENU_RETURN, name = function() return color.highlight("Esc") .. "ape to loader prompt"; end, func = function() loader.setenv("autoboot_delay", "NO"); end, alias = {core.KEYSTR_ESCAPE} }, -- reboot { entry_type = core.MENU_ENTRY, name = function() return color.highlight("R") .. "eboot"; end, func = function() loader.perform("reboot"); end, alias = {"r", "R"} }, { entry_type = core.MENU_SEPARATOR, name = function() return ""; end }, { entry_type = core.MENU_SEPARATOR, name = function() return "Options:"; end }, -- kernel options { entry_type = core.MENU_CAROUSEL_ENTRY, carousel_id = "kernel", items = core.kernelList, name = function(idx, choice, all_choices) if (#all_choices == 0) then return "Kernel: "; end local is_default = (idx == 1); local kernel_name = ""; local name_color; if (is_default) then name_color = color.escapef(color.GREEN); kernel_name = "default/"; else name_color = color.escapef(color.BLUE); end kernel_name = kernel_name .. name_color .. choice .. color.default(); return color.highlight("K") .. "ernel: " .. kernel_name .. " (" .. idx .. " of " .. #all_choices .. ")"; end, func = function(idx, choice, all_choices) config.selectkernel(choice); end, alias = {"k", "K"} }, -- boot options { entry_type = core.MENU_SUBMENU, name = function() return "Boot " .. color.highlight("O") .. "ptions"; end, submenu = function() return menu.boot_options; end, alias = {"o", "O"} }, }, }; -- The first item in every carousel is always the default item. function menu.getCarouselIndex(id) local val = carousel_choices[id]; if (val == nil) then return 1; end return val; end function menu.setCarouselIndex(id, idx) carousel_choices[id] = idx; end function menu.run(m) if (menu.skip()) then core.autoboot(); return false; end if (m == nil) then m = menu.welcome; end -- redraw screen screen.clear(); screen.defcursor(); local alias_table = drawer.drawscreen(m); menu.autoboot(); cont = true; while (cont) do local key = io.getchar(); -- Special key behaviors if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and (m ~= menu.welcome) then break; elseif (key == core.KEY_ENTER) then core.boot(); -- Should not return end key = string.char(key) -- check to see if key is an alias local sel_entry = nil; for k, v in pairs(alias_table) do if (key == k) then sel_entry = v; end end -- if we have an alias do the assigned action: if (sel_entry ~= nil) then -- Get menu handler local handler = menu.handlers[sel_entry.entry_type]; if (handler ~= nil) then -- The handler's return value indicates whether -- we need to exit this menu. An omitted return -- value means "continue" by default. cont = handler(m, sel_entry); if (cont == nil) then cont = true; end end -- if we got an alias key the screen is out of date: screen.clear(); screen.defcursor(); alias_table = drawer.drawscreen(m); end end if (m == menu.welcome) then screen.defcursor(); print("Exiting menu!"); config.loadelf(); return false; end return true; end function menu.skip() if (core.isSerialBoot()) then return true; end local c = string.lower(loader.getenv("console") or ""); if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then return true; end c = string.lower(loader.getenv("beastie_disable") or ""); print("beastie_disable", c); return c == "yes"; end function menu.autoboot() if (menu.already_autoboot == true) then return; end menu.already_autoboot = true; local ab = loader.getenv("autoboot_delay"); if (ab ~= nil) and (ab:lower() == "no") then return; elseif (tonumber(ab) == -1) then core.boot(); end ab = tonumber(ab) or 10; local x = loader.getenv("loader_menu_timeout_x") or 5; local y = loader.getenv("loader_menu_timeout_y") or 22; local endtime = loader.time() + ab; local time; repeat time = endtime - loader.time(); screen.setcursor(x, y); print("Autoboot in " .. time .. " seconds, hit [Enter] to boot" .. " or any other key to stop "); screen.defcursor(); if (io.ischar()) then local ch = io.getchar(); if (ch == core.KEY_ENTER) then break; else -- erase autoboot msg screen.setcursor(0, y); print(" " .. " "); screen.defcursor(); return; end end loader.delay(50000); until time <= 0; core.boot(); -end - -function OnOff(str, b) - if (b) then - return str .. color.escapef(color.GREEN) .. "On" .. - color.escapef(color.WHITE); - else - return str .. color.escapef(color.RED) .. "off" .. - color.escapef(color.WHITE); - end end return menu; Index: head/stand/lua/screen.lua =================================================================== --- head/stand/lua/screen.lua (revision 329639) +++ head/stand/lua/screen.lua (revision 329640) @@ -1,85 +1,85 @@ -- -- Copyright (c) 2015 Pedro Souza -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions -- are met: -- 1. Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution. -- -- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -- ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -- SUCH DAMAGE. -- -- $FreeBSD$ -- local color = require("color"); local core = require("core"); local screen = {}; -- XXX TODO: This should be fixed in the interpreter to not print decimals -function intstring(num) +local intstring = function(num) local str = tostring(num); local decimal = str:find("%."); if (decimal) then return str:sub(1, decimal - 1); end return str; end function screen.clear() if (core.isSerialBoot()) then return; end loader.printc("\027[H\027[J"); end function screen.setcursor(x, y) if (core.isSerialBoot()) then return; end loader.printc("\027[" .. intstring(y) .. ";" .. intstring(x) .. "H"); end function screen.setforeground(c) if (color.disabled) then return c; end loader.printc("\027[3" .. c .. "m"); end function screen.setbackground(c) if (color.disabled) then return c; end loader.printc("\027[4" .. c .. "m"); end function screen.defcolor() loader.printc(color.default()); end function screen.defcursor() if (core.isSerialBoot()) then return; end loader.printc("\027[25;0H"); end return screen;