bgColor = -1 -- white fontW, fontH = 16, 16 displayCols, displayRows = 40, 10 displayW, displayH = fontW*displayCols, fontH*displayRows width, height = displayW, displayH cursorBlinkRate = 500 --eventRate = 50 eventRate = 20 -- high speed! eventsPerCursorBlink = cursorBlinkRate/eventRate needsRepaint = false repaintTriggeringVars = {"curX", "curY", "cursorVisible", "scrollX", "scrollY"} fatCursor = true autoScroll = loadSnippet("#192") -- volatile data if not inited then scrollX, scrollY = 0, 0 curX, curY = 0, 0 if lines == nil then lines = {"hello", "world"} end needsRepaint = true inited = true end local min, max = math.min, math.max get("#109") -- copyRect fontRendererEnv = newCleanEnv() --fontRenderer = get("#178", fontRendererEnv) fontRenderer = get("#186", fontRendererEnv) grab("#175") -- tableToString function renderText(x, y, s) --print("renderText", x, y, s) fontRendererEnv.render(s) --print("fontWidth="..tostring(fontRendererEnv.fontWidth)) copyRect(fontRendererEnv, _G, 0, 0, fontRendererEnv.width, fontRendererEnv.height, x, y) --[[for y=0, 1 do for x=0, 15 do print(fontRendererEnv.pixels[x+y*fontRendererEnv.width+1]) end end]] end -- "hook" is an identifier for the place to extend. -- example: "processEvents" for extending the processEvents -- function. function extensions(hook) -- put your extensions here :) end function setPixel(x, y, col) if x >= 0 and y >= 0 and x < displayW and y < displayH then pixels[x+y*width+1] = col end end function invertPixel(x, y) if x >= 0 and y >= 0 and x < displayW and y < displayH then local ofs = x+y*width+1 pixels[ofs] = pixels[ofs] and -1-pixels[ofs] or -1 end end function stepCursor() if lastCurX ~= curX or lastCurY ~= curY then cursorCount = -1 end lastCurX = curX lastCurY = curY cursorCount = (cursorCount+1) % (eventsPerCursorBlink*2) cursorVisible = cursorCount < eventsPerCursorBlink end function renderCursor() if cursorVisible then for x=0, fatCursor and fontW-1 or 0 do for y=0, fontH-1 do invertPixel((curX-scrollX)*fontW+x, (curY-scrollY)*fontH+y) end end end end function cursorLeft() if curX > 0 then curX=curX-1 elseif curY > 0 then curY=curY-1 end end function cursorRight() curX=curX+1 end function cursorUp() if curY > 0 then curY=curY-1 end end function cursorDown() if curY < #lines-1 then curY=curY+1 end end function saveVars() savedVars = {} for _, var in ipairs(repaintTriggeringVars) do savedVars[var] = _G[var] end end function compareVars() for _, var in ipairs(repaintTriggeringVars) do if savedVars[var] ~= _G[var] then needsRepaint = true return end end end function moveCursorToSpot(x, y) local col = math.floor((x+scrollX)/fontW) local row = math.floor((y+scrollY)/fontH) row = max(0, min(row, #lines-1)) col = max(0, col) curX, curY = col, row end function insertText2(s) if s == '' then return end local l = lines[curY+1] local left, right = l:sub(1, curX), l:sub(curX+1) if s == '\n' then table.insert(lines, curY+1, left) lines[curY+2] = right curY = curY+1 curX = 0 else lines[curY+1] = left..s..right curX = curX+#s needsRepaint = true end end function insertText(s) local i = s:find("\n", 1, plain) if not i then insertText2(s) else insertText2(s:sub(1, i-1)) insertText2("\n") insertText(s:sub(i+1)) -- gotta love recursion! end end function backspace() local l = lines[curY+1] if curX > 0 then lines[curY+1] = l:sub(1, curX-1)..l:sub(curX+1) curX=curX-1 elseif curY > 0 then local p = lines[curY] lines[curY] = p..l table.remove(lines, curY+1) curY=curY-1 curX=#p end end function home() curX = 0 end function endPos() curX = #lines[curY+1] end function processEvents() saveVars() if inputEvents then for _, e in ipairs(inputEvents) do --print("event:", tableToString(e)) if e[1] == 'keyPressed' then if e.keyCode == 39 then cursorRight() elseif e.keyCode == 37 then cursorLeft() elseif e.keyCode == 40 then cursorDown() elseif e.keyCode == 38 then cursorUp() elseif e.keyCode == 0x24 then home() elseif e.keyCode == 0x23 then endPos() end elseif e[1] == 'keyTyped' then if e.keyChar == '\8' then backspace() else insertText(e.keyChar) end elseif e[1] == 'mousePressed' then moveCursorToSpot(e.x, e.y) end end end extensions('processEvents1') stepCursor() autoScroll() extensions('processEvents') compareVars() end function render() if needsRepaint then pixels = {} for i = 1, width*height do pixels[i] = bgColor end for l = 1, min(#lines-scrollY, displayRows) do renderText(0, (l-1)*fontH, lines[l+scrollY]:sub(scrollX+1, scrollX+1+displayCols)) end renderCursor() needsRepaint = false end end function step() pixels = nil processEvents() render() if exit then animateAgainIn = nil else animateAgainIn = {eventRate, step} end end step()