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.

211 lines
5.0KB

  1. local vtansi = {}
  2. vtansi.sequences = {
  3. [28] = "\n", -- newline
  4. [200] = "\27[A", -- up
  5. [203] = "\27[D", -- left
  6. [205] = "\27[C", -- right
  7. [208] = "\27[B", -- down
  8. [201] = "\27[5~", -- page up
  9. [209] = "\27[6~" -- page down
  10. }
  11. function vtansi.vtemu(gpu) -- table -- function -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal
  12. local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
  13. local mx, my = gpu.maxResolution()
  14. local cx, cy = 1, 1
  15. local pc = " "
  16. local lc = ""
  17. local mode = 0 -- 0 normal, 1 escape, 2 command
  18. local lw = true
  19. local sx, sy = 1,1
  20. local cs = ""
  21. local bg, fg = 0, 0xFFFFFF
  22. -- setup
  23. gpu.setResolution(mx,my)
  24. gpu.fill(1,1,mx,my," ")
  25. local function checkCursor()
  26. if cx > mx and lw then
  27. cx, cy = 1, cy+1
  28. end
  29. if cy > my then
  30. gpu.copy(1,2,mx,my-1,0,-1)
  31. gpu.fill(1,my,mx,1," ")
  32. cy=my
  33. end
  34. if cy < 1 then cy = 1 end
  35. if cx < 1 then cx = 1 end
  36. end
  37. local function termwrite(s)
  38. local wb = ""
  39. local lb, ec = nil, nil
  40. local function flushwb()
  41. while wb:len() > 0 do
  42. checkCursor()
  43. local wl = wb:sub(1,mx-cx+1)
  44. wb = wb:sub(wl:len()+1)
  45. gpu.set(cx, cy, wl)
  46. cx = cx + wl:len()
  47. end
  48. end
  49. local rs = ""
  50. s=s:gsub("\8","\27[D")
  51. pc = gpu.get(cx,cy)
  52. gpu.setForeground(fg)
  53. gpu.setBackground(bg)
  54. gpu.set(cx,cy,pc)
  55. for cc in s:gmatch(".") do
  56. if mode == 0 then
  57. if cc == "\n" then
  58. flushwb()
  59. cx,cy = 1, cy+1
  60. checkCursor()
  61. elseif cc == "\t" then
  62. wb=wb..(" "):rep(8*((cx+9)//8))
  63. elseif cc == "\27" then
  64. flushwb()
  65. mode = 1
  66. else
  67. wb = wb .. cc
  68. end
  69. elseif mode == 1 then
  70. if cc == "[" then
  71. mode = 2
  72. else
  73. mode = 0
  74. end
  75. elseif mode == 2 then
  76. if cc:match("[%d;]") then
  77. cs = cs .. cc
  78. else
  79. mode = 0
  80. local tA = {}
  81. for s in cs:gmatch("%d+") do
  82. tA[#tA+1] = tonumber(s)
  83. end
  84. if cc == "H" then
  85. cx, cy = math.min(mx,tA[1] or 1), math.min(my,tA[2] or 1)
  86. elseif cc == "A" then
  87. for i = 1, (tA[1] or 1) do
  88. cy = cy - 1
  89. checkCursor()
  90. end
  91. elseif cc == "B" then
  92. for i = 1, (tA[1] or 1) do
  93. cy = cy + 1
  94. checkCursor()
  95. end
  96. elseif cc == "C" then
  97. for i = 1, (tA[1] or 1) do
  98. cx = cx + 1
  99. checkCursor()
  100. end
  101. elseif cc == "D" then
  102. for i = 1, (tA[1] or 1) do
  103. cx = cx - 1
  104. checkCursor()
  105. end
  106. elseif cc == "s" then
  107. sx, sy = cx, cy
  108. elseif cc == "u" then
  109. cx, cy = sx, sy
  110. elseif cc == "n" and tA[1] == 6 then
  111. rs = string.format("%s\27[%d;%dR",rs,cx,cy)
  112. dprint(string.format("reporting %d;%d as current cursor position",cx,cy))
  113. elseif cc == "K" and tA[1] == 1 then
  114. gpu.fill(1,cy,cx,1," ")
  115. elseif cc == "K" and tA[1] == 2 then
  116. gpu.fill(cx,cy,mx,1," ")
  117. elseif cc == "K" then
  118. gpu.fill(1,cy,mx,1," ")
  119. elseif cc == "J" and tA[1] == 1 then
  120. gpu.fill(1,1,mx,cy," ")
  121. elseif cc == "J" and tA[1] == 2 then
  122. gpu.fill(1,1,mx,my," ")
  123. cx, cy = 1, 1
  124. elseif cc == "J" then
  125. gpu.fill(1,cy,mx,my," ")
  126. elseif cc == "m" then
  127. for _,num in ipairs(tA) do
  128. if num == 0 then
  129. fg,bg,ec,lb = 0xFFFFFF,0,false,true
  130. elseif num == 7 then
  131. local nfg,nbg = bg, fg
  132. fg, bg = nfg, nbg
  133. elseif num > 29 and num < 38 then
  134. fg = colours[num-29]
  135. elseif num > 39 and num < 48 then
  136. bg = colours[num-39]
  137. elseif num == 100 or num == 8 then -- disable local echo
  138. ec = false
  139. elseif num == 101 then -- disable line mode
  140. lb = false
  141. end
  142. end
  143. gpu.setForeground(fg)
  144. gpu.setBackground(bg)
  145. end
  146. cs = ""
  147. checkCursor()
  148. end
  149. end
  150. end
  151. flushwb()
  152. checkCursor()
  153. pc = gpu.get(cx,cy)
  154. gpu.setForeground(bg)
  155. gpu.setBackground(fg)
  156. gpu.set(cx,cy,pc)
  157. gpu.setForeground(fg)
  158. gpu.setBackground(bg)
  159. return rs, lb, ec
  160. end
  161. return termwrite
  162. end
  163. function vtansi.vtsession(gpua,scra) -- string string -- table -- creates a process to handle the GPU and screen address combination *gpua*/*scra*. Returns read, write and "close" functions.
  164. local gpu = component.proxy(gpua)
  165. gpu.bind(scra)
  166. local write = vtansi.vtemu(gpu)
  167. local kba = {}
  168. for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
  169. kba[v]=true
  170. end
  171. local buf, lbuf, echo = "", false, false
  172. os.spawn(function()
  173. while true do
  174. local ty,ka,ch,kc = coroutine.yield()
  175. if ty == "key_down" and kba[ka] then
  176. local outs
  177. if ch > 0 then
  178. outs = string.char(ch)
  179. end
  180. outs = vtansi.sequences[kc] or outs
  181. if outs then
  182. if echo then write(outs) end
  183. buf=buf..outs
  184. end
  185. end
  186. end
  187. end,string.format("ttyd[%s:%s]",gpua:sub(1,8),scra:sub(1,8)))
  188. local function bread(n)
  189. coroutine.yield()
  190. local r = buf
  191. buf = ""
  192. return r
  193. end
  194. local function bwrite(d)
  195. local ba, lb, ec = write(d)
  196. buf = buf .. ba
  197. if lb ~= nil then
  198. lbuf = lb
  199. end
  200. if ec ~= nil then
  201. echo = ec
  202. end
  203. end
  204. return bread, bwrite, function() io.write("\27[2J\27[H") end
  205. end
  206. return vtansi