From 9e4eb1207e3338c5b502befc0f173f61fa12ab6f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 23 Apr 2019 14:48:39 -0400 Subject: [PATCH 001/236] better shell launcher --- sys/apps/ShellLauncher.lua | 27 +++++++++++++++++++++++++++ sys/apps/system/launcher.lua | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 sys/apps/ShellLauncher.lua diff --git a/sys/apps/ShellLauncher.lua b/sys/apps/ShellLauncher.lua new file mode 100644 index 0000000..9407e46 --- /dev/null +++ b/sys/apps/ShellLauncher.lua @@ -0,0 +1,27 @@ +local kernel = _G.kernel +local os = _G.os +local shell = _ENV.shell + +local launcherTab = kernel.getCurrent() + +kernel.hook('kernel_focus', function(_, eventData) + local focusTab = eventData and eventData[1] + if focusTab == launcherTab.uid then + local previousTab = eventData[2] + local nextTab = launcherTab + if not previousTab then + for _, v in pairs(kernel.routines) do + if not v.hidden and v.uid > nextTab.uid then + nextTab = v + end + end + end + if nextTab == launcherTab then + shell.switchTab(shell.openTab('sys/apps/shell.lua')) + else + shell.switchTab(nextTab.uid) + end + end +end) + +while os.pullEventRaw() do end \ No newline at end of file diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index a2fb843..5108ad0 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -17,7 +17,7 @@ local tab = UI.Tab { x = 13, y = 2, width = 12, choices = { { name = 'Overview', value = 'sys/apps/Overview.lua' }, - { name = 'Shell', value = 'sys/apps/shell.lua' }, + { name = 'Shell', value = 'sys/apps/ShellLauncher.lua' }, { name = 'Custom', value = 'custom' }, }, }, -- 2.49.1 From 7317e1b73b1e56915dd7573c2982faccd8bd820a Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Tue, 23 Apr 2019 20:36:30 -0400 Subject: [PATCH 002/236] Migrate sha1 to sha2 --- sys/apis/sha2.lua | 195 +++++++++++++++++++++++++++++++++++ sys/apps/Overview.lua | 6 +- sys/apps/Welcome.lua | 4 +- sys/apps/password.lua | 4 +- sys/apps/system/password.lua | 6 +- sys/apps/trust.lua | 4 +- 6 files changed, 207 insertions(+), 12 deletions(-) create mode 100644 sys/apis/sha2.lua diff --git a/sys/apis/sha2.lua b/sys/apis/sha2.lua new file mode 100644 index 0000000..6c1660b --- /dev/null +++ b/sys/apis/sha2.lua @@ -0,0 +1,195 @@ +-- SHA-256, HMAC and PBKDF2 functions in ComputerCraft +-- By Anavrins + +local sha2 = {} + +local mod32 = 2^32 +local band = bit32 and bit32.band or bit.band +local bnot = bit32 and bit32.bnot or bit.bnot +local bxor = bit32 and bit32.bxor or bit.bxor +local blshift = bit32 and bit32.lshift or bit.blshift +local upack = unpack + +local function rrotate(n, b) + local s = n/(2^b) + local f = s%1 + return (s-f) + f*mod32 +end +local function brshift(int, by) + local s = int / (2^by) + return s - s%1 +end + +local H = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, +} + +local K = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +} + +local function counter(incr) + local t1, t2 = 0, 0 + if 0xFFFFFFFF - t1 < incr then + t2 = t2 + 1 + t1 = incr - (0xFFFFFFFF - t1) - 1 + else t1 = t1 + incr + end + return t2, t1 +end + +local function BE_toInt(bs, i) + return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0) +end + +local function preprocess(data) + local len = #data + local proc = {} + data[#data+1] = 0x80 + while #data%64~=56 do data[#data+1] = 0 end + local blocks = math.ceil(#data/64) + for i = 1, blocks do + proc[i] = {} + for j = 1, 16 do + proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4)) + end + end + proc[blocks][15], proc[blocks][16] = counter(len*8) + return proc +end + +local function digestblock(w, C) + for j = 17, 64 do + local v = w[j-15] + local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3)) + local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10)) + w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32 + end + local a, b, c, d, e, f, g, h = upack(C) + for j = 1, 64 do + local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25)) + local ch = bxor(band(e, f), band(bnot(e), g)) + local temp1 = (h + S1 + ch + K[j] + w[j])%mod32 + local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22)) + local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c)) + local temp2 = (S0 + maj)%mod32 + h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32 + end + C[1] = (C[1] + a)%mod32 + C[2] = (C[2] + b)%mod32 + C[3] = (C[3] + c)%mod32 + C[4] = (C[4] + d)%mod32 + C[5] = (C[5] + e)%mod32 + C[6] = (C[6] + f)%mod32 + C[7] = (C[7] + g)%mod32 + C[8] = (C[8] + h)%mod32 + return C +end + +local mt = { + __tostring = function(a) return string.char(unpack(a)) end, + __index = { + toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end, + isEqual = function(self, t) + if type(t) ~= "table" then return false end + if #self ~= #t then return false end + local ret = 0 + for i = 1, #self do + ret = bit32.bor(ret, bxor(self[i], t[i])) + end + return ret == 0 + end + } +} + +local function toBytes(t, n) + local b = {} + for i = 1, n do + b[(i-1)*4+1] = band(brshift(t[i], 24), 0xFF) + b[(i-1)*4+2] = band(brshift(t[i], 16), 0xFF) + b[(i-1)*4+3] = band(brshift(t[i], 8), 0xFF) + b[(i-1)*4+4] = band(t[i], 0xFF) + end + return setmetatable(b, mt) +end + +function sha2.digest(data) + local data = data or "" + data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} + + data = preprocess(data) + local C = {upack(H)} + for i = 1, #data do C = digestblock(data[i], C) end + return toBytes(C, 8) +end + +function sha2.hmac(data, key) + local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} + local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)} + + local blocksize = 64 + + key = #key > blocksize and digest(key) or key + + local ipad = {} + local opad = {} + local padded_key = {} + + for i = 1, blocksize do + ipad[i] = bxor(0x36, key[i] or 0) + opad[i] = bxor(0x5C, key[i] or 0) + end + + for i = 1, #data do + ipad[blocksize+i] = data[i] + end + + ipad = digest(ipad) + + for i = 1, blocksize do + padded_key[i] = opad[i] + padded_key[blocksize+i] = ipad[i] + end + + return digest(padded_key) +end + +function sha2.pbkdf2(pass, salt, iter, dklen) + local salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} + local hashlen = 32 + local dklen = dklen or 32 + local block = 1 + local out = {} + + while dklen > 0 do + local ikey = {} + local isalt = {upack(salt)} + local clen = dklen > hashlen and hashlen or dklen + + isalt[#isalt+1] = band(brshift(block, 24), 0xFF) + isalt[#isalt+1] = band(brshift(block, 16), 0xFF) + isalt[#isalt+1] = band(brshift(block, 8), 0xFF) + isalt[#isalt+1] = band(block, 0xFF) + + for j = 1, iter do + isalt = hmac(isalt, pass) + for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end + if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end + end + dklen = dklen - clen + block = block+1 + for k = 1, clen do out[#out+1] = ikey[k] end + end + + return setmetatable(out, mt) +end + +return sha2 \ No newline at end of file diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 1c0fe01..4f6de88 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -3,7 +3,7 @@ local Config = require('config') local Event = require('event') local NFT = require('nft') local Packages = require('packages') -local SHA1 = require('sha1') +local SHA2 = require('sha2') local Tween = require('ui.tween') local UI = require('ui') local Util = require('util') @@ -502,7 +502,7 @@ end function page.editor:updateApplications(app) if not app.key then - app.key = SHA1.sha1(app.title) + app.key = SHA2.digest(app.title):toHex() end local filename = app.filename or fs.combine(REGISTRY_DIR, app.key) Util.writeTable(filename, app) @@ -571,7 +571,7 @@ end Event.on('overview_shortcut', function(_, app) if not app.key then - app.key = SHA1.sha1(app.title) + app.key = SHA2.digest(app.title):toHex() end local filename = app.filename or fs.combine(REGISTRY_DIR, app.key) if not fs.exists(filename) then diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index e735840..631c248 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -1,7 +1,7 @@ local Ansi = require('ansi') local Config = require('config') local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('sha2') local UI = require('ui') local colors = _G.colors @@ -108,7 +108,7 @@ end function page.wizard.pages.password:validate() if #self.newPass.value > 0 then - Security.updatePassword(SHA1.sha1(self.newPass.value)) + Security.updatePassword(SHA2.digest(self.newPass.value):toHex()) end --[[ if #self.group.value > 0 then diff --git a/sys/apps/password.lua b/sys/apps/password.lua index ca0f5b5..97ed6a4 100644 --- a/sys/apps/password.lua +++ b/sys/apps/password.lua @@ -1,10 +1,10 @@ local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('sha2') local Terminal = require('terminal') local password = Terminal.readPassword('Enter new password: ') if password then - Security.updatePassword(SHA1.sha1(password)) + Security.updatePassword(SHA2.digest(password):toHex()) print('Password updated') end diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 5f705a6..2e74430 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -1,5 +1,5 @@ local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('sha2') local UI = require('ui') local colors = _G.colors @@ -40,11 +40,11 @@ function passwordTab:eventHandler(event) if #self.newPass.value == 0 then self:emit({ type = 'error_message', message = 'Invalid password' }) - elseif Security.getPassword() and not Security.verifyPassword(SHA1.sha1(self.oldPass.value)) then + elseif Security.getPassword() and not Security.verifyPassword(SHA2.digest(self.oldPass.value):toHex()) then self:emit({ type = 'error_message', message = 'Passwords do not match' }) else - Security.updatePassword(SHA1.sha1(self.newPass.value)) + Security.updatePassword(SHA2.digest(self.newPass.value):toHex()) self.oldPass.inactive = false self:emit({ type = 'success_message', message = 'Password updated' }) end diff --git a/sys/apps/trust.lua b/sys/apps/trust.lua index da8ff55..61b3526 100644 --- a/sys/apps/trust.lua +++ b/sys/apps/trust.lua @@ -1,6 +1,6 @@ local Crypto = require('crypto') local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('sha2') local Socket = require('socket') local Terminal = require('terminal') @@ -35,7 +35,7 @@ end local publicKey = Security.getPublicKey() -socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, SHA1.sha1(password))) +socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, SHA2.digest(password):toHex())) local data = socket:read(2) socket:close() -- 2.49.1 From 3f5b18a886df322a8d0c37e2432920dcdfeef3f8 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Wed, 24 Apr 2019 00:35:17 -0400 Subject: [PATCH 003/236] Migrate cipher to ChaCha20 --- sys/apis/crypto.lua | 295 ++++++++++++++++++++++++-------------------- sys/apis/util.lua | 9 ++ 2 files changed, 170 insertions(+), 134 deletions(-) diff --git a/sys/apis/crypto.lua b/sys/apis/crypto.lua index fc1075b..94970e0 100644 --- a/sys/apis/crypto.lua +++ b/sys/apis/crypto.lua @@ -1,150 +1,177 @@ --- https://github.com/PixelToast/ComputerCraft/blob/master/apis/enc +-- Chacha20 cipher in ComputerCraft +-- By Anavrins -local Crypto = { } +local sha2 = require("sha2") +local util = require("util") -local function serialize(t) - local sType = type(t) - if sType == "table" then - local lstcnt=0 - for k,v in pairs(t) do - lstcnt = lstcnt + 1 - end - local result = "{" - local aset=1 - for k,v in pairs(t) do - if k==aset then - result = result..serialize(v).."," - aset=aset+1 - else - result = result..("["..serialize(k).."]="..serialize(v)..",") +local Crypto = {} +local ROUNDS = 20 -- Adjust this for speed tradeoff + +local bxor = bit32.bxor +local band = bit32.band +local blshift = bit32.lshift +local brshift = bit32.arshift + +local mod = 2^32 +local tau = {("expand 16-byte k"):byte(1,-1)} +local sigma = {("expand 32-byte k"):byte(1,-1)} + +local function rotl(n, b) + local s = n/(2^(32-b)) + local f = s%1 + return (s-f) + f*mod +end + +local function quarterRound(s, a, b, c, d) + s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 16) + s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 12) + s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 8) + s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 7) + return s +end + +local function hashBlock(state, rnd) + local s = {unpack(state)} + for i = 1, rnd do + local r = i%2==1 + s = r and quarterRound(s, 1, 5, 9, 13) or quarterRound(s, 1, 6, 11, 16) + s = r and quarterRound(s, 2, 6, 10, 14) or quarterRound(s, 2, 7, 12, 13) + s = r and quarterRound(s, 3, 7, 11, 15) or quarterRound(s, 3, 8, 9, 14) + s = r and quarterRound(s, 4, 8, 12, 16) or quarterRound(s, 4, 5, 10, 15) + end + for i = 1, 16 do s[i] = (s[i]+state[i])%mod end + return s +end + +local function LE_toInt(bs, i) + return (bs[i+1] or 0)+ + blshift((bs[i+2] or 0), 8)+ + blshift((bs[i+3] or 0), 16)+ + blshift((bs[i+4] or 0), 24) +end + +local function initState(key, nonce, counter) + local isKey256 = #key == 32 + local const = isKey256 and sigma or tau + local state = {} + + state[ 1] = LE_toInt(const, 0) + state[ 2] = LE_toInt(const, 4) + state[ 3] = LE_toInt(const, 8) + state[ 4] = LE_toInt(const, 12) + + state[ 5] = LE_toInt(key, 0) + state[ 6] = LE_toInt(key, 4) + state[ 7] = LE_toInt(key, 8) + state[ 8] = LE_toInt(key, 12) + state[ 9] = LE_toInt(key, isKey256 and 16 or 0) + state[10] = LE_toInt(key, isKey256 and 20 or 4) + state[11] = LE_toInt(key, isKey256 and 24 or 8) + state[12] = LE_toInt(key, isKey256 and 28 or 12) + + state[13] = counter + state[14] = LE_toInt(nonce, 0) + state[15] = LE_toInt(nonce, 4) + state[16] = LE_toInt(nonce, 8) + + return state +end + +local function serialize(state) + local r = {} + for i = 1, 16 do + r[#r+1] = band(state[i], 0xFF) + r[#r+1] = band(brshift(state[i], 8), 0xFF) + r[#r+1] = band(brshift(state[i], 16), 0xFF) + r[#r+1] = band(brshift(state[i], 24), 0xFF) + end + return r +end + +local mt = { + __tostring = function(a) return string.char(unpack(a)) end, + __index = { + toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end, + isEqual = function(self, t) + if type(t) ~= "table" then return false end + if #self ~= #t then return false end + local ret = 0 + for i = 1, #self do + ret = bit32.bor(ret, bxor(self[i], t[i])) end + return ret == 0 end - result = result.."}" - return result - elseif sType == "string" then - return string.format("%q",t) - elseif sType == "number" or sType == "boolean" or sType == "nil" then - return tostring(t) - elseif sType == "function" then - local status,data=pcall(string.dump,t) - if status then - data2="" - for char in string.gmatch(data,".") do - data2=data2..zfill(string.byte(char)) - end - return 'f("'..data2..'")' - else - error("Invalid function: "..data) + } +} + +function crypt(data, key, nonce, cntr, round) + assert(type(key) == "table", "ChaCha20: Invalid key format ("..type(key).."), must be table") + assert(type(nonce) == "table", "ChaCha20: Invalid nonce format ("..type(nonce).."), must be table") + assert(#key == 16 or #key == 32, "ChaCha20: Invalid key length ("..#key.."), must be 16 or 32") + assert(#nonce == 12, "ChaCha20: Invalid nonce length ("..#nonce.."), must be 12") + + local data = type(data) == "table" and {unpack(data)} or {tostring(data):byte(1,-1)} + cntr = tonumber(cntr) or 1 + round = tonumber(round) or 20 + + local out = {} + local state = initState(key, nonce, cntr) + local blockAmt = math.floor(#data/64) + for i = 0, blockAmt do + local ks = serialize(hashBlock(state, round)) + state[13] = (state[13]+1) % mod + + local block = {} + for j = 1, 64 do + block[j] = data[((i)*64)+j] end - else - error("Could not serialize type "..sType..".") - end -end - -local function unserialize( s ) - local func, e = loadstring( "return "..s, "serialize" ) - if not func then - return s,e - else - setfenv( func, { - f=function(S) - return loadstring(splitnum(S)) - end, - }) - return func() - end -end - -local function splitnum(S) - local Out="" - for l1=1,#S,2 do - local l2=(#S-l1)+1 - local function sure(N,n) - if (l2-n)<1 then N="0" end - return N + for j = 1, #block do + out[#out+1] = bxor(block[j], ks[j]) end - local CNum=tonumber("0x"..sure(string.sub(S,l2-1,l2-1),1) .. sure(string.sub(S,l2,l2),0)) - Out=string.char(CNum)..Out - end - return Out -end -local function zfill(N) - N=string.format("%X",N) - Zs="" - if #N==1 then - Zs="0" - end - return Zs..N -end - -local function wrap(N) - return N-(math.floor(N/256)*256) -end - -local function checksum(S) - local sum=0 - for char in string.gmatch(S,".") do - math.randomseed(string.byte(char)+sum) - sum=sum+math.random(0,9999) - end - math.randomseed(sum) - return sum -end - -local function genkey(len,psw) - checksum(psw) - local key={} - local tKeys={} - for l1=1,len do - local num=math.random(1,len) - while tKeys[num] do - num=math.random(1,len) + if i % 1000 == 0 then + os.queueEvent("") + os.pullEvent("") end - tKeys[num]=true - key[l1]={num,math.random(0,255)} end - return key + return setmetatable(out, mt) end -function Crypto.encrypt(data,psw) - data=serialize(data) - local chs=checksum(data) - local key=genkey(#data,psw) - local out={} - local cnt=1 - for char in string.gmatch(data,".") do - table.insert(out,key[cnt][1],zfill(wrap(string.byte(char)+key[cnt][2])),chars) - cnt=cnt+1 - end - return string.sub(serialize({chs,table.concat(out)}),2,-3) +local function genNonce(len) + local nonce = {} + for i = 1, len do + nonce[i] = math.random(0, 0xFF) + end + return setmetatable(nonce, mt) end -function Crypto.decrypt(data,psw) - local oData=data - data=unserialize("{"..data.."}") - if type(data)~="table" then - return oData - end - local chs=data[1] - data=data[2] - local key=genkey((#data)/2,psw) - local sKey={} - for k,v in pairs(key) do - sKey[v[1]]={k,v[2]} - end - local str=splitnum(data) - local cnt=1 - local out={} - for char in string.gmatch(str,".") do - table.insert(out,sKey[cnt][1],string.char(wrap(string.byte(char)-sKey[cnt][2]))) - cnt=cnt+1 - end - out=table.concat(out) - if checksum(out or "")==chs then - return unserialize(out) - end - return oData,out,chs +local function serialize(data) + if type(data) == "table" then + return textutils.serialise(data) + end + return data +end + +local function unserialize(data) + local undata = textutils.unserialise(tostring(data)) + if type(undata) == "table" then return undata end + return data +end + +function Crypto.encrypt(data, key) + local data = serialize(data) + local nonce = genNonce(12) + local key = sha2.digest(key) + local ctx = crypt(data, key, nonce, 1, ROUNDS) + return {nonce:toHex(), ctx:toHex()} +end + +function Crypto.decrypt(data, key) + local nonce = util.hexToByteArray(data[1]) + local data = util.hexToByteArray(data[2]) + local key = sha2.digest(key) + local ptx = crypt(data, key, nonce, 1, ROUNDS) + return unserialize(ptx) end return Crypto diff --git a/sys/apis/util.lua b/sys/apis/util.lua index f1f5a8d..128e46b 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -10,6 +10,15 @@ local _sformat = string.format local _srep = string.rep local _ssub = string.sub +function Util.hexToByteArray(str) + local r = {} + str = tostring(str) + for b in str:gmatch("%x%x?") do + r[#r+1] = tonumber(b, 16) + end + return r +end + function Util.tryTimed(timeout, f, ...) local c = os.clock() repeat -- 2.49.1 From 0c811ef892fbfd416265088096946d27795aa868 Mon Sep 17 00:00:00 2001 From: hugeblank Date: Wed, 24 Apr 2019 00:42:23 -0700 Subject: [PATCH 004/236] improve startup option screen - Replaced the old startup alternate menu with a cleaner crisper interface - Up and down arrows as well as the mouse scroll wheel will move the cursor up or down. Enter will proceed to boot. - OR if the option desired is between 1 and 9, pressing the corresponding key will select and boot that option. - Appended .lua to the startup file --- startup => startup.lua | 75 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 13 deletions(-) rename startup => startup.lua (53%) diff --git a/startup b/startup.lua similarity index 53% rename from startup rename to startup.lua index 47678d3..082bdc9 100644 --- a/startup +++ b/startup.lua @@ -12,25 +12,74 @@ local bootOptions = { local bootOption = 2 if settings then settings.load('.settings') - bootOption = tonumber(settings.get('opus.boot_option') or 2) or 2 + bootOption = tonumber(settings.get('opus.boot_option')) or bootOption end local function startupMenu() - while true do + local x, y = term.getSize() + local align, selected = 0, 1 + local function redraw() + local title = "Boot Options:" term.clear() - term.setCursorPos(1, 1) - print('Select startup mode') - print() - for k,option in pairs(bootOptions) do - print(k .. ' : ' .. option.prompt) - end - print('') - term.write('> ') - local ch = tonumber(_G.read()) - if ch and bootOptions[ch] then - return ch + term.setTextColor(colors.white) + term.setCursorPos((x/2)-(#title/2), (y/2)-(#bootOptions/2)-1) + term.write(title) + for i = 1, #bootOptions do + local txt = i..". "..bootOptions[i].prompt + term.setCursorPos((x/2)-(align/2), (y/2)-(#bootOptions/2)+i) + term.write(txt) end end + + for i = 1, #bootOptions do + if (bootOptions[i].prompt):len() > align then + align = (bootOptions[i].prompt):len() + end + end + + redraw() + repeat + term.setCursorPos((x/2)-(align/2)-2, (y/2)-(#bootOptions/2)+selected) + if term.isColor() then + term.setTextColor(colors.yellow) + else + term.setTextColor(colors.lightGray) + end + term.write(">") + local k = ({os.pullEvent()}) + if k[1] == "mouse_scroll" then + if k[2] == 1 then + k = keys.down + else + k = keys.up + end + elseif k[1] == "key" then + k = k[2] + else + k = nil + end + if k then + if k == keys.enter or k == keys.right then + return selected + elseif k == keys.down then + if selected == #bootOptions then + selected = 0 + end + selected = selected+1 + elseif k == keys.up then + if selected == 1 then + selected = #bootOptions+1 + end + selected = selected-1 + elseif k >= keys.one and k <= #bootOptions+1 and k < keys.zero then + selected = k-1 + return selected + end + local cx, cy = term.getCursorPos() + term.setCursorPos(cx-1, cy) + term.write(" ") + end + until true == false end local function splash() -- 2.49.1 From 366ab1736b51d2ad370950f0c59a53693c1ad3d8 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 24 Apr 2019 20:32:38 -0400 Subject: [PATCH 005/236] remove old startup --- sys/autorun/upgraded.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index 7b30a1f..5c947c5 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -4,3 +4,4 @@ end if fs.exists('sys/etc/app.db') then fs.delete('sys/etc/app.db') end if fs.exists('sys/extensions') then fs.delete('sys/extensions') end if fs.exists('sys/network') then fs.delete('sys/network') end +if fs.exists('startup') then fs.delete('startup') end -- 2.49.1 From 0e3cb15356dd8bea2b76b4ad434c440612a9210e Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 24 Apr 2019 22:09:36 -0400 Subject: [PATCH 006/236] blacklist items for turtle digging --- sys/init/6.tl3.lua | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/sys/init/6.tl3.lua b/sys/init/6.tl3.lua index ab4d93f..feeb5a0 100644 --- a/sys/init/6.tl3.lua +++ b/sys/init/6.tl3.lua @@ -51,6 +51,7 @@ function turtle.resetState() state.digPolicy = noop state.movePolicy = _defaultMove state.moveCallback = noop + state.blacklist = nil Pathing.reset() return true end @@ -283,6 +284,34 @@ turtle.addPolicy('turtleSafe', function(action) end) end) +local function isBlacklisted(b) + if b and state.blacklist then + for k, v in pairs(state.blacklist) do + if b.name:find(v) then + return true + end + end + end +end + +turtle.addPolicy('blacklist', function(action) + if action.side == 'back' then + return false + end + local s, m = action.inspect() + if not isBlacklisted(s and m) then + return action.dig() + end + if s and m and m.name:find('turtle') then + return Util.tryTimes(math.random(3, 6), function() + os.sleep(.25) + if not action.detect() then + return true + end + end) + end +end) + turtle.addPolicy('digAndDrop', function(action) if action.detect() then local slots = turtle.getInventory() @@ -347,6 +376,9 @@ function turtle.set(args) elseif k == 'status' then turtle.setStatus(v) + elseif k == 'blacklist' then + state.blacklist = v + else error('Invalid turle.set: ' .. tostring(k)) end -- 2.49.1 From f4bcd611160bd5c8894c4959d99ea6c3c4f47d49 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 25 Apr 2019 14:44:36 -0400 Subject: [PATCH 007/236] tweaks --- sys/apis/event.lua | 6 +++++- sys/apis/fs/netfs.lua | 1 + sys/apps/Files.lua | 4 ++-- sys/autorun/apps.lua | 2 +- sys/init/6.tl3.lua | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sys/apis/event.lua b/sys/apis/event.lua index b563f2f..924461e 100644 --- a/sys/apis/event.lua +++ b/sys/apis/event.lua @@ -118,12 +118,16 @@ function Event.off(h) end function Event.onInterval(interval, fn) - return Event.addRoutine(function() + local h = Event.addRoutine(function() while true do os.sleep(interval) fn() end end) + function h.updateInterval(i) + interval = i + end + return h end function Event.onTimeout(timeout, fn) diff --git a/sys/apis/fs/netfs.lua b/sys/apis/fs/netfs.lua index d7eb2eb..77ec3b0 100644 --- a/sys/apis/fs/netfs.lua +++ b/sys/apis/fs/netfs.lua @@ -36,6 +36,7 @@ end local methods = { 'delete', 'exists', 'getFreeSpace', 'makeDir', 'list', 'listEx' } local function resolveDir(dir, node) + -- TODO: Wrong ! (does not support names with dashes) dir = dir:gsub(node.mountPoint, '', 1) return fs.combine(node.directory, dir) end diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 0e40ec5..f7564ed 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -146,9 +146,9 @@ local Browser = UI.Page { s = 'shell', p = 'pastebin', r = 'refresh', - space = 'mark', + [ ' ' ] = 'mark', + m = 'mark', backspace = 'updir', - m = 'move', u = 'unmark', d = 'delete', delete = 'delete', diff --git a/sys/autorun/apps.lua b/sys/autorun/apps.lua index c66656b..5322c79 100644 --- a/sys/autorun/apps.lua +++ b/sys/autorun/apps.lua @@ -1,4 +1,4 @@ -fs.mount('sys/apps/pain.lua', 'urlfs', 'http://pastebin.com/raw/wJQ7jav0') +fs.mount('sys/apps/pain.lua', 'urlfs', 'https://github.com/LDDestroier/CC/raw/master/pain.lua') fs.mount('sys/apps/update.lua', 'urlfs', 'http://pastebin.com/raw/UzGHLbNC') fs.mount('sys/apps/Enchat.lua', 'urlfs', 'https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua') fs.mount('sys/apps/cloud.lua', 'urlfs', 'https://cloud-catcher.squiddev.cc/cloud.lua') diff --git a/sys/init/6.tl3.lua b/sys/init/6.tl3.lua index feeb5a0..53519c4 100644 --- a/sys/init/6.tl3.lua +++ b/sys/init/6.tl3.lua @@ -286,7 +286,7 @@ end) local function isBlacklisted(b) if b and state.blacklist then - for k, v in pairs(state.blacklist) do + for _, v in pairs(state.blacklist) do if b.name:find(v) then return true end -- 2.49.1 From 59552d4217c495cc07f8f3b81c1a737d435ca53a Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 27 Apr 2019 19:33:21 -0400 Subject: [PATCH 008/236] canvas update for resizing --- sys/apis/input.lua | 2 +- sys/apis/terminal.lua | 7 +++++-- sys/apis/ui/canvas.lua | 18 ++++++++++++++++-- sys/init/1.device.lua | 8 -------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/sys/apis/input.lua b/sys/apis/input.lua index 661ad70..4134461 100644 --- a/sys/apis/input.lua +++ b/sys/apis/input.lua @@ -91,7 +91,7 @@ function input:translate(event, code, p1, p2) end return { code = ch } end - else + elseif code then self.state[code] = true self.fired = false end diff --git a/sys/apis/terminal.lua b/sys/apis/terminal.lua index dc10311..214e4ec 100644 --- a/sys/apis/terminal.lua +++ b/sys/apis/terminal.lua @@ -192,8 +192,11 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end function win.restoreCursor() - win.setCursorPos(cx, cy) - win.setCursorBlink(blink) + if isVisible then + win.setCursorPos(cx, cy) + win.setTextColor(fg) + win.setCursorBlink(blink) + end end function win.getPosition() diff --git a/sys/apis/ui/canvas.lua b/sys/apis/ui/canvas.lua index d980845..c7fbca3 100644 --- a/sys/apis/ui/canvas.lua +++ b/sys/apis/ui/canvas.lua @@ -63,8 +63,22 @@ function Canvas:resize(w, h) table.remove(self.lines, #self.lines) end - if w ~= self.width then - self:clear() + if w < self.width then + for i = 1, h do + self.lines[i].text = _sub(self.lines[i].text, 1, w) + self.lines[i].fg = _sub(self.lines[i].fg, 1, w) + self.lines[i].bg = _sub(self.lines[i].bg, 1, w) + end + elseif w > self.width then + local d = w - self.width + local text = _rep(' ', d) + local fg = _rep(self.palette[self.fg or colors.white], d) + local bg = _rep(self.palette[self.bg or colors.black], d) + for i = 1, h do + self.lines[i].text = self.lines[i].text .. text + self.lines[i].fg = self.lines[i].fg .. fg + self.lines[i].bg = self.lines[i].bg .. bg + end end self.ex = self.x + w - 1 diff --git a/sys/init/1.device.lua b/sys/init/1.device.lua index 3d25083..803898d 100644 --- a/sys/init/1.device.lua +++ b/sys/init/1.device.lua @@ -121,14 +121,6 @@ function keyboard.removeHotkey(code) keyboard.hotkeys[code] = nil end -kernel.hook('monitor_touch', function(event, eventData) - local monitor = Peripheral.getBySide(eventData[1]) - if monitor and monitor.eventChannel then - monitor.eventChannel(event, table.unpack(eventData)) - return true -- stop propagation - end -end) - local function createDevice(name, devType, method, manipulator) local dev = { name = name, -- 2.49.1 From c44dc765daa127f7036dc0a675ad43f3664ca425 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 3 May 2019 15:30:09 -0400 Subject: [PATCH 009/236] change _debug to _syslog --- sys/apis/terminal.lua | 4 ++-- sys/apis/trace.lua | 2 +- sys/apis/ui/canvas.lua | 16 +++++++++------- sys/apps/network/transport.lua | 16 ++++++++-------- sys/boot/opus.boot | 2 +- sys/kernel.lua | 8 +------- 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/sys/apis/terminal.lua b/sys/apis/terminal.lua index 214e4ec..e73d822 100644 --- a/sys/apis/terminal.lua +++ b/sys/apis/terminal.lua @@ -100,9 +100,9 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end function win.setCursorPos(x, y) - cx, cy = x, y + cx, cy = math.floor(x), math.floor(y) if isVisible then - parent.setCursorPos(x + canvas.x - 1, y + canvas.y - 1) + parent.setCursorPos(cx + canvas.x - 1, cy + canvas.y - 1) end end diff --git a/sys/apis/trace.lua b/sys/apis/trace.lua index ddb023c..4323003 100644 --- a/sys/apis/trace.lua +++ b/sys/apis/trace.lua @@ -85,7 +85,7 @@ return function (fn, ...) end for _, line in pairs(trace) do - _G._debug(line) + _G._syslog(line) end -- If this traceback is more than 15 elements long, keep the first 9, last 5 diff --git a/sys/apis/ui/canvas.lua b/sys/apis/ui/canvas.lua index c7fbca3..b7969b0 100644 --- a/sys/apis/ui/canvas.lua +++ b/sys/apis/ui/canvas.lua @@ -199,13 +199,15 @@ function Canvas:blit(x, y, text, bg, fg) end local line = self.lines[y] - line.dirty = true - line.text = replace(line.text, x, text, width) - if fg then - line.fg = replace(line.fg, x, fg, width) - end - if bg then - line.bg = replace(line.bg, x, bg, width) + if line then + line.dirty = true + line.text = replace(line.text, x, text, width) + if fg then + line.fg = replace(line.fg, x, fg, width) + end + if bg then + line.bg = replace(line.bg, x, bg, width) + end end end end diff --git a/sys/apps/network/transport.lua b/sys/apps/network/transport.lua index 2eeb801..eccd9ef 100644 --- a/sys/apps/network/transport.lua +++ b/sys/apps/network/transport.lua @@ -33,7 +33,7 @@ function transport.read(socket) end function transport.write(socket, data) - --_debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq })) + --_syslog('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq })) socket.transmit(socket.dport, socket.dhost, data) --local timerId = os.startTimer(3) @@ -45,7 +45,7 @@ function transport.write(socket, data) end function transport.ping(socket) - --_debug('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq })) + --_syslog('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq })) if os.clock() - socket.activityTimer > 10 then socket.activityTimer = os.clock() socket.transmit(socket.dport, socket.dhost, { @@ -78,9 +78,9 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance) local socket = transport.sockets[dport] if socket and socket.connected then - --if msg.type then _debug('<< ' .. Util.tostring(msg)) end + --if msg.type then _syslog('<< ' .. Util.tostring(msg)) end if socket.co and coroutine.status(socket.co) == 'dead' then - _G._debug('socket coroutine dead') + _G._syslog('socket coroutine dead') socket:close() elseif msg.type == 'DISC' then @@ -111,9 +111,9 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance) socket.activityTimer = os.clock() if msg.seq ~= socket.rseq then print('transport seq error - closing socket ' .. socket.sport) - _debug(msg.data) - _debug('current ' .. socket.rseq) - _debug('expected ' .. msg.seq) + _syslog(msg.data) + _syslog('current ' .. socket.rseq) + _syslog('expected ' .. msg.seq) -- socket:close() -- os.queueEvent('transport_' .. socket.uid) else @@ -125,7 +125,7 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance) os.queueEvent('transport_' .. socket.uid) end - --_debug('>> ' .. Util.tostring({ type = 'ACK', seq = msg.seq })) + --_syslog('>> ' .. Util.tostring({ type = 'ACK', seq = msg.seq })) --socket.transmit(socket.dport, socket.dhost, { -- type = 'ACK', -- seq = msg.seq, diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index 7ba7a1d..d139e37 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -11,7 +11,7 @@ for k,v in pairs(_ENV) do sandboxEnv[k] = v end -_G._debug = function() end +_G._syslog = function() end local function makeEnv() local env = setmetatable({ }, { __index = _G }) diff --git a/sys/kernel.lua b/sys/kernel.lua index a787513..799164e 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -28,19 +28,13 @@ local focusedRoutineEvents = Util.transpose { 'paste', 'terminate', } -_G._debug = function(pattern, ...) +_G._syslog = function(pattern, ...) local oldTerm = term.redirect(kernel.window) kernel.window.scrollBottom() Util.print(pattern, ...) term.redirect(oldTerm) end -if not _G.debug then -- don't clobber lua debugger - function _G.debug(...) - _G._debug(...) - end -end - -- any function that runs in a kernel hook does not run in -- a separate coroutine or have a window. an error in a hook -- function will crash the system. -- 2.49.1 From bf870479c4f6a6e74ea72682823e041bd8eb76b4 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 4 May 2019 06:11:22 -0400 Subject: [PATCH 010/236] mark with drag fix --- sys/apis/ui.lua | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sys/apis/ui.lua b/sys/apis/ui.lua index 41c7242..3b0c26b 100644 --- a/sys/apis/ui.lua +++ b/sys/apis/ui.lua @@ -114,7 +114,6 @@ function Manager:init() mouse_up = function(_, button, x, y) local ie = Input:translate('mouse_up', button, x, y) - local currentPage = self:getActivePage() if ie.code == 'control-shift-mouse_click' then -- hack @@ -134,12 +133,9 @@ function Manager:init() mouse_drag = function(_, button, x, y) local ie = Input:translate('mouse_drag', button, x, y) local currentPage = self:getActivePage() + if ie and currentPage then - local event = currentPage:pointToChild(x, y) - event.type = ie.code - event.ie = ie - self:inputEvent(event.element, event) - currentPage:sync() + self:click(currentPage, ie.code, button, x, y) end end, -- 2.49.1 From 5296e134a6a39f40837eba121ab2853c2bcdacf4 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Mon, 6 May 2019 01:01:06 -0400 Subject: [PATCH 011/236] Put all crypto stuff in their folder --- sys/apis/{crypto.lua => crypto/chacha20.lua} | 2 +- sys/apis/crypto/ecc/ecc.lua | 87 ++ sys/apis/crypto/ecc/elliptic.lua | 300 ++++++ sys/apis/crypto/ecc/fp.lua | 928 +++++++++++++++++++ sys/apis/crypto/ecc/fq.lua | 741 +++++++++++++++ sys/apis/{ => crypto}/sha1.lua | 0 sys/apis/{ => crypto}/sha2.lua | 0 sys/apis/socket.lua | 2 +- sys/apps/Overview.lua | 2 +- sys/apps/Welcome.lua | 2 +- sys/apps/network/trust.lua | 2 +- sys/apps/password.lua | 2 +- sys/apps/system/password.lua | 2 +- sys/apps/trust.lua | 4 +- 14 files changed, 2065 insertions(+), 9 deletions(-) rename sys/apis/{crypto.lua => crypto/chacha20.lua} (99%) create mode 100644 sys/apis/crypto/ecc/ecc.lua create mode 100644 sys/apis/crypto/ecc/elliptic.lua create mode 100644 sys/apis/crypto/ecc/fp.lua create mode 100644 sys/apis/crypto/ecc/fq.lua rename sys/apis/{ => crypto}/sha1.lua (100%) rename sys/apis/{ => crypto}/sha2.lua (100%) diff --git a/sys/apis/crypto.lua b/sys/apis/crypto/chacha20.lua similarity index 99% rename from sys/apis/crypto.lua rename to sys/apis/crypto/chacha20.lua index 94970e0..4d8f72d 100644 --- a/sys/apis/crypto.lua +++ b/sys/apis/crypto/chacha20.lua @@ -1,7 +1,7 @@ -- Chacha20 cipher in ComputerCraft -- By Anavrins -local sha2 = require("sha2") +local sha2 = require("crypto.sha2") local util = require("util") local Crypto = {} diff --git a/sys/apis/crypto/ecc/ecc.lua b/sys/apis/crypto/ecc/ecc.lua new file mode 100644 index 0000000..030ce61 --- /dev/null +++ b/sys/apis/crypto/ecc/ecc.lua @@ -0,0 +1,87 @@ +local fq = require('crypto.ecc.fp') +local elliptic = require('crypto.ecc.elliptic') +local sha256 = require('crypto.sha2') + +local q = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532} + +local sLen = 24 +local eLen = 24 + +local function hashModQ(sk) + local hash = sha256.hmac({0x00}, sk) + local x + repeat + hash = sha256.digest(hash) + x = fq.fromBytes(hash) + until fq.cmp(x, q) <= 0 + + return x +end + +local function publicKey(sk) + local x = hashModQ(sk) + + local Y = elliptic.scalarMulG(x) + local pk = elliptic.pointEncode(Y) + + return pk +end + +local function exchange(sk, pk) + local Y = elliptic.pointDecode(pk) + local x = hashModQ(sk) + + local Z = elliptic.scalarMul(x, Y) + Z = elliptic.pointScale(Z) + + local ss = fq.bytes(Z[2]) + local ss = sha256.digest(ss) + + return ss +end + +local function sign(sk, message) + message = type(message) == "table" and string.char(unpack(message)) or message + sk = type(sk) == "table" and string.char(unpack(sk)) or sk + local epoch = tostring(os.epoch("utc")) + local x = hashModQ(sk) + local k = hashModQ(message .. epoch .. sk) + + local R = elliptic.scalarMulG(k) + R = string.char(unpack(elliptic.pointEncode(R))) + local e = hashModQ(R .. message) + local s = fq.sub(k, fq.mul(x, e)) + + e = fq.bytes(e) + s = fq.bytes(s) + + local sig = {unpack(e)} + + for i = 1, #s do + sig[#sig + 1] = s[i] + end + + return sig +end + +local function verify(pk, message, sig) + local Y = elliptic.pointDecode(pk) + local e = {unpack(sig, 1, eLen)} + local s = {unpack(sig, eLen + 1, eLen + sLen)} + + e = fq.fromBytes(e) + s = fq.fromBytes(s) + + local R = elliptic.pointAdd(elliptic.scalarMulG(s), elliptic.scalarMul(e, Y)) + R = string.char(unpack(elliptic.pointEncode(R))) + local e2 = hashModQ(R .. message) + + return fq.eq(e2, e) +end + +return { + publicKey = publicKey, + exchange = exchange, + sign = sign, + verify = verify, +} diff --git a/sys/apis/crypto/ecc/elliptic.lua b/sys/apis/crypto/ecc/elliptic.lua new file mode 100644 index 0000000..0e4da04 --- /dev/null +++ b/sys/apis/crypto/ecc/elliptic.lua @@ -0,0 +1,300 @@ +---- Elliptic Curve Arithmetic + +---- About the Curve Itself +-- Field Size: 192 bits +-- Field Modulus (p): 65533 * 2^176 + 3 +-- Equation: x^2 + y^2 = 1 + 108 * x^2 * y^2 +-- Parameters: Edwards Curve with c = 1, and d = 108 +-- Curve Order (n): 4 * 1569203598118192102418711808268118358122924911136798015831 +-- Cofactor (h): 4 +-- Generator Order (q): 1569203598118192102418711808268118358122924911136798015831 +---- About the Curve's Security +-- Current best attack security: 94.822 bits (Pollard's Rho) +-- Rho Security: log2(0.884 * sqrt(q)) = 94.822 +-- Transfer Security? Yes: p ~= q; k > 20 +-- Field Discriminant Security? Yes: t = 67602300638727286331433024168; s = 2^2; |D| = 5134296629560551493299993292204775496868940529592107064435 > 2^100 +-- Rigidity? A little, the parameters are somewhat small. +-- XZ/YZ Ladder Security? No: Single coordinate ladders are insecure, so they can't be used. +-- Small Subgroup Security? Yes: Secret keys are calculated modulo 4q. +-- Invalid Curve Security? Yes: Any point to be multiplied is checked beforehand. +-- Invalid Curve Twist Security? No: The curve is not protected against single coordinate ladder attacks, so don't use them. +-- Completeness? Yes: The curve is an Edwards Curve with non-square d and square a, so the curve is complete. +-- Indistinguishability? No: The curve does not support indistinguishability maps. + +local fp = require('crypto.ecc.fp') +local eq = fp.eq +local mul = fp.mul +local sqr = fp.sqr +local add = fp.add +local sub = fp.sub +local shr = fp.shr +local mont = fp.mont +local invMont = fp.invMont +local sub192 = fp.sub192 + +local bits = 192 +local pMinusTwoBinary = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} +local pMinusThreeOverFourBinary = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0} +local ZERO = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +local ONE = mont({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + +local p = mont({3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65533}) +local G = { + mont({30457, 58187, 5603, 63215, 8936, 58151, 26571, 7272, 26680, 23486, 32353, 59456}), + mont({3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), + mont({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) +} +local GTable = {G} + +local d = mont({108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + +local function generator() + return G +end + +local function expMod(a, t) + local a = {unpack(a)} + local result = {unpack(ONE)} + + for i = 1, bits do + if t[i] == 1 then + result = mul(result, a) + end + a = mul(a, a) + end + + return result +end + +-- We're using Projective Coordinates +-- For Edwards curves +-- The identity element is represented by (0:1:1) +local function pointDouble(P1) + local X1, Y1, Z1 = unpack(P1) + + local b = add(X1, Y1) + local B = sqr(b) + local C = sqr(X1) + local D = sqr(Y1) + local E = add(C, D) + local H = sqr(Z1) + local J = sub(E, add(H, H)) + local X3 = mul(sub(B, E), J) + local Y3 = mul(E, sub(C, D)) + local Z3 = mul(E, J) + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointAdd(P1, P2) + local X1, Y1, Z1 = unpack(P1) + local X2, Y2, Z2 = unpack(P2) + + local A = mul(Z1, Z2) + local B = sqr(A) + local C = mul(X1, X2) + local D = mul(Y1, Y2) + local E = mul(d, mul(C, D)) + local F = sub(B, E) + local G = add(B, E) + local X3 = mul(A, mul(F, sub(mul(add(X1, Y1), add(X2, Y2)), add(C, D)))) + local Y3 = mul(A, mul(G, sub(D, C))) + local Z3 = mul(F, G) + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointNeg(P1) + local X1, Y1, Z1 = unpack(P1) + + local X3 = sub(p, X1) + local Y3 = {unpack(Y1)} + local Z3 = {unpack(Z1)} + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointSub(P1, P2) + return pointAdd(P1, pointNeg(P2)) +end + +local function pointScale(P1) + local X1, Y1, Z1 = unpack(P1) + + local A = expMod(Z1, pMinusTwoBinary) + local X3 = mul(X1, A) + local Y3 = mul(Y1, A) + local Z3 = {unpack(ONE)} + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointEq(P1, P2) + local X1, Y1, Z1 = unpack(P1) + local X2, Y2, Z2 = unpack(P2) + + local A1 = mul(X1, Z2) + local B1 = mul(Y1, Z2) + local A2 = mul(X2, Z1) + local B2 = mul(Y2, Z1) + + return eq(A1, A2) and eq(B1, B2) +end + +local function isOnCurve(P1) + local X1, Y1, Z1 = unpack(P1) + + local X12 = sqr(X1) + local Y12 = sqr(Y1) + local Z12 = sqr(Z1) + local Z14 = sqr(Z12) + local a = add(X12, Y12) + a = mul(a, Z12) + local b = mul(d, mul(X12, Y12)) + b = add(Z14, b) + + return eq(a, b) +end + +local function mods(d) + -- w = 5 + local result = d[1] % 32 + + if result >= 16 then + result = result - 32 + end + + return result +end + +local function NAF(d) + local t = {} + local d = {unpack(d)} + + while d[12] >= 0 and not eq(d, ZERO) do + if d[1] % 2 == 1 then + t[#t + 1] = mods(d) + d = sub192(d, {t[#t], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + else + t[#t + 1] = 0 + end + + d = shr(d) + end + + return t +end + +local function scalarMul(s, P1) + local naf = NAF(s) + local PTable = {P1} + local P2 = pointDouble(P1) + + for i = 3, 31, 2 do + PTable[i] = pointAdd(PTable[i - 2], P2) + end + + local Q = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}} + for i = #naf, 1, -1 do + Q = pointDouble(Q) + if naf[i] > 0 then + Q = pointAdd(Q, PTable[naf[i]]) + elseif naf[i] < 0 then + Q = pointSub(Q, PTable[-naf[i]]) + end + end + + return Q +end + +for i = 2, 196 do + GTable[i] = pointDouble(GTable[i - 1]) +end + +local function scalarMulG(s) + local result = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}} + local k = 1 + + for i = 1, 12 do + local w = s[i] + + for j = 1, 16 do + if w % 2 == 1 then + result = pointAdd(result, GTable[k]) + end + + k = k + 1 + + w = w / 2 + w = w - w % 1 + end + end + + return result +end + +local function pointEncode(P1) + P1 = pointScale(P1) + + local result = {} + local x, y = unpack(P1) + + result[1] = x[1] % 2 + + for i = 1, 12 do + local m = y[i] % 256 + result[2 * i] = m + result[2 * i + 1] = (y[i] - m) / 256 + end + + return result +end + +local function pointDecode(enc) + local y = {} + for i = 1, 12 do + y[i] = enc[2 * i] + y[i] = y[i] + enc[2 * i + 1] * 256 + end + + local y2 = sqr(y) + local u = sub(y2, ONE) + local v = sub(mul(d, y2), ONE) + local u2 = sqr(u) + local u3 = mul(u, u2) + local u5 = mul(u3, u2) + local v3 = mul(v, sqr(v)) + local w = mul(u5, v3) + local x = mul(u3, mul(v, expMod(w, pMinusThreeOverFourBinary))) + + if x[1] % 2 ~= enc[1] then + x = sub(p, x) + end + + local P3 = {x, y, {unpack(ONE)}} + + return P3 +end + +return { + generator = generator, + pointDouble = pointDouble, + pointAdd = pointAdd, + pointNeg = pointNeg, + pointSub = pointSub, + pointScale = pointScale, + pointEq = pointEq, + isOnCurve = isOnCurve, + scalarMul = scalarMul, + scalarMulG = scalarMulG, + pointEncode = pointEncode, + pointDecode = pointDecode, +} diff --git a/sys/apis/crypto/ecc/fp.lua b/sys/apis/crypto/ecc/fp.lua new file mode 100644 index 0000000..e5d97ed --- /dev/null +++ b/sys/apis/crypto/ecc/fp.lua @@ -0,0 +1,928 @@ +-- Fp Integer Arithmetic + +local n = 0xffff +local m = 0x10000 + +local p = {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65533} +local p2 = {21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 43690} +local r2 = {44014, 58358, 19452, 6484, 45852, 58974, 63348, 64806, 65292, 65454, 65508, 21512} + +local function eq(a, b) + for i = 1, 12 do + if a[i] ~= b[i] then + return false + end + end + + return true +end + +local function reduce(a) + local r1 = a[1] + local r2 = a[2] + local r3 = a[3] + local r4 = a[4] + local r5 = a[5] + local r6 = a[6] + local r7 = a[7] + local r8 = a[8] + local r9 = a[9] + local r10 = a[10] + local r11 = a[11] + local r12 = a[12] + + if r12 < 65533 or r12 == 65533 and r1 < 3 then + return {unpack(a)} + end + + r1 = r1 - 3 + r12 = r12 - 65533 + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + return {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} +end + +local function add(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return reduce(result) +end + +local function shr(a) + local r1 = a[1] + local r2 = a[2] + local r3 = a[3] + local r4 = a[4] + local r5 = a[5] + local r6 = a[6] + local r7 = a[7] + local r8 = a[8] + local r9 = a[9] + local r10 = a[10] + local r11 = a[11] + local r12 = a[12] + + r1 = r1 / 2 + r1 = r1 - r1 % 1 + r1 = r1 + (r2 % 2) * 0x8000 + r2 = r2 / 2 + r2 = r2 - r2 % 1 + r2 = r2 + (r3 % 2) * 0x8000 + r3 = r3 / 2 + r3 = r3 - r3 % 1 + r3 = r3 + (r4 % 2) * 0x8000 + r4 = r4 / 2 + r4 = r4 - r4 % 1 + r4 = r4 + (r5 % 2) * 0x8000 + r5 = r5 / 2 + r5 = r5 - r5 % 1 + r5 = r5 + (r6 % 2) * 0x8000 + r6 = r6 / 2 + r6 = r6 - r6 % 1 + r6 = r6 + (r7 % 2) * 0x8000 + r7 = r7 / 2 + r7 = r7 - r7 % 1 + r7 = r7 + (r8 % 2) * 0x8000 + r8 = r8 / 2 + r8 = r8 - r8 % 1 + r8 = r8 + (r9 % 2) * 0x8000 + r9 = r9 / 2 + r9 = r9 - r9 % 1 + r9 = r9 + (r10 % 2) * 0x8000 + r10 = r10 / 2 + r10 = r10 - r10 % 1 + r10 = r10 + (r11 % 2) * 0x8000 + r11 = r11 / 2 + r11 = r11 - r11 % 1 + r11 = r11 + (r12 % 2) * 0x8000 + r12 = r12 / 2 + r12 = r12 - r12 % 1 + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return result +end + +local function sub192(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return result +end + +local function sub(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + if r12 < 0 then + result = add(result, p) + end + + return result +end + +local function add384(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + local r13 = a[13] + b[13] + local r14 = a[14] + b[14] + local r15 = a[15] + b[15] + local r16 = a[16] + b[16] + local r17 = a[17] + b[17] + local r18 = a[18] + b[18] + local r19 = a[19] + b[19] + local r20 = a[20] + b[20] + local r21 = a[21] + b[21] + local r22 = a[22] + b[22] + local r23 = a[23] + b[23] + local r24 = a[24] + b[24] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + if r12 > n then + r13 = r13 + 1 + r12 = r12 - m + end + if r13 > n then + r14 = r14 + 1 + r13 = r13 - m + end + if r14 > n then + r15 = r15 + 1 + r14 = r14 - m + end + if r15 > n then + r16 = r16 + 1 + r15 = r15 - m + end + if r16 > n then + r17 = r17 + 1 + r16 = r16 - m + end + if r17 > n then + r18 = r18 + 1 + r17 = r17 - m + end + if r18 > n then + r19 = r19 + 1 + r18 = r18 - m + end + if r19 > n then + r20 = r20 + 1 + r19 = r19 - m + end + if r20 > n then + r21 = r21 + 1 + r20 = r20 - m + end + if r21 > n then + r22 = r22 + 1 + r21 = r21 - m + end + if r22 > n then + r23 = r23 + 1 + r22 = r22 - m + end + if r23 > n then + r24 = r24 + 1 + r23 = r23 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function mul384(a, b) + local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 = unpack(a) + local b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12 = unpack(b) + + local r1 = a1 * b1 + + local r2 = a1 * b2 + r2 = r2 + a2 * b1 + + local r3 = a1 * b3 + r3 = r3 + a2 * b2 + r3 = r3 + a3 * b1 + + local r4 = a1 * b4 + r4 = r4 + a2 * b3 + r4 = r4 + a3 * b2 + r4 = r4 + a4 * b1 + + local r5 = a1 * b5 + r5 = r5 + a2 * b4 + r5 = r5 + a3 * b3 + r5 = r5 + a4 * b2 + r5 = r5 + a5 * b1 + + local r6 = a1 * b6 + r6 = r6 + a2 * b5 + r6 = r6 + a3 * b4 + r6 = r6 + a4 * b3 + r6 = r6 + a5 * b2 + r6 = r6 + a6 * b1 + + local r7 = a1 * b7 + r7 = r7 + a2 * b6 + r7 = r7 + a3 * b5 + r7 = r7 + a4 * b4 + r7 = r7 + a5 * b3 + r7 = r7 + a6 * b2 + r7 = r7 + a7 * b1 + + local r8 = a1 * b8 + r8 = r8 + a2 * b7 + r8 = r8 + a3 * b6 + r8 = r8 + a4 * b5 + r8 = r8 + a5 * b4 + r8 = r8 + a6 * b3 + r8 = r8 + a7 * b2 + r8 = r8 + a8 * b1 + + local r9 = a1 * b9 + r9 = r9 + a2 * b8 + r9 = r9 + a3 * b7 + r9 = r9 + a4 * b6 + r9 = r9 + a5 * b5 + r9 = r9 + a6 * b4 + r9 = r9 + a7 * b3 + r9 = r9 + a8 * b2 + r9 = r9 + a9 * b1 + + local r10 = a1 * b10 + r10 = r10 + a2 * b9 + r10 = r10 + a3 * b8 + r10 = r10 + a4 * b7 + r10 = r10 + a5 * b6 + r10 = r10 + a6 * b5 + r10 = r10 + a7 * b4 + r10 = r10 + a8 * b3 + r10 = r10 + a9 * b2 + r10 = r10 + a10 * b1 + + local r11 = a1 * b11 + r11 = r11 + a2 * b10 + r11 = r11 + a3 * b9 + r11 = r11 + a4 * b8 + r11 = r11 + a5 * b7 + r11 = r11 + a6 * b6 + r11 = r11 + a7 * b5 + r11 = r11 + a8 * b4 + r11 = r11 + a9 * b3 + r11 = r11 + a10 * b2 + r11 = r11 + a11 * b1 + + local r12 = a1 * b12 + r12 = r12 + a2 * b11 + r12 = r12 + a3 * b10 + r12 = r12 + a4 * b9 + r12 = r12 + a5 * b8 + r12 = r12 + a6 * b7 + r12 = r12 + a7 * b6 + r12 = r12 + a8 * b5 + r12 = r12 + a9 * b4 + r12 = r12 + a10 * b3 + r12 = r12 + a11 * b2 + r12 = r12 + a12 * b1 + + local r13 = a2 * b12 + r13 = r13 + a3 * b11 + r13 = r13 + a4 * b10 + r13 = r13 + a5 * b9 + r13 = r13 + a6 * b8 + r13 = r13 + a7 * b7 + r13 = r13 + a8 * b6 + r13 = r13 + a9 * b5 + r13 = r13 + a10 * b4 + r13 = r13 + a11 * b3 + r13 = r13 + a12 * b2 + + local r14 = a3 * b12 + r14 = r14 + a4 * b11 + r14 = r14 + a5 * b10 + r14 = r14 + a6 * b9 + r14 = r14 + a7 * b8 + r14 = r14 + a8 * b7 + r14 = r14 + a9 * b6 + r14 = r14 + a10 * b5 + r14 = r14 + a11 * b4 + r14 = r14 + a12 * b3 + + local r15 = a4 * b12 + r15 = r15 + a5 * b11 + r15 = r15 + a6 * b10 + r15 = r15 + a7 * b9 + r15 = r15 + a8 * b8 + r15 = r15 + a9 * b7 + r15 = r15 + a10 * b6 + r15 = r15 + a11 * b5 + r15 = r15 + a12 * b4 + + local r16 = a5 * b12 + r16 = r16 + a6 * b11 + r16 = r16 + a7 * b10 + r16 = r16 + a8 * b9 + r16 = r16 + a9 * b8 + r16 = r16 + a10 * b7 + r16 = r16 + a11 * b6 + r16 = r16 + a12 * b5 + + local r17 = a6 * b12 + r17 = r17 + a7 * b11 + r17 = r17 + a8 * b10 + r17 = r17 + a9 * b9 + r17 = r17 + a10 * b8 + r17 = r17 + a11 * b7 + r17 = r17 + a12 * b6 + + local r18 = a7 * b12 + r18 = r18 + a8 * b11 + r18 = r18 + a9 * b10 + r18 = r18 + a10 * b9 + r18 = r18 + a11 * b8 + r18 = r18 + a12 * b7 + + local r19 = a8 * b12 + r19 = r19 + a9 * b11 + r19 = r19 + a10 * b10 + r19 = r19 + a11 * b9 + r19 = r19 + a12 * b8 + + local r20 = a9 * b12 + r20 = r20 + a10 * b11 + r20 = r20 + a11 * b10 + r20 = r20 + a12 * b9 + + local r21 = a10 * b12 + r21 = r21 + a11 * b11 + r21 = r21 + a12 * b10 + + local r22 = a11 * b12 + r22 = r22 + a12 * b11 + + local r23 = a12 * b12 + + local r24 = 0 + + r2 = r2 + (r1 / m) + r2 = r2 - r2 % 1 + r1 = r1 % m + r3 = r3 + (r2 / m) + r3 = r3 - r3 % 1 + r2 = r2 % m + r4 = r4 + (r3 / m) + r4 = r4 - r4 % 1 + r3 = r3 % m + r5 = r5 + (r4 / m) + r5 = r5 - r5 % 1 + r4 = r4 % m + r6 = r6 + (r5 / m) + r6 = r6 - r6 % 1 + r5 = r5 % m + r7 = r7 + (r6 / m) + r7 = r7 - r7 % 1 + r6 = r6 % m + r8 = r8 + (r7 / m) + r8 = r8 - r8 % 1 + r7 = r7 % m + r9 = r9 + (r8 / m) + r9 = r9 - r9 % 1 + r8 = r8 % m + r10 = r10 + (r9 / m) + r10 = r10 - r10 % 1 + r9 = r9 % m + r11 = r11 + (r10 / m) + r11 = r11 - r11 % 1 + r10 = r10 % m + r12 = r12 + (r11 / m) + r12 = r12 - r12 % 1 + r11 = r11 % m + r13 = r13 + (r12 / m) + r13 = r13 - r13 % 1 + r12 = r12 % m + r14 = r14 + (r13 / m) + r14 = r14 - r14 % 1 + r13 = r13 % m + r15 = r15 + (r14 / m) + r15 = r15 - r15 % 1 + r14 = r14 % m + r16 = r16 + (r15 / m) + r16 = r16 - r16 % 1 + r15 = r15 % m + r17 = r17 + (r16 / m) + r17 = r17 - r17 % 1 + r16 = r16 % m + r18 = r18 + (r17 / m) + r18 = r18 - r18 % 1 + r17 = r17 % m + r19 = r19 + (r18 / m) + r19 = r19 - r19 % 1 + r18 = r18 % m + r20 = r20 + (r19 / m) + r20 = r20 - r20 % 1 + r19 = r19 % m + r21 = r21 + (r20 / m) + r21 = r21 - r21 % 1 + r20 = r20 % m + r22 = r22 + (r21 / m) + r22 = r22 - r22 % 1 + r21 = r21 % m + r23 = r23 + (r22 / m) + r23 = r23 - r23 % 1 + r22 = r22 % m + r24 = r24 + (r23 / m) + r24 = r24 - r24 % 1 + r23 = r23 % m + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function REDC(T) + local m = {unpack(mul384({unpack(T, 1, 12)}, p2), 1, 12)} + local t = {unpack(add384(T, mul384(m, p)), 13, 24)} + + return reduce(t) +end + +local function mul(a, b) + return REDC(mul384(a, b)) +end + +local function sqr(a) + local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 = unpack(a) + + local r1 = a1 * a1 + + local r2 = a1 * a2 * 2 + + local r3 = a1 * a3 * 2 + r3 = r3 + a2 * a2 + + local r4 = a1 * a4 * 2 + r4 = r4 + a2 * a3 * 2 + + local r5 = a1 * a5 * 2 + r5 = r5 + a2 * a4 * 2 + r5 = r5 + a3 * a3 + + local r6 = a1 * a6 * 2 + r6 = r6 + a2 * a5 * 2 + r6 = r6 + a3 * a4 * 2 + + local r7 = a1 * a7 * 2 + r7 = r7 + a2 * a6 * 2 + r7 = r7 + a3 * a5 * 2 + r7 = r7 + a4 * a4 + + local r8 = a1 * a8 * 2 + r8 = r8 + a2 * a7 * 2 + r8 = r8 + a3 * a6 * 2 + r8 = r8 + a4 * a5 * 2 + + local r9 = a1 * a9 * 2 + r9 = r9 + a2 * a8 * 2 + r9 = r9 + a3 * a7 * 2 + r9 = r9 + a4 * a6 * 2 + r9 = r9 + a5 * a5 + + local r10 = a1 * a10 * 2 + r10 = r10 + a2 * a9 * 2 + r10 = r10 + a3 * a8 * 2 + r10 = r10 + a4 * a7 * 2 + r10 = r10 + a5 * a6 * 2 + + local r11 = a1 * a11 * 2 + r11 = r11 + a2 * a10 * 2 + r11 = r11 + a3 * a9 * 2 + r11 = r11 + a4 * a8 * 2 + r11 = r11 + a5 * a7 * 2 + r11 = r11 + a6 * a6 + + local r12 = a1 * a12 * 2 + r12 = r12 + a2 * a11 * 2 + r12 = r12 + a3 * a10 * 2 + r12 = r12 + a4 * a9 * 2 + r12 = r12 + a5 * a8 * 2 + r12 = r12 + a6 * a7 * 2 + + local r13 = a2 * a12 * 2 + r13 = r13 + a3 * a11 * 2 + r13 = r13 + a4 * a10 * 2 + r13 = r13 + a5 * a9 * 2 + r13 = r13 + a6 * a8 * 2 + r13 = r13 + a7 * a7 + + local r14 = a3 * a12 * 2 + r14 = r14 + a4 * a11 * 2 + r14 = r14 + a5 * a10 * 2 + r14 = r14 + a6 * a9 * 2 + r14 = r14 + a7 * a8 * 2 + + local r15 = a4 * a12 * 2 + r15 = r15 + a5 * a11 * 2 + r15 = r15 + a6 * a10 * 2 + r15 = r15 + a7 * a9 * 2 + r15 = r15 + a8 * a8 + + local r16 = a5 * a12 * 2 + r16 = r16 + a6 * a11 * 2 + r16 = r16 + a7 * a10 * 2 + r16 = r16 + a8 * a9 * 2 + + local r17 = a6 * a12 * 2 + r17 = r17 + a7 * a11 * 2 + r17 = r17 + a8 * a10 * 2 + r17 = r17 + a9 * a9 + + local r18 = a7 * a12 * 2 + r18 = r18 + a8 * a11 * 2 + r18 = r18 + a9 * a10 * 2 + + local r19 = a8 * a12 * 2 + r19 = r19 + a9 * a11 * 2 + r19 = r19 + a10 * a10 + + local r20 = a9 * a12 * 2 + r20 = r20 + a10 * a11 * 2 + + local r21 = a10 * a12 * 2 + r21 = r21 + a11 * a11 + + local r22 = a11 * a12 * 2 + + local r23 = a12 * a12 + + local r24 = 0 + + r2 = r2 + (r1 / m) + r2 = r2 - r2 % 1 + r1 = r1 % m + r3 = r3 + (r2 / m) + r3 = r3 - r3 % 1 + r2 = r2 % m + r4 = r4 + (r3 / m) + r4 = r4 - r4 % 1 + r3 = r3 % m + r5 = r5 + (r4 / m) + r5 = r5 - r5 % 1 + r4 = r4 % m + r6 = r6 + (r5 / m) + r6 = r6 - r6 % 1 + r5 = r5 % m + r7 = r7 + (r6 / m) + r7 = r7 - r7 % 1 + r6 = r6 % m + r8 = r8 + (r7 / m) + r8 = r8 - r8 % 1 + r7 = r7 % m + r9 = r9 + (r8 / m) + r9 = r9 - r9 % 1 + r8 = r8 % m + r10 = r10 + (r9 / m) + r10 = r10 - r10 % 1 + r9 = r9 % m + r11 = r11 + (r10 / m) + r11 = r11 - r11 % 1 + r10 = r10 % m + r12 = r12 + (r11 / m) + r12 = r12 - r12 % 1 + r11 = r11 % m + r13 = r13 + (r12 / m) + r13 = r13 - r13 % 1 + r12 = r12 % m + r14 = r14 + (r13 / m) + r14 = r14 - r14 % 1 + r13 = r13 % m + r15 = r15 + (r14 / m) + r15 = r15 - r15 % 1 + r14 = r14 % m + r16 = r16 + (r15 / m) + r16 = r16 - r16 % 1 + r15 = r15 % m + r17 = r17 + (r16 / m) + r17 = r17 - r17 % 1 + r16 = r16 % m + r18 = r18 + (r17 / m) + r18 = r18 - r18 % 1 + r17 = r17 % m + r19 = r19 + (r18 / m) + r19 = r19 - r19 % 1 + r18 = r18 % m + r20 = r20 + (r19 / m) + r20 = r20 - r20 % 1 + r19 = r19 % m + r21 = r21 + (r20 / m) + r21 = r21 - r21 % 1 + r20 = r20 % m + r22 = r22 + (r21 / m) + r22 = r22 - r22 % 1 + r21 = r21 % m + r23 = r23 + (r22 / m) + r23 = r23 - r23 % 1 + r22 = r22 % m + r24 = r24 + (r23 / m) + r24 = r24 - r24 % 1 + r23 = r23 % m + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return REDC(result) +end + +local function mont(a) + return mul(a, r2) +end + +local function invMont(a) + local a = {unpack(a)} + + for i = 13, 24 do + a[i] = 0 + end + + return REDC(a) +end + +return { + eq = eq, + add = add, + shr = shr, + sub192 = sub192, + sub = sub, + mul = mul, + sqr = sqr, + mont = mont, + invMont = invMont, +} diff --git a/sys/apis/crypto/ecc/fq.lua b/sys/apis/crypto/ecc/fq.lua new file mode 100644 index 0000000..12e6388 --- /dev/null +++ b/sys/apis/crypto/ecc/fq.lua @@ -0,0 +1,741 @@ +-- Fq Integer Arithmetic + +local n = 0xffff +local m = 0x10000 + +local q = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532} +local qn = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +local function eq(a, b) + for i = 1, 12 do + if a[i] ~= b[i] then + return false + end + end + + return true +end + +local function cmp(a, b) + for i = 12, 1, -1 do + if a[i] > b[i] then + return 1 + elseif a[i] < b[i] then + return -1 + end + end + + return 0 +end + +local function cmp384(a, b) + for i = 24, 1, -1 do + if a[i] > b[i] then + return 1 + elseif a[i] < b[i] then + return -1 + end + end + + return 0 +end + +local function bytes(x) + local result = {} + + for i = 0, 11 do + local m = x[i + 1] % 256 + result[2 * i + 1] = m + result[2 * i + 2] = (x[i + 1] - m) / 256 + end + + return result +end + +local function fromBytes(enc) + local result = {} + + for i = 0, 11 do + result[i + 1] = enc[2 * i + 1] % 256 + result[i + 1] = result[i + 1] + enc[2 * i + 2] * 256 + end + + return result +end + +local function sub192(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return result +end + +local function reduce(a) + local result = {unpack(a)} + + if cmp(result, q) >= 0 then + result = sub192(result, q) + end + + return result +end + +local function add(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return reduce(result) +end + +local function sub(a, b) + local result = sub192(a, b) + + if result[12] < 0 then + result = add(result, q) + end + + return result +end + +local function add384(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + local r13 = a[13] + b[13] + local r14 = a[14] + b[14] + local r15 = a[15] + b[15] + local r16 = a[16] + b[16] + local r17 = a[17] + b[17] + local r18 = a[18] + b[18] + local r19 = a[19] + b[19] + local r20 = a[20] + b[20] + local r21 = a[21] + b[21] + local r22 = a[22] + b[22] + local r23 = a[23] + b[23] + local r24 = a[24] + b[24] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + if r12 > n then + r13 = r13 + 1 + r12 = r12 - m + end + if r13 > n then + r14 = r14 + 1 + r13 = r13 - m + end + if r14 > n then + r15 = r15 + 1 + r14 = r14 - m + end + if r15 > n then + r16 = r16 + 1 + r15 = r15 - m + end + if r16 > n then + r17 = r17 + 1 + r16 = r16 - m + end + if r17 > n then + r18 = r18 + 1 + r17 = r17 - m + end + if r18 > n then + r19 = r19 + 1 + r18 = r18 - m + end + if r19 > n then + r20 = r20 + 1 + r19 = r19 - m + end + if r20 > n then + r21 = r21 + 1 + r20 = r20 - m + end + if r21 > n then + r22 = r22 + 1 + r21 = r21 - m + end + if r22 > n then + r23 = r23 + 1 + r22 = r22 - m + end + if r23 > n then + r24 = r24 + 1 + r23 = r23 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function sub384(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + local r13 = a[13] - b[13] + local r14 = a[14] - b[14] + local r15 = a[15] - b[15] + local r16 = a[16] - b[16] + local r17 = a[17] - b[17] + local r18 = a[18] - b[18] + local r19 = a[19] - b[19] + local r20 = a[20] - b[20] + local r21 = a[21] - b[21] + local r22 = a[22] - b[22] + local r23 = a[23] - b[23] + local r24 = a[24] - b[24] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + if r12 < 0 then + r13 = r13 - 1 + r12 = r12 + m + end + if r13 < 0 then + r14 = r14 - 1 + r13 = r13 + m + end + if r14 < 0 then + r15 = r15 - 1 + r14 = r14 + m + end + if r15 < 0 then + r16 = r16 - 1 + r15 = r15 + m + end + if r16 < 0 then + r17 = r17 - 1 + r16 = r16 + m + end + if r17 < 0 then + r18 = r18 - 1 + r17 = r17 + m + end + if r18 < 0 then + r19 = r19 - 1 + r18 = r18 + m + end + if r19 < 0 then + r20 = r20 - 1 + r19 = r19 + m + end + if r20 < 0 then + r21 = r21 - 1 + r20 = r20 + m + end + if r21 < 0 then + r22 = r22 - 1 + r21 = r21 + m + end + if r22 < 0 then + r23 = r23 - 1 + r22 = r22 + m + end + if r23 < 0 then + r24 = r24 - 1 + r23 = r23 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function mul384(a, b) + local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 = unpack(a) + local b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12 = unpack(b) + + local r1 = a1 * b1 + + local r2 = a1 * b2 + r2 = r2 + a2 * b1 + + local r3 = a1 * b3 + r3 = r3 + a2 * b2 + r3 = r3 + a3 * b1 + + local r4 = a1 * b4 + r4 = r4 + a2 * b3 + r4 = r4 + a3 * b2 + r4 = r4 + a4 * b1 + + local r5 = a1 * b5 + r5 = r5 + a2 * b4 + r5 = r5 + a3 * b3 + r5 = r5 + a4 * b2 + r5 = r5 + a5 * b1 + + local r6 = a1 * b6 + r6 = r6 + a2 * b5 + r6 = r6 + a3 * b4 + r6 = r6 + a4 * b3 + r6 = r6 + a5 * b2 + r6 = r6 + a6 * b1 + + local r7 = a1 * b7 + r7 = r7 + a2 * b6 + r7 = r7 + a3 * b5 + r7 = r7 + a4 * b4 + r7 = r7 + a5 * b3 + r7 = r7 + a6 * b2 + r7 = r7 + a7 * b1 + + local r8 = a1 * b8 + r8 = r8 + a2 * b7 + r8 = r8 + a3 * b6 + r8 = r8 + a4 * b5 + r8 = r8 + a5 * b4 + r8 = r8 + a6 * b3 + r8 = r8 + a7 * b2 + r8 = r8 + a8 * b1 + + local r9 = a1 * b9 + r9 = r9 + a2 * b8 + r9 = r9 + a3 * b7 + r9 = r9 + a4 * b6 + r9 = r9 + a5 * b5 + r9 = r9 + a6 * b4 + r9 = r9 + a7 * b3 + r9 = r9 + a8 * b2 + r9 = r9 + a9 * b1 + + local r10 = a1 * b10 + r10 = r10 + a2 * b9 + r10 = r10 + a3 * b8 + r10 = r10 + a4 * b7 + r10 = r10 + a5 * b6 + r10 = r10 + a6 * b5 + r10 = r10 + a7 * b4 + r10 = r10 + a8 * b3 + r10 = r10 + a9 * b2 + r10 = r10 + a10 * b1 + + local r11 = a1 * b11 + r11 = r11 + a2 * b10 + r11 = r11 + a3 * b9 + r11 = r11 + a4 * b8 + r11 = r11 + a5 * b7 + r11 = r11 + a6 * b6 + r11 = r11 + a7 * b5 + r11 = r11 + a8 * b4 + r11 = r11 + a9 * b3 + r11 = r11 + a10 * b2 + r11 = r11 + a11 * b1 + + local r12 = a1 * b12 + r12 = r12 + a2 * b11 + r12 = r12 + a3 * b10 + r12 = r12 + a4 * b9 + r12 = r12 + a5 * b8 + r12 = r12 + a6 * b7 + r12 = r12 + a7 * b6 + r12 = r12 + a8 * b5 + r12 = r12 + a9 * b4 + r12 = r12 + a10 * b3 + r12 = r12 + a11 * b2 + r12 = r12 + a12 * b1 + + local r13 = a2 * b12 + r13 = r13 + a3 * b11 + r13 = r13 + a4 * b10 + r13 = r13 + a5 * b9 + r13 = r13 + a6 * b8 + r13 = r13 + a7 * b7 + r13 = r13 + a8 * b6 + r13 = r13 + a9 * b5 + r13 = r13 + a10 * b4 + r13 = r13 + a11 * b3 + r13 = r13 + a12 * b2 + + local r14 = a3 * b12 + r14 = r14 + a4 * b11 + r14 = r14 + a5 * b10 + r14 = r14 + a6 * b9 + r14 = r14 + a7 * b8 + r14 = r14 + a8 * b7 + r14 = r14 + a9 * b6 + r14 = r14 + a10 * b5 + r14 = r14 + a11 * b4 + r14 = r14 + a12 * b3 + + local r15 = a4 * b12 + r15 = r15 + a5 * b11 + r15 = r15 + a6 * b10 + r15 = r15 + a7 * b9 + r15 = r15 + a8 * b8 + r15 = r15 + a9 * b7 + r15 = r15 + a10 * b6 + r15 = r15 + a11 * b5 + r15 = r15 + a12 * b4 + + local r16 = a5 * b12 + r16 = r16 + a6 * b11 + r16 = r16 + a7 * b10 + r16 = r16 + a8 * b9 + r16 = r16 + a9 * b8 + r16 = r16 + a10 * b7 + r16 = r16 + a11 * b6 + r16 = r16 + a12 * b5 + + local r17 = a6 * b12 + r17 = r17 + a7 * b11 + r17 = r17 + a8 * b10 + r17 = r17 + a9 * b9 + r17 = r17 + a10 * b8 + r17 = r17 + a11 * b7 + r17 = r17 + a12 * b6 + + local r18 = a7 * b12 + r18 = r18 + a8 * b11 + r18 = r18 + a9 * b10 + r18 = r18 + a10 * b9 + r18 = r18 + a11 * b8 + r18 = r18 + a12 * b7 + + local r19 = a8 * b12 + r19 = r19 + a9 * b11 + r19 = r19 + a10 * b10 + r19 = r19 + a11 * b9 + r19 = r19 + a12 * b8 + + local r20 = a9 * b12 + r20 = r20 + a10 * b11 + r20 = r20 + a11 * b10 + r20 = r20 + a12 * b9 + + local r21 = a10 * b12 + r21 = r21 + a11 * b11 + r21 = r21 + a12 * b10 + + local r22 = a11 * b12 + r22 = r22 + a12 * b11 + + local r23 = a12 * b12 + + local r24 = 0 + + r2 = r2 + (r1 / m) + r2 = r2 - r2 % 1 + r1 = r1 % m + r3 = r3 + (r2 / m) + r3 = r3 - r3 % 1 + r2 = r2 % m + r4 = r4 + (r3 / m) + r4 = r4 - r4 % 1 + r3 = r3 % m + r5 = r5 + (r4 / m) + r5 = r5 - r5 % 1 + r4 = r4 % m + r6 = r6 + (r5 / m) + r6 = r6 - r6 % 1 + r5 = r5 % m + r7 = r7 + (r6 / m) + r7 = r7 - r7 % 1 + r6 = r6 % m + r8 = r8 + (r7 / m) + r8 = r8 - r8 % 1 + r7 = r7 % m + r9 = r9 + (r8 / m) + r9 = r9 - r9 % 1 + r8 = r8 % m + r10 = r10 + (r9 / m) + r10 = r10 - r10 % 1 + r9 = r9 % m + r11 = r11 + (r10 / m) + r11 = r11 - r11 % 1 + r10 = r10 % m + r12 = r12 + (r11 / m) + r12 = r12 - r12 % 1 + r11 = r11 % m + r13 = r13 + (r12 / m) + r13 = r13 - r13 % 1 + r12 = r12 % m + r14 = r14 + (r13 / m) + r14 = r14 - r14 % 1 + r13 = r13 % m + r15 = r15 + (r14 / m) + r15 = r15 - r15 % 1 + r14 = r14 % m + r16 = r16 + (r15 / m) + r16 = r16 - r16 % 1 + r15 = r15 % m + r17 = r17 + (r16 / m) + r17 = r17 - r17 % 1 + r16 = r16 % m + r18 = r18 + (r17 / m) + r18 = r18 - r18 % 1 + r17 = r17 % m + r19 = r19 + (r18 / m) + r19 = r19 - r19 % 1 + r18 = r18 % m + r20 = r20 + (r19 / m) + r20 = r20 - r20 % 1 + r19 = r19 % m + r21 = r21 + (r20 / m) + r21 = r21 - r21 % 1 + r20 = r20 % m + r22 = r22 + (r21 / m) + r22 = r22 - r22 % 1 + r21 = r21 % m + r23 = r23 + (r22 / m) + r23 = r23 - r23 % 1 + r22 = r22 % m + r24 = r24 + (r23 / m) + r24 = r24 - r24 % 1 + r23 = r23 % m + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function reduce384(a) + local result = {unpack(a)} + + while cmp384(result, qn) >= 0 do + local qn = {unpack(qn)} + local qn2 = add384(qn, qn) + while cmp384(result, qn2) > 0 do + qn = qn2 + qn2 = add384(qn2, qn2) + end + result = sub384(result, qn) + end + + result = {unpack(result, 1, 12)} + + return result +end + +local function mul(a, b) + return reduce384(mul384(a, b)) +end + +return { + eq = eq, + cmp = cmp, + bytes = bytes, + fromBytes = fromBytes, + reduce = reduce, + add = add, + sub = sub, + mul = mul, +} diff --git a/sys/apis/sha1.lua b/sys/apis/crypto/sha1.lua similarity index 100% rename from sys/apis/sha1.lua rename to sys/apis/crypto/sha1.lua diff --git a/sys/apis/sha2.lua b/sys/apis/crypto/sha2.lua similarity index 100% rename from sys/apis/sha2.lua rename to sys/apis/crypto/sha2.lua diff --git a/sys/apis/socket.lua b/sys/apis/socket.lua index fd73abd..aeed53d 100644 --- a/sys/apis/socket.lua +++ b/sys/apis/socket.lua @@ -1,4 +1,4 @@ -local Crypto = require('crypto') +local Crypto = require('crypto.chacha20') local Security = require('security') local Util = require('util') diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 4f6de88..1a30f72 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -3,7 +3,7 @@ local Config = require('config') local Event = require('event') local NFT = require('nft') local Packages = require('packages') -local SHA2 = require('sha2') +local SHA2 = require('crypto.sha2') local Tween = require('ui.tween') local UI = require('ui') local Util = require('util') diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 631c248..f2b9dba 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -1,7 +1,7 @@ local Ansi = require('ansi') local Config = require('config') local Security = require('security') -local SHA2 = require('sha2') +local SHA2 = require('crypto.sha2') local UI = require('ui') local colors = _G.colors diff --git a/sys/apps/network/trust.lua b/sys/apps/network/trust.lua index 9d12c3a..8294278 100644 --- a/sys/apps/network/trust.lua +++ b/sys/apps/network/trust.lua @@ -1,4 +1,4 @@ -local Crypto = require('crypto') +local Crypto = require('crypto.chacha20') local Event = require('event') local Security = require('security') local Socket = require('socket') diff --git a/sys/apps/password.lua b/sys/apps/password.lua index 97ed6a4..6beb700 100644 --- a/sys/apps/password.lua +++ b/sys/apps/password.lua @@ -1,5 +1,5 @@ local Security = require('security') -local SHA2 = require('sha2') +local SHA2 = require('crypto.sha2') local Terminal = require('terminal') local password = Terminal.readPassword('Enter new password: ') diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 2e74430..7ad5769 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -1,5 +1,5 @@ local Security = require('security') -local SHA2 = require('sha2') +local SHA2 = require('crypto.sha2') local UI = require('ui') local colors = _G.colors diff --git a/sys/apps/trust.lua b/sys/apps/trust.lua index 61b3526..90c01d4 100644 --- a/sys/apps/trust.lua +++ b/sys/apps/trust.lua @@ -1,6 +1,6 @@ -local Crypto = require('crypto') +local Crypto = require('crypto.chacha20') local Security = require('security') -local SHA2 = require('sha2') +local SHA2 = require('crypto.sha2') local Socket = require('socket') local Terminal = require('terminal') -- 2.49.1 From 10fca522903943d7fb09ca6edd6d109eb733265d Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Mon, 6 May 2019 01:48:31 -0400 Subject: [PATCH 012/236] Fixes --- sys/apis/crypto/chacha20.lua | 4 ++-- sys/apis/crypto/ecc/ecc.lua | 2 +- sys/apis/crypto/sha2.lua | 14 ++++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/sys/apis/crypto/chacha20.lua b/sys/apis/crypto/chacha20.lua index 4d8f72d..3c05ae6 100644 --- a/sys/apis/crypto/chacha20.lua +++ b/sys/apis/crypto/chacha20.lua @@ -1,8 +1,8 @@ -- Chacha20 cipher in ComputerCraft -- By Anavrins -local sha2 = require("crypto.sha2") -local util = require("util") +local sha2 = require('crypto.sha2') +local util = require('util') local Crypto = {} local ROUNDS = 20 -- Adjust this for speed tradeoff diff --git a/sys/apis/crypto/ecc/ecc.lua b/sys/apis/crypto/ecc/ecc.lua index 030ce61..ffaf852 100644 --- a/sys/apis/crypto/ecc/ecc.lua +++ b/sys/apis/crypto/ecc/ecc.lua @@ -1,4 +1,4 @@ -local fq = require('crypto.ecc.fp') +local fq = require('crypto.ecc.fq') local elliptic = require('crypto.ecc.elliptic') local sha256 = require('crypto.sha2') diff --git a/sys/apis/crypto/sha2.lua b/sys/apis/crypto/sha2.lua index 6c1660b..162f5cb 100644 --- a/sys/apis/crypto/sha2.lua +++ b/sys/apis/crypto/sha2.lua @@ -1,8 +1,6 @@ -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft -- By Anavrins -local sha2 = {} - local mod32 = 2^32 local band = bit32 and bit32.band or bit.band local bnot = bit32 and bit32.bnot or bit.bnot @@ -121,7 +119,7 @@ local function toBytes(t, n) return setmetatable(b, mt) end -function sha2.digest(data) +local function digest(data) local data = data or "" data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} @@ -131,7 +129,7 @@ function sha2.digest(data) return toBytes(C, 8) end -function sha2.hmac(data, key) +local function hmac(data, key) local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)} @@ -162,7 +160,7 @@ function sha2.hmac(data, key) return digest(padded_key) end -function sha2.pbkdf2(pass, salt, iter, dklen) +local function pbkdf2(pass, salt, iter, dklen) local salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} local hashlen = 32 local dklen = dklen or 32 @@ -192,4 +190,8 @@ function sha2.pbkdf2(pass, salt, iter, dklen) return setmetatable(out, mt) end -return sha2 \ No newline at end of file +return { + digest = digest, + hmac = hmac, + pbkdf2 = pbkdf2, +} -- 2.49.1 From 43163053a58193b81d37c52f369f849d6a3125c3 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 12 May 2019 10:02:46 -0400 Subject: [PATCH 013/236] Lua: timing --- sys/apis/map.lua | 2 ++ sys/apis/util.lua | 3 +++ sys/apps/Lua.lua | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sys/apis/map.lua b/sys/apis/map.lua index 9e4ab93..a6b033c 100644 --- a/sys/apis/map.lua +++ b/sys/apis/map.lua @@ -6,6 +6,8 @@ local Map = { } -- TODO: refactor Map.merge = Util.merge Map.shallowCopy = Util.shallowCopy +Map.find = Util.find +Map.filter = Util.filter function Map.removeMatches(t, values) local function matchAll(entry) diff --git a/sys/apis/util.lua b/sys/apis/util.lua index f1f5a8d..e774ed9 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -77,6 +77,9 @@ function Util.tostring(pattern, ...) end if type(pattern) == 'string' then + if select('#', ...) == 0 then + return pattern + end return _sformat(pattern, ...) elseif type(pattern) == 'table' then return serialize(pattern, term.current().getSize()) diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 5627d6c..4eba8ab 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -294,6 +294,7 @@ end function page:rawExecute(s) local fn, m local wrapped + local t = os.clock() fn = load('return (' ..s.. ')', 'lua', nil, sandboxEnv) @@ -319,7 +320,7 @@ function page:rawExecute(s) local bg, fg = term.getBackgroundColor(), term.getTextColor() term.setTextColor(colors.cyan) term.setBackgroundColor(colors.black) - term.write(string.format('out [%d]: ', counter)) + term.write(string.format('out [%.2f]: ', os.clock() - t)) term.setBackgroundColor(bg) term.setTextColor(fg) Util.print(m or 'nil') -- 2.49.1 From 310879a801ec84a557dfdcd04f1a35dfe7ea498c Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 20 May 2019 15:19:39 -0400 Subject: [PATCH 014/236] sanitize discovery messages --- sys/apps/network/snmp.lua | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index dcc7bff..0e69a03 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -121,20 +121,26 @@ print('discovery: listening on port 999') Event.on('modem_message', function(_, _, sport, id, info, distance) if sport == 999 and tonumber(id) and type(info) == 'table' then - if not network[id] then - network[id] = { } - end - Util.merge(network[id], info) - network[id].distance = distance - network[id].timestamp = os.clock() + if info.label and info.id and + type(info.label) == 'string' and type(info.id) == 'number' then - if not network[id].label then - network[id].label = 'unknown' - end + if not network[id] then + network[id] = { } + end + Util.merge(network[id], info) + network[id].distance = type(distance) == 'number' and distance + network[id].timestamp = os.clock() - if not network[id].active then - network[id].active = true - os.queueEvent('network_attach', network[id]) + if not network[id].label then + network[id].label = 'unknown' + end + + if not network[id].active then + network[id].active = true + os.queueEvent('network_attach', network[id]) + end + else + print('discovery: Invalid alive message ' .. id) end end end) -- 2.49.1 From 82ec4db50fc9aa0ac072d81b2d473c6f0e573ee3 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 26 May 2019 11:05:37 -0400 Subject: [PATCH 015/236] input rework - again --- sys/apis/input.lua | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sys/apis/input.lua b/sys/apis/input.lua index 4134461..ee003a0 100644 --- a/sys/apis/input.lua +++ b/sys/apis/input.lua @@ -92,28 +92,32 @@ function input:translate(event, code, p1, p2) return { code = ch } end elseif code then - self.state[code] = true - self.fired = false + --self.fired = true + local ch = input:toCode(keys.getName(code), code) + if #ch ~= 1 then + return { code = ch } + end +-- self.state[code] = true end elseif event == 'char' then local combo = isCombo() - if not self.fired then + --if not self.fired then if combo or not (keyboard.state[keys.leftCtrl] or keyboard.state[keys.rightCtrl]) then self.fired = not combo return { code = event, ch = code } - end + --end -- return { code = event, ch = input:toCode(code) } end - elseif event == 'key_up' then + elseif event == 'key_upx' then if not self.fired then - if self.state[code] then + --if self.state[code] then self.fired = true local ch = input:toCode(keys.getName(code), code) self.state[code] = nil return { code = ch } - end + --end end self.state[code] = nil -- 2.49.1 From 3c22a872b0eb408fcbe28010533b9d06cbf5b925 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 18 Jun 2019 15:19:24 -0400 Subject: [PATCH 016/236] spaces->tabs + cleanup + pathing fixes --- sys/apis/bulkget.lua | 42 +- sys/apis/fs/redfs.lua | 61 --- sys/apis/fs/urlfs.lua | 6 +- sys/apis/json.lua | 727 ++++++++++++++++++++------- sys/apis/map.lua | 6 +- sys/apis/pathfind.lua | 25 +- sys/apis/point.lua | 15 +- sys/apis/proxy.lua | 32 -- sys/apis/rttp.lua | 95 ---- sys/apis/sha2.lua | 200 ++++++++ sys/apis/sync.lua | 86 ++-- sys/apis/trace.lua | 158 +++--- sys/apis/ui/components/Grid.lua | 29 +- sys/apis/util.lua | 2 +- sys/apps/Files.lua | 852 ++++++++++++++++---------------- sys/apps/Lua.lua | 19 +- sys/apps/Network.lua | 2 + sys/apps/ShellLauncher.lua | 36 +- sys/apps/Welcome.lua | 170 +++---- sys/apps/autorun.lua | 86 ++-- sys/apps/cedit.lua | 36 +- sys/apps/cshell.lua | 24 +- sys/apps/network/redserver.lua | 115 ----- sys/apps/network/snmp.lua | 2 +- sys/apps/sniff.lua | 69 --- sys/apps/system/cloud.lua | 78 +-- sys/apps/system/kiosk.lua | 14 +- sys/apps/system/launcher.lua | 112 ++--- sys/apps/system/path.lua | 108 ++-- sys/apps/system/requires.lua | 114 ++--- sys/apps/system/shell.lua | 180 +++---- sys/autorun/complete.lua | 36 +- sys/autorun/welcome.lua | 6 +- sys/init/3.modules.lua | 62 +-- sys/init/3.relay.lua | 30 +- sys/init/6.packages.lua | 4 +- sys/init/6.tl3.lua | 12 +- 37 files changed, 1948 insertions(+), 1703 deletions(-) delete mode 100644 sys/apis/fs/redfs.lua delete mode 100644 sys/apis/proxy.lua delete mode 100644 sys/apis/rttp.lua create mode 100644 sys/apis/sha2.lua delete mode 100644 sys/apps/network/redserver.lua delete mode 100644 sys/apps/sniff.lua diff --git a/sys/apis/bulkget.lua b/sys/apis/bulkget.lua index 1135847..a9679ef 100644 --- a/sys/apis/bulkget.lua +++ b/sys/apis/bulkget.lua @@ -5,29 +5,29 @@ local parallel = _G.parallel local BulkGet = { } function BulkGet.download(list, callback) - local t = { } - local failed = false + local t = { } + local failed = false - for _ = 1, 5 do - table.insert(t, function() - while true do - local entry = table.remove(list) - if not entry then - break - end - local s, m = Util.download(entry.url, entry.path) - if not s then - failed = true - end - callback(entry, s, m) - if failed then - break - end - end - end) - end + for _ = 1, 5 do + table.insert(t, function() + while true do + local entry = table.remove(list) + if not entry then + break + end + local s, m = Util.download(entry.url, entry.path) + if not s then + failed = true + end + callback(entry, s, m) + if failed then + break + end + end + end) + end - parallel.waitForAll(table.unpack(t)) + parallel.waitForAll(table.unpack(t)) end return BulkGet diff --git a/sys/apis/fs/redfs.lua b/sys/apis/fs/redfs.lua deleted file mode 100644 index bbd981a..0000000 --- a/sys/apis/fs/redfs.lua +++ /dev/null @@ -1,61 +0,0 @@ ---[[ - Mount a readonly file system from another computer across rednet. The - target computer must be running OpusOS or redserver. Dissimlar to samba - in that a snapshot of the target is taken upon mounting - making this - faster. - - Useful for mounting a non-changing directory tree. - - Syntax: - rttp:///directory/subdir - - Examples: - rttp://12/usr/etc - rttp://8/usr -]]-- - -local rttp = require('rttp') - -local fs = _G.fs - -local redfs = { } - -local function getListing(uri) - local success, response = rttp.get(uri .. '?recursive=true') - - if not success then - error(response) - end - - if response.statusCode ~= 200 then - error('Received response ' .. response.statusCode) - end - - local list = { } - for _,v in pairs(response.data) do - if not v.isDir then - list[v.path] = { - url = uri .. '/' .. v.path, - size = v.size, - } - end - end - - return list -end - -function redfs.mount(dir, uri) - if not uri then - error('redfs syntax: uri') - end - - local list = getListing(uri) - for path, entry in pairs(list) do - if not fs.exists(fs.combine(dir, path)) then - local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url) - node.size = entry.size - end - end -end - -return redfs diff --git a/sys/apis/fs/urlfs.lua b/sys/apis/fs/urlfs.lua index 6feb4f0..fc3b655 100644 --- a/sys/apis/fs/urlfs.lua +++ b/sys/apis/fs/urlfs.lua @@ -1,4 +1,4 @@ -local rttp = require('rttp') +--local rttp = require('rttp') local Util = require('util') local fs = _G.fs @@ -39,7 +39,6 @@ function urlfs.getDrive() end function urlfs.open(node, fn, fl) - if fl == 'w' or fl == 'wb' then fs.delete(fn) return fs.open(fn, fl) @@ -51,12 +50,15 @@ function urlfs.open(node, fn, fl) local c = node.cache if not c then + --[[ if node.url:match("^(rttps?:)") then local s, response = rttp.get(node.url) c = s and response.statusCode == 200 and response.data else c = Util.httpGet(node.url) end + ]]-- + c = Util.httpGet(node.url) if c then node.cache = c node.size = #c diff --git a/sys/apis/json.lua b/sys/apis/json.lua index 64f8825..04b1cd1 100644 --- a/sys/apis/json.lua +++ b/sys/apis/json.lua @@ -1,215 +1,584 @@ --- credit ElvishJerricco --- http://pastebin.com/raw.php?i=4nRg9CHU +-- Module options: +local register_global_module_table = false +local global_module_name = 'json' -local json = { } +--[==[ +NOTE: Modified to reduce file size. +See https://github.com/LuaDist/dkjson/blob/master/dkjson.lua +for full version. ------------------------------------------------------------------- utils -local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"} +David Kolf's JSON module for Lua 5.1/5.2 +Version 2.5 -local function isArray(t) - local max = 0 - for k,v in pairs(t) do - if type(k) ~= "number" then - return false - elseif k > max then - max = k - end - end - return max == #t +For the documentation see the corresponding readme.txt or visit +. + +You can contact the author by sending an e-mail to 'david' at the +domain 'dkolf.de'. + +Copyright (C) 2010-2014 David Heiko Kolf + +Refer to license located at https://github.com/LuaDist/dkjson/blob/master/dkjson.lua + +--]==] + +-- global dependencies: +local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = + pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset +local error, require, pcall, select = error, require, pcall, select +local floor, huge = math.floor, math.huge +local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = + string.rep, string.gsub, string.sub, string.byte, string.char, + string.find, string.len, string.format +local strmatch = string.match +local concat = table.concat + +local json = { version = "dkjson 2.5" } + +if register_global_module_table then + _G[global_module_name] = json end -local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true} -local function removeWhite(str) - while whites[str:sub(1, 1)] do - str = str:sub(2) - end - return str -end +local _ENV = nil -- blocking globals in Lua 5.2 ------------------------------------------------------------------- encoding +pcall (function() + -- Enable access to blocked metatables. + -- Don't worry, this module doesn't change anything in them. + local debmeta = require "debug".getmetatable + if debmeta then getmetatable = debmeta end +end) -local function encodeCommon(val, pretty, tabLevel, tTracking) - local str = "" +json.null = setmetatable ({}, { + __tojson = function () return "null" end +}) - -- Tabbing util - local function tab(s) - str = str .. ("\t"):rep(tabLevel) .. s - end - - local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc) - str = str .. bracket - if pretty then - str = str .. "\n" - tabLevel = tabLevel + 1 - end - for k,v in iterator(val) do - tab("") - loopFunc(k,v) - str = str .. "," - if pretty then str = str .. "\n" end - end - if pretty then - tabLevel = tabLevel - 1 - end - if str:sub(-2) == ",\n" then - str = str:sub(1, -3) .. "\n" - elseif str:sub(-1) == "," then - str = str:sub(1, -2) - end - tab(closeBracket) - end - - -- Table encoding - if type(val) == "table" then - assert(not tTracking[val], "Cannot encode a table holding itself recursively") - tTracking[val] = true - if isArray(val) then - arrEncoding(val, "[", "]", ipairs, function(k,v) - str = str .. encodeCommon(v, pretty, tabLevel, tTracking) - end) +local function isarray (tbl) + local max, n, arraylen = 0, 0, 0 + for k,v in pairs (tbl) do + if k == 'n' and type(v) == 'number' then + arraylen = v + if v > max then + max = v + end else - arrEncoding(val, "{", "}", pairs, function(k,v) - assert(type(k) == "string", "JSON object keys must be strings", 2) - str = str .. encodeCommon(k, pretty, tabLevel, tTracking) - str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking) - end) + if type(k) ~= 'number' or k < 1 or floor(k) ~= k then + return false + end + if k > max then + max = k + end + n = n + 1 end - -- String encoding - elseif type(val) == "string" then - str = '"' .. val:gsub("[%c\"\\]", controls) .. '"' - -- Number encoding - elseif type(val) == "number" or type(val) == "boolean" then - str = tostring(val) + end + if max > 10 and max > arraylen and max > n * 2 then + return false -- don't create an array with too many holes + end + return true, max +end + +local escapecodes = { + ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", + ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" +} + +local function escapeutf8 (uchar) + local value = escapecodes[uchar] + if value then + return value + end + local a, b, c, d = strbyte (uchar, 1, 4) + a, b, c, d = a or 0, b or 0, c or 0, d or 0 + if a <= 0x7f then + value = a + elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then + value = (a - 0xc0) * 0x40 + b - 0x80 + elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then + value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 + elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then + value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 else - error("JSON only supports arrays, objects, numbers, booleans, and strings", 2) + return "" end - return str -end - -function json.encode(val) - return encodeCommon(val, false, 0, {}) -end - -function json.encodePretty(val) - return encodeCommon(val, true, 0, {}) -end - -function json.encodeToFile(path, val) - local file = io.open(path, "w") - assert(file, "Unable to open file") - file:write(json.encodePretty(val)) - file:close() -end - ------------------------------------------------------------------- decoding - -local decodeControls = {} -for k,v in pairs(controls) do - decodeControls[v] = k -end - -local function parseBoolean(str) - if str:sub(1, 4) == "true" then - return true, removeWhite(str:sub(5)) + if value <= 0xffff then + return strformat ("\\u%.4x", value) + elseif value <= 0x10ffff then + -- encode as UTF-16 surrogate pair + value = value - 0x10000 + local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) + return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) else - return false, removeWhite(str:sub(6)) + return "" end end -local function parseNull(str) - return nil, removeWhite(str:sub(5)) -end - -local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true} -local function parseNumber(str) - local i = 1 - while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do - i = i + 1 +local function fsub (str, pattern, repl) + -- gsub always builds a new string in a buffer, even when no match + -- exists. First using find should be more efficient when most strings + -- don't contain the pattern. + if strfind (str, pattern) then + return gsub (str, pattern, repl) + else + return str end - local val = tonumber(str:sub(1, i - 1)) - str = removeWhite(str:sub(i)) - return val, str end -local function parseString(str) - str = str:sub(2) - local s = "" - while str:sub(1,1) ~= "\"" do - local next = str:sub(1,1) - str = str:sub(2) - assert(next ~= "\n", "Unclosed string") +local function quotestring (value) + -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js + value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) + if strfind (value, "[\194\216\220\225\226\239]") then + value = fsub (value, "\194[\128-\159\173]", escapeutf8) + value = fsub (value, "\216[\128-\132]", escapeutf8) + value = fsub (value, "\220\143", escapeutf8) + value = fsub (value, "\225\158[\180\181]", escapeutf8) + value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) + value = fsub (value, "\226\129[\160-\175]", escapeutf8) + value = fsub (value, "\239\187\191", escapeutf8) + value = fsub (value, "\239\191[\176-\191]", escapeutf8) + end + return "\"" .. value .. "\"" +end +json.quotestring = quotestring - if next == "\\" then - local escape = str:sub(1,1) - str = str:sub(2) +local function replace(str, o, n) + local i, j = strfind (str, o, 1, true) + if i then + return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) + else + return str + end +end - next = assert(decodeControls[next..escape], "Invalid escape character") +-- locale independent num2str and str2num functions +local decpoint, numfilter + +local function updatedecpoint () + decpoint = strmatch(tostring(0.5), "([^05+])") + -- build a filter that can be used to remove group separators + numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" +end + +updatedecpoint() + +local function num2str (num) + return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") +end + +local function str2num (str) + local num = tonumber(replace(str, ".", decpoint)) + if not num then + updatedecpoint() + num = tonumber(replace(str, ".", decpoint)) + end + return num +end + +local function addnewline2 (level, buffer, buflen) + buffer[buflen+1] = "\n" + buffer[buflen+2] = strrep (" ", level) + buflen = buflen + 2 + return buflen +end + +function json.addnewline (state) + if state.indent then + state.bufferlen = addnewline2 (state.level or 0, + state.buffer, state.bufferlen or #(state.buffer)) + end +end + +local encode2 -- forward declaration + +local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) + local kt = type (key) + if kt ~= 'string' and kt ~= 'number' then + return nil, "type '" .. kt .. "' is not supported as a key by JSON." + end + if prev then + buflen = buflen + 1 + buffer[buflen] = "," + end + if indent then + buflen = addnewline2 (level, buffer, buflen) + end + buffer[buflen+1] = quotestring (key) + buffer[buflen+2] = ":" + return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) +end + +local function appendcustom(res, buffer, state) + local buflen = state.bufferlen + if type (res) == 'string' then + buflen = buflen + 1 + buffer[buflen] = res + end + return buflen +end + +local function exception(reason, value, state, buffer, buflen, defaultmessage) + defaultmessage = defaultmessage or reason + local handler = state.exception + if not handler then + return nil, defaultmessage + else + state.bufferlen = buflen + local ret, msg = handler (reason, value, state, defaultmessage) + if not ret then return nil, msg or defaultmessage end + return appendcustom(ret, buffer, state) + end +end + +function json.encodeexception(reason, value, state, defaultmessage) + return quotestring("<" .. defaultmessage .. ">") +end + +encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) + local valtype = type (value) + local valmeta = getmetatable (value) + valmeta = type (valmeta) == 'table' and valmeta -- only tables + local valtojson = valmeta and valmeta.__tojson + if valtojson then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) end - - s = s .. next + tables[value] = true + state.bufferlen = buflen + local ret, msg = valtojson (value, state) + if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end + tables[value] = nil + buflen = appendcustom(ret, buffer, state) + elseif value == nil then + buflen = buflen + 1 + buffer[buflen] = "null" + elseif valtype == 'number' then + local s + if value ~= value or value >= huge or -value >= huge then + -- This is the behaviour of the original JSON implementation. + s = "null" + else + s = num2str (value) + end + buflen = buflen + 1 + buffer[buflen] = s + elseif valtype == 'boolean' then + buflen = buflen + 1 + buffer[buflen] = value and "true" or "false" + elseif valtype == 'string' then + buflen = buflen + 1 + buffer[buflen] = quotestring (value) + elseif valtype == 'table' then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + level = level + 1 + local isa, n = isarray (value) + if n == 0 and valmeta and valmeta.__jsontype == 'object' then + isa = false + end + local msg + if isa then -- JSON array + buflen = buflen + 1 + buffer[buflen] = "[" + for i = 1, n do + buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + if i < n then + buflen = buflen + 1 + buffer[buflen] = "," + end + end + buflen = buflen + 1 + buffer[buflen] = "]" + else -- JSON object + local prev = false + buflen = buflen + 1 + buffer[buflen] = "{" + local order = valmeta and valmeta.__jsonorder or globalorder + if order then + local used = {} + n = #order + for i = 1, n do + local k = order[i] + local v = value[k] + if v then + used[k] = true + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + prev = true -- add a seperator before the next element + end + end + for k,v in pairs (value) do + if not used[k] then + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + else -- unordered + for k,v in pairs (value) do + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + if indent then + buflen = addnewline2 (level - 1, buffer, buflen) + end + buflen = buflen + 1 + buffer[buflen] = "}" + end + tables[value] = nil + else + return exception ('unsupported type', value, state, buffer, buflen, + "type '" .. valtype .. "' is not supported by JSON.") end - return s, removeWhite(str:sub(2)) + return buflen end -function json.parseArray(str) - str = removeWhite(str:sub(2)) - - local val = {} - local i = 1 - while str:sub(1, 1) ~= "]" do - local v - v, str = json.parseValue(str) - val[i] = v - i = i + 1 - str = removeWhite(str) - end - str = removeWhite(str:sub(2)) - return val, str -end - -function json.parseValue(str) - local fchar = str:sub(1, 1) - if fchar == "{" then - return json.parseObject(str) - elseif fchar == "[" then - return json.parseArray(str) - elseif tonumber(fchar) ~= nil or numChars[fchar] then - return parseNumber(str) - elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then - return parseBoolean(str) - elseif fchar == "\"" then - return parseString(str) - elseif str:sub(1, 4) == "null" then - return parseNull(str) +function json.encode (value, state) + state = state or {} + local oldbuffer = state.buffer + local buffer = oldbuffer or {} + state.buffer = buffer + updatedecpoint() + local ret, msg = encode2 (value, state.indent, state.level or 0, + buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) + if not ret then + error (msg, 2) + elseif oldbuffer == buffer then + state.bufferlen = ret + return true + else + state.bufferlen = nil + state.buffer = nil + return concat (buffer) end end -function json.parseMember(str) - local k, val - k, str = json.parseValue(str) - val, str = json.parseValue(str) - return k, val, str -end - -function json.parseObject(str) - str = removeWhite(str:sub(2)) - - local val = {} - while str:sub(1, 1) ~= "}" do - local k, v - k, v, str = json.parseMember(str) - val[k] = v - str = removeWhite(str) +local function loc (str, where) + local line, pos, linepos = 1, 1, 0 + while true do + pos = strfind (str, "\n", pos, true) + if pos and pos < where then + line = line + 1 + linepos = pos + pos = pos + 1 + else + break + end end - str = removeWhite(str:sub(2)) - return val, str + return "line " .. line .. ", column " .. (where - linepos) end -function json.decode(str) - str = removeWhite(str) - return json.parseValue(str) +local function unterminated (str, what, where) + return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) end +local function scanwhite (str, pos) + while true do + pos = strfind (str, "%S", pos) + if not pos then return nil end + local sub2 = strsub (str, pos, pos + 1) + if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then + -- UTF-8 Byte Order Mark + pos = pos + 3 + elseif sub2 == "//" then + pos = strfind (str, "[\n\r]", pos + 2) + if not pos then return nil end + elseif sub2 == "/*" then + pos = strfind (str, "*/", pos + 2) + if not pos then return nil end + pos = pos + 2 + else + return pos + end + end +end + +local escapechars = { + ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", + ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" +} + +local function unichar (value) + if value < 0 then + return nil + elseif value <= 0x007f then + return strchar (value) + elseif value <= 0x07ff then + return strchar (0xc0 + floor(value/0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0xffff then + return strchar (0xe0 + floor(value/0x1000), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0x10ffff then + return strchar (0xf0 + floor(value/0x40000), + 0x80 + (floor(value/0x1000) % 0x40), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + else + return nil + end +end + +local function scanstring (str, pos) + local lastpos = pos + 1 + local buffer, n = {}, 0 + while true do + local nextpos = strfind (str, "[\"\\]", lastpos) + if not nextpos then + return unterminated (str, "string", pos) + end + if nextpos > lastpos then + n = n + 1 + buffer[n] = strsub (str, lastpos, nextpos - 1) + end + if strsub (str, nextpos, nextpos) == "\"" then + lastpos = nextpos + 1 + break + else + local escchar = strsub (str, nextpos + 1, nextpos + 1) + local value + if escchar == "u" then + value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) + if value then + local value2 + if 0xD800 <= value and value <= 0xDBff then + -- we have the high surrogate of UTF-16. Check if there is a + -- low surrogate escaped nearby to combine them. + if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then + value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) + if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then + value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 + else + value2 = nil -- in case it was out of range for a low surrogate + end + end + end + value = value and unichar (value) + if value then + if value2 then + lastpos = nextpos + 12 + else + lastpos = nextpos + 6 + end + end + end + end + if not value then + value = escapechars[escchar] or escchar + lastpos = nextpos + 2 + end + n = n + 1 + buffer[n] = value + end + end + if n == 1 then + return buffer[1], lastpos + elseif n > 1 then + return concat (buffer), lastpos + else + return "", lastpos + end +end + +local scanvalue -- forward declaration + +local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) + local len = strlen (str) + local tbl, n = {}, 0 + local pos = startpos + 1 + if what == 'object' then + setmetatable (tbl, objectmeta) + else + setmetatable (tbl, arraymeta) + end + while true do + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + local char = strsub (str, pos, pos) + if char == closechar then + return tbl, pos + 1 + end + local val1, err + val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + if char == ":" then + if val1 == nil then + return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" + end + pos = scanwhite (str, pos + 1) + if not pos then return unterminated (str, what, startpos) end + local val2 + val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + tbl[val1] = val2 + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + else + n = n + 1 + tbl[n] = val1 + end + if char == "," then + pos = pos + 1 + end + end +end + +scanvalue = function (str, pos, nullval, objectmeta, arraymeta) + pos = pos or 1 + pos = scanwhite (str, pos) + if not pos then + return nil, strlen (str) + 1, "no valid JSON value (reached the end)" + end + local char = strsub (str, pos, pos) + if char == "{" then + return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) + elseif char == "[" then + return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) + elseif char == "\"" then + return scanstring (str, pos) + else + local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) + if pstart then + local number = str2num (strsub (str, pstart, pend)) + if number then + return number, pend + 1 + end + end + pstart, pend = strfind (str, "^%a%w*", pos) + if pstart then + local name = strsub (str, pstart, pend) + if name == "true" then + return true, pend + 1 + elseif name == "false" then + return false, pend + 1 + elseif name == "null" then + return nullval, pend + 1 + end + end + return nil, pos, "no valid JSON value at " .. loc (str, pos) + end +end + +local function optionalmetatables(...) + if select("#", ...) > 0 then + return ... + else + return {__jsontype = 'object'}, {__jsontype = 'array'} + end +end + +function json.decode (str, pos, nullval, ...) + local objectmeta, arraymeta = optionalmetatables(...) + return scanvalue (str, pos, nullval, objectmeta, arraymeta) +end + +-- NOTE: added method - not in original source function json.decodeFromFile(path) local file = assert(fs.open(path, "r")) local decoded = json.decode(file.readAll()) diff --git a/sys/apis/map.lua b/sys/apis/map.lua index a6b033c..8380f38 100644 --- a/sys/apis/map.lua +++ b/sys/apis/map.lua @@ -19,9 +19,9 @@ function Map.removeMatches(t, values) return true end - for k,v in pairs(t) do - if matchAll(v) then - t[k] = nil + for _, key in pairs(Util.keys(t)) do + if matchAll(t[key]) then + t[key] = nil end end end diff --git a/sys/apis/pathfind.lua b/sys/apis/pathfind.lua index ffb5d25..f34c6ac 100644 --- a/sys/apis/pathfind.lua +++ b/sys/apis/pathfind.lua @@ -106,6 +106,17 @@ local function selectDestination(pts, box, grid) end end +local function updateCanvas(path) + local t = { } + for node in path:nodes() do + table.insert(t, { x = node.x, y = node.y, z = node.z }) + end + os.queueEvent('canvas', { + type = 'canvas_path', + data = t, + }) +end + local function pathTo(dest, options) local blocks = options.blocks or turtle.getState().blocks or { } local dests = options.dest or { dest } -- support alternative destinations @@ -156,6 +167,8 @@ local function pathTo(dest, options) if not path then Util.removeByValue(dests, dest) else + updateCanvas(path) + path:filter() for node in path:nodes() do @@ -173,11 +186,19 @@ local function pathTo(dest, options) -- use single turn method so the turtle doesn't turn around -- when encountering obstacles - if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then - --if not turtle.goto(pt) then + --if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then + pt.heading = nil + if not turtle.go(pt) then local bpt = Point.nearestTo(turtle.point, pt) + if turtle.getFuelLevel() == 0 then + return false, 'Out of fuel' + end table.insert(blocks, bpt) + os.queueEvent('canvas', { + type = 'canvas_barrier', + data = { bpt }, + }) -- really need to check if the block we ran into was a turtle. -- if so, this block should be temporary (1-2 secs) diff --git a/sys/apis/point.lua b/sys/apis/point.lua index 690b446..da1d15b 100644 --- a/sys/apis/point.lua +++ b/sys/apis/point.lua @@ -131,8 +131,11 @@ end function Point.calculateMoves(pta, ptb, distance) local heading = pta.heading local moves = distance or Point.turtleDistance(pta, ptb) + local weighted = moves + if (pta.heading % 2) == 0 and pta.z ~= ptb.z then moves = moves + 1 + weighted = weighted + .9 if ptb.heading and (ptb.heading % 2 == 1) then heading = ptb.heading elseif ptb.z > pta.z then @@ -142,6 +145,7 @@ function Point.calculateMoves(pta, ptb, distance) end elseif (pta.heading % 2) == 1 and pta.x ~= ptb.x then moves = moves + 1 + weighted = weighted + .9 if ptb.heading and (ptb.heading % 2 == 0) then heading = ptb.heading elseif ptb.x > pta.x then @@ -152,15 +156,18 @@ function Point.calculateMoves(pta, ptb, distance) end if not ptb.heading then - return moves, heading, moves + return moves, heading, weighted end + -- need to know if we are in digging mode + -- if so, we need to face blocks -- need a no-backwards flag + -- calc turns as slightly less than moves - local weighted = moves + -- local weighted = moves if heading ~= ptb.heading then local turns = Point.calculateTurns(heading, ptb.heading) moves = moves + turns - local wturns = { [0] = 0, [1] = .9, [2] = 1.9 } + local wturns = { [0] = 0, [1] = .9, [2] = 1.8 } weighted = weighted + wturns[turns] heading = ptb.heading end @@ -233,7 +240,7 @@ end function Point.nearestTo(pta, ptb) local heading - if pta.x < ptb.x then + if pta.x < ptb.x then heading = 0 elseif pta.z < ptb.z then heading = 1 diff --git a/sys/apis/proxy.lua b/sys/apis/proxy.lua deleted file mode 100644 index eb3d819..0000000 --- a/sys/apis/proxy.lua +++ /dev/null @@ -1,32 +0,0 @@ -local Socket = require('socket') - -local Proxy = { } - -function Proxy.create(remoteId, uri) - local socket, msg = Socket.connect(remoteId, 188) - - if not socket then - error(msg) - end - - socket.co = coroutine.running() - - socket:write(uri) - local methods = socket:read(2) or error('Timed out') - - local hijack = { } - for _,method in pairs(methods) do - hijack[method] = function(...) - socket:write({ method, ... }) - local resp = socket:read() - if not resp then - error('timed out: ' .. method) - end - return table.unpack(resp) - end - end - - return hijack, socket -end - -return Proxy diff --git a/sys/apis/rttp.lua b/sys/apis/rttp.lua deleted file mode 100644 index 8696e9e..0000000 --- a/sys/apis/rttp.lua +++ /dev/null @@ -1,95 +0,0 @@ -local device = _G.device -local os = _G.os - -local rttp = { } -local computerId = os.getComputerID() - -local function parse(url, default) - -- initialize default parameters - local parsed = {} - local authority - - for i,v in pairs(default or parsed) do parsed[i] = v end - -- remove whitespace - -- url = string.gsub(url, "%s", "") - -- Decode unreserved characters - url = string.gsub(url, "%%(%x%x)", function(hex) - local char = string.char(tonumber(hex, 16)) - if string.match(char, "[a-zA-Z0-9._~-]") then - return char - end - -- Hex encodings that are not unreserved must be preserved. - return nil - end) - -- get fragment - url = string.gsub(url, "#(.*)$", function(f) - parsed.fragment = f - return "" - end) - -- get scheme. Lower-case according to RFC 3986 section 3.1. - url = string.gsub(url, "^(%w[%w.+-]*):", - function(s) parsed.scheme = string.lower(s); return "" end) - -- get authority - url = string.gsub(url, "^//([^/]*)", function(n) - authority = n - return "" - end) - -- get query stringing - url = string.gsub(url, "%?(.*)", function(q) - parsed.query = q - return "" - end) - -- get params - url = string.gsub(url, "%;(.*)", function(p) - parsed.params = p - return "" - end) - - -- path is whatever was left - parsed.path = url - - -- Represents host:port, port = nil if not used. - if authority then - authority = string.gsub(authority, ":(%d+)$", - function(p) parsed.port = tonumber(p); return "" end) - if authority ~= "" then - parsed.host = authority - end - end - return parsed -end - -function rttp.get(url) - local modem = device.wireless_modem or error('Modem not found') - local parsed = parse(url, { port = 80 }) - - parsed.host = tonumber(parsed.host) or error('Invalid url') - - for i = 16384, 32767 do - if not modem.isOpen(i) then - modem.open(i) - local path = parsed.query and parsed.path .. '?' .. parsed.query or parsed.path - - modem.transmit(parsed.port, parsed.host, { - method = 'GET', - replyAddress = computerId, - replyPort = i, - path = path, - }) - local timerId = os.startTimer(3) - repeat - local event, id, dport, dhost, response = os.pullEvent() - if event == 'modem_message' and - dport == i and - dhost == computerId and - type(response) == 'table' then - modem.close(i) - return true, response - end - until event == 'timer' and id == timerId - return false, 'timeout' - end - end -end - -return rttp diff --git a/sys/apis/sha2.lua b/sys/apis/sha2.lua new file mode 100644 index 0000000..f7965bc --- /dev/null +++ b/sys/apis/sha2.lua @@ -0,0 +1,200 @@ +-- SHA-256, HMAC and PBKDF2 functions in ComputerCraft +-- By Anavrins + +local bit = _G.bit + +local mod32 = 2^32 +local band = bit32 and bit32.band or bit.band +local bnot = bit32 and bit32.bnot or bit.bnot +local bxor = bit32 and bit32.bxor or bit.bxor +local blshift = bit32 and bit32.lshift or bit.blshift +local upack = unpack + +local function rrotate(n, b) + local s = n/(2^b) + local f = s%1 + return (s-f) + f*mod32 +end +local function brshift(int, by) + local s = int / (2^by) + return s - s%1 +end + +local H = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, +} + +local K = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +} + +local function counter(incr) + local t1, t2 = 0, 0 + if 0xFFFFFFFF - t1 < incr then + t2 = t2 + 1 + t1 = incr - (0xFFFFFFFF - t1) - 1 + else t1 = t1 + incr + end + return t2, t1 +end + +local function BE_toInt(bs, i) + return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0) +end + +local function preprocess(data) + local len = #data + local proc = {} + data[#data+1] = 0x80 + while #data%64~=56 do data[#data+1] = 0 end + local blocks = math.ceil(#data/64) + for i = 1, blocks do + proc[i] = {} + for j = 1, 16 do + proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4)) + end + end + proc[blocks][15], proc[blocks][16] = counter(len*8) + return proc +end + +local function digestblock(w, C) + for j = 17, 64 do + --local v = w[j-15] + local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3)) + local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10)) + w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32 + end + local a, b, c, d, e, f, g, h = upack(C) + for j = 1, 64 do + local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25)) + local ch = bxor(band(e, f), band(bnot(e), g)) + local temp1 = (h + S1 + ch + K[j] + w[j])%mod32 + local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22)) + local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c)) + local temp2 = (S0 + maj)%mod32 + h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32 + end + C[1] = (C[1] + a)%mod32 + C[2] = (C[2] + b)%mod32 + C[3] = (C[3] + c)%mod32 + C[4] = (C[4] + d)%mod32 + C[5] = (C[5] + e)%mod32 + C[6] = (C[6] + f)%mod32 + C[7] = (C[7] + g)%mod32 + C[8] = (C[8] + h)%mod32 + return C +end + +local mt = { + __tostring = function(a) return string.char(unpack(a)) end, + __index = { + toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end, + isEqual = function(self, t) + if type(t) ~= "table" then return false end + if #self ~= #t then return false end + local ret = 0 + for i = 1, #self do + ret = bit32.bor(ret, bxor(self[i], t[i])) + end + return ret == 0 + end + } +} + +local function toBytes(t, n) + local b = {} + for i = 1, n do + b[(i-1)*4+1] = band(brshift(t[i], 24), 0xFF) + b[(i-1)*4+2] = band(brshift(t[i], 16), 0xFF) + b[(i-1)*4+3] = band(brshift(t[i], 8), 0xFF) + b[(i-1)*4+4] = band(t[i], 0xFF) + end + return setmetatable(b, mt) +end + +local function digest(data) + data = data or "" + data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} + + data = preprocess(data) + local C = {upack(H)} + for i = 1, #data do C = digestblock(data[i], C) end + return toBytes(C, 8) +end + +local function hmac(data, key) + data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} + key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)} + + local blocksize = 64 + + key = #key > blocksize and digest(key) or key + + local ipad = {} + local opad = {} + local padded_key = {} + + for i = 1, blocksize do + ipad[i] = bxor(0x36, key[i] or 0) + opad[i] = bxor(0x5C, key[i] or 0) + end + + for i = 1, #data do + ipad[blocksize+i] = data[i] + end + + ipad = digest(ipad) + + for i = 1, blocksize do + padded_key[i] = opad[i] + padded_key[blocksize+i] = ipad[i] + end + + return digest(padded_key) +end + +local function pbkdf2(pass, salt, iter, dklen) + local hashlen = 32 + local block = 1 + local out = {} + + dklen = dklen or 32 + salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} + + while dklen > 0 do + local ikey = {} + local isalt = {upack(salt)} + local clen = dklen > hashlen and hashlen or dklen + + isalt[#isalt+1] = band(brshift(block, 24), 0xFF) + isalt[#isalt+1] = band(brshift(block, 16), 0xFF) + isalt[#isalt+1] = band(brshift(block, 8), 0xFF) + isalt[#isalt+1] = band(block, 0xFF) + + for j = 1, iter do + isalt = hmac(isalt, pass) + for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end + if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end + end + dklen = dklen - clen + block = block+1 + for k = 1, clen do out[#out+1] = ikey[k] end + end + + return setmetatable(out, mt) +end + +return { + digest = digest, + hmac = hmac, + pbkdf2 = pbkdf2, +} \ No newline at end of file diff --git a/sys/apis/sync.lua b/sys/apis/sync.lua index 7987cb4..fd3402c 100644 --- a/sys/apis/sync.lua +++ b/sys/apis/sync.lua @@ -1,61 +1,61 @@ local Sync = { - syncLocks = { } + syncLocks = { } } local os = _G.os function Sync.sync(obj, fn) - local key = tostring(obj) - if Sync.syncLocks[key] then - local cos = tostring(coroutine.running()) - table.insert(Sync.syncLocks[key], cos) - repeat - local _, co = os.pullEvent('sync_lock') - until co == cos - else - Sync.syncLocks[key] = { } - end - local s, m = pcall(fn) - local co = table.remove(Sync.syncLocks[key], 1) - if co then - os.queueEvent('sync_lock', co) - else - Sync.syncLocks[key] = nil - end - if not s then - error(m) - end + local key = tostring(obj) + if Sync.syncLocks[key] then + local cos = tostring(coroutine.running()) + table.insert(Sync.syncLocks[key], cos) + repeat + local _, co = os.pullEvent('sync_lock') + until co == cos + else + Sync.syncLocks[key] = { } + end + local s, m = pcall(fn) + local co = table.remove(Sync.syncLocks[key], 1) + if co then + os.queueEvent('sync_lock', co) + else + Sync.syncLocks[key] = nil + end + if not s then + error(m) + end end function Sync.lock(obj) - local key = tostring(obj) - if Sync.syncLocks[key] then - local cos = tostring(coroutine.running()) - table.insert(Sync.syncLocks[key], cos) - repeat - local _, co = os.pullEvent('sync_lock') - until co == cos - else - Sync.syncLocks[key] = { } - end + local key = tostring(obj) + if Sync.syncLocks[key] then + local cos = tostring(coroutine.running()) + table.insert(Sync.syncLocks[key], cos) + repeat + local _, co = os.pullEvent('sync_lock') + until co == cos + else + Sync.syncLocks[key] = { } + end end function Sync.release(obj) - local key = tostring(obj) - if not Sync.syncLocks[key] then - error('Sync.release: Lock was not obtained', 2) - end - local co = table.remove(Sync.syncLocks[key], 1) - if co then - os.queueEvent('sync_lock', co) - else - Sync.syncLocks[key] = nil - end + local key = tostring(obj) + if not Sync.syncLocks[key] then + error('Sync.release: Lock was not obtained', 2) + end + local co = table.remove(Sync.syncLocks[key], 1) + if co then + os.queueEvent('sync_lock', co) + else + Sync.syncLocks[key] = nil + end end function Sync.isLocked(obj) - local key = tostring(obj) - return not not Sync.syncLocks[key] + local key = tostring(obj) + return not not Sync.syncLocks[key] end return Sync diff --git a/sys/apis/trace.lua b/sys/apis/trace.lua index 4323003..5673b6d 100644 --- a/sys/apis/trace.lua +++ b/sys/apis/trace.lua @@ -5,106 +5,106 @@ local type = type local debug_traceback = type(debug) == "table" and type(debug.traceback) == "function" and debug.traceback local function traceback(x) - -- Attempt to detect error() and error("xyz", 0). - -- This probably means they're erroring the program intentionally and so we - -- shouldn't display anything. - if x == nil or (type(x) == "string" and not x:find(":%d+:")) then - return x - end + -- Attempt to detect error() and error("xyz", 0). + -- This probably means they're erroring the program intentionally and so we + -- shouldn't display anything. + if x == nil or (type(x) == "string" and not x:find(":%d+:")) then + return x + end - if debug_traceback then - -- The parens are important, as they prevent a tail call occuring, meaning - -- the stack level is preserved. This ensures the code behaves identically - -- on LuaJ and PUC Lua. - return (debug_traceback(tostring(x), 2)) - else - local level = 3 - local out = { tostring(x), "stack traceback:" } - while true do - local _, msg = pcall(error, "", level) - if msg == "" then break end + if debug_traceback then + -- The parens are important, as they prevent a tail call occuring, meaning + -- the stack level is preserved. This ensures the code behaves identically + -- on LuaJ and PUC Lua. + return (debug_traceback(tostring(x), 2)) + else + local level = 3 + local out = { tostring(x), "stack traceback:" } + while true do + local _, msg = pcall(error, "", level) + if msg == "" then break end - out[#out + 1] = " " .. msg - level = level + 1 - end + out[#out + 1] = " " .. msg + level = level + 1 + end - return table.concat(out, "\n") - end + return table.concat(out, "\n") + end end local function trim_traceback(target, marker) - local ttarget, tmarker = {}, {} - for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end - for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end + local ttarget, tmarker = {}, {} + for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end + for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end - -- Trim identical suffixes - local t_len, m_len = #ttarget, #tmarker - while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do - table.remove(ttarget, t_len) - t_len, m_len = t_len - 1, m_len - 1 - end + -- Trim identical suffixes + local t_len, m_len = #ttarget, #tmarker + while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do + table.remove(ttarget, t_len) + t_len, m_len = t_len - 1, m_len - 1 + end - -- Trim elements from this file and xpcall invocations - while t_len >= 1 and ttarget[t_len]:find("^\tstack_trace%.lua:%d+:") or - ttarget[t_len] == "\t[C]: in function 'xpcall'" or ttarget[t_len] == " xpcall: " do - table.remove(ttarget, t_len) - t_len = t_len - 1 - end + -- Trim elements from this file and xpcall invocations + while t_len >= 1 and ttarget[t_len]:find("^\tstack_trace%.lua:%d+:") or + ttarget[t_len] == "\t[C]: in function 'xpcall'" or ttarget[t_len] == " xpcall: " do + table.remove(ttarget, t_len) + t_len = t_len - 1 + end - ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall - ttarget[#ttarget] = nil + ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall + ttarget[#ttarget] = nil - return ttarget + return ttarget end --- Run a function with return function (fn, ...) - -- So this is rather grim: we need to get the full traceback and current one and remove - -- the common prefix - local trace - local args = { ... } + -- So this is rather grim: we need to get the full traceback and current one and remove + -- the common prefix + local trace + local args = { ... } - -- xpcall in Lua 5.1 does not accept parameters - -- which is not ideal - local res = table.pack(xpcall(function() - return fn(table.unpack(args)) - end, traceback)) + -- xpcall in Lua 5.1 does not accept parameters + -- which is not ideal + local res = table.pack(xpcall(function() + return fn(table.unpack(args)) + end, traceback)) - if not res[1] then - trace = traceback("trace.lua:1:") - end - local ok, err = res[1], res[2] + if not res[1] then + trace = traceback("trace.lua:1:") + end + local ok, err = res[1], res[2] - if not ok and err ~= nil then - trace = trim_traceback(err, trace) + if not ok and err ~= nil then + trace = trim_traceback(err, trace) - -- Find the position where the stack traceback actually starts - local trace_starts - for i = #trace, 1, -1 do - if trace[i] == "stack traceback:" then trace_starts = i; break end - end + -- Find the position where the stack traceback actually starts + local trace_starts + for i = #trace, 1, -1 do + if trace[i] == "stack traceback:" then trace_starts = i; break end + end - for _, line in pairs(trace) do - _G._syslog(line) - end + for _, line in pairs(trace) do + _G._syslog(line) + end - -- If this traceback is more than 15 elements long, keep the first 9, last 5 - -- and put an ellipsis between the rest - local max = 10 - if trace_starts and #trace - trace_starts > max then - local keep_starts = trace_starts + 7 - for i = #trace - trace_starts - max, 0, -1 do - table.remove(trace, keep_starts + i) - end - table.insert(trace, keep_starts, " ...") - end + -- If this traceback is more than 15 elements long, keep the first 9, last 5 + -- and put an ellipsis between the rest + local max = 10 + if trace_starts and #trace - trace_starts > max then + local keep_starts = trace_starts + 7 + for i = #trace - trace_starts - max, 0, -1 do + table.remove(trace, keep_starts + i) + end + table.insert(trace, keep_starts, " ...") + end - for k, line in pairs(trace) do - trace[k] = line:gsub("in function", " in") - end + for k, line in pairs(trace) do + trace[k] = line:gsub("in function", " in") + end - return false, table.remove(trace, 1), table.concat(trace, "\n") - end + return false, table.remove(trace, 1), table.concat(trace, "\n") + end - return table.unpack(res, 1, res.n) + return table.unpack(res, 1, res.n) end diff --git a/sys/apis/ui/components/Grid.lua b/sys/apis/ui/components/Grid.lua index 262faee..21069ee 100644 --- a/sys/apis/ui/components/Grid.lua +++ b/sys/apis/ui/components/Grid.lua @@ -329,23 +329,13 @@ function UI.Grid:drawRows() local rawRow = self.values[key] local row = self:getDisplayValues(rawRow, key) - local ind = ' ' - if self.focused and index == self.index and not self.inactive then - ind = self.focusIndicator - end - local selected = index == self.index and not self.inactive local bg = self:getRowBackgroundColor(rawRow, selected) local fg = self:getRowTextColor(rawRow, selected) + local focused = self.focused and selected + + self:drawRow(sb, row, focused, bg, fg) - for _,col in pairs(self.columns) do - sb:write(ind .. safeValue(row[col.key] or ''), - col.cw + 1, - col.align, - bg, - fg) - ind = ' ' - end sb:finish(bg) end @@ -354,6 +344,19 @@ function UI.Grid:drawRows() end end +function UI.Grid:drawRow(sb, row, focused, bg, fg) + local ind = focused and self.focusIndicator or ' ' + + for _,col in pairs(self.columns) do + sb:write(ind .. safeValue(row[col.key] or ''), + col.cw + 1, + col.align, + bg, + fg) + ind = ' ' + end +end + function UI.Grid:getRowTextColor(row, selected) if selected then if self.focused then diff --git a/sys/apis/util.lua b/sys/apis/util.lua index e774ed9..042ba84 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -560,7 +560,7 @@ function Util.insertString(str, istr, pos) end function Util.split(str, pattern) - if not str then error('Util.split: Invalid parameters', 2) end + if not str or type(str) ~= 'string' then error('Util.split: Invalid parameters', 2) end pattern = pattern or "(.-)\n" local t = {} local function helper(line) table.insert(t, line) return "" end diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index f7564ed..ea3126d 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -15,11 +15,11 @@ local FILE = 1 UI:configure('Files', ...) local config = Config.load('Files', { - showHidden = false, - showDirSizes = false, + showHidden = false, + showDirSizes = false, }) config.associations = config.associations or { - nft = 'pain', + nft = 'pain', } local copied = { } @@ -28,517 +28,517 @@ local directories = { } local cutMode = false local function formatSize(size) - if size >= 1000000 then - return string.format('%dM', math.floor(size/1000000, 2)) - elseif size >= 1000 then - return string.format('%dK', math.floor(size/1000, 2)) - end - return size + if size >= 1000000 then + return string.format('%dM', math.floor(size/1000000, 2)) + elseif size >= 1000 then + return string.format('%dK', math.floor(size/1000, 2)) + end + return size end local Browser = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = '^-', event = 'updir' }, - { text = 'File', dropdown = { - { text = 'Run', event = 'run', flags = FILE }, - { text = 'Edit e', event = 'edit', flags = FILE }, - { text = 'Cloud edit c', event = 'cedit', flags = FILE }, - { text = 'Pastebin put p', event = 'pastebin', flags = FILE }, - { text = 'Shell s', event = 'shell' }, - { spacer = true }, - { text = 'Quit q', event = 'quit' }, - } }, - { text = 'Edit', dropdown = { - { text = 'Cut ^x', event = 'cut' }, - { text = 'Copy ^c', event = 'copy' }, - { text = 'Copy path ', event = 'copy_path' }, - { text = 'Paste ^v', event = 'paste' }, - { spacer = true }, - { text = 'Mark m', event = 'mark' }, - { text = 'Unmark all u', event = 'unmark' }, - { spacer = true }, - { text = 'Delete del', event = 'delete' }, - } }, - { text = 'View', dropdown = { - { text = 'Refresh r', event = 'refresh' }, - { text = 'Hidden ^h', event = 'toggle_hidden' }, - { text = 'Dir Size ^s', event = 'toggle_dirSize' }, - } }, - { text = '\187', - x = -3, - dropdown = { - { text = 'Associations', event = 'associate' }, - } }, - }, - }, - grid = UI.ScrollingGrid { - columns = { - { heading = 'Name', key = 'name' }, - { key = 'flags', width = 2 }, - { heading = 'Size', key = 'fsize', width = 5 }, - }, - sortColumn = 'name', - y = 2, ey = -2, - }, - statusBar = UI.StatusBar { - columns = { - { key = 'status' }, - { key = 'totalSize', width = 6 }, - }, - }, - notification = UI.Notification { }, - associations = UI.SlideOut { - backgroundColor = colors.cyan, - menuBar = UI.MenuBar { - buttons = { - { text = 'Save', event = 'save' }, - { text = 'Cancel', event = 'cancel' }, - }, - }, - grid = UI.ScrollingGrid { - x = 2, ex = -6, y = 3, ey = -5, - columns = { - { heading = 'Extension', key = 'name' }, - { heading = 'Program', key = 'value' }, - }, - autospace = true, - sortColumn = 'name', - accelerators = { - delete = 'remove_entry', - }, - }, - remove = UI.Button { - x = -4, y = 6, - text = '-', event = 'remove_entry', help = 'Remove', - }, - form = UI.Form { - x = 3, y = -3, ey = -2, - margin = 1, - manualControls = true, - [1] = UI.TextEntry { - width = 20, - formLabel = 'Extension', formKey = 'name', - shadowText = 'extension', - required = true, - limit = 64, - }, - [2] = UI.TextEntry { - width = 20, - formLabel = 'Program', formKey = 'value', - shadowText = 'program', - required = true, - limit = 128, - }, - add = UI.Button { - x = -11, y = 1, - text = 'Add', event = 'add_association', - }, - }, - statusBar = UI.StatusBar { - backgroundColor = colors.cyan, - }, - }, - accelerators = { - q = 'quit', - c = 'cedit', - e = 'edit', - s = 'shell', - p = 'pastebin', - r = 'refresh', - [ ' ' ] = 'mark', - m = 'mark', - backspace = 'updir', - u = 'unmark', - d = 'delete', - delete = 'delete', - [ 'control-h' ] = 'toggle_hidden', - [ 'control-s' ] = 'toggle_dirSize', - [ 'control-x' ] = 'cut', - [ 'control-c' ] = 'copy', - paste = 'paste', - }, + menuBar = UI.MenuBar { + buttons = { + { text = '^-', event = 'updir' }, + { text = 'File', dropdown = { + { text = 'Run', event = 'run', flags = FILE }, + { text = 'Edit e', event = 'edit', flags = FILE }, + { text = 'Cloud edit c', event = 'cedit', flags = FILE }, + { text = 'Pastebin put p', event = 'pastebin', flags = FILE }, + { text = 'Shell s', event = 'shell' }, + { spacer = true }, + { text = 'Quit q', event = 'quit' }, + } }, + { text = 'Edit', dropdown = { + { text = 'Cut ^x', event = 'cut' }, + { text = 'Copy ^c', event = 'copy' }, + { text = 'Copy path ', event = 'copy_path' }, + { text = 'Paste ^v', event = 'paste' }, + { spacer = true }, + { text = 'Mark m', event = 'mark' }, + { text = 'Unmark all u', event = 'unmark' }, + { spacer = true }, + { text = 'Delete del', event = 'delete' }, + } }, + { text = 'View', dropdown = { + { text = 'Refresh r', event = 'refresh' }, + { text = 'Hidden ^h', event = 'toggle_hidden' }, + { text = 'Dir Size ^s', event = 'toggle_dirSize' }, + } }, + { text = '\187', + x = -3, + dropdown = { + { text = 'Associations', event = 'associate' }, + } }, + }, + }, + grid = UI.ScrollingGrid { + columns = { + { heading = 'Name', key = 'name' }, + { key = 'flags', width = 2 }, + { heading = 'Size', key = 'fsize', width = 5 }, + }, + sortColumn = 'name', + y = 2, ey = -2, + }, + statusBar = UI.StatusBar { + columns = { + { key = 'status' }, + { key = 'totalSize', width = 6 }, + }, + }, + notification = UI.Notification { }, + associations = UI.SlideOut { + backgroundColor = colors.cyan, + menuBar = UI.MenuBar { + buttons = { + { text = 'Save', event = 'save' }, + { text = 'Cancel', event = 'cancel' }, + }, + }, + grid = UI.ScrollingGrid { + x = 2, ex = -6, y = 3, ey = -5, + columns = { + { heading = 'Extension', key = 'name' }, + { heading = 'Program', key = 'value' }, + }, + autospace = true, + sortColumn = 'name', + accelerators = { + delete = 'remove_entry', + }, + }, + remove = UI.Button { + x = -4, y = 6, + text = '-', event = 'remove_entry', help = 'Remove', + }, + form = UI.Form { + x = 3, y = -3, ey = -2, + margin = 1, + manualControls = true, + [1] = UI.TextEntry { + width = 20, + formLabel = 'Extension', formKey = 'name', + shadowText = 'extension', + required = true, + limit = 64, + }, + [2] = UI.TextEntry { + width = 20, + formLabel = 'Program', formKey = 'value', + shadowText = 'program', + required = true, + limit = 128, + }, + add = UI.Button { + x = -11, y = 1, + text = 'Add', event = 'add_association', + }, + }, + statusBar = UI.StatusBar { + backgroundColor = colors.cyan, + }, + }, + accelerators = { + q = 'quit', + c = 'cedit', + e = 'edit', + s = 'shell', + p = 'pastebin', + r = 'refresh', + [ ' ' ] = 'mark', + m = 'mark', + backspace = 'updir', + u = 'unmark', + d = 'delete', + delete = 'delete', + [ 'control-h' ] = 'toggle_hidden', + [ 'control-s' ] = 'toggle_dirSize', + [ 'control-x' ] = 'cut', + [ 'control-c' ] = 'copy', + paste = 'paste', + }, } function Browser:enable() - UI.Page.enable(self) - self:setFocus(self.grid) + UI.Page.enable(self) + self:setFocus(self.grid) end function Browser.menuBar:getActive(menuItem) - local file = Browser.grid:getSelected() - if menuItem.flags == FILE then - return file and not file.isDir - end - return true + local file = Browser.grid:getSelected() + if menuItem.flags == FILE then + return file and not file.isDir + end + return true end function Browser.grid:sortCompare(a, b) - if self.sortColumn == 'fsize' then - return a.size < b.size - elseif self.sortColumn == 'flags' then - return a.flags < b.flags - end - if a.isDir == b.isDir then - return a.name:lower() < b.name:lower() - end - return a.isDir + if self.sortColumn == 'fsize' then + return a.size < b.size + elseif self.sortColumn == 'flags' then + return a.flags < b.flags + end + if a.isDir == b.isDir then + return a.name:lower() < b.name:lower() + end + return a.isDir end function Browser.grid:getRowTextColor(file) - if file.marked then - return colors.green - end - if file.isDir then - return colors.cyan - end - if file.isReadOnly then - return colors.pink - end - return colors.white + if file.marked then + return colors.green + end + if file.isDir then + return colors.cyan + end + if file.isReadOnly then + return colors.pink + end + return colors.white end function Browser.grid:eventHandler(event) - if event.type == 'copy' then -- let copy be handled by parent - return false - end - return UI.ScrollingGrid.eventHandler(self, event) + if event.type == 'copy' then -- let copy be handled by parent + return false + end + return UI.ScrollingGrid.eventHandler(self, event) end function Browser.statusBar:draw() - if self.parent.dir then - local info = '#:' .. Util.size(self.parent.dir.files) - local numMarked = Util.size(marked) - if numMarked > 0 then - info = info .. ' M:' .. numMarked - end - self:setValue('info', info) - self:setValue('totalSize', formatSize(self.parent.dir.totalSize)) - UI.StatusBar.draw(self) - end + if self.parent.dir then + local info = '#:' .. Util.size(self.parent.dir.files) + local numMarked = Util.size(marked) + if numMarked > 0 then + info = info .. ' M:' .. numMarked + end + self:setValue('info', info) + self:setValue('totalSize', formatSize(self.parent.dir.totalSize)) + UI.StatusBar.draw(self) + end end function Browser:setStatus(status, ...) - self.notification:info(string.format(status, ...)) + self.notification:info(string.format(status, ...)) end function Browser:unmarkAll() - for _,m in pairs(marked) do - m.marked = false - end - Util.clear(marked) + for _,m in pairs(marked) do + m.marked = false + end + Util.clear(marked) end function Browser:getDirectory(directory) - local s, dir = pcall(function() + local s, dir = pcall(function() - local dir = directories[directory] - if not dir then - dir = { - name = directory, - size = 0, - files = { }, - totalSize = 0, - index = 1 - } - directories[directory] = dir - end + local dir = directories[directory] + if not dir then + dir = { + name = directory, + size = 0, + files = { }, + totalSize = 0, + index = 1 + } + directories[directory] = dir + end - self:updateDirectory(dir) + self:updateDirectory(dir) - return dir - end) + return dir + end) - return s, dir + return s, dir end function Browser:updateDirectory(dir) - dir.size = 0 - dir.totalSize = 0 - Util.clear(dir.files) + dir.size = 0 + dir.totalSize = 0 + Util.clear(dir.files) - local files = fs.listEx(dir.name) - if files then - dir.size = #files - for _, file in pairs(files) do - file.fullName = fs.combine(dir.name, file.name) - file.flags = '' - if not file.isDir then - dir.totalSize = dir.totalSize + file.size - file.fsize = formatSize(file.size) - else - if config.showDirSizes then - file.size = fs.getSize(file.fullName, true) + local files = fs.listEx(dir.name) + if files then + dir.size = #files + for _, file in pairs(files) do + file.fullName = fs.combine(dir.name, file.name) + file.flags = '' + if not file.isDir then + dir.totalSize = dir.totalSize + file.size + file.fsize = formatSize(file.size) + else + if config.showDirSizes then + file.size = fs.getSize(file.fullName, true) - dir.totalSize = dir.totalSize + file.size - file.fsize = formatSize(file.size) - end - file.flags = 'D' - end - if file.isReadOnly then - file.flags = file.flags .. 'R' - end - if config.showHidden or file.name:sub(1, 1) ~= '.' then - dir.files[file.fullName] = file - end - end - end + dir.totalSize = dir.totalSize + file.size + file.fsize = formatSize(file.size) + end + file.flags = 'D' + end + if file.isReadOnly then + file.flags = file.flags .. 'R' + end + if config.showHidden or file.name:sub(1, 1) ~= '.' then + dir.files[file.fullName] = file + end + end + end -- self.grid:update() -- self.grid:setIndex(dir.index) - self.grid:setValues(dir.files) + self.grid:setValues(dir.files) end function Browser:setDir(dirName, noStatus) - self:unmarkAll() + self:unmarkAll() - if self.dir then - self.dir.index = self.grid:getIndex() - end - local DIR = fs.combine('', dirName) - shell.setDir(DIR) - local s, dir = self:getDirectory(DIR) - if s then - self.dir = dir - elseif noStatus then - error(dir) - else - self:setStatus(dir) - self:setDir('', true) - return - end + if self.dir then + self.dir.index = self.grid:getIndex() + end + local DIR = fs.combine('', dirName) + shell.setDir(DIR) + local s, dir = self:getDirectory(DIR) + if s then + self.dir = dir + elseif noStatus then + error(dir) + else + self:setStatus(dir) + self:setDir('', true) + return + end - if not noStatus then - self.statusBar:setValue('status', '/' .. self.dir.name) - self.statusBar:draw() - end - self.grid:setIndex(self.dir.index) + if not noStatus then + self.statusBar:setValue('status', '/' .. self.dir.name) + self.statusBar:draw() + end + self.grid:setIndex(self.dir.index) end function Browser:run(...) - if multishell then - local tabId = shell.openTab(...) - multishell.setFocus(tabId) - else - shell.run(...) - Event.terminate = false - self:draw() - end + if multishell then + local tabId = shell.openTab(...) + multishell.setFocus(tabId) + else + shell.run(...) + Event.terminate = false + self:draw() + end end function Browser:hasMarked() - if Util.size(marked) == 0 then - local file = self.grid:getSelected() - if file then - file.marked = true - marked[file.fullName] = file - self.grid:draw() - end - end - return Util.size(marked) > 0 + if Util.size(marked) == 0 then + local file = self.grid:getSelected() + if file then + file.marked = true + marked[file.fullName] = file + self.grid:draw() + end + end + return Util.size(marked) > 0 end function Browser:eventHandler(event) - local file = self.grid:getSelected() + local file = self.grid:getSelected() - if event.type == 'quit' then - Event.exitPullEvents() + if event.type == 'quit' then + Event.exitPullEvents() - elseif event.type == 'edit' and file then - self:run('edit', file.name) + elseif event.type == 'edit' and file then + self:run('edit', file.name) - elseif event.type == 'cedit' and file then - self:run('cedit', file.name) - self:setStatus('Started cloud edit') + elseif event.type == 'cedit' and file then + self:run('cedit', file.name) + self:setStatus('Started cloud edit') - elseif event.type == 'shell' then - self:run('sys/apps/shell.lua') + elseif event.type == 'shell' then + self:run('sys/apps/shell.lua') - elseif event.type == 'refresh' then - self:updateDirectory(self.dir) - self.grid:draw() - self:setStatus('Refreshed') + elseif event.type == 'refresh' then + self:updateDirectory(self.dir) + self.grid:draw() + self:setStatus('Refreshed') - elseif event.type == 'associate' then - self.associations:show() + elseif event.type == 'associate' then + self.associations:show() - elseif event.type == 'pastebin' then - if file and not file.isDir then - local s, m = pastebin.put(file.fullName) - if s then - os.queueEvent('clipboard_copy', s) - self.notification:success(string.format('Uploaded as %s', s), 0) - else - self.notification:error(m) - end - end + elseif event.type == 'pastebin' then + if file and not file.isDir then + local s, m = pastebin.put(file.fullName) + if s then + os.queueEvent('clipboard_copy', s) + self.notification:success(string.format('Uploaded as %s', s), 0) + else + self.notification:error(m) + end + end - elseif event.type == 'toggle_hidden' then - config.showHidden = not config.showHidden - Config.update('Files', config) + elseif event.type == 'toggle_hidden' then + config.showHidden = not config.showHidden + Config.update('Files', config) - self:updateDirectory(self.dir) - self.grid:draw() - if not config.showHidden then - self:setStatus('Hiding hidden') - else - self:setStatus('Displaying hidden') - end + self:updateDirectory(self.dir) + self.grid:draw() + if not config.showHidden then + self:setStatus('Hiding hidden') + else + self:setStatus('Displaying hidden') + end - elseif event.type == 'toggle_dirSize' then - config.showDirSizes = not config.showDirSizes - Config.update('Files', config) + elseif event.type == 'toggle_dirSize' then + config.showDirSizes = not config.showDirSizes + Config.update('Files', config) - self:updateDirectory(self.dir) - self.grid:draw() - if config.showDirSizes then - self:setStatus('Displaying dir sizes') - end + self:updateDirectory(self.dir) + self.grid:draw() + if config.showDirSizes then + self:setStatus('Displaying dir sizes') + end - elseif event.type == 'mark' and file then - file.marked = not file.marked - if file.marked then - marked[file.fullName] = file - else - marked[file.fullName] = nil - end - self.grid:draw() - self.statusBar:draw() + elseif event.type == 'mark' and file then + file.marked = not file.marked + if file.marked then + marked[file.fullName] = file + else + marked[file.fullName] = nil + end + self.grid:draw() + self.statusBar:draw() - elseif event.type == 'unmark' then - self:unmarkAll() - self.grid:draw() - self:setStatus('Marked files cleared') + elseif event.type == 'unmark' then + self:unmarkAll() + self.grid:draw() + self:setStatus('Marked files cleared') - elseif event.type == 'grid_select' or event.type == 'run' then - if file then - if file.isDir then - self:setDir(file.fullName) - else - local ext = file.name:match('%.(%w+)$') - if ext and config.associations[ext] then - self:run(config.associations[ext], '/' .. file.fullName) - else - self:run(file.name) - end - end - end + elseif event.type == 'grid_select' or event.type == 'run' then + if file then + if file.isDir then + self:setDir(file.fullName) + else + local ext = file.name:match('%.(%w+)$') + if ext and config.associations[ext] then + self:run(config.associations[ext], '/' .. file.fullName) + else + self:run(file.name) + end + end + end - elseif event.type == 'updir' then - local dir = (self.dir.name:match("(.*/)")) - self:setDir(dir or '/') + elseif event.type == 'updir' then + local dir = (self.dir.name:match("(.*/)")) + self:setDir(dir or '/') - elseif event.type == 'delete' then - if self:hasMarked() then - local width = self.statusBar:getColumnWidth('status') - self.statusBar:setColumnWidth('status', UI.term.width) - self.statusBar:setValue('status', 'Delete marked? (y/n)') - self.statusBar:draw() - self.statusBar:sync() - local _, ch = os.pullEvent('char') - if ch == 'y' or ch == 'Y' then - for _,m in pairs(marked) do - pcall(function() - fs.delete(m.fullName) - end) - end - end - marked = { } - self.statusBar:setColumnWidth('status', width) - self.statusBar:setValue('status', '/' .. self.dir.name) - self:updateDirectory(self.dir) + elseif event.type == 'delete' then + if self:hasMarked() then + local width = self.statusBar:getColumnWidth('status') + self.statusBar:setColumnWidth('status', UI.term.width) + self.statusBar:setValue('status', 'Delete marked? (y/n)') + self.statusBar:draw() + self.statusBar:sync() + local _, ch = os.pullEvent('char') + if ch == 'y' or ch == 'Y' then + for _,m in pairs(marked) do + pcall(function() + fs.delete(m.fullName) + end) + end + end + marked = { } + self.statusBar:setColumnWidth('status', width) + self.statusBar:setValue('status', '/' .. self.dir.name) + self:updateDirectory(self.dir) - self.statusBar:draw() - self.grid:draw() - self:setFocus(self.grid) - end + self.statusBar:draw() + self.grid:draw() + self:setFocus(self.grid) + end - elseif event.type == 'copy' or event.type == 'cut' then - if self:hasMarked() then - cutMode = event.type == 'cut' - Util.clear(copied) - Util.merge(copied, marked) - --self:unmarkAll() - self.grid:draw() - self:setStatus('Copied %d file(s)', Util.size(copied)) - end + elseif event.type == 'copy' or event.type == 'cut' then + if self:hasMarked() then + cutMode = event.type == 'cut' + Util.clear(copied) + Util.merge(copied, marked) + --self:unmarkAll() + self.grid:draw() + self:setStatus('Copied %d file(s)', Util.size(copied)) + end - elseif event.type == 'copy_path' then - if file then - os.queueEvent('clipboard_copy', file.fullName) - end + elseif event.type == 'copy_path' then + if file then + os.queueEvent('clipboard_copy', file.fullName) + end - elseif event.type == 'paste' then - for _,m in pairs(copied) do - local s, m = pcall(function() - if cutMode then - fs.move(m.fullName, fs.combine(self.dir.name, m.name)) - else - fs.copy(m.fullName, fs.combine(self.dir.name, m.name)) - end - end) - end - self:updateDirectory(self.dir) - self.grid:draw() - self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)') + elseif event.type == 'paste' then + for _,m in pairs(copied) do + local s, m = pcall(function() + if cutMode then + fs.move(m.fullName, fs.combine(self.dir.name, m.name)) + else + fs.copy(m.fullName, fs.combine(self.dir.name, m.name)) + end + end) + end + self:updateDirectory(self.dir) + self.grid:draw() + self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)') - else - return UI.Page.eventHandler(self, event) - end - self:setFocus(self.grid) - return true + else + return UI.Page.eventHandler(self, event) + end + self:setFocus(self.grid) + return true end --[[ Associations slide out ]] -- function Browser.associations:show() - self.grid.values = { } - for k, v in pairs(config.associations) do - table.insert(self.grid.values, { - name = k, - value = v, - }) - end - self.grid:update() - UI.SlideOut.show(self) - self:setFocus(self.form[1]) + self.grid.values = { } + for k, v in pairs(config.associations) do + table.insert(self.grid.values, { + name = k, + value = v, + }) + end + self.grid:update() + UI.SlideOut.show(self) + self:setFocus(self.form[1]) end function Browser.associations:eventHandler(event) - if event.type == 'remove_entry' then - local row = self.grid:getSelected() - if row then - Util.removeByValue(self.grid.values, row) - self.grid:update() - self.grid:draw() - end + if event.type == 'remove_entry' then + local row = self.grid:getSelected() + if row then + Util.removeByValue(self.grid.values, row) + self.grid:update() + self.grid:draw() + end - elseif event.type == 'add_association' then - if self.form:save() then - local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { } - entry.name = self.form[1].value - entry.value = self.form[2].value - table.insert(self.grid.values, entry) - self.form[1]:reset() - self.form[2]:reset() - self.grid:update() - self.grid:draw() - end + elseif event.type == 'add_association' then + if self.form:save() then + local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { } + entry.name = self.form[1].value + entry.value = self.form[2].value + table.insert(self.grid.values, entry) + self.form[1]:reset() + self.form[2]:reset() + self.grid:update() + self.grid:draw() + end - elseif event.type == 'cancel' then - self:hide() + elseif event.type == 'cancel' then + self:hide() - elseif event.type == 'save' then - config.associations = { } - for _, v in pairs(self.grid.values) do - config.associations[v.name] = v.value - end - Config.update('Files', config) - self:hide() + elseif event.type == 'save' then + config.associations = { } + for _, v in pairs(self.grid.values) do + config.associations[v.name] = v.value + end + Config.update('Files', config) + self:hide() - else - return UI.SlideOut.eventHandler(self, event) - end - return true + else + return UI.SlideOut.eventHandler(self, event) + end + return true end --[[-- Startup logic --]]-- diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 4eba8ab..bfc080b 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -294,7 +294,6 @@ end function page:rawExecute(s) local fn, m local wrapped - local t = os.clock() fn = load('return (' ..s.. ')', 'lua', nil, sandboxEnv) @@ -303,6 +302,7 @@ function page:rawExecute(s) wrapped = true end + local t = os.clock() if fn then fn, m = pcall(fn) if #m <= 1 and wrapped then @@ -311,19 +311,24 @@ function page:rawExecute(s) else fn, m = load(s, 'lua', nil, sandboxEnv) if fn then + t = os.clock() fn, m = pcall(fn) end end if fn then + t = os.clock() - t + + local bg, fg = term.getBackgroundColor(), term.getTextColor() + term.setTextColor(colors.cyan) + term.setBackgroundColor(colors.black) + term.write(string.format('out [%.2f]: ', t)) + term.setBackgroundColor(bg) + term.setTextColor(fg) if m or wrapped then - local bg, fg = term.getBackgroundColor(), term.getTextColor() - term.setTextColor(colors.cyan) - term.setBackgroundColor(colors.black) - term.write(string.format('out [%.2f]: ', os.clock() - t)) - term.setBackgroundColor(bg) - term.setTextColor(fg) Util.print(m or 'nil') + else + print() end else _G.printError(m) diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index 8b7b7b2..e6341eb 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -127,12 +127,14 @@ local function sendCommand(host, command) end end +--[[ function page.ports:eventHandler(event) if event.type == 'grid_select' then shell.openForegroundTab('sniff ' .. event.selected.port) end return UI.SlideOut.eventHandler(self, event) end +]] function page.ports.grid:update() local function findConnection(port) diff --git a/sys/apps/ShellLauncher.lua b/sys/apps/ShellLauncher.lua index 9407e46..d905653 100644 --- a/sys/apps/ShellLauncher.lua +++ b/sys/apps/ShellLauncher.lua @@ -5,23 +5,23 @@ local shell = _ENV.shell local launcherTab = kernel.getCurrent() kernel.hook('kernel_focus', function(_, eventData) - local focusTab = eventData and eventData[1] - if focusTab == launcherTab.uid then - local previousTab = eventData[2] - local nextTab = launcherTab - if not previousTab then - for _, v in pairs(kernel.routines) do - if not v.hidden and v.uid > nextTab.uid then - nextTab = v - end - end - end - if nextTab == launcherTab then - shell.switchTab(shell.openTab('sys/apps/shell.lua')) - else - shell.switchTab(nextTab.uid) - end - end + local focusTab = eventData and eventData[1] + if focusTab == launcherTab.uid then + local previousTab = eventData[2] + local nextTab = launcherTab + if not previousTab then + for _, v in pairs(kernel.routines) do + if not v.hidden and v.uid > nextTab.uid then + nextTab = v + end + end + end + if nextTab == launcherTab then + shell.switchTab(shell.openTab('sys/apps/shell.lua')) + else + shell.switchTab(nextTab.uid) + end + end end) -while os.pullEventRaw() do end \ No newline at end of file +os.pullEventRaw('kernel_halt') \ No newline at end of file diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index e735840..80d170e 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -23,115 +23,115 @@ local packagesIntro = [[Setup Complete local page = UI.Page { wizard = UI.Wizard { - ey = -2, + ey = -2, pages = { splash = UI.WizardPage { - index = 1, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = string.format(splashIntro, Ansi.white), - }, - }, + index = 1, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = string.format(splashIntro, Ansi.white), + }, + }, label = UI.WizardPage { - index = 2, - labelText = UI.Text { - x = 3, y = 2, - value = 'Label' - }, - label = UI.TextEntry { - x = 9, y = 2, ex = -3, - limit = 32, - value = os.getComputerLabel(), - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 4, ey = -3, - value = string.format(labelIntro, Ansi.white), - }, - }, + index = 2, + labelText = UI.Text { + x = 3, y = 2, + value = 'Label' + }, + label = UI.TextEntry { + x = 9, y = 2, ex = -3, + limit = 32, + value = os.getComputerLabel(), + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 4, ey = -3, + value = string.format(labelIntro, Ansi.white), + }, + }, password = UI.WizardPage { index = 3, - passwordLabel = UI.Text { - x = 3, y = 2, - value = 'Password' - }, - newPass = UI.TextEntry { - x = 12, ex = -3, y = 2, - limit = 32, - mask = true, - shadowText = 'password', - }, + passwordLabel = UI.Text { + x = 3, y = 2, + value = 'Password' + }, + newPass = UI.TextEntry { + x = 12, ex = -3, y = 2, + limit = 32, + mask = true, + shadowText = 'password', + }, --[[ - groupLabel = UI.Text { - x = 3, y = 3, - value = 'Group' - }, - group = UI.TextEntry { - x = 12, ex = -3, y = 3, - limit = 32, - shadowText = 'network group', - }, + groupLabel = UI.Text { + x = 3, y = 3, + value = 'Group' + }, + group = UI.TextEntry { + x = 12, ex = -3, y = 3, + limit = 32, + shadowText = 'network group', + }, ]] - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 5, ey = -3, - value = string.format(passwordIntro, Ansi.white), - }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 5, ey = -3, + value = string.format(passwordIntro, Ansi.white), + }, }, packages = UI.WizardPage { index = 4, - button = UI.Button { - x = 3, y = -3, - text = 'Open Package Manager', - event = 'packages', - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -4, - value = string.format(packagesIntro, Ansi.white), - }, + button = UI.Button { + x = 3, y = -3, + text = 'Open Package Manager', + event = 'packages', + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -4, + value = string.format(packagesIntro, Ansi.white), + }, }, }, - }, - notification = UI.Notification { }, + }, + notification = UI.Notification { }, } function page.wizard.pages.label:validate() - os.setComputerLabel(self.label.value) - return true + os.setComputerLabel(self.label.value) + return true end function page.wizard.pages.password:validate() - if #self.newPass.value > 0 then - Security.updatePassword(SHA1.sha1(self.newPass.value)) - end - --[[ - if #self.group.value > 0 then - local config = Config.load('os') - config.group = self.group.value - Config.update('os', config) - end - ]] - return true + if #self.newPass.value > 0 then + Security.updatePassword(SHA1.sha1(self.newPass.value)) + end + --[[ + if #self.group.value > 0 then + local config = Config.load('os') + config.group = self.group.value + Config.update('os', config) + end + ]] + return true end function page:eventHandler(event) - if event.type == 'skip' then - self.wizard:emit({ type = 'nextView' }) + if event.type == 'skip' then + self.wizard:emit({ type = 'nextView' }) - elseif event.type == 'view_enabled' then - event.view:focusFirst() + elseif event.type == 'view_enabled' then + event.view:focusFirst() - elseif event.type == 'packages' then - shell.openForegroundTab('PackageManager') + elseif event.type == 'packages' then + shell.openForegroundTab('PackageManager') - elseif event.type == 'wizard_complete' or event.type == 'cancel' then - UI.exitPullEvents() + elseif event.type == 'wizard_complete' or event.type == 'cancel' then + UI.exitPullEvents() else return UI.Page.eventHandler(self, event) diff --git a/sys/apps/autorun.lua b/sys/apps/autorun.lua index 40e0e74..1d650da 100644 --- a/sys/apps/autorun.lua +++ b/sys/apps/autorun.lua @@ -11,60 +11,60 @@ local term = _G.term local success = true local function runDir(directory) - if not fs.exists(directory) then - return true - end + if not fs.exists(directory) then + return true + end - local files = fs.list(directory) - table.sort(files) + local files = fs.list(directory) + table.sort(files) - for _,file in ipairs(files) do - os.sleep(0) - local result, err = shell.run(directory .. '/' .. file) + for _,file in ipairs(files) do + os.sleep(0) + local result, err = shell.run(directory .. '/' .. file) - if result then - if term.isColor() then - term.setTextColor(colors.green) - end - term.write('[PASS] ') - term.setTextColor(colors.white) - term.write(fs.combine(directory, file)) - print() - else - if term.isColor() then - term.setTextColor(colors.red) - end - term.write('[FAIL] ') - term.setTextColor(colors.white) - term.write(fs.combine(directory, file)) - if err then - _G.printError('\n' .. err) - end - print() - success = false - end - end + if result then + if term.isColor() then + term.setTextColor(colors.green) + end + term.write('[PASS] ') + term.setTextColor(colors.white) + term.write(fs.combine(directory, file)) + print() + else + if term.isColor() then + term.setTextColor(colors.red) + end + term.write('[FAIL] ') + term.setTextColor(colors.white) + term.write(fs.combine(directory, file)) + if err then + _G.printError('\n' .. err) + end + print() + success = false + end + end end runDir('sys/autorun') for name in pairs(Packages:installed()) do - local packageDir = 'packages/' .. name .. '/autorun' - runDir(packageDir) + local packageDir = 'packages/' .. name .. '/autorun' + runDir(packageDir) end runDir('usr/autorun') if not success then - if multishell then - multishell.setFocus(multishell.getCurrent()) - end - _G.printError('A startup program has errored') - print('Press enter to continue') + if multishell then + multishell.setFocus(multishell.getCurrent()) + end + _G.printError('A startup program has errored') + print('Press enter to continue') - while true do - local e, code = os.pullEventRaw('key') - if e == 'terminate' or e == 'key' and code == keys.enter then - break - end - end + while true do + local e, code = os.pullEventRaw('key') + if e == 'terminate' or e == 'key' and code == keys.enter then + break + end + end end diff --git a/sys/apps/cedit.lua b/sys/apps/cedit.lua index f62a063..138f977 100644 --- a/sys/apps/cedit.lua +++ b/sys/apps/cedit.lua @@ -7,32 +7,32 @@ local shell = _ENV.shell local args = { ... } if not args[1] then - error('Syntax: cedit ') + error('Syntax: cedit ') end if not _G.http.websocket then - error('Requires CC: Tweaked') + error('Requires CC: Tweaked') end if not _G.cloud_catcher then - local key = Config.load('cloud').key + local key = Config.load('cloud').key - if not key then - print('Visit https://cloud-catcher.squiddev.cc') - print('Paste key: ') - key = read() - if #key == 0 then - return - end - end + if not key then + print('Visit https://cloud-catcher.squiddev.cc') + print('Paste key: ') + key = read() + if #key == 0 then + return + end + end - -- open an unfocused tab - local id = shell.openTab('cloud ' .. key) - print('Connecting...') - while not _G.cloud_catcher do - os.sleep(.2) - end - multishell.setTitle(id, 'Cloud') + -- open an unfocused tab + local id = shell.openTab('cloud ' .. key) + print('Connecting...') + while not _G.cloud_catcher do + os.sleep(.2) + end + multishell.setTitle(id, 'Cloud') end shell.run('cloud edit ' .. table.unpack({ ... })) diff --git a/sys/apps/cshell.lua b/sys/apps/cshell.lua index 44cee34..34e5c91 100644 --- a/sys/apps/cshell.lua +++ b/sys/apps/cshell.lua @@ -4,20 +4,20 @@ local read = _G.read local shell = _ENV.shell if not _G.http.websocket then - error('Requires CC: Tweaked') + error('Requires CC: Tweaked') end if not _G.cloud_catcher then - local key = Config.load('cloud').key + local key = Config.load('cloud').key - if not key then - print('Visit https://cloud-catcher.squiddev.cc') - print('Paste key: ') - key = read() - if #key == 0 then - return - end - end - print('Connecting...') - shell.run('cloud ' .. key) + if not key then + print('Visit https://cloud-catcher.squiddev.cc') + print('Paste key: ') + key = read() + if #key == 0 then + return + end + end + print('Connecting...') + shell.run('cloud ' .. key) end diff --git a/sys/apps/network/redserver.lua b/sys/apps/network/redserver.lua deleted file mode 100644 index 6c63d21..0000000 --- a/sys/apps/network/redserver.lua +++ /dev/null @@ -1,115 +0,0 @@ -local Event = require('event') -local Util = require('util') - -local fs = _G.fs -local modem = _G.device.wireless_modem -local os = _G.os - -local computerId = os.getComputerID() - ---modem.open(80) - --- https://github.com/golgote/neturl/blob/master/lib/net/url.lua -local function parseQuery(str) - local sep = '&' - - local values = {} - for key,val in str:gmatch(string.format('([^%q=]+)(=*[^%q=]*)', sep, sep)) do - --local key = decode(key) - local keys = {} - key = key:gsub('%[([^%]]*)%]', function(v) - -- extract keys between balanced brackets - if string.find(v, "^-?%d+$") then - v = tonumber(v) - --else - --v = decode(v) - end - table.insert(keys, v) - return "=" - end) - key = key:gsub('=+.*$', "") - key = key:gsub('%s', "_") -- remove spaces in parameter name - val = val:gsub('^=+', "") - - if not values[key] then - values[key] = {} - end - if #keys > 0 and type(values[key]) ~= 'table' then - values[key] = {} - elseif #keys == 0 and type(values[key]) == 'table' then - values[key] = val --decode(val) - end - - local t = values[key] - for i,k in ipairs(keys) do - if type(t) ~= 'table' then - t = {} - end - if k == "" then - k = #t+1 - end - if not t[k] then - t[k] = {} - end - if i == #keys then - t[k] = val --decode(val) - end - t = t[k] - end - end - return values -end - -local function getListing(path, recursive) - local list = { } - local function listing(p) - for _, f in pairs(fs.listEx(p)) do - local abs = fs.combine(p, f.name) - table.insert(list, { - isDir = f.isDir, - path = string.sub(abs, #path + 1), - size = f.size, - }) - if recursive and f.isDir then - listing(abs) - end - end - end - listing(path) - return list -end - ---[[ -Event.on('modem_message', function(_, _, dport, dhost, request) - if dport == 80 and dhost == computerId and type(request) == 'table' then - if request.method == 'GET' then - local query - if not request.path or type(request.path) ~= 'string' then - return - end - local path = request.path:gsub('%?(.*)', function(v) - query = parseQuery(v) - return '' - end) - if fs.isDir(path) then - -- TODO: more validation - modem.transmit(request.replyPort, request.replyAddress, { - statusCode = 200, - contentType = 'table/directory', - data = getListing(path, query and query.recursive == 'true'), - }) - elseif fs.exists(path) then - modem.transmit(request.replyPort, request.replyAddress, { - statusCode = 200, - contentType = 'table/file', - data = Util.readFile(path), - }) - else - modem.transmit(request.replyPort, request.replyAddress, { - statusCode = 404, - }) - end - end - end -end) -]] diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index 0e69a03..11ed673 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -156,7 +156,7 @@ local function sendInfo() info.label = os.getComputerLabel() info.uptime = math.floor(os.clock()) info.group = network.getGroup() - if turtle then + if turtle and turtle.getStatus then info.fuel = turtle.getFuelLevel() info.status = turtle.getStatus() info.point = turtle.point diff --git a/sys/apps/sniff.lua b/sys/apps/sniff.lua deleted file mode 100644 index ada3d77..0000000 --- a/sys/apps/sniff.lua +++ /dev/null @@ -1,69 +0,0 @@ -local Event = require('event') -local Terminal = require('terminal') -local Util = require('util') - -local colors = _G.colors -local modem = _G.device.wireless_modem -local term = _G.term -local textutils = _G.textutils - -local terminal = Terminal.window(term.current()) -terminal.setMaxScroll(300) -local oldTerm = term.redirect(terminal) - -local function syntax() - error('Syntax: sniff [port]') -end - -local port = ({ ... })[1] or syntax() -port = tonumber(port) or syntax() - -Event.on('modem_message', - function(_, _, dport, _, data, _) - if dport == port then - terminal.scrollBottom() - terminal.setTextColor(colors.white) - print(textutils.serialize(data)) - end - end) - -Event.on('mouse_scroll', function(_, direction) - if direction == -1 then - terminal.scrollUp() - else - terminal.scrollDown() - end -end) - -local function sniffer(_, _, data) - terminal.scrollBottom() - terminal.setTextColor(colors.yellow) - local ot = term.redirect(terminal) - print(textutils.serialize(data)) - term.redirect(ot) -end - -local socket = _G.transport.sockets[port] -if socket then - if not socket.sniffers then - socket.sniffers = { modem.transmit } - socket.transmit = function(...) - for _,v in pairs(socket.sniffers) do - v(...) - end - end - end - table.insert(socket.sniffers, sniffer) -end - -local s, m = pcall(Event.pullEvents) - -if socket then - Util.removeByValue(socket.sniffers, sniffer) -end - -term.redirect(oldTerm) - -if not s and m then - error(m) -end diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index 434a16c..61d47f9 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -7,51 +7,51 @@ local colors = _G.colors -- -t80x30 if _G.http.websocket then - local config = Config.load('cloud') + local config = Config.load('cloud') - local tab = UI.Tab { - tabTitle = 'Cloud', - description = 'Cloud catcher options', - key = UI.TextEntry { - x = 3, ex = -3, y = 2, - limit = 32, - value = config.key, - shadowText = 'Cloud key', - accelerators = { - enter = 'update_key', - }, - }, - button = UI.Button { - x = 3, y = 4, - text = 'Update', - event = 'update_key', - }, - labelText = UI.TextArea { - x = 3, ex = -3, y = 6, - textColor = colors.yellow, - marginLeft = 0, marginRight = 0, - value = string.format( + local tab = UI.Tab { + tabTitle = 'Cloud', + description = 'Cloud catcher options', + key = UI.TextEntry { + x = 3, ex = -3, y = 2, + limit = 32, + value = config.key, + shadowText = 'Cloud key', + accelerators = { + enter = 'update_key', + }, + }, + button = UI.Button { + x = 3, y = 4, + text = 'Update', + event = 'update_key', + }, + labelText = UI.TextArea { + x = 3, ex = -3, y = 6, + textColor = colors.yellow, + marginLeft = 0, marginRight = 0, + value = string.format( [[Use a non-changing cloud key. Note that only a single computer can use this session at one time. To obtain a key, visit: %shttps://cloud-catcher.squiddev.cc%s then bookmark: %shttps://cloud-catcher.squiddev.cc/?id=KEY - ]], - Ansi.white, Ansi.reset, Ansi.white), - }, - } + ]], + Ansi.white, Ansi.reset, Ansi.white), + }, + } - function tab:eventHandler(event) - if event.type == 'update_key' then - if #self.key.value > 0 then - config.key = self.key.value - else - config.key = nil - end - Config.update('cloud', config) - self:emit({ type = 'success_message', message = 'Updated' }) - end - end + function tab:eventHandler(event) + if event.type == 'update_key' then + if #self.key.value > 0 then + config.key = self.key.value + else + config.key = nil + end + Config.update('cloud', config) + self:emit({ type = 'success_message', message = 'Updated' }) + end + end - return tab + return tab end diff --git a/sys/apps/system/kiosk.lua b/sys/apps/system/kiosk.lua index 89a24e2..1adb110 100644 --- a/sys/apps/system/kiosk.lua +++ b/sys/apps/system/kiosk.lua @@ -14,13 +14,13 @@ local tab = UI.Tab { formLabel = 'Monitor', formKey = 'monitor', }, textScale = UI.Chooser { - formLabel = 'Font Size', formKey = 'textScale', - nochoice = 'Small', - choices = { - { name = 'Small', value = '.5' }, - { name = 'Large', value = '1' }, - }, - help = 'Adjust text scaling', + formLabel = 'Font Size', formKey = 'textScale', + nochoice = 'Small', + choices = { + { name = 'Small', value = '.5' }, + { name = 'Large', value = '1' }, + }, + help = 'Adjust text scaling', }, labelText = UI.TextArea { x = 2, ex = -2, y = 5, diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index 5108ad0..cebd5b7 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -9,76 +9,76 @@ local config = Config.load('multishell') local tab = UI.Tab { tabTitle = 'Launcher', description = 'Set the application launcher', - launcherLabel = UI.Text { - x = 3, y = 2, - value = 'Launcher', - }, - launcher = UI.Chooser { - x = 13, y = 2, width = 12, - choices = { - { name = 'Overview', value = 'sys/apps/Overview.lua' }, - { name = 'Shell', value = 'sys/apps/ShellLauncher.lua' }, - { name = 'Custom', value = 'custom' }, - }, - }, - custom = UI.TextEntry { - x = 13, ex = -3, y = 3, - limit = 128, - shadowText = 'File name', - }, - button = UI.Button { - x = 3, y = 5, - text = 'Update', - event = 'update', - }, - labelText = UI.TextArea { - x = 3, ex = -3, y = 7, - textColor = colors.yellow, - value = 'Choose an application launcher', - }, + launcherLabel = UI.Text { + x = 3, y = 2, + value = 'Launcher', + }, + launcher = UI.Chooser { + x = 13, y = 2, width = 12, + choices = { + { name = 'Overview', value = 'sys/apps/Overview.lua' }, + { name = 'Shell', value = 'sys/apps/ShellLauncher.lua' }, + { name = 'Custom', value = 'custom' }, + }, + }, + custom = UI.TextEntry { + x = 13, ex = -3, y = 3, + limit = 128, + shadowText = 'File name', + }, + button = UI.Button { + x = 3, y = 5, + text = 'Update', + event = 'update', + }, + labelText = UI.TextArea { + x = 3, ex = -3, y = 7, + textColor = colors.yellow, + value = 'Choose an application launcher', + }, } function tab:enable() - local launcher = config.launcher and 'custom' or 'sys/apps/Overview.lua' + local launcher = config.launcher and 'custom' or 'sys/apps/Overview.lua' - for _, v in pairs(self.launcher.choices) do - if v.value == config.launcher then - launcher = v.value - break - end - end + for _, v in pairs(self.launcher.choices) do + if v.value == config.launcher then + launcher = v.value + break + end + end UI.Tab.enable(self) - self.launcher.value = launcher - self.custom.enabled = launcher == 'custom' + self.launcher.value = launcher + self.custom.enabled = launcher == 'custom' end function tab:eventHandler(event) if event.type == 'choice_change' then - self.custom.enabled = event.value == 'custom' - if self.custom.enabled then - self.custom.value = config.launcher - end - self:draw() + self.custom.enabled = event.value == 'custom' + if self.custom.enabled then + self.custom.value = config.launcher + end + self:draw() - elseif event.type == 'update' then - local launcher + elseif event.type == 'update' then + local launcher - if self.launcher.value ~= 'custom' then - launcher = self.launcher.value - elseif fs.exists(self.custom.value) and not fs.isDir(self.custom.value) then - launcher = self.custom.value - end + if self.launcher.value ~= 'custom' then + launcher = self.launcher.value + elseif fs.exists(self.custom.value) and not fs.isDir(self.custom.value) then + launcher = self.custom.value + end - if launcher then - config.launcher = launcher - Config.update('multishell', config) - self:emit({ type = 'success_message', message = 'Updated' }) - else - self:emit({ type = 'error_message', message = 'Invalid file' }) - end - end + if launcher then + config.launcher = launcher + Config.update('multishell', config) + self:emit({ type = 'success_message', message = 'Updated' }) + else + self:emit({ type = 'error_message', message = 'Invalid file' }) + end + end end return tab diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index 504c7e1..803ed51 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -13,91 +13,91 @@ local tab = UI.Tab { accelerators = { enter = 'update_path', }, - help = 'add a new path', + help = 'add a new path', }, grid = UI.Grid { y = 4, ey = -3, disableHeader = true, columns = { { key = 'value' } }, autospace = true, - sortColumn = 'index', - help = 'double-click to remove, shift-arrow to move', - accelerators = { - delete = 'remove', - }, + sortColumn = 'index', + help = 'double-click to remove, shift-arrow to move', + accelerators = { + delete = 'remove', + }, + }, + statusBar = UI.StatusBar { }, + accelerators = { + [ 'shift-up' ] = 'move_up', + [ 'shift-down' ] = 'move_down', }, - statusBar = UI.StatusBar { }, - accelerators = { - [ 'shift-up' ] = 'move_up', - [ 'shift-down' ] = 'move_down', - }, } function tab:updateList(path) self.grid.values = { } for k,v in ipairs(Util.split(path, '(.-):')) do table.insert(self.grid.values, { index = k, value = v }) - end - self.grid:update() + end + self.grid:update() end function tab:enable() - local env = Config.load('shell') - self:updateList(env.path) + local env = Config.load('shell') + self:updateList(env.path) UI.Tab.enable(self) end function tab:save() - local t = { } - for _, v in ipairs(self.grid.values) do - table.insert(t, v.value) - end - local env = Config.load('shell') - env.path = table.concat(t, ':') - self:updateList(env.path) - Config.update('shell', env) + local t = { } + for _, v in ipairs(self.grid.values) do + table.insert(t, v.value) + end + local env = Config.load('shell') + env.path = table.concat(t, ':') + self:updateList(env.path) + Config.update('shell', env) end function tab:eventHandler(event) - if event.type == 'update_path' then - table.insert(self.grid.values, { - value = self.entry.value, - }) + if event.type == 'update_path' then + table.insert(self.grid.values, { + value = self.entry.value, + }) self:save() self.entry:reset() - self.entry:draw() - self.grid:draw() + self.entry:draw() + self.grid:draw() return true - elseif event.type == 'grid_select' or event.type == 'remove' then - local selected = self.grid:getSelected() - if selected then - table.remove(self.grid.values, selected.index) - self:save() - self.grid:draw() - end + elseif event.type == 'grid_select' or event.type == 'remove' then + local selected = self.grid:getSelected() + if selected then + table.remove(self.grid.values, selected.index) + self:save() + self.grid:draw() + end - elseif event.type == 'focus_change' then + elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) - elseif event.type == 'move_up' then - local entry = self.grid:getSelected() - if entry.index > 1 then - table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index - 1) - self:save() - self.grid:draw() - end + elseif event.type == 'move_up' then + local entry = self.grid:getSelected() + if entry.index > 1 then + table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index - 1) + self:save() + self.grid:draw() + end - elseif event.type == 'move_down' then - local entry = self.grid:getSelected() - if entry.index < #self.grid.values then - table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index + 1) - self:save() - self.grid:draw() - end - end + elseif event.type == 'move_down' then + local entry = self.grid:getSelected() + if entry.index < #self.grid.values then + table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index + 1) + self:save() + self.grid:draw() + end + end end return tab diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index 2bd97cf..b98d93b 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -13,91 +13,91 @@ local tab = UI.Tab { accelerators = { enter = 'update_path', }, - help = 'add a new path (reboot required)', + help = 'add a new path (reboot required)', }, grid = UI.Grid { y = 4, ey = -3, disableHeader = true, columns = { { key = 'value' } }, - autospace = true, - sortColumn = 'index', - help = 'double-click to remove, shift-arrow to move', - accelerators = { - delete = 'remove', - }, - }, - statusBar = UI.StatusBar { }, - accelerators = { - [ 'shift-up' ] = 'move_up', - [ 'shift-down' ] = 'move_down', - }, + autospace = true, + sortColumn = 'index', + help = 'double-click to remove, shift-arrow to move', + accelerators = { + delete = 'remove', + }, + }, + statusBar = UI.StatusBar { }, + accelerators = { + [ 'shift-up' ] = 'move_up', + [ 'shift-down' ] = 'move_down', + }, } function tab:updateList(lua_path) self.grid.values = { } for k,v in ipairs(Util.split(lua_path, '(.-);')) do table.insert(self.grid.values, { index = k, value = v }) - end - self.grid:update() + end + self.grid:update() end function tab:enable() - local env = Config.load('shell') - self:updateList(env.lua_path) + local env = Config.load('shell') + self:updateList(env.lua_path) UI.Tab.enable(self) end function tab:save() - local t = { } - for _, v in ipairs(self.grid.values) do - table.insert(t, v.value) - end - local env = Config.load('shell') - env.lua_path = table.concat(t, ';') - self:updateList(env.lua_path) - Config.update('shell', env) + local t = { } + for _, v in ipairs(self.grid.values) do + table.insert(t, v.value) + end + local env = Config.load('shell') + env.lua_path = table.concat(t, ';') + self:updateList(env.lua_path) + Config.update('shell', env) end function tab:eventHandler(event) - if event.type == 'update_path' then - table.insert(self.grid.values, { - value = self.entry.value, - }) - self:save() + if event.type == 'update_path' then + table.insert(self.grid.values, { + value = self.entry.value, + }) + self:save() self.entry:reset() - self.entry:draw() - self.grid:draw() + self.entry:draw() + self.grid:draw() return true - elseif event.type == 'grid_select' or event.type == 'remove' then - local selected = self.grid:getSelected() - if selected then - table.remove(self.grid.values, selected.index) - self:save() - self.grid:draw() - end + elseif event.type == 'grid_select' or event.type == 'remove' then + local selected = self.grid:getSelected() + if selected then + table.remove(self.grid.values, selected.index) + self:save() + self.grid:draw() + end - elseif event.type == 'focus_change' then + elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) - elseif event.type == 'move_up' then - local entry = self.grid:getSelected() - if entry.index > 1 then - table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index - 1) - self:save() - self.grid:draw() - end + elseif event.type == 'move_up' then + local entry = self.grid:getSelected() + if entry.index > 1 then + table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index - 1) + self:save() + self.grid:draw() + end - elseif event.type == 'move_down' then - local entry = self.grid:getSelected() - if entry.index < #self.grid.values then - table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index + 1) - self:save() - self.grid:draw() - end - end + elseif event.type == 'move_down' then + local entry = self.grid:getSelected() + if entry.index < #self.grid.values then + table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index + 1) + self:save() + self.grid:draw() + end + end end return tab diff --git a/sys/apps/system/shell.lua b/sys/apps/system/shell.lua index ee413f5..238a8d9 100644 --- a/sys/apps/system/shell.lua +++ b/sys/apps/system/shell.lua @@ -9,134 +9,134 @@ local config = Config.load('shellprompt') local allColors = { } for k,v in pairs(colors) do - if type(v) == 'number' then - table.insert(allColors, { name = k, value = v }) - end + if type(v) == 'number' then + table.insert(allColors, { name = k, value = v }) + end end local defaults = { - textColor = colors.white, - commandTextColor = colors.yellow, - directoryTextColor = colors.orange, - directoryBackgroundColor = colors.black, - promptTextColor = colors.blue, - promptBackgroundColor = colors.black, - directoryColor = colors.green, - fileColor = colors.white, - backgroundColor = colors.black, + textColor = colors.white, + commandTextColor = colors.yellow, + directoryTextColor = colors.orange, + directoryBackgroundColor = colors.black, + promptTextColor = colors.blue, + promptBackgroundColor = colors.black, + directoryColor = colors.green, + fileColor = colors.white, + backgroundColor = colors.black, } local _colors = config.color or Util.shallowCopy(defaults) local allSettings = { } for k, v in pairs(defaults) do - table.insert(allSettings, { name = k }) + table.insert(allSettings, { name = k }) end -- temp if not _colors.backgroundColor then - _colors.backgroundColor = colors.black - _colors.fileColor = colors.white + _colors.backgroundColor = colors.black + _colors.fileColor = colors.white end local tab = UI.Tab { tabTitle = 'Shell', - description = 'Shell options', - grid1 = UI.ScrollingGrid { - y = 2, ey = -10, x = 3, ex = -16, - disableHeader = true, - columns = { { key = 'name' } }, - values = allSettings, - sortColumn = 'name', - }, - grid2 = UI.ScrollingGrid { - y = 2, ey = -10, x = -14, ex = -3, + description = 'Shell options', + grid1 = UI.ScrollingGrid { + y = 2, ey = -10, x = 3, ex = -16, disableHeader = true, columns = { { key = 'name' } }, - values = allColors, - sortColumn = 'name', + values = allSettings, + sortColumn = 'name', + }, + grid2 = UI.ScrollingGrid { + y = 2, ey = -10, x = -14, ex = -3, + disableHeader = true, + columns = { { key = 'name' } }, + values = allColors, + sortColumn = 'name', + }, + directoryLabel = UI.Text { + x = 2, y = -2, + value = 'Display directory', + }, + directory = UI.Checkbox { + x = 20, y = -2, + value = config.displayDirectory + }, + reset = UI.Button { + x = -18, y = -2, + text = 'Reset', + event = 'reset', + }, + button = UI.Button { + x = -9, y = -2, + text = 'Update', + event = 'update', + }, + display = UI.Window { + x = 3, ex = -3, y = -8, height = 5, }, - directoryLabel = UI.Text { - x = 2, y = -2, - value = 'Display directory', - }, - directory = UI.Checkbox { - x = 20, y = -2, - value = config.displayDirectory - }, - reset = UI.Button { - x = -18, y = -2, - text = 'Reset', - event = 'reset', - }, - button = UI.Button { - x = -9, y = -2, - text = 'Update', - event = 'update', - }, - display = UI.Window { - x = 3, ex = -3, y = -8, height = 5, - }, } function tab.grid2:getRowTextColor(row) - local selected = tab.grid1:getSelected() - if _colors[selected.name] == row.value then - return colors.yellow - end - return UI.Grid.getRowTextColor(self, row) + local selected = tab.grid1:getSelected() + if _colors[selected.name] == row.value then + return colors.yellow + end + return UI.Grid.getRowTextColor(self, row) end function tab.display:draw() - self:clear(_colors.backgroundColor) - local offset = 0 - if config.displayDirectory then - self:write(1, 1, - '==' .. os.getComputerLabel() .. ':/dir/etc', - _colors.directoryBackgroundColor, _colors.directoryTextColor) - offset = 1 - end + self:clear(_colors.backgroundColor) + local offset = 0 + if config.displayDirectory then + self:write(1, 1, + '==' .. os.getComputerLabel() .. ':/dir/etc', + _colors.directoryBackgroundColor, _colors.directoryTextColor) + offset = 1 + end - self:write(1, 1 + offset, '$ ', - _colors.promptBackgroundColor, _colors.promptTextColor) + self:write(1, 1 + offset, '$ ', + _colors.promptBackgroundColor, _colors.promptTextColor) - self:write(3, 1 + offset, 'ls /', - _colors.backgroundColor, _colors.commandTextColor) + self:write(3, 1 + offset, 'ls /', + _colors.backgroundColor, _colors.commandTextColor) - self:write(1, 2 + offset, 'sys usr', - _colors.backgroundColor, _colors.directoryColor) + self:write(1, 2 + offset, 'sys usr', + _colors.backgroundColor, _colors.directoryColor) - self:write(1, 3 + offset, 'startup', - _colors.backgroundColor, _colors.fileColor) + self:write(1, 3 + offset, 'startup', + _colors.backgroundColor, _colors.fileColor) end function tab:eventHandler(event) - if event.type =='checkbox_change' then - config.displayDirectory = not not event.checked - self.display:draw() + if event.type =='checkbox_change' then + config.displayDirectory = not not event.checked + self.display:draw() - elseif event.type == 'grid_focus_row' and event.element == self.grid1 then - self.grid2:draw() + elseif event.type == 'grid_focus_row' and event.element == self.grid1 then + self.grid2:draw() - elseif event.type == 'grid_select' and event.element == self.grid2 then - _colors[tab.grid1:getSelected().name] = event.selected.value - self.display:draw() - self.grid2:draw() + elseif event.type == 'grid_select' and event.element == self.grid2 then + _colors[tab.grid1:getSelected().name] = event.selected.value + self.display:draw() + self.grid2:draw() - elseif event.type == 'reset' then - config.color = defaults - config.displayDirectory = true - self.directory.value = true - _colors = Util.shallowCopy(defaults) + elseif event.type == 'reset' then + config.color = defaults + config.displayDirectory = true + self.directory.value = true + _colors = Util.shallowCopy(defaults) - Config.update('shellprompt', config) - self:draw() + Config.update('shellprompt', config) + self:draw() - elseif event.type == 'update' then - config.color = _colors - Config.update('shellprompt', config) + elseif event.type == 'update' then + config.color = _colors + Config.update('shellprompt', config) - end - return UI.Tab.eventHandler(self, event) + end + return UI.Tab.eventHandler(self, event) end return tab diff --git a/sys/autorun/complete.lua b/sys/autorun/complete.lua index 332c8a3..82b2fe5 100644 --- a/sys/autorun/complete.lua +++ b/sys/autorun/complete.lua @@ -1,22 +1,22 @@ local function completeMultipleChoice(sText, tOptions, bAddSpaces) - local tResults = { } - for n = 1,#tOptions do - local sOption = tOptions[n] - if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub(sOption, 1, #sText) == sText then - local sResult = string.sub(sOption, #sText + 1) - if bAddSpaces then - table.insert(tResults, sResult .. " ") - else - table.insert(tResults, sResult) - end - end - end - return tResults + local tResults = { } + for n = 1,#tOptions do + local sOption = tOptions[n] + if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub(sOption, 1, #sText) == sText then + local sResult = string.sub(sOption, #sText + 1) + if bAddSpaces then + table.insert(tResults, sResult .. " ") + else + table.insert(tResults, sResult) + end + end + end + return tResults end _ENV.shell.setCompletionFunction("sys/apps/package.lua", - function(_, index, text) - if index == 1 then - return completeMultipleChoice(text, { "install ", "update ", "uninstall " }) - end - end) + function(_, index, text) + if index == 1 then + return completeMultipleChoice(text, { "install ", "update ", "uninstall " }) + end + end) diff --git a/sys/autorun/welcome.lua b/sys/autorun/welcome.lua index df0b2fd..2ecf6d8 100644 --- a/sys/autorun/welcome.lua +++ b/sys/autorun/welcome.lua @@ -4,8 +4,8 @@ local shell = _ENV.shell local config = Config.load('os') if not config.welcomed and shell.openForegroundTab then - config.welcomed = true - Config.update('os', config) + config.welcomed = true + Config.update('os', config) - shell.openForegroundTab('Welcome') + shell.openForegroundTab('Welcome') end diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index 6eb8c57..91a7159 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -6,47 +6,47 @@ local os = _G.os local peripheral = _G.peripheral local containers = { - manipulator = true, - neuralInterface = true, + manipulator = true, + neuralInterface = true, } local function getModules(dev, side) - local list = { } + local list = { } - if dev then - for _, module in pairs(dev.listModules()) do - list[module] = Util.shallowCopy(dev) - list[module].name = module - list[module].type = module - list[module].side = side - end - end - return list + if dev then + for _, module in pairs(dev.listModules()) do + list[module] = Util.shallowCopy(dev) + list[module].name = module + list[module].type = module + list[module].side = side + end + end + return list end for _,v in pairs(device) do - if containers[v.type] then - local list = getModules(v, v.side) - for k, dev in pairs(list) do - -- neural and attached modules have precedence over manipulator modules - if not device[k] or v.type ~= 'manipulator' then - device[k] = dev - end - end - end + if containers[v.type] then + local list = getModules(v, v.side) + for k, dev in pairs(list) do + -- neural and attached modules have precedence over manipulator modules + if not device[k] or v.type ~= 'manipulator' then + device[k] = dev + end + end + end end -- register modules as peripherals kernel.hook('device_attach', function(_, eventData) - local dev = eventData[2] + local dev = eventData[2] - if dev and containers[dev.type] then - local list = getModules(peripheral.wrap(dev.side), dev.side) - for k,v in pairs(list) do - if not device[k] or dev.type ~= 'manipulator' then - device[k] = v - os.queueEvent('device_attach', k, v) - end - end - end + if dev and containers[dev.type] then + local list = getModules(peripheral.wrap(dev.side), dev.side) + for k,v in pairs(list) do + if not device[k] or dev.type ~= 'manipulator' then + device[k] = v + os.queueEvent('device_attach', k, v) + end + end + end end) diff --git a/sys/init/3.relay.lua b/sys/init/3.relay.lua index ca75dc2..5ddc4bc 100644 --- a/sys/init/3.relay.lua +++ b/sys/init/3.relay.lua @@ -2,26 +2,26 @@ local device = _G.device local kernel = _G.kernel local function register(v) - if v and v.isWireless and v.isAccessPoint and v.getNamesRemote then - v._children = { } - for _, name in pairs(v.getNamesRemote()) do - local dev = v.getMethodsRemote(name) - if dev then - dev.name = name - dev.side = name - dev.type = v.getTypeRemote(name) - device[name] = dev - table.insert(v._children, dev) - end - end - end + if v and v.isWireless and v.isAccessPoint and v.getNamesRemote then + v._children = { } + for _, name in pairs(v.getNamesRemote()) do + local dev = v.getMethodsRemote(name) + if dev then + dev.name = name + dev.side = name + dev.type = v.getTypeRemote(name) + device[name] = dev + table.insert(v._children, dev) + end + end + end end for _,v in pairs(device) do - register(v) + register(v) end -- register oc devices as peripherals kernel.hook('device_attach', function(_, eventData) - register(device[eventData[2]]) + register(device[eventData[2]]) end) diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index 9baf271..ba52af1 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -6,7 +6,9 @@ local help = _G.help local shell = _ENV.shell if not fs.exists('usr/config/packages') then - Packages:downloadList() + pcall(function() + Packages:downloadList() + end) end local appPaths = Util.split(shell.path(), '(.-):') diff --git a/sys/init/6.tl3.lua b/sys/init/6.tl3.lua index 53519c4..4e797e2 100644 --- a/sys/init/6.tl3.lua +++ b/sys/init/6.tl3.lua @@ -52,6 +52,7 @@ function turtle.resetState() state.movePolicy = _defaultMove state.moveCallback = noop state.blacklist = nil + state.reference = nil -- gps reference when converting to relative coords Pathing.reset() return true end @@ -77,8 +78,6 @@ local function _dig(name, inspect, dig) return dig() end --- override dig --- optionally check that the block is a certain type function turtle.dig(s) return _dig(s, turtle.inspect, turtle.native.dig) end @@ -359,7 +358,7 @@ function turtle.set(args) turtle.setDigPolicy(turtle.getPolicy(v)) elseif k == 'movePolicy' then - turtle.setMovePolicy(turtle.getPolicy(v)) + state.movePolicy = turtle.getPolicy(v) elseif k == 'movementStrategy' then turtle.setMovementStrategy(v) @@ -379,6 +378,9 @@ function turtle.set(args) elseif k == 'blacklist' then state.blacklist = v + elseif k == 'reference' then + state.reference = v + else error('Invalid turle.set: ' .. tostring(k)) end @@ -426,6 +428,10 @@ function turtle.setHeading(heading) return false, 'Invalid heading' end + if heading == turtle.point.heading then + return turtle.point + end + local fi = Point.facings[heading] if not fi then return false, 'Invalid heading' -- 2.49.1 From 97a442e99974356e313fd9c9d0bcb5723737f69c Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 27 Jun 2019 16:29:12 -0400 Subject: [PATCH 017/236] socket update --- sys/apis/socket.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sys/apis/socket.lua b/sys/apis/socket.lua index fd73abd..726b4d3 100644 --- a/sys/apis/socket.lua +++ b/sys/apis/socket.lua @@ -115,7 +115,7 @@ function Socket.connect(host, port) type = 'OPEN', shost = socket.shost, dhost = socket.dhost, - t = Crypto.encrypt({ ts = os.time(), seq = socket.seq }, Security.getPublicKey()), + t = Crypto.encrypt({ ts = os.time(), seq = socket.seq, nts = os.epoch('utc') }, Security.getPublicKey()), rseq = socket.wseq, wseq = socket.rseq, }) @@ -170,6 +170,10 @@ local function trusted(msg, port) if pubKey then local data = Crypto.decrypt(msg.t or '', pubKey) + if data.nts then -- upgraded security + return data.nts and tonumber(data.nts) and math.abs(os.epoch('utc') - data.nts) < 1024 + end + --local sharedKey = modexp(pubKey, exchange.secretKey, public.primeMod) return data.ts and tonumber(data.ts) and math.abs(os.time() - data.ts) < 24 end -- 2.49.1 From bcd33af599c1fda61043276b1f959f2fe5edd03b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 27 Jun 2019 21:08:46 -0400 Subject: [PATCH 018/236] The big Anavrins security update (round 1) --- sys/apis/crypto.lua | 150 ----- sys/apis/crypto/chacha20.lua | 167 ++++++ sys/apis/crypto/ecc/elliptic.lua | 300 ++++++++++ sys/apis/crypto/ecc/fp.lua | 928 +++++++++++++++++++++++++++++++ sys/apis/crypto/ecc/fq.lua | 741 ++++++++++++++++++++++++ sys/apis/crypto/ecc/init.lua | 87 +++ sys/apis/{ => crypto}/sha2.lua | 21 +- sys/apis/injector.lua | 2 +- sys/apis/security.lua | 29 +- sys/apis/socket.lua | 11 +- sys/apis/util.lua | 13 + sys/apps/Welcome.lua | 5 +- sys/apps/network/trust.lua | 6 +- sys/apps/password.lua | 4 +- sys/apps/system/password.lua | 6 +- sys/apps/trust.lua | 6 +- sys/autorun/welcome.lua | 36 +- 17 files changed, 2308 insertions(+), 204 deletions(-) delete mode 100644 sys/apis/crypto.lua create mode 100644 sys/apis/crypto/chacha20.lua create mode 100644 sys/apis/crypto/ecc/elliptic.lua create mode 100644 sys/apis/crypto/ecc/fp.lua create mode 100644 sys/apis/crypto/ecc/fq.lua create mode 100644 sys/apis/crypto/ecc/init.lua rename sys/apis/{ => crypto}/sha2.lua (92%) diff --git a/sys/apis/crypto.lua b/sys/apis/crypto.lua deleted file mode 100644 index fc1075b..0000000 --- a/sys/apis/crypto.lua +++ /dev/null @@ -1,150 +0,0 @@ --- https://github.com/PixelToast/ComputerCraft/blob/master/apis/enc - -local Crypto = { } - -local function serialize(t) - local sType = type(t) - if sType == "table" then - local lstcnt=0 - for k,v in pairs(t) do - lstcnt = lstcnt + 1 - end - local result = "{" - local aset=1 - for k,v in pairs(t) do - if k==aset then - result = result..serialize(v).."," - aset=aset+1 - else - result = result..("["..serialize(k).."]="..serialize(v)..",") - end - end - result = result.."}" - return result - elseif sType == "string" then - return string.format("%q",t) - elseif sType == "number" or sType == "boolean" or sType == "nil" then - return tostring(t) - elseif sType == "function" then - local status,data=pcall(string.dump,t) - if status then - data2="" - for char in string.gmatch(data,".") do - data2=data2..zfill(string.byte(char)) - end - return 'f("'..data2..'")' - else - error("Invalid function: "..data) - end - else - error("Could not serialize type "..sType..".") - end -end - -local function unserialize( s ) - local func, e = loadstring( "return "..s, "serialize" ) - if not func then - return s,e - else - setfenv( func, { - f=function(S) - return loadstring(splitnum(S)) - end, - }) - return func() - end -end - -local function splitnum(S) - local Out="" - for l1=1,#S,2 do - local l2=(#S-l1)+1 - local function sure(N,n) - if (l2-n)<1 then N="0" end - return N - end - local CNum=tonumber("0x"..sure(string.sub(S,l2-1,l2-1),1) .. sure(string.sub(S,l2,l2),0)) - Out=string.char(CNum)..Out - end - return Out -end - -local function zfill(N) - N=string.format("%X",N) - Zs="" - if #N==1 then - Zs="0" - end - return Zs..N -end - -local function wrap(N) - return N-(math.floor(N/256)*256) -end - -local function checksum(S) - local sum=0 - for char in string.gmatch(S,".") do - math.randomseed(string.byte(char)+sum) - sum=sum+math.random(0,9999) - end - math.randomseed(sum) - return sum -end - -local function genkey(len,psw) - checksum(psw) - local key={} - local tKeys={} - for l1=1,len do - local num=math.random(1,len) - while tKeys[num] do - num=math.random(1,len) - end - tKeys[num]=true - key[l1]={num,math.random(0,255)} - end - return key -end - -function Crypto.encrypt(data,psw) - data=serialize(data) - local chs=checksum(data) - local key=genkey(#data,psw) - local out={} - local cnt=1 - for char in string.gmatch(data,".") do - table.insert(out,key[cnt][1],zfill(wrap(string.byte(char)+key[cnt][2])),chars) - cnt=cnt+1 - end - return string.sub(serialize({chs,table.concat(out)}),2,-3) -end - -function Crypto.decrypt(data,psw) - local oData=data - data=unserialize("{"..data.."}") - if type(data)~="table" then - return oData - end - local chs=data[1] - data=data[2] - local key=genkey((#data)/2,psw) - local sKey={} - for k,v in pairs(key) do - sKey[v[1]]={k,v[2]} - end - local str=splitnum(data) - local cnt=1 - local out={} - for char in string.gmatch(str,".") do - table.insert(out,sKey[cnt][1],string.char(wrap(string.byte(char)-sKey[cnt][2]))) - cnt=cnt+1 - end - out=table.concat(out) - if checksum(out or "")==chs then - return unserialize(out) - end - return oData,out,chs -end - -return Crypto diff --git a/sys/apis/crypto/chacha20.lua b/sys/apis/crypto/chacha20.lua new file mode 100644 index 0000000..b0763dd --- /dev/null +++ b/sys/apis/crypto/chacha20.lua @@ -0,0 +1,167 @@ +-- Chacha20 cipher in ComputerCraft +-- By Anavrins + +local sha2 = require('crypto.sha2') +local util = require('util') + +local ROUNDS = 20 -- Adjust this for speed tradeoff + +local bxor = bit32.bxor +local band = bit32.band +local blshift = bit32.lshift +local brshift = bit32.arshift +local textutils = _G.textutils + +local mod = 2^32 +local tau = {("expand 16-byte k"):byte(1,-1)} +local sigma = {("expand 32-byte k"):byte(1,-1)} + +local function rotl(n, b) + local s = n/(2^(32-b)) + local f = s%1 + return (s-f) + f*mod +end + +local function quarterRound(s, a, b, c, d) + s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 16) + s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 12) + s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 8) + s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 7) + return s +end + +local function hashBlock(state, rnd) + local s = {unpack(state)} + for i = 1, rnd do + local r = i%2==1 + s = r and quarterRound(s, 1, 5, 9, 13) or quarterRound(s, 1, 6, 11, 16) + s = r and quarterRound(s, 2, 6, 10, 14) or quarterRound(s, 2, 7, 12, 13) + s = r and quarterRound(s, 3, 7, 11, 15) or quarterRound(s, 3, 8, 9, 14) + s = r and quarterRound(s, 4, 8, 12, 16) or quarterRound(s, 4, 5, 10, 15) + end + for i = 1, 16 do s[i] = (s[i]+state[i])%mod end + return s +end + +local function LE_toInt(bs, i) + return (bs[i+1] or 0)+ + blshift((bs[i+2] or 0), 8)+ + blshift((bs[i+3] or 0), 16)+ + blshift((bs[i+4] or 0), 24) +end + +local function initState(key, nonce, counter) + local isKey256 = #key == 32 + local const = isKey256 and sigma or tau + local state = {} + + state[ 1] = LE_toInt(const, 0) + state[ 2] = LE_toInt(const, 4) + state[ 3] = LE_toInt(const, 8) + state[ 4] = LE_toInt(const, 12) + + state[ 5] = LE_toInt(key, 0) + state[ 6] = LE_toInt(key, 4) + state[ 7] = LE_toInt(key, 8) + state[ 8] = LE_toInt(key, 12) + state[ 9] = LE_toInt(key, isKey256 and 16 or 0) + state[10] = LE_toInt(key, isKey256 and 20 or 4) + state[11] = LE_toInt(key, isKey256 and 24 or 8) + state[12] = LE_toInt(key, isKey256 and 28 or 12) + + state[13] = counter + state[14] = LE_toInt(nonce, 0) + state[15] = LE_toInt(nonce, 4) + state[16] = LE_toInt(nonce, 8) + + return state +end + +local function serialize(state) + local r = {} + for i = 1, 16 do + r[#r+1] = band(state[i], 0xFF) + r[#r+1] = band(brshift(state[i], 8), 0xFF) + r[#r+1] = band(brshift(state[i], 16), 0xFF) + r[#r+1] = band(brshift(state[i], 24), 0xFF) + end + return r +end + +local mt = { + __tostring = function(a) return string.char(unpack(a)) end, + __index = { + toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end, + isEqual = function(self, t) + if type(t) ~= "table" then return false end + if #self ~= #t then return false end + local ret = 0 + for i = 1, #self do + ret = bit32.bor(ret, bxor(self[i], t[i])) + end + return ret == 0 + end + } +} + +local function crypt(data, key, nonce, cntr, round) + assert(type(key) == "table", "ChaCha20: Invalid key format ("..type(key).."), must be table") + assert(type(nonce) == "table", "ChaCha20: Invalid nonce format ("..type(nonce).."), must be table") + assert(#key == 16 or #key == 32, "ChaCha20: Invalid key length ("..#key.."), must be 16 or 32") + assert(#nonce == 12, "ChaCha20: Invalid nonce length ("..#nonce.."), must be 12") + + local data = type(data) == "table" and {unpack(data)} or {tostring(data):byte(1,-1)} + cntr = tonumber(cntr) or 1 + round = tonumber(round) or 20 + + local out = {} + local state = initState(key, nonce, cntr) + local blockAmt = math.floor(#data/64) + for i = 0, blockAmt do + local ks = serialize(hashBlock(state, round)) + state[13] = (state[13]+1) % mod + + local block = {} + for j = 1, 64 do + block[j] = data[((i)*64)+j] + end + for j = 1, #block do + out[#out+1] = bxor(block[j], ks[j]) + end + + if i % 1000 == 0 then + os.queueEvent("") + os.pullEvent("") + end + end + return setmetatable(out, mt) +end + +local function genNonce(len) + local nonce = {} + for i = 1, len do + nonce[i] = math.random(0, 0xFF) + end + return setmetatable(nonce, mt) +end + +local function encrypt(data, key) + local nonce = genNonce(12) + data = textutils.serialise(data) + key = sha2.digest(key) + local ctx = crypt(data, key, nonce, 1, ROUNDS) + return { nonce:toHex(), ctx:toHex() } +end + +local function decrypt(data, key) + local nonce = util.hexToByteArray(data[1]) + data = util.hexToByteArray(data[2]) + key = sha2.digest(key) + local ptx = crypt(data, key, nonce, 1, ROUNDS) + return textutils.unserialise(tostring(ptx)) +end + +return { + encrypt = encrypt, + decrypt = decrypt, +} diff --git a/sys/apis/crypto/ecc/elliptic.lua b/sys/apis/crypto/ecc/elliptic.lua new file mode 100644 index 0000000..ca2b5b5 --- /dev/null +++ b/sys/apis/crypto/ecc/elliptic.lua @@ -0,0 +1,300 @@ +---- Elliptic Curve Arithmetic + +---- About the Curve Itself +-- Field Size: 192 bits +-- Field Modulus (p): 65533 * 2^176 + 3 +-- Equation: x^2 + y^2 = 1 + 108 * x^2 * y^2 +-- Parameters: Edwards Curve with c = 1, and d = 108 +-- Curve Order (n): 4 * 1569203598118192102418711808268118358122924911136798015831 +-- Cofactor (h): 4 +-- Generator Order (q): 1569203598118192102418711808268118358122924911136798015831 +---- About the Curve's Security +-- Current best attack security: 94.822 bits (Pollard's Rho) +-- Rho Security: log2(0.884 * sqrt(q)) = 94.822 +-- Transfer Security? Yes: p ~= q; k > 20 +-- Field Discriminant Security? Yes: t = 67602300638727286331433024168; s = 2^2; |D| = 5134296629560551493299993292204775496868940529592107064435 > 2^100 +-- Rigidity? A little, the parameters are somewhat small. +-- XZ/YZ Ladder Security? No: Single coordinate ladders are insecure, so they can't be used. +-- Small Subgroup Security? Yes: Secret keys are calculated modulo 4q. +-- Invalid Curve Security? Yes: Any point to be multiplied is checked beforehand. +-- Invalid Curve Twist Security? No: The curve is not protected against single coordinate ladder attacks, so don't use them. +-- Completeness? Yes: The curve is an Edwards Curve with non-square d and square a, so the curve is complete. +-- Indistinguishability? No: The curve does not support indistinguishability maps. + +local fp = require('crypto.ecc.fp') +local eq = fp.eq +local mul = fp.mul +local sqr = fp.sqr +local add = fp.add +local sub = fp.sub +local shr = fp.shr +local mont = fp.mont +local invMont = fp.invMont +local sub192 = fp.sub192 + +local bits = 192 +local pMinusTwoBinary = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} +local pMinusThreeOverFourBinary = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0} +local ZERO = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +local ONE = mont({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + +local p = mont({3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65533}) +local G = { + mont({30457, 58187, 5603, 63215, 8936, 58151, 26571, 7272, 26680, 23486, 32353, 59456}), + mont({3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), + mont({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) +} +local GTable = {G} + +local d = mont({108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + +local function generator() + return G +end + +local function expMod(a, t) + local a = {unpack(a)} + local result = {unpack(ONE)} + + for i = 1, bits do + if t[i] == 1 then + result = mul(result, a) + end + a = mul(a, a) + end + + return result +end + +-- We're using Projective Coordinates +-- For Edwards curves +-- The identity element is represented by (0:1:1) +local function pointDouble(P1) + local X1, Y1, Z1 = unpack(P1) + + local b = add(X1, Y1) + local B = sqr(b) + local C = sqr(X1) + local D = sqr(Y1) + local E = add(C, D) + local H = sqr(Z1) + local J = sub(E, add(H, H)) + local X3 = mul(sub(B, E), J) + local Y3 = mul(E, sub(C, D)) + local Z3 = mul(E, J) + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointAdd(P1, P2) + local X1, Y1, Z1 = unpack(P1) + local X2, Y2, Z2 = unpack(P2) + + local A = mul(Z1, Z2) + local B = sqr(A) + local C = mul(X1, X2) + local D = mul(Y1, Y2) + local E = mul(d, mul(C, D)) + local F = sub(B, E) + local G = add(B, E) + local X3 = mul(A, mul(F, sub(mul(add(X1, Y1), add(X2, Y2)), add(C, D)))) + local Y3 = mul(A, mul(G, sub(D, C))) + local Z3 = mul(F, G) + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointNeg(P1) + local X1, Y1, Z1 = unpack(P1) + + local X3 = sub(p, X1) + local Y3 = {unpack(Y1)} + local Z3 = {unpack(Z1)} + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointSub(P1, P2) + return pointAdd(P1, pointNeg(P2)) +end + +local function pointScale(P1) + local X1, Y1, Z1 = unpack(P1) + + local A = expMod(Z1, pMinusTwoBinary) + local X3 = mul(X1, A) + local Y3 = mul(Y1, A) + local Z3 = {unpack(ONE)} + + local P3 = {X3, Y3, Z3} + + return P3 +end + +local function pointEq(P1, P2) + local X1, Y1, Z1 = unpack(P1) + local X2, Y2, Z2 = unpack(P2) + + local A1 = mul(X1, Z2) + local B1 = mul(Y1, Z2) + local A2 = mul(X2, Z1) + local B2 = mul(Y2, Z1) + + return eq(A1, A2) and eq(B1, B2) +end + +local function isOnCurve(P1) + local X1, Y1, Z1 = unpack(P1) + + local X12 = sqr(X1) + local Y12 = sqr(Y1) + local Z12 = sqr(Z1) + local Z14 = sqr(Z12) + local a = add(X12, Y12) + a = mul(a, Z12) + local b = mul(d, mul(X12, Y12)) + b = add(Z14, b) + + return eq(a, b) +end + +local function mods(d) + -- w = 5 + local result = d[1] % 32 + + if result >= 16 then + result = result - 32 + end + + return result +end + +local function NAF(d) + local t = {} + local d = {unpack(d)} + + while d[12] >= 0 and not eq(d, ZERO) do + if d[1] % 2 == 1 then + t[#t + 1] = mods(d) + d = sub192(d, {t[#t], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + else + t[#t + 1] = 0 + end + + d = shr(d) + end + + return t +end + +local function scalarMul(s, P1) + local naf = NAF(s) + local PTable = {P1} + local P2 = pointDouble(P1) + + for i = 3, 31, 2 do + PTable[i] = pointAdd(PTable[i - 2], P2) + end + + local Q = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}} + for i = #naf, 1, -1 do + Q = pointDouble(Q) + if naf[i] > 0 then + Q = pointAdd(Q, PTable[naf[i]]) + elseif naf[i] < 0 then + Q = pointSub(Q, PTable[-naf[i]]) + end + end + + return Q +end + +for i = 2, 196 do + GTable[i] = pointDouble(GTable[i - 1]) +end + +local function scalarMulG(s) + local result = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}} + local k = 1 + + for i = 1, 12 do + local w = s[i] + + for j = 1, 16 do + if w % 2 == 1 then + result = pointAdd(result, GTable[k]) + end + + k = k + 1 + + w = w / 2 + w = w - w % 1 + end + end + + return result +end + +local function pointEncode(P1) + P1 = pointScale(P1) + + local result = {} + local x, y = unpack(P1) + + result[1] = x[1] % 2 + + for i = 1, 12 do + local m = y[i] % 256 + result[2 * i] = m + result[2 * i + 1] = (y[i] - m) / 256 + end + + return result +end + +local function pointDecode(enc) + local y = {} + for i = 1, 12 do + y[i] = enc[2 * i] + y[i] = y[i] + enc[2 * i + 1] * 256 + end + + local y2 = sqr(y) + local u = sub(y2, ONE) + local v = sub(mul(d, y2), ONE) + local u2 = sqr(u) + local u3 = mul(u, u2) + local u5 = mul(u3, u2) + local v3 = mul(v, sqr(v)) + local w = mul(u5, v3) + local x = mul(u3, mul(v, expMod(w, pMinusThreeOverFourBinary))) + + if x[1] % 2 ~= enc[1] then + x = sub(p, x) + end + + local P3 = {x, y, {unpack(ONE)}} + + return P3 +end + +return { + generator = generator, + pointDouble = pointDouble, + pointAdd = pointAdd, + pointNeg = pointNeg, + pointSub = pointSub, + pointScale = pointScale, + pointEq = pointEq, + isOnCurve = isOnCurve, + scalarMul = scalarMul, + scalarMulG = scalarMulG, + pointEncode = pointEncode, + pointDecode = pointDecode, +} diff --git a/sys/apis/crypto/ecc/fp.lua b/sys/apis/crypto/ecc/fp.lua new file mode 100644 index 0000000..3a8c4c6 --- /dev/null +++ b/sys/apis/crypto/ecc/fp.lua @@ -0,0 +1,928 @@ +-- Fp Integer Arithmetic + +local n = 0xffff +local m = 0x10000 + +local p = {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65533} +local p2 = {21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 43690} +local r2 = {44014, 58358, 19452, 6484, 45852, 58974, 63348, 64806, 65292, 65454, 65508, 21512} + +local function eq(a, b) + for i = 1, 12 do + if a[i] ~= b[i] then + return false + end + end + + return true +end + +local function reduce(a) + local r1 = a[1] + local r2 = a[2] + local r3 = a[3] + local r4 = a[4] + local r5 = a[5] + local r6 = a[6] + local r7 = a[7] + local r8 = a[8] + local r9 = a[9] + local r10 = a[10] + local r11 = a[11] + local r12 = a[12] + + if r12 < 65533 or r12 == 65533 and r1 < 3 then + return {unpack(a)} + end + + r1 = r1 - 3 + r12 = r12 - 65533 + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + return {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} +end + +local function add(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return reduce(result) +end + +local function shr(a) + local r1 = a[1] + local r2 = a[2] + local r3 = a[3] + local r4 = a[4] + local r5 = a[5] + local r6 = a[6] + local r7 = a[7] + local r8 = a[8] + local r9 = a[9] + local r10 = a[10] + local r11 = a[11] + local r12 = a[12] + + r1 = r1 / 2 + r1 = r1 - r1 % 1 + r1 = r1 + (r2 % 2) * 0x8000 + r2 = r2 / 2 + r2 = r2 - r2 % 1 + r2 = r2 + (r3 % 2) * 0x8000 + r3 = r3 / 2 + r3 = r3 - r3 % 1 + r3 = r3 + (r4 % 2) * 0x8000 + r4 = r4 / 2 + r4 = r4 - r4 % 1 + r4 = r4 + (r5 % 2) * 0x8000 + r5 = r5 / 2 + r5 = r5 - r5 % 1 + r5 = r5 + (r6 % 2) * 0x8000 + r6 = r6 / 2 + r6 = r6 - r6 % 1 + r6 = r6 + (r7 % 2) * 0x8000 + r7 = r7 / 2 + r7 = r7 - r7 % 1 + r7 = r7 + (r8 % 2) * 0x8000 + r8 = r8 / 2 + r8 = r8 - r8 % 1 + r8 = r8 + (r9 % 2) * 0x8000 + r9 = r9 / 2 + r9 = r9 - r9 % 1 + r9 = r9 + (r10 % 2) * 0x8000 + r10 = r10 / 2 + r10 = r10 - r10 % 1 + r10 = r10 + (r11 % 2) * 0x8000 + r11 = r11 / 2 + r11 = r11 - r11 % 1 + r11 = r11 + (r12 % 2) * 0x8000 + r12 = r12 / 2 + r12 = r12 - r12 % 1 + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return result +end + +local function sub192(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return result +end + +local function sub(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + if r12 < 0 then + result = add(result, p) + end + + return result +end + +local function add384(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + local r13 = a[13] + b[13] + local r14 = a[14] + b[14] + local r15 = a[15] + b[15] + local r16 = a[16] + b[16] + local r17 = a[17] + b[17] + local r18 = a[18] + b[18] + local r19 = a[19] + b[19] + local r20 = a[20] + b[20] + local r21 = a[21] + b[21] + local r22 = a[22] + b[22] + local r23 = a[23] + b[23] + local r24 = a[24] + b[24] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + if r12 > n then + r13 = r13 + 1 + r12 = r12 - m + end + if r13 > n then + r14 = r14 + 1 + r13 = r13 - m + end + if r14 > n then + r15 = r15 + 1 + r14 = r14 - m + end + if r15 > n then + r16 = r16 + 1 + r15 = r15 - m + end + if r16 > n then + r17 = r17 + 1 + r16 = r16 - m + end + if r17 > n then + r18 = r18 + 1 + r17 = r17 - m + end + if r18 > n then + r19 = r19 + 1 + r18 = r18 - m + end + if r19 > n then + r20 = r20 + 1 + r19 = r19 - m + end + if r20 > n then + r21 = r21 + 1 + r20 = r20 - m + end + if r21 > n then + r22 = r22 + 1 + r21 = r21 - m + end + if r22 > n then + r23 = r23 + 1 + r22 = r22 - m + end + if r23 > n then + r24 = r24 + 1 + r23 = r23 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function mul384(a, b) + local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 = unpack(a) + local b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12 = unpack(b) + + local r1 = a1 * b1 + + local r2 = a1 * b2 + r2 = r2 + a2 * b1 + + local r3 = a1 * b3 + r3 = r3 + a2 * b2 + r3 = r3 + a3 * b1 + + local r4 = a1 * b4 + r4 = r4 + a2 * b3 + r4 = r4 + a3 * b2 + r4 = r4 + a4 * b1 + + local r5 = a1 * b5 + r5 = r5 + a2 * b4 + r5 = r5 + a3 * b3 + r5 = r5 + a4 * b2 + r5 = r5 + a5 * b1 + + local r6 = a1 * b6 + r6 = r6 + a2 * b5 + r6 = r6 + a3 * b4 + r6 = r6 + a4 * b3 + r6 = r6 + a5 * b2 + r6 = r6 + a6 * b1 + + local r7 = a1 * b7 + r7 = r7 + a2 * b6 + r7 = r7 + a3 * b5 + r7 = r7 + a4 * b4 + r7 = r7 + a5 * b3 + r7 = r7 + a6 * b2 + r7 = r7 + a7 * b1 + + local r8 = a1 * b8 + r8 = r8 + a2 * b7 + r8 = r8 + a3 * b6 + r8 = r8 + a4 * b5 + r8 = r8 + a5 * b4 + r8 = r8 + a6 * b3 + r8 = r8 + a7 * b2 + r8 = r8 + a8 * b1 + + local r9 = a1 * b9 + r9 = r9 + a2 * b8 + r9 = r9 + a3 * b7 + r9 = r9 + a4 * b6 + r9 = r9 + a5 * b5 + r9 = r9 + a6 * b4 + r9 = r9 + a7 * b3 + r9 = r9 + a8 * b2 + r9 = r9 + a9 * b1 + + local r10 = a1 * b10 + r10 = r10 + a2 * b9 + r10 = r10 + a3 * b8 + r10 = r10 + a4 * b7 + r10 = r10 + a5 * b6 + r10 = r10 + a6 * b5 + r10 = r10 + a7 * b4 + r10 = r10 + a8 * b3 + r10 = r10 + a9 * b2 + r10 = r10 + a10 * b1 + + local r11 = a1 * b11 + r11 = r11 + a2 * b10 + r11 = r11 + a3 * b9 + r11 = r11 + a4 * b8 + r11 = r11 + a5 * b7 + r11 = r11 + a6 * b6 + r11 = r11 + a7 * b5 + r11 = r11 + a8 * b4 + r11 = r11 + a9 * b3 + r11 = r11 + a10 * b2 + r11 = r11 + a11 * b1 + + local r12 = a1 * b12 + r12 = r12 + a2 * b11 + r12 = r12 + a3 * b10 + r12 = r12 + a4 * b9 + r12 = r12 + a5 * b8 + r12 = r12 + a6 * b7 + r12 = r12 + a7 * b6 + r12 = r12 + a8 * b5 + r12 = r12 + a9 * b4 + r12 = r12 + a10 * b3 + r12 = r12 + a11 * b2 + r12 = r12 + a12 * b1 + + local r13 = a2 * b12 + r13 = r13 + a3 * b11 + r13 = r13 + a4 * b10 + r13 = r13 + a5 * b9 + r13 = r13 + a6 * b8 + r13 = r13 + a7 * b7 + r13 = r13 + a8 * b6 + r13 = r13 + a9 * b5 + r13 = r13 + a10 * b4 + r13 = r13 + a11 * b3 + r13 = r13 + a12 * b2 + + local r14 = a3 * b12 + r14 = r14 + a4 * b11 + r14 = r14 + a5 * b10 + r14 = r14 + a6 * b9 + r14 = r14 + a7 * b8 + r14 = r14 + a8 * b7 + r14 = r14 + a9 * b6 + r14 = r14 + a10 * b5 + r14 = r14 + a11 * b4 + r14 = r14 + a12 * b3 + + local r15 = a4 * b12 + r15 = r15 + a5 * b11 + r15 = r15 + a6 * b10 + r15 = r15 + a7 * b9 + r15 = r15 + a8 * b8 + r15 = r15 + a9 * b7 + r15 = r15 + a10 * b6 + r15 = r15 + a11 * b5 + r15 = r15 + a12 * b4 + + local r16 = a5 * b12 + r16 = r16 + a6 * b11 + r16 = r16 + a7 * b10 + r16 = r16 + a8 * b9 + r16 = r16 + a9 * b8 + r16 = r16 + a10 * b7 + r16 = r16 + a11 * b6 + r16 = r16 + a12 * b5 + + local r17 = a6 * b12 + r17 = r17 + a7 * b11 + r17 = r17 + a8 * b10 + r17 = r17 + a9 * b9 + r17 = r17 + a10 * b8 + r17 = r17 + a11 * b7 + r17 = r17 + a12 * b6 + + local r18 = a7 * b12 + r18 = r18 + a8 * b11 + r18 = r18 + a9 * b10 + r18 = r18 + a10 * b9 + r18 = r18 + a11 * b8 + r18 = r18 + a12 * b7 + + local r19 = a8 * b12 + r19 = r19 + a9 * b11 + r19 = r19 + a10 * b10 + r19 = r19 + a11 * b9 + r19 = r19 + a12 * b8 + + local r20 = a9 * b12 + r20 = r20 + a10 * b11 + r20 = r20 + a11 * b10 + r20 = r20 + a12 * b9 + + local r21 = a10 * b12 + r21 = r21 + a11 * b11 + r21 = r21 + a12 * b10 + + local r22 = a11 * b12 + r22 = r22 + a12 * b11 + + local r23 = a12 * b12 + + local r24 = 0 + + r2 = r2 + (r1 / m) + r2 = r2 - r2 % 1 + r1 = r1 % m + r3 = r3 + (r2 / m) + r3 = r3 - r3 % 1 + r2 = r2 % m + r4 = r4 + (r3 / m) + r4 = r4 - r4 % 1 + r3 = r3 % m + r5 = r5 + (r4 / m) + r5 = r5 - r5 % 1 + r4 = r4 % m + r6 = r6 + (r5 / m) + r6 = r6 - r6 % 1 + r5 = r5 % m + r7 = r7 + (r6 / m) + r7 = r7 - r7 % 1 + r6 = r6 % m + r8 = r8 + (r7 / m) + r8 = r8 - r8 % 1 + r7 = r7 % m + r9 = r9 + (r8 / m) + r9 = r9 - r9 % 1 + r8 = r8 % m + r10 = r10 + (r9 / m) + r10 = r10 - r10 % 1 + r9 = r9 % m + r11 = r11 + (r10 / m) + r11 = r11 - r11 % 1 + r10 = r10 % m + r12 = r12 + (r11 / m) + r12 = r12 - r12 % 1 + r11 = r11 % m + r13 = r13 + (r12 / m) + r13 = r13 - r13 % 1 + r12 = r12 % m + r14 = r14 + (r13 / m) + r14 = r14 - r14 % 1 + r13 = r13 % m + r15 = r15 + (r14 / m) + r15 = r15 - r15 % 1 + r14 = r14 % m + r16 = r16 + (r15 / m) + r16 = r16 - r16 % 1 + r15 = r15 % m + r17 = r17 + (r16 / m) + r17 = r17 - r17 % 1 + r16 = r16 % m + r18 = r18 + (r17 / m) + r18 = r18 - r18 % 1 + r17 = r17 % m + r19 = r19 + (r18 / m) + r19 = r19 - r19 % 1 + r18 = r18 % m + r20 = r20 + (r19 / m) + r20 = r20 - r20 % 1 + r19 = r19 % m + r21 = r21 + (r20 / m) + r21 = r21 - r21 % 1 + r20 = r20 % m + r22 = r22 + (r21 / m) + r22 = r22 - r22 % 1 + r21 = r21 % m + r23 = r23 + (r22 / m) + r23 = r23 - r23 % 1 + r22 = r22 % m + r24 = r24 + (r23 / m) + r24 = r24 - r24 % 1 + r23 = r23 % m + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function REDC(T) + local m = {unpack(mul384({unpack(T, 1, 12)}, p2), 1, 12)} + local t = {unpack(add384(T, mul384(m, p)), 13, 24)} + + return reduce(t) +end + +local function mul(a, b) + return REDC(mul384(a, b)) +end + +local function sqr(a) + local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 = unpack(a) + + local r1 = a1 * a1 + + local r2 = a1 * a2 * 2 + + local r3 = a1 * a3 * 2 + r3 = r3 + a2 * a2 + + local r4 = a1 * a4 * 2 + r4 = r4 + a2 * a3 * 2 + + local r5 = a1 * a5 * 2 + r5 = r5 + a2 * a4 * 2 + r5 = r5 + a3 * a3 + + local r6 = a1 * a6 * 2 + r6 = r6 + a2 * a5 * 2 + r6 = r6 + a3 * a4 * 2 + + local r7 = a1 * a7 * 2 + r7 = r7 + a2 * a6 * 2 + r7 = r7 + a3 * a5 * 2 + r7 = r7 + a4 * a4 + + local r8 = a1 * a8 * 2 + r8 = r8 + a2 * a7 * 2 + r8 = r8 + a3 * a6 * 2 + r8 = r8 + a4 * a5 * 2 + + local r9 = a1 * a9 * 2 + r9 = r9 + a2 * a8 * 2 + r9 = r9 + a3 * a7 * 2 + r9 = r9 + a4 * a6 * 2 + r9 = r9 + a5 * a5 + + local r10 = a1 * a10 * 2 + r10 = r10 + a2 * a9 * 2 + r10 = r10 + a3 * a8 * 2 + r10 = r10 + a4 * a7 * 2 + r10 = r10 + a5 * a6 * 2 + + local r11 = a1 * a11 * 2 + r11 = r11 + a2 * a10 * 2 + r11 = r11 + a3 * a9 * 2 + r11 = r11 + a4 * a8 * 2 + r11 = r11 + a5 * a7 * 2 + r11 = r11 + a6 * a6 + + local r12 = a1 * a12 * 2 + r12 = r12 + a2 * a11 * 2 + r12 = r12 + a3 * a10 * 2 + r12 = r12 + a4 * a9 * 2 + r12 = r12 + a5 * a8 * 2 + r12 = r12 + a6 * a7 * 2 + + local r13 = a2 * a12 * 2 + r13 = r13 + a3 * a11 * 2 + r13 = r13 + a4 * a10 * 2 + r13 = r13 + a5 * a9 * 2 + r13 = r13 + a6 * a8 * 2 + r13 = r13 + a7 * a7 + + local r14 = a3 * a12 * 2 + r14 = r14 + a4 * a11 * 2 + r14 = r14 + a5 * a10 * 2 + r14 = r14 + a6 * a9 * 2 + r14 = r14 + a7 * a8 * 2 + + local r15 = a4 * a12 * 2 + r15 = r15 + a5 * a11 * 2 + r15 = r15 + a6 * a10 * 2 + r15 = r15 + a7 * a9 * 2 + r15 = r15 + a8 * a8 + + local r16 = a5 * a12 * 2 + r16 = r16 + a6 * a11 * 2 + r16 = r16 + a7 * a10 * 2 + r16 = r16 + a8 * a9 * 2 + + local r17 = a6 * a12 * 2 + r17 = r17 + a7 * a11 * 2 + r17 = r17 + a8 * a10 * 2 + r17 = r17 + a9 * a9 + + local r18 = a7 * a12 * 2 + r18 = r18 + a8 * a11 * 2 + r18 = r18 + a9 * a10 * 2 + + local r19 = a8 * a12 * 2 + r19 = r19 + a9 * a11 * 2 + r19 = r19 + a10 * a10 + + local r20 = a9 * a12 * 2 + r20 = r20 + a10 * a11 * 2 + + local r21 = a10 * a12 * 2 + r21 = r21 + a11 * a11 + + local r22 = a11 * a12 * 2 + + local r23 = a12 * a12 + + local r24 = 0 + + r2 = r2 + (r1 / m) + r2 = r2 - r2 % 1 + r1 = r1 % m + r3 = r3 + (r2 / m) + r3 = r3 - r3 % 1 + r2 = r2 % m + r4 = r4 + (r3 / m) + r4 = r4 - r4 % 1 + r3 = r3 % m + r5 = r5 + (r4 / m) + r5 = r5 - r5 % 1 + r4 = r4 % m + r6 = r6 + (r5 / m) + r6 = r6 - r6 % 1 + r5 = r5 % m + r7 = r7 + (r6 / m) + r7 = r7 - r7 % 1 + r6 = r6 % m + r8 = r8 + (r7 / m) + r8 = r8 - r8 % 1 + r7 = r7 % m + r9 = r9 + (r8 / m) + r9 = r9 - r9 % 1 + r8 = r8 % m + r10 = r10 + (r9 / m) + r10 = r10 - r10 % 1 + r9 = r9 % m + r11 = r11 + (r10 / m) + r11 = r11 - r11 % 1 + r10 = r10 % m + r12 = r12 + (r11 / m) + r12 = r12 - r12 % 1 + r11 = r11 % m + r13 = r13 + (r12 / m) + r13 = r13 - r13 % 1 + r12 = r12 % m + r14 = r14 + (r13 / m) + r14 = r14 - r14 % 1 + r13 = r13 % m + r15 = r15 + (r14 / m) + r15 = r15 - r15 % 1 + r14 = r14 % m + r16 = r16 + (r15 / m) + r16 = r16 - r16 % 1 + r15 = r15 % m + r17 = r17 + (r16 / m) + r17 = r17 - r17 % 1 + r16 = r16 % m + r18 = r18 + (r17 / m) + r18 = r18 - r18 % 1 + r17 = r17 % m + r19 = r19 + (r18 / m) + r19 = r19 - r19 % 1 + r18 = r18 % m + r20 = r20 + (r19 / m) + r20 = r20 - r20 % 1 + r19 = r19 % m + r21 = r21 + (r20 / m) + r21 = r21 - r21 % 1 + r20 = r20 % m + r22 = r22 + (r21 / m) + r22 = r22 - r22 % 1 + r21 = r21 % m + r23 = r23 + (r22 / m) + r23 = r23 - r23 % 1 + r22 = r22 % m + r24 = r24 + (r23 / m) + r24 = r24 - r24 % 1 + r23 = r23 % m + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return REDC(result) +end + +local function mont(a) + return mul(a, r2) +end + +local function invMont(a) + local a = {unpack(a)} + + for i = 13, 24 do + a[i] = 0 + end + + return REDC(a) +end + +return { + eq = eq, + add = add, + shr = shr, + sub192 = sub192, + sub = sub, + mul = mul, + sqr = sqr, + mont = mont, + invMont = invMont, +} diff --git a/sys/apis/crypto/ecc/fq.lua b/sys/apis/crypto/ecc/fq.lua new file mode 100644 index 0000000..277f64d --- /dev/null +++ b/sys/apis/crypto/ecc/fq.lua @@ -0,0 +1,741 @@ +-- Fq Integer Arithmetic + +local n = 0xffff +local m = 0x10000 + +local q = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532} +local qn = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +local function eq(a, b) + for i = 1, 12 do + if a[i] ~= b[i] then + return false + end + end + + return true +end + +local function cmp(a, b) + for i = 12, 1, -1 do + if a[i] > b[i] then + return 1 + elseif a[i] < b[i] then + return -1 + end + end + + return 0 +end + +local function cmp384(a, b) + for i = 24, 1, -1 do + if a[i] > b[i] then + return 1 + elseif a[i] < b[i] then + return -1 + end + end + + return 0 +end + +local function bytes(x) + local result = {} + + for i = 0, 11 do + local m = x[i + 1] % 256 + result[2 * i + 1] = m + result[2 * i + 2] = (x[i + 1] - m) / 256 + end + + return result +end + +local function fromBytes(enc) + local result = {} + + for i = 0, 11 do + result[i + 1] = enc[2 * i + 1] % 256 + result[i + 1] = result[i + 1] + enc[2 * i + 2] * 256 + end + + return result +end + +local function sub192(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return result +end + +local function reduce(a) + local result = {unpack(a)} + + if cmp(result, q) >= 0 then + result = sub192(result, q) + end + + return result +end + +local function add(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12} + + return reduce(result) +end + +local function sub(a, b) + local result = sub192(a, b) + + if result[12] < 0 then + result = add(result, q) + end + + return result +end + +local function add384(a, b) + local r1 = a[1] + b[1] + local r2 = a[2] + b[2] + local r3 = a[3] + b[3] + local r4 = a[4] + b[4] + local r5 = a[5] + b[5] + local r6 = a[6] + b[6] + local r7 = a[7] + b[7] + local r8 = a[8] + b[8] + local r9 = a[9] + b[9] + local r10 = a[10] + b[10] + local r11 = a[11] + b[11] + local r12 = a[12] + b[12] + local r13 = a[13] + b[13] + local r14 = a[14] + b[14] + local r15 = a[15] + b[15] + local r16 = a[16] + b[16] + local r17 = a[17] + b[17] + local r18 = a[18] + b[18] + local r19 = a[19] + b[19] + local r20 = a[20] + b[20] + local r21 = a[21] + b[21] + local r22 = a[22] + b[22] + local r23 = a[23] + b[23] + local r24 = a[24] + b[24] + + if r1 > n then + r2 = r2 + 1 + r1 = r1 - m + end + if r2 > n then + r3 = r3 + 1 + r2 = r2 - m + end + if r3 > n then + r4 = r4 + 1 + r3 = r3 - m + end + if r4 > n then + r5 = r5 + 1 + r4 = r4 - m + end + if r5 > n then + r6 = r6 + 1 + r5 = r5 - m + end + if r6 > n then + r7 = r7 + 1 + r6 = r6 - m + end + if r7 > n then + r8 = r8 + 1 + r7 = r7 - m + end + if r8 > n then + r9 = r9 + 1 + r8 = r8 - m + end + if r9 > n then + r10 = r10 + 1 + r9 = r9 - m + end + if r10 > n then + r11 = r11 + 1 + r10 = r10 - m + end + if r11 > n then + r12 = r12 + 1 + r11 = r11 - m + end + if r12 > n then + r13 = r13 + 1 + r12 = r12 - m + end + if r13 > n then + r14 = r14 + 1 + r13 = r13 - m + end + if r14 > n then + r15 = r15 + 1 + r14 = r14 - m + end + if r15 > n then + r16 = r16 + 1 + r15 = r15 - m + end + if r16 > n then + r17 = r17 + 1 + r16 = r16 - m + end + if r17 > n then + r18 = r18 + 1 + r17 = r17 - m + end + if r18 > n then + r19 = r19 + 1 + r18 = r18 - m + end + if r19 > n then + r20 = r20 + 1 + r19 = r19 - m + end + if r20 > n then + r21 = r21 + 1 + r20 = r20 - m + end + if r21 > n then + r22 = r22 + 1 + r21 = r21 - m + end + if r22 > n then + r23 = r23 + 1 + r22 = r22 - m + end + if r23 > n then + r24 = r24 + 1 + r23 = r23 - m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function sub384(a, b) + local r1 = a[1] - b[1] + local r2 = a[2] - b[2] + local r3 = a[3] - b[3] + local r4 = a[4] - b[4] + local r5 = a[5] - b[5] + local r6 = a[6] - b[6] + local r7 = a[7] - b[7] + local r8 = a[8] - b[8] + local r9 = a[9] - b[9] + local r10 = a[10] - b[10] + local r11 = a[11] - b[11] + local r12 = a[12] - b[12] + local r13 = a[13] - b[13] + local r14 = a[14] - b[14] + local r15 = a[15] - b[15] + local r16 = a[16] - b[16] + local r17 = a[17] - b[17] + local r18 = a[18] - b[18] + local r19 = a[19] - b[19] + local r20 = a[20] - b[20] + local r21 = a[21] - b[21] + local r22 = a[22] - b[22] + local r23 = a[23] - b[23] + local r24 = a[24] - b[24] + + if r1 < 0 then + r2 = r2 - 1 + r1 = r1 + m + end + if r2 < 0 then + r3 = r3 - 1 + r2 = r2 + m + end + if r3 < 0 then + r4 = r4 - 1 + r3 = r3 + m + end + if r4 < 0 then + r5 = r5 - 1 + r4 = r4 + m + end + if r5 < 0 then + r6 = r6 - 1 + r5 = r5 + m + end + if r6 < 0 then + r7 = r7 - 1 + r6 = r6 + m + end + if r7 < 0 then + r8 = r8 - 1 + r7 = r7 + m + end + if r8 < 0 then + r9 = r9 - 1 + r8 = r8 + m + end + if r9 < 0 then + r10 = r10 - 1 + r9 = r9 + m + end + if r10 < 0 then + r11 = r11 - 1 + r10 = r10 + m + end + if r11 < 0 then + r12 = r12 - 1 + r11 = r11 + m + end + if r12 < 0 then + r13 = r13 - 1 + r12 = r12 + m + end + if r13 < 0 then + r14 = r14 - 1 + r13 = r13 + m + end + if r14 < 0 then + r15 = r15 - 1 + r14 = r14 + m + end + if r15 < 0 then + r16 = r16 - 1 + r15 = r15 + m + end + if r16 < 0 then + r17 = r17 - 1 + r16 = r16 + m + end + if r17 < 0 then + r18 = r18 - 1 + r17 = r17 + m + end + if r18 < 0 then + r19 = r19 - 1 + r18 = r18 + m + end + if r19 < 0 then + r20 = r20 - 1 + r19 = r19 + m + end + if r20 < 0 then + r21 = r21 - 1 + r20 = r20 + m + end + if r21 < 0 then + r22 = r22 - 1 + r21 = r21 + m + end + if r22 < 0 then + r23 = r23 - 1 + r22 = r22 + m + end + if r23 < 0 then + r24 = r24 - 1 + r23 = r23 + m + end + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function mul384(a, b) + local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 = unpack(a) + local b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12 = unpack(b) + + local r1 = a1 * b1 + + local r2 = a1 * b2 + r2 = r2 + a2 * b1 + + local r3 = a1 * b3 + r3 = r3 + a2 * b2 + r3 = r3 + a3 * b1 + + local r4 = a1 * b4 + r4 = r4 + a2 * b3 + r4 = r4 + a3 * b2 + r4 = r4 + a4 * b1 + + local r5 = a1 * b5 + r5 = r5 + a2 * b4 + r5 = r5 + a3 * b3 + r5 = r5 + a4 * b2 + r5 = r5 + a5 * b1 + + local r6 = a1 * b6 + r6 = r6 + a2 * b5 + r6 = r6 + a3 * b4 + r6 = r6 + a4 * b3 + r6 = r6 + a5 * b2 + r6 = r6 + a6 * b1 + + local r7 = a1 * b7 + r7 = r7 + a2 * b6 + r7 = r7 + a3 * b5 + r7 = r7 + a4 * b4 + r7 = r7 + a5 * b3 + r7 = r7 + a6 * b2 + r7 = r7 + a7 * b1 + + local r8 = a1 * b8 + r8 = r8 + a2 * b7 + r8 = r8 + a3 * b6 + r8 = r8 + a4 * b5 + r8 = r8 + a5 * b4 + r8 = r8 + a6 * b3 + r8 = r8 + a7 * b2 + r8 = r8 + a8 * b1 + + local r9 = a1 * b9 + r9 = r9 + a2 * b8 + r9 = r9 + a3 * b7 + r9 = r9 + a4 * b6 + r9 = r9 + a5 * b5 + r9 = r9 + a6 * b4 + r9 = r9 + a7 * b3 + r9 = r9 + a8 * b2 + r9 = r9 + a9 * b1 + + local r10 = a1 * b10 + r10 = r10 + a2 * b9 + r10 = r10 + a3 * b8 + r10 = r10 + a4 * b7 + r10 = r10 + a5 * b6 + r10 = r10 + a6 * b5 + r10 = r10 + a7 * b4 + r10 = r10 + a8 * b3 + r10 = r10 + a9 * b2 + r10 = r10 + a10 * b1 + + local r11 = a1 * b11 + r11 = r11 + a2 * b10 + r11 = r11 + a3 * b9 + r11 = r11 + a4 * b8 + r11 = r11 + a5 * b7 + r11 = r11 + a6 * b6 + r11 = r11 + a7 * b5 + r11 = r11 + a8 * b4 + r11 = r11 + a9 * b3 + r11 = r11 + a10 * b2 + r11 = r11 + a11 * b1 + + local r12 = a1 * b12 + r12 = r12 + a2 * b11 + r12 = r12 + a3 * b10 + r12 = r12 + a4 * b9 + r12 = r12 + a5 * b8 + r12 = r12 + a6 * b7 + r12 = r12 + a7 * b6 + r12 = r12 + a8 * b5 + r12 = r12 + a9 * b4 + r12 = r12 + a10 * b3 + r12 = r12 + a11 * b2 + r12 = r12 + a12 * b1 + + local r13 = a2 * b12 + r13 = r13 + a3 * b11 + r13 = r13 + a4 * b10 + r13 = r13 + a5 * b9 + r13 = r13 + a6 * b8 + r13 = r13 + a7 * b7 + r13 = r13 + a8 * b6 + r13 = r13 + a9 * b5 + r13 = r13 + a10 * b4 + r13 = r13 + a11 * b3 + r13 = r13 + a12 * b2 + + local r14 = a3 * b12 + r14 = r14 + a4 * b11 + r14 = r14 + a5 * b10 + r14 = r14 + a6 * b9 + r14 = r14 + a7 * b8 + r14 = r14 + a8 * b7 + r14 = r14 + a9 * b6 + r14 = r14 + a10 * b5 + r14 = r14 + a11 * b4 + r14 = r14 + a12 * b3 + + local r15 = a4 * b12 + r15 = r15 + a5 * b11 + r15 = r15 + a6 * b10 + r15 = r15 + a7 * b9 + r15 = r15 + a8 * b8 + r15 = r15 + a9 * b7 + r15 = r15 + a10 * b6 + r15 = r15 + a11 * b5 + r15 = r15 + a12 * b4 + + local r16 = a5 * b12 + r16 = r16 + a6 * b11 + r16 = r16 + a7 * b10 + r16 = r16 + a8 * b9 + r16 = r16 + a9 * b8 + r16 = r16 + a10 * b7 + r16 = r16 + a11 * b6 + r16 = r16 + a12 * b5 + + local r17 = a6 * b12 + r17 = r17 + a7 * b11 + r17 = r17 + a8 * b10 + r17 = r17 + a9 * b9 + r17 = r17 + a10 * b8 + r17 = r17 + a11 * b7 + r17 = r17 + a12 * b6 + + local r18 = a7 * b12 + r18 = r18 + a8 * b11 + r18 = r18 + a9 * b10 + r18 = r18 + a10 * b9 + r18 = r18 + a11 * b8 + r18 = r18 + a12 * b7 + + local r19 = a8 * b12 + r19 = r19 + a9 * b11 + r19 = r19 + a10 * b10 + r19 = r19 + a11 * b9 + r19 = r19 + a12 * b8 + + local r20 = a9 * b12 + r20 = r20 + a10 * b11 + r20 = r20 + a11 * b10 + r20 = r20 + a12 * b9 + + local r21 = a10 * b12 + r21 = r21 + a11 * b11 + r21 = r21 + a12 * b10 + + local r22 = a11 * b12 + r22 = r22 + a12 * b11 + + local r23 = a12 * b12 + + local r24 = 0 + + r2 = r2 + (r1 / m) + r2 = r2 - r2 % 1 + r1 = r1 % m + r3 = r3 + (r2 / m) + r3 = r3 - r3 % 1 + r2 = r2 % m + r4 = r4 + (r3 / m) + r4 = r4 - r4 % 1 + r3 = r3 % m + r5 = r5 + (r4 / m) + r5 = r5 - r5 % 1 + r4 = r4 % m + r6 = r6 + (r5 / m) + r6 = r6 - r6 % 1 + r5 = r5 % m + r7 = r7 + (r6 / m) + r7 = r7 - r7 % 1 + r6 = r6 % m + r8 = r8 + (r7 / m) + r8 = r8 - r8 % 1 + r7 = r7 % m + r9 = r9 + (r8 / m) + r9 = r9 - r9 % 1 + r8 = r8 % m + r10 = r10 + (r9 / m) + r10 = r10 - r10 % 1 + r9 = r9 % m + r11 = r11 + (r10 / m) + r11 = r11 - r11 % 1 + r10 = r10 % m + r12 = r12 + (r11 / m) + r12 = r12 - r12 % 1 + r11 = r11 % m + r13 = r13 + (r12 / m) + r13 = r13 - r13 % 1 + r12 = r12 % m + r14 = r14 + (r13 / m) + r14 = r14 - r14 % 1 + r13 = r13 % m + r15 = r15 + (r14 / m) + r15 = r15 - r15 % 1 + r14 = r14 % m + r16 = r16 + (r15 / m) + r16 = r16 - r16 % 1 + r15 = r15 % m + r17 = r17 + (r16 / m) + r17 = r17 - r17 % 1 + r16 = r16 % m + r18 = r18 + (r17 / m) + r18 = r18 - r18 % 1 + r17 = r17 % m + r19 = r19 + (r18 / m) + r19 = r19 - r19 % 1 + r18 = r18 % m + r20 = r20 + (r19 / m) + r20 = r20 - r20 % 1 + r19 = r19 % m + r21 = r21 + (r20 / m) + r21 = r21 - r21 % 1 + r20 = r20 % m + r22 = r22 + (r21 / m) + r22 = r22 - r22 % 1 + r21 = r21 % m + r23 = r23 + (r22 / m) + r23 = r23 - r23 % 1 + r22 = r22 % m + r24 = r24 + (r23 / m) + r24 = r24 - r24 % 1 + r23 = r23 % m + + local result = {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, r23, r24} + + return result +end + +local function reduce384(a) + local result = {unpack(a)} + + while cmp384(result, qn) >= 0 do + local qn = {unpack(qn)} + local qn2 = add384(qn, qn) + while cmp384(result, qn2) > 0 do + qn = qn2 + qn2 = add384(qn2, qn2) + end + result = sub384(result, qn) + end + + result = {unpack(result, 1, 12)} + + return result +end + +local function mul(a, b) + return reduce384(mul384(a, b)) +end + +return { + eq = eq, + cmp = cmp, + bytes = bytes, + fromBytes = fromBytes, + reduce = reduce, + add = add, + sub = sub, + mul = mul, +} diff --git a/sys/apis/crypto/ecc/init.lua b/sys/apis/crypto/ecc/init.lua new file mode 100644 index 0000000..51e3b57 --- /dev/null +++ b/sys/apis/crypto/ecc/init.lua @@ -0,0 +1,87 @@ +local fq = require('crypto.ecc.fq') +local elliptic = require('crypto.ecc.elliptic') +local sha256 = require('crypto.sha2') + +local q = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532} + +local sLen = 24 +local eLen = 24 + +local function hashModQ(sk) + local hash = sha256.hmac({0x00}, sk) + local x + repeat + hash = sha256.digest(hash) + x = fq.fromBytes(hash) + until fq.cmp(x, q) <= 0 + + return x +end + +local function publicKey(sk) + local x = hashModQ(sk) + + local Y = elliptic.scalarMulG(x) + local pk = elliptic.pointEncode(Y) + + return pk +end + +local function exchange(sk, pk) + local Y = elliptic.pointDecode(pk) + local x = hashModQ(sk) + + local Z = elliptic.scalarMul(x, Y) + Z = elliptic.pointScale(Z) + + local ss = fq.bytes(Z[2]) + local ss = sha256.digest(ss) + + return ss +end + +local function sign(sk, message) + message = type(message) == "table" and string.char(unpack(message)) or message + sk = type(sk) == "table" and string.char(unpack(sk)) or sk + local epoch = tostring(os.epoch("utc")) + local x = hashModQ(sk) + local k = hashModQ(message .. epoch .. sk) + + local R = elliptic.scalarMulG(k) + R = string.char(unpack(elliptic.pointEncode(R))) + local e = hashModQ(R .. message) + local s = fq.sub(k, fq.mul(x, e)) + + e = fq.bytes(e) + s = fq.bytes(s) + + local sig = {unpack(e)} + + for i = 1, #s do + sig[#sig + 1] = s[i] + end + + return sig +end + +local function verify(pk, message, sig) + local Y = elliptic.pointDecode(pk) + local e = {unpack(sig, 1, eLen)} + local s = {unpack(sig, eLen + 1, eLen + sLen)} + + e = fq.fromBytes(e) + s = fq.fromBytes(s) + + local R = elliptic.pointAdd(elliptic.scalarMulG(s), elliptic.scalarMul(e, Y)) + R = string.char(unpack(elliptic.pointEncode(R))) + local e2 = hashModQ(R .. message) + + return fq.eq(e2, e) +end + +return { + publicKey = publicKey, + exchange = exchange, + sign = sign, + verify = verify, +} diff --git a/sys/apis/sha2.lua b/sys/apis/crypto/sha2.lua similarity index 92% rename from sys/apis/sha2.lua rename to sys/apis/crypto/sha2.lua index f7965bc..162f5cb 100644 --- a/sys/apis/sha2.lua +++ b/sys/apis/crypto/sha2.lua @@ -1,8 +1,6 @@ -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft -- By Anavrins -local bit = _G.bit - local mod32 = 2^32 local band = bit32 and bit32.band or bit.band local bnot = bit32 and bit32.bnot or bit.bnot @@ -40,7 +38,7 @@ local function counter(incr) local t1, t2 = 0, 0 if 0xFFFFFFFF - t1 < incr then t2 = t2 + 1 - t1 = incr - (0xFFFFFFFF - t1) - 1 + t1 = incr - (0xFFFFFFFF - t1) - 1 else t1 = t1 + incr end return t2, t1 @@ -68,7 +66,7 @@ end local function digestblock(w, C) for j = 17, 64 do - --local v = w[j-15] + local v = w[j-15] local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3)) local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10)) w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32 @@ -97,7 +95,7 @@ end local mt = { __tostring = function(a) return string.char(unpack(a)) end, __index = { - toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end, + toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end, isEqual = function(self, t) if type(t) ~= "table" then return false end if #self ~= #t then return false end @@ -122,7 +120,7 @@ local function toBytes(t, n) end local function digest(data) - data = data or "" + local data = data or "" data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} data = preprocess(data) @@ -132,8 +130,8 @@ local function digest(data) end local function hmac(data, key) - data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} - key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)} + local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} + local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)} local blocksize = 64 @@ -163,13 +161,12 @@ local function hmac(data, key) end local function pbkdf2(pass, salt, iter, dklen) + local salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} local hashlen = 32 + local dklen = dklen or 32 local block = 1 local out = {} - dklen = dklen or 32 - salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} - while dklen > 0 do local ikey = {} local isalt = {upack(salt)} @@ -197,4 +194,4 @@ return { digest = digest, hmac = hmac, pbkdf2 = pbkdf2, -} \ No newline at end of file +} diff --git a/sys/apis/injector.lua b/sys/apis/injector.lua index 5497b24..799cf0b 100644 --- a/sys/apis/injector.lua +++ b/sys/apis/injector.lua @@ -192,7 +192,7 @@ return function(env) error(msg, 2) end end - error('Unable to find module ' .. modname) + error('Unable to find module ' .. modname, 2) end return env.require -- backwards compatible diff --git a/sys/apis/security.lua b/sys/apis/security.lua index ab0fea8..691df97 100644 --- a/sys/apis/security.lua +++ b/sys/apis/security.lua @@ -1,4 +1,6 @@ local Config = require('config') +local Util = require('util') +local ECC = require('crypto.ecc') local Security = { } @@ -14,33 +16,18 @@ end function Security.getSecretKey() local config = Config.load('os') if not config.secretKey then - config.secretKey = math.random(100000, 999999) + config.secretKey = "" + for _ = 1, 32 do + config.secretKey = config.secretKey .. ("%02x"):format(math.random(0, 0xFF)) + end Config.update('os', config) end - return config.secretKey + return Util.hexToByteArray(config.secretKey) end function Security.getPublicKey() - local exchange = { - base = 11, - primeMod = 625210769 - } - - local function modexp(base, exponent, modulo) - local remainder = base - - for _ = 1, exponent-1 do - remainder = remainder * remainder - if remainder >= modulo then - remainder = remainder % modulo - end - end - - return remainder - end - local secretKey = Security.getSecretKey() - return modexp(exchange.base, secretKey, exchange.primeMod) + return ECC.publicKey(secretKey) end function Security.updatePassword(password) diff --git a/sys/apis/socket.lua b/sys/apis/socket.lua index 726b4d3..ae1bc60 100644 --- a/sys/apis/socket.lua +++ b/sys/apis/socket.lua @@ -1,4 +1,4 @@ -local Crypto = require('crypto') +local Crypto = require('crypto.chacha20') local Security = require('security') local Util = require('util') @@ -167,15 +167,16 @@ local function trusted(msg, port) local trustList = Util.readTable('usr/.known_hosts') or { } local pubKey = trustList[msg.shost] - if pubKey then - local data = Crypto.decrypt(msg.t or '', pubKey) + if pubKey and msg.t then + pubKey = Util.hexToByteArray(pubKey) + local data = Crypto.decrypt(msg.t, pubKey) - if data.nts then -- upgraded security + if data and data.nts then -- upgraded security return data.nts and tonumber(data.nts) and math.abs(os.epoch('utc') - data.nts) < 1024 end --local sharedKey = modexp(pubKey, exchange.secretKey, public.primeMod) - return data.ts and tonumber(data.ts) and math.abs(os.time() - data.ts) < 24 + return data and data.ts and tonumber(data.ts) and math.abs(os.time() - data.ts) < 24 end end diff --git a/sys/apis/util.lua b/sys/apis/util.lua index 042ba84..271fcfd 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -10,6 +10,19 @@ local _sformat = string.format local _srep = string.rep local _ssub = string.sub +function Util.hexToByteArray(str) + local r = {} + str = tostring(str) + for b in str:gmatch("%x%x?") do + r[#r+1] = tonumber(b, 16) + end + return r +end + +function Util.byteArrayToHex(tbl) + return ("%02x"):rep(#tbl):format(unpack(tbl)) +end + function Util.tryTimed(timeout, f, ...) local c = os.clock() repeat diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 80d170e..8a4507b 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -1,7 +1,6 @@ local Ansi = require('ansi') -local Config = require('config') local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('crypto.sha2') local UI = require('ui') local colors = _G.colors @@ -108,7 +107,7 @@ end function page.wizard.pages.password:validate() if #self.newPass.value > 0 then - Security.updatePassword(SHA1.sha1(self.newPass.value)) + Security.updatePassword(SHA2.digest(self.newPass.value):toHex()) end --[[ if #self.group.value > 0 then diff --git a/sys/apps/network/trust.lua b/sys/apps/network/trust.lua index 9d12c3a..b2e493f 100644 --- a/sys/apps/network/trust.lua +++ b/sys/apps/network/trust.lua @@ -1,4 +1,4 @@ -local Crypto = require('crypto') +local Crypto = require('crypto.chacha20') local Event = require('event') local Security = require('security') local Socket = require('socket') @@ -14,7 +14,7 @@ local function trustConnection(socket) data = Crypto.decrypt(data, password) if data and data.pk and data.dh == socket.dhost then local trustList = Util.readTable('usr/.known_hosts') or { } - trustList[data.dh] = data.pk + trustList[data.dh] = Util.byteArrayToHex(data.pk) Util.writeTable('usr/.known_hosts', trustList) socket:write({ success = true, msg = 'Trust accepted' }) @@ -26,8 +26,8 @@ local function trustConnection(socket) end Event.addRoutine(function() - print('trust: listening on port 19') + while true do local socket = Socket.server(19) diff --git a/sys/apps/password.lua b/sys/apps/password.lua index ca0f5b5..6beb700 100644 --- a/sys/apps/password.lua +++ b/sys/apps/password.lua @@ -1,10 +1,10 @@ local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('crypto.sha2') local Terminal = require('terminal') local password = Terminal.readPassword('Enter new password: ') if password then - Security.updatePassword(SHA1.sha1(password)) + Security.updatePassword(SHA2.digest(password):toHex()) print('Password updated') end diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 5f705a6..7ad5769 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -1,5 +1,5 @@ local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('crypto.sha2') local UI = require('ui') local colors = _G.colors @@ -40,11 +40,11 @@ function passwordTab:eventHandler(event) if #self.newPass.value == 0 then self:emit({ type = 'error_message', message = 'Invalid password' }) - elseif Security.getPassword() and not Security.verifyPassword(SHA1.sha1(self.oldPass.value)) then + elseif Security.getPassword() and not Security.verifyPassword(SHA2.digest(self.oldPass.value):toHex()) then self:emit({ type = 'error_message', message = 'Passwords do not match' }) else - Security.updatePassword(SHA1.sha1(self.newPass.value)) + Security.updatePassword(SHA2.digest(self.newPass.value):toHex()) self.oldPass.inactive = false self:emit({ type = 'success_message', message = 'Password updated' }) end diff --git a/sys/apps/trust.lua b/sys/apps/trust.lua index da8ff55..90c01d4 100644 --- a/sys/apps/trust.lua +++ b/sys/apps/trust.lua @@ -1,6 +1,6 @@ -local Crypto = require('crypto') +local Crypto = require('crypto.chacha20') local Security = require('security') -local SHA1 = require('sha1') +local SHA2 = require('crypto.sha2') local Socket = require('socket') local Terminal = require('terminal') @@ -35,7 +35,7 @@ end local publicKey = Security.getPublicKey() -socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, SHA1.sha1(password))) +socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, SHA2.digest(password):toHex())) local data = socket:read(2) socket:close() diff --git a/sys/autorun/welcome.lua b/sys/autorun/welcome.lua index 2ecf6d8..e3a7b5a 100644 --- a/sys/autorun/welcome.lua +++ b/sys/autorun/welcome.lua @@ -1,11 +1,45 @@ local Config = require('config') +local Util = require('util') -local shell = _ENV.shell +local fs = _G.fs +local shell = _ENV.shell local config = Config.load('os') if not config.welcomed and shell.openForegroundTab then config.welcomed = true + config.securityUpdate = true + config.readNotes = 1 Config.update('os', config) shell.openForegroundTab('Welcome') end + +if not config.securityUpdate then + config.securityUpdate = true + config.secretKey = nil + config.password = nil + config.readNotes = 1 + Config.update('os', config) + + fs.delete('usr/.known_hosts') + + Util.writeFile('sys/notes_1.txt', [[ +An important security update has been applied. + +Unfortunately, this update has reset the +password on the system. You can set a new +password in System->System->Password. + +All computers that you connect to will also +need to be updated as well. + +Thanks for your patience. And... thanks to +Anavrins for the much improved security. + ]]) +end + +if fs.exists('sys/notes_1.txt') and shell.openForegroundTab then + shell.openForegroundTab('edit sys/notes_1.txt') + os.sleep(2) + fs.delete('sys/notes_1.txt') +end -- 2.49.1 From c3d52c1aaba0681a911b4da42efc1f07bebfecb4 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 28 Jun 2019 06:33:47 -0400 Subject: [PATCH 019/236] crypto cleanup --- sys/apis/crypto/chacha20.lua | 5 +- sys/apis/crypto/ecc/init.lua | 6 +- sys/apis/crypto/sha2.lua | 25 ++-- sys/apis/sha1.lua | 280 ----------------------------------- sys/apps/Overview.lua | 6 +- sys/apps/Welcome.lua | 4 +- sys/apps/password.lua | 4 +- sys/apps/system/password.lua | 6 +- sys/apps/trust.lua | 4 +- sys/autorun/welcome.lua | 1 + 10 files changed, 35 insertions(+), 306 deletions(-) delete mode 100644 sys/apis/sha1.lua diff --git a/sys/apis/crypto/chacha20.lua b/sys/apis/crypto/chacha20.lua index b0763dd..03386a4 100644 --- a/sys/apis/crypto/chacha20.lua +++ b/sys/apis/crypto/chacha20.lua @@ -10,6 +10,7 @@ local bxor = bit32.bxor local band = bit32.band local blshift = bit32.lshift local brshift = bit32.arshift +local os = _G.os local textutils = _G.textutils local mod = 2^32 @@ -91,7 +92,7 @@ end local mt = { __tostring = function(a) return string.char(unpack(a)) end, __index = { - toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end, + toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end, isEqual = function(self, t) if type(t) ~= "table" then return false end if #self ~= #t then return false end @@ -110,7 +111,7 @@ local function crypt(data, key, nonce, cntr, round) assert(#key == 16 or #key == 32, "ChaCha20: Invalid key length ("..#key.."), must be 16 or 32") assert(#nonce == 12, "ChaCha20: Invalid nonce length ("..#nonce.."), must be 12") - local data = type(data) == "table" and {unpack(data)} or {tostring(data):byte(1,-1)} + data = type(data) == "table" and {unpack(data)} or {tostring(data):byte(1,-1)} cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 diff --git a/sys/apis/crypto/ecc/init.lua b/sys/apis/crypto/ecc/init.lua index 51e3b57..f765542 100644 --- a/sys/apis/crypto/ecc/init.lua +++ b/sys/apis/crypto/ecc/init.lua @@ -2,6 +2,8 @@ local fq = require('crypto.ecc.fq') local elliptic = require('crypto.ecc.elliptic') local sha256 = require('crypto.sha2') +local os = _G.os + local q = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532} local sLen = 24 @@ -35,9 +37,7 @@ local function exchange(sk, pk) Z = elliptic.pointScale(Z) local ss = fq.bytes(Z[2]) - local ss = sha256.digest(ss) - - return ss + return sha256.digest(ss) end local function sign(sk, message) diff --git a/sys/apis/crypto/sha2.lua b/sys/apis/crypto/sha2.lua index 162f5cb..a4f29f3 100644 --- a/sys/apis/crypto/sha2.lua +++ b/sys/apis/crypto/sha2.lua @@ -1,7 +1,9 @@ -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft -- By Anavrins -local mod32 = 2^32 +local bit = _G.bit +local os = _G.os +local mod32 = 2^32 local band = bit32 and bit32.band or bit.band local bnot = bit32 and bit32.bnot or bit.bnot local bxor = bit32 and bit32.bxor or bit.bxor @@ -38,7 +40,7 @@ local function counter(incr) local t1, t2 = 0, 0 if 0xFFFFFFFF - t1 < incr then t2 = t2 + 1 - t1 = incr - (0xFFFFFFFF - t1) - 1 + t1 = incr - (0xFFFFFFFF - t1) - 1 else t1 = t1 + incr end return t2, t1 @@ -66,7 +68,7 @@ end local function digestblock(w, C) for j = 17, 64 do - local v = w[j-15] + -- local v = w[j-15] local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3)) local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10)) w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32 @@ -95,7 +97,7 @@ end local mt = { __tostring = function(a) return string.char(unpack(a)) end, __index = { - toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end, + toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end, isEqual = function(self, t) if type(t) ~= "table" then return false end if #self ~= #t then return false end @@ -120,7 +122,7 @@ local function toBytes(t, n) end local function digest(data) - local data = data or "" + data = data or "" data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} data = preprocess(data) @@ -130,8 +132,8 @@ local function digest(data) end local function hmac(data, key) - local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} - local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)} + data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)} + key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)} local blocksize = 64 @@ -161,9 +163,9 @@ local function hmac(data, key) end local function pbkdf2(pass, salt, iter, dklen) - local salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} + salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} local hashlen = 32 - local dklen = dklen or 32 + dklen = dklen or 32 local block = 1 local out = {} @@ -190,8 +192,13 @@ local function pbkdf2(pass, salt, iter, dklen) return setmetatable(out, mt) end +local function compute(data) + return digest(data):toHex() +end + return { digest = digest, + compute = compute, hmac = hmac, pbkdf2 = pbkdf2, } diff --git a/sys/apis/sha1.lua b/sys/apis/sha1.lua deleted file mode 100644 index 0be5024..0000000 --- a/sys/apis/sha1.lua +++ /dev/null @@ -1,280 +0,0 @@ -local sha1 = { - _VERSION = "sha.lua 0.5.0", - _URL = "https://github.com/kikito/sha.lua", - _DESCRIPTION = [[ - SHA-1 secure hash computation, and HMAC-SHA1 signature computation in Lua (5.1) - Based on code originally by Jeffrey Friedl (http://regex.info/blog/lua/sha1) - And modified by Eike Decker - (http://cube3d.de/uploads/Main/sha1.txt) - ]], - _LICENSE = [[ - MIT LICENSE - - Copyright (c) 2013 Enrique Garcia Cota + Eike Decker + Jeffrey Friedl - - https://opensource.org/licenses/MIT - ]] -} - ------------------------------------------------------------------------------------ - --- loading this file (takes a while but grants a boost of factor 13) -local PRELOAD_CACHE = false - -local BLOCK_SIZE = 64 -- 512 bits - --- local storing of global functions (minor speedup) -local floor,modf = math.floor,math.modf -local char,format,rep = string.char,string.format,string.rep - --- merge 4 bytes to an 32 bit word -local function bytes_to_w32(a,b,c,d) return a*0x1000000+b*0x10000+c*0x100+d end --- split a 32 bit word into four 8 bit numbers -local function w32_to_bytes(i) - return floor(i/0x1000000)%0x100,floor(i/0x10000)%0x100,floor(i/0x100)%0x100,i%0x100 -end - --- shift the bits of a 32 bit word. Don't use negative values for "bits" -local function w32_rot(bits,a) - local b2 = 2^(32-bits) - local a,b = modf(a/b2) - return a+b*b2*(2^(bits)) -end - --- caching function for functions that accept 2 arguments, both of values between --- 0 and 255. The function to be cached is passed, all values are calculated --- during loading and a function is returned that returns the cached values (only) -local function cache2arg(fn) - if not PRELOAD_CACHE then return fn end - local lut = {} - for i=0,0xffff do - local a,b = floor(i/0x100),i%0x100 - lut[i] = fn(a,b) - end - return function(a,b) - return lut[a*0x100+b] - end -end - --- splits an 8-bit number into 8 bits, returning all 8 bits as booleans -local function byte_to_bits(b) - local b = function(n) - local b = floor(b/n) - return b%2==1 - end - return b(1),b(2),b(4),b(8),b(16),b(32),b(64),b(128) -end - --- builds an 8bit number from 8 booleans -local function bits_to_byte(a,b,c,d,e,f,g,h) - local function n(b,x) return b and x or 0 end - return n(a,1)+n(b,2)+n(c,4)+n(d,8)+n(e,16)+n(f,32)+n(g,64)+n(h,128) -end - --- bitwise "and" function for 2 8bit number -local band = cache2arg (function(a,b) - local A,B,C,D,E,F,G,H = byte_to_bits(b) - local a,b,c,d,e,f,g,h = byte_to_bits(a) - return bits_to_byte( - A and a, B and b, C and c, D and d, - E and e, F and f, G and g, H and h) -end) - --- bitwise "or" function for 2 8bit numbers -local bor = cache2arg(function(a,b) - local A,B,C,D,E,F,G,H = byte_to_bits(b) - local a,b,c,d,e,f,g,h = byte_to_bits(a) - return bits_to_byte( - A or a, B or b, C or c, D or d, - E or e, F or f, G or g, H or h) -end) - --- bitwise "xor" function for 2 8bit numbers -local bxor = cache2arg(function(a,b) - local A,B,C,D,E,F,G,H = byte_to_bits(b) - local a,b,c,d,e,f,g,h = byte_to_bits(a) - return bits_to_byte( - A ~= a, B ~= b, C ~= c, D ~= d, - E ~= e, F ~= f, G ~= g, H ~= h) -end) - --- bitwise complement for one 8bit number -local function bnot(x) - return 255-(x % 256) -end - --- creates a function to combine to 32bit numbers using an 8bit combination function -local function w32_comb(fn) - return function(a,b) - local aa,ab,ac,ad = w32_to_bytes(a) - local ba,bb,bc,bd = w32_to_bytes(b) - return bytes_to_w32(fn(aa,ba),fn(ab,bb),fn(ac,bc),fn(ad,bd)) - end -end - --- create functions for and, xor and or, all for 2 32bit numbers -local w32_and = w32_comb(band) -local w32_xor = w32_comb(bxor) -local w32_or = w32_comb(bor) - --- xor function that may receive a variable number of arguments -local function w32_xor_n(a,...) - local aa,ab,ac,ad = w32_to_bytes(a) - for i=1,select('#',...) do - local ba,bb,bc,bd = w32_to_bytes(select(i,...)) - aa,ab,ac,ad = bxor(aa,ba),bxor(ab,bb),bxor(ac,bc),bxor(ad,bd) - end - return bytes_to_w32(aa,ab,ac,ad) -end - --- combining 3 32bit numbers through binary "or" operation -local function w32_or3(a,b,c) - local aa,ab,ac,ad = w32_to_bytes(a) - local ba,bb,bc,bd = w32_to_bytes(b) - local ca,cb,cc,cd = w32_to_bytes(c) - return bytes_to_w32( - bor(aa,bor(ba,ca)), bor(ab,bor(bb,cb)), bor(ac,bor(bc,cc)), bor(ad,bor(bd,cd)) - ) -end - --- binary complement for 32bit numbers -local function w32_not(a) - return 4294967295-(a % 4294967296) -end - --- adding 2 32bit numbers, cutting off the remainder on 33th bit -local function w32_add(a,b) return (a+b) % 4294967296 end - --- adding n 32bit numbers, cutting off the remainder (again) -local function w32_add_n(a,...) - for i=1,select('#',...) do - a = (a+select(i,...)) % 4294967296 - end - return a -end --- converting the number to a hexadecimal string -local function w32_to_hexstring(w) return format("%08x",w) end - -local function hex_to_binary(hex) - return hex:gsub('..', function(hexval) - return string.char(tonumber(hexval, 16)) - end) -end - --- building the lookuptables ahead of time (instead of littering the source code --- with precalculated values) -local xor_with_0x5c = {} -local xor_with_0x36 = {} -for i=0,0xff do - xor_with_0x5c[char(i)] = char(bxor(i,0x5c)) - xor_with_0x36[char(i)] = char(bxor(i,0x36)) -end - ------------------------------------------------------------------------------ - --- calculating the SHA1 for some text -function sha1.sha1(msg) - local H0,H1,H2,H3,H4 = 0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0 - local msg_len_in_bits = #msg * 8 - - local first_append = char(0x80) -- append a '1' bit plus seven '0' bits - - local non_zero_message_bytes = #msg +1 +8 -- the +1 is the appended bit 1, the +8 are for the final appended length - local current_mod = non_zero_message_bytes % 64 - local second_append = current_mod>0 and rep(char(0), 64 - current_mod) or "" - - -- now to append the length as a 64-bit number. - local B1, R1 = modf(msg_len_in_bits / 0x01000000) - local B2, R2 = modf( 0x01000000 * R1 / 0x00010000) - local B3, R3 = modf( 0x00010000 * R2 / 0x00000100) - local B4 = 0x00000100 * R3 - - local L64 = char( 0) .. char( 0) .. char( 0) .. char( 0) -- high 32 bits - .. char(B1) .. char(B2) .. char(B3) .. char(B4) -- low 32 bits - - msg = msg .. first_append .. second_append .. L64 - - assert(#msg % 64 == 0) - - local chunks = #msg / 64 - - local W = { } - local start, A, B, C, D, E, f, K, TEMP - local chunk = 0 - - while chunk < chunks do - -- - -- break chunk up into W[0] through W[15] - -- - start,chunk = chunk * 64 + 1,chunk + 1 - - for t = 0, 15 do - W[t] = bytes_to_w32(msg:byte(start, start + 3)) - start = start + 4 - end - - -- - -- build W[16] through W[79] - -- - for t = 16, 79 do - -- For t = 16 to 79 let Wt = S1(Wt-3 XOR Wt-8 XOR Wt-14 XOR Wt-16). - W[t] = w32_rot(1, w32_xor_n(W[t-3], W[t-8], W[t-14], W[t-16])) - end - - A,B,C,D,E = H0,H1,H2,H3,H4 - - for t = 0, 79 do - if t <= 19 then - -- (B AND C) OR ((NOT B) AND D) - f = w32_or(w32_and(B, C), w32_and(w32_not(B), D)) - K = 0x5A827999 - elseif t <= 39 then - -- B XOR C XOR D - f = w32_xor_n(B, C, D) - K = 0x6ED9EBA1 - elseif t <= 59 then - -- (B AND C) OR (B AND D) OR (C AND D - f = w32_or3(w32_and(B, C), w32_and(B, D), w32_and(C, D)) - K = 0x8F1BBCDC - else - -- B XOR C XOR D - f = w32_xor_n(B, C, D) - K = 0xCA62C1D6 - end - - -- TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt; - A,B,C,D,E = w32_add_n(w32_rot(5, A), f, E, W[t], K), - A, w32_rot(30, B), C, D - end - -- Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. - H0,H1,H2,H3,H4 = w32_add(H0, A),w32_add(H1, B),w32_add(H2, C),w32_add(H3, D),w32_add(H4, E) - end - local f = w32_to_hexstring - return f(H0) .. f(H1) .. f(H2) .. f(H3) .. f(H4) -end - - -function sha1.binary(msg) - return hex_to_binary(sha1.sha1(msg)) -end - -function sha1.hmac(key, text) - assert(type(key) == 'string', "key passed to sha1.hmac should be a string") - assert(type(text) == 'string', "text passed to sha1.hmac should be a string") - - if #key > BLOCK_SIZE then - key = sha1.binary(key) - end - - local key_xord_with_0x36 = key:gsub('.', xor_with_0x36) .. string.rep(string.char(0x36), BLOCK_SIZE - #key) - local key_xord_with_0x5c = key:gsub('.', xor_with_0x5c) .. string.rep(string.char(0x5c), BLOCK_SIZE - #key) - - return sha1.sha1(key_xord_with_0x5c .. sha1.binary(key_xord_with_0x36 .. text)) -end - -function sha1.hmac_binary(key, text) - return hex_to_binary(sha1.hmac(key, text)) -end - -setmetatable(sha1, {__call = function(_,msg) return sha1.sha1(msg) end }) - -return sha1 \ No newline at end of file diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 1c0fe01..a2692be 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -3,7 +3,7 @@ local Config = require('config') local Event = require('event') local NFT = require('nft') local Packages = require('packages') -local SHA1 = require('sha1') +local SHA = require('crypto.sha2') local Tween = require('ui.tween') local UI = require('ui') local Util = require('util') @@ -502,7 +502,7 @@ end function page.editor:updateApplications(app) if not app.key then - app.key = SHA1.sha1(app.title) + app.key = SHA.compute(app.title) end local filename = app.filename or fs.combine(REGISTRY_DIR, app.key) Util.writeTable(filename, app) @@ -571,7 +571,7 @@ end Event.on('overview_shortcut', function(_, app) if not app.key then - app.key = SHA1.sha1(app.title) + app.key = SHA.compute(app.title) end local filename = app.filename or fs.combine(REGISTRY_DIR, app.key) if not fs.exists(filename) then diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 8a4507b..399d0b2 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -1,6 +1,6 @@ local Ansi = require('ansi') local Security = require('security') -local SHA2 = require('crypto.sha2') +local SHA = require('crypto.sha2') local UI = require('ui') local colors = _G.colors @@ -107,7 +107,7 @@ end function page.wizard.pages.password:validate() if #self.newPass.value > 0 then - Security.updatePassword(SHA2.digest(self.newPass.value):toHex()) + Security.updatePassword(SHA.compute(self.newPass.value)) end --[[ if #self.group.value > 0 then diff --git a/sys/apps/password.lua b/sys/apps/password.lua index 6beb700..be64a0e 100644 --- a/sys/apps/password.lua +++ b/sys/apps/password.lua @@ -1,10 +1,10 @@ local Security = require('security') -local SHA2 = require('crypto.sha2') +local SHA = require('crypto.sha2') local Terminal = require('terminal') local password = Terminal.readPassword('Enter new password: ') if password then - Security.updatePassword(SHA2.digest(password):toHex()) + Security.updatePassword(SHA.compute(password)) print('Password updated') end diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 7ad5769..ecb7677 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -1,5 +1,5 @@ local Security = require('security') -local SHA2 = require('crypto.sha2') +local SHA = require('crypto.sha2') local UI = require('ui') local colors = _G.colors @@ -40,11 +40,11 @@ function passwordTab:eventHandler(event) if #self.newPass.value == 0 then self:emit({ type = 'error_message', message = 'Invalid password' }) - elseif Security.getPassword() and not Security.verifyPassword(SHA2.digest(self.oldPass.value):toHex()) then + elseif Security.getPassword() and not Security.verifyPassword(SHA.compute(self.oldPass.value)) then self:emit({ type = 'error_message', message = 'Passwords do not match' }) else - Security.updatePassword(SHA2.digest(self.newPass.value):toHex()) + Security.updatePassword(SHA.compute(self.newPass.value)) self.oldPass.inactive = false self:emit({ type = 'success_message', message = 'Password updated' }) end diff --git a/sys/apps/trust.lua b/sys/apps/trust.lua index 90c01d4..dbe0b33 100644 --- a/sys/apps/trust.lua +++ b/sys/apps/trust.lua @@ -1,6 +1,6 @@ local Crypto = require('crypto.chacha20') local Security = require('security') -local SHA2 = require('crypto.sha2') +local SHA = require('crypto.sha2') local Socket = require('socket') local Terminal = require('terminal') @@ -35,7 +35,7 @@ end local publicKey = Security.getPublicKey() -socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, SHA2.digest(password):toHex())) +socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, SHA.compute(password))) local data = socket:read(2) socket:close() diff --git a/sys/autorun/welcome.lua b/sys/autorun/welcome.lua index e3a7b5a..0ee57d0 100644 --- a/sys/autorun/welcome.lua +++ b/sys/autorun/welcome.lua @@ -2,6 +2,7 @@ local Config = require('config') local Util = require('util') local fs = _G.fs +local os = _G.os local shell = _ENV.shell local config = Config.load('os') -- 2.49.1 From 343ce7fdc2814c9de4786f68ed841b754681e46a Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 28 Jun 2019 13:50:02 -0400 Subject: [PATCH 020/236] move apis into rom/modules/main for shell compatibility --- sys/apps/Files.lua | 10 +++++----- sys/apps/Help.lua | 4 ++-- sys/apps/Lua.lua | 6 +++--- sys/apps/Network.lua | 10 +++++----- sys/apps/Overview.lua | 18 +++++++++--------- sys/apps/PackageManager.lua | 8 ++++---- sys/apps/System.lua | 4 ++-- sys/apps/Tasks.lua | 4 ++-- sys/apps/Welcome.lua | 8 ++++---- sys/apps/autorun.lua | 2 +- sys/apps/cedit.lua | 2 +- sys/apps/cshell.lua | 2 +- sys/apps/netdaemon.lua | 4 ++-- sys/apps/network/proxy.lua | 6 +++--- sys/apps/network/samba.lua | 4 ++-- sys/apps/network/snmp.lua | 8 ++++---- sys/apps/network/telnet.lua | 6 +++--- sys/apps/network/transport.lua | 2 +- sys/apps/network/trust.lua | 10 +++++----- sys/apps/network/vnc.lua | 6 +++--- sys/apps/package.lua | 8 ++++---- sys/apps/password.lua | 6 +++--- sys/apps/pastebin.lua | 2 +- sys/apps/shell.lua | 16 ++++++++-------- sys/apps/system/aliases.lua | 4 ++-- sys/apps/system/cloud.lua | 6 +++--- sys/apps/system/kiosk.lua | 2 +- sys/apps/system/label.lua | 4 ++-- sys/apps/system/launcher.lua | 4 ++-- sys/apps/system/network.lua | 6 +++--- sys/apps/system/password.lua | 6 +++--- sys/apps/system/path.lua | 6 +++--- sys/apps/system/requires.lua | 6 +++--- sys/apps/system/settings.lua | 2 +- sys/apps/system/shell.lua | 6 +++--- sys/apps/telnet.lua | 8 ++++---- sys/apps/trust.lua | 10 +++++----- sys/apps/vnc.lua | 8 ++++---- sys/autorun/clipboard.lua | 2 +- sys/autorun/hotkeys.lua | 2 +- sys/autorun/welcome.lua | 4 ++-- sys/boot/opus.boot | 10 +++++----- sys/init/1.device.lua | 6 +++--- sys/init/2.vfs.lua | 4 ++-- sys/init/3.modules.lua | 2 +- sys/init/4.user.lua | 6 +++--- sys/init/5.network.lua | 2 +- sys/init/6.packages.lua | 8 +++++--- sys/init/6.tl3.lua | 8 ++++---- sys/init/7.multishell.lua | 6 +++--- sys/kernel.lua | 4 ++-- sys/{apis => modules/opus}/ansi.lua | 0 sys/{apis => modules/opus}/array.lua | 0 sys/{apis => modules/opus}/bulkget.lua | 2 +- sys/{apis => modules/opus}/class.lua | 0 sys/{apis => modules/opus}/config.lua | 2 +- sys/{apis => modules/opus}/crypto/chacha20.lua | 4 ++-- .../opus}/crypto/ecc/elliptic.lua | 2 +- sys/{apis => modules/opus}/crypto/ecc/fp.lua | 0 sys/{apis => modules/opus}/crypto/ecc/fq.lua | 0 sys/{apis => modules/opus}/crypto/ecc/init.lua | 6 +++--- sys/{apis => modules/opus}/crypto/sha2.lua | 0 sys/{apis => modules/opus}/entry.lua | 2 +- sys/{apis => modules/opus}/event.lua | 0 sys/{apis => modules/opus}/fs/gitfs.lua | 2 +- sys/{apis => modules/opus}/fs/linkfs.lua | 0 sys/{apis => modules/opus}/fs/netfs.lua | 4 ++-- sys/{apis => modules/opus}/fs/ramfs.lua | 2 +- sys/{apis => modules/opus}/fs/urlfs.lua | 2 +- sys/{apis => modules/opus}/git.lua | 4 ++-- sys/{apis => modules/opus}/gps.lua | 0 sys/{apis => modules/opus}/history.lua | 2 +- sys/{apis => modules/opus}/http/pastebin.lua | 0 sys/{apis => modules/opus}/injector.lua | 14 ++++++++++---- sys/{apis => modules/opus}/input.lua | 4 ++-- sys/{apis => modules/opus}/json.lua | 0 .../opus}/jumper/core/bheap.lua | 0 .../opus}/jumper/core/node.lua | 0 .../opus}/jumper/core/path.lua | 0 .../opus}/jumper/core/utils.lua | 0 sys/{apis => modules/opus}/jumper/grid.lua | 0 .../opus}/jumper/pathfinder.lua | 0 .../opus}/jumper/search/astar.lua | 0 sys/{apis => modules/opus}/map.lua | 2 +- sys/{apis => modules/opus}/nft.lua | 2 +- sys/{apis => modules/opus}/packages.lua | 2 +- sys/{apis => modules/opus}/pathfind.lua | 8 ++++---- sys/{apis => modules/opus}/peripheral.lua | 4 ++-- sys/{apis => modules/opus}/point.lua | 2 +- sys/{apis => modules/opus}/security.lua | 6 +++--- sys/{apis => modules/opus}/socket.lua | 6 +++--- sys/{apis => modules/opus}/sound.lua | 0 sys/{apis => modules/opus}/sync.lua | 0 sys/{apis => modules/opus}/terminal.lua | 2 +- sys/{apis => modules/opus}/trace.lua | 0 sys/{apis => modules/opus}/ui.lua | 16 ++++++++-------- sys/{apis => modules/opus}/ui/canvas.lua | 6 +++--- .../opus}/ui/components/ActiveLayer.lua | 4 ++-- .../opus}/ui/components/Button.lua | 6 +++--- .../opus}/ui/components/Checkbox.lua | 4 ++-- .../opus}/ui/components/Chooser.lua | 6 +++--- .../opus}/ui/components/Dialog.lua | 8 ++++---- .../opus}/ui/components/DropMenu.lua | 6 +++--- .../opus}/ui/components/DropMenuItem.lua | 4 ++-- .../opus}/ui/components/Embedded.lua | 6 +++--- .../opus}/ui/components/Form.lua | 6 +++--- .../opus}/ui/components/Grid.lua | 6 +++--- .../opus}/ui/components/Image.lua | 4 ++-- .../opus}/ui/components/Menu.lua | 4 ++-- .../opus}/ui/components/MenuBar.lua | 4 ++-- .../opus}/ui/components/MenuItem.lua | 4 ++-- .../opus}/ui/components/NftImage.lua | 4 ++-- .../opus}/ui/components/Notification.lua | 10 +++++----- .../opus}/ui/components/ProgressBar.lua | 4 ++-- .../opus}/ui/components/ScrollBar.lua | 6 +++--- .../opus}/ui/components/ScrollingGrid.lua | 6 +++--- .../opus}/ui/components/SlideOut.lua | 4 ++-- .../opus}/ui/components/StatusBar.lua | 8 ++++---- .../opus}/ui/components/Tab.lua | 4 ++-- .../opus}/ui/components/TabBar.lua | 6 +++--- .../opus}/ui/components/TabBarMenuItem.lua | 4 ++-- .../opus}/ui/components/Tabs.lua | 6 +++--- .../opus}/ui/components/Text.lua | 6 +++--- .../opus}/ui/components/TextArea.lua | 4 ++-- .../opus}/ui/components/TextEntry.lua | 8 ++++---- .../opus}/ui/components/Throttle.lua | 4 ++-- .../opus}/ui/components/TitleBar.lua | 4 ++-- .../opus}/ui/components/VerticalMeter.lua | 4 ++-- .../opus}/ui/components/Viewport.lua | 4 ++-- .../opus}/ui/components/Wizard.lua | 6 +++--- .../opus}/ui/components/WizardPage.lua | 4 ++-- sys/{apis => modules/opus}/ui/region.lua | 0 sys/{apis => modules/opus}/ui/transition.lua | 2 +- sys/{apis => modules/opus}/ui/tween.lua | 0 sys/{apis => modules/opus}/util.lua | 0 135 files changed, 297 insertions(+), 289 deletions(-) rename sys/{apis => modules/opus}/ansi.lua (100%) rename sys/{apis => modules/opus}/array.lua (100%) rename sys/{apis => modules/opus}/bulkget.lua (93%) rename sys/{apis => modules/opus}/class.lua (100%) rename sys/{apis => modules/opus}/config.lua (96%) rename sys/{apis => modules/opus}/crypto/chacha20.lua (98%) rename sys/{apis => modules/opus}/crypto/ecc/elliptic.lua (99%) rename sys/{apis => modules/opus}/crypto/ecc/fp.lua (100%) rename sys/{apis => modules/opus}/crypto/ecc/fq.lua (100%) rename sys/{apis => modules/opus}/crypto/ecc/init.lua (92%) rename sys/{apis => modules/opus}/crypto/sha2.lua (100%) rename sys/{apis => modules/opus}/entry.lua (99%) rename sys/{apis => modules/opus}/event.lua (100%) rename sys/{apis => modules/opus}/fs/gitfs.lua (91%) rename sys/{apis => modules/opus}/fs/linkfs.lua (100%) rename sys/{apis => modules/opus}/fs/netfs.lua (97%) rename sys/{apis => modules/opus}/fs/ramfs.lua (98%) rename sys/{apis => modules/opus}/fs/urlfs.lua (97%) rename sys/{apis => modules/opus}/git.lua (95%) rename sys/{apis => modules/opus}/gps.lua (100%) rename sys/{apis => modules/opus}/history.lua (96%) rename sys/{apis => modules/opus}/http/pastebin.lua (100%) rename sys/{apis => modules/opus}/injector.lua (91%) rename sys/{apis => modules/opus}/input.lua (98%) rename sys/{apis => modules/opus}/json.lua (100%) rename sys/{apis => modules/opus}/jumper/core/bheap.lua (100%) rename sys/{apis => modules/opus}/jumper/core/node.lua (100%) rename sys/{apis => modules/opus}/jumper/core/path.lua (100%) rename sys/{apis => modules/opus}/jumper/core/utils.lua (100%) rename sys/{apis => modules/opus}/jumper/grid.lua (100%) rename sys/{apis => modules/opus}/jumper/pathfinder.lua (100%) rename sys/{apis => modules/opus}/jumper/search/astar.lua (100%) rename sys/{apis => modules/opus}/map.lua (96%) rename sys/{apis => modules/opus}/nft.lua (98%) rename sys/{apis => modules/opus}/packages.lua (97%) rename sys/{apis => modules/opus}/pathfind.lua (97%) rename sys/{apis => modules/opus}/peripheral.lua (97%) rename sys/{apis => modules/opus}/point.lua (99%) rename sys/{apis => modules/opus}/security.lua (88%) rename sys/{apis => modules/opus}/socket.lua (97%) rename sys/{apis => modules/opus}/sound.lua (100%) rename sys/{apis => modules/opus}/sync.lua (100%) rename sys/{apis => modules/opus}/terminal.lua (99%) rename sys/{apis => modules/opus}/trace.lua (100%) rename sys/{apis => modules/opus}/ui.lua (98%) rename sys/{apis => modules/opus}/ui/canvas.lua (98%) rename sys/{apis => modules/opus}/ui/components/ActiveLayer.lua (89%) rename sys/{apis => modules/opus}/ui/components/Button.lua (92%) rename sys/{apis => modules/opus}/ui/components/Checkbox.lua (95%) rename sys/{apis => modules/opus}/ui/components/Chooser.lua (95%) rename sys/{apis => modules/opus}/ui/components/Dialog.lua (87%) rename sys/{apis => modules/opus}/ui/components/DropMenu.lua (93%) rename sys/{apis => modules/opus}/ui/components/DropMenuItem.lua (87%) rename sys/{apis => modules/opus}/ui/components/Embedded.lua (93%) rename sys/{apis => modules/opus}/ui/components/Form.lua (96%) rename sys/{apis => modules/opus}/ui/components/Grid.lua (99%) rename sys/{apis => modules/opus}/ui/components/Image.lua (90%) rename sys/{apis => modules/opus}/ui/components/Menu.lua (94%) rename sys/{apis => modules/opus}/ui/components/MenuBar.lua (96%) rename sys/{apis => modules/opus}/ui/components/MenuItem.lua (79%) rename sys/{apis => modules/opus}/ui/components/NftImage.lua (89%) rename sys/{apis => modules/opus}/ui/components/Notification.lua (91%) rename sys/{apis => modules/opus}/ui/components/ProgressBar.lua (84%) rename sys/{apis => modules/opus}/ui/components/ScrollBar.lua (95%) rename sys/{apis => modules/opus}/ui/components/ScrollingGrid.lua (93%) rename sys/{apis => modules/opus}/ui/components/SlideOut.lua (92%) rename sys/{apis => modules/opus}/ui/components/StatusBar.lua (93%) rename sys/{apis => modules/opus}/ui/components/Tab.lua (69%) rename sys/{apis => modules/opus}/ui/components/TabBar.lua (90%) rename sys/{apis => modules/opus}/ui/components/TabBarMenuItem.lua (90%) rename sys/{apis => modules/opus}/ui/components/Tabs.lua (94%) rename sys/{apis => modules/opus}/ui/components/Text.lua (76%) rename sys/{apis => modules/opus}/ui/components/TextArea.lua (89%) rename sys/{apis => modules/opus}/ui/components/TextEntry.lua (94%) rename sys/{apis => modules/opus}/ui/components/Throttle.lua (95%) rename sys/{apis => modules/opus}/ui/components/TitleBar.lua (96%) rename sys/{apis => modules/opus}/ui/components/VerticalMeter.lua (85%) rename sys/{apis => modules/opus}/ui/components/Viewport.lua (97%) rename sys/{apis => modules/opus}/ui/components/Wizard.lua (96%) rename sys/{apis => modules/opus}/ui/components/WizardPage.lua (70%) rename sys/{apis => modules/opus}/ui/region.lua (100%) rename sys/{apis => modules/opus}/ui/transition.lua (96%) rename sys/{apis => modules/opus}/ui/tween.lua (100%) rename sys/{apis => modules/opus}/util.lua (100%) diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index ea3126d..6c6b453 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -1,8 +1,8 @@ -local Config = require('config') -local Event = require('event') -local pastebin = require('http.pastebin') -local UI = require('ui') -local Util = require('util') +local Config = require('opus.config') +local Event = require('opus.event') +local pastebin = require('opus.http.pastebin') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local fs = _G.fs diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index d00c80b..b6e862c 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -1,5 +1,5 @@ -local UI = require('ui') -local Util = require('util') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local help = _G.help diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index bfc080b..a7442be 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -1,9 +1,9 @@ -- Lua may be called from outside of shell - inject a require _G.requireInjector(_ENV) -local History = require('history') -local UI = require('ui') -local Util = require('util') +local History = require('opus.history') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local os = _G.os diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index e6341eb..f9f4cc1 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -1,8 +1,8 @@ -local Config = require('config') -local Event = require('event') -local Socket = require('socket') -local UI = require('ui') -local Util = require('util') +local Config = require('opus.config') +local Event = require('opus.event') +local Socket = require('opus.socket') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local device = _G.device diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index a2692be..bfcbbb3 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -1,12 +1,12 @@ -local class = require('class') -local Config = require('config') -local Event = require('event') -local NFT = require('nft') -local Packages = require('packages') -local SHA = require('crypto.sha2') -local Tween = require('ui.tween') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local Config = require('opus.config') +local Event = require('opus.event') +local NFT = require('opus.nft') +local Packages = require('opus.packages') +local SHA = require('opus.crypto.sha2') +local Tween = require('opus.ui.tween') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local device = _G.device diff --git a/sys/apps/PackageManager.lua b/sys/apps/PackageManager.lua index 6ed2d41..de8af57 100644 --- a/sys/apps/PackageManager.lua +++ b/sys/apps/PackageManager.lua @@ -1,7 +1,7 @@ -local Ansi = require('ansi') -local Packages = require('packages') -local UI = require('ui') -local Util = require('util') +local Ansi = require('opus.ansi') +local Packages = require('opus.packages') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local term = _G.term diff --git a/sys/apps/System.lua b/sys/apps/System.lua index 5b1595a..b97d2e9 100644 --- a/sys/apps/System.lua +++ b/sys/apps/System.lua @@ -1,5 +1,5 @@ -local UI = require('ui') -local Util = require('util') +local UI = require('opus.ui') +local Util = require('opus.util') local fs = _G.fs local shell = _ENV.shell diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index 9b35be2..73ff93b 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -1,5 +1,5 @@ -local Event = require('event') -local UI = require('ui') +local Event = require('opus.event') +local UI = require('opus.ui') local kernel = _G.kernel local multishell = _ENV.multishell diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 399d0b2..76c4c7f 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -1,7 +1,7 @@ -local Ansi = require('ansi') -local Security = require('security') -local SHA = require('crypto.sha2') -local UI = require('ui') +local Ansi = require('opus.ansi') +local Security = require('opus.security') +local SHA = require('opus.crypto.sha2') +local UI = require('opus.ui') local colors = _G.colors local os = _G.os diff --git a/sys/apps/autorun.lua b/sys/apps/autorun.lua index 1d650da..e8b378f 100644 --- a/sys/apps/autorun.lua +++ b/sys/apps/autorun.lua @@ -1,4 +1,4 @@ -local Packages = require('packages') +local Packages = require('opus.packages') local colors = _G.colors local fs = _G.fs diff --git a/sys/apps/cedit.lua b/sys/apps/cedit.lua index 138f977..2231e73 100644 --- a/sys/apps/cedit.lua +++ b/sys/apps/cedit.lua @@ -1,4 +1,4 @@ -local Config = require('config') +local Config = require('opus.config') local multishell = _ENV.multishell local os = _G.os diff --git a/sys/apps/cshell.lua b/sys/apps/cshell.lua index 34e5c91..9a0e1af 100644 --- a/sys/apps/cshell.lua +++ b/sys/apps/cshell.lua @@ -1,4 +1,4 @@ -local Config = require('config') +local Config = require('opus.config') local read = _G.read local shell = _ENV.shell diff --git a/sys/apps/netdaemon.lua b/sys/apps/netdaemon.lua index cc14a7b..fb9e23e 100644 --- a/sys/apps/netdaemon.lua +++ b/sys/apps/netdaemon.lua @@ -1,7 +1,7 @@ _G.requireInjector(_ENV) -local Event = require('event') -local Util = require('util') +local Event = require('opus.event') +local Util = require('opus.util') local device = _G.device local fs = _G.fs diff --git a/sys/apps/network/proxy.lua b/sys/apps/network/proxy.lua index a90dcef..9e487b0 100644 --- a/sys/apps/network/proxy.lua +++ b/sys/apps/network/proxy.lua @@ -1,6 +1,6 @@ -local Event = require('event') -local Socket = require('socket') -local Util = require('util') +local Event = require('opus.event') +local Socket = require('opus.socket') +local Util = require('opus.util') local function getProxy(path) local x = Util.split(path, '(.-)/') diff --git a/sys/apps/network/samba.lua b/sys/apps/network/samba.lua index a1d5227..f679b4d 100644 --- a/sys/apps/network/samba.lua +++ b/sys/apps/network/samba.lua @@ -1,5 +1,5 @@ -local Event = require('event') -local Socket = require('socket') +local Event = require('opus.event') +local Socket = require('opus.socket') local fs = _G.fs diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index 11ed673..ff42b99 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -1,7 +1,7 @@ -local Event = require('event') -local GPS = require('gps') -local Socket = require('socket') -local Util = require('util') +local Event = require('opus.event') +local GPS = require('opus.gps') +local Socket = require('opus.socket') +local Util = require('opus.util') local device = _G.device local kernel = _G.kernel diff --git a/sys/apps/network/telnet.lua b/sys/apps/network/telnet.lua index a396570..dc2bda0 100644 --- a/sys/apps/network/telnet.lua +++ b/sys/apps/network/telnet.lua @@ -1,6 +1,6 @@ -local Event = require('event') -local Socket = require('socket') -local Util = require('util') +local Event = require('opus.event') +local Socket = require('opus.socket') +local Util = require('opus.util') local kernel = _G.kernel local term = _G.term diff --git a/sys/apps/network/transport.lua b/sys/apps/network/transport.lua index eccd9ef..fa1978c 100644 --- a/sys/apps/network/transport.lua +++ b/sys/apps/network/transport.lua @@ -5,7 +5,7 @@ * background read buffering ]]-- -local Event = require('event') +local Event = require('opus.event') local os = _G.os diff --git a/sys/apps/network/trust.lua b/sys/apps/network/trust.lua index b2e493f..b31acbc 100644 --- a/sys/apps/network/trust.lua +++ b/sys/apps/network/trust.lua @@ -1,8 +1,8 @@ -local Crypto = require('crypto.chacha20') -local Event = require('event') -local Security = require('security') -local Socket = require('socket') -local Util = require('util') +local Crypto = require('opus.crypto.chacha20') +local Event = require('opus.event') +local Security = require('opus.security') +local Socket = require('opus.socket') +local Util = require('opus.util') local function trustConnection(socket) local data = socket:read(2) diff --git a/sys/apps/network/vnc.lua b/sys/apps/network/vnc.lua index 779a75d..54ea8fc 100644 --- a/sys/apps/network/vnc.lua +++ b/sys/apps/network/vnc.lua @@ -1,6 +1,6 @@ -local Event = require('event') -local Socket = require('socket') -local Util = require('util') +local Event = require('opus.event') +local Socket = require('opus.socket') +local Util = require('opus.util') local os = _G.os local terminal = _G.device.terminal diff --git a/sys/apps/package.lua b/sys/apps/package.lua index d1e2071..864f4fd 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -1,7 +1,7 @@ -local BulkGet = require('bulkget') -local Git = require('git') -local Packages = require('packages') -local Util = require('util') +local BulkGet = require('opus.bulkget') +local Git = require('opus.git') +local Packages = require('opus.packages') +local Util = require('opus.util') local fs = _G.fs local term = _G.term diff --git a/sys/apps/password.lua b/sys/apps/password.lua index be64a0e..e1585b0 100644 --- a/sys/apps/password.lua +++ b/sys/apps/password.lua @@ -1,6 +1,6 @@ -local Security = require('security') -local SHA = require('crypto.sha2') -local Terminal = require('terminal') +local Security = require('opus.security') +local SHA = require('opus.crypto.sha2') +local Terminal = require('opus.terminal') local password = Terminal.readPassword('Enter new password: ') diff --git a/sys/apps/pastebin.lua b/sys/apps/pastebin.lua index 4cdc80b..aaade9a 100644 --- a/sys/apps/pastebin.lua +++ b/sys/apps/pastebin.lua @@ -11,7 +11,7 @@ if not http then return end -local pastebin = require('http.pastebin') +local pastebin = require('opus.http.pastebin') local tArgs = { ... } local sCommand = tArgs[1] diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index a32676a..5d7f067 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -13,8 +13,8 @@ sandboxEnv.shell = shell _G.requireInjector(_ENV) -local trace = require('trace') -local Util = require('util') +local trace = require('opus.trace') +local Util = require('opus.util') local DIR = (parentShell and parentShell.dir()) or "" local PATH = (parentShell and parentShell.path()) or ".:/rom/programs" @@ -358,12 +358,12 @@ if #tArgs > 0 then return run(env, ...) end -local Config = require('config') -local Entry = require('entry') -local History = require('history') -local Input = require('input') -local Sound = require('sound') -local Terminal = require('terminal') +local Config = require('opus.config') +local Entry = require('opus.entry') +local History = require('opus.history') +local Input = require('opus.input') +local Sound = require('opus.sound') +local Terminal = require('opus.terminal') local colors = _G.colors local os = _G.os diff --git a/sys/apps/system/aliases.lua b/sys/apps/system/aliases.lua index 3a98bc6..6b04c5c 100644 --- a/sys/apps/system/aliases.lua +++ b/sys/apps/system/aliases.lua @@ -1,5 +1,5 @@ -local Config = require('config') -local UI = require('ui') +local Config = require('opus.config') +local UI = require('opus.ui') local kernel = _G.kernel diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index 61d47f9..e40d924 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -1,6 +1,6 @@ -local Ansi = require('ansi') -local Config = require('config') -local UI = require('ui') +local Ansi = require('opus.ansi') +local Config = require('opus.config') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apps/system/kiosk.lua b/sys/apps/system/kiosk.lua index 1adb110..becee72 100644 --- a/sys/apps/system/kiosk.lua +++ b/sys/apps/system/kiosk.lua @@ -1,4 +1,4 @@ -local UI = require('ui') +local UI = require('opus.ui') local colors = _G.colors local peripheral = _G.peripheral diff --git a/sys/apps/system/label.lua b/sys/apps/system/label.lua index 7ed6f6f..e4a73f2 100644 --- a/sys/apps/system/label.lua +++ b/sys/apps/system/label.lua @@ -1,5 +1,5 @@ -local UI = require('ui') -local Util = require('util') +local UI = require('opus.ui') +local Util = require('opus.util') local fs = _G.fs local os = _G.os diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index cebd5b7..6bd290c 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -1,5 +1,5 @@ -local Config = require('config') -local UI = require('ui') +local Config = require('opus.config') +local UI = require('opus.ui') local colors = _G.colors local fs = _G.fs diff --git a/sys/apps/system/network.lua b/sys/apps/system/network.lua index 11f3c00..414c080 100644 --- a/sys/apps/system/network.lua +++ b/sys/apps/system/network.lua @@ -1,6 +1,6 @@ -local Ansi = require('ansi') -local Config = require('config') -local UI = require('ui') +local Ansi = require('opus.ansi') +local Config = require('opus.config') +local UI = require('opus.ui') local device = _G.device diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index ecb7677..4340a73 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -1,6 +1,6 @@ -local Security = require('security') -local SHA = require('crypto.sha2') -local UI = require('ui') +local Security = require('opus.security') +local SHA = require('opus.crypto.sha2') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index 803ed51..bf30f7e 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -1,6 +1,6 @@ -local Config = require('config') -local UI = require('ui') -local Util = require('util') +local Config = require('opus.config') +local UI = require('opus.ui') +local Util = require('opus.util') local tab = UI.Tab { tabTitle = 'Path', diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index b98d93b..38acef9 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -1,6 +1,6 @@ -local Config = require('config') -local UI = require('ui') -local Util = require('util') +local Config = require('opus.config') +local UI = require('opus.ui') +local Util = require('opus.util') local tab = UI.Tab { tabTitle = 'Requires', diff --git a/sys/apps/system/settings.lua b/sys/apps/system/settings.lua index f5d35db..8e071ac 100644 --- a/sys/apps/system/settings.lua +++ b/sys/apps/system/settings.lua @@ -1,4 +1,4 @@ -local UI = require('ui') +local UI = require('opus.ui') local settings = _G.settings diff --git a/sys/apps/system/shell.lua b/sys/apps/system/shell.lua index 238a8d9..13dfda6 100644 --- a/sys/apps/system/shell.lua +++ b/sys/apps/system/shell.lua @@ -1,6 +1,6 @@ -local Config = require('config') -local UI = require('ui') -local Util = require('util') +local Config = require('opus.config') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local os = _G.os diff --git a/sys/apps/telnet.lua b/sys/apps/telnet.lua index 40a1134..91b163e 100644 --- a/sys/apps/telnet.lua +++ b/sys/apps/telnet.lua @@ -1,7 +1,7 @@ -local Event = require('event') -local Socket = require('socket') -local Terminal = require('terminal') -local Util = require('util') +local Event = require('opus.event') +local Socket = require('opus.socket') +local Terminal = require('opus.terminal') +local Util = require('opus.util') local multishell = _ENV.multishell local os = _G.os diff --git a/sys/apps/trust.lua b/sys/apps/trust.lua index dbe0b33..b1283f8 100644 --- a/sys/apps/trust.lua +++ b/sys/apps/trust.lua @@ -1,8 +1,8 @@ -local Crypto = require('crypto.chacha20') -local Security = require('security') -local SHA = require('crypto.sha2') -local Socket = require('socket') -local Terminal = require('terminal') +local Crypto = require('opus.crypto.chacha20') +local Security = require('opus.security') +local SHA = require('opus.crypto.sha2') +local Socket = require('opus.socket') +local Terminal = require('opus.terminal') local os = _G.os diff --git a/sys/apps/vnc.lua b/sys/apps/vnc.lua index b57df3b..cb2d945 100644 --- a/sys/apps/vnc.lua +++ b/sys/apps/vnc.lua @@ -1,7 +1,7 @@ -local Event = require('event') -local Socket = require('socket') -local Terminal = require('terminal') -local Util = require('util') +local Event = require('opus.event') +local Socket = require('opus.socket') +local Terminal = require('opus.terminal') +local Util = require('opus.util') local colors = _G.colors local multishell = _ENV.multishell diff --git a/sys/autorun/clipboard.lua b/sys/autorun/clipboard.lua index ad3286f..72c9434 100644 --- a/sys/autorun/clipboard.lua +++ b/sys/autorun/clipboard.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local kernel = _G.kernel local keyboard = _G.device.keyboard diff --git a/sys/autorun/hotkeys.lua b/sys/autorun/hotkeys.lua index cf50efb..5c6b539 100644 --- a/sys/autorun/hotkeys.lua +++ b/sys/autorun/hotkeys.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local kernel = _G.kernel local keyboard = _G.device.keyboard diff --git a/sys/autorun/welcome.lua b/sys/autorun/welcome.lua index 0ee57d0..b8d113b 100644 --- a/sys/autorun/welcome.lua +++ b/sys/autorun/welcome.lua @@ -1,5 +1,5 @@ -local Config = require('config') -local Util = require('util') +local Config = require('opus.config') +local Util = require('opus.util') local fs = _G.fs local os = _G.os diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index d139e37..f3b5ffb 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -44,19 +44,19 @@ local function runUrl(file, ...) end -- Install require shim -if fs.exists('sys/apis/injector.lua') then - _G.requireInjector = run('sys/apis/injector.lua') +if fs.exists('sys/modules/opus/injector.lua') then + _G.requireInjector = run('sys/modules/opus/injector.lua') else -- not local, run the file system directly from git if package and package.path then - package.path = package.path .. ';' .. BASE .. '/sys/apis' + package.path = package.path .. ';' .. BASE .. '/sys/modules/opus' else sandboxEnv.package = { - path = BASE .. '/sys/apis' + path = BASE .. '/sys/modules/opus' } end - _G.requireInjector = runUrl('sys/apis/injector.lua') + _G.requireInjector = runUrl('sys/modules/opus/injector.lua') runUrl('sys/init/2.vfs.lua') diff --git a/sys/init/1.device.lua b/sys/init/1.device.lua index 803898d..65b1e76 100644 --- a/sys/init/1.device.lua +++ b/sys/init/1.device.lua @@ -1,6 +1,6 @@ _G.requireInjector(_ENV) -local Peripheral = require('peripheral') +local Peripheral = require('opus.peripheral') _G.device = Peripheral.getList() @@ -24,8 +24,8 @@ _G.device.mouse = { state = { }, } -local Input = require('input') -local Util = require('util') +local Input = require('opus.input') +local Util = require('opus.util') local device = _G.device local kernel = _G.kernel diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index b32f6b4..0ddc672 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -3,7 +3,7 @@ if fs.native then end _G.requireInjector(_ENV) -local Util = require('util') +local Util = require('opus.util') -- TODO: support getDrive for virtual nodes @@ -256,7 +256,7 @@ end local function getfstype(fstype) local vfs = fstypes[fstype] if not vfs then - vfs = require('fs.' .. fstype) + vfs = require('opus.fs.' .. fstype) fs.registerType(fstype, vfs) end return vfs diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index 91a7159..8dd5fcb 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local device = _G.device local kernel = _G.kernel diff --git a/sys/init/4.user.lua b/sys/init/4.user.lua index e795997..0349808 100644 --- a/sys/init/4.user.lua +++ b/sys/init/4.user.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local fs = _G.fs local shell = _ENV.shell @@ -43,7 +43,7 @@ end shell.setPath(table.concat(path, ':')) -_G.LUA_PATH = config.lua_path -_G.settings.set('mbs.shell.require_path', config.lua_path) +--_G.LUA_PATH = config.lua_path +--_G.settings.set('mbs.shell.require_path', config.lua_path) fs.loadTab('usr/config/fstab') diff --git a/sys/init/5.network.lua b/sys/init/5.network.lua index bd97122..6526c5f 100644 --- a/sys/init/5.network.lua +++ b/sys/init/5.network.lua @@ -1,6 +1,6 @@ _G.requireInjector(_ENV) -local Config = require('config') +local Config = require('opus.config') local device = _G.device local kernel = _G.kernel diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index ba52af1..ad667cd 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -1,5 +1,5 @@ -local Packages = require('packages') -local Util = require('util') +local Packages = require('opus.packages') +local Util = require('opus.util') local fs = _G.fs local help = _G.help @@ -22,7 +22,7 @@ for name in pairs(Packages:installed()) do table.insert(appPaths, 1, packageDir) local apiPath = fs.combine(packageDir, 'apis') if fs.exists(apiPath) then - fs.mount(fs.combine('sys/apis', name), 'linkfs', apiPath) + fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath) end local helpPath = '/' .. fs.combine(packageDir, 'help') @@ -33,3 +33,5 @@ end help.setPath(table.concat(helpPaths, ':')) shell.setPath(table.concat(appPaths, ':')) + +fs.mount('rom/modules/main/opus', 'linkfs', 'sys/modules/opus') diff --git a/sys/init/6.tl3.lua b/sys/init/6.tl3.lua index 4e797e2..ee3f3aa 100644 --- a/sys/init/6.tl3.lua +++ b/sys/init/6.tl3.lua @@ -2,10 +2,10 @@ if not _G.turtle then return end -local Pathing = require('pathfind') -local Point = require('point') -local synchronized = require('sync').sync -local Util = require('util') +local Pathing = require('opus.pathfind') +local Point = require('opus.point') +local synchronized = require('opus.sync').sync +local Util = require('opus.util') local os = _G.os local peripheral = _G.peripheral diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index d9a2536..b6ada5d 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -1,8 +1,8 @@ _G.requireInjector(_ENV) -local Config = require('config') -local trace = require('trace') -local Util = require('util') +local Config = require('opus.config') +local trace = require('opus.trace') +local Util = require('opus.util') local colors = _G.colors local fs = _G.fs diff --git a/sys/kernel.lua b/sys/kernel.lua index 799164e..62ab6bc 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -1,7 +1,7 @@ _G.requireInjector(_ENV) -local Terminal = require('terminal') -local Util = require('util') +local Terminal = require('opus.terminal') +local Util = require('opus.util') _G.kernel = { UID = 0, diff --git a/sys/apis/ansi.lua b/sys/modules/opus/ansi.lua similarity index 100% rename from sys/apis/ansi.lua rename to sys/modules/opus/ansi.lua diff --git a/sys/apis/array.lua b/sys/modules/opus/array.lua similarity index 100% rename from sys/apis/array.lua rename to sys/modules/opus/array.lua diff --git a/sys/apis/bulkget.lua b/sys/modules/opus/bulkget.lua similarity index 93% rename from sys/apis/bulkget.lua rename to sys/modules/opus/bulkget.lua index a9679ef..29eadcf 100644 --- a/sys/apis/bulkget.lua +++ b/sys/modules/opus/bulkget.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local parallel = _G.parallel diff --git a/sys/apis/class.lua b/sys/modules/opus/class.lua similarity index 100% rename from sys/apis/class.lua rename to sys/modules/opus/class.lua diff --git a/sys/apis/config.lua b/sys/modules/opus/config.lua similarity index 96% rename from sys/apis/config.lua rename to sys/modules/opus/config.lua index 35f7310..3518ba2 100644 --- a/sys/apis/config.lua +++ b/sys/modules/opus/config.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local fs = _G.fs local shell = _ENV.shell diff --git a/sys/apis/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua similarity index 98% rename from sys/apis/crypto/chacha20.lua rename to sys/modules/opus/crypto/chacha20.lua index 03386a4..8302d1b 100644 --- a/sys/apis/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -1,8 +1,8 @@ -- Chacha20 cipher in ComputerCraft -- By Anavrins -local sha2 = require('crypto.sha2') -local util = require('util') +local sha2 = require('opus.crypto.sha2') +local util = require('opus.util') local ROUNDS = 20 -- Adjust this for speed tradeoff diff --git a/sys/apis/crypto/ecc/elliptic.lua b/sys/modules/opus/crypto/ecc/elliptic.lua similarity index 99% rename from sys/apis/crypto/ecc/elliptic.lua rename to sys/modules/opus/crypto/ecc/elliptic.lua index ca2b5b5..4177258 100644 --- a/sys/apis/crypto/ecc/elliptic.lua +++ b/sys/modules/opus/crypto/ecc/elliptic.lua @@ -21,7 +21,7 @@ -- Completeness? Yes: The curve is an Edwards Curve with non-square d and square a, so the curve is complete. -- Indistinguishability? No: The curve does not support indistinguishability maps. -local fp = require('crypto.ecc.fp') +local fp = require('opus.crypto.ecc.fp') local eq = fp.eq local mul = fp.mul local sqr = fp.sqr diff --git a/sys/apis/crypto/ecc/fp.lua b/sys/modules/opus/crypto/ecc/fp.lua similarity index 100% rename from sys/apis/crypto/ecc/fp.lua rename to sys/modules/opus/crypto/ecc/fp.lua diff --git a/sys/apis/crypto/ecc/fq.lua b/sys/modules/opus/crypto/ecc/fq.lua similarity index 100% rename from sys/apis/crypto/ecc/fq.lua rename to sys/modules/opus/crypto/ecc/fq.lua diff --git a/sys/apis/crypto/ecc/init.lua b/sys/modules/opus/crypto/ecc/init.lua similarity index 92% rename from sys/apis/crypto/ecc/init.lua rename to sys/modules/opus/crypto/ecc/init.lua index f765542..e1c1284 100644 --- a/sys/apis/crypto/ecc/init.lua +++ b/sys/modules/opus/crypto/ecc/init.lua @@ -1,6 +1,6 @@ -local fq = require('crypto.ecc.fq') -local elliptic = require('crypto.ecc.elliptic') -local sha256 = require('crypto.sha2') +local fq = require('opus.crypto.ecc.fq') +local elliptic = require('opus.crypto.ecc.elliptic') +local sha256 = require('opus.crypto.sha2') local os = _G.os diff --git a/sys/apis/crypto/sha2.lua b/sys/modules/opus/crypto/sha2.lua similarity index 100% rename from sys/apis/crypto/sha2.lua rename to sys/modules/opus/crypto/sha2.lua diff --git a/sys/apis/entry.lua b/sys/modules/opus/entry.lua similarity index 99% rename from sys/apis/entry.lua rename to sys/modules/opus/entry.lua index 5956762..0029ded 100644 --- a/sys/apis/entry.lua +++ b/sys/modules/opus/entry.lua @@ -1,4 +1,4 @@ -local class = require('class') +local class = require('opus.class') local os = _G.os diff --git a/sys/apis/event.lua b/sys/modules/opus/event.lua similarity index 100% rename from sys/apis/event.lua rename to sys/modules/opus/event.lua diff --git a/sys/apis/fs/gitfs.lua b/sys/modules/opus/fs/gitfs.lua similarity index 91% rename from sys/apis/fs/gitfs.lua rename to sys/modules/opus/fs/gitfs.lua index 026d9e8..21fe1d9 100644 --- a/sys/apis/fs/gitfs.lua +++ b/sys/modules/opus/fs/gitfs.lua @@ -1,4 +1,4 @@ -local git = require('git') +local git = require('opus.git') local fs = _G.fs diff --git a/sys/apis/fs/linkfs.lua b/sys/modules/opus/fs/linkfs.lua similarity index 100% rename from sys/apis/fs/linkfs.lua rename to sys/modules/opus/fs/linkfs.lua diff --git a/sys/apis/fs/netfs.lua b/sys/modules/opus/fs/netfs.lua similarity index 97% rename from sys/apis/fs/netfs.lua rename to sys/modules/opus/fs/netfs.lua index 77ec3b0..e36a1cc 100644 --- a/sys/apis/fs/netfs.lua +++ b/sys/modules/opus/fs/netfs.lua @@ -1,5 +1,5 @@ -local Socket = require('socket') -local synchronized = require('sync').sync +local Socket = require('opus.socket') +local synchronized = require('opus.sync').sync local fs = _G.fs diff --git a/sys/apis/fs/ramfs.lua b/sys/modules/opus/fs/ramfs.lua similarity index 98% rename from sys/apis/fs/ramfs.lua rename to sys/modules/opus/fs/ramfs.lua index 5dc7470..0d943a8 100644 --- a/sys/apis/fs/ramfs.lua +++ b/sys/modules/opus/fs/ramfs.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local fs = _G.fs diff --git a/sys/apis/fs/urlfs.lua b/sys/modules/opus/fs/urlfs.lua similarity index 97% rename from sys/apis/fs/urlfs.lua rename to sys/modules/opus/fs/urlfs.lua index fc3b655..960806e 100644 --- a/sys/apis/fs/urlfs.lua +++ b/sys/modules/opus/fs/urlfs.lua @@ -1,5 +1,5 @@ --local rttp = require('rttp') -local Util = require('util') +local Util = require('opus.util') local fs = _G.fs diff --git a/sys/apis/git.lua b/sys/modules/opus/git.lua similarity index 95% rename from sys/apis/git.lua rename to sys/modules/opus/git.lua index 058f202..49c42e4 100644 --- a/sys/apis/git.lua +++ b/sys/modules/opus/git.lua @@ -1,5 +1,5 @@ -local json = require('json') -local Util = require('util') +local json = require('opus.json') +local Util = require('opus.util') local TREE_URL = 'https://api.github.com/repos/%s/%s/git/trees/%s?recursive=1' local FILE_URL = 'https://raw.githubusercontent.com/%s/%s/%s/%s' diff --git a/sys/apis/gps.lua b/sys/modules/opus/gps.lua similarity index 100% rename from sys/apis/gps.lua rename to sys/modules/opus/gps.lua diff --git a/sys/apis/history.lua b/sys/modules/opus/history.lua similarity index 96% rename from sys/apis/history.lua rename to sys/modules/opus/history.lua index 7162a48..99cc060 100644 --- a/sys/apis/history.lua +++ b/sys/modules/opus/history.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local History = { } local History_mt = { __index = History } diff --git a/sys/apis/http/pastebin.lua b/sys/modules/opus/http/pastebin.lua similarity index 100% rename from sys/apis/http/pastebin.lua rename to sys/modules/opus/http/pastebin.lua diff --git a/sys/apis/injector.lua b/sys/modules/opus/injector.lua similarity index 91% rename from sys/apis/injector.lua rename to sys/modules/opus/injector.lua index 799cf0b..0835c6b 100644 --- a/sys/apis/injector.lua +++ b/sys/modules/opus/injector.lua @@ -8,21 +8,27 @@ local function split(str, pattern) return t end +local hasMain local luaPaths = package and package.path and split(package.path, '(.-);') or { } for i = 1, #luaPaths do if luaPaths[i] == '?' or luaPaths[i] == '?.lua' then luaPaths[i] = nil + elseif string.find(luaPaths[i], '/rom/modules/main') then + hasMain = true end end table.insert(luaPaths, 1, '?.lua') table.insert(luaPaths, 2, '?/init.lua') -table.insert(luaPaths, 3, '/usr/apis/?.lua') -table.insert(luaPaths, 4, '/usr/apis/?/init.lua') -table.insert(luaPaths, 5, '/sys/apis/?.lua') -table.insert(luaPaths, 6, '/sys/apis/?/init.lua') +table.insert(luaPaths, 3, '/usr/modules/?.lua') +table.insert(luaPaths, 4, '/usr/modules/?/init.lua') +table.insert(luaPaths, 5, '/sys/modules/?.lua') +table.insert(luaPaths, 6, '/sys/modules/?/init.lua') local DEFAULT_PATH = table.concat(luaPaths, ';') +if not hasMain then + DEFAULT_PATH = DEFAULT_PATH .. ';/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua' +end local fs = _G.fs local http = _G.http diff --git a/sys/apis/input.lua b/sys/modules/opus/input.lua similarity index 98% rename from sys/apis/input.lua rename to sys/modules/opus/input.lua index ee003a0..b7894d1 100644 --- a/sys/apis/input.lua +++ b/sys/modules/opus/input.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local keyboard = _G.device and _G.device.keyboard local keys = _G.keys @@ -203,4 +203,4 @@ function input:test() end end -return input \ No newline at end of file +return input diff --git a/sys/apis/json.lua b/sys/modules/opus/json.lua similarity index 100% rename from sys/apis/json.lua rename to sys/modules/opus/json.lua diff --git a/sys/apis/jumper/core/bheap.lua b/sys/modules/opus/jumper/core/bheap.lua similarity index 100% rename from sys/apis/jumper/core/bheap.lua rename to sys/modules/opus/jumper/core/bheap.lua diff --git a/sys/apis/jumper/core/node.lua b/sys/modules/opus/jumper/core/node.lua similarity index 100% rename from sys/apis/jumper/core/node.lua rename to sys/modules/opus/jumper/core/node.lua diff --git a/sys/apis/jumper/core/path.lua b/sys/modules/opus/jumper/core/path.lua similarity index 100% rename from sys/apis/jumper/core/path.lua rename to sys/modules/opus/jumper/core/path.lua diff --git a/sys/apis/jumper/core/utils.lua b/sys/modules/opus/jumper/core/utils.lua similarity index 100% rename from sys/apis/jumper/core/utils.lua rename to sys/modules/opus/jumper/core/utils.lua diff --git a/sys/apis/jumper/grid.lua b/sys/modules/opus/jumper/grid.lua similarity index 100% rename from sys/apis/jumper/grid.lua rename to sys/modules/opus/jumper/grid.lua diff --git a/sys/apis/jumper/pathfinder.lua b/sys/modules/opus/jumper/pathfinder.lua similarity index 100% rename from sys/apis/jumper/pathfinder.lua rename to sys/modules/opus/jumper/pathfinder.lua diff --git a/sys/apis/jumper/search/astar.lua b/sys/modules/opus/jumper/search/astar.lua similarity index 100% rename from sys/apis/jumper/search/astar.lua rename to sys/modules/opus/jumper/search/astar.lua diff --git a/sys/apis/map.lua b/sys/modules/opus/map.lua similarity index 96% rename from sys/apis/map.lua rename to sys/modules/opus/map.lua index 8380f38..bd576d3 100644 --- a/sys/apis/map.lua +++ b/sys/modules/opus/map.lua @@ -1,5 +1,5 @@ -- convience functions for tables with key/value pairs -local Util = require('util') +local Util = require('opus.util') local Map = { } diff --git a/sys/apis/nft.lua b/sys/modules/opus/nft.lua similarity index 98% rename from sys/apis/nft.lua rename to sys/modules/opus/nft.lua index 056961b..f6d5468 100644 --- a/sys/apis/nft.lua +++ b/sys/modules/opus/nft.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local NFT = { } diff --git a/sys/apis/packages.lua b/sys/modules/opus/packages.lua similarity index 97% rename from sys/apis/packages.lua rename to sys/modules/opus/packages.lua index 6148e28..c54bb65 100644 --- a/sys/apis/packages.lua +++ b/sys/modules/opus/packages.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local fs = _G.fs local textutils = _G.textutils diff --git a/sys/apis/pathfind.lua b/sys/modules/opus/pathfind.lua similarity index 97% rename from sys/apis/pathfind.lua rename to sys/modules/opus/pathfind.lua index f34c6ac..06c12f1 100644 --- a/sys/apis/pathfind.lua +++ b/sys/modules/opus/pathfind.lua @@ -1,7 +1,7 @@ -local Grid = require('jumper.grid') -local Pathfinder = require('jumper.pathfinder') -local Point = require('point') -local Util = require('util') +local Grid = require('opus.jumper.grid') +local Pathfinder = require('opus.jumper.pathfinder') +local Point = require('opus.point') +local Util = require('opus.util') local turtle = _G.turtle diff --git a/sys/apis/peripheral.lua b/sys/modules/opus/peripheral.lua similarity index 97% rename from sys/apis/peripheral.lua rename to sys/modules/opus/peripheral.lua index 1d84c8a..86d63fc 100644 --- a/sys/apis/peripheral.lua +++ b/sys/modules/opus/peripheral.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local Peripheral = Util.shallowCopy(_G.peripheral) @@ -123,4 +123,4 @@ function Peripheral.get(args) end end -return Peripheral \ No newline at end of file +return Peripheral diff --git a/sys/apis/point.lua b/sys/modules/opus/point.lua similarity index 99% rename from sys/apis/point.lua rename to sys/modules/opus/point.lua index da1d15b..a118df3 100644 --- a/sys/apis/point.lua +++ b/sys/modules/opus/point.lua @@ -1,4 +1,4 @@ -local Util = require('util') +local Util = require('opus.util') local Point = { } diff --git a/sys/apis/security.lua b/sys/modules/opus/security.lua similarity index 88% rename from sys/apis/security.lua rename to sys/modules/opus/security.lua index 691df97..725d864 100644 --- a/sys/apis/security.lua +++ b/sys/modules/opus/security.lua @@ -1,6 +1,6 @@ -local Config = require('config') -local Util = require('util') -local ECC = require('crypto.ecc') +local Config = require('opus.config') +local Util = require('opus.util') +local ECC = require('opus.crypto.ecc') local Security = { } diff --git a/sys/apis/socket.lua b/sys/modules/opus/socket.lua similarity index 97% rename from sys/apis/socket.lua rename to sys/modules/opus/socket.lua index ae1bc60..5e83424 100644 --- a/sys/apis/socket.lua +++ b/sys/modules/opus/socket.lua @@ -1,6 +1,6 @@ -local Crypto = require('crypto.chacha20') -local Security = require('security') -local Util = require('util') +local Crypto = require('opus.crypto.chacha20') +local Security = require('opus.security') +local Util = require('opus.util') local device = _G.device local os = _G.os diff --git a/sys/apis/sound.lua b/sys/modules/opus/sound.lua similarity index 100% rename from sys/apis/sound.lua rename to sys/modules/opus/sound.lua diff --git a/sys/apis/sync.lua b/sys/modules/opus/sync.lua similarity index 100% rename from sys/apis/sync.lua rename to sys/modules/opus/sync.lua diff --git a/sys/apis/terminal.lua b/sys/modules/opus/terminal.lua similarity index 99% rename from sys/apis/terminal.lua rename to sys/modules/opus/terminal.lua index e73d822..cb7df22 100644 --- a/sys/apis/terminal.lua +++ b/sys/modules/opus/terminal.lua @@ -1,4 +1,4 @@ -local Canvas = require('ui.canvas') +local Canvas = require('opus.ui.canvas') local colors = _G.colors local term = _G.term diff --git a/sys/apis/trace.lua b/sys/modules/opus/trace.lua similarity index 100% rename from sys/apis/trace.lua rename to sys/modules/opus/trace.lua diff --git a/sys/apis/ui.lua b/sys/modules/opus/ui.lua similarity index 98% rename from sys/apis/ui.lua rename to sys/modules/opus/ui.lua index 3b0c26b..87a6a67 100644 --- a/sys/apis/ui.lua +++ b/sys/modules/opus/ui.lua @@ -1,9 +1,9 @@ -local Canvas = require('ui.canvas') -local class = require('class') -local Event = require('event') -local Input = require('input') -local Transition = require('ui.transition') -local Util = require('util') +local Canvas = require('opus.ui.canvas') +local class = require('opus.class') +local Event = require('opus.event') +local Input = require('opus.input') +local Transition = require('opus.ui.transition') +local Util = require('opus.util') local _rep = string.rep local _sub = string.sub @@ -1168,7 +1168,7 @@ end local function loadComponents() local function load(name) - local s, m = Util.run(_ENV, 'sys/apis/ui/components/' .. name .. '.lua') + local s, m = Util.run(_ENV, 'sys/modules/opus/ui/components/' .. name .. '.lua') if not s then error(m) end @@ -1181,7 +1181,7 @@ local function loadComponents() return UI[name] end - local components = fs.list('sys/apis/ui/components') + local components = fs.list('sys/modules/opus/ui/components') for _, f in pairs(components) do local name = f:match('(.+)%.') diff --git a/sys/apis/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua similarity index 98% rename from sys/apis/ui/canvas.lua rename to sys/modules/opus/ui/canvas.lua index b7969b0..260ad89 100644 --- a/sys/apis/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -1,6 +1,6 @@ -local class = require('class') -local Region = require('ui.region') -local Util = require('util') +local class = require('opus.class') +local Region = require('opus.ui.region') +local Util = require('opus.util') local _rep = string.rep local _sub = string.sub diff --git a/sys/apis/ui/components/ActiveLayer.lua b/sys/modules/opus/ui/components/ActiveLayer.lua similarity index 89% rename from sys/apis/ui/components/ActiveLayer.lua rename to sys/modules/opus/ui/components/ActiveLayer.lua index 94354fe..dcbb499 100644 --- a/sys/apis/ui/components/ActiveLayer.lua +++ b/sys/modules/opus/ui/components/ActiveLayer.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') UI.ActiveLayer = class(UI.Window) UI.ActiveLayer.defaults = { diff --git a/sys/apis/ui/components/Button.lua b/sys/modules/opus/ui/components/Button.lua similarity index 92% rename from sys/apis/ui/components/Button.lua rename to sys/modules/opus/ui/components/Button.lua index be31cad..c6ea3bf 100644 --- a/sys/apis/ui/components/Button.lua +++ b/sys/modules/opus/ui/components/Button.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors diff --git a/sys/apis/ui/components/Checkbox.lua b/sys/modules/opus/ui/components/Checkbox.lua similarity index 95% rename from sys/apis/ui/components/Checkbox.lua rename to sys/modules/opus/ui/components/Checkbox.lua index 6356ebf..a90e911 100644 --- a/sys/apis/ui/components/Checkbox.lua +++ b/sys/modules/opus/ui/components/Checkbox.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/Chooser.lua b/sys/modules/opus/ui/components/Chooser.lua similarity index 95% rename from sys/apis/ui/components/Chooser.lua rename to sys/modules/opus/ui/components/Chooser.lua index 0284fbf..e4d41fa 100644 --- a/sys/apis/ui/components/Chooser.lua +++ b/sys/modules/opus/ui/components/Chooser.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors diff --git a/sys/apis/ui/components/Dialog.lua b/sys/modules/opus/ui/components/Dialog.lua similarity index 87% rename from sys/apis/ui/components/Dialog.lua rename to sys/modules/opus/ui/components/Dialog.lua index a293052..dd9de1a 100644 --- a/sys/apis/ui/components/Dialog.lua +++ b/sys/modules/opus/ui/components/Dialog.lua @@ -1,6 +1,6 @@ -local Canvas = require('ui.canvas') -local class = require('class') -local UI = require('ui') +local Canvas = require('opus.ui.canvas') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors @@ -36,4 +36,4 @@ function UI.Dialog:eventHandler(event) self:hide() end return UI.SlideOut.eventHandler(self, event) -end \ No newline at end of file +end diff --git a/sys/apis/ui/components/DropMenu.lua b/sys/modules/opus/ui/components/DropMenu.lua similarity index 93% rename from sys/apis/ui/components/DropMenu.lua rename to sys/modules/opus/ui/components/DropMenu.lua index 10ad096..5cce43a 100644 --- a/sys/apis/ui/components/DropMenu.lua +++ b/sys/modules/opus/ui/components/DropMenu.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors diff --git a/sys/apis/ui/components/DropMenuItem.lua b/sys/modules/opus/ui/components/DropMenuItem.lua similarity index 87% rename from sys/apis/ui/components/DropMenuItem.lua rename to sys/modules/opus/ui/components/DropMenuItem.lua index a08f505..09263da 100644 --- a/sys/apis/ui/components/DropMenuItem.lua +++ b/sys/modules/opus/ui/components/DropMenuItem.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/Embedded.lua b/sys/modules/opus/ui/components/Embedded.lua similarity index 93% rename from sys/apis/ui/components/Embedded.lua rename to sys/modules/opus/ui/components/Embedded.lua index ad4db59..fcdee82 100644 --- a/sys/apis/ui/components/Embedded.lua +++ b/sys/modules/opus/ui/components/Embedded.lua @@ -1,6 +1,6 @@ -local class = require('class') -local Terminal = require('terminal') -local UI = require('ui') +local class = require('opus.class') +local Terminal = require('opus.terminal') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua similarity index 96% rename from sys/apis/ui/components/Form.lua rename to sys/modules/opus/ui/components/Form.lua index 8ba925e..f1eb6f3 100644 --- a/sys/apis/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -1,6 +1,6 @@ -local class = require('class') -local Sound = require('sound') -local UI = require('ui') +local class = require('opus.class') +local Sound = require('opus.sound') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/Grid.lua b/sys/modules/opus/ui/components/Grid.lua similarity index 99% rename from sys/apis/ui/components/Grid.lua rename to sys/modules/opus/ui/components/Grid.lua index 21069ee..a1dc638 100644 --- a/sys/apis/ui/components/Grid.lua +++ b/sys/modules/opus/ui/components/Grid.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local os = _G.os diff --git a/sys/apis/ui/components/Image.lua b/sys/modules/opus/ui/components/Image.lua similarity index 90% rename from sys/apis/ui/components/Image.lua rename to sys/modules/opus/ui/components/Image.lua index 630cf3c..787d813 100644 --- a/sys/apis/ui/components/Image.lua +++ b/sys/modules/opus/ui/components/Image.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') UI.Image = class(UI.Window) UI.Image.defaults = { diff --git a/sys/apis/ui/components/Menu.lua b/sys/modules/opus/ui/components/Menu.lua similarity index 94% rename from sys/apis/ui/components/Menu.lua rename to sys/modules/opus/ui/components/Menu.lua index 3377a0e..f581874 100644 --- a/sys/apis/ui/components/Menu.lua +++ b/sys/modules/opus/ui/components/Menu.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') --[[-- Menu --]]-- UI.Menu = class(UI.Grid) diff --git a/sys/apis/ui/components/MenuBar.lua b/sys/modules/opus/ui/components/MenuBar.lua similarity index 96% rename from sys/apis/ui/components/MenuBar.lua rename to sys/modules/opus/ui/components/MenuBar.lua index 4c239c0..bccb9be 100644 --- a/sys/apis/ui/components/MenuBar.lua +++ b/sys/modules/opus/ui/components/MenuBar.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/MenuItem.lua b/sys/modules/opus/ui/components/MenuItem.lua similarity index 79% rename from sys/apis/ui/components/MenuItem.lua rename to sys/modules/opus/ui/components/MenuItem.lua index c4dbc7a..2f0efe8 100644 --- a/sys/apis/ui/components/MenuItem.lua +++ b/sys/modules/opus/ui/components/MenuItem.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/NftImage.lua b/sys/modules/opus/ui/components/NftImage.lua similarity index 89% rename from sys/apis/ui/components/NftImage.lua rename to sys/modules/opus/ui/components/NftImage.lua index 37740c5..b932dfb 100644 --- a/sys/apis/ui/components/NftImage.lua +++ b/sys/modules/opus/ui/components/NftImage.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') UI.NftImage = class(UI.Window) UI.NftImage.defaults = { diff --git a/sys/apis/ui/components/Notification.lua b/sys/modules/opus/ui/components/Notification.lua similarity index 91% rename from sys/apis/ui/components/Notification.lua rename to sys/modules/opus/ui/components/Notification.lua index 33a5745..f1b38f6 100644 --- a/sys/apis/ui/components/Notification.lua +++ b/sys/modules/opus/ui/components/Notification.lua @@ -1,8 +1,8 @@ -local class = require('class') -local Event = require('event') -local Sound = require('sound') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local Event = require('opus.event') +local Sound = require('opus.sound') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors diff --git a/sys/apis/ui/components/ProgressBar.lua b/sys/modules/opus/ui/components/ProgressBar.lua similarity index 84% rename from sys/apis/ui/components/ProgressBar.lua rename to sys/modules/opus/ui/components/ProgressBar.lua index 2a78c5f..56d6156 100644 --- a/sys/apis/ui/components/ProgressBar.lua +++ b/sys/modules/opus/ui/components/ProgressBar.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/ScrollBar.lua b/sys/modules/opus/ui/components/ScrollBar.lua similarity index 95% rename from sys/apis/ui/components/ScrollBar.lua rename to sys/modules/opus/ui/components/ScrollBar.lua index 2299502..d4bef52 100644 --- a/sys/apis/ui/components/ScrollBar.lua +++ b/sys/modules/opus/ui/components/ScrollBar.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors diff --git a/sys/apis/ui/components/ScrollingGrid.lua b/sys/modules/opus/ui/components/ScrollingGrid.lua similarity index 93% rename from sys/apis/ui/components/ScrollingGrid.lua rename to sys/modules/opus/ui/components/ScrollingGrid.lua index c29fe74..93a35e6 100644 --- a/sys/apis/ui/components/ScrollingGrid.lua +++ b/sys/modules/opus/ui/components/ScrollingGrid.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') --[[-- ScrollingGrid --]]-- UI.ScrollingGrid = class(UI.Grid) diff --git a/sys/apis/ui/components/SlideOut.lua b/sys/modules/opus/ui/components/SlideOut.lua similarity index 92% rename from sys/apis/ui/components/SlideOut.lua rename to sys/modules/opus/ui/components/SlideOut.lua index 9e85251..37f5e37 100644 --- a/sys/apis/ui/components/SlideOut.lua +++ b/sys/modules/opus/ui/components/SlideOut.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') --[[-- SlideOut --]]-- UI.SlideOut = class(UI.Window) diff --git a/sys/apis/ui/components/StatusBar.lua b/sys/modules/opus/ui/components/StatusBar.lua similarity index 93% rename from sys/apis/ui/components/StatusBar.lua rename to sys/modules/opus/ui/components/StatusBar.lua index af1d2c4..a92c0c2 100644 --- a/sys/apis/ui/components/StatusBar.lua +++ b/sys/modules/opus/ui/components/StatusBar.lua @@ -1,7 +1,7 @@ -local class = require('class') -local Event = require('event') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local Event = require('opus.event') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors diff --git a/sys/apis/ui/components/Tab.lua b/sys/modules/opus/ui/components/Tab.lua similarity index 69% rename from sys/apis/ui/components/Tab.lua rename to sys/modules/opus/ui/components/Tab.lua index d564c86..2f86b2f 100644 --- a/sys/apis/ui/components/Tab.lua +++ b/sys/modules/opus/ui/components/Tab.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/TabBar.lua b/sys/modules/opus/ui/components/TabBar.lua similarity index 90% rename from sys/apis/ui/components/TabBar.lua rename to sys/modules/opus/ui/components/TabBar.lua index 1431314..72e07f6 100644 --- a/sys/apis/ui/components/TabBar.lua +++ b/sys/modules/opus/ui/components/TabBar.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors diff --git a/sys/apis/ui/components/TabBarMenuItem.lua b/sys/modules/opus/ui/components/TabBarMenuItem.lua similarity index 90% rename from sys/apis/ui/components/TabBarMenuItem.lua rename to sys/modules/opus/ui/components/TabBarMenuItem.lua index 28e585b..0a7799c 100644 --- a/sys/apis/ui/components/TabBarMenuItem.lua +++ b/sys/modules/opus/ui/components/TabBarMenuItem.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua similarity index 94% rename from sys/apis/ui/components/Tabs.lua rename to sys/modules/opus/ui/components/Tabs.lua index 7c2d967..46f87d1 100644 --- a/sys/apis/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') UI.Tabs = class(UI.Window) UI.Tabs.defaults = { diff --git a/sys/apis/ui/components/Text.lua b/sys/modules/opus/ui/components/Text.lua similarity index 76% rename from sys/apis/ui/components/Text.lua rename to sys/modules/opus/ui/components/Text.lua index 68926d2..0250a48 100644 --- a/sys/apis/ui/components/Text.lua +++ b/sys/modules/opus/ui/components/Text.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') UI.Text = class(UI.Window) UI.Text.defaults = { diff --git a/sys/apis/ui/components/TextArea.lua b/sys/modules/opus/ui/components/TextArea.lua similarity index 89% rename from sys/apis/ui/components/TextArea.lua rename to sys/modules/opus/ui/components/TextArea.lua index 8602866..51b8972 100644 --- a/sys/apis/ui/components/TextArea.lua +++ b/sys/modules/opus/ui/components/TextArea.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') --[[-- TextArea --]]-- UI.TextArea = class(UI.Viewport) diff --git a/sys/apis/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua similarity index 94% rename from sys/apis/ui/components/TextEntry.lua rename to sys/modules/opus/ui/components/TextEntry.lua index 69566c6..512f668 100644 --- a/sys/apis/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -1,7 +1,7 @@ -local class = require('class') -local entry = require('entry') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local entry = require('opus.entry') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local _rep = string.rep diff --git a/sys/apis/ui/components/Throttle.lua b/sys/modules/opus/ui/components/Throttle.lua similarity index 95% rename from sys/apis/ui/components/Throttle.lua rename to sys/modules/opus/ui/components/Throttle.lua index 1e2dc02..0466486 100644 --- a/sys/apis/ui/components/Throttle.lua +++ b/sys/modules/opus/ui/components/Throttle.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors local os = _G.os diff --git a/sys/apis/ui/components/TitleBar.lua b/sys/modules/opus/ui/components/TitleBar.lua similarity index 96% rename from sys/apis/ui/components/TitleBar.lua rename to sys/modules/opus/ui/components/TitleBar.lua index 872d78b..ce7fdd4 100644 --- a/sys/apis/ui/components/TitleBar.lua +++ b/sys/modules/opus/ui/components/TitleBar.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors local _rep = string.rep diff --git a/sys/apis/ui/components/VerticalMeter.lua b/sys/modules/opus/ui/components/VerticalMeter.lua similarity index 85% rename from sys/apis/ui/components/VerticalMeter.lua rename to sys/modules/opus/ui/components/VerticalMeter.lua index e4f1e7b..012d0e5 100644 --- a/sys/apis/ui/components/VerticalMeter.lua +++ b/sys/modules/opus/ui/components/VerticalMeter.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/Viewport.lua b/sys/modules/opus/ui/components/Viewport.lua similarity index 97% rename from sys/apis/ui/components/Viewport.lua rename to sys/modules/opus/ui/components/Viewport.lua index b0e2a30..acb84cd 100644 --- a/sys/apis/ui/components/Viewport.lua +++ b/sys/modules/opus/ui/components/Viewport.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/components/Wizard.lua b/sys/modules/opus/ui/components/Wizard.lua similarity index 96% rename from sys/apis/ui/components/Wizard.lua rename to sys/modules/opus/ui/components/Wizard.lua index 67dcc2b..e6bce7e 100644 --- a/sys/apis/ui/components/Wizard.lua +++ b/sys/modules/opus/ui/components/Wizard.lua @@ -1,6 +1,6 @@ -local class = require('class') -local UI = require('ui') -local Util = require('util') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') UI.Wizard = class(UI.Window) UI.Wizard.defaults = { diff --git a/sys/apis/ui/components/WizardPage.lua b/sys/modules/opus/ui/components/WizardPage.lua similarity index 70% rename from sys/apis/ui/components/WizardPage.lua rename to sys/modules/opus/ui/components/WizardPage.lua index dae4e9a..cb2c2de 100644 --- a/sys/apis/ui/components/WizardPage.lua +++ b/sys/modules/opus/ui/components/WizardPage.lua @@ -1,5 +1,5 @@ -local class = require('class') -local UI = require('ui') +local class = require('opus.class') +local UI = require('opus.ui') local colors = _G.colors diff --git a/sys/apis/ui/region.lua b/sys/modules/opus/ui/region.lua similarity index 100% rename from sys/apis/ui/region.lua rename to sys/modules/opus/ui/region.lua diff --git a/sys/apis/ui/transition.lua b/sys/modules/opus/ui/transition.lua similarity index 96% rename from sys/apis/ui/transition.lua rename to sys/modules/opus/ui/transition.lua index 90db9ee..4448760 100644 --- a/sys/apis/ui/transition.lua +++ b/sys/modules/opus/ui/transition.lua @@ -1,4 +1,4 @@ -local Tween = require('ui.tween') +local Tween = require('opus.ui.tween') local Transition = { } diff --git a/sys/apis/ui/tween.lua b/sys/modules/opus/ui/tween.lua similarity index 100% rename from sys/apis/ui/tween.lua rename to sys/modules/opus/ui/tween.lua diff --git a/sys/apis/util.lua b/sys/modules/opus/util.lua similarity index 100% rename from sys/apis/util.lua rename to sys/modules/opus/util.lua -- 2.49.1 From d90aa0e2fd0bf27224998f789dd60e150ace7087 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 28 Jun 2019 14:22:03 -0400 Subject: [PATCH 021/236] cleanup --- sys/autorun/upgraded.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index 5c947c5..f988ba1 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -5,3 +5,7 @@ if fs.exists('sys/etc/app.db') then fs.delete('sys/etc/app.db') end if fs.exists('sys/extensions') then fs.delete('sys/extensions') end if fs.exists('sys/network') then fs.delete('sys/network') end if fs.exists('startup') then fs.delete('startup') end + +if fs.exists('sys/autorun/gps.lua') then fs.delete('sys/autorun/gps.lua') end +if fs.exists('sys/apps/network/redserver.lua') then fs.delete('sys/apps/network/redserver.lua') end +if fs.exists('sys/apis') then fs.delete('sys/apis') end -- 2.49.1 From 00293033c8fef3aaeb627d84960038e1423c94e7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 29 Jun 2019 02:44:30 -0400 Subject: [PATCH 022/236] security update round 2 --- sys/apps/shell.lua | 1 + sys/autorun/upgraded.lua | 1 + sys/boot/opus.boot | 52 ++---------- sys/modules/opus/crypto/chacha20.lua | 6 +- sys/modules/opus/crypto/sha2.lua | 18 ++++- sys/modules/opus/injector.lua | 114 ++++----------------------- sys/modules/opus/security.lua | 23 +++++- sys/modules/opus/socket.lua | 35 ++++++-- 8 files changed, 91 insertions(+), 159 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 5d7f067..c5dbf28 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -71,6 +71,7 @@ local function run(env, ...) tProgramStack[#tProgramStack + 1] = path end + env[ "arg" ] = { [0] = path, table.unpack(args) } local r = { fn(table.unpack(args)) } tProgramStack[#tProgramStack] = nil diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index f988ba1..d3a2737 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -7,5 +7,6 @@ if fs.exists('sys/network') then fs.delete('sys/network') end if fs.exists('startup') then fs.delete('startup') end if fs.exists('sys/autorun/gps.lua') then fs.delete('sys/autorun/gps.lua') end +if fs.exists('sys/autorun/gpsHost.lua') then fs.delete('sys/autorun/gpsHost.lua') end if fs.exists('sys/apps/network/redserver.lua') then fs.delete('sys/apps/network/redserver.lua') end if fs.exists('sys/apis') then fs.delete('sys/apis') end diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index f3b5ffb..2a02d14 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -1,68 +1,28 @@ --- Loads the Opus environment regardless if the file system is local or not -local fs = _G.fs -local http = _G.http - -_G.OPUS_BRANCH = 'develop-1.8' -local GIT_REPO = 'kepler155c/opus/' .. _G.OPUS_BRANCH -local BASE = 'https://raw.githubusercontent.com/' .. GIT_REPO +local fs = _G.fs local sandboxEnv = setmetatable({ }, { __index = _G }) for k,v in pairs(_ENV) do sandboxEnv[k] = v end -_G._syslog = function() end - -local function makeEnv() +local function run(file, ...) local env = setmetatable({ }, { __index = _G }) for k,v in pairs(sandboxEnv) do env[k] = v end - return env -end -local function run(file, ...) - local s, m = loadfile(file, makeEnv()) + local s, m = loadfile(file, env) if s then return s(...) end error('Error loading ' .. file .. '\n' .. m) end -local function runUrl(file, ...) - local url = BASE .. '/' .. file - - local u = http.get(url) - if u then - local fn = load(u.readAll(), url, nil, makeEnv()) - u.close() - if fn then - return fn(...) - end - end - error('Failed to download ' .. url) -end +_G._syslog = function() end +_G.OPUS_BRANCH = 'develop-1.8' -- Install require shim -if fs.exists('sys/modules/opus/injector.lua') then - _G.requireInjector = run('sys/modules/opus/injector.lua') -else - -- not local, run the file system directly from git - if package and package.path then - package.path = package.path .. ';' .. BASE .. '/sys/modules/opus' - else - sandboxEnv.package = { - path = BASE .. '/sys/modules/opus' - } - end - - _G.requireInjector = runUrl('sys/modules/opus/injector.lua') - - runUrl('sys/init/2.vfs.lua') - - -- install file system - fs.mount('', 'gitfs', GIT_REPO) -end +_G.requireInjector = run('sys/modules/opus/injector.lua') local s, m = pcall(run, 'sys/apps/shell.lua', 'sys/kernel.lua', ...) diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 8302d1b..b08d09a 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -115,6 +115,7 @@ local function crypt(data, key, nonce, cntr, round) cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 + local throttle = util.throttle() local out = {} local state = initState(key, nonce, cntr) local blockAmt = math.floor(#data/64) @@ -131,8 +132,9 @@ local function crypt(data, key, nonce, cntr, round) end if i % 1000 == 0 then - os.queueEvent("") - os.pullEvent("") + throttle() + --os.queueEvent("") + --os.pullEvent("") end end return setmetatable(out, mt) diff --git a/sys/modules/opus/crypto/sha2.lua b/sys/modules/opus/crypto/sha2.lua index a4f29f3..631488a 100644 --- a/sys/modules/opus/crypto/sha2.lua +++ b/sys/modules/opus/crypto/sha2.lua @@ -162,12 +162,25 @@ local function hmac(data, key) return digest(padded_key) end +local function throttler() + local ts = os.clock() + local timeout = .095 + return function() + local nts = os.clock() + if nts > ts + timeout then + os.sleep(0) + ts = os.clock() + end + end +end + local function pbkdf2(pass, salt, iter, dklen) salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} local hashlen = 32 dklen = dklen or 32 local block = 1 local out = {} + local throttle = throttler() while dklen > 0 do local ikey = {} @@ -182,7 +195,10 @@ local function pbkdf2(pass, salt, iter, dklen) for j = 1, iter do isalt = hmac(isalt, pass) for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end - if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end + if j % 200 == 0 then + throttle() + --os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") + end end dklen = dklen - clen block = block+1 diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index 0835c6b..e93e5d1 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -1,6 +1,3 @@ -local PASTEBIN_URL = 'http://pastebin.com/raw' -local GIT_URL = 'https://raw.githubusercontent.com' - local function split(str, pattern) local t = { } local function helper(line) table.insert(t, line) return "" end @@ -11,7 +8,7 @@ end local hasMain local luaPaths = package and package.path and split(package.path, '(.-);') or { } for i = 1, #luaPaths do - if luaPaths[i] == '?' or luaPaths[i] == '?.lua' then + if luaPaths[i] == '?' or luaPaths[i] == '?.lua' or luaPaths[i] == '?/init.lua' then luaPaths[i] = nil elseif string.find(luaPaths[i], '/rom/modules/main') then hasMain = true @@ -22,70 +19,20 @@ table.insert(luaPaths, 1, '?.lua') table.insert(luaPaths, 2, '?/init.lua') table.insert(luaPaths, 3, '/usr/modules/?.lua') table.insert(luaPaths, 4, '/usr/modules/?/init.lua') -table.insert(luaPaths, 5, '/sys/modules/?.lua') -table.insert(luaPaths, 6, '/sys/modules/?/init.lua') +if not hasMain then + table.insert(luaPaths, 5, '/rom/modules/main/?') + table.insert(luaPaths, 6, '/rom/modules/main/?.lua') + table.insert(luaPaths, 7, '/rom/modules/main/?/init.lua') +end +table.insert(luaPaths, '/sys/modules/?.lua') +table.insert(luaPaths, '/sys/modules/?/init.lua') local DEFAULT_PATH = table.concat(luaPaths, ';') -if not hasMain then - DEFAULT_PATH = DEFAULT_PATH .. ';/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua' -end local fs = _G.fs -local http = _G.http local os = _G.os local string = _G.string ---[[ -if not http._patched then - -- fix broken http get (http.get is not coroutine safe) - local syncLocks = { } - - local function sync(obj, fn) - local key = tostring(obj) - if syncLocks[key] then - local cos = tostring(coroutine.running()) - table.insert(syncLocks[key], cos) - repeat - local _, co = os.pullEvent('sync_lock') - until co == cos - else - syncLocks[key] = { } - end - fn() - local co = table.remove(syncLocks[key], 1) - if co then - os.queueEvent('sync_lock', co) - else - syncLocks[key] = nil - end - end - - -- todo -- completely replace http.get with function that - -- checks for success on permanent redirects (minecraft 1.75 bug) - - http._patched = http.get - function http.get(url, headers) - local s, m - sync(url, function() - s, m = http._patched(url, headers) - end) - return s, m - end -end ---]] - -local function loadUrl(url) - local c - local h = http.get(url) - if h then - c = h.readAll() - h.close() - end - if c and #c > 0 then - return c - end -end - -- Add require and package to the environment return function(env) local function preloadSearcher(modname) @@ -118,45 +65,14 @@ return function(env) local sPath = string.gsub(pattern, "%?", fname) -- TODO: if there's no shell, we should not be checking relative paths below -- as they will resolve to root directory - if sPath:match("^(https?:)") then - local c = loadUrl(sPath) - if c then - return load(c, modname, nil, env) - end - else - if env.shell and - type(env.shell.getRunningProgram) == 'function' and - sPath:sub(1, 1) ~= "/" then + if env.shell and + type(env.shell.getRunningProgram) == 'function' and + sPath:sub(1, 1) ~= "/" then - sPath = fs.combine(fs.getDir(env.shell.getRunningProgram() or ''), sPath) - end - if fs.exists(sPath) and not fs.isDir(sPath) then - return loadfile(sPath, env) - end + sPath = fs.combine(fs.getDir(env.shell.getRunningProgram() or ''), sPath) end - end - end - - -- require('BniCQPVf') - local function pastebinSearcher(modname) - if #modname == 8 and not modname:match('%W') then - local url = PASTEBIN_URL .. '/' .. modname - local c = loadUrl(url) - if c then - return load(c, modname, nil, env) - end - end - end - - -- require('kepler155c.opus.master.sys.apis.util') - local function gitSearcher(modname) - local fname = modname:gsub('%.', '/') .. '.lua' - local _, count = fname:gsub("/", "") - if count >= 3 then - local url = GIT_URL .. '/' .. fname - local c = loadUrl(url) - if c then - return load(c, modname, nil, env) + if fs.exists(sPath) and not fs.isDir(sPath) then + return loadfile(sPath, env) end end end @@ -178,8 +94,6 @@ return function(env) preloadSearcher, loadedSearcher, pathSearcher, - pastebinSearcher, - gitSearcher, } } diff --git a/sys/modules/opus/security.lua b/sys/modules/opus/security.lua index 725d864..ce194f9 100644 --- a/sys/modules/opus/security.lua +++ b/sys/modules/opus/security.lua @@ -13,13 +13,28 @@ function Security.hasPassword() return not not Security.getPassword() end +local function genKey() + local key = { } + for _ = 1, 32 do + table.insert(key, ("%02x"):format(math.random(0, 0xFF))) + end + return table.concat(key) +end + +function Security.generateKeyPair() + local privateKey = Util.hexToByteArray(genKey()) + return privateKey, ECC.publicKey(privateKey) +end + +function Security.getIdentifier() + return Security.geetPublicKey() +end + +-- deprecate - will use getIdentifier function Security.getSecretKey() local config = Config.load('os') if not config.secretKey then - config.secretKey = "" - for _ = 1, 32 do - config.secretKey = config.secretKey .. ("%02x"):format(math.random(0, 0xFF)) - end + config.secretKey = genKey() Config.update('os', config) end return Util.hexToByteArray(config.secretKey) diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 5e83424..da8ceff 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -1,5 +1,7 @@ local Crypto = require('opus.crypto.chacha20') +local ECC = require('opus.crypto.ecc') local Security = require('opus.security') +local SHA = require('opus.crypto.sha2') local Util = require('opus.util') local device = _G.device @@ -60,6 +62,14 @@ function socketClass:ping() end end +function socketClass:setupEncryption() + self.sharedKey = ECC.exchange(self.privKey, self.remotePubKey) + self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) + self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) + self.rseed = SHA.pbkdf2(self.sharedKey, "3rseed", 1) + self.wseed = SHA.pbkdf2(self.sharedKey, "4sseed", 1) +end + function socketClass:close() if self.connected then self.transmit(self.dport, self.dhost, { @@ -110,14 +120,20 @@ function Socket.connect(host, port) local socket = newSocket(host == os.getComputerID()) socket.dhost = tonumber(host) + socket.privKey, socket.pubKey = Security.generateKeyPair() socket.transmit(port, socket.sport, { type = 'OPEN', shost = socket.shost, dhost = socket.dhost, - t = Crypto.encrypt({ ts = os.time(), seq = socket.seq, nts = os.epoch('utc') }, Security.getPublicKey()), rseq = socket.wseq, wseq = socket.rseq, + t = Crypto.encrypt({ + ts = os.time(), + seq = socket.seq, + nts = os.epoch('utc'), + pk = Util.byteArrayToHex(socket.pubKey), + }, Security.getPublicKey()), }) local timerId = os.startTimer(3) @@ -133,6 +149,8 @@ function Socket.connect(host, port) if msg.type == 'CONN' then socket.dport = dport socket.connected = true + socket.remotePubKey = Util.hexToByteArray(msg.pk) + socket:setupEncryption() -- Logger.log('socket', 'connection established to %d %d->%d', -- host, socket.sport, socket.dport) _G.transport.open(socket) @@ -153,7 +171,7 @@ function Socket.connect(host, port) return false, 'Connection timed out', 'TIMEOUT' end -local function trusted(msg, port) +local function trusted(socket, msg, port) if port == 19 or msg.shost == os.getComputerID() then -- no auth for trust server or loopback return true @@ -168,11 +186,12 @@ local function trusted(msg, port) local pubKey = trustList[msg.shost] if pubKey and msg.t then - pubKey = Util.hexToByteArray(pubKey) - local data = Crypto.decrypt(msg.t, pubKey) + local data = Crypto.decrypt(msg.t, Util.hexToByteArray(pubKey)) if data and data.nts then -- upgraded security - return data.nts and tonumber(data.nts) and math.abs(os.epoch('utc') - data.nts) < 1024 + if data.nts and tonumber(data.nts) and math.abs(os.epoch('utc') - data.nts) < 1024 then + socket.remotePubKey = Util.hexToByteArray(data.pk) + end end --local sharedKey = modexp(pubKey, exchange.secretKey, public.primeMod) @@ -207,13 +226,17 @@ function Socket.server(port) }) socket:close() - elseif trusted(msg, port) then + elseif trusted(socket, msg, port) then socket.connected = true + socket.privKey, socket.pubKey = Security.generateKeyPair() + socket:setupEncryption() socket.transmit(socket.dport, socket.sport, { type = 'CONN', dhost = socket.dhost, shost = socket.shost, + pk = Util.byteArrayToHex(socket.pubKey), }) + -- Logger.log('socket', 'Connection established %d->%d', socket.sport, socket.dport) _G.transport.open(socket) -- 2.49.1 From 5283de18ed918a3160fb81f99e746bc6bec92e35 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 29 Jun 2019 04:04:51 -0400 Subject: [PATCH 023/236] oops --- sys/modules/opus/security.lua | 2 +- sys/modules/opus/socket.lua | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sys/modules/opus/security.lua b/sys/modules/opus/security.lua index ce194f9..a44605c 100644 --- a/sys/modules/opus/security.lua +++ b/sys/modules/opus/security.lua @@ -27,7 +27,7 @@ function Security.generateKeyPair() end function Security.getIdentifier() - return Security.geetPublicKey() + return Security.getPublicKey() end -- deprecate - will use getIdentifier diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index da8ceff..bb3b6ef 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -63,11 +63,13 @@ function socketClass:ping() end function socketClass:setupEncryption() + if false then self.sharedKey = ECC.exchange(self.privKey, self.remotePubKey) self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) self.rseed = SHA.pbkdf2(self.sharedKey, "3rseed", 1) self.wseed = SHA.pbkdf2(self.sharedKey, "4sseed", 1) + end end function socketClass:close() -- 2.49.1 From 69522e61d44bdae66716d055d7960743fbc3aab9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 29 Jun 2019 08:50:11 -0400 Subject: [PATCH 024/236] better info on upgrade --- sys/autorun/welcome.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sys/autorun/welcome.lua b/sys/autorun/welcome.lua index b8d113b..b7fc3cc 100644 --- a/sys/autorun/welcome.lua +++ b/sys/autorun/welcome.lua @@ -6,13 +6,14 @@ local os = _G.os local shell = _ENV.shell local config = Config.load('os') -if not config.welcomed and shell.openForegroundTab then +if not config.welcomed then config.welcomed = true config.securityUpdate = true config.readNotes = 1 Config.update('os', config) - - shell.openForegroundTab('Welcome') + if shell.openForegroundTab then + shell.openForegroundTab('Welcome') + end end if not config.securityUpdate then @@ -34,9 +35,13 @@ password in System->System->Password. All computers that you connect to will also need to be updated as well. +Also, I have changed the location for apis. +This will require you to update all installed +packages. Sorry ! + Thanks for your patience. And... thanks to Anavrins for the much improved security. - ]]) +]]) end if fs.exists('sys/notes_1.txt') and shell.openForegroundTab then -- 2.49.1 From e75a357209174ece39d1c5fc49e41a08de2949d7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 29 Jun 2019 16:35:33 -0400 Subject: [PATCH 025/236] security updates --- sys/apps/Network.lua | 10 ++- sys/apps/netdaemon.lua | 1 + sys/apps/network/keygen.lua | 39 +++++++++++ sys/apps/network/transport.lua | 47 +++++-------- sys/apps/network/trust.lua | 6 +- sys/apps/trust.lua | 7 +- sys/modules/opus/crypto/chacha20.lua | 8 +-- sys/modules/opus/crypto/ecc/elliptic.lua | 16 +++-- sys/modules/opus/crypto/ecc/fp.lua | 2 + sys/modules/opus/crypto/ecc/fq.lua | 2 + sys/modules/opus/crypto/ecc/init.lua | 1 + sys/modules/opus/crypto/sha2.lua | 16 +---- sys/modules/opus/security.lua | 26 ++++---- sys/modules/opus/socket.lua | 84 +++++++++++------------- sys/modules/opus/util.lua | 1 + 15 files changed, 147 insertions(+), 119 deletions(-) create mode 100644 sys/apps/network/keygen.lua diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index f9f4cc1..1c8405d 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -137,10 +137,14 @@ end ]] function page.ports.grid:update() + local transport = network:getTransport() + local function findConnection(port) - for _,socket in pairs(_G.transport.sockets) do - if socket.sport == port then - return socket + if transport then + for _,socket in pairs(transport.sockets) do + if socket.sport == port then + return socket + end end end end diff --git a/sys/apps/netdaemon.lua b/sys/apps/netdaemon.lua index fb9e23e..2457a49 100644 --- a/sys/apps/netdaemon.lua +++ b/sys/apps/netdaemon.lua @@ -14,6 +14,7 @@ if not device.wireless_modem then end print('Net daemon starting') +device.wireless_modem.closeAll() for _,file in pairs(fs.list('sys/apps/network')) do local fn, msg = Util.run(_ENV, 'sys/apps/network/' .. file) diff --git a/sys/apps/network/keygen.lua b/sys/apps/network/keygen.lua new file mode 100644 index 0000000..65f04b8 --- /dev/null +++ b/sys/apps/network/keygen.lua @@ -0,0 +1,39 @@ +local ECC = require('opus.crypto.ecc') +local Event = require('opus.event') +local Util = require('opus.util') + +local network = _G.network +local os = _G.os + +local keyPairs = { } + +local function generateKeyPair() + local key = { } + for _ = 1, 32 do + table.insert(key, ("%02x"):format(math.random(0, 0xFF))) + end + local privateKey = Util.hexToByteArray(table.concat(key)) + return privateKey, ECC.publicKey(privateKey) +end + +getmetatable(network).__index.getKeyPair = function() + local keys = table.remove(keyPairs) + os.queueEvent('generate_keypair') + if not keys then + return generateKeyPair() + end + return table.unpack(keys) +end + +-- Generate key pairs in the background as this is a time-consuming process +Event.on('generate_keypair', function() + while true do + os.sleep(5) + local timer = Util.timer() + table.insert(keyPairs, { generateKeyPair() }) + _G._syslog('Generated keypair in ' .. timer()) + if #keyPairs >= 3 then + break + end + end +end) diff --git a/sys/apps/network/transport.lua b/sys/apps/network/transport.lua index fa1978c..316f46a 100644 --- a/sys/apps/network/transport.lua +++ b/sys/apps/network/transport.lua @@ -6,7 +6,9 @@ ]]-- local Event = require('opus.event') +local SHA = require('opus.crypto.sha2') +local network = _G.network local os = _G.os local computerId = os.getComputerID() @@ -15,7 +17,10 @@ local transport = { sockets = { }, UID = 0, } -_G.transport = transport + +getmetatable(network).__index.getTransport = function() + return transport +end function transport.open(socket) transport.UID = transport.UID + 1 @@ -33,19 +38,11 @@ function transport.read(socket) end function transport.write(socket, data) - --_syslog('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq })) socket.transmit(socket.dport, socket.dhost, data) - - --local timerId = os.startTimer(3) - - --transport.timers[timerId] = socket - --socket.timers[socket.wseq] = timerId - - socket.wseq = socket.wseq + 1 + socket.wseq = SHA.digest(socket.wseq):toHex() end function transport.ping(socket) - --_syslog('>> ' .. Util.tostring({ type = 'DATA', seq = socket.wseq })) if os.clock() - socket.activityTimer > 10 then socket.activityTimer = os.clock() socket.transmit(socket.dport, socket.dhost, { @@ -53,7 +50,7 @@ function transport.ping(socket) seq = -1, }) - local timerId = os.startTimer(5) + local timerId = os.startTimer(3) transport.timers[timerId] = socket socket.timers[-1] = timerId end @@ -78,18 +75,19 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance) local socket = transport.sockets[dport] if socket and socket.connected then - --if msg.type then _syslog('<< ' .. Util.tostring(msg)) end if socket.co and coroutine.status(socket.co) == 'dead' then _G._syslog('socket coroutine dead') socket:close() elseif msg.type == 'DISC' then -- received disconnect from other end - if socket.connected then - os.queueEvent('transport_' .. socket.uid) + if msg.seq == socket.rseq then + if socket.connected then + os.queueEvent('transport_' .. socket.uid) + end + socket.connected = false + socket:close() end - socket.connected = false - socket:close() elseif msg.type == 'ACK' then local ackTimerId = socket.timers[msg.seq] @@ -108,28 +106,19 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance) }) elseif msg.type == 'DATA' and msg.data then - socket.activityTimer = os.clock() if msg.seq ~= socket.rseq then print('transport seq error - closing socket ' .. socket.sport) _syslog(msg.data) - _syslog('current ' .. socket.rseq) - _syslog('expected ' .. msg.seq) --- socket:close() --- os.queueEvent('transport_' .. socket.uid) + _syslog('expected ' .. socket.rseq) + _syslog('got ' .. msg.seq) else - socket.rseq = socket.rseq + 1 + socket.activityTimer = os.clock() + socket.rseq = SHA.digest(socket.rseq):toHex() table.insert(socket.messages, { msg.data, distance }) - -- use resume instead ?? if not socket.messages[2] then -- table size is 1 os.queueEvent('transport_' .. socket.uid) end - - --_syslog('>> ' .. Util.tostring({ type = 'ACK', seq = msg.seq })) - --socket.transmit(socket.dport, socket.dhost, { - -- type = 'ACK', - -- seq = msg.seq, - --}) end end end diff --git a/sys/apps/network/trust.lua b/sys/apps/network/trust.lua index b31acbc..58d3847 100644 --- a/sys/apps/network/trust.lua +++ b/sys/apps/network/trust.lua @@ -4,6 +4,8 @@ local Security = require('opus.security') local Socket = require('opus.socket') local Util = require('opus.util') +local trustId = '01c3ba27fe01383a03a1785276d99df27c3edcef68fbf231ca' + local function trustConnection(socket) local data = socket:read(2) if data then @@ -14,7 +16,7 @@ local function trustConnection(socket) data = Crypto.decrypt(data, password) if data and data.pk and data.dh == socket.dhost then local trustList = Util.readTable('usr/.known_hosts') or { } - trustList[data.dh] = Util.byteArrayToHex(data.pk) + trustList[data.dh] = data.pk Util.writeTable('usr/.known_hosts', trustList) socket:write({ success = true, msg = 'Trust accepted' }) @@ -29,7 +31,7 @@ Event.addRoutine(function() print('trust: listening on port 19') while true do - local socket = Socket.server(19) + local socket = Socket.server(19, { identifier = trustId }) print('trust: connection from ' .. socket.dhost) diff --git a/sys/apps/trust.lua b/sys/apps/trust.lua index b1283f8..9d036c9 100644 --- a/sys/apps/trust.lua +++ b/sys/apps/trust.lua @@ -27,15 +27,16 @@ if not password then end print('connecting...') -local socket, msg = Socket.connect(remoteId, 19) +local trustId = '01c3ba27fe01383a03a1785276d99df27c3edcef68fbf231ca' +local socket, msg = Socket.connect(remoteId, 19, { identifier = trustId }) if not socket then error(msg) end -local publicKey = Security.getPublicKey() +local identifier = Security.getIdentifier() -socket:write(Crypto.encrypt({ pk = publicKey, dh = os.getComputerID() }, SHA.compute(password))) +socket:write(Crypto.encrypt({ pk = identifier, dh = os.getComputerID() }, SHA.compute(password))) local data = socket:read(2) socket:close() diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index b08d09a..3b3569a 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -2,7 +2,7 @@ -- By Anavrins local sha2 = require('opus.crypto.sha2') -local util = require('opus.util') +local Util = require('opus.util') local ROUNDS = 20 -- Adjust this for speed tradeoff @@ -115,7 +115,7 @@ local function crypt(data, key, nonce, cntr, round) cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 - local throttle = util.throttle() + local throttle = Util.throttle() local out = {} local state = initState(key, nonce, cntr) local blockAmt = math.floor(#data/64) @@ -157,8 +157,8 @@ local function encrypt(data, key) end local function decrypt(data, key) - local nonce = util.hexToByteArray(data[1]) - data = util.hexToByteArray(data[2]) + local nonce = Util.hexToByteArray(data[1]) + data = Util.hexToByteArray(data[2]) key = sha2.digest(key) local ptx = crypt(data, key, nonce, 1, ROUNDS) return textutils.unserialise(tostring(ptx)) diff --git a/sys/modules/opus/crypto/ecc/elliptic.lua b/sys/modules/opus/crypto/ecc/elliptic.lua index 4177258..9af2683 100644 --- a/sys/modules/opus/crypto/ecc/elliptic.lua +++ b/sys/modules/opus/crypto/ecc/elliptic.lua @@ -22,6 +22,8 @@ -- Indistinguishability? No: The curve does not support indistinguishability maps. local fp = require('opus.crypto.ecc.fp') +local Util = require('opus.util') + local eq = fp.eq local mul = fp.mul local sqr = fp.sqr @@ -31,6 +33,7 @@ local shr = fp.shr local mont = fp.mont local invMont = fp.invMont local sub192 = fp.sub192 +local unpack = table.unpack local bits = 192 local pMinusTwoBinary = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} @@ -203,20 +206,23 @@ local function scalarMul(s, P1) end local Q = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}} - for i = #naf, 1, -1 do + for i = #naf, 1, -1 do -- can this loop be optimized ? + local n = naf[i] Q = pointDouble(Q) - if naf[i] > 0 then - Q = pointAdd(Q, PTable[naf[i]]) - elseif naf[i] < 0 then - Q = pointSub(Q, PTable[-naf[i]]) + if n > 0 then + Q = pointAdd(Q, PTable[n]) + elseif n < 0 then + Q = pointSub(Q, PTable[-n]) end end return Q end +local throttle = Util.throttle() for i = 2, 196 do GTable[i] = pointDouble(GTable[i - 1]) + throttle() end local function scalarMulG(s) diff --git a/sys/modules/opus/crypto/ecc/fp.lua b/sys/modules/opus/crypto/ecc/fp.lua index 3a8c4c6..9e791bd 100644 --- a/sys/modules/opus/crypto/ecc/fp.lua +++ b/sys/modules/opus/crypto/ecc/fp.lua @@ -1,5 +1,7 @@ -- Fp Integer Arithmetic +local unpack = table.unpack + local n = 0xffff local m = 0x10000 diff --git a/sys/modules/opus/crypto/ecc/fq.lua b/sys/modules/opus/crypto/ecc/fq.lua index 277f64d..c552688 100644 --- a/sys/modules/opus/crypto/ecc/fq.lua +++ b/sys/modules/opus/crypto/ecc/fq.lua @@ -1,5 +1,7 @@ -- Fq Integer Arithmetic +local unpack = table.unpack + local n = 0xffff local m = 0x10000 diff --git a/sys/modules/opus/crypto/ecc/init.lua b/sys/modules/opus/crypto/ecc/init.lua index e1c1284..980f699 100644 --- a/sys/modules/opus/crypto/ecc/init.lua +++ b/sys/modules/opus/crypto/ecc/init.lua @@ -3,6 +3,7 @@ local elliptic = require('opus.crypto.ecc.elliptic') local sha256 = require('opus.crypto.sha2') local os = _G.os +local unpack = table.unpack local q = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532} diff --git a/sys/modules/opus/crypto/sha2.lua b/sys/modules/opus/crypto/sha2.lua index 631488a..2754ee7 100644 --- a/sys/modules/opus/crypto/sha2.lua +++ b/sys/modules/opus/crypto/sha2.lua @@ -1,8 +1,8 @@ -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft -- By Anavrins +local Util = require('opus.util') local bit = _G.bit -local os = _G.os local mod32 = 2^32 local band = bit32 and bit32.band or bit.band local bnot = bit32 and bit32.bnot or bit.bnot @@ -162,25 +162,13 @@ local function hmac(data, key) return digest(padded_key) end -local function throttler() - local ts = os.clock() - local timeout = .095 - return function() - local nts = os.clock() - if nts > ts + timeout then - os.sleep(0) - ts = os.clock() - end - end -end - local function pbkdf2(pass, salt, iter, dklen) salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)} local hashlen = 32 dklen = dklen or 32 local block = 1 local out = {} - local throttle = throttler() + local throttle = Util.throttle() while dklen > 0 do local ikey = {} diff --git a/sys/modules/opus/security.lua b/sys/modules/opus/security.lua index a44605c..599f793 100644 --- a/sys/modules/opus/security.lua +++ b/sys/modules/opus/security.lua @@ -1,6 +1,6 @@ local Config = require('opus.config') -local Util = require('opus.util') local ECC = require('opus.crypto.ecc') +local Util = require('opus.util') local Security = { } @@ -21,16 +21,6 @@ local function genKey() return table.concat(key) end -function Security.generateKeyPair() - local privateKey = Util.hexToByteArray(genKey()) - return privateKey, ECC.publicKey(privateKey) -end - -function Security.getIdentifier() - return Security.getPublicKey() -end - --- deprecate - will use getIdentifier function Security.getSecretKey() local config = Config.load('os') if not config.secretKey then @@ -40,9 +30,17 @@ function Security.getSecretKey() return Util.hexToByteArray(config.secretKey) end -function Security.getPublicKey() - local secretKey = Security.getSecretKey() - return ECC.publicKey(secretKey) +function Security.getIdentifier() + local config = Config.load('os') + if config.identifier then + return config.identifier + end + -- preserve the hash the user generated + local identifier = ECC.publicKey(Security.getSecretKey()) + config.identifier = Util.byteArrayToHex(identifier) + Config.update('os', config) + + return config.identifier end function Security.updatePassword(password) diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index bb3b6ef..721f8e4 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -6,11 +6,12 @@ local Util = require('opus.util') local device = _G.device local os = _G.os +local network = _G.network local socketClass = { } function socketClass:read(timeout) - local data, distance = _G.transport.read(self) + local data, distance = network.getTransport().read(self) if data then return data, distance end @@ -25,7 +26,7 @@ function socketClass:read(timeout) local e, id = os.pullEvent() if e == 'transport_' .. self.uid then - data, distance = _G.transport.read(self) + data, distance = network.getTransport().read(self) if data then os.cancelTimer(timerId) return data, distance @@ -46,7 +47,7 @@ end function socketClass:write(data) if self.connected then - _G.transport.write(self, { + network.getTransport().write(self, { type = 'DATA', seq = self.wseq, data = data, @@ -57,30 +58,31 @@ end function socketClass:ping() if self.connected then - _G.transport.ping(self) + network.getTransport().ping(self) return true end end -function socketClass:setupEncryption() - if false then +function socketClass:setupEncryption(x) +local timer = Util.timer() self.sharedKey = ECC.exchange(self.privKey, self.remotePubKey) self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) - self.rseed = SHA.pbkdf2(self.sharedKey, "3rseed", 1) - self.wseed = SHA.pbkdf2(self.sharedKey, "4sseed", 1) - end + self.rseq = SHA.pbkdf2(self.sharedKey, x and "3rseed" or "4sseed", 1):toHex() + self.wseq = SHA.pbkdf2(self.sharedKey, x and "4sseed" or "3rseed", 1):toHex() +_syslog('shared in ' .. timer()) end function socketClass:close() if self.connected then self.transmit(self.dport, self.dhost, { type = 'DISC', + seq = self.wseq, }) self.connected = false end device.wireless_modem.close(self.sport) - _G.transport.close(self) + network.getTransport().close(self) end local Socket = { } @@ -115,27 +117,24 @@ local function newSocket(isLoopback) error('No ports available') end -function Socket.connect(host, port) +function Socket.connect(host, port, options) if not device.wireless_modem then return false, 'Wireless modem not found', 'NOMODEM' end - +local timer = Util.timer() local socket = newSocket(host == os.getComputerID()) socket.dhost = tonumber(host) - socket.privKey, socket.pubKey = Security.generateKeyPair() + socket.privKey, socket.pubKey = network.getKeyPair() + local identifier = options and options.identifier or Security.getIdentifier() socket.transmit(port, socket.sport, { type = 'OPEN', shost = socket.shost, dhost = socket.dhost, - rseq = socket.wseq, - wseq = socket.rseq, - t = Crypto.encrypt({ - ts = os.time(), - seq = socket.seq, - nts = os.epoch('utc'), + t = Crypto.encrypt({ -- this is not that much data... + ts = os.epoch('utc'), pk = Util.byteArrayToHex(socket.pubKey), - }, Security.getPublicKey()), + }, Util.hexToByteArray(identifier)), }) local timerId = os.startTimer(3) @@ -152,10 +151,11 @@ function Socket.connect(host, port) socket.dport = dport socket.connected = true socket.remotePubKey = Util.hexToByteArray(msg.pk) - socket:setupEncryption() + socket:setupEncryption(true) -- Logger.log('socket', 'connection established to %d %d->%d', -- host, socket.sport, socket.dport) - _G.transport.open(socket) + network.getTransport().open(socket) +_syslog('connection in ' .. timer()) return socket elseif msg.type == 'NOPASS' then @@ -173,35 +173,30 @@ function Socket.connect(host, port) return false, 'Connection timed out', 'TIMEOUT' end -local function trusted(socket, msg, port) - if port == 19 or msg.shost == os.getComputerID() then - -- no auth for trust server or loopback - return true +local function trusted(socket, msg, options) + local function getIdentifier() + local trustList = Util.readTable('usr/.known_hosts') or { } + return trustList[msg.shost] end - if not Security.hasPassword() then - -- no password has been set on this computer - --return true - end + local identifier = options and options.identifier or getIdentifier() - local trustList = Util.readTable('usr/.known_hosts') or { } - local pubKey = trustList[msg.shost] + if identifier and msg.t and type(msg.t) == 'table' then + local data = Crypto.decrypt(msg.t, Util.hexToByteArray(identifier)) - if pubKey and msg.t then - local data = Crypto.decrypt(msg.t, Util.hexToByteArray(pubKey)) - - if data and data.nts then -- upgraded security - if data.nts and tonumber(data.nts) and math.abs(os.epoch('utc') - data.nts) < 1024 then + if data and data.ts and tonumber(data.ts) then +_G._syslog('time diff ' .. math.abs(os.epoch('utc') - data.ts)) + if math.abs(os.epoch('utc') - data.ts) < 4096 then socket.remotePubKey = Util.hexToByteArray(data.pk) + socket.privKey, socket.pubKey = network.getKeyPair() + socket:setupEncryption() + return true end end - - --local sharedKey = modexp(pubKey, exchange.secretKey, public.primeMod) - return data and data.ts and tonumber(data.ts) and math.abs(os.time() - data.ts) < 24 end end -function Socket.server(port) +function Socket.server(port, options) device.wireless_modem.open(port) -- Logger.log('socket', 'Waiting for connections on port ' .. port) @@ -219,6 +214,7 @@ function Socket.server(port) socket.dhost = msg.shost socket.wseq = msg.wseq socket.rseq = msg.rseq + socket.options = options if not Security.hasPassword() then socket.transmit(socket.dport, socket.sport, { @@ -228,10 +224,8 @@ function Socket.server(port) }) socket:close() - elseif trusted(socket, msg, port) then + elseif trusted(socket, msg, options) then socket.connected = true - socket.privKey, socket.pubKey = Security.generateKeyPair() - socket:setupEncryption() socket.transmit(socket.dport, socket.sport, { type = 'CONN', dhost = socket.dhost, @@ -241,7 +235,7 @@ function Socket.server(port) -- Logger.log('socket', 'Connection established %d->%d', socket.sport, socket.dport) - _G.transport.open(socket) + network.getTransport().open(socket) return socket else diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 271fcfd..9e2d63d 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -20,6 +20,7 @@ function Util.hexToByteArray(str) end function Util.byteArrayToHex(tbl) + if not tbl then error('byteArrayToHex: invalid table', 2) end return ("%02x"):rep(#tbl):format(unpack(tbl)) end -- 2.49.1 From 1c291979835ab976298c3d9f41dcffc2f4a317a2 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 29 Jun 2019 22:02:00 -0400 Subject: [PATCH 026/236] security - final round? --- sys/apps/network/transport.lua | 6 ++--- sys/modules/opus/crypto/chacha20.lua | 31 ++++++++++++++++++++++++- sys/modules/opus/socket.lua | 34 ++++++++++++---------------- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/sys/apps/network/transport.lua b/sys/apps/network/transport.lua index 316f46a..3858383 100644 --- a/sys/apps/network/transport.lua +++ b/sys/apps/network/transport.lua @@ -6,7 +6,6 @@ ]]-- local Event = require('opus.event') -local SHA = require('opus.crypto.sha2') local network = _G.network local os = _G.os @@ -39,7 +38,7 @@ end function transport.write(socket, data) socket.transmit(socket.dport, socket.dhost, data) - socket.wseq = SHA.digest(socket.wseq):toHex() + socket.wseq = socket.wrng:nextInt(5) end function transport.ping(socket) @@ -113,7 +112,8 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance) _syslog('got ' .. msg.seq) else socket.activityTimer = os.clock() - socket.rseq = SHA.digest(socket.rseq):toHex() + socket.rseq = socket.rrng:nextInt(5) + table.insert(socket.messages, { msg.data, distance }) if not socket.messages[2] then -- table size is 1 diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 3b3569a..c99a854 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -10,12 +10,13 @@ local bxor = bit32.bxor local band = bit32.band local blshift = bit32.lshift local brshift = bit32.arshift -local os = _G.os local textutils = _G.textutils local mod = 2^32 local tau = {("expand 16-byte k"):byte(1,-1)} local sigma = {("expand 32-byte k"):byte(1,-1)} +local null32 = {("A"):rep(32):byte(1,-1)} +local null12 = {("A"):rep(12):byte(1,-1)} local function rotl(n, b) local s = n/(2^(32-b)) @@ -164,7 +165,35 @@ local function decrypt(data, key) return textutils.unserialise(tostring(ptx)) end +local obj = {} +local mt = {['__index'] = obj} + +function obj:nextInt(byte) + if byte < 1 or byte > 6 then error("Can only return 1-6 bytes", 2) end + local output = 0 + for i = 0, byte-1 do + if #self.block == 0 then + self.cnt = self.cnt + 1 + self.block = crypt(null32, self.seed, null12, self.cnt) + end + + local newByte = table.remove(self.block) + output = output + (newByte * (2^(8*i))) + end + return output +end + +local function newRNG(seed) + local o = {} + o.seed = seed + o.cnt = 0 + o.block = {} + + return setmetatable(o, mt) +end + return { encrypt = encrypt, decrypt = decrypt, + newRNG = newRNG, } diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 721f8e4..c1518fb 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -64,13 +64,16 @@ function socketClass:ping() end function socketClass:setupEncryption(x) -local timer = Util.timer() + self.rrng = Crypto.newRNG( + SHA.pbkdf2(self.sharedKey, x and "3rseed" or "4sseed", 1)) + self.wrng = Crypto.newRNG( + SHA.pbkdf2(self.sharedKey, x and "4sseed" or "3rseed", 1)) + self.sharedKey = ECC.exchange(self.privKey, self.remotePubKey) - self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) - self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) - self.rseq = SHA.pbkdf2(self.sharedKey, x and "3rseed" or "4sseed", 1):toHex() - self.wseq = SHA.pbkdf2(self.sharedKey, x and "4sseed" or "3rseed", 1):toHex() -_syslog('shared in ' .. timer()) + --self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) + --self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) + self.rseq = self.rrng:nextInt(5) + self.wseq = self.wrng:nextInt(5) end function socketClass:close() @@ -99,8 +102,6 @@ local function newSocket(isLoopback) shost = os.getComputerID(), sport = i, transmit = device.wireless_modem.transmit, - wseq = math.random(100, 100000), - rseq = math.random(100, 100000), timers = { }, messages = { }, } @@ -121,7 +122,7 @@ function Socket.connect(host, port, options) if not device.wireless_modem then return false, 'Wireless modem not found', 'NOMODEM' end -local timer = Util.timer() + local socket = newSocket(host == os.getComputerID()) socket.dhost = tonumber(host) socket.privKey, socket.pubKey = network.getKeyPair() @@ -143,7 +144,8 @@ local timer = Util.timer() if e == 'modem_message' and sport == socket.sport and type(msg) == 'table' and - msg.dhost == socket.shost then + msg.dhost == socket.shost and + type(msg.pk) == 'string' then os.cancelTimer(timerId) @@ -152,10 +154,7 @@ local timer = Util.timer() socket.connected = true socket.remotePubKey = Util.hexToByteArray(msg.pk) socket:setupEncryption(true) - -- Logger.log('socket', 'connection established to %d %d->%d', - -- host, socket.sport, socket.dport) network.getTransport().open(socket) -_syslog('connection in ' .. timer()) return socket elseif msg.type == 'NOPASS' then @@ -181,24 +180,23 @@ local function trusted(socket, msg, options) local identifier = options and options.identifier or getIdentifier() - if identifier and msg.t and type(msg.t) == 'table' then + if identifier and type(msg.t) == 'table' then local data = Crypto.decrypt(msg.t, Util.hexToByteArray(identifier)) if data and data.ts and tonumber(data.ts) then -_G._syslog('time diff ' .. math.abs(os.epoch('utc') - data.ts)) if math.abs(os.epoch('utc') - data.ts) < 4096 then socket.remotePubKey = Util.hexToByteArray(data.pk) socket.privKey, socket.pubKey = network.getKeyPair() socket:setupEncryption() return true end + _G._syslog('time diff ' .. math.abs(os.epoch('utc') - data.ts)) end end end function Socket.server(port, options) device.wireless_modem.open(port) - -- Logger.log('socket', 'Waiting for connections on port ' .. port) while true do local _, _, sport, dport, msg = os.pullEvent('modem_message') @@ -212,8 +210,6 @@ function Socket.server(port, options) local socket = newSocket(msg.shost == os.getComputerID()) socket.dport = dport socket.dhost = msg.shost - socket.wseq = msg.wseq - socket.rseq = msg.rseq socket.options = options if not Security.hasPassword() then @@ -233,8 +229,6 @@ function Socket.server(port, options) pk = Util.byteArrayToHex(socket.pubKey), }) - -- Logger.log('socket', 'Connection established %d->%d', socket.sport, socket.dport) - network.getTransport().open(socket) return socket -- 2.49.1 From 67779ab81409ed7054bb31461e7a3361e40d71c0 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 29 Jun 2019 23:43:21 -0400 Subject: [PATCH 027/236] secure telnet --- sys/apps/network/telnet.lua | 23 ++++++++++++++++--- sys/apps/network/transport.lua | 8 +++++-- sys/apps/telnet.lua | 7 +++--- sys/modules/opus/crypto/chacha20.lua | 6 ++--- sys/modules/opus/security.lua | 34 ++++++++-------------------- sys/modules/opus/socket.lua | 14 ++++++++---- 6 files changed, 51 insertions(+), 41 deletions(-) diff --git a/sys/apps/network/telnet.lua b/sys/apps/network/telnet.lua index dc2bda0..8bd11de 100644 --- a/sys/apps/network/telnet.lua +++ b/sys/apps/network/telnet.lua @@ -6,7 +6,7 @@ local kernel = _G.kernel local term = _G.term local window = _G.window -local function telnetHost(socket) +local function telnetHost(socket, mode) local methods = { 'clear', 'clearLine', 'setCursorPos', 'write', 'blit', 'setTextColor', 'setTextColour', 'setBackgroundColor', 'setBackgroundColour', 'scroll', 'setCursorBlink', } @@ -43,7 +43,7 @@ local function telnetHost(socket) local shellThread = kernel.run({ terminal = win, window = win, - title = 'Telnet client', + title = mode .. ' client', hidden = true, co = coroutine.create(function() Util.run(_ENV, 'sys/apps/shell.lua', table.unpack(termInfo.program)) @@ -68,6 +68,23 @@ local function telnetHost(socket) end) end +Event.addRoutine(function() + print('ssh: listening on port 22') + while true do + local socket = Socket.server(22, { ENCRYPT = true }) + + print('ssh: connection from ' .. socket.dhost) + + Event.addRoutine(function() + local s, m = pcall(telnetHost, socket, 'SSH') + if not s and m then + print('ssh error') + _G.printError(m) + end + end) + end +end) + Event.addRoutine(function() print('telnet: listening on port 23') while true do @@ -76,7 +93,7 @@ Event.addRoutine(function() print('telnet: connection from ' .. socket.dhost) Event.addRoutine(function() - local s, m = pcall(telnetHost, socket) + local s, m = pcall(telnetHost, socket, 'Telnet') if not s and m then print('Telnet error') _G.printError(m) diff --git a/sys/apps/network/transport.lua b/sys/apps/network/transport.lua index 3858383..9717e4e 100644 --- a/sys/apps/network/transport.lua +++ b/sys/apps/network/transport.lua @@ -5,7 +5,8 @@ * background read buffering ]]-- -local Event = require('opus.event') +local Crypto = require('opus.crypto.chacha20') +local Event = require('opus.event') local network = _G.network local os = _G.os @@ -32,7 +33,10 @@ end function transport.read(socket) local data = table.remove(socket.messages, 1) if data then - return unpack(data) + if socket.options.ENCRYPT then + return table.unpack(Crypto.decrypt(data[1], socket.enckey)), data[2] + end + return table.unpack(data) end end diff --git a/sys/apps/telnet.lua b/sys/apps/telnet.lua index 91b163e..ab9f024 100644 --- a/sys/apps/telnet.lua +++ b/sys/apps/telnet.lua @@ -9,7 +9,7 @@ local read = _G.read local shell = _ENV.shell local term = _G.term -local args = { ... } +local args, options = Util.parse(...) local remoteId = tonumber(table.remove(args, 1) or '') if not remoteId then @@ -22,13 +22,14 @@ if not remoteId then end if multishell then - multishell.setTitle(multishell.getCurrent(), 'Telnet ' .. remoteId) + multishell.setTitle(multishell.getCurrent(), + (options.s and 'Secure ' or 'Telnet ') .. remoteId) end local socket, msg, reason while true do - socket, msg, reason = Socket.connect(remoteId, 23) + socket, msg, reason = Socket.connect(remoteId, options.s and 22 or 23) if socket then break diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index c99a854..f28f8dc 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -166,10 +166,10 @@ local function decrypt(data, key) end local obj = {} -local mt = {['__index'] = obj} +local rng_mt = {['__index'] = obj} function obj:nextInt(byte) - if byte < 1 or byte > 6 then error("Can only return 1-6 bytes", 2) end + if not byte or byte < 1 or byte > 6 then error("Can only return 1-6 bytes", 2) end local output = 0 for i = 0, byte-1 do if #self.block == 0 then @@ -189,7 +189,7 @@ local function newRNG(seed) o.cnt = 0 o.block = {} - return setmetatable(o, mt) + return setmetatable(o, rng_mt) end return { diff --git a/sys/modules/opus/security.lua b/sys/modules/opus/security.lua index 599f793..4fb6f8d 100644 --- a/sys/modules/opus/security.lua +++ b/sys/modules/opus/security.lua @@ -1,6 +1,4 @@ local Config = require('opus.config') -local ECC = require('opus.crypto.ecc') -local Util = require('opus.util') local Security = { } @@ -13,32 +11,18 @@ function Security.hasPassword() return not not Security.getPassword() end -local function genKey() - local key = { } - for _ = 1, 32 do - table.insert(key, ("%02x"):format(math.random(0, 0xFF))) - end - return table.concat(key) -end - -function Security.getSecretKey() - local config = Config.load('os') - if not config.secretKey then - config.secretKey = genKey() - Config.update('os', config) - end - return Util.hexToByteArray(config.secretKey) -end - function Security.getIdentifier() local config = Config.load('os') - if config.identifier then - return config.identifier + + if not config.identifier then + local key = { } + for _ = 1, 32 do + table.insert(key, ("%02x"):format(math.random(0, 0xFF))) + end + config.identifier = table.concat(key) + + Config.update('os', config) end - -- preserve the hash the user generated - local identifier = ECC.publicKey(Security.getSecretKey()) - config.identifier = Util.byteArrayToHex(identifier) - Config.update('os', config) return config.identifier end diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index c1518fb..9f77f99 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -47,6 +47,9 @@ end function socketClass:write(data) if self.connected then + if self.options.ENCRYPT then + data = Crypto.encrypt({ data }, self.enckey) + end network.getTransport().write(self, { type = 'DATA', seq = self.wseq, @@ -70,7 +73,7 @@ function socketClass:setupEncryption(x) SHA.pbkdf2(self.sharedKey, x and "4sseed" or "3rseed", 1)) self.sharedKey = ECC.exchange(self.privKey, self.remotePubKey) - --self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) + self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) --self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) self.rseq = self.rrng:nextInt(5) self.wseq = self.wrng:nextInt(5) @@ -144,15 +147,15 @@ function Socket.connect(host, port, options) if e == 'modem_message' and sport == socket.sport and type(msg) == 'table' and - msg.dhost == socket.shost and - type(msg.pk) == 'string' then + msg.dhost == socket.shost then os.cancelTimer(timerId) - if msg.type == 'CONN' then + if msg.type == 'CONN' and type(msg.pk) == 'string' then socket.dport = dport socket.connected = true socket.remotePubKey = Util.hexToByteArray(msg.pk) + socket.options = msg.options or { } socket:setupEncryption(true) network.getTransport().open(socket) return socket @@ -210,7 +213,7 @@ function Socket.server(port, options) local socket = newSocket(msg.shost == os.getComputerID()) socket.dport = dport socket.dhost = msg.shost - socket.options = options + socket.options = options or { } if not Security.hasPassword() then socket.transmit(socket.dport, socket.sport, { @@ -227,6 +230,7 @@ function Socket.server(port, options) dhost = socket.dhost, shost = socket.shost, pk = Util.byteArrayToHex(socket.pubKey), + options = socket.options.ENCRYPT and { ENCRYPT = true }, }) network.getTransport().open(socket) -- 2.49.1 From 721cd840b36bc6f8d60736b4975e23717c3532eb Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 30 Jun 2019 14:47:45 -0400 Subject: [PATCH 028/236] encrypt improvements --- sys/apps/network/transport.lua | 27 +++- sys/modules/opus/crypto/chacha20.lua | 13 +- sys/modules/opus/crypto/lualzw.lua | 164 +++++++++++++++++++++++++ sys/modules/opus/crypto/serializer.lua | 50 ++++++++ sys/modules/opus/socket.lua | 33 +++-- sys/modules/opus/util.lua | 2 +- 6 files changed, 262 insertions(+), 27 deletions(-) create mode 100644 sys/modules/opus/crypto/lualzw.lua create mode 100644 sys/modules/opus/crypto/serializer.lua diff --git a/sys/apps/network/transport.lua b/sys/apps/network/transport.lua index 9717e4e..2732d12 100644 --- a/sys/apps/network/transport.lua +++ b/sys/apps/network/transport.lua @@ -15,6 +15,7 @@ local computerId = os.getComputerID() local transport = { timers = { }, sockets = { }, + encryptQueue = { }, UID = 0, } @@ -40,8 +41,15 @@ function transport.read(socket) end end -function transport.write(socket, data) - socket.transmit(socket.dport, socket.dhost, data) +function transport.write(socket, msg) + if socket.options.ENCRYPT then + if #transport.encryptQueue == 0 then + os.queueEvent('transport_encrypt') + end + table.insert(transport.encryptQueue, { socket.sport, msg }) + else + socket.transmit(socket.dport, socket.dhost, msg) + end socket.wseq = socket.wrng:nextInt(5) end @@ -63,6 +71,19 @@ function transport.close(socket) transport.sockets[socket.sport] = nil end +Event.on('transport_encrypt', function() + while #transport.encryptQueue > 0 do + local entry = table.remove(transport.encryptQueue, 1) + local socket = transport.sockets[entry[1]] + + if socket and socket.connected then + local msg = entry[2] + msg.data = Crypto.encrypt({ msg.data }, socket.enckey) + socket.transmit(socket.dport, socket.dhost, msg) + end + end +end) + Event.on('timer', function(_, timerId) local socket = transport.timers[timerId] @@ -110,7 +131,7 @@ Event.on('modem_message', function(_, _, dport, dhost, msg, distance) elseif msg.type == 'DATA' and msg.data then if msg.seq ~= socket.rseq then - print('transport seq error - closing socket ' .. socket.sport) + print('transport seq error ' .. socket.sport) _syslog(msg.data) _syslog('expected ' .. socket.rseq) _syslog('got ' .. msg.seq) diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index f28f8dc..c82763f 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -1,7 +1,9 @@ -- Chacha20 cipher in ComputerCraft -- By Anavrins +local LZW = require('opus.crypto.lualzw') local sha2 = require('opus.crypto.sha2') +local Serializer = require('opus.crypto.serializer') local Util = require('opus.util') local ROUNDS = 20 -- Adjust this for speed tradeoff @@ -116,7 +118,7 @@ local function crypt(data, key, nonce, cntr, round) cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 - local throttle = Util.throttle() + local throttle = Util.throttle(function() _syslog('throttle') end) local out = {} local state = initState(key, nonce, cntr) local blockAmt = math.floor(#data/64) @@ -132,11 +134,11 @@ local function crypt(data, key, nonce, cntr, round) out[#out+1] = bxor(block[j], ks[j]) end - if i % 1000 == 0 then + --if i % 1000 == 0 then throttle() --os.queueEvent("") --os.pullEvent("") - end + --end end return setmetatable(out, mt) end @@ -151,7 +153,8 @@ end local function encrypt(data, key) local nonce = genNonce(12) - data = textutils.serialise(data) + data = Serializer.serialize(data) + data = LZW.compress(data) key = sha2.digest(key) local ctx = crypt(data, key, nonce, 1, ROUNDS) return { nonce:toHex(), ctx:toHex() } @@ -162,7 +165,7 @@ local function decrypt(data, key) data = Util.hexToByteArray(data[2]) key = sha2.digest(key) local ptx = crypt(data, key, nonce, 1, ROUNDS) - return textutils.unserialise(tostring(ptx)) + return textutils.unserialise(LZW.decompress(tostring(ptx))) end local obj = {} diff --git a/sys/modules/opus/crypto/lualzw.lua b/sys/modules/opus/crypto/lualzw.lua new file mode 100644 index 0000000..7bdfa21 --- /dev/null +++ b/sys/modules/opus/crypto/lualzw.lua @@ -0,0 +1,164 @@ +-- see: https://github.com/Rochet2/lualzw +--[[ +MIT License + +Copyright (c) 2016 Rochet2 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] + +local char = string.char +local type = type +local sub = string.sub +local tconcat = table.concat + +local basedictcompress = {} +local basedictdecompress = {} +for i = 0, 255 do + local ic, iic = char(i), char(i, 0) + basedictcompress[ic] = iic + basedictdecompress[iic] = ic +end + +local function dictAddA(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[str] = char(a,b) + a = a+1 + return dict, a, b +end + +local function compress(input) + if type(input) ~= "string" then + error ("string expected, got "..type(input)) + end + local len = #input + if len <= 1 then + return "u"..input + end + + local dict = {} + local a, b = 0, 1 + + local result = {"c"} + local resultlen = 1 + local n = 2 + local word = "" + for i = 1, len do + local c = sub(input, i, i) + local wc = word..c + if not (basedictcompress[wc] or dict[wc]) then + local write = basedictcompress[word] or dict[word] + if not write then + error "algorithm error, could not fetch word" + end + result[n] = write + resultlen = resultlen + #write + n = n+1 + if len <= resultlen then + return "u"..input + end + dict, a, b = dictAddA(wc, dict, a, b) + word = c + else + word = wc + end + end + result[n] = basedictcompress[word] or dict[word] + resultlen = resultlen+#result[n] + if len <= resultlen then + return "u"..input + end + return tconcat(result) +end + +local function dictAddB(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[char(a,b)] = str + a = a+1 + return dict, a, b +end + +local function decompress(input) + if type(input) ~= "string" then + error( "string expected, got "..type(input)) + end + + if #input < 1 then + error("invalid input - not a compressed string") + end + + local control = sub(input, 1, 1) + if control == "u" then + return sub(input, 2) + elseif control ~= "c" then + error( "invalid input - not a compressed string") + end + input = sub(input, 2) + local len = #input + + if len < 2 then + error( "invalid input - not a compressed string") + end + + local dict = {} + local a, b = 0, 1 + + local result = {} + local n = 1 + local last = sub(input, 1, 2) + result[n] = basedictdecompress[last] or dict[last] + n = n+1 + for i = 3, len, 2 do + local code = sub(input, i, i+1) + local lastStr = basedictdecompress[last] or dict[last] + if not lastStr then + error( "could not find last from dict. Invalid input?") + end + local toAdd = basedictdecompress[code] or dict[code] + if toAdd then + result[n] = toAdd + n = n+1 + dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b) + else + local tmp = lastStr..sub(lastStr, 1, 1) + result[n] = tmp + n = n+1 + dict, a, b = dictAddB(tmp, dict, a, b) + end + last = code + end + return tconcat(result) +end + +return { + compress = compress, + decompress = decompress, +} \ No newline at end of file diff --git a/sys/modules/opus/crypto/serializer.lua b/sys/modules/opus/crypto/serializer.lua new file mode 100644 index 0000000..db19336 --- /dev/null +++ b/sys/modules/opus/crypto/serializer.lua @@ -0,0 +1,50 @@ +local Serializer = { } + +local insert = table.insert +local format = string.format + +function Serializer.serialize(tbl) + local output = { } + + local function recurse(t) + local sType = type(t) + if sType == 'table' then + if next(t) == nil then + insert(output, '{}') + else + insert(output, '{') + local tSeen = {} + for k, v in ipairs(t) do + tSeen[k] = true + recurse(v) + insert(output, ',') + end + for k, v in pairs(t) do + if not tSeen[k] then + if type(k) == 'string' and string.match(k, '^[%a_][%a%d_]*$') then + insert(output, k .. '=') + recurse(v) + insert(output, ',') + else + insert(output, '[') + recurse(k) + insert(output, ']=') + recurse(v) + insert(output, ',') + end + end + end + insert(output, '}') + end + elseif sType == 'string' then + insert(output, format('%q', t)) + else + insert(output, tostring(t)) + end + end + + recurse(tbl) + return table.concat(output) +end + +return Serializer diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 9f77f99..3eadac7 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -47,9 +47,6 @@ end function socketClass:write(data) if self.connected then - if self.options.ENCRYPT then - data = Crypto.encrypt({ data }, self.enckey) - end network.getTransport().write(self, { type = 'DATA', seq = self.wseq, @@ -66,19 +63,6 @@ function socketClass:ping() end end -function socketClass:setupEncryption(x) - self.rrng = Crypto.newRNG( - SHA.pbkdf2(self.sharedKey, x and "3rseed" or "4sseed", 1)) - self.wrng = Crypto.newRNG( - SHA.pbkdf2(self.sharedKey, x and "4sseed" or "3rseed", 1)) - - self.sharedKey = ECC.exchange(self.privKey, self.remotePubKey) - self.enckey = SHA.pbkdf2(self.sharedKey, "1enc", 1) - --self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) - self.rseq = self.rrng:nextInt(5) - self.wseq = self.wrng:nextInt(5) -end - function socketClass:close() if self.connected then self.transmit(self.dport, self.dhost, { @@ -121,6 +105,19 @@ local function newSocket(isLoopback) error('No ports available') end +local function setupCrypto(socket, isClient) + socket.rrng = Crypto.newRNG( + SHA.pbkdf2(socket.sharedKey, isClient and "3rseed" or "4sseed", 1)) + socket.wrng = Crypto.newRNG( + SHA.pbkdf2(socket.sharedKey, isClient and "4sseed" or "3rseed", 1)) + + socket.sharedKey = ECC.exchange(socket.privKey, socket.remotePubKey) + socket.enckey = SHA.pbkdf2(socket.sharedKey, "1enc", 1) + --self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) + socket.rseq = socket.rrng:nextInt(5) + socket.wseq = socket.wrng:nextInt(5) +end + function Socket.connect(host, port, options) if not device.wireless_modem then return false, 'Wireless modem not found', 'NOMODEM' @@ -156,7 +153,7 @@ function Socket.connect(host, port, options) socket.connected = true socket.remotePubKey = Util.hexToByteArray(msg.pk) socket.options = msg.options or { } - socket:setupEncryption(true) + setupCrypto(socket, true) network.getTransport().open(socket) return socket @@ -190,7 +187,7 @@ local function trusted(socket, msg, options) if math.abs(os.epoch('utc') - data.ts) < 4096 then socket.remotePubKey = Util.hexToByteArray(data.pk) socket.privKey, socket.pubKey = network.getKeyPair() - socket:setupEncryption() + setupCrypto(socket) return true end _G._syslog('time diff ' .. math.abs(os.epoch('utc') - data.ts)) diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 9e2d63d..e670857 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -56,7 +56,7 @@ Util.Timer = Util.timer -- deprecate function Util.throttle(fn) local ts = os.clock() - local timeout = .095 + local timeout = .295 return function(...) local nts = os.clock() if nts > ts + timeout then -- 2.49.1 From 86e918667cfdcbc5ee062307a773673e1b6a18d8 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 30 Jun 2019 17:20:55 -0400 Subject: [PATCH 029/236] encrypt optimization --- sys/modules/opus/crypto/cbor.lua | 581 +++++++++++++++++++++++++++ sys/modules/opus/crypto/chacha20.lua | 2 +- 2 files changed, 582 insertions(+), 1 deletion(-) create mode 100644 sys/modules/opus/crypto/cbor.lua diff --git a/sys/modules/opus/crypto/cbor.lua b/sys/modules/opus/crypto/cbor.lua new file mode 100644 index 0000000..a55df44 --- /dev/null +++ b/sys/modules/opus/crypto/cbor.lua @@ -0,0 +1,581 @@ +-- Concise Binary Object Representation (CBOR) +-- RFC 7049 + +local function softreq(pkg, field) + local ok, mod = pcall(require, pkg); + if not ok then return end + if field then return mod[field]; end + return mod; +end +local dostring = function (s) + local ok, f = pcall(loadstring or load, s); -- luacheck: read globals loadstring + if ok and f then return f(); end +end + +local setmetatable = setmetatable; +local getmetatable = getmetatable; +local dbg_getmetatable = debug.getmetatable; +local assert = assert; +local error = error; +local type = type; +local pairs = pairs; +local ipairs = ipairs; +local tostring = tostring; +local s_char = string.char; +local t_concat = table.concat; +local t_sort = table.sort; +local m_floor = math.floor; +local m_abs = math.abs; +local m_huge = math.huge; +local m_max = math.max; +local maxint = math.maxinteger or 9007199254740992; +local minint = math.mininteger or -9007199254740992; +local NaN = 0/0; +local m_frexp = math.frexp; +local m_ldexp = math.ldexp or function (x, exp) return x * 2.0 ^ exp; end; +local m_type = math.type or function (n) return n % 1 == 0 and n <= maxint and n >= minint and "integer" or "float" end; +local s_pack = string.pack or softreq("struct", "pack"); +local s_unpack = string.unpack or softreq("struct", "unpack"); +local b_rshift = softreq("bit32", "rshift") or softreq("bit", "rshift") or + dostring "return function(a,b) return a >> b end" or + function (a, b) return m_max(0, m_floor(a / (2 ^ b))); end; + +-- sanity check +if s_pack and s_pack(">I2", 0) ~= "\0\0" then + s_pack = nil; +end +if s_unpack and s_unpack(">I2", "\1\2\3\4") ~= 0x102 then + s_unpack = nil; +end + +local _ENV = nil; -- luacheck: ignore 211 + +local encoder = {}; + +local function encode(obj, opts) + return encoder[type(obj)](obj, opts); +end + +-- Major types 0, 1 and length encoding for others +local function integer(num, m) + if m == 0 and num < 0 then + -- negative integer, major type 1 + num, m = - num - 1, 32; + end + if num < 24 then + return s_char(m + num); + elseif num < 2 ^ 8 then + return s_char(m + 24, num); + elseif num < 2 ^ 16 then + return s_char(m + 25, b_rshift(num, 8), num % 0x100); + elseif num < 2 ^ 32 then + return s_char(m + 26, + b_rshift(num, 24) % 0x100, + b_rshift(num, 16) % 0x100, + b_rshift(num, 8) % 0x100, + num % 0x100); + elseif num < 2 ^ 64 then + local high = m_floor(num / 2 ^ 32); + num = num % 2 ^ 32; + return s_char(m + 27, + b_rshift(high, 24) % 0x100, + b_rshift(high, 16) % 0x100, + b_rshift(high, 8) % 0x100, + high % 0x100, + b_rshift(num, 24) % 0x100, + b_rshift(num, 16) % 0x100, + b_rshift(num, 8) % 0x100, + num % 0x100); + end + error "int too large"; +end + +if s_pack then + function integer(num, m) + local fmt; + m = m or 0; + if num < 24 then + fmt, m = ">B", m + num; + elseif num < 256 then + fmt, m = ">BB", m + 24; + elseif num < 65536 then + fmt, m = ">BI2", m + 25; + elseif num < 4294967296 then + fmt, m = ">BI4", m + 26; + else + fmt, m = ">BI8", m + 27; + end + return s_pack(fmt, m, num); + end +end + +local simple_mt = {}; +function simple_mt:__tostring() return self.name or ("simple(%d)"):format(self.value); end +function simple_mt:__tocbor() return self.cbor or integer(self.value, 224); end + +local function simple(value, name, cbor) + assert(value >= 0 and value <= 255, "bad argument #1 to 'simple' (integer in range 0..255 expected)"); + return setmetatable({ value = value, name = name, cbor = cbor }, simple_mt); +end + +local tagged_mt = {}; +function tagged_mt:__tostring() return ("%d(%s)"):format(self.tag, tostring(self.value)); end +function tagged_mt:__tocbor() return integer(self.tag, 192) .. encode(self.value); end + +local function tagged(tag, value) + assert(tag >= 0, "bad argument #1 to 'tagged' (positive integer expected)"); + return setmetatable({ tag = tag, value = value }, tagged_mt); +end + +local null = simple(22, "null"); -- explicit null +local undefined = simple(23, "undefined"); -- undefined or nil +local BREAK = simple(31, "break", "\255"); + +-- Number types dispatch +function encoder.number(num) + return encoder[m_type(num)](num); +end + +-- Major types 0, 1 +function encoder.integer(num) + if num < 0 then + return integer(-1 - num, 32); + end + return integer(num, 0); +end + +-- Major type 7 +function encoder.float(num) + if num ~= num then -- NaN shortcut + return "\251\127\255\255\255\255\255\255\255"; + end + local sign = (num > 0 or 1 / num > 0) and 0 or 1; + num = m_abs(num) + if num == m_huge then + return s_char(251, sign * 128 + 128 - 1) .. "\240\0\0\0\0\0\0"; + end + local fraction, exponent = m_frexp(num) + if fraction == 0 then + return s_char(251, sign * 128) .. "\0\0\0\0\0\0\0"; + end + fraction = fraction * 2; + exponent = exponent + 1024 - 2; + if exponent <= 0 then + fraction = fraction * 2 ^ (exponent - 1) + exponent = 0; + else + fraction = fraction - 1; + end + return s_char(251, + sign * 2 ^ 7 + m_floor(exponent / 2 ^ 4) % 2 ^ 7, + exponent % 2 ^ 4 * 2 ^ 4 + + m_floor(fraction * 2 ^ 4 % 0x100), + m_floor(fraction * 2 ^ 12 % 0x100), + m_floor(fraction * 2 ^ 20 % 0x100), + m_floor(fraction * 2 ^ 28 % 0x100), + m_floor(fraction * 2 ^ 36 % 0x100), + m_floor(fraction * 2 ^ 44 % 0x100), + m_floor(fraction * 2 ^ 52 % 0x100) + ) +end + +if s_pack then + function encoder.float(num) + return s_pack(">Bd", 251, num); + end +end + + +-- Major type 2 - byte strings +function encoder.bytestring(s) + return integer(#s, 64) .. s; +end + +-- Major type 3 - UTF-8 strings +function encoder.utf8string(s) + return integer(#s, 96) .. s; +end + +-- Lua strings are byte strings +encoder.string = encoder.bytestring; + +function encoder.boolean(bool) + return bool and "\245" or "\244"; +end + +encoder["nil"] = function() return "\246"; end + +function encoder.userdata(ud, opts) + local mt = dbg_getmetatable(ud); + if mt then + local encode_ud = opts and opts[mt] or mt.__tocbor; + if encode_ud then + return encode_ud(ud, opts); + end + end + error "can't encode userdata"; +end + +function encoder.table(t, opts) + local mt = getmetatable(t); + if mt then + local encode_t = opts and opts[mt] or mt.__tocbor; + if encode_t then + return encode_t(t, opts); + end + end + -- the table is encoded as an array iff when we iterate over it, + -- we see successive integer keys starting from 1. The lua + -- language doesn't actually guarantee that this will be the case + -- when we iterate over a table with successive integer keys, but + -- due an implementation detail in PUC Rio Lua, this is what we + -- usually observe. See the Lua manual regarding the # (length) + -- operator. In the case that this does not happen, we will fall + -- back to a map with integer keys, which becomes a bit larger. + local array, map, i, p = { integer(#t, 128) }, { "\191" }, 1, 2; + local is_array = true; + for k, v in pairs(t) do + is_array = is_array and i == k; + i = i + 1; + + local encoded_v = encode(v, opts); + array[i] = encoded_v; + + map[p], p = encode(k, opts), p + 1; + map[p], p = encoded_v, p + 1; + end + -- map[p] = "\255"; + map[1] = integer(i - 1, 160); + return t_concat(is_array and array or map); +end + +-- Array or dict-only encoders, which can be set as __tocbor metamethod +function encoder.array(t, opts) + local array = { }; + for i, v in ipairs(t) do + array[i] = encode(v, opts); + end + return integer(#array, 128) .. t_concat(array); +end + +function encoder.map(t, opts) + local map, p, len = { "\191" }, 2, 0; + for k, v in pairs(t) do + map[p], p = encode(k, opts), p + 1; + map[p], p = encode(v, opts), p + 1; + len = len + 1; + end + -- map[p] = "\255"; + map[1] = integer(len, 160); + return t_concat(map); +end +encoder.dict = encoder.map; -- COMPAT + +function encoder.ordered_map(t, opts) + local map = {}; + if not t[1] then -- no predefined order + local i = 0; + for k in pairs(t) do + i = i + 1; + map[i] = k; + end + t_sort(map); + end + for i, k in ipairs(t[1] and t or map) do + map[i] = encode(k, opts) .. encode(t[k], opts); + end + return integer(#map, 160) .. t_concat(map); +end + +encoder["function"] = function () + error "can't encode function"; +end + +-- Decoder +-- Reads from a file-handle like object +local function read_bytes(fh, len) + return fh:read(len); +end + +local function read_byte(fh) + return fh:read(1):byte(); +end + +local function read_length(fh, mintyp) + if mintyp < 24 then + return mintyp; + elseif mintyp < 28 then + local out = 0; + for _ = 1, 2 ^ (mintyp - 24) do + out = out * 256 + read_byte(fh); + end + return out; + else + error "invalid length"; + end +end + +local decoder = {}; + +local function read_type(fh) + local byte = read_byte(fh); + return b_rshift(byte, 5), byte % 32; +end + +local function read_object(fh, opts) + local typ, mintyp = read_type(fh); + return decoder[typ](fh, mintyp, opts); +end + +local function read_integer(fh, mintyp) + return read_length(fh, mintyp); +end + +local function read_negative_integer(fh, mintyp) + return -1 - read_length(fh, mintyp); +end + +local function read_string(fh, mintyp) + if mintyp ~= 31 then + return read_bytes(fh, read_length(fh, mintyp)); + end + local out = {}; + local i = 1; + local v = read_object(fh); + while v ~= BREAK do + out[i], i = v, i + 1; + v = read_object(fh); + end + return t_concat(out); +end + +local function read_unicode_string(fh, mintyp) + return read_string(fh, mintyp); + -- local str = read_string(fh, mintyp); + -- if have_utf8 and not utf8.len(str) then + -- TODO How to handle this? + -- end + -- return str; +end + +local function read_array(fh, mintyp, opts) + local out = {}; + if mintyp == 31 then + local i = 1; + local v = read_object(fh, opts); + while v ~= BREAK do + out[i], i = v, i + 1; + v = read_object(fh, opts); + end + else + local len = read_length(fh, mintyp); + for i = 1, len do + out[i] = read_object(fh, opts); + end + end + return out; +end + +local function read_map(fh, mintyp, opts) + local out = {}; + local k; + if mintyp == 31 then + local i = 1; + k = read_object(fh, opts); + while k ~= BREAK do + out[k], i = read_object(fh, opts), i + 1; + k = read_object(fh, opts); + end + else + local len = read_length(fh, mintyp); + for _ = 1, len do + k = read_object(fh, opts); + out[k] = read_object(fh, opts); + end + end + return out; +end + +local tagged_decoders = {}; + +local function read_semantic(fh, mintyp, opts) + local tag = read_length(fh, mintyp); + local value = read_object(fh, opts); + local postproc = opts and opts[tag] or tagged_decoders[tag]; + if postproc then + return postproc(value); + end + return tagged(tag, value); +end + +local function read_half_float(fh) + local exponent = read_byte(fh); + local fraction = read_byte(fh); + local sign = exponent < 128 and 1 or -1; -- sign is highest bit + + fraction = fraction + (exponent * 256) % 1024; -- copy two(?) bits from exponent to fraction + exponent = b_rshift(exponent, 2) % 32; -- remove sign bit and two low bits from fraction; + + if exponent == 0 then + return sign * m_ldexp(fraction, -24); + elseif exponent ~= 31 then + return sign * m_ldexp(fraction + 1024, exponent - 25); + elseif fraction == 0 then + return sign * m_huge; + else + return NaN; + end +end + +local function read_float(fh) + local exponent = read_byte(fh); + local fraction = read_byte(fh); + local sign = exponent < 128 and 1 or -1; -- sign is highest bit + exponent = exponent * 2 % 256 + b_rshift(fraction, 7); + fraction = fraction % 128; + fraction = fraction * 256 + read_byte(fh); + fraction = fraction * 256 + read_byte(fh); + + if exponent == 0 then + return sign * m_ldexp(exponent, -149); + elseif exponent ~= 0xff then + return sign * m_ldexp(fraction + 2 ^ 23, exponent - 150); + elseif fraction == 0 then + return sign * m_huge; + else + return NaN; + end +end + +local function read_double(fh) + local exponent = read_byte(fh); + local fraction = read_byte(fh); + local sign = exponent < 128 and 1 or -1; -- sign is highest bit + + exponent = exponent % 128 * 16 + b_rshift(fraction, 4); + fraction = fraction % 16; + fraction = fraction * 256 + read_byte(fh); + fraction = fraction * 256 + read_byte(fh); + fraction = fraction * 256 + read_byte(fh); + fraction = fraction * 256 + read_byte(fh); + fraction = fraction * 256 + read_byte(fh); + fraction = fraction * 256 + read_byte(fh); + + if exponent == 0 then + return sign * m_ldexp(exponent, -149); + elseif exponent ~= 0xff then + return sign * m_ldexp(fraction + 2 ^ 52, exponent - 1075); + elseif fraction == 0 then + return sign * m_huge; + else + return NaN; + end +end + + +if s_unpack then + function read_float(fh) return s_unpack(">f", read_bytes(fh, 4)) end + function read_double(fh) return s_unpack(">d", read_bytes(fh, 8)) end +end + +local function read_simple(fh, value, opts) + if value == 24 then + value = read_byte(fh); + end + if value == 20 then + return false; + elseif value == 21 then + return true; + elseif value == 22 then + return null; + elseif value == 23 then + return undefined; + elseif value == 25 then + return read_half_float(fh); + elseif value == 26 then + return read_float(fh); + elseif value == 27 then + return read_double(fh); + elseif value == 31 then + return BREAK; + end + if opts and opts.simple then + return opts.simple(value); + end + return simple(value); +end + +decoder[0] = read_integer; +decoder[1] = read_negative_integer; +decoder[2] = read_string; +decoder[3] = read_unicode_string; +decoder[4] = read_array; +decoder[5] = read_map; +decoder[6] = read_semantic; +decoder[7] = read_simple; + +-- opts.more(n) -> want more data +-- opts.simple -> decode simple value +-- opts[int] -> tagged decoder +local function decode(s, opts) + local fh = {}; + local pos = 1; + + local more; + if type(opts) == "function" then + more = opts; + elseif type(opts) == "table" then + more = opts.more; + elseif opts ~= nil then + error(("bad argument #2 to 'decode' (function or table expected, got %s)"):format(type(opts))); + end + if type(more) ~= "function" then + function more() + error "input too short"; + end + end + + function fh:read(bytes) + local ret = s:sub(pos, pos + bytes - 1); + if #ret < bytes then + ret = more(bytes - #ret, fh, opts); + if ret then self:write(ret); end + return self:read(bytes); + end + pos = pos + bytes; + return ret; + end + + function fh:write(bytes) -- luacheck: no self + s = s .. bytes; + if pos > 256 then + s = s:sub(pos + 1); + pos = 1; + end + return #bytes; + end + + return read_object(fh, opts); +end + +return { + -- en-/decoder functions + encode = encode; + decode = decode; + decode_file = read_object; + + -- tables of per-type en-/decoders + type_encoders = encoder; + type_decoders = decoder; + + -- special treatment for tagged values + tagged_decoders = tagged_decoders; + + -- constructors for annotated types + simple = simple; + tagged = tagged; + + -- pre-defined simple values + null = null; + undefined = undefined; +} diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index c82763f..ba1afa7 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -6,7 +6,7 @@ local sha2 = require('opus.crypto.sha2') local Serializer = require('opus.crypto.serializer') local Util = require('opus.util') -local ROUNDS = 20 -- Adjust this for speed tradeoff +local ROUNDS = 8 -- Adjust this for speed tradeoff local bxor = bit32.bxor local band = bit32.band -- 2.49.1 From 159dc622fd175e608289683211b0bc98046cda90 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 30 Jun 2019 17:40:57 -0400 Subject: [PATCH 030/236] oops --- sys/modules/opus/socket.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 3eadac7..d56ea38 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -106,14 +106,15 @@ local function newSocket(isLoopback) end local function setupCrypto(socket, isClient) + socket.sharedKey = ECC.exchange(socket.privKey, socket.remotePubKey) + socket.enckey = SHA.pbkdf2(socket.sharedKey, "1enc", 1) + --self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) + socket.rrng = Crypto.newRNG( SHA.pbkdf2(socket.sharedKey, isClient and "3rseed" or "4sseed", 1)) socket.wrng = Crypto.newRNG( SHA.pbkdf2(socket.sharedKey, isClient and "4sseed" or "3rseed", 1)) - socket.sharedKey = ECC.exchange(socket.privKey, socket.remotePubKey) - socket.enckey = SHA.pbkdf2(socket.sharedKey, "1enc", 1) - --self.hmackey = SHA.pbkdf2(self.sharedKey, "2hmac", 1) socket.rseq = socket.rrng:nextInt(5) socket.wseq = socket.wrng:nextInt(5) end -- 2.49.1 From 61a26d7c55bdbf706a2dd4b2e79dde90f0950132 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 30 Jun 2019 19:53:26 -0400 Subject: [PATCH 031/236] encryption perf --- sys/apps/network/vnc.lua | 20 +++ sys/apps/vnc.lua | 7 +- sys/modules/opus/{crypto => }/cbor.lua | 0 sys/modules/opus/crypto/chacha20.lua | 9 +- sys/modules/opus/crypto/lualzw.lua | 164 ------------------------- sys/modules/opus/crypto/serializer.lua | 50 -------- 6 files changed, 28 insertions(+), 222 deletions(-) rename sys/modules/opus/{crypto => }/cbor.lua (100%) delete mode 100644 sys/modules/opus/crypto/lualzw.lua delete mode 100644 sys/modules/opus/crypto/serializer.lua diff --git a/sys/apps/network/vnc.lua b/sys/apps/network/vnc.lua index 54ea8fc..c833ead 100644 --- a/sys/apps/network/vnc.lua +++ b/sys/apps/network/vnc.lua @@ -71,3 +71,23 @@ Event.addRoutine(function() end end end) + +Event.addRoutine(function() + + print('svnc: listening on port 5901') + + while true do + local socket = Socket.server(5901, { ENCRYPT = true }) + + print('svnc: connection from ' .. socket.dhost) + + -- no new process - only 1 connection allowed + -- due to term size issues + local s, m = pcall(vncHost, socket) + socket:close() + if not s and m then + print('vnc error') + _G.printError(m) + end + end +end) diff --git a/sys/apps/vnc.lua b/sys/apps/vnc.lua index cb2d945..0265254 100644 --- a/sys/apps/vnc.lua +++ b/sys/apps/vnc.lua @@ -10,7 +10,7 @@ local shell = _ENV.shell local term = _G.term local remoteId -local args = { ... } +local args, options = Util.parse(...) if #args == 1 then remoteId = tonumber(args[1]) else @@ -23,11 +23,12 @@ if not remoteId then end if multishell then - multishell.setTitle(multishell.getCurrent(), 'VNC-' .. remoteId) + multishell.setTitle(multishell.getCurrent(), + (options.s and 'SVNC-' or 'VNC-') .. remoteId) end local function connect() - local socket, msg, reason = Socket.connect(remoteId, 5900) + local socket, msg, reason = Socket.connect(remoteId, options.s and 5901 or 5900) if reason == 'NOTRUST' then local s, m = shell.run('trust ' .. remoteId) diff --git a/sys/modules/opus/crypto/cbor.lua b/sys/modules/opus/cbor.lua similarity index 100% rename from sys/modules/opus/crypto/cbor.lua rename to sys/modules/opus/cbor.lua diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index ba1afa7..89cf0ac 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -1,9 +1,8 @@ -- Chacha20 cipher in ComputerCraft -- By Anavrins -local LZW = require('opus.crypto.lualzw') +local cbor = require('opus.cbor') local sha2 = require('opus.crypto.sha2') -local Serializer = require('opus.crypto.serializer') local Util = require('opus.util') local ROUNDS = 8 -- Adjust this for speed tradeoff @@ -153,10 +152,10 @@ end local function encrypt(data, key) local nonce = genNonce(12) - data = Serializer.serialize(data) - data = LZW.compress(data) + data = cbor.encode(data) key = sha2.digest(key) local ctx = crypt(data, key, nonce, 1, ROUNDS) + return { nonce:toHex(), ctx:toHex() } end @@ -165,7 +164,7 @@ local function decrypt(data, key) data = Util.hexToByteArray(data[2]) key = sha2.digest(key) local ptx = crypt(data, key, nonce, 1, ROUNDS) - return textutils.unserialise(LZW.decompress(tostring(ptx))) + return cbor.decode(tostring(ptx)) end local obj = {} diff --git a/sys/modules/opus/crypto/lualzw.lua b/sys/modules/opus/crypto/lualzw.lua deleted file mode 100644 index 7bdfa21..0000000 --- a/sys/modules/opus/crypto/lualzw.lua +++ /dev/null @@ -1,164 +0,0 @@ --- see: https://github.com/Rochet2/lualzw ---[[ -MIT License - -Copyright (c) 2016 Rochet2 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -]] - -local char = string.char -local type = type -local sub = string.sub -local tconcat = table.concat - -local basedictcompress = {} -local basedictdecompress = {} -for i = 0, 255 do - local ic, iic = char(i), char(i, 0) - basedictcompress[ic] = iic - basedictdecompress[iic] = ic -end - -local function dictAddA(str, dict, a, b) - if a >= 256 then - a, b = 0, b+1 - if b >= 256 then - dict = {} - b = 1 - end - end - dict[str] = char(a,b) - a = a+1 - return dict, a, b -end - -local function compress(input) - if type(input) ~= "string" then - error ("string expected, got "..type(input)) - end - local len = #input - if len <= 1 then - return "u"..input - end - - local dict = {} - local a, b = 0, 1 - - local result = {"c"} - local resultlen = 1 - local n = 2 - local word = "" - for i = 1, len do - local c = sub(input, i, i) - local wc = word..c - if not (basedictcompress[wc] or dict[wc]) then - local write = basedictcompress[word] or dict[word] - if not write then - error "algorithm error, could not fetch word" - end - result[n] = write - resultlen = resultlen + #write - n = n+1 - if len <= resultlen then - return "u"..input - end - dict, a, b = dictAddA(wc, dict, a, b) - word = c - else - word = wc - end - end - result[n] = basedictcompress[word] or dict[word] - resultlen = resultlen+#result[n] - if len <= resultlen then - return "u"..input - end - return tconcat(result) -end - -local function dictAddB(str, dict, a, b) - if a >= 256 then - a, b = 0, b+1 - if b >= 256 then - dict = {} - b = 1 - end - end - dict[char(a,b)] = str - a = a+1 - return dict, a, b -end - -local function decompress(input) - if type(input) ~= "string" then - error( "string expected, got "..type(input)) - end - - if #input < 1 then - error("invalid input - not a compressed string") - end - - local control = sub(input, 1, 1) - if control == "u" then - return sub(input, 2) - elseif control ~= "c" then - error( "invalid input - not a compressed string") - end - input = sub(input, 2) - local len = #input - - if len < 2 then - error( "invalid input - not a compressed string") - end - - local dict = {} - local a, b = 0, 1 - - local result = {} - local n = 1 - local last = sub(input, 1, 2) - result[n] = basedictdecompress[last] or dict[last] - n = n+1 - for i = 3, len, 2 do - local code = sub(input, i, i+1) - local lastStr = basedictdecompress[last] or dict[last] - if not lastStr then - error( "could not find last from dict. Invalid input?") - end - local toAdd = basedictdecompress[code] or dict[code] - if toAdd then - result[n] = toAdd - n = n+1 - dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b) - else - local tmp = lastStr..sub(lastStr, 1, 1) - result[n] = tmp - n = n+1 - dict, a, b = dictAddB(tmp, dict, a, b) - end - last = code - end - return tconcat(result) -end - -return { - compress = compress, - decompress = decompress, -} \ No newline at end of file diff --git a/sys/modules/opus/crypto/serializer.lua b/sys/modules/opus/crypto/serializer.lua deleted file mode 100644 index db19336..0000000 --- a/sys/modules/opus/crypto/serializer.lua +++ /dev/null @@ -1,50 +0,0 @@ -local Serializer = { } - -local insert = table.insert -local format = string.format - -function Serializer.serialize(tbl) - local output = { } - - local function recurse(t) - local sType = type(t) - if sType == 'table' then - if next(t) == nil then - insert(output, '{}') - else - insert(output, '{') - local tSeen = {} - for k, v in ipairs(t) do - tSeen[k] = true - recurse(v) - insert(output, ',') - end - for k, v in pairs(t) do - if not tSeen[k] then - if type(k) == 'string' and string.match(k, '^[%a_][%a%d_]*$') then - insert(output, k .. '=') - recurse(v) - insert(output, ',') - else - insert(output, '[') - recurse(k) - insert(output, ']=') - recurse(v) - insert(output, ',') - end - end - end - insert(output, '}') - end - elseif sType == 'string' then - insert(output, format('%q', t)) - else - insert(output, tostring(t)) - end - end - - recurse(tbl) - return table.concat(output) -end - -return Serializer -- 2.49.1 From 9e85a0dae1c8b5e2b87071b827fd899d455d5a1b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 1 Jul 2019 17:22:19 -0400 Subject: [PATCH 032/236] shuffled some files around --- sys/apps/network/trust.lua | 4 +++- sys/autorun/apps.lua | 4 ---- sys/autorun/upgraded.lua | 1 + sys/etc/fstab | 4 ++++ sys/init/3.sys.lua | 4 ++++ sys/init/6.packages.lua | 7 +++++-- sys/modules/opus/crypto/chacha20.lua | 2 +- sys/modules/opus/socket.lua | 24 +++++++++++++++--------- 8 files changed, 33 insertions(+), 17 deletions(-) delete mode 100644 sys/autorun/apps.lua create mode 100644 sys/etc/fstab create mode 100644 sys/init/3.sys.lua diff --git a/sys/apps/network/trust.lua b/sys/apps/network/trust.lua index 58d3847..7f80427 100644 --- a/sys/apps/network/trust.lua +++ b/sys/apps/network/trust.lua @@ -13,7 +13,9 @@ local function trustConnection(socket) if not password then socket:write({ msg = 'No password has been set' }) else - data = Crypto.decrypt(data, password) + pcall(function() + data = Crypto.decrypt(data, password) + end) if data and data.pk and data.dh == socket.dhost then local trustList = Util.readTable('usr/.known_hosts') or { } trustList[data.dh] = data.pk diff --git a/sys/autorun/apps.lua b/sys/autorun/apps.lua deleted file mode 100644 index 5322c79..0000000 --- a/sys/autorun/apps.lua +++ /dev/null @@ -1,4 +0,0 @@ -fs.mount('sys/apps/pain.lua', 'urlfs', 'https://github.com/LDDestroier/CC/raw/master/pain.lua') -fs.mount('sys/apps/update.lua', 'urlfs', 'http://pastebin.com/raw/UzGHLbNC') -fs.mount('sys/apps/Enchat.lua', 'urlfs', 'https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua') -fs.mount('sys/apps/cloud.lua', 'urlfs', 'https://cloud-catcher.squiddev.cc/cloud.lua') diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index d3a2737..6cb1c92 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -10,3 +10,4 @@ if fs.exists('sys/autorun/gps.lua') then fs.delete('sys/autorun/gps.lua') end if fs.exists('sys/autorun/gpsHost.lua') then fs.delete('sys/autorun/gpsHost.lua') end if fs.exists('sys/apps/network/redserver.lua') then fs.delete('sys/apps/network/redserver.lua') end if fs.exists('sys/apis') then fs.delete('sys/apis') end +if fs.exists('sys/autorun/apps.lua') then fs.delete('sys/autorun/apps.lua') end diff --git a/sys/etc/fstab b/sys/etc/fstab new file mode 100644 index 0000000..deeac19 --- /dev/null +++ b/sys/etc/fstab @@ -0,0 +1,4 @@ +sys/apps/pain.lua urlfs https://github.com/LDDestroier/CC/raw/master/pain.lua +sys/apps/update.lua urlfs http://pastebin.com/raw/UzGHLbNC +sys/apps/Enchat.lua urlfs https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua +sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua \ No newline at end of file diff --git a/sys/init/3.sys.lua b/sys/init/3.sys.lua new file mode 100644 index 0000000..ce04901 --- /dev/null +++ b/sys/init/3.sys.lua @@ -0,0 +1,4 @@ +local fs = _G.fs + +fs.mount('rom/modules/main/opus', 'linkfs', 'sys/modules/opus') +fs.loadTab('sys/etc/fstab') diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index ad667cd..c991821 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -29,9 +29,12 @@ for name in pairs(Packages:installed()) do if fs.exists(helpPath) then table.insert(helpPaths, helpPath) end + + local fstabPath = fs.combine(packageDir, 'etc/fstab') + if fs.exists(fstabPath) then + fs.loadTab(fstabPath) + end end help.setPath(table.concat(helpPaths, ':')) shell.setPath(table.concat(appPaths, ':')) - -fs.mount('rom/modules/main/opus', 'linkfs', 'sys/modules/opus') diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 89cf0ac..9547f6c 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -164,7 +164,7 @@ local function decrypt(data, key) data = Util.hexToByteArray(data[2]) key = sha2.digest(key) local ptx = crypt(data, key, nonce, 1, ROUNDS) - return cbor.decode(tostring(ptx)) + return cbor.decode(tostring(ptx)) end local obj = {} diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index d56ea38..822d195 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -181,18 +181,24 @@ local function trusted(socket, msg, options) local identifier = options and options.identifier or getIdentifier() - if identifier and type(msg.t) == 'table' then - local data = Crypto.decrypt(msg.t, Util.hexToByteArray(identifier)) + local s, m = pcall(function() + if identifier and type(msg.t) == 'table' then + local data = Crypto.decrypt(msg.t, Util.hexToByteArray(identifier)) - if data and data.ts and tonumber(data.ts) then - if math.abs(os.epoch('utc') - data.ts) < 4096 then - socket.remotePubKey = Util.hexToByteArray(data.pk) - socket.privKey, socket.pubKey = network.getKeyPair() - setupCrypto(socket) - return true + if data and data.ts and tonumber(data.ts) then + if math.abs(os.epoch('utc') - data.ts) < 4096 then + socket.remotePubKey = Util.hexToByteArray(data.pk) + socket.privKey, socket.pubKey = network.getKeyPair() + setupCrypto(socket) + return true + end + _G._syslog('time diff ' .. math.abs(os.epoch('utc') - data.ts)) end - _G._syslog('time diff ' .. math.abs(os.epoch('utc') - data.ts)) end + end) + if not s and m then + _G._syslog('trust failure') + _G._syslog(m) end end -- 2.49.1 From cd7122921f4cd563aab6f8e1bcfc9d59f58f909f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 1 Jul 2019 19:20:38 -0400 Subject: [PATCH 033/236] oops --- sys/modules/opus/socket.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 822d195..0ff53a9 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -200,6 +200,7 @@ local function trusted(socket, msg, options) _G._syslog('trust failure') _G._syslog(m) end + return s and m end function Socket.server(port, options) -- 2.49.1 From aec5ac012167b76c25e9eeb6ff46870d5160c8d7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 1 Jul 2019 20:39:34 -0400 Subject: [PATCH 034/236] oops again --- sys/apps/network/trust.lua | 7 +++---- sys/modules/opus/socket.lua | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/sys/apps/network/trust.lua b/sys/apps/network/trust.lua index 7f80427..c31b6ec 100644 --- a/sys/apps/network/trust.lua +++ b/sys/apps/network/trust.lua @@ -13,10 +13,9 @@ local function trustConnection(socket) if not password then socket:write({ msg = 'No password has been set' }) else - pcall(function() - data = Crypto.decrypt(data, password) - end) - if data and data.pk and data.dh == socket.dhost then + local s + s, data = pcall(Crypto.decrypt, data, password) + if s and data and data.pk and data.dh == socket.dhost then local trustList = Util.readTable('usr/.known_hosts') or { } trustList[data.dh] = data.pk Util.writeTable('usr/.known_hosts', trustList) diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 0ff53a9..8151855 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -210,7 +210,6 @@ function Socket.server(port, options) local _, _, sport, dport, msg = os.pullEvent('modem_message') if sport == port and - msg and type(msg) == 'table' and msg.dhost == os.getComputerID() and msg.type == 'OPEN' then -- 2.49.1 From 1dcb6d67b7ef6bfd6075af7eb2fa34290d61d02f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 2 Jul 2019 10:19:08 -0400 Subject: [PATCH 035/236] tweaks + Anavrins disk usage system module --- sys/apps/system/diskusage.lua | 146 ++++++++++++++++++ sys/autorun/clipboard.lua | 11 +- sys/init/1.device.lua | 18 ++- sys/modules/opus/input.lua | 43 +----- sys/modules/opus/ui.lua | 19 ++- sys/modules/opus/ui/components/NftImage.lua | 1 - .../opus/ui/components/ProgressBar.lua | 16 +- 7 files changed, 200 insertions(+), 54 deletions(-) create mode 100644 sys/apps/system/diskusage.lua diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua new file mode 100644 index 0000000..0ce1e14 --- /dev/null +++ b/sys/apps/system/diskusage.lua @@ -0,0 +1,146 @@ +local UI = require('opus.ui') +local Event = require('opus.event') +local Util = require('opus.util') +local NFT = require('opus.nft') + +local NftImages = { + blank = '\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153', + drive = '', + rom = '', + hdd = '', +} + +local tab = UI.Tab { + tabTitle = '1.Disks Usage', + description = 'Visualise HDD and disks usage', + drives = UI.ScrollingGrid { + y = 2, ey = 9, x = 2, ex = '47%', + columns = { + { heading = 'Drive', key = 'name' }, + { heading = 'Side' ,key = 'side' } + }, + sortColumn = 'name', + }, + infos = UI.Grid { + x = '52%', y = 3, ex = -2, ey = 9, + disableHeader = true, + unfocusedBackgroundSelectedColor = colors.black, + inactive = true, + backgroundSelectedColor = colors.black, --?? + columns = { + {key = 'name' }, + {key = 'value', align = 'right' }, + } + }, + + progress = UI.ProgressBar { + x = 11, y = 11, ex = -2, + barChar = '\127', + textColor = colors.blue, + backgroundColor = colors.black, + }, + percentage = UI.Text { + x = 11, y = 12, ex = -2, + align = 'center', + }, + icon = UI.NftImage { + x = 2, y = 11, + image = NFT.parse(NftImages.blank) + }, +} + +local function getDrives() + local unique = { ['hdd'] = true, ['virt'] = true } + local exclude = {} + local drives = { + {name = 'hdd', side = ''}, + } + for _, drive in pairs(fs.list('/')) do + local side = fs.getDrive(drive) + if side and not unique[side] then + unique[side] = true + exclude[drive] = true + table.insert(drives, {name=drive, side=side}) + end + end + return drives, exclude +end + +local function getDriveInfo(p) + local files, dirs, total = 0, 0, 0 + + if p == "hdd" then p = "/" end + p = fs.combine(p, '') + local drive = fs.getDrive(p) + + local function recurse(path) + if fs.getDrive(path) == drive then + if fs.isDir(path) then + if path ~= p then + total = total + 500 + dirs = dirs + 1 + end + for _, v in pairs(fs.list(path)) do + recurse(fs.combine(path, v)) + end + else + local sz = fs.getSize(path) + + files = files + 1 + if drive == 'rom' then + total = total + sz + else + total = total + math.max(500, sz) + end + end + end + end + + recurse(p) + local info = {} + table.insert(info, { name = 'Type', value = peripheral.getType(drive) or drive }) + table.insert(info, { name = 'Used', value = total }) + table.insert(info, { name = 'Total', value = total + fs.getFreeSpace(p) }) + table.insert(info, { name = 'Free', value = fs.getFreeSpace(p) }) + table.insert(info, { }) + table.insert(info, { name = 'Files', value = files }) + table.insert(info, { name = 'Dirs', value = dirs }) + return info, math.floor((total / (total + fs.getFreeSpace(p))) * 100) +end + +function tab:updateInfo() + local selected = self.drives:getSelected() + local info, percent = getDriveInfo(selected and selected.name or self.drives.values[1].name) + self.infos:setValues(info) + self.progress.value = percent + self.percentage.value = ('%#3d%%'):format(percent) + self:draw() +end + +function tab:updateDrives() + local drives, exclude = getDrives() + self.exclude = exclude + self.drives:setValues(drives) +end + +function tab:enable() + self:updateDrives() + self:updateInfo() + UI.Tab.enable(self) +end + +function tab:eventHandler(event) + if event.type == 'grid_focus_row' then + self:updateInfo() + end + return UI.Tab.eventHandler(self, event) +end + +Event.on({ 'disk', 'disk_eject' }, function() + sleep(1) + tab:updateDrives() + tab:updateInfo() + tab:sync() +end) + +return tab diff --git a/sys/autorun/clipboard.lua b/sys/autorun/clipboard.lua index 72c9434..992af47 100644 --- a/sys/autorun/clipboard.lua +++ b/sys/autorun/clipboard.lua @@ -5,19 +5,18 @@ local keyboard = _G.device.keyboard local os = _G.os local textutils = _G.textutils -local data - kernel.hook('clipboard_copy', function(_, args) - data = args[1] + keyboard.clipboard = args[1] end) keyboard.addHotkey('shift-paste', function() + local data = keyboard.clipboard + if type(data) == 'table' then local s, m = pcall(textutils.serialize, data) - data = (s and m) or Util.tostring(data) + data = s and m or Util.tostring(data) end - -- replace the event paste data with our internal data - -- args[1] = Util.tostring(data or '') + if data then os.queueEvent('paste', data) end diff --git a/sys/init/1.device.lua b/sys/init/1.device.lua index 65b1e76..c4946d3 100644 --- a/sys/init/1.device.lua +++ b/sys/init/1.device.lua @@ -78,16 +78,12 @@ local modifiers = Util.transpose { keys.leftAlt, keys.rightAlt, } -kernel.hook({ 'key', 'key_up', 'char', 'paste' }, function(event, eventData) +kernel.hook({ 'key', 'char', 'paste' }, function(event, eventData) local code = eventData[1] -- maintain global keyboard modifier state - if modifiers[code] then - if event == 'key' then - keyboard.state[code] = true - elseif event == 'key_up' then - keyboard.state[code] = nil - end + if event == 'key' and modifiers[code] then + keyboard.state[code] = true end -- and fire hotkeys @@ -99,6 +95,14 @@ kernel.hook({ 'key', 'key_up', 'char', 'paste' }, function(event, eventData) end end) +kernel.hook('key_up', function(_, eventData) + local code = eventData[1] + + if modifiers[code] then + keyboard.state[code] = nil + end +end) + kernel.hook({ 'mouse_click', 'mouse_up', 'mouse_drag' }, function(event, eventData) local button = eventData[1] if event == 'mouse_click' then diff --git a/sys/modules/opus/input.lua b/sys/modules/opus/input.lua index b7894d1..62d51b4 100644 --- a/sys/modules/opus/input.lua +++ b/sys/modules/opus/input.lua @@ -10,13 +10,7 @@ local modifiers = Util.transpose { keys.leftAlt, keys.rightAlt, } -local input = { - state = { }, -} - -if not keyboard then - keyboard = { state = input.state } -end +local input = { } function input:modifierPressed() return keyboard.state[keys.leftCtrl] or @@ -64,7 +58,6 @@ end function input:reset() self.state = { } - self.fired = nil self.timer = nil self.mch = nil @@ -81,7 +74,6 @@ function input:translate(event, code, p1, p2) if event == 'key' then if p1 then -- key is held down if not modifiers[code] then - self.fired = true local ch = input:toCode(keys.getName(code), code) if #ch == 1 then return { @@ -92,37 +84,19 @@ function input:translate(event, code, p1, p2) return { code = ch } end elseif code then - --self.fired = true - local ch = input:toCode(keys.getName(code), code) - if #ch ~= 1 then - return { code = ch } - end --- self.state[code] = true + local ch = input:toCode(keys.getName(code), code) + if #ch ~= 1 then + return { code = ch } + end end elseif event == 'char' then local combo = isCombo() - --if not self.fired then - if combo or not (keyboard.state[keys.leftCtrl] or keyboard.state[keys.rightCtrl]) then - self.fired = not combo - return { code = event, ch = code } - --end --- return { code = event, ch = input:toCode(code) } + if combo or not (keyboard.state[keys.leftCtrl] or keyboard.state[keys.rightCtrl]) then + return { code = event, ch = code } end - elseif event == 'key_upx' then - if not self.fired then - --if self.state[code] then - self.fired = true - local ch = input:toCode(keys.getName(code), code) - self.state[code] = nil - return { code = ch } - --end - end - self.state[code] = nil - elseif event == 'paste' then - self.fired = true if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] then return { code = 'shift-paste', text = code } else @@ -142,7 +116,6 @@ function input:translate(event, code, p1, p2) elseif event == 'mouse_drag' then self.mfired = true - self.fired = true return { code = input:toCode('mouse_drag', 255), button = code, @@ -169,7 +142,6 @@ function input:translate(event, code, p1, p2) self.mch = 'mouse_up' self.mfired = input:toCode(self.mch, 255) end - self.fired = true return { code = self.mfired, button = code, @@ -182,7 +154,6 @@ function input:translate(event, code, p1, p2) [ -1 ] = 'scroll_up', [ 1 ] = 'scroll_down' } - self.fired = true return { code = input:toCode(directions[code], 255), x = p1, diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 87a6a67..50864c7 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -457,7 +457,6 @@ function UI.Window:initChildren() if not child.parent then child.parent = self child:setParent() - -- child:reposition() -- maybe if self.enabled then child:enable() end @@ -468,6 +467,24 @@ function UI.Window:initChildren() end function UI.Window:layout() + local function calc(p, max) + p = tonumber(p:match('(%d+)%%')) + return p and math.floor(max * p / 100) or 1 + end + + if type(self.x) == 'string' then + self.x = calc(self.x, self.parent.width) + end + if type(self.ex) == 'string' then + self.ex = calc(self.ex, self.parent.width) + end + if type(self.y) == 'string' then + self.y = calc(self.y, self.parent.height) + end + if type(self.ey) == 'string' then + self.ey = calc(self.ey, self.parent.height) + end + if self.x < 0 then self.x = self.parent.width + self.x + 1 end diff --git a/sys/modules/opus/ui/components/NftImage.lua b/sys/modules/opus/ui/components/NftImage.lua index b932dfb..3457711 100644 --- a/sys/modules/opus/ui/components/NftImage.lua +++ b/sys/modules/opus/ui/components/NftImage.lua @@ -4,7 +4,6 @@ local UI = require('opus.ui') UI.NftImage = class(UI.Window) UI.NftImage.defaults = { UIElement = 'NftImage', - event = 'button_press', } function UI.NftImage:setParent() if self.image then diff --git a/sys/modules/opus/ui/components/ProgressBar.lua b/sys/modules/opus/ui/components/ProgressBar.lua index 56d6156..af12708 100644 --- a/sys/modules/opus/ui/components/ProgressBar.lua +++ b/sys/modules/opus/ui/components/ProgressBar.lua @@ -6,13 +6,23 @@ local colors = _G.colors UI.ProgressBar = class(UI.Window) UI.ProgressBar.defaults = { UIElement = 'ProgressBar', - progressColor = colors.lime, backgroundColor = colors.gray, height = 1, + progressColor = colors.lime, + progressChar = UI.extChars and '\153' or ' ', + fillChar = ' ', + fillColor = colors.gray, + textColor = colors.green, value = 0, } function UI.ProgressBar:draw() - self:clear() local width = math.ceil(self.value / 100 * self.width) - self:clearArea(1, 1, width, self.height, self.progressColor) + + local filler = string.rep(self.fillChar, self.width) + local progress = string.rep(self.progressChar, width) + + for i = 1, self.height do + self:write(1, i, filler, nil, self.fillColor) + self:write(1, i, progress, self.progressColor) + end end -- 2.49.1 From 6ba458646ff8f16555c5923472873253523910bd Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 2 Jul 2019 14:11:33 -0400 Subject: [PATCH 036/236] control-q instead of q for exitting apps + grid column override colors --- sys/apps/Files.lua | 4 ++-- sys/apps/Help.lua | 4 ++-- sys/apps/Network.lua | 2 +- sys/apps/System.lua | 2 +- sys/apps/Tasks.lua | 2 +- sys/apps/Welcome.lua | 16 ++++++++++++++++ sys/apps/system/diskusage.lua | 6 +++--- sys/modules/opus/ui/components/Grid.lua | 4 ++-- 8 files changed, 28 insertions(+), 12 deletions(-) diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 6c6b453..019979e 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -47,7 +47,7 @@ local Browser = UI.Page { { text = 'Pastebin put p', event = 'pastebin', flags = FILE }, { text = 'Shell s', event = 'shell' }, { spacer = true }, - { text = 'Quit q', event = 'quit' }, + { text = 'Quit ^q', event = 'quit' }, } }, { text = 'Edit', dropdown = { { text = 'Cut ^x', event = 'cut' }, @@ -140,7 +140,7 @@ local Browser = UI.Page { }, }, accelerators = { - q = 'quit', + [ 'control-q' ] = 'quit', c = 'cedit', e = 'edit', s = 'shell', diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index b6e862c..abb6099 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -31,7 +31,7 @@ local page = UI.Page { sortColumn = 'name', }, accelerators = { - q = 'quit', + [ 'control-q' ] = 'quit', enter = 'grid_select', }, } @@ -47,7 +47,7 @@ local topicPage = UI.Page { x = 2, ex = -1, y = 3, ey = -2, }, accelerators = { - q = 'back', + [ 'control-q' ] = 'back', backspace = 'back', }, } diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index 1c8405d..52be626 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -103,7 +103,7 @@ This only needs to be done once. t = 'telnet', v = 'vnc', r = 'reboot', - q = 'quit', + [ 'control-q' ] = 'quit', c = 'clear', }, } diff --git a/sys/apps/System.lua b/sys/apps/System.lua index b97d2e9..b18200d 100644 --- a/sys/apps/System.lua +++ b/sys/apps/System.lua @@ -23,7 +23,7 @@ local systemPage = UI.Page { }, notification = UI.Notification(), accelerators = { - q = 'quit', + [ 'control-q' ] = 'quit', }, } diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index 73ff93b..f67d71b 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -26,7 +26,7 @@ local page = UI.Page { autospace = true, }, accelerators = { - q = 'quit', + [ 'control-q' ] = 'quit', space = 'activate', t = 'terminate', }, diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 76c4c7f..5405174 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -19,6 +19,13 @@ local passwordIntro = [[A password is required for wireless access. local packagesIntro = [[Setup Complete %sOpen the package manager to add software to this computer.]] +local contributorsIntro = [[Contributors%s + +Anavrins: Encryption/security/custom apps +Community: Several selected applications +hugeblank: Startup screen improvements +LDDestroier: Art design + custom apps +Lemmmy: Application improvements]] local page = UI.Page { wizard = UI.Wizard { @@ -95,6 +102,15 @@ local page = UI.Page { value = string.format(packagesIntro, Ansi.white), }, }, + contributors = UI.WizardPage { + index = 5, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = string.format(contributorsIntro, Ansi.white), + }, + }, }, }, notification = UI.Notification { }, diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 0ce1e14..7ae69ff 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -17,7 +17,7 @@ local tab = UI.Tab { y = 2, ey = 9, x = 2, ex = '47%', columns = { { heading = 'Drive', key = 'name' }, - { heading = 'Side' ,key = 'side' } + { heading = 'Side' ,key = 'side', textColor = colors.yellow } }, sortColumn = 'name', }, @@ -29,7 +29,7 @@ local tab = UI.Tab { backgroundSelectedColor = colors.black, --?? columns = { {key = 'name' }, - {key = 'value', align = 'right' }, + {key = 'value', align = 'right', textColor = colors.yellow }, } }, @@ -113,7 +113,7 @@ function tab:updateInfo() local info, percent = getDriveInfo(selected and selected.name or self.drives.values[1].name) self.infos:setValues(info) self.progress.value = percent - self.percentage.value = ('%#3d%%'):format(percent) + self.percentage.value = ('%#3d%% used'):format(percent) self:draw() end diff --git a/sys/modules/opus/ui/components/Grid.lua b/sys/modules/opus/ui/components/Grid.lua index a1dc638..bcdab81 100644 --- a/sys/modules/opus/ui/components/Grid.lua +++ b/sys/modules/opus/ui/components/Grid.lua @@ -351,8 +351,8 @@ function UI.Grid:drawRow(sb, row, focused, bg, fg) sb:write(ind .. safeValue(row[col.key] or ''), col.cw + 1, col.align, - bg, - fg) + col.backgroundColor or bg, + col.textColor or fg) ind = ' ' end end -- 2.49.1 From 2f5aea912b0f2bd5a9e9df61c4140dab9ad0da6d Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 2 Jul 2019 14:25:17 -0400 Subject: [PATCH 037/236] diskusage --- sys/apps/system/diskusage.lua | 41 ++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 7ae69ff..c78ef96 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -1,8 +1,12 @@ local UI = require('opus.ui') local Event = require('opus.event') -local Util = require('opus.util') local NFT = require('opus.nft') +local colors = _G.colors +local fs = _G.files +local os = _G.os +local peripheral = _G.peripheral + local NftImages = { blank = '\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153', drive = '', @@ -11,40 +15,42 @@ local NftImages = { } local tab = UI.Tab { - tabTitle = '1.Disks Usage', + tabTitle = 'Disks Usage', description = 'Visualise HDD and disks usage', + drives = UI.ScrollingGrid { - y = 2, ey = 9, x = 2, ex = '47%', + x = 2, y = 1, + ex = '47%', ey = 8, columns = { { heading = 'Drive', key = 'name' }, - { heading = 'Side' ,key = 'side', textColor = colors.yellow } + { heading = 'Side' ,key = 'side', textColor = colors.yellow } }, sortColumn = 'name', }, infos = UI.Grid { - x = '52%', y = 3, ex = -2, ey = 9, + x = '52%', y = 2, + ex = -2, ey = 8, disableHeader = true, unfocusedBackgroundSelectedColor = colors.black, inactive = true, - backgroundSelectedColor = colors.black, --?? + backgroundSelectedColor = colors.black, columns = { - {key = 'name' }, - {key = 'value', align = 'right', textColor = colors.yellow }, + { key = 'name' }, + { key = 'value', align = 'right', textColor = colors.yellow }, } }, progress = UI.ProgressBar { - x = 11, y = 11, ex = -2, - barChar = '\127', - textColor = colors.blue, - backgroundColor = colors.black, + x = 11, y = 10, + ex = -2, }, percentage = UI.Text { - x = 11, y = 12, ex = -2, + x = 11, y = 11, + ex = -2, align = 'center', }, icon = UI.NftImage { - x = 2, y = 11, + x = 2, y = 10, image = NFT.parse(NftImages.blank) }, } @@ -96,7 +102,8 @@ local function getDriveInfo(p) end end - recurse(p) + recurse(p) + local info = {} table.insert(info, { name = 'Type', value = peripheral.getType(drive) or drive }) table.insert(info, { name = 'Used', value = total }) @@ -113,7 +120,7 @@ function tab:updateInfo() local info, percent = getDriveInfo(selected and selected.name or self.drives.values[1].name) self.infos:setValues(info) self.progress.value = percent - self.percentage.value = ('%#3d%% used'):format(percent) + self.percentage.value = ('%#3d%%'):format(percent) self:draw() end @@ -137,7 +144,7 @@ function tab:eventHandler(event) end Event.on({ 'disk', 'disk_eject' }, function() - sleep(1) + os.sleep(1) tab:updateDrives() tab:updateInfo() tab:sync() -- 2.49.1 From 9456d318819f19e461c121654ab999e17449c519 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 2 Jul 2019 14:32:41 -0400 Subject: [PATCH 038/236] oops --- sys/apps/system/diskusage.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index c78ef96..3cec732 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -3,7 +3,7 @@ local Event = require('opus.event') local NFT = require('opus.nft') local colors = _G.colors -local fs = _G.files +local fs = _G.fs local os = _G.os local peripheral = _G.peripheral -- 2.49.1 From 0b222207ba512d5c8248252b46665c99accce2e9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 3 Jul 2019 10:44:30 -0400 Subject: [PATCH 039/236] package manager improvements --- sys/apps/PackageManager.lua | 26 ++++++++++++++++++++++---- sys/apps/Welcome.lua | 7 +++++-- sys/apps/package.lua | 27 ++++++++++++++++++++++----- sys/autorun/complete.lua | 2 +- sys/init/6.packages.lua | 6 ------ sys/modules/opus/packages.lua | 3 +++ 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/sys/apps/PackageManager.lua b/sys/apps/PackageManager.lua index de8af57..1329ec7 100644 --- a/sys/apps/PackageManager.lua +++ b/sys/apps/PackageManager.lua @@ -33,6 +33,12 @@ local page = UI.Page { operationText = 'Remove', help = 'Remove', }, + updateall = UI.Button { + ex = -2, y = -3, width = 12, + text = 'Update All', + event = 'updateall', + help = 'Update all installed packages', + }, description = UI.TextArea { x = 16, y = 3, ey = -5, marginRight = 0, marginLeft = 0, @@ -138,6 +144,13 @@ function page:eventHandler(event) self.description:draw() self:updateSelection(event.selected) + elseif event.type == 'updateall' then + self.operation = 'updateall' + self.action.button.text = ' Begin ' + self.action.button.event = 'begin' + self.action.titleBar.title = 'Update All' + self.action:show() + elseif event.type == 'action' then local selected = self.grid:getSelected() if selected then @@ -153,11 +166,16 @@ function page:eventHandler(event) self.action:hide() elseif event.type == 'begin' then - local selected = self.grid:getSelected() - self:run(self.operation, selected.name) - selected.installed = Packages:isInstalled(selected.name) + if self.operation == 'updateall' then + self:run(self.operation, '') + else + local selected = self.grid:getSelected() + self:run(self.operation, selected.name) + selected.installed = Packages:isInstalled(selected.name) + + self:updateSelection(selected) + end - self:updateSelection(selected) self.action.button.text = ' Done ' self.action.button.event = 'hide-action' self.action.button:draw() diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 5405174..47c658f 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -25,7 +25,10 @@ Anavrins: Encryption/security/custom apps Community: Several selected applications hugeblank: Startup screen improvements LDDestroier: Art design + custom apps -Lemmmy: Application improvements]] +Lemmmy: Application improvements + +%sContribute at:%s +https://github.com/kepler155c/opus]] local page = UI.Page { wizard = UI.Wizard { @@ -108,7 +111,7 @@ local page = UI.Page { textColor = colors.yellow, inactive = true, x = 3, ex = -3, y = 2, ey = -2, - value = string.format(contributorsIntro, Ansi.white), + value = string.format(contributorsIntro, Ansi.white, Ansi.yellow, Ansi.white), }, }, }, diff --git a/sys/apps/package.lua b/sys/apps/package.lua index 864f4fd..3d95c17 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -35,13 +35,15 @@ local function progress(max) end end -local function install(name, isUpdate) +local function install(name, isUpdate, ignoreDeps) local manifest = Packages:downloadManifest(name) or error('Invalid package') - if manifest.required then - for _, v in pairs(manifest.required) do - if isUpdate or not Packages:isInstalled(v) then - install(v, isUpdate) + if not ignoreDeps then + if manifest.required then + for _, v in pairs(manifest.required) do + if isUpdate or not Packages:isInstalled(v) then + install(v, isUpdate) + end end end end @@ -89,6 +91,21 @@ if action == 'install' then return end +if action == 'refresh' then + print('Downloading...') + Packages:downloadList() + print('refresh complete') + return +end + +if action == 'updateall' then + for name in pairs(Packages:installed()) do + install(name, true, true) + end + print('updateall complete') + return +end + if action == 'update' then local name = args[1] or Syntax('Invalid package') if not Packages:isInstalled(name) then diff --git a/sys/autorun/complete.lua b/sys/autorun/complete.lua index 82b2fe5..31a6bd9 100644 --- a/sys/autorun/complete.lua +++ b/sys/autorun/complete.lua @@ -17,6 +17,6 @@ end _ENV.shell.setCompletionFunction("sys/apps/package.lua", function(_, index, text) if index == 1 then - return completeMultipleChoice(text, { "install ", "update ", "uninstall " }) + return completeMultipleChoice(text, { "install ", "update ", "uninstall ", "updateall ", "refresh" }) end end) diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index c991821..b1e6ef7 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -5,12 +5,6 @@ local fs = _G.fs local help = _G.help local shell = _ENV.shell -if not fs.exists('usr/config/packages') then - pcall(function() - Packages:downloadList() - end) -end - local appPaths = Util.split(shell.path(), '(.-):') local helpPaths = Util.split(help.path(), '(.-):') diff --git a/sys/modules/opus/packages.lua b/sys/modules/opus/packages.lua index c54bb65..a3890da 100644 --- a/sys/modules/opus/packages.lua +++ b/sys/modules/opus/packages.lua @@ -21,6 +21,9 @@ function Packages:installed() end function Packages:list() + if not fs.exists('usr/config/packages') then + self:downloadList() + end return Util.readTable('usr/config/packages') or { } end -- 2.49.1 From ddb5433c0176d1d345f8cf33965baa4eaec99fd2 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Thu, 4 Jul 2019 21:33:47 -0400 Subject: [PATCH 040/236] New sniffer app --- sys/apps/Network.lua | 4 +- sys/apps/Sniff.lua | 361 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 sys/apps/Sniff.lua diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index 52be626..c437fe9 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -44,7 +44,7 @@ local page = UI.Page { x = -3, dropdown = { { text = 'Port Status', event = 'ports', modem = true }, - { spacer = true }, + { spacer = true }, { text = 'Help', event = 'help', noCheck = true }, }, }, @@ -127,14 +127,12 @@ local function sendCommand(host, command) end end ---[[ function page.ports:eventHandler(event) if event.type == 'grid_select' then shell.openForegroundTab('sniff ' .. event.selected.port) end return UI.SlideOut.eventHandler(self, event) end -]] function page.ports.grid:update() local transport = network:getTransport() diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua new file mode 100644 index 0000000..8499c72 --- /dev/null +++ b/sys/apps/Sniff.lua @@ -0,0 +1,361 @@ +local UI = require('opus.ui') +local Event = require('opus.event') +local Util = require('opus.util') + +local colors = _G.colors +local device = _G.device +local textutils = _G.textutils +local peripheral = _G.peripheral +local multishell = _ENV.multishell + +local gridColumns = {} +table.insert(gridColumns, { heading = '#', key = 'id', width = 4, align = 'right' }) +table.insert(gridColumns, { heading = 'Port', key = 'portid', width = 5, align = 'right' }) +table.insert(gridColumns, { heading = 'Reply', key = 'replyid', width = 5, align = 'right' }) +if UI.defaultDevice.width > 50 then table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' }) end +table.insert(gridColumns, { heading = 'Msg', key = 'message' }) + +local page = UI.Page { + paused = false, + index = 0, + notification = UI.Notification { }, + accelerators = { ['control-q'] = 'quit' }, + + menuBar = UI.MenuBar { + buttons = { + { text = 'Pause', event = 'pause_click', name = 'pauseButton' }, + { text = 'Clear', event = 'clear_click' }, + { text = 'Config', event = 'config_click' }, + }, + }, + + packetGrid = UI.ScrollingGrid { + y = 2, + maxPacket = 300, + inverseSort = true, + sortColumn = 'id', + columns = gridColumns, + accelerators = { ['space'] = 'pause_click' }, + }, + + configSlide = UI.SlideOut { + y = -11, + titleBar = UI.TitleBar { title = 'Sniffer Config', event = 'config_close' }, + accelerators = { ['backspace'] = 'config_close' }, + configTabs = UI.Tabs { + y = 2, + filterTab = UI.Tab { + tabTitle = 'Filter', + filterGridText = UI.Text { + x = 2, y = 2, + value = 'ID filter', + }, + filterGrid = UI.ScrollingGrid { + x = 2, y = 3, + width = 10, height = 4, + disableHeader = true, + columns = { + { key = 'id', width = 5 }, + }, + }, + filterEntry = UI.TextEntry { + x = 2, y = 8, + width = 7, + shadowText = 'ID', + limit = 5, + accelerators = { enter = 'filter_add' }, + }, + filterAdd = UI.Button { + x = 10, y = 8, + text = '+', + event = 'filter_add', + }, + filterAllCheck = UI.Checkbox { + x = 13, y = 4, + value = false, + }, + filterAddText = UI.Text { + x = 17, y = 4, + value = "Use ID filter", + }, + }, + modemTab = UI.Tab { + tabTitle = 'Modem', + channelGrid = UI.ScrollingGrid { + x = 2, y = 2, + width = 12, height = 5, + autospace = true, + columns = {{ heading = 'Open Ports', key = 'port' }}, + }, + modemGrid = UI.ScrollingGrid { + x = 15, y = 2, + ex = -2, height = 5, + autospace = true, + columns = { + { heading = 'Side', key = 'side' }, + { heading = 'Type', key = 'type' }, + }, + }, + channelEntry = UI.TextEntry { + x = 2, y = 8, + width = 7, + shadowText = 'ID', + limit = 5, + accelerators = { enter = 'channel_add' }, + }, + channelAdd = UI.Button { + x = 10, y = 8, + text = '+', + event = 'channel_add', + }, + }, + }, + }, + + packetSlide = UI.SlideOut { + titleBar = UI.TitleBar { + title = 'Packet Information', + event = 'packet_close', + }, + backgroundColor = colors.cyan, + accelerators = { + ['backspace'] = 'packet_close', + ['left'] = 'prev_packet', + ['right'] = 'next_packet', + }, + packetMeta = UI.Grid { + x = 2, y = 2, + ex = 23, height = 4, + inactive = true, + columns = { + { key = 'text' }, + { key = 'value', align = 'right', textColor = colors.yellow }, + }, + values = { + port = { text = 'Port' }, + reply = { text = 'Reply' }, + dist = { text = 'Distance' }, + } + }, + packetButton = UI.Button { + x = 25, y = 5, + text = 'Open in Lua', + event = 'packet_lua', + }, + packetData = UI.TextArea { + y = 7, ey = -1, + backgroundColor = colors.black, + }, + }, +} + +local filterConfig = page.configSlide.configTabs.filterTab +local modemConfig = page.configSlide.configTabs.modemTab + +function filterConfig:eventHandler(event) + if event.type == 'filter_add' then + local id = tonumber(self.filterEntry.value) + if id then self.filterGrid.values[id] = { id = id } + self.filterGrid:update() + self.filterEntry:reset() + self:draw() + end + + elseif event.type == 'grid_select' then + self.filterGrid.values[event.selected.id] = nil + self.filterGrid:update() + self.filterGrid:draw() + + else return UI.Tab.eventHandler(self, event) + end + return true +end + +function modemConfig:loadChannel() + for chan = 0, 65535 do + self.currentModem.openChannels[chan] = self.currentModem.device.isOpen(chan) and { port = chan } or nil + end + self.channelGrid:setValues(self.currentModem.openChannels) + self.currentModem.loaded = true +end + +function modemConfig:enable() + if not self.currentModem.loaded then + self:loadChannel() + end + + UI.Tab.enable(self) +end + +function modemConfig:eventHandler(event) + if event.type == 'channel_add' then + local id = tonumber(modemConfig.channelEntry.value) + if id then + self.currentModem.openChannels[id] = { port = id } + self.currentModem.device.open(id) + self.channelGrid:setValues(self.currentModem.openChannels) + self.channelGrid:update() + self.channelEntry:reset() + self:draw() + end + + elseif event.type == 'grid_select' then + if event.element == self.channelGrid then + self.currentModem.openChannels[event.selected.port] = nil + self.currentModem.device.close(event.selected.port) + self.channelGrid:setValues(self.currentModem.openChannels) + page.configSlide.configTabs.modemTab.channelGrid:update() + page.configSlide.configTabs.modemTab.channelGrid:draw() + + elseif event.element == self.modemGrid then + self.currentModem = event.selected + page.notification:info("Loading channel list") + page:sync() + modemConfig:loadChannel() + page.notification:success("Now using modem on " .. self.currentModem.side) + self.channelGrid:draw() + end + + else return UI.Tab.eventHandler(self, event) + end + return true +end + +function page.packetSlide:setPacket(packet) + self.currentPacket = packet + local p, res = pcall(textutils.serialize, page.packetSlide.currentPacket.message) + self.packetData.textColor = p and colors.white or colors.red + self.packetData:setText(res) + self.packetMeta.values.port.value = page.packetSlide.currentPacket.portid + self.packetMeta.values.reply.value = page.packetSlide.currentPacket.replyid + self.packetMeta.values.dist.value = Util.round(page.packetSlide.currentPacket.distance, 2) +end + +function page.packetSlide:show(packet) + self:setPacket(packet) + + UI.SlideOut.show(self) +end + +function page.packetSlide:eventHandler(event) + if event.type == 'packet_close' then + self:hide() + page:setFocus(page.packetGrid) + + elseif event.type == 'packet_lua' then + multishell.openTab({ path = 'sys/apps/Lua.lua', args = { self.currentPacket.message }, focused = true }) + + elseif event.type == 'prev_packet' then + local c = self.currentPacket + local n = page.packetGrid.values[c.id - 1] + if n then + self:setPacket(n) + self:draw() + end + + elseif event.type == 'next_packet' then + local c = self.currentPacket + local n = page.packetGrid.values[c.id + 1] + if n then + self:setPacket(n) + self:draw() + end + + else return UI.SlideOut.eventHandler(self, event) + end + return true +end + +function page:enable() + modemConfig.modems = {} + peripheral.find('modem', function(side, dev) + modemConfig.modems[side] = { + type = dev.isWireless() and 'Wireless' or 'Wired', + side = side, + openChannels = { }, + device = dev, + loaded = false + } + end) + modemConfig.currentModem = device.wireless_modem and + modemConfig.modems[device.wireless_modem.side] or + device.wired_modem and + modemConfig.modems[device.wired_modem.side] or + nil + + modemConfig.modemGrid.values = modemConfig.modems + modemConfig.modemGrid:update() + modemConfig.modemGrid:setSelected(modemConfig.currentModem) + + UI.Page.enable(self) +end + + +function page:eventHandler(event) + if event.type == 'pause_click' then + self.paused = not self.paused + self.menuBar.pauseButton.text = self.paused and 'Resume' or 'Pause' + self.notification:success(self.paused and 'Paused' or 'Resumed', 2) + self.menuBar:draw() + + elseif event.type == 'clear_click' then + self.packetGrid:setValues({ }) + self.notification:success('Cleared', 2) + self.packetGrid:draw() + + elseif event.type == 'config_click' then + self.configSlide:show() + self:setFocus(filterConfig.filterEntry) + + elseif event.type == 'config_close' then + self.configSlide:hide() + self:setFocus(self.packetGrid) + + elseif event.type == 'grid_select' then + self.packetSlide:show(event.selected) + + elseif event.type == 'quit' then + Event.exitPullEvents() + + else return UI.Page.eventHandler(self, event) + end + return true +end + +Event.on('modem_message', function(event, side, chan, reply, msg, dist) + if not page.paused and modemConfig.currentModem.side == side and (not filterConfig.filterAllCheck.value or filterConfig.filterGrid.values[chan]) then + page.index = page.index + 1 + table.insert(page.packetGrid.values, { + id = page.index, + portid = chan, + replyid = reply, + message = msg, + distance = dist, + }) + + if #page.packetGrid.values > page.packetGrid.maxPacket then + local t = { } + for i = 10, #page.packetGrid.values do + t[i - 9] = page.packetGrid.values[i] + end + page.packetGrid:setValues(t) + end + + page.packetGrid:update() + page.packetGrid:draw() + page:sync() + end +end) + +local args = {...} +if args[1] then + local id = tonumber(args[1]) + if id then + filterConfig.filterGrid.values[id] = { id = id } + filterConfig.filterAllCheck:setValue(true) + filterConfig.filterGrid:update() + end +end + +UI:setPage(page) +UI:pullEvents() -- 2.49.1 From 08eac79109562d22b4d5c0e074727cf86bd26881 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Thu, 4 Jul 2019 21:46:09 -0400 Subject: [PATCH 041/236] Oops --- sys/apps/Network.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index c437fe9..bc551a4 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -129,7 +129,7 @@ end function page.ports:eventHandler(event) if event.type == 'grid_select' then - shell.openForegroundTab('sniff ' .. event.selected.port) + shell.openForegroundTab('Sniff ' .. event.selected.port) end return UI.SlideOut.eventHandler(self, event) end -- 2.49.1 From 4a089ecd85d91404291af0b0762ab465ff6acb33 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Fri, 5 Jul 2019 02:14:28 -0400 Subject: [PATCH 042/236] Added icons to diskusage --- sys/apps/system/diskusage.lua | 222 +++++++++++++++++----------------- 1 file changed, 112 insertions(+), 110 deletions(-) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 3cec732..c07c485 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -8,146 +8,148 @@ local os = _G.os local peripheral = _G.peripheral local NftImages = { - blank = '\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153', - drive = '', - rom = '', - hdd = '', + blank = '\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153', + disk = '\30\32\31\32\32\30\98\31\98\128\30\56\31\56\128\128\30\102\149\30\98\149\31\57\139\10\30\32\31\32\32\30\98\31\98\128\128\128\128\128\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128', + rom = '\30\57\31\57\128\31\56\144\144\144\144\144\31\57\128\10\30\56\31\57\157\30\55\31\55\128\128\128\128\128\30\57\31\56\145\10\30\57\31\56\136\30\55\31\55\128\30\55\31\48\82\79\77\30\55\128\30\57\31\56\132\10\30\56\31\57\157\30\55\31\55\128\128\128\128\128\30\57\31\56\145\10\30\57\31\57\128\31\56\129\129\129\129\129\31\57\128', + hdd = '\30\32\31\32\32\30\55\31\55\128\30\48\135\131\139\30\55\128\10\30\32\31\32\32\30\48\31\55\149\31\48\128\30\55\131\30\48\128\30\55\149\10\30\32\31\32\32\30\55\31\48\130\30\48\31\55\144\30\56\31\48\133\30\55\159\129\10\30\32\31\32\32\30\56\31\55\149\129\142\159\30\55\128\10\30\32\31\32\32\30\57\31\55\143\143\143\143\143', } local tab = UI.Tab { - tabTitle = 'Disks Usage', - description = 'Visualise HDD and disks usage', + tabTitle = 'Disks Usage', + description = 'Visualise HDD and disks usage', - drives = UI.ScrollingGrid { - x = 2, y = 1, - ex = '47%', ey = 8, - columns = { - { heading = 'Drive', key = 'name' }, - { heading = 'Side' ,key = 'side', textColor = colors.yellow } - }, - sortColumn = 'name', - }, - infos = UI.Grid { - x = '52%', y = 2, - ex = -2, ey = 8, - disableHeader = true, - unfocusedBackgroundSelectedColor = colors.black, - inactive = true, - backgroundSelectedColor = colors.black, - columns = { - { key = 'name' }, - { key = 'value', align = 'right', textColor = colors.yellow }, - } - }, + drives = UI.ScrollingGrid { + x = 2, y = 1, + ex = '47%', ey = 8, + columns = { + { heading = 'Drive', key = 'name' }, + { heading = 'Side' ,key = 'side', textColor = colors.yellow } + }, + sortColumn = 'name', + }, + infos = UI.Grid { + x = '52%', y = 2, + ex = -2, ey = 8, + disableHeader = true, + unfocusedBackgroundSelectedColor = colors.black, + inactive = true, + backgroundSelectedColor = colors.black, + columns = { + { key = 'name' }, + { key = 'value', align = 'right', textColor = colors.yellow }, + } + }, - progress = UI.ProgressBar { - x = 11, y = 10, - ex = -2, - }, - percentage = UI.Text { - x = 11, y = 11, - ex = -2, - align = 'center', - }, - icon = UI.NftImage { - x = 2, y = 10, - image = NFT.parse(NftImages.blank) - }, + progress = UI.ProgressBar { + x = 11, y = 10, + ex = -2, + }, + percentage = UI.Text { + x = 11, y = 11, + ex = -2, + align = 'center', + }, + icon = UI.NftImage { + x = 2, y = 10, + image = NFT.parse(NftImages.blank) + }, } local function getDrives() - local unique = { ['hdd'] = true, ['virt'] = true } - local exclude = {} - local drives = { - {name = 'hdd', side = ''}, - } - for _, drive in pairs(fs.list('/')) do - local side = fs.getDrive(drive) - if side and not unique[side] then - unique[side] = true - exclude[drive] = true - table.insert(drives, {name=drive, side=side}) - end - end - return drives, exclude + local unique = { ['hdd'] = true, ['virt'] = true } + local exclude = {} + local drives = { + {name = 'hdd', side = ''}, + } + for _, drive in pairs(fs.list('/')) do + local side = fs.getDrive(drive) + if side and not unique[side] then + unique[side] = true + exclude[drive] = true + table.insert(drives, {name=drive, side=side}) + end + end + return drives, exclude end local function getDriveInfo(p) - local files, dirs, total = 0, 0, 0 + local files, dirs, total = 0, 0, 0 - if p == "hdd" then p = "/" end - p = fs.combine(p, '') - local drive = fs.getDrive(p) + if p == "hdd" then p = "/" end + p = fs.combine(p, '') + local drive = fs.getDrive(p) - local function recurse(path) - if fs.getDrive(path) == drive then - if fs.isDir(path) then - if path ~= p then - total = total + 500 - dirs = dirs + 1 - end - for _, v in pairs(fs.list(path)) do - recurse(fs.combine(path, v)) - end - else - local sz = fs.getSize(path) + local function recurse(path) + if fs.getDrive(path) == drive then + if fs.isDir(path) then + if path ~= p then + total = total + 500 + dirs = dirs + 1 + end + for _, v in pairs(fs.list(path)) do + recurse(fs.combine(path, v)) + end + else + local sz = fs.getSize(path) + files = files + 1 + if drive == 'rom' then + total = total + sz + else + total = total + math.max(500, sz) + end + end + end + end - files = files + 1 - if drive == 'rom' then - total = total + sz - else - total = total + math.max(500, sz) - end - end - end - end + recurse(p) - recurse(p) - - local info = {} - table.insert(info, { name = 'Type', value = peripheral.getType(drive) or drive }) - table.insert(info, { name = 'Used', value = total }) - table.insert(info, { name = 'Total', value = total + fs.getFreeSpace(p) }) - table.insert(info, { name = 'Free', value = fs.getFreeSpace(p) }) - table.insert(info, { }) - table.insert(info, { name = 'Files', value = files }) - table.insert(info, { name = 'Dirs', value = dirs }) - return info, math.floor((total / (total + fs.getFreeSpace(p))) * 100) + local info = {} + table.insert(info, { name = 'Type', value = peripheral.getType(drive) or drive }) + table.insert(info, { name = 'Used', value = total }) + table.insert(info, { name = 'Total', value = total + fs.getFreeSpace(p) }) + table.insert(info, { name = 'Free', value = fs.getFreeSpace(p) }) + table.insert(info, { }) + table.insert(info, { name = 'Files', value = files }) + table.insert(info, { name = 'Dirs', value = dirs }) + return info, math.floor((total / (total + fs.getFreeSpace(p))) * 100) end function tab:updateInfo() - local selected = self.drives:getSelected() - local info, percent = getDriveInfo(selected and selected.name or self.drives.values[1].name) - self.infos:setValues(info) - self.progress.value = percent - self.percentage.value = ('%#3d%%'):format(percent) - self:draw() + local selected = self.drives:getSelected() + _syslog(selected) + local info, percent = getDriveInfo(selected and selected.name or self.drives.values[1].name) + self.infos:setValues(info) + self.progress.value = percent + self.percentage.value = ('%#3d%%'):format(percent) + self.icon.image = NFT.parse(NftImages[selected.name] or NftImages.blank) + self:draw() end function tab:updateDrives() - local drives, exclude = getDrives() - self.exclude = exclude - self.drives:setValues(drives) + local drives, exclude = getDrives() + self.exclude = exclude + self.drives:setValues(drives) end function tab:enable() - self:updateDrives() - self:updateInfo() - UI.Tab.enable(self) + self:updateDrives() + self:updateInfo() + UI.Tab.enable(self) end function tab:eventHandler(event) - if event.type == 'grid_focus_row' then - self:updateInfo() - end - return UI.Tab.eventHandler(self, event) + if event.type == 'grid_focus_row' then + self:updateInfo() + else return UI.Tab.eventHandler(self, event) + end + return true end Event.on({ 'disk', 'disk_eject' }, function() - os.sleep(1) - tab:updateDrives() - tab:updateInfo() - tab:sync() + os.sleep(1) + tab:updateDrives() + tab:updateInfo() + tab:sync() end) return tab -- 2.49.1 From 88d06267eadec8972d6a121971af617a0aac5c64 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Fri, 5 Jul 2019 19:10:44 -0400 Subject: [PATCH 043/236] More refactor --- sys/apps/Sniff.lua | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 8499c72..3e82605 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -9,7 +9,7 @@ local peripheral = _G.peripheral local multishell = _ENV.multishell local gridColumns = {} -table.insert(gridColumns, { heading = '#', key = 'id', width = 4, align = 'right' }) +table.insert(gridColumns, { heading = '#', key = 'id', width = 5, align = 'right' }) table.insert(gridColumns, { heading = 'Port', key = 'portid', width = 5, align = 'right' }) table.insert(gridColumns, { heading = 'Reply', key = 'replyid', width = 5, align = 'right' }) if UI.defaultDevice.width > 50 then table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' }) end @@ -17,7 +17,7 @@ table.insert(gridColumns, { heading = 'Msg', key = 'message' }) local page = UI.Page { paused = false, - index = 0, + index = 1, notification = UI.Notification { }, accelerators = { ['control-q'] = 'quit' }, @@ -266,6 +266,24 @@ function page.packetSlide:eventHandler(event) return true end +function page.packetGrid:addPacket(packet) + if not page.paused and (not filterConfig.filterAllCheck.value or filterConfig.filterGrid.values[packet.portid]) then + page.index = page.index + 1 + table.insert(self.values, packet) + end + if #self.values > self.maxPacket then + local t = { } + for i = 10, #self.values do + t[i - 9] = self.values[i] + end + self:setValues(t) + end + + self:update() + self:draw() + page:sync() +end + function page:enable() modemConfig.modems = {} peripheral.find('modem', function(side, dev) @@ -323,27 +341,14 @@ function page:eventHandler(event) end Event.on('modem_message', function(event, side, chan, reply, msg, dist) - if not page.paused and modemConfig.currentModem.side == side and (not filterConfig.filterAllCheck.value or filterConfig.filterGrid.values[chan]) then - page.index = page.index + 1 - table.insert(page.packetGrid.values, { + if modemConfig.currentModem.side == side then + page.packetGrid:addPacket({ id = page.index, portid = chan, replyid = reply, message = msg, distance = dist, }) - - if #page.packetGrid.values > page.packetGrid.maxPacket then - local t = { } - for i = 10, #page.packetGrid.values do - t[i - 9] = page.packetGrid.values[i] - end - page.packetGrid:setValues(t) - end - - page.packetGrid:update() - page.packetGrid:draw() - page:sync() end end) -- 2.49.1 From f1b9dcc4f4f48d9475fdf3869dc2d65e67c07ba1 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Fri, 5 Jul 2019 19:45:29 -0400 Subject: [PATCH 044/236] Last oopsie --- sys/apps/system/diskusage.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index c07c485..3749642 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -9,7 +9,7 @@ local peripheral = _G.peripheral local NftImages = { blank = '\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153', - disk = '\30\32\31\32\32\30\98\31\98\128\30\56\31\56\128\128\30\102\149\30\98\149\31\57\139\10\30\32\31\32\32\30\98\31\98\128\128\128\128\128\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128', + drive = '\30\32\31\32\32\30\98\31\98\128\30\56\31\56\128\128\30\102\149\30\98\149\31\57\139\10\30\32\31\32\32\30\98\31\98\128\128\128\128\128\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128', rom = '\30\57\31\57\128\31\56\144\144\144\144\144\31\57\128\10\30\56\31\57\157\30\55\31\55\128\128\128\128\128\30\57\31\56\145\10\30\57\31\56\136\30\55\31\55\128\30\55\31\48\82\79\77\30\55\128\30\57\31\56\132\10\30\56\31\57\157\30\55\31\55\128\128\128\128\128\30\57\31\56\145\10\30\57\31\57\128\31\56\129\129\129\129\129\31\57\128', hdd = '\30\32\31\32\32\30\55\31\55\128\30\48\135\131\139\30\55\128\10\30\32\31\32\32\30\48\31\55\149\31\48\128\30\55\131\30\48\128\30\55\149\10\30\32\31\32\32\30\55\31\48\130\30\48\31\55\144\30\56\31\48\133\30\55\159\129\10\30\32\31\32\32\30\56\31\55\149\129\142\159\30\55\128\10\30\32\31\32\32\30\57\31\55\143\143\143\143\143', } @@ -116,12 +116,11 @@ end function tab:updateInfo() local selected = self.drives:getSelected() - _syslog(selected) local info, percent = getDriveInfo(selected and selected.name or self.drives.values[1].name) self.infos:setValues(info) self.progress.value = percent self.percentage.value = ('%#3d%%'):format(percent) - self.icon.image = NFT.parse(NftImages[selected.name] or NftImages.blank) + self.icon.image = NFT.parse(NftImages[info[1].value] or NftImages.blank) self:draw() end -- 2.49.1 From ae2ea81d1d3720809bcbf269a74fd36e7900d480 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Sat, 6 Jul 2019 21:56:21 -0400 Subject: [PATCH 045/236] Show serialized packet in packetGrid --- sys/apps/Sniff.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 3e82605..b90bbe8 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -13,7 +13,7 @@ table.insert(gridColumns, { heading = '#', key = 'id', width = 5, align = 'righ table.insert(gridColumns, { heading = 'Port', key = 'portid', width = 5, align = 'right' }) table.insert(gridColumns, { heading = 'Reply', key = 'replyid', width = 5, align = 'right' }) if UI.defaultDevice.width > 50 then table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' }) end -table.insert(gridColumns, { heading = 'Msg', key = 'message' }) +table.insert(gridColumns, { heading = 'Msg', key = 'packetStr' }) local page = UI.Page { paused = false, @@ -269,6 +269,8 @@ end function page.packetGrid:addPacket(packet) if not page.paused and (not filterConfig.filterAllCheck.value or filterConfig.filterGrid.values[packet.portid]) then page.index = page.index + 1 + local p, res = pcall(textutils.serialize, packet.message) + packet.packetStr = res:gsub("\n%s*", "") table.insert(self.values, packet) end if #self.values > self.maxPacket then @@ -341,7 +343,7 @@ function page:eventHandler(event) end Event.on('modem_message', function(event, side, chan, reply, msg, dist) - if modemConfig.currentModem.side == side then + if modemConfig.currentModem.side == side then page.packetGrid:addPacket({ id = page.index, portid = chan, -- 2.49.1 From cf87c29cf62f5b15ae300e2700d176201aa3d120 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 6 Jul 2019 23:52:29 -0400 Subject: [PATCH 046/236] lint warnings + old file cleanup --- sys/apps/Sniff.lua | 8 +++++--- sys/autorun/upgraded.lua | 23 ++++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index b90bbe8..c0d299b 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -12,7 +12,9 @@ local gridColumns = {} table.insert(gridColumns, { heading = '#', key = 'id', width = 5, align = 'right' }) table.insert(gridColumns, { heading = 'Port', key = 'portid', width = 5, align = 'right' }) table.insert(gridColumns, { heading = 'Reply', key = 'replyid', width = 5, align = 'right' }) -if UI.defaultDevice.width > 50 then table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' }) end +if UI.defaultDevice.width > 50 then + table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' }) +end table.insert(gridColumns, { heading = 'Msg', key = 'packetStr' }) local page = UI.Page { @@ -269,7 +271,7 @@ end function page.packetGrid:addPacket(packet) if not page.paused and (not filterConfig.filterAllCheck.value or filterConfig.filterGrid.values[packet.portid]) then page.index = page.index + 1 - local p, res = pcall(textutils.serialize, packet.message) + local _, res = pcall(textutils.serialize, packet.message) packet.packetStr = res:gsub("\n%s*", "") table.insert(self.values, packet) end @@ -342,7 +344,7 @@ function page:eventHandler(event) return true end -Event.on('modem_message', function(event, side, chan, reply, msg, dist) +Event.on('modem_message', function(_, side, chan, reply, msg, dist) if modemConfig.currentModem.side == side then page.packetGrid:addPacket({ id = page.index, diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index 6cb1c92..96bd5f1 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -1,13 +1,14 @@ -if fs.exists('sys/apps/shell') and fs.exists('sys/apps/shell.lua') then - fs.delete('sys/apps/shell') -end -if fs.exists('sys/etc/app.db') then fs.delete('sys/etc/app.db') end -if fs.exists('sys/extensions') then fs.delete('sys/extensions') end -if fs.exists('sys/network') then fs.delete('sys/network') end -if fs.exists('startup') then fs.delete('startup') end +local fs = _G.fs -if fs.exists('sys/autorun/gps.lua') then fs.delete('sys/autorun/gps.lua') end -if fs.exists('sys/autorun/gpsHost.lua') then fs.delete('sys/autorun/gpsHost.lua') end -if fs.exists('sys/apps/network/redserver.lua') then fs.delete('sys/apps/network/redserver.lua') end +-- cleanup outdated files +fs.delete('sys/apps/shell') +fs.delete('sys/etc/app.db') +fs.delete('sys/extensions') +fs.delete('sys/network') +fs.delete('startup') +fs.delete('sys/apps/system/turtle.lua') +fs.delete('sys/autorun/gps.lua') +fs.delete('sys/autorun/gpsHost.lua') +fs.delete('sys/apps/network/redserver.lua') if fs.exists('sys/apis') then fs.delete('sys/apis') end -if fs.exists('sys/autorun/apps.lua') then fs.delete('sys/autorun/apps.lua') end +fs.delete('sys/autorun/apps.lua') -- 2.49.1 From 86cd6e3c0e13fb3ea0e16bf26c05e3b29b0db871 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 7 Jul 2019 01:14:39 -0400 Subject: [PATCH 047/236] app icons --- sys/apps/system/diskusage.lua | 12 ++++++------ sys/autorun/upgraded.lua | 2 +- sys/etc/apps.db | 6 ++++++ sys/modules/opus/packages.lua | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 3749642..99e3547 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -20,7 +20,7 @@ local tab = UI.Tab { drives = UI.ScrollingGrid { x = 2, y = 1, - ex = '47%', ey = 8, + ex = '47%', ey = -7, columns = { { heading = 'Drive', key = 'name' }, { heading = 'Side' ,key = 'side', textColor = colors.yellow } @@ -29,7 +29,7 @@ local tab = UI.Tab { }, infos = UI.Grid { x = '52%', y = 2, - ex = -2, ey = 8, + ex = -2, ey = -4, disableHeader = true, unfocusedBackgroundSelectedColor = colors.black, inactive = true, @@ -41,16 +41,16 @@ local tab = UI.Tab { }, progress = UI.ProgressBar { - x = 11, y = 10, + x = 11, y = -2, ex = -2, }, percentage = UI.Text { - x = 11, y = 11, + x = 11, y = -1, ex = -2, align = 'center', }, icon = UI.NftImage { - x = 2, y = 10, + x = 2, y = -5, image = NFT.parse(NftImages.blank) }, } @@ -151,4 +151,4 @@ Event.on({ 'disk', 'disk_eject' }, function() tab:sync() end) -return tab +return tab \ No newline at end of file diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index 96bd5f1..f6881db 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -8,7 +8,7 @@ fs.delete('sys/network') fs.delete('startup') fs.delete('sys/apps/system/turtle.lua') fs.delete('sys/autorun/gps.lua') -fs.delete('sys/autorun/gpsHost.lua') +fs.delete('sys/autorun/gpshost.lua') fs.delete('sys/apps/network/redserver.lua') if fs.exists('sys/apis') then fs.delete('sys/apis') end fs.delete('sys/autorun/apps.lua') diff --git a/sys/etc/apps.db b/sys/etc/apps.db index d7ea0dd..951778a 100644 --- a/sys/etc/apps.db +++ b/sys/etc/apps.db @@ -100,6 +100,12 @@ \030 \031f\030f\0318\143\133\0312\136\0302\031f\159\159\143\131\030f\0312\132", run = "pain", }, + [ "6a381ca189cbddd63737cbaf6e8b593844ce467ba52b1c5e5e05d8f29864385d" ] = { + title = "Sniff", + category = "Apps", + icon = "\31\102\128\128\128\128\31\53\149\30\53\31\102\154\30\102\31\53\137\10\30\52\31\102\159\31\52\128\128\30\102\144\31\53\130\30\53\31\102\155\140\10\31\52\151\30\52\31\102\148\30\102\31\52\151\30\52\31\102\148\30\102\128\128\128", + run = "Sniff.lua", + }, [ "01c933b2a36ad8ed2d54089cb2903039046c1216" ] = { title = "Enchat", icon = "\030e\031f\151\030f\031e\156\0311\140\0314\140\0315\140\031d\140\031b\140\031a\132\ diff --git a/sys/modules/opus/packages.lua b/sys/modules/opus/packages.lua index a3890da..0c5187a 100644 --- a/sys/modules/opus/packages.lua +++ b/sys/modules/opus/packages.lua @@ -33,7 +33,7 @@ end function Packages:downloadList() local packages = { - [ 'develop-1.8' ] = 'https://pastebin.com/raw/WhEiNGZE', + [ 'develop-1.8' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/packages.list', [ 'master-1.8' ] = 'https://pastebin.com/raw/pexZpAxt', } -- 2.49.1 From ccc7693f460cefd602dde746c8eb3147ad267319 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 8 Jul 2019 00:16:28 -0400 Subject: [PATCH 048/236] minor tweaks --- sys/apps/Sniff.lua | 2 +- sys/apps/network/snmp.lua | 2 ++ sys/etc/apps.db | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index c0d299b..6a683eb 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -351,7 +351,7 @@ Event.on('modem_message', function(_, side, chan, reply, msg, dist) portid = chan, replyid = reply, message = msg, - distance = dist, + distance = dist or -1, }) end end) diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index ff42b99..ad976a4 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -215,3 +215,5 @@ Event.on('turtle_response', function() sendInfo() end end) + +sendInfo() diff --git a/sys/etc/apps.db b/sys/etc/apps.db index 951778a..edfabba 100644 --- a/sys/etc/apps.db +++ b/sys/etc/apps.db @@ -101,7 +101,7 @@ run = "pain", }, [ "6a381ca189cbddd63737cbaf6e8b593844ce467ba52b1c5e5e05d8f29864385d" ] = { - title = "Sniff", + title = "Sniffer", category = "Apps", icon = "\31\102\128\128\128\128\31\53\149\30\53\31\102\154\30\102\31\53\137\10\30\52\31\102\159\31\52\128\128\30\102\144\31\53\130\30\53\31\102\155\140\10\31\52\151\30\52\31\102\148\30\102\31\52\151\30\52\31\102\148\30\102\128\128\128", run = "Sniff.lua", -- 2.49.1 From 4c2d1215627e6e9b8a63130c2655be89b0ff2512 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 8 Jul 2019 13:52:53 -0400 Subject: [PATCH 049/236] reduce net traffic --- sys/apps/network/snmp.lua | 54 +++++++++++++++++++---------------- sys/apps/system/diskusage.lua | 23 +++++++-------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index ad976a4..0fc9a34 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -121,8 +121,7 @@ print('discovery: listening on port 999') Event.on('modem_message', function(_, _, sport, id, info, distance) if sport == 999 and tonumber(id) and type(info) == 'table' then - if info.label and info.id and - type(info.label) == 'string' and type(info.id) == 'number' then + if type(info.label) == 'string' and type(info.id) == 'number' then if not network[id] then network[id] = { } @@ -150,6 +149,15 @@ local info = { } local infoTimer = os.clock() +local function getSlots() + return Util.reduce(turtle.getInventory(), function(acc, v) + if v.count > 0 then + acc[v.index .. ',' .. v.count] = v.key + end + return acc + end, { }) +end + local function sendInfo() if os.clock() - infoTimer >= 1 then -- don't flood infoTimer = os.clock() @@ -160,7 +168,7 @@ local function sendInfo() info.fuel = turtle.getFuelLevel() info.status = turtle.getStatus() info.point = turtle.point - info.inventory = turtle.getInventory() + info.inv = getSlots() info.slotIndex = turtle.getSelectedSlot() end if device.neuralInterface then @@ -168,28 +176,24 @@ local function sendInfo() if not info.status and device.neuralInterface.getMetaOwner then pcall(function() local meta = device.neuralInterface.getMetaOwner() - - if meta.isWet then - info.status = 'Swimming' - elseif meta.isElytraFlying then - info.status = 'Flying' - elseif meta.isBurning then - info.status = 'Burning' - elseif meta.isDead then - info.status = 'Deceased' - elseif meta.isOnLadder then - info.status = 'Climbing' - elseif meta.isRiding then - info.status = 'Riding' - elseif meta.isSneaking then - info.status = 'Sneaking' - elseif meta.isSprinting then - info.status = 'Running' - else - info.status = 'health: ' .. - math.floor(meta.health / - meta.maxHealth * 100) + local states = { + isWet = 'Swimming', + isElytraFlying = 'Flying', + isBurning = 'Burning', + isDead = 'Deceased', + isOnLadder = 'Climbing', + isRiding = 'Riding', + isSneaking = 'Sneaking', + isSprinting = 'Running', + } + for k,v in pairs(states) do + if meta[k] then + info.status = v + break + end end + info.status = info.status or 'health: ' .. + math.floor(meta.health / meta.maxHealth * 100) end) end end @@ -216,4 +220,4 @@ Event.on('turtle_response', function() end end) -sendInfo() +Event.onTimeout(1, sendInfo) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 99e3547..8fbdd71 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -45,8 +45,8 @@ local tab = UI.Tab { ex = -2, }, percentage = UI.Text { - x = 11, y = -1, - ex = -2, + x = 11, y = -3, + ex = '47%', align = 'center', }, icon = UI.NftImage { @@ -57,19 +57,16 @@ local tab = UI.Tab { local function getDrives() local unique = { ['hdd'] = true, ['virt'] = true } - local exclude = {} - local drives = { - {name = 'hdd', side = ''}, - } + local drives = { { name = 'hdd', side = '' } } + for _, drive in pairs(fs.list('/')) do local side = fs.getDrive(drive) if side and not unique[side] then unique[side] = true - exclude[drive] = true - table.insert(drives, {name=drive, side=side}) + table.insert(drives, { name = drive, side = side }) end end - return drives, exclude + return drives end local function getDriveInfo(p) @@ -125,8 +122,7 @@ function tab:updateInfo() end function tab:updateDrives() - local drives, exclude = getDrives() - self.exclude = exclude + local drives = getDrives() self.drives:setValues(drives) end @@ -139,7 +135,8 @@ end function tab:eventHandler(event) if event.type == 'grid_focus_row' then self:updateInfo() - else return UI.Tab.eventHandler(self, event) + else + return UI.Tab.eventHandler(self, event) end return true end @@ -151,4 +148,4 @@ Event.on({ 'disk', 'disk_eject' }, function() tab:sync() end) -return tab \ No newline at end of file +return tab -- 2.49.1 From 8f176572633f2026f224b09eef5124a994011c4b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 10 Jul 2019 11:06:29 -0600 Subject: [PATCH 050/236] make page/window themeable - oops --- sys/modules/opus/ui.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 50864c7..5f2245e 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -1217,6 +1217,8 @@ end loadComponents() UI:loadTheme('usr/config/ui.theme') +Util.merge(UI.Window.defaults, UI.theme.Window) +Util.merge(UI.Page.defaults, UI.theme.Page) UI:setDefaultDevice(UI.Device({ device = term.current() })) return UI -- 2.49.1 From 0e1b712adcbb1f4c2a2c44658efb64ea82463230 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 10 Jul 2019 19:02:46 -0600 Subject: [PATCH 051/236] ntf transparency --- sys/modules/opus/nft.lua | 7 ++++--- sys/modules/opus/ui.lua | 8 +++++--- sys/modules/opus/ui/components/NftImage.lua | 5 ++++- sys/modules/opus/util.lua | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/sys/modules/opus/nft.lua b/sys/modules/opus/nft.lua index f6d5468..52d5c6c 100644 --- a/sys/modules/opus/nft.lua +++ b/sys/modules/opus/nft.lua @@ -50,9 +50,10 @@ function NFT.parse(imageText) currFG = getColourOf(nextChar) fgNext = false else - if nextChar ~= " " and currFG == nil then - currFG = _G.colors.white - end + --if nextChar ~= " " and currFG == nil then + -- any color not in range is considered transparent + -- currFG = _G.colors.white + --end image.bg[num][writeIndex] = currBG image.fg[num][writeIndex] = currFG image.text[num][writeIndex] = nextChar diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 5f2245e..0acfb16 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -912,10 +912,12 @@ function UI.Window:emit(event) end end +function UI.Window:getProperty(property) + return self[property] or self.parent and self.parent:getProperty(property) +end + function UI.Window:find(uid) - if self.children then - return Util.find(self.children, 'uid', uid) - end + return self.children and Util.find(self.children, 'uid', uid) end function UI.Window:eventHandler() diff --git a/sys/modules/opus/ui/components/NftImage.lua b/sys/modules/opus/ui/components/NftImage.lua index 3457711..4d295d7 100644 --- a/sys/modules/opus/ui/components/NftImage.lua +++ b/sys/modules/opus/ui/components/NftImage.lua @@ -17,9 +17,12 @@ end function UI.NftImage:draw() if self.image then + -- due to blittle, the background and foreground transparent + -- color is the same as the background color + local bg = self:getProperty('backgroundColor') for y = 1, self.image.height do for x = 1, #self.image.text[y] do - self:write(x, y, self.image.text[y][x], self.image.bg[y][x], self.image.fg[y][x]) + self:write(x, y, self.image.text[y][x], self.image.bg[y][x], self.image.fg[y][x] or bg) end end else diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index e670857..2c8e097 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -412,8 +412,8 @@ function Util.first(t, order) end --[[ File functions ]]-- -function Util.readFile(fname) - local f = fs.open(fname, "r") +function Util.readFile(fname, flags) + local f = fs.open(fname, flags or "r") if f then local t = f.readAll() f.close() -- 2.49.1 From e9559165a443d4df65a279668dd774be790bc521 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 11 Jul 2019 00:42:57 -0600 Subject: [PATCH 052/236] icon transparency --- sys/apps/Overview.lua | 4 --- sys/etc/apps.db | 69 ++++++++++++---------------------------- sys/etc/fstab | 3 +- sys/modules/opus/nft.lua | 37 +++++++++------------ 4 files changed, 37 insertions(+), 76 deletions(-) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index bfcbbb3..8cd2c92 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -155,10 +155,6 @@ local page = UI.Page { }, } -if extSupport then - page.container.backgroundColor = colors.black -end - local function loadApplications() local requirements = { turtle = not not turtle, diff --git a/sys/etc/apps.db b/sys/etc/apps.db index edfabba..7bf79ab 100644 --- a/sys/etc/apps.db +++ b/sys/etc/apps.db @@ -3,17 +3,13 @@ title = "Packages", category = "System", run = "PackageManager.lua", - iconExt = "\030c\0317\151\131\131\131\0307\031c\148\ -\030c\0317\151\131\0310\143\0317\131\0307\031c\148\ -\0307\031c\138\030f\0317\151\131\131\131", + iconExt = "\030c\0317\151\131\131\131\0307\031c\148\010\030c\0317\151\131\0310\143\0317\131\0307\031c\148\010\0307\031c\138\030 \0317\151\131\131\131", }, [ "b2efeaa1a7d6d2185ea02473cf758203dfcea3fe" ] = { title = "Cloud", category = "Apps", run = "cshell.lua", - iconExt = "\0300\031f\159\131\135\0310\128\128\031f\139\131\030f\0310\144\ -\0300\128\031f\137\144\0310\128\030a\136\149\133\0300\128\ -\0300\031f\144\134\136\132\031a\142\138\138\030f\0310\159", + iconExt = "\0300\031 \159\131\135\0310\128\128\031 \139\131\030 \0310\144\010\0300\128\031f\137\144\0310\128\030a\136\149\133\0300\128\010\0300\031 \144\031f\134\136\132\031a\142\138\138\030 \0310\159", }, [ "53ebc572b4a44802ba114729f07bdaaf5409a9d7" ] = { title = "Network", @@ -21,9 +17,7 @@ icon = "\0304 \030 \ \030f \0304 \0307 \030 \031 \031f)\ \030f \0304 \0307 \030 \031f)", - iconExt = "\030 \031f \0305\031f\140\030f\0315\137\144\ -\030 \031f\030f\0314\131\131\0304\031f\148\030 \0305\155\150\149\ -\030 \031f\030f\0310\147\0300\031f\141\0304\149\0307\0318\149\030 ", + iconExt = "\030 \031 \128\128\128\128\0305\140\030 \0315\137\144\010\0314\131\131\0304\031f\148\030 \031 \128\0305\155\150\149\010\147\0300\031f\141\0304\149\0307\0318\149\030 \031 \128\128\128", run = "Network.lua", }, c7116629a6a855cb774d9c7c8ad822fd83c71fb5 = { @@ -32,9 +26,7 @@ icon = "\0304\031f \030f\0310o..\0304\031f \ \0304\031f \030f\0310.o.\0304\031f \ \0304\031f - ", - iconExt = "\0307\031f\135\0300\0317\159\0307\0310\144\031f\139\ -\0300\0317\131\0307\0310\147\0300\0317\156\131\ -\130\143\143\129", + iconExt = "\0307\031 \135\0300\0317\159\0307\0310\144\031 \139\010\0300\0317\131\0307\0310\147\0300\0317\156\131\010\030 \130\143\143\129", run = "rom/programs/reboot", }, fb91e24fa52d8d2b32937bf04d843f730319a902 = { @@ -43,9 +35,7 @@ icon = "\0301\03171\03180\030 \031 \ \0301\03181\030 \031 \ \0301\03170\03180\03171\0307\031f>", - iconExt = "\031f\128\0313\152\131\131\132\031f\128\ -\0313\139\159\129\0303\031f\159\129\139\ -\031f\128\0313\136\0303\031f\143\143\030f\0313\134\031f\128", + iconExt = "\030 \031 \128\0313\152\131\131\132\031 \128\010\030 \0313\139\159\129\0303\031 \159\129\139\010\030 \031 \128\0313\136\0303\031 \143\143\030 \0313\134\031 \128", run = "update update", }, c47ae15370cfe1ed2781eedc1dc2547d12d9e972 = { @@ -54,9 +44,7 @@ icon = " \031f?\031 \ \031f?\031 \ \031f?", - iconExt = "\0300\031f\129\030f\0310\131\0300\031f\148\030f\0310\148\ -\030 \031 \0300\031f\131\030f\0310\142\129\ -\030 \031 \0300\031f\131\030f\128", + iconExt = "\0300\031 \129\030 \0310\131\0300\031 \148\030 \0310\148\010\030 \031 \128\0300\131\030 \0310\142\129\010\030 \031 \128\0300\131\030 \128\128", run = "Help.lua", }, b0832074630eb731d7fbe8074de48a90cd9bb220 = { @@ -65,9 +53,7 @@ icon = "\030f \ \030f\0310lua>\031 \ \030f ", - iconExt = "\0300\031f\151\030f\128\0300\159\159\159\030f\0310\144\0304\031f\159\030f\128\ -\0300\031f\149\030f\128\0300\149\149\151\145\030f\128\0314\153\ -\130\131\130\131\130\131\0314\130\031f\128", + iconExt = "\0300\031 \151\030 \128\0300\159\159\159\030 \0310\144\0304\031 \159\030 \128\010\0300\031 \149\030 \128\0300\149\149\151\145\030 \128\0314\153\010\030 \130\131\130\131\130\131\0314\130\031 \128", run = "Lua.lua", }, bc0792d8dc81e8aa30b987246a5ce97c40cd6833 = { @@ -76,9 +62,7 @@ icon = " \0307\031f| \ \0307\031f---o\030 \031 \ \0307\031f| ", - iconExt = "\0318\138\0308\031f\130\0318\128\031f\129\030f\0318\133\ -\0318\143\0308\128\0317\143\0318\128\030f\143\ -\0318\138\135\143\139\133", + iconExt = "\030 \0318\138\0308\031 \130\0318\128\031 \129\030 \0318\133\010\030 \0318\143\0308\128\0317\143\0318\128\030 \143\010\030 \0318\138\135\143\139\133", run = "System.lua", }, [ "2a4d562b1d9a9c90bdede6fac8ce4f7402462b86" ] = { @@ -87,23 +71,20 @@ icon = "\030f\031f \0315/\ \030f\031f \0315/\\/ \ \030f\0315/\031f ", - iconExt = "\031f\128\128\0305\159\030f\128\0305\159\030f\0315\134\031f\128\ -\031f\128\0315\152\129\137\0305\031f\158\139\030f\0317 \ -\0315\134\031f\128\128\128\128\0305\154\030f\0317 ", + iconExt = "\030 \031 \128\128\0305\159\030 \128\0305\159\030 \0315\134\031 \128\010\030 \031 \128\0315\152\129\137\0305\031 \158\139\030 \128\010\030 \0315\134\031 \128\128\128\128\0305\154\030 \128", run = "Tasks.lua", }, [ "a0365977708b7387ee9ce2c13e5820e6e11732cb" ] = { title = "Pain", category = "Apps", - icon = "\030 \031f\0307\031f\159\030 \159\030 \ -\030 \031f\0308\031f\135\0307\0318\144\140\030f\0317\159\143\031c\139\0302\135\030f\0312\157\ -\030 \031f\030f\0318\143\133\0312\136\0302\031f\159\159\143\131\030f\0312\132", + iconExt = "\0307\031 \159\030 \128\128\128\128\128\128\128\010\0308\031 \135\0307\0318\144\140\030 \0317\159\143\031c\139\0302\135\030 \0312\157\010\030 \0318\143\133\0312\136\0302\031 \159\159\143\131\030 \0312\132", run = "pain", }, [ "6a381ca189cbddd63737cbaf6e8b593844ce467ba52b1c5e5e05d8f29864385d" ] = { title = "Sniffer", category = "Apps", icon = "\31\102\128\128\128\128\31\53\149\30\53\31\102\154\30\102\31\53\137\10\30\52\31\102\159\31\52\128\128\30\102\144\31\53\130\30\53\31\102\155\140\10\31\52\151\30\52\31\102\148\30\102\31\52\151\30\52\31\102\148\30\102\128\128\128", + iconExt = "\030 \031 \128\128\128\128\0315\149\0305\031 \154\030 \0315\137\010\0304\031 \159\0314\128\128\030 \144\0315\130\0305\031 \155\140\010\0314\151\0304\031f\148\030f\0314\151\0304\031f\148\030 \031 \128\128\128", run = "Sniff.lua", }, [ "01c933b2a36ad8ed2d54089cb2903039046c1216" ] = { @@ -120,9 +101,7 @@ icon = "\0300\0317==\031 \0307 \ \0300\0317====\ \0300\0317====", - iconExt = "\030 \031f\0300\031f\136\140\132\0308\130\030f\0318\144\ -\030 \031f\030f\0310\157\0300\031f\147\030f\0310\142\143\149\ -\030 \031f\0300\031f\136\140\132\140\030f\0310\149", + iconExt = "\0300\031f\136\140\132\0308\031 \130\030 \0318\144\010\157\0300\031f\147\030f\0310\142\143\030 \149\010\0300\031f\136\140\132\140\030 \0310\149", run = "Files.lua", }, [ "7fddb7d8d1d60b1eeefa9af01082e0811d4b484d" ] = { @@ -131,23 +110,19 @@ icon = "\0304\031f \ \0304\031f \030f\0310zz\031 \ \0304\031f \030f ", - iconExt = "\030e\031f\135\030f\031e\148\030e\128\031f\151\139\ -\030e\031e\128\030f\031f\128\031e\143\031f\128\030e\031e\128\ -\031e\139\030e\031f\130\131\129\030f\031e\135", + iconExt = "\030e\031 \135\030 \031e\148\030e\128\031 \151\139\010\030e\031e\128\030 \031 \128\031e\143\031 \128\030e\031e\128\010\030 \031e\139\030e\031 \130\131\129\030 \031e\135", run = "/rom/programs/shutdown", }, - bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d = { + [ "bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d" ] = { title = "Shell", category = "Apps", icon = "\0304 \030 \ \0304 \030f\0314> \0310_\031 \ \0304 \030f \030 ", - iconExt = "\030f\0314\151\131\131\131\131\ -\030f\0314\149\030f\0314> \0310_ \ -\030f\0314\149\030f ", + iconExt = "\030f\0314\151\131\131\131\131\010\030f\0314\149\030f\0314> \0310_ \010\030f\0314\149\030f ", run = "shell", }, - b77aad5fb24921ef76ac8f3e500ed93fddae8f2a = { + [ "b77aad5fb24921ef76ac8f3e500ed93fddae8f2a" ] = { title = "Redirection", category = "Games", icon = "\0307 \0308 \0307 \ @@ -156,7 +131,7 @@ run = "rom/programs/fun/advanced/redirection", requires = 'advanced', }, - f39d173d91c22348565c20283b89d4d1cabd3b7e = { + [ "f39d173d91c22348565c20283b89d4d1cabd3b7e" ] = { title = "Falling", category = "Games", icon = "\030f \0302 \ @@ -165,7 +140,7 @@ run = "rom/programs/pocket/falling", requires = 'advancedPocket', }, - db56e2e1db9f7accfc37f2b132d27505c66ba521 = { + [ "db56e2e1db9f7accfc37f2b132d27505c66ba521" ] = { title = "Adventure", category = "Games", icon = "\030f\0310You \031 \ @@ -179,9 +154,7 @@ icon = "\030d \030 \030e \030 \ \030d \030 \ \030d ", - iconExt = "\030 \031f\0305\031f\151\030f\0315\135\131\0305\031f\146\ -\030 \031f\030f\0315\130\141\0305\031f\139\030f\0315\130\ -\030 \031f\0305\031f\146\143\030f\0315\158\031e\130", + iconExt = "\0305\031 \151\030 \0315\135\131\0305\031 \146\010\030 \0315\130\141\0305\031 \139\030 \0315\130\010\0305\031 \146\143\030 \0315\158\031e\130", run = "/rom/programs/fun/worm", }, [ "9f46ca3ef617166776ef6014a58d4e66859caa62" ] = { @@ -190,9 +163,7 @@ icon = " \030f \ \030f \0307 \ \030f \0307 \0300 ", - iconExt = "\031f\128\0307\143\131\131\131\131\143\030f\128\ -\0307\031f\129\0317\128\0319\136\0309\031b\136\132\0307\0319\132\0317\128\031f\130\ -\0317\130\143\0307\128\128\128\128\030f\143\129", + iconExt = "\030 \031 \128\0307\143\131\131\131\131\143\030 \128\010\0307\031 \129\0317\128\0319\136\0309\031b\136\132\0307\0319\132\0317\128\031 \130\010\030 \0317\130\143\0307\128\128\128\128\030 \143\129", run = "/rom/programs/fun/dj", }, } diff --git a/sys/etc/fstab b/sys/etc/fstab index deeac19..235ecf0 100644 --- a/sys/etc/fstab +++ b/sys/etc/fstab @@ -1,4 +1,5 @@ sys/apps/pain.lua urlfs https://github.com/LDDestroier/CC/raw/master/pain.lua sys/apps/update.lua urlfs http://pastebin.com/raw/UzGHLbNC sys/apps/Enchat.lua urlfs https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua -sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua \ No newline at end of file +sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua +sys/apps/ntftrans.lua urlfs https://pastebin.com/raw/e8XrzeDY \ No newline at end of file diff --git a/sys/modules/opus/nft.lua b/sys/modules/opus/nft.lua index 52d5c6c..4fd8d02 100644 --- a/sys/modules/opus/nft.lua +++ b/sys/modules/opus/nft.lua @@ -34,30 +34,23 @@ function NFT.parse(imageText) --As we're no longer 1-1, we keep track of what index to write to local writeIndex = 1 --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour - local bgNext, fgNext = false, false - --The current background and foreground colours - local currBG, currFG = nil,nil - for i = 1, #sLine do - local nextChar = string.sub(sLine, i, i) - if nextChar:byte() == 30 then - bgNext = true - elseif nextChar:byte() == 31 then - fgNext = true - elseif bgNext then - currBG = getColourOf(nextChar) - bgNext = false - elseif fgNext then - currFG = getColourOf(nextChar) - fgNext = false + + local tcol, bcol = colors.white,colors.black + local cx, sx = 1, 0 + while sx < #sLine do + sx = sx + 1 + if sLine:sub(sx,sx) == "\30" then + bcol = getColourOf(sLine:sub(sx+1,sx+1)) + sx = sx + 1 + elseif sLine:sub(sx,sx) == "\31" then + tcol = getColourOf(sLine:sub(sx+1,sx+1)) + sx = sx + 1 else - --if nextChar ~= " " and currFG == nil then - -- any color not in range is considered transparent - -- currFG = _G.colors.white - --end - image.bg[num][writeIndex] = currBG - image.fg[num][writeIndex] = currFG - image.text[num][writeIndex] = nextChar + image.bg[num][writeIndex] = bcol + image.fg[num][writeIndex] = tcol + image.text[num][writeIndex] = sLine:sub(sx,sx) writeIndex = writeIndex + 1 + cx = cx + 1 end end image.height = num -- 2.49.1 From 43c86c263ba80f5647bd9a6caf797edfa0ae6764 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 11 Jul 2019 10:36:17 -0600 Subject: [PATCH 053/236] upper/lowercase transformations for TextEntry --- sys/apps/Help.lua | 1 + sys/modules/opus/ui/components/TextEntry.lua | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index abb6099..72b5308 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -21,6 +21,7 @@ local page = UI.Page { filter = UI.TextEntry { x = 10, y = 2, ex = -3, limit = 32, + transform = 'lowercase', }, grid = UI.ScrollingGrid { y = 4, diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 512f668..09631ef 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -5,6 +5,8 @@ local Util = require('opus.util') local colors = _G.colors local _rep = string.rep +local _lower = string.lower +local _upper = string.upper UI.TextEntry = class(UI.Window) UI.TextEntry.defaults = { @@ -104,12 +106,21 @@ function UI.TextEntry:focus() end end +function UI.TextEntry:_transform(text) + if self.transform == 'lowercase' then + return _lower(text) + elseif self.transform == 'uppercase' then + return _upper(text) + end + return text +end + function UI.TextEntry:eventHandler(event) local text = self.value self.entry.value = tostring(text) if event.ie and self.entry:process(event.ie) then if self.entry.textChanged then - self.value = self.entry.value + self.value = self:_transform(self.entry.value) self:draw() if text ~= self.value then self:emit({ type = 'text_change', text = self.value, element = self }) -- 2.49.1 From 51e40a1dd73ed14c8188fbc1554f3d506853ce22 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 12 Jul 2019 02:59:36 -0600 Subject: [PATCH 054/236] Anavrins Slider component --- sys/modules/opus/ui/components/Slider.lua | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 sys/modules/opus/ui/components/Slider.lua diff --git a/sys/modules/opus/ui/components/Slider.lua b/sys/modules/opus/ui/components/Slider.lua new file mode 100644 index 0000000..5966160 --- /dev/null +++ b/sys/modules/opus/ui/components/Slider.lua @@ -0,0 +1,48 @@ +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') + +local colors = _G.colors + +UI.Slider = class(UI.Window) +UI.Slider.defaults = { + UIElement = 'Slider', + height = 1, + barChar = UI.extChars and '\140' or '-', + barColor = colors.gray, + sliderChar = UI.extChars and '\143' or '\124', + sliderColor = colors.blue, + leftBorder = '\141', + rightBorder = '\142', + value = 0, + min = 0, + max = 100, + event = 'slider_update', +} +function UI.Slider:draw() + local range = self.max - self.min + local perc = (self.value - self.min) / range + local progress = Util.clamp(1 + self.width * perc, 1, self.width) + + local bar = { } + for i = 1, self.width do + local filler = + i == 1 and self.leftBorder or + i == self.width and self.rightBorder or + self.barChar + + table.insert(bar, filler) + end + self:write(1, 1, table.concat(bar), nil, self.barColor) + self:write(progress, 1, self.sliderChar, nil, self.sliderColor) +end + +function UI.Slider:eventHandler(event) + if event.type == "mouse_down" or event.type == "mouse_drag" then + local range = self.max - self.min + local i = (event.x - 1) / (self.width - 1) + self.value = self.min + (i * range) + self:emit({ type = self.event, value = self.value, element = self }) + self:draw() + end +end -- 2.49.1 From c43fe19f1d82eee4b39cb64ef7ce3522da0041f4 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 12 Jul 2019 12:47:51 -0600 Subject: [PATCH 055/236] sliders in forms + form component setValue (hopefully didnt break anything) --- sys/modules/opus/ui/components/Form.lua | 9 ++--- sys/modules/opus/ui/components/Slider.lua | 35 ++++++++++++++++++-- sys/modules/opus/ui/components/TextEntry.lua | 4 +-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/sys/modules/opus/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua index f1eb6f3..68b59d3 100644 --- a/sys/modules/opus/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -29,10 +29,11 @@ function UI.Form:setValues(values) self.values = values for _,child in pairs(self.children) do if child.formKey then - -- this should be child:setValue(self.values[child.formKey]) - -- so chooser can set default choice if null - -- null should be valid as well - child.value = self.values[child.formKey] or '' + if child.setValue then + child:setValue(self.values[child.formKey]) + else + child.value = self.values[child.formKey] or '' + end end end end diff --git a/sys/modules/opus/ui/components/Slider.lua b/sys/modules/opus/ui/components/Slider.lua index 5966160..9dee765 100644 --- a/sys/modules/opus/ui/components/Slider.lua +++ b/sys/modules/opus/ui/components/Slider.lua @@ -12,13 +12,31 @@ UI.Slider.defaults = { barColor = colors.gray, sliderChar = UI.extChars and '\143' or '\124', sliderColor = colors.blue, - leftBorder = '\141', - rightBorder = '\142', + sliderFocusColor = colors.lightBlue, + leftBorder = UI.extChars and '\141' or '\124', + rightBorder = UI.extChars and '\142' or '\124', value = 0, min = 0, max = 100, event = 'slider_update', + accelerators = { + right = 'slide_right', + left = 'slide_left', + } } +function UI.Slider:setValue(value) + self.value = tonumber(value) or self.min +end + +function UI.Slider:reset() -- form support + self.value = self.min + self:draw() +end + +function UI.Slider:focus() + self:draw() +end + function UI.Slider:draw() local range = self.max - self.min local perc = (self.value - self.min) / range @@ -34,7 +52,7 @@ function UI.Slider:draw() table.insert(bar, filler) end self:write(1, 1, table.concat(bar), nil, self.barColor) - self:write(progress, 1, self.sliderChar, nil, self.sliderColor) + self:write(progress, 1, self.sliderChar, nil, self.focused and self.sliderFocusColor or self.sliderColor) end function UI.Slider:eventHandler(event) @@ -44,5 +62,16 @@ function UI.Slider:eventHandler(event) self.value = self.min + (i * range) self:emit({ type = self.event, value = self.value, element = self }) self:draw() + + elseif event.type == 'slide_left' or event.type == 'slide_right' then + local range = self.max - self.min + local step = range / self.width + if event.type == 'slide_left' then + self.value = Util.clamp(self.value - step, self.min, self.max) + else + self.value = Util.clamp(self.value + step, self.min, self.max) + end + self:emit({ type = self.event, value = self.value, element = self }) + self:draw() end end diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 09631ef..151dd2b 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -25,7 +25,7 @@ UI.TextEntry.defaults = { } } function UI.TextEntry:postInit() - self.value = tostring(self.value) + self.value = tostring(self.value) -- is this right ? shouldnt raw numbers be allowed self.entry = entry({ limit = self.limit, offset = 2 }) end @@ -35,7 +35,7 @@ function UI.TextEntry:layout() end function UI.TextEntry:setValue(value) - self.value = value + self.value = value or '' self.entry:unmark() self.entry.value = tostring(value) self.entry:updateScroll() -- 2.49.1 From 87c7e5ff7e4de9e4202323a4acbcea38b43302f9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 15 Jul 2019 20:08:30 -0600 Subject: [PATCH 056/236] ui theme generator + better module handling --- sys/apps/system/requires.lua | 3 +- sys/init/1.device.lua | 78 +-------------------- sys/init/3.modules.lua | 93 ++++++++++++++++++++----- sys/init/3.relay.lua | 2 +- sys/modules/opus/ui.lua | 29 ++++++++ sys/modules/opus/ui/components/Menu.lua | 1 + 6 files changed, 110 insertions(+), 96 deletions(-) diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index 38acef9..1e41216 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -100,4 +100,5 @@ function tab:eventHandler(event) end end -return tab +--this needs rework - see 4.user.lua +--return tab diff --git a/sys/init/1.device.lua b/sys/init/1.device.lua index c4946d3..b61a767 100644 --- a/sys/init/1.device.lua +++ b/sys/init/1.device.lua @@ -34,25 +34,12 @@ local keys = _G.keys local mouse = _G.device.mouse local os = _G.os -local drivers = { } - kernel.hook('peripheral', function(_, eventData) local side = eventData[1] if side then local dev = Peripheral.addDevice(device, side) if dev then - if drivers[dev.type] then - local e = drivers[dev.type](dev) - if type(e) == 'table' then - for _, v in pairs(e) do - os.queueEvent('device_attach', v.name) - end - elseif e then - os.queueEvent('device_attach', e.name) - end - end - - os.queueEvent('device_attach', dev.name, dev) + os.queueEvent('device_attach', dev.name) end end end) @@ -61,12 +48,7 @@ kernel.hook('peripheral_detach', function(_, eventData) local side = eventData[1] if side then for _, dev in pairs(Util.findAll(device, 'side', side)) do - os.queueEvent('device_detach', dev.name, dev) - if dev._children then - for _,v in pairs(dev._children) do - os.queueEvent('peripheral_detach', v.name) - end - end + os.queueEvent('device_detach', dev.name) device[dev.name] = nil end end @@ -124,59 +106,3 @@ end function keyboard.removeHotkey(code) keyboard.hotkeys[code] = nil end - -local function createDevice(name, devType, method, manipulator) - local dev = { - name = name, - side = name, - type = devType, - } - local methods = { - 'drop', 'getDocs', 'getItem', 'getItemMeta', 'getTransferLocations', - 'list', 'pullItems', 'pushItems', 'size', 'suck', - } - if manipulator[method] then - for _,k in pairs(methods) do - dev[k] = function(...) - return manipulator[method]()[k](...) - end - end - if not manipulator._children then - manipulator._children = { dev } - else - table.insert(manipulator._children, dev) - end - device[name] = dev - end -end - -drivers['manipulator'] = function(dev) - if dev.getName then - pcall(function() - local name = dev.getName() - if name then - if dev.getInventory then - createDevice(name .. ':inventory', 'inventory', 'getInventory', dev) - end - if dev.getEquipment then - createDevice(name .. ':equipment', 'equipment', 'getEquipment', dev) - end - if dev.getEnder then - createDevice(name .. ':enderChest', 'enderChest', 'getEnder', dev) - end - - return dev._children - end - end) - end -end - --- initialize drivers -for _,v in pairs(device) do - if drivers[v.type] then - local s, m = pcall(drivers[v.type], v) - if not s and m then - _G.printError(m) - end - end -end diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index 8dd5fcb..174f2be 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -3,17 +3,17 @@ local Util = require('opus.util') local device = _G.device local kernel = _G.kernel local os = _G.os -local peripheral = _G.peripheral local containers = { manipulator = true, neuralInterface = true, } +local cache = { } + local function getModules(dev, side) local list = { } - - if dev then + if dev and dev.listModules then for _, module in pairs(dev.listModules()) do list[module] = Util.shallowCopy(dev) list[module].name = module @@ -24,29 +24,86 @@ local function getModules(dev, side) return list end -for _,v in pairs(device) do - if containers[v.type] then - local list = getModules(v, v.side) - for k, dev in pairs(list) do - -- neural and attached modules have precedence over manipulator modules - if not device[k] or v.type ~= 'manipulator' then - device[k] = dev - end +-- if a device has been reattached, reuse the existing +-- table so any references to the table are retained +local function addDevice(dev, args, doQueue) + local name = args.name + + if not cache[name] then + cache[name] = { } + end + device[name] = cache[name] + Util.merge(device[name], dev) + Util.merge(device[name], args) + + if doQueue then + os.queueEvent('device_attach', name) + end +end + +local function addContainer(v, doQueue) + -- add devices like plethora:scanner + for name, dev in pairs(getModules(v, v.side)) do + -- neural and attached modules have precedence over manipulator modules + if not device[name] or v.type ~= 'manipulator' then + addDevice(dev, { name = dev.name, type = dev.name, side = dev.side }, doQueue) end end + + if v.getName then + pcall(function() + local name = v.getName() + if name then + if v.getInventory then + addDevice(v.getInventory(), { + name = name .. ':inventory', + type = 'inventory', + side = v.side + }, doQueue) + end + if v.getEquipment then + addDevice(v.getEquipment(), { + name = name .. ':equipment', + type = 'equipment', + side = v.side + }, doQueue) + end + if v.getEnder then + addDevice(v.getEnder(), { + name = name .. ':enderChest', + type = 'enderChest', + side = v.side + }, doQueue) + end + end + end) + end +end + +for k,v in pairs(device) do + if containers[v.type] then + cache[k] = v + addContainer(v) + end end -- register modules as peripherals kernel.hook('device_attach', function(_, eventData) - local dev = eventData[2] + local name = eventData[1] + local dev = device[name] if dev and containers[dev.type] then - local list = getModules(peripheral.wrap(dev.side), dev.side) - for k,v in pairs(list) do - if not device[k] or dev.type ~= 'manipulator' then - device[k] = v - os.queueEvent('device_attach', k, v) - end + -- so... basically, if you get a handle to device.neuralInterface + -- (or manipulator) - that handle will still be valid after + -- a module is removed + if cache[name] then + device[name] = cache[name] + -- TODO: cannot simply merge - need to remove + -- all functions then merge + Util.merge(device[name], dev) + else + cache[name] = dev end + addContainer(dev, true) end end) diff --git a/sys/init/3.relay.lua b/sys/init/3.relay.lua index 5ddc4bc..a2132e7 100644 --- a/sys/init/3.relay.lua +++ b/sys/init/3.relay.lua @@ -23,5 +23,5 @@ end -- register oc devices as peripherals kernel.hook('device_attach', function(_, eventData) - register(device[eventData[2]]) + register(device[eventData[1]]) end) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 0acfb16..b291e82 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -12,6 +12,7 @@ local fs = _G.fs local os = _G.os local peripheral = _G.peripheral local term = _G.term +local textutils = _G.textutils --[[ Using the shorthand window definition, elements are created from @@ -217,6 +218,34 @@ function Manager:loadTheme(filename) end end +function Manager:generateTheme(filename) + local t = { } + for k,v in pairs(self) do + if type(v) == 'table' then + if v._preload then + v._preload() + v = self[k] + end + if v.defaults and v.defaults.UIElement ~= 'Device' then + for p,d in pairs(v.defaults) do + if p:find('olor') then + if not t[k] then + t[k] = { } + end + for c, n in pairs(colors) do + if n == d then + t[k][p] = 'colors.' .. c + break + end + end + end + end + end + end + end + Util.writeFile(filename, textutils.serialize(t):gsub('(")', '')) +end + function Manager:emitEvent(event) local currentPage = self:getActivePage() if currentPage and currentPage.focused then diff --git a/sys/modules/opus/ui/components/Menu.lua b/sys/modules/opus/ui/components/Menu.lua index f581874..e31e8df 100644 --- a/sys/modules/opus/ui/components/Menu.lua +++ b/sys/modules/opus/ui/components/Menu.lua @@ -7,6 +7,7 @@ UI.Menu.defaults = { UIElement = 'Menu', disableHeader = true, columns = { { heading = 'Prompt', key = 'prompt', width = 20 } }, + menuItems = { }, } function UI.Menu:postInit() self.values = self.menuItems -- 2.49.1 From c8f200ebb6cc140d46b31721c6e872bf7c4a0fa9 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Tue, 16 Jul 2019 22:29:47 -0400 Subject: [PATCH 057/236] Fix apps.db and legacy icon transparency --- sys/apps/Network.lua | 4 +-- sys/etc/apps.db | 77 +++++++++++++------------------------------- sys/etc/fstab | 2 +- 3 files changed, 24 insertions(+), 59 deletions(-) diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index bc551a4..6ae002a 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -188,9 +188,7 @@ function page:eventHandler(event) os.queueEvent('overview_shortcut', { title = t.label, category = "VNC", - icon = "\ - \031e\\\031 \031e/\031dn\ - \031e\\/\031 \0319c", + icon = "\010\030 \009\009\031e\\\031 \031e/\031dn\010\030 \009\009 \031e\\/\031 \031bc", run = "vnc.lua " .. t.id, }) diff --git a/sys/etc/apps.db b/sys/etc/apps.db index 7bf79ab..e753fd1 100644 --- a/sys/etc/apps.db +++ b/sys/etc/apps.db @@ -14,63 +14,49 @@ [ "53ebc572b4a44802ba114729f07bdaaf5409a9d7" ] = { title = "Network", category = "Apps", - icon = "\0304 \030 \ -\030f \0304 \0307 \030 \031 \031f)\ -\030f \0304 \0307 \030 \031f)", + icon = "\0304 \030 \010\030f \0304 \0307 \030 \031 \031f)\010\030f \0304 \0307 \030 \031f)", iconExt = "\030 \031 \128\128\128\128\0305\140\030 \0315\137\144\010\0314\131\131\0304\031f\148\030 \031 \128\0305\155\150\149\010\147\0300\031f\141\0304\149\0307\0318\149\030 \031 \128\128\128", run = "Network.lua", }, - c7116629a6a855cb774d9c7c8ad822fd83c71fb5 = { + [ "c7116629a6a855cb774d9c7c8ad822fd83c71fb5" ] = { title = "Reboot", category = "System", - icon = "\0304\031f \030f\0310o..\0304\031f \ -\0304\031f \030f\0310.o.\0304\031f \ -\0304\031f - ", + icon = "\0304\031f \030f\0310o..\0304\031f \010\0304\031f \030f\0310.o.\0304\031f \010\0304\031f - ", iconExt = "\0307\031 \135\0300\0317\159\0307\0310\144\031 \139\010\0300\0317\131\0307\0310\147\0300\0317\156\131\010\030 \130\143\143\129", run = "rom/programs/reboot", }, - fb91e24fa52d8d2b32937bf04d843f730319a902 = { + [ "fb91e24fa52d8d2b32937bf04d843f730319a902" ] = { title = "Update", category = "System", - icon = "\0301\03171\03180\030 \031 \ -\0301\03181\030 \031 \ -\0301\03170\03180\03171\0307\031f>", + icon = "\0301\03171\03180\030 \031 \010\0301\03181\030 \031 \010\0301\03170\03180\03171\0307\031f>", iconExt = "\030 \031 \128\0313\152\131\131\132\031 \128\010\030 \0313\139\159\129\0303\031 \159\129\139\010\030 \031 \128\0313\136\0303\031 \143\143\030 \0313\134\031 \128", run = "update update", }, - c47ae15370cfe1ed2781eedc1dc2547d12d9e972 = { + [ "c47ae15370cfe1ed2781eedc1dc2547d12d9e972" ] = { title = "Help", category = "Apps", - icon = " \031f?\031 \ -\031f?\031 \ - \031f?", + icon = "\030 \0310 ? \010\030 \0310? \010\030 \0310\009 ?", iconExt = "\0300\031 \129\030 \0310\131\0300\031 \148\030 \0310\148\010\030 \031 \128\0300\131\030 \0310\142\129\010\030 \031 \128\0300\131\030 \128\128", run = "Help.lua", }, - b0832074630eb731d7fbe8074de48a90cd9bb220 = { + [ "b0832074630eb731d7fbe8074de48a90cd9bb220" ] = { title = "Lua", category = "Apps", - icon = "\030f \ -\030f\0310lua>\031 \ -\030f ", + icon = "\030 \010\030 \0310lua>\031 \010\030 ", iconExt = "\0300\031 \151\030 \128\0300\159\159\159\030 \0310\144\0304\031 \159\030 \128\010\0300\031 \149\030 \128\0300\149\149\151\145\030 \128\0314\153\010\030 \130\131\130\131\130\131\0314\130\031 \128", run = "Lua.lua", }, - bc0792d8dc81e8aa30b987246a5ce97c40cd6833 = { + [ "bc0792d8dc81e8aa30b987246a5ce97c40cd6833" ] = { title = "System", category = "System", - icon = " \0307\031f| \ -\0307\031f---o\030 \031 \ - \0307\031f| ", + icon = "\030 \0307\031f| \010\0307\031f---o\030 \031 \010\030 \009 \0307\031f| ", iconExt = "\030 \0318\138\0308\031 \130\0318\128\031 \129\030 \0318\133\010\030 \0318\143\0308\128\0317\143\0318\128\030 \143\010\030 \0318\138\135\143\139\133", run = "System.lua", }, [ "2a4d562b1d9a9c90bdede6fac8ce4f7402462b86" ] = { title = "Tasks", category = "System", - icon = "\030f\031f \0315/\ -\030f\031f \0315/\\/ \ -\030f\0315/\031f ", + icon = "\030 \031f \0315/\010\030 \031f \0315/\\/ \010\030 \0315/\031f ", iconExt = "\030 \031 \128\128\0305\159\030 \128\0305\159\030 \0315\134\031 \128\010\030 \031 \128\0315\152\129\137\0305\031 \158\139\030 \128\010\030 \0315\134\031 \128\128\128\128\0305\154\030 \128", run = "Tasks.lua", }, @@ -83,86 +69,67 @@ [ "6a381ca189cbddd63737cbaf6e8b593844ce467ba52b1c5e5e05d8f29864385d" ] = { title = "Sniffer", category = "Apps", - icon = "\31\102\128\128\128\128\31\53\149\30\53\31\102\154\30\102\31\53\137\10\30\52\31\102\159\31\52\128\128\30\102\144\31\53\130\30\53\31\102\155\140\10\31\52\151\30\52\31\102\148\30\102\31\52\151\30\52\31\102\148\30\102\128\128\128", - iconExt = "\030 \031 \128\128\128\128\0315\149\0305\031 \154\030 \0315\137\010\0304\031 \159\0314\128\128\030 \144\0315\130\0305\031 \155\140\010\0314\151\0304\031f\148\030f\0314\151\0304\031f\148\030 \031 \128\128\128", + iconExt = "\030 \031 \128\128\128\128\0315\149\0305\031 \154\030 \0315\137\010\0304\031 \159\0314\128\128\030 \144\0315\130\0305\031 \155\140\010\0314\151\0304\031f\148\030f\0314\151\0304\031f\148\030 \031 \128\128\128", run = "Sniff.lua", }, [ "01c933b2a36ad8ed2d54089cb2903039046c1216" ] = { title = "Enchat", - icon = "\030e\031f\151\030f\031e\156\0311\140\0314\140\0315\140\031d\140\031b\140\031a\132\ -\030f\0314\128\030e\031f\132\030f\031e\132\0318nchat\ -\030f\031e\138\141\0311\140\0314\140\0315\132\0317v\03183\031a\132", + iconExt = "\030e\031f\151\030f\031e\156\0311\140\0314\140\0315\140\031d\140\031b\140\031a\132\010\030f\0314\128\030e\031f\132\030f\031e\132\0318nchat\010\030f\031e\138\141\0311\140\0314\140\0315\132\0317v\03183\031a\132", category = "Apps", run = "Enchat", }, [ "6ce6c512ea433a7fc5c8841628e7696cd0ff7f2b" ] = { title = "Files", category = "Apps", - icon = "\0300\0317==\031 \0307 \ -\0300\0317====\ -\0300\0317====", + icon = "\0300\0317==\031 \0307 \010\0300\0317====\010\0300\0317====", iconExt = "\0300\031f\136\140\132\0308\031 \130\030 \0318\144\010\157\0300\031f\147\030f\0310\142\143\030 \149\010\0300\031f\136\140\132\140\030 \0310\149", run = "Files.lua", }, [ "7fddb7d8d1d60b1eeefa9af01082e0811d4b484d" ] = { title = "Shutdown", category = "System", - icon = "\0304\031f \ -\0304\031f \030f\0310zz\031 \ -\0304\031f \030f ", + icon = "\0304\031f \010\0304\031f \030f\0310zz\031 \010\0304\031f \030f ", iconExt = "\030e\031 \135\030 \031e\148\030e\128\031 \151\139\010\030e\031e\128\030 \031 \128\031e\143\031 \128\030e\031e\128\010\030 \031e\139\030e\031 \130\131\129\030 \031e\135", run = "/rom/programs/shutdown", }, [ "bdc1fd5d3c0f3dcfd55d010426e61bf9451e680d" ] = { title = "Shell", category = "Apps", - icon = "\0304 \030 \ -\0304 \030f\0314> \0310_\031 \ -\0304 \030f \030 ", + icon = "\0304 \030 \010\0304 \030f\0314> \0310_\031 \010\0304 \030f \030 ", iconExt = "\030f\0314\151\131\131\131\131\010\030f\0314\149\030f\0314> \0310_ \010\030f\0314\149\030f ", run = "shell", }, [ "b77aad5fb24921ef76ac8f3e500ed93fddae8f2a" ] = { title = "Redirection", category = "Games", - icon = "\0307 \0308 \0307 \ -\0308\031b> \030b\0310>\0308\0318 \ -\0307 ", + icon = "\0307 \0308 \0307 \010\0308\031b> \030b\0310>\0308\0318 \010\0307 ", run = "rom/programs/fun/advanced/redirection", requires = 'advanced', }, [ "f39d173d91c22348565c20283b89d4d1cabd3b7e" ] = { title = "Falling", category = "Games", - icon = "\030f \0302 \ -\0309 \0302 \0301 \ -\030e \0309 \0301 ", + icon = "\030f \0302 \010\0309 \0302 \0301 \010\030e \0309 \0301 ", run = "rom/programs/pocket/falling", requires = 'advancedPocket', }, [ "db56e2e1db9f7accfc37f2b132d27505c66ba521" ] = { title = "Adventure", category = "Games", - icon = "\030f\0310You \031 \ -\030f\0310Ther\030 \031 \ -\030f\0314?\031f \031 \030 ", + icon = "\030f\0310You \031 \010\030f\0310Ther\030 \031 \010\030f\0314?\031f \031 \030 ", run = "rom/programs/fun/adventure", }, [ "76b849f460640bc789c433894382fb5acbac42a2" ] = { title = "Worm", category = "Games", - icon = "\030d \030 \030e \030 \ -\030d \030 \ -\030d ", + icon = "\030d \030 \030e \030 \010\030d \030 \010\030d ", iconExt = "\0305\031 \151\030 \0315\135\131\0305\031 \146\010\030 \0315\130\141\0305\031 \139\030 \0315\130\010\0305\031 \146\143\030 \0315\158\031e\130", run = "/rom/programs/fun/worm", }, [ "9f46ca3ef617166776ef6014a58d4e66859caa62" ] = { title = "DJ", category = "Games", - icon = " \030f \ -\030f \0307 \ -\030f \0307 \0300 ", + icon = " \030f \010\030f \0307 \010\030f \0307 \0300 ", iconExt = "\030 \031 \128\0307\143\131\131\131\131\143\030 \128\010\0307\031 \129\0317\128\0319\136\0309\031b\136\132\0307\0319\132\0317\128\031 \130\010\030 \0317\130\143\0307\128\128\128\128\030 \143\129", run = "/rom/programs/fun/dj", }, diff --git a/sys/etc/fstab b/sys/etc/fstab index 235ecf0..6514f50 100644 --- a/sys/etc/fstab +++ b/sys/etc/fstab @@ -2,4 +2,4 @@ sys/apps/pain.lua urlfs https://github.com/LDDestroier/CC/raw/master/pain.lua sys/apps/update.lua urlfs http://pastebin.com/raw/UzGHLbNC sys/apps/Enchat.lua urlfs https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua -sys/apps/ntftrans.lua urlfs https://pastebin.com/raw/e8XrzeDY \ No newline at end of file +sys/apps/nfttrans.lua urlfs https://pastebin.com/raw/e8XrzeDY -- 2.49.1 From 672cca30841d6fdd40f75d9025aa9810ff900e14 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Tue, 16 Jul 2019 23:21:33 -0400 Subject: [PATCH 058/236] ui theme generator + better module handling --- sys/apps/system/requires.lua | 3 +- sys/init/1.device.lua | 78 +-------------------- sys/init/3.modules.lua | 93 ++++++++++++++++++++----- sys/init/3.relay.lua | 2 +- sys/modules/opus/ui.lua | 29 ++++++++ sys/modules/opus/ui/components/Menu.lua | 1 + 6 files changed, 110 insertions(+), 96 deletions(-) diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index 38acef9..1e41216 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -100,4 +100,5 @@ function tab:eventHandler(event) end end -return tab +--this needs rework - see 4.user.lua +--return tab diff --git a/sys/init/1.device.lua b/sys/init/1.device.lua index c4946d3..b61a767 100644 --- a/sys/init/1.device.lua +++ b/sys/init/1.device.lua @@ -34,25 +34,12 @@ local keys = _G.keys local mouse = _G.device.mouse local os = _G.os -local drivers = { } - kernel.hook('peripheral', function(_, eventData) local side = eventData[1] if side then local dev = Peripheral.addDevice(device, side) if dev then - if drivers[dev.type] then - local e = drivers[dev.type](dev) - if type(e) == 'table' then - for _, v in pairs(e) do - os.queueEvent('device_attach', v.name) - end - elseif e then - os.queueEvent('device_attach', e.name) - end - end - - os.queueEvent('device_attach', dev.name, dev) + os.queueEvent('device_attach', dev.name) end end end) @@ -61,12 +48,7 @@ kernel.hook('peripheral_detach', function(_, eventData) local side = eventData[1] if side then for _, dev in pairs(Util.findAll(device, 'side', side)) do - os.queueEvent('device_detach', dev.name, dev) - if dev._children then - for _,v in pairs(dev._children) do - os.queueEvent('peripheral_detach', v.name) - end - end + os.queueEvent('device_detach', dev.name) device[dev.name] = nil end end @@ -124,59 +106,3 @@ end function keyboard.removeHotkey(code) keyboard.hotkeys[code] = nil end - -local function createDevice(name, devType, method, manipulator) - local dev = { - name = name, - side = name, - type = devType, - } - local methods = { - 'drop', 'getDocs', 'getItem', 'getItemMeta', 'getTransferLocations', - 'list', 'pullItems', 'pushItems', 'size', 'suck', - } - if manipulator[method] then - for _,k in pairs(methods) do - dev[k] = function(...) - return manipulator[method]()[k](...) - end - end - if not manipulator._children then - manipulator._children = { dev } - else - table.insert(manipulator._children, dev) - end - device[name] = dev - end -end - -drivers['manipulator'] = function(dev) - if dev.getName then - pcall(function() - local name = dev.getName() - if name then - if dev.getInventory then - createDevice(name .. ':inventory', 'inventory', 'getInventory', dev) - end - if dev.getEquipment then - createDevice(name .. ':equipment', 'equipment', 'getEquipment', dev) - end - if dev.getEnder then - createDevice(name .. ':enderChest', 'enderChest', 'getEnder', dev) - end - - return dev._children - end - end) - end -end - --- initialize drivers -for _,v in pairs(device) do - if drivers[v.type] then - local s, m = pcall(drivers[v.type], v) - if not s and m then - _G.printError(m) - end - end -end diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index 8dd5fcb..174f2be 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -3,17 +3,17 @@ local Util = require('opus.util') local device = _G.device local kernel = _G.kernel local os = _G.os -local peripheral = _G.peripheral local containers = { manipulator = true, neuralInterface = true, } +local cache = { } + local function getModules(dev, side) local list = { } - - if dev then + if dev and dev.listModules then for _, module in pairs(dev.listModules()) do list[module] = Util.shallowCopy(dev) list[module].name = module @@ -24,29 +24,86 @@ local function getModules(dev, side) return list end -for _,v in pairs(device) do - if containers[v.type] then - local list = getModules(v, v.side) - for k, dev in pairs(list) do - -- neural and attached modules have precedence over manipulator modules - if not device[k] or v.type ~= 'manipulator' then - device[k] = dev - end +-- if a device has been reattached, reuse the existing +-- table so any references to the table are retained +local function addDevice(dev, args, doQueue) + local name = args.name + + if not cache[name] then + cache[name] = { } + end + device[name] = cache[name] + Util.merge(device[name], dev) + Util.merge(device[name], args) + + if doQueue then + os.queueEvent('device_attach', name) + end +end + +local function addContainer(v, doQueue) + -- add devices like plethora:scanner + for name, dev in pairs(getModules(v, v.side)) do + -- neural and attached modules have precedence over manipulator modules + if not device[name] or v.type ~= 'manipulator' then + addDevice(dev, { name = dev.name, type = dev.name, side = dev.side }, doQueue) end end + + if v.getName then + pcall(function() + local name = v.getName() + if name then + if v.getInventory then + addDevice(v.getInventory(), { + name = name .. ':inventory', + type = 'inventory', + side = v.side + }, doQueue) + end + if v.getEquipment then + addDevice(v.getEquipment(), { + name = name .. ':equipment', + type = 'equipment', + side = v.side + }, doQueue) + end + if v.getEnder then + addDevice(v.getEnder(), { + name = name .. ':enderChest', + type = 'enderChest', + side = v.side + }, doQueue) + end + end + end) + end +end + +for k,v in pairs(device) do + if containers[v.type] then + cache[k] = v + addContainer(v) + end end -- register modules as peripherals kernel.hook('device_attach', function(_, eventData) - local dev = eventData[2] + local name = eventData[1] + local dev = device[name] if dev and containers[dev.type] then - local list = getModules(peripheral.wrap(dev.side), dev.side) - for k,v in pairs(list) do - if not device[k] or dev.type ~= 'manipulator' then - device[k] = v - os.queueEvent('device_attach', k, v) - end + -- so... basically, if you get a handle to device.neuralInterface + -- (or manipulator) - that handle will still be valid after + -- a module is removed + if cache[name] then + device[name] = cache[name] + -- TODO: cannot simply merge - need to remove + -- all functions then merge + Util.merge(device[name], dev) + else + cache[name] = dev end + addContainer(dev, true) end end) diff --git a/sys/init/3.relay.lua b/sys/init/3.relay.lua index 5ddc4bc..a2132e7 100644 --- a/sys/init/3.relay.lua +++ b/sys/init/3.relay.lua @@ -23,5 +23,5 @@ end -- register oc devices as peripherals kernel.hook('device_attach', function(_, eventData) - register(device[eventData[2]]) + register(device[eventData[1]]) end) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 0acfb16..b291e82 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -12,6 +12,7 @@ local fs = _G.fs local os = _G.os local peripheral = _G.peripheral local term = _G.term +local textutils = _G.textutils --[[ Using the shorthand window definition, elements are created from @@ -217,6 +218,34 @@ function Manager:loadTheme(filename) end end +function Manager:generateTheme(filename) + local t = { } + for k,v in pairs(self) do + if type(v) == 'table' then + if v._preload then + v._preload() + v = self[k] + end + if v.defaults and v.defaults.UIElement ~= 'Device' then + for p,d in pairs(v.defaults) do + if p:find('olor') then + if not t[k] then + t[k] = { } + end + for c, n in pairs(colors) do + if n == d then + t[k][p] = 'colors.' .. c + break + end + end + end + end + end + end + end + Util.writeFile(filename, textutils.serialize(t):gsub('(")', '')) +end + function Manager:emitEvent(event) local currentPage = self:getActivePage() if currentPage and currentPage.focused then diff --git a/sys/modules/opus/ui/components/Menu.lua b/sys/modules/opus/ui/components/Menu.lua index f581874..e31e8df 100644 --- a/sys/modules/opus/ui/components/Menu.lua +++ b/sys/modules/opus/ui/components/Menu.lua @@ -7,6 +7,7 @@ UI.Menu.defaults = { UIElement = 'Menu', disableHeader = true, columns = { { heading = 'Prompt', key = 'prompt', width = 20 } }, + menuItems = { }, } function UI.Menu:postInit() self.values = self.menuItems -- 2.49.1 From 4093d4cd0d8ac4ff0f63f13a1b822a016f1113b9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 17 Jul 2019 15:29:26 -0600 Subject: [PATCH 059/236] fix issue with manipulator not working after relog --- sys/init/3.modules.lua | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index 174f2be..c80bcc7 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -39,6 +39,21 @@ local function addDevice(dev, args, doQueue) if doQueue then os.queueEvent('device_attach', name) end + return device[name] +end + +local function damnManipulator(container, method, args, doQueue) + local dev = addDevice(container[method](), args) + for k,v in pairs(dev) do + if type(v) == 'function' then + dev[k] = function(...) + return device[container.name][method]()[k](...) + end + end + end + if doQueue then + os.queueEvent('device_attach', args.name) + end end local function addContainer(v, doQueue) @@ -55,21 +70,21 @@ local function addContainer(v, doQueue) local name = v.getName() if name then if v.getInventory then - addDevice(v.getInventory(), { + damnManipulator(v, 'getInventory', { name = name .. ':inventory', type = 'inventory', side = v.side }, doQueue) end if v.getEquipment then - addDevice(v.getEquipment(), { + damnManipulator(v, 'getEquipment', { name = name .. ':equipment', type = 'equipment', side = v.side }, doQueue) end if v.getEnder then - addDevice(v.getEnder(), { + damnManipulator(v, 'getEnder', { name = name .. ':enderChest', type = 'enderChest', side = v.side -- 2.49.1 From d8dd17333c1cfe491fb23bd0d79785ac86c54af1 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Thu, 18 Jul 2019 01:00:22 -0400 Subject: [PATCH 060/236] Disk Usage icon transparency --- sys/apps/system/diskusage.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 8fbdd71..fb958f5 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -8,10 +8,10 @@ local os = _G.os local peripheral = _G.peripheral local NftImages = { - blank = '\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153\10\30\55\31\56\153\153\153\153\153\153\153\153\10\30\56\31\55\153\153\153\153\153\153\153\153', - drive = '\30\32\31\32\32\30\98\31\98\128\30\56\31\56\128\128\30\102\149\30\98\149\31\57\139\10\30\32\31\32\32\30\98\31\98\128\128\128\128\128\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128\10\30\32\31\32\32\30\98\31\98\128\30\48\31\55\95\95\95\95\30\98\31\98\128', - rom = '\30\57\31\57\128\31\56\144\144\144\144\144\31\57\128\10\30\56\31\57\157\30\55\31\55\128\128\128\128\128\30\57\31\56\145\10\30\57\31\56\136\30\55\31\55\128\30\55\31\48\82\79\77\30\55\128\30\57\31\56\132\10\30\56\31\57\157\30\55\31\55\128\128\128\128\128\30\57\31\56\145\10\30\57\31\57\128\31\56\129\129\129\129\129\31\57\128', - hdd = '\30\32\31\32\32\30\55\31\55\128\30\48\135\131\139\30\55\128\10\30\32\31\32\32\30\48\31\55\149\31\48\128\30\55\131\30\48\128\30\55\149\10\30\32\31\32\32\30\55\31\48\130\30\48\31\55\144\30\56\31\48\133\30\55\159\129\10\30\32\31\32\32\30\56\31\55\149\129\142\159\30\55\128\10\30\32\31\32\32\30\57\31\55\143\143\143\143\143', + blank = '\0308\0317\153\153\153\153\153\153\153\153\010\0307\0318\153\153\153\153\153\153\153\153\010\0308\0317\153\153\153\153\153\153\153\153\010\0307\0318\153\153\153\153\153\153\153\153\010\0308\0317\153\153\153\153\153\153\153\153', + drive = '\030 \031 \030b\031b\128\0308\0318\128\128\030f\149\030b\149\031 \139\010\030 \031 \030b\031b\128\128\128\128\128\128\010\030 \031 \030b\031b\128\0300\0317____\030b\031b\128\010\030 \031 \030b\031b\128\0300\0317____\030b\031b\128', + rom = '\030 \031 \128\0318\144\144\144\144\144\031 \128\010\0308\031 \157\0307\0317\128\128\128\128\128\030 \0318\145\010\030 \0318\136\0307\0317\128\0307\0310ROM\0307\128\030 \0318\132\010\0308\031 \157\0307\0317\128\128\128\128\128\030 \0318\145\010\030 \031 \128\0318\129\129\129\129\129\031 \128', + hdd = '\030 \031 \0307\0317\128\0300\135\131\139\0307\128\010\030 \031 \0300\0317\149\0310\128\0307\131\0300\128\0307\149\010\030 \031 \0307\0310\130\0300\0317\144\0308\0310\133\0307\159\129\010\030 \031 \0308\0317\149\129\142\159\0307\128\010\030 \031 \030 \0317\143\143\143\143\143', } local tab = UI.Tab { -- 2.49.1 From 92686381c7fb2016d12724081877b88271eee6e3 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 19 Jul 2019 10:06:34 -0600 Subject: [PATCH 061/236] manipulators aaarrrggghhh --- sys/etc/fstab | 1 + sys/init/3.modules.lua | 89 ++++++++++++++++++++++++------------------ sys/init/3.sys.lua | 1 - sys/init/4.label.lua | 10 ++++- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/sys/etc/fstab b/sys/etc/fstab index 6514f50..60fb9e1 100644 --- a/sys/etc/fstab +++ b/sys/etc/fstab @@ -3,3 +3,4 @@ sys/apps/update.lua urlfs http://pastebin.com/raw/UzGHLbNC sys/apps/Enchat.lua urlfs https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua sys/apps/nfttrans.lua urlfs https://pastebin.com/raw/e8XrzeDY +rom/modules/main/opus linkfs sys/modules/opus \ No newline at end of file diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index c80bcc7..3d585c0 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -11,15 +11,22 @@ local containers = { local cache = { } +-- manipulators will throw an error on listModules +-- if the user has logged off local function getModules(dev, side) local list = { } - if dev and dev.listModules then - for _, module in pairs(dev.listModules()) do - list[module] = Util.shallowCopy(dev) - list[module].name = module - list[module].type = module - list[module].side = side + local s, m = pcall(function() + if dev and dev.listModules then + for _, module in pairs(dev.listModules()) do + list[module] = Util.shallowCopy(dev) + list[module].name = module + list[module].type = module + list[module].side = side + end end + end) + if not s and m then + _G._syslog(m) end return list end @@ -39,20 +46,27 @@ local function addDevice(dev, args, doQueue) if doQueue then os.queueEvent('device_attach', name) end - return device[name] end +-- directly access the peripheral as the methods in getInventory, etc. +-- can become invalid without any way to tell local function damnManipulator(container, method, args, doQueue) - local dev = addDevice(container[method](), args) - for k,v in pairs(dev) do - if type(v) == 'function' then + local dev = { } + local methods = { + 'drop', 'getDocs', 'getItem', 'getItemMeta', 'getTransferLocations', + 'list', 'pullItems', 'pushItems', 'size', 'suck', + } + -- the user might not be logged in when the compputer is started + -- and there's no way to know when they have logged in. + -- these methods will error if the user is not logged in + if container[method] then + for _,k in pairs(methods) do dev[k] = function(...) return device[container.name][method]()[k](...) end end - end - if doQueue then - os.queueEvent('device_attach', args.name) + + addDevice(dev, args, doQueue) end end @@ -66,32 +80,29 @@ local function addContainer(v, doQueue) end if v.getName then - pcall(function() + local s, m = pcall(function() local name = v.getName() if name then - if v.getInventory then - damnManipulator(v, 'getInventory', { - name = name .. ':inventory', - type = 'inventory', - side = v.side - }, doQueue) - end - if v.getEquipment then - damnManipulator(v, 'getEquipment', { - name = name .. ':equipment', - type = 'equipment', - side = v.side - }, doQueue) - end - if v.getEnder then - damnManipulator(v, 'getEnder', { - name = name .. ':enderChest', - type = 'enderChest', - side = v.side - }, doQueue) - end + damnManipulator(v, 'getInventory', { + name = name .. ':inventory', + type = 'inventory', + side = v.side + }, doQueue) + damnManipulator(v, 'getEquipment', { + name = name .. ':equipment', + type = 'equipment', + side = v.side + }, doQueue) + damnManipulator(v, 'getEnder', { + name = name .. ':enderChest', + type = 'enderChest', + side = v.side + }, doQueue) end end) + if not s and m then + _G._syslog(m) + end end end @@ -113,9 +124,11 @@ kernel.hook('device_attach', function(_, eventData) -- a module is removed if cache[name] then device[name] = cache[name] - -- TODO: cannot simply merge - need to remove - -- all functions then merge - Util.merge(device[name], dev) + for k,v in pairs(device[name]) do + if type(v) == 'function' then + device[name][k] = nil + end + end else cache[name] = dev end diff --git a/sys/init/3.sys.lua b/sys/init/3.sys.lua index ce04901..7e18d18 100644 --- a/sys/init/3.sys.lua +++ b/sys/init/3.sys.lua @@ -1,4 +1,3 @@ local fs = _G.fs -fs.mount('rom/modules/main/opus', 'linkfs', 'sys/modules/opus') fs.loadTab('sys/etc/fstab') diff --git a/sys/init/4.label.lua b/sys/init/4.label.lua index 0a34862..dfdb89c 100644 --- a/sys/init/4.label.lua +++ b/sys/init/4.label.lua @@ -1,14 +1,22 @@ -local os = _G.os +local os = _G.os +local peripheral = _G.peripheral -- Default label if not os.getComputerLabel() then local id = os.getComputerID() + if _G.turtle then os.setComputerLabel('turtle_' .. id) + elseif _G.pocket then os.setComputerLabel('pocket_' .. id) + elseif _G.commands then os.setComputerLabel('command_' .. id) + + elseif peripheral.find('neuralInterface') then + os.setComputerLabel('neural_' .. id) + else os.setComputerLabel('computer_' .. id) end -- 2.49.1 From 9ce61b5d98d5cee2b859d74a4348d5229ae418a8 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 19 Jul 2019 10:12:25 -0600 Subject: [PATCH 062/236] oops --- sys/init/3.modules.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index 3d585c0..c01ae43 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -129,6 +129,7 @@ kernel.hook('device_attach', function(_, eventData) device[name][k] = nil end end + Util.merge(device[name], dev) else cache[name] = dev end -- 2.49.1 From 09be81be27859de6654a69bfc6668b1c97c6eb3a Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Sat, 20 Jul 2019 18:19:30 -0400 Subject: [PATCH 063/236] lua 5.2 compatibility (#14) compatibility update - making a few changes in next commit... --- startup.lua | 2 +- sys/apps/mount.lua | 2 +- sys/apps/network/samba.lua | 2 +- sys/apps/vnc.lua | 2 +- sys/init/6.tl3.lua | 4 ++-- sys/modules/opus/crypto/chacha20.lua | 8 ++++---- sys/modules/opus/crypto/sha2.lua | 6 +++--- sys/modules/opus/util.lua | 11 ++++++----- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/startup.lua b/startup.lua index 082bdc9..0306df6 100644 --- a/startup.lua +++ b/startup.lua @@ -139,7 +139,7 @@ end term.clear() term.setCursorPos(1, 1) if bootOptions[bootOption].args then - os.run(_G.getfenv(1), table.unpack(bootOptions[bootOption].args)) + os.run({}, table.unpack(bootOptions[bootOption].args)) else print(bootOptions[bootOption].prompt) end diff --git a/sys/apps/mount.lua b/sys/apps/mount.lua index d7bde84..a25eb38 100644 --- a/sys/apps/mount.lua +++ b/sys/apps/mount.lua @@ -3,4 +3,4 @@ local args = { ... } local target = table.remove(args, 1) target = shell.resolve(target) -fs.mount(target, unpack(args)) +fs.mount(target, table.unpack(args)) diff --git a/sys/apps/network/samba.lua b/sys/apps/network/samba.lua index f679b4d..50cbfd0 100644 --- a/sys/apps/network/samba.lua +++ b/sys/apps/network/samba.lua @@ -48,7 +48,7 @@ local function sambaConnection(socket) end local ret local s, m = pcall(function() - ret = fn(unpack(msg.args)) + ret = fn(table.unpack(msg.args)) end) if not s and m then _G.printError('samba: ' .. m) diff --git a/sys/apps/vnc.lua b/sys/apps/vnc.lua index 0265254..c9d865c 100644 --- a/sys/apps/vnc.lua +++ b/sys/apps/vnc.lua @@ -70,7 +70,7 @@ local function connect() break end for _,v in ipairs(data) do - ct[v.f](unpack(v.args)) + ct[v.f](table.unpack(v.args)) end end end) diff --git a/sys/init/6.tl3.lua b/sys/init/6.tl3.lua index ee3f3aa..e1e9ecf 100644 --- a/sys/init/6.tl3.lua +++ b/sys/init/6.tl3.lua @@ -241,7 +241,7 @@ local function _place(action, indexOrId) if not state.digPolicy(action) then state.attackPolicy(action) end - return unpack(result) + return table.unpack(result) end) end @@ -1024,7 +1024,7 @@ function turtle.run(fn, ...) synchronized(turtle, function() turtle.resetState() - s, m = pcall(function() fn(unpack(args)) end) + s, m = pcall(function() fn(table.unpack(args)) end) turtle.resetState() if not s and m then _G.printError(m) diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 9547f6c..7c3b819 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -34,7 +34,7 @@ local function quarterRound(s, a, b, c, d) end local function hashBlock(state, rnd) - local s = {unpack(state)} + local s = {table.unpack(state)} for i = 1, rnd do local r = i%2==1 s = r and quarterRound(s, 1, 5, 9, 13) or quarterRound(s, 1, 6, 11, 16) @@ -92,9 +92,9 @@ local function serialize(state) end local mt = { - __tostring = function(a) return string.char(unpack(a)) end, + __tostring = function(a) return string.char(table.unpack(a)) end, __index = { - toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end, + toHex = function(self) return ("%02x"):rep(#self):format(table.unpack(self)) end, isEqual = function(self, t) if type(t) ~= "table" then return false end if #self ~= #t then return false end @@ -113,7 +113,7 @@ local function crypt(data, key, nonce, cntr, round) assert(#key == 16 or #key == 32, "ChaCha20: Invalid key length ("..#key.."), must be 16 or 32") assert(#nonce == 12, "ChaCha20: Invalid nonce length ("..#nonce.."), must be 12") - data = type(data) == "table" and {unpack(data)} or {tostring(data):byte(1,-1)} + data = type(data) == "table" and {table.unpack(data)} or {tostring(data):byte(1,-1)} cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 diff --git a/sys/modules/opus/crypto/sha2.lua b/sys/modules/opus/crypto/sha2.lua index 2754ee7..4dcb98b 100644 --- a/sys/modules/opus/crypto/sha2.lua +++ b/sys/modules/opus/crypto/sha2.lua @@ -8,7 +8,7 @@ local band = bit32 and bit32.band or bit.band local bnot = bit32 and bit32.bnot or bit.bnot local bxor = bit32 and bit32.bxor or bit.bxor local blshift = bit32 and bit32.lshift or bit.blshift -local upack = unpack +local upack = unpack or table.unpack local function rrotate(n, b) local s = n/(2^b) @@ -95,9 +95,9 @@ local function digestblock(w, C) end local mt = { - __tostring = function(a) return string.char(unpack(a)) end, + __tostring = function(a) return string.char(upack(a)) end, __index = { - toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end, + toHex = function(self) return ("%02x"):rep(#self):format(upack(self)) end, isEqual = function(self, t) if type(t) ~= "table" then return false end if #self ~= #t then return false end diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 2c8e097..85ab2f0 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -21,7 +21,7 @@ end function Util.byteArrayToHex(tbl) if not tbl then error('byteArrayToHex: invalid table', 2) end - return ("%02x"):rep(#tbl):format(unpack(tbl)) + return ("%02x"):rep(#tbl):format(table.unpack(tbl)) end function Util.tryTimed(timeout, f, ...) @@ -39,10 +39,10 @@ function Util.tryTimes(attempts, f, ...) for _ = 1, attempts do result = { f(...) } if result[1] then - return unpack(result) + return table.unpack(result) end end - return unpack(result) + return table.unpack(result) end function Util.timer() @@ -490,7 +490,7 @@ function Util.loadTable(fname) if not fc then return false, 'Unable to read file' end - local s, m = loadstring('return ' .. fc, fname) + local s, m = load('return ' .. fc, fname) if s then s, m = pcall(s) if s then @@ -551,8 +551,9 @@ function Util.run(env, path, ...) end function Util.runFunction(env, fn, ...) - setfenv(fn, env) + --setfenv(fn, env) setmetatable(env, { __index = _G }) + fn = load(fn,"util.runfunction",nil,env) return pcall(fn, ...) end -- 2.49.1 From 30b71999761bd7999bed4ffd3d273f1b01a4edfd Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 20 Jul 2019 16:23:48 -0600 Subject: [PATCH 064/236] path fix + compat changes --- startup.lua | 2 +- sys/init/4.user.lua | 4 ++-- sys/init/6.packages.lua | 2 +- sys/modules/opus/util.lua | 5 ++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/startup.lua b/startup.lua index 0306df6..d94be2b 100644 --- a/startup.lua +++ b/startup.lua @@ -139,7 +139,7 @@ end term.clear() term.setCursorPos(1, 1) if bootOptions[bootOption].args then - os.run({}, table.unpack(bootOptions[bootOption].args)) + os.run(_ENV, table.unpack(bootOptions[bootOption].args)) else print(bootOptions[bootOption].prompt) end diff --git a/sys/init/4.user.lua b/sys/init/4.user.lua index 0349808..4decc1f 100644 --- a/sys/init/4.user.lua +++ b/sys/init/4.user.lua @@ -19,7 +19,7 @@ end if not fs.exists('usr/config/shell') then Util.writeTable('usr/config/shell', { aliases = shell.aliases(), - path = 'usr/apps', + path = '/usr/apps', lua_path = package.path, upgraded = 1, }) @@ -36,7 +36,7 @@ if config.aliases then end local path = config.path and Util.split(config.path, '(.-):') or { } -table.insert(path, 'sys/apps') +table.insert(path, '/sys/apps') for _, v in pairs(Util.split(shell.path(), '(.-):')) do table.insert(path, v) end diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index b1e6ef7..7d06abe 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -13,7 +13,7 @@ table.insert(helpPaths, '/sys/help') for name in pairs(Packages:installed()) do local packageDir = fs.combine('packages', name) - table.insert(appPaths, 1, packageDir) + table.insert(appPaths, 1, '/' .. packageDir) local apiPath = fs.combine(packageDir, 'apis') if fs.exists(apiPath) then fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath) diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 85ab2f0..eee7f70 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -490,7 +490,7 @@ function Util.loadTable(fname) if not fc then return false, 'Unable to read file' end - local s, m = load('return ' .. fc, fname) + local s, m = loadstring('return ' .. fc, fname) if s then s, m = pcall(s) if s then @@ -551,9 +551,8 @@ function Util.run(env, path, ...) end function Util.runFunction(env, fn, ...) - --setfenv(fn, env) + setfenv(fn, env) setmetatable(env, { __index = _G }) - fn = load(fn,"util.runfunction",nil,env) return pcall(fn, ...) end -- 2.49.1 From ec7fc5bb23c9bfcb8073051e7324f4f41b1657df Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 20 Jul 2019 19:29:44 -0600 Subject: [PATCH 065/236] fix ui default device via args --- sys/modules/opus/ui.lua | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index b291e82..409ec85 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -125,9 +125,9 @@ function Manager:init() focused = true }) elseif ie and currentPage then - --if not self.currentPage.parent.device.side then + if not currentPage.parent.device.side then self:click(currentPage, ie.code, button, x, y) - --end + end end end, @@ -158,21 +158,16 @@ function Manager:init() end function Manager:configure(appName, ...) - local options = { - device = { arg = 'd', type = 'string', - desc = 'Device type' }, - textScale = { arg = 't', type = 'number', - desc = 'Text scale' }, - } local defaults = Util.loadTable('usr/config/' .. appName) or { } if not defaults.device then defaults.device = { } end - Util.getOptions(options, { ... }, true) + -- starting a program: gpsServer --display=monitor_3148 --scale=.5 gps + local _, options = Util.parse(...) local optionValues = { - name = options.device.value, - textScale = options.textScale.value, + name = options.display, + textScale = tonumber(options.scale), } Util.merge(defaults.device, optionValues) @@ -183,7 +178,7 @@ function Manager:configure(appName, ...) if defaults.device.name == 'terminal' then dev = term.current() else - dev = peripheral.wrap(defaults.device.name) + dev = _G.device[defaults.device.name] end if not dev then -- 2.49.1 From ae98aaf9d5239a8459219016d85ca2f15ae14bbd Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 21 Jul 2019 10:16:47 -0600 Subject: [PATCH 066/236] lint warnings --- sys/modules/opus/ui.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 409ec85..9e9a4c5 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -8,9 +8,9 @@ local Util = require('opus.util') local _rep = string.rep local _sub = string.sub local colors = _G.colors +local device = _G.device local fs = _G.fs local os = _G.os -local peripheral = _G.peripheral local term = _G.term local textutils = _G.textutils @@ -178,7 +178,7 @@ function Manager:configure(appName, ...) if defaults.device.name == 'terminal' then dev = term.current() else - dev = _G.device[defaults.device.name] + dev = device[defaults.device.name] end if not dev then -- 2.49.1 From c18945467f0f476814c88b1d2c8efb04f436099f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 22 Jul 2019 22:21:41 -0600 Subject: [PATCH 067/236] ability to specify index in form fields --- sys/modules/opus/ui/components/Form.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/modules/opus/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua index 68b59d3..527af3f 100644 --- a/sys/modules/opus/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -60,14 +60,14 @@ function UI.Form:createForm() end if child.formLabel then child.x = self.labelWidth + self.margin - 1 - child.y = y + child.y = child.formIndex and (child.formIndex + self.margin - 1) or y if not child.width and not child.ex then child.ex = -self.margin end table.insert(self.children, UI.Text { x = self.margin, - y = y, + y = child.y, textColor = colors.black, width = #child.formLabel, value = child.formLabel, -- 2.49.1 From 5dd3001ffd26d1d947bfb53a05c17c9561fb674b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 22 Jul 2019 22:57:22 -0600 Subject: [PATCH 068/236] dont close modem ports on network startup (due to multiple computers may be using same modem) --- sys/apps/netdaemon.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sys/apps/netdaemon.lua b/sys/apps/netdaemon.lua index 2457a49..1d3fd66 100644 --- a/sys/apps/netdaemon.lua +++ b/sys/apps/netdaemon.lua @@ -14,7 +14,9 @@ if not device.wireless_modem then end print('Net daemon starting') -device.wireless_modem.closeAll() +-- don't close as multiple computers may be sharing the +-- wireless modem +--device.wireless_modem.closeAll() for _,file in pairs(fs.list('sys/apps/network')) do local fn, msg = Util.run(_ENV, 'sys/apps/network/' .. file) -- 2.49.1 From e084c733f83d9c5a3255a8a3f03d4505cc961740 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 23 Jul 2019 13:29:43 -0600 Subject: [PATCH 069/236] consistent command line processing-usage --- sys/apps/Files.lua | 2 +- sys/apps/Help.lua | 2 +- sys/apps/Lua.lua | 2 +- sys/apps/Sniff.lua | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 019979e..9f17255 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -542,7 +542,7 @@ function Browser.associations:eventHandler(event) end --[[-- Startup logic --]]-- -local args = { ... } +local args = Util.parse(...) Browser:setDir(args[1] or shell.dir()) diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index 72b5308..ffd0486 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -99,6 +99,6 @@ function page:eventHandler(event) end end -local args = { ... } +local args = Util.parse(...) UI:setPage(args[1] and topicPage or page, args[1]) UI:pullEvents() diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index a7442be..45cec04 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -376,7 +376,7 @@ function page:executeStatement(statement) end end -local args = { ... } +local args = Util.parse(...) if args[1] then command = 'args[1]' sandboxEnv.args = args diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 6a683eb..3a8257c 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -356,7 +356,7 @@ Event.on('modem_message', function(_, side, chan, reply, msg, dist) end end) -local args = {...} +local args = Util.parse(...) if args[1] then local id = tonumber(args[1]) if id then -- 2.49.1 From cd2024d7cefe4f5fb37ce0d6d0d5697052273c7b Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Wed, 24 Jul 2019 00:37:41 -0400 Subject: [PATCH 070/236] TextEntry number transform --- sys/modules/opus/ui/components/TextEntry.lua | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 151dd2b..dda3766 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -11,7 +11,7 @@ local _upper = string.upper UI.TextEntry = class(UI.Window) UI.TextEntry.defaults = { UIElement = 'TextEntry', - value = '', + --value = '', shadowText = '', focused = false, textColor = colors.white, @@ -25,7 +25,6 @@ UI.TextEntry.defaults = { } } function UI.TextEntry:postInit() - self.value = tostring(self.value) -- is this right ? shouldnt raw numbers be allowed self.entry = entry({ limit = self.limit, offset = 2 }) end @@ -35,7 +34,7 @@ function UI.TextEntry:layout() end function UI.TextEntry:setValue(value) - self.value = value or '' + self.value = value --or '' self.entry:unmark() self.entry.value = tostring(value) self.entry:updateScroll() @@ -43,7 +42,7 @@ end function UI.TextEntry:setPosition(pos) self.entry.pos = pos - self.entry.value = tostring(self.value) + self.entry.value = tostring(self.value or '') self.entry:updateScroll() end @@ -54,7 +53,7 @@ function UI.TextEntry:draw() bg = self.backgroundFocusColor end - local text = tostring(self.value) + local text = tostring(self.value or '') if #text > 0 then if self.entry.scroll > 0 then text = text:sub(1 + self.entry.scroll) @@ -88,7 +87,7 @@ end function UI.TextEntry:reset() self.entry:reset() - self.value = '' + self.value = nil--'' self:draw() self:updateCursor() end @@ -111,13 +110,15 @@ function UI.TextEntry:_transform(text) return _lower(text) elseif self.transform == 'uppercase' then return _upper(text) + elseif self.transform == 'number' then + return tonumber(text) --or 0 end return text end function UI.TextEntry:eventHandler(event) - local text = self.value - self.entry.value = tostring(text) + local text = self.value --or '' + self.entry.value = tostring(text or '') if event.ie and self.entry:process(event.ie) then if self.entry.textChanged then self.value = self:_transform(self.entry.value) -- 2.49.1 From b3efbc7438a3a25167f9c39b4bd7c6f1708bd161 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Wed, 24 Jul 2019 00:45:00 -0400 Subject: [PATCH 071/236] Distance filter in Sniffer --- sys/apps/Sniff.lua | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 3a8257c..2bda4f0 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -73,13 +73,24 @@ local page = UI.Page { event = 'filter_add', }, filterAllCheck = UI.Checkbox { - x = 13, y = 4, + x = 14, y = 8, value = false, }, filterAddText = UI.Text { - x = 17, y = 4, + x = 18, y = 8, value = "Use ID filter", }, + rangeText = UI.Text { + x = 15, y = 2, + value = "Distance filter", + }, + rangeEntry = UI.TextEntry { + x = 15, y = 3, + width = 10, + limit = 8, + shadowText = 'Range', + transform = 'number', + }, }, modemTab = UI.Tab { tabTitle = 'Modem', @@ -268,8 +279,14 @@ function page.packetSlide:eventHandler(event) return true end +function page.packetGrid:getDisplayValues(row) + local row = Util.shallowCopy(row) + row.distance = Util.toBytes(Util.round(row.distance), 2) + return row +end + function page.packetGrid:addPacket(packet) - if not page.paused and (not filterConfig.filterAllCheck.value or filterConfig.filterGrid.values[packet.portid]) then + if not page.paused and (packet.distance <= (filterConfig.rangeEntry.value or math.huge)) and (not filterConfig.filterAllCheck.value or filterConfig.filterGrid.values[packet.portid]) then page.index = page.index + 1 local _, res = pcall(textutils.serialize, packet.message) packet.packetStr = res:gsub("\n%s*", "") -- 2.49.1 From b739c0c77e23fb4c87a6dc92d8347bad89085017 Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Thu, 25 Jul 2019 22:51:16 -0400 Subject: [PATCH 072/236] fix no password error --- sys/apps/Welcome.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 47c658f..04b60f9 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -125,7 +125,7 @@ function page.wizard.pages.label:validate() end function page.wizard.pages.password:validate() - if #self.newPass.value > 0 then + if type(self.newPass.value) == "string" and #self.newPass.value > 0 then Security.updatePassword(SHA.compute(self.newPass.value)) end --[[ -- 2.49.1 From 9c4aac27f8521be23f680650b1bf0b92bd7d8119 Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Thu, 25 Jul 2019 23:04:20 -0400 Subject: [PATCH 073/236] Update upgraded.lua Kind of make the program look better as the 'sys/apis' line is the only one not to do fs.exists. Also fixes error when opus is nested inside a potatOS instance. --- sys/autorun/upgraded.lua | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index f6881db..5922c3a 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -1,14 +1,20 @@ local fs = _G.fs +local function deleteIfExists(path) + if fs.exists(path) then + fs.delete(path) + print("Deleted outdated file at: "..path) + end +end -- cleanup outdated files -fs.delete('sys/apps/shell') -fs.delete('sys/etc/app.db') -fs.delete('sys/extensions') -fs.delete('sys/network') -fs.delete('startup') -fs.delete('sys/apps/system/turtle.lua') -fs.delete('sys/autorun/gps.lua') -fs.delete('sys/autorun/gpshost.lua') -fs.delete('sys/apps/network/redserver.lua') -if fs.exists('sys/apis') then fs.delete('sys/apis') end -fs.delete('sys/autorun/apps.lua') +deleteIfExists('sys/apps/shell') +deleteIfExists('sys/etc/app.db') +deleteIfExists('sys/extensions') +deleteIfExists('sys/network') +deleteIfExists('startup') +deleteIfExists('sys/apps/system/turtle.lua') +deleteIfExists('sys/autorun/gps.lua') +deleteIfExists('sys/autorun/gpshost.lua') +deleteIfExists('sys/apps/network/redserver.lua') +deleteIfExists('sys/apis') +deleteIfExists('sys/autorun/apps.lua') -- 2.49.1 From c1430f6dac79cf687a10c15af786fa9204a0b3b6 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Sat, 27 Jul 2019 00:13:24 -0400 Subject: [PATCH 074/236] GPS ambiguous position fix --- sys/modules/opus/gps.lua | 24 +++++------ sys/modules/opus/util.lua | 83 ++++++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 43 deletions(-) diff --git a/sys/modules/opus/gps.lua b/sys/modules/opus/gps.lua index 1b3e223..0ca7a66 100644 --- a/sys/modules/opus/gps.lua +++ b/sys/modules/opus/gps.lua @@ -1,3 +1,5 @@ +local Util = require('opus.util') + local GPS = { } local device = _G.device @@ -89,19 +91,17 @@ end -- end stock gps api function GPS.trilaterate(tFixes) - local pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[3]) - - if pos2 then - pos1, pos2 = narrow(pos1, pos2, tFixes[4]) + local attemps = 0 + for tFixes in Util.permutation(tFixes) do + attemps = attemps + 1 + local pos1, pos2 = trilaterate(tFixes[4], tFixes[3], tFixes[2]) + if pos2 then + pos1, pos2 = narrow(pos1, pos2, tFixes[1]) + end + if not pos2 then + return pos1, attemps + end end - - if pos1 and pos2 then - print("Ambiguous position") - print("Could be "..pos1.x..","..pos1.y..","..pos1.z.." or "..pos2.x..","..pos2.y..","..pos2.z ) - return - end - - return pos1 end return GPS \ No newline at end of file diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index eee7f70..4604199 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -46,10 +46,10 @@ function Util.tryTimes(attempts, f, ...) end function Util.timer() - local ct = os.clock() - return function() - return os.clock() - ct - end + local ct = os.clock() + return function() + return os.clock() - ct + end end Util.Timer = Util.timer -- deprecate @@ -678,33 +678,33 @@ end -- https://github.com/MightyPirates/OpenComputers function Util.parse(...) - local params = table.pack(...) - local args = {} - local options = {} - local doneWithOptions = false - for i = 1, params.n do - local param = params[i] - if not doneWithOptions and type(param) == "string" then - if param == "--" then - doneWithOptions = true -- stop processing options at `--` - elseif param:sub(1, 2) == "--" then - local key, value = param:match("%-%-(.-)=(.*)") - if not key then - key, value = param:sub(3), true - end - options[key] = value - elseif param:sub(1, 1) == "-" and param ~= "-" then - for j = 2, string.len(param) do - options[string.sub(param, j, j)] = true - end - else - table.insert(args, param) - end - else - table.insert(args, param) - end - end - return args, options + local params = table.pack(...) + local args = {} + local options = {} + local doneWithOptions = false + for i = 1, params.n do + local param = params[i] + if not doneWithOptions and type(param) == "string" then + if param == "--" then + doneWithOptions = true -- stop processing options at `--` + elseif param:sub(1, 2) == "--" then + local key, value = param:match("%-%-(.-)=(.*)") + if not key then + key, value = param:sub(3), true + end + options[key] = value + elseif param:sub(1, 1) == "-" and param ~= "-" then + for j = 2, string.len(param) do + options[string.sub(param, j, j)] = true + end + else + table.insert(args, param) + end + else + table.insert(args, param) + end + end + return args, options end function Util.args(arg) @@ -799,4 +799,25 @@ function Util.getOptions(options, args, ignoreInvalid) return true, Util.size(rawOptions) end +-- https://www.lua.org/pil/9.3.html +function Util.permutation(tbl) + local function permgen(a, n) + if n == 0 then + coroutine.yield(a) + else + for i=1,n do + a[n], a[i] = a[i], a[n] + permgen(a, n - 1) + a[n], a[i] = a[i], a[n] + end + end + end + + local co = coroutine.create(function() permgen(tbl, #tbl) end) + return function() + local _, res = coroutine.resume(co) + return res + end +end + return Util -- 2.49.1 From 7e8dc5bd49f0407a420e09bd4747ae30db6e0f66 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 27 Jul 2019 19:07:34 -0600 Subject: [PATCH 075/236] fix for new text entry behaviour + oc relay fix --- sys/apps/system/password.lua | 13 +------------ sys/init/3.relay.lua | 3 +-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 4340a73..d822996 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -7,13 +7,6 @@ local colors = _G.colors local passwordTab = UI.Tab { tabTitle = 'Password', description = 'Wireless network password', - oldPass = UI.TextEntry { - x = 3, ex = -3, y = 2, - limit = 32, - mask = true, - shadowText = 'old password', - inactive = not Security.getPassword(), - }, newPass = UI.TextEntry { x = 3, ex = -3, y = 3, limit = 32, @@ -37,15 +30,11 @@ local passwordTab = UI.Tab { } function passwordTab:eventHandler(event) if event.type == 'update_password' then - if #self.newPass.value == 0 then + if not self.newPass.value or #self.newPass.value == 0 then self:emit({ type = 'error_message', message = 'Invalid password' }) - elseif Security.getPassword() and not Security.verifyPassword(SHA.compute(self.oldPass.value)) then - self:emit({ type = 'error_message', message = 'Passwords do not match' }) - else Security.updatePassword(SHA.compute(self.newPass.value)) - self.oldPass.inactive = false self:emit({ type = 'success_message', message = 'Password updated' }) end return true diff --git a/sys/init/3.relay.lua b/sys/init/3.relay.lua index a2132e7..1b24f07 100644 --- a/sys/init/3.relay.lua +++ b/sys/init/3.relay.lua @@ -8,10 +8,9 @@ local function register(v) local dev = v.getMethodsRemote(name) if dev then dev.name = name - dev.side = name + dev.side = v.side dev.type = v.getTypeRemote(name) device[name] = dev - table.insert(v._children, dev) end end end -- 2.49.1 From 748dcd8f8207378f85775fe61b28871ae8cc92c1 Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Wed, 31 Jul 2019 18:21:18 -0400 Subject: [PATCH 076/236] Remove confusion over kiosk mode (#7) --- sys/boot/kiosk.boot | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/boot/kiosk.boot b/sys/boot/kiosk.boot index ea36162..61155fe 100644 --- a/sys/boot/kiosk.boot +++ b/sys/boot/kiosk.boot @@ -15,6 +15,7 @@ end local mon = name and peripheral.wrap(name) if mon then + print("Opus OS is running in Kiosk mode, and the screen will be redirected to the monitor. To undo this, go to the boot option menu by pressing a key while booting, then select the option 2.") term.redirect(mon) mon.setTextScale(tonumber(settings.get('kiosk.textscale')) or 1) -- 2.49.1 From 6e6d4b81cd3dd506807e226a956548f67c27712e Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 3 Aug 2019 08:33:32 -0600 Subject: [PATCH 077/236] keyboard handling when not using opus os --- sys/modules/opus/input.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sys/modules/opus/input.lua b/sys/modules/opus/input.lua index 62d51b4..46b58fe 100644 --- a/sys/modules/opus/input.lua +++ b/sys/modules/opus/input.lua @@ -10,6 +10,17 @@ local modifiers = Util.transpose { keys.leftAlt, keys.rightAlt, } +if not keyboard then -- not running under Opus OS + keyboard = { state = { } } + + local Event = require('opus.event') + Event.on({ 'key', 'key_up' }, function(event, code) + if modifiers[code] then + keyboard.state[code] = event == 'key' + end + end) +end + local input = { } function input:modifierPressed() -- 2.49.1 From 2f597d0dc403be6d9bbc4e901fb012d8c772ea1a Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Tue, 13 Aug 2019 13:14:11 -0400 Subject: [PATCH 078/236] support MOTDs (#19) --- sys/apps/shell.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index c5dbf28..d64f046 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -3,6 +3,7 @@ local parentShell = _ENV.shell _ENV.shell = { } local fs = _G.fs +local settings = _G.settings local shell = _ENV.shell local sandboxEnv = setmetatable({ }, { __index = _G }) @@ -673,6 +674,10 @@ local history = History.load('usr/.shell_history', 25) term.setBackgroundColor(_colors.backgroundColor) term.clear() +if settings.get("motd.enabled") then + shell.run("motd") +end + while not bExit do if config.displayDirectory then term.setTextColour(_colors.directoryTextColor) -- 2.49.1 From 3512b2441d00beef553b19677790b6fb6f7c9d94 Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Tue, 13 Aug 2019 13:14:45 -0400 Subject: [PATCH 079/236] update license / readme (#18) * Update README.md the installer already reboots for you * Update LICENSE.md --- LICENSE.md | 2 +- README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index a5e2567..8e8b7db 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016-2017 kepler155c +Copyright (c) 2016-2019 kepler155c Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 3a2d070..7b5e66d 100644 --- a/README.md +++ b/README.md @@ -15,5 +15,4 @@ ## Install ``` pastebin run uzghlbnc -reboot ``` -- 2.49.1 From 457c8a8a526401c503141a184d3e0cdf2643b0f5 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Sun, 18 Aug 2019 14:53:27 -0400 Subject: [PATCH 080/236] Fix forms numeric validation --- sys/modules/opus/ui/components/Form.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/modules/opus/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua index 527af3f..ed9ae0d 100644 --- a/sys/modules/opus/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -100,6 +100,7 @@ function UI.Form:validateField(field) end end if field.validate == 'numeric' then + field.value = field.value or '' if #tostring(field.value) > 0 then if not tonumber(field.value) then return false, 'Invalid number' -- 2.49.1 From 774d3ed415257ba44f7687a529e5e205df1dd334 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 28 Oct 2019 20:01:57 -0600 Subject: [PATCH 081/236] improve startup, dont rely on debug (cbor) --- startup.lua | 146 ++++++++++++++++++++++--------------- sys/modules/opus/cbor.lua | 2 +- sys/modules/opus/entry.lua | 26 +++---- 3 files changed, 103 insertions(+), 71 deletions(-) diff --git a/startup.lua b/startup.lua index d94be2b..0cfcd7f 100644 --- a/startup.lua +++ b/startup.lua @@ -1,14 +1,48 @@ +--[[ + .startup.boot + delay + description: delays amount before starting the default selection + default: 1.5 + + preload + description : runs before menu is displayed, can be used for password + locking, drive encryption, etc. + example : { [1] = '/path/somefile.lua', [2] = 'path2/another.lua' } + + menu + description: array of menu entries (see .startup.boot for examples) +]] + local colors = _G.colors local os = _G.os local settings = _G.settings local term = _G.term -local bootOptions = { - { prompt = os.version() }, - { prompt = 'Opus' , args = { '/sys/boot/opus.boot' } }, - { prompt = 'Opus Shell' , args = { '/sys/boot/opus.boot', 'sys/apps/shell.lua' } }, - { prompt = 'Opus Kiosk' , args = { '/sys/boot/kiosk.boot' } }, -} +local function loadBootOptions() + if not fs.exists('.startup.boot') then + local f = fs.open('.startup.boot', 'w') + f.write(textutils.serialize({ + delay = 1.5, + preload = { }, + menu = { + { prompt = os.version() }, + { prompt = 'Opus' , args = { '/sys/boot/opus.boot' } }, + { prompt = 'Opus Shell' , args = { '/sys/boot/opus.boot', 'sys/apps/shell.lua' } }, + { prompt = 'Opus Kiosk' , args = { '/sys/boot/kiosk.boot' } }, + }, + })) + f.close() + end + + local f = fs.open('.startup.boot', 'r') + local options = textutils.unserialize(f.readAll()) + f.close() + + return options +end + +local bootOptions = loadBootOptions() + local bootOption = 2 if settings then settings.load('.settings') @@ -17,69 +51,63 @@ end local function startupMenu() local x, y = term.getSize() - local align, selected = 0, 1 + local align, selected = 0, bootOption + local function redraw() local title = "Boot Options:" term.clear() term.setTextColor(colors.white) - term.setCursorPos((x/2)-(#title/2), (y/2)-(#bootOptions/2)-1) + term.setCursorPos((x/2)-(#title/2), (y/2)-(#bootOptions.menu/2)-1) term.write(title) - for i = 1, #bootOptions do - local txt = i..". "..bootOptions[i].prompt - term.setCursorPos((x/2)-(align/2), (y/2)-(#bootOptions/2)+i) + for i, item in pairs(bootOptions.menu) do + local txt = i .. ". " .. item.prompt + term.setCursorPos((x/2)-(align/2), (y/2)-(#bootOptions.menu/2)+i) term.write(txt) end end - for i = 1, #bootOptions do - if (bootOptions[i].prompt):len() > align then - align = (bootOptions[i].prompt):len() + for _, item in pairs(bootOptions.menu) do + if #item.prompt > align then + align = #item.prompt end end redraw() - repeat - term.setCursorPos((x/2)-(align/2)-2, (y/2)-(#bootOptions/2)+selected) - if term.isColor() then - term.setTextColor(colors.yellow) - else - term.setTextColor(colors.lightGray) - end + while true do + term.setCursorPos((x/2)-(align/2)-2, (y/2)-(#bootOptions.menu/2)+selected) + term.setTextColor(term.isColor() and colors.yellow or colors.lightGray) + term.write(">") - local k = ({os.pullEvent()}) - if k[1] == "mouse_scroll" then - if k[2] == 1 then - k = keys.down - else - k = keys.up - end - elseif k[1] == "key" then - k = k[2] - else - k = nil + local event, key = os.pullEvent() + if event == "mouse_scroll" then + key = key == 1 and keys.down or keys.up + elseif event == 'key_up' then + key = nil -- only process key events end - if k then - if k == keys.enter or k == keys.right then - return selected - elseif k == keys.down then - if selected == #bootOptions then - selected = 0 - end - selected = selected+1 - elseif k == keys.up then - if selected == 1 then - selected = #bootOptions+1 - end - selected = selected-1 - elseif k >= keys.one and k <= #bootOptions+1 and k < keys.zero then - selected = k-1 - return selected + + if key == keys.enter or key == keys.right then + return selected + elseif key == keys.down then + if selected == #bootOptions.menu then + selected = 0 + end + selected = selected + 1 + elseif key == keys.up then + if selected == 1 then + selected = #bootOptions.menu + 1 + end + selected = selected - 1 + elseif event == 'char' then + key = tonumber(key) or 0 + if bootOptions.menu[key] then + return key end - local cx, cy = term.getCursorPos() - term.setCursorPos(cx-1, cy) - term.write(" ") end - until true == false + + local cx, cy = term.getCursorPos() + term.setCursorPos(cx-1, cy) + term.write(" ") + end end local function splash() @@ -120,13 +148,17 @@ end term.clear() splash() -local timerId = os.startTimer(1.5) +for _, v in pairs(bootOptions.preload) do + os.run(_ENV, v) +end + +local timerId = os.startTimer(bootOptions.delay) while true do local e, id = os.pullEvent() if e == 'timer' and id == timerId then break end - if e == 'char' then + if e == 'char' or e == 'key' then bootOption = startupMenu() if settings then settings.set('opus.boot_option', bootOption) @@ -138,9 +170,9 @@ end term.clear() term.setCursorPos(1, 1) -if bootOptions[bootOption].args then - os.run(_ENV, table.unpack(bootOptions[bootOption].args)) +if bootOptions.menu[bootOption].args then + os.run(_ENV, table.unpack(bootOptions.menu[bootOption].args)) else - print(bootOptions[bootOption].prompt) + print(bootOptions.menu[bootOption].prompt) end diff --git a/sys/modules/opus/cbor.lua b/sys/modules/opus/cbor.lua index a55df44..95e41ca 100644 --- a/sys/modules/opus/cbor.lua +++ b/sys/modules/opus/cbor.lua @@ -14,7 +14,7 @@ end local setmetatable = setmetatable; local getmetatable = getmetatable; -local dbg_getmetatable = debug.getmetatable; +local dbg_getmetatable = debug and debug.getmetatable; local assert = assert; local error = error; local type = type; diff --git a/sys/modules/opus/entry.lua b/sys/modules/opus/entry.lua index 0029ded..693fe87 100644 --- a/sys/modules/opus/entry.lua +++ b/sys/modules/opus/entry.lua @@ -195,7 +195,7 @@ end function Entry:paste(ie) if #ie.text > 0 then if self.mark.active then - self:delete() + self:delete() end self:insertText(self.pos, ie.text) end @@ -347,16 +347,16 @@ local mappings = { -- [ 'control-y' ] = Entry.paste, -- well this won't work... [ 'mouse_doubleclick' ] = Entry.markWord, - [ 'shift-left' ] = Entry.markLeft, - [ 'shift-right' ] = Entry.markRight, - [ 'mouse_down' ] = Entry.markAnchor, - [ 'mouse_drag' ] = Entry.markTo, - [ 'shift-mouse_click' ] = Entry.markTo, - [ 'control-a' ] = Entry.markAll, - [ 'control-shift-right' ] = Entry.markNextWord, - [ 'control-shift-left' ] = Entry.markPrevWord, - [ 'shift-end' ] = Entry.markEnd, - [ 'shift-home' ] = Entry.markHome, + [ 'shift-left' ] = Entry.markLeft, + [ 'shift-right' ] = Entry.markRight, + [ 'mouse_down' ] = Entry.markAnchor, + [ 'mouse_drag' ] = Entry.markTo, + [ 'shift-mouse_click' ] = Entry.markTo, + [ 'control-a' ] = Entry.markAll, + [ 'control-shift-right' ] = Entry.markNextWord, + [ 'control-shift-left' ] = Entry.markPrevWord, + [ 'shift-end' ] = Entry.markEnd, + [ 'shift-home' ] = Entry.markHome, } function Entry:process(ie) @@ -369,7 +369,7 @@ function Entry:process(ie) local line = self.value local wasMarking = self.mark.continue - self.mark.continue = false + self.mark.continue = false action(self, ie) @@ -378,7 +378,7 @@ function Entry:process(ie) self:updateScroll() if not self.mark.continue and wasMarking then - self:unmark() + self:unmark() end return true -- 2.49.1 From e5a5f76fb33fb4b88051c60978cce2372c1fb6c3 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 30 Oct 2019 22:49:30 -0600 Subject: [PATCH 082/236] restructure --- sys/apps/PackageManager.lua | 3 + sys/apps/autorun.lua | 4 +- sys/init/5.network.lua | 2 +- sys/init/6.tl3.lua | 1273 ---------------------- sys/lzwfs.lua | 247 +++++ sys/modules/opus/jumper/core/bheap.lua | 175 --- sys/modules/opus/jumper/core/node.lua | 41 - sys/modules/opus/jumper/core/path.lua | 67 -- sys/modules/opus/jumper/core/utils.lua | 57 - sys/modules/opus/jumper/grid.lua | 101 -- sys/modules/opus/jumper/pathfinder.lua | 104 -- sys/modules/opus/jumper/search/astar.lua | 77 -- sys/modules/opus/packages.lua | 23 + sys/modules/opus/pathfind.lua | 256 ----- 14 files changed, 276 insertions(+), 2154 deletions(-) delete mode 100644 sys/init/6.tl3.lua create mode 100644 sys/lzwfs.lua delete mode 100644 sys/modules/opus/jumper/core/bheap.lua delete mode 100644 sys/modules/opus/jumper/core/node.lua delete mode 100644 sys/modules/opus/jumper/core/path.lua delete mode 100644 sys/modules/opus/jumper/core/utils.lua delete mode 100644 sys/modules/opus/jumper/grid.lua delete mode 100644 sys/modules/opus/jumper/pathfinder.lua delete mode 100644 sys/modules/opus/jumper/search/astar.lua delete mode 100644 sys/modules/opus/pathfind.lua diff --git a/sys/apps/PackageManager.lua b/sys/apps/PackageManager.lua index 1329ec7..3a3dd8b 100644 --- a/sys/apps/PackageManager.lua +++ b/sys/apps/PackageManager.lua @@ -58,6 +58,9 @@ local page = UI.Page { }, }, statusBar = UI.StatusBar { }, + accelerators = { + [ 'control-q' ] = 'quit', + }, } function page:loadPackages() diff --git a/sys/apps/autorun.lua b/sys/apps/autorun.lua index e8b378f..1274666 100644 --- a/sys/apps/autorun.lua +++ b/sys/apps/autorun.lua @@ -47,8 +47,8 @@ local function runDir(directory) end runDir('sys/autorun') -for name in pairs(Packages:installed()) do - local packageDir = 'packages/' .. name .. '/autorun' +for _, package in pairs(Packages:installedSorted()) do + local packageDir = 'packages/' .. package.name .. '/autorun' runDir(packageDir) end runDir('usr/autorun') diff --git a/sys/init/5.network.lua b/sys/init/5.network.lua index 6526c5f..b02937a 100644 --- a/sys/init/5.network.lua +++ b/sys/init/5.network.lua @@ -39,7 +39,7 @@ local function setModem(dev) end end --- create a psuedo-device named 'wireleess_modem' +-- create a psuedo-device named 'wireless_modem' kernel.hook('device_attach', function(_, eventData) local dev = device[eventData[1]] if dev and dev.type == 'modem' then diff --git a/sys/init/6.tl3.lua b/sys/init/6.tl3.lua deleted file mode 100644 index e1e9ecf..0000000 --- a/sys/init/6.tl3.lua +++ /dev/null @@ -1,1273 +0,0 @@ -if not _G.turtle then - return -end - -local Pathing = require('opus.pathfind') -local Point = require('opus.point') -local synchronized = require('opus.sync').sync -local Util = require('opus.util') - -local os = _G.os -local peripheral = _G.peripheral -local turtle = _G.turtle - -local function noop() end -local headings = Point.headings -local state = { } - -turtle.pathfind = Pathing.pathfind -turtle.point = { x = 0, y = 0, z = 0, heading = 0 } - -function turtle.getState() return state end -function turtle.isAborted() return state.abort end -function turtle.getStatus() return state.status end -function turtle.setStatus(s) state.status = s end - -local function _defaultMove(action) - while not action.move() do - if not state.digPolicy(action) and not state.attackPolicy(action) then - return false - end - end - return true -end - -function turtle.getPoint() return turtle.point end -function turtle.setPoint(pt, isGPS) - turtle.point.x = pt.x - turtle.point.y = pt.y - turtle.point.z = pt.z - if pt.heading then - turtle.point.heading = pt.heading - end - turtle.point.gps = isGPS - return true -end - -function turtle.resetState() - state.abort = false - state.status = 'idle' - state.attackPolicy = noop - state.digPolicy = noop - state.movePolicy = _defaultMove - state.moveCallback = noop - state.blacklist = nil - state.reference = nil -- gps reference when converting to relative coords - Pathing.reset() - return true -end - -function turtle.reset() - turtle.point.x = 0 - turtle.point.y = 0 - turtle.point.z = 0 - turtle.point.heading = 0 -- should be facing - turtle.point.gps = false - - turtle.resetState() - return true -end - -local function _dig(name, inspect, dig) - if name then - local s, b = inspect() - if not s or b.name ~= name then - return false - end - end - return dig() -end - -function turtle.dig(s) - return _dig(s, turtle.inspect, turtle.native.dig) -end - -function turtle.digUp(s) - return _dig(s, turtle.inspectUp, turtle.native.digUp) -end - -function turtle.digDown(s) - return _dig(s, turtle.inspectDown, turtle.native.digDown) -end - -local actions = { - up = { - detect = turtle.native.detectUp, - dig = turtle.digUp, - move = turtle.native.up, - attack = turtle.native.attackUp, - place = turtle.native.placeUp, - drop = turtle.native.dropUp, - suck = turtle.native.suckUp, - compare = turtle.native.compareUp, - inspect = turtle.native.inspectUp, - side = 'top' - }, - down = { - detect = turtle.native.detectDown, - dig = turtle.digDown, - move = turtle.native.down, - attack = turtle.native.attackDown, - place = turtle.native.placeDown, - drop = turtle.native.dropDown, - suck = turtle.native.suckDown, - compare = turtle.native.compareDown, - inspect = turtle.native.inspectDown, - side = 'bottom' - }, - forward = { - detect = turtle.native.detect, - dig = turtle.dig, - move = turtle.native.forward, - attack = turtle.native.attack, - place = turtle.native.place, - drop = turtle.native.drop, - suck = turtle.native.suck, - compare = turtle.native.compare, - inspect = turtle.native.inspect, - side = 'front' - }, - back = { - detect = noop, - dig = noop, - move = turtle.native.back, - attack = noop, - place = noop, - suck = noop, - compare = noop, - side = 'back' - }, -} - -function turtle.getAction(direction) - return actions[direction] -end - -function turtle.getHeadingInfo(heading) - heading = heading or turtle.point.heading - return headings[heading] -end - -function turtle.isTurtleAtSide(side) - local sideType = peripheral.getType(side) - return sideType and sideType == 'turtle' -end - --- [[ Policies ]] -- -turtle.policies = { } - -function turtle.addPolicy(name, policy) - turtle.policies[name] = policy -end - -function turtle.getPolicy(policy) - if type(policy) == 'function' then - return policy - end - local p = turtle.policies[policy] - if not p then - error('Invalid policy: ' .. tostring(policy)) - end - return p -end - --- [[ Basic turtle actions ]] -- -local function inventoryAction(fn, name, qty) - local slots = turtle.getFilledSlots() - local s - for _,slot in pairs(slots) do - if slot.key == name or slot.name == name then - turtle.native.select(slot.index) - if not qty then - s = fn() - else - s = fn(math.min(qty, slot.count)) - qty = qty - slot.count - if qty < 0 then - break - end - end - end - end - if not s then - return false, 'No items found' - end - return s -end - --- [[ Attack ]] -- -local function _attack(action) - if action.attack() then - repeat until not action.attack() - return true - end - return false -end - -function turtle.attack() return _attack(actions.forward) end -function turtle.attackUp() return _attack(actions.up) end -function turtle.attackDown() return _attack(actions.down) end - -turtle.addPolicy('attackNone', noop) -turtle.addPolicy('attack', function(action) - return _attack(action) -end) - -function turtle.setAttackPolicy(policy) state.attackPolicy = policy end - --- [[ Place ]] -- -local function _place(action, indexOrId) - local slot - - if indexOrId then - slot = turtle.getSlot(indexOrId) - if not slot then - return false, 'No items to place' - end - end - - if slot and slot.count == 0 then - return false, 'No items to place' - end - - return Util.tryTimes(3, function() - if slot then - turtle.select(slot.index) - end - local result = { action.place() } - if result[1] then - return true - end - if not state.digPolicy(action) then - state.attackPolicy(action) - end - return table.unpack(result) - end) -end - -function turtle.place(slot) return _place(actions.forward, slot) end -function turtle.placeUp(slot) return _place(actions.up, slot) end -function turtle.placeDown(slot) return _place(actions.down, slot) end - --- [[ Drop ]] -- -local function _drop(action, qtyOrName, qty) - if not qtyOrName or type(qtyOrName) == 'number' then - return action.drop(qtyOrName or 64) - end - return inventoryAction(action.drop, qtyOrName, qty) -end - -function turtle.drop(count, slot) return _drop(actions.forward, count, slot) end -function turtle.dropUp(count, slot) return _drop(actions.up, count, slot) end -function turtle.dropDown(count, slot) return _drop(actions.down, count, slot) end - --- [[ Dig ]] -- -turtle.addPolicy('digNone', noop) - -turtle.addPolicy('dig', function(action) - return action.dig() -end) - -turtle.addPolicy('turtleSafe', function(action) - if action.side == 'back' then - return false - end - if not turtle.isTurtleAtSide(action.side) then - return action.dig() - end - return Util.tryTimes(6, function() - os.sleep(.25) - if not action.detect() then - return true - end - end) -end) - -local function isBlacklisted(b) - if b and state.blacklist then - for _, v in pairs(state.blacklist) do - if b.name:find(v) then - return true - end - end - end -end - -turtle.addPolicy('blacklist', function(action) - if action.side == 'back' then - return false - end - local s, m = action.inspect() - if not isBlacklisted(s and m) then - return action.dig() - end - if s and m and m.name:find('turtle') then - return Util.tryTimes(math.random(3, 6), function() - os.sleep(.25) - if not action.detect() then - return true - end - end) - end -end) - -turtle.addPolicy('digAndDrop', function(action) - if action.detect() then - local slots = turtle.getInventory() - if action.dig() then - turtle.reconcileInventory(slots) - return true - end - end - return false -end) - -function turtle.setDigPolicy(policy) state.digPolicy = policy end - --- [[ Move ]] -- -turtle.addPolicy('moveNone', noop) -turtle.addPolicy('moveDefault', _defaultMove) -turtle.addPolicy('moveAssured', function(action) - if not _defaultMove(action) then - if action.side == 'back' then - return false - end - local oldStatus = state.status - print('assured move: stuck') - state.status = 'stuck' - repeat - os.sleep(1) - until _defaultMove(action) - state.status = oldStatus - end - return true -end) - -function turtle.setMoveCallback(cb) state.moveCallback = cb end -function turtle.clearMoveCallback() state.moveCallback = noop end -function turtle.getMoveCallback() return state.moveCallback end - --- convenience method for setting multiple values -function turtle.set(args) - for k,v in pairs(args) do - - if k == 'attackPolicy' then - turtle.setAttackPolicy(turtle.getPolicy(v)) - - elseif k == 'digPolicy' then - turtle.setDigPolicy(turtle.getPolicy(v)) - - elseif k == 'movePolicy' then - state.movePolicy = turtle.getPolicy(v) - - elseif k == 'movementStrategy' then - turtle.setMovementStrategy(v) - - elseif k == 'pathingBox' then - turtle.setPathingBox(v) - - elseif k == 'point' then - turtle.setPoint(v) - - elseif k == 'moveCallback' then - turtle.setMoveCallback(v) - - elseif k == 'status' then - turtle.setStatus(v) - - elseif k == 'blacklist' then - state.blacklist = v - - elseif k == 'reference' then - state.reference = v - - else - error('Invalid turle.set: ' .. tostring(k)) - end - end -end - --- [[ Fuel ]] -- -if type(turtle.getFuelLevel()) ~= 'number' then - -- Support unlimited fuel - function turtle.getFuelLevel() - return 100000 - end -end - --- override to optionally specify a fuel -function turtle.refuel(qtyOrName, qty) - if not qtyOrName or type(qtyOrName) == 'number' then - return turtle.native.refuel(qtyOrName or 64) - end - return inventoryAction(turtle.native.refuel, qtyOrName, qty or 64) -end - --- [[ Heading ]] -- -function turtle.getHeading() - return turtle.point.heading -end - -function turtle.turnRight() - turtle.setHeading((turtle.point.heading + 1) % 4) - return turtle.point -end - -function turtle.turnLeft() - turtle.setHeading((turtle.point.heading - 1) % 4) - return turtle.point -end - -function turtle.turnAround() - turtle.setHeading((turtle.point.heading + 2) % 4) - return turtle.point -end - -function turtle.setHeading(heading) - if not heading then - return false, 'Invalid heading' - end - - if heading == turtle.point.heading then - return turtle.point - end - - local fi = Point.facings[heading] - if not fi then - return false, 'Invalid heading' - end - - heading = fi.heading % 4 - if heading ~= turtle.point.heading then - while heading < turtle.point.heading do - heading = heading + 4 - end - if heading - turtle.point.heading == 3 then - turtle.native.turnLeft() - turtle.point.heading = (turtle.point.heading - 1) % 4 - state.moveCallback('turn', turtle.point) - else - local turns = heading - turtle.point.heading - while turns > 0 do - turns = turns - 1 - turtle.native.turnRight() - turtle.point.heading = (turtle.point.heading + 1) % 4 - state.moveCallback('turn', turtle.point) - end - end - end - - return turtle.point -end - -function turtle.headTowardsX(dx) - if turtle.point.x ~= dx then - if turtle.point.x > dx then - turtle.setHeading(2) - else - turtle.setHeading(0) - end - end -end - -function turtle.headTowardsZ(dz) - if turtle.point.z ~= dz then - if turtle.point.z > dz then - turtle.setHeading(3) - else - turtle.setHeading(1) - end - end -end - -function turtle.headTowards(pt) - local xd = math.abs(turtle.point.x - pt.x) - local zd = math.abs(turtle.point.z - pt.z) - if xd > zd then - turtle.headTowardsX(pt.x) - else - turtle.headTowardsZ(pt.z) - end -end - --- [[ move ]] -- -function turtle.up() - if state.movePolicy(actions.up) then - turtle.point.y = turtle.point.y + 1 - state.moveCallback('up', turtle.point) - return true, turtle.point - end -end - -function turtle.down() - if state.movePolicy(actions.down) then - turtle.point.y = turtle.point.y - 1 - state.moveCallback('down', turtle.point) - return true, turtle.point - end -end - -function turtle.forward() - if state.movePolicy(actions.forward) then - turtle.point.x = turtle.point.x + headings[turtle.point.heading].xd - turtle.point.z = turtle.point.z + headings[turtle.point.heading].zd - state.moveCallback('forward', turtle.point) - return true, turtle.point - end -end - -function turtle.back() - if state.movePolicy(actions.back) then - turtle.point.x = turtle.point.x - headings[turtle.point.heading].xd - turtle.point.z = turtle.point.z - headings[turtle.point.heading].zd - state.moveCallback('back', turtle.point) - return true, turtle.point - end -end - -local function moveTowardsX(dx) - if not tonumber(dx) then error('moveTowardsX: Invalid arguments') end - local direction = dx - turtle.point.x - local move - - if direction == 0 then - return true - end - - if direction > 0 and turtle.point.heading == 0 or - direction < 0 and turtle.point.heading == 2 then - move = turtle.forward - else - move = turtle.back - end - - repeat - if not move() then - return false - end - until turtle.point.x == dx - return true -end - -local function moveTowardsZ(dz) - local direction = dz - turtle.point.z - local move - - if direction == 0 then - return true - end - - if direction > 0 and turtle.point.heading == 1 or - direction < 0 and turtle.point.heading == 3 then - move = turtle.forward - else - move = turtle.back - end - - repeat - if not move() then - return false - end - until turtle.point.z == dz - return true -end - --- [[ go ]] -- --- 1 turn goto (going backwards if possible) -function turtle.gotoSingleTurn(dx, dy, dz, dh) - dx = dx or turtle.point.x - dy = dy or turtle.point.y - dz = dz or turtle.point.z - - local function gx() - if turtle.point.x ~= dx then - moveTowardsX(dx) - end - if turtle.point.z ~= dz then - if dh and dh % 2 == 1 then - turtle.setHeading(dh) - else - turtle.headTowardsZ(dz) - end - end - end - - local function gz() - if turtle.point.z ~= dz then - moveTowardsZ(dz) - end - if turtle.point.x ~= dx then - if dh and dh % 2 == 0 then - turtle.setHeading(dh) - else - turtle.headTowardsX(dx) - end - end - end - - repeat - local x, z - local y = turtle.point.y - - repeat - x, z = turtle.point.x, turtle.point.z - - if turtle.point.heading % 2 == 0 then - gx() - gz() - else - gz() - gx() - end - until x == turtle.point.x and z == turtle.point.z - - if turtle.point.y ~= dy then - turtle.gotoY(dy) - end - - if turtle.point.x == dx and turtle.point.z == dz and turtle.point.y == dy then - return true - end - - until x == turtle.point.x and z == turtle.point.z and y == turtle.point.y - - return false -end - -local function gotoEx(dx, dy, dz) - -- determine the heading to ensure the least amount of turns - -- first check is 1 turn needed - remaining require 2 turns - if turtle.point.heading == 0 and turtle.point.x <= dx or - turtle.point.heading == 2 and turtle.point.x >= dx or - turtle.point.heading == 1 and turtle.point.z <= dz or - turtle.point.heading == 3 and turtle.point.z >= dz then - -- maintain current heading - -- nop - elseif dz > turtle.point.z and turtle.point.heading == 0 or - dz < turtle.point.z and turtle.point.heading == 2 or - dx < turtle.point.x and turtle.point.heading == 1 or - dx > turtle.point.x and turtle.point.heading == 3 then - turtle.turnRight() - else - turtle.turnLeft() - end - - if (turtle.point.heading % 2) == 1 then - if not turtle.gotoZ(dz) then return false end - if not turtle.gotoX(dx) then return false end - else - if not turtle.gotoX(dx) then return false end - if not turtle.gotoZ(dz) then return false end - end - - if dy then - if not turtle.gotoY(dy) then return false end - end - - return true -end - --- fallback goto - will turn around if was previously moving backwards -local function gotoMultiTurn(dx, dy, dz) - if gotoEx(dx, dy, dz) then - return true - end - - local moved - repeat - local x, y, z = turtle.point.x, turtle.point.y, turtle.point.z - - -- try going the other way - if (turtle.point.heading % 2) == 1 then - turtle.headTowardsX(dx) - else - turtle.headTowardsZ(dz) - end - - if gotoEx(dx, dy, dz) then - return true - end - - if dy then - turtle.gotoY(dy) - end - - moved = x ~= turtle.point.x or y ~= turtle.point.y or z ~= turtle.point.z - until not moved - - return false -end - --- go backwards - turning around if necessary to fight mobs / break blocks -function turtle.goback() - local hi = headings[turtle.point.heading] - return turtle.go({ - x = turtle.point.x - hi.xd, - y = turtle.point.y, - z = turtle.point.z - hi.zd, - heading = turtle.point.heading, - }) -end - -function turtle.gotoYfirst(pt) - if turtle.gotoY(pt.y) then - if turtle.go(pt) then - turtle.setHeading(pt.heading) - return true - end - end -end - -function turtle.go(pt) - if not pt.x and not pt.z and pt.y then - if turtle.gotoY(pt.y) then - turtle.setHeading(pt.heading) - return true - end - return false, 'Failed to reach location' - end - - local dx = pt.x or turtle.point.x - local dz = pt.z or turtle.point.z - local dy, dh = pt.y, pt.heading - if not turtle.gotoSingleTurn(dx, dy, dz, dh) then - if not gotoMultiTurn(dx, dy, dz) then - return false, 'Failed to reach location' - end - end - turtle.setHeading(dh) - return pt -end - --- avoid lint errors --- deprecated -turtle['goto'] = turtle.go -turtle['_goto'] = turtle.go - --- TODO: localize these goto functions -function turtle.gotoX(dx) - turtle.headTowardsX(dx) - - while turtle.point.x ~= dx do - if not turtle.forward() then - return false - end - end - return true -end - -function turtle.gotoZ(dz) - turtle.headTowardsZ(dz) - - while turtle.point.z ~= dz do - if not turtle.forward() then - return false - end - end - return true -end - -function turtle.gotoY(dy) - while turtle.point.y > dy do - if not turtle.down() then - return false - end - end - - while turtle.point.y < dy do - if not turtle.up() then - return false - end - end - return true -end - --- [[ Inventory ]] -- -function turtle.getSlot(indexOrId, slots) - if type(indexOrId) == 'string' then - slots = slots or turtle.getInventory() - local _,c = string.gsub(indexOrId, ':', '') - if c == 2 then -- combined id and dmg .. ie. minecraft:coal:0 - return Util.find(slots, 'key', indexOrId) - end - return Util.find(slots, 'name', indexOrId) - end - - local detail = turtle.getItemDetail(indexOrId) - if detail then - return { - name = detail.name, - damage = detail.damage, - count = detail.count, - key = detail.name .. ':' .. detail.damage, - - index = indexOrId, - - -- deprecate - qty = detail.count, - dmg = detail.damage, - id = detail.name, - } - end - - -- inconsistent return value - -- null is returned if indexOrId is a string and no item is present - return { - qty = 0, -- deprecate - count = 0, - index = indexOrId, - } -end - -function turtle.select(indexOrId) - if type(indexOrId) == 'number' then - return turtle.native.select(indexOrId) - end - - local s = turtle.getSlot(indexOrId) - if s then - turtle.native.select(s.index) - return s - end - - return false, 'Inventory does not contain item' -end - -function turtle.getInventory(slots) - slots = slots or { } - for i = 1, 16 do - slots[i] = turtle.getSlot(i) - end - return slots -end - -function turtle.getSummedInventory() - local slots = turtle.getFilledSlots() - local t = { } - for _,slot in pairs(slots) do - local entry = t[slot.key] - if not entry then - entry = { - count = 0, - damage = slot.damage, - name = slot.name, - key = slot.key, - - -- deprecate - qty = 0, - dmg = slot.dmg, - id = slot.id, - } - t[slot.key] = entry - end - entry.qty = entry.qty + slot.qty - entry.count = entry.qty - end - return t -end - -function turtle.has(item, count) - if item:match('.*:%d') then - local slot = turtle.getSummedInventory()[item] - return slot and slot.count >= (count or 1) - end - local slot = turtle.getSlot(item) - return slot and slot.count >= (count or 1) -end - -function turtle.getFilledSlots(startSlot) - startSlot = startSlot or 1 - - local slots = { } - for i = startSlot, 16 do - local count = turtle.getItemCount(i) - if count > 0 then - slots[i] = turtle.getSlot(i) - end - end - return slots -end - -function turtle.eachFilledSlot(fn) - local slots = turtle.getFilledSlots() - for _,slot in pairs(slots) do - fn(slot) - end -end - -function turtle.emptyInventory(dropAction) - dropAction = dropAction or turtle.native.drop - turtle.eachFilledSlot(function(slot) - turtle.select(slot.index) - dropAction() - end) - turtle.select(1) -end - -function turtle.reconcileInventory(slots, dropAction) - dropAction = dropAction or turtle.native.drop - for _,s in pairs(slots) do - local qty = turtle.getItemCount(s.index) - if qty > s.qty then - turtle.select(s.index) - dropAction(qty-s.qty, s) - end - end -end - -function turtle.selectSlotWithItems(startSlot) - startSlot = startSlot or 1 - for i = startSlot, 16 do - if turtle.getItemCount(i) > 0 then - turtle.select(i) - return i - end - end -end - -function turtle.selectSlotWithQuantity(qty, startSlot) - startSlot = startSlot or 1 - - for i = startSlot, 16 do - if turtle.getItemCount(i) == qty then - turtle.select(i) - return i - end - end -end - -function turtle.selectOpenSlot(startSlot) - return turtle.selectSlotWithQuantity(0, startSlot) -end - -function turtle.condense() - local slots = turtle.getInventory() - - for i = 1, 16 do - if slots[i].count < 64 then - for j = 16, i + 1, -1 do - if slots[j].count > 0 and (slots[i].count == 0 or slots[i].key == slots[j].key) then - turtle.select(j) - if turtle.transferTo(i) then - local transferred = turtle.getItemCount(i) - slots[i].count - slots[j].count = slots[j].count - transferred - slots[i].count = slots[i].count + transferred - slots[i].key = slots[j].key - if slots[j].count == 0 then - slots[j].key = nil - end - if slots[i].count == 64 then - break - end - else - break - end - end - end - end - end - turtle.select(1) - return true -end - -function turtle.getItemCount(idOrName) - if type(idOrName) == 'number' then - return turtle.native.getItemCount(idOrName) - end - local slots = turtle.getFilledSlots() - local count = 0 - for _,slot in pairs(slots) do - if slot.key == idOrName or slot.name == idOrName then - count = count + slot.count - end - end - return count -end - --- [[ Equipment ]] -- -function turtle.equip(side, item) - if item then - if not turtle.select(item) then - return false, 'Unable to equip ' .. item - end - end - - if side == 'left' then - return turtle.equipLeft() - end - return turtle.equipRight() -end - -function turtle.isEquipped(item) - if peripheral.getType('left') == item then - return 'left' - elseif peripheral.getType('right') == item then - return 'right' - end -end - -function turtle.unequip(side) - if not turtle.selectSlotWithQuantity(0) then - return false, 'No slots available' - end - return turtle.equip(side) -end - --- deprecate -function turtle.run(fn, ...) - local args = { ... } - local s, m - - if type(fn) == 'string' then - fn = turtle[fn] - end - - synchronized(turtle, function() - turtle.resetState() - s, m = pcall(function() fn(table.unpack(args)) end) - turtle.resetState() - if not s and m then - _G.printError(m) - end - end) - - return s, m -end - -function turtle.abort(abort) - state.abort = abort - if abort then - os.queueEvent('turtle_abort') - end -end - --- [[ Pathing ]] -- -function turtle.setPersistent(isPersistent) - if isPersistent then - Pathing.setBlocks({ }) - else - Pathing.setBlocks() - end -end - -function turtle.setPathingBox(box) - Pathing.setBox(box) -end - -function turtle.addWorldBlock(pt) - Pathing.addBlock(pt) -end - -function turtle.addWorldBlocks(pts) - Util.each(pts, function(pt) - Pathing.addBlock(pt) - end) -end - -local movementStrategy = turtle.pathfind - -function turtle.setMovementStrategy(strategy) - if strategy == 'pathing' then - movementStrategy = turtle.pathfind - elseif strategy == 'goto' then - movementStrategy = turtle.go - else - error('Invalid movement strategy') - end -end - -function turtle.faceAgainst(pt, options) -- 4 sided - options = options or { } - options.dest = { } - - for i = 0, 3 do - local hi = Point.facings[i] - table.insert(options.dest, { - x = pt.x + hi.xd, - z = pt.z + hi.zd, - y = pt.y + hi.yd, - heading = (hi.heading + 2) % 4, - }) - end - - return movementStrategy(Point.closest(turtle.point, options.dest), options) -end - --- move against this point --- if the point does not contain a heading, then the turtle --- will face the block (if on same plane) --- if above or below, the heading is undetermined unless specified -function turtle.moveAgainst(pt, options) -- 6 sided - options = options or { } - options.dest = { } - - for i = 0, 5 do - local hi = turtle.getHeadingInfo(i) - local heading, direction - if i < 4 then - heading = (hi.heading + 2) % 4 - direction = 'forward' - elseif i == 4 then - direction = 'down' - elseif i == 5 then - direction = 'up' - end - - table.insert(options.dest, { - x = pt.x + hi.xd, - z = pt.z + hi.zd, - y = pt.y + hi.yd, - direction = direction, - heading = pt.heading or heading, - }) - end - - return movementStrategy(Point.closest(turtle.point, options.dest), options) -end - -local actionsAt = { - detect = { - up = turtle.detectUp, - down = turtle.detectDown, - forward = turtle.detect, - }, - dig = { - up = turtle.digUp, - down = turtle.digDown, - forward = turtle.dig, - }, - move = { - up = turtle.moveUp, - down = turtle.moveDown, - forward = turtle.move, - }, - attack = { - up = turtle.attackUp, - down = turtle.attackDown, - forward = turtle.attack, - }, - place = { - up = turtle.placeUp, - down = turtle.placeDown, - forward = turtle.place, - }, - drop = { - up = turtle.dropUp, - down = turtle.dropDown, - forward = turtle.drop, - }, - suck = { - up = turtle.suckUp, - down = turtle.suckDown, - forward = turtle.suck, - }, - compare = { - up = turtle.compareUp, - down = turtle.compareDown, - forward = turtle.compare, - }, - inspect = { - up = turtle.inspectUp, - down = turtle.inspectDown, - forward = turtle.inspect, - }, -} - --- pt = { x,y,z,heading,direction } --- direction should only be up or down if provided --- heading can be provided to tell which way to face during action --- ex: place a block at the point from above facing east -local function _actionAt(action, pt, ...) - if not pt.heading and not pt.direction then - local msg - pt, msg = turtle.moveAgainst(pt) - if pt then - return action[pt.direction](...) - end - return pt, msg - end - - local reversed = - { [0] = 2, [1] = 3, [2] = 0, [3] = 1, [4] = 5, [5] = 4, } - local dir = reversed[headings[pt.direction or pt.heading].heading] - local apt = { x = pt.x + headings[dir].xd, - y = pt.y + headings[dir].yd, - z = pt.z + headings[dir].zd, } - local direction - - -- ex: place a block at this point, from above, facing east - if dir < 4 then - apt.heading = (dir + 2) % 4 - direction = 'forward' - elseif dir == 4 then - apt.heading = pt.heading - direction = 'down' - elseif dir == 5 then - apt.heading = pt.heading - direction = 'up' - end - - if movementStrategy(apt) then - return action[direction](...) - end -end - -local function _actionDownAt(action, pt, ...) - pt = Util.shallowCopy(pt) - pt.direction = Point.DOWN - return _actionAt(action, pt, ...) -end - -local function _actionUpAt(action, pt, ...) - pt = Util.shallowCopy(pt) - pt.direction = Point.UP - return _actionAt(action, pt, ...) -end - -local function _actionForwardAt(action, pt, ...) - if turtle.faceAgainst(pt) then - return action.forward(...) - end -end - -function turtle.detectAt(pt) return _actionAt(actionsAt.detect, pt) end -function turtle.detectDownAt(pt) return _actionDownAt(actionsAt.detect, pt) end -function turtle.detectForwardAt(pt) return _actionForwardAt(actionsAt.detect, pt) end -function turtle.detectUpAt(pt) return _actionUpAt(actionsAt.detect, pt) end - -function turtle.digAt(pt, ...) return _actionAt(actionsAt.dig, pt, ...) end -function turtle.digDownAt(pt, ...) return _actionDownAt(actionsAt.dig, pt, ...) end -function turtle.digForwardAt(pt, ...) return _actionForwardAt(actionsAt.dig, pt, ...) end -function turtle.digUpAt(pt, ...) return _actionUpAt(actionsAt.dig, pt, ...) end - -function turtle.attackAt(pt) return _actionAt(actionsAt.attack, pt) end -function turtle.attackDownAt(pt) return _actionDownAt(actionsAt.attack, pt) end -function turtle.attackForwardAt(pt) return _actionForwardAt(actionsAt.attack, pt) end -function turtle.attackUpAt(pt) return _actionUpAt(actionsAt.attack, pt) end - -function turtle.placeAt(pt, arg, dir) return _actionAt(actionsAt.place, pt, arg, dir) end -function turtle.placeDownAt(pt, arg) return _actionDownAt(actionsAt.place, pt, arg) end -function turtle.placeForwardAt(pt, arg) return _actionForwardAt(actionsAt.place, pt, arg) end -function turtle.placeUpAt(pt, arg) return _actionUpAt(actionsAt.place, pt, arg) end - -function turtle.dropAt(pt, ...) return _actionAt(actionsAt.drop, pt, ...) end -function turtle.dropDownAt(pt, ...) return _actionDownAt(actionsAt.drop, pt, ...) end -function turtle.dropForwardAt(pt, ...) return _actionForwardAt(actionsAt.drop, pt, ...) end -function turtle.dropUpAt(pt, ...) return _actionUpAt(actionsAt.drop, pt, ...) end - -function turtle.suckAt(pt, qty) return _actionAt(actionsAt.suck, pt, qty or 64) end -function turtle.suckDownAt(pt, qty) return _actionDownAt(actionsAt.suck, pt, qty or 64) end -function turtle.suckForwardAt(pt, qty) return _actionForwardAt(actionsAt.suck, pt, qty or 64) end -function turtle.suckUpAt(pt, qty) return _actionUpAt(actionsAt.suck, pt, qty or 64) end - -function turtle.compareAt(pt) return _actionAt(actionsAt.compare, pt) end -function turtle.compareDownAt(pt) return _actionDownAt(actionsAt.compare, pt) end -function turtle.compareForwardAt(pt) return _actionForwardAt(actionsAt.compare, pt) end -function turtle.compareUpAt(pt) return _actionUpAt(actionsAt.compare, pt) end - -function turtle.inspectAt(pt) return _actionAt(actionsAt.inspect, pt) end -function turtle.inspectDownAt(pt) return _actionDownAt(actionsAt.inspect, pt) end -function turtle.inspectForwardAt(pt) return _actionForwardAt(actionsAt.inspect, pt) end -function turtle.inspectUpAt(pt) return _actionUpAt(actionsAt.inspect, pt) end - -turtle.reset() diff --git a/sys/lzwfs.lua b/sys/lzwfs.lua new file mode 100644 index 0000000..ac2ddb8 --- /dev/null +++ b/sys/lzwfs.lua @@ -0,0 +1,247 @@ +-- see: https://github.com/Rochet2/lualzw +-- MIT License - Copyright (c) 2016 Rochet2 + +-- Transparent file system compression for non-binary files using lzw + +-- Files that are compressed will have the first bytes in file set to 'LZWC'. +-- If a file does not benefit from compression, the contents will not be altered. + +-- Allow exclusions for files that shouldn't be compressed +-- Also allow for future types of exclusions using bit operations +-- 1 is reserved for compression exclusion +-- fs.addException('startup.lua', 1) + +-- To renable compression for a file +-- fs.removeException('startup.lua', 1) + +-- Restores file system +-- fs.restore() + +local char = string.char +local type = type +local sub = string.sub +local tconcat = table.concat +local tinsert = table.insert + +local SIGC = 'LZWC' +local IGNORE_COMPRESSION = 1 -- support other bits as well + +local basedictcompress = {} +local basedictdecompress = {} +for i = 0, 255 do + local ic, iic = char(i), char(i, 0) + basedictcompress[ic] = iic + basedictdecompress[iic] = ic +end + +local native = { open = fs.open } +fs.exceptions = fs.exceptions or { } + +local function dictAddA(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[str] = char(a,b) + a = a+1 + return dict, a, b +end + +local function compress(input) + if type(input) ~= "string" then + error ("string expected, got "..type(input)) + end + local len = #input + if len <= 1 then + return input + end + + local dict = {} + local a, b = 0, 1 + + local result = { SIGC } + local resultlen = 1 + local n = 2 + local word = "" + for i = 1, len do + local c = sub(input, i, i) + local wc = word..c + if not (basedictcompress[wc] or dict[wc]) then + local write = basedictcompress[word] or dict[word] + if not write then + error "algorithm error, could not fetch word" + end + result[n] = write + resultlen = resultlen + #write + n = n+1 + if len <= resultlen then + return input + end + dict, a, b = dictAddA(wc, dict, a, b) + word = c + else + word = wc + end + end + result[n] = basedictcompress[word] or dict[word] + resultlen = resultlen+#result[n] + if len <= resultlen then + return input + end + return tconcat(result) +end + +local function dictAddB(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[char(a,b)] = str + a = a+1 + return dict, a, b +end + +local function decompress(input) + if type(input) ~= "string" then + error( "string expected, got "..type(input)) + end + + if #input <= 1 then + return input + end + + local control = sub(input, 1, 4) + if control ~= SIGC then + return input + end + input = sub(input, 5) + local len = #input + + if len < 2 then + error("invalid input - not a compressed string") + end + + local dict = {} + local a, b = 0, 1 + + local result = {} + local n = 1 + local last = sub(input, 1, 2) + result[n] = basedictdecompress[last] or dict[last] + n = n+1 + for i = 3, len, 2 do + local code = sub(input, i, i+1) + local lastStr = basedictdecompress[last] or dict[last] + if not lastStr then + error( "could not find last from dict. Invalid input?") + end + local toAdd = basedictdecompress[code] or dict[code] + if toAdd then + result[n] = toAdd + n = n+1 + dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b) + else + local tmp = lastStr..sub(lastStr, 1, 1) + result[n] = tmp + n = n+1 + dict, a, b = dictAddB(tmp, dict, a, b) + end + last = code + end + return tconcat(result) +end + +function split(str, pattern) + pattern = pattern or "(.-)\n" + local t = {} + local function helper(line) tinsert(t, line) return "" end + helper((str:gsub(pattern, helper))) + return t +end + +function fs.open(fname, flags) + if flags == 'r' then + local f, err = native.open(fname, 'rb') + if not f then + return f, err + end + + local ctr = 0 + local lines + return { + readLine = function() + if not lines then + lines = split(decompress(f.readAll())) + end + ctr = ctr + 1 + return lines[ctr] + end, + readAll = function() + return decompress(f.readAll()) + end, + close = function() + f.close() + end, + } + elseif flags == 'w' or flags == 'a' then + if bit.band(fs.exceptions[fs.combine(fname, '')] or 0, IGNORE_COMPRESSION) == IGNORE_COMPRESSION then + return native.open(fname, flags) + end + + local c = { } + + if flags == 'a' then + local f = fs.open(fname, 'r') + if f then + tinsert(c, f.readAll()) + f.close() + end + end + + local f, err = native.open(fname, 'wb') + if not f then + return f, err + end + + return { + write = function(str) + tinsert(c, str) + end, + writeLine = function(str) + tinsert(c, str) + tinsert(c, '\n') + end, + flush = function() + -- this isn't gonna work... + // f.write(compress(tconcat(c))) + f.flush(); + end, + close = function() + f.write(compress(tconcat(c))) + f.close() + end, + } + end + + return native.open(fname, flags) +end + +function fs.addException(fname, mode) + fname = fs.combine(fname, '') + fs.exceptions[fname] = bit.bor(fs.exceptions[fname] or 0, mode) +end + +function fs.removeException(fname, mode) + fname = fs.combine(fname, '') + fs.exceptions[fname] = bit.bxor(fs.exceptions[fname] or 0, mode) +end + +function fs.restore() + fs.open = native.open +end diff --git a/sys/modules/opus/jumper/core/bheap.lua b/sys/modules/opus/jumper/core/bheap.lua deleted file mode 100644 index c58ce2f..0000000 --- a/sys/modules/opus/jumper/core/bheap.lua +++ /dev/null @@ -1,175 +0,0 @@ ---- A light implementation of Binary heaps data structure. --- While running a search, some search algorithms (Astar, Dijkstra, Jump Point Search) have to maintains --- a list of nodes called __open list__. Retrieve from this list the lowest cost node can be quite slow, --- as it normally requires to skim through the full set of nodes stored in this list. This becomes a real --- problem especially when dozens of nodes are being processed (on large maps). --- --- The current module implements a binary heap --- data structure, from which the search algorithm will instantiate an open list, and cache the nodes being --- examined during a search. As such, retrieving the lower-cost node is faster and globally makes the search end --- up quickly. --- --- This module is internally used by the library on purpose. --- It should normally not be used explicitely, yet it remains fully accessible. --- - ---[[ - Notes: - This lighter implementation of binary heaps, based on : - https://github.com/Yonaba/Binary-Heaps ---]] - -if (...) then - - -- Dependency - local Utils = require((...):gsub('%.bheap$','.utils')) - - -- Local reference - local floor = math.floor - - -- Default comparison function - local function f_min(a,b) return a < b end - - -- Percolates up - local function percolate_up(heap, index) - if index == 1 then return end - local pIndex - if index <= 1 then return end - if index%2 == 0 then - pIndex = index/2 - else pIndex = (index-1)/2 - end - if not heap._sort(heap._heap[pIndex], heap._heap[index]) then - heap._heap[pIndex], heap._heap[index] = - heap._heap[index], heap._heap[pIndex] - percolate_up(heap, pIndex) - end - end - - -- Percolates down - local function percolate_down(heap,index) - local lfIndex,rtIndex,minIndex - lfIndex = 2*index - rtIndex = lfIndex + 1 - if rtIndex > heap._size then - if lfIndex > heap._size then return - else minIndex = lfIndex end - else - if heap._sort(heap._heap[lfIndex],heap._heap[rtIndex]) then - minIndex = lfIndex - else - minIndex = rtIndex - end - end - if not heap._sort(heap._heap[index],heap._heap[minIndex]) then - heap._heap[index],heap._heap[minIndex] = heap._heap[minIndex],heap._heap[index] - percolate_down(heap,minIndex) - end - end - - -- Produces a new heap - local function newHeap(template,comp) - return setmetatable({_heap = {}, - _sort = comp or f_min, _size = 0}, - template) - end - - - --- The `heap` class.
- -- This class is callable. - -- _Therefore,_ heap(...) _is used to instantiate new heaps_. - -- @type heap - local heap = setmetatable({}, - {__call = function(self,...) - return newHeap(self,...) - end}) - heap.__index = heap - - --- Checks if a `heap` is empty - -- @class function - -- @treturn bool __true__ of no item is queued in the heap, __false__ otherwise - -- @usage - -- if myHeap:empty() then - -- print('Heap is empty!') - -- end - function heap:empty() - return (self._size==0) - end - - --- Clears the `heap` (removes all items queued in the heap) - -- @class function - -- @treturn heap self (the calling `heap` itself, can be chained) - -- @usage myHeap:clear() - function heap:clear() - self._heap = {} - self._size = 0 - self._sort = self._sort or f_min - return self - end - - --- Adds a new item in the `heap` - -- @class function - -- @tparam value item a new value to be queued in the heap - -- @treturn heap self (the calling `heap` itself, can be chained) - -- @usage - -- myHeap:push(1) - -- -- or, with chaining - -- myHeap:push(1):push(2):push(4) - function heap:push(item) - if item then - self._size = self._size + 1 - self._heap[self._size] = item - percolate_up(self, self._size) - end - return self - end - - --- Pops from the `heap`. - -- Removes and returns the lowest cost item (with respect to the comparison function being used) from the `heap`. - -- @class function - -- @treturn value a value previously pushed into the heap - -- @usage - -- while not myHeap:empty() do - -- local lowestValue = myHeap:pop() - -- ... - -- end - function heap:pop() - local root - if self._size > 0 then - root = self._heap[1] - self._heap[1] = self._heap[self._size] - self._heap[self._size] = nil - self._size = self._size-1 - if self._size>1 then - percolate_down(self, 1) - end - end - return root - end - - --- Restores the `heap` property. - -- Reorders the `heap` with respect to the comparison function being used. - -- When given argument __item__ (a value existing in the `heap`), will sort from that very item in the `heap`. - -- Otherwise, the whole `heap` will be cheacked. - -- @class function - -- @tparam[opt] value item the modified value - -- @treturn heap self (the calling `heap` itself, can be chained) - -- @usage myHeap:heapify() - function heap:heapify(item) - if self._size == 0 then return end - if item then - local i = Utils.indexOf(self._heap,item) - if i then - percolate_down(self, i) - percolate_up(self, i) - end - return - end - for i = floor(self._size/2),1,-1 do - percolate_down(self,i) - end - return self - end - - return heap -end \ No newline at end of file diff --git a/sys/modules/opus/jumper/core/node.lua b/sys/modules/opus/jumper/core/node.lua deleted file mode 100644 index 09d1db4..0000000 --- a/sys/modules/opus/jumper/core/node.lua +++ /dev/null @@ -1,41 +0,0 @@ ---- The Node class. --- The `node` represents a cell (or a tile) on a collision map. Basically, for each single cell (tile) --- in the collision map passed-in upon initialization, a `node` object will be generated --- and then cached within the `grid`. --- --- In the following implementation, nodes can be compared using the `<` operator. The comparison is --- made with regards of their `f` cost. From a given node being examined, the `pathfinder` will expand the search --- to the next neighbouring node having the lowest `f` cost. See `core.bheap` for more details. --- -if (...) then - - local Node = {} - Node.__index = Node - - function Node:new(x,y,z) - return setmetatable({x = x, y = y, z = z }, Node) - end - - -- Enables the use of operator '<' to compare nodes. - -- Will be used to sort a collection of nodes in a binary heap on the basis of their F-cost - function Node.__lt(A,B) return (A._f < B._f) end - - function Node:getX() return self.x end - function Node:getY() return self.y end - function Node:getZ() return self.z end - - --- Clears temporary cached attributes of a `node`. - -- Deletes the attributes cached within a given node after a pathfinding call. - -- This function is internally used by the search algorithms, so you should not use it explicitely. - function Node:reset() - self._g, self._h, self._f = nil, nil, nil - self._opened, self._closed, self._parent = nil, nil, nil - return self - end - - return setmetatable(Node, - {__call = function(_,...) - return Node:new(...) - end} - ) -end \ No newline at end of file diff --git a/sys/modules/opus/jumper/core/path.lua b/sys/modules/opus/jumper/core/path.lua deleted file mode 100644 index f02c41b..0000000 --- a/sys/modules/opus/jumper/core/path.lua +++ /dev/null @@ -1,67 +0,0 @@ ---- The Path class. --- The `path` class is a structure which represents a path (ordered set of nodes) from a start location to a goal. --- An instance from this class would be a result of a request addressed to `Pathfinder:getPath`. --- --- This module is internally used by the library on purpose. --- It should normally not be used explicitely, yet it remains fully accessible. --- - -if (...) then - - local t_remove = table.remove - - local Path = {} - Path.__index = Path - - function Path:new() - return setmetatable({_nodes = {}}, Path) - end - - --- Iterates on each single `node` along a `path`. At each step of iteration, - -- returns the `node` plus a count value. Aliased as @{Path:nodes} - -- @usage - -- for node, count in p:iter() do - -- ... - -- end - function Path:nodes() - local i = 1 - return function() - if self._nodes[i] then - i = i+1 - return self._nodes[i-1],i-1 - end - end - end - - --- `Path` compression modifier. Given a `path`, eliminates useless nodes to return a lighter `path` - -- consisting of straight moves. Does the opposite of @{Path:fill} - -- @class function - -- @treturn path self (the calling `path` itself, can be chained) - -- @see Path:fill - -- @usage p:filter() - function Path:filter() - local i = 2 - local xi,yi,zi,dx,dy,dz, olddx, olddy, olddz - xi,yi,zi = self._nodes[i].x, self._nodes[i].y, self._nodes[i].z - dx, dy,dz = xi - self._nodes[i-1].x, yi-self._nodes[i-1].y, zi-self._nodes[i-1].z - while true do - olddx, olddy, olddz = dx, dy, dz - if self._nodes[i+1] then - i = i+1 - xi, yi, zi = self._nodes[i].x, self._nodes[i].y, self._nodes[i].z - dx, dy, dz = xi - self._nodes[i-1].x, yi - self._nodes[i-1].y, zi - self._nodes[i-1].z - if olddx == dx and olddy == dy and olddz == dz then - t_remove(self._nodes, i-1) - i = i - 1 - end - else break end - end - return self - end - - return setmetatable(Path, - {__call = function(_,...) - return Path:new(...) - end - }) -end \ No newline at end of file diff --git a/sys/modules/opus/jumper/core/utils.lua b/sys/modules/opus/jumper/core/utils.lua deleted file mode 100644 index ab5f764..0000000 --- a/sys/modules/opus/jumper/core/utils.lua +++ /dev/null @@ -1,57 +0,0 @@ --- Various utilities for Jumper top-level modules - -if (...) then - - -- Dependencies - local _PATH = (...):gsub('%.utils$','') - local Path = require (_PATH .. '.path') - - -- Local references - local pairs = pairs - local t_insert = table.insert - - -- Raw array items count - local function arraySize(t) - local count = 0 - for _ in pairs(t) do - count = count+1 - end - return count - end - - -- Extract a path from a given start/end position - local function traceBackPath(finder, node, startNode) - local path = Path:new() - path._grid = finder._grid - while true do - if node._parent then - t_insert(path._nodes,1,node) - node = node._parent - else - t_insert(path._nodes,1,startNode) - return path - end - end - end - - -- Lookup for value in a table - local indexOf = function(t,v) - for i = 1,#t do - if t[i] == v then return i end - end - return nil - end - - -- Is i out of range - local function outOfRange(i,low,up) - return (i< low or i > up) - end - - return { - arraySize = arraySize, - indexOf = indexOf, - outOfRange = outOfRange, - traceBackPath = traceBackPath - } - -end diff --git a/sys/modules/opus/jumper/grid.lua b/sys/modules/opus/jumper/grid.lua deleted file mode 100644 index 6cfc53a..0000000 --- a/sys/modules/opus/jumper/grid.lua +++ /dev/null @@ -1,101 +0,0 @@ ---- The Grid class. --- Implementation of the `grid` class. --- The `grid` is a implicit graph which represents the 2D --- world map layout on which the `pathfinder` object will run. --- During a search, the `pathfinder` object needs to save some critical values. --- These values are cached within each `node` --- object, and the whole set of nodes are tight inside the `grid` object itself. - -if (...) then - - -- Dependencies - local _PATH = (...):gsub('%.grid$','') - - -- Local references - local Utils = require (_PATH .. '.core.utils') - local Node = require (_PATH .. '.core.node') - - -- Local references - local setmetatable = setmetatable - - -- Offsets for straights moves - local straightOffsets = { - {x = 1, y = 0, z = 0} --[[W]], {x = -1, y = 0, z = 0}, --[[E]] - {x = 0, y = 1, z = 0} --[[S]], {x = 0, y = -1, z = 0}, --[[N]] - {x = 0, y = 0, z = 1} --[[U]], {x = 0, y = -0, z = -1}, --[[D]] - } - - local Grid = {} - Grid.__index = Grid - - function Grid:new(dim) - local newGrid = { } - newGrid._min_x, newGrid._max_x = dim.x, dim.ex - newGrid._min_y, newGrid._max_y = dim.y, dim.ey - newGrid._min_z, newGrid._max_z = dim.z, dim.ez - newGrid._nodes = { } - newGrid._width = (newGrid._max_x-newGrid._min_x)+1 - newGrid._height = (newGrid._max_y-newGrid._min_y)+1 - newGrid._length = (newGrid._max_z-newGrid._min_z)+1 - return setmetatable(newGrid,Grid) - end - - function Grid:isWalkableAt(x, y, z) - local node = self:getNodeAt(x,y,z) - return node and node.walkable ~= 1 - end - - function Grid:getWidth() - return self._width - end - - function Grid:getHeight() - return self._height - end - - function Grid:getNodes() - return self._nodes - end - - function Grid:getBounds() - return self._min_x, self._min_y, self._min_z, self._max_x, self._max_y, self._max_z - end - - --- Returns neighbours. The returned value is an array of __walkable__ nodes neighbouring a given `node`. - -- @treturn {node,...} an array of nodes neighbouring a given node - function Grid:getNeighbours(node) - local neighbours = {} - for i = 1,#straightOffsets do - local n = self:getNodeAt( - node.x + straightOffsets[i].x, - node.y + straightOffsets[i].y, - node.z + straightOffsets[i].z - ) - if n and self:isWalkableAt(n.x, n.y, n.z) then - neighbours[#neighbours+1] = n - end - end - - return neighbours - end - - function Grid:getNodeAt(x,y,z) - if not x or not y or not z then return end - if Utils.outOfRange(x,self._min_x,self._max_x) then return end - if Utils.outOfRange(y,self._min_y,self._max_y) then return end - if Utils.outOfRange(z,self._min_z,self._max_z) then return end - - -- inefficient - if not self._nodes[y] then self._nodes[y] = {} end - if not self._nodes[y][x] then self._nodes[y][x] = {} end - if not self._nodes[y][x][z] then self._nodes[y][x][z] = Node:new(x,y,z) end - return self._nodes[y][x][z] - end - - return setmetatable(Grid,{ - __call = function(self,...) - return self:new(...) - end - }) - -end diff --git a/sys/modules/opus/jumper/pathfinder.lua b/sys/modules/opus/jumper/pathfinder.lua deleted file mode 100644 index 0ff2844..0000000 --- a/sys/modules/opus/jumper/pathfinder.lua +++ /dev/null @@ -1,104 +0,0 @@ ---[[ - The following License applies to all files within the jumper directory. - - Note that this is only a partial copy of the full jumper code base. Also, - the code was modified to support 3D maps. ---]] - ---[[ -This work is under MIT-LICENSE -Copyright (c) 2012-2013 Roland Yonaba. - --- https://opensource.org/licenses/MIT - ---]] - -local _VERSION = "" -local _RELEASEDATE = "" - -if (...) then - - -- Dependencies - local _PATH = (...):gsub('%.pathfinder$','') - local Utils = require (_PATH .. '.core.utils') - - -- Internalization - local pairs = pairs - local assert = assert - local setmetatable = setmetatable - - --- Finders (search algorithms implemented). Refers to the search algorithms actually implemented in Jumper. - --
  • [A*](http://en.wikipedia.org/wiki/A*_search_algorithm)
  • - local Finders = { - ['ASTAR'] = require (_PATH .. '.search.astar'), - } - - -- Will keep track of all nodes expanded during the search - -- to easily reset their properties for the next pathfinding call - local toClear = {} - - -- Performs a traceback from the goal node to the start node - -- Only happens when the path was found - - local Pathfinder = {} - Pathfinder.__index = Pathfinder - - function Pathfinder:new(heuristic) - local newPathfinder = {} - setmetatable(newPathfinder, Pathfinder) - self._finder = Finders.ASTAR - self._heuristic = heuristic - return newPathfinder - end - - function Pathfinder:setGrid(grid) - self._grid = grid - return self - end - - --- Calculates a `path`. Returns the `path` from start to end location - -- Both locations must exist on the collision map. The starting location can be unwalkable. - -- @treturn path a path (array of nodes) when found, otherwise nil - -- @usage local path = myFinder:getPath(1,1,5,5) - function Pathfinder:getPath(startX, startY, startZ, ih, endX, endY, endZ, oh) - self:reset() - local startNode = self._grid:getNodeAt(startX, startY, startZ) - local endNode = self._grid:getNodeAt(endX, endY, endZ) - if not startNode or not endNode then - return nil - end - - startNode.heading = ih - endNode.heading = oh - - assert(startNode, ('Invalid location [%d, %d, %d]'):format(startX, startY, startZ)) - assert(endNode and self._grid:isWalkableAt(endX, endY, endZ), - ('Invalid or unreachable location [%d, %d, %d]'):format(endX, endY, endZ)) - local _endNode = self._finder(self, startNode, endNode, toClear) - if _endNode then - return Utils.traceBackPath(self, _endNode, startNode) - end - return nil - end - - --- Resets the `pathfinder`. This function is called internally between - -- successive pathfinding calls, so you should not - -- use it explicitely, unless under specific circumstances. - -- @class function - -- @treturn pathfinder self (the calling `pathfinder` itself, can be chained) - -- @usage local path, len = myFinder:getPath(1,1,5,5) - function Pathfinder:reset() - for node in pairs(toClear) do node:reset() end - toClear = {} - return self - end - - -- Returns Pathfinder class - Pathfinder._VERSION = _VERSION - Pathfinder._RELEASEDATE = _RELEASEDATE - return setmetatable(Pathfinder,{ - __call = function(self,...) - return self:new(...) - end - }) -end diff --git a/sys/modules/opus/jumper/search/astar.lua b/sys/modules/opus/jumper/search/astar.lua deleted file mode 100644 index c92dabc..0000000 --- a/sys/modules/opus/jumper/search/astar.lua +++ /dev/null @@ -1,77 +0,0 @@ --- Astar algorithm --- This actual implementation of A-star is based on --- [Nash A. & al. pseudocode](http://aigamedev.com/open/tutorials/theta-star-any-angle-paths/) - -if (...) then - - -- Internalization - local huge = math.huge - - -- Dependancies - local _PATH = (...):match('(.+)%.search.astar$') - local Heap = require (_PATH.. '.core.bheap') - - -- Updates G-cost - local function computeCost(node, neighbour, heuristic) - local mCost, heading = heuristic(neighbour, node) -- Heuristics.EUCLIDIAN(neighbour, node) - - if node._g + mCost < neighbour._g then - neighbour._parent = node - neighbour._g = node._g + mCost - neighbour.heading = heading - end - end - - -- Updates vertex node-neighbour - local function updateVertex(openList, node, neighbour, endNode, heuristic) - local oldG = neighbour._g - computeCost(node, neighbour, heuristic) - if neighbour._g < oldG then - if neighbour._opened then neighbour._opened = false end - neighbour._h = heuristic(endNode, neighbour) - neighbour._f = neighbour._g + neighbour._h - openList:push(neighbour) - neighbour._opened = true - end - end - - -- Calculates a path. - -- Returns the path from location `` to location ``. - return function (finder, startNode, endNode, toClear) - local openList = Heap() - startNode._g = 0 - startNode._h = finder._heuristic(endNode, startNode) - startNode._f = startNode._g + startNode._h - openList:push(startNode) - toClear[startNode] = true - startNode._opened = true - - while not openList:empty() do - local node = openList:pop() - node._closed = true - if node == endNode then return node end - local neighbours = finder._grid:getNeighbours(node) - for i = 1,#neighbours do - local neighbour = neighbours[i] - if not neighbour._closed then - toClear[neighbour] = true - if not neighbour._opened then - neighbour._g = huge - neighbour._parent = nil - end - updateVertex(openList, node, neighbour, endNode, finder._heuristic) - end - end - - --[[ - printf('x:%d y:%d z:%d g:%d', node.x, node.y, node.z, node._g) - for i = 1,#neighbours do - local n = neighbours[i] - printf('x:%d y:%d z:%d f:%f g:%f h:%d', n.x, n.y, n.z, n._f, n._g, n.heading or -1) - end - --]] - - end - return nil - end -end diff --git a/sys/modules/opus/packages.lua b/sys/modules/opus/packages.lua index 0c5187a..8b0f5e1 100644 --- a/sys/modules/opus/packages.lua +++ b/sys/modules/opus/packages.lua @@ -20,6 +20,29 @@ function Packages:installed() return list end +function Packages:installedSorted() + local list = { } + + for k, v in pairs(self.installed()) do + v.name = k + v.deps = { } + table.insert(list, v) + for _, v2 in pairs(v.required or { }) do + v.deps[v2] = true + end + end + + table.sort(list, function(a, b) + return not not (b.deps and b.deps[a.name]) + end) + + table.sort(list, function(a, b) + return not (a.deps and a.deps[b.name]) + end) + + return list +end + function Packages:list() if not fs.exists('usr/config/packages') then self:downloadList() diff --git a/sys/modules/opus/pathfind.lua b/sys/modules/opus/pathfind.lua deleted file mode 100644 index 06c12f1..0000000 --- a/sys/modules/opus/pathfind.lua +++ /dev/null @@ -1,256 +0,0 @@ -local Grid = require('opus.jumper.grid') -local Pathfinder = require('opus.jumper.pathfinder') -local Point = require('opus.point') -local Util = require('opus.util') - -local turtle = _G.turtle - -local function addBlock(grid, b, dim) - if Point.inBox(b, dim) then - local node = grid:getNodeAt(b.x, b.y, b.z) - if node then - node.walkable = 1 - end - end -end - --- map shrinks/grows depending upon blocks encountered --- the map will encompass any blocks encountered, the turtle position, and the destination -local function mapDimensions(dest, blocks, boundingBox, dests) - local box = Point.makeBox(turtle.point, turtle.point) - - Point.expandBox(box, dest) - - for _,d in pairs(dests) do - Point.expandBox(box, d) - end - - for _,b in pairs(blocks) do - Point.expandBox(box, b) - end - - -- expand one block out in all directions - if boundingBox then - box.x = math.max(box.x - 1, boundingBox.x) - box.z = math.max(box.z - 1, boundingBox.z) - box.y = math.max(box.y - 1, boundingBox.y) - box.ex = math.min(box.ex + 1, boundingBox.ex) - box.ez = math.min(box.ez + 1, boundingBox.ez) - box.ey = math.min(box.ey + 1, boundingBox.ey) - else - box.x = box.x - 1 - box.z = box.z - 1 - box.y = box.y - 1 - box.ex = box.ex + 1 - box.ez = box.ez + 1 - box.ey = box.ey + 1 - end - - return box -end - -local function nodeToPoint(node) - return { x = node.x, y = node.y, z = node.z, heading = node.heading } -end - -local function heuristic(n, node) - return Point.calculateMoves(node, n) --- { x = node.x, y = node.y, z = node.z, heading = node.heading }, --- { x = n.x, y = n.y, z = n.z, heading = n.heading }) -end - -local function dimsAreEqual(d1, d2) - return d1.ex == d2.ex and - d1.ey == d2.ey and - d1.ez == d2.ez and - d1.x == d2.x and - d1.y == d2.y and - d1.z == d2.z -end - --- turtle sensor returns blocks in relation to the world - not turtle orientation --- so cannot figure out block location unless we know our orientation in the world --- really kinda dumb since it returns the coordinates as offsets of our location --- instead of true coordinates -local function addSensorBlocks(blocks, sblocks) - for _,b in pairs(sblocks) do - if b.type ~= 'AIR' then - local pt = { x = turtle.point.x, y = turtle.point.y + b.y, z = turtle.point.z } - pt.x = pt.x - b.x - pt.z = pt.z - b.z -- this will only work if we were originally facing west - local found = false - for _,ob in pairs(blocks) do - if pt.x == ob.x and pt.y == ob.y and pt.z == ob.z then - found = true - break - end - end - if not found then - table.insert(blocks, pt) - end - end - end -end - -local function selectDestination(pts, box, grid) - while #pts > 0 do - local pt = Point.closest(turtle.point, pts) - if box and not Point.inBox(pt, box) then - Util.removeByValue(pts, pt) - else - if grid:isWalkableAt(pt.x, pt.y, pt.z) then - return pt - end - Util.removeByValue(pts, pt) - end - end -end - -local function updateCanvas(path) - local t = { } - for node in path:nodes() do - table.insert(t, { x = node.x, y = node.y, z = node.z }) - end - os.queueEvent('canvas', { - type = 'canvas_path', - data = t, - }) -end - -local function pathTo(dest, options) - local blocks = options.blocks or turtle.getState().blocks or { } - local dests = options.dest or { dest } -- support alternative destinations - local box = options.box or turtle.getState().box - local lastDim - local grid - - if box then - box = Point.normalizeBox(box) - end - - -- Creates a pathfinder object - local finder = Pathfinder(heuristic) - - while turtle.point.x ~= dest.x or turtle.point.z ~= dest.z or turtle.point.y ~= dest.y do - - -- map expands as we encounter obstacles - local dim = mapDimensions(dest, blocks, box, dests) - - -- reuse map if possible - if not lastDim or not dimsAreEqual(dim, lastDim) then - -- Creates a grid object - grid = Grid(dim) - finder:setGrid(grid) - - lastDim = dim - end - for _,b in pairs(blocks) do - addBlock(grid, b, dim) - end - - dest = selectDestination(dests, box, grid) - if not dest then - return false, 'failed to reach destination' - end - if turtle.point.x == dest.x and turtle.point.z == dest.z and turtle.point.y == dest.y then - break - end - - -- Define start and goal locations coordinates - local startPt = turtle.point - - -- Calculates the path, and its length - local path = finder:getPath( - startPt.x, startPt.y, startPt.z, turtle.point.heading, - dest.x, dest.y, dest.z, dest.heading) - - if not path then - Util.removeByValue(dests, dest) - else - updateCanvas(path) - - path:filter() - - for node in path:nodes() do - local pt = nodeToPoint(node) - - if turtle.isAborted() then - return false, 'aborted' - end - ---if this is the next to last node ---and we are traveling up or down, then the ---heading for this node should be the heading of the last node ---or, maybe.. ---if last node is up or down (or either?) - - -- use single turn method so the turtle doesn't turn around - -- when encountering obstacles - --if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then - pt.heading = nil - if not turtle.go(pt) then - local bpt = Point.nearestTo(turtle.point, pt) - - if turtle.getFuelLevel() == 0 then - return false, 'Out of fuel' - end - table.insert(blocks, bpt) - os.queueEvent('canvas', { - type = 'canvas_barrier', - data = { bpt }, - }) - -- really need to check if the block we ran into was a turtle. - -- if so, this block should be temporary (1-2 secs) - - --local side = turtle.getSide(turtle.point, pt) - --if turtle.isTurtleAtSide(side) then - -- pt.timestamp = os.clock() + ? - --end - -- if dim has not changed, then need to update grid with - -- walkable = nil (after time has elapsed) - - --if device.turtlesensorenvironment then - -- addSensorBlocks(blocks, device.turtlesensorenvironment.sonicScan()) - --end - break - end - end - end - end - - if dest.heading then - turtle.setHeading(dest.heading) - end - return dest -end - -return { - pathfind = function(dest, options) - options = options or { } - --if not options.blocks and turtle.gotoPoint(dest) then - -- return dest - --end - return pathTo(dest, options) - end, - - -- set a global bounding box - -- box can be overridden by passing box in pathfind options - setBox = function(box) - turtle.getState().box = box - end, - - setBlocks = function(blocks) - turtle.getState().blocks = blocks - end, - - addBlock = function(block) - if turtle.getState().blocks then - table.insert(turtle.getState().blocks, block) - end - end, - - reset = function() - turtle.getState().box = nil - turtle.getState().blocks = nil - end, -} -- 2.49.1 From aaab059cee6647668aedee9dd690306fc0ae30c5 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 1 Nov 2019 16:35:58 -0600 Subject: [PATCH 083/236] prepare for lock screen --- sys/init/7.multishell.lua | 3 ++- sys/kernel.lua | 4 ++++ sys/modules/opus/ui/tween.lua | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index b6ada5d..d8b722a 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -264,7 +264,7 @@ kernel.hook('multishell_redraw', function() write(currentTab.sx - 1, ' ' .. currentTab.title:sub(1, currentTab.width - 1) .. ' ', _colors.focusBackgroundColor, _colors.focusTextColor) - if not currentTab.isOverview then + if not currentTab.noTerminate then write(w, closeInd, _colors.backgroundColor, _colors.focusTextColor) end end @@ -340,6 +340,7 @@ kernel.hook('kernel_ready', function() overviewId = multishell.openTab({ path = config.launcher or 'sys/apps/Overview.lua', isOverview = true, + noTerminate = true, focused = true, title = '+', env = env, diff --git a/sys/kernel.lua b/sys/kernel.lua index 62ab6bc..a817d23 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -179,6 +179,10 @@ function kernel.run(args) end function kernel.raise(uid) + if kernel.getFocused() and kernel.getFocused().pinned then + return false + end + local routine = Util.find(kernel.routines, 'uid', uid) if routine then diff --git a/sys/modules/opus/ui/tween.lua b/sys/modules/opus/ui/tween.lua index 7d0a13c..22601de 100644 --- a/sys/modules/opus/ui/tween.lua +++ b/sys/modules/opus/ui/tween.lua @@ -7,7 +7,7 @@ local tween = { Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga - Licence details: https://opensource.org/licenses/MIT + license details: https://opensource.org/licenses/MIT ]] } -- 2.49.1 From 5c35a8383e06719f7b3559df962bce5b699c8cac Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 1 Nov 2019 22:37:08 -0600 Subject: [PATCH 084/236] lock computer --- startup.lua | 31 +++++++++++++++++-------------- sys/init/2.vfs.lua | 10 +++++----- sys/lzwfs.lua | 9 ++++++--- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/startup.lua b/startup.lua index 0cfcd7f..b6381d1 100644 --- a/startup.lua +++ b/startup.lua @@ -13,10 +13,13 @@ description: array of menu entries (see .startup.boot for examples) ]] -local colors = _G.colors -local os = _G.os -local settings = _G.settings -local term = _G.term +local colors = _G.colors +local fs = _G.fs +local keys = _G.keys +local os = _G.os +local settings = _G.settings +local term = _G.term +local textutils = _G.textutils local function loadBootOptions() if not fs.exists('.startup.boot') then @@ -87,19 +90,19 @@ local function startupMenu() if key == keys.enter or key == keys.right then return selected - elseif key == keys.down then - if selected == #bootOptions.menu then - selected = 0 + elseif key == keys.down then + if selected == #bootOptions.menu then + selected = 0 end selected = selected + 1 - elseif key == keys.up then - if selected == 1 then - selected = #bootOptions.menu + 1 + elseif key == keys.up then + if selected == 1 then + selected = #bootOptions.menu + 1 end selected = selected - 1 elseif event == 'char' then key = tonumber(key) or 0 - if bootOptions.menu[key] then + if bootOptions.menu[key] then return key end end @@ -145,13 +148,13 @@ local function splash() term.write(str) end -term.clear() -splash() - for _, v in pairs(bootOptions.preload) do os.run(_ENV, v) end +term.clear() +splash() + local timerId = os.startTimer(bootOptions.delay) while true do local e, id = os.pullEvent() diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 0ddc672..62f5b4d 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -1,3 +1,5 @@ +local fs = _G.fs + if fs.native then return end @@ -7,8 +9,6 @@ local Util = require('opus.util') -- TODO: support getDrive for virtual nodes -local fs = _G.fs - fs.native = Util.shallowCopy(fs) local fstypes = { } @@ -16,7 +16,7 @@ local nativefs = { } for k,fn in pairs(fs) do if type(fn) == 'function' then - nativefs[k] = function(node, ...) + nativefs[k] = function(_, ...) return fn(...) end end @@ -340,8 +340,8 @@ function fs.unmount(path) end end -function fs.registerType(name, fs) - fstypes[name] = fs +function fs.registerType(name, vfs) + fstypes[name] = vfs end function fs.getTypes() diff --git a/sys/lzwfs.lua b/sys/lzwfs.lua index ac2ddb8..c1e9088 100644 --- a/sys/lzwfs.lua +++ b/sys/lzwfs.lua @@ -11,7 +11,7 @@ -- 1 is reserved for compression exclusion -- fs.addException('startup.lua', 1) --- To renable compression for a file +-- To renable compression for a file -- fs.removeException('startup.lua', 1) -- Restores file system @@ -23,6 +23,9 @@ local sub = string.sub local tconcat = table.concat local tinsert = table.insert +local bit = _G.bit +local fs = _G.fs + local SIGC = 'LZWC' local IGNORE_COMPRESSION = 1 -- support other bits as well @@ -157,7 +160,7 @@ local function decompress(input) return tconcat(result) end -function split(str, pattern) +local function split(str, pattern) pattern = pattern or "(.-)\n" local t = {} local function helper(line) tinsert(t, line) return "" end @@ -219,7 +222,7 @@ function fs.open(fname, flags) end, flush = function() -- this isn't gonna work... - // f.write(compress(tconcat(c))) + -- f.write(compress(tconcat(c))) f.flush(); end, close = function() -- 2.49.1 From fba2d48a4b9d5134e794ec04a1de2d45529b4a28 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 2 Nov 2019 12:23:40 -0600 Subject: [PATCH 085/236] minor fixes for user errors --- sys/modules/opus/fs/linkfs.lua | 3 +++ sys/modules/opus/ui/canvas.lua | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/modules/opus/fs/linkfs.lua b/sys/modules/opus/fs/linkfs.lua index 38cceac..79ec13d 100644 --- a/sys/modules/opus/fs/linkfs.lua +++ b/sys/modules/opus/fs/linkfs.lua @@ -19,6 +19,9 @@ function linkfs.mount(_, source) error('Source is required') end source = fs.combine(source, '') + if not fs.exists(source) then + error('Source is missing') + end if fs.isDir(source) then return { source = source, diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index 260ad89..75e1792 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -152,7 +152,7 @@ function Canvas:write(x, y, text, bg, fg) bg = _rep(self.palette[bg], #text) end if fg then - fg = _rep(self.palette[fg], #text) + fg = _rep(self.palette[fg] or self.palette[1], #text) end self:blit(x, y, text, bg, fg) end -- 2.49.1 From f2cf20c27426988809a2ef8dc406ccaf4c7db1a7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 5 Nov 2019 11:18:21 -0700 Subject: [PATCH 086/236] cleanup --- sys/lzwfs.lua | 250 -------------------------------------------------- 1 file changed, 250 deletions(-) delete mode 100644 sys/lzwfs.lua diff --git a/sys/lzwfs.lua b/sys/lzwfs.lua deleted file mode 100644 index c1e9088..0000000 --- a/sys/lzwfs.lua +++ /dev/null @@ -1,250 +0,0 @@ --- see: https://github.com/Rochet2/lualzw --- MIT License - Copyright (c) 2016 Rochet2 - --- Transparent file system compression for non-binary files using lzw - --- Files that are compressed will have the first bytes in file set to 'LZWC'. --- If a file does not benefit from compression, the contents will not be altered. - --- Allow exclusions for files that shouldn't be compressed --- Also allow for future types of exclusions using bit operations --- 1 is reserved for compression exclusion --- fs.addException('startup.lua', 1) - --- To renable compression for a file --- fs.removeException('startup.lua', 1) - --- Restores file system --- fs.restore() - -local char = string.char -local type = type -local sub = string.sub -local tconcat = table.concat -local tinsert = table.insert - -local bit = _G.bit -local fs = _G.fs - -local SIGC = 'LZWC' -local IGNORE_COMPRESSION = 1 -- support other bits as well - -local basedictcompress = {} -local basedictdecompress = {} -for i = 0, 255 do - local ic, iic = char(i), char(i, 0) - basedictcompress[ic] = iic - basedictdecompress[iic] = ic -end - -local native = { open = fs.open } -fs.exceptions = fs.exceptions or { } - -local function dictAddA(str, dict, a, b) - if a >= 256 then - a, b = 0, b+1 - if b >= 256 then - dict = {} - b = 1 - end - end - dict[str] = char(a,b) - a = a+1 - return dict, a, b -end - -local function compress(input) - if type(input) ~= "string" then - error ("string expected, got "..type(input)) - end - local len = #input - if len <= 1 then - return input - end - - local dict = {} - local a, b = 0, 1 - - local result = { SIGC } - local resultlen = 1 - local n = 2 - local word = "" - for i = 1, len do - local c = sub(input, i, i) - local wc = word..c - if not (basedictcompress[wc] or dict[wc]) then - local write = basedictcompress[word] or dict[word] - if not write then - error "algorithm error, could not fetch word" - end - result[n] = write - resultlen = resultlen + #write - n = n+1 - if len <= resultlen then - return input - end - dict, a, b = dictAddA(wc, dict, a, b) - word = c - else - word = wc - end - end - result[n] = basedictcompress[word] or dict[word] - resultlen = resultlen+#result[n] - if len <= resultlen then - return input - end - return tconcat(result) -end - -local function dictAddB(str, dict, a, b) - if a >= 256 then - a, b = 0, b+1 - if b >= 256 then - dict = {} - b = 1 - end - end - dict[char(a,b)] = str - a = a+1 - return dict, a, b -end - -local function decompress(input) - if type(input) ~= "string" then - error( "string expected, got "..type(input)) - end - - if #input <= 1 then - return input - end - - local control = sub(input, 1, 4) - if control ~= SIGC then - return input - end - input = sub(input, 5) - local len = #input - - if len < 2 then - error("invalid input - not a compressed string") - end - - local dict = {} - local a, b = 0, 1 - - local result = {} - local n = 1 - local last = sub(input, 1, 2) - result[n] = basedictdecompress[last] or dict[last] - n = n+1 - for i = 3, len, 2 do - local code = sub(input, i, i+1) - local lastStr = basedictdecompress[last] or dict[last] - if not lastStr then - error( "could not find last from dict. Invalid input?") - end - local toAdd = basedictdecompress[code] or dict[code] - if toAdd then - result[n] = toAdd - n = n+1 - dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b) - else - local tmp = lastStr..sub(lastStr, 1, 1) - result[n] = tmp - n = n+1 - dict, a, b = dictAddB(tmp, dict, a, b) - end - last = code - end - return tconcat(result) -end - -local function split(str, pattern) - pattern = pattern or "(.-)\n" - local t = {} - local function helper(line) tinsert(t, line) return "" end - helper((str:gsub(pattern, helper))) - return t -end - -function fs.open(fname, flags) - if flags == 'r' then - local f, err = native.open(fname, 'rb') - if not f then - return f, err - end - - local ctr = 0 - local lines - return { - readLine = function() - if not lines then - lines = split(decompress(f.readAll())) - end - ctr = ctr + 1 - return lines[ctr] - end, - readAll = function() - return decompress(f.readAll()) - end, - close = function() - f.close() - end, - } - elseif flags == 'w' or flags == 'a' then - if bit.band(fs.exceptions[fs.combine(fname, '')] or 0, IGNORE_COMPRESSION) == IGNORE_COMPRESSION then - return native.open(fname, flags) - end - - local c = { } - - if flags == 'a' then - local f = fs.open(fname, 'r') - if f then - tinsert(c, f.readAll()) - f.close() - end - end - - local f, err = native.open(fname, 'wb') - if not f then - return f, err - end - - return { - write = function(str) - tinsert(c, str) - end, - writeLine = function(str) - tinsert(c, str) - tinsert(c, '\n') - end, - flush = function() - -- this isn't gonna work... - -- f.write(compress(tconcat(c))) - f.flush(); - end, - close = function() - f.write(compress(tconcat(c))) - f.close() - end, - } - end - - return native.open(fname, flags) -end - -function fs.addException(fname, mode) - fname = fs.combine(fname, '') - fs.exceptions[fname] = bit.bor(fs.exceptions[fname] or 0, mode) -end - -function fs.removeException(fname, mode) - fname = fs.combine(fname, '') - fs.exceptions[fname] = bit.bxor(fs.exceptions[fname] or 0, mode) -end - -function fs.restore() - fs.open = native.open -end -- 2.49.1 From e9717c4def8fbede084fee4a95994104cb450acd Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 7 Nov 2019 18:00:54 -0700 Subject: [PATCH 087/236] Events shows all + tweaks --- sys/apps/Files.lua | 1 + sys/kernel.lua | 21 ++++++++++++++++----- sys/modules/opus/array.lua | 9 +++++++++ sys/modules/opus/fs/ramfs.lua | 4 ++++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 9f17255..f254a97 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -20,6 +20,7 @@ local config = Config.load('Files', { }) config.associations = config.associations or { nft = 'pain', + txt = 'edit', } local copied = { } diff --git a/sys/kernel.lua b/sys/kernel.lua index a817d23..b2ef8a8 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -1,5 +1,6 @@ _G.requireInjector(_ENV) +local Array = require('opus.array') local Terminal = require('opus.terminal') local Util = require('opus.util') @@ -55,7 +56,7 @@ end function kernel.unhook(event, fn) local eventHooks = kernel.hooks[event] if eventHooks then - Util.removeByValue(eventHooks, fn) + Array.removeByValue(eventHooks, fn) if #eventHooks == 0 then kernel.hooks[event] = nil end @@ -107,7 +108,7 @@ function Routine:resume(event, ...) error(result, -1) end if coroutine.status(self.co) == 'dead' then - Util.removeByValue(kernel.routines, self) + Array.removeByValue(kernel.routines, self) if #kernel.routines > 0 then switch(kernel.routines[1]) end @@ -188,7 +189,7 @@ function kernel.raise(uid) if routine then local previous = kernel.routines[1] if routine ~= previous then - Util.removeByValue(kernel.routines, routine) + Array.removeByValue(kernel.routines, routine) table.insert(kernel.routines, 1, routine) end @@ -211,7 +212,7 @@ function kernel.lower(uid) end end - Util.removeByValue(kernel.routines, routine) + Array.removeByValue(kernel.routines, routine) table.insert(kernel.routines, routine) return true end @@ -229,7 +230,17 @@ end function kernel.event(event, eventData) local stopPropagation - local eventHooks = kernel.hooks[event] + local eventHooks = kernel.hooks['*'] + if eventHooks then + for i = #eventHooks, 1, -1 do + stopPropagation = eventHooks[i](event, eventData) + if stopPropagation then + break + end + end + end + + eventHooks = kernel.hooks[event] if eventHooks then for i = #eventHooks, 1, -1 do stopPropagation = eventHooks[i](event, eventData) diff --git a/sys/modules/opus/array.lua b/sys/modules/opus/array.lua index b6ba255..f0aa73f 100644 --- a/sys/modules/opus/array.lua +++ b/sys/modules/opus/array.lua @@ -10,4 +10,13 @@ function Array.filter(it, f) return ot end +function Array.removeByValue(t, e) + for k,v in pairs(t) do + if v == e then + table.remove(t, k) + break + end + end +end + return Array diff --git a/sys/modules/opus/fs/ramfs.lua b/sys/modules/opus/fs/ramfs.lua index 0d943a8..1ed2f7a 100644 --- a/sys/modules/opus/fs/ramfs.lua +++ b/sys/modules/opus/fs/ramfs.lua @@ -48,6 +48,10 @@ function ramfs.getDrive() return 'ram' end +function ramfs.getFreeSpace() + return math.huge +end + function ramfs.list(node, dir) if node.nodes and node.mountPoint == dir then local files = { } -- 2.49.1 From 6d8d62d309accc6a0af4c5b4f33b6c1d011a9d9c Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 7 Nov 2019 22:50:11 -0700 Subject: [PATCH 088/236] tweaks --- sys/apps/system/cloud.lua | 2 +- sys/apps/system/diskusage.lua | 1 + sys/init/2.vfs.lua | 5 ++++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index e40d924..221c828 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -11,7 +11,7 @@ if _G.http.websocket then local tab = UI.Tab { tabTitle = 'Cloud', - description = 'Cloud catcher options', + description = 'Cloud Catcher options', key = UI.TextEntry { x = 3, ex = -3, y = 2, limit = 32, diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index fb958f5..98b8516 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -10,6 +10,7 @@ local peripheral = _G.peripheral local NftImages = { blank = '\0308\0317\153\153\153\153\153\153\153\153\010\0307\0318\153\153\153\153\153\153\153\153\010\0308\0317\153\153\153\153\153\153\153\153\010\0307\0318\153\153\153\153\153\153\153\153\010\0308\0317\153\153\153\153\153\153\153\153', drive = '\030 \031 \030b\031b\128\0308\0318\128\128\030f\149\030b\149\031 \139\010\030 \031 \030b\031b\128\128\128\128\128\128\010\030 \031 \030b\031b\128\0300\0317____\030b\031b\128\010\030 \031 \030b\031b\128\0300\0317____\030b\031b\128', + ram = '\030 \031 \128\0318\144\144\144\144\144\031 \128\010\0308\031 \157\0307\0317\128\128\128\128\128\030 \0318\145\010\030 \0318\136\0307\0317\128\0307\0310RAM\0307\128\030 \0318\132\010\0308\031 \157\0307\0317\128\128\128\128\128\030 \0318\145\010\030 \031 \128\0318\129\129\129\129\129\031 \128', rom = '\030 \031 \128\0318\144\144\144\144\144\031 \128\010\0308\031 \157\0307\0317\128\128\128\128\128\030 \0318\145\010\030 \0318\136\0307\0317\128\0307\0310ROM\0307\128\030 \0318\132\010\0308\031 \157\0307\0317\128\128\128\128\128\030 \0318\145\010\030 \031 \128\0318\129\129\129\129\129\031 \128', hdd = '\030 \031 \0307\0317\128\0300\135\131\139\0307\128\010\030 \031 \0300\0317\149\0310\128\0307\131\0300\128\0307\149\010\030 \031 \0307\0310\130\0300\0317\144\0308\0310\133\0307\159\129\010\030 \031 \0308\0317\149\129\142\159\0307\128\010\030 \031 \030 \0317\143\143\143\143\143', } diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 62f5b4d..5b3c5f9 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -220,6 +220,8 @@ function fs.find(spec) -- not optimized -- local files = node.fs.find(node, spec) local files = { } -- method from https://github.com/N70/deltaOS/blob/dev/vfs + + -- REVISIT - see globbing in shellex package local function recurse_spec(results, path, spec) local segment = spec:match('([^/]*)'):gsub('/', '') local pattern = '^' .. segment:gsub("[%.%[%]%(%)%%%+%-%?%^%$]","%%%1"):gsub("%z","%%z"):gsub("%*","[^/]-") .. '$' @@ -303,7 +305,8 @@ function fs.loadTab(path) local mounts = Util.readFile(path) if mounts then for _,l in ipairs(Util.split(mounts)) do - if l:sub(1, 1) ~= '#' then + l = Util.trim(l) + if #l > 0 and l:sub(1, 1) ~= '#' then local s, m = pcall(function() fs.mount(table.unpack(Util.matches(l))) end) -- 2.49.1 From 57ea46dde7e9161f7d8acc70d1c7e8f4fd23ef10 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 8 Nov 2019 19:54:41 -0700 Subject: [PATCH 089/236] alternatives --- sys/apps/Files.lua | 3 +- sys/apps/Overview.lua | 7 +++-- sys/apps/network/telnet.lua | 3 +- sys/modules/opus/alternate.lua | 52 ++++++++++++++++++++++++++++++++++ sys/modules/opus/util.lua | 6 ++-- 5 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 sys/modules/opus/alternate.lua diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index f254a97..b39ddd9 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -1,3 +1,4 @@ +local Alt = require('opus.alternate') local Config = require('opus.config') local Event = require('opus.event') local pastebin = require('opus.http.pastebin') @@ -353,7 +354,7 @@ function Browser:eventHandler(event) self:setStatus('Started cloud edit') elseif event.type == 'shell' then - self:run('sys/apps/shell.lua') + self:run(Alt.get('shell')) elseif event.type == 'refresh' then self:updateDirectory(self.dir) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 8cd2c92..92c09bf 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -1,3 +1,4 @@ +local Alt = require('opus.alternate') local class = require('opus.class') local Config = require('opus.config') local Event = require('opus.event') @@ -420,13 +421,13 @@ function page:eventHandler(event) shell.switchTab(shell.openTab(event.button.app.run)) elseif event.type == 'shell' then - shell.switchTab(shell.openTab('sys/apps/shell.lua')) + shell.switchTab(shell.openTab(Alt.get('shell'))) elseif event.type == 'lua' then - shell.switchTab(shell.openTab('sys/apps/Lua.lua')) + shell.switchTab(shell.openTab(Alt.get('lua'))) elseif event.type == 'files' then - shell.switchTab(shell.openTab('sys/apps/Files.lua')) + shell.switchTab(shell.openTab(Alt.get('files'))) elseif event.type == 'focus_change' then if event.focused.parent.UIElement == 'Icon' then diff --git a/sys/apps/network/telnet.lua b/sys/apps/network/telnet.lua index 8bd11de..9aec83c 100644 --- a/sys/apps/network/telnet.lua +++ b/sys/apps/network/telnet.lua @@ -1,3 +1,4 @@ +local Alt = require('opus.alternate') local Event = require('opus.event') local Socket = require('opus.socket') local Util = require('opus.util') @@ -46,7 +47,7 @@ local function telnetHost(socket, mode) title = mode .. ' client', hidden = true, co = coroutine.create(function() - Util.run(_ENV, 'sys/apps/shell.lua', table.unpack(termInfo.program)) + Util.run(_ENV, Alt.get('shell'), table.unpack(termInfo.program)) if socket.queue then socket:write(socket.queue) end diff --git a/sys/modules/opus/alternate.lua b/sys/modules/opus/alternate.lua new file mode 100644 index 0000000..532fc20 --- /dev/null +++ b/sys/modules/opus/alternate.lua @@ -0,0 +1,52 @@ +local Config = require('opus.config') +local Util = require('opus.util') + +local function getConfig() + return Config.load('alternate', { + default = { + shell = 'sys/apps/shell.lua', + lua = 'sys/apps/Lua.lua', + files = 'sys/apps/Files.lua', + }, + choices = { + shell = { + 'sys/apps/shell.lua', + 'rom/programs/shell', + }, + lua = { + 'sys/apps/Lua.lua', + 'rom/programs/lua.lua', + }, + files = { + 'sys/apps/Files.lua', + } + } + }) +end + +local Alt = { } + +function Alt.get(key) + return getConfig().default[key] +end + +function Alt.set(key, value) + local config = getConfig() + + config.default[key] = value + Config.update('alternate', config) +end + +function Alt.addChoice(key, value) + local config = getConfig() + + if not config.choices[key] then + config.choices[key] = { } + end + if not Util.contains(config.choices[key], value) then + config.choices[key] = value + Config.update('alternate', config) + end +end + +return Alt diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 4604199..564b9f8 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -621,17 +621,17 @@ end -- http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76 function Util.trim(s) - return s:find'^%s*$' and '' or s:match'^%s*(.*%S)' + return s:find('^%s*$') and '' or s:match('^%s*(.*%S)') end -- trim whitespace from left end of string function Util.triml(s) - return s:match'^%s*(.*)' + return s:match('^%s*(.*)') end -- trim whitespace from right end of string function Util.trimr(s) - return s:find'^%s*$' and '' or s:match'^(.*%S)' + return s:find('^%s*$') and '' or s:match('^(.*%S)') end -- end http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76 -- 2.49.1 From 9ef4d9ef64d17ddaf774c32cd078d442824b3b7c Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 9 Nov 2019 21:15:07 -0700 Subject: [PATCH 090/236] package manager install/uninstall directives --- sys/apps/package.lua | 26 ++++++++++++++++++++++++++ sys/modules/opus/alternate.lua | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/sys/apps/package.lua b/sys/apps/package.lua index 3d95c17..d1d963e 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -9,6 +9,12 @@ local term = _G.term local args = { ... } local action = table.remove(args, 1) +local function makeSandbox() + local sandbox = setmetatable(Util.shallowCopy(_ENV), { __index = _G }) + _G.requireInjector(sandbox) + return sandbox +end + local function Syntax(msg) _G.printError(msg) print('\nSyntax: Package list | install [name] ... | update [name] | uninstall [name]') @@ -71,6 +77,17 @@ local function install(name, isUpdate, ignoreDeps) end showProgress() end) + + if not isUpdate then + if manifest.install then + local s, m = pcall(function() + load(manifest.install, 'install', nil, makeSandbox()) + end) + if not s and m then + _G.printError(m) + end + end + end end if action == 'list' then @@ -121,6 +138,15 @@ if action == 'uninstall' then if not Packages:isInstalled(name) then error('Package is not installed') end + local manifest = Packages:getManifest(name) + if manifest.uninstall then + local s, m = pcall(function() + load(manifest.uninstall, 'uninstall', nil, makeSandbox()) + end) + if not s and m then + _G.printError(m) + end + end local packageDir = fs.combine('packages', name) fs.delete(packageDir) print('removed: ' .. packageDir) diff --git a/sys/modules/opus/alternate.lua b/sys/modules/opus/alternate.lua index 532fc20..33e668b 100644 --- a/sys/modules/opus/alternate.lua +++ b/sys/modules/opus/alternate.lua @@ -44,7 +44,7 @@ function Alt.addChoice(key, value) config.choices[key] = { } end if not Util.contains(config.choices[key], value) then - config.choices[key] = value + table.insert(config.choices[key], value) Config.update('alternate', config) end end -- 2.49.1 From 340e37da820c336a9753e83db5c32d9a573b61c4 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 9 Nov 2019 22:01:48 -0700 Subject: [PATCH 091/236] package manager wip --- sys/apps/package.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/apps/package.lua b/sys/apps/package.lua index d1d963e..61c07af 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -81,7 +81,7 @@ local function install(name, isUpdate, ignoreDeps) if not isUpdate then if manifest.install then local s, m = pcall(function() - load(manifest.install, 'install', nil, makeSandbox()) + load(manifest.install, 'install', nil, makeSandbox())() end) if not s and m then _G.printError(m) @@ -141,7 +141,7 @@ if action == 'uninstall' then local manifest = Packages:getManifest(name) if manifest.uninstall then local s, m = pcall(function() - load(manifest.uninstall, 'uninstall', nil, makeSandbox()) + load(manifest.uninstall, 'uninstall', nil, makeSandbox())() end) if not s and m then _G.printError(m) -- 2.49.1 From 4a6af34d7ca766649eeb5512cdb4d240f53f0b88 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 9 Nov 2019 22:26:11 -0700 Subject: [PATCH 092/236] alternative updates --- sys/modules/opus/alternate.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sys/modules/opus/alternate.lua b/sys/modules/opus/alternate.lua index 33e668b..b7e4024 100644 --- a/sys/modules/opus/alternate.lua +++ b/sys/modules/opus/alternate.lua @@ -1,3 +1,4 @@ +local Array = require('opus.array') local Config = require('opus.config') local Util = require('opus.util') @@ -33,10 +34,22 @@ end function Alt.set(key, value) local config = getConfig() + Alt.addChoice(key, value) + config.default[key] = value Config.update('alternate', config) end +function Alt.remove(key, value) + local config = getConfig() + + Array.removeByValue(config.choices[key], value) + if config.default[key] == value then + config.default[key] = config.choices[key][1] + end + Config.update('alternate', config) +end + function Alt.addChoice(key, value) local config = getConfig() -- 2.49.1 From 8a5d30a441c95119e428cba8fa94660ec7f0c9b4 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 9 Nov 2019 22:33:16 -0700 Subject: [PATCH 093/236] oops --- sys/modules/opus/alternate.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sys/modules/opus/alternate.lua b/sys/modules/opus/alternate.lua index b7e4024..595d210 100644 --- a/sys/modules/opus/alternate.lua +++ b/sys/modules/opus/alternate.lua @@ -32,10 +32,9 @@ function Alt.get(key) end function Alt.set(key, value) - local config = getConfig() - Alt.addChoice(key, value) + local config = getConfig() config.default[key] = value Config.update('alternate', config) end -- 2.49.1 From c9fd7efc2694bd116a8264308f5115718605e31a Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 9 Nov 2019 22:56:36 -0700 Subject: [PATCH 094/236] alternative optimize --- sys/modules/opus/alternate.lua | 70 +++++++++++++--------------------- 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/sys/modules/opus/alternate.lua b/sys/modules/opus/alternate.lua index 595d210..6f0d24f 100644 --- a/sys/modules/opus/alternate.lua +++ b/sys/modules/opus/alternate.lua @@ -3,62 +3,46 @@ local Config = require('opus.config') local Util = require('opus.util') local function getConfig() - return Config.load('alternate', { - default = { - shell = 'sys/apps/shell.lua', - lua = 'sys/apps/Lua.lua', - files = 'sys/apps/Files.lua', - }, - choices = { - shell = { - 'sys/apps/shell.lua', - 'rom/programs/shell', - }, - lua = { - 'sys/apps/Lua.lua', - 'rom/programs/lua.lua', - }, - files = { - 'sys/apps/Files.lua', - } - } - }) + return Config.load('alternate', { + shell = { + 'sys/apps/shell.lua', + 'rom/programs/shell', + }, + lua = { + 'sys/apps/Lua.lua', + 'rom/programs/lua.lua', + }, + files = { + 'sys/apps/Files.lua', + } + }) end local Alt = { } function Alt.get(key) - return getConfig().default[key] + return getConfig()[key][1] end function Alt.set(key, value) - Alt.addChoice(key, value) - - local config = getConfig() - config.default[key] = value - Config.update('alternate', config) + local config = getConfig() + Array.removeByValue(config[key], value) + table.insert(config[key], 1, value) + Config.update('alternate', config) end function Alt.remove(key, value) - local config = getConfig() - - Array.removeByValue(config.choices[key], value) - if config.default[key] == value then - config.default[key] = config.choices[key][1] - end - Config.update('alternate', config) + local config = getConfig() + Array.removeByValue(config[key], value) + Config.update('alternate', config) end -function Alt.addChoice(key, value) - local config = getConfig() - - if not config.choices[key] then - config.choices[key] = { } - end - if not Util.contains(config.choices[key], value) then - table.insert(config.choices[key], value) - Config.update('alternate', config) - end +function Alt.add(key, value) + local config = getConfig() + if not Util.contains(config[key], value) then + table.insert(config[key], value) + Config.update('alternate', config) + end end return Alt -- 2.49.1 From 674c6af50902f2b324e1c4509a0e91cd72bb1846 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 10 Nov 2019 15:58:36 -0700 Subject: [PATCH 095/236] alternate support --- sys/apps/ShellLauncher.lua | 5 ++- sys/apps/package.lua | 35 ++++++++------- sys/apps/system/alternate.lua | 80 ++++++++++++++++++++++++++++++++++ sys/autorun/hotkeys.lua | 2 +- sys/autorun/log.lua | 1 + sys/modules/opus/alternate.lua | 2 +- 6 files changed, 106 insertions(+), 19 deletions(-) create mode 100644 sys/apps/system/alternate.lua diff --git a/sys/apps/ShellLauncher.lua b/sys/apps/ShellLauncher.lua index d905653..f2eebb7 100644 --- a/sys/apps/ShellLauncher.lua +++ b/sys/apps/ShellLauncher.lua @@ -1,8 +1,11 @@ +local Alt = require('opus.alternate') + local kernel = _G.kernel local os = _G.os local shell = _ENV.shell local launcherTab = kernel.getCurrent() +launcherTab.noFocus = true kernel.hook('kernel_focus', function(_, eventData) local focusTab = eventData and eventData[1] @@ -17,7 +20,7 @@ kernel.hook('kernel_focus', function(_, eventData) end end if nextTab == launcherTab then - shell.switchTab(shell.openTab('sys/apps/shell.lua')) + shell.switchTab(shell.openTab(Alt.get('shell'))) else shell.switchTab(nextTab.uid) end diff --git a/sys/apps/package.lua b/sys/apps/package.lua index 61c07af..b5fdba3 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -41,6 +41,21 @@ local function progress(max) end end +local function runScript(script) + if script then + local s, m = pcall(function() + local fn, m = load(script, 'script', nil, makeSandbox()) + if not fn then + error(m) + end + fn() + end) + if not s and m then + _G.printError(m) + end + end +end + local function install(name, isUpdate, ignoreDeps) local manifest = Packages:downloadManifest(name) or error('Invalid package') @@ -79,14 +94,7 @@ local function install(name, isUpdate, ignoreDeps) end) if not isUpdate then - if manifest.install then - local s, m = pcall(function() - load(manifest.install, 'install', nil, makeSandbox())() - end) - if not s and m then - _G.printError(m) - end - end + runScript(manifest.install) end end @@ -138,15 +146,10 @@ if action == 'uninstall' then if not Packages:isInstalled(name) then error('Package is not installed') end + local manifest = Packages:getManifest(name) - if manifest.uninstall then - local s, m = pcall(function() - load(manifest.uninstall, 'uninstall', nil, makeSandbox())() - end) - if not s and m then - _G.printError(m) - end - end + runScript(manifest.uninstall) + local packageDir = fs.combine('packages', name) fs.delete(packageDir) print('removed: ' .. packageDir) diff --git a/sys/apps/system/alternate.lua b/sys/apps/system/alternate.lua new file mode 100644 index 0000000..6894428 --- /dev/null +++ b/sys/apps/system/alternate.lua @@ -0,0 +1,80 @@ +local Array = require('opus.array') +local Config = require('opus.config') +local UI = require('opus.ui') + +local colors = _G.colors + +local tab = UI.Tab { + tabTitle = 'Preferred', + description = 'Select preferred applications', + apps = UI.ScrollingGrid { + x = 2, y = 2, + ex = 12, ey = -3, + columns = { + { key = 'name' }, + }, + sortColumn = 'name', + disableHeader = true, + }, + choices = UI.Grid { + x = 14, y = 2, + ex = -2, ey = -3, + disableHeader = true, + columns = { + { key = 'file' }, + } + }, + statusBar = UI.StatusBar { + values = 'Double-click to set as preferred' + }, +} + +function tab.choices:getRowTextColor(row) + if row == self.values[1] then + return colors.yellow + end + return UI.Grid.getRowTextColor(self, row) +end + +function tab:updateChoices() + local app = self.apps:getSelected().name + local choices = { } + for _, v in pairs(self.config[app]) do + table.insert(choices, { file = v }) + end + self.choices:setValues(choices) + self.choices:draw() +end + +function tab:enable() + self.config = Config.load('alternate') + + local apps = { } + for k, _ in pairs(self.config) do + table.insert(apps, { name = k }) + end + self.apps:setValues(apps) + + self:updateChoices() + + UI.Tab.enable(self) +end + +function tab:eventHandler(event) + if event.type == 'grid_focus_row' and event.element == self.apps then + self:updateChoices() + + elseif event.type == 'grid_select' and event.element == self.choices then + local app = self.apps:getSelected().name + Array.removeByValue(self.config[app], event.selected.file) + table.insert(self.config[app], 1, event.selected.file) + self:updateChoices() + Config.update('alternate', self.config) + + else + return UI.Tab.eventHandler(self, event) + end + return true +end + +return tab diff --git a/sys/autorun/hotkeys.lua b/sys/autorun/hotkeys.lua index 5c6b539..cbd93bf 100644 --- a/sys/autorun/hotkeys.lua +++ b/sys/autorun/hotkeys.lua @@ -42,7 +42,7 @@ keyboard.addHotkey('control-tab', function() return a.uid < b.uid end for _,tab in Util.spairs(tabs, compareTab) do - if not tab.hidden then + if not tab.hidden and not tab.noFocus then table.insert(visibleTabs, tab) end end diff --git a/sys/autorun/log.lua b/sys/autorun/log.lua index 4f53ed4..91bb193 100644 --- a/sys/autorun/log.lua +++ b/sys/autorun/log.lua @@ -54,6 +54,7 @@ if multishell and multishell.openTab then multishell.openTab({ title = 'System Log', fn = systemLog, + noTerminate = true, hidden = true, }) else diff --git a/sys/modules/opus/alternate.lua b/sys/modules/opus/alternate.lua index 6f0d24f..101de58 100644 --- a/sys/modules/opus/alternate.lua +++ b/sys/modules/opus/alternate.lua @@ -6,7 +6,7 @@ local function getConfig() return Config.load('alternate', { shell = { 'sys/apps/shell.lua', - 'rom/programs/shell', + 'rom/programs/shell.lua', }, lua = { 'sys/apps/Lua.lua', -- 2.49.1 From 25405f15c8d60bc9d6ae66ee580c6efb829719ae Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 12 Nov 2019 21:13:17 -0700 Subject: [PATCH 096/236] UI inspector --- sys/apps/inspect.lua | 154 ++++++++++++++++++ sys/modules/opus/ui/components/Button.lua | 20 +++ .../opus/ui/components/CheckboxGrid.lua | 54 ++++++ sys/modules/opus/ui/components/Tabs.lua | 16 ++ sys/modules/opus/ui/components/TextArea.lua | 6 + sys/modules/opus/ui/components/TextEntry.lua | 32 ++++ 6 files changed, 282 insertions(+) create mode 100644 sys/apps/inspect.lua create mode 100644 sys/modules/opus/ui/components/CheckboxGrid.lua diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua new file mode 100644 index 0000000..1824fde --- /dev/null +++ b/sys/apps/inspect.lua @@ -0,0 +1,154 @@ +local Event = require('opus.event') +local UI = require('opus.ui') + +local colors = _G.colors +local multishell = _ENV.multishell + +local args = { ... } +local name = args[1] or error('Syntax: inspect COMPONENT') +local events = { } +local page + +local function isRelevant(el) + return page.testContainer == el or el.parent and isRelevant(el.parent) +end + +local emitter = UI.Window.emit +function UI.Window:emit(event) + if not event.recorded and isRelevant(self) then + event.recorded = true + local t = { } + for k,v in pairs(event) do + if k ~= 'type' and k ~= 'recorded' then + table.insert(t, k .. ':' .. (type(v) == 'table' and (v.UIElement and v.uid or 'tbl') or tostring(v))) + end + end + table.insert(events, 1, { type = event.type, value = table.concat(t, ' '), raw = event }) + while #events > 10 do + table.remove(events) + end + end + return emitter(self, event) +end + +page = UI.Page { + testContainer = UI.Window { + ey = 10, + }, + tabs = UI.Tabs { + y = 11, + properties = UI.Tab { + backgroundColor = colors.red, + tabTitle = 'Properties', + grid = UI.ScrollingGrid { + headerBackgroundColor = colors.red, + sortColumn = 'key', + columns = { + { heading = 'key', key = 'key' }, + { heading = 'value', key = 'value', } + }, + accelerators = { + grid_select = 'edit_property', + }, + }, + }, + methodsTab = UI.Tab { + backgroundColor = colors.red, + tabTitle = 'Methods', + grid = UI.ScrollingGrid { + headerBackgroundColor = colors.red, + sortColumn = 'key', + columns = { + { heading = 'key', key = 'key' }, + }, + }, + }, + events = UI.Tab { + backgroundColor = colors.red, + tabTitle = 'Events', + grid = UI.ScrollingGrid { + headerBackgroundColor = colors.red, + values = events, + autospace = true, + columns = { + { heading = 'type', key = 'type' }, + { heading = 'value', key = 'value', } + }, + } + } + }, + editor = UI.SlideOut { + y = -4, height = 4, + backgroundColor = colors.green, + titleBar = UI.TitleBar { + event = 'editor_cancel', + title = 'Enter value', + }, + entry = UI.TextEntry { + y = 3, x = 2, ex = 10, + accelerators = { + enter = 'editor_apply', + }, + }, + }, + eventHandler = function (self, event) + if event.type == 'focus_change' and isRelevant(event.focused) then + local t = { } + for k,v in pairs(event.focused) do + table.insert(t, { + key = k, + value = tostring(v), + }) + end + self.tabs.properties.grid:setValues(t) + self.tabs.properties.grid:update() + self.tabs.properties.grid:draw() + + t = { } + for k,v in pairs(getmetatable(event.focused)) do + if type(v) == 'function' then + table.insert(t, { + key = k, + }) + end + end + self.tabs.methodsTab.grid:setValues(t) + self.tabs.methodsTab.grid:update() + self.tabs.methodsTab.grid:draw() + + elseif event.type == 'grid_select' and event.element == self.tabs.events.grid then + event.selected.raw.recorded = nil + multishell.openTab({ + path = 'sys/apps/Lua.lua', + args = { event.selected.raw }, + focused = true, + }) + + elseif event.type == 'grid_select' and event.element == self.tabs.properties.grid then + self.editor.entry.value = event.selected.value + self.editor:show() + + elseif event.type == 'editor_cancel' then + self.editor:hide() + + elseif event.type == 'editor_apply' then + self.editor:hide() + end + + return UI.Page.eventHandler(self, event) + end +} + +Event.onInterval(1, function() + page.tabs.events.grid:update() + page.tabs.events.grid:draw() + page.tabs.events.grid:sync() +end) + +local component = UI[name]() +local testing = component.example() + +page.testContainer:add({ test = testing }) + +UI:setPage(page) +UI:pullEvents() diff --git a/sys/modules/opus/ui/components/Button.lua b/sys/modules/opus/ui/components/Button.lua index c6ea3bf..2442614 100644 --- a/sys/modules/opus/ui/components/Button.lua +++ b/sys/modules/opus/ui/components/Button.lua @@ -64,3 +64,23 @@ function UI.Button:eventHandler(event) end return false end + +function UI.Button.example() + return UI.Window { + button1 = UI.Button { + x = 2, y = 2, + text = 'Press', + }, + button2 = UI.Button { + x = 2, y = 4, + backgroundColor = colors.green, + event = 'custom_event', + }, + button3 = UI.Button { + x = 12, y = 2, + height = 5, + event = 'big_event', + text = 'large button' + } + } +end diff --git a/sys/modules/opus/ui/components/CheckboxGrid.lua b/sys/modules/opus/ui/components/CheckboxGrid.lua new file mode 100644 index 0000000..c57c098 --- /dev/null +++ b/sys/modules/opus/ui/components/CheckboxGrid.lua @@ -0,0 +1,54 @@ +local class = require('opus.class') +local UI = require('opus.ui') + +local function safeValue(v) + local t = type(v) + if t == 'string' or t == 'number' then + return v + end + return tostring(v) +end + +UI.CheckboxGrid = class(UI.Grid) +UI.CheckboxGrid.defaults = { + UIElement = 'CheckboxGrid', + checkedKey = 'checked', + accelerators = { + space = 'grid_toggle', + }, +} +function UI.CheckboxGrid:drawRow(sb, row, focused, bg, fg) + local ind = focused and self.focusIndicator or ' ' + + for _,col in pairs(self.columns) do + sb:write(ind .. safeValue(row[col.key] or ''), + col.cw + 1, + col.align, + col.backgroundColor or bg, + col.textColor or fg) + ind = ' ' + end +end + +function UI.CheckboxGrid:eventHandler(event) + if event.type == 'key_enter' and self.selected then + self.selected.checked = not self.selected.checked + self:draw() + self:emit({ type = 'grid_check', checked = self.selected, element = self }) + else + return UI.Grid.eventHandler(self, event) + end +end + +function UI.CheckboxGrid.example() + return UI.CheckboxGrid { + values = { + { checked = false, name = 'unchecked' }, + { checked = true, name = 'checked' }, + }, + columns = { + { heading = 'Checked', key = 'checked' }, + { heading = 'Data', key = 'name', } + }, + } +end diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index 46f87d1..a27bcbb 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -87,3 +87,19 @@ function UI.Tabs:eventHandler(event) tab:draw() end end + +function UI.Tabs.example() + return UI.Tabs { + [1] = UI.Tab { + tabTitle = 'tab1', + entry = UI.TextEntry { y = 3, shadowText = 'text' }, + }, + [2] = UI.Tab { + tabTitle = 'tab2', + button = UI.Button { y = 3 }, + }, + [3] = UI.Tab { + tabTitle = 'tab3', + } + } +end diff --git a/sys/modules/opus/ui/components/TextArea.lua b/sys/modules/opus/ui/components/TextArea.lua index 51b8972..ea7540f 100644 --- a/sys/modules/opus/ui/components/TextArea.lua +++ b/sys/modules/opus/ui/components/TextArea.lua @@ -34,3 +34,9 @@ function UI.TextArea:draw() end end end + +function UI.TextArea.example() + return UI.TextArea { + value = 'sample text\nabc' + } +end \ No newline at end of file diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index dda3766..ea507c8 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -134,3 +134,35 @@ function UI.TextEntry:eventHandler(event) return false end + +function UI.TextEntry.example() + return UI.Window { + text = UI.TextEntry { + x = 2, y = 2, + width = 12, + limit = 36, + shadowText = 'normal', + }, + upper = UI.TextEntry { + x = 2, y = 3, + width = 12, + limit = 36, + shadowText = 'upper', + transform = 'uppercase', + }, + lower = UI.TextEntry { + x = 2, y = 4, + width = 12, + limit = 36, + shadowText = 'lower', + transform = 'lowercase', + }, + number = UI.TextEntry { + x = 2, y = 5, + width = 12, + limit = 36, + transform = 'number', + shadowText = 'number', + }, + } +end -- 2.49.1 From 053003f429e35978d53ed562f1d426c255d93e5b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 12 Nov 2019 23:04:31 -0700 Subject: [PATCH 097/236] inspect cleanup --- sys/apps/inspect.lua | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 1824fde..ce41738 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -1,13 +1,16 @@ -local Event = require('opus.event') -local UI = require('opus.ui') +local UI = require('opus.ui') local colors = _G.colors local multishell = _ENV.multishell -local args = { ... } -local name = args[1] or error('Syntax: inspect COMPONENT') +local name = ({ ... })[1] or error('Syntax: inspect COMPONENT') local events = { } local page +local component = UI[name] and UI[name]() or error('Invalid component') + +if not component.example then + error('No example present') +end local function isRelevant(el) return page.testContainer == el or el.parent and isRelevant(el.parent) @@ -27,6 +30,10 @@ function UI.Window:emit(event) while #events > 10 do table.remove(events) end + page.tabs.events.grid:update() + if page.tabs.events.enabled then + page.tabs.events.grid:draw() + end end return emitter(self, event) end @@ -34,11 +41,11 @@ end page = UI.Page { testContainer = UI.Window { ey = 10, + testing = component.example(), }, tabs = UI.Tabs { y = 11, properties = UI.Tab { - backgroundColor = colors.red, tabTitle = 'Properties', grid = UI.ScrollingGrid { headerBackgroundColor = colors.red, @@ -53,7 +60,6 @@ page = UI.Page { }, }, methodsTab = UI.Tab { - backgroundColor = colors.red, tabTitle = 'Methods', grid = UI.ScrollingGrid { headerBackgroundColor = colors.red, @@ -64,7 +70,6 @@ page = UI.Page { }, }, events = UI.Tab { - backgroundColor = colors.red, tabTitle = 'Events', grid = UI.ScrollingGrid { headerBackgroundColor = colors.red, @@ -139,16 +144,5 @@ page = UI.Page { end } -Event.onInterval(1, function() - page.tabs.events.grid:update() - page.tabs.events.grid:draw() - page.tabs.events.grid:sync() -end) - -local component = UI[name]() -local testing = component.example() - -page.testContainer:add({ test = testing }) - UI:setPage(page) UI:pullEvents() -- 2.49.1 From 65c6ebf7114f6790f56922e92d7c540ab2676e75 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 13 Nov 2019 14:24:43 -0700 Subject: [PATCH 098/236] properly handle empty text entry fields (including transformations) --- sys/apps/Help.lua | 2 +- sys/apps/Lua.lua | 9 ++- sys/apps/Welcome.lua | 4 +- sys/apps/inspect.lua | 17 ++-- sys/apps/system/label.lua | 2 +- sys/apps/system/path.lua | 2 +- sys/modules/opus/entry.lua | 81 ++++++++++++-------- sys/modules/opus/ui/components/Form.lua | 18 +---- sys/modules/opus/ui/components/Grid.lua | 40 ++++++++++ sys/modules/opus/ui/components/TextEntry.lua | 39 ++++------ 10 files changed, 131 insertions(+), 83 deletions(-) diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index ffd0486..8bae671 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -81,7 +81,7 @@ function page:eventHandler(event) end elseif event.type == 'text_change' then - if #event.text == 0 then + if not event.text then self.grid.values = topics else self.grid.values = { } diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 45cec04..2de6195 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -153,10 +153,11 @@ function page:eventHandler(event) self.tabs:selectTab(self.tabs[2]) elseif event.type == 'autocomplete' then - local sz = #self.prompt.value + local value = self.prompt.value or '' + local sz = #value local pos = self.prompt.entry.pos - self:setPrompt(autocomplete(sandboxEnv, self.prompt.value, self.prompt.entry.pos)) - self.prompt:setPosition(pos + #self.prompt.value - sz) + self:setPrompt(autocomplete(sandboxEnv, value, self.prompt.entry.pos)) + self.prompt:setPosition(pos + #value - sz) self.prompt:updateCursor() elseif event.type == 'device' then @@ -177,7 +178,7 @@ function page:eventHandler(event) history:reset() elseif event.type == 'command_enter' then - local s = tostring(self.prompt.value) + local s = tostring(self.prompt.value or '') if #s > 0 then self:executeStatement(s) diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 04b60f9..a25278f 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -120,7 +120,9 @@ local page = UI.Page { } function page.wizard.pages.label:validate() - os.setComputerLabel(self.label.value) + if self.label.value then + os.setComputerLabel(self.label.value) + end return true end diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index ce41738..ae991d6 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -6,11 +6,6 @@ local multishell = _ENV.multishell local name = ({ ... })[1] or error('Syntax: inspect COMPONENT') local events = { } local page -local component = UI[name] and UI[name]() or error('Invalid component') - -if not component.example then - error('No example present') -end local function isRelevant(el) return page.testContainer == el or el.parent and isRelevant(el.parent) @@ -18,8 +13,7 @@ end local emitter = UI.Window.emit function UI.Window:emit(event) - if not event.recorded and isRelevant(self) then - event.recorded = true + if not event._recorded and isRelevant(self) then local t = { } for k,v in pairs(event) do if k ~= 'type' and k ~= 'recorded' then @@ -35,9 +29,16 @@ function UI.Window:emit(event) page.tabs.events.grid:draw() end end + event._recorded = true return emitter(self, event) end +-- do not load component until emit hook is in place +local component = UI[name] and UI[name]() or error('Invalid component') +if not component.example then + error('No example present') +end + page = UI.Page { testContainer = UI.Window { ey = 10, @@ -122,7 +123,7 @@ page = UI.Page { self.tabs.methodsTab.grid:draw() elseif event.type == 'grid_select' and event.element == self.tabs.events.grid then - event.selected.raw.recorded = nil + event.selected.raw._recorded = nil multishell.openTab({ path = 'sys/apps/Lua.lua', args = { event.selected.raw }, diff --git a/sys/apps/system/label.lua b/sys/apps/system/label.lua index e4a73f2..0dc77a7 100644 --- a/sys/apps/system/label.lua +++ b/sys/apps/system/label.lua @@ -39,7 +39,7 @@ local labelTab = UI.Tab { } function labelTab:eventHandler(event) - if event.type == 'update_label' then + if event.type == 'update_label' and self.label.value then os.setComputerLabel(self.label.value) self:emit({ type = 'success_message', message = 'Label updated' }) return true diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index bf30f7e..33249dd 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -59,7 +59,7 @@ function tab:save() end function tab:eventHandler(event) - if event.type == 'update_path' then + if event.type == 'update_path' and self.entry.value then table.insert(self.grid.values, { value = self.entry.value, }) diff --git a/sys/modules/opus/entry.lua b/sys/modules/opus/entry.lua index 693fe87..46e4eeb 100644 --- a/sys/modules/opus/entry.lua +++ b/sys/modules/opus/entry.lua @@ -2,39 +2,48 @@ local class = require('opus.class') local os = _G.os +-- convert value to a string (supporting nils or numbers in value) +local function _val(a) + return a and tostring(a) or '' +end + local Entry = class() function Entry:init(args) self.pos = 0 self.scroll = 0 - self.value = '' + self.value = args.value self.width = args.width or 256 self.limit = args.limit or 1024 self.mark = { } self.offset = args.offset or 1 + self.transform = args.transform or function(a) return a end end function Entry:reset() self.pos = 0 self.scroll = 0 - self.value = '' + self.value = nil self.mark = { } end function Entry:nextWord() - return select(2, self.value:find("[%s%p]?%w[%s%p]", self.pos + 1)) or #self.value + local value = _val(self.value) + return select(2, value:find("[%s%p]?%w[%s%p]", self.pos + 1)) or #value end function Entry:prevWord() - local x = #self.value - (self.pos - 1) - local _, n = self.value:reverse():find("[%s%p]?%w[%s%p]", x) - return n and #self.value - n + 1 or 0 + local value = _val(self.value) + local x = #value - (self.pos - 1) + local _, n = value:reverse():find("[%s%p]?%w[%s%p]", x) + return n and #value - n + 1 or 0 end function Entry:updateScroll() local ps = self.scroll - if self.pos > #self.value then - self.pos = #self.value + local value = _val(self.value) + if self.pos > #value then + self.pos = #value self.scroll = 0 -- ?? end if self.pos - self.scroll > self.width then @@ -48,21 +57,25 @@ function Entry:updateScroll() end function Entry:copyText(cx, ex) - return self.value:sub(cx + 1, ex) + -- this should be transformed (ie. if number) + return _val(self.value):sub(cx + 1, ex) end function Entry:insertText(x, text) - if #self.value + #text > self.limit then - text = text:sub(1, self.limit-#self.value) + text = tostring(self.transform(text)) or '' + local value = _val(self.value) + if #value + #text > self.limit then + text = text:sub(1, self.limit-#value) end - self.value = self.value:sub(1, x) .. text .. self.value:sub(x + 1) + self.value = self.transform(value:sub(1, x) .. text .. value:sub(x + 1)) self.pos = self.pos + #text end function Entry:deleteText(sx, ex) - local front = self.value:sub(1, sx) - local back = self.value:sub(ex + 1, #self.value) - self.value = front .. back + local value = _val(self.value) + local front = value:sub(1, sx) + local back = value:sub(ex + 1, #value) + self.value = self.transform(front .. back) self.pos = sx end @@ -74,7 +87,7 @@ function Entry:moveLeft() end function Entry:moveRight() - if self.pos < #self.value then + if self.pos < #_val(self.value) then self.pos = self.pos + 1 return true end @@ -88,14 +101,14 @@ function Entry:moveHome() end function Entry:moveEnd() - if self.pos ~= #self.value then - self.pos = #self.value + if self.pos ~= #_val(self.value) then + self.pos = #_val(self.value) return true end end function Entry:moveTo(ie) - self.pos = math.max(0, math.min(ie.x + self.scroll - self.offset, #self.value)) + self.pos = math.max(0, math.min(ie.x + self.scroll - self.offset, #_val(self.value))) end function Entry:backspace() @@ -107,7 +120,7 @@ function Entry:backspace() end function Entry:moveWordRight() - if self.pos < #self.value then + if self.pos < #_val(self.value) then self.pos = self:nextWord(self.value, self.pos + 1) return true end @@ -123,7 +136,7 @@ end function Entry:delete() if self.mark.active then self:deleteText(self.mark.x, self.mark.ex) - elseif self.pos < #self.value then + elseif self.pos < #_val(self.value) then self:deleteText(self.pos, self.pos + 1) end end @@ -137,15 +150,16 @@ function Entry:cutFromStart() end function Entry:cutToEnd() - if self.pos < #self.value then - local text = self:copyText(self.pos, #self.value) - self:deleteText(self.pos, #self.value) + local value = _val(self.value) + if self.pos < #value then + local text = self:copyText(self.pos, #value) + self:deleteText(self.pos, #value) os.queueEvent('clipboard_copy', text) end end function Entry:cutNextWord() - if self.pos < #self.value then + if self.pos < #_val(self.value) then local ex = self:nextWord(self.value, self.pos) local text = self:copyText(self.pos, ex) self:deleteText(self.pos, ex) @@ -170,7 +184,7 @@ function Entry:insertChar(ie) end function Entry:copy() - if #self.value > 0 then + if #_val(self.value) > 0 then self.mark.continue = true if self.mark.active then self:copyMarked() @@ -202,7 +216,7 @@ function Entry:paste(ie) end function Entry:clearLine() - if #self.value > 0 then + if #_val(self.value) > 0 then self:reset() end end @@ -233,10 +247,13 @@ function Entry:unmark() end function Entry:markAnchor(ie) + local wasMarking = self.mark.active self:unmark() self:moveTo(ie) self:markBegin() self:markFinish() + + self.textChanged = wasMarking end function Entry:markLeft() @@ -257,7 +274,7 @@ function Entry:markWord(ie) local index = 1 self:moveTo(ie) while true do - local s, e = self.value:find('%w+', index) + local s, e = _val(self.value):find('%w+', index) if not s or s - 1 > self.pos then break end @@ -288,12 +305,12 @@ function Entry:markPrevWord() end function Entry:markAll() - if #self.value > 0 then + if #_val(self.value) > 0 then self.mark.anchor = { x = 1 } self.mark.active = true self.mark.continue = true self.mark.x = 0 - self.mark.ex = #self.value + self.mark.ex = #_val(self.value) self.textChanged = true end end @@ -373,6 +390,10 @@ function Entry:process(ie) action(self, ie) + if not self.value or #_val(self.value) == 0 then + self.value = nil + end +_syslog(tostring(line) .. ' ' .. tostring(self.value) .. ' ' .. tostring(self.textChanged)) self.textChanged = self.textChanged or self.value ~= line self.posChanged = pos ~= self.pos self:updateScroll() diff --git a/sys/modules/opus/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua index ed9ae0d..da312d9 100644 --- a/sys/modules/opus/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -32,7 +32,7 @@ function UI.Form:setValues(values) if child.setValue then child:setValue(self.values[child.formKey]) else - child.value = self.values[child.formKey] or '' + child.value = self.values[child.formKey] end end end @@ -56,7 +56,7 @@ function UI.Form:createForm() for _, child in pairs(self) do if type(child) == 'table' and child.UIElement then if child.formKey then - child.value = self.values[child.formKey] or '' + child.value = self.values[child.formKey] end if child.formLabel then child.x = self.labelWidth + self.margin - 1 @@ -99,14 +99,6 @@ function UI.Form:validateField(field) return false, 'Field is required' end end - if field.validate == 'numeric' then - field.value = field.value or '' - if #tostring(field.value) > 0 then - if not tonumber(field.value) then - return false, 'Invalid number' - end - end - end return true end @@ -124,11 +116,7 @@ function UI.Form:save() end for _,child in pairs(self.children) do if child.formKey then - if child.validate == 'numeric' then - self.values[child.formKey] = tonumber(child.value) - else - self.values[child.formKey] = child.value - end + self.values[child.formKey] = child.value end end diff --git a/sys/modules/opus/ui/components/Grid.lua b/sys/modules/opus/ui/components/Grid.lua index bcdab81..e1edfe8 100644 --- a/sys/modules/opus/ui/components/Grid.lua +++ b/sys/modules/opus/ui/components/Grid.lua @@ -493,3 +493,43 @@ function UI.Grid:eventHandler(event) end return true end + +function UI.Grid.example() + local values = { + { key = 'key1', value = 'value1' }, + { key = 'key2', value = 'value2' }, + { key = 'key3', value = 'value3-longer value text' }, + { key = 'key4', value = 'value4' }, + { key = 'key5', value = 'value5' }, + } + return UI.Window { + regular = UI.Grid { + ex = '48%', ey = 4, + values = values, + sortColumn = 'key', + inverseSort = true, + columns = { + { heading = 'key', key = 'key' }, + { heading = 'value', key = 'value' }, + }, + }, + noheader = UI.Grid { + ex = '48%', y = 6, ey = -2, + disableHeader = true, + values = values, + columns = { + { heading = 'key', key = 'key', width = 6, }, + { heading = 'value', key = 'value', textColor = colors.yellow }, + }, + }, + autospace = UI.Grid { + x = '52%', ey = 4, + autospace = true, + values = values, + columns = { + { heading = 'key', key = 'key' }, + { heading = 'value', key = 'value' }, + }, + }, + } +end diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index ea507c8..1a3f604 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -5,8 +5,15 @@ local Util = require('opus.util') local colors = _G.colors local _rep = string.rep -local _lower = string.lower -local _upper = string.upper + +local function transform(directive) + local transforms = { + lowercase = string.lower, + uppercase = string.upper, + number = tonumber, + } + return transforms[directive] +end UI.TextEntry = class(UI.Window) UI.TextEntry.defaults = { @@ -25,7 +32,7 @@ UI.TextEntry.defaults = { } } function UI.TextEntry:postInit() - self.entry = entry({ limit = self.limit, offset = 2 }) + self.entry = entry({ limit = self.limit, offset = 2, transform = transform(self.transform) }) end function UI.TextEntry:layout() @@ -36,13 +43,13 @@ end function UI.TextEntry:setValue(value) self.value = value --or '' self.entry:unmark() - self.entry.value = tostring(value) + self.entry.value = value --tostring(value or '') self.entry:updateScroll() end function UI.TextEntry:setPosition(pos) self.entry.pos = pos - self.entry.value = tostring(self.value or '') + self.entry.value = self.value --tostring(self.value or '') -- WHY HERE ? self.entry:updateScroll() end @@ -105,27 +112,15 @@ function UI.TextEntry:focus() end end -function UI.TextEntry:_transform(text) - if self.transform == 'lowercase' then - return _lower(text) - elseif self.transform == 'uppercase' then - return _upper(text) - elseif self.transform == 'number' then - return tonumber(text) --or 0 - end - return text -end - function UI.TextEntry:eventHandler(event) - local text = self.value --or '' - self.entry.value = tostring(text or '') + local text = self.value + self.entry.value = text if event.ie and self.entry:process(event.ie) then if self.entry.textChanged then - self.value = self:_transform(self.entry.value) +--_syslog(tostring(self.entry.value) .. ' ' .. tostring(self.value)) + self.value = self.entry.value self:draw() - if text ~= self.value then - self:emit({ type = 'text_change', text = self.value, element = self }) - end + self:emit({ type = 'text_change', text = self.value, element = self }) elseif self.entry.posChanged then self:updateCursor() end -- 2.49.1 From 0a828fecc558394e82e2e0948c098f733de78736 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 13 Nov 2019 14:38:24 -0700 Subject: [PATCH 099/236] oops --- sys/apps/shell.lua | 5 ++++- sys/modules/opus/entry.lua | 2 +- sys/modules/opus/ui/components/TextEntry.lua | 7 +++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index d64f046..6124a3d 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -586,6 +586,7 @@ local function shellRead(history) end local _,cy = term.getCursorPos() term.setCursorPos(3, cy) + entry.value = entry.value or '' local filler = #entry.value < lastLen and string.rep(' ', lastLen - #entry.value) or '' @@ -635,6 +636,7 @@ local function shellRead(history) redraw() elseif ie.code == 'tab' then + entry.value = entry.value or '' if entry.pos == #entry.value then local cline = autocomplete(entry.value) if cline then @@ -650,6 +652,7 @@ local function shellRead(history) else entry:process(ie) + entry.value = entry.value or '' if entry.textChanged then redraw() elseif entry.posChanged then @@ -666,7 +669,7 @@ local function shellRead(history) print() term.setCursorBlink( false ) - return entry.value + return entry.value or '' end local history = History.load('usr/.shell_history', 25) diff --git a/sys/modules/opus/entry.lua b/sys/modules/opus/entry.lua index 46e4eeb..7deb36f 100644 --- a/sys/modules/opus/entry.lua +++ b/sys/modules/opus/entry.lua @@ -393,7 +393,7 @@ function Entry:process(ie) if not self.value or #_val(self.value) == 0 then self.value = nil end -_syslog(tostring(line) .. ' ' .. tostring(self.value) .. ' ' .. tostring(self.textChanged)) + self.textChanged = self.textChanged or self.value ~= line self.posChanged = pos ~= self.pos self:updateScroll() diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 1a3f604..75665a6 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -41,15 +41,15 @@ function UI.TextEntry:layout() end function UI.TextEntry:setValue(value) - self.value = value --or '' + self.value = value self.entry:unmark() - self.entry.value = value --tostring(value or '') + self.entry.value = value self.entry:updateScroll() end function UI.TextEntry:setPosition(pos) self.entry.pos = pos - self.entry.value = self.value --tostring(self.value or '') -- WHY HERE ? + self.entry.value = self.value -- WHY HERE ? self.entry:updateScroll() end @@ -117,7 +117,6 @@ function UI.TextEntry:eventHandler(event) self.entry.value = text if event.ie and self.entry:process(event.ie) then if self.entry.textChanged then ---_syslog(tostring(self.entry.value) .. ' ' .. tostring(self.value)) self.value = self.entry.value self:draw() self:emit({ type = 'text_change', text = self.value, element = self }) -- 2.49.1 From db48031c7c19fbaa9ba2fa210e14dc3cc1c922f1 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 13 Nov 2019 15:17:23 -0700 Subject: [PATCH 100/236] funnel all events through emit --- sys/modules/opus/entry.lua | 14 ++++++++------ sys/modules/opus/ui.lua | 27 ++++++++++----------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/sys/modules/opus/entry.lua b/sys/modules/opus/entry.lua index 7deb36f..aa2397f 100644 --- a/sys/modules/opus/entry.lua +++ b/sys/modules/opus/entry.lua @@ -62,13 +62,15 @@ function Entry:copyText(cx, ex) end function Entry:insertText(x, text) - text = tostring(self.transform(text)) or '' - local value = _val(self.value) - if #value + #text > self.limit then - text = text:sub(1, self.limit-#value) + text = tostring(self.transform(text) or '') + if #text > 0 then + local value = _val(self.value) + if #value + #text > self.limit then + text = text:sub(1, self.limit-#value) + end + self.value = self.transform(value:sub(1, x) .. text .. value:sub(x + 1)) + self.pos = self.pos + #text end - self.value = self.transform(value:sub(1, x) .. text .. value:sub(x + 1)) - self.pos = self.pos + #text end function Entry:deleteText(sx, ex) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 9e9a4c5..2ab507d 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -248,23 +248,8 @@ function Manager:emitEvent(event) end end -function Manager:inputEvent(parent, event) - while parent do - if parent.accelerators then - local acc = parent.accelerators[event.key] - if acc then - if parent:emit({ type = acc, element = parent }) then - return true - end - end - end - if parent.eventHandler then - if parent:eventHandler(event) then - return true - end - end - parent = parent.parent - end +function Manager:inputEvent(parent, event) -- deprecate ? + return parent and parent:emit(event) end function Manager:click(target, code, button, x, y) @@ -927,6 +912,14 @@ end function UI.Window:emit(event) local parent = self while parent do + if parent.accelerators and event.key then -- not ideal + -- could be [event.key or event.type] to support accelerators + -- for non-input type events + local acc = parent.accelerators[event.key] + if acc and parent:emit({ type = acc, element = parent }) then + return true + end + end if parent.eventHandler then if parent:eventHandler(event) then return true -- 2.49.1 From 3241326a2f1662fdd312a868ab1eab49ed4c1257 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 13 Nov 2019 21:50:00 -0700 Subject: [PATCH 101/236] accelerators for any event + more inspect examples --- sys/apps/inspect.lua | 30 ++++++++++++++----- sys/modules/opus/ui.lua | 16 ++++++---- sys/modules/opus/ui/components/Checkbox.lua | 6 ++++ sys/modules/opus/ui/components/Chooser.lua | 24 ++++++++++++++- sys/modules/opus/ui/components/DropMenu.lua | 13 ++++++++ sys/modules/opus/ui/components/Form.lua | 20 +++++++++++++ sys/modules/opus/ui/components/Grid.lua | 3 ++ sys/modules/opus/ui/components/MenuBar.lua | 9 ++++++ .../opus/ui/components/Notification.lua | 25 ++++++++++++++++ sys/modules/opus/ui/components/Slider.lua | 8 +++++ 10 files changed, 140 insertions(+), 14 deletions(-) diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index ae991d6..48feeb4 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -1,4 +1,5 @@ -local UI = require('opus.ui') +local UI = require('opus.ui') +local Util = require('opus.util') local colors = _G.colors local multishell = _ENV.multishell @@ -6,6 +7,7 @@ local multishell = _ENV.multishell local name = ({ ... })[1] or error('Syntax: inspect COMPONENT') local events = { } local page +local lastEvent local function isRelevant(el) return page.testContainer == el or el.parent and isRelevant(el.parent) @@ -13,7 +15,8 @@ end local emitter = UI.Window.emit function UI.Window:emit(event) - if not event._recorded and isRelevant(self) then + if event ~= lastEvent and isRelevant(self) then + lastEvent = event local t = { } for k,v in pairs(event) do if k ~= 'type' and k ~= 'recorded' then @@ -21,7 +24,7 @@ function UI.Window:emit(event) end end table.insert(events, 1, { type = event.type, value = table.concat(t, ' '), raw = event }) - while #events > 10 do + while #events > 20 do table.remove(events) end page.tabs.events.grid:update() @@ -29,7 +32,6 @@ function UI.Window:emit(event) page.tabs.events.grid:draw() end end - event._recorded = true return emitter(self, event) end @@ -72,7 +74,14 @@ page = UI.Page { }, events = UI.Tab { tabTitle = 'Events', + UI.MenuBar { + y = -1, + buttons = { + { text = 'Clear', event = 'event_clear' }, + } + }, grid = UI.ScrollingGrid { + ey = -2, headerBackgroundColor = colors.red, values = events, autospace = true, @@ -80,6 +89,9 @@ page = UI.Page { { heading = 'type', key = 'type' }, { heading = 'value', key = 'value', } }, + accelerators = { + grid_select = 'event_inspect', + }, } } }, @@ -122,15 +134,19 @@ page = UI.Page { self.tabs.methodsTab.grid:update() self.tabs.methodsTab.grid:draw() - elseif event.type == 'grid_select' and event.element == self.tabs.events.grid then - event.selected.raw._recorded = nil + elseif event.type == 'event_clear' then + Util.clear(self.tabs.events.grid.values) + self.tabs.events.grid:update() + self.tabs.events.grid:draw() + + elseif event.type == 'event_inspect' then multishell.openTab({ path = 'sys/apps/Lua.lua', args = { event.selected.raw }, focused = true, }) - elseif event.type == 'grid_select' and event.element == self.tabs.properties.grid then + elseif event.type == 'edit_property' then self.editor.entry.value = event.selected.value self.editor:show() diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 2ab507d..5686f93 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -912,12 +912,16 @@ end function UI.Window:emit(event) local parent = self while parent do - if parent.accelerators and event.key then -- not ideal - -- could be [event.key or event.type] to support accelerators - -- for non-input type events - local acc = parent.accelerators[event.key] - if acc and parent:emit({ type = acc, element = parent }) then - return true + if parent.accelerators then + -- events types can be made unique via accelerators + local acc = parent.accelerators[event.key or event.type] + if acc and acc ~= event.type then -- don't get stuck in a loop + local event2 = Util.shallowCopy(event) + event2.type = acc + event2.key = nil + if parent:emit(event2) then + return true + end end end if parent.eventHandler then diff --git a/sys/modules/opus/ui/components/Checkbox.lua b/sys/modules/opus/ui/components/Checkbox.lua index a90e911..54343e5 100644 --- a/sys/modules/opus/ui/components/Checkbox.lua +++ b/sys/modules/opus/ui/components/Checkbox.lua @@ -61,3 +61,9 @@ function UI.Checkbox:eventHandler(event) return true end end + +function UI.Checkbox.example() + return UI.Checkbox { + x = 2, y = 2, + } +end diff --git a/sys/modules/opus/ui/components/Chooser.lua b/sys/modules/opus/ui/components/Chooser.lua index e4d41fa..9698f6f 100644 --- a/sys/modules/opus/ui/components/Chooser.lua +++ b/sys/modules/opus/ui/components/Chooser.lua @@ -53,7 +53,7 @@ function UI.Chooser:eventHandler(event) if event.key == 'right' or event.key == 'space' then local _,k = Util.find(self.choices, 'value', self.value) local choice - if not k then k = 1 end + if not k then k = 0 end if k and k < #self.choices then choice = self.choices[k+1] else @@ -86,3 +86,25 @@ function UI.Chooser:eventHandler(event) end end end + +function UI.Chooser.example() + return UI.Window { + a = UI.Chooser { + x = 2, y = 2, + choices = { + { name = 'choice1', value = 'value1' }, + { name = 'choice2', value = 'value2' }, + { name = 'choice3', value = 'value3' }, + }, + value = 'value2', + }, + b = UI.Chooser { + x = 2, y = 4, + choices = { + { name = 'choice1', value = 'value1' }, + { name = 'choice2', value = 'value2' }, + { name = 'choice3', value = 'value3' }, + }, + } + } +end diff --git a/sys/modules/opus/ui/components/DropMenu.lua b/sys/modules/opus/ui/components/DropMenu.lua index 5cce43a..41bdaf9 100644 --- a/sys/modules/opus/ui/components/DropMenu.lua +++ b/sys/modules/opus/ui/components/DropMenu.lua @@ -73,3 +73,16 @@ function UI.DropMenu:eventHandler(event) end return true end + +function UI.DropMenu.example() + return UI.MenuBar { + buttons = { + { text = 'File', dropdown = { + { text = 'Run', event = 'run' }, + { text = 'Shell s', event = 'shell' }, + { spacer = true }, + { text = 'Quit ^q', event = 'quit' }, + } }, + } + } +end diff --git a/sys/modules/opus/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua index da312d9..69cb7ca 100644 --- a/sys/modules/opus/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -134,3 +134,23 @@ function UI.Form:eventHandler(event) end return true end + +function UI.Form.example() + return UI.Form { + x = 2, ex = -2, y = 2, + ptype = UI.Chooser { + formLabel = 'Type', formKey = 'type', + width = 10, + choices = { + { name = 'Modem', value = 'wireless_modem' }, + { name = 'Drive', value = 'disk_drive' }, + }, + }, + drive_id = UI.TextEntry { + formLabel = 'Drive', formKey = 'drive_id', + required = true, + width = 5, + transform = 'number', + }, + } +end diff --git a/sys/modules/opus/ui/components/Grid.lua b/sys/modules/opus/ui/components/Grid.lua index e1edfe8..1d6e480 100644 --- a/sys/modules/opus/ui/components/Grid.lua +++ b/sys/modules/opus/ui/components/Grid.lua @@ -512,6 +512,9 @@ function UI.Grid.example() { heading = 'key', key = 'key' }, { heading = 'value', key = 'value' }, }, + accelerators = { + grid_select = 'custom_select', + } }, noheader = UI.Grid { ex = '48%', y = 6, ey = -2, diff --git a/sys/modules/opus/ui/components/MenuBar.lua b/sys/modules/opus/ui/components/MenuBar.lua index bccb9be..85f435b 100644 --- a/sys/modules/opus/ui/components/MenuBar.lua +++ b/sys/modules/opus/ui/components/MenuBar.lua @@ -88,3 +88,12 @@ function UI.MenuBar:eventHandler(event) return true end end + +function UI.MenuBar.example() + return UI.MenuBar { + buttons = { + { text = 'Choice1', event = 'event1' }, + { text = 'Choice2', event = 'event2' }, + } + } +end diff --git a/sys/modules/opus/ui/components/Notification.lua b/sys/modules/opus/ui/components/Notification.lua index f1b38f6..bf02256 100644 --- a/sys/modules/opus/ui/components/Notification.lua +++ b/sys/modules/opus/ui/components/Notification.lua @@ -90,3 +90,28 @@ function UI.Notification:eventHandler(event) end end end + +function UI.Notification.example() + return UI.Window { + notify = UI.Notification { + anchor = 'top', + }, + button1 = UI.Button { + x = 2, y = 3, + text = 'success', + event = 'test_success', + }, + button2 = UI.Button { + x = 2, y = 5, + text = 'error', + event = 'test_error', + }, + eventHandler = function (self, event) + if event.type == 'test_success' then + self.notify:success('Example text') + elseif event.type == 'test_error' then + self.notify:error('Example text') + end + end, + } +end diff --git a/sys/modules/opus/ui/components/Slider.lua b/sys/modules/opus/ui/components/Slider.lua index 9dee765..09325b4 100644 --- a/sys/modules/opus/ui/components/Slider.lua +++ b/sys/modules/opus/ui/components/Slider.lua @@ -75,3 +75,11 @@ function UI.Slider:eventHandler(event) self:draw() end end + +function UI.Slider.example() + return UI.Slider { + y = 2, x = 2, ex = -2, + min = 0, max = 1, + help = 'Volume setting', + } +end -- 2.49.1 From 14057c2bf9a06ee5f2aebeeda28d3e39df890af8 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 14 Nov 2019 15:43:20 -0700 Subject: [PATCH 102/236] moar ui examples --- sys/modules/opus/ui/components/Embedded.lua | 23 ++++++++++++++ .../opus/ui/components/Notification.lua | 2 +- sys/modules/opus/ui/components/SlideOut.lua | 26 ++++++++++++++++ sys/modules/opus/ui/components/Slider.lua | 3 +- sys/modules/opus/ui/components/StatusBar.lua | 17 ++++++++++ sys/modules/opus/ui/components/TextEntry.lua | 7 ++++- sys/modules/opus/ui/components/Wizard.lua | 31 +++++++++++++++++++ 7 files changed, 105 insertions(+), 4 deletions(-) diff --git a/sys/modules/opus/ui/components/Embedded.lua b/sys/modules/opus/ui/components/Embedded.lua index fcdee82..e15df33 100644 --- a/sys/modules/opus/ui/components/Embedded.lua +++ b/sys/modules/opus/ui/components/Embedded.lua @@ -74,3 +74,26 @@ end function UI.Embedded:focus() -- allow scrolling end + +function UI.Embedded.example() + local Event = require('opus.event') + local Util = require('opus.util') + local term = _G.term + + return UI.Embedded { + visible = true, + enable = function (self) + UI.Embedded.enable(self) + Event.addRoutine(function() + local oterm = term.redirect(self.win) + Util.run(_ENV, '/sys/apps/shell.lua') + term.redirect(oterm) + end) + end, + eventHandler = function(_, event) + if event.type == 'key' then + return true + end + end + } +end diff --git a/sys/modules/opus/ui/components/Notification.lua b/sys/modules/opus/ui/components/Notification.lua index bf02256..7b11543 100644 --- a/sys/modules/opus/ui/components/Notification.lua +++ b/sys/modules/opus/ui/components/Notification.lua @@ -110,7 +110,7 @@ function UI.Notification.example() if event.type == 'test_success' then self.notify:success('Example text') elseif event.type == 'test_error' then - self.notify:error('Example text') + self.notify:error('Example text', 0) end end, } diff --git a/sys/modules/opus/ui/components/SlideOut.lua b/sys/modules/opus/ui/components/SlideOut.lua index 37f5e37..c5779dd 100644 --- a/sys/modules/opus/ui/components/SlideOut.lua +++ b/sys/modules/opus/ui/components/SlideOut.lua @@ -50,3 +50,29 @@ function UI.SlideOut:eventHandler(event) return true end end + +function UI.SlideOut.example() + return UI.Window { + button = UI.Button { + x = 2, y = 2, + text = 'show', + }, + slideOut = UI.SlideOut { + backgroundColor = colors.yellow, + y = -4, height = 4, + button = UI.Button { + x = 2, y = 2, + text = 'hide', + }, + }, + eventHandler = function (self, event) + if event.type == 'button_press' then + if self.slideOut.enabled then + self.slideOut:hide() + else + self.slideOut:show() + end + end + end, + } +end diff --git a/sys/modules/opus/ui/components/Slider.lua b/sys/modules/opus/ui/components/Slider.lua index 09325b4..9a58cdb 100644 --- a/sys/modules/opus/ui/components/Slider.lua +++ b/sys/modules/opus/ui/components/Slider.lua @@ -49,7 +49,7 @@ function UI.Slider:draw() i == self.width and self.rightBorder or self.barChar - table.insert(bar, filler) + table.insert(bar, filler) end self:write(1, 1, table.concat(bar), nil, self.barColor) self:write(progress, 1, self.sliderChar, nil, self.focused and self.sliderFocusColor or self.sliderColor) @@ -80,6 +80,5 @@ function UI.Slider.example() return UI.Slider { y = 2, x = 2, ex = -2, min = 0, max = 1, - help = 'Volume setting', } end diff --git a/sys/modules/opus/ui/components/StatusBar.lua b/sys/modules/opus/ui/components/StatusBar.lua index a92c0c2..96a1943 100644 --- a/sys/modules/opus/ui/components/StatusBar.lua +++ b/sys/modules/opus/ui/components/StatusBar.lua @@ -96,3 +96,20 @@ function UI.StatusBar:draw() self:write(1, 1, Util.widthify(s, self.width)) end end + +function UI.StatusBar.example() + return UI.Window { + status1 = UI.StatusBar { values = 'standard' }, + status2 = UI.StatusBar { + ey = -3, + columns = { + { key = 'field1' }, + { key = 'field2', width = 6 }, + }, + values = { + field1 = 'test', + field2 = '42', + } + } + } +end diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 75665a6..523208c 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -117,9 +117,14 @@ function UI.TextEntry:eventHandler(event) self.entry.value = text if event.ie and self.entry:process(event.ie) then if self.entry.textChanged then + local changed = self.value ~= self.entry.value self.value = self.entry.value self:draw() - self:emit({ type = 'text_change', text = self.value, element = self }) + if changed then + -- we get entry.textChanged when marking is updated + -- no need to emit in that case + self:emit({ type = 'text_change', text = self.value, element = self }) + end elseif self.entry.posChanged then self:updateCursor() end diff --git a/sys/modules/opus/ui/components/Wizard.lua b/sys/modules/opus/ui/components/Wizard.lua index e6bce7e..549669e 100644 --- a/sys/modules/opus/ui/components/Wizard.lua +++ b/sys/modules/opus/ui/components/Wizard.lua @@ -122,3 +122,34 @@ function UI.Wizard:eventHandler(event) self:draw() end end + +function UI.Wizard.example() + return UI.Wizard { + ey = -2, + pages = { + splash = UI.WizardPage { + index = 1, + intro = UI.TextArea { + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = 'sample text', + }, + }, + label = UI.WizardPage { + index = 2, + intro = UI.TextArea { + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = 'sample more text', + }, + }, + password = UI.WizardPage { + index = 3, + text = UI.TextEntry { + x = 12, ex = -3, y = 2, + shadowText = 'tet', + }, + }, + }, + } +end -- 2.49.1 From efa1a5bbf5b5a8bed1ff216e51d75185fc5b8bbe Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 15 Nov 2019 12:51:44 -0700 Subject: [PATCH 103/236] clipping for transistions + tab ordering via index + more examples --- sys/apps/Tasks.lua | 50 +++++++++---------- sys/apps/inspect.lua | 35 ++++++------- sys/autorun/complete.lua | 13 +++++ sys/modules/opus/ui/canvas.lua | 6 +++ sys/modules/opus/ui/components/Form.lua | 4 +- sys/modules/opus/ui/components/Menu.lua | 11 ++++ sys/modules/opus/ui/components/MenuBar.lua | 9 ++++ .../opus/ui/components/ProgressBar.lua | 16 ++++++ sys/modules/opus/ui/components/SlideOut.lua | 19 ++++--- sys/modules/opus/ui/components/Tabs.lua | 16 +++--- .../opus/ui/components/VerticalMeter.lua | 16 ++++++ 11 files changed, 138 insertions(+), 57 deletions(-) diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index f67d71b..3dbd395 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -24,40 +24,38 @@ local page = UI.Page { values = kernel.routines, sortColumn = 'uid', autospace = true, + getDisplayValues = function(_, row) + local elapsed = os.clock()-row.timestamp + return { + uid = row.uid, + title = row.title, + status = row.isDead and 'error' or coroutine.status(row.co), + timestamp = elapsed < 60 and + string.format("%ds", math.floor(elapsed)) or + string.format("%sm", math.floor(elapsed/6)/10), + } + end }, accelerators = { [ 'control-q' ] = 'quit', space = 'activate', t = 'terminate', }, -} - -function page:eventHandler(event) - local t = self.grid:getSelected() - if t then - if event.type == 'activate' or event.type == 'grid_select' then - multishell.setFocus(t.uid) - elseif event.type == 'terminate' then - multishell.terminate(t.uid) + eventHandler = function (self, event) + local t = self.grid:getSelected() + if t then + if event.type == 'activate' or event.type == 'grid_select' then + multishell.setFocus(t.uid) + elseif event.type == 'terminate' then + multishell.terminate(t.uid) + end end + if event.type == 'quit' then + Event.exitPullEvents() + end + UI.Page.eventHandler(self, event) end - if event.type == 'quit' then - Event.exitPullEvents() - end - UI.Page.eventHandler(self, event) -end - -function page.grid:getDisplayValues(row) - local elapsed = os.clock()-row.timestamp - return { - uid = row.uid, - title = row.title, - status = row.isDead and 'error' or coroutine.status(row.co), - timestamp = elapsed < 60 and - string.format("%ds", math.floor(elapsed)) or - string.format("%sm", math.floor(elapsed/6)/10), - } -end +} Event.onInterval(1, function() page.grid:update() diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 48feeb4..6cc4048 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -63,6 +63,7 @@ page = UI.Page { }, }, methodsTab = UI.Tab { + index = 2, tabTitle = 'Methods', grid = UI.ScrollingGrid { headerBackgroundColor = colors.red, @@ -73,11 +74,12 @@ page = UI.Page { }, }, events = UI.Tab { + index = 1, tabTitle = 'Events', UI.MenuBar { y = -1, buttons = { - { text = 'Clear', event = 'event_clear' }, + { text = 'Clear' }, } }, grid = UI.ScrollingGrid { @@ -89,10 +91,21 @@ page = UI.Page { { heading = 'type', key = 'type' }, { heading = 'value', key = 'value', } }, - accelerators = { - grid_select = 'event_inspect', - }, - } + }, + eventHandler = function (self, event) + if event.type == 'button_press' then + Util.clear(self.grid.values) + self.grid:update() + self.grid:draw() + + elseif event.type == 'grid_select' then + multishell.openTab({ + path = 'sys/apps/Lua.lua', + args = { event.selected.raw }, + focused = true, + }) + end + end } }, editor = UI.SlideOut { @@ -134,18 +147,6 @@ page = UI.Page { self.tabs.methodsTab.grid:update() self.tabs.methodsTab.grid:draw() - elseif event.type == 'event_clear' then - Util.clear(self.tabs.events.grid.values) - self.tabs.events.grid:update() - self.tabs.events.grid:draw() - - elseif event.type == 'event_inspect' then - multishell.openTab({ - path = 'sys/apps/Lua.lua', - args = { event.selected.raw }, - focused = true, - }) - elseif event.type == 'edit_property' then self.editor.entry.value = event.selected.value self.editor:show() diff --git a/sys/autorun/complete.lua b/sys/autorun/complete.lua index 31a6bd9..584fd97 100644 --- a/sys/autorun/complete.lua +++ b/sys/autorun/complete.lua @@ -1,3 +1,5 @@ +local fs = _G.fs + local function completeMultipleChoice(sText, tOptions, bAddSpaces) local tResults = { } for n = 1,#tOptions do @@ -20,3 +22,14 @@ _ENV.shell.setCompletionFunction("sys/apps/package.lua", return completeMultipleChoice(text, { "install ", "update ", "uninstall ", "updateall ", "refresh" }) end end) + +_ENV.shell.setCompletionFunction("sys/apps/inspect.lua", + function(_, index, text) + if index == 1 then + local components = { } + for _, f in pairs(fs.list('sys/modules/opus/ui/components')) do + table.insert(components, (f:gsub("%.lua$", ""))) + end + return completeMultipleChoice(text, components) + end + end) diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index 75e1792..d0d4ee9 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -353,6 +353,12 @@ function Canvas:__renderLayers(device, offset) end function Canvas:__blitClipped(device, offset) + if self.parent then + -- contain the rendered region in the parent's region + local p = Region.new(1, 1, self.parent.width, self.parent.height) + self.regions:andRegion(p) + end + for _,region in ipairs(self.regions.region) do self:__blitRect(device, { x = region[1] - offset.x, diff --git a/sys/modules/opus/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua index 69cb7ca..bda9850 100644 --- a/sys/modules/opus/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -139,7 +139,7 @@ function UI.Form.example() return UI.Form { x = 2, ex = -2, y = 2, ptype = UI.Chooser { - formLabel = 'Type', formKey = 'type', + formLabel = 'Type', formKey = 'type', formIndex = 1, width = 10, choices = { { name = 'Modem', value = 'wireless_modem' }, @@ -147,7 +147,7 @@ function UI.Form.example() }, }, drive_id = UI.TextEntry { - formLabel = 'Drive', formKey = 'drive_id', + formLabel = 'Drive', formKey = 'drive_id', formIndex = 2, required = true, width = 5, transform = 'number', diff --git a/sys/modules/opus/ui/components/Menu.lua b/sys/modules/opus/ui/components/Menu.lua index e31e8df..8f1c837 100644 --- a/sys/modules/opus/ui/components/Menu.lua +++ b/sys/modules/opus/ui/components/Menu.lua @@ -59,3 +59,14 @@ function UI.Menu:eventHandler(event) end return UI.Grid.eventHandler(self, event) end + +function UI.Menu.example() + return UI.Menu { + x = 2, y = 2, height = 3, + menuItems = { + { prompt = 'Start', event = 'start' }, + { prompt = 'Continue', event = 'continue' }, + { prompt = 'Quit', event = 'quit' } + } + } +end diff --git a/sys/modules/opus/ui/components/MenuBar.lua b/sys/modules/opus/ui/components/MenuBar.lua index 85f435b..217fccd 100644 --- a/sys/modules/opus/ui/components/MenuBar.lua +++ b/sys/modules/opus/ui/components/MenuBar.lua @@ -34,6 +34,15 @@ function UI.MenuBar:addButtons(buttons) self.children = { } end + for _,button in pairs(buttons) do + if button.index then -- don't sort unless needed + table.sort(buttons, function(a, b) + return (a.index or 999) < (b.index or 999) + end) + break + end + end + for _,button in pairs(buttons) do if button.UIElement then table.insert(self.children, button) diff --git a/sys/modules/opus/ui/components/ProgressBar.lua b/sys/modules/opus/ui/components/ProgressBar.lua index af12708..a066952 100644 --- a/sys/modules/opus/ui/components/ProgressBar.lua +++ b/sys/modules/opus/ui/components/ProgressBar.lua @@ -26,3 +26,19 @@ function UI.ProgressBar:draw() self:write(1, i, progress, self.progressColor) end end + +function UI.ProgressBar.example() + local Event = require('opus.event') + return UI.ProgressBar { + x = 2, ex = -2, y = 2, + focus = function() end, + enable = function(self) + Event.onInterval(.25, function() + self.value = self.value == 100 and 0 or self.value + 5 + self:draw() + self:sync() + end) + return UI.ProgressBar.enable(self) + end + } +end diff --git a/sys/modules/opus/ui/components/SlideOut.lua b/sys/modules/opus/ui/components/SlideOut.lua index c5779dd..1e9558f 100644 --- a/sys/modules/opus/ui/components/SlideOut.lua +++ b/sys/modules/opus/ui/components/SlideOut.lua @@ -19,6 +19,14 @@ end function UI.SlideOut:enable() end +function UI.SlideOut:toggle() + if self.enabled then + self:hide() + else + self:show() + end +end + function UI.SlideOut:show(...) self:addTransition('expandUp') self.canvas:raise() @@ -52,7 +60,9 @@ function UI.SlideOut:eventHandler(event) end function UI.SlideOut.example() - return UI.Window { + -- for the transistion to work properly, the parent must have a canvas + return UI.ActiveLayer { + backgroundColor = colors.cyan, button = UI.Button { x = 2, y = 2, text = 'show', @@ -67,11 +77,8 @@ function UI.SlideOut.example() }, eventHandler = function (self, event) if event.type == 'button_press' then - if self.slideOut.enabled then - self.slideOut:hide() - else - self.slideOut:show() - end + self.slideOut.canvas.xxx = true + self.slideOut:toggle() end end, } diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index a27bcbb..c9c0075 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -16,6 +16,7 @@ function UI.Tabs:add(children) if type(child) == 'table' and child.UIElement and child.tabTitle then child.y = 2 table.insert(buttons, { + index = child.index, text = child.tabTitle, event = 'tab_select', tabUid = child.uid, @@ -32,7 +33,7 @@ function UI.Tabs:add(children) end if self.parent then - return UI.Window.add(self, children) + UI.Window.add(self, children) end end @@ -57,7 +58,7 @@ function UI.Tabs:enable() local menuItem = Util.find(self.tabBar.children, 'selected', true) - for _,child in pairs(self.children) do + for _,child in pairs(self.children or { }) do if child.uid == menuItem.tabUid then child:enable() self:emit({ type = 'tab_activate', activated = child }) @@ -90,16 +91,19 @@ end function UI.Tabs.example() return UI.Tabs { - [1] = UI.Tab { + tab1 = UI.Tab { + index = 1, tabTitle = 'tab1', entry = UI.TextEntry { y = 3, shadowText = 'text' }, }, - [2] = UI.Tab { + tab2 = UI.Tab { + index = 2, tabTitle = 'tab2', button = UI.Button { y = 3 }, }, - [3] = UI.Tab { + tab3 = UI.Tab { + index = 3, tabTitle = 'tab3', - } + }, } end diff --git a/sys/modules/opus/ui/components/VerticalMeter.lua b/sys/modules/opus/ui/components/VerticalMeter.lua index 012d0e5..051d740 100644 --- a/sys/modules/opus/ui/components/VerticalMeter.lua +++ b/sys/modules/opus/ui/components/VerticalMeter.lua @@ -16,3 +16,19 @@ function UI.VerticalMeter:draw() self:clear() self:clearArea(1, height + 1, self.width, self.height, self.meterColor) end + +function UI.VerticalMeter.example() + local Event = require('opus.event') + return UI.VerticalMeter { + x = 2, width = 3, y = 2, ey = -2, + focus = function() end, + enable = function(self) + Event.onInterval(.25, function() + self.value = self.value == 100 and 0 or self.value + 5 + self:draw() + self:sync() + end) + return UI.VerticalMeter.enable(self) + end + } +end \ No newline at end of file -- 2.49.1 From a3a8c64be8d73ef0d7c65c6ce37b3696d6af6c2b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 16 Nov 2019 22:12:02 -0700 Subject: [PATCH 104/236] UI docs --- sys/apps/inspect.lua | 20 ++++-- sys/modules/opus/ui.lua | 68 +++++++++++++++++-- sys/modules/opus/ui/canvas.lua | 4 +- sys/modules/opus/ui/components/Checkbox.lua | 32 +++++---- .../opus/ui/components/DropMenuItem.lua | 1 - sys/modules/opus/ui/components/Menu.lua | 1 - sys/modules/opus/ui/components/MenuBar.lua | 1 + sys/modules/opus/ui/components/MenuItem.lua | 1 - .../opus/ui/components/Notification.lua | 13 ++-- .../opus/ui/components/ScrollingGrid.lua | 1 - sys/modules/opus/ui/components/SlideOut.lua | 6 +- sys/modules/opus/ui/components/Tab.lua | 3 - sys/modules/opus/ui/components/TabBar.lua | 6 +- .../opus/ui/components/TabBarMenuItem.lua | 4 +- sys/modules/opus/ui/components/Tabs.lua | 3 + sys/modules/opus/ui/components/TextArea.lua | 1 - sys/modules/opus/ui/components/TextEntry.lua | 3 + sys/modules/opus/ui/components/Viewport.lua | 1 - 18 files changed, 123 insertions(+), 46 deletions(-) diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 6cc4048..1d4f95b 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -6,8 +6,7 @@ local multishell = _ENV.multishell local name = ({ ... })[1] or error('Syntax: inspect COMPONENT') local events = { } -local page -local lastEvent +local page, lastEvent, focused local function isRelevant(el) return page.testContainer == el or el.parent and isRelevant(el.parent) @@ -43,11 +42,12 @@ end page = UI.Page { testContainer = UI.Window { - ey = 10, + ey = '50%', testing = component.example(), }, tabs = UI.Tabs { - y = 11, + backgroundColor = colors.red, + y = '50%', properties = UI.Tab { tabTitle = 'Properties', grid = UI.ScrollingGrid { @@ -66,18 +66,29 @@ page = UI.Page { index = 2, tabTitle = 'Methods', grid = UI.ScrollingGrid { + ex = '50%', headerBackgroundColor = colors.red, sortColumn = 'key', columns = { { heading = 'key', key = 'key' }, }, }, + docs = UI.TextArea { + x = '50%', + backgroundColor = colors.black, + }, + eventHandler = function (self, event) + if event.type == 'grid_focus_row' and focused then + self.docs:setText(focused:getDoc(event.selected.key) or '') + end + end, }, events = UI.Tab { index = 1, tabTitle = 'Events', UI.MenuBar { y = -1, + backgroundColor = colors.red, buttons = { { text = 'Clear' }, } @@ -124,6 +135,7 @@ page = UI.Page { }, eventHandler = function (self, event) if event.type == 'focus_change' and isRelevant(event.focused) then + focused = event.focused local t = { } for k,v in pairs(event.focused) do table.insert(t, { diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 5686f93..6e464ad 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -398,6 +398,7 @@ local UI = Manager() --[[-- Basic drawable area --]]-- UI.Window = class() UI.Window.uid = 1 +UI.Window.docs = { } UI.Window.defaults = { UIElement = 'Window', x = 1, @@ -482,13 +483,14 @@ function UI.Window:layout() end if type(self.x) == 'string' then - self.x = calc(self.x, self.parent.width) + self.x = calc(self.x, self.parent.width) + 1 + -- +1 in order to allow both x and ex to use the same % end if type(self.ex) == 'string' then self.ex = calc(self.ex, self.parent.width) end if type(self.y) == 'string' then - self.y = calc(self.y, self.parent.height) + self.y = calc(self.y, self.parent.height) + 1 end if type(self.ey) == 'string' then self.ey = calc(self.ey, self.parent.height) @@ -539,6 +541,22 @@ function UI.Window:setParent() self:layout() + -- Experimental + -- Inherit properties from the parent container + -- does this need to be in reverse order ? + local m = getmetatable(self) -- get the class for this instance + repeat + if m.inherits then + for k, v in pairs(m.inherits) do + local value = self.parent:getProperty(v) + if value then + self[k] = value + end + end + end + m = m._base + until not m + self:initChildren() end @@ -555,6 +573,13 @@ function UI.Window:resize() end end +UI.Window.docs.add = [[add(TABLE) +Add element(s) to a window. Example: +page:add({ + text = UI.Text { + x=5,value='help' + } +})]] function UI.Window:add(children) UI:mergeProperties(self, children) self:initChildren() @@ -574,6 +599,8 @@ function UI.Window:setCursorBlink(blink) self.parent:setCursorBlink(blink) end +UI.Window.docs.draw = [[draw(VOID) +Redraws the window in the internal buffer.]] function UI.Window:draw() self:clear(self.backgroundColor) if self.children then @@ -585,6 +612,21 @@ function UI.Window:draw() end end +UI.Window.docs.getDoc = [[getDoc(STRING method) +Gets the documentation for a method.]] +function UI.Window:getDoc(method) + local m = getmetatable(self) -- get the class for this instance + repeat + if m.docs and m.docs[method] then + return m.docs[method] + end + m = m._base + until not m +end + +UI.Window.docs.sync = [[sync(VOID) +Invoke a screen update. Automatically called at top level after an input event. +Call to force a screen update.]] function UI.Window:sync() if self.parent then self.parent:sync() @@ -614,9 +656,11 @@ function UI.Window:setTextScale(textScale) self.parent:setTextScale(textScale) end +UI.Window.docs.clear = [[clear(opt COLOR bg, opt COLOR fg) +Clears the window using the either the passed values or the defaults for that window.]] function UI.Window:clear(bg, fg) if self.canvas then - self.canvas:clear(bg or self.backgroundColor, fg or self.textColor) + self.canvas:clear(bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) else self:clearArea(1 + self.offx, 1 + self.offy, self.width, self.height, bg) end @@ -635,18 +679,18 @@ function UI.Window:clearArea(x, y, width, height, bg) end end -function UI.Window:write(x, y, text, bg, tc) +function UI.Window:write(x, y, text, bg, fg) bg = bg or self.backgroundColor - tc = tc or self.textColor + fg = fg or self.textColor if self.canvas then - self.canvas:write(x, y, text, bg, tc) + self.canvas:write(x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) else x = x - self.offx y = y - self.offy if y <= self.height and y > 0 then self.parent:write( - self.x + x - 1, self.y + y - 1, tostring(text), bg, tc) + self.x + x - 1, self.y + y - 1, tostring(text), bg, fg) end end end @@ -752,12 +796,20 @@ function UI.Window:print(text, bg, fg) return self.cursorX, self.cursorY end +UI.Window.docs.focus = [[focus(VOID) +If the function is present on a class, it indicates +that this element can accept focus. Called when receiving focus.]] + +UI.Window.docs.setFocus = [[setFocus(ELEMENT el) +Set the page's focus to the passed element.]] function UI.Window:setFocus(focus) if self.parent then self.parent:setFocus(focus) end end +UI.Window.docs.capture = [[capture(ELEMENT el) +Restricts input to the passed element's tree.]] function UI.Window:capture(child) if self.parent then self.parent:capture(child) @@ -793,6 +845,8 @@ function UI.Window:pointToChild(x, y) } end +UI.Window.docs.getFocusables = [[getFocusables(VOID) +Returns a list of children that can accept focus.]] function UI.Window:getFocusables() local focusable = { } diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index d0d4ee9..cb46ede 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -355,7 +355,9 @@ end function Canvas:__blitClipped(device, offset) if self.parent then -- contain the rendered region in the parent's region - local p = Region.new(1, 1, self.parent.width, self.parent.height) + local p = Region.new(1, 1, + self.parent.width + offset.x - self.x + 1, + self.parent.height + offset.y - self.y + 1) self.regions:andRegion(p) end diff --git a/sys/modules/opus/ui/components/Checkbox.lua b/sys/modules/opus/ui/components/Checkbox.lua index 54343e5..567833e 100644 --- a/sys/modules/opus/ui/components/Checkbox.lua +++ b/sys/modules/opus/ui/components/Checkbox.lua @@ -21,21 +21,20 @@ UI.Checkbox.defaults = { mouse_click = 'checkbox_toggle', } } +UI.Checkbox.inherits = { + labelBackgroundColor = 'backgroundColor', +} +function UI.Checkbox:postInit() + self.width = self.label and #self.label + 4 or 3 +end + function UI.Checkbox:draw() - local bg = self.backgroundColor - if self.focused then - bg = self.backgroundFocusColor - end - if type(self.value) == 'string' then - self.value = nil -- TODO: fix form - end - local text = string.format('[%s]', not self.value and ' ' or self.checkedIndicator) + local bg = self.focused and self.backgroundFocusColor or self.backgroundColor local x = 1 if self.label then - self:write(1, 1, self.label) + self:write(1, 1, self.label, self.labelBackgroundColor) x = #self.label + 2 end - self:write(x, 1, text, bg) self:write(x, 1, self.leftMarker, self.backgroundColor, self.textColor) self:write(x + 1, 1, not self.value and ' ' or self.checkedIndicator, bg) self:write(x + 2, 1, self.rightMarker, self.backgroundColor, self.textColor) @@ -46,11 +45,12 @@ function UI.Checkbox:focus() end function UI.Checkbox:setValue(v) - self.value = v + self.value = not not v end function UI.Checkbox:reset() self.value = false + self:draw() end function UI.Checkbox:eventHandler(event) @@ -63,7 +63,13 @@ function UI.Checkbox:eventHandler(event) end function UI.Checkbox.example() - return UI.Checkbox { - x = 2, y = 2, + return UI.Window { + ex1 = UI.Checkbox { + label = 'test', + x = 2, y = 2, + }, + ex2 = UI.Checkbox { + x = 2, y = 4, + }, } end diff --git a/sys/modules/opus/ui/components/DropMenuItem.lua b/sys/modules/opus/ui/components/DropMenuItem.lua index 09263da..ff28047 100644 --- a/sys/modules/opus/ui/components/DropMenuItem.lua +++ b/sys/modules/opus/ui/components/DropMenuItem.lua @@ -3,7 +3,6 @@ local UI = require('opus.ui') local colors = _G.colors ---[[-- DropMenuItem --]]-- UI.DropMenuItem = class(UI.Button) UI.DropMenuItem.defaults = { UIElement = 'DropMenuItem', diff --git a/sys/modules/opus/ui/components/Menu.lua b/sys/modules/opus/ui/components/Menu.lua index 8f1c837..0e18682 100644 --- a/sys/modules/opus/ui/components/Menu.lua +++ b/sys/modules/opus/ui/components/Menu.lua @@ -1,7 +1,6 @@ local class = require('opus.class') local UI = require('opus.ui') ---[[-- Menu --]]-- UI.Menu = class(UI.Grid) UI.Menu.defaults = { UIElement = 'Menu', diff --git a/sys/modules/opus/ui/components/MenuBar.lua b/sys/modules/opus/ui/components/MenuBar.lua index 217fccd..1b544cc 100644 --- a/sys/modules/opus/ui/components/MenuBar.lua +++ b/sys/modules/opus/ui/components/MenuBar.lua @@ -51,6 +51,7 @@ function UI.MenuBar:addButtons(buttons) x = self.lastx, width = #(button.text or 'button') + self.spacing, centered = false, + backgroundColor = self.backgroundColor, } self.lastx = self.lastx + buttonProperties.width UI:mergeProperties(buttonProperties, button) diff --git a/sys/modules/opus/ui/components/MenuItem.lua b/sys/modules/opus/ui/components/MenuItem.lua index 2f0efe8..61bc0b1 100644 --- a/sys/modules/opus/ui/components/MenuItem.lua +++ b/sys/modules/opus/ui/components/MenuItem.lua @@ -3,7 +3,6 @@ local UI = require('opus.ui') local colors = _G.colors ---[[-- MenuItem --]]-- UI.MenuItem = class(UI.Button) UI.MenuItem.defaults = { UIElement = 'MenuItem', diff --git a/sys/modules/opus/ui/components/Notification.lua b/sys/modules/opus/ui/components/Notification.lua index 7b11543..701733e 100644 --- a/sys/modules/opus/ui/components/Notification.lua +++ b/sys/modules/opus/ui/components/Notification.lua @@ -92,25 +92,26 @@ function UI.Notification:eventHandler(event) end function UI.Notification.example() - return UI.Window { - notify = UI.Notification { + return UI.ActiveLayer { + notify1 = UI.Notification { anchor = 'top', }, + notify2 = UI.Notification { }, button1 = UI.Button { x = 2, y = 3, - text = 'success', + text = 'example 1', event = 'test_success', }, button2 = UI.Button { x = 2, y = 5, - text = 'error', + text = 'example 2', event = 'test_error', }, eventHandler = function (self, event) if event.type == 'test_success' then - self.notify:success('Example text') + self.notify1:success('Example text') elseif event.type == 'test_error' then - self.notify:error('Example text', 0) + self.notify2:error('Example text', 0) end end, } diff --git a/sys/modules/opus/ui/components/ScrollingGrid.lua b/sys/modules/opus/ui/components/ScrollingGrid.lua index 93a35e6..06dd5b8 100644 --- a/sys/modules/opus/ui/components/ScrollingGrid.lua +++ b/sys/modules/opus/ui/components/ScrollingGrid.lua @@ -2,7 +2,6 @@ local class = require('opus.class') local UI = require('opus.ui') local Util = require('opus.util') ---[[-- ScrollingGrid --]]-- UI.ScrollingGrid = class(UI.Grid) UI.ScrollingGrid.defaults = { UIElement = 'ScrollingGrid', diff --git a/sys/modules/opus/ui/components/SlideOut.lua b/sys/modules/opus/ui/components/SlideOut.lua index 1e9558f..789220f 100644 --- a/sys/modules/opus/ui/components/SlideOut.lua +++ b/sys/modules/opus/ui/components/SlideOut.lua @@ -1,7 +1,6 @@ local class = require('opus.class') local UI = require('opus.ui') ---[[-- SlideOut --]]-- UI.SlideOut = class(UI.Window) UI.SlideOut.defaults = { UIElement = 'SlideOut', @@ -62,9 +61,12 @@ end function UI.SlideOut.example() -- for the transistion to work properly, the parent must have a canvas return UI.ActiveLayer { + y = 1, -- TODO: if this is set to anything greater than 1, then + -- the layer is not rendered in the correct location + -- a general issue in canvas layers backgroundColor = colors.cyan, button = UI.Button { - x = 2, y = 2, + x = 2, y = 5, text = 'show', }, slideOut = UI.SlideOut { diff --git a/sys/modules/opus/ui/components/Tab.lua b/sys/modules/opus/ui/components/Tab.lua index 2f86b2f..1e31de6 100644 --- a/sys/modules/opus/ui/components/Tab.lua +++ b/sys/modules/opus/ui/components/Tab.lua @@ -1,12 +1,9 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.Tab = class(UI.ActiveLayer) UI.Tab.defaults = { UIElement = 'Tab', tabTitle = 'tab', - backgroundColor = colors.cyan, y = 2, } diff --git a/sys/modules/opus/ui/components/TabBar.lua b/sys/modules/opus/ui/components/TabBar.lua index 72e07f6..e684cae 100644 --- a/sys/modules/opus/ui/components/TabBar.lua +++ b/sys/modules/opus/ui/components/TabBar.lua @@ -2,13 +2,13 @@ local class = require('opus.class') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors - UI.TabBar = class(UI.MenuBar) UI.TabBar.defaults = { UIElement = 'TabBar', buttonClass = 'TabBarMenuItem', - selectedBackgroundColor = colors.cyan, +} +UI.TabBar.inherits = { + selectedBackgroundColor = 'backgroundColor', } function UI.TabBar:enable() UI.MenuBar.enable(self) diff --git a/sys/modules/opus/ui/components/TabBarMenuItem.lua b/sys/modules/opus/ui/components/TabBarMenuItem.lua index 0a7799c..f9f549b 100644 --- a/sys/modules/opus/ui/components/TabBarMenuItem.lua +++ b/sys/modules/opus/ui/components/TabBarMenuItem.lua @@ -3,7 +3,6 @@ local UI = require('opus.ui') local colors = _G.colors ---[[-- TabBarMenuItem --]]-- UI.TabBarMenuItem = class(UI.Button) UI.TabBarMenuItem.defaults = { UIElement = 'TabBarMenuItem', @@ -13,6 +12,9 @@ UI.TabBarMenuItem.defaults = { unselectedBackgroundColor = colors.lightGray, backgroundColor = colors.lightGray, } +UI.TabBarMenuItem.inherits = { + selectedBackgroundColor = 'selectedBackgroundColor', +} function UI.TabBarMenuItem:draw() if self.selected then self.backgroundColor = self.selectedBackgroundColor diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index c9c0075..2b0c824 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -3,6 +3,7 @@ local UI = require('opus.ui') local Util = require('opus.util') UI.Tabs = class(UI.Window) +UI.Tabs.docs = { } UI.Tabs.defaults = { UIElement = 'Tabs', } @@ -37,6 +38,8 @@ function UI.Tabs:add(children) end end +UI.Tabs.docs.selectTab = [[selectTab(TAB) +Make to the passed tab active.]] function UI.Tabs:selectTab(tab) local menuItem = Util.find(self.tabBar.children, 'tabUid', tab.uid) if menuItem then diff --git a/sys/modules/opus/ui/components/TextArea.lua b/sys/modules/opus/ui/components/TextArea.lua index ea7540f..2b8c342 100644 --- a/sys/modules/opus/ui/components/TextArea.lua +++ b/sys/modules/opus/ui/components/TextArea.lua @@ -1,7 +1,6 @@ local class = require('opus.class') local UI = require('opus.ui') ---[[-- TextArea --]]-- UI.TextArea = class(UI.Viewport) UI.TextArea.defaults = { UIElement = 'TextArea', diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 523208c..929cb3c 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -16,6 +16,7 @@ local function transform(directive) end UI.TextEntry = class(UI.Window) +UI.TextEntry.docs = { } UI.TextEntry.defaults = { UIElement = 'TextEntry', --value = '', @@ -92,6 +93,8 @@ function UI.TextEntry:draw() end end +UI.TextEntry.docs.reset = [[reset() +Clears the value and resets the cursor.]] function UI.TextEntry:reset() self.entry:reset() self.value = nil--'' diff --git a/sys/modules/opus/ui/components/Viewport.lua b/sys/modules/opus/ui/components/Viewport.lua index acb84cd..35cd0eb 100644 --- a/sys/modules/opus/ui/components/Viewport.lua +++ b/sys/modules/opus/ui/components/Viewport.lua @@ -3,7 +3,6 @@ local UI = require('opus.ui') local colors = _G.colors ---[[-- Viewport --]]-- UI.Viewport = class(UI.Window) UI.Viewport.defaults = { UIElement = 'Viewport', -- 2.49.1 From ffa412c59d158fcc2d0c7c42a0c7b569f37de5c0 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 18 Nov 2019 14:32:10 -0700 Subject: [PATCH 105/236] ui fixes --- sys/apps/Tasks.lua | 2 +- sys/apps/Welcome.lua | 44 ++--- sys/modules/opus/ui.lua | 170 ++------------------ sys/modules/opus/ui/canvas.lua | 37 +++-- sys/modules/opus/ui/components/Chooser.lua | 70 ++++---- sys/modules/opus/ui/components/Page.lua | 163 +++++++++++++++++++ sys/modules/opus/ui/components/SlideOut.lua | 9 +- sys/modules/opus/ui/transition.lua | 2 +- 8 files changed, 245 insertions(+), 252 deletions(-) create mode 100644 sys/modules/opus/ui/components/Page.lua diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index 3dbd395..cde082a 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -24,7 +24,7 @@ local page = UI.Page { values = kernel.routines, sortColumn = 'uid', autospace = true, - getDisplayValues = function(_, row) + getDisplayValues = function (_, row) local elapsed = os.clock()-row.timestamp return { uid = row.uid, diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index a25278f..980f27f 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -60,6 +60,12 @@ local page = UI.Page { x = 3, ex = -3, y = 4, ey = -3, value = string.format(labelIntro, Ansi.white), }, + validate = function (self) + if self.label.value then + os.setComputerLabel(self.label.value) + end + return true + end, }, password = UI.WizardPage { index = 3, @@ -73,23 +79,18 @@ local page = UI.Page { mask = true, shadowText = 'password', }, ---[[ - groupLabel = UI.Text { - x = 3, y = 3, - value = 'Group' - }, - group = UI.TextEntry { - x = 12, ex = -3, y = 3, - limit = 32, - shadowText = 'network group', - }, -]] intro = UI.TextArea { textColor = colors.yellow, inactive = true, x = 3, ex = -3, y = 5, ey = -3, value = string.format(passwordIntro, Ansi.white), }, + validate = function (self) + if type(self.newPass.value) == "string" and #self.newPass.value > 0 then + Security.updatePassword(SHA.compute(self.newPass.value)) + end + return true + end, }, packages = UI.WizardPage { index = 4, @@ -119,27 +120,6 @@ local page = UI.Page { notification = UI.Notification { }, } -function page.wizard.pages.label:validate() - if self.label.value then - os.setComputerLabel(self.label.value) - end - return true -end - -function page.wizard.pages.password:validate() - if type(self.newPass.value) == "string" and #self.newPass.value > 0 then - Security.updatePassword(SHA.compute(self.newPass.value)) - end - --[[ - if #self.group.value > 0 then - local config = Config.load('os') - config.group = self.group.value - Config.update('os', config) - end - ]] - return true -end - function page:eventHandler(event) if event.type == 'skip' then self.wizard:emit({ type = 'nextView' }) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 6e464ad..4ff0bea 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -1,4 +1,3 @@ -local Canvas = require('opus.ui.canvas') local class = require('opus.class') local Event = require('opus.event') local Input = require('opus.input') @@ -19,18 +18,18 @@ local textutils = _G.textutils the bottom up. Once reaching the top, setParent is called top down. On :init(), elements do not know the parent or can calculate sizing. -]] --- need to add offsets to this test -local function getPosition(element) - local x, y = 1, 1 - repeat - x = element.x + x - 1 - y = element.y + y - 1 - element = element.parent - until not element - return x, y -end + Calling order: + window:postInit() + at this point, the window has all default values set + window:setParent() + parent has been assigned + following are called: + window:layout() + sizing / positioning is performed + window:initChildren() + each child of window will get initialized +]] --[[-- Top Level Manager --]]-- local Manager = class() @@ -953,7 +952,7 @@ function UI.Window:addTransition(effect, args) if self.parent then args = args or { } if not args.x then -- not good - args.x, args.y = getPosition(self) + args.x, args.y = self.x, self.y -- getPosition(self) args.width = self.width args.height = self.height end @@ -1117,149 +1116,7 @@ function UI.Device:sync() end end ---[[-- Page (focus manager) --]]-- -UI.Page = class(UI.Window) -UI.Page.defaults = { - UIElement = 'Page', - accelerators = { - down = 'focus_next', - enter = 'focus_next', - tab = 'focus_next', - ['shift-tab' ] = 'focus_prev', - up = 'focus_prev', - }, - backgroundColor = colors.cyan, - textColor = colors.white, -} -function UI.Page:postInit() - self.parent = self.parent or UI.defaultDevice - self.__target = self - self.canvas = Canvas({ - x = 1, y = 1, width = self.parent.width, height = self.parent.height, - isColor = self.parent.isColor, - }) - self.canvas:clear(self.backgroundColor, self.textColor) -end - -function UI.Page:enable() - self.canvas.visible = true - UI.Window.enable(self) - - if not self.focused or not self.focused.enabled then - self:focusFirst() - end -end - -function UI.Page:disable() - self.canvas.visible = false - UI.Window.disable(self) -end - -function UI.Page:sync() - if self.enabled then - self.parent:sync() - end -end - -function UI.Page:capture(child) - self.__target = child -end - -function UI.Page:release(child) - if self.__target == child then - self.__target = self - end -end - -function UI.Page:pointToChild(x, y) - if self.__target == self then - return UI.Window.pointToChild(self, x, y) - end - x = x + self.offx - self.x + 1 - y = y + self.offy - self.y + 1 - return self.__target:pointToChild(x, y) -end - -function UI.Page:getFocusables() - if self.__target == self or self.__target.pageType ~= 'modal' then - return UI.Window.getFocusables(self) - end - return self.__target:getFocusables() -end - -function UI.Page:getFocused() - return self.focused -end - -function UI.Page:focusPrevious() - local function getPreviousFocus(focused) - local focusables = self:getFocusables() - local k = Util.contains(focusables, focused) - if k then - if k > 1 then - return focusables[k - 1] - end - return focusables[#focusables] - end - end - - local focused = getPreviousFocus(self.focused) - if focused then - self:setFocus(focused) - end -end - -function UI.Page:focusNext() - local function getNextFocus(focused) - local focusables = self:getFocusables() - local k = Util.contains(focusables, focused) - if k then - if k < #focusables then - return focusables[k + 1] - end - return focusables[1] - end - end - - local focused = getNextFocus(self.focused) - if focused then - self:setFocus(focused) - end -end - -function UI.Page:setFocus(child) - if not child or not child.focus then - return - end - - if self.focused and self.focused ~= child then - self.focused.focused = false - self.focused:focus() - self.focused:emit({ type = 'focus_lost', focused = child }) - end - - self.focused = child - if not child.focused then - child.focused = true - child:emit({ type = 'focus_change', focused = child }) - --self:emit({ type = 'focus_change', focused = child }) - end - - child:focus() -end - -function UI.Page:eventHandler(event) - if self.focused then - if event.type == 'focus_next' then - self:focusNext() - return true - elseif event.type == 'focus_prev' then - self:focusPrevious() - return true - end - end -end - +-- lazy load components local function loadComponents() local function load(name) local s, m = Util.run(_ENV, 'sys/modules/opus/ui/components/' .. name .. '.lua') @@ -1295,7 +1152,6 @@ end loadComponents() UI:loadTheme('usr/config/ui.theme') Util.merge(UI.Window.defaults, UI.theme.Window) -Util.merge(UI.Page.defaults, UI.theme.Page) UI:setDefaultDevice(UI.Device({ device = term.current() })) return UI diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index cb46ede..6fe12b1 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -9,6 +9,7 @@ local colors = _G.colors local Canvas = class() +Canvas.__visualize = false Canvas.colorPalette = { } Canvas.darkPalette = { } Canvas.grayscalePalette = { } @@ -303,7 +304,7 @@ end -- the array. function Canvas:__renderLayers(device, offset) if #self.layers > 0 then - self.regions = self.regions or Region.new(self.x, self.y, self.ex, self.ey) + self.regions = self.regions or Region.new(self.x + offset.x, self.y + offset.y, self.ex + offset.x, self.ey + offset.y) for i = 1, #self.layers do local canvas = self.layers[i] @@ -379,31 +380,29 @@ function Canvas:__punch(rect, offset) rect.ey + offset.y) end +-- performance can probably be improved by using one more buffer tied to the device function Canvas:__blitRect(device, src, tgt) src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 } tgt = tgt or self - --[[ - -- for visualizing updates on the screen - local drew - for i = 0, src.ey - src.y do - local line = self.lines[src.y + i + (self.offy or 0)] - if line and line.dirty then - drew = true - local t, fg, bg = line.text, line.fg, line.bg - if src.x > 1 or src.ex < self.ex then - t = _sub(t, src.x, src.ex) - fg = _rep(1, src.ex-src.x + 1) - bg = _rep(2, src.ex-src.x + 1) + -- for visualizing updates on the screen + if Canvas.__visualize then + local drew + local t = _rep(' ', src.ex-src.x + 1) + local bg = _rep(2, src.ex-src.x + 1) + for i = 0, src.ey - src.y do + local line = self.lines[src.y + i + (self.offy or 0)] + if line and line.dirty then + drew = true + device.setCursorPos(tgt.x, tgt.y + i) + device.blit(t, bg, bg) end - device.setCursorPos(tgt.x, tgt.y + i) - device.blit(t, fg, bg) + end + if drew then + local t = os.clock() + repeat until os.clock()-t > .2 end end - if drew then - os.sleep(.3) - end - ]] for i = 0, src.ey - src.y do local line = self.lines[src.y + i + (self.offy or 0)] if line and line.dirty then diff --git a/sys/modules/opus/ui/components/Chooser.lua b/sys/modules/opus/ui/components/Chooser.lua index 9698f6f..32fd3e2 100644 --- a/sys/modules/opus/ui/components/Chooser.lua +++ b/sys/modules/opus/ui/components/Chooser.lua @@ -14,6 +14,11 @@ UI.Chooser.defaults = { leftIndicator = UI.extChars and '\17' or '<', rightIndicator = UI.extChars and '\16' or '>', height = 1, + accelerators = { + space = 'choice_next', + right = 'choice_next', + left = 'choice_prev', + } } function UI.Chooser:setParent() if not self.width and not self.ex then @@ -29,16 +34,11 @@ function UI.Chooser:setParent() end function UI.Chooser:draw() - local bg = self.backgroundColor - if self.focused then - bg = self.backgroundFocusColor - end + local bg = self.focused and self.backgroundFocusColor or self.backgroundColor local fg = self.inactive and self.textInactiveColor or self.textColor local choice = Util.find(self.choices, 'value', self.value) - local value = self.nochoice - if choice then - value = choice.name - end + local value = choice and choice.name or self.nochoice + self:write(1, 1, self.leftIndicator, self.backgroundColor, colors.black) self:write(2, 1, ' ' .. Util.widthify(tostring(value), self.width-4) .. ' ', bg, fg) self:write(self.width, 1, self.rightIndicator, self.backgroundColor, colors.black) @@ -49,39 +49,37 @@ function UI.Chooser:focus() end function UI.Chooser:eventHandler(event) - if event.type == 'key' then - if event.key == 'right' or event.key == 'space' then - local _,k = Util.find(self.choices, 'value', self.value) - local choice - if not k then k = 0 end - if k and k < #self.choices then - choice = self.choices[k+1] - else - choice = self.choices[1] - end - self.value = choice.value - self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) - self:draw() - return true - elseif event.key == 'left' then - local _,k = Util.find(self.choices, 'value', self.value) - local choice - if k and k > 1 then - choice = self.choices[k-1] - else - choice = self.choices[#self.choices] - end - self.value = choice.value - self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) - self:draw() - return true + if event.type == 'choice_next' then + local _,k = Util.find(self.choices, 'value', self.value) + local choice + if not k then k = 0 end + if k and k < #self.choices then + choice = self.choices[k+1] + else + choice = self.choices[1] end + self.value = choice.value + self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) + self:draw() + return true + elseif event.type == 'choice_prev' then + local _,k = Util.find(self.choices, 'value', self.value) + local choice + if k and k > 1 then + choice = self.choices[k-1] + else + choice = self.choices[#self.choices] + end + self.value = choice.value + self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) + self:draw() + return true elseif event.type == 'mouse_click' or event.type == 'mouse_doubleclick' then if event.x == 1 then - self:emit({ type = 'key', key = 'left' }) + self:emit({ type = 'choice_prev' }) return true elseif event.x == self.width then - self:emit({ type = 'key', key = 'right' }) + self:emit({ type = 'choice_next' }) return true end end diff --git a/sys/modules/opus/ui/components/Page.lua b/sys/modules/opus/ui/components/Page.lua new file mode 100644 index 0000000..7fbaa0f --- /dev/null +++ b/sys/modules/opus/ui/components/Page.lua @@ -0,0 +1,163 @@ +local Canvas = require('opus.ui.canvas') +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') + +local colors = _G.colors + +-- need to add offsets to this test +local function getPosition(element) + local x, y = 1, 1 + repeat + x = element.x + x - 1 + y = element.y + y - 1 + element = element.parent + until not element + return x, y +end + +UI.Page = class(UI.Window) +UI.Page.defaults = { + UIElement = 'Page', + accelerators = { + down = 'focus_next', + enter = 'focus_next', + tab = 'focus_next', + ['shift-tab' ] = 'focus_prev', + up = 'focus_prev', + }, + backgroundColor = colors.cyan, + textColor = colors.white, +} +function UI.Page:postInit() + self.parent = self.parent or UI.defaultDevice + self.__target = self + self.canvas = Canvas({ + x = 1, y = 1, width = self.parent.width, height = self.parent.height, + isColor = self.parent.isColor, + }) + self.canvas:clear(self.backgroundColor, self.textColor) +end + +function UI.Page:enable() + self.canvas.visible = true + UI.Window.enable(self) + + if not self.focused or not self.focused.enabled then + self:focusFirst() + end +end + +function UI.Page:disable() + self.canvas.visible = false + UI.Window.disable(self) +end + +function UI.Page:sync() + if self.enabled then + self.parent:sync() + end +end + +function UI.Page:capture(child) + self.__target = child +end + +function UI.Page:release(child) + if self.__target == child then + self.__target = self + end +end + +function UI.Page:pointToChild(x, y) + if self.__target == self then + return UI.Window.pointToChild(self, x, y) + end + local absX, absY = getPosition(self.__target) + + -- this is sketchy + x = x + self.offx - self.x - (absX - self.__target.x) + 1 + y = y + self.offy - self.y - (absY - self.__target.y) + 1 + + return self.__target:pointToChild(x, y) +end + +function UI.Page:getFocusables() + if self.__target == self or self.__target.pageType ~= 'modal' then + return UI.Window.getFocusables(self) + end + return self.__target:getFocusables() +end + +function UI.Page:getFocused() + return self.focused +end + +function UI.Page:focusPrevious() + local function getPreviousFocus(focused) + local focusables = self:getFocusables() + local k = Util.contains(focusables, focused) + if k then + if k > 1 then + return focusables[k - 1] + end + return focusables[#focusables] + end + end + + local focused = getPreviousFocus(self.focused) + if focused then + self:setFocus(focused) + end +end + +function UI.Page:focusNext() + local function getNextFocus(focused) + local focusables = self:getFocusables() + local k = Util.contains(focusables, focused) + if k then + if k < #focusables then + return focusables[k + 1] + end + return focusables[1] + end + end + + local focused = getNextFocus(self.focused) + if focused then + self:setFocus(focused) + end +end + +function UI.Page:setFocus(child) + if not child or not child.focus then + return + end + + if self.focused and self.focused ~= child then + self.focused.focused = false + self.focused:focus() + self.focused:emit({ type = 'focus_lost', focused = child, unfocused = self.focused }) + end + + self.focused = child + if not child.focused then + child.focused = true + child:emit({ type = 'focus_change', focused = child }) + --self:emit({ type = 'focus_change', focused = child }) + end + + child:focus() +end + +function UI.Page:eventHandler(event) + if self.focused then + if event.type == 'focus_next' then + self:focusNext() + return true + elseif event.type == 'focus_prev' then + self:focusPrevious() + return true + end + end +end diff --git a/sys/modules/opus/ui/components/SlideOut.lua b/sys/modules/opus/ui/components/SlideOut.lua index 789220f..479be4b 100644 --- a/sys/modules/opus/ui/components/SlideOut.lua +++ b/sys/modules/opus/ui/components/SlideOut.lua @@ -61,17 +61,14 @@ end function UI.SlideOut.example() -- for the transistion to work properly, the parent must have a canvas return UI.ActiveLayer { - y = 1, -- TODO: if this is set to anything greater than 1, then - -- the layer is not rendered in the correct location - -- a general issue in canvas layers - backgroundColor = colors.cyan, + y = 2, button = UI.Button { x = 2, y = 5, text = 'show', }, slideOut = UI.SlideOut { - backgroundColor = colors.yellow, - y = -4, height = 4, + backgroundColor = _G.colors.yellow, + y = -4, height = 4, x = 3, ex = -3, button = UI.Button { x = 2, y = 2, text = 'hide', diff --git a/sys/modules/opus/ui/transition.lua b/sys/modules/opus/ui/transition.lua index 4448760..f8cee0b 100644 --- a/sys/modules/opus/ui/transition.lua +++ b/sys/modules/opus/ui/transition.lua @@ -39,7 +39,7 @@ function Transition.expandUp(args) local easing = args.easing or 'linear' local pos = { y = args.ey + 1 } local tween = Tween.new(ticks, pos, { y = args.y }, easing) - +_syslog(args) args.canvas:move(args.x, pos.y) return function() -- 2.49.1 From 1e675a2e3523411b45655e450be62b137f41230a Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 1 Dec 2019 13:25:26 -0700 Subject: [PATCH 106/236] revert multiple sub-canvas mouse click --- sys/modules/opus/ui/components/Page.lua | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sys/modules/opus/ui/components/Page.lua b/sys/modules/opus/ui/components/Page.lua index 7fbaa0f..7bc8e70 100644 --- a/sys/modules/opus/ui/components/Page.lua +++ b/sys/modules/opus/ui/components/Page.lua @@ -73,12 +73,17 @@ function UI.Page:pointToChild(x, y) if self.__target == self then return UI.Window.pointToChild(self, x, y) end + x = x + self.offx - self.x + 1 + y = y + self.offy - self.y + 1 +--[[ + -- this is supposed to fix when there are multiple sub canvases local absX, absY = getPosition(self.__target) - - -- this is sketchy - x = x + self.offx - self.x - (absX - self.__target.x) + 1 - y = y + self.offy - self.y - (absY - self.__target.y) + 1 - + if self.__target.canvas then + x = x - (self.__target.canvas.x - self.__target.x) + y = y - (self.__target.canvas.y - self.__target.y) + _syslog({'raw', self.__target.canvas.y, self.__target.y}) + end + ]] return self.__target:pointToChild(x, y) end -- 2.49.1 From 424fff784211d88a5f1d59debf6832d4796449e8 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 5 Dec 2019 13:44:35 -0700 Subject: [PATCH 107/236] apparently read was added to fs.open "r" mode in 2017 --- sys/modules/opus/fs/ramfs.lua | 4 ++++ sys/modules/opus/fs/urlfs.lua | 4 ++++ sys/modules/opus/ui/transition.lua | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sys/modules/opus/fs/ramfs.lua b/sys/modules/opus/fs/ramfs.lua index 1ed2f7a..a516d80 100644 --- a/sys/modules/opus/fs/ramfs.lua +++ b/sys/modules/opus/fs/ramfs.lua @@ -77,6 +77,10 @@ function ramfs.open(node, fn, fl) local ctr = 0 local lines return { + read = function() + ctr = ctr + 1 + return node.contents:sub(ctr, ctr) + end, readLine = function() if not lines then lines = Util.split(node.contents) diff --git a/sys/modules/opus/fs/urlfs.lua b/sys/modules/opus/fs/urlfs.lua index 960806e..3595cb3 100644 --- a/sys/modules/opus/fs/urlfs.lua +++ b/sys/modules/opus/fs/urlfs.lua @@ -74,6 +74,10 @@ function urlfs.open(node, fn, fl) if fl == 'r' then return { + read = function() + ctr = ctr + 1 + return c:sub(ctr, ctr) + end, readLine = function() if not lines then lines = Util.split(c) diff --git a/sys/modules/opus/ui/transition.lua b/sys/modules/opus/ui/transition.lua index f8cee0b..4448760 100644 --- a/sys/modules/opus/ui/transition.lua +++ b/sys/modules/opus/ui/transition.lua @@ -39,7 +39,7 @@ function Transition.expandUp(args) local easing = args.easing or 'linear' local pos = { y = args.ey + 1 } local tween = Tween.new(ticks, pos, { y = args.y }, easing) -_syslog(args) + args.canvas:move(args.x, pos.y) return function() -- 2.49.1 From 31f43067bf747ceeef3aa6d628357818837bc8fe Mon Sep 17 00:00:00 2001 From: Anavrins Date: Fri, 6 Dec 2019 20:14:18 -0500 Subject: [PATCH 108/236] Change how Sniff.lua detects modem --- sys/apps/Sniff.lua | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 2bda4f0..3b4ca9c 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -307,14 +307,16 @@ end function page:enable() modemConfig.modems = {} - peripheral.find('modem', function(side, dev) - modemConfig.modems[side] = { - type = dev.isWireless() and 'Wireless' or 'Wired', - side = side, - openChannels = { }, - device = dev, - loaded = false - } + Util.each(_G.device, function(dev) + if dev.type == "modem" then + modemConfig.modems[dev.side] = { + type = dev.isWireless() and 'Wireless' or 'Wired', + side = dev.side, + openChannels = { }, + device = dev, + loaded = false + } + end end) modemConfig.currentModem = device.wireless_modem and modemConfig.modems[device.wireless_modem.side] or -- 2.49.1 From 28b2ba3386056e1422dfbaef7a5fdd32556cc71c Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 7 Dec 2019 12:04:58 -0700 Subject: [PATCH 109/236] tweaks --- sys/apps/Help.lua | 100 ++++++++++++++++++--------------------- sys/apps/Overview.lua | 6 +++ sys/autorun/hotkeys.lua | 51 ++++++++------------ sys/autorun/log.lua | 19 +++----- sys/autorun/upgraded.lua | 4 ++ sys/modules/opus/ui.lua | 6 +-- 6 files changed, 85 insertions(+), 101 deletions(-) diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index 8bae671..3702371 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -8,12 +8,10 @@ UI:configure('Help', ...) local topics = { } for _,topic in pairs(help.topics()) do - if help.lookup(topic) then - table.insert(topics, { name = topic }) - end + table.insert(topics, { name = topic, lname = topic:lower() }) end -local page = UI.Page { +UI:addPage('main', UI.Page { labelText = UI.Text { x = 3, y = 2, value = 'Search', @@ -21,7 +19,6 @@ local page = UI.Page { filter = UI.TextEntry { x = 10, y = 2, ex = -3, limit = 32, - transform = 'lowercase', }, grid = UI.ScrollingGrid { y = 4, @@ -29,15 +26,44 @@ local page = UI.Page { columns = { { heading = 'Topic', key = 'name' }, }, - sortColumn = 'name', + sortColumn = 'lname', }, accelerators = { [ 'control-q' ] = 'quit', enter = 'grid_select', }, -} + eventHandler = function(self, event) + if event.type == 'quit' then + UI:quit() -local topicPage = UI.Page { + elseif event.type == 'grid_select' then + if self.grid:getSelected() then + local name = self.grid:getSelected().name + + UI:setPage('topic', name) + end + + elseif event.type == 'text_change' then + if not event.text then + self.grid.values = topics + else + self.grid.values = { } + for _,f in pairs(topics) do + if string.find(f.lname, event.text:lower()) then + table.insert(self.grid.values, f) + end + end + end + self.grid:update() + self.grid:setIndex(1) + self.grid:draw() + else + return UI.Page.eventHandler(self, event) + end + end, +}) + +UI:addPage('topic', UI.Page { backgroundColor = colors.black, titleBar = UI.TitleBar { title = 'text', @@ -51,54 +77,22 @@ local topicPage = UI.Page { [ 'control-q' ] = 'back', backspace = 'back', }, -} + enable = function(self, name) + local f = help.lookup(name) -function topicPage:enable(name) - local f = help.lookup(name) + self.titleBar.title = name + self.helpText:setText(f and Util.readFile(f) or 'No help available for ' .. name) - self.titleBar.title = name - self.helpText:setText(f and Util.readFile(f) or 'No help available for ' .. name) - - return UI.Page.enable(self) -end - -function topicPage:eventHandler(event) - if event.type == 'back' then - UI:setPage(page) - end - return UI.Page.eventHandler(self, event) -end - -function page:eventHandler(event) - if event.type == 'quit' then - UI:exitPullEvents() - - elseif event.type == 'grid_select' then - if self.grid:getSelected() then - local name = self.grid:getSelected().name - - UI:setPage(topicPage, name) + return UI.Page.enable(self) + end, + eventHandler = function(self, event) + if event.type == 'back' then + UI:setPage('main') end - - elseif event.type == 'text_change' then - if not event.text then - self.grid.values = topics - else - self.grid.values = { } - for _,f in pairs(topics) do - if string.find(f.name, event.text) then - table.insert(self.grid.values, f) - end - end - end - self.grid:update() - self.grid:setIndex(1) - self.grid:draw() - else return UI.Page.eventHandler(self, event) - end -end + end, +}) local args = Util.parse(...) -UI:setPage(args[1] and topicPage or page, args[1]) -UI:pullEvents() +UI:setPage(args[1] and 'topic' or 'main', args[1]) +UI:start() diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 92c09bf..b4b524f 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -27,6 +27,12 @@ local DEFAULT_ICON = NFT.parse("\0308\0317\153\153\153\153\153\ \0307\0318\153\153\153\153\153\ \0308\0317\153\153\153\153\153") +-- overview +local uid = _ENV.multishell.getCurrent() +device.keyboard.addHotkey('control-o', function() + _ENV.multishell.setFocus(uid) +end) + UI:configure('Overview', ...) local config = { diff --git a/sys/autorun/hotkeys.lua b/sys/autorun/hotkeys.lua index cbd93bf..ab591e0 100644 --- a/sys/autorun/hotkeys.lua +++ b/sys/autorun/hotkeys.lua @@ -4,58 +4,45 @@ local kernel = _G.kernel local keyboard = _G.device.keyboard local multishell = _ENV.multishell -if not multishell or not multishell.getTabs then - return -end - --- overview -keyboard.addHotkey('control-o', function() - for _,tab in pairs(multishell.getTabs()) do - if tab.isOverview then - multishell.setFocus(tab.uid) +if multishell and multishell.getTabs then + -- restart tab + keyboard.addHotkey('control-backspace', function() + local tab = kernel.getFocused() + if tab and not tab.noTerminate then + multishell.terminate(tab.uid) + multishell.openTab({ + path = tab.path, + env = tab.env, + args = tab.args, + focused = true, + }) end - end -end) - --- restart tab -keyboard.addHotkey('control-backspace', function() - local uid = multishell.getFocus() - local tab = kernel.find(uid) - if not tab.isOverview then - multishell.terminate(uid) - multishell.openTab({ - path = tab.path, - env = tab.env, - args = tab.args, - focused = true, - }) - end -end) + end) +end -- next tab keyboard.addHotkey('control-tab', function() - local tabs = multishell.getTabs() local visibleTabs = { } - local currentTabId = multishell.getFocus() + local currentTab = kernel.getFocused() local function compareTab(a, b) return a.uid < b.uid end - for _,tab in Util.spairs(tabs, compareTab) do + for _,tab in Util.spairs(kernel.routines, compareTab) do if not tab.hidden and not tab.noFocus then table.insert(visibleTabs, tab) end end for k,tab in ipairs(visibleTabs) do - if tab.uid == currentTabId then + if tab.uid == currentTab.uid then if k < #visibleTabs then - multishell.setFocus(visibleTabs[k + 1].uid) + kernel.raise(visibleTabs[k + 1].uid) return end end end if #visibleTabs > 0 then - multishell.setFocus(visibleTabs[1].uid) + kernel.raise(visibleTabs[1].uid) end end) diff --git a/sys/autorun/log.lua b/sys/autorun/log.lua index 91bb193..5e2b5e7 100644 --- a/sys/autorun/log.lua +++ b/sys/autorun/log.lua @@ -50,16 +50,9 @@ local function systemLog() keyboard.removeHotkey('control-d') end -if multishell and multishell.openTab then - multishell.openTab({ - title = 'System Log', - fn = systemLog, - noTerminate = true, - hidden = true, - }) -else - kernel.run({ - title = 'Syslog', - fn = systemLog, - }) -end +kernel.run({ + title = 'System Log', + fn = systemLog, + noTerminate = true, + hidden = true, +}) diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index 5922c3a..141bf1d 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -18,3 +18,7 @@ deleteIfExists('sys/autorun/gpshost.lua') deleteIfExists('sys/apps/network/redserver.lua') deleteIfExists('sys/apis') deleteIfExists('sys/autorun/apps.lua') +deleteIfExists('sys/init/6.tl3.lua') + +-- remove this file +deleteIfExists('sys/autorun/upgraded.lua') \ No newline at end of file diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 4ff0bea..c627005 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -388,9 +388,9 @@ function Manager:pullEvents(...) end end -function Manager:exitPullEvents() - Event.exitPullEvents() -end +Manager.exitPullEvents = Event.exitPullEvents +Manager.quit = Event.exitPullEvents +Manager.start = Manager.pullEvents local UI = Manager() -- 2.49.1 From 6204c46cc4971f800cb3ca8a4759dcb868ae5cd7 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Wed, 11 Dec 2019 11:18:02 -0500 Subject: [PATCH 110/236] Fix empty TextEntry in cloud config --- sys/apps/system/cloud.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index 221c828..66078f6 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -42,7 +42,7 @@ To obtain a key, visit: function tab:eventHandler(event) if event.type == 'update_key' then - if #self.key.value > 0 then + if self.key.value then config.key = self.key.value else config.key = nil -- 2.49.1 From 9e3cf50ccc153346e675233fe65fa4fab67fcb2f Mon Sep 17 00:00:00 2001 From: Anavrins Date: Wed, 11 Dec 2019 11:20:12 -0500 Subject: [PATCH 111/236] socket.lua: Option to pass user generated keypairs --- sys/modules/opus/socket.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 8151855..b126643 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -126,7 +126,11 @@ function Socket.connect(host, port, options) local socket = newSocket(host == os.getComputerID()) socket.dhost = tonumber(host) - socket.privKey, socket.pubKey = network.getKeyPair() + if options and options.keypair then + socket.privKey, socket.pubKey = unpack(options.keypair) + else + socket.privKey, socket.pubKey = network.getKeyPair() + end local identifier = options and options.identifier or Security.getIdentifier() socket.transmit(port, socket.sport, { -- 2.49.1 From cc80e084073f2c890cef6c1c7dabeff1453682ff Mon Sep 17 00:00:00 2001 From: Anavrins Date: Wed, 11 Dec 2019 11:38:12 -0500 Subject: [PATCH 112/236] gps.lua: Added nil and nan checks --- sys/modules/opus/gps.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/modules/opus/gps.lua b/sys/modules/opus/gps.lua index 0ca7a66..8e85977 100644 --- a/sys/modules/opus/gps.lua +++ b/sys/modules/opus/gps.lua @@ -98,7 +98,7 @@ function GPS.trilaterate(tFixes) if pos2 then pos1, pos2 = narrow(pos1, pos2, tFixes[1]) end - if not pos2 then + if not pos2 and pos1 and not (pos1.x ~= pos1.x) then return pos1, attemps end end -- 2.49.1 From 942f0bda92f0aac48bf157ebef9a2c79791a294a Mon Sep 17 00:00:00 2001 From: Anavrins Date: Fri, 27 Dec 2019 01:11:36 -0500 Subject: [PATCH 113/236] GPS overhaul Changes how GPS works to avoid returning ambiguous coordinates and nan errors --- sys/modules/opus/gps.lua | 76 ++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/sys/modules/opus/gps.lua b/sys/modules/opus/gps.lua index 8e85977..3b0ae74 100644 --- a/sys/modules/opus/gps.lua +++ b/sys/modules/opus/gps.lua @@ -1,17 +1,71 @@ local Util = require('opus.util') local GPS = { } +GPS.CHANNEL_GPS = 65534 local device = _G.device -local gps = _G.gps +local vector = _G.vector function GPS.locate(timeout, debug) - local pt = { } - timeout = timeout or 10 - pt.x, pt.y, pt.z = gps.locate(timeout, debug) - if pt.x then - return pt + if not device.wireless_modem then + if debug then + print('No wireless modem attached') + end + return nil end + + if debug then + print('Finding position...') + end + + local modem = device.wireless_modem + local closeChannel = false + local selfID = os.getComputerID() + if not modem.isOpen(selfID) then + modem.open(selfID) + closeChannel = true + end + + modem.transmit(GPS.CHANNEL_GPS, selfID, "PING") + + local fixes = {} + local pos = nil + local timer = os.startTimer(timeout or 1) + while true do + local e, side, chan, reply, msg, dist = os.pullEvent() + if e == "modem_message" then + if side == modem.side and chan == selfID and reply == GPS.CHANNEL_GPS and dist then + if type(msg) == "table" and #msg == 3 and tonumber(msg[1]) and tonumber(msg[2]) and tonumber(msg[3]) then + local fix = { + position = vector.new(unpack(msg)), + distance = dist, + } + if debug then + print(fix.distance..' meters from '..fix.position:tostring()) + end + if fix.distance == 0 then + pos = fix.position + else + fixes[#fixes+1] = fix + if #fixes > 3 then + pos = GPS.trilaterate(fixes) + if pos then break end + end + end + end + end + elseif e == "timer" and side == timer then + break + end + end + + if closeChannel then + modem.close(selfID) + end + if debug then + print("Position is "..pos.x..","..pos.y..","..pos.z) + end + return vector.new(pos.x, pos.y, pos.z) end function GPS.isAvailable() @@ -66,26 +120,26 @@ local function trilaterate(A, B, C) local result1 = result + (ez * z) local result2 = result - (ez * z) - local rounded1, rounded2 = result1:round(), result2:round() + local rounded1, rounded2 = result1:round(0.01), result2:round(0.01) if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then return rounded1, rounded2 else return rounded1 end end - return result:round() + return result:round(0.01) end local function narrow( p1, p2, fix ) local dist1 = math.abs( (p1 - fix.position):length() - fix.distance ) local dist2 = math.abs( (p2 - fix.position):length() - fix.distance ) - if math.abs(dist1 - dist2) < 0.05 then + if math.abs(dist1 - dist2) < 0.01 then return p1, p2 elseif dist1 < dist2 then - return p1:round() + return p1:round(0.01) else - return p2:round() + return p2:round(0.01) end end -- end stock gps api -- 2.49.1 From 210c5f5a11c4f68a03c1ff3e1cea1920eb838146 Mon Sep 17 00:00:00 2001 From: Wojbie Date: Sun, 9 Feb 2020 23:20:03 +0100 Subject: [PATCH 114/236] Update git.lua to use headers authorization. --- sys/modules/opus/git.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sys/modules/opus/git.lua b/sys/modules/opus/git.lua index 49c42e4..a06cc70 100644 --- a/sys/modules/opus/git.lua +++ b/sys/modules/opus/git.lua @@ -3,10 +3,11 @@ local Util = require('opus.util') local TREE_URL = 'https://api.github.com/repos/%s/%s/git/trees/%s?recursive=1' local FILE_URL = 'https://raw.githubusercontent.com/%s/%s/%s/%s' +local TREE_HEADERS = {} local git = { } if _G._GIT_API_KEY then - TREE_URL = TREE_URL .. '&access_token=' .. _G._GIT_API_KEY + TREE_HEADERS.Authorization = 'token ' .. _G._GIT_API_KEY end function git.list(repository) @@ -23,8 +24,10 @@ function git.list(repository) local function getContents() local dataUrl = string.format(TREE_URL, user, repo, branch) - local contents = Util.download(dataUrl) - if contents then + local contents, msg = Util.httpGet(dataUrl,TREE_HEADERS) + if not contents then + error(_sformat('Failed to download %s\n%s', dataUrl, msg), 2) + else return json.decode(contents) end end -- 2.49.1 From 39522ee5b1f2c6fb875bbe1ea76bec8836e5dac8 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 30 Mar 2020 02:07:20 -0400 Subject: [PATCH 115/236] Cleanup --- sys/apps/Overview.lua | 4 +++ sys/apps/ShellLauncher.lua | 2 +- sys/apps/network/keygen.lua | 34 +++++++++++------------ sys/autorun/upgraded.lua | 2 +- sys/modules/opus/crypto/chacha20.lua | 29 ++++---------------- sys/modules/opus/crypto/ecc/init.lua | 7 +++-- sys/modules/opus/crypto/sha2.lua | 26 ++++-------------- sys/modules/opus/socket.lua | 4 +-- sys/modules/opus/util.lua | 41 +++++++++++++++++++++++++--- 9 files changed, 78 insertions(+), 71 deletions(-) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index b4b524f..c93f516 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -157,6 +157,7 @@ local page = UI.Page { f = 'files', s = 'shell', l = 'lua', + n = 'network', [ 'control-n' ] = 'new', delete = 'delete', }, @@ -435,6 +436,9 @@ function page:eventHandler(event) elseif event.type == 'files' then shell.switchTab(shell.openTab(Alt.get('files'))) + elseif event.type == 'network' then + shell.switchTab(shell.openTab('network')) + elseif event.type == 'focus_change' then if event.focused.parent.UIElement == 'Icon' then event.focused.parent:scrollIntoView() diff --git a/sys/apps/ShellLauncher.lua b/sys/apps/ShellLauncher.lua index f2eebb7..570234b 100644 --- a/sys/apps/ShellLauncher.lua +++ b/sys/apps/ShellLauncher.lua @@ -27,4 +27,4 @@ kernel.hook('kernel_focus', function(_, eventData) end end) -os.pullEventRaw('kernel_halt') \ No newline at end of file +os.pullEventRaw('kernel_halt') diff --git a/sys/apps/network/keygen.lua b/sys/apps/network/keygen.lua index 65f04b8..4a74670 100644 --- a/sys/apps/network/keygen.lua +++ b/sys/apps/network/keygen.lua @@ -10,30 +10,30 @@ local keyPairs = { } local function generateKeyPair() local key = { } for _ = 1, 32 do - table.insert(key, ("%02x"):format(math.random(0, 0xFF))) + table.insert(key, math.random(0, 0xFF)) end - local privateKey = Util.hexToByteArray(table.concat(key)) + local privateKey = setmetatable(key, Util.byteArrayMT) return privateKey, ECC.publicKey(privateKey) end getmetatable(network).__index.getKeyPair = function() - local keys = table.remove(keyPairs) - os.queueEvent('generate_keypair') - if not keys then - return generateKeyPair() - end - return table.unpack(keys) + local keys = table.remove(keyPairs) + os.queueEvent('generate_keypair') + if not keys then + return generateKeyPair() + end + return table.unpack(keys) end -- Generate key pairs in the background as this is a time-consuming process Event.on('generate_keypair', function() - while true do - os.sleep(5) - local timer = Util.timer() - table.insert(keyPairs, { generateKeyPair() }) - _G._syslog('Generated keypair in ' .. timer()) - if #keyPairs >= 3 then - break - end - end + while true do + os.sleep(5) + local timer = Util.timer() + table.insert(keyPairs, { generateKeyPair() }) + _G._syslog('Generated keypair in ' .. timer()) + if #keyPairs >= 3 then + break + end + end end) diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index 141bf1d..1cb0c1a 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -21,4 +21,4 @@ deleteIfExists('sys/autorun/apps.lua') deleteIfExists('sys/init/6.tl3.lua') -- remove this file -deleteIfExists('sys/autorun/upgraded.lua') \ No newline at end of file +deleteIfExists('sys/autorun/upgraded.lua') diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 7c3b819..9db2d08 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -12,12 +12,11 @@ local band = bit32.band local blshift = bit32.lshift local brshift = bit32.arshift local textutils = _G.textutils +local mt = Util.byteArrayMT local mod = 2^32 local tau = {("expand 16-byte k"):byte(1,-1)} local sigma = {("expand 32-byte k"):byte(1,-1)} -local null32 = {("A"):rep(32):byte(1,-1)} -local null12 = {("A"):rep(12):byte(1,-1)} local function rotl(n, b) local s = n/(2^(32-b)) @@ -91,22 +90,6 @@ local function serialize(state) return r end -local mt = { - __tostring = function(a) return string.char(table.unpack(a)) end, - __index = { - toHex = function(self) return ("%02x"):rep(#self):format(table.unpack(self)) end, - isEqual = function(self, t) - if type(t) ~= "table" then return false end - if #self ~= #t then return false end - local ret = 0 - for i = 1, #self do - ret = bit32.bor(ret, bxor(self[i], t[i])) - end - return ret == 0 - end - } -} - local function crypt(data, key, nonce, cntr, round) assert(type(key) == "table", "ChaCha20: Invalid key format ("..type(key).."), must be table") assert(type(nonce) == "table", "ChaCha20: Invalid nonce format ("..type(nonce).."), must be table") @@ -133,15 +116,12 @@ local function crypt(data, key, nonce, cntr, round) out[#out+1] = bxor(block[j], ks[j]) end - --if i % 1000 == 0 then - throttle() - --os.queueEvent("") - --os.pullEvent("") - --end + throttle() end return setmetatable(out, mt) end +-- Helper functions local function genNonce(len) local nonce = {} for i = 1, len do @@ -170,6 +150,9 @@ end local obj = {} local rng_mt = {['__index'] = obj} +-- PRNG object +local null32 = {("A"):rep(32):byte(1,-1)} +local null12 = {("A"):rep(12):byte(1,-1)} function obj:nextInt(byte) if not byte or byte < 1 or byte > 6 then error("Can only return 1-6 bytes", 2) end local output = 0 diff --git a/sys/modules/opus/crypto/ecc/init.lua b/sys/modules/opus/crypto/ecc/init.lua index 980f699..4712dca 100644 --- a/sys/modules/opus/crypto/ecc/init.lua +++ b/sys/modules/opus/crypto/ecc/init.lua @@ -1,9 +1,12 @@ local fq = require('opus.crypto.ecc.fq') local elliptic = require('opus.crypto.ecc.elliptic') local sha256 = require('opus.crypto.sha2') +local Util = require('opus.util') + local os = _G.os local unpack = table.unpack +local mt = Util.byteArrayMT local q = {1372, 62520, 47765, 8105, 45059, 9616, 65535, 65535, 65535, 65535, 65535, 65532} @@ -27,7 +30,7 @@ local function publicKey(sk) local Y = elliptic.scalarMulG(x) local pk = elliptic.pointEncode(Y) - return pk + return setmetatable(pk, mt) end local function exchange(sk, pk) @@ -62,7 +65,7 @@ local function sign(sk, message) sig[#sig + 1] = s[i] end - return sig + return setmetatable(sig, mt) end local function verify(pk, message, sig) diff --git a/sys/modules/opus/crypto/sha2.lua b/sys/modules/opus/crypto/sha2.lua index 4dcb98b..17df659 100644 --- a/sys/modules/opus/crypto/sha2.lua +++ b/sys/modules/opus/crypto/sha2.lua @@ -9,6 +9,7 @@ local bnot = bit32 and bit32.bnot or bit.bnot local bxor = bit32 and bit32.bxor or bit.bxor local blshift = bit32 and bit32.lshift or bit.blshift local upack = unpack or table.unpack +local mt = Util.byteArrayMT local function rrotate(n, b) local s = n/(2^b) @@ -68,17 +69,16 @@ end local function digestblock(w, C) for j = 17, 64 do - -- local v = w[j-15] - local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3)) - local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10)) + local s0 = bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18), brshift(w[j-15], 3)) + local s1 = bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19), brshift(w[j-2], 10)) w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32 end local a, b, c, d, e, f, g, h = upack(C) for j = 1, 64 do - local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25)) + local S1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25)) local ch = bxor(band(e, f), band(bnot(e), g)) local temp1 = (h + S1 + ch + K[j] + w[j])%mod32 - local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22)) + local S0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22)) local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c)) local temp2 = (S0 + maj)%mod32 h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32 @@ -94,22 +94,6 @@ local function digestblock(w, C) return C end -local mt = { - __tostring = function(a) return string.char(upack(a)) end, - __index = { - toHex = function(self) return ("%02x"):rep(#self):format(upack(self)) end, - isEqual = function(self, t) - if type(t) ~= "table" then return false end - if #self ~= #t then return false end - local ret = 0 - for i = 1, #self do - ret = bit32.bor(ret, bxor(self[i], t[i])) - end - return ret == 0 - end - } -} - local function toBytes(t, n) local b = {} for i = 1, n do diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index b126643..89afa4b 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -139,7 +139,7 @@ function Socket.connect(host, port, options) dhost = socket.dhost, t = Crypto.encrypt({ -- this is not that much data... ts = os.epoch('utc'), - pk = Util.byteArrayToHex(socket.pubKey), + pk = socket.pubKey:toHex(), }, Util.hexToByteArray(identifier)), }) @@ -237,7 +237,7 @@ function Socket.server(port, options) type = 'CONN', dhost = socket.dhost, shost = socket.shost, - pk = Util.byteArrayToHex(socket.pubKey), + pk = socket.pubKey:toHex(), options = socket.options.ENCRYPT and { ENCRYPT = true }, }) diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 564b9f8..383420e 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -9,6 +9,39 @@ local textutils = _G.textutils local _sformat = string.format local _srep = string.rep local _ssub = string.sub +local _unpack = table.unpack +local _bor = bit32.bor +local _bxor = bit32.bxor + +byteArrayMT = { + __tostring = function(a) return string.char(_unpack(a)) end, + __index = { + toHex = function(self) return ("%02x"):rep(#self):format(_unpack(self)) end, + isEqual = function(self, t) + if type(t) ~= "table" then return false end + if #self ~= #t then return false end + local ret = 0 + for i = 1, #self do + ret = _bor(ret, _bxor(self[i], t[i])) + end + return ret == 0 + end, + sub = function(self, a, b) + local len = #self+1 + local start = a%len + local stop = (b or len-1)%len + local ret = {} + local i = 1 + for j = start, stop, start Date: Mon, 20 Apr 2020 19:33:22 -0400 Subject: [PATCH 116/236] Temporary fix for blit crashing with spaces --- startup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/startup.lua b/startup.lua index b6381d1..f72fb21 100644 --- a/startup.lua +++ b/startup.lua @@ -127,7 +127,7 @@ local function splash() local opus = { 'fffff00', 'ffff07000', - 'ff00770b00 4444', + 'ff00770b00f4444', 'ff077777444444444', 'f07777744444444444', 'f0000777444444444', -- 2.49.1 From 7224d441ca6cca2115892ac8421c3ffd298b1976 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 21 Apr 2020 22:40:59 -0600 Subject: [PATCH 117/236] Ui enhancements 2.0 (#31) * canvas overhaul * minor tweaks * list mode for overview * bugfixes + tweaks for editor 2.0 * minor tweaks * more editor work * refactor + new transitions * use layout() where appropriate and cleanup * mouse triple click + textEntry scroll ind * cleanup * cleanup + theme editor * color rework + cleanup * changes for deprecated ui methods * can now use named colors --- .gitignore | 1 + sys/apps/Files.lua | 148 +++-- sys/apps/Help.lua | 13 +- sys/apps/Lua.lua | 18 +- sys/apps/Network.lua | 75 ++- sys/apps/Overview.lua | 311 ++++++---- sys/apps/PackageManager.lua | 5 +- sys/apps/Sniff.lua | 13 +- sys/apps/System.lua | 8 +- sys/apps/Tasks.lua | 7 +- sys/apps/Welcome.lua | 4 +- sys/apps/fileui.lua | 39 ++ sys/apps/inspect.lua | 23 +- sys/apps/network/keygen.lua | 2 +- sys/apps/shell.lua | 14 +- sys/apps/system/aliases.lua | 2 +- sys/apps/system/alternate.lua | 17 +- sys/apps/system/cloud.lua | 20 +- sys/apps/system/diskusage.lua | 25 +- sys/apps/system/kiosk.lua | 61 +- sys/apps/system/label.lua | 31 +- sys/apps/system/launcher.lua | 17 +- sys/apps/system/network.lua | 74 +-- sys/apps/system/password.lua | 41 +- sys/apps/system/path.lua | 7 +- sys/apps/system/settings.lua | 108 +++- sys/apps/system/shell.lua | 139 ++--- sys/apps/system/theme.lua | 89 +++ sys/autorun/clipboard.lua | 7 +- sys/autorun/upgraded.lua | 10 +- sys/etc/apps.db | 5 +- sys/help/Overview | 3 +- sys/init/1.device.lua | 2 - sys/init/2.vfs.lua | 5 - sys/init/5.network.lua | 2 - sys/init/6.packages.lua | 20 + sys/init/7.multishell.lua | 39 +- sys/kernel.lua | 2 - sys/modules/opus/array.lua | 6 +- sys/modules/opus/config.lua | 18 - sys/modules/opus/entry.lua | 18 +- sys/modules/opus/fuzzy.lua | 21 + sys/modules/opus/git.lua | 4 +- sys/modules/opus/gps.lua | 2 +- sys/modules/opus/input.lua | 37 +- sys/modules/opus/nft.lua | 56 +- sys/modules/opus/terminal.lua | 113 ++-- sys/modules/opus/ui.lua | 579 +++++++++--------- sys/modules/opus/ui/blit.lua | 174 ++++++ sys/modules/opus/ui/canvas.lua | 345 +++++------ .../opus/ui/components/ActiveLayer.lua | 32 - sys/modules/opus/ui/components/Button.lua | 32 +- sys/modules/opus/ui/components/Checkbox.lua | 14 +- sys/modules/opus/ui/components/Chooser.lua | 8 +- sys/modules/opus/ui/components/Dialog.lua | 46 +- sys/modules/opus/ui/components/DropMenu.lua | 64 +- .../opus/ui/components/DropMenuItem.lua | 14 +- sys/modules/opus/ui/components/Embedded.lua | 77 ++- sys/modules/opus/ui/components/FileSelect.lua | 118 ++++ sys/modules/opus/ui/components/FlatButton.lua | 16 + sys/modules/opus/ui/components/Form.lua | 4 +- sys/modules/opus/ui/components/Grid.lua | 41 +- sys/modules/opus/ui/components/Image.lua | 36 +- sys/modules/opus/ui/components/Menu.lua | 4 +- sys/modules/opus/ui/components/MenuBar.lua | 61 +- sys/modules/opus/ui/components/MenuItem.lua | 10 +- .../opus/ui/components/MiniSlideOut.lua | 31 + sys/modules/opus/ui/components/NftImage.lua | 11 +- .../opus/ui/components/Notification.lua | 43 +- sys/modules/opus/ui/components/Page.lua | 75 +-- .../opus/ui/components/ProgressBar.lua | 24 +- sys/modules/opus/ui/components/Question.lua | 27 + sys/modules/opus/ui/components/ScrollBar.lua | 19 +- .../opus/ui/components/ScrollingGrid.lua | 19 + sys/modules/opus/ui/components/SlideOut.lua | 45 +- sys/modules/opus/ui/components/Slider.lua | 18 +- sys/modules/opus/ui/components/StatusBar.lua | 29 +- sys/modules/opus/ui/components/Tab.lua | 9 +- sys/modules/opus/ui/components/TabBar.lua | 8 +- .../opus/ui/components/TabBarMenuItem.lua | 18 +- sys/modules/opus/ui/components/Tabs.lua | 31 +- sys/modules/opus/ui/components/Text.lua | 4 +- sys/modules/opus/ui/components/TextArea.lua | 38 +- sys/modules/opus/ui/components/TextEntry.lua | 27 +- sys/modules/opus/ui/components/Throttle.lua | 43 +- sys/modules/opus/ui/components/TitleBar.lua | 114 ++-- .../opus/ui/components/VerticalMeter.lua | 11 +- sys/modules/opus/ui/components/Viewport.lua | 101 +-- sys/modules/opus/ui/components/Wizard.lua | 12 +- sys/modules/opus/ui/components/WizardPage.lua | 5 +- sys/modules/opus/ui/transition.lua | 77 ++- sys/modules/opus/util.lua | 48 +- 92 files changed, 2471 insertions(+), 1773 deletions(-) create mode 100644 sys/apps/fileui.lua create mode 100644 sys/apps/system/theme.lua create mode 100644 sys/modules/opus/fuzzy.lua create mode 100644 sys/modules/opus/ui/blit.lua delete mode 100644 sys/modules/opus/ui/components/ActiveLayer.lua create mode 100644 sys/modules/opus/ui/components/FileSelect.lua create mode 100644 sys/modules/opus/ui/components/FlatButton.lua create mode 100644 sys/modules/opus/ui/components/MiniSlideOut.lua create mode 100644 sys/modules/opus/ui/components/Question.lua diff --git a/.gitignore b/.gitignore index b69bcf7..8852524 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /ignore +.project diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index b39ddd9..65c806d 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -82,16 +82,60 @@ local Browser = UI.Page { }, sortColumn = 'name', y = 2, ey = -2, + sortCompare = function(self, a, b) + if self.sortColumn == 'fsize' then + return a.size < b.size + elseif self.sortColumn == 'flags' then + return a.flags < b.flags + end + if a.isDir == b.isDir then + return a.name:lower() < b.name:lower() + end + return a.isDir + end, + getRowTextColor = function(_, file) + if file.marked then + return colors.green + end + if file.isDir then + return colors.cyan + end + if file.isReadOnly then + return colors.pink + end + return colors.white + end, + eventHandler = function(self, event) + if event.type == 'copy' then -- let copy be handled by parent + return false + end + return UI.ScrollingGrid.eventHandler(self, event) + end }, statusBar = UI.StatusBar { columns = { { key = 'status' }, { key = 'totalSize', width = 6 }, }, + draw = function(self) + if self.parent.dir then + local info = '#:' .. Util.size(self.parent.dir.files) + local numMarked = Util.size(marked) + if numMarked > 0 then + info = info .. ' M:' .. numMarked + end + self:setValue('info', info) + self:setValue('totalSize', formatSize(self.parent.dir.totalSize)) + UI.StatusBar.draw(self) + end + end, + }, + question = UI.Question { + y = -2, x = -19, + label = 'Delete', }, notification = UI.Notification { }, associations = UI.SlideOut { - backgroundColor = colors.cyan, menuBar = UI.MenuBar { buttons = { { text = 'Save', event = 'save' }, @@ -99,7 +143,7 @@ local Browser = UI.Page { }, }, grid = UI.ScrollingGrid { - x = 2, ex = -6, y = 3, ey = -5, + x = 2, ex = -6, y = 3, ey = -8, columns = { { heading = 'Extension', key = 'name' }, { heading = 'Program', key = 'value' }, @@ -114,8 +158,11 @@ local Browser = UI.Page { x = -4, y = 6, text = '-', event = 'remove_entry', help = 'Remove', }, + [1] = UI.Window { + x = 2, y = -6, ex = -6, ey = -3, + }, form = UI.Form { - x = 3, y = -3, ey = -2, + x = 3, y = -5, ex = -7, ey = -3, margin = 1, manualControls = true, [1] = UI.TextEntry { @@ -137,9 +184,7 @@ local Browser = UI.Page { text = 'Add', event = 'add_association', }, }, - statusBar = UI.StatusBar { - backgroundColor = colors.cyan, - }, + statusBar = UI.StatusBar { }, }, accelerators = { [ 'control-q' ] = 'quit', @@ -175,51 +220,6 @@ function Browser.menuBar:getActive(menuItem) return true end -function Browser.grid:sortCompare(a, b) - if self.sortColumn == 'fsize' then - return a.size < b.size - elseif self.sortColumn == 'flags' then - return a.flags < b.flags - end - if a.isDir == b.isDir then - return a.name:lower() < b.name:lower() - end - return a.isDir -end - -function Browser.grid:getRowTextColor(file) - if file.marked then - return colors.green - end - if file.isDir then - return colors.cyan - end - if file.isReadOnly then - return colors.pink - end - return colors.white -end - -function Browser.grid:eventHandler(event) - if event.type == 'copy' then -- let copy be handled by parent - return false - end - return UI.ScrollingGrid.eventHandler(self, event) -end - -function Browser.statusBar:draw() - if self.parent.dir then - local info = '#:' .. Util.size(self.parent.dir.files) - local numMarked = Util.size(marked) - if numMarked > 0 then - info = info .. ' M:' .. numMarked - end - self:setValue('info', info) - self:setValue('totalSize', formatSize(self.parent.dir.totalSize)) - UI.StatusBar.draw(self) - end -end - function Browser:setStatus(status, ...) self.notification:info(string.format(status, ...)) end @@ -255,7 +255,6 @@ function Browser:getDirectory(directory) end function Browser:updateDirectory(dir) - dir.size = 0 dir.totalSize = 0 Util.clear(dir.files) @@ -344,7 +343,7 @@ function Browser:eventHandler(event) local file = self.grid:getSelected() if event.type == 'quit' then - Event.exitPullEvents() + UI:quit() elseif event.type == 'edit' and file then self:run('edit', file.name) @@ -432,28 +431,25 @@ function Browser:eventHandler(event) elseif event.type == 'delete' then if self:hasMarked() then - local width = self.statusBar:getColumnWidth('status') - self.statusBar:setColumnWidth('status', UI.term.width) - self.statusBar:setValue('status', 'Delete marked? (y/n)') - self.statusBar:draw() - self.statusBar:sync() - local _, ch = os.pullEvent('char') - if ch == 'y' or ch == 'Y' then - for _,m in pairs(marked) do - pcall(function() - fs.delete(m.fullName) - end) - end - end - marked = { } - self.statusBar:setColumnWidth('status', width) - self.statusBar:setValue('status', '/' .. self.dir.name) - self:updateDirectory(self.dir) - - self.statusBar:draw() - self.grid:draw() - self:setFocus(self.grid) + self.question:show() end + return true + + elseif event.type == 'question_yes' then + for _,m in pairs(marked) do + pcall(fs.delete, m.fullName) + end + marked = { } + self:updateDirectory(self.dir) + + self.question:hide() + self.statusBar:draw() + self.grid:draw() + self:setFocus(self.grid) + + elseif event.type == 'question_no' then + self.question:hide() + self:setFocus(self.grid) elseif event.type == 'copy' or event.type == 'cut' then if self:hasMarked() then @@ -549,6 +545,4 @@ local args = Util.parse(...) Browser:setDir(args[1] or shell.dir()) UI:setPage(Browser) - -Event.pullEvents() -UI.term:reset() +UI:start() diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index 3702371..ba530c8 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -1,7 +1,6 @@ local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors local help = _G.help UI:configure('Help', ...) @@ -12,11 +11,11 @@ for _,topic in pairs(help.topics()) do end UI:addPage('main', UI.Page { - labelText = UI.Text { + UI.Text { x = 3, y = 2, value = 'Search', }, - filter = UI.TextEntry { + UI.TextEntry { x = 10, y = 2, ex = -3, limit = 32, }, @@ -38,9 +37,7 @@ UI:addPage('main', UI.Page { elseif event.type == 'grid_select' then if self.grid:getSelected() then - local name = self.grid:getSelected().name - - UI:setPage('topic', name) + UI:setPage('topic', self.grid:getSelected().name) end elseif event.type == 'text_change' then @@ -57,6 +54,7 @@ UI:addPage('main', UI.Page { self.grid:update() self.grid:setIndex(1) self.grid:draw() + else return UI.Page.eventHandler(self, event) end @@ -64,13 +62,12 @@ UI:addPage('main', UI.Page { }) UI:addPage('topic', UI.Page { - backgroundColor = colors.black, + backgroundColor = 'black', titleBar = UI.TitleBar { title = 'text', event = 'back', }, helpText = UI.TextArea { - backgroundColor = colors.black, x = 2, ex = -1, y = 3, ey = -2, }, accelerators = { diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 2de6195..44f2c78 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -58,11 +58,16 @@ local page = UI.Page { }, [2] = UI.Tab { tabTitle = 'Output', + backgroundColor = 'black', output = UI.Embedded { - visible = true, + y = 2, maxScroll = 1000, - backgroundColor = colors.black, + backgroundColor = 'black', }, + draw = function(self) + self:write(1, 1, string.rep('\131', self.width), 'black', 'primary') + self:drawChildren() + end, }, }, } @@ -157,7 +162,7 @@ function page:eventHandler(event) local sz = #value local pos = self.prompt.entry.pos self:setPrompt(autocomplete(sandboxEnv, value, self.prompt.entry.pos)) - self.prompt:setPosition(pos + #value - sz) + self.prompt:setPosition(pos + #(self.prompt.value or '') - sz) self.prompt:updateCursor() elseif event.type == 'device' then @@ -196,7 +201,6 @@ function page:eventHandler(event) command = nil self.grid:setValues(t) self.grid:setIndex(1) - self.grid:adjustWidth() self:draw() end return true @@ -243,7 +247,6 @@ function page:setResult(result) end self.grid:setValues(t) self.grid:setIndex(1) - self.grid:adjustWidth() self:draw() end @@ -373,7 +376,7 @@ function page:executeStatement(statement) end if _exit then - UI:exitPullEvents() + UI:quit() end end @@ -382,7 +385,8 @@ if args[1] then command = 'args[1]' sandboxEnv.args = args page:setResult(args[1]) + page:setPrompt(command) end UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index 6ae002a..1f03fe4 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -4,7 +4,6 @@ local Socket = require('opus.socket') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors local device = _G.device local network = _G.network local os = _G.os @@ -56,6 +55,31 @@ local page = UI.Page { columns = gridColumns, sortColumn = 'label', autospace = true, + getRowTextColor = function(self, row, selected) + if not row.active then + return 'lightGray' + end + return UI.Grid.getRowTextColor(self, row, selected) + end, + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + if row.uptime then + if row.uptime < 60 then + row.uptime = string.format("%ds", math.floor(row.uptime)) + elseif row.uptime < 3600 then + row.uptime = string.format("%sm", math.floor(row.uptime / 60)) + else + row.uptime = string.format("%sh", math.floor(row.uptime / 3600)) + end + end + if row.fuel then + row.fuel = row.fuel > 0 and Util.toBytes(row.fuel) or '' + end + if row.distance then + row.distance = Util.toBytes(Util.round(row.distance, 1)) + end + return row + end, }, ports = UI.SlideOut { titleBar = UI.TitleBar { @@ -72,17 +96,22 @@ local page = UI.Page { sortColumn = 'port', autospace = true, }, + eventHandler = function(self, event) + if event.type == 'grid_select' then + shell.openForegroundTab('Sniff ' .. event.selected.port) + end + return UI.SlideOut.eventHandler(self, event) + end, }, help = UI.SlideOut { - backgroundColor = colors.cyan, x = 5, ex = -5, height = 8, y = -8, titleBar = UI.TitleBar { title = 'Network Help', event = 'slide_hide', }, text = UI.TextArea { - x = 2, y = 2, - backgroundColor = colors.cyan, + x = 1, y = 2, + marginLeft = 1, value = [[ In order to connect to another computer: @@ -127,13 +156,6 @@ local function sendCommand(host, command) end end -function page.ports:eventHandler(event) - if event.type == 'grid_select' then - shell.openForegroundTab('Sniff ' .. event.selected.port) - end - return UI.SlideOut.eventHandler(self, event) -end - function page.ports.grid:update() local transport = network:getTransport() @@ -230,7 +252,7 @@ function page:eventHandler(event) Config.update('network', config) elseif event.type == 'quit' then - Event.exitPullEvents() + UI:quit() end UI.Page.eventHandler(self, event) end @@ -243,33 +265,6 @@ function page.menuBar:getActive(menuItem) return menuItem.noCheck or not not t end -function page.grid:getRowTextColor(row, selected) - if not row.active then - return colors.lightGray - end - return UI.Grid.getRowTextColor(self, row, selected) -end - -function page.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - if row.uptime then - if row.uptime < 60 then - row.uptime = string.format("%ds", math.floor(row.uptime)) - elseif row.uptime < 3600 then - row.uptime = string.format("%sm", math.floor(row.uptime / 60)) - else - row.uptime = string.format("%sh", math.floor(row.uptime / 3600)) - end - end - if row.fuel then - row.fuel = row.fuel > 0 and Util.toBytes(row.fuel) or '' - end - if row.distance then - row.distance = Util.toBytes(Util.round(row.distance, 1)) - end - return row -end - Event.onInterval(1, function() page.grid:update() page.grid:draw() @@ -295,4 +290,4 @@ if not device.wireless_modem then end UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index c93f516..b879fd0 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -1,4 +1,5 @@ local Alt = require('opus.alternate') +local Array = require('opus.array') local class = require('opus.class') local Config = require('opus.config') local Event = require('opus.event') @@ -9,7 +10,6 @@ local Tween = require('opus.ui.tween') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors local device = _G.device local fs = _G.fs local os = _G.os @@ -18,6 +18,12 @@ local shell = _ENV.shell local term = _G.term local turtle = _G.turtle +--[[ + turtle: 39x13 + computer: 51x19 + pocket: 26x20 +]] + if not _ENV.multishell then error('multishell is required') end @@ -26,6 +32,9 @@ local REGISTRY_DIR = 'usr/.registry' local DEFAULT_ICON = NFT.parse("\0308\0317\153\153\153\153\153\ \0307\0318\153\153\153\153\153\ \0308\0317\153\153\153\153\153") +local TRANS_ICON = NFT.parse("\0302\0312\32\32\32\32\32\ +\0302\0312\32\32\32\32\32\ +\0302\0312\32\32\32\32\32") -- overview local uid = _ENV.multishell.getCurrent() @@ -65,6 +74,7 @@ local function parseIcon(iconText) if icon.height > 3 or icon.width > 8 then error('Must be an NFT image - 3 rows, 8 cols max') end + NFT.transparency(icon) end return icon end) @@ -76,45 +86,38 @@ local function parseIcon(iconText) return s, m end -UI.VerticalTabBar = class(UI.TabBar) -function UI.VerticalTabBar:setParent() - self.x = 1 - self.width = 8 - self.height = nil - self.ey = -2 - UI.TabBar.setParent(self) - for k,c in pairs(self.children) do - c.x = 1 - c.y = k + 1 - c.ox, c.oy = c.x, c.y - c.ow = 8 - c.width = 8 - end -end - -local cx = 9 -local cy = 1 - local page = UI.Page { container = UI.Viewport { - x = cx, - y = cy, + x = 9, y = 1, + }, + tabBar = UI.TabBar { + ey = -2, + width = 8, + selectedBackgroundColor = 'primary', + backgroundColor = 'tertiary', + layout = function(self) + self.height = nil + UI.TabBar.layout(self) + end, }, tray = UI.Window { y = -1, width = 8, - backgroundColor = colors.lightGray, - newApp = UI.Button { + backgroundColor = 'tertiary', + newApp = UI.FlatButton { + x = 2, text = '+', event = 'new', }, - --[[ - volume = UI.Button { - x = 3, - text = '\15', event = 'volume', - },]] + mode = UI.FlatButton { + x = 4, + text = '=', event = 'display_mode', + }, + help = UI.FlatButton { + x = 6, + text = '?', event = 'help', + }, }, editor = UI.SlideOut { y = -12, height = 12, - backgroundColor = colors.cyan, titleBar = UI.TitleBar { title = 'Edit Application', event = 'slide_hide', @@ -122,7 +125,7 @@ local page = UI.Page { form = UI.Form { y = 2, ey = -2, [1] = UI.TextEntry { - formLabel = 'Title', formKey = 'title', limit = 11, help = 'Application title', + formLabel = 'Title', formKey = 'title', limit = 11, width = 13, help = 'Application title', required = true, }, [2] = UI.TextEntry { @@ -130,23 +133,50 @@ local page = UI.Page { required = true, }, [3] = UI.TextEntry { - formLabel = 'Category', formKey = 'category', limit = 11, help = 'Category of application', + formLabel = 'Category', formKey = 'category', limit = 6, width = 8, help = 'Category of application', required = true, }, - iconFile = UI.TextEntry { - x = 11, ex = -12, y = 7, - limit = 128, help = 'Path to icon file', - shadowText = 'Path to icon file', + editIcon = UI.Button { + x = 11, y = 6, + text = 'Edit', event = 'editIcon', help = 'Edit icon file', }, loadIcon = UI.Button { - x = 11, y = 9, + x = 11, y = 8, + text = 'Load', event = 'loadIcon', help = 'Load icon file', + }, + helpIcon = UI.Button { + x = 11, y = 8, text = 'Load', event = 'loadIcon', help = 'Load icon file', }, image = UI.NftImage { - backgroundColor = colors.black, - y = 7, x = 2, height = 3, width = 8, + backgroundColor = 'black', + y = 6, x = 2, height = 3, width = 8, }, }, + file_open = UI.FileSelect { + modal = true, + enable = function() end, + transitionHint = 'expandUp', + show = function(self) + UI.FileSelect.enable(self) + self:focusFirst() + self:draw() + end, + disable = function(self) + UI.FileSelect.disable(self) + self.parent:focusFirst() + -- need to recapture as we are opening a modal within another modal + self.parent:capture(self.parent) + end, + eventHandler = function(self, event) + if event.type == 'select_cancel' then + self:disable() + elseif event.type == 'select_file' then + self:disable() + end + return UI.FileSelect.eventHandler(self, event) + end, + }, notification = UI.Notification(), statusBar = UI.StatusBar(), }, @@ -205,7 +235,7 @@ local function loadApplications() return requirements[a.requires] end - return true -- Util.startsWith(a.run, 'http') or shell.resolveProgram(a.run) + return true end) local categories = { } @@ -215,6 +245,7 @@ local function loadApplications() categories[f.category] = true table.insert(buttons, { text = f.category, + width = 8, selected = config.currentCategory == f.category }) end @@ -222,13 +253,13 @@ local function loadApplications() table.sort(buttons, function(a, b) return a.text < b.text end) table.insert(buttons, 1, { text = 'Recent' }) - Util.removeByValue(page.children, page.tabBar) + for k,v in pairs(buttons) do + v.x = 1 + v.y = k + 1 + end - page:add { - tabBar = UI.VerticalTabBar { - buttons = buttons, - }, - } + page.tabBar.children = { } + page.tabBar:addButtons(buttons) --page.tabBar:selectTab(config.currentCategory or 'Apps') page.container:setCategory(config.currentCategory or 'Apps') @@ -243,7 +274,6 @@ UI.Icon.defaults = { function UI.Icon:eventHandler(event) if event.type == 'mouse_click' then self:setFocus(self.button) - --self:emit({ type = self.button.event, button = self.button }) return true elseif event.type == 'mouse_doubleclick' then self:emit({ type = self.button.event, button = self.button }) @@ -259,37 +289,23 @@ function page.container:setCategory(categoryName, animate) self.children = { } self:reset() - local function filter(it, f) - local ot = { } - for _,v in pairs(it) do - if f(v) then - table.insert(ot, v) - end - end - return ot - end - - local filtered + local filtered = { } if categoryName == 'Recent' then - filtered = { } - for _,v in ipairs(config.Recent) do local app = Util.find(applications, 'key', v) - if app then -- and fs.exists(app.run) then + if app then table.insert(filtered, app) end end - else - filtered = filter(applications, function(a) - return a.category == categoryName -- and fs.exists(a.run) + filtered = Array.filter(applications, function(a) + return a.category == categoryName end) table.sort(filtered, function(a, b) return a.title < b.title end) end for _,program in ipairs(filtered) do - local icon if extSupport and program.iconExt then icon = parseIcon(program.iconExt) @@ -304,27 +320,43 @@ function page.container:setCategory(categoryName, animate) local title = ellipsis(program.title, 8) local width = math.max(icon.width + 2, #title + 2) - table.insert(self.children, UI.Icon({ - width = width, - image = UI.NftImage({ - x = math.floor((width - icon.width) / 2) + 1, - image = icon, - width = 5, - height = 3, - }), - button = UI.Button({ - x = math.floor((width - #title - 2) / 2) + 1, - y = 4, - text = title, - backgroundColor = self.backgroundColor, - backgroundFocusColor = colors.gray, - textColor = colors.white, - textFocusColor = colors.white, - width = #title + 2, - event = 'button', - app = program, - }), - })) + if config.listMode then + table.insert(self.children, UI.Icon { + width = self.width - 2, + height = 1, + UI.Button { + x = 1, ex = -1, + text = program.title, + centered = false, + backgroundColor = self:getProperty('backgroundColor'), + backgroundFocusColor = 'gray', + textColor = 'white', + textFocusColor = 'white', + event = 'button', + app = program, + } + }) + else + table.insert(self.children, UI.Icon({ + width = width, + image = UI.NftImage({ + x = math.floor((width - icon.width) / 2) + 1, + image = icon, + }), + button = UI.Button({ + x = math.floor((width - #title - 2) / 2) + 1, + y = 4, + text = title, + backgroundColor = self:getProperty('backgroundColor'), + backgroundFocusColor = 'gray', + textColor = 'white', + textFocusColor = 'white', + width = #title + 2, + event = 'button', + app = program, + }), + })) + end end local gutter = 2 @@ -334,7 +366,8 @@ function page.container:setCategory(categoryName, animate) local col, row = gutter, 2 local count = #self.children - local r = math.random(1, 5) + local r = math.random(1, 7) + local frames = 5 -- reposition all children for k,child in ipairs(self.children) do if r == 1 then @@ -356,19 +389,27 @@ function page.container:setCategory(categoryName, animate) child.x = self.width child.y = self.height - 3 end + elseif r == 6 then + child.x = col + child.y = 1 + elseif r == 7 then + child.x = 1 + child.y = self.height - 3 end - child.tween = Tween.new(6, child, { x = col, y = row }, 'linear') + child.tween = Tween.new(frames, child, { x = col, y = row }, 'inQuad') if not animate then child.x = col child.y = row end + self:setViewHeight(row + (config.listMode and 1 or 4)) + if k < count then col = col + child.width if col + self.children[k + 1].width + gutter - 2 > self.width then col = gutter - row = row + 5 + row = row + (config.listMode and 1 or 5) end end end @@ -378,15 +419,12 @@ function page.container:setCategory(categoryName, animate) local function transition() local i = 1 return function() - self:clear() for _,child in pairs(self.children) do child.tween:update(1) - child.x = math.floor(child.x) - child.y = math.floor(child.y) - child:draw() + child:move(math.floor(child.x), math.floor(child.y)) end i = i + 1 - return i < 7 + return i <= frames end end self:addTransition(transition) @@ -439,6 +477,9 @@ function page:eventHandler(event) elseif event.type == 'network' then shell.switchTab(shell.openTab('network')) + elseif event.type == 'help' then + shell.switchTab(shell.openTab('Help Overview')) + elseif event.type == 'focus_change' then if event.focused.parent.UIElement == 'Icon' then event.focused.parent:scrollIntoView() @@ -473,6 +514,13 @@ function page:eventHandler(event) end self.editor:show({ category = category }) + elseif event.type == 'display_mode' then + config.listMode = not config.listMode + Config.update('Overview', config) + loadApplications() + self:refresh() + self:draw() + elseif event.type == 'edit' then local focused = page:getFocused() if focused.app then @@ -480,7 +528,7 @@ function page:eventHandler(event) end else - UI.Page.eventHandler(self, event) + return UI.Page.eventHandler(self, event) end return true end @@ -502,11 +550,6 @@ function page.editor:show(app) self:focusFirst() end -function page.editor.form.image:draw() - self:clear() - UI.NftImage.draw(self) -end - function page.editor:updateApplications(app) if not app.key then app.key = SHA.compute(app.title) @@ -516,36 +559,51 @@ function page.editor:updateApplications(app) loadApplications() end +function page.editor:loadImage(filename) + local s, m = pcall(function() + local iconLines = Util.readFile(filename) + if not iconLines then + error('Must be an NFT image - 3 rows, 8 cols max') + end + local icon, m = parseIcon(iconLines) + if not icon then + error(m) + end + if extSupport then + self.form.values.iconExt = iconLines + else + self.form.values.icon = iconLines + end + self.form.image:setImage(icon) + self.form.image:draw() + end) + if not s and m then + local msg = m:gsub('.*: (.*)', '%1') + self.notification:error(msg) + end +end + function page.editor:eventHandler(event) if event.type == 'form_cancel' or event.type == 'cancel' then self:hide() elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help or '') - self.statusBar:draw() + + elseif event.type == 'editIcon' then + local filename = '/tmp/editing.nft' + NFT.save(self.form.image.image or TRANS_ICON, filename) + local success = shell.run('pain.lua ' .. filename) + self.parent:dirty(true) + if success then + self:loadImage(filename) + end + + elseif event.type == 'select_file' then + self:loadImage(event.file) elseif event.type == 'loadIcon' then - local s, m = pcall(function() - local iconLines = Util.readFile(self.form.iconFile.value) - if not iconLines then - error('Must be an NFT image - 3 rows, 8 cols max') - end - local icon, m = parseIcon(iconLines) - if not icon then - error(m) - end - if extSupport then - self.form.values.iconExt = iconLines - else - self.form.values.icon = iconLines - end - self.form.image:setImage(icon) - self.form.image:draw() - end) - if not s and m then - local msg = m:gsub('.*: (.*)', '%1') - self.notification:error(msg) - end + self.file_open:show() elseif event.type == 'form_invalid' then self.notification:error(event.message) @@ -554,8 +612,6 @@ function page.editor:eventHandler(event) local values = self.form.values self:hide() self:updateApplications(values) - --page:refresh() - --page:draw() config.currentCategory = values.category Config.update('Overview', config) os.queueEvent('overview_refresh') @@ -565,10 +621,6 @@ function page.editor:eventHandler(event) return true end -UI:setPages({ - main = page, -}) - local function reload() loadApplications() page:refresh() @@ -594,5 +646,4 @@ end) loadApplications() UI:setPage(page) - -UI:pullEvents() +UI:start() diff --git a/sys/apps/PackageManager.lua b/sys/apps/PackageManager.lua index 3a3dd8b..ec8d9ea 100644 --- a/sys/apps/PackageManager.lua +++ b/sys/apps/PackageManager.lua @@ -44,7 +44,6 @@ local page = UI.Page { marginRight = 0, marginLeft = 0, }, action = UI.SlideOut { - backgroundColor = colors.cyan, titleBar = UI.TitleBar { event = 'hide-action', }, @@ -184,7 +183,7 @@ function page:eventHandler(event) self.action.button:draw() elseif event.type == 'quit' then - UI:exitPullEvents() + UI:quit() end UI.Page.eventHandler(self, event) end @@ -196,4 +195,4 @@ Packages:downloadList() page:loadPackages() page:sync() -UI:pullEvents() +UI:start() diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 3b4ca9c..bec9ac0 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -5,14 +5,13 @@ local Util = require('opus.util') local colors = _G.colors local device = _G.device local textutils = _G.textutils -local peripheral = _G.peripheral local multishell = _ENV.multishell local gridColumns = {} table.insert(gridColumns, { heading = '#', key = 'id', width = 5, align = 'right' }) table.insert(gridColumns, { heading = 'Port', key = 'portid', width = 5, align = 'right' }) table.insert(gridColumns, { heading = 'Reply', key = 'replyid', width = 5, align = 'right' }) -if UI.defaultDevice.width > 50 then +if UI.term.width > 50 then table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' }) end table.insert(gridColumns, { heading = 'Msg', key = 'packetStr' }) @@ -42,12 +41,13 @@ local page = UI.Page { configSlide = UI.SlideOut { y = -11, - titleBar = UI.TitleBar { title = 'Sniffer Config', event = 'config_close' }, + titleBar = UI.TitleBar { title = 'Sniffer Config', event = 'config_close', backgroundColor = colors.black }, accelerators = { ['backspace'] = 'config_close' }, configTabs = UI.Tabs { y = 2, filterTab = UI.Tab { tabTitle = 'Filter', + noFill = true, filterGridText = UI.Text { x = 2, y = 2, value = 'ID filter', @@ -130,7 +130,6 @@ local page = UI.Page { title = 'Packet Information', event = 'packet_close', }, - backgroundColor = colors.cyan, accelerators = { ['backspace'] = 'packet_close', ['left'] = 'prev_packet', @@ -280,7 +279,7 @@ function page.packetSlide:eventHandler(event) end function page.packetGrid:getDisplayValues(row) - local row = Util.shallowCopy(row) + row = Util.shallowCopy(row) row.distance = Util.toBytes(Util.round(row.distance), 2) return row end @@ -356,7 +355,7 @@ function page:eventHandler(event) self.packetSlide:show(event.selected) elseif event.type == 'quit' then - Event.exitPullEvents() + UI:quit() else return UI.Page.eventHandler(self, event) end @@ -386,4 +385,4 @@ if args[1] then end UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/sys/apps/System.lua b/sys/apps/System.lua index b18200d..7005d83 100644 --- a/sys/apps/System.lua +++ b/sys/apps/System.lua @@ -11,7 +11,7 @@ local systemPage = UI.Page { settings = UI.Tab { tabTitle = 'Category', grid = UI.ScrollingGrid { - y = 2, + x = 2, y = 2, ex = -2, ey = -2, columns = { { heading = 'Name', key = 'name' }, { heading = 'Description', key = 'description' }, @@ -35,14 +35,14 @@ function systemPage.tabs.settings:eventHandler(event) tab:disable() end systemPage.tabs:selectTab(tab) - self.parent:draw() + --self.parent:draw() return true end end function systemPage:eventHandler(event) if event.type == 'quit' then - UI:exitPullEvents() + UI:quit() elseif event.type == 'success_message' then self.notification:success(event.message) @@ -82,4 +82,4 @@ local plugins = loadDirectory(fs.combine(programDir, 'system'), { }) systemPage.tabs.settings.grid:setValues(plugins) UI:setPage(systemPage) -UI:pullEvents() +UI:start() diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index cde082a..f8c2edd 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -3,6 +3,7 @@ local UI = require('opus.ui') local kernel = _G.kernel local multishell = _ENV.multishell +local tasks = multishell and multishell.getTabs and multishell.getTabs() or kernel.routines UI:configure('Tasks', ...) @@ -21,7 +22,7 @@ local page = UI.Page { { heading = 'Status', key = 'status' }, { heading = 'Time', key = 'timestamp' }, }, - values = kernel.routines, + values = tasks, sortColumn = 'uid', autospace = true, getDisplayValues = function (_, row) @@ -51,7 +52,7 @@ local page = UI.Page { end end if event.type == 'quit' then - Event.exitPullEvents() + UI:quit() end UI.Page.eventHandler(self, event) end @@ -64,4 +65,4 @@ Event.onInterval(1, function() end) UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 980f27f..1154fbf 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -131,7 +131,7 @@ function page:eventHandler(event) shell.openForegroundTab('PackageManager') elseif event.type == 'wizard_complete' or event.type == 'cancel' then - UI.exitPullEvents() + UI:quit() else return UI.Page.eventHandler(self, event) @@ -140,4 +140,4 @@ function page:eventHandler(event) end UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/sys/apps/fileui.lua b/sys/apps/fileui.lua new file mode 100644 index 0000000..1f95f5e --- /dev/null +++ b/sys/apps/fileui.lua @@ -0,0 +1,39 @@ +local UI = require('opus.ui') +local Util = require('opus.util') + +local shell = _ENV.shell +local multishell = _ENV.multishell + +-- fileui [--path=path] [--exec=filename] [--title=title] + +local page = UI.Page { + fileselect = UI.FileSelect { }, + eventHandler = function(self, event) + if event.type == 'select_file' then + self.selected = event.file + UI:quit() + + elseif event.type == 'select_cancel' then + UI:quit() + end + + return UI.FileSelect.eventHandler(self, event) + end, +} + +local _, args = Util.parse(...) + +if args.title and multishell then + multishell.setTitle(multishell.getCurrent(), args.title) +end + +UI:setPage(page, args.path) +UI:start() +UI.term:setCursorBlink(false) + +if args.exec and page.selected then + shell.openForegroundTab(string.format('%s %s', args.exec, page.selected)) + return +end + +return page.selected diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 1d4f95b..35a9f5c 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -133,6 +133,12 @@ page = UI.Page { }, }, }, + accelerators = { + ['shift-right'] = 'size', + ['shift-left' ] = 'size', + ['shift-up' ] = 'size', + ['shift-down' ] = 'size', + }, eventHandler = function (self, event) if event.type == 'focus_change' and isRelevant(event.focused) then focused = event.focused @@ -144,7 +150,6 @@ page = UI.Page { }) end self.tabs.properties.grid:setValues(t) - self.tabs.properties.grid:update() self.tabs.properties.grid:draw() t = { } @@ -156,7 +161,6 @@ page = UI.Page { end end self.tabs.methodsTab.grid:setValues(t) - self.tabs.methodsTab.grid:update() self.tabs.methodsTab.grid:draw() elseif event.type == 'edit_property' then @@ -168,6 +172,19 @@ page = UI.Page { elseif event.type == 'editor_apply' then self.editor:hide() + + elseif event.type == 'size' then + local sizing = { + ['shift-right'] = { 1, 0 }, + ['shift-left' ] = { -1, 0 }, + ['shift-up' ] = { 0, -1 }, + ['shift-down' ] = { 0, 1 }, + } + self.ox = math.max(self.ox + sizing[event.ie.code][1], 1) + self.oy = math.max(self.oy + sizing[event.ie.code][2], 1) + UI.term:clear() + self:resize() + self:draw() end return UI.Page.eventHandler(self, event) @@ -175,4 +192,4 @@ page = UI.Page { } UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/sys/apps/network/keygen.lua b/sys/apps/network/keygen.lua index 4a74670..ea2ae40 100644 --- a/sys/apps/network/keygen.lua +++ b/sys/apps/network/keygen.lua @@ -33,7 +33,7 @@ Event.on('generate_keypair', function() table.insert(keyPairs, { generateKeyPair() }) _G._syslog('Generated keypair in ' .. timer()) if #keyPairs >= 3 then - break + break end end end) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 6124a3d..7036afa 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -66,11 +66,11 @@ local function run(env, ...) _ENV.multishell.setTitle(_ENV.multishell.getCurrent(), fs.getName(path):match('([^%.]+)')) end - if isUrl then - tProgramStack[#tProgramStack + 1] = path -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$") - else - tProgramStack[#tProgramStack + 1] = path - end + tProgramStack[#tProgramStack + 1] = { + path = path, -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$") + env = env, + args = args, + } env[ "arg" ] = { [0] = path, table.unpack(args) } local r = { fn(table.unpack(args)) } @@ -278,6 +278,10 @@ function shell.getCompletionInfo() end function shell.getRunningProgram() + return tProgramStack[#tProgramStack] and tProgramStack[#tProgramStack].path +end + +function shell.getRunningInfo() return tProgramStack[#tProgramStack] end diff --git a/sys/apps/system/aliases.lua b/sys/apps/system/aliases.lua index 6b04c5c..2d122bb 100644 --- a/sys/apps/system/aliases.lua +++ b/sys/apps/system/aliases.lua @@ -20,7 +20,7 @@ local aliasTab = UI.Tab { }, }, grid = UI.Grid { - y = 5, + x = 2, y = 5, ex = -2, ey = -2, sortColumn = 'alias', columns = { { heading = 'Alias', key = 'alias' }, diff --git a/sys/apps/system/alternate.lua b/sys/apps/system/alternate.lua index 6894428..e903074 100644 --- a/sys/apps/system/alternate.lua +++ b/sys/apps/system/alternate.lua @@ -2,8 +2,6 @@ local Array = require('opus.array') local Config = require('opus.config') local UI = require('opus.ui') -local colors = _G.colors - local tab = UI.Tab { tabTitle = 'Preferred', description = 'Select preferred applications', @@ -22,20 +20,19 @@ local tab = UI.Tab { disableHeader = true, columns = { { key = 'file' }, - } + }, + getRowTextColor = function(self, row) + if row == self.values[1] then + return 'yellow' + end + return UI.Grid.getRowTextColor(self, row) + end, }, statusBar = UI.StatusBar { values = 'Double-click to set as preferred' }, } -function tab.choices:getRowTextColor(row) - if row == self.values[1] then - return colors.yellow - end - return UI.Grid.getRowTextColor(self, row) -end - function tab:updateChoices() local app = self.apps:getSelected().name local choices = { } diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index 66078f6..d837f2e 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -2,18 +2,17 @@ local Ansi = require('opus.ansi') local Config = require('opus.config') local UI = require('opus.ui') -local colors = _G.colors - --- -t80x30 - if _G.http.websocket then local config = Config.load('cloud') local tab = UI.Tab { tabTitle = 'Cloud', description = 'Cloud Catcher options', + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 4, + }, key = UI.TextEntry { - x = 3, ex = -3, y = 2, + x = 3, ex = -3, y = 3, limit = 32, value = config.key, shadowText = 'Cloud key', @@ -22,14 +21,15 @@ if _G.http.websocket then }, }, button = UI.Button { - x = 3, y = 4, - text = 'Update', + x = -8, ex = -2, y = -2, + text = 'Apply', event = 'update_key', }, labelText = UI.TextArea { - x = 3, ex = -3, y = 6, - textColor = colors.yellow, - marginLeft = 0, marginRight = 0, + x = 2, ex = -2, y = 5, ey = -4, + textColor = 'yellow', + backgroundColor = 'black', + marginLeft = 1, marginRight = 1, marginTop = 1, value = string.format( [[Use a non-changing cloud key. Note that only a single computer can use this session at one time. To obtain a key, visit: diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 98b8516..c0db595 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -20,8 +20,8 @@ local tab = UI.Tab { description = 'Visualise HDD and disks usage', drives = UI.ScrollingGrid { - x = 2, y = 1, - ex = '47%', ey = -7, + x = 2, y = 2, + ex = '47%', ey = -8, columns = { { heading = 'Drive', key = 'name' }, { heading = 'Side' ,key = 'side', textColor = colors.yellow } @@ -30,7 +30,7 @@ local tab = UI.Tab { }, infos = UI.Grid { x = '52%', y = 2, - ex = -2, ey = -4, + ex = -2, ey = -8, disableHeader = true, unfocusedBackgroundSelectedColor = colors.black, inactive = true, @@ -40,18 +40,23 @@ local tab = UI.Tab { { key = 'value', align = 'right', textColor = colors.yellow }, } }, - + [1] = UI.Window { + x = 2, y = -6, ex = -2, ey = -2, + backgroundColor = colors.black, + }, progress = UI.ProgressBar { - x = 11, y = -2, - ex = -2, + x = 11, y = -3, + ex = -3, }, percentage = UI.Text { - x = 11, y = -3, - ex = '47%', - align = 'center', + y = -4, width = 5, + x = 12, + --align = 'center', + backgroundColor = colors.black, }, icon = UI.NftImage { - x = 2, y = -5, + x = 2, y = -6, ey = -2, + backgroundColor = colors.black, image = NFT.parse(NftImages.blank) }, } diff --git a/sys/apps/system/kiosk.lua b/sys/apps/system/kiosk.lua index becee72..2f9510b 100644 --- a/sys/apps/system/kiosk.lua +++ b/sys/apps/system/kiosk.lua @@ -4,11 +4,11 @@ local colors = _G.colors local peripheral = _G.peripheral local settings = _G.settings -local tab = UI.Tab { +return peripheral.find('monitor') and UI.Tab { tabTitle = 'Kiosk', description = 'Kiosk options', form = UI.Form { - x = 2, ex = -2, + x = 2, y = 2, ex = -2, ey = 5, manualControls = true, monitor = UI.Chooser { formLabel = 'Monitor', formKey = 'monitor', @@ -22,41 +22,36 @@ local tab = UI.Tab { }, help = 'Adjust text scaling', }, - labelText = UI.TextArea { - x = 2, ex = -2, y = 5, - textColor = colors.yellow, - value = 'Settings apply to kiosk mode selected during startup' - }, }, -} + labelText = UI.TextArea { + x = 2, ex = -2, y = 7, ey = -2, + textColor = colors.yellow, + backgroundColor = colors.black, + value = 'Settings apply to kiosk mode selected during startup' + }, + enable = function(self) + local choices = { } -function tab:enable() - local choices = { } + peripheral.find('monitor', function(side) + table.insert(choices, { name = side, value = side }) + end) - peripheral.find('monitor', function(side) - table.insert(choices, { name = side, value = side }) - end) + self.form.monitor.choices = choices + self.form.monitor.value = settings.get('kiosk.monitor') - self.form.monitor.choices = choices - self.form.monitor.value = settings.get('kiosk.monitor') + self.form.textScale.value = settings.get('kiosk.textscale') - self.form.textScale.value = settings.get('kiosk.textscale') - - UI.Tab.enable(self) -end - -function tab:eventHandler(event) - if event.type == 'choice_change' then - if self.form.monitor.value then - settings.set('kiosk.monitor', self.form.monitor.value) + UI.Tab.enable(self) + end, + eventHandler = function(self, event) + if event.type == 'choice_change' then + if self.form.monitor.value then + settings.set('kiosk.monitor', self.form.monitor.value) + end + if self.form.textScale.value then + settings.set('kiosk.textscale', self.form.textScale.value) + end + settings.save('.settings') end - if self.form.textScale.value then - settings.set('kiosk.textscale', self.form.textScale.value) - end - settings.save('.settings') end -end - -if peripheral.find('monitor') then - return tab -end +} diff --git a/sys/apps/system/label.lua b/sys/apps/system/label.lua index 0dc77a7..8fe3d1e 100644 --- a/sys/apps/system/label.lua +++ b/sys/apps/system/label.lua @@ -4,23 +4,26 @@ local Util = require('opus.util') local fs = _G.fs local os = _G.os -local labelTab = UI.Tab { +return UI.Tab { tabTitle = 'Label', description = 'Set the computer label', labelText = UI.Text { - x = 3, y = 2, + x = 3, y = 3, value = 'Label' }, label = UI.TextEntry { - x = 9, y = 2, ex = -4, + x = 9, y = 3, ex = -4, limit = 32, value = os.getComputerLabel(), accelerators = { enter = 'update_label', }, }, + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 4, + }, grid = UI.ScrollingGrid { - y = 3, + x = 2, y = 5, ex = -2, ey = -2, values = { { name = '', value = '' }, { name = 'CC version', value = Util.getVersion() }, @@ -30,20 +33,18 @@ local labelTab = UI.Tab { { name = 'Computer ID', value = tostring(os.getComputerID()) }, { name = 'Day', value = tostring(os.day()) }, }, + disableHeader = true, inactive = true, columns = { { key = 'name', width = 12 }, - { key = 'value' }, + { key = 'value', textColor = colors.yellow }, }, }, + eventHandler = function(self, event) + if event.type == 'update_label' and self.label.value then + os.setComputerLabel(self.label.value) + self:emit({ type = 'success_message', message = 'Label updated' }) + return true + end + end, } - -function labelTab:eventHandler(event) - if event.type == 'update_label' and self.label.value then - os.setComputerLabel(self.label.value) - self:emit({ type = 'success_message', message = 'Label updated' }) - return true - end -end - -return labelTab diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index 6bd290c..9c87fe9 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -9,12 +9,15 @@ local config = Config.load('multishell') local tab = UI.Tab { tabTitle = 'Launcher', description = 'Set the application launcher', + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 5, + }, launcherLabel = UI.Text { - x = 3, y = 2, + x = 3, y = 3, value = 'Launcher', }, launcher = UI.Chooser { - x = 13, y = 2, width = 12, + x = 13, y = 3, width = 12, choices = { { name = 'Overview', value = 'sys/apps/Overview.lua' }, { name = 'Shell', value = 'sys/apps/ShellLauncher.lua' }, @@ -22,18 +25,20 @@ local tab = UI.Tab { }, }, custom = UI.TextEntry { - x = 13, ex = -3, y = 3, + x = 13, ex = -3, y = 4, limit = 128, shadowText = 'File name', }, button = UI.Button { - x = 3, y = 5, - text = 'Update', + x = -8, ex = -2, y = -2, + text = 'Apply', event = 'update', }, labelText = UI.TextArea { - x = 3, ex = -3, y = 7, + x = 2, ex = -2, y = 6, ey = -4, + backgroundColor = colors.black, textColor = colors.yellow, + marginLeft = 1, marginRight = 1, marginTop = 1, value = 'Choose an application launcher', }, } diff --git a/sys/apps/system/network.lua b/sys/apps/system/network.lua index 414c080..b4fd802 100644 --- a/sys/apps/system/network.lua +++ b/sys/apps/system/network.lua @@ -2,59 +2,61 @@ local Ansi = require('opus.ansi') local Config = require('opus.config') local UI = require('opus.ui') +local colors = _G.colors local device = _G.device -local tab = UI.Tab { +return UI.Tab { tabTitle = 'Network', description = 'Networking options', info = UI.TextArea { - x = 3, y = 4, + x = 2, y = 5, ex = -2, ey = -2, + backgroundColor = colors.black, + marginLeft = 1, marginRight = 1, marginTop = 1, value = string.format( [[%sSet the primary modem used for wireless communications.%s Reboot to take effect.]], Ansi.yellow, Ansi.reset) }, + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 4, + }, label = UI.Text { - x = 3, y = 2, + x = 3, y = 3, value = 'Modem', }, modem = UI.Chooser { - x = 10, ex = -3, y = 2, + x = 10, ex = -3, y = 3, nochoice = 'auto', }, -} + enable = function(self) + local width = 7 + local choices = { + { name = 'auto', value = 'auto' }, + { name = 'disable', value = 'none' }, + } -function tab:enable() - local width = 7 - local choices = { - { name = 'auto', value = 'auto' }, - { name = 'disable', value = 'none' }, - } + for k,v in pairs(device) do + if v.isWireless and v.isWireless() and k ~= 'wireless_modem' then + table.insert(choices, { name = k, value = v.name }) + width = math.max(width, #k) + end + end - for k,v in pairs(device) do - if v.isWireless and v.isWireless() and k ~= 'wireless_modem' then - table.insert(choices, { name = k, value = v.name }) - width = math.max(width, #k) + self.modem.choices = choices + --self.modem.width = width + 4 + + local config = Config.load('os') + self.modem.value = config.wirelessModem or 'auto' + + UI.Tab.enable(self) + end, + eventHandler = function(self, event) + if event.type == 'choice_change' then + local config = Config.load('os') + config.wirelessModem = self.modem.value + Config.update('os', config) + self:emit({ type = 'success_message', message = 'reboot to take effect' }) + return true end end - - self.modem.choices = choices - --self.modem.width = width + 4 - - local config = Config.load('os') - self.modem.value = config.wirelessModem or 'auto' - - UI.Tab.enable(self) -end - -function tab:eventHandler(event) - if event.type == 'choice_change' then - local config = Config.load('os') - config.wirelessModem = self.modem.value - Config.update('os', config) - self:emit({ type = 'success_message', message = 'reboot to take effect' }) - return true - end -end - -return tab +} diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index d822996..8040c76 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -2,11 +2,12 @@ local Security = require('opus.security') local SHA = require('opus.crypto.sha2') local UI = require('opus.ui') -local colors = _G.colors - -local passwordTab = UI.Tab { +return UI.Tab { tabTitle = 'Password', description = 'Wireless network password', + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 4, + }, newPass = UI.TextEntry { x = 3, ex = -3, y = 3, limit = 32, @@ -17,28 +18,28 @@ local passwordTab = UI.Tab { }, }, button = UI.Button { - x = 3, y = 5, - text = 'Update', + x = -8, ex = -2, y = -2, + text = 'Apply', event = 'update_password', }, info = UI.TextArea { - x = 3, ex = -3, y = 7, - textColor = colors.yellow, + x = 2, ex = -2, y = 5, ey = -4, + backgroundColor = 'black', + textColor = 'yellow', inactive = true, + marginLeft = 1, marginRight = 1, marginTop = 1, value = 'Add a password to enable other computers to connect to this one.', - } -} -function passwordTab:eventHandler(event) - if event.type == 'update_password' then - if not self.newPass.value or #self.newPass.value == 0 then - self:emit({ type = 'error_message', message = 'Invalid password' }) + }, + eventHandler = function(self, event) + if event.type == 'update_password' then + if not self.newPass.value or #self.newPass.value == 0 then + self:emit({ type = 'error_message', message = 'Invalid password' }) - else - Security.updatePassword(SHA.compute(self.newPass.value)) - self:emit({ type = 'success_message', message = 'Password updated' }) + else + Security.updatePassword(SHA.compute(self.newPass.value)) + self:emit({ type = 'success_message', message = 'Password updated' }) + end + return true end - return true end -end - -return passwordTab +} diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index 33249dd..eef9f16 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -6,8 +6,11 @@ local tab = UI.Tab { tabTitle = 'Path', description = 'Set the shell path', tabClose = true, + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 4, + }, entry = UI.TextEntry { - x = 2, y = 2, ex = -2, + x = 3, y = 3, ex = -3, limit = 256, shadowText = 'enter new path', accelerators = { @@ -16,7 +19,7 @@ local tab = UI.Tab { help = 'add a new path', }, grid = UI.Grid { - y = 4, ey = -3, + x = 2, y = 6, ex = -2, ey = -3, disableHeader = true, columns = { { key = 'value' } }, autospace = true, diff --git a/sys/apps/system/settings.lua b/sys/apps/system/settings.lua index 8e071ac..27f1f2b 100644 --- a/sys/apps/system/settings.lua +++ b/sys/apps/system/settings.lua @@ -2,48 +2,94 @@ local UI = require('opus.ui') local settings = _G.settings -if settings then - local settingsTab = UI.Tab { - tabTitle = 'Settings', - description = 'Computercraft configurable settings', - grid = UI.Grid { - y = 2, - autospace = true, - sortColumn = 'name', - columns = { - { heading = 'Setting', key = 'name' }, - { heading = 'Value', key = 'value' }, - }, - }, - } +local transform = { + string = tostring, + number = tonumber, +} - function settingsTab:enable() +return settings and UI.Tab { + tabTitle = 'Settings', + description = 'Computercraft settings', + grid = UI.Grid { + x = 2, y = 2, ex = -2, ey = -2, + sortColumn = 'name', + columns = { + { heading = 'Setting', key = 'name' }, + { heading = 'Value', key = 'value' }, + }, + }, + editor = UI.SlideOut { + y = -6, height = 6, + titleBar = UI.TitleBar { + event = 'slide_hide', + title = 'Enter value', + }, + form = UI.Form { + y = 2, + value = UI.TextEntry { + limit = 256, + formIndex = 1, + formLabel = 'Value', + formKey = 'value', + }, + validateField = function(self, entry) + if entry.value then + return transform[self.type](entry.value) + end + return true + end, + }, + accelerators = { + form_cancel = 'slide_hide', + }, + show = function(self, entry) + self.form.type = type(entry.value) or 'string' + self.form:setValues(entry) + self.titleBar.title = entry.name + UI.SlideOut.show(self) + end, + eventHandler = function(self, event) + if event.type == 'form_complete' then + if not event.values.value then + settings.unset(event.values.name) + self.parent:reload() + else + event.values.value = transform[self.form.type](event.values.value) + settings.set(event.values.name, event.values.value) + end + self.parent.grid:draw() + self:hide() + settings.save('.settings') + end + return UI.SlideOut.eventHandler(self, event) + end, + }, + reload = function(self) local values = { } for _,v in pairs(settings.getNames()) do - local value = settings.get(v) - if not value then - value = false - end table.insert(values, { name = v, - value = value, + value = settings.get(v) or false, }) end self.grid:setValues(values) + self.grid:setIndex(1) + end, + enable = function(self) + self:reload() UI.Tab.enable(self) - end - - function settingsTab:eventHandler(event) + end, + eventHandler = function(self, event) if event.type == 'grid_select' then - if not event.selected.value or type(event.selected.value) == 'boolean' then + if type(event.selected.value) == 'boolean' then event.selected.value = not event.selected.value + settings.set(event.selected.name, event.selected.value) + settings.save('.settings') + self.grid:draw() + else + self.editor:show(event.selected) end - settings.set(event.selected.name, event.selected.value) - settings.save('.settings') - self.grid:draw() return true end - end - - return settingsTab -end + end, +} diff --git a/sys/apps/system/shell.lua b/sys/apps/system/shell.lua index 13dfda6..8ca32fe 100644 --- a/sys/apps/system/shell.lua +++ b/sys/apps/system/shell.lua @@ -28,7 +28,7 @@ local defaults = { local _colors = config.color or Util.shallowCopy(defaults) local allSettings = { } -for k, v in pairs(defaults) do +for k in pairs(defaults) do table.insert(allSettings, { name = k }) end @@ -38,29 +38,34 @@ if not _colors.backgroundColor then _colors.fileColor = colors.white end -local tab = UI.Tab { +return UI.Tab { tabTitle = 'Shell', description = 'Shell options', grid1 = UI.ScrollingGrid { - y = 2, ey = -10, x = 3, ex = -16, + y = 2, ey = -10, x = 2, ex = -17, disableHeader = true, columns = { { key = 'name' } }, values = allSettings, sortColumn = 'name', }, grid2 = UI.ScrollingGrid { - y = 2, ey = -10, x = -14, ex = -3, + y = 2, ey = -10, x = -14, ex = -2, disableHeader = true, columns = { { key = 'name' } }, values = allColors, sortColumn = 'name', - }, - directoryLabel = UI.Text { - x = 2, y = -2, - value = 'Display directory', + getRowTextColor = function(self, row) + local selected = self.parent.grid1:getSelected() + if _colors[selected.name] == row.value then + return colors.yellow + end + return UI.Grid.getRowTextColor(self, row) + end }, directory = UI.Checkbox { - x = 20, y = -2, + x = 2, y = -2, + labelBackgroundColor = colors.black, + label = 'Directory', value = config.displayDirectory }, reset = UI.Button { @@ -74,69 +79,57 @@ local tab = UI.Tab { event = 'update', }, display = UI.Window { - x = 3, ex = -3, y = -8, height = 5, + x = 2, ex = -2, y = -8, height = 5, + draw = function(self) + self:clear(_colors.backgroundColor) + local offset = 0 + if config.displayDirectory then + self:write(1, 1, + '==' .. os.getComputerLabel() .. ':/dir/etc', + _colors.directoryBackgroundColor, _colors.directoryTextColor) + offset = 1 + end + + self:write(1, 1 + offset, '$ ', + _colors.promptBackgroundColor, _colors.promptTextColor) + + self:write(3, 1 + offset, 'ls /', + _colors.backgroundColor, _colors.commandTextColor) + + self:write(1, 2 + offset, 'sys usr', + _colors.backgroundColor, _colors.directoryColor) + + self:write(1, 3 + offset, 'startup', + _colors.backgroundColor, _colors.fileColor) + end, }, + eventHandler = function(self, event) + if event.type =='checkbox_change' then + config.displayDirectory = not not event.checked + self.display:draw() + + elseif event.type == 'grid_focus_row' and event.element == self.grid1 then + self.grid2:draw() + + elseif event.type == 'grid_select' and event.element == self.grid2 then + _colors[self.grid1:getSelected().name] = event.selected.value + self.display:draw() + self.grid2:draw() + + elseif event.type == 'reset' then + config.color = defaults + config.displayDirectory = true + self.directory.value = true + _colors = Util.shallowCopy(defaults) + + Config.update('shellprompt', config) + self:draw() + + elseif event.type == 'update' then + config.color = _colors + Config.update('shellprompt', config) + + end + return UI.Tab.eventHandler(self, event) + end } - -function tab.grid2:getRowTextColor(row) - local selected = tab.grid1:getSelected() - if _colors[selected.name] == row.value then - return colors.yellow - end - return UI.Grid.getRowTextColor(self, row) -end - -function tab.display:draw() - self:clear(_colors.backgroundColor) - local offset = 0 - if config.displayDirectory then - self:write(1, 1, - '==' .. os.getComputerLabel() .. ':/dir/etc', - _colors.directoryBackgroundColor, _colors.directoryTextColor) - offset = 1 - end - - self:write(1, 1 + offset, '$ ', - _colors.promptBackgroundColor, _colors.promptTextColor) - - self:write(3, 1 + offset, 'ls /', - _colors.backgroundColor, _colors.commandTextColor) - - self:write(1, 2 + offset, 'sys usr', - _colors.backgroundColor, _colors.directoryColor) - - self:write(1, 3 + offset, 'startup', - _colors.backgroundColor, _colors.fileColor) -end - -function tab:eventHandler(event) - if event.type =='checkbox_change' then - config.displayDirectory = not not event.checked - self.display:draw() - - elseif event.type == 'grid_focus_row' and event.element == self.grid1 then - self.grid2:draw() - - elseif event.type == 'grid_select' and event.element == self.grid2 then - _colors[tab.grid1:getSelected().name] = event.selected.value - self.display:draw() - self.grid2:draw() - - elseif event.type == 'reset' then - config.color = defaults - config.displayDirectory = true - self.directory.value = true - _colors = Util.shallowCopy(defaults) - - Config.update('shellprompt', config) - self:draw() - - elseif event.type == 'update' then - config.color = _colors - Config.update('shellprompt', config) - - end - return UI.Tab.eventHandler(self, event) -end - -return tab diff --git a/sys/apps/system/theme.lua b/sys/apps/system/theme.lua new file mode 100644 index 0000000..b9d4ed4 --- /dev/null +++ b/sys/apps/system/theme.lua @@ -0,0 +1,89 @@ +local Config = require('opus.config') +local UI = require('opus.ui') +local Util = require('opus.util') + +local colors = _G.colors + +local allColors = { } +for k,v in pairs(colors) do + if type(v) == 'number' then + table.insert(allColors, { name = k, value = v }) + end +end + +local allSettings = { } +for k,v in pairs(UI.colors) do + allSettings[k] = { name = k, value = v } +end + +return UI.Tab { + tabTitle = 'Theme', + description = 'Theme colors', + grid1 = UI.ScrollingGrid { + y = 2, ey = -10, x = 2, ex = -17, + disableHeader = true, + columns = { { key = 'name' } }, + values = allSettings, + sortColumn = 'name', + }, + grid2 = UI.ScrollingGrid { + y = 2, ey = -10, x = -14, ex = -2, + disableHeader = true, + columns = { { key = 'name' } }, + values = allColors, + sortColumn = 'name', + getRowTextColor = function(self, row) + local selected = self.parent.grid1:getSelected() + if selected.value == row.value then + return colors.yellow + end + return UI.Grid.getRowTextColor(self, row) + end + }, + button = UI.Button { + x = -9, y = -2, + text = 'Update', + event = 'update', + }, + display = UI.Window { + x = 2, ex = -2, y = -8, height = 5, + textColor = colors.black, + backgroundColor = colors.black, + draw = function(self) + self:clear() + + self:write(1, 1, Util.widthify(' Local Global Device', self.width), + allSettings.secondary.value) + + self:write(2, 2, 'enter command ', + colors.black, colors.gray) + + self:write(1, 3, ' Formatted ', + allSettings.primary.value) + + self:write(12, 3, Util.widthify(' Output ', self.width - 11), + allSettings.tertiary.value) + + self:write(1, 4, Util.widthify(' Key', self.width), + allSettings.primary.value) + end, + }, + eventHandler = function(self, event) + if event.type == 'grid_focus_row' and event.element == self.grid1 then + self.grid2:draw() + + elseif event.type == 'grid_select' and event.element == self.grid2 then + self.grid1:getSelected().value = event.selected.value + self.display:draw() + self.grid2:draw() + + elseif event.type == 'update' then + local config = Config.load('ui.theme', { colors = { } }) + for k,v in pairs(allSettings) do + config.colors[k] = v.value + end + Config.update('ui.theme', config) + end + return UI.Tab.eventHandler(self, event) + end +} diff --git a/sys/autorun/clipboard.lua b/sys/autorun/clipboard.lua index 992af47..2a0774c 100644 --- a/sys/autorun/clipboard.lua +++ b/sys/autorun/clipboard.lua @@ -9,7 +9,7 @@ kernel.hook('clipboard_copy', function(_, args) keyboard.clipboard = args[1] end) -keyboard.addHotkey('shift-paste', function() +local function queuePaste() local data = keyboard.clipboard if type(data) == 'table' then @@ -20,4 +20,7 @@ keyboard.addHotkey('shift-paste', function() if data then os.queueEvent('paste', data) end -end) +end + +kernel.hook('clipboard_paste', queuePaste) +keyboard.addHotkey('shift-paste', queuePaste) diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index 1cb0c1a..cc1063d 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -1,10 +1,10 @@ local fs = _G.fs local function deleteIfExists(path) - if fs.exists(path) then - fs.delete(path) - print("Deleted outdated file at: "..path) - end + if fs.exists(path) then + fs.delete(path) + print("Deleted outdated file at: "..path) + end end -- cleanup outdated files deleteIfExists('sys/apps/shell') @@ -21,4 +21,4 @@ deleteIfExists('sys/autorun/apps.lua') deleteIfExists('sys/init/6.tl3.lua') -- remove this file -deleteIfExists('sys/autorun/upgraded.lua') +-- deleteIfExists('sys/autorun/upgraded.lua') \ No newline at end of file diff --git a/sys/etc/apps.db b/sys/etc/apps.db index e753fd1..f342709 100644 --- a/sys/etc/apps.db +++ b/sys/etc/apps.db @@ -50,7 +50,10 @@ title = "System", category = "System", icon = "\030 \0307\031f| \010\0307\031f---o\030 \031 \010\030 \009 \0307\031f| ", - iconExt = "\030 \0318\138\0308\031 \130\0318\128\031 \129\030 \0318\133\010\030 \0318\143\0308\128\0317\143\0318\128\030 \143\010\030 \0318\138\135\143\139\133", + iconExt = "22€070†b02‹4Ÿ24\ +02—7Ž704ˆ4€€€€\ +7ƒ07„1ƒ7‹24ƒƒ", + --iconExt = "\030 \0318\138\0308\031 \130\0318\128\031 \129\030 \0318\133\010\030 \0318\143\0308\128\0317\143\0318\128\030 \143\010\030 \0318\138\135\143\139\133", run = "System.lua", }, [ "2a4d562b1d9a9c90bdede6fac8ce4f7402462b86" ] = { diff --git a/sys/help/Overview b/sys/help/Overview index d39a4dd..60b8c52 100644 --- a/sys/help/Overview +++ b/sys/help/Overview @@ -6,10 +6,11 @@ Shortcut keys * l: Lua application * f: Files * e: Edit an application (or right-click) + * n: Network * control-n: Add a new application * delete: Delete an application Adding a new application ======================== The run entry can be either a disk file or a URL. -Icons must be in NFT format with a height of 3 and a width of 3 to 8 characters. \ No newline at end of file +Icons must be in NFT format with a height of 3 and a width of 3 to 8 characters. Magenta is used for transparency. \ No newline at end of file diff --git a/sys/init/1.device.lua b/sys/init/1.device.lua index b61a767..024a08f 100644 --- a/sys/init/1.device.lua +++ b/sys/init/1.device.lua @@ -1,5 +1,3 @@ -_G.requireInjector(_ENV) - local Peripheral = require('opus.peripheral') _G.device = Peripheral.getList() diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 5b3c5f9..69eb387 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -4,11 +4,8 @@ if fs.native then return end -_G.requireInjector(_ENV) local Util = require('opus.util') --- TODO: support getDrive for virtual nodes - fs.native = Util.shallowCopy(fs) local fstypes = { } @@ -23,7 +20,6 @@ for k,fn in pairs(fs) do end function nativefs.list(node, dir) - local files if fs.native.isDir(dir) then files = fs.native.list(dir) @@ -265,7 +261,6 @@ local function getfstype(fstype) end function fs.mount(path, fstype, ...) - local vfs = getfstype(fstype) if not vfs then error('Invalid file system type') diff --git a/sys/init/5.network.lua b/sys/init/5.network.lua index b02937a..d526b62 100644 --- a/sys/init/5.network.lua +++ b/sys/init/5.network.lua @@ -1,5 +1,3 @@ -_G.requireInjector(_ENV) - local Config = require('opus.config') local device = _G.device diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index 7d06abe..49c55bf 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -32,3 +32,23 @@ end help.setPath(table.concat(helpPaths, ':')) shell.setPath(table.concat(appPaths, ':')) + +local function runDir(directory) + local files = fs.list(directory) + table.sort(files) + + for _,file in ipairs(files) do + os.sleep(0) + local result, err = shell.run(directory .. '/' .. file) + if not result and err then + _G.printError('\n' .. err) + end + end +end + +for _, package in pairs(Packages:installedSorted()) do + local packageDir = 'packages/' .. package.name .. '/init' + if fs.exists(packageDir) and fs.isDir(packageDir) then + runDir(packageDir) + end +end diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index d8b722a..1f44a0d 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -1,5 +1,4 @@ -_G.requireInjector(_ENV) - +local Blit = require('opus.ui.blit') local Config = require('opus.config') local trace = require('opus.trace') local Util = require('opus.util') @@ -47,6 +46,7 @@ local config = { Config.load('multishell', config) local _colors = parentTerm.isColor() and config.color or config.standard +local palette = parentTerm.isColor() and Blit.colorPalette or Blit.grayscalePalette local function redrawMenu() if not tabsDirty then @@ -207,17 +207,11 @@ end) kernel.hook('multishell_redraw', function() tabsDirty = false - local function write(x, text, bg, fg) - parentTerm.setBackgroundColor(bg) - parentTerm.setTextColor(fg) - parentTerm.setCursorPos(x, 1) - parentTerm.write(text) - end - - local bg = _colors.tabBarBackgroundColor - parentTerm.setBackgroundColor(bg) - parentTerm.setCursorPos(1, 1) - parentTerm.clearLine() + local blit = Blit(w, { + bg = _colors.tabBarBackgroundColor, + fg = _colors.textColor, + palette = palette, + }) local currentTab = kernel.getFocused() @@ -254,21 +248,26 @@ kernel.hook('multishell_redraw', function() tabX = tabX + tab.width if tab ~= currentTab then local textColor = tab.isDead and _colors.errorColor or _colors.textColor - write(tab.sx, tab.title:sub(1, tab.width - 1), + blit:write(tab.sx, tab.title:sub(1, tab.width - 1), _colors.backgroundColor, textColor) end end end if currentTab then - write(currentTab.sx - 1, - ' ' .. currentTab.title:sub(1, currentTab.width - 1) .. ' ', - _colors.focusBackgroundColor, _colors.focusTextColor) + if currentTab.sx then + blit:write(currentTab.sx - 1, + ' ' .. currentTab.title:sub(1, currentTab.width - 1) .. ' ', + _colors.focusBackgroundColor, _colors.focusTextColor) + end if not currentTab.noTerminate then - write(w, closeInd, _colors.backgroundColor, _colors.focusTextColor) + blit:write(w, closeInd, nil, _colors.focusTextColor) end end + parentTerm.setCursorPos(1, 1) + parentTerm.blit(blit.text, blit.fg, blit.bg) + if currentTab and currentTab.window then currentTab.window.restoreCursor() end @@ -334,16 +333,12 @@ kernel.hook('mouse_scroll', function(_, eventData) end) kernel.hook('kernel_ready', function() - local env = Util.shallowCopy(shell.getEnv()) - _G.requireInjector(env) - overviewId = multishell.openTab({ path = config.launcher or 'sys/apps/Overview.lua', isOverview = true, noTerminate = true, focused = true, title = '+', - env = env, }) multishell.openTab({ diff --git a/sys/kernel.lua b/sys/kernel.lua index b2ef8a8..546300d 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -1,5 +1,3 @@ -_G.requireInjector(_ENV) - local Array = require('opus.array') local Terminal = require('opus.terminal') local Util = require('opus.util') diff --git a/sys/modules/opus/array.lua b/sys/modules/opus/array.lua index f0aa73f..6980a86 100644 --- a/sys/modules/opus/array.lua +++ b/sys/modules/opus/array.lua @@ -1,3 +1,5 @@ +local Util = require('opus.util') + local Array = { } function Array.filter(it, f) @@ -14,9 +16,11 @@ function Array.removeByValue(t, e) for k,v in pairs(t) do if v == e then table.remove(t, k) - break + return e end end end +Array.find = Util.find + return Array diff --git a/sys/modules/opus/config.lua b/sys/modules/opus/config.lua index 3518ba2..d41c1c7 100644 --- a/sys/modules/opus/config.lua +++ b/sys/modules/opus/config.lua @@ -1,7 +1,6 @@ local Util = require('opus.util') local fs = _G.fs -local shell = _ENV.shell local Config = { } @@ -25,23 +24,6 @@ function Config.load(fname, data) return data end -function Config.loadWithCheck(fname, data) - local filename = 'usr/config/' .. fname - - if not fs.exists(filename) then - Config.load(fname, data) - print() - print('The configuration file has been created.') - print('The file name is: ' .. filename) - print() - _G.printError('Press enter to configure') - _G.read() - shell.run('edit ' .. filename) - end - - return Config.load(fname, data) -end - function Config.update(fname, data) local filename = 'usr/config/' .. fname Util.writeTable(filename, data) diff --git a/sys/modules/opus/entry.lua b/sys/modules/opus/entry.lua index aa2397f..0dc3536 100644 --- a/sys/modules/opus/entry.lua +++ b/sys/modules/opus/entry.lua @@ -41,9 +41,9 @@ end function Entry:updateScroll() local ps = self.scroll - local value = _val(self.value) - if self.pos > #value then - self.pos = #value + local len = #_val(self.value) + if self.pos > len then + self.pos = len self.scroll = 0 -- ?? end if self.pos - self.scroll > self.width then @@ -51,6 +51,11 @@ function Entry:updateScroll() elseif self.pos < self.scroll then self.scroll = self.pos end + if self.scroll > 0 then + if self.scroll + self.width > len then + self.scroll = len - self.width + end + end if ps ~= self.scroll then self.textChanged = true end @@ -217,6 +222,10 @@ function Entry:paste(ie) end end +function Entry:forcePaste() + os.queueEvent('clipboard_paste') +end + function Entry:clearLine() if #_val(self.value) > 0 then self:reset() @@ -363,9 +372,10 @@ local mappings = { --[ 'control-d' ] = Entry.cutNextWord, [ 'control-x' ] = Entry.cut, [ 'paste' ] = Entry.paste, --- [ 'control-y' ] = Entry.paste, -- well this won't work... + [ 'control-y' ] = Entry.forcePaste, -- well this won't work... [ 'mouse_doubleclick' ] = Entry.markWord, + [ 'mouse_tripleclick' ] = Entry.markAll, [ 'shift-left' ] = Entry.markLeft, [ 'shift-right' ] = Entry.markRight, [ 'mouse_down' ] = Entry.markAnchor, diff --git a/sys/modules/opus/fuzzy.lua b/sys/modules/opus/fuzzy.lua new file mode 100644 index 0000000..c71ac08 --- /dev/null +++ b/sys/modules/opus/fuzzy.lua @@ -0,0 +1,21 @@ +-- Based on Squid's fuzzy search +-- https://github.com/SquidDev-CC/artist/blob/vnext/artist/lib/match.lua +-- +-- not very fuzzy anymore + +local SCORE_WEIGHT = 1000 +local LEADING_LETTER_PENALTY = -30 +local LEADING_LETTER_PENALTY_MAX = -90 + +local _find = string.find +local _max = math.max + +return function(str, pattern) + local start = _find(str, pattern, 1, true) + if start then + -- All letters before the current one are considered leading, so add them to our penalty + return SCORE_WEIGHT + + _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX) + - (#str - #pattern) + end +end diff --git a/sys/modules/opus/git.lua b/sys/modules/opus/git.lua index a06cc70..c4a17d7 100644 --- a/sys/modules/opus/git.lua +++ b/sys/modules/opus/git.lua @@ -24,9 +24,9 @@ function git.list(repository) local function getContents() local dataUrl = string.format(TREE_URL, user, repo, branch) - local contents, msg = Util.httpGet(dataUrl,TREE_HEADERS) + local contents, msg = Util.httpGet(dataUrl, TREE_HEADERS) if not contents then - error(_sformat('Failed to download %s\n%s', dataUrl, msg), 2) + error(string.format('Failed to download %s\n%s', dataUrl, msg), 2) else return json.decode(contents) end diff --git a/sys/modules/opus/gps.lua b/sys/modules/opus/gps.lua index 3b0ae74..1132b76 100644 --- a/sys/modules/opus/gps.lua +++ b/sys/modules/opus/gps.lua @@ -65,7 +65,7 @@ function GPS.locate(timeout, debug) if debug then print("Position is "..pos.x..","..pos.y..","..pos.z) end - return vector.new(pos.x, pos.y, pos.z) + return pos and vector.new(pos.x, pos.y, pos.z) end function GPS.isAvailable() diff --git a/sys/modules/opus/input.lua b/sys/modules/opus/input.lua index 46b58fe..956e0ba 100644 --- a/sys/modules/opus/input.lua +++ b/sys/modules/opus/input.lua @@ -50,18 +50,20 @@ function input:toCode(ch, code) table.insert(result, 'alt') end - if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] or - code == keys.leftShift or code == keys.rightShift then - if code and modifiers[code] then - table.insert(result, 'shift') - elseif #ch == 1 then - table.insert(result, ch:upper()) - else - table.insert(result, 'shift') + if ch then -- some weird things happen with control/command on mac + if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] or + code == keys.leftShift or code == keys.rightShift then + if code and modifiers[code] then + table.insert(result, 'shift') + elseif #ch == 1 then + table.insert(result, ch:upper()) + else + table.insert(result, 'shift') + table.insert(result, ch) + end + elseif not code or not modifiers[code] then table.insert(result, ch) end - elseif not code or not modifiers[code] then - table.insert(result, ch) end return table.concat(result, '-') @@ -118,6 +120,7 @@ function input:translate(event, code, p1, p2) local buttons = { 'mouse_click', 'mouse_rightclick' } self.mch = buttons[code] self.mfired = nil + self.anchor = { x = p1, y = p2 } return { code = input:toCode('mouse_down', 255), button = code, @@ -132,6 +135,8 @@ function input:translate(event, code, p1, p2) button = code, x = p1, y = p2, + dx = p1 - self.anchor.x, + dy = p2 - self.anchor.y, } elseif event == 'mouse_up' then @@ -141,18 +146,26 @@ function input:translate(event, code, p1, p2) p1 == self.x and p2 == self.y and (clock - self.timer < .5) then - self.mch = 'mouse_doubleclick' - self.timer = nil + self.clickCount = self.clickCount + 1 + if self.clickCount == 3 then + self.mch = 'mouse_tripleclick' + self.timer = nil + self.clickCount = 1 + else + self.mch = 'mouse_doubleclick' + end else self.timer = os.clock() self.x = p1 self.y = p2 + self.clickCount = 1 end self.mfired = input:toCode(self.mch, 255) else self.mch = 'mouse_up' self.mfired = input:toCode(self.mch, 255) end + return { code = self.mfired, button = code, diff --git a/sys/modules/opus/nft.lua b/sys/modules/opus/nft.lua index 4fd8d02..a12834d 100644 --- a/sys/modules/opus/nft.lua +++ b/sys/modules/opus/nft.lua @@ -1,16 +1,19 @@ local Util = require('opus.util') +local colors = _G.colors + local NFT = { } -- largely copied from http://www.computercraft.info/forums2/index.php?/topic/5029-145-npaintpro/ -local tColourLookup = { } +local hexToColor = { } for n = 1, 16 do - tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1) + hexToColor[string.sub("0123456789abcdef", n, n)] = 2 ^ (n - 1) end +local colorToHex = Util.transpose(hexToColor) local function getColourOf(hex) - return tColourLookup[hex:byte()] + return hexToColor[hex] end function NFT.parse(imageText) @@ -62,8 +65,22 @@ function NFT.parse(imageText) return image end -function NFT.load(path) +function NFT.transparency(image) + for y = 1, image.height do + for _,key in pairs(Util.keys(image.fg[y])) do + if image.fg[y][key] == colors.magenta then + image.fg[y][key] = nil + end + end + for _,key in pairs(Util.keys(image.bg[y])) do + if image.bg[y][key] == colors.magenta then + image.bg[y][key] = nil + end + end + end +end +function NFT.load(path) local imageText = Util.readFile(path) if not imageText then error('Unable to read image file') @@ -71,4 +88,35 @@ function NFT.load(path) return NFT.parse(imageText) end +function NFT.save(image, filename) + local bgcode, txcode = '\30', '\31' + local output = { } + + for y = 1, image.height do + local lastBG, lastFG + if image.text[y] then + for x = 1, #image.text[y] do + local bg = image.bg[y][x] or colors.magenta + if bg ~= lastBG then + lastBG = bg + table.insert(output, bgcode .. colorToHex[bg]) + end + + local fg = image.fg[y][x] or colors.magenta + if fg ~= lastFG then + lastFG = fg + table.insert(output, txcode .. colorToHex[fg]) + end + + table.insert(output, image.text[y][x]) + end + end + + if y < image.height then + table.insert(output, '\n') + end + end + Util.writeFile(filename, table.concat(output)) +end + return NFT diff --git a/sys/modules/opus/terminal.lua b/sys/modules/opus/terminal.lua index cb7df22..7b04a42 100644 --- a/sys/modules/opus/terminal.lua +++ b/sys/modules/opus/terminal.lua @@ -36,61 +36,66 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) local maxScroll = 100 local cx, cy = 1, 1 local blink = false - local bg, fg = parent.getBackgroundColor(), parent.getTextColor() + local _bg, _fg = parent.getBackgroundColor(), parent.getTextColor() - local canvas = Canvas({ + win.canvas = Canvas({ x = sx, y = sy, width = w, height = h, isColor = parent.isColor(), offy = 0, + bg = _bg, + fg = _fg, }) - win.canvas = canvas - local function update() if isVisible then - canvas:render(parent) + win.canvas:render(parent) win.setCursorPos(cx, cy) end end local function scrollTo(y) y = math.max(0, y) - y = math.min(#canvas.lines - canvas.height, y) + y = math.min(#win.canvas.lines - win.canvas.height, y) - if y ~= canvas.offy then - canvas.offy = y - canvas:dirty() + if y ~= win.canvas.offy then + win.canvas.offy = y + win.canvas:dirty() update() end end function win.write(str) str = tostring(str) or '' - canvas:write(cx, cy + canvas.offy, str, bg, fg) + win.canvas:write(cx, cy + win.canvas.offy, str, win.canvas.bg, win.canvas.fg) win.setCursorPos(cx + #str, cy) update() end function win.blit(str, fg, bg) - canvas:blit(cx, cy + canvas.offy, str, bg, fg) + win.canvas:blit(cx, cy + win.canvas.offy, str, bg, fg) win.setCursorPos(cx + #str, cy) update() end function win.clear() - canvas.offy = 0 - for i = #canvas.lines, canvas.height + 1, -1 do - canvas.lines[i] = nil + win.canvas.offy = 0 + for i = #win.canvas.lines, win.canvas.height + 1, -1 do + win.canvas.lines[i] = nil end - canvas:clear(bg, fg) + win.canvas:clear() update() end + function win.getLine(n) + local line = win.canvas.lines[n] + return line.text, line.fg, line.bg + end + function win.clearLine() - canvas:clearLine(cy + canvas.offy, bg, fg) + win.canvas:clearLine(cy + win.canvas.offy) win.setCursorPos(cx, cy) update() end @@ -102,10 +107,14 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) function win.setCursorPos(x, y) cx, cy = math.floor(x), math.floor(y) if isVisible then - parent.setCursorPos(cx + canvas.x - 1, cy + canvas.y - 1) + parent.setCursorPos(cx + win.canvas.x - 1, cy + win.canvas.y - 1) end end + function win.getCursorBlink() + return blink + end + function win.setCursorBlink(b) blink = b if isVisible then @@ -114,12 +123,12 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end function win.isColor() - return canvas.isColor + return win.canvas.isColor end win.isColour = win.isColor function win.setTextColor(c) - fg = c + win.canvas.fg = c end win.setTextColour = win.setTextColor @@ -139,38 +148,38 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) win.setPaletteColour = win.setPaletteColor function win.setBackgroundColor(c) - bg = c + win.canvas.bg = c end win.setBackgroundColour = win.setBackgroundColor function win.getSize() - return canvas.width, canvas.height + return win.canvas.width, win.canvas.height end function win.scroll(n) n = n or 1 if n > 0 then - local lines = #canvas.lines + local lines = #win.canvas.lines for i = 1, n do - canvas.lines[lines + i] = { } - canvas:clearLine(lines + i, bg, fg) + win.canvas.lines[lines + i] = { } + win.canvas:clearLine(lines + i) end - while #canvas.lines > maxScroll do - table.remove(canvas.lines, 1) + while #win.canvas.lines > maxScroll do + table.remove(win.canvas.lines, 1) end - scrollTo(#canvas.lines) - canvas:dirty() + scrollTo(#win.canvas.lines) + win.canvas:dirty() update() end end function win.getTextColor() - return fg + return win.canvas.fg end win.getTextColour = win.getTextColor function win.getBackgroundColor() - return bg + return win.canvas.bg end win.getBackgroundColour = win.getBackgroundColor @@ -178,7 +187,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) if visible ~= isVisible then isVisible = visible if isVisible then - canvas:dirty() + win.canvas:dirty() update() end end @@ -186,7 +195,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) function win.redraw() if isVisible then - canvas:dirty() + win.canvas:dirty() update() end end @@ -194,27 +203,27 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) function win.restoreCursor() if isVisible then win.setCursorPos(cx, cy) - win.setTextColor(fg) + win.setTextColor(win.canvas.fg) win.setCursorBlink(blink) end end function win.getPosition() - return canvas.x, canvas.y + return win.canvas.x, win.canvas.y end function win.reposition(x, y, width, height) - canvas.x, canvas.y = x, y - canvas:resize(width or canvas.width, height or canvas.height) + win.canvas.x, win.canvas.y = x, y + win.canvas:resize(width or win.canvas.width, height or win.canvas.height) end --[[ Additional methods ]]-- function win.scrollDown() - scrollTo(canvas.offy + 1) + scrollTo(win.canvas.offy + 1) end function win.scrollUp() - scrollTo(canvas.offy - 1) + scrollTo(win.canvas.offy - 1) end function win.scrollTop() @@ -222,7 +231,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end function win.scrollBottom() - scrollTo(#canvas.lines) + scrollTo(#win.canvas.lines) end function win.setMaxScroll(ms) @@ -230,37 +239,35 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end function win.getCanvas() - return canvas + return win.canvas end function win.getParent() return parent end - canvas:clear() + win.canvas:clear() return win end -- get windows contents -function Terminal.getContents(win, parent) - local oblit, oscp = parent.blit, parent.setCursorPos - local lines = { } +function Terminal.getContents(win) + if not win.getLine then + error('window is required') + end - parent.blit = function(text, fg, bg) - lines[#lines + 1] = { + local lines = { } + local _, h = win.getSize() + + for i = 1, h do + local text, fg, bg = win.getLine(i) + lines[i] = { text = text, fg = fg, bg = bg, } end - parent.setCursorPos = function() end - - win.setVisible(true) - win.redraw() - - parent.blit = oblit - parent.setCursorPos = oscp return lines end diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index c627005..625128f 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -1,3 +1,6 @@ +local Array = require('opus.array') +local Blit = require('opus.ui.blit') +local Canvas = require('opus.ui.canvas') local class = require('opus.class') local Event = require('opus.event') local Input = require('opus.input') @@ -5,7 +8,6 @@ local Transition = require('opus.ui.transition') local Util = require('opus.util') local _rep = string.rep -local _sub = string.sub local colors = _G.colors local device = _G.device local fs = _G.fs @@ -32,11 +34,16 @@ local textutils = _G.textutils ]] --[[-- Top Level Manager --]]-- -local Manager = class() -function Manager:init() +local UI = { } +function UI:init() self.devices = { } self.theme = { } self.extChars = Util.getVersion() >= 1.76 + self.colors = { + primary = colors.green, + secondary = colors.lightGray, + tertiary = colors.gray, + } local function keyFunction(event, code, held) local ie = Input:translate(event, code, held) @@ -44,8 +51,7 @@ function Manager:init() local currentPage = self:getActivePage() if ie and currentPage then local target = currentPage.focused or currentPage - self:inputEvent(target, - { type = 'key', key = ie.code == 'char' and ie.ch or ie.code, element = target, ie = ie }) + target:emit({ type = 'key', key = ie.code == 'char' and ie.ch or ie.code, element = target, ie = ie }) currentPage:sync() end end @@ -53,10 +59,7 @@ function Manager:init() local function resize(_, side) local dev = self.devices[side or 'terminal'] if dev and dev.currentPage then - -- the parent doesn't have any children set... - -- that's why we have to resize both the parent and the current page - -- kinda makes sense - dev.currentPage.parent:resize() + dev:resize() dev.currentPage:resize() dev.currentPage:draw() @@ -72,17 +75,14 @@ function Manager:init() monitor_resize = resize, mouse_scroll = function(_, direction, x, y) + local ie = Input:translate('mouse_scroll', direction, x, y) + local currentPage = self:getActivePage() if currentPage then local event = currentPage:pointToChild(x, y) - local directions = { - [ -1 ] = 'up', - [ 1 ] = 'down' - } - -- revisit - should send out scroll_up and scroll_down events - -- let the element convert them to up / down - self:inputEvent(event.element, - { type = 'key', key = directions[direction] }) + event.type = ie.code + event.ie = { code = ie.code, x = event.x, y = event.y } + event.element:emit(event) currentPage:sync() end end, @@ -92,7 +92,7 @@ function Manager:init() if dev and dev.currentPage then Input:translate('mouse_click', 1, x, y) local ie = Input:translate('mouse_up', 1, x, y) - self:click(dev.currentPage, ie.code, 1, x, y) + self:click(dev.currentPage, ie) end end, @@ -107,7 +107,7 @@ function Manager:init() currentPage:setFocus(event.element) currentPage:sync() end - self:click(currentPage, ie.code, button, x, y) + self:click(currentPage, ie) end end end, @@ -125,7 +125,7 @@ function Manager:init() elseif ie and currentPage then if not currentPage.parent.device.side then - self:click(currentPage, ie.code, button, x, y) + self:click(currentPage, ie) end end end, @@ -135,7 +135,7 @@ function Manager:init() local currentPage = self:getActivePage() if ie and currentPage then - self:click(currentPage, ie.code, button, x, y) + self:click(currentPage, ie) end end, @@ -156,7 +156,7 @@ function Manager:init() end) end -function Manager:configure(appName, ...) +function UI:configure(appName, ...) local defaults = Util.loadTable('usr/config/' .. appName) or { } if not defaults.device then defaults.device = { } @@ -190,19 +190,15 @@ function Manager:configure(appName, ...) end if defaults.theme then - for k,v in pairs(defaults.theme) do - if self[k] and self[k].defaults then - Util.merge(self[k].defaults, v) - end - end + Util.deepMerge(self.theme, defaults.theme) end end -function Manager:disableEffects() - self.defaultDevice.effectsEnabled = false +function UI:disableEffects() + self.term.effectsEnabled = false end -function Manager:loadTheme(filename) +function UI:loadTheme(filename) if fs.exists(filename) then local theme, err = Util.loadTable(filename) if not theme then @@ -212,8 +208,20 @@ function Manager:loadTheme(filename) end end -function Manager:generateTheme(filename) +function UI:generateTheme(filename) local t = { } + + local function getName(d) + if type(d) == 'string' then + return string.format("'%s'", d) + end + for c, n in pairs(colors) do + if n == d then + return 'colors.' .. c + end + end + end + for k,v in pairs(self) do if type(v) == 'table' then if v._preload then @@ -226,72 +234,96 @@ function Manager:generateTheme(filename) if not t[k] then t[k] = { } end - for c, n in pairs(colors) do - if n == d then - t[k][p] = 'colors.' .. c - break - end - end + t[k][p] = getName(d) end end end end end + t.colors = { + primary = getName(self.colors.primary), + secondary = getName(self.colors.secondary), + tertiary = getName(self.colors.tertiary), + } Util.writeFile(filename, textutils.serialize(t):gsub('(")', '')) end -function Manager:emitEvent(event) +function UI:emitEvent(event) local currentPage = self:getActivePage() if currentPage and currentPage.focused then return currentPage.focused:emit(event) end end -function Manager:inputEvent(parent, event) -- deprecate ? - return parent and parent:emit(event) -end +function UI:click(target, ie) + local clickEvent -function Manager:click(target, code, button, x, y) - local clickEvent = target:pointToChild(x, y) + if ie.code == 'mouse_drag' then + local function getPosition(element, x, y) + repeat + x = x - element.x + 1 + y = y - element.y + 1 + element = element.parent + until not element + return x, y + end - if code == 'mouse_doubleclick' then - if self.doubleClickElement ~= clickEvent.element then + local x, y = getPosition(self.lastClicked, ie.x, ie.y) + + clickEvent = { + element = self.lastClicked, + x = x, + y = y, + dx = ie.dx, + dy = ie.dy, + } + else + clickEvent = target:pointToChild(ie.x, ie.y) + end + + -- hack for dropdown menus + if ie.code == 'mouse_click' and not clickEvent.element.focus then + self:emitEvent({ type = 'mouse_out' }) + end + + if ie.code == 'mouse_doubleclick' then + if self.lastClicked ~= clickEvent.element then return end else - self.doubleClickElement = clickEvent.element + self.lastClicked = clickEvent.element end - clickEvent.button = button - clickEvent.type = code - clickEvent.key = code - clickEvent.ie = { code = code, x = clickEvent.x, y = clickEvent.y } + clickEvent.button = ie.button + clickEvent.type = ie.code + clickEvent.key = ie.code + clickEvent.ie = { code = ie.code, x = clickEvent.x, y = clickEvent.y } + clickEvent.raw = ie if clickEvent.element.focus then target:setFocus(clickEvent.element) end - self:inputEvent(clickEvent.element, clickEvent) + clickEvent.element:emit(clickEvent) target:sync() end -function Manager:setDefaultDevice(dev) - self.defaultDevice = dev +function UI:setDefaultDevice(dev) self.term = dev end -function Manager:addPage(name, page) +function UI:addPage(name, page) if not self.pages then self.pages = { } end self.pages[name] = page end -function Manager:setPages(pages) +function UI:setPages(pages) self.pages = pages end -function Manager:getPage(pageName) +function UI:getPage(pageName) local page = self.pages[pageName] if not page then @@ -301,19 +333,18 @@ function Manager:getPage(pageName) return page end -function Manager:getActivePage(page) +function UI:getActivePage(page) if page then return page.parent.currentPage end - return self.defaultDevice.currentPage + return self.term.currentPage end -function Manager:setActivePage(page) +function UI:setActivePage(page) page.parent.currentPage = page - page.parent.canvas = page.canvas end -function Manager:setPage(pageOrName, ...) +function UI:setPage(pageOrName, ...) local page = pageOrName if type(pageOrName) == 'string' then @@ -333,7 +364,6 @@ function Manager:setPage(pageOrName, ...) page.previousPage = currentPage end self:setActivePage(page) - --page:clear(page.backgroundColor) page:enable(...) page:draw() if page.focused then @@ -344,27 +374,27 @@ function Manager:setPage(pageOrName, ...) end end -function Manager:getCurrentPage() - return self.defaultDevice.currentPage +function UI:getCurrentPage() + return self.term.currentPage end -function Manager:setPreviousPage() - if self.defaultDevice.currentPage.previousPage then - local previousPage = self.defaultDevice.currentPage.previousPage.previousPage - self:setPage(self.defaultDevice.currentPage.previousPage) - self.defaultDevice.currentPage.previousPage = previousPage +function UI:setPreviousPage() + if self.term.currentPage.previousPage then + local previousPage = self.term.currentPage.previousPage.previousPage + self:setPage(self.term.currentPage.previousPage) + self.term.currentPage.previousPage = previousPage end end -function Manager:getDefaults(element, args) +function UI:getDefaults(element, args) local defaults = Util.deepCopy(element.defaults) if args then - Manager:mergeProperties(defaults, args) + UI:mergeProperties(defaults, args) end return defaults end -function Manager:mergeProperties(obj, args) +function UI:mergeProperties(obj, args) if args then for k,v in pairs(args) do if k == 'accelerators' then @@ -380,7 +410,7 @@ function Manager:mergeProperties(obj, args) end end -function Manager:pullEvents(...) +function UI:pullEvents(...) local s, m = pcall(Event.pullEvents, ...) self.term:reset() if not s and m then @@ -388,21 +418,20 @@ function Manager:pullEvents(...) end end -Manager.exitPullEvents = Event.exitPullEvents -Manager.quit = Event.exitPullEvents -Manager.start = Manager.pullEvents +UI.exitPullEvents = Event.exitPullEvents +UI.quit = Event.exitPullEvents +UI.start = UI.pullEvents -local UI = Manager() +UI:init() --[[-- Basic drawable area --]]-- -UI.Window = class() +UI.Window = class(Canvas) UI.Window.uid = 1 UI.Window.docs = { } UI.Window.defaults = { UIElement = 'Window', x = 1, y = 1, - -- z = 0, -- eventually... offx = 0, offy = 0, cursorX = 1, @@ -413,7 +442,9 @@ function UI.Window:init(args) local defaults = args local m = getmetatable(self) -- get the class for this instance repeat - defaults = UI:getDefaults(m, defaults) + if m ~= Canvas then + defaults = UI:getDefaults(m, defaults) + end m = m._base until not m UI:mergeProperties(self, defaults) @@ -438,6 +469,9 @@ function UI.Window:init(args) until not m end +UI.Window.docs.postInit = [[postInit(VOID) +Called once the window has all the properties set. +Override to calculate properties or to dynamically add children]] function UI.Window:postInit() if self.parent then -- this will cascade down the whole tree of elements starting at the @@ -531,47 +565,60 @@ function UI.Window:layout() if not self.height then self.height = self.parent.height - self.y + 1 end + + self.width = math.max(self.width, 1) + self.height = math.max(self.height, 1) + + self:reposition(self.x, self.y, self.width, self.height) end -- Called when the window's parent has be assigned function UI.Window:setParent() self.oh, self.ow = self.height, self.width self.ox, self.oy = self.x, self.y + self.oex, self.oey = self.ex, self.ey self:layout() - - -- Experimental - -- Inherit properties from the parent container - -- does this need to be in reverse order ? - local m = getmetatable(self) -- get the class for this instance - repeat - if m.inherits then - for k, v in pairs(m.inherits) do - local value = self.parent:getProperty(v) - if value then - self[k] = value - end - end - end - m = m._base - until not m - self:initChildren() end function UI.Window:resize() self.height, self.width = self.oh, self.ow self.x, self.y = self.ox, self.oy + self.ex, self.ey = self.oex, self.oey self:layout() if self.children then - for _,child in ipairs(self.children) do + for child in self:eachChild() do child:resize() end end end +function UI.Window:reposition(x, y, w, h) + if not self.lines then + Canvas.init(self, { + x = x, + y = y, + width = w, + height = h, + isColor = self.parent.isColor, + }) + else + self:move(x, y) + Canvas.resize(self, w, h) + end +end + +UI.Window.docs.raise = [[raise(VOID) +Raise this window to the top]] +function UI.Window:raise() + Array.removeByValue(self.parent.children, self) + table.insert(self.parent.children, self) + self:dirty(true) +end + UI.Window.docs.add = [[add(TABLE) Add element(s) to a window. Example: page:add({ @@ -584,6 +631,20 @@ function UI.Window:add(children) self:initChildren() end +function UI.Window:eachChild() + local c = self.children and Util.shallowCopy(self.children) + local i = 0 + return function() + i = i + 1 + return c and c[i] + end +end + +function UI.Window:remove() + Array.removeByValue(self.parent.children, self) + self.parent:dirty(true) +end + function UI.Window:getCursorPos() return self.cursorX, self.cursorY end @@ -595,18 +656,20 @@ function UI.Window:setCursorPos(x, y) end function UI.Window:setCursorBlink(blink) - self.parent:setCursorBlink(blink) + self.cursorBlink = blink end UI.Window.docs.draw = [[draw(VOID) Redraws the window in the internal buffer.]] function UI.Window:draw() - self:clear(self.backgroundColor) - if self.children then - for _,child in pairs(self.children) do - if child.enabled then - child:draw() - end + self:clear() + self:drawChildren() +end + +function UI.Window:drawChildren() + for child in self:eachChild() do + if child.enabled then + child:draw() end end end @@ -633,19 +696,38 @@ function UI.Window:sync() end function UI.Window:enable(...) - self.enabled = true - if self.children then - for _,child in pairs(self.children) do - child:enable(...) + if not self.enabled then + self.enabled = true + if self.transitionHint then + self:addTransition(self.transitionHint) + end + + if self.modal then + self:raise() + self:capture(self) + end + + for child in self:eachChild() do + if not child.enabled then + child:enable(...) + end end end end function UI.Window:disable() - self.enabled = false - if self.children then - for _,child in pairs(self.children) do - child:disable() + if self.enabled then + self.enabled = false + self.parent:dirty(true) + + if self.modal then + self:release(self) + end + + for child in self:eachChild() do + if child.enabled then + child:disable() + end end end end @@ -656,13 +738,9 @@ function UI.Window:setTextScale(textScale) end UI.Window.docs.clear = [[clear(opt COLOR bg, opt COLOR fg) -Clears the window using the either the passed values or the defaults for that window.]] +Clears the window using either the passed values or the defaults for that window.]] function UI.Window:clear(bg, fg) - if self.canvas then - self.canvas:clear(bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) - else - self:clearArea(1 + self.offx, 1 + self.offy, self.width, self.height, bg) - end + Canvas.clear(self, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) end function UI.Window:clearLine(y, bg) @@ -670,39 +748,28 @@ function UI.Window:clearLine(y, bg) end function UI.Window:clearArea(x, y, width, height, bg) + self:fillArea(x, y, width, height, ' ', bg) +end + +function UI.Window:fillArea(x, y, width, height, fillChar, bg, fg) if width > 0 then - local filler = _rep(' ', width) + local filler = _rep(fillChar, width) for i = 0, height - 1 do - self:write(x, y + i, filler, bg) + self:write(x, y + i, filler, bg, fg) end end end function UI.Window:write(x, y, text, bg, fg) - bg = bg or self.backgroundColor - fg = fg or self.textColor - - if self.canvas then - self.canvas:write(x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) - else - x = x - self.offx - y = y - self.offy - if y <= self.height and y > 0 then - self.parent:write( - self.x + x - 1, self.y + y - 1, tostring(text), bg, fg) - end - end + Canvas.write(self, x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) end function UI.Window:centeredWrite(y, text, bg, fg) if #text >= self.width then self:write(1, y, text, bg, fg) else - local space = math.floor((self.width-#text) / 2) - local filler = _rep(' ', space + 1) - local str = _sub(filler, 1, space) .. text - str = str .. _sub(filler, self.width - #str + 1) - self:write(1, y, str, bg, fg) + local x = math.floor((self.width-#text) / 2) + 1 + self:write(x, y, text, bg, fg) end end @@ -710,89 +777,19 @@ function UI.Window:print(text, bg, fg) local marginLeft = self.marginLeft or 0 local marginRight = self.marginRight or 0 local width = self.width - marginLeft - marginRight + local cs = { + bg = bg or self:getProperty('backgroundColor'), + fg = fg or self:getProperty('textColor'), + palette = self.palette, + } - local function nextWord(line, cx) - local result = { line:find("(%w+)", cx) } - if #result > 1 and result[2] > cx then - return _sub(line, cx, result[2] + 1) - elseif #result > 0 and result[1] == cx then - result = { line:find("(%w+)", result[2]) } - if #result > 0 then - return _sub(line, cx, result[1] + 1) - end - end - if cx <= #line then - return _sub(line, cx, #line) + local y = (self.marginTop or 0) + 1 + for _,line in pairs(Util.split(text)) do + for _, ln in ipairs(Blit(line, cs):wrap(width)) do + self:blit(marginLeft + 1, y, ln.text, ln.bg, ln.fg) + y = y + 1 end end - - local function pieces(f, bg, fg) - local pos = 1 - local t = { } - while true do - local s = string.find(f, '\027', pos, true) - if not s then - break - end - if pos < s then - table.insert(t, _sub(f, pos, s - 1)) - end - local seq = _sub(f, s) - seq = seq:match("\027%[([%d;]+)m") - local e = { } - for color in string.gmatch(seq, "%d+") do - color = tonumber(color) - if color == 0 then - e.fg = fg - e.bg = bg - elseif color > 20 then - e.bg = 2 ^ (color - 21) - else - e.fg = 2 ^ (color - 1) - end - end - table.insert(t, e) - pos = s + #seq + 3 - end - if pos <= #f then - table.insert(t, _sub(f, pos)) - end - return t - end - - local lines = Util.split(text) - for k,line in pairs(lines) do - local fragments = pieces(line, bg, fg) - for _, fragment in ipairs(fragments) do - local lx = 1 - if type(fragment) == 'table' then -- ansi sequence - fg = fragment.fg - bg = fragment.bg - else - while true do - local word = nextWord(fragment, lx) - if not word then - break - end - local w = word - if self.cursorX + #word > width then - self.cursorX = marginLeft + 1 - self.cursorY = self.cursorY + 1 - w = word:gsub('^ ', '') - end - self:write(self.cursorX, self.cursorY, w, bg, fg) - self.cursorX = self.cursorX + #w - lx = lx + #word - end - end - end - if lines[k + 1] then - self.cursorX = marginLeft + 1 - self.cursorY = self.cursorY + 1 - end - end - - return self.cursorX, self.cursorY end UI.Window.docs.focus = [[focus(VOID) @@ -822,11 +819,11 @@ function UI.Window:release(child) end function UI.Window:pointToChild(x, y) - -- TODO: get rid of this offx/y mess and scroll canvas instead x = x + self.offx - self.x + 1 y = y + self.offy - self.y + 1 if self.children then - for _,child in pairs(self.children) do + for i = #self.children, 1, -1 do + local child = self.children[i] if child.enabled and not child.inactive and x >= child.x and x < child.x + child.width and y >= child.y and y < child.y + child.height then @@ -868,7 +865,7 @@ function UI.Window:getFocusables() end if self.children then - getFocusable(self, self.x, self.y) + getFocusable(self) end return focusable @@ -882,36 +879,28 @@ function UI.Window:focusFirst() end end -function UI.Window:refocus() - local el = self - while el do - local focusables = el:getFocusables() - if focusables[1] then - self:setFocus(focusables[1]) - break - end - el = el.parent - end -end - function UI.Window:scrollIntoView() local parent = self.parent + local offx, offy = parent.offx, parent.offy if self.x <= parent.offx then parent.offx = math.max(0, self.x - 1) - parent:draw() + if offx ~= parent.offx then + parent:draw() + end elseif self.x + self.width > parent.width + parent.offx then parent.offx = self.x + self.width - parent.width - 1 - parent:draw() + if offx ~= parent.offx then + parent:draw() + end end -- TODO: fix local function setOffset(y) parent.offy = y - if parent.canvas then - parent.canvas.offy = parent.offy + if offy ~= parent.offy then + parent:draw() end - parent:draw() end if self.y <= parent.offy then @@ -921,45 +910,8 @@ function UI.Window:scrollIntoView() end end -function UI.Window:getCanvas() - local el = self - repeat - if el.canvas then - return el.canvas - end - el = el.parent - until not el -end - -function UI.Window:addLayer(bg, fg) - local canvas = self:getCanvas() - local x, y = self.x, self.y - local parent = self.parent - while parent and not parent.canvas do - x = x + parent.x - 1 - y = y + parent.y - 1 - parent = parent.parent - end - canvas = canvas:addLayer({ - x = x, y = y, height = self.height, width = self.width - }, bg, fg) - - canvas:clear(bg or self.backgroundColor, fg or self.textColor) - return canvas -end - -function UI.Window:addTransition(effect, args) - if self.parent then - args = args or { } - if not args.x then -- not good - args.x, args.y = self.x, self.y -- getPosition(self) - args.width = self.width - args.height = self.height - end - - args.canvas = args.canvas or self.canvas - self.parent:addTransition(effect, args) - end +function UI.Window:addTransition(effect, args, canvas) + self.parent:addTransition(effect, args, canvas or self) end function UI.Window:emit(event) @@ -991,7 +943,16 @@ function UI.Window:getProperty(property) end function UI.Window:find(uid) - return self.children and Util.find(self.children, 'uid', uid) + local el = self.children and Util.find(self.children, 'uid', uid) + if not el then + for child in self:eachChild() do + el = child:find(uid) + if el then + break + end + end + end + return el end function UI.Window:eventHandler() @@ -1010,18 +971,14 @@ UI.Device.defaults = { function UI.Device:postInit() self.device = self.device or term.current() - --if self.deviceType then - -- self.device = device[self.deviceType] - --end - if not self.device.setTextScale then self.device.setTextScale = function() end end self.device.setTextScale(self.textScale) self.width, self.height = self.device.getSize() - self.isColor = self.device.isColor() + Canvas.init(self, { isColor = self.isColor }) UI.devices[self.device.side or 'terminal'] = self end @@ -1031,8 +988,8 @@ function UI.Device:resize() self.width, self.height = self.device.getSize() self.lines = { } -- TODO: resize all pages added to this device - self.canvas:resize(self.width, self.height) - self.canvas:clear(self.backgroundColor, self.textColor) + Canvas.resize(self, self.width, self.height) + Canvas.clear(self, self.backgroundColor, self.textColor) end function UI.Device:setCursorPos(x, y) @@ -1046,7 +1003,6 @@ end function UI.Device:setCursorBlink(blink) self.cursorBlink = blink - self.device.setCursorBlink(blink) end function UI.Device:setTextScale(textScale) @@ -1061,27 +1017,30 @@ function UI.Device:reset() self.device.setCursorPos(1, 1) end -function UI.Device:addTransition(effect, args) +function UI.Device:addTransition(effect, args, canvas) if not self.transitions then self.transitions = { } end - args = args or { } - args.ex = args.x + args.width - 1 - args.ey = args.y + args.height - 1 - args.canvas = args.canvas or self.canvas - if type(effect) == 'string' then - effect = Transition[effect] - if not effect then - error('Invalid transition') + effect = Transition[effect] or error('Invalid transition') + end + + -- there can be only one + for k,v in pairs(self.transitions) do + if v.canvas == canvas then + table.remove(self.transitions, k) + break end end - table.insert(self.transitions, { update = effect(args), args = args }) + table.insert(self.transitions, { effect = effect, args = args or { }, canvas = canvas }) end -function UI.Device:runTransitions(transitions, canvas) +function UI.Device:runTransitions(transitions) + for _,k in pairs(transitions) do + k.update = k.effect(k.canvas, k.args) + end while true do for _,k in ipairs(Util.keys(transitions)) do local transition = transitions[k] @@ -1089,7 +1048,7 @@ function UI.Device:runTransitions(transitions, canvas) transitions[k] = nil end end - canvas:render(self.device) + self.currentPage:render(self, true) if Util.empty(transitions) then break end @@ -1101,17 +1060,19 @@ function UI.Device:sync() local transitions = self.effectsEnabled and self.transitions self.transitions = nil - if self:getCursorBlink() then - self.device.setCursorBlink(false) - end + self.device.setCursorBlink(false) - self.canvas:render(self.device) if transitions then - self:runTransitions(transitions, self.canvas) + self:runTransitions(transitions) + else + self.currentPage:render(self, true) end if self:getCursorBlink() then self.device.setCursorPos(self.cursorX, self.cursorY) + if self.isColor then + self.device.setTextColor(colors.orange) + end self.device.setCursorBlink(true) end end @@ -1143,7 +1104,7 @@ local function loadComponents() return self(...) end }) - UI[name]._preload = function(self) + UI[name]._preload = function() return load(name) end end @@ -1152,6 +1113,12 @@ end loadComponents() UI:loadTheme('usr/config/ui.theme') Util.merge(UI.Window.defaults, UI.theme.Window) -UI:setDefaultDevice(UI.Device({ device = term.current() })) +Util.merge(UI.colors, UI.theme.colors) +UI:setDefaultDevice(UI.Device()) + +for k,v in pairs(UI.colors) do + Canvas.colorPalette[k] = Canvas.colorPalette[v] + Canvas.grayscalePalette[k] = Canvas.grayscalePalette[v] +end return UI diff --git a/sys/modules/opus/ui/blit.lua b/sys/modules/opus/ui/blit.lua new file mode 100644 index 0000000..a9774d5 --- /dev/null +++ b/sys/modules/opus/ui/blit.lua @@ -0,0 +1,174 @@ +local colors = _G.colors +local _rep = string.rep +local _sub = string.sub + +local Blit = { } + +Blit.colorPalette = { } +Blit.grayscalePalette = { } + +for n = 1, 16 do + Blit.colorPalette[2 ^ (n - 1)] = _sub("0123456789abcdef", n, n) + Blit.grayscalePalette[2 ^ (n - 1)] = _sub("088888878877787f", n, n) +end + +-- default palette +Blit.palette = Blit.colorPalette + +function Blit:init(t, args) + if args then + for k,v in pairs(args) do + self[k] = v + end + end + + if type(t) == 'string' then + -- create a blit from a string + self.text, self.bg, self.fg = Blit.toblit(t, args or { }) + + elseif type(t) == 'number' then + -- create a fixed width blit + self.width = t + self.text = _rep(' ', self.width) + self.bg = _rep(self.palette[args.bg], self.width) + self.fg = _rep(self.palette[args.fg], self.width) + + else + self.text = t.text + self.bg = t.bg + self.fg = t.fg + end +end + +function Blit:write(x, text, bg, fg) + self:insert(x, text, + bg and _rep(self.palette[bg], #text), + fg and _rep(self.palette[fg], #text)) +end + +function Blit:insert(x, text, bg, fg) + if x <= self.width then + local width = #text + local tx, tex + + if x < 1 then + tx = 2 - x + width = width + x - 1 + x = 1 + end + + if x + width - 1 > self.width then + tex = self.width - x + (tx or 1) + width = tex - (tx or 1) + 1 + end + + if width > 0 then + local function replace(sstr, rstr) + if tx or tex then + rstr = _sub(rstr, tx or 1, tex) + end + if x == 1 and width == self.width then + return rstr + elseif x == 1 then + return rstr .. _sub(sstr, x + width) + elseif x + width > self.width then + return _sub(sstr, 1, x - 1) .. rstr + end + return _sub(sstr, 1, x - 1) .. rstr .. _sub(sstr, x + width) + end + + self.text = replace(self.text, text) + if fg then + self.fg = replace(self.fg, fg) + end + if bg then + self.bg = replace(self.bg, bg) + end + end + end +end + +function Blit:sub(s, e) + return Blit({ + text = self.text:sub(s, e), + bg = self.bg:sub(s, e), + fg = self.fg:sub(s, e), + }) +end + +function Blit:wrap(max) + local lines = { } + local data = self + + repeat + if #data.text <= max then + table.insert(lines, data) + break + elseif data.text:sub(max+1, max+1) == ' ' then + table.insert(lines, data:sub(1, max)) + data = data:sub(max + 2) + else + local x = data.text:sub(1, max) + local s = x:match('(.*) ') or x + table.insert(lines, data:sub(1, #s)) + data = data:sub(#s + 1) + end + local t = data.text:match('^%s*(.*)') + local spaces = #data.text - #t + if spaces > 0 then + data = data:sub(spaces + 1) + end + until not data.text or #data.text == 0 + + return lines +end + +-- convert a string of text to blit format doing color conversion +-- and processing ansi color sequences +function Blit.toblit(str, cs) + local text, fg, bg = '', '', '' + + if not cs.cbg then + -- reset colors + cs.rbg = cs.bg or colors.black + cs.rfg = cs.fg or colors.white + -- current colors + cs.cbg = cs.rbg + cs.cfg = cs.rfg + + cs.palette = cs.palette or Blit.palette + end + + str = str:gsub('(.-)\027%[([%d;]+)m', + function(k, seq) + text = text .. k + bg = bg .. string.rep(cs.palette[cs.cbg], #k) + fg = fg .. string.rep(cs.palette[cs.cfg], #k) + for color in string.gmatch(seq, "%d+") do + color = tonumber(color) + if color == 0 then + -- reset to default + cs.cfg = cs.rfg + cs.cbg = cs.rbg + elseif color > 20 then + cs.cbg = 2 ^ (color - 21) + else + cs.cfg = 2 ^ (color - 1) + end + end + return k + end) + + local k = str:sub(#text + 1) + return text .. k, + bg .. string.rep(cs.palette[cs.cbg], #k), + fg .. string.rep(cs.palette[cs.cfg], #k) +end + +return setmetatable(Blit, { + __call = function(_, ...) + local obj = setmetatable({ }, { __index = Blit }) + obj:init(...) + return obj + end +}) diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index 6fe12b1..3003981 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -9,28 +9,34 @@ local colors = _G.colors local Canvas = class() -Canvas.__visualize = false -Canvas.colorPalette = { } -Canvas.darkPalette = { } -Canvas.grayscalePalette = { } - -for n = 1, 16 do - Canvas.colorPalette[2 ^ (n - 1)] = _sub("0123456789abcdef", n, n) - Canvas.grayscalePalette[2 ^ (n - 1)] = _sub("088888878877787f", n, n) - Canvas.darkPalette[2 ^ (n - 1)] = _sub("8777777f77fff77f", n, n) +local function genPalette(map) + local t = { } + local rcolors = Util.transpose(colors) + for n = 1, 16 do + local pow = 2 ^ (n - 1) + local ch = _sub(map, n, n) + t[pow] = ch + t[rcolors[pow]] = ch + end + return t end +Canvas.colorPalette = genPalette('0123456789abcdef') +Canvas.grayscalePalette = genPalette('088888878877787f') + --[[ A canvas can have more lines than canvas.height in order to scroll -]] + TODO: finish vertical scrolling +]] function Canvas:init(args) - self.x = 1 - self.y = 1 - self.layers = { } + self.bg = colors.black + self.fg = colors.white Util.merge(self, args) + self.x = self.x or 1 + self.y = self.y or 1 self.ex = self.x + self.width - 1 self.ey = self.y + self.height - 1 @@ -46,16 +52,31 @@ function Canvas:init(args) for i = 1, self.height do self.lines[i] = { } end + + self:clear() end function Canvas:move(x, y) self.x, self.y = x, y self.ex = self.x + self.width - 1 self.ey = self.y + self.height - 1 + if self.parent then + self.parent:dirty(true) + end end function Canvas:resize(w, h) - for i = #self.lines, h do + self:resizeBuffer(w, h) + + self.ex = self.x + w - 1 + self.ey = self.y + h - 1 + self.width = w + self.height = h +end + +-- resize the canvas buffer - not the canvas itself +function Canvas:resizeBuffer(w, h) + for i = #self.lines + 1, h do self.lines[i] = { } self:clearLine(i) end @@ -66,26 +87,24 @@ function Canvas:resize(w, h) if w < self.width then for i = 1, h do - self.lines[i].text = _sub(self.lines[i].text, 1, w) - self.lines[i].fg = _sub(self.lines[i].fg, 1, w) - self.lines[i].bg = _sub(self.lines[i].bg, 1, w) + local ln = self.lines[i] + ln.text = _sub(ln.text, 1, w) + ln.fg = _sub(ln.fg, 1, w) + ln.bg = _sub(ln.bg, 1, w) end elseif w > self.width then local d = w - self.width local text = _rep(' ', d) - local fg = _rep(self.palette[self.fg or colors.white], d) - local bg = _rep(self.palette[self.bg or colors.black], d) + local fg = _rep(self.palette[self.fg], d) + local bg = _rep(self.palette[self.bg], d) for i = 1, h do - self.lines[i].text = self.lines[i].text .. text - self.lines[i].fg = self.lines[i].fg .. fg - self.lines[i].bg = self.lines[i].bg .. bg + local ln = self.lines[i] + ln.text = ln.text .. text + ln.fg = ln.fg .. fg + ln.bg = ln.bg .. bg + ln.dirty = true end end - - self.ex = self.x + w - 1 - self.ey = self.y + h - 1 - self.width = w - self.height = h end function Canvas:copy() @@ -105,30 +124,26 @@ function Canvas:copy() end function Canvas:addLayer(layer) - local canvas = Canvas({ - x = layer.x, - y = layer.y, - width = layer.width, - height = layer.height, - isColor = self.isColor, - }) - canvas.parent = self - table.insert(self.layers, canvas) - return canvas + layer.parent = self + if not self.children then + self.children = { } + end + table.insert(self.children, 1, layer) + return layer end function Canvas:removeLayer() - for k, layer in pairs(self.parent.layers) do + for k, layer in pairs(self.parent.children) do if layer == self then self:setVisible(false) - table.remove(self.parent.layers, k) + table.remove(self.parent.children, k) break end end end function Canvas:setVisible(visible) - self.visible = visible + self.visible = visible -- TODO: use self.active = visible if not visible and self.parent then self.parent:dirty() -- TODO: set parent's lines to dirty for each line in self @@ -137,11 +152,10 @@ end -- Push a layer to the top function Canvas:raise() - if self.parent then - local layers = self.parent.layers or { } - for k, v in pairs(layers) do + if self.parent and self.parent.children then + for k, v in pairs(self.parent.children) do if v == self then - table.insert(layers, table.remove(layers, k)) + table.insert(self.parent.children, table.remove(self.parent.children, k)) break end end @@ -161,54 +175,42 @@ end function Canvas:blit(x, y, text, bg, fg) if y > 0 and y <= #self.lines and x <= self.width then local width = #text + local tx, tex - -- fix ffs if x < 1 then - text = _sub(text, 2 - x) - if bg then - bg = _sub(bg, 2 - x) - end - if fg then - fg = _sub(fg, 2 - x) - end + tx = 2 - x width = width + x - 1 x = 1 end if x + width - 1 > self.width then - text = _sub(text, 1, self.width - x + 1) - if bg then - bg = _sub(bg, 1, self.width - x + 1) - end - if fg then - fg = _sub(fg, 1, self.width - x + 1) - end - width = #text + tex = self.width - x + (tx or 1) + width = tex - (tx or 1) + 1 end if width > 0 then - - local function replace(sstr, pos, rstr) - if pos == 1 and width == self.width then - return rstr - elseif pos == 1 then - return rstr .. _sub(sstr, pos+width) - elseif pos + width > self.width then - return _sub(sstr, 1, pos-1) .. rstr + local function replace(sstr, rstr) + if tx or tex then + rstr = _sub(rstr, tx or 1, tex) end - return _sub(sstr, 1, pos-1) .. rstr .. _sub(sstr, pos+width) + if x == 1 and width == self.width then + return rstr + elseif x == 1 then + return rstr .. _sub(sstr, x + width) + elseif x + width > self.width then + return _sub(sstr, 1, x - 1) .. rstr + end + return _sub(sstr, 1, x - 1) .. rstr .. _sub(sstr, x + width) end local line = self.lines[y] - if line then - line.dirty = true - line.text = replace(line.text, x, text, width) - if fg then - line.fg = replace(line.fg, x, fg, width) - end - if bg then - line.bg = replace(line.bg, x, bg, width) - end + line.dirty = true + line.text = replace(line.text, text) + if fg then + line.fg = replace(line.fg, fg) + end + if bg then + line.bg = replace(line.bg, bg) end end end @@ -224,15 +226,15 @@ function Canvas:writeLine(y, text, fg, bg) end function Canvas:clearLine(y, bg, fg) - fg = _rep(self.palette[fg or colors.white], self.width) - bg = _rep(self.palette[bg or colors.black], self.width) + fg = _rep(self.palette[fg or self.fg], self.width) + bg = _rep(self.palette[bg or self.bg], self.width) self:writeLine(y, _rep(' ', self.width), fg, bg) end function Canvas:clear(bg, fg) local text = _rep(' ', self.width) - fg = _rep(self.palette[fg or colors.white], self.width) - bg = _rep(self.palette[bg or colors.black], self.width) + fg = _rep(self.palette[fg or self.fg], self.width) + bg = _rep(self.palette[bg or self.bg], self.width) for i = 1, #self.lines do self:writeLine(i, text, fg, bg) end @@ -246,13 +248,16 @@ function Canvas:isDirty() end end -function Canvas:dirty() - for i = 1, #self.lines do - self.lines[i].dirty = true - end - if self.layers then - for _, canvas in pairs(self.layers) do - canvas:dirty() +function Canvas:dirty(includingChildren) + if self.lines then + for i = 1, #self.lines do + self.lines[i].dirty = true + end + + if includingChildren and self.children then + for _, child in pairs(self.children) do + child:dirty(true) + end end end end @@ -278,115 +283,95 @@ function Canvas:applyPalette(palette) self.palette = palette end -function Canvas:render(device) - local offset = { x = 0, y = 0 } - local parent = self.parent - while parent do - offset.x = offset.x + parent.x - 1 - offset.y = offset.y + parent.y - 1 - parent = parent.parent - end - if #self.layers > 0 then - self:__renderLayers(device, offset) - else - self:__blitRect(device, nil, { - x = self.x + offset.x, - y = self.y + offset.y - }) - self:clean() +-- either render directly to the device +-- or use another canvas as a backing buffer +function Canvas:render(device, doubleBuffer) + self.regions = Region.new(self.x, self.y, self.ex, self.ey) + self:__renderLayers(device, { x = self.x - 1, y = self.y - 1 }, doubleBuffer) + + -- doubleBuffering to reduce the amount of + -- setCursorPos, blits + if doubleBuffer then + --[[ + local drew = false + local bg = _rep(2, device.width) + for k,v in pairs(device.lines) do + if v.dirty then + device.device.setCursorPos(device.x, device.y + k - 1) + device.device.blit(v.text, v.fg, bg) + drew = true + end + end + if drew then + local c = os.clock() + repeat until os.clock()-c > .1 + end + ]] + for k,v in pairs(device.lines) do + if v.dirty then + device.device.setCursorPos(device.x, device.y + k - 1) + device.device.blit(v.text, v.fg, v.bg) + v.dirty = false + end + end end end --- regions are comprised of absolute values that coorespond to the output device. +-- regions are comprised of absolute values that correspond to the output device. -- canvases have coordinates relative to their parent. -- canvas layer's stacking order is determined by the position within the array. -- layers in the beginning of the array are overlayed by layers further down in -- the array. -function Canvas:__renderLayers(device, offset) - if #self.layers > 0 then - self.regions = self.regions or Region.new(self.x + offset.x, self.y + offset.y, self.ex + offset.x, self.ey + offset.y) - - for i = 1, #self.layers do - local canvas = self.layers[i] - if canvas.visible then - - -- punch out this area from the parent's canvas - self:__punch(canvas, offset) - +function Canvas:__renderLayers(device, offset, doubleBuffer) + if self.children then + for i = #self.children, 1, -1 do + local canvas = self.children[i] + if canvas.visible or canvas.enabled then -- get the area to render for this layer canvas.regions = Region.new( - canvas.x + offset.x, - canvas.y + offset.y, - canvas.ex + offset.x, - canvas.ey + offset.y) + canvas.x + offset.x - (self.offx or 0), + canvas.y + offset.y - (self.offy or 0), + canvas.ex + offset.x - (self.offx or 0), + canvas.ey + offset.y - (self.offy or 0)) + + -- contain within parent + canvas.regions:andRegion(self.regions) + + -- punch out this area from the parent's canvas + self.regions:subRect( + canvas.x + offset.x - (self.offx or 0), + canvas.y + offset.y - (self.offy or 0), + canvas.ex + offset.x - (self.offx or 0), + canvas.ey + offset.y - (self.offy or 0)) - -- punch out any layers that overlap this one - for j = i + 1, #self.layers do - if self.layers[j].visible then - canvas:__punch(self.layers[j], offset) - end - end if #canvas.regions.region > 0 then canvas:__renderLayers(device, { - x = canvas.x + offset.x - 1, - y = canvas.y + offset.y - 1, - }) + x = canvas.x + offset.x - 1 - (self.offx or 0), + y = canvas.y + offset.y - 1 - (self.offy or 0), + }, doubleBuffer) end canvas.regions = nil end end - - self:__blitClipped(device, offset) - self.regions = nil - - elseif self.regions and #self.regions.region > 0 then - self:__blitClipped(device, offset) - self.regions = nil - - else - self:__blitRect(device, nil, { - x = self.x + offset.x, - y = self.y + offset.y - }) - self.regions = nil - end - self:clean() -end - -function Canvas:__blitClipped(device, offset) - if self.parent then - -- contain the rendered region in the parent's region - local p = Region.new(1, 1, - self.parent.width + offset.x - self.x + 1, - self.parent.height + offset.y - self.y + 1) - self.regions:andRegion(p) end for _,region in ipairs(self.regions.region) do self:__blitRect(device, { x = region[1] - offset.x, - y = region[2] - offset.y, - ex = region[3] - offset.x, - ey = region[4] - offset.y}, - { x = region[1], y = region[2] }) + y = region[2] - offset.y, + ex = region[3] - offset.x, + ey = region[4] - offset.y }, + { x = region[1], y = region[2] }, doubleBuffer) end + self.regions = nil + + self:clean() end -function Canvas:__punch(rect, offset) - self.regions:subRect( - rect.x + offset.x, - rect.y + offset.y, - rect.ex + offset.x, - rect.ey + offset.y) -end - --- performance can probably be improved by using one more buffer tied to the device -function Canvas:__blitRect(device, src, tgt) - src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 } - tgt = tgt or self - +function Canvas:__blitRect(device, src, tgt, doubleBuffer) -- for visualizing updates on the screen - if Canvas.__visualize then + --[[ + if Canvas.__visualize or self.visualize then local drew local t = _rep(' ', src.ex-src.x + 1) local bg = _rep(2, src.ex-src.x + 1) @@ -399,10 +384,11 @@ function Canvas:__blitRect(device, src, tgt) end end if drew then - local t = os.clock() - repeat until os.clock()-t > .2 + local c = os.clock() + repeat until os.clock()-c > .03 end end + ]] for i = 0, src.ey - src.y do local line = self.lines[src.y + i + (self.offy or 0)] if line and line.dirty then @@ -412,8 +398,13 @@ function Canvas:__blitRect(device, src, tgt) fg = _sub(fg, src.x, src.ex) bg = _sub(bg, src.x, src.ex) end - device.setCursorPos(tgt.x, tgt.y + i) - device.blit(t, fg, bg) + if doubleBuffer then + Canvas.blit(device, tgt.x, tgt.y + i, + t, bg, fg) + else + device.setCursorPos(tgt.x, tgt.y + i) + device.blit(t, fg, bg) + end end end end diff --git a/sys/modules/opus/ui/components/ActiveLayer.lua b/sys/modules/opus/ui/components/ActiveLayer.lua deleted file mode 100644 index dcbb499..0000000 --- a/sys/modules/opus/ui/components/ActiveLayer.lua +++ /dev/null @@ -1,32 +0,0 @@ -local class = require('opus.class') -local UI = require('opus.ui') - -UI.ActiveLayer = class(UI.Window) -UI.ActiveLayer.defaults = { - UIElement = 'ActiveLayer', -} -function UI.ActiveLayer:layout() - UI.Window.layout(self) - if not self.canvas then - self.canvas = self:addLayer() - else - self.canvas:resize(self.width, self.height) - end -end - -function UI.ActiveLayer:enable(...) - self.canvas:raise() - self.canvas:setVisible(true) - UI.Window.enable(self, ...) - if self.parent.transitionHint then - self:addTransition(self.parent.transitionHint) - end - self:focusFirst() -end - -function UI.ActiveLayer:disable() - if self.canvas then - self.canvas:setVisible(false) - end - UI.Window.disable(self) -end diff --git a/sys/modules/opus/ui/components/Button.lua b/sys/modules/opus/ui/components/Button.lua index 2442614..426d3e0 100644 --- a/sys/modules/opus/ui/components/Button.lua +++ b/sys/modules/opus/ui/components/Button.lua @@ -2,32 +2,30 @@ local class = require('opus.class') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors - UI.Button = class(UI.Window) UI.Button.defaults = { UIElement = 'Button', text = 'button', - backgroundColor = colors.lightGray, - backgroundFocusColor = colors.gray, - textFocusColor = colors.white, - textInactiveColor = colors.gray, - textColor = colors.black, + backgroundColor = 'lightGray', + backgroundFocusColor = 'gray', + textFocusColor = 'white', + textInactiveColor = 'gray', + textColor = 'black', centered = true, height = 1, focusIndicator = ' ', event = 'button_press', accelerators = { - space = 'button_activate', + [ ' ' ] = 'button_activate', enter = 'button_activate', mouse_click = 'button_activate', } } -function UI.Button:setParent() +function UI.Button:layout() if not self.width and not self.ex then - self.width = #self.text + 2 + self.width = self.noPadding and #self.text or #self.text + 2 end - UI.Window.setParent(self) + UI.Window.layout(self) end function UI.Button:draw() @@ -35,13 +33,13 @@ function UI.Button:draw() local bg = self.backgroundColor local ind = ' ' if self.focused then - bg = self.backgroundFocusColor - fg = self.textFocusColor + bg = self:getProperty('backgroundFocusColor') + fg = self:getProperty('textFocusColor') ind = self.focusIndicator elseif self.inactive then - fg = self.textInactiveColor + fg = self:getProperty('textInactiveColor') end - local text = ind .. self.text .. ' ' + local text = self.noPadding and self.text or ind .. self.text .. ' ' if self.centered then self:clear(bg) self:centeredWrite(1 + math.floor(self.height / 2), text, bg, fg) @@ -59,7 +57,7 @@ end function UI.Button:eventHandler(event) if event.type == 'button_activate' then - self:emit({ type = self.event, button = self }) + self:emit({ type = self.event, button = self, element = self }) return true end return false @@ -73,7 +71,7 @@ function UI.Button.example() }, button2 = UI.Button { x = 2, y = 4, - backgroundColor = colors.green, + backgroundColor = 'green', event = 'custom_event', }, button3 = UI.Button { diff --git a/sys/modules/opus/ui/components/Checkbox.lua b/sys/modules/opus/ui/components/Checkbox.lua index 567833e..0322ad6 100644 --- a/sys/modules/opus/ui/components/Checkbox.lua +++ b/sys/modules/opus/ui/components/Checkbox.lua @@ -1,8 +1,6 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.Checkbox = class(UI.Window) UI.Checkbox.defaults = { UIElement = 'Checkbox', @@ -11,9 +9,9 @@ UI.Checkbox.defaults = { leftMarker = UI.extChars and '\124' or '[', rightMarker = UI.extChars and '\124' or ']', value = false, - textColor = colors.white, - backgroundColor = colors.black, - backgroundFocusColor = colors.lightGray, + textColor = 'white', + backgroundColor = 'black', + backgroundFocusColor = 'lightGray', height = 1, width = 3, accelerators = { @@ -21,11 +19,9 @@ UI.Checkbox.defaults = { mouse_click = 'checkbox_toggle', } } -UI.Checkbox.inherits = { - labelBackgroundColor = 'backgroundColor', -} -function UI.Checkbox:postInit() +function UI.Checkbox:layout() self.width = self.label and #self.label + 4 or 3 + UI.Window.layout(self) end function UI.Checkbox:draw() diff --git a/sys/modules/opus/ui/components/Chooser.lua b/sys/modules/opus/ui/components/Chooser.lua index 32fd3e2..6aa2e4a 100644 --- a/sys/modules/opus/ui/components/Chooser.lua +++ b/sys/modules/opus/ui/components/Chooser.lua @@ -11,8 +11,8 @@ UI.Chooser.defaults = { nochoice = 'Select', backgroundFocusColor = colors.lightGray, textInactiveColor = colors.gray, - leftIndicator = UI.extChars and '\17' or '<', - rightIndicator = UI.extChars and '\16' or '>', + leftIndicator = UI.extChars and '\171' or '<', + rightIndicator = UI.extChars and '\187' or '>', height = 1, accelerators = { space = 'choice_next', @@ -20,7 +20,7 @@ UI.Chooser.defaults = { left = 'choice_prev', } } -function UI.Chooser:setParent() +function UI.Chooser:layout() if not self.width and not self.ex then self.width = 1 for _,v in pairs(self.choices) do @@ -30,7 +30,7 @@ function UI.Chooser:setParent() end self.width = self.width + 4 end - UI.Window.setParent(self) + UI.Window.layout(self) end function UI.Chooser:draw() diff --git a/sys/modules/opus/ui/components/Dialog.lua b/sys/modules/opus/ui/components/Dialog.lua index dd9de1a..b2d1639 100644 --- a/sys/modules/opus/ui/components/Dialog.lua +++ b/sys/modules/opus/ui/components/Dialog.lua @@ -1,15 +1,11 @@ -local Canvas = require('opus.ui.canvas') local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.Dialog = class(UI.SlideOut) UI.Dialog.defaults = { UIElement = 'Dialog', height = 7, - textColor = colors.black, - backgroundColor = colors.white, + noFill = true, okEvent ='dialog_ok', cancelEvent = 'dialog_cancel', } @@ -18,22 +14,36 @@ function UI.Dialog:postInit() self.titleBar = UI.TitleBar({ event = self.cancelEvent, title = self.title }) end -function UI.Dialog:show(...) - local canvas = self.parent:getCanvas() - self.oldPalette = canvas.palette - canvas:applyPalette(Canvas.darkPalette) - UI.SlideOut.show(self, ...) -end - -function UI.Dialog:hide(...) - self.parent:getCanvas().palette = self.oldPalette - UI.SlideOut.hide(self, ...) - self.parent:draw() -end - function UI.Dialog:eventHandler(event) if event.type == 'dialog_cancel' then self:hide() end return UI.SlideOut.eventHandler(self, event) end + +function UI.Dialog.example() + return UI.Dialog { + title = 'Enter Starting Level', + height = 7, + form = UI.Form { + y = 3, x = 2, height = 4, + event = 'setStartLevel', + cancelEvent = 'slide_hide', + text = UI.Text { + x = 5, y = 1, width = 20, + textColor = 'gray', + }, + textEntry = UI.TextEntry { + formKey = 'level', + x = 15, y = 1, width = 7, + }, + }, + statusBar = UI.StatusBar(), + enable = function(self) + require('opus.event').onTimeout(0, function() + self:show() + self:sync() + end) + end, + } +end diff --git a/sys/modules/opus/ui/components/DropMenu.lua b/sys/modules/opus/ui/components/DropMenu.lua index 41bdaf9..8e59a91 100644 --- a/sys/modules/opus/ui/components/DropMenu.lua +++ b/sys/modules/opus/ui/components/DropMenu.lua @@ -2,12 +2,10 @@ local class = require('opus.class') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors - UI.DropMenu = class(UI.MenuBar) UI.DropMenu.defaults = { UIElement = 'DropMenu', - backgroundColor = colors.white, + backgroundColor = 'white', buttonClass = 'DropMenuItem', } function UI.DropMenu:layout() @@ -32,42 +30,53 @@ function UI.DropMenu:layout() self.height = #self.children + 1 self.width = maxWidth + 2 - if not self.canvas then - self.canvas = self:addLayer() - else - self.canvas:resize(self.width, self.height) + if self.x + self.width > self.parent.width then + self.x = self.parent.width - self.width + 1 end + + self:reposition(self.x, self.y, self.width, self.height) end function UI.DropMenu:enable() -end + local menuBar = self.parent:find(self.menuUid) + local hasActive -function UI.DropMenu:show(x, y) - self.x, self.y = x, y - self.canvas:move(x, y) - self.canvas:setVisible(true) + for _,c in pairs(self.children) do + if not c.spacer and menuBar then + c.inactive = not menuBar:getActive(c) + end + if not c.inactive then + hasActive = true + end + end + + -- jump through a lot of hoops if all selections are inactive + -- there's gotta be a better way + -- lots of exception code just to handle drop menus + self.focus = not hasActive and function() end UI.Window.enable(self) - + if self.focus then + self:setFocus(self) + else + self:focusFirst() + end self:draw() - self:capture(self) - self:focusFirst() end -function UI.DropMenu:hide() - self:disable() - self.canvas:setVisible(false) - self:release(self) +function UI.DropMenu:disable() + UI.Window.disable(self) + self:remove() end function UI.DropMenu:eventHandler(event) if event.type == 'focus_lost' and self.enabled then - if not Util.contains(self.children, event.focused) then - self:hide() + if not (Util.contains(self.children, event.focused) or event.focused == self) then + self:disable() end elseif event.type == 'mouse_out' and self.enabled then - self:hide() - self:refocus() + self:disable() + self:setFocus(self.parent:find(self.lastFocus)) else return UI.MenuBar.eventHandler(self, event) end @@ -83,6 +92,15 @@ function UI.DropMenu.example() { spacer = true }, { text = 'Quit ^q', event = 'quit' }, } }, + { text = 'Edit', dropdown = { + { text = 'Copy', event = 'run' }, + { text = 'Paste s', event = 'shell' }, + } }, + { text = '\187', + x = -3, + dropdown = { + { text = 'Associations', event = 'associate' }, + } }, } } end diff --git a/sys/modules/opus/ui/components/DropMenuItem.lua b/sys/modules/opus/ui/components/DropMenuItem.lua index ff28047..e1f2dd8 100644 --- a/sys/modules/opus/ui/components/DropMenuItem.lua +++ b/sys/modules/opus/ui/components/DropMenuItem.lua @@ -1,20 +1,18 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.DropMenuItem = class(UI.Button) UI.DropMenuItem.defaults = { UIElement = 'DropMenuItem', - textColor = colors.black, - backgroundColor = colors.white, - textFocusColor = colors.white, - textInactiveColor = colors.lightGray, - backgroundFocusColor = colors.lightGray, + textColor = 'black', + backgroundColor = 'white', + textFocusColor = 'white', + textInactiveColor = 'lightGray', + backgroundFocusColor = 'lightGray', } function UI.DropMenuItem:eventHandler(event) if event.type == 'button_activate' then - self.parent:hide() + self.parent:disable() end return UI.Button.eventHandler(self, event) end diff --git a/sys/modules/opus/ui/components/Embedded.lua b/sys/modules/opus/ui/components/Embedded.lua index e15df33..f7996ce 100644 --- a/sys/modules/opus/ui/components/Embedded.lua +++ b/sys/modules/opus/ui/components/Embedded.lua @@ -1,62 +1,63 @@ local class = require('opus.class') +local Event = require('opus.event') local Terminal = require('opus.terminal') local UI = require('opus.ui') -local colors = _G.colors - UI.Embedded = class(UI.Window) UI.Embedded.defaults = { UIElement = 'Embedded', - backgroundColor = colors.black, - textColor = colors.white, + backgroundColor = 'black', + textColor = 'white', maxScroll = 100, accelerators = { up = 'scroll_up', down = 'scroll_down', } } -function UI.Embedded:setParent() - UI.Window.setParent(self) - - self.win = Terminal.window(UI.term.device, self.x, self.y, self.width, self.height, false) - self.win.setMaxScroll(self.maxScroll) - - local canvas = self:getCanvas() - self.win.getCanvas().parent = canvas - table.insert(canvas.layers, self.win.getCanvas()) - self.canvas = self.win.getCanvas() - - self.win.setCursorPos(1, 1) - self.win.setBackgroundColor(self.backgroundColor) - self.win.setTextColor(self.textColor) - self.win.clear() -end - function UI.Embedded:layout() UI.Window.layout(self) - if self.win then - self.win.reposition(self.x, self.y, self.width, self.height) + + if not self.win then + local t + function self.render() + if not t then + t = Event.onTimeout(0, function() + t = nil + if self.focused then + self:setCursorPos(self.win.getCursorPos()) + end + self:sync() + end) + end + end + self.win = Terminal.window(UI.term.device, self.x, self.y, self.width, self.height, false) + self.win.canvas = self + self.win.setMaxScroll(self.maxScroll) + self.win.setCursorPos(1, 1) + self.win.setBackgroundColor(self.backgroundColor) + self.win.setTextColor(self.textColor) + self.win.clear() end end function UI.Embedded:draw() - self.canvas:dirty() + self:dirty() +end + +function UI.Embedded:focus() + -- allow scrolling + if self.focused then + self:setCursorBlink(self.win.getCursorBlink()) + end end function UI.Embedded:enable() - self.canvas:setVisible(true) - self.canvas:raise() - if self.visible then - -- the window will automatically update on changes - -- the canvas does not need to be rendereed - self.win.setVisible(true) - end UI.Window.enable(self) - self.canvas:dirty() + self.win.setVisible(true) + self:dirty() end function UI.Embedded:disable() - self.canvas:setVisible(false) self.win.setVisible(false) UI.Window.disable(self) end @@ -71,17 +72,12 @@ function UI.Embedded:eventHandler(event) end end -function UI.Embedded:focus() - -- allow scrolling -end - function UI.Embedded.example() - local Event = require('opus.event') local Util = require('opus.util') local term = _G.term return UI.Embedded { - visible = true, + y = 2, x = 2, ex = -2, ey = -2, enable = function (self) UI.Embedded.enable(self) Event.addRoutine(function() @@ -90,10 +86,11 @@ function UI.Embedded.example() term.redirect(oterm) end) end, - eventHandler = function(_, event) + eventHandler = function(self, event) if event.type == 'key' then return true end + return UI.Embedded.eventHandler(self, event) end } end diff --git a/sys/modules/opus/ui/components/FileSelect.lua b/sys/modules/opus/ui/components/FileSelect.lua new file mode 100644 index 0000000..55b86b7 --- /dev/null +++ b/sys/modules/opus/ui/components/FileSelect.lua @@ -0,0 +1,118 @@ +local class = require('opus.class') +local UI = require('opus.ui') +local Util = require('opus.util') + +local colors = _G.colors +local fs = _G.fs + +UI.FileSelect = class(UI.Window) +UI.FileSelect.defaults = { + UIElement = 'FileSelect', +} +function UI.FileSelect:postInit() + self.grid = UI.ScrollingGrid { + x = 2, y = 2, ex = -2, ey = -4, + dir = '/', + sortColumn = 'name', + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Size', key = 'size', width = 5 } + }, + getDisplayValues = function(_, row) + if row.size then + row = Util.shallowCopy(row) + row.size = Util.toBytes(row.size) + end + return row + end, + getRowTextColor = function(_, file) + if file.isDir then + return colors.cyan + end + if file.isReadOnly then + return colors.pink + end + return colors.white + end, + sortCompare = function(self, a, b) + if self.sortColumn == 'size' then + return a.size < b.size + end + if a.isDir == b.isDir then + return a.name:lower() < b.name:lower() + end + return a.isDir + end, + draw = function(self) + local files = fs.listEx(self.dir) + if #self.dir > 0 then + table.insert(files, { + name = '..', + isDir = true, + }) + end + self:setValues(files) + self:setIndex(1) + UI.Grid.draw(self) + end, + } + self.path = UI.TextEntry { + x = 2, + y = -2, + ex = -11, + limit = 256, + accelerators = { + enter = 'path_enter', + } + } + self.cancel = UI.Button { + text = 'Cancel', + x = -9, + y = -2, + event = 'select_cancel', + } +end + +function UI.FileSelect:draw() + self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray) + self:drawChildren() +end + +function UI.FileSelect:enable(path) + self:setPath(path or '') + UI.Window.enable(self) +end + +function UI.FileSelect:setPath(path) + self.grid.dir = path + while not fs.isDir(self.grid.dir) do + self.grid.dir = fs.getDir(self.grid.dir) + end + self.path.value = self.grid.dir +end + +function UI.FileSelect:eventHandler(event) + if event.type == 'grid_select' then + self.grid.dir = fs.combine(self.grid.dir, event.selected.name) + self.path.value = self.grid.dir + if event.selected.isDir then + self.grid:draw() + self.path:draw() + else + self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) + end + return true + + elseif event.type == 'path_enter' then + if self.path.value then + if fs.isDir(self.path.value) then + self:setPath(self.path.value) + self.grid:draw() + self.path:draw() + else + self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) + end + end + return true + end +end diff --git a/sys/modules/opus/ui/components/FlatButton.lua b/sys/modules/opus/ui/components/FlatButton.lua new file mode 100644 index 0000000..c9a5ad0 --- /dev/null +++ b/sys/modules/opus/ui/components/FlatButton.lua @@ -0,0 +1,16 @@ +local class = require('opus.class') +local UI = require('opus.ui') + +UI.FlatButton = class(UI.Button) +UI.FlatButton.defaults = { + UIElement = 'FlatButton', + textColor = 'black', + textFocusColor = 'white', + noPadding = true, +} +function UI.FlatButton:setParent() + self.backgroundColor = self.parent:getProperty('backgroundColor') + self.backgroundFocusColor = self.backgroundColor + + UI.Button.setParent(self) +end diff --git a/sys/modules/opus/ui/components/Form.lua b/sys/modules/opus/ui/components/Form.lua index bda9850..92a39a7 100644 --- a/sys/modules/opus/ui/components/Form.lua +++ b/sys/modules/opus/ui/components/Form.lua @@ -2,8 +2,6 @@ local class = require('opus.class') local Sound = require('opus.sound') local UI = require('opus.ui') -local colors = _G.colors - UI.Form = class(UI.Window) UI.Form.defaults = { UIElement = 'Form', @@ -68,7 +66,7 @@ function UI.Form:createForm() table.insert(self.children, UI.Text { x = self.margin, y = child.y, - textColor = colors.black, + textColor = 'black', width = #child.formLabel, value = child.formLabel, }) diff --git a/sys/modules/opus/ui/components/Grid.lua b/sys/modules/opus/ui/components/Grid.lua index 1d6e480..80189f5 100644 --- a/sys/modules/opus/ui/components/Grid.lua +++ b/sys/modules/opus/ui/components/Grid.lua @@ -2,10 +2,8 @@ local class = require('opus.class') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors local os = _G.os local _rep = string.rep -local _sub = string.sub local function safeValue(v) local t = type(v) @@ -23,18 +21,7 @@ function Writer:init(element, y) end function Writer:write(s, width, align, bg, fg) - local len = #tostring(s or '') - if len > width then - s = _sub(s, 1, width) - end - local padding = len < width and _rep(' ', width - len) - if padding then - if align == 'right' then - s = padding .. s - else - s = s .. padding - end - end + s = Util.widthify(s, width, align) self.element:write(self.x, self.y, s, bg, fg) self.x = self.x + width end @@ -56,16 +43,16 @@ UI.Grid.defaults = { disableHeader = false, headerHeight = 1, marginRight = 0, - textColor = colors.white, - textSelectedColor = colors.white, - backgroundColor = colors.black, - backgroundSelectedColor = colors.gray, - headerBackgroundColor = colors.cyan, - headerTextColor = colors.white, - headerSortColor = colors.yellow, - unfocusedTextSelectedColor = colors.white, - unfocusedBackgroundSelectedColor = colors.gray, - focusIndicator = UI.extChars and '\183' or '>', + textColor = 'white', + textSelectedColor = 'white', + backgroundColor = 'black', + backgroundSelectedColor = 'gray', + headerBackgroundColor = 'primary', + headerTextColor = 'white', + headerSortColor = 'yellow', + unfocusedTextSelectedColor = 'white', + unfocusedBackgroundSelectedColor = 'gray', + focusIndicator = UI.extChars and '\26' or '>', sortIndicator = ' ', inverseSortIndicator = UI.extChars and '\24' or '^', values = { }, @@ -83,8 +70,8 @@ UI.Grid.defaults = { [ 'control-f' ] = 'scroll_pageDown', }, } -function UI.Grid:setParent() - UI.Window.setParent(self) +function UI.Grid:layout() + UI.Window.layout(self) for _,c in pairs(self.columns) do c.cw = c.width @@ -522,7 +509,7 @@ function UI.Grid.example() values = values, columns = { { heading = 'key', key = 'key', width = 6, }, - { heading = 'value', key = 'value', textColor = colors.yellow }, + { heading = 'value', key = 'value', textColor = 'yellow' }, }, }, autospace = UI.Grid { diff --git a/sys/modules/opus/ui/components/Image.lua b/sys/modules/opus/ui/components/Image.lua index 787d813..7d67c35 100644 --- a/sys/modules/opus/ui/components/Image.lua +++ b/sys/modules/opus/ui/components/Image.lua @@ -1,19 +1,28 @@ local class = require('opus.class') local UI = require('opus.ui') +local Util = require('opus.util') +local lookup = '0123456789abcdef' + +-- handle files produced by Paint UI.Image = class(UI.Window) UI.Image.defaults = { UIElement = 'Image', event = 'button_press', } -function UI.Image:setParent() - if self.image then +function UI.Image:postInit() + if self.filename then + self.image = Util.readLines(self.filename) + end + + if self.image and not (self.height or self.ey) then self.height = #self.image end - if self.image and not self.width then - self.width = #self.image[1] + if self.image and not (self.width or self.ex) then + for i = 1, self.height do + self.width = math.max(self.width or 0, #self.image[i]) + end end - UI.Window.setParent(self) end function UI.Image:draw() @@ -22,19 +31,22 @@ function UI.Image:draw() for y = 1, #self.image do local line = self.image[y] for x = 1, #line do - local ch = line[x] - if type(ch) == 'number' then - if ch > 0 then - self:write(x, y, ' ', ch) - end - else - self:write(x, y, ch) + local ch = lookup:find(line:sub(x, x)) + if ch then + self:write(x, y, ' ', 2 ^ (ch -1)) end end end end + self:drawChildren() end function UI.Image:setImage(image) self.image = image end + +function UI.Image.example() + return UI.Image { + filename = 'test.paint', + } +end diff --git a/sys/modules/opus/ui/components/Menu.lua b/sys/modules/opus/ui/components/Menu.lua index 0e18682..b5462cf 100644 --- a/sys/modules/opus/ui/components/Menu.lua +++ b/sys/modules/opus/ui/components/Menu.lua @@ -13,8 +13,7 @@ function UI.Menu:postInit() self.pageSize = #self.menuItems end -function UI.Menu:setParent() - UI.Grid.setParent(self) +function UI.Menu:layout() self.itemWidth = 1 for _,v in pairs(self.values) do if #v.prompt > self.itemWidth then @@ -28,6 +27,7 @@ function UI.Menu:setParent() else self.width = self.itemWidth + 2 end + UI.Grid.layout(self) end function UI.Menu:center() diff --git a/sys/modules/opus/ui/components/MenuBar.lua b/sys/modules/opus/ui/components/MenuBar.lua index 1b544cc..7ddf97b 100644 --- a/sys/modules/opus/ui/components/MenuBar.lua +++ b/sys/modules/opus/ui/components/MenuBar.lua @@ -1,28 +1,15 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - -local function getPosition(element) - local x, y = 1, 1 - repeat - x = element.x + x - 1 - y = element.y + y - 1 - element = element.parent - until not element - return x, y -end - UI.MenuBar = class(UI.Window) UI.MenuBar.defaults = { UIElement = 'MenuBar', buttons = { }, height = 1, - backgroundColor = colors.lightGray, - textColor = colors.black, + backgroundColor = 'secondary', + textColor = 'black', spacing = 2, lastx = 1, - showBackButton = false, buttonClass = 'MenuItem', } function UI.MenuBar:postInit() @@ -62,10 +49,6 @@ function UI.MenuBar:addButtons(buttons) else table.insert(self.children, button) end - - if button.dropdown then - button.dropmenu = UI.DropMenu { buttons = button.dropdown } - end end end if self.parent then @@ -78,23 +61,28 @@ function UI.MenuBar:getActive(menuItem) end function UI.MenuBar:eventHandler(event) - if event.type == 'button_press' and event.button.dropmenu then - if event.button.dropmenu.enabled then - event.button.dropmenu:hide() - self:refocus() - return true - else - local x, y = getPosition(event.button) - if x + event.button.dropmenu.width > self.width then - x = self.width - event.button.dropmenu.width + 1 - end - for _,c in pairs(event.button.dropmenu.children) do - if not c.spacer then - c.inactive = not self:getActive(c) - end - end - event.button.dropmenu:show(x, y + 1) + if event.type == 'button_press' and event.button.dropdown then + local function getPosition(element) + local x, y = 1, 1 + repeat + x = element.x + x - 1 + y = element.y + y - 1 + element = element.parent + until not element + return x, y end + + local x, y = getPosition(event.button) + + local menu = UI.DropMenu { + buttons = event.button.dropdown, + x = x, + y = y + 1, + lastFocus = event.button.uid, + menuUid = self.uid, + } + self.parent:add({ dropmenu = menu }) + return true end end @@ -103,7 +91,8 @@ function UI.MenuBar.example() return UI.MenuBar { buttons = { { text = 'Choice1', event = 'event1' }, - { text = 'Choice2', event = 'event2' }, + { text = 'Choice2', event = 'event2', inactive = true }, + { text = 'Choice3', event = 'event3' }, } } end diff --git a/sys/modules/opus/ui/components/MenuItem.lua b/sys/modules/opus/ui/components/MenuItem.lua index 61bc0b1..26bcf91 100644 --- a/sys/modules/opus/ui/components/MenuItem.lua +++ b/sys/modules/opus/ui/components/MenuItem.lua @@ -1,13 +1,9 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - -UI.MenuItem = class(UI.Button) +UI.MenuItem = class(UI.FlatButton) UI.MenuItem.defaults = { UIElement = 'MenuItem', - textColor = colors.black, - backgroundColor = colors.lightGray, - textFocusColor = colors.white, - backgroundFocusColor = colors.lightGray, + noPadding = false, + textInactiveColor = 'gray', } diff --git a/sys/modules/opus/ui/components/MiniSlideOut.lua b/sys/modules/opus/ui/components/MiniSlideOut.lua new file mode 100644 index 0000000..4d68a0e --- /dev/null +++ b/sys/modules/opus/ui/components/MiniSlideOut.lua @@ -0,0 +1,31 @@ +local class = require('opus.class') +local UI = require('opus.ui') + +UI.MiniSlideOut = class(UI.SlideOut) +UI.MiniSlideOut.defaults = { + UIElement = 'MiniSlideOut', + noFill = true, + backgroundColor = 'primary', + height = 1, +} +function UI.MiniSlideOut:postInit() + self.close_button = UI.Button { + x = -1, + backgroundColor = self.backgroundColor, + backgroundFocusColor = self.backgroundColor, + text = 'x', + event = 'slide_hide', + noPadding = true, + } + if self.label then + self.label_text = UI.Text { + x = 2, + value = self.label, + } + end +end + +function UI.MiniSlideOut:show(...) + UI.SlideOut.show(self, ...) + self:addTransition('slideLeft', { easing = 'outBounce' }) +end diff --git a/sys/modules/opus/ui/components/NftImage.lua b/sys/modules/opus/ui/components/NftImage.lua index 4d295d7..6c5158f 100644 --- a/sys/modules/opus/ui/components/NftImage.lua +++ b/sys/modules/opus/ui/components/NftImage.lua @@ -5,17 +5,18 @@ UI.NftImage = class(UI.Window) UI.NftImage.defaults = { UIElement = 'NftImage', } -function UI.NftImage:setParent() - if self.image then +function UI.NftImage:postInit() + if self.image and not (self.ey or self.height) then self.height = self.image.height end - if self.image and not self.width then + if self.image and not (self.ex or self.width) then self.width = self.image.width end - UI.Window.setParent(self) end function UI.NftImage:draw() + self:clear() + if self.image then -- due to blittle, the background and foreground transparent -- color is the same as the background color @@ -25,8 +26,6 @@ function UI.NftImage:draw() self:write(x, y, self.image.text[y][x], self.image.bg[y][x], self.image.fg[y][x] or bg) end end - else - self:clear() end end diff --git a/sys/modules/opus/ui/components/Notification.lua b/sys/modules/opus/ui/components/Notification.lua index 701733e..2d52872 100644 --- a/sys/modules/opus/ui/components/Notification.lua +++ b/sys/modules/opus/ui/components/Notification.lua @@ -4,36 +4,34 @@ local Sound = require('opus.sound') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors - UI.Notification = class(UI.Window) UI.Notification.defaults = { UIElement = 'Notification', - backgroundColor = colors.gray, + backgroundColor = 'gray', closeInd = UI.extChars and '\215' or '*', height = 3, timeout = 3, anchor = 'bottom', } -function UI.Notification:draw() +function UI.Notification.draw() end -function UI.Notification:enable() +function UI.Notification.enable() end function UI.Notification:error(value, timeout) - self.backgroundColor = colors.red + self.backgroundColor = 'red' Sound.play('entity.villager.no', .5) self:display(value, timeout) end function UI.Notification:info(value, timeout) - self.backgroundColor = colors.lightGray + self.backgroundColor = 'lightGray' self:display(value, timeout) end function UI.Notification:success(value, timeout) - self.backgroundColor = colors.green + self.backgroundColor = 'green' self:display(value, timeout) end @@ -43,32 +41,34 @@ function UI.Notification:cancel() self.timer = nil end - if self.canvas then - self.enabled = false - self.canvas:removeLayer() - self.canvas = nil - end + self:disable() end function UI.Notification:display(value, timeout) - self:cancel() - self.enabled = true local lines = Util.wordWrap(value, self.width - 3) + + self.enabled = true self.height = #lines if self.anchor == 'bottom' then self.y = self.parent.height - self.height + 1 - self.canvas = self:addLayer(self.backgroundColor, self.textColor) self:addTransition('expandUp', { ticks = self.height }) else - self.canvas = self:addLayer(self.backgroundColor, self.textColor) self.y = 1 end - self.canvas:setVisible(true) + + self:reposition(self.x, self.y, self.width, self.height) + self:raise() self:clear() for k,v in pairs(lines) do self:write(2, k, v) end + self:write(self.width, 1, self.closeInd) + + if self.timer then + Event.off(self.timer) + self.timer = nil + end timeout = timeout or self.timeout if timeout > 0 then @@ -77,7 +77,6 @@ function UI.Notification:display(value, timeout) self:sync() end) else - self:write(self.width, 1, self.closeInd) self:sync() end end @@ -92,7 +91,7 @@ function UI.Notification:eventHandler(event) end function UI.Notification.example() - return UI.ActiveLayer { + return UI.Window { notify1 = UI.Notification { anchor = 'top', }, @@ -111,7 +110,9 @@ function UI.Notification.example() if event.type == 'test_success' then self.notify1:success('Example text') elseif event.type == 'test_error' then - self.notify2:error('Example text', 0) + self.notify2:error([[Example text test test +test test test test test +test test test]], 0) end end, } diff --git a/sys/modules/opus/ui/components/Page.lua b/sys/modules/opus/ui/components/Page.lua index 7bc8e70..9daed63 100644 --- a/sys/modules/opus/ui/components/Page.lua +++ b/sys/modules/opus/ui/components/Page.lua @@ -1,60 +1,31 @@ -local Canvas = require('opus.ui.canvas') local class = require('opus.class') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors - --- need to add offsets to this test -local function getPosition(element) - local x, y = 1, 1 - repeat - x = element.x + x - 1 - y = element.y + y - 1 - element = element.parent - until not element - return x, y -end - UI.Page = class(UI.Window) UI.Page.defaults = { UIElement = 'Page', accelerators = { down = 'focus_next', + scroll_down = 'focus_next', enter = 'focus_next', tab = 'focus_next', ['shift-tab' ] = 'focus_prev', up = 'focus_prev', + scroll_up = 'focus_prev', }, - backgroundColor = colors.cyan, - textColor = colors.white, + backgroundColor = 'primary', + textColor = 'white', } function UI.Page:postInit() - self.parent = self.parent or UI.defaultDevice + self.parent = self.parent or UI.term self.__target = self - self.canvas = Canvas({ - x = 1, y = 1, width = self.parent.width, height = self.parent.height, - isColor = self.parent.isColor, - }) - self.canvas:clear(self.backgroundColor, self.textColor) -end - -function UI.Page:enable() - self.canvas.visible = true - UI.Window.enable(self) - - if not self.focused or not self.focused.enabled then - self:focusFirst() - end -end - -function UI.Page:disable() - self.canvas.visible = false - UI.Window.disable(self) end function UI.Page:sync() if self.enabled then + self:checkFocus() + self.parent:setCursorBlink(self.focused and self.focused.cursorBlink) self.parent:sync() end end @@ -73,22 +44,23 @@ function UI.Page:pointToChild(x, y) if self.__target == self then return UI.Window.pointToChild(self, x, y) end - x = x + self.offx - self.x + 1 - y = y + self.offy - self.y + 1 ---[[ - -- this is supposed to fix when there are multiple sub canvases - local absX, absY = getPosition(self.__target) - if self.__target.canvas then - x = x - (self.__target.canvas.x - self.__target.x) - y = y - (self.__target.canvas.y - self.__target.y) - _syslog({'raw', self.__target.canvas.y, self.__target.y}) + + local function getPosition(element) + local x, y = 1, 1 + repeat + x = element.x + x - 1 + y = element.y + y - 1 + element = element.parent + until not element + return x, y end - ]] - return self.__target:pointToChild(x, y) + + local absX, absY = getPosition(self.__target) + return self.__target:pointToChild(x - absX + self.__target.x, y - absY + self.__target.y) end function UI.Page:getFocusables() - if self.__target == self or self.__target.pageType ~= 'modal' then + if self.__target == self or not self.__target.modal then return UI.Window.getFocusables(self) end return self.__target:getFocusables() @@ -149,12 +121,17 @@ function UI.Page:setFocus(child) if not child.focused then child.focused = true child:emit({ type = 'focus_change', focused = child }) - --self:emit({ type = 'focus_change', focused = child }) end child:focus() end +function UI.Page:checkFocus() + if not self.focused or not self.focused.enabled then + self.__target:focusFirst() + end +end + function UI.Page:eventHandler(event) if self.focused then if event.type == 'focus_next' then diff --git a/sys/modules/opus/ui/components/ProgressBar.lua b/sys/modules/opus/ui/components/ProgressBar.lua index a066952..c769449 100644 --- a/sys/modules/opus/ui/components/ProgressBar.lua +++ b/sys/modules/opus/ui/components/ProgressBar.lua @@ -1,39 +1,31 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.ProgressBar = class(UI.Window) UI.ProgressBar.defaults = { UIElement = 'ProgressBar', - backgroundColor = colors.gray, + backgroundColor = 'gray', height = 1, - progressColor = colors.lime, + progressColor = 'lime', progressChar = UI.extChars and '\153' or ' ', fillChar = ' ', - fillColor = colors.gray, - textColor = colors.green, + fillColor = 'gray', + textColor = 'green', value = 0, } function UI.ProgressBar:draw() local width = math.ceil(self.value / 100 * self.width) - local filler = string.rep(self.fillChar, self.width) - local progress = string.rep(self.progressChar, width) - - for i = 1, self.height do - self:write(1, i, filler, nil, self.fillColor) - self:write(1, i, progress, self.progressColor) - end + self:fillArea(width + 1, 1, self.width - width, self.height, self.fillChar, nil, self.fillColor) + self:fillArea(1, 1, width, self.height, self.progressChar, self.progressColor) end function UI.ProgressBar.example() - local Event = require('opus.event') return UI.ProgressBar { - x = 2, ex = -2, y = 2, + x = 2, ex = -2, y = 2, height = 2, focus = function() end, enable = function(self) - Event.onInterval(.25, function() + require('opus.event').onInterval(.25, function() self.value = self.value == 100 and 0 or self.value + 5 self:draw() self:sync() diff --git a/sys/modules/opus/ui/components/Question.lua b/sys/modules/opus/ui/components/Question.lua new file mode 100644 index 0000000..3be0d52 --- /dev/null +++ b/sys/modules/opus/ui/components/Question.lua @@ -0,0 +1,27 @@ +local class = require('opus.class') +local UI = require('opus.ui') + +UI.Question = class(UI.MiniSlideOut) +UI.Question.defaults = { + UIElement = 'Question', + accelerators = { + y = 'question_yes', + n = 'question_no', + } +} +function UI.Question:postInit() + local x = self.label and #self.label + 3 or 1 + + self.yes_button = UI.Button { + x = x, + text = 'Yes', + backgroundColor = 'primary', + event = 'question_yes', + } + self.no_button = UI.Button { + x = x + 5, + text = 'No', + backgroundColor = 'primary', + event = 'question_no', + } +end diff --git a/sys/modules/opus/ui/components/ScrollBar.lua b/sys/modules/opus/ui/components/ScrollBar.lua index d4bef52..8f8b6ab 100644 --- a/sys/modules/opus/ui/components/ScrollBar.lua +++ b/sys/modules/opus/ui/components/ScrollBar.lua @@ -17,7 +17,13 @@ UI.ScrollBar.defaults = { ey = -1, } function UI.ScrollBar:draw() - local view = self.parent:getViewArea() + local parent = self.target or self.parent --self:find(self.target) + local view = parent:getViewArea() + + self:clear() + + -- ... + self:write(1, 1, ' ', view.fill) if view.totalHeight > view.height then local maxScroll = view.totalHeight - view.height @@ -27,7 +33,7 @@ function UI.ScrollBar:draw() local row = view.y if not view.static then -- does the container scroll ? - self.height = view.totalHeight + self:reposition(self.x, self.y, self.width, view.totalHeight) end for i = 1, view.height - 2 do @@ -56,16 +62,17 @@ end function UI.ScrollBar:eventHandler(event) if event.type == 'mouse_click' or event.type == 'mouse_doubleclick' then if event.x == 1 then - local view = self.parent:getViewArea() + local parent = self.target or self.parent --self:find(self.target) + local view = parent:getViewArea() if view.totalHeight > view.height then if event.y == view.y then - self:emit({ type = 'scroll_up'}) + parent:emit({ type = 'scroll_up'}) elseif event.y == view.y + view.height - 1 then - self:emit({ type = 'scroll_down'}) + parent:emit({ type = 'scroll_down'}) else local percent = (event.y - view.y) / (view.height - 2) local y = math.floor((view.totalHeight - view.height) * percent) - self:emit({ type = 'scroll_to', offset = y }) + parent :emit({ type = 'scroll_to', offset = y }) end end return true diff --git a/sys/modules/opus/ui/components/ScrollingGrid.lua b/sys/modules/opus/ui/components/ScrollingGrid.lua index 06dd5b8..864d7ad 100644 --- a/sys/modules/opus/ui/components/ScrollingGrid.lua +++ b/sys/modules/opus/ui/components/ScrollingGrid.lua @@ -29,6 +29,7 @@ function UI.ScrollingGrid:getViewArea() height = self.pageSize, -- viewable height totalHeight = Util.size(self.values), -- total height offsetY = self.scrollOffset, -- scroll offset + fill = not self.disableHeader and self.headerBackgroundColor, } end @@ -57,3 +58,21 @@ function UI.ScrollingGrid:setIndex(index) end UI.Grid.setIndex(self, index) end + +function UI.ScrollingGrid.example() + local values = { } + for i = 1, 20 do + table.insert(values, { key = 'key' .. i, value = 'value' .. i }) + end + return UI.ScrollingGrid { + values = values, + sortColumn = 'key', + columns = { + { heading = 'key', key = 'key' }, + { heading = 'value', key = 'value' }, + }, + accelerators = { + grid_select = 'custom_select', + } + } +end \ No newline at end of file diff --git a/sys/modules/opus/ui/components/SlideOut.lua b/sys/modules/opus/ui/components/SlideOut.lua index 479be4b..efafd68 100644 --- a/sys/modules/opus/ui/components/SlideOut.lua +++ b/sys/modules/opus/ui/components/SlideOut.lua @@ -4,17 +4,9 @@ local UI = require('opus.ui') UI.SlideOut = class(UI.Window) UI.SlideOut.defaults = { UIElement = 'SlideOut', - pageType = 'modal', + transitionHint = 'expandUp', + modal = true, } -function UI.SlideOut:layout() - UI.Window.layout(self) - if not self.canvas then - self.canvas = self:addLayer() - else - self.canvas:resize(self.width, self.height) - end -end - function UI.SlideOut:enable() end @@ -27,24 +19,20 @@ function UI.SlideOut:toggle() end function UI.SlideOut:show(...) - self:addTransition('expandUp') - self.canvas:raise() - self.canvas:setVisible(true) UI.Window.enable(self, ...) self:draw() - self:capture(self) self:focusFirst() end -function UI.SlideOut:disable() - self.canvas:setVisible(false) - UI.Window.disable(self) -end - function UI.SlideOut:hide() self:disable() - self:release(self) - self:refocus() +end + +function UI.SlideOut:draw() + if not self.noFill then + self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), 'black', 'gray') + end + self:drawChildren() end function UI.SlideOut:eventHandler(event) @@ -59,24 +47,27 @@ function UI.SlideOut:eventHandler(event) end function UI.SlideOut.example() - -- for the transistion to work properly, the parent must have a canvas - return UI.ActiveLayer { - y = 2, + return UI.Window { + y = 3, + backgroundColor = 2048, button = UI.Button { x = 2, y = 5, text = 'show', }, slideOut = UI.SlideOut { - backgroundColor = _G.colors.yellow, - y = -4, height = 4, x = 3, ex = -3, + backgroundColor = 16, + y = -7, height = 4, x = 3, ex = -3, + titleBar = UI.TitleBar { + title = 'test', + }, button = UI.Button { x = 2, y = 2, text = 'hide', + --visualize = true, }, }, eventHandler = function (self, event) if event.type == 'button_press' then - self.slideOut.canvas.xxx = true self.slideOut:toggle() end end, diff --git a/sys/modules/opus/ui/components/Slider.lua b/sys/modules/opus/ui/components/Slider.lua index 9a58cdb..620877e 100644 --- a/sys/modules/opus/ui/components/Slider.lua +++ b/sys/modules/opus/ui/components/Slider.lua @@ -2,17 +2,15 @@ local class = require('opus.class') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors - UI.Slider = class(UI.Window) UI.Slider.defaults = { UIElement = 'Slider', height = 1, barChar = UI.extChars and '\140' or '-', - barColor = colors.gray, + barColor = 'gray', sliderChar = UI.extChars and '\143' or '\124', - sliderColor = colors.blue, - sliderFocusColor = colors.lightBlue, + sliderColor = 'blue', + sliderFocusColor = 'lightBlue', leftBorder = UI.extChars and '\141' or '\124', rightBorder = UI.extChars and '\142' or '\124', value = 0, @@ -57,8 +55,16 @@ end function UI.Slider:eventHandler(event) if event.type == "mouse_down" or event.type == "mouse_drag" then + + local pos = event.x - 1 + if event.type == 'mouse_down' then + self.anchor = event.x - 1 + else + pos = self.anchor + event.dx + end + local range = self.max - self.min - local i = (event.x - 1) / (self.width - 1) + local i = pos / (self.width - 1) self.value = self.min + (i * range) self:emit({ type = self.event, value = self.value, element = self }) self:draw() diff --git a/sys/modules/opus/ui/components/StatusBar.lua b/sys/modules/opus/ui/components/StatusBar.lua index 96a1943..1b2d2e0 100644 --- a/sys/modules/opus/ui/components/StatusBar.lua +++ b/sys/modules/opus/ui/components/StatusBar.lua @@ -3,17 +3,16 @@ local Event = require('opus.event') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors - UI.StatusBar = class(UI.Window) UI.StatusBar.defaults = { UIElement = 'StatusBar', - backgroundColor = colors.lightGray, - textColor = colors.gray, + backgroundColor = 'lightGray', + textColor = 'gray', height = 1, ey = -1, } -function UI.StatusBar:adjustWidth() +function UI.StatusBar:layout() + UI.Window.layout(self) -- Can only have 1 adjustable width if self.columns then local w = self.width - #self.columns - 1 @@ -31,16 +30,6 @@ function UI.StatusBar:adjustWidth() end end -function UI.StatusBar:resize() - UI.Window.resize(self) - self:adjustWidth() -end - -function UI.StatusBar:setParent() - UI.Window.setParent(self) - self:adjustWidth() -end - function UI.StatusBar:setStatus(status) if self.values ~= status then self.values = status @@ -63,7 +52,7 @@ end function UI.StatusBar:timedStatus(status, timeout) self:write(2, 1, Util.widthify(status, self.width-2), self.backgroundColor) - Event.on(timeout or 3, function() + Event.onTimeout(timeout or 3, function() if self.enabled then self:draw() self:sync() @@ -89,11 +78,13 @@ function UI.StatusBar:draw() elseif type(self.values) == 'string' then self:write(1, 1, Util.widthify(' ' .. self.values, self.width)) else - local s = '' + local x = 2 + self:clear() for _,c in ipairs(self.columns) do - s = s .. ' ' .. Util.widthify(tostring(self.values[c.key] or ''), c.cw) + local s = Util.widthify(tostring(self.values[c.key] or ''), c.cw) + self:write(x, 1, s, c.bg, c.fg) + x = x + c.cw + 1 end - self:write(1, 1, Util.widthify(s, self.width)) end end diff --git a/sys/modules/opus/ui/components/Tab.lua b/sys/modules/opus/ui/components/Tab.lua index 1e31de6..d2e591b 100644 --- a/sys/modules/opus/ui/components/Tab.lua +++ b/sys/modules/opus/ui/components/Tab.lua @@ -1,9 +1,16 @@ local class = require('opus.class') local UI = require('opus.ui') -UI.Tab = class(UI.ActiveLayer) +UI.Tab = class(UI.Window) UI.Tab.defaults = { UIElement = 'Tab', tabTitle = 'tab', y = 2, } + +function UI.Tab:draw() + if not self.noFill then + self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray) + end + self:drawChildren() +end diff --git a/sys/modules/opus/ui/components/TabBar.lua b/sys/modules/opus/ui/components/TabBar.lua index e684cae..eb81b3a 100644 --- a/sys/modules/opus/ui/components/TabBar.lua +++ b/sys/modules/opus/ui/components/TabBar.lua @@ -6,9 +6,9 @@ UI.TabBar = class(UI.MenuBar) UI.TabBar.defaults = { UIElement = 'TabBar', buttonClass = 'TabBarMenuItem', -} -UI.TabBar.inherits = { - selectedBackgroundColor = 'backgroundColor', + backgroundColor = 'black', + selectedBackgroundColor = 'primary', + unselectedBackgroundColor = 'tertiary', } function UI.TabBar:enable() UI.MenuBar.enable(self) @@ -32,7 +32,7 @@ function UI.TabBar:eventHandler(event) self:emit({ type = 'tab_change', current = si, last = pi, tab = selected }) end end - UI.MenuBar.draw(self) + self:draw(self) end return UI.MenuBar.eventHandler(self, event) end diff --git a/sys/modules/opus/ui/components/TabBarMenuItem.lua b/sys/modules/opus/ui/components/TabBarMenuItem.lua index f9f549b..0ade0b0 100644 --- a/sys/modules/opus/ui/components/TabBarMenuItem.lua +++ b/sys/modules/opus/ui/components/TabBarMenuItem.lua @@ -1,27 +1,19 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.TabBarMenuItem = class(UI.Button) UI.TabBarMenuItem.defaults = { UIElement = 'TabBarMenuItem', event = 'tab_select', - textColor = colors.black, - selectedBackgroundColor = colors.cyan, - unselectedBackgroundColor = colors.lightGray, - backgroundColor = colors.lightGray, -} -UI.TabBarMenuItem.inherits = { - selectedBackgroundColor = 'selectedBackgroundColor', + textInactiveColor = 'lightGray', } function UI.TabBarMenuItem:draw() if self.selected then - self.backgroundColor = self.selectedBackgroundColor - self.backgroundFocusColor = self.selectedBackgroundColor + self.backgroundColor = self:getProperty('selectedBackgroundColor') + self.backgroundFocusColor = self.backgroundColor else - self.backgroundColor = self.unselectedBackgroundColor - self.backgroundFocusColor = self.unselectedBackgroundColor + self.backgroundColor = self:getProperty('unselectedBackgroundColor') + self.backgroundFocusColor = self.backgroundColor end UI.Button.draw(self) end diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index 2b0c824..8c2a692 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -56,12 +56,12 @@ end function UI.Tabs:enable() self.enabled = true - self.transitionHint = nil self.tabBar:enable() local menuItem = Util.find(self.tabBar.children, 'selected', true) - for _,child in pairs(self.children or { }) do + for child in self:eachChild() do + child.transitionHint = nil if child.uid == menuItem.tabUid then child:enable() self:emit({ type = 'tab_activate', activated = child }) @@ -74,14 +74,11 @@ end function UI.Tabs:eventHandler(event) if event.type == 'tab_change' then local tab = self:find(event.tab.tabUid) - if event.current > event.last then - self.transitionHint = 'slideLeft' - else - self.transitionHint = 'slideRight' - end + local hint = event.current > event.last and 'slideLeft' or 'slideRight' - for _,child in pairs(self.children) do + for child in self:eachChild() do if child.uid == event.tab.tabUid then + child.transitionHint = hint child:enable() elseif child.tabTitle then child:disable() @@ -89,6 +86,7 @@ function UI.Tabs:eventHandler(event) end self:emit({ type = 'tab_activate', activated = tab }) tab:draw() + return true end end @@ -102,11 +100,26 @@ function UI.Tabs.example() tab2 = UI.Tab { index = 2, tabTitle = 'tab2', - button = UI.Button { y = 3 }, + subtabs = UI.Tabs { + x = 3, y = 2, ex = -3, ey = -2, + tab1 = UI.Tab { + index = 1, + tabTitle = 'tab4', + entry = UI.TextEntry { y = 3, shadowText = 'text' }, + }, + tab3 = UI.Tab { + index = 2, + tabTitle = 'tab5', + }, + }, }, tab3 = UI.Tab { index = 3, tabTitle = 'tab3', }, + enable = function(self) + UI.Tabs.enable(self) + self:setActive(self.tab3, false) + end, } end diff --git a/sys/modules/opus/ui/components/Text.lua b/sys/modules/opus/ui/components/Text.lua index 0250a48..5bf3799 100644 --- a/sys/modules/opus/ui/components/Text.lua +++ b/sys/modules/opus/ui/components/Text.lua @@ -8,11 +8,11 @@ UI.Text.defaults = { value = '', height = 1, } -function UI.Text:setParent() +function UI.Text:layout() if not self.width and not self.ex then self.width = #tostring(self.value) end - UI.Window.setParent(self) + UI.Window.layout(self) end function UI.Text:draw() diff --git a/sys/modules/opus/ui/components/TextArea.lua b/sys/modules/opus/ui/components/TextArea.lua index 2b8c342..3b821c7 100644 --- a/sys/modules/opus/ui/components/TextArea.lua +++ b/sys/modules/opus/ui/components/TextArea.lua @@ -6,36 +6,44 @@ UI.TextArea.defaults = { UIElement = 'TextArea', marginRight = 2, value = '', + showScrollBar = true, } -function UI.TextArea:postInit() - self.scrollBar = UI.ScrollBar() -end - function UI.TextArea:setText(text) self:reset() self.value = text self:draw() end -function UI.TextArea:focus() +function UI.TextArea.focus() -- allow keyboard scrolling end function UI.TextArea:draw() self:clear() --- self:setCursorPos(1, 1) - self.cursorX, self.cursorY = 1, 1 self:print(self.value) - - for _,child in pairs(self.children) do - if child.enabled then - child:draw() - end - end + self:drawChildren() end function UI.TextArea.example() - return UI.TextArea { - value = 'sample text\nabc' + local Ansi = require('opus.ansi') + return UI.Window { + backgroundColor = 2048, + t1 = UI.TextArea { + ey = 3, + value = 'sample text\nabc' + }, + t2 = UI.TextArea { + y = 5, + backgroundColor = 'green', + value = string.format([[now %%is the %stime %sfor%s all good men to come to the aid of their country. +1 +2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 +3 +4 +5 +6 +7 +8]], Ansi.yellow, Ansi.onred, Ansi.reset), + } } end \ No newline at end of file diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 929cb3c..9fd61a5 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -3,7 +3,6 @@ local entry = require('opus.entry') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors local _rep = string.rep local function transform(directive) @@ -19,15 +18,16 @@ UI.TextEntry = class(UI.Window) UI.TextEntry.docs = { } UI.TextEntry.defaults = { UIElement = 'TextEntry', - --value = '', shadowText = '', focused = false, - textColor = colors.white, - shadowTextColor = colors.gray, - backgroundColor = colors.black, -- colors.lightGray, - backgroundFocusColor = colors.black, --lightGray, + textColor = 'white', + shadowTextColor = 'gray', + markBackgroundColor = 'gray', + backgroundColor = 'black', + backgroundFocusColor = 'black', height = 1, limit = 6, + cursorBlink = true, accelerators = { [ 'control-c' ] = 'copy', } @@ -74,7 +74,9 @@ function UI.TextEntry:draw() text = self.shadowText end - self:write(1, 1, ' ' .. Util.widthify(text, self.width - 2) .. ' ', bg, tc) + local ss = self.entry.scroll > 0 and '\183' or ' ' + self:write(2, 1, Util.widthify(text, self.width - 2) .. ' ', bg, tc) + self:write(1, 1, ss, bg, self.shadowTextColor) if self.entry.mark.active then local tx = math.max(self.entry.mark.x - self.entry.scroll, 0) @@ -85,7 +87,7 @@ function UI.TextEntry:draw() end if tx ~= tex then - self:write(tx + 2, 1, text:sub(tx + 1, tex), colors.gray, tc) + self:write(tx + 2, 1, text:sub(tx + 1, tex), self.markBackgroundColor, tc) end end if self.focused then @@ -106,13 +108,12 @@ function UI.TextEntry:updateCursor() self:setCursorPos(self.entry.pos - self.entry.scroll + 2, 1) end +function UI.TextEntry:markAll() + self.entry:markAll() +end + function UI.TextEntry:focus() self:draw() - if self.focused then - self:setCursorBlink(true) - else - self:setCursorBlink(false) - end end function UI.TextEntry:eventHandler(event) diff --git a/sys/modules/opus/ui/components/Throttle.lua b/sys/modules/opus/ui/components/Throttle.lua index 0466486..d83662e 100644 --- a/sys/modules/opus/ui/components/Throttle.lua +++ b/sys/modules/opus/ui/components/Throttle.lua @@ -20,24 +20,15 @@ UI.Throttle.defaults = { ' //) (O ). @ \\-d ) (@ ' } } -function UI.Throttle:setParent() +function UI.Throttle:layout() self.x = math.ceil((self.parent.width - self.width) / 2) self.y = math.ceil((self.parent.height - self.height) / 2) - UI.Window.setParent(self) + self:reposition(self.x, self.y, self.width, self.height) end function UI.Throttle:enable() self.c = os.clock() - self.enabled = false -end - -function UI.Throttle:disable() - if self.canvas then - self.enabled = false - self.canvas:removeLayer() - self.canvas = nil - self.ctr = 0 - end + self.ctr = 0 end function UI.Throttle:update() @@ -46,11 +37,7 @@ function UI.Throttle:update() os.sleep(0) self.c = os.clock() self.enabled = true - if not self.canvas then - self.canvas = self:addLayer(self.backgroundColor, self.borderColor) - self.canvas:setVisible(true) - self:clear(self.borderColor) - end + self:clear(self.borderColor) local image = self.image[self.ctr + 1] local width = self.width - 2 for i = 0, #self.image do @@ -63,3 +50,25 @@ function UI.Throttle:update() self:sync() end end + +function UI.Throttle.example() + return UI.Window { + button1 = UI.Button { + x = 2, y = 2, + text = 'Test', + }, + throttle = UI.Throttle { + textColor = colors.yellow, + borderColor = colors.green, + }, + eventHandler = function (self, event) + if event.type == 'button_press' then + for _ = 1, 40 do + self.throttle:update() + os.sleep(.05) + end + self.throttle:disable() + end + end, + } +end diff --git a/sys/modules/opus/ui/components/TitleBar.lua b/sys/modules/opus/ui/components/TitleBar.lua index ce7fdd4..743103a 100644 --- a/sys/modules/opus/ui/components/TitleBar.lua +++ b/sys/modules/opus/ui/components/TitleBar.lua @@ -1,59 +1,20 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors -local _rep = string.rep -local _sub = string.sub - --- For manipulating text in a fixed width string -local SB = class() -function SB:init(width) - self.width = width - self.buf = _rep(' ', width) -end -function SB:insert(x, str, width) - if x < 1 then - x = self.width + x + 1 - end - width = width or #str - if x + width - 1 > self.width then - width = self.width - x - end - if width > 0 then - self.buf = _sub(self.buf, 1, x - 1) .. _sub(str, 1, width) .. _sub(self.buf, x + width) - end -end -function SB:fill(x, ch, width) - width = width or self.width - x + 1 - self:insert(x, _rep(ch, width)) -end -function SB:center(str) - self:insert(math.max(1, math.ceil((self.width - #str + 1) / 2)), str) -end -function SB:get() - return self.buf -end - UI.TitleBar = class(UI.Window) UI.TitleBar.defaults = { UIElement = 'TitleBar', height = 1, - textColor = colors.white, - backgroundColor = colors.cyan, title = '', frameChar = UI.extChars and '\140' or '-', closeInd = UI.extChars and '\215' or '*', } function UI.TitleBar:draw() - local sb = SB(self.width) - sb:fill(2, self.frameChar, sb.width - 3) - sb:center(string.format(' %s ', self.title)) + self:fillArea(2, 1, self.width - 2, 1, self.frameChar) + self:centeredWrite(1, string.format(' %s ', self.title)) if self.previousPage or self.event then - sb:insert(-1, self.closeInd) - else - sb:insert(-2, self.frameChar) + self:write(self.width - 1, 1, ' ' .. self.closeInd) end - self:write(1, 1, sb:get()) end function UI.TitleBar:eventHandler(event) @@ -69,5 +30,74 @@ function UI.TitleBar:eventHandler(event) end return true end + + elseif event.type == 'mouse_down' then + self.anchor = { x = event.x, y = event.y, ox = self.parent.x, oy = self.parent.y, h = self.parent.height } + + elseif event.type == 'mouse_drag' then + if self.expand == 'height' then + local d = event.dy + if self.anchor.h - d > 0 and self.anchor.oy + d > 0 then + self.parent:reposition(self.parent.x, self.anchor.oy + event.dy, self.width, self.anchor.h - d) + end + + elseif self.moveable then + local d = event.dy + if self.anchor.oy + d > 0 and self.anchor.oy + d <= self.parent.parent.height then + self.parent:move(self.anchor.ox + event.dx, self.anchor.oy + event.dy) + end + end end end + +function UI.TitleBar.example() + return UI.Window { + win1 = UI.Window { + x = 9, y = 2, ex = -7, ey = -3, + backgroundColor = 'green', + titleBar = UI.TitleBar { + title = 'A really, really, really long title', moveable = true, + }, + button1 = UI.Button { + x = 2, y = 3, + text = 'Press', + }, + focus = function (self) + self:raise() + end, + }, + win2 = UI.Window { + x = 7, y = 3, ex = -9, ey = -2, + backgroundColor = 'orange', + titleBar = UI.TitleBar { + title = 'test', moveable = true, + event = 'none', + }, + button1 = UI.Button { + x = 2, y = 3, + text = 'Press', + }, + focus = function (self) + self:raise() + end, + }, + draw = function(self, isBG) + for i = 1, self.height do + self:write(1, i, self.filler or '') + end + if not isBG then + for _,v in pairs(self.children) do + v:draw() + end + end + end, + enable = function (self) + require('opus.event').onInterval(.5, function() + self.filler = string.rep(string.char(math.random(33, 126)), self.width) + self:draw(true) + self:sync() + end) + UI.Window.enable(self) + end + } +end diff --git a/sys/modules/opus/ui/components/VerticalMeter.lua b/sys/modules/opus/ui/components/VerticalMeter.lua index 051d740..8129990 100644 --- a/sys/modules/opus/ui/components/VerticalMeter.lua +++ b/sys/modules/opus/ui/components/VerticalMeter.lua @@ -1,13 +1,11 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.VerticalMeter = class(UI.Window) UI.VerticalMeter.defaults = { UIElement = 'VerticalMeter', - backgroundColor = colors.gray, - meterColor = colors.lime, + backgroundColor = 'gray', + meterColor = 'lime', width = 1, value = 0, } @@ -18,12 +16,11 @@ function UI.VerticalMeter:draw() end function UI.VerticalMeter.example() - local Event = require('opus.event') return UI.VerticalMeter { x = 2, width = 3, y = 2, ey = -2, focus = function() end, enable = function(self) - Event.onInterval(.25, function() + require('opus.event').onInterval(.25, function() self.value = self.value == 100 and 0 or self.value + 5 self:draw() self:sync() @@ -31,4 +28,4 @@ function UI.VerticalMeter.example() return UI.VerticalMeter.enable(self) end } -end \ No newline at end of file +end diff --git a/sys/modules/opus/ui/components/Viewport.lua b/sys/modules/opus/ui/components/Viewport.lua index 35cd0eb..9389db5 100644 --- a/sys/modules/opus/ui/components/Viewport.lua +++ b/sys/modules/opus/ui/components/Viewport.lua @@ -1,16 +1,15 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - UI.Viewport = class(UI.Window) UI.Viewport.defaults = { UIElement = 'Viewport', - backgroundColor = colors.cyan, accelerators = { down = 'scroll_down', up = 'scroll_up', home = 'scroll_top', + left = 'scroll_left', + right = 'scroll_right', [ 'end' ] = 'scroll_bottom', pageUp = 'scroll_pageUp', [ 'control-b' ] = 'scroll_pageUp', @@ -18,53 +17,60 @@ UI.Viewport.defaults = { [ 'control-f' ] = 'scroll_pageDown', }, } -function UI.Viewport:layout() - UI.Window.layout(self) - if not self.canvas then - self.canvas = self:addLayer() - else - self.canvas:resize(self.width, self.height) +function UI.Viewport:postInit() + if self.showScrollBar then + self.scrollBar = UI.ScrollBar() end end -function UI.Viewport:enable() - UI.Window.enable(self) - self.canvas:setVisible(true) -end - -function UI.Viewport:disable() - UI.Window.disable(self) - self.canvas:setVisible(false) -end - -function UI.Viewport:setScrollPosition(offset) - local oldOffset = self.offy - self.offy = math.max(offset, 0) - self.offy = math.min(self.offy, math.max(#self.canvas.lines, self.height) - self.height) - if self.offy ~= oldOffset then +function UI.Viewport:setScrollPosition(offy, offx) -- argh - reverse + local oldOffy = self.offy + self.offy = math.max(offy, 0) + self.offy = math.min(self.offy, math.max(#self.lines, self.height) - self.height) + if self.offy ~= oldOffy then if self.scrollBar then self.scrollBar:draw() end - self.canvas.offy = offset - self.canvas:dirty() + self.offy = offy + self:dirty(true) + end + + local oldOffx = self.offx + self.offx = math.max(offx or 0, 0) + self.offx = math.min(self.offx, math.max(#self.lines[1], self.width) - self.width) + if self.offx ~= oldOffx then + if self.scrollBar then + --self.scrollBar:draw() + end + self.offx = offx or 0 + self:dirty(true) end end -function UI.Viewport:write(x, y, text, bg, tc) - if y > #self.canvas.lines then - for i = #self.canvas.lines, y do - self.canvas.lines[i + 1] = { } - self.canvas:clearLine(i + 1, self.backgroundColor, self.textColor) - end +function UI.Viewport:blit(x, y, text, bg, fg) + if y > #self.lines then + self:resizeBuffer(self.width, y) + end + return UI.Window.blit(self, x, y, text, bg, fg) +end + +function UI.Viewport:write(x, y, text, bg, fg) + if y > #self.lines then + self:resizeBuffer(self.width, y) + end + return UI.Window.write(self, x, y, text, bg, fg) +end + +function UI.Viewport:setViewHeight(h) + if h > #self.lines then + self:resizeBuffer(self.width, h) end - return UI.Window.write(self, x, y, text, bg, tc) end function UI.Viewport:reset() self.offy = 0 - self.canvas.offy = 0 - for i = self.height + 1, #self.canvas.lines do - self.canvas.lines[i] = nil + for i = self.height + 1, #self.lines do + self.lines[i] = nil end end @@ -72,26 +78,33 @@ function UI.Viewport:getViewArea() return { y = (self.offy or 0) + 1, height = self.height, - totalHeight = #self.canvas.lines, + totalHeight = #self.lines, offsetY = self.offy or 0, } end function UI.Viewport:eventHandler(event) + if #self.lines <= self.height then + return + end if event.type == 'scroll_down' then - self:setScrollPosition(self.offy + 1) + self:setScrollPosition(self.offy + 1, self.offx) elseif event.type == 'scroll_up' then - self:setScrollPosition(self.offy - 1) + self:setScrollPosition(self.offy - 1, self.offx) + elseif event.type == 'scroll_left' then + self:setScrollPosition(self.offy, self.offx - 1) + elseif event.type == 'scroll_right' then + self:setScrollPosition(self.offy, self.offx + 1) elseif event.type == 'scroll_top' then - self:setScrollPosition(0) + self:setScrollPosition(0, 0) elseif event.type == 'scroll_bottom' then - self:setScrollPosition(10000000) + self:setScrollPosition(10000000, 0) elseif event.type == 'scroll_pageUp' then - self:setScrollPosition(self.offy - self.height) + self:setScrollPosition(self.offy - self.height, self.offx) elseif event.type == 'scroll_pageDown' then - self:setScrollPosition(self.offy + self.height) + self:setScrollPosition(self.offy + self.height, self.offx) elseif event.type == 'scroll_to' then - self:setScrollPosition(event.offset) + self:setScrollPosition(event.offset, 0) else return false end diff --git a/sys/modules/opus/ui/components/Wizard.lua b/sys/modules/opus/ui/components/Wizard.lua index 549669e..0fcab17 100644 --- a/sys/modules/opus/ui/components/Wizard.lua +++ b/sys/modules/opus/ui/components/Wizard.lua @@ -25,9 +25,6 @@ function UI.Wizard:postInit() } Util.merge(self, self.pages) - --for _, child in pairs(self.pages) do - -- child.ey = -2 - --end end function UI.Wizard:add(pages) @@ -50,9 +47,8 @@ end function UI.Wizard:enable(...) self.enabled = true self.index = 1 - self.transitionHint = nil local initial = self:getPage(1) - for _,child in pairs(self.children) do + for child in self:eachChild() do if child == initial or not child.index then child:enable(...) else @@ -93,12 +89,13 @@ function UI.Wizard:eventHandler(event) elseif event.type == 'enable_view' then local current = event.next or event.prev if not current then error('property "index" is required on wizard pages') end + local hint if event.current then if event.next then - self.transitionHint = 'slideLeft' + hint = 'slideLeft' elseif event.prev then - self.transitionHint = 'slideRight' + hint = 'slideRight' end event.current:disable() end @@ -117,6 +114,7 @@ function UI.Wizard:eventHandler(event) self.nextButton.event = 'wizard_complete' end -- a new current view + current.transitionHint = hint current:enable() current:emit({ type = 'view_enabled', view = current }) self:draw() diff --git a/sys/modules/opus/ui/components/WizardPage.lua b/sys/modules/opus/ui/components/WizardPage.lua index cb2c2de..da895ef 100644 --- a/sys/modules/opus/ui/components/WizardPage.lua +++ b/sys/modules/opus/ui/components/WizardPage.lua @@ -1,11 +1,8 @@ local class = require('opus.class') local UI = require('opus.ui') -local colors = _G.colors - -UI.WizardPage = class(UI.ActiveLayer) +UI.WizardPage = class(UI.Window) UI.WizardPage.defaults = { UIElement = 'WizardPage', - backgroundColor = colors.cyan, ey = -2, } diff --git a/sys/modules/opus/ui/transition.lua b/sys/modules/opus/ui/transition.lua index 4448760..24d07ee 100644 --- a/sys/modules/opus/ui/transition.lua +++ b/sys/modules/opus/ui/transition.lua @@ -2,50 +2,85 @@ local Tween = require('opus.ui.tween') local Transition = { } -function Transition.slideLeft(args) - local ticks = args.ticks or 10 - local easing = args.easing or 'outQuint' - local pos = { x = args.ex } - local tween = Tween.new(ticks, pos, { x = args.x }, easing) +function Transition.slideLeft(canvas, args) + local ticks = args.ticks or 6 + local easing = args.easing or 'inCirc' + local pos = { x = canvas.ex } + local tween = Tween.new(ticks, pos, { x = canvas.x }, easing) - args.canvas:move(pos.x, args.canvas.y) + canvas:move(pos.x, canvas.y) return function() local finished = tween:update(1) - args.canvas:move(math.floor(pos.x), args.canvas.y) - args.canvas:dirty() + canvas:move(math.floor(pos.x), canvas.y) + canvas:dirty(true) return not finished end end -function Transition.slideRight(args) - local ticks = args.ticks or 10 - local easing = args.easing or'outQuint' - local pos = { x = -args.canvas.width } +function Transition.slideRight(canvas, args) + local ticks = args.ticks or 6 + local easing = args.easing or 'inCirc' + local pos = { x = -canvas.width } local tween = Tween.new(ticks, pos, { x = 1 }, easing) - args.canvas:move(pos.x, args.canvas.y) + canvas:move(pos.x, canvas.y) return function() local finished = tween:update(1) - args.canvas:move(math.floor(pos.x), args.canvas.y) - args.canvas:dirty() + canvas:move(math.floor(pos.x), canvas.y) + canvas:dirty(true) return not finished end end -function Transition.expandUp(args) +function Transition.expandUp(canvas, args) local ticks = args.ticks or 3 local easing = args.easing or 'linear' - local pos = { y = args.ey + 1 } - local tween = Tween.new(ticks, pos, { y = args.y }, easing) + local pos = { y = canvas.ey + 1 } + local tween = Tween.new(ticks, pos, { y = canvas.y }, easing) - args.canvas:move(args.x, pos.y) + canvas:move(canvas.x, pos.y) return function() local finished = tween:update(1) - args.canvas:move(args.x, math.floor(pos.y)) - args.canvas:dirty() + canvas:move(canvas.x, math.floor(pos.y)) + canvas.parent:dirty(true) + return not finished + end +end + +function Transition.shake(canvas, args) + local ticks = args.ticks or 8 + local i = ticks + + return function() + i = -i + canvas:move(canvas.x + i, canvas.y) + if i > 0 then + i = i - 2 + end + return i ~= 0 + end +end + +function Transition.shuffle(canvas, args) + local ticks = args.ticks or 4 + local easing = args.easing or 'linear' + local t = { } + + for _,child in pairs(canvas.children) do + t[child] = Tween.new(ticks, child, { x = child.x, y = child.y }, easing) + child.x = math.random(1, canvas.parent.width) + child.y = math.random(1, canvas.parent.height) + end + + return function() + local finished + for child, tween in pairs(t) do + finished = tween:update(1) + child:move(math.floor(child.x), math.floor(child.y)) + end return not finished end end diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 383420e..8cd7286 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -13,6 +13,7 @@ local _unpack = table.unpack local _bor = bit32.bor local _bxor = bit32.bxor +local byteArrayMT byteArrayMT = { __tostring = function(a) return string.char(_unpack(a)) end, __index = { @@ -668,42 +669,31 @@ function Util.trimr(s) end -- end http://snippets.luacode.org/?p=snippets/trim_whitespace_from_string_76 --- word wrapping based on: --- https://www.rosettacode.org/wiki/Word_wrap#Lua and --- http://lua-users.org/wiki/StringRecipes -local function paragraphwrap(text, linewidth, res) - linewidth = linewidth or 75 - local spaceleft = linewidth - local line = { } - - for word in text:gmatch("%S+") do - local len = #word + 1 - - --if colorMode then - -- word:gsub('()@([@%d])', function(pos, c) len = len - 2 end) - --end - - if len > spaceleft then - table.insert(res, table.concat(line, ' ')) - line = { word } - spaceleft = linewidth - len - 1 +local function wrap(text, max, lines) + local index = 1 + repeat + if #text <= max then + table.insert(lines, text) + text = '' + elseif text:sub(max+1, max+1) == ' ' then + table.insert(lines, text:sub(index, max)) + text = text:sub(max + 2) else - table.insert(line, word) - spaceleft = spaceleft - len + local x = text:sub(1, max) + local s = x:match('(.*) ') or x + text = text:sub(#s + 1) + table.insert(lines, s) end - end - - table.insert(res, table.concat(line, ' ')) - return table.concat(res, '\n') + text = text:match('^%s*(.*)') + until not text or #text == 0 + return lines end --- end word wrapping function Util.wordWrap(str, limit) - local longLines = Util.split(str) local lines = { } - for _,line in ipairs(longLines) do - paragraphwrap(line, limit, lines) + for _,line in ipairs(Util.split(str)) do + wrap(line, limit, lines) end return lines -- 2.49.1 From b0d2ce0199741a647349a48c57e3bc0079e43083 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 22 Apr 2020 23:37:12 -0600 Subject: [PATCH 118/236] minor bugfixes - cleanup --- sys/apps/Lua.lua | 17 ++--------------- sys/apps/system/diskusage.lua | 20 +++++++++++++------- sys/modules/opus/ui/canvas.lua | 3 ++- sys/modules/opus/ui/components/Checkbox.lua | 3 ++- sys/modules/opus/ui/components/TitleBar.lua | 4 +--- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 44f2c78..d4d7d50 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -138,19 +138,6 @@ function page:eventHandler(event) self:executeStatement('_ENV') command = nil - elseif event.type == 'hide_output' then - self.output:disable() - - self.titleBar.oy = -1 - self.titleBar.event = 'show_output' - self.titleBar.closeInd = '^' - self.titleBar:resize() - - self.grid.ey = -2 - self.grid:resize() - - self:draw() - elseif event.type == 'tab_select' then self:setFocus(self.prompt) @@ -201,7 +188,7 @@ function page:eventHandler(event) command = nil self.grid:setValues(t) self.grid:setIndex(1) - self:draw() + self.grid:draw() end return true @@ -247,7 +234,7 @@ function page:setResult(result) end self.grid:setValues(t) self.grid:setIndex(1) - self:draw() + self.grid:draw() end function page.grid:eventHandler(event) diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index c0db595..9a07e17 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -136,6 +136,19 @@ function tab:enable() self:updateDrives() self:updateInfo() UI.Tab.enable(self) + self.handler = Event.on({ 'disk', 'disk_eject' }, function() + os.sleep(1) + if tab.enabled then + tab:updateDrives() + tab:updateInfo() + tab:sync() + end + end) +end + +function tab:disable() + Event.off(self.handler) + UI.Tab.disable(self) end function tab:eventHandler(event) @@ -147,11 +160,4 @@ function tab:eventHandler(event) return true end -Event.on({ 'disk', 'disk_eject' }, function() - os.sleep(1) - tab:updateDrives() - tab:updateInfo() - tab:sync() -end) - return tab diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index 3003981..8795fa7 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -361,7 +361,8 @@ function Canvas:__renderLayers(device, offset, doubleBuffer) y = region[2] - offset.y, ex = region[3] - offset.x, ey = region[4] - offset.y }, - { x = region[1], y = region[2] }, doubleBuffer) + { x = region[1], y = region[2] }, + doubleBuffer) end self.regions = nil diff --git a/sys/modules/opus/ui/components/Checkbox.lua b/sys/modules/opus/ui/components/Checkbox.lua index 0322ad6..50bae5f 100644 --- a/sys/modules/opus/ui/components/Checkbox.lua +++ b/sys/modules/opus/ui/components/Checkbox.lua @@ -12,6 +12,7 @@ UI.Checkbox.defaults = { textColor = 'white', backgroundColor = 'black', backgroundFocusColor = 'lightGray', + event = 'checkbox_change', height = 1, width = 3, accelerators = { @@ -52,7 +53,7 @@ end function UI.Checkbox:eventHandler(event) if event.type == 'checkbox_toggle' then self.value = not self.value - self:emit({ type = 'checkbox_change', checked = self.value, element = self }) + self:emit({ type = self.event, checked = self.value, element = self }) self:draw() return true end diff --git a/sys/modules/opus/ui/components/TitleBar.lua b/sys/modules/opus/ui/components/TitleBar.lua index 743103a..035642b 100644 --- a/sys/modules/opus/ui/components/TitleBar.lua +++ b/sys/modules/opus/ui/components/TitleBar.lua @@ -77,9 +77,7 @@ function UI.TitleBar.example() x = 2, y = 3, text = 'Press', }, - focus = function (self) - self:raise() - end, + focus = UI.Window.raise, }, draw = function(self, isBG) for i = 1, self.height do -- 2.49.1 From ef9f0e09b6909a72f99a59205ea72dd813ab3bbf Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 26 Apr 2020 19:39:58 -0600 Subject: [PATCH 119/236] partition manager + tab/wizard rework --- sys/apps/Lua.lua | 16 +- sys/apps/Overview.lua | 12 +- sys/apps/Partition.lua | 238 ++++++++++++++++++ sys/apps/Sniff.lua | 4 +- sys/apps/System.lua | 115 +++++---- sys/apps/Welcome.lua | 152 ++++++----- sys/apps/inspect.lua | 6 +- sys/apps/system/aliases.lua | 2 +- sys/apps/system/alternate.lua | 2 +- sys/apps/system/cloud.lua | 2 +- sys/apps/system/diskusage.lua | 10 +- sys/apps/system/kiosk.lua | 2 +- sys/apps/system/label.lua | 2 +- sys/apps/system/launcher.lua | 2 +- sys/apps/system/network.lua | 2 +- sys/apps/system/password.lua | 2 +- sys/apps/system/path.lua | 2 +- sys/apps/system/requires.lua | 2 +- sys/apps/system/settings.lua | 2 +- sys/apps/system/shell.lua | 2 +- sys/apps/system/theme.lua | 2 +- sys/etc/apps.db | 6 + sys/init/2.vfs.lua | 2 + sys/init/4.user.lua | 9 +- sys/init/6.packages.lua | 2 +- sys/modules/opus/ui.lua | 8 +- sys/modules/opus/ui/components/FileSelect.lua | 157 ++++++------ sys/modules/opus/ui/components/Image.lua | 3 +- sys/modules/opus/ui/components/ScrollBar.lua | 2 +- sys/modules/opus/ui/components/Tab.lua | 7 +- sys/modules/opus/ui/components/TabBar.lua | 6 - sys/modules/opus/ui/components/Tabs.lua | 57 +++-- sys/modules/opus/ui/components/TextArea.lua | 3 +- sys/modules/opus/ui/components/Wizard.lua | 78 +++--- sys/modules/opus/ui/components/WizardPage.lua | 3 + 35 files changed, 588 insertions(+), 334 deletions(-) create mode 100644 sys/apps/Partition.lua diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index d4d7d50..352606e 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -45,8 +45,9 @@ local page = UI.Page { }, tabs = UI.Tabs { y = 3, - [1] = UI.Tab { - tabTitle = 'Formatted', + formatted = UI.Tab { + title = 'Formatted', + index = 1, grid = UI.ScrollingGrid { columns = { { heading = 'Key', key = 'name' }, @@ -56,8 +57,9 @@ local page = UI.Page { autospace = true, }, }, - [2] = UI.Tab { - tabTitle = 'Output', + output = UI.Tab { + title = 'Output', + index = 2, backgroundColor = 'black', output = UI.Embedded { y = 2, @@ -72,8 +74,8 @@ local page = UI.Page { }, } -page.grid = page.tabs[1].grid -page.output = page.tabs[2].output +page.grid = page.tabs.formatted.grid +page.output = page.tabs.output.output function page:setPrompt(value, focus) self.prompt:setValue(value) @@ -142,7 +144,7 @@ function page:eventHandler(event) self:setFocus(self.prompt) elseif event.type == 'show_output' then - self.tabs:selectTab(self.tabs[2]) + self.tabs:selectTab(self.tabs.output) elseif event.type == 'autocomplete' then local value = self.prompt.value or '' diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index b879fd0..1b68f5b 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -29,12 +29,14 @@ if not _ENV.multishell then end local REGISTRY_DIR = 'usr/.registry' -local DEFAULT_ICON = NFT.parse("\0308\0317\153\153\153\153\153\ -\0307\0318\153\153\153\153\153\ -\0308\0317\153\153\153\153\153") -local TRANS_ICON = NFT.parse("\0302\0312\32\32\32\32\32\ + +-- icon:gsub('.', function(b) return '\\' .. b:byte() end) +local DEFAULT_ICON = NFT.parse('\30\55\31\48\136\140\140\140\132\ +\30\48\31\55\149\31\48\128\128\128\30\55\149\ +\30\55\31\48\138\143\143\143\133') +local TRANS_ICON = NFT.parse('\0302\0312\32\32\32\32\32\ \0302\0312\32\32\32\32\32\ -\0302\0312\32\32\32\32\32") +\0302\0312\32\32\32\32\32') -- overview local uid = _ENV.multishell.getCurrent() diff --git a/sys/apps/Partition.lua b/sys/apps/Partition.lua new file mode 100644 index 0000000..499222e --- /dev/null +++ b/sys/apps/Partition.lua @@ -0,0 +1,238 @@ +local Ansi = require('opus.ansi') +local Event = require('opus.event') +local UI = require('opus.ui') +local Util = require('opus.util') + +local fs = _G.fs +local peripheral = _G.peripheral + +local source, target + +local function getDriveInfo(tgt) + local total = 0 + local throttle = Util.throttle() + + tgt = fs.combine(tgt, '') + local src = fs.getNode(tgt).source or tgt + + local function recurse(path) + throttle() + if fs.isDir(path) then + if path ~= src then + total = total + 500 + end + for _, v in pairs(fs.native.list(path)) do + recurse(fs.combine(path, v)) + end + else + local sz = fs.getSize(path) + total = total + math.max(500, sz) + end + end + + recurse(src) + + local drive = fs.getDrive(src) + return { + path = tgt, + drive = drive, + type = peripheral.getType(drive) or drive, + used = total, + free = fs.getFreeSpace(src), + mountPoint = src, + } +end + +local function getDrives(exclude) + local drives = { } + + for _, path in pairs(fs.native.list('/')) do + local side = fs.getDrive(path) + if side and not drives[side] and not fs.isReadOnly(path) and side ~= exclude then + if side == 'hdd' then + path = '' + end + drives[side] = getDriveInfo(path) + end + end + return drives +end + +local page = UI.Page { + wizard = UI.Wizard { + ey = -2, + partitions = UI.WizardPage { + index = 1, + info = UI.TextArea { + x = 3, y = 2, ex = -3, ey = 5, + value = [[Move the contents of a directory to another disk. A link will be created to point to that location.]] + }, + grid = UI.Grid { + x = 2, y = 7, ex = -2, ey = -2, + columns = { + { heading = 'Path', key = 'path', textColor = 'yellow', width = 10 }, + { heading = 'Mount Point', key = 'mountPoint' }, + { heading = 'Used', key = 'used', width = 6 }, + }, + sortColumn = 'path', + getDisplayValues = function (_, row) + row = Util.shallowCopy(row) + row.used = Util.toBytes(row.used) + return row + end, + enable = function(self) + Event.onTimeout(0, function() + local mounts = { + usr = getDriveInfo('usr/config'), + packages = getDriveInfo('packages'), + } + self:setValues(mounts) + self:draw() + self:sync() + end) + self:setValues({ }) + UI.Grid.enable(self) + end, + }, + validate = function(self) + target = self.grid:getSelected() + return not not target + end, + }, + mounts = UI.WizardPage { + index = 2, + info = UI.TextArea { + x = 3, y = 2, ex = -3, ey = 5, + value = [[Select the target disk. Labeled computers can be inserted into disk drives for larger volumes.]] + }, + grid = UI.Grid { + x = 2, y = 7, ex = -2, ey = -2, + columns = { + { heading = 'Path', key = 'path', textColor = 'yellow', width = 10 }, + { heading = 'Type', key = 'type' }, + { heading = 'Side', key = 'drive' }, + { heading = 'Free', key = 'free', width = 6 }, + }, + sortColumn = 'path', + getDisplayValues = function (_, row) + row = Util.shallowCopy(row) + row.free = Util.toBytes(row.free) + return row + end, + getRowTextColor = function(self, row) + if row.free < target.used then + return 'lightGray' + end + return UI.Grid.getRowTextColor(self, row) + end, + enable = function(self) + Event.on({ 'disk', 'disk_eject', 'partition_update' }, function() + self:setValues(getDrives(target.drive)) + self:draw() + self:sync() + end) + os.queueEvent('partition_update') + self:setValues({ }) + UI.Grid.enable(self) + end, + }, + validate = function(self) + source = self.grid:getSelected() + if not source then + self:emit({ type = 'notify', message = 'No drive selected' }) + elseif source.free < target.used then + self:emit({ type = 'notify', message = 'Insufficient disk space' }) + else + return true + end + end, + }, + confirm = UI.WizardPage { + index = 3, + info = UI.TextArea { + x = 2, y = 2, ex = -2, ey = -2, + marginTop = 1, marginLeft = 1, + backgroundColor = 'black', + }, + enable = function(self) + local fstab = Util.readFile('usr/etc/fstab') + local lines = { } + table.insert(lines, string.format('%sReview changes%s\n', Ansi.yellow, Ansi.reset)) + if fstab then + for _,l in ipairs(Util.split(fstab)) do + l = Util.trim(l) + if #l > 0 and l:sub(1, 1) ~= '#' then + local m = Util.matches(l) + if m and m[1] and m[1] == target.path then + table.insert(lines, string.format('Removed from usr/etc/fstab:\n%s%s%s\n', Ansi.red, l, Ansi.reset)) + end + end + end + end + local t = target.path + local s = fs.combine(source.path .. '/' .. target.path, '') + if t ~= s then + table.insert(lines, string.format('Added to usr/etc/fstab:\n%s%s linkfs %s%s\n', Ansi.green, t, s, Ansi.reset)) + end + + table.insert(lines, string.format('Move directory:\n%s/%s -> /%s', Ansi.green, target.mountPoint, s)) + + self.info:setText(table.concat(lines, '\n')) + UI.WizardPage.enable(self) + end, + validate = function(self) + if self.changesApplied then + return true + end + local fstab = Util.readFile('usr/etc/fstab') + local lines = { } + if fstab then + for _,l in ipairs(Util.split(fstab)) do + table.insert(lines, l) + l = Util.trim(l) + if #l > 0 and l:sub(1, 1) ~= '#' then + local m = Util.matches(l) + if m and m[1] and m[1] == target.path then + fs.unmount(m[1]) + table.remove(lines) + end + end + end + end + + local t = target.path + local s = fs.combine(source.path .. '/' .. target.path, '') + + fs.move('/' .. target.mountPoint, '/' .. s) + + if t ~= s then + table.insert(lines, string.format('%s linkfs %s', t, s)) + fs.mount(t, 'linkfs', s) + end + + Util.writeFile('usr/etc/fstab', table.concat(lines, '\n')) + + self.parent.nextButton.text = 'Exit' + self.parent.cancelButton:disable() + self.parent.previousButton:disable() + + self.changesApplied = true + self.info:setValue('Changes have been applied') + self.parent:draw() + end, + }, + }, + notification = UI.Notification { }, + eventHandler = function(self, event) + if event.type == 'notify' then + self.notification:error(event.message) + elseif event.type == 'accept' or event.type == 'cancel' then + UI:quit() + end + return UI.Page.eventHandler(self, event) + end, +} + +UI:disableEffects() +UI:setPage(page) +UI:start() diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index bec9ac0..5336234 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -46,7 +46,7 @@ local page = UI.Page { configTabs = UI.Tabs { y = 2, filterTab = UI.Tab { - tabTitle = 'Filter', + title = 'Filter', noFill = true, filterGridText = UI.Text { x = 2, y = 2, @@ -93,7 +93,7 @@ local page = UI.Page { }, }, modemTab = UI.Tab { - tabTitle = 'Modem', + title = 'Modem', channelGrid = UI.ScrollingGrid { x = 2, y = 2, width = 12, height = 5, diff --git a/sys/apps/System.lua b/sys/apps/System.lua index 7005d83..cd22812 100644 --- a/sys/apps/System.lua +++ b/sys/apps/System.lua @@ -6,62 +6,6 @@ local shell = _ENV.shell UI:configure('System', ...) -local systemPage = UI.Page { - tabs = UI.Tabs { - settings = UI.Tab { - tabTitle = 'Category', - grid = UI.ScrollingGrid { - x = 2, y = 2, ex = -2, ey = -2, - columns = { - { heading = 'Name', key = 'name' }, - { heading = 'Description', key = 'description' }, - }, - sortColumn = 'name', - autospace = true, - }, - }, - }, - notification = UI.Notification(), - accelerators = { - [ 'control-q' ] = 'quit', - }, -} - -function systemPage.tabs.settings:eventHandler(event) - if event.type == 'grid_select' then - local tab = event.selected.tab - if not systemPage.tabs[tab.tabTitle] then - systemPage.tabs:add({ [ tab.tabTitle ] = tab }) - tab:disable() - end - systemPage.tabs:selectTab(tab) - --self.parent:draw() - return true - end -end - -function systemPage:eventHandler(event) - if event.type == 'quit' then - UI:quit() - - elseif event.type == 'success_message' then - self.notification:success(event.message) - - elseif event.type == 'info_message' then - self.notification:info(event.message) - - elseif event.type == 'error_message' then - self.notification:error(event.message) - - elseif event.type == 'tab_activate' then - event.activated:focusFirst() - - else - return UI.Page.eventHandler(self, event) - end - return true -end - local function loadDirectory(dir) local plugins = { } for _, file in pairs(fs.list(dir)) do @@ -70,7 +14,7 @@ local function loadDirectory(dir) _G.printError('Error loading: ' .. file) error(m or 'Unknown error') elseif s and m then - table.insert(plugins, { tab = m, name = m.tabTitle, description = m.description }) + table.insert(plugins, { tab = m, name = m.title, description = m.description }) end end return plugins @@ -79,7 +23,60 @@ end local programDir = fs.getDir(shell.getRunningProgram()) local plugins = loadDirectory(fs.combine(programDir, 'system'), { }) -systemPage.tabs.settings.grid:setValues(plugins) +local page = UI.Page { + tabs = UI.Tabs { + settings = UI.Tab { + title = 'Category', + grid = UI.ScrollingGrid { + x = 2, y = 2, ex = -2, ey = -2, + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Description', key = 'description' }, + }, + sortColumn = 'name', + autospace = true, + values = plugins, + }, + accelerators = { + grid_select = 'category_select', + } + }, + }, + notification = UI.Notification(), + accelerators = { + [ 'control-q' ] = 'quit', + }, + eventHandler = function(self, event) + if event.type == 'quit' then + UI:quit() -UI:setPage(systemPage) + elseif event.type == 'category_select' then + local tab = event.selected.tab + + if not self.tabs[tab.title] then + self.tabs:add({ [ tab.title ] = tab }) + end + self.tabs:selectTab(tab) + return true + + elseif event.type == 'success_message' then + self.notification:success(event.message) + + elseif event.type == 'info_message' then + self.notification:info(event.message) + + elseif event.type == 'error_message' then + self.notification:error(event.message) + + elseif event.type == 'tab_activate' then + event.activated:focusFirst() + + else + return UI.Page.eventHandler(self, event) + end + return true + end, +} + +UI:setPage(page) UI:start() diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 1154fbf..f1a9972 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -33,87 +33,85 @@ https://github.com/kepler155c/opus]] local page = UI.Page { wizard = UI.Wizard { ey = -2, - pages = { - splash = UI.WizardPage { - index = 1, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = string.format(splashIntro, Ansi.white), - }, + splash = UI.WizardPage { + index = 1, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = string.format(splashIntro, Ansi.white), }, - label = UI.WizardPage { - index = 2, - labelText = UI.Text { - x = 3, y = 2, - value = 'Label' - }, - label = UI.TextEntry { - x = 9, y = 2, ex = -3, - limit = 32, - value = os.getComputerLabel(), - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 4, ey = -3, - value = string.format(labelIntro, Ansi.white), - }, - validate = function (self) - if self.label.value then - os.setComputerLabel(self.label.value) - end - return true - end, + }, + label = UI.WizardPage { + index = 2, + labelText = UI.Text { + x = 3, y = 2, + value = 'Label' }, - password = UI.WizardPage { - index = 3, - passwordLabel = UI.Text { - x = 3, y = 2, - value = 'Password' - }, - newPass = UI.TextEntry { - x = 12, ex = -3, y = 2, - limit = 32, - mask = true, - shadowText = 'password', - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 5, ey = -3, - value = string.format(passwordIntro, Ansi.white), - }, - validate = function (self) - if type(self.newPass.value) == "string" and #self.newPass.value > 0 then - Security.updatePassword(SHA.compute(self.newPass.value)) - end - return true - end, + label = UI.TextEntry { + x = 9, y = 2, ex = -3, + limit = 32, + value = os.getComputerLabel(), }, - packages = UI.WizardPage { - index = 4, - button = UI.Button { - x = 3, y = -3, - text = 'Open Package Manager', - event = 'packages', - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -4, - value = string.format(packagesIntro, Ansi.white), - }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 4, ey = -3, + value = string.format(labelIntro, Ansi.white), }, - contributors = UI.WizardPage { - index = 5, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = string.format(contributorsIntro, Ansi.white, Ansi.yellow, Ansi.white), - }, + validate = function (self) + if self.label.value then + os.setComputerLabel(self.label.value) + end + return true + end, + }, + password = UI.WizardPage { + index = 3, + passwordLabel = UI.Text { + x = 3, y = 2, + value = 'Password' + }, + newPass = UI.TextEntry { + x = 12, ex = -3, y = 2, + limit = 32, + mask = true, + shadowText = 'password', + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 5, ey = -3, + value = string.format(passwordIntro, Ansi.white), + }, + validate = function (self) + if type(self.newPass.value) == "string" and #self.newPass.value > 0 then + Security.updatePassword(SHA.compute(self.newPass.value)) + end + return true + end, + }, + packages = UI.WizardPage { + index = 4, + button = UI.Button { + x = 3, y = -3, + text = 'Open Package Manager', + event = 'packages', + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -4, + value = string.format(packagesIntro, Ansi.white), + }, + }, + contributors = UI.WizardPage { + index = 5, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = string.format(contributorsIntro, Ansi.white, Ansi.yellow, Ansi.white), }, }, }, diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 35a9f5c..0df9526 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -49,7 +49,7 @@ page = UI.Page { backgroundColor = colors.red, y = '50%', properties = UI.Tab { - tabTitle = 'Properties', + title = 'Properties', grid = UI.ScrollingGrid { headerBackgroundColor = colors.red, sortColumn = 'key', @@ -64,7 +64,7 @@ page = UI.Page { }, methodsTab = UI.Tab { index = 2, - tabTitle = 'Methods', + title = 'Methods', grid = UI.ScrollingGrid { ex = '50%', headerBackgroundColor = colors.red, @@ -85,7 +85,7 @@ page = UI.Page { }, events = UI.Tab { index = 1, - tabTitle = 'Events', + title = 'Events', UI.MenuBar { y = -1, backgroundColor = colors.red, diff --git a/sys/apps/system/aliases.lua b/sys/apps/system/aliases.lua index 2d122bb..ede9349 100644 --- a/sys/apps/system/aliases.lua +++ b/sys/apps/system/aliases.lua @@ -4,7 +4,7 @@ local UI = require('opus.ui') local kernel = _G.kernel local aliasTab = UI.Tab { - tabTitle = 'Aliases', + title = 'Aliases', description = 'Shell aliases', alias = UI.TextEntry { x = 2, y = 2, ex = -2, diff --git a/sys/apps/system/alternate.lua b/sys/apps/system/alternate.lua index e903074..35bd601 100644 --- a/sys/apps/system/alternate.lua +++ b/sys/apps/system/alternate.lua @@ -3,7 +3,7 @@ local Config = require('opus.config') local UI = require('opus.ui') local tab = UI.Tab { - tabTitle = 'Preferred', + title = 'Preferred', description = 'Select preferred applications', apps = UI.ScrollingGrid { x = 2, y = 2, diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index d837f2e..3cbe740 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -6,7 +6,7 @@ if _G.http.websocket then local config = Config.load('cloud') local tab = UI.Tab { - tabTitle = 'Cloud', + title = 'Cloud', description = 'Cloud Catcher options', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 4, diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 9a07e17..1042cc4 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -16,7 +16,7 @@ local NftImages = { } local tab = UI.Tab { - tabTitle = 'Disks Usage', + title = 'Disks Usage', description = 'Visualise HDD and disks usage', drives = UI.ScrollingGrid { @@ -138,11 +138,9 @@ function tab:enable() UI.Tab.enable(self) self.handler = Event.on({ 'disk', 'disk_eject' }, function() os.sleep(1) - if tab.enabled then - tab:updateDrives() - tab:updateInfo() - tab:sync() - end + tab:updateDrives() + tab:updateInfo() + tab:sync() end) end diff --git a/sys/apps/system/kiosk.lua b/sys/apps/system/kiosk.lua index 2f9510b..be5b18e 100644 --- a/sys/apps/system/kiosk.lua +++ b/sys/apps/system/kiosk.lua @@ -5,7 +5,7 @@ local peripheral = _G.peripheral local settings = _G.settings return peripheral.find('monitor') and UI.Tab { - tabTitle = 'Kiosk', + title = 'Kiosk', description = 'Kiosk options', form = UI.Form { x = 2, y = 2, ex = -2, ey = 5, diff --git a/sys/apps/system/label.lua b/sys/apps/system/label.lua index 8fe3d1e..ba38109 100644 --- a/sys/apps/system/label.lua +++ b/sys/apps/system/label.lua @@ -5,7 +5,7 @@ local fs = _G.fs local os = _G.os return UI.Tab { - tabTitle = 'Label', + title = 'Label', description = 'Set the computer label', labelText = UI.Text { x = 3, y = 3, diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index 9c87fe9..c3f8588 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -7,7 +7,7 @@ local fs = _G.fs local config = Config.load('multishell') local tab = UI.Tab { - tabTitle = 'Launcher', + title = 'Launcher', description = 'Set the application launcher', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 5, diff --git a/sys/apps/system/network.lua b/sys/apps/system/network.lua index b4fd802..b214d12 100644 --- a/sys/apps/system/network.lua +++ b/sys/apps/system/network.lua @@ -6,7 +6,7 @@ local colors = _G.colors local device = _G.device return UI.Tab { - tabTitle = 'Network', + title = 'Network', description = 'Networking options', info = UI.TextArea { x = 2, y = 5, ex = -2, ey = -2, diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 8040c76..a01c610 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -3,7 +3,7 @@ local SHA = require('opus.crypto.sha2') local UI = require('opus.ui') return UI.Tab { - tabTitle = 'Password', + title = 'Password', description = 'Wireless network password', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 4, diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index eef9f16..9550cb4 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -3,7 +3,7 @@ local UI = require('opus.ui') local Util = require('opus.util') local tab = UI.Tab { - tabTitle = 'Path', + title = 'Path', description = 'Set the shell path', tabClose = true, [1] = UI.Window { diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index 1e41216..54d78d8 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -3,7 +3,7 @@ local UI = require('opus.ui') local Util = require('opus.util') local tab = UI.Tab { - tabTitle = 'Requires', + title = 'Requires', description = 'Require path', tabClose = true, entry = UI.TextEntry { diff --git a/sys/apps/system/settings.lua b/sys/apps/system/settings.lua index 27f1f2b..6676910 100644 --- a/sys/apps/system/settings.lua +++ b/sys/apps/system/settings.lua @@ -8,7 +8,7 @@ local transform = { } return settings and UI.Tab { - tabTitle = 'Settings', + title = 'Settings', description = 'Computercraft settings', grid = UI.Grid { x = 2, y = 2, ex = -2, ey = -2, diff --git a/sys/apps/system/shell.lua b/sys/apps/system/shell.lua index 8ca32fe..8fe9807 100644 --- a/sys/apps/system/shell.lua +++ b/sys/apps/system/shell.lua @@ -39,7 +39,7 @@ if not _colors.backgroundColor then end return UI.Tab { - tabTitle = 'Shell', + title = 'Shell', description = 'Shell options', grid1 = UI.ScrollingGrid { y = 2, ey = -10, x = 2, ex = -17, diff --git a/sys/apps/system/theme.lua b/sys/apps/system/theme.lua index b9d4ed4..0df173c 100644 --- a/sys/apps/system/theme.lua +++ b/sys/apps/system/theme.lua @@ -17,7 +17,7 @@ for k,v in pairs(UI.colors) do end return UI.Tab { - tabTitle = 'Theme', + title = 'Theme', description = 'Theme colors', grid1 = UI.ScrollingGrid { y = 2, ey = -10, x = 2, ex = -17, diff --git a/sys/etc/apps.db b/sys/etc/apps.db index f342709..7e96569 100644 --- a/sys/etc/apps.db +++ b/sys/etc/apps.db @@ -136,4 +136,10 @@ iconExt = "\030 \031 \128\0307\143\131\131\131\131\143\030 \128\010\0307\031 \129\0317\128\0319\136\0309\031b\136\132\0307\0319\132\0317\128\031 \130\010\030 \0317\130\143\0307\128\128\128\128\030 \143\129", run = "/rom/programs/fun/dj", }, + [ "4dbdd221e957eff27cc47796f3ed8447290f71c7ad8b95e5bd828b31c1858f15" ] = { + title = "Partition", + category = "System", + iconExt = "\30\55\31\55\128\30\48\135\131\139\30\55\128\128\128\10\30\48\31\55\149\31\48\128\30\55\145\30\48\31\56\140\30\55\157\144\144\10\30\55\31\55\128\31\48\139\143\135\31\55\128\31\56\142\133", + run = "Partition", + } } diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 69eb387..d624810 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -134,6 +134,8 @@ local function getNode(dir) return node end +fs.getNode = getNode + local methods = { 'delete', 'getFreeSpace', 'exists', 'isDir', 'getSize', 'isReadOnly', 'makeDir', 'getDrive', 'list', 'open' } diff --git a/sys/init/4.user.lua b/sys/init/4.user.lua index 4decc1f..a742b34 100644 --- a/sys/init/4.user.lua +++ b/sys/init/4.user.lua @@ -10,6 +10,13 @@ if not fs.exists('usr/autorun') then fs.makeDir('usr/autorun') end +-- move the fstab out of config so that the config directory +-- can be remapped to another disk (and for consistency) +if fs.exists('usr/config/fstab') and not fs.exists('usr/etc/fstab') then + fs.move('usr/config/fstab', 'usr/etc/fstab') +end +fs.loadTab('usr/etc/fstab') + -- TODO: Temporary local upgrade = Util.readTable('usr/config/shell') if upgrade and (not upgrade.upgraded or upgrade.upgraded ~= 1) then @@ -45,5 +52,3 @@ shell.setPath(table.concat(path, ':')) --_G.LUA_PATH = config.lua_path --_G.settings.set('mbs.shell.require_path', config.lua_path) - -fs.loadTab('usr/config/fstab') diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index 49c55bf..3ad43ab 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -14,7 +14,7 @@ for name in pairs(Packages:installed()) do local packageDir = fs.combine('packages', name) table.insert(appPaths, 1, '/' .. packageDir) - local apiPath = fs.combine(packageDir, 'apis') + local apiPath = fs.combine(packageDir, 'apis') -- TODO: rename dir to 'modules' (someday) if fs.exists(apiPath) then fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath) end diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 625128f..f64a77f 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -675,7 +675,7 @@ function UI.Window:drawChildren() end UI.Window.docs.getDoc = [[getDoc(STRING method) -Gets the documentation for a method.]] +Get the documentation for a method.]] function UI.Window:getDoc(method) local m = getmetatable(self) -- get the class for this instance repeat @@ -743,6 +743,8 @@ function UI.Window:clear(bg, fg) Canvas.clear(self, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) end +UI.Window.docs.clearLine = [[clearLine(NUMBER y, opt COLOR bg) +Clears the specified line.]] function UI.Window:clearLine(y, bg) self:write(1, y, _rep(' ', self.width), bg) end @@ -760,6 +762,10 @@ function UI.Window:fillArea(x, y, width, height, fillChar, bg, fg) end end +UI.Window.docs.write = [[write(NUMBER x, NUMBER y, String text, opt COLOR bg, opt COLOR fg) +Write text to the canvas. +If colors are not specified, the colors from the base class will be used. +If the base class does not have colors defined, colors will be inherited from the parent container.]] function UI.Window:write(x, y, text, bg, fg) Canvas.write(self, x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) end diff --git a/sys/modules/opus/ui/components/FileSelect.lua b/sys/modules/opus/ui/components/FileSelect.lua index 55b86b7..dd8747e 100644 --- a/sys/modules/opus/ui/components/FileSelect.lua +++ b/sys/modules/opus/ui/components/FileSelect.lua @@ -1,86 +1,75 @@ local class = require('opus.class') -local UI = require('opus.ui') -local Util = require('opus.util') +local UI = require('opus.ui') +local Util = require('opus.util') -local colors = _G.colors -local fs = _G.fs +local fs = _G.fs UI.FileSelect = class(UI.Window) UI.FileSelect.defaults = { UIElement = 'FileSelect', } function UI.FileSelect:postInit() - self.grid = UI.ScrollingGrid { - x = 2, y = 2, ex = -2, ey = -4, - dir = '/', - sortColumn = 'name', - columns = { - { heading = 'Name', key = 'name' }, - { heading = 'Size', key = 'size', width = 5 } - }, - getDisplayValues = function(_, row) - if row.size then - row = Util.shallowCopy(row) - row.size = Util.toBytes(row.size) - end - return row - end, - getRowTextColor = function(_, file) - if file.isDir then - return colors.cyan - end - if file.isReadOnly then - return colors.pink - end - return colors.white - end, - sortCompare = function(self, a, b) - if self.sortColumn == 'size' then - return a.size < b.size - end - if a.isDir == b.isDir then - return a.name:lower() < b.name:lower() - end - return a.isDir - end, - draw = function(self) - local files = fs.listEx(self.dir) - if #self.dir > 0 then - table.insert(files, { - name = '..', - isDir = true, - }) - end - self:setValues(files) - self:setIndex(1) - UI.Grid.draw(self) - end, - } - self.path = UI.TextEntry { - x = 2, - y = -2, - ex = -11, - limit = 256, - accelerators = { - enter = 'path_enter', - } - } - self.cancel = UI.Button { - text = 'Cancel', - x = -9, - y = -2, - event = 'select_cancel', - } + self.grid = UI.ScrollingGrid { + x = 2, y = 2, ex = -2, ey = -4, + dir = '/', + sortColumn = 'name', + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Size', key = 'size', width = 5 } + }, + getDisplayValues = function(_, row) + return { + name = row.name, + size = row.size and Util.toBytes(row.size), + } + end, + getRowTextColor = function(_, file) + return file.isDir and 'cyan' or file.isReadOnly and 'pink' or 'white' + end, + sortCompare = function(self, a, b) + if self.sortColumn == 'size' then + return a.size < b.size + end + if a.isDir == b.isDir then + return a.name:lower() < b.name:lower() + end + return a.isDir + end, + draw = function(self) + local files = fs.listEx(self.dir) + if #self.dir > 0 then + table.insert(files, { + name = '..', + isDir = true, + }) + end + self:setValues(files) + self:setIndex(1) + UI.Grid.draw(self) + end, + } + self.path = UI.TextEntry { + x = 2, y = -2, ex = -11, + limit = 256, + accelerators = { + enter = 'path_enter', + } + } + self.cancel = UI.Button { + x = -9, y = -2, + text = 'Cancel', + event = 'select_cancel', + } end function UI.FileSelect:draw() - self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray) - self:drawChildren() + self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), 'black', 'gray') + self:drawChildren() end function UI.FileSelect:enable(path) - self:setPath(path or '') - UI.Window.enable(self) + self:setPath(path or '') + UI.Window.enable(self) end function UI.FileSelect:setPath(path) @@ -98,21 +87,21 @@ function UI.FileSelect:eventHandler(event) if event.selected.isDir then self.grid:draw() self.path:draw() - else - self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) - end - return true + else + self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) + end + return true - elseif event.type == 'path_enter' then - if self.path.value then - if fs.isDir(self.path.value) then - self:setPath(self.path.value) - self.grid:draw() - self.path:draw() - else - self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) - end - end - return true + elseif event.type == 'path_enter' then + if self.path.value then + if fs.isDir(self.path.value) then + self:setPath(self.path.value) + self.grid:draw() + self.path:draw() + else + self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) + end + end + return true end end diff --git a/sys/modules/opus/ui/components/Image.lua b/sys/modules/opus/ui/components/Image.lua index 7d67c35..f24c911 100644 --- a/sys/modules/opus/ui/components/Image.lua +++ b/sys/modules/opus/ui/components/Image.lua @@ -33,7 +33,7 @@ function UI.Image:draw() for x = 1, #line do local ch = lookup:find(line:sub(x, x)) if ch then - self:write(x, y, ' ', 2 ^ (ch -1)) + self:write(x, y, ' ', 2 ^ (ch - 1)) end end end @@ -47,6 +47,7 @@ end function UI.Image.example() return UI.Image { + backgroundColor = 'primary', filename = 'test.paint', } end diff --git a/sys/modules/opus/ui/components/ScrollBar.lua b/sys/modules/opus/ui/components/ScrollBar.lua index 8f8b6ab..715c3cb 100644 --- a/sys/modules/opus/ui/components/ScrollBar.lua +++ b/sys/modules/opus/ui/components/ScrollBar.lua @@ -7,7 +7,7 @@ local colors = _G.colors UI.ScrollBar = class(UI.Window) UI.ScrollBar.defaults = { UIElement = 'ScrollBar', - lineChar = '|', + lineChar = '\166', sliderChar = UI.extChars and '\127' or '#', upArrowChar = UI.extChars and '\30' or '^', downArrowChar = UI.extChars and '\31' or 'v', diff --git a/sys/modules/opus/ui/components/Tab.lua b/sys/modules/opus/ui/components/Tab.lua index d2e591b..ee835d4 100644 --- a/sys/modules/opus/ui/components/Tab.lua +++ b/sys/modules/opus/ui/components/Tab.lua @@ -4,7 +4,7 @@ local UI = require('opus.ui') UI.Tab = class(UI.Window) UI.Tab.defaults = { UIElement = 'Tab', - tabTitle = 'tab', + title = 'tab', y = 2, } @@ -14,3 +14,8 @@ function UI.Tab:draw() end self:drawChildren() end + +function UI.Tab:enable() + UI.Window.enable(self) + self:emit({ type = 'tab_activate', activated = self }) +end diff --git a/sys/modules/opus/ui/components/TabBar.lua b/sys/modules/opus/ui/components/TabBar.lua index eb81b3a..0f75e05 100644 --- a/sys/modules/opus/ui/components/TabBar.lua +++ b/sys/modules/opus/ui/components/TabBar.lua @@ -37,9 +37,3 @@ function UI.TabBar:eventHandler(event) return UI.MenuBar.eventHandler(self, event) end -function UI.TabBar:selectTab(text) - local menuItem = Util.find(self.children, 'text', text) - if menuItem then - menuItem.selected = true - end -end diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index 8c2a692..f2b61a8 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -14,11 +14,11 @@ end function UI.Tabs:add(children) local buttons = { } for _,child in pairs(children) do - if type(child) == 'table' and child.UIElement and child.tabTitle then + if type(child) == 'table' and child.UIElement and child.UIElement == 'Tab' then child.y = 2 table.insert(buttons, { index = child.index, - text = child.tabTitle, + text = child.title, event = 'tab_select', tabUid = child.uid, }) @@ -34,7 +34,12 @@ function UI.Tabs:add(children) end if self.parent then + local enabled = self.enabled + + -- don't enable children upon add + self.enabled = nil UI.Window.add(self, children) + self.enabled = enabled end end @@ -43,7 +48,15 @@ Make to the passed tab active.]] function UI.Tabs:selectTab(tab) local menuItem = Util.find(self.tabBar.children, 'tabUid', tab.uid) if menuItem then - self.tabBar:emit({ type = 'tab_select', button = { uid = menuItem.uid } }) + if self.enabled then + self.tabBar:emit({ type = 'tab_select', button = { uid = menuItem.uid } }) + else + local previous = Util.find(self.tabBar.children, 'selected', true) + if previous then + previous.selected = false + end + menuItem.selected = true + end end end @@ -59,14 +72,20 @@ function UI.Tabs:enable() self.tabBar:enable() local menuItem = Util.find(self.tabBar.children, 'selected', true) + self:enableTab(menuItem.tabUid) +end +function UI.Tabs:enableTab(tabUid, hint) for child in self:eachChild() do - child.transitionHint = nil - if child.uid == menuItem.tabUid then - child:enable() - self:emit({ type = 'tab_activate', activated = child }) - elseif child.tabTitle then - child:disable() + child.transitionHint = hint + if child.uid == tabUid then + if not child.enabled then + child:enable() + end + elseif child.UIElement == 'Tab' then + if child.enabled then + child:disable() + end end end end @@ -76,15 +95,7 @@ function UI.Tabs:eventHandler(event) local tab = self:find(event.tab.tabUid) local hint = event.current > event.last and 'slideLeft' or 'slideRight' - for child in self:eachChild() do - if child.uid == event.tab.tabUid then - child.transitionHint = hint - child:enable() - elseif child.tabTitle then - child:disable() - end - end - self:emit({ type = 'tab_activate', activated = tab }) + self:enableTab(event.tab.tabUid, hint) tab:draw() return true end @@ -94,28 +105,28 @@ function UI.Tabs.example() return UI.Tabs { tab1 = UI.Tab { index = 1, - tabTitle = 'tab1', + title = 'tab1', entry = UI.TextEntry { y = 3, shadowText = 'text' }, }, tab2 = UI.Tab { index = 2, - tabTitle = 'tab2', + title = 'tab2', subtabs = UI.Tabs { x = 3, y = 2, ex = -3, ey = -2, tab1 = UI.Tab { index = 1, - tabTitle = 'tab4', + title = 'tab4', entry = UI.TextEntry { y = 3, shadowText = 'text' }, }, tab3 = UI.Tab { index = 2, - tabTitle = 'tab5', + title = 'tab5', }, }, }, tab3 = UI.Tab { index = 3, - tabTitle = 'tab3', + title = 'tab3', }, enable = function(self) UI.Tabs.enable(self) diff --git a/sys/modules/opus/ui/components/TextArea.lua b/sys/modules/opus/ui/components/TextArea.lua index 3b821c7..8572a59 100644 --- a/sys/modules/opus/ui/components/TextArea.lua +++ b/sys/modules/opus/ui/components/TextArea.lua @@ -8,11 +8,12 @@ UI.TextArea.defaults = { value = '', showScrollBar = true, } -function UI.TextArea:setText(text) +function UI.TextArea:setValue(text) self:reset() self.value = text self:draw() end +UI.TextArea.setText = UI.TextArea.setValue -- deprecate function UI.TextArea.focus() -- allow keyboard scrolling diff --git a/sys/modules/opus/ui/components/Wizard.lua b/sys/modules/opus/ui/components/Wizard.lua index 0fcab17..791a8a9 100644 --- a/sys/modules/opus/ui/components/Wizard.lua +++ b/sys/modules/opus/ui/components/Wizard.lua @@ -15,52 +15,50 @@ function UI.Wizard:postInit() } self.previousButton = UI.Button { x = -18, y = -1, - text = '< Back', + text = '\17 Back', event = 'previousView', } self.nextButton = UI.Button { x = -9, y = -1, - text = 'Next >', + text = 'Next \16', event = 'nextView', } Util.merge(self, self.pages) end -function UI.Wizard:add(pages) - Util.merge(self.pages, pages) - Util.merge(self, pages) - - for _, child in pairs(self.pages) do - child.ey = child.ey or -2 - end - - if self.parent then - self:initChildren() +function UI.Wizard:getPages() + local t = { } + for child in self:eachChild() do + if type(child) == 'table' and child.UIElement == 'WizardPage' then + table.insert(t, child) + end end + return t end function UI.Wizard:getPage(index) - return Util.find(self.pages, 'index', index) + local pages = self:getPages() + return Util.find(pages, 'index', index) end -function UI.Wizard:enable(...) +function UI.Wizard:enable() self.enabled = true self.index = 1 - local initial = self:getPage(1) for child in self:eachChild() do - if child == initial or not child.index then - child:enable(...) - else + if child.UIElement ~= 'WizardPage' then + child:enable() + elseif child.enabled then child:disable() end end + local initial = self:getPage(1) self:emit({ type = 'enable_view', next = initial }) end function UI.Wizard:isViewValid() local currentView = self:getPage(self.index) - return not currentView.validate and true or currentView:validate() + return currentView:validate() end function UI.Wizard:eventHandler(event) @@ -107,7 +105,7 @@ function UI.Wizard:eventHandler(event) end if self:getPage(self.index + 1) then - self.nextButton.text = 'Next >' + self.nextButton.text = 'Next \16' self.nextButton.event = 'nextView' else self.nextButton.text = 'Accept' @@ -124,29 +122,27 @@ end function UI.Wizard.example() return UI.Wizard { ey = -2, - pages = { - splash = UI.WizardPage { - index = 1, - intro = UI.TextArea { - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = 'sample text', - }, + splash = UI.WizardPage { + index = 1, + intro = UI.TextArea { + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = 'sample text', }, - label = UI.WizardPage { - index = 2, - intro = UI.TextArea { - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = 'sample more text', - }, + }, + label = UI.WizardPage { + index = 2, + intro = UI.TextArea { + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = 'sample more text', }, - password = UI.WizardPage { - index = 3, - text = UI.TextEntry { - x = 12, ex = -3, y = 2, - shadowText = 'tet', - }, + }, + password = UI.WizardPage { + index = 3, + text = UI.TextEntry { + x = 12, ex = -3, y = 2, + shadowText = 'tet', }, }, } diff --git a/sys/modules/opus/ui/components/WizardPage.lua b/sys/modules/opus/ui/components/WizardPage.lua index da895ef..f23f107 100644 --- a/sys/modules/opus/ui/components/WizardPage.lua +++ b/sys/modules/opus/ui/components/WizardPage.lua @@ -6,3 +6,6 @@ UI.WizardPage.defaults = { UIElement = 'WizardPage', ey = -2, } +function UI.WizardPage.validate() + return true +end -- 2.49.1 From 602d12afc567c059931ef36b43a48d2de6cc447e Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 26 Apr 2020 20:36:50 -0600 Subject: [PATCH 120/236] vfs bugfix --- sys/init/2.vfs.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index d624810..a19078a 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -279,6 +279,9 @@ function fs.mount(path, fstype, ...) end if not tp.nodes[d] then tp.nodes[d] = Util.shallowCopy(tp) + if tp.fstype == 'linkfs' then + tp.nodes[d].source = fs.combine(tp.nodes[d].source, d) + end tp.nodes[d].nodes = { } tp.nodes[d].mountPoint = fs.combine(tp.mountPoint, d) end -- 2.49.1 From d72ae3de4a54257eea7887d0575558ed26cea0b5 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 26 Apr 2020 22:07:18 -0600 Subject: [PATCH 121/236] vfs bugfix --- sys/init/2.vfs.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index a19078a..bc1dc96 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -267,6 +267,16 @@ function fs.mount(path, fstype, ...) if not vfs then error('Invalid file system type') end + +-- hack - get the mount point for the path +-- ie. if packages is mapped to disk/packages +-- and a request to mount /packages/foo +-- then use disk/packages/foo as the mountPoint +local n = getNode(path) +if n.fstype == 'linkfs' then + path = path:gsub(n.mountPoint, n.source, 1) +end + local node = vfs.mount(path, ...) if node then local parts = splitpath(path) @@ -279,9 +289,12 @@ function fs.mount(path, fstype, ...) end if not tp.nodes[d] then tp.nodes[d] = Util.shallowCopy(tp) + + -- another related hack if tp.fstype == 'linkfs' then tp.nodes[d].source = fs.combine(tp.nodes[d].source, d) end + tp.nodes[d].nodes = { } tp.nodes[d].mountPoint = fs.combine(tp.mountPoint, d) end -- 2.49.1 From e116caf16effae122e9560de3ea0ac47eb6f71e6 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 27 Apr 2020 15:44:09 -0600 Subject: [PATCH 122/236] fix netfs issues - implement fs.attributes --- sys/apps/Lua.lua | 8 +------ sys/apps/package.lua | 2 +- sys/init/2.vfs.lua | 28 ++++++++++++++---------- sys/modules/opus/fs/linkfs.lua | 8 +++++-- sys/modules/opus/fs/netfs.lua | 39 +++++++++++++++++----------------- sys/modules/opus/fs/ramfs.lua | 13 ++++++++++++ sys/modules/opus/fs/urlfs.lua | 11 ++++++++++ sys/modules/opus/socket.lua | 21 +++++++++--------- 8 files changed, 78 insertions(+), 52 deletions(-) diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 352606e..557573c 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -10,10 +10,8 @@ local os = _G.os local textutils = _G.textutils local term = _G.term -local _exit - local sandboxEnv = setmetatable(Util.shallowCopy(_ENV), { __index = _G }) -sandboxEnv.exit = function() _exit = true end +sandboxEnv.exit = function() UI:quit() end sandboxEnv._echo = function( ... ) return { ... } end _G.requireInjector(sandboxEnv) @@ -363,10 +361,6 @@ function page:executeStatement(statement) self:emit({ type = 'show_output' }) end end - - if _exit then - UI:quit() - end end local args = Util.parse(...) diff --git a/sys/apps/package.lua b/sys/apps/package.lua index b5fdba3..324d321 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -151,7 +151,7 @@ if action == 'uninstall' then runScript(manifest.uninstall) local packageDir = fs.combine('packages', name) - fs.delete(packageDir) + fs.delete(fs.resolve(packageDir)) print('removed: ' .. packageDir) return end diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index bc1dc96..3361d80 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -19,6 +19,10 @@ for k,fn in pairs(fs) do end end +function nativefs.resolve(_, dir) + return dir +end + function nativefs.list(node, dir) local files if fs.native.isDir(dir) then @@ -137,7 +141,7 @@ end fs.getNode = getNode local methods = { 'delete', 'getFreeSpace', 'exists', 'isDir', 'getSize', - 'isReadOnly', 'makeDir', 'getDrive', 'list', 'open' } + 'isReadOnly', 'makeDir', 'getDrive', 'list', 'open', 'attributes' } for _,m in pairs(methods) do fs[m] = function(dir, ...) @@ -147,6 +151,12 @@ for _,m in pairs(methods) do end end +-- if a link, return the source for this link +function fs.resolve(dir) + local n = getNode(dir) + return n.fs.resolve and n.fs.resolve(n, dir) or dir +end + function fs.complete(partial, dir, includeFiles, includeSlash) dir = fs.combine(dir, '') local node = getNode(dir) @@ -268,14 +278,11 @@ function fs.mount(path, fstype, ...) error('Invalid file system type') end --- hack - get the mount point for the path --- ie. if packages is mapped to disk/packages --- and a request to mount /packages/foo --- then use disk/packages/foo as the mountPoint -local n = getNode(path) -if n.fstype == 'linkfs' then - path = path:gsub(n.mountPoint, n.source, 1) -end + -- get the mount point for the path + -- ie. if packages is mapped to disk/packages + -- and a request to mount /packages/foo + -- then use disk/packages/foo as the mountPoint + path = fs.resolve(path) local node = vfs.mount(path, ...) if node then @@ -290,8 +297,7 @@ end if not tp.nodes[d] then tp.nodes[d] = Util.shallowCopy(tp) - -- another related hack - if tp.fstype == 'linkfs' then + if tp.nodes[d].source then tp.nodes[d].source = fs.combine(tp.nodes[d].source, d) end diff --git a/sys/modules/opus/fs/linkfs.lua b/sys/modules/opus/fs/linkfs.lua index 79ec13d..8ffdb4c 100644 --- a/sys/modules/opus/fs/linkfs.lua +++ b/sys/modules/opus/fs/linkfs.lua @@ -4,16 +4,20 @@ local linkfs = { } -- TODO: implement broken links -local methods = { 'exists', 'getFreeSpace', 'getSize', +local methods = { 'exists', 'getFreeSpace', 'getSize', 'attributes', 'isDir', 'isReadOnly', 'list', 'listEx', 'makeDir', 'open', 'getDrive' } for _,m in pairs(methods) do linkfs[m] = function(node, dir, ...) - dir = dir:gsub(node.mountPoint, node.source, 1) + dir = linkfs.resolve(node, dir) return fs[m](dir, ...) end end +function linkfs.resolve(node, dir) + return dir:gsub(node.mountPoint, node.source, 1) +end + function linkfs.mount(_, source) if not source then error('Source is required') diff --git a/sys/modules/opus/fs/netfs.lua b/sys/modules/opus/fs/netfs.lua index e36a1cc..5fed98d 100644 --- a/sys/modules/opus/fs/netfs.lua +++ b/sys/modules/opus/fs/netfs.lua @@ -6,7 +6,6 @@ local fs = _G.fs local netfs = { } local function remoteCommand(node, msg) - for _ = 1, 2 do if not node.socket then node.socket = Socket.connect(node.id, 139) @@ -33,17 +32,17 @@ local function remoteCommand(node, msg) error('netfs: Connection failed', 2) end -local methods = { 'delete', 'exists', 'getFreeSpace', 'makeDir', 'list', 'listEx' } +local methods = { 'delete', 'exists', 'getFreeSpace', 'makeDir', 'list', 'listEx', 'attributes' } -local function resolveDir(dir, node) +local function resolve(node, dir) -- TODO: Wrong ! (does not support names with dashes) dir = dir:gsub(node.mountPoint, '', 1) - return fs.combine(node.directory, dir) + return fs.combine(node.source, dir) end for _,m in pairs(methods) do netfs[m] = function(node, dir) - dir = resolveDir(dir, node) + dir = resolve(node, dir) return remoteCommand(node, { fn = m, @@ -52,14 +51,14 @@ for _,m in pairs(methods) do end end -function netfs.mount(_, id, directory) +function netfs.mount(_, id, source) if not id or not tonumber(id) then error('ramfs syntax: computerId [directory]') end return { id = tonumber(id), nodes = { }, - directory = directory or '', + source = source or '', } end @@ -68,7 +67,7 @@ function netfs.getDrive() end function netfs.complete(node, partial, dir, includeFiles, includeSlash) - dir = resolveDir(dir, node) + dir = resolve(node, dir) return remoteCommand(node, { fn = 'complete', @@ -77,8 +76,8 @@ function netfs.complete(node, partial, dir, includeFiles, includeSlash) end function netfs.copy(node, s, t) - s = resolveDir(s, node) - t = resolveDir(t, node) + s = resolve(node, s) + t = resolve(node, t) return remoteCommand(node, { fn = 'copy', @@ -87,37 +86,37 @@ function netfs.copy(node, s, t) end function netfs.isDir(node, dir) - if dir == node.mountPoint and node.directory == '' then + if dir == node.mountPoint and node.source == '' then return true end return remoteCommand(node, { fn = 'isDir', - args = { resolveDir(dir, node) }, + args = { resolve(node, dir) }, }) end function netfs.isReadOnly(node, dir) - if dir == node.mountPoint and node.directory == '' then + if dir == node.mountPoint and node.source == '' then return false end return remoteCommand(node, { fn = 'isReadOnly', - args = { resolveDir(dir, node) }, + args = { resolve(node, dir) }, }) end function netfs.getSize(node, dir) - if dir == node.mountPoint and node.directory == '' then + if dir == node.mountPoint and node.source == '' then return 0 end return remoteCommand(node, { fn = 'getSize', - args = { resolveDir(dir, node) }, + args = { resolve(node, dir) }, }) end function netfs.find(node, spec) - spec = resolveDir(spec, node) + spec = resolve(node, spec) local list = remoteCommand(node, { fn = 'find', args = { spec }, @@ -131,8 +130,8 @@ function netfs.find(node, spec) end function netfs.move(node, s, t) - s = resolveDir(s, node) - t = resolveDir(t, node) + s = resolve(node, s) + t = resolve(node, t) return remoteCommand(node, { fn = 'move', @@ -141,7 +140,7 @@ function netfs.move(node, s, t) end function netfs.open(node, fn, fl) - fn = resolveDir(fn, node) + fn = resolve(node, fn) local vfh = remoteCommand(node, { fn = 'open', diff --git a/sys/modules/opus/fs/ramfs.lua b/sys/modules/opus/fs/ramfs.lua index a516d80..9681b0d 100644 --- a/sys/modules/opus/fs/ramfs.lua +++ b/sys/modules/opus/fs/ramfs.lua @@ -9,15 +9,28 @@ function ramfs.mount(_, nodeType) return { nodes = { }, size = 0, + created = os.epoch('utc'), + modification = os.epoch('utc'), } elseif nodeType == 'file' then return { size = 0, + created = os.epoch('utc'), + modification = os.epoch('utc'), } end error('ramfs syntax: [directory, file]') end +function ramfs.attributes(node) + return { + created = node.created, + isDir = not not node.nodes, + modification = node.modification, + size = node.size, + } +end + function ramfs.delete(node, dir) if node.mountPoint == dir then fs.unmount(node.mountPoint) diff --git a/sys/modules/opus/fs/urlfs.lua b/sys/modules/opus/fs/urlfs.lua index 3595cb3..96b7854 100644 --- a/sys/modules/opus/fs/urlfs.lua +++ b/sys/modules/opus/fs/urlfs.lua @@ -11,6 +11,17 @@ function urlfs.mount(_, url) end return { url = url, + created = os.epoch('utc'), + modification = os.epoch('utc'), + } +end + +function urlfs.attributes(node) + return { + created = node.created, + isDir = false, + modification = node.modification, + size = node.size or 0, } end diff --git a/sys/modules/opus/socket.lua b/sys/modules/opus/socket.lua index 89afa4b..8aad0c9 100644 --- a/sys/modules/opus/socket.lua +++ b/sys/modules/opus/socket.lua @@ -6,12 +6,11 @@ local Util = require('opus.util') local device = _G.device local os = _G.os -local network = _G.network local socketClass = { } function socketClass:read(timeout) - local data, distance = network.getTransport().read(self) + local data, distance = _G.network.getTransport().read(self) if data then return data, distance end @@ -26,7 +25,7 @@ function socketClass:read(timeout) local e, id = os.pullEvent() if e == 'transport_' .. self.uid then - data, distance = network.getTransport().read(self) + data, distance = _G.network.getTransport().read(self) if data then os.cancelTimer(timerId) return data, distance @@ -47,7 +46,7 @@ end function socketClass:write(data) if self.connected then - network.getTransport().write(self, { + _G.network.getTransport().write(self, { type = 'DATA', seq = self.wseq, data = data, @@ -58,7 +57,7 @@ end function socketClass:ping() if self.connected then - network.getTransport().ping(self) + _G.network.getTransport().ping(self) return true end end @@ -72,7 +71,7 @@ function socketClass:close() self.connected = false end device.wireless_modem.close(self.sport) - network.getTransport().close(self) + _G.network.getTransport().close(self) end local Socket = { } @@ -127,9 +126,9 @@ function Socket.connect(host, port, options) local socket = newSocket(host == os.getComputerID()) socket.dhost = tonumber(host) if options and options.keypair then - socket.privKey, socket.pubKey = unpack(options.keypair) + socket.privKey, socket.pubKey = table.unpack(options.keypair) else - socket.privKey, socket.pubKey = network.getKeyPair() + socket.privKey, socket.pubKey = _G.network.getKeyPair() end local identifier = options and options.identifier or Security.getIdentifier() @@ -159,7 +158,7 @@ function Socket.connect(host, port, options) socket.remotePubKey = Util.hexToByteArray(msg.pk) socket.options = msg.options or { } setupCrypto(socket, true) - network.getTransport().open(socket) + _G.network.getTransport().open(socket) return socket elseif msg.type == 'NOPASS' then @@ -192,7 +191,7 @@ local function trusted(socket, msg, options) if data and data.ts and tonumber(data.ts) then if math.abs(os.epoch('utc') - data.ts) < 4096 then socket.remotePubKey = Util.hexToByteArray(data.pk) - socket.privKey, socket.pubKey = network.getKeyPair() + socket.privKey, socket.pubKey = _G.network.getKeyPair() setupCrypto(socket) return true end @@ -241,7 +240,7 @@ function Socket.server(port, options) options = socket.options.ENCRYPT and { ENCRYPT = true }, }) - network.getTransport().open(socket) + _G.network.getTransport().open(socket) return socket else -- 2.49.1 From 287adb123551bb63c49ee2c1f061838de0bf1c14 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 18:51:36 -0600 Subject: [PATCH 123/236] auto upgrade packages on base opus update - github actions wip --- .github/workflows/main.yml | 33 +++++++++++++++++++ sys/apps/fileui.lua | 2 +- sys/apps/package.lua | 12 ++++--- sys/apps/system/theme.lua | 2 +- sys/autorun/upgraded.lua | 29 ++++------------ sys/help/{CloudCatcher => CloudCatcher.txt} | 0 ...pus-Applications => Opus-Applications.txt} | 3 +- sys/help/{Opus => Opus.txt} | 0 sys/help/{Overview => Overview.txt} | 0 sys/help/{pastebin => pastebin.txt} | 0 sys/modules/opus/entry.lua | 4 +-- sys/modules/opus/ui.lua | 31 +++++++++-------- sys/modules/opus/ui/components/Chooser.lua | 8 +++-- 13 files changed, 77 insertions(+), 47 deletions(-) create mode 100644 .github/workflows/main.yml rename sys/help/{CloudCatcher => CloudCatcher.txt} (100%) rename sys/help/{Opus-Applications => Opus-Applications.txt} (84%) rename sys/help/{Opus => Opus.txt} (100%) rename sys/help/{Overview => Overview.txt} (100%) rename sys/help/{pastebin => pastebin.txt} (100%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..9515431 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,33 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the action will run. Triggers the workflow on push or pull request +# events but only for the master branch +on: + push: + branches: [ develop-1.8 ] + pull_request: + branches: [ develop-1.8 ] + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Runs a single command using the runners shell + - name: Run a one-line script + run: ls + + # Runs a set of commands using the runners shell + - name: Run a multi-line script + run: | + date + echo test, and deploy your project. diff --git a/sys/apps/fileui.lua b/sys/apps/fileui.lua index 1f95f5e..61354bb 100644 --- a/sys/apps/fileui.lua +++ b/sys/apps/fileui.lua @@ -17,7 +17,7 @@ local page = UI.Page { UI:quit() end - return UI.FileSelect.eventHandler(self, event) + return UI.Page.eventHandler(self, event) end, } diff --git a/sys/apps/package.lua b/sys/apps/package.lua index 324d321..8177fbd 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -16,9 +16,8 @@ local function makeSandbox() end local function Syntax(msg) - _G.printError(msg) - print('\nSyntax: Package list | install [name] ... | update [name] | uninstall [name]') - error(0) + print('Syntax: package list | install [name] ... | update [name] | updateall | uninstall [name]\n') + error(msg) end local function progress(max) @@ -76,6 +75,11 @@ local function install(name, isUpdate, ignoreDeps) local packageDir = fs.combine('packages', name) local list = Git.list(manifest.repository) + -- clear out contents before install/update + -- TODO: figure out whether to run + -- install/uninstall for the package + fs.delete(packageDir) + local showProgress = progress(Util.size(list)) local getList = { } @@ -151,7 +155,7 @@ if action == 'uninstall' then runScript(manifest.uninstall) local packageDir = fs.combine('packages', name) - fs.delete(fs.resolve(packageDir)) + fs.delete(packageDir) print('removed: ' .. packageDir) return end diff --git a/sys/apps/system/theme.lua b/sys/apps/system/theme.lua index 0df173c..8eee483 100644 --- a/sys/apps/system/theme.lua +++ b/sys/apps/system/theme.lua @@ -12,7 +12,7 @@ for k,v in pairs(colors) do end local allSettings = { } -for k,v in pairs(UI.colors) do +for k,v in pairs(UI.theme.colors) do allSettings[k] = { name = k, value = v } end diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua index cc1063d..67d3da5 100644 --- a/sys/autorun/upgraded.lua +++ b/sys/autorun/upgraded.lua @@ -1,24 +1,9 @@ -local fs = _G.fs +local fs = _G.fs +local shell = _ENV.shell -local function deleteIfExists(path) - if fs.exists(path) then - fs.delete(path) - print("Deleted outdated file at: "..path) - end +if fs.exists('.opus_upgrade') then + fs.delete('.opus_upgrade') + print('Updating packages') + shell.run('package updateall') + os.reboot() end --- cleanup outdated files -deleteIfExists('sys/apps/shell') -deleteIfExists('sys/etc/app.db') -deleteIfExists('sys/extensions') -deleteIfExists('sys/network') -deleteIfExists('startup') -deleteIfExists('sys/apps/system/turtle.lua') -deleteIfExists('sys/autorun/gps.lua') -deleteIfExists('sys/autorun/gpshost.lua') -deleteIfExists('sys/apps/network/redserver.lua') -deleteIfExists('sys/apis') -deleteIfExists('sys/autorun/apps.lua') -deleteIfExists('sys/init/6.tl3.lua') - --- remove this file --- deleteIfExists('sys/autorun/upgraded.lua') \ No newline at end of file diff --git a/sys/help/CloudCatcher b/sys/help/CloudCatcher.txt similarity index 100% rename from sys/help/CloudCatcher rename to sys/help/CloudCatcher.txt diff --git a/sys/help/Opus-Applications b/sys/help/Opus-Applications.txt similarity index 84% rename from sys/help/Opus-Applications rename to sys/help/Opus-Applications.txt index a7747a3..01cfb5e 100644 --- a/sys/help/Opus-Applications +++ b/sys/help/Opus-Applications.txt @@ -7,9 +7,10 @@ Shell usage: > package list > package install > package update +> package updateall > package uninstall -Package definitions are located in usr/apps/packages. This file can be modified to add custom packages. +Package definitions are located in usr/config/packages. This file can be modified to add custom packages. Current stable packages ======================= diff --git a/sys/help/Opus b/sys/help/Opus.txt similarity index 100% rename from sys/help/Opus rename to sys/help/Opus.txt diff --git a/sys/help/Overview b/sys/help/Overview.txt similarity index 100% rename from sys/help/Overview rename to sys/help/Overview.txt diff --git a/sys/help/pastebin b/sys/help/pastebin.txt similarity index 100% rename from sys/help/pastebin rename to sys/help/pastebin.txt diff --git a/sys/modules/opus/entry.lua b/sys/modules/opus/entry.lua index 0dc3536..e15f853 100644 --- a/sys/modules/opus/entry.lua +++ b/sys/modules/opus/entry.lua @@ -47,13 +47,13 @@ function Entry:updateScroll() self.scroll = 0 -- ?? end if self.pos - self.scroll > self.width then - self.scroll = self.pos - self.width + self.scroll = math.max(0, self.pos - self.width) elseif self.pos < self.scroll then self.scroll = self.pos end if self.scroll > 0 then if self.scroll + self.width > len then - self.scroll = len - self.width + self.scroll = math.max(0, len - self.width) end end if ps ~= self.scroll then diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index f64a77f..4f4efb5 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -37,13 +37,14 @@ local textutils = _G.textutils local UI = { } function UI:init() self.devices = { } - self.theme = { } - self.extChars = Util.getVersion() >= 1.76 - self.colors = { - primary = colors.green, - secondary = colors.lightGray, - tertiary = colors.gray, + self.theme = { + colors = { + primary = colors.green, + secondary = colors.lightGray, + tertiary = colors.gray, + } } + self.extChars = Util.getVersion() >= 1.76 local function keyFunction(event, code, held) local ie = Input:translate(event, code, held) @@ -206,6 +207,10 @@ function UI:loadTheme(filename) end Util.deepMerge(self.theme, theme) end + for k,v in pairs(self.theme.colors) do + Canvas.colorPalette[k] = Canvas.colorPalette[v] + Canvas.grayscalePalette[k] = Canvas.grayscalePalette[v] + end end function UI:generateTheme(filename) @@ -920,6 +925,13 @@ function UI.Window:addTransition(effect, args, canvas) self.parent:addTransition(effect, args, canvas or self) end +UI.Window.docs.emit = [[emit(TABLE event) +Send an event to the element. The event handler for the element is called. +If the event handler returns true, then no further processing is done. +If the event handler does not return true, then the event is sent to the parent element +and continues up the element tree. +If an accelerator is defined, the accelerated event is processed in the same manner. +Accelerators are useful for making events unique.]] function UI.Window:emit(event) local parent = self while parent do @@ -1118,13 +1130,6 @@ end loadComponents() UI:loadTheme('usr/config/ui.theme') -Util.merge(UI.Window.defaults, UI.theme.Window) -Util.merge(UI.colors, UI.theme.colors) UI:setDefaultDevice(UI.Device()) -for k,v in pairs(UI.colors) do - Canvas.colorPalette[k] = Canvas.colorPalette[v] - Canvas.grayscalePalette[k] = Canvas.grayscalePalette[v] -end - return UI diff --git a/sys/modules/opus/ui/components/Chooser.lua b/sys/modules/opus/ui/components/Chooser.lua index 6aa2e4a..49f882c 100644 --- a/sys/modules/opus/ui/components/Chooser.lua +++ b/sys/modules/opus/ui/components/Chooser.lua @@ -40,7 +40,7 @@ function UI.Chooser:draw() local value = choice and choice.name or self.nochoice self:write(1, 1, self.leftIndicator, self.backgroundColor, colors.black) - self:write(2, 1, ' ' .. Util.widthify(tostring(value), self.width-4) .. ' ', bg, fg) + self:write(2, 1, ' ' .. Util.widthify(tostring(value), self.width - 4) .. ' ', bg, fg) self:write(self.width, 1, self.rightIndicator, self.backgroundColor, colors.black) end @@ -54,7 +54,7 @@ function UI.Chooser:eventHandler(event) local choice if not k then k = 0 end if k and k < #self.choices then - choice = self.choices[k+1] + choice = self.choices[k + 1] else choice = self.choices[1] end @@ -62,11 +62,12 @@ function UI.Chooser:eventHandler(event) self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) self:draw() return true + elseif event.type == 'choice_prev' then local _,k = Util.find(self.choices, 'value', self.value) local choice if k and k > 1 then - choice = self.choices[k-1] + choice = self.choices[k - 1] else choice = self.choices[#self.choices] end @@ -74,6 +75,7 @@ function UI.Chooser:eventHandler(event) self:emit({ type = 'choice_change', value = self.value, element = self, choice = choice }) self:draw() return true + elseif event.type == 'mouse_click' or event.type == 'mouse_doubleclick' then if event.x == 1 then self:emit({ type = 'choice_prev' }) -- 2.49.1 From 1543f4d62417f51664951d67d6a3b5b09e2b8a1e Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 19:08:29 -0600 Subject: [PATCH 124/236] github actions test --- .github/workflows/main.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9515431..017607a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,10 +24,11 @@ jobs: # Runs a single command using the runners shell - name: Run a one-line script - run: ls + run: echo date > .opus_version - # Runs a set of commands using the runners shell - - name: Run a multi-line script - run: | - date - echo test, and deploy your project. + - name: Update version file + uses: alexesprit/action-update-file@master + with: + file-path: .opus_version + commit-msg: Update version date + github-token: ${{ secrets.GITHUB_TOKEN }} -- 2.49.1 From e721eb68b6f3b3b8c96eefda297c598971c2d23f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 20:22:44 -0600 Subject: [PATCH 125/236] update notification --- .github/workflows/main.yml | 3 ++- sys/autorun/upgraded.lua | 9 ------- sys/autorun/version.lua | 53 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) delete mode 100644 sys/autorun/upgraded.lua create mode 100644 sys/autorun/version.lua diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 017607a..f6d3458 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,11 +24,12 @@ jobs: # Runs a single command using the runners shell - name: Run a one-line script - run: echo date > .opus_version + run: echo `date` > .opus_version - name: Update version file uses: alexesprit/action-update-file@master with: + branch: develop-1.8 file-path: .opus_version commit-msg: Update version date github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/sys/autorun/upgraded.lua b/sys/autorun/upgraded.lua deleted file mode 100644 index 67d3da5..0000000 --- a/sys/autorun/upgraded.lua +++ /dev/null @@ -1,9 +0,0 @@ -local fs = _G.fs -local shell = _ENV.shell - -if fs.exists('.opus_upgrade') then - fs.delete('.opus_upgrade') - print('Updating packages') - shell.run('package updateall') - os.reboot() -end diff --git a/sys/autorun/version.lua b/sys/autorun/version.lua new file mode 100644 index 0000000..8acf87d --- /dev/null +++ b/sys/autorun/version.lua @@ -0,0 +1,53 @@ +local Config = require('opus.config') +local Util = require('opus.util') + +local fs = _G.fs +local shell = _ENV.shell + +local URL = 'https://raw.githubusercontent.com/kepler155c/opus/%s/.opus_version' + +local function notifyUpdate(config) + print('Opus has been updated to: ' .. config.current) + read() +end + +if fs.exists('.opus_version') then + local f = fs.open('.opus_version') + local date = f.readLine() + f.close() + + if type(date) == 'string' and #date > 0 then + local today = os.date('%j') + local config = Config.load('version', { + opus = date, + packages = date, + checked = today, + }) + + -- check if packages need an update + if config.opus ~= config.packages then + config.packages = config.opus + Config.update('version', config) + print('Updating packages') + shell.run('package updateall') + os.reboot() + end + + if config.checked ~= today then + config.checked = today + Config.update('version', config) + print('Checking for new version') + pcall(function() + local c = Util.httpGet(string.format(URL, _G.OPUS_BRANCH)) + if c then + c = Util.split(c)[1] + if config.opus ~= c and config.skip ~= c then + config.current = c + Config.update('version', config) + notifyUpdate(config) + end + end + end) + end + end +end -- 2.49.1 From 3368fa3266da64d1c50d876f2bee70d3a76ff2a3 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 21:27:07 -0600 Subject: [PATCH 126/236] update notification --- sys/apps/Version.lua | 48 +++++++++++++++++++++++++++++++++++++++++ sys/autorun/version.lua | 11 +++++----- 2 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 sys/apps/Version.lua diff --git a/sys/apps/Version.lua b/sys/apps/Version.lua new file mode 100644 index 0000000..55d196e --- /dev/null +++ b/sys/apps/Version.lua @@ -0,0 +1,48 @@ +local Config = require('opus.config') +local UI = require('opus.ui') + +local shell = _ENV.shell + +local config = Config.load('version') +if not config.current then + return +end + +UI:setPage(UI.Page { + UI.TextArea { + x = 2, y = 2, ey = -2, + value = 'A new version of Opus is available.' + }, + UI.Button { + x = 2, y = 5, width = 21, + event = 'skip', + text = 'Skip this version', + }, + UI.Button { + x = 2, y = 7, width = 21, + event = 'remind', + text = 'Remind me tomorrow', + }, + UI.Button { + x = 2, y = 9, width = 21, + event = 'update', + text = 'Update' + }, + eventHandler = function(self, event) + if event.type == 'skip' then + config.skip = config.current + Config.update('version', config) + UI:quit() + + elseif event.type == 'remind' then + UI:quit() + + elseif event.type == 'update' then + shell.openForegroundTab('update update') + UI:quit() + end + return UI.Page.eventHandler(self, event) + end, +}) + +UI:start() diff --git a/sys/autorun/version.lua b/sys/autorun/version.lua index 8acf87d..f551f4a 100644 --- a/sys/autorun/version.lua +++ b/sys/autorun/version.lua @@ -6,15 +6,11 @@ local shell = _ENV.shell local URL = 'https://raw.githubusercontent.com/kepler155c/opus/%s/.opus_version' -local function notifyUpdate(config) - print('Opus has been updated to: ' .. config.current) - read() -end - if fs.exists('.opus_version') then local f = fs.open('.opus_version') local date = f.readLine() f.close() + date = type(date) == 'string' and Util.split(date)[1] if type(date) == 'string' and #date > 0 then local today = os.date('%j') @@ -44,7 +40,10 @@ if fs.exists('.opus_version') then if config.opus ~= c and config.skip ~= c then config.current = c Config.update('version', config) - notifyUpdate(config) + print('New version available') + if _ENV.multishell then + shell.openForegroundTab('Version') + end end end end) -- 2.49.1 From 55f444c37eabd39e4def3b1c5e3f7274149c848d Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 21:38:38 -0600 Subject: [PATCH 127/236] github actions test --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f6d3458..81423f2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: - name: Update version file uses: alexesprit/action-update-file@master with: - branch: develop-1.8 file-path: .opus_version commit-msg: Update version date github-token: ${{ secrets.GITHUB_TOKEN }} + branch: develop-1.8 -- 2.49.1 From 0188e886c0fa4ae3da55cfe96f4a90991fc1f5bc Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 21:48:55 -0600 Subject: [PATCH 128/236] github actions test --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 81423f2..f6b3c81 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,13 +23,13 @@ jobs: - uses: actions/checkout@v2 # Runs a single command using the runners shell - - name: Run a one-line script + - name: Generate version file run: echo `date` > .opus_version - name: Update version file - uses: alexesprit/action-update-file@master + uses: github-actions-x/commit@v2.6 with: - file-path: .opus_version - commit-msg: Update version date github-token: ${{ secrets.GITHUB_TOKEN }} - branch: develop-1.8 + push-branch: 'develop-1.8' + commit-message: 'Update version file' + files: .opus_version -- 2.49.1 From c5e0ac0af6ba3fd58b88042c509930977bcd52da Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 21:56:44 -0600 Subject: [PATCH 129/236] github actions test --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f6b3c81..be4a900 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,13 +23,13 @@ jobs: - uses: actions/checkout@v2 # Runs a single command using the runners shell - - name: Generate version file + - name: Run a one-line script run: echo `date` > .opus_version - name: Update version file - uses: github-actions-x/commit@v2.6 + uses: alexesprit/action-update-file@master with: + branch: 'develop-1.8' + file-path: .opus_version + commit-msg: Update version date github-token: ${{ secrets.GITHUB_TOKEN }} - push-branch: 'develop-1.8' - commit-message: 'Update version file' - files: .opus_version -- 2.49.1 From 98f4bf7a7e9519609fe373cd2039e5c3aad71276 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 03:57:20 +0000 Subject: [PATCH 130/236] Update version date --- .opus_version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .opus_version diff --git a/.opus_version b/.opus_version new file mode 100644 index 0000000..de1cc61 --- /dev/null +++ b/.opus_version @@ -0,0 +1 @@ +Fri May 1 03:57:19 UTC 2020 -- 2.49.1 From c622b2f7fbd434c9d718e6966e4cdaea9b576dd9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 22:05:23 -0600 Subject: [PATCH 131/236] update notification --- sys/autorun/version.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/autorun/version.lua b/sys/autorun/version.lua index f551f4a..5550092 100644 --- a/sys/autorun/version.lua +++ b/sys/autorun/version.lua @@ -7,7 +7,7 @@ local shell = _ENV.shell local URL = 'https://raw.githubusercontent.com/kepler155c/opus/%s/.opus_version' if fs.exists('.opus_version') then - local f = fs.open('.opus_version') + local f = fs.open('.opus_version', 'r') local date = f.readLine() f.close() date = type(date) == 'string' and Util.split(date)[1] -- 2.49.1 From cb55b1daab3d3d9c6e3a8b83d00f8ee49dca6044 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 04:07:10 +0000 Subject: [PATCH 132/236] Update version date --- .opus_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.opus_version b/.opus_version index de1cc61..14b3ccb 100644 --- a/.opus_version +++ b/.opus_version @@ -1 +1 @@ -Fri May 1 03:57:19 UTC 2020 +Fri May 1 04:07:09 UTC 2020 -- 2.49.1 From fae759618271180c00c5f4f503fab897a849bca1 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 30 Apr 2020 22:13:03 -0600 Subject: [PATCH 133/236] update notification --- sys/autorun/version.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/autorun/version.lua b/sys/autorun/version.lua index 5550092..304fe0d 100644 --- a/sys/autorun/version.lua +++ b/sys/autorun/version.lua @@ -42,7 +42,7 @@ if fs.exists('.opus_version') then Config.update('version', config) print('New version available') if _ENV.multishell then - shell.openForegroundTab('Version') + shell.openForegroundTab('sys/apps/Version.lua') end end end -- 2.49.1 From c1cbcf6b61e119298a0b547bdde51c9b8a6a032b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 May 2020 04:14:02 +0000 Subject: [PATCH 134/236] Update version date --- .opus_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.opus_version b/.opus_version index 14b3ccb..7e66a11 100644 --- a/.opus_version +++ b/.opus_version @@ -1 +1 @@ -Fri May 1 04:07:09 UTC 2020 +Fri May 1 04:14:02 UTC 2020 -- 2.49.1 From 9c97849cff802ca59fadf6767fd0f5d202b4a07f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 1 May 2020 22:26:15 -0600 Subject: [PATCH 135/236] more work on update notification --- .github/workflows/main.yml | 9 +++++---- sys/apps/Version.lua | 16 +++++++++++----- sys/autorun/version.lua | 8 +++++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index be4a900..697ed1d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,11 +22,12 @@ jobs: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 - # Runs a single command using the runners shell - - name: Run a one-line script - run: echo `date` > .opus_version + - name: Create version file + run: | + echo `date` > .opus_version + git log -8 --pretty=format:'%s (%cr)' >> .opus_version - - name: Update version file + - name: Commit version file uses: alexesprit/action-update-file@master with: branch: 'develop-1.8' diff --git a/sys/apps/Version.lua b/sys/apps/Version.lua index 55d196e..3d83ac4 100644 --- a/sys/apps/Version.lua +++ b/sys/apps/Version.lua @@ -9,22 +9,28 @@ if not config.current then end UI:setPage(UI.Page { + UI.Text { + x = 2, y = 2, ex = -2, + align = 'center', + value = 'Opus has been updated.', + textColor = 'yellow', + }, UI.TextArea { - x = 2, y = 2, ey = -2, - value = 'A new version of Opus is available.' + x = 2, y = 4, ey = -8, + text = config.details, }, UI.Button { - x = 2, y = 5, width = 21, + x = 2, y = -6, width = 21, event = 'skip', text = 'Skip this version', }, UI.Button { - x = 2, y = 7, width = 21, + x = 2, y = -4, width = 21, event = 'remind', text = 'Remind me tomorrow', }, UI.Button { - x = 2, y = 9, width = 21, + x = 2, y = -2, width = 21, event = 'update', text = 'Update' }, diff --git a/sys/autorun/version.lua b/sys/autorun/version.lua index 304fe0d..ef3d5a6 100644 --- a/sys/autorun/version.lua +++ b/sys/autorun/version.lua @@ -36,9 +36,11 @@ if fs.exists('.opus_version') then pcall(function() local c = Util.httpGet(string.format(URL, _G.OPUS_BRANCH)) if c then - c = Util.split(c)[1] - if config.opus ~= c and config.skip ~= c then - config.current = c + local lines = Util.split(c) + local revdate = table.remove(lines, 1) + if config.opus ~= revdate and config.skip ~= revdate then + config.current = revdate + config.details = table.concat(lines, '\n') Config.update('version', config) print('New version available') if _ENV.multishell then -- 2.49.1 From 74d3d1df335020571a287587d577489d74c7f1cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 2 May 2020 04:26:51 +0000 Subject: [PATCH 136/236] Update version date --- .opus_version | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.opus_version b/.opus_version index 7e66a11..c1ca6e5 100644 --- a/.opus_version +++ b/.opus_version @@ -1 +1,2 @@ -Fri May 1 04:14:02 UTC 2020 +Sat May 2 04:26:50 UTC 2020 +more work on update notification (35 seconds ago) \ No newline at end of file -- 2.49.1 From 3cbdf7b97b8780f3d25ae496dfc37c7536f2ea7f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 1 May 2020 22:40:17 -0600 Subject: [PATCH 137/236] more work on update notification --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 697ed1d..837dfc7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: - name: Create version file run: | echo `date` > .opus_version - git log -8 --pretty=format:'%s (%cr)' >> .opus_version + git log >> .opus_version - name: Commit version file uses: alexesprit/action-update-file@master -- 2.49.1 From 4a46d673044998a995ffa402b821b6e92c9a3f5f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 2 May 2020 04:40:52 +0000 Subject: [PATCH 138/236] Update version date --- .opus_version | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.opus_version b/.opus_version index c1ca6e5..850bdb8 100644 --- a/.opus_version +++ b/.opus_version @@ -1,2 +1,6 @@ -Sat May 2 04:26:50 UTC 2020 -more work on update notification (35 seconds ago) \ No newline at end of file +Sat May 2 04:40:52 UTC 2020 +commit 3cbdf7b97b8780f3d25ae496dfc37c7536f2ea7f +Author: kepler155c@gmail.com +Date: Fri May 1 22:40:17 2020 -0600 + + more work on update notification -- 2.49.1 From 7f92286fb1e3947edb65c5578852a80bd79adeb7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 1 May 2020 23:09:46 -0600 Subject: [PATCH 139/236] more work on update notification --- sys/autorun/version.lua | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/sys/autorun/version.lua b/sys/autorun/version.lua index ef3d5a6..7c5e5b2 100644 --- a/sys/autorun/version.lua +++ b/sys/autorun/version.lua @@ -12,23 +12,22 @@ if fs.exists('.opus_version') then f.close() date = type(date) == 'string' and Util.split(date)[1] + local today = os.date('%j') + local config = Config.load('version', { + packages = date, + checked = today, + }) + + -- check if packages need an update + if date ~= config.packages then + config.packages = date + Config.update('version', config) + print('Updating packages') + shell.run('package updateall') + os.reboot() + end + if type(date) == 'string' and #date > 0 then - local today = os.date('%j') - local config = Config.load('version', { - opus = date, - packages = date, - checked = today, - }) - - -- check if packages need an update - if config.opus ~= config.packages then - config.packages = config.opus - Config.update('version', config) - print('Updating packages') - shell.run('package updateall') - os.reboot() - end - if config.checked ~= today then config.checked = today Config.update('version', config) @@ -38,7 +37,7 @@ if fs.exists('.opus_version') then if c then local lines = Util.split(c) local revdate = table.remove(lines, 1) - if config.opus ~= revdate and config.skip ~= revdate then + if date ~= revdate and config.skip ~= revdate then config.current = revdate config.details = table.concat(lines, '\n') Config.update('version', config) -- 2.49.1 From 3488da649e0d20d94bfba250c70470093bccf245 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 2 May 2020 05:10:19 +0000 Subject: [PATCH 140/236] Update version date --- .opus_version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.opus_version b/.opus_version index 850bdb8..bdb9adb 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat May 2 04:40:52 UTC 2020 -commit 3cbdf7b97b8780f3d25ae496dfc37c7536f2ea7f +Sat May 2 05:10:18 UTC 2020 +commit 7f92286fb1e3947edb65c5578852a80bd79adeb7 Author: kepler155c@gmail.com -Date: Fri May 1 22:40:17 2020 -0600 +Date: Fri May 1 23:09:46 2020 -0600 more work on update notification -- 2.49.1 From 5afb21b68ec1ddb9d79393277a586ceda9abbbb5 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 1 May 2020 23:16:10 -0600 Subject: [PATCH 141/236] more work on update notification --- sys/apps/Version.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/apps/Version.lua b/sys/apps/Version.lua index 3d83ac4..7a17499 100644 --- a/sys/apps/Version.lua +++ b/sys/apps/Version.lua @@ -17,7 +17,7 @@ UI:setPage(UI.Page { }, UI.TextArea { x = 2, y = 4, ey = -8, - text = config.details, + value = config.details, }, UI.Button { x = 2, y = -6, width = 21, -- 2.49.1 From 447f4daa927455b2c02c00b66e9c18ad045c3c12 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 2 May 2020 05:16:44 +0000 Subject: [PATCH 142/236] Update version date --- .opus_version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.opus_version b/.opus_version index bdb9adb..e6f16a5 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat May 2 05:10:18 UTC 2020 -commit 7f92286fb1e3947edb65c5578852a80bd79adeb7 +Sat May 2 05:16:44 UTC 2020 +commit 5afb21b68ec1ddb9d79393277a586ceda9abbbb5 Author: kepler155c@gmail.com -Date: Fri May 1 23:09:46 2020 -0600 +Date: Fri May 1 23:16:10 2020 -0600 more work on update notification -- 2.49.1 From e2ba9e2a032f4de832dc8e97f5b900ccb261b8e0 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 4 May 2020 03:55:55 -0400 Subject: [PATCH 143/236] Fix bug in CheckboxGrid --- sys/modules/opus/ui/components/CheckboxGrid.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sys/modules/opus/ui/components/CheckboxGrid.lua b/sys/modules/opus/ui/components/CheckboxGrid.lua index c57c098..0e6a62d 100644 --- a/sys/modules/opus/ui/components/CheckboxGrid.lua +++ b/sys/modules/opus/ui/components/CheckboxGrid.lua @@ -11,11 +11,12 @@ end UI.CheckboxGrid = class(UI.Grid) UI.CheckboxGrid.defaults = { - UIElement = 'CheckboxGrid', - checkedKey = 'checked', - accelerators = { - space = 'grid_toggle', - }, + UIElement = 'CheckboxGrid', + checkedKey = 'checked', + accelerators = { + space = 'grid_toggle', + key_enter = 'grid_toggle', + }, } function UI.CheckboxGrid:drawRow(sb, row, focused, bg, fg) local ind = focused and self.focusIndicator or ' ' @@ -31,7 +32,7 @@ function UI.CheckboxGrid:drawRow(sb, row, focused, bg, fg) end function UI.CheckboxGrid:eventHandler(event) - if event.type == 'key_enter' and self.selected then + if event.type == 'grid_toggle' and self.selected then self.selected.checked = not self.selected.checked self:draw() self:emit({ type = 'grid_check', checked = self.selected, element = self }) -- 2.49.1 From d7f41f2bce51bd481b24bc6614b43af42f486f11 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 4 May 2020 16:41:35 -0600 Subject: [PATCH 144/236] multishell/kernel support for windows on different devices --- sys/init/7.multishell.lua | 48 ++++++++++++++++++++++----------------- sys/kernel.lua | 21 ++++++++++++----- sys/modules/opus/ui.lua | 32 ++++++++++++-------------- 3 files changed, 56 insertions(+), 45 deletions(-) diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index 1f44a0d..4b581bd 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -296,40 +296,46 @@ kernel.hook('term_resize', function(_, eventData) end) kernel.hook('mouse_click', function(_, eventData) - local x, y = eventData[2], eventData[3] + if not eventData[4] then + local x, y = eventData[2], eventData[3] - if y == 1 then - if x == 1 then - multishell.setFocus(overviewId) - elseif x == w then - local currentTab = kernel.getFocused() - if currentTab then - multishell.terminate(currentTab.uid) - end - else - for _,tab in pairs(kernel.routines) do - if not tab.hidden and tab.sx then - if x >= tab.sx and x <= tab.ex then - multishell.setFocus(tab.uid) - break + if y == 1 then + if x == 1 then + multishell.setFocus(overviewId) + elseif x == w then + local currentTab = kernel.getFocused() + if currentTab then + multishell.terminate(currentTab.uid) + end + else + for _,tab in pairs(kernel.routines) do + if not tab.hidden and tab.sx then + if x >= tab.sx and x <= tab.ex then + multishell.setFocus(tab.uid) + break + end end end end + return true end - return true + eventData[3] = eventData[3] - 1 end - eventData[3] = eventData[3] - 1 end) kernel.hook({ 'mouse_up', 'mouse_drag' }, function(_, eventData) - eventData[3] = eventData[3] - 1 + if not eventData[4] then + eventData[3] = eventData[3] - 1 + end end) kernel.hook('mouse_scroll', function(_, eventData) - if eventData[3] == 1 then - return true + if not eventData[4] then + if eventData[3] == 1 then + return true + end + eventData[3] = eventData[3] - 1 end - eventData[3] = eventData[3] - 1 end) kernel.hook('kernel_ready', function() diff --git a/sys/kernel.lua b/sys/kernel.lua index 546300d..ea4233c 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -50,13 +50,19 @@ function kernel.hook(event, fn) end end --- you can only unhook from within the function that hooked +-- you *should* only unhook from within the function that hooked function kernel.unhook(event, fn) - local eventHooks = kernel.hooks[event] - if eventHooks then - Array.removeByValue(eventHooks, fn) - if #eventHooks == 0 then - kernel.hooks[event] = nil + if type(event) == 'table' then + for _,v in pairs(event) do + kernel.unhook(v, fn) + end + else + local eventHooks = kernel.hooks[event] + if eventHooks then + Array.removeByValue(eventHooks, fn) + if #eventHooks == 0 then + kernel.hooks[event] = nil + end end end end @@ -107,6 +113,9 @@ function Routine:resume(event, ...) end if coroutine.status(self.co) == 'dead' then Array.removeByValue(kernel.routines, self) + if self.onDestroy then + self.onDestroy(self) + end if #kernel.routines > 0 then switch(kernel.routines[1]) end diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 4f4efb5..36afdc2 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -75,11 +75,11 @@ function UI:init() term_resize = resize, monitor_resize = resize, - mouse_scroll = function(_, direction, x, y) + mouse_scroll = function(_, direction, x, y, side) local ie = Input:translate('mouse_scroll', direction, x, y) local currentPage = self:getActivePage() - if currentPage then + if currentPage and currentPage.parent.device.side == side then local event = currentPage:pointToChild(x, y) event.type = ie.code event.ie = { code = ie.code, x = event.x, y = event.y } @@ -97,23 +97,21 @@ function UI:init() end end, - mouse_click = function(_, button, x, y) + mouse_click = function(_, button, x, y, side) local ie = Input:translate('mouse_click', button, x, y) local currentPage = self:getActivePage() - if currentPage then - if not currentPage.parent.device.side then - local event = currentPage:pointToChild(x, y) - if event.element.focus and not event.element.inactive then - currentPage:setFocus(event.element) - currentPage:sync() - end - self:click(currentPage, ie) + if currentPage and currentPage.parent.device.side == side then + local event = currentPage:pointToChild(x, y) + if event.element.focus and not event.element.inactive then + currentPage:setFocus(event.element) + currentPage:sync() end + self:click(currentPage, ie) end end, - mouse_up = function(_, button, x, y) + mouse_up = function(_, button, x, y, side) local ie = Input:translate('mouse_up', button, x, y) local currentPage = self:getActivePage() @@ -124,18 +122,16 @@ function UI:init() args = { event.element }, focused = true }) - elseif ie and currentPage then - if not currentPage.parent.device.side then - self:click(currentPage, ie) - end + elseif ie and currentPage and currentPage.parent.device.side == side then + self:click(currentPage, ie) end end, - mouse_drag = function(_, button, x, y) + mouse_drag = function(_, button, x, y, side) local ie = Input:translate('mouse_drag', button, x, y) local currentPage = self:getActivePage() - if ie and currentPage then + if ie and currentPage and currentPage.parent.device.side == side then self:click(currentPage, ie) end end, -- 2.49.1 From cdf8645c888c981a56f9f5e14f746aa57188c22c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 May 2020 22:45:09 +0000 Subject: [PATCH 145/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index e6f16a5..fe40c86 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat May 2 05:16:44 UTC 2020 -commit 5afb21b68ec1ddb9d79393277a586ceda9abbbb5 +Mon May 4 22:45:08 UTC 2020 +commit d7f41f2bce51bd481b24bc6614b43af42f486f11 Author: kepler155c@gmail.com -Date: Fri May 1 23:16:10 2020 -0600 +Date: Mon May 4 16:41:35 2020 -0600 - more work on update notification + multishell/kernel support for windows on different devices -- 2.49.1 From 3d022307429ca6b97e7d4683d932482da830d36b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 4 May 2020 21:06:23 -0600 Subject: [PATCH 146/236] major oops - fix poluted envs --- sys/apps/shell.lua | 8 +++++++- sys/kernel.lua | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 7036afa..91228fb 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -10,6 +10,10 @@ local sandboxEnv = setmetatable({ }, { __index = _G }) for k,v in pairs(_ENV) do sandboxEnv[k] = v end +sandboxEnv.package = nil +sandboxEnv.require = nil +sandboxEnv.arg = nil +sandboxEnv._ENV = nil sandboxEnv.shell = shell _G.requireInjector(_ENV) @@ -291,7 +295,9 @@ function shell.setEnv(name, value) end function shell.getEnv() - return sandboxEnv + local env = Util.shallowCopy(sandboxEnv) + _G.requireInjector(env) + return env end function shell.setAlias( _sCommand, _sProgram ) diff --git a/sys/kernel.lua b/sys/kernel.lua index ea4233c..4838892 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -151,7 +151,7 @@ function kernel.newRoutine(args) }, { __index = Routine }) Util.merge(routine, args) - routine.env = args.env or Util.shallowCopy(shell.getEnv()) + routine.env = args.env or shell.getEnv() return routine end -- 2.49.1 From 72560b1de00cc666069b7504143653342ffaad46 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 03:07:05 +0000 Subject: [PATCH 147/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index fe40c86..87e4bf6 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Mon May 4 22:45:08 UTC 2020 -commit d7f41f2bce51bd481b24bc6614b43af42f486f11 +Tue May 5 03:07:04 UTC 2020 +commit 3d022307429ca6b97e7d4683d932482da830d36b Author: kepler155c@gmail.com -Date: Mon May 4 16:41:35 2020 -0600 +Date: Mon May 4 21:06:23 2020 -0600 - multishell/kernel support for windows on different devices + major oops - fix poluted envs -- 2.49.1 From 4485dd2bd5079e94c2ce705e1325a8ffe3149c58 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Tue, 5 May 2020 02:45:42 -0400 Subject: [PATCH 148/236] Remove default limit in TextEntry --- sys/modules/opus/ui/components/TextEntry.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/sys/modules/opus/ui/components/TextEntry.lua b/sys/modules/opus/ui/components/TextEntry.lua index 9fd61a5..ca82b11 100644 --- a/sys/modules/opus/ui/components/TextEntry.lua +++ b/sys/modules/opus/ui/components/TextEntry.lua @@ -26,7 +26,6 @@ UI.TextEntry.defaults = { backgroundColor = 'black', backgroundFocusColor = 'black', height = 1, - limit = 6, cursorBlink = true, accelerators = { [ 'control-c' ] = 'copy', -- 2.49.1 From 4018d4bcfb71104b63a86eb7d9f277108275037e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 06:46:37 +0000 Subject: [PATCH 149/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 87e4bf6..553aa0e 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Tue May 5 03:07:04 UTC 2020 -commit 3d022307429ca6b97e7d4683d932482da830d36b -Author: kepler155c@gmail.com -Date: Mon May 4 21:06:23 2020 -0600 +Tue May 5 06:46:36 UTC 2020 +commit 4485dd2bd5079e94c2ce705e1325a8ffe3149c58 +Author: Anavrins +Date: Tue May 5 02:45:42 2020 -0400 - major oops - fix poluted envs + Remove default limit in TextEntry -- 2.49.1 From 3d3e5400cfd3d40853dc3fe6a6491232c8368524 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 5 May 2020 17:25:58 -0600 Subject: [PATCH 150/236] tidy up env creation - round 1 --- sys/apps/Lua.lua | 3 --- sys/apps/network/snmp.lua | 4 ++-- sys/apps/shell.lua | 27 +++++++++++---------------- sys/kernel.lua | 4 +++- sys/modules/opus/terminal.lua | 2 +- sys/modules/opus/ui.lua | 2 +- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 557573c..8371d06 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -1,6 +1,3 @@ --- Lua may be called from outside of shell - inject a require -_G.requireInjector(_ENV) - local History = require('opus.history') local UI = require('opus.ui') local Util = require('opus.util') diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index 0fc9a34..18a4143 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -31,7 +31,7 @@ local function snmpConnection(socket) socket:write('pong') elseif msg.type == 'script' then - local env = setmetatable(Util.shallowCopy(_ENV), { __index = _G }) + local env = kernel.makeEnv() local fn, err = load(msg.args, 'script', nil, env) if fn then kernel.run({ @@ -45,7 +45,7 @@ local function snmpConnection(socket) elseif msg.type == 'scriptEx' then local s, m = pcall(function() - local env = setmetatable(Util.shallowCopy(_ENV), { __index = _G }) + local env = kernel.makeEnv() local fn, m = load(msg.args, 'script', nil, env) if not fn then error(m) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 91228fb..bc3707f 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -6,7 +6,7 @@ local fs = _G.fs local settings = _G.settings local shell = _ENV.shell -local sandboxEnv = setmetatable({ }, { __index = _G }) +local sandboxEnv = { } for k,v in pairs(_ENV) do sandboxEnv[k] = v end @@ -47,10 +47,11 @@ local function tokenise( ... ) return tWords end -local function run(env, ...) +local function run(...) local args = tokenise(...) local command = table.remove(args, 1) or error('No such program') local isUrl = not not command:match("^(https?:)") + local env = shell.makeEnv() local path, loadFn if isUrl then @@ -92,10 +93,7 @@ function shell.run(...) oldTitle = _ENV.multishell.getTitle(_ENV.multishell.getCurrent()) end - local env = setmetatable(Util.shallowCopy(sandboxEnv), { __index = _G }) - _G.requireInjector(env) - - local r = { trace(run, env, ...) } + local r = { trace(run, ...) } if _ENV.multishell then _ENV.multishell.setTitle(_ENV.multishell.getCurrent(), oldTitle or 'shell') @@ -294,8 +292,8 @@ function shell.setEnv(name, value) sandboxEnv[name] = value end -function shell.getEnv() - local env = Util.shallowCopy(sandboxEnv) +function shell.makeEnv() + local env = setmetatable(Util.shallowCopy(sandboxEnv), { __index = _G }) _G.requireInjector(env) return env end @@ -323,7 +321,7 @@ function shell.newTab(tabInfo, ...) if path then tabInfo.path = path - tabInfo.env = Util.shallowCopy(sandboxEnv) + tabInfo.env = shell.makeEnv() tabInfo.args = args tabInfo.title = fs.getName(path):match('([^%.]+)') @@ -336,16 +334,16 @@ function shell.newTab(tabInfo, ...) return nil, 'No such program' end -function shell.openTab( ... ) +function shell.openTab(...) -- needs to use multishell.launch .. so we can run with stock multishell local tWords = tokenise( ... ) local sCommand = tWords[1] if sCommand then local sPath = shell.resolveProgram(sCommand) if sPath == "sys/apps/shell.lua" then - return _ENV.multishell.launch(Util.shallowCopy(sandboxEnv), sPath, table.unpack(tWords, 2)) + return _ENV.multishell.launch(shell.makeEnv(), sPath, table.unpack(tWords, 2)) else - return _ENV.multishell.launch(Util.shallowCopy(sandboxEnv), "sys/apps/shell.lua", sCommand, table.unpack(tWords, 2)) + return _ENV.multishell.launch(shell.makeEnv(), "sys/apps/shell.lua", sCommand, table.unpack(tWords, 2)) end end end @@ -364,10 +362,7 @@ end local tArgs = { ... } if #tArgs > 0 then - local env = setmetatable(Util.shallowCopy(sandboxEnv), { __index = _G }) - _G.requireInjector(env) - - return run(env, ...) + return run(...) end local Config = require('opus.config') diff --git a/sys/kernel.lua b/sys/kernel.lua index 4838892..b7ea08b 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -139,6 +139,8 @@ function kernel.getShell() return shell end +kernel.makeEnv = shell.makeEnv + function kernel.newRoutine(args) kernel.UID = kernel.UID + 1 @@ -151,7 +153,7 @@ function kernel.newRoutine(args) }, { __index = Routine }) Util.merge(routine, args) - routine.env = args.env or shell.getEnv() + routine.env = args.env or shell.makeEnv() return routine end diff --git a/sys/modules/opus/terminal.lua b/sys/modules/opus/terminal.lua index 7b04a42..7d3339c 100644 --- a/sys/modules/opus/terminal.lua +++ b/sys/modules/opus/terminal.lua @@ -36,7 +36,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) local maxScroll = 100 local cx, cy = 1, 1 local blink = false - local _bg, _fg = parent.getBackgroundColor(), parent.getTextColor() + local _bg, _fg = colors.black, colors.white win.canvas = Canvas({ x = sx, diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 36afdc2..fb67603 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -119,7 +119,7 @@ function UI:init() local event = currentPage:pointToChild(x, y) _ENV.multishell.openTab({ path = 'sys/apps/Lua.lua', - args = { event.element }, + args = { event.element, self, _ENV }, focused = true }) elseif ie and currentPage and currentPage.parent.device.side == side then -- 2.49.1 From 51724ccd782ee4330e37e2256fe61940b4f3fb42 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 May 2020 23:26:40 +0000 Subject: [PATCH 151/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 553aa0e..2b60fb6 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Tue May 5 06:46:36 UTC 2020 -commit 4485dd2bd5079e94c2ce705e1325a8ffe3149c58 -Author: Anavrins -Date: Tue May 5 02:45:42 2020 -0400 +Tue May 5 23:26:39 UTC 2020 +commit 3d3e5400cfd3d40853dc3fe6a6491232c8368524 +Author: kepler155c@gmail.com +Date: Tue May 5 17:25:58 2020 -0600 - Remove default limit in TextEntry + tidy up env creation - round 1 -- 2.49.1 From c24411717ae504aa93fda9d35dcd58d4dcf17473 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 6 May 2020 18:01:10 -0600 Subject: [PATCH 152/236] optionally show value for slider component - remove some unneeded limits for textEntries --- sys/apps/Files.lua | 1 - sys/apps/Lua.lua | 1 - sys/apps/system/aliases.lua | 1 - sys/apps/system/launcher.lua | 1 - sys/apps/system/path.lua | 1 - sys/apps/system/requires.lua | 1 - sys/apps/system/settings.lua | 1 - sys/kernel.lua | 2 +- sys/modules/opus/ui/components/FileSelect.lua | 1 - sys/modules/opus/ui/components/Slider.lua | 65 ++++++++++++------- 10 files changed, 42 insertions(+), 33 deletions(-) diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 65c806d..1529c6d 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -177,7 +177,6 @@ local Browser = UI.Page { formLabel = 'Program', formKey = 'value', shadowText = 'program', required = true, - limit = 128, }, add = UI.Button { x = -11, y = 1, diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 8371d06..2bcd8e5 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -29,7 +29,6 @@ local page = UI.Page { prompt = UI.TextEntry { y = 2, shadowText = 'enter command', - limit = 1024, accelerators = { enter = 'command_enter', up = 'history_back', diff --git a/sys/apps/system/aliases.lua b/sys/apps/system/aliases.lua index ede9349..20e1356 100644 --- a/sys/apps/system/aliases.lua +++ b/sys/apps/system/aliases.lua @@ -13,7 +13,6 @@ local aliasTab = UI.Tab { }, path = UI.TextEntry { y = 3, x = 2, ex = -2, - limit = 256, shadowText = 'Program path', accelerators = { enter = 'new_alias', diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index c3f8588..b714d9a 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -26,7 +26,6 @@ local tab = UI.Tab { }, custom = UI.TextEntry { x = 13, ex = -3, y = 4, - limit = 128, shadowText = 'File name', }, button = UI.Button { diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index 9550cb4..bef226f 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -11,7 +11,6 @@ local tab = UI.Tab { }, entry = UI.TextEntry { x = 3, y = 3, ex = -3, - limit = 256, shadowText = 'enter new path', accelerators = { enter = 'update_path', diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index 54d78d8..00da01b 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -8,7 +8,6 @@ local tab = UI.Tab { tabClose = true, entry = UI.TextEntry { x = 2, y = 2, ex = -2, - limit = 256, shadowText = 'Enter new require path', accelerators = { enter = 'update_path', diff --git a/sys/apps/system/settings.lua b/sys/apps/system/settings.lua index 6676910..cb12557 100644 --- a/sys/apps/system/settings.lua +++ b/sys/apps/system/settings.lua @@ -27,7 +27,6 @@ return settings and UI.Tab { form = UI.Form { y = 2, value = UI.TextEntry { - limit = 256, formIndex = 1, formLabel = 'Value', formKey = 'value', diff --git a/sys/kernel.lua b/sys/kernel.lua index b7ea08b..51cba75 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -114,7 +114,7 @@ function Routine:resume(event, ...) if coroutine.status(self.co) == 'dead' then Array.removeByValue(kernel.routines, self) if self.onDestroy then - self.onDestroy(self) + pcall(self.onDestroy, self) end if #kernel.routines > 0 then switch(kernel.routines[1]) diff --git a/sys/modules/opus/ui/components/FileSelect.lua b/sys/modules/opus/ui/components/FileSelect.lua index dd8747e..7dbec8b 100644 --- a/sys/modules/opus/ui/components/FileSelect.lua +++ b/sys/modules/opus/ui/components/FileSelect.lua @@ -50,7 +50,6 @@ function UI.FileSelect:postInit() } self.path = UI.TextEntry { x = 2, y = -2, ex = -11, - limit = 256, accelerators = { enter = 'path_enter', } diff --git a/sys/modules/opus/ui/components/Slider.lua b/sys/modules/opus/ui/components/Slider.lua index 620877e..4210773 100644 --- a/sys/modules/opus/ui/components/Slider.lua +++ b/sys/modules/opus/ui/components/Slider.lua @@ -13,17 +13,21 @@ UI.Slider.defaults = { sliderFocusColor = 'lightBlue', leftBorder = UI.extChars and '\141' or '\124', rightBorder = UI.extChars and '\142' or '\124', + labelWidth = 0, value = 0, min = 0, max = 100, event = 'slider_update', + transform = function(v) return Util.round(v, 2) end, accelerators = { right = 'slide_right', left = 'slide_left', } } function UI.Slider:setValue(value) - self.value = tonumber(value) or self.min + self.value = self.transform(tonumber(value) or self.min) + self.value = Util.clamp(self.value, self.min, self.max) + self:draw() end function UI.Slider:reset() -- form support @@ -35,56 +39,69 @@ function UI.Slider:focus() self:draw() end +function UI.Slider:getSliderWidth() + local labelWidth = self.labelWidth > 0 and self.labelWidth + 1 + return self.width - (labelWidth or 0) +end + function UI.Slider:draw() + local labelWidth = self.labelWidth > 0 and self.labelWidth + 1 + local width = self.width - (labelWidth or 0) local range = self.max - self.min local perc = (self.value - self.min) / range - local progress = Util.clamp(1 + self.width * perc, 1, self.width) + local progress = Util.clamp(1 + width * perc, 1, width) - local bar = { } - for i = 1, self.width do - local filler = - i == 1 and self.leftBorder or - i == self.width and self.rightBorder or - self.barChar - - table.insert(bar, filler) - end - self:write(1, 1, table.concat(bar), nil, self.barColor) + local bar = self.leftBorder .. string.rep(self.barChar, width - 2) .. self.rightBorder + self:write(1, 1, bar, nil, self.barColor) self:write(progress, 1, self.sliderChar, nil, self.focused and self.sliderFocusColor or self.sliderColor) + if labelWidth then + self:write(self.width - labelWidth + 2, 1, Util.widthify(tostring(self.value), self.labelWidth)) + end end function UI.Slider:eventHandler(event) if event.type == "mouse_down" or event.type == "mouse_drag" then - local pos = event.x - 1 if event.type == 'mouse_down' then self.anchor = event.x - 1 else pos = self.anchor + event.dx end - local range = self.max - self.min - local i = pos / (self.width - 1) - self.value = self.min + (i * range) + local i = pos / (self:getSliderWidth() - 1) + self:setValue(self.min + (i * range)) self:emit({ type = self.event, value = self.value, element = self }) - self:draw() + return true elseif event.type == 'slide_left' or event.type == 'slide_right' then local range = self.max - self.min - local step = range / self.width + local step = range / (self:getSliderWidth() - 1) if event.type == 'slide_left' then - self.value = Util.clamp(self.value - step, self.min, self.max) + self:setValue(self.value - step) else - self.value = Util.clamp(self.value + step, self.min, self.max) + self:setValue(self.value + step) end self:emit({ type = self.event, value = self.value, element = self }) - self:draw() + return true end end function UI.Slider.example() - return UI.Slider { - y = 2, x = 2, ex = -2, - min = 0, max = 1, + return UI.Window { + UI.Slider { + y = 2, x = 2, ex = -2, + min = 0, max = 1, + }, + UI.Slider { + y = 4, x = 2, ex = -2, + min = -1, max = 1, + labelWidth = 5, + }, + UI.Slider { + y = 6, x = 2, ex = -2, + min = 0, max = 100, + labelWidth = 3, + transform = math.floor, + }, } end -- 2.49.1 From 4e6c5172d11f1e8cdd10d912efca82cfe6d61929 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 May 2020 00:01:53 +0000 Subject: [PATCH 153/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 2b60fb6..80289b9 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Tue May 5 23:26:39 UTC 2020 -commit 3d3e5400cfd3d40853dc3fe6a6491232c8368524 +Thu May 7 00:01:52 UTC 2020 +commit c24411717ae504aa93fda9d35dcd58d4dcf17473 Author: kepler155c@gmail.com -Date: Tue May 5 17:25:58 2020 -0600 +Date: Wed May 6 18:01:10 2020 -0600 - tidy up env creation - round 1 + optionally show value for slider component - remove some unneeded limits for textEntries -- 2.49.1 From 8b14399a20e9e36bb1c67a023a884d5bb0eaa912 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 7 May 2020 21:34:50 -0600 Subject: [PATCH 154/236] a temporary fix for vfs/delete --- sys/apps/Overview.lua | 2 +- sys/apps/Tasks.lua | 2 +- sys/init/2.vfs.lua | 20 +++++++++++++++++++ sys/modules/opus/ui/components/Checkbox.lua | 2 +- .../opus/ui/components/CheckboxGrid.lua | 2 +- sys/modules/opus/ui/components/Chooser.lua | 2 +- sys/modules/opus/util.lua | 18 ----------------- 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 1b68f5b..7fae2ba 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -30,7 +30,7 @@ end local REGISTRY_DIR = 'usr/.registry' --- icon:gsub('.', function(b) return '\\' .. b:byte() end) +-- iconExt:gsub('.', function(b) return '\\' .. b:byte() end) local DEFAULT_ICON = NFT.parse('\30\55\31\48\136\140\140\140\132\ \30\48\31\55\149\31\48\128\128\128\30\55\149\ \30\55\31\48\138\143\143\143\133') diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index f8c2edd..be21f9c 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -39,7 +39,7 @@ local page = UI.Page { }, accelerators = { [ 'control-q' ] = 'quit', - space = 'activate', + [ ' ' ] = 'activate', t = 'terminate', }, eventHandler = function (self, event) diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 3361d80..9dc946d 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -100,6 +100,26 @@ end function nativefs.delete(node, dir) if node.mountPoint == dir then fs.unmount(dir) + -- hack here + -- if a file is mounted over an existing directory + -- ie. sys/apps/MOUNT.LUA + -- then sys and sys/apps are created as temp nodes + -- therefore, trying to delete sys will was only + -- removing the node and not deleting the directory + -- Need a better way to backfill nodes in a way + -- that preserves the vfs functionality + + -- perhaps a flag that denotes that this is a + -- file/directory is the actual mount + + -- this hack will not fix + -- rm packages/common + -- where packages is linked from a drive + -- and urls are mounted under packages/common + -- (as the fstype will be linkfs) + if node.fstype == 'nativefs' then + fs.native.delete(dir) + end else fs.native.delete(dir) end diff --git a/sys/modules/opus/ui/components/Checkbox.lua b/sys/modules/opus/ui/components/Checkbox.lua index 50bae5f..ddde55a 100644 --- a/sys/modules/opus/ui/components/Checkbox.lua +++ b/sys/modules/opus/ui/components/Checkbox.lua @@ -16,7 +16,7 @@ UI.Checkbox.defaults = { height = 1, width = 3, accelerators = { - space = 'checkbox_toggle', + [ ' ' ] = 'checkbox_toggle', mouse_click = 'checkbox_toggle', } } diff --git a/sys/modules/opus/ui/components/CheckboxGrid.lua b/sys/modules/opus/ui/components/CheckboxGrid.lua index 0e6a62d..5712225 100644 --- a/sys/modules/opus/ui/components/CheckboxGrid.lua +++ b/sys/modules/opus/ui/components/CheckboxGrid.lua @@ -14,7 +14,7 @@ UI.CheckboxGrid.defaults = { UIElement = 'CheckboxGrid', checkedKey = 'checked', accelerators = { - space = 'grid_toggle', + [ ' ' ] = 'grid_toggle', key_enter = 'grid_toggle', }, } diff --git a/sys/modules/opus/ui/components/Chooser.lua b/sys/modules/opus/ui/components/Chooser.lua index 49f882c..9da5e15 100644 --- a/sys/modules/opus/ui/components/Chooser.lua +++ b/sys/modules/opus/ui/components/Chooser.lua @@ -15,7 +15,7 @@ UI.Chooser.defaults = { rightIndicator = UI.extChars and '\187' or '>', height = 1, accelerators = { - space = 'choice_next', + [ ' ' ] = 'choice_next', right = 'choice_next', left = 'choice_prev', } diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 8cd7286..c1c152f 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -730,24 +730,6 @@ function Util.parse(...) return args, options end -function Util.args(arg) - local options, args = { }, { } - - local k = 1 - while k <= #arg do - local v = arg[k] - if _ssub(v, 1, 1) == '-' then - local opt = _ssub(v, 2) - options[opt] = arg[k + 1] - k = k + 1 - else - table.insert(args, v) - end - k = k + 1 - end - return options, args -end - -- http://lua-users.org/wiki/AlternativeGetOpt local function getopt( arg, options ) local tab = {} -- 2.49.1 From 3f80faa0fe03545929f7f5f7eb2d4d7f1d89c85d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 03:35:30 +0000 Subject: [PATCH 155/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 80289b9..6e8e0a4 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Thu May 7 00:01:52 UTC 2020 -commit c24411717ae504aa93fda9d35dcd58d4dcf17473 +Fri May 8 03:35:29 UTC 2020 +commit 8b14399a20e9e36bb1c67a023a884d5bb0eaa912 Author: kepler155c@gmail.com -Date: Wed May 6 18:01:10 2020 -0600 +Date: Thu May 7 21:34:50 2020 -0600 - optionally show value for slider component - remove some unneeded limits for textEntries + a temporary fix for vfs/delete -- 2.49.1 From db9c05fa89465f677da02c4ad51a22ce24c2d40f Mon Sep 17 00:00:00 2001 From: devomaa <25231103+devomaa@users.noreply.github.com> Date: Fri, 8 May 2020 20:09:32 +0300 Subject: [PATCH 156/236] Update Overview.lua to work with N hotkey (#33) --- sys/apps/Overview.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 7fae2ba..b670fc2 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -477,7 +477,7 @@ function page:eventHandler(event) shell.switchTab(shell.openTab(Alt.get('files'))) elseif event.type == 'network' then - shell.switchTab(shell.openTab('network')) + shell.switchTab(shell.openTab('Network')) elseif event.type == 'help' then shell.switchTab(shell.openTab('Help Overview')) -- 2.49.1 From 71bbd2d4571d466379cb07dd83292d275c84a3db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 May 2020 17:10:12 +0000 Subject: [PATCH 157/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 6e8e0a4..77a8f73 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Fri May 8 03:35:29 UTC 2020 -commit 8b14399a20e9e36bb1c67a023a884d5bb0eaa912 -Author: kepler155c@gmail.com -Date: Thu May 7 21:34:50 2020 -0600 +Fri May 8 17:10:11 UTC 2020 +commit db9c05fa89465f677da02c4ad51a22ce24c2d40f +Author: devomaa <25231103+devomaa@users.noreply.github.com> +Date: Fri May 8 20:09:32 2020 +0300 - a temporary fix for vfs/delete + Update Overview.lua to work with N hotkey (#33) -- 2.49.1 From b0db0b86bd5514bbb5e8c2a4ccdebc8cf1fc3182 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 8 May 2020 22:32:44 -0600 Subject: [PATCH 158/236] kernel improvements --- sys/apps/network/telnet.lua | 7 ++- sys/autorun/log.lua | 2 +- sys/init/2.vfs.lua | 4 +- sys/init/7.multishell.lua | 43 ++++------------ sys/kernel.lua | 93 ++++++++++++++++++++--------------- sys/modules/opus/injector.lua | 2 +- sys/modules/opus/trace.lua | 19 ++++++- 7 files changed, 87 insertions(+), 83 deletions(-) diff --git a/sys/apps/network/telnet.lua b/sys/apps/network/telnet.lua index 9aec83c..87ab04d 100644 --- a/sys/apps/network/telnet.lua +++ b/sys/apps/network/telnet.lua @@ -42,17 +42,16 @@ local function telnetHost(socket, mode) end local shellThread = kernel.run({ - terminal = win, window = win, title = mode .. ' client', hidden = true, - co = coroutine.create(function() - Util.run(_ENV, Alt.get('shell'), table.unpack(termInfo.program)) + fn = function() + Util.run(kernel.makeEnv(), Alt.get('shell'), table.unpack(termInfo.program)) if socket.queue then socket:write(socket.queue) end socket:close() - end) + end, }) Event.addRoutine(function() diff --git a/sys/autorun/log.lua b/sys/autorun/log.lua index 5e2b5e7..9e5b04e 100644 --- a/sys/autorun/log.lua +++ b/sys/autorun/log.lua @@ -26,7 +26,7 @@ local function systemLog() if y > 1 then local currentTab = kernel.getFocused() if currentTab == routine then - if currentTab.terminal.scrollUp and not currentTab.terminal.noAutoScroll then + if currentTab.terminal.scrollUp then if dir == -1 then currentTab.terminal.scrollUp() else diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 9dc946d..b0e2ea7 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -104,12 +104,12 @@ function nativefs.delete(node, dir) -- if a file is mounted over an existing directory -- ie. sys/apps/MOUNT.LUA -- then sys and sys/apps are created as temp nodes - -- therefore, trying to delete sys will was only + -- therefore, trying to delete sys was only -- removing the node and not deleting the directory -- Need a better way to backfill nodes in a way -- that preserves the vfs functionality - -- perhaps a flag that denotes that this is a + -- perhaps a flag that denotes that this -- file/directory is the actual mount -- this hack will not fix diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index 4b581bd..e81b8ca 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -1,6 +1,5 @@ local Blit = require('opus.ui.blit') local Config = require('opus.config') -local trace = require('opus.trace') local Util = require('opus.util') local colors = _G.colors @@ -103,48 +102,25 @@ function multishell.launch( tProgramEnv, sProgramPath, ... ) }) end -local function xprun(env, path, ...) - setmetatable(env, { __index = _G }) - local fn, m = loadfile(path, env) - if fn then - return trace(fn, ...) - end - return fn, m -end - function multishell.openTab(tab) if not tab.title and tab.path then tab.title = fs.getName(tab.path):match('([^%.]+)') end tab.title = tab.title or 'untitled' tab.window = tab.window or window.create(parentTerm, 1, 2, w, h - 1, false) - tab.terminal = tab.terminal or tab.window - - local routine = kernel.newRoutine(tab) - - routine.co = coroutine.create(function() - local result, err - - if tab.fn then - result, err = Util.runFunction(routine.env, tab.fn, table.unpack(tab.args or { } )) - elseif tab.path then - result, err = xprun(routine.env, tab.path, table.unpack(tab.args or { } )) - else - err = 'multishell: invalid tab' - end - + tab.onExit = function(self, result, err) if not result and err and err ~= 'Terminated' or (err and err ~= 0) then - tab.terminal.setBackgroundColor(colors.black) + self.terminal.setBackgroundColor(colors.black) if tonumber(err) then - tab.terminal.setTextColor(colors.orange) + self.terminal.setTextColor(colors.orange) print('Process exited with error code: ' .. err) elseif err then printError(tostring(err)) end - tab.terminal.setTextColor(colors.white) + self.terminal.setTextColor(colors.white) print('\nPress enter to close') - routine.isDead = true - routine.hidden = false + self.isDead = true + self.hidden = false redrawMenu() while true do local e, code = os.pullEventRaw('key') @@ -153,9 +129,9 @@ function multishell.openTab(tab) end end end - end) + end - kernel.launch(routine) + local routine = kernel.run(tab) if tab.focused then multishell.setFocus(routine.uid) @@ -256,9 +232,10 @@ kernel.hook('multishell_redraw', function() if currentTab then if currentTab.sx then + local textColor = currentTab.isDead and _colors.errorColor or _colors.focusTextColor blit:write(currentTab.sx - 1, ' ' .. currentTab.title:sub(1, currentTab.width - 1) .. ' ', - _colors.focusBackgroundColor, _colors.focusTextColor) + _colors.focusBackgroundColor, textColor) end if not currentTab.noTerminate then blit:write(w, closeInd, nil, _colors.focusTextColor) diff --git a/sys/kernel.lua b/sys/kernel.lua index 51cba75..7bb0aa5 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -1,5 +1,6 @@ local Array = require('opus.array') local Terminal = require('opus.terminal') +local trace = require('opus.trace') local Util = require('opus.util') _G.kernel = { @@ -67,8 +68,6 @@ function kernel.unhook(event, fn) end end -local Routine = { } - local function switch(routine, previous) if routine then if previous and previous.window then @@ -86,6 +85,8 @@ local function switch(routine, previous) end end +local Routine = { } + function Routine:resume(event, ...) if not self.co or coroutine.status(self.co) == 'dead' then return @@ -95,38 +96,33 @@ function Routine:resume(event, ...) local previousTerm = term.redirect(self.terminal) local previous = kernel.running - kernel.running = self -- stupid shell set title + kernel.running = self local ok, result = coroutine.resume(self.co, event, ...) kernel.running = previous - if ok then - self.filter = result - else - _G.printError(result) - end - + self.filter = result self.terminal = term.current() term.redirect(previousTerm) - if not ok and self.haltOnError then - error(result, -1) - end - if coroutine.status(self.co) == 'dead' then - Array.removeByValue(kernel.routines, self) - if self.onDestroy then - pcall(self.onDestroy, self) - end - if #kernel.routines > 0 then - switch(kernel.routines[1]) - end - if self.haltOnExit then - kernel.halt() - end - end return ok, result end end +-- override if any post processing is required +-- routine:cleanup must be called explicitly if overridden +function Routine:onExit(status, message) -- self, status, message + if not status and message ~= 'Terminated' then + _G.printError(message) + end +end + +function Routine:cleanup() + Array.removeByValue(kernel.routines, self) + if #kernel.routines > 0 then + switch(kernel.routines[1]) + end +end + function kernel.getFocused() return kernel.routines[1] end @@ -147,17 +143,26 @@ function kernel.newRoutine(args) local routine = setmetatable({ uid = kernel.UID, timestamp = os.clock(), - terminal = kernel.window, window = kernel.window, title = 'untitled', }, { __index = Routine }) Util.merge(routine, args) routine.env = args.env or shell.makeEnv() + routine.terminal = routine.terminal or routine.window return routine end +local function xprun(env, path, ...) + setmetatable(env, { __index = _G }) + local fn, m = loadfile(path, env) + if fn then + return trace(fn, ...) + end + return fn, m +end + function kernel.launch(routine) routine.co = routine.co or coroutine.create(function() local result, err @@ -165,14 +170,13 @@ function kernel.launch(routine) if routine.fn then result, err = Util.runFunction(routine.env, routine.fn, table.unpack(routine.args or { } )) elseif routine.path then - result, err = Util.run(routine.env, routine.path, table.unpack(routine.args or { } )) + result, err = xprun(routine.env, routine.path, table.unpack(routine.args or { } )) else err = 'kernel: invalid routine' end - if not result and err ~= 'Terminated' then - error(err or 'Error occurred', 2) - end + pcall(routine.onExit, routine, result, err) + routine:cleanup() end) table.insert(kernel.routines, routine) @@ -203,8 +207,6 @@ function kernel.raise(uid) end switch(routine, previous) --- local previous = eventData[2] --- local routine = kernel.find(previous) return true end return false @@ -232,8 +234,8 @@ function kernel.find(uid) return Util.find(kernel.routines, 'uid', uid) end -function kernel.halt() - os.queueEvent('kernel_halt') +function kernel.halt(status, message) + os.queueEvent('kernel_halt', status, message) end function kernel.event(event, eventData) @@ -275,15 +277,20 @@ function kernel.event(event, eventData) end function kernel.start() - local s, m = pcall(function() + local s, m + pcall(function() repeat local eventData = { os.pullEventRaw() } local event = table.remove(eventData, 1) kernel.event(event, eventData) + if event == 'kernel_halt' then + s = eventData[1] + m = eventData[2] + end until event == 'kernel_halt' end) - if not s then + if not s and m then kernel.window.setVisible(true) term.redirect(kernel.window) print('\nCrash detected\n') @@ -320,15 +327,15 @@ local function init(...) term.redirect(kernel.window) shell.run('sys/apps/autorun.lua') - local shellWindow = window.create(kernel.terminal, 1, 1, w, h, false) + local win = window.create(kernel.terminal, 1, 1, w, h, true) local s, m = kernel.run({ title = args[1], path = 'sys/apps/shell.lua', args = args, - haltOnExit = true, - haltOnError = true, - terminal = shellWindow, - window = shellWindow, + window = win, + onExit = function(_, s, m) + kernel.halt(s, m) + end, }) if s then kernel.raise(s.uid) @@ -342,8 +349,12 @@ end kernel.run({ fn = init, title = 'init', - haltOnError = true, args = { ... }, + onExit = function(_, status, message) + if not status then + kernel.halt(status, message) + end + end, }) kernel.start() diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index e93e5d1..aea109a 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -79,7 +79,7 @@ return function(env) -- place package and require function into env env.package = { - path = env.LUA_PATH or _G.LUA_PATH or DEFAULT_PATH, + path = env.LUA_PATH or _G.LUA_PATH or DEFAULT_PATH, config = '/\n:\n?\n!\n-', preload = { }, loaded = { diff --git a/sys/modules/opus/trace.lua b/sys/modules/opus/trace.lua index 5673b6d..c8cf2a9 100644 --- a/sys/modules/opus/trace.lua +++ b/sys/modules/opus/trace.lua @@ -37,6 +37,22 @@ local function trim_traceback(target, marker) for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end +--[[ + TODO : fix this trace + Anavrins - if you could take a look, it would be appreciated + -- basically i want the stack logged in a more readable + -- format and the normal code/error message to be returned + + -- unsure why the traceback method concatenates the stack + -- when it can just be returned as a table - would make + -- filtering much simpler + + -- the following seems to reduce the stacktrace way too + -- much - losing most of the relevant call stack + + -- i have modified this a bit from the original - not sure + -- if my changes are causing the issues or not + -- Trim identical suffixes local t_len, m_len = #ttarget, #tmarker while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do @@ -50,7 +66,7 @@ local function trim_traceback(target, marker) table.remove(ttarget, t_len) t_len = t_len - 1 end - +]] ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall ttarget[#ttarget] = nil @@ -84,6 +100,7 @@ return function (fn, ...) if trace[i] == "stack traceback:" then trace_starts = i; break end end + _G._syslog('') for _, line in pairs(trace) do _G._syslog(line) end -- 2.49.1 From cfc18e10cd3a5dd2c356ed59fb63bab5e3fd0486 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 May 2020 04:33:20 +0000 Subject: [PATCH 159/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 77a8f73..a379d0f 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Fri May 8 17:10:11 UTC 2020 -commit db9c05fa89465f677da02c4ad51a22ce24c2d40f -Author: devomaa <25231103+devomaa@users.noreply.github.com> -Date: Fri May 8 20:09:32 2020 +0300 +Sat May 9 04:33:19 UTC 2020 +commit b0db0b86bd5514bbb5e8c2a4ccdebc8cf1fc3182 +Author: kepler155c@gmail.com +Date: Fri May 8 22:32:44 2020 -0600 - Update Overview.lua to work with N hotkey (#33) + kernel improvements -- 2.49.1 From a3a819256f4c4441a550eff1d06592e70a49f9ec Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 10 May 2020 14:04:20 -0600 Subject: [PATCH 160/236] fix resizing scrolling terminals --- sys/apps/shell.lua | 3 ++- sys/autorun/log.lua | 11 ---------- sys/init/7.multishell.lua | 2 +- sys/kernel.lua | 13 +++++++----- sys/modules/opus/input.lua | 17 ++++++++++++--- sys/modules/opus/terminal.lua | 39 +++++++++++++++++++++++++++++++---- 6 files changed, 60 insertions(+), 25 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index bc3707f..6b23582 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -666,6 +666,7 @@ local function shellRead(history) end elseif event == "term_resize" then + terminal.reposition(1, 1, oldTerm.getSize()) entry.width = term.getSize() - 3 entry:updateScroll() redraw() @@ -680,7 +681,7 @@ end local history = History.load('usr/.shell_history', 25) term.setBackgroundColor(_colors.backgroundColor) -term.clear() +--term.clear() if settings.get("motd.enabled") then shell.run("motd") diff --git a/sys/autorun/log.lua b/sys/autorun/log.lua index 9e5b04e..3540f0d 100644 --- a/sys/autorun/log.lua +++ b/sys/autorun/log.lua @@ -4,22 +4,11 @@ local kernel = _G.kernel local keyboard = _G.device.keyboard -local multishell = _ENV.multishell local os = _G.os -local term = _G.term local function systemLog() local routine = kernel.getCurrent() - if multishell and multishell.openTab then - local w, h = kernel.window.getSize() - kernel.window.reposition(1, 2, w, h - 1) - - routine.terminal = kernel.window - routine.window = kernel.window - term.redirect(kernel.window) - end - kernel.hook('mouse_scroll', function(_, eventData) local dir, y = eventData[1], eventData[3] diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index e81b8ca..f37033c 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -20,7 +20,7 @@ local multishell = { } shell.setEnv('multishell', multishell) -multishell.term = parentTerm --deprecated use device.terminal +kernel.window.reposition(1, 2, w, h - 1) local config = { standard = { diff --git a/sys/kernel.lua b/sys/kernel.lua index 7bb0aa5..1535b2c 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -109,7 +109,6 @@ function Routine:resume(event, ...) end -- override if any post processing is required --- routine:cleanup must be called explicitly if overridden function Routine:onExit(status, message) -- self, status, message if not status and message ~= 'Terminated' then _G.printError(message) @@ -177,13 +176,17 @@ function kernel.launch(routine) pcall(routine.onExit, routine, result, err) routine:cleanup() + + if not result then + error(err) + end end) table.insert(kernel.routines, routine) local s, m = routine:resume() - return not s and s or routine.uid, m + return s and routine.uid, m end function kernel.run(args) @@ -278,7 +281,7 @@ end function kernel.start() local s, m - pcall(function() + local s2, m2 = pcall(function() repeat local eventData = { os.pullEventRaw() } local event = table.remove(eventData, 1) @@ -290,11 +293,11 @@ function kernel.start() until event == 'kernel_halt' end) - if not s and m then + if (not s and m) or (not s2 and m2) then kernel.window.setVisible(true) term.redirect(kernel.window) print('\nCrash detected\n') - _G.printError(m) + _G.printError(m or m2) end term.redirect(kernel.terminal) end diff --git a/sys/modules/opus/input.lua b/sys/modules/opus/input.lua index 956e0ba..98e3db2 100644 --- a/sys/modules/opus/input.lua +++ b/sys/modules/opus/input.lua @@ -189,11 +189,22 @@ function input:translate(event, code, p1, p2) end end -function input:test() +if not ({ ...})[1] then + local colors = _G.colors + local term = _G.term + while true do - local ch = self:translate(os.pullEvent()) + local e = { os.pullEvent() } + local ch = input:translate(table.unpack(e)) if ch then - Util.print(ch) + term.setTextColor(colors.white) + print(table.unpack(e)) + term.setTextColor(colors.lime) + local t = { } + for k,v in pairs(ch) do + table.insert(t, k .. ':' .. v) + end + print('--> ' .. table.concat(t, ' ') .. '\n') end end end diff --git a/sys/modules/opus/terminal.lua b/sys/modules/opus/terminal.lua index 7d3339c..21fce97 100644 --- a/sys/modules/opus/terminal.lua +++ b/sys/modules/opus/terminal.lua @@ -33,7 +33,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end local win = { } - local maxScroll = 100 + local maxScroll local cx, cy = 1, 1 local blink = false local _bg, _fg = colors.black, colors.white @@ -164,7 +164,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) win.canvas.lines[lines + i] = { } win.canvas:clearLine(lines + i) end - while #win.canvas.lines > maxScroll do + while #win.canvas.lines > (maxScroll or win.canvas.height) do table.remove(win.canvas.lines, 1) end scrollTo(#win.canvas.lines) @@ -213,8 +213,39 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) end function win.reposition(x, y, width, height) - win.canvas.x, win.canvas.y = x, y - win.canvas:resize(width or win.canvas.width, height or win.canvas.height) + if not maxScroll then + win.canvas:move(x, y) + win.canvas:resize(width or win.canvas.width, height or win.canvas.height) + return + end + + -- special processing for scrolling terminal like windows + local delta = height - win.canvas.height + + if delta > 0 then -- grow + for _ = 1, delta do + win.canvas.lines[#win.canvas.lines + 1] = { } + win.canvas:clearLine(#win.canvas.lines) + end + + elseif delta < 0 then -- shrink + for _ = delta + 1, 0 do + if cy < win.canvas.height then + win.canvas.lines[#win.canvas.lines] = nil + else + cy = cy - 1 + win.canvas.offy = win.canvas.offy + 1 + end + end + end + + win.canvas:resizeBuffer(width, #win.canvas.lines) + + win.canvas.height = height + win.canvas.width = width + win.canvas:move(x, y) + + update() end --[[ Additional methods ]]-- -- 2.49.1 From bd911e80e8f114d12ed27dbbb642449084ee04ed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 10 May 2020 20:04:57 +0000 Subject: [PATCH 161/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index a379d0f..70f2f1d 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat May 9 04:33:19 UTC 2020 -commit b0db0b86bd5514bbb5e8c2a4ccdebc8cf1fc3182 +Sun May 10 20:04:56 UTC 2020 +commit a3a819256f4c4441a550eff1d06592e70a49f9ec Author: kepler155c@gmail.com -Date: Fri May 8 22:32:44 2020 -0600 +Date: Sun May 10 14:04:20 2020 -0600 - kernel improvements + fix resizing scrolling terminals -- 2.49.1 From 8279c1ae12b1417380c348f39f735a47395d12a9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 11 May 2020 17:25:58 -0600 Subject: [PATCH 162/236] environments, error messages, and stack traces, oh my! Changed the way processes are launched. multishell.openTab and kernel.run now accept the current environment as a parameter. That new process inherits a copy of the passed environment. Reduces complexity as the calling process is not required to create a suitable env. Stack traces have been greatly improved and now include the stack for coroutines that error. --- sys/apps/Sniff.lua | 2 +- sys/apps/Tasks.lua | 7 ++ sys/apps/inspect.lua | 2 +- sys/apps/netdaemon.lua | 2 - sys/apps/network/snmp.lua | 17 ++--- sys/apps/network/telnet.lua | 4 +- sys/apps/shell.lua | 45 ++++-------- sys/autorun/hotkeys.lua | 3 +- sys/autorun/log.lua | 2 +- sys/init/5.network.lua | 2 +- sys/init/7.multishell.lua | 42 ++++++----- sys/kernel.lua | 94 ++++++++++++------------ sys/modules/opus/crypto/chacha20.lua | 2 +- sys/modules/opus/event.lua | 8 +++ sys/modules/opus/trace.lua | 104 ++++++++++----------------- sys/modules/opus/ui.lua | 2 +- 16 files changed, 151 insertions(+), 187 deletions(-) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 5336234..41c1543 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -255,7 +255,7 @@ function page.packetSlide:eventHandler(event) page:setFocus(page.packetGrid) elseif event.type == 'packet_lua' then - multishell.openTab({ path = 'sys/apps/Lua.lua', args = { self.currentPacket.message }, focused = true }) + multishell.openTab(_ENV, { path = 'sys/apps/Lua.lua', args = { self.currentPacket.message }, focused = true }) elseif event.type == 'prev_packet' then local c = self.currentPacket diff --git a/sys/apps/Tasks.lua b/sys/apps/Tasks.lua index be21f9c..4e359fc 100644 --- a/sys/apps/Tasks.lua +++ b/sys/apps/Tasks.lua @@ -12,6 +12,7 @@ local page = UI.Page { buttons = { { text = 'Activate', event = 'activate' }, { text = 'Terminate', event = 'terminate' }, + { text = 'Inspect', event = 'inspect' }, }, }, grid = UI.ScrollingGrid { @@ -49,6 +50,12 @@ local page = UI.Page { multishell.setFocus(t.uid) elseif event.type == 'terminate' then multishell.terminate(t.uid) + elseif event.type == 'inspect' then + multishell.openTab(_ENV, { + path = 'sys/apps/Lua.lua', + args = { t }, + focused = true, + }) end end if event.type == 'quit' then diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 0df9526..90b59db 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -110,7 +110,7 @@ page = UI.Page { self.grid:draw() elseif event.type == 'grid_select' then - multishell.openTab({ + multishell.openTab(_ENV, { path = 'sys/apps/Lua.lua', args = { event.selected.raw }, focused = true, diff --git a/sys/apps/netdaemon.lua b/sys/apps/netdaemon.lua index 1d3fd66..8ffca25 100644 --- a/sys/apps/netdaemon.lua +++ b/sys/apps/netdaemon.lua @@ -1,5 +1,3 @@ -_G.requireInjector(_ENV) - local Event = require('opus.event') local Util = require('opus.util') diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index 18a4143..2c48ee1 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -31,21 +31,14 @@ local function snmpConnection(socket) socket:write('pong') elseif msg.type == 'script' then - local env = kernel.makeEnv() - local fn, err = load(msg.args, 'script', nil, env) - if fn then - kernel.run({ - fn = fn, - env = env, - title = 'script', - }) - else - _G.printError(err) - end + kernel.run(_ENV, { + chunk = msg.args, + title = 'script', + }) elseif msg.type == 'scriptEx' then local s, m = pcall(function() - local env = kernel.makeEnv() + local env = kernel.makeEnv(_ENV) local fn, m = load(msg.args, 'script', nil, env) if not fn then error(m) diff --git a/sys/apps/network/telnet.lua b/sys/apps/network/telnet.lua index 87ab04d..ff92298 100644 --- a/sys/apps/network/telnet.lua +++ b/sys/apps/network/telnet.lua @@ -41,12 +41,12 @@ local function telnetHost(socket, mode) end end - local shellThread = kernel.run({ + local shellThread = kernel.run(_ENV, { window = win, title = mode .. ' client', hidden = true, fn = function() - Util.run(kernel.makeEnv(), Alt.get('shell'), table.unpack(termInfo.program)) + Util.run(kernel.makeEnv(_ENV), Alt.get('shell'), table.unpack(termInfo.program)) if socket.queue then socket:write(socket.queue) end diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 6b23582..92cf23d 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -6,16 +6,6 @@ local fs = _G.fs local settings = _G.settings local shell = _ENV.shell -local sandboxEnv = { } -for k,v in pairs(_ENV) do - sandboxEnv[k] = v -end -sandboxEnv.package = nil -sandboxEnv.require = nil -sandboxEnv.arg = nil -sandboxEnv._ENV = nil -sandboxEnv.shell = shell - _G.requireInjector(_ENV) local trace = require('opus.trace') @@ -51,7 +41,7 @@ local function run(...) local args = tokenise(...) local command = table.remove(args, 1) or error('No such program') local isUrl = not not command:match("^(https?:)") - local env = shell.makeEnv() + local env = shell.makeEnv(_ENV) local path, loadFn if isUrl then @@ -64,7 +54,7 @@ local function run(...) local fn, err = loadFn(path, env) if not fn then - error(err) + error(err, -1) end if _ENV.multishell then @@ -287,13 +277,9 @@ function shell.getRunningInfo() return tProgramStack[#tProgramStack] end -function shell.setEnv(name, value) - _ENV[name] = value - sandboxEnv[name] = value -end - -function shell.makeEnv() - local env = setmetatable(Util.shallowCopy(sandboxEnv), { __index = _G }) +-- convenience function for making a runnable env +function shell.makeEnv(env) + env = setmetatable(Util.shallowCopy(env), { __index = _G }) _G.requireInjector(env) return env end @@ -321,7 +307,6 @@ function shell.newTab(tabInfo, ...) if path then tabInfo.path = path - tabInfo.env = shell.makeEnv() tabInfo.args = args tabInfo.title = fs.getName(path):match('([^%.]+)') @@ -329,25 +314,21 @@ function shell.newTab(tabInfo, ...) table.insert(tabInfo.args, 1, tabInfo.path) tabInfo.path = 'sys/apps/shell.lua' end - return _ENV.multishell.openTab(tabInfo) + return _ENV.multishell.openTab(_ENV, tabInfo) end return nil, 'No such program' end -function shell.openTab(...) - -- needs to use multishell.launch .. so we can run with stock multishell - local tWords = tokenise( ... ) - local sCommand = tWords[1] - if sCommand then - local sPath = shell.resolveProgram(sCommand) - if sPath == "sys/apps/shell.lua" then - return _ENV.multishell.launch(shell.makeEnv(), sPath, table.unpack(tWords, 2)) - else - return _ENV.multishell.launch(shell.makeEnv(), "sys/apps/shell.lua", sCommand, table.unpack(tWords, 2)) - end +if not _ENV.multishell then + function shell.newTab() + error('Multishell is not available') end end +function shell.openTab(...) + return shell.newTab({ }, ...) +end + function shell.openForegroundTab( ... ) return shell.newTab({ focused = true }, ...) end diff --git a/sys/autorun/hotkeys.lua b/sys/autorun/hotkeys.lua index ab591e0..ab65ad6 100644 --- a/sys/autorun/hotkeys.lua +++ b/sys/autorun/hotkeys.lua @@ -10,9 +10,8 @@ if multishell and multishell.getTabs then local tab = kernel.getFocused() if tab and not tab.noTerminate then multishell.terminate(tab.uid) - multishell.openTab({ + multishell.openTab(tab.env, { path = tab.path, - env = tab.env, args = tab.args, focused = true, }) diff --git a/sys/autorun/log.lua b/sys/autorun/log.lua index 3540f0d..c43c1dc 100644 --- a/sys/autorun/log.lua +++ b/sys/autorun/log.lua @@ -39,7 +39,7 @@ local function systemLog() keyboard.removeHotkey('control-d') end -kernel.run({ +kernel.run(_ENV, { title = 'System Log', fn = systemLog, noTerminate = true, diff --git a/sys/init/5.network.lua b/sys/init/5.network.lua index d526b62..8e31fa4 100644 --- a/sys/init/5.network.lua +++ b/sys/init/5.network.lua @@ -15,7 +15,7 @@ do end local function startNetwork() - kernel.run({ + kernel.run(_ENV, { title = 'Net daemon', path = 'sys/apps/netdaemon.lua', hidden = true, diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index f37033c..3d2f0ba 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -8,7 +8,6 @@ local kernel = _G.kernel local keys = _G.keys local os = _G.os local printError = _G.printError -local shell = _ENV.shell local window = _G.window local parentTerm = _G.device.terminal @@ -18,7 +17,7 @@ local tabsDirty = false local closeInd = Util.getVersion() >= 1.76 and '\215' or '*' local multishell = { } -shell.setEnv('multishell', multishell) +_ENV.multishell = multishell kernel.window.reposition(1, 2, w, h - 1) @@ -93,22 +92,21 @@ function multishell.getTabs() return kernel.routines end -function multishell.launch( tProgramEnv, sProgramPath, ... ) +function multishell.launch(env, path, ...) -- backwards compatibility - return multishell.openTab({ - env = tProgramEnv, - path = sProgramPath, + return multishell.openTab(env, { + path = path, args = { ... }, }) end -function multishell.openTab(tab) +function multishell.openTab(env, tab) if not tab.title and tab.path then tab.title = fs.getName(tab.path):match('([^%.]+)') end tab.title = tab.title or 'untitled' tab.window = tab.window or window.create(parentTerm, 1, 2, w, h - 1, false) - tab.onExit = function(self, result, err) + tab.onExit = tab.onExit or function(self, result, err) if not result and err and err ~= 'Terminated' or (err and err ~= 0) then self.terminal.setBackgroundColor(colors.black) if tonumber(err) then @@ -131,14 +129,17 @@ function multishell.openTab(tab) end end - local routine = kernel.run(tab) + local routine, message = kernel.run(env, tab) - if tab.focused then - multishell.setFocus(routine.uid) - else - redrawMenu() + if routine then + if tab.focused then + multishell.setFocus(routine.uid) + else + redrawMenu() + end end - return routine.uid + + return routine and routine.uid, message end function multishell.hideTab(tabId) @@ -316,15 +317,22 @@ kernel.hook('mouse_scroll', function(_, eventData) end) kernel.hook('kernel_ready', function() - overviewId = multishell.openTab({ - path = config.launcher or 'sys/apps/Overview.lua', + overviewId = multishell.openTab(_ENV, { + path = 'sys/apps/shell.lua', + args = { config.launcher or 'sys/apps/Overview.lua' }, isOverview = true, noTerminate = true, focused = true, title = '+', + onExit = function(_, s, m) + if not s then + kernel.halt(s, m) + end + end, }) + multishell.setTitle(overviewId, '+') - multishell.openTab({ + multishell.openTab(_ENV, { path = 'sys/apps/shell.lua', args = { 'sys/apps/autorun.lua' }, title = 'Autorun', diff --git a/sys/kernel.lua b/sys/kernel.lua index 1535b2c..5f86bd4 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -108,6 +108,38 @@ function Routine:resume(event, ...) end end +function Routine:run() + self.co = self.co or coroutine.create(function() + local result, err, fn + + if self.fn then + fn = self.fn + _G.setfenv(fn, self.env) + elseif self.path then + fn, err = loadfile(self.path, self.env) + elseif self.chunk then + fn, err = load(self.chunk, self.title, nil, self.env) + end + + if fn then + result, err = trace(fn, table.unpack(self.args or { } )) + else + err = err or 'kernel: invalid routine' + end + + pcall(self.onExit, self, result, err) + self:cleanup() + + if not result then + error(err) + end + end) + + table.insert(kernel.routines, self) + + return self:resume() +end + -- override if any post processing is required function Routine:onExit(status, message) -- self, status, message if not status and message ~= 'Terminated' then @@ -134,9 +166,14 @@ function kernel.getShell() return shell end -kernel.makeEnv = shell.makeEnv +-- each routine inherits the parent's env +function kernel.makeEnv(env) + env = setmetatable(Util.shallowCopy(env or _ENV), { __index = _G }) + _G.requireInjector(env) + return env +end -function kernel.newRoutine(args) +function kernel.newRoutine(env, args) kernel.UID = kernel.UID + 1 local routine = setmetatable({ @@ -147,52 +184,16 @@ function kernel.newRoutine(args) }, { __index = Routine }) Util.merge(routine, args) - routine.env = args.env or shell.makeEnv() + routine.env = args.env or kernel.makeEnv(env) routine.terminal = routine.terminal or routine.window return routine end -local function xprun(env, path, ...) - setmetatable(env, { __index = _G }) - local fn, m = loadfile(path, env) - if fn then - return trace(fn, ...) - end - return fn, m -end - -function kernel.launch(routine) - routine.co = routine.co or coroutine.create(function() - local result, err - - if routine.fn then - result, err = Util.runFunction(routine.env, routine.fn, table.unpack(routine.args or { } )) - elseif routine.path then - result, err = xprun(routine.env, routine.path, table.unpack(routine.args or { } )) - else - err = 'kernel: invalid routine' - end - - pcall(routine.onExit, routine, result, err) - routine:cleanup() - - if not result then - error(err) - end - end) - - table.insert(kernel.routines, routine) - - local s, m = routine:resume() - - return s and routine.uid, m -end - -function kernel.run(args) - local routine = kernel.newRoutine(args) - kernel.launch(routine) - return routine +function kernel.run(env, args) + local routine = kernel.newRoutine(env, args) + local s, m = routine:run() + return s and routine, m end function kernel.raise(uid) @@ -314,9 +315,10 @@ local function init(...) for _,file in ipairs(files) do local level = file:match('(%d).%S+.lua') or 99 if tonumber(level) <= runLevel then + -- All init programs run under the original shell local s, m = shell.run(fs.combine(dir, file)) if not s then - error(m) + error(m, -1) end os.sleep(0) end @@ -331,7 +333,7 @@ local function init(...) shell.run('sys/apps/autorun.lua') local win = window.create(kernel.terminal, 1, 1, w, h, true) - local s, m = kernel.run({ + local s, m = kernel.run(_ENV, { title = args[1], path = 'sys/apps/shell.lua', args = args, @@ -349,7 +351,7 @@ local function init(...) end end -kernel.run({ +kernel.run(_ENV, { fn = init, title = 'init', args = { ... }, diff --git a/sys/modules/opus/crypto/chacha20.lua b/sys/modules/opus/crypto/chacha20.lua index 9db2d08..c98654c 100644 --- a/sys/modules/opus/crypto/chacha20.lua +++ b/sys/modules/opus/crypto/chacha20.lua @@ -100,7 +100,7 @@ local function crypt(data, key, nonce, cntr, round) cntr = tonumber(cntr) or 1 round = tonumber(round) or 20 - local throttle = Util.throttle(function() _syslog('throttle') end) + local throttle = Util.throttle() local out = {} local state = initState(key, nonce, cntr) local blockAmt = math.floor(#data/64) diff --git a/sys/modules/opus/event.lua b/sys/modules/opus/event.lua index 924461e..5560beb 100644 --- a/sys/modules/opus/event.lua +++ b/sys/modules/opus/event.lua @@ -58,6 +58,14 @@ function Routine:resume(event, ...) else s, m = coroutine.resume(self.co, event, ...) end + + if not s and event ~= 'terminate' then + if m and debug and debug.traceback then + local t = (debug.traceback(self.co, 1)) or '' + m = m .. '\n' .. t:match('%d\n(.+)') + end + end + if self:isDead() then self.co = nil self.filter = nil diff --git a/sys/modules/opus/trace.lua b/sys/modules/opus/trace.lua index c8cf2a9..bb12fde 100644 --- a/sys/modules/opus/trace.lua +++ b/sys/modules/opus/trace.lua @@ -32,95 +32,63 @@ local function traceback(x) end end -local function trim_traceback(target, marker) - local ttarget, tmarker = {}, {} - for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end - for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end +local function trim_traceback(target) + local t = { } + local filters = { + "%[C%]: in function 'xpcall'", + "(...tail calls...)", + "xpcall: $", + "trace.lua:%d+:", + } ---[[ - TODO : fix this trace - Anavrins - if you could take a look, it would be appreciated - -- basically i want the stack logged in a more readable - -- format and the normal code/error message to be returned - - -- unsure why the traceback method concatenates the stack - -- when it can just be returned as a table - would make - -- filtering much simpler - - -- the following seems to reduce the stacktrace way too - -- much - losing most of the relevant call stack - - -- i have modified this a bit from the original - not sure - -- if my changes are causing the issues or not - - -- Trim identical suffixes - local t_len, m_len = #ttarget, #tmarker - while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do - table.remove(ttarget, t_len) - t_len, m_len = t_len - 1, m_len - 1 + local function matchesFilter(line) + for _, filter in pairs(filters) do + if line:match(filter) then + return true + end + end end - -- Trim elements from this file and xpcall invocations - while t_len >= 1 and ttarget[t_len]:find("^\tstack_trace%.lua:%d+:") or - ttarget[t_len] == "\t[C]: in function 'xpcall'" or ttarget[t_len] == " xpcall: " do - table.remove(ttarget, t_len) - t_len = t_len - 1 + for line in target:gmatch("([^\n]*)\n?") do + if not matchesFilter(line) then + table.insert(t, line) + end end -]] - ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall - ttarget[#ttarget] = nil - return ttarget + return t end ---- Run a function with return function (fn, ...) - -- So this is rather grim: we need to get the full traceback and current one and remove - -- the common prefix - local trace - local args = { ... } - -- xpcall in Lua 5.1 does not accept parameters -- which is not ideal + local args = { ... } local res = table.pack(xpcall(function() return fn(table.unpack(args)) end, traceback)) - if not res[1] then - trace = traceback("trace.lua:1:") - end local ok, err = res[1], res[2] if not ok and err ~= nil then - trace = trim_traceback(err, trace) + local trace = trim_traceback(err) - -- Find the position where the stack traceback actually starts - local trace_starts - for i = #trace, 1, -1 do - if trace[i] == "stack traceback:" then trace_starts = i; break end - end - - _G._syslog('') - for _, line in pairs(trace) do - _G._syslog(line) - end - - -- If this traceback is more than 15 elements long, keep the first 9, last 5 - -- and put an ellipsis between the rest - local max = 10 - if trace_starts and #trace - trace_starts > max then - local keep_starts = trace_starts + 7 - for i = #trace - trace_starts - max, 0, -1 do - table.remove(trace, keep_starts + i) + err = { } + while true do + local line = table.remove(trace, 1) + if not line or line == 'stack traceback:' then + break + end + table.insert(err, line) + end + err = table.concat(err, '\n') + + _G._syslog('\n' .. err .. '\n' .. 'stack traceback:') + for _, v in ipairs(trace) do + if v ~= 'stack traceback:' then + _G._syslog(v:gsub("in function", "in")) end - table.insert(trace, keep_starts, " ...") end - for k, line in pairs(trace) do - trace[k] = line:gsub("in function", " in") - end - - return false, table.remove(trace, 1), table.concat(trace, "\n") + return ok, err end return table.unpack(res, 1, res.n) diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index fb67603..25c5083 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -117,7 +117,7 @@ function UI:init() if ie.code == 'control-shift-mouse_click' then -- hack local event = currentPage:pointToChild(x, y) - _ENV.multishell.openTab({ + _ENV.multishell.openTab(_ENV, { path = 'sys/apps/Lua.lua', args = { event.element, self, _ENV }, focused = true }) -- 2.49.1 From 2629f2a172f1ba2b6edf2e46ef291aa6c6b6257d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 11 May 2020 23:26:37 +0000 Subject: [PATCH 163/236] Update version date --- .opus_version | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 70f2f1d..4d6c03f 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,11 @@ -Sun May 10 20:04:56 UTC 2020 -commit a3a819256f4c4441a550eff1d06592e70a49f9ec +Mon May 11 23:26:36 UTC 2020 +commit 8279c1ae12b1417380c348f39f735a47395d12a9 Author: kepler155c@gmail.com -Date: Sun May 10 14:04:20 2020 -0600 +Date: Mon May 11 17:25:58 2020 -0600 - fix resizing scrolling terminals + environments, error messages, and stack traces, oh my! + Changed the way processes are launched. + multishell.openTab and kernel.run now accept the current environment as a parameter. + That new process inherits a copy of the passed environment. + Reduces complexity as the calling process is not required to create a suitable env. + Stack traces have been greatly improved and now include the stack for coroutines that error. -- 2.49.1 From 90ce2bb1a52ed416a2506f0ae96184ba442a00c6 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 12 May 2020 21:25:37 -0600 Subject: [PATCH 164/236] Improved error messages --- sys/apps/shell.lua | 6 ++--- sys/init/7.multishell.lua | 31 ++++++++++++++++++++------ sys/kernel.lua | 6 ++--- sys/modules/opus/trace.lua | 45 +++++++++++++++++++------------------- 4 files changed, 53 insertions(+), 35 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 92cf23d..52dcc33 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -52,8 +52,8 @@ local function run(...) loadFn = loadfile end - local fn, err = loadFn(path, env) - if not fn then + local bill, err = loadFn(path, env) + if not bill then error(err, -1) end @@ -68,7 +68,7 @@ local function run(...) } env[ "arg" ] = { [0] = path, table.unpack(args) } - local r = { fn(table.unpack(args)) } + local r = { bill(table.unpack(args)) } tProgramStack[#tProgramStack] = nil diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index 3d2f0ba..3930eae 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -106,17 +106,34 @@ function multishell.openTab(env, tab) end tab.title = tab.title or 'untitled' tab.window = tab.window or window.create(parentTerm, 1, 2, w, h - 1, false) - tab.onExit = tab.onExit or function(self, result, err) - if not result and err and err ~= 'Terminated' or (err and err ~= 0) then - self.terminal.setBackgroundColor(colors.black) + tab.onExit = tab.onExit or function(self, result, err, stack) + if not result and err and err ~= 'Terminated' then + self.terminal.setTextColor(colors.white) + self.terminal.setCursorBlink(false) + print('\nThe program terminated with an error.\n') if tonumber(err) then - self.terminal.setTextColor(colors.orange) - print('Process exited with error code: ' .. err) + printError('Process exited with error code: ' .. err) elseif err then printError(tostring(err)) end - self.terminal.setTextColor(colors.white) - print('\nPress enter to close') + if type(stack) == 'table' and #stack > 0 then + local _, cy = self.terminal.getCursorPos() + local _, th = self.terminal.getSize() + self.terminal.setTextColor(colors.white) + if cy < th - 4 then + print('\nstack traceback:') + for _, v in ipairs(stack or { }) do + _, cy = self.terminal.getCursorPos() + if cy > th - 3 then + print(' ...') + break + end + print(v) + end + end + end + self.terminal.setTextColor(parentTerm.isColor() and colors.yellow or colors.white) + _G.write('\nPress enter to close') self.isDead = true self.hidden = false redrawMenu() diff --git a/sys/kernel.lua b/sys/kernel.lua index 5f86bd4..446d227 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -110,7 +110,7 @@ end function Routine:run() self.co = self.co or coroutine.create(function() - local result, err, fn + local result, err, fn, stack if self.fn then fn = self.fn @@ -122,12 +122,12 @@ function Routine:run() end if fn then - result, err = trace(fn, table.unpack(self.args or { } )) + result, err, stack = trace(fn, table.unpack(self.args or { } )) else err = err or 'kernel: invalid routine' end - pcall(self.onExit, self, result, err) + pcall(self.onExit, self, result, err, stack) self:cleanup() if not result then diff --git a/sys/modules/opus/trace.lua b/sys/modules/opus/trace.lua index bb12fde..d003f48 100644 --- a/sys/modules/opus/trace.lua +++ b/sys/modules/opus/trace.lua @@ -32,15 +32,28 @@ local function traceback(x) end end -local function trim_traceback(target) - local t = { } +local function trim_traceback(stack) + local trace = { } local filters = { "%[C%]: in function 'xpcall'", "(...tail calls...)", "xpcall: $", "trace.lua:%d+:", + "stack traceback:", } + for line in stack:gmatch("([^\n]*)\n?") do table.insert(trace, line) end + + local err = { } + while true do + local line = table.remove(trace, 1) + if not line or line == 'stack traceback:' then + break + end + table.insert(err, line) + end + err = table.concat(err, '\n') + local function matchesFilter(line) for _, filter in pairs(filters) do if line:match(filter) then @@ -49,13 +62,15 @@ local function trim_traceback(target) end end - for line in target:gmatch("([^\n]*)\n?") do + local t = { } + for _, line in pairs(trace) do if not matchesFilter(line) then + line = line:gsub("in function", "in") table.insert(t, line) end end - return t + return err, t end return function (fn, ...) @@ -66,29 +81,15 @@ return function (fn, ...) return fn(table.unpack(args)) end, traceback)) - local ok, err = res[1], res[2] - - if not ok and err ~= nil then - local trace = trim_traceback(err) - - err = { } - while true do - local line = table.remove(trace, 1) - if not line or line == 'stack traceback:' then - break - end - table.insert(err, line) - end - err = table.concat(err, '\n') + if not res[1] and res[2] ~= nil then + local err, trace = trim_traceback(res[2]) _G._syslog('\n' .. err .. '\n' .. 'stack traceback:') for _, v in ipairs(trace) do - if v ~= 'stack traceback:' then - _G._syslog(v:gsub("in function", "in")) - end + _G._syslog(v) end - return ok, err + return res[1], err, trace end return table.unpack(res, 1, res.n) -- 2.49.1 From c7c594d6c3f11d9aaf09f1ade643d5393887321f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 May 2020 03:26:28 +0000 Subject: [PATCH 165/236] Update version date --- .opus_version | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.opus_version b/.opus_version index 4d6c03f..f1e3e3c 100644 --- a/.opus_version +++ b/.opus_version @@ -1,11 +1,6 @@ -Mon May 11 23:26:36 UTC 2020 -commit 8279c1ae12b1417380c348f39f735a47395d12a9 +Wed May 13 03:26:26 UTC 2020 +commit 90ce2bb1a52ed416a2506f0ae96184ba442a00c6 Author: kepler155c@gmail.com -Date: Mon May 11 17:25:58 2020 -0600 +Date: Tue May 12 21:25:37 2020 -0600 - environments, error messages, and stack traces, oh my! - Changed the way processes are launched. - multishell.openTab and kernel.run now accept the current environment as a parameter. - That new process inherits a copy of the passed environment. - Reduces complexity as the calling process is not required to create a suitable env. - Stack traces have been greatly improved and now include the stack for coroutines that error. + Improved error messages -- 2.49.1 From a7e331822622f973a736826b258019babc924af4 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 17 May 2020 19:36:33 -0600 Subject: [PATCH 166/236] add standard lua os methods, another fix for vfs links within links, allow write on urlfs mounted files --- sys/apps/shell.lua | 8 +++---- sys/init/2.vfs.lua | 25 --------------------- sys/init/3.sys.lua | 41 +++++++++++++++++++++++++++++++++- sys/init/7.multishell.lua | 3 +++ sys/modules/opus/fs/linkfs.lua | 7 ++++-- sys/modules/opus/fs/urlfs.lua | 18 +++++++++------ sys/modules/opus/injector.lua | 1 + 7 files changed, 64 insertions(+), 39 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 52dcc33..54eeb2c 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -52,8 +52,8 @@ local function run(...) loadFn = loadfile end - local bill, err = loadFn(path, env) - if not bill then + local funkshun, err = loadFn(path, env) + if not funkshun then error(err, -1) end @@ -68,7 +68,7 @@ local function run(...) } env[ "arg" ] = { [0] = path, table.unpack(args) } - local r = { bill(table.unpack(args)) } + local r = { funkshun(table.unpack(args)) } tProgramStack[#tProgramStack] = nil @@ -659,7 +659,7 @@ local function shellRead(history) return entry.value or '' end -local history = History.load('usr/.shell_history', 25) +local history = History.load('usr/.shell_history', 100) term.setBackgroundColor(_colors.backgroundColor) --term.clear() diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index b0e2ea7..e2406dc 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -100,26 +100,6 @@ end function nativefs.delete(node, dir) if node.mountPoint == dir then fs.unmount(dir) - -- hack here - -- if a file is mounted over an existing directory - -- ie. sys/apps/MOUNT.LUA - -- then sys and sys/apps are created as temp nodes - -- therefore, trying to delete sys was only - -- removing the node and not deleting the directory - -- Need a better way to backfill nodes in a way - -- that preserves the vfs functionality - - -- perhaps a flag that denotes that this - -- file/directory is the actual mount - - -- this hack will not fix - -- rm packages/common - -- where packages is linked from a drive - -- and urls are mounted under packages/common - -- (as the fstype will be linkfs) - if node.fstype == 'nativefs' then - fs.native.delete(dir) - end else fs.native.delete(dir) end @@ -316,11 +296,6 @@ function fs.mount(path, fstype, ...) end if not tp.nodes[d] then tp.nodes[d] = Util.shallowCopy(tp) - - if tp.nodes[d].source then - tp.nodes[d].source = fs.combine(tp.nodes[d].source, d) - end - tp.nodes[d].nodes = { } tp.nodes[d].mountPoint = fs.combine(tp.mountPoint, d) end diff --git a/sys/init/3.sys.lua b/sys/init/3.sys.lua index 7e18d18..ace7684 100644 --- a/sys/init/3.sys.lua +++ b/sys/init/3.sys.lua @@ -1,3 +1,42 @@ -local fs = _G.fs +local fs = _G.fs +local os = _G.os fs.loadTab('sys/etc/fstab') + +-- add some Lua compatibility functions +function os.remove(a) + if fs.exists(a) then + local s = pcall(fs.delete, a) + return s and true or nil, a .. ': Unable to remove file' + end + return nil, a .. ': No such file or directory' +end + +os.execute = function(cmd) + if not cmd then + return 1 + end + + local env = _G.getfenv(2) + local s, m = env.shell.run('sys/apps/shell.lua ' .. cmd) + + if not s then + return 1, m + end + + return 0 +end + +os.tmpname = function() + local fname + repeat + fname = 'tmp/a' .. math.random(1, 32768) + until not fs.exists(fname) + + return fname +end + +-- non-standard - will raise error instead +os.exit = function(code) + error('Terminated with ' .. code) +end diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index 3930eae..e7bb1e2 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -144,6 +144,9 @@ function multishell.openTab(env, tab) end end end + if tab.chainExit then + tab.chainExit(self, result, err, stack) + end end local routine, message = kernel.run(env, tab) diff --git a/sys/modules/opus/fs/linkfs.lua b/sys/modules/opus/fs/linkfs.lua index 8ffdb4c..813ce5f 100644 --- a/sys/modules/opus/fs/linkfs.lua +++ b/sys/modules/opus/fs/linkfs.lua @@ -5,7 +5,7 @@ local linkfs = { } -- TODO: implement broken links local methods = { 'exists', 'getFreeSpace', 'getSize', 'attributes', - 'isDir', 'isReadOnly', 'list', 'listEx', 'makeDir', 'open', 'getDrive' } + 'isDir', 'isReadOnly', 'list', 'makeDir', 'open', 'getDrive' } for _,m in pairs(methods) do linkfs[m] = function(node, dir, ...) @@ -18,7 +18,7 @@ function linkfs.resolve(node, dir) return dir:gsub(node.mountPoint, node.source, 1) end -function linkfs.mount(_, source) +function linkfs.mount(path, source) if not source then error('Source is required') end @@ -26,6 +26,9 @@ function linkfs.mount(_, source) if not fs.exists(source) then error('Source is missing') end + if path == source then + return + end if fs.isDir(source) then return { source = source, diff --git a/sys/modules/opus/fs/urlfs.lua b/sys/modules/opus/fs/urlfs.lua index 96b7854..b2c47ca 100644 --- a/sys/modules/opus/fs/urlfs.lua +++ b/sys/modules/opus/fs/urlfs.lua @@ -5,15 +5,19 @@ local fs = _G.fs local urlfs = { } -function urlfs.mount(_, url) +function urlfs.mount(path, url, force) if not url then error('URL is required') end - return { - url = url, - created = os.epoch('utc'), - modification = os.epoch('utc'), - } + + -- only mount if the file does not exist already + if not fs.exists(path) or force then + return { + url = url, + created = os.epoch('utc'), + modification = os.epoch('utc'), + } + end end function urlfs.attributes(node) @@ -38,7 +42,7 @@ function urlfs.getSize(node) end function urlfs.isReadOnly() - return true + return false end function urlfs.isDir() diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index aea109a..17a17f1 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -89,6 +89,7 @@ return function(env) os = os, string = string, table = table, + debug = debug, }, loaders = { preloadSearcher, -- 2.49.1 From b93d69c2610d6a40258130ec3877a56d44736975 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 May 2020 01:37:11 +0000 Subject: [PATCH 167/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index f1e3e3c..8e5393c 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Wed May 13 03:26:26 UTC 2020 -commit 90ce2bb1a52ed416a2506f0ae96184ba442a00c6 +Mon May 18 01:37:10 UTC 2020 +commit a7e331822622f973a736826b258019babc924af4 Author: kepler155c@gmail.com -Date: Tue May 12 21:25:37 2020 -0600 +Date: Sun May 17 19:36:33 2020 -0600 - Improved error messages + add standard lua os methods, another fix for vfs links within links, allow write on urlfs mounted files -- 2.49.1 From 985830fcfda832bfa4d2597297319490fe104c9d Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 19 May 2020 17:09:10 -0600 Subject: [PATCH 168/236] better fuzzy matching + vfs type flag in Files --- sys/apps/Files.lua | 19 +++++----- sys/apps/Help.lua | 13 +++---- sys/apps/PackageManager.lua | 6 ++-- sys/apps/shell.lua | 6 ++-- sys/init/2.vfs.lua | 17 ++++++--- sys/init/3.sys.lua | 38 +++++++++++--------- sys/init/6.packages.lua | 11 +++--- sys/modules/opus/fuzzy.lua | 71 +++++++++++++++++++++++++++---------- 8 files changed, 115 insertions(+), 66 deletions(-) diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 1529c6d..8cb09cc 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -77,8 +77,8 @@ local Browser = UI.Page { grid = UI.ScrollingGrid { columns = { { heading = 'Name', key = 'name' }, - { key = 'flags', width = 2 }, - { heading = 'Size', key = 'fsize', width = 5 }, + { key = 'flags', width = 3, textColor = 'lightGray' }, + { heading = 'Size', key = 'fsize', width = 5, textColor = 'yellow' }, }, sortColumn = 'name', y = 2, ey = -2, @@ -211,7 +211,7 @@ function Browser:enable() self:setFocus(self.grid) end -function Browser.menuBar:getActive(menuItem) +function Browser.menuBar.getActive(_, menuItem) local file = Browser.grid:getSelected() if menuItem.flags == FILE then return file and not file.isDir @@ -223,7 +223,7 @@ function Browser:setStatus(status, ...) self.notification:info(string.format(status, ...)) end -function Browser:unmarkAll() +function Browser.unmarkAll() for _,m in pairs(marked) do m.marked = false end @@ -263,10 +263,11 @@ function Browser:updateDirectory(dir) dir.size = #files for _, file in pairs(files) do file.fullName = fs.combine(dir.name, file.name) - file.flags = '' + file.flags = file.fstype or ' ' if not file.isDir then dir.totalSize = dir.totalSize + file.size file.fsize = formatSize(file.size) + file.flags = file.flags .. ' ' else if config.showDirSizes then file.size = fs.getSize(file.fullName, true) @@ -274,11 +275,9 @@ function Browser:updateDirectory(dir) dir.totalSize = dir.totalSize + file.size file.fsize = formatSize(file.size) end - file.flags = 'D' - end - if file.isReadOnly then - file.flags = file.flags .. 'R' + file.flags = file.flags .. 'D' end + file.flags = file.flags .. (file.isReadOnly and 'R' or ' ') if config.showHidden or file.name:sub(1, 1) ~= '.' then dir.files[file.fullName] = file end @@ -467,7 +466,7 @@ function Browser:eventHandler(event) elseif event.type == 'paste' then for _,m in pairs(copied) do - local s, m = pcall(function() + pcall(function() if cutMode then fs.move(m.fullName, fs.combine(self.dir.name, m.name)) else diff --git a/sys/apps/Help.lua b/sys/apps/Help.lua index ba530c8..429c356 100644 --- a/sys/apps/Help.lua +++ b/sys/apps/Help.lua @@ -1,3 +1,4 @@ +local fuzzy = require('opus.fuzzy') local UI = require('opus.ui') local Util = require('opus.util') @@ -42,13 +43,13 @@ UI:addPage('main', UI.Page { elseif event.type == 'text_change' then if not event.text then - self.grid.values = topics + self.grid.sortColumn = 'lname' else - self.grid.values = { } - for _,f in pairs(topics) do - if string.find(f.lname, event.text:lower()) then - table.insert(self.grid.values, f) - end + self.grid.sortColumn = 'score' + self.grid.inverseSort = false + local pattern = event.text:lower() + for _,v in pairs(self.grid.values) do + v.score = -fuzzy(v.lname, pattern) end end self.grid:update() diff --git a/sys/apps/PackageManager.lua b/sys/apps/PackageManager.lua index ec8d9ea..4140732 100644 --- a/sys/apps/PackageManager.lua +++ b/sys/apps/PackageManager.lua @@ -41,7 +41,7 @@ local page = UI.Page { }, description = UI.TextArea { x = 16, y = 3, ey = -5, - marginRight = 0, marginLeft = 0, + marginRight = 2, marginLeft = 0, }, action = UI.SlideOut { titleBar = UI.TitleBar { @@ -140,9 +140,9 @@ function page:eventHandler(event) elseif event.type == 'grid_focus_row' then local manifest = event.selected.manifest - self.description.value = string.format('%s%s\n\n%s%s', + self.description:setValue(string.format('%s%s\n\n%s%s', Ansi.yellow, manifest.title, - Ansi.white, manifest.description) + Ansi.white, manifest.description)) self.description:draw() self:updateSelection(event.selected) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 54eeb2c..4f8d659 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -52,8 +52,8 @@ local function run(...) loadFn = loadfile end - local funkshun, err = loadFn(path, env) - if not funkshun then + local O_v_O, err = loadFn(path, env) + if not O_v_O then error(err, -1) end @@ -68,7 +68,7 @@ local function run(...) } env[ "arg" ] = { [0] = path, table.unpack(args) } - local r = { funkshun(table.unpack(args)) } + local r = { O_v_O(table.unpack(args)) } tProgramStack[#tProgramStack] = nil diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index e2406dc..816f38f 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -166,6 +166,13 @@ function fs.complete(partial, dir, includeFiles, includeSlash) return fs.native.complete(partial, dir, includeFiles, includeSlash) end +local displayFlags = { + urlfs = 'U', + linkfs = 'L', + ramfs = 'T', + netfs = 'N', +} + function fs.listEx(dir) dir = fs.combine(dir, '') local node = getNode(dir) @@ -176,20 +183,22 @@ function fs.listEx(dir) local t = { } local files = node.fs.list(node, dir) - pcall(function() - for _,f in ipairs(files) do + for _,f in ipairs(files) do + pcall(function() local fullName = fs.combine(dir, f) + local n = fs.getNode(fullName) local file = { name = f, isDir = fs.isDir(fullName), isReadOnly = fs.isReadOnly(fullName), + fstype = n.mountPoint == fullName and displayFlags[n.fstype], } if not file.isDir then file.size = fs.getSize(fullName) end table.insert(t, file) - end - end) + end) + end return t end diff --git a/sys/init/3.sys.lua b/sys/init/3.sys.lua index ace7684..d43bf53 100644 --- a/sys/init/3.sys.lua +++ b/sys/init/3.sys.lua @@ -6,37 +6,41 @@ fs.loadTab('sys/etc/fstab') -- add some Lua compatibility functions function os.remove(a) if fs.exists(a) then - local s = pcall(fs.delete, a) - return s and true or nil, a .. ': Unable to remove file' + local s = pcall(fs.delete, a) + return s and true or nil, a .. ': Unable to remove file' end return nil, a .. ': No such file or directory' end os.execute = function(cmd) - if not cmd then - return 1 - end + local env = _G.getfenv(2) + if not cmd then + return env.shell and 1 or 0 + end - local env = _G.getfenv(2) - local s, m = env.shell.run('sys/apps/shell.lua ' .. cmd) + if not env.shell then + return 0 + end - if not s then - return 1, m - end + local s, m = env.shell.run('sys/apps/shell.lua ' .. cmd) - return 0 + if not s then + return 1, m + end + + return 0 end os.tmpname = function() - local fname - repeat - fname = 'tmp/a' .. math.random(1, 32768) - until not fs.exists(fname) + local fname + repeat + fname = 'tmp/a' .. math.random(1, 32768) + until not fs.exists(fname) - return fname + return fname end -- non-standard - will raise error instead os.exit = function(code) - error('Terminated with ' .. code) + error('Terminated with ' .. code) end diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index 3ad43ab..ece54fd 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -13,7 +13,13 @@ table.insert(helpPaths, '/sys/help') for name in pairs(Packages:installed()) do local packageDir = fs.combine('packages', name) + local fstabPath = fs.combine(packageDir, 'etc/fstab') + if fs.exists(fstabPath) then + fs.loadTab(fstabPath) + end + table.insert(appPaths, 1, '/' .. packageDir) + local apiPath = fs.combine(packageDir, 'apis') -- TODO: rename dir to 'modules' (someday) if fs.exists(apiPath) then fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath) @@ -23,11 +29,6 @@ for name in pairs(Packages:installed()) do if fs.exists(helpPath) then table.insert(helpPaths, helpPath) end - - local fstabPath = fs.combine(packageDir, 'etc/fstab') - if fs.exists(fstabPath) then - fs.loadTab(fstabPath) - end end help.setPath(table.concat(helpPaths, ':')) diff --git a/sys/modules/opus/fuzzy.lua b/sys/modules/opus/fuzzy.lua index c71ac08..353c142 100644 --- a/sys/modules/opus/fuzzy.lua +++ b/sys/modules/opus/fuzzy.lua @@ -1,21 +1,56 @@ --- Based on Squid's fuzzy search --- https://github.com/SquidDev-CC/artist/blob/vnext/artist/lib/match.lua --- --- not very fuzzy anymore +local find = string.find +local floor = math.floor +local min = math.min +local max = math.max +local sub = string.sub -local SCORE_WEIGHT = 1000 -local LEADING_LETTER_PENALTY = -30 -local LEADING_LETTER_PENALTY_MAX = -90 - -local _find = string.find -local _max = math.max - -return function(str, pattern) - local start = _find(str, pattern, 1, true) - if start then - -- All letters before the current one are considered leading, so add them to our penalty - return SCORE_WEIGHT - + _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX) - - (#str - #pattern) +-- https://rosettacode.org/wiki/Jaro_distance (ported to lua) +return function(s1, s2) + local l1, l2 = #s1, #s2; + if l1 == 0 then + return l2 == 0 and 1.0 or 0.0 end + + local match_distance = max(floor(max(l1, l2) / 2) - 1, 0) + local s1_matches = { } + local s2_matches = { } + local matches = 0 + + for i = 1, l1 do + local _end = min(i + match_distance + 1, l2) + for k = max(1, i - match_distance), _end do + if not s2_matches[k] and sub(s1, i, i) == sub(s2, k, k) then + s1_matches[i] = true + s2_matches[k] = true + matches = matches + 1 + break + end + end + end + if matches == 0 then + return 0.0 + end + + local t = 0.0 + local k = 1 + for i = 1, l1 do + if s1_matches[i] then + while not s2_matches[k] do + k = k + 1 + end + if sub(s1, i, i) ~= sub(s2, k, k) then + t = t + 0.5 + end + k = k + 1 + end + end + + -- provide a major boost for exact matches + local b = 0.0 + if find(s1, s2, 1, true) then + b = b + .5 + end + + local m = matches + return (m / l1 + m / l2 + (m - t) / m) / 3.0 + b end -- 2.49.1 From 97bfae10fbc88add98e544f16a254d5c178cfafb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 19 May 2020 23:09:31 +0000 Subject: [PATCH 169/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 8e5393c..0bdd013 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Mon May 18 01:37:10 UTC 2020 -commit a7e331822622f973a736826b258019babc924af4 +Tue May 19 23:09:30 UTC 2020 +commit 985830fcfda832bfa4d2597297319490fe104c9d Author: kepler155c@gmail.com -Date: Sun May 17 19:36:33 2020 -0600 +Date: Tue May 19 17:09:10 2020 -0600 - add standard lua os methods, another fix for vfs links within links, allow write on urlfs mounted files + better fuzzy matching + vfs type flag in Files -- 2.49.1 From 26bbb509815c46f5cabec45e0c3e8bcab5b43322 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 23 May 2020 21:45:05 -0600 Subject: [PATCH 170/236] debugger support --- sys/autorun/version.lua | 80 ++++++------- sys/boot/opus.boot | 15 +++ sys/init/3.sys.lua | 2 +- sys/modules/opus/fuzzy.lua | 34 +++--- sys/modules/opus/trace.lua | 18 ++- .../opus/ui/components/QuickSelect.lua | 107 ++++++++++++++++++ 6 files changed, 194 insertions(+), 62 deletions(-) create mode 100644 sys/modules/opus/ui/components/QuickSelect.lua diff --git a/sys/autorun/version.lua b/sys/autorun/version.lua index 7c5e5b2..798e8ec 100644 --- a/sys/autorun/version.lua +++ b/sys/autorun/version.lua @@ -7,47 +7,47 @@ local shell = _ENV.shell local URL = 'https://raw.githubusercontent.com/kepler155c/opus/%s/.opus_version' if fs.exists('.opus_version') then - local f = fs.open('.opus_version', 'r') - local date = f.readLine() - f.close() - date = type(date) == 'string' and Util.split(date)[1] + local f = fs.open('.opus_version', 'r') + local date = f.readLine() + f.close() + date = type(date) == 'string' and Util.split(date)[1] - local today = os.date('%j') - local config = Config.load('version', { - packages = date, - checked = today, - }) + local today = os.date('%j') + local config = Config.load('version', { + packages = date, + checked = today, + }) - -- check if packages need an update - if date ~= config.packages then - config.packages = date - Config.update('version', config) - print('Updating packages') - shell.run('package updateall') - os.reboot() - end + -- check if packages need an update + if date ~= config.packages then + config.packages = date + Config.update('version', config) + print('Updating packages') + shell.run('package updateall') + os.reboot() + end - if type(date) == 'string' and #date > 0 then - if config.checked ~= today then - config.checked = today - Config.update('version', config) - print('Checking for new version') - pcall(function() - local c = Util.httpGet(string.format(URL, _G.OPUS_BRANCH)) - if c then - local lines = Util.split(c) - local revdate = table.remove(lines, 1) - if date ~= revdate and config.skip ~= revdate then - config.current = revdate - config.details = table.concat(lines, '\n') - Config.update('version', config) - print('New version available') - if _ENV.multishell then - shell.openForegroundTab('sys/apps/Version.lua') - end - end - end - end) - end - end + if type(date) == 'string' and #date > 0 then + if config.checked ~= today then + config.checked = today + Config.update('version', config) + print('Checking for new version') + pcall(function() + local c = Util.httpGet(string.format(URL, _G.OPUS_BRANCH)) + if c then + local lines = Util.split(c) + local revdate = table.remove(lines, 1) + if date ~= revdate and config.skip ~= revdate then + config.current = revdate + config.details = table.concat(lines, '\n') + Config.update('version', config) + print('New version available') + if _ENV.multishell then + shell.openForegroundTab('sys/apps/Version.lua') + end + end + end + end) + end + end end diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index 2a02d14..da4a3c4 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -1,5 +1,20 @@ local fs = _G.fs +-- override bios function to include the actual filename +function _G.loadfile(filename, env) + -- Support the previous `loadfile(filename, env)` form instead. + if type(mode) == "table" and env == nil then + mode, env = nil, mode + end + + local file = fs.open(filename, "r") + if not file then return nil, "File not found" end + + local func, err = load(file.readAll(), '@' .. filename, mode, env) + file.close() + return func, err +end + local sandboxEnv = setmetatable({ }, { __index = _G }) for k,v in pairs(_ENV) do sandboxEnv[k] = v diff --git a/sys/init/3.sys.lua b/sys/init/3.sys.lua index d43bf53..e8103d4 100644 --- a/sys/init/3.sys.lua +++ b/sys/init/3.sys.lua @@ -42,5 +42,5 @@ end -- non-standard - will raise error instead os.exit = function(code) - error('Terminated with ' .. code) + error(code) end diff --git a/sys/modules/opus/fuzzy.lua b/sys/modules/opus/fuzzy.lua index 353c142..232af5a 100644 --- a/sys/modules/opus/fuzzy.lua +++ b/sys/modules/opus/fuzzy.lua @@ -7,41 +7,41 @@ local sub = string.sub -- https://rosettacode.org/wiki/Jaro_distance (ported to lua) return function(s1, s2) local l1, l2 = #s1, #s2; - if l1 == 0 then + if l1 == 0 then return l2 == 0 and 1.0 or 0.0 end local match_distance = max(floor(max(l1, l2) / 2) - 1, 0) - local s1_matches = { } - local s2_matches = { } - local matches = 0 + local s1_matches = { } + local s2_matches = { } + local matches = 0 for i = 1, l1 do - local _end = min(i + match_distance + 1, l2) + local _end = min(i + match_distance + 1, l2) for k = max(1, i - match_distance), _end do - if not s2_matches[k] and sub(s1, i, i) == sub(s2, k, k) then - s1_matches[i] = true - s2_matches[k] = true - matches = matches + 1 - break + if not s2_matches[k] and sub(s1, i, i) == sub(s2, k, k) then + s1_matches[i] = true + s2_matches[k] = true + matches = matches + 1 + break end end end - if matches == 0 then + if matches == 0 then return 0.0 end - local t = 0.0 - local k = 1 + local t = 0.0 + local k = 1 for i = 1, l1 do - if s1_matches[i] then + if s1_matches[i] then while not s2_matches[k] do k = k + 1 end if sub(s1, i, i) ~= sub(s2, k, k) then t = t + 0.5 end - k = k + 1 + k = k + 1 end end @@ -51,6 +51,6 @@ return function(s1, s2) b = b + .5 end - local m = matches - return (m / l1 + m / l2 + (m - t) / m) / 3.0 + b + local m = matches + return (m / l1 + m / l2 + (m - t) / m) / 3.0 + b end diff --git a/sys/modules/opus/trace.lua b/sys/modules/opus/trace.lua index d003f48..ed2738a 100644 --- a/sys/modules/opus/trace.lua +++ b/sys/modules/opus/trace.lua @@ -12,6 +12,10 @@ local function traceback(x) return x end + if x and x:match(':%d+: 0$') then + return x + end + if debug_traceback then -- The parens are important, as they prevent a tail call occuring, meaning -- the stack level is preserved. This ensures the code behaves identically @@ -65,7 +69,7 @@ local function trim_traceback(stack) local t = { } for _, line in pairs(trace) do if not matchesFilter(line) then - line = line:gsub("in function", "in") + line = line:gsub("in function", "in"):gsub('%w+/', '') table.insert(t, line) end end @@ -84,9 +88,15 @@ return function (fn, ...) if not res[1] and res[2] ~= nil then local err, trace = trim_traceback(res[2]) - _G._syslog('\n' .. err .. '\n' .. 'stack traceback:') - for _, v in ipairs(trace) do - _G._syslog(v) + if #trace > 0 then + _G._syslog('\n' .. err .. '\n' .. 'stack traceback:') + for _, v in ipairs(trace) do + _G._syslog(v) + end + end + + if err:match(':%d+: 0$') then + return true end return res[1], err, trace diff --git a/sys/modules/opus/ui/components/QuickSelect.lua b/sys/modules/opus/ui/components/QuickSelect.lua new file mode 100644 index 0000000..dc96979 --- /dev/null +++ b/sys/modules/opus/ui/components/QuickSelect.lua @@ -0,0 +1,107 @@ +local class = require('opus.class') +local fuzzy = require('opus.fuzzy') +local UI = require('opus.ui') + +local fs = _G.fs +local _insert = table.insert + +UI.QuickSelect = class(UI.Window) +UI.QuickSelect.defaults = { + UIElement = 'QuickSelect', +} +function UI.QuickSelect:postInit() + self.filterEntry = UI.TextEntry { + x = 2, y = 2, ex = -2, + shadowText = 'File name', + accelerators = { + [ 'enter' ] = 'accept', + [ 'up' ] = 'grid_up', + [ 'down' ] = 'grid_down', + }, + } + self.grid = UI.ScrollingGrid { + x = 2, y = 3, ex = -2, ey = -4, + disableHeader = true, + columns = { + { key = 'name' }, + { key = 'dir', textColor = 'lightGray' }, + }, + accelerators = { + grid_select = 'accept', + }, + } + self.cancel = UI.Button { + x = -9, y = -2, + text = 'Cancel', + event = 'select_cancel', + } +end + +function UI.QuickSelect:draw() + self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), 'black', 'gray') + self:drawChildren() +end + +function UI.QuickSelect:applyFilter(filter) + if filter then + filter = filter:lower() + self.grid.sortColumn = 'score' + + for _,v in pairs(self.grid.values) do + v.score = -fuzzy(v.lname, filter) + end + else + self.grid.sortColumn = 'lname' + end + + self.grid:update() + self.grid:setIndex(1) +end + +function UI.QuickSelect:enable() + self.grid.values = { } + local function recurse(dir) + local files = fs.list(dir) + for _,f in ipairs(files) do + local fullName = fs.combine(dir, f) + if fs.native.isDir(fullName) then -- skip virtual dirs + if f ~= '.git' then recurse(fullName) end + else + _insert(self.grid.values, { + name = f, + dir = dir, + lname = f:lower(), + fullName = fullName, + }) + end + end + end + recurse('') + self:applyFilter() + self.filterEntry:reset() + UI.Window.enable(self) +end + +function UI.QuickSelect:eventHandler(event) + if event.type == 'grid_up' then + self.grid:emit({ type = 'scroll_up' }) + return true + + elseif event.type == 'grid_down' then + self.grid:emit({ type = 'scroll_down' }) + return true + + elseif event.type == 'accept' then + local sel = self.grid:getSelected() + if sel then + self:emit({ type = 'select_file', file = sel.fullName, element = self }) + end + return true + + elseif event.type == 'text_change' then + self:applyFilter(event.text) + self.grid:draw() + return true + + end +end -- 2.49.1 From 1cce4aad034180d88c53be2fd01cae8502f9e1f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 May 2020 03:45:25 +0000 Subject: [PATCH 171/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 0bdd013..b452f68 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Tue May 19 23:09:30 UTC 2020 -commit 985830fcfda832bfa4d2597297319490fe104c9d +Sun May 24 03:45:25 UTC 2020 +commit 26bbb509815c46f5cabec45e0c3e8bcab5b43322 Author: kepler155c@gmail.com -Date: Tue May 19 17:09:10 2020 -0600 +Date: Sat May 23 21:45:05 2020 -0600 - better fuzzy matching + vfs type flag in Files + debugger support -- 2.49.1 From a4f4f34576e4c512ca0b40a397d1aa564cf27135 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 25 May 2020 21:48:37 -0600 Subject: [PATCH 172/236] minor cleanup --- sys/init/3.sys.lua | 2 +- sys/modules/opus/event.lua | 2 +- sys/modules/opus/injector.lua | 10 ++++++---- sys/modules/opus/trace.lua | 10 ++++------ sys/modules/opus/ui/components/Button.lua | 2 ++ 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sys/init/3.sys.lua b/sys/init/3.sys.lua index e8103d4..5a5583a 100644 --- a/sys/init/3.sys.lua +++ b/sys/init/3.sys.lua @@ -42,5 +42,5 @@ end -- non-standard - will raise error instead os.exit = function(code) - error(code) + error(code or 0) end diff --git a/sys/modules/opus/event.lua b/sys/modules/opus/event.lua index 5560beb..cd57b0f 100644 --- a/sys/modules/opus/event.lua +++ b/sys/modules/opus/event.lua @@ -60,7 +60,7 @@ function Routine:resume(event, ...) end if not s and event ~= 'terminate' then - if m and debug and debug.traceback then + if m and type(debug) == 'table' and debug.traceback then local t = (debug.traceback(self.co, 1)) or '' m = m .. '\n' .. t:match('%d\n(.+)') end diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index 17a17f1..266d5a8 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -1,3 +1,5 @@ +-- https://www.lua.org/manual/5.1/manual.html#pdf-require + local function split(str, pattern) local t = { } local function helper(line) table.insert(t, line) return "" end @@ -65,14 +67,14 @@ return function(env) local sPath = string.gsub(pattern, "%?", fname) -- TODO: if there's no shell, we should not be checking relative paths below -- as they will resolve to root directory - if env.shell and - type(env.shell.getRunningProgram) == 'function' and - sPath:sub(1, 1) ~= "/" then + if env.shell + and type(env.shell.getRunningProgram) == 'function' + and sPath:sub(1, 1) ~= "/" then sPath = fs.combine(fs.getDir(env.shell.getRunningProgram() or ''), sPath) end if fs.exists(sPath) and not fs.isDir(sPath) then - return loadfile(sPath, env) + return loadfile(fs.combine(sPath, ''), env) end end end diff --git a/sys/modules/opus/trace.lua b/sys/modules/opus/trace.lua index ed2738a..8f669d2 100644 --- a/sys/modules/opus/trace.lua +++ b/sys/modules/opus/trace.lua @@ -78,8 +78,6 @@ local function trim_traceback(stack) end return function (fn, ...) - -- xpcall in Lua 5.1 does not accept parameters - -- which is not ideal local args = { ... } local res = table.pack(xpcall(function() return fn(table.unpack(args)) @@ -88,6 +86,10 @@ return function (fn, ...) if not res[1] and res[2] ~= nil then local err, trace = trim_traceback(res[2]) + if err:match(':%d+: 0$') then + return true + end + if #trace > 0 then _G._syslog('\n' .. err .. '\n' .. 'stack traceback:') for _, v in ipairs(trace) do @@ -95,10 +97,6 @@ return function (fn, ...) end end - if err:match(':%d+: 0$') then - return true - end - return res[1], err, trace end diff --git a/sys/modules/opus/ui/components/Button.lua b/sys/modules/opus/ui/components/Button.lua index 426d3e0..f7f339c 100644 --- a/sys/modules/opus/ui/components/Button.lua +++ b/sys/modules/opus/ui/components/Button.lua @@ -19,6 +19,8 @@ UI.Button.defaults = { [ ' ' ] = 'button_activate', enter = 'button_activate', mouse_click = 'button_activate', + mouse_doubleclick = 'button_activate', + mouse_tripleclick = 'button_activate', } } function UI.Button:layout() -- 2.49.1 From a7069f7ea8f585c1b06bf64d828c88a81ca05adc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 May 2020 03:48:56 +0000 Subject: [PATCH 173/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index b452f68..6857924 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sun May 24 03:45:25 UTC 2020 -commit 26bbb509815c46f5cabec45e0c3e8bcab5b43322 +Tue May 26 03:48:55 UTC 2020 +commit a4f4f34576e4c512ca0b40a397d1aa564cf27135 Author: kepler155c@gmail.com -Date: Sat May 23 21:45:05 2020 -0600 +Date: Mon May 25 21:48:37 2020 -0600 - debugger support + minor cleanup -- 2.49.1 From 7b225a7747c84e907160cba5c7e3a256ebf51ce6 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 28 May 2020 20:10:01 -0600 Subject: [PATCH 174/236] proper fix for builtin broken http.get --- sys/init/1.http.lua | 35 +++++++++++++++++++++++++++++++++++ sys/modules/opus/util.lua | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 sys/init/1.http.lua diff --git a/sys/init/1.http.lua b/sys/init/1.http.lua new file mode 100644 index 0000000..4fe167f --- /dev/null +++ b/sys/init/1.http.lua @@ -0,0 +1,35 @@ +--[[ +FIX for http.get +currently, when 2 requests are generated at same time (diff coroutines) they both +will receive the same file handle. This change will ensure each request gets the +proper response. +--]] + +local http = _G.http + +local reqs = { } + +local function wrapRequest(_url, ...) + local ok, err = http.request(...) + if ok then + while true do + local event, param1, param2, param3 = os.pullEvent() + + if event == "http_success" + and param1 == _url + and not reqs[tostring(param2)] then + + reqs[tostring(param2)] = true + return param2 + + elseif event == "http_failure" and param1 == _url then + return nil, param2, param3 + end + end + end + return nil, err +end + +http.safeGet = function(_url, _headers, _binary) + return wrapRequest(_url, _url, nil, _headers, _binary) +end diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index c1c152f..058529d 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -536,7 +536,7 @@ end --[[ loading and running functions ]] -- function Util.httpGet(url, headers, isBinary) - local h, msg = http.get(url, headers, isBinary) + local h, msg = http.safeGet(url, headers, isBinary) if h then local contents = h.readAll() h.close() -- 2.49.1 From f38afbbd3634c33284bdb1871263b3989bc0e5c2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 02:10:25 +0000 Subject: [PATCH 175/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 6857924..ab7ab58 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Tue May 26 03:48:55 UTC 2020 -commit a4f4f34576e4c512ca0b40a397d1aa564cf27135 +Fri May 29 02:10:25 UTC 2020 +commit 7b225a7747c84e907160cba5c7e3a256ebf51ce6 Author: kepler155c@gmail.com -Date: Mon May 25 21:48:37 2020 -0600 +Date: Thu May 28 20:10:01 2020 -0600 - minor cleanup + proper fix for builtin broken http.get -- 2.49.1 From 92972236409f2161487a332355188ff0c95eaaf5 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 28 May 2020 20:15:19 -0600 Subject: [PATCH 176/236] proper fix for builtin broken http.get - try 2 --- sys/init/1.http.lua | 35 ----------------------------------- sys/modules/opus/util.lua | 29 +++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 35 deletions(-) delete mode 100644 sys/init/1.http.lua diff --git a/sys/init/1.http.lua b/sys/init/1.http.lua deleted file mode 100644 index 4fe167f..0000000 --- a/sys/init/1.http.lua +++ /dev/null @@ -1,35 +0,0 @@ ---[[ -FIX for http.get -currently, when 2 requests are generated at same time (diff coroutines) they both -will receive the same file handle. This change will ensure each request gets the -proper response. ---]] - -local http = _G.http - -local reqs = { } - -local function wrapRequest(_url, ...) - local ok, err = http.request(...) - if ok then - while true do - local event, param1, param2, param3 = os.pullEvent() - - if event == "http_success" - and param1 == _url - and not reqs[tostring(param2)] then - - reqs[tostring(param2)] = true - return param2 - - elseif event == "http_failure" and param1 == _url then - return nil, param2, param3 - end - end - end - return nil, err -end - -http.safeGet = function(_url, _headers, _binary) - return wrapRequest(_url, _url, nil, _headers, _binary) -end diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 058529d..3d69dcf 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -13,6 +13,35 @@ local _unpack = table.unpack local _bor = bit32.bor local _bxor = bit32.bxor +if not http.safeGet then -- really no good place to put this hack + local reqs = { } + + local function wrapRequest(_url, ...) + local ok, err = http.request(...) + if ok then + while true do + local event, param1, param2, param3 = os.pullEvent() + + if event == "http_success" + and param1 == _url + and not reqs[tostring(param2)] then + + reqs[tostring(param2)] = true + return param2 + + elseif event == "http_failure" and param1 == _url then + return nil, param2, param3 + end + end + end + return nil, err + end + + http.safeGet = function(_url, _headers, _binary) + return wrapRequest(_url, _url, nil, _headers, _binary) + end +end + local byteArrayMT byteArrayMT = { __tostring = function(a) return string.char(_unpack(a)) end, -- 2.49.1 From f00bece02bd3c31ae0b04dd6a76b50e31192f305 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 02:15:35 +0000 Subject: [PATCH 177/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index ab7ab58..8134478 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Fri May 29 02:10:25 UTC 2020 -commit 7b225a7747c84e907160cba5c7e3a256ebf51ce6 +Fri May 29 02:15:34 UTC 2020 +commit 92972236409f2161487a332355188ff0c95eaaf5 Author: kepler155c@gmail.com -Date: Thu May 28 20:10:01 2020 -0600 +Date: Thu May 28 20:15:19 2020 -0600 - proper fix for builtin broken http.get + proper fix for builtin broken http.get - try 2 -- 2.49.1 From d1565c62e0af2ecff2d9f469bf933af7d693c93a Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 28 May 2020 22:54:48 -0600 Subject: [PATCH 178/236] oops in loadfile replacement --- sys/boot/opus.boot | 2 +- sys/modules/opus/util.lua | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index da4a3c4..f189ad5 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -1,7 +1,7 @@ local fs = _G.fs -- override bios function to include the actual filename -function _G.loadfile(filename, env) +function _G.loadfile(filename, mode, env) -- Support the previous `loadfile(filename, env)` form instead. if type(mode) == "table" and env == nil then mode, env = nil, mode diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 3d69dcf..ec3c7d6 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -13,6 +13,7 @@ local _unpack = table.unpack local _bor = bit32.bor local _bxor = bit32.bxor +-- support multiple simultaneous gets for same url if not http.safeGet then -- really no good place to put this hack local reqs = { } -- 2.49.1 From b6f439e8dc306f65b4f87f80354434cfdc1ff9b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 May 2020 04:55:20 +0000 Subject: [PATCH 179/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 8134478..2a53284 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Fri May 29 02:15:34 UTC 2020 -commit 92972236409f2161487a332355188ff0c95eaaf5 +Fri May 29 04:55:19 UTC 2020 +commit 41c0758857a105728f26e982f6c588ada75803bc Author: kepler155c@gmail.com -Date: Thu May 28 20:15:19 2020 -0600 +Date: Thu May 28 22:54:53 2020 -0600 - proper fix for builtin broken http.get - try 2 + Merge branch 'develop-1.8' of https://github.com/kepler155c/opus into develop-1.8 -- 2.49.1 From 21057995248b7128d8772f93c024e9db2d378f77 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 30 May 2020 20:05:53 -0600 Subject: [PATCH 180/236] minor cleanup --- sys/apps/Overview.lua | 1 + sys/etc/fstab | 1 - sys/init/7.multishell.lua | 27 +++++-- sys/kernel.lua | 4 +- sys/modules/opus/terminal.lua | 73 +++++++++++++++++++ sys/modules/opus/ui.lua | 2 +- sys/modules/opus/ui/components/TabBar.lua | 2 - .../opus/ui/components/TabBarMenuItem.lua | 3 +- sys/modules/opus/ui/components/Tabs.lua | 4 + 9 files changed, 104 insertions(+), 13 deletions(-) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index b670fc2..d8cb0ef 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -97,6 +97,7 @@ local page = UI.Page { width = 8, selectedBackgroundColor = 'primary', backgroundColor = 'tertiary', + unselectedTextColor = 'lightGray', layout = function(self) self.height = nil UI.TabBar.layout(self) diff --git a/sys/etc/fstab b/sys/etc/fstab index 60fb9e1..76f0a4e 100644 --- a/sys/etc/fstab +++ b/sys/etc/fstab @@ -2,5 +2,4 @@ sys/apps/pain.lua urlfs https://github.com/LDDestroier/CC/raw/master/pain.lua sys/apps/update.lua urlfs http://pastebin.com/raw/UzGHLbNC sys/apps/Enchat.lua urlfs https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua sys/apps/cloud.lua urlfs https://cloud-catcher.squiddev.cc/cloud.lua -sys/apps/nfttrans.lua urlfs https://pastebin.com/raw/e8XrzeDY rom/modules/main/opus linkfs sys/modules/opus \ No newline at end of file diff --git a/sys/init/7.multishell.lua b/sys/init/7.multishell.lua index e7bb1e2..728a380 100644 --- a/sys/init/7.multishell.lua +++ b/sys/init/7.multishell.lua @@ -100,13 +100,33 @@ function multishell.launch(env, path, ...) }) end +local function chain(orig, fn) + if not orig then + return fn + end + + if type(orig) == 'table' then + table.insert(orig, fn) + return orig + end + + return setmetatable({ orig, fn }, { + __call = function(self, ...) + for _,v in pairs(self) do + v(...) + end + end + }) +end + function multishell.openTab(env, tab) if not tab.title and tab.path then tab.title = fs.getName(tab.path):match('([^%.]+)') end tab.title = tab.title or 'untitled' tab.window = tab.window or window.create(parentTerm, 1, 2, w, h - 1, false) - tab.onExit = tab.onExit or function(self, result, err, stack) + -- require('opus.terminal').window(parentTerm, 1, 2, w, h - 1, false) + tab.onExit = chain(tab.onExit, function(self, result, err, stack) if not result and err and err ~= 'Terminated' then self.terminal.setTextColor(colors.white) self.terminal.setCursorBlink(false) @@ -144,10 +164,7 @@ function multishell.openTab(env, tab) end end end - if tab.chainExit then - tab.chainExit(self, result, err, stack) - end - end + end) local routine, message = kernel.run(env, tab) diff --git a/sys/kernel.lua b/sys/kernel.lua index 446d227..3a86708 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -29,10 +29,8 @@ local focusedRoutineEvents = Util.transpose { } _G._syslog = function(pattern, ...) - local oldTerm = term.redirect(kernel.window) kernel.window.scrollBottom() - Util.print(pattern, ...) - term.redirect(oldTerm) + kernel.window.print(Util.tostring(pattern, ...)) end -- any function that runs in a kernel hook does not run in diff --git a/sys/modules/opus/terminal.lua b/sys/modules/opus/terminal.lua index 21fce97..88f4815 100644 --- a/sys/modules/opus/terminal.lua +++ b/sys/modules/opus/terminal.lua @@ -277,6 +277,79 @@ function Terminal.window(parent, sx, sy, w, h, isVisible) return parent end + function win.writeX(sText) + -- expect(1, sText, "string", "number") + local nLinesPrinted = 0 + local function newLine() + if cy + 1 <= win.canvas.height then + cx, cy = 1, cy + 1 + else + cx, cy = 1, win.canvas.height + win.scroll(1) + end + nLinesPrinted = nLinesPrinted + 1 + end + + -- Print the line with proper word wrapping + sText = tostring(sText) + while #sText > 0 do + local whitespace = string.match(sText, "^[ \t]+") + if whitespace then + -- Print whitespace + win.write(whitespace) + sText = string.sub(sText, #whitespace + 1) + end + + local newline = string.match(sText, "^\n") + if newline then + -- Print newlines + newLine() + sText = string.sub(sText, 2) + end + + local text = string.match(sText, "^[^ \t\n]+") + if text then + sText = string.sub(sText, #text + 1) + if #text > win.canvas.width then + -- Print a multiline word + while #text > 0 do + if cx > win.canvas.width then + newLine() + end + win.write(text) + text = string.sub(text, win.canvas.width - cx + 2) + end + else + -- Print a word normally + if cx + #text - 1 > win.canvas.width then + newLine() + end + win.write(text) + end + end + end + + return nLinesPrinted + end + + function win.print(...) + local vis = isVisible + isVisible = false + local nLinesPrinted = 0 + local nLimit = select("#", ...) + for n = 1, nLimit do + local s = tostring(select(n, ...)) + if n < nLimit then + s = s .. "\t" + end + nLinesPrinted = nLinesPrinted + win.writeX(s) + end + nLinesPrinted = nLinesPrinted + win.writeX("\n") + isVisible = vis + update() + return nLinesPrinted + end + win.canvas:clear() return win diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 25c5083..87865ee 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -763,7 +763,7 @@ function UI.Window:fillArea(x, y, width, height, fillChar, bg, fg) end end -UI.Window.docs.write = [[write(NUMBER x, NUMBER y, String text, opt COLOR bg, opt COLOR fg) +UI.Window.docs.write = [[write(NUMBER x, NUMBER y, STRING text, opt COLOR bg, opt COLOR fg) Write text to the canvas. If colors are not specified, the colors from the base class will be used. If the base class does not have colors defined, colors will be inherited from the parent container.]] diff --git a/sys/modules/opus/ui/components/TabBar.lua b/sys/modules/opus/ui/components/TabBar.lua index 0f75e05..bed07c3 100644 --- a/sys/modules/opus/ui/components/TabBar.lua +++ b/sys/modules/opus/ui/components/TabBar.lua @@ -7,8 +7,6 @@ UI.TabBar.defaults = { UIElement = 'TabBar', buttonClass = 'TabBarMenuItem', backgroundColor = 'black', - selectedBackgroundColor = 'primary', - unselectedBackgroundColor = 'tertiary', } function UI.TabBar:enable() UI.MenuBar.enable(self) diff --git a/sys/modules/opus/ui/components/TabBarMenuItem.lua b/sys/modules/opus/ui/components/TabBarMenuItem.lua index 0ade0b0..0e28002 100644 --- a/sys/modules/opus/ui/components/TabBarMenuItem.lua +++ b/sys/modules/opus/ui/components/TabBarMenuItem.lua @@ -5,15 +5,16 @@ UI.TabBarMenuItem = class(UI.Button) UI.TabBarMenuItem.defaults = { UIElement = 'TabBarMenuItem', event = 'tab_select', - textInactiveColor = 'lightGray', } function UI.TabBarMenuItem:draw() if self.selected then self.backgroundColor = self:getProperty('selectedBackgroundColor') self.backgroundFocusColor = self.backgroundColor + self.textColor = self:getProperty('selectedTextColor') else self.backgroundColor = self:getProperty('unselectedBackgroundColor') self.backgroundFocusColor = self.backgroundColor + self.textColor = self:getProperty('unselectedTextColor') end UI.Button.draw(self) end diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index f2b61a8..566993a 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -6,6 +6,10 @@ UI.Tabs = class(UI.Window) UI.Tabs.docs = { } UI.Tabs.defaults = { UIElement = 'Tabs', + selectedBackgroundColor = 'primary', + unselectedBackgroundColor = 'tertiary', + unselectedTextColor = 'lightGray', + selectedTextColor = 'black', } function UI.Tabs:postInit() self:add(self) -- 2.49.1 From 18b7f540ab0c2d5f2222b7dad76a7674e0427b84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 31 May 2020 02:06:14 +0000 Subject: [PATCH 181/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 2a53284..13073a6 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Fri May 29 04:55:19 UTC 2020 -commit 41c0758857a105728f26e982f6c588ada75803bc +Sun May 31 02:06:13 UTC 2020 +commit 21057995248b7128d8772f93c024e9db2d378f77 Author: kepler155c@gmail.com -Date: Thu May 28 22:54:53 2020 -0600 +Date: Sat May 30 20:05:53 2020 -0600 - Merge branch 'develop-1.8' of https://github.com/kepler155c/opus into develop-1.8 + minor cleanup -- 2.49.1 From 4796e9e77ad8edb47f536fa44e6b28b81c542931 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 31 May 2020 23:50:30 -0600 Subject: [PATCH 182/236] ramfs bugfixes --- sys/modules/opus/fs/ramfs.lua | 54 ++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/sys/modules/opus/fs/ramfs.lua b/sys/modules/opus/fs/ramfs.lua index 9681b0d..bea2370 100644 --- a/sys/modules/opus/fs/ramfs.lua +++ b/sys/modules/opus/fs/ramfs.lua @@ -53,8 +53,10 @@ function ramfs.makeDir(_, dir) fs.mount(dir, 'ramfs', 'directory') end -function ramfs.isDir(node) - return not not node.nodes +function ramfs.isDir(node, dir) + if node.mountPoint == dir then + return not not node.nodes + end end function ramfs.getDrive() @@ -77,7 +79,6 @@ function ramfs.list(node, dir) end function ramfs.open(node, fn, fl) - if fl ~= 'r' and fl ~= 'w' and fl ~= 'rb' and fl ~= 'wb' then error('Unsupported mode') end @@ -87,22 +88,31 @@ function ramfs.open(node, fn, fl) return end + local c = type(node.contents) == 'table' + and string.char(table.unpack(node.contents)) + or node.contents + local ctr = 0 local lines return { - read = function() - ctr = ctr + 1 - return node.contents:sub(ctr, ctr) + read = function(n) + n = n or 1 + if ctr >= node.size then + return + end + local t = c:sub(ctr + 1, ctr + n) + ctr = ctr + n + return t end, readLine = function() if not lines then - lines = Util.split(node.contents) + lines = Util.split(c) end ctr = ctr + 1 return lines[ctr] end, readAll = function() - return node.contents + return c end, close = function() lines = nil @@ -134,11 +144,27 @@ function ramfs.open(node, fn, fl) return end + local c = node.contents + if type(node.contents) == 'string' then + c = { } + for i = 1, node.size do + c[i] = node.contents:sub(i, i):byte() + end + end + local ctr = 0 return { - read = function() + read = function(n) + if n and n > 1 and ctr < node.size then + -- some programs open in rb, when it should have + -- been opened in r - attempt to support multiple read + -- if nils are present in data, this will fail + local t = string.char(table.unpack(c, ctr + 1, ctr + n)) + ctr = ctr + n + return t + end ctr = ctr + 1 - return node.contents[ctr] + return c[ctr] end, close = function() end, @@ -150,7 +176,13 @@ function ramfs.open(node, fn, fl) local c = { } return { write = function(b) - table.insert(c, b) + if type(b) == 'number' then + table.insert(c, b) + else + for i = 1, #b do + table.insert(c, b:sub(i, i):byte()) + end + end end, flush = function() node.contents = c -- 2.49.1 From 0359a89e12b7be6ef9fca8da38977789cf4bd608 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2020 05:50:47 +0000 Subject: [PATCH 183/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 13073a6..437c595 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sun May 31 02:06:13 UTC 2020 -commit 21057995248b7128d8772f93c024e9db2d378f77 +Mon Jun 1 05:50:45 UTC 2020 +commit 4796e9e77ad8edb47f536fa44e6b28b81c542931 Author: kepler155c@gmail.com -Date: Sat May 30 20:05:53 2020 -0600 +Date: Sun May 31 23:50:30 2020 -0600 - minor cleanup + ramfs bugfixes -- 2.49.1 From 5c4ab57ec8fe06879adf38c7edb3c447c1f242df Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 1 Jun 2020 16:53:01 -0600 Subject: [PATCH 184/236] binary support fixes --- sys/apps/shell.lua | 4 ++++ sys/init/2.vfs.lua | 6 +++--- sys/init/3.sys.lua | 2 +- sys/modules/opus/fs/linkfs.lua | 2 +- sys/modules/opus/fs/ramfs.lua | 3 +++ sys/modules/opus/ui/components/Tabs.lua | 1 + sys/modules/opus/util.lua | 4 ++-- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 4f8d659..d0002ce 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -669,6 +669,10 @@ if settings.get("motd.enabled") then end while not bExit do + local cx = term.getCursorPos() + if cx ~= 1 then + print() + end if config.displayDirectory then term.setTextColour(_colors.directoryTextColor) term.setBackgroundColor(_colors.directoryBackgroundColor) diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 816f38f..4734981 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -223,12 +223,12 @@ function fs.copy(s, t) end else - local sf = Util.readFile(s) + local sf = Util.readFile(s, 'rb') if not sf then error('No such file') end - Util.writeFile(t, sf) + Util.writeFile(t, sf, 'wb') end end @@ -378,4 +378,4 @@ function fs.restore() local native = fs.native Util.clear(fs) Util.merge(fs, native) -end +end \ No newline at end of file diff --git a/sys/init/3.sys.lua b/sys/init/3.sys.lua index 5a5583a..3f73c6d 100644 --- a/sys/init/3.sys.lua +++ b/sys/init/3.sys.lua @@ -43,4 +43,4 @@ end -- non-standard - will raise error instead os.exit = function(code) error(code or 0) -end +end \ No newline at end of file diff --git a/sys/modules/opus/fs/linkfs.lua b/sys/modules/opus/fs/linkfs.lua index 813ce5f..7a98b77 100644 --- a/sys/modules/opus/fs/linkfs.lua +++ b/sys/modules/opus/fs/linkfs.lua @@ -72,4 +72,4 @@ function linkfs.move(node, s, t) return fs.move(s, t) end -return linkfs +return linkfs \ No newline at end of file diff --git a/sys/modules/opus/fs/ramfs.lua b/sys/modules/opus/fs/ramfs.lua index bea2370..9c5d718 100644 --- a/sys/modules/opus/fs/ramfs.lua +++ b/sys/modules/opus/fs/ramfs.lua @@ -154,6 +154,9 @@ function ramfs.open(node, fn, fl) local ctr = 0 return { + readAll = function() + return string.char(table.unpack(c)) + end, read = function(n) if n and n > 1 and ctr < node.size then -- some programs open in rb, when it should have diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index 566993a..93ab9e7 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -32,6 +32,7 @@ function UI.Tabs:add(children) if not self.tabBar then self.tabBar = UI.TabBar({ buttons = buttons, + backgroundColor = self.barBackgroundColor, }) else self.tabBar:addButtons(buttons) diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index ec3c7d6..8dfa148 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -493,7 +493,7 @@ function Util.backup(fname) fs.copy(fname, backup) end -function Util.writeFile(fname, data) +function Util.writeFile(fname, data, flags) if not fname or not data then error('Util.writeFile: invalid parameters', 2) end if fs.exists(fname) then @@ -505,7 +505,7 @@ function Util.writeFile(fname, data) end end - local file = io.open(fname, "w") + local file = io.open(fname, flags or "w") if not file then error('Unable to open ' .. fname, 2) end -- 2.49.1 From 6009f22d8ed95c81bc79fb4eb3129ec786ba9fc6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2020 22:53:29 +0000 Subject: [PATCH 185/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 437c595..120fb23 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Mon Jun 1 05:50:45 UTC 2020 -commit 4796e9e77ad8edb47f536fa44e6b28b81c542931 +Mon Jun 1 22:53:28 UTC 2020 +commit f951f10df5c0f2bf15d4ed51ce8eed9486efad94 Author: kepler155c@gmail.com -Date: Sun May 31 23:50:30 2020 -0600 +Date: Mon Jun 1 16:53:09 2020 -0600 - ramfs bugfixes + Merge branch 'develop-1.8' of https://github.com/kepler155c/opus into develop-1.8 -- 2.49.1 From 6a3b38922b0b13917cdace5701cc890db5ccfd7c Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 3 Jun 2020 20:40:48 -0600 Subject: [PATCH 186/236] packages stored in compressed state - experimental (with config option) --- sys/apps/System.lua | 2 +- sys/apps/package.lua | 9 + sys/init/5.unpackage.lua | 27 +++ sys/modules/opus/compress/lzw.lua | 142 ++++++++++++++++ sys/modules/opus/compress/tar.lua | 270 ++++++++++++++++++++++++++++++ 5 files changed, 449 insertions(+), 1 deletion(-) create mode 100644 sys/init/5.unpackage.lua create mode 100644 sys/modules/opus/compress/lzw.lua create mode 100644 sys/modules/opus/compress/tar.lua diff --git a/sys/apps/System.lua b/sys/apps/System.lua index cd22812..b0ebf4d 100644 --- a/sys/apps/System.lua +++ b/sys/apps/System.lua @@ -20,7 +20,7 @@ local function loadDirectory(dir) return plugins end -local programDir = fs.getDir(shell.getRunningProgram()) +local programDir = fs.getDir(_ENV.arg[0]) local plugins = loadDirectory(fs.combine(programDir, 'system'), { }) local page = UI.Page { diff --git a/sys/apps/package.lua b/sys/apps/package.lua index 8177fbd..e1af0af 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -1,6 +1,9 @@ local BulkGet = require('opus.bulkget') +local Config = require('opus.config') local Git = require('opus.git') +local LZW = require('opus.compress.lzw') local Packages = require('opus.packages') +local Tar = require('opus.compress.tar') local Util = require('opus.util') local fs = _G.fs @@ -100,6 +103,12 @@ local function install(name, isUpdate, ignoreDeps) if not isUpdate then runScript(manifest.install) end + + if Config.load('package').compression then + local c = Tar.tar_string(packageDir) + Util.writeFile(name .. '.tar.lzw', LZW.compress(c), 'wb') + fs.delete(packageDir) + end end if action == 'list' then diff --git a/sys/init/5.unpackage.lua b/sys/init/5.unpackage.lua new file mode 100644 index 0000000..6cd4227 --- /dev/null +++ b/sys/init/5.unpackage.lua @@ -0,0 +1,27 @@ +local LZW = require('opus.compress.lzw') +local Tar = require('opus.compress.tar') +local Util = require('opus.util') + +local fs = _G.fs + +for _, name in pairs(fs.list('packages')) do + local fullName = fs.combine('packages', name) + local packageName = name:match('(.+)%.tar%.lzw$') + if packageName and not fs.isDir(fullName) then + local dir = fs.combine('packages', packageName) + if not fs.exists(dir) then + local s, m = pcall(function() + fs.mount(dir, 'ramfs', 'directory') + + local c = Util.readFile(fullName, 'rb') + + Tar.untar_string(LZW.decompress(c), dir) + end) + if not s then + fs.delete(dir) + print('failed to extract ' .. fullName) + print(m) + end + end + end +end diff --git a/sys/modules/opus/compress/lzw.lua b/sys/modules/opus/compress/lzw.lua new file mode 100644 index 0000000..8434dac --- /dev/null +++ b/sys/modules/opus/compress/lzw.lua @@ -0,0 +1,142 @@ +-- see: https://github.com/Rochet2/lualzw +-- MIT License - Copyright (c) 2016 Rochet2 + +local char = string.char +local type = type +local sub = string.sub +local tconcat = table.concat + +local SIGC = 'LZWC' + +local basedictcompress = {} +local basedictdecompress = {} +for i = 0, 255 do + local ic, iic = char(i), char(i, 0) + basedictcompress[ic] = iic + basedictdecompress[iic] = ic +end + +local function dictAddA(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[str] = char(a,b) + a = a+1 + return dict, a, b +end + +local function compress(input) + if type(input) ~= "string" then + error ("string expected, got "..type(input)) + end + local len = #input + if len <= 1 then + return input + end + + local dict = {} + local a, b = 0, 1 + + local result = { SIGC } + local resultlen = 1 + local n = 2 + local word = "" + for i = 1, len do + local c = sub(input, i, i) + local wc = word..c + if not (basedictcompress[wc] or dict[wc]) then + local write = basedictcompress[word] or dict[word] + if not write then + error "algorithm error, could not fetch word" + end + result[n] = write + resultlen = resultlen + #write + n = n+1 + if len <= resultlen then + return input + end + dict, a, b = dictAddA(wc, dict, a, b) + word = c + else + word = wc + end + end + result[n] = basedictcompress[word] or dict[word] + resultlen = resultlen+#result[n] + if len <= resultlen then + return input + end + return tconcat(result) +end + +local function dictAddB(str, dict, a, b) + if a >= 256 then + a, b = 0, b+1 + if b >= 256 then + dict = {} + b = 1 + end + end + dict[char(a,b)] = str + a = a+1 + return dict, a, b +end + +local function decompress(input) + if type(input) ~= "string" then + error( "string expected, got "..type(input)) + end + + if #input < 4 then + return input + end + + local control = sub(input, 1, 4) + if control ~= SIGC then + return input + end + input = sub(input, 5) + local len = #input + + if len < 2 then + error("invalid input - not a compressed string") + end + + local dict = {} + local a, b = 0, 1 + + local result = {} + local n = 1 + local last = sub(input, 1, 2) + result[n] = basedictdecompress[last] or dict[last] + n = n+1 + for i = 3, len, 2 do + local code = sub(input, i, i+1) + local lastStr = basedictdecompress[last] or dict[last] + if not lastStr then + error( "could not find last from dict. Invalid input?") + end + local toAdd = basedictdecompress[code] or dict[code] + if toAdd then + result[n] = toAdd + n = n+1 + dict, a, b = dictAddB(lastStr..sub(toAdd, 1, 1), dict, a, b) + else + local tmp = lastStr..sub(lastStr, 1, 1) + result[n] = tmp + n = n+1 + dict, a, b = dictAddB(tmp, dict, a, b) + end + last = code + end + return tconcat(result) +end + +return { + compress = compress, + decompress = decompress, +} diff --git a/sys/modules/opus/compress/tar.lua b/sys/modules/opus/compress/tar.lua new file mode 100644 index 0000000..4bf04cf --- /dev/null +++ b/sys/modules/opus/compress/tar.lua @@ -0,0 +1,270 @@ + +-- see: https://github.com/luarocks/luarocks/blob/master/src/luarocks/tools/tar.lua +-- A pure-Lua implementation of untar (unpacking .tar archives) +local Util = require('opus.util') + +local fs = _G.fs +local _sub = string.sub + +local blocksize = 512 + +local function get_typeflag(flag) + if flag == "0" or flag == "\0" then return "file" + elseif flag == "1" then return "link" + elseif flag == "2" then return "symlink" -- "reserved" in POSIX, "symlink" in GNU + elseif flag == "3" then return "character" + elseif flag == "4" then return "block" + elseif flag == "5" then return "directory" + elseif flag == "6" then return "fifo" + elseif flag == "7" then return "contiguous" -- "reserved" in POSIX, "contiguous" in GNU + elseif flag == "x" then return "next file" + elseif flag == "g" then return "global extended header" + elseif flag == "L" then return "long name" + elseif flag == "K" then return "long link name" + end + return "unknown" +end + +local function octal_to_number(octal) + local exp = 0 + local number = 0 + octal = octal:gsub("%s", "") + for i = #octal,1,-1 do + local digit = tonumber(octal:sub(i,i)) + if not digit then + break + end + number = number + (digit * 8^exp) + exp = exp + 1 + end + return number +end + +local function checksum_header(block) + local sum = 256 + for i = 1,148 do + local b = block:byte(i) or 0 + sum = sum + b + end + for i = 157,500 do + local b = block:byte(i) or 0 + sum = sum + b + end + return sum +end + +local function nullterm(s) + return s:match("^[^%z]*") +end + +local function read_header_block(block) + local header = {} + header.name = nullterm(block:sub(1,100)) + header.mode = nullterm(block:sub(101,108)):gsub(" ", "") + header.uid = octal_to_number(nullterm(block:sub(109,116))) + header.gid = octal_to_number(nullterm(block:sub(117,124))) + header.size = octal_to_number(nullterm(block:sub(125,136))) + header.mtime = octal_to_number(nullterm(block:sub(137,148))) + header.chksum = octal_to_number(nullterm(block:sub(149,156))) + header.typeflag = get_typeflag(block:sub(157,157)) + header.linkname = nullterm(block:sub(158,257)) + header.magic = block:sub(258,263) + header.version = block:sub(264,265) + header.uname = nullterm(block:sub(266,297)) + header.gname = nullterm(block:sub(298,329)) + header.devmajor = octal_to_number(nullterm(block:sub(330,337))) + header.devminor = octal_to_number(nullterm(block:sub(338,345))) + header.prefix = block:sub(346,500) + if not checksum_header(block) == header.chksum then + return false, "Failed header checksum" + end + return header +end + +local function untar_stream(tar_handle, destdir, verbose) + assert(type(destdir) == "string") + + local long_name, long_link_name + local ok, err + + local make_dir = function(a) + if not fs.exists(a) then + fs.makeDir(a) + end + return true + end + + while true do + local block + repeat + block = tar_handle:read(blocksize) + until (not block) or checksum_header(block) > 256 + if not block then break end + if #block < blocksize then + ok, err = nil, "Invalid block size -- corrupted file?" + break + end + local header + header, err = read_header_block(block) + if not header then + ok = false + break + end + + local file_data = tar_handle:read(math.ceil(header.size / blocksize) * blocksize):sub(1,header.size) + + if header.typeflag == "long name" then + long_name = nullterm(file_data) + elseif header.typeflag == "long link name" then + long_link_name = nullterm(file_data) + else + if long_name then + header.name = long_name + long_name = nil + end + if long_link_name then + header.name = long_link_name + long_link_name = nil + end + end + local pathname = fs.combine(destdir, header.name) + + if header.typeflag == "directory" then + ok, err = make_dir(pathname) + if not ok then + break + end + elseif header.typeflag == "file" then + local dirname = fs.getDir(pathname) + if dirname ~= "" then + ok, err = make_dir(dirname) + if not ok then + break + end + end + local file_handle + if verbose then + print(pathname) + end + file_handle, err = io.open(pathname, "wb") + if not file_handle then + ok = nil + break + end + file_handle:write(file_data) + file_handle:close() + end + end + + return ok, err +end + +local function untar_string(str, destdir, verbose) + local ctr = 1 + local len = #str + local handle = { + read = function(_, n) + if ctr < len then + local s = _sub(str, ctr, ctr + n - 1) + ctr = ctr + n + return s + end + end + } + return untar_stream(handle, destdir, verbose) +end + +local function untar(filename, destdir, verbose) + assert(type(filename) == "string") + assert(type(destdir) == "string") + + local tar_handle = io.open(filename, "rb") + if not tar_handle then return nil, "Error opening file "..filename end + + local ok, err = untar_stream(filename, destdir, verbose) + + tar_handle:close() + return ok, err +end + +local function create_header_block(filename, abspath) + local block = ('\0'):rep(blocksize) + + local function number_to_octal(n) + return ('%o'):format(n) + end + + local function ins(pos, istr) + block = block:sub(1, pos - 1) .. istr .. block:sub(pos + #istr) + end + + ins(1, filename) -- header + ins(125, number_to_octal(fs.getSize(abspath))) + ins(157, '0') -- typeflag + + ins(149, number_to_octal(checksum_header(block))) + + return block +end + +local function tar_stream(tar_handle, root, files) + if not files then + files = { } + local function recurse(rel) + local abs = fs.combine(root, rel) + for _,f in ipairs(fs.list(abs)) do + local fullName = fs.combine(abs, f) + if fs.native.isDir(fullName) then -- skip virtual dirs + recurse(fs.combine(rel, f)) + else + table.insert(files, fs.combine(rel, f)) + end + end + end + recurse('') + end + + for _, file in pairs(files) do + local abs = fs.combine(root, file) + local block = create_header_block(file, abs) + tar_handle:write(block) + local f = Util.readFile(abs, 'rb') + tar_handle:write(f) + local padding = #f % blocksize + if padding > 0 then + tar_handle:write(('\0'):rep(blocksize - padding)) + end + end +end + +local function tar_string(root, files) + local t = { } + local handle = { + write = function(_, s) + table.insert(t, s) + end + } + tar_stream(handle, root, files) + return table.concat(t) +end + +-- the bare minimum for this program to untar +local function tar(filename, root, files) + assert(type(filename) == "string") + assert(type(root) == "string") + + local tar_handle = io.open(filename, "wb") + if not tar_handle then return nil, "Error opening file "..filename end + + local ok, err = tar_stream(tar_handle, root, files) + + tar_handle:close() + return ok, err +end + +return { + tar = tar, + untar = untar, + tar_string = tar_string, + untar_string = untar_string, +} -- 2.49.1 From d678eeeaca484acb27713968ac305ba1b471ee0e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2020 02:41:07 +0000 Subject: [PATCH 187/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 120fb23..253a290 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Mon Jun 1 22:53:28 UTC 2020 -commit f951f10df5c0f2bf15d4ed51ce8eed9486efad94 +Thu Jun 4 02:41:06 UTC 2020 +commit 6a3b38922b0b13917cdace5701cc890db5ccfd7c Author: kepler155c@gmail.com -Date: Mon Jun 1 16:53:09 2020 -0600 +Date: Wed Jun 3 20:40:48 2020 -0600 - Merge branch 'develop-1.8' of https://github.com/kepler155c/opus into develop-1.8 + packages stored in compressed state - experimental (with config option) -- 2.49.1 From 704ef46b621279a893e8b185a0791ea20c1a3f2d Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 3 Jun 2020 21:11:54 -0600 Subject: [PATCH 188/236] packages stored in compressed state - experimental (with config option) --- sys/apps/package.lua | 2 +- sys/modules/opus/compress/tar.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/apps/package.lua b/sys/apps/package.lua index e1af0af..0ede6f5 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -106,7 +106,7 @@ local function install(name, isUpdate, ignoreDeps) if Config.load('package').compression then local c = Tar.tar_string(packageDir) - Util.writeFile(name .. '.tar.lzw', LZW.compress(c), 'wb') + Util.writeFile(packageDir .. '.tar.lzw', LZW.compress(c), 'wb') fs.delete(packageDir) end end diff --git a/sys/modules/opus/compress/tar.lua b/sys/modules/opus/compress/tar.lua index 4bf04cf..0798234 100644 --- a/sys/modules/opus/compress/tar.lua +++ b/sys/modules/opus/compress/tar.lua @@ -214,7 +214,7 @@ local function tar_stream(tar_handle, root, files) local abs = fs.combine(root, rel) for _,f in ipairs(fs.list(abs)) do local fullName = fs.combine(abs, f) - if fs.native.isDir(fullName) then -- skip virtual dirs + if fs.isDir(fullName) then -- skip virtual dirs recurse(fs.combine(rel, f)) else table.insert(files, fs.combine(rel, f)) -- 2.49.1 From 788f6e7de0f026977c6ddabe941289a7e09364a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2020 03:12:11 +0000 Subject: [PATCH 189/236] Update version date --- .opus_version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.opus_version b/.opus_version index 253a290..ae83020 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Thu Jun 4 02:41:06 UTC 2020 -commit 6a3b38922b0b13917cdace5701cc890db5ccfd7c +Thu Jun 4 03:12:10 UTC 2020 +commit 704ef46b621279a893e8b185a0791ea20c1a3f2d Author: kepler155c@gmail.com -Date: Wed Jun 3 20:40:48 2020 -0600 +Date: Wed Jun 3 21:11:54 2020 -0600 packages stored in compressed state - experimental (with config option) -- 2.49.1 From 7dfa80f5f32ea6bdc80e278c60f4378c4e018900 Mon Sep 17 00:00:00 2001 From: kepler155c Date: Wed, 3 Jun 2020 22:30:54 -0600 Subject: [PATCH 190/236] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7b5e66d..606978a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Opus OS for computercraft + + ## Features * Multitasking OS - run programs in separate tabs * Telnet (wireless remote shell) -- 2.49.1 From e7fcd68a0ff1d7c7c82a7683b350febb734063e5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2020 04:31:10 +0000 Subject: [PATCH 191/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index ae83020..7407a2a 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Thu Jun 4 03:12:10 UTC 2020 -commit 704ef46b621279a893e8b185a0791ea20c1a3f2d -Author: kepler155c@gmail.com -Date: Wed Jun 3 21:11:54 2020 -0600 +Thu Jun 4 04:31:09 UTC 2020 +commit 7dfa80f5f32ea6bdc80e278c60f4378c4e018900 +Author: kepler155c +Date: Wed Jun 3 22:30:54 2020 -0600 - packages stored in compressed state - experimental (with config option) + Update README.md -- 2.49.1 From 6c5cc508b1884d080b5940f819a53d54333f8dd6 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Fri, 5 Jun 2020 21:38:26 -0600 Subject: [PATCH 192/236] moonscript support + cleanup --- sys/apps/package.lua | 4 ++-- sys/modules/opus/fs/urlfs.lua | 3 +++ sys/modules/opus/injector.lua | 12 +++++------- sys/modules/opus/ui/components/QuickSelect.lua | 7 +++++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/sys/apps/package.lua b/sys/apps/package.lua index 0ede6f5..0b6d582 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -1,9 +1,9 @@ local BulkGet = require('opus.bulkget') local Config = require('opus.config') local Git = require('opus.git') -local LZW = require('opus.compress.lzw') +local LZW = require('opus.compress.lzw') local Packages = require('opus.packages') -local Tar = require('opus.compress.tar') +local Tar = require('opus.compress.tar') local Util = require('opus.util') local fs = _G.fs diff --git a/sys/modules/opus/fs/urlfs.lua b/sys/modules/opus/fs/urlfs.lua index b2c47ca..e9a4193 100644 --- a/sys/modules/opus/fs/urlfs.lua +++ b/sys/modules/opus/fs/urlfs.lua @@ -109,6 +109,9 @@ function urlfs.open(node, fn, fl) } end return { + readAll = function() + return c + end, read = function() ctr = ctr + 1 return c:sub(ctr, ctr):byte() diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index 266d5a8..634babd 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -43,9 +43,9 @@ return function(env) return env.package.preload[modname](modname, env) end end - end + --end - local function loadedSearcher(modname) + --local function loadedSearcher(modname) if env.package.loaded[modname] then return function() return env.package.loaded[modname] @@ -95,7 +95,7 @@ return function(env) }, loaders = { preloadSearcher, - loadedSearcher, + --loadedSearcher, pathSearcher, } } @@ -104,10 +104,8 @@ return function(env) for _,searcher in ipairs(env.package.loaders) do local fn, msg = searcher(modname) if fn then - local module, msg2 = fn(modname, env) - if not module then - error(msg2 or (modname .. ' module returned nil'), 2) - end + local module = fn(modname, env) or true + env.package.loaded[modname] = module return module end diff --git a/sys/modules/opus/ui/components/QuickSelect.lua b/sys/modules/opus/ui/components/QuickSelect.lua index dc96979..5626f8c 100644 --- a/sys/modules/opus/ui/components/QuickSelect.lua +++ b/sys/modules/opus/ui/components/QuickSelect.lua @@ -64,8 +64,11 @@ function UI.QuickSelect:enable() local files = fs.list(dir) for _,f in ipairs(files) do local fullName = fs.combine(dir, f) - if fs.native.isDir(fullName) then -- skip virtual dirs - if f ~= '.git' then recurse(fullName) end + if fs.isDir(fullName) then + -- skip virtual dirs + if f ~= '.git' and fs.native.isDir(fullName) then + recurse(fullName) + end else _insert(self.grid.values, { name = f, -- 2.49.1 From 4d02f75a19327809762f902f6cd1967335aa3d68 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 03:38:48 +0000 Subject: [PATCH 193/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 7407a2a..a751348 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Thu Jun 4 04:31:09 UTC 2020 -commit 7dfa80f5f32ea6bdc80e278c60f4378c4e018900 -Author: kepler155c -Date: Wed Jun 3 22:30:54 2020 -0600 +Sat Jun 6 03:38:47 UTC 2020 +commit 6c5cc508b1884d080b5940f819a53d54333f8dd6 +Author: kepler155c@gmail.com +Date: Fri Jun 5 21:38:26 2020 -0600 - Update README.md + moonscript support + cleanup -- 2.49.1 From 2d942ef00170a174352f86b7d498e9e1e54577b7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 6 Jun 2020 11:00:07 -0600 Subject: [PATCH 194/236] experimental packages fix https://github.com/kepler155c/opus/issues/35 --- sys/apps/package.lua | 1 + sys/init/5.unpackage.lua | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/sys/apps/package.lua b/sys/apps/package.lua index 0b6d582..83c1e50 100644 --- a/sys/apps/package.lua +++ b/sys/apps/package.lua @@ -165,6 +165,7 @@ if action == 'uninstall' then local packageDir = fs.combine('packages', name) fs.delete(packageDir) + fs.delete(packageDir .. '.tar.lzw') print('removed: ' .. packageDir) return end diff --git a/sys/init/5.unpackage.lua b/sys/init/5.unpackage.lua index 6cd4227..3695d76 100644 --- a/sys/init/5.unpackage.lua +++ b/sys/init/5.unpackage.lua @@ -4,6 +4,10 @@ local Util = require('opus.util') local fs = _G.fs +if not fs.exists('packages') or not fs.isDir('packages') then + return +end + for _, name in pairs(fs.list('packages')) do local fullName = fs.combine('packages', name) local packageName = name:match('(.+)%.tar%.lzw$') -- 2.49.1 From 039fa7374945bf7c2d7a1820cc7990ab80cf213e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 17:00:25 +0000 Subject: [PATCH 195/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index a751348..88d4050 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat Jun 6 03:38:47 UTC 2020 -commit 6c5cc508b1884d080b5940f819a53d54333f8dd6 +Sat Jun 6 17:00:23 UTC 2020 +commit 2d942ef00170a174352f86b7d498e9e1e54577b7 Author: kepler155c@gmail.com -Date: Fri Jun 5 21:38:26 2020 -0600 +Date: Sat Jun 6 11:00:07 2020 -0600 - moonscript support + cleanup + experimental packages fix https://github.com/kepler155c/opus/issues/35 -- 2.49.1 From e9580d67ebeabdc105440404b3ae4873dc76b397 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 9 Jun 2020 18:16:51 -0600 Subject: [PATCH 196/236] pure lua compatiblity fixes for moonlight, busted, etc --- sys/apps/compat.lua | 25 ++++++++ sys/apps/shell.lua | 7 ++- sys/boot/opus.boot | 13 +++- sys/init/2.vfs.lua | 16 +++++ sys/kernel.lua | 2 +- sys/modules/opus/fs/urlfs.lua | 26 +++----- sys/modules/opus/injector.lua | 114 +++++++++++++++++++--------------- sys/modules/opus/util.lua | 3 - 8 files changed, 133 insertions(+), 73 deletions(-) create mode 100644 sys/apps/compat.lua diff --git a/sys/apps/compat.lua b/sys/apps/compat.lua new file mode 100644 index 0000000..840f517 --- /dev/null +++ b/sys/apps/compat.lua @@ -0,0 +1,25 @@ +local Util = require('opus.util') + +-- some programs expect to be run in the global scope +-- ie. busted, moonscript + +-- create a new sandbox mimicing pure lua + +local fs = _G.fs +local shell = _ENV.shell + +local env = Util.shallowCopy(_G) +Util.merge(env, _ENV) +env._G = env + +env.arg = { ... } +env.arg[0] = shell.resolveProgram(table.remove(env.arg, 1)) + +_G.requireInjector(env, fs.getDir(env.arg[0])) + +local s, m = Util.run(env, env.arg[0], table.unpack(env.arg)) + +if not s then + error(m) +end + diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index d0002ce..b1cbe69 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -6,7 +6,7 @@ local fs = _G.fs local settings = _G.settings local shell = _ENV.shell -_G.requireInjector(_ENV) +--_G.requireInjector(_ENV) local trace = require('opus.trace') local Util = require('opus.util') @@ -43,6 +43,11 @@ local function run(...) local isUrl = not not command:match("^(https?:)") local env = shell.makeEnv(_ENV) + if command:match('(.+)%.moon$') then + table.insert(args, 1, command) + command = 'moon' + end + local path, loadFn if isUrl then path = command diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index f189ad5..c0d022d 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -1,5 +1,10 @@ local fs = _G.fs +-- override bios function to use the global scope of the current env +function _G.loadstring(string, chunkname) + return load(string, chunkname, nil, getfenv(2)._G) +end + -- override bios function to include the actual filename function _G.loadfile(filename, mode, env) -- Support the previous `loadfile(filename, env)` form instead. @@ -20,12 +25,17 @@ for k,v in pairs(_ENV) do sandboxEnv[k] = v end +-- Install require shim +_G.requireInjector = loadfile('sys/modules/opus/injector.lua', _ENV)() + local function run(file, ...) local env = setmetatable({ }, { __index = _G }) for k,v in pairs(sandboxEnv) do env[k] = v end + _G.requireInjector(env) + local s, m = loadfile(file, env) if s then return s(...) @@ -36,9 +46,6 @@ end _G._syslog = function() end _G.OPUS_BRANCH = 'develop-1.8' --- Install require shim -_G.requireInjector = run('sys/modules/opus/injector.lua') - local s, m = pcall(run, 'sys/apps/shell.lua', 'sys/kernel.lua', ...) if not s then diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 4734981..35fd6ce 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -83,6 +83,18 @@ function nativefs.isDir(node, dir) return fs.native.isDir(dir) end +function nativefs.attributes(node, path) + if node.mountPoint == path then + return { + created = node.created or os.epoch('utc'), + modification = node.modification or os.epoch('utc'), + isDir = not not node.nodes, + size = node.size or 0, + } + end + return fs.native.attributes(path) +end + function nativefs.exists(node, dir) if node.mountPoint == dir then return true @@ -307,12 +319,16 @@ function fs.mount(path, fstype, ...) tp.nodes[d] = Util.shallowCopy(tp) tp.nodes[d].nodes = { } tp.nodes[d].mountPoint = fs.combine(tp.mountPoint, d) + tp.nodes[d].created = os.epoch('utc') + tp.nodes[d].modification = os.epoch('utc') end tp = tp.nodes[d] end node.fs = vfs node.fstype = fstype + node.created = node.created or os.epoch('utc') + node.modification = node.modification or os.epoch('utc') if not targetName then node.mountPoint = '' fs.nodes = node diff --git a/sys/kernel.lua b/sys/kernel.lua index 3a86708..833631d 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -20,7 +20,7 @@ local w, h = term.getSize() kernel.terminal = term.current() kernel.window = Terminal.window(kernel.terminal, 1, 1, w, h, false) -kernel.window.setMaxScroll(100) +kernel.window.setMaxScroll(200) local focusedRoutineEvents = Util.transpose { 'char', 'key', 'key_up', diff --git a/sys/modules/opus/fs/urlfs.lua b/sys/modules/opus/fs/urlfs.lua index e9a4193..ff49176 100644 --- a/sys/modules/opus/fs/urlfs.lua +++ b/sys/modules/opus/fs/urlfs.lua @@ -20,8 +20,8 @@ function urlfs.mount(path, url, force) end end -function urlfs.attributes(node) - return { +function urlfs.attributes(node, path) + return path == node.mountPoint and { created = node.created, isDir = false, modification = node.modification, @@ -29,16 +29,18 @@ function urlfs.attributes(node) } end -function urlfs.delete(_, dir) - fs.unmount(dir) +function urlfs.delete(node, path) + if path == node.mountPoint then + fs.unmount(path) + end end -function urlfs.exists() - return true +function urlfs.exists(node, path) + return path == node.mountPoint end -function urlfs.getSize(node) - return node.size or 0 +function urlfs.getSize(node, path) + return path == node.mountPoint and node.size or 0 end function urlfs.isReadOnly() @@ -65,14 +67,6 @@ function urlfs.open(node, fn, fl) local c = node.cache if not c then - --[[ - if node.url:match("^(rttps?:)") then - local s, response = rttp.get(node.url) - c = s and response.statusCode == 200 and response.data - else - c = Util.httpGet(node.url) - end - ]]-- c = Util.httpGet(node.url) if c then node.cache = c diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index 634babd..8f66685 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -1,73 +1,69 @@ -- https://www.lua.org/manual/5.1/manual.html#pdf-require +-- https://github.com/LuaDist/lua/blob/d2e7e7d4d43ff9068b279a617c5b2ca2c2771676/src/loadlib.c -local function split(str, pattern) - local t = { } - local function helper(line) table.insert(t, line) return "" end - helper((str:gsub(pattern, helper))) - return t -end +local defaultPath = { } -local hasMain -local luaPaths = package and package.path and split(package.path, '(.-);') or { } -for i = 1, #luaPaths do - if luaPaths[i] == '?' or luaPaths[i] == '?.lua' or luaPaths[i] == '?/init.lua' then - luaPaths[i] = nil - elseif string.find(luaPaths[i], '/rom/modules/main') then - hasMain = true +do + local function split(str) + local t = { } + local function helper(line) table.insert(t, line) return "" end + helper((str:gsub('(.-);', helper))) + return t + end + + local function insert(p) + for _,v in pairs(defaultPath) do + if v == p then + return + end + end + table.insert(defaultPath, p) + + end + + local paths = '?.lua;?/init.lua;' + paths = paths .. '/usr/modules/?.lua;/usr/modules/?/init.lua;' + paths = paths .. '/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua;' + paths = paths .. '/sys/modules/?.lua;/sys/modules/?/init.lua' + + for _,v in pairs(split(paths)) do + insert(v) + end + + local luaPaths = package and package.path and split(package.path) or { } + for _,v in pairs(luaPaths) do + if v ~= '?' then + insert(v) + end end end -table.insert(luaPaths, 1, '?.lua') -table.insert(luaPaths, 2, '?/init.lua') -table.insert(luaPaths, 3, '/usr/modules/?.lua') -table.insert(luaPaths, 4, '/usr/modules/?/init.lua') -if not hasMain then - table.insert(luaPaths, 5, '/rom/modules/main/?') - table.insert(luaPaths, 6, '/rom/modules/main/?.lua') - table.insert(luaPaths, 7, '/rom/modules/main/?/init.lua') -end -table.insert(luaPaths, '/sys/modules/?.lua') -table.insert(luaPaths, '/sys/modules/?/init.lua') - -local DEFAULT_PATH = table.concat(luaPaths, ';') +local DEFAULT_PATH = table.concat(defaultPath, ';') local fs = _G.fs local os = _G.os local string = _G.string -- Add require and package to the environment -return function(env) +return function(env, programDir) local function preloadSearcher(modname) if env.package.preload[modname] then return function() return env.package.preload[modname](modname, env) end end - --end - - --local function loadedSearcher(modname) - if env.package.loaded[modname] then - return function() - return env.package.loaded[modname] - end - end end - local sentinel = { } - local function pathSearcher(modname) - if env.package.loaded[modname] == sentinel then - error("loop or previous error loading module '" .. modname .. "'", 0) - end - env.package.loaded[modname] = sentinel - local fname = modname:gsub('%.', '/') for pattern in string.gmatch(env.package.path, "[^;]+") do local sPath = string.gsub(pattern, "%?", fname) -- TODO: if there's no shell, we should not be checking relative paths below -- as they will resolve to root directory - if env.shell + if programDir and sPath:sub(1, 1) ~= "/" then + sPath = fs.combine(programDir, sPath) + elseif env.shell and type(env.shell.getRunningProgram) == 'function' and sPath:sub(1, 1) ~= "/" then @@ -81,38 +77,58 @@ return function(env) -- place package and require function into env env.package = { - path = env.LUA_PATH or _G.LUA_PATH or DEFAULT_PATH, - config = '/\n:\n?\n!\n-', + path = env.LUA_PATH or _G.LUA_PATH or DEFAULT_PATH, + cpath = '', + config = '/\n:\n?\n!\n-', preload = { }, - loaded = { + loaded = { + bit32 = bit32, coroutine = coroutine, + _G = env._G, io = io, math = math, os = os, string = string, table = table, debug = debug, + utf8 = utf8, }, loaders = { preloadSearcher, - --loadedSearcher, pathSearcher, } } + env.package.loaded.package = env.package + + local sentinel = { } function env.require(modname) + if env.package.loaded[modname] then + if env.package.loaded[modname] == sentinel then + error("loop or previous error loading module '" .. modname .. "'", 0) + end + return env.package.loaded[modname] + end + + local t = { } for _,searcher in ipairs(env.package.loaders) do local fn, msg = searcher(modname) - if fn then + if type(fn) == 'function' then + env.package.loaded[modname] = sentinel + local module = fn(modname, env) or true env.package.loaded[modname] = module return module end if msg then - error(msg, 2) + table.insert(t, msg) end end + + if #t > 0 then + error(table.concat(t, '\n'), 2) + end error('Unable to find module ' .. modname, 2) end diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 8dfa148..484231b 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -596,7 +596,6 @@ function Util.loadUrl(url, env) -- loadfile equivalent end function Util.runUrl(env, url, ...) -- os.run equivalent - setmetatable(env, { __index = _G }) local fn, m = Util.loadUrl(url, env) if fn then return pcall(fn, ...) @@ -606,7 +605,6 @@ end function Util.run(env, path, ...) if type(env) ~= 'table' then error('Util.run: env must be a table', 2) end - setmetatable(env, { __index = _G }) local fn, m = loadfile(path, env) if fn then return pcall(fn, ...) @@ -616,7 +614,6 @@ end function Util.runFunction(env, fn, ...) setfenv(fn, env) - setmetatable(env, { __index = _G }) return pcall(fn, ...) end -- 2.49.1 From 4f39604c6301cdabf1617a309237e76b645b42e5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jun 2020 00:17:16 +0000 Subject: [PATCH 197/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 88d4050..5e53c03 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat Jun 6 17:00:23 UTC 2020 -commit 2d942ef00170a174352f86b7d498e9e1e54577b7 +Wed Jun 10 00:17:16 UTC 2020 +commit e9580d67ebeabdc105440404b3ae4873dc76b397 Author: kepler155c@gmail.com -Date: Sat Jun 6 11:00:07 2020 -0600 +Date: Tue Jun 9 18:16:51 2020 -0600 - experimental packages fix https://github.com/kepler155c/opus/issues/35 + pure lua compatiblity fixes for moonlight, busted, etc -- 2.49.1 From 2fdcc338ad54f2113cd988c0d36c3cab9f4b5f3f Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 10 Jun 2020 19:46:34 -0600 Subject: [PATCH 198/236] transparent compatiblity for moonscript --- sys/apps/compat.lua | 5 +- sys/apps/shell.lua | 79 +++++++++++++------ sys/modules/opus/map.lua | 1 + .../opus/ui/components/QuickSelect.lua | 11 ++- 4 files changed, 64 insertions(+), 32 deletions(-) diff --git a/sys/apps/compat.lua b/sys/apps/compat.lua index 840f517..812410d 100644 --- a/sys/apps/compat.lua +++ b/sys/apps/compat.lua @@ -13,13 +13,12 @@ Util.merge(env, _ENV) env._G = env env.arg = { ... } -env.arg[0] = shell.resolveProgram(table.remove(env.arg, 1)) +env.arg[0] = shell.resolveProgram(table.remove(env.arg, 1) or error('file name is required')) _G.requireInjector(env, fs.getDir(env.arg[0])) local s, m = Util.run(env, env.arg[0], table.unpack(env.arg)) if not s then - error(m) + error(m, -1) end - diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index b1cbe69..a3f7812 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -6,8 +6,6 @@ local fs = _G.fs local settings = _G.settings local shell = _ENV.shell ---_G.requireInjector(_ENV) - local trace = require('opus.trace') local Util = require('opus.util') @@ -37,43 +35,72 @@ local function tokenise( ... ) return tWords end +local defaultHandlers = { + urlHandler = function(args, env) + return args[1]:match("^(https?:)") and { + title = fs.getName(args[1]), + path = table.remove(args, 1), + args = args, + load = Util.loadUrl, + env = env, + } + end, + + pathHandler = function(args, env) + local command = table.remove(args, 1) + return { + title = fs.getName(command):match('([^%.]+)'), + path = shell.resolveProgram(command) or error('No such program'), + args = args, + load = loadfile, + env = env, + } + end, +} + +function shell.getHandlers() + if parentShell and parentShell.getHandlers then + return parentShell.getHandlers() + end + return defaultHandlers +end + +local handlers = shell.getHandlers() + +function shell.registerHandler(fn) + table.insert(handlers, 1, fn) +end + +local function handleCommand(args, env) + for _,v in pairs(handlers) do + local pi = v(args, env) + if pi then + return pi + end + end +end + local function run(...) local args = tokenise(...) - local command = table.remove(args, 1) or error('No such program') - local isUrl = not not command:match("^(https?:)") - local env = shell.makeEnv(_ENV) - - if command:match('(.+)%.moon$') then - table.insert(args, 1, command) - command = 'moon' + if #args == 0 then + error('No such program') end - local path, loadFn - if isUrl then - path = command - loadFn = Util.loadUrl - else - path = shell.resolveProgram(command) or error('No such program') - loadFn = loadfile - end + local pi = handleCommand(args, shell.makeEnv(_ENV)) - local O_v_O, err = loadFn(path, env) + local O_v_O, err = pi.load(pi.path, pi.env) if not O_v_O then error(err, -1) end if _ENV.multishell then - _ENV.multishell.setTitle(_ENV.multishell.getCurrent(), fs.getName(path):match('([^%.]+)')) + _ENV.multishell.setTitle(_ENV.multishell.getCurrent(), pi.title) end - tProgramStack[#tProgramStack + 1] = { - path = path, -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$") - env = env, - args = args, - } + tProgramStack[#tProgramStack + 1] = pi - env[ "arg" ] = { [0] = path, table.unpack(args) } - local r = { O_v_O(table.unpack(args)) } + pi.env[ "arg" ] = { [0] = pi.path, table.unpack(pi.args) } + local r = { O_v_O(table.unpack(pi.args)) } tProgramStack[#tProgramStack] = nil diff --git a/sys/modules/opus/map.lua b/sys/modules/opus/map.lua index bd576d3..c751ffc 100644 --- a/sys/modules/opus/map.lua +++ b/sys/modules/opus/map.lua @@ -8,6 +8,7 @@ Map.merge = Util.merge Map.shallowCopy = Util.shallowCopy Map.find = Util.find Map.filter = Util.filter +Map.transpose = Util.transpose function Map.removeMatches(t, values) local function matchAll(entry) diff --git a/sys/modules/opus/ui/components/QuickSelect.lua b/sys/modules/opus/ui/components/QuickSelect.lua index 5626f8c..bbddcba 100644 --- a/sys/modules/opus/ui/components/QuickSelect.lua +++ b/sys/modules/opus/ui/components/QuickSelect.lua @@ -58,8 +58,8 @@ function UI.QuickSelect:applyFilter(filter) self.grid:setIndex(1) end -function UI.QuickSelect:enable() - self.grid.values = { } +function UI.QuickSelect.getFiles() + local t = { } local function recurse(dir) local files = fs.list(dir) for _,f in ipairs(files) do @@ -70,7 +70,7 @@ function UI.QuickSelect:enable() recurse(fullName) end else - _insert(self.grid.values, { + _insert(t, { name = f, dir = dir, lname = f:lower(), @@ -80,6 +80,11 @@ function UI.QuickSelect:enable() end end recurse('') + return t +end + +function UI.QuickSelect:enable() + self.grid.values = self:getFiles() self:applyFilter() self.filterEntry:reset() UI.Window.enable(self) -- 2.49.1 From 1bf6daedffbad2e57ba77a455903abd8592287c3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2020 01:46:52 +0000 Subject: [PATCH 199/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 5e53c03..97a4737 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Wed Jun 10 00:17:16 UTC 2020 -commit e9580d67ebeabdc105440404b3ae4873dc76b397 +Thu Jun 11 01:46:51 UTC 2020 +commit 2fdcc338ad54f2113cd988c0d36c3cab9f4b5f3f Author: kepler155c@gmail.com -Date: Tue Jun 9 18:16:51 2020 -0600 +Date: Wed Jun 10 19:46:34 2020 -0600 - pure lua compatiblity fixes for moonlight, busted, etc + transparent compatiblity for moonscript -- 2.49.1 From a2af4405e79a066a7a395dace99d20d53e44019d Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Thu, 11 Jun 2020 13:28:43 -0600 Subject: [PATCH 200/236] require cleanup + compatibility fixes --- sys/apps/compat.lua | 2 +- sys/apps/shell.lua | 20 +++++++++++--------- sys/kernel.lua | 6 +++--- sys/modules/opus/injector.lua | 12 ++---------- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/sys/apps/compat.lua b/sys/apps/compat.lua index 812410d..c7cfc46 100644 --- a/sys/apps/compat.lua +++ b/sys/apps/compat.lua @@ -3,7 +3,7 @@ local Util = require('opus.util') -- some programs expect to be run in the global scope -- ie. busted, moonscript --- create a new sandbox mimicing pure lua +-- create a new environment mimicing pure lua local fs = _G.fs local shell = _ENV.shell diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index a3f7812..2172b74 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -36,24 +36,26 @@ local function tokenise( ... ) end local defaultHandlers = { - urlHandler = function(args, env) + function(args, env) return args[1]:match("^(https?:)") and { title = fs.getName(args[1]), path = table.remove(args, 1), args = args, load = Util.loadUrl, - env = env, + env = shell.makeEnv(env), } end, - pathHandler = function(args, env) - local command = table.remove(args, 1) + function(args, env) + local command = shell.resolveProgram(table.remove(args, 1)) + or error('No such program') + return { title = fs.getName(command):match('([^%.]+)'), - path = shell.resolveProgram(command) or error('No such program'), + path = command, args = args, load = loadfile, - env = env, + env = shell.makeEnv(env, fs.getDir(command)), } end, } @@ -86,7 +88,7 @@ local function run(...) error('No such program') end - local pi = handleCommand(args, shell.makeEnv(_ENV)) + local pi = handleCommand(args, _ENV) local O_v_O, err = pi.load(pi.path, pi.env) if not O_v_O then @@ -310,9 +312,9 @@ function shell.getRunningInfo() end -- convenience function for making a runnable env -function shell.makeEnv(env) +function shell.makeEnv(env, dir) env = setmetatable(Util.shallowCopy(env), { __index = _G }) - _G.requireInjector(env) + _G.requireInjector(env, dir) return env end diff --git a/sys/kernel.lua b/sys/kernel.lua index 833631d..a4ee040 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -165,9 +165,9 @@ function kernel.getShell() end -- each routine inherits the parent's env -function kernel.makeEnv(env) +function kernel.makeEnv(env, dir) env = setmetatable(Util.shallowCopy(env or _ENV), { __index = _G }) - _G.requireInjector(env) + _G.requireInjector(env, dir) return env end @@ -182,7 +182,7 @@ function kernel.newRoutine(env, args) }, { __index = Routine }) Util.merge(routine, args) - routine.env = args.env or kernel.makeEnv(env) + routine.env = args.env or kernel.makeEnv(env, routine.path and fs.getDir(routine.path)) routine.terminal = routine.terminal or routine.window return routine diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index 8f66685..f9c0ee4 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -38,7 +38,7 @@ do end end -local DEFAULT_PATH = table.concat(defaultPath, ';') +local DEFAULT_PATH = table.concat(defaultPath, ';') local fs = _G.fs local os = _G.os @@ -59,15 +59,9 @@ return function(env, programDir) for pattern in string.gmatch(env.package.path, "[^;]+") do local sPath = string.gsub(pattern, "%?", fname) - -- TODO: if there's no shell, we should not be checking relative paths below - -- as they will resolve to root directory + if programDir and sPath:sub(1, 1) ~= "/" then sPath = fs.combine(programDir, sPath) - elseif env.shell - and type(env.shell.getRunningProgram) == 'function' - and sPath:sub(1, 1) ~= "/" then - - sPath = fs.combine(fs.getDir(env.shell.getRunningProgram() or ''), sPath) end if fs.exists(sPath) and not fs.isDir(sPath) then return loadfile(fs.combine(sPath, ''), env) @@ -131,6 +125,4 @@ return function(env, programDir) end error('Unable to find module ' .. modname, 2) end - - return env.require -- backwards compatible end -- 2.49.1 From 947f502c6d58a712ac6880632f0341b446237310 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jun 2020 19:29:04 +0000 Subject: [PATCH 201/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 97a4737..53f63a0 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Thu Jun 11 01:46:51 UTC 2020 -commit 2fdcc338ad54f2113cd988c0d36c3cab9f4b5f3f +Thu Jun 11 19:29:02 UTC 2020 +commit a2af4405e79a066a7a395dace99d20d53e44019d Author: kepler155c@gmail.com -Date: Wed Jun 10 19:46:34 2020 -0600 +Date: Thu Jun 11 13:28:43 2020 -0600 - transparent compatiblity for moonscript + require cleanup + compatibility fixes -- 2.49.1 From b69dcdeffaf8d1e2ea1a6160169d11723c764a56 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 13 Jun 2020 12:18:04 -0600 Subject: [PATCH 202/236] shell cleanup --- sys/apps/PackageManager.lua | 26 ++- sys/apps/shell.lua | 327 ++++++++++++++---------------------- sys/apps/system/shell.lua | 6 +- sys/modules/opus/entry.lua | 18 +- sys/modules/opus/ui.lua | 4 +- 5 files changed, 164 insertions(+), 217 deletions(-) diff --git a/sys/apps/PackageManager.lua b/sys/apps/PackageManager.lua index 4140732..d0d9598 100644 --- a/sys/apps/PackageManager.lua +++ b/sys/apps/PackageManager.lua @@ -1,4 +1,5 @@ local Ansi = require('opus.ansi') +local Config = require('opus.config') local Packages = require('opus.packages') local UI = require('opus.ui') local Util = require('opus.util') @@ -8,9 +9,11 @@ local term = _G.term UI:configure('PackageManager', ...) +local config = Config.load('package') + local page = UI.Page { grid = UI.ScrollingGrid { - x = 2, ex = 14, y = 2, ey = -5, + x = 2, ex = 14, y = 2, ey = -6, values = { }, columns = { { heading = 'Package', key = 'name' }, @@ -21,13 +24,13 @@ local page = UI.Page { }, add = UI.Button { x = 2, y = -3, - text = 'Install', + text = ' + ', event = 'action', help = 'Install or update', }, remove = UI.Button { - x = 12, y = -3, - text = 'Remove ', + x = 8, y = -3, + text = ' - ', event = 'action', operation = 'uninstall', operationText = 'Remove', @@ -43,6 +46,14 @@ local page = UI.Page { x = 16, y = 3, ey = -5, marginRight = 2, marginLeft = 0, }, + UI.Checkbox { + x = 3, y = -5, + label = 'Compress', + textColor = 'yellow', + backgroundColor = 'primary', + value = config.compression, + help = 'Compress packages (experimental)', + }, action = UI.SlideOut { titleBar = UI.TitleBar { event = 'hide-action', @@ -102,8 +113,6 @@ end function page.action:show() self.output.win:clear() UI.SlideOut.show(self) - --self.output:draw() - --self.output.win.redraw() end function page:run(operation, name) @@ -127,7 +136,6 @@ end function page:updateSelection(selected) self.add.operation = selected.installed and 'update' or 'install' self.add.operationText = selected.installed and 'Update' or 'Install' - self.add.text = selected.installed and 'Update' or 'Install' self.remove.inactive = not selected.installed self.add:draw() self.remove:draw() @@ -146,6 +154,10 @@ function page:eventHandler(event) self.description:draw() self:updateSelection(event.selected) + elseif event.type == 'checkbox_change' then + config.compression = not config.compression + Config.update('package', config) + elseif event.type == 'updateall' then self.operation = 'updateall' self.action.button.text = ' Begin ' diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 2172b74..dbc611e 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -1,13 +1,12 @@ local parentShell = _ENV.shell - _ENV.shell = { } -local fs = _G.fs -local settings = _G.settings -local shell = _ENV.shell - local trace = require('opus.trace') -local Util = require('opus.util') +local Util = require('opus.util') + +local fs = _G.fs +local settings = _G.settings +local shell = _ENV.shell local DIR = (parentShell and parentShell.dir()) or "" local PATH = (parentShell and parentShell.path()) or ".:/rom/programs" @@ -17,16 +16,16 @@ local tCompletionInfo = (parentShell and parentShell.getCompletionInfo()) or {} local bExit = false local tProgramStack = {} -local function tokenise( ... ) - local sLine = table.concat( { ... }, " " ) - local tWords = {} +local function tokenise(...) + local sLine = table.concat({ ... }, ' ') + local tWords = { } local bQuoted = false - for match in string.gmatch( sLine .. "\"", "(.-)\"" ) do + for match in string.gmatch(sLine .. "\"", "(.-)\"") do if bQuoted then - table.insert( tWords, match ) + table.insert(tWords, match) else - for m in string.gmatch( match, "[^ \t]+" ) do - table.insert( tWords, m ) + for m in string.gmatch(match, "[^ \t]+") do + table.insert(tWords, m) end end bQuoted = not bQuoted @@ -36,26 +35,27 @@ local function tokenise( ... ) end local defaultHandlers = { - function(args, env) - return args[1]:match("^(https?:)") and { - title = fs.getName(args[1]), - path = table.remove(args, 1), + function(env, command, args) + return command:match("^(https?:)") and { + title = fs.getName(command), + path = command, args = args, load = Util.loadUrl, - env = shell.makeEnv(env), + env = env, } end, - function(args, env) - local command = shell.resolveProgram(table.remove(args, 1)) + function(env, command, args) + command = env.shell.resolveProgram(command) or error('No such program') + _G.requireInjector(env, fs.getDir(command)) return { title = fs.getName(command):match('([^%.]+)'), path = command, args = args, load = loadfile, - env = shell.makeEnv(env, fs.getDir(command)), + env = env, } end, } @@ -73,9 +73,9 @@ function shell.registerHandler(fn) table.insert(handlers, 1, fn) end -local function handleCommand(args, env) +local function handleCommand(env, command, args) for _,v in pairs(handlers) do - local pi = v(args, env) + local pi = v(env, command, args) if pi then return pi end @@ -88,7 +88,7 @@ local function run(...) error('No such program') end - local pi = handleCommand(args, _ENV) + local pi = handleCommand(shell.makeEnv(_ENV), table.remove(args, 1), args) local O_v_O, err = pi.load(pi.path, pi.env) if not O_v_O then @@ -144,15 +144,11 @@ function shell.resolve( _sPath ) end end -function shell.resolveProgram( _sCommand ) +function shell.resolveProgram(_sCommand) if tAliases[_sCommand] ~= nil then _sCommand = tAliases[_sCommand] end - if _sCommand:match("^(https?:)") then - return _sCommand - end - local path = shell.resolve(_sCommand) if fs.exists(path) and not fs.isDir(path) then return path @@ -161,32 +157,22 @@ function shell.resolveProgram( _sCommand ) return path .. '.lua' end - -- If the path is a global path, use it directly - local sStartChar = string.sub( _sCommand, 1, 1 ) - if sStartChar == "/" or sStartChar == "\\" then - local sPath = fs.combine( "", _sCommand ) - if fs.exists( sPath ) and not fs.isDir( sPath ) then - return sPath - end - return nil - end - -- Otherwise, look on the path variable - for sPath in string.gmatch(PATH or '', "[^:]+") do - sPath = fs.combine(sPath, _sCommand ) - if fs.exists( sPath ) and not fs.isDir( sPath ) then - return sPath - end - if fs.exists(sPath .. '.lua') then - return sPath .. '.lua' + if not _sCommand:find('/') then + for sPath in string.gmatch(PATH or '', "[^:]+") do + sPath = fs.combine(sPath, _sCommand ) + if fs.exists(sPath) and not fs.isDir(sPath) then + return sPath + end + if fs.exists(sPath .. '.lua') then + return sPath .. '.lua' + end end end - -- Not found - return nil end -function shell.programs( _bIncludeHidden ) - local tItems = {} +function shell.programs(_bIncludeHidden) + local tItems = { } -- Add programs from the path for sPath in string.gmatch(PATH, "[^:]+") do @@ -203,96 +189,82 @@ function shell.programs( _bIncludeHidden ) end -- Sort and return - local tItemList = {} - for sItem in pairs( tItems ) do - table.insert( tItemList, sItem ) + local tItemList = { } + for sItem in pairs(tItems) do + table.insert(tItemList, sItem) end - table.sort( tItemList ) + table.sort(tItemList) return tItemList end -local function completeProgram( sLine ) - if #sLine > 0 and string.sub( sLine, 1, 1 ) == "/" then +function shell.completeProgram(sLine) + if #sLine > 0 and string.sub(sLine, 1, 1) == '/' then -- Add programs from the root - return fs.complete( sLine, "", true, false ) - else - local tResults = {} - local tSeen = {} + return fs.complete(sLine, '', true, false) + end - -- Add aliases - for sAlias in pairs( tAliases ) do - if #sAlias > #sLine and string.sub( sAlias, 1, #sLine ) == sLine then - local sResult = string.sub( sAlias, #sLine + 1 ) - if not tSeen[ sResult ] then - table.insert( tResults, sResult ) - tSeen[ sResult ] = true - end + local tResults = { } + local tSeen = { } + + -- Add aliases + for sAlias in pairs( tAliases ) do + if #sAlias > #sLine and string.sub(sAlias, 1, #sLine) == sLine then + local sResult = string.sub(sAlias, #sLine + 1) + if not tSeen[sResult] then + table.insert(tResults, sResult .. ' ') + tSeen[sResult] = true end end + end - -- Add programs from the path - local tPrograms = shell.programs() - for n=1,#tPrograms do - local sProgram = tPrograms[n] - if #sProgram > #sLine and string.sub( sProgram, 1, #sLine ) == sLine then - local sResult = string.sub( sProgram, #sLine + 1 ) - if not tSeen[ sResult ] then - table.insert( tResults, sResult ) - tSeen[ sResult ] = true - end + -- Add programs from the path + local tPrograms = shell.programs() + for n=1,#tPrograms do + local sProgram = tPrograms[n] + if #sProgram >= #sLine and string.sub(sProgram, 1, #sLine) == sLine then + local sResult = string.sub(sProgram, #sLine + 1) + if not tSeen[sResult] then + table.insert(tResults, sResult .. ' ') + tSeen[sResult] = true end end - - -- Sort and return - table.sort( tResults ) - return tResults end -end -local function completeProgramArgument( sProgram, nArgument, sPart, tPreviousParts ) - local tInfo = tCompletionInfo[ sProgram ] - if tInfo then - return tInfo.fnComplete( shell, nArgument, sPart, tPreviousParts ) - end - return nil + -- Sort and return + table.sort(tResults) + return tResults end function shell.complete(sLine) - if #sLine > 0 then - local tWords = tokenise( sLine ) - local nIndex = #tWords - if string.sub( sLine, #sLine, #sLine ) == " " then - nIndex = nIndex + 1 - end - if nIndex == 1 then - local sBit = tWords[1] or "" - local sPath = shell.resolveProgram( sBit ) - if tCompletionInfo[ sPath ] then - return { " " } - else - local tResults = completeProgram( sBit ) - for n=1,#tResults do - local sResult = tResults[n] - local cPath = shell.resolveProgram( sBit .. sResult ) - if tCompletionInfo[ cPath ] then - tResults[n] = sResult .. " " - end - end - return tResults - end - - elseif nIndex > 1 then - local sPath = shell.resolveProgram( tWords[1] ) - local sPart = tWords[nIndex] or "" - local tPreviousParts = tWords - tPreviousParts[nIndex] = nil - return completeProgramArgument( sPath , nIndex - 1, sPart, tPreviousParts ) - end + local tWords = tokenise(sLine) + local nIndex = #tWords + if string.sub(sLine, #sLine, #sLine) == ' ' and #Util.trim(sLine) > 0 then + nIndex = nIndex + 1 end -end -function shell.completeProgram( sProgram ) - return completeProgram( sProgram ) + if nIndex == 0 then + return fs.complete('', shell.dir(), true, false) + + elseif nIndex == 1 then + local results = shell.completeProgram(tWords[1] or '') + for _, v in pairs(fs.complete(table.concat(tWords, ' '), shell.dir(), true, false)) do + table.insert(results, v) + end + return results + + else + local sPath = shell.resolveProgram(tWords[1]) + local sPart = tWords[nIndex] or '' + local tPreviousParts = tWords + tPreviousParts[nIndex] = nil + local results + local tInfo = tCompletionInfo[sPath] + if tInfo then + results = tInfo.fnComplete(shell, nIndex - 1, sPart, tPreviousParts) + end + return results and #results > 0 and results + or fs.complete(sPart, shell.dir(), true, false) + end end function shell.setCompletionFunction(sProgram, fnComplete) @@ -318,11 +290,11 @@ function shell.makeEnv(env, dir) return env end -function shell.setAlias( _sCommand, _sProgram ) +function shell.setAlias(_sCommand, _sProgram) tAliases[_sCommand] = _sProgram end -function shell.clearAlias( _sCommand ) +function shell.clearAlias(_sCommand) tAliases[_sCommand] = nil end @@ -394,23 +366,16 @@ local textutils = _G.textutils local oldTerm local terminal = term.current() +local _len = string.len local _rep = string.rep local _sub = string.sub -if not terminal.scrollUp then - terminal = Terminal.window(term.current()) - terminal.setMaxScroll(200) - oldTerm = term.redirect(terminal) -end - local config = { color = { textColor = colors.white, commandTextColor = colors.yellow, directoryTextColor = colors.orange, - directoryBackgroundColor = colors.black, promptTextColor = colors.blue, - promptBackgroundColor = colors.black, directoryColor = colors.green, fileColor = colors.white, backgroundColor = colors.black, @@ -427,43 +392,15 @@ if not _colors.backgroundColor then _colors.fileColor = colors.white end -local palette = { } -for n = 1, 16 do - palette[2 ^ (n - 1)] = _sub("0123456789abcdef", n, n) +if not terminal.scrollUp then + terminal = Terminal.window(term.current()) + terminal.setMaxScroll(200) + oldTerm = term.redirect(terminal) + term.setBackgroundColor(_colors.backgroundColor) + term.clear() end -if not term.isColor() then - _colors = { } - for k, v in pairs(config.color) do - _colors[k] = Terminal.colorToGrayscale(v) - end - for n = 1, 16 do - palette[2 ^ (n - 1)] = _sub("088888878877787f", n, n) - end -end - -local function autocompleteArgument(program, words) - local word = '' - if #words > 1 then - word = words[#words] - end - - local tInfo = tCompletionInfo[program] - return tInfo.fnComplete(shell, #words - 1, word, words) -end - -local function autocompleteAnything(line, words) - local results = shell.complete(line) - - if results and #results == 0 and #words == 1 then - results = nil - end - if not results then - results = fs.complete(words[#words] or '', shell.dir(), true, false) - end - - return results -end +local palette = terminal.canvas.palette local function autocomplete(line) local words = { } @@ -477,14 +414,7 @@ local function autocomplete(line) words = { '' } end - local results - - local program = shell.resolveProgram(words[1]) - if tCompletionInfo[program] then - results = autocompleteArgument(program, words) or { } - else - results = autocompleteAnything(line, words) or { } - end + local results = shell.complete(line) or { } Util.filterInplace(results, function(f) return not Util.key(results, f .. '/') @@ -497,8 +427,8 @@ local function autocomplete(line) if #results == 1 then words[#words] = results[1] return table.concat(words, ' ') - elseif #results > 1 then + elseif #results > 1 then local function someComplete() -- ugly (complete as much as possible) local word = words[#words] or '' @@ -507,16 +437,16 @@ local function autocomplete(line) local ch for _,f in ipairs(results) do if #f < i then - words[#words] = string.sub(f, 1, i - 1) + words[#words] = _sub(f, 1, i - 1) return table.concat(words, ' ') end if not ch then - ch = string.sub(f, i, i) - elseif string.sub(f, i, i) ~= ch then + ch = _sub(f, i, i) + elseif _sub(f, i, i) ~= ch then if i == #word + 1 then return end - words[#words] = string.sub(f, 1, i - 1) + words[#words] = _sub(f, 1, i - 1) return table.concat(words, ' ') end end @@ -559,10 +489,9 @@ local function autocomplete(line) local tw = term.getSize() local nMaxLen = tw / 8 for _,sItem in pairs(results) do - nMaxLen = math.max(string.len(sItem) + 1, nMaxLen) + nMaxLen = math.max(_len(sItem) + 1, nMaxLen) end - local w = term.getSize() - local nCols = math.floor(w / nMaxLen) + local nCols = math.floor(tw / nMaxLen) if #tDirs < nCols then for _ = #tDirs + 1, nCols do table.insert(tDirs, '') @@ -577,11 +506,9 @@ local function autocomplete(line) end term.setTextColour(_colors.promptTextColor) - term.setBackgroundColor(_colors.promptBackgroundColor) term.write("$ " ) term.setTextColour(_colors.commandTextColor) - term.setBackgroundColor(_colors.backgroundColor) return line end end @@ -608,17 +535,16 @@ local function shellRead(history) term.setCursorPos(3, cy) entry.value = entry.value or '' local filler = #entry.value < lastLen - and string.rep(' ', lastLen - #entry.value) + and _rep(' ', lastLen - #entry.value) or '' - local str = string.sub(entry.value, entry.scroll + 1, entry.width + entry.scroll) .. filler + local str = _sub(entry.value, entry.scroll + 1, entry.width + entry.scroll) .. filler local fg = _rep(palette[_colors.commandTextColor], #str) local bg = _rep(palette[_colors.backgroundColor], #str) if entry.mark.active then - local sx = entry.mark.x - entry.scroll + 1 - local ex = entry.mark.ex - entry.scroll + 1 - bg = string.rep('f', sx - 1) .. - string.rep('7', ex - sx) .. - string.rep('f', #str - ex + 1) + bg = _rep('f', entry.mark.x) .. + _rep('7', entry.mark.ex - entry.mark.x) .. + _rep('f', #entry.value - entry.mark.ex + #filler + 1) + bg = _sub(bg, entry.scroll + 1, entry.scroll + #str) end term.blit(str, fg, bg) updateCursor() @@ -689,34 +615,26 @@ local function shellRead(history) end print() - term.setCursorBlink( false ) + term.setCursorBlink(false) return entry.value or '' end local history = History.load('usr/.shell_history', 100) term.setBackgroundColor(_colors.backgroundColor) ---term.clear() -if settings.get("motd.enabled") then +if settings.get("motd.enable") then shell.run("motd") end while not bExit do - local cx = term.getCursorPos() - if cx ~= 1 then - print() - end if config.displayDirectory then term.setTextColour(_colors.directoryTextColor) - term.setBackgroundColor(_colors.directoryBackgroundColor) print('==' .. os.getComputerLabel() .. ':/' .. DIR) end term.setTextColour(_colors.promptTextColor) - term.setBackgroundColor(_colors.promptBackgroundColor) term.write("$ " ) term.setTextColour(_colors.commandTextColor) - term.setBackgroundColor(_colors.backgroundColor) local sLine = shellRead(history) if bExit then -- terminated break @@ -728,6 +646,11 @@ while not bExit do term.setTextColour(_colors.textColor) if #sLine > 0 then local result, err = shell.run(sLine) + local cx = term.getCursorPos() + if cx ~= 1 then + print() + end + term.setBackgroundColor(_colors.backgroundColor) if not result and err then _G.printError(err) end diff --git a/sys/apps/system/shell.lua b/sys/apps/system/shell.lua index 8fe9807..8440408 100644 --- a/sys/apps/system/shell.lua +++ b/sys/apps/system/shell.lua @@ -18,9 +18,7 @@ local defaults = { textColor = colors.white, commandTextColor = colors.yellow, directoryTextColor = colors.orange, - directoryBackgroundColor = colors.black, promptTextColor = colors.blue, - promptBackgroundColor = colors.black, directoryColor = colors.green, fileColor = colors.white, backgroundColor = colors.black, @@ -86,12 +84,12 @@ return UI.Tab { if config.displayDirectory then self:write(1, 1, '==' .. os.getComputerLabel() .. ':/dir/etc', - _colors.directoryBackgroundColor, _colors.directoryTextColor) + _colors.backgroundColor, _colors.directoryTextColor) offset = 1 end self:write(1, 1 + offset, '$ ', - _colors.promptBackgroundColor, _colors.promptTextColor) + _colors.backgroundColor, _colors.promptTextColor) self:write(3, 1 + offset, 'ls /', _colors.backgroundColor, _colors.commandTextColor) diff --git a/sys/modules/opus/entry.lua b/sys/modules/opus/entry.lua index e15f853..447beff 100644 --- a/sys/modules/opus/entry.lua +++ b/sys/modules/opus/entry.lua @@ -222,7 +222,7 @@ function Entry:paste(ie) end end -function Entry:forcePaste() +function Entry.forcePaste() os.queueEvent('clipboard_paste') end @@ -234,7 +234,9 @@ end function Entry:markBegin() if not self.mark.active then - self.mark.active = true + if #_val(self.value) > 0 then + self.mark.active = true + end self.mark.anchor = { x = self.pos } end end @@ -271,6 +273,8 @@ function Entry:markLeft() self:markBegin() if self:moveLeft() then self:markFinish() + else + self.mark.continue = self.mark.active end end @@ -278,6 +282,8 @@ function Entry:markRight() self:markBegin() if self:moveRight() then self:markFinish() + else + self.mark.continue = self.mark.active end end @@ -305,6 +311,8 @@ function Entry:markNextWord() self:markBegin() if self:moveWordRight() then self:markFinish() + else + self.mark.continue = self.mark.active end end @@ -312,6 +320,8 @@ function Entry:markPrevWord() self:markBegin() if self:moveWordLeft() then self:markFinish() + else + self.mark.continue = self.mark.active end end @@ -330,6 +340,8 @@ function Entry:markHome() self:markBegin() if self:moveHome() then self:markFinish() + else + self.mark.continue = self.mark.active end end @@ -337,6 +349,8 @@ function Entry:markEnd() self:markBegin() if self:moveEnd() then self:markFinish() + else + self.mark.continue = self.mark.active end end diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 87865ee..8451e1d 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -989,6 +989,7 @@ function UI.Device:postInit() self.device.setTextScale = function() end end + self._obg = term.getBackgroundColor() self.device.setTextScale(self.textScale) self.width, self.height = self.device.getSize() self.isColor = self.device.isColor() @@ -1025,8 +1026,7 @@ function UI.Device:setTextScale(textScale) end function UI.Device:reset() - self.device.setBackgroundColor(colors.black) - self.device.setTextColor(colors.white) + self.device.setBackgroundColor(self._obg) self.device.clear() self.device.setCursorPos(1, 1) end -- 2.49.1 From 9bf91a8762a9fa33f72f72149e6be4d835c263ca Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Jun 2020 18:18:21 +0000 Subject: [PATCH 203/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 53f63a0..bb35670 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Thu Jun 11 19:29:02 UTC 2020 -commit a2af4405e79a066a7a395dace99d20d53e44019d +Sat Jun 13 18:18:20 UTC 2020 +commit b69dcdeffaf8d1e2ea1a6160169d11723c764a56 Author: kepler155c@gmail.com -Date: Thu Jun 11 13:28:43 2020 -0600 +Date: Sat Jun 13 12:18:04 2020 -0600 - require cleanup + compatibility fixes + shell cleanup -- 2.49.1 From 7df4a47ba041c411598a874346058c09d2b3ef7b Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 13 Jun 2020 20:26:17 -0600 Subject: [PATCH 204/236] oops --- sys/apps/shell.lua | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index dbc611e..febcde1 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -149,26 +149,30 @@ function shell.resolveProgram(_sCommand) _sCommand = tAliases[_sCommand] end - local path = shell.resolve(_sCommand) - if fs.exists(path) and not fs.isDir(path) then - return path - end - if fs.exists(path .. '.lua') then - return path .. '.lua' + local function check(f) + return fs.exists(f) and not fs.isDir(f) and f end - -- Otherwise, look on the path variable - if not _sCommand:find('/') then - for sPath in string.gmatch(PATH or '', "[^:]+") do - sPath = fs.combine(sPath, _sCommand ) - if fs.exists(sPath) and not fs.isDir(sPath) then - return sPath - end - if fs.exists(sPath .. '.lua') then - return sPath .. '.lua' + local function inPath() + -- Otherwise, look on the path variable + if not _sCommand:find('/') then + for sPath in string.gmatch(PATH or '', "[^:]+") do + sPath = fs.combine(sPath, _sCommand ) + if fs.exists(sPath) and not fs.isDir(sPath) then + return sPath + end + if fs.exists(sPath .. '.lua') then + return sPath .. '.lua' + end end end end + + return check(_sCommand) + or check(_sCommand .. '.lua') + or check(shell.resolve(_sCommand)) + or check(shell.resolve(_sCommand) .. '.lua') + or inPath() end function shell.programs(_bIncludeHidden) -- 2.49.1 From 156b604a58b8958d50581acc71a9a8bb2d7cd3ee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jun 2020 02:26:36 +0000 Subject: [PATCH 205/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index bb35670..5b5f0e8 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat Jun 13 18:18:20 UTC 2020 -commit b69dcdeffaf8d1e2ea1a6160169d11723c764a56 +Sun Jun 14 02:26:34 UTC 2020 +commit 7df4a47ba041c411598a874346058c09d2b3ef7b Author: kepler155c@gmail.com -Date: Sat Jun 13 12:18:04 2020 -0600 +Date: Sat Jun 13 20:26:17 2020 -0600 - shell cleanup + oops -- 2.49.1 From 42bd4b2b69eff2a610c2fcdd53cb500f3af7afab Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 15 Jun 2020 19:54:42 -0600 Subject: [PATCH 206/236] remove preferred apps/alternates - same can be accomplished using aliases --- sys/apps/Files.lua | 3 +- sys/apps/Overview.lua | 7 ++-- sys/apps/ShellLauncher.lua | 4 +- sys/apps/network/telnet.lua | 4 +- sys/apps/shell.lua | 40 +++++++++++------- sys/apps/system/alternate.lua | 77 ---------------------------------- sys/modules/opus/alternate.lua | 48 --------------------- sys/modules/opus/fs/ramfs.lua | 32 +++++++++++++- 8 files changed, 64 insertions(+), 151 deletions(-) delete mode 100644 sys/apps/system/alternate.lua delete mode 100644 sys/modules/opus/alternate.lua diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 8cb09cc..288d0a5 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -1,4 +1,3 @@ -local Alt = require('opus.alternate') local Config = require('opus.config') local Event = require('opus.event') local pastebin = require('opus.http.pastebin') @@ -351,7 +350,7 @@ function Browser:eventHandler(event) self:setStatus('Started cloud edit') elseif event.type == 'shell' then - self:run(Alt.get('shell')) + self:run('shell') elseif event.type == 'refresh' then self:updateDirectory(self.dir) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index d8cb0ef..722127e 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -1,4 +1,3 @@ -local Alt = require('opus.alternate') local Array = require('opus.array') local class = require('opus.class') local Config = require('opus.config') @@ -469,13 +468,13 @@ function page:eventHandler(event) shell.switchTab(shell.openTab(event.button.app.run)) elseif event.type == 'shell' then - shell.switchTab(shell.openTab(Alt.get('shell'))) + shell.switchTab(shell.openTab('shell')) elseif event.type == 'lua' then - shell.switchTab(shell.openTab(Alt.get('lua'))) + shell.switchTab(shell.openTab('Lua')) elseif event.type == 'files' then - shell.switchTab(shell.openTab(Alt.get('files'))) + shell.switchTab(shell.openTab('Files')) elseif event.type == 'network' then shell.switchTab(shell.openTab('Network')) diff --git a/sys/apps/ShellLauncher.lua b/sys/apps/ShellLauncher.lua index 570234b..293bced 100644 --- a/sys/apps/ShellLauncher.lua +++ b/sys/apps/ShellLauncher.lua @@ -1,5 +1,3 @@ -local Alt = require('opus.alternate') - local kernel = _G.kernel local os = _G.os local shell = _ENV.shell @@ -20,7 +18,7 @@ kernel.hook('kernel_focus', function(_, eventData) end end if nextTab == launcherTab then - shell.switchTab(shell.openTab(Alt.get('shell'))) + shell.switchTab(shell.openTab('shell')) else shell.switchTab(nextTab.uid) end diff --git a/sys/apps/network/telnet.lua b/sys/apps/network/telnet.lua index ff92298..53a1ce1 100644 --- a/sys/apps/network/telnet.lua +++ b/sys/apps/network/telnet.lua @@ -1,9 +1,9 @@ -local Alt = require('opus.alternate') local Event = require('opus.event') local Socket = require('opus.socket') local Util = require('opus.util') local kernel = _G.kernel +local shell = _ENV.shell local term = _G.term local window = _G.window @@ -46,7 +46,7 @@ local function telnetHost(socket, mode) title = mode .. ' client', hidden = true, fn = function() - Util.run(kernel.makeEnv(_ENV), Alt.get('shell'), table.unpack(termInfo.program)) + Util.run(kernel.makeEnv(_ENV), shell.resolveProgram('shell'), table.unpack(termInfo.program)) if socket.queue then socket:write(socket.queue) end diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index febcde1..9672d6d 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -131,7 +131,14 @@ function shell.exit() end function shell.dir() return DIR end -function shell.setDir(d) DIR = d end +function shell.setDir(d) + d = fs.combine(d, '') + if not fs.isDir(d) then + error("Not a directory", 2) + end + DIR = d +end + function shell.path() return PATH end function shell.setPath(p) PATH = p end @@ -155,24 +162,29 @@ function shell.resolveProgram(_sCommand) local function inPath() -- Otherwise, look on the path variable - if not _sCommand:find('/') then - for sPath in string.gmatch(PATH or '', "[^:]+") do - sPath = fs.combine(sPath, _sCommand ) - if fs.exists(sPath) and not fs.isDir(sPath) then - return sPath - end - if fs.exists(sPath .. '.lua') then - return sPath .. '.lua' - end + for sPath in string.gmatch(PATH or '', "[^:]+") do + sPath = fs.combine(sPath, _sCommand ) + if check(sPath) then + return sPath + end + if check(sPath .. '.lua') then + return sPath .. '.lua' end end end - return check(_sCommand) - or check(_sCommand .. '.lua') - or check(shell.resolve(_sCommand)) + if not _sCommand:find('/') then + return inPath() + end + + -- so... even if you are in the rom directory and you run: + -- 'packages/common/edit.lua', allow this even though it + -- does not use a leading slash. Ideally, fs.combine would + -- provide the leading slash... but it does not. + return check(shell.resolve(_sCommand)) or check(shell.resolve(_sCommand) .. '.lua') - or inPath() + or check(_sCommand) + or check(_sCommand .. '.lua') end function shell.programs(_bIncludeHidden) diff --git a/sys/apps/system/alternate.lua b/sys/apps/system/alternate.lua deleted file mode 100644 index 35bd601..0000000 --- a/sys/apps/system/alternate.lua +++ /dev/null @@ -1,77 +0,0 @@ -local Array = require('opus.array') -local Config = require('opus.config') -local UI = require('opus.ui') - -local tab = UI.Tab { - title = 'Preferred', - description = 'Select preferred applications', - apps = UI.ScrollingGrid { - x = 2, y = 2, - ex = 12, ey = -3, - columns = { - { key = 'name' }, - }, - sortColumn = 'name', - disableHeader = true, - }, - choices = UI.Grid { - x = 14, y = 2, - ex = -2, ey = -3, - disableHeader = true, - columns = { - { key = 'file' }, - }, - getRowTextColor = function(self, row) - if row == self.values[1] then - return 'yellow' - end - return UI.Grid.getRowTextColor(self, row) - end, - }, - statusBar = UI.StatusBar { - values = 'Double-click to set as preferred' - }, -} - -function tab:updateChoices() - local app = self.apps:getSelected().name - local choices = { } - for _, v in pairs(self.config[app]) do - table.insert(choices, { file = v }) - end - self.choices:setValues(choices) - self.choices:draw() -end - -function tab:enable() - self.config = Config.load('alternate') - - local apps = { } - for k, _ in pairs(self.config) do - table.insert(apps, { name = k }) - end - self.apps:setValues(apps) - - self:updateChoices() - - UI.Tab.enable(self) -end - -function tab:eventHandler(event) - if event.type == 'grid_focus_row' and event.element == self.apps then - self:updateChoices() - - elseif event.type == 'grid_select' and event.element == self.choices then - local app = self.apps:getSelected().name - Array.removeByValue(self.config[app], event.selected.file) - table.insert(self.config[app], 1, event.selected.file) - self:updateChoices() - Config.update('alternate', self.config) - - else - return UI.Tab.eventHandler(self, event) - end - return true -end - -return tab diff --git a/sys/modules/opus/alternate.lua b/sys/modules/opus/alternate.lua deleted file mode 100644 index 101de58..0000000 --- a/sys/modules/opus/alternate.lua +++ /dev/null @@ -1,48 +0,0 @@ -local Array = require('opus.array') -local Config = require('opus.config') -local Util = require('opus.util') - -local function getConfig() - return Config.load('alternate', { - shell = { - 'sys/apps/shell.lua', - 'rom/programs/shell.lua', - }, - lua = { - 'sys/apps/Lua.lua', - 'rom/programs/lua.lua', - }, - files = { - 'sys/apps/Files.lua', - } - }) -end - -local Alt = { } - -function Alt.get(key) - return getConfig()[key][1] -end - -function Alt.set(key, value) - local config = getConfig() - Array.removeByValue(config[key], value) - table.insert(config[key], 1, value) - Config.update('alternate', config) -end - -function Alt.remove(key, value) - local config = getConfig() - Array.removeByValue(config[key], value) - Config.update('alternate', config) -end - -function Alt.add(key, value) - local config = getConfig() - if not Util.contains(config[key], value) then - table.insert(config[key], value) - Config.update('alternate', config) - end -end - -return Alt diff --git a/sys/modules/opus/fs/ramfs.lua b/sys/modules/opus/fs/ramfs.lua index 9c5d718..9e8f613 100644 --- a/sys/modules/opus/fs/ramfs.lua +++ b/sys/modules/opus/fs/ramfs.lua @@ -79,10 +79,40 @@ function ramfs.list(node, dir) end function ramfs.open(node, fn, fl) - if fl ~= 'r' and fl ~= 'w' and fl ~= 'rb' and fl ~= 'wb' then + local modes = Util.transpose { 'r', 'w', 'rb', 'wb', 'a' } + if not modes[fl] then error('Unsupported mode') end + if fl == 'a' then + if node.mountPoint ~= fn then + fl = 'w' + else + local c = type(node.contents) == 'table' + and string.char(table.unpack(node.contents)) + or node.contents + or '' + + return { + write = function(str) + c = c .. str + end, + writeLine = function(str) + c = c .. str .. '\n' + end, + flush = function() + node.contents = c + node.size = #c + end, + close = function() + node.contents = c + node.size = #c + c = nil + end, + } + end + end + if fl == 'r' then if node.mountPoint ~= fn then return -- 2.49.1 From 1cb2c5f7857b9b52528f76e67c12758f55df55e6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2020 01:55:00 +0000 Subject: [PATCH 207/236] Update version date --- .opus_version | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.opus_version b/.opus_version index 5b5f0e8..cb056a5 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sun Jun 14 02:26:34 UTC 2020 -commit 7df4a47ba041c411598a874346058c09d2b3ef7b +Tue Jun 16 01:54:59 UTC 2020 +commit 42bd4b2b69eff2a610c2fcdd53cb500f3af7afab Author: kepler155c@gmail.com -Date: Sat Jun 13 20:26:17 2020 -0600 +Date: Mon Jun 15 19:54:42 2020 -0600 - oops + remove preferred apps/alternates - same can be accomplished using aliases -- 2.49.1 From 45f1bd1c5de42841aee47d64ec003bb1f41a4360 Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Fri, 24 Jul 2020 20:36:57 -0400 Subject: [PATCH 208/236] Put pastebin link in the right case (Fixes #40) (#41) Pastebin recently removed support for case-insensitive links with no warning. This puts the original casing of the paste ID in README.md, instead of all-lowercase. Some other things might also need to be changed! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 606978a..5e9f684 100644 --- a/README.md +++ b/README.md @@ -16,5 +16,5 @@ ## Install ``` -pastebin run uzghlbnc +pastebin run UzGHLbNC ``` -- 2.49.1 From b45cd45bcb5ce127f4a1ec3614a5025bb3952271 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2020 00:37:14 +0000 Subject: [PATCH 209/236] Update version date --- .opus_version | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index cb056a5..baf0998 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,8 @@ -Tue Jun 16 01:54:59 UTC 2020 -commit 42bd4b2b69eff2a610c2fcdd53cb500f3af7afab -Author: kepler155c@gmail.com -Date: Mon Jun 15 19:54:42 2020 -0600 +Sat Jul 25 00:37:13 UTC 2020 +commit 45f1bd1c5de42841aee47d64ec003bb1f41a4360 +Author: Kan18 <24967425+Kan18@users.noreply.github.com> +Date: Fri Jul 24 20:36:57 2020 -0400 - remove preferred apps/alternates - same can be accomplished using aliases + Put pastebin link in the right case (Fixes #40) (#41) + + Pastebin recently removed support for case-insensitive links with no warning. This puts the original casing of the paste ID in README.md, instead of all-lowercase. Some other things might also need to be changed! -- 2.49.1 From 816ea366abc21ab1d65a57137fb25902a615034d Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Fri, 24 Jul 2020 20:41:21 -0400 Subject: [PATCH 210/236] TLCO fix & boot file extension change (#37) This commit fixes the TLCO boot option (which hasn't been working for a while now), and also changes boot file extensions from .boot to .lua. --- startup.lua | 21 ++++++++++++++++++--- sys/boot/{kiosk.boot => kiosk.lua} | 0 sys/boot/{opus.boot => opus.lua} | 0 sys/boot/tlco.boot | 15 --------------- sys/boot/tlco.lua | 30 ++++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 18 deletions(-) rename sys/boot/{kiosk.boot => kiosk.lua} (100%) rename sys/boot/{opus.boot => opus.lua} (100%) delete mode 100644 sys/boot/tlco.boot create mode 100644 sys/boot/tlco.lua diff --git a/startup.lua b/startup.lua index f72fb21..c81a2c1 100644 --- a/startup.lua +++ b/startup.lua @@ -29,9 +29,10 @@ local function loadBootOptions() preload = { }, menu = { { prompt = os.version() }, - { prompt = 'Opus' , args = { '/sys/boot/opus.boot' } }, - { prompt = 'Opus Shell' , args = { '/sys/boot/opus.boot', 'sys/apps/shell.lua' } }, - { prompt = 'Opus Kiosk' , args = { '/sys/boot/kiosk.boot' } }, + { prompt = 'Opus' , args = { '/sys/boot/opus.lua' } }, + { prompt = 'Opus Shell' , args = { '/sys/boot/opus.lua', '/sys/apps/shell.lua' } }, + { prompt = 'Opus Kiosk' , args = { '/sys/boot/kiosk.lua' } }, + { prompt = 'Opus TLCO' , args = { '/sys/boot/tlco.lua' } }, }, })) f.close() @@ -41,6 +42,20 @@ local function loadBootOptions() local options = textutils.unserialize(f.readAll()) f.close() + -- Backwards compatibility for .startup.boot files created before sys/boot files' extensions were changed + local changed = false + for _, item in pairs(options.menu) do + if item.args and item.args[1]:match("/?sys/boot/%l+%.boot") then + item.args[1] = item.args[1]:gsub("%.boot", "%.lua") + changed = true + end + end + if changed then + local f = fs.open(".startup.boot", "w") + f.write(textutils.serialize(options)) + f.close() + end + return options end diff --git a/sys/boot/kiosk.boot b/sys/boot/kiosk.lua similarity index 100% rename from sys/boot/kiosk.boot rename to sys/boot/kiosk.lua diff --git a/sys/boot/opus.boot b/sys/boot/opus.lua similarity index 100% rename from sys/boot/opus.boot rename to sys/boot/opus.lua diff --git a/sys/boot/tlco.boot b/sys/boot/tlco.boot deleted file mode 100644 index 350e68f..0000000 --- a/sys/boot/tlco.boot +++ /dev/null @@ -1,15 +0,0 @@ -local pullEvent = os.pullEventRaw -local shutdown = os.shutdown - -os.pullEventRaw = function() - error('') -end - -os.shutdown = function() - os.pullEventRaw = pullEvent - os.shutdown = shutdown - - os.run(getfenv(1), 'sys/boot/opus.boot') -end - -os.queueEvent('modem_message') diff --git a/sys/boot/tlco.lua b/sys/boot/tlco.lua new file mode 100644 index 0000000..cbbba0c --- /dev/null +++ b/sys/boot/tlco.lua @@ -0,0 +1,30 @@ +local run = os.run +local shutdown = os.shutdown + +local args = {...} -- keep the args so that they can be passed to opus.lua + +os.run = function() + os.run = run +end + +os.shutdown = function() + os.shutdown = shutdown + + _ENV.multishell = nil -- prevent sys/apps/shell.lua erroring for odd reasons + + local success, err = pcall(function() + run(_ENV, 'sys/boot/opus.lua', table.unpack(args)) + end) + term.redirect(term.native()) + if success then + print("Opus OS abruptly stopped.") + else + printError("Opus OS errored.") + printError(err) + end + print("Press any key to continue.") + os.pullEvent("key") + shutdown() +end + +shell.exit() \ No newline at end of file -- 2.49.1 From 624af53f4e7cd4b249f4c96f0e49ecdcc1b27f4d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2020 00:41:36 +0000 Subject: [PATCH 211/236] Update version date --- .opus_version | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index baf0998..8cea539 100644 --- a/.opus_version +++ b/.opus_version @@ -1,8 +1,9 @@ -Sat Jul 25 00:37:13 UTC 2020 -commit 45f1bd1c5de42841aee47d64ec003bb1f41a4360 +Sat Jul 25 00:41:35 UTC 2020 +commit 816ea366abc21ab1d65a57137fb25902a615034d Author: Kan18 <24967425+Kan18@users.noreply.github.com> -Date: Fri Jul 24 20:36:57 2020 -0400 +Date: Fri Jul 24 20:41:21 2020 -0400 - Put pastebin link in the right case (Fixes #40) (#41) + TLCO fix & boot file extension change (#37) - Pastebin recently removed support for case-insensitive links with no warning. This puts the original casing of the paste ID in README.md, instead of all-lowercase. Some other things might also need to be changed! + This commit fixes the TLCO boot option (which hasn't been working for a + while now), and also changes boot file extensions from .boot to .lua. -- 2.49.1 From c24a5a71151b7c6ecd85da8637e9b1eb599d5fbb Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 25 Jul 2020 18:52:41 -0600 Subject: [PATCH 212/236] help cleanup --- sys/apps/Network.lua | 29 +++-------------------------- sys/help/Networking.txt | 9 +++++++++ sys/help/Opus-Applications.txt | 31 ------------------------------- sys/help/Opus.txt | 12 +----------- sys/help/Packages.txt | 13 +++++++++++++ 5 files changed, 26 insertions(+), 68 deletions(-) create mode 100644 sys/help/Networking.txt delete mode 100644 sys/help/Opus-Applications.txt create mode 100644 sys/help/Packages.txt diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index 1f03fe4..f5f91d3 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -6,7 +6,6 @@ local Util = require('opus.util') local device = _G.device local network = _G.network -local os = _G.os local shell = _ENV.shell UI:configure('Network', ...) @@ -103,30 +102,6 @@ local page = UI.Page { return UI.SlideOut.eventHandler(self, event) end, }, - help = UI.SlideOut { - x = 5, ex = -5, height = 8, y = -8, - titleBar = UI.TitleBar { - title = 'Network Help', - event = 'slide_hide', - }, - text = UI.TextArea { - x = 1, y = 2, - marginLeft = 1, - value = [[ - -In order to connect to another computer: - -1. The target computer must have a password set (run 'password' from the shell prompt). - -2. From this computer, click trust and enter the password for that computer. - -This only needs to be done once. - ]], - }, - accelerators = { - q = 'slide_hide', - } - }, notification = UI.Notification { }, accelerators = { t = 'telnet', @@ -207,12 +182,14 @@ function page:eventHandler(event) elseif event.type == 'vnc' then shell.openForegroundTab('vnc.lua ' .. t.id) + --[[ os.queueEvent('overview_shortcut', { title = t.label, category = "VNC", icon = "\010\030 \009\009\031e\\\031 \031e/\031dn\010\030 \009\009 \031e\\/\031 \031bc", run = "vnc.lua " .. t.id, }) + --]] elseif event.type == 'clear' then Util.clear(network) @@ -231,7 +208,7 @@ function page:eventHandler(event) end if event.type == 'help' then - self.help:show() + shell.switchTab(shell.openTab('Help Networking')) elseif event.type == 'ports' then self.ports.grid:update() diff --git a/sys/help/Networking.txt b/sys/help/Networking.txt new file mode 100644 index 0000000..25d12f5 --- /dev/null +++ b/sys/help/Networking.txt @@ -0,0 +1,9 @@ +Wireless Networking +=================== +To establish one-way trust between two computers with modems, run the following in a shell prompt: + +On the target computer, run: +> password + +On the source computer, run: +> trust \ No newline at end of file diff --git a/sys/help/Opus-Applications.txt b/sys/help/Opus-Applications.txt deleted file mode 100644 index 01cfb5e..0000000 --- a/sys/help/Opus-Applications.txt +++ /dev/null @@ -1,31 +0,0 @@ -Opus applications are grouped into packages with a common theme. - -To install a package, use either the System -> Packages program or the package command line program. - -Shell usage: - -> package list -> package install -> package update -> package updateall -> package uninstall - -Package definitions are located in usr/config/packages. This file can be modified to add custom packages. - -Current stable packages -======================= - -* core -Programming and miscellaneous applications. Also contains drivers needed for other packages. - -* builder -A program for creating structures from schematic files using a turtle (requires core). - -* farms -Various programs for farming resources (wood, crops, animals). - -* milo -An A/E like storage implementation (requires core). - -* miners -Mining programs. diff --git a/sys/help/Opus.txt b/sys/help/Opus.txt index e4c1fc3..c1a8b55 100644 --- a/sys/help/Opus.txt +++ b/sys/help/Opus.txt @@ -6,17 +6,7 @@ Shortcut Keys * Control-d: Show/toggle logging screen * Control-c: Copy (in most applications) * Control-shift-v: Paste from internal clipboard - * Control-shift-doubleclick: Open in Lua the clicked UI element - -Wireless Networking -=================== -To establish one-way trust between two computers with modems, run the following in a shell prompt: - -On the target computer, run: -> password - -On the source computer, run: -> trust + * Control-shift-click: Open the clicked UI element in Lua Running Custom Programs ======================= diff --git a/sys/help/Packages.txt b/sys/help/Packages.txt new file mode 100644 index 0000000..9c494d3 --- /dev/null +++ b/sys/help/Packages.txt @@ -0,0 +1,13 @@ +Opus applications are grouped into packages with a common theme. + +To install a package, use either the System -> Packages program or the package command line program. + +Shell usage: + +> package list +> package install +> package update +> package updateall +> package uninstall + +Package definitions are located in usr/config/packages. This file can be modified to add custom packages. -- 2.49.1 From 48f32946ecf74d78d3bdeb6717231e1781ae2925 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 26 Jul 2020 00:52:59 +0000 Subject: [PATCH 213/236] Update version date --- .opus_version | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.opus_version b/.opus_version index 8cea539..ec678fc 100644 --- a/.opus_version +++ b/.opus_version @@ -1,9 +1,6 @@ -Sat Jul 25 00:41:35 UTC 2020 -commit 816ea366abc21ab1d65a57137fb25902a615034d -Author: Kan18 <24967425+Kan18@users.noreply.github.com> -Date: Fri Jul 24 20:41:21 2020 -0400 +Sun Jul 26 00:52:58 UTC 2020 +commit c24a5a71151b7c6ecd85da8637e9b1eb599d5fbb +Author: kepler155c@gmail.com +Date: Sat Jul 25 18:52:41 2020 -0600 - TLCO fix & boot file extension change (#37) - - This commit fixes the TLCO boot option (which hasn't been working for a - while now), and also changes boot file extensions from .boot to .lua. + help cleanup -- 2.49.1 From f4494d6103c0a82f6ab35d771c70a9c63afa72d1 Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Mon, 3 Aug 2020 22:00:11 -0400 Subject: [PATCH 214/236] Fix untar (#42) small fix - untar_stream is supposed to be getting the handle, not the filename --- sys/modules/opus/compress/tar.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/modules/opus/compress/tar.lua b/sys/modules/opus/compress/tar.lua index 0798234..8a18259 100644 --- a/sys/modules/opus/compress/tar.lua +++ b/sys/modules/opus/compress/tar.lua @@ -181,7 +181,7 @@ local function untar(filename, destdir, verbose) local tar_handle = io.open(filename, "rb") if not tar_handle then return nil, "Error opening file "..filename end - local ok, err = untar_stream(filename, destdir, verbose) + local ok, err = untar_stream(tar_handle, destdir, verbose) tar_handle:close() return ok, err -- 2.49.1 From 2520f53046cf373b63dbdfa33b026cfad6103e20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Aug 2020 02:00:28 +0000 Subject: [PATCH 215/236] Update version date --- .opus_version | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index ec678fc..00ea0e6 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,8 @@ -Sun Jul 26 00:52:58 UTC 2020 -commit c24a5a71151b7c6ecd85da8637e9b1eb599d5fbb -Author: kepler155c@gmail.com -Date: Sat Jul 25 18:52:41 2020 -0600 +Tue Aug 4 02:00:27 UTC 2020 +commit f4494d6103c0a82f6ab35d771c70a9c63afa72d1 +Author: Kan18 <24967425+Kan18@users.noreply.github.com> +Date: Mon Aug 3 22:00:11 2020 -0400 - help cleanup + Fix untar (#42) + + small fix - untar_stream is supposed to be getting the handle, not the filename -- 2.49.1 From f36e5470b12b5e36a5f747f5767b0a6423ea178e Mon Sep 17 00:00:00 2001 From: Anavrins Date: Fri, 21 Aug 2020 19:15:07 -0400 Subject: [PATCH 216/236] Anonymize GPS Damn you gollark! --- sys/modules/opus/gps.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sys/modules/opus/gps.lua b/sys/modules/opus/gps.lua index 1132b76..18cb609 100644 --- a/sys/modules/opus/gps.lua +++ b/sys/modules/opus/gps.lua @@ -20,13 +20,12 @@ function GPS.locate(timeout, debug) local modem = device.wireless_modem local closeChannel = false - local selfID = os.getComputerID() - if not modem.isOpen(selfID) then - modem.open(selfID) + if not modem.isOpen(GPS.CHANNEL_GPS) then + modem.open(GPS.CHANNEL_GPS) closeChannel = true end - modem.transmit(GPS.CHANNEL_GPS, selfID, "PING") + modem.transmit(GPS.CHANNEL_GPS, GPS.CHANNEL_GPS, "PING") local fixes = {} local pos = nil @@ -34,7 +33,7 @@ function GPS.locate(timeout, debug) while true do local e, side, chan, reply, msg, dist = os.pullEvent() if e == "modem_message" then - if side == modem.side and chan == selfID and reply == GPS.CHANNEL_GPS and dist then + if side == modem.side and chan == GPS.CHANNEL_GPS and reply == GPS.CHANNEL_GPS and dist then if type(msg) == "table" and #msg == 3 and tonumber(msg[1]) and tonumber(msg[2]) and tonumber(msg[3]) then local fix = { position = vector.new(unpack(msg)), @@ -60,7 +59,7 @@ function GPS.locate(timeout, debug) end if closeChannel then - modem.close(selfID) + modem.close(GPS.CHANNEL_GPS) end if debug then print("Position is "..pos.x..","..pos.y..","..pos.z) -- 2.49.1 From 01d8d651786553edfe0e754e3c43d12f0ef278a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 23:15:31 +0000 Subject: [PATCH 217/236] Update version date --- .opus_version | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.opus_version b/.opus_version index 00ea0e6..f21f33c 100644 --- a/.opus_version +++ b/.opus_version @@ -1,8 +1,7 @@ -Tue Aug 4 02:00:27 UTC 2020 -commit f4494d6103c0a82f6ab35d771c70a9c63afa72d1 -Author: Kan18 <24967425+Kan18@users.noreply.github.com> -Date: Mon Aug 3 22:00:11 2020 -0400 +Fri Aug 21 23:15:30 UTC 2020 +commit f36e5470b12b5e36a5f747f5767b0a6423ea178e +Author: Anavrins +Date: Fri Aug 21 19:15:07 2020 -0400 - Fix untar (#42) - - small fix - untar_stream is supposed to be getting the handle, not the filename + Anonymize GPS + Damn you gollark! -- 2.49.1 From a77deb72ec575fe34c2866e5daea9683e629ace2 Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Mon, 24 Aug 2020 01:20:42 -0400 Subject: [PATCH 218/236] Add a one-time password system (#47) This commit adds a one-time password system. Users can generate passwords by using the `genotp` command (or manually queuing a set_otp event with the SHA-256 hash of the one-time password they want to set) and these passwords will be valid, along with the normal password, either until the computer shuts down (or the net daemon is killed), a new one-time password is generated, or the one-time password is used. --- sys/apps/genotp.lua | 14 +++++++++++ sys/apps/network/trust.lua | 48 +++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 sys/apps/genotp.lua diff --git a/sys/apps/genotp.lua b/sys/apps/genotp.lua new file mode 100644 index 0000000..cdaf6eb --- /dev/null +++ b/sys/apps/genotp.lua @@ -0,0 +1,14 @@ +local SHA = require("opus.crypto.sha2") + +local acceptableCharacters = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"} +local acceptableCharactersLen = #acceptableCharacters + +local password = "" + +for _i = 1, 8 do + password = password .. acceptableCharacters[math.random(acceptableCharactersLen)] +end + +os.queueEvent("set_otp", SHA.compute(password)) + +print("Your one-time password is: " .. password) \ No newline at end of file diff --git a/sys/apps/network/trust.lua b/sys/apps/network/trust.lua index c31b6ec..4b12559 100644 --- a/sys/apps/network/trust.lua +++ b/sys/apps/network/trust.lua @@ -6,6 +6,22 @@ local Util = require('opus.util') local trustId = '01c3ba27fe01383a03a1785276d99df27c3edcef68fbf231ca' +local oneTimePassword -- nil by default + +local function validateData(data, password, dhost) + local s + s, data = pcall(Crypto.decrypt, data, password) + + if s and data and type(data) == "table" and data.pk and data.dh == dhost then + local trustList = Util.readTable('usr/.known_hosts') or { } + trustList[data.dh] = data.pk + Util.writeTable('usr/.known_hosts', trustList) + return true + else + return false + end +end + local function trustConnection(socket) local data = socket:read(2) if data then @@ -13,17 +29,22 @@ local function trustConnection(socket) if not password then socket:write({ msg = 'No password has been set' }) else - local s - s, data = pcall(Crypto.decrypt, data, password) - if s and data and data.pk and data.dh == socket.dhost then - local trustList = Util.readTable('usr/.known_hosts') or { } - trustList[data.dh] = data.pk - Util.writeTable('usr/.known_hosts', trustList) - + if validateData(data, password, socket.dhost) then + print("Accepted trust from " .. socket.dhost) socket:write({ success = true, msg = 'Trust accepted' }) - else - socket:write({ msg = 'Invalid password' }) + return end + + if oneTimePassword then + if validateData(data, oneTimePassword, socket.dhost) then + print("Accepted trust from " .. socket.dhost .. "using one-time password") + socket:write({ success = true, msg = 'Trust accepted - this one-time password will not be usable again' }) + oneTimePassword = nil -- Make sure nobody can use the one-time password again + return + end + end + + socket:write({ msg = 'Invalid password' }) end end end @@ -44,3 +65,12 @@ Event.addRoutine(function() end end end) + +Event.addRoutine(function() + while true do + local _event, password = os.pullEvent("set_otp") + + oneTimePassword = password + print("got new one-time password") + end +end) \ No newline at end of file -- 2.49.1 From 2c0db368faca769ddeec9209c85301cc116aa591 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2020 05:20:58 +0000 Subject: [PATCH 219/236] Update version date --- .opus_version | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.opus_version b/.opus_version index f21f33c..66aa1f7 100644 --- a/.opus_version +++ b/.opus_version @@ -1,7 +1,13 @@ -Fri Aug 21 23:15:30 UTC 2020 -commit f36e5470b12b5e36a5f747f5767b0a6423ea178e -Author: Anavrins -Date: Fri Aug 21 19:15:07 2020 -0400 +Mon Aug 24 05:20:57 UTC 2020 +commit a77deb72ec575fe34c2866e5daea9683e629ace2 +Author: Kan18 <24967425+Kan18@users.noreply.github.com> +Date: Mon Aug 24 01:20:42 2020 -0400 - Anonymize GPS - Damn you gollark! + Add a one-time password system (#47) + + This commit adds a one-time password system. Users can generate + passwords by using the `genotp` command (or manually queuing a set_otp + event with the SHA-256 hash of the one-time password they want to set) + and these passwords will be valid, along with the normal password, + either until the computer shuts down (or the net daemon is killed), a + new one-time password is generated, or the one-time password is used. -- 2.49.1 From ac91d8ed089e7aa13b8723953b9c67f8538accd0 Mon Sep 17 00:00:00 2001 From: Boom Date: Mon, 24 Aug 2020 01:41:25 -0400 Subject: [PATCH 220/236] Trust manager (#46) --- sys/apps/system/trust.lua | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 sys/apps/system/trust.lua diff --git a/sys/apps/system/trust.lua b/sys/apps/system/trust.lua new file mode 100644 index 0000000..399fb8e --- /dev/null +++ b/sys/apps/system/trust.lua @@ -0,0 +1,55 @@ +local UI = require("opus.ui") +local Util = require("opus.util") +local SHA = require('opus.crypto.sha2') + +local function split(s) + local b = "" + for i = 1, #s, 5 do + b = b .. s:sub(i, i+4) + if i ~= #s-4 then + b = b .. "-" + end + end + return b +end + +return UI.Tab { + title = 'Trust', + description = 'Manage trusted devices', + grid = UI.Grid { + x = 2, y = 2, ex = -2, ey = -3, + autospace = true, + sortColumn = 'id', + columns = { + { heading = 'Computer ID', key = 'id'}, + { heading = 'Identity', key = 'pkey'} + } + }, + statusBar = UI.StatusBar { values = 'double-click to revoke trust' }, + reload = function(self) + local values = {} + for k,v in pairs(Util.readTable('usr/.known_hosts') or {}) do + table.insert(values, { + id = k, + pkey = split(SHA.compute(v):sub(-20):upper()) -- Obfuscate private key for visual ident + }) + end + self.grid:setValues(values) + self.grid:setIndex(1) + end, + enable = function(self) + self:reload() + UI.Tab.enable(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + local hosts = Util.readTable('usr/.known_hosts') + hosts[event.selected.id] = nil + Util.writeTable('usr/.known_hosts', hosts) + self:reload() + else + return UI.Tab.eventHandler(self, event) + end + return true + end +} \ No newline at end of file -- 2.49.1 From 6fdce03a10281a1200e32e5379f881a9745d2c1e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Aug 2020 05:41:41 +0000 Subject: [PATCH 221/236] Update version date --- .opus_version | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/.opus_version b/.opus_version index 66aa1f7..13d9d90 100644 --- a/.opus_version +++ b/.opus_version @@ -1,13 +1,6 @@ -Mon Aug 24 05:20:57 UTC 2020 -commit a77deb72ec575fe34c2866e5daea9683e629ace2 -Author: Kan18 <24967425+Kan18@users.noreply.github.com> -Date: Mon Aug 24 01:20:42 2020 -0400 +Mon Aug 24 05:41:40 UTC 2020 +commit ac91d8ed089e7aa13b8723953b9c67f8538accd0 +Author: Boom +Date: Mon Aug 24 01:41:25 2020 -0400 - Add a one-time password system (#47) - - This commit adds a one-time password system. Users can generate - passwords by using the `genotp` command (or manually queuing a set_otp - event with the SHA-256 hash of the one-time password they want to set) - and these passwords will be valid, along with the normal password, - either until the computer shuts down (or the net daemon is killed), a - new one-time password is generated, or the one-time password is used. + Trust manager (#46) -- 2.49.1 From ac51771b121b3724150ffa94aed966e3f2904e05 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Sun, 25 Oct 2020 02:40:21 -0400 Subject: [PATCH 222/236] Fix kiosk bug from #37 --- sys/boot/kiosk.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/boot/kiosk.lua b/sys/boot/kiosk.lua index 61155fe..25fbc2b 100644 --- a/sys/boot/kiosk.lua +++ b/sys/boot/kiosk.lua @@ -21,7 +21,7 @@ if mon then parallel.waitForAny( function() - os.run(_ENV, '/sys/boot/opus.boot') + os.run(_ENV, '/sys/boot/opus.lua') end, function() @@ -36,5 +36,5 @@ if mon then end ) else - os.run(_ENV, '/sys/boot/opus.boot') + os.run(_ENV, '/sys/boot/opus.lua') end -- 2.49.1 From 37c4ca102c02d96f615f19d7838cb5a9eaccb7de Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 26 Oct 2020 17:18:37 -0400 Subject: [PATCH 223/236] Maybe fix github actions --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 837dfc7..46d0ce2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ jobs: git log >> .opus_version - name: Commit version file - uses: alexesprit/action-update-file@master + uses: alexesprit/action-update-file@main with: branch: 'develop-1.8' file-path: .opus_version -- 2.49.1 From 5488a7d732875ccdf3a3cb58b5388d5292383478 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2020 21:19:02 +0000 Subject: [PATCH 224/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 13d9d90..fdb68cb 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Mon Aug 24 05:41:40 UTC 2020 -commit ac91d8ed089e7aa13b8723953b9c67f8538accd0 -Author: Boom -Date: Mon Aug 24 01:41:25 2020 -0400 +Mon Oct 26 21:19:01 UTC 2020 +commit 37c4ca102c02d96f615f19d7838cb5a9eaccb7de +Author: Anavrins +Date: Mon Oct 26 17:18:37 2020 -0400 - Trust manager (#46) + Maybe fix github actions -- 2.49.1 From 2ff4ee56701a1c238f3fda52fd7764efa90559f4 Mon Sep 17 00:00:00 2001 From: Luca Date: Thu, 31 Dec 2020 03:49:51 +0100 Subject: [PATCH 225/236] fix: boolean value false is now correctly displayed in Lua.lua (#51) --- sys/apps/Lua.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 2bcd8e5..9af06fd 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -348,7 +348,7 @@ function page:executeStatement(statement) term.redirect(oterm) counter = counter + 1 - if s and m then + if s and type(m) ~= "nil" then self:setResult(m) else self.grid:setValues({ }) -- 2.49.1 From 65bf756084e862017823988e57b80be9ec203854 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 02:50:07 +0000 Subject: [PATCH 226/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index fdb68cb..fd09489 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Mon Oct 26 21:19:01 UTC 2020 -commit 37c4ca102c02d96f615f19d7838cb5a9eaccb7de -Author: Anavrins -Date: Mon Oct 26 17:18:37 2020 -0400 +Thu Dec 31 02:50:06 UTC 2020 +commit 2ff4ee56701a1c238f3fda52fd7764efa90559f4 +Author: Luca +Date: Thu Dec 31 03:49:51 2020 +0100 - Maybe fix github actions + fix: boolean value false is now correctly displayed in Lua.lua (#51) -- 2.49.1 From 2b75fbd1eee93c88b8a9b0030ef35d06f88be42c Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 15 Feb 2021 21:28:57 -0500 Subject: [PATCH 227/236] fix: up-to-date package on master branch --- sys/apps/shell.lua | 5 +++++ sys/modules/opus/packages.lua | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index 9672d6d..a08e0b0 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -612,6 +612,11 @@ local function shellRead(history) end end + elseif ie.code == 'control-l' then + term.clear() + term.setCursorPos(1, 0) -- Y:0 ? + break + else entry:process(ie) entry.value = entry.value or '' diff --git a/sys/modules/opus/packages.lua b/sys/modules/opus/packages.lua index 8b0f5e1..446e1ba 100644 --- a/sys/modules/opus/packages.lua +++ b/sys/modules/opus/packages.lua @@ -57,7 +57,7 @@ end function Packages:downloadList() local packages = { [ 'develop-1.8' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/develop-1.8/packages.list', - [ 'master-1.8' ] = 'https://pastebin.com/raw/pexZpAxt', + [ 'master-1.8' ] = 'https://raw.githubusercontent.com/kepler155c/opus-apps/master-1.8/packages.list', } if packages[_G.OPUS_BRANCH] then -- 2.49.1 From e74db023711c2afc884f32b5d6f10d027cb7766d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Feb 2021 02:29:40 +0000 Subject: [PATCH 228/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index fd09489..a6d73aa 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Thu Dec 31 02:50:06 UTC 2020 -commit 2ff4ee56701a1c238f3fda52fd7764efa90559f4 -Author: Luca -Date: Thu Dec 31 03:49:51 2020 +0100 +Tue Feb 16 02:29:37 UTC 2021 +commit 2b75fbd1eee93c88b8a9b0030ef35d06f88be42c +Author: Anavrins +Date: Mon Feb 15 21:28:57 2021 -0500 - fix: boolean value false is now correctly displayed in Lua.lua (#51) + fix: up-to-date package on master branch -- 2.49.1 From bff84370b24967b55ab3ed649085046e9bfd5412 Mon Sep 17 00:00:00 2001 From: Tomo <68489118+Tomodachi94@users.noreply.github.com> Date: Fri, 19 Feb 2021 16:57:25 -0800 Subject: [PATCH 229/236] Create issue templates (#50) --- .github/ISSUE_TEMPLATES/bug.md | 17 +++++++++++++++++ .github/ISSUE_TEMPLATES/feature.md | 13 +++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 .github/ISSUE_TEMPLATES/bug.md create mode 100644 .github/ISSUE_TEMPLATES/feature.md diff --git a/.github/ISSUE_TEMPLATES/bug.md b/.github/ISSUE_TEMPLATES/bug.md new file mode 100644 index 0000000..cb10f7d --- /dev/null +++ b/.github/ISSUE_TEMPLATES/bug.md @@ -0,0 +1,17 @@ +--- +name: Bug Report +about: Did something go wrong? File an issue! +title: Good titles include first line of stack trace or short summary of problem +labels: bug +--- + +# Details + +## Further context + +## Versions +Branch: + +Opus Version: + +CraftOS Version: diff --git a/.github/ISSUE_TEMPLATES/feature.md b/.github/ISSUE_TEMPLATES/feature.md new file mode 100644 index 0000000..143b071 --- /dev/null +++ b/.github/ISSUE_TEMPLATES/feature.md @@ -0,0 +1,13 @@ +--- +name: Enhancement +about: Suggest a new feature or change to Opus. +labels: enhancement +--- + +# Summary + +## Additional Context + +## Related + + -- 2.49.1 From f6d09abd54cd7ef3e67642dd9d26df8984a6a773 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Feb 2021 00:57:43 +0000 Subject: [PATCH 230/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index a6d73aa..498489b 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Tue Feb 16 02:29:37 UTC 2021 -commit 2b75fbd1eee93c88b8a9b0030ef35d06f88be42c -Author: Anavrins -Date: Mon Feb 15 21:28:57 2021 -0500 +Sat Feb 20 00:57:40 UTC 2021 +commit bff84370b24967b55ab3ed649085046e9bfd5412 +Author: Tomo <68489118+Tomodachi94@users.noreply.github.com> +Date: Fri Feb 19 16:57:25 2021 -0800 - fix: up-to-date package on master branch + Create issue templates (#50) -- 2.49.1 From 16d233df5d0cd0184b252b9380cc4a0843b2a148 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Fri, 19 Feb 2021 20:11:13 -0500 Subject: [PATCH 231/236] Actually working bug/feature templates --- .github/ISSUE_TEMPLATE/bug_report.md | 30 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..f3923a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Versions** +What version of Minecraft, CC:Tweaked, Plethora (if applicable), Opus branch are you using + - MC : [e.g. 1.12.2] + - CC:T : [e.g. 1.88] + - Opus : [e.g. develop-1.8] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..11fc491 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. -- 2.49.1 From ce6a741690eff165daa55ff9d9e05b435a77143e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Feb 2021 01:11:28 +0000 Subject: [PATCH 232/236] Update version date --- .opus_version | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 498489b..18e6cb1 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,6 @@ -Sat Feb 20 00:57:40 UTC 2021 -commit bff84370b24967b55ab3ed649085046e9bfd5412 -Author: Tomo <68489118+Tomodachi94@users.noreply.github.com> -Date: Fri Feb 19 16:57:25 2021 -0800 +Sat Feb 20 01:11:25 UTC 2021 +commit 16d233df5d0cd0184b252b9380cc4a0843b2a148 +Author: Anavrins +Date: Fri Feb 19 20:11:13 2021 -0500 - Create issue templates (#50) + Actually working bug/feature templates -- 2.49.1 From 7d9029c70643a85e2034747048dd1c83d7a78180 Mon Sep 17 00:00:00 2001 From: xAnavrins Date: Tue, 16 Nov 2021 22:59:19 -0500 Subject: [PATCH 233/236] Reduce peripheral calls in Network app Add a manual refresh button instead --- sys/apps/Network.lua | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index f5f91d3..52bc974 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -85,8 +85,14 @@ local page = UI.Page { title = 'Ports', event = 'ports_hide', }, - grid = UI.ScrollingGrid { + menuBar = UI.MenuBar { y = 2, + buttons = { + { text = 'Refresh', event = 'ports_update' }, + } + }, + grid = UI.ScrollingGrid { + y = 3, columns = { { heading = 'Port', key = 'port' }, { heading = 'State', key = 'state' }, @@ -214,11 +220,16 @@ function page:eventHandler(event) self.ports.grid:update() self.ports:show() - self.portsHandler = Event.onInterval(3, function() + -- self.portsHandler = Event.onInterval(3, function() + -- self.ports.grid:update() + -- self.ports.grid:draw() + -- self:sync() + -- end) + + elseif event.type == 'ports_update' then self.ports.grid:update() self.ports.grid:draw() self:sync() - end) elseif event.type == 'ports_hide' then Event.off(self.portsHandler) -- 2.49.1 From 7c5f749f0245545fb3132c81001d1b13c272172f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Nov 2021 04:00:12 +0000 Subject: [PATCH 234/236] Update version date --- .opus_version | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.opus_version b/.opus_version index 18e6cb1..ab93bfd 100644 --- a/.opus_version +++ b/.opus_version @@ -1,6 +1,7 @@ -Sat Feb 20 01:11:25 UTC 2021 -commit 16d233df5d0cd0184b252b9380cc4a0843b2a148 -Author: Anavrins -Date: Fri Feb 19 20:11:13 2021 -0500 +Wed Nov 17 04:00:09 UTC 2021 +commit 7d9029c70643a85e2034747048dd1c83d7a78180 +Author: xAnavrins +Date: Tue Nov 16 22:59:19 2021 -0500 - Actually working bug/feature templates + Reduce peripheral calls in Network app + Add a manual refresh button instead -- 2.49.1 From 3150525ee2024fc605669093b89f75f0c741a81f Mon Sep 17 00:00:00 2001 From: Kan18 <24967425+Kan18@users.noreply.github.com> Date: Mon, 4 Jul 2022 00:08:59 -0400 Subject: [PATCH 235/236] Fix #48 (shell resolving issue) (#58) --- sys/apps/shell.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index a08e0b0..1eecbfa 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -173,15 +173,12 @@ function shell.resolveProgram(_sCommand) end end - if not _sCommand:find('/') then - return inPath() - end - -- so... even if you are in the rom directory and you run: -- 'packages/common/edit.lua', allow this even though it -- does not use a leading slash. Ideally, fs.combine would -- provide the leading slash... but it does not. - return check(shell.resolve(_sCommand)) + return (not _sCommand:find('/')) and inPath() + or check(shell.resolve(_sCommand)) or check(shell.resolve(_sCommand) .. '.lua') or check(_sCommand) or check(_sCommand .. '.lua') -- 2.49.1 From 4104750539695affeb6518947b53ef0c5ba372fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 4 Jul 2022 04:09:14 +0000 Subject: [PATCH 236/236] Update version date --- .opus_version | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.opus_version b/.opus_version index ab93bfd..911be79 100644 --- a/.opus_version +++ b/.opus_version @@ -1,7 +1,6 @@ -Wed Nov 17 04:00:09 UTC 2021 -commit 7d9029c70643a85e2034747048dd1c83d7a78180 -Author: xAnavrins -Date: Tue Nov 16 22:59:19 2021 -0500 +Mon Jul 4 04:09:12 UTC 2022 +commit 3150525ee2024fc605669093b89f75f0c741a81f +Author: Kan18 <24967425+Kan18@users.noreply.github.com> +Date: Mon Jul 4 00:08:59 2022 -0400 - Reduce peripheral calls in Network app - Add a manual refresh button instead + Fix #48 (shell resolving issue) (#58) -- 2.49.1