Coverage Report - orca.orca

ModuleCoverage %
orca.orca
43%
1
# Orca
2
#
3
# Copyright 2004-2007 Sun Microsystems Inc.
4
#
5
# This library is free software; you can redistribute it and/or
6
# modify it under the terms of the GNU Library General Public
7
# License as published by the Free Software Foundation; either
8
# version 2 of the License, or (at your option) any later version.
9
#
10
# This library is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
# Library General Public License for more details.
14
#
15
# You should have received a copy of the GNU Library General Public
16
# License along with this library; if not, write to the
17
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
# Boston, MA 02111-1307, USA.
19
20 1
"""The main module for the Orca screen reader."""
21
22 1
__id__        = "$Id: orca.py 2529 2007-07-09 14:49:14Z wwalker $"
23 1
__version__   = "$Revision: 2529 $"
24 1
__date__      = "$Date: 2007-07-09 10:49:14 -0400 (Mon, 09 Jul 2007) $"
25 1
__copyright__ = "Copyright (c) 2005-2007 Sun Microsystems Inc."
26 1
__license__   = "LGPL"
27
28
# We're going to force the name of the app to "orca" so pygtk
29
# will end up showing us as "orca" to the AT-SPI.  If we don't
30
# do this, the name can end up being "-c".  See bug 364452 at
31
# http://bugzilla.gnome.org/show_bug.cgi?id=364452 for more
32
# information.
33
#
34 1
import sys
35 1
sys.argv[0] = "orca"
36
37 1
try:
38
    # This can fail due to gtk not being available.  We want to
39
    # be able to recover from that if possible.  The main driver
40
    # for this is to allow "orca --text-setup" to work even if
41
    # the desktop is not running.
42
    #
43 1
    import gtk
44 0
except:
45 0
    pass
46
47 1
import getopt
48 1
import os
49 1
import signal
50 1
import time
51 1
import unicodedata
52
53 1
import atspi
54 1
import braille
55 1
import debug
56 1
import httpserver
57 1
import keynames
58 1
import keybindings
59 1
import mag
60 1
import orca_state
61 1
import platform
62 1
import rolenames
63 1
import settings
64 1
import speech
65
66 1
from input_event import BrailleEvent
67 1
from input_event import KeyboardEvent
68 1
from input_event import MouseButtonEvent
69 1
from input_event import InputEventHandler
70
71 1
from orca_i18n import _           # for gettext support
72
73 1
if settings.debugMemoryUsage:
74 0
    import gc
75 0
    gc.set_debug(gc.DEBUG_UNCOLLECTABLE
76
                 | gc.DEBUG_COLLECTABLE
77
                 | gc.DEBUG_INSTANCES
78
                 | gc.DEBUG_OBJECTS
79 0
                 | gc.DEBUG_SAVEALL)
80
81
# The user-settings module (see loadUserSettings).
82
#
83 1
_userSettings = None
84
85
# Command line options that override any other settings.
86
#
87 1
_commandLineSettings = {}
88
89
########################################################################
90
#                                                                      #
91
# METHODS FOR HANDLING PRESENTATION MANAGERS                           #
92
#                                                                      #
93
# A presentation manager is what reacts to AT-SPI object events as     #
94
# well as user input events (keyboard and Braille) to present info     #
95
# to the user.                                                         #
96
#                                                                      #
97
########################################################################
98
99
# The known presentation managers (set up in start())
100
#
101 1
_PRESENTATION_MANAGERS = None
102
103
# The current presentation manager, which is an index into the
104
# _PRESENTATION_MANAGERS list.
105
#
106 1
_currentPresentationManager = -1
107
108 1
def _switchToPresentationManager(index):
109
    """Switches to the given presentation manager.
110
111
    Arguments:
112
    - index: an index into _PRESENTATION_MANAGERS
113
    """
114
115
    global _currentPresentationManager
116
117 1
    if _currentPresentationManager >= 0:
118 0
        _PRESENTATION_MANAGERS[_currentPresentationManager].deactivate()
119
120 1
    _currentPresentationManager = index
121
122
    # Wrap the presenter index around.
123
    #
124 1
    if _currentPresentationManager >= len(_PRESENTATION_MANAGERS):
125 0
        _currentPresentationManager = 0
126 1
    elif _currentPresentationManager < 0:
127 0
        _currentPresentationManager = len(_PRESENTATION_MANAGERS) - 1
128
129 1
    _PRESENTATION_MANAGERS[_currentPresentationManager].activate()
130
131 1
def _switchToNextPresentationManager(script=None, inputEvent=None):
132
    """Switches to the next presentation manager.
133
134
    Arguments:
135
    - inputEvent: the InputEvent instance that caused this to be called.
136
137
    Returns True indicating the event should be consumed.
138
    """
139
140 0
    _switchToPresentationManager(_currentPresentationManager + 1)
141 0
    return True
142
143
########################################################################
144
#                                                                      #
145
# METHODS TO HANDLE APPLICATION LIST AND FOCUSED OBJECTS               #
146
#                                                                      #
147
########################################################################
148
149 1
def setLocusOfFocus(event, obj, notifyPresentationManager=True):
150
    """Sets the locus of focus (i.e., the object with visual focus) and
151
    notifies the current presentation manager of the change.
152
153
    Arguments:
154
    - event: if not None, the Event that caused this to happen
155
    - obj: the Accessible with the new locus of focus.
156
    - notifyPresentationManager: if True, propagate this event
157
    """
158
159 1047
    if obj == orca_state.locusOfFocus:
160 144
        return
161
162 903
    oldLocusOfFocus = orca_state.locusOfFocus
163 903
    if oldLocusOfFocus and not oldLocusOfFocus.valid:
164 0
        oldLocusOfFocus = None
165
166 903
    orca_state.locusOfFocus = obj
167 903
    if orca_state.locusOfFocus and not orca_state.locusOfFocus.valid:
168 0
        orca_state.locusOfFocus = None
169
170 903
    if orca_state.locusOfFocus:
171 825
        appname = ""
172 825
        if not orca_state.locusOfFocus.app:
173 0
            appname = "None"
174
        else:
175 825
            appname = "'" + orca_state.locusOfFocus.app.name + "'"
176
177 825
        debug.println(debug.LEVEL_FINE,
178 825
                      "LOCUS OF FOCUS: app=%s name='%s' role='%s'" \
179 825
                      % (appname,
180 825
                         orca_state.locusOfFocus.name,
181 825
                         orca_state.locusOfFocus.role))
182
183 825
        if event:
184 825
            debug.println(debug.LEVEL_FINE,
185 825
                          "                event='%s'" % event.type)
186
        else:
187 0
            debug.println(debug.LEVEL_FINE,
188 0
                          "                event=None")
189
    else:
190 78
        if event:
191 78
            debug.println(debug.LEVEL_FINE,
192 78
                          "LOCUS OF FOCUS: None event='%s'" % event.type)
193
        else:
194 0
            debug.println(debug.LEVEL_FINE,
195 0
                          "LOCUS OF FOCUS: None event=None")
196
197 903
    if notifyPresentationManager and _currentPresentationManager >= 0:
198 903
        _PRESENTATION_MANAGERS[_currentPresentationManager].\
199 903
            locusOfFocusChanged(event,
200 903
                                oldLocusOfFocus,
201 903
                                orca_state.locusOfFocus)
202
203 1
def visualAppearanceChanged(event, obj):
204
    """Called (typically by scripts) when the visual appearance of an object
205
    changes and notifies the current presentation manager of the change.  This
206
    method should not be called for objects whose visual appearance changes
207
    solely because of focus -- setLocusOfFocus is used for that.  Instead, it
208
    is intended mostly for objects whose notional 'value' has changed, such as
209
    a checkbox changing state, a progress bar advancing, a slider moving, text
210
    inserted, caret moved, etc.
211
212
    Arguments:
213
    - event: if not None, the Event that caused this to happen
214
    - obj: the Accessible whose visual appearance changed.
215
    """
216
217 841
    if _currentPresentationManager >= 0:
218 841
        _PRESENTATION_MANAGERS[_currentPresentationManager].\
219 841
            visualAppearanceChanged(event, obj)
220
221 1
def _onChildrenChanged(e):
222
    """Tracks children-changed events on the desktop to determine when
223
    apps start and stop.
224
225
    Arguments:
226
    - e: at-spi event from the at-api registry
227
    """
228
229 3615
    registry = atspi.Registry()
230 3615
    if e.source == registry.desktop:
231
232
        # If the desktop is empty, the user has logged out-- shutdown Orca
233
        #
234 149
        try:
235 149
            if registry.desktop.childCount == 0:
236 0
                speech.speak(_("Goodbye."))
237 0
                shutdown()
238 0
                return
239 0
        except: # could be a CORBA.COMM_FAILURE
240 0
            debug.printException(debug.LEVEL_FINEST)
241 0
            shutdown()
242 0
            return
243
244 1
def _onMouseButton(e):
245
    """Tracks mouse button events, stopping any speech in progress.
246
247
    Arguments:
248
    - e: at-spi event from the at-api registry
249
    """
250
251 0
    event = atspi.Event(e)
252 0
    orca_state.lastInputEvent = MouseButtonEvent(event)
253
254
    # A mouse button event looks like: mouse:button:1p, where the
255
    # number is the button number and the 'p' is either 'p' or 'r',
256
    # meaning pressed or released.  We only want to stop speech on
257
    # button presses.
258
    #
259 0
    if event.type.endswith("p"):
260 0
        speech.stop()
261
262
########################################################################
263
#                                                                      #
264
# Keyboard Event Recording Support                                     #
265
#                                                                      #
266
########################################################################
267
268 1
_recordingKeystrokes = False
269 1
_keystrokesFile = None
270
271 1
def _closeKeystrokeWindowAndRecord(entry, window):
272
    global _keystrokesFile
273 0
    window.destroy()
274 0
    entry_text = entry.get_text()
275 0
    _keystrokesFile = open(entry_text, 'w')
276
277 1
def _closeKeystrokeWindowAndCancel(window):
278
    global _recordingKeystrokes
279 0
    window.destroy()
280 0
    _recordingKeystrokes = False
281
282 1
def toggleKeystrokeRecording(script=None, inputEvent=None):
283
    """Toggles the recording of keystrokes on and off.  When the
284
    user presses the magic key (Pause), Orca will pop up a window
285
    requesting a filename.  When the user presses the close button,
286
    Orca will start recording keystrokes to the file and will continue
287
    recording them until the user presses the magic key again.
288
289
    This functionality is used primarily to help gather keystroke
290
    information for regression testing purposes.  The keystrokes are
291
    recorded in such a way that they can be played back via the
292
    src/tools/play_keystrokes.py utility.
293
294
    Arguments:
295
    - inputEvent: the key event (if any) which caused this to be called.
296
297
    Returns True indicating the event should be consumed.
298
    """
299
300
    global _recordingKeystrokes
301
    global _keystrokesFile
302
303 0
    if _recordingKeystrokes:
304
        # If the filename entry window is still up, we don't have a file
305
        # yet.
306
        #
307 0
        if _keystrokesFile:
308 0
            _keystrokesFile.close()
309 0
            _keystrokesFile = None
310 0
            _recordingKeystrokes = False
311
    else:
312 0
        _recordingKeystrokes = True
313 0
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
314 0
        window.set_title("Keystroke Filename")
315
316 0
        vbox = gtk.VBox(False, 0)
317 0
        window.add(vbox)
318 0
        vbox.show()
319
320 0
        entry = gtk.Entry()
321 0
        entry.set_max_length(50)
322 0
        entry.set_editable(True)
323 0
        entry.set_text("keystrokes.txt")
324 0
        entry.select_region(0, len(entry.get_text()))
325
        # For now, do not allow "Return" to close the window - the reason
326
        # for this is that the key press closes the window, and the key
327
        # release will end up getting recorded.
328
        #
329
        #entry.connect("activate", _closeKeystrokeWindow, window)
330 0
        vbox.pack_start(entry, True, True, 0)
331 0
        entry.show()
332
333 0
        hbox = gtk.HBox(False, 0)
334 0
        vbox.add(hbox)
335 0
        hbox.show()
336
337 0
        ok = gtk.Button(stock=gtk.STOCK_OK)
338 0
        ok.connect("clicked", lambda w: _closeKeystrokeWindowAndRecord(\
339 0
            entry, \
340 0
            window))
341
342 0
        cancel = gtk.Button(stock=gtk.STOCK_CANCEL)
343 0
        cancel.connect("clicked", lambda w: _closeKeystrokeWindowAndCancel(\
344 0
            window))
345
346 0
        vbox.pack_start(cancel, True, True, 0)
347 0
        vbox.pack_start(ok, True, True, 0)
348
349 0
        ok.set_flags(gtk.CAN_DEFAULT)
350 0
        ok.grab_default()
351 0
        ok.show()
352 0
        cancel.show()
353
354 0
        window.set_modal(True)
355 0
        window.show()
356 0
    return True
357
358
########################################################################
359
#                                                                      #
360
# DEBUG support.                                                       #
361
#                                                                      #
362
########################################################################
363
364 1
def cycleDebugLevel(script=None, inputEvent=None):
365
    global _debugLevel
366
367 0
    level = debug.debugLevel
368
369 0
    if level == debug.LEVEL_ALL:
370 0
        level = debug.LEVEL_FINEST
371 0
    elif level == debug.LEVEL_FINEST:
372 0
        level = debug.LEVEL_FINER
373 0
    elif level == debug.LEVEL_FINER:
374 0
        level = debug.LEVEL_FINE
375 0
    elif level == debug.LEVEL_FINE:
376 0
        level = debug.LEVEL_CONFIGURATION
377 0
    elif level == debug.LEVEL_CONFIGURATION:
378 0
        level = debug.LEVEL_INFO
379 0
    elif level == debug.LEVEL_INFO:
380 0
        level = debug.LEVEL_WARNING
381 0
    elif level == debug.LEVEL_WARNING:
382 0
        level = debug.LEVEL_SEVERE
383 0
    elif level == debug.LEVEL_SEVERE:
384 0
        level = debug.LEVEL_OFF
385 0
    elif level == debug.LEVEL_OFF:
386 0
        level = debug.LEVEL_ALL
387
388 0
    debug.debugLevel = level
389
390 0
    if level == debug.LEVEL_ALL:
391 0
        speech.speak("Debug level all.")
392 0
    elif level == debug.LEVEL_FINEST:
393 0
        speech.speak("Debug level finest.")
394 0
    elif level == debug.LEVEL_FINER:
395 0
        speech.speak("Debug level finer.")
396 0
    elif level == debug.LEVEL_FINE:
397 0
        speech.speak("Debug level fine.")
398 0
    elif level == debug.LEVEL_CONFIGURATION:
399 0
        speech.speak("Debug level configuration.")
400 0
    elif level == debug.LEVEL_INFO:
401 0
        speech.speak("Debug level info.")
402 0
    elif level == debug.LEVEL_WARNING:
403 0
        speech.speak("Debug level warning.")
404 0
    elif level == debug.LEVEL_SEVERE:
405 0
        speech.speak("Debug level severe.")
406 0
    elif level == debug.LEVEL_OFF:
407 0
        speech.speak("Debug level off.")
408
409 0
    return True
410
411
########################################################################
412
#                                                                      #
413
# METHODS FOR PRE-PROCESSING AND MASSAGING KEYBOARD EVENTS.            #
414
#                                                                      #
415
# All keyboard events are funnelled through here first.  Orca itself   #
416
# might have global keybindings (e.g., to switch between presenters),  #
417
# but it will typically pass the event onto the currently active       #
418
# active presentation manager.                                         #
419
#                                                                      #
420
########################################################################
421
422
# Keybindings that Orca itself cares about.
423
#
424 1
_keyBindings = None
425
426
# True if the orca modifier key is currently pressed.
427
#
428 1
_orcaModifierPressed = False
429
430 1
def _isPrintableKey(event_string):
431
    """Return an indication of whether this is an alphanumeric or
432
       punctuation key.
433
434
    Arguments:
435
    - event: the event string
436
437
    Returns True if this is an alphanumeric or punctuation key.
438
    """
439
440 0
    if event_string == "space":
441 0
        reply = True
442
    else:
443 0
        unicodeString = event_string.decode("UTF-8")
444 0
        reply = (len(unicodeString) == 1) \
445 0
                and (unicodeString.isalnum() or unicodeString.isspace()
446 0
                or unicodedata.category(unicodeString)[0] in ('P', 'S'))
447 0
    debug.println(debug.LEVEL_FINEST,
448 0
                  "orca._echoPrintableKey: returning: %s" % reply)
449 0
    return reply
450
451 1
def _isModifierKey(event_string):
452
    """Return an indication of whether this is a modifier key.
453
454
    Arguments:
455
    - event: the event string
456
457
    Returns True if this is a modifier key
458
    """
459
460
    # [[[TODO:richb - the Fn key on my laptop doesn't seem to generate an
461
    #    event.]]]
462
463 3579
    modifierKeys = [ 'Alt_L', 'Alt_R', 'Control_L', 'Control_R', \
464 3579
                     'Shift_L', 'Shift_R', 'Meta_L', 'Meta_R' ]
465 3579
    modifierKeys.extend(settings.orcaModifierKeys)
466
467 3579
    reply = event_string in modifierKeys
468 3579
    debug.println(debug.LEVEL_FINEST,
469 3579
                  "orca._echoModifierKey: returning: %s" % reply)
470 3579
    return reply
471
472 1
def _isLockingKey(event_string):
473
    """Return an indication of whether this is a locking key.
474
475
    Arguments:
476
    - event: the event string
477
478
    Returns True if this is a locking key.
479
    """
480
481 0
    lockingKeys = [ "Caps_Lock", "Num_Lock", "Scroll_Lock" ]
482
483 0
    reply = event_string in lockingKeys \
484 0
            and not event_string in settings.orcaModifierKeys
485 0
    debug.println(debug.LEVEL_FINEST,
486 0
                  "orca._echoLockingKey: returning: %s" % reply)
487 0
    return reply
488
489 1
def _isFunctionKey(event_string):
490
    """Return an indication of whether this is a function key.
491
492
    Arguments:
493
    - event: the event string
494
495
    Returns True if this is a function key.
496
    """
497
498
    # [[[TODO:richb - what should be done about the function keys on the left
499
    #    side of my Sun keyboard and the other keys (like Scroll Lock), which
500
    #    generate "Fn" key events?]]]
501
502 0
    functionKeys = [ "F1", "F2", "F3", "F4", "F5", "F6",
503 0
                     "F7", "F8", "F9", "F10", "F11", "F12" ]
504
505 0
    reply = event_string in functionKeys
506 0
    debug.println(debug.LEVEL_FINEST,
507 0
                  "orca._echoFunctionKey: returning: %s" % reply)
508 0
    return reply
509
510 1
def _isActionKey(event_string):
511
    """Return an indication of whether this is an action key.
512
513
    Arguments:
514
    - event: the event string
515
516
    Returns True if this is an action key.
517
    """
518
519 0
    actionKeys = [ "Return", "Escape", "Tab", "BackSpace", "Delete",
520 0
                   "Page_Up", "Page_Down", "Home", "End" ]
521
522 0
    reply = event_string in actionKeys
523 0
    debug.println(debug.LEVEL_FINEST,
524 0
                  "orca._echoActionKey: returning: %s" % reply)
525 0
    return reply
526
527 2
class KeyEventType:
528 1
    """Definition of available key event types."""
529
530
    """An alphanumeric or punctuation key event."""
531 1
    PRINTABLE = 'PRINTABLE'
532
533
    """A modifier key event."""
534 1
    MODIFIER = 'MODIFIER'
535
536
    """A locking key event."""
537 1
    LOCKING = 'LOCKING'
538
539
    """A locking key lock event."""
540 1
    LOCKING_LOCKED = 'LOCKING_LOCKED'
541
542
    """A locking key unlock event."""
543 1
    LOCKING_UNLOCKED = 'LOCKING_UNLOCKED'
544
545
    """A function key event."""
546 1
    FUNCTION = 'FUNCTION'
547
548
    """An action key event."""
549 1
    ACTION = 'ACTION'
550
551 1
def _keyEcho(event):
552
    """If the keyEcho setting is enabled, check to see what type of key
553
    event it is and echo it via speech, if the user wants that type of
554
    key echoed.
555
556
    Uppercase keys will be spoken using the "uppercase" voice style,
557
    whereas lowercase keys will be spoken using the "default" voice style.
558
559
    Arguments:
560
    - event: an AT-SPI DeviceEvent
561
    """
562
563
    # If this keyboard event was for an object like a password text
564
    # field, then don't echo it.
565
    #
566 1802
    if orca_state.locusOfFocus \
567 1800
        and (orca_state.locusOfFocus.role == rolenames.ROLE_PASSWORD_TEXT):
568 0
        return
569
570 1802
    event_string = event.event_string
571 1802
    debug.println(debug.LEVEL_FINEST,
572 1802
                  "orca._keyEcho: string to echo: %s" % event_string)
573
574
    # If key echo is enabled, then check to see what type of key event
575
    # it is and echo it via speech, if the user wants that type of key
576
    # echoed.
577
    #
578 1802
    if settings.enableKeyEcho:
579
580 0
        if _isPrintableKey(event_string):
581 0
            if not settings.enablePrintableKeys:
582 0
                return
583 0
            type = KeyEventType.PRINTABLE
584
585 0
        elif _isModifierKey(event_string):
586 0
            if not settings.enableModifierKeys:
587 0
                return
588 0
            type = KeyEventType.MODIFIER
589
590 0
        elif _isLockingKey(event_string):
591 0
            if not settings.enableLockingKeys:
592 0
                return
593 0
            type = KeyEventType.LOCKING
594
595 0
            modifiers = event.modifiers
596
597 0
            if event_string == "Caps_Lock":
598 0
                if modifiers & (1 << atspi.Accessibility.MODIFIER_SHIFTLOCK):
599 0
                    type = KeyEventType.LOCKING_UNLOCKED
600
                else:
601 0
                    type = KeyEventType.LOCKING_LOCKED
602
603 0
            elif event_string == "Num_Lock":
604
                # [[[TODO: richb - we are not getting a correct modifier
605
                # state value returned when Num Lock is turned off.
606
                # Commenting out the speaking of the bogus on/off state
607
                # until this can be fixed.]]]
608
                #
609
                #if modifiers & (1 << atspi.Accessibility.MODIFIER_NUMLOCK):
610
                #    type = KeyEventType.LOCKING_UNLOCKED
611
                #else:
612
                #    type = KeyEventType.LOCKING_LOCKED
613 0
                pass
614
615 0
        elif _isFunctionKey(event_string):
616 0
            if not settings.enableFunctionKeys:
617 0
                return
618 0
            type = KeyEventType.FUNCTION
619
620 0
        elif _isActionKey(event_string):
621 0
            if not settings.enableActionKeys:
622 0
                return
623 0
            type = KeyEventType.ACTION
624
625
        else:
626 0
            debug.println(debug.LEVEL_FINEST,
627 0
                  "orca._keyEcho: event string not handled: %s" % event_string)
628 0
            return
629
630 0
        debug.println(debug.LEVEL_FINEST,
631 0
                      "orca._keyEcho: speaking: %s" % event_string)
632
633
        # We keep track of the time as means to let others know that
634
        # we are probably echoing a key and should not be interrupted.
635
        #
636 0
        orca_state.lastKeyEchoTime = time.time()
637
638 0
        speech.speakKeyEvent(event_string, type)
639
640 1
def _processKeyCaptured(event):
641
    """Called when a new key event arrives and orca_state.capturingKeys=True.
642
    (used for key bindings redefinition)
643
    """
644
645 0
    if event.type == 0:
646 0
        if _isModifierKey(event.event_string) \
647 0
               or event.event_string == "Return":
648 0
            pass
649 0
        elif event.event_string == "Escape":
650 0
            orca_state.capturingKeys = False
651
        else:
652
            # Translators: this is a spoken prompt letting the user know
653
            # Orca has recorded a new key combination (e.g., Alt+Ctrl+g)
654
            # based upon their input.
655
            #
656 0
            speech.speak(_("Key captured: %s. Press enter to confirm.") \
657 0
                         % str(event.event_string))
658 0
            orca_state.lastCapturedKey = event
659
    else:
660
        pass
661 0
    return False
662
663 1
def _processKeyboardEvent(event):
664
    """The primary key event handler for Orca.  Keeps track of various
665
    attributes, such as the lastInputEvent.  Also calls keyEcho as well
666
    as any local keybindings before passing the event on to the active
667
    presentation manager.  This method is called synchronously from the
668
    AT-SPI registry and should be performant.  In addition, it
669
    must return True if it has consumed the event (and False if not).
670
671
    Arguments:
672
    - event: an AT-SPI DeviceEvent
673
674
    Returns True if the event should be consumed.
675
    """
676
    global _orcaModifierPressed
677
678 3579
    orca_state.lastInputEventTimestamp = event.timestamp
679
680
    # Log the keyboard event for future playback, if desired.
681
    # Note here that the key event_string being output is
682
    # exactly what we received.  The KeyboardEvent object,
683
    # however, will translate the event_string for control
684
    # characters to their upper case ASCII equivalent.
685
    #
686 3579
    string = atspi.KeystrokeListener.keyEventToString(event)
687 3579
    if _recordingKeystrokes and _keystrokesFile \
688 0
       and (event.event_string != "Pause") \
689 0
       and (event.event_string != "F21"):
690 0
        _keystrokesFile.write(string + "\n")
691 3579
    debug.printInputEvent(debug.LEVEL_FINE, string)
692
693 3579
    keyboardEvent = KeyboardEvent(event)
694
695
    # See if this is one of our special Orca modifier keys.
696
    #
697
    # [[[TODO: WDW - Note that just looking at the keycode should
698
    # suffice, but there is a "feature" in the Java Access Bridge
699
    # where it chooses to emit Java platform-independent keycodes
700
    # instead of the keycodes for the base platform:
701
    #
702
    # http://bugzilla.gnome.org/show_bug.cgi?id=106004
703
    # http://bugzilla.gnome.org/show_bug.cgi?id=318615
704
    #
705
    # So...we need to workaround this problem.
706
    #
707
    # If you make the following expression True we will get a positive
708
    # match for all keysyms associated with a given keysym specified
709
    # as an Orca modifier key.
710
    #
711
    # For example, assume the Orca modifier is set to \ for some
712
    # reason.  The key that has \ on it produces \ without the Shift
713
    # key and | with the Shift key.  If the following expression is
714
    # True, both the \ and | will be viewed as the Orca modifier.  If
715
    # the following expression is False, only the \ will be viewed as
716
    # the Orca modifier (i.e., Shift+\ will still function as the |
717
    # character).  In general, I think we want to avoid sucking in all
718
    # possible keysyms because it can have unexpected results.]]]
719
    #
720 3579
    if False:
721 0
        allPossibleKeysyms = []
722 0
        for keysym in settings.orcaModifierKeys:
723 0
            allPossibleKeysyms.extend(keybindings.getAllKeysyms(keysym))
724
    else:
725 3579
        allPossibleKeysyms = settings.orcaModifierKeys
726
727 3579
    isOrcaModifier = allPossibleKeysyms.count(keyboardEvent.event_string) > 0
728
729 3579
    if event.type == atspi.Accessibility.KEY_PRESSED_EVENT:
730
        # Key presses always interrupt speech.
731
        #
732 1802
        speech.stop()
733
734
        # If learn mode is enabled, it will echo the keys.
735
        #
736 1802
        if not settings.learnModeEnabled:
737 1802
            _keyEcho(keyboardEvent)
738
739
        # We treat the Insert key as a modifier - so just swallow it and
740
        # set our internal state.
741
        #
742 1802
        if isOrcaModifier:
743 5
            _orcaModifierPressed = True
744
745 1777
    elif isOrcaModifier \
746 5
        and (keyboardEvent.type == atspi.Accessibility.KEY_RELEASED_EVENT):
747 5
        _orcaModifierPressed = False
748
749 3579
    if _orcaModifierPressed:
750 10
        keyboardEvent.modifiers = keyboardEvent.modifiers \
751 10
                                  | (1 << settings.MODIFIER_ORCA)
752
753
    # Orca gets first stab at the event.  Then, the presenter gets
754
    # a shot. [[[TODO: WDW - might want to let the presenter try first?
755
    # The main reason this is staying as is is that we may not want
756
    # scripts to override fundamental Orca key bindings.]]]
757
    #
758 3579
    consumed = False
759 3579
    try:
760 3579
        if orca_state.capturingKeys:
761 0
            _processKeyCaptured(keyboardEvent)
762
        else:
763 3579
            consumed = _keyBindings.consumeKeyboardEvent(None, keyboardEvent)
764 3579
            if (not consumed) and (_currentPresentationManager >= 0):
765 3579
                consumed = _PRESENTATION_MANAGERS[_currentPresentationManager].\
766 3579
                           processKeyboardEvent(keyboardEvent)
767 3579
            if (not consumed) and settings.learnModeEnabled:
768 0
                if keyboardEvent.type \
769 0
                    == atspi.Accessibility.KEY_PRESSED_EVENT:
770 0
                    clickCount = orca_state.activeScript.getClickCount(\
771 0
                                                orca_state.lastInputEvent,
772 0
                                                keyboardEvent)
773 0
                    if clickCount == 2:
774 0
                        orca_state.activeScript.phoneticSpellCurrentItem(\
775 0
                            keyboardEvent.event_string)
776
                    else:
777
                        # Check to see if there are localized words to be
778
                        # spoken for this key event.
779
                        #
780 0
                        braille.displayMessage(keyboardEvent.event_string)
781 0
                        event_string = keyboardEvent.event_string
782 0
                        event_string = keynames.getKeyName(event_string)
783 0
                        speech.speak(event_string)
784 0
                consumed = True
785 0
    except:
786 0
        debug.printException(debug.LEVEL_SEVERE)
787
788 3579
    orca_state.lastInputEvent = keyboardEvent
789
790
    # If this is a key event for a non-modifier key, save a handle to it.
791
    # This is needed to help determine user actions when a multi-key chord
792
    # has been pressed, and we might get the key events in different orders.
793
    # See comment #15 of bug #435201 for more details.
794
    #
795 3579
    if not _isModifierKey(keyboardEvent.event_string):
796 3292
        orca_state.lastNonModifierKeyEvent = keyboardEvent
797
798 3579
    return consumed or isOrcaModifier
799
800
########################################################################
801
#                                                                      #
802
# METHODS FOR PRE-PROCESSING AND MASSAGING BRAILLE EVENTS.             #
803
#                                                                      #
804
########################################################################
805
806 1
def _processBrailleEvent(command):
807
    """Called whenever a  key is pressed on the Braille display.
808
809
    Arguments:
810
    - command: the BrlAPI command for the key that was pressed.
811
812
    Returns True if the event was consumed; otherwise False
813
    """
814
815
    # [[[TODO: WDW - probably should add braille bindings to this module.]]]
816
817 0
    consumed = False
818
819
    # Braille key presses always interrupt speech.
820
    #
821 0
    speech.stop()
822
823 0
    event = BrailleEvent(command)
824 0
    orca_state.lastInputEvent = event
825
826 0
    try:
827 0
        consumed = _PRESENTATION_MANAGERS[_currentPresentationManager].\
828 0
                   processBrailleEvent(event)
829 0
    except:
830 0
        debug.printException(debug.LEVEL_SEVERE)
831
832 0
    if (not consumed) and settings.learnModeEnabled:
833 0
        consumed = True
834
835 0
    return consumed
836
837
########################################################################
838
#                                                                      #
839
# METHODS FOR HANDLING INITIALIZATION, SHUTDOWN, AND USE.              #
840
#                                                                      #
841
########################################################################
842
843 1
def _toggleSilenceSpeech(script=None, inputEvent=None):
844
    """Toggle the silencing of speech.
845
846
    Returns True to indicate the input event has been consumed.
847
    """
848 0
    speech.stop()
849 0
    if settings.silenceSpeech:
850 0
        settings.silenceSpeech = False
851
        # Translators: this is a spoken prompt letting the user know
852
        # that speech synthesis has been turned back on.
853
        #
854 0
        speech.speak(_("Speech enabled."))
855
    else:
856
        # Translators: this is a spoken prompt letting the user know
857
        # that speech synthesis has been temporarily turned off.
858
        #
859 0
        speech.speak(_("Speech disabled."))
860 0
        settings.silenceSpeech = True
861 0
    return True
862
863 1
def loadUserSettings(script=None, inputEvent=None):
864
    """Loads (and reloads) the user settings module, reinitializing
865
    things such as speech if necessary.
866
867
    Returns True to indicate the input event has been consumed.
868
    """
869
870
    global _userSettings
871
872
    # Shutdown the output drivers and give them a chance to die.
873
    #
874 1
    httpserver.shutdown()
875 1
    speech.shutdown()
876 1
    braille.shutdown()
877 1
    mag.shutdown()
878
879 1
    if _currentPresentationManager >= 0:
880 0
        _PRESENTATION_MANAGERS[_currentPresentationManager].deactivate()
881
882 1
    time.sleep(1)
883
884 1
    reloaded = False
885 1
    if _userSettings:
886 0
        try:
887 0
            reload(_userSettings)
888 0
            reloaded = True
889 0
        except ImportError:
890 0
            debug.printException(debug.LEVEL_FINEST)
891
    else:
892 1
        try:
893 1
            _userSettings = __import__("user-settings")
894 0
        except ImportError:
895 0
            debug.printException(debug.LEVEL_FINEST)
896
897
    # If any settings were added to the command line, they take
898
    # precedence over everything else.
899
    #
900 1
    for key in _commandLineSettings:
901 0
        settings.__dict__[key] = _commandLineSettings[key]
902
903 1
    if settings.enableSpeech:
904 1
        try:
905 1
            speech.init()
906 1
            if reloaded:
907
                # Translators: there is a keystroke to reload the user
908
                # preferences.  This is a spoken prompt to let the user
909
                # know when the preferences has been reloaded.
910
                #
911 0
                speech.speak(_("Orca user settings reloaded."))
912 1
            debug.println(debug.LEVEL_CONFIGURATION,
913 1
                          "Speech module has been initialized.")
914 0
        except:
915 0
            debug.printException(debug.LEVEL_SEVERE)
916 0
            debug.println(debug.LEVEL_SEVERE,
917 0
                          "Could not initialize connection to speech.")
918
    else:
919 0
        debug.println(debug.LEVEL_CONFIGURATION,
920 0
                      "Speech module has NOT been initialized.")
921
922 1
    if settings.enableBraille:
923 1
        try:
924 1
            braille.init(_processBrailleEvent, settings.tty)
925 1
        except:
926 1
            debug.printException(debug.LEVEL_WARNING)
927 1
            debug.println(debug.LEVEL_WARNING,
928 1
                          "Could not initialize connection to braille.")
929
930 1
    if settings.enableMagnifier:
931 0
        try:
932 0
            mag.init()
933 0
            debug.println(debug.LEVEL_CONFIGURATION,
934 0
                          "Magnification module has been initialized.")
935 0
        except:
936 0
            debug.printException(debug.LEVEL_SEVERE)
937 0
            debug.println(debug.LEVEL_SEVERE,
938 0
                          "Could not initialize connection to magnifier.")
939
    else:
940 1
        debug.println(debug.LEVEL_CONFIGURATION,
941 1
                      "Magnification module has NOT been initialized.")
942
943
    # We don't want the Caps_Lock modifier to act as a locking
944
    # modifier if it used as the Orca modifier key.  In addition, if
945
    # the KP_Insert key is used as the Orca modifier key, we want to
946
    # make sure we clear any other keysyms that might be in use on
947
    # that key since we won't be able to detect them as being the Orca
948
    # modifier key.  For example, KP_Insert produces "KP_Insert" when
949
    # pressed by itself, but Shift+KP_Insert produces "0".
950
    #
951
    # The original values are saved/reset in the orca shell script.
952
    #
953
    # [[[TODO: WDW - we probably should just to a 'xmodmap -e "%s = %s"'
954
    # for all of the orcaModifierKeys, but saving/restoring the values
955
    # becomes a little more difficult.  If we could assume a writeable
956
    # filesystem (we cannot), we could do a 'xmodmap -pke > /tmp/foo'
957
    # to save the keymap and a 'xmodmap /tmp/foo' to restore it.
958
    # For now, we'll just look at the Orca modifier keys we support
959
    # (Caps Lock, KP_Insert, and Insert).]]]
960
    #
961 3
    for keyName in settings.orcaModifierKeys:
962 2
        if keyName == "Caps_Lock":
963 0
            os.system('xmodmap -e "clear Lock"')
964 2
        if keyName in ["Caps_Lock", "KP_Insert", "Insert"]:
965 2
            command = 'xmodmap -e "keysym %s = %s"' % (keyName, keyName)
966 2
            os.system(command)
967
968 1
    if _currentPresentationManager >= 0:
969 0
        _PRESENTATION_MANAGERS[_currentPresentationManager].activate()
970
971 1
    _showMainWindowGUI()
972
973 1
    httpserver.init()
974
975 1
    return True
976
977 1
def _showAppPreferencesGUI(script=None, inputEvent=None):
978
    """Displays the user interace to configure the settings for a
979
    specific applications within Orca and set up those app-specific
980
    user preferences using a GUI.
981
982
    Returns True to indicate the input event has been consumed.
983
    """
984
985 0
    try:
986 0
        module = __import__(settings.appGuiPreferencesModule,
987 0
                            globals(),
988 0
                            locals(),
989 0
                            [''])
990 0
        module.showPreferencesUI()
991 0
    except:
992 0
        debug.printException(debug.LEVEL_SEVERE)
993 0
        pass
994
995 0
    return True
996
997 1
def _showPreferencesGUI(script=None, inputEvent=None):
998
    """Displays the user interace to configure Orca and set up
999
    user preferences using a GUI.
1000
1001
    Returns True to indicate the input event has been consumed.
1002
    """
1003
1004 0
    try:
1005 0
        module = __import__(settings.guiPreferencesModule,
1006 0
                            globals(),
1007 0
                            locals(),
1008 0
                            [''])
1009 0
        module.showPreferencesUI()
1010 0
    except:
1011 0
        debug.printException(debug.LEVEL_SEVERE)
1012 0
        pass
1013
1014 0
    return True
1015
1016 1
def _showMainWindowGUI(script=None, inputEvent=None):
1017
    """Displays the Orca main window.
1018
1019
    Returns True to indicate the input event has been consumed.
1020
    """
1021
1022 1
    try:
1023 1
        module = __import__(settings.mainWindowModule,
1024 1
                            globals(),
1025 1
                            locals(),
1026 1
                            [''])
1027 1
        if settings.showMainWindow:
1028 1
            module.showMainUI()
1029
        else:
1030 0
            module.hideMainUI()
1031 0
    except:
1032 0
        debug.printException(debug.LEVEL_SEVERE)
1033 0
        pass
1034
1035 1
    return True
1036
1037 1
def _showPreferencesConsole(script=None, inputEvent=None):
1038
    """Displays the user interace to configure Orca and set up
1039
    user preferences via a command line interface.
1040
1041
    Returns True to indicate the input event has been consumed.
1042
    """
1043
1044 0
    try:
1045 0
        module = __import__(settings.consolePreferencesModule,
1046 0
                            globals(),
1047 0
                            locals(),
1048 0
                            [''])
1049 0
        module.showPreferencesUI()
1050 0
    except:
1051 0
        debug.printException(debug.LEVEL_SEVERE)
1052 0
        pass
1053
1054 0
    return True
1055
1056 1
def quitOrca(script=None, inputEvent=None):
1057
    """Quit Orca. Check if the user wants to confirm this action.
1058
    If so, show the confirmation GUI otherwise just shutdown.
1059
1060
    Returns True to indicate the input event has been consumed.
1061
    """
1062
1063 1
    if settings.quitOrcaNoConfirmation:
1064 0
        shutdown()
1065
    else:
1066 1
        try:
1067 1
            module = __import__(settings.quitModule,
1068 1
                                globals(),
1069 1
                                locals(),
1070 1
                                [''])
1071 1
            module.showQuitUI()
1072 0
        except:
1073 0
            debug.printException(debug.LEVEL_SEVERE)
1074 0
            pass
1075
1076 1
    return True
1077
1078 1
def _showFindGUI(script=None, inputEvent=None):
1079
    """Displays the user interace to perform an Orca Find.
1080
1081
    Returns True to indicate the input event has been consumed.
1082
    """
1083
1084 0
    try:
1085 0
        module = __import__(settings.findModule,
1086 0
                            globals(),
1087 0
                            locals(),
1088 0
                            [''])
1089 0
        module.showFindUI()
1090 0
    except:
1091 0
        debug.printException(debug.LEVEL_SEVERE)
1092 0
        pass
1093
1094
# If True, this module has been initialized.
1095
#
1096 1
_initialized = False
1097
1098 1
def init(registry):
1099
    """Initialize the orca module, which initializes speech, braille,
1100
    and mag modules.  Also builds up the application list, registers
1101
    for AT-SPI events, and creates scripts for all known applications.
1102
1103
    Returns True if the initialization procedure has run, or False if this
1104
    module has already been initialized.
1105
    """
1106
1107
    global _initialized
1108
    global _keyBindings
1109
1110 1
    if _initialized:
1111 0
        return False
1112
1113
    # Do not hang on initialization if we can help it.
1114
    #
1115 1
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1116 1
        signal.signal(signal.SIGALRM, settings.timeoutCallback)
1117 1
        signal.alarm(settings.timeoutTime)
1118
1119
    # Note that we have moved the Orca specific keybindings to the default
1120
    # script, so _keyBindings is currently empty. The logic is retained
1121
    # here, just in case we wish to reinstate them in the future.
1122
    #
1123 1
    _keyBindings = keybindings.KeyBindings()
1124
1125
    # Create and load an app's script when it is added to the desktop
1126
    #
1127 1
    registry.registerEventListener(_onChildrenChanged,
1128 1
                                   "object:children-changed:")
1129
1130
    # We also want to stop speech when a mouse button is pressed.
1131
    #
1132 1
    registry.registerEventListener(_onMouseButton,
1133 1
                                   "mouse:button")
1134
1135 1
    loadUserSettings()
1136
1137 1
    registry.registerKeystrokeListeners(_processKeyboardEvent)
1138
1139 1
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1140 1
        signal.alarm(0)
1141
1142 1
    _initialized = True
1143 1
    return True
1144
1145 1
def start(registry):
1146
    """Starts Orca.
1147
    """
1148
1149
    global _PRESENTATION_MANAGERS
1150
1151 1
    if not _initialized:
1152 0
        init(registry)
1153
1154
    # Do not hang on startup if we can help it.
1155
    #
1156 1
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1157 1
        signal.signal(signal.SIGALRM, settings.timeoutCallback)
1158 1
        signal.alarm(settings.timeoutTime)
1159
1160 1
    if not _PRESENTATION_MANAGERS:
1161 1
        import focus_tracking_presenter
1162
        _PRESENTATION_MANAGERS = \
1163 1
            [focus_tracking_presenter.FocusTrackingPresenter()]
1164
1165 1
    _switchToPresentationManager(0) # focus_tracking_presenter
1166
1167 1
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1168 1
        signal.alarm(0)
1169
1170 1
    registry.start()
1171
1172 1
def abort(exitCode=1):
1173 0
    os._exit(exitCode)
1174
1175 1
def timeout(signum=None, frame=None):
1176 0
    debug.println(debug.LEVEL_SEVERE,
1177 0
                  "TIMEOUT: something has hung.  Aborting.")
1178 0
    debug.printStack(debug.LEVEL_ALL)
1179 0
    abort(50)
1180
1181 1
def shutdown(script=None, inputEvent=None):
1182
    """Exits Orca.  Unregisters any event listeners and cleans up.  Also
1183
    quits the bonobo main loop and resets the initialized state to False.
1184
1185
    Returns True if the shutdown procedure ran or False if this module
1186
    was never initialized.
1187
    """
1188
1189
    global _initialized
1190
1191 1
    if not _initialized:
1192 0
        return False
1193
1194
    # Try to say goodbye, but be defensive if something has hung.
1195
    #
1196 1
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1197 1
        signal.signal(signal.SIGALRM, settings.timeoutCallback)
1198 1
        signal.alarm(settings.timeoutTime)
1199
1200
    # Translators: this is what Orca speaks and brailles when it quits.
1201
    #
1202 1
    speech.speak(_("Goodbye."))
1203 1
    braille.displayMessage(_("Goodbye."))
1204
1205
    # Deregister our event listeners
1206
    #
1207 1
    registry = atspi.Registry()
1208 1
    registry.deregisterEventListener(_onChildrenChanged,
1209 1
                                     "object:children-changed:")
1210 1
    registry.deregisterEventListener(_onMouseButton,
1211 1
                                     "mouse:button")
1212
1213 1
    if _currentPresentationManager >= 0:
1214 1
        _PRESENTATION_MANAGERS[_currentPresentationManager].deactivate()
1215
1216
    # Shutdown all the other support.
1217
    #
1218 1
    if settings.enableSpeech:
1219 1
        speech.shutdown()
1220 1
    if settings.enableBraille:
1221 1
        braille.shutdown();
1222 1
    if settings.enableMagnifier:
1223 0
        mag.shutdown();
1224
1225 1
    registry.stop()
1226
1227 1
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1228 1
        signal.alarm(0)
1229
1230 1
    _initialized = False
1231 1
    return True
1232
1233 1
exitCount = 0
1234 1
def shutdownOnSignal(signum, frame):
1235
    global exitCount
1236
1237 0
    debug.println(debug.LEVEL_ALL,
1238 0
                  "Shutting down and exiting due to signal = %d" \
1239 0
                  % signum)
1240
1241 0
    debug.println(debug.LEVEL_ALL, "Current stack is:")
1242 0
    debug.printStack(debug.LEVEL_ALL)
1243
1244
    # Well...we'll try to exit nicely, but if we keep getting called,
1245
    # something bad is happening, so just quit.
1246
    #
1247 0
    if exitCount:
1248 0
        abort(signum)
1249
    else:
1250 0
        exitCount += 1
1251
1252
    # Try to do a graceful shutdown if we can.
1253
    #
1254 0
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1255 0
        signal.signal(signal.SIGALRM, settings.timeoutCallback)
1256 0
        signal.alarm(settings.timeoutTime)
1257
1258 0
    try:
1259 0
        if _initialized:
1260 0
            shutdown()
1261
        else:
1262
            # We always want to try to shutdown speech since the
1263
            # speech servers are very persistent about living.
1264
            #
1265 0
            speech.shutdown()
1266 0
            shutdown()
1267 0
        cleanExit = True
1268 0
    except:
1269 0
        cleanExit = False
1270
1271 0
    if settings.timeoutCallback and (settings.timeoutTime > 0):
1272 0
        signal.alarm(0)
1273
1274 0
    if not cleanExit:
1275 0
        abort(signum)
1276
1277 1
def abortOnSignal(signum, frame):
1278 0
    debug.println(debug.LEVEL_ALL,
1279 0
                  "Aborting due to signal = %d" \
1280 0
                  % signum)
1281 0
    abort(signum)
1282
1283 1
def usage():
1284
    """Prints out usage information."""
1285 0
    print "Usage: orca [OPTION...]"
1286 0
    print
1287 0
    print "-?, --help                   Show this help message"
1288 0
    print "-v, --version                %s" % platform.version
1289 0
    print "-s, --setup, --gui-setup     Set up user preferences"
1290 0
    print "-t, --text-setup             Set up user preferences (text version)"
1291 0
    print "-n, --no-setup               Skip set up of user preferences"
1292 0
    print "-u, --user-prefs-dir=dirname Use alternate directory for user preferences"
1293 0
    print "-e, --enable=[speech|braille|braille-monitor|magnifier|main-window] Force use of option"
1294 0
    print "-d, --disable=[speech|braille|braille-monitor|magnifier|main-window] Prevent use of option"
1295 0
    print "-q, --quit                   Quits Orca (if shell script used)"
1296 0
    print
1297 0
    print "If Orca has not been previously set up by the user, Orca\nwill automatically launch the preferences set up unless\nthe -n or --no-setup option is used."
1298 0
    print
1299 0
    print "Report bugs to orca-list@gnome.org."
1300 0
    pass
1301
1302 1
def main():
1303
    """The main entry point for Orca.  The exit codes for Orca will
1304
    loosely be based on signals, where the exit code will be the
1305
    signal used to terminate Orca (if a signal was used).  Otherwise,
1306
    an exit code of 0 means normal completion and an exit code of 50
1307
    means Orca exited because of a hang."""
1308
1309
    global _commandLineSettings
1310
1311
    # Method to call when we think something might be hung.
1312
    #
1313 1
    settings.timeoutCallback = timeout
1314
1315
    # Various signal handlers we want to listen for.
1316
    #
1317 1
    signal.signal(signal.SIGHUP, shutdownOnSignal)
1318 1
    signal.signal(signal.SIGINT, shutdownOnSignal)
1319 1
    signal.signal(signal.SIGTERM, shutdownOnSignal)
1320 1
    signal.signal(signal.SIGQUIT, shutdownOnSignal)
1321 1
    signal.signal(signal.SIGSEGV, abortOnSignal)
1322
1323
    # See if the desktop is running.  If it is, the import of gtk will
1324
    # succeed.  If it isn't, the import will fail.
1325
    #
1326 1
    desktopRunning = False
1327 1
    try:
1328 1
        if gtk.gdk.display_get_default():
1329 1
            desktopRunning = True
1330 0
    except:
1331 0
        pass
1332
1333
    # Parse the command line options.
1334
    #
1335
    # Run the preferences setup if the user has specified
1336
    # "--setup" or "--text-setup" on the command line.  If the
1337
    # desktop is not running, we will fallback to the console-based
1338
    # method as appropriate.
1339
    #
1340 1
    bypassSetup     = False
1341 1
    setupRequested  = False
1342 1
    showGUI         = False
1343
1344
    # We hack a little here because the shell script to start orca can
1345
    # conflate all of command line arguments into one string, which is
1346
    # not what we want.  We detect this by seeing if the length of the
1347
    # argument list is 1.
1348
    #
1349 1
    arglist = sys.argv[1:]
1350 1
    if len(arglist) == 1:
1351 0
        arglist = arglist[0].split()
1352
1353 1
    try:
1354
        # ? is for help
1355
        # e is for enabling a feature
1356
        # d is for disabling a feature
1357
        # h is for help
1358
        # u is for alternate user preferences location
1359
        # s is for setup
1360
        # n is for no setup
1361
        # t is for text setup
1362
        # v is for version
1363
        #
1364 1
        opts, args = getopt.getopt(
1365 1
            arglist,
1366 1
            "?stnvd:e:u:",
1367 1
            ["help",
1368 1
             "user-prefs-dir=",
1369 1
             "enable=",
1370 1
             "disable=",
1371 1
             "setup",
1372 1
             "gui-setup",
1373 1
             "text-setup",
1374 1
             "no-setup",
1375 1
             "version"])
1376 1
        for opt, val in opts:
1377 0
            if opt in ("-u", "--user-prefs-dir"):
1378 0
                userPrefsDir = val.strip();
1379 0
                try:
1380 0
                    os.chdir(userPrefsDir)
1381 0
                    settings.userPrefsDir = userPrefsDir
1382 0
                except:
1383 0
                    debug.printException(debug.LEVEL_FINEST)
1384
1385 0
            if opt in ("-e", "--enable"):
1386 0
                feature = val.strip()
1387 0
                if feature == "speech":
1388 0
                    _commandLineSettings["enableSpeech"] = True
1389 0
                elif feature == "braille":
1390 0
                    _commandLineSettings["enableBraille"] = True
1391 0
                elif feature == "braille-monitor":
1392 0
                    _commandLineSettings["enableBrailleMonitor"] = True
1393 0
                elif feature == "magnifier":
1394 0
                    _commandLineSettings["enableMagnifier"] = True
1395 0
                elif feature == "main-window":
1396 0
                    _commandLineSettings["showMainWindow"] = True
1397
                else:
1398 0
                    usage()
1399 0
                    os._exit(2)
1400
1401 0
            if opt in ("-d", "--disable"):
1402 0
                feature = val.strip()
1403 0
                if feature == "speech":
1404 0
                    _commandLineSettings["enableSpeech"] = False
1405 0
                elif feature == "braille":
1406 0
                    _commandLineSettings["enableBraille"] = False
1407 0
                elif feature == "braille-monitor":
1408 0
                    _commandLineSettings["enableBrailleMonitor"] = False
1409 0
                elif feature == "magnifier":
1410 0
                    _commandLineSettings["enableMagnifier"] = False
1411 0
                elif feature == "main-window":
1412 0
                    _commandLineSettings["showMainWindow"] = False
1413
                else:
1414 0
                    usage()
1415 0
                    os._exit(2)
1416
1417 0
            if opt in ("-s", "--gui-setup", "--setup"):
1418 0
                setupRequested = True
1419 0
                showGUI = desktopRunning
1420 0
            if opt in ("-t", "--text-setup"):
1421 0
                setupRequested = True
1422 0
                showGUI = False
1423 0
            if opt in ("-n", "--no-setup"):
1424 0
                bypassSetup = True
1425 0
            if opt in ("-?", "--help"):
1426 0
                usage()
1427 0
                os._exit(0)
1428 0
            if opt in ("-v", "--version"):
1429 0
                print "Orca %s" % platform.version
1430 0
                os._exit(0)
1431 0
    except:
1432 0
        debug.printException(debug.LEVEL_OFF)
1433 0
        usage()
1434 0
        os._exit(2)
1435
1436
    # Do not run Orca if accessibility has not been enabled.
1437
    # We do allow, however, one to force Orca to run via the
1438
    # "-n" switch.  The main reason is so that things such
1439
    # as accessible login can work -- in those cases, the gconf
1440
    # setting is typically not set since the gdm user does not
1441
    # have a home.
1442
    #
1443 1
    a11yEnabled = settings.isAccessibilityEnabled()
1444 1
    if (not bypassSetup) and (not a11yEnabled):
1445 0
        _showPreferencesConsole()
1446 0
        abort()
1447
1448 1
    if setupRequested and (not bypassSetup) and (not showGUI):
1449 0
        _showPreferencesConsole()
1450
1451 1
    if not desktopRunning:
1452 0
        print "Cannot start Orca because it cannot connect"
1453 0
        print "to the Desktop.  Please make sure the DISPLAY"
1454 0
        print "environment variable has been set."
1455 0
        return 1
1456
1457 1
    userprefs = settings.userPrefsDir
1458 1
    sys.path.insert(0, userprefs)
1459 1
    sys.path.insert(0, '') # current directory
1460
1461 1
    registry = atspi.Registry()
1462 1
    init(registry)
1463
1464 1
    try:
1465 1
        message = _("Welcome to Orca.")
1466 1
        speech.speak(message)
1467 1
        braille.displayMessage(message)
1468 0
    except:
1469 0
        debug.printException(debug.LEVEL_SEVERE)
1470
1471
    # Check to see if the user wants the configuration GUI. It's
1472
    # done here so that the user's existing preferences can be used
1473
    # to set the initial GUI state.  We'll also force the set to
1474
    # be run if the preferences file doesn't exist, unless the
1475
    # user has bypassed any setup via the --no-setup switch.
1476
    #
1477 1
    if setupRequested and (not bypassSetup) and showGUI:
1478 0
        _showPreferencesGUI()
1479 1
    elif (not _userSettings) and (not bypassSetup):
1480 0
        if desktopRunning:
1481 0
            _showPreferencesGUI()
1482
        else:
1483 0
            _showPreferencesConsole()
1484
1485 1
    start(registry) # waits until we stop the registry
1486 1
    return 0
1487
1488 1
if __name__ == "__main__":
1489 0
    sys.exit(main())