mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-14 23:33:28 +00:00
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>
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user