mirror of
https://github.com/wassname/pyreadline.git
synced 2026-06-27 16:10:38 +08:00
Adding Gary Bishops readline v1.12 as is from readline-1.12.zip under the name pyreadline.
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: readline
|
||||
Version: 1.12
|
||||
Summary: Python implementation of GNU readline
|
||||
Home-page: UNKNOWN
|
||||
Author: Gary Bishop
|
||||
Author-email: gb@cs.unc.edu
|
||||
License: UNKNOWN
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
@@ -0,0 +1,712 @@
|
||||
'''Cursor control and color for the Windows console.
|
||||
|
||||
This was modeled after the C extension of the same name by Fredrik Lundh.
|
||||
'''
|
||||
|
||||
# primitive debug printing that won't interfere with the screen
|
||||
if 0:
|
||||
fp = open('debug.txt', 'w')
|
||||
def log(s):
|
||||
print >>fp, s
|
||||
fp.flush()
|
||||
else:
|
||||
def log(s):
|
||||
pass
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
import re
|
||||
|
||||
try:
|
||||
# I developed this with ctypes 0.6
|
||||
from ctypes import *
|
||||
from _ctypes import call_function
|
||||
except ImportError:
|
||||
print 'you need the ctypes module to run this code'
|
||||
print 'http://starship.python.net/crew/theller/ctypes/'
|
||||
raise
|
||||
|
||||
# my code
|
||||
from keysyms import make_keysym, make_keyinfo
|
||||
|
||||
# some constants we need
|
||||
STD_INPUT_HANDLE = -10
|
||||
STD_OUTPUT_HANDLE = -11
|
||||
ENABLE_WINDOW_INPUT = 0x0008
|
||||
ENABLE_MOUSE_INPUT = 0x0010
|
||||
ENABLE_PROCESSED_INPUT = 0x0001
|
||||
WHITE = 0x7
|
||||
BLACK = 0
|
||||
MENU_EVENT = 0x0008
|
||||
KEY_EVENT = 0x0001
|
||||
MOUSE_MOVED = 0x0001
|
||||
MOUSE_EVENT = 0x0002
|
||||
WINDOW_BUFFER_SIZE_EVENT = 0x0004
|
||||
FOCUS_EVENT = 0x0010
|
||||
MENU_EVENT = 0x0008
|
||||
VK_SHIFT = 0x10
|
||||
VK_CONTROL = 0x11
|
||||
VK_MENU = 0x12
|
||||
GENERIC_READ = int(0x80000000L)
|
||||
GENERIC_WRITE = 0x40000000
|
||||
|
||||
# Windows structures we'll need later
|
||||
class COORD(Structure):
|
||||
_fields_ = [("X", c_short),
|
||||
("Y", c_short)]
|
||||
|
||||
class SMALL_RECT(Structure):
|
||||
_fields_ = [("Left", c_short),
|
||||
("Top", c_short),
|
||||
("Right", c_short),
|
||||
("Bottom", c_short)]
|
||||
|
||||
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
|
||||
_fields_ = [("dwSize", COORD),
|
||||
("dwCursorPosition", COORD),
|
||||
("wAttributes", c_short),
|
||||
("srWindow", SMALL_RECT),
|
||||
("dwMaximumWindowSize", COORD)]
|
||||
|
||||
class CHAR_UNION(Union):
|
||||
_fields_ = [("UnicodeChar", c_short),
|
||||
("AsciiChar", c_char)]
|
||||
|
||||
class CHAR_INFO(Structure):
|
||||
_fields_ = [("Char", CHAR_UNION),
|
||||
("Attributes", c_short)]
|
||||
|
||||
class KEY_EVENT_RECORD(Structure):
|
||||
_fields_ = [("bKeyDown", c_byte),
|
||||
("pad2", c_byte),
|
||||
('pad1', c_short),
|
||||
("wRepeatCount", c_short),
|
||||
("wVirtualKeyCode", c_short),
|
||||
("wVirtualScanCode", c_short),
|
||||
("uChar", CHAR_UNION),
|
||||
("dwControlKeyState", c_int)]
|
||||
|
||||
class MOUSE_EVENT_RECORD(Structure):
|
||||
_fields_ = [("dwMousePosition", COORD),
|
||||
("dwButtonState", c_int),
|
||||
("dwControlKeyState", c_int),
|
||||
("dwEventFlags", c_int)]
|
||||
|
||||
class WINDOW_BUFFER_SIZE_RECORD(Structure):
|
||||
_fields_ = [("dwSize", COORD)]
|
||||
|
||||
class MENU_EVENT_RECORD(Structure):
|
||||
_fields_ = [("dwCommandId", c_uint)]
|
||||
|
||||
class FOCUS_EVENT_RECORD(Structure):
|
||||
_fields_ = [("bSetFocus", c_byte)]
|
||||
|
||||
class INPUT_UNION(Union):
|
||||
_fields_ = [("KeyEvent", KEY_EVENT_RECORD),
|
||||
("MouseEvent", MOUSE_EVENT_RECORD),
|
||||
("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD),
|
||||
("MenuEvent", MENU_EVENT_RECORD),
|
||||
("FocusEvent", FOCUS_EVENT_RECORD)]
|
||||
|
||||
class INPUT_RECORD(Structure):
|
||||
_fields_ = [("EventType", c_short),
|
||||
("Event", INPUT_UNION)]
|
||||
|
||||
class CONSOLE_CURSOR_INFO(Structure):
|
||||
_fields_ = [("dwSize", c_int),
|
||||
("bVisible", c_byte)]
|
||||
|
||||
# I didn't want to have to individually import these so I made a list, they are
|
||||
# added to the Console class later in this file.
|
||||
|
||||
funcs = [
|
||||
'AllocConsole',
|
||||
'CreateConsoleScreenBuffer',
|
||||
'FillConsoleOutputAttribute',
|
||||
'FillConsoleOutputCharacterA',
|
||||
'FreeConsole',
|
||||
'GetConsoleCursorInfo',
|
||||
'GetConsoleMode',
|
||||
'GetConsoleScreenBufferInfo',
|
||||
'GetConsoleTitleA',
|
||||
'GetProcAddress',
|
||||
'GetStdHandle',
|
||||
'PeekConsoleInputA',
|
||||
'ReadConsoleInputA',
|
||||
'ScrollConsoleScreenBufferA',
|
||||
'SetConsoleActiveScreenBuffer',
|
||||
'SetConsoleCursorInfo',
|
||||
'SetConsoleCursorPosition',
|
||||
'SetConsoleMode',
|
||||
'SetConsoleScreenBufferSize',
|
||||
'SetConsoleTextAttribute',
|
||||
'SetConsoleTitleA',
|
||||
'SetConsoleWindowInfo',
|
||||
'WriteConsoleA',
|
||||
'WriteConsoleOutputCharacterA',
|
||||
]
|
||||
|
||||
# I don't want events for these keys, they are just a bother for my application
|
||||
key_modifiers = { VK_SHIFT:1,
|
||||
VK_CONTROL:1,
|
||||
VK_MENU:1, # alt key
|
||||
0x5b:1, # windows key
|
||||
}
|
||||
|
||||
class Console(object):
|
||||
'''Console driver for Windows.
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self, newbuffer=0):
|
||||
'''Initialize the Console object.
|
||||
|
||||
newbuffer=1 will allocate a new buffer so the old content will be restored
|
||||
on exit.
|
||||
'''
|
||||
#Do I need the following line? It causes a console to be created whenever
|
||||
#readline is imported into a pythonw application which seems wrong. Things
|
||||
#seem to work without it...
|
||||
#self.AllocConsole()
|
||||
|
||||
if newbuffer:
|
||||
self.hout = self.CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
|
||||
0, None, 1, None)
|
||||
self.SetConsoleActiveScreenBuffer(self.hout)
|
||||
else:
|
||||
self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
|
||||
self.hin = self.GetStdHandle(STD_INPUT_HANDLE)
|
||||
self.inmode = c_int(0)
|
||||
self.GetConsoleMode(self.hin, byref(self.inmode))
|
||||
self.SetConsoleMode(self.hin, 0xf)
|
||||
info = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
self.GetConsoleScreenBufferInfo(self.hout, byref(info))
|
||||
self.attr = info.wAttributes # remember the initial colors
|
||||
background = self.attr & 0xf0
|
||||
for escape in self.escape_to_color:
|
||||
if self.escape_to_color[escape] is not None:
|
||||
self.escape_to_color[escape] |= background
|
||||
log('initial attr=%x' % self.attr)
|
||||
self.softspace = 0 # this is for using it as a file-like object
|
||||
self.serial = 0
|
||||
|
||||
self.pythondll = CDLL('python%s%s' % (sys.version[0], sys.version[2]))
|
||||
self.inputHookPtr = c_int.from_address(addressof(self.pythondll.PyOS_InputHook)).value
|
||||
setattr(Console, 'PyMem_Malloc', self.pythondll.PyMem_Malloc)
|
||||
|
||||
def __del__(self):
|
||||
'''Cleanup the console when finished.'''
|
||||
# I don't think this ever gets called
|
||||
self.SetConsoleTextAttribute(self.hout, self.saveattr)
|
||||
self.SetConsoleMode(self.hin, self.inmode)
|
||||
self.FreeConsole()
|
||||
|
||||
def fixcoord(self, x, y):
|
||||
'''Return a long with x and y packed inside, also handle negative x and y.'''
|
||||
if x < 0 or y < 0:
|
||||
info = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
self.GetConsoleScreenBufferInfo(self.hout, byref(info))
|
||||
if x < 0:
|
||||
x = info.srWindow.Right - x
|
||||
y = info.srWindow.Bottom + y
|
||||
|
||||
# this is a hack! ctypes won't pass structures but COORD is just like a
|
||||
# long, so this works.
|
||||
return c_int(y << 16 | x)
|
||||
|
||||
def pos(self, x=None, y=None):
|
||||
'''Move or query the window cursor.'''
|
||||
if x is None:
|
||||
info = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
self.GetConsoleScreenBufferInfo(self.hout, byref(info))
|
||||
return (info.dwCursorPosition.X, info.dwCursorPosition.Y)
|
||||
else:
|
||||
return self.SetConsoleCursorPosition(self.hout, self.fixcoord(x, y))
|
||||
|
||||
def home(self):
|
||||
'''Move to home.'''
|
||||
self.pos(0,0)
|
||||
|
||||
# Map ANSI color escape sequences into Windows Console Attributes
|
||||
|
||||
terminal_escape = re.compile('(\001?\033\\[[0-9;]+m\002?)')
|
||||
escape_parts = re.compile('\001?\033\\[([0-9;]+)m\002?')
|
||||
escape_to_color = { '0;30': 0x0, #black
|
||||
'0;31': 0x4, #red
|
||||
'0;32': 0x2, #green
|
||||
'0;33': 0x4+0x2, #brown?
|
||||
'0;34': 0x1, #blue
|
||||
'0;35': 0x1+0x4, #purple
|
||||
'0;36': 0x2+0x4, #cyan
|
||||
'0;37': 0x1+0x2+0x4, #grey
|
||||
'1;30': 0x1+0x2+0x4, #dark gray
|
||||
'1;31': 0x4+0x8, #red
|
||||
'1;32': 0x2+0x8, #light green
|
||||
'1;33': 0x4+0x2+0x8, #yellow
|
||||
'1;34': 0x1+0x8, #light blue
|
||||
'1;35': 0x1+0x4+0x8, #light purple
|
||||
'1;36': 0x1+0x2+0x8, #light cyan
|
||||
'1;37': 0x1+0x2+0x4+0x8, #white
|
||||
'0': None,
|
||||
}
|
||||
|
||||
# This pattern should match all characters that change the cursor position differently
|
||||
# than a normal character.
|
||||
motion_char_re = re.compile('([\n\r\t\010\007])')
|
||||
|
||||
def write_scrolling(self, text, attr=None):
|
||||
'''write text at current cursor position while watching for scrolling.
|
||||
|
||||
If the window scrolls because you are at the bottom of the screen
|
||||
buffer, all positions that you are storing will be shifted by the
|
||||
scroll amount. For example, I remember the cursor position of the
|
||||
prompt so that I can redraw the line but if the window scrolls,
|
||||
the remembered position is off.
|
||||
|
||||
This variant of write tries to keep track of the cursor position
|
||||
so that it will know when the screen buffer is scrolled. It
|
||||
returns the number of lines that the buffer scrolled.
|
||||
|
||||
'''
|
||||
x, y = self.pos()
|
||||
w, h = self.size()
|
||||
scroll = 0 # the result
|
||||
|
||||
# split the string into ordinary characters and funny characters
|
||||
chunks = self.motion_char_re.split(text)
|
||||
for chunk in chunks:
|
||||
log('C:'+chunk)
|
||||
n = self.write_color(chunk, attr)
|
||||
if len(chunk) == 1: # the funny characters will be alone
|
||||
if chunk[0] == '\n': # newline
|
||||
x = 0
|
||||
y += 1
|
||||
elif chunk[0] == '\r': # carriage return
|
||||
x = 0
|
||||
elif chunk[0] == '\t': # tab
|
||||
x = 8*(int(x/8)+1)
|
||||
if x > w: # newline
|
||||
x -= w
|
||||
y += 1
|
||||
elif chunk[0] == '\007': # bell
|
||||
pass
|
||||
elif chunk[0] == '\010':
|
||||
x -= 1
|
||||
if x < 0:
|
||||
y -= 1 # backed up 1 line
|
||||
else: # ordinary character
|
||||
x += 1
|
||||
if x == w: # wrap
|
||||
x = 0
|
||||
y += 1
|
||||
if y == h: # scroll
|
||||
scroll += 1
|
||||
y = h - 1
|
||||
else: # chunk of ordinary characters
|
||||
x += n
|
||||
l = int(x / w) # lines we advanced
|
||||
x = x % w # new x value
|
||||
y += l
|
||||
if y >= h: # scroll
|
||||
scroll += y - h + 1
|
||||
y = h - 1
|
||||
return scroll
|
||||
|
||||
def write_color(self, text, attr=None):
|
||||
'''write text at current cursor position and interpret color escapes.
|
||||
|
||||
return the number of characters written.
|
||||
'''
|
||||
log('write_color("%s", %s)' % (text, attr))
|
||||
chunks = self.terminal_escape.split(text)
|
||||
log('chunks=%s' % repr(chunks))
|
||||
junk = c_int(0)
|
||||
n = 0 # count the characters we actually write, omitting the escapes
|
||||
for chunk in chunks:
|
||||
m = self.escape_parts.match(chunk)
|
||||
if m:
|
||||
attr = self.escape_to_color[m.group(1)]
|
||||
continue
|
||||
n += len(chunk)
|
||||
log('attr=%s' % attr)
|
||||
if attr is None:
|
||||
attr = self.attr
|
||||
self.SetConsoleTextAttribute(self.hout, attr)
|
||||
self.WriteConsoleA(self.hout, chunk, len(chunk), byref(junk), None)
|
||||
return n
|
||||
|
||||
def write_plain(self, text, attr=None):
|
||||
'''write text at current cursor position.'''
|
||||
log('write("%s", %s)' %(text,attr))
|
||||
if attr is None:
|
||||
attr = self.attr
|
||||
n = c_int(0)
|
||||
self.SetConsoleTextAttribute(self.hout, attr)
|
||||
self.WriteConsoleA(self.hout, text, len(text), byref(n), None)
|
||||
return len(text)
|
||||
|
||||
# make this class look like a file object
|
||||
def write(self, text):
|
||||
log('write("%s")' % text)
|
||||
return self.write_color(text)
|
||||
|
||||
#write = write_scrolling
|
||||
|
||||
def isatty(self):
|
||||
return True
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def page(self, attr=None, fill=' '):
|
||||
'''Fill the entire screen.'''
|
||||
if attr is None:
|
||||
attr = self.attr
|
||||
if len(fill) != 1:
|
||||
raise ValueError
|
||||
info = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
self.GetConsoleScreenBufferInfo(self.hout, byref(info))
|
||||
if info.dwCursorPosition.X != 0 or info.dwCursorPosition.Y != 0:
|
||||
self.SetConsoleCursorPosition(self.hout, self.fixcoord(0, 0))
|
||||
|
||||
w = info.dwSize.X
|
||||
n = c_int(0)
|
||||
for y in range(info.dwSize.Y):
|
||||
self.FillConsoleOutputAttribute(self.hout, attr, w, self.fixcoord(0, y), byref(n))
|
||||
self.FillConsoleOutputCharacterA(self.hout, ord(fill[0]), w, self.fixcoord(0, y), byref(n))
|
||||
|
||||
self.attr = attr
|
||||
|
||||
def text(self, x, y, text, attr=None):
|
||||
'''Write text at the given position.'''
|
||||
if attr is None:
|
||||
attr = self.attr
|
||||
|
||||
pos = self.fixcoord(x, y)
|
||||
n = c_int(0)
|
||||
self.WriteConsoleOutputCharacterA(self.hout, text, len(text), pos, byref(n))
|
||||
self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n))
|
||||
|
||||
def rectangle(self, rect, attr=None, fill=' '):
|
||||
'''Fill Rectangle.'''
|
||||
x0, y0, x1, y1 = rect
|
||||
n = c_int(0)
|
||||
if attr is None:
|
||||
attr = self.attr
|
||||
for y in range(y0, y1):
|
||||
pos = self.fixcoord(x0, y)
|
||||
self.FillConsoleOutputAttribute(self.hout, attr, x1-x0, pos, byref(n))
|
||||
self.FillConsoleOutputCharacterA(self.hout, ord(fill[0]), x1-x0, pos, byref(n))
|
||||
|
||||
def scroll(self, rect, dx, dy, attr=None, fill=' '):
|
||||
'''Scroll a rectangle.'''
|
||||
if attr is None:
|
||||
attr = self.attr
|
||||
|
||||
x0, y0, x1, y1 = rect
|
||||
source = SMALL_RECT(x0, y0, x1-1, y1-1)
|
||||
dest = self.fixcoord(x0+dx, y0+dy)
|
||||
style = CHAR_INFO()
|
||||
style.Char.AsciiChar = fill[0]
|
||||
style.Attributes = attr
|
||||
|
||||
return self.ScrollConsoleScreenBufferA(self.hout, byref(source), byref(source),
|
||||
dest, byref(style))
|
||||
|
||||
def scroll_window(self, lines):
|
||||
'''Scroll the window by the indicated number of lines.'''
|
||||
info = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
self.GetConsoleScreenBufferInfo(self.hout, byref(info))
|
||||
rect = info.srWindow
|
||||
log('sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom))
|
||||
top = rect.Top + lines
|
||||
bot = rect.Bottom + lines
|
||||
h = bot - top
|
||||
maxbot = info.dwSize.Y-1
|
||||
log('sw: lines=%d mb=%d top=%d bot=%d' % (lines,maxbot,top,bot))
|
||||
if top < 0:
|
||||
top = 0
|
||||
bot = h
|
||||
if bot > maxbot:
|
||||
bot = maxbot
|
||||
top = bot - h
|
||||
|
||||
nrect = SMALL_RECT()
|
||||
nrect.Top = top
|
||||
nrect.Bottom = bot
|
||||
nrect.Left = rect.Left
|
||||
nrect.Right = rect.Right
|
||||
log('sn: top=%d bot=%d' % (top,bot))
|
||||
r=self.SetConsoleWindowInfo(self.hout, True, byref(nrect))
|
||||
log('r=%d' % r)
|
||||
|
||||
def get(self):
|
||||
'''Get next event from queue.'''
|
||||
inputHookFunc = c_int.from_address(self.inputHookPtr).value
|
||||
|
||||
Cevent = INPUT_RECORD()
|
||||
count = c_int(0)
|
||||
while 1:
|
||||
if inputHookFunc:
|
||||
call_function(inputHookFunc, ())
|
||||
status = self.ReadConsoleInputA(self.hin, byref(Cevent), 1, byref(count))
|
||||
if status and count.value == 1:
|
||||
e = event(self, Cevent)
|
||||
return e
|
||||
|
||||
def getkeypress(self):
|
||||
'''Return next key press event from the queue, ignoring others.'''
|
||||
while 1:
|
||||
e = self.get()
|
||||
if e.type == 'KeyPress' and e.keycode not in key_modifiers:
|
||||
log(e)
|
||||
if e.keysym == 'Next':
|
||||
self.scroll_window(12)
|
||||
elif e.keysym == 'Prior':
|
||||
self.scroll_window(-12)
|
||||
else:
|
||||
return e
|
||||
|
||||
def getchar(self):
|
||||
'''Get next character from queue.'''
|
||||
|
||||
Cevent = INPUT_RECORD()
|
||||
count = c_int(0)
|
||||
while 1:
|
||||
status = self.ReadConsoleInputA(self.hin, byref(Cevent), 1, byref(count))
|
||||
if (status and count.value==1 and Cevent.EventType == 1 and
|
||||
Cevent.Event.KeyEvent.bKeyDown):
|
||||
sym = keysym(Cevent.Event.KeyEvent.wVirtualKeyCode)
|
||||
if len(sym) == 0:
|
||||
sym = Cevent.Event.KeyEvent.uChar.AsciiChar
|
||||
return sym
|
||||
|
||||
def peek(self):
|
||||
'''Check event queue.'''
|
||||
Cevent = INPUT_RECORD()
|
||||
count = c_int(0)
|
||||
status = PeekConsoleInput(self.hin, byref(Cevent), 1, byref(count))
|
||||
if status and count == 1:
|
||||
return event(self, Cevent)
|
||||
|
||||
def title(self, txt=None):
|
||||
'''Set/get title.'''
|
||||
if txt:
|
||||
self.SetConsoleTitleA(txt)
|
||||
else:
|
||||
buffer = c_buffer(200)
|
||||
n = self.GetConsoleTitleA(buffer, 200)
|
||||
if n > 0:
|
||||
return buffer.value[:n]
|
||||
|
||||
def size(self, width=None, height=None):
|
||||
'''Set/get window size.'''
|
||||
info = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
status = self.GetConsoleScreenBufferInfo(self.hout, byref(info))
|
||||
if not status:
|
||||
return None
|
||||
if width is not None and height is not None:
|
||||
wmin = info.srWindow.Right - info.srWindow.Left + 1
|
||||
hmin = info.srWindow.Bottom - info.srWindow.Top + 1
|
||||
#print wmin, hmin
|
||||
width = max(width, wmin)
|
||||
height = max(height, hmin)
|
||||
#print width, height
|
||||
self.SetConsoleScreenBufferSize(self.hout, self.fixcoord(width, height))
|
||||
else:
|
||||
return (info.dwSize.X, info.dwSize.Y)
|
||||
|
||||
def cursor(self, visible):
|
||||
'''Set cursor on or off.'''
|
||||
info = CONSOLE_CURSOR_INFO()
|
||||
if self.GetConsoleCursorInfo(self.hout, byref(info)):
|
||||
info.bVisible = visible
|
||||
self.SetConsoleCursorInfo(self.hout, byref(info))
|
||||
|
||||
def bell(self):
|
||||
self.write('\007')
|
||||
|
||||
def next_serial(self):
|
||||
'''Get next event serial number.'''
|
||||
self.serial += 1
|
||||
return self.serial
|
||||
|
||||
# add the functions from the dll to the class
|
||||
for func in funcs:
|
||||
setattr(Console, func, getattr(windll.kernel32, func))
|
||||
|
||||
class event(object):
|
||||
'''Represent events from the console.'''
|
||||
def __init__(self, console, input):
|
||||
'''Initialize an event from the Windows input structure.'''
|
||||
self.type = '??'
|
||||
self.serial = console.next_serial()
|
||||
self.width = 0
|
||||
self.height = 0
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.char = ''
|
||||
self.keycode = 0
|
||||
self.keysym = '??'
|
||||
self.keyinfo = '' # a tuple with (control, meta, shift, keycode) for dispatch
|
||||
self.width = None
|
||||
|
||||
if input.EventType == KEY_EVENT:
|
||||
if input.Event.KeyEvent.bKeyDown:
|
||||
self.type = "KeyPress"
|
||||
else:
|
||||
self.type = "KeyRelease"
|
||||
self.char = input.Event.KeyEvent.uChar.AsciiChar
|
||||
self.keycode = input.Event.KeyEvent.wVirtualKeyCode
|
||||
self.state = input.Event.KeyEvent.dwControlKeyState
|
||||
self.keysym = make_keysym(self.keycode)
|
||||
self.keyinfo = make_keyinfo(self.keycode, self.state)
|
||||
elif input.EventType == MOUSE_EVENT:
|
||||
if input.Event.MouseEvent.dwEventFlags & MOUSE_MOVED:
|
||||
self.type = "Motion"
|
||||
else:
|
||||
self.type = "Button"
|
||||
self.x = input.Event.MouseEvent.dwMousePosition.X
|
||||
self.y = input.Event.MouseEvent.dwMousePosition.Y
|
||||
self.state = input.Event.MouseEvent.dwButtonState
|
||||
elif input.EventType == WINDOW_BUFFER_SIZE_EVENT:
|
||||
self.type = "Configure"
|
||||
self.width = input.Event.WindowBufferSizeEvent.dwSize.X
|
||||
self.height = input.Event.WindowBufferSizeEvent.dwSize.Y
|
||||
elif input.EventType == FOCUS_EVENT:
|
||||
if input.Event.FocusEvent.bSetFocus:
|
||||
self.type = "FocusIn"
|
||||
else:
|
||||
self.type = "FocusOut"
|
||||
elif input.EventType == MENU_EVENT:
|
||||
self.type = "Menu"
|
||||
self.state = input.Event.MenuEvent.dwCommandId
|
||||
|
||||
def __repr__(self):
|
||||
'''Display an event for debugging.'''
|
||||
if self.type in ['KeyPress', 'KeyRelease']:
|
||||
s = "%s char='%s'%d keysym='%s' keycode=%d:%x state=%x keyinfo=%s" % \
|
||||
(self.type, self.char, ord(self.char), self.keysym, self.keycode, self.keycode,
|
||||
self.state, self.keyinfo)
|
||||
elif self.type in ['Motion', 'Button']:
|
||||
s = '%s x=%d y=%d state=%x' % (self.type, self.x, self.y, self.state)
|
||||
elif self.type == 'Configure':
|
||||
s = '%s w=%d h=%d' % (self.type, self.width, self.height)
|
||||
elif self.type in ['FocusIn', 'FocusOut']:
|
||||
s = self.type
|
||||
elif self.type == 'Menu':
|
||||
s = '%s state=%x' % (self.type, self.state)
|
||||
else:
|
||||
s = 'unknown event type'
|
||||
return s
|
||||
|
||||
def getconsole(buffer=1):
|
||||
"""Get a console handle.
|
||||
|
||||
If buffer is non-zero, a new console buffer is allocated and
|
||||
installed. Otherwise, this returns a handle to the current
|
||||
console buffer"""
|
||||
|
||||
c = Console(buffer)
|
||||
|
||||
return c
|
||||
|
||||
# The following code uses ctypes to allow a Python callable to
|
||||
# substitute for GNU readline within the Python interpreter. Calling
|
||||
# raw_input or other functions that do input, inside your callable
|
||||
# might be a bad idea, then again, it might work.
|
||||
|
||||
# The Python callable can raise EOFError or KeyboardInterrupt and
|
||||
# these will be translated into the appropriate outputs from readline
|
||||
# so that they will then be translated back!
|
||||
|
||||
# If the Python callable raises any other exception, a traceback will
|
||||
# be printed and readline will appear to return an empty line.
|
||||
|
||||
# I use ctypes to create a C-callable from a Python wrapper that
|
||||
# handles the exceptions and gets the result into the right form.
|
||||
|
||||
# the type for our C-callable wrapper
|
||||
HOOKFUNC22 = CFUNCTYPE(c_char_p, c_char_p)
|
||||
HOOKFUNC23 = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p)
|
||||
|
||||
readline_hook = None # the python hook goes here
|
||||
readline_ref = None # this holds a reference to the c-callable to keep it alive
|
||||
|
||||
def hook_wrapper_23(stdin, stdout, prompt):
|
||||
'''Wrap a Python readline so it behaves like GNU readline.'''
|
||||
try:
|
||||
# call the Python hook
|
||||
res = readline_hook(prompt)
|
||||
# make sure it returned the right sort of thing
|
||||
if res and not isinstance(res, str):
|
||||
raise TypeError, 'readline must return a string.'
|
||||
except KeyboardInterrupt:
|
||||
# GNU readline returns 0 on keyboard interrupt
|
||||
return 0
|
||||
except EOFError:
|
||||
# It returns an empty string on EOF
|
||||
res = ''
|
||||
except:
|
||||
print >>sys.stderr, 'Readline internal error'
|
||||
traceback.print_exc()
|
||||
res = '\n'
|
||||
# we have to make a copy because the caller expects to free the result
|
||||
n = len(res)
|
||||
p = Console.PyMem_Malloc(n+1)
|
||||
cdll.msvcrt.strncpy(p, res, n+1)
|
||||
return p
|
||||
|
||||
def hook_wrapper(prompt):
|
||||
'''Wrap a Python readline so it behaves like GNU readline.'''
|
||||
try:
|
||||
# call the Python hook
|
||||
res = readline_hook(prompt)
|
||||
# make sure it returned the right sort of thing
|
||||
if res and not isinstance(res, str):
|
||||
raise TypeError, 'readline must return a string.'
|
||||
except KeyboardInterrupt:
|
||||
# GNU readline returns 0 on keyboard interrupt
|
||||
return 0
|
||||
except EOFError:
|
||||
# It returns an empty string on EOF
|
||||
res = ''
|
||||
except:
|
||||
print >>sys.stderr, 'Readline internal error'
|
||||
traceback.print_exc()
|
||||
res = '\n'
|
||||
# we have to make a copy because the caller expects to free the result
|
||||
p = cdll.msvcrt._strdup(res)
|
||||
return p
|
||||
|
||||
def install_readline(hook):
|
||||
'''Set up things for the interpreter to call our function like GNU readline.'''
|
||||
global readline_hook, readline_ref
|
||||
# save the hook so the wrapper can call it
|
||||
readline_hook = hook
|
||||
# get the address of PyOS_ReadlineFunctionPointer so we can update it
|
||||
PyOS_RFP = c_int.from_address(Console.GetProcAddress(sys.dllhandle,
|
||||
"PyOS_ReadlineFunctionPointer"))
|
||||
# save a reference to the generated C-callable so it doesn't go away
|
||||
if sys.version < '2.3':
|
||||
readline_ref = HOOKFUNC22(hook_wrapper)
|
||||
else:
|
||||
readline_ref = HOOKFUNC23(hook_wrapper_23)
|
||||
# get the address of the function
|
||||
func_start = c_int.from_address(addressof(readline_ref)).value
|
||||
# write the function address into PyOS_ReadlineFunctionPointer
|
||||
PyOS_RFP.value = func_start
|
||||
|
||||
if __name__ == '__main__':
|
||||
import time, sys
|
||||
c = Console(0)
|
||||
sys.stdout = c
|
||||
sys.stderr = c
|
||||
c.page()
|
||||
c.pos(5, 10)
|
||||
c.write('hi there')
|
||||
print 'some printed output'
|
||||
for i in range(10):
|
||||
c.getkeypress()
|
||||
del c
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
from PyReadline import *
|
||||
|
||||
__all__ = [ 'parse_and_bind',
|
||||
'get_line_buffer',
|
||||
'insert_text',
|
||||
'read_init_file',
|
||||
'read_history_file',
|
||||
'write_history_file',
|
||||
'get_history_length',
|
||||
'set_history_length',
|
||||
'set_startup_hook',
|
||||
'set_pre_input_hook',
|
||||
'set_completer',
|
||||
'get_completer',
|
||||
'get_begidx',
|
||||
'get_endidx',
|
||||
'set_completer_delims',
|
||||
'get_completer_delims',
|
||||
'add_history' ]
|
||||
@@ -0,0 +1,173 @@
|
||||
import win32con as c32
|
||||
from ctypes import windll
|
||||
|
||||
# table for translating virtual keys to X windows key symbols
|
||||
code2sym_map = {c32.VK_CANCEL: 'Cancel',
|
||||
c32.VK_BACK: 'BackSpace',
|
||||
c32.VK_TAB: 'Tab',
|
||||
c32.VK_CLEAR: 'Clear',
|
||||
c32.VK_RETURN: 'Return',
|
||||
c32.VK_SHIFT:'Shift_L',
|
||||
c32.VK_CONTROL: 'Control_L',
|
||||
c32.VK_MENU: 'Alt_L',
|
||||
c32.VK_PAUSE: 'Pause',
|
||||
c32.VK_CAPITAL: 'Caps_Lock',
|
||||
c32.VK_ESCAPE: 'Escape',
|
||||
c32.VK_SPACE: 'space',
|
||||
c32.VK_PRIOR: 'Prior',
|
||||
c32.VK_NEXT: 'Next',
|
||||
c32.VK_END: 'End',
|
||||
c32.VK_HOME: 'Home',
|
||||
c32.VK_LEFT: 'Left',
|
||||
c32.VK_UP: 'Up',
|
||||
c32.VK_RIGHT: 'Right',
|
||||
c32.VK_DOWN: 'Down',
|
||||
c32.VK_SELECT: 'Select',
|
||||
c32.VK_PRINT: 'Print',
|
||||
c32.VK_EXECUTE: 'Execute',
|
||||
c32.VK_SNAPSHOT: 'Snapshot',
|
||||
c32.VK_INSERT: 'Insert',
|
||||
c32.VK_DELETE: 'Delete',
|
||||
c32.VK_HELP: 'Help',
|
||||
c32.VK_F1: 'F1',
|
||||
c32.VK_F2: 'F2',
|
||||
c32.VK_F3: 'F3',
|
||||
c32.VK_F4: 'F4',
|
||||
c32.VK_F5: 'F5',
|
||||
c32.VK_F6: 'F6',
|
||||
c32.VK_F7: 'F7',
|
||||
c32.VK_F8: 'F8',
|
||||
c32.VK_F9: 'F9',
|
||||
c32.VK_F10: 'F10',
|
||||
c32.VK_F11: 'F11',
|
||||
c32.VK_F12: 'F12',
|
||||
c32.VK_F13: 'F13',
|
||||
c32.VK_F14: 'F14',
|
||||
c32.VK_F15: 'F15',
|
||||
c32.VK_F16: 'F16',
|
||||
c32.VK_F17: 'F17',
|
||||
c32.VK_F18: 'F18',
|
||||
c32.VK_F19: 'F19',
|
||||
c32.VK_F20: 'F20',
|
||||
c32.VK_F21: 'F21',
|
||||
c32.VK_F22: 'F22',
|
||||
c32.VK_F23: 'F23',
|
||||
c32.VK_F24: 'F24',
|
||||
c32.VK_NUMLOCK: 'Num_Lock,',
|
||||
c32.VK_SCROLL: 'Scroll_Lock',
|
||||
c32.VK_APPS: 'VK_APPS',
|
||||
c32.VK_PROCESSKEY: 'VK_PROCESSKEY',
|
||||
c32.VK_ATTN: 'VK_ATTN',
|
||||
c32.VK_CRSEL: 'VK_CRSEL',
|
||||
c32.VK_EXSEL: 'VK_EXSEL',
|
||||
c32.VK_EREOF: 'VK_EREOF',
|
||||
c32.VK_PLAY: 'VK_PLAY',
|
||||
c32.VK_ZOOM: 'VK_ZOOM',
|
||||
c32.VK_NONAME: 'VK_NONAME',
|
||||
c32.VK_PA1: 'VK_PA1',
|
||||
c32.VK_OEM_CLEAR: 'VK_OEM_CLEAR',
|
||||
c32.VK_NUMPAD0: 'NUMPAD0',
|
||||
c32.VK_NUMPAD1: 'NUMPAD1',
|
||||
c32.VK_NUMPAD2: 'NUMPAD2',
|
||||
c32.VK_NUMPAD3: 'NUMPAD3',
|
||||
c32.VK_NUMPAD4: 'NUMPAD4',
|
||||
c32.VK_NUMPAD5: 'NUMPAD5',
|
||||
c32.VK_NUMPAD6: 'NUMPAD6',
|
||||
c32.VK_NUMPAD7: 'NUMPAD7',
|
||||
c32.VK_NUMPAD8: 'NUMPAD8',
|
||||
c32.VK_NUMPAD9: 'NUMPAD9',
|
||||
c32.VK_DIVIDE: 'Divide',
|
||||
c32.VK_MULTIPLY: 'Multiply',
|
||||
c32.VK_ADD: 'Add',
|
||||
c32.VK_SUBTRACT: 'Subtract',
|
||||
c32.VK_DECIMAL: 'VK_DECIMAL'
|
||||
}
|
||||
|
||||
# function to handle the mapping
|
||||
def make_keysym(keycode):
|
||||
try:
|
||||
sym = code2sym_map[keycode]
|
||||
except KeyError:
|
||||
sym = ''
|
||||
return sym
|
||||
|
||||
sym2code_map = {}
|
||||
for code,sym in code2sym_map.iteritems():
|
||||
sym2code_map[sym.lower()] = code
|
||||
|
||||
def key_text_to_keyinfo(keytext):
|
||||
'''Convert a GNU readline style textual description of a key to keycode with modifiers'''
|
||||
if keytext.startswith('"'): # "
|
||||
return keyseq_to_keyinfo(keytext[1:-1])
|
||||
else:
|
||||
return keyname_to_keyinfo(keytext)
|
||||
|
||||
VkKeyScan = windll.user32.VkKeyScanA
|
||||
|
||||
def char_to_keyinfo(char, control=False, meta=False, shift=False):
|
||||
vk = VkKeyScan(ord(char))
|
||||
if vk & 0xffff == 0xffff:
|
||||
print 'VkKeyScan("%s") = %x' % (char, vk)
|
||||
raise ValueError, 'bad key'
|
||||
if vk & 0x100:
|
||||
shift = True
|
||||
if vk & 0x200:
|
||||
control = True
|
||||
if vk & 0x400:
|
||||
meta = True
|
||||
return (control, meta, shift, vk & 0xff)
|
||||
|
||||
def keyname_to_keyinfo(keyname):
|
||||
control = False
|
||||
meta = False
|
||||
shift = False
|
||||
|
||||
while 1:
|
||||
lkeyname = keyname.lower()
|
||||
if lkeyname.startswith('control-'):
|
||||
control = True
|
||||
keyname = keyname[8:]
|
||||
elif lkeyname.startswith('meta-'):
|
||||
meta = True
|
||||
keyname = keyname[5:]
|
||||
elif lkeyname.startswith('alt-'):
|
||||
meta = True
|
||||
keyname = keyname[4:]
|
||||
elif lkeyname.startswith('shift-'):
|
||||
shift = True
|
||||
keyname = keyname[6:]
|
||||
else:
|
||||
if len(keyname) > 1:
|
||||
return (control, meta, shift, sym2code_map[keyname.lower()])
|
||||
else:
|
||||
return char_to_keyinfo(keyname, control, meta, shift)
|
||||
|
||||
def keyseq_to_keyinfo(keyseq):
|
||||
res = []
|
||||
control = False
|
||||
meta = False
|
||||
shift = False
|
||||
|
||||
while 1:
|
||||
if keyseq.startswith('\\C-'):
|
||||
control = True
|
||||
keyseq = keyseq[3:]
|
||||
elif keyseq.startswith('\\M-'):
|
||||
meta = True
|
||||
keyseq = keyseq[3:]
|
||||
elif keyseq.startswith('\\e'):
|
||||
res.append(char_to_keyinfo('\033', control, meta, shift))
|
||||
control = meta = shift = False
|
||||
keyseq = keyseq[2:]
|
||||
elif len(keyseq) >= 1:
|
||||
res.append(char_to_keyinfo(keyseq[0], control, meta, shift))
|
||||
control = meta = shift = False
|
||||
keyseq = keyseq[1:]
|
||||
else:
|
||||
return res[0]
|
||||
|
||||
def make_keyinfo(keycode, state):
|
||||
control = (state & (4+8)) != 0
|
||||
meta = (state & (1+2)) != 0
|
||||
shift = (state & 0x10) != 0
|
||||
return (control, meta, shift, keycode)
|
||||
Reference in New Issue
Block a user