#!/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)