From 779ecf291defdd8013b35144cbf0dd51cad87b98 Mon Sep 17 00:00:00 2001 From: Eugenio Parodi Date: Tue, 1 Aug 2023 14:43:13 +0100 Subject: [PATCH] Basic alt mode implementation --- TermTk/TTkWidgets/TTkTerminal/mode.py | 142 ++++++++++++++ TermTk/TTkWidgets/TTkTerminal/terminal.py | 136 ++++++++++---- TermTk/TTkWidgets/TTkTerminal/terminal_alt.py | 176 +++++++++++++----- .../TTkWidgets/TTkTerminal/terminal_normal.py | 4 +- tests/test.pty.006.terminal.02.py | 2 +- 5 files changed, 371 insertions(+), 89 deletions(-) create mode 100644 TermTk/TTkWidgets/TTkTerminal/mode.py diff --git a/TermTk/TTkWidgets/TTkTerminal/mode.py b/TermTk/TTkWidgets/TTkTerminal/mode.py new file mode 100644 index 00000000..f55363f8 --- /dev/null +++ b/TermTk/TTkWidgets/TTkTerminal/mode.py @@ -0,0 +1,142 @@ +# MIT License +# +# Copyright (c) 2023 Eugenio Parodi +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class TTkTerminalModes(int): + # Ps = 1 ⇒ Application Cursor Keys (DECCKM), VT100. + # Ps = 1 ⇒ Normal Cursor Keys (DECCKM), VT100. + + # Ps = 2 ⇒ Designate VT52 mode (DECANM), VT100. + # Ps = 3 ⇒ 80 Column Mode (DECCOLM), VT100. + # Ps = 4 ⇒ Jump (Fast) Scroll (DECSCLM), VT100. + # Ps = 5 ⇒ Normal Video (DECSCNM), VT100. + # Ps = 6 ⇒ Normal Cursor Mode (DECOM), VT100. + # Ps = 7 ⇒ No Auto-Wrap Mode (DECAWM), VT100. + # Ps = 8 ⇒ No Auto-Repeat Keys (DECARM), VT100. + # Ps = 9 ⇒ Don't send Mouse X & Y on button press, xterm. + # Ps = 1 0 ⇒ Hide toolbar (rxvt). + # Ps = 1 2 ⇒ Stop blinking cursor (AT&T 610). + # Ps = 1 3 ⇒ Disable blinking cursor (reset only via + # resource or menu). + # Ps = 1 4 ⇒ Disable XOR of blinking cursor control sequence + # and menu. + # Ps = 1 8 ⇒ Don't Print Form Feed (DECPFF), VT220. + # Ps = 1 9 ⇒ Limit print to scrolling region (DECPEX), + # VT220. + # Ps = 2 5 ⇒ Hide cursor (DECTCEM), VT220. + # Ps = 3 0 ⇒ Don't show scrollbar (rxvt). + # Ps = 3 5 ⇒ Disable font-shifting functions (rxvt). + # Ps = 4 0 ⇒ Disallow 80 ⇒ 132 mode, xterm. + # Ps = 4 1 ⇒ No more(1) fix (see curses resource). + # Ps = 4 2 ⇒ Disable National Replacement Character sets + # (DECNRCM), VT220. + # Ps = 4 3 ⇒ Disable Graphic Expanded Print Mode (DECGEPM), + # VT340. + # Ps = 4 4 ⇒ Turn off margin bell, xterm. + # Ps = 4 4 ⇒ Disable Graphic Print Color Mode (DECGPCM), + # VT340. + # Ps = 4 5 ⇒ No Reverse-wraparound mode (XTREVWRAP), xterm. + # Ps = 4 5 ⇒ Disable Graphic Print Color Syntax (DECGPCS), + # VT340. + # Ps = 4 6 ⇒ Stop logging (XTLOGGING), xterm. This is + # normally disabled by a compile-time option. + # Ps = 4 7 ⇒ Use Normal Screen Buffer, xterm. + # Ps = 4 7 ⇒ Disable Graphic Rotated Print Mode (DECGRPM), + # VT340. + # Ps = 6 6 ⇒ Numeric keypad mode (DECNKM), VT320. + # Ps = 6 7 ⇒ Backarrow key sends delete (DECBKM), VT340, + # VT420. This sets the backarrowKey resource to "false". + # Ps = 6 9 ⇒ Disable left and right margin mode (DECLRMM), + # VT420 and up. + # Ps = 8 0 ⇒ Disable Sixel Display Mode (DECSDM), VT330, + # VT340, VT382. Turns on "Sixel Scrolling". See the section + # Sixel Graphics and mode 8 4 5 2 . + # Ps = 9 5 ⇒ Clear screen when DECCOLM is set/reset + # (DECNCSM), VT510 and up. + # Ps = 1 0 0 0 ⇒ Don't send Mouse X & Y on button press and + # release. See the section Mouse Tracking. + # Ps = 1 0 0 1 ⇒ Don't use Hilite Mouse Tracking, xterm. + # Ps = 1 0 0 2 ⇒ Don't use Cell Motion Mouse Tracking, + # xterm. See the section Button-event tracking. + # Ps = 1 0 0 3 ⇒ Don't use All Motion Mouse Tracking, xterm. + # See the section Any-event tracking. + # Ps = 1 0 0 4 ⇒ Don't send FocusIn/FocusOut events, xterm. + # Ps = 1 0 0 5 ⇒ Disable UTF-8 Mouse Mode, xterm. + # Ps = 1 0 0 6 ⇒ Disable SGR Mouse Mode, xterm. + # Ps = 1 0 0 7 ⇒ Disable Alternate Scroll Mode, xterm. This + # corresponds to the alternateScroll resource. + # Ps = 1 0 1 0 ⇒ Don't scroll to bottom on tty output + # (rxvt). This sets the scrollTtyOutput resource to "false". + # Ps = 1 0 1 1 ⇒ Don't scroll to bottom on key press (rxvt). + # This sets the scrollKey resource to "false". + # Ps = 1 0 1 5 ⇒ Disable urxvt Mouse Mode. + # Ps = 1 0 1 6 ⇒ Disable SGR Mouse Pixel-Mode, xterm. + # Ps = 1 0 3 4 ⇒ Don't interpret "meta" key, xterm. This + # disables the eightBitInput resource. + # Ps = 1 0 3 5 ⇒ Disable special modifiers for Alt and + # NumLock keys, xterm. This disables the numLock resource. + # Ps = 1 0 3 6 ⇒ Don't send ESC when Meta modifies a key, + # xterm. This disables the metaSendsEscape resource. + # Ps = 1 0 3 7 ⇒ Send VT220 Remove from the editing-keypad + # Delete key, xterm. + # Ps = 1 0 3 9 ⇒ Don't send ESC when Alt modifies a key, + # xterm. This disables the altSendsEscape resource. + # Ps = 1 0 4 0 ⇒ Do not keep selection when not highlighted, + # xterm. This disables the keepSelection resource. + # Ps = 1 0 4 1 ⇒ Use the PRIMARY selection, xterm. This + # disables the selectToClipboard resource. + # Ps = 1 0 4 2 ⇒ Disable Urgency window manager hint when + # Control-G is received, xterm. This disables the bellIsUrgent + # resource. + # Ps = 1 0 4 3 ⇒ Disable raising of the window when Control- + # G is received, xterm. This disables the popOnBell resource. + # Ps = 1 0 4 5 ⇒ No Extended Reverse-wraparound mode + # (XTREVWRAP2), xterm. + # Ps = 1 0 4 6 ⇒ Disable switching to/from Alternate Screen + # Buffer, xterm. This works for terminfo-based systems, + # updating the titeInhibit resource. If currently using the + # Alternate Screen Buffer, xterm switches to the Normal Screen + # Buffer. + # Ps = 1 0 4 7 ⇒ Use Normal Screen Buffer, xterm. Clear the + # screen first if in the Alternate Screen Buffer. This may be + # disabled by the titeInhibit resource. + # Ps = 1 0 4 8 ⇒ Restore cursor as in DECRC, xterm. This + # may be disabled by the titeInhibit resource. + # Ps = 1 0 4 9 ⇒ Use Normal Screen Buffer and restore cursor + # as in DECRC, xterm. This may be disabled by the titeInhibit + # resource. This combines the effects of the 1 0 4 7 and 1 0 4 + # 8 modes. Use this with terminfo-based applications rather + # than the 4 7 mode. + # Ps = 1 0 5 0 ⇒ Reset terminfo/termcap function-key mode, + # xterm. + # Ps = 1 0 5 1 ⇒ Reset Sun function-key mode, xterm. + # Ps = 1 0 5 2 ⇒ Reset HP function-key mode, xterm. + # Ps = 1 0 5 3 ⇒ Reset SCO function-key mode, xterm. + # Ps = 1 0 6 0 ⇒ Reset legacy keyboard emulation, i.e, + # X11R6, xterm. + # Ps = 1 0 6 1 ⇒ Reset keyboard emulation to Sun/PC style, + # xterm. + # Ps = 2 0 0 1 ⇒ Disable readline mouse button-1, xterm. + # Ps = 2 0 0 2 ⇒ Disable readline mouse button-2, xterm. + # Ps = 2 0 0 3 ⇒ Disable readline mouse button-3, xterm. + # Ps = 2 0 0 4 ⇒ Reset bracketed paste mode, xterm. + # Ps = 2 0 0 5 ⇒ Disable readline character-quoting, xterm. + # Ps = 2 0 0 6 ⇒ Disable readline newline pasting, xterm. diff --git a/TermTk/TTkWidgets/TTkTerminal/terminal.py b/TermTk/TTkWidgets/TTkTerminal/terminal.py index ece26237..349ce9a6 100644 --- a/TermTk/TTkWidgets/TTkTerminal/terminal.py +++ b/TermTk/TTkWidgets/TTkTerminal/terminal.py @@ -67,6 +67,9 @@ class TTkTerminal(TTkWidget): super().__init__(*args, **kwargs) + # self._screen_alt._CSI_MAP |= self._CSI_MAP + # self._screen_current._CSI_MAP |= self._CSI_MAP + w,h = self.size() self._screen_alt.resize(w,h) self._screen_normal.resize(w,h) @@ -86,7 +89,7 @@ class TTkTerminal(TTkWidget): if pid == 0: argv = [self._shell] - env = dict(TERM="screen", DISPLAY=":1") + env = os.environ os.execvpe(argv[0], argv, env) else: self._inout = os.fdopen(self._fd, "w+b", 0) @@ -98,20 +101,21 @@ class TTkTerminal(TTkWidget): # xterm escape sequences from: # https://invisible-island.net/xterm/ctlseqs/ctlseqs.html # https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ - re_CURSOR = re.compile('^((\d*)(;(\d*))*)([@ABCDEFGIJKLMPSTXZ^`abcdeginmqxHf])') + re_CURSOR = re.compile('^\[((\d*)(;(\d*))*)([@^`A-Za-z])') # https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ # Basic Re for CSI Ps matches: # CSI : Control Sequence Introducer "[" = '\033[' # Ps : A single (usually optional) numeric parameter, composed of one or more digits. # fn : the single char defining the function - re_CSI_Ps_fu = re.compile('^(\d*)([@ABCDEFGIJKLMPSTXZ^`abcdeginqx])') - re_CSI_Ps_Ps_fu = re.compile('^(\d*);(\d*)([Hf])') - re_DEC_SET_RST = re.compile('^\?(\d+)([lh])') + re_CSI_Ps_fu = re.compile('^\[(\d*)([@ABCDEFGIJKLMPSTXZ^`abcdeghinqx])') + re_CSI_Ps_Ps_fu = re.compile('^\[(\d*);(\d*)([Hf])') + + re_DEC_SET_RST = re.compile('^\[\?(\d+)([lh])') # re_CURSOR_1 = re.compile(r'^(\d+)([ABCDEFGIJKLMPSTXZHf])') def loop(self, _): while rs := select( [self._inout], [], [])[0]: - TTkLog.debug(f"Select - {rs=}") + # TTkLog.debug(f"Select - {rs=}") for r in rs: if r is self._inout: try: @@ -119,39 +123,34 @@ class TTkTerminal(TTkWidget): except Exception as e: TTkLog.error(f"Error: {e=}") return - out = out.decode('utf-8','ignore') + # out = out.decode('utf-8','ignore') + out = out.decode() for bi, bout in enumerate(out.split('\a')): # grab the bells # TTkLog.debug(f'Eugenio->{out}') # sout = bout.decode('utf-8','ignore') if bi: TTkLog.debug("BELL!!! 🔔🔔🔔") - sout = bout.split('\033[') - self._screen_current.pushLine(sout[0]) + sout = bout.split('\033') TTkLog.debug(f"{sout[0]=}") + self._screen_current.pushLine(sout[0]) for slice in sout[1:]: + ssss = slice.replace('\033','').replace('\n','\\n').replace('\r','\\r') + TTkLog.debug(f"slice: {ssss}") if m := TTkTerminal.re_DEC_SET_RST.match(slice): - # l = len(m.group(0)) en = m.end() ps = int(m.group(1)) - sr = m.group(2) - if ps == 1049 and sr == 'l': # NORMAL SCREEN - self._screen_current = self._screen_normal - elif ps == 1049 and sr == 'h': # ALT SCREEN - self._screen_current = self._screen_alt - elif ps == 2004: - # Bracketedpaste ON - # Bracketedpaste OFF - pass + sr = (m.group(2) == 'h') + self._CSI_DEC_SET_RST(ps,sr) slice = slice[en:] elif ( (m := TTkTerminal.re_CURSOR.match(slice)) and (mg := m.groups()) and mg[-1] == 'm' ): - TTkLog.debug(f"Handle color {mg[0]=}") + # TTkLog.debug(f"Handle color {mg[0]=}") en = m.end() color=TTkColor.RST - if mg[0] != '0m': + if mg[0] not in ['0','']: values = mg[0].split(';') fg = None @@ -196,28 +195,47 @@ class TTkTerminal(TTkWidget): self._screen_alt.setColor(color) self._screen_normal.setColor(color) - TTkLog.debug(TTkString(f"color - [{mg[0]}",color).toAnsi()) + # TTkLog.debug(TTkString(f"color - [{mg[0]}",color).toAnsi()) slice = slice[en:] elif m: en = m.end() - y = ps = int(y) if (y:=m.group(1)) else 1 - sep = m.group(2) - x = int(x) if (x:=m.group(3)) else 1 - fn = m.group(4) - TTkLog.debug(f"ps:{y=} {sep=} {x=} {fn=}") - _ex = self._screen_current._CSI_MAP.get( - fn, - lambda a,b,c: TTkLog.warn(f"Unhandled {y=} {sep=} {x=} {fn=}")) - _ex(self._screen_current,y,x) + y = ps = int(y) if (y:=m.group(2)) else 1 + sep = m.group(3) + x = int(x) if (x:=m.group(4)) else 1 + fn = m.group(5) + TTkLog.debug(f"{mg[0]}{fn} = ps:{y=} {sep=} {x=} {fn=}") + if fn in ['n']: + _ex = self._CSI_MAP.get( + fn, + lambda a,b,c: TTkLog.warn(f"Unhandled [{mg[0]}{fn} = ps:{y=} {sep=} {x=} {fn=}")) + _ex(self,y,x) + else: + _ex = self._screen_current._CSI_MAP.get( + fn, + lambda a,b,c: TTkLog.warn(f"Unhandled [{mg[0]}{fn} = ps:{y=} {sep=} {x=} {fn=}")) + _ex(self._screen_current,y,x) slice = slice[en:] else: - slice = '\033[' + slice.replace('\r','') + slice = '\033' + slice.replace('\r','') + ssss = slice.replace('\033','').replace('\n','\\n') + TTkLog.warn(f"Unhandled Slice:{ssss}") # TTkLog.debug(f'Eugenio->{slice}') - self._screen_current.pushLine(slice) - slice = slice.replace('\033','').replace('\n','\\n') - TTkLog.debug(f"{slice=}") + if '\033' in slice: + # import time + # ssss = slice.replace('\033','').replace('\n','\\n') + # TTkLog.warn(f"WAIT FOR Unhandled Slice:{ssss}") + # time.sleep(0.5) + # self._screen_current.pushLine(slice) + # TTkLog.warn(f"DONE!!!! Unhandled Slice:{ssss}") + slice = slice.replace('\033','').replace('\n','\\n') + TTkLog.warn(f"Unhandled slice: {slice=}") + # time.sleep(1.5) + else: + self._screen_current.pushLine(slice) + slice = slice.replace('\033','').replace('\n','\\n') + # TTkLog.debug(f"{slice=}") self.update() self.setWidgetCursor(pos=self._screen_current.getCursor()) TTkLog.debug(f"wc:{self._screen_current.getCursor()}") @@ -242,3 +260,51 @@ class TTkTerminal(TTkWidget): def paintEvent(self, canvas: TTkCanvas): w,h = self.size() self._screen_current.paintEvent(canvas,w,h) + + # CSI Ps n Device Status Report (DSR). + # Ps = 5 ⇒ Status Report. + # Result ("OK") is CSI 0 n + # Ps = 6 ⇒ Report Cursor Position (CPR) [row;column]. + # Result is CSI r ; c R + # + # Note: it is possible for this sequence to be sent by a + # function key. For example, with the default keyboard + # configuration the shifted F1 key may send (with shift-, + # control-, alt-modifiers) + # + # CSI 1 ; 2 R , or + # CSI 1 ; 5 R , or + # CSI 1 ; 6 R , etc. + # + # The second parameter encodes the modifiers; values range from + # 2 to 16. See the section PC-Style Function Keys for the + # codes. The modifyFunctionKeys and modifyKeyboard resources + # can change the form of the string sent from the modified F1 + # key. + def _CSI_n_DSR(self, ps, _): + x,y = self._screen_current.getCursor() + if ps==6: + self._inout.write(f"\033[{y+1};{x+1}R".encode()) + elif ps==5: + self._inout.write(f"\033[0n".encode()) + + _CSI_MAP = { + 'n': _CSI_n_DSR, + } + + def _CSI_DEC_SET_RST(self, ps, s): + _dec = self._CSI_DEC_SET_RST_MAP.get( + ps, + lambda a,b: TTkLog.warn(f"Unhandled DEC [{ps}{'h' if s else 'l'}")) + _dec(self, s) + + def _CSI_DEC_SR_1049(self, s): + if s: + self._screen_current = self._screen_alt + else: + self._screen_current = self._screen_normal + + + _CSI_DEC_SET_RST_MAP = { + 1049: _CSI_DEC_SR_1049 + } \ No newline at end of file diff --git a/TermTk/TTkWidgets/TTkTerminal/terminal_alt.py b/TermTk/TTkWidgets/TTkTerminal/terminal_alt.py index b4e993ec..30eebbb5 100644 --- a/TermTk/TTkWidgets/TTkTerminal/terminal_alt.py +++ b/TermTk/TTkWidgets/TTkTerminal/terminal_alt.py @@ -39,13 +39,14 @@ from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView, TTkAbst from TermTk.TTkWidgets.widget import TTkWidget class _TTkTerminalAltScreen(): - __slots__ = ('_lines', '_terminalCursor', '_w', '_h', '_color') + __slots__ = ('_lines', '_terminalCursor', '_w', '_h', '_color', '_canvas') def __init__(self, w=80, h=24, color=TTkColor.RST) -> None: self._lines = [TTkString()] self._terminalCursor = (0,0) self._w = w self._h = h self._color = color + self._canvas = TTkCanvas(width=w, height=h) def setColor(self, color): self._color = color @@ -56,64 +57,129 @@ class _TTkTerminalAltScreen(): def resize(self, w, h): self._w = w self._h = h + newCanvas = TTkCanvas(width=w, height=h) + s = (0,0,w,h) + newCanvas.paintCanvas(self._canvas,s,s,s) + + self._canvas = newCanvas + x,y = self._terminalCursor + self._terminalCursor = (min(x,w-1),min(y,h-1)) + + def _pushTxt(self, txt): + x,y = self._terminalCursor + w,h = self._w, self._h + txt = TTkString(txt, self._color) + self._terminalCursor = (min(w-1,x+len(txt)),y) + self._canvas.drawTTkString(text=txt, pos=(x,y)) def pushLine(self, line:str): - lines = line.replace('\r','').split('\n') + if not line: return + x,y = self._terminalCursor + w,h = self._w, self._h + lines = line.split('\n') for i,l in enumerate(lines): if i: - self._lines.append(TTkString(l)) - else: - self._lines[-1] += TTkString(l) + self._terminalCursor = (x,y) = (0,min(h-1,y+1)) + ls = l.split('\r') + for ii,ll in enumerate(ls): + if ii: + self._terminalCursor = (x,y) = (0,y) + lls = ll.split('\b') # 0x08 = Backspace + for iii,lll in enumerate(lls): + if iii: + self._terminalCursor = (max(0,x-1),y) + self._pushTxt(lll) def paintEvent(self, canvas: TTkCanvas, w:int, h:int, ox:int=0, oy:int=0) -> None: - canvas.drawText(text="ALT Screen", pos=(0,0), width=w) - + w,h = self._w, self._h + s = (0,0,w,h) + canvas.paintCanvas(self._canvas,s,s,s) + TTkLog.debug("Paint") # CSI Ps @ Insert Ps (Blank) Character(s) (default = 1) (ICH). - def _CSI___ICH(self, ps, _): pass + def _CSI___ICH(self, ps, _): + x,y = self._terminalCursor + self._canvas.drawText(' '*ps,pos=(x,y)) # CSI Ps SP @ (SP = Space) # Shift left Ps columns(s) (default = 1) (SL), ECMA-48. - def _CSI___SL( self, ps, _): pass + def _CSI___SL( self, ps, _): + x,y = self._terminalCursor + self._canvas.drawText(' '*ps,pos=(x,y)) # CSI Ps A Cursor Up Ps Times (default = 1) (CUU). - def _CSI_A_CUU(self, ps, _): pass + def _CSI_A_CUU(self, ps, _): + x,y = self._terminalCursor + self._terminalCursor = (x,max(0,y-ps)) # CSI Ps SP A (SP = Space) # Shift right Ps columns(s) (default = 1) (SR), ECMA-48. - def _CSI_A_SR( self, ps, _): pass + # def _CSI_A_SR( self, ps, _): + # x,y = self._terminalCursor + # w,h = self._w, self._h + # self._terminalCursor = (min(x+ps,w-1),y) # CSI Ps B Cursor Down Ps Times (default = 1) (CUD). - def _CSI_B_CUD(self, ps, _): pass + def _CSI_B_CUD(self, ps, _): + x,y = self._terminalCursor + w,h = self._w, self._h + self._terminalCursor = (x,min(y+ps,h-1)) # CSI Ps C Cursor Forward Ps Times (default = 1) (CUF). - def _CSI_C_CUF(self, ps, _): pass + def _CSI_C_CUF(self, ps, _): + x,y = self._terminalCursor + w,h = self._w, self._h + self._terminalCursor = (min(x+ps,w-1),y) # CSI Ps D Cursor Backward Ps Times (default = 1) (CUB). - def _CSI_D_CUB(self, ps, _): pass + def _CSI_D_CUB(self, ps, _): + x,y = self._terminalCursor + self._terminalCursor = (max(0,x-ps),y) # CSI Ps E Cursor Next Line Ps Times (default = 1) (CNL). - def _CSI_E_CNL(self, ps, _): pass + def _CSI_E_CNL(self, ps, _): + x,y = self._terminalCursor + w,h = self._w, self._h + self._terminalCursor = (0,min(y+ps,h-1)) # CSI Ps F Cursor Preceding Line Ps Times (default = 1) (CPL). - def _CSI_F_CPL(self, ps, _): pass + def _CSI_F_CPL(self, ps, _): + x,y = self._terminalCursor + self._terminalCursor = (0,max(0,y-ps)) # CSI Ps G Cursor Character Absolute [column] (default = [row,1]) (CHA). - def _CSI_G_CHA(self, ps, _): pass + def _CSI_G_CHA(self, col, _): + x,y = self._terminalCursor + w,h = self._w, self._h + self._terminalCursor = (min(col-1,w-1),y) + # CSI Ps ; Ps H # Cursor Position [row;column] (default = [1,1]) (CUP). - def _CSI_H_CUP(self, row, col): pass + def _CSI_H_CUP(self, row, col): + x,y = self._terminalCursor + w,h = self._w, self._h + self._terminalCursor = (min(col-1,w-1), min(row-1,h-1)) # CSI Ps I Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). - def _CSI_I_CHT(self, ps, _): pass + # def _CSI_I_CHT(self, ps, _): pass # CSI Ps J Erase in Display (ED), VT100. # Ps = 0 ⇒ Erase Below (default). # Ps = 1 ⇒ Erase Above. # Ps = 2 ⇒ Erase All. # Ps = 3 ⇒ Erase Saved Lines, xterm. - def _CSI_J_ED(self, ps, _): pass + def _CSI_J_ED(self, ps, _): + x,y = self._terminalCursor + w,h = self._w,self._h + if ps == 0: + self._canvas.fill(char=' ', pos=(0,y+1),size=(w,h)) + self._canvas.fill(char=' ', pos=(x,y),size=(w,1)) + elif ps == 1: + self._canvas.fill(char=' ', pos=(0,0),size=(w,y-1)) + self._canvas.fill(char=' ', pos=(0,y),size=(x,1)) + elif ps == 2: + self._canvas.fill(char=' ', pos=(0,0),size=(w,h)) # CSI ? Ps J # Erase in Display (DECSED), VT220. @@ -126,7 +192,15 @@ class _TTkTerminalAltScreen(): # Ps = 0 ⇒ Erase to Right (default). # Ps = 1 ⇒ Erase to Left. # Ps = 2 ⇒ Erase All. - def _CSI_K_EL(self, ps, _): pass + def _CSI_K_EL(self, ps, _): + x,y = self._terminalCursor + w,h = self._w,self._h + if ps == 0: + self._canvas.fill(char=' ', pos=(0,y),size=(x,1)) + elif ps == 1: + self._canvas.fill(char=' ', pos=(x,y),size=(w,1)) + elif ps == 2: + self._canvas.fill(char=' ', pos=(0,y),size=(w,1)) # CSI ? Ps K # Erase in Line (DECSEL), VT220. @@ -135,13 +209,13 @@ class _TTkTerminalAltScreen(): # Ps = 2 ⇒ Selective Erase All. # CSI Ps L Insert Ps Line(s) (default = 1) (IL). - def _CSI_L_IL(self, ps, _): pass + # def _CSI_L_IL(self, ps, _): pass # CSI Ps M Delete Ps Line(s) (default = 1) (DL). - def _CSI_M_DL(self, ps, _): pass + # def _CSI_M_DL(self, ps, _): pass # CSI Ps P Delete Ps Character(s) (default = 1) (DCH). - def _CSI_P_DCH(self, ps, _): pass + # def _CSI_P_DCH(self, ps, _): pass # CSI # P # CSI Pm # P @@ -841,7 +915,7 @@ class _TTkTerminalAltScreen(): # codes. The modifyFunctionKeys and modifyKeyboard resources # can change the form of the string sent from the modified F1 # key. - def _CSI_n_DSR(self, ps, _): pass + # def _CSI_n_DSR(self, ps, _): pass # CSI > Ps n # Disable key modifier options, xterm. These modifiers may be @@ -1384,32 +1458,32 @@ class _TTkTerminalAltScreen(): 'F': _CSI_F_CPL, 'G': _CSI_G_CHA, 'H': _CSI_H_CUP, - 'I': _CSI_I_CHT, + # 'I': _CSI_I_CHT, 'J': _CSI_J_ED, 'K': _CSI_K_EL, - 'L': _CSI_L_IL, - 'M': _CSI_M_DL, - 'P': _CSI_P_DCH, - 'S': _CSI_S_SU, - 'T': _CSI_T_SD, - 'X': _CSI_X_ECH, - 'Z': _CSI_Z_CBT, - '^': _CSI___SD, - '`': _CSI___HPA, - 'a': _CSI_a_HPR, - 'b': _CSI_b_REP, - 'c': _CSI_c_Pri_DA, - 'd': _CSI_d_VPA, - 'e': _CSI_e_VPR, - 'f': _CSI_f_HVP, - 'g': _CSI_g_TBC, - 'h': _CSI_h_SM, - 'i': _CSI_i_MC, - 'l': _CSI_l_RM, - 'm': _CSI_m_SGR, - 'n': _CSI_n_DSR, - 'q': _CSI_q_DECLL, - 's': _CSI_s_SCOSC, - 'u': _CSI_u_SCORC, - 'x': _CSI_x_DECREQTPARM + # 'L': _CSI_L_IL, + # 'M': _CSI_M_DL, + # 'P': _CSI_P_DCH, + # 'S': _CSI_S_SU, + # 'T': _CSI_T_SD, + # 'X': _CSI_X_ECH, + # 'Z': _CSI_Z_CBT, + # '^': _CSI___SD, + # '`': _CSI___HPA, + # 'a': _CSI_a_HPR, + # 'b': _CSI_b_REP, + # 'c': _CSI_c_Pri_DA, + # 'd': _CSI_d_VPA, + # 'e': _CSI_e_VPR, + # 'f': _CSI_f_HVP, + # 'g': _CSI_g_TBC, + # 'h': _CSI_h_SM, + # 'i': _CSI_i_MC, + # 'l': _CSI_l_RM, + # 'm': _CSI_m_SGR, + # 'n': _CSI_n_DSR, + # 'q': _CSI_q_DECLL, + # 's': _CSI_s_SCOSC, + # 'u': _CSI_u_SCORC, + # 'x': _CSI_x_DECREQTPARM } diff --git a/TermTk/TTkWidgets/TTkTerminal/terminal_normal.py b/TermTk/TTkWidgets/TTkTerminal/terminal_normal.py index a4bb93ec..5a6f9417 100644 --- a/TermTk/TTkWidgets/TTkTerminal/terminal_normal.py +++ b/TermTk/TTkWidgets/TTkTerminal/terminal_normal.py @@ -184,7 +184,7 @@ class _TTkTerminalNormalScreen(): # CSI Ps I Cursor Forward Tabulation Ps tab stops (default = 1) (CHT). - def _CSI_I_CHT(self, ps, _): pass + # def _CSI_I_CHT(self, ps, _): pass # CSI Ps J Erase in Display (ED), VT100. # Ps = 0 ⇒ Erase Below (default). @@ -1510,7 +1510,7 @@ class _TTkTerminalNormalScreen(): 'F': _CSI_F_CPL, 'G': _CSI_G_CHA, 'H': _CSI_H_CUP, - 'I': _CSI_I_CHT, + # 'I': _CSI_I_CHT, 'J': _CSI_J_ED, 'K': _CSI_K_EL, 'L': _CSI_L_IL, diff --git a/tests/test.pty.006.terminal.02.py b/tests/test.pty.006.terminal.02.py index 671197e1..51fed843 100755 --- a/tests/test.pty.006.terminal.02.py +++ b/tests/test.pty.006.terminal.02.py @@ -54,7 +54,7 @@ split = ttk.TTkSplitter(parent=root, orientation=ttk.TTkK.VERTICAL) split.addItem(top := ttk.TTkLayout()) -split.addWidget(ttk.TTkLogViewer(follow=False ), title='Log') +split.addWidget(ttk.TTkLogViewer(follow=False ), title='Log', size=20) win1 = ttk.TTkWindow(pos=(90,5), size=(70,15), title="Terminallo n.1", border=True, layout=ttk.TTkVBoxLayout(), flags = ttk.TTkK.WindowFlag.WindowMinMaxButtonsHint) term1 = ttk.TTkTerminal(parent=win1)