|
1 | 1 | __copyright__ = """
|
2 | 2 | Copyright (C) 2009-2017 Andreas Kloeckner
|
3 | 3 | Copyright (C) 2014-2017 Aaron Meurer
|
| 4 | +Copyright (C) 2024 Gerhard Sittig |
4 | 5 | """
|
5 | 6 |
|
6 | 7 | __license__ = """
|
|
27 | 28 | import logging
|
28 | 29 | import sys
|
29 | 30 | from datetime import datetime
|
| 31 | +from enum import Enum, auto |
30 | 32 |
|
31 | 33 |
|
32 | 34 | logfile = [None]
|
@@ -283,4 +285,81 @@ def decode_lines(lines):
|
283 | 285 |
|
284 | 286 | # }}}
|
285 | 287 |
|
| 288 | + |
| 289 | +# {{{ get single key press from console outside of curses |
| 290 | + |
| 291 | +class KeyReadImpl(Enum): |
| 292 | + INPUT = auto() |
| 293 | + GETCH = auto() |
| 294 | + SELECT = auto() |
| 295 | + |
| 296 | + |
| 297 | +_keyread_impl = KeyReadImpl.INPUT |
| 298 | +if sys.platform in ("emscripten", "wasi"): |
| 299 | + pass |
| 300 | +elif sys.platform in ("win32",): |
| 301 | + _keyread_impl = KeyReadImpl.GETCH |
| 302 | +else: |
| 303 | + _keyread_impl = KeyReadImpl.SELECT |
| 304 | + |
| 305 | + |
| 306 | +class ConsoleSingleKeyReader: |
| 307 | + """ |
| 308 | + Get a single key press from a terminal without a prompt. |
| 309 | +
|
| 310 | + Eliminates the necessity to press ENTER before other input also |
| 311 | + becomes available. Avoids the accumulation of prompts on the screen |
| 312 | + as was the case with Python's input() call. Is used in situations |
| 313 | + where urwid is disabled and curses calls are not available. |
| 314 | +
|
| 315 | + Supports major desktop platforms with special cases (msvcrt getch(), |
| 316 | + termios and select). Transparently falls back to Python's input() |
| 317 | + method. Call sites remain simple and straight forward. |
| 318 | + """ |
| 319 | + |
| 320 | + def __enter__(self): |
| 321 | + if _keyread_impl == KeyReadImpl.SELECT: |
| 322 | + import termios |
| 323 | + import tty |
| 324 | + self.prev_settings = termios.tcgetattr(sys.stdin) |
| 325 | + tty.setcbreak(sys.stdin.fileno()) |
| 326 | + return self |
| 327 | + |
| 328 | + def __exit__(self, type, value, traceback): |
| 329 | + if _keyread_impl == KeyReadImpl.SELECT: |
| 330 | + import termios |
| 331 | + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.prev_settings) |
| 332 | + |
| 333 | + def get_single_key(self): |
| 334 | + if _keyread_impl == KeyReadImpl.GETCH: |
| 335 | + import msvcrt |
| 336 | + # https://docs.python.org/3/library/msvcrt.html#msvcrt.getch |
| 337 | + # Most keys are returned in the first getch() call. Some |
| 338 | + # special keys (function keys, cursor, keypad) require |
| 339 | + # another call when the first returned '\0' or '\xe0'. |
| 340 | + c = msvcrt.getch() |
| 341 | + if c in ("\x00", "\xe0"): |
| 342 | + c = msvcrt.getch() |
| 343 | + return c |
| 344 | + |
| 345 | + elif _keyread_impl == KeyReadImpl.SELECT: |
| 346 | + import select |
| 347 | + rset, _, _ = select.select([sys.stdin], [], [], None) |
| 348 | + assert sys.stdin in rset |
| 349 | + return sys.stdin.read(1) |
| 350 | + |
| 351 | + # Strictly speaking putting the fallback here which requires |
| 352 | + # pressing ENTER is not correct, this is the "non buffered" |
| 353 | + # console support code. But it simplifies call sites. And is |
| 354 | + # easy to tell by users because a prompt is provided. This is |
| 355 | + # the most portable approach, and backwards compatible with |
| 356 | + # earlier PuDB releases. It's a most appropriate default for |
| 357 | + # otherwise unsupported platforms. Or when users choose to |
| 358 | + # not accept single key presses, or keys other than ENTER. |
| 359 | + else: |
| 360 | + input("Hit Enter to return:") |
| 361 | + return None |
| 362 | + |
| 363 | +# }}} |
| 364 | + |
286 | 365 | # vim: foldmethod=marker
|
0 commit comments