Files
ages/agame/display/unix.py
Alek Ratzloff e868d0e14f Add display abstraction
In case we want to run this on something that isn't an ANSI terminal, we
have the option to implement it however we want.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2021-11-20 19:38:06 -08:00

89 lines
2.4 KiB
Python

import sys
import termios
from typing import Optional, TextIO
from agame.color import colorize
from .display import Display
ESC = "\u001b"
ANSI_MOVE_BOTTOM = f"{ESC}[999;0f"
class ANSIDisplay(Display):
"""
Display for a modern terminal.
This display attempts to keep the input at the bottom, and the output
scrolling up.
This utilizes `termios` and is therefore UNIX-only.
"""
def __init__(
self,
input_prompt: str = colorize("{{>}} "),
stdin: TextIO = sys.stdin,
stdout: TextIO = sys.stdout,
):
self.input_prompt = input_prompt
self.__stdin = stdin
self.__stdout = stdout
# Initial set-up:
# . Move the cursor to the bottom left part of the screen (this will set
# the current line to a really high value and will move us to the
# bottom of the screen)
self.stdout.write(ANSI_MOVE_BOTTOM)
# . Disable echo, set cbreak
fd = self.stdin.fileno()
self.__old_attrs = termios.tcgetattr(fd)
self.cbreak()
# . Clear
self.clear()
def line(self, line: str = ""):
self.stdout.write(line)
self.stdout.write("\n")
def input(self) -> str:
try:
self.nocbreak()
return input(self.input_prompt)
finally:
self.cbreak()
def wait_for_ack(self, prompt: Optional[str] = "Press any key to continue..."):
# Flush unread data
termios.tcflush(self.stdin.fileno(), termios.TCIFLUSH)
if prompt is not None:
self.line(prompt)
self.stdin.read(1)
def cbreak(self):
fd = self.stdin.fileno()
new = termios.tcgetattr(fd)
new[3] &= ~(termios.ECHO | termios.ICANON)
termios.tcsetattr(fd, termios.TCSADRAIN, new)
def nocbreak(self):
fd = self.stdin.fileno()
new = termios.tcgetattr(fd)
new[3] |= termios.ECHO | termios.ICANON
termios.tcsetattr(fd, termios.TCSADRAIN, new)
def finish(self):
# Clean up by restoring the old terminal state
fd = self.stdin.fileno()
termios.tcsetattr(fd, termios.TCSADRAIN, self.__old_attrs)
def clear(self):
self.stdout.write(f"{ESC}[2J")
@property
def stdin(self) -> TextIO:
return self.__stdin
@property
def stdout(self) -> TextIO:
return self.__stdout