From a39db1ed4fc8b6e66779f9ae5b9341763c9d4c1f Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Thu, 5 Jan 2017 20:26:13 +0100 Subject: improve corner cases related to resizing and mode switching A few operations would previously fail unexpectedly: * Running tcvt with many columns and a small terminal width would crash instead of falling back to a simple screen. * After switching the mode, column ordering would be lost. * Switching the mode from simple to columns after resizing could result in a crash. Instead of collecting the screen options and passing them down individually, the Terminal class now asks for a preconfigured screen factory. This should eliminate future potential for similar mistakes. --- tcvt.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/tcvt.py b/tcvt.py index 3cdb957..c30f4e0 100755 --- a/tcvt.py +++ b/tcvt.py @@ -305,35 +305,34 @@ simple_characters = bytearray( b'\xb4\xb6\xb7\xc3\xc4\xd6\xdc\xe4\xe9\xfc\xf6') class Terminal: - def __init__(self, acsc, columns, reverse=False): + def __init__(self, acsc, screenfactory): self.mode = (self.feed_simple,) self.realscreen = None self.screen = None self.fg = self.bg = 0 self.graphics_font = False - self.graphics_chars = acsc # really initialized after + self.graphics_chars = acsc # really initialized in __enter__ self.lastchar = ord(b' ') - self.columns = columns - self.reverse = reverse + self.screenfactory = screenfactory self.need_refresh = None - def switchmode(self): - if isinstance(self.screen, Columns): + def makescreen(self, switch=False): + try: + if isinstance(self.screen, Simple) ^ switch: + raise BadWidth("use simple screen") + self.screen = self.screenfactory(self.realscreen) + except BadWidth: self.screen = Simple(self.realscreen) - else: - self.screen = Columns(self.realscreen, self.columns) self.request_refresh() + def switchmode(self): + self.makescreen(True) + def resized(self): # The refresh call causes curses to notice the new dimensions. self.realscreen.refresh() self.realscreen.clear() - try: - self.screen = Columns(self.realscreen, self.columns, - reverse=self.reverse) - except BadWidth: - self.screen = Simple(self.realscreen) - self.request_refresh() + self.makescreen() def resizepty(self, ptyfd): ym, xm = self.screen.getmaxyx() @@ -366,8 +365,7 @@ class Terminal: self.realscreen.keypad(1) curses.start_color() init_color_pairs() - self.screen = Columns(self.realscreen, self.columns, - reverse=self.reverse) + self.makescreen() curses.noecho() curses.raw() self.graphics_chars = compose_dicts(self.graphics_chars, acs_map()) @@ -804,10 +802,13 @@ def main(): options, args = parser.parse_args() keymapping, acsc = compute_keymap(symbolic_keymapping) + def screenfactory(realscreen): + return Columns(realscreen, options.columns, reverse=options.reverse) + process = ForkPty(args or [os.environ["SHELL"]], dict(TERM="ansi")) try: with process as masterfd: - with Terminal(acsc, options.columns, reverse=options.reverse) as t: + with Terminal(acsc, screenfactory) as t: t.resizepty(masterfd) process.start() while True: -- cgit v1.2.3