To make Fenrir easier to approach for new developer, start code migration to be pep8 compliant.

This commit is contained in:
Storm Dragon
2025-07-01 22:23:50 -04:00
parent 4bcf82178e
commit 7408951152
345 changed files with 8688 additions and 3852 deletions

View File

@ -7,6 +7,7 @@
from fenrirscreenreader.core import debug
from fenrirscreenreader.core.screenDriver import screenDriver
class driver(screenDriver):
def __init__(self):
screenDriver.__init__(self)

View File

@ -4,7 +4,18 @@
# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributers.
import os, struct, sys, pty, tty, termios, shlex, signal, pyte, time, fcntl ,getpass
import os
import struct
import sys
import pty
import tty
import termios
import shlex
import signal
import pyte
import time
import fcntl
import getpass
from select import select
from fenrirscreenreader.core import debug
from fenrirscreenreader.core.eventData import fenrirEventType
@ -17,6 +28,7 @@ class fenrirScreen(pyte.Screen):
kwargs.pop("private", None)
super(fenrirScreen, self).set_margins(*args, **kwargs)
class Terminal:
def __init__(self, columns, lines, p_in):
self.text = ''
@ -26,56 +38,75 @@ class Terminal:
lambda data: p_in.write(data.encode())
self.stream = pyte.ByteStream()
self.stream.attach(self.screen)
def feed(self, data):
self.stream.feed(data)
def updateAttributes(self, initialize = False):
def updateAttributes(self, initialize=False):
buffer = self.screen.buffer
lines = None
if not initialize:
lines = self.screen.dirty
else:
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:
try:
t = self.attributes[y]
except Exception as e:
# Terminal class doesn't have access to env, use fallback logging
print(f'ptyDriver Terminal updateAttributes: Error accessing attributes: {e}')
# Terminal class doesn't have access to env, use fallback
# logging
print(
f'ptyDriver Terminal updateAttributes: Error accessing attributes: {e}')
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:
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):
self.screen.resize(lines, columns)
self.setCursor()
self.updateAttributes(True)
def setCursor(self, x = -1, y = -1):
def setCursor(self, x=-1, y=-1):
xPos = x
yPos = y
if xPos == -1:
xPos = self.screen.cursor.x
if yPos == -1:
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)
def GetScreenContent(self):
cursor = self.screen.cursor
self.text = '\n'.join(self.screen.display)
self.updateAttributes(self.attributes == None)
self.updateAttributes(self.attributes is None)
self.screen.dirty.clear()
return {"cursor": (cursor.x, cursor.y),
'lines': self.screen.lines,
'columns': self.screen.columns,
"text": self.text,
'attributes': self.attributes.copy(),
'screen': 'pty',
'screenUpdateTime': time.time(),
}.copy()
'lines': self.screen.lines,
'columns': self.screen.columns,
"text": self.text,
'attributes': self.attributes.copy(),
'screen': 'pty',
'screenUpdateTime': time.time(),
}.copy()
class driver(screenDriver):
def __init__(self):
@ -85,16 +116,21 @@ class driver(screenDriver):
self.terminal = None
self.p_pid = -1
signal.signal(signal.SIGWINCH, self.handleSigwinch)
def initialize(self, environment):
self.env = environment
self.command = self.env['runtime']['settingsManager'].getSetting('general','shell')
self.shortcutType = self.env['runtime']['inputManager'].getShortcutType()
self.env['runtime']['processManager'].addCustomEventThread(self.terminalEmulation)
self.command = self.env['runtime']['settingsManager'].getSetting(
'general', 'shell')
self.shortcutType = self.env['runtime']['inputManager'].getShortcutType(
)
self.env['runtime']['processManager'].addCustomEventThread(
self.terminalEmulation)
def getCurrScreen(self):
self.env['screen']['oldTTY'] = 'pty'
self.env['screen']['newTTY'] = 'pty'
def injectTextToScreen(self, msgBytes, screen = None):
def injectTextToScreen(self, msgBytes, screen=None):
if not screen:
screen = self.p_out.fileno()
if isinstance(msgBytes, str):
@ -105,8 +141,9 @@ class driver(screenDriver):
self.env['screen']['autoIgnoreScreens'] = []
self.env['general']['prevUser'] = getpass.getuser()
self.env['general']['currUser'] = getpass.getuser()
def readAll(self, fd, timeout = 0.3, interruptFd = None, len = 65536):
msgBytes = b''
def readAll(self, fd, timeout=0.3, interruptFd=None, len=65536):
msgBytes = b''
fdList = []
fdList += [fd]
if interruptFd:
@ -115,7 +152,7 @@ class driver(screenDriver):
while True:
r = screen_utils.hasMoreWhat(fdList, 0.0001)
# nothing more to read
if not fd in r:
if fd not in r:
break
data = os.read(fd, len)
if data == b'':
@ -124,47 +161,57 @@ class driver(screenDriver):
# exit on interrupt available
if interruptFd in r:
break
# 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:
break
return msgBytes
def openTerminal(self, columns, lines, command):
p_pid, master_fd = pty.fork()
if p_pid == 0: # Child.
argv = shlex.split(command)
env = os.environ.copy()
#values are VT100,xterm-256color,linux
try:
# values are VT100,xterm-256color,linux
try:
if env["TERM"] == '':
env["TERM"] = 'linux'
except Exception as e:
# Child process doesn't have access to env, use fallback logging
print(f'ptyDriver spawnTerminal: Error checking TERM environment: {e}')
# Child process doesn't have access to env, use fallback
# logging
print(
f'ptyDriver spawnTerminal: Error checking TERM environment: {e}')
env["TERM"] = 'linux'
os.execvpe(argv[0], argv, env)
# File-like object for I/O with the child process aka command.
p_out = os.fdopen(master_fd, "w+b", 0)
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 = fcntl.ioctl(0, termios.TIOCGWINSZ, s)
fcntl.ioctl(fd, termios.TIOCSWINSZ, s)
lines, columns, _, _ = struct.unpack('hhhh', s)
return lines, columns
def getTerminalSize(self, fd):
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
def handleSigwinch(self, *args):
os.write(self.signalPipe[1], b'w')
def terminalEmulation(self,active , eventQueue):
def terminalEmulation(self, active, eventQueue):
try:
old_attr = termios.tcgetattr(sys.stdin)
tty.setraw(0)
lines, columns = self.getTerminalSize(0)
if self.command == '':
self.command = screen_utils.getShell()
self.terminal, self.p_pid, self.p_out = self.openTerminal(columns, lines, self.command)
self.terminal, self.p_pid, self.p_out = self.openTerminal(
columns, lines, self.command)
lines, columns = self.resizeTerminal(self.p_out)
self.terminal.resize(lines, columns)
fdList = [sys.stdin, self.p_out, self.signalPipe[0]]
@ -183,41 +230,53 @@ class driver(screenDriver):
try:
msgBytes = self.readAll(sys.stdin.fileno(), len=4096)
except (EOFError, OSError):
eventQueue.put({"Type":fenrirEventType.StopMainLoop,"Data":None})
eventQueue.put(
{"Type": fenrirEventType.StopMainLoop, "Data": None})
break
if self.shortcutType == 'KEY':
try:
self.injectTextToScreen(msgBytes)
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('ptyDriver getInputData: Error injecting text to screen: ' + str(e), debug.debugLevel.ERROR)
eventQueue.put({"Type":fenrirEventType.StopMainLoop,"Data":None})
self.env['runtime']['debug'].writeDebugOut(
'ptyDriver getInputData: Error injecting text to screen: ' + str(e),
debug.debugLevel.ERROR)
eventQueue.put(
{"Type": fenrirEventType.StopMainLoop, "Data": None})
break
else:
eventQueue.put({"Type":fenrirEventType.ByteInput,
"Data":msgBytes })
eventQueue.put({"Type": fenrirEventType.ByteInput,
"Data": msgBytes})
# output
if self.p_out in r:
try:
msgBytes = self.readAll(self.p_out.fileno(), interruptFd=sys.stdin.fileno())
msgBytes = self.readAll(
self.p_out.fileno(), interruptFd=sys.stdin.fileno())
except (EOFError, OSError):
eventQueue.put({"Type":fenrirEventType.StopMainLoop,"Data":None})
eventQueue.put(
{"Type": fenrirEventType.StopMainLoop, "Data": None})
break
# feed and send event bevore write, the pyte already has the right state
# so fenrir already can progress bevore os.write what should give some better reaction time
# so fenrir already can progress bevore os.write what
# should give some better reaction time
self.terminal.feed(msgBytes)
eventQueue.put({"Type":fenrirEventType.ScreenUpdate,
"Data":screen_utils.createScreenEventData(self.terminal.GetScreenContent())
})
self.injectTextToScreen(msgBytes, screen=sys.stdout.fileno())
eventQueue.put(
{
"Type": fenrirEventType.ScreenUpdate,
"Data": screen_utils.createScreenEventData(
self.terminal.GetScreenContent())})
self.injectTextToScreen(
msgBytes, screen=sys.stdout.fileno())
except Exception as e: # Process died?
print(e)
eventQueue.put({"Type":fenrirEventType.StopMainLoop,"Data":None})
eventQueue.put(
{"Type": fenrirEventType.StopMainLoop, "Data": None})
finally:
os.kill(self.p_pid, signal.SIGTERM)
self.p_out.close()
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)
def getCurrApplication(self):
pass

View File

@ -3,15 +3,16 @@
# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributers.
#attrib:
#http://rampex.ihep.su/Linux/linux_howto/html/tutorials/mini/Colour-ls-6.html
#0 = black, 1 = blue, 2 = green, 3 = cyan, 4 = red, 5 = purple, 6 = brown/yellow, 7 = white.
#https://github.com/jwilk/vcsapeek/blob/master/linuxvt.py
#blink = 5 if attr & 1 else 0
#bold = 1 if attr & 16 else 0
# attrib:
# http://rampex.ihep.su/Linux/linux_howto/html/tutorials/mini/Colour-ls-6.html
# 0 = black, 1 = blue, 2 = green, 3 = cyan, 4 = red, 5 = purple, 6 = brown/yellow, 7 = white.
# https://github.com/jwilk/vcsapeek/blob/master/linuxvt.py
# blink = 5 if attr & 1 else 0
# bold = 1 if attr & 16 else 0
import subprocess
import glob, os
import glob
import os
import termios
import time
import select
@ -25,46 +26,78 @@ from fenrirscreenreader.core.eventData import fenrirEventType
from fenrirscreenreader.core.screenDriver import screenDriver
from fenrirscreenreader.utils import screen_utils
class driver(screenDriver):
def __init__(self):
screenDriver.__init__(self)
self.ListSessions = None
self.sysBus = None
self.charmap = {}
self.bgColorValues = {0: 'black', 1: 'blue', 2: 'green', 3: 'cyan', 4: 'red', 5: 'magenta', 6: 'brown/yellow', 7: 'white'}
self.fgColorValues = {0: 'black', 1: 'blue', 2: 'green', 3: 'cyan', 4: 'red', 5: 'magenta', 6: 'brown/yellow', 7: 'light gray', 8: 'dark gray', 9: 'light blue', 10: 'light green', 11: 'light cyan', 12: 'light red', 13: 'light magenta', 14: 'light yellow', 15: 'white'}
self.bgColorValues = {
0: 'black',
1: 'blue',
2: 'green',
3: 'cyan',
4: 'red',
5: 'magenta',
6: 'brown/yellow',
7: 'white'}
self.fgColorValues = {
0: 'black',
1: 'blue',
2: 'green',
3: 'cyan',
4: 'red',
5: 'magenta',
6: 'brown/yellow',
7: 'light gray',
8: 'dark gray',
9: 'light blue',
10: 'light green',
11: 'light cyan',
12: 'light red',
13: 'light magenta',
14: 'light yellow',
15: 'white'}
self.hichar = None
try:
# set workaround for paste clipboard -> injectTextToScreen
subprocess.run(['sysctl', 'dev.tty.legacy_tiocsti=1'],
check=False, capture_output=True, timeout=5)
subprocess.run(['sysctl', 'dev.tty.legacy_tiocsti=1'],
check=False, capture_output=True, timeout=5)
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error running fgconsole: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver shutdown: Error running fgconsole: ' + str(e),
debug.debugLevel.ERROR)
def initialize(self, environment):
self.env = environment
self.env['runtime']['attributeManager'].appendDefaultAttributes([
self.fgColorValues[7], # fg
self.bgColorValues[0], # bg
False, # bold
False, # italics
False, # underscore
False, # strikethrough
False, # reverse
False, # blink
'default', # fontsize
'default' # fontfamily
]) #end attribute )
self.env['runtime']['processManager'].addCustomEventThread(self.updateWatchdog, multiprocess=True)
self.fgColorValues[7], # fg
self.bgColorValues[0], # bg
False, # bold
False, # italics
False, # underscore
False, # strikethrough
False, # reverse
False, # blink
'default', # fontsize
'default' # fontfamily
]) # end attribute )
self.env['runtime']['processManager'].addCustomEventThread(
self.updateWatchdog, multiprocess=True)
def getCurrScreen(self):
self.env['screen']['oldTTY'] = self.env['screen']['newTTY']
try:
with open('/sys/devices/virtual/tty/tty0/active','r') as currScreenFile:
with open('/sys/devices/virtual/tty/tty0/active', 'r') as currScreenFile:
self.env['screen']['newTTY'] = str(currScreenFile.read()[3:-1])
except Exception as e:
self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR)
def injectTextToScreen(self, text, screen = None):
self.env['runtime']['debug'].writeDebugOut(
str(e), debug.debugLevel.ERROR)
def injectTextToScreen(self, text, screen=None):
useScreen = "/dev/tty" + self.env['screen']['newTTY']
if screen != None:
if screen is not None:
useScreen = screen
with open(useScreen, 'w') as fd:
for c in text:
@ -75,38 +108,48 @@ class driver(screenDriver):
try:
if not self.sysBus:
self.sysBus = dbus.SystemBus()
obj = self.sysBus.get_object('org.freedesktop.login1', '/org/freedesktop/login1')
obj = self.sysBus.get_object(
'org.freedesktop.login1', '/org/freedesktop/login1')
inf = dbus.Interface(obj, 'org.freedesktop.login1.Manager')
self.ListSessions = inf.get_dbus_method('ListSessions')
sessions = self.ListSessions()
for session in sessions:
obj = self.sysBus.get_object('org.freedesktop.login1', session[4])
obj = self.sysBus.get_object(
'org.freedesktop.login1', session[4])
inf = dbus.Interface(obj, 'org.freedesktop.DBus.Properties')
sessionType = inf.Get('org.freedesktop.login1.Session', 'Type')
screen = str(inf.Get('org.freedesktop.login1.Session', 'VTNr'))
if screen == '':
screen = str(inf.Get('org.freedesktop.login1.Session', 'TTY'))
if screen == '':
screen = str(
inf.Get(
'org.freedesktop.login1.Session',
'TTY'))
screen = screen[screen.upper().find('TTY') + 3:]
if screen == '':
self.env['runtime']['debug'].writeDebugOut('No TTY found for session:' + session[4],debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'No TTY found for session:' + session[4], debug.debugLevel.ERROR)
return
if sessionType.upper() != 'TTY':
self.env['screen']['autoIgnoreScreens'] += [screen]
if screen == self.env['screen']['newTTY'] :
if screen == self.env['screen']['newTTY']:
if self.env['general']['currUser'] != session[2]:
self.env['general']['prevUser'] = self.env['general']['currUser']
self.env['general']['currUser'] = session[2]
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('getSessionInformation: Maybe no LoginD:' + str(e),debug.debugLevel.ERROR)
#self.env['runtime']['debug'].writeDebugOut('getSessionInformation:' + str(self.env['screen']['autoIgnoreScreens']) + ' ' + str(self.env['general']) ,debug.debugLevel.INFO)
self.env['runtime']['debug'].writeDebugOut(
'getSessionInformation: Maybe no LoginD:' + str(e), debug.debugLevel.ERROR)
# self.env['runtime']['debug'].writeDebugOut('getSessionInformation:' + str(self.env['screen']['autoIgnoreScreens']) + ' ' + str(self.env['general']) ,debug.debugLevel.INFO)
def readFile(self, file):
d = b''
file.seek(0)
try:
d = file.read()
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver getScreenText: Error reading file: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver getScreenText: Error reading file: ' + str(e),
debug.debugLevel.ERROR)
file.seek(0)
while True:
# Read from file
@ -117,7 +160,8 @@ class driver(screenDriver):
except Exception as e:
break
return d
def updateWatchdog(self, active , eventQueue):
def updateWatchdog(self, active, eventQueue):
vcsa = {}
vcsu = {}
tty = None
@ -127,29 +171,31 @@ class driver(screenDriver):
vcsaDevices = glob.glob('/dev/vcsa*')
vcsuDevices = None
lastScreenContent = b''
# Open TTY file with proper cleanup
tty = open('/sys/devices/virtual/tty/tty0/active','r')
tty = open('/sys/devices/virtual/tty/tty0/active', 'r')
currScreen = str(tty.read()[3:-1])
oldScreen = currScreen
# Open VCSA devices with proper cleanup tracking
for vcsaDev in vcsaDevices:
index = str(vcsaDev[9:])
vcsa[index] = open(vcsaDev,'rb')
vcsa[index] = open(vcsaDev, 'rb')
if index == currScreen:
lastScreenContent = self.readFile(vcsa[index])
# Open VCSU devices if available
if useVCSU:
vcsuDevices = glob.glob('/dev/vcsu*')
for vcsuDev in vcsuDevices:
index = str(vcsuDev[9:])
vcsu[index] = open(vcsuDev,'rb')
vcsu[index] = open(vcsuDev, 'rb')
self.updateCharMap(currScreen)
watchdog = select.epoll()
watchdog.register(vcsa[currScreen], select.POLLPRI | select.POLLERR)
watchdog.register(
vcsa[currScreen],
select.POLLPRI | select.POLLERR)
watchdog.register(tty, select.POLLPRI | select.POLLERR)
while active.value:
changes = watchdog.poll(1)
@ -157,34 +203,43 @@ class driver(screenDriver):
fileno = change[0]
event = change[1]
if fileno == tty.fileno():
self.env['runtime']['debug'].writeDebugOut('ScreenChange',debug.debugLevel.INFO)
self.env['runtime']['debug'].writeDebugOut(
'ScreenChange', debug.debugLevel.INFO)
tty.seek(0)
currScreen = str(tty.read()[3:-1])
if currScreen != oldScreen:
try:
watchdog.unregister(vcsa[oldScreen])
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error unregistering watchdog: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver updateWatchdog: Error unregistering watchdog: ' + str(e),
debug.debugLevel.ERROR)
try:
watchdog.register(vcsa[currScreen], select.POLLPRI | select.POLLERR)
watchdog.register(
vcsa[currScreen], select.POLLPRI | select.POLLERR)
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error registering watchdog: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver updateWatchdog: Error registering watchdog: ' + str(e),
debug.debugLevel.ERROR)
self.updateCharMap(currScreen)
oldScreen = currScreen
try:
vcsa[currScreen].seek(0)
lastScreenContent = self.readFile(vcsa[currScreen])
lastScreenContent = self.readFile(
vcsa[currScreen])
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error reading screen content: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver updateWatchdog: Error reading screen content: ' + str(e),
debug.debugLevel.ERROR)
vcsuContent = None
if useVCSU:
vcsu[currScreen].seek(0)
vcsuContent = self.readFile(vcsu[currScreen])
eventQueue.put({"Type":fenrirEventType.ScreenChanged,
"Data":self.createScreenEventData(currScreen, lastScreenContent, vcsuContent)
})
eventQueue.put({"Type": fenrirEventType.ScreenChanged, "Data": self.createScreenEventData(
currScreen, lastScreenContent, vcsuContent)})
else:
self.env['runtime']['debug'].writeDebugOut('ScreenUpdate',debug.debugLevel.INFO)
self.env['runtime']['debug'].writeDebugOut(
'ScreenUpdate', debug.debugLevel.INFO)
vcsa[currScreen].seek(0)
time.sleep(0.01)
dirtyContent = self.readFile(vcsa[currScreen])
@ -196,12 +251,12 @@ class driver(screenDriver):
continue
if lastScreenContent == b'':
lastScreenContent = screenContent
if (abs( int(screenContent[2]) - int(lastScreenContent[2])) in [1,2]) and \
(int(screenContent[3]) == int(lastScreenContent[3])):
if (abs(int(screenContent[2]) - int(lastScreenContent[2])) in [1, 2]) and (
int(screenContent[3]) == int(lastScreenContent[3])):
# Skip X Movement
pass
elif (abs( int(screenContent[3]) - int(lastScreenContent[3])) in [1]) and \
(int(screenContent[2]) == int(lastScreenContent[2])):
elif (abs(int(screenContent[3]) - int(lastScreenContent[3])) in [1]) and \
(int(screenContent[2]) == int(lastScreenContent[2])):
# Skip Y Movement
pass
else:
@ -209,8 +264,8 @@ class driver(screenDriver):
while True:
screenContent = dirtyContent
time.sleep(0.02)
#r,_,_ = select.select([vcsa[currScreen]], [], [], 0.07)
#if not vcsa[currScreen] in r:
# r,_,_ = select.select([vcsa[currScreen]], [], [], 0.07)
# if not vcsa[currScreen] in r:
# break
vcsa[currScreen].seek(0)
dirtyContent = self.readFile(vcsa[currScreen])
@ -223,11 +278,11 @@ class driver(screenDriver):
vcsu[currScreen].seek(0)
vcsuContent = self.readFile(vcsu[currScreen])
lastScreenContent = screenContent
eventQueue.put({"Type":fenrirEventType.ScreenUpdate,
"Data":self.createScreenEventData(currScreen, screenContent, vcsuContent)
})
eventQueue.put({"Type": fenrirEventType.ScreenUpdate, "Data": self.createScreenEventData(
currScreen, screenContent, vcsuContent)})
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('VCSA:updateWatchdog:' + str(e),debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'VCSA:updateWatchdog:' + str(e), debug.debugLevel.ERROR)
time.sleep(0.2)
finally:
# Clean up all file handles
@ -235,51 +290,65 @@ class driver(screenDriver):
if watchdog:
watchdog.close()
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver updateWatchdog: Error closing watchdog: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver updateWatchdog: Error closing watchdog: ' + str(e),
debug.debugLevel.ERROR)
try:
if tty:
tty.close()
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error closing TTY: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver shutdown: Error closing TTY: ' + str(e), debug.debugLevel.ERROR)
for handle in vcsa.values():
try:
handle.close()
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error closing VCSA handle: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver shutdown: Error closing VCSA handle: ' + str(e),
debug.debugLevel.ERROR)
for handle in vcsu.values():
try:
handle.close()
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver shutdown: Error closing VCSU handle: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver shutdown: Error closing VCSU handle: ' + str(e),
debug.debugLevel.ERROR)
def createScreenEventData(self, screen, vcsaContent, vcsuContent = None):
def createScreenEventData(self, screen, vcsaContent, vcsuContent=None):
eventData = {
'bytes': vcsaContent,
'lines': int( vcsaContent[0]),
'columns': int( vcsaContent[1]),
'textCursor':
'lines': int(vcsaContent[0]),
'columns': int(vcsaContent[1]),
'textCursor':
{
'x': int( vcsaContent[2]),
'y': int( vcsaContent[3])
},
'x': int(vcsaContent[2]),
'y': int(vcsaContent[3])
},
'screen': screen,
'screenUpdateTime': time.time(),
'text': '',
'attributes': [],
}
try:
eventData['text'], eventData['attributes'] =\
self.autoDecodeVCSA(vcsaContent[4:], eventData['lines'], eventData['columns'])
eventData['text'], eventData['attributes'] = self.autoDecodeVCSA(
vcsaContent[4:], eventData['lines'], eventData['columns'])
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver createScreenEventData: Error decoding VCSA content: ' + str(e), debug.debugLevel.ERROR)
# VCSU seems to give b' ' instead of b'\x00\x00\x00' (tsp), deactivated until its fixed
if vcsuContent != None:
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver createScreenEventData: Error decoding VCSA content: ' + str(e),
debug.debugLevel.ERROR)
# VCSU seems to give b' ' instead of b'\x00\x00\x00' (tsp),
# deactivated until its fixed
if vcsuContent is not None:
try:
vcsuContentAsText = vcsuContent.decode('UTF-32')
eventData['text'] = screen_utils.insertNewlines(vcsuContentAsText, eventData['columns'])
eventData['text'] = screen_utils.insertNewlines(
vcsuContentAsText, eventData['columns'])
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver createScreenEventData: Error decoding VCSU content: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver createScreenEventData: Error decoding VCSU content: ' + str(e),
debug.debugLevel.ERROR)
return eventData.copy()
def updateCharMap(self, screen):
self.charmap = {}
try:
@ -293,18 +362,23 @@ class driver(screenDriver):
line = ''
while True:
try:
unipairs = array("H", [0]*(2*sz))
unimapdesc = array("B", pack("@HP", sz, unipairs.buffer_info()[0]))
unipairs = array("H", [0] * (2 * sz))
unimapdesc = array(
"B", pack(
"@HP", sz, unipairs.buffer_info()[0]))
ioctl(tty.fileno(), GIO_UNIMAP, unimapdesc)
break
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('VCSA:updateCharMap:scaling up sz=' + str(sz) + ' ' + str(e),debug.debugLevel.WARNING)
self.env['runtime']['debug'].writeDebugOut(
'VCSA:updateCharMap:scaling up sz=' + str(sz) + ' ' + str(e),
debug.debugLevel.WARNING)
sz *= 2
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('VCSA:updateCharMap:' + str(e),debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'VCSA:updateCharMap:' + str(e), debug.debugLevel.ERROR)
return
ncodes, = unpack_from("@H", unimapdesc)
utable = unpack_from("@%dH" % (2*ncodes), unipairs)
utable = unpack_from("@%dH" % (2 * ncodes), unipairs)
for u, b in zip(utable[::2], utable[1::2]):
if self.charmap.get(b) is None:
self.charmap[b] = chr(u)
@ -319,26 +393,26 @@ class driver(screenDriver):
blink = 0
bold = 0
ink = 7
paper = 0
paper = 0
for x in range(cols):
data = allData[i: i + 2]
i += 2
if data == b' \x07':
#attr = 7
#ink = 7
#paper = 0
#ch = ' '
# attr = 7
# ink = 7
# paper = 0
# ch = ' '
charAttrib = [
self.fgColorValues[7], # fg
self.bgColorValues[0], # bg
False, # bold
False, # italics
False, # underscore
False, # strikethrough
False, # reverse
False, # blink
'default', # fontsize
'default'] # fontfamily
self.fgColorValues[7], # fg
self.bgColorValues[0], # bg
False, # bold
False, # italics
False, # underscore
False, # strikethrough
False, # reverse
False, # blink
'default', # fontsize
'default'] # fontfamily
lineAttrib.append(charAttrib)
lineText += ' '
continue
@ -351,53 +425,65 @@ class driver(screenDriver):
if sh & self.hichar:
ch |= 0x100
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver autoDecodeVCSA: Error processing character: ' + str(e), debug.debugLevel.ERROR)
ch = None
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver autoDecodeVCSA: Error processing character: ' + str(e),
debug.debugLevel.ERROR)
ch = None
if self.hichar == 0x100:
attr >>= 1
ink = attr & 0x0F
paper = (attr>>4) & 0x0F
if attr & 1:
paper = (attr >> 4) & 0x0F
if attr & 1:
blink = 1
# blink seems to be set always, ignore for now
blink = 0
bold = 0
blink = 0
bold = 0
if attr & 16:
bold = 1
#if (ink != 7) or (paper != 0):
# if (ink != 7) or (paper != 0):
# print(ink,paper)
except Exception as e:
self.env['runtime']['debug'].writeDebugOut('vcsaDriver autoDecodeVCSA: Error processing attributes: ' + str(e), debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
'vcsaDriver autoDecodeVCSA: Error processing attributes: ' + str(e),
debug.debugLevel.ERROR)
try:
lineText += self.charmap[ch]
except KeyError:
lineText += '?'
charAttrib = [
self.fgColorValues[ink],
self.bgColorValues[paper],
bold == 1, # bold
False, # italics
False, # underscore
False, # strikethrough
False, # reverse
blink == 1, # blink
'default', # fontsize
'default'] # fontfamily
self.fgColorValues[ink],
self.bgColorValues[paper],
bold == 1, # bold
False, # italics
False, # underscore
False, # strikethrough
False, # reverse
blink == 1, # blink
'default', # fontsize
'default'] # fontfamily
lineAttrib.append(charAttrib)
allText += lineText
if y + 1 < rows:
allText += '\n'
allAttrib.append(lineAttrib)
return str(allText), allAttrib
def getCurrApplication(self):
apps = []
try:
currScreen = self.env['screen']['newTTY']
apps = subprocess.Popen('ps -t tty' + currScreen + ' -o comm,tty,stat', shell=True, stdout=subprocess.PIPE).stdout.read().decode()[:-1].split('\n')
apps = subprocess.Popen(
'ps -t tty' +
currScreen +
' -o comm,tty,stat',
shell=True,
stdout=subprocess.PIPE).stdout.read().decode()[
:-
1].split('\n')
except Exception as e:
self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
str(e), debug.debugLevel.ERROR)
return
try:
for i in apps:
@ -408,11 +494,12 @@ class driver(screenDriver):
if '+' in i[2]:
if i[0] != '':
if not "GREP" == i[0] and \
not "SH" == i[0] and \
not "PS" == i[0]:
if "TTY"+currScreen in i[1]:
not "SH" == i[0] and \
not "PS" == i[0]:
if "TTY" + currScreen in i[1]:
if self.env['screen']['newApplication'] != i[0]:
self.env['screen']['newApplication'] = i[0]
return
except Exception as e:
self.env['runtime']['debug'].writeDebugOut(str(e),debug.debugLevel.ERROR)
self.env['runtime']['debug'].writeDebugOut(
str(e), debug.debugLevel.ERROR)