From 610d3d3509381895da78dafcd68f5719ad21e68d Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Sun, 8 Jun 2025 13:07:06 +0200 Subject: [PATCH] Implement clipboard paste via OSC 52 sequences. --- ircplom.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/ircplom.py b/ircplom.py index b1605bf..87ecac3 100755 --- a/ircplom.py +++ b/ircplom.py @@ -2,6 +2,7 @@ 'Attempt at an IRC client.' from abc import ABC, abstractmethod +from base64 import b64decode from contextlib import contextmanager from inspect import _empty as inspect_empty, signature, stack from queue import SimpleQueue, Empty as QueueEmpty @@ -28,11 +29,13 @@ KEYBINDINGS = { 'KEY_PGDOWN': ('window.log.scroll', 'down'), '[91, 49, 59, 51, 68]': ('window', 'left'), '[91, 49, 59, 51, 67]': ('window', 'right'), + 'KEY_F1': ('window.paste',), } CMD_SHORTCUTS = { 'disconnect': 'window.disconnect', 'reconnect': 'window.reconnect' } +PREFIX_B64 = 'b64:' IRCSPEC_LINE_SEPARATOR = b'\r\n' IRCSPEC_TAG_ESCAPES = ((r'\:', ';'), @@ -142,6 +145,18 @@ class Terminal: n_gotchs_unprocessed -= 1 n_gotchs_unprocessed -= n_chs_blessed_key if unhandleds: + if unhandleds[:6] == [93, 53, 50, 59, 99, 59]: + if len(unhandleds) > 6: + encoded = ''.join([chr(c) for c in unhandleds[:6]]) + else: + encoded = '' + while True: + gotch = self._blessed.getch() + if ord(gotch) == 7: + break + encoded += gotch + yield f'{PREFIX_B64}{encoded}' + continue yield str(unhandleds) elif blessed_key.name: yield blessed_key.name @@ -630,6 +645,11 @@ class Window(Widget): self._term.write_yx(YX(self._y_status, 0), status_line) self.prompt.draw() + def cmd__paste(self) -> None: + 'Write OSC 52 ? sequence to get encoded clipboard paste into stdin.' + self._term.write_yx(YX(self._y_status, 0), '\033]52;c;?\007') + self.draw() + class ConnectionWindow(Window): 'Window with attributes and methods for dealing with an IrcConnection.' @@ -711,7 +731,7 @@ class TuiLoop(Loop): cmd = self._cmd_name_to_cmd(event.payload[0]) assert cmd is not None cmd(*event.payload[1:]) - elif event.type_ == 'INPUT_CHAR': + elif event.type_ == 'PROMPT_ADD': self.window.prompt.append(event.payload) # elif event.type_ == 'DEBUG': # from traceback import format_exception @@ -814,10 +834,13 @@ class KeyboardLoop(Loop): 'Loop receiving and translating keyboard events towards main loop.' def process_bonus(self, yielded: str) -> None: - if yielded in KEYBINDINGS: + if yielded.startswith(PREFIX_B64): + encoded = yielded[len(PREFIX_B64):] + self.broadcast('PROMPT_ADD', b64decode(encoded).decode('utf-8')) + elif yielded in KEYBINDINGS: self.broadcast('KEYBINDING', KEYBINDINGS[yielded]) elif len(yielded) == 1: - self.broadcast('INPUT_CHAR', yielded) + self.broadcast('PROMPT_ADD', yielded) else: self.broadcast('ALERT', f'unknown keyboard input: {yielded}') -- 2.30.2