Exemple #1
0
def create_output(stdout: Optional[TextIO] = None) -> Output:
    """
    Return an :class:`~prompt_toolkit_dev.output.Output` instance for the command
    line.

    :param stdout: The stdout object
    """
    if stdout is None:
        # By default, render to stdout. If the output is piped somewhere else,
        # render to stderr. The prompt_toolkit_dev render output is not meant to be
        # consumed by something other then a terminal, so this is a reasonable
        # default.
        if sys.stdout.isatty():
            stdout = sys.stdout
        else:
            stdout = sys.stderr

    if is_windows():
        from .conemu import ConEmuOutput
        from .win32 import Win32Output
        from .windows10 import is_win_vt100_enabled, Windows10_Output

        if is_win_vt100_enabled():
            return cast(Output, Windows10_Output(stdout))
        if is_conemu_ansi():
            return cast(Output, ConEmuOutput(stdout))
        else:
            return Win32Output(stdout)
    else:
        from .vt100 import Vt100_Output
        return Vt100_Output.from_pty(stdout,
                                     term=get_term_environment_variable())
 def height_is_known(self) -> bool:
     """
     True when the height from the cursor until the bottom of the terminal
     is known. (It's often nicer to draw bottom toolbars only if the height
     is known, in order to avoid flickering when the CPR response arrives.)
     """
     return self.full_screen or self._min_available_height > 0 or \
         is_windows()  # On Windows, we don't have to wait for a CPR.
def create_input(stdin: Optional[TextIO] = None) -> Input:
    stdin = stdin or sys.stdin

    if is_windows():
        from .win32 import Win32Input
        return Win32Input(stdin)
    else:
        from .vt100 import Vt100Input
        return Vt100Input(stdin)
    def request_absolute_cursor_position(self) -> None:
        """
        Get current cursor position.

        We do this to calculate the minimum available height that we can
        consume for rendering the prompt. This is the available space below te
        cursor.

        For vt100: Do CPR request. (answer will arrive later.)
        For win32: Do API call. (Answer comes immediately.)
        """
        # Only do this request when the cursor is at the top row. (after a
        # clear or reset). We will rely on that in `report_absolute_cursor_row`.
        assert self._cursor_pos.y == 0

        # In full-screen mode, always use the total height as min-available-height.
        if self.full_screen:
            self._min_available_height = self.output.get_size().rows

        # For Win32, we have an API call to get the number of rows below the
        # cursor.
        elif is_windows():
            self._min_available_height = self.output.get_rows_below_cursor_position(
            )

        # Use CPR.
        else:
            if self.cpr_support == CPR_Support.NOT_SUPPORTED:
                return

            def do_cpr() -> None:
                # Asks for a cursor position report (CPR).
                self._waiting_for_cpr_futures.append(Future())
                self.output.ask_for_cpr()

            if self.cpr_support == CPR_Support.SUPPORTED:
                do_cpr()

            # If we don't know whether CPR is supported, only do a request if
            # none is pending, and test it, using a timer.
            elif self.cpr_support == CPR_Support.UNKNOWN and not self.waiting_for_cpr:
                do_cpr()

                async def timer() -> None:
                    await sleep(self.CPR_TIMEOUT)

                    # Not set in the meantime -> not supported.
                    if self.cpr_support == CPR_Support.UNKNOWN:
                        self.cpr_support = CPR_Support.NOT_SUPPORTED

                        if self.cpr_not_supported_callback:
                            # Make sure to call this callback in the main thread.
                            self.cpr_not_supported_callback()

                get_app().create_background_task(timer())
def create_pipe_input() -> Input:
    """
    Create an input pipe.
    This is mostly useful for unit testing.
    """
    if is_windows():
        from .win32_pipe import Win32PipeInput
        return Win32PipeInput()
    else:
        from .posix_pipe import PosixPipeInput
        return PosixPipeInput()
    def select(self, timeout=None):
        # If there are tasks in the current event loop,
        # don't run the input hook.
        if len(get_event_loop()._ready) > 0:
            return self.selector.select(timeout=timeout)

        ready = False
        result = None

        # Run selector in other thread.
        def run_selector() -> None:
            nonlocal ready, result
            result = self.selector.select(timeout=timeout)
            os.write(self._w, b'x')
            ready = True
        th = threading.Thread(target=run_selector)
        th.start()

        def input_is_ready() -> bool:
            return ready

        # Call inputhook.
        # The inputhook function is supposed to return when our selector
        # becomes ready. The inputhook can do that by registering the fd in its
        # own loop, or by checking the `input_is_ready` function regularly.
        self.inputhook(InputHookContext(self._r, input_is_ready))

        # Flush the read end of the pipe.
        try:
            # Before calling 'os.read', call select.select. This is required
            # when the gevent monkey patch has been applied. 'os.read' is never
            # monkey patched and won't be cooperative, so that would block all
            # other select() calls otherwise.
            # See: http://www.gevent.org/gevent.os.html

            # Note: On Windows, this is apparently not an issue.
            #       However, if we would ever want to add a select call, it
            #       should use `windll.kernel32.WaitForMultipleObjects`,
            #       because `select.select` can't wait for a pipe on Windows.
            if not is_windows():
                select.select([self._r], [], [], None)

            os.read(self._r, 1024)
        except OSError:
            # This happens when the window resizes and a SIGWINCH was received.
            # We get 'Error: [Errno 4] Interrupted system call'
            # Just ignore.
            pass

        # Wait for the real selector to be done.
        th.join()
        return result
    def reset(self,
              _scroll: bool = False,
              leave_alternate_screen: bool = True) -> None:

        # Reset position
        self._cursor_pos = Point(x=0, y=0)

        # Remember the last screen instance between renderers. This way,
        # we can create a `diff` between two screens and only output the
        # difference. It's also to remember the last height. (To show for
        # instance a toolbar at the bottom position.)
        self._last_screen: Optional[Screen] = None
        self._last_size: Optional[Size] = None
        self._last_style: Optional[str] = None

        # Default MouseHandlers. (Just empty.)
        self.mouse_handlers = MouseHandlers()

        #: Space from the top of the layout, until the bottom of the terminal.
        #: We don't know this until a `report_absolute_cursor_row` call.
        self._min_available_height = 0

        # In case of Windows, also make sure to scroll to the current cursor
        # position. (Only when rendering the first time.)
        if is_windows() and _scroll:
            self.output.scroll_buffer_to_prompt()

        # Quit alternate screen.
        if self._in_alternate_screen and leave_alternate_screen:
            self.output.quit_alternate_screen()
            self._in_alternate_screen = False

        # Disable mouse support.
        if self._mouse_support_enabled:
            self.output.disable_mouse_support()
            self._mouse_support_enabled = False

        # Disable bracketed paste.
        if self._bracketed_paste_enabled:
            self.output.disable_bracketed_paste()
            self._bracketed_paste_enabled = False

        # Flush output. `disable_mouse_support` needs to write to stdout.
        self.output.flush()
Exemple #8
0
    def _(event: E) -> None:
        """
        Handling of mouse events for Windows.
        """
        assert is_windows()  # This key binding should only exist for Windows.

        # Parse data.
        event_type, x, y = event.data.split(';')
        x = int(x)
        y = int(y)

        # Make coordinates absolute to the visible part of the terminal.
        screen_buffer_info = event.app.renderer.output.get_win32_screen_buffer_info(
        )
        rows_above_cursor = screen_buffer_info.dwCursorPosition.Y - event.app.renderer._cursor_pos.y
        y -= rows_above_cursor

        # Call the mouse event handler.
        handler = event.app.renderer.mouse_handlers.mouse_handlers[x, y]
        handler(MouseEvent(position=Point(x=x, y=y), event_type=event_type))
Exemple #9
0
    def default(cls, term: str = '') -> 'ColorDepth':
        """
        If the user doesn't specify a color depth, use this as a default.
        """
        if term in ('linux', 'eterm-color'):
            return cls.DEPTH_4_BIT

        # For now, always use 4 bit color on Windows 10 by default, even when
        # vt100 escape sequences with ENABLE_VIRTUAL_TERMINAL_PROCESSING are
        # supported. We don't have a reliable way yet to know whether our
        # console supports true color or only 4-bit.
        if is_windows() and 'PROMPT_TOOLKIT_COLOR_DEPTH' not in os.environ:
            return cls.DEPTH_4_BIT

        # Check the `PROMPT_TOOLKIT_COLOR_DEPTH` environment variable.
        all_values = [i.value for i in ColorDepth]

        if os.environ.get('PROMPT_TOOLKIT_COLOR_DEPTH') in all_values:
            return cls(os.environ['PROMPT_TOOLKIT_COLOR_DEPTH'])

        return cls.DEPTH_8_BIT
def is_win_vt100_enabled() -> bool:
    """
    Returns True when we're running Windows and VT100 escape sequences are
    supported.
    """
    if not is_windows():
        return False

    hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)

    # Get original console mode.
    original_mode = DWORD(0)
    windll.kernel32.GetConsoleMode(hconsole, byref(original_mode))

    try:
        # Try to enable VT100 sequences.
        result = windll.kernel32.SetConsoleMode(
            hconsole,
            DWORD(ENABLE_PROCESSED_INPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING))

        return result == 1
    finally:
        windll.kernel32.SetConsoleMode(hconsole, original_mode)
Exemple #11
0
        return b''.join(self._data)

    def flush(self):
        pass

    def isatty(self):
        return True

    def fileno(self):
        # File descriptor is not used for printing formatted text.
        # (It is only needed for getting the terminal size.)
        return -1


@pytest.mark.skipif(
    is_windows(), reason="Doesn't run on Windows yet.")
def test_print_formatted_text():
    f = _Capture()
    pt_print([('', 'hello'), ('', 'world')], file=f)
    assert b'hello' in f.data
    assert b'world' in f.data

@pytest.mark.skipif(
    is_windows(), reason="Doesn't run on Windows yet.")
def test_print_formatted_text_backslash_r():
    f = _Capture()
    pt_print('hello\r\n', file=f)
    assert b'hello' in f.data

@pytest.mark.skipif(
    is_windows(), reason="Doesn't run on Windows yet.")