1
0
mirror of https://github.com/20kdc/OC-KittenOS.git synced 2024-11-16 23:48:05 +11:00
OC-KittenOS/code/apps/sys-glacier.lua
20kdc de822181bc Hopefully work out a corrected solution to the legal fun, and fix control, textedit, taskmgr, neoux, everest and glacier
taskmgr and textedit had issues with the DEL key
neoux needed clipboard support that worked
control... I forget
glacier missed a pcall
everest's launcher change support wasn't working
compliance.lua was accused of being itself
2018-04-23 20:20:58 +01:00

441 lines
12 KiB
Lua

-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- s-donkonit : config, shutdown, screens
local donkonitSPProvider = neo.requireAccess("r.neo.sys.manage", "creating NEO core APIs") -- Restrict to s-
-- Doesn't matter what calls this service, because there's a mutex here.
local donkonitRDProvider = neo.requireAccess("r.neo.sys.screens", "creating NEO core APIs")
local glacierDCProvider = neo.requireAccess("r.neo.pub.globals", "creating NEO core APIs")
local computer = neo.requireAccess("k.computer", "shutting down")
local fs = neo.requireAccess("c.filesystem", "settings I/O")
local gpus = neo.requireAccess("c.gpu", "screen control")
local screens = neo.requireAccess("c.screen", "screen control")
neo.requireAccess("s.h.component_added", "HW management")
neo.requireAccess("s.h.component_removed", "HW management")
local function shutdownFin(reboot)
-- any final actions donkonit needs to take here
computer.shutdown(reboot)
end
-- keys are pids
local targs = {} -- settings notify targs
local targsDC = {} -- displaycontrol settings notify targs
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 = "",
["pub.clipboard"] = "",
["sys-init.shell"] = "sys-everest",
["sys-everest.launcher"] = "app-launcher",
["run.sys-icecap"] = "yes",
-- scr.w/h/d/t.<uuid>
}
local function loadSettings()
pcall(function ()
local fw = require("sys-filewrap").create
local se = require("serial").deserialize
local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", false)
local cfg = st.read("*a")
st.close()
st = nil
fw = nil
cfg = se(cfg)
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
local function saveSettings()
local fw = require("sys-filewrap").create
local se = require("serial").serialize
fs.primary.makeDirectory("data/sys-glacier")
local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", true)
st.write(se(settings))
st.close()
end
-- [i] = screenProxy
local monitorPool = {}
-- [screenAddr] = {gpu, claimedLoseCallback}
local monitorClaims = {}
-- [gpuAddr] = monitorAddr
local currentGPUBinding = {}
-- [gpuAddr] = userCount
local currentGPUUsers = {}
-- Thanks to Skye for this!
local keyboardMonCacheK, keyboardMonCacheV = nil
local function announceFreeMonitor(address, except)
for k, v in pairs(targsRD) do
if k ~= except then
v[1]("available", address)
end
end
end
local function getGPU(monitor)
local bestG, bestStats = nil, {-math.huge, -math.huge, -math.huge}
currentGPUBinding = {}
for v in gpus.list() do
v.bind(monitor.address, false)
local w, h = v.maxResolution()
local quality = w * h * v.maxDepth()
local users = (currentGPUUsers[v.address] or 0)
local gquality = 0
for scr in screens.list() do
v.bind(scr.address, false)
w, h = v.maxResolution()
local squality = w * h * v.maxDepth()
gquality = math.max(gquality, squality)
end
local stats = {quality, -users, -gquality}
for i = 1, #stats do
if stats[i] > bestStats[i] then
bestG = v
bestStats = stats
break
elseif stats[i] < bestStats[i] then
break
end
end
end
if bestG then
neo.emergency("glacier bound " .. monitor.address .. " to " .. bestG.address)
else
neo.emergency("glacier failed to bind " .. monitor.address)
end
return bestG
end
local function sRattle(name, val)
for _, v in pairs(targs) do
v("set_setting", name, val)
end
if name:sub(1, 4) == "scr." or name:sub(1, 4) == "pub." then
for k, v in pairs(targsDC) do
v("set_setting", name, val)
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
local t = ((settings["scr.t." .. a] == "yes") and "yes") or "no"
w, h, d = math.floor(w), math.floor(h), math.floor(d)
return w, h, d, t
end
local function setupMonitor(gpu, monitor)
monitor.setPrecise(true)
monitor.turnOn()
gpu.bind(monitor.address, false)
currentGPUBinding[gpu.address] = monitor.address
local maxW, maxH = gpu.maxResolution()
local maxD = gpu.maxDepth()
local w, h, d, t = getMonitorSettings(monitor.address)
w, h, d = math.min(w, maxW), math.min(h, maxH), math.min(d, maxD)
if monitor.setTouchModeInverted then
monitor.setTouchModeInverted(t == "yes")
else
t = "no"
end
settings["scr.w." .. monitor.address] = tostring(w)
settings["scr.h." .. monitor.address] = tostring(h)
settings["scr.d." .. monitor.address] = tostring(d)
settings["scr.t." .. monitor.address] = t
sRattle("scr.w." .. monitor.address, tostring(w))
sRattle("scr.h." .. monitor.address, tostring(h))
sRattle("scr.d." .. monitor.address, tostring(d))
sRattle("scr.t." .. monitor.address, t)
gpu.setResolution(w, h)
gpu.setDepth(d)
pcall(saveSettings)
end
donkonitSPProvider(function (pkg, pid, sendSig)
targs[pid] = sendSig
return {
listSettings = function ()
local s = {}
for k, v in pairs(settings) do
table.insert(s, k)
end
return s
end,
-- NOTE: REPLICATED IN GB
getSetting = function (name)
neo.ensureType(name, "string")
return settings[name]
end,
delSetting = function (name)
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,
--
registerForShutdownEvent = function ()
targsSD[pid] = sendSig
end,
registerSavingThrow = function (st)
neo.ensureType(st, "function")
targsST[pid] = st
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(reboot)
end
end)
end
if counter == 0 then
shutdownFin(reboot)
end
-- donkonit will shutdown when the timer is hit.
end
}
end)
donkonitRDProvider(function (pkg, pid, sendSig)
local claimed = {}
targsRD[pid] = {sendSig, function ()
for k, v in pairs(claimed) do
-- Nothing to really do here
v(false)
end
end}
return {
getMonitorByKeyboard = function (kb)
if keyboardMonCacheK == kb then
return keyboardMonCacheV
end
for v in screens.list() do
for _, v2 in ipairs(v.getKeyboards()) do
if v2 == kb then
keyboardMonCacheK, keyboardMonCacheV = kb, v.address
return v.address
end
end
end
end,
getClaimable = function ()
local c = {}
-- do we have gpu?
if not gpus.list()() then return c end
for _, v in ipairs(monitorPool) do
table.insert(c, v.address)
end
return c
end,
claim = function (address)
neo.ensureType(address, "string")
for k, v in ipairs(monitorPool) do
if v.address == address then
local gpu = getGPU(v)
if gpu then
setupMonitor(gpu, v)
gpu = gpu.address
currentGPUBinding[gpu] = address
currentGPUUsers[gpu] = (currentGPUUsers[gpu] or 0) + 1
local disclaimer = function (wasDevLoss)
-- we lost it
monitorClaims[address] = nil
claimed[address] = nil
if not wasDevLoss then
currentGPUUsers[gpu] = currentGPUUsers[gpu] - 1
table.insert(monitorPool, v)
announceFreeMonitor(address, pid)
else
sendSig("lost", address)
end
end
claimed[address] = disclaimer
monitorClaims[address] = {gpu, disclaimer}
table.remove(monitorPool, k)
return function ()
for v in gpus.list() do
if v.address == gpu then
local didBind = false
if currentGPUBinding[gpu] ~= address then
v.bind(address, false)
didBind = true
end
currentGPUBinding[gpu] = address
return v, didBind
end
end
end, v
end
end
end
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 = {}
currentGPUBinding = {}
currentGPUUsers = {}
keyboardMonCacheK, keyboardMonCacheV = nil, nil
local hasGPU = gpus.list()()
for k, v in pairs(monitorClaims) do
v[2](true)
end
monitorClaims = {}
for m in screens.list() do
table.insert(monitorPool, m)
if hasGPU then
announceFreeMonitor(m.address)
end
end
end
rescanDevs()
-- Save any settings made during the above (or just the language)
pcall(saveSettings)
-- --
glacierDCProvider(function (pkg, pid, sendSig)
targsDC[pid] = sendSig
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
table.insert(tbl, {k, true, getMonitorSettings(k)})
end
return tbl
end,
changeMonitorSetup = function (ma, w, h, d, t)
neo.ensureType(ma, "string")
neo.ensureType(w, "number")
neo.ensureType(h, "number")
neo.ensureType(d, "number")
neo.ensureType(t, "string")
w = math.floor(w)
h = math.floor(h)
d = math.floor(d)
if t ~= "yes" then t = "no" end
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
settings["scr.t." .. ma] = t
sRattle("scr.w." .. ma, w)
sRattle("scr.h." .. ma, h)
sRattle("scr.d." .. ma, d)
sRattle("scr.t." .. ma, t)
pcall(saveSettings)
end,
forceRescan = rescanDevs,
-- NOTE: "pub." prefixed version of functions in sys.manage
getSetting = function (name)
neo.ensureType(name, "string")
return settings["pub." .. name]
end,
delSetting = function (name)
neo.ensureType(name, "string")
local val = nil
if name == "clipboard" then val = "" end
settings["pub." .. name] = val
sRattle("pub." .. name, val)
pcall(saveSettings)
end,
setSetting = function (name, val)
neo.ensureType(name, "string")
neo.ensureType(val, "string")
settings["pub." .. name] = val
sRattle("pub." .. name, val)
pcall(saveSettings)
end
}
end)
-- main loop
while true do
local s = {coroutine.yield()}
if s[1] == "k.timer" then
-- always shutdown
shutdownFin(shutdownMode)
end
if s[1] == "h.component_added" or s[1] == "h.component_removed" then
-- Anything important?
if s[3] == "gpu" or s[3] == "screen" then
rescanDevs()
end
end
if s[1] == "k.procdie" then
targs[s[3]] = nil
targsDC[s[3]] = nil
targsSD[s[3]] = nil
if targsST[s[3]] then
if s[4] then
coroutine.resume(coroutine.create(targsST[s[3]]))
end
end
targsST[s[3]] = nil
if targsRD[s[3]] then
targsRD[s[3]][2]()
end
end
end