Spaces:
No application file
No application file
# -*- 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 | |
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) | |
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 | |
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 | |