term.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2014 Philemon <mmgen-py@yandex.com>
  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 sys, os, struct
  22. from mmgen.util import msg, msg_r
  23. def _kb_hold_protect_unix():
  24. fd = sys.stdin.fileno()
  25. old = termios.tcgetattr(fd)
  26. tty.setcbreak(fd)
  27. timeout = float(0.3)
  28. while True:
  29. key = select([sys.stdin], [], [], timeout)[0]
  30. if key: sys.stdin.read(1)
  31. else:
  32. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  33. break
  34. def _get_keypress_unix(prompt="",immed_chars="",prehold_protect=True):
  35. msg_r(prompt)
  36. timeout = float(0.3)
  37. fd = sys.stdin.fileno()
  38. old = termios.tcgetattr(fd)
  39. tty.setcbreak(fd)
  40. while True:
  41. # Protect against held-down key before read()
  42. key = select([sys.stdin], [], [], timeout)[0]
  43. ch = sys.stdin.read(1)
  44. if prehold_protect:
  45. if key: continue
  46. if immed_chars == "ALL" or ch in immed_chars: break
  47. if immed_chars == "ALL_EXCEPT_ENTER" and not ch in "\n\r": break
  48. # Protect against long keypress
  49. key = select([sys.stdin], [], [], timeout)[0]
  50. if not key: break
  51. termios.tcsetattr(fd, termios.TCSADRAIN, old)
  52. return ch
  53. def _kb_hold_protect_mswin():
  54. timeout = float(0.5)
  55. while True:
  56. hit_time = time.time()
  57. while True:
  58. if msvcrt.kbhit():
  59. msvcrt.getch()
  60. break
  61. if float(time.time() - hit_time) > timeout:
  62. return
  63. def _get_keypress_mswin(prompt="",immed_chars="",prehold_protect=True):
  64. msg_r(prompt)
  65. timeout = float(0.5)
  66. while True:
  67. if msvcrt.kbhit():
  68. ch = msvcrt.getch()
  69. if ord(ch) == 3: raise KeyboardInterrupt
  70. if immed_chars == "ALL" or ch in immed_chars:
  71. return ch
  72. if immed_chars == "ALL_EXCEPT_ENTER" and not ch in "\n\r":
  73. return ch
  74. hit_time = time.time()
  75. while True:
  76. if msvcrt.kbhit(): break
  77. if float(time.time() - hit_time) > timeout:
  78. return ch
  79. def _get_terminal_size_linux():
  80. def ioctl_GWINSZ(fd):
  81. try:
  82. import fcntl
  83. cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
  84. return cr
  85. except:
  86. pass
  87. cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
  88. if not cr:
  89. try:
  90. fd = os.open(os.ctermid(), os.O_RDONLY)
  91. cr = ioctl_GWINSZ(fd)
  92. os.close(fd)
  93. except:
  94. pass
  95. if not cr:
  96. try:
  97. cr = (os.environ['LINES'], os.environ['COLUMNS'])
  98. except:
  99. return 80,25
  100. return int(cr[1]), int(cr[0])
  101. def _get_terminal_size_mswin():
  102. try:
  103. from ctypes import windll, create_string_buffer
  104. # stdin handle is -10
  105. # stdout handle is -11
  106. # stderr handle is -12
  107. h = windll.kernel32.GetStdHandle(-12)
  108. csbi = create_string_buffer(22)
  109. res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
  110. if res:
  111. (bufx, bufy, curx, cury, wattr, left, top, right, bottom,
  112. maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
  113. sizex = right - left + 1
  114. sizey = bottom - top + 1
  115. return sizex, sizey
  116. except:
  117. return 80,25
  118. def mswin_dummy_flush(fd,termconst): pass
  119. try:
  120. import tty, termios
  121. from select import select
  122. get_char = _get_keypress_unix
  123. kb_hold_protect = _kb_hold_protect_unix
  124. get_terminal_size = _get_terminal_size_linux
  125. myflush = termios.tcflush
  126. # call: myflush(sys.stdin, termios.TCIOFLUSH)
  127. except:
  128. try:
  129. import msvcrt, time
  130. get_char = _get_keypress_mswin
  131. kb_hold_protect = _kb_hold_protect_mswin
  132. get_terminal_size = _get_terminal_size_mswin
  133. myflush = mswin_dummy_flush
  134. except:
  135. if not sys.platform.startswith("linux") \
  136. and not sys.platform.startswith("win"):
  137. msg("Unsupported platform: %s" % sys.platform)
  138. msg("This program currently runs only on Linux and Windows")
  139. else:
  140. msg("Unable to set terminal mode")
  141. sys.exit(2)
  142. def do_pager(text):
  143. pagers = ["less","more"]
  144. shell = False
  145. from os import environ
  146. # Hack for MS Windows command line (i.e. non CygWin) environment
  147. # When 'shell' is true, Windows aborts the calling program if executable
  148. # not found.
  149. # When 'shell' is false, an exception is raised, invoking the fallback
  150. # 'print' instead of the pager.
  151. # We risk assuming that "more" will always be available on a stock
  152. # Windows installation.
  153. if sys.platform.startswith("win") and 'HOME' not in environ:
  154. shell = True
  155. pagers = ["more"]
  156. if 'PAGER' in environ and environ['PAGER'] != pagers[0]:
  157. pagers = [environ['PAGER']] + pagers
  158. for pager in pagers:
  159. end = "" if pager == "less" else "\n(end of text)\n"
  160. try:
  161. from subprocess import Popen, PIPE, STDOUT
  162. p = Popen([pager], stdin=PIPE, shell=shell)
  163. except: pass
  164. else:
  165. p.communicate(text+end+"\n")
  166. msg_r("\r")
  167. break
  168. else: print text+end