diff --git a/kitty/boss.py b/kitty/boss.py index 33a2d1649..388af1bae 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -2,18 +2,27 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal -from PyQt5.QtCore import QObject +import os +import io + +from PyQt5.QtCore import QObject, QSocketNotifier from .screen import Screen from .term import TerminalWidget -from .utils import resize_pty, hangup +from .utils import resize_pty, hangup, create_pty class Boss(QObject): - def __init__(self, opts, parent=None): + def __init__(self, opts, parent): QObject.__init__(self, parent) - self.screen = Screen(opts, parent=self) + self.write_buf = memoryview(b'') + self.read_notifier = QSocketNotifier(create_pty()[0], QSocketNotifier.Read, self) + self.read_notifier.activated.connect(self.read_ready) + self.write_notifier = QSocketNotifier(create_pty()[0], QSocketNotifier.Write, self) + self.write_notifier.setEnabled(False) + self.write_notifier.activated.connect(self.write_ready) + self.screen = Screen(opts, self.write_to_child, parent=self) self.term = TerminalWidget(opts, self.screen.linebuf, parent) self.term.relayout_lines.connect(self.relayout_lines) resize_pty(self.screen.columns, self.screen.lines) @@ -22,6 +31,25 @@ class Boss(QObject): self.screen.apply_opts(opts) self.term.apply_opts(opts) + def read_ready(self, read_fd): + data = os.read(read_fd, io.DEFAULT_BUFFER_SIZE) + if not data: + # EOF + self.parent().child_process_died() + return + + def write_ready(self, write_fd): + while self.write_buf: + n = os.write(write_fd, io.DEFAULT_BUFFER_SIZE) + if not n: + return + self.write_buf = self.write_buf[n:] + self.write_notifier.setEnabled(False) + + def write_to_child(self, data): + 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) resize_pty(cells_per_line, lines_per_screen) diff --git a/kitty/main.py b/kitty/main.py index 85ee9f49e..8ddac753d 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -59,6 +59,9 @@ class MainWindow(QMainWindow): if signal.SIGINT in signals or signal.SIGTERM in signals: self.shutdown() + def child_process_died(self): + self.shutdown() + def shutdown(self): self.close() self.boss.shutdown() diff --git a/kitty/screen.py b/kitty/screen.py index 8ae7cd33e..6fb9a550c 100644 --- a/kitty/screen.py +++ b/kitty/screen.py @@ -45,8 +45,9 @@ class Screen(QObject): line_added_to_history = pyqtSignal() _notify_cursor_position = True - def __init__(self, opts, columns: int=80, lines: int=24, parent=None): + def __init__(self, opts, write_to_child, columns: int=80, lines: int=24, parent=None): QObject.__init__(self, parent) + self.write_process_input = write_to_child self.savepoints = deque() self.columns = columns self.lines = lines @@ -855,16 +856,6 @@ class Screen(QObject): self.write_process_input( ctrl.CSI + "{0};{1}R".format(y, x).encode()) - def write_process_input(self, data): - """Writes data to the process running inside the terminal. - - By default is a noop. - - :param bytes data: data to write to the process ``stdin``. - - .. versionadded:: 0.5.0 - """ - def debug(self, *args, **kwargs): """Endpoint for unrecognized escape sequences. diff --git a/kitty/utils.py b/kitty/utils.py index 302c6acf7..0ec814980 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -36,6 +36,7 @@ def create_pty(): if not hasattr(create_pty, 'master'): create_pty.master, create_pty.slave = os.openpty() fcntl.fcntl(create_pty.slave, fcntl.F_SETFD, fcntl.fcntl(create_pty.slave, fcntl.F_GETFD) & ~fcntl.FD_CLOEXEC) + # Note that master and slave are in blocking mode return create_pty.master, create_pty.slave