generated from mwc/lab_retro
164 lines
5.7 KiB
Python
164 lines
5.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Module containing Windows version of :class:`Terminal`."""
|
|
|
|
from __future__ import absolute_import
|
|
|
|
# std imports
|
|
import time
|
|
import msvcrt # pylint: disable=import-error
|
|
import contextlib
|
|
|
|
# 3rd party
|
|
from jinxed import win32 # pylint: disable=import-error
|
|
|
|
# local
|
|
from .terminal import WINSZ
|
|
from .terminal import Terminal as _Terminal
|
|
|
|
|
|
class Terminal(_Terminal):
|
|
"""Windows subclass of :class:`Terminal`."""
|
|
|
|
def getch(self):
|
|
r"""
|
|
Read, decode, and return the next byte from the keyboard stream.
|
|
|
|
:rtype: unicode
|
|
:returns: a single unicode character, or ``u''`` if a multi-byte
|
|
sequence has not yet been fully received.
|
|
|
|
For versions of Windows 10.0.10586 and later, the console is expected
|
|
to be in ENABLE_VIRTUAL_TERMINAL_INPUT mode and the default method is
|
|
called.
|
|
|
|
For older versions of Windows, msvcrt.getwch() is used. If the received
|
|
character is ``\x00`` or ``\xe0``, the next character is
|
|
automatically retrieved.
|
|
"""
|
|
if win32.VTMODE_SUPPORTED:
|
|
return super(Terminal, self).getch()
|
|
|
|
rtn = msvcrt.getwch()
|
|
if rtn in ('\x00', '\xe0'):
|
|
rtn += msvcrt.getwch()
|
|
return rtn
|
|
|
|
def kbhit(self, timeout=None):
|
|
"""
|
|
Return whether a keypress has been detected on the keyboard.
|
|
|
|
This method is used by :meth:`inkey` to determine if a byte may
|
|
be read using :meth:`getch` without blocking. This is implemented
|
|
by wrapping msvcrt.kbhit() in a timeout.
|
|
|
|
:arg float timeout: When ``timeout`` is 0, this call is
|
|
non-blocking, otherwise blocking indefinitely until keypress
|
|
is detected when None (default). When ``timeout`` is a
|
|
positive number, returns after ``timeout`` seconds have
|
|
elapsed (float).
|
|
:rtype: bool
|
|
:returns: True if a keypress is awaiting to be read on the keyboard
|
|
attached to this terminal.
|
|
"""
|
|
end = time.time() + (timeout or 0)
|
|
while True:
|
|
|
|
if msvcrt.kbhit():
|
|
return True
|
|
|
|
if timeout is not None and end < time.time():
|
|
break
|
|
|
|
time.sleep(0.01) # Sleep to reduce CPU load
|
|
return False
|
|
|
|
@staticmethod
|
|
def _winsize(fd):
|
|
"""
|
|
Return named tuple describing size of the terminal by ``fd``.
|
|
|
|
:arg int fd: file descriptor queries for its window size.
|
|
:rtype: WINSZ
|
|
:returns: named tuple describing size of the terminal
|
|
|
|
WINSZ is a :class:`collections.namedtuple` instance, whose structure
|
|
directly maps to the return value of the :const:`termios.TIOCGWINSZ`
|
|
ioctl return value. The return parameters are:
|
|
|
|
- ``ws_row``: width of terminal by its number of character cells.
|
|
- ``ws_col``: height of terminal by its number of character cells.
|
|
- ``ws_xpixel``: width of terminal by pixels (not accurate).
|
|
- ``ws_ypixel``: height of terminal by pixels (not accurate).
|
|
"""
|
|
window = win32.get_terminal_size(fd)
|
|
return WINSZ(ws_row=window.lines, ws_col=window.columns,
|
|
ws_xpixel=0, ws_ypixel=0)
|
|
|
|
@contextlib.contextmanager
|
|
def cbreak(self):
|
|
"""
|
|
Allow each keystroke to be read immediately after it is pressed.
|
|
|
|
This is a context manager for ``jinxed.w32.setcbreak()``.
|
|
|
|
.. note:: You must explicitly print any user input you would like
|
|
displayed. If you provide any kind of editing, you must handle
|
|
backspace and other line-editing control functions in this mode
|
|
as well!
|
|
|
|
**Normally**, characters received from the keyboard cannot be read
|
|
by Python until the *Return* key is pressed. Also known as *cooked* or
|
|
*canonical input* mode, it allows the tty driver to provide
|
|
line-editing before shuttling the input to your program and is the
|
|
(implicit) default terminal mode set by most unix shells before
|
|
executing programs.
|
|
"""
|
|
if self._keyboard_fd is not None:
|
|
|
|
filehandle = msvcrt.get_osfhandle(self._keyboard_fd)
|
|
|
|
# Save current terminal mode:
|
|
save_mode = win32.get_console_mode(filehandle)
|
|
save_line_buffered = self._line_buffered
|
|
win32.setcbreak(filehandle)
|
|
try:
|
|
self._line_buffered = False
|
|
yield
|
|
finally:
|
|
win32.set_console_mode(filehandle, save_mode)
|
|
self._line_buffered = save_line_buffered
|
|
|
|
else:
|
|
yield
|
|
|
|
@contextlib.contextmanager
|
|
def raw(self):
|
|
"""
|
|
A context manager for ``jinxed.w32.setcbreak()``.
|
|
|
|
Although both :meth:`break` and :meth:`raw` modes allow each keystroke
|
|
to be read immediately after it is pressed, Raw mode disables
|
|
processing of input and output.
|
|
|
|
In cbreak mode, special input characters such as ``^C`` are
|
|
interpreted by the terminal driver and excluded from the stdin stream.
|
|
In raw mode these values are receive by the :meth:`inkey` method.
|
|
"""
|
|
if self._keyboard_fd is not None:
|
|
|
|
filehandle = msvcrt.get_osfhandle(self._keyboard_fd)
|
|
|
|
# Save current terminal mode:
|
|
save_mode = win32.get_console_mode(filehandle)
|
|
save_line_buffered = self._line_buffered
|
|
win32.setraw(filehandle)
|
|
try:
|
|
self._line_buffered = False
|
|
yield
|
|
finally:
|
|
win32.set_console_mode(filehandle, save_mode)
|
|
self._line_buffered = save_line_buffered
|
|
|
|
else:
|
|
yield
|