kbhit.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. #!/usr/bin/env python3
  2. import sys
  3. import termios
  4. import atexit
  5. from select import select
  6. class KBHit:
  7. def __init__(self) -> None:
  8. ''' Creates a KBHit object that you can call to do various keyboard things.
  9. '''
  10. self.stdin_fd = sys.stdin.fileno()
  11. self.set_kbhit_terminal()
  12. def set_kbhit_terminal(self) -> None:
  13. ''' Save old terminal settings for closure, remove ICANON & ECHO flags.
  14. '''
  15. # Save the terminal settings
  16. self.old_term = termios.tcgetattr(self.stdin_fd)
  17. self.new_term = self.old_term.copy()
  18. # New terminal setting unbuffered
  19. self.new_term[3] &= ~(termios.ICANON | termios.ECHO)
  20. termios.tcsetattr(self.stdin_fd, termios.TCSAFLUSH, self.new_term)
  21. # Support normal-terminal reset at exit
  22. atexit.register(self.set_normal_term)
  23. def set_normal_term(self) -> None:
  24. ''' Resets to normal terminal. On Windows this is a no-op.
  25. '''
  26. termios.tcsetattr(self.stdin_fd, termios.TCSAFLUSH, self.old_term)
  27. @staticmethod
  28. def getch() -> str:
  29. ''' Returns a keyboard character after kbhit() has been called.
  30. Should not be called in the same program as getarrow().
  31. '''
  32. return sys.stdin.read(1)
  33. @staticmethod
  34. def getarrow() -> int:
  35. ''' Returns an arrow-key code after kbhit() has been called. Codes are
  36. 0 : up
  37. 1 : right
  38. 2 : down
  39. 3 : left
  40. Should not be called in the same program as getch().
  41. '''
  42. c = sys.stdin.read(3)[2]
  43. vals = [65, 67, 66, 68]
  44. return vals.index(ord(c))
  45. @staticmethod
  46. def kbhit():
  47. ''' Returns True if keyboard character was hit, False otherwise.
  48. '''
  49. return select([sys.stdin], [], [], 0)[0] != []
  50. # Test
  51. if __name__ == "__main__":
  52. kb = KBHit()
  53. print('Hit any key, or ESC to exit')
  54. while True:
  55. if kb.kbhit():
  56. c = kb.getch()
  57. if c == '\x1b': # ESC
  58. break
  59. print(c)
  60. kb.set_normal_term()