diff --git a/src/main/resources/assets/opencomputers/loot/openos/etc/motd b/src/main/resources/assets/opencomputers/loot/openos/etc/motd index 85ec4759f..da961297b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/etc/motd +++ b/src/main/resources/assets/opencomputers/loot/openos/etc/motd @@ -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") diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua index 384901d5c..2d128df54 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua index 8f8bcd536..351582e29 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua new file mode 100644 index 000000000..396ebbc9a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua @@ -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 + diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua index 6fdca240a..dc7f2eb4d 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua @@ -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 + diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua new file mode 100644 index 000000000..a69b4b6a9 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua index 2a41f0a80..52ad8761b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua index 7c77f4041..5af1187c9 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua @@ -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') diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua index 820585a1f..c7ad2093c 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua index fc06ad8f6..fce23034d 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua @@ -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