term.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2019 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. term.py: Terminal-handling routines for the MMGen suite
  20. """
  21. import os,struct
  22. from mmgen.common import *
  23. try:
  24. import tty,termios
  25. from select import select
  26. _platform = 'linux'
  27. except:
  28. try:
  29. import msvcrt,time
  30. _platform = 'win'
  31. except:
  32. die(2,'Unable to set terminal mode')
  33. if not sys.stdin.isatty():
  34. msvcrt.setmode(sys.stdin.fileno(),os.O_BINARY)
  35. def _kb_hold_protect_unix():
  36. if g.test_suite: return
  37. fd = sys.stdin.fileno()
  38. old = termios.tcgetattr(fd)
  39. tty.setcbreak(fd)
  40. timeout = float(0.3)
  41. while True:
  42. key = select([sys.stdin], [], [], timeout)[0]
  43. if key: sys.stdin.read(1)
  44. else:
  45. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  46. break
  47. # Use os.read(), not file.read(), to get a variable number of bytes without blocking.
  48. # Request 5 bytes to cover escape sequences generated by F1, F2, .. Fn keys (5 bytes)
  49. # as well as UTF8 chars (4 bytes max).
  50. def _get_keypress_unix(prompt='',immed_chars='',prehold_protect=True,num_chars=5):
  51. fd_err = sys.stderr.fileno()
  52. os.write(fd_err,prompt.encode())
  53. timeout = float(0.3)
  54. fd = sys.stdin.fileno()
  55. old = termios.tcgetattr(fd)
  56. tty.setcbreak(fd)
  57. immed_chars = immed_chars.encode()
  58. if g.test_suite: prehold_protect = False
  59. while True:
  60. # Protect against held-down key before read()
  61. key = select([sys.stdin], [], [], timeout)[0]
  62. s = os.read(fd,num_chars)
  63. if prehold_protect:
  64. if key: continue
  65. if immed_chars == 'ALL' or s in immed_chars: break
  66. if immed_chars == 'ALL_EXCEPT_ENTER' and not s in '\n\r': break
  67. # Protect against long keypress
  68. key = select([sys.stdin], [], [], timeout)[0]
  69. if not key: break
  70. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  71. return s
  72. def _get_keypress_unix_raw(prompt='',immed_chars='',prehold_protect=None,num_chars=5):
  73. msg_r(prompt)
  74. fd = sys.stdin.fileno()
  75. old = termios.tcgetattr(fd)
  76. tty.setcbreak(fd)
  77. ch = os.read(fd,num_chars)
  78. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  79. return ch
  80. def _get_keypress_unix_stub(prompt='',immed_chars='',prehold_protect=None,num_chars=None):
  81. msg_r(prompt)
  82. return sys.stdin.read(1).encode()
  83. #_get_keypress_unix_stub = _get_keypress_unix
  84. def _kb_hold_protect_mswin():
  85. timeout = float(0.5)
  86. while True:
  87. hit_time = time.time()
  88. while True:
  89. if msvcrt.kbhit():
  90. msvcrt.getch()
  91. break
  92. if float(time.time() - hit_time) > timeout:
  93. return
  94. def _get_keypress_mswin(prompt='',immed_chars='',prehold_protect=True,num_chars=None):
  95. msg_r(prompt)
  96. timeout = float(0.5)
  97. while True:
  98. if msvcrt.kbhit():
  99. ch = msvcrt.getch()
  100. if ord(ch) == 3: raise KeyboardInterrupt
  101. if immed_chars == 'ALL' or ch in immed_chars:
  102. return ch
  103. if immed_chars == 'ALL_EXCEPT_ENTER' and not ch in '\n\r':
  104. return ch
  105. hit_time = time.time()
  106. while True:
  107. if msvcrt.kbhit(): break
  108. if float(time.time() - hit_time) > timeout:
  109. return ch
  110. def _get_keypress_mswin_raw(prompt='',immed_chars='',prehold_protect=None,num_chars=None):
  111. msg_r(prompt)
  112. ch = msvcrt.getch()
  113. if ord(ch) == 3: raise KeyboardInterrupt
  114. return ch
  115. def _get_keypress_mswin_stub(prompt='',immed_chars='',prehold_protect=None,num_chars=None):
  116. msg_r(prompt)
  117. return sys.stdin.read(1)
  118. def _get_terminal_size_linux():
  119. try:
  120. return tuple(os.get_terminal_size())
  121. except:
  122. try:
  123. return (os.environ['LINES'],os.environ['COLUMNS'])
  124. except:
  125. return (80,25)
  126. def _get_terminal_size_mswin():
  127. import sys,os,struct
  128. x,y = 0,0
  129. try:
  130. from ctypes import windll,create_string_buffer
  131. # handles - stdin: -10, stdout: -11, stderr: -12
  132. csbi = create_string_buffer(22)
  133. h = windll.kernel32.GetStdHandle(-12)
  134. res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
  135. if res:
  136. (bufx, bufy, curx, cury, wattr, left, top, right, bottom,
  137. maxx, maxy) = struct.unpack('hhhhHhhhhhh', csbi.raw)
  138. x = right - left + 1
  139. y = bottom - top + 1
  140. except:
  141. pass
  142. if x and y:
  143. return x, y
  144. else:
  145. msg(yellow('Warning: could not get terminal size. Using fallback dimensions.'))
  146. return 80,25
  147. def set_terminal_vars():
  148. global get_char,get_char_raw,kb_hold_protect,get_terminal_size
  149. if _platform == 'linux':
  150. get_char = _get_keypress_unix
  151. get_char_raw = _get_keypress_unix_raw
  152. kb_hold_protect = _kb_hold_protect_unix
  153. if not sys.stdin.isatty():
  154. get_char = get_char_raw = _get_keypress_unix_stub
  155. kb_hold_protect = lambda: None
  156. get_terminal_size = _get_terminal_size_linux
  157. else:
  158. get_char = _get_keypress_mswin
  159. get_char_raw = _get_keypress_mswin_raw
  160. kb_hold_protect = _kb_hold_protect_mswin
  161. if not sys.stdin.isatty():
  162. get_char = get_char_raw = _get_keypress_mswin_stub
  163. kb_hold_protect = lambda: None
  164. get_terminal_size = _get_terminal_size_mswin