local unicode = import("unicode") local event = import("event") --local keyboard = import("keyboard") --local ocelot = component.proxy(component.list("ocelot")()) local component = import("component") local computer = import("computer") local gpu = component.gpu _G.termlib = {} termlib.cursorPosX = 1 termlib.cursorPosY = 1 termlib.readHistory = {} local width, height = gpu.getResolution() local ANSIColorPalette = { ["dark"] = { [0] = 0x000000, [1] = 0x800000, [2] = 0x008000, [3] = 0x808000, [4] = 0x000080, [5] = 0x800080, [6] = 0x008080, [7] = 0xC0C0C0 }, ["bright"] = { [0] = 0x808080, [1] = 0xFF0000, [2] = 0x00FF00, [3] = 0xFFFF00, [4] = 0x0000FF, [5] = 0xFF00FF, [6] = 0x00FFFF, [7] = 0xFFFFFF } } defaultForegroundColor = ANSIColorPalette["bright"][7] defaultBackgroundColor = ANSIColorPalette["dark"][0] gpu.setForeground(defaultForegroundColor) gpu.setBackground(defaultBackgroundColor) local function scrollDown() width, height = gpu.getResolution() if gpu.copy(1,1,width,height,0,-1) then local prevForeground = gpu.getForeground() local prevBackground = gpu.getBackground() gpu.setForeground(defaultForegroundColor) gpu.setBackground(defaultBackgroundColor) gpu.fill(1, height, width, 1, " ") gpu.setForeground(prevForeground) gpu.setBackground(prevBackground) termlib.cursorPosY=height end end local function newLine() termlib.cursorPosX=1 termlib.cursorPosY = termlib.cursorPosY + 1 if termlib.cursorPosY>height then scrollDown() end end local function parseCodeNumbers(code) o = {} for num in code:sub(3,-2):gmatch("[^;]+") do table.insert(o,tonumber(num)) end return o end function termlib.write(text, textWrap) width, height = gpu.getResolution() -- you don't know how tiring this was just for ANSI escape code support if textWrap == nil then textWrap = true end if not text or not tostring(text) then return end if text:find("\a") then computer.beep() end text = tostring(text) text = "\27[0m" .. text:gsub("\t", " ") readBreak = 0 -- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character. -- unfortunately, changing the "i" variable would have unpredictable effects, so to not risk anything, this workaround was done. section = "" local function printSection() if #section==0 then return end while true do gpu.set(termlib.cursorPosX,termlib.cursorPosY,section) if unicode.wlen(section) > width - termlib.cursorPosX + 1 and textWrap then section = section:sub(width - termlib.cursorPosX + 2) newLine() else termlib.cursorPosX = termlib.cursorPosX+unicode.wlen(section) break end end section = "" end for i=1,#text do if readBreak>0 then readBreak = readBreak - 1 goto continue end if string.byte(text,i)==10 then printSection() newLine() elseif string.byte(text,i)==13 then printSection() termlib.cursorPosX=1 elseif string.byte(text,i)==0x1b and i<=#text-2 then printSection() --ocelot.log("0x1b char detected") codeType = string.sub(text,i+1,i+1) if codeType=="[" then -- Control Sequence Introducer --ocelot.log("Control Sequence Introducer") codeEndIdx = string.find(text,"m",i) code = string.sub(text,i,codeEndIdx) --ocelot.log("Code: "..code.." ("..i..", "..codeEndIdx..")") readBreak = readBreak + #code - 1 nums = parseCodeNumbers(code) codeEnd = code:sub(-1) --ocelot.log("Code end: "..codeEnd..", "..#codeEnd) if codeEnd == "m" then -- Select Graphic Rendition --ocelot.log("Select Graphic Rendition, ID "..nums[1]) if nums[1]>=30 and nums[1]<=37 then gpu.setForeground(ANSIColorPalette["dark"][nums[1]%10]) end if nums[1]==39 or nums[1]==0 then gpu.setForeground(defaultForegroundColor) end if nums[1]>=40 and nums[1]<=47 then gpu.setBackground(ANSIColorPalette["dark"][nums[1]%10]) end if nums[1]==49 or nums[1]==0 then gpu.setBackground(defaultBackgroundColor) end if nums[1]>=90 and nums[1]<=97 then gpu.setForeground(ANSIColorPalette["bright"][nums[1]%10]) end if nums[1]>=100 and nums[1]<=107 then gpu.setBackground(ANSIColorPalette["bright"][nums[1]%10]) end end end else --gpu.set(termlib.cursorPosX,termlib.cursorPosY,string.sub(text,i,i)) section = section..string.sub(text,i,i) end ::continue:: end printSection() end function _G.print(...) local args = {...} local stringArgs = {} for _, arg in pairs(args) do if tostring(arg) then table.insert(stringArgs, tostring(arg)) end end termlib.write(table.concat(stringArgs, " ") .. "\n") end function _G.clear() width, height = gpu.getResolution() gpu.setForeground(defaultForegroundColor) gpu.setBackground(defaultBackgroundColor) gpu.fill(1,1,width,height," ") termlib.cursorPosX, termlib.cursorPosY = 1, 1 end function _G.read(readHistoryType, prefix, defaultText, maxChars) checkArg(1, readHistoryType, "string", "nil") checkArg(2, prefix, "string", "nil") checkArg(3, defaultText, "string", "nil") checkArg(4, maxChars, "number", "nil") maxChars = maxChars or math.huge local text = defaultText or "" local historyIdx if readHistoryType then if not termlib.readHistory[readHistoryType] then termlib.readHistory[readHistoryType] = {text} elseif termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType] ] ~= "" then table.insert(termlib.readHistory[readHistoryType], text) end historyIdx = #termlib.readHistory[readHistoryType] end local cur = unicode.len(text)+1 if prefix then termlib.write(prefix) end local startX, startY = termlib.cursorPosX, termlib.cursorPosY local fg, bg = gpu.getForeground(), gpu.getBackground() local cursorBlink = true local function get(idx) idx=startX+idx-1 return gpu.get(idx%width,startY+(idx//width)) end local function checkScroll(y) for i=1,y-height do scrollDown() startY=startY-1 end return math.min(y,height) end local function set(idx,chr,rev) if chr==nil or chr=="" then return end if rev then gpu.setForeground(bg) gpu.setBackground(fg) else gpu.setForeground(fg) gpu.setBackground(bg) end idx=startX+idx-1 local setX, setY = (idx-1)%width+1, startY+((idx-1)//width+1)-1 setY = checkScroll(setY) gpu.set(setX,setY,unicode.sub(chr,1,width-setX+1)) for i=1,math.ceil((#chr+setX-1)/width)+1 do gpu.set(1,setY+i,unicode.sub(chr,2-setX+i*width,width+i*width-setX)) setY = checkScroll(setY) end end local function strDef(a,b) if #a==0 then return b end return a end local function curPos(cur) -- component.ocelot.log(table.concat({cur,unicode.wlen(unicode.sub(text,1,cur)),unicode.len(text)}," ")) return unicode.wlen(unicode.sub(text,1,cur-1))+1 end local function add(chr) if type(chr)~="string" or #chr==0 then return end if unicode.len(text)>=maxChars then return end if maxChars1 then text=unicode.sub(text,1,cur-2)..unicode.sub(text,cur) cur=cur-1 set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true) cursorBlink = true set(curPos(cur)+1,unicode.sub(text,cur+1).." ",false) updateHistory() elseif key=="delete" then text = unicode.sub(text,1,cur-1)..unicode.sub(text,cur+1) set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true) cursorBlink = true if cur<=unicode.len(text) then set(curPos(cur+1),unicode.sub(text,cur+1).." ",false) end updateHistory() elseif key=="enter" then set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false) break elseif not (args[3]<32 or (args[3]>0x7F and args[3]<=0x9F)) then add(unicode.char(args[3]) or " ") updateHistory() end elseif args and args[1]=="clipboard" then local clip = args[3] if not args[3] then goto continue end while isLine(unicode.sub(clip,1,1)) do clip=unicode.sub(clip,2) end while isLine(unicode.sub(clip,-1)) do clip=unicode.sub(clip,1,-2) end add(clip) updateHistory() else cursorBlink=not cursorBlink set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),cursorBlink) end ::continue:: end if readHistoryType then if termlib.readHistory[readHistoryType][#termlib.readHistory[readHistoryType]]=="" then table.remove(termlib.readHistory[readHistoryType],#termlib.readHistory[readHistoryType]) end if historyIdx<#termlib.readHistory[readHistoryType] then table.remove(termlib.readHistory[readHistoryType],historyIdx) table.insert(termlib.readHistory[readHistoryType],text) end while #termlib.readHistory[readHistoryType] > 50 do table.remove(termlib.readHistory[readHistoryType], 1) end end termlib.cursorPosX=1 termlib.cursorPosY=termlib.cursorPosY+math.ceil((unicode.wlen(text)+startX-1)/width) if termlib.cursorPosY>height then scrollDown() end return text end