mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-12 08:50:04 -04:00
saving more memory and improving the vt100 library
move all vt100 code to vt100 library delay load event rare code fix shell parse for %d>&%d not followed by whitespace remove weird tty blink code and use vt100 codes bump openos patch version
This commit is contained in:
parent
5b0c085c2d
commit
7ec85999da
@ -1,35 +1,38 @@
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local text = require("text")
|
||||
local unicode = require("unicode")
|
||||
local tty = require("tty")
|
||||
|
||||
if not component.isAvailable("gpu") then
|
||||
return
|
||||
end
|
||||
local lines = {_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)"}
|
||||
local maxWidth = unicode.len(lines[1])
|
||||
|
||||
local f = io.open("/usr/misc/greetings.txt")
|
||||
local lines = {_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)"}
|
||||
local greeting = ""
|
||||
if f then
|
||||
local greetings = {}
|
||||
pcall(function()
|
||||
for line in f:lines() do table.insert(greetings, line) end
|
||||
end)
|
||||
f:close()
|
||||
local greeting = greetings[math.random(1, #greetings)]
|
||||
if greeting then
|
||||
local width = math.max(10, tty.getViewport())
|
||||
for line in text.wrappedLines(greeting, width - 4, width - 4) do
|
||||
table.insert(lines, line)
|
||||
maxWidth = math.max(maxWidth, unicode.len(line))
|
||||
end
|
||||
end
|
||||
greeting = greetings[math.random(1, math.max(#greetings, 1))] or ""
|
||||
end
|
||||
local width = math.min(#greeting, tty.getViewport() - 5)
|
||||
local maxLine = #lines[1]
|
||||
while #greeting > 0 do
|
||||
local si, ei = greeting:sub(1, width):find("%s%S*$")
|
||||
local line = #greeting <= width and greeting or greeting:sub(1, si or width)
|
||||
lines[#lines + 1] = line
|
||||
maxLine = math.max(maxLine, #line)
|
||||
greeting = greeting:sub(#line + 1)
|
||||
end
|
||||
|
||||
local borders = {{unicode.char(0x2552), unicode.char(0x2550), unicode.char(0x2555)},
|
||||
{unicode.char(0x2502), nil, unicode.char(0x2502)},
|
||||
{unicode.char(0x2514), unicode.char(0x2500), unicode.char(0x2518)}}
|
||||
io.write(borders[1][1] .. string.rep(borders[1][2], maxWidth + 2) .. borders[1][3] .. "\n")
|
||||
for _, line in ipairs(lines) do
|
||||
io.write(borders[2][1] .. " " .. text.padRight(line, maxWidth) .. " " .. borders[2][3] .. "\n")
|
||||
io.write(borders[1][1], string.rep(borders[1][2], maxLine + 2), borders[1][3], "\n")
|
||||
for _,line in ipairs(lines) do
|
||||
io.write(borders[2][1], " ", line, (" "):rep(maxLine - #line + 1), borders[2][3], " \n")
|
||||
end
|
||||
io.write(borders[3][1] .. string.rep(borders[3][2], maxWidth + 2) .. borders[3][3] .. "\n")
|
||||
io.write(borders[3][1] .. string.rep(borders[3][2], maxLine + 2) .. borders[3][3] .. "\n")
|
||||
|
@ -39,14 +39,8 @@ function buffer:flush()
|
||||
local tmp = self.bufferWrite
|
||||
self.bufferWrite = ""
|
||||
local result, reason = self.stream:write(tmp)
|
||||
if result then
|
||||
self.bufferWrite = ""
|
||||
else
|
||||
if reason then
|
||||
return nil, reason
|
||||
else
|
||||
return nil, "bad file descriptor"
|
||||
end
|
||||
if not result then
|
||||
return nil, reason or "bad file descriptor"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
-- called from /init.lua
|
||||
local raw_loadfile = ...
|
||||
|
||||
_G._OSVERSION = "OpenOS 1.7.0"
|
||||
_G._OSVERSION = "OpenOS 1.7.1"
|
||||
|
||||
local component = component
|
||||
local computer = computer
|
||||
|
@ -0,0 +1,61 @@
|
||||
local event = require("event")
|
||||
|
||||
local function createMultipleFilter(...)
|
||||
local filter = table.pack(...)
|
||||
if filter.n == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
return function(...)
|
||||
local signal = table.pack(...)
|
||||
if type(signal[1]) ~= "string" then
|
||||
return false
|
||||
end
|
||||
for i = 1, filter.n do
|
||||
if filter[i] ~= nil and signal[1]:match(filter[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function event.pullMultiple(...)
|
||||
local seconds
|
||||
local args
|
||||
if type(...) == "number" then
|
||||
seconds = ...
|
||||
args = table.pack(select(2,...))
|
||||
for i=1,args.n do
|
||||
checkArg(i+1, args[i], "string", "nil")
|
||||
end
|
||||
else
|
||||
args = table.pack(...)
|
||||
for i=1,args.n do
|
||||
checkArg(i, args[i], "string", "nil")
|
||||
end
|
||||
end
|
||||
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
|
||||
end
|
||||
|
||||
function event.cancel(timerId)
|
||||
checkArg(1, timerId, "number")
|
||||
if handlers[timerId] then
|
||||
handlers[timerId] = nil
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function event.ignore(name, callback)
|
||||
checkArg(1, name, "string")
|
||||
checkArg(2, callback, "function")
|
||||
for id, handler in pairs(handlers) do
|
||||
if handler.key == name and handler.callback == callback then
|
||||
handlers[id] = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
@ -244,3 +244,43 @@ function text.padLeft(value, length)
|
||||
return string.rep(" ", length - unicode.wlen(value)) .. value
|
||||
end
|
||||
end
|
||||
|
||||
function text.padRight(value, length)
|
||||
checkArg(1, value, "string", "nil")
|
||||
checkArg(2, length, "number")
|
||||
if not value or unicode.wlen(value) == 0 then
|
||||
return string.rep(" ", length)
|
||||
else
|
||||
return value .. string.rep(" ", length - unicode.wlen(value))
|
||||
end
|
||||
end
|
||||
|
||||
function text.wrap(value, width, maxWidth)
|
||||
checkArg(1, value, "string")
|
||||
checkArg(2, width, "number")
|
||||
checkArg(3, maxWidth, "number")
|
||||
local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline
|
||||
if unicode.wlen(line) > width then -- do we even need to wrap?
|
||||
local partial = unicode.wtrunc(line, width)
|
||||
local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])")
|
||||
if wrapped or unicode.wlen(line) > maxWidth then
|
||||
partial = wrapped or partial
|
||||
return partial, unicode.sub(value, unicode.len(partial) + 1), true
|
||||
else
|
||||
return "", value, true -- write in new line.
|
||||
end
|
||||
end
|
||||
local start = unicode.len(line) + unicode.len(nl) + 1
|
||||
return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0
|
||||
end
|
||||
|
||||
function text.wrappedLines(value, width, maxWidth)
|
||||
local line
|
||||
return function()
|
||||
if value then
|
||||
line, value = text.wrap(value, width, maxWidth)
|
||||
return line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -0,0 +1,93 @@
|
||||
local vt100 = require("vt100")
|
||||
|
||||
local rules = vt100.rules
|
||||
|
||||
-- [?7[hl] wrap mode
|
||||
rules[{"%[", "%?", "7", "[hl]"}] = function(window, _, _, _, nowrap)
|
||||
window.nowrap = nowrap == "l"
|
||||
end
|
||||
|
||||
-- helper scroll function
|
||||
local function set_cursor(window, x, y)
|
||||
window.x = math.min(math.max(x, 1), window.width)
|
||||
window.y = math.min(math.max(y, 1), window.height)
|
||||
end
|
||||
|
||||
-- -- These DO NOT SCROLL
|
||||
-- [(%d+)A move cursor up n lines
|
||||
-- [(%d+)B move cursor down n lines
|
||||
-- [(%d+)C move cursor right n lines
|
||||
-- [(%d+)D move cursor left n lines
|
||||
rules[{"%[", "%d+", "[ABCD]"}] = function(window, _, n, dir)
|
||||
local dx, dy = 0, 0
|
||||
n = tonumber(n)
|
||||
if dir == "A" then
|
||||
dy = -n
|
||||
elseif dir == "B" then
|
||||
dy = n
|
||||
elseif dir == "C" then
|
||||
dx = n
|
||||
else -- D
|
||||
dx = -n
|
||||
end
|
||||
set_cursor(window, window.x + dx, window.y + dy)
|
||||
end
|
||||
|
||||
-- [Line;ColumnH Move cursor to screen location v,h
|
||||
-- [Line;Columnf ^ same
|
||||
rules[{"%[", "%d+", ";", "%d+", "[Hf]"}] = function(window, _, y, _, x)
|
||||
set_cursor(window, tonumber(x), tonumber(y))
|
||||
end
|
||||
|
||||
-- [K clear line from cursor right
|
||||
-- [0K ^ same
|
||||
-- [1K clear line from cursor left
|
||||
-- [2K clear entire line
|
||||
local function clear_line(window, _, n)
|
||||
n = tonumber(n) or 0
|
||||
local x = n == 0 and window.x or 1
|
||||
local rep = n == 1 and window.x or window.width
|
||||
window.gpu.set(x, window.y, (" "):rep(rep))
|
||||
end
|
||||
rules[{"%[", "[012]?", "K"}] = clear_line
|
||||
|
||||
-- [J clear screen from cursor down
|
||||
-- [0J ^ same
|
||||
-- [1J clear screen from cursor up
|
||||
-- [2J clear entire screen
|
||||
rules[{"%[", "[012]?", "J"}] = function(window, _, n)
|
||||
clear_line(window, _, n)
|
||||
n = tonumber(n) or 0
|
||||
local y = n == 0 and (window.y + 1) or 1
|
||||
local rep = n == 1 and (window.y - 1) or window.height
|
||||
window.gpu.fill(1, y, window.width, rep, " ")
|
||||
end
|
||||
|
||||
-- [H move cursor to upper left corner
|
||||
-- [;H ^ same
|
||||
-- [f ^ same
|
||||
-- [;f ^ same
|
||||
rules[{"%[;?", "[Hf]"}] = function(window)
|
||||
set_cursor(window, 1, 1)
|
||||
end
|
||||
|
||||
-- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ]
|
||||
rules[{"%[", "6", "n"}] = function(window)
|
||||
-- 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
|
||||
|
||||
-- D scroll up one line -- moves cursor down
|
||||
-- E move to next line (acts the same ^, but x=1)
|
||||
-- M scroll down one line -- moves cursor up
|
||||
rules[{"[DEM]"}] = function(window, dir)
|
||||
if dir == "D" then
|
||||
window.y = window.y + 1
|
||||
elseif dir == "E" then
|
||||
window.y = window.y + 1
|
||||
window.x = 1
|
||||
else -- M
|
||||
window.y = window.y - 1
|
||||
end
|
||||
end
|
@ -89,48 +89,8 @@ local function createPlainFilter(name, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local function createMultipleFilter(...)
|
||||
local filter = table.pack(...)
|
||||
if filter.n == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
return function(...)
|
||||
local signal = table.pack(...)
|
||||
if type(signal[1]) ~= "string" then
|
||||
return false
|
||||
end
|
||||
for i = 1, filter.n do
|
||||
if filter[i] ~= nil and signal[1]:match(filter[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function event.cancel(timerId)
|
||||
checkArg(1, timerId, "number")
|
||||
if handlers[timerId] then
|
||||
handlers[timerId] = nil
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function event.ignore(name, callback)
|
||||
checkArg(1, name, "string")
|
||||
checkArg(2, callback, "function")
|
||||
for id, handler in pairs(handlers) do
|
||||
if handler.key == name and handler.callback == callback then
|
||||
handlers[id] = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function event.listen(name, callback)
|
||||
checkArg(1, name, "string")
|
||||
checkArg(2, callback, "function")
|
||||
@ -161,24 +121,6 @@ function event.pull(...)
|
||||
end
|
||||
end
|
||||
|
||||
function event.pullMultiple(...)
|
||||
local seconds
|
||||
local args
|
||||
if type(...) == "number" then
|
||||
seconds = ...
|
||||
args = table.pack(select(2,...))
|
||||
for i=1,args.n do
|
||||
checkArg(i+1, args[i], "string", "nil")
|
||||
end
|
||||
else
|
||||
args = table.pack(...)
|
||||
for i=1,args.n do
|
||||
checkArg(i, args[i], "string", "nil")
|
||||
end
|
||||
end
|
||||
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
|
||||
end
|
||||
|
||||
function event.pullFiltered(...)
|
||||
local args = table.pack(...)
|
||||
local seconds, filter
|
||||
@ -217,6 +159,8 @@ end
|
||||
-- users may expect to find event.push to exist
|
||||
event.push = computer.pushSignal
|
||||
|
||||
require("package").delay(event, "/lib/core/full_event.lua")
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
return event
|
||||
|
@ -4,53 +4,13 @@ local tx = require("transforms")
|
||||
local text = {}
|
||||
text.internal = {}
|
||||
|
||||
text.syntax = {"^%d?>>?&%d+$","^%d?>>?",">>?","<%&%d+","<",";","&&","||?"}
|
||||
text.syntax = {"^%d?>>?&%d+","^%d?>>?",">>?","<%&%d+","<",";","&&","||?"}
|
||||
|
||||
function text.trim(value) -- from http://lua-users.org/wiki/StringTrim
|
||||
local from = string.match(value, "^%s*()")
|
||||
return from > #value and "" or string.match(value, ".*%S", from)
|
||||
end
|
||||
|
||||
-- used in motd
|
||||
function text.padRight(value, length)
|
||||
checkArg(1, value, "string", "nil")
|
||||
checkArg(2, length, "number")
|
||||
if not value or unicode.wlen(value) == 0 then
|
||||
return string.rep(" ", length)
|
||||
else
|
||||
return value .. string.rep(" ", length - unicode.wlen(value))
|
||||
end
|
||||
end
|
||||
|
||||
function text.wrap(value, width, maxWidth)
|
||||
checkArg(1, value, "string")
|
||||
checkArg(2, width, "number")
|
||||
checkArg(3, maxWidth, "number")
|
||||
local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline
|
||||
if unicode.wlen(line) > width then -- do we even need to wrap?
|
||||
local partial = unicode.wtrunc(line, width)
|
||||
local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])")
|
||||
if wrapped or unicode.wlen(line) > maxWidth then
|
||||
partial = wrapped or partial
|
||||
return partial, unicode.sub(value, unicode.len(partial) + 1), true
|
||||
else
|
||||
return "", value, true -- write in new line.
|
||||
end
|
||||
end
|
||||
local start = unicode.len(line) + unicode.len(nl) + 1
|
||||
return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0
|
||||
end
|
||||
|
||||
function text.wrappedLines(value, width, maxWidth)
|
||||
local line
|
||||
return function()
|
||||
if value then
|
||||
line, value = text.wrap(value, width, maxWidth)
|
||||
return line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- used by lib/sh
|
||||
function text.escapeMagic(txt)
|
||||
return txt:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1')
|
||||
|
@ -101,49 +101,23 @@ function tty.isAvailable()
|
||||
return not not (gpu and gpu.getScreen())
|
||||
end
|
||||
|
||||
function tty.stream:blink(done)
|
||||
local width, height, dx, dy, x, y = tty.getViewport()
|
||||
local gpu = tty.gpu()
|
||||
if not gpu or x < 1 or x > width or y < 1 or y > height then
|
||||
return true
|
||||
end
|
||||
x, y = x + dx, y + dy
|
||||
local blinked, bgColor, bgIsPalette, fgColor, fgIsPalette, char_at_cursor = table.unpack(self.blink_cache or {})
|
||||
if done == nil then -- reset
|
||||
blinked = false
|
||||
bgColor, bgIsPalette = gpu.getBackground()
|
||||
-- it can happen during a type of race condition when a screen is removed
|
||||
if not bgColor then
|
||||
return
|
||||
end
|
||||
|
||||
fgColor, fgIsPalette = gpu.getForeground()
|
||||
char_at_cursor = gpu.get(x, y)
|
||||
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"
|
||||
local width, height, dx, dy, x, y = tty.getViewport()
|
||||
local gpu = tty.gpu()
|
||||
if x < 1 or x > width or y < 1 or y > height then
|
||||
gpu = nil
|
||||
end
|
||||
local char_at_cursor
|
||||
local blinked
|
||||
if gpu then
|
||||
blinked, char_at_cursor = pcall(gpu.get, x + dx, y + dy)
|
||||
if not blinked then
|
||||
return nil, "interrupted"
|
||||
end
|
||||
io.write("\0277\27[7m", char_at_cursor, "\0278")
|
||||
end
|
||||
|
||||
-- get the next event
|
||||
@ -152,7 +126,15 @@ function tty.stream:pull(timeout, ...)
|
||||
|
||||
timeout = timeout - blink_timeout
|
||||
local done = signal.n > 1 or timeout < blink_timeout
|
||||
self:blink(done)
|
||||
if gpu then
|
||||
if not blinked and not done then
|
||||
io.write("\0277\27[7m", char_at_cursor, "\0278")
|
||||
blinked = true
|
||||
elseif blinked and (done or tty.window.blink) then
|
||||
io.write("\0277", char_at_cursor, "\0278")
|
||||
blinked = false
|
||||
end
|
||||
end
|
||||
|
||||
if done then
|
||||
return table.unpack(signal, 1, signal.n)
|
||||
@ -336,27 +318,7 @@ function tty.stream:write(value)
|
||||
-- parse the instruction in segment
|
||||
-- [ (%d+;)+ %d+m
|
||||
window.ansi_escape = window.ansi_escape .. value
|
||||
local color_attributes = {tonumber(window.ansi_escape:match("^%[(%d%d)m"))}
|
||||
if not color_attributes[1] then
|
||||
color_attributes, ansi_print, value = require("vt100").parse(window)
|
||||
else
|
||||
value = window.ansi_escape:sub(5)
|
||||
end
|
||||
for _,catt in ipairs(color_attributes) do
|
||||
-- B6 is closer to cyan in 4 bit color
|
||||
local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00B6ff,0xffffff}
|
||||
catt = catt - 29
|
||||
local method = "setForeground"
|
||||
if catt > 10 then
|
||||
method = "setBackground"
|
||||
catt = catt - 10
|
||||
end
|
||||
local c = colors[catt]
|
||||
if c then
|
||||
gpu[method](c)
|
||||
end
|
||||
window.ansi_escape = nil -- might happen multiple times, that's fine
|
||||
end
|
||||
value, ansi_print = require("vt100").parse(window)
|
||||
end
|
||||
|
||||
-- scroll before parsing next line
|
||||
|
@ -1,190 +1,129 @@
|
||||
local text = require("text")
|
||||
local vt100 = {}
|
||||
|
||||
-- runs patterns on ansi until failure
|
||||
-- returns valid:boolean, completed_index:nil|number
|
||||
function vt100.validate(ansi, patterns)
|
||||
local last_index = 0
|
||||
local captures = {}
|
||||
for _,pattern in ipairs(patterns) do
|
||||
if last_index >= #ansi then
|
||||
return true
|
||||
end
|
||||
local si, ei, capture = ansi:find("^(" .. pattern .. ")", last_index + 1)
|
||||
if not si then -- failed to match
|
||||
return
|
||||
end
|
||||
captures[#captures + 1] = capture
|
||||
last_index = ei
|
||||
end
|
||||
return true, last_index, captures
|
||||
end
|
||||
|
||||
local rules = {}
|
||||
local vt100 = {rules=rules}
|
||||
local full
|
||||
|
||||
-- colors
|
||||
-- colors, blinking, and reverse
|
||||
-- [%d+;%d+;..%d+m
|
||||
rules[{"%[", "[%d;]*", "m"}] = function(_, _, number_text)
|
||||
local numbers = {}
|
||||
-- cost: 2,250
|
||||
rules[{"%[", "[%d;]*", "m"}] = function(window, _, number_text)
|
||||
-- prefix and suffix ; act as reset
|
||||
-- e.g. \27[41;m is actually 41 followed by a reset
|
||||
number_text = ";" .. number_text:gsub("^;$","") .. ";"
|
||||
local parts = text.split(number_text, {";"})
|
||||
local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00B6ff,0xffffff}
|
||||
local fg, bg = window.gpu.setForeground, window.gpu.setBackground
|
||||
if window.flip then
|
||||
fg, bg = bg, fg
|
||||
end
|
||||
number_text = " _ " .. number_text:gsub("^;$", ""):gsub(";", " _ ") .. " _ "
|
||||
local parts = text.internal.tokenize(number_text)
|
||||
local last_was_break
|
||||
for _,part in ipairs(parts) do
|
||||
local num = tonumber(part)
|
||||
if not num then
|
||||
num = last_was_break and 0
|
||||
last_was_break = true
|
||||
else
|
||||
last_was_break = false
|
||||
end
|
||||
if num == 0 then
|
||||
numbers[#numbers + 1] = 40
|
||||
numbers[#numbers + 1] = 37
|
||||
local num = tonumber(part[1].txt)
|
||||
last_was_break, num = not num, num or last_was_break and 0
|
||||
|
||||
if num == 7 then
|
||||
if not window.flip then
|
||||
fg(bg(window.gpu.getForeground()))
|
||||
fg, bg = bg, fg
|
||||
end
|
||||
window.flip = true
|
||||
elseif num == 5 then
|
||||
window.blink = true
|
||||
elseif num == 0 then
|
||||
bg(colors[1])
|
||||
fg(colors[8])
|
||||
elseif num then
|
||||
numbers[#numbers + 1] = num
|
||||
num = num - 29
|
||||
local set = fg
|
||||
if num > 10 then
|
||||
num = num - 10
|
||||
set = bg
|
||||
end
|
||||
local color = colors[num]
|
||||
if color then
|
||||
set(color)
|
||||
end
|
||||
end
|
||||
end
|
||||
return numbers
|
||||
end
|
||||
|
||||
-- [?7[hl] wrap mode
|
||||
rules[{"%[", "%?", "7", "[hl]"}] = function(window, _, _, _, nowrap)
|
||||
window.nowrap = nowrap == "l"
|
||||
end
|
||||
|
||||
-- helper scroll function
|
||||
local function set_cursor(window, x, y)
|
||||
window.x = math.min(math.max(x, 1), window.width)
|
||||
window.y = math.min(math.max(y, 1), window.height)
|
||||
end
|
||||
|
||||
-- -- These DO NOT SCROLL
|
||||
-- [(%d+)A move cursor up n lines
|
||||
-- [(%d+)B move cursor down n lines
|
||||
-- [(%d+)C move cursor right n lines
|
||||
-- [(%d+)D move cursor left n lines
|
||||
rules[{"%[", "%d+", "[ABCD]"}] = function(window, _, n, dir)
|
||||
local dx, dy = 0, 0
|
||||
n = tonumber(n)
|
||||
if dir == "A" then
|
||||
dy = -n
|
||||
elseif dir == "B" then
|
||||
dy = n
|
||||
elseif dir == "C" then
|
||||
dx = n
|
||||
else -- D
|
||||
dx = -n
|
||||
end
|
||||
set_cursor(window, window.x + dx, window.y + dy)
|
||||
end
|
||||
|
||||
-- [Line;ColumnH Move cursor to screen location v,h
|
||||
-- [Line;Columnf ^ same
|
||||
rules[{"%[", "%d+", ";", "%d+", "[Hf]"}] = function(window, _, y, _, x)
|
||||
set_cursor(window, tonumber(x), tonumber(y))
|
||||
end
|
||||
|
||||
-- [K clear line from cursor right
|
||||
-- [0K ^ same
|
||||
-- [1K clear line from cursor left
|
||||
-- [2K clear entire line
|
||||
local function clear_line(window, _, n)
|
||||
n = tonumber(n) or 0
|
||||
local x = n == 0 and window.x or 1
|
||||
local rep = n == 1 and window.x or window.width
|
||||
window.gpu.set(x, window.y, (" "):rep(rep))
|
||||
end
|
||||
rules[{"%[", "[012]?", "K"}] = clear_line
|
||||
|
||||
-- [J clear screen from cursor down
|
||||
-- [0J ^ same
|
||||
-- [1J clear screen from cursor up
|
||||
-- [2J clear entire screen
|
||||
rules[{"%[", "[012]?", "J"}] = function(window, _, n)
|
||||
clear_line(window, _, n)
|
||||
n = tonumber(n) or 0
|
||||
local y = n == 0 and (window.y + 1) or 1
|
||||
local rep = n == 1 and (window.y - 1) or window.height
|
||||
window.gpu.fill(1, y, window.width, rep, " ")
|
||||
end
|
||||
|
||||
-- [H move cursor to upper left corner
|
||||
-- [;H ^ same
|
||||
-- [f ^ same
|
||||
-- [;f ^ same
|
||||
rules[{"%[;?", "[Hf]"}] = function(window)
|
||||
set_cursor(window, 1, 1)
|
||||
end
|
||||
|
||||
-- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ]
|
||||
rules[{"%[", "6", "n"}] = function(window)
|
||||
-- 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
|
||||
|
||||
-- D scroll up one line -- moves cursor down
|
||||
-- E move to next line (acts the same ^, but x=1)
|
||||
-- M scroll down one line -- moves cursor up
|
||||
rules[{"[DEM]"}] = function(window, dir)
|
||||
if dir == "D" then
|
||||
window.y = window.y + 1
|
||||
elseif dir == "E" then
|
||||
window.y = window.y + 1
|
||||
window.x = 1
|
||||
else -- M
|
||||
window.y = window.y - 1
|
||||
end
|
||||
end
|
||||
|
||||
local function save_attributes(window, save)
|
||||
if save then
|
||||
local data = window.saved or {1, 1, {0x0}, {0xffffff}}
|
||||
local function save_attributes(window, seven, s)
|
||||
if seven == "7" or s == "s" then
|
||||
window.saved =
|
||||
{
|
||||
window.x,
|
||||
window.y,
|
||||
{window.gpu.getBackground()},
|
||||
{window.gpu.getForeground()},
|
||||
window.flip,
|
||||
window.blink
|
||||
}
|
||||
else
|
||||
local data = window.saved or {1, 1, {0x0}, {0xffffff}, window.flip, window.blink}
|
||||
window.x = data[1]
|
||||
window.y = data[2]
|
||||
window.gpu.setBackground(table.unpack(data[3]))
|
||||
window.gpu.setForeground(table.unpack(data[4]))
|
||||
else
|
||||
window.saved = {window.x, window.y, {window.gpu.getBackground()}, {window.gpu.getForeground()}}
|
||||
window.flip = data[5]
|
||||
window.blink = data[6]
|
||||
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
|
||||
rules[{"[78]"}] = save_attributes
|
||||
|
||||
-- s save cursor position
|
||||
-- u restore cursor position
|
||||
rules[{"%[", "[su]"}] = function(window, _, restore)
|
||||
save_attributes(window, restore == "u")
|
||||
end
|
||||
rules[{"%[", "[su]"}] = save_attributes
|
||||
|
||||
-- returns 2 values
|
||||
-- value: parsed text
|
||||
-- ansi_print: failed to parse
|
||||
function vt100.parse(window)
|
||||
local ansi = window.ansi_escape
|
||||
window.ansi_escape = nil
|
||||
local any_valid
|
||||
|
||||
for rule,action in pairs(rules) do
|
||||
local ok, completed, captures = vt100.validate(ansi, rule)
|
||||
if completed then
|
||||
return action(window, table.unpack(captures)) or {}, "", ansi:sub(completed + 1)
|
||||
elseif ok then
|
||||
any_valid = true
|
||||
local last_index = 0
|
||||
local captures = {}
|
||||
for _,pattern in ipairs(rule) do
|
||||
if last_index >= #ansi then
|
||||
any_valid = true
|
||||
break
|
||||
end
|
||||
local si, ei, capture = ansi:find("^(" .. pattern .. ")", last_index + 1)
|
||||
if not si then
|
||||
break
|
||||
end
|
||||
captures[#captures + 1] = capture
|
||||
last_index = ei
|
||||
end
|
||||
|
||||
if #captures == #rule then
|
||||
action(window, table.unpack(captures))
|
||||
return ansi:sub(last_index + 1), ""
|
||||
end
|
||||
end
|
||||
|
||||
if not full then
|
||||
-- maybe it did satisfy a rule, load more rules
|
||||
full = true
|
||||
dofile("/lib/core/full_vt.lua")
|
||||
window.ansi_escape = ansi
|
||||
return vt100.parse(window)
|
||||
end
|
||||
|
||||
if not any_valid then
|
||||
-- malformed
|
||||
return {}, string.char(0x1b), ansi
|
||||
return ansi, "\27"
|
||||
end
|
||||
|
||||
-- else, still consuming
|
||||
window.ansi_escape = ansi
|
||||
return {}, "", ""
|
||||
return "", ""
|
||||
end
|
||||
|
||||
return vt100
|
||||
|
Loading…
x
Reference in New Issue
Block a user