selectel / pyte

Simple VTXXX-compatible linux terminal emulator
http://pyte.readthedocs.org/
GNU Lesser General Public License v3.0
658 stars 102 forks source link

Implement scroll up and down #103

Open zblz opened 6 years ago

zblz commented 6 years ago

This PR implements handling of the SU and SD CSI commands in pyte.Stream and the corresponding scroll_up and scroll_down methods in pyte.Screen.

superbobry commented 6 years ago

Thanks for the contribution! Could you point me to documentation for these escape sequences?

zblz commented 6 years ago

They are (sparsely) documented here: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html

The most common use of this is when you call clear inside of a tmux session with a status bar. Instead of clearin everything and redrawing the status bar, tmux will scroll the non-status-bar lines out of the screen using scroll up. We also looked at vttest requirements to check its behaviour.

mvilim commented 5 years ago

Any chance this can be merged?

I ran into the lack of scroll support when using the Python curses module. Scrolling can be invoked implicitly in ncurses (I believe this is due to hardscroll.c, which appears to detect 'scroll-like' updates, even if a scroll was not explicitly requested.)

Here is a script that demonstrates psuedo-scroll behavior. With TERM set to a terminal with scrolling capability (e.g. xterm-256color), this script fails to update the 0, 0 cell to 1 (i.e. the first assert should fail) because ncurses decides to send a scroll command. With TERM set to linux the script passes.

import curses
import pyte
import os

def psuedo_scroll(stdscr):
    stdscr.addstr(0, 0, '0')
    stdscr.addstr(2, 0, '1')
    stdscr.refresh()
    stdscr.addstr(0, 0, '1')
    stdscr.addstr(2, 0, '2')
    stdscr.refresh()

pid, fd = os.forkpty()
if pid == 0:
    curses.wrapper(psuedo_scroll)
else:
    screen = pyte.Screen(80, 24)
    stream = pyte.ByteStream(screen)
    # pass through bytes directly
    stream.select_other_charset('@')
    while True:
        try:
            output = os.read(fd, 1024)
            stream.feed(output)
        except OSError:
            break
    assert(screen.buffer[0][0].data == '1')
    assert(screen.buffer[2][0].data == '2')

I don't know of any easy way to set the term capabilities with greater granularlity than TERM (to disable scrolling but leave support for other features (e.g. color)), so I would prefer to not use TERM=linux. Additionally, merging this would save some time for others who run into the issue (it took some debugging to trace observed incorrect terminal state to the scroll behavior).

I tested this pull request and it solves both my original issue and causes the above test to pass.