Code cleanups, fixes to systemd files, url corrections.
This commit is contained in:
@@ -4,7 +4,7 @@ Wants=systemd-udev-settle.service
|
|||||||
After=systemd-udev-settle.service getty.target
|
After=systemd-udev-settle.service getty.target
|
||||||
[Service]
|
[Service]
|
||||||
Type=forking
|
Type=forking
|
||||||
PIDFile=/var/run/fenrir.pid
|
PIDFile=/run/fenrir.pid
|
||||||
ExecStart=/usr/bin/fenrir
|
ExecStart=/usr/bin/fenrir
|
||||||
ExecReload=/usr/bin/kill -HUP $MAINPID
|
ExecReload=/usr/bin/kill -HUP $MAINPID
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Wants=systemd-udev-settle.service
|
|||||||
After=systemd-udev-settle.service sound.target
|
After=systemd-udev-settle.service sound.target
|
||||||
[Service]
|
[Service]
|
||||||
Type=forking
|
Type=forking
|
||||||
PIDFile=/var/run/fenrir.pid
|
PIDFile=/run/fenrir.pid
|
||||||
ExecStart=/usr/local/bin/fenrir
|
ExecStart=/usr/local/bin/fenrir
|
||||||
ExecReload=/usr/bin/kill -HUP $MAINPID
|
ExecReload=/usr/bin/kill -HUP $MAINPID
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|||||||
4
bugs
4
bugs
@@ -1,5 +1,5 @@
|
|||||||
Please report Bugs and feature requests to:
|
Please report bugs and feature requests to:
|
||||||
https://github.com/chrys87/fenrir/issues
|
https://git.stormux.org/storm/fenrir/issues
|
||||||
|
|
||||||
For bugs, please provide a debug file that shows the issue.
|
For bugs, please provide a debug file that shows the issue.
|
||||||
How to create a debug file:
|
How to create a debug file:
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ configurable and easy to customize and extend.
|
|||||||
=== Credit and intended audience
|
=== Credit and intended audience
|
||||||
|
|
||||||
This document is just a customization for Slint of the genuine
|
This document is just a customization for Slint of the genuine
|
||||||
https://github.com/chrys87/fenrir/blob/master/docu/user.txt[Fenrir User
|
https://git.stormux.org/storm/fenrir/src/branch/master/docs/user.txt[Fenrir User
|
||||||
Manual] motly written by Chrys, main developer of Fenrir.
|
Manual] motly written by Chrys, main developer of Fenrir.
|
||||||
|
|
||||||
It has been adapted to its intended audience: end users of Fenrir on
|
It has been adapted to its intended audience: end users of Fenrir on
|
||||||
Slint where it is already installed, thus concentrates on its setting
|
Slint where it is already installed, thus concentrates on its setting
|
||||||
and usage. You will find more information about its features,
|
and usage. You will find more information about its features,
|
||||||
installation and how customize and troubleshoot it and contribute to its
|
installation and how customize and troubleshoot it and contribute to its
|
||||||
development on https://github.com/chrys87/fenrir[the Fenrir Git
|
development on https://git.stormux.org/storm/fenrir[the Fenrir Git
|
||||||
repository].
|
repository].
|
||||||
|
|
||||||
=== Getting started with Fenrir
|
=== Getting started with Fenrir
|
||||||
@@ -2193,9 +2193,9 @@ settings.conf). Commands are python files with a special scheme. You can
|
|||||||
assign them to a shortcut using the filename without an extension or
|
assign them to a shortcut using the filename without an extension or
|
||||||
place them in a hook trigger like OnInput or OnScreenChange. For further
|
place them in a hook trigger like OnInput or OnScreenChange. For further
|
||||||
information see developer guide. Good Examples:
|
information see developer guide. Good Examples:
|
||||||
https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/date.py["date.py"]
|
https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/date.py["date.py"]
|
||||||
(announce the Date),
|
(announce the Date),
|
||||||
https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/shut_up.py["shut_up.py"]
|
https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/shut_up.py["shut_up.py"]
|
||||||
(interrupt output) the basic scheme for a command is as follows:
|
(interrupt output) the basic scheme for a command is as follows:
|
||||||
|
|
||||||
....
|
....
|
||||||
@@ -2218,7 +2218,7 @@ class command():
|
|||||||
pass
|
pass
|
||||||
....
|
....
|
||||||
|
|
||||||
* https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/command_template.py[Template
|
* https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/command_template.py[Template
|
||||||
lives here]
|
lives here]
|
||||||
* The class needs to have the name "command".
|
* The class needs to have the name "command".
|
||||||
* "initialize" is running once whilst loading the command.
|
* "initialize" is running once whilst loading the command.
|
||||||
@@ -2276,7 +2276,7 @@ root.
|
|||||||
=== Bugreports and feature requests
|
=== Bugreports and feature requests
|
||||||
|
|
||||||
Please report Bugs and feature requests to:
|
Please report Bugs and feature requests to:
|
||||||
https://github.com/chrys87/fenrir/issues
|
https://git.stormux.org/storm/fenrir/issues
|
||||||
|
|
||||||
for bugs please provide a link:#Howto create a debug file[debug] file
|
for bugs please provide a link:#Howto create a debug file[debug] file
|
||||||
that shows the issue.
|
that shows the issue.
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ For Arch there are PKGBUILDs in the AUR:
|
|||||||
|
|
||||||
- Download the latest stable version from the [[https://linux-a11y.org/index.php?page=fenrir-screenreader|Fenrir-Project]] site.
|
- Download the latest stable version from the [[https://linux-a11y.org/index.php?page=fenrir-screenreader|Fenrir-Project]] site.
|
||||||
- Unpack the archive
|
- Unpack the archive
|
||||||
- Check the needed Dependencys by running [[https://github.com/chrys87/fenrir/blob/master/check-dependencies.py|check-dependencys.py]] script
|
- Check the needed Dependencys by running [[https://git.stormux.org/storm/fenrir/src/branch/master/check-dependencies.py|check-dependencys.py]] script
|
||||||
- install the missing dependencies an standard installation requires the following:
|
- install the missing dependencies an standard installation requires the following:
|
||||||
* python3 >= 3.3 (and all the following is needed for python3 )
|
* python3 >= 3.3 (and all the following is needed for python3 )
|
||||||
* python3-speechd (screen)
|
* python3-speechd (screen)
|
||||||
@@ -171,7 +171,7 @@ For Arch there are PKGBUILDs in the AUR:
|
|||||||
* python3-pyenchant (spellchecker)
|
* python3-pyenchant (spellchecker)
|
||||||
* your language for aspell (aspell-<lang>) (spellchecker)
|
* your language for aspell (aspell-<lang>) (spellchecker)
|
||||||
* sox (sound)
|
* sox (sound)
|
||||||
* For an individual installation see [[#Support and Requirements|Support and Requirements]] or consult the [[https://github.com/chrys87/fenrir/blob/master/README.md|Readme]])
|
* For an individual installation see [[#Support and Requirements|Support and Requirements]] or consult the [[https://git.stormux.org/storm/fenrir/src/branch/master/README.md|Readme]])
|
||||||
- run "install.sh" as root
|
- run "install.sh" as root
|
||||||
|
|
||||||
this installs Fenrir as the following
|
this installs Fenrir as the following
|
||||||
@@ -185,7 +185,7 @@ to remove Fenrir just run uninstall.sh as root
|
|||||||
|
|
||||||
if you want to get the latest code you can use git to get a development snapshot:
|
if you want to get the latest code you can use git to get a development snapshot:
|
||||||
|
|
||||||
git clone https://github.com/chrys87/fenrir.git
|
git clone https://git.stormux.org/storm/fenrir.git
|
||||||
|
|
||||||
===== Auto Start =====
|
===== Auto Start =====
|
||||||
|
|
||||||
@@ -1270,7 +1270,7 @@ File: ''/usr/share/fenrirscreenreader/scripts/helloWorld__-__key_h.sh'':
|
|||||||
===== Commands =====
|
===== Commands =====
|
||||||
You can place your own commands in "/usr/share/fenrirscreenreader/commands" (path is configurable in settings.conf).
|
You can place your own commands in "/usr/share/fenrirscreenreader/commands" (path is configurable in settings.conf).
|
||||||
Commands are python files with a special scheme. You can assign them to a shortcut using the filename without an extension or place them in a hook trigger like OnInput or OnScreenChange. For further information see developer guide.
|
Commands are python files with a special scheme. You can assign them to a shortcut using the filename without an extension or place them in a hook trigger like OnInput or OnScreenChange. For further information see developer guide.
|
||||||
Good Examples: [[https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/date.py|"date.py"]] (announce the Date), [[https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/commands/shut_up.py|"shut_up.py"]] (interrupt output)
|
Good Examples: [[https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/date.py|"date.py"]] (announce the Date), [[https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/commands/shut_up.py|"shut_up.py"]] (interrupt output)
|
||||||
the basic scheme for a command is as follows:
|
the basic scheme for a command is as follows:
|
||||||
|
|
||||||
from core import debug
|
from core import debug
|
||||||
@@ -1289,7 +1289,7 @@ the basic scheme for a command is as follows:
|
|||||||
def setCallback(self, callback):
|
def setCallback(self, callback):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
* [[https://github.com/chrys87/fenrir/blob/master/src/fenrir/commands/command_template.py|Template lives here]]
|
* [[https://git.stormux.org/storm/fenrir/src/branch/master/src/fenrirscreenreader/commands/command_template.py|Template lives here]]
|
||||||
* The class needs to have the name "command".
|
* The class needs to have the name "command".
|
||||||
* "initialize" is running once whilst loading the command.
|
* "initialize" is running once whilst loading the command.
|
||||||
* "shutdown" is running on unload like the command (quit fenrir)
|
* "shutdown" is running on unload like the command (quit fenrir)
|
||||||
@@ -1319,7 +1319,7 @@ the basic scheme for a command is as follows:
|
|||||||
- You can test if speech-dispatcher works by invoking it as root\\ ''sudo spd-say "hello world"''
|
- You can test if speech-dispatcher works by invoking it as root\\ ''sudo spd-say "hello world"''
|
||||||
===== Bugreports and feature requests =====
|
===== Bugreports and feature requests =====
|
||||||
Please report Bugs and feature requests to:
|
Please report Bugs and feature requests to:
|
||||||
[[https://github.com/chrys87/fenrir/issues|https://github.com/chrys87/fenrir/issues]]
|
[[https://git.stormux.org/storm/fenrir/issues|https://git.stormux.org/storm/fenrir/issues]]
|
||||||
|
|
||||||
for bugs please provide a [[#Howto create a debug file|debug]] file that shows the issue.
|
for bugs please provide a [[#Howto create a debug file|debug]] file that shows the issue.
|
||||||
==== How-to create a debug file ====
|
==== How-to create a debug file ====
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ To test Fenrir:
|
|||||||
sudo fenrir
|
sudo fenrir
|
||||||
|
|
||||||
To have Fenrir start on system boot using systemd:
|
To have Fenrir start on system boot using systemd:
|
||||||
download service file: https://raw.githubusercontent.com/chrys87/fenrir/master/autostart/systemd/Arch/fenrir.service
|
download service file: https://git.stormux.org/storm/fenrir/raw/branch/master/autostart/systemd/Arch/fenrir.service
|
||||||
move the service file to: /etc/systemd/system/fenrir.service
|
move the service file to: /etc/systemd/system/fenrir.service
|
||||||
sudo systemctl enable fenrir
|
sudo systemctl enable fenrir
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
# Fenrir TTY screen reader
|
# Fenrir TTY screen reader
|
||||||
# By Chrys, Storm Dragon, and contributors.
|
# By Chrys, Storm Dragon, and contributors.
|
||||||
|
|
||||||
version = "2025.11.23"
|
version = "2025.11.24"
|
||||||
code_name = "testing"
|
code_name = "testing"
|
||||||
|
|||||||
@@ -1,306 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import configparser
|
|
||||||
import dialog
|
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
import select
|
|
||||||
import tempfile
|
|
||||||
from typing import Dict, List, Optional, Tuple
|
|
||||||
|
|
||||||
class FenrirConfigTool:
|
|
||||||
def __init__(self):
|
|
||||||
os.environ['DIALOGOPTS'] = '--no-lines --visit-items'
|
|
||||||
self.tui = dialog.Dialog(dialog="dialog")
|
|
||||||
self.settingsFile = '/etc/fenrirscreenreader/settings/settings.conf'
|
|
||||||
|
|
||||||
# Check if we need to re-run with elevated privileges
|
|
||||||
if not self.check_root():
|
|
||||||
self.escalate_privileges()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
self.instructions = {
|
|
||||||
'menu': "\nNavigation: Use Up/Down arrows to move, Enter to select, Escape to go back",
|
|
||||||
'radiolist': "\nNavigation: Use Up/Down arrows to move, Space to select option, Enter to confirm, Escape to cancel",
|
|
||||||
'inputbox': "\nEnter your value and press Enter to confirm, or Escape to cancel"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configuration presets and help text from original FenrirConfig
|
|
||||||
self.presetOptions = {
|
|
||||||
'sound.driver': ['genericDriver', 'gstreamerDriver'],
|
|
||||||
'speech.driver': ['speechdDriver', 'genericDriver'],
|
|
||||||
'screen.driver': ['vcsaDriver', 'dummyDriver', 'ptyDriver', 'debugDriver'],
|
|
||||||
'keyboard.driver': ['evdevDriver', 'dummyDriver'],
|
|
||||||
'remote.driver': ['unixDriver', 'tcpDriver'],
|
|
||||||
'keyboard.charEchoMode': ['0', '1', '2'],
|
|
||||||
'general.punctuationLevel': ['none', 'some', 'most', 'all'],
|
|
||||||
'general.debugMode': ['File', 'Print']
|
|
||||||
}
|
|
||||||
|
|
||||||
self.helpText = {
|
|
||||||
'sound.volume': 'Volume level from 0 (quietest) to 1.0 (loudest)',
|
|
||||||
'speech.rate': 'Speech rate from 0 (slowest) to 1.0 (fastest)',
|
|
||||||
'speech.pitch': 'Voice pitch from 0 (lowest) to 1.0 (highest)',
|
|
||||||
'keyboard.charEchoMode': '0 = None, 1 = always, 2 = only while capslock'
|
|
||||||
}
|
|
||||||
|
|
||||||
def check_root(self) -> bool:
|
|
||||||
return os.geteuid() == 0
|
|
||||||
|
|
||||||
def find_privilege_escalation_tool(self) -> Optional[str]:
|
|
||||||
for tool in ['sudo', 'doas']:
|
|
||||||
if subprocess.run(['which', tool], stdout=subprocess.PIPE).returncode == 0:
|
|
||||||
return tool
|
|
||||||
return None
|
|
||||||
|
|
||||||
def escalate_privileges(self):
|
|
||||||
tool = self.find_privilege_escalation_tool()
|
|
||||||
if not tool:
|
|
||||||
self.tui.msgbox("Error: Neither sudo nor doas found. Please run this script as root.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
scriptPath = os.path.abspath(sys.argv[0])
|
|
||||||
command = [tool, sys.executable, scriptPath] + sys.argv[1:]
|
|
||||||
os.execvp(tool, command)
|
|
||||||
except Exception as e:
|
|
||||||
self.tui.msgbox(f"Error escalating privileges: {str(e)}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def is_boolean_option(self, value: str) -> bool:
|
|
||||||
"""Check if the current value is likely a boolean option"""
|
|
||||||
return value.lower() in ['true', 'false']
|
|
||||||
|
|
||||||
def validate_input(self, section: str, option: str, value: str) -> tuple[bool, str]:
|
|
||||||
"""Validate user input based on the option type and constraints"""
|
|
||||||
try:
|
|
||||||
if option.endswith('volume') or option.endswith('rate') or option.endswith('pitch'):
|
|
||||||
floatVal = float(value)
|
|
||||||
if not 0 <= floatVal <= 1.0:
|
|
||||||
return False, "Value must be between 0 and 1.0"
|
|
||||||
return True, value
|
|
||||||
except ValueError:
|
|
||||||
return False, "Invalid number format"
|
|
||||||
|
|
||||||
def get_value_with_presets(self, section: str, option: str, currentValue: str) -> Optional[str]:
|
|
||||||
"""Get value using appropriate input method based on option type"""
|
|
||||||
key = f"{section}.{option}"
|
|
||||||
|
|
||||||
# Handle boolean options
|
|
||||||
if self.is_boolean_option(currentValue):
|
|
||||||
choices = [
|
|
||||||
('True', '', currentValue.lower() == 'true'),
|
|
||||||
('False', '', currentValue.lower() == 'false')
|
|
||||||
]
|
|
||||||
code, tag = self.tui.radiolist(
|
|
||||||
f"Select value for '{option}'" + self.instructions['radiolist'],
|
|
||||||
choices=choices
|
|
||||||
)
|
|
||||||
return tag if code == self.tui.OK else None
|
|
||||||
|
|
||||||
# Handle other preset options
|
|
||||||
elif key in self.presetOptions:
|
|
||||||
choices = [(opt, "", opt == currentValue) for opt in self.presetOptions[key]]
|
|
||||||
code, tag = self.tui.radiolist(
|
|
||||||
f"Select value for '{option}'" +
|
|
||||||
(f"\n{self.helpText[key]}" if key in self.helpText else "") +
|
|
||||||
self.instructions['radiolist'],
|
|
||||||
choices=choices
|
|
||||||
)
|
|
||||||
return tag if code == self.tui.OK else None
|
|
||||||
|
|
||||||
# Handle free-form input
|
|
||||||
else:
|
|
||||||
helpText = self.helpText.get(key, "")
|
|
||||||
code, value = self.tui.inputbox(
|
|
||||||
f"Enter value for '{option}'" +
|
|
||||||
(f"\n{helpText}" if helpText else "") +
|
|
||||||
self.instructions['inputbox'],
|
|
||||||
init=currentValue
|
|
||||||
)
|
|
||||||
if code == self.tui.OK:
|
|
||||||
isValid, message = self.validate_input(section, option, value)
|
|
||||||
if not isValid:
|
|
||||||
self.tui.msgbox(f"Invalid input: {message}")
|
|
||||||
return None
|
|
||||||
return value
|
|
||||||
return None
|
|
||||||
|
|
||||||
def run_command(self, cmd: List[str], needsRoot: bool = False) -> Optional[str]:
|
|
||||||
try:
|
|
||||||
if needsRoot and not self.check_root():
|
|
||||||
tool = self.find_privilege_escalation_tool()
|
|
||||||
if tool:
|
|
||||||
cmd = [tool] + cmd
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
||||||
return result.stdout.strip() if result.returncode == 0 else None
|
|
||||||
except Exception as e:
|
|
||||||
self.tui.msgbox(f"Error running command {' '.join(cmd)}: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_speechd_modules(self) -> List[str]:
|
|
||||||
output = self.run_command(['spd-say', '-O'], True)
|
|
||||||
if output:
|
|
||||||
lines = output.split('\n')
|
|
||||||
return [line.strip() for line in lines[1:] if line.strip()]
|
|
||||||
return []
|
|
||||||
|
|
||||||
def process_espeak_voice(self, voiceLine: str) -> Optional[str]:
|
|
||||||
parts = [p for p in voiceLine.split() if p]
|
|
||||||
if len(parts) < 2:
|
|
||||||
return None
|
|
||||||
langCode = parts[-2].lower()
|
|
||||||
variant = parts[-1].lower()
|
|
||||||
return f"{langCode}+{variant}" if variant and variant != 'none' else langCode
|
|
||||||
|
|
||||||
def get_module_voices(self, moduleName: str) -> List[str]:
|
|
||||||
output = self.run_command(['spd-say', '-o', moduleName, '-L'], True)
|
|
||||||
if output:
|
|
||||||
lines = output.split('\n')
|
|
||||||
voices = []
|
|
||||||
for line in lines[1:]:
|
|
||||||
if not line.strip():
|
|
||||||
continue
|
|
||||||
if moduleName.lower() == 'espeak-ng':
|
|
||||||
voice = self.process_espeak_voice(line)
|
|
||||||
if voice:
|
|
||||||
voices.append(voice)
|
|
||||||
else:
|
|
||||||
voices.append(line.strip())
|
|
||||||
return voices
|
|
||||||
return []
|
|
||||||
|
|
||||||
def configure_speech(self) -> None:
|
|
||||||
moduleList = self.get_speechd_modules()
|
|
||||||
if not moduleList:
|
|
||||||
self.tui.msgbox("No speech-dispatcher modules found!")
|
|
||||||
return
|
|
||||||
|
|
||||||
code, moduleChoice = self.tui.menu(
|
|
||||||
"Select speech module:" + self.instructions['menu'],
|
|
||||||
choices=[(module, "") for module in moduleList]
|
|
||||||
)
|
|
||||||
|
|
||||||
if code != self.tui.OK:
|
|
||||||
return
|
|
||||||
|
|
||||||
voiceList = self.get_module_voices(moduleChoice)
|
|
||||||
if not voiceList:
|
|
||||||
self.tui.msgbox(f"No voices found for module {moduleChoice}")
|
|
||||||
return
|
|
||||||
|
|
||||||
code, voice = self.tui.menu(
|
|
||||||
f"Select voice for {moduleChoice}:" + self.instructions['menu'],
|
|
||||||
choices=[(v, "") for v in voiceList]
|
|
||||||
)
|
|
||||||
|
|
||||||
if code != self.tui.OK:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Test voice configuration
|
|
||||||
if self.test_voice(moduleChoice, voice):
|
|
||||||
config = configparser.ConfigParser(interpolation=None)
|
|
||||||
config.read(self.settingsFile)
|
|
||||||
|
|
||||||
if 'speech' not in config:
|
|
||||||
config['speech'] = {}
|
|
||||||
|
|
||||||
config['speech'].update({
|
|
||||||
'driver': 'speechdDriver',
|
|
||||||
'module': moduleChoice,
|
|
||||||
'voice': voice,
|
|
||||||
'enabled': 'True',
|
|
||||||
'rate': '0.25',
|
|
||||||
'pitch': '0.5',
|
|
||||||
'volume': '1.0'
|
|
||||||
})
|
|
||||||
|
|
||||||
with open(self.settingsFile, 'w') as configfile:
|
|
||||||
config.write(configfile)
|
|
||||||
|
|
||||||
self.tui.msgbox("Speech configuration updated successfully!\nPlease restart Fenrir for changes to take effect.")
|
|
||||||
|
|
||||||
def test_voice(self, moduleName: str, voiceName: str) -> bool:
|
|
||||||
testMessage = "If you hear this message, press Enter within 30 seconds to confirm."
|
|
||||||
try:
|
|
||||||
process = subprocess.Popen(
|
|
||||||
['spd-say', '-o', moduleName, '-y', voiceName, testMessage]
|
|
||||||
)
|
|
||||||
|
|
||||||
code = self.tui.pause(
|
|
||||||
"Waiting for voice test...\n"
|
|
||||||
"Press Enter if you hear the message, or wait for timeout.",
|
|
||||||
30
|
|
||||||
)
|
|
||||||
|
|
||||||
process.terminate()
|
|
||||||
return code == self.tui.OK
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.tui.msgbox(f"Error testing voice: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def edit_general_config(self) -> None:
|
|
||||||
while True:
|
|
||||||
config = configparser.ConfigParser(interpolation=None)
|
|
||||||
config.read(self.settingsFile)
|
|
||||||
sections = config.sections()
|
|
||||||
|
|
||||||
code, section = self.tui.menu(
|
|
||||||
"Select a section to configure:" + self.instructions['menu'],
|
|
||||||
choices=[(s, "") for s in sections] + [("Go Back", "")]
|
|
||||||
)
|
|
||||||
|
|
||||||
if code != self.tui.OK or section == "Go Back":
|
|
||||||
break
|
|
||||||
|
|
||||||
while True:
|
|
||||||
options = config.options(section)
|
|
||||||
choices = [(o, f"Current: {config.get(section, o)}") for o in options]
|
|
||||||
choices.append(("Go Back", ""))
|
|
||||||
|
|
||||||
code, option = self.tui.menu(
|
|
||||||
f"Select option to edit in '{section}':" + self.instructions['menu'],
|
|
||||||
choices=choices
|
|
||||||
)
|
|
||||||
|
|
||||||
if code != self.tui.OK or option == "Go Back":
|
|
||||||
break
|
|
||||||
|
|
||||||
currentValue = config.get(section, option)
|
|
||||||
newValue = self.get_value_with_presets(section, option, currentValue)
|
|
||||||
|
|
||||||
if newValue is not None and newValue != currentValue:
|
|
||||||
config.set(section, option, newValue)
|
|
||||||
with open(self.settingsFile, 'w') as configfile:
|
|
||||||
config.write(configfile)
|
|
||||||
self.tui.msgbox("Setting updated successfully.")
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while True:
|
|
||||||
code, choice = self.tui.menu(
|
|
||||||
"Fenrir Configuration Tool" + self.instructions['menu'],
|
|
||||||
choices=[
|
|
||||||
("speech-dispatcher", "Configure module and voice"),
|
|
||||||
("Advanced", "Edit Fenrir settings"),
|
|
||||||
("Exit", "")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
if code != self.tui.OK or choice == "Exit":
|
|
||||||
break
|
|
||||||
|
|
||||||
if choice == "speech-dispatcher":
|
|
||||||
self.configure_speech()
|
|
||||||
elif choice == "Advanced":
|
|
||||||
self.edit_general_config()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
configTool = FenrirConfigTool()
|
|
||||||
try:
|
|
||||||
configTool.run()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Unexpected error occurred: {str(e)}", file=sys.stderr)
|
|
||||||
sys.exit(1)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
sinks=(`pacmd list-sinks | sed -n -e 's/\**[[:space:]]index:[[:space:]]\([[:digit:]]\)/\1/p'`)
|
|
||||||
sinks_count=${#sinks[@]}
|
|
||||||
active_sink_index=`pacmd list-sinks | sed -n -e 's/\*[[:space:]]index:[[:space:]]\([[:digit:]]\)/\1/p'`
|
|
||||||
newSink=${sinks[0]}
|
|
||||||
ord=0
|
|
||||||
|
|
||||||
while [ $ord -lt $sinks_count ];
|
|
||||||
do
|
|
||||||
echo ${sinks[$ord]}
|
|
||||||
if [ ${sinks[$ord]} -gt $active_sink_index ] ; then
|
|
||||||
newSink=${sinks[$ord]}
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
let ord++
|
|
||||||
done
|
|
||||||
|
|
||||||
pactl list short sink-inputs|while read stream; do
|
|
||||||
streamId=$(echo $stream|cut '-d ' -f1)
|
|
||||||
echo "moving stream $streamId"
|
|
||||||
pactl move-sink-input "$streamId" "$newSink"
|
|
||||||
done
|
|
||||||
pacmd set-default-sink "$newSink"
|
|
||||||
|
|
||||||
#https://unix.stackexchange.com/questions/65246/change-pulseaudio-input-output-from-shell
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from pyudev import Context
|
from pyudev import Context
|
||||||
context = Context()
|
context = Context()
|
||||||
for device in context.list_devices(subsystem='input'):
|
for device in context.list_devices(subsystem='input'):
|
||||||
'{0} - {1}'.format(device.sys_name, device.device_type)
|
print('{0} - {1}'.format(device.sys_name, device.device_type))
|
||||||
|
|||||||
@@ -1,96 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Make sure this script is ran as root
|
|
||||||
if [[ "$(whoami)" != "root" ]]; then
|
|
||||||
echo "Please run $0 with oot privileges."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# This script checks for, and creates if needed, the fenrirscreenreader user.
|
|
||||||
|
|
||||||
# Find out which group to use for uinput
|
|
||||||
uinput="$(stat -c '%G' /dev/uinput | grep -v root)"
|
|
||||||
if ! [[ "$uinput" =~ ^[a-zA-Z]+$ ]]; then
|
|
||||||
groupadd -r uinput
|
|
||||||
chown root:uinput /dev/uinput
|
|
||||||
fi
|
|
||||||
|
|
||||||
# find out which group to use for /dev/input.
|
|
||||||
input="$(stat -c '%G' /dev/input/* | grep -v root | head -1)"
|
|
||||||
if ! [[ "$input" =~ ^[a-zA-Z]+$ ]]; then
|
|
||||||
# Create the input group
|
|
||||||
groupadd --system input
|
|
||||||
echo 'KERNEL=="event*", NAME="input/%k", MODE="660", GROUP="input"' >> /etc/udev/rules.d/99-input.rules
|
|
||||||
input="input"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# find out which group to use for /dev/tty.
|
|
||||||
tty="$(stat -c '%G' /dev/tty | grep -v root)"
|
|
||||||
if ! [[ "$tty" =~ ^[a-zA-Z]+$ ]]; then
|
|
||||||
# Create the tty group
|
|
||||||
groupadd --system tty
|
|
||||||
echo 'KERNEL=="event*", NAME="tty/%k", MODE="660", GROUP="tty"' >> /etc/udev/rules.d/99-tty.rules
|
|
||||||
tty="tty"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add fenrirscreenreader
|
|
||||||
id fenrirscreenreader &> /dev/null ||
|
|
||||||
useradd -m -d /var/fenrirscreenreader -r -G $input,$tty,$uinput -s /bin/nologin -U fenrirscreenreader
|
|
||||||
|
|
||||||
#configure directory structure.
|
|
||||||
mkdir -p /var/log/fenrirscreenreader /etc/fenrirscreenreader
|
|
||||||
|
|
||||||
# Set directory ownership
|
|
||||||
chown -R fenrirscreenreader:fenrirscreenreader /var/log/fenrirscreenreader
|
|
||||||
chmod -R 755 /var/log/fenrirscreenreader
|
|
||||||
chown -R root:fenrirscreenreader /etc/fenrirscreenreader
|
|
||||||
|
|
||||||
# Fix permissions on tty#s
|
|
||||||
for i in /dev/tty[0-9]* ; do
|
|
||||||
chmod 660 "$i"
|
|
||||||
done
|
|
||||||
|
|
||||||
sudo -Hu fenrirscreenreader mkdir /var/fenrirscreenreader/.config/pulse
|
|
||||||
|
|
||||||
# Set up sound
|
|
||||||
cat << EOF > /var/fenrirscreenreader/.config/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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
Reference in New Issue
Block a user