Initial commit

This commit is contained in:
Lyx committed 2025-03-14 16:23:01 +01:00
commit 3394196287
2164 files changed
+358958

No files matched your search

@@ -0,0 +1,21 @@
import importlib.metadata
__doc__ = """Library to easily read single chars and key strokes"""
__version__ = importlib.metadata.version(__package__)
__all__ = ["readchar", "readkey", "key", "config"]
from sys import platform
from ._config import config
if platform.startswith(("linux", "darwin", "freebsd", "openbsd")):
from . import _posix_key as key
from ._posix_read import readchar, readkey
elif platform in ("win32", "cygwin"):
from . import _win_key as key
from ._win_read import readchar, readkey
else:
raise NotImplementedError(f"The platform {platform} is not supported yet")
@@ -0,0 +1,34 @@
# common
LF = "\x0a"
CR = "\x0d"
SPACE = "\x20"
ESC = "\x1b"
TAB = "\x09"
# CTRL
CTRL_A = "\x01"
CTRL_B = "\x02"
CTRL_C = "\x03"
CTRL_D = "\x04"
CTRL_E = "\x05"
CTRL_F = "\x06"
CTRL_G = "\x07"
CTRL_H = "\x08"
CTRL_I = TAB
CTRL_J = LF
CTRL_K = "\x0b"
CTRL_L = "\x0c"
CTRL_M = CR
CTRL_N = "\x0e"
CTRL_O = "\x0f"
CTRL_P = "\x10"
CTRL_Q = "\x11"
CTRL_R = "\x12"
CTRL_S = "\x13"
CTRL_T = "\x14"
CTRL_U = "\x15"
CTRL_V = "\x16"
CTRL_W = "\x17"
CTRL_X = "\x18"
CTRL_Y = "\x19"
CTRL_Z = "\x1a"
@@ -0,0 +1,13 @@
from typing import List
from . import _base_key as key
class config:
"""Static class that contains Constants used throughout the library.
You can directly use the class-attributes, do not create instances of it!"""
def __new__(cls):
raise SyntaxError("you can't create instances of this class")
INTERRUPT_KEYS: List[str] = [key.CTRL_C]
@@ -0,0 +1,50 @@
from ._base_key import *
# common
BACKSPACE = "\x7f"
# cursors
UP = "\x1b\x5b\x41"
DOWN = "\x1b\x5b\x42"
LEFT = "\x1b\x5b\x44"
RIGHT = "\x1b\x5b\x43"
# navigation keys
INSERT = "\x1b\x5b\x32\x7e"
SUPR = "\x1b\x5b\x33\x7e"
HOME = "\x1b\x5b\x48"
END = "\x1b\x5b\x46"
PAGE_UP = "\x1b\x5b\x35\x7e"
PAGE_DOWN = "\x1b\x5b\x36\x7e"
# function keys
F1 = "\x1b\x4f\x50"
F2 = "\x1b\x4f\x51"
F3 = "\x1b\x4f\x52"
F4 = "\x1b\x4f\x53"
F5 = "\x1b\x5b\x31\x35\x7e"
F6 = "\x1b\x5b\x31\x37\x7e"
F7 = "\x1b\x5b\x31\x38\x7e"
F8 = "\x1b\x5b\x31\x39\x7e"
F9 = "\x1b\x5b\x32\x30\x7e"
F10 = "\x1b\x5b\x32\x31\x7e"
F11 = "\x1b\x5b\x32\x33\x7e"
F12 = "\x1b\x5b\x32\x34\x7e"
# SHIFT+_
SHIFT_TAB = "\x1b\x5b\x5a"
# other
CTRL_ALT_SUPR = "\x1b\x5b\x33\x5e"
# ALT+_
ALT_A = "\x1b\x61"
# CTRL+ALT+_
CTRL_ALT_A = "\x1b\x01"
# aliases
ENTER = LF
DELETE = SUPR
@@ -0,0 +1,55 @@
import sys
import termios
from ._config import config
# Initially taken from:
# http://code.activestate.com/recipes/134892/
# Thanks to Danny Yoo
# more infos from:
# https://gist.github.com/michelbl/efda48b19d3e587685e3441a74457024
# Thanks to Michel Blancard
def readchar() -> str:
"""Reads a single character from the input stream.
Blocks until a character is available."""
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
term = termios.tcgetattr(fd)
try:
term[3] &= ~(termios.ICANON | termios.ECHO | termios.IGNBRK | termios.BRKINT)
termios.tcsetattr(fd, termios.TCSAFLUSH, term)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
def readkey() -> str:
"""Get a keypress. If an escaped key is pressed, the full sequence is
read and returned as noted in `_posix_key.py`."""
c1 = readchar()
if c1 in config.INTERRUPT_KEYS:
raise KeyboardInterrupt
if c1 != "\x1B":
return c1
c2 = readchar()
if c2 not in "\x4F\x5B":
return c1 + c2
c3 = readchar()
if c3 not in "\x31\x32\x33\x35\x36":
return c1 + c2 + c3
c4 = readchar()
if c4 not in "\x30\x31\x33\x34\x35\x37\x38\x39":
return c1 + c2 + c3 + c4
c5 = readchar()
return c1 + c2 + c3 + c4 + c5
@@ -0,0 +1,57 @@
from ._base_key import *
# Windows uses scan codes for extended characters. This dictionary
# translates the second half of the scan codes of special Keys
# into the corresponding variable used by readchar.
#
# for windows scan codes see:
# https://msdn.microsoft.com/en-us/library/aa299374
# or
# https://www.freepascal.org/docs-html/rtl/keyboard/kbdscancode.html
# common
BACKSPACE = "\x08"
# cursors
UP = "\x00\x48"
DOWN = "\x00\x50"
LEFT = "\x00\x4b"
RIGHT = "\x00\x4d"
# navigation keys
INSERT = "\x00\x52"
SUPR = "\x00\x53"
HOME = "\x00\x47"
END = "\x00\x4f"
PAGE_UP = "\x00\x49"
PAGE_DOWN = "\x00\x51"
# function keys
F1 = "\x00\x3b"
F2 = "\x00\x3c"
F3 = "\x00\x3d"
F4 = "\x00\x3e"
F5 = "\x00\x3f"
F6 = "\x00\x40"
F7 = "\x00\x41"
F8 = "\x00\x42"
F9 = "\x00\x43"
F10 = "\x00\x44"
F11 = "\x00\x85" # only in second source
F12 = "\x00\x86" # only in second source
# other
ESC_2 = "\x00\x01"
ENTER_2 = "\x00\x1c"
# don't have table entries for...
# ALT_[A-Z]
# CTRL_ALT_[A-Z],
# CTRL_ALT_SUPR,
# CTRL-F1
# aliases
ENTER = CR
DELETE = SUPR
@@ -0,0 +1,41 @@
import msvcrt
from ._config import config
def readchar() -> str:
"""Reads a single utf8-character from the input stream.
Blocks until a character is available."""
# read a single wide character from the input
return msvcrt.getwch()
def readkey() -> str:
"""Reads the next keypress. If an escaped key is pressed, the full
sequence is read and returned as noted in `_win_key.py`."""
# read first character
ch = readchar()
# keys like CTRL+C should cause a interrupt
if ch in config.INTERRUPT_KEYS:
raise KeyboardInterrupt
# parse special multi character keys (see key module)
# https://learn.microsoft.com/cpp/c-runtime-library/reference/getch-getwch#remarks
if ch in "\x00\xe0":
# read the second half
# we always return the 0x00 prefix, this avoids duplications in the key module
ch = "\x00" + readchar()
# parse unicode surrogates
# https://docs.python.org/3/c-api/unicode.html#c.Py_UNICODE_IS_SURROGATE
if "\uD800" <= ch <= "\uDFFF":
ch += readchar()
# combine the characters into a single utf-16 encoded string.
# this prevents the character from being treated as a surrogate pair again.
ch = ch.encode("utf-16", errors="surrogatepass").decode("utf-16")
return ch
@@ -0,0 +1,11 @@
# flake8: noqa E401,E403
# this file exists only for backwards compatibility
# it allows the use of `import readchar.key`
import sys
from . import key as __key
for __k, __v in vars(__key).items():
if not __k.startswith("__"):
setattr(sys.modules[__name__], __k, __v)
Whitespace-only changes.