Skip to content

Commit

Permalink
_pty.py: Cheaply improve PTY experience
Browse files Browse the repository at this point in the history
  • Loading branch information
hartwork committed Dec 16, 2023
1 parent ba79456 commit 8677601
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 4 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ authors = [
requires-python = ">=3.9"
dependencies = [
"coloredlogs>=15.0.1",
"pexpect>=4.9.0",
]
keywords = ["Wine", "sandbox", "sandboxing", "bubblewrap", "bwrap"]
classifiers = [
Expand Down
47 changes: 43 additions & 4 deletions sandwine/_pty.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,54 @@
# You should have received a copy of the GNU General Public License along
# with sandwine. If not, see <https://www.gnu.org/licenses/>.

import fcntl
import os
import pty
import signal
import struct
import termios
from functools import partial
from unittest.mock import patch

import pexpect


def _copy_window_size(to_p: pexpect.spawn, from_fd: int):
if not os.isatty(from_fd):
return

s = struct.pack('HHHH', 0, 0, 0, 0)
a = struct.unpack('HHHH', fcntl.ioctl(from_fd, termios.TIOCGWINSZ, s))
if not to_p.closed:
to_p.setwinsize(a[0], a[1])


def _handle_sigwinch(p: pexpect.spawn, *_):
_copy_window_size(to_p=p, from_fd=pty.STDOUT_FILENO)


def pty_spawn_argv(argv):
wait_status = pty.spawn(argv)
# NOTE: This implementation is known to not be the real deal,
# e.g. Ctrl+Z will not work at all. It's just a cheap win over
# the previous implementation before the real deal.
p = pexpect.spawn(command=argv[0], args=argv[1:], timeout=None)

signal.signal(signal.SIGWINCH, partial(_handle_sigwinch, p))

_copy_window_size(p, from_fd=pty.STDOUT_FILENO)

if os.isatty(pty.STDIN_FILENO):
p.interact()
else:
with patch('tty.tcgetattr'), patch('tty.tcsetattr'), patch('tty.setraw'):
p.interact()

p.wait()
p.close()

exit_code = os.waitstatus_to_exitcode(wait_status)
if exit_code < 0: # e.g. -2 for "killed by SIGINT"
exit_code = 128 - exit_code # e.g. -2 -> 130
if p.signalstatus is not None:
exit_code = 128 + p.signalstatus
else:
exit_code = p.exitstatus

return exit_code

0 comments on commit 8677601

Please sign in to comment.