From 9b544f83e0a0743e2f94c64f548a06c375a5e942 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 11 Nov 2016 18:35:26 +0530 Subject: [PATCH] Move change tracking into the screen class --- kitty/boss.py | 20 ++++------ kitty/screen.py | 14 +++++-- kitty_tests/__init__.py | 10 ++--- kitty_tests/screen.py | 82 ++++++++++++++++++++--------------------- 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 3c38ff263..11436b156 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -20,7 +20,6 @@ from pyte import modes as mo from .char_grid import CharGrid from .keys import interpret_text_event, interpret_key_event from .screen import Screen -from .tracker import ChangeTracker from .utils import resize_pty, create_pty @@ -53,8 +52,7 @@ class Boss(Thread): self.queue_action(self.initialize) self.profile = args.profile self.window, self.opts = window, opts - self.tracker = ChangeTracker() - self.screen = Screen(self.opts, self.tracker, self) + self.screen = Screen(self.opts, self) self.char_grid = CharGrid(self.screen, opts, window_width, window_height) sclass = DebugStream if args.dump_commands else Stream self.stream = sclass(self.screen) @@ -179,10 +177,11 @@ class Boss(Thread): dispatch[r]() if writers: self.write_ready() - if self.tracker.dirty: - self.mark_dirtied() - if self.pending_update_screen is not None and monotonic() > self.pending_update_screen: - self.apply_update_screen() + if self.pending_update_screen is not None: + if monotonic() > self.pending_update_screen: + self.apply_update_screen() + elif self.screen.is_dirty: + self.pending_update_screen = monotonic() + self.SCREEN_UPDATE_DELAY def close(self): if not self.shutting_down: @@ -234,14 +233,9 @@ class Boss(Thread): def queue_write(self, data): self.write_buf = memoryview(self.write_buf.tobytes() + data) - def mark_dirtied(self): - # Batch screen updates - if self.pending_update_screen is None: - self.pending_update_screen = monotonic() + self.SCREEN_UPDATE_DELAY - def apply_update_screen(self): self.pending_update_screen = None - changes = self.tracker.consolidate_changes() + changes = self.screen.consolidate_changes() self.char_grid.update_cell_data(changes) glfw.glfwPostEmptyEvent() diff --git a/kitty/screen.py b/kitty/screen.py index a28974640..e3db249a0 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -11,6 +11,7 @@ from typing import Sequence from pyte import charsets as cs, graphics as g, modes as mo from .utils import wcwidth, is_simple_string, sanitize_title from .unicode import ignore_pat +from .tracker import ChangeTracker from .fast_data_types import LineBuf, REVERSE, Cursor @@ -55,21 +56,28 @@ class Screen: tracker_callbacks = 'cursor_changed update_screen update_line_range update_cell_range line_added_to_history'.split() _notify_cursor_position = True - def __init__(self, opts, tracker, callbacks=None, columns: int=80, lines: int=24): - for attr in self.tracker_callbacks: - setattr(self, attr, getattr(tracker, attr)) + def __init__(self, opts, callbacks=None, columns: int=80, lines: int=24): for attr in default_callbacks: setattr(self, attr, getattr(callbacks, attr, default_callbacks[attr])) self.main_savepoints, self.alt_savepoints = deque(), deque() self.savepoints = self.main_savepoints self.columns = columns self.lines = lines + self.tracker = ChangeTracker() + for attr in self.tracker_callbacks: + setattr(self, attr, getattr(self.tracker, attr)) + self.consolidate_changes = self.tracker.consolidate_changes + self.reset_dirty = self.tracker.reset sz = max(1000, opts.scrollback_lines) self.tophistorybuf = deque(maxlen=sz) self.main_linebuf, self.alt_linebuf = LineBuf(self.lines, self.columns), LineBuf(self.lines, self.columns) self.linebuf = self.main_linebuf self.reset(notify=False) + @property + def is_dirty(self): + return self.tracker.dirty + def apply_opts(self, opts): sz = max(1000, opts.scrollback_lines) if sz != self.tophistorybuf.maxlen: diff --git a/kitty_tests/__init__.py b/kitty_tests/__init__.py index c935c6749..0bceb910e 100644 --- a/kitty_tests/__init__.py +++ b/kitty_tests/__init__.py @@ -6,7 +6,6 @@ from collections import defaultdict from unittest import TestCase from kitty.screen import Screen -from kitty.tracker import ChangeTracker from kitty.config import defaults from kitty.fast_data_types import LineBuf, Cursor @@ -34,10 +33,9 @@ class BaseTest(TestCase): ae = TestCase.assertEqual def create_screen(self, cols=5, lines=5, history_size=5): - t = ChangeTracker() opts = defaults._replace(scrollback_lines=history_size) - s = Screen(opts, t, columns=cols, lines=lines) - return s, t + s = Screen(opts, columns=cols, lines=lines) + return s def assertEqualAttributes(self, c1, c2): x1, y1, c1.x, c1.y = c1.x, c1.y, 0, 0 @@ -47,8 +45,8 @@ class BaseTest(TestCase): finally: c1.x, c1.y, c2.x, c2.y = x1, y1, x2, y2 - def assertChanges(self, t, ignore='', **expected_changes): - actual_changes = t.consolidate_changes() + def assertChanges(self, s, ignore='', **expected_changes): + actual_changes = s.consolidate_changes() ignore = frozenset(ignore.split()) for k, v in actual_changes.items(): if isinstance(v, defaultdict): diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index 5f90065d4..6b0467e36 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -11,11 +11,11 @@ class TestScreen(BaseTest): def test_draw_fast(self): # Test in line-wrap, non-insert mode - s, t = self.create_screen() + s = self.create_screen() s.draw(b'a' * 5) self.ae(str(s.line(0)), 'a' * 5) self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0) - self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)}) s.draw(b'b' * 7) self.assertTrue(s.linebuf.is_continued(1)) self.assertTrue(s.linebuf.is_continued(2)) @@ -23,124 +23,124 @@ class TestScreen(BaseTest): self.ae(str(s.line(1)), 'b' * 5) self.ae(str(s.line(2)), 'b' * 2 + ' ' * 3) self.ae(s.cursor.x, 2), self.ae(s.cursor.y, 2) - self.assertChanges(t, ignore='cursor', cells={1: ((0, 4),), 2: ((0, 1),)}) + self.assertChanges(s, ignore='cursor', cells={1: ((0, 4),), 2: ((0, 1),)}) s.draw(b'c' * 15) self.ae(str(s.line(0)), 'b' * 5) self.ae(str(s.line(1)), 'bbccc') # Now test without line-wrap - s.reset(), t.reset() + s.reset(), s.reset_dirty() s.reset_mode(mo.DECAWM) s.draw(b'0123456789') self.ae(str(s.line(0)), '01239') self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0) - self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)}) s.draw(b'ab') self.ae(str(s.line(0)), '0123b') self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0) - self.assertChanges(t, ignore='cursor', cells={0: ((4, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((4, 4),)}) # Now test in insert mode - s.reset(), t.reset() + s.reset(), s.reset_dirty() s.set_mode(mo.IRM) s.draw(b'12345' * 5) s.cursor_back(5) self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 4) - t.reset() + s.reset_dirty() s.draw(b'ab') self.ae(str(s.line(4)), 'ab123') self.ae((s.cursor.x, s.cursor.y), (2, 4)) - self.assertChanges(t, ignore='cursor', cells={4: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={4: ((0, 4),)}) def test_draw_char(self): # Test in line-wrap, non-insert mode - s, t = self.create_screen() + s = self.create_screen() s.draw('ココx'.encode('utf-8')) self.ae(str(s.line(0)), 'ココx') self.ae(tuple(map(s.line(0).width, range(5))), (2, 0, 2, 0, 1)) self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0) - self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)}) s.draw('ニチハ'.encode('utf-8')) self.ae(str(s.line(0)), 'ココx') self.ae(str(s.line(1)), 'ニチ ') self.ae(str(s.line(2)), 'ハ ') - self.assertChanges(t, ignore='cursor', cells={1: ((0, 3),), 2: ((0, 1),)}) + self.assertChanges(s, ignore='cursor', cells={1: ((0, 3),), 2: ((0, 1),)}) self.ae(s.cursor.x, 2), self.ae(s.cursor.y, 2) s.draw('Ƶ̧\u0308'.encode('utf-8')) self.ae(str(s.line(2)), 'ハƵ̧\u0308 ') self.ae(s.cursor.x, 3), self.ae(s.cursor.y, 2) - self.assertChanges(t, ignore='cursor', cells={2: ((2, 2),)}) + self.assertChanges(s, ignore='cursor', cells={2: ((2, 2),)}) s.draw(b'xy'), s.draw('\u0306'.encode('utf-8')) self.ae(str(s.line(2)), 'ハƵ̧\u0308xy\u0306') self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 2) - self.assertChanges(t, ignore='cursor', cells={2: ((3, 4),)}) + self.assertChanges(s, ignore='cursor', cells={2: ((3, 4),)}) s.draw(b'c' * 15) self.ae(str(s.line(0)), 'ニチ ') # Now test without line-wrap - s.reset(), t.reset() + s.reset(), s.reset_dirty() s.reset_mode(mo.DECAWM) s.draw('0\u030612345\u03066789\u0306'.encode('utf-8')) self.ae(str(s.line(0)), '0\u03061239\u0306') self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0) - self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)}) s.draw('ab\u0306'.encode('utf-8')) self.ae(str(s.line(0)), '0\u0306123b\u0306') self.ae(s.cursor.x, 5), self.ae(s.cursor.y, 0) - self.assertChanges(t, ignore='cursor', cells={0: ((4, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((4, 4),)}) # Now test in insert mode - s.reset(), t.reset() + s.reset(), s.reset_dirty() s.set_mode(mo.IRM) s.draw('1\u03062345'.encode('utf-8') * 5) s.cursor_back(5) self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 4) - t.reset() + s.reset_dirty() s.draw('a\u0306b'.encode('utf-8')) self.ae(str(s.line(4)), 'a\u0306b1\u030623') self.ae((s.cursor.x, s.cursor.y), (2, 4)) - self.assertChanges(t, ignore='cursor', cells={4: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={4: ((0, 4),)}) def test_char_manipulation(self): - s, t = self.create_screen() + s = self.create_screen() def init(): - s.reset(), t.reset() + s.reset(), s.reset_dirty() s.draw(b'abcde') s.cursor.bold = True s.cursor_back(4) - t.reset() + s.reset_dirty() self.ae(s.cursor.x, 1) init() s.insert_characters(2) self.ae(str(s.line(0)), 'a bc') self.assertTrue(s.line(0).cursor_from(1).bold) - self.assertChanges(t, ignore='cursor', cells={0: ((1, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((1, 4),)}) s.cursor_back(1) s.insert_characters(20) self.ae(str(s.line(0)), ' ') - self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)}) s.draw('xココ'.encode('utf-8')) s.cursor_back(5) - t.reset() + s.reset_dirty() s.insert_characters(1) self.ae(str(s.line(0)), ' xコ ') - self.assertChanges(t, ignore='cursor', cells={0: ((0, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((0, 4),)}) init() s.delete_characters(2) self.ae(str(s.line(0)), 'ade ') self.assertTrue(s.line(0).cursor_from(4).bold) self.assertFalse(s.line(0).cursor_from(2).bold) - self.assertChanges(t, ignore='cursor', cells={0: ((1, 4),)}) + self.assertChanges(s, ignore='cursor', cells={0: ((1, 4),)}) init() s.erase_characters(2) self.ae(str(s.line(0)), 'a de') self.assertTrue(s.line(0).cursor_from(1).bold) self.assertFalse(s.line(0).cursor_from(4).bold) - self.assertChanges(t, cells={0: ((1, 2),)}) + self.assertChanges(s, cells={0: ((1, 2),)}) s.erase_characters(20) self.ae(str(s.line(0)), 'a ') @@ -149,54 +149,54 @@ class TestScreen(BaseTest): self.ae(str(s.line(0)), 'a ') self.assertTrue(s.line(0).cursor_from(1).bold) self.assertFalse(s.line(0).cursor_from(0).bold) - self.assertChanges(t, cells={0: ((1, 4),)}) + self.assertChanges(s, cells={0: ((1, 4),)}) init() s.erase_in_line(1) self.ae(str(s.line(0)), ' cde') - self.assertChanges(t, cells={0: ((0, 1),)}) + self.assertChanges(s, cells={0: ((0, 1),)}) init() s.erase_in_line(2) self.ae(str(s.line(0)), ' ') - self.assertChanges(t, cells={0: ((0, 4),)}) + self.assertChanges(s, cells={0: ((0, 4),)}) init() s.erase_in_line(2, private=True) self.ae((False, False, False, False, False), tuple(map(lambda i: s.line(0).cursor_from(i).bold, range(5)))) def test_erase_in_screen(self): - s, t = self.create_screen() + s = self.create_screen() def init(): s.reset() s.draw(b'12345' * 5) - t.reset() + s.reset_dirty() s.cursor.x, s.cursor.y = 2, 1 s.cursor.bold = True init() s.erase_in_display() self.ae(s.display, ('12345', '12 ', ' ', ' ', ' ')) - self.assertChanges(t, lines={2, 3, 4}, cells={1: ((2, 4),)}) + self.assertChanges(s, lines={2, 3, 4}, cells={1: ((2, 4),)}) init() s.erase_in_display(1) self.ae(s.display, (' ', ' 45', '12345', '12345', '12345')) - self.assertChanges(t, lines={0}, cells={1: ((0, 2),)}) + self.assertChanges(s, lines={0}, cells={1: ((0, 2),)}) init() s.erase_in_display(2) self.ae(s.display, (' ', ' ', ' ', ' ', ' ')) - self.assertChanges(t, lines=set(range(5))) + self.assertChanges(s, lines=set(range(5))) self.assertTrue(s.line(0).cursor_from(1).bold) init() s.erase_in_display(2, private=True) self.ae(s.display, (' ', ' ', ' ', ' ', ' ')) - self.assertChanges(t, lines=set(range(5))) + self.assertChanges(s, lines=set(range(5))) self.assertFalse(s.line(0).cursor_from(1).bold) def test_cursor_movement(self): - s, t = self.create_screen() + s = self.create_screen() s.draw(b'12345' * 5) - t.reset() + s.reset_dirty() s.cursor_up(2) self.ae((s.cursor.x, s.cursor.y), (4, 2)) s.cursor_up1() @@ -210,7 +210,7 @@ class TestScreen(BaseTest): s.cursor_down1(5) self.ae((s.cursor.x, s.cursor.y), (0, 4)) - s, t = self.create_screen() + s = self.create_screen() s.draw(b'12345' * 5) s.index() self.ae(str(s.line(4)), ' ' * 5)