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 .config import parse_config, parse_send_text_bytes
|
||||||
from .constants import appname
|
from .constants import appname
|
||||||
from .tabs import SpecialWindow
|
from .tabs import SpecialWindow
|
||||||
from .utils import non_blocking_read
|
|
||||||
|
|
||||||
|
|
||||||
class MatchError(ValueError):
|
class MatchError(ValueError):
|
||||||
@ -132,27 +131,32 @@ def cmd_send_text(global_opts, opts, args):
|
|||||||
limit = 1024
|
limit = 1024
|
||||||
ret = {'match': opts.match, 'is_binary': False}
|
ret = {'match': opts.match, 'is_binary': False}
|
||||||
|
|
||||||
def pipe(src=sys.stdin):
|
def pipe():
|
||||||
ret['is_binary'] = True
|
ret['is_binary'] = True
|
||||||
import select
|
if sys.stdin.isatty():
|
||||||
with non_blocking_read() as fd:
|
import select
|
||||||
|
fd = sys.stdin.fileno()
|
||||||
keep_going = True
|
keep_going = True
|
||||||
while keep_going:
|
while keep_going:
|
||||||
rd = select.select([fd], [], [])
|
rd = select.select([fd], [], [])[0]
|
||||||
if rd:
|
if not rd:
|
||||||
data = sys.stdin.buffer.read()
|
|
||||||
if not data:
|
|
||||||
break
|
|
||||||
data = data.decode('utf-8')
|
|
||||||
if '\x04' in data:
|
|
||||||
data = data[:data.index('\x04')]
|
|
||||||
keep_going = False
|
|
||||||
while data:
|
|
||||||
ret['text'] = data[:limit]
|
|
||||||
yield ret
|
|
||||||
data = data[limit:]
|
|
||||||
else:
|
|
||||||
break
|
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
|
||||||
|
ret['text'] = data
|
||||||
|
yield ret
|
||||||
|
else:
|
||||||
|
while True:
|
||||||
|
data = sys.stdin.read(limit)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
ret['text'] = data[:limit]
|
||||||
|
yield ret
|
||||||
|
|
||||||
def chunks(text):
|
def chunks(text):
|
||||||
ret['is_binary'] = False
|
ret['is_binary'] = False
|
||||||
|
|||||||
@ -3,18 +3,14 @@
|
|||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import types
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from io import DEFAULT_BUFFER_SIZE
|
|
||||||
|
|
||||||
from .cli import emph, parse_args
|
from .cli import emph, parse_args
|
||||||
from .cmds import cmap, parse_subcommand_cli
|
from .cmds import cmap, parse_subcommand_cli
|
||||||
from .constants import appname, version
|
from .constants import appname, version
|
||||||
from .fast_data_types import close_tty, open_tty
|
from .utils import TTYIO, parse_address_spec
|
||||||
from .utils import parse_address_spec, write_all
|
|
||||||
|
|
||||||
|
|
||||||
def handle_cmd(boss, window, cmd):
|
def handle_cmd(boss, window, cmd):
|
||||||
@ -66,7 +62,14 @@ class SocketIO:
|
|||||||
def send(self, data):
|
def send(self, data):
|
||||||
import socket
|
import socket
|
||||||
with self.socket.makefile('wb') as out:
|
with self.socket.makefile('wb') as out:
|
||||||
out.write(data)
|
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)
|
self.socket.shutdown(socket.SHUT_WR)
|
||||||
|
|
||||||
def recv(self, more_needed, timeout):
|
def recv(self, more_needed, timeout):
|
||||||
@ -78,41 +81,23 @@ class SocketIO:
|
|||||||
more_needed(data)
|
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):
|
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()
|
io = SocketIO(to) if to else TTYIO()
|
||||||
with io:
|
with io:
|
||||||
io.send(send)
|
io.send(send_data)
|
||||||
if no_response:
|
if no_response:
|
||||||
return {'ok': True}
|
return {'ok': True}
|
||||||
|
|
||||||
dcs = re.compile(br'\x1bP@kitty-cmd([^\x1b]+)\x1b\\')
|
dcs = re.compile(br'\x1bP@kitty-cmd([^\x1b]+)\x1b\\')
|
||||||
|
|
||||||
received = b''
|
received = b''
|
||||||
@ -168,11 +153,6 @@ def main(args):
|
|||||||
'cmd': cmd,
|
'cmd': cmd,
|
||||||
'version': version,
|
'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:
|
if payload is not None:
|
||||||
send['payload'] = payload
|
send['payload'] = payload
|
||||||
response = do_io(global_opts.to, send, func.no_response)
|
response = do_io(global_opts.to, send, func.no_response)
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import readline
|
|||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import types
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
from .cli import (
|
from .cli import (
|
||||||
@ -134,11 +133,6 @@ def run_cmd(global_opts, cmd, func, opts, items):
|
|||||||
'cmd': cmd,
|
'cmd': cmd,
|
||||||
'version': version,
|
'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:
|
if payload is not None:
|
||||||
send['payload'] = payload
|
send['payload'] = payload
|
||||||
response = do_io(global_opts.to, send, func.no_response)
|
response = do_io(global_opts.to, send, func.no_response)
|
||||||
|
|||||||
@ -10,14 +10,14 @@ import os
|
|||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
|
||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
from .constants import (
|
from .constants import (
|
||||||
appname, is_macos, is_wayland, supports_primary_selection
|
appname, is_macos, is_wayland, supports_primary_selection
|
||||||
)
|
)
|
||||||
from .fast_data_types import (
|
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
|
from .rgb import Color, to_color
|
||||||
|
|
||||||
@ -362,45 +362,35 @@ def write_all(fd, data):
|
|||||||
data = data[n:]
|
data = data[n:]
|
||||||
|
|
||||||
|
|
||||||
def make_fd_non_blocking(fd):
|
class TTYIO:
|
||||||
oldfl = fcntl.fcntl(fd, fcntl.F_GETFL)
|
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl | os.O_NONBLOCK)
|
|
||||||
return oldfl
|
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.tty_fd, self.original_termios = open_tty()
|
||||||
|
return self
|
||||||
|
|
||||||
@contextmanager
|
def __exit__(self, *a):
|
||||||
def non_blocking_read(src=sys.stdin, disable_echo=False):
|
close_tty(self.tty_fd, self.original_termios)
|
||||||
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 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):
|
||||||
import select
|
from io import DEFAULT_BUFFER_SIZE
|
||||||
start_time = monotonic()
|
import select
|
||||||
with non_blocking_read(src) as fd:
|
fd = self.tty_fd
|
||||||
|
start_time = monotonic()
|
||||||
while timeout > monotonic() - start_time:
|
while timeout > monotonic() - start_time:
|
||||||
rd = select.select([fd], [], [], max(0, timeout - (monotonic() - start_time)))[0]
|
rd = select.select([fd], [], [], max(0, timeout - (monotonic() - start_time)))[0]
|
||||||
if rd:
|
if not rd:
|
||||||
data = src.read()
|
break
|
||||||
if not data:
|
data = os.read(fd, DEFAULT_BUFFER_SIZE)
|
||||||
break # eof
|
if not data:
|
||||||
if not more_needed(data):
|
break # eof
|
||||||
break
|
if not more_needed(data):
|
||||||
else:
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user