Browse Source

Handled few more escape codes in the normal terminal

pull/150/head
Eugenio Parodi 3 years ago
parent
commit
ac2dfafdbc
  1. 16
      TermTk/TTkWidgets/TTkTerminal/terminal.py
  2. 18
      TermTk/TTkWidgets/TTkTerminal/terminal_alt.py
  3. 106
      TermTk/TTkWidgets/TTkTerminal/terminal_normal.py
  4. 5
      tests/test.pty.006.terminal.02.py

16
TermTk/TTkWidgets/TTkTerminal/terminal.py

@ -64,7 +64,17 @@ class TTkTerminal(TTkWidget):
super().__init__(*args, **kwargs)
w,h = self.size()
self._screen_alt.resize(w,h)
self._screen_normal.resize(w,h)
self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus)
self.enableWidgetCursor()
def resizeEvent(self, w: int, h: int):
self._screen_alt.resize(w,h)
self._screen_normal.resize(w,h)
return super().resizeEvent(w, h)
def runShell(self, program=None):
self._shell = program if program else self._shell
@ -85,7 +95,7 @@ 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^`abcdeginqx])')
re_CURSOR = re.compile('^(\d*)(;(\d*))?([@ABCDEFGIJKLMPSTXZ^`abcdeginqxHf])')
# 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 "<ESC>[" = '\033['
@ -145,6 +155,8 @@ class TTkTerminal(TTkWidget):
slice = slice.replace('\033','<ESC>').replace('\n','\\n')
TTkLog.debug(f"{slice=}")
self.update()
self.setWidgetCursor(pos=self._screen_current.getCursor())
TTkLog.debug(f"wc:{self._screen_current.getCursor()}")
def mousePressEvent(self, evt):
return True
@ -152,7 +164,6 @@ class TTkTerminal(TTkWidget):
def keyEvent(self, evt):
# TTkLog.debug(f"Key: {evt.code=}")
TTkLog.debug(f"Key: {str(evt)=}")
self._inout.write(evt.code.encode())
if evt.type == TTkK.SpecialKey:
if evt.key == TTkK.Key_Enter:
TTkLog.debug(f"Key: Enter")
@ -161,6 +172,7 @@ class TTkTerminal(TTkWidget):
else: # Input char
TTkLog.debug(f"Key: {evt.key=}")
# self._inout.write(evt.key.encode())
self._inout.write(evt.code.encode())
return True
def paintEvent(self, canvas: TTkCanvas):

18
TermTk/TTkWidgets/TTkTerminal/terminal_alt.py

@ -39,9 +39,19 @@ from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView, TTkAbst
from TermTk.TTkWidgets.widget import TTkWidget
class _TTkTerminalAltScreen():
__slots__ = ('_lines')
def __init__(self) -> None:
__slots__ = ('_lines', '_terminalCursor', '_w', '_h')
def __init__(self, w=80, h=24) -> None:
self._lines = [TTkString()]
self._terminalCursor = (0,0)
self._w = w
self._h = h
def getCursor(self):
return self._terminalCursor
def resize(self, w, h):
self._w = w
self._h = h
def pushLine(self, line:str):
lines = line.replace('\r','').split('\n')
@ -112,7 +122,7 @@ 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, _): pass
# CSI ? Ps K
# Erase in Line (DECSEL), VT220.
@ -1372,7 +1382,7 @@ class _TTkTerminalAltScreen():
'H': _CSI_H_CUP,
'I': _CSI_I_CHT,
'J': _CSI_J_ED,
'K': _CSI_K_el,
'K': _CSI_K_EL,
'L': _CSI_L_IL,
'M': _CSI_M_DL,
'P': _CSI_P_DCH,

106
TermTk/TTkWidgets/TTkTerminal/terminal_normal.py

@ -39,30 +39,52 @@ from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView, TTkAbst
from TermTk.TTkWidgets.widget import TTkWidget
class _TTkTerminalNormalScreen():
__slots__ = ('_lines', '_terminalCursor')
def __init__(self) -> None:
__slots__ = ('_lines', '_terminalCursor', '_w', '_h', '_bufferSize')
def __init__(self, w=80, h=24, bufferSize=200) -> None:
self._lines = [TTkString()]
self._terminalCursor = (0,0)
self._w = w
self._h = h
self._bufferSize = bufferSize
def getCursor(self):
x,y = self._terminalCursor
h = self._h
l = len(self._lines)
y = y if l<h else y-l+h
return x,y
def resize(self, w, h):
self._w = w
self._h = h
def _pushTxt(self, txt):
x,y = self._terminalCursor
txt = TTkString(txt)
self._terminalCursor = (x+len(txt),y)
l = self._lines[y]
if x < len(l):
ll = len(l)
if x < ll:
self._lines[y] = l.substring(to=x) + txt + l.substring(fr=x+len(txt))
else:
elif x == ll:
self._lines[y] += txt
else:
self._lines[y] += TTkString(' '*(x-ll)) + txt
def pushLine(self, line:str):
x,y = self._terminalCursor
lines = line.split('\n')
for i,l in enumerate(lines):
if i:
self._lines.append(TTkString(color=self._lines[-1]._baseColor))
self._terminalCursor = (0,len(self._lines)-1)
y += 1
if y >= len(self._lines):
self._lines.append(TTkString())
self._lines = self._lines[-self._bufferSize:]
self._terminalCursor = (x,y) = (0,min(self._bufferSize-1, y))
ls = l.split('\r')
for ii,ll in enumerate(ls):
if ii:
self._terminalCursor=(0,len(self._lines)-1)
self._terminalCursor=(0,y)
lls = ll.split('\b') # 0x08 = Backspace
for iii,lll in enumerate(lls):
if iii:
@ -70,8 +92,6 @@ class _TTkTerminalNormalScreen():
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="NORMAL Screen", pos=(0,0), width=w)
for y,l in enumerate(self._lines[-h:]):
@ -93,7 +113,9 @@ class _TTkTerminalNormalScreen():
# CSI Ps A Cursor Up Ps Times (default = 1) (CUU).
def _CSI_A_CUU(self, ps, _):
x,y = self._terminalCursor
self._terminalCursor=(x,max(0,y-ps))
l = len(self._lines)
y = max(0, l-self._h, y-ps)
self._terminalCursor=(x,y)
# CSI Ps SP A (SP = Space)
# Shift right Ps columns(s) (default = 1) (SR), ECMA-48.
@ -104,50 +126,57 @@ class _TTkTerminalNormalScreen():
# CSI Ps B Cursor Down Ps Times (default = 1) (CUD).
def _CSI_B_CUD(self, ps, _):
x,y = self._terminalCursor
# w,h = self.size()
y = min(len(self._lines)-1,y+ps)
l = len(self._lines)
y = min(l-1, y+ps)
self._terminalCursor=(x,y)
# CSI Ps C Cursor Forward Ps Times (default = 1) (CUF).
def _CSI_C_CUF(self, ps, _):
x,y = self._terminalCursor
w,h = self.size()
x = min(w-1,x+ps)
x = min(self._w-1, x+ps)
self._terminalCursor=(x,y)
# CSI Ps D Cursor Backward Ps Times (default = 1) (CUB).
def _CSI_D_CUB(self, ps, _):
x,y = self._terminalCursor
# w,h = self.size()
x = max(0,x-ps)
self._terminalCursor=(x,y)
# CSI Ps E Cursor Next Line Ps Times (default = 1) (CNL).
def _CSI_E_CNL(self, ps, _):
x,y = self._terminalCursor
# w,h = self.size()
y = min(len(self._lines)-1,y+ps)
l = len(self._lines)
y = min(l-1, y+ps)
self._terminalCursor=(0,y)
# CSI Ps F Cursor Preceding Line Ps Times (default = 1) (CPL).
def _CSI_F_CPL(self, ps, _):
x,y = self._terminalCursor
w,h = self.size()
y = max(0,len(self._lines)-h,y-ps)
l = len(self._lines)
y = max(0, l-self._h, y-ps)
self._terminalCursor=(0,y)
# CSI Ps G Cursor Character Absolute [column] (default = [row,1]) (CHA).
def _CSI_G_CHA(self, row, _):
x,y = self._terminalCursor
w,h = self.size()
self._terminalCursor=(max(0,min(w-1,row)),y)
w = self._w
x = max( 0, min(w-1, row-1))
self._terminalCursor=(x,y)
# CSI Ps ; Ps H
# Cursor Position [row;column] (default = [1,1]) (CUP).
def _CSI_H_CUP(self, row, col):
x,y = self._terminalCursor
w,h = self.size()
self._terminalCursor=(max(0,min(w-1,col)),max(0,min(len(self._lines)-1,col)))
w,h = self._w, self._h
row, col = row-1, col-1
l = len(self._lines)
oy = max(0,l-h) # y offset
ny = row + oy # y row
x = max(0, min(w-1, col))
y = max(oy, min(ny,oy+h))
self._terminalCursor=(x,y)
if y>=l:
self._lines += [TTkString()]*(y-l+1)
# CSI Ps I Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
@ -158,7 +187,19 @@ class _TTkTerminalNormalScreen():
# 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
h = self._h
l = len(self._lines)
# y = max(0,y-l-h)
if ps == 0:
self._lines[y+1:] = [TTkString()]*(l-y-1)
self._lines[y] = self._lines[y].substring(to=x)
elif ps == 1:
self._lines[-h:y-1] = [TTkString()]*(y-1-l+h)
self._lines[y] = TTkString(' '*x) + self._lines[y].substring(fr=x)
elif ps == 2:
self._lines[-h:] = [TTkString()]*(h)
# CSI ? Ps J
# Erase in Display (DECSED), VT220.
@ -171,16 +212,27 @@ class _TTkTerminalNormalScreen():
# 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
line = self._lines[y]
if ps == 0:
self._lines[y] = line.substring(to=x)
elif ps == 1:
self._lines[y] = TTkString(' '*x) + self._lines[y].substring(fr=x)
elif ps == 2:
self._lines[y] = TTkString()
# CSI ? Ps K
# Erase in Line (DECSEL), VT220.
# Ps = 0 ⇒ Selective Erase to Right (default).
# Ps = 1 ⇒ Selective Erase to Left.
# Ps = 2 ⇒ Selective Erase All.
# def _CSI_K_DeCSEL(self, ps, _): pass
# CSI Ps L Insert Ps Line(s) (default = 1) (IL).
def _CSI_L_IL(self, ps, _): pass
def _CSI_L_IL(self, ps, _):
x,y = self._terminalCursor
self._lines[y:y] = [TTkString()]*ps
# CSI Ps M Delete Ps Line(s) (default = 1) (DL).
def _CSI_M_DL(self, ps, _): pass
@ -1431,7 +1483,7 @@ class _TTkTerminalNormalScreen():
'H': _CSI_H_CUP,
'I': _CSI_I_CHT,
'J': _CSI_J_ED,
'K': _CSI_K_el,
'K': _CSI_K_EL,
'L': _CSI_L_IL,
'M': _CSI_M_DL,
'P': _CSI_P_DCH,

5
tests/test.pty.006.terminal.02.py

@ -56,15 +56,16 @@ split.addItem(top := ttk.TTkLayout())
split.addWidget(ttk.TTkLogViewer(follow=False ), title='Log')
win1 = ttk.TTkWindow(pos=(1,1), size=(70,15), title="Terminallo n.1", border=True, layout=ttk.TTkVBoxLayout(), flags = ttk.TTkK.WindowFlag.WindowMinMaxButtonsHint)
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)
term1.runShell()
win2 = ttk.TTkWindow(pos=(10,5), size=(70,15), title="Terminallo n.2", border=True, layout=ttk.TTkVBoxLayout(), flags = ttk.TTkK.WindowFlag.WindowMinMaxButtonsHint)
win2 = ttk.TTkWindow(pos=(0,0), size=(150,30), title="Terminallo n.2", border=True, layout=ttk.TTkVBoxLayout(), flags = ttk.TTkK.WindowFlag.WindowMinMaxButtonsHint)
term2 = ttk.TTkTerminal(parent=win2)
term2.runShell()
top.addWidgets([win1,win2])
term2.setFocus()
root.mainloop()
Loading…
Cancel
Save