mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-23 19:08:05 +11:00
dcd7154ec2
This license change should only increase the amount of stuff people can do in countries where the public domain is not a valid concept. If this license somehow restricts you as opposed to the previous one, please file an issue.
457 lines
12 KiB
Lua
457 lines
12 KiB
Lua
-- Copyright (C) 2018-2021 by KittenOS NEO contributors
|
|
--
|
|
-- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
|
--
|
|
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
-- THIS SOFTWARE.
|
|
|
|
-- 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 "<Shutdown>"
|
|
end, function (key)
|
|
if key == 13 then
|
|
alert("Shutting down...")
|
|
shutdown(false)
|
|
end
|
|
end, 2, scrH - 1, 10},
|
|
{function ()
|
|
return "<Reboot>"
|
|
end, function (key)
|
|
if key == 13 then
|
|
alert("Rebooting...")
|
|
shutdown(true)
|
|
end
|
|
end, 13, scrH - 1, 8},
|
|
{function ()
|
|
return "<Safe Mode>"
|
|
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()
|