Source code for pysnap.terminal.keymap

"""Translate prompt_toolkit key presses into byte sequences for the guest."""

from __future__ import annotations

from prompt_toolkit.key_binding.key_processor import KeyPress
from prompt_toolkit.keys import Keys


SPECIAL_KEY_SEQUENCES: dict[Keys, bytes] = {
    Keys.Up: b"\x1b[A",
    Keys.Down: b"\x1b[B",
    Keys.Right: b"\x1b[C",
    Keys.Left: b"\x1b[D",
    Keys.Home: b"\x1b[H",
    Keys.End: b"\x1b[F",
    Keys.Insert: b"\x1b[2~",
    Keys.Delete: b"\x1b[3~",
    Keys.PageUp: b"\x1b[5~",
    Keys.PageDown: b"\x1b[6~",
    Keys.BackTab: b"\x1b[Z",
    Keys.F1: b"\x1bOP",
    Keys.F2: b"\x1bOQ",
    Keys.F3: b"\x1bOR",
    Keys.F4: b"\x1bOS",
    Keys.F5: b"\x1b[15~",
    Keys.F6: b"\x1b[17~",
    Keys.F7: b"\x1b[18~",
    Keys.F8: b"\x1b[19~",
    Keys.F9: b"\x1b[20~",
    Keys.F10: b"\x1b[21~",
    Keys.F11: b"\x1b[23~",
    Keys.F12: b"\x1b[24~",
}


[docs] def key_press_to_bytes(key_press: KeyPress) -> bytes | None: """Translate one key press into bytes for the VM serial console. :param key_press: Prompt-toolkit key press. :returns: Bytes to send or ``None`` when the key is unsupported. """ key = key_press.key data = key_press.data if isinstance(key, Keys) and data == "": # prompt_toolkit splits some sequences, such as Alt+Up from an xterm # (``ESC [ 1 ; 3 A``), into several keys; the first one carries all # bytes and the following ones carry none. return None if isinstance(key, Keys): if key in SPECIAL_KEY_SEQUENCES: return SPECIAL_KEY_SEQUENCES[key] if key == Keys.BracketedPaste: return paste_to_bytes(data, bracketed=False) if data and not data.startswith("<"): return data.encode("utf-8", errors="replace") return None return str(key).encode("utf-8", errors="replace")
PASTE_START = "\x1b[200~" PASTE_END = "\x1b[201~"
[docs] def paste_to_bytes(text: str, *, bracketed: bool) -> bytes: """Return pasted text as the bytes a terminal sends for it. Line breaks of any system (``CR LF``, ``LF``) become ``CR``, the Enter key of a terminal, so a text copied on Windows does not give empty lines. When the guest asked for bracketed paste (``ESC [ ? 2004 h``, as bash and vim do), the text is put between ``ESC [ 200 ~`` and ``ESC [ 201 ~``; markers inside the text are removed, so that it cannot end the paste early. :param text: Pasted text. :param bracketed: Whether the guest enabled bracketed paste. :returns: UTF-8 bytes for the guest. """ text = text.replace("\r\n", "\r").replace("\n", "\r") if bracketed: text = PASTE_START + text.replace(PASTE_START, "").replace(PASTE_END, "") + PASTE_END return text.encode("utf-8", errors="replace")