Browse Source

Handled terminal resize event, moved all the plaform specific routines into the driver folder

pull/192/head
Eugenio Parodi 2 years ago
parent
commit
eff23e4968
  1. 12
      TermTk/TTkCore/TTkTerm/term.py
  2. 8
      TermTk/TTkCore/TTkTerm/term_base.py
  3. 15
      TermTk/TTkCore/drivers/__init__.py
  4. 40
      TermTk/TTkCore/drivers/pyodide.py
  5. 4
      TermTk/TTkCore/drivers/term_pyodide.py
  6. 4
      TermTk/TTkCore/drivers/term_unix.py
  7. 43
      TermTk/TTkCore/drivers/term_windows.py
  8. 26
      TermTk/TTkCore/drivers/unix.py
  9. 35
      TermTk/TTkCore/drivers/windows.py
  10. 19
      TermTk/TTkCore/ttk.py

12
TermTk/TTkCore/TTkTerm/term.py

@ -22,14 +22,4 @@
__all__ = ['TTkTerm']
import importlib.util
import platform
if importlib.util.find_spec('pyodideProxy'):
from .term_pyodide import TTkTerm
elif platform.system() == 'Linux':
from .term_unix import TTkTerm
elif platform.system() == 'Darwin':
from .term_unix import TTkTerm
elif platform.system() == 'Windows':
from .term_windows import TTkTerm
from ..drivers import *

8
TermTk/TTkCore/TTkTerm/term_base.py

@ -91,7 +91,7 @@ class TTkTermBase():
_sigWinChCb = None
@staticmethod
def init(title: str = "TermTk", sigmask=0):
def init(title: str = "TermTk", sigmask=0) -> None:
TTkTermBase.title = title
TTkTermBase.Cursor.hide()
TTkTermBase.push(TTkTermBase.escTitle(TTkTermBase.title))
@ -116,21 +116,21 @@ class TTkTermBase():
TTkTermBase.push(TTkTermBase.Mouse.DIRECT_OFF)
@staticmethod
def exit():
def exit() -> None:
TTkTermBase.push(TTkTermBase.Mouse.OFF + TTkTermBase.Mouse.DIRECT_OFF)
TTkTermBase.push(TTkTermBase.CLEAR + TTkTermBase.NORMAL_SCREEN + TTkTermBase.RESET_BRACKETED_PM + TTkTermBase.Cursor.SHOW + TTkTermBase.escTitle())
TTkTermBase.setEcho(True)
TTkTermBase.CRNL(True)
@staticmethod
def stop():
def stop() -> None:
TTkTermBase.push(TTkTermBase.Mouse.OFF + TTkTermBase.Mouse.DIRECT_OFF)
TTkTermBase.push(TTkTermBase.CLEAR + TTkTermBase.NORMAL_SCREEN + TTkTermBase.RESET_BRACKETED_PM + TTkTermBase.Cursor.SHOW + TTkTermBase.escTitle())
TTkTermBase.setEcho(True)
TTkTermBase.CRNL(True)
@staticmethod
def cont():
def cont() -> None:
TTkTermBase.push(TTkTermBase.ALT_SCREEN + TTkTermBase.SET_BRACKETED_PM + TTkTermBase.CLEAR + TTkTermBase.Cursor.HIDE + TTkTermBase.escTitle(TTkTermBase.title))
TTkTermBase.setMouse(TTkTermBase.mouse, TTkTermBase.directMouse)
TTkTermBase.setEcho(False)

15
TermTk/TTkCore/drivers/__init__.py

@ -0,0 +1,15 @@
import importlib.util
import platform
if importlib.util.find_spec('pyodideProxy'):
from .pyodide import *
from .term_pyodide import *
elif platform.system() == 'Linux':
from .unix import *
from .term_unix import *
elif platform.system() == 'Darwin':
from .unix import *
from .term_unix import *
elif platform.system() == 'Windows':
from .windows import *
from .term_windows import *

40
TermTk/TTkCore/drivers/pyodide.py

@ -0,0 +1,40 @@
# 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.
__all__ = ['TTkSignalDriver','TTkInputDriver']
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
class TTkInputDriver():
def close(self): pass
def cont(self): pass
def read(self): pass
class TTkSignalDriver():
sigStop = pyTTkSignal()
sigCont = pyTTkSignal()
sigInt = pyTTkSignal()
@staticmethod
def init(): pass
def exit(): pass

4
TermTk/TTkCore/TTkTerm/term_pyodide.py → TermTk/TTkCore/drivers/term_pyodide.py

@ -20,9 +20,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
__all__ = ['TTkTerm']
import pyodideProxy
from .term_base import TTkTermBase
from ..TTkTerm.term_base import TTkTermBase
class TTkTerm(TTkTermBase):
@staticmethod

4
TermTk/TTkCore/TTkTerm/term_unix.py → TermTk/TTkCore/drivers/term_unix.py

@ -20,6 +20,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
__all__ = ['TTkTerm']
import sys, os, signal
from threading import Thread, Lock
@ -28,7 +30,7 @@ except Exception as e:
print(f'ERROR: {e}')
exit(1)
from .term_base import TTkTermBase
from ..TTkTerm.term_base import TTkTermBase
from TermTk.TTkCore.log import TTkLog
class TTkTerm(TTkTermBase):

43
TermTk/TTkCore/TTkTerm/term_windows.py → TermTk/TTkCore/drivers/term_windows.py

@ -20,13 +20,22 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
__all__ = ['TTkTerm']
import sys, os, signal
from threading import Thread, Lock
from .term_base import TTkTermBase
from ..TTkTerm.term_base import TTkTermBase
from TermTk.TTkCore.log import TTkLog
from .windows import *
class TTkTerm(TTkTermBase):
# force directMouse onn Windows
# otherwise the mouse events are not received
@staticmethod
def setMouse(mouse:bool=False, directMouse:bool=False) -> None:
TTkTermBase.setMouse(mouse|directMouse, mouse|directMouse)
@staticmethod
def _push(*args):
try:
@ -51,23 +60,21 @@ class TTkTerm(TTkTermBase):
print(f'ERROR: {e}')
TTkTermBase.getTerminalSize = _getTerminalSize
@staticmethod
def _sigWinChThreaded():
if not TTkTerm._sigWinChMutex.acquire(blocking=False): return
while (TTkTerm.width, TTkTerm.height) != (wh:=TTkTerm.getTerminalSize()):
TTkTerm.width, TTkTerm.height = wh
if TTkTerm._sigWinChCb is not None:
TTkTerm._sigWinChCb(TTkTerm.width, TTkTerm.height)
TTkTerm._sigWinChMutex.release()
_sigWinChMutex = Lock()
@staticmethod
def _sigWinCh(signum, frame):
Thread(target=TTkTerm._sigWinChThreaded).start()
def _sigWinCh(w,h):
def _sigWinChThreaded():
if not TTkTerm._sigWinChMutex.acquire(blocking=False): return
while (TTkTerm.width, TTkTerm.height) != (wh:=TTkTerm.getTerminalSize()):
TTkTerm.width, TTkTerm.height = wh
if TTkTerm._sigWinChCb is not None:
TTkTerm._sigWinChCb(TTkTerm.width, TTkTerm.height)
TTkTerm._sigWinChMutex.release()
Thread(target=_sigWinChThreaded).start()
# @staticmethod
# def _registerResizeCb(callback):
# TTkTerm._sigWinChCb = callback
# # Dummy call to retrieve the terminal size
# TTkTerm._sigWinCh(signal.SIGWINCH, None)
# signal.signal(signal.SIGWINCH, TTkTerm._sigWinCh)
# TTkTermBase.registerResizeCb = _registerResizeCb
@staticmethod
def _registerResizeCb(callback):
TTkTerm._sigWinChCb = callback
TTkInputDriver.windowResized.connect(TTkTerm._sigWinCh)
TTkTermBase.registerResizeCb = _registerResizeCb

26
TermTk/TTkCore/drivers/unix.py

@ -20,9 +20,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
__all__ = ['']
__all__ = ['TTkSignalDriver','TTkInputDriver']
import sys, os, re
import signal
from select import select
try: import fcntl, termios, tty
@ -30,6 +31,8 @@ except Exception as e:
print(f'ERROR: {e}')
exit(1)
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
class TTkInputDriver():
__slots__ = ('_readPipe','_attr')
@ -66,3 +69,24 @@ class TTkInputDriver():
else:
for ch in sr:
yield ch
class TTkSignalDriver():
sigStop = pyTTkSignal()
sigCont = pyTTkSignal()
sigInt = pyTTkSignal()
@staticmethod
def init():
# Register events
signal.signal(signal.SIGTSTP, TTkSignalDriver._SIGSTOP) # Ctrl-Z
signal.signal(signal.SIGCONT, TTkSignalDriver._SIGCONT) # Resume
signal.signal(signal.SIGINT, TTkSignalDriver._SIGINT) # Ctrl-C
def exit():
signal.signal(signal.SIGINT, signal.SIG_DFL)
def _SIGSTOP(signum, frame): TTkSignalDriver.sigStop.emit()
def _SIGCONT(signum, frame): TTkSignalDriver.sigCont.emit()
def _SIGINT( signum, frame): TTkSignalDriver.sigInt.emit()

35
TermTk/TTkCore/drivers/windows.py

@ -20,15 +20,15 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
__all__ = ['']
__all__ = ['TTkSignalDriver','TTkInputDriver']
import sys
import signal
from ctypes import Structure, Union, byref, wintypes, windll
from TermTk.TTkCore.constant import TTkK
from TermTk.TTkCore.signal import pyTTkSignal, pyTTkSlot
from TermTk.TTkCore.log import TTkLog
from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent
# Based on the example ported from:
# https://learn.microsoft.com/en-us/windows/console/reading-input-buffer-events
@ -199,6 +199,7 @@ class INPUT_RECORD(Structure):
class TTkInputDriver():
windowResized = pyTTkSignal(int,int)
def __init__(self):
self._run = True
self._initTerminal()
@ -337,8 +338,28 @@ class TTkInputDriver():
# everything is received as ANSI sequence
pass
elif bb.EventType == WINDOW_BUFFER_SIZE_EVENT:
TTkLog.debug(f"{bb.Event.WindowBufferSizeEvent=}")
TTkLog.debug(f"{bb.Event.WindowBufferSizeEvent.dwSize.X=}")
TTkLog.debug(f"{bb.Event.WindowBufferSizeEvent.dwSize.Y=}")
# TTkLog.debug(f"{bb.Event.WindowBufferSizeEvent=}")
# TTkLog.debug(f"{bb.Event.WindowBufferSizeEvent.dwSize.X=}")
# TTkLog.debug(f"{bb.Event.WindowBufferSizeEvent.dwSize.Y=}")
TTkInputDriver.windowResized.emit(bb.Event.WindowBufferSizeEvent.dwSize.X, bb.Event.WindowBufferSizeEvent.dwSize.Y)
yield saveKey
class TTkSignalDriver():
sigStop = pyTTkSignal()
sigCont = pyTTkSignal()
sigInt = pyTTkSignal()
@staticmethod
def init():
# Register events
# signal.signal(signal.SIGTSTP, TTkSignalDriver._SIGSTOP) # Ctrl-Z
# signal.signal(signal.SIGCONT, TTkSignalDriver._SIGCONT) # Resume
signal.signal(signal.SIGINT, TTkSignalDriver._SIGINT) # Ctrl-C
def exit():
signal.signal(signal.SIGINT, signal.SIG_DFL)
def _SIGSTOP(signum, frame): TTkSignalDriver.sigStop.emit()
def _SIGCONT(signum, frame): TTkSignalDriver.sigCont.emit()
def _SIGINT( signum, frame): TTkSignalDriver.sigInt.emit()

19
TermTk/TTkCore/ttk.py

@ -29,6 +29,7 @@ import queue
import threading
import platform
from TermTk.TTkCore.drivers import *
from TermTk.TTkCore.TTkTerm.input import TTkInput
from TermTk.TTkCore.TTkTerm.inputkey import TTkKeyEvent
from TermTk.TTkCore.TTkTerm.inputmouse import TTkMouseEvent
@ -100,6 +101,9 @@ class TTk(TTkContainer):
self._termDirectMouse = kwargs.get('mouseTrack',False)
TTkInput.inputEvent.connect(self._processInput)
TTkInput.pasteEvent.connect(self._processPaste)
TTkSignalDriver.sigStop.connect(self._SIGSTOP)
TTkSignalDriver.sigCont.connect(self._SIGCONT)
TTkSignalDriver.sigInt.connect( self._SIGINT)
self._title = kwargs.get('title','TermTk')
self._sigmask = kwargs.get('sigmask', TTkK.NONE)
self._showMouseCursor = os.environ.get("TTK_MOUSE",kwargs.get('mouseCursor', False))
@ -146,9 +150,7 @@ class TTk(TTkContainer):
TTkLog.debug(f"screen = ({TTkTerm.getTerminalSize()})")
# Register events
signal.signal(signal.SIGTSTP, self._SIGSTOP) # Ctrl-Z
signal.signal(signal.SIGCONT, self._SIGCONT) # Resume
signal.signal(signal.SIGINT, self._SIGINT) # Ctrl-C
TTkSignalDriver.init()
TTkLog.debug("Signal Event Registered")
@ -176,7 +178,7 @@ class TTk(TTkContainer):
self._mainLoop()
finally:
if platform.system() != 'Emscripten':
signal.signal(signal.SIGINT, signal.SIG_DFL)
TTkSignalDriver.exit()
self.quit()
TTkTerm.exit()
@ -329,7 +331,8 @@ class TTk(TTkContainer):
self._paintEvent.set()
TTkInput.close()
def _SIGSTOP(self, signum, frame):
@pyTTkSlot()
def _SIGSTOP(self):
"""Reset terminal settings and stop background input read before putting to sleep"""
TTkLog.debug("Captured SIGSTOP <CTRL-z>")
TTkTerm.stop()
@ -337,7 +340,8 @@ class TTk(TTkContainer):
# TODO: stop the threads
os.kill(os.getpid(), signal.SIGSTOP)
def _SIGCONT(self, signum, frame):
@pyTTkSlot()
def _SIGCONT(self):
"""Set terminal settings and restart background input read"""
TTkLog.debug("Captured SIGCONT 'fg/bg'")
TTkTerm.cont()
@ -346,7 +350,8 @@ class TTk(TTkContainer):
# TODO: Restart threads
# TODO: Redraw the screen
def _SIGINT(self, signum, fraTERMTK_STACKTRACEme):
@pyTTkSlot()
def _SIGINT(self):
# If the "TERMTK_STACKTRACE" env variable is defined
# a stacktrace file is generated once CTRL+C is pressed
# i.e.

Loading…
Cancel
Save