From 7bde8fee55d08ee5ca35f0031442ab0df336e681 Mon Sep 17 00:00:00 2001 From: 20kdc Date: Fri, 30 Mar 2018 12:36:48 +0100 Subject: [PATCH] Finish lowering memory use, R1 Since this is after the technical "release", version numbers have been bumped to 1. Changes before this commit for R1: Kernel memory usage reduction schemes, with some security fixes. Still need to deal w/ proxies (see later) Changes in this commit: Some various little things in apps CLAW inet actually works now on 192K sys-icecap no longer uses the event/neoux combination, and now handles Everest disappearance as a mass-close, but still handles Everest not being around on window create. So it still handles every situation that matters. neoux no longer handles everest crash protection. Security policy and filedialog obviously don't use neoux anymore. Kernel now only guarantees parsing, not event-loop, by executeAsync This is safer and allows app-launcher to get rid of NeoUX by any means necessary. wrapMeta cache now exists, and proxies get wrapMeta'd to deal with various low-priority security shenanigans. This is a *stopgap* until I work out how to force OCEmu to give me totally accurate boot-time memory figures, so I can create the ultimate lowmem proxy. I'm calling it "puppet". FG knows why. --- .gitignore | 8 ++ clawmerge.lua | 17 ++++ code/apps/app-claw.lua | 14 ++- code/apps/app-taskmgr.lua | 4 +- code/apps/sys-icecap.lua | 133 ++++++++++++++++++++++----- code/data/app-claw/local.lua | 14 +-- code/init.lua | 22 +++-- code/libs/fmttext.lua | 67 ++++++++++++++ code/libs/neoux.lua | 170 +++++------------------------------ code/libs/sys-filedialog.lua | 32 +++++-- code/libs/sys-secpolicy.lua | 101 ++++++++++++++++----- package.sh | 8 ++ 12 files changed, 361 insertions(+), 229 deletions(-) create mode 100644 clawmerge.lua create mode 100644 code/libs/fmttext.lua diff --git a/.gitignore b/.gitignore index 6fff533..2dc72df 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,14 @@ work/*/ work/*/* work/*/*/ work/*/*/* +repobuild +repobuild/ +repobuild/* +repobuild/*/ +repobuild/*/* +repobuild/*/*/ +repobuild/*/*/* + inst.lua com2/code.tar.bd upldr.sh diff --git a/clawmerge.lua b/clawmerge.lua new file mode 100644 index 0000000..30b3214 --- /dev/null +++ b/clawmerge.lua @@ -0,0 +1,17 @@ +local merges = {...} +neo = { + wrapMeta = function (x) + return x + end +} +local serial = loadfile("code/libs/serial.lua")() +local repo = {} +for _, v in ipairs(merges) do + local f = io.open(v, "rb") + local fd = f:read("*a") + f:close() + for k, v in pairs(serial.deserialize(fd)) do + repo[k] = v + end +end +io.write(serial.serialize(repo)) diff --git a/code/apps/app-claw.lua b/code/apps/app-claw.lua index 22d6ccc..5eea8f4 100644 --- a/code/apps/app-claw.lua +++ b/code/apps/app-claw.lua @@ -20,7 +20,7 @@ if primaryINet then primaryINet = primaryINet.list()() end local function yielder() -- slightly dangerous, but what can we do? - event.sleepTo(os.uptime() + 0.05) + pcall(event.sleepTo, os.uptime() + 0.05) end local function download(url, cb) @@ -28,16 +28,12 @@ local function download(url, cb) local req, err = primaryINet.request(source .. url) if not req then cb(nil) - return nil, tostring(err) - end - local ok, err = req.finishConnect() - if not req.finishConnect() then - req.close() - cb(nil) - return nil, tostring(err) + return nil, "dlR/" .. tostring(err) end + -- OpenComputers#535 + req.finishConnect() while true do - local n, n2 = req.read() + local n, n2 = req.read(neo.readBufSize) local o, r = cb(n) if not o then req.close() diff --git a/code/apps/app-taskmgr.lua b/code/apps/app-taskmgr.lua index 58d680a..a6daa93 100644 --- a/code/apps/app-taskmgr.lua +++ b/code/apps/app-taskmgr.lua @@ -4,8 +4,7 @@ -- app-taskmgr: Task manager -- a-hello : simple test program for Everest. -local everest = neo.requestAccess("x.neo.pub.window") -if not everest then error("no everest") return end +local everest = neo.requireAccess("x.neo.pub.window", "main window") local kill = neo.requestAccess("k.kill") @@ -13,7 +12,6 @@ local sW, sH = 20, 8 local headlines = 2 local window = everest(sW, sH) -if not window then error("no window") end local lastIdleTimeTime = os.uptime() local lastIdleTime = neo.totalIdleTime() diff --git a/code/apps/sys-icecap.lua b/code/apps/sys-icecap.lua index 270e8e2..a5f6ed4 100644 --- a/code/apps/sys-icecap.lua +++ b/code/apps/sys-icecap.lua @@ -4,11 +4,6 @@ -- s-icecap : Responsible for x.neo.pub API, crash dialogs, and security policy that isn't "sys- has ALL access, anything else has none" -- In general, this is what userspace will be interacting with in some way or another to get stuff done -local event = require("event")(neo) -local neoux, err = require("neoux") -if not neoux then error(err) end -- This app is basically neoux's testcase -neoux = neoux(event, neo) - local settings = neo.requireAccess("x.neo.sys.manage", "security sysconf access") local fs = neo.requireAccess("c.filesystem", "file managers") @@ -17,6 +12,72 @@ local donkonitDFProvider = neo.requireAccess("r.neo.pub.base", "creating basic N local targsDH = {} -- data disposal +local todo = {} + +local onEverest = {} +local everestWindows = {} + +local nexus + +local function resumeWF(...) + local ok, e = coroutine.resume(...) + if not ok then + e = tostring(e) + neo.emergency(e) + nexus.startDialog(e, "ice") + end + return ok +end + +nexus = { + createNexusThread = function (f, ...) + local t = coroutine.create(f) + if not resumeWF(t, ...) then return end + local early = neo.requestAccess("x.neo.pub.window") + if early then + onEverest[#onEverest] = nil + resumeWF(t, early) + end + return function () + for k, v in ipairs(onEverest) do + if v == t then + table.remove(onEverest, k) + return + end + end + end + end, + create = function (w, h, t) + local thr = coroutine.running() + table.insert(onEverest, thr) + local everest = coroutine.yield() + local dw = everest(w, h, title) + everestWindows[dw.id] = thr + return dw + end, + startDialog = function (tx, ti) + local fmt = require("fmttext") + local txl = fmt.fmtText(unicode.safeTextFormat(tx), 40) + fmt = nil + nexus.createNexusThread(function () + local w = nexus.create(40, #txl, ti) + while true do + local ev, a = coroutine.yield() + if ev == "line" then + w.span(1, a, txl[a], 0xFFFFFF, 0) + elseif ev == "close" then + w.close() + return + end + end + end) + end, + close = function (wnd) + wnd.close() + everestWindows[wnd.id] = nil + end +} + donkonitDFProvider(function (pkg, pid, sendSig) local prefixNS = "data/" .. pkg local prefixWS = "data/" .. pkg .. "/" @@ -32,9 +93,10 @@ donkonitDFProvider(function (pkg, pid, sendSig) -- Not hooked into the event API, so can't safely interfere -- Thus, this is async and uses a return event. local tag = {} - event.runAt(0, function () + neo.scheduleTimer(0) + table.insert(todo, function () -- sys-filedialog is yet another "library to control memory usage". - local closer = require("sys-filedialog")(event, neoux, function (res) openHandles[tag] = nil sendSig("filedialog", tag, res) end, fs, pkg, forWrite) + local closer = require("sys-filedialog")(event, nexus, function (res) openHandles[tag] = nil sendSig("filedialog", tag, res) end, fs, pkg, forWrite) openHandles[tag] = closer end) return tag @@ -122,9 +184,10 @@ rootAccess.securityPolicy = function (pid, proc, perm, req) req(def) return end - -- Push to ICECAP thread to avoid deadlock on neoux b/c wrong event-pull context - event.runAt(0, function () - local ok, err = pcall(secpol, neoux, settings, proc.pkg, pid, perm, req) + -- Push to ICECAP thread to avoid deadlock b/c wrong event-pull context + neo.scheduleTimer(0) + table.insert(todo, function () + local ok, err = pcall(secpol, nexus, settings, proc.pkg, pid, perm, req) if not ok then neo.emergency("Used fallback policy because of run-err: " .. err) req(def) @@ -132,19 +195,41 @@ rootAccess.securityPolicy = function (pid, proc, perm, req) end) end -event.listen("k.procdie", function (evt, pkg, pid, reason) - if targsDH[pid] then - targsDH[pid]() - end - targsDH[pid] = nil - if reason then - -- Process death logging in console (for lifecycle dbg) - -- neo.emergency(n[2]) - -- neo.emergency(n[4]) - neoux.startDialog(string.format("%s/%i died:\n%s", pkg, pid, reason), "error") - end -end) - while true do - event.pull() + local ev = {coroutine.yield()} + if ev[1] == "k.procdie" then + local _, pkg, pid, reason = table.unpack(ev) + if targsDH[pid] then + targsDH[pid]() + end + targsDH[pid] = nil + if reason then + nexus.startDialog(string.format("%s/%i died:\n%s", pkg, pid, reason), "error") + end + elseif ev[1] == "k.timer" then + local nt = todo + todo = {} + for _, v in ipairs(nt) do + local ok, e = pcall(v) + if not ok then + nexus.startDialog(tostring(e), "terr") + end + end + elseif ev[1] == "k.registration" then + if ev[2] == "x.neo.pub.window" then + local nt = onEverest + onEverest = {} + for _, v in ipairs(nt) do + coroutine.resume(v, neo.requestAccess("x.neo.pub.window")) + end + end + elseif ev[1] == "x.neo.pub.window" then + local v = everestWindows[ev[2]] + if v then + resumeWF(v, table.unpack(ev, 3)) + if coroutine.status(v) == "dead" then + everestWindows[ev[2]] = nil + end + end + end end diff --git a/code/data/app-claw/local.lua b/code/data/app-claw/local.lua index 0a7abeb..a40a7dc 100644 --- a/code/data/app-claw/local.lua +++ b/code/data/app-claw/local.lua @@ -1,7 +1,7 @@ return { ["neo"] = { desc = "KittenOS NEO Kernel & Base Libs", - v = 0, + v = 1, deps = { }, dirs = { @@ -14,6 +14,7 @@ return { "apps/sys-glacier.lua", "libs/event.lua", "libs/serial.lua", + "libs/fmttext.lua", "libs/neoux.lua", "libs/braille.lua", "libs/sys-filewrap.lua" @@ -62,7 +63,7 @@ return { }, ["neo-icecap"] = { desc = "KittenOS NEO / Icecap", - v = 0, + v = 1, deps = { "neo" }, @@ -79,20 +80,19 @@ return { }, ["neo-secpolicy"] = { desc = "KittenOS NEO / Secpolicy", - v = 0, + v = 1, deps = { }, dirs = { "libs" }, files = { - "libs/sys-secpolicy.lua", - "libs/sys-criticals.lua" + "libs/sys-secpolicy.lua" } }, ["neo-coreapps"] = { desc = "KittenOS NEO Core Apps", - v = 0, + v = 1, deps = { "neo" }, @@ -136,7 +136,7 @@ return { }, ["app-claw"] = { desc = "KittenOS NEO Package Manager", - v = 0, + v = 1, deps = { "neo" }, diff --git a/code/init.lua b/code/init.lua index 0ea16e5..790f4ad 100644 --- a/code/init.lua +++ b/code/init.lua @@ -101,12 +101,23 @@ end uniqueNEOProtectionObject = {} +wrapMetaCache = {} +setmetatable(wrapMetaCache, {__mode = "v"}) + function wrapMeta(t) if type(t) == "table" then + if wrapMetaCache[t] then + return wrapMetaCache[t] + end local t2 = {} + wrapMetaCache[t] = t2 setmetatable(t2, { __index = function (a, k) return wrapMeta(t[k]) end, __newindex = error, + -- WTF + __call = function (_, ...) + return t(...) + end, __pairs = function (a) return function (x, key) local k, v = next(t, k) @@ -392,8 +403,8 @@ function retrieveAccess(perm, pkg, pid) local temporary = nil local t = perm:sub(3) if t == "filesystem" then - primary = primaryDisk - temporary = component.proxy(computer.tmpAddress()) + primary = wrapMeta(primaryDisk) + temporary = wrapMeta(component.proxy(computer.tmpAddress())) end return { list = function () @@ -401,7 +412,7 @@ function retrieveAccess(perm, pkg, pid) return function () local ii = i() if not ii then return nil end - return component.proxy(ii) + return wrapMeta(component.proxy(ii)) end end, primary = primary, @@ -556,10 +567,7 @@ function start(pkg, ...) -- Note the target process doesn't get the procnew (the dist occurs before it's creation) pcall(distEvent, nil, "k.procnew", pkg, pid) processes[pid] = proc - -- For processes waiting on others, this at least tries to guarantee some safety. - if not pcall(execEvent, pid, ...) then - return nil, "neocore" - end + table.insert(timers, {0, execEvent, pid, ...}) return pid end diff --git a/code/libs/fmttext.lua b/code/libs/fmttext.lua new file mode 100644 index 0000000..3a5d8cf --- /dev/null +++ b/code/libs/fmttext.lua @@ -0,0 +1,67 @@ +local fmt +fmt = { + pad = function (t, len, centre, cut) + local l = unicode.len(t) + local add = len - l + if add > 0 then + if centre then + t = (" "):rep(math.floor(add / 2)) .. t .. (" "):rep(math.ceil(add / 2)) + else + t = t .. (" "):rep(add) + end + end + if cut then + t = unicode.sub(t, 1, len) + end + return t + end, + fmtText = function (text, w) + local nl = text:find("\n") + if nl then + local base = text:sub(1, nl - 1) + local ext = text:sub(nl + 1) + local baseT = fmt.fmtText(base, w) + local extT = fmt.fmtText(ext, w) + for _, v in ipairs(extT) do + table.insert(baseT, v) + end + return baseT + end + if unicode.len(text) > w then + local lastSpace + for i = 1, w do + if unicode.sub(text, i, i) == " " then + -- Check this isn't an inserted space (unicode safe text format) + local ok = true + if i > 1 then + if unicode.charWidth(unicode.sub(text, i - 1, i - 1)) ~= 1 then + ok = false + end + end + if ok then + lastSpace = i + end + end + end + local baseText, extText + if not lastSpace then + -- Break at a 1-earlier boundary + local wEffect = w + if unicode.charWidth(unicode.sub(text, w, w)) ~= 1 then + -- Guaranteed to be safe, so + wEffect = wEffect - 1 + end + baseText = unicode.sub(text, 1, wEffect) + extText = unicode.sub(text, wEffect + 1) + else + baseText = unicode.sub(text, 1, lastSpace - 1) + extText = unicode.sub(text, lastSpace + 1) + end + local full = fmt.fmtText(extText, w) + table.insert(full, 1, fmt.pad(baseText, w)) + return full + end + return {fmt.pad(text, w)} + end +} +return neo.wrapMeta(fmt) diff --git a/code/libs/neoux.lua b/code/libs/neoux.lua index 3d52af8..00e2b10 100644 --- a/code/libs/neoux.lua +++ b/code/libs/neoux.lua @@ -16,57 +16,12 @@ -- Global forces reference. Otherwise, nasty duplication happens. newNeoux = function (event, neo) - -- this is why neo access is 'needed' - local function retrieveIcecap() - return neo.requestAccess("x.neo.pub.base") - end - local function retrieveEverest() - return neo.requestAccess("x.neo.pub.window") - end - -- id -> {lclEv, w, h, title, callback} - local windows = {} + -- id -> callback local lclEvToW = {} - retrieveEverest() - local function everestDied() - for _, v in pairs(windows) do - v[1] = nil - end - lclEvToW = {} - end - local function pushWindowToEverest(k) - local everest = retrieveEverest() - if not everest then - everestDied() - return - end - local v = windows[k] - local r, res = pcall(everest, v[2], v[3], v[4]) - if not r then - everestDied() - return - else - -- res is the window! - lclEvToW[res.id] = k - windows[k][1] = res - end - end - event.listen("k.registration", function (_, xe) - if #windows > 0 then - if xe == "x.neo.pub.window" then - for k, v in pairs(windows) do - pushWindowToEverest(k) - end - end - end - end) - event.listen("k.deregistration", function (_, xe) - if xe == "x.neo.pub.window" then - everestDied() - end - end) + local everest = neo.requireAccess("x.neo.pub.window", "windowing") event.listen("x.neo.pub.window", function (_, window, tp, ...) if lclEvToW[window] then - windows[lclEvToW[window]][5](tp, ...) + lclEvToW[window](tp, ...) end end) local neoux = {} @@ -80,7 +35,7 @@ newNeoux = function (event, neo) rtt = rt end end - local tag = retrieveIcecap().showFileDialogAsync(forWrite) + local tag = neo.requestAccess("x.neo.pub.base").showFileDialogAsync(forWrite) local f f = function (_, fd, tg, re) if fd == "filedialog" then @@ -99,117 +54,40 @@ newNeoux = function (event, neo) -- Creates a wrapper around a window. neoux.create = function (w, h, title, callback) local window = {} - local windowCore = {nil, w, h, title, function (...) callback(window, ...) end} - local k = #windows + 1 - table.insert(windows, windowCore) - pushWindowToEverest(k) + local windowCore = everest(w, h, title) + -- res is the window! + lclEvToW[windowCore.id] = function (...) callback(window, ...) end -- API convenience: args compatible with .create window.reset = function (nw, nh, _, cb) callback = cb - if nw or nh then - windowCore[2] = nw - windowCore[3] = nh - end - if windowCore[1] then - windowCore[1].setSize(windowCore[2], windowCore[3]) - end + w = nw or w + h = nh or h + windowCore.setSize(w, h) end window.getSize = function () - return windowCore[2], windowCore[3] + return w, h end - window.getDepth = function () - if windowCore[1] then - return windowCore[1].getDepth() - end - return 1 - end - window.setSize = function (w, h) - windowCore[2] = w - windowCore[3] = h - if windowCore[1] then - windowCore[1].setSize(w, h) - end - end - window.span = function (x, y, text, bg, fg) - if windowCore[1] then - pcall(windowCore[1].span, x, y, text, bg, fg) - end + window.getDepth = windowCore.getDepth + window.setSize = function (nw, nh) + w = nw + h = nh + windowCore.setSize(w, h) end + window.span = windowCore.span window.close = function () - if windowCore[1] then - windowCore[1].close() - lclEvToW[windowCore[1].id] = nil - windowCore[1] = nil - end - windows[k] = nil + windowCore.close() + lclEvToW[windowCore.id] = nil + windowCore = nil end return window end -- Padding function - neoux.pad = function (t, len, centre, cut) - local l = unicode.len(t) - local add = len - l - if add > 0 then - if centre then - t = (" "):rep(math.floor(add / 2)) .. t .. (" "):rep(math.ceil(add / 2)) - else - t = t .. (" "):rep(add) - end - end - if cut then - t = unicode.sub(t, 1, len) - end - return t - end + neoux.pad = require("fmttext").pad -- Text dialog formatting function. -- Assumes you've run unicode.safeTextFormat if need be - neoux.fmtText = function (text, w) - local nl = text:find("\n") - if nl then - local base = text:sub(1, nl - 1) - local ext = text:sub(nl + 1) - local baseT = neoux.fmtText(base, w) - local extT = neoux.fmtText(ext, w) - for _, v in ipairs(extT) do - table.insert(baseT, v) - end - return baseT - end - if unicode.len(text) > w then - local lastSpace - for i = 1, w do - if unicode.sub(text, i, i) == " " then - -- Check this isn't an inserted space (unicode safe text format) - local ok = true - if i > 1 then - if unicode.charWidth(unicode.sub(text, i - 1, i - 1)) ~= 1 then - ok = false - end - end - if ok then - lastSpace = i - end - end - end - local baseText, extText - if not lastSpace then - -- Break at a 1-earlier boundary - local wEffect = w - if unicode.charWidth(unicode.sub(text, w, w)) ~= 1 then - -- Guaranteed to be safe, so - wEffect = wEffect - 1 - end - baseText = unicode.sub(text, 1, wEffect) - extText = unicode.sub(text, wEffect + 1) - else - baseText = unicode.sub(text, 1, lastSpace - 1) - extText = unicode.sub(text, lastSpace + 1) - end - local full = neoux.fmtText(extText, w) - table.insert(full, 1, neoux.pad(baseText, w)) - return full - end - return {neoux.pad(text, w)} + neoux.fmtText = function (...) + local fmt = require("fmttext") + return fmt.fmtText(...) end -- UI FRAMEWORK -- neoux.tcwindow = function (w, h, controls, closing, bg, fg, selIndex) diff --git a/code/libs/sys-filedialog.lua b/code/libs/sys-filedialog.lua index ae4c854..68a3f38 100644 --- a/code/libs/sys-filedialog.lua +++ b/code/libs/sys-filedialog.lua @@ -2,8 +2,8 @@ -- No warranty is provided, implied or otherwise. -- just don't bother with proper indent here -return function (event, neoux, retFunc, fs, pkg, mode) - +return function (event, nexus, retFunc, fs, pkg, mode) +local fmt = require("fmttext") local class = "manage" if mode ~= nil then if mode then @@ -23,7 +23,7 @@ local function cb(...) name = "F.M. Error", list = function () local l = {} - for k, v in ipairs(neoux.fmtText(unicode.safeTextFormat(e), 25)) do + for k, v in ipairs(fmt.fmtText(unicode.safeTextFormat(e), 25)) do l[k] = {v, function () return true end} end return l @@ -64,7 +64,7 @@ local function prepareNodeI(node) if sel then colB, colA = 0xFFFFFF, 0 end - wnd.span(1, a, neoux.pad(unicode.safeTextFormat(text), w, cen, true), colA, colB) + wnd.span(1, a, fmt.pad(unicode.safeTextFormat(text), w, cen, true), colA, colB) end local function flush(wnd) for i = 1, h do @@ -108,7 +108,7 @@ local function prepareNodeI(node) end if aResult then retFunc(res) - wnd.close() + nexus.close(wnd) else prepareNode(res) end @@ -148,13 +148,13 @@ local function prepareNodeI(node) end if evt == "close" then retFunc(nil) - wnd.close() + nexus.close(wnd) end end end local text = class .. " " .. pkg -local window = neoux.create(25, 10, text, cb) +local window function prepareNode(node) local w, h, c = prepareNodeI(node) @@ -162,10 +162,24 @@ function prepareNode(node) window.setSize(w, h) end -prepareNode(require("sys-filevfs")(fs, mode)) +local closer = nexus.createNexusThread(function () + window = nexus.create(25, 10, text) + prepareNode(require("sys-filevfs")(fs, mode)) + while window do + cb(window, coroutine.yield()) + end +end) +if not closer then + retFunc() + return +end return function () retFunc() - window.close() + closer() + if window then + nexus.close(window) + window = nil + end end -- end bad indent diff --git a/code/libs/sys-secpolicy.lua b/code/libs/sys-secpolicy.lua index 45f9254..a54c9fd 100644 --- a/code/libs/sys-secpolicy.lua +++ b/code/libs/sys-secpolicy.lua @@ -51,39 +51,92 @@ local actualPolicy = function (pkg, pid, perm) return "ask" end -return function (neoux, settings, pkg, pid, perm, rsp) +return function (nexus, settings, pkg, pid, perm, rsp) local res = actualPolicy(pkg, pid, perm) if res == "ask" and settings then res = settings.getSetting("perm|" .. pkg .. "|" .. perm) or "ask" end - if res == "ask" and neoux then - local fmt = neoux.fmtText(unicode.safeTextFormat(string.format("%s/%i wants:\n%s\nAllow this?", pkg, pid, perm)), 20) - local always = "Always" - local yes = "Yes" - local no = "No" - local totalW = (#yes) + (#always) + (#no) + 8 - neoux.create(20, #fmt + 2, "security", neoux.tcwindow(20, #fmt + 3, { - neoux.tcbutton(1, #fmt + 2, no, function (w) + if res == "ask" and nexus then + local totalW = 3 + 6 + 2 + 8 + local fmt = require("fmttext").fmtText(unicode.safeTextFormat(string.format("%s/%i wants:\n%s\nAllow this?\n\n", pkg, pid, perm)), totalW) + local buttons = { + {"", function (w) rsp(false) - w.close() - end), - neoux.tcbutton(totalW - ((#yes) + 1), #fmt + 2, yes, function (w) - rsp(true) - w.close() - end), - neoux.tcbutton((#yes) + 3, #fmt + 2, always, function (w) + nexus.close(w) + end}, + {"", function (w) if settings then settings.setSetting("perm|" .. pkg .. "|" .. perm, "allow") end rsp(true) - w.close() - end), - neoux.tchdivider(1, #fmt + 1, 21), - neoux.tcrawview(1, 1, fmt), - }, function (w) - rsp(false) - w.close() - end, 0xFFFFFF, 0)) + nexus.close(w) + end}, + {"", function (w) + rsp(true) + nexus.close(w) + end} + } + nexus.createNexusThread(function () + local window = nexus.create(totalW, #fmt, "security") + local cButton = 0 + local ev, a, b, c + while true do + if not ev then + ev, a, b, c = coroutine.yield() + end + if ev == "line" or ev == "touch" then + local cor = b + if ev == "line" then + cor = a + if fmt[a] then + window.span(1, a, fmt[a], 0xFFFFFF, 0) + end + end + if cor == #fmt then + local x = 1 + for k, v in ipairs(buttons) do + if ev == "line" then + if k ~= cButton + 1 then + window.span(x, a, v[1], 0xFFFFFF, 0) + else + window.span(x, a, v[1], 0, 0xFFFFFF) + end + elseif a >= x and a < (x + #v[1]) then + cButton = k - 1 + ev = "key" + a = 32 + b = 0 + c = true + break + end + x = x + #v[1] + 1 + end + end + elseif ev == "close" then + rsp(false) + nexus.close(window) + return + end + if ev == "key" then + if c and (a == 9 or b == 205) then + cButton = (cButton + 1) % #buttons + ev = "line" + a = #fmt + elseif c and b == 203 then + cButton = (cButton - 1) % #buttons + ev = "line" + a = #fmt + elseif c and (a == 13 or a == 32) then + buttons[cButton + 1][2](window) + ev = nil + else + ev = nil + end + else + ev = nil + end + end + end) else rsp(res == "allow") end diff --git a/package.sh b/package.sh index 90412c7..e2ab61f 100755 --- a/package.sh +++ b/package.sh @@ -7,3 +7,11 @@ rm code.tar # Hey, look behind you, there's nothing to see here. # ... ok, are they seriously all named "Mann"? tar --owner=gray:0 --group=mann:0 -cf code.tar code +lua heroes.lua `wc -c code.tar` > inst.lua + +stat repobuild/data/app-claw/local.lua && rm -rf repobuild +mkdir repobuild +cp -r code/* repobuild/ +cp -r repository/* repobuild/ +cp inst.lua repobuild/ +lua clawmerge.lua repository/data/app-claw/local.lua code/data/app-claw/local.lua > repobuild/data/app-claw/local.lua