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-donkonit : config, shutdown, screens
2018-03-19 14:08:09 +11:00
local donkonitSPProvider = neo.requireAccess ( " r.neo.sys.manage " , " creating NEO core APIs " ) -- Restrict to s-
2018-03-22 13:33:52 +11:00
-- Doesn't matter what calls this service, because there's a mutex here.
2018-03-19 14:08:09 +11:00
local donkonitRDProvider = neo.requireAccess ( " r.neo.sys.screens " , " creating NEO core APIs " )
2018-03-24 02:35:43 +11:00
local glacierDCProvider = neo.requireAccess ( " r.neo.pub.globals " , " creating NEO core APIs " )
2018-03-19 14:08:09 +11:00
2018-04-27 01:13:42 +10:00
local shutdownFin = neo.requireAccess ( " k.computer " , " shutting down " ) . shutdown
local primary = neo.requireAccess ( " c.filesystem " , " settings I/O " ) . primary
local gpus = neo.requireAccess ( " c.gpu " , " screen control " ) . list
local screens = neo.requireAccess ( " c.screen " , " screen control " ) . list
2018-03-19 14:08:09 +11:00
neo.requireAccess ( " s.h.component_added " , " HW management " )
neo.requireAccess ( " s.h.component_removed " , " HW management " )
2018-03-19 10:10:54 +11:00
-- keys are pids
local targs = { } -- settings notify targs
2018-03-22 13:33:52 +11:00
local targsDC = { } -- displaycontrol settings notify targs
2018-03-19 10:10:54 +11:00
local targsSD = { } -- shutdown notify targs
local targsST = { } -- saving throws
local targsRD = { } -- pid->{sendSig,dead}
-- screen address -> {gpu, monitor}
local monitorMap = { }
local shuttingDown = false
local shutdownMode = false
-- needs improvements
local settings = {
-- The list of settings is here:
-- password
password = " " ,
2018-03-24 02:35:43 +11:00
[ " pub.clipboard " ] = " " ,
2018-03-23 12:12:08 +11:00
[ " sys-init.shell " ] = " sys-everest " ,
2018-04-24 05:16:30 +10:00
[ " sys-everest.launcher " ] = " app-launcher " ,
2018-03-19 10:10:54 +11:00
[ " run.sys-icecap " ] = " yes " ,
2018-03-29 08:15:09 +11:00
-- scr.w/h/d/t.<uuid>
2018-03-19 10:10:54 +11:00
}
local function loadSettings ( )
pcall ( function ( )
2018-04-12 09:04:16 +10:00
local fw = require ( " sys-filewrap " ) . create
local se = require ( " serial " ) . deserialize
2018-04-27 01:13:42 +10:00
local st = fw ( primary , " data/sys-glacier/sysconf.lua " , false )
2018-03-19 10:10:54 +11:00
local cfg = st.read ( " *a " )
st.close ( )
st = nil
fw = nil
2018-04-12 09:04:16 +10:00
cfg = se ( cfg )
2018-03-19 10:10:54 +11:00
for k , v in pairs ( cfg ) do
if type ( k ) == " string " then
if type ( v ) == " string " then
settings [ k ] = v
end
end
end
end )
end
2018-04-27 01:13:42 +10:00
2018-03-19 10:10:54 +11:00
local function saveSettings ( )
2018-04-12 09:04:16 +10:00
local fw = require ( " sys-filewrap " ) . create
local se = require ( " serial " ) . serialize
2018-04-27 01:13:42 +10:00
primary.makeDirectory ( " data/sys-glacier " )
local st = fw ( primary , " data/sys-glacier/sysconf.lua " , true )
2018-04-12 09:04:16 +10:00
st.write ( se ( settings ) )
2018-03-19 10:10:54 +11:00
st.close ( )
end
2018-03-22 13:33:52 +11:00
-- [i] = screenProxy
2018-03-19 10:10:54 +11:00
local monitorPool = { }
2018-03-22 13:33:52 +11:00
-- [screenAddr] = {gpu, claimedLoseCallback}
2018-03-19 10:10:54 +11:00
local monitorClaims = { }
2018-03-23 10:37:19 +11:00
-- [gpuAddr] = monitorAddr
local currentGPUBinding = { }
2018-03-23 12:12:08 +11:00
-- [gpuAddr] = userCount
local currentGPUUsers = { }
2018-03-19 10:10:54 +11:00
2018-04-27 01:13:42 +10:00
-- Thanks to Skye for this design!
2018-03-30 23:50:15 +11:00
local keyboardMonCacheK , keyboardMonCacheV = nil
2018-03-19 10:10:54 +11:00
local function announceFreeMonitor ( address , except )
for k , v in pairs ( targsRD ) do
if k ~= except then
v [ 1 ] ( " available " , address )
end
end
end
2018-03-22 13:33:52 +11:00
local function sRattle ( name , val )
for _ , v in pairs ( targs ) do
v ( " set_setting " , name , val )
end
2018-03-24 02:35:43 +11:00
if name : sub ( 1 , 4 ) == " scr. " or name : sub ( 1 , 4 ) == " pub. " then
2018-03-22 13:33:52 +11:00
for k , v in pairs ( targsDC ) do
2018-04-12 09:38:49 +10:00
v ( " set_setting " , name , val )
2018-03-22 13:33:52 +11:00
end
end
end
-- Settings integration w/ monitors
local function getMonitorSettings ( a )
local w = tonumber ( settings [ " scr.w. " .. a ] ) or 80
local h = tonumber ( settings [ " scr.h. " .. a ] ) or 25
local d = tonumber ( settings [ " scr.d. " .. a ] ) or 8
2018-03-29 08:15:09 +11:00
local t = ( ( settings [ " scr.t. " .. a ] == " yes " ) and " yes " ) or " no "
2018-03-22 13:33:52 +11:00
w , h , d = math.floor ( w ) , math.floor ( h ) , math.floor ( d )
2018-03-29 08:15:09 +11:00
return w , h , d , t
2018-03-22 13:33:52 +11:00
end
2018-03-19 10:10:54 +11:00
2018-04-27 02:38:33 +10:00
-- Settings API
local mBase = {
getSetting = function ( name )
neo.ensureType ( name , " string " )
return settings [ name ]
end ,
listSettings = function ( )
local s = { }
for k , v in pairs ( settings ) do
table.insert ( s , k )
end
return s
end ,
2018-07-10 03:17:49 +10:00
delSetting = function ( name )
2018-04-27 02:38:33 +10:00
neo.ensureType ( name , " string " )
local val = nil
if name == " password " or name == " pub.clipboard " then val = " " end
settings [ name ] = val
sRattle ( name , val )
pcall ( saveSettings )
end ,
setSetting = function ( name , val )
neo.ensureType ( name , " string " )
neo.ensureType ( val , " string " )
settings [ name ] = val
-- NOTE: Either a monitor is under application control,
-- or it's not under any control.
-- Monitor settings are applied on the transition to control.
sRattle ( name , val )
pcall ( saveSettings )
end ,
shutdown = function ( reboot )
neo.ensureType ( reboot , " boolean " )
if shuttingDown then return end
shuttingDown = true
shutdownMode = reboot
local counter = 0
neo.scheduleTimer ( os.uptime ( ) + 5 ) -- in case the upcoming code fails in some way
for f , v in pairs ( targsSD ) do
counter = counter + 1
v ( " shutdown " , reboot , function ( )
counter = counter - 1
if counter == 0 then
shutdownFin ( shutdownMode )
end
end )
end
if counter == 0 then
shutdownFin ( shutdownMode )
end
-- donkonit will shutdown when the timer is hit.
end
}
2018-03-19 10:10:54 +11:00
donkonitSPProvider ( function ( pkg , pid , sendSig )
targs [ pid ] = sendSig
2018-04-27 02:38:33 +10:00
local n = {
2018-03-19 10:10:54 +11:00
registerForShutdownEvent = function ( )
targsSD [ pid ] = sendSig
end ,
registerSavingThrow = function ( st )
2018-04-09 09:04:40 +10:00
neo.ensureType ( st , " function " )
2018-03-19 10:10:54 +11:00
targsST [ pid ] = st
end
}
2018-04-27 02:38:33 +10:00
return setmetatable ( n , {
__index = mBase ,
__metatable = 0
} )
2018-03-19 10:10:54 +11:00
end )
donkonitRDProvider ( function ( pkg , pid , sendSig )
local claimed = { }
targsRD [ pid ] = { sendSig , function ( )
for k , v in pairs ( claimed ) do
-- Nothing to really do here
2018-03-23 12:12:08 +11:00
v ( false )
2018-03-19 10:10:54 +11:00
end
end }
return {
2018-03-29 08:15:09 +11:00
getMonitorByKeyboard = function ( kb )
2018-04-12 09:38:49 +10:00
if keyboardMonCacheK == kb then
2018-03-30 23:50:15 +11:00
return keyboardMonCacheV
end
2018-04-27 01:13:42 +10:00
for v in screens ( ) do
2018-03-29 08:15:09 +11:00
for _ , v2 in ipairs ( v.getKeyboards ( ) ) do
if v2 == kb then
2018-04-12 09:38:49 +10:00
keyboardMonCacheK , keyboardMonCacheV = kb , v.address
2018-03-29 08:15:09 +11:00
return v.address
end
end
end
end ,
2018-03-19 10:10:54 +11:00
getClaimable = function ( )
local c = { }
-- do we have gpu?
2018-04-27 01:13:42 +10:00
if not gpus ( ) ( ) then return c end
2018-03-19 10:10:54 +11:00
for _ , v in ipairs ( monitorPool ) do
table.insert ( c , v.address )
end
return c
end ,
2018-04-27 01:13:42 +10:00
claim = function ( ... ) -- see sys-gpualloc
return require ( " sys-gpualloc " ) (
gpus , screens ,
getMonitorSettings , settings , sRattle , saveSettings ,
announceFreeMonitor , pid , claimed , sendSig ,
monitorClaims , monitorPool , currentGPUUsers , currentGPUBinding ,
... )
2018-03-19 10:10:54 +11:00
end ,
disclaim = function ( address )
if not address then error ( " Cannot disclaim nothing. " ) end
if claimed [ address ] then
claimed [ address ] ( false )
end
end
}
end )
-- -- The actual initialization
loadSettings ( )
local function rescanDevs ( )
monitorPool = { }
2018-03-23 10:37:19 +11:00
currentGPUBinding = { }
2018-03-23 12:12:08 +11:00
currentGPUUsers = { }
2018-03-30 23:50:15 +11:00
keyboardMonCacheK , keyboardMonCacheV = nil , nil
2018-03-22 13:33:52 +11:00
for k , v in pairs ( monitorClaims ) do
v [ 2 ] ( true )
end
monitorClaims = { }
2018-04-27 01:13:42 +10:00
for m in screens ( ) do
2018-03-19 10:10:54 +11:00
table.insert ( monitorPool , m )
2018-04-27 01:13:42 +10:00
if gpus ( ) ( ) then
2018-03-19 10:10:54 +11:00
announceFreeMonitor ( m.address )
end
end
end
rescanDevs ( )
-- Save any settings made during the above (or just the language)
2018-04-24 05:16:30 +10:00
pcall ( saveSettings )
2018-03-19 10:10:54 +11:00
-- --
2018-03-22 13:33:52 +11:00
glacierDCProvider ( function ( pkg , pid , sendSig )
targsDC [ pid ] = sendSig
2018-04-27 02:38:33 +10:00
local function sWrap ( f )
return function ( s , ... )
return f ( " pub. " .. s , ... )
end
end
2018-03-22 13:33:52 +11:00
return {
getKnownMonitors = function ( )
local tbl = { }
-- yes, this should work fine so long as GMS is the *last* one #luaquirks
for k , v in ipairs ( monitorPool ) do
tbl [ k ] = { v.address , false , getMonitorSettings ( v.address ) }
end
for k , v in pairs ( monitorClaims ) do
2018-03-30 03:31:51 +11:00
table.insert ( tbl , { k , true , getMonitorSettings ( k ) } )
2018-03-22 13:33:52 +11:00
end
2018-03-30 03:31:51 +11:00
return tbl
2018-03-22 13:33:52 +11:00
end ,
2018-03-30 03:31:51 +11:00
changeMonitorSetup = function ( ma , w , h , d , t )
2018-03-22 13:33:52 +11:00
neo.ensureType ( ma , " string " )
neo.ensureType ( w , " number " )
neo.ensureType ( h , " number " )
neo.ensureType ( d , " number " )
2018-03-30 03:31:51 +11:00
neo.ensureType ( t , " string " )
2018-03-22 13:33:52 +11:00
w = math.floor ( w )
h = math.floor ( h )
d = math.floor ( d )
2018-03-30 03:31:51 +11:00
if t ~= " yes " then t = " no " end
2018-03-22 13:33:52 +11:00
if w < 1 then error ( " Invalid width " ) end
if h < 1 then error ( " Invalid height " ) end
if d < 1 then error ( " Invalid depth " ) end
w , h , d = tostring ( w ) , tostring ( h ) , tostring ( d )
settings [ " scr.w. " .. ma ] = w
settings [ " scr.h. " .. ma ] = h
settings [ " scr.d. " .. ma ] = d
2018-03-30 03:31:51 +11:00
settings [ " scr.t. " .. ma ] = t
2018-03-22 13:33:52 +11:00
sRattle ( " scr.w. " .. ma , w )
sRattle ( " scr.h. " .. ma , h )
sRattle ( " scr.d. " .. ma , d )
2018-03-30 03:31:51 +11:00
sRattle ( " scr.t. " .. ma , t )
2018-03-22 13:33:52 +11:00
pcall ( saveSettings )
end ,
2018-03-24 02:35:43 +11:00
forceRescan = rescanDevs ,
-- NOTE: "pub." prefixed version of functions in sys.manage
2018-04-27 02:38:33 +10:00
getSetting = sWrap ( mBase.getSetting ) ,
delSetting = sWrap ( mBase.delSetting ) ,
setSetting = sWrap ( mBase.setSetting )
2018-03-22 13:33:52 +11:00
}
end )
-- main loop
2018-03-19 10:10:54 +11:00
while true do
local s = { coroutine.yield ( ) }
if s [ 1 ] == " k.timer " then
-- always shutdown
shutdownFin ( shutdownMode )
end
2018-04-07 06:05:47 +10:00
if s [ 1 ] == " h.component_added " or s [ 1 ] == " h.component_removed " then
-- Anything important?
2018-03-22 13:33:52 +11:00
if s [ 3 ] == " gpu " or s [ 3 ] == " screen " then
rescanDevs ( )
end
2018-03-19 10:10:54 +11:00
end
if s [ 1 ] == " k.procdie " then
targs [ s [ 3 ] ] = nil
2018-03-22 13:33:52 +11:00
targsDC [ s [ 3 ] ] = nil
2018-03-19 10:10:54 +11:00
targsSD [ s [ 3 ] ] = nil
if targsST [ s [ 3 ] ] then
if s [ 4 ] then
2018-03-30 07:06:53 +11:00
coroutine.resume ( coroutine.create ( targsST [ s [ 3 ] ] ) )
2018-03-19 10:10:54 +11:00
end
end
targsST [ s [ 3 ] ] = nil
if targsRD [ s [ 3 ] ] then
targsRD [ s [ 3 ] ] [ 2 ] ( )
end
end
end