2016-01-07 07:26:29 +03:00
|
|
|
# Copyright 2014-2016 OpenMarket Ltd
|
2014-08-13 06:32:18 +04:00
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
2014-08-12 22:36:36 +04:00
|
|
|
import curses
|
|
|
|
import curses.wrapper
|
|
|
|
from curses.ascii import isprint
|
|
|
|
|
|
|
|
from twisted.internet import reactor
|
|
|
|
|
|
|
|
|
2019-06-20 12:32:02 +03:00
|
|
|
class CursesStdIO:
|
2014-08-12 22:36:36 +04:00
|
|
|
def __init__(self, stdscr, callback=None):
|
|
|
|
self.statusText = "Synapse test app -"
|
2019-06-20 12:32:02 +03:00
|
|
|
self.searchText = ""
|
2014-08-12 22:36:36 +04:00
|
|
|
self.stdscr = stdscr
|
|
|
|
|
2019-06-20 12:32:02 +03:00
|
|
|
self.logLine = ""
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
self.callback = callback
|
|
|
|
|
|
|
|
self._setup()
|
|
|
|
|
|
|
|
def _setup(self):
|
|
|
|
self.stdscr.nodelay(1) # Make non blocking
|
|
|
|
|
|
|
|
self.rows, self.cols = self.stdscr.getmaxyx()
|
|
|
|
self.lines = []
|
|
|
|
|
|
|
|
curses.use_default_colors()
|
|
|
|
|
|
|
|
self.paintStatus(self.statusText)
|
|
|
|
self.stdscr.refresh()
|
|
|
|
|
|
|
|
def set_callback(self, callback):
|
|
|
|
self.callback = callback
|
|
|
|
|
|
|
|
def fileno(self):
|
2021-06-17 17:20:06 +03:00
|
|
|
"""We want to select on FD 0"""
|
2014-08-12 22:36:36 +04:00
|
|
|
return 0
|
|
|
|
|
|
|
|
def connectionLost(self, reason):
|
|
|
|
self.close()
|
|
|
|
|
|
|
|
def print_line(self, text):
|
2021-06-17 17:20:06 +03:00
|
|
|
"""add a line to the internal list of lines"""
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
self.lines.append(text)
|
|
|
|
self.redraw()
|
|
|
|
|
|
|
|
def print_log(self, text):
|
|
|
|
self.logLine = text
|
|
|
|
self.redraw()
|
|
|
|
|
|
|
|
def redraw(self):
|
2021-02-17 01:32:34 +03:00
|
|
|
"""method for redisplaying lines based on internal list of lines"""
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
self.stdscr.clear()
|
|
|
|
self.paintStatus(self.statusText)
|
|
|
|
i = 0
|
|
|
|
index = len(self.lines) - 1
|
|
|
|
while i < (self.rows - 3) and index >= 0:
|
2019-06-20 12:32:02 +03:00
|
|
|
self.stdscr.addstr(self.rows - 3 - i, 0, self.lines[index], curses.A_NORMAL)
|
2014-08-12 22:36:36 +04:00
|
|
|
i = i + 1
|
|
|
|
index = index - 1
|
|
|
|
|
|
|
|
self.printLogLine(self.logLine)
|
|
|
|
|
|
|
|
self.stdscr.refresh()
|
|
|
|
|
|
|
|
def paintStatus(self, text):
|
|
|
|
if len(text) > self.cols:
|
|
|
|
raise RuntimeError("TextTooLongError")
|
|
|
|
|
|
|
|
self.stdscr.addstr(
|
2019-06-20 12:32:02 +03:00
|
|
|
self.rows - 2, 0, text + " " * (self.cols - len(text)), curses.A_STANDOUT
|
|
|
|
)
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
def printLogLine(self, text):
|
|
|
|
self.stdscr.addstr(
|
2019-06-20 12:32:02 +03:00
|
|
|
0, 0, text + " " * (self.cols - len(text)), curses.A_STANDOUT
|
|
|
|
)
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
def doRead(self):
|
2021-06-17 17:20:06 +03:00
|
|
|
"""Input is ready!"""
|
2014-08-12 22:36:36 +04:00
|
|
|
curses.noecho()
|
|
|
|
c = self.stdscr.getch() # read a character
|
|
|
|
|
|
|
|
if c == curses.KEY_BACKSPACE:
|
|
|
|
self.searchText = self.searchText[:-1]
|
|
|
|
|
|
|
|
elif c == curses.KEY_ENTER or c == 10:
|
|
|
|
text = self.searchText
|
2019-06-20 12:32:02 +03:00
|
|
|
self.searchText = ""
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
self.print_line(">> %s" % text)
|
|
|
|
|
|
|
|
try:
|
|
|
|
if self.callback:
|
|
|
|
self.callback.on_line(text)
|
|
|
|
except Exception as e:
|
|
|
|
self.print_line(str(e))
|
|
|
|
|
|
|
|
self.stdscr.refresh()
|
|
|
|
|
|
|
|
elif isprint(c):
|
|
|
|
if len(self.searchText) == self.cols - 2:
|
|
|
|
return
|
|
|
|
self.searchText = self.searchText + chr(c)
|
|
|
|
|
2019-06-20 12:32:02 +03:00
|
|
|
self.stdscr.addstr(
|
|
|
|
self.rows - 1,
|
|
|
|
0,
|
|
|
|
self.searchText + (" " * (self.cols - len(self.searchText) - 2)),
|
|
|
|
)
|
2014-08-12 22:36:36 +04:00
|
|
|
|
2019-06-20 12:32:02 +03:00
|
|
|
self.paintStatus(self.statusText + " %d" % len(self.searchText))
|
2014-08-12 22:36:36 +04:00
|
|
|
self.stdscr.move(self.rows - 1, len(self.searchText))
|
|
|
|
self.stdscr.refresh()
|
|
|
|
|
|
|
|
def logPrefix(self):
|
|
|
|
return "CursesStdIO"
|
|
|
|
|
|
|
|
def close(self):
|
2021-06-17 17:20:06 +03:00
|
|
|
"""clean up"""
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
curses.nocbreak()
|
|
|
|
self.stdscr.keypad(0)
|
|
|
|
curses.echo()
|
|
|
|
curses.endwin()
|
|
|
|
|
|
|
|
|
2020-09-04 13:54:56 +03:00
|
|
|
class Callback:
|
2014-08-12 22:36:36 +04:00
|
|
|
def __init__(self, stdio):
|
|
|
|
self.stdio = stdio
|
|
|
|
|
|
|
|
def on_line(self, text):
|
|
|
|
self.stdio.print_line(text)
|
|
|
|
|
|
|
|
|
|
|
|
def main(stdscr):
|
2019-06-20 12:32:02 +03:00
|
|
|
screen = CursesStdIO(stdscr) # create Screen object
|
2014-08-12 22:36:36 +04:00
|
|
|
|
|
|
|
callback = Callback(screen)
|
|
|
|
|
|
|
|
screen.set_callback(callback)
|
|
|
|
|
|
|
|
stdscr.refresh()
|
|
|
|
reactor.addReader(screen)
|
|
|
|
reactor.run()
|
|
|
|
screen.close()
|
|
|
|
|
|
|
|
|
2019-06-20 12:32:02 +03:00
|
|
|
if __name__ == "__main__":
|
2014-08-12 22:36:36 +04:00
|
|
|
curses.wrapper(main)
|