From 5d510bcf71ebbee97c4ab74c444fbeca4f8f1c0d Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Wed, 16 Aug 2023 14:02:52 +0100 Subject: [PATCH] Support bracketed paste as terminal input --- TermTk/TTkCore/TTkTerm/input.py | 30 ++++++++++++++++++++++++----- TermTk/TTkCore/TTkTerm/term_base.py | 11 +++++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/TermTk/TTkCore/TTkTerm/input.py b/TermTk/TTkCore/TTkTerm/input.py index d32748bb..65feb76e 100644 --- a/TermTk/TTkCore/TTkTerm/input.py +++ b/TermTk/TTkCore/TTkTerm/input.py @@ -46,12 +46,16 @@ class TTkInput: '_readInput', '_leftLastTime', '_midLastTime', '_rightLastTime', '_leftTap', '_midTap', '_rightTap', + '_pasteBuffer', '_bracketedPaste', # Signals - 'inputEvent' + 'inputEvent', 'pasteEvent' ) def __init__(self): self.inputEvent = pyTTkSignal(TTkKeyEvent, TTkMouseEvent) + self.pasteEvent = pyTTkSignal(str) + self._pasteBuffer = "" + self._bracketedPaste = False self._readInput = None self._leftLastTime = 0 self._midLastTime = 0 @@ -79,7 +83,18 @@ class TTkInput: mouse_re = re.compile(r"\033\[<(\d+);(\d+);(\d+)([mM])") def key_process(self, stdinRead): + if self._bracketedPaste: + if stdinRead.endswith("\033[201~"): + self._pasteBuffer += stdinRead[:6] + self._bracketedPaste = False + self.pasteEvent.emit(self._pasteBuffer) + self._pasteBuffer = "" + else: + self._pasteBuffer += stdinRead + return + mevt,kevt = None, None + if not stdinRead.startswith("\033[<"): # Key Event kevt = TTkKeyEvent.parse(stdinRead) @@ -153,12 +168,17 @@ class TTkInput: evt = TTkMouseEvent.Move mevt = TTkMouseEvent(x, y, key, evt, mod, tap, m.group(0).replace("\033", "")) + if kevt or mevt: + self.inputEvent.emit(kevt, mevt) + return - if kevt is None and mevt is None: - hex = [f"0x{ord(x):02x}" for x in stdinRead] - TTkLog.error("UNHANDLED: "+stdinRead.replace("\033","") + " - "+",".join(hex)) + if stdinRead.startswith("\033[200~"): + self._pasteBuffer = stdinRead[6:] + self._bracketedPaste = True + return - self.inputEvent.emit(kevt, mevt) + hex = [f"0x{ord(x):02x}" for x in stdinRead] + TTkLog.error("UNHANDLED: "+stdinRead.replace("\033","") + " - "+",".join(hex)) def main(): diff --git a/TermTk/TTkCore/TTkTerm/term_base.py b/TermTk/TTkCore/TTkTerm/term_base.py index b6f7e2c6..0477eb8c 100644 --- a/TermTk/TTkCore/TTkTerm/term_base.py +++ b/TermTk/TTkCore/TTkTerm/term_base.py @@ -29,6 +29,9 @@ class TTkTermBase(): ALT_SCREEN = "\033[?1049h" #* Switch to alternate screen NORMAL_SCREEN = "\033[?1049l" #* Switch to normal screen + SET_BRACKETED_PM = "\033[?2004h" # Ps = 2 0 0 4 ⇒ Set bracketed paste mode, xterm. + RESET_BRACKETED_PM = "\033[?2004l" # Ps = 2 0 0 4 ⇒ Reset bracketed paste mode, xterm. + class Mouse(): ON = "\033[?1002h\033[?1006h" # Enable reporting of mouse position on click and release OFF = "\033[?1002l\033[?1006l" # Disable mouse reporting @@ -95,7 +98,7 @@ class TTkTermBase(): TTkTermBase.mouse = mouse | directMouse TTkTermBase.directMouse = directMouse TTkTermBase.Cursor.hide() - TTkTermBase.push(TTkTermBase.ALT_SCREEN + TTkTermBase.CLEAR + TTkTermBase.Cursor.HIDE + TTkTermBase.escTitle(TTkTermBase.title)) + TTkTermBase.push(TTkTermBase.ALT_SCREEN + TTkTermBase.SET_BRACKETED_PM + TTkTermBase.CLEAR + TTkTermBase.Cursor.HIDE + TTkTermBase.escTitle(TTkTermBase.title)) if TTkTermBase.mouse: TTkTermBase.push(TTkTermBase.Mouse.ON) if TTkTermBase.directMouse: @@ -107,20 +110,20 @@ class TTkTermBase(): @staticmethod def exit(): TTkTermBase.push(TTkTermBase.Mouse.OFF + TTkTermBase.Mouse.DIRECT_OFF) - TTkTermBase.push(TTkTermBase.CLEAR + TTkTermBase.NORMAL_SCREEN + TTkTermBase.Cursor.SHOW + TTkTermBase.escTitle()) + TTkTermBase.push(TTkTermBase.CLEAR + TTkTermBase.NORMAL_SCREEN + TTkTermBase.RESET_BRACKETED_PM + TTkTermBase.Cursor.SHOW + TTkTermBase.escTitle()) TTkTermBase.setEcho(True) TTkTermBase.CRNL(True) @staticmethod def stop(): TTkTermBase.push(TTkTermBase.Mouse.OFF + TTkTermBase.Mouse.DIRECT_OFF) - TTkTermBase.push(TTkTermBase.CLEAR + TTkTermBase.NORMAL_SCREEN + TTkTermBase.Cursor.SHOW + TTkTermBase.escTitle()) + TTkTermBase.push(TTkTermBase.CLEAR + TTkTermBase.NORMAL_SCREEN + TTkTermBase.RESET_BRACKETED_PM + TTkTermBase.Cursor.SHOW + TTkTermBase.escTitle()) TTkTermBase.setEcho(True) TTkTermBase.CRNL(True) @staticmethod def cont(): - TTkTermBase.push(TTkTermBase.ALT_SCREEN + TTkTermBase.CLEAR + TTkTermBase.Cursor.HIDE + TTkTermBase.escTitle(TTkTermBase.title)) + TTkTermBase.push(TTkTermBase.ALT_SCREEN + TTkTermBase.SET_BRACKETED_PM + TTkTermBase.CLEAR + TTkTermBase.Cursor.HIDE + TTkTermBase.escTitle(TTkTermBase.title)) if TTkTermBase.mouse: TTkTermBase.push(TTkTermBase.Mouse.ON) if TTkTermBase.directMouse: