106 lines
2.6 KiB
Python
Executable File
106 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import re
|
|
import sys
|
|
import termios
|
|
import tty
|
|
from base64 import standard_b64encode
|
|
from io import BytesIO
|
|
|
|
from PIL import Image, ImageDraw
|
|
|
|
def draw_to_terminal(img: Image.Image) -> None:
|
|
buffer: BytesIO = BytesIO()
|
|
img.save(buffer, format='PNG')
|
|
write_chunked(a='T', i=1, f=100, q=2, data=buffer.getvalue())
|
|
|
|
def serialize_gr_command(**cmd):
|
|
payload = cmd.pop('payload', None)
|
|
cmd = ','.join(f'{k}={v}' for k, v in cmd.items())
|
|
ans = []
|
|
w = ans.append
|
|
w(b'\033_G'), w(cmd.encode('ascii'))
|
|
if payload:
|
|
w(b';')
|
|
w(payload)
|
|
w(b'\033\\')
|
|
return b''.join(ans)
|
|
|
|
def write_chunked(**cmd):
|
|
data = standard_b64encode(cmd.pop('data'))
|
|
while data:
|
|
chunk, data = data[:4096], data[4096:]
|
|
m = 1 if data else 0
|
|
sys.stdout.buffer.write(serialize_gr_command(payload=chunk, m=m,
|
|
**cmd))
|
|
sys.stdout.flush()
|
|
cmd.clear()
|
|
|
|
def hide_cursor():
|
|
sys.stdout.write("\x1b[?25l")
|
|
sys.stdout.flush()
|
|
|
|
def show_cursor():
|
|
sys.stdout.write("\x1b[?25h")
|
|
sys.stdout.flush()
|
|
|
|
def set_position(y, x):
|
|
sys.stdout.write(f"\x1b[{y};{x}H")
|
|
sys.stdout.flush()
|
|
|
|
def get_position():
|
|
# Save the current terminal settings
|
|
fd = sys.stdin.fileno()
|
|
old_settings = termios.tcgetattr(fd)
|
|
|
|
try:
|
|
# Set terminal to raw mode
|
|
tty.setraw(fd)
|
|
|
|
# Send the ESC[6n command to request cursor position
|
|
sys.stdout.write("\x1b[6n")
|
|
sys.stdout.flush()
|
|
|
|
# Read the response: ESC [ row ; col R
|
|
response = ''
|
|
while True:
|
|
ch = sys.stdin.read(1)
|
|
response += ch
|
|
if ch == 'R':
|
|
break
|
|
|
|
finally:
|
|
# Restore the terminal settings
|
|
termios.tcsetattr(fd, termios.TCSANOW, old_settings)
|
|
|
|
# Parse the response using regex
|
|
match = re.search(r'\[(\d+);(\d+)R', response)
|
|
if match:
|
|
y, x = map(int, match.groups())
|
|
return y, x
|
|
else:
|
|
raise ValueError("Failed to parse cursor position response")
|
|
|
|
|
|
# sys.stdout.write("\x1b[6n")
|
|
# sys.stdout.flush()
|
|
# response = ''
|
|
# while True:
|
|
# ch = sys.stdin.read(1)
|
|
# response += ch
|
|
# if ch == 'R':
|
|
# break
|
|
# match = re.search(r'\[(\d+);(\d+)R', response)
|
|
# if match:
|
|
# y, x = map(int, match.groups())
|
|
# return y, x
|
|
|
|
if __name__ == '__main__':
|
|
i = Image.new("RGB", (100, 100), (0, 0, 0))
|
|
d = ImageDraw.Draw(i)
|
|
d.ellipse([(5, 5), (95, 95)])
|
|
d.ellipse([(10, 10), (90, 90)])
|
|
d.line(((50, 0), (50, 100)))
|
|
d.line(((0, 50), (100, 50)))
|
|
|
|
draw_to_terminal(i)
|