- @raise LookupError: When the i-th desktop cannot be retrieved
- """
- try:
- return self.reg.getDesktop(i)
- except Exception, e:
- raise LookupError(e)
-
- def registerEventListener(self, client, *names):
- """
- Registers a new client callback for the given event names. Supports
- registration for all subevents if only partial event name is specified.
- Do not include a trailing colon.
-
- For example, 'object' will register for all object events,
- 'object:property-change' will register for all property change events,
- and 'object:property-change:accessible-parent' will register only for the
- parent property change event.
-
- Registered clients will not be automatically removed when the client dies.
- To ensure the client is properly garbage collected, call
- L{deregisterEventListener}.
-
- @param client: Callable to be invoked when the event occurs
- @type client: callable
- @param names: List of full or partial event names
- @type names: list of string
- """
- for name in names:
- # store the callback for each specific event name
- self._registerClients(client, name)
-
- def deregisterEventListener(self, client, *names):
- """
- Unregisters an existing client callback for the given event names. Supports
- unregistration for all subevents if only partial event name is specified.
- Do not include a trailing colon.
-
- This method must be called to ensure a client registered by
- L{registerEventListener} is properly garbage collected.
-
- @param client: Client callback to remove
- @type client: callable
- @param names: List of full or partial event names
- @type names: list of string
- @return: Were event names specified for which the given client was not
- registered?
- @rtype: boolean
- """
- missed = False
- for name in names:
- # remove the callback for each specific event name
- missed |= self._unregisterClients(client, name)
- return missed
-
- def registerKeystrokeListener(self, client, key_set=[], mask=0,
- kind=(constants.KEY_PRESSED_EVENT,
- constants.KEY_RELEASED_EVENT),
- synchronous=True, preemptive=True,
- global_=False):
- """
- Registers a listener for key stroke events.
-
- @param client: Callable to be invoked when the event occurs
- @type client: callable
- @param key_set: Set of hardware key codes to stop monitoring. Leave empty
- to indicate all keys.
- @type key_set: list of integer
- @param mask: When the mask is None, the codes in the key_set will be
- monitored only when no modifier is held. When the mask is an
- integer, keys in the key_set will be monitored only when the modifiers in
- 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, iterable, None
- @param kind: Kind of events to watch, KEY_PRESSED_EVENT or
- KEY_RELEASED_EVENT.
- @type kind: list
- @param synchronous: Should the callback notification be synchronous, giving
- the client the chance to consume the event?
- @type synchronous: boolean
- @param preemptive: Should the callback be allowed to preempt / consume the
- event?
- @type preemptive: boolean
- @param global_: Should callback occur even if an application not supporting
- AT-SPI is in the foreground? (requires xevie)
- @type global_: boolean
- """
- try:
- # see if we already have an observer for this client
- ob = self.clients[client]
- except KeyError:
- # create a new device observer for this client
- ob = _DeviceObserver(self, synchronous, preemptive, global_)
- # 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)
-
- def deregisterKeystrokeListener(self, client, key_set=[], mask=0,
- kind=(constants.KEY_PRESSED_EVENT,
- constants.KEY_RELEASED_EVENT)):
- """
- Deregisters a listener for key stroke events.
-
- @param client: Callable to be invoked when the event occurs
- @type client: callable
- @param key_set: Set of hardware key codes to stop monitoring. Leave empty
- to indicate all keys.
- @type key_set: list of integer
- @param mask: When the mask is None, the codes in the key_set will be
- monitored only when no modifier is held. When the mask is an
- integer, keys in the key_set will be monitored only when the modifiers in
- 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, iterable, None
- @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or
- KEY_RELEASED_EVENT.
- @type kind: list
- @raise KeyError: When the client isn't already registered for events
- """
- # 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)
-
- def generateKeyboardEvent(self, keycode, keysym, kind):
- """
- Generates a keyboard event. One of the keycode or the keysym parameters
- should be specified and the other should be None. The kind parameter is
- required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
- KEY_SYM, or KEY_STRING.
-
- @param keycode: Hardware keycode or None
- @type keycode: integer
- @param keysym: Symbolic key string or None
- @type keysym: string
- @param kind: Kind of event to synthesize
- @type kind: integer
- """
- if keysym is None:
- self.dev.generateKeyboardEvent(keycode, '', kind)
- else:
- self.dev.generateKeyboardEvent(None, keysym, kind)
-
- def generateMouseEvent(self, x, y, name):
- """
- Generates a mouse event at the given absolute x and y coordinate. The kind
- of event generated is specified by the name. For example, MOUSE_B1P
- (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3
- double-click).
-
- @param x: Horizontal coordinate, usually left-hand oriented
- @type x: integer
- @param y: Vertical coordinate, usually left-hand oriented
- @type y: integer
- @param name: Name of the event to generate
- @type name: string
- """
- self.dev.generateMouseEvent(x, y, name)
-
- def handleDeviceEvent(self, event, ob):
- """
- 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 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.
-
- @param event: AT-SPI device event
- @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 False
- # make the call to the client
- try:
- return client(event) or event.consume
- except Exception:
- # print the exception, but don't let it stop notification
- traceback.print_exc()
-
- def handleEvent(self, event):
- """
- Handles an AT-SPI event by either queuing it for later dispatch when the
- L{Registry.async} flag is set, or dispatching it immediately.
-
- @param event: AT-SPI event
- @type event: L{event.Event}
- """
- if self.async:
- # queue for now
- self.queue.put_nowait(event)
- else:
- # dispatch immediately
- self._dispatchEvent(event)
-
- def _dispatchEvent(self, event):
- """
- 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
- 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[et.name]
- except KeyError:
- 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:
- consume = client(event) or False
- except Exception:
- # print the exception, but don't let it stop notification
- traceback.print_exc()
- 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
- names. Allows a client to incompletely specify an event name in order to
- register for subevents without specifying their full names manually.
-
- @param client: Client callback to receive event notifications
- @type client: callable
- @param name: Partial or full event name
- @type name: string
- """
- try:
- # look for an event name in our event tree dictionary
- events = constants.EVENT_TREE[name]
- except KeyError:
- # if the event name doesn't exist, it's a leaf event meaning there are
- # no subtypes for that event
- # add this client to the list of clients already in the dictionary
- # using the event name as the key; if there are no clients yet for this
- # event, insert an empty list into the dictionary before appending
- # the client
- et = event.EventType(name)
- clients = self.clients.setdefault(et.name, [])
- try:
- # if this succeeds, this client is already registered for the given
- # event type, so ignore the request
- clients.index(client)
- except ValueError:
- # else register the client
- clients.append(client)
- self._registerObserver(name)
- else:
- # if the event name does exist in the tree, there are subevents for
- # this event; loop through them calling this method again to get to
- # the leaf events
- for e in events:
- self._registerClients(client, e)
-
- def _unregisterClients(self, client, name):
- """
- Internal method that recursively unassociates a client with AT-SPI event
- names. Allows a client to incompletely specify an event name in order to
- unregister for subevents without specifying their full names manually.
-
- @param client: Client callback to receive event notifications
- @type client: callable
- @param name: Partial or full event name
- @type name: string
- """
- missed = False
- try:
- # look for an event name in our event tree dictionary
- events = constants.EVENT_TREE[name]
- except KeyError:
- try:
- # if the event name doesn't exist, it's a leaf event meaning there are
- # no subtypes for that event
- # get the list of registered clients and try to remove the one provided
- et = event.EventType(name)
- clients = self.clients[et.name]
- clients.remove(client)
- self._unregisterObserver(name)
- except (ValueError, KeyError):
- # ignore any exceptions indicating the client is not registered
- missed = True
- return missed
- # if the event name does exist in the tree, there are subevents for this
- # event; loop through them calling this method again to get to the leaf
- # events
- for e in events:
- missed |= self._unregisterClients(client, e)
- return missed
-
- def _registerObserver(self, name):
- """
- Creates a new L{_Observer} to watch for events of the given type or
- returns the existing observer if one is already registered. One
- L{_Observer} is created for each leaf in the L{constants.EVENT_TREE} or
- any event name not found in the tree.
-
- @param name: Raw name of the event to observe
- @type name: string
- @return: L{_Observer} object that is monitoring the event
- @rtype: L{_Observer}
- """
- et = event.EventType(name)
- try:
- # see if an observer already exists for this event
- ob = self.observers[et.name]
- except KeyError:
- # build a new observer if one does not exist
- ob = _EventObserver(self)
- # we have to register for the raw name because it may be different from
- # the parsed name determined by EventType (e.g. trailing ':' might be
- # missing)
- ob.register(self.reg, name)
- self.observers[et.name] = ob
- # increase our client ref count so we know someone new is watching for the
- # event
- ob.clientRef()
- return ob
-
- def _unregisterObserver(self, name):
- """
- Destroys an existing L{_Observer} for the given event type only if no
- clients are registered for the events it is monitoring.
-
- @param name: Name of the event to observe
- @type name: string
- @raise KeyError: When an observer for the given event is not regist