diff --git a/TermTk/TTkCore/TTkTerm/colors.py b/TermTk/TTkCore/TTkTerm/colors.py index 93991216..b92c86b3 100644 --- a/TermTk/TTkCore/TTkTerm/colors.py +++ b/TermTk/TTkCore/TTkTerm/colors.py @@ -30,12 +30,35 @@ import re from .colors_ansi_map import ansiMap256, ansiMap16 class TTkTermColor(): - BOLD = 0x01 - ITALIC = 0x02 - UNDERLINE = 0x04 - STRIKETROUGH = 0x08 - BLINKING = 0x10 - REVERSED = 0x20 + BOLD = 0x01 # [1m + FAINT = 0x02 # [2m + ITALIC = 0x04 # [3m + UNDERLINE = 0x08 # [4m + BLINKING = 0x10 # [5m + REVERSED = 0x20 # [7m + HIDDEN = 0x40 # [8m + STRIKETROUGH = 0x80 # [9m + + SGR_SET = { # CSI Pm m Character Attributes (SGR). + # Ps = 0 ⇒ Normal (default), VT100. + 1: BOLD , # Ps = 1 ⇒ Bold, VT100. + 2: FAINT , # Ps = 2 ⇒ Faint, decreased intensity, ECMA-48 2nd. + 3: ITALIC , # Ps = 3 ⇒ Italicized, ECMA-48 2nd. + 4: UNDERLINE , # Ps = 4 ⇒ Underlined, VT100. + 5: BLINKING , # Ps = 5 ⇒ Blink, VT100. - This appears as Bold in X11R6 xterm. + 7: REVERSED , # Ps = 7 ⇒ Inverse, VT100. + 8: HIDDEN , # Ps = 8 ⇒ Invisible, i.e., hidden, ECMA-48 2nd, VT300. + 9: STRIKETROUGH } # Ps = 9 ⇒ Crossed-out characters, ECMA-48 3rd. + + SGR_RST = { # CSI Pm m Character Attributes (SGR). + # Ps = 2 1 ⇒ Doubly-underlined, ECMA-48 3rd. + 22: ~(BOLD|FAINT), # Ps = 2 2 ⇒ Normal (neither bold nor faint), ECMA-48 3rd. + 23: ~ITALIC, # Ps = 2 3 ⇒ Not italicized, ECMA-48 3rd. + 24: ~UNDERLINE, # Ps = 2 4 ⇒ Not underlined, ECMA-48 3rd. + 25: ~BLINKING, # Ps = 2 5 ⇒ Steady (not blinking), ECMA-48 3rd. + 27: ~REVERSED, # Ps = 2 7 ⇒ Positive (not inverse), ECMA-48 3rd. + 28: ~HIDDEN, # Ps = 2 8 ⇒ Visible, i.e., not hidden, ECMA-48 3rd, VT300. + 29: ~STRIKETROUGH } # Ps = 2 9 ⇒ Not crossed-out, ECMA-48 3rd. @staticmethod def rgb2ansi(fg: tuple=None, bg:tuple=None, mod:int=0, clean:bool=False): @@ -51,17 +74,20 @@ class TTkTermColor(): if mod & TTkTermColor.BOLD: ret.append('1') + if mod & TTkTermColor.FAINT: + ret.append('2') if mod & TTkTermColor.ITALIC: ret.append('3') if mod & TTkTermColor.UNDERLINE: ret.append('4') - if mod & TTkTermColor.STRIKETROUGH: - ret.append('9') if mod & TTkTermColor.BLINKING: ret.append('5') if mod & TTkTermColor.REVERSED: ret.append('7') - + if mod & TTkTermColor.HIDDEN: + ret.append('8') + if mod & TTkTermColor.STRIKETROUGH: + ret.append('9') if ret: return f'\033[{";".join(str(x) for x in ret)}m' else: @@ -111,11 +137,10 @@ class TTkTermColor(): bg = None mod = 0 clean = True - elif s==1: mod |= TTkTermColor.BOLD - elif s==3: mod |= TTkTermColor.ITALIC - elif s==4: mod |= TTkTermColor.UNDERLINE - elif s==9: mod |= TTkTermColor.STRIKETROUGH - elif s==5: mod |= TTkTermColor.BLINKING + elif _sgr:=TTkTermColor.SGR_SET.get(s,None): + mod |= _sgr + elif _sgr:=TTkTermColor.SGR_RST.get(s,None): + mod &= _sgr return fg,bg,mod,clean diff --git a/TermTk/TTkWidgets/TTkTerminal/terminal.py b/TermTk/TTkWidgets/TTkTerminal/terminal.py index d45ebdba..61873be5 100644 --- a/TermTk/TTkWidgets/TTkTerminal/terminal.py +++ b/TermTk/TTkWidgets/TTkTerminal/terminal.py @@ -219,6 +219,8 @@ class TTkTerminal(TTkWidget): yield out def loop(self, _): + SGR_SET = TTkTermColor.SGR_SET # Precacing those variables to + SGR_RST = TTkTermColor.SGR_RST # speedup the search inputgenerator = self._inputGenerator() for out in inputgenerator: sout = out.split('\033') @@ -302,12 +304,12 @@ class TTkTerminal(TTkWidget): bg = ansiMap256.get(int(values.pop(0))) if t == 2:# 24 bit bg bg = (int(values.pop(0)),int(values.pop(0)),int(values.pop(0))) - elif s==1: mod |= TTkTermColor.BOLD - elif s==3: mod |= TTkTermColor.ITALIC - elif s==4: mod |= TTkTermColor.UNDERLINE - elif s==9: mod |= TTkTermColor.STRIKETROUGH - elif s==5: mod |= TTkTermColor.BLINKING - elif s==7: mod |= TTkTermColor.REVERSED + elif _sgr:=SGR_SET.get(s,None): + mod |= _sgr + elif _sgr:=SGR_RST.get(s,None): + mod &= _sgr + else: + _termLog.warn(f"Unhandled color: {slice}") color = TTkColor(fg=fg, bg=bg, mod=mod, clean=clean) self._screen_alt.setColor(color) @@ -551,7 +553,7 @@ class TTkTerminal(TTkWidget): TTkK.WHEEL_Up: ( 0,'M'), TTkK.WHEEL_Down:( 1,'M')}.get( evt.evt,(0,'M')) - + # _termLog.error(f'Mouse: [<{k+km};{x};{y}{pr}') self._inout.write(f'\033[<{k+km};{x};{y}{pr}'.encode()) else: head = { @@ -566,16 +568,20 @@ class TTkTerminal(TTkWidget): bah = bytearray(head) bah.append((x+32)%0xff) bah.append((y+32)%0xff) + # _termLog.error(f'Mouse: '+bah.decode().replace('\033','')) self._inout.write(bah) return True def mousePressEvent(self, evt): return self._sendMouse(evt) def mouseReleaseEvent(self, evt): return self._sendMouse(evt) def mouseDragEvent(self, evt): return self._sendMouse(evt) - def mouseMoveEvent(self, evt): return self._sendMouse(evt) def wheelEvent(self, evt): return self._sendMouse(evt) def mouseTapEvent(self, evt): return self._sendMouse(evt) def mouseDoubleClickEvent(self, evt): return self._sendMouse(evt) + def mouseMoveEvent(self, evt): + if self._mouse.reportMove: + return self._sendMouse(evt) + return True def paintEvent(self, canvas: TTkCanvas): w,h = self.size() @@ -709,6 +715,20 @@ class TTkTerminal(TTkWidget): self._mouse.reportMove = False _termLog.info(f"1002 Mouse Tracking {s=}") + # CSI ? Pm h + # DEC Private Mode Set (DECSET). + # Ps = 1 0 0 3 ⇒ Use All Motion Mouse Tracking, xterm. See + # the section Any-event tracking. + # CSI ? Pm l + # DEC Private Mode Reset (DECRST). + # Ps = 1 0 0 3 ⇒ Don't use All Motion Mouse Tracking, xterm. + # See the section Any-event tracking. + def _CSI_DEC_SR_1003(self, s): + self._mouse.reportPress = s + self._mouse.reportDrag = s + self._mouse.reportMove = s + _termLog.info(f"1003 Mouse Tracking {s=}") + # CSI ? Pm h # DEC Private Mode Set (DECSET). # Ps = 1 0 0 6 ⇒ Enable SGR Mouse Mode, xterm. @@ -785,6 +805,7 @@ class TTkTerminal(TTkWidget): 25 : _CSI_DEC_SR_25_DECTCEM, 1000: _CSI_DEC_SR_1000, 1002: _CSI_DEC_SR_1002, + 1003: _CSI_DEC_SR_1003, 1006: _CSI_DEC_SR_1006, 1015: _CSI_DEC_SR_1015, 1047: _CSI_DEC_SR_1047, diff --git a/tests/test.pty.006.terminal.03.py b/tests/test.pty.006.terminal.03.py index 6a6be9a9..2359030d 100755 --- a/tests/test.pty.006.terminal.03.py +++ b/tests/test.pty.006.terminal.03.py @@ -52,7 +52,7 @@ parser.add_argument('-d', help='Debug (Add LogViewer Panel)', action='store_t args = parser.parse_args() # ttk.TTkLog.use_default_file_logging() -root = ttk.TTk(layout=ttk.TTkGridLayout()) +root = ttk.TTk(layout=ttk.TTkGridLayout(), mouseTrack=True) split = ttk.TTkSplitter(parent=root, orientation=ttk.TTkK.VERTICAL) diff --git a/tests/timeit/16.dict.02.get.py b/tests/timeit/16.dict.02.get.py new file mode 100755 index 00000000..d23edae6 --- /dev/null +++ b/tests/timeit/16.dict.02.get.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 + +# 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. + +import sys, os + +import timeit + +sys.path.append(os.path.join(sys.path[0],'../..')) +from TermTk import TTkTermColor + +testInput = [0,1,2,3,4,5,6,7,8,9,10,20,21,22,23,24,25,26,27,28,29,30,31] + +def test1(ttt=testInput): + mod = 0 + for s in ttt: + if s==1: mod |= TTkTermColor.BOLD + elif s==2: mod += TTkTermColor.FAINT + elif s==3: mod += TTkTermColor.ITALIC + elif s==4: mod += TTkTermColor.UNDERLINE + elif s==5: mod += TTkTermColor.BLINKING + elif s==7: mod += TTkTermColor.REVERSED + elif s==8: mod += TTkTermColor.HIDDEN + elif s==9: mod += TTkTermColor.STRIKETROUGH + elif s==22: mod += ~(TTkTermColor.BOLD|TTkTermColor.FAINT) + elif s==23: mod += ~TTkTermColor.ITALIC + elif s==24: mod += ~TTkTermColor.UNDERLINE + elif s==25: mod += ~TTkTermColor.BLINKING + elif s==27: mod += ~TTkTermColor.REVERSED + elif s==28: mod += ~TTkTermColor.HIDDEN + elif s==29: mod += ~TTkTermColor.STRIKETROUGH + return mod + +def test2(ttt=testInput): + mod = 0 + for s in ttt: + if mm := { + 1: TTkTermColor.BOLD , + 2: TTkTermColor.FAINT , + 3: TTkTermColor.ITALIC , + 4: TTkTermColor.UNDERLINE , + 5: TTkTermColor.BLINKING , + 7: TTkTermColor.REVERSED , + 8: TTkTermColor.HIDDEN , + 9: TTkTermColor.STRIKETROUGH + }.get(s,None): + mod += mm + elif mm := { + 22: ~(TTkTermColor.BOLD|TTkTermColor.FAINT), + 23: ~TTkTermColor.ITALIC, + 24: ~TTkTermColor.UNDERLINE, + 25: ~TTkTermColor.BLINKING, + 27: ~TTkTermColor.REVERSED, + 28: ~TTkTermColor.HIDDEN, + 29: ~TTkTermColor.STRIKETROUGH, + }.get(s,None): + mod += mm + return mod + +t3_or = { + 1: TTkTermColor.BOLD , + 2: TTkTermColor.FAINT , + 3: TTkTermColor.ITALIC , + 4: TTkTermColor.UNDERLINE , + 5: TTkTermColor.BLINKING , + 7: TTkTermColor.REVERSED , + 8: TTkTermColor.HIDDEN , + 9: TTkTermColor.STRIKETROUGH } +t3_and = { + 22: ~(TTkTermColor.BOLD|TTkTermColor.FAINT), + 23: ~TTkTermColor.ITALIC, + 24: ~TTkTermColor.UNDERLINE, + 25: ~TTkTermColor.BLINKING, + 27: ~TTkTermColor.REVERSED, + 28: ~TTkTermColor.HIDDEN, + 29: ~TTkTermColor.STRIKETROUGH } + +def test3(ttt=testInput): + mod = 0 + for s in ttt: + if mm := t3_or.get(s,None): + mod += mm + elif mm := t3_and.get(s,None): + mod += mm + return mod + +class t4(): + t4_or = { + 1: TTkTermColor.BOLD , + 2: TTkTermColor.FAINT , + 3: TTkTermColor.ITALIC , + 4: TTkTermColor.UNDERLINE , + 5: TTkTermColor.BLINKING , + 7: TTkTermColor.REVERSED , + 8: TTkTermColor.HIDDEN , + 9: TTkTermColor.STRIKETROUGH } + t4_and = { + 22: ~(TTkTermColor.BOLD|TTkTermColor.FAINT), + 23: ~TTkTermColor.ITALIC, + 24: ~TTkTermColor.UNDERLINE, + 25: ~TTkTermColor.BLINKING, + 27: ~TTkTermColor.REVERSED, + 28: ~TTkTermColor.HIDDEN, + 29: ~TTkTermColor.STRIKETROUGH } + +def test4(ttt=testInput): + mod = 0 + for s in ttt: + if mm := t4.t4_or.get(s,None): + mod += mm + elif mm := t4.t4_and.get(s,None): + mod += mm + return mod + +def test5(ttt=testInput): + mod = 0 + t5_or = t4.t4_or + t5_and = t4.t4_and + for s in ttt: + if mm := t5_or.get(s,None): + mod += mm + elif mm := t5_and.get(s,None): + mod += mm + return mod + +def test6(): return 1 +def test7(): return 1 +def test8(): return 1 +def test9(): return 1 + +loop = 150000 + +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 + diff --git a/tests/timeit/conditional.py b/tests/timeit/17.conditional.01.py similarity index 100% rename from tests/timeit/conditional.py rename to tests/timeit/17.conditional.01.py