1
0
mirror of https://github.com/20kdc/OC-KittenOS.git synced 2024-09-28 06:31:07 +10:00
OC-KittenOS/apps/omniterm.lua
gamemanj ea6b987c21 Added 'omniterm' - basic terminal app
Omniterm, + some sort of RPC->net interface,
 should allow applications that just need a text console to run,
 and have a nice transparent interface between local and remote control.

And in any case, it should definitely work for the more likely use case
 of wanting to have a chat over a local OpenComputers network in a base.
2016-10-27 20:59:25 +01:00

268 lines
6.3 KiB
Lua

-- OmniTerm: multi-driver terminal
-- Has RPC (lets apps use OmniTerm as a helper. Protocol commands are "msg", "join".),
-- Echo (test),
--
local table, unicode, math, proc, lang = A.request("table", "unicode", "math", "proc", "lang")
local app = {}
local termWidth = 25
-- console buffer cannot scroll left/right
local consoleBuffer = lang.getTable()
if not consoleBuffer then
consoleBuffer = {
--1234512345123451234512345
"OmniTerm Interfaces:",
"rpc:<appId> |RPC",
"tun:<CA> |Linked Card",
"net:<RA>:<pt>|Net.Card",
--Allows talking with everybody on a network.
"net:<pt> |Net.Card-BC",
--Allows talking with MineOS Chat... in theory...
"moschat:<RA> |MineOS Chat",
"componentlist|Components.",
"Exit with Ctrl-W."
}
end
local lineBuffer = ""
local function incomingSubLine(txt)
for i = 1, #consoleBuffer - 1 do
consoleBuffer[i] = consoleBuffer[i + 1]
end
consoleBuffer[#consoleBuffer] = txt
end
local function incomingLine(txt)
txt = unicode.safeTextFormat(txt)
-- Note: >2 wide characters can *stay away*. Well away.
-- The result will always leave a 1-char gap at the end of a line,
-- which suffices to allow wide characters to work properly.
while unicode.len(txt) > termWidth do
incomingSubLine(unicode.sub(txt, 1, termWidth - 1))
txt = " " .. unicode.sub(txt, termWidth)
end
incomingSubLine(txt)
end
local function outgoingLine(txt)
end
local function driverShutdown()
end
-- Driver utils
local function parseAddrPort(id)
if id:match("[a-f0-9%-]+:[0-9]+") ~= id then
error("Bad Syntax (<proto>:<hw-addr>:<port>)")
end
local addr = id:match("[a-f0-9%-]+")
local port = tonumber(id:match(":[0-9]+"):sub(2))
return addr, port
end
local drivers = {
-- rpc:appID
rpc = function (id)
proc.sendRPC(id, "join")
app.rpc = function (srcP, srcD, cmd, txt)
if srcD == id then
if cmd == "msg" then
incomingLine(tostring(txt))
A.timer(0.01)
end
end
end
outgoingLine = function (txt)
proc.sendRPC(id, "msg", txt)
end
end,
-- tun:<addr>
tun = function (id)
local cT = A.request("c.tunnel")
local tunnel = nil
if id ~= "" then
for v in cT.list() do
if v.address:sub(1, id:len()) == id then tunnel = v end
end
end
if not tunnel then error("Couldn't find that tunnel.") end
app.event = function (...)
local ev = {...}
if ev[1] == "modem_message" then
if ev[2] == tunnel.address then
incomingLine(tostring(ev[6]))
return true
end
end
end
outgoingLine = function (txt)
tunnel.send(txt)
end
end,
-- net:targetAddr:port
-- Uses all modems to send to a target. (Tunnels do not count.)
net = function (id)
local addr = nil
local port = tonumber(id)
if not port then
addr, port = parseAddrPort(id)
end
local cM = A.request("c.modem")
for v in cM.list() do
v.open(port)
if v.isWireless() then
-- Probably not the best, but it'll do until a proper strength controller is written.
-- I don't know if it's set to full strength by default, and that's critical!
v.setStrength(400)
end
end
app.event = function (...)
local a = {...}
if a[1] == "modem_message" then
if (a[3] == addr) or (not addr) then
if a[4] == port then
incomingLine(tostring(a[6]))
end
end
end
return true
end
outgoingLine = function (txt)
for v in cM.list() do
if addr then
v.send(addr, port, txt)
else
v.broadcast(port, txt)
end
end
end
end,
-- moschat:<address>
-- Allows basic communication with MineOS Chat systems, in theory.
-- In practice? This is untested against real MineOS.
moschat = function (id)
local port = 899 -- MOSChat port
local cM = A.request("c.modem")
for v in cM.list() do
v.open(port)
end
app.event = function (...)
local a = {...}
if a[1] == "modem_message" then
if a[3] == id then
if a[4] == port then
if a[6] == "HereIsMessageToYou" then
incomingLine(tostring(a[8]))
end
end
end
end
return true
end
outgoingLine = function (txt)
for v in cM.list() do
v.send(id, port, "HereIsMessageToYou", nil, txt)
end
end
end,
-- echo:postfix
-- Echoes back any line sent, plus a postfix.
echo = function (id)
outgoingLine = function (txt)
incomingLine(txt .. id)
end
end,
-- componentlist
-- Used to list components
-- Useful because OpenComputers stupidly cuts off text,
-- which is really bad when you need the *whole* ID...
-- >.< like when doing anything involving networking.
componentlist = function (id)
local stat = A.request("stat")
outgoingLine = function (txt)
incomingLine("Components [" .. txt .. "]:")
for ad, tp in stat.componentList() do
if tp == txt then
incomingLine(ad)
end
end
end
end
}
local hasDriver = false
local function typedLine(txt)
if not hasDriver then
-- Initialize driver
local dname = txt:match("^[^:]+")
if not dname then
incomingLine("You must specify what to connect to.")
return
end
if not drivers[dname] then
incomingLine("No such driver '" .. dname .. "'")
return
end
txt = txt:sub(dname:len() + 2)
incomingLine("Connecting with driver '" .. dname .. "'")
local ok, err = pcall(drivers[dname], txt)
if not ok then
incomingLine("Error: " .. err)
else
local a = ""
for i = 1, termWidth do a = a .. "-" end
incomingLine(a)
hasDriver = true
end
else
incomingLine(txt)
outgoingLine(txt)
end
end
-- triggered by drivers that can't directly cause redraws
function app.update()
return true
end
function app.get_ch(x, y)
local l, lp = consoleBuffer[y], 0
if y == #consoleBuffer + 1 then
lp = unicode.len(lineBuffer) - math.floor(termWidth / 2)
if lp < 1 then lp = 1 end
l, lp = unicode.safeTextFormat(lineBuffer, lp)
lp = lp - 1
end
return unicode.sub(l, x + lp, x + lp)
end
local ctrlFlag = false
function app.key(ka, kc, down)
if kc == 29 then
ctrlFlag = down
return false
end
if ctrlFlag then
if down then
if kc == 17 then -- W
driverShutdown()
A.die()
end
end
return false
end
if down then
if ka ~= 0 then
if ka == 13 then
typedLine(lineBuffer)
lineBuffer = ""
return true
else
if ka == 8 then
lineBuffer = unicode.sub(lineBuffer, 1, unicode.len(lineBuffer) - 1)
return true
else
lineBuffer = lineBuffer .. unicode.char(ka)
return true
end
end
end
end
end
return app, termWidth, #consoleBuffer + 1