diff --git a/TermTk/TTkCore/TTkTerm/input.py b/TermTk/TTkCore/TTkTerm/input.py index a2e71730..d32748bb 100644 --- a/TermTk/TTkCore/TTkTerm/input.py +++ b/TermTk/TTkCore/TTkTerm/input.py @@ -73,7 +73,7 @@ class TTkInput: def start(self): self._readInput = ReadInput() - while stdinRead := self._readInput.read(): + for stdinRead in self._readInput.read(): self.key_process(stdinRead) TTkLog.debug("Close TTkInput") diff --git a/TermTk/TTkCore/TTkTerm/readinputlinux.py b/TermTk/TTkCore/TTkTerm/readinputlinux.py index 38c48a08..a5852ac9 100644 --- a/TermTk/TTkCore/TTkTerm/readinputlinux.py +++ b/TermTk/TTkCore/TTkTerm/readinputlinux.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -import sys, os, select +import sys, os, select, re try: import fcntl, termios, tty except Exception as e: @@ -46,15 +46,19 @@ class ReadInput(): tty.setcbreak(sys.stdin) def read(self): - rlist, _, _ = select.select( [sys.stdin, self._readPipe[0]], [], [] ) - if self._readPipe[0] in rlist: - return None - - if (stdinRead := sys.stdin.read(1)) == "\033": # Check if the stream start with an escape sequence + rm = re.compile('(\033?[^\033]+)') + while self._readPipe[0] not in (list := select.select( [sys.stdin, self._readPipe[0]], [], [] )[0]): + # Read all the full input _fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) fcntl.fcntl(sys.stdin, fcntl.F_SETFL, _fl | os.O_NONBLOCK) # Set the input as NONBLOCK to read the full sequence - stdinRead += sys.stdin.read(20) # Check if the stream start with an escape sequence - if stdinRead.startswith("\033[<"): # Clear the buffer if this is a mouse code - sys.stdin.read(0x40) + stdinRead = sys.stdin.read() fcntl.fcntl(sys.stdin, fcntl.F_SETFL, _fl) - return stdinRead + + # Split all the ansi sequences + # or yield any separate input char + for sr in rm.findall(stdinRead): + if '\033' == sr[0]: + yield sr + else: + for ch in sr: + yield ch diff --git a/TermTk/TTkGui/textcursor.py b/TermTk/TTkGui/textcursor.py index 7a98d9f8..48c34af7 100644 --- a/TermTk/TTkGui/textcursor.py +++ b/TermTk/TTkGui/textcursor.py @@ -242,7 +242,6 @@ class TTkTextCursor(): return color def setPosition(self, line, pos, moveMode=MoveMode.MoveAnchor, cID=0): - # TTkLog.debug(f"{line=}, {pos=}, {moveMode=}") self._properties[cID].position.set(line,pos) if moveMode==TTkTextCursor.MoveAnchor: self._properties[cID].anchor.set(line,pos) @@ -483,11 +482,6 @@ class TTkTextCursor(): return True return False - def clearSelection(self): - for p in self._properties: - p.anchor.pos = p.position.pos - p.anchor.line = p.position.line - def _removeSelectedText(self): currPos = self.position().toNum() diff --git a/tests/test.input.raw.py b/tests/test.input.raw.py new file mode 100755 index 00000000..bc362e6a --- /dev/null +++ b/tests/test.input.raw.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 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, select +import logging + +try: import fcntl, termios, tty +except Exception as e: + print(f'ERROR: {e}') + exit(1) + +sys.path.append(os.path.join(sys.path[0],'..')) +from TermTk import TTkLog, TTkK, TTkInput, TTkTerm + +TTkLog.info("Retrieve Keyboard, Mouse press/drag/wheel Events") +TTkLog.info("Press q or to exit") + +TTkTerm.push(TTkTerm.Mouse.ON) +# TTkTerm.push(TTkTerm.Mouse.DIRECT_ON) +TTkTerm.setEcho(False) + +# Init +_attr = termios.tcgetattr(sys.stdin) +tty.setcbreak(sys.stdin) + +def read(): + rlist, _, _ = select.select( [sys.stdin], [], [] ) + + _fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) + fcntl.fcntl(sys.stdin, fcntl.F_SETFL, _fl | os.O_NONBLOCK) # Set the input as NONBLOCK to read the full sequence + if (stdinRead := sys.stdin.read(10000))[0] == "\033": # Check if the stream start with an escape sequence + # stdinRead += sys.stdin.read(20) # Check if the stream start with an escape sequence + # if stdinRead.startswith("\033[<"): # Clear the buffer if this is a mouse code + # sys.stdin.read(0x40) + pass + fcntl.fcntl(sys.stdin, fcntl.F_SETFL, _fl) + print(f"{len(stdinRead)=}") + return stdinRead + +def read_new(): + stdinRead = '' + while rlist := select.select( [sys.stdin], [], [] )[0]: + _fl = fcntl.fcntl(sys.stdin, fcntl.F_GETFL) + fcntl.fcntl(sys.stdin, fcntl.F_SETFL, _fl | os.O_NONBLOCK) # Set the input as NONBLOCK to read the full sequence + stdinRead = sys.stdin.read() + fcntl.fcntl(sys.stdin, fcntl.F_SETFL, _fl) + print(f"{len(stdinRead)=}") + if '\033' in stdinRead: + stdinSplit = stdinRead.split('\033') + for ansi in stdinSplit[1:]: + print(f"{ansi=}") + yield '\033'+ansi + else: + for ch in stdinRead: + yield ch + +try: + for stdinRead in read_new(): + print(f"{stdinRead=}") +finally: + termios.tcsetattr(sys.stdin, termios.TCSANOW, _attr) + TTkTerm.push(TTkTerm.Mouse.OFF + TTkTerm.Mouse.DIRECT_OFF) + TTkTerm.setEcho(True) diff --git a/tests/timeit/12.split.vs.re.py b/tests/timeit/12.split.vs.re.py new file mode 100644 index 00000000..227bffd1 --- /dev/null +++ b/tests/timeit/12.split.vs.re.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2021 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, re + +import timeit +import random +import unicodedata + +sys.path.append(os.path.join(sys.path[0],'../..')) +sys.path.append(os.path.join(sys.path[0],'.')) +import TermTk as ttk + +stra = '\033Eugenio\033Parodi' +strb = 'print([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),Eugenio\033Parodi\033Parodi\033Parodi\033Parodi\033Parodiprint([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),Eugenio\033Parodi\033Parodi\033Parodi\033Parodi\033Parodiprint([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),print([m.start() for m in rm.finditer(stra)]),Eugenio\033Parodi\033Parodi\033Parodi\033Parodi\033Parodi' +strc = 'Eugenio' +strd = '\033Eugenio' + +rm = re.compile('(\033?[^\033]+)') + +print(stra.split('\033')) +print(strb.split('\033')) +print(strc.split('\033')) + +print(rm.match(stra)) +print(rm.findall(stra)) +print(rm.findall(strb)) +print(rm.findall(strc)) +print(rm.findall(strd)) +print([m.start() for m in rm.finditer(stra)]) +print([m.start() for m in rm.finditer(strb)]) +print([m.start() for m in rm.finditer(strc)]) + + +def _re(s): + rm.findall(s) +def _old(s): + ss = s.split('\033') + rs='' + for sss in ss[1:]: + rs += '\033'+sss + return len(rs) + +def test1(v): return _re(v) +def test2(v): return _old(v) +def test3(v): return _re(v) +def test4(v): return _old(v) +def test5(v): return _re(v) +def test6(v): return _old(v) +def test7(v): return _re(v) +def test8(v): return _old(v) + +loop = 20000 + + +a=stra +result = timeit.timeit('test1(a)', globals=globals(), number=loop) +print(f"1a s {result / loop:.10f} - {result / loop} {test1(a)}") +result = timeit.timeit('test2(a)', globals=globals(), number=loop) +print(f"2a {result / loop:.10f} - {result / loop} {test2(a)}") +a=strb +result = timeit.timeit('test3(a)', globals=globals(), number=loop) +print(f"3b s {result / loop:.10f} - {result / loop} {test3(a)}") +result = timeit.timeit('test4(a)', globals=globals(), number=loop) +print(f"4b {result / loop:.10f} - {result / loop} {test4(a)}") +a=strc +result = timeit.timeit('test5(a)', globals=globals(), number=loop) +print(f"5c s {result / loop:.10f} - {result / loop} {test5(a)}") +result = timeit.timeit('test6(a)', globals=globals(), number=loop) +print(f"6c {result / loop:.10f} - {result / loop} {test6(a)}") +a=strd +result = timeit.timeit('test7(a)', globals=globals(), number=loop) +print(f"7d s {result / loop:.10f} - {result / loop} {test7(a)}") +result = timeit.timeit('test8(a)', globals=globals(), number=loop) +print(f"8d {result / loop:.10f} - {result / loop} {test8(a)}")