# std imports import re import time import platform from collections import OrderedDict
# 3rd party import six
# isort: off # curses if platform.system() == 'Windows': # pylint: disable=import-error import jinxed as curses from jinxed.has_key import _capability_names as capability_names else: import curses from curses.has_key import _capability_names as capability_names
class Keystroke(six.text_type): """
A unicode-derived classfor describing a single keystroke.
A class instance describes a single keystroke received on input,
which may contain multiple characters as a multibyte sequence,
which is indicated by properties :attr:`is_sequence` returning
``True``.
When the string is a known sequence, :attr:`code` matches terminal class attributes for comparison, such as ``term.KEY_LEFT``.
The string-name of the sequence, such as ``u'KEY_LEFT'`` is accessed
by property :attr:`name`, andis used by the :meth:`__repr__` method
to display a human-readable form of the Keystroke this class
instance represents. It may otherwise by joined, split, or evaluated
just asas any other unicode string. """
def __new__(cls, ucs='', code=None, name=None): """Class constructor."""
new = six.text_type.__new__(cls, ucs)
new._name = name
new._code = code return new
@property def is_sequence(self): """Whether the value represents a multibyte sequence (bool).""" return self._code isnotNone
@property def name(self): """String-name of key sequence, such as ``u'KEY_LEFT'`` (str).""" return self._name
@property def code(self): """Integer keycode value of multibyte sequence (int).""" return self._code
def get_curses_keycodes(): """ Return mapping of curses key-names paired by their keycode integer value.
:rtype: dict
:returns: Dictionary of (name, code) pairs for curses keyboard constant
values and their mnemonic name. Such as code ``260``, with the value of
its key-name identity, ``u'KEY_LEFT'``. """
_keynames = [attr for attr in dir(curses) if attr.startswith('KEY_')] return {keyname: getattr(curses, keyname) for keyname in _keynames}
def get_keyboard_codes(): """ Return mapping of keycode integer values paired by their curses key-name.
:rtype: dict
:returns: Dictionary of (code, name) pairs for curses keyboard constant
values and their mnemonic name. Such as key ``260``, with the value of
its identity, ``u'KEY_LEFT'``.
These keys are derived from the attributes by the same of the curses module, with the following exceptions:
* ``KEY_DELETE`` in place of ``KEY_DC``
* ``KEY_INSERT`` in place of ``KEY_IC``
* ``KEY_PGUP`` in place of ``KEY_PPAGE``
* ``KEY_PGDOWN`` in place of ``KEY_NPAGE``
* ``KEY_ESCAPE`` in place of ``KEY_EXIT``
* ``KEY_SUP`` in place of ``KEY_SR``
* ``KEY_SDOWN`` in place of ``KEY_SF``
This function is the inverse of :func:`get_curses_keycodes`. With the
given override "mixins" listed above, the keycode for the delete key will
map to our imaginary ``KEY_DELETE`` mnemonic, effectively erasing the
phrase ``KEY_DC`` from our code vocabulary for anyone that wishes to use
the return value to determine the key-name by keycode. """
keycodes = OrderedDict(get_curses_keycodes())
keycodes.update(CURSES_KEYCODE_OVERRIDE_MIXIN) # merge _CURSES_KEYCODE_ADDINS added to our module space
keycodes.update((name, value) for name, value in globals().items() if name.startswith('KEY_'))
# invert dictionary (key, values) => (values, key), preferring the # last-most inserted value ('KEY_DELETE' over 'KEY_DC'). return dict(zip(keycodes.values(), keycodes.keys()))
def _alternative_left_right(term):
r"""
Determine andreturn mapping of left and right arrow keys sequences.
:arg blessed.Terminal term: :class:`~.Terminal` instance.
:rtype: dict
:returns: Dictionary of sequences ``term._cuf1``, and ``term._cub1``,
valued as ``KEY_RIGHT``, ``KEY_LEFT`` (when appropriate).
This function supports :func:`get_terminal_sequences` to discover
the preferred input sequence for the left and right application keys.
It is necessary to check the value of these sequences to ensure we do not
use ``u' '`` and ``u'\b'`` for ``KEY_RIGHT`` and ``KEY_LEFT``,
preferring their true application key sequence, instead. """ # pylint: disable=protected-access
keymap = {} if term._cuf1 and term._cuf1 != u' ':
keymap[term._cuf1] = curses.KEY_RIGHT if term._cub1 and term._cub1 != u'\b':
keymap[term._cub1] = curses.KEY_LEFT return keymap
def get_keyboard_sequences(term):
r""" Return mapping of keyboard sequences paired by keycodes.
:arg blessed.Terminal term: :class:`~.Terminal` instance.
:returns: mapping of keyboard unicode sequences paired by keycodes as integer. This is used as the argument ``mapper`` to
the supporting function :func:`resolve_sequence`.
:rtype: OrderedDict
Initialize andreturn a keyboard map and sequence lookup table,
(sequence, keycode) from :class:`~.Terminal` instance ``term``,
where ``sequence`` is a multibyte input sequence of unicode
characters, such as ``u'\x1b[D'``, and ``keycode`` is an integer
value, matching curses constant such as term.KEY_LEFT.
The return value is an OrderedDict instance, with their keys
sorted longest-first. """ # A small gem from curses.has_key that makes this all possible, # _capability_names: a lookup table of terminal capability names for # keyboard sequences (fe. kcub1, key_left), keyed by the values of # constants found beginning with KEY_ in the main curses module # (such as KEY_LEFT). # # latin1 encoding is used so that bytes in 8-bit range of 127-255 # have equivalent chr() and unichr() values, so that the sequence # of a kermit or avatar terminal, for example, remains unchanged # in its byte sequence values even when represented by unicode. #
sequence_map = dict((
(seq.decode('latin1'), val) for (seq, val) in (
(curses.tigetstr(cap), val) for (val, cap) in capability_names.items()
) if seq
) if term.does_styling else ())
# This is for fast lookup matching of sequences, preferring # full-length sequence such as ('\x1b[D', KEY_LEFT) # over simple sequences such as ('\x1b', KEY_EXIT). return OrderedDict((
(seq, sequence_map[seq]) for seq in sorted(
sequence_map.keys(), key=len, reverse=True)))
def get_leading_prefixes(sequences): """ Return a set of proper prefixes for given sequence of strings.
:arg iterable sequences
:rtype: set
:return: Set of all string prefixes
Given an iterable of strings, all textparts leading up to the final
string is returned as a unique set. This function supports the
:meth:`~.Terminal.inkey` method by determining whether the given
input is a sequence that **may** lead to a final matching pattern.
>>> prefixes(['abc', 'abdf', 'e', 'jkl'])
set([u'a', u'ab', u'abd', u'j', u'jk']) """ return {seq[:i] for seq in sequences for i in range(1, len(seq))}
def resolve_sequence(text, mapper, codes):
r""" Return a single :class:`Keystroke` instance for given sequence ``text``.
:arg str text: string of characters received from terminal input stream.
:arg OrderedDict mapper: unicode multibyte sequences, such as ``u'\x1b[D'``
paired by their integer value (260)
:arg dict codes: a :type:`dict` of integer values (such as 260) paired
by their mnemonic name, such as ``'KEY_LEFT'``.
:rtype: Keystroke
:returns: Keystroke instance for the given sequence
The given ``text`` may extend beyond a matching sequence, such as
``u\x1b[Dxxx`` returns a :class:`Keystroke` instance of attribute
:attr:`Keystroke.sequence` valued only ``u\x1b[D``. It is up to
calls to determine that ``xxx`` remains unresolved. """ for sequence, code in mapper.items(): if text.startswith(sequence): return Keystroke(ucs=sequence, code=code, name=codes[code]) return Keystroke(ucs=text and text[0] or u'')
def _time_left(stime, timeout): """ Return time remaining since ``stime`` before given ``timeout``.
This function assists determining the value of ``timeout`` for class method :meth:`~.Terminal.kbhit` and similar functions.
:arg float stime: starting time for measurement
:arg float timeout: timeout period, may be set to None to
indicate no timeout (where Noneis always returned).
:rtype: float or int
:returns: time remaining as float. If no time is remaining,
then the integer ``0`` is returned. """ return max(0, timeout - (time.time() - stime)) if timeout else timeout
:arg blessed.Terminal term: :class:`~.Terminal` instance.
:arg float timeout: timeout period, may be set to None to indicate no
timeout (where 0 is always returned).
:arg str pattern: target regular expression pattern to seek.
:rtype: tuple
:returns: tuple in form of ``(match, str)``, *match*
may be :class:`re.MatchObject` if pattern is discovered in input stream before timeout has elapsed, otherwise None. ``str`` is any remaining text received exclusive
of the matching pattern).
The reason a tuple containing non-matching data is returned, is that the
consumer should push such data back into the input buffer by
:meth:`~.Terminal.ungetch` if any was received.
For example, when a user is performing rapid input keystrokes while its
terminal emulator surreptitiously responds to this in-band sequence, we
must ensure any such keyboard data is well-received by the next call to
term.inkey() without delay. """
stime = time.time()
match, buf = None, u''
# first, buffer all pending data. pexpect library provides a # 'searchwindowsize' attribute that limits this memory region. We're not # concerned about OOM conditions: only (human) keyboard input and terminal # response sequences are expected.
whileTrue: # pragma: no branch # block as long as necessary to ensure at least one character is # received on input or remaining timeout has elapsed.
ucs = term.inkey(timeout=_time_left(stime, timeout)) # while the keyboard buffer is "hot" (has input), we continue to # aggregate all awaiting data. We do this to ensure slow I/O # calls do not unnecessarily give up within the first 'while' loop # for short timeout periods. while ucs:
buf += ucs
ucs = term.inkey(timeout=0)
match = re.search(pattern=pattern, string=buf) if match isnotNone: # match break
if timeout isnotNoneandnot _time_left(stime, timeout): # timeout break
return match, buf
#: Though we may determine *keynames* and codes for keyboard input that #: generate multibyte sequences, it is also especially useful to aliases #: a few basic ASCII characters such as ``KEY_TAB`` instead of ``u'\t'`` for #: uniformity. #: #: Furthermore, many key-names for application keys enabled only by context #: manager :meth:`~.Terminal.keypad` are surprisingly absent. We inject them #: here directly into the curses module.
_CURSES_KEYCODE_ADDINS = ( 'TAB', 'KP_MULTIPLY', 'KP_ADD', 'KP_SEPARATOR', 'KP_SUBTRACT', 'KP_DECIMAL', 'KP_DIVIDE', 'KP_EQUAL', 'KP_0', 'KP_1', 'KP_2', 'KP_3', 'KP_4', 'KP_5', 'KP_6', 'KP_7', 'KP_8', 'KP_9')
_LASTVAL = max(get_curses_keycodes().values()) for keycode_name in _CURSES_KEYCODE_ADDINS:
_LASTVAL += 1
globals()['KEY_' + keycode_name] = _LASTVAL
#: In a perfect world, terminal emulators would always send exactly what #: the terminfo(5) capability database plans for them, accordingly by the #: value of the ``TERM`` name they declare. #: #: But this isn't a perfect world. Many vt220-derived terminals, such as #: those declaring 'xterm', will continue to send vt220 codes instead of #: their native-declared codes, for backwards-compatibility. #: #: This goes for many: rxvt, putty, iTerm. #: #: These "mixins" are used for *all* terminals, regardless of their type. #: #: Furthermore, curses does not provide sequences sent by the keypad, #: at least, it does not provide a way to distinguish between keypad 0 #: and numeric 0.
DEFAULT_SEQUENCE_MIXIN = ( # these common control characters (and 127, ctrl+'?') mapped to # an application key definition.
(six.unichr(10), curses.KEY_ENTER),
(six.unichr(13), curses.KEY_ENTER),
(six.unichr(8), curses.KEY_BACKSPACE),
(six.unichr(9), KEY_TAB), # noqa # pylint: disable=undefined-variable
(six.unichr(27), curses.KEY_EXIT),
(six.unichr(127), curses.KEY_BACKSPACE),
(u"\x1b[A", curses.KEY_UP),
(u"\x1b[B", curses.KEY_DOWN),
(u"\x1b[C", curses.KEY_RIGHT),
(u"\x1b[D", curses.KEY_LEFT),
(u"\x1b[1;2A", curses.KEY_SR),
(u"\x1b[1;2B", curses.KEY_SF),
(u"\x1b[1;2C", curses.KEY_SRIGHT),
(u"\x1b[1;2D", curses.KEY_SLEFT),
(u"\x1b[F", curses.KEY_END),
(u"\x1b[H", curses.KEY_HOME), # not sure where these are from .. please report
(u"\x1b[K", curses.KEY_END),
(u"\x1b[U", curses.KEY_NPAGE),
(u"\x1b[V", curses.KEY_PPAGE),
# keypad, numlock off
(u"\x1b[1~", curses.KEY_FIND), # find
(u"\x1b[2~", curses.KEY_IC), # insert (0)
(u"\x1b[3~", curses.KEY_DC), # delete (.), "Execute"
(u"\x1b[4~", curses.KEY_SELECT), # select
(u"\x1b[5~", curses.KEY_PPAGE), # pgup (9)
(u"\x1b[6~", curses.KEY_NPAGE), # pgdown (3)
(u"\x1b[7~", curses.KEY_HOME), # home
(u"\x1b[8~", curses.KEY_END), # end
(u"\x1b[OA", curses.KEY_UP), # up (8)
(u"\x1b[OB", curses.KEY_DOWN), # down (2)
(u"\x1b[OC", curses.KEY_RIGHT), # right (6)
(u"\x1b[OD", curses.KEY_LEFT), # left (4)
(u"\x1b[OF", curses.KEY_END), # end (1)
(u"\x1b[OH", curses.KEY_HOME), # home (7)
# The vt220 placed F1-F4 above the keypad, in place of actual # F1-F4 were local functions (hold screen, print screen, # set up, data/talk, break).
(u"\x1bOP", curses.KEY_F1),
(u"\x1bOQ", curses.KEY_F2),
(u"\x1bOR", curses.KEY_F3),
(u"\x1bOS", curses.KEY_F4),
)
#: Override mixins for a few curses constants with easier #: mnemonics: there may only be a 1:1 mapping when only a #: keycode (int) is given, where these phrases are preferred.
CURSES_KEYCODE_OVERRIDE_MIXIN = (
('KEY_DELETE', curses.KEY_DC),
('KEY_INSERT', curses.KEY_IC),
('KEY_PGUP', curses.KEY_PPAGE),
('KEY_PGDOWN', curses.KEY_NPAGE),
('KEY_ESCAPE', curses.KEY_EXIT),
('KEY_SUP', curses.KEY_SR),
('KEY_SDOWN', curses.KEY_SF),
('KEY_UP_LEFT', curses.KEY_A1),
('KEY_UP_RIGHT', curses.KEY_A3),
('KEY_CENTER', curses.KEY_B2),
('KEY_BEGIN', curses.KEY_BEG),
)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.