2021-01-12 23:11:00 +11:00
-- 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.
2018-03-19 10:10:54 +11:00
-- 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 " )
2018-09-28 08:01:05 +10:00
neo.requestAccess ( " s.h._kosneo_syslog " )
2018-03-19 10:10:54 +11:00
2018-09-28 08:01:05 +10:00
-- gpuG/performDisclaim are GPU management, while screen is used for prioritization
local gpuG , performDisclaim , screen = nil
2021-04-27 03:06:46 +10:00
local scrW , scrH = 1 , 1
2018-09-28 08:01:05 +10:00
local nssInst
local console = { }
local helpActive = false
local buttonsActive = false
2018-03-19 10:10:54 +11:00
-- Attempts to call upon nsm for a safe shutdown
local function shutdown ( reboot )
2018-03-19 12:27:30 +11:00
local nsm = neo.requestAccess ( " x.neo.sys.manage " )
if nsm then
nsm.shutdown ( reboot )
2018-03-19 10:10:54 +11:00
while true do
coroutine.yield ( )
end
else
shutdownEmergency ( reboot )
end
end
2018-09-28 08:01:05 +10:00
local function basicDraw ( bg )
local gpu = gpuG ( )
pcall ( gpu.setBackground , bg or 0xFFFFFF )
2018-04-12 09:04:16 +10:00
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 " )
2018-03-19 10:10:54 +11:00
local usage = math.floor ( ( os.totalMemory ( ) - os.freeMemory ( ) ) / 1024 )
2018-04-12 09:04:16 +10:00
pcall ( gpu.set , 2 , 3 , " RAM Usage: " .. usage .. " K / " .. math.floor ( os.totalMemory ( ) / 1024 ) .. " K " )
2018-09-28 08:01:05 +10:00
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 " " )
2018-03-19 10:10:54 +11:00
end
2018-09-28 08:01:05 +10:00
for i = 1 , n2 do
table.remove ( console , # console )
end
return gpu
2018-03-19 10:10:54 +11:00
end
2018-11-10 10:19:27 +11:00
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
2018-09-28 08:01:05 +10:00
-- Attempts to get an NSS monitor with a priority list of screens
local function retrieveNssMonitor ( ... )
local spc = { ... }
2018-03-23 10:37:19 +11:00
gpuG = nil
2018-03-19 10:10:54 +11:00
local subpool = { }
2018-03-23 10:37:19 +11:00
while not gpuG do
2018-03-19 10:10:54 +11:00
if performDisclaim then
2018-03-23 12:12:08 +11:00
performDisclaim ( true )
performDisclaim = nil
2018-03-19 10:10:54 +11:00
end
-- nss available - this means the monitor pool is now ready.
-- If no monitors are available, shut down now.
2018-03-19 12:27:30 +11:00
-- NSS monitor pool output is smaller than, but similar to, Everest monitor data:
2018-03-19 10:10:54 +11:00
-- {gpu, screenAddr}
2018-09-28 08:01:05 +10:00
local pool = nssInst.getClaimable ( )
2018-03-19 10:10:54 +11:00
while not pool [ 1 ] do
2018-09-28 08:01:05 +10:00
-- wait for presumably a NSS notification
consoleEventHandler ( { coroutine.yield ( ) } )
pool = nssInst.getClaimable ( )
2018-03-19 10:10:54 +11:00
end
subpool = { }
-- Specifies which element to elevate to top priority
local optimalSwap = nil
2018-09-28 08:01:05 +10:00
local optimal = # spc + 1
2018-03-19 10:10:54 +11:00
if screen then
for k , v in ipairs ( pool ) do
2018-09-28 08:01:05 +10:00
for k2 , v2 in ipairs ( spc ) do
if v == v2 and optimal > k2 then
optimalSwap , optimal = k , k2
break
end
2018-03-19 10:10:54 +11:00
end
end
end
if optimalSwap then
local swapA = pool [ optimalSwap ]
pool [ optimalSwap ] = pool [ 1 ]
pool [ 1 ] = swapA
end
for _ , v in ipairs ( pool ) do
2018-09-28 08:01:05 +10:00
local gpu = nssInst.claim ( v )
2018-03-19 10:10:54 +11:00
if gpu then
local gcb = gpu ( )
if gcb then
2018-04-12 09:04:16 +10:00
pcall ( function ( )
gcb.setBackground ( 0x000020 )
local w , h = gcb.getResolution ( )
gcb.fill ( 1 , 1 , w , h , " " )
table.insert ( subpool , { gpu , v } )
end )
2018-03-19 10:10:54 +11:00
end
end
end
2018-09-28 08:01:05 +10:00
if subpool [ 1 ] then
gpuG , screen = table.unpack ( subpool [ 1 ] )
2018-03-23 12:12:08 +11:00
end
2018-03-19 10:10:54 +11:00
end
-- done with search
2018-03-23 12:12:08 +11:00
performDisclaim = function ( full )
2018-09-28 08:01:05 +10:00
nssInst.disclaim ( subpool [ 1 ] [ 2 ] )
2018-03-23 12:12:08 +11:00
if full then
for _ , v in ipairs ( subpool ) do
2018-09-28 08:01:05 +10:00
nssInst.disclaim ( v [ 2 ] )
2018-03-23 12:12:08 +11:00
end
2018-03-19 10:10:54 +11:00
end
end
2018-09-28 08:01:05 +10:00
end
2018-03-19 10:10:54 +11:00
local function sleep ( t )
neo.scheduleTimer ( os.uptime ( ) + t )
while true do
local ev = { coroutine.yield ( ) }
2018-09-28 08:01:05 +10:00
consoleEventHandler ( ev )
2018-03-19 10:10:54 +11:00
if ev [ 1 ] == " k.timer " then
break
end
if ev [ 1 ] == " x.neo.sys.screens " then
2018-09-28 08:01:05 +10:00
retrieveNssMonitor ( screen )
basicDraw ( )
2018-03-19 10:10:54 +11:00
end
end
end
2018-09-28 08:01:05 +10:00
local function alert ( s )
console = { s }
helpActive , buttonsActive = false , false
basicDraw ( )
sleep ( 1 )
buttonsActive = true
end
2018-03-19 10:10:54 +11:00
local function finalPrompt ( )
2018-09-28 08:01:05 +10:00
nssInst = neo.requestAccess ( " x.neo.sys.screens " )
if not nssInst then
console = { " sys-glacier not available " }
basicDraw ( )
error ( " no nssInst " )
2018-03-19 10:10:54 +11:00
end
2018-09-28 08:01:05 +10:00
retrieveNssMonitor ( )
helpActive , buttonsActive = true , true
2018-03-19 10:10:54 +11:00
-- 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 " )
2018-03-28 00:40:05 +11:00
if nsm.getSetting ( " sys-init.nologin " ) == " yes " then
return false
end
2018-03-19 10:10:54 +11:00
end
-- The actual main prompt loop
while waiting do
local entry = " "
local entry2 = " "
local active = true
local pw = { function ( )
2018-03-21 02:19:36 +11:00
return " Password: " .. entry2
2018-03-19 10:10:54 +11:00
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
2018-09-28 08:01:05 +10:00
alert ( " Incorrect password " )
2018-03-19 10:10:54 +11:00
end
active = false
end
end , 2 , 5 , scrW - 2 }
if password == " " then
pw = { function ( )
2018-03-21 02:19:36 +11:00
return " Log in... "
2018-03-19 10:10:54 +11:00
end , function ( key )
if key == 13 then
waiting = false
active = false
end
end , 2 , 5 , scrW - 2 }
end
local controls = {
{ function ( )
2018-09-28 08:01:05 +10:00
return " <Shutdown> "
2018-03-19 10:10:54 +11:00
end , function ( key )
if key == 13 then
2018-09-28 08:01:05 +10:00
alert ( " Shutting down... " )
2018-03-19 10:10:54 +11:00
shutdown ( false )
end
2018-09-28 08:01:05 +10:00
end , 2 , scrH - 1 , 10 } ,
2018-03-19 10:10:54 +11:00
{ function ( )
2018-09-28 08:01:05 +10:00
return " <Reboot> "
2018-03-19 10:10:54 +11:00
end , function ( key )
if key == 13 then
2018-09-28 08:01:05 +10:00
alert ( " Rebooting... " )
2018-03-19 10:10:54 +11:00
shutdown ( true )
end
2018-09-28 08:01:05 +10:00
end , 13 , scrH - 1 , 8 } ,
2018-03-19 10:10:54 +11:00
{ function ( )
2018-09-28 08:01:05 +10:00
return " <Safe Mode> "
2018-03-19 10:10:54 +11:00
end , function ( key )
if key == 13 then
safeModeActive = true
2018-09-28 08:01:05 +10:00
alert ( " Login to activate Safe Mode. " )
2018-03-19 10:10:54 +11:00
end
2018-09-28 08:01:05 +10:00
end , 22 , scrH - 1 , 11 } ,
2018-03-19 10:10:54 +11:00
pw ,
}
local control = # controls
2018-09-28 08:01:05 +10:00
local lastKeyboard
2018-03-19 10:10:54 +11:00
while active do
2018-09-28 08:01:05 +10:00
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 ] ( ) )
2018-03-19 10:10:54 +11:00
end
end
-- event handling...
local sig = { coroutine.yield ( ) }
2018-09-28 08:01:05 +10:00
consoleEventHandler ( sig )
2018-03-19 10:10:54 +11:00
if sig [ 1 ] == " x.neo.sys.screens " then
-- We need to reinit screens no matter what.
2018-09-28 08:01:05 +10:00
retrieveNssMonitor ( screen )
2021-04-27 03:06:46 +10:00
active = false
2018-03-19 10:10:54 +11:00
end
if sig [ 1 ] == " h.key_down " then
2018-09-28 08:01:05 +10:00
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 )
2021-04-27 03:06:46 +10:00
active = false
2018-09-28 08:01:05 +10:00
end
end
2018-03-19 10:10:54 +11:00
if sig [ 4 ] == 15 then
-- this makes sense in context
2018-09-28 08:01:05 +10:00
control = control % # controls
2018-03-19 10:10:54 +11:00
control = control + 1
else
controls [ control ] [ 2 ] ( sig [ 3 ] )
end
end
end
end
2018-09-28 08:01:05 +10:00
helpActive , buttonsActive = false , false
2018-03-19 10:10:54 +11:00
return safeModeActive
end
local function postPrompt ( )
2018-03-23 12:12:08 +11:00
local nsm = neo.requestAccess ( " x.neo.sys.manage " )
local sh = " sys-everest "
2018-09-28 08:01:05 +10:00
console = { " Unable to get shell (no sys-glacier) " }
2018-03-23 12:12:08 +11:00
if nsm then
sh = nsm.getSetting ( " sys-init.shell " ) or sh
2018-09-28 08:01:05 +10:00
console = { " Starting " .. sh }
2018-03-23 12:12:08 +11:00
end
2018-09-28 08:01:05 +10:00
basicDraw ( )
2018-03-23 12:12:08 +11:00
performDisclaim ( )
neo.executeAsync ( sh )
2018-09-28 08:01:05 +10:00
-- There's a delay here to allow taking the monitor.
2018-03-23 12:12:08 +11:00
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
2018-03-19 10:10:54 +11:00
return
end
end
2018-03-23 12:12:08 +11:00
-- ...oh. hope this works then?
2018-09-28 08:01:05 +10:00
console = { " x.neo.sys.session not found, try Safe Mode. " }
retrieveNssMonitor ( screen )
basicDraw ( )
2018-03-19 10:10:54 +11:00
sleep ( 1 )
shutdown ( true )
end
local function initializeSystem ( )
-- System has just booted, bristol is in charge
2018-09-28 08:01:05 +10:00
-- No screen configuration, so just guess.
2018-03-22 13:33:52 +11:00
-- 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 " )
2018-03-23 10:37:19 +11:00
local gpu
2018-03-22 13:33:52 +11:00
-- 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
2018-03-23 10:37:19 +11:00
g.bind ( s.address , false )
2018-03-23 12:12:08 +11:00
local whd = g.maxDepth ( )
2018-03-22 13:33:52 +11:00
if whd > scrBestWHD then
screen = s
gpu = g
scrBestWHD = whd
end
end
end
2018-03-19 10:10:54 +11:00
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 )
2018-04-12 09:04:16 +10:00
pcall ( gpu.setResolution , gW , gH )
2018-03-29 08:15:09 +11:00
pcall ( gpu.setDepth , gpu.maxDepth ( ) ) -- can crash on OCEmu if done at the "wrong time"
2018-03-19 10:10:54 +11:00
end
2018-09-28 08:01:05 +10:00
-- Setup the new GPU provider
gpuG = function ( ) return gpu end
--
2018-03-19 10:10:54 +11:00
local w = 1
2018-09-28 08:01:05 +10:00
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
2018-03-19 10:10:54 +11:00
local stepCount = # steps
neo.scheduleTimer ( os.uptime ( ) )
while true do
local ev = { coroutine.yield ( ) }
2018-09-28 08:01:05 +10:00
consoleEventHandler ( ev )
2018-03-19 10:10:54 +11:00
if ev [ 1 ] == " k.timer " then
if gpu then
2018-09-28 08:01:05 +10:00
local bg = 0xFFFFFF
2018-03-19 10:10:54 +11:00
if w < stepCount then
local n = math.floor ( ( w / stepCount ) * 255 )
2018-09-28 08:01:05 +10:00
bg = ( n * 0x10000 ) + ( n * 0x100 ) + n
2018-03-19 10:10:54 +11:00
end
2018-09-28 08:01:05 +10:00
basicDraw ( bg )
2018-03-19 10:10:54 +11:00
end
if steps [ w ] then
if steps [ w ] == " INJECT " then
2018-03-19 12:27:30 +11:00
local nsm = neo.requestAccess ( " x.neo.sys.manage " )
if not nsm then
2018-09-28 08:01:05 +10:00
table.insert ( console , " Settings not available for INJECT. " )
2018-03-19 10:10:54 +11:00
else
local nextstepsA = { }
local nextstepsB = { }
for _ , v in ipairs ( neo.listApps ( ) ) do
2018-03-19 12:27:30 +11:00
if nsm.getSetting ( " run. " .. v ) == " yes " then
2018-03-19 10:10:54 +11:00
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
2018-09-28 08:01:05 +10:00
neo.emergency ( " failed start: " , steps [ w ] )
2018-04-07 06:05:47 +10:00
neo.emergency ( err )
2018-03-19 10:10:54 +11:00
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
2018-03-19 12:27:30 +11:00
local nsm = neo.requestAccess ( " x.neo.sys.manage " )
if nsm then
2018-09-28 08:01:05 +10:00
console = { " Rebooting for Safe Mode... " }
basicDraw ( )
2018-03-19 12:27:30 +11:00
for _ , v in ipairs ( nsm.listSettings ( ) ) do
2018-03-19 10:10:54 +11:00
if v ~= " password " then
2018-03-19 12:27:30 +11:00
nsm.delSetting ( v )
2018-03-19 10:10:54 +11:00
end
end
else
-- assume sysconf.lua did something very bad
2018-09-28 08:01:05 +10:00
console = { " No NSM. Wiping configuration completely. " }
2018-03-19 10:10:54 +11:00
local fs = neo.requestAccess ( " c.filesystem " )
2018-03-23 10:37:19 +11:00
if not fs then
2018-09-28 08:01:05 +10:00
table.insert ( console , " Failed to get permission, you're doomed. " )
else
fs.primary . remove ( " /data/sys-glacier/sysconf.lua " )
2018-03-23 10:37:19 +11:00
end
2018-09-28 08:01:05 +10:00
basicDraw ( )
2018-03-19 10:10:54 +11:00
end
-- Do not give anything a chance to alter the new configuration
shutdownEmergency ( true )
return
end
postPrompt ( )