improve code

This commit is contained in:
Chrys 2019-08-13 09:41:16 +02:00
parent 52adc37434
commit 8ea6e37537

View File

@ -1,216 +1,215 @@
#!/bin/python #!/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Fenrir TTY screen reader # Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributers. # By Chrys, Storm Dragon, and contributers.
import os, struct, sys, pty, tty, termios, shlex, signal, select, pyte, time, fcntl ,getpass import os, struct, sys, pty, tty, termios, shlex, signal, select, pyte, time, fcntl ,getpass
from fenrirscreenreader.core import debug from fenrirscreenreader.core import debug
from fenrirscreenreader.core.eventData import fenrirEventType from fenrirscreenreader.core.eventData import fenrirEventType
from fenrirscreenreader.core.screenDriver import screenDriver from fenrirscreenreader.core.screenDriver import screenDriver
from fenrirscreenreader.utils import screen_utils from fenrirscreenreader.utils import screen_utils
class fenrirScreen(pyte.HistoryScreen): class fenrirScreen(pyte.HistoryScreen):
def set_margins(self, *args, **kwargs): def set_margins(self, *args, **kwargs):
kwargs.pop("private", None) kwargs.pop("private", None)
return super(fenrirScreen, self).set_margins(*args, **kwargs) return super(fenrirScreen, self).set_margins(*args, **kwargs)
class Terminal: class Terminal:
def __init__(self, columns, lines, p_in): def __init__(self, columns, lines, p_in):
self.text = '' self.text = ''
self.attributes = None self.attributes = None
self.screen = fenrirScreen(columns, lines) self.screen = fenrirScreen(columns, lines)
self.screen.set_mode(pyte.modes.LNM) self.screen.set_mode(pyte.modes.LNM)
self.screen.write_process_input = \ self.screen.write_process_input = \
lambda data: p_in.write(data.encode()) lambda data: p_in.write(data.encode())
self.stream = pyte.ByteStream() self.stream = pyte.ByteStream()
self.stream.attach(self.screen) self.stream.attach(self.screen)
def feed(self, data): def feed(self, data):
self.stream.feed(data) self.stream.feed(data)
def updateAttributes(self, initialize = False): def updateAttributes(self, initialize = False):
buffer = self.screen.buffer buffer = self.screen.buffer
lines = None lines = None
if not initialize: if not initialize:
lines = self.screen.dirty lines = self.screen.dirty
else: else:
lines = range(self.screen.lines) lines = range(self.screen.lines)
self.attributes = [[list(attribute[1:]) + [False, 'default', 'default'] for attribute in line.values()] for line in buffer.values()] self.attributes = [[list(attribute[1:]) + [False, 'default', 'default'] for attribute in line.values()] for line in buffer.values()]
for y in lines: for y in lines:
try: try:
t = self.attributes[y] t = self.attributes[y]
except: except:
self.attributes.append([]) self.attributes.append([])
self.attributes[y] = [list(attribute[1:]) + [False, 'default', 'default'] for attribute in (buffer[y].values())] self.attributes[y] = [list(attribute[1:]) + [False, 'default', 'default'] for attribute in (buffer[y].values())]
if len(self.attributes[y]) < self.screen.columns: if len(self.attributes[y]) < self.screen.columns:
diff = self.screen.columns - len(self.attributes[y]) diff = self.screen.columns - len(self.attributes[y])
self.attributes[y] += [['default', 'default', False, False, False, False, False, False, 'default', 'default']] * diff self.attributes[y] += [['default', 'default', False, False, False, False, False, False, 'default', 'default']] * diff
def resize(self, lines, columns): def resize(self, lines, columns):
self.screen.resize(lines, columns) self.screen.resize(lines, columns)
self.setCursor() self.setCursor()
self.updateAttributes(True) self.updateAttributes(True)
def setCursor(self, x = -1, y = -1): def setCursor(self, x = -1, y = -1):
xPos = x xPos = x
yPos = y yPos = y
if xPos == -1: if xPos == -1:
xPos = self.screen.cursor.x xPos = self.screen.cursor.x
if yPos == -1: if yPos == -1:
yPos = self.screen.cursor.y yPos = self.screen.cursor.y
self.screen.cursor.x = min(self.screen.cursor.x, self.screen.columns - 1) self.screen.cursor.x = min(self.screen.cursor.x, self.screen.columns - 1)
self.screen.cursor.y = min(self.screen.cursor.y, self.screen.lines - 1) self.screen.cursor.y = min(self.screen.cursor.y, self.screen.lines - 1)
def GetScreenContent(self): def GetScreenContent(self):
cursor = self.screen.cursor cursor = self.screen.cursor
self.text = '\n'.join(self.screen.display) self.text = '\n'.join(self.screen.display)
self.updateAttributes(self.attributes == None) self.updateAttributes(self.attributes == None)
self.screen.dirty.clear() self.screen.dirty.clear()
return {"cursor": (cursor.x, cursor.y), return {"cursor": (cursor.x, cursor.y),
'lines': self.screen.lines, 'lines': self.screen.lines,
'columns': self.screen.columns, 'columns': self.screen.columns,
"text": self.text, "text": self.text,
'attributes': self.attributes.copy(), 'attributes': self.attributes.copy(),
'screen': 'pty', 'screen': 'pty',
'screenUpdateTime': time.time(), 'screenUpdateTime': time.time(),
}.copy() }.copy()
class driver(screenDriver): class driver(screenDriver):
def __init__(self): def __init__(self):
screenDriver.__init__(self) screenDriver.__init__(self)
self.signalPipe = os.pipe() self.signalPipe = os.pipe()
self.p_out = None self.p_out = None
signal.signal(signal.SIGWINCH, self.handleSigwinch) signal.signal(signal.SIGWINCH, self.handleSigwinch)
def initialize(self, environment): def initialize(self, environment):
self.env = environment self.env = environment
self.command = self.env['runtime']['settingsManager'].getSetting('general','shell') self.command = self.env['runtime']['settingsManager'].getSetting('general','shell')
self.shortcutType = self.env['runtime']['inputManager'].getShortcutType() self.shortcutType = self.env['runtime']['inputManager'].getShortcutType()
self.env['runtime']['processManager'].addCustomEventThread(self.terminalEmulation) self.env['runtime']['processManager'].addCustomEventThread(self.terminalEmulation)
def getCurrScreen(self): def getCurrScreen(self):
self.env['screen']['oldTTY'] = 'pty' self.env['screen']['oldTTY'] = 'pty'
self.env['screen']['newTTY'] = 'pty' self.env['screen']['newTTY'] = 'pty'
def injectTextToScreen(self, msgBytes, screen = None): def injectTextToScreen(self, msgBytes, screen = None):
if not screen: if not screen:
screen = self.p_out.fileno() screen = self.p_out.fileno()
if isinstance(msgBytes, str): if isinstance(msgBytes, str):
msgBytes = bytes(msgBytes, 'UTF-8') msgBytes = bytes(msgBytes, 'UTF-8')
os.write(screen, msgBytes) os.write(screen, msgBytes)
def getSessionInformation(self): def getSessionInformation(self):
self.env['screen']['autoIgnoreScreens'] = [] self.env['screen']['autoIgnoreScreens'] = []
self.env['general']['prevUser'] = getpass.getuser() self.env['general']['prevUser'] = getpass.getuser()
self.env['general']['currUser'] = getpass.getuser() self.env['general']['currUser'] = getpass.getuser()
def readAll(self, fd, timeout = 9999999, interruptFd = None, len = 2048): def readAll(self, fd, timeout = 9999999, interruptFd = None, len = 2048):
bytes = b'' bytes = b''
fdList = [] fdList = []
fdList += [fd] fdList += [fd]
if interruptFd: if interruptFd:
fdList += [interruptFd] fdList += [interruptFd]
starttime = time.time() starttime = time.time()
while True: while True:
# respect timeout but wait a little bit of time to see if something more is here # respect timeout but wait a little bit of time to see if something more is here
if (time.time() - starttime) >= timeout: if (time.time() - starttime) >= timeout:
break break
r = screen_utils.hasMoreWhat(fdList,0) r = screen_utils.hasMoreWhat(fdList,0)
hasmore = fd in r hasmore = fd in r
if not hasmore: if not hasmore:
break break
# exit on interrupt available # exit on interrupt available
if interruptFd in r: if interruptFd in r:
break break
data = os.read(fd, len) data = os.read(fd, len)
if data == b'': if data == b'':
raise EOFError raise EOFError
bytes += data bytes += data
return bytes return bytes
def openTerminal(self, columns, lines, command): def openTerminal(self, columns, lines, command):
p_pid, master_fd = pty.fork() p_pid, master_fd = pty.fork()
if p_pid == 0: # Child. if p_pid == 0: # Child.
argv = shlex.split(command) argv = shlex.split(command)
env = os.environ.copy() env = os.environ.copy()
#values are VT100,xterm-256color,linux #values are VT100,xterm-256color,linux
try: try:
eterm = env["TERM"] if env["TERM"] == '':
if eterm == '': env["TERM"] = 'linux'
env["TERM"] = 'linux' except:
except: env["TERM"] = 'linux'
env["TERM"] = 'linux' os.execvpe(argv[0], argv, env)
os.execvpe(argv[0], argv, env) # File-like object for I/O with the child process aka command.
# File-like object for I/O with the child process aka command. p_out = os.fdopen(master_fd, "w+b", 0)
p_out = os.fdopen(master_fd, "w+b", 0) return Terminal(columns, lines, p_out), p_pid, p_out
return Terminal(columns, lines, p_out), p_pid, p_out def resizeTerminal(self,fd):
def resizeTerminal(self,fd): s = struct.pack('HHHH', 0, 0, 0, 0)
s = struct.pack('HHHH', 0, 0, 0, 0) s = fcntl.ioctl(0, termios.TIOCGWINSZ, s)
s = fcntl.ioctl(0, termios.TIOCGWINSZ, s) fcntl.ioctl(fd, termios.TIOCSWINSZ, s)
fcntl.ioctl(fd, termios.TIOCSWINSZ, s) lines, columns, _, _ = struct.unpack('hhhh', s)
lines, columns, _, _ = struct.unpack('hhhh', s) return lines, columns
return lines, columns def getTerminalSize(self, fd):
def getTerminalSize(self, fd): s = struct.pack('HHHH', 0, 0, 0, 0)
s = struct.pack('HHHH', 0, 0, 0, 0) lines, columns, _, _ = struct.unpack('HHHH', fcntl.ioctl(fd, termios.TIOCGWINSZ, s))
lines, columns, _, _ = struct.unpack('HHHH', fcntl.ioctl(fd, termios.TIOCGWINSZ, s)) return lines, columns
return lines, columns def handleSigwinch(self, *args):
def handleSigwinch(self, *args): os.write(self.signalPipe[1], b'w')
os.write(self.signalPipe[1], b'w') def terminalEmulation(self,active , eventQueue):
def terminalEmulation(self,active , eventQueue): try:
try: old_attr = termios.tcgetattr(sys.stdin)
old_attr = termios.tcgetattr(sys.stdin) tty.setraw(0)
tty.setraw(0) lines, columns = self.getTerminalSize(0)
lines, columns = self.getTerminalSize(0) if self.command == '':
if self.command == '': self.command = screen_utils.getShell()
self.command = screen_utils.getShell() terminal, p_pid, self.p_out = self.openTerminal(columns, lines, self.command)
terminal, p_pid, self.p_out = self.openTerminal(columns, lines, self.command) lines, columns = self.resizeTerminal(self.p_out)
lines, columns = self.resizeTerminal(self.p_out) terminal.resize(lines, columns)
terminal.resize(lines, columns) fdList = [sys.stdin, self.p_out, self.signalPipe[0]]
fdList = [sys.stdin, self.p_out, self.signalPipe[0]] while active.value:
while active.value: r, _, _ = select.select(fdList, [], [], 1)
r, _, _ = select.select(fdList, [], [], 1) # none
# none if r == []:
if r == []: continue
continue # signals
# signals if self.signalPipe[0] in r:
if self.signalPipe[0] in r: os.read(self.signalPipe[0], 1)
os.read(self.signalPipe[0], 1) lines, columns = self.resizeTerminal(self.p_out)
lines, columns = self.resizeTerminal(self.p_out) terminal.resize(lines, columns)
terminal.resize(lines, columns) # input
# input if sys.stdin in r:
if sys.stdin in r: try:
try: msgBytes = self.readAll(sys.stdin.fileno())
msgBytes = self.readAll(sys.stdin.fileno()) except (EOFError, OSError):
except (EOFError, OSError): active.value = False
active.value = False break
break if self.shortcutType == 'KEY':
if self.shortcutType == 'KEY': try:
try: self.injectTextToScreen(msgBytes)
self.injectTextToScreen(msgBytes) except:
except: active.value = False
active.value = False break
break else:
else: eventQueue.put({"Type":fenrirEventType.ByteInput,
eventQueue.put({"Type":fenrirEventType.ByteInput, "Data":msgBytes })
"Data":msgBytes }) # output
# output if self.p_out in r:
if self.p_out in r: try:
try: msgBytes = self.readAll(self.p_out.fileno(), timeout=0.001, interruptFd=sys.stdin)
msgBytes = self.readAll(self.p_out.fileno(), timeout=0.001, interruptFd=sys.stdin) except (EOFError, OSError):
except (EOFError, OSError): active.value = False
active.value = False break
break terminal.feed(msgBytes)
terminal.feed(msgBytes) os.write(sys.stdout.fileno(), msgBytes)
os.write(sys.stdout.fileno(), msgBytes) eventQueue.put({"Type":fenrirEventType.ScreenUpdate,
eventQueue.put({"Type":fenrirEventType.ScreenUpdate, "Data":screen_utils.createScreenEventData(terminal.GetScreenContent())
"Data":screen_utils.createScreenEventData(terminal.GetScreenContent()) })
}) except Exception as e: # Process died?
except Exception as e: # Process died? print(e)
print(e) active.value = False
active.value = False finally:
finally: os.kill(p_pid, signal.SIGTERM)
os.kill(p_pid, signal.SIGTERM) self.p_out.close()
self.p_out.close() termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_attr)
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_attr) eventQueue.put({"Type":fenrirEventType.StopMainLoop,"Data":None})
eventQueue.put({"Type":fenrirEventType.StopMainLoop,"Data":None}) sys.exit(0)
sys.exit(0)
def getCurrApplication(self):
def getCurrApplication(self): pass
pass