177 lines
5.2 KiB
Python
177 lines
5.2 KiB
Python
import sys, time
|
|
|
|
try:
|
|
import select, termios
|
|
except ImportError:
|
|
termios = None
|
|
select = None
|
|
import msvcrt, signal
|
|
|
|
|
|
class ConsolePosix:
|
|
def __init__(self):
|
|
self.infd = sys.stdin.fileno()
|
|
self.infile = sys.stdin.buffer
|
|
self.outfile = sys.stdout.buffer
|
|
if hasattr(self.infile, "raw"):
|
|
self.infile = self.infile.raw
|
|
if hasattr(self.outfile, "raw"):
|
|
self.outfile = self.outfile.raw
|
|
|
|
self.orig_attr = termios.tcgetattr(self.infd)
|
|
|
|
def enter(self):
|
|
# attr is: [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
|
|
attr = termios.tcgetattr(self.infd)
|
|
attr[0] &= ~(
|
|
termios.BRKINT | termios.ICRNL | termios.INPCK | termios.ISTRIP | termios.IXON
|
|
)
|
|
attr[1] = 0
|
|
attr[2] = attr[2] & ~(termios.CSIZE | termios.PARENB) | termios.CS8
|
|
attr[3] = 0
|
|
attr[6][termios.VMIN] = 1
|
|
attr[6][termios.VTIME] = 0
|
|
termios.tcsetattr(self.infd, termios.TCSANOW, attr)
|
|
|
|
def exit(self):
|
|
termios.tcsetattr(self.infd, termios.TCSANOW, self.orig_attr)
|
|
|
|
def waitchar(self, pyb_serial):
|
|
# TODO pyb_serial might not have fd
|
|
select.select([self.infd, pyb_serial.fd], [], [])
|
|
|
|
def readchar(self):
|
|
res = select.select([self.infd], [], [], 0)
|
|
if res[0]:
|
|
return self.infile.read(1)
|
|
else:
|
|
return None
|
|
|
|
def write(self, buf):
|
|
self.outfile.write(buf)
|
|
|
|
|
|
class ConsoleWindows:
|
|
KEY_MAP = {
|
|
b"H": b"A", # UP
|
|
b"P": b"B", # DOWN
|
|
b"M": b"C", # RIGHT
|
|
b"K": b"D", # LEFT
|
|
b"G": b"H", # POS1
|
|
b"O": b"F", # END
|
|
b"Q": b"6~", # PGDN
|
|
b"I": b"5~", # PGUP
|
|
b"s": b"1;5D", # CTRL-LEFT,
|
|
b"t": b"1;5C", # CTRL-RIGHT,
|
|
b"\x8d": b"1;5A", # CTRL-UP,
|
|
b"\x91": b"1;5B", # CTRL-DOWN,
|
|
b"w": b"1;5H", # CTRL-POS1
|
|
b"u": b"1;5F", # CTRL-END
|
|
b"\x98": b"1;3A", # ALT-UP,
|
|
b"\xa0": b"1;3B", # ALT-DOWN,
|
|
b"\x9d": b"1;3C", # ALT-RIGHT,
|
|
b"\x9b": b"1;3D", # ALT-LEFT,
|
|
b"\x97": b"1;3H", # ALT-POS1,
|
|
b"\x9f": b"1;3F", # ALT-END,
|
|
b"S": b"3~", # DEL,
|
|
b"\x93": b"3;5~", # CTRL-DEL
|
|
b"R": b"2~", # INS
|
|
b"\x92": b"2;5~", # CTRL-INS
|
|
b"\x94": b"Z", # Ctrl-Tab = BACKTAB,
|
|
}
|
|
|
|
def __init__(self):
|
|
self.ctrl_c = 0
|
|
|
|
def _sigint_handler(self, signo, frame):
|
|
self.ctrl_c += 1
|
|
|
|
def enter(self):
|
|
signal.signal(signal.SIGINT, self._sigint_handler)
|
|
|
|
def exit(self):
|
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
|
|
def inWaiting(self):
|
|
return 1 if self.ctrl_c or msvcrt.kbhit() else 0
|
|
|
|
def waitchar(self, pyb_serial):
|
|
while not (self.inWaiting() or pyb_serial.inWaiting()):
|
|
time.sleep(0.01)
|
|
|
|
def readchar(self):
|
|
if self.ctrl_c:
|
|
self.ctrl_c -= 1
|
|
return b"\x03"
|
|
if msvcrt.kbhit():
|
|
ch = msvcrt.getch()
|
|
while ch in b"\x00\xe0": # arrow or function key prefix?
|
|
if not msvcrt.kbhit():
|
|
return None
|
|
ch = msvcrt.getch() # second call returns the actual key code
|
|
try:
|
|
ch = b"\x1b[" + self.KEY_MAP[ch]
|
|
except KeyError:
|
|
return None
|
|
return ch
|
|
|
|
def write(self, buf):
|
|
buf = buf.decode() if isinstance(buf, bytes) else buf
|
|
sys.stdout.write(buf)
|
|
sys.stdout.flush()
|
|
# for b in buf:
|
|
# if isinstance(b, bytes):
|
|
# msvcrt.putch(b)
|
|
# else:
|
|
# msvcrt.putwch(b)
|
|
|
|
|
|
if termios:
|
|
Console = ConsolePosix
|
|
VT_ENABLED = True
|
|
else:
|
|
Console = ConsoleWindows
|
|
|
|
# Windows VT mode ( >= win10 only)
|
|
# https://bugs.python.org/msg291732
|
|
import ctypes, os
|
|
from ctypes import wintypes
|
|
|
|
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
|
|
ERROR_INVALID_PARAMETER = 0x0057
|
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
|
|
|
def _check_bool(result, func, args):
|
|
if not result:
|
|
raise ctypes.WinError(ctypes.get_last_error())
|
|
return args
|
|
|
|
LPDWORD = ctypes.POINTER(wintypes.DWORD)
|
|
kernel32.GetConsoleMode.errcheck = _check_bool
|
|
kernel32.GetConsoleMode.argtypes = (wintypes.HANDLE, LPDWORD)
|
|
kernel32.SetConsoleMode.errcheck = _check_bool
|
|
kernel32.SetConsoleMode.argtypes = (wintypes.HANDLE, wintypes.DWORD)
|
|
|
|
def set_conout_mode(new_mode, mask=0xFFFFFFFF):
|
|
# don't assume StandardOutput is a console.
|
|
# open CONOUT$ instead
|
|
fdout = os.open("CONOUT$", os.O_RDWR)
|
|
try:
|
|
hout = msvcrt.get_osfhandle(fdout)
|
|
old_mode = wintypes.DWORD()
|
|
kernel32.GetConsoleMode(hout, ctypes.byref(old_mode))
|
|
mode = (new_mode & mask) | (old_mode.value & ~mask)
|
|
kernel32.SetConsoleMode(hout, mode)
|
|
return old_mode.value
|
|
finally:
|
|
os.close(fdout)
|
|
|
|
# def enable_vt_mode():
|
|
mode = mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
try:
|
|
set_conout_mode(mode, mask)
|
|
VT_ENABLED = True
|
|
except WindowsError:
|
|
VT_ENABLED = False
|