|
- local vtansi = {}
- local keyboardIgnore = {}
- vtansi.activeBuffers = {}
- vtansi.sequences = {
- [28] = "\n", -- newline
- [200] = "\27[A", -- up
- [203] = "\27[D", -- left
- [205] = "\27[C", -- right
- [208] = "\27[B", -- down
- [201] = "\27[5~", -- page up
- [209] = "\27[6~" -- page down
- }
- vtansi.keys = {}
- vtansi.keys[0x38] = "lalt"
- vtansi.keys[0xB8] = "ralt"
- function vtansi.saveToBuffer(gpu,idx)
- gpu.bitblt(idx, nil, nil, nil, nil, 0)
- end
- function vtansi.loadFromBuffer(gpu,idx)
- gpu.bitblt(0, nil, nil, nil, nil, idx)
- end
- function vtansi.switchToBuffer(gpu,idx)
- -- copy screen to the active buffer
- vtansi.saveToBuffer(gpu,vtansi.activeBuffers[gpu.address])
- -- copy the new buffer to the screen
- vtansi.loadFromBuffer(gpu,idx)
- vtansi.activeBuffers[gpu.address] = idx
- end
- function vtansi.vtemu(gpu,bn) -- table number -- function -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal, either allocating a new buffer or using *bn*.
- local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
- local mx, my = gpu.maxResolution()
- local buffer = nil
- local cx, cy = 1, 1
- local pc = " "
- local lc = ""
- local mode = 0 -- 0 normal, 1 escape, 2 command
- local lw = true
- local sx, sy = 1,1
- local cs = ""
- local bg, fg = 0, 0xFFFFFF
-
- -- setup
- if gpu.getActiveBuffer then
- buffer = bn or gpu.allocateBuffer(mx,my)
- vtansi.activeBuffers[gpu.address] = vtansi.activeBuffers[gpu.address] or buffer
- local oldActiveBuffer = vtansi.activeBuffers[gpu.address]
- gpu.setActiveBuffer(buffer)
- gpu.setResolution(mx,my)
- gpu.fill(1,1,mx,my," ")
- gpu.setActiveBuffer(oldActiveBuffer)
- else
- gpu.setResolution(mx,my)
- gpu.fill(1,1,mx,my," ")
- end
-
- local function checkCursor()
- if cx > mx and lw then
- cx, cy = 1, cy+1
- end
- if cy > my then
- gpu.copy(1,2,mx,my-1,0,-1)
- gpu.fill(1,my,mx,1," ")
- cy=my
- end
- if cy < 1 then cy = 1 end
- if cx < 1 then cx = 1 end
- end
-
- local function termwrite(s)
- if buffer then
- if vtansi.activeBuffers[gpu.address] == buffer then
- gpu.setActiveBuffer(0)
- else
- gpu.setActiveBuffer(buffer)
- end
- end
- local wb = ""
- local lb, ec = nil, nil
- local function flushwb()
- while wb:len() > 0 do
- checkCursor()
- local wl = wb:sub(1,mx-cx+1)
- wb = wb:sub(wl:len()+1)
- gpu.set(cx, cy, wl)
- cx = cx + wl:len()
- end
- end
- local rs = ""
- s=s:gsub("\8","\27[D")
- pc = gpu.get(cx,cy)
- gpu.setForeground(fg)
- gpu.setBackground(bg)
- gpu.set(cx,cy,pc)
- for cc in s:gmatch(".") do
- if mode == 0 then
- if cc == "\n" then
- flushwb()
- cx,cy = 1, cy+1
- checkCursor()
- elseif cc == "\t" then
- wb=wb..(" "):rep((((cx//8)+1) * 8) - cx + 1)
- elseif cc == "\27" then
- flushwb()
- mode = 1
- else
- wb = wb .. cc
- end
- elseif mode == 1 then
- if cc == "[" then
- mode = 2
- else
- mode = 0
- end
- elseif mode == 2 then
- if cc:match("[%d;]") then
- cs = cs .. cc
- else
- mode = 0
- local tA = {}
- for s in cs:gmatch("%d+") do
- tA[#tA+1] = tonumber(s)
- end
- if cc == "H" then
- cx, cy = math.min(mx,tA[1] or 1), math.min(my,tA[2] or 1)
- elseif cc == "A" then
- for i = 1, (tA[1] or 1) do
- cy = cy - 1
- checkCursor()
- end
- elseif cc == "B" then
- for i = 1, (tA[1] or 1) do
- cy = cy + 1
- checkCursor()
- end
- elseif cc == "C" then
- for i = 1, (tA[1] or 1) do
- cx = cx + 1
- checkCursor()
- end
- elseif cc == "D" then
- for i = 1, (tA[1] or 1) do
- cx = cx - 1
- checkCursor()
- end
- elseif cc == "s" then
- sx, sy = cx, cy
- elseif cc == "u" then
- cx, cy = sx, sy
- elseif cc == "n" and tA[1] == 6 then
- rs = string.format("%s\27[%d;%dR",rs,cx,cy)
- elseif cc == "K" and tA[1] == 1 then
- gpu.fill(1,cy,cx,1," ")
- elseif cc == "K" and tA[1] == 2 then
- gpu.fill(cx,cy,mx,1," ")
- elseif cc == "K" then
- gpu.fill(1,cy,mx,1," ")
- elseif cc == "J" and tA[1] == 1 then
- gpu.fill(1,1,mx,cy," ")
- elseif cc == "J" and tA[1] == 2 then
- gpu.fill(1,1,mx,my," ")
- cx, cy = 1, 1
- elseif cc == "J" then
- gpu.fill(1,cy,mx,my," ")
- elseif cc == "m" then
- for _,num in ipairs(tA) do
- if num == 0 then
- fg,bg,ec,lb = 0xFFFFFF,0,false,true
- elseif num == 7 then
- local nfg,nbg = bg, fg
- fg, bg = nfg, nbg
- elseif num > 29 and num < 38 then
- fg = colours[num-29]
- elseif num > 39 and num < 48 then
- bg = colours[num-39]
- elseif num == 100 or num == 8 then -- disable local echo
- ec = false
- elseif num == 101 then -- disable line mode
- lb = false
- end
- end
- gpu.setForeground(fg)
- gpu.setBackground(bg)
- end
- cs = ""
- checkCursor()
- end
- end
- end
- flushwb()
- checkCursor()
- pc = gpu.get(cx,cy)
- gpu.setForeground(bg)
- gpu.setBackground(fg)
- gpu.set(cx,cy,pc)
- gpu.setForeground(fg)
- gpu.setBackground(bg)
- return rs, lb, ec
- end
-
- return termwrite, buffer
- end
-
- function vtansi.vtsession(gpua,scra,bn) -- string string number -- function function function -- creates a process to handle the GPU and screen address combination *gpua*/*scra*, optionally using buffer number *bn* specifically. Returns read, write and "close" functions.
- local modifiers = {}
- local gpu = component.proxy(gpua)
- -- gpu.bind(scra)
- local write, bn = vtansi.vtemu(gpu,bn)
- local kba = {}
- for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
- kba[v]=true
- end
- local buf, lbuf, echo = "", false, false
- os.spawn(function()
- while true do
- local ty,ka,ch,kc = coroutine.yield()
- if kba[ka] and keyboardIgnore[ka] == bn then
- keyboardIgnore[ka] = nil
- end
- if kba[ka] and vtansi.keys[kc] then
- modifiers[vtansi.keys[kc]] = ty == "key_down"
- end
- if ty == "key_down" and kba[ka] and (bn == nil or vtansi.activeBuffers[gpua] == bn) then
- if bn and ty == "key_down" and kba[ka] and ch == 46 and kc == 52 and (modifiers.lalt or modifiers.ralt) then
- -- next buffer
- local allBuffers = gpu.buffers()
- for k,v in ipairs(allBuffers) do
- if v == vtansi.activeBuffers[gpu.address] and allBuffers[k+1] and not keyboardIgnore[ka] then
- keyboardIgnore[ka] = bn
- vtansi.switchToBuffer(gpu,allBuffers[k+1])
- end
- end
- elseif bn and ty == "key_down" and kba[ka] and ch == 44 and kc == 51 and (modifiers.lalt or modifiers.ralt) then
- -- previous buffer
- local allBuffers = gpu.buffers()
- for k,v in ipairs(allBuffers) do
- if v == vtansi.activeBuffers[gpu.address] and allBuffers[k-1] and not keyboardIgnore[ka] then
- keyboardIgnore[ka] = bn
- vtansi.switchToBuffer(gpu,allBuffers[k-1])
- end
- end
- else
- local outs
- if ch > 0 then
- outs = string.char(ch)
- end
- outs = vtansi.sequences[kc] or outs
- if outs then
- if echo then write(outs) end
- buf=buf..outs
- end
- end
- end
- end
- end,string.format("ttyd[%s:%s/%i]",gpua:sub(1,8),scra:sub(1,8),tonumber(bn) or 0))
- local function bread(n)
- coroutine.yield()
- local r = buf
- buf = ""
- return r
- end
- local function bwrite(d)
- local ba, lb, ec = write(d)
- buf = buf .. ba
- if lb ~= nil then
- lbuf = lb
- end
- if ec ~= nil then
- echo = ec
- end
- end
- return bread, bwrite, function() io.write("\27[2J\27[H") end
- end
- return vtansi
|