Split getch() into _getch_unix() and _getch_windows()

and
detect correct flavor at import time (speedup)
. Prepare 2.1
This commit is contained in:
anatoly techtonik 2013-08-26 09:03:00 +03:00
parent 4eed52d880
commit e7f19d27ee
2 changed files with 82 additions and 67 deletions

View File

@ -21,6 +21,9 @@ Demo
Status
------
2.1 (stable)
- split getch() into _getch_unix() and _getch_windows()
and detect correct flavor at import time (speedup)
2.0 (stable) - API Break in getch() function
- getch() now always returns list of characters

146
pager.py
View File

@ -11,10 +11,10 @@ parameters::
python pager.py
Author: anatoly techtonik <techtonik@gmail.com>
License: Public Domain (use MIT if Public Domain doesn't work for you)
License: Public Domain (use MIT if the former doesn't work for you)
"""
__version__ = '2.0'
__version__ = '2.1'
import os,sys
@ -29,6 +29,8 @@ STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
# --- console/window operations ---
if WINDOWS:
# get console handle
from ctypes import windll, Structure, byref
@ -136,7 +138,7 @@ def getheight():
return height or 25
# --- keyboard constants and input logic ---
# --- keyboard input operations and constants ---
if WINDOWS:
ENTER = ['\x0d']
@ -171,7 +173,80 @@ def dumpkey(key):
else:
return ' '.join( [hex3fy(s) for s in key] )
def getch():
def _getch_windows():
chars = []
if PY3K:
from msvcrt import kbhit, getwch as _getch
else:
from msvcrt import kbhit, getch as _getch
chars = [_getch()] # wait for the keypress
while kbhit(): # deplete input buffer
chars.append(_getch())
return chars
def _getch_unix(): # [ ] _getch_linux()? (test on FreeBSD and MacOS)
"""
# --- current algorithm ---
# 1. switch to char-by-char input mode
# 2. turn off echo
# 3. wait for at least one char to appear
# 4. read the rest of the character buffer
# 5. return list of characters
"""
import sys, termios
fd = sys.stdin.fileno()
# save old terminal settings
old_settings = termios.tcgetattr(fd)
chars = []
try:
# change terminal settings - turn off canonical mode and echo
# in canonical mode read from stdin returns one line at a time
# and we need one char at a time (see DESIGN.rst for more info)
newattr = list(old_settings)
newattr[3] &= ~termios.ICANON
newattr[3] &= ~termios.ECHO
newattr[6][termios.VMIN] = 1 # block until one char received
newattr[6][termios.VTIME] = 0
# TCSANOW below means apply settings immediately
termios.tcsetattr(fd, termios.TCSANOW, newattr)
# [ ] this fails when stdin is redirected, like
# ls -la | pager.py
# [ ] also check on Windows
ch = sys.stdin.read(1)
chars = [ch]
# move rest of chars (if any) from input buffer
# change terminal settings - enable non-blocking read
newattr = termios.tcgetattr(fd)
newattr[6][termios.VMIN] = 0 # CC structure
newattr[6][termios.VTIME] = 0
termios.tcsetattr(fd, termios.TCSANOW, newattr)
while True:
ch = sys.stdin.read(1)
if ch != '':
chars.append(ch)
else:
break
finally:
# restore terminal settings. Do this when all output is
# finished - TCSADRAIN flag
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return chars
# choose correct getch function at module import time
if os.name == 'nt':
getch = _getch_windows
else:
getch = _getch_linux
getch.__doc__ = \
"""
Wait for keypress(es), return list of chars generated as a result.
@ -185,69 +260,6 @@ def getch():
# Ctrl-C [n] Windows [y] Linux [ ] OSX
# Ctrl-Break [y] Windows [n] Linux [ ] OSX
chars = []
try:
if PY3K:
from msvcrt import kbhit, getwch as _getch
else:
from msvcrt import kbhit, getch as _getch
chars = [_getch()] # wait for the keypress
while kbhit(): # deplete input buffer
chars.append(_getch())
except ImportError:
''' we're not on Windows, try Unix-like approach '''
# --- current algorithm ---
# 1. switch to char-by-char input mode
# 2. turn off echo
# 3. wait for at least one char to appear
# 4. read the rest of the character buffer
# 5. return list of characters
import sys, termios
fd = sys.stdin.fileno()
# save old terminal settings
old_settings = termios.tcgetattr(fd)
try:
# change terminal settings - turn off canonical mode and echo
# in canonical mode read from stdin returns one line at a time
# and we need one char at a time (see DESIGN.rst for more info)
newattr = list(old_settings)
newattr[3] &= ~termios.ICANON
newattr[3] &= ~termios.ECHO
newattr[6][termios.VMIN] = 1 # block until one char received
newattr[6][termios.VTIME] = 0
# TCSANOW below means apply settings immediately
termios.tcsetattr(fd, termios.TCSANOW, newattr)
# [ ] this fails when stdin is redirected, like
# ls -la | pager.py
# [ ] also check on Windows
ch = sys.stdin.read(1)
chars = [ch]
# move rest of chars (if any) from input buffer
# change terminal settings - enable non-blocking read
newattr = termios.tcgetattr(fd)
newattr[6][termios.VMIN] = 0 # CC structure
newattr[6][termios.VTIME] = 0
termios.tcsetattr(fd, termios.TCSANOW, newattr)
while True:
ch = sys.stdin.read(1)
if ch != '':
chars.append(ch)
else:
break
finally:
# restore terminal settings. Do this when all output is
# finished - TCSADRAIN flag
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return chars
# --- /getch() stuff ---
def echo(msg):
"""