# 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. # pylint: disable=too-many-statements # pylint: disable=wrong-import-position """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 os import signal import sys import gi gi.require_version("Atspi", "2.0") gi.require_version("Gdk", "3.0") from gi.repository import Atspi from gi.repository import Gdk from gi.repository import Gio from gi.repository import GLib from . import braille from . import braille_presenter from . import clipboard from . import dbus_service from . import debug from . import debugging_tools_manager from . import event_manager from . import focus_manager from . import input_event_manager from . import messages from . import mouse_review from . import orca_modifier_manager from . import script_manager from . import settings from . import settings_manager from . import speech_and_verbosity_manager from . import sound from . import systemd from .ax_utilities import AXUtilities def load_user_settings(script=None, skip_reload_message=False, is_reload=True): """(Re)Loads the user settings module, re-initializing things such as speech if necessary.""" debug.print_message(debug.LEVEL_INFO, 'ORCA: Loading User Settings', True) if is_reload: sound.get_player().shutdown() speech_and_verbosity_manager.get_manager().shutdown_speech() braille.shutdown() mouse_review.get_reviewer().deactivate() event_manager.get_manager().pause_queuing(True, True, "Loading user settings.") if is_reload: _profile = settings_manager.get_manager().get_setting('activeProfile')[1] settings_manager.get_manager().set_profile(_profile) if script is None: script = script_manager.get_manager().get_default_script() settings_manager.get_manager().load_app_settings(script) if settings_manager.get_manager().get_setting('enableSpeech'): speech_and_verbosity_manager.get_manager().start_speech() if is_reload and not skip_reload_message: script.speak_message(messages.SETTINGS_RELOADED) if braille_presenter.get_presenter().get_braille_is_enabled(): braille.init(input_event_manager.get_manager().process_braille_event) # TODO - JD: This ultimately belongs in an extension manager. mouse_reviewer = mouse_review.get_reviewer() if mouse_reviewer.get_is_enabled(): mouse_reviewer.activate() if settings_manager.get_manager().get_setting('enableSound'): sound.get_player().init() # Handle the case where a change was made in the Orca Preferences dialog. orca_modifier_manager.get_manager().refresh_orca_modifiers("Loading user settings.") event_manager.get_manager().pause_queuing(False, False, "User settings loaded.") debug.print_message(debug.LEVEL_INFO, "ORCA: User Settings Loaded", True) manager = debugging_tools_manager.get_manager() manager.print_session_details() manager.print_running_applications(force=False) def shutdown(script=None, _event=None, _signum=None): """Exits Orca. Returns True if shutdown ran to completion.""" if hasattr(shutdown, "in_progress"): msg = "ORCA: Shutdown already in progress" debug.print_message(debug.LEVEL_INFO, msg, True) return False systemd.get_manager().notify_stopping() shutdown.in_progress = True def _timeout(_signum=None, _frame=None): msg = "TIMEOUT: something has hung. Aborting." debug.print_message(debug.LEVEL_SEVERE, msg, True) debugging_tools_manager.get_manager().print_running_applications(force=True) os.kill(os.getpid(), signal.SIGKILL) debug.print_message(debug.LEVEL_INFO, "ORCA: Shutting down", True) signal.signal(signal.SIGALRM, _timeout) signal.alarm(5) dbus_service.get_remote_controller().shutdown() orca_modifier_manager.get_manager().unset_orca_modifiers("Shutting down.") if script := script_manager.get_manager().get_active_script(): if speech_and_verbosity_manager.get_manager().get_current_server(): script.interrupt_presentation() script.present_message(messages.STOP_ORCA, reset_styles=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.get_manager().pause_queuing(True, True, "Shutting down.") script_manager.get_manager().deactivate() event_manager.get_manager().deactivate() # TODO - JD: This ultimately belongs in an extension manager. clipboard.get_presenter().deactivate() mouse_review.get_reviewer().deactivate() # Shutdown all the other support. # if settings.enableSpeech: speech_and_verbosity_manager.get_manager().shutdown_speech() if braille_presenter.get_presenter().get_braille_is_enabled(): braille.shutdown() if settings.enableSound: player = sound.get_player() player.shutdown() signal.alarm(0) debug.print_message(debug.LEVEL_INFO, 'ORCA: Quitting Atspi main event loop', True) Atspi.event_quit() debug.print_message(debug.LEVEL_INFO, 'ORCA: Shutdown complete', True) return True def main(): """The main entry point for Orca.""" def _reload_on_signal(signum, frame): signal_string = f'({signal.strsignal(signum)})' tokens = [f"ORCA: Reloading due to signal={signum} {signal_string}", frame] debug.print_tokens(debug.LEVEL_INFO, tokens, True) systemd.get_manager().notify_reloading() load_user_settings() systemd.get_manager().notify_ready() def _shutdown_on_signal(signum, frame): signal_string = f'({signal.strsignal(signum)})' tokens = [f"ORCA: Shutting down and exiting due to signal={signum} {signal_string}", frame] debug.print_tokens(debug.LEVEL_INFO, tokens, True) shutdown() def _show_preferences_on_signal(signum, frame): signal_string = f'({signal.strsignal(signum)})' tokens = [f"ORCA: Showing preferences due to signal={signum} {signal_string}", frame] debug.print_tokens(debug.LEVEL_INFO, tokens, True) manager = script_manager.get_manager() script = manager.get_active_script() or manager.get_default_script() if script: script.show_preferences_gui() signal.signal(signal.SIGHUP, _reload_on_signal) signal.signal(signal.SIGINT, _shutdown_on_signal) signal.signal(signal.SIGTERM, _shutdown_on_signal) signal.signal(signal.SIGQUIT, _shutdown_on_signal) signal.signal(signal.SIGUSR1, _show_preferences_on_signal) systemd.get_manager().start_watchdog() debug.print_message(debug.LEVEL_INFO, "ORCA: Enabling accessibility (if needed).", True) if not settings_manager.get_manager().is_accessibility_enabled(): settings_manager.get_manager().set_accessibility(True) load_user_settings(is_reload=False) if not systemd.get_manager().is_systemd_managed(): # Legacy behavior, here for backwards-compatibility. You really should # never rely on this. Run git blame and read the commit message! def _on_enabled_changed(gsetting, key): enabled = gsetting.get_boolean(key) msg = f"ORCA: {key} changed to {enabled}." debug.print_message(debug.LEVEL_INFO, msg, True) if key == "screen-reader-enabled" and not enabled: shutdown() try: _a11y_applications_gsetting = Gio.Settings( schema_id="org.gnome.desktop.a11y.applications") connection = _a11y_applications_gsetting.connect("changed", _on_enabled_changed) msg = f"ORCA: Connected to a11y applications gsetting: {bool(connection)}" debug.print_message(debug.LEVEL_INFO, msg, True) except GLib.Error as error: msg = ( f"ORCA: EXCEPTION connecting to a11y applications (schema may be missing): {error}" ) debug.print_message(debug.LEVEL_SEVERE, msg, True) except (AttributeError, TypeError) as error: msg = ( "ORCA: EXCEPTION connecting to a11y applications (version incompatibility):" f"{error}" ) debug.print_message(debug.LEVEL_SEVERE, msg, True) dbus_service.get_remote_controller().start() script = script_manager.get_manager().get_default_script() script.present_message(messages.START_ORCA) event_manager.get_manager().activate() script_manager.get_manager().activate() window = AXUtilities.find_active_window() if window and not focus_manager.get_manager().get_locus_of_focus(): app = AXUtilities.get_application(window) focus_manager.get_manager().set_active_window( window, app, set_window_as_focus=True, notify_script=True) focused_object = focus_manager.get_manager().find_focused_object() if focused_object: focus_manager.get_manager().set_locus_of_focus(None, focused_object) script = script_manager.get_manager().get_script(app, focused_object or window) script_manager.get_manager().set_active_script(script, "Launching.") clipboard.get_presenter().activate() Gdk.notify_startup_complete() systemd.get_manager().notify_ready() try: debug.print_message(debug.LEVEL_INFO, "ORCA: Starting Atspi main event loop", True) Atspi.event_main() except GLib.Error as error: msg = f"ORCA: Exception starting ATSPI registry: {error}" debug.print_message(debug.LEVEL_SEVERE, msg, True) os.kill(os.getpid(), signal.SIGKILL) return 0 if __name__ == "__main__": sys.exit(main())