Operating system for OpenComputers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
7.2KB

  1. local vtansi = {}
  2. local keyboardIgnore = {}
  3. vtansi.activeBuffers = {}
  4. vtansi.sequences = {
  5. [28] = "\n", -- newline
  6. [200] = "\27[A", -- up
  7. [203] = "\27[D", -- left
  8. [205] = "\27[C", -- right
  9. [208] = "\27[B", -- down
  10. [201] = "\27[5~", -- page up
  11. [209] = "\27[6~" -- page down
  12. }
  13. vtansi.keys = {}
  14. vtansi.keys[0x38] = "lalt"
  15. vtansi.keys[0xB8] = "ralt"
  16. function vtansi.saveToBuffer(gpu,idx)
  17. gpu.bitblt(idx, nil, nil, nil, nil, 0)
  18. end
  19. function vtansi.loadFromBuffer(gpu,idx)
  20. gpu.bitblt(0, nil, nil, nil, nil, idx)
  21. end
  22. function vtansi.switchToBuffer(gpu,idx)
  23. -- copy screen to the active buffer
  24. vtansi.saveToBuffer(gpu,vtansi.activeBuffers[gpu.address])
  25. -- copy the new buffer to the screen
  26. vtansi.loadFromBuffer(gpu,idx)
  27. vtansi.activeBuffers[gpu.address] = idx
  28. end
  29. 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*.
  30. local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
  31. local mx, my = gpu.maxResolution()
  32. local buffer = nil
  33. local cx, cy = 1, 1
  34. local pc = " "
  35. local lc = ""
  36. local mode = 0 -- 0 normal, 1 escape, 2 command
  37. local lw = true
  38. local sx, sy = 1,1
  39. local cs = ""
  40. local bg, fg = 0, 0xFFFFFF
  41. -- setup
  42. if gpu.getActiveBuffer then
  43. buffer = bn or gpu.allocateBuffer(mx,my)
  44. vtansi.activeBuffers[gpu.address] = vtansi.activeBuffers[gpu.address] or buffer
  45. local oldActiveBuffer = vtansi.activeBuffers[gpu.address]
  46. gpu.setActiveBuffer(buffer)
  47. gpu.setResolution(mx,my)
  48. gpu.fill(1,1,mx,my," ")
  49. gpu.setActiveBuffer(oldActiveBuffer)
  50. else
  51. gpu.setResolution(mx,my)
  52. gpu.fill(1,1,mx,my," ")
  53. end
  54. local function checkCursor()
  55. if cx > mx and lw then
  56. cx, cy = 1, cy+1
  57. end
  58. if cy > my then
  59. gpu.copy(1,2,mx,my-1,0,-1)
  60. gpu.fill(1,my,mx,1," ")
  61. cy=my
  62. end
  63. if cy < 1 then cy = 1 end
  64. if cx < 1 then cx = 1 end
  65. end
  66. local function termwrite(s)
  67. if buffer then
  68. if vtansi.activeBuffers[gpu.address] == buffer then
  69. gpu.setActiveBuffer(0)
  70. else
  71. gpu.setActiveBuffer(buffer)
  72. end
  73. end
  74. local wb = ""
  75. local lb, ec = nil, nil
  76. local function flushwb()
  77. while wb:len() > 0 do
  78. checkCursor()
  79. local wl = wb:sub(1,mx-cx+1)
  80. wb = wb:sub(wl:len()+1)
  81. gpu.set(cx, cy, wl)
  82. cx = cx + wl:len()
  83. end
  84. end
  85. local rs = ""
  86. s=s:gsub("\8","\27[D")
  87. pc = gpu.get(cx,cy)
  88. gpu.setForeground(fg)
  89. gpu.setBackground(bg)
  90. gpu.set(cx,cy,pc)
  91. for cc in s:gmatch(".") do
  92. if mode == 0 then
  93. if cc == "\n" then
  94. flushwb()
  95. cx,cy = 1, cy+1
  96. checkCursor()
  97. elseif cc == "\t" then
  98. wb=wb..(" "):rep((((cx//8)+1) * 8) - cx + 1)
  99. elseif cc == "\27" then
  100. flushwb()
  101. mode = 1
  102. else
  103. wb = wb .. cc
  104. end
  105. elseif mode == 1 then
  106. if cc == "[" then
  107. mode = 2
  108. else
  109. mode = 0
  110. end
  111. elseif mode == 2 then
  112. if cc:match("[%d;]") then
  113. cs = cs .. cc
  114. else
  115. mode = 0
  116. local tA = {}
  117. for s in cs:gmatch("%d+") do
  118. tA[#tA+1] = tonumber(s)
  119. end
  120. if cc == "H" then
  121. cx, cy = math.min(mx,tA[1] or 1), math.min(my,tA[2] or 1)
  122. elseif cc == "A" then
  123. for i = 1, (tA[1] or 1) do
  124. cy = cy - 1
  125. checkCursor()
  126. end
  127. elseif cc == "B" then
  128. for i = 1, (tA[1] or 1) do
  129. cy = cy + 1
  130. checkCursor()
  131. end
  132. elseif cc == "C" then
  133. for i = 1, (tA[1] or 1) do
  134. cx = cx + 1
  135. checkCursor()
  136. end
  137. elseif cc == "D" then
  138. for i = 1, (tA[1] or 1) do
  139. cx = cx - 1
  140. checkCursor()
  141. end
  142. elseif cc == "s" then
  143. sx, sy = cx, cy
  144. elseif cc == "u" then
  145. cx, cy = sx, sy
  146. elseif cc == "n" and tA[1] == 6 then
  147. rs = string.format("%s\27[%d;%dR",rs,cx,cy)
  148. elseif cc == "K" and tA[1] == 1 then
  149. gpu.fill(1,cy,cx,1," ")
  150. elseif cc == "K" and tA[1] == 2 then
  151. gpu.fill(cx,cy,mx,1," ")
  152. elseif cc == "K" then
  153. gpu.fill(1,cy,mx,1," ")
  154. elseif cc == "J" and tA[1] == 1 then
  155. gpu.fill(1,1,mx,cy," ")
  156. elseif cc == "J" and tA[1] == 2 then
  157. gpu.fill(1,1,mx,my," ")
  158. cx, cy = 1, 1
  159. elseif cc == "J" then
  160. gpu.fill(1,cy,mx,my," ")
  161. elseif cc == "m" then
  162. for _,num in ipairs(tA) do
  163. if num == 0 then
  164. fg,bg,ec,lb = 0xFFFFFF,0,false,true
  165. elseif num == 7 then
  166. local nfg,nbg = bg, fg
  167. fg, bg = nfg, nbg
  168. elseif num > 29 and num < 38 then
  169. fg = colours[num-29]
  170. elseif num > 39 and num < 48 then
  171. bg = colours[num-39]
  172. elseif num == 100 or num == 8 then -- disable local echo
  173. ec = false
  174. elseif num == 101 then -- disable line mode
  175. lb = false
  176. end
  177. end
  178. gpu.setForeground(fg)
  179. gpu.setBackground(bg)
  180. end
  181. cs = ""
  182. checkCursor()
  183. end
  184. end
  185. end
  186. flushwb()
  187. checkCursor()
  188. pc = gpu.get(cx,cy)
  189. gpu.setForeground(bg)
  190. gpu.setBackground(fg)
  191. gpu.set(cx,cy,pc)
  192. gpu.setForeground(fg)
  193. gpu.setBackground(bg)
  194. return rs, lb, ec
  195. end
  196. return termwrite, buffer
  197. end
  198. 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.
  199. local modifiers = {}
  200. local gpu = component.proxy(gpua)
  201. -- gpu.bind(scra)
  202. local write, bn = vtansi.vtemu(gpu,bn)
  203. local kba = {}
  204. for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
  205. kba[v]=true
  206. end
  207. local buf, lbuf, echo = "", false, false
  208. os.spawn(function()
  209. while true do
  210. local ty,ka,ch,kc = coroutine.yield()
  211. if kba[ka] and keyboardIgnore[ka] == bn then
  212. keyboardIgnore[ka] = nil
  213. end
  214. if kba[ka] and vtansi.keys[kc] then
  215. modifiers[vtansi.keys[kc]] = ty == "key_down"
  216. end
  217. if ty == "key_down" and kba[ka] and (bn == nil or vtansi.activeBuffers[gpua] == bn) then
  218. if bn and ty == "key_down" and kba[ka] and ch == 46 and kc == 52 and (modifiers.lalt or modifiers.ralt) then
  219. -- next buffer
  220. local allBuffers = gpu.buffers()
  221. for k,v in ipairs(allBuffers) do
  222. if v == vtansi.activeBuffers[gpu.address] and allBuffers[k+1] and not keyboardIgnore[ka] then
  223. keyboardIgnore[ka] = bn
  224. vtansi.switchToBuffer(gpu,allBuffers[k+1])
  225. end
  226. end
  227. elseif bn and ty == "key_down" and kba[ka] and ch == 44 and kc == 51 and (modifiers.lalt or modifiers.ralt) then
  228. -- previous buffer
  229. local allBuffers = gpu.buffers()
  230. for k,v in ipairs(allBuffers) do
  231. if v == vtansi.activeBuffers[gpu.address] and allBuffers[k-1] and not keyboardIgnore[ka] then
  232. keyboardIgnore[ka] = bn
  233. vtansi.switchToBuffer(gpu,allBuffers[k-1])
  234. end
  235. end
  236. else
  237. local outs
  238. if ch > 0 then
  239. outs = string.char(ch)
  240. end
  241. outs = vtansi.sequences[kc] or outs
  242. if outs then
  243. if echo then write(outs) end
  244. buf=buf..outs
  245. end
  246. end
  247. end
  248. end
  249. end,string.format("ttyd[%s:%s/%i]",gpua:sub(1,8),scra:sub(1,8),tonumber(bn) or 0))
  250. local function bread(n)
  251. coroutine.yield()
  252. local r = buf
  253. buf = ""
  254. return r
  255. end
  256. local function bwrite(d)
  257. local ba, lb, ec = write(d)
  258. buf = buf .. ba
  259. if lb ~= nil then
  260. lbuf = lb
  261. end
  262. if ec ~= nil then
  263. echo = ec
  264. end
  265. end
  266. return bread, bwrite, function() io.write("\27[2J\27[H") end
  267. end
  268. return vtansi