From 9376ed14f98c73d5d145ab48878bb0b2a4eb7f4c Mon Sep 17 00:00:00 2001 From: prod2 Date: Wed, 28 Dec 2022 23:55:34 +0100 Subject: [PATCH] ctrl+c, ctrl+d, start of a buffer lib --- termBuffer/buffer.nim | 134 +++++++++++++++++++++++++++++++++ termBuffer/escapeSequences.nim | 14 ++++ 2 files changed, 148 insertions(+) create mode 100644 termBuffer/buffer.nim create mode 100644 termBuffer/escapeSequences.nim diff --git a/termBuffer/buffer.nim b/termBuffer/buffer.nim new file mode 100644 index 0000000..be68fe7 --- /dev/null +++ b/termBuffer/buffer.nim @@ -0,0 +1,134 @@ +import unicode +import escapeSequences + +type + Buffer* = ref object + # AS IS ON SCREEN VALUES + stdout: File # usually you want to put stdout here + displayed: seq[Cell] + displayX, displayY: int # where the cursor is on display + positionX, positionY: int # where the buffer's top left corner is + + # SIMULATED VALUES + buffered: seq[Cell] + # cursor position + bufferX, bufferY: int # where the cursor is, in buffer + width, height: int + # current style we're writing with + cursorAttributes: seq[uint8] + + ForegroundColors* = enum + fcBlack = 30, fcRed, fcGreen, fcYellow, fcBlue, + fcMagenta, fcCyan, fcLightGray, + fcDefault = 39, + fcDarkGray = 90, fcLightRed, fcLightGreen, + fcLightYellow, fcLightBlue, fcLightMagenta, + fcLightCyan, fcWhite + BackgroundColors* = enum + bcBlack, bcRed, bcGreen, bcYellow, bcBlue, + bcMagenta, bcCyan, bcLightGray, + bcDefault = 49, + bcDarkGray = 100, bcLightRed, bcLightGreen, + bcLightYellow, bcLightBlue, bcLightMagenta, + bcLightCyan, bcWhite + FontStyle* = enum + # these two are the most widely supported + fsReset = 0, + fsBold = 1, + fsUnderlined = 4, + Cell = object + attributes: seq[uint8] + text: Rune + +func getCursorPos*(buf: Buffer): (int, int) = + (buf.bufferX, buf.bufferY) + +proc setCursorPos*(buf: Buffer, x, y: int) = + if x < 0 or y < 0 or x >= buf.width or y >= buf.height: + raise newException(ValueError, "Provided x or y out of bounds for SetCursorPos.") + buf.bufferX = x + buf.bufferY = y + +func getCursorXPos*(buf: Buffer): int = + buf.bufferX + +proc setCursorXPos*(buf: Buffer, x: int) = + if x < 0 or x >= buf.width: + raise newException(ValueError, "Provided x is out of bounds.") + buf.bufferX = x + + +proc setAttributes*(buf: Buffer, attributes: seq[uint8]) = + buf.cursorAttributes = attributes + +proc addAttribute*(buf: Buffer, attribute: uint8) = + buf.cursorAttributes.add(attribute) + +proc clearAttributes*(buf: Buffer) = + buf.cursorAttributes = @[] + +proc clear*(buf: Buffer) = + for i in 0..buf.buffered.high(): + buf.buffered[i] = Cell(text: Rune(0)) + buf.bufferX = 0 + buf.bufferY = 0 + +template lineStart(buf: Buffer, line: int): int = + # returns where line starts + buf.width * line + +template lineEnd(buf: Buffer, line: int): int = + buf.lineStart(line + 1) - 1 + +proc clearLine*(buf: Buffer) = + for i in buf.lineStart(buf.bufferY)..buf.lineEnd(buf.bufferY): + buf.buffered[i] = Cell(text: Rune(0)) + buf.bufferX = 0 + +proc write*(buf: Buffer, text: string) = + # convert text to characters + let runes = text.toRunes() + + # if it would go out of the screen, crash + if buf.bufferX + runes.len() >= buf.width: + raise newException(ValueError, "Text too long, would overflow the buffer.") + + let fromPos = buf.lineStart(buf.bufferY) + buf.bufferX + let toPos = fromPos + runes.high() + + for i in fromPos..toPos: + buf.buffered[i] = Cell(text: runes[i-fromPos], attributes: buf.cursorAttributes) + + +proc redraw*(buf: Buffer) = + var toPrint = "" + # save cursor state + let oldDisplayX = buf.displayX + let oldDisplayY = buf.displayY + + # go over display and buffer to find any differences + for i in 0..buf.buffered.high(): + let cellBuffer = buf.buffered[i] + let cellDisplay = buf.displayed[i] + # TODO continue here :) + + + + + # finish + toPrint &= escCursorPos(oldDisplayX, oldDisplayY) + buf.stdout.write(toPrint) + + +func getSize*(buf: Buffer): (int, int) = + (buf.width, buf.height) + +proc resize*(buf: Buffer, newX, newY: int) = + raise newException(Defect, "Not implemented") + +func getPosition*(buf: Buffer): (int, int) = + (buf.positionX, buf.positionY) + +proc setPosition*(buf: Buffer, x, y: int) = + buf.positionX = x + buf.positionY = y \ No newline at end of file diff --git a/termBuffer/escapeSequences.nim b/termBuffer/escapeSequences.nim new file mode 100644 index 0000000..c292752 --- /dev/null +++ b/termBuffer/escapeSequences.nim @@ -0,0 +1,14 @@ +# list of functions to generate escape sequences + +import strformat +import strutils + +func escCursorPos*(x, y: int): string = + &"\e[{x+1};{y+1}H" + +func escAttributes*(attributes: seq[uint8]): string = + let joined = attributes.join(";") + &"\e[{joined}m" + +func escEraseCharacters*(n: int): string = + &"\e[{n}X" \ No newline at end of file