Compare commits

...

3 Commits

Author SHA1 Message Date
20kdc 22c1c211ef Bugfix: fix 'delete' key, fix Everest KB/M matching 2020-03-30 19:11:50 +01:00
20kdc 7840f0a231 Improve BDIVIDE (88 bytes free) 2020-03-30 17:55:20 +01:00
20kdc 339571ee9b Even more line editing improvements 2020-03-30 14:36:33 +01:00
18 changed files with 384 additions and 108 deletions

5
.gitignore vendored
View File

@ -30,7 +30,10 @@ laboratory/*/*/*/*
inst.lua
# Available as the respective release
inst-gold.lua
inst/code.tar.bd
# Compression stuff
inst/*/output.bin
inst/*/vfyerr.bin
# internal
upldr.sh
upldr-dev.sh
upldr-gold.sh

View File

@ -24,7 +24,7 @@ return {
},
["neo-docs"] = {
desc = "KittenOS NEO system documentation",
v = 8,
v = 9,
deps = {
"zzz-license-pd"
},
@ -43,6 +43,7 @@ return {
"docs/us-setti",
"docs/us-evrst",
"docs/us-clawf",
"docs/us-termi",
"docs/ul-seria",
"docs/ul-fwrap",
"docs/ul-event",
@ -50,6 +51,7 @@ return {
"docs/ul-neoux",
"docs/ul-brail",
"docs/ul-bmp__",
"docs/ul-linee",
"docs/gp-pedan",
"docs/repoauthors/neo-docs"
},

View File

@ -131,7 +131,7 @@ local function getline(y)
return ("¬"):rep(sW)
end
line = unicode.safeTextFormat(line)
return lineEdit.draw(sW, line, cursorXP, rY == cursorY, rX)
return lineEdit.draw(sW, line, rY == cursorY and cursorXP, rX)
end
local function delLine()
local contents = lines[cursorY]
@ -268,11 +268,15 @@ local function key(ks, kc, down)
elseif lX == "l>" and cursorY < #lines then
cursorY = cursorY + 1
cursorX = 1
elseif lX == "wpl" and cursorY ~= 1 then
elseif lX == "w<" and cursorY ~= 1 then
local l = table.remove(lines, cursorY)
cursorY = cursorY - 1
cursorX = unicode.len(lines[cursorY]) + 1
lines[cursorY] = lines[cursorY] .. l
elseif lX == "w>" and cursorY ~= #lines then
local l = table.remove(lines, cursorY)
cursorX = unicode.len(l) + 1
lines[cursorY] = l .. lines[cursorY]
elseif lX == "nl" then
local line = lines[cursorY]
lines[cursorY] = unicode.sub(line, 1, cursorX - 1)

View File

@ -43,8 +43,9 @@ local function fmtLine(s)
end
local function line(i)
local l = console[i] or l15
l = require("lineedit").draw(sW, l, cX, i == sH)
local l, c = console[i] or l15
l, c = unicode.safeTextFormat(l, cX)
l = require("lineedit").draw(sW, l, i == sH and c)
if i ~= sH then
window.span(1, i, l, 0xFFFFFF, 0)
else

View File

@ -600,13 +600,16 @@ end
local isAltDown = false
local isCtrDown = false
local function key(ku, ka, kc, down)
local ku = screens.getMonitorByKeyboard(ku)
if not ku then return end
local ku, lin = screens.getMonitorByKeyboard(ku)
for k, v in ipairs(monitors) do
if v[2] == mu then
lIM = k
if ku and v[2] == ku then
lin = k
break
end
end
if not lin then return end
lIM = lin
local focus = surfaces[1]
if kc == 29 then isCtrDown = down end
if kc == 56 then isAltDown = down end

View File

@ -3,13 +3,13 @@
return {
-- note: everything must already be unicode.safeTextFormat'd
draw = function (sW, line, cursorX, cursorOn, rX)
draw = function (sW, line, cursorX, rX)
-- if no camera, provide a default
rX = rX or math.max(1, cursorX - math.floor(sW * 2 / 3))
rX = rX or math.max(1, (cursorX or 1) - math.floor(sW * 2 / 3))
-- transform into area-relative
local tl = unicode.sub(line, rX, rX + sW - 1)
-- inject cursor
if cursorOn then
if cursorX then
cursorX = (cursorX - rX) + 1
if cursorX >= 1 then
if cursorX <= sW then
@ -31,6 +31,7 @@ return {
key = function (ks, kc, line, cursorX)
local cS = unicode.sub(line, 1, cursorX - 1)
local cE = unicode.sub(line, cursorX)
local ll = unicode.len(line)
if kc == 203 then -- navi <
if cursorX > 1 then
return nil, cursorX - 1
@ -39,7 +40,6 @@ return {
return nil, nil, "l<"
end
elseif kc == 205 then -- navi >
local ll = unicode.len(line)
if cursorX > ll then
-- cline overflow
return nil, nil, "l>"
@ -49,14 +49,18 @@ return {
return nil, 1
elseif kc == 207 then -- end
return nil, unicode.len(line) + 1
elseif ks == "\8" or kc == 211 then -- del
elseif ks == "\8" then -- del
if cursorX == 1 then
-- weld prev line
return nil, nil, "wpl"
-- weld prev
return nil, nil, "w<"
else
cS = unicode.sub(cS, 1, unicode.len(cS) - 1)
return cS .. cE, cursorX - 1
return unicode.sub(cS, 1, unicode.len(cS) - 1) .. cE, cursorX - 1
end
elseif kc == 211 then -- del
if cursorX > ll then
return nil, nil, "w>"
end
return cS .. unicode.sub(cE, 2)
elseif ks then -- standard letters
if ks == "\r" then
-- new line

View File

@ -383,7 +383,7 @@ newNeoux = function (event, neo)
local t, e, r = textprop(), require("lineedit")
p = e.clamp(t, p)
t, r = unicode.safeTextFormat(t, p)
window.span(x, y, "[" .. e.draw(w - 2, t, r, selected) .. "]", bg, fg)
window.span(x, y, "[" .. e.draw(w - 2, t, selected and r) .. "]", bg, fg)
end
}
end

View File

@ -3,16 +3,12 @@
-- PREPROC (r9 edition): preprocess input to be 7-bit
local frw = require("libs.frw")
local
-- SHARED WITH DECOMPRESSION ENGINE
function p(x, y)
if x == 126 then
if y >= 32 then
return ({
-- Before adding to this, check how installer size is changed.
"\x7E", "\x7F"
})[y - 31], 3
end
return string.char(y), 3
elseif x == 127 then
return string.char(128 + y), 3
@ -23,7 +19,7 @@ function p(x, y)
elseif x == 30 then
return "\x00", 2
end
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte(math.floor(x / 5) + 1)), 2
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte((x - x % 5) / 5 + 1)), 2
end
local preprocParts = {}
@ -45,9 +41,10 @@ for i = 0, 127 do
end
end
local function preproc(blk)
local function preproc(blk, p)
local out = ""
while blk ~= "" do
p(blk)
local len = math.min(preprocMaxLen, #blk)
while len > 0 do
local seg = blk:sub(1, len)
@ -71,7 +68,7 @@ end
-- Position is where in the window it was found, minus 1.
-- windowSize must be the same between the encoder and decoder,
-- and is the amount of data preserved after cropping.
local function bdivide(blk)
local function bdivide(blk, p)
local out = ""
local windowSize = 0x10000
@ -82,6 +79,7 @@ local function bdivide(blk)
end
while blk ~= "" do
p(blk)
local bestData = blk:sub(1, 1)
local bestRes = bestData
for lm = 0, 127 do
@ -111,8 +109,19 @@ local function bdivide(blk)
end
return function (data)
data = preproc(data)
io.stderr:write("---\n")
data = bdivide(data)
return data
io.stderr:write("preproc: ")
local pi = frw.progress()
local function p(b)
pi(1 - (#b / #data))
end
data = preproc(data, p)
io.stderr:write("\nbdivide: ")
pi = frw.progress()
data = bdivide(data, p)
io.stderr:write("\n")
-- These are used to pad the stream to flush the pipeline.
-- It's cheaper than the required code.
-- 1 byte of buffer for preproc,
-- 2 bytes of buffer for bdivide.
return data .. ("\x00"):rep(3)
end

View File

@ -4,35 +4,35 @@
-- BDIVIDE (r5 edition) and PREPROC (r9 edition)
-- decompression engine for installer
-- cb: sector accumulator
-- ct: preproc accumulator
-- cc: bdivide accumulator
-- cw: bdivide window
-- a: temporary
-- touched by q,L
-- b: sector accumulator
-- c: bdivide accumulator
-- d: temporary
-- touched by q,L
-- t: preproc accumulator
-- q: function to submit to preproc
-- s: temporary
-- touched by L
-- w: bdivide window
-- cp: function to submit to preproc
-- cd: function to submit to bdivide
-- L: function to submit to bdivide
cb,ct,cc,cw="","","",("\x00"):rep(2^16)
b,t,c,w="","","",("\x00"):rep(2^16)
-- High-level breakdown:
-- CP is unescaper & TAR-sector-breakup.
-- q is unescaper & TAR-sector-breakup.
-- It'll only begin to input if at least 3 bytes are available,
-- so you'll want to throw in 2 extra zeroes at the end of stream as done here.
-- It uses Ct (input buffer) and Cp (output buffer).
-- It uses t (input buffer) and p (output buffer).
-- Ignore its second argument, as that's a lie, it's just there for a local.
-- CD is the actual decompressor. It has the same quirk as CP, wanting two more bytes.
-- It stores to Cc (compressed), and Cw (window).
-- It outputs that which goes to the window to CP also.
-- L is the actual decompressor. It has the same quirk as q, wanting two more bytes.
-- It stores to c (compressed), and w (window).
-- It outputs that which goes to the window to q also.
-- And it also uses a fake local.
-- SEE compress.lua FOR THIS FUNCTION
function p(x, y)
if x == 126 then
if y >= 32 then
return ({
-- Before adding to this, check how installer size is changed.
"\x7E", "\x7F"
})[y - 31], 3
end
return string.char(y), 3
elseif x == 127 then
return string.char(128 + y), 3
@ -43,43 +43,34 @@ function p(x, y)
elseif x == 30 then
return "\x00", 2
end
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte(math.floor(x / 5) + 1)), 2
return string.char(("enart"):byte(x % 5 + 1), ("ndtelh"):byte((x - x % 5) / 5 + 1)), 2
end
function cp(d, b, a)
ct = ct .. d
while #ct > 1 do
b, a = p(ct:byte(), ct:byte(2))
cb = cb .. b
ct = ct:sub(a)
if #cb > 511 then
M(cb:sub(1, 512))
cb = cb:sub(513)
function q(w)
t = t .. w
while #t > 1 do
d, a = p(t:byte(), t:byte(2))
b = b .. d
t = t:sub(a)
if #b > 511 then
M(b:sub(1, 512))
b = b:sub(513)
end
end
end
function cd(d, b, p)
cc = cc .. d
while #cc > 2 do
b = cc:byte()
if b < 128 then
b, cc = cc:sub(1, 1), cc:sub(2)
else
p = cc:byte(2) * 256 + cc:byte(3) + 1
b, cc = cw:sub(p, p + b - 125), cc:sub(4)
end
cp(b)
cw = (cw .. b):sub(-65536)
end
end
-- quick & dirty integration with the existing stuff
function L(d)
if not d then
cd("\x00\x00")cp("\x00\x00")
else
cd(d)
c = c .. d
while #c > 2 do
s = c:byte()
if s < 128 then
s, c = c:sub(1, 1), c:sub(2)
else
a = c:byte(2) * 256 + c:byte(3) + 1
s, c = w:sub(a, a + s - 125), c:sub(4)
end
q(s)
w = (w .. s):sub(-2^16)
end
end

View File

@ -2,30 +2,66 @@
-- No warranty is provided, implied or otherwise.
-- KittenOS NEO Installer Generator --
local alg, tarName = ...
local alg, tarName, cid = ...
cid = (cid or "UNKNOWN"):sub(1, 7)
local function read(fn)
local f = io.open(fn, "rb")
local d = f:read("*a")
f:close()
return d
end
local u = require("libs.frw")
local tarData = read(tarName)
local tarData = u.read(tarName)
local tarSectors = math.floor(#tarData / 512)
local instCode = "K=" .. tarSectors .. "\n" .. read(alg .. "/instdeco.lua") .. read("instbase.lua")
local instSize = 0
local function put(data)
io.write(data)
instSize = instSize + #data
end
put("--" .. cid .. "\n")
put("--This is released into the public domain. No warranty is provided, implied or otherwise.\n")
local instCode = "K=" .. tarSectors .. "\n" .. u.read(alg .. "/instdeco.lua") .. u.read("instbase.lua")
instCode = require("libs.lexcrunch")(instCode)
io.write(instCode)
put(instCode)
-- the \x00 is the indicator to start reading
io.write("--[[\x00")
put("--[[\x00")
io.stderr:write("compressing...\n")
local compressedData = require(alg .. ".compress")(tarData)
u.write(alg .. "/output.bin", compressedData)
io.stderr:write("compression with " .. alg .. ": " .. #tarData .. " -> " .. #compressedData .. "\n")
-- Program the read-in state machine
compressedData = compressedData:gsub("\xFE", "\xFE\xFE")
compressedData = compressedData:gsub("]]", "]\xFE]")
io.write(compressedData)
io.write("]]")
put(compressedData)
put("]]")
local status = ""
local statusDetail = ""
local blinkI = ""
if instSize > 65536 then
blinkI = "5;31;"
status = " DO NOT SHIP "
statusDetail = "The installer is too big to ship safely.\nIt's possible it may crash on Tier 1 systems.\nUpgrade the compression system or remove existing code."
elseif instSize > 64000 then
blinkI = "33;"
status = " Shippable * "
statusDetail = "The installer is getting dangerously large.\nReserve further room for bugfixes."
else
blinkI = "32;"
status = " All Green "
statusDetail = "The installer is well within budget with room for features.\nDevelop as normal."
end
io.stderr:write("\n")
local ctS, ctM, ctE = " \x1b[1;" .. blinkI .. "7m", "\x1b[0;7m", "\x1b[0m\n"
io.stderr:write(ctS .. " " .. ctM .. " " .. ctE)
io.stderr:write(ctS .. status .. ctM .. string.format(" %07i ", 65536 - instSize) .. ctE)
io.stderr:write(ctS .. " " .. ctM .. " " .. ctE)
io.stderr:write("\n")
io.stderr:write(statusDetail .. "\n")
io.stderr:write("\n")
io.stderr:write("Size: " .. instSize .. "\n")
io.stderr:write(" max. 65536\n")
io.stderr:write("\n")

View File

@ -33,7 +33,7 @@
-- Z: component: filesystem
B = computer
C = component
assert(C, "To install, please copy as init.lua to a blank disk or a system to update, then remove all other disks and reboot.")
assert(C, "KittenOS NEO installer: Copy as init.lua to the target disk, then remove other disks & reboot.")
X = C.list("screen", true)()
Y = C.list("gpu", true)()
@ -113,12 +113,6 @@ end
while true do
A = Z.read(P, 64)
if not A then
L()
-- IF WE GET HERE,
-- YOU BROKE SOMETHING!
A()
end
D = ""
for i = 1, #A do
-- Read-in state machine

30
inst/libs/frw.lua Normal file
View File

@ -0,0 +1,30 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
return {
read = function (fn)
local f = io.open(fn, "rb")
local d = f:read("*a")
f:close()
return d
end,
write = function (fn, data)
local f = io.open(fn, "wb")
f:write(data)
f:close()
end,
progress = function ()
io.stderr:write("00% \\")
local lastPercent = 0
local chr = 0
return function (fraction)
local percent = math.ceil(fraction * 100)
if percent ~= lastPercent then
lastPercent = percent
chr = (chr + 1) % 4
io.stderr:write(string.format("\8\8\8\8\8%02i%% %s", percent, ("\\|/-"):sub(chr + 1, chr + 1)))
end
end
end
}

View File

@ -3,6 +3,7 @@
t = ""
function L(d)
if not d then return end
t = t .. d
while #t >= 512 do
M(t:sub(1, 512))

30
inst/verify.lua Normal file
View File

@ -0,0 +1,30 @@
-- This is released into the public domain.
-- No warranty is provided, implied or otherwise.
-- Installer Compression Verification Tool --
local alg, tarName = ...
local u = require("libs.frw")
io.stderr:write("verifying... ")
local p = u.progress()
local tarData = u.read(tarName)
local total = ""
function M(t)
assert(#t == 512)
total = total .. t
p(#total / #tarData)
end
dofile(alg .. "/instdeco.lua")
L(u.read(alg .. "/output.bin"))
if total ~= tarData then
io.stderr:write("\n" .. #total .. " : " .. #tarData .. "\n")
u.write(alg .. "/vfyerr.bin", total)
error("VERIFICATION FAILURE : see inst/" .. alg .. "/vfyerr.bin!")
end
io.stderr:write("\nverification success\n")

View File

@ -13,15 +13,10 @@ cd code
tar --mtime=0 --owner=gray:0 --group=mann:0 -cf ../code.tar .
cd ..
# Solely for ensuring that a -gold.lua file can be checked before being pushed to repository.
echo -n "-- KOSNEO inst. " > inst.lua
git status --porcelain=2 --branch | grep branch.oid >> inst.lua
echo "-- This is released into the public domain." >> inst.lua
echo "-- No warranty is provided, implied or otherwise." >> inst.lua
# The Installer Creator
cd inst
lua build.lua $1 ../code.tar >> ../inst.lua
lua build.lua $1 ../code.tar `git status --porcelain=2 --branch | grep branch.oid | grep -E -o "[0-9a-f]*$" -` > ../inst.lua
lua verify.lua $1 ../code.tar
cd ..
# Common Repository Setup Code

View File

@ -16,4 +16,6 @@ repository/docs/us-nxapp: 20kdc, Public Domain
repository/docs/us-perms: 20kdc, Public Domain
repository/docs/us-setti: 20kdc, Public Domain
repository/docs/us-clawf: 20kdc, Public Domain
repository/docs/ul-linee: 20kdc, Public Domain
repository/docs/us-termi: 20kdc, Public Domain

78
repository/docs/ul-linee Normal file
View File

@ -0,0 +1,78 @@
The "lineedit" library provides a
quick way to implement nice line
editing into applications.
It requires the user to store the
line and the cursor position for the
line, which allows the user to
implement any additional logic for
the application.
(To clarify, this implies that the
library's functions are stateless.)
The functions are as follows:
draw = function (sW, line, curX, rX):
Returns the spantext.
sW is the width of the area.
line is the safeTextFormatted text.
curX is the cursor in screen units
(usually done as part of the text
formatting)
This is optional, and if not provided
the cursor will not be shown.
rX is an optional camera override.
If not provided, the cursor will be
used; if that is not provided, then
the left side will be shown.
clamp = function (line, curX):
Returns curX clamped to the line's
length (does not check for < 1).
key = function (ks, kc, line, curX):
Performs something.
ks, if truthy, is the unicode.char
of the key event key character.
kc is the key event keycode.
(If feeding in clipboard characters,
use 0 here.)
line is the text.
This returns three values, any of
which or all of which may be nil for
a 'no effect' response:
The new line text, 'lT'.
The new curX, 'lC'.
The extended action, 'lX'.
The following extended actions exist:
"l<": The cursor should be warped to
the end of the previous line, if one
exists (if not, do nothing)
"l>": The cursor should be warped to
the start of the next line, if one
exists (if not, do nothing)
"w<": This line should be welded to
the previous line, and the cursor
should be placed at the weld point.
"w>": This line should be welded to
the next line, and the cursor
should be placed at the weld point.
"nl": The Enter button was pressed.
-- This is released into
the public domain.
-- No warranty is provided,
implied or otherwise.

93
repository/docs/us-termi Normal file
View File

@ -0,0 +1,93 @@
The "svc-t" program / "x.svc.t"
permission makes up the terminal
subsystem for KittenOS NEO.
--- THEORETICAL TERMINALS MODEL ---
The theoretical model for terminals
in KittenOS NEO is that of a stack
of processes controlling a player's
connection to a MUD, where text is
provided to the server and to the
player in a line-by-line format,
with no "flow control"/ttyattrs.
A process starting another process
connected to the same terminal is
advised to wait for that process to
die before continuing in terminal
activities, unless some sort of
'in-band notification' functionality
is intended.
The controlling process is whichever
process is supposed to be accepting
user input. This is contextual, and
there is no mechanism to control
this explicitly.
The controlling process should show
text in response to any user input,
or at least provide some form of
acknowledgement that user input has
been received.
User input IS NOT automatically
echoed by the terminal.
--- ACTUAL USAGE OF TERMINALS ---
Access control on terminals is looser
than for most permissions, as it has
to be able to be 'sublet' in some
cases, including events.
A terminal program is given a string
argument for the ID of the terminal
to connect to.
A terminal always has an ID beginning
with "x.svc.t/". ALWAYS CHECK THIS.
Requiring the responsible access
connects to the terminal. All
terminal programs SHOULD check for
the death of their parent terminal
(via the k.procdie event) and
self-destruct accordingly.
A program may start svc-t directly.
In this case, it must pass a function
(resTbl) and may pass a title.
When the terminal has shown, the
function provided is called with a
table as follows:
access = "x.svc.t/<...>"
close = function (): close terminal
The k.kill permission and the close
function are the only ways for a
program to kill a terminal, and the
close function is only given to the
creating process.
In either case, when the access has
been acquired, the following API is
presented:
id = "x.svc.t/<...>"
pid = <The terminal's PID.>
line = function (text): Shows a line.
When the user sends a line, an event
of: <id>, "line", <text>
is provided.
-- This is released into
the public domain.
-- No warranty is provided,
implied or otherwise.