Coverage for view / keyboard.py: 100.00%

41 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-07 00:07 +0000

1import sys 

2import tty 

3import termios 

4from enum import Enum, auto 

5from dataclasses import dataclass 

6from typing import Optional 

7 

8class KeyboardKey(Enum): 

9 UP = auto() 

10 DOWN = auto() 

11 ENTER = auto() 

12 SPACE = auto() 

13 BACKSPACE = auto() 

14 CHAR = auto() 

15 UNKNOWN = auto() 

16 

17 

18@dataclass(frozen=True) 

19class KeyboardToken: 

20 key: KeyboardKey 

21 char: Optional[str] = None 

22 

23 

24 

25def read_char(): 

26 """Reads a single character from stdin without waiting for Enter.""" 

27 fd = sys.stdin.fileno() 

28 attr = termios.tcgetattr(fd) 

29 try: 

30 tty.setraw(fd) 

31 ch = sys.stdin.read(1) 

32 if ch == "\x1b": 

33 next1 = sys.stdin.read(1) 

34 next2 = sys.stdin.read(1) 

35 if next1 == "[": 

36 if next2 == "A": 

37 return KeyboardToken(KeyboardKey.UP) 

38 if next2 == "B": 

39 return KeyboardToken(KeyboardKey.DOWN) 

40 return KeyboardToken(KeyboardKey.UNKNOWN) 

41 

42 if ch in ("\r", "\n"): 

43 return KeyboardToken(KeyboardKey.ENTER) 

44 if ch == " ": 

45 return KeyboardToken(KeyboardKey.SPACE) 

46 if ch in ("\x7f", "\b"): 

47 return KeyboardToken(KeyboardKey.BACKSPACE) 

48 return KeyboardToken(KeyboardKey.CHAR, ch.lower()) 

49 

50 finally: 

51 termios.tcsetattr(fd, termios.TCSANOW, attr)