#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Fenrir TTY screen reader # By Chrys, Storm Dragon, and contributors. """ Remote controll: sectioncommandparameters 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)