? orca/beat_gaim
? orca/keystrokes.txt
Index: orca/ChangeLog
===================================================================
RCS file: /cvs/gnome/orca/ChangeLog,v
retrieving revision 1.625
diff -p -u -r1.625 ChangeLog
--- orca/ChangeLog	26 Jul 2006 01:29:08 -0000	1.625
+++ orca/ChangeLog	26 Jul 2006 14:26:46 -0000
@@ -1,5 +1,17 @@
 2006-07-25  Willie Walker <william.walker@sun.com>
 
+	* src/orca/settings.py, src/orca/focus_tracking_presenter.py,
+	src/orca/braille.py, src/orca/atspi.py, src/orca/orca.py: add hang
+	detection handlers.  These are managed via the
+	settings.timeoutTime and settings.timeoutCallback properties.
+	Before doing an operation that might hang, Orca will put a timer
+	in place.  This timer will call settings.timeoutCallback if the
+	timer expires after settings.timeoutTime seconds.  The current
+	timeout callback merely aborts Orca, and the timeout is currently
+	set to a pretty long time period (10 seconds).  The next step here
+	is to create a script that will detect these aborts and restart
+	Orca if appropriate.
+	
         * src/orca/orca.py: add a debug.LEVEL_ALL stack dump on the
 	signal handling routines to help us better understand where
 	Orca is when we send it signals.
Index: orca/src/orca/atspi.py
===================================================================
RCS file: /cvs/gnome/orca/src/orca/atspi.py,v
retrieving revision 1.38
diff -p -u -r1.38 atspi.py
--- orca/src/orca/atspi.py	18 Jul 2006 14:15:43 -0000	1.38
+++ orca/src/orca/atspi.py	26 Jul 2006 14:26:46 -0000
@@ -25,6 +25,7 @@ __date__      = "$Date: 2006/07/18 14:15
 __copyright__ = "Copyright (c) 2005-2006 Sun Microsystems Inc."
 __license__   = "LGPL"
 
+import threading
 import time
 
 import gobject
@@ -277,11 +278,22 @@ class EventListener(Accessibility__POA.E
         self.__registered = False
 
     def notifyEvent(self, event):
+        if settings.timeoutCallback and (settings.timeoutTime > 0):
+            timer = threading.Timer(settings.timeoutTime,
+                                    settings.timeoutCallback)
+            timer.start()
+        else:
+            timer = None
+
         try:
             self.callback(event)
         except:
             debug.printException(debug.LEVEL_WARNING)
 
+        if timer:
+            timer.cancel()
+            del timer
+
     def __del__(self):
         self.deregister()
 
@@ -356,11 +368,24 @@ class KeystrokeListener(Accessibility__P
 
         Returns True if the event has been consumed.
         """
+        if settings.timeoutCallback and (settings.timeoutTime > 0):
+            timer = threading.Timer(settings.timeoutTime,
+                                    settings.timeoutCallback)
+            timer.start()
+        else:
+            timer = None
+
         try:
-            return self.callback(event)
+            consumed = self.callback(event)
         except:
             debug.printException(debug.LEVEL_WARNING)
-            return False
+            consumed = False
+
+        if timer:
+            timer.cancel()
+            del timer
+
+        return consumed
 
     def __del__(self):
         self.deregister()
Index: orca/src/orca/braille.py
===================================================================
RCS file: /cvs/gnome/orca/src/orca/braille.py,v
retrieving revision 1.56
diff -p -u -r1.56 braille.py
--- orca/src/orca/braille.py	27 Jun 2006 13:59:59 -0000	1.56
+++ orca/src/orca/braille.py	26 Jul 2006 14:26:46 -0000
@@ -31,6 +31,8 @@ __date__      = "$Date: 2006/06/27 13:59
 __copyright__ = "Copyright (c) 2005-2006 Sun Microsystems Inc."
 __license__   = "LGPL"
 
+import threading
+
 import atspi
 import brl
 try:
@@ -790,14 +792,27 @@ def _processBrailleEvent(command):
         return True
 
     if _callback:
+        if settings.timeoutCallback and (settings.timeoutTime > 0):
+            timer = threading.Timer(settings.timeoutTime,
+                                    settings.timeoutCallback)
+            timer.start()
+        else:
+            timer = None
+
         try:
             # Like key event handlers, a return value of True means
             # the command was consumed.
             #
-            if _callback(command):
-                return True
+            consumed = _callback(command)
         except:
-            debug.printException(debug.LEVEL_SEVERE)
+            debug.printException(debug.LEVEL_WARNING)
+            consumed = False
+
+        if timer:
+            timer.cancel()
+            del timer
+
+        return consumed
 
     if (command >= 0x100) and (command < (0x100 + _displaySize[0])):
         if len(_lines) > 0:
Index: orca/src/orca/focus_tracking_presenter.py
===================================================================
RCS file: /cvs/gnome/orca/src/orca/focus_tracking_presenter.py,v
retrieving revision 1.81
diff -p -u -r1.81 focus_tracking_presenter.py
--- orca/src/orca/focus_tracking_presenter.py	25 Jul 2006 15:18:28 -0000	1.81
+++ orca/src/orca/focus_tracking_presenter.py	26 Jul 2006 14:26:47 -0000
@@ -556,13 +556,6 @@ class FocusTrackingPresenter(presentatio
         #    print "Quitting Orca."
         #    orca.shutdown()
 
-    def _timeout(self):
-        """Timer that will be called if we time out while trying to perform
-        an operation."""
-        debug.println(debug.LEVEL_SEVERE,
-                      "TIMEOUT: Looks like something has hung. Quitting Orca.")
-        orca.shutdown()
-
     def _dequeueEvent(self):
         """Handles all events destined for scripts.  Called by the GTK
         idle thread.
@@ -612,13 +605,6 @@ class FocusTrackingPresenter(presentatio
                 debug.println(debug.LEVEL_FINEST, "DEQUEUED EVENT %s <----------" \
                               % event.type)
 
-                # [[[TODO: WDW - the timer stuff is an experiment to see if
-                # we can recover from hangs.  It's only experimental, so it's
-                # commented out for now.]]]
-                #
-                #timer = threading.Timer(5.0, self._timeout)
-                #timer.start()
-
                 if (not debug.eventDebugFilter) \
                     or (debug.eventDebugFilter \
                         and debug.eventDebugFilter.match(event.type)):
@@ -632,9 +618,6 @@ class FocusTrackingPresenter(presentatio
                     debug.println(debug.eventDebugLevel,
                                   "^^^^^ PROCESS OBJECT EVENT %s ^^^^^\n" \
                                   % event.type)
-
-                #timer.cancel()
-                #del timer
 
             # [[[TODO: HACK - it would seem logical to only do this if we
             # discover the queue is empty, but this inroduces a hang for
Index: orca/src/orca/orca.py
===================================================================
RCS file: /cvs/gnome/orca/src/orca/orca.py,v
retrieving revision 1.145
diff -p -u -r1.145 orca.py
--- orca/src/orca/orca.py	26 Jul 2006 01:29:09 -0000	1.145
+++ orca/src/orca/orca.py	26 Jul 2006 14:26:47 -0000
@@ -970,6 +970,15 @@ def init(registry):
     if _initialized:
         return False
 
+    # Do not hang on initialization if we can help it.
+    #
+    if settings.timeoutCallback and (settings.timeoutTime > 0):
+        timer = threading.Timer(settings.timeoutTime,
+                                settings.timeoutCallback)
+        timer.start()
+    else:
+        timer = None
+
     # Note that we have moved the Orca specific keybindings to the default
     # script, so _keyBindings is currently empty. The logic is retained
     # here, just in case we wish to reinstate them in the future.
@@ -990,6 +999,10 @@ def init(registry):
 
     registry.registerKeystrokeListeners(_processKeyboardEvent)
 
+    if timer:
+        timer.cancel()
+        del timer
+
     _initialized = True
     return True
 
@@ -1002,6 +1015,15 @@ def start(registry):
     if not _initialized:
         init(registry)
 
+    # Do not hang on startup if we can help it.
+    #
+    if settings.timeoutCallback and (settings.timeoutTime > 0):
+        timer = threading.Timer(settings.timeoutTime,
+                                settings.timeoutCallback)
+        timer.start()
+    else:
+        timer = None
+
     try:
         speech.speak(_("Welcome to Orca."))
         braille.displayMessage(_("Welcome to Orca."))
@@ -1027,13 +1049,18 @@ def start(registry):
 
     _switchToPresentationManager(0) # focus_tracking_presenter
 
+    if timer:
+        timer.cancel()
+        del timer
+
     registry.start()
 
 def abort():
     os._exit(1)
 
 def timeout():
-    print "TIMEOUT: Hung while trying to shutdown.  Aborting."
+    debug.println(debug.LEVEL_SEVERE,
+                  "TIMEOUT: something has hung.  Aborting.")
     abort()
 
 def shutdown(script=None, inputEvent=None):
@@ -1049,12 +1076,14 @@ def shutdown(script=None, inputEvent=Non
     if not _initialized:
         return False
 
-    # [[[TODO: WDW - the timer stuff is an experiment to see if
-    # we can recover from hangs.  It's only experimental, so it's
-    # commented out for now.]]]
+    # Try to say goodbye, but be defensive if something has hung.
     #
-    #timer = threading.Timer(5.0, timeout)
-    #timer.start()
+    if settings.timeoutCallback and (settings.timeoutTime > 0):
+        timer = threading.Timer(settings.timeoutTime,
+                                settings.timeoutCallback)
+        timer.start()
+    else:
+        timer = None
 
     speech.speak(_("goodbye."))
     braille.displayMessage(_("Goodbye."))
@@ -1081,8 +1110,9 @@ def shutdown(script=None, inputEvent=Non
 
     registry.stop()
 
-    #timer.cancel()
-    #del timer
+    if timer:
+        timer.cancel()
+        del timer
 
     _initialized = False
     return True
@@ -1097,7 +1127,7 @@ def shutdownOnSignal(signum, frame):
 
     debug.println(debug.LEVEL_ALL, "Current stack is:")
     debug.printStack(debug.LEVEL_ALL)
-    
+
     # Well...we'll try to exit nicely, but if we keep getting called,
     # something bad is happening, so just quit.
     #
@@ -1126,10 +1156,6 @@ def abortOnSignal(signum, frame):
     debug.println(debug.LEVEL_ALL,
                   "Aborting due to signal = %d" \
                   % signum)
-
-    debug.println(debug.LEVEL_ALL, "Current stack is:")
-    debug.printStack(debug.LEVEL_ALL)
-    
     abort()
 
 def usage():
@@ -1148,6 +1174,12 @@ def usage():
     pass
 
 def main():
+    # Method to call when we think something might be hung.
+    #
+    settings.timeoutCallback = timeout
+
+    # Various signal handlers we want to listen for.
+    #
     signal.signal(signal.SIGHUP, shutdownOnSignal)
     signal.signal(signal.SIGINT, shutdownOnSignal)
     signal.signal(signal.SIGTERM, shutdownOnSignal)
Index: orca/src/orca/settings.py
===================================================================
RCS file: /cvs/gnome/orca/src/orca/settings.py,v
retrieving revision 1.68
diff -p -u -r1.68 settings.py
--- orca/src/orca/settings.py	19 Jul 2006 20:46:28 -0000	1.68
+++ orca/src/orca/settings.py	26 Jul 2006 14:26:47 -0000
@@ -330,6 +330,15 @@ useBonoboMain           = True
 #
 debugEventQueue         = False
 
+# The timeout value (in seconds) and callback used to determine if
+# Orca has hung or not.  The only setting one should muck with here is
+# the timeoutTime unless you want to create a custom callback handler
+# for the timeout.  See braille.py, atspi.py, and orca.py:init for how
+# these are used.
+#
+timeoutTime             = 10.0 # a value of 0 means don't do hang checking
+timeoutCallback         = None # Set by orca.py:init to orca.timeout
+
 # Assists with dealing with CORBA COMM_FAILURES.  A failure doesn't
 # always mean an object disappeared - there just might be a network
 # glitch.  So, on COMM_FAILURES, we might retry a few times before
