1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
_WINDOWS = False
from typing import Tuple
import sys
try:
import tty
import termios
except ModuleNotFoundError:
import msvcrt
_WINDOWS = True
from easyansi._core.codes import CSI as _CSI
from easyansi._core.validations import validate_at_least_minimum as _validate_at_least_minimum
from easyansi._core.prnt import prnt as _prnt
MIN_COLUMN = 0
MIN_ROW = 0
MIN_MOVEMENT = 1
def up_code(rows: int = 1) -> str:
"""Return the ANSI code to move the cursor up n rows on the screen."""
_validate_at_least_minimum(rows, MIN_MOVEMENT, "Rows up")
return _CSI + str(rows) + "A"
def up(rows: int = 1) -> None:
"""Output the ANSI code to move the cursor up n rows on the screen."""
_prnt(up_code(rows=rows))
def down_code(rows: int = 1) -> str:
"""Return the ANSI code to move the cursor down n rows on the screen."""
_validate_at_least_minimum(rows, MIN_MOVEMENT, "Rows down")
return _CSI + str(rows) + "B"
def down(rows: int = 1) -> None:
"""Output the ANSI code to move the cursor down n rows on the screen."""
_prnt(down_code(rows=rows))
def right_code(cols: int = 1) -> str:
"""Return the ANSI code to move the cursor right n columns on the screen."""
_validate_at_least_minimum(cols, MIN_MOVEMENT, "Columns right")
return _CSI + str(cols) + "C"
def right(cols: int = 1) -> None:
"""Output the ANSI code to move the cursor right n columns on the screen."""
_prnt(right_code(cols=cols))
def left_code(cols: int = 1) -> str:
"""Return the ANSI code to move the cursor left n columns on the screen."""
_validate_at_least_minimum(cols, MIN_MOVEMENT, "Columns left")
return _CSI + str(cols) + "D"
def left(cols: int = 1) -> None:
"""Output the ANSI code to move the cursor left n columns on the screen."""
_prnt(left_code(cols=cols))
def next_line_code(rows_down: int = 1) -> str:
"""Return the ANSI code to move the cursor to the beginning of a specified number of rows down."""
_validate_at_least_minimum(rows_down, MIN_MOVEMENT, "Rows down")
return _CSI + str(rows_down) + "E"
def next_line(rows_down: int = 1) -> None:
"""Output the ANSI code to move the cursor to the beginning of a specified number of rows down."""
_prnt(next_line_code(rows_down=rows_down))
def previous_line_code(rows_up: int = 1) -> str:
"""Return the ANSI code to move the cursor to the beginning of a specified number of rows up."""
_validate_at_least_minimum(rows_up, MIN_MOVEMENT, "Rows up")
return _CSI + str(rows_up) + "F"
def previous_line(rows_up: int = 1) -> None:
"""Output the ANSI code to move the cursor to the beginning of a specified number of rows up."""
_prnt(previous_line_code(rows_up=rows_up))
def locate_code(x: int, y: int) -> str:
"""Return the ANSI code to move the cursor to the x, y coordinates on the screen.
The coordinate system is 0, 0 based."""
_validate_at_least_minimum(x, MIN_COLUMN, "X-Coordinate")
_validate_at_least_minimum(y, MIN_ROW, "Y-Coordinate")
return _CSI + str(y+1) + ";" + str(x+1) + "H"
def locate(x: int, y: int) -> None:
"""Output the ANSI code to move the cursor to the x, y coordinates on the screen.
The coordinate system is 0, 0 based."""
_prnt(locate_code(x=x, y=y))
def locate_column_code(x: int) -> str:
"""Return the ANSI code to move the cursor to the specified column (x) on the current line."""
_validate_at_least_minimum(x, MIN_COLUMN, "Column Number")
return _CSI + str(x+1) + "G"
def locate_column(x: int) -> None:
"""Output the ANSI code to move the cursor to the specified column (x) on the current line."""
_prnt(locate_column_code(x=x))
def hide_code() -> str:
"""Return the ANSI code to hide the cursor."""
return _CSI + "?25l"
def hide() -> None:
"""Output the ANSI code to hide the cursor."""
_prnt(hide_code())
def show_code() -> str:
"""Return the ANSI code to show the cursor."""
return _CSI + "?25h"
def show() -> None:
"""Output the ANSI code to show the cursor."""
_prnt(show_code())
def get_location() -> Tuple[int, int]:
"""Return the x, y coordinates of the cursor."""
response = _get_location_response()
location = response.split(";")
x = int(location[1]) - 1
y = int(location[0]) - 1
return x, y
def _get_location_response() -> str:
"""Make the actual ANSI call for the cursor location.
This code is separated out to make it easy to mock in unit tests."""
response = ""
_prnt(_CSI + "6n") # Send ANSI request for cursor location
if _WINDOWS:
while msvcrt.kbhit(): # type: ignore
char_in = msvcrt.getch() # type: ignore
response += char_in.decode("utf-8")
response = response[2:-1] # We do not need the first 2 bytes or the last byte
else:
f = sys.stdin.fileno()
terminal_settings = termios.tcgetattr(f) # Save terminal settings
try:
tty.setraw(f)
sys.stdin.read(2) # We do not need the first 2 bytes
char_in = sys.stdin.read(1)
while char_in != "R": # R will be the last character of the ANSI response, and we don't need it
response += char_in
char_in = sys.stdin.read(1) # Read a single character
finally:
termios.tcsetattr(f, termios.TCSADRAIN, terminal_settings) # Restore terminal settings
return response
|