-- This is released into the public domain. -- No warranty is provided, implied or otherwise. -- s-bristol splash screen, login agent -- Named to allude to Plymouth (splash screen used in some linux distros) local callerPkg, callerPid, callerScr = ... local shutdownEmergency = neo.requestAccess("k.computer").shutdown neo.requestAccess("s.h.key_down") neo.requestAccess("s.h._kosneo_syslog") -- gpuG/performDisclaim are GPU management, while screen is used for prioritization local gpuG, performDisclaim, screen = nil local scrW, scrH local nssInst local console = {} local helpActive = false local buttonsActive = false -- Attempts to call upon nsm for a safe shutdown local function shutdown(reboot) local nsm = neo.requestAccess("x.neo.sys.manage") if nsm then nsm.shutdown(reboot) while true do coroutine.yield() end else shutdownEmergency(reboot) end end local function basicDraw(bg) local gpu = gpuG() pcall(gpu.setBackground, bg or 0xFFFFFF) pcall(gpu.setForeground, 0x000000) local ok, sw, sh = pcall(gpu.getResolution) if not ok then return end scrW, scrH = sw, sh pcall(gpu.fill, 1, 1, scrW, scrH, " ") pcall(gpu.set, 2, 2, "KittenOS NEO") local usage = math.floor((os.totalMemory() - os.freeMemory()) / 1024) pcall(gpu.set, 2, 3, "RAM Usage: " .. usage .. "K / " .. math.floor(os.totalMemory() / 1024) .. "K") local cut = 7 if buttonsActive then cut = 9 end local areaSize = scrH - cut local n2 = 0 if helpActive then if _VERSION == "Lua 5.2" then table.insert(console, "WARNING: Lua 5.2 memory usage issue!") table.insert(console, "Shift-right-click while holding the CPU/APU.") n2 = 2 end table.insert(console, "TAB to change option, ENTER to select.") n2 = n2 + 1 end for i = 1, areaSize do pcall(gpu.set, 2, 6 + i, console[#console + i - areaSize] or "") end for i = 1, n2 do table.remove(console, #console) end return gpu end local function consoleEventHandler(ev) if ev[1] == "h._kosneo_syslog" then local text = "" for i = 3, #ev do if i ~= 3 then text = text .. " " end text = text .. tostring(ev[i]) end table.insert(console, text) end end -- Attempts to get an NSS monitor with a priority list of screens local function retrieveNssMonitor(...) local spc = {...} gpuG = nil local subpool = {} while not gpuG do if performDisclaim then performDisclaim(true) performDisclaim = nil end -- nss available - this means the monitor pool is now ready. -- If no monitors are available, shut down now. -- NSS monitor pool output is smaller than, but similar to, Everest monitor data: -- {gpu, screenAddr} local pool = nssInst.getClaimable() while not pool[1] do -- wait for presumably a NSS notification consoleEventHandler({coroutine.yield()}) pool = nssInst.getClaimable() end subpool = {} -- Specifies which element to elevate to top priority local optimalSwap = nil local optimal = #spc + 1 if screen then for k, v in ipairs(pool) do for k2, v2 in ipairs(spc) do if v == v2 and optimal > k2 then optimalSwap, optimal = k, k2 break end end end end if optimalSwap then local swapA = pool[optimalSwap] pool[optimalSwap] = pool[1] pool[1] = swapA end for _, v in ipairs(pool) do local gpu = nssInst.claim(v) if gpu then local gcb = gpu() if gcb then pcall(function () gcb.setBackground(0x000020) local w, h = gcb.getResolution() gcb.fill(1, 1, w, h, " ") table.insert(subpool, {gpu, v}) end) end end end if subpool[1] then gpuG, screen = table.unpack(subpool[1]) end end -- done with search performDisclaim = function (full) nssInst.disclaim(subpool[1][2]) if full then for _, v in ipairs(subpool) do nssInst.disclaim(v[2]) end end end end local function sleep(t) neo.scheduleTimer(os.uptime() + t) while true do local ev = {coroutine.yield()} consoleEventHandler(ev) if ev[1] == "k.timer" then break end if ev[1] == "x.neo.sys.screens" then retrieveNssMonitor(screen) basicDraw() end end end local function alert(s) console = {s} helpActive, buttonsActive = false, false basicDraw() sleep(1) buttonsActive = true end local function finalPrompt() nssInst = neo.requestAccess("x.neo.sys.screens") if not nssInst then console = {"sys-glacier not available"} basicDraw() error("no nssInst") end retrieveNssMonitor() helpActive, buttonsActive = true, true -- This is nsm's final chance to make itself available and thus allow the password to be set local nsm = neo.requestAccess("x.neo.sys.manage") local waiting = true local safeModeActive = false local password = "" if nsm then password = nsm.getSetting("password") if nsm.getSetting("sys-init.nologin") == "yes" then return false end end -- The actual main prompt loop while waiting do local entry = "" local entry2 = "" local active = true local pw = {function () return "Password: " .. entry2 end, function (key) if key >= 32 then entry = entry .. unicode.char(key) entry2 = entry2 .. "*" end if key == 13 then if entry == password then waiting = false else alert("Incorrect password") end active = false end end, 2, 5, scrW - 2} if password == "" then pw = {function () return "Log in..." end, function (key) if key == 13 then waiting = false active = false end end, 2, 5, scrW - 2} end local controls = { {function () return "" end, function (key) if key == 13 then alert("Shutting down...") shutdown(false) end end, 2, scrH - 1, 10}, {function () return "" end, function (key) if key == 13 then alert("Rebooting...") shutdown(true) end end, 13, scrH - 1, 8}, {function () return "" end, function (key) if key == 13 then safeModeActive = true alert("Login to activate Safe Mode.") end end, 22, scrH - 1, 11}, pw, } local control = #controls local lastKeyboard while active do local gpu = basicDraw() if gpu then for k, v in ipairs(controls) do if k == control then pcall(gpu.setBackground, 0x000000) pcall(gpu.setForeground, 0xFFFFFF) else pcall(gpu.setBackground, 0xFFFFFF) pcall(gpu.setForeground, 0x000000) end pcall(gpu.fill, v[3], v[4], v[5], 1, " ") pcall(gpu.set, v[3], v[4], v[1]()) end end -- event handling... local sig = {coroutine.yield()} consoleEventHandler(sig) if sig[1] == "x.neo.sys.screens" then -- We need to reinit screens no matter what. retrieveNssMonitor(screen) end if sig[1] == "h.key_down" then if sig[2] ~= lastKeyboard then lastKeyboard = sig[2] local nScreen = nssInst.getMonitorByKeyboard(lastKeyboard) if nScreen and nScreen ~= screen then neo.emergency("new primary:", nScreen) retrieveNssMonitor(nScreen, screen) basicDraw() end end if sig[4] == 15 then -- this makes sense in context control = control % #controls control = control + 1 else controls[control][2](sig[3]) end end end end helpActive, buttonsActive = false, false return safeModeActive end local function postPrompt() local nsm = neo.requestAccess("x.neo.sys.manage") local sh = "sys-everest" console = {"Unable to get shell (no sys-glacier)"} if nsm then sh = nsm.getSetting("sys-init.shell") or sh console = {"Starting " .. sh} end basicDraw() performDisclaim() neo.executeAsync(sh) -- There's a delay here to allow taking the monitor. sleep(0.5) for i = 1, 9 do local v = neo.requestAccess("x.neo.sys.session") sleep(0.5) -- Important timing - allows it to take the monitor if v then return end end -- ...oh. hope this works then? console = {"x.neo.sys.session not found, try Safe Mode."} retrieveNssMonitor(screen) basicDraw() sleep(1) shutdown(true) end local function initializeSystem() -- System has just booted, bristol is in charge -- No screen configuration, so just guess. -- Note that we should try to keep going with this if there's no reason to do otherwise. local gpuAc = neo.requestAccess("c.gpu") local screenAc = neo.requestAccess("c.screen") local gpu -- time to setup gpu/screen variables! if gpuAc and screenAc then local scrBestWHD = 0 for s in screenAc.list() do for g in gpuAc.list() do g.bind(s.address, false) local whd = g.maxDepth() if whd > scrBestWHD then screen = s gpu = g scrBestWHD = whd end end end end if gpu then screen = screen.address gpu.bind(screen, true) local gW, gH = gpu.maxResolution() gW, gH = math.min(80, gW), math.min(25, gH) pcall(gpu.setResolution, gW, gH) pcall(gpu.setDepth, gpu.maxDepth()) -- can crash on OCEmu if done at the "wrong time" end -- Setup the new GPU provider gpuG = function () return gpu end -- local w = 1 local steps = {"sys-glacier"} for i = 1, 4 do table.insert(steps, "WAIT") end table.insert(steps, "INJECT") for i = 1, 8 do table.insert(steps, "WAIT") end local stepCount = #steps neo.scheduleTimer(os.uptime()) while true do local ev = {coroutine.yield()} consoleEventHandler(ev) if ev[1] == "k.timer" then if gpu then local bg = 0xFFFFFF if w < stepCount then local n = math.floor((w / stepCount) * 255) bg = (n * 0x10000) + (n * 0x100) + n end basicDraw(bg) end if steps[w] then if steps[w] == "INJECT" then local nsm = neo.requestAccess("x.neo.sys.manage") if not nsm then table.insert(console, "Settings not available for INJECT.") else local nextstepsA = {} local nextstepsB = {} for _, v in ipairs(neo.listApps()) do if nsm.getSetting("run." .. v) == "yes" then if v:sub(1, 4) == "sys-" then table.insert(nextstepsA, v) else table.insert(nextstepsB, v) end end end for _, v in ipairs(nextstepsB) do table.insert(steps, w + 1, v) end for _, v in ipairs(nextstepsA) do table.insert(steps, w + 1, v) end end elseif steps[w] == "WAIT" then else local v, err = neo.executeAsync(steps[w]) if not v then neo.emergency("failed start:", steps[w]) neo.emergency(err) end end else break end w = w + 1 neo.scheduleTimer(os.uptime() + 0.049) end end end -- Actual sequence if callerPkg ~= nil then screen = callerScr -- Skip to "System initialized" (Everest either logged off, or died & used a Saving Throw to restart) else initializeSystem() end -- System initialized if finalPrompt() then -- Safe Mode local nsm = neo.requestAccess("x.neo.sys.manage") if nsm then console = {"Rebooting for Safe Mode..."} basicDraw() for _, v in ipairs(nsm.listSettings()) do if v ~= "password" then nsm.delSetting(v) end end else -- assume sysconf.lua did something very bad console = {"No NSM. Wiping configuration completely."} local fs = neo.requestAccess("c.filesystem") if not fs then table.insert(console, "Failed to get permission, you're doomed.") else fs.primary.remove("/data/sys-glacier/sysconf.lua") end basicDraw() end -- Do not give anything a chance to alter the new configuration shutdownEmergency(true) return end postPrompt()