Exemplo n.º 1
0
    def set_margins(self, top=None, bottom=None):
        """Selects top and bottom margins for the scrolling region.
        Margins determine which screen lines move during scrolling
        (see :meth:`index` and :meth:`reverse_index`). Characters added
        outside the scrolling region do not cause the screen to scroll.
        :param int top: the smallest line number that is scrolled.
        :param int bottom: the biggest line number that is scrolled.
        """
        if top is None and bottom is None:
            return

        margins = self.margins or Margins(0, self.lines - 1)

        top = margins.top if top is None else top - 1
        bottom = margins.bottom if bottom is None else bottom - 1

        # Arguments are 1-based, while :attr:`margins` are zero based --
        # so we have to decrement them by one. We also make sure that
        # both of them is bounded by [0, lines - 1].
        top = max(0, min(top, self.lines - 1))
        bottom = max(0, min(bottom, self.lines - 1))

        # Even though VT102 and VT220 require DECSTBM to ignore regions
        # of width less than 2, some programs (like aptitude for example)
        # rely on it. Practicality beats purity.
        if bottom - top >= 1:
            self.margins = Margins(top, bottom)

            # The cursor moves to the home position when the top and
            # bottom margins of the scrolling region (DECSTBM) changes.
            self.cursor_position()
Exemplo n.º 2
0
    def resize(self, lines=None, columns=None):
        """Resize the screen to the given dimensions keeping the history intact.

        If the requested screen size has more lines than the existing
        screen, lines will be added at the bottom. If the requested
        size has less lines than the existing screen, lines will be
        clipped at the top of the screen. Similarly, if the existing
        screen has less columns than the requested screen, columns will
        be added at the right, and if it has more -- columns will be
        clipped at the right.

        .. note:: According to `xterm`, we should also reset origin
                  mode and screen margins, see ``xterm/screen.c:1761``.
                  -> but we don't do this here
        :param int lines: number of lines in the new screen.
        :param int columns: number of columns in the new screen.
        """
        self._flush_events()

        old_lines = self.lines

        self.lines = (lines or self.lines)
        self.columns = (columns or self.columns)

        if mo.DECALTBUF in self.mode and False:
            # home cursor
            self.reset_mode(mo.DECOM)
        else:
            # cursor: make sure that it 'stays' on its current line
            cursor_delta = self.linecontainer.resize(old_lines, self.lines,
                                                     self.columns)
            self.cursor.y += cursor_delta
            self.cursor.x = min(max(self.cursor.x, 0), self.columns - 1)

        self.margins = Margins(0, self.lines - 1)
Exemplo n.º 3
0
    def delete_lines(self, count=None):
        """Deletes the indicated # of lines, starting at line with
        cursor. As lines are deleted, lines displayed below cursor
        move up. Lines added to bottom of screen have spaces with same
        character attributes as last line moved up.

        :param int count: number of lines to delete.
        """
        count = count or 1
        top, bottom = self.margins or Margins(0, self.lines - 1)
        line_offset = self.line_offset
        pt_cursor_position = self.pt_cursor_position

        # If cursor is outside scrolling margins it -- do nothin'.
        if top <= pt_cursor_position.y - line_offset <= bottom:
            data_buffer = self.data_buffer

            # Iterate from the cursor Y position until the end of the visible input.
            for line in range(pt_cursor_position.y - line_offset, bottom + 1):
                # When 'x' lines further are out of the margins, replace by an empty line,
                # Otherwise copy the line from there.
                if line + count > bottom:
                    data_buffer.pop(line + line_offset, None)
                else:
                    data_buffer[line +
                                line_offset] = self.data_buffer[line + count +
                                                                line_offset]
Exemplo n.º 4
0
    def insert_lines(self, count=None):
        """Inserts the indicated # of lines at line with cursor. Lines
        displayed **at** and below the cursor move down. Lines moved
        past the bottom margin are lost.

        :param count: number of lines to delete.
        """
        count = count or 1
        top, bottom = self.margins or Margins(0, self.lines - 1)

        data_buffer = self.data_buffer
        line_offset = self.line_offset
        pt_cursor_position = self.pt_cursor_position

        # If cursor is outside scrolling margins it -- do nothing.
        if top <= pt_cursor_position.y - self.line_offset <= bottom:
            for line in range(bottom, pt_cursor_position.y - line_offset, -1):
                if line - count < top:
                    data_buffer.pop(line + line_offset, None)
                else:
                    data_buffer[line +
                                line_offset] = data_buffer[line + line_offset -
                                                           count]
                    data_buffer.pop(line + line_offset - count, None)

            self.carriage_return()
Exemplo n.º 5
0
 def scroll_down(self, n):
     top, bottom = self.margins or Margins(0, self.lines - 1)
     for y in reversed(range(top, bottom + 1)):
         if y - n < top:
             self.buffer[y].clear()
         else:
             self.buffer[y] = copy(self.buffer[y - n])
     self.dirty.update(range(self.lines))
Exemplo n.º 6
0
 def scroll_up(self, n):
     top, bottom = self.margins or Margins(0, self.lines - 1)
     for y in range(top, bottom + 1):
         if y + n > bottom:
             self.buffer[y].clear()
         else:
             self.buffer[y] = copy(self.buffer[y + n])
     self.dirty.update(range(self.lines))
Exemplo n.º 7
0
 def index(self):
     top, bottom = self.margins or Margins(0, self.lines - 1)
     if self.cursor.y == bottom:
         # TODO: mark only the lines within margins?
         self.dirty.update(range(self.lines))
         for y in range(top, bottom):
             self.buffer[y] = self.buffer[y + 1]
         self.buffer.pop(bottom, None)
     else:
         self.cursor_down()
Exemplo n.º 8
0
 def reverse_index(self):
     max_line = self.line_buffer.max_line
     min_line = self.line_buffer.min_line
     top, bottom = self.margins or Margins(min_line, max_line)
     if self.cursor.y == top:
         self.dirty.update(range(min_line, max_line + 1))
         for y in range(bottom, top, -1):
             self.buffer[y] = self.buffer[y - 1]
         self.buffer.pop(top, None)
     else:
         self.cursor_up()
Exemplo n.º 9
0
    def reverse_index(self):
        margins = self.margins or Margins(0, self.lines - 1)
        top, bottom = margins
        line_offset = self.line_offset

        # When scrolling over the full screen -> keep history.
        if self.pt_cursor_position.y - line_offset == top:
            for i in range(bottom - 1, top - 1, -1):
                self.data_buffer[i + line_offset + 1] = self.data_buffer[i + line_offset]
                del self.data_buffer[i + line_offset]
        else:
            self.cursor_up()
Exemplo n.º 10
0
    def _reset_offset_and_margins(self):
        """
        Recalculate offset and move cursor (make sure that the bottom is
        visible.)
        """
        self.margins = Margins(0, self.lines - 1)

        if self._in_alternate_screen:
            self.line_offset = 0

        elif self.data_buffer:
            self.line_offset = max(0, self.max_y - self.lines + 1)
Exemplo n.º 11
0
    def cursor_down(self, count=None):
        """Moves cursor down the indicated # of lines in same column.
        Cursor stops at bottom margin.

        :param int count: number of lines to skip.
        """
        cursor_position = self.pt_cursor_position
        margins = self.margins or Margins(0, self.lines - 1)

        # Ensure bounds.
        # (Following code is faster than calling `self.ensure_bounds`.)
        _, bottom = margins
        cursor_position.y = min(cursor_position.y + (count or 1),
                                bottom + self.line_offset + 1)

        self.max_y = max(self.max_y, cursor_position.y)
Exemplo n.º 12
0
    def _reset_screen(self):
        """ Reset the Screen content. (also called when switching from/to
        alternate buffer. """
        self.pt_screen = Screen(default_char=Char(' ', DEFAULT_TOKEN))

        self.pt_screen.cursor_position = CursorPosition(0, 0)
        self.pt_screen.show_cursor = True

        self.data_buffer = self.pt_screen.data_buffer

        self._attrs = Attrs(color=None, bgcolor=None, bold=False,
                            underline=False, italic=False, blink=False, reverse=False)

        self.margins = Margins(0, self.lines - 1)

        self.line_offset = 0  # Index of the line that's currently displayed on top.
        self.max_y = 0  # Max 'y' position to which is written.
Exemplo n.º 13
0
    def reset(self):
        """Resets the terminal to its initial state.

        * Scroll margins are reset to screen boundaries.
        * Cursor is moved to home location -- ``(0, 0)`` and its
          attributes are set to defaults (see :attr:`default_char`).
        * Screen is cleared -- each character is reset to
          :attr:`default_char`.
        * Tabstops are reset to "every eight columns".

        .. note::

           Neither VT220 nor VT102 manuals mentioned that terminal modes
           and tabstops should be reset as well, thanks to
           :manpage:`xterm` -- we now know that.
        """
        self._flush_events()
        self.linecontainer.reset(self.lines)

        if self.iframe_mode:
            self.iframe_leave()

        self.mode = set([mo.DECAWM, mo.DECTCEM, mo.LNM, mo.DECTCEM])
        self.margins = Margins(0, self.lines - 1)

        # According to VT220 manual and ``linux/drivers/tty/vt.c``
        # the default G0 charset is latin-1, but for reasons unknown
        # latin-1 breaks ascii-graphics; so G0 defaults to cp437.
        self.charset = 0
        self.g0_charset = cs.IBMPC_MAP
        self.g1_charset = cs.VT100_MAP

        # From ``man terminfo`` -- "... hardware tabs are initially
        # set every `n` spaces when the terminal is powered up. Since
        # we aim to support VT102 / VT220 and linux -- we use n = 8.
        self.tabstops = set(range(7, self.columns, 8))

        self.cursor = Cursor(0, 0)
        self.cursor_position()