1""" 
    2Output for vt100 terminals. 
    3 
    4A lot of thanks, regarding outputting of colors, goes to the Pygments project: 
    5(We don't rely on Pygments anymore, because many things are very custom, and 
    6everything has been highly optimized.) 
    7http://pygments.org/ 
    8""" 
    9 
    10from __future__ import annotations 
    11 
    12import io 
    13import os 
    14import sys 
    15from typing import Callable, Dict, Hashable, Iterable, Sequence, TextIO, Tuple 
    16 
    17from prompt_toolkit.cursor_shapes import CursorShape 
    18from prompt_toolkit.data_structures import Size 
    19from prompt_toolkit.output import Output 
    20from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs 
    21from prompt_toolkit.utils import is_dumb_terminal 
    22 
    23from .color_depth import ColorDepth 
    24from .flush_stdout import flush_stdout 
    25 
    26__all__ = [ 
    27    "Vt100_Output", 
    28] 
    29 
    30 
    31FG_ANSI_COLORS = { 
    32    "ansidefault": 39, 
    33    # Low intensity. 
    34    "ansiblack": 30, 
    35    "ansired": 31, 
    36    "ansigreen": 32, 
    37    "ansiyellow": 33, 
    38    "ansiblue": 34, 
    39    "ansimagenta": 35, 
    40    "ansicyan": 36, 
    41    "ansigray": 37, 
    42    # High intensity. 
    43    "ansibrightblack": 90, 
    44    "ansibrightred": 91, 
    45    "ansibrightgreen": 92, 
    46    "ansibrightyellow": 93, 
    47    "ansibrightblue": 94, 
    48    "ansibrightmagenta": 95, 
    49    "ansibrightcyan": 96, 
    50    "ansiwhite": 97, 
    51} 
    52 
    53BG_ANSI_COLORS = { 
    54    "ansidefault": 49, 
    55    # Low intensity. 
    56    "ansiblack": 40, 
    57    "ansired": 41, 
    58    "ansigreen": 42, 
    59    "ansiyellow": 43, 
    60    "ansiblue": 44, 
    61    "ansimagenta": 45, 
    62    "ansicyan": 46, 
    63    "ansigray": 47, 
    64    # High intensity. 
    65    "ansibrightblack": 100, 
    66    "ansibrightred": 101, 
    67    "ansibrightgreen": 102, 
    68    "ansibrightyellow": 103, 
    69    "ansibrightblue": 104, 
    70    "ansibrightmagenta": 105, 
    71    "ansibrightcyan": 106, 
    72    "ansiwhite": 107, 
    73} 
    74 
    75 
    76ANSI_COLORS_TO_RGB = { 
    77    "ansidefault": ( 
    78        0x00, 
    79        0x00, 
    80        0x00, 
    81    ),  # Don't use, 'default' doesn't really have a value. 
    82    "ansiblack": (0x00, 0x00, 0x00), 
    83    "ansigray": (0xE5, 0xE5, 0xE5), 
    84    "ansibrightblack": (0x7F, 0x7F, 0x7F), 
    85    "ansiwhite": (0xFF, 0xFF, 0xFF), 
    86    # Low intensity. 
    87    "ansired": (0xCD, 0x00, 0x00), 
    88    "ansigreen": (0x00, 0xCD, 0x00), 
    89    "ansiyellow": (0xCD, 0xCD, 0x00), 
    90    "ansiblue": (0x00, 0x00, 0xCD), 
    91    "ansimagenta": (0xCD, 0x00, 0xCD), 
    92    "ansicyan": (0x00, 0xCD, 0xCD), 
    93    # High intensity. 
    94    "ansibrightred": (0xFF, 0x00, 0x00), 
    95    "ansibrightgreen": (0x00, 0xFF, 0x00), 
    96    "ansibrightyellow": (0xFF, 0xFF, 0x00), 
    97    "ansibrightblue": (0x00, 0x00, 0xFF), 
    98    "ansibrightmagenta": (0xFF, 0x00, 0xFF), 
    99    "ansibrightcyan": (0x00, 0xFF, 0xFF), 
    100} 
    101 
    102 
    103assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) 
    104assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) 
    105assert set(ANSI_COLORS_TO_RGB) == set(ANSI_COLOR_NAMES) 
    106 
    107 
    108def _get_closest_ansi_color(r: int, g: int, b: int, exclude: Sequence[str] = ()) -> str: 
    109    """ 
    110    Find closest ANSI color. Return it by name. 
    111 
    112    :param r: Red (Between 0 and 255.) 
    113    :param g: Green (Between 0 and 255.) 
    114    :param b: Blue (Between 0 and 255.) 
    115    :param exclude: A tuple of color names to exclude. (E.g. ``('ansired', )``.) 
    116    """ 
    117    exclude = list(exclude) 
    118 
    119    # When we have a bit of saturation, avoid the gray-like colors, otherwise, 
    120    # too often the distance to the gray color is less. 
    121    saturation = abs(r - g) + abs(g - b) + abs(b - r)  # Between 0..510 
    122 
    123    if saturation > 30: 
    124        exclude.extend(["ansilightgray", "ansidarkgray", "ansiwhite", "ansiblack"]) 
    125 
    126    # Take the closest color. 
    127    # (Thanks to Pygments for this part.) 
    128    distance = 257 * 257 * 3  # "infinity" (>distance from #000000 to #ffffff) 
    129    match = "ansidefault" 
    130 
    131    for name, (r2, g2, b2) in ANSI_COLORS_TO_RGB.items(): 
    132        if name != "ansidefault" and name not in exclude: 
    133            d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 
    134 
    135            if d < distance: 
    136                match = name 
    137                distance = d 
    138 
    139    return match 
    140 
    141 
    142_ColorCodeAndName = Tuple[int, str] 
    143 
    144 
    145class _16ColorCache: 
    146    """ 
    147    Cache which maps (r, g, b) tuples to 16 ansi colors. 
    148 
    149    :param bg: Cache for background colors, instead of foreground. 
    150    """ 
    151 
    152    def __init__(self, bg: bool = False) -> None: 
    153        self.bg = bg 
    154        self._cache: dict[Hashable, _ColorCodeAndName] = {} 
    155 
    156    def get_code( 
    157        self, value: tuple[int, int, int], exclude: Sequence[str] = () 
    158    ) -> _ColorCodeAndName: 
    159        """ 
    160        Return a (ansi_code, ansi_name) tuple. (E.g. ``(44, 'ansiblue')``.) for 
    161        a given (r,g,b) value. 
    162        """ 
    163        key: Hashable = (value, tuple(exclude)) 
    164        cache = self._cache 
    165 
    166        if key not in cache: 
    167            cache[key] = self._get(value, exclude) 
    168 
    169        return cache[key] 
    170 
    171    def _get( 
    172        self, value: tuple[int, int, int], exclude: Sequence[str] = () 
    173    ) -> _ColorCodeAndName: 
    174        r, g, b = value 
    175        match = _get_closest_ansi_color(r, g, b, exclude=exclude) 
    176 
    177        # Turn color name into code. 
    178        if self.bg: 
    179            code = BG_ANSI_COLORS[match] 
    180        else: 
    181            code = FG_ANSI_COLORS[match] 
    182 
    183        return code, match 
    184 
    185 
    186class _256ColorCache(Dict[Tuple[int, int, int], int]): 
    187    """ 
    188    Cache which maps (r, g, b) tuples to 256 colors. 
    189    """ 
    190 
    191    def __init__(self) -> None: 
    192        # Build color table. 
    193        colors: list[tuple[int, int, int]] = [] 
    194 
    195        # colors 0..15: 16 basic colors 
    196        colors.append((0x00, 0x00, 0x00))  # 0 
    197        colors.append((0xCD, 0x00, 0x00))  # 1 
    198        colors.append((0x00, 0xCD, 0x00))  # 2 
    199        colors.append((0xCD, 0xCD, 0x00))  # 3 
    200        colors.append((0x00, 0x00, 0xEE))  # 4 
    201        colors.append((0xCD, 0x00, 0xCD))  # 5 
    202        colors.append((0x00, 0xCD, 0xCD))  # 6 
    203        colors.append((0xE5, 0xE5, 0xE5))  # 7 
    204        colors.append((0x7F, 0x7F, 0x7F))  # 8 
    205        colors.append((0xFF, 0x00, 0x00))  # 9 
    206        colors.append((0x00, 0xFF, 0x00))  # 10 
    207        colors.append((0xFF, 0xFF, 0x00))  # 11 
    208        colors.append((0x5C, 0x5C, 0xFF))  # 12 
    209        colors.append((0xFF, 0x00, 0xFF))  # 13 
    210        colors.append((0x00, 0xFF, 0xFF))  # 14 
    211        colors.append((0xFF, 0xFF, 0xFF))  # 15 
    212 
    213        # colors 16..232: the 6x6x6 color cube 
    214        valuerange = (0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF) 
    215 
    216        for i in range(217): 
    217            r = valuerange[(i // 36) % 6] 
    218            g = valuerange[(i // 6) % 6] 
    219            b = valuerange[i % 6] 
    220            colors.append((r, g, b)) 
    221 
    222        # colors 233..253: grayscale 
    223        for i in range(1, 22): 
    224            v = 8 + i * 10 
    225            colors.append((v, v, v)) 
    226 
    227        self.colors = colors 
    228 
    229    def __missing__(self, value: tuple[int, int, int]) -> int: 
    230        r, g, b = value 
    231 
    232        # Find closest color. 
    233        # (Thanks to Pygments for this!) 
    234        distance = 257 * 257 * 3  # "infinity" (>distance from #000000 to #ffffff) 
    235        match = 0 
    236 
    237        for i, (r2, g2, b2) in enumerate(self.colors): 
    238            if i >= 16:  # XXX: We ignore the 16 ANSI colors when mapping RGB 
    239                # to the 256 colors, because these highly depend on 
    240                # the color scheme of the terminal. 
    241                d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 
    242 
    243                if d < distance: 
    244                    match = i 
    245                    distance = d 
    246 
    247        # Turn color name into code. 
    248        self[value] = match 
    249        return match 
    250 
    251 
    252_16_fg_colors = _16ColorCache(bg=False) 
    253_16_bg_colors = _16ColorCache(bg=True) 
    254_256_colors = _256ColorCache() 
    255 
    256 
    257class _EscapeCodeCache(Dict[Attrs, str]): 
    258    """ 
    259    Cache for VT100 escape codes. It maps 
    260    (fgcolor, bgcolor, bold, underline, strike, italic, blink, reverse, hidden, dim) tuples to VT100 
    261    escape sequences. 
    262 
    263    :param true_color: When True, use 24bit colors instead of 256 colors. 
    264    """ 
    265 
    266    def __init__(self, color_depth: ColorDepth) -> None: 
    267        self.color_depth = color_depth 
    268 
    269    def __missing__(self, attrs: Attrs) -> str: 
    270        ( 
    271            fgcolor, 
    272            bgcolor, 
    273            bold, 
    274            underline, 
    275            strike, 
    276            italic, 
    277            blink, 
    278            reverse, 
    279            hidden, 
    280            dim, 
    281        ) = attrs 
    282        parts: list[str] = [] 
    283 
    284        parts.extend(self._colors_to_code(fgcolor or "", bgcolor or "")) 
    285 
    286        if bold: 
    287            parts.append("1") 
    288        if dim: 
    289            parts.append("2") 
    290        if italic: 
    291            parts.append("3") 
    292        if blink: 
    293            parts.append("5") 
    294        if underline: 
    295            parts.append("4") 
    296        if reverse: 
    297            parts.append("7") 
    298        if hidden: 
    299            parts.append("8") 
    300        if strike: 
    301            parts.append("9") 
    302 
    303        if parts: 
    304            result = "\x1b[0;" + ";".join(parts) + "m" 
    305        else: 
    306            result = "\x1b[0m" 
    307 
    308        self[attrs] = result 
    309        return result 
    310 
    311    def _color_name_to_rgb(self, color: str) -> tuple[int, int, int]: 
    312        "Turn 'ffffff', into (0xff, 0xff, 0xff)." 
    313        try: 
    314            rgb = int(color, 16) 
    315        except ValueError: 
    316            raise 
    317        else: 
    318            r = (rgb >> 16) & 0xFF 
    319            g = (rgb >> 8) & 0xFF 
    320            b = rgb & 0xFF 
    321            return r, g, b 
    322 
    323    def _colors_to_code(self, fg_color: str, bg_color: str) -> Iterable[str]: 
    324        """ 
    325        Return a tuple with the vt100 values  that represent this color. 
    326        """ 
    327        # When requesting ANSI colors only, and both fg/bg color were converted 
    328        # to ANSI, ensure that the foreground and background color are not the 
    329        # same. (Unless they were explicitly defined to be the same color.) 
    330        fg_ansi = "" 
    331 
    332        def get(color: str, bg: bool) -> list[int]: 
    333            nonlocal fg_ansi 
    334 
    335            table = BG_ANSI_COLORS if bg else FG_ANSI_COLORS 
    336 
    337            if not color or self.color_depth == ColorDepth.DEPTH_1_BIT: 
    338                return [] 
    339 
    340            # 16 ANSI colors. (Given by name.) 
    341            elif color in table: 
    342                return [table[color]] 
    343 
    344            # RGB colors. (Defined as 'ffffff'.) 
    345            else: 
    346                try: 
    347                    rgb = self._color_name_to_rgb(color) 
    348                except ValueError: 
    349                    return [] 
    350 
    351                # When only 16 colors are supported, use that. 
    352                if self.color_depth == ColorDepth.DEPTH_4_BIT: 
    353                    if bg:  # Background. 
    354                        if fg_color != bg_color: 
    355                            exclude = [fg_ansi] 
    356                        else: 
    357                            exclude = [] 
    358                        code, name = _16_bg_colors.get_code(rgb, exclude=exclude) 
    359                        return [code] 
    360                    else:  # Foreground. 
    361                        code, name = _16_fg_colors.get_code(rgb) 
    362                        fg_ansi = name 
    363                        return [code] 
    364 
    365                # True colors. (Only when this feature is enabled.) 
    366                elif self.color_depth == ColorDepth.DEPTH_24_BIT: 
    367                    r, g, b = rgb 
    368                    return [(48 if bg else 38), 2, r, g, b] 
    369 
    370                # 256 RGB colors. 
    371                else: 
    372                    return [(48 if bg else 38), 5, _256_colors[rgb]] 
    373 
    374        result: list[int] = [] 
    375        result.extend(get(fg_color, False)) 
    376        result.extend(get(bg_color, True)) 
    377 
    378        return map(str, result) 
    379 
    380 
    381def _get_size(fileno: int) -> tuple[int, int]: 
    382    """ 
    383    Get the size of this pseudo terminal. 
    384 
    385    :param fileno: stdout.fileno() 
    386    :returns: A (rows, cols) tuple. 
    387    """ 
    388    size = os.get_terminal_size(fileno) 
    389    return size.lines, size.columns 
    390 
    391 
    392class Vt100_Output(Output): 
    393    """ 
    394    :param get_size: A callable which returns the `Size` of the output terminal. 
    395    :param stdout: Any object with has a `write` and `flush` method + an 'encoding' property. 
    396    :param term: The terminal environment variable. (xterm, xterm-256color, linux, ...) 
    397    :param enable_cpr: When `True` (the default), send "cursor position 
    398        request" escape sequences to the output in order to detect the cursor 
    399        position. That way, we can properly determine how much space there is 
    400        available for the UI (especially for drop down menus) to render. The 
    401        `Renderer` will still try to figure out whether the current terminal 
    402        does respond to CPR escapes. When `False`, never attempt to send CPR 
    403        requests. 
    404    """ 
    405 
    406    # For the error messages. Only display "Output is not a terminal" once per 
    407    # file descriptor. 
    408    _fds_not_a_terminal: set[int] = set() 
    409 
    410    def __init__( 
    411        self, 
    412        stdout: TextIO, 
    413        get_size: Callable[[], Size], 
    414        term: str | None = None, 
    415        default_color_depth: ColorDepth | None = None, 
    416        enable_bell: bool = True, 
    417        enable_cpr: bool = True, 
    418    ) -> None: 
    419        assert all(hasattr(stdout, a) for a in ("write", "flush")) 
    420 
    421        self._buffer: list[str] = [] 
    422        self.stdout: TextIO = stdout 
    423        self.default_color_depth = default_color_depth 
    424        self._get_size = get_size 
    425        self.term = term 
    426        self.enable_bell = enable_bell 
    427        self.enable_cpr = enable_cpr 
    428 
    429        # Cache for escape codes. 
    430        self._escape_code_caches: dict[ColorDepth, _EscapeCodeCache] = { 
    431            ColorDepth.DEPTH_1_BIT: _EscapeCodeCache(ColorDepth.DEPTH_1_BIT), 
    432            ColorDepth.DEPTH_4_BIT: _EscapeCodeCache(ColorDepth.DEPTH_4_BIT), 
    433            ColorDepth.DEPTH_8_BIT: _EscapeCodeCache(ColorDepth.DEPTH_8_BIT), 
    434            ColorDepth.DEPTH_24_BIT: _EscapeCodeCache(ColorDepth.DEPTH_24_BIT), 
    435        } 
    436 
    437        # Keep track of whether the cursor shape was ever changed. 
    438        # (We don't restore the cursor shape if it was never changed - by 
    439        # default, we don't change them.) 
    440        self._cursor_shape_changed = False 
    441 
    442        # Don't hide/show the cursor when this was already done. 
    443        # (`None` means that we don't know whether the cursor is visible or 
    444        # not.) 
    445        self._cursor_visible: bool | None = None 
    446 
    447    @classmethod 
    448    def from_pty( 
    449        cls, 
    450        stdout: TextIO, 
    451        term: str | None = None, 
    452        default_color_depth: ColorDepth | None = None, 
    453        enable_bell: bool = True, 
    454    ) -> Vt100_Output: 
    455        """ 
    456        Create an Output class from a pseudo terminal. 
    457        (This will take the dimensions by reading the pseudo 
    458        terminal attributes.) 
    459        """ 
    460        fd: int | None 
    461        # Normally, this requires a real TTY device, but people instantiate 
    462        # this class often during unit tests as well. For convenience, we print 
    463        # an error message, use standard dimensions, and go on. 
    464        try: 
    465            fd = stdout.fileno() 
    466        except io.UnsupportedOperation: 
    467            fd = None 
    468 
    469        if not stdout.isatty() and (fd is None or fd not in cls._fds_not_a_terminal): 
    470            msg = "Warning: Output is not a terminal (fd=%r).\n" 
    471            sys.stderr.write(msg % fd) 
    472            sys.stderr.flush() 
    473            if fd is not None: 
    474                cls._fds_not_a_terminal.add(fd) 
    475 
    476        def get_size() -> Size: 
    477            # If terminal (incorrectly) reports its size as 0, pick a 
    478            # reasonable default.  See 
    479            # https://github.com/ipython/ipython/issues/10071 
    480            rows, columns = (None, None) 
    481 
    482            # It is possible that `stdout` is no longer a TTY device at this 
    483            # point. In that case we get an `OSError` in the ioctl call in 
    484            # `get_size`. See: 
    485            # https://github.com/prompt-toolkit/python-prompt-toolkit/pull/1021 
    486            try: 
    487                rows, columns = _get_size(stdout.fileno()) 
    488            except OSError: 
    489                pass 
    490            return Size(rows=rows or 24, columns=columns or 80) 
    491 
    492        return cls( 
    493            stdout, 
    494            get_size, 
    495            term=term, 
    496            default_color_depth=default_color_depth, 
    497            enable_bell=enable_bell, 
    498        ) 
    499 
    500    def get_size(self) -> Size: 
    501        return self._get_size() 
    502 
    503    def fileno(self) -> int: 
    504        "Return file descriptor." 
    505        return self.stdout.fileno() 
    506 
    507    def encoding(self) -> str: 
    508        "Return encoding used for stdout." 
    509        return self.stdout.encoding 
    510 
    511    def write_raw(self, data: str) -> None: 
    512        """ 
    513        Write raw data to output. 
    514        """ 
    515        self._buffer.append(data) 
    516 
    517    def write(self, data: str) -> None: 
    518        """ 
    519        Write text to output. 
    520        (Removes vt100 escape codes. -- used for safely writing text.) 
    521        """ 
    522        self._buffer.append(data.replace("\x1b", "?")) 
    523 
    524    def set_title(self, title: str) -> None: 
    525        """ 
    526        Set terminal title. 
    527        """ 
    528        if self.term not in ( 
    529            "linux", 
    530            "eterm-color", 
    531        ):  # Not supported by the Linux console. 
    532            self.write_raw( 
    533                "\x1b]2;{}\x07".format(title.replace("\x1b", "").replace("\x07", "")) 
    534            ) 
    535 
    536    def clear_title(self) -> None: 
    537        self.set_title("") 
    538 
    539    def erase_screen(self) -> None: 
    540        """ 
    541        Erases the screen with the background color and moves the cursor to 
    542        home. 
    543        """ 
    544        self.write_raw("\x1b[2J") 
    545 
    546    def enter_alternate_screen(self) -> None: 
    547        self.write_raw("\x1b[?1049h\x1b[H") 
    548 
    549    def quit_alternate_screen(self) -> None: 
    550        self.write_raw("\x1b[?1049l") 
    551 
    552    def enable_mouse_support(self) -> None: 
    553        self.write_raw("\x1b[?1000h") 
    554 
    555        # Enable mouse-drag support. 
    556        self.write_raw("\x1b[?1003h") 
    557 
    558        # Enable urxvt Mouse mode. (For terminals that understand this.) 
    559        self.write_raw("\x1b[?1015h") 
    560 
    561        # Also enable Xterm SGR mouse mode. (For terminals that understand this.) 
    562        self.write_raw("\x1b[?1006h") 
    563 
    564        # Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr 
    565        #       extensions. 
    566 
    567    def disable_mouse_support(self) -> None: 
    568        self.write_raw("\x1b[?1000l") 
    569        self.write_raw("\x1b[?1015l") 
    570        self.write_raw("\x1b[?1006l") 
    571        self.write_raw("\x1b[?1003l") 
    572 
    573    def erase_end_of_line(self) -> None: 
    574        """ 
    575        Erases from the current cursor position to the end of the current line. 
    576        """ 
    577        self.write_raw("\x1b[K") 
    578 
    579    def erase_down(self) -> None: 
    580        """ 
    581        Erases the screen from the current line down to the bottom of the 
    582        screen. 
    583        """ 
    584        self.write_raw("\x1b[J") 
    585 
    586    def reset_attributes(self) -> None: 
    587        self.write_raw("\x1b[0m") 
    588 
    589    def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: 
    590        """ 
    591        Create new style and output. 
    592 
    593        :param attrs: `Attrs` instance. 
    594        """ 
    595        # Get current depth. 
    596        escape_code_cache = self._escape_code_caches[color_depth] 
    597 
    598        # Write escape character. 
    599        self.write_raw(escape_code_cache[attrs]) 
    600 
    601    def disable_autowrap(self) -> None: 
    602        self.write_raw("\x1b[?7l") 
    603 
    604    def enable_autowrap(self) -> None: 
    605        self.write_raw("\x1b[?7h") 
    606 
    607    def enable_bracketed_paste(self) -> None: 
    608        self.write_raw("\x1b[?2004h") 
    609 
    610    def disable_bracketed_paste(self) -> None: 
    611        self.write_raw("\x1b[?2004l") 
    612 
    613    def reset_cursor_key_mode(self) -> None: 
    614        """ 
    615        For vt100 only. 
    616        Put the terminal in cursor mode (instead of application mode). 
    617        """ 
    618        # Put the terminal in cursor mode. (Instead of application mode.) 
    619        self.write_raw("\x1b[?1l") 
    620 
    621    def cursor_goto(self, row: int = 0, column: int = 0) -> None: 
    622        """ 
    623        Move cursor position. 
    624        """ 
    625        self.write_raw("\x1b[%i;%iH" % (row, column)) 
    626 
    627    def cursor_up(self, amount: int) -> None: 
    628        if amount == 0: 
    629            pass 
    630        elif amount == 1: 
    631            self.write_raw("\x1b[A") 
    632        else: 
    633            self.write_raw("\x1b[%iA" % amount) 
    634 
    635    def cursor_down(self, amount: int) -> None: 
    636        if amount == 0: 
    637            pass 
    638        elif amount == 1: 
    639            # Note: Not the same as '\n', '\n' can cause the window content to 
    640            #       scroll. 
    641            self.write_raw("\x1b[B") 
    642        else: 
    643            self.write_raw("\x1b[%iB" % amount) 
    644 
    645    def cursor_forward(self, amount: int) -> None: 
    646        if amount == 0: 
    647            pass 
    648        elif amount == 1: 
    649            self.write_raw("\x1b[C") 
    650        else: 
    651            self.write_raw("\x1b[%iC" % amount) 
    652 
    653    def cursor_backward(self, amount: int) -> None: 
    654        if amount == 0: 
    655            pass 
    656        elif amount == 1: 
    657            self.write_raw("\b")  # '\x1b[D' 
    658        else: 
    659            self.write_raw("\x1b[%iD" % amount) 
    660 
    661    def hide_cursor(self) -> None: 
    662        if self._cursor_visible in (True, None): 
    663            self._cursor_visible = False 
    664            self.write_raw("\x1b[?25l") 
    665 
    666    def show_cursor(self) -> None: 
    667        if self._cursor_visible in (False, None): 
    668            self._cursor_visible = True 
    669            self.write_raw("\x1b[?12l\x1b[?25h")  # Stop blinking cursor and show. 
    670 
    671    def set_cursor_shape(self, cursor_shape: CursorShape) -> None: 
    672        if cursor_shape == CursorShape._NEVER_CHANGE: 
    673            return 
    674 
    675        self._cursor_shape_changed = True 
    676        self.write_raw( 
    677            { 
    678                CursorShape.BLOCK: "\x1b[2 q", 
    679                CursorShape.BEAM: "\x1b[6 q", 
    680                CursorShape.UNDERLINE: "\x1b[4 q", 
    681                CursorShape.BLINKING_BLOCK: "\x1b[1 q", 
    682                CursorShape.BLINKING_BEAM: "\x1b[5 q", 
    683                CursorShape.BLINKING_UNDERLINE: "\x1b[3 q", 
    684            }.get(cursor_shape, "") 
    685        ) 
    686 
    687    def reset_cursor_shape(self) -> None: 
    688        "Reset cursor shape." 
    689        # (Only reset cursor shape, if we ever changed it.) 
    690        if self._cursor_shape_changed: 
    691            self._cursor_shape_changed = False 
    692 
    693            # Reset cursor shape. 
    694            self.write_raw("\x1b[0 q") 
    695 
    696    def flush(self) -> None: 
    697        """ 
    698        Write to output stream and flush. 
    699        """ 
    700        if not self._buffer: 
    701            return 
    702 
    703        data = "".join(self._buffer) 
    704        self._buffer = [] 
    705 
    706        flush_stdout(self.stdout, data) 
    707 
    708    def ask_for_cpr(self) -> None: 
    709        """ 
    710        Asks for a cursor position report (CPR). 
    711        """ 
    712        self.write_raw("\x1b[6n") 
    713        self.flush() 
    714 
    715    @property 
    716    def responds_to_cpr(self) -> bool: 
    717        if not self.enable_cpr: 
    718            return False 
    719 
    720        # When the input is a tty, we assume that CPR is supported. 
    721        # It's not when the input is piped from Pexpect. 
    722        if os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1": 
    723            return False 
    724 
    725        if is_dumb_terminal(self.term): 
    726            return False 
    727        try: 
    728            return self.stdout.isatty() 
    729        except ValueError: 
    730            return False  # ValueError: I/O operation on closed file 
    731 
    732    def bell(self) -> None: 
    733        "Sound bell." 
    734        if self.enable_bell: 
    735            self.write_raw("\a") 
    736            self.flush() 
    737 
    738    def get_default_color_depth(self) -> ColorDepth: 
    739        """ 
    740        Return the default color depth for a vt100 terminal, according to the 
    741        our term value. 
    742 
    743        We prefer 256 colors almost always, because this is what most terminals 
    744        support these days, and is a good default. 
    745        """ 
    746        if self.default_color_depth is not None: 
    747            return self.default_color_depth 
    748 
    749        term = self.term 
    750 
    751        if term is None: 
    752            return ColorDepth.DEFAULT 
    753 
    754        if is_dumb_terminal(term): 
    755            return ColorDepth.DEPTH_1_BIT 
    756 
    757        if term in ("linux", "eterm-color"): 
    758            return ColorDepth.DEPTH_4_BIT 
    759 
    760        return ColorDepth.DEFAULT