mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-15 02:12:42 -04:00
Merge remote-tracking branch 'upstream/master-MC1.8.9' into master-MC1.9.4
This commit is contained in:
commit
e810b556e1
@ -17,18 +17,18 @@ for i = 1, #args do
|
|||||||
if args[i] == "-" then
|
if args[i] == "-" then
|
||||||
file, reason = io.stdin, "missing stdin"
|
file, reason = io.stdin, "missing stdin"
|
||||||
else
|
else
|
||||||
file, reason = io.open(shell.resolve(args[i]))
|
file, reason = fs.open(shell.resolve(args[i]))
|
||||||
end
|
end
|
||||||
if not file then
|
if not file then
|
||||||
io.stderr:write(string.format("cat: %s: %s\n", args[i], tostring(reason)))
|
io.stderr:write(string.format("cat: %s: %s\n", args[i], tostring(reason)))
|
||||||
ec = 1
|
ec = 1
|
||||||
else
|
else
|
||||||
repeat
|
repeat
|
||||||
local line = file:read("*L")
|
local chunk = file:read(2048)
|
||||||
if line then
|
if chunk then
|
||||||
io.write(line)
|
io.write(chunk)
|
||||||
end
|
end
|
||||||
until not line
|
until not chunk
|
||||||
file:close()
|
file:close()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
local args, options = require("shell").parse(...)
|
local args, options = require("shell").parse(...)
|
||||||
if options.help then
|
if options.help then
|
||||||
print([[`echo` writes the provided string(s) to the standard output.
|
io.write([[
|
||||||
|
`echo` writes the provided string(s) to the standard output.
|
||||||
-n do not output the trialing newline
|
-n do not output the trialing newline
|
||||||
-e enable interpretation of backslash escapes
|
-e enable interpretation of backslash escapes
|
||||||
--help display this help and exit]])
|
--help display this help and exit
|
||||||
|
]])
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if options.e then
|
if options.e then
|
||||||
for index,arg in ipairs(args) do
|
for index,arg in ipairs(args) do
|
||||||
|
-- use lua load here to interpret escape sequences such as \27
|
||||||
|
-- instead of writing my own language to interpret them myself
|
||||||
|
-- note that in a real terminal, \e is used for \27
|
||||||
args[index] = assert(load("return \"" .. arg:gsub('"', [[\"]]) .. "\""))()
|
args[index] = assert(load("return \"" .. arg:gsub('"', [[\"]]) .. "\""))()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
io.write(table.concat(args," "))
|
io.write(table.concat(args," "))
|
||||||
if not options.n then
|
if not options.n then
|
||||||
print()
|
io.write("\n")
|
||||||
end
|
end
|
||||||
|
@ -68,7 +68,7 @@ local function confirm()
|
|||||||
if bForce then
|
if bForce then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
local r = io.read("*l")
|
local r = io.read()
|
||||||
return r == 'y' or r == 'yes'
|
return r == 'y' or r == 'yes'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ if #args == 0 then
|
|||||||
end
|
end
|
||||||
io.write(sh.expand(os.getenv("PS1") or "$ "))
|
io.write(sh.expand(os.getenv("PS1") or "$ "))
|
||||||
end
|
end
|
||||||
local command = tty:read(input_handler)
|
local command = tty.read(input_handler)
|
||||||
if command then
|
if command then
|
||||||
command = text.trim(command)
|
command = text.trim(command)
|
||||||
if command == "exit" then
|
if command == "exit" then
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
local shell = require('shell')
|
local shell = require("shell")
|
||||||
|
local tty = require("tty")
|
||||||
local args, options = shell.parse(...)
|
local args, options = shell.parse(...)
|
||||||
|
|
||||||
if options.help then
|
if options.help then
|
||||||
@ -50,4 +51,11 @@ for _,v in ipairs(args) do
|
|||||||
total_time = total_time + time_type_multiplier(time_type) * interval
|
total_time = total_time + time_type_multiplier(time_type) * interval
|
||||||
end
|
end
|
||||||
|
|
||||||
os.sleep(total_time)
|
local ins = io.stdin.stream
|
||||||
|
local pull = ins.pull
|
||||||
|
local start = 1
|
||||||
|
if not pull then
|
||||||
|
pull = require("event").pull
|
||||||
|
start = 2
|
||||||
|
end
|
||||||
|
pull(select(start, ins, total_time, "interrupted"))
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
local buffer = require("buffer")
|
local buffer = require("buffer")
|
||||||
local tty = require("tty")
|
local tty_stream = require("tty").stream
|
||||||
|
|
||||||
local core_stdin = buffer.new("r", tty)
|
local core_stdin = buffer.new("r", tty_stream)
|
||||||
local core_stdout = buffer.new("w", tty)
|
local core_stdout = buffer.new("w", tty_stream)
|
||||||
local core_stderr = buffer.new("w", setmetatable(
|
local core_stderr = buffer.new("w", setmetatable(
|
||||||
{
|
{
|
||||||
write = function(_, str)
|
write = function(_, str)
|
||||||
return tty:write("\27[31m"..str.."\27[37m")
|
return tty_stream:write("\27[31m"..str.."\27[37m")
|
||||||
end
|
end
|
||||||
}, {__index=tty}))
|
}, {__index=tty_stream}))
|
||||||
|
|
||||||
core_stdout:setvbuf("no")
|
core_stdout:setvbuf("no")
|
||||||
core_stderr:setvbuf("no")
|
core_stderr:setvbuf("no")
|
||||||
@ -16,9 +16,9 @@ core_stdin.tty = true
|
|||||||
core_stdout.tty = true
|
core_stdout.tty = true
|
||||||
core_stderr.tty = true
|
core_stderr.tty = true
|
||||||
|
|
||||||
core_stdin.close = tty.close
|
core_stdin.close = tty_stream.close
|
||||||
core_stdout.close = tty.close
|
core_stdout.close = tty_stream.close
|
||||||
core_stderr.close = tty.close
|
core_stderr.close = tty_stream.close
|
||||||
|
|
||||||
local io_mt = getmetatable(io) or {}
|
local io_mt = getmetatable(io) or {}
|
||||||
io_mt.__index = function(_, k)
|
io_mt.__index = function(_, k)
|
||||||
|
@ -7,7 +7,6 @@ if tty.isAvailable() then
|
|||||||
io.write("\27[40m\27[37m")
|
io.write("\27[40m\27[37m")
|
||||||
tty.clear()
|
tty.clear()
|
||||||
end
|
end
|
||||||
tty.setCursorBlink(true)
|
|
||||||
end
|
end
|
||||||
dofile("/etc/motd")
|
dofile("/etc/motd")
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
-- called from /init.lua
|
-- called from /init.lua
|
||||||
local raw_loadfile = ...
|
local raw_loadfile = ...
|
||||||
|
|
||||||
_G._OSVERSION = "OpenOS 1.6.7"
|
_G._OSVERSION = "OpenOS 1.6.8"
|
||||||
|
|
||||||
local component = component
|
local component = component
|
||||||
local computer = computer
|
local computer = computer
|
||||||
|
@ -85,7 +85,3 @@ function tty.on_tab(handler, cursor)
|
|||||||
cursor:move(-tail)
|
cursor:move(-tail)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty:size()
|
|
||||||
return #(self.window.ansi_response or "")
|
|
||||||
end
|
|
||||||
|
@ -10,12 +10,11 @@ end
|
|||||||
|
|
||||||
local env -- forward declare for binding in metamethod
|
local env -- forward declare for binding in metamethod
|
||||||
env = setmetatable({}, {
|
env = setmetatable({}, {
|
||||||
__index = function(t, k)
|
__index = function(_, k)
|
||||||
_ENV[k] = _ENV[k] or optrequire(k)
|
_ENV[k] = _ENV[k] or optrequire(k)
|
||||||
return _ENV[k]
|
return _ENV[k]
|
||||||
end,
|
end,
|
||||||
__pairs = function(self)
|
__pairs = function(t)
|
||||||
local t = self
|
|
||||||
return function(_, key)
|
return function(_, key)
|
||||||
local k, v = next(t, key)
|
local k, v = next(t, key)
|
||||||
if not k and t == env then
|
if not k and t == env then
|
||||||
@ -94,7 +93,7 @@ io.write("Press Ctrl+D to exit the interpreter.\n\27[37m")
|
|||||||
|
|
||||||
while tty.isAvailable() do
|
while tty.isAvailable() do
|
||||||
io.write(env._PROMPT)
|
io.write(env._PROMPT)
|
||||||
local command = tty:read(read_handler)
|
local command = tty.read(read_handler)
|
||||||
if not command then -- eof
|
if not command then -- eof
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -150,4 +150,11 @@ function process.internal.continue(co, ...)
|
|||||||
return table.unpack(result, 2, result.n)
|
return table.unpack(result, 2, result.n)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function process.running(level) -- kept for backwards compat, prefer process.info
|
||||||
|
local info = process.info(level)
|
||||||
|
if info then
|
||||||
|
return info.path, info.env, info.command
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return process
|
return process
|
||||||
|
@ -17,40 +17,40 @@ function serialization.serialize(value, pretty)
|
|||||||
["until"]=true, ["while"]=true}
|
["until"]=true, ["while"]=true}
|
||||||
local id = "^[%a_][%w_]*$"
|
local id = "^[%a_][%w_]*$"
|
||||||
local ts = {}
|
local ts = {}
|
||||||
local function s(v, l)
|
local result_pack = {}
|
||||||
local t = type(v)
|
local function recurse(current_value, depth)
|
||||||
if t == "nil" then
|
local t = type(current_value)
|
||||||
return "nil"
|
if t == "number" then
|
||||||
elseif t == "boolean" then
|
if current_value ~= current_value then
|
||||||
return v and "true" or "false"
|
table.insert(result_pack, "0/0")
|
||||||
elseif t == "number" then
|
elseif current_value == math.huge then
|
||||||
if v ~= v then
|
table.insert(result_pack, "math.huge")
|
||||||
return "0/0"
|
elseif current_value == -math.huge then
|
||||||
elseif v == math.huge then
|
table.insert(result_pack, "-math.huge")
|
||||||
return "math.huge"
|
|
||||||
elseif v == -math.huge then
|
|
||||||
return "-math.huge"
|
|
||||||
else
|
else
|
||||||
return tostring(v)
|
table.insert(result_pack, tostring(current_value))
|
||||||
end
|
end
|
||||||
elseif t == "string" then
|
elseif t == "string" then
|
||||||
return string.format("%q", v):gsub("\\\n","\\n")
|
table.insert(result_pack, (string.format("%q", current_value):gsub("\\\n","\\n")))
|
||||||
elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then
|
elseif
|
||||||
return tostring(v)
|
t == "nil" or
|
||||||
|
t == "boolean" or
|
||||||
|
pretty and (t ~= "table" or (getmetatable(current_value) or {}).__tostring) then
|
||||||
|
table.insert(result_pack, tostring(current_value))
|
||||||
elseif t == "table" then
|
elseif t == "table" then
|
||||||
if ts[v] then
|
if ts[current_value] then
|
||||||
if pretty then
|
if pretty then
|
||||||
return "recursion"
|
table.insert(result_pack, "recursion")
|
||||||
|
return
|
||||||
else
|
else
|
||||||
error("tables with cycles are not supported")
|
error("tables with cycles are not supported")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ts[v] = true
|
ts[current_value] = true
|
||||||
local i, r = 1, nil
|
|
||||||
local f
|
local f
|
||||||
if pretty then
|
if pretty then
|
||||||
local ks, sks, oks = {}, {}, {}
|
local ks, sks, oks = {}, {}, {}
|
||||||
for k in local_pairs(v) do
|
for k in local_pairs(current_value) do
|
||||||
if type(k) == "number" then
|
if type(k) == "number" then
|
||||||
table.insert(ks, k)
|
table.insert(ks, k)
|
||||||
elseif type(k) == "string" then
|
elseif type(k) == "string" then
|
||||||
@ -72,46 +72,51 @@ function serialization.serialize(value, pretty)
|
|||||||
n = n + 1
|
n = n + 1
|
||||||
local k = ks[n]
|
local k = ks[n]
|
||||||
if k ~= nil then
|
if k ~= nil then
|
||||||
return k, v[k]
|
return k, current_value[k]
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
f = table.pack(local_pairs(v))
|
f = table.pack(local_pairs(current_value))
|
||||||
end
|
end
|
||||||
|
local i = 1
|
||||||
|
local first = true
|
||||||
|
table.insert(result_pack, "{")
|
||||||
for k, v in table.unpack(f) do
|
for k, v in table.unpack(f) do
|
||||||
if r then
|
if not first then
|
||||||
r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "")
|
table.insert(result_pack, ",")
|
||||||
else
|
if pretty then
|
||||||
r = "{"
|
table.insert(result_pack, "\n" .. string.rep(" ", depth))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
first = nil
|
||||||
local tk = type(k)
|
local tk = type(k)
|
||||||
if tk == "number" and k == i then
|
if tk == "number" and k == i then
|
||||||
i = i + 1
|
i = i + 1
|
||||||
r = r .. s(v, l + 1)
|
recurse(v, depth + 1)
|
||||||
else
|
else
|
||||||
if tk == "string" and not kw[k] and string.match(k, id) then
|
if tk == "string" and not kw[k] and string.match(k, id) then
|
||||||
r = r .. k
|
table.insert(result_pack, k)
|
||||||
else
|
else
|
||||||
r = r .. "[" .. s(k, l + 1) .. "]"
|
table.insert(result_pack, "[")
|
||||||
|
recurse(k, depth + 1)
|
||||||
|
table.insert(result_pack, "]")
|
||||||
end
|
end
|
||||||
r = r .. "=" .. s(v, l + 1)
|
table.insert(result_pack, "=")
|
||||||
|
recurse(v, depth + 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ts[v] = nil -- allow writing same table more than once
|
ts[current_value] = nil -- allow writing same table more than once
|
||||||
return (r or "{") .. "}"
|
table.insert(result_pack, "}")
|
||||||
else
|
else
|
||||||
if pretty then
|
error("unsupported type: " .. t)
|
||||||
return tostring(v)
|
|
||||||
else
|
|
||||||
error("unsupported type: " .. t)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local result = s(value, 1)
|
recurse(value, 1)
|
||||||
local limit = type(pretty) == "number" and pretty or 10
|
local result = table.concat(result_pack)
|
||||||
if pretty then
|
if pretty then
|
||||||
|
local limit = type(pretty) == "number" and pretty or 10
|
||||||
local truncate = 0
|
local truncate = 0
|
||||||
while limit > 0 and truncate do
|
while limit > 0 and truncate do
|
||||||
truncate = string.find(result, "\n", truncate + 1, true)
|
truncate = string.find(result, "\n", truncate + 1, true)
|
||||||
|
@ -70,7 +70,7 @@ end
|
|||||||
local function build_horizontal_reader(cursor)
|
local function build_horizontal_reader(cursor)
|
||||||
cursor.clear_tail = function(self)
|
cursor.clear_tail = function(self)
|
||||||
local w,_,dx,dy,x,y = tty.getViewport()
|
local w,_,dx,dy,x,y = tty.getViewport()
|
||||||
local _,s2=tty.internal.split(self)
|
local _,s2=tty.split(self)
|
||||||
local wlen = math.min(unicode.wlen(s2),w-x+1)
|
local wlen = math.min(unicode.wlen(s2),w-x+1)
|
||||||
tty.gpu().fill(x+dx,y+dy,wlen,1," ")
|
tty.gpu().fill(x+dx,y+dy,wlen,1," ")
|
||||||
end
|
end
|
||||||
@ -87,15 +87,16 @@ local function build_horizontal_reader(cursor)
|
|||||||
cursor.draw = function(_, text)
|
cursor.draw = function(_, text)
|
||||||
local nowrap = tty.window.nowrap
|
local nowrap = tty.window.nowrap
|
||||||
tty.window.nowrap = true
|
tty.window.nowrap = true
|
||||||
tty:write(text)
|
tty.stream:write(text)
|
||||||
tty.window.nowrap = nowrap
|
tty.window.nowrap = nowrap
|
||||||
end
|
end
|
||||||
cursor.scroll = function(self)
|
cursor.scroll = function(self, goback, prev_x)
|
||||||
local win = tty.window
|
local win = tty.window
|
||||||
local gpu,data,px,i = win.gpu, self.data, self.promptx, self.index
|
win.x = goback and prev_x or win.x
|
||||||
local w,_,dx,dy,x,y = tty.getViewport()
|
local x = win.x
|
||||||
win.x = math.max(self.promptx, math.min(w, x))
|
local w = win.width
|
||||||
local available,sx,sy = w-px+1,px+dx,y+dy
|
local data,px,i = self.data, self.promptx, self.index
|
||||||
|
local available = w-px+1
|
||||||
if x > w then
|
if x > w then
|
||||||
local blank
|
local blank
|
||||||
if i == unicode.len(data) then
|
if i == unicode.len(data) then
|
||||||
@ -107,14 +108,19 @@ local function build_horizontal_reader(cursor)
|
|||||||
local rev = unicode.reverse(data)
|
local rev = unicode.reverse(data)
|
||||||
local ending = unicode.wtrunc(rev, available+1)
|
local ending = unicode.wtrunc(rev, available+1)
|
||||||
data = unicode.reverse(ending)
|
data = unicode.reverse(ending)
|
||||||
gpu.set(sx,sy,data..blank)
|
win.x = self.promptx
|
||||||
win.x=math.min(w,self.promptx+unicode.wlen(data))
|
self:draw(data..blank)
|
||||||
|
-- wide chars may place the cursor not exactly at the end
|
||||||
|
win.x = math.min(w, self.promptx + unicode.wlen(data))
|
||||||
|
-- x could be negative, we scroll it back into view
|
||||||
elseif x < self.promptx then
|
elseif x < self.promptx then
|
||||||
data = unicode.sub(data, self.index+1)
|
data = unicode.sub(data, self.index+1)
|
||||||
if unicode.wlen(data) > available then
|
if unicode.wlen(data) > available then
|
||||||
data = unicode.wtrunc(data,available+1)
|
data = unicode.wtrunc(data,available+1)
|
||||||
end
|
end
|
||||||
gpu.set(sx,sy,data)
|
win.x = self.promptx
|
||||||
|
self:draw(data)
|
||||||
|
win.x = math.max(px, math.min(w, x))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cursor.clear = function(self)
|
cursor.clear = function(self)
|
||||||
@ -135,25 +141,15 @@ local function inject_filter(handler, filter)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local mt =
|
handler.key_down = function(self, cursor, char, code)
|
||||||
{
|
if code == keys.enter or code == keys.numpadenter then
|
||||||
__newindex = function(tbl, key, value)
|
if not filter(cursor.data) then
|
||||||
if key == "key_down" then
|
computer.beep(2000, 0.1)
|
||||||
local tty_key_down = value
|
return false -- ignore
|
||||||
value = function(_handler, cursor, char, code)
|
|
||||||
if code == keys.enter or code == keys.numpadenter then
|
|
||||||
if not filter(cursor.data) then
|
|
||||||
computer.beep(2000, 0.1)
|
|
||||||
return false -- ignore
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return tty_key_down(_handler, cursor, char, code)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
rawset(tbl, key, value)
|
|
||||||
end
|
end
|
||||||
}
|
return tty.key_down_handler(self, cursor, char, code)
|
||||||
setmetatable(handler, mt)
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -198,15 +194,16 @@ function term.read(history, dobreak, hint, pwchar, filter)
|
|||||||
local handler = history
|
local handler = history
|
||||||
handler.hint = handler.hint or hint
|
handler.hint = handler.hint or hint
|
||||||
|
|
||||||
local cursor = tty.internal.build_vertical_reader()
|
local cursor = tty.build_vertical_reader()
|
||||||
if handler.nowrap then
|
if handler.nowrap then
|
||||||
build_horizontal_reader(cursor)
|
build_horizontal_reader(cursor)
|
||||||
end
|
end
|
||||||
|
|
||||||
inject_filter(handler, filter)
|
inject_filter(handler, filter)
|
||||||
inject_mask(cursor, dobreak, pwchar or history.pwchar)
|
inject_mask(cursor, dobreak, pwchar or history.pwchar)
|
||||||
|
handler.cursor = cursor
|
||||||
|
|
||||||
return tty:read(handler, cursor)
|
return tty.read(handler)
|
||||||
end
|
end
|
||||||
|
|
||||||
function term.getGlobalArea(window)
|
function term.getGlobalArea(window)
|
||||||
@ -221,6 +218,14 @@ function term.clearLine(window)
|
|||||||
window.x = 1
|
window.x = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function term.setCursorBlink(enabled)
|
||||||
|
tty.window.blink = enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
function term.getCursorBlink()
|
||||||
|
return tty.window.blink
|
||||||
|
end
|
||||||
|
|
||||||
function term.pull(...)
|
function term.pull(...)
|
||||||
local args = table.pack(...)
|
local args = table.pack(...)
|
||||||
local timeout = nil
|
local timeout = nil
|
||||||
@ -228,7 +233,15 @@ function term.pull(...)
|
|||||||
timeout = table.remove(args, 1)
|
timeout = table.remove(args, 1)
|
||||||
args.n = args.n - 1
|
args.n = args.n - 1
|
||||||
end
|
end
|
||||||
return tty.pull(nil, timeout, table.unpack(args, 1, args.n))
|
local stdin_stream = io.stdin.stream
|
||||||
|
if stdin_stream.pull then
|
||||||
|
return stdin_stream:pull(nil, timeout, table.unpack(args, 1, args.n))
|
||||||
|
end
|
||||||
|
-- if stdin does not have pull() we can build the result
|
||||||
|
local result = io.read(1)
|
||||||
|
if result then
|
||||||
|
return "clipboard", nil, result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function term.bind(gpu, window)
|
function term.bind(gpu, window)
|
||||||
|
@ -77,17 +77,29 @@ local function get_box_thread_handle(handles, bCreate)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function box_thread:resume()
|
function box_thread:resume()
|
||||||
if self:status() ~= "suspended" then
|
local mt = getmetatable(self)
|
||||||
return nil, "cannot resume " .. self:status() .. " thread"
|
if mt.__status ~= "suspended" then
|
||||||
|
return nil, "cannot resume " .. mt.__status .. " thread"
|
||||||
end
|
end
|
||||||
getmetatable(self).__status = "running"
|
mt.__status = "running"
|
||||||
|
-- register the thread to wake up
|
||||||
|
if coroutine.status(self.pco.root) == "suspended" and not mt.reg then
|
||||||
|
mt.register(0)
|
||||||
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function box_thread:suspend()
|
function box_thread:suspend()
|
||||||
if self:status() ~= "running" then
|
local mt = getmetatable(self)
|
||||||
return nil, "cannot suspend " .. self:status() .. " thread"
|
if mt.__status ~= "running" then
|
||||||
|
return nil, "cannot suspend " .. mt.__status .. " thread"
|
||||||
end
|
end
|
||||||
getmetatable(self).__status = "suspended"
|
mt.__status = "suspended"
|
||||||
|
local pco_status = coroutine.status(self.pco.root)
|
||||||
|
if pco_status == "running" or pco_status == "normal" then
|
||||||
|
mt.coma()
|
||||||
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function box_thread:status()
|
function box_thread:status()
|
||||||
@ -113,29 +125,27 @@ function box_thread:attach(parent)
|
|||||||
if not proc then return nil, "thread failed to attach, process not found" end
|
if not proc then return nil, "thread failed to attach, process not found" end
|
||||||
if mt.attached == proc then return self end -- already attached
|
if mt.attached == proc then return self end -- already attached
|
||||||
|
|
||||||
local waiting_handler
|
|
||||||
if mt.attached then
|
if mt.attached then
|
||||||
local prev_btHandle = assert(get_box_thread_handle(mt.attached.data.handles), "thread panic: no thread handle")
|
local prev_btHandle = assert(get_box_thread_handle(mt.attached.data.handles), "thread panic: no thread handle")
|
||||||
for i,h in ipairs(prev_btHandle) do
|
for i,h in ipairs(prev_btHandle) do
|
||||||
if h == self then
|
if h == self then
|
||||||
table.remove(prev_btHandle, i)
|
table.remove(prev_btHandle, i)
|
||||||
if mt.id then
|
|
||||||
waiting_handler = assert(mt.attached.data.handlers[mt.id], "thread panic: no event handler")
|
|
||||||
mt.attached.data.handlers[mt.id] = nil
|
|
||||||
end
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- registration happens on the attached proc, unregister before reparenting
|
||||||
|
local waiting_handler = mt.unregister()
|
||||||
|
|
||||||
-- attach to parent or the current process
|
-- attach to parent or the current process
|
||||||
mt.attached = proc
|
mt.attached = proc
|
||||||
local handles = proc.data.handles
|
|
||||||
|
|
||||||
-- this process may not have a box_thread manager handle
|
-- this process may not have a box_thread manager handle
|
||||||
local btHandle = get_box_thread_handle(handles, true)
|
local btHandle = get_box_thread_handle(proc.data.handles, true)
|
||||||
table.insert(btHandle, self)
|
table.insert(btHandle, self)
|
||||||
|
|
||||||
|
-- register on the new parent
|
||||||
if waiting_handler then -- event-waiting
|
if waiting_handler then -- event-waiting
|
||||||
mt.register(waiting_handler.timeout - computer.uptime())
|
mt.register(waiting_handler.timeout - computer.uptime())
|
||||||
end
|
end
|
||||||
@ -143,6 +153,23 @@ function box_thread:attach(parent)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function thread.current()
|
||||||
|
local proc = process.findProcess()
|
||||||
|
local thread_root
|
||||||
|
while proc do
|
||||||
|
if thread_root then
|
||||||
|
for _,bt in ipairs(get_box_thread_handle(proc.data.handles) or {}) do
|
||||||
|
if bt.pco.root == thread_root then
|
||||||
|
return bt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
thread_root = proc.data.coroutine_handler.root
|
||||||
|
end
|
||||||
|
proc = proc.parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function thread.create(fp, ...)
|
function thread.create(fp, ...)
|
||||||
checkArg(1, fp, "function")
|
checkArg(1, fp, "function")
|
||||||
|
|
||||||
@ -153,8 +180,8 @@ function thread.create(fp, ...)
|
|||||||
mt.__status = "running"
|
mt.__status = "running"
|
||||||
local fp_co = t.pco.create(fp)
|
local fp_co = t.pco.create(fp)
|
||||||
-- run fp_co until dead
|
-- run fp_co until dead
|
||||||
-- pullSignal will yield_all past this point
|
-- pullSignal will yield_past this point
|
||||||
-- but yield will return here, we pullSignal from here to yield_all
|
-- but yield will return here, we pullSignal from here to yield_past
|
||||||
local args = table.pack(...)
|
local args = table.pack(...)
|
||||||
while true do
|
while true do
|
||||||
local result = table.pack(t.pco.resume(fp_co, table.unpack(args, 1, args.n)))
|
local result = table.pack(t.pco.resume(fp_co, table.unpack(args, 1, args.n)))
|
||||||
@ -188,7 +215,7 @@ function thread.create(fp, ...)
|
|||||||
|
|
||||||
--special resume to keep track of process death
|
--special resume to keep track of process death
|
||||||
function mt.private_resume(...)
|
function mt.private_resume(...)
|
||||||
mt.id = nil
|
mt.unregister()
|
||||||
-- this thread may have been killed
|
-- this thread may have been killed
|
||||||
if t:status() == "dead" then return end
|
if t:status() == "dead" then return end
|
||||||
local result = table.pack(t.pco.resume(t.pco.root, ...))
|
local result = table.pack(t.pco.resume(t.pco.root, ...))
|
||||||
@ -209,17 +236,50 @@ function thread.create(fp, ...)
|
|||||||
timeout, -- wait for the time specified by the caller
|
timeout, -- wait for the time specified by the caller
|
||||||
1, -- we only want this thread to wake up once
|
1, -- we only want this thread to wake up once
|
||||||
mt.attached.data.handlers) -- optional arg, to specify our own handlers
|
mt.attached.data.handlers) -- optional arg, to specify our own handlers
|
||||||
|
mt.reg = mt.attached.data.handlers[mt.id]
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt.unregister()
|
||||||
|
local id = mt.id
|
||||||
|
local reg = mt.reg
|
||||||
|
mt.id = nil
|
||||||
|
mt.reg = nil
|
||||||
|
-- before just removing a handler, make sure it is still ours
|
||||||
|
if id and mt.attached and mt.attached.data.handlers[id] == reg then
|
||||||
|
mt.attached.data.handlers[id] = nil
|
||||||
|
return reg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt.coma()
|
||||||
|
mt.unregister() -- we should not wake up again (until resumed)
|
||||||
|
while mt.__status == "suspended" do
|
||||||
|
t.pco.yield_past(t.pco.root, 0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function mt.process.data.pull(_, timeout)
|
function mt.process.data.pull(_, timeout)
|
||||||
|
--[==[
|
||||||
|
yield_past(root) will yield until out of this thread
|
||||||
|
registration puts in a callback to resume this thread
|
||||||
|
|
||||||
|
Subsequent registrations are necessary in case the thread is suspended
|
||||||
|
This thread yields when suspended, entering a coma state
|
||||||
|
-> coma state: yield without registration
|
||||||
|
|
||||||
|
resume will regsiter a wakeup call, breaks coma
|
||||||
|
|
||||||
|
subsequent yields need not specify a timeout because
|
||||||
|
we already legitimately resumed only to find out we had been suspended
|
||||||
|
|
||||||
|
3 places register for wake up
|
||||||
|
1. computer.pullSignal [this path]
|
||||||
|
2. t:attach(proc) will unregister and re-register
|
||||||
|
3. t:resume() of a suspended thread
|
||||||
|
]==]
|
||||||
mt.register(timeout)
|
mt.register(timeout)
|
||||||
-- yield_past(root) will yield until out of this thread
|
local event_data = table.pack(t.pco.yield_past(t.pco.root, timeout))
|
||||||
-- the callback will resume this stack
|
mt.coma()
|
||||||
local event_data
|
|
||||||
repeat
|
|
||||||
event_data = table.pack(t.pco.yield_past(t.pco.root, timeout))
|
|
||||||
-- during sleep, we may have been suspended
|
|
||||||
until t:status() ~= "suspended"
|
|
||||||
return table.unpack(event_data, 1, event_data.n)
|
return table.unpack(event_data, 1, event_data.n)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ tty.window =
|
|||||||
y = 1,
|
y = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
tty.internal = {}
|
tty.stream = {}
|
||||||
|
|
||||||
function tty.key_down_handler(handler, cursor, char, code)
|
function tty.key_down_handler(handler, cursor, char, code)
|
||||||
local data = cursor.data
|
local data = cursor.data
|
||||||
@ -92,7 +92,7 @@ function tty.gpu()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function tty.clear()
|
function tty.clear()
|
||||||
tty.scroll(math.huge)
|
tty.stream.scroll(math.huge)
|
||||||
tty.setCursor(1, 1)
|
tty.setCursor(1, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -101,77 +101,66 @@ function tty.isAvailable()
|
|||||||
return not not (gpu and gpu.getScreen())
|
return not not (gpu and gpu.getScreen())
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty.pull(cursor, timeout, ...)
|
function tty.stream:blink(done)
|
||||||
local blink = tty.getCursorBlink()
|
|
||||||
timeout = timeout or math.huge
|
|
||||||
local blink_timeout = blink and .5 or math.huge
|
|
||||||
local gpu = tty.gpu()
|
|
||||||
local width, height, dx, dy, x, y = tty.getViewport()
|
local width, height, dx, dy, x, y = tty.getViewport()
|
||||||
|
local gpu = tty.gpu()
|
||||||
if gpu then
|
if not gpu or x < 1 or x > width or y < 1 or y > height then
|
||||||
if x < 1 or x > width or y < 1 or y > height then
|
return true
|
||||||
if cursor then
|
|
||||||
cursor:move(0)
|
|
||||||
cursor:scroll()
|
|
||||||
else
|
|
||||||
gpu = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
x, y = tty.getCursor()
|
|
||||||
x, y = x + dx, y + dy
|
|
||||||
end
|
end
|
||||||
|
x, y = x + dx, y + dy
|
||||||
local bgColor, bgIsPalette
|
local blinked, bgColor, bgIsPalette, fgColor, fgIsPalette, char_at_cursor = table.unpack(self.blink_cache or {})
|
||||||
local fgColor, fgIsPalette
|
if done == nil then -- reset
|
||||||
local char_at_cursor
|
blinked = false
|
||||||
if gpu then
|
|
||||||
bgColor, bgIsPalette = gpu.getBackground()
|
bgColor, bgIsPalette = gpu.getBackground()
|
||||||
-- it can happen during a type of race condition when a screen is removed
|
-- it can happen during a type of race condition when a screen is removed
|
||||||
if not bgColor then
|
if not bgColor then
|
||||||
return nil, "interrupted"
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
fgColor, fgIsPalette = gpu.getForeground()
|
fgColor, fgIsPalette = gpu.getForeground()
|
||||||
char_at_cursor = gpu.get(x, y)
|
char_at_cursor = gpu.get(x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not blinked and not done then
|
||||||
|
gpu.setForeground(bgColor, bgIsPalette)
|
||||||
|
gpu.setBackground(fgColor, fgIsPalette)
|
||||||
|
gpu.set(x, y, char_at_cursor)
|
||||||
|
gpu.setForeground(fgColor, fgIsPalette)
|
||||||
|
gpu.setBackground(bgColor, bgIsPalette)
|
||||||
|
blinked = true
|
||||||
|
elseif blinked and (done or tty.window.blink) then
|
||||||
|
gpu.set(x, y, char_at_cursor)
|
||||||
|
blinked = false
|
||||||
|
end
|
||||||
|
|
||||||
|
self.blink_cache = table.pack(blinked, bgColor, bgIsPalette, fgColor, fgIsPalette, char_at_cursor)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function tty.stream:pull(timeout, ...)
|
||||||
|
timeout = timeout or math.huge
|
||||||
|
local blink_timeout = tty.window.blink and .5 or math.huge
|
||||||
|
|
||||||
|
-- it can happen during a type of race condition when a screen is removed
|
||||||
|
if not self:blink() then
|
||||||
|
return nil, "interrupted"
|
||||||
|
end
|
||||||
|
|
||||||
-- get the next event
|
-- get the next event
|
||||||
local blinked = false
|
|
||||||
local done = false
|
|
||||||
local signal
|
|
||||||
while true do
|
while true do
|
||||||
if gpu then
|
local signal = table.pack(event.pull(math.min(blink_timeout, timeout), ...))
|
||||||
if not blinked and not done then
|
|
||||||
gpu.setForeground(bgColor, bgIsPalette)
|
timeout = timeout - blink_timeout
|
||||||
gpu.setBackground(fgColor, fgIsPalette)
|
local done = signal.n > 1 or timeout < blink_timeout
|
||||||
gpu.set(x, y, char_at_cursor)
|
self:blink(done)
|
||||||
gpu.setForeground(fgColor, fgIsPalette)
|
|
||||||
gpu.setBackground(bgColor, bgIsPalette)
|
|
||||||
blinked = true
|
|
||||||
elseif blinked and (done or blink) then
|
|
||||||
gpu.set(x, y, char_at_cursor)
|
|
||||||
blinked = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if done then
|
if done then
|
||||||
return table.unpack(signal, 1, signal.n)
|
return table.unpack(signal, 1, signal.n)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if vt100 ansi codes have anything buffered for read, return that first
|
|
||||||
if tty.window.ansi_response then
|
|
||||||
signal = {"clipboard", tty.keyboard(), tty.window.ansi_response, n=3}
|
|
||||||
tty.window.ansi_response = nil
|
|
||||||
else
|
|
||||||
signal = table.pack(event.pull(math.min(blink_timeout, timeout), ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
timeout = timeout - blink_timeout
|
|
||||||
done = signal.n > 1 or timeout < blink_timeout
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty.internal.split(cursor)
|
function tty.split(cursor)
|
||||||
local data, index = cursor.data, cursor.index
|
local data, index = cursor.data, cursor.index
|
||||||
local dlen = unicode.len(data)
|
local dlen = unicode.len(data)
|
||||||
index = math.max(0, math.min(index, dlen))
|
index = math.max(0, math.min(index, dlen))
|
||||||
@ -179,22 +168,26 @@ function tty.internal.split(cursor)
|
|||||||
return unicode.sub(data, 1, index), tail == 0 and "" or unicode.sub(data, -tail)
|
return unicode.sub(data, 1, index), tail == 0 and "" or unicode.sub(data, -tail)
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty.internal.build_vertical_reader()
|
function tty.build_vertical_reader()
|
||||||
local x, y = tty.getCursor()
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
promptx = x,
|
promptx = tty.window.x,
|
||||||
prompty = y,
|
prompty = tty.window.y,
|
||||||
index = 0,
|
index = 0,
|
||||||
data = "",
|
data = "",
|
||||||
sy = 0,
|
sy = 0,
|
||||||
scroll = function(self)
|
scroll = function(self, goback, prev_x, prev_y)
|
||||||
self.sy = self.sy + tty.scroll()
|
local width, x = tty.window.width, tty.getCursor() - 1
|
||||||
|
tty.setCursor(x % width + 1, tty.window.y + math.floor(x / width))
|
||||||
|
self:draw("")
|
||||||
|
if goback then
|
||||||
|
tty.setCursor(prev_x, prev_y - self.sy)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
move = function(self, n)
|
move = function(self, n)
|
||||||
local win = tty.window
|
local win = tty.window
|
||||||
self.index = math.min(math.max(0, self.index + n), unicode.len(self.data))
|
self.index = math.min(math.max(0, self.index + n), unicode.len(self.data))
|
||||||
local s1, s2 = tty.internal.split(self)
|
local s1, s2 = tty.split(self)
|
||||||
s2 = unicode.sub(s2.." ", 1, 1)
|
s2 = unicode.sub(s2.." ", 1, 1)
|
||||||
local data_remaining = ("_"):rep(self.promptx - 1)..s1..s2
|
local data_remaining = ("_"):rep(self.promptx - 1)..s1..s2
|
||||||
win.y = self.prompty - self.sy
|
win.y = self.prompty - self.sy
|
||||||
@ -221,7 +214,7 @@ function tty.internal.build_vertical_reader()
|
|||||||
tty.gpu().fill(cx + dx, ey + dy, width - cx + 1, 1, " ")
|
tty.gpu().fill(cx + dx, ey + dy, width - cx + 1, 1, " ")
|
||||||
end,
|
end,
|
||||||
update = function(self, arg)
|
update = function(self, arg)
|
||||||
local s1, s2 = tty.internal.split(self)
|
local s1, s2 = tty.split(self)
|
||||||
if type(arg) == "string" then
|
if type(arg) == "string" then
|
||||||
self.data = s1 .. arg .. s2
|
self.data = s1 .. arg .. s2
|
||||||
self.index = self.index + unicode.len(arg)
|
self.index = self.index + unicode.len(arg)
|
||||||
@ -242,11 +235,10 @@ function tty.internal.build_vertical_reader()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- redraw suffix
|
-- redraw suffix
|
||||||
if s2 ~= "" then
|
local prev_x, prev_y = tty.getCursor()
|
||||||
local ps, px, py = self.sy, tty.getCursor()
|
prev_y = prev_y + self.sy -- scroll will remove it
|
||||||
self:draw(s2)
|
self:draw(s2)
|
||||||
tty.setCursor(px, py - (self.sy - ps))
|
self:scroll(s2 ~= "", prev_x, prev_y)
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
clear = function(self)
|
clear = function(self)
|
||||||
self:move(-math.huge)
|
self:move(-math.huge)
|
||||||
@ -256,28 +248,30 @@ function tty.internal.build_vertical_reader()
|
|||||||
self.data = ""
|
self.data = ""
|
||||||
end,
|
end,
|
||||||
draw = function(self, text)
|
draw = function(self, text)
|
||||||
self.sy = self.sy + tty:write(text)
|
self.sy = self.sy + tty.stream:write(text)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PLEASE do not use this method directly, use io.read or tty.read
|
function tty.read(handler)
|
||||||
function tty.read(_, handler, cursor)
|
tty.window.handler = handler
|
||||||
checkArg(1, handler, "table", "number")
|
|
||||||
checkArg(2, cursor, "table", "nil")
|
|
||||||
|
|
||||||
if not io.stdin.tty then
|
local stdin = io.stdin
|
||||||
return io.stdin:readLine(false)
|
local result = table.pack(pcall(stdin.readLine, stdin, false))
|
||||||
end
|
tty.window.handler = nil
|
||||||
|
return select(2, assert(table.unpack(result)))
|
||||||
|
end
|
||||||
|
|
||||||
if type(handler) ~= "table" then
|
-- PLEASE do not use this method directly, use io.read or term.read
|
||||||
handler = {}
|
function tty.stream:read()
|
||||||
end
|
local handler = tty.window.handler or {}
|
||||||
|
local cursor = handler.cursor or tty.build_vertical_reader()
|
||||||
|
|
||||||
|
tty.window.handler = nil
|
||||||
handler.index = 0
|
handler.index = 0
|
||||||
cursor = cursor or tty.internal.build_vertical_reader()
|
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local name, address, char, code = tty.pull(cursor)
|
local name, address, char, code = self:pull()
|
||||||
-- we may have lost tty during the pull
|
-- we may have lost tty during the pull
|
||||||
if not tty.isAvailable() then
|
if not tty.isAvailable() then
|
||||||
return
|
return
|
||||||
@ -288,7 +282,7 @@ function tty.read(_, handler, cursor)
|
|||||||
local main_kb = tty.keyboard()
|
local main_kb = tty.keyboard()
|
||||||
local main_sc = tty.screen()
|
local main_sc = tty.screen()
|
||||||
if name == "interrupted" then
|
if name == "interrupted" then
|
||||||
tty:write("^C\n")
|
self:write("^C\n")
|
||||||
return false
|
return false
|
||||||
elseif address == main_kb or address == main_sc then
|
elseif address == main_kb or address == main_sc then
|
||||||
local handler_method = handler[name] or
|
local handler_method = handler[name] or
|
||||||
@ -321,10 +315,7 @@ function tty.setCursor(x, y)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- PLEASE do not use this method directly, use io.write or term.write
|
-- PLEASE do not use this method directly, use io.write or term.write
|
||||||
function tty.write(_, value)
|
function tty.stream:write(value)
|
||||||
if not io.stdout.tty then
|
|
||||||
return io.write(value)
|
|
||||||
end
|
|
||||||
local gpu = tty.gpu()
|
local gpu = tty.gpu()
|
||||||
if not gpu then
|
if not gpu then
|
||||||
return
|
return
|
||||||
@ -370,7 +361,7 @@ function tty.write(_, value)
|
|||||||
|
|
||||||
-- scroll before parsing next line
|
-- scroll before parsing next line
|
||||||
-- the value may only have been a newline
|
-- the value may only have been a newline
|
||||||
sy = sy + tty.scroll()
|
sy = sy + self.scroll()
|
||||||
-- we may have needed to scroll one last time [nowrap adjustments]
|
-- we may have needed to scroll one last time [nowrap adjustments]
|
||||||
if #value == 0 then
|
if #value == 0 then
|
||||||
break
|
break
|
||||||
@ -386,15 +377,18 @@ function tty.write(_, value)
|
|||||||
local tail = ""
|
local tail = ""
|
||||||
local wlen_needed = unicode.wlen(segment)
|
local wlen_needed = unicode.wlen(segment)
|
||||||
local wlen_remaining = window.width - x + 1
|
local wlen_remaining = window.width - x + 1
|
||||||
if not window.nowrap and wlen_remaining < wlen_needed then
|
if wlen_remaining < wlen_needed then
|
||||||
segment = unicode.wtrunc(segment, wlen_remaining + 1)
|
segment = unicode.wtrunc(segment, wlen_remaining + 1)
|
||||||
wlen_needed = unicode.wlen(segment)
|
local wlen_used = unicode.wlen(segment)
|
||||||
-- we can clear the line because we already know remaining < needed
|
-- we can clear the line because we already know remaining < needed
|
||||||
tail = (" "):rep(wlen_remaining - wlen_needed)
|
tail = (" "):rep(wlen_remaining - wlen_used)
|
||||||
-- we have to reparse the delimeter
|
if not window.nowrap then
|
||||||
ei = #segment
|
-- we have to reparse the delimeter
|
||||||
-- fake a newline
|
ei = #segment
|
||||||
delim = "\n"
|
-- fake a newline
|
||||||
|
delim = "\n"
|
||||||
|
wlen_needed = wlen_used
|
||||||
|
end
|
||||||
end
|
end
|
||||||
gpu.set(gpu_x, gpu_y, segment..tail)
|
gpu.set(gpu_x, gpu_y, segment..tail)
|
||||||
x = x + wlen_needed
|
x = x + wlen_needed
|
||||||
@ -420,14 +414,6 @@ function tty.write(_, value)
|
|||||||
return sy
|
return sy
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty.setCursorBlink(enabled)
|
|
||||||
tty.window.blink = enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
function tty.getCursorBlink()
|
|
||||||
return tty.window.blink
|
|
||||||
end
|
|
||||||
|
|
||||||
local gpu_intercept = {}
|
local gpu_intercept = {}
|
||||||
function tty.bind(gpu)
|
function tty.bind(gpu)
|
||||||
checkArg(1, gpu, "table")
|
checkArg(1, gpu, "table")
|
||||||
@ -446,10 +432,12 @@ function tty.bind(gpu)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
local window = tty.window
|
local window = tty.window
|
||||||
window.gpu = gpu
|
if not window.gpu or window.gpu == gpu then
|
||||||
window.keyboard = nil -- without a keyboard bound, always use the screen's main keyboard (1st)
|
window.gpu = gpu
|
||||||
|
window.keyboard = nil -- without a keyboard bound, always use the screen's main keyboard (1st)
|
||||||
|
tty.getViewport()
|
||||||
|
end
|
||||||
screen_reset(gpu)
|
screen_reset(gpu)
|
||||||
tty.getViewport()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty.keyboard()
|
function tty.keyboard()
|
||||||
@ -492,17 +480,29 @@ function tty.screen()
|
|||||||
return gpu.getScreen()
|
return gpu.getScreen()
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty.scroll(number)
|
function tty.stream.scroll(lines)
|
||||||
local gpu = tty.gpu()
|
local gpu = tty.gpu()
|
||||||
if not gpu then
|
if not gpu then
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
local width, height, dx, dy, x, y = tty.getViewport()
|
local width, height, dx, dy, x, y = tty.getViewport()
|
||||||
|
|
||||||
local lines = number or (y - height)
|
-- nil lines indicates a request to auto scroll
|
||||||
if lines == 0 -- if zero scroll length is requested, do nothing
|
-- auto scroll is when the cursor has gone below the bottom on the terminal
|
||||||
or not number and lines < 0 then -- do not auto scroll back up, only down
|
-- and the text is scroll up, pulling the cursor back into view
|
||||||
return 0
|
|
||||||
|
-- lines<0 scrolls up (text down)
|
||||||
|
-- lines>0 scrolls down (text up)
|
||||||
|
|
||||||
|
-- no lines count given, the user is asking to auto scroll y back into view
|
||||||
|
if not lines then
|
||||||
|
if y < 1 then
|
||||||
|
lines = y - 1 -- y==0 scrolls back -1
|
||||||
|
elseif y > height then
|
||||||
|
lines = y - height -- y==height+1 scroll forward 1
|
||||||
|
else
|
||||||
|
return 0 -- do nothing
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
lines = math.min(lines, height)
|
lines = math.min(lines, height)
|
||||||
@ -516,16 +516,16 @@ function tty.scroll(number)
|
|||||||
gpu.copy(dx + 1, dy + 1 + math.max(0, lines), width, box_height, 0, -lines)
|
gpu.copy(dx + 1, dy + 1 + math.max(0, lines), width, box_height, 0, -lines)
|
||||||
gpu.fill(dx + 1, fill_top, width, abs_lines, ' ')
|
gpu.fill(dx + 1, fill_top, width, abs_lines, ' ')
|
||||||
|
|
||||||
tty.setCursor(x, math.min(y, height))
|
tty.setCursor(x, math.max(1, math.min(y, height)))
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
-- stream methods
|
-- stream methods
|
||||||
local function bfd() return nil, "tty: invalid operation" end
|
local function bfd() return nil, "tty: invalid operation" end
|
||||||
tty.close = bfd
|
tty.stream.close = bfd
|
||||||
tty.seek = bfd
|
tty.stream.seek = bfd
|
||||||
tty.handle = "tty"
|
tty.stream.handle = "tty"
|
||||||
|
|
||||||
require("package").delay(tty, "/lib/core/full_tty.lua")
|
require("package").delay(tty, "/lib/core/full_tty.lua")
|
||||||
|
|
||||||
|
@ -120,7 +120,9 @@ end
|
|||||||
|
|
||||||
-- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ]
|
-- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ]
|
||||||
rules[{"%[", "6", "n"}] = function(window)
|
rules[{"%[", "6", "n"}] = function(window)
|
||||||
window.ansi_response = string.format("%s%d;%dR", string.char(0x1b), window.y, window.x)
|
-- this solution puts the response on stdin, but it isn't echo'd
|
||||||
|
-- I'm personally fine with the lack of echo
|
||||||
|
io.stdin.bufferRead = string.format("%s%s%d;%dR", io.stdin.bufferRead, string.char(0x1b), window.y, window.x)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- D scroll up one line -- moves cursor down
|
-- D scroll up one line -- moves cursor down
|
||||||
@ -137,10 +139,8 @@ rules[{"[DEM]"}] = function(window, dir)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 7 save cursor position and attributes
|
local function save_attributes(window, save)
|
||||||
-- 8 restore cursor position and attributes
|
if save then
|
||||||
rules[{"[78]"}] = function(window, restore)
|
|
||||||
if restore == "8" then
|
|
||||||
local data = window.saved or {1, 1, {0x0}, {0xffffff}}
|
local data = window.saved or {1, 1, {0x0}, {0xffffff}}
|
||||||
window.x = data[1]
|
window.x = data[1]
|
||||||
window.y = data[2]
|
window.y = data[2]
|
||||||
@ -151,6 +151,18 @@ rules[{"[78]"}] = function(window, restore)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- 7 save cursor position and attributes
|
||||||
|
-- 8 restore cursor position and attributes
|
||||||
|
rules[{"[78]"}] = function(window, restore)
|
||||||
|
save_attributes(window, restore == "8")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- s save cursor position
|
||||||
|
-- u restore cursor position
|
||||||
|
rules[{"%[", "[su]"}] = function(window, _, restore)
|
||||||
|
save_attributes(window, restore == "u")
|
||||||
|
end
|
||||||
|
|
||||||
function vt100.parse(window)
|
function vt100.parse(window)
|
||||||
local ansi = window.ansi_escape
|
local ansi = window.ansi_escape
|
||||||
window.ansi_escape = nil
|
window.ansi_escape = nil
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
{ignore=true}
|
@ -73,6 +73,7 @@ Laire # Perry Rhodan
|
|||||||
Loader 1340 # Borderlands 2
|
Loader 1340 # Borderlands 2
|
||||||
LordFokas # Contributor
|
LordFokas # Contributor
|
||||||
Marvin # Hitchhiker's Guide to the Galaxy
|
Marvin # Hitchhiker's Guide to the Galaxy
|
||||||
|
Mawhrin-skel # The Player of Games (Iain M Banks)
|
||||||
Michiyo # Contributor
|
Michiyo # Contributor
|
||||||
*Mute # Analogue: A Hate Story / Hate Plus
|
*Mute # Analogue: A Hate Story / Hate Plus
|
||||||
Mycroft Holmes # The Moon Is a Harsh Mistress
|
Mycroft Holmes # The Moon Is a Harsh Mistress
|
||||||
@ -87,6 +88,7 @@ Replicator # Stargate
|
|||||||
Robby # Forbidden Planet
|
Robby # Forbidden Planet
|
||||||
Roomba # Under your couch... wait.
|
Roomba # Under your couch... wait.
|
||||||
Rosie # The Jetsons
|
Rosie # The Jetsons
|
||||||
|
Skaffen-Amtiskaw # Use of weapons (Iain M Banks)
|
||||||
Shakey # The first general-purpose mobile robot that could reason about its actions.
|
Shakey # The first general-purpose mobile robot that could reason about its actions.
|
||||||
SHODAN # System Shock
|
SHODAN # System Shock
|
||||||
Skynet # Terminator
|
Skynet # Terminator
|
||||||
|
Loading…
x
Reference in New Issue
Block a user