mirror of
https://github.com/20kdc/OC-KittenOS.git
synced 2024-11-23 10:58:06 +11:00
All the current progress, including KTC1 draft in case there's no other standard
KTC1 is only a draft for now, and if there's something equivalent to replace it, I will not hesitate to do so.
This commit is contained in:
parent
6c0659de60
commit
a5372eafe1
52
KTC1.md
Normal file
52
KTC1.md
Normal file
@ -0,0 +1,52 @@
|
||||
# KTC1 Specification
|
||||
|
||||
KittenOS Texture Compression 1 is an image compression format that, while not
|
||||
size-optimized or performance-optimized for OC screens, is optimized for
|
||||
streaming from disk, and always produces precise results.
|
||||
|
||||
KTC1's concepts are "inspired by" ETC1, and it was conceived after evaluating
|
||||
ETC1 for use in KittenOS NEO. ETC1, however, is not optimally fit for
|
||||
OpenComputers rendering, and thus KTC1 was created to provide an equal-size
|
||||
solution that better fit these requirements.
|
||||
|
||||
A 256-colour palette is assumed, and it is assumed that the palette is provided
|
||||
outside of KTC1's context.
|
||||
|
||||
A KTC1 block is one OpenComputers character (2x4 pixels), and is 4 bytes long.
|
||||
|
||||
The format amounts to a foreground palette index, a background palette index,
|
||||
and a Unicode character index in the Basic Multilingual Plane.
|
||||
|
||||
The unicode character is displayed with the given colours at the position of the
|
||||
block.
|
||||
|
||||
The renderer does not get more complicated when more blocks are involved.
|
||||
|
||||
Simply put, blocks that are overlapped by a previous wide character are to be
|
||||
totally ignored.
|
||||
|
||||
The size of this format is equivalent to 4-bit indexed data and to ETC1.
|
||||
|
||||
For standardization's sake, the container format for KTC1 has an 8-byte header:
|
||||
"OC" followed by two 16-bit big-endian unsigned integers, for width
|
||||
and height in blocks, the bytes-per-block count for this format (4) as
|
||||
an unsigned byte, and the amount of "comment" bytes that go after the image,
|
||||
as another unsigned byte.
|
||||
|
||||
Example image, showing a 4x4 white "A" on black, with a standard text
|
||||
black "A" on white underneath:
|
||||
|
||||
4F 43 00 02 00 02 04 00
|
||||
FF 00 28 6E FF 00 28 B5
|
||||
00 FF 00 41 00 FF 00 00
|
||||
|
||||
## Additional Notes
|
||||
|
||||
A KTC1 file is theoretically a "lossless" screenshot under the limits of the
|
||||
OpenComputers system assuming the palette is correct.
|
||||
|
||||
The Basic Multilingual Plane access allows mixing images and text inside a
|
||||
KTC1 file, and covers all characters that OpenComputers supports.
|
||||
|
||||
This makes KTC1 an interesting option for use as a mixed text/image interchange
|
||||
format between applications.
|
@ -120,11 +120,11 @@ local function monitorGPUColours(m, cb, bg, fg)
|
||||
local nbg = m[5]
|
||||
local nfg = m[6]
|
||||
if nbg ~= bg then
|
||||
cb.setBackground(bg)
|
||||
pcall(cb.setBackground, bg)
|
||||
m[5] = bg
|
||||
end
|
||||
if nfg ~= fg then
|
||||
cb.setForeground(fg)
|
||||
pcall(cb.setForeground, fg)
|
||||
m[6] = fg
|
||||
end
|
||||
end
|
||||
@ -138,11 +138,11 @@ local function doBackgroundLine(m, mg, bdx, bdy, bdl)
|
||||
monitorGPUColours(m, mg, 0x000000, 0xFFFFFF)
|
||||
local str = unicode.sub(statusLine, bdx, bdx + bdl - 1)
|
||||
local strl = unicode.len(str)
|
||||
mg.set(bdx, bdy, unicode.undoSafeTextFormat(str))
|
||||
mg.fill(bdx + strl, bdy, bdl - strl, 1, " ")
|
||||
pcall(mg.set, bdx, bdy, unicode.undoSafeTextFormat(str))
|
||||
pcall(mg.fill, bdx + strl, bdy, bdl - strl, 1, " ")
|
||||
else
|
||||
monitorGPUColours(m, mg, 0x000020, 0)
|
||||
mg.fill(bdx, bdy, bdl, 1, " ")
|
||||
pcall(mg.fill, bdx, bdy, bdl, 1, " ")
|
||||
end
|
||||
end
|
||||
|
||||
@ -265,7 +265,7 @@ local function moveSurface(surface, m, x, y, w, h, force)
|
||||
if renderingAllowed() and not force then
|
||||
local cb, b = monitors[m][1]()
|
||||
if b then
|
||||
monitorResetBF(b)
|
||||
monitorResetBF(monitors[m])
|
||||
end
|
||||
if cb then
|
||||
cb.copy(ox, oy, w, h, x - ox, y - oy)
|
||||
@ -326,7 +326,7 @@ local function handleSpan(target, x, y, text, bg, fg)
|
||||
base = unicode.sub(text, buildingSegment, buildingSegmentE)
|
||||
-- rely on undoSafeTextFormat for this transform now
|
||||
monitorGPUColours(m, cb, bg, fg)
|
||||
cb.set(buildingSegmentWX, buildingSegmentWY, unicode.undoSafeTextFormat(base))
|
||||
pcall(cb.set, buildingSegmentWX, buildingSegmentWY, unicode.undoSafeTextFormat(base))
|
||||
buildingSegment = nil
|
||||
end
|
||||
end
|
||||
@ -520,15 +520,37 @@ everestProvider(function (pkg, pid, sendSig)
|
||||
end
|
||||
end)
|
||||
-- THE EVEREST USER API ENDS (now for the session API, which just does boring stuff)
|
||||
-- used later on for lost monitor, too
|
||||
local function disclaimMonitor(mon)
|
||||
neo.ensureType(mon, "string")
|
||||
screens.disclaim(mon)
|
||||
for k, v in ipairs(monitors) do
|
||||
if v[2] == mon then
|
||||
table.remove(monitors, k)
|
||||
reconcileAll()
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
everestSessionProvider(function (pkg, pid, sendSig)
|
||||
return {
|
||||
endSession = function (gotoBristol)
|
||||
neo.ensureType(gotoBristol, "boolean")
|
||||
shuttingDown = true
|
||||
if gotoBristol then
|
||||
suggestAppsStop()
|
||||
dying()
|
||||
end
|
||||
end,
|
||||
getMonitors = function ()
|
||||
local details = {}
|
||||
for k, v in ipairs(monitors) do
|
||||
details[k] = v[2]
|
||||
end
|
||||
return details
|
||||
end,
|
||||
disclaimMonitor = disclaimMonitor
|
||||
}
|
||||
end)
|
||||
-- THE EVEREST SESSION API ENDS
|
||||
@ -539,7 +561,14 @@ end)
|
||||
-- Alt-Up/Down/Left/Right: Move surface
|
||||
local isAltDown = false
|
||||
local isCtrDown = false
|
||||
local function key(ka, kc, down)
|
||||
local function key(ku, ka, kc, down)
|
||||
local ku = screens.getMonitorByKeyboard(ku)
|
||||
if not ku then return end
|
||||
for k, v in ipairs(monitors) do
|
||||
if v[2] == mu then
|
||||
lIM = k
|
||||
end
|
||||
end
|
||||
local focus = surfaces[1]
|
||||
if kc == 29 then isCtrDown = down end
|
||||
if kc == 56 then isAltDown = down end
|
||||
@ -617,16 +646,10 @@ while not shuttingDown do
|
||||
local s = {coroutine.yield()}
|
||||
if renderingAllowed() then
|
||||
if s[1] == "h.key_down" then
|
||||
local m = screens.getMonitorByKeyboard(s[2])
|
||||
for k, v in ipairs(monitors) do
|
||||
if v[2] == m then
|
||||
lIM = k
|
||||
end
|
||||
end
|
||||
key(s[3], s[4], true)
|
||||
key(s[2], s[3], s[4], true)
|
||||
end
|
||||
if s[1] == "h.key_up" then
|
||||
key(s[3], s[4], false)
|
||||
key(s[2], s[3], s[4], false)
|
||||
end
|
||||
if s[1] == "h.clipboard" then
|
||||
if surfaces[1] then
|
||||
@ -697,13 +720,7 @@ while not shuttingDown do
|
||||
performClaim(s[3])
|
||||
end
|
||||
if s[2] == "lost" then
|
||||
for k, v in ipairs(monitors) do
|
||||
if v[2] == s[3] then
|
||||
table.remove(monitors, k)
|
||||
reconcileAll()
|
||||
break
|
||||
end
|
||||
end
|
||||
handleLostMonitor(s[3])
|
||||
end
|
||||
end
|
||||
if s[1] == "x.neo.sys.manage" then
|
||||
|
@ -47,14 +47,14 @@ local settings = {
|
||||
|
||||
local function loadSettings()
|
||||
pcall(function ()
|
||||
local fw = require("sys-filewrap")
|
||||
local se = require("serial")
|
||||
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.deserialize(cfg)
|
||||
cfg = se(cfg)
|
||||
for k, v in pairs(cfg) do
|
||||
if type(k) == "string" then
|
||||
if type(v) == "string" then
|
||||
@ -65,11 +65,11 @@ local function loadSettings()
|
||||
end)
|
||||
end
|
||||
local function saveSettings()
|
||||
local fw = require("sys-filewrap")
|
||||
local se = require("serial")
|
||||
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.serialize(settings))
|
||||
st.write(se(settings))
|
||||
st.close()
|
||||
end
|
||||
|
||||
@ -252,7 +252,6 @@ donkonitRDProvider(function (pkg, pid, sendSig)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
getClaimable = function ()
|
||||
local c = {}
|
||||
-- do we have gpu?
|
||||
|
@ -78,24 +78,31 @@ nexus = {
|
||||
end
|
||||
}
|
||||
|
||||
local function matchesSvc(xd, pkg, perm)
|
||||
local function getPfx(xd, pkg)
|
||||
-- This is to ensure the prefix naming scheme is FOLLOWED!
|
||||
-- sys- : System, part of KittenOS NEO and thus tries to present a "unified fragmented interface" in 'neo'
|
||||
-- app- : Application - these can have ad-hoc relationships. It is EXPECTED these have a GUI
|
||||
-- svc- : Service - Same as Application but with no expectation of desktop usability
|
||||
-- Libraries "have no rights" as they are essentially loadable blobs of Lua code.
|
||||
-- They have access via the calling program, and have a subset of the NEO Kernel API
|
||||
-- Apps can register with their own name, w/ details
|
||||
local pfx = nil
|
||||
if pkg:sub(1, 4) == "app-" then pfx = "app" end
|
||||
if pkg:sub(1, 4) == "svc-" then pfx = "svc" end
|
||||
if pfx then
|
||||
-- Apps can register with their own name, w/ details
|
||||
return xd .. pfx .. "." .. pkg:sub(5)
|
||||
end
|
||||
end
|
||||
|
||||
local function matchesSvc(xd, pkg, perm)
|
||||
local pfx = getPfx(xd, pkg)
|
||||
if pfx then
|
||||
local permAct = perm
|
||||
local paP = permAct:match("/[a-z0-9/%.]*$")
|
||||
if paP then
|
||||
permAct = permAct:sub(1, #permAct - #paP)
|
||||
end
|
||||
if permAct == xd .. pfx .. "." .. pkg:sub(5) then
|
||||
if permAct == pfx then
|
||||
return "allow"
|
||||
end
|
||||
end
|
||||
@ -113,6 +120,9 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
||||
end
|
||||
return {
|
||||
showFileDialogAsync = function (forWrite)
|
||||
if not rawequal(forWrite, nil) then
|
||||
require("sys-filewrap").ensureMode(forWrite)
|
||||
end
|
||||
-- Not hooked into the event API, so can't safely interfere
|
||||
-- Thus, this is async and uses a return event.
|
||||
local tag = {}
|
||||
@ -124,6 +134,7 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
||||
end)
|
||||
return tag
|
||||
end,
|
||||
myApi = getPfx("", pkg),
|
||||
lockPerm = function (perm)
|
||||
-- Are we allowed to?
|
||||
if not matchesSvc("x.", pkg, perm) then
|
||||
@ -141,22 +152,22 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
||||
end,
|
||||
-- Paths must begin with / implicitly
|
||||
list = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) ~= "/" then error("Expected / at end") end
|
||||
return fs.primary.list(path:sub(1, #path - 1))
|
||||
end,
|
||||
makeDirectory = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
return fs.primary.makeDirectory(path)
|
||||
end,
|
||||
rename = function (path1, path2)
|
||||
if type(path1) ~= "string" then error("Expected path to be string") end
|
||||
if type(path2) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path1, "string")
|
||||
neo.ensureType(path2, "string")
|
||||
path1 = prefixNS .. path1
|
||||
path2 = prefixNS .. path2
|
||||
neo.ensurePath(path1, prefixWS)
|
||||
@ -166,12 +177,12 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
||||
return fs.primary.rename(path1, path2)
|
||||
end,
|
||||
open = function (path, mode)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
if type(mode) ~= "boolean" then error("Expected mode to be boolean (writing)") end
|
||||
neo.ensureType(path, "string")
|
||||
-- mode verified by filewrap
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
local fw, closer = require("sys-filewrap")(fs.primary, path, mode)
|
||||
local fw, closer = require("sys-filewrap").create(fs.primary, path, mode)
|
||||
local oc = fw.close
|
||||
fw.close = function ()
|
||||
oc()
|
||||
@ -181,14 +192,14 @@ donkonitDFProvider(function (pkg, pid, sendSig)
|
||||
return fw
|
||||
end,
|
||||
remove = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
return fs.primary.remove(path)
|
||||
end,
|
||||
stat = function (path)
|
||||
if type(path) ~= "string" then error("Expected path to be string") end
|
||||
neo.ensureType(path, "string")
|
||||
path = prefixNS .. path
|
||||
neo.ensurePath(path, prefixWS)
|
||||
if path:sub(#path, #path) == "/" then error("Expected no / at end") end
|
||||
|
@ -31,22 +31,24 @@ local function shutdown(reboot)
|
||||
end
|
||||
|
||||
local function rstfbDraw(gpu)
|
||||
gpu.setBackground(0xFFFFFF)
|
||||
gpu.setForeground(0x000000)
|
||||
pcall(gpu.setBackground, 0xFFFFFF)
|
||||
pcall(gpu.setForeground, 0x000000)
|
||||
end
|
||||
|
||||
local function basicDraw(gpu)
|
||||
scrW, scrH = gpu.getResolution()
|
||||
gpu.fill(1, 1, scrW, scrH, " ")
|
||||
gpu.set(2, 2, "KittenOS NEO")
|
||||
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")
|
||||
end
|
||||
|
||||
local function advDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
local usage = math.floor((os.totalMemory() - os.freeMemory()) / 1024)
|
||||
gpu.set(2, 3, "RAM Usage: " .. usage .. "K / " .. math.floor(os.totalMemory() / 1024) .. "K")
|
||||
pcall(gpu.set, 2, 3, "RAM Usage: " .. usage .. "K / " .. math.floor(os.totalMemory() / 1024) .. "K")
|
||||
for i = 1, #warnings do
|
||||
gpu.set(2, 6 + i, warnings[i])
|
||||
pcall(gpu.set, 2, 6 + i, warnings[i])
|
||||
end
|
||||
end
|
||||
|
||||
@ -91,10 +93,12 @@ local function retrieveNssMonitor(nss)
|
||||
if gpu then
|
||||
local gcb = gpu()
|
||||
if gcb then
|
||||
table.insert(subpool, {gpu, v})
|
||||
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
|
||||
@ -107,9 +111,7 @@ local function retrieveNssMonitor(nss)
|
||||
end
|
||||
-- done with search
|
||||
local gpu = gpuG()
|
||||
scrW, scrH = gpu.getResolution()
|
||||
rstfbDraw(gpu)
|
||||
gpu.fill(1, 1, scrW, scrH, " ")
|
||||
performDisclaim = function (full)
|
||||
nss.disclaim(subpool[1][2])
|
||||
if full then
|
||||
@ -212,7 +214,7 @@ local function finalPrompt()
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
gpu.set(2, 4, "Shutting down...")
|
||||
pcall(gpu.set, 2, 4, "Shutting down...")
|
||||
shutdown(false)
|
||||
end
|
||||
end, 2, scrH - 1, unicode.len(shButton)},
|
||||
@ -223,7 +225,7 @@ local function finalPrompt()
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
gpu.set(2, 4, "Rebooting...")
|
||||
pcall(gpu.set, 2, 4, "Rebooting...")
|
||||
shutdown(true)
|
||||
end
|
||||
end, 3 + unicode.len(shButton), scrH - 1, unicode.len(rbButton)},
|
||||
@ -234,7 +236,7 @@ local function finalPrompt()
|
||||
local gpu = gpuG()
|
||||
rstfbDraw(gpu)
|
||||
basicDraw(gpu)
|
||||
gpu.set(2, 4, "Login to activate Safe Mode.")
|
||||
pcall(gpu.set, 2, 4, "Login to activate Safe Mode.")
|
||||
sleep(1)
|
||||
gpu = gpuG()
|
||||
safeModeActive = true
|
||||
@ -249,14 +251,14 @@ local function finalPrompt()
|
||||
local gpu = gpuG()
|
||||
for k, v in ipairs(controls) do
|
||||
if k == control then
|
||||
gpu.setBackground(0x000000)
|
||||
gpu.setForeground(0xFFFFFF)
|
||||
pcall(gpu.setBackground, 0x000000)
|
||||
pcall(gpu.setForeground, 0xFFFFFF)
|
||||
else
|
||||
gpu.setBackground(0xFFFFFF)
|
||||
gpu.setForeground(0x000000)
|
||||
pcall(gpu.setBackground, 0xFFFFFF)
|
||||
pcall(gpu.setForeground, 0x000000)
|
||||
end
|
||||
gpu.fill(v[3], v[4], v[5], 1, " ")
|
||||
gpu.set(v[3], v[4], v[1]())
|
||||
pcall(gpu.fill, v[3], v[4], v[5], 1, " ")
|
||||
pcall(gpu.set, v[3], v[4], v[1]())
|
||||
end
|
||||
-- event handling...
|
||||
local sig = {coroutine.yield()}
|
||||
@ -336,9 +338,9 @@ local function initializeSystem()
|
||||
gpu.bind(screen, true)
|
||||
local gW, gH = gpu.maxResolution()
|
||||
gW, gH = math.min(80, gW), math.min(25, gH)
|
||||
gpu.setResolution(gW, gH)
|
||||
pcall(gpu.setResolution, gW, gH)
|
||||
pcall(gpu.setDepth, gpu.maxDepth()) -- can crash on OCEmu if done at the "wrong time"
|
||||
gpu.setForeground(0x000000)
|
||||
pcall(gpu.setForeground, 0x000000)
|
||||
end
|
||||
local w = 1
|
||||
local steps = {
|
||||
@ -373,12 +375,12 @@ local function initializeSystem()
|
||||
end
|
||||
if ev[1] == "k.timer" then
|
||||
if gpu then
|
||||
gpu.setForeground(0x000000)
|
||||
pcall(gpu.setForeground, 0x000000)
|
||||
if w < stepCount then
|
||||
local n = math.floor((w / stepCount) * 255)
|
||||
gpu.setBackground((n * 0x10000) + (n * 0x100) + n)
|
||||
pcall(gpu.setBackground, (n * 0x10000) + (n * 0x100) + n)
|
||||
else
|
||||
gpu.setBackground(0xFFFFFF)
|
||||
pcall(gpu.setBackground, 0xFFFFFF)
|
||||
end
|
||||
basicDraw(gpu)
|
||||
end
|
||||
@ -440,7 +442,7 @@ if finalPrompt() then
|
||||
basicDraw(gpu)
|
||||
local nsm = neo.requestAccess("x.neo.sys.manage")
|
||||
if nsm then
|
||||
gpu.set(2, 4, "Rebooting for Safe Mode...")
|
||||
pcall(gpu.set, 2, 4, "Rebooting for Safe Mode...")
|
||||
for _, v in ipairs(nsm.listSettings()) do
|
||||
if v ~= "password" then
|
||||
nsm.delSetting(v)
|
||||
@ -448,10 +450,10 @@ if finalPrompt() then
|
||||
end
|
||||
else
|
||||
-- assume sysconf.lua did something very bad
|
||||
gpu.set(2, 4, "No NSM. Wiping configuration completely.")
|
||||
pcall(gpu.set, 2, 4, "No NSM. Wiping configuration completely.")
|
||||
local fs = neo.requestAccess("c.filesystem")
|
||||
if not fs then
|
||||
gpu.set(2, 4, "Failed to get permission, you're doomed.")
|
||||
pcall(gpu.set, 2, 4, "Failed to get permission, you're doomed.")
|
||||
end
|
||||
fs.primary.remove("/data/sys-glacier/sysconf.lua")
|
||||
end
|
||||
|
@ -270,13 +270,17 @@ wrapTable = wrapMeta(table)
|
||||
wrapString = wrapMeta(string)
|
||||
wrapUnicode = wrapMeta(unicode)
|
||||
wrapCoroutine = wrapMeta(coroutine)
|
||||
wrapOs = wrapMeta({
|
||||
totalMemory = computer.totalMemory, freeMemory = computer.freeMemory,
|
||||
energy = computer.energy, maxEnergy = computer.maxEnergy,
|
||||
clock = os.clock, date = os.date, difftime = os.difftime,
|
||||
time = os.time, uptime = computer.uptime, address = computer.address
|
||||
})
|
||||
-- inject stuff into os
|
||||
os.totalMemory = computer.totalMemory
|
||||
os.freeMemory = computer.freeMemory
|
||||
os.energy = computer.energy
|
||||
os.maxEnergy = computer.maxEnergy
|
||||
os.uptime = computer.uptime
|
||||
os.address = computer.address
|
||||
wrapOs = wrapMeta(os)
|
||||
wrapDebug = wrapMeta(debug)
|
||||
wrapBit32 = wrapMeta(bit32)
|
||||
wrapUtf8 = wrapMeta(utf8)
|
||||
|
||||
baseProcEnvCore = {
|
||||
_VERSION = _VERSION,
|
||||
@ -287,6 +291,8 @@ baseProcEnvCore = {
|
||||
coroutine = wrapCoroutine,
|
||||
os = wrapOs,
|
||||
debug = wrapDebug,
|
||||
bit32 = wrapBit32,
|
||||
utf8 = wrapUtf8,
|
||||
require = loadLibraryInner,
|
||||
assert = assert, ipairs = ipairs,
|
||||
load = load,
|
||||
@ -382,7 +388,7 @@ function retrieveAccess(perm, pkg, pid)
|
||||
-- "c.<hw>": Component
|
||||
-- "s.<event>": Signal receiver (with responsibilities for Security Request watchers)
|
||||
-- "s.k.<...>": Kernel stuff
|
||||
-- "s.k.procnew" : New process (pkg, pid)
|
||||
-- "s.k.procnew" : New process (pkg, pid, ppkg, ppid)
|
||||
-- "s.k.procdie" : Process dead (pkg, pid, reason, usage)
|
||||
-- "s.k.registration" : Registration of service alert ("x." .. etc)
|
||||
-- "s.k.deregistration" : Registration of service alert ("x." .. etc)
|
||||
@ -480,7 +486,7 @@ function retrieveAccess(perm, pkg, pid)
|
||||
end
|
||||
end
|
||||
|
||||
function start(pkg, ...)
|
||||
function start(pkg, ppkg, ppid, ...)
|
||||
local proc = {}
|
||||
local pid = lastPID
|
||||
lastPID = lastPID + 1
|
||||
@ -571,7 +577,7 @@ function start(pkg, ...)
|
||||
env.neo.scheduleTimer = function (time)
|
||||
ensureType(time, "number")
|
||||
local tag = {}
|
||||
table.insert(timers, {time, execEvent, pid, "k.timer", tag, time, ofs})
|
||||
table.insert(timers, {time, execEvent, pid, "k.timer", tag, time})
|
||||
return tag
|
||||
end
|
||||
|
||||
@ -587,9 +593,9 @@ function start(pkg, ...)
|
||||
proc.deathCBs = {function () pcall(function () env.neo.dead = true end) end}
|
||||
proc.cpuUsage = 0
|
||||
-- Note the target process doesn't get the procnew (the dist occurs before it's creation)
|
||||
pcall(distEvent, nil, "k.procnew", pkg, pid)
|
||||
pcall(distEvent, nil, "k.procnew", pkg, pid, ppkg, ppid)
|
||||
processes[pid] = proc
|
||||
table.insert(timers, {0, execEvent, pid, ...})
|
||||
table.insert(timers, {0, execEvent, pid, ppkg, ppid, ...})
|
||||
return pid
|
||||
end
|
||||
|
||||
|
@ -113,7 +113,7 @@ getFsNode = function (fs, parent, fsc, path, mode)
|
||||
end,
|
||||
unknownAvailable = mode ~= nil,
|
||||
selectUnknown = function (text)
|
||||
local rt, re = require("sys-filewrap")(fsc, path .. text, mode)
|
||||
local rt, re = require("sys-filewrap").create(fsc, path .. text, mode)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
@ -130,8 +130,14 @@ getFsNode = function (fs, parent, fsc, path, mode)
|
||||
return nil, parent
|
||||
end})
|
||||
if mode ~= nil then
|
||||
table.insert(n, {"Open", function ()
|
||||
local rt, re = require("sys-filewrap")(fsc, path, mode)
|
||||
local tx = "Open"
|
||||
if mode == true then
|
||||
tx = "Save"
|
||||
elseif mode == "append" then
|
||||
tx = "Append"
|
||||
end
|
||||
table.insert(n, {tx, function ()
|
||||
local rt, re = require("sys-filewrap").create(fsc, path, mode)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
@ -139,7 +145,7 @@ getFsNode = function (fs, parent, fsc, path, mode)
|
||||
end})
|
||||
end
|
||||
table.insert(n, {"Copy", function ()
|
||||
local rt, re = require("sys-filewrap")(fsc, path, false)
|
||||
local rt, re = require("sys-filewrap").create(fsc, path, false)
|
||||
if not rt then
|
||||
return false, dialog("Open Error: " .. tostring(re), parent)
|
||||
end
|
||||
|
@ -1,9 +1,25 @@
|
||||
-- This is released into the public domain.
|
||||
-- No warranty is provided, implied or otherwise.
|
||||
|
||||
return function(dev, file, mode)
|
||||
local function ensureMode(mode)
|
||||
local n = "rb"
|
||||
if mode then n = "wb" end
|
||||
if type(mode) == "boolean" then
|
||||
if mode then
|
||||
n = "wb"
|
||||
end
|
||||
elseif type(mode) == "string" then
|
||||
if mode == "append" then
|
||||
n = "ab"
|
||||
else
|
||||
error("Invalid fmode " .. mode)
|
||||
end
|
||||
else
|
||||
error("Invalid fmode")
|
||||
end
|
||||
return n
|
||||
end
|
||||
local function create(dev, file, mode)
|
||||
local n = ensureMode(mode)
|
||||
local handle, r = dev.open(file, n)
|
||||
if not handle then return nil, r end
|
||||
local open = true
|
||||
@ -14,15 +30,8 @@ return function(dev, file, mode)
|
||||
dev.close(handle)
|
||||
end)
|
||||
end
|
||||
local function seeker(whence, point)
|
||||
local function reader(len)
|
||||
if not open then return end
|
||||
return dev.seek(handle, whence, point)
|
||||
end
|
||||
if not mode then
|
||||
return {
|
||||
close = closer,
|
||||
seek = seeker,
|
||||
read = function (len)
|
||||
if len == "*a" then
|
||||
local ch = ""
|
||||
local c = dev.read(handle, neo.readBufSize)
|
||||
@ -35,16 +44,31 @@ return function(dev, file, mode)
|
||||
if type(len) ~= "number" then error("Length of read must be number or '*a'") end
|
||||
return dev.read(handle, len)
|
||||
end
|
||||
local function writer(txt)
|
||||
if not open then return end
|
||||
neo.ensureType(txt, "string")
|
||||
return dev.write(handle, txt)
|
||||
end
|
||||
local function seeker(whence, point)
|
||||
if not open then return end
|
||||
return dev.seek(handle, whence, point)
|
||||
end
|
||||
if mode == "rb" then
|
||||
return {
|
||||
close = closer,
|
||||
seek = seeker,
|
||||
read = reader
|
||||
}, closer
|
||||
else
|
||||
return {
|
||||
close = closer,
|
||||
seek = seeker,
|
||||
write = function (txt)
|
||||
if type(txt) ~= "string" then error("Write data must be string-bytearray") end
|
||||
local ok, b = dev.write(handle, txt)
|
||||
if not ok then error(tostring(b)) end
|
||||
end
|
||||
read = reader,
|
||||
write = writer
|
||||
}, closer
|
||||
end
|
||||
end
|
||||
return {
|
||||
createMode = createMode,
|
||||
create = create
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ return {
|
||||
"docs/us-nxapp",
|
||||
"docs/us-setti",
|
||||
"docs/ul-seria",
|
||||
"docs/ul-fwrap",
|
||||
"docs/ul-event",
|
||||
"docs/ul-fmttx",
|
||||
"docs/ul-neoux",
|
||||
|
@ -57,7 +57,7 @@ The following are just wrapMeta'd
|
||||
host libraries (*: altered):
|
||||
|
||||
math, table, string, unicode*,
|
||||
coroutine, os*, debug
|
||||
coroutine, os*, debug, utf8, bit32
|
||||
|
||||
unicode is extended with:
|
||||
safeTextFormat(s, p):
|
||||
@ -92,14 +92,11 @@ Programs that thus try to work around
|
||||
as-needed if and when the issue is
|
||||
resolved.
|
||||
|
||||
os is replaced with (wrapMeta'd):
|
||||
os is extended with:
|
||||
totalMemory = computer.totalMemory,
|
||||
freeMemory = computer.freeMemory,
|
||||
energy = computer.energy,
|
||||
maxEnergy = computer.maxEnergy,
|
||||
clock = os.clock, date = os.date,
|
||||
difftime = os.difftime,
|
||||
time = os.time,
|
||||
uptime = computer.uptime,
|
||||
address = computer.address
|
||||
|
||||
@ -317,6 +314,38 @@ The additional things available to
|
||||
and timers stick around after the
|
||||
process that owns them is dead.
|
||||
|
||||
The list of events, tacked on at the
|
||||
end here:
|
||||
|
||||
k.procnew(pkg, pid, ppkg, ppid):
|
||||
New process creation, with parent
|
||||
information (for seat tracking)
|
||||
This is not given to the process
|
||||
being created, as all of this gets
|
||||
given to it anyway on main function
|
||||
start.
|
||||
|
||||
k.procdie(pkg, pid, reason, cpuTime):
|
||||
Process death.
|
||||
|
||||
k.registration(uid):
|
||||
Registration of an access.
|
||||
|
||||
k.deregistration(uid):
|
||||
Deregistration of an access.
|
||||
|
||||
k.securityresponse(perm, obj):
|
||||
Response to a security request made
|
||||
with neo.requestAccess or such.
|
||||
|
||||
k.timer(tag, time):
|
||||
A timer. Includes the planned uptime
|
||||
for comparison.
|
||||
|
||||
h.*(...):
|
||||
Hardware signals, by type, such
|
||||
as "h.key_up"
|
||||
|
||||
With that, I hope I have documented
|
||||
the kernel's interface to programs.
|
||||
|
||||
|
49
repository/docs/ul-fwrap
Normal file
49
repository/docs/ul-fwrap
Normal file
@ -0,0 +1,49 @@
|
||||
"sys-filewrap" is responsible for
|
||||
wrapping a filesystem object with a
|
||||
file-like mechanism.
|
||||
|
||||
It simply provides two functions:
|
||||
ensureMode(mode): Ensures that a
|
||||
mode is valid, and translates it.
|
||||
create(dev, file, mode):
|
||||
Open a file using a given proxy,
|
||||
filename and mode.
|
||||
|
||||
The mode you give to it can be one of
|
||||
the following:
|
||||
false: Read "rb"
|
||||
true: Write "wb"
|
||||
"append": Append "ab"
|
||||
May have some readability
|
||||
propertiues - just in
|
||||
case, I've added 'read',
|
||||
but don't get your hopes
|
||||
up...
|
||||
|
||||
It returns two things - a table, that
|
||||
being the file object, and the
|
||||
'close' function from that object,
|
||||
for closing the file without using
|
||||
a potentially modifiable table.
|
||||
|
||||
If the table is nil, then the "close"
|
||||
function is actually a string, that
|
||||
being the error.
|
||||
|
||||
The possible functions are:
|
||||
[arw] close(): Closes the file.
|
||||
[arw] seek(whence, pos): Seeks in
|
||||
the file.
|
||||
[aw] write(data): Writes to the
|
||||
file.
|
||||
[arw] read(data): Reads from the
|
||||
file.
|
||||
|
||||
NOTE: Some of these may not actually
|
||||
work. They're just there as more or
|
||||
less a "do no harm" approach.
|
||||
|
||||
-- This is released into
|
||||
the public domain.
|
||||
-- No warranty is provided,
|
||||
implied or otherwise.
|
@ -18,6 +18,16 @@ For programs with the prefixes "svc-"
|
||||
pattern "/[a-z0-9/%.]*$", or none
|
||||
at all.
|
||||
|
||||
Examples:
|
||||
|
||||
r.app.nucleus:
|
||||
Registers x.app.nucleus from
|
||||
app-nucleus
|
||||
|
||||
r.svc.nucleus/ABCD:
|
||||
Registers x.svc.nucleus/ABCD from
|
||||
svc-nucleus
|
||||
|
||||
For how this registration works,
|
||||
and how to access the resulting
|
||||
service, please see the kn-perms
|
||||
@ -33,31 +43,149 @@ APIs registered in this manner are
|
||||
mechanism if they so wish, and this
|
||||
will cause a silent failure of the
|
||||
lockPerm function.)
|
||||
A mechanism may also be introduced
|
||||
in later versions of KittenOS NEO to
|
||||
easily allow changing your svc/app's
|
||||
own API to a "ask"-style security
|
||||
model, but this will not be the
|
||||
default, and may still be overridden
|
||||
by a user with access to the
|
||||
Advanced Settings control panel.
|
||||
|
||||
As for the system APIs...
|
||||
|
||||
-- x.neo.pub.base @ sys-icecap --
|
||||
|
||||
-- x.neo.pub.session @ <a shell> --
|
||||
Gaining access to this API creates
|
||||
this application's data directory,
|
||||
wherever that is.
|
||||
|
||||
This API is
|
||||
Paths for the IO parts of this API
|
||||
must start with "/", and must follow
|
||||
the standard KittenOS NEO path
|
||||
safety rules in the kernel.
|
||||
|
||||
-- x.neo.pub.window @ <a shell> --
|
||||
showFileDialogAsync(mode): Shows a
|
||||
filedialog with a given filemode,
|
||||
or nil for "none". Returns a new,
|
||||
empty table as a "tag", and emits a
|
||||
"filedialog" event on completion.
|
||||
|
||||
myApi: A string such as "svc.abc"
|
||||
for the program svc-abc, used for
|
||||
automatically finding the correct
|
||||
API name to register for your app.
|
||||
|
||||
lockPerm(perm): Changes the default
|
||||
permission setting for anything the
|
||||
process should have permission to
|
||||
define according to the matchesSvc
|
||||
function, so that the user must be
|
||||
asked before a program can access
|
||||
the permission.
|
||||
|
||||
NOTE: LIST REQUIRES "/" AT THE END
|
||||
AND START, WHILE THE REST CANNOT
|
||||
HAVE IT AT THE END BUT MUST AT THE
|
||||
START, BECAUSE THE ROOT IS "/".
|
||||
There's logic here, specifically to
|
||||
stop you trying to do nonsense like
|
||||
deleting your own data directory...
|
||||
|
||||
list(path): After ensuring that the
|
||||
path has a "/" at the end, lists
|
||||
the contents of a directory
|
||||
accessible to the application.
|
||||
Returns a table containing
|
||||
file/directory names, with "/"
|
||||
postfixes for directories. If this
|
||||
contains the invalid names "." or
|
||||
"..", please report as a bug at
|
||||
once, this shouldn't happen.
|
||||
|
||||
Everything after here ensures that
|
||||
there is a "/" at the start and no
|
||||
"/" at the end
|
||||
|
||||
makeDirectory(path): Creates the
|
||||
directory with the given path.
|
||||
Returns whatever the component call
|
||||
did.
|
||||
|
||||
rename(pathA, pathB): Renames or
|
||||
moves a file or directory around.
|
||||
Returns whatever the component call
|
||||
did.
|
||||
|
||||
open(path, mode): Opens a file with
|
||||
a given mode (see ul-fwrap for the
|
||||
list of modes).
|
||||
Returns the file object,
|
||||
|
||||
remove(path): Removes a file.
|
||||
|
||||
stat(path): "I got lazy, so I took 3
|
||||
API functions and combined them as
|
||||
one API function!" - 20kdc
|
||||
Returns {
|
||||
isDirectory
|
||||
size
|
||||
lastModified
|
||||
}
|
||||
|
||||
spaceUsed, spaceTotal, isReadOnly:
|
||||
The filesystem proxy functions.
|
||||
|
||||
Events:
|
||||
api, "filedialog", tag, res
|
||||
|
||||
-- x.neo.pub.session @ <a shell> --
|
||||
|
||||
This API must be implemented by any
|
||||
program that the user wants to use
|
||||
as a shell.
|
||||
|
||||
A shell should set up a Saving Throw
|
||||
with sys-glacier in case of errors.
|
||||
|
||||
endSession(backToInit): Stops the
|
||||
current session, and optionally
|
||||
starts up sys-init.
|
||||
endSession(false) is for switching
|
||||
to a new shell.
|
||||
endSession(true) is for logging out
|
||||
of the session.
|
||||
|
||||
getMonitors(): Returns an ipairs-
|
||||
form list of screen addresses for
|
||||
use in disclaimMonitor and the
|
||||
subsequent screens.claim call.
|
||||
|
||||
disclaimMonitor(address):
|
||||
Disclaims a monitor. Returns true
|
||||
on success. The monitor may be
|
||||
reclaimed if something causes the
|
||||
shell to be notified of the
|
||||
monitor's existence (such as a
|
||||
monitor rescan in Glacier).
|
||||
|
||||
No events are given. Deregistration
|
||||
is equivalent to stating that you
|
||||
are no longer in control of the
|
||||
session, and that something else is.
|
||||
|
||||
-- x.neo.pub.window @ sys-everest --
|
||||
|
||||
This API is the reference definition
|
||||
of how the windowing system works in
|
||||
KittenOS NEO.
|
||||
|
||||
-- x.neo.sys.manage @ sys-glacier --
|
||||
|
||||
This API is how settings and ST are
|
||||
managed
|
||||
|
||||
-- x.neo.sys.screens @ sys-glacier --
|
||||
|
||||
This API is how screens are managed
|
||||
|
||||
-- x.neo.pub.globals @ sys-glacier --
|
||||
|
||||
This API is some public global stuff
|
||||
and spooky screen control???
|
||||
|
||||
-- This is released into
|
||||
the public domain.
|
||||
-- No warranty is provided,
|
||||
|
Loading…
Reference in New Issue
Block a user