From a5372eafe16702013099e1ea83a24e14906e2786 Mon Sep 17 00:00:00 2001 From: 20kdc Date: Thu, 12 Apr 2018 00:04:16 +0100 Subject: [PATCH] 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. --- KTC1.md | 52 ++++++++++ code/apps/sys-everest.lua | 65 ++++++++----- code/apps/sys-glacier.lua | 13 ++- code/apps/sys-icecap.lua | 35 ++++--- code/apps/sys-init.lua | 62 ++++++------ code/init.lua | 28 +++--- code/libs/sys-filevfs.lua | 14 ++- code/libs/sys-filewrap.lua | 66 +++++++++---- repository/data/app-claw/local.lua | 1 + repository/docs/kn-refer | 39 +++++++- repository/docs/ul-fwrap | 49 ++++++++++ repository/docs/us-perms | 150 ++++++++++++++++++++++++++--- 12 files changed, 449 insertions(+), 125 deletions(-) create mode 100644 KTC1.md create mode 100644 repository/docs/ul-fwrap diff --git a/KTC1.md b/KTC1.md new file mode 100644 index 0000000..984c821 --- /dev/null +++ b/KTC1.md @@ -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. diff --git a/code/apps/sys-everest.lua b/code/apps/sys-everest.lua index 11a4134..7b74d92 100644 --- a/code/apps/sys-everest.lua +++ b/code/apps/sys-everest.lua @@ -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 + 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 diff --git a/code/apps/sys-glacier.lua b/code/apps/sys-glacier.lua index bf1a63b..5e2cd70 100644 --- a/code/apps/sys-glacier.lua +++ b/code/apps/sys-glacier.lua @@ -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? diff --git a/code/apps/sys-icecap.lua b/code/apps/sys-icecap.lua index 386521e..f3fb286 100644 --- a/code/apps/sys-icecap.lua +++ b/code/apps/sys-icecap.lua @@ -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 diff --git a/code/apps/sys-init.lua b/code/apps/sys-init.lua index c99cf8c..396c148 100644 --- a/code/apps/sys-init.lua +++ b/code/apps/sys-init.lua @@ -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}) - gcb.setBackground(0x000020) - local w, h = gcb.getResolution() - gcb.fill(1, 1, w, h, " ") + 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 diff --git a/code/init.lua b/code/init.lua index 578402c..8b75cf4 100644 --- a/code/init.lua +++ b/code/init.lua @@ -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.": Component -- "s.": 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 diff --git a/code/libs/sys-filevfs.lua b/code/libs/sys-filevfs.lua index 4dea226..ca4b032 100644 --- a/code/libs/sys-filevfs.lua +++ b/code/libs/sys-filevfs.lua @@ -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 diff --git a/code/libs/sys-filewrap.lua b/code/libs/sys-filewrap.lua index d3c726e..36c8d63 100644 --- a/code/libs/sys-filewrap.lua +++ b/code/libs/sys-filewrap.lua @@ -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,37 +30,45 @@ return function(dev, file, mode) dev.close(handle) end) end + local function reader(len) + if not open then return end + if len == "*a" then + local ch = "" + local c = dev.read(handle, neo.readBufSize) + while c do + ch = ch .. c + c = dev.read(handle, neo.readBufSize) + end + return ch + end + 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 not mode then + if mode == "rb" then return { close = closer, seek = seeker, - read = function (len) - if len == "*a" then - local ch = "" - local c = dev.read(handle, neo.readBufSize) - while c do - ch = ch .. c - c = dev.read(handle, neo.readBufSize) - end - return ch - end - if type(len) ~= "number" then error("Length of read must be number or '*a'") end - return dev.read(handle, len) - end + 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 +} diff --git a/repository/data/app-claw/local.lua b/repository/data/app-claw/local.lua index 72ba050..7541abf 100644 --- a/repository/data/app-claw/local.lua +++ b/repository/data/app-claw/local.lua @@ -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", diff --git a/repository/docs/kn-refer b/repository/docs/kn-refer index 33052f0..7421e8d 100644 --- a/repository/docs/kn-refer +++ b/repository/docs/kn-refer @@ -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. diff --git a/repository/docs/ul-fwrap b/repository/docs/ul-fwrap new file mode 100644 index 0000000..e411360 --- /dev/null +++ b/repository/docs/ul-fwrap @@ -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. diff --git a/repository/docs/us-perms b/repository/docs/us-perms index 86c0f2f..93e8a54 100644 --- a/repository/docs/us-perms +++ b/repository/docs/us-perms @@ -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 @ -- +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 @ -- + 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 @ -- +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,