%PDF- %PDF-
Direktori : /lib/python3/dist-packages/orca/ |
Current File : //lib/python3/dist-packages/orca/orca.py |
# Orca # # Copyright 2004-2009 Sun Microsystems Inc. # Copyright 2010-2011 The Orca Team # Copyright 2012 Igalia, S.L. # # This library 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.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., Franklin Street, Fifth Floor, # Boston MA 02110-1301 USA. """The main module for the Orca screen reader.""" __id__ = "$Id$" __version__ = "$Revision$" __date__ = "$Date$" __copyright__ = "Copyright (c) 2004-2009 Sun Microsystems Inc." \ "Copyright (c) 2010-2011 The Orca Team" \ "Copyright (c) 2012 Igalia, S.L." __license__ = "LGPL" import faulthandler import gi import importlib import os import signal import sys gi.require_version("Atspi", "2.0") gi.require_version("Gdk", "3.0") from gi.repository import Atspi from gi.repository import Gdk try: from gi.repository.Gio import Settings a11yAppSettings = Settings(schema_id='org.gnome.desktop.a11y.applications') except Exception: a11yAppSettings = None from . import braille from . import debug from . import event_manager from . import focus_manager from . import logger from . import messages from . import mouse_review from . import orca_modifier_manager from . import orca_state from . import orca_platform from . import script_manager from . import settings from . import settings_manager from . import speech from . import sound from .ax_object import AXObject _logger = logger.getLogger() def onEnabledChanged(gsetting, key): try: enabled = gsetting.get_boolean(key) except Exception: return if key == 'screen-reader-enabled' and not enabled: shutdown() def getSettingsManager(): return settings_manager.getManager() def getLogger(): return _logger EXIT_CODE_HANG = 50 # The user-settings module (see loadUserSettings). # _userSettings = None def deviceChangeHandler(deviceManager, device): """Handles device-* signals.""" source = device.get_source() if source == Gdk.InputSource.KEYBOARD: orca_modifier_manager.getManager().refresh_orca_modifiers("Keyboard change detected.") def loadUserSettings(script=None, inputEvent=None, skipReloadMessage=False): """Loads (and reloads) the user settings module, reinitializing things such as speech if necessary. Returns True to indicate the input event has been consumed. """ debug.printMessage(debug.LEVEL_INFO, 'ORCA: Loading User Settings', True) global _userSettings # Shutdown the output drivers and give them a chance to die. player = sound.getPlayer() player.shutdown() speech.shutdown() braille.shutdown() script_manager.getManager().deactivate() reloaded = False if _userSettings: _profile = settings_manager.getManager().getSetting('activeProfile')[1] try: _userSettings = settings_manager.getManager().getGeneralSettings(_profile) settings_manager.getManager().setProfile(_profile) reloaded = True except ImportError: debug.printException(debug.LEVEL_INFO) except Exception: debug.printException(debug.LEVEL_SEVERE) else: _profile = settings_manager.getManager().profile try: _userSettings = settings_manager.getManager().getGeneralSettings(_profile) except ImportError: debug.printException(debug.LEVEL_INFO) except Exception: debug.printException(debug.LEVEL_SEVERE) if not script: script = script_manager.getManager().getDefaultScript() settings_manager.getManager().loadAppSettings(script) if settings_manager.getManager().getSetting('enableSpeech'): msg = 'ORCA: About to enable speech' debug.printMessage(debug.LEVEL_INFO, msg, True) try: speech.init() if reloaded and not skipReloadMessage: script.speakMessage(messages.SETTINGS_RELOADED) except Exception: debug.printException(debug.LEVEL_SEVERE) else: msg = 'ORCA: Speech is not enabled in settings' debug.printMessage(debug.LEVEL_INFO, msg, True) if settings_manager.getManager().getSetting('enableBraille'): msg = 'ORCA: About to enable braille' debug.printMessage(debug.LEVEL_INFO, msg, True) try: braille.init(event_manager.getManager().process_braille_event) except Exception: debug.printException(debug.LEVEL_WARNING) msg = 'ORCA: Could not initialize connection to braille.' debug.printMessage(debug.LEVEL_WARNING, msg, True) else: msg = 'ORCA: Braille is not enabled in settings' debug.printMessage(debug.LEVEL_INFO, msg, True) if settings_manager.getManager().getSetting('enableMouseReview'): mouse_review.getReviewer().activate() else: mouse_review.getReviewer().deactivate() if settings_manager.getManager().getSetting('enableSound'): player.init() # Handle the case where a change was made in the Orca Preferences dialog. orca_modifier_manager.getManager().refresh_orca_modifiers("Loading user settings.") event_manager.getManager().activate() script_manager.getManager().activate() debug.printMessage(debug.LEVEL_INFO, 'ORCA: User Settings Loaded', True) return True def _showPreferencesUI(script, prefs): if orca_state.orcaOS: orca_state.orcaOS.showGUI() return try: module = importlib.import_module('.orca_gui_prefs', 'orca') except Exception: debug.printException(debug.LEVEL_SEVERE) return uiFile = os.path.join(orca_platform.datadir, orca_platform.package, "ui", "orca-setup.ui") orca_state.orcaOS = module.OrcaSetupGUI(uiFile, "orcaSetupWindow", prefs) orca_state.orcaOS.init(script) orca_state.orcaOS.showGUI() def showAppPreferencesGUI(script=None, inputEvent=None): """Displays the user interface to configure the settings for a specific applications within Orca and set up those app-specific user preferences using a GUI. Returns True to indicate the input event has been consumed. """ prefs = {} for key in settings.userCustomizableSettings: prefs[key] = settings_manager.getManager().getSetting(key) if script is None: script = script_manager.getManager().getActiveScript() _showPreferencesUI(script, prefs) return True def showPreferencesGUI(script=None, inputEvent=None): """Displays the user interface to configure Orca and set up user preferences using a GUI. Returns True to indicate the input event has been consumed. """ prefs = settings_manager.getManager().getGeneralSettings(settings_manager.getManager().profile) script = script_manager.getManager().getDefaultScript() _showPreferencesUI(script, prefs) return True def quitOrca(script=None, inputEvent=None): """Quit Orca. Check if the user wants to confirm this action. If so, show the confirmation GUI otherwise just shutdown. Returns True to indicate the input event has been consumed. """ shutdown() return True def showFindGUI(script=None, inputEvent=None): """Displays the user interface to perform an Orca Find. Returns True to indicate the input event has been consumed. """ try: module = importlib.import_module('.orca_gui_find', 'orca') module.showFindUI() except Exception: debug.printException(debug.LEVEL_SEVERE) # If True, this module has been initialized. # _initialized = False def init(): """Initialize the orca module, which initializes the speech and braille modules. Also builds up the application list, registers for AT-SPI events, and creates scripts for all known applications. Returns True if the initialization procedure has run, or False if this module has already been initialized. """ debug.printMessage(debug.LEVEL_INFO, 'ORCA: Initializing', True) global _initialized if _initialized and settings_manager.getManager().isScreenReaderServiceEnabled(): debug.printMessage(debug.LEVEL_INFO, 'ORCA: Already initialized', True) return False # Do not hang on initialization if we can help it. # if settings.timeoutCallback and (settings.timeoutTime > 0): signal.signal(signal.SIGALRM, settings.timeoutCallback) signal.alarm(settings.timeoutTime) loadUserSettings() if settings.timeoutCallback and (settings.timeoutTime > 0): signal.alarm(0) _initialized = True # In theory, we can do this through dbus. In practice, it fails to # work sometimes. Until we know why, we need to leave this as-is # so that we respond when gnome-control-center is used to stop Orca. if a11yAppSettings: a11yAppSettings.connect('changed', onEnabledChanged) debug.printMessage(debug.LEVEL_INFO, 'ORCA: Initialized', True) return True def start(): """Starts Orca.""" debug.printMessage(debug.LEVEL_INFO, 'ORCA: Starting', True) if not _initialized: init() # Do not hang on startup if we can help it. # if settings.timeoutCallback and (settings.timeoutTime > 0): signal.signal(signal.SIGALRM, settings.timeoutCallback) signal.alarm(settings.timeoutTime) if settings.timeoutCallback and (settings.timeoutTime > 0): signal.alarm(0) # Event handlers for input devices being plugged in/unplugged. display = Gdk.Display.get_default() devmanager=display.get_device_manager() devmanager.connect("device-added", deviceChangeHandler) devmanager.connect("device-removed", deviceChangeHandler) Gdk.notify_startup_complete() msg = 'ORCA: Startup complete notification made' debug.printMessage(debug.LEVEL_INFO, msg, True) debug.printMessage(debug.LEVEL_INFO, 'ORCA: Starting Atspi main event loop', True) Atspi.event_main() def die(exitCode=1): pid = os.getpid() if exitCode == EXIT_CODE_HANG: # Someting is hung and we wish to abort. os.kill(pid, signal.SIGKILL) return shutdown() sys.exit(exitCode) if exitCode > 1: os.kill(pid, signal.SIGTERM) def timeout(signum=None, frame=None): msg = 'TIMEOUT: something has hung. Aborting.' debug.printMessage(debug.LEVEL_SEVERE, msg, True) debug.printStack(debug.LEVEL_SEVERE) debug.examineProcesses(force=True) die(EXIT_CODE_HANG) def shutdown(script=None, inputEvent=None): """Exits Orca. Unregisters any event listeners and cleans up. Returns True if the shutdown procedure ran or False if this module was never initialized. """ debug.printMessage(debug.LEVEL_INFO, 'ORCA: Shutting down', True) global _initialized if not _initialized: return False # Try to say goodbye, but be defensive if something has hung. # if settings.timeoutCallback and (settings.timeoutTime > 0): signal.signal(signal.SIGALRM, settings.timeoutCallback) signal.alarm(settings.timeoutTime) script = script_manager.getManager().getActiveScript() if script is not None: script.presentationInterrupt() script.presentMessage(messages.STOP_ORCA, resetStyles=False) # Pause event queuing first so that it clears its queue and will not accept new # events. Then let the script manager unregister script event listeners as well # as key grabs. Finally deactivate the event manager, which will also cause the # Atspi.Device to be set to None. event_manager.getManager().pauseQueuing(True, True, "Shutting down.") script_manager.getManager().deactivate() event_manager.getManager().deactivate() # Shutdown all the other support. # if settings.enableSpeech: speech.shutdown() if settings.enableBraille: braille.shutdown() if settings.enableSound: player = sound.getPlayer() player.shutdown() if settings.timeoutCallback and (settings.timeoutTime > 0): signal.alarm(0) _initialized = False orca_modifier_manager.getManager().unset_orca_modifiers("Shutting down.") debug.printMessage(debug.LEVEL_INFO, 'ORCA: Quitting Atspi main event loop', True) Atspi.event_quit() debug.printMessage(debug.LEVEL_INFO, 'ORCA: Shutdown complete', True) return True exitCount = 0 def shutdownOnSignal(signum, frame): global exitCount signalString = f'({signal.strsignal(signum)})' msg = f"ORCA: Shutting down and exiting due to signal={signum} {signalString}" debug.printMessage(debug.LEVEL_INFO, msg, True) # Well...we'll try to exit nicely, but if we keep getting called, # something bad is happening, so just quit. # if exitCount: die(signum) else: exitCount += 1 # Try to do a graceful shutdown if we can. # if settings.timeoutCallback and (settings.timeoutTime > 0): signal.signal(signal.SIGALRM, settings.timeoutCallback) signal.alarm(settings.timeoutTime) try: if _initialized: shutdown() else: # We always want to try to shutdown speech since the # speech servers are very persistent about living. # speech.shutdown() shutdown() cleanExit = True except Exception: cleanExit = False if settings.timeoutCallback and (settings.timeoutTime > 0): signal.alarm(0) if not cleanExit: die(EXIT_CODE_HANG) def crashOnSignal(signum, frame): signalString = f'({signal.strsignal(signum)})' msg = f"ORCA: Shutting down and exiting due to signal={signum} {signalString}" debug.printMessage(debug.LEVEL_SEVERE, msg, True) debug.printStack(debug.LEVEL_SEVERE) orca_modifier_manager.getManager().unset_orca_modifiers(f"Shutting down: {signalString}.") script = script_manager.getManager().getActiveScript() if script is not None: script.presentationInterrupt() script.presentMessage(messages.STOP_ORCA, resetStyles=False) sys.exit(1) def main(): """The main entry point for Orca. The exit codes for Orca will loosely be based on signals, where the exit code will be the signal used to terminate Orca (if a signal was used). Otherwise, an exit code of 0 means normal completion and an exit code of 50 means Orca exited because of a hang.""" msg = f"ORCA: Launching version {orca_platform.version}" if orca_platform.revision: msg += f" (rev {orca_platform.revision})" atspiVersion = Atspi.get_version() msg += f" AT-SPI2 version: {atspiVersion[0]}.{atspiVersion[1]}.{atspiVersion[2]}" sessionType = os.environ.get('XDG_SESSION_TYPE') or "" sessionDesktop = os.environ.get('XDG_SESSION_DESKTOP') or "" session = "%s %s".strip() % (sessionType, sessionDesktop) if session: msg += f" Session: {session}" debug.printMessage(debug.LEVEL_INFO, msg, True) if debug.debugFile and os.path.exists(debug.debugFile.name): faulthandler.enable(file=debug.debugFile, all_threads=True) else: faulthandler.enable(all_threads=False) # Method to call when we think something might be hung. # settings.timeoutCallback = timeout # Various signal handlers we want to listen for. # signal.signal(signal.SIGHUP, shutdownOnSignal) signal.signal(signal.SIGINT, shutdownOnSignal) signal.signal(signal.SIGTERM, shutdownOnSignal) signal.signal(signal.SIGQUIT, shutdownOnSignal) signal.signal(signal.SIGSEGV, crashOnSignal) debug.printMessage(debug.LEVEL_INFO, "ORCA: Enabling accessibility (if needed).", True) if not settings_manager.getManager().isAccessibilityEnabled(): settings_manager.getManager().setAccessibility(True) debug.printMessage(debug.LEVEL_INFO, "ORCA: Initializing.", True) init() debug.printMessage(debug.LEVEL_INFO, "ORCA: Initialized.", True) try: script = script_manager.getManager().getDefaultScript() script.presentMessage(messages.START_ORCA) except Exception: debug.printException(debug.LEVEL_SEVERE) window = focus_manager.getManager().find_active_window() if window and not focus_manager.getManager().get_locus_of_focus(): app = AXObject.get_application(window) # TODO - JD: Consider having the focus tracker update the active script. script = script_manager.getManager().getScript(app, window) script_manager.getManager().setActiveScript(script, "Launching.") focus_manager.getManager().set_active_window( window, app, set_window_as_focus=True, notify_script=True) # TODO - JD: Consider having the focus tracker update the active script. focusedObject = focus_manager.getManager().find_focused_object() if focusedObject: focus_manager.getManager().set_locus_of_focus(None, focusedObject) script = script_manager.getManager().getScript( AXObject.get_application(focusedObject), focusedObject) script_manager.getManager().setActiveScript(script, "Found focused object.") try: msg = "ORCA: Starting ATSPI registry." debug.printMessage(debug.LEVEL_INFO, msg, True) start() # waits until we stop the registry except Exception: msg = "ORCA: Exception starting ATSPI registry." debug.printMessage(debug.LEVEL_SEVERE, msg, True) die(EXIT_CODE_HANG) return 0 if __name__ == "__main__": sys.exit(main())