diff options
author | Helmut Grohne <helmut@subdivi.de> | 2017-01-05 20:26:13 +0100 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2017-01-05 20:26:13 +0100 |
commit | a39db1ed4fc8b6e66779f9ae5b9341763c9d4c1f (patch) | |
tree | a4704c7c7f4f92037e19dd109b383ee570173d83 | |
parent | b8fb407411a4e5c0ed52ec33dc3c52e68837e341 (diff) | |
download | tcvt-a39db1ed4fc8b6e66779f9ae5b9341763c9d4c1f.tar.gz |
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.
-rwxr-xr-x | tcvt.py | 35 |
1 files changed, 18 insertions, 17 deletions
@@ -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: |