mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-08 06:41:23 -04:00
openos 1.6.8 update
Changes * thread: fix coma state issue where thread would not resume * thread: add thread.current(), returns current thread [ the init thread is still technically not a thread ] * tty: simplify the api with read, and separate all stream methods from the library * sleep: /bin/sleep will now show blinking cursor and can be interrupted * echo: use io.write instead of print. print loads slightly more resources
This commit is contained in:
parent
a1512451fe
commit
fc486d4bd2
@ -1,9 +1,11 @@
|
||||
local args, options = require("shell").parse(...)
|
||||
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
|
||||
-e enable interpretation of backslash escapes
|
||||
--help display this help and exit]])
|
||||
--help display this help and exit
|
||||
]])
|
||||
return
|
||||
end
|
||||
if options.e then
|
||||
@ -16,5 +18,5 @@ if options.e then
|
||||
end
|
||||
io.write(table.concat(args," "))
|
||||
if not options.n then
|
||||
print()
|
||||
io.write("\n")
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ if #args == 0 then
|
||||
end
|
||||
io.write(sh.expand(os.getenv("PS1") or "$ "))
|
||||
end
|
||||
local command = tty:read(input_handler)
|
||||
local command = tty.read(input_handler)
|
||||
if command then
|
||||
command = text.trim(command)
|
||||
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(...)
|
||||
|
||||
if options.help then
|
||||
@ -50,4 +51,9 @@ for _,v in ipairs(args) do
|
||||
total_time = total_time + time_type_multiplier(time_type) * interval
|
||||
end
|
||||
|
||||
os.sleep(total_time)
|
||||
local stdin_stream = io.stdin.stream
|
||||
if stdin_stream.pull then
|
||||
return stdin_stream:pull(nil, total_time, "interrupted")
|
||||
else
|
||||
os.sleep(total_time)
|
||||
end
|
||||
|
@ -1,14 +1,14 @@
|
||||
local buffer = require("buffer")
|
||||
local tty = require("tty")
|
||||
local tty_stream = require("tty").stream
|
||||
|
||||
local core_stdin = buffer.new("r", tty)
|
||||
local core_stdout = buffer.new("w", tty)
|
||||
local core_stdin = buffer.new("r", tty_stream)
|
||||
local core_stdout = buffer.new("w", tty_stream)
|
||||
local core_stderr = buffer.new("w", setmetatable(
|
||||
{
|
||||
write = function(_, str)
|
||||
return tty:write("\27[31m"..str.."\27[37m")
|
||||
return tty_stream:write("\27[31m"..str.."\27[37m")
|
||||
end
|
||||
}, {__index=tty}))
|
||||
}, {__index=tty_stream}))
|
||||
|
||||
core_stdout:setvbuf("no")
|
||||
core_stderr:setvbuf("no")
|
||||
@ -16,9 +16,9 @@ core_stdin.tty = true
|
||||
core_stdout.tty = true
|
||||
core_stderr.tty = true
|
||||
|
||||
core_stdin.close = tty.close
|
||||
core_stdout.close = tty.close
|
||||
core_stderr.close = tty.close
|
||||
core_stdin.close = tty_stream.close
|
||||
core_stdout.close = tty_stream.close
|
||||
core_stderr.close = tty_stream.close
|
||||
|
||||
local io_mt = getmetatable(io) or {}
|
||||
io_mt.__index = function(_, k)
|
||||
|
@ -1,7 +1,7 @@
|
||||
-- called from /init.lua
|
||||
local raw_loadfile = ...
|
||||
|
||||
_G._OSVERSION = "OpenOS 1.6.7"
|
||||
_G._OSVERSION = "OpenOS 1.6.8"
|
||||
|
||||
local component = component
|
||||
local computer = computer
|
||||
|
@ -10,12 +10,11 @@ end
|
||||
|
||||
local env -- forward declare for binding in metamethod
|
||||
env = setmetatable({}, {
|
||||
__index = function(t, k)
|
||||
__index = function(_, k)
|
||||
_ENV[k] = _ENV[k] or optrequire(k)
|
||||
return _ENV[k]
|
||||
end,
|
||||
__pairs = function(self)
|
||||
local t = self
|
||||
__pairs = function(t)
|
||||
return function(_, key)
|
||||
local k, v = next(t, key)
|
||||
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
|
||||
io.write(env._PROMPT)
|
||||
local command = tty:read(read_handler)
|
||||
local command = tty.read(read_handler)
|
||||
if not command then -- eof
|
||||
return
|
||||
end
|
||||
|
@ -70,7 +70,7 @@ end
|
||||
local function build_horizontal_reader(cursor)
|
||||
cursor.clear_tail = function(self)
|
||||
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)
|
||||
tty.gpu().fill(x+dx,y+dy,wlen,1," ")
|
||||
end
|
||||
@ -198,15 +198,16 @@ function term.read(history, dobreak, hint, pwchar, filter)
|
||||
local handler = history
|
||||
handler.hint = handler.hint or hint
|
||||
|
||||
local cursor = tty.internal.build_vertical_reader()
|
||||
local cursor = tty.build_vertical_reader()
|
||||
if handler.nowrap then
|
||||
build_horizontal_reader(cursor)
|
||||
end
|
||||
|
||||
inject_filter(handler, filter)
|
||||
inject_mask(cursor, dobreak, pwchar or history.pwchar)
|
||||
handler.cursor = cursor
|
||||
|
||||
return tty:read(handler, cursor)
|
||||
return tty.read(handler)
|
||||
end
|
||||
|
||||
function term.getGlobalArea(window)
|
||||
@ -228,7 +229,15 @@ function term.pull(...)
|
||||
timeout = table.remove(args, 1)
|
||||
args.n = args.n - 1
|
||||
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
|
||||
|
||||
function term.bind(gpu, window)
|
||||
|
@ -77,17 +77,27 @@ local function get_box_thread_handle(handles, bCreate)
|
||||
end
|
||||
|
||||
function box_thread:resume()
|
||||
if self:status() ~= "suspended" then
|
||||
return nil, "cannot resume " .. self:status() .. " thread"
|
||||
local mt = getmetatable(self)
|
||||
if mt.__status ~= "suspended" then
|
||||
return nil, "cannot resume " .. mt.__status .. " thread"
|
||||
end
|
||||
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
|
||||
getmetatable(self).__status = "running"
|
||||
end
|
||||
|
||||
function box_thread:suspend()
|
||||
if self:status() ~= "running" then
|
||||
return nil, "cannot suspend " .. self:status() .. " thread"
|
||||
local mt = getmetatable(self)
|
||||
if mt.__status ~= "running" then
|
||||
return nil, "cannot suspend " .. mt.__status .. " thread"
|
||||
end
|
||||
mt.__status = "suspended"
|
||||
local pco_status = coroutine.status(self.pco.root)
|
||||
if pco_status == "running" or pco_status == "normal" then
|
||||
mt.coma()
|
||||
end
|
||||
getmetatable(self).__status = "suspended"
|
||||
end
|
||||
|
||||
function box_thread:status()
|
||||
@ -113,29 +123,27 @@ function box_thread:attach(parent)
|
||||
if not proc then return nil, "thread failed to attach, process not found" end
|
||||
if mt.attached == proc then return self end -- already attached
|
||||
|
||||
local waiting_handler
|
||||
if mt.attached then
|
||||
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
|
||||
if h == self then
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- registration happens on the attached proc, unregister before reparenting
|
||||
local waiting_handler = mt.unregister()
|
||||
|
||||
-- attach to parent or the current process
|
||||
mt.attached = proc
|
||||
local handles = proc.data.handles
|
||||
|
||||
-- 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)
|
||||
|
||||
-- register on the new parent
|
||||
if waiting_handler then -- event-waiting
|
||||
mt.register(waiting_handler.timeout - computer.uptime())
|
||||
end
|
||||
@ -143,6 +151,23 @@ function box_thread:attach(parent)
|
||||
return self
|
||||
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, ...)
|
||||
checkArg(1, fp, "function")
|
||||
|
||||
@ -153,8 +178,8 @@ function thread.create(fp, ...)
|
||||
mt.__status = "running"
|
||||
local fp_co = t.pco.create(fp)
|
||||
-- run fp_co until dead
|
||||
-- pullSignal will yield_all past this point
|
||||
-- but yield will return here, we pullSignal from here to yield_all
|
||||
-- pullSignal will yield_past this point
|
||||
-- but yield will return here, we pullSignal from here to yield_past
|
||||
local args = table.pack(...)
|
||||
while true do
|
||||
local result = table.pack(t.pco.resume(fp_co, table.unpack(args, 1, args.n)))
|
||||
@ -188,7 +213,7 @@ function thread.create(fp, ...)
|
||||
|
||||
--special resume to keep track of process death
|
||||
function mt.private_resume(...)
|
||||
mt.id = nil
|
||||
mt.unregister()
|
||||
-- this thread may have been killed
|
||||
if t:status() == "dead" then return end
|
||||
local result = table.pack(t.pco.resume(t.pco.root, ...))
|
||||
@ -209,17 +234,50 @@ function thread.create(fp, ...)
|
||||
timeout, -- wait for the time specified by the caller
|
||||
1, -- we only want this thread to wake up once
|
||||
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
|
||||
|
||||
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)
|
||||
-- yield_past(root) will yield until out of this thread
|
||||
-- the callback will resume this stack
|
||||
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"
|
||||
local event_data = table.pack(t.pco.yield_past(t.pco.root, timeout))
|
||||
mt.coma()
|
||||
return table.unpack(event_data, 1, event_data.n)
|
||||
end
|
||||
|
||||
|
@ -16,7 +16,7 @@ tty.window =
|
||||
y = 1,
|
||||
}
|
||||
|
||||
tty.internal = {}
|
||||
tty.stream = {}
|
||||
|
||||
function tty.key_down_handler(handler, cursor, char, code)
|
||||
local data = cursor.data
|
||||
@ -101,7 +101,7 @@ function tty.isAvailable()
|
||||
return not not (gpu and gpu.getScreen())
|
||||
end
|
||||
|
||||
function tty.pull(cursor, timeout, ...)
|
||||
function tty.stream:pull(cursor, timeout, ...)
|
||||
local blink = tty.getCursorBlink()
|
||||
timeout = timeout or math.huge
|
||||
local blink_timeout = blink and .5 or math.huge
|
||||
@ -171,7 +171,7 @@ function tty.pull(cursor, timeout, ...)
|
||||
end
|
||||
end
|
||||
|
||||
function tty.internal.split(cursor)
|
||||
function tty.split(cursor)
|
||||
local data, index = cursor.data, cursor.index
|
||||
local dlen = unicode.len(data)
|
||||
index = math.max(0, math.min(index, dlen))
|
||||
@ -179,7 +179,7 @@ function tty.internal.split(cursor)
|
||||
return unicode.sub(data, 1, index), tail == 0 and "" or unicode.sub(data, -tail)
|
||||
end
|
||||
|
||||
function tty.internal.build_vertical_reader()
|
||||
function tty.build_vertical_reader()
|
||||
local x, y = tty.getCursor()
|
||||
return
|
||||
{
|
||||
@ -194,7 +194,7 @@ function tty.internal.build_vertical_reader()
|
||||
move = function(self, n)
|
||||
local win = tty.window
|
||||
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)
|
||||
local data_remaining = ("_"):rep(self.promptx - 1)..s1..s2
|
||||
win.y = self.prompty - self.sy
|
||||
@ -221,7 +221,7 @@ function tty.internal.build_vertical_reader()
|
||||
tty.gpu().fill(cx + dx, ey + dy, width - cx + 1, 1, " ")
|
||||
end,
|
||||
update = function(self, arg)
|
||||
local s1, s2 = tty.internal.split(self)
|
||||
local s1, s2 = tty.split(self)
|
||||
if type(arg) == "string" then
|
||||
self.data = s1 .. arg .. s2
|
||||
self.index = self.index + unicode.len(arg)
|
||||
@ -256,28 +256,30 @@ function tty.internal.build_vertical_reader()
|
||||
self.data = ""
|
||||
end,
|
||||
draw = function(self, text)
|
||||
self.sy = self.sy + tty:write(text)
|
||||
self.sy = self.sy + tty.stream:write(text)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
-- PLEASE do not use this method directly, use io.read or tty.read
|
||||
function tty.read(_, handler, cursor)
|
||||
checkArg(1, handler, "table", "number")
|
||||
checkArg(2, cursor, "table", "nil")
|
||||
function tty.read(handler)
|
||||
tty.window.handler = handler
|
||||
|
||||
if not io.stdin.tty then
|
||||
return io.stdin:readLine(false)
|
||||
end
|
||||
local result = table.pack(pcall(io.read))
|
||||
tty.window.handler = nil
|
||||
|
||||
if type(handler) ~= "table" then
|
||||
handler = {}
|
||||
end
|
||||
return select(2, assert(table.unpack(result)))
|
||||
end
|
||||
|
||||
-- PLEASE do not use this method directly, use io.read or term.read
|
||||
function tty.stream:read()
|
||||
local handler = tty.window.handler or {}
|
||||
local cursor = handler.cursor or tty.build_vertical_reader()
|
||||
|
||||
tty.window.handler = nil
|
||||
handler.index = 0
|
||||
cursor = cursor or tty.internal.build_vertical_reader()
|
||||
|
||||
while true do
|
||||
local name, address, char, code = tty.pull(cursor)
|
||||
local name, address, char, code = tty.stream:pull(cursor)
|
||||
-- we may have lost tty during the pull
|
||||
if not tty.isAvailable() then
|
||||
return
|
||||
@ -288,7 +290,7 @@ function tty.read(_, handler, cursor)
|
||||
local main_kb = tty.keyboard()
|
||||
local main_sc = tty.screen()
|
||||
if name == "interrupted" then
|
||||
tty:write("^C\n")
|
||||
tty.stream:write("^C\n")
|
||||
return false
|
||||
elseif address == main_kb or address == main_sc then
|
||||
local handler_method = handler[name] or
|
||||
@ -321,10 +323,7 @@ function tty.setCursor(x, y)
|
||||
end
|
||||
|
||||
-- PLEASE do not use this method directly, use io.write or term.write
|
||||
function tty.write(_, value)
|
||||
if not io.stdout.tty then
|
||||
return io.write(value)
|
||||
end
|
||||
function tty.stream:write(value)
|
||||
local gpu = tty.gpu()
|
||||
if not gpu then
|
||||
return
|
||||
@ -446,10 +445,12 @@ function tty.bind(gpu)
|
||||
end
|
||||
end
|
||||
local window = tty.window
|
||||
window.gpu = gpu
|
||||
window.keyboard = nil -- without a keyboard bound, always use the screen's main keyboard (1st)
|
||||
if not window.gpu or window.gpu == gpu then
|
||||
window.gpu = gpu
|
||||
window.keyboard = nil -- without a keyboard bound, always use the screen's main keyboard (1st)
|
||||
tty.getViewport()
|
||||
end
|
||||
screen_reset(gpu)
|
||||
tty.getViewport()
|
||||
end
|
||||
|
||||
function tty.keyboard()
|
||||
@ -492,17 +493,29 @@ function tty.screen()
|
||||
return gpu.getScreen()
|
||||
end
|
||||
|
||||
function tty.scroll(number)
|
||||
function tty.scroll(lines)
|
||||
local gpu = tty.gpu()
|
||||
if not gpu then
|
||||
return 0
|
||||
end
|
||||
local width, height, dx, dy, x, y = tty.getViewport()
|
||||
|
||||
local lines = number or (y - height)
|
||||
if lines == 0 -- if zero scroll length is requested, do nothing
|
||||
or not number and lines < 0 then -- do not auto scroll back up, only down
|
||||
return 0
|
||||
-- nil lines indicates a request to auto scroll
|
||||
-- auto scroll is when the cursor has gone below the bottom on the terminal
|
||||
-- and the text is scroll up, pulling the cursor back into view
|
||||
|
||||
-- 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
|
||||
|
||||
lines = math.min(lines, height)
|
||||
@ -516,16 +529,16 @@ function tty.scroll(number)
|
||||
gpu.copy(dx + 1, dy + 1 + math.max(0, lines), width, box_height, 0, -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
|
||||
end
|
||||
|
||||
-- stream methods
|
||||
local function bfd() return nil, "tty: invalid operation" end
|
||||
tty.close = bfd
|
||||
tty.seek = bfd
|
||||
tty.handle = "tty"
|
||||
tty.stream.close = bfd
|
||||
tty.stream.seek = bfd
|
||||
tty.stream.handle = "tty"
|
||||
|
||||
require("package").delay(tty, "/lib/core/full_tty.lua")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user