Paring down urwid.
This commit is contained in:
parent
23a0fe2ab4
commit
7fa19c7e74
6 changed files with 3 additions and 1072 deletions
|
|
@ -1,37 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Urwid python compatibility definitions
|
||||
# Copyright (C) 2011 Ian Ward
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Urwid web site: http://excess.org/urwid/
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
PYTHON3 = sys.version_info > (3, 0)
|
||||
|
||||
# for iterating over byte strings:
|
||||
# ord2 calls ord in python2 only
|
||||
# chr2 converts an ordinal value to a length-1 byte string
|
||||
# B returns a byte string in all supported python versions
|
||||
# bytes3 creates a byte string from a list of ordinal values
|
||||
ord2 = lambda x: x
|
||||
chr2 = lambda x: bytes([x])
|
||||
B = lambda x: x.encode('iso8859-1')
|
||||
bytes = bytes
|
||||
bytes3 = bytes
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# Urwid common display code
|
||||
# Copyright (C) 2004-2011 Ian Ward
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Urwid web site: http://excess.org/urwid/
|
||||
|
||||
import os
|
||||
import sys
|
||||
import termios
|
||||
|
||||
from urwid.util import StoppingContext
|
||||
|
||||
|
||||
class RealTerminal(object):
|
||||
def __init__(self):
|
||||
super(RealTerminal,self).__init__()
|
||||
self._signal_keys_set = False
|
||||
self._old_signal_keys = None
|
||||
|
||||
def tty_signal_keys(self, intr=None, quit=None, start=None,
|
||||
stop=None, susp=None, fileno=None):
|
||||
"""
|
||||
Read and/or set the tty's signal character settings.
|
||||
This function returns the current settings as a tuple.
|
||||
|
||||
Use the string 'undefined' to unmap keys from their signals.
|
||||
The value None is used when no change is being made.
|
||||
Setting signal keys is done using the integer ascii
|
||||
code for the key, eg. 3 for CTRL+C.
|
||||
|
||||
If this function is called after start() has been called
|
||||
then the original settings will be restored when stop()
|
||||
is called.
|
||||
"""
|
||||
if fileno is None:
|
||||
fileno = sys.stdin.fileno()
|
||||
if not os.isatty(fileno):
|
||||
return
|
||||
|
||||
tattr = termios.tcgetattr(fileno)
|
||||
sattr = tattr[6]
|
||||
skeys = (sattr[termios.VINTR], sattr[termios.VQUIT],
|
||||
sattr[termios.VSTART], sattr[termios.VSTOP],
|
||||
sattr[termios.VSUSP])
|
||||
|
||||
if intr == 'undefined': intr = 0
|
||||
if quit == 'undefined': quit = 0
|
||||
if start == 'undefined': start = 0
|
||||
if stop == 'undefined': stop = 0
|
||||
if susp == 'undefined': susp = 0
|
||||
|
||||
if intr is not None: tattr[6][termios.VINTR] = intr
|
||||
if quit is not None: tattr[6][termios.VQUIT] = quit
|
||||
if start is not None: tattr[6][termios.VSTART] = start
|
||||
if stop is not None: tattr[6][termios.VSTOP] = stop
|
||||
if susp is not None: tattr[6][termios.VSUSP] = susp
|
||||
|
||||
if intr is not None or quit is not None or \
|
||||
start is not None or stop is not None or \
|
||||
susp is not None:
|
||||
termios.tcsetattr(fileno, termios.TCSADRAIN, tattr)
|
||||
self._signal_keys_set = True
|
||||
|
||||
return skeys
|
||||
|
||||
|
||||
class ScreenError(Exception):
|
||||
pass
|
||||
|
||||
class BaseScreen():
|
||||
"""
|
||||
Base class for Screen classes (raw_display.Screen, .. etc)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(BaseScreen,self).__init__()
|
||||
self._palette = {}
|
||||
self._started = False
|
||||
|
||||
started = property(lambda self: self._started)
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
"""Set up the screen. If the screen has already been started, does
|
||||
nothing.
|
||||
|
||||
May be used as a context manager, in which case :meth:`stop` will
|
||||
automatically be called at the end of the block:
|
||||
|
||||
with screen.start():
|
||||
...
|
||||
|
||||
You shouldn't override this method in a subclass; instead, override
|
||||
:meth:`_start`.
|
||||
"""
|
||||
if not self._started:
|
||||
self._start(*args, **kwargs)
|
||||
self._started = True
|
||||
return StoppingContext(self)
|
||||
|
||||
def _start(self):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
if self._started:
|
||||
self._stop()
|
||||
self._started = False
|
||||
|
||||
def _stop(self):
|
||||
pass
|
||||
|
||||
|
||||
def _test():
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
|
||||
if __name__=='__main__':
|
||||
_test()
|
||||
|
|
@ -26,11 +26,6 @@ Terminal Escape Sequences for input and display
|
|||
|
||||
import re
|
||||
|
||||
from urwid import old_str_util as str_util
|
||||
|
||||
from urwid.compat import bytes, bytes3
|
||||
|
||||
within_double_byte = str_util.within_double_byte
|
||||
|
||||
SO = "\x0e"
|
||||
SI = "\x0f"
|
||||
|
|
@ -324,45 +319,6 @@ def process_keyqueue(codes, more_available):
|
|||
if code >27 and code <32:
|
||||
return ["ctrl %s" % chr(ord('A')+code-1)], codes[1:]
|
||||
|
||||
em = str_util.get_byte_encoding()
|
||||
|
||||
if (em == 'wide' and code < 256 and
|
||||
within_double_byte(chr(code),0,0)):
|
||||
if not codes[1:]:
|
||||
if more_available:
|
||||
raise MoreInputRequired()
|
||||
if codes[1:] and codes[1] < 256:
|
||||
db = chr(code)+chr(codes[1])
|
||||
if within_double_byte(db, 0, 1):
|
||||
return [db], codes[2:]
|
||||
if em == 'utf8' and code>127 and code<256:
|
||||
if code & 0xe0 == 0xc0: # 2-byte form
|
||||
need_more = 1
|
||||
elif code & 0xf0 == 0xe0: # 3-byte form
|
||||
need_more = 2
|
||||
elif code & 0xf8 == 0xf0: # 4-byte form
|
||||
need_more = 3
|
||||
else:
|
||||
return ["<%d>"%code], codes[1:]
|
||||
|
||||
for i in range(need_more):
|
||||
if len(codes)-1 <= i:
|
||||
if more_available:
|
||||
raise MoreInputRequired()
|
||||
else:
|
||||
return ["<%d>"%code], codes[1:]
|
||||
k = codes[i+1]
|
||||
if k>256 or k&0xc0 != 0x80:
|
||||
return ["<%d>"%code], codes[1:]
|
||||
|
||||
s = bytes3(codes[:need_more+1])
|
||||
|
||||
assert isinstance(s, bytes)
|
||||
try:
|
||||
return [s.decode("utf-8")], codes[need_more+1:]
|
||||
except UnicodeDecodeError:
|
||||
return ["<%d>"%code], codes[1:]
|
||||
|
||||
if code >127 and code <256:
|
||||
key = chr(code)
|
||||
return [key], codes[1:]
|
||||
|
|
|
|||
|
|
@ -1,368 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Urwid unicode character processing tables
|
||||
# Copyright (C) 2004-2011 Ian Ward
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Urwid web site: http://excess.org/urwid/
|
||||
|
||||
|
||||
import re
|
||||
|
||||
from urwid.compat import bytes, B, ord2
|
||||
|
||||
SAFE_ASCII_RE = re.compile("^[ -~]*$")
|
||||
SAFE_ASCII_BYTES_RE = re.compile(B("^[ -~]*$"))
|
||||
|
||||
_byte_encoding = None
|
||||
|
||||
# GENERATED DATA
|
||||
# generated from
|
||||
# http://www.unicode.org/Public/4.0-Update/EastAsianWidth-4.0.0.txt
|
||||
|
||||
widths = [
|
||||
(126, 1),
|
||||
(159, 0),
|
||||
(687, 1),
|
||||
(710, 0),
|
||||
(711, 1),
|
||||
(727, 0),
|
||||
(733, 1),
|
||||
(879, 0),
|
||||
(1154, 1),
|
||||
(1161, 0),
|
||||
(4347, 1),
|
||||
(4447, 2),
|
||||
(7467, 1),
|
||||
(7521, 0),
|
||||
(8369, 1),
|
||||
(8426, 0),
|
||||
(9000, 1),
|
||||
(9002, 2),
|
||||
(11021, 1),
|
||||
(12350, 2),
|
||||
(12351, 1),
|
||||
(12438, 2),
|
||||
(12442, 0),
|
||||
(19893, 2),
|
||||
(19967, 1),
|
||||
(55203, 2),
|
||||
(63743, 1),
|
||||
(64106, 2),
|
||||
(65039, 1),
|
||||
(65059, 0),
|
||||
(65131, 2),
|
||||
(65279, 1),
|
||||
(65376, 2),
|
||||
(65500, 1),
|
||||
(65510, 2),
|
||||
(120831, 1),
|
||||
(262141, 2),
|
||||
(1114109, 1),
|
||||
]
|
||||
|
||||
# ACCESSOR FUNCTIONS
|
||||
|
||||
def get_width( o ):
|
||||
"""Return the screen column width for unicode ordinal o."""
|
||||
global widths
|
||||
if o == 0xe or o == 0xf:
|
||||
return 0
|
||||
for num, wid in widths:
|
||||
if o <= num:
|
||||
return wid
|
||||
return 1
|
||||
|
||||
def decode_one( text, pos ):
|
||||
"""
|
||||
Return (ordinal at pos, next position) for UTF-8 encoded text.
|
||||
"""
|
||||
assert isinstance(text, bytes), text
|
||||
b1 = ord2(text[pos])
|
||||
if not b1 & 0x80:
|
||||
return b1, pos+1
|
||||
error = ord("?"), pos+1
|
||||
lt = len(text)
|
||||
lt = lt-pos
|
||||
if lt < 2:
|
||||
return error
|
||||
if b1 & 0xe0 == 0xc0:
|
||||
b2 = ord2(text[pos+1])
|
||||
if b2 & 0xc0 != 0x80:
|
||||
return error
|
||||
o = ((b1&0x1f)<<6)|(b2&0x3f)
|
||||
if o < 0x80:
|
||||
return error
|
||||
return o, pos+2
|
||||
if lt < 3:
|
||||
return error
|
||||
if b1 & 0xf0 == 0xe0:
|
||||
b2 = ord2(text[pos+1])
|
||||
if b2 & 0xc0 != 0x80:
|
||||
return error
|
||||
b3 = ord2(text[pos+2])
|
||||
if b3 & 0xc0 != 0x80:
|
||||
return error
|
||||
o = ((b1&0x0f)<<12)|((b2&0x3f)<<6)|(b3&0x3f)
|
||||
if o < 0x800:
|
||||
return error
|
||||
return o, pos+3
|
||||
if lt < 4:
|
||||
return error
|
||||
if b1 & 0xf8 == 0xf0:
|
||||
b2 = ord2(text[pos+1])
|
||||
if b2 & 0xc0 != 0x80:
|
||||
return error
|
||||
b3 = ord2(text[pos+2])
|
||||
if b3 & 0xc0 != 0x80:
|
||||
return error
|
||||
b4 = ord2(text[pos+2])
|
||||
if b4 & 0xc0 != 0x80:
|
||||
return error
|
||||
o = ((b1&0x07)<<18)|((b2&0x3f)<<12)|((b3&0x3f)<<6)|(b4&0x3f)
|
||||
if o < 0x10000:
|
||||
return error
|
||||
return o, pos+4
|
||||
return error
|
||||
|
||||
def decode_one_uni(text, i):
|
||||
"""
|
||||
decode_one implementation for unicode strings
|
||||
"""
|
||||
return ord(text[i]), i+1
|
||||
|
||||
def decode_one_right(text, pos):
|
||||
"""
|
||||
Return (ordinal at pos, next position) for UTF-8 encoded text.
|
||||
pos is assumed to be on the trailing byte of a utf-8 sequence.
|
||||
"""
|
||||
assert isinstance(text, bytes), text
|
||||
error = ord("?"), pos-1
|
||||
p = pos
|
||||
while p >= 0:
|
||||
if ord2(text[p])&0xc0 != 0x80:
|
||||
o, next = decode_one( text, p )
|
||||
return o, p-1
|
||||
p -=1
|
||||
if p == p-4:
|
||||
return error
|
||||
|
||||
def set_byte_encoding(enc):
|
||||
assert enc in ('utf8', 'narrow', 'wide')
|
||||
global _byte_encoding
|
||||
_byte_encoding = enc
|
||||
|
||||
def get_byte_encoding():
|
||||
return _byte_encoding
|
||||
|
||||
def calc_text_pos(text, start_offs, end_offs, pref_col):
|
||||
"""
|
||||
Calculate the closest position to the screen column pref_col in text
|
||||
where start_offs is the offset into text assumed to be screen column 0
|
||||
and end_offs is the end of the range to search.
|
||||
|
||||
text may be unicode or a byte string in the target _byte_encoding
|
||||
|
||||
Returns (position, actual_col).
|
||||
"""
|
||||
assert start_offs <= end_offs, repr((start_offs, end_offs))
|
||||
utfs = isinstance(text, bytes) and _byte_encoding == "utf8"
|
||||
unis = not isinstance(text, bytes)
|
||||
if unis or utfs:
|
||||
decode = [decode_one, decode_one_uni][unis]
|
||||
i = start_offs
|
||||
sc = 0
|
||||
n = 1 # number to advance by
|
||||
while i < end_offs:
|
||||
o, n = decode(text, i)
|
||||
w = get_width(o)
|
||||
if w+sc > pref_col:
|
||||
return i, sc
|
||||
i = n
|
||||
sc += w
|
||||
return i, sc
|
||||
assert type(text) == bytes, repr(text)
|
||||
# "wide" and "narrow"
|
||||
i = start_offs+pref_col
|
||||
if i >= end_offs:
|
||||
return end_offs, end_offs-start_offs
|
||||
if _byte_encoding == "wide":
|
||||
if within_double_byte(text, start_offs, i) == 2:
|
||||
i -= 1
|
||||
return i, i-start_offs
|
||||
|
||||
def calc_width(text, start_offs, end_offs):
|
||||
"""
|
||||
Return the screen column width of text between start_offs and end_offs.
|
||||
|
||||
text may be unicode or a byte string in the target _byte_encoding
|
||||
|
||||
Some characters are wide (take two columns) and others affect the
|
||||
previous character (take zero columns). Use the widths table above
|
||||
to calculate the screen column width of text[start_offs:end_offs]
|
||||
"""
|
||||
|
||||
assert start_offs <= end_offs, repr((start_offs, end_offs))
|
||||
|
||||
utfs = isinstance(text, bytes) and _byte_encoding == "utf8"
|
||||
unis = not isinstance(text, bytes)
|
||||
if (unis and not SAFE_ASCII_RE.match(text)
|
||||
) or (utfs and not SAFE_ASCII_BYTES_RE.match(text)):
|
||||
decode = [decode_one, decode_one_uni][unis]
|
||||
i = start_offs
|
||||
sc = 0
|
||||
n = 1 # number to advance by
|
||||
while i < end_offs:
|
||||
o, n = decode(text, i)
|
||||
w = get_width(o)
|
||||
i = n
|
||||
sc += w
|
||||
return sc
|
||||
# "wide", "narrow" or all printable ASCII, just return the character count
|
||||
return end_offs - start_offs
|
||||
|
||||
def is_wide_char(text, offs):
|
||||
"""
|
||||
Test if the character at offs within text is wide.
|
||||
|
||||
text may be unicode or a byte string in the target _byte_encoding
|
||||
"""
|
||||
if isinstance(text, str):
|
||||
o = ord(text[offs])
|
||||
return get_width(o) == 2
|
||||
assert isinstance(text, bytes)
|
||||
if _byte_encoding == "utf8":
|
||||
o, n = decode_one(text, offs)
|
||||
return get_width(o) == 2
|
||||
if _byte_encoding == "wide":
|
||||
return within_double_byte(text, offs, offs) == 1
|
||||
return False
|
||||
|
||||
def move_prev_char(text, start_offs, end_offs):
|
||||
"""
|
||||
Return the position of the character before end_offs.
|
||||
"""
|
||||
assert start_offs < end_offs
|
||||
if isinstance(text, str):
|
||||
return end_offs-1
|
||||
assert isinstance(text, bytes)
|
||||
if _byte_encoding == "utf8":
|
||||
o = end_offs-1
|
||||
while ord2(text[o])&0xc0 == 0x80:
|
||||
o -= 1
|
||||
return o
|
||||
if _byte_encoding == "wide" and within_double_byte(text,
|
||||
start_offs, end_offs-1) == 2:
|
||||
return end_offs-2
|
||||
return end_offs-1
|
||||
|
||||
def move_next_char(text, start_offs, end_offs):
|
||||
"""
|
||||
Return the position of the character after start_offs.
|
||||
"""
|
||||
assert start_offs < end_offs
|
||||
if isinstance(text, str):
|
||||
return start_offs+1
|
||||
assert isinstance(text, bytes)
|
||||
if _byte_encoding == "utf8":
|
||||
o = start_offs+1
|
||||
while o<end_offs and ord2(text[o])&0xc0 == 0x80:
|
||||
o += 1
|
||||
return o
|
||||
if _byte_encoding == "wide" and within_double_byte(text,
|
||||
start_offs, start_offs) == 1:
|
||||
return start_offs +2
|
||||
return start_offs+1
|
||||
|
||||
def within_double_byte(text, line_start, pos):
|
||||
"""Return whether pos is within a double-byte encoded character.
|
||||
|
||||
text -- byte string in question
|
||||
line_start -- offset of beginning of line (< pos)
|
||||
pos -- offset in question
|
||||
|
||||
Return values:
|
||||
0 -- not within dbe char, or double_byte_encoding == False
|
||||
1 -- pos is on the 1st half of a dbe char
|
||||
2 -- pos is on the 2nd half of a dbe char
|
||||
"""
|
||||
assert isinstance(text, bytes)
|
||||
v = ord2(text[pos])
|
||||
|
||||
if v >= 0x40 and v < 0x7f:
|
||||
# might be second half of big5, uhc or gbk encoding
|
||||
if pos == line_start: return 0
|
||||
|
||||
if ord2(text[pos-1]) >= 0x81:
|
||||
if within_double_byte(text, line_start, pos-1) == 1:
|
||||
return 2
|
||||
return 0
|
||||
|
||||
if v < 0x80: return 0
|
||||
|
||||
i = pos -1
|
||||
while i >= line_start:
|
||||
if ord2(text[i]) < 0x80:
|
||||
break
|
||||
i -= 1
|
||||
|
||||
if (pos - i) & 1:
|
||||
return 1
|
||||
return 2
|
||||
|
||||
# TABLE GENERATION CODE
|
||||
|
||||
def process_east_asian_width():
|
||||
import sys
|
||||
out = []
|
||||
last = None
|
||||
for line in sys.stdin.readlines():
|
||||
if line[:1] == "#": continue
|
||||
line = line.strip()
|
||||
hex,rest = line.split(";",1)
|
||||
wid,rest = rest.split(" # ",1)
|
||||
word1 = rest.split(" ",1)[0]
|
||||
|
||||
if "." in hex:
|
||||
hex = hex.split("..")[1]
|
||||
num = int(hex, 16)
|
||||
|
||||
if word1 in ("COMBINING","MODIFIER","<control>"):
|
||||
l = 0
|
||||
elif wid in ("W", "F"):
|
||||
l = 2
|
||||
else:
|
||||
l = 1
|
||||
|
||||
if last is None:
|
||||
out.append((0, l))
|
||||
last = l
|
||||
|
||||
if last == l:
|
||||
out[-1] = (num, l)
|
||||
else:
|
||||
out.append( (num, l) )
|
||||
last = l
|
||||
|
||||
print("widths = [")
|
||||
for o in out[1:]: # treat control characters same as ascii
|
||||
print("\t%r," % (o,))
|
||||
print("]")
|
||||
|
||||
if __name__ == "__main__":
|
||||
process_east_asian_width()
|
||||
|
||||
|
|
@ -31,16 +31,13 @@ import tty
|
|||
|
||||
from urwid import escape
|
||||
|
||||
from urwid.display_common import BaseScreen, RealTerminal
|
||||
|
||||
class Screen:
|
||||
|
||||
class Screen(BaseScreen, RealTerminal):
|
||||
def __init__(self, input=sys.stdin, output=sys.stdout):
|
||||
"""Initialize a screen that directly prints escape codes to an output
|
||||
terminal.
|
||||
"""
|
||||
super(Screen, self).__init__()
|
||||
self._keyqueue = []
|
||||
self.prev_input_resize = 0
|
||||
self.set_input_timeouts()
|
||||
self._mouse_tracking_enabled = False
|
||||
|
|
@ -95,7 +92,7 @@ class Screen(BaseScreen, RealTerminal):
|
|||
else:
|
||||
self.write(escape.MOUSE_TRACKING_OFF)
|
||||
|
||||
def _start(self, alternate_buffer=True):
|
||||
def start(self, alternate_buffer=True):
|
||||
"""
|
||||
Initialize the screen and input mode.
|
||||
|
||||
|
|
@ -115,14 +112,9 @@ class Screen(BaseScreen, RealTerminal):
|
|||
self._alternate_buffer = alternate_buffer
|
||||
self._next_timeout = self.max_wait
|
||||
|
||||
if not self._signal_keys_set:
|
||||
self._old_signal_keys = self.tty_signal_keys(fileno=fd)
|
||||
|
||||
self._mouse_tracking(self._mouse_tracking_enabled)
|
||||
|
||||
return super(Screen, self)._start()
|
||||
|
||||
def _stop(self):
|
||||
def stop(self):
|
||||
"""
|
||||
Restore the screen.
|
||||
"""
|
||||
|
|
@ -144,12 +136,6 @@ class Screen(BaseScreen, RealTerminal):
|
|||
+ escape.SHOW_CURSOR)
|
||||
self.flush()
|
||||
|
||||
if self._old_signal_keys:
|
||||
self.tty_signal_keys(*(self._old_signal_keys + (fd,)))
|
||||
|
||||
super(Screen, self)._stop()
|
||||
|
||||
|
||||
def write(self, data):
|
||||
"""Write some data to the terminal.
|
||||
|
||||
|
|
@ -210,7 +196,6 @@ class Screen(BaseScreen, RealTerminal):
|
|||
* Mouse button release: ('mouse release', 0, 18, 13),
|
||||
('ctrl mouse release', 0, 17, 23)
|
||||
"""
|
||||
assert self._started
|
||||
|
||||
self._wait_for_input_ready(self._next_timeout)
|
||||
keys, raw = self.parse_input(None, None, self.get_available_raw_input())
|
||||
|
|
|
|||
474
urwid/util.py
474
urwid/util.py
|
|
@ -1,474 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Urwid utility functions
|
||||
# Copyright (C) 2004-2011 Ian Ward
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Urwid web site: http://excess.org/urwid/
|
||||
|
||||
from urwid import escape
|
||||
from urwid.compat import bytes
|
||||
|
||||
import codecs
|
||||
|
||||
str_util = escape.str_util
|
||||
|
||||
# bring str_util functions into our namespace
|
||||
calc_text_pos = str_util.calc_text_pos
|
||||
calc_width = str_util.calc_width
|
||||
is_wide_char = str_util.is_wide_char
|
||||
move_next_char = str_util.move_next_char
|
||||
move_prev_char = str_util.move_prev_char
|
||||
within_double_byte = str_util.within_double_byte
|
||||
|
||||
|
||||
def detect_encoding():
|
||||
# Try to determine if using a supported double-byte encoding
|
||||
import locale
|
||||
try:
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except locale.Error:
|
||||
pass
|
||||
return locale.getlocale()[1] or ""
|
||||
except ValueError as e:
|
||||
# with invalid LANG value python will throw ValueError
|
||||
if e.args and e.args[0].startswith("unknown locale"):
|
||||
return ""
|
||||
else:
|
||||
raise
|
||||
|
||||
if 'detected_encoding' not in locals():
|
||||
detected_encoding = detect_encoding()
|
||||
else:
|
||||
assert 0, "It worked!"
|
||||
|
||||
_target_encoding = None
|
||||
_use_dec_special = True
|
||||
|
||||
|
||||
def set_encoding( encoding ):
|
||||
"""
|
||||
Set the byte encoding to assume when processing strings and the
|
||||
encoding to use when converting unicode strings.
|
||||
"""
|
||||
encoding = encoding.lower()
|
||||
|
||||
global _target_encoding, _use_dec_special
|
||||
|
||||
if encoding in ( 'utf-8', 'utf8', 'utf' ):
|
||||
str_util.set_byte_encoding("utf8")
|
||||
|
||||
_use_dec_special = False
|
||||
elif encoding in ( 'euc-jp' # JISX 0208 only
|
||||
, 'euc-kr', 'euc-cn', 'euc-tw' # CNS 11643 plain 1 only
|
||||
, 'gb2312', 'gbk', 'big5', 'cn-gb', 'uhc'
|
||||
# these shouldn't happen, should they?
|
||||
, 'eucjp', 'euckr', 'euccn', 'euctw', 'cncb' ):
|
||||
str_util.set_byte_encoding("wide")
|
||||
|
||||
_use_dec_special = True
|
||||
else:
|
||||
str_util.set_byte_encoding("narrow")
|
||||
_use_dec_special = True
|
||||
|
||||
# if encoding is valid for conversion from unicode, remember it
|
||||
_target_encoding = 'ascii'
|
||||
try:
|
||||
if encoding:
|
||||
"".encode(encoding)
|
||||
_target_encoding = encoding
|
||||
except LookupError: pass
|
||||
|
||||
|
||||
def get_encoding_mode():
|
||||
"""
|
||||
Get the mode Urwid is using when processing text strings.
|
||||
Returns 'narrow' for 8-bit encodings, 'wide' for CJK encodings
|
||||
or 'utf8' for UTF-8 encodings.
|
||||
"""
|
||||
return str_util.get_byte_encoding()
|
||||
|
||||
|
||||
def apply_target_encoding( s ):
|
||||
"""
|
||||
Return (encoded byte string, character set rle).
|
||||
"""
|
||||
if _use_dec_special and type(s) == str:
|
||||
# first convert drawing characters
|
||||
try:
|
||||
s = s.translate( escape.DEC_SPECIAL_CHARMAP )
|
||||
except NotImplementedError:
|
||||
# python < 2.4 needs to do this the hard way..
|
||||
for c, alt in zip(escape.DEC_SPECIAL_CHARS,
|
||||
escape.ALT_DEC_SPECIAL_CHARS):
|
||||
s = s.replace( c, escape.SO+alt+escape.SI )
|
||||
|
||||
if type(s) == str:
|
||||
s = s.replace(escape.SI+escape.SO, "") # remove redundant shifts
|
||||
s = codecs.encode(s, _target_encoding, 'replace')
|
||||
|
||||
assert isinstance(s, bytes)
|
||||
SO = escape.SO.encode('ascii')
|
||||
SI = escape.SI.encode('ascii')
|
||||
|
||||
sis = s.split(SO)
|
||||
|
||||
assert isinstance(sis[0], bytes)
|
||||
|
||||
sis0 = sis[0].replace(SI, bytes())
|
||||
sout = []
|
||||
cout = []
|
||||
if sis0:
|
||||
sout.append( sis0 )
|
||||
cout.append( (None,len(sis0)) )
|
||||
|
||||
if len(sis)==1:
|
||||
return sis0, cout
|
||||
|
||||
for sn in sis[1:]:
|
||||
assert isinstance(sn, bytes)
|
||||
assert isinstance(SI, bytes)
|
||||
sl = sn.split(SI, 1)
|
||||
if len(sl) == 1:
|
||||
sin = sl[0]
|
||||
assert isinstance(sin, bytes)
|
||||
sout.append(sin)
|
||||
rle_append_modify(cout, (escape.DEC_TAG.encode('ascii'), len(sin)))
|
||||
continue
|
||||
sin, son = sl
|
||||
son = son.replace(SI, bytes())
|
||||
if sin:
|
||||
sout.append(sin)
|
||||
rle_append_modify(cout, (escape.DEC_TAG, len(sin)))
|
||||
if son:
|
||||
sout.append(son)
|
||||
rle_append_modify(cout, (None, len(son)))
|
||||
|
||||
outstr = bytes().join(sout)
|
||||
return outstr, cout
|
||||
|
||||
|
||||
######################################################################
|
||||
# Try to set the encoding using the one detected by the locale module
|
||||
set_encoding( detected_encoding )
|
||||
######################################################################
|
||||
|
||||
|
||||
def supports_unicode():
|
||||
"""
|
||||
Return True if python is able to convert non-ascii unicode strings
|
||||
to the current encoding.
|
||||
"""
|
||||
return _target_encoding and _target_encoding != 'ascii'
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def calc_trim_text( text, start_offs, end_offs, start_col, end_col ):
|
||||
"""
|
||||
Calculate the result of trimming text.
|
||||
start_offs -- offset into text to treat as screen column 0
|
||||
end_offs -- offset into text to treat as the end of the line
|
||||
start_col -- screen column to trim at the left
|
||||
end_col -- screen column to trim at the right
|
||||
|
||||
Returns (start, end, pad_left, pad_right), where:
|
||||
start -- resulting start offset
|
||||
end -- resulting end offset
|
||||
pad_left -- 0 for no pad or 1 for one space to be added
|
||||
pad_right -- 0 for no pad or 1 for one space to be added
|
||||
"""
|
||||
spos = start_offs
|
||||
pad_left = pad_right = 0
|
||||
if start_col > 0:
|
||||
spos, sc = calc_text_pos( text, spos, end_offs, start_col )
|
||||
if sc < start_col:
|
||||
pad_left = 1
|
||||
spos, sc = calc_text_pos( text, start_offs,
|
||||
end_offs, start_col+1 )
|
||||
run = end_col - start_col - pad_left
|
||||
pos, sc = calc_text_pos( text, spos, end_offs, run )
|
||||
if sc < run:
|
||||
pad_right = 1
|
||||
return ( spos, pos, pad_left, pad_right )
|
||||
|
||||
|
||||
|
||||
|
||||
def trim_text_attr_cs( text, attr, cs, start_col, end_col ):
|
||||
"""
|
||||
Return ( trimmed text, trimmed attr, trimmed cs ).
|
||||
"""
|
||||
spos, epos, pad_left, pad_right = calc_trim_text(
|
||||
text, 0, len(text), start_col, end_col )
|
||||
attrtr = rle_subseg( attr, spos, epos )
|
||||
cstr = rle_subseg( cs, spos, epos )
|
||||
if pad_left:
|
||||
al = rle_get_at( attr, spos-1 )
|
||||
rle_append_beginning_modify( attrtr, (al, 1) )
|
||||
rle_append_beginning_modify( cstr, (None, 1) )
|
||||
if pad_right:
|
||||
al = rle_get_at( attr, epos )
|
||||
rle_append_modify( attrtr, (al, 1) )
|
||||
rle_append_modify( cstr, (None, 1) )
|
||||
|
||||
return (bytes().rjust(pad_left) + text[spos:epos] +
|
||||
bytes().rjust(pad_right), attrtr, cstr)
|
||||
|
||||
|
||||
def rle_get_at( rle, pos ):
|
||||
"""
|
||||
Return the attribute at offset pos.
|
||||
"""
|
||||
x = 0
|
||||
if pos < 0:
|
||||
return None
|
||||
for a, run in rle:
|
||||
if x+run > pos:
|
||||
return a
|
||||
x += run
|
||||
return None
|
||||
|
||||
|
||||
def rle_subseg( rle, start, end ):
|
||||
"""Return a sub segment of an rle list."""
|
||||
l = []
|
||||
x = 0
|
||||
for a, run in rle:
|
||||
if start:
|
||||
if start >= run:
|
||||
start -= run
|
||||
x += run
|
||||
continue
|
||||
x += start
|
||||
run -= start
|
||||
start = 0
|
||||
if x >= end:
|
||||
break
|
||||
if x+run > end:
|
||||
run = end-x
|
||||
x += run
|
||||
l.append( (a, run) )
|
||||
return l
|
||||
|
||||
|
||||
def rle_len( rle ):
|
||||
"""
|
||||
Return the number of characters covered by a run length
|
||||
encoded attribute list.
|
||||
"""
|
||||
|
||||
run = 0
|
||||
for v in rle:
|
||||
assert type(v) == tuple, repr(rle)
|
||||
a, r = v
|
||||
run += r
|
||||
return run
|
||||
|
||||
def rle_append_beginning_modify(rle, a_r):
|
||||
"""
|
||||
Append (a, r) (unpacked from *a_r*) to BEGINNING of rle.
|
||||
Merge with first run when possible
|
||||
|
||||
MODIFIES rle parameter contents. Returns None.
|
||||
"""
|
||||
a, r = a_r
|
||||
if not rle:
|
||||
rle[:] = [(a, r)]
|
||||
else:
|
||||
al, run = rle[0]
|
||||
if a == al:
|
||||
rle[0] = (a,run+r)
|
||||
else:
|
||||
rle[0:0] = [(al, r)]
|
||||
|
||||
|
||||
def rle_append_modify(rle, a_r):
|
||||
"""
|
||||
Append (a, r) (unpacked from *a_r*) to the rle list rle.
|
||||
Merge with last run when possible.
|
||||
|
||||
MODIFIES rle parameter contents. Returns None.
|
||||
"""
|
||||
a, r = a_r
|
||||
if not rle or rle[-1][0] != a:
|
||||
rle.append( (a,r) )
|
||||
return
|
||||
la,lr = rle[-1]
|
||||
rle[-1] = (a, lr+r)
|
||||
|
||||
def rle_join_modify( rle, rle2 ):
|
||||
"""
|
||||
Append attribute list rle2 to rle.
|
||||
Merge last run of rle with first run of rle2 when possible.
|
||||
|
||||
MODIFIES attr parameter contents. Returns None.
|
||||
"""
|
||||
if not rle2:
|
||||
return
|
||||
rle_append_modify(rle, rle2[0])
|
||||
rle += rle2[1:]
|
||||
|
||||
def rle_product( rle1, rle2 ):
|
||||
"""
|
||||
Merge the runs of rle1 and rle2 like this:
|
||||
eg.
|
||||
rle1 = [ ("a", 10), ("b", 5) ]
|
||||
rle2 = [ ("Q", 5), ("P", 10) ]
|
||||
rle_product: [ (("a","Q"), 5), (("a","P"), 5), (("b","P"), 5) ]
|
||||
|
||||
rle1 and rle2 are assumed to cover the same total run.
|
||||
"""
|
||||
i1 = i2 = 1 # rle1, rle2 indexes
|
||||
if not rle1 or not rle2: return []
|
||||
a1, r1 = rle1[0]
|
||||
a2, r2 = rle2[0]
|
||||
|
||||
l = []
|
||||
while r1 and r2:
|
||||
r = min(r1, r2)
|
||||
rle_append_modify( l, ((a1,a2),r) )
|
||||
r1 -= r
|
||||
if r1 == 0 and i1< len(rle1):
|
||||
a1, r1 = rle1[i1]
|
||||
i1 += 1
|
||||
r2 -= r
|
||||
if r2 == 0 and i2< len(rle2):
|
||||
a2, r2 = rle2[i2]
|
||||
i2 += 1
|
||||
return l
|
||||
|
||||
|
||||
def rle_factor( rle ):
|
||||
"""
|
||||
Inverse of rle_product.
|
||||
"""
|
||||
rle1 = []
|
||||
rle2 = []
|
||||
for (a1, a2), r in rle:
|
||||
rle_append_modify( rle1, (a1, r) )
|
||||
rle_append_modify( rle2, (a2, r) )
|
||||
return rle1, rle2
|
||||
|
||||
|
||||
class TagMarkupException(Exception): pass
|
||||
|
||||
def decompose_tagmarkup(tm):
|
||||
"""Return (text string, attribute list) for tagmarkup passed."""
|
||||
|
||||
tl, al = _tagmarkup_recurse(tm, None)
|
||||
# join as unicode or bytes based on type of first element
|
||||
text = tl[0][:0].join(tl)
|
||||
|
||||
if al and al[-1][0] is None:
|
||||
del al[-1]
|
||||
|
||||
return text, al
|
||||
|
||||
def _tagmarkup_recurse( tm, attr ):
|
||||
"""Return (text list, attribute list) for tagmarkup passed.
|
||||
|
||||
tm -- tagmarkup
|
||||
attr -- current attribute or None"""
|
||||
|
||||
if type(tm) == list:
|
||||
# for lists recurse to process each subelement
|
||||
rtl = []
|
||||
ral = []
|
||||
for element in tm:
|
||||
tl, al = _tagmarkup_recurse( element, attr )
|
||||
if ral:
|
||||
# merge attributes when possible
|
||||
last_attr, last_run = ral[-1]
|
||||
top_attr, top_run = al[0]
|
||||
if last_attr == top_attr:
|
||||
ral[-1] = (top_attr, last_run + top_run)
|
||||
del al[-1]
|
||||
rtl += tl
|
||||
ral += al
|
||||
return rtl, ral
|
||||
|
||||
if type(tm) == tuple:
|
||||
# tuples mark a new attribute boundary
|
||||
if len(tm) != 2:
|
||||
raise TagMarkupException("Tuples must be in the form (attribute, tagmarkup): %r" % (tm,))
|
||||
|
||||
attr, element = tm
|
||||
return _tagmarkup_recurse( element, attr )
|
||||
|
||||
if not isinstance(tm,(str, bytes)):
|
||||
raise TagMarkupException("Invalid markup element: %r" % tm)
|
||||
|
||||
# text
|
||||
return [tm], [(attr, len(tm))]
|
||||
|
||||
|
||||
|
||||
def is_mouse_event( ev ):
|
||||
return type(ev) == tuple and len(ev)==4 and ev[0].find("mouse")>=0
|
||||
|
||||
def is_mouse_press( ev ):
|
||||
return ev.find("press")>=0
|
||||
|
||||
|
||||
|
||||
class MetaSuper(type):
|
||||
"""adding .__super"""
|
||||
def __init__(cls, name, bases, d):
|
||||
super(MetaSuper, cls).__init__(name, bases, d)
|
||||
if hasattr(cls, "_%s__super" % name):
|
||||
raise AttributeError("Class has same name as one of its super classes")
|
||||
setattr(cls, "_%s__super" % name, super(cls))
|
||||
|
||||
|
||||
|
||||
def int_scale(val, val_range, out_range):
|
||||
"""
|
||||
Scale val in the range [0, val_range-1] to an integer in the range
|
||||
[0, out_range-1]. This implementation uses the "round-half-up" rounding
|
||||
method.
|
||||
|
||||
>>> "%x" % int_scale(0x7, 0x10, 0x10000)
|
||||
'7777'
|
||||
>>> "%x" % int_scale(0x5f, 0x100, 0x10)
|
||||
'6'
|
||||
>>> int_scale(2, 6, 101)
|
||||
40
|
||||
>>> int_scale(1, 3, 4)
|
||||
2
|
||||
"""
|
||||
num = int(val * (out_range-1) * 2 + (val_range-1))
|
||||
dem = ((val_range-1) * 2)
|
||||
# if num % dem == 0 then we are exactly half-way and have rounded up.
|
||||
return num // dem
|
||||
|
||||
|
||||
class StoppingContext(object):
|
||||
"""Context manager that calls ``stop`` on a given object on exit. Used to
|
||||
make the ``start`` method on `MainLoop` and `BaseScreen` optionally act as
|
||||
context managers.
|
||||
"""
|
||||
def __init__(self, wrapped):
|
||||
self._wrapped = wrapped
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc_info):
|
||||
self._wrapped.stop()
|
||||
Loading…
Add table
Add a link
Reference in a new issue