* registry.py (Registry.pumpQueuedEvents): Added this method for
[platform/core/uifw/at-spi2-atk.git] / pyatspi / registry.py
index 0ff0eb0..de37259 100644 (file)
@@ -146,7 +146,7 @@ class _DeviceObserver(_Observer, Accessibility__POA.DeviceEventListener):
     @type key_set: list of integer
     @param mask: Integer modifier mask or an iterable over multiple masks to
       unapply all at once
-    @type mask: integer or iterable
+    @type mask: integer, iterable, or None
     @param kind: Kind of events to monitor
     @type kind: integer
     '''
@@ -171,7 +171,7 @@ class _DeviceObserver(_Observer, Accessibility__POA.DeviceEventListener):
     @type key_set: list of integer
     @param mask: Integer modifier mask or an iterable over multiple masks to
       unapply all at once
-    @type mask: integer or iterable
+    @type mask: integer, iterable, or None
     @param kind: Kind of events to monitor
     @type kind: integer
     '''
@@ -180,12 +180,10 @@ class _DeviceObserver(_Observer, Accessibility__POA.DeviceEventListener):
       iter(mask)
     except TypeError:
       # unregister a single integer if not
-      dc.deregisterKeystrokeListener(self._this(), key_set, mask, kind, 
-                                     self.mode)
+      dc.deregisterKeystrokeListener(self._this(), key_set, mask, kind)
     else:
       for m in mask:
-        dc.deregisterKeystrokeListener(self._this(), key_set, m, kind, 
-                                       self.mode)
+        dc.deregisterKeystrokeListener(self._this(), key_set, m, kind)
       
   def queryInterface(self, repo_id):
     '''
@@ -206,9 +204,8 @@ class _DeviceObserver(_Observer, Accessibility__POA.DeviceEventListener):
     '''
     Notifies the L{Registry} that an event has occurred. Wraps the raw event 
     object in our L{Event} class to support automatic ref and unref calls. An
-    observer can set the L{Event} consume flag to True to indicate this event
-    should not be allowed to pass to other AT-SPI observers or the underlying
-    application.
+    observer can return True to indicate this event should not be allowed to pass 
+    to other AT-SPI observers or the underlying application.
     
     @param ev: Keyboard event
     @type ev: Accessibility.DeviceEvent
@@ -218,8 +215,7 @@ class _DeviceObserver(_Observer, Accessibility__POA.DeviceEventListener):
     '''
     # wrap the device event
     ev = event.DeviceEvent(ev)
-    self.registry.handleDeviceEvent(ev, self)
-    return ev.consume
+    return self.registry.handleDeviceEvent(ev, self)
 
 class _EventObserver(_Observer, Accessibility__POA.EventListener):
   '''
@@ -335,22 +331,32 @@ class Registry(object):
     '''
     self.async = async
     
-    # register a signal handler for gracefully killing the loop
-    signal.signal(signal.SIGINT, self.stop)
-    signal.signal(signal.SIGTERM, self.stop)
-  
     if gil:
       def releaseGIL():
-        time.sleep(1e-5)
+        try:
+          time.sleep(1e-5)
+        except KeyboardInterrupt, e:
+          # store the exception for later
+          releaseGIL.keyboard_exception = e
+          self.stop()
         return True
+      # make room for an exception if one occurs during the 
+      releaseGIL.keyboard_exception = None
       i = gobject.idle_add(releaseGIL)
       
     # enter the main loop
-    bonobo.main()
-    
-    if gil:
-      gobject.source_remove(i)
-    
+    try:
+      bonobo.main()
+    finally:
+      # clear all observers
+      for name, ob in self.observers.items():
+        ob.unregister(self.reg, name)
+      if gil:
+        gobject.source_remove(i)
+        if releaseGIL.keyboard_exception is not None:
+          # raise an keyboard exception we may have gotten earlier
+          raise releaseGIL.keyboard_exception
+
   def stop(self, *args):
     '''Quits the main loop.'''
     try:
@@ -358,6 +364,7 @@ class Registry(object):
     except RuntimeError:
       # ignore errors when quitting (probably already quitting)
       pass
+    self.flushEvents()
     
   def getDesktopCount(self):
     '''
@@ -453,7 +460,7 @@ class Registry(object):
       the mask are held. When the mask is an iterable over more than one 
       integer, keys in the key_set will be monitored when any of the modifier
       combinations in the set are held.
-    @type mask: integer
+    @type mask: integer, iterable, None
     @param kind: Kind of events to watch, KEY_PRESSED_EVENT or 
       KEY_RELEASED_EVENT.
     @type kind: list
@@ -476,6 +483,9 @@ class Registry(object):
       # store the observer to client mapping, and the inverse
       self.clients[ob] = client
       self.clients[client] = ob
+    if mask is None:
+      # None means all modifier combinations
+      mask = utils.allModifiers()
     # register for new keystrokes on the observer
     ob.register(self.dev, key_set, mask, kind)
 
@@ -496,7 +506,7 @@ class Registry(object):
       the mask are held. When the mask is an iterable over more than one 
       integer, keys in the key_set will be monitored when any of the modifier
       combinations in the set are held.
-    @type mask: integer
+    @type mask: integer, iterable, None
     @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or 
       KEY_RELEASED_EVENT.
     @type kind: list
@@ -504,6 +514,9 @@ class Registry(object):
     '''
     # see if we already have an observer for this client
     ob = self.clients[client]
+    if mask is None:
+      # None means all modifier combinations
+      mask = utils.allModifiers()
     # register for new keystrokes on the observer
     ob.unregister(self.dev, key_set, mask, kind)
 
@@ -546,10 +559,9 @@ class Registry(object):
     '''
     Dispatches L{event.DeviceEvent}s to registered clients. Clients are called
     in the order they were registered for the given AT-SPI event. If any
-    client sets the L{event.DeviceEvent.consume} flag to True, callbacks cease
-    for the event for clients of this registry instance. Clients of other
-    registry instances and clients in other processes may be affected
-    depending on the values of synchronous and preemptive used when invoking
+    client returns True, callbacks cease for the event for clients of this registry 
+    instance. Clients of other registry instances and clients in other processes may 
+    be affected depending on the values of synchronous and preemptive used when invoking
     L{registerKeystrokeListener}. 
     
     @note: Asynchronous dispatch of device events is not supported.
@@ -558,16 +570,20 @@ class Registry(object):
     @type event: L{event.DeviceEvent}
     @param ob: Observer that received the event
     @type ob: L{_DeviceObserver}
+
+    @return: Should the event be consumed (True) or allowed to pass on to other
+      AT-SPI observers (False)?
+    @rtype: boolean
     '''
     try:
       # try to get the client registered for this event type
       client = self.clients[ob]
     except KeyError:
       # client may have unregistered recently, ignore event
-      return
+      return False
     # make the call to the client
     try:
-      client(event)
+      return client(event) or event.consume
     except Exception:
       # print the exception, but don't let it stop notification
       traceback.print_exc()
@@ -591,30 +607,74 @@ class Registry(object):
     '''
     Dispatches L{event.Event}s to registered clients. Clients are called in
     the order they were registered for the given AT-SPI event. If any client
-    sets the L{Event} consume flag to True, callbacks cease for the event for
-    clients of this registry instance. Clients of other registry instances and
-    clients in other processes are unaffected.
+    returns True, callbacks cease for the event for clients of this registry 
+    instance. Clients of other registry instances and clients in other processes 
+    are unaffected.
 
     @param event: AT-SPI event
     @type event: L{event.Event}
     '''
+    et = event.type
     try:
       # try to get the client registered for this event type
-      clients = self.clients[event.type.name]
+      clients = self.clients[et.name]
     except KeyError:
-      # client may have unregistered recently, ignore event
-      return
+      try:
+        # we may not have registered for the complete subtree of events
+        # if our tree does not list all of a certain type (e.g.
+        # object:state-changed:*); try again with klass and major only
+        if et.detail is not None:
+          # Strip the 'detail' field.
+          clients = self.clients['%s:%s:%s' % (et.klass, et.major, et.minor)]
+        elif et.minor is not None:
+          # The event could possibly be object:state-changed:*.
+          clients = self.clients['%s:%s' % (et.klass, et.major)]
+      except KeyError:
+        # client may have unregistered recently, ignore event
+        return
     # make the call to each client
+    consume = False
     for client in clients:
       try:
-        client(event)
+        consume = client(event) or False
       except Exception:
         # print the exception, but don't let it stop notification
         traceback.print_exc()
-      if event.consume:
-        # don't allow further processing if the consume flag is set
+      if consume or event.consume:
+        # don't allow further processing if a client returns True
         break
 
+  def flushEvents(self):
+    '''
+    Flushes the event queue by destroying it and recreating it.
+    '''
+    self.queue = Queue.Queue()
+
+  def pumpQueuedEvents(self, num=-1):
+    '''
+    Provides asynch processing of events in the queue by executeing them with 
+    _dispatchEvent() (as is done immediately when synch processing). 
+    This method would normally be called from a main loop or idle function.
+
+    @param num: Number of events to pump. If number is negative it pumps
+    the entire queue. Default is -1.
+    @type num: integer
+    @return: True if queue is not empty after events were pumped.
+    @rtype: boolean
+    '''
+    if num < 0:
+      # Dequeue as many events as currently in the queue.
+      num = self.queue.qsize()
+    for i in xrange(num):
+      try:
+        # get next waiting event
+        event = self.queue.get_nowait()
+      except Queue.Empty:
+        break
+      self._dispatchEvent(event)
+
+    return not self.queue.empty()
   def _registerClients(self, client, name):
     '''
     Internal method that recursively associates a client with AT-SPI event 
@@ -731,5 +791,5 @@ class Registry(object):
     ob = self.observers[et.name]
     ob.clientUnref()
     if ob.getClientRefCount() == 0:
-      ob.unregister(self.registry, name)
+      ob.unregister(self.reg, name)
       del self.observers[et.name]