term.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2018 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. fd = sys.stdin.fileno()
  37. old = termios.tcgetattr(fd)
  38. tty.setcbreak(fd)
  39. timeout = float(0.3)
  40. while True:
  41. key = select([sys.stdin], [], [], timeout)[0]
  42. if key: sys.stdin.read(1)
  43. else:
  44. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  45. break
  46. # Use os.read(), not file.read(), to get a variable number of bytes without blocking.
  47. # Request 5 bytes to cover escape sequences generated by F1, F2, .. Fn keys (5 bytes)
  48. # as well as UTF8 chars (4 bytes max).
  49. def _get_keypress_unix(prompt='',immed_chars='',prehold_protect=True):
  50. msg_r(prompt)
  51. timeout = float(0.3)
  52. fd = sys.stdin.fileno()
  53. old = termios.tcgetattr(fd)
  54. tty.setcbreak(fd)
  55. while True:
  56. # Protect against held-down key before read()
  57. key = select([sys.stdin], [], [], timeout)[0]
  58. ch = os.read(fd,5)
  59. if prehold_protect:
  60. if key: continue
  61. if immed_chars == 'ALL' or ch in immed_chars: break
  62. if immed_chars == 'ALL_EXCEPT_ENTER' and not ch in '\n\r': break
  63. # Protect against long keypress
  64. key = select([sys.stdin], [], [], timeout)[0]
  65. if not key: break
  66. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  67. return ch
  68. def _get_keypress_unix_raw(prompt='',immed_chars='',prehold_protect=None):
  69. msg_r(prompt)
  70. fd = sys.stdin.fileno()
  71. old = termios.tcgetattr(fd)
  72. tty.setcbreak(fd)
  73. ch = os.read(fd,5)
  74. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  75. return ch
  76. def _get_keypress_unix_stub(prompt='',immed_chars='',prehold_protect=None):
  77. msg_r(prompt)
  78. return sys.stdin.read(1)
  79. def _kb_hold_protect_mswin():
  80. timeout = float(0.5)
  81. while True:
  82. hit_time = time.time()
  83. while True:
  84. if msvcrt.kbhit():
  85. msvcrt.getch()
  86. break
  87. if float(time.time() - hit_time) > timeout:
  88. return
  89. def _get_keypress_mswin(prompt='',immed_chars='',prehold_protect=True):
  90. msg_r(prompt)
  91. timeout = float(0.5)
  92. while True:
  93. if msvcrt.kbhit():
  94. ch = msvcrt.getch()
  95. if ord(ch) == 3: raise KeyboardInterrupt
  96. if immed_chars == 'ALL' or ch in immed_chars:
  97. return ch
  98. if immed_chars == 'ALL_EXCEPT_ENTER' and not ch in '\n\r':
  99. return ch
  100. hit_time = time.time()
  101. while True:
  102. if msvcrt.kbhit(): break
  103. if float(time.time() - hit_time) > timeout:
  104. return ch
  105. def _get_keypress_mswin_raw(prompt='',immed_chars='',prehold_protect=None):
  106. msg_r(prompt)
  107. ch = msvcrt.getch()
  108. if ord(ch) == 3: raise KeyboardInterrupt
  109. return ch
  110. def _get_keypress_mswin_stub(prompt='',immed_chars='',prehold_protect=None):
  111. msg_r(prompt)
  112. return sys.stdin.read(1)
  113. def _get_terminal_size_linux():
  114. def ioctl_GWINSZ(fd):
  115. try:
  116. import fcntl
  117. cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
  118. return cr
  119. except:
  120. pass
  121. cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
  122. if not cr:
  123. try:
  124. fd = os.open(os.ctermid(), os.O_RDONLY)
  125. cr = ioctl_GWINSZ(fd)
  126. os.close(fd)
  127. except:
  128. pass
  129. if not cr:
  130. try:
  131. cr = (os.environ['LINES'], os.environ['COLUMNS'])
  132. except:
  133. return 80,25
  134. return int(cr[1]), int(cr[0])
  135. def _get_terminal_size_mswin():
  136. import sys,os,struct
  137. x,y = 0,0
  138. try:
  139. from ctypes import windll,create_string_buffer
  140. # handles - stdin: -10, stdout: -11, stderr: -12
  141. csbi = create_string_buffer(22)
  142. h = windll.kernel32.GetStdHandle(-12)
  143. res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
  144. if res:
  145. (bufx, bufy, curx, cury, wattr, left, top, right, bottom,
  146. maxx, maxy) = struct.unpack('hhhhHhhhhhh', csbi.raw)
  147. x = right - left + 1
  148. y = bottom - top + 1
  149. except:
  150. pass
  151. if x and y:
  152. return x, y
  153. else:
  154. msg(yellow('Warning: could not get terminal size. Using fallback dimensions.'))
  155. return 80,25
  156. def set_terminal_vars():
  157. global get_char,get_char_raw,kb_hold_protect,get_terminal_size
  158. if _platform == 'linux':
  159. get_char = _get_keypress_unix
  160. get_char_raw = _get_keypress_unix_raw
  161. kb_hold_protect = _kb_hold_protect_unix
  162. if not sys.stdin.isatty():
  163. get_char = get_char_raw = _get_keypress_unix_stub
  164. kb_hold_protect = lambda: None
  165. get_terminal_size = _get_terminal_size_linux
  166. else:
  167. get_char = _get_keypress_mswin
  168. get_char_raw = _get_keypress_mswin_raw
  169. kb_hold_protect = _kb_hold_protect_mswin
  170. if not sys.stdin.isatty():
  171. get_char = get_char_raw = _get_keypress_mswin_stub
  172. kb_hold_protect = lambda: None
  173. get_terminal_size = _get_terminal_size_mswin