Fixed escape sequence handling.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test_input.py Wed Mar 16 15:19:05 2011 +0100
@@ -0,0 +1,33 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+import locale
+locale.setlocale(locale.LC_ALL, '')
+
+from tuikit import *
+
+
+class MyApplication(Application):
+ def __init__(self):
+ Application.__init__(self)
+ self.top.connect('keypress', self.globalkeypress)
+
+ self.text = ''
+ textedit = TextEdit(100, 40, self.text)
+ self.top.add(textedit)
+ textedit.x = 2
+ self.textedit = textedit
+
+
+ def globalkeypress(self, keyname, char):
+ if char == 'q':
+ self.terminate()
+ self.text += 'keyname: %s char: %s\n' % (keyname, char)
+ self.textedit.settext(self.text)
+ self.textedit.scrolltoend()
+
+
+if __name__ == '__main__':
+ app = MyApplication()
+ app.start()
+
--- a/tuikit/backend_curses.py Tue Mar 15 17:27:09 2011 +0100
+++ b/tuikit/backend_curses.py Wed Mar 16 15:19:05 2011 +0100
@@ -255,7 +255,23 @@
## input ##
- def inputqueue_fill(self):
+ def inputqueue_fill(self, timeout=None):
+ if timeout is None:
+ # wait indefinitely
+ c = self.screen.getch()
+ self.inputqueue.insert(0, c)
+
+ elif timeout > 0:
+ # wait
+ curses.halfdelay(timeout)
+ c = self.screen.getch()
+ curses.cbreak()
+ if c == -1:
+ return
+ self.inputqueue.insert(0, c)
+
+ # timeout = 0 -> no wait
+
self.screen.nodelay(1)
while True:
@@ -267,51 +283,63 @@
self.screen.nodelay(0)
- def inputqueue_next(self):
+ def inputqueue_top(self, num=0):
+ return self.inputqueue[-1-num]
+
+
+ def inputqueue_get(self):
+ c = None
+ try:
+ c = self.inputqueue.pop()
+ except IndexError:
+ pass
+ return c
+
+
+ def inputqueue_get_wait(self):
c = None
while c is None:
try:
c = self.inputqueue.pop()
except IndexError:
curses.napms(25)
- self.inputqueue_fill()
+ self.inputqueue_fill(0)
return c
+ def inputqueue_unget(self, c):
+ self.inputqueue.append(c)
+
+
def process_input(self, timeout=None):
- if len(self.inputqueue) > 0:
- c = self.inputqueue_next()
- else:
- if not timeout is None:
- curses.halfdelay(timeout)
- c = self.screen.getch()
- curses.cbreak()
- if c == -1:
- return []
- else:
- c = self.screen.getch()
+ # empty queue -> fill
+ if len(self.inputqueue) == 0:
+ self.inputqueue_fill(timeout)
- if c == curses.KEY_MOUSE:
- return self.process_mouse()
+ res = []
+ while len(self.inputqueue):
+ c = self.inputqueue_get()
+
+ if c == curses.KEY_MOUSE:
+ res += self.process_mouse()
- elif curses.ascii.isctrl(c):
- self.inputqueue.append(c)
- self.inputqueue_fill()
- return self.process_control_chars()
+ elif curses.ascii.isctrl(c):
+ self.inputqueue_unget(c)
+ res += self.process_control_chars()
+
+ elif c >= 192 and c <= 255:
+ self.inputqueue_unget(c)
+ res += self.process_utf8_chars()
- elif c >= 192 and c <= 255:
- self.inputqueue.append(c)
- self.inputqueue_fill()
- return self.process_utf8_chars()
+ elif curses.ascii.isprint(c):
+ res += [('keypress', None, str(chr(c)))]
- elif curses.ascii.isprint(c):
- return [('keypress', None, str(chr(c)))]
+ else:
+ #self.top.keypress(None, unicode(chr(c)))
+ self.inputqueue_unget(c)
+ res += self.process_control_chars()
- else:
- #self.top.keypress(None, unicode(chr(c)))
- self.inputqueue.append(c)
- self.inputqueue_fill()
- return self.process_control_chars()
+ return res
def process_mouse(self):
@@ -346,7 +374,7 @@
#FIXME read exact number of chars as defined by utf-8
utf = ''
while len(utf) <= 6:
- c = self.inputqueue_next()
+ c = self.inputqueue_get_wait()
utf += chr(c)
try:
uni = str(utf, 'utf-8')
@@ -357,25 +385,56 @@
def process_control_chars(self):
- keyname = None
- for code in self.xterm_codes:
- ok = False
- if len(self.inputqueue) >= len(code) - 1:
- ok = True
- for i in range(len(code)-1):
- if self.inputqueue[-i-1] != code[i]:
- ok = False
- break
+ codes = self.xterm_codes
+ matchingcodes = []
+ match = None
+ consumed = []
+
+ # consume next char, filter out matching codes
+ c = self.inputqueue_get_wait()
+ consumed.append(c)
+
+ while True:
+ self.log.debug('c=%s len=%s', c, len(codes))
+ for code in codes:
+ if c == code[len(consumed)-1]:
+ if len(code) - 1 == len(consumed):
+ match = code
+ else:
+ matchingcodes += [code]
+
+ self.log.debug('matching=%s', len(matchingcodes))
+
+ # match found, or no matching code found -> stop
+ if len(matchingcodes) == 0:
+ break
- if ok:
- keyname = code[-1]
- self.inputqueue = self.inputqueue[:-len(code)+1]
+ # match found and some sequencies still match -> continue
+ if len(matchingcodes) > 0:
+ if len(self.inputqueue) == 0:
+ self.inputqueue_fill(1)
+
+ c = self.inputqueue_get()
+ if c:
+ consumed.append(c)
+ codes = matchingcodes
+ matchingcodes = []
+ else:
+ break
- if keyname is None:
+ keyname = None
+ if match:
+ # compare match to consumed, return unused chars
+ l = len(match) - 1
+ while len(consumed) > l:
+ self.inputqueue_unget(consumed[-1])
+ del consumed[-1]
+ keyname = match[-1]
+
+ if match is None:
self.log.debug('Unknown control sequence: %s',
- ','.join(reversed(['0x%x'%x for x in self.inputqueue])))
- c = self.inputqueue_next()
- return [('keypress', 'Unknown%x' % c, None)]
+ ','.join(['0x%x'%x for x in consumed]))
+ return [('keypress', 'Unknown', None)]
if keyname == 'mouse':
return self.process_xterm_mouse()
@@ -384,9 +443,9 @@
def process_xterm_mouse(self):
- t = self.inputqueue_next()
- x = self.inputqueue_next() - 0x21
- y = self.inputqueue_next() - 0x21
+ t = self.inputqueue_get_wait()
+ x = self.inputqueue_get_wait() - 0x21
+ y = self.inputqueue_get_wait() - 0x21
ev = MouseEvent(x, y)
out = []
--- a/tuikit/editbox.py Tue Mar 15 17:27:09 2011 +0100
+++ b/tuikit/editbox.py Wed Mar 16 15:19:05 2011 +0100
@@ -6,8 +6,6 @@
def __init__(self, width=20, height=20, text=''):
Widget.__init__(self, width, height)
- self.set_text(text)
-
self.xofs = 0
self.yofs = 0
@@ -26,6 +24,8 @@
self.newevent('scroll')
self.newevent('areasize')
+ self.set_text(text)
+
def on_draw(self, screen, x, y):
for j in range(self.height):
@@ -98,6 +98,7 @@
def set_text(self, text):
self.lines = text.split('\n')
+ self.handle('areasize')
def get_text(self):
@@ -115,10 +116,10 @@
def set_yofs(self, yofs):
+ if yofs > len(self.lines) - self.height:
+ yofs = len(self.lines) - self.height
if yofs < 0:
yofs = 0
- if yofs > len(self.lines) - self.height:
- yofs = len(self.lines) - self.height
self.yofs = yofs
self.handle('scroll')
@@ -183,6 +184,16 @@
self.set_yofs(self.cline)
+ def move_pagefirst(self):
+ self.cline = 0
+ self.set_yofs(0)
+
+
+ def move_pagelast(self):
+ self.cline = len(self.lines) - 1
+ self.set_yofs(self.cline)
+
+
def add_char(self, c):
ln = self.lines[self.cline]
cpos = self.get_cpos()
--- a/tuikit/scrollbar.py Tue Mar 15 17:27:09 2011 +0100
+++ b/tuikit/scrollbar.py Wed Mar 16 15:19:05 2011 +0100
@@ -26,7 +26,9 @@
def setpos(self, pos):
self.pos = pos
- self.thumbpos = int(round(self.pos / self.max * (self.height - 3)))
+ self.thumbpos = 0
+ if self.max and self.pos <= self.max:
+ self.thumbpos = int(round(self.pos / self.max * (self.height - 3)))
def on_draw(self, screen, x, y):
--- a/tuikit/textedit.py Tue Mar 15 17:27:09 2011 +0100
+++ b/tuikit/textedit.py Wed Mar 16 15:19:05 2011 +0100
@@ -31,6 +31,10 @@
self.editbox.set_text(text)
+ def scrolltoend(self):
+ self.editbox.move_pagelast()
+
+
def on_draw(self, screen, x, y):
screen.frame(x, y, self.width, self.height)
@@ -40,9 +44,13 @@
def on_editbox_areasize(self):
- self.vscroll.max = len(self.editbox.lines) - self.editbox.height
+ smax = len(self.editbox.lines) - self.editbox.height
+ if smax < 0:
+ smax = 0
+ self.vscroll.max = smax
def on_vscroll_change(self):
self.editbox.yofs = self.vscroll.pos
self.editbox.redraw()
+