Browse Source

Merge branch 'TTkTerminal' into workbench

pull/162/head
Eugenio Parodi 3 years ago
parent
commit
cb2ca30de5
  1. 118
      TermTk/TTkWidgets/TTkTerminal/terminal.py
  2. 18
      TermTk/TTkWidgets/TTkTerminal/terminal_alt.py
  3. 6
      docs/MDNotes/terminal/Vim Internals.md
  4. 13
      tests/test.input.raw.py
  5. 1
      tests/test.ui.001.frame.01.py
  6. 34
      tests/test.ui.001.window.01.py
  7. 106
      tests/timeit/18.itertools.01.groupby.py

118
TermTk/TTkWidgets/TTkTerminal/terminal.py

@ -191,6 +191,8 @@ class TTkTerminal(TTkWidget):
re_OSC_ps_Pt = re.compile('^(\d*);(.*)$')
re_XTWINOPS = re.compile('^')
@pyTTkSlot()
def _quit(self):
if self._quit_pipe:
@ -244,7 +246,7 @@ class TTkTerminal(TTkWidget):
for slice in escapeGenerator:
leftUnhandled = ""
ssss = slice.replace('\033','<ESC>').replace('\n','\\n').replace('\r','\\r')
_termLog.debug(f"slice: {ssss}")
_termLog.debug(f"slice: '{ssss}'")
################################################
# CSI Modes
@ -829,6 +831,7 @@ class TTkTerminal(TTkWidget):
return True
if ( not self._mouse.reportDrag and
evt.evt in (TTkK.Drag, TTkK.Move)):
_termLog.error(f"{self._mouse.reportDrag=} {evt.evt in (TTkK.Drag, TTkK.Move)=}")
return True
x,y = evt.x+1, evt.y+1
@ -843,21 +846,21 @@ class TTkTerminal(TTkWidget):
TTkK.Wheel : 64,
}.get(evt.key, 0)
km,pr = {
TTkK.Press: ( 0,'M'),
TTkK.Release: ( 0,'m'),
TTkK.Move: ( 0,'M'),
TTkK.Drag: (32,'M'),
TTkK.WHEEL_Up: ( 0,'M'),
TTkK.WHEEL_Down:( 1,'M')}.get(
evt.evt,(0,'M'))
# _termLog.error(f'Mouse: <ESC>[<{k+km};{x};{y}{pr}')
k,km,pr = {
TTkK.Press: (k, 0,'M'),
TTkK.Release: (k, 0,'m'),
TTkK.Move: (35, 0,'M'),
TTkK.Drag: (k, 32,'M'),
TTkK.WHEEL_Up: (k, 0,'M'),
TTkK.WHEEL_Down:(k, 1,'M')}.get(
evt.evt,(0,0,'M'))
_termLog.error(f'Mouse: <ESC>[<{k+km};{x};{y}{pr}')
self._inout.write(f'\033[<{k+km};{x};{y}{pr}'.encode())
else:
head = {
TTkK.Press: b'\033[M ',
TTkK.Release: b'\033[M#',
# TTkK.Move: b'\033[M@',
TTkK.Move: b'\033[MC',
TTkK.Drag: b'\033[M@',
TTkK.WHEEL_Up: b'\033[M`',
TTkK.WHEEL_Down:b'\033[Ma'}.get(
@ -866,7 +869,7 @@ class TTkTerminal(TTkWidget):
bah = bytearray(head)
bah.append((x+32)%0xff)
bah.append((y+32)%0xff)
# _termLog.error(f'Mouse: '+bah.decode().replace('\033','<ESC>'))
_termLog.error(f'Mouse: '+bah.decode().replace('\033','<ESC>'))
self._inout.write(bah)
return True
@ -923,6 +926,84 @@ class TTkTerminal(TTkWidget):
elif ps==5:
self._inout.write(f"\033[0n".encode())
# CSI Ps ; Ps ; Ps t
# Window manipulation (XTWINOPS), dtterm, extended by xterm.
# These controls may be disabled using the allowWindowOps
# resource.
#
# xterm uses Extended Window Manager Hints (EWMH) to maximize
# the window. Some window managers have incomplete support for
# EWMH. For instance, fvwm, flwm and quartz-wm advertise
# support for maximizing windows horizontally or vertically, but
# in fact equate those to the maximize operation.
#
# Valid values for the first (and any additional parameters)
# are:
# Ps = 1 ⇒ De-iconify window.
# Ps = 2 ⇒ Iconify window.
# Ps = 3 ; x ; y ⇒ Move window to [x, y].
# Ps = 4 ; height ; width ⇒ Resize the xterm window to
# given height and width in pixels. Omitted parameters reuse
# the current height or width. Zero parameters use the
# display's height or width.
# Ps = 5 ⇒ Raise the xterm window to the front of the
# stacking order.
# Ps = 6 ⇒ Lower the xterm window to the bottom of the
# stacking order.
# Ps = 7 ⇒ Refresh the xterm window.
# Ps = 8 ; height ; width ⇒ Resize the text area to given
# height and width in characters. Omitted parameters reuse the
# current height or width. Zero parameters use the display's
# height or width.
# Ps = 9 ; 0 ⇒ Restore maximized window.
# Ps = 9 ; 1 ⇒ Maximize window (i.e., resize to screen
# size).
# Ps = 9 ; 2 ⇒ Maximize window vertically.
# Ps = 9 ; 3 ⇒ Maximize window horizontally.
# Ps = 1 0 ; 0 ⇒ Undo full-screen mode.
# Ps = 1 0 ; 1 ⇒ Change to full-screen.
# Ps = 1 0 ; 2 ⇒ Toggle full-screen.
# Ps = 1 1 ⇒ Report xterm window state.
# If the xterm window is non-iconified, it returns CSI 1 t .
# If the xterm window is iconified, it returns CSI 2 t .
# Ps = 1 3 ⇒ Report xterm window position.
# Note: X Toolkit positions can be negative, but the reported
# values are unsigned, in the range 0-65535. Negative values
# correspond to 32768-65535.
# Result is CSI 3 ; x ; y t
# Ps = 1 3 ; 2 ⇒ Report xterm text-area position.
# Result is CSI 3 ; x ; y t
# Ps = 1 4 ⇒ Report xterm text area size in pixels.
# Result is CSI 4 ; height ; width t
# Ps = 1 4 ; 2 ⇒ Report xterm window size in pixels.
# Normally xterm's window is larger than its text area, since it
# includes the frame (or decoration) applied by the window
# manager, as well as the area used by a scroll-bar.
# Result is CSI 4 ; height ; width t
# Ps = 1 5 ⇒ Report size of the screen in pixels.
# Result is CSI 5 ; height ; width t
# Ps = 1 6 ⇒ Report xterm character cell size in pixels.
# Result is CSI 6 ; height ; width t
# Ps = 1 8 ⇒ Report the size of the text area in characters.
# Result is CSI 8 ; height ; width t
# Ps = 1 9 ⇒ Report the size of the screen in characters.
# Result is CSI 9 ; height ; width t
# Ps = 2 0 ⇒ Report xterm window's icon label.
# Result is OSC L label ST
# Ps = 2 1 ⇒ Report xterm window's title.
# Result is OSC l label ST
# Ps = 2 2 ; 0 ⇒ Save xterm icon and window title on stack.
# Ps = 2 2 ; 1 ⇒ Save xterm icon title on stack.
# Ps = 2 2 ; 2 ⇒ Save xterm window title on stack.
# Ps = 2 3 ; 0 ⇒ Restore xterm icon and window title from
# stack.
# Ps = 2 3 ; 1 ⇒ Restore xterm icon title from stack.
# Ps = 2 3 ; 2 ⇒ Restore xterm window title from stack.
# Ps >= 2 4 ⇒ Resize to Ps lines (DECSLPP), VT340 and VT420.
# xterm adapts this by resizing its window.
def _CSI_t_XTWINOPS(self, ps, _):
pass
_CSI_MAP = {
'n': _CSI_n_DSR,
}
@ -997,7 +1078,7 @@ class TTkTerminal(TTkWidget):
self._mouse.reportPress = s
self._mouse.reportDrag = False
self._mouse.reportMove = False
_termLog.info(f"1002 Mouse Tracking {s=}")
_termLog.info(f"1000 Mouse Tracking {s=}")
# CSI ? Pm h
# DEC Private Mode Set (DECSET).
@ -1027,6 +1108,16 @@ class TTkTerminal(TTkWidget):
self._mouse.reportMove = s
_termLog.info(f"1003 Mouse Tracking {s=}")
# CSI ? Pm h
# DEC Private Mode Set (DECSET).
# Ps = 1 0 0 4 ⇒ Send FocusIn/FocusOut events, xterm.
# CSI ? Pm l
# DEC Private Mode Reset (DECRST).
# Ps = 1 0 0 4 ⇒ Don't send FocusIn/FocusOut events, xterm.
def _CSI_DEC_SR_1004(self, s):
_termLog.warn(f"Unhandled 1004 Focus In/Out event {s=}")
# CSI ? Pm h
# DEC Private Mode Set (DECSET).
# Ps = 1 0 0 6 ⇒ Enable SGR Mouse Mode, xterm.
@ -1104,6 +1195,7 @@ class TTkTerminal(TTkWidget):
1000: _CSI_DEC_SR_1000,
1002: _CSI_DEC_SR_1002,
1003: _CSI_DEC_SR_1003,
1004: _CSI_DEC_SR_1004,
1006: _CSI_DEC_SR_1006,
1015: _CSI_DEC_SR_1015,
1047: _CSI_DEC_SR_1047,

18
TermTk/TTkWidgets/TTkTerminal/terminal_alt.py

@ -46,6 +46,7 @@ class _TTkTerminalAltScreen():
'_scrollingRegion',
'_bufferSize', '_bufferedLines',
'_w', '_h', '_color', '_canvas',
'_last',
# Signals
'bell'
)
@ -53,6 +54,7 @@ class _TTkTerminalAltScreen():
self.bell = pyTTkSignal()
self._w = w
self._h = h
self._last = None
self._bufferSize = bufferSize
self._bufferedLines = collections.deque(maxlen=bufferSize)
self._terminalCursor = (0,0)
@ -91,6 +93,8 @@ class _TTkTerminalAltScreen():
x,y = self._terminalCursor
w,h = self._w, self._h
st,sb = self._scrollingRegion
self._last = txt[-1] if txt else None
for bi, tout in enumerate(txt.split('\a')): # grab the bells
if bi:
self.bell.emit()
@ -507,7 +511,9 @@ class _TTkTerminalAltScreen():
def _CSI_a_HPR(self, ps, _): pass
# CSI Ps b Repeat the preceding graphic character Ps times (REP).
def _CSI_b_REP(self, ps, _): pass
def _CSI_b_REP(self, ps, _):
if self._last:
self._pushTxt(self._last*ps)
# CSI Ps c Send Device Attributes (Primary DA).
# Ps = 0 or omitted ⇒ request attributes from terminal. The
@ -1642,8 +1648,8 @@ class _TTkTerminalAltScreen():
'J': _CSI_J_ED, # CSI Ps J Erase in Display (ED), VT100. [0:Below, 1:Above, 2:All, 3:SavedLines]
'K': _CSI_K_EL, # CSI Ps K Erase in Line (EL), VT100. [0:Right, 1:Left, 2:All]
'L': _CSI_L_IL, # CSI Ps L Insert Ps Line(s) (default = 1) (IL).
'M': _CSI_M_DL,
'P': _CSI_P_DCH,
'M': _CSI_M_DL, # CSI Ps M Delete Ps Line(s) (default = 1) (DL).
'P': _CSI_P_DCH, # CSI Ps P Delete Ps Character(s) (default = 1) (DCH).
'S': _CSI_S_SU, # CSI Ps S Scroll up Ps lines (default = 1) (SU), VT420, ECMA-48.
'T': _CSI_T_SD, # CSI Ps T Scroll down Ps lines (default = 1) (SD), VT420.
# 'X': _CSI_X_ECH,
@ -1651,11 +1657,11 @@ class _TTkTerminalAltScreen():
# '^': _CSI___SD,
# '`': _CSI___HPA,
# 'a': _CSI_a_HPR,
# 'b': _CSI_b_REP,
'b': _CSI_b_REP, # CSI Ps b Repeat the preceding graphic character Ps times (REP).
# 'c': _CSI_c_Pri_DA,
'd': _CSI_d_VPA,
'd': _CSI_d_VPA, # CSI Ps d Line Position Absolute [row] (default = [1,column]) (VPA).
# 'e': _CSI_e_VPR,
'f': _CSI_f_HVP, # CSI Ps ; Ps f Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP).
'f': _CSI_f_HVP, # CSI Ps ; Ps f Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP).
# 'g': _CSI_g_TBC,
# 'h': _CSI_h_SM,
# 'i': _CSI_i_MC,

6
docs/MDNotes/terminal/Vim Internals.md

@ -0,0 +1,6 @@
### mouse drag
https://github.com/vim/vim/blob/545c8a506e7e0921ded7eb7ffe3518279cbcb16a/src/os_unix.c
CSI ? 1000 h = enable the mouse press release reported by the terminal
CSI ? 1002 h = enable the mouse press release drag reported by the mouse terminal
if vim does not recognise the ttym flags either as (SGR, RXVT, XTERM2, XTERM), the mouse drag report is not requested
Note: My fault, my .vimrc does not set xterm2 if the environment is not inside tmux

13
tests/test.input.raw.py

@ -40,6 +40,7 @@ def reset():
# Reset
TTkTerm.push("\033[?1000l")
TTkTerm.push("\033[?1002l")
TTkTerm.push("\033[?1003l")
TTkTerm.push("\033[?1015l")
TTkTerm.push("\033[?1006l")
TTkTerm.push("\033[?1049l") # Switch to normal screen
@ -47,12 +48,16 @@ def reset():
reset()
TTkTerm.push("\033[?2004h") # Paste Bracketed mode
# TTkTerm.push("\033[?1000h")
# TTkTerm.push("\033[?2004h") # Paste Bracketed mode
TTkTerm.push("\033[?2004l") # disable Paste Bracketed mode
TTkTerm.push("\033[?1049h") # Switch to alternate screen
TTkTerm.push("\033[?1000h")
# TTkTerm.push("\033[?1002h")
TTkTerm.push("\033[?1003h")
# TTkTerm.push("\033[?1006h")
# TTkTerm.push("\033[?1015h")
TTkTerm.push("\033[?1049h") # Switch to alternate screen
TTkTerm.push("\033[?1015h")
TTkTerm.push("\033[?1006h")
TTkTerm.push("\033[?25l")
# TTkTerm.push(TTkTerm.Mouse.ON)
# TTkTerm.push(TTkTerm.Mouse.DIRECT_ON)

1
tests/test.ui.001.py → tests/test.ui.001.frame.01.py

@ -31,5 +31,6 @@ ttk.TTkLog.use_default_file_logging()
root = ttk.TTk()
ttk.TTkFrame(parent=root, x=5, y=3, width=20, height=15, border=True)
# ttk.TTkWindow(parent=root, x=5, y=3, width=20, height=15, border=True)
# ttk.Button(root, text="Hello World").grid()
root.mainloop()

34
tests/test.ui.001.window.01.py

@ -0,0 +1,34 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# 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.
import sys, os
sys.path.append(os.path.join(sys.path[0],'..'))
import TermTk as ttk
ttk.TTkLog.use_default_file_logging()
root = ttk.TTk()
ttk.TTkWindow(parent=root, pops=(0,0), size=(30,5), border=True)
root.mainloop()

106
tests/timeit/18.itertools.01.groupby.py

@ -0,0 +1,106 @@
#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2023 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
#
# 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.
# This test is bases on the example in:
# https://www.geeksforgeeks.org/python-consecutive-characters-frequency/
import sys, os
import timeit
from itertools import groupby
sys.path.append(os.path.join(sys.path[0],'../..'))
testString1 = "EugenioXXXXXXXXXXXXXXXXXXParodiYYYYYYmalleoloZZZZsassso_______________122333444455555666666777777888888889999999990000000000"
def process1(txt):
ret = ""
for ch in txt:
ret += ch
return ret
def process2(txt):
# return ''.join([ch*l if (l:=len(list(j)))>4 else f"[{l}b{ch}" for ch, j in groupby(txt)])
return ''.join([ch*l if (l:=len(list(j)))<=4 else f"[{l}b{ch}" for ch, j in groupby(txt)])
def process3(txt):
chBk = txt[0]
count = 0
ret = ""
for ch in txt:
if ch == chBk:
count +=1
else:
if count>4:
ret += f"[{count}b{chBk}"
else:
ret += chBk*count
chBk = ch
count = 1
if count>4:
ret += f"[{count}b{chBk}"
else:
ret += chBk*count
return ret
def process4(txt):
chBk = txt[0]
count = 0
ret = ""
# genStr = (c for c in txt)
genStr = iter(txt)
ch = next(genStr)
while ch:
count = 1
while ch == (_ch:=next(genStr,None)):
count +=1
if count>4:
ret += f"[{count}b{ch}"
else:
ret += ch*count
ch = _ch
# if count>4:
# ret += f"[{count}b{ch}"
# else:
# ret += ch*count
return ret
def test1(): return process1(testString1)
def test2(): return process2(testString1)
def test3(): return process3(testString1)
def test4(): return process4(testString1)
loop = 100000
a={}
iii = 1
while (testName := f'test{iii}') and (testName in globals()):
result = timeit.timeit(f'{testName}(*a)', globals=globals(), number=loop)
# print(f"test{iii}) fps {loop / result :.3f} - s {result / loop:.10f} - {result / loop} {globals()[testName](*a)}")
print(f"test{iii:02}) | {result / loop:.10f} sec. | {loop / result : 15.3f} Fps ╞╡-> {globals()[testName](*a)}")
iii+=1
Loading…
Cancel
Save