Clean up usage of TTYIO for remote cmds and the shell
This commit is contained in:
parent
71c1432892
commit
5dbaf9aab0
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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():
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user