diff options
author | Matt Rixman <5834582+MatrixManAtYrService@users.noreply.github.com> | 2023-10-08 16:56:18 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-08 18:56:18 +0200 |
commit | 5bcea05cafb2a655ed1dd44544998eac5bed7c7f (patch) | |
tree | 619763a0651a20cab19a5d1192d193c33b407a2e | |
parent | efa64aa49b11e93461626e84b9f67b6185f26e1f (diff) | |
download | Nim-5bcea05cafb2a655ed1dd44544998eac5bed7c7f.tar.gz |
Add getCursorPos() to std/terminal (#22749)
This would be handy for making terminal apps which display content below the prompt (e.g. `fzf` does this). Need to test it on windows before I remove "draft" status. --------- Co-authored-by: Matt Rixman <MatrixManAtYrService@users.noreply.github.com> Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
-rw-r--r-- | lib/pure/terminal.nim | 48 |
1 files changed, 47 insertions, 1 deletions
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 4177eb002..da95ac32a 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -60,7 +60,7 @@ runnableExamples("-r:off"): import macros import strformat -from strutils import toLowerAscii, `%` +from strutils import toLowerAscii, `%`, parseInt import colors when defined(windows): @@ -96,6 +96,7 @@ const fgPrefix = "\e[38;2;" bgPrefix = "\e[48;2;" ansiResetCode* = "\e[0m" + getPos = "\e[6n" stylePrefix = "\e[" when defined(windows): @@ -220,6 +221,9 @@ when defined(windows): raiseOSError(osLastError()) return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y)) + proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError, OSError].} = + return getCursorPos(getStdHandle(STD_OUTPUT_HANDLE)) + proc setCursorPos(h: Handle, x, y: int) = var c: COORD c.x = int16(x) @@ -267,6 +271,48 @@ else: mode.c_cc[VTIME] = 0.cuchar discard fd.tcSetAttr(time, addr mode) + proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError].} = + ## Returns cursor position (x, y) + ## writes to stdout and expects the terminal to respond via stdin + var + xStr = "" + yStr = "" + ch: char + ct: int + readX = false + + # use raw mode to ask terminal for cursor position + let fd = getFileHandle(stdin) + var oldMode: Termios + discard fd.tcGetAttr(addr oldMode) + fd.setRaw() + stdout.write(getPos) + flushFile(stdout) + + try: + # parse response format: [yyy;xxxR + while true: + let n = readBuffer(stdin, addr ch, 1) + if n == 0 or ch == 'R': + if xStr == "" or yStr == "": + raise newException(ValueError, "Got character position message that was missing data") + break + ct += 1 + if ct > 16: + raise newException(ValueError, "Got unterminated character position message from terminal") + if ch == ';': + readX = true + elif ch in {'0'..'9'}: + if readX: + xStr.add(ch) + else: + yStr.add(ch) + finally: + # restore previous terminal mode + discard fd.tcSetAttr(TCSADRAIN, addr oldMode) + + return (parseInt(xStr), parseInt(yStr)) + proc terminalWidthIoctl*(fds: openArray[int]): int = ## Returns terminal width from first fd that supports the ioctl. |