Skip to content

Fixed most issues on Windows/Python 3.5 + Moved the linux loop in the linux code file #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -10,3 +10,4 @@ venv/
coverage.xml
.eggs
*.egg
.idea
9 changes: 9 additions & 0 deletions example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import readchar

if __name__ == '__main__':
print('start... press q to quit')
while 1:
k = readchar.readkey()
print('you pressed ' + str(k))
if k == 'q':
break
89 changes: 66 additions & 23 deletions readchar/key.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2014, 2015 Miguel Ángel García (@magmax9).
# Based on previous work on gist getch()-like unbuffered character
# reading from stdin on both Windows and Unix (Python recipe),
# started by Danny Yoo. Licensed under the MIT license.

# common
LF = '\x0d'
CR = '\x0a'
ENTER = '\x0d'
BACKSPACE = '\x7f'
SUPR = ''
SPACE = '\x20'
ESC = '\x1b'
TAB = '\x09'

# CTRL
CTRL_A = '\x01'
@@ -16,6 +21,8 @@
CTRL_F = '\x06'
CTRL_Z = '\x1a'

LEFTBRACKET = '\x5b'

# ALT
ALT_A = '\x1b\x61'

@@ -50,30 +57,66 @@
END = '\x1b\x5b\x46'

INSERT = '\x1b\x5b\x32\x7e'
SUPR = '\x1b\x5b\x33\x7e'

DELETE = '\x1b\x5b\x33\x7e'

ESCAPE_SEQUENCES = (
ESC,
ESC + '\x5b',
ESC + '\x5b' + '\x31',
ESC + '\x5b' + '\x32',
ESC + '\x5b' + '\x33',
ESC + '\x5b' + '\x35',
ESC + '\x5b' + '\x36',
ESC + '\x5b' + '\x31' + '\x35',
ESC + '\x5b' + '\x31' + '\x36',
ESC + '\x5b' + '\x31' + '\x37',
ESC + '\x5b' + '\x31' + '\x38',
ESC + '\x5b' + '\x31' + '\x39',
ESC + '\x5b' + '\x32' + '\x30',
ESC + '\x5b' + '\x32' + '\x31',
ESC + '\x5b' + '\x32' + '\x32',
ESC + '\x5b' + '\x32' + '\x33',
ESC + '\x5b' + '\x32' + '\x34',
ESC + LEFTBRACKET,
ESC + LEFTBRACKET + '\x31',
ESC + LEFTBRACKET + '\x32',
ESC + LEFTBRACKET + '\x33',
ESC + LEFTBRACKET + '\x35',
ESC + LEFTBRACKET + '\x36',
ESC + LEFTBRACKET + '\x31' + '\x35',
ESC + LEFTBRACKET + '\x31' + '\x36',
ESC + LEFTBRACKET + '\x31' + '\x37',
ESC + LEFTBRACKET + '\x31' + '\x38',
ESC + LEFTBRACKET + '\x31' + '\x39',
ESC + LEFTBRACKET + '\x32' + '\x30',
ESC + LEFTBRACKET + '\x32' + '\x31',
ESC + LEFTBRACKET + '\x32' + '\x32',
ESC + LEFTBRACKET + '\x32' + '\x33',
ESC + LEFTBRACKET + '\x32' + '\x34',
ESC + '\x4f',
ESC + ESC,
ESC + ESC + '\x5b',
ESC + ESC + '\x5b' + '\x32',
ESC + ESC + '\x5b' + '\x33',
ESC + ESC + LEFTBRACKET,
ESC + ESC + LEFTBRACKET + '\x32',
ESC + ESC + LEFTBRACKET + '\x33',
)

linux_keys = {
# Single keys:
CR: 'cr',
ENTER: 'enter',
BACKSPACE: 'backspace',
ESC: 'escape',
HOME: "home",
UP: 'up',
TAB: 'tab',
RIGHT: 'right',
DOWN: 'down',
LEFT: 'left',
PAGE_UP: 'page_up',
PAGE_DOWN: 'page_down',
END: "end",
F1: 'f1',
F2: 'f2',
F3: 'f3',
F4: 'f4',
F5: 'f5',
F6: 'f6',
F7: 'f7',
F8: 'f8',
F9: 'f9',
F10: 'f10',
DELETE: 'delete',
CTRL_A: 'ctrl_a',
CTRL_B: 'ctrl_b',
CTRL_C: 'ctrl_c',
CTRL_D: 'ctrl_d',
CTRL_E: 'ctrl_e',
CTRL_F: 'ctrl_f',
CTRL_Z: 'ctrl_z',
INSERT: 'insert',
CTRL_ALT_A: 'ctrl_alt_a'
}
100 changes: 100 additions & 0 deletions readchar/key_windows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# These following keys are ones that have 2 meanings.
# The first key command is the one that is not in the dict below:
# ctrl_i is tab
# ctrl_[ is escape
# ctrl_2 is ctrl_c
# ctrl_h is backspace
# ctrl_j is ctrl_enter
# ctrl_m is enter

windows_special_keys = {
# Single keys:
'H': 'up',
'M': 'right',
'P': 'down',
'K': 'left',
';': 'f1',
'<': 'f2',
'=': 'f3',
'>': 'f4',
'?': 'f5',
'@': 'f6',
'A': 'f7',
'B': 'f8',
'C': 'f9',
'D': 'f10',
'S': 'delete',
# key combos & shifted_keys
'T': 'shift_f1',
'U': 'shift_f2',
'V': 'shift_f3',
'W': 'shift_f4',
'X': 'shift_f5',
'Y': 'shift_f6',
'Z': 'shift_f7',
'[': 'shift_f8',
'\\\\': 'shift_f9',
']': 'shift_f10',
# ctrl_keys
'^': 'ctrl_f1',
'_': 'ctrl_f2',
'`': 'ctrl_f3',
'a': 'ctrl_f4',
'b': 'ctrl_f5',
'c': 'ctrl_f6',
'd': 'ctrl_f7',
'e': 'ctrl_f8',
'f': 'ctrl_f9',
'g': 'ctrl_f10',
'\\x8d': 'ctrl_up',
'\\x91': 'ctrl_down',
's': 'ctrl_left',
't': 'ctrl_right',
}

windows_keys = {
# Single keys:
'\\x1b': 'escape',
' ': 'space',
'\\x85': 'f11',
'\\x86': 'f12',
'\\x08': 'backspace',
'\\r': 'enter',
'\\t': 'tab',
# key combos & shifted_keys
'\\x87': 'shift_f11',
'\\x88': 'shift_f12',
# ctrl_keys
'\\x89': 'ctrl_f11',
'\\x8a': 'ctrl_f12',
'\\x93': 'ctrl_delete',
'\\n': 'ctrl_enter',
'\\x94': 'ctrl_tab',
'\\x7f': 'ctrl_backspace',
'\\x1c': 'ctrl_\\',
'\\x1d': 'ctrl_]',
# ctrl letters
'\\x01': 'ctrl_a',
'\\x02': 'ctrl_b',
'\\x03': 'ctrl_c',
'\\x04': 'ctrl_d',
'\\x05': 'ctrl_e',
'\\x06': 'ctrl_f',
'\\x07': 'ctrl_g',
'\\x0b': 'ctrl_k',
'\\x0c': 'ctrl_l',
'\\x0e': 'ctrl_n',
'\\x0f': 'ctrl_o',
'\\x10': 'ctrl_p',
'\\x11': 'ctrl_q',
'\\x12': 'ctrl_r',
'\\x13': 'ctrl_s',
'\\x14': 'ctrl_t',
'\\x15': 'ctrl_u',
'\\x16': 'ctrl_v',
'\\x17': 'ctrl_w',
'\\x18': 'ctrl_x',
'\\x19': 'ctrl_y',
'\\x1a': 'ctrl_z',
'\\\\': '\\',
}
15 changes: 3 additions & 12 deletions readchar/readchar.py
Original file line number Diff line number Diff line change
@@ -15,16 +15,7 @@
raise NotImplemented('The platform %s is not supported yet' % sys.platform)


def readkey(getchar_fn=None):
def readkey(getchar_fn=None, blocking=True):
getchar = getchar_fn or readchar
c1 = getchar()
if ord(c1) != 0x1b:
return c1
c2 = getchar()
if ord(c2) != 0x5b:
return c1 + c2
c3 = getchar()
if ord(c3) != 0x33:
return c1 + c2 + c3
c4 = getchar()
return c1 + c2 + c3 + c4
buffer = getchar(blocking)
return buffer
54 changes: 44 additions & 10 deletions readchar/readchar_linux.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,52 @@
# -*- coding: utf-8 -*-
# Initially taken from:
# http://code.activestate.com/recipes/134892/
# Thanks to Danny Yoo
# Copyright (c) 2014, 2015 Miguel Ángel García (@magmax9).
# Based on previous work on gist getch()-like unbuffered character
# reading from stdin on both Windows and Unix (Python recipe),
# started by Danny Yoo. Licensed under the MIT license.

import sys
import tty
import termios
from . import key


def readchar(blocking):
old_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin.fileno())
charbuffer = ''

while True:
if charbuffer in key.ESCAPE_SEQUENCES:
char1 = getkey(False, old_settings)
else:
char1 = getkey(blocking, old_settings)
if (charbuffer + char1) not in key.ESCAPE_SEQUENCES:
# escape sequence complete or not an escape character..
return convertchar(charbuffer + char1)

# handle cases where the escape is finished, but looks incomplete -
# such as a plain old 'ESC' (\x1b) that is not followed by other
# codes:
if (charbuffer + char1) == charbuffer:
return convertchar(charbuffer)

def readchar():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
charbuffer += char1


def getkey(blocking, old_settings):
charbuffer = ''
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
if blocking or select.select([sys.stdin, ], [], [], 0.0)[0]:
char = os.read(sys.stdin.fileno(), 1)
charbuffer = char if type(char) is str else char.decode()
except Exception:
pass
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
return charbuffer


def convertchar(charbuffer):
if charbuffer in key.linux_keys:
return key.linux_keys[charbuffer]
return charbuffer
30 changes: 22 additions & 8 deletions readchar/readchar_windows.py
Original file line number Diff line number Diff line change
@@ -3,15 +3,29 @@
# http://code.activestate.com/recipes/134892/#c9
# Thanks to Stephen Chappell
import msvcrt
from .key_windows import windows_keys, windows_special_keys


def readchar(blocking=False):
"Get a single character on Windows."

while msvcrt.kbhit():
msvcrt.getch()
def get_char():
"""Get a single character on Windows."""
ch = msvcrt.getch()
while ch.decode() in '\x00\xe0':
msvcrt.getch()
if ch in b'\x00\xe0':
ch = msvcrt.getch()
return ch.decode()
ch = repr(ch)[2:-1]
ch = windows_special_keys.get(ch, ch)
else:
ch = repr(ch)[2:-1]
if str(ch)[0] == '\\' or ch == ' ':
ch = windows_keys.get(ch, ch)
return ch


def readchar(blocking):
"""gets a character or combo on windows and returns a string.
If blocking is True then it will catch ctrl+c and not have them end the program.
It will also wait for a key to be pressed before continuing on with the loop."""
if blocking:
return get_char()
else:
if msvcrt.kbhit():
return get_char()