@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
'''
@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
'''
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):
'''
'''
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
'''
# 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):
'''
'''
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:
except RuntimeError:
# ignore errors when quitting (probably already quitting)
pass
+ self.flushEvents()
def getDesktopCount(self):
'''
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
# 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)
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
'''
# 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)
'''
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.
@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()
'''
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
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]