415 lines
16 KiB
Python
415 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Fenrir TTY screen reader
|
|
# By Chrys, Storm Dragon, and contributors.
|
|
|
|
"""
|
|
Remote controll:
|
|
section<space>command<space>parameters
|
|
sections:command,setting
|
|
setting commands:
|
|
- set section#setting=value[;section#setting=value]
|
|
- reset
|
|
command commands:
|
|
- say text to speech
|
|
- interrupt
|
|
examples
|
|
settings:
|
|
settings set section#setting=value[,section#setting=value]
|
|
setting set speech#voice=de
|
|
setting reset
|
|
setting save /path/settings.conf
|
|
command:
|
|
command say this is a test
|
|
command interrupt
|
|
"""
|
|
|
|
|
|
import os
|
|
import time
|
|
|
|
from fenrirscreenreader.core import debug
|
|
from fenrirscreenreader.core.eventData import FenrirEventType
|
|
from fenrirscreenreader.core.i18n import _
|
|
|
|
|
|
class RemoteManager:
|
|
def __init__(self):
|
|
# command controll
|
|
self.commandConst = "COMMAND "
|
|
self.sayConst = "SAY "
|
|
self.vmenuConst = "VMENU "
|
|
self.resetVmenuConst = "RESETVMENU"
|
|
self.interruptConst = "INTERRUPT"
|
|
self.quitAppConst = "QUITAPPLICATION"
|
|
self.tempDisableSpeechConst = "TEMPDISABLESPEECH"
|
|
self.defineWindowConst = "WINDOW "
|
|
self.resetWindowConst = "RESETWINDOW"
|
|
self.setClipboardConst = "CLIPBOARD "
|
|
self.exportClipboardConst = "EXPORTCLIPBOARD"
|
|
# setting controll
|
|
self.settingConst = "SETTING "
|
|
self.setSettingConst = "SET "
|
|
self.saveAsSettingConst = "SAVEAS "
|
|
self.saveSettingConst = "SAVE"
|
|
self.resetSettingConst = "RESET"
|
|
|
|
def initialize(self, environment):
|
|
self.env = environment
|
|
self.env["runtime"]["SettingsManager"].load_driver(
|
|
self.env["runtime"]["SettingsManager"].get_setting(
|
|
"remote", "driver"
|
|
),
|
|
"RemoteDriver",
|
|
)
|
|
|
|
def shutdown(self):
|
|
self.env["runtime"]["SettingsManager"].shutdown_driver("RemoteDriver")
|
|
|
|
def handle_settings_change_with_response(self, settings_text):
|
|
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
|
"remote", "enable_settings_remote"
|
|
):
|
|
return {
|
|
"success": False,
|
|
"message": "Settings remote control is disabled",
|
|
}
|
|
|
|
upper_settings_text = settings_text.upper()
|
|
try:
|
|
# set setting
|
|
if upper_settings_text.startswith(self.setSettingConst):
|
|
parameter_text = settings_text[len(self.setSettingConst) :]
|
|
self.set_settings(parameter_text)
|
|
return {
|
|
"success": True,
|
|
"message": f"Setting applied: {parameter_text}",
|
|
}
|
|
# save as setting
|
|
elif upper_settings_text.startswith(self.saveAsSettingConst):
|
|
parameter_text = settings_text[len(self.saveAsSettingConst) :]
|
|
self.save_settings(parameter_text)
|
|
return {
|
|
"success": True,
|
|
"message": f"Settings saved to: {parameter_text}",
|
|
}
|
|
# save setting
|
|
elif upper_settings_text == self.saveSettingConst:
|
|
self.save_settings()
|
|
return {"success": True, "message": "Settings saved"}
|
|
# reset setting
|
|
elif upper_settings_text == self.resetSettingConst:
|
|
self.reset_settings()
|
|
return {
|
|
"success": True,
|
|
"message": "Settings reset to defaults",
|
|
}
|
|
else:
|
|
return {
|
|
"success": False,
|
|
"message": f"Unknown settings command: {settings_text}",
|
|
}
|
|
except Exception as e:
|
|
return {"success": False, "message": f"Settings error: {str(e)}"}
|
|
|
|
def handle_settings_change(self, settings_text):
|
|
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
|
"remote", "enable_settings_remote"
|
|
):
|
|
return
|
|
|
|
upper_settings_text = settings_text.upper()
|
|
# set setting
|
|
if upper_settings_text.startswith(self.setSettingConst):
|
|
parameter_text = settings_text[len(self.setSettingConst) :]
|
|
self.set_settings(parameter_text)
|
|
# save as setting
|
|
elif upper_settings_text.startswith(self.saveAsSettingConst):
|
|
parameter_text = settings_text[len(self.saveAsSettingConst) :]
|
|
self.save_settings(parameter_text)
|
|
# save setting
|
|
elif upper_settings_text == self.saveSettingConst:
|
|
self.save_settings()
|
|
# reset setting
|
|
elif upper_settings_text == self.resetSettingConst:
|
|
self.reset_settings()
|
|
|
|
def handle_command_execution_with_response(self, command_text):
|
|
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
|
"remote", "enable_command_remote"
|
|
):
|
|
return {
|
|
"success": False,
|
|
"message": "Command remote control is disabled",
|
|
}
|
|
|
|
upper_command_text = command_text.upper()
|
|
|
|
try:
|
|
# say
|
|
if upper_command_text.startswith(self.sayConst):
|
|
parameter_text = command_text[len(self.sayConst) :]
|
|
self.say(parameter_text)
|
|
return {
|
|
"success": True,
|
|
"message": f"Speaking: {parameter_text[:50]}{'...' if len(parameter_text) > 50 else ''}",
|
|
}
|
|
# interrupt
|
|
elif upper_command_text == self.interruptConst:
|
|
self.interrupt_speech()
|
|
return {"success": True, "message": "Speech interrupted"}
|
|
# temp disable speech
|
|
elif upper_command_text == self.tempDisableSpeechConst:
|
|
self.temp_disable_speech()
|
|
return {
|
|
"success": True,
|
|
"message": "Speech temporarily disabled",
|
|
}
|
|
# set vmenu
|
|
elif upper_command_text.startswith(self.vmenuConst):
|
|
parameter_text = command_text[len(self.vmenuConst) :]
|
|
self.set_vmenu(parameter_text)
|
|
return {
|
|
"success": True,
|
|
"message": f"VMenu set to: {parameter_text}",
|
|
}
|
|
# reset vmenu
|
|
elif upper_command_text == self.resetVmenuConst:
|
|
self.reset_vmenu()
|
|
return {"success": True, "message": "VMenu reset"}
|
|
# quit fenrir
|
|
elif upper_command_text == self.quitAppConst:
|
|
self.quit_fenrir()
|
|
return {"success": True, "message": "Fenrir shutting down"}
|
|
# define window
|
|
elif upper_command_text.startswith(self.defineWindowConst):
|
|
parameter_text = command_text[len(self.defineWindowConst) :]
|
|
self.define_window(parameter_text)
|
|
return {
|
|
"success": True,
|
|
"message": f"Window defined: {parameter_text}",
|
|
}
|
|
# reset window
|
|
elif upper_command_text == self.resetWindowConst:
|
|
self.reset_window()
|
|
return {"success": True, "message": "Window reset"}
|
|
# set clipboard
|
|
elif upper_command_text.startswith(self.setClipboardConst):
|
|
parameter_text = command_text[len(self.setClipboardConst) :]
|
|
self.set_clipboard(parameter_text)
|
|
return {
|
|
"success": True,
|
|
"message": f"Clipboard set: {parameter_text[:50]}{'...' if len(parameter_text) > 50 else ''}",
|
|
}
|
|
elif upper_command_text.startswith(self.exportClipboardConst):
|
|
self.export_clipboard()
|
|
return {
|
|
"success": True,
|
|
"message": "Clipboard exported to file",
|
|
}
|
|
else:
|
|
return {
|
|
"success": False,
|
|
"message": f"Unknown command: {command_text}",
|
|
}
|
|
except Exception as e:
|
|
return {"success": False, "message": f"Command error: {str(e)}"}
|
|
|
|
def handle_command_execution(self, command_text):
|
|
if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
|
|
"remote", "enable_command_remote"
|
|
):
|
|
return
|
|
|
|
upper_command_text = command_text.upper()
|
|
|
|
# say
|
|
if upper_command_text.startswith(self.sayConst):
|
|
parameter_text = command_text[len(self.sayConst) :]
|
|
self.say(parameter_text)
|
|
# interrupt
|
|
elif upper_command_text == self.interruptConst:
|
|
self.interrupt_speech()
|
|
# temp disable speech
|
|
elif upper_command_text == self.tempDisableSpeechConst:
|
|
self.temp_disable_speech()
|
|
# set vmenu
|
|
elif upper_command_text.startswith(self.vmenuConst):
|
|
parameter_text = command_text[len(self.vmenuConst) :]
|
|
self.set_vmenu(parameter_text)
|
|
# reset vmenu
|
|
elif upper_command_text == self.resetVmenuConst:
|
|
self.reset_vmenu()
|
|
# quit fenrir
|
|
elif upper_command_text == self.quitAppConst:
|
|
self.quit_fenrir()
|
|
# define window
|
|
elif upper_command_text.startswith(self.defineWindowConst):
|
|
parameter_text = command_text[len(self.defineWindowConst) :]
|
|
self.define_window(parameter_text)
|
|
# reset window
|
|
elif upper_command_text == self.resetWindowConst:
|
|
self.reset_window()
|
|
# set clipboard
|
|
elif upper_command_text.startswith(self.setClipboardConst):
|
|
parameter_text = command_text[len(self.setClipboardConst) :]
|
|
self.set_clipboard(parameter_text)
|
|
elif upper_command_text.startswith(self.exportClipboardConst):
|
|
self.export_clipboard()
|
|
|
|
def temp_disable_speech(self):
|
|
self.env["runtime"]["OutputManager"].temp_disable_speech()
|
|
|
|
def set_vmenu(self, vmenu=""):
|
|
self.env["runtime"]["VmenuManager"].set_curr_menu(vmenu)
|
|
|
|
def reset_vmenu(self):
|
|
self.env["runtime"]["VmenuManager"].set_curr_menu()
|
|
|
|
def set_clipboard(self, text=""):
|
|
self.env["runtime"]["MemoryManager"].add_value_to_first_index(
|
|
"clipboardHistory", text
|
|
)
|
|
|
|
def quit_fenrir(self):
|
|
self.env["runtime"]["EventManager"].stop_main_event_loop()
|
|
|
|
def define_window(self, window_text):
|
|
start = {}
|
|
end = {}
|
|
try:
|
|
window_list = window_text.split(" ")
|
|
if len(window_list) < 4:
|
|
return
|
|
start["x"] = int(window_list[0])
|
|
start["y"] = int(window_list[1])
|
|
end["x"] = int(window_list[2])
|
|
end["y"] = int(window_list[3])
|
|
|
|
self.env["runtime"]["CursorManager"].set_window_for_application(
|
|
start, end
|
|
)
|
|
except Exception as e:
|
|
pass
|
|
|
|
def reset_window(self):
|
|
self.env["runtime"]["CursorManager"].clear_window_for_application()
|
|
|
|
def say(self, text):
|
|
if not text:
|
|
return
|
|
if text == "":
|
|
return
|
|
self.env["runtime"]["OutputManager"].speak_text(text)
|
|
|
|
def interrupt_speech(self):
|
|
self.env["runtime"]["OutputManager"].interrupt_output()
|
|
|
|
def export_clipboard(self):
|
|
clipboard_file_path = self.env["runtime"][
|
|
"SettingsManager"
|
|
].get_setting("general", "clipboard_export_path")
|
|
clipboard_file_path = clipboard_file_path.replace(
|
|
"$user", self.env["general"]["curr_user"]
|
|
)
|
|
clipboard_file_path = clipboard_file_path.replace(
|
|
"$USER", self.env["general"]["curr_user"]
|
|
)
|
|
clipboard_file_path = clipboard_file_path.replace(
|
|
"$User", self.env["general"]["curr_user"]
|
|
)
|
|
clipboard_file = open(clipboard_file_path, "w")
|
|
try:
|
|
if self.env["runtime"]["MemoryManager"].is_index_list_empty(
|
|
"clipboardHistory"
|
|
):
|
|
self.env["runtime"]["OutputManager"].present_text(
|
|
_("clipboard empty"), interrupt=True
|
|
)
|
|
return
|
|
clipboard = self.env["runtime"][
|
|
"MemoryManager"
|
|
].get_index_list_element("clipboardHistory")
|
|
# Fenrir will crash if the clipboard variable is type None
|
|
if clipboard is not None:
|
|
clipboard_file.write(clipboard)
|
|
else:
|
|
clipboard_file.write("")
|
|
clipboard_file.close()
|
|
os.chmod(clipboard_file_path, 0o644)
|
|
self.env["runtime"]["OutputManager"].present_text(
|
|
_("clipboard exported to file"), interrupt=True
|
|
)
|
|
except Exception as e:
|
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
|
"export_clipboard_to_file:run: Filepath:"
|
|
+ clipboard_file
|
|
+ " trace:"
|
|
+ str(e),
|
|
debug.DebugLevel.ERROR,
|
|
)
|
|
|
|
def save_settings(self, setting_config_path=None):
|
|
if not setting_config_path:
|
|
setting_config_path = self.env["runtime"][
|
|
"SettingsManager"
|
|
].get_settings_file()
|
|
if setting_config_path == "":
|
|
return
|
|
self.env["runtime"]["SettingsManager"].save_settings(
|
|
setting_config_path
|
|
)
|
|
|
|
def reset_settings(self):
|
|
self.env["runtime"]["SettingsManager"].reset_setting_arg_dict()
|
|
|
|
def set_settings(self, settingsArgs):
|
|
self.env["runtime"]["SettingsManager"].parse_setting_args(settingsArgs)
|
|
self.env["runtime"]["ScreenManager"].update_screen_ignored()
|
|
self.env["runtime"]["InputManager"].handle_device_grab(force=True)
|
|
|
|
def handle_remote_incomming_with_response(self, event_data):
|
|
if not event_data:
|
|
return {"success": False, "message": "No data received"}
|
|
|
|
upper_event_data = event_data.upper()
|
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
|
"RemoteManager:handle_remote_incomming_with_response: event: "
|
|
+ str(event_data),
|
|
debug.DebugLevel.INFO,
|
|
)
|
|
|
|
try:
|
|
if upper_event_data.startswith(self.settingConst):
|
|
settings_text = event_data[len(self.settingConst) :]
|
|
return self.handle_settings_change_with_response(settings_text)
|
|
elif upper_event_data.startswith(self.commandConst):
|
|
command_text = event_data[len(self.commandConst) :]
|
|
return self.handle_command_execution_with_response(
|
|
command_text
|
|
)
|
|
else:
|
|
return {
|
|
"success": False,
|
|
"message": "Unknown command format. Use 'COMMAND ...' or 'SETTING ...'",
|
|
}
|
|
except Exception as e:
|
|
return {"success": False, "message": f"Exception: {str(e)}"}
|
|
|
|
def handle_remote_incomming(self, event_data):
|
|
if not event_data:
|
|
return
|
|
upper_event_data = event_data.upper()
|
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
|
"RemoteManager:handle_remote_incomming: event: " + str(event_data),
|
|
debug.DebugLevel.INFO,
|
|
)
|
|
|
|
if upper_event_data.startswith(self.settingConst):
|
|
settings_text = event_data[len(self.settingConst) :]
|
|
self.handle_settings_change(settings_text)
|
|
elif upper_event_data.startswith(self.commandConst):
|
|
command_text = event_data[len(self.commandConst) :]
|
|
self.handle_command_execution(command_text)
|