Add more local sourced directories for scripts and sounds meaning each user can have own scripts and sound themes when using -x. Hopefully fixed the remainder of the random freeze bug.

This commit is contained in:
Storm Dragon
2026-05-12 17:23:50 -04:00
parent 4022fc4006
commit b599a25945
15 changed files with 516 additions and 879 deletions
@@ -6,7 +6,6 @@
import _thread
import os
import subprocess
from subprocess import PIPE
from subprocess import Popen
@@ -53,10 +52,11 @@ class command:
def _thread_run(self):
try:
callstring = (
self.script_path + " " + self.env["general"]["curr_user"]
p = Popen(
[self.script_path, self.env["general"]["curr_user"]],
stdout=PIPE,
stderr=PIPE,
)
p = Popen(callstring, stdout=PIPE, stderr=PIPE, shell=True)
stdout, stderr = p.communicate()
stdout = stdout.decode("utf-8")
stderr = stderr.decode("utf-8")
@@ -22,23 +22,24 @@ class command(config_command):
def run(self):
current_theme = self.get_setting("sound", "theme", "default")
current_theme_name = os.path.basename(
os.path.normpath(current_theme)
)
# Present current theme
self.present_text(f"Current sound theme: {current_theme}")
# Look for available sound themes
sound_paths = [
"/usr/share/sounds",
"/usr/share/fenrirscreenreader/sounds",
os.path.expanduser("~/.local/share/fenrirscreenreader/sounds"),
]
sound_paths = self.env[
"runtime"
]["SettingsManager"].get_sound_theme_roots()
available_themes = self.get_available_themes(sound_paths)
if len(available_themes) > 1:
# For this implementation, cycle through available themes
try:
current_index = available_themes.index(current_theme)
current_index = available_themes.index(current_theme_name)
next_index = (current_index + 1) % len(available_themes)
new_theme = available_themes[next_index]
except ValueError:
+115 -55
View File
@@ -174,90 +174,121 @@ class CommandManager:
)
continue
def get_script_paths(self, script_path=""):
if script_path:
candidate_paths = [script_path]
else:
settings_manager = self.env["runtime"]["SettingsManager"]
candidate_paths = [
settings_manager.get_user_script_path(),
settings_manager.get_setting("general", "script_path"),
os.path.join(fenrir_path, "../../config/scripts/"),
]
script_paths = []
seen_paths = set()
for path in candidate_paths:
if not path:
continue
normalized_path = os.path.abspath(os.path.expanduser(path))
if normalized_path in seen_paths:
continue
script_paths.append(normalized_path)
seen_paths.add(normalized_path)
return script_paths
def load_script_commands(self, section="commands", script_path=""):
if script_path == "":
script_path = self.env["runtime"]["SettingsManager"].get_setting(
"general", "script_path"
)
if not script_path.endswith("/"):
script_path += "/"
if not os.path.exists(script_path):
if os.path.exists(fenrir_path + "/../../config/scripts/"):
script_path = fenrir_path + "/../../config/scripts/"
else:
self.env["runtime"]["DebugManager"].write_debug_out(
"scriptpath not exists:" + script_path,
debug.DebugLevel.WARNING,
script_paths = self.get_script_paths(script_path)
loaded_any_path = False
loaded_script_names = set()
for path in script_paths:
loaded_any_path = (
self.load_script_commands_from_path(
section, path, loaded_script_names
)
return
or loaded_any_path
)
if not loaded_any_path:
self.env["runtime"]["DebugManager"].write_debug_out(
"No script paths available:" + str(script_paths),
debug.DebugLevel.WARNING,
)
def load_script_commands_from_path(
self, section, script_path, loaded_script_names=None
):
if loaded_script_names is None:
loaded_script_names = set()
if not os.path.exists(script_path):
return False
if not os.path.isdir(script_path):
self.env["runtime"]["DebugManager"].write_debug_out(
"scriptpath not a directory:" + script_path,
debug.DebugLevel.ERROR,
)
return
return False
if not os.access(script_path, os.R_OK):
self.env["runtime"]["DebugManager"].write_debug_out(
"scriptpath not readable:" + script_path,
debug.DebugLevel.ERROR,
)
return
command_list = glob.glob(script_path + "*")
return False
command_list = sorted(glob.glob(os.path.join(script_path, "*")))
sub_command = fenrir_path + "/commands/commands/subprocess.py"
for command in command_list:
invalid = False
try:
if not os.path.isfile(command):
continue
file_name, file_extension = os.path.splitext(command)
file_name = file_name.split("/")[-1]
if file_name.startswith("__"):
continue
if file_name.upper() in self.env["commands"][section]:
command_name = file_name.upper()
script_name = self.get_script_name(file_name)
if script_name in loaded_script_names:
self.env["runtime"]["DebugManager"].write_debug_out(
"Skip script with duplicate script name:"
+ command_name,
debug.DebugLevel.INFO,
)
continue
if command_name in self.env["commands"][section]:
self.env["runtime"]["DebugManager"].write_debug_out(
"Skip script with duplicate command name:"
+ command_name,
debug.DebugLevel.INFO,
)
continue
shortcut = self.get_script_shortcut(file_name)
if not shortcut:
continue
shortcut_key = str(shortcut)
if shortcut_key in self.env["bindings"]:
self.env["runtime"]["DebugManager"].write_debug_out(
"Skip script with duplicate shortcut:"
+ command_name
+ " "
+ shortcut_key,
debug.DebugLevel.INFO,
)
continue
command_mod = module_utils.import_module(
file_name, sub_command
)
self.env["commands"][section][
file_name.upper()
] = command_mod.command()
self.env["commands"][section][file_name.upper()].initialize(
self.env["commands"][section][command_name] = (
command_mod.command()
)
self.env["commands"][section][command_name].initialize(
self.env, command
)
self.env["bindings"][shortcut_key] = command_name
self.env["rawBindings"][shortcut_key] = shortcut
loaded_script_names.add(script_name)
self.env["runtime"]["DebugManager"].write_debug_out(
"Load script:" + section + "." + file_name.upper(),
"Load script:" + section + "." + command_name,
debug.DebugLevel.INFO,
on_any_level=True,
)
comm_settings = file_name.upper().split("__-__")
if len(comm_settings) == 1:
keys = comm_settings[0]
elif len(comm_settings) == 2:
keys = comm_settings[1]
elif len(comm_settings) > 2:
continue
keys = keys.split("__+__")
shortcut_keys = []
shortcut = []
for key in keys:
if not self.env["runtime"]["InputManager"].is_valid_key(
key.upper()
):
self.env["runtime"]["DebugManager"].write_debug_out(
"invalid key : "
+ key.upper()
+ " script:"
+ file_name,
debug.DebugLevel.WARNING,
)
invalid = True
break
shortcut_keys.append(key.upper())
if invalid:
continue
if "KEY_SCRIPT" not in shortcut_keys:
shortcut_keys.append("KEY_SCRIPT")
shortcut.append(1)
shortcut.append(sorted(shortcut_keys))
self.env["bindings"][str(shortcut)] = file_name.upper()
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"Loading script:" + file_name, debug.DebugLevel.ERROR
@@ -266,6 +297,35 @@ class CommandManager:
str(e), debug.DebugLevel.ERROR
)
continue
return True
def get_script_name(self, file_name):
return file_name.upper().split("__-__", 1)[0]
def get_script_shortcut(self, file_name):
comm_settings = file_name.upper().split("__-__")
if len(comm_settings) == 1:
keys = comm_settings[0]
elif len(comm_settings) == 2:
keys = comm_settings[1]
else:
return None
shortcut_keys = []
for key in keys.split("__+__"):
key = key.upper()
if not key:
return None
if not self.env["runtime"]["InputManager"].is_valid_key(key):
self.env["runtime"]["DebugManager"].write_debug_out(
"invalid key : " + key + " script:" + file_name,
debug.DebugLevel.WARNING,
)
return None
shortcut_keys.append(key)
if "KEY_SCRIPT" not in shortcut_keys:
shortcut_keys.append("KEY_SCRIPT")
return [1, sorted(shortcut_keys)]
def shutdown_commands(self, section):
# Check if the section exists in the commands dictionary
+11 -1
View File
@@ -45,7 +45,7 @@ class OutputManager:
announce_capital=False,
flush=True,
):
if text == "":
if text == "" and sound_icon == "":
return
if (
self.env["runtime"]["SettingsManager"].get_setting_as_bool(
@@ -63,6 +63,8 @@ class OutputManager:
"sound_icon found", debug.DebugLevel.INFO
)
return
if text == "":
return
if (len(text) > 1) and (text.strip(string.whitespace) == ""):
return
is_capital = self._should_announce_capital(text, announce_capital)
@@ -349,6 +351,14 @@ class OutputManager:
return False
def play_sound(self, sound_icon="", interrupt=True):
aliases = {
"ERROR": "ERRORSCREEN",
"ERRORSOUND": "ERRORSCREEN",
}
normalized_icon = aliases.get(str(sound_icon).upper(), sound_icon)
return self.play_sound_icon(normalized_icon, interrupt)
def play_frequence(self, frequence, duration, interrupt=True):
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
"sound", "enabled"
+71 -65
View File
@@ -47,6 +47,11 @@ class SettingsManager:
user_settings_file = (
"~/.local/share/stormux/fenrirscreenreader/settings/settings.conf"
)
user_resource_root = "~/.local/stormux/fenrir/"
system_sound_roots = [
"/usr/share/sounds/fenrir/",
"/usr/share/sounds/fenrirscreenreader/",
]
def __init__(self):
self.settings = settings_data
@@ -134,6 +139,15 @@ class SettingsManager:
def get_bundled_settings_root(self):
return os.path.abspath(os.path.join(fenrir_path, "../../config/")) + "/"
def get_user_resource_root(self):
return os.path.expanduser(self.user_resource_root)
def get_user_script_path(self):
return self.get_user_resource_root()
def get_user_sound_root(self):
return os.path.join(self.get_user_resource_root(), "sounds/")
def get_resource_settings_root(self):
if os.path.exists(self.system_settings_root):
return self.system_settings_root
@@ -142,6 +156,56 @@ class SettingsManager:
return bundled_settings_root
return ""
def get_sound_theme_roots(self):
candidate_roots = [
self.get_user_sound_root(),
*self.system_sound_roots,
os.path.join(self.get_bundled_settings_root(), "sound/"),
]
sound_roots = []
seen_roots = set()
for root in candidate_roots:
normalized_root = os.path.abspath(os.path.expanduser(root))
if normalized_root in seen_roots:
continue
sound_roots.append(normalized_root + "/")
seen_roots.add(normalized_root)
return sound_roots
def resolve_sound_theme_path(self, theme):
if not theme:
return ""
theme = os.path.expanduser(theme)
if os.path.exists(os.path.join(theme, "soundicons.conf")):
return theme
for sound_root in self.get_sound_theme_roots():
theme_path = os.path.join(sound_root, theme)
if os.path.exists(os.path.join(theme_path, "soundicons.conf")):
return theme_path
return ""
def load_keyboard_layout(self, environment):
settings_root = self.get_resource_settings_root()
keyboard_layout = self.get_setting("keyboard", "keyboard_layout")
if os.path.exists(keyboard_layout):
environment["runtime"]["InputManager"].load_shortcuts(
keyboard_layout
)
return
layout_path = os.path.join(settings_root, "keyboard", keyboard_layout)
if os.path.exists(layout_path):
self.set_setting("keyboard", "keyboard_layout", layout_path)
environment["runtime"]["InputManager"].load_shortcuts(layout_path)
return
layout_path = os.path.join(
settings_root, "keyboard", keyboard_layout + ".conf"
)
if os.path.exists(layout_path):
self.set_setting("keyboard", "keyboard_layout", layout_path)
environment["runtime"]["InputManager"].load_shortcuts(layout_path)
def resolve_settings_file(self, requested_settings_file=None):
if requested_settings_file and os.path.exists(requested_settings_file):
return requested_settings_file
@@ -300,7 +364,7 @@ class SettingsManager:
try:
if self.env["runtime"][driverType] is not None:
self.env["runtime"][driverType].shutdown(self.env)
self.env["runtime"][driverType].shutdown()
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"settings_manager load_driver: Error shutting down driver: "
@@ -538,14 +602,6 @@ class SettingsManager:
if not settings_root:
return None
sound_root = "/usr/share/sounds/fenrirscreenreader/"
if not os.path.exists(sound_root):
bundled_sound_root = os.path.join(
self.get_bundled_settings_root(), "sound/"
)
if os.path.exists(bundled_sound_root):
sound_root = bundled_sound_root
self.save_settings_path = self.get_default_save_settings_file()
settings_file = self.resolve_settings_file(cliArgs.setting)
if not settings_file:
@@ -599,22 +655,11 @@ class SettingsManager:
"screen", "ignore_screen", ",".join(ignore_screens)
)
if not os.path.exists(
self.get_setting("sound", "theme") + "/soundicons.conf"
):
if os.path.exists(sound_root + self.get_setting("sound", "theme")):
self.set_setting(
"sound",
"theme",
sound_root + self.get_setting("sound", "theme"),
)
if os.path.exists(
self.get_setting("sound", "theme") + "/soundicons.conf"
):
environment["runtime"]["SettingsManager"].load_sound_icons(
self.get_setting("sound", "theme"), environment
)
else:
sound_theme_path = self.resolve_sound_theme_path(
self.get_setting("sound", "theme")
)
if sound_theme_path:
self.set_setting("sound", "theme", sound_theme_path)
environment["runtime"]["SettingsManager"].load_sound_icons(
self.get_setting("sound", "theme"), environment
)
@@ -691,6 +736,7 @@ class SettingsManager:
environment["runtime"]["InputManager"] = inputManager.InputManager()
environment["runtime"]["InputManager"].initialize(environment)
self.load_keyboard_layout(environment)
environment["runtime"]["ScreenManager"] = screenManager.ScreenManager()
environment["runtime"]["ScreenManager"].initialize(environment)
@@ -711,46 +757,6 @@ class SettingsManager:
] = diffReviewManager.DiffReviewManager()
environment["runtime"]["DiffReviewManager"].initialize(environment)
if not os.path.exists(
self.get_setting("keyboard", "keyboard_layout")
):
if os.path.exists(
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout")
):
self.set_setting(
"keyboard",
"keyboard_layout",
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout"),
)
environment["runtime"]["InputManager"].load_shortcuts(
self.get_setting("keyboard", "keyboard_layout")
)
if os.path.exists(
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout")
+ ".conf"
):
self.set_setting(
"keyboard",
"keyboard_layout",
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout")
+ ".conf",
)
environment["runtime"]["InputManager"].load_shortcuts(
self.get_setting("keyboard", "keyboard_layout")
)
else:
environment["runtime"]["InputManager"].load_shortcuts(
self.get_setting("keyboard", "keyboard_layout")
)
environment["runtime"]["CursorManager"] = cursorManager.CursorManager()
environment["runtime"]["CursorManager"].initialize(environment)
environment["runtime"][
@@ -1,742 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributors.
import inspect
import os
from configparser import ConfigParser
from fenrirscreenreader.core import applicationManager
from fenrirscreenreader.core import attributeManager
from fenrirscreenreader.core import barrierManager
from fenrirscreenreader.core import commandManager
from fenrirscreenreader.core import cursorManager
from fenrirscreenreader.core import debug
from fenrirscreenreader.core import debugManager
from fenrirscreenreader.core import diffReviewManager
from fenrirscreenreader.core import environment
from fenrirscreenreader.core import eventManager
from fenrirscreenreader.core import helpManager
from fenrirscreenreader.core import inputManager
from fenrirscreenreader.core import memoryManager
from fenrirscreenreader.core import outputManager
from fenrirscreenreader.core import processManager
from fenrirscreenreader.core import punctuationManager
from fenrirscreenreader.core import quickMenuManager
from fenrirscreenreader.core import readAllManager
from fenrirscreenreader.core import remoteManager
from fenrirscreenreader.core import sayAllManager
from fenrirscreenreader.core import screenManager
from fenrirscreenreader.core import tableManager
from fenrirscreenreader.core import textManager
from fenrirscreenreader.core import vmenuManager
from fenrirscreenreader.core.settingsData import settings_data
from fenrirscreenreader.utils import module_utils
currentdir = os.path.dirname(
os.path.realpath(os.path.abspath(inspect.getfile(inspect.currentframe())))
)
fenrir_path = os.path.dirname(currentdir)
class SettingsManager:
def __init__(self):
self.settings = settings_data
self.settingArgDict = {}
self.bindingsBackup = None
self.settings_file = ""
def initialize(self, environment):
self.env = environment
def shutdown(self):
pass
def get_binding_backup(self):
return self.bindingsBackup.copy()
def load_sound_icons(self, soundIconPath, environment=None):
# Use passed environment or fall back to self.env
env = environment if environment is not None else self.env
try:
with open(soundIconPath + "/soundicons.conf", "r") as siConfig:
while True:
line = siConfig.readline()
if not line:
break
line = line.replace("\n", "")
if line.replace(" ", "") == "":
continue
if line.replace(" ", "").startswith("#"):
continue
if line.count("=") != 1:
continue
values = line.split("=")
sound_icon = values[0].upper()
values[1] = values[1].replace("'", "")
values[1] = values[1].replace('"', "")
sound_icon_file = ""
if os.path.exists(values[1]):
sound_icon_file = values[1]
else:
if not soundIconPath.endswith("/"):
soundIconPath += "/"
if os.path.exists(soundIconPath + values[1]):
sound_icon_file = soundIconPath + values[1]
env["soundIcons"][sound_icon] = sound_icon_file
env["runtime"]["DebugManager"].write_debug_out(
"SoundIcon: " + sound_icon + "." + sound_icon_file,
debug.DebugLevel.INFO,
on_any_level=True,
)
except (IOError, OSError) as e:
env["runtime"]["DebugManager"].write_debug_out(
"load_sound_icons: failed to load sound icons from "
+ soundIconPath
+ ". Error: "
+ str(e),
debug.DebugLevel.ERROR,
)
def get_settings_file(self):
return self.settings_file
def set_settings_file(self, settings_file):
if not os.path.exists(settings_file):
return
if not os.access(settings_file, os.R_OK):
return
self.settings_file = settings_file
def load_settings(self, setting_config_path):
if not os.path.exists(setting_config_path):
return False
if not os.access(setting_config_path, os.R_OK):
return False
self.env["settings"] = ConfigParser()
try:
self.env["settings"].read(setting_config_path)
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"settings_manager load_settings: Error reading config file: "
+ str(e),
debug.DebugLevel.ERROR,
)
return False
self.set_settings_file(setting_config_path)
return True
def save_settings(self, setting_config_path):
# set opt dict here
# save file
try:
# print('file: ',setting_config_path)
for section, settings in self.settingArgDict.items():
for setting, value in settings.items():
# print(section, setting, value)
self.env["settings"].set(section, setting, value)
# print('full',self.env['settings'])
config_file = open(setting_config_path, "w")
self.env["settings"].write(config_file)
config_file.close()
os.chmod(setting_config_path, 0o644)
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"save_settings: save settingsfile:"
+ setting_config_path
+ "failed. Error:"
+ str(e),
debug.DebugLevel.ERROR,
)
def set_setting(self, section, setting, value):
self.set_option_arg_dict(section, setting, value)
# self.env['settings'].set(section, setting, value)
def get_setting(self, section, setting):
value = ""
try:
value = self.settingArgDict[section][setting]
return value
except Exception as e:
pass
try:
value = self.env["settings"].get(section, setting)
except Exception as e:
value = str(self.settings[section][setting])
return value
def get_setting_as_int(self, section, setting):
value = 0
try:
value = int(self.settingArgDict[section][setting])
return value
except Exception as e:
pass
try:
value = self.env["settings"].getint(section, setting)
except Exception as e:
value = self.settings[section][setting]
return value
def get_setting_as_float(self, section, setting):
value = 0.0
try:
value = float(self.settingArgDict[section][setting])
return value
except Exception as e:
pass
try:
value = self.env["settings"].getfloat(section, setting)
except Exception as e:
value = self.settings[section][setting]
return value
def get_setting_as_bool(self, section, setting):
value = False
try:
value = self.settingArgDict[section][setting].upper() in [
"1",
"YES",
"JA",
"TRUE",
]
return value
except Exception as e:
pass
try:
value = self.env["settings"].getboolean(section, setting)
except Exception as e:
value = self.settings[section][setting]
return value
def load_driver(self, driverName, driverType):
# Map runtime keys to actual directory names
driver_dir_map = {
"InputDriver": "inputDriver",
"ScreenDriver": "screenDriver",
"SpeechDriver": "speechDriver",
"SoundDriver": "soundDriver",
"RemoteDriver": "remoteDriver",
}
driver_dir = driver_dir_map.get(driverType, driverType)
try:
if self.env["runtime"][driverType] is not None:
self.env["runtime"][driverType].shutdown(self.env)
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"settings_manager load_driver: Error shutting down driver: "
+ str(e),
debug.DebugLevel.ERROR,
)
try:
driver_mod = module_utils.import_module(
driverName,
fenrir_path + "/" + driver_dir + "/" + driverName + ".py",
)
self.env["runtime"][driverType] = driver_mod.driver()
self.env["runtime"][driverType].initialize(self.env)
self.env["runtime"]["DebugManager"].write_debug_out(
"Loading Driver " + driverType + " (" + driverName + ") OK",
debug.DebugLevel.INFO,
on_any_level=True,
)
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"Loading Driver "
+ driverType
+ " ("
+ driverName
+ ") FAILED:"
+ str(e),
debug.DebugLevel.ERROR,
)
try:
driver_mod = module_utils.import_module(
driverName,
fenrir_path + "/" + driver_dir + "/dummyDriver.py",
)
self.env["runtime"][driverType] = driver_mod.driver()
self.env["runtime"][driverType].initialize(self.env)
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"(fallback) Loading Driver "
+ driverType
+ " (dummyDriver) FAILED:"
+ str(e),
debug.DebugLevel.ERROR,
)
def shutdown_driver(self, driverType):
try:
self.env["runtime"][driverType].shutdown()
except Exception as e:
pass
del self.env["runtime"][driverType]
def set_fenrir_keys(self, keys):
keys = keys.upper()
key_list = keys.split(",")
for key in key_list:
if key not in self.env["input"]["fenrir_key"]:
self.env["input"]["fenrir_key"].append(key)
def set_script_keys(self, keys):
keys = keys.upper()
key_list = keys.split(",")
for key in key_list:
if key not in self.env["input"]["script_key"]:
self.env["input"]["script_key"].append(key)
def reset_setting_arg_dict(self):
self.settingArgDict = {}
self.env["runtime"]["OutputManager"].reset_SpeechDriver()
def set_option_arg_dict(self, section, setting, value):
# section = section.lower()
# setting = setting.lower()
try:
e = self.settingArgDict[section]
except KeyError:
self.settingArgDict[section] = {}
try:
t = self.settings[section][setting]
except Exception as e:
print(section, setting, "not found")
return
try:
v = value # Initialize v with the original value
if isinstance(self.settings[section][setting], str):
v = str(value)
elif isinstance(self.settings[section][setting], bool):
if value not in ["True", "False"]:
raise ValueError(
"could not convert string to bool: " + value
)
v = value == "True"
elif isinstance(self.settings[section][setting], int):
v = int(value)
elif isinstance(self.settings[section][setting], float):
v = float(value)
# Content validation for critical settings
self._validate_setting_value(section, setting, v)
self.settingArgDict[section][setting] = str(value)
except Exception as e:
print(
"settings_manager:set_option_arg_dict:Datatype missmatch: "
+ section
+ "#"
+ setting
+ "="
+ str(value)
+ " Error:"
+ str(e)
)
# self.env['runtime']['DebugManager'].write_debug_out('settings_manager:set_option_arg_dict:Datatype
# missmatch: '+ section + '#' + setting + '=' + value + ' Error:'
# + str(e), debug.DebugLevel.ERROR)
return
def _validate_setting_value(self, section, setting, value):
"""Validate setting values for critical screen reader functionality.
Only validates settings that could cause crashes or accessibility issues.
Invalid values raise ValueError which is caught by the calling method.
"""
# Speech settings validation - critical for accessibility
if section == "speech":
if setting == "rate":
if not (0.0 <= value <= 3.0):
raise ValueError(
f"Speech rate must be between 0.0 and 3.0, got {value}"
)
elif setting == "pitch":
if not (0.0 <= value <= 2.0):
raise ValueError(
f"Speech pitch must be between 0.0 and 2.0, got {value}"
)
elif setting == "volume":
if not (0.0 <= value <= 1.5):
raise ValueError(
f"Speech volume must be between 0.0 and 1.5, got {value}"
)
elif setting == "driver":
valid_drivers = [
"speechdDriver",
"genericDriver",
"dummyDriver",
]
if value not in valid_drivers:
raise ValueError(
f"Invalid speech driver: {value}. Valid options: {valid_drivers}"
)
# Sound settings validation
elif section == "sound":
if setting == "volume":
if not (0.0 <= value <= 1.5):
raise ValueError(
f"Sound volume must be between 0.0 and 1.5, got {value}"
)
elif setting == "driver":
valid_drivers = [
"genericDriver",
"gstreamerDriver",
"dummyDriver",
]
if value not in valid_drivers:
raise ValueError(
f"Invalid sound driver: {value}. Valid options: {valid_drivers}"
)
# Screen settings validation
elif section == "screen":
if setting == "driver":
valid_drivers = ["vcsaDriver", "ptyDriver", "dummyDriver"]
if value not in valid_drivers:
raise ValueError(
f"Invalid screen driver: {value}. Valid options: {valid_drivers}"
)
# Input settings validation
elif section == "keyboard":
if setting == "driver":
valid_drivers = [
"evdevDriver",
"x11Driver",
"dummyDriver",
]
if value not in valid_drivers:
raise ValueError(
f"Invalid input driver: {value}. Valid options: {valid_drivers}"
)
# General settings validation
elif section == "general":
if setting == "debug_level":
if not (0 <= value <= 3):
raise ValueError(
f"Debug level must be between 0 and 3, got {value}"
)
def parse_setting_args(self, settingArgs):
if settingArgs is None:
return
for optionElem in settingArgs.split(";"):
setting_val_list = []
section_option_list = []
section = ""
option = ""
value = ""
setting_val_list = optionElem.split("=", 1)
if len(setting_val_list) != 2:
continue
if "#" in setting_val_list[0]:
section_option_list = setting_val_list[0].split("#", 1)
elif "." in setting_val_list[0]:
section_option_list = setting_val_list[0].split(".", 1)
elif "," in setting_val_list[0]:
section_option_list = setting_val_list[0].split(",", 1)
elif "!" in setting_val_list[0]:
section_option_list = setting_val_list[0].split("!", 1)
else:
continue
if len(section_option_list) != 2:
continue
section = str(section_option_list[0])
option = str(section_option_list[1])
value = str(setting_val_list[1])
self.set_option_arg_dict(section, option, value)
def init_fenrir_config(
self, cliArgs, fenrir_manager=None, environment=environment.environment
):
settings_root = "/etc/fenrirscreenreader/"
settings_file = cliArgs.setting
sound_root = "/usr/share/sounds/fenrirscreenreader/"
# get fenrir settings root
if not os.path.exists(settings_root):
if os.path.exists(fenrir_path + "/../../config/"):
settings_root = fenrir_path + "/../../config/"
else:
return None
# get settings file
if settings_file is None or not os.path.exists(settings_file):
if os.path.exists(settings_root + "/settings/settings.conf"):
settings_file = settings_root + "/settings/settings.conf"
else:
return None
# get sound themes root
if not os.path.exists(sound_root):
if os.path.exists(fenrir_path + "/../../config/sound/"):
sound_root = fenrir_path + "/../../config/sound/"
environment["runtime"]["SettingsManager"] = self
environment["runtime"]["SettingsManager"].initialize(environment)
valid_config = environment["runtime"]["SettingsManager"].load_settings(
settings_file
)
if not valid_config:
return None
if cliArgs.options != "":
self.parse_setting_args(cliArgs.options)
if cliArgs.debug:
self.set_setting("general", "debug_level", 3)
if cliArgs.print:
self.set_setting("general", "debug_level", 3)
self.set_setting("general", "debug_mode", "PRINT")
if cliArgs.x11:
self.set_setting("screen", "driver", "ptyDriver")
self.set_setting("keyboard", "driver", "x11Driver")
if cliArgs.x11_window_id:
self.set_setting(
"keyboard", "x11_window_id", cliArgs.x11_window_id
)
self.set_fenrir_keys(self.get_setting("general", "fenrir_keys"))
self.set_script_keys(self.get_setting("general", "script_keys"))
environment["runtime"]["DebugManager"] = debugManager.DebugManager(
self.env["runtime"]["SettingsManager"].get_setting(
"general", "debug_file"
)
)
environment["runtime"]["DebugManager"].initialize(environment)
if cliArgs.force_all_screens:
environment["runtime"]["force_all_screens"] = True
if cliArgs.ignore_screen:
current_ignore_screen = self.get_setting("screen", "ignore_screen")
if current_ignore_screen:
ignore_screens = (
current_ignore_screen.split(",") + cliArgs.ignore_screen
)
else:
ignore_screens = cliArgs.ignore_screen
self.set_setting(
"screen", "ignore_screen", ",".join(ignore_screens)
)
if not os.path.exists(
self.get_setting("sound", "theme") + "/soundicons.conf"
):
if os.path.exists(sound_root + self.get_setting("sound", "theme")):
self.set_setting(
"sound",
"theme",
sound_root + self.get_setting("sound", "theme"),
)
if os.path.exists(
self.get_setting("sound", "theme") + "/soundicons.conf"
):
environment["runtime"]["SettingsManager"].load_sound_icons(
self.get_setting("sound", "theme"), environment
)
else:
environment["runtime"]["SettingsManager"].load_sound_icons(
self.get_setting("sound", "theme"), environment
)
environment["runtime"][
"PunctuationManager"
] = punctuationManager.PunctuationManager()
environment["runtime"]["PunctuationManager"].initialize(environment)
environment["runtime"]["TextManager"] = textManager.TextManager()
environment["runtime"]["TextManager"].initialize(environment)
if not os.path.exists(
self.get_setting("general", "punctuation_profile")
):
if os.path.exists(
settings_root
+ "punctuation/"
+ self.get_setting("general", "punctuation_profile")
):
self.set_setting(
"general",
"punctuation_profile",
settings_root
+ "punctuation/"
+ self.get_setting("general", "punctuation_profile"),
)
environment["runtime"]["PunctuationManager"].load_dicts(
self.get_setting("general", "punctuation_profile")
)
if os.path.exists(
settings_root
+ "punctuation/"
+ self.get_setting("general", "punctuation_profile")
+ ".conf"
):
self.set_setting(
"general",
"punctuation_profile",
settings_root
+ "punctuation/"
+ self.get_setting("general", "punctuation_profile")
+ ".conf",
)
environment["runtime"]["PunctuationManager"].load_dicts(
self.get_setting("general", "punctuation_profile")
)
else:
environment["runtime"]["PunctuationManager"].load_dicts(
self.get_setting("general", "punctuation_profile")
)
if fenrir_manager:
environment["runtime"]["FenrirManager"] = fenrir_manager
environment["runtime"]["MemoryManager"] = memoryManager.MemoryManager()
environment["runtime"]["MemoryManager"].initialize(environment)
environment["runtime"][
"AttributeManager"
] = attributeManager.AttributeManager()
environment["runtime"]["AttributeManager"].initialize(environment)
environment["runtime"]["EventManager"] = eventManager.EventManager()
environment["runtime"]["EventManager"].initialize(environment)
environment["runtime"][
"ProcessManager"
] = processManager.ProcessManager()
environment["runtime"]["ProcessManager"].initialize(environment)
environment["runtime"]["OutputManager"] = outputManager.OutputManager()
environment["runtime"]["OutputManager"].initialize(environment)
environment["runtime"]["InputManager"] = inputManager.InputManager()
environment["runtime"]["InputManager"].initialize(environment)
environment["runtime"]["ScreenManager"] = screenManager.ScreenManager()
environment["runtime"]["ScreenManager"].initialize(environment)
environment["runtime"][
"CommandManager"
] = commandManager.CommandManager()
environment["runtime"]["CommandManager"].initialize(environment)
environment["runtime"]["HelpManager"] = helpManager.HelpManager()
environment["runtime"]["HelpManager"].initialize(environment)
environment["runtime"]["RemoteManager"] = remoteManager.RemoteManager()
environment["runtime"]["RemoteManager"].initialize(environment)
environment["runtime"][
"DiffReviewManager"
] = diffReviewManager.DiffReviewManager()
environment["runtime"]["DiffReviewManager"].initialize(environment)
if not os.path.exists(
self.get_setting("keyboard", "keyboard_layout")
):
if os.path.exists(
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout")
):
self.set_setting(
"keyboard",
"keyboard_layout",
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout"),
)
environment["runtime"]["InputManager"].load_shortcuts(
self.get_setting("keyboard", "keyboard_layout")
)
if os.path.exists(
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout")
+ ".conf"
):
self.set_setting(
"keyboard",
"keyboard_layout",
settings_root
+ "keyboard/"
+ self.get_setting("keyboard", "keyboard_layout")
+ ".conf",
)
environment["runtime"]["InputManager"].load_shortcuts(
self.get_setting("keyboard", "keyboard_layout")
)
else:
environment["runtime"]["InputManager"].load_shortcuts(
self.get_setting("keyboard", "keyboard_layout")
)
environment["runtime"]["CursorManager"] = cursorManager.CursorManager()
environment["runtime"]["CursorManager"].initialize(environment)
environment["runtime"][
"ApplicationManager"
] = applicationManager.ApplicationManager()
environment["runtime"]["ApplicationManager"].initialize(environment)
environment["runtime"]["TextManager"] = textManager.TextManager()
environment["runtime"]["TextManager"].initialize(environment)
environment["runtime"]["TableManager"] = tableManager.TableManager()
environment["runtime"]["TableManager"].initialize(environment)
environment["runtime"][
"BarrierManager"
] = barrierManager.BarrierManager()
environment["runtime"]["BarrierManager"].initialize(environment)
environment["runtime"]["SayAllManager"] = sayAllManager.SayAllManager()
environment["runtime"]["SayAllManager"].initialize(environment)
environment["runtime"]["VmenuManager"] = vmenuManager.VmenuManager()
environment["runtime"]["VmenuManager"].initialize(environment)
environment["runtime"][
"QuickMenuManager"
] = quickMenuManager.QuickMenuManager()
environment["runtime"]["QuickMenuManager"].initialize(environment)
environment["runtime"][
"ReadAllManager"
] = readAllManager.ReadAllManager()
environment["runtime"]["ReadAllManager"].initialize(environment)
# only possible after having input and screen managers with clean
# buffer
environment["runtime"]["InputManager"].write_event_buffer()
environment["runtime"]["InputManager"].handle_device_grab(force=True)
environment["runtime"]["DebugManager"].write_debug_out(
r"/-------environment-------/",
debug.DebugLevel.INFO,
on_any_level=True,
)
environment["runtime"]["DebugManager"].write_debug_out(
str(environment), debug.DebugLevel.INFO, on_any_level=True
)
environment["runtime"]["DebugManager"].write_debug_out(
r"/-------settings.conf-------/",
debug.DebugLevel.INFO,
on_any_level=True,
)
environment["runtime"]["DebugManager"].write_debug_out(
str(environment["settings"]._sections),
debug.DebugLevel.INFO,
on_any_level=True,
)
environment["runtime"]["DebugManager"].write_debug_out(
r"/-------self.settingArgDict-------/",
debug.DebugLevel.INFO,
on_any_level=True,
)
environment["runtime"]["DebugManager"].write_debug_out(
str(self.settingArgDict), debug.DebugLevel.INFO, on_any_level=True
)
self.bindingsBackup = environment["bindings"].copy()
return environment
+1 -1
View File
@@ -20,7 +20,7 @@ class sound_driver:
if not self._initialized:
return
self.cancel()
self._is_initialized = False
self._initialized = False
def play_frequence(
self, frequence, duration, adjust_volume=0.0, interrupt=True
@@ -202,6 +202,9 @@ class driver(screenDriver):
self.terminal = None
self.p_pid = -1
self.terminal_lock = threading.Lock() # Synchronize terminal operations
self.stdin_interrupt_lock = threading.Lock()
self.stdin_interrupt_running = False
self.stdin_interrupt_thread = None
signal.signal(signal.SIGWINCH, self.handle_sigwinch)
# Runtime configuration storage
@@ -301,7 +304,31 @@ class driver(screenDriver):
"keyboard", "interrupt_on_key_press_filter"
).strip():
return
self.env["runtime"]["OutputManager"].interrupt_output()
self.start_stdin_interrupt_thread()
def start_stdin_interrupt_thread(self):
with self.stdin_interrupt_lock:
if self.stdin_interrupt_running:
return
self.stdin_interrupt_running = True
self.stdin_interrupt_thread = threading.Thread(
target=self.run_stdin_interrupt,
daemon=True,
)
self.stdin_interrupt_thread.start()
def run_stdin_interrupt(self):
try:
self.env["runtime"]["OutputManager"].interrupt_output()
except Exception as e:
self.env["runtime"]["DebugManager"].write_debug_out(
"ptyDriver interrupt_output_on_stdin_input: "
+ str(e),
debug.DebugLevel.ERROR,
)
finally:
with self.stdin_interrupt_lock:
self.stdin_interrupt_running = False
def handle_stdin_input(self, msg_bytes, event_queue):
if self.synthesize_backspace_shortcut(msg_bytes, event_queue):
@@ -36,7 +36,7 @@ class driver(sound_driver):
self._initialized = _gstreamerAvailable
if not self._initialized:
global _availableError
self.environment["runtime"]["DebugManager"].write_debug_out(
self.env["runtime"]["DebugManager"].write_debug_out(
"Gstreamer not available " + _availableError,
debug.DebugLevel.ERROR,
)