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