Implement progressive enhancement of key event reporting
This commit is contained in:
parent
295e8db04c
commit
a30ea2b7f8
@ -21,11 +21,11 @@ advanced usages. The protocol is based on initial work in `fixterms
|
||||
<http://www.leonerd.org.uk/hacks/fixterms/>`_, however, it corrects various
|
||||
issues in that proposal, namely:
|
||||
|
||||
* No way to disambiguate Esc keypresses, other than using 8-bit controls
|
||||
* No way to disambiguate :kbd:`Esc` keypresses, other than using 8-bit controls
|
||||
which are undesirable for other reasons
|
||||
* Incorrectly encoding shifted keys when shift modifier is used
|
||||
* No way to not have :kbd:`Alt+letter` key presses generate escape codes that
|
||||
conflict with other escape codes
|
||||
* No way to have non-conflicting escape codes for :kbd:`alt+letter,
|
||||
ctrl+letter, ctrl+alt+letter` key presses
|
||||
* No way to specify both shifted and unshifted keys for robust shortcut
|
||||
matching (think matching :kbd:`ctrl+shift+equal` and :kbd:`ctrl+plus`)
|
||||
* No way to specify alternate layout key. This is useful for keyboard layouts
|
||||
@ -62,6 +62,8 @@ are separated by the semi-colon and sub-fields by the colon. Only the
|
||||
escape code is terminated by the ``u`` character (the byte ``0x75``).
|
||||
|
||||
|
||||
.. _key_codes:
|
||||
|
||||
Key codes
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@ -113,6 +115,8 @@ and so on. If the modifier field is not present in the escape code, its default
|
||||
value is ``1`` which means no modifiers.
|
||||
|
||||
|
||||
.. _event_types:
|
||||
|
||||
Event types
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -130,8 +134,8 @@ has value ``1`` and is the default if no event type sub field is present. The
|
||||
|
||||
|
||||
.. note:: Key events that result in text are reported as plain UTF-8 text, so
|
||||
events are not supported for them, unless the application requests key
|
||||
report mode, see below.
|
||||
events are not supported for them, unless the application requests *key
|
||||
report mode*, see below.
|
||||
|
||||
|
||||
Non-Unicode keys
|
||||
@ -145,11 +149,90 @@ names to code points for these keys is in the
|
||||
:ref:`Functional key definition table below <functional>`.
|
||||
|
||||
|
||||
Progressive enhancement
|
||||
--------------------------
|
||||
|
||||
While, in theory, every key event could be completely represented by this
|
||||
protocol and all would be hunk-dory, in reality there is a vast universe of
|
||||
existing terminal programs that expect legacy control codes for key events and
|
||||
that are not likely to ever be updated. To support these, in default mode,
|
||||
the terminal will emit legacy escape codes for compatibility. If a terminal
|
||||
program wants more robust key handling, it can request it from the terminal,
|
||||
via the mechanism described here. Each enhancement is described in detail
|
||||
below. The escape code for requesting enhancements is::
|
||||
|
||||
CSI = flags ; mode u
|
||||
|
||||
Here ``flags`` is a decimal encoded integer to specify a set of bit-flags. The
|
||||
meanings of the flags are given below. The second, ``mode`` parameter is
|
||||
optional (defaulting to ``1``) and specifies how the flags are applied.
|
||||
The value ``1`` means all set bits are set and all unset bits are reset.
|
||||
The value ``2`` means all set bits are set, unset bits are left unchanged.
|
||||
The value ``3`` means all set bits are reset, unset bits are left unchanged.
|
||||
|
||||
.. csv-table:: The progressive enhancement flags
|
||||
:header: "Bit", "Meaning"
|
||||
|
||||
"0b1 (1)", "Disambiguate escape codes"
|
||||
"0b10 (2)", "Report key event types"
|
||||
"0b100 (4)", "Report alternate keys"
|
||||
"0b1000 (8)", "Report all keys as CSIu escape codes"
|
||||
|
||||
The program running in the terminal can query the terminal for the
|
||||
current values of the flags by sending::
|
||||
|
||||
CSI ? u
|
||||
|
||||
The terminal will reply with::
|
||||
|
||||
CSI ? flags u
|
||||
|
||||
The program can also push/pop the current flags onto a stack in the
|
||||
terminal with::
|
||||
|
||||
CSI > flags u # for push, if flags ommitted default to zero
|
||||
CSI < number u # to pop number entries, defaulting to 1 if unspecified
|
||||
|
||||
Terminals should limit the size of the stack as appropriate, to prevent
|
||||
Denial-of-Service attacks. Terminals must maintain separate stacks for the main
|
||||
and alternate screens. If a pop request is received that empties the stack,
|
||||
all flags are reset. If a push request is received and the stack is full, the
|
||||
oldest entry from the stack must be evicted.
|
||||
|
||||
Disambiguate escape codes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This type of progressive enhancement fixes the problem of some legacy key
|
||||
press encodings overlapping with other control codes. For instance, pressing
|
||||
the :kbd:`Esc` key generates the byte ``0x1b`` which also is used to indicate
|
||||
the start of an escape code. Similarly pressing the key :kbd:`alt+[` will
|
||||
generate the bytes used for CSI control codes. Turning on this flag will cause
|
||||
the terminal to report the :kbd:`Esc, alt+letter, ctrl+letter, ctrl+alt+letter`
|
||||
keys using CSIu sequences instead of legacy ones. Here letter is any printable
|
||||
ASCII letter (from 32 (i.e. space) to 126 (i.e. ~)).
|
||||
|
||||
Report event types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This type of progressive enhancement causes the terminal to report key repeat
|
||||
and key release events. Normally only key press events are reported and key
|
||||
repeat events are treated as key press events. See :ref:`event_types` for
|
||||
details on how these are reported.
|
||||
|
||||
|
||||
Report alternate keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This type of progressive enhancement causes the terminal to report alternate
|
||||
key values in addition to the main value, to aid in shortcut matching. See
|
||||
:ref:`key_codes` for details on how these are reported.
|
||||
|
||||
.. _functional:
|
||||
|
||||
Functional key definitions
|
||||
----------------------------
|
||||
|
||||
.. {{{
|
||||
.. start functional key table (auto generated by gen-key-constants.py do not edit)
|
||||
|
||||
.. csv-table:: Functional key codes
|
||||
@ -261,3 +344,4 @@ Functional key definitions
|
||||
"MUTE_VOLUME", "E067"
|
||||
|
||||
.. end functional key table
|
||||
.. }}}
|
||||
|
||||
@ -160,8 +160,10 @@ encode_function_key(const KeyEvent *ev, char *output) {
|
||||
|
||||
static int
|
||||
encode_printable_ascii_key_legacy(const KeyEvent *ev, char *output) {
|
||||
char shifted_key = 0;
|
||||
if (!ev->mods.value) return snprintf(output, KEY_BUFFER_SIZE, "%c", (char)ev->key);
|
||||
if (ev->disambiguate) return 0;
|
||||
|
||||
char shifted_key = 0;
|
||||
if ('a' <= ev->key && ev->key <= 'z') shifted_key = ev->key + ('A' - 'a');
|
||||
switch(ev->key) {
|
||||
#define S(which, val) case which: shifted_key = val; break;
|
||||
@ -169,14 +171,13 @@ encode_printable_ascii_key_legacy(const KeyEvent *ev, char *output) {
|
||||
S('`', '~') S('-', '_') S('=', '+') S('[', '{') S(']', '}') S('\\', '|') S(';', ':') S('\'', '"') S(',', '<') S('.', '>') S('/', '?')
|
||||
#undef S
|
||||
}
|
||||
|
||||
if (!ev->mods.value) return snprintf(output, KEY_BUFFER_SIZE, "%c", (char)ev->key);
|
||||
if (!ev->disambiguate) {
|
||||
if ((ev->mods.value == ALT || ev->mods.value == (SHIFT | ALT)))
|
||||
return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", (shifted_key && ev->mods.shift) ? shifted_key : (char)ev->key);
|
||||
}
|
||||
if (ev->mods.value == CTRL && (ev->key != 'i' && ev->key != 'm' && ev->key != '[' && ev->key != '@'))
|
||||
return snprintf(output, KEY_BUFFER_SIZE, "%c", ev->key & 0x7f);
|
||||
shifted_key = (shifted_key && ev->mods.shift) ? shifted_key : (char)ev->key;
|
||||
if ((ev->mods.value == ALT || ev->mods.value == (SHIFT | ALT)))
|
||||
return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", shifted_key);
|
||||
if (ev->mods.value == CTRL)
|
||||
return snprintf(output, KEY_BUFFER_SIZE, "%c", ev->key & 0x1f);
|
||||
if (ev->mods.value == (CTRL | ALT))
|
||||
return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", ev->key & 0x1f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ on_key_input(GLFWkeyevent *ev) {
|
||||
screen_history_scroll(screen, SCROLL_FULL, false); // scroll back to bottom
|
||||
}
|
||||
char encoded_key[KEY_BUFFER_SIZE] = {0};
|
||||
int size = encode_glfw_key_event(ev, screen->modes.mDECCKM, screen->key_encoding_flags, encoded_key);
|
||||
int size = encode_glfw_key_event(ev, screen->modes.mDECCKM, screen_current_key_encoding_flags(screen), encoded_key);
|
||||
if (size == SEND_TEXT_TO_CHILD) {
|
||||
schedule_write_to_child(w->id, 1, text, strlen(text));
|
||||
debug("sent text to child\n");
|
||||
@ -130,19 +130,20 @@ fake_scroll(Window *w, int amount, bool upwards) {
|
||||
GLFWkeyevent ev = {.key = key };
|
||||
char encoded_key[KEY_BUFFER_SIZE] = {0};
|
||||
Screen *screen = w->render_data.screen;
|
||||
uint8_t flags = screen_current_key_encoding_flags(screen);
|
||||
while (amount-- > 0) {
|
||||
ev.action = GLFW_PRESS;
|
||||
int size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, screen->key_encoding_flags, encoded_key);
|
||||
int size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, flags, encoded_key);
|
||||
if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);
|
||||
ev.action = GLFW_RELEASE;
|
||||
size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, screen->key_encoding_flags, encoded_key);
|
||||
size = encode_glfw_key_event(&ev, screen->modes.mDECCKM, flags, encoded_key);
|
||||
if (size > 0) schedule_write_to_child(w->id, 1, encoded_key, size);
|
||||
}
|
||||
}
|
||||
|
||||
#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args)
|
||||
#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL;
|
||||
#define M(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL}
|
||||
#define M(name, arg_type) {#name, (PyCFunction)(void (*) (void))(py##name), arg_type, NULL}
|
||||
|
||||
PYWRAP1(key_for_native_key_name) {
|
||||
const char *name;
|
||||
|
||||
@ -683,7 +683,7 @@ dispatch_csi(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
||||
unsigned int num = screen->parser_buf_pos, start, i, num_params=0, p1, p2;
|
||||
static unsigned int params[MAX_PARAMS] = {0};
|
||||
bool private;
|
||||
if (buf[0] == '>' || buf[0] == '?' || buf[0] == '!' || buf[0] == '=' || buf[0] == '-') {
|
||||
if (buf[0] == '>' || buf[0] == '<' || buf[0] == '?' || buf[0] == '!' || buf[0] == '=' || buf[0] == '-') {
|
||||
start_modifier = (char)screen->parser_buf[0];
|
||||
buf++; num--;
|
||||
}
|
||||
@ -835,6 +835,23 @@ dispatch_csi(Screen *screen, PyObject DUMP_UNUSED *dump_callback) {
|
||||
screen_restore_cursor(screen);
|
||||
break;
|
||||
}
|
||||
if (!end_modifier && start_modifier == '?') {
|
||||
REPORT_COMMAND(screen_report_key_encoding_flags);
|
||||
screen_report_key_encoding_flags(screen);
|
||||
break;
|
||||
}
|
||||
if (!end_modifier && start_modifier == '=') {
|
||||
CALL_CSI_HANDLER2(screen_set_key_encoding_flags, 0, 1);
|
||||
break;
|
||||
}
|
||||
if (!end_modifier && start_modifier == '>') {
|
||||
CALL_CSI_HANDLER1(screen_push_key_encoding_flags, 0);
|
||||
break;
|
||||
}
|
||||
if (!end_modifier && start_modifier == '<') {
|
||||
CALL_CSI_HANDLER1(screen_pop_key_encoding_flags, 1);
|
||||
break;
|
||||
}
|
||||
REPORT_ERROR("Unknown CSI u sequence with start and end modifiers: '%c' '%c' and %u parameters", start_modifier, end_modifier, num_params);
|
||||
break;
|
||||
case 'r':
|
||||
@ -1090,6 +1107,7 @@ accumulate_csi(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_callback)
|
||||
break;
|
||||
case '?':
|
||||
case '>':
|
||||
case '<':
|
||||
case '!':
|
||||
case '=':
|
||||
case '-':
|
||||
|
||||
@ -135,6 +135,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||
self->tabstops = self->main_tabstops;
|
||||
init_tabstops(self->main_tabstops, self->columns);
|
||||
init_tabstops(self->alt_tabstops, self->columns);
|
||||
self->key_encoding_flags = self->main_key_encoding_flags;
|
||||
if (!init_overlay_line(self, self->columns)) { Py_CLEAR(self); return NULL; }
|
||||
self->hyperlink_pool = alloc_hyperlink_pool();
|
||||
if (!self->hyperlink_pool) { Py_CLEAR(self); return PyErr_NoMemory(); }
|
||||
@ -150,7 +151,8 @@ void
|
||||
screen_reset(Screen *self) {
|
||||
if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self, true, true);
|
||||
if (self->overlay_line.is_active) deactivate_overlay_line(self);
|
||||
self->key_encoding_flags = 0;
|
||||
memset(self->main_key_encoding_flags, 0, sizeof(self->main_key_encoding_flags));
|
||||
memset(self->alt_key_encoding_flags, 0, sizeof(self->alt_key_encoding_flags));
|
||||
self->last_graphic_char = 0;
|
||||
self->main_savepoint.is_valid = false;
|
||||
self->alt_savepoint.is_valid = false;
|
||||
@ -718,12 +720,14 @@ screen_toggle_screen_buffer(Screen *self, bool save_cursor, bool clear_alt_scree
|
||||
if (save_cursor) screen_save_cursor(self);
|
||||
self->linebuf = self->alt_linebuf;
|
||||
self->tabstops = self->alt_tabstops;
|
||||
self->key_encoding_flags = self->alt_key_encoding_flags;
|
||||
self->grman = self->alt_grman;
|
||||
screen_cursor_position(self, 1, 1);
|
||||
cursor_reset(self->cursor);
|
||||
} else {
|
||||
self->linebuf = self->main_linebuf;
|
||||
self->tabstops = self->main_tabstops;
|
||||
self->key_encoding_flags = self->main_key_encoding_flags;
|
||||
if (save_cursor) screen_restore_cursor(self);
|
||||
self->grman = self->main_grman;
|
||||
}
|
||||
@ -833,6 +837,54 @@ screen_set_8bit_controls(Screen *self, bool yes) {
|
||||
self->modes.eight_bit_controls = yes;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
screen_current_key_encoding_flags(Screen *self) {
|
||||
for (unsigned i = arraysz(self->main_key_encoding_flags); i-- > 0; ) {
|
||||
if (self->key_encoding_flags[i] & 0x80) return self->key_encoding_flags[i] & 0xf;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
screen_report_key_encoding_flags(Screen *self) {
|
||||
char buf[16] = {0};
|
||||
snprintf(buf, sizeof(buf), "?%uu", screen_current_key_encoding_flags(self));
|
||||
write_escape_code_to_child(self, CSI, buf);
|
||||
}
|
||||
|
||||
void
|
||||
screen_set_key_encoding_flags(Screen *self, uint32_t val, uint32_t how) {
|
||||
unsigned idx = 0;
|
||||
for (unsigned i = arraysz(self->main_key_encoding_flags); i-- > 0; ) {
|
||||
if (self->key_encoding_flags[i] & 0x80) { idx = i; break; }
|
||||
}
|
||||
uint8_t q = val & 0xf;
|
||||
if (how == 1) self->key_encoding_flags[idx] = q;
|
||||
else if (how == 2) self->key_encoding_flags[idx] |= q;
|
||||
else if (how == 3) self->key_encoding_flags[idx] &= ~q;
|
||||
self->key_encoding_flags[idx] |= 0x80;
|
||||
}
|
||||
|
||||
void
|
||||
screen_push_key_encoding_flags(Screen *self, uint32_t val) {
|
||||
uint8_t q = val & 0xf;
|
||||
const unsigned sz = arraysz(self->main_key_encoding_flags);
|
||||
unsigned current_idx = 0;
|
||||
for (unsigned i = arraysz(self->main_key_encoding_flags); i-- > 0; ) {
|
||||
if (self->key_encoding_flags[i] & 0x80) { current_idx = i; break; }
|
||||
}
|
||||
if (current_idx == sz - 1) memmove(self->key_encoding_flags, self->key_encoding_flags + 1, (sz - 1) * sizeof(self->main_key_encoding_flags[0]));
|
||||
else self->key_encoding_flags[current_idx++] |= 0x80;
|
||||
self->key_encoding_flags[current_idx] = 0x80 | q;
|
||||
}
|
||||
|
||||
void
|
||||
screen_pop_key_encoding_flags(Screen *self, uint32_t num) {
|
||||
for (unsigned i = arraysz(self->main_key_encoding_flags); num && i-- > 0; ) {
|
||||
if (self->key_encoding_flags[i] & 0x80) { num--; self->key_encoding_flags[i] = 0; }
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// Cursor {{{
|
||||
|
||||
@ -129,7 +129,7 @@ typedef struct {
|
||||
HYPERLINK_POOL_HANDLE hyperlink_pool;
|
||||
ANSIBuf as_ansi_buf;
|
||||
char_type last_graphic_char;
|
||||
unsigned key_encoding_flags;
|
||||
uint8_t main_key_encoding_flags[8], alt_key_encoding_flags[8], *key_encoding_flags;
|
||||
} Screen;
|
||||
|
||||
|
||||
@ -223,9 +223,15 @@ void screen_rescale_images(Screen *self);
|
||||
void screen_report_size(Screen *, unsigned int which);
|
||||
void screen_manipulate_title_stack(Screen *, unsigned int op, unsigned int which);
|
||||
void screen_draw_overlay_text(Screen *self, const char *utf8_text);
|
||||
void screen_set_key_encoding_flags(Screen *self, uint32_t val, uint32_t how);
|
||||
void screen_push_key_encoding_flags(Screen *self, uint32_t val);
|
||||
void screen_pop_key_encoding_flags(Screen *self, uint32_t num);
|
||||
uint8_t screen_current_key_encoding_flags(Screen *self);
|
||||
void screen_report_key_encoding_flags(Screen *self);
|
||||
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
|
||||
DECLARE_CH_SCREEN_HANDLER(bell)
|
||||
DECLARE_CH_SCREEN_HANDLER(backspace)
|
||||
DECLARE_CH_SCREEN_HANDLER(tab)
|
||||
DECLARE_CH_SCREEN_HANDLER(linefeed)
|
||||
DECLARE_CH_SCREEN_HANDLER(carriage_return)
|
||||
#undef DECLARE_CH_SCREEN_HANDLER
|
||||
|
||||
@ -2,82 +2,11 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from functools import partial
|
||||
|
||||
import kitty.fast_data_types as defines
|
||||
from kitty.keys import (
|
||||
interpret_key_event, modify_complex_key, modify_key_bytes, smkx_key_map
|
||||
)
|
||||
|
||||
from . import BaseTest
|
||||
|
||||
|
||||
class DummyWindow:
|
||||
|
||||
def __init__(self):
|
||||
self.screen = self
|
||||
self.extended_keyboard = False
|
||||
self.cursor_key_mode = True
|
||||
|
||||
|
||||
class TestParser(BaseTest):
|
||||
|
||||
def test_modify_complex_key(self):
|
||||
self.ae(modify_complex_key('kcuu1', 4), b'\033[1;4A')
|
||||
self.ae(modify_complex_key('kcuu1', 3), b'\033[1;3A')
|
||||
self.ae(modify_complex_key('kf5', 3), b'\033[15;3~')
|
||||
self.assertRaises(ValueError, modify_complex_key, 'kri', 3)
|
||||
|
||||
def test_interpret_key_event(self):
|
||||
# test rmkx/smkx
|
||||
w = DummyWindow()
|
||||
|
||||
def k(expected, key, mods=0):
|
||||
actual = interpret_key_event(
|
||||
getattr(defines, 'GLFW_KEY_' + key),
|
||||
0,
|
||||
mods,
|
||||
w,
|
||||
defines.GLFW_PRESS,
|
||||
)
|
||||
self.ae(b'\033' + expected.encode('ascii'), actual)
|
||||
|
||||
for ckm, mch in {True: 'O', False: '['}.items():
|
||||
w.cursor_key_mode = ckm
|
||||
for name, ch in {
|
||||
'UP': 'A',
|
||||
'DOWN': 'B',
|
||||
'RIGHT': 'C',
|
||||
'LEFT': 'D',
|
||||
'HOME': 'H',
|
||||
'END': 'F',
|
||||
}.items():
|
||||
k(mch + ch, name)
|
||||
w.cursor_key_mode = True
|
||||
|
||||
# test remaining special keys
|
||||
for key, num in zip('INSERT DELETE PAGE_UP PAGE_DOWN'.split(), '2356'):
|
||||
k('[' + num + '~', key)
|
||||
for key, num in zip('1234', 'PQRS'):
|
||||
k('O' + num, 'F' + key)
|
||||
for key, num in zip(range(5, 13), (15, 17, 18, 19, 20, 21, 23, 24)):
|
||||
k('[' + str(num) + '~', 'F{}'.format(key))
|
||||
|
||||
# test modifiers
|
||||
SPECIAL_KEYS = 'UP DOWN RIGHT LEFT HOME END INSERT DELETE PAGE_UP PAGE_DOWN '
|
||||
for i in range(1, 13):
|
||||
SPECIAL_KEYS += 'F{} '.format(i)
|
||||
SPECIAL_KEYS = SPECIAL_KEYS.strip().split()
|
||||
for mods, num in zip(('CONTROL', 'ALT', 'SHIFT+ALT'), '534'):
|
||||
fmods = 0
|
||||
num = int(num)
|
||||
for m in mods.split('+'):
|
||||
fmods |= getattr(defines, 'GLFW_MOD_' + m)
|
||||
km = partial(k, mods=fmods)
|
||||
for key in SPECIAL_KEYS:
|
||||
keycode = getattr(defines, 'GLFW_KEY_' + key)
|
||||
base_key = smkx_key_map[keycode]
|
||||
km(modify_key_bytes(base_key, num).decode('ascii')[1:], key)
|
||||
class TestKeys(BaseTest):
|
||||
|
||||
def test_encode_mouse_event(self):
|
||||
NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL = range(4)
|
||||
|
||||
@ -728,6 +728,49 @@ class TestScreen(BaseTest):
|
||||
self.ae(str(s.linebuf), '0\n5\n6\n7\n\n')
|
||||
self.ae(str(s.historybuf), '')
|
||||
|
||||
def test_key_encoding_flags_stack(self):
|
||||
s = self.create_screen()
|
||||
c = s.callbacks
|
||||
|
||||
def w(code, p1='', p2=''):
|
||||
p = f'{p1}'
|
||||
if p2:
|
||||
p += f';{p2}'
|
||||
return parse_bytes(s, f'\033[{code}{p}u'.encode('ascii'))
|
||||
|
||||
def ac(flags):
|
||||
parse_bytes(s, '\033[?u'.encode('ascii'))
|
||||
self.ae(c.wtcbuf, f'\033[?{flags}u'.encode('ascii'))
|
||||
c.clear()
|
||||
|
||||
ac(0)
|
||||
w('=', 0b1001)
|
||||
ac(0b1001)
|
||||
w('=', 0b0011, 2)
|
||||
ac(0b1011)
|
||||
w('=', 0b0110, 3)
|
||||
ac(0b1001)
|
||||
s.reset()
|
||||
ac(0)
|
||||
|
||||
w('>', 0b0011)
|
||||
ac(0b0011)
|
||||
w('=', 0b1111)
|
||||
ac(0b1111)
|
||||
w('>', 0b10)
|
||||
ac(0b10)
|
||||
w('<')
|
||||
ac(0b1111)
|
||||
for i in range(10):
|
||||
w('<')
|
||||
ac(0)
|
||||
s.reset()
|
||||
|
||||
for i in range(1, 16):
|
||||
w('>', i)
|
||||
ac(15)
|
||||
w('<'), ac(14), w('<'), ac(13)
|
||||
|
||||
def test_color_stack(self):
|
||||
s = self.create_screen()
|
||||
c = s.callbacks
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user