diff --git a/TermTk/TTkCore/TTkTerm/term.py b/TermTk/TTkCore/TTkTerm/term.py index 7256449a..6bf9af8f 100644 --- a/TermTk/TTkCore/TTkTerm/term.py +++ b/TermTk/TTkCore/TTkTerm/term.py @@ -84,6 +84,7 @@ class TTkTerm(): CTRL_C = 0x0001 CTRL_S = 0x0002 CTRL_Z = 0x0004 + CTRL_Q = 0x0008 title: str = "TermTk" mouse: bool = True @@ -92,10 +93,45 @@ class TTkTerm(): _sigWinChCb = None - # Save treminal attributes during the initialization in order to + # Save treminal attributes during the initialization in order to # restore later the original states _termAttr = termios.tcgetattr(sys.stdin) + _termAttrBk = [] + @staticmethod + def saveTermAttr(): + TTkTerm._termAttrBk.append(termios.tcgetattr(sys.stdin)) + + @staticmethod + def restoreTermAttr(): + if TTkTerm._termAttrBk: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, TTkTerm._termAttrBk.pop()) + else: + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, TTkTerm._termAttr) + + @staticmethod + def setSigmask(mask, value=True): + attr = termios.tcgetattr(sys.stdin) + if mask & TTkTerm.Sigmask.CTRL_C: + attr[6][termios.VINTR]= b'\x03' if value else 0 + if mask & TTkTerm.Sigmask.CTRL_S: + attr[6][termios.VSTOP]= b'\x13' if value else 0 + if mask & TTkTerm.Sigmask.CTRL_Z: + attr[6][termios.VSUSP]= b'\x1a' if value else 0 + if mask & TTkTerm.Sigmask.CTRL_Q: + attr[6][termios.VSTART]= b'\x11' if value else 0 + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr) + + @staticmethod + def getSigmask(): + mask = 0x00 + attr = termios.tcgetattr(sys.stdin) + mask |= TTkTerm.Sigmask.CTRL_C if attr[6][termios.VINTR] else 0 + mask |= TTkTerm.Sigmask.CTRL_S if attr[6][termios.VSTOP] else 0 + mask |= TTkTerm.Sigmask.CTRL_Z if attr[6][termios.VSUSP] else 0 + mask |= TTkTerm.Sigmask.CTRL_Q if attr[6][termios.VSTART] else 0 + return mask + @staticmethod def init(mouse: bool = True, title: str = "TermTk", sigmask=0): TTkTerm.title = title @@ -104,15 +140,7 @@ class TTkTerm(): if TTkTerm.mouse: TTkTerm.push(TTkTerm.Mouse.ON) TTkTerm.setEcho(False) - if sigmask: - attr = termios.tcgetattr(sys.stdin) - if sigmask & TTkTerm.Sigmask.CTRL_C: - attr[6][termios.VINTR]=0 - if sigmask & TTkTerm.Sigmask.CTRL_S: - attr[6][termios.VSTOP]=0 - if sigmask & TTkTerm.Sigmask.CTRL_Z: - attr[6][termios.VSUSP]=0 - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr) + TTkTerm.setSigmask(sigmask, False) @staticmethod def exit(): diff --git a/TermTk/TTkCore/helper.py b/TermTk/TTkCore/helper.py index 9f1551e6..c71d01d1 100644 --- a/TermTk/TTkCore/helper.py +++ b/TermTk/TTkCore/helper.py @@ -94,6 +94,11 @@ class TTkHelper: TTkHelper._updateBuffer = [] TTkHelper._updateWidget = [] + @staticmethod + def quit(): + if TTkHelper._rootWidget: + TTkHelper._rootWidget.quit() + @staticmethod def rootOverlay(widget): if widget is None: diff --git a/TermTk/TTkTheme/draw_ascii.py b/TermTk/TTkTheme/draw_ascii.py index 001f5ff5..02f69393 100644 --- a/TermTk/TTkTheme/draw_ascii.py +++ b/TermTk/TTkTheme/draw_ascii.py @@ -69,6 +69,12 @@ class TTkTheme(): ) buttonBox = ( + ('X','X','X', + 'X',' ','X', + 'X','X','X'), + ('X','X','X', + 'X',' ','X', + 'X','X','X'), ('X','X','X', 'X',' ','X', 'X','X','X'), @@ -76,6 +82,8 @@ class TTkTheme(): 'X',' ','X', 'X','X','X')) + combobox = {'( )','(x)'} + checkbox = {'[ ]','[x]','[/]'} hscroll = ('<','-','X','>') vscroll = ('^','|','X','v') diff --git a/demo/demo.py b/demo/demo.py index 46174bf7..32efa472 100755 --- a/demo/demo.py +++ b/demo/demo.py @@ -50,6 +50,7 @@ from showcase.fancytree import demoFancyTree from showcase.textedit import demoTextEdit from showcase.dragndrop import demoDnD from showcase.dndtabs import demoDnDTabs +from showcase.sigmask import demoSigmask words = ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit,", "sed", "do", "eiusmod", "tempor", "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua.", "Ut", "enim", "ad", "minim", "veniam,", "quis", "nostrud", "exercitation", "ullamco", "laboris", "nisi", "ut", "aliquip", "ex", "ea", "commodo", "consequat.", "Duis", "aute", "irure", "dolor", "in", "reprehenderit", "in", "voluptate", "velit", "esse", "cillum", "dolore", "eu", "fugiat", "nulla", "pariatur.", "Excepteur", "sint", "occaecat", "cupidatat", "non", "proident,", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollit", "anim", "id", "est", "laborum."] def getWord(): @@ -205,10 +206,12 @@ def demoShowcase(root=None, border=True, quit=None): tabArea.addTab(demoScrollArea(), " Scroll Area ") tabArea.addTab(demoDnD(), " Drag'n Drop ") tabArea.addTab(demoDnDTabs(), " D'n D Tabs ") + tabArea.addTab(demoSigmask(), " Sigmask ") tabAreaSources = [ 'showcase/scrollarea.py', 'showcase/dragndrop.py', - 'showcase/dndtabs.py' ] + 'showcase/dndtabs.py', + 'showcase/sigmask.py' ] tabArea.addMenu("sources", ttk.TTkK.RIGHT).menuButtonClicked.connect(lambda x : showSource(tabAreaSources[tabArea.currentIndex()])) diff --git a/demo/showcase/sigmask.py b/demo/showcase/sigmask.py new file mode 100755 index 00000000..48ca26c4 --- /dev/null +++ b/demo/showcase/sigmask.py @@ -0,0 +1,74 @@ +#!/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 os +import sys +import argparse + +sys.path.append(os.path.join(sys.path[0],'../..')) +import TermTk as ttk + +def demoSigmask(root=None): + frame = ttk.TTkFrame(parent=root, border=False) + frame.setLayout(grid:=ttk.TTkGridLayout()) + + grid.addWidget(cb_c := ttk.TTkCheckbox(text="CTRL-C (VINTR) ", checked=ttk.TTkK.Checked),0,0,1,2) + grid.addWidget(cb_s := ttk.TTkCheckbox(text="CTRL-S (VSTOP) ", checked=ttk.TTkK.Checked),1,0,1,2) + grid.addWidget(cb_z := ttk.TTkCheckbox(text="CTRL-Z (VSUSP) ", checked=ttk.TTkK.Checked),2,0,1,2) + grid.addWidget(cb_q := ttk.TTkCheckbox(text="CTRL-Q (VSTART)", checked=ttk.TTkK.Checked),3,0,1,2) + + grid.addWidget(btn_q := ttk.TTkButton(text="Quit", maxSize=(6,3), border=True),4,0) + + grid.addWidget(ttk.TTkKeyPressView(),5,0,1,3) + + btn_q.clicked.connect(ttk.TTkHelper.quit) + + cb_c.stateChanged.connect(lambda x: ttk.TTkTerm.setSigmask(ttk.TTkTerm.Sigmask.CTRL_C,x==ttk.TTkK.Checked)) + cb_s.stateChanged.connect(lambda x: ttk.TTkTerm.setSigmask(ttk.TTkTerm.Sigmask.CTRL_S,x==ttk.TTkK.Checked)) + cb_z.stateChanged.connect(lambda x: ttk.TTkTerm.setSigmask(ttk.TTkTerm.Sigmask.CTRL_Z,x==ttk.TTkK.Checked)) + cb_q.stateChanged.connect(lambda x: ttk.TTkTerm.setSigmask(ttk.TTkTerm.Sigmask.CTRL_Q,x==ttk.TTkK.Checked)) + + return frame + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('-f', help='Full Screen (default)', action='store_true') + parser.add_argument('-w', help='Windowed', action='store_true') + args = parser.parse_args() + windowed = args.w + + ttk.TTkLog.use_default_file_logging() + + root = ttk.TTk() + + if windowed: + rootDemo = ttk.TTkWindow(parent=root, pos=(0,0), size=(70,20), title="Test Text Edit", layout=ttk.TTkGridLayout(), border=True) + else: + rootDemo = root + root.setLayout(ttk.TTkGridLayout()) + demoSigmask(rootDemo) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/demo/showcase/textedit.py b/demo/showcase/textedit.py index 6c78415a..e7cc443e 100755 --- a/demo/showcase/textedit.py +++ b/demo/showcase/textedit.py @@ -226,7 +226,11 @@ def main(): ttk.TTkLog.use_default_file_logging() - root = ttk.TTk(sigmask=ttk.TTkTerm.Sigmask.CTRL_S | ttk.TTkTerm.Sigmask.CTRL_Z | ttk.TTkTerm.Sigmask.CTRL_C) + root = ttk.TTk(sigmask=( + ttk.TTkTerm.Sigmask.CTRL_Q | + ttk.TTkTerm.Sigmask.CTRL_S | + ttk.TTkTerm.Sigmask.CTRL_Z | + ttk.TTkTerm.Sigmask.CTRL_C )) if windowed: rootTree = ttk.TTkWindow(parent=root,pos = (0,0), size=(70,40), title="Test Text Edit", layout=ttk.TTkGridLayout(), border=True) else: diff --git a/docs/MDNotes/Resources.md b/docs/MDNotes/Resources.md index 8b5c8e43..36553188 100644 --- a/docs/MDNotes/Resources.md +++ b/docs/MDNotes/Resources.md @@ -74,4 +74,7 @@ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, attr) ### Terminal Mapping: - CTRL-C -> termios.VINTR - CTRL-S -> termios.VSTOP - - CTRL-Z -> termios.VSUSP \ No newline at end of file + - CTRL-Z -> termios.VSUSP + - CTRL-Q -> termios.VSTART + +Have a look at [test.termios.001.py](../../tests/test.termios.001.py) \ No newline at end of file diff --git a/docs/MDNotes/tests/test.termios.001.py b/docs/MDNotes/tests/test.termios.001.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test.termios.001.py b/tests/test.termios.001.py new file mode 100644 index 00000000..1cbfacdc --- /dev/null +++ b/tests/test.termios.001.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2022 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, termios + +attr = termios.tcgetattr(sys.stdin) + +print(f"{termios.VDISCARD=} -> {attr[6][termios.VDISCARD]}") +print(f"{termios.VEOL2=} -> {attr[6][termios.VEOL2]}") +print(f"{termios.VKILL=} -> {attr[6][termios.VKILL]}") +print(f"{termios.VQUIT=} -> {attr[6][termios.VQUIT]}") +print(f"{termios.VSTOP=} -> {attr[6][termios.VSTOP]}") +print(f"{termios.VSWTCH=} -> {attr[6][termios.VSWTCH]}") +print(f"{termios.VTDLY=} -> XXX") +print(f"{termios.VEOF=} -> {attr[6][termios.VEOF]}") +print(f"{termios.VERASE=} -> {attr[6][termios.VERASE]}") +print(f"{termios.VLNEXT=} -> {attr[6][termios.VLNEXT]}") +print(f"{termios.VREPRINT=} -> {attr[6][termios.VREPRINT]}") +print(f"{termios.VSUSP=} -> {attr[6][termios.VSUSP]}") +print(f"{termios.VT0=} -> {attr[6][termios.VT0]}") +print(f"{termios.VTIME=} -> {attr[6][termios.VTIME]}") +print(f"{termios.VEOL=} -> {attr[6][termios.VEOL]}") +print(f"{termios.VINTR=} -> {attr[6][termios.VINTR]}") +print(f"{termios.VMIN=} -> {attr[6][termios.VMIN]}") +print(f"{termios.VSTART=} -> {attr[6][termios.VSTART]}") +print(f"{termios.VSWTC=} -> {attr[6][termios.VSWTC]}") +print(f"{termios.VT1=} -> XXX") +print(f"{termios.VWERASE=} -> {attr[6][termios.VWERASE]}")