You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

155 lines
3.7 KiB

  1. import curses
  2. import curses.wrapper
  3. from curses.ascii import isprint
  4. from twisted.internet import reactor
  5. class CursesStdIO():
  6. def __init__(self, stdscr, callback=None):
  7. self.statusText = "Synapse test app -"
  8. self.searchText = ''
  9. self.stdscr = stdscr
  10. self.logLine = ''
  11. self.callback = callback
  12. self._setup()
  13. def _setup(self):
  14. self.stdscr.nodelay(1) # Make non blocking
  15. self.rows, self.cols = self.stdscr.getmaxyx()
  16. self.lines = []
  17. curses.use_default_colors()
  18. self.paintStatus(self.statusText)
  19. self.stdscr.refresh()
  20. def set_callback(self, callback):
  21. self.callback = callback
  22. def fileno(self):
  23. """ We want to select on FD 0 """
  24. return 0
  25. def connectionLost(self, reason):
  26. self.close()
  27. def print_line(self, text):
  28. """ add a line to the internal list of lines"""
  29. self.lines.append(text)
  30. self.redraw()
  31. def print_log(self, text):
  32. self.logLine = text
  33. self.redraw()
  34. def redraw(self):
  35. """ method for redisplaying lines
  36. based on internal list of lines """
  37. self.stdscr.clear()
  38. self.paintStatus(self.statusText)
  39. i = 0
  40. index = len(self.lines) - 1
  41. while i < (self.rows - 3) and index >= 0:
  42. self.stdscr.addstr(self.rows - 3 - i, 0, self.lines[index],
  43. curses.A_NORMAL)
  44. i = i + 1
  45. index = index - 1
  46. self.printLogLine(self.logLine)
  47. self.stdscr.refresh()
  48. def paintStatus(self, text):
  49. if len(text) > self.cols:
  50. raise RuntimeError("TextTooLongError")
  51. self.stdscr.addstr(
  52. self.rows - 2, 0,
  53. text + ' ' * (self.cols - len(text)),
  54. curses.A_STANDOUT)
  55. def printLogLine(self, text):
  56. self.stdscr.addstr(
  57. 0, 0,
  58. text + ' ' * (self.cols - len(text)),
  59. curses.A_STANDOUT)
  60. def doRead(self):
  61. """ Input is ready! """
  62. curses.noecho()
  63. c = self.stdscr.getch() # read a character
  64. if c == curses.KEY_BACKSPACE:
  65. self.searchText = self.searchText[:-1]
  66. elif c == curses.KEY_ENTER or c == 10:
  67. text = self.searchText
  68. self.searchText = ''
  69. self.print_line(">> %s" % text)
  70. try:
  71. if self.callback:
  72. self.callback.on_line(text)
  73. except Exception as e:
  74. self.print_line(str(e))
  75. self.stdscr.refresh()
  76. elif isprint(c):
  77. if len(self.searchText) == self.cols - 2:
  78. return
  79. self.searchText = self.searchText + chr(c)
  80. self.stdscr.addstr(self.rows - 1, 0,
  81. self.searchText + (' ' * (
  82. self.cols - len(self.searchText) - 2)))
  83. self.paintStatus(self.statusText + ' %d' % len(self.searchText))
  84. self.stdscr.move(self.rows - 1, len(self.searchText))
  85. self.stdscr.refresh()
  86. def logPrefix(self):
  87. return "CursesStdIO"
  88. def close(self):
  89. """ clean up """
  90. curses.nocbreak()
  91. self.stdscr.keypad(0)
  92. curses.echo()
  93. curses.endwin()
  94. class Callback(object):
  95. def __init__(self, stdio):
  96. self.stdio = stdio
  97. def on_line(self, text):
  98. self.stdio.print_line(text)
  99. def main(stdscr):
  100. screen = CursesStdIO(stdscr) # create Screen object
  101. callback = Callback(screen)
  102. screen.set_callback(callback)
  103. stdscr.refresh()
  104. reactor.addReader(screen)
  105. reactor.run()
  106. screen.close()
  107. if __name__ == '__main__':
  108. curses.wrapper(main)