Commits

Henning Schröder committed 5efc22e

cleanup/refactoring, current code does not work because it depends on cursesengine

Comments (0)

Files changed (11)

tgui/ansiconsole.py

+# -*- coding: utf-8 -*-
+import atexit
+import sys
+import os
+import fcntl
+import termios
+import struct
+import signal
+
+write = sys.stdout.write
+
+# http://en.wikipedia.org/wiki/ANSI_escape_code
+ESC_CLEAR_SCREEN = "\033[2J"
+ESC_GOTOXY = "\033[%d;%dH"
+ESC_COLOR = "\033[%dm " 
+ESC_RESET = ESC_COLOR % 0
+ESC_BRIGHT = ESC_COLOR % 1
+ESC_DIM = ESC_COLOR % 2
+ESC_NORMAL = ESC_COLOR % 22
+ESC_HIDE_CURSOR = "\033[?25l"
+ESC_SHOW_CURSOR = "\033[?25h"
+
+ESC_BACKGROUND_COLORS = {}
+ESC_FOREGROUND_COLORS = {}
+_color_names = """black red green yellow blue magenta cyan white"""
+for i, _cn in enumerate(_color_names.split()):
+    _bright_cn = "bright-%s" % _cn
+    intern(_cn)
+    intern(_bright_cn)
+    ESC_FOREGROUND_COLORS[_cn] = ESC_COLOR % (30 + i) + ESC_NORMAL
+    ESC_BACKGROUND_COLORS[_cn] = ESC_COLOR % (40 + i) + ESC_NORMAL
+    ESC_FOREGROUND_COLORS["bright-%s" % _cn] = ESC_COLOR % (30 + i) + ESC_BRIGHT
+    ESC_BACKGROUND_COLORS["bright-%s" % _cn] = ESC_COLOR % (40 + i) + ESC_BRIGHT
+
+# http://en.wikipedia.org/wiki/Box-drawing_character
+class UnicodeBorder(object):
+    BORDER_TOP_LEFT = u"\u250c"      # ┌
+    BORDER_TOP_RIGHT = u"\u2510"     # ┐
+    BORDER_VLINE = u"\u2502"         # │
+    BORDER_HLINE = u"\u2500"         # ─
+    BORDER_BOTTOM_LEFT = u"\u2514"   # └
+    BORDER_BOTTOM_RIGHT = u"\u2518"  # ┘
+
+class AnsiBorder(object):
+    ESC_BORDER = "\033(0%s\033(B"
+    BORDER_TOP_LEFT = ESC_BORDER % "\x6c"       # ┌
+    BORDER_TOP_RIGHT = ESC_BORDER % "\x6b"      # ┐
+    BORDER_VLINE = ESC_BORDER % "\x78"          # │
+    BORDER_HLINE = ESC_BORDER % "\x71"          # ─
+    BORDER_BOTTOM_LEFT = ESC_BORDER % "\x6d"    # └
+    BORDER_BOTTOM_RIGHT = ESC_BORDER % "\x6a"   # ┘
+
+
+def show_cursor():
+    write(ESC_SHOW_CURSOR)
+
+def _hide_cursor_direct():
+    write(ESC_HIDE_CURSOR)
+
+def hide_cursor():
+    global hide_cursor
+    atexit.register(show_cursor)
+    hide_cursor = _hide_cursor_direct
+    _hide_cursor_direct()
+
+def fg_color(foreground):
+    write(ESC_FOREGROUND_COLORS[foreground])
+
+def bg_color(background):
+    write(ESC_BACKGROUND_COLORS[background])
+
+def color(foreground, background):
+    write(ESC_BACKGROUND_COLORS[background])
+    write(ESC_FOREGROUND_COLORS[foreground])
+
+def clear_screen():
+    color("white", "black")
+    write(ESC_CLEAR_SCREEN)
+    write(ESC_RESET)
+    write(ESC_NORMAL)
+
+
+@atexit.register
+def _exit_handler():
+    write(ESC_RESET)
+    color("white", "black")
+
+def gotoxy(x, y):
+    write(ESC_GOTOXY % (y + 1, x))
+
+    
+def text(x, y, fg, bg, s):
+    out = ESC_GOTOXY % (y, x) 
+    out += ESC_BACKGROUND_COLORS[bg]
+    out += ESC_FOREGROUND_COLORS[fg]
+    out += s
+    write(out)
+
+def box(x1, y1, x2, y2, fill_char=u" ", border=AnsiBorder):
+    width = x2 - y1 - 2
+    inner = border.BORDER_VLINE + (fill_char * width) + border.BORDER_VLINE
+    hline = (border.BORDER_HLINE * width)
+    top = border.BORDER_TOP_LEFT + hline + border.BORDER_TOP_RIGHT
+    gotoxy(x1, y1)
+    write(top)
+    for y in range(y1 + 1, y2):
+        if fill_char:
+            gotoxy(x1, y)
+            write(inner)
+        else:
+            gotoxy(x1, y)
+            write(border.BORDER_VLINE)
+            gotoxy(x2, y)
+            write(border.BORDER_VLINE)
+    bottom = border.BORDER_BOTTOM_LEFT + hline + border.BORDER_BOTTOM_RIGHT
+    gotoxy(x1, y2)
+    write(bottom)
+
+
+def terminal_size():
+    # adapted from
+    # http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python
+
+    #h, w, hp, wp = struct.unpack('HHHH',
+    #    fcntl.ioctl(0, termios.TIOCGWINSZ,
+    #    struct.pack('HHHH', 0, 0, 0, 0)))
+    # return w, h
+
+    def ioctl_GWINSZ(fd):
+        try:
+            cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
+        except Exception, exc:
+            print exc
+            return
+        return cr
+    cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
+    if not cr:
+        try:
+            fd = os.open(os.ctermid(), os.O_RDONLY)
+            cr = ioctl_GWINSZ(fd)
+            os.close(fd)
+        except:
+            pass
+    if not cr:
+        cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80))
+    return int(cr[1]), int(cr[0])
+
+
+COLUMNS, ROWS = terminal_size()
+
+def sigwinch_handler(_n, _frame):
+    global ROWS, COLUMNS
+    (COLUMNS, ROWS) = terminal_size()
+
+signal.signal(signal.SIGWINCH, sigwinch_handler)
+
+
+class KeyInput(object):
+
+    instance = None
+    __slots__ = ("_activated", "_stdin_fd", "_terminal_attributes",
+                 "_stdin_flags", "_new_terminal_attributes")
+
+
+    def __init__(self):
+        if KeyInput.instance is not None:
+            raise RuntimeError("Only one instance of %s is allowed" % self.__class__.__name__)
+        KeyInput.instance = self
+        self._stdin_fd = sys.stdin.fileno()
+        self._terminal_attributes = termios.tcgetattr(self._stdin_fd)
+        self._stdin_flags = fcntl.fcntl(self._stdin_fd, fcntl.F_GETFL)
+        self._new_terminal_attributes = termios.tcgetattr(self._stdin_fd)
+        self._new_terminal_attributes[3] = self._new_terminal_attributes[3] & ~termios.ICANON & ~termios.ECHO
+        self._activated = False
+        self.activate()
+        atexit.register(self.deactivate) # XXX: also called on exception?
+
+    def activate(self):
+        if not self._activated:
+            termios.tcsetattr(self._stdin_fd, termios.TCSANOW, self._new_terminal_attributes)
+            fcntl.fcntl(self._stdin_fd, fcntl.F_SETFL, self._stdin_flags | os.O_NONBLOCK)
+            self._activated = True
+
+    def deactivate(self):
+        if self._activated:
+            termios.tcsetattr(self._stdin_fd, termios.TCSAFLUSH, self._terminal_attributes)
+            fcntl.fcntl(self._stdin_fd, fcntl.F_SETFL, self._stdin_flags)
+            self._activated = False
+
+    def __enter__(self):
+        self.activate()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.deactivate()
+
+    def poll(self):
+        try:
+            return sys.stdin.read()  # sys.stdin.read(1)
+        except IOError:
+            pass
+
+    __call__ = poll
+
+    def wait(self):
+        with self:
+            chars = self.poll()
+            while not chars:
+                chars = self.poll()
+            return chars
+
+readkeys = KeyInput()
+
+
+
+if __name__ == "__main__":
+    clear_screen()
+    hide_cursor()
+    color("bright-white", "black")
+    box(0, 0, COLUMNS, ROWS)
+    if 1:
+        for y, fg in enumerate(ESC_BACKGROUND_COLORS.keys()):
+            for x, bg in enumerate(ESC_FOREGROUND_COLORS.keys()):
+                text(1+x * 5, 1+y, fg, bg, fg.replace("bright-", "")[:4])
+    gotoxy(1, 1)
+    color("red", "black")
+    #print "done",
+    wait = readkeys.wait()

tgui/attic/cursesengine.py

+# -*_ coding: utf-8 -*-
+# http://gnosis.cx/publish/programming/charming_python_6.html
+import sys
+import curses
+import _curses
+import fcntl
+import termios
+import struct
+
+from tgui.keymap import keymap
+from tgui.palette import TColorScheme
+
+
+def debug(*args):
+    s = " ".join(str(a) for a in args)
+    open("/tmp/debug.log", "a").write("%s\n" % s)
+
+
+def terminal_size():
+    # Taken from http://stackoverflow.com/a/566752
+    height, width, hp, wp = struct.unpack('HHHH',
+                                          fcntl.ioctl(0, termios.TIOCGWINSZ,
+                                                      struct.pack('HHHH', 0, 0, 0, 0)))
+    return width, height
+
+
+class BasePaintEngine(object):
+
+    def rectangle(self, x1, y1, x2, y2):
+        width = abs(x2 - y1)
+        self.engine.text(x1, y1, "+")                    # top-left
+        self.engine.text(x2, y1, "+")                    # top-right
+        self.engine.text(x1 + 1, y1, "-" * (width - 2))  # top line
+        self.engine.text(x1, y2, "+")                    # bottomleft
+        self.engine.text(x2, y2, "+")                    # bottomright
+        self.engine.text(x1 + 1, y2, "-" * (width - 2))    # bottom line
+        for y in range(y1 + 1, y2):                        # side lines
+            self.engine.text(x1, y, "|")  # (left)
+            self.engine.text(x2, y, "|")  # (right)
+
+
+class TInputEngine(object):
+    instance = None
+
+    def __init__(self):
+        if TInputEngine.instance:
+            raise RuntimeError("Only one TInputEngine instance allowed")
+        TInputEngine.instance = self
+        if not TPaintEngine.instance:
+            raise RuntimeError(
+                "A TPaintEngine instance is requird by TnputEngine")
+        self.screen = TPaintEngine.instance.screen
+        self._mouse_tracking_enabled = False
+
+    def getchar(self, wait=False):
+        KEY_RESIZE = 410  # curses.KEY_RESIZE (sometimes not defined)
+        KEY_MOUSE = 409   # curses.KEY_MOUSE
+        curses.doupdate() # woraround to get screen resize
+        if not wait:
+            self.screen.nodelay(1)
+            while 1:
+                try:
+                    curses.cbreak()
+                    break
+                except _curses.error:
+                    pass
+        return self.screen.getch()
+
+    def readkeys(self):
+        l = []
+        while True:
+            key = self.getchar()
+            if key == -1:
+                break
+            l.append(chr(key))
+        return "".join(l)
+
+    def xreadkeys(self):
+        umlauts = {
+            '\xc3\xa4': u'ä',
+            '\xc3\x84': u'Ä',
+            '\xc3\xb6': u'ö',
+            '\xc3\x96': u'Ö',
+            '\xc3\xbc': u'ü',
+            '\xc3\x9c': u'Ü',
+            '\xc3\x9f': u'ß',
+        }
+        # TODO: complete the list
+        keys = self.readkeys()
+        if keys in umlauts:
+            return False, umlauts[keys]
+        if keys == "\x7f":
+            return True, "backspace"
+        if keys.startswith(chr(27)):
+            xkeys = keymap.get(keys[1:], None)
+            if xkeys:
+                return True, xkeys
+            elif len(keys) == 2:
+                return True, "meta %s" % keys[1]
+        elif len(keys) == 1:
+            code = ord(keys[0])
+            if code < 32 and code not in (9, 10):
+                return True, "ctrl %s" % chr(ord('A') + code - 1) + str(code)
+        return False, keys
+
+
+    def set_mouse_tracking(self, flag=True):
+        flag = bool(flag)
+        if flag == self._mouse_tracking_enabled:
+            return
+        if flag:
+            curses.mousemask(0
+                | curses.BUTTON1_PRESSED | curses.BUTTON1_RELEASED
+                | curses.BUTTON2_PRESSED | curses.BUTTON2_RELEASED
+                | curses.BUTTON3_PRESSED | curses.BUTTON3_RELEASED
+                | curses.BUTTON4_PRESSED | curses.BUTTON4_RELEASED
+                | curses.BUTTON1_DOUBLE_CLICKED | curses.BUTTON1_TRIPLE_CLICKED
+                | curses.BUTTON2_DOUBLE_CLICKED | curses.BUTTON2_TRIPLE_CLICKED
+                | curses.BUTTON3_DOUBLE_CLICKED | curses.BUTTON3_TRIPLE_CLICKED
+                | curses.BUTTON4_DOUBLE_CLICKED | curses.BUTTON4_TRIPLE_CLICKED
+                | curses.BUTTON_SHIFT | curses.BUTTON_ALT
+                | curses.BUTTON_CTRL)
+        else:
+            raise NotImplementedError(self.set_mouse_tracking)
+        self._mouse_tracking_enabled = flag
+
+
+class TPaintEngine(BasePaintEngine):
+    instance = None
+
+    def __init__(self, width=None, height=None):
+        if TPaintEngine.instance:
+            raise RuntimeError("Only one TPaintEngine instance allowed")
+        TPaintEngine.instance = self
+        sys.excepthook = self.excepthook
+        self._x = 0
+        self._y = 0
+        self.cursor_state = True
+        self.window = curses.initscr()
+        # if curses.has_colors():
+        curses.start_color()
+        curses.noecho()
+        # curses.cbreak()
+        curses.raw()  # intercept everything
+        # In keypad mode, escape sequences for special keys
+        # (like the cursor keys) will be interpreted and
+        # a special value like curses.KEY_LEFT will be returned
+        self.window.keypad(1)
+
+        term_width, term_height = terminal_size()
+        self.width = width or term_width
+        self.height = height or term_height
+        self.screen = self.window.subwin(self.height, self.width, 0, 0)
+        # self.screen.box()
+        #self.screen.hline(2, 1, curses.ACS_HLINE, 77)
+        self.screen.refresh()
+
+    def excepthook(self, *args):
+        self.close()
+        sys.__excepthook__(*args)
+        sys.exit(1)
+
+    def __del__(self):
+        self.close()
+
+    def close(self):
+        if self.window:
+            self.window.keypad(0)
+        curses.echo()
+        # curses.nocbreak()
+        curses.endwin()
+        TPaintEngine.instance = None
+
+    def refresh(self):
+        self.screen.refresh()
+
+    def text(self, column, row, value, style=curses.A_NORMAL):
+        if isinstance(style, TColorScheme):
+            style = style.color_pair()
+        if isinstance(value, unicode):
+            value = value.encode("utf-8")
+        elif isinstance(value, int):
+            self.screen.addch(row, column, value, style)
+            return
+        else:
+            if not isinstance(value, str):
+                value = str(value)
+        self.screen.addstr(row, column, value, style)
+        #self.window.move(self._y, self._x)
+
+    def clear(self):  # XXX: better name?
+        self.window.erase()
+
+    def gotoxy(self, x, y):
+        self._x = x
+        self._y = y
+        #self.window.move(y, x)
+        self.text(x, y, u"")
+
+    def show_cursor(self, block=True):
+        if block:
+            curses.curs_set(2)
+            self.cursor_state = 2
+        else:
+            curses.curs_set(1)
+            self.cursor_state = 1
+
+    def hide_cursor(self):
+        curses.curs_set(0)
+        self.cursor_state = 0
+
+    def currentxy(self):
+        (y, x) = self.window.getyx()
+        return (x, y)
+
+    def rectangle(self, ulx, uly, lrx, lry, style=curses.A_NORMAL):
+        if isinstance(style, TColorScheme):
+            style = style.color_pair()
+        # taken from curses.textpad
+        self.window.vline(uly + 1, ulx, curses.ACS_VLINE, lry - uly - 1, style)
+        self.window.hline(uly, ulx + 1, curses.ACS_HLINE, lrx - ulx - 1, style)
+        self.window.hline(lry, ulx + 1, curses.ACS_HLINE, lrx - ulx - 1, style)
+        self.window.vline(uly + 1, lrx, curses.ACS_VLINE, lry - uly - 1, style)
+        self.window.addch(uly, ulx, curses.ACS_ULCORNER, style)
+        self.window.addch(uly, lrx, curses.ACS_URCORNER, style)
+        self.window.addch(lry, lrx, curses.ACS_LRCORNER, style)
+        self.window.addch(lry, ulx, curses.ACS_LLCORNER, style)
+
+    def palette(self):
+        from tgui import TPalette
+        return TPalette()
+
+    def display_text(self, text, width):
+        return text
+        from tgui import elide
+        if len(text) > width:
+            text = elide(text, width)
+        else:
+            text = text.ljust(width)
+        return text
+
+    def draw_window(self, x, y, width, height, pal=None):
+        from tgui import TPalette
+        if pal is None:
+            pal = TPalette()
+        self.rectangle(x, y, x + width, self.y + height, pal.window())
+
+    def draw_button(self, x, y, width, height, text,
+                    current=True, enabled=True, pal=None):
+        assert height == 1
+        pal = pal or self.palette()
+        text_width = width - 4
+        color = (pal or self.palette()).button()
+        self.text(x, y, u"< %s >" % self.display_text(text, text_width), color)
+
+    def draw_radiobutton(self, x, y, width, height, text, checked=True,
+                         current=True, enabled=True, pal=None):
+        assert height == 1
+        pal = pal or self.palette()
+        text_width = width - 4
+        color = (pal or self.palette()).text()
+        self.text(x, y, u"(%s) %s" % (checked and "*" or " ",
+                                     self.display_text(text, text_width)), color)
+
+    def draw_checkbox(self, x, y, width, height, text, checked=True,
+                         current=True, enabled=True, pal=None):
+        assert height == 1, "checkbox height is %s" % height
+        pal = pal or self.palette()
+        text_width = width - 4
+        color = (pal or self.palette()).text()
+        self.text(x, y, u"[%s] %s" % (checked and "x" or " ",
+                                     self.display_text(text, text_width)), color)
+
+    def draw_label(self, x, y, width, height, text, pal=None):
+        assert height == 1
+        pal = pal or self.palette()
+        color = pal.text()
+        self.text(x, y, self.display_text(text, width), color)
+
+    def fill_rect(self, x1, y1, width, height, char, color):
+        y2 = y1 + height - 1
+        x2 = x1 + width - 1
+        for y in range(y1, y2):
+            for x in range(x1, x2):
+                self.text(x, y, char, color)
+
+    def draw_desktop(self, x, y, width, height, pal=None):
+        pal = pal or self.palette()
+        # http://www.melvilletheatre.com/articles/ncurses-extended-characters/index.html
+        self.fill_rect(x, y, width, height, _curses.ACS_CKBOARD, pal.desktop())
+
+
+if __name__ == '__main__':
+    paint = TPaintEngine()
+    inp = TInputEngine()
+    paint.text(1, 1, "hello, world!")
+    i = 0
+    while True:
+        extended, keys = inp.xreadkeys()
+        if keys or extended:
+            i += 1
+            paint.text(1, i, "%r  %s   " % (keys, extended))
+    paint.close()

tgui/attic/generate_keymap.py

+# -*- coding: utf-8 -*-
+
+
+def escape_modifier(digit):
+    mode = ord(digit) - ord("1")
+    return "shift " * (mode & 1) + "meta " * ((mode & 2) / 2) + "ctrl " * ((mode & 4) / 4)
+
+
+keys = [('[A', 'up'), ('[B', 'down'), ('[C', 'right'), ('[D', 'left'),
+        ('[E', '5'), ('[F', 'end'), ('[G', '5'), ('[H', 'home'),
+        ('[1~', 'home'), ('[2~', 'insert'), ('[3~', 'delete'), ('[4~', 'end'),
+        ('[5~', 'page up'), ('[6~', 'page down'),
+        ('[7~', 'home'), ('[8~', 'end'),
+
+        ('[[A', 'f1'), ('[[B', 'f2'), (
+            '[[C', 'f3'), ('[[D', 'f4'), ('[[E', 'f5'),
+
+        ('[11~', 'f1'), ('[12~', 'f2'), ('[13~', 'f3'), ('[14~', 'f4'),
+        ('[15~', 'f5'), ('[17~', 'f6'), ('[18~', 'f7'), ('[19~', 'f8'),
+        ('[20~', 'f9'), ('[21~', 'f10'), ('[23~', 'f11'), ('[24~', 'f12'),
+        ('[25~', 'f13'), ('[26~', 'f14'), ('[28~', 'f15'), ('[29~', 'f16'),
+        ('[31~', 'f17'), ('[32~', 'f18'), ('[33~', 'f19'), ('[34~', 'f20'),
+
+        ('OA', 'up'), ('OB', 'down'), ('OC', 'right'), ('OD', 'left'),
+        ('OH', 'home'), ('OF', 'end'),
+        ('OP', 'f1'), ('OQ', 'f2'), ('OR', 'f3'), ('OS', 'f4'),
+        ('Oo', '/'), ('Oj', '*'), ('Om', '-'), ('Ok', '+'),
+
+        ('[Z', 'shift tab')
+        ] + [
+    #('%s' % chr(i) , 'ctrl %s' % chr(ord('A') + i)) for i in range(27)
+] + [
+    # modified cursor keys + home, end, 5 -- [#X and [1;#X forms
+    (prefix + digit + letter, escape_modifier(digit) + key)
+    for prefix in "[", "[1;"
+    for digit in "12345678"
+    for letter, key in zip("ABCDEFGH",
+                           ('up', 'down', 'right', 'left', '5', 'end', '5', 'home'))
+] + [
+    # modified F1-F4 keys -- O#X form
+    ("O" + digit + letter, escape_modifier(digit) + key)
+    for digit in "12345678"
+    for letter, key in zip("PQRS", ('f1', 'f2', 'f3', 'f4'))
+] + [
+    # modified F1-F13 keys -- [XX;#~ form
+    ("[" + str(num) + ";" + digit + "~", escape_modifier(digit) + key)
+    for digit in "12345678"
+    for num, key in zip(
+        (11, 12, 13, 14, 15, 17, 18, 19, 20, 21,
+         23, 24, 25, 26, 28, 29, 31, 32, 33, 34),
+        ('f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11',
+         'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f20'))
+]
+
+
+if __name__ == "__main__":
+    f = open("keymap.py", "w")
+    print >>f, "# -*- coding: utf-8 -*-"
+    print >>f, "keymap = {"
+    for chars, name in keys:
+        print >>f, "    %r: %r," % (chars, name)
+    print >>f, "}"
+    f.close()

tgui/attic/keymap.py

+# -*- coding: utf-8 -*-
+keymap = {
+    '[A': 'up',
+    '[B': 'down',
+    '[C': 'right',
+    '[D': 'left',
+    '[E': '5',
+    '[F': 'end',
+    '[G': '5',
+    '[H': 'home',
+    '[1~': 'home',
+    '[2~': 'insert',
+    '[3~': 'delete',
+    '[4~': 'end',
+    '[5~': 'page up',
+    '[6~': 'page down',
+    '[7~': 'home',
+    '[8~': 'end',
+    '[[A': 'f1',
+    '[[B': 'f2',
+    '[[C': 'f3',
+    '[[D': 'f4',
+    '[[E': 'f5',
+    '[11~': 'f1',
+    '[12~': 'f2',
+    '[13~': 'f3',
+    '[14~': 'f4',
+    '[15~': 'f5',
+    '[17~': 'f6',
+    '[18~': 'f7',
+    '[19~': 'f8',
+    '[20~': 'f9',
+    '[21~': 'f10',
+    '[23~': 'f11',
+    '[24~': 'f12',
+    '[25~': 'f13',
+    '[26~': 'f14',
+    '[28~': 'f15',
+    '[29~': 'f16',
+    '[31~': 'f17',
+    '[32~': 'f18',
+    '[33~': 'f19',
+    '[34~': 'f20',
+    'OA': 'up',
+    'OB': 'down',
+    'OC': 'right',
+    'OD': 'left',
+    'OH': 'home',
+    'OF': 'end',
+    'OP': 'f1',
+    'OQ': 'f2',
+    'OR': 'f3',
+    'OS': 'f4',
+    'Oo': '/',
+    'Oj': '*',
+    'Om': '-',
+    'Ok': '+',
+    '[Z': 'shift tab',
+    '[1A': 'up',
+    '[1B': 'down',
+    '[1C': 'right',
+    '[1D': 'left',
+    '[1E': '5',
+    '[1F': 'end',
+    '[1G': '5',
+    '[1H': 'home',
+    '[2A': 'shift up',
+    '[2B': 'shift down',
+    '[2C': 'shift right',
+    '[2D': 'shift left',
+    '[2E': 'shift 5',
+    '[2F': 'shift end',
+    '[2G': 'shift 5',
+    '[2H': 'shift home',
+    '[3A': 'meta up',
+    '[3B': 'meta down',
+    '[3C': 'meta right',
+    '[3D': 'meta left',
+    '[3E': 'meta 5',
+    '[3F': 'meta end',
+    '[3G': 'meta 5',
+    '[3H': 'meta home',
+    '[4A': 'shift meta up',
+    '[4B': 'shift meta down',
+    '[4C': 'shift meta right',
+    '[4D': 'shift meta left',
+    '[4E': 'shift meta 5',
+    '[4F': 'shift meta end',
+    '[4G': 'shift meta 5',
+    '[4H': 'shift meta home',
+    '[5A': 'ctrl up',
+    '[5B': 'ctrl down',
+    '[5C': 'ctrl right',
+    '[5D': 'ctrl left',
+    '[5E': 'ctrl 5',
+    '[5F': 'ctrl end',
+    '[5G': 'ctrl 5',
+    '[5H': 'ctrl home',
+    '[6A': 'shift ctrl up',
+    '[6B': 'shift ctrl down',
+    '[6C': 'shift ctrl right',
+    '[6D': 'shift ctrl left',
+    '[6E': 'shift ctrl 5',
+    '[6F': 'shift ctrl end',
+    '[6G': 'shift ctrl 5',
+    '[6H': 'shift ctrl home',
+    '[7A': 'meta ctrl up',
+    '[7B': 'meta ctrl down',
+    '[7C': 'meta ctrl right',
+    '[7D': 'meta ctrl left',
+    '[7E': 'meta ctrl 5',
+    '[7F': 'meta ctrl end',
+    '[7G': 'meta ctrl 5',
+    '[7H': 'meta ctrl home',
+    '[8A': 'shift meta ctrl up',
+    '[8B': 'shift meta ctrl down',
+    '[8C': 'shift meta ctrl right',
+    '[8D': 'shift meta ctrl left',
+    '[8E': 'shift meta ctrl 5',
+    '[8F': 'shift meta ctrl end',
+    '[8G': 'shift meta ctrl 5',
+    '[8H': 'shift meta ctrl home',
+    '[1;1A': 'up',
+    '[1;1B': 'down',
+    '[1;1C': 'right',
+    '[1;1D': 'left',
+    '[1;1E': '5',
+    '[1;1F': 'end',
+    '[1;1G': '5',
+    '[1;1H': 'home',
+    '[1;2A': 'shift up',
+    '[1;2B': 'shift down',
+    '[1;2C': 'shift right',
+    '[1;2D': 'shift left',
+    '[1;2E': 'shift 5',
+    '[1;2F': 'shift end',
+    '[1;2G': 'shift 5',
+    '[1;2H': 'shift home',
+    '[1;3A': 'meta up',
+    '[1;3B': 'meta down',
+    '[1;3C': 'meta right',
+    '[1;3D': 'meta left',
+    '[1;3E': 'meta 5',
+    '[1;3F': 'meta end',
+    '[1;3G': 'meta 5',
+    '[1;3H': 'meta home',
+    '[1;4A': 'shift meta up',
+    '[1;4B': 'shift meta down',
+    '[1;4C': 'shift meta right',
+    '[1;4D': 'shift meta left',
+    '[1;4E': 'shift meta 5',
+    '[1;4F': 'shift meta end',
+    '[1;4G': 'shift meta 5',
+    '[1;4H': 'shift meta home',
+    '[1;5A': 'ctrl up',
+    '[1;5B': 'ctrl down',
+    '[1;5C': 'ctrl right',
+    '[1;5D': 'ctrl left',
+    '[1;5E': 'ctrl 5',
+    '[1;5F': 'ctrl end',
+    '[1;5G': 'ctrl 5',
+    '[1;5H': 'ctrl home',
+    '[1;6A': 'shift ctrl up',
+    '[1;6B': 'shift ctrl down',
+    '[1;6C': 'shift ctrl right',
+    '[1;6D': 'shift ctrl left',
+    '[1;6E': 'shift ctrl 5',
+    '[1;6F': 'shift ctrl end',
+    '[1;6G': 'shift ctrl 5',
+    '[1;6H': 'shift ctrl home',
+    '[1;7A': 'meta ctrl up',
+    '[1;7B': 'meta ctrl down',
+    '[1;7C': 'meta ctrl right',
+    '[1;7D': 'meta ctrl left',
+    '[1;7E': 'meta ctrl 5',
+    '[1;7F': 'meta ctrl end',
+    '[1;7G': 'meta ctrl 5',
+    '[1;7H': 'meta ctrl home',
+    '[1;8A': 'shift meta ctrl up',
+    '[1;8B': 'shift meta ctrl down',
+    '[1;8C': 'shift meta ctrl right',
+    '[1;8D': 'shift meta ctrl left',
+    '[1;8E': 'shift meta ctrl 5',
+    '[1;8F': 'shift meta ctrl end',
+    '[1;8G': 'shift meta ctrl 5',
+    '[1;8H': 'shift meta ctrl home',
+    'O1P': 'f1',
+    'O1Q': 'f2',
+    'O1R': 'f3',
+    'O1S': 'f4',
+    'O2P': 'shift f1',
+    'O2Q': 'shift f2',
+    'O2R': 'shift f3',
+    'O2S': 'shift f4',
+    'O3P': 'meta f1',
+    'O3Q': 'meta f2',
+    'O3R': 'meta f3',
+    'O3S': 'meta f4',
+    'O4P': 'shift meta f1',
+    'O4Q': 'shift meta f2',
+    'O4R': 'shift meta f3',
+    'O4S': 'shift meta f4',
+    'O5P': 'ctrl f1',
+    'O5Q': 'ctrl f2',
+    'O5R': 'ctrl f3',
+    'O5S': 'ctrl f4',
+    'O6P': 'shift ctrl f1',
+    'O6Q': 'shift ctrl f2',
+    'O6R': 'shift ctrl f3',
+    'O6S': 'shift ctrl f4',
+    'O7P': 'meta ctrl f1',
+    'O7Q': 'meta ctrl f2',
+    'O7R': 'meta ctrl f3',
+    'O7S': 'meta ctrl f4',
+    'O8P': 'shift meta ctrl f1',
+    'O8Q': 'shift meta ctrl f2',
+    'O8R': 'shift meta ctrl f3',
+    'O8S': 'shift meta ctrl f4',
+    '[11;1~': 'f1',
+    '[12;1~': 'f2',
+    '[13;1~': 'f3',
+    '[14;1~': 'f4',
+    '[15;1~': 'f5',
+    '[17;1~': 'f6',
+    '[18;1~': 'f7',
+    '[19;1~': 'f8',
+    '[20;1~': 'f9',
+    '[21;1~': 'f10',
+    '[23;1~': 'f11',
+    '[24;1~': 'f12',
+    '[25;1~': 'f13',
+    '[26;1~': 'f14',
+    '[28;1~': 'f15',
+    '[29;1~': 'f16',
+    '[31;1~': 'f17',
+    '[32;1~': 'f18',
+    '[33;1~': 'f19',
+    '[34;1~': 'f20',
+    '[11;2~': 'shift f1',
+    '[12;2~': 'shift f2',
+    '[13;2~': 'shift f3',
+    '[14;2~': 'shift f4',
+    '[15;2~': 'shift f5',
+    '[17;2~': 'shift f6',
+    '[18;2~': 'shift f7',
+    '[19;2~': 'shift f8',
+    '[20;2~': 'shift f9',
+    '[21;2~': 'shift f10',
+    '[23;2~': 'shift f11',
+    '[24;2~': 'shift f12',
+    '[25;2~': 'shift f13',
+    '[26;2~': 'shift f14',
+    '[28;2~': 'shift f15',
+    '[29;2~': 'shift f16',
+    '[31;2~': 'shift f17',
+    '[32;2~': 'shift f18',
+    '[33;2~': 'shift f19',
+    '[34;2~': 'shift f20',
+    '[11;3~': 'meta f1',
+    '[12;3~': 'meta f2',
+    '[13;3~': 'meta f3',
+    '[14;3~': 'meta f4',
+    '[15;3~': 'meta f5',
+    '[17;3~': 'meta f6',
+    '[18;3~': 'meta f7',
+    '[19;3~': 'meta f8',
+    '[20;3~': 'meta f9',
+    '[21;3~': 'meta f10',
+    '[23;3~': 'meta f11',
+    '[24;3~': 'meta f12',
+    '[25;3~': 'meta f13',
+    '[26;3~': 'meta f14',
+    '[28;3~': 'meta f15',
+    '[29;3~': 'meta f16',
+    '[31;3~': 'meta f17',
+    '[32;3~': 'meta f18',
+    '[33;3~': 'meta f19',
+    '[34;3~': 'meta f20',
+    '[11;4~': 'shift meta f1',
+    '[12;4~': 'shift meta f2',
+    '[13;4~': 'shift meta f3',
+    '[14;4~': 'shift meta f4',
+    '[15;4~': 'shift meta f5',
+    '[17;4~': 'shift meta f6',
+    '[18;4~': 'shift meta f7',
+    '[19;4~': 'shift meta f8',
+    '[20;4~': 'shift meta f9',
+    '[21;4~': 'shift meta f10',
+    '[23;4~': 'shift meta f11',
+    '[24;4~': 'shift meta f12',
+    '[25;4~': 'shift meta f13',
+    '[26;4~': 'shift meta f14',
+    '[28;4~': 'shift meta f15',
+    '[29;4~': 'shift meta f16',
+    '[31;4~': 'shift meta f17',
+    '[32;4~': 'shift meta f18',
+    '[33;4~': 'shift meta f19',
+    '[34;4~': 'shift meta f20',
+    '[11;5~': 'ctrl f1',
+    '[12;5~': 'ctrl f2',
+    '[13;5~': 'ctrl f3',
+    '[14;5~': 'ctrl f4',
+    '[15;5~': 'ctrl f5',
+    '[17;5~': 'ctrl f6',
+    '[18;5~': 'ctrl f7',
+    '[19;5~': 'ctrl f8',
+    '[20;5~': 'ctrl f9',
+    '[21;5~': 'ctrl f10',
+    '[23;5~': 'ctrl f11',
+    '[24;5~': 'ctrl f12',
+    '[25;5~': 'ctrl f13',
+    '[26;5~': 'ctrl f14',
+    '[28;5~': 'ctrl f15',
+    '[29;5~': 'ctrl f16',
+    '[31;5~': 'ctrl f17',
+    '[32;5~': 'ctrl f18',
+    '[33;5~': 'ctrl f19',
+    '[34;5~': 'ctrl f20',
+    '[11;6~': 'shift ctrl f1',
+    '[12;6~': 'shift ctrl f2',
+    '[13;6~': 'shift ctrl f3',
+    '[14;6~': 'shift ctrl f4',
+    '[15;6~': 'shift ctrl f5',
+    '[17;6~': 'shift ctrl f6',
+    '[18;6~': 'shift ctrl f7',
+    '[19;6~': 'shift ctrl f8',
+    '[20;6~': 'shift ctrl f9',
+    '[21;6~': 'shift ctrl f10',
+    '[23;6~': 'shift ctrl f11',
+    '[24;6~': 'shift ctrl f12',
+    '[25;6~': 'shift ctrl f13',
+    '[26;6~': 'shift ctrl f14',
+    '[28;6~': 'shift ctrl f15',
+    '[29;6~': 'shift ctrl f16',
+    '[31;6~': 'shift ctrl f17',
+    '[32;6~': 'shift ctrl f18',
+    '[33;6~': 'shift ctrl f19',
+    '[34;6~': 'shift ctrl f20',
+    '[11;7~': 'meta ctrl f1',
+    '[12;7~': 'meta ctrl f2',
+    '[13;7~': 'meta ctrl f3',
+    '[14;7~': 'meta ctrl f4',
+    '[15;7~': 'meta ctrl f5',
+    '[17;7~': 'meta ctrl f6',
+    '[18;7~': 'meta ctrl f7',
+    '[19;7~': 'meta ctrl f8',
+    '[20;7~': 'meta ctrl f9',
+    '[21;7~': 'meta ctrl f10',
+    '[23;7~': 'meta ctrl f11',
+    '[24;7~': 'meta ctrl f12',
+    '[25;7~': 'meta ctrl f13',
+    '[26;7~': 'meta ctrl f14',
+    '[28;7~': 'meta ctrl f15',
+    '[29;7~': 'meta ctrl f16',
+    '[31;7~': 'meta ctrl f17',
+    '[32;7~': 'meta ctrl f18',
+    '[33;7~': 'meta ctrl f19',
+    '[34;7~': 'meta ctrl f20',
+    '[11;8~': 'shift meta ctrl f1',
+    '[12;8~': 'shift meta ctrl f2',
+    '[13;8~': 'shift meta ctrl f3',
+    '[14;8~': 'shift meta ctrl f4',
+    '[15;8~': 'shift meta ctrl f5',
+    '[17;8~': 'shift meta ctrl f6',
+    '[18;8~': 'shift meta ctrl f7',
+    '[19;8~': 'shift meta ctrl f8',
+    '[20;8~': 'shift meta ctrl f9',
+    '[21;8~': 'shift meta ctrl f10',
+    '[23;8~': 'shift meta ctrl f11',
+    '[24;8~': 'shift meta ctrl f12',
+    '[25;8~': 'shift meta ctrl f13',
+    '[26;8~': 'shift meta ctrl f14',
+    '[28;8~': 'shift meta ctrl f15',
+    '[29;8~': 'shift meta ctrl f16',
+    '[31;8~': 'shift meta ctrl f17',
+    '[32;8~': 'shift meta ctrl f18',
+    '[33;8~': 'shift meta ctrl f19',
+    '[34;8~': 'shift meta ctrl f20',
+}

tgui/create_keymap.py

+from tgui.ansiconsole import readkeys
+
+try:
+    from keymap import key_map
+except ImportError:
+    key_map = {}
+    import string
+    for char in string.letters + string.digits:
+        key_map[char] = char
+
+
+def main():
+    import pprint
+    pprint.pprint(key_map)
+    while 1:
+        print "Please press key combination:"
+        chars = readkeys.wait()
+        if chars in key_map:
+            print "key name for %r is %s" % (chars, key_map[chars])
+        else:
+            readkeys.deactivate()
+            name = raw_input("key name for %r (empty to quit):" % chars)
+            if not name:
+                break
+            key_map[name] = chars
+            key_map_src = "key_map = %s" % pprint.pformat(key_map)
+            open("keymap.py", "w").write(key_map_src)
+
+
+if __name__ == "__main__":
+    main()

tgui/cursesengine.py

-# -*_ coding: utf-8 -*-
-# http://gnosis.cx/publish/programming/charming_python_6.html
-import sys
-import curses
-import _curses
-import fcntl
-import termios
-import struct
-
-from tgui.keymap import keymap
-from tgui.palette import TColorScheme
-
-
-def debug(*args):
-    s = " ".join(str(a) for a in args)
-    open("/tmp/debug.log", "a").write("%s\n" % s)
-
-
-def terminal_size():
-    # Taken from http://stackoverflow.com/a/566752
-    height, width, hp, wp = struct.unpack('HHHH',
-                                          fcntl.ioctl(0, termios.TIOCGWINSZ,
-                                                      struct.pack('HHHH', 0, 0, 0, 0)))
-    return width, height
-
-
-class BasePaintEngine(object):
-
-    def rectangle(self, x1, y1, x2, y2):
-        width = abs(x2 - y1)
-        self.engine.text(x1, y1, "+")                    # top-left
-        self.engine.text(x2, y1, "+")                    # top-right
-        self.engine.text(x1 + 1, y1, "-" * (width - 2))  # top line
-        self.engine.text(x1, y2, "+")                    # bottomleft
-        self.engine.text(x2, y2, "+")                    # bottomright
-        self.engine.text(x1 + 1, y2, "-" * (width - 2))    # bottom line
-        for y in range(y1 + 1, y2):                        # side lines
-            self.engine.text(x1, y, "|")  # (left)
-            self.engine.text(x2, y, "|")  # (right)
-
-
-class TInputEngine(object):
-    instance = None
-
-    def __init__(self):
-        if TInputEngine.instance:
-            raise RuntimeError("Only one TInputEngine instance allowed")
-        TInputEngine.instance = self
-        if not TPaintEngine.instance:
-            raise RuntimeError(
-                "A TPaintEngine instance is requird by TnputEngine")
-        self.screen = TPaintEngine.instance.screen
-        self._mouse_tracking_enabled = False
-
-    def getchar(self, wait=False):
-        KEY_RESIZE = 410  # curses.KEY_RESIZE (sometimes not defined)
-        KEY_MOUSE = 409   # curses.KEY_MOUSE
-        curses.doupdate() # woraround to get screen resize
-        if not wait:
-            self.screen.nodelay(1)
-            while 1:
-                try:
-                    curses.cbreak()
-                    break
-                except _curses.error:
-                    pass
-        return self.screen.getch()
-
-    def readkeys(self):
-        l = []
-        while True:
-            key = self.getchar()
-            if key == -1:
-                break
-            l.append(chr(key))
-        return "".join(l)
-
-    def xreadkeys(self):
-        umlauts = {
-            '\xc3\xa4': u'ä',
-            '\xc3\x84': u'Ä',
-            '\xc3\xb6': u'ö',
-            '\xc3\x96': u'Ö',
-            '\xc3\xbc': u'ü',
-            '\xc3\x9c': u'Ü',
-            '\xc3\x9f': u'ß',
-        }
-        # TODO: complete the list
-        keys = self.readkeys()
-        if keys in umlauts:
-            return False, umlauts[keys]
-        if keys == "\x7f":
-            return True, "backspace"
-        if keys.startswith(chr(27)):
-            xkeys = keymap.get(keys[1:], None)
-            if xkeys:
-                return True, xkeys
-            elif len(keys) == 2:
-                return True, "meta %s" % keys[1]
-        elif len(keys) == 1:
-            code = ord(keys[0])
-            if code < 32 and code not in (9, 10):
-                return True, "ctrl %s" % chr(ord('A') + code - 1) + str(code)
-        return False, keys
-
-
-    def set_mouse_tracking(self, flag=True):
-        flag = bool(flag)
-        if flag == self._mouse_tracking_enabled:
-            return
-        if flag:
-            curses.mousemask(0
-                | curses.BUTTON1_PRESSED | curses.BUTTON1_RELEASED
-                | curses.BUTTON2_PRESSED | curses.BUTTON2_RELEASED
-                | curses.BUTTON3_PRESSED | curses.BUTTON3_RELEASED
-                | curses.BUTTON4_PRESSED | curses.BUTTON4_RELEASED
-                | curses.BUTTON1_DOUBLE_CLICKED | curses.BUTTON1_TRIPLE_CLICKED
-                | curses.BUTTON2_DOUBLE_CLICKED | curses.BUTTON2_TRIPLE_CLICKED
-                | curses.BUTTON3_DOUBLE_CLICKED | curses.BUTTON3_TRIPLE_CLICKED
-                | curses.BUTTON4_DOUBLE_CLICKED | curses.BUTTON4_TRIPLE_CLICKED
-                | curses.BUTTON_SHIFT | curses.BUTTON_ALT
-                | curses.BUTTON_CTRL)
-        else:
-            raise NotImplementedError(self.set_mouse_tracking)
-        self._mouse_tracking_enabled = flag
-
-
-class TPaintEngine(BasePaintEngine):
-    instance = None
-
-    def __init__(self, width=None, height=None):
-        if TPaintEngine.instance:
-            raise RuntimeError("Only one TPaintEngine instance allowed")
-        TPaintEngine.instance = self
-        sys.excepthook = self.excepthook
-        self._x = 0
-        self._y = 0
-        self.cursor_state = True
-        self.window = curses.initscr()
-        # if curses.has_colors():
-        curses.start_color()
-        curses.noecho()
-        # curses.cbreak()
-        curses.raw()  # intercept everything
-        # In keypad mode, escape sequences for special keys
-        # (like the cursor keys) will be interpreted and
-        # a special value like curses.KEY_LEFT will be returned
-        self.window.keypad(1)
-
-        term_width, term_height = terminal_size()
-        self.width = width or term_width
-        self.height = height or term_height
-        self.screen = self.window.subwin(self.height, self.width, 0, 0)
-        # self.screen.box()
-        #self.screen.hline(2, 1, curses.ACS_HLINE, 77)
-        self.screen.refresh()
-
-    def excepthook(self, *args):
-        self.close()
-        sys.__excepthook__(*args)
-        sys.exit(1)
-
-    def __del__(self):
-        self.close()
-
-    def close(self):
-        if self.window:
-            self.window.keypad(0)
-        curses.echo()
-        # curses.nocbreak()
-        curses.endwin()
-        TPaintEngine.instance = None
-
-    def refresh(self):
-        self.screen.refresh()
-
-    def text(self, column, row, value, style=curses.A_NORMAL):
-        if isinstance(style, TColorScheme):
-            style = style.color_pair()
-        if isinstance(value, unicode):
-            value = value.encode("utf-8")
-        elif isinstance(value, int):
-            self.screen.addch(row, column, value, style)
-            return
-        else:
-            if not isinstance(value, str):
-                value = str(value)
-        self.screen.addstr(row, column, value, style)
-        #self.window.move(self._y, self._x)
-
-    def clear(self):  # XXX: better name?
-        self.window.erase()
-
-    def gotoxy(self, x, y):
-        self._x = x
-        self._y = y
-        #self.window.move(y, x)
-        self.text(x, y, u"")
-
-    def show_cursor(self, block=True):
-        if block:
-            curses.curs_set(2)
-            self.cursor_state = 2
-        else:
-            curses.curs_set(1)
-            self.cursor_state = 1
-
-    def hide_cursor(self):
-        curses.curs_set(0)
-        self.cursor_state = 0
-
-    def currentxy(self):
-        (y, x) = self.window.getyx()
-        return (x, y)
-
-    def rectangle(self, ulx, uly, lrx, lry, style=curses.A_NORMAL):
-        if isinstance(style, TColorScheme):
-            style = style.color_pair()
-        # taken from curses.textpad
-        self.window.vline(uly + 1, ulx, curses.ACS_VLINE, lry - uly - 1, style)
-        self.window.hline(uly, ulx + 1, curses.ACS_HLINE, lrx - ulx - 1, style)
-        self.window.hline(lry, ulx + 1, curses.ACS_HLINE, lrx - ulx - 1, style)
-        self.window.vline(uly + 1, lrx, curses.ACS_VLINE, lry - uly - 1, style)
-        self.window.addch(uly, ulx, curses.ACS_ULCORNER, style)
-        self.window.addch(uly, lrx, curses.ACS_URCORNER, style)
-        self.window.addch(lry, lrx, curses.ACS_LRCORNER, style)
-        self.window.addch(lry, ulx, curses.ACS_LLCORNER, style)
-
-    def palette(self):
-        from tgui import TPalette
-        return TPalette()
-
-    def display_text(self, text, width):
-        return text
-        from tgui import elide
-        if len(text) > width:
-            text = elide(text, width)
-        else:
-            text = text.ljust(width)
-        return text
-
-    def draw_window(self, x, y, width, height, pal=None):
-        from tgui import TPalette
-        if pal is None:
-            pal = TPalette()
-        self.rectangle(x, y, x + width, self.y + height, pal.window())
-
-    def draw_button(self, x, y, width, height, text,
-                    current=True, enabled=True, pal=None):
-        assert height == 1
-        pal = pal or self.palette()
-        text_width = width - 4
-        color = (pal or self.palette()).button()
-        self.text(x, y, u"< %s >" % self.display_text(text, text_width), color)
-
-    def draw_radiobutton(self, x, y, width, height, text, checked=True,
-                         current=True, enabled=True, pal=None):
-        assert height == 1
-        pal = pal or self.palette()
-        text_width = width - 4
-        color = (pal or self.palette()).text()
-        self.text(x, y, u"(%s) %s" % (checked and "*" or " ",
-                                     self.display_text(text, text_width)), color)
-
-    def draw_checkbox(self, x, y, width, height, text, checked=True,
-                         current=True, enabled=True, pal=None):
-        assert height == 1, "checkbox height is %s" % height
-        pal = pal or self.palette()
-        text_width = width - 4
-        color = (pal or self.palette()).text()
-        self.text(x, y, u"[%s] %s" % (checked and "x" or " ",
-                                     self.display_text(text, text_width)), color)
-
-    def draw_label(self, x, y, width, height, text, pal=None):
-        assert height == 1
-        pal = pal or self.palette()
-        color = pal.text()
-        self.text(x, y, self.display_text(text, width), color)
-
-    def fill_rect(self, x1, y1, width, height, char, color):
-        y2 = y1 + height - 1
-        x2 = x1 + width - 1
-        for y in range(y1, y2):
-            for x in range(x1, x2):
-                self.text(x, y, char, color)
-
-    def draw_desktop(self, x, y, width, height, pal=None):
-        pal = pal or self.palette()
-        # http://www.melvilletheatre.com/articles/ncurses-extended-characters/index.html
-        self.fill_rect(x, y, width, height, _curses.ACS_CKBOARD, pal.desktop())
-
-
-if __name__ == '__main__':
-    paint = TPaintEngine()
-    inp = TInputEngine()
-    paint.text(1, 1, "hello, world!")
-    i = 0
-    while True:
-        extended, keys = inp.xreadkeys()
-        if keys or extended:
-            i += 1
-            paint.text(1, i, "%r  %s   " % (keys, extended))
-    paint.close()

tgui/generate_keymap.py

-# -*- coding: utf-8 -*-
-
-
-def escape_modifier(digit):
-    mode = ord(digit) - ord("1")
-    return "shift " * (mode & 1) + "meta " * ((mode & 2) / 2) + "ctrl " * ((mode & 4) / 4)
-
-
-keys = [('[A', 'up'), ('[B', 'down'), ('[C', 'right'), ('[D', 'left'),
-        ('[E', '5'), ('[F', 'end'), ('[G', '5'), ('[H', 'home'),
-        ('[1~', 'home'), ('[2~', 'insert'), ('[3~', 'delete'), ('[4~', 'end'),
-        ('[5~', 'page up'), ('[6~', 'page down'),
-        ('[7~', 'home'), ('[8~', 'end'),
-
-        ('[[A', 'f1'), ('[[B', 'f2'), (
-            '[[C', 'f3'), ('[[D', 'f4'), ('[[E', 'f5'),
-
-        ('[11~', 'f1'), ('[12~', 'f2'), ('[13~', 'f3'), ('[14~', 'f4'),
-        ('[15~', 'f5'), ('[17~', 'f6'), ('[18~', 'f7'), ('[19~', 'f8'),
-        ('[20~', 'f9'), ('[21~', 'f10'), ('[23~', 'f11'), ('[24~', 'f12'),
-        ('[25~', 'f13'), ('[26~', 'f14'), ('[28~', 'f15'), ('[29~', 'f16'),
-        ('[31~', 'f17'), ('[32~', 'f18'), ('[33~', 'f19'), ('[34~', 'f20'),
-
-        ('OA', 'up'), ('OB', 'down'), ('OC', 'right'), ('OD', 'left'),
-        ('OH', 'home'), ('OF', 'end'),
-        ('OP', 'f1'), ('OQ', 'f2'), ('OR', 'f3'), ('OS', 'f4'),
-        ('Oo', '/'), ('Oj', '*'), ('Om', '-'), ('Ok', '+'),
-
-        ('[Z', 'shift tab')
-        ] + [
-    #('%s' % chr(i) , 'ctrl %s' % chr(ord('A') + i)) for i in range(27)
-] + [
-    # modified cursor keys + home, end, 5 -- [#X and [1;#X forms
-    (prefix + digit + letter, escape_modifier(digit) + key)
-    for prefix in "[", "[1;"
-    for digit in "12345678"
-    for letter, key in zip("ABCDEFGH",
-                           ('up', 'down', 'right', 'left', '5', 'end', '5', 'home'))
-] + [
-    # modified F1-F4 keys -- O#X form
-    ("O" + digit + letter, escape_modifier(digit) + key)
-    for digit in "12345678"
-    for letter, key in zip("PQRS", ('f1', 'f2', 'f3', 'f4'))
-] + [
-    # modified F1-F13 keys -- [XX;#~ form
-    ("[" + str(num) + ";" + digit + "~", escape_modifier(digit) + key)
-    for digit in "12345678"
-    for num, key in zip(
-        (11, 12, 13, 14, 15, 17, 18, 19, 20, 21,
-         23, 24, 25, 26, 28, 29, 31, 32, 33, 34),
-        ('f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11',
-         'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f20'))
-]
-
-
-if __name__ == "__main__":
-    f = open("keymap.py", "w")
-    print >>f, "# -*- coding: utf-8 -*-"
-    print >>f, "keymap = {"
-    for chars, name in keys:
-        print >>f, "    %r: %r," % (chars, name)
-    print >>f, "}"
-    f.close()
+key_map = {'0': '0',
+ '1': '1',
+ '2': '2',
+ '3': '3',
+ '4': '4',
+ '5': '5',
+ '6': '6',
+ '7': '7',
+ '8': '8',
+ '9': '9',
+ 'A': 'A',
+ 'B': 'B',
+ 'C': 'C',
+ 'C-a': '\x01',
+ 'C-v': '\x16',
+ 'C-w': '\x17',
+ 'C-x': '\x18',
+ 'D': 'D',
+ 'DOWN': '\x1b[B',
+ 'E': 'E',
+ 'ESC': '\x1b',
+ 'Enter': '\n',
+ 'F': 'F',
+ 'F"': '\x1bOQ',
+ 'F1': '\x1bOP',
+ 'F10': '\x1b[21~',
+ 'F2': '\x1bOQ',
+ 'F3': '\x1bOR',
+ 'F4': '\x1bOS',
+ 'F5': '\x1b[15~',
+ 'F6': '\x1b[17~',
+ 'F7': '\x1b[18~',
+ 'F8': '\x1b[19~',
+ 'F9': '\x1b[20~',
+ 'G': 'G',
+ 'H': 'H',
+ 'I': 'I',
+ 'J': 'J',
+ 'K': 'K',
+ 'L': 'L',
+ 'LEFT': '\x1b[D',
+ 'M': 'M',
+ 'N': 'N',
+ 'O': 'O',
+ 'P': 'P',
+ 'Q': 'Q',
+ 'R': 'R',
+ 'RIGHT': '\x1b[C',
+ 'Return': '\n',
+ 'S': 'S',
+ 'T': 'T',
+ 'U': 'U',
+ 'UP': '\x1b[A',
+ 'V': 'V',
+ 'W': 'W',
+ 'X': 'X',
+ 'Y': 'Y',
+ 'Z': 'Z',
+ 'a': 'a',
+ 'b': 'b',
+ 'c': 'c',
+ 'd': 'd',
+ 'e': 'e',
+ 'f': 'f',
+ 'g': 'g',
+ 'h': 'h',
+ 'i': 'i',
+ 'j': 'j',
+ 'k': 'k',
+ 'l': 'l',
+ 'm': 'm',
+ 'n': 'n',
+ 'o': 'o',
+ 'p': 'p',
+ 'q': 'q',
+ 'r': 'r',
+ 's': 's',
+ 't': 't',
+ 'u': 'u',
+ 'v': 'v',
+ 'w': 'w',
+ 'x': 'x',
+ 'y': 'y',
+ 'z': 'z'}

tgui/keymap.py

-# -*- coding: utf-8 -*-
-keymap = {
-    '[A': 'up',
-    '[B': 'down',
-    '[C': 'right',
-    '[D': 'left',
-    '[E': '5',
-    '[F': 'end',
-    '[G': '5',
-    '[H': 'home',
-    '[1~': 'home',
-    '[2~': 'insert',
-    '[3~': 'delete',
-    '[4~': 'end',
-    '[5~': 'page up',
-    '[6~': 'page down',
-    '[7~': 'home',
-    '[8~': 'end',
-    '[[A': 'f1',
-    '[[B': 'f2',
-    '[[C': 'f3',
-    '[[D': 'f4',
-    '[[E': 'f5',
-    '[11~': 'f1',
-    '[12~': 'f2',
-    '[13~': 'f3',
-    '[14~': 'f4',
-    '[15~': 'f5',
-    '[17~': 'f6',
-    '[18~': 'f7',
-    '[19~': 'f8',
-    '[20~': 'f9',
-    '[21~': 'f10',
-    '[23~': 'f11',
-    '[24~': 'f12',
-    '[25~': 'f13',
-    '[26~': 'f14',
-    '[28~': 'f15',
-    '[29~': 'f16',
-    '[31~': 'f17',
-    '[32~': 'f18',
-    '[33~': 'f19',
-    '[34~': 'f20',
-    'OA': 'up',
-    'OB': 'down',
-    'OC': 'right',
-    'OD': 'left',
-    'OH': 'home',
-    'OF': 'end',
-    'OP': 'f1',
-    'OQ': 'f2',
-    'OR': 'f3',
-    'OS': 'f4',
-    'Oo': '/',
-    'Oj': '*',
-    'Om': '-',
-    'Ok': '+',
-    '[Z': 'shift tab',
-    '[1A': 'up',
-    '[1B': 'down',
-    '[1C': 'right',
-    '[1D': 'left',
-    '[1E': '5',
-    '[1F': 'end',
-    '[1G': '5',
-    '[1H': 'home',
-    '[2A': 'shift up',
-    '[2B': 'shift down',
-    '[2C': 'shift right',
-    '[2D': 'shift left',
-    '[2E': 'shift 5',
-    '[2F': 'shift end',
-    '[2G': 'shift 5',
-    '[2H': 'shift home',
-    '[3A': 'meta up',
-    '[3B': 'meta down',
-    '[3C': 'meta right',
-    '[3D': 'meta left',
-    '[3E': 'meta 5',
-    '[3F': 'meta end',
-    '[3G': 'meta 5',
-    '[3H': 'meta home',
-    '[4A': 'shift meta up',
-    '[4B': 'shift meta down',
-    '[4C': 'shift meta right',
-    '[4D': 'shift meta left',
-    '[4E': 'shift meta 5',
-    '[4F': 'shift meta end',
-    '[4G': 'shift meta 5',
-    '[4H': 'shift meta home',
-    '[5A': 'ctrl up',
-    '[5B': 'ctrl down',
-    '[5C': 'ctrl right',
-    '[5D': 'ctrl left',
-    '[5E': 'ctrl 5',
-    '[5F': 'ctrl end',
-    '[5G': 'ctrl 5',
-    '[5H': 'ctrl home',
-    '[6A': 'shift ctrl up',
-    '[6B': 'shift ctrl down',
-    '[6C': 'shift ctrl right',
-    '[6D': 'shift ctrl left',
-    '[6E': 'shift ctrl 5',
-    '[6F': 'shift ctrl end',
-    '[6G': 'shift ctrl 5',
-    '[6H': 'shift ctrl home',
-    '[7A': 'meta ctrl up',
-    '[7B': 'meta ctrl down',
-    '[7C': 'meta ctrl right',
-    '[7D': 'meta ctrl left',
-    '[7E': 'meta ctrl 5',
-    '[7F': 'meta ctrl end',
-    '[7G': 'meta ctrl 5',
-    '[7H': 'meta ctrl home',
-    '[8A': 'shift meta ctrl up',
-    '[8B': 'shift meta ctrl down',
-    '[8C': 'shift meta ctrl right',
-    '[8D': 'shift meta ctrl left',
-    '[8E': 'shift meta ctrl 5',
-    '[8F': 'shift meta ctrl end',
-    '[8G': 'shift meta ctrl 5',
-    '[8H': 'shift meta ctrl home',
-    '[1;1A': 'up',
-    '[1;1B': 'down',
-    '[1;1C': 'right',
-    '[1;1D': 'left',
-    '[1;1E': '5',
-    '[1;1F': 'end',
-    '[1;1G': '5',
-    '[1;1H': 'home',
-    '[1;2A': 'shift up',
-    '[1;2B': 'shift down',
-    '[1;2C': 'shift right',
-    '[1;2D': 'shift left',
-    '[1;2E': 'shift 5',
-    '[1;2F': 'shift end',
-    '[1;2G': 'shift 5',
-    '[1;2H': 'shift home',
-    '[1;3A': 'meta up',
-    '[1;3B': 'meta down',
-    '[1;3C': 'meta right',
-    '[1;3D': 'meta left',
-    '[1;3E': 'meta 5',
-    '[1;3F': 'meta end',
-    '[1;3G': 'meta 5',
-    '[1;3H': 'meta home',
-    '[1;4A': 'shift meta up',
-    '[1;4B': 'shift meta down',
-    '[1;4C': 'shift meta right',
-    '[1;4D': 'shift meta left',
-    '[1;4E': 'shift meta 5',
-    '[1;4F': 'shift meta end',
-    '[1;4G': 'shift meta 5',
-    '[1;4H': 'shift meta home',
-    '[1;5A': 'ctrl up',
-    '[1;5B': 'ctrl down',
-    '[1;5C': 'ctrl right',
-    '[1;5D': 'ctrl left',
-    '[1;5E': 'ctrl 5',
-    '[1;5F': 'ctrl end',
-    '[1;5G': 'ctrl 5',
-    '[1;5H': 'ctrl home',
-    '[1;6A': 'shift ctrl up',
-    '[1;6B': 'shift ctrl down',
-    '[1;6C': 'shift ctrl right',
-    '[1;6D': 'shift ctrl left',
-    '[1;6E': 'shift ctrl 5',
-    '[1;6F': 'shift ctrl end',
-    '[1;6G': 'shift ctrl 5',
-    '[1;6H': 'shift ctrl home',
-    '[1;7A': 'meta ctrl up',
-    '[1;7B': 'meta ctrl down',
-    '[1;7C': 'meta ctrl right',
-    '[1;7D': 'meta ctrl left',
-    '[1;7E': 'meta ctrl 5',
-    '[1;7F': 'meta ctrl end',
-    '[1;7G': 'meta ctrl 5',
-    '[1;7H': 'meta ctrl home',
-    '[1;8A': 'shift meta ctrl up',
-    '[1;8B': 'shift meta ctrl down',
-    '[1;8C': 'shift meta ctrl right',
-    '[1;8D': 'shift meta ctrl left',
-    '[1;8E': 'shift meta ctrl 5',
-    '[1;8F': 'shift meta ctrl end',
-    '[1;8G': 'shift meta ctrl 5',
-    '[1;8H': 'shift meta ctrl home',
-    'O1P': 'f1',
-    'O1Q': 'f2',
-    'O1R': 'f3',
-    'O1S': 'f4',
-    'O2P': 'shift f1',
-    'O2Q': 'shift f2',
-    'O2R': 'shift f3',
-    'O2S': 'shift f4',
-    'O3P': 'meta f1',
-    'O3Q': 'meta f2',
-    'O3R': 'meta f3',
-    'O3S': 'meta f4',
-    'O4P': 'shift meta f1',
-    'O4Q': 'shift meta f2',
-    'O4R': 'shift meta f3',
-    'O4S': 'shift meta f4',
-    'O5P': 'ctrl f1',
-    'O5Q': 'ctrl f2',
-    'O5R': 'ctrl f3',
-    'O5S': 'ctrl f4',
-    'O6P': 'shift ctrl f1',
-    'O6Q': 'shift ctrl f2',
-    'O6R': 'shift ctrl f3',
-    'O6S': 'shift ctrl f4',
-    'O7P': 'meta ctrl f1',
-    'O7Q': 'meta ctrl f2',
-    'O7R': 'meta ctrl f3',
-    'O7S': 'meta ctrl f4',
-    'O8P': 'shift meta ctrl f1',
-    'O8Q': 'shift meta ctrl f2',
-    'O8R': 'shift meta ctrl f3',
-    'O8S': 'shift meta ctrl f4',
-    '[11;1~': 'f1',
-    '[12;1~': 'f2',
-    '[13;1~': 'f3',
-    '[14;1~': 'f4',
-    '[15;1~': 'f5',
-    '[17;1~': 'f6',
-    '[18;1~': 'f7',
-    '[19;1~': 'f8',
-    '[20;1~': 'f9',
-    '[21;1~': 'f10',
-    '[23;1~': 'f11',
-    '[24;1~': 'f12',
-    '[25;1~': 'f13',
-    '[26;1~': 'f14',
-    '[28;1~': 'f15',
-    '[29;1~': 'f16',
-    '[31;1~': 'f17',
-    '[32;1~': 'f18',
-    '[33;1~': 'f19',
-    '[34;1~': 'f20',
-    '[11;2~': 'shift f1',
-    '[12;2~': 'shift f2',
-    '[13;2~': 'shift f3',
-    '[14;2~': 'shift f4',
-    '[15;2~': 'shift f5',
-    '[17;2~': 'shift f6',
-    '[18;2~': 'shift f7',
-    '[19;2~': 'shift f8',
-    '[20;2~': 'shift f9',
-    '[21;2~': 'shift f10',
-    '[23;2~': 'shift f11',
-    '[24;2~': 'shift f12',
-    '[25;2~': 'shift f13',
-    '[26;2~': 'shift f14',
-    '[28;2~': 'shift f15',
-    '[29;2~': 'shift f16',
-    '[31;2~': 'shift f17',
-    '[32;2~': 'shift f18',
-    '[33;2~': 'shift f19',
-    '[34;2~': 'shift f20',
-    '[11;3~': 'meta f1',
-    '[12;3~': 'meta f2',
-    '[13;3~': 'meta f3',
-    '[14;3~': 'meta f4',
-    '[15;3~': 'meta f5',
-    '[17;3~': 'meta f6',
-    '[18;3~': 'meta f7',
-    '[19;3~': 'meta f8',
-    '[20;3~': 'meta f9',
-    '[21;3~': 'meta f10',
-    '[23;3~': 'meta f11',
-    '[24;3~': 'meta f12',
-    '[25;3~': 'meta f13',
-    '[26;3~': 'meta f14',
-    '[28;3~': 'meta f15',
-    '[29;3~': 'meta f16',
-    '[31;3~': 'meta f17',
-    '[32;3~': 'meta f18',
-    '[33;3~': 'meta f19',
-    '[34;3~': 'meta f20',
-    '[11;4~': 'shift meta f1',
-    '[12;4~': 'shift meta f2',
-    '[13;4~': 'shift meta f3',
-    '[14;4~': 'shift meta f4',
-    '[15;4~': 'shift meta f5',
-    '[17;4~': 'shift meta f6',
-    '[18;4~': 'shift meta f7',
-    '[19;4~': 'shift meta f8',
-    '[20;4~': 'shift meta f9',
-    '[21;4~': 'shift meta f10',
-    '[23;4~': 'shift meta f11',
-    '[24;4~': 'shift meta f12',
-    '[25;4~': 'shift meta f13',
-    '[26;4~': 'shift meta f14',
-    '[28;4~': 'shift meta f15',
-    '[29;4~': 'shift meta f16',
-    '[31;4~': 'shift meta f17',
-    '[32;4~': 'shift meta f18',
-    '[33;4~': 'shift meta f19',
-    '[34;4~': 'shift meta f20',
-    '[11;5~': 'ctrl f1',
-    '[12;5~': 'ctrl f2',
-    '[13;5~': 'ctrl f3',
-    '[14;5~': 'ctrl f4',
-    '[15;5~': 'ctrl f5',
-    '[17;5~': 'ctrl f6',
-    '[18;5~': 'ctrl f7',
-    '[19;5~': 'ctrl f8',
-    '[20;5~': 'ctrl f9',
-    '[21;5~': 'ctrl f10',
-    '[23;5~': 'ctrl f11',
-    '[24;5~': 'ctrl f12',
-    '[25;5~': 'ctrl f13',
-    '[26;5~': 'ctrl f14',
-    '[28;5~': 'ctrl f15',
-    '[29;5~': 'ctrl f16',
-    '[31;5~': 'ctrl f17',
-    '[32;5~': 'ctrl f18',
-    '[33;5~': 'ctrl f19',
-    '[34;5~': 'ctrl f20',
-    '[11;6~': 'shift ctrl f1',
-    '[12;6~': 'shift ctrl f2',
-    '[13;6~': 'shift ctrl f3',
-    '[14;6~': 'shift ctrl f4',
-    '[15;6~': 'shift ctrl f5',
-    '[17;6~': 'shift ctrl f6',
-    '[18;6~': 'shift ctrl f7',
-    '[19;6~': 'shift ctrl f8',
-    '[20;6~': 'shift ctrl f9',
-    '[21;6~': 'shift ctrl f10',
-    '[23;6~': 'shift ctrl f11',
-    '[24;6~': 'shift ctrl f12',
-    '[25;6~': 'shift ctrl f13',
-    '[26;6~': 'shift ctrl f14',
-    '[28;6~': 'shift ctrl f15',
-    '[29;6~': 'shift ctrl f16',
-    '[31;6~': 'shift ctrl f17',
-    '[32;6~': 'shift ctrl f18',
-    '[33;6~': 'shift ctrl f19',
-    '[34;6~': 'shift ctrl f20',
-    '[11;7~': 'meta ctrl f1',
-    '[12;7~': 'meta ctrl f2',
-    '[13;7~': 'meta ctrl f3',
-    '[14;7~': 'meta ctrl f4',
-    '[15;7~': 'meta ctrl f5',
-    '[17;7~': 'meta ctrl f6',
-    '[18;7~': 'meta ctrl f7',
-    '[19;7~': 'meta ctrl f8',
-    '[20;7~': 'meta ctrl f9',
-    '[21;7~': 'meta ctrl f10',
-    '[23;7~': 'meta ctrl f11',
-    '[24;7~': 'meta ctrl f12',
-    '[25;7~': 'meta ctrl f13',
-    '[26;7~': 'meta ctrl f14',
-    '[28;7~': 'meta ctrl f15',
-    '[29;7~': 'meta ctrl f16',
-    '[31;7~': 'meta ctrl f17',
-    '[32;7~': 'meta ctrl f18',
-    '[33;7~': 'meta ctrl f19',
-    '[34;7~': 'meta ctrl f20',
-    '[11;8~': 'shift meta ctrl f1',
-    '[12;8~': 'shift meta ctrl f2',
-    '[13;8~': 'shift meta ctrl f3',
-    '[14;8~': 'shift meta ctrl f4',
-    '[15;8~': 'shift meta ctrl f5',
-    '[17;8~': 'shift meta ctrl f6',
-    '[18;8~': 'shift meta ctrl f7',
-    '[19;8~': 'shift meta ctrl f8',
-    '[20;8~': 'shift meta ctrl f9',
-    '[21;8~': 'shift meta ctrl f10',
-    '[23;8~': 'shift meta ctrl f11',
-    '[24;8~': 'shift meta ctrl f12',
-    '[25;8~': 'shift meta ctrl f13',
-    '[26;8~': 'shift meta ctrl f14',
-    '[28;8~': 'shift meta ctrl f15',
-    '[29;8~': 'shift meta ctrl f16',
-    '[31;8~': 'shift meta ctrl f17',
-    '[32;8~': 'shift meta ctrl f18',
-    '[33;8~': 'shift meta ctrl f19',
-    '[34;8~': 'shift meta ctrl f20',
-}

tgui/tapplication.py

         self.input_context = TInputContext()
 
     def close(self):
-        TPaintEngine.instance.close()
+        TPaintEngine.instance.deactivate()
         
     def quit(self, exitcode=0):
         if TPaintEngine.instance:
     try:
         if do_time:
             x.feed(data)
-            x.close()
+            x.deactivate()
         else:
             for c in data:
                 x.feed(c)
-            x.close()
+            x.deactivate()
     except Error, msg:
         t1 = time()
         print msg