Clean up usage of TTYIO for remote cmds and the shell

This commit is contained in:
Kovid Goyal 2018-06-08 20:17:27 +05:30
parent 71c1432892
commit 5dbaf9aab0
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 68 additions and 100 deletions

View File

@ -10,7 +10,6 @@ from .cli import parse_args
from .config import parse_config, parse_send_text_bytes
from .constants import appname
from .tabs import SpecialWindow
from .utils import non_blocking_read
class MatchError(ValueError):
@ -132,27 +131,32 @@ def cmd_send_text(global_opts, opts, args):
limit = 1024
ret = {'match': opts.match, 'is_binary': False}
def pipe(src=sys.stdin):
def pipe():
ret['is_binary'] = True
if sys.stdin.isatty():
import select
with non_blocking_read() as fd:
fd = sys.stdin.fileno()
keep_going = True
while keep_going:
rd = select.select([fd], [], [])
if rd:
data = sys.stdin.buffer.read()
if not data:
rd = select.select([fd], [], [])[0]
if not rd:
break
data = os.read(fd, limit)
if not data:
break # eof
data = data.decode('utf-8')
if '\x04' in data:
data = data[:data.index('\x04')]
keep_going = False
while data:
ret['text'] = data
yield ret
else:
while True:
data = sys.stdin.read(limit)
if not data:
break
ret['text'] = data[:limit]
yield ret
data = data[limit:]
else:
break
def chunks(text):
ret['is_binary'] = False

View File

@ -3,18 +3,14 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os
import re
import sys
import types
from functools import partial
from io import DEFAULT_BUFFER_SIZE
from .cli import emph, parse_args
from .cmds import cmap, parse_subcommand_cli
from .constants import appname, version
from .fast_data_types import close_tty, open_tty
from .utils import parse_address_spec, write_all
from .utils import TTYIO, parse_address_spec
def handle_cmd(boss, window, cmd):
@ -66,7 +62,14 @@ class SocketIO:
def send(self, data):
import socket
with self.socket.makefile('wb') as out:
if isinstance(data, bytes):
out.write(data)
else:
for chunk in data:
if isinstance(chunk, str):
chunk = chunk.encode('utf-8')
out.write(chunk)
out.flush()
self.socket.shutdown(socket.SHUT_WR)
def recv(self, more_needed, timeout):
@ -78,41 +81,23 @@ class SocketIO:
more_needed(data)
class TTYIO:
def __enter__(self):
self.tty_fd, self.original_termios = open_tty()
def __exit__(self, *a):
close_tty(self.tty_fd, self.original_termios)
def send(self, data):
write_all(self.tty_fd, data)
def recv(self, more_needed, timeout):
import select
from time import monotonic
fd = self.tty_fd
start_time = monotonic()
while timeout > monotonic() - start_time:
rd = select.select([fd], [], [], max(0, timeout - (monotonic() - start_time)))[0]
if rd:
data = os.read(fd, DEFAULT_BUFFER_SIZE)
if not data:
break # eof
if not more_needed(data):
break
else:
break
def do_io(to, send, no_response):
send = encode_send(send)
payload = send.get('payload')
if payload is None or isinstance(payload, (str, bytes)):
send_data = encode_send(send)
else:
def send_generator():
for chunk in payload:
send['payload'] = chunk
yield encode_send(send)
send_data = send_generator()
io = SocketIO(to) if to else TTYIO()
with io:
io.send(send)
io.send(send_data)
if no_response:
return {'ok': True}
dcs = re.compile(br'\x1bP@kitty-cmd([^\x1b]+)\x1b\\')
received = b''
@ -168,11 +153,6 @@ def main(args):
'cmd': cmd,
'version': version,
}
if func.no_response and isinstance(payload, types.GeneratorType):
for item in payload:
send['payload'] = item
do_io(global_opts.to, send, func.no_response)
return
if payload is not None:
send['payload'] = payload
response = do_io(global_opts.to, send, func.no_response)

View File

@ -7,7 +7,6 @@ import readline
import shlex
import sys
import traceback
import types
from functools import lru_cache
from .cli import (
@ -134,11 +133,6 @@ def run_cmd(global_opts, cmd, func, opts, items):
'cmd': cmd,
'version': version,
}
if func.no_response and isinstance(payload, types.GeneratorType):
for item in payload:
send['payload'] = item
do_io(global_opts.to, send, func.no_response)
return
if payload is not None:
send['payload'] = payload
response = do_io(global_opts.to, send, func.no_response)

View File

@ -10,14 +10,14 @@ import os
import re
import string
import sys
from contextlib import contextmanager
from time import monotonic
from .constants import (
appname, is_macos, is_wayland, supports_primary_selection
)
from .fast_data_types import (
GLSL_VERSION, log_error_string, redirect_std_streams, x11_display
GLSL_VERSION, close_tty, log_error_string, open_tty, redirect_std_streams,
x11_display
)
from .rgb import Color, to_color
@ -362,46 +362,36 @@ def write_all(fd, data):
data = data[n:]
def make_fd_non_blocking(fd):
oldfl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl | os.O_NONBLOCK)
return oldfl
class TTYIO:
def __enter__(self):
self.tty_fd, self.original_termios = open_tty()
return self
@contextmanager
def non_blocking_read(src=sys.stdin, disable_echo=False):
import termios
import tty
import fcntl
fd = src.fileno()
if src.isatty():
old = termios.tcgetattr(fd)
tty.setraw(fd)
if disable_echo:
new = list(old)
new[3] |= termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, new)
oldfl = make_fd_non_blocking(fd)
yield fd
if src.isatty():
termios.tcsetattr(fd, termios.TCSADRAIN, old)
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl)
def __exit__(self, *a):
close_tty(self.tty_fd, self.original_termios)
def send(self, data):
if isinstance(data, (str, bytes)):
write_all(self.tty_fd, data)
else:
for chunk in data:
write_all(self.tty_fd, chunk)
def read_with_timeout(more_needed, timeout=10, src=sys.stdin.buffer):
def recv(self, more_needed, timeout):
from io import DEFAULT_BUFFER_SIZE
import select
fd = self.tty_fd
start_time = monotonic()
with non_blocking_read(src) as fd:
while timeout > monotonic() - start_time:
rd = select.select([fd], [], [], max(0, timeout - (monotonic() - start_time)))[0]
if rd:
data = src.read()
if not rd:
break
data = os.read(fd, DEFAULT_BUFFER_SIZE)
if not data:
break # eof
if not more_needed(data):
break
else:
break
def get_editor():