Delegate kitty +kitten clipboard to kitty-tool

This commit is contained in:
Kovid Goyal 2022-11-26 20:09:43 +05:30
parent 5eb2142d70
commit 375fa73826
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 29 additions and 125 deletions

View File

@ -1,20 +1,7 @@
#!/usr/bin/env python3
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import codecs
import io
import os
import select
import sys
from typing import List, NoReturn, Optional
from kitty.cli import parse_args
from kitty.cli_stub import ClipboardCLIOptions
from kitty.fast_data_types import parse_input_from_terminal
from ..tui.operations import (
raw_mode, request_from_clipboard, write_to_clipboard
)
OPTIONS = r'''
--get-clipboard
@ -45,103 +32,8 @@ popup, see :opt:`clipboard_control` for details.
'''
usage = ''
got_capability_response = False
got_clipboard_response = False
clipboard_contents = ''
clipboard_from_primary = False
def ignore(x: str) -> None:
pass
def on_text(x: str) -> None:
if '\x03' in x:
raise KeyboardInterrupt()
if '\x04' in x:
raise EOFError()
def on_dcs(dcs: str) -> None:
global got_capability_response
if dcs.startswith('1+r'):
got_capability_response = True
def on_osc(osc: str) -> None:
global clipboard_contents, clipboard_from_primary, got_clipboard_response
idx = osc.find(';')
if idx <= 0:
return
q = osc[:idx]
if q == '52':
got_clipboard_response = True
widx = osc.find(';', idx + 1)
if widx < idx:
clipboard_from_primary = osc.find('p', idx + 1) > -1
clipboard_contents = ''
else:
from base64 import standard_b64decode
clipboard_from_primary = osc.find('p', idx+1, widx) > -1
data = memoryview(osc.encode('ascii'))
clipboard_contents = standard_b64decode(data[widx+1:]).decode('utf-8')
def wait_loop(tty_fd: int) -> None:
os.set_blocking(tty_fd, False)
decoder = codecs.getincrementaldecoder('utf-8')('ignore')
with raw_mode(tty_fd):
buf = ''
while not got_capability_response and not got_clipboard_response:
rd = select.select([tty_fd], [], [])[0]
if rd:
raw = os.read(tty_fd, io.DEFAULT_BUFFER_SIZE)
if not raw:
raise EOFError()
data = decoder.decode(raw)
buf = (buf + data) if buf else data
buf = parse_input_from_terminal(on_text, on_dcs, ignore, on_osc, ignore, ignore, buf, False)
def main(args: List[str]) -> NoReturn:
cli_opts, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten clipboard', result_class=ClipboardCLIOptions)
if items:
raise SystemExit('Unrecognized extra command line arguments')
data: Optional[bytes] = None
if not sys.stdin.isatty():
data = sys.stdin.buffer.read()
wait_for_capability_response = False
data_to_write = []
if data:
data_to_write.append(write_to_clipboard(data, cli_opts.use_primary).encode('ascii'))
if not cli_opts.get_clipboard and cli_opts.wait_for_completion:
data_to_write.append(b'\x1bP+q544e\x1b\\')
wait_for_capability_response = True
if cli_opts.get_clipboard:
data_to_write.append(request_from_clipboard(cli_opts.use_primary).encode('ascii'))
wait_for_capability_response = True
tty_fd = os.open(os.ctermid(), os.O_RDWR | os.O_CLOEXEC)
retcode = 0
with open(tty_fd, 'wb', closefd=True) as ttyf:
for x in data_to_write:
ttyf.write(x)
ttyf.flush()
if wait_for_capability_response:
try:
wait_loop(tty_fd)
except KeyboardInterrupt:
sys.excepthook = lambda *a: None
raise
except EOFError:
retcode = 1
if clipboard_contents:
print(end=clipboard_contents)
raise SystemExit(retcode)
if __name__ == '__main__':
main(sys.argv)
raise SystemExit('This should be run as kitty-tool clipboard')
elif __name__ == '__doc__':
cd = sys.cli_docs # type: ignore
cd['usage'] = usage

View File

@ -316,19 +316,32 @@ ensure_working_stdio(void) {
#undef C
}
static bool
is_wrapped_kitten(const char *arg) {
char buf[64];
snprintf(buf, sizeof(buf)-1, " %s ", arg);
return strstr(" " WRAPPED_KITTENS " ", buf);
}
static void
delegate_to_kitty_tool_if_possible(int argc, char *argv[], char* exe_dir) {
if (argc > 1 && argv[1][0] == '@') {
exec_kitty_tool(int argc, char *argv[], char *exe_dir) {
char exe[PATH_MAX+1] = {0};
snprintf(exe, PATH_MAX, "%s/kitty-tool", exe_dir);
char **newargv = malloc(sizeof(char*) * (argc + 1));
memcpy(newargv, argv, sizeof(char*) * argc);
newargv[argc] = 0;
newargv[0] = "kitty-tool";
errno = 0;
execv(exe, argv);
fprintf(stderr, "Failed to execute kitty-tool (%s) with error: %s\n", exe, strerror(errno));
exit(1);
}
}
static void
delegate_to_kitty_tool_if_possible(int argc, char *argv[], char* exe_dir) {
if (argc > 1 && argv[1][0] == '@') exec_kitty_tool(argc, argv, exe_dir);
if (argc > 2 && strcmp(argv[1], "+kitten") == 0 && is_wrapped_kitten(argv[2])) exec_kitty_tool(argc - 1, argv + 1, exe_dir);
if (argc > 3 && strcmp(argv[1], "+") == 0 && strcmp(argv[2], "kitten") == 0 && is_wrapped_kitten(argv[3])) exec_kitty_tool(argc - 2, argv + 2, exe_dir);
}
int main(int argc, char *argv[], char* envp[]) {

View File

@ -529,8 +529,7 @@ def get_source_specific_defines(env: Env, src: str) -> Tuple[str, Optional[List[
if src == 'kitty/parser_dump.c':
return 'kitty/parser.c', ['DUMP_COMMANDS']
if src == 'kitty/data-types.c':
wk = ' '.join(wrapped_kittens())
return src, [f'KITTY_VCS_REV="{get_vcs_rev_define()}"', f'WRAPPED_KITTENS="{wk}"']
return src, [f'KITTY_VCS_REV="{get_vcs_rev_define()}"', f'WRAPPED_KITTENS="{wrapped_kittens()}"']
try:
return src, env.library_paths[src]
except KeyError:
@ -863,12 +862,12 @@ def build_ref_map() -> str:
@lru_cache
def wrapped_kittens() -> Sequence[str]:
def wrapped_kittens() -> str:
with open('shell-integration/ssh/kitty') as f:
for line in f:
if line.startswith(' wrapped_kittens="'):
val = line.strip().partition('"')[2][:-1]
return tuple(sorted(filter(None, val.split())))
return ' '.join(sorted(filter(None, val.split())))
raise Exception('Failed to read wrapped kittens from kitty wrapper script')
@ -965,7 +964,7 @@ def build_static_binaries(args: Options, launcher_dir: str) -> None:
def build_launcher(args: Options, launcher_dir: str = '.', bundle_type: str = 'source') -> None:
werror = '' if args.ignore_compiler_warnings else '-pedantic-errors -Werror'
cflags = f'-Wall {werror} -fpie'.split()
cppflags = []
cppflags = [define(f'WRAPPED_KITTENS=" {wrapped_kittens()} "')]
libs: List[str] = []
ldflags = shlex.split(os.environ.get('LDFLAGS', ''))
if args.profile or args.sanitize: