local unicode=require("unicode") local event=require("event") local component=require("component") local computer=require("computer") local tty={} tty.window= { fullscreen=true, blink=true, dx=0, dy=0, x=1, y=1, output_buffer="", } tty.stream={} local screen_cache={} local function screen_reset(gpu,addr) screen_cache[addr or gpu.getScreen()or false]=nil end event.listen("screen_resized",screen_reset) function tty.getViewport() local window=tty.window local screen=tty.screen() if window.fullscreen and screen and not screen_cache[screen]then screen_cache[screen]=true window.width,window.height=window.gpu.getViewport() end return window.width,window.height,window.dx,window.dy,window.x,window.y end function tty.setViewport(width,height,dx,dy,x,y) checkArg(1,width,"number") checkArg(2,height,"number") local window=tty.window dx,dy,x,y=dx or 0,dy or 0,x or 1,y or 1 window.width,window.height,window.dx,window.dy,window.x,window.y=width,height,dx,dy,x,y end function tty.gpu() return tty.window.gpu end function tty.clear() tty.stream.scroll(math.huge) tty.setCursor(1,1) end function tty.isAvailable() local gpu=tty.gpu() return not not(gpu and gpu.getScreen()) end function tty.stream.read() local core=require("core/cursor") local cursor=core.new(tty.window.cursor) tty.window.cursor=cursor local ok,result,reason=xpcall(core.read,debug.traceback,cursor) if not ok or not result then pcall(cursor.update,cursor) end return select(2,assert(ok,result,reason)) end function tty.stream:write(value) local gpu=tty.gpu() if not gpu then return end local window=tty.window local cursor=window.cursor or{} cursor.sy=cursor.sy or 0 cursor.tails=cursor.tails or{} local beeped local uptime=computer.uptime local last_sleep=uptime() window.output_buffer=window.output_buffer..value while true do if uptime()-last_sleep>3 then os.sleep(0) last_sleep=uptime() end local ansi_print=require("vt100").parse(window) cursor.sy=cursor.sy+self.scroll() if#window.output_buffer==0 or not ansi_print then break end local x,y=tty.getCursor() local _,ei,delim=unicode.sub(window.output_buffer,1,window.width):find("([\27\t\r\n\a\b\v\15])") local segment=ansi_print..(ei and window.output_buffer:sub(1,ei-1)or window.output_buffer) if segment~=""then local gpu_x,gpu_y=x+window.dx,y+window.dy local tail="" local wlen_needed=unicode.wlen(segment) local wlen_remaining=window.width-x+1 if wlen_remainingheight then lines=y-height else return 0 end end lines=math.min(lines,height) lines=math.max(lines,-height) local abs_lines=math.abs(lines) local box_height=height-abs_lines local fill_top=dy+1+(lines<0 and 0 or box_height) gpu.copy(dx+1,dy+1+math.max(0,lines),width,box_height,0,-lines) gpu.fill(dx+1,fill_top,width,abs_lines,' ') tty.setCursor(x,math.max(1,math.min(y,height))) return lines end local function bfd()return nil,"tty: invalid operation"end tty.stream.close=bfd tty.stream.seek=bfd tty.stream.handle="tty" return tty