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.

199 lines
4.8KB

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