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:
20kdc 2018-04-12 00:04:16 +01:00
parent 6c0659de60
commit a5372eafe1
12 changed files with 449 additions and 125 deletions

52
KTC1.md Normal file
View 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.

View File

@ -120,11 +120,11 @@ local function monitorGPUColours(m, cb, bg, fg)
local nbg = m[5] local nbg = m[5]
local nfg = m[6] local nfg = m[6]
if nbg ~= bg then if nbg ~= bg then
cb.setBackground(bg) pcall(cb.setBackground, bg)
m[5] = bg m[5] = bg
end end
if nfg ~= fg then if nfg ~= fg then
cb.setForeground(fg) pcall(cb.setForeground, fg)
m[6] = fg m[6] = fg
end end
end end
@ -138,11 +138,11 @@ local function doBackgroundLine(m, mg, bdx, bdy, bdl)
monitorGPUColours(m, mg, 0x000000, 0xFFFFFF) monitorGPUColours(m, mg, 0x000000, 0xFFFFFF)
local str = unicode.sub(statusLine, bdx, bdx + bdl - 1) local str = unicode.sub(statusLine, bdx, bdx + bdl - 1)
local strl = unicode.len(str) local strl = unicode.len(str)
mg.set(bdx, bdy, unicode.undoSafeTextFormat(str)) pcall(mg.set, bdx, bdy, unicode.undoSafeTextFormat(str))
mg.fill(bdx + strl, bdy, bdl - strl, 1, " ") pcall(mg.fill, bdx + strl, bdy, bdl - strl, 1, " ")
else else
monitorGPUColours(m, mg, 0x000020, 0) monitorGPUColours(m, mg, 0x000020, 0)
mg.fill(bdx, bdy, bdl, 1, " ") pcall(mg.fill, bdx, bdy, bdl, 1, " ")
end end
end end
@ -265,7 +265,7 @@ local function moveSurface(surface, m, x, y, w, h, force)
if renderingAllowed() and not force then if renderingAllowed() and not force then
local cb, b = monitors[m][1]() local cb, b = monitors[m][1]()
if b then if b then
monitorResetBF(b) monitorResetBF(monitors[m])
end end
if cb then if cb then
cb.copy(ox, oy, w, h, x - ox, y - oy) 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) base = unicode.sub(text, buildingSegment, buildingSegmentE)
-- rely on undoSafeTextFormat for this transform now -- rely on undoSafeTextFormat for this transform now
monitorGPUColours(m, cb, bg, fg) monitorGPUColours(m, cb, bg, fg)
cb.set(buildingSegmentWX, buildingSegmentWY, unicode.undoSafeTextFormat(base)) pcall(cb.set, buildingSegmentWX, buildingSegmentWY, unicode.undoSafeTextFormat(base))
buildingSegment = nil buildingSegment = nil
end end
end end
@ -520,15 +520,37 @@ everestProvider(function (pkg, pid, sendSig)
end end
end) end)
-- THE EVEREST USER API ENDS (now for the session API, which just does boring stuff) -- 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) everestSessionProvider(function (pkg, pid, sendSig)
return { return {
endSession = function (gotoBristol) endSession = function (gotoBristol)
neo.ensureType(gotoBristol, "boolean")
shuttingDown = true shuttingDown = true
if gotoBristol then if gotoBristol then
suggestAppsStop() suggestAppsStop()
dying() dying()
end end
end,
getMonitors = function ()
local details = {}
for k, v in ipairs(monitors) do
details[k] = v[2]
end end
return details
end,
disclaimMonitor = disclaimMonitor
} }
end) end)
-- THE EVEREST SESSION API ENDS -- THE EVEREST SESSION API ENDS
@ -539,7 +561,14 @@ end)
-- Alt-Up/Down/Left/Right: Move surface -- Alt-Up/Down/Left/Right: Move surface
local isAltDown = false local isAltDown = false
local isCtrDown = 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] local focus = surfaces[1]
if kc == 29 then isCtrDown = down end if kc == 29 then isCtrDown = down end
if kc == 56 then isAltDown = down end if kc == 56 then isAltDown = down end
@ -617,16 +646,10 @@ while not shuttingDown do
local s = {coroutine.yield()} local s = {coroutine.yield()}
if renderingAllowed() then if renderingAllowed() then
if s[1] == "h.key_down" then if s[1] == "h.key_down" then
local m = screens.getMonitorByKeyboard(s[2]) key(s[2], s[3], s[4], true)
for k, v in ipairs(monitors) do
if v[2] == m then
lIM = k
end
end
key(s[3], s[4], true)
end end
if s[1] == "h.key_up" then if s[1] == "h.key_up" then
key(s[3], s[4], false) key(s[2], s[3], s[4], false)
end end
if s[1] == "h.clipboard" then if s[1] == "h.clipboard" then
if surfaces[1] then if surfaces[1] then
@ -697,13 +720,7 @@ while not shuttingDown do
performClaim(s[3]) performClaim(s[3])
end end
if s[2] == "lost" then if s[2] == "lost" then
for k, v in ipairs(monitors) do handleLostMonitor(s[3])
if v[2] == s[3] then
table.remove(monitors, k)
reconcileAll()
break
end
end
end end
end end
if s[1] == "x.neo.sys.manage" then if s[1] == "x.neo.sys.manage" then

View File

@ -47,14 +47,14 @@ local settings = {
local function loadSettings() local function loadSettings()
pcall(function () pcall(function ()
local fw = require("sys-filewrap") local fw = require("sys-filewrap").create
local se = require("serial") local se = require("serial").deserialize
local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", false) local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", false)
local cfg = st.read("*a") local cfg = st.read("*a")
st.close() st.close()
st = nil st = nil
fw = nil fw = nil
cfg = se.deserialize(cfg) cfg = se(cfg)
for k, v in pairs(cfg) do for k, v in pairs(cfg) do
if type(k) == "string" then if type(k) == "string" then
if type(v) == "string" then if type(v) == "string" then
@ -65,11 +65,11 @@ local function loadSettings()
end) end)
end end
local function saveSettings() local function saveSettings()
local fw = require("sys-filewrap") local fw = require("sys-filewrap").create
local se = require("serial") local se = require("serial").serialize
fs.primary.makeDirectory("data/sys-glacier") fs.primary.makeDirectory("data/sys-glacier")
local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", true) local st = fw(fs.primary, "data/sys-glacier/sysconf.lua", true)
st.write(se.serialize(settings)) st.write(se(settings))
st.close() st.close()
end end
@ -252,7 +252,6 @@ donkonitRDProvider(function (pkg, pid, sendSig)
end end
end end
end, end,
getClaimable = function () getClaimable = function ()
local c = {} local c = {}
-- do we have gpu? -- do we have gpu?

View File

@ -78,24 +78,31 @@ nexus = {
end end
} }
local function matchesSvc(xd, pkg, perm) local function getPfx(xd, pkg)
-- This is to ensure the prefix naming scheme is FOLLOWED! -- 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' -- 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 -- 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 -- 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. -- 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 -- 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 local pfx = nil
if pkg:sub(1, 4) == "app-" then pfx = "app" end if pkg:sub(1, 4) == "app-" then pfx = "app" end
if pkg:sub(1, 4) == "svc-" then pfx = "svc" end if pkg:sub(1, 4) == "svc-" then pfx = "svc" end
if pfx then 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 permAct = perm
local paP = permAct:match("/[a-z0-9/%.]*$") local paP = permAct:match("/[a-z0-9/%.]*$")
if paP then if paP then
permAct = permAct:sub(1, #permAct - #paP) permAct = permAct:sub(1, #permAct - #paP)
end end
if permAct == xd .. pfx .. "." .. pkg:sub(5) then if permAct == pfx then
return "allow" return "allow"
end end
end end
@ -113,6 +120,9 @@ donkonitDFProvider(function (pkg, pid, sendSig)
end end
return { return {
showFileDialogAsync = function (forWrite) 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 -- Not hooked into the event API, so can't safely interfere
-- Thus, this is async and uses a return event. -- Thus, this is async and uses a return event.
local tag = {} local tag = {}
@ -124,6 +134,7 @@ donkonitDFProvider(function (pkg, pid, sendSig)
end) end)
return tag return tag
end, end,
myApi = getPfx("", pkg),
lockPerm = function (perm) lockPerm = function (perm)
-- Are we allowed to? -- Are we allowed to?
if not matchesSvc("x.", pkg, perm) then if not matchesSvc("x.", pkg, perm) then
@ -141,22 +152,22 @@ donkonitDFProvider(function (pkg, pid, sendSig)
end, end,
-- Paths must begin with / implicitly -- Paths must begin with / implicitly
list = function (path) list = function (path)
if type(path) ~= "string" then error("Expected path to be string") end neo.ensureType(path, "string")
path = prefixNS .. path path = prefixNS .. path
neo.ensurePath(path, prefixWS) neo.ensurePath(path, prefixWS)
if path:sub(#path, #path) ~= "/" then error("Expected / at end") end if path:sub(#path, #path) ~= "/" then error("Expected / at end") end
return fs.primary.list(path:sub(1, #path - 1)) return fs.primary.list(path:sub(1, #path - 1))
end, end,
makeDirectory = function (path) makeDirectory = function (path)
if type(path) ~= "string" then error("Expected path to be string") end neo.ensureType(path, "string")
path = prefixNS .. path path = prefixNS .. path
neo.ensurePath(path, prefixWS) neo.ensurePath(path, prefixWS)
if path:sub(#path, #path) == "/" then error("Expected no / at end") end if path:sub(#path, #path) == "/" then error("Expected no / at end") end
return fs.primary.makeDirectory(path) return fs.primary.makeDirectory(path)
end, end,
rename = function (path1, path2) rename = function (path1, path2)
if type(path1) ~= "string" then error("Expected path to be string") end neo.ensureType(path1, "string")
if type(path2) ~= "string" then error("Expected path to be string") end neo.ensureType(path2, "string")
path1 = prefixNS .. path1 path1 = prefixNS .. path1
path2 = prefixNS .. path2 path2 = prefixNS .. path2
neo.ensurePath(path1, prefixWS) neo.ensurePath(path1, prefixWS)
@ -166,12 +177,12 @@ donkonitDFProvider(function (pkg, pid, sendSig)
return fs.primary.rename(path1, path2) return fs.primary.rename(path1, path2)
end, end,
open = function (path, mode) open = function (path, mode)
if type(path) ~= "string" then error("Expected path to be string") end neo.ensureType(path, "string")
if type(mode) ~= "boolean" then error("Expected mode to be boolean (writing)") end -- mode verified by filewrap
path = prefixNS .. path path = prefixNS .. path
neo.ensurePath(path, prefixWS) neo.ensurePath(path, prefixWS)
if path:sub(#path, #path) == "/" then error("Expected no / at end") end 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 local oc = fw.close
fw.close = function () fw.close = function ()
oc() oc()
@ -181,14 +192,14 @@ donkonitDFProvider(function (pkg, pid, sendSig)
return fw return fw
end, end,
remove = function (path) remove = function (path)
if type(path) ~= "string" then error("Expected path to be string") end neo.ensureType(path, "string")
path = prefixNS .. path path = prefixNS .. path
neo.ensurePath(path, prefixWS) neo.ensurePath(path, prefixWS)
if path:sub(#path, #path) == "/" then error("Expected no / at end") end if path:sub(#path, #path) == "/" then error("Expected no / at end") end
return fs.primary.remove(path) return fs.primary.remove(path)
end, end,
stat = function (path) stat = function (path)
if type(path) ~= "string" then error("Expected path to be string") end neo.ensureType(path, "string")
path = prefixNS .. path path = prefixNS .. path
neo.ensurePath(path, prefixWS) neo.ensurePath(path, prefixWS)
if path:sub(#path, #path) == "/" then error("Expected no / at end") end if path:sub(#path, #path) == "/" then error("Expected no / at end") end

View File

@ -31,22 +31,24 @@ local function shutdown(reboot)
end end
local function rstfbDraw(gpu) local function rstfbDraw(gpu)
gpu.setBackground(0xFFFFFF) pcall(gpu.setBackground, 0xFFFFFF)
gpu.setForeground(0x000000) pcall(gpu.setForeground, 0x000000)
end end
local function basicDraw(gpu) local function basicDraw(gpu)
scrW, scrH = gpu.getResolution() local ok, sw, sh = pcall(gpu.getResolution)
gpu.fill(1, 1, scrW, scrH, " ") if not ok then return end
gpu.set(2, 2, "KittenOS NEO") scrW, scrH = sw, sh
pcall(gpu.fill, 1, 1, scrW, scrH, " ")
pcall(gpu.set, 2, 2, "KittenOS NEO")
end end
local function advDraw(gpu) local function advDraw(gpu)
basicDraw(gpu) basicDraw(gpu)
local usage = math.floor((os.totalMemory() - os.freeMemory()) / 1024) 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 for i = 1, #warnings do
gpu.set(2, 6 + i, warnings[i]) pcall(gpu.set, 2, 6 + i, warnings[i])
end end
end end
@ -91,10 +93,12 @@ local function retrieveNssMonitor(nss)
if gpu then if gpu then
local gcb = gpu() local gcb = gpu()
if gcb then if gcb then
table.insert(subpool, {gpu, v}) pcall(function ()
gcb.setBackground(0x000020) gcb.setBackground(0x000020)
local w, h = gcb.getResolution() local w, h = gcb.getResolution()
gcb.fill(1, 1, w, h, " ") gcb.fill(1, 1, w, h, " ")
table.insert(subpool, {gpu, v})
end)
end end
end end
end end
@ -107,9 +111,7 @@ local function retrieveNssMonitor(nss)
end end
-- done with search -- done with search
local gpu = gpuG() local gpu = gpuG()
scrW, scrH = gpu.getResolution()
rstfbDraw(gpu) rstfbDraw(gpu)
gpu.fill(1, 1, scrW, scrH, " ")
performDisclaim = function (full) performDisclaim = function (full)
nss.disclaim(subpool[1][2]) nss.disclaim(subpool[1][2])
if full then if full then
@ -212,7 +214,7 @@ local function finalPrompt()
local gpu = gpuG() local gpu = gpuG()
rstfbDraw(gpu) rstfbDraw(gpu)
basicDraw(gpu) basicDraw(gpu)
gpu.set(2, 4, "Shutting down...") pcall(gpu.set, 2, 4, "Shutting down...")
shutdown(false) shutdown(false)
end end
end, 2, scrH - 1, unicode.len(shButton)}, end, 2, scrH - 1, unicode.len(shButton)},
@ -223,7 +225,7 @@ local function finalPrompt()
local gpu = gpuG() local gpu = gpuG()
rstfbDraw(gpu) rstfbDraw(gpu)
basicDraw(gpu) basicDraw(gpu)
gpu.set(2, 4, "Rebooting...") pcall(gpu.set, 2, 4, "Rebooting...")
shutdown(true) shutdown(true)
end end
end, 3 + unicode.len(shButton), scrH - 1, unicode.len(rbButton)}, end, 3 + unicode.len(shButton), scrH - 1, unicode.len(rbButton)},
@ -234,7 +236,7 @@ local function finalPrompt()
local gpu = gpuG() local gpu = gpuG()
rstfbDraw(gpu) rstfbDraw(gpu)
basicDraw(gpu) basicDraw(gpu)
gpu.set(2, 4, "Login to activate Safe Mode.") pcall(gpu.set, 2, 4, "Login to activate Safe Mode.")
sleep(1) sleep(1)
gpu = gpuG() gpu = gpuG()
safeModeActive = true safeModeActive = true
@ -249,14 +251,14 @@ local function finalPrompt()
local gpu = gpuG() local gpu = gpuG()
for k, v in ipairs(controls) do for k, v in ipairs(controls) do
if k == control then if k == control then
gpu.setBackground(0x000000) pcall(gpu.setBackground, 0x000000)
gpu.setForeground(0xFFFFFF) pcall(gpu.setForeground, 0xFFFFFF)
else else
gpu.setBackground(0xFFFFFF) pcall(gpu.setBackground, 0xFFFFFF)
gpu.setForeground(0x000000) pcall(gpu.setForeground, 0x000000)
end end
gpu.fill(v[3], v[4], v[5], 1, " ") pcall(gpu.fill, v[3], v[4], v[5], 1, " ")
gpu.set(v[3], v[4], v[1]()) pcall(gpu.set, v[3], v[4], v[1]())
end end
-- event handling... -- event handling...
local sig = {coroutine.yield()} local sig = {coroutine.yield()}
@ -336,9 +338,9 @@ local function initializeSystem()
gpu.bind(screen, true) gpu.bind(screen, true)
local gW, gH = gpu.maxResolution() local gW, gH = gpu.maxResolution()
gW, gH = math.min(80, gW), math.min(25, gH) 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" pcall(gpu.setDepth, gpu.maxDepth()) -- can crash on OCEmu if done at the "wrong time"
gpu.setForeground(0x000000) pcall(gpu.setForeground, 0x000000)
end end
local w = 1 local w = 1
local steps = { local steps = {
@ -373,12 +375,12 @@ local function initializeSystem()
end end
if ev[1] == "k.timer" then if ev[1] == "k.timer" then
if gpu then if gpu then
gpu.setForeground(0x000000) pcall(gpu.setForeground, 0x000000)
if w < stepCount then if w < stepCount then
local n = math.floor((w / stepCount) * 255) local n = math.floor((w / stepCount) * 255)
gpu.setBackground((n * 0x10000) + (n * 0x100) + n) pcall(gpu.setBackground, (n * 0x10000) + (n * 0x100) + n)
else else
gpu.setBackground(0xFFFFFF) pcall(gpu.setBackground, 0xFFFFFF)
end end
basicDraw(gpu) basicDraw(gpu)
end end
@ -440,7 +442,7 @@ if finalPrompt() then
basicDraw(gpu) basicDraw(gpu)
local nsm = neo.requestAccess("x.neo.sys.manage") local nsm = neo.requestAccess("x.neo.sys.manage")
if nsm then 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 for _, v in ipairs(nsm.listSettings()) do
if v ~= "password" then if v ~= "password" then
nsm.delSetting(v) nsm.delSetting(v)
@ -448,10 +450,10 @@ if finalPrompt() then
end end
else else
-- assume sysconf.lua did something very bad -- 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") local fs = neo.requestAccess("c.filesystem")
if not fs then 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 end
fs.primary.remove("/data/sys-glacier/sysconf.lua") fs.primary.remove("/data/sys-glacier/sysconf.lua")
end end

View File

@ -270,13 +270,17 @@ wrapTable = wrapMeta(table)
wrapString = wrapMeta(string) wrapString = wrapMeta(string)
wrapUnicode = wrapMeta(unicode) wrapUnicode = wrapMeta(unicode)
wrapCoroutine = wrapMeta(coroutine) wrapCoroutine = wrapMeta(coroutine)
wrapOs = wrapMeta({ -- inject stuff into os
totalMemory = computer.totalMemory, freeMemory = computer.freeMemory, os.totalMemory = computer.totalMemory
energy = computer.energy, maxEnergy = computer.maxEnergy, os.freeMemory = computer.freeMemory
clock = os.clock, date = os.date, difftime = os.difftime, os.energy = computer.energy
time = os.time, uptime = computer.uptime, address = computer.address os.maxEnergy = computer.maxEnergy
}) os.uptime = computer.uptime
os.address = computer.address
wrapOs = wrapMeta(os)
wrapDebug = wrapMeta(debug) wrapDebug = wrapMeta(debug)
wrapBit32 = wrapMeta(bit32)
wrapUtf8 = wrapMeta(utf8)
baseProcEnvCore = { baseProcEnvCore = {
_VERSION = _VERSION, _VERSION = _VERSION,
@ -287,6 +291,8 @@ baseProcEnvCore = {
coroutine = wrapCoroutine, coroutine = wrapCoroutine,
os = wrapOs, os = wrapOs,
debug = wrapDebug, debug = wrapDebug,
bit32 = wrapBit32,
utf8 = wrapUtf8,
require = loadLibraryInner, require = loadLibraryInner,
assert = assert, ipairs = ipairs, assert = assert, ipairs = ipairs,
load = load, load = load,
@ -382,7 +388,7 @@ function retrieveAccess(perm, pkg, pid)
-- "c.<hw>": Component -- "c.<hw>": Component
-- "s.<event>": Signal receiver (with responsibilities for Security Request watchers) -- "s.<event>": Signal receiver (with responsibilities for Security Request watchers)
-- "s.k.<...>": Kernel stuff -- "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.procdie" : Process dead (pkg, pid, reason, usage)
-- "s.k.registration" : Registration of service alert ("x." .. etc) -- "s.k.registration" : Registration of service alert ("x." .. etc)
-- "s.k.deregistration" : 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
end end
function start(pkg, ...) function start(pkg, ppkg, ppid, ...)
local proc = {} local proc = {}
local pid = lastPID local pid = lastPID
lastPID = lastPID + 1 lastPID = lastPID + 1
@ -571,7 +577,7 @@ function start(pkg, ...)
env.neo.scheduleTimer = function (time) env.neo.scheduleTimer = function (time)
ensureType(time, "number") ensureType(time, "number")
local tag = {} 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 return tag
end end
@ -587,9 +593,9 @@ function start(pkg, ...)
proc.deathCBs = {function () pcall(function () env.neo.dead = true end) end} proc.deathCBs = {function () pcall(function () env.neo.dead = true end) end}
proc.cpuUsage = 0 proc.cpuUsage = 0
-- Note the target process doesn't get the procnew (the dist occurs before it's creation) -- 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 processes[pid] = proc
table.insert(timers, {0, execEvent, pid, ...}) table.insert(timers, {0, execEvent, pid, ppkg, ppid, ...})
return pid return pid
end end

View File

@ -113,7 +113,7 @@ getFsNode = function (fs, parent, fsc, path, mode)
end, end,
unknownAvailable = mode ~= nil, unknownAvailable = mode ~= nil,
selectUnknown = function (text) 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 if not rt then
return false, dialog("Open Error: " .. tostring(re), parent) return false, dialog("Open Error: " .. tostring(re), parent)
end end
@ -130,8 +130,14 @@ getFsNode = function (fs, parent, fsc, path, mode)
return nil, parent return nil, parent
end}) end})
if mode ~= nil then if mode ~= nil then
table.insert(n, {"Open", function () local tx = "Open"
local rt, re = require("sys-filewrap")(fsc, path, mode) 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 if not rt then
return false, dialog("Open Error: " .. tostring(re), parent) return false, dialog("Open Error: " .. tostring(re), parent)
end end
@ -139,7 +145,7 @@ getFsNode = function (fs, parent, fsc, path, mode)
end}) end})
end end
table.insert(n, {"Copy", function () 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 if not rt then
return false, dialog("Open Error: " .. tostring(re), parent) return false, dialog("Open Error: " .. tostring(re), parent)
end end

View File

@ -1,9 +1,25 @@
-- This is released into the public domain. -- This is released into the public domain.
-- No warranty is provided, implied or otherwise. -- No warranty is provided, implied or otherwise.
return function(dev, file, mode) local function ensureMode(mode)
local n = "rb" 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) local handle, r = dev.open(file, n)
if not handle then return nil, r end if not handle then return nil, r end
local open = true local open = true
@ -14,15 +30,8 @@ return function(dev, file, mode)
dev.close(handle) dev.close(handle)
end) end)
end end
local function seeker(whence, point) local function reader(len)
if not open then return end 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 if len == "*a" then
local ch = "" local ch = ""
local c = dev.read(handle, neo.readBufSize) 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 if type(len) ~= "number" then error("Length of read must be number or '*a'") end
return dev.read(handle, len) return dev.read(handle, len)
end 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 }, closer
else else
return { return {
close = closer, close = closer,
seek = seeker, seek = seeker,
write = function (txt) read = reader,
if type(txt) ~= "string" then error("Write data must be string-bytearray") end write = writer
local ok, b = dev.write(handle, txt)
if not ok then error(tostring(b)) end
end
}, closer }, closer
end end
end end
return {
createMode = createMode,
create = create
}

View File

@ -36,6 +36,7 @@ return {
"docs/us-nxapp", "docs/us-nxapp",
"docs/us-setti", "docs/us-setti",
"docs/ul-seria", "docs/ul-seria",
"docs/ul-fwrap",
"docs/ul-event", "docs/ul-event",
"docs/ul-fmttx", "docs/ul-fmttx",
"docs/ul-neoux", "docs/ul-neoux",

View File

@ -57,7 +57,7 @@ The following are just wrapMeta'd
host libraries (*: altered): host libraries (*: altered):
math, table, string, unicode*, math, table, string, unicode*,
coroutine, os*, debug coroutine, os*, debug, utf8, bit32
unicode is extended with: unicode is extended with:
safeTextFormat(s, p): safeTextFormat(s, p):
@ -92,14 +92,11 @@ Programs that thus try to work around
as-needed if and when the issue is as-needed if and when the issue is
resolved. resolved.
os is replaced with (wrapMeta'd): os is extended with:
totalMemory = computer.totalMemory, totalMemory = computer.totalMemory,
freeMemory = computer.freeMemory, freeMemory = computer.freeMemory,
energy = computer.energy, energy = computer.energy,
maxEnergy = computer.maxEnergy, maxEnergy = computer.maxEnergy,
clock = os.clock, date = os.date,
difftime = os.difftime,
time = os.time,
uptime = computer.uptime, uptime = computer.uptime,
address = computer.address address = computer.address
@ -317,6 +314,38 @@ The additional things available to
and timers stick around after the and timers stick around after the
process that owns them is dead. 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 With that, I hope I have documented
the kernel's interface to programs. the kernel's interface to programs.

49
repository/docs/ul-fwrap Normal file
View 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.

View File

@ -18,6 +18,16 @@ For programs with the prefixes "svc-"
pattern "/[a-z0-9/%.]*$", or none pattern "/[a-z0-9/%.]*$", or none
at all. 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, For how this registration works,
and how to access the resulting and how to access the resulting
service, please see the kn-perms service, please see the kn-perms
@ -33,31 +43,149 @@ APIs registered in this manner are
mechanism if they so wish, and this mechanism if they so wish, and this
will cause a silent failure of the will cause a silent failure of the
lockPerm function.) 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... As for the system APIs...
-- x.neo.pub.base @ sys-icecap -- -- 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> -- -- 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 -- -- x.neo.sys.manage @ sys-glacier --
This API is how settings and ST are
managed
-- x.neo.sys.screens @ sys-glacier -- -- x.neo.sys.screens @ sys-glacier --
This API is how screens are managed
-- x.neo.pub.globals @ sys-glacier -- -- x.neo.pub.globals @ sys-glacier --
This API is some public global stuff
and spooky screen control???
-- This is released into -- This is released into
the public domain. the public domain.
-- No warranty is provided, -- No warranty is provided,