terminal query functions, refactoring
This commit is contained in:
parent
037ce01efd
commit
51a30d5147
75
kitty.py
75
kitty.py
@ -5,11 +5,39 @@ import termios
|
|||||||
import tty
|
import tty
|
||||||
from base64 import standard_b64encode
|
from base64 import standard_b64encode
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import array
|
||||||
|
import fcntl
|
||||||
|
|
||||||
|
|
||||||
def draw_to_terminal(buffer: BytesIO) -> None:
|
def draw_to_terminal(buffer: BytesIO) -> None:
|
||||||
write_chunked(a="T", i=1, f=100, q=2, data=buffer.getvalue())
|
write_chunked(a="T", i=1, f=100, q=2, data=buffer.getvalue())
|
||||||
|
|
||||||
|
def get_terminal_cell_size() -> tuple[int, int]:
|
||||||
|
reply = _query_terminal("\x1b[16t", "t")
|
||||||
|
|
||||||
|
match = re.search(r"\[6;(\d+);(\d+)t", reply)
|
||||||
|
if match:
|
||||||
|
v_pix, h_pix = map(int, match.groups())
|
||||||
|
return v_pix, h_pix
|
||||||
|
else:
|
||||||
|
print(reply)
|
||||||
|
raise ValueError("Failed to parse terminal cell size response")
|
||||||
|
|
||||||
|
def get_terminal_size_pixel() -> tuple[int, int]:
|
||||||
|
reply = _query_terminal("\x1b[14t", "t")
|
||||||
|
|
||||||
|
match = re.search(r"\[4;(\d+);(\d+)t", reply)
|
||||||
|
if match:
|
||||||
|
height, width = map(int, match.groups())
|
||||||
|
return height, width
|
||||||
|
else:
|
||||||
|
raise ValueError("Failed to parse terminal pixel size response")
|
||||||
|
|
||||||
|
def get_terminal_size() -> tuple[int, int]:
|
||||||
|
buf = array.array('H', [0, 0, 0, 0])
|
||||||
|
fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)
|
||||||
|
rows, cols, width, height = buf
|
||||||
|
return (rows, cols)
|
||||||
|
|
||||||
def serialize_gr_command(**cmd) -> bytes:
|
def serialize_gr_command(**cmd) -> bytes:
|
||||||
payload = cmd.pop("payload", None)
|
payload = cmd.pop("payload", None)
|
||||||
@ -35,21 +63,21 @@ def write_chunked(**cmd) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def hide_cursor() -> None:
|
def hide_cursor() -> None:
|
||||||
sys.stdout.write("\x1b[?25l")
|
_write_stdout("\x1b[?25l")
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def show_cursor() -> None:
|
def show_cursor() -> None:
|
||||||
sys.stdout.write("\x1b[?25h")
|
_write_stdout("\x1b[?25h")
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def set_position(y: int, x: int) -> None:
|
def set_position(y: int, x: int) -> None:
|
||||||
sys.stdout.write(f"\x1b[{y};{x}H")
|
_write_stdout(f"\x1b[{y};{x}H")
|
||||||
|
|
||||||
|
def _write_stdout(cmd: str) -> None:
|
||||||
|
sys.stdout.write(cmd)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
def _query_terminal(escape: str, endchar: str) -> str:
|
||||||
def get_position() -> tuple[int, int]:
|
|
||||||
# Save the current terminal settings
|
# Save the current terminal settings
|
||||||
fd = sys.stdin.fileno()
|
fd = sys.stdin.fileno()
|
||||||
old_settings = termios.tcgetattr(fd)
|
old_settings = termios.tcgetattr(fd)
|
||||||
@ -58,23 +86,25 @@ def get_position() -> tuple[int, int]:
|
|||||||
# Set terminal to raw mode
|
# Set terminal to raw mode
|
||||||
tty.setraw(fd)
|
tty.setraw(fd)
|
||||||
# Send the ESC[6n command to request cursor position
|
# Send the ESC[6n command to request cursor position
|
||||||
sys.stdout.write("\x1b[6n")
|
_write_stdout(escape)
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
# Read the response: ESC [ row ; col R
|
# Read the response: ESC [ row ; col R
|
||||||
response = ""
|
response = ""
|
||||||
while True:
|
while True:
|
||||||
ch = sys.stdin.read(1)
|
ch = sys.stdin.read(1)
|
||||||
response += ch
|
response += ch
|
||||||
if ch == "R":
|
if ch == endchar:
|
||||||
break
|
break
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Restore the terminal settings
|
# Restore the terminal settings
|
||||||
termios.tcsetattr(fd, termios.TCSANOW, old_settings)
|
termios.tcsetattr(fd, termios.TCSANOW, old_settings)
|
||||||
|
return response
|
||||||
|
|
||||||
# Parse the response using regex
|
def get_position() -> tuple[int, int]:
|
||||||
match = re.search(r"\[(\d+);(\d+)R", response)
|
reply = _query_terminal("\x1b[6n", "R")
|
||||||
|
|
||||||
|
match = re.search(r"\[(\d+);(\d+)R", reply)
|
||||||
if match:
|
if match:
|
||||||
y, x = map(int, match.groups())
|
y, x = map(int, match.groups())
|
||||||
return y, x
|
return y, x
|
||||||
@ -85,12 +115,27 @@ def get_position() -> tuple[int, int]:
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import pyvista as pv
|
import pyvista as pv
|
||||||
|
|
||||||
|
cols, rows = get_terminal_size()
|
||||||
|
v_pix, h_pix = get_terminal_cell_size()
|
||||||
|
height, width = get_terminal_size_pixel()
|
||||||
|
|
||||||
|
y, x = get_position()
|
||||||
|
print(f'Terminal has {rows} rows, {cols} cols = {rows * cols} cells')
|
||||||
|
print(f'Cell size: {h_pix}x{v_pix} px')
|
||||||
|
print(f'Dimensions: {width}x{height} px')
|
||||||
|
|
||||||
|
|
||||||
|
new_lines = int((height/v_pix)-6)
|
||||||
|
print('\n' * new_lines, end='')
|
||||||
|
|
||||||
|
set_position(y - new_lines - 6, x)
|
||||||
|
|
||||||
s = pv.Sphere()
|
s = pv.Sphere()
|
||||||
pl = pv.Plotter()
|
pl = pv.Plotter(off_screen=True)
|
||||||
pl.add_mesh(s)
|
pl.add_mesh(s)
|
||||||
b = BytesIO()
|
b = BytesIO()
|
||||||
pl.show()
|
pl.screenshot(b, transparent_background=True, window_size=(width, height))
|
||||||
pl.screenshot(b)
|
|
||||||
# i = Image.new("RGB", (100, 100), (0, 0, 0))
|
# i = Image.new("RGB", (100, 100), (0, 0, 0))
|
||||||
# d = ImageDraw.Draw(i)
|
# d = ImageDraw.Draw(i)
|
||||||
# d.ellipse([(5, 5), (95, 95)])
|
# d.ellipse([(5, 5), (95, 95)])
|
||||||
|
Loading…
Reference in New Issue
Block a user