1
0
mirror of https://github.com/20kdc/OC-KittenOS.git synced 2024-11-26 12:28:05 +11:00

Compare commits

..

No commits in common. "fe17b0fb9340fc3f32170ba6b1b2e4646c782455" and "ca4b6e5df25128da8302800f94e52930201d207c" have entirely different histories.

6 changed files with 207 additions and 159 deletions

View File

@ -93,7 +93,7 @@ return {
}, },
["app-kmt"] = { ["app-kmt"] = {
desc = "Line-terminal for MUDs & such", desc = "Line-terminal for MUDs & such",
v = 2, v = 1,
deps = { deps = {
"neo", "neo",
"zzz-license-pd" "zzz-license-pd"

View File

@ -10,11 +10,11 @@ local termClose
if not ok then if not ok then
termId = nil termId = nil
assert(neo.executeAsync("svc-t", function (res) neo.executeAsync("svc-t", function (res)
termId = res.access termId = res.access
termClose = res.close termClose = res.close
neo.scheduleTimer(0) neo.scheduleTimer(0)
end, "luashell")) end, "luashell")
while not termId do while not termId do
coroutine.yield() coroutine.yield()
end end
@ -32,7 +32,7 @@ event.listen("k.procdie", function (_, _, pid)
end end
end) end)
TERM.write([[ TERM.write(([[
KittenOS NEO Shell Usage Notes KittenOS NEO Shell Usage Notes
Prefixing = is an alias for 'return '. Prefixing = is an alias for 'return '.
@ -46,7 +46,7 @@ os.exit(): quit the shell
=listCmdApps(): -t- (terminal) apps =listCmdApps(): -t- (terminal) apps
event: useful for setting up listeners event: useful for setting up listeners
without breaking shell functionality without breaking shell functionality
]]) ]]):gsub("[\r]*\n", "\r\n"))
function listCmdApps() function listCmdApps()
local apps = {} local apps = {}

View File

@ -30,10 +30,7 @@ local console = {}
-- This must not go below 3. -- This must not go below 3.
local conW = 40 local conW = 40
local conCX, conCY = 1, 1 local conCX, conCY = 1, 1
local conSCX, conSCY = 1, 1 local conCV = false
-- Performance
local consoleShown = {}
local conCYShown
for i = 1, 14 do for i = 1, 14 do
console[i] = (" "):rep(conW) console[i] = (" "):rep(conW)
end end
@ -73,9 +70,6 @@ local window = neo.requireAccess("x.neo.pub.window", "window")(conW, #console +
local function setSize(w, h) local function setSize(w, h)
conW = w conW = w
for i = 1, #console do
consoleShown[i] = nil
end
while #console < h do while #console < h do
table.insert(console, "") table.insert(console, "")
end end
@ -107,7 +101,7 @@ end
local function draw(i) local function draw(i)
if console[i] then if console[i] then
window.span(1, i, console[i], 0, 0xFFFFFF) window.span(1, i, console[i], 0, 0xFFFFFF)
if i == conCY and not leText then if i == conCY and conCV then
window.span(conCX, i, unicode.sub(console[i], conCX, conCX), 0xFFFFFF, 0) window.span(conCX, i, unicode.sub(console[i], conCX, conCX), 0xFFFFFF, 0)
end end
elseif leText then elseif leText then
@ -115,13 +109,7 @@ local function draw(i)
end end
end end
local function drawDisplay() local function drawDisplay()
for i = 1, #console do for i = 1, #console do draw(i) end
if consoleShown[i] ~= console[i] or i == conCY or i == conCYShown then
draw(i)
consoleShown[i] = console[i]
end
end
conCYShown = conCY
end end
-- Terminal Visual -- -- Terminal Visual --
@ -139,13 +127,6 @@ local function consoleSU()
end end
end end
local function consoleCLS()
for i = 1, #console do
console[i] = (" "):rep(conW)
end
conCX, conCY = 1, 1
end
local function writeFF() local function writeFF()
if conCY ~= #console then if conCY ~= #console then
conCY = conCY + 1 conCY = conCY + 1
@ -158,7 +139,6 @@ local function writeData(data)
-- handle data until completion -- handle data until completion
while #data > 0 do while #data > 0 do
local char = unicode.sub(data, 1, 1) local char = unicode.sub(data, 1, 1)
--neo.emergency("svc-t.data: " .. char:byte())
data = unicode.sub(data, 2) data = unicode.sub(data, 2)
-- handle character -- handle character
if char == "\t" then if char == "\t" then
@ -167,8 +147,6 @@ local function writeData(data)
end end
if char == "\r" then if char == "\r" then
conCX = 1 conCX = 1
elseif char == "\x00" then
-- caused by TELNET \r rules
elseif char == "\n" then elseif char == "\n" then
conCX = 1 conCX = 1
writeFF() writeFF()
@ -193,17 +171,29 @@ local function writeData(data)
end end
local function writeANSI(s) local function writeANSI(s)
--neo.emergency("svc-t.ansi: " .. s)
-- This supports just about enough to get by. -- This supports just about enough to get by.
if s == "c" then if s == "c" then
consoleCLS() for i = 1, #console do
console[i] = (" "):rep(conW)
end
conCX, conCY = 1, 1
return return
end end
local pfx = s:sub(1, 1) local pfx = s:sub(1, 1)
local cmd = s:sub(#s) local cmd = s:sub(#s)
if pfx == "[" then if pfx == "[" then
local np = tonumber(s:sub(2, -2)) or 1 local np = tonumber(s:sub(2, -2)) or 1
if cmd == "A" then if cmd == "H" or cmd == "f" then
local p = s:find(";")
if not p then
conCX, conCY = 1, 1
else
conCY = tonumber(s:sub(2, p - 1)) or 1
conCX = tonumber(s:sub(p + 1, -2)) or 1
end
elseif cmd == "K" then
console[conCY] = unicode.sub(console[conCY], 1, conCX - 1) .. (" "):rep(1 + conW - conCX)
elseif cmd == "A" then
conCY = conCY - np conCY = conCY - np
elseif cmd == "B" then elseif cmd == "B" then
conCY = conCY + np conCY = conCY + np
@ -211,34 +201,14 @@ local function writeANSI(s)
conCX = conCX + np conCX = conCX + np
elseif cmd == "D" then elseif cmd == "D" then
conCX = conCX - np conCX = conCX - np
elseif cmd == "f" or cmd == "H" then elseif cmd == "S" then
local p = s:find(";") for i = 1, np do
if not p then consoleSU()
conCY = np
conCX = 1
else
conCY = tonumber(s:sub(2, p - 1)) or 1
conCX = tonumber(s:sub(p + 1, -2)) or 1
end end
elseif cmd == "J" then elseif cmd == "T" then
consoleCLS() for i = 1, np do
elseif cmd == "K" then consoleSD()
if s == "[K" or s == "[0K" then
-- bash needs this
console[conCY] = unicode.sub(console[conCY], 1, conCX - 1) .. (" "):rep(1 + conW - conCX)
else
console[conCY] = (" "):rep(conW)
end end
elseif cmd == "n" then
if s == "[6n" then
for _, v in pairs(sendSigs) do
v("data", "\x1b[" .. conY .. ";" .. conX .. "R")
end
end
elseif cmd == "s" then
conSCX, conSCY = conCX, conCY
elseif cmd == "u" then
conCX, conCY = conSCX, conSCY
end end
end end
conCX = math.min(math.max(math.floor(conCX), 1), conW) conCX = math.min(math.max(math.floor(conCX), 1), conW)
@ -402,24 +372,10 @@ local function key(a, c)
-- Line Editing not active. -- Line Editing not active.
-- For now support a bare minimum. -- For now support a bare minimum.
for _, v in pairs(sendSigs) do for _, v in pairs(sendSigs) do
if a == "\x03" then if control then
if a == 99 then
v("telnet", "\xFF\xF4") v("telnet", "\xFF\xF4")
elseif c == 199 then end
v("data", "\x1b[H")
elseif c == 201 then
v("data", "\x1b[5~")
elseif c == 207 then
v("data", "\x1b[F")
elseif c == 209 then
v("data", "\x1b[6~")
elseif c == 203 then
v("data", "\x1b[D")
elseif c == 205 then
v("data", "\x1b[C")
elseif c == 200 then
v("data", "\x1b[A")
elseif c == 208 then
v("data", "\x1b[B")
elseif a == "\r" then elseif a == "\r" then
v("data", "\r\n") v("data", "\r\n")
elseif a then elseif a then

View File

@ -3,8 +3,8 @@ ocemu {
components { components {
{"gpu", "c1-gpu-tier3", 0, 160, 50, 3}, {"gpu", "c1-gpu-tier3", 0, 160, 50, 3},
{"gpu", "c1-gpu-tier1", 0, 50, 16, 1}, {"gpu", "c1-gpu-tier1", 0, 50, 16, 1},
{"screen_sdl2", "c1-screen-tier3", -1, 160, 50, 3}, -- {"screen_sdl2", "c1-screen-tier3", -1, 160, 50, 3},
-- {"screen_sdl2", "c1-screen-tier1", -1, 50, 16, 1}, {"screen_sdl2", "c1-screen-tier1", -1, 50, 16, 1},
{"modem", "c1-modem", 1, false}, {"modem", "c1-modem", 1, false},
{"eeprom", "c1-eeprom", 9, "lua/bios.lua"}, {"eeprom", "c1-eeprom", 9, "lua/bios.lua"},
{"filesystem", "c1-tmpfs", -1, "tmpfs", "tmpfs", false, 5}, {"filesystem", "c1-tmpfs", -1, "tmpfs", "tmpfs", false, 5},

View File

@ -1,96 +1,181 @@
-- This is released into the public domain. -- This is released into the public domain.
-- No warranty is provided, implied or otherwise. -- No warranty is provided, implied or otherwise.
-- app-kmt.lua : just a utility now -- app-kmt.lua : LC emergency plan
-- Authors: 20kdc -- Authors: 20kdc
local lcOverride = false
local l15 = "20kdc.duckdns.org:8888"
-- unicode.safeTextFormat'd lines
local console = {
"┎┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┒",
"┋ ┃ ┃╲ ╱┃ ▀▀┃▀▀ ┋",
"┋ ┃╳ ┃ ╲ ┃ ┃ ┋",
"┋ ┃ ╲ ┃ ╲╱ ┃ ┃ ┋",
"┋ ┋",
"┋ KittenOS NEO MUD Terminal ┋",
"┖┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┚",
":Type text, Enter key sends.",
":'>' is 'from you', '<' 'to you'.",
":':' is 'internal message'.",
":Control-Arrows resizes window.",
-- ":F5/F6 copies/pastes current line.",
":",
":",
":Enter target server:port",
-- 14
}
--++++++++++++++++++++++++++++++++++++++++
-- sW must not go below 3.
-- sH must not go below 2.
local sW, sH = 40, 15
local inet = neo.requireAccess("c.internet", "internet").list()() local inet = neo.requireAccess("c.internet", "internet").list()()
local windows = neo.requireAccess("x.neo.pub.window", "windows")
local window = windows(sW, sH)
local _, _, termId = ... local tcp = nil
local ok = pcall(function () local dummyTcp = {read = function() return "" end, write = function() end, close = function() end}
assert(string.sub(termId, 1, 12) == "x.neo.pub.t/") local tcpBuf = ""
end)
local termClose local function fmtLine(s)
s = unicode.safeTextFormat(s)
local l = unicode.len(s)
return unicode.sub(s .. (" "):rep(sW - l), -sW)
end
if not ok then local function line(i)
termId = nil if i ~= sH then
assert(neo.executeAsync("svc-t", function (res) assert(console[i], "console" .. i)
termId = res.access window.span(1, i, fmtLine(console[i]), 0xFFFFFF, 0)
termClose = res.close else
neo.scheduleTimer(0) window.span(1, i, fmtLine(l15), 0, 0xFFFFFF)
end, "kmt"))
while true do
if coroutine.yield() == "k.timer" then break end
end end
end end
local term = neo.requireAccess(termId, "terminal")
term.write([[ local function incoming(s)
local function shift(f)
for i = 1, #console - 1 do
console[i] = console[i + 1]
end
console[#console] = f
KittenOS NEO MUD Terminal end
-- Need to break this safely.
export TERM=ansi.sys <- IMPORTANT!!! shift("")
Enter target server:port... for i = 1, unicode.len(s) do
]]) local ch = unicode.sub(s, i, i)
if unicode.wlen(console[#console] .. ch) > sW then
shift(" ")
end
console[#console] = console[#console] .. ch
end
for i = 1, #console do
line(i)
end
end
local targetBuffer = "" local function submitLine()
incoming(">" .. l15)
if not tcp then
tcp = inet.connect(l15)
if not tcp then
incoming(":The connection could not be created.")
tcp = dummyTcp
else
if not tcp.finishConnect() then
incoming(":Warning: finishConnect = false")
end
neo.scheduleTimer(os.uptime() + 0.1)
end
else
-- PRJblackstar doesn't need \r but others might
tcp.write(l15 .. "\r\n")
end
l15 = ""
line(sH)
end
neo.scheduleTimer(0) if lcOverride then
submitLine()
end
local function clipEnt(tx)
tx = tx:gsub("\r", "")
local ci = tx:find("\n") or (#tx + 1)
tx = tx:sub(1, ci - 1)
l15 = tx
line(sH)
end
local control = false
while true do while true do
local e = {coroutine.yield()} local e = {coroutine.yield()}
if e[1] == "k.timer" then if e[1] == "k.timer" then
while tcp do while true do
local b, e = tcp.read(neo.readBufSize) local b, e = tcp.read(neo.readBufSize)
if not b then if not b then
if e then if e then
term.write("\nkmt: " .. tostring(e) .. "\n") incoming(":Warning: " .. e)
tcp.close() tcp.close()
tcp = nil tcp = dummyTcp
end end
break
elseif b == "" then elseif b == "" then
break break
else else
term.write(b) tcpBuf = tcpBuf .. b:gsub("\r", "")
end while true do
end local nlp = tcpBuf:find("\n")
neo.scheduleTimer(os.uptime() + 0.049) if nlp then
elseif e[1] == "k.procdie" then incoming("<" .. tcpBuf:sub(1, nlp - 1))
if e[3] == term.pid then tcpBuf = tcpBuf:sub(nlp + 1)
else
break break
end end
elseif e[1] == termId then
if targetBuffer and e[2] == "data" then
targetBuffer = targetBuffer .. e[3]:gsub("\r", "")
local p = targetBuffer:find("\n")
if p then
local ok, res, rer = pcall(inet.connect, targetBuffer:sub(1, p - 1))
targetBuffer = targetBuffer:sub(p + 1):gsub("\n", "\r\n")
if not ok then
-- Likes to return this kind
term.write("kmt: " .. tostring(res) .. "\n")
elseif not res then
-- Could theoretically return this kind
term.write("kmt: " .. tostring(rer) .. "\n")
else
-- Hopefully this kind
term.write("kmt: Connecting...\n")
tcp = res
tcp.write(targetBuffer)
targetBuffer = nil
end
end
elseif tcp and e[2] == "data" or e[2] == "telnet" then
tcp.write(e[3])
end end
end end
end end
neo.scheduleTimer(os.uptime() + 0.1)
elseif e[1] == "x.neo.pub.window" then
if e[3] == "close" then
if tcp then if tcp then
tcp.close() tcp.close()
end end
return
elseif e[3] == "clipboard" then
clipEnt(e[4])
elseif e[3] == "key" then
if e[5] == 29 or e[5] == 157 then
control = e[6]
elseif e[6] then
if not control then
if e[4] == 8 or e[4] == 127 then
l15 = unicode.sub(l15, 1, -2)
elseif e[4] == 13 then
submitLine()
elseif e[4] >= 32 then
l15 = l15 .. unicode.char(e[4])
end
line(sH)
elseif e[5] == 203 and sW > 8 then
sW = sW - 1
window.setSize(sW, sH)
elseif e[5] == 200 and sH > 2 then
sH = sH - 1
table.remove(console, 1)
window.setSize(sW, sH)
elseif e[5] == 205 then
sW = sW + 1
window.setSize(sW, sH)
elseif e[5] == 208 then
sH = sH + 1
table.insert(console, 1, "")
window.setSize(sW, sH)
end
end
elseif e[3] == "line" then
line(e[4])
end
end
end

View File

@ -1,27 +1,34 @@
The "svc-t" program / "x.neo.pub.t" The "svc-t" program / "x.svc.t"
permission makes up the terminal permission makes up the terminal
subsystem for KittenOS NEO. subsystem for KittenOS NEO.
--- THEORETICAL TERMINALS MODEL --- --- THEORETICAL TERMINALS MODEL ---
The theoretical model for terminals The theoretical model for terminals
in KittenOS NEO is a TELNET client, in KittenOS NEO is a TELNET client
using the non-standard behavior of that only supports the ECHO option,
treating a lack of remote echo as and uses the non-standard behavior
meaning 'local line editing'. of treating ECHO ON as 'enable local
line editing'.
To prevent code size going too far, To prevent code size going too far,
the shipped terminal supports: the client is extremely restricted
in capabilities.
1. Built-in history as part of the
line editing functionality
2. ANSI.SYS-compatible display, but
no support for attributes.
If you really want full support, If you really want full support,
write a better terminal application. write a better terminal application.
Features that get added will be added
in accordance with ANSI/TELNET where
reasonable or in a compatible-ish
fashion where unreasonable.
The defaults will be set based on
whatever app-luashell requires, as
this is what is expected of modern
terminal systems regardless of what
the standards may have to say.
A process starting another process A process starting another process
connected to the same terminal is connected to the same terminal is
advised to wait for that process to advised to wait for that process to
@ -75,7 +82,7 @@ When the terminal has shown, the
function provided is called with a function provided is called with a
table as follows: table as follows:
access = "x.neo.pub.t/<...>" access = "x.svc.t/<...>"
close = function (): close terminal close = function (): close terminal
The k.kill permission and the close The k.kill permission and the close
@ -88,7 +95,7 @@ In either case, when the access has
been acquired, the following API is been acquired, the following API is
presented: presented:
id = "x.neo.pub.t/<...>" id = "x.svc.t/<...>"
pid = <The terminal's PID.> pid = <The terminal's PID.>
write = function (text): Writes the write = function (text): Writes the
TELNET data to the terminal. TELNET data to the terminal.