diff --git a/kitty/boss.py b/kitty/boss.py index d379a2419..c43baf994 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -7,16 +7,13 @@ import io from PyQt5.QtCore import QObject, QSocketNotifier -from .screen import Screen from .term import TerminalWidget from .utils import resize_pty, hangup, create_pty -from .tracker import ChangeTracker -from pyte.streams import Stream class Boss(QObject): - def __init__(self, opts, parent): + def __init__(self, opts, parent, dump_commands): QObject.__init__(self, parent) self.shutting_down = False self.write_buf = memoryview(b'') @@ -25,17 +22,13 @@ class Boss(QObject): self.write_notifier = QSocketNotifier(create_pty()[0], QSocketNotifier.Write, self) self.write_notifier.setEnabled(False) self.write_notifier.activated.connect(self.write_ready) - self.tracker = ChangeTracker(self) - self.screen = s = Screen(opts, self.tracker, parent=self) - self.stream = Stream(s) - s.write_to_child.connect(self.write_to_child) - self.term = TerminalWidget(opts, self.tracker, self.screen.linebuf, parent) + self.term = TerminalWidget(opts, parent, dump_commands) self.term.relayout_lines.connect(self.relayout_lines) self.term.send_data_to_child.connect(self.write_to_child) - resize_pty(self.screen.columns, self.screen.lines) + self.term.write_to_child.connect(self.write_to_child) + resize_pty(80, 24) def apply_opts(self, opts): - self.screen.apply_opts(opts) self.term.apply_opts(opts) def read_ready(self, read_fd): @@ -50,7 +43,7 @@ class Boss(QObject): self.read_notifier.setEnabled(False) self.parent().child_process_died() return - self.stream.feed(data) + self.term.feed(data) def write_ready(self, write_fd): if not self.shutting_down: @@ -65,8 +58,7 @@ class Boss(QObject): self.write_buf = memoryview(self.write_buf.tobytes() + data) self.write_notifier.setEnabled(True) - def relayout_lines(self, previous, cells_per_line, previousl, lines_per_screen): - self.screen.resize(lines_per_screen, cells_per_line) + def relayout_lines(self, cells_per_line, lines_per_screen): resize_pty(cells_per_line, lines_per_screen) def shutdown(self): diff --git a/kitty/main.py b/kitty/main.py index 8da5c4fc6..74fba0226 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -24,13 +24,13 @@ class MainWindow(QMainWindow): report_error = pyqtSignal(object) - def __init__(self, opts): + def __init__(self, opts, dump_commands): QMainWindow.__init__(self) self.setWindowTitle(appname) sys.excepthook = self.on_unhandled_error self.report_error.connect(self.show_error, type=Qt.QueuedConnection) self.handle_unix_signals() - self.boss = Boss(opts, self) + self.boss = Boss(opts, self, dump_commands) self.setCentralWidget(self.boss.term) def on_unhandled_error(self, etype, value, tb): @@ -85,6 +85,7 @@ def option_parser(): a('-d', '--directory', default='.', help=_('Change to the specified directory when launching')) a('--version', action='version', version='{} {} by Kovid Goyal'.format(appname, '.'.join(str_version))) a('--profile', action='store_true', default=False, help=_('Show profiling data after exit')) + a('--dump-commands', action='store_true', default=False, help=_('Output commands received from child process to stdout')) return parser @@ -114,7 +115,7 @@ def main(): validate_font(opts) except ValueError as err: raise SystemExit(str(err)) from None - w = MainWindow(opts) + w = MainWindow(opts, args.dump_commands) w.show() if args.profile: import cProfile diff --git a/kitty/screen.py b/kitty/screen.py index c34166d56..e43514dc6 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -69,6 +69,9 @@ class Screen(QObject): if sz != self.tophistorybuf.maxlen: self.tophistorybuf = deque(self.tophistorybuf, maxlen=sz) + def line(self, i): + return self.linebuf[i] + def __repr__(self): return ("{0}({1}, {2})".format(self.__class__.__name__, self.columns, self.lines)) diff --git a/kitty/term.py b/kitty/term.py index 7c3f158e4..b07c856be 100644 --- a/kitty/term.py +++ b/kitty/term.py @@ -4,18 +4,20 @@ from functools import lru_cache from itertools import product -from typing import Tuple, Iterator, Sequence +from typing import Tuple, Iterator from PyQt5.QtCore import pyqtSignal, QTimer, QRect, Qt from PyQt5.QtGui import QColor, QPainter, QFont, QFontMetrics, QRegion, QPen, QPixmap from PyQt5.QtWidgets import QWidget from .config import build_ansi_color_tables, Options, fg_color_table, bg_color_table -from .data_types import Line, Cursor, HAS_BG_MASK, COL_SHIFT, COL_MASK, as_color +from .data_types import Cursor, HAS_BG_MASK, COL_SHIFT, COL_MASK, as_color from .utils import set_current_font_metrics from .tracker import ChangeTracker from .screen import wrap_cursor_position from .keys import key_event_to_data +from .screen import Screen +from pyte.streams import Stream, DebugStream def ascii_width(fm: QFontMetrics) -> int: @@ -41,18 +43,24 @@ def pixmap_for_text(text, color, default_fg, font, w, h, baseline): class TerminalWidget(QWidget): - relayout_lines = pyqtSignal(object, object, object, object) + relayout_lines = pyqtSignal(object, object) + write_to_child = pyqtSignal(object) send_data_to_child = pyqtSignal(object) cells_per_line = 80 lines_per_screen = 24 - def __init__(self, opts: Options, tracker: ChangeTracker, linebuf: Sequence[Line], parent: QWidget=None): + def __init__(self, opts: Options, parent: QWidget=None, dump_commands: bool=False): QWidget.__init__(self, parent) + self.cursor = Cursor() + self.tracker = ChangeTracker(self) + self.tracker.dirtied.connect(self.update_screen) + sclass = DebugStream if dump_commands else Stream + self.screen = Screen(opts, self.tracker, parent=self) + self.screen.write_to_child.connect(self.write_to_child) + self.stream = sclass(self.screen) + self.feed = self.stream.feed self.last_drew_cursor_at = (0, 0) self.setFocusPolicy(Qt.WheelFocus) - tracker.dirtied.connect(self.update_screen) - self.linebuf = linebuf - self.cursor = Cursor() self.setAutoFillBackground(True) self.apply_opts(opts) self.debounce_resize_timer = t = QTimer(self) @@ -66,6 +74,7 @@ class TerminalWidget(QWidget): self.pending_update = QRegion() def apply_opts(self, opts): + self.screen.apply_opts(opts) self.opts = opts pixmap_for_text.cache_clear() pal = self.palette() @@ -97,7 +106,8 @@ class TerminalWidget(QWidget): self.line_width = self.cells_per_line * self.cell_width self.layout_size = self.size() if (previous, previousl) != (self.cells_per_line, self.lines_per_screen): - self.relayout_lines.emit(previous, self.cells_per_line, previousl, self.lines_per_screen) + self.screen.resize(self.lines_per_screen, self.cells_per_line) + self.relayout_lines.emit(self.cells_per_line, self.lines_per_screen) self.update() def resizeEvent(self, ev): @@ -180,7 +190,7 @@ class TerminalWidget(QWidget): x, y = wrap_cursor_position(self.cursor.x, self.cursor.y, len(self.line_positions), len(self.cell_positions)) r = QRect(self.cell_positions[x], self.line_positions[y], self.cell_width, self.cell_height) self.last_drew_cursor_at = x, y - line = self.linebuf[x] + line = self.screen.line(x) colors = line.basic_cell_data(y)[2] if colors & HAS_BG_MASK: bg = as_color(colors >> COL_SHIFT, bg_color_table()) @@ -193,7 +203,7 @@ class TerminalWidget(QWidget): painter.drawRect(r) def paint_cell(self, painter: QPainter, col: int, row: int) -> None: - line = self.linebuf[row] + line = self.screen.line(row) ch, attrs, colors = line.basic_cell_data(col) x, y = self.cell_positions[col], self.line_positions[row] if colors & HAS_BG_MASK and (col != self.last_drew_cursor_at[0] or row != self.last_drew_cursor_at[1]): diff --git a/pyte/streams.py b/pyte/streams.py index 20b20a0fe..47c9a2966 100644 --- a/pyte/streams.py +++ b/pyte/streams.py @@ -362,7 +362,10 @@ class DebugStream(Stream): default, which means -- debug all events). """ - def __init__(self, to=sys.stdout, only=(), *args, **kwargs): + def __init__(self, *args, **kwargs): + to = kwargs.pop('to', sys.stdout) + only = kwargs.pop('only', ()) + def safe_str(chunk): if isinstance(chunk, bytes): chunk = chunk.decode("utf-8") @@ -375,6 +378,7 @@ class DebugStream(Stream): pass class Bugger(object): + def __getattr__(self, event): if only and event not in only: return noop