home · contact · privacy
Implement clipboard paste via OSC 52 sequences.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 8 Jun 2025 11:07:06 +0000 (13:07 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 8 Jun 2025 11:07:06 +0000 (13:07 +0200)
ircplom.py

index b1605bfe1b8c7073d4d74db5333fb7d4b424dcb3..87ecac3ccb84522be45eca5679c22367acb01627 100755 (executable)
@@ -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}')