diff --git a/src/fenrir-package/__init__.py b/src/fenrir-package/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/src/fenrir-package/braille/__init__.py b/src/fenrir-package/braille/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/src/fenrir-package/braille/connector.py b/src/fenrir-package/braille/connector.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fenrir-package/commands/__init__.py b/src/fenrir-package/commands/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/src/fenrir-package/commands/next_line.py b/src/fenrir-package/commands/next_line.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fenrir-package/commands/prev_line.py b/src/fenrir-package/commands/prev_line.py new file mode 100644 index 00000000..e69de29b diff --git a/src/fenrir-package/core/commandManager.py b/src/fenrir-package/core/commandManager.py new file mode 100644 index 00000000..5ceb5faa --- /dev/null +++ b/src/fenrir-package/core/commandManager.py @@ -0,0 +1 @@ +#!/bin/python diff --git a/src/fenrir-package/core/environment.py b/src/fenrir-package/core/environment.py new file mode 100644 index 00000000..5862f05d --- /dev/null +++ b/src/fenrir-package/core/environment.py @@ -0,0 +1,25 @@ +#!/bin/python + +runtime = { +'running':True, +'columns': 0, +'lines': 0, +'screenDriver': '/dev/vcsa', +'delta': '', +'oldCursor':{'x':0,'y':0}, +'oldContentBytes': b'', +'oldContentText': '', +'oldContentAttrib': b'', +'newCursor':{'x':0,'y':0}, +'newContentBytes': b'', +'newContentText': '', +'newContentAttrib': b'', +'oldTTY':'0', +'newTTY':'0', +'speechDriverString':'', +'speechDriver': None, +'screenDriverString': '', +'screenDriver': None, +'soundDriverString': '', +'soundDriver': None, +} diff --git a/src/fenrir-package/core/inputManager.py b/src/fenrir-package/core/inputManager.py new file mode 100644 index 00000000..0ba0ee13 --- /dev/null +++ b/src/fenrir-package/core/inputManager.py @@ -0,0 +1,9 @@ +#!/bin/python + +class inputManager(): + def __init__(self): + pass + + def getCommandQueue(self, runtime): + return[] + diff --git a/src/fenrir-package/core/settingsManager.py b/src/fenrir-package/core/settingsManager.py new file mode 100644 index 00000000..5ceb5faa --- /dev/null +++ b/src/fenrir-package/core/settingsManager.py @@ -0,0 +1 @@ +#!/bin/python diff --git a/src/fenrir-package/fenrir.py b/src/fenrir-package/fenrir.py new file mode 100755 index 00000000..44e9d2dc --- /dev/null +++ b/src/fenrir-package/fenrir.py @@ -0,0 +1,31 @@ +#!/bin/python + +# Fenrir TTY screen reader +# By Chrys, Storm Dragon, and contributers. + +import os, sys + +if not os.getcwd() in sys.path: + sys.path.append(os.getcwd()) + + +from core import environment +from utils import debug +from speech import espeak as es +from speech import speechd as sd +from screen import linux as lx + +class fenrir(): + def __init__(self): + self.runtime = environment.runtime + self.runtime['speechDriverString'] = 'speechd' + self.runtime['speechDriver'] = sd.speech() + self.runtime['screenDriverString'] = 'linux' + self.runtime['screenDriver'] = lx.screenManager() + + def proceed(self): + while(self.runtime['running']): + self.runtime = self.runtime['screenDriver'].analyzeScreen(self.runtime) + +app = fenrir() +app.proceed() diff --git a/src/fenrir-package/screen/__init__.py b/src/fenrir-package/screen/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/src/fenrir-package/screen/linux.py b/src/fenrir-package/screen/linux.py new file mode 100644 index 00000000..7a55596a --- /dev/null +++ b/src/fenrir-package/screen/linux.py @@ -0,0 +1,58 @@ +#!/bin/python + +import difflib +import textwrap +import time + +#import fenrir.utils.debug +class screenManager(): + def __init__(self, device='/dev/vcsa'): + self.vcsaDevice = device + + def analyzeScreen(self, runtime): + # read screen + currTTY = open('/sys/devices/virtual/tty/tty0/active','r') + runtime['newTTY'] = currTTY.read()[3:-1] + currTTY.close() + + try: + vcsa = open(self.vcsaDevice + runtime['newTTY'] ,'rb',0) + runtime['newContentBytes'] = vcsa.read() + vcsa.close() + except: + return runtime + + # get metadata like cursor or screensize + runtime['lines'] = int( runtime['newContentBytes'][0]) + runtime['columns'] = int( runtime['newContentBytes'][1]) + runtime['newCursor']['x'] = int( runtime['newContentBytes'][2]) + runtime['newCursor']['y'] = int( runtime['newContentBytes'][3]) + + # analyze content + runtime['newContentText'] = str(runtime['newContentBytes'][4:][::2].decode('cp1252').encode('utf-8'))[2:] + runtime['newContentAttrib'] = runtime['newContentBytes'][5:][::2] + runtime['newContentText'] = '\n'.join(textwrap.wrap(runtime['newContentText'], runtime['columns']))[:-2] + + if runtime['newTTY'] != runtime['oldTTY']: + runtime['oldContentBytes'] = b'' + runtime['oldContentAttrib'] = b'' + runtime['oldContentText'] = '' + runtime['oldCursor']['x'] = 0 + runtime['oldCursor']['y'] = 0 + + # changes on the screen + if runtime['oldContentBytes'] != runtime['newContentBytes']: + if ((len(runtime['delta']) < 4) or runtime['oldTTY'] != runtime['newTTY']): + runtime['speechDriver'].cancel() + diff = difflib.ndiff(runtime['oldContentText'], runtime['newContentText']) + runtime['delta'] = ''.join(x[2:] for x in diff if x.startswith('+ ')) + runtime['speechDriver'].speak(runtime['delta']) + + # set new "old" values + runtime['oldContentBytes'] = runtime['newContentBytes'] + runtime['oldContentText'] = runtime['newContentText'] + runtime['oldContentTextAttrib'] = runtime['newContentAttrib'] + runtime['oldCursor']['x'] = runtime['newCursor']['x'] + runtime['oldCursor']['y'] = runtime['newCursor']['y'] + runtime['oldTTY'] = runtime['newTTY'] + return runtime diff --git a/src/fenrir-package/sound/__init__.py b/src/fenrir-package/sound/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/src/fenrir-package/sound/sox.py b/src/fenrir-package/sound/sox.py new file mode 100644 index 00000000..5ceb5faa --- /dev/null +++ b/src/fenrir-package/sound/sox.py @@ -0,0 +1 @@ +#!/bin/python diff --git a/src/fenrir-package/speech/Readme.md b/src/fenrir-package/speech/Readme.md new file mode 100644 index 00000000..de077fd4 --- /dev/null +++ b/src/fenrir-package/speech/Readme.md @@ -0,0 +1,3 @@ +espeak = espeak driver +speechd = speech-dispatcher driver +generic = generic driver via /bin/say diff --git a/src/fenrir-package/speech/__init__.py b/src/fenrir-package/speech/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/src/fenrir-package/speech/espeak.py b/src/fenrir-package/speech/espeak.py new file mode 100644 index 00000000..0ec32693 --- /dev/null +++ b/src/fenrir-package/speech/espeak.py @@ -0,0 +1,54 @@ +#!/usr/bin/python + +# Espeak driver + +class speech(): + def __init__(self, ): + self._es = None + self._isInitialized = False + try: + from espeak import espeak + self._es = espeak + self._isInitialized = True + except: + self._initialized = False + + + def speak(self,text, queueable=True): + if not self._isInitialized: + return False + if queueable == False: self.cancel() + self._es.synth(text) + return True + + def cancel(self): + if not self._isInitialized: + return False + self._es.cancel() + return True + + def clear_buffer(self): + if not self._isInitialized: + return False + return True + + def setVoice(self, voice): + if not self._isInitialized: + return False + return _es.set_voice('de') + + def setPitch(self, pitch): + if not self._isInitialized: + return False + return _es.set_parameter(espeak.Parameter.Pitch, pitch) + + def setSpeed(self, speed): + if not self._isInitialized: + return False + return _es.set_parameter(espeak.Parameter.Rate, speed) + + def setModule(self, module): + pass + + def shutdown(self): + pass diff --git a/src/fenrir-package/speech/generic.py b/src/fenrir-package/speech/generic.py new file mode 100644 index 00000000..44b65ad0 --- /dev/null +++ b/src/fenrir-package/speech/generic.py @@ -0,0 +1,20 @@ +#!/usr/bin/python + +# Generic speech driver + +class speech(): + def __init__(self, ): + self.gn = None + self.isInitialized = False +# try: + + + def speak(self,text, queueable=True): + if queueable == False: self.stop() + self.gn.synth(text) + + def stop(self): + self.gn.cancel() + + def clear_buffer(self): + pass diff --git a/src/fenrir-package/speech/speechd.py b/src/fenrir-package/speech/speechd.py new file mode 100644 index 00000000..ddce7504 --- /dev/null +++ b/src/fenrir-package/speech/speechd.py @@ -0,0 +1,74 @@ +#!/usr/bin/python + +# speech-dispatcher driver + +class speech(): + def __init__(self ): + self._sd = None + self._isInitialized = False + try: + import speechd + self._sd = speechd.SSIPClient('fenrir') + self._isInitialized = True + except: + self._initialized = False + + + def speak(self,text, queueable=True): + if not self._isInitialized: + return False + if queueable == False: self.cancel() + self._sd.speak(text) + return True + + def cancel(self): + if not self._isInitialized: + return False + self._sd.cancel() + return True + + def clear_buffer(self): + if not self._isInitialized: + return False + return True + + def setVoice(self, voice): + if not self._isInitialized: + return False + try: + self._sd.set_voice(voice) + return True + except: + return False + + def setPitch(self, pitch): + if not self._isInitialized: + return False + try: + self._sd.set_pitch(pitch) + return True + except: + return False + + def setSpeed(self, speed): + if not self._isInitialized: + return False + try: + self._sd.set_rate(speed) + return True + except: + return False + + def setModule(self, module): + if not self._isInitialized: + return False + try: + self._sd.set_output_module(module) + return True + except: + return False + + + def shutdown(self): + self.cancel() + self._sd.close() diff --git a/src/fenrir-package/utils/__init__.py b/src/fenrir-package/utils/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/src/fenrir-package/utils/configure_pulse.sh b/src/fenrir-package/utils/configure_pulse.sh new file mode 100755 index 00000000..8fc62fa1 --- /dev/null +++ b/src/fenrir-package/utils/configure_pulse.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# This script configures pulse to work both in the graphical invironment and in the console with root apps. + +# Make sure we aren't started as root: +if [[ $(whoami) = "root" ]]; then + echo "Please don't start this script with root priveleges, the script will call sudo when needed." + exit 1 +fi + +# Get the current user's XDG_HOME +xdgPath="${XDG_CONFIG_HOME:-$HOME/.config}" + +# Warn user if we are going to overwrite an existing default.pa +if [ -f "$xdgPath/pulse/default.pa" ]; then + read -p "This will replace the current file located at $xdgPath/pulse/default.pa, press enter to continue or control+c to abort. " continue +fi +echo '.include /etc/pulse/default.pa +load-module module-native-protocol-unix auth-anonymous=1 socket=/tmp/pulse.sock' > $xdgPath/pulse/default.pa + +# This section does the root part: +read -p "This next sections requires root priveleges.. If you don't have access to sudo, please control c to abort this script, if you can use sudo, press enter to continue. " continue + + +# Get root's 's XDG_HOME +xdgPath="$(sudo bash -c '${XDG_CONFIG_HOME:-$HOME/.config}')" + +# Warn user if we are going to overwrite an existing default.pa +if [ -f "$xdgPath/pulse/default.pa" ]; then + read -p "This will replace the current file located at $xdgPath/pulse/default.pa, press enter to continue or control+c to abort. " continue +fi + +sudo cat << EOF > "$xdgPath/pulse/client.conf" +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see . + +## Configuration file for PulseAudio clients. See pulse-client.conf(5) for +## more information. Default values are commented out. Use either ; or # for +## commenting. + +; default-sink = +; default-source = +default-server = unix:/tmp/pulse.sock +; default-dbus-server = + +autospawn = no +; autospawn = yes +; daemon-binary = /usr/bin/pulseaudio +; extra-arguments = --log-target=syslog + +; cookie-file = + +; enable-shm = yes +; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB + +; auto-connect-localhost = no +; auto-connect-display = no +EOF + +# If there were no errors tell user to restart, else warn them errors happened. +if [ $? -eq 0 ]; then +echo "Configuration created succeswsfully, restart pulse, or your system, for changes to take affect." +else +echo "Errors were encountered while writing the configuration. please correct them manually." +fi +exit 0 diff --git a/src/fenrir-package/utils/debug.py b/src/fenrir-package/utils/debug.py new file mode 100644 index 00000000..116e37e6 --- /dev/null +++ b/src/fenrir-package/utils/debug.py @@ -0,0 +1,49 @@ +#!/usr/bin/python + +# Debugger module for the Fenrir screen reader. +ERROR = 0 +WARNING = 1 +INFO = 2 + +class debug(): + def __init__(self, fileName='/var/log/fenrir.log', level = ERROR): + self._level = level + self._fileName= fileName + self._file = open(self._fileName,'w') + self._fileOpened = True + + def openDebugFile(self, fileName = ''): + if fileName != '': + self._fileName = fileName + if self._fileName != '': + self.file = open(self._fileName,'w') + self._fileOpened = True + + def writeLog(self, text, level = ERROR): + if not self._fileOpened: + return False + if self._level < level: + return False + self._file.write(text + '\n') + return True + + def closeDebugFile(self): + if not self._fileOpened: + return False + self._file.close() + self._fileOpened = False + return True + + def getDebugLevel(self): + return self._level + + def setDebugLevel(self, level): + self._level = level + + def getDebugFile(self): + return self._fileName + + def setDebugFile(self, fileName): + self._fileName = fileName + +