2008-08-21 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / event.py
1 #Copyright (C) 2008 Codethink Ltd
2 #copyright: Copyright (c) 2005, 2007 IBM Corporation
3
4 #This library is free software; you can redistribute it and/or
5 #modify it under the terms of the GNU Lesser General Public
6 #License version 2 as published by the Free Software Foundation.
7
8 #This program is distributed in the hope that it will be useful,
9 #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #GNU General Public License for more details.
12 #You should have received a copy of the GNU Lesser General Public License
13 #along with this program; if not, write to the Free Software
14 #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15
16 #Portions of this code originally licensed and copyright (c) 2005, 2007
17 #IBM Corporation under the BSD license, available at
18 #U{http://www.opensource.org/licenses/bsd-license.php}
19
20 #authors: Peter Parente, Mark Doffman
21
22 #import signal
23 #import time
24 #import weakref
25 #import Queue
26 #import traceback
27 #import gobject
28 #import utils
29 #import constants
30 #import event
31
32 ATSPI_DEVICE_EVENT_CONTROLLER = 'org.freedesktop.atspi.DeviceEventController'
33 ATSPI_DEVICE_EVENT_LISTENER = 'org.freedesktop.atspi.DeviceEventListener'
34
35 import constants
36
37 class _Observer(object):
38         """
39         Parent class for all event observers. Dispatches all received events to the 
40         L{Registry} that created this L{_Observer}. Provides basic reference counting
41         functionality needed by L{Registry} to determine when an L{_Observer} can be
42         released for garbage collection. 
43         
44         The reference counting provided by this class is independent of the reference
45         counting used by CORBA. Keeping the counts separate makes it easier for the
46         L{Registry} to detect when an L{_Observer} can be freed in the 
47         L{Registry._unregisterObserver} method.
48         
49         @ivar registry: Reference to the L{Registry} that created this L{_Observer}
50         @type registry: weakref.proxy to L{Registry}
51         @ivar ref_count: Reference count on this L{_Observer}
52         @type ref_count: integer
53         """
54         def __init__(self, registry):
55                 """
56                 Stores a reference to the creating L{Registry}. Intializes the reference
57                 count on this object to zero.
58                 
59                 @param registry: The L{Registry} that created this observer
60                 @type registry: weakref.proxy to L{Registry}
61                 """
62                 self.registry = weakref.proxy(registry)
63                 self.ref_count = 0
64
65         def clientRef(self):
66                 """
67                 Increments the Python reference count on this L{_Observer} by one. This
68                 method is called when a new client is registered in L{Registry} to receive
69                 notification of an event type monitored by this L{_Observer}.
70                 """
71                 self.ref_count += 1
72                 
73         def clientUnref(self):
74                 """             
75                 Decrements the pyatspi reference count on this L{_Observer} by one. This
76                 method is called when a client is unregistered in L{Registry} to stop
77                 receiving notifications of an event type monitored by this L{_Observer}.
78                 """
79                 self.ref_count -= 1
80                 
81         def getClientRefCount(self):
82                 """
83                 @return: Current Python reference count on this L{_Observer}
84                 @rtype: integer
85                 """
86                 return self.ref_count
87         
88         def ref(self): 
89                 """Required by CORBA. Does nothing."""
90                 pass
91                 
92         def unref(self): 
93                 """Required by CORBA. Does nothing."""
94                 pass
95
96 class _DeviceObserver(_Observer, Accessibility__POA.DeviceEventListener):
97         """
98         Observes keyboard press and release events.
99         
100         @ivar registry: The L{Registry} that created this observer
101         @type registry: L{Registry}
102         @ivar key_set: Set of keys to monitor
103         @type key_set: list of integer
104         @ivar mask: Watch for key events while these modifiers are held
105         @type mask: integer
106         @ivar kind: Kind of events to monitor
107         @type kind: integer
108         @ivar mode: Keyboard event mode
109         @type mode: Accessibility.EventListenerMode
110         """
111         def __init__(self, registry, synchronous, preemptive, global_):
112                 """
113                 Creates a mode object that defines when key events will be received from 
114                 the system. Stores all other information for later registration.
115                 
116                 @param registry: The L{Registry} that created this observer
117                 @type registry: L{Registry}
118                 @param synchronous: Handle the key event synchronously?
119                 @type synchronous: boolean
120                 @param preemptive: Allow event to be consumed?
121                 @type preemptive: boolean
122                 @param global_: Watch for events on inaccessible applications too?
123                 @type global_: boolean
124                 """
125                 _Observer.__init__(self, registry)       
126                 self.mode = Accessibility.EventListenerMode()
127                 self.mode.preemptive = preemptive
128                 self.mode.synchronous = synchronous
129                 self.mode._global = global_             
130          
131         def register(self, dc, key_set, mask, kind):
132                 """
133                 Starts keyboard event monitoring.
134                 
135                 @param dc: Reference to a device controller
136                 @type dc: Accessibility.DeviceEventController
137                 @param key_set: Set of keys to monitor
138                 @type key_set: list of integer
139                 @param mask: Integer modifier mask or an iterable over multiple masks to
140                         unapply all at once
141                 @type mask: integer, iterable, or None
142                 @param kind: Kind of events to monitor
143                 @type kind: integer
144                 """
145                 try:
146                         # check if the mask is iterable
147                         iter(mask)
148                 except TypeError:
149                         # register a single integer if not
150                         dc.registerKeystrokeListener(self._this(), key_set, mask, kind, 
151                                                                                                                                          self.mode)
152                 else:
153                         for m in mask:
154                                 dc.registerKeystrokeListener(self._this(), key_set, m, kind, self.mode)
155
156         def unregister(self, dc, key_set, mask, kind):
157                 """
158                 Stops keyboard event monitoring.
159                 
160                 @param dc: Reference to a device controller
161                 @type dc: Accessibility.DeviceEventController
162                 @param key_set: Set of keys to monitor
163                 @type key_set: list of integer
164                 @param mask: Integer modifier mask or an iterable over multiple masks to
165                         unapply all at once
166                 @type mask: integer, iterable, or None
167                 @param kind: Kind of events to monitor
168                 @type kind: integer
169                 """
170                 try:
171                         # check if the mask is iterable
172                         iter(mask)
173                 except TypeError:
174                         # unregister a single integer if not
175                         dc.deregisterKeystrokeListener(self._this(), key_set, mask, kind)
176                 else:
177                         for m in mask:
178                                 dc.deregisterKeystrokeListener(self._this(), key_set, m, kind)
179                         
180         def queryInterface(self, repo_id):
181                 """
182                 Reports that this class only implements the AT-SPI DeviceEventListener 
183                 interface. Required by AT-SPI.
184                 
185                 @param repo_id: Request for an interface 
186                 @type repo_id: string
187                 @return: The underlying CORBA object for the device event listener
188                 @rtype: Accessibility.EventListener
189                 """
190                 if repo_id == utils.getInterfaceIID(Accessibility.DeviceEventListener):
191                         return self._this()
192                 else:
193                         return None
194
195         def notifyEvent(self, ev):
196                 """
197                 Notifies the L{Registry} that an event has occurred. Wraps the raw event 
198                 object in our L{Event} class to support automatic ref and unref calls. An
199                 observer can return True to indicate this event should not be allowed to pass 
200                 to other AT-SPI observers or the underlying application.
201                 
202                 @param ev: Keyboard event
203                 @type ev: Accessibility.DeviceEvent
204                 @return: Should the event be consumed (True) or allowed to pass on to other
205                         AT-SPI observers (False)?
206                 @rtype: boolean
207                 """
208                 # wrap the device event
209                 ev = event.DeviceEvent(ev)
210                 return self.registry.handleDeviceEvent(ev, self)
211
212 class _EventObserver(_Observer, Accessibility__POA.EventListener):
213         """
214         Observes all non-keyboard AT-SPI events. Can be reused across event types.
215         """
216         def register(self, reg, name):
217                 """
218                 Starts monitoring for the given event.
219                 
220                 @param name: Name of the event to start monitoring
221                 @type name: string
222                 @param reg: Reference to the raw registry object
223                 @type reg: Accessibility.Registry
224                 """
225                 reg.registerGlobalEventListener(self._this(), name)
226                 
227         def unregister(self, reg, name):
228                 """
229                 Stops monitoring for the given event.
230                 
231                 @param name: Name of the event to stop monitoring
232                 @type name: string
233                 @param reg: Reference to the raw registry object
234                 @type reg: Accessibility.Registry
235                 """
236                 reg.deregisterGlobalEventListener(self._this(), name)
237
238         def queryInterface(self, repo_id):
239                 """
240                 Reports that this class only implements the AT-SPI DeviceEventListener 
241                 interface. Required by AT-SPI.
242
243                 @param repo_id: Request for an interface 
244                 @type repo_id: string
245                 @return: The underlying CORBA object for the device event listener
246                 @rtype: Accessibility.EventListener
247                 """
248                 if repo_id == utils.getInterfaceIID(Accessibility.EventListener):
249                         return self._this()
250                 else:
251                         return None
252
253         def notifyEvent(self, ev):
254                 """
255                 Notifies the L{Registry} that an event has occurred. Wraps the raw event 
256                 object in our L{Event} class to support automatic ref and unref calls.
257                 Aborts on any exception indicating the event could not be wrapped.
258                 
259                 @param ev: AT-SPI event signal (anything but keyboard)
260                 @type ev: Accessibility.Event
261                 """
262                 # wrap raw event so ref counts are correct before queueing
263                 ev = event.Event(ev)
264                 self.registry.handleEvent(ev)
265
266 class DeviceEvent(object):
267         """
268         Wraps an AT-SPI device event with a more Pythonic interface. Primarily adds
269         a consume attribute which can be used to cease propagation of a device event.
270         
271         @ivar consume: Should this event be consumed and not allowed to pass on to
272                 observers further down the dispatch chain in this process or possibly
273                 system wide?
274         @type consume: boolean
275         @ivar type: Kind of event, KEY_PRESSED_EVENT or KEY_RELEASED_EVENT
276         @type type: Accessibility.EventType
277         @ivar id: Serial identifier for this key event
278         @type id: integer
279         @ivar hw_code: Hardware scan code for the key
280         @type hw_code: integer
281         @ivar modifiers: Modifiers held at the time of the key event
282         @type modifiers: integer
283         @ivar timestamp: Time at which the event occurred relative to some platform
284                 dependent starting point (e.g. XWindows start time)
285         @type timestamp: integer
286         @ivar event_string: String describing the key pressed (e.g. keysym)
287         @type event_string: string
288         @ivar is_text: Is the event representative of text to be inserted (True), or 
289                 of a control key (False)?
290         @type is_text: boolean
291         """
292         def __init__(self, event):
293                 """
294                 Attaches event data to this object.
295                 
296                 @param event: Event object
297                 @type event: Accessibility.DeviceEvent
298                 """
299                 self.consume = False
300                 self.type = event.type
301                 self.id = event.id
302                 self.hw_code = event.hw_code
303                 self.modifiers = event.modifiers
304                 self.timestamp = event.timestamp
305                 self.event_string = event.event_string
306                 self.is_text = event.is_text
307                 
308         def __str__(self):
309                 """
310                 Builds a human readable representation of the event.
311
312                 @return: Event description
313                 @rtype: string
314                 """
315                 if self.type == constants.KEY_PRESSED_EVENT:
316                         kind = 'pressed'
317                 elif self.type == constants.KEY_RELEASED_EVENT:
318                         kind = 'released'
319                 return """\
320 %s
321 \thw_code: %d
322 \tevent_string: %s
323 \tmodifiers: %d
324 \tid: %d
325 \ttimestamp: %d
326 \tis_text: %s""" % (kind, self.hw_code, self.event_string, self.modifiers,
327                 self.id, self.timestamp, self.is_text)
328
329 class Event(object):
330         """
331         Wraps an AT-SPI event with a more Pythonic interface managing exceptions,
332         the differences in any_data across versions, and the reference counting of
333         accessibles provided with the event.
334         
335         @note: All unmarked attributes of this class should be considered public
336                 readable and writable as the class is acting as a record object.
337                 
338         @ivar consume: Should this event be consumed and not allowed to pass on to
339                 observers further down the dispatch chain in this process?
340         @type consume: boolean
341         @ivar type: The type of the AT-SPI event
342         @type type: L{EventType}
343         @ivar detail1: First AT-SPI event parameter
344         @type detail1: integer
345         @ivar detail2: Second AT-SPI event parameter
346         @type detail2: integer
347         @ivar any_data: Extra AT-SPI data payload
348         @type any_data: object
349         @ivar host_application: Application owning the event source
350         @type host_application: Accessibility.Application
351         @ivar source_name: Name of the event source at the time of event dispatch
352         @type source_name: string
353         @ivar source_role: Role of the event source at the time of event dispatch
354         @type source_role: Accessibility.Role
355         @ivar source: Source of the event
356         @type source: Accessibility.Accessible
357         """
358         def __init__(self, event):
359                 """
360                 Extracts information from the provided event. If the event is a "normal" 
361                 event, pulls the detail1, detail2, any_data, and source values out of the
362                 given object and stores it in this object. If the event is a device event,
363                 key ID is stored in detail1, scan code is stored in detail2, key name, 
364                 key modifiers (e.g. ALT, CTRL, etc.), is text flag, and timestamp are 
365                 stored as a 4-tuple in any_data, and source is None (since key events are
366                 global).
367
368                 @param event: Event from an AT-SPI callback
369                 @type event: Accessibility.Event or Accessibility.DeviceEvent
370                 """
371                 # always start out assuming no consume
372                 self.consume = False
373                 self.type = EventType(event.type)
374                 self.detail1 = event.detail1
375                 self.detail2 = event.detail2
376                 # store the event source and increase the reference count since event 
377                 # sources are borrowed references; the AccessibleMixin automatically
378                 # decrements it later
379                 try:
380                         event.source.ref()
381                 except AttributeError:
382                         pass
383                 self.source = event.source
384
385                 # process any_data in a at-spi version independent manner
386                 details = event.any_data.value()
387                 try:
388                         # see if we have a "new" any_data object which is an EventDetails struct
389                         self.any_data = details.any_data.value()
390                 except Exception:
391                         # any kind of error means we have an "old" any_data object and None of
392                         # the extra data so set them to None
393                         self.any_data = details
394                         self.host_application = None
395                         self.source_name = None
396                         self.source_role = None
397                 else:
398                         # the rest of the data should be here, so retrieve it
399                         self.host_application = details.host_application
400                         self.source_name = details.source_name
401                         self.source_role = details.source_role
402                 try:
403                         # if we received an accessible, be sure to increment the ref count
404                         self.any_data.ref()
405                 except AttributeError:
406                         pass
407                 try:
408                         # if we received a host application, be sure to increment the ref count
409                         self.host_application.ref()
410                 except AttributeError:
411                         pass
412
413         def __str__(self):
414                 """
415                 Builds a human readable representation of the event including event type,
416                 parameters, and source info.
417
418                 @return: Event description
419                 @rtype: string
420                 """
421                 return '%s(%s, %s, %s)\n\tsource: %s\n\thost_application: %s' % \
422                                          (self.type, self.detail1, self.detail2, self.any_data,
423                                                 self.source, self.host_application)
424         
425 class EventType(str):
426         """
427         Wraps the AT-SPI event type string so its components can be accessed 
428         individually as klass (can't use the keyword class), major, minor, and detail 
429         (klass:major:minor:detail).
430         
431         @note: All attributes of an instance of this class should be considered 
432                 public readable as it is acting a a struct.
433         @ivar klass: Most general event type identifier (object, window, mouse, etc.)
434         @type klass: string
435         @ivar major: Second level event type description
436         @type major: string
437         @ivar minor: Third level event type description
438         @type minor: string
439         @ivar detail: Lowest level event type description
440         @type detail: string
441         @ivar name: Full, unparsed event name as received from AT-SPI
442         @type name: string
443         @cvar format: Names of the event string components
444         @type format: 4-tuple of string
445         """
446         format = ('klass', 'major', 'minor', 'detail')
447
448         def __init__(self, name):
449                 """             
450                 Parses the full AT-SPI event name into its components
451                 (klass:major:minor:detail). If the provided event name is an integer
452                 instead of a string, then the event is really a device event.
453                 
454                 @param name: Full AT-SPI event name
455                 @type name: string
456                 @raise AttributeError: When the given event name is not a valid string 
457                 """
458                 # get rid of any leading and trailing ':' separators
459                 self.value = name.strip(':')
460                 self.name = self.value # Backward compatability
461                 self.klass = None
462                 self.major = None
463                 self.minor = None
464                 self.detail = None
465                 
466                 # split type according to delimiters
467                 split = self.value.split(':', 3)
468                 # loop over all the components
469                 for i in xrange(len(split)):
470                         # store values of attributes in this object
471                         setattr(self, self.format[i], split[i])
472
473 class Registry(object):
474         """
475         Wraps the Accessibility.Registry to provide more Pythonic registration for
476         events. 
477         
478         This object should be treated as a singleton, but such treatment is not
479         enforced. You can construct another instance of this object and give it a
480         reference to the Accessibility.Registry singleton. Doing so is harmless and
481         has no point.
482         
483         @ivar async: Should event dispatch to local listeners be decoupled from event
484                 receiving from the registry?
485         @type async: boolean
486         @ivar reg: Reference to the real, wrapped registry object
487         @type reg: Accessibility.Registry
488         @ivar dev: Reference to the device controller
489         @type dev: Accessibility.DeviceEventController
490         @ivar queue: Queue of events awaiting local dispatch
491         @type queue: Queue.Queue
492         @ivar clients: Map of event names to client listeners
493         @type clients: dictionary
494         @ivar observers: Map of event names to AT-SPI L{_Observer} objects
495         @type observers: dictionary
496         """
497
498         _REGISTRY_NAME = 'org.freedesktop.atspi.Registry'
499
500         def __init__(self, app_name=None):
501                 """
502                 Stores a reference to the AT-SPI registry. Gets and stores a reference
503                 to the DeviceEventController.
504                 
505                 @param reg: Reference to the AT-SPI registry daemon
506                 @type reg: Accessibility.Registry
507                 """
508                 self._bus = dbus.SessionBus()
509                 if app_path:
510                         self._app_name = app_name
511                         self._cache = TestApplicationCache(self._bus, app_name)
512
513                 #self.async = None
514                 #self.reg = reg
515                 #self.dev = self.reg.getDeviceEventController()
516                 #self.queue = Queue.Queue()
517                 #self.clients = {}
518                 #self.observers = {}
519                 
520         def __call__(self):
521                 """
522                 @return: This instance of the registry
523                 @rtype: L{Registry}
524                 """
525                 return self
526         
527         def start(self, async=False, gil=True):
528                 """
529                 Enter the main loop to start receiving and dispatching events.
530                 
531                 @param async: Should event dispatch be asynchronous (decoupled) from 
532                         event receiving from the AT-SPI registry?
533                 @type async: boolean
534                 @param gil: Add an idle callback which releases the Python GIL for a few
535                         milliseconds to allow other threads to run? Necessary if other threads
536                         will be used in this process.
537                         Note - No Longer used.
538                 @type gil: boolean
539                 """
540                 self._loop = gobject.MainLoop()
541                 self._loop.run()
542
543         def stop(self, *args):
544                 """Quits the main loop."""
545                 self._loop.quit()
546                 self.flushEvents()
547                 
548         def getDesktopCount(self):
549                 """
550                 Gets the number of available desktops.
551                 
552                 @return: Number of desktops
553                 @rtype: integer
554                 """
555                 return 1
556                 
557         def getDesktop(self, i):
558                 """
559                 Gets a reference to the i-th desktop.
560                 
561                 @param i: Which desktop to get
562                 @type i: integer
563                 @return: Desktop reference
564                 @rtype: Accessibility.Desktop
565                 """
566                 return Desktop(self._cache)
567                 
568         def registerEventListener(self, client, *names):
569                 """
570                 Registers a new client callback for the given event names. Supports 
571                 registration for all subevents if only partial event name is specified.
572                 Do not include a trailing colon.
573                 
574                 For example, 'object' will register for all object events, 
575                 'object:property-change' will register for all property change events,
576                 and 'object:property-change:accessible-parent' will register only for the
577                 parent property change event.
578                 
579                 Registered clients will not be automatically removed when the client dies.
580                 To ensure the client is properly garbage collected, call 
581                 L{deregisterEventListener}.
582
583                 @param client: Callable to be invoked when the event occurs
584                 @type client: callable
585                 @param names: List of full or partial event names
586                 @type names: list of string
587                 """
588                 for name in names:
589                         # store the callback for each specific event name
590                         self._registerClients(client, name)
591
592         def deregisterEventListener(self, client, *names):
593                 """
594                 Unregisters an existing client callback for the given event names. Supports 
595                 unregistration for all subevents if only partial event name is specified.
596                 Do not include a trailing colon.
597                 
598                 This method must be called to ensure a client registered by
599                 L{registerEventListener} is properly garbage collected.
600
601                 @param client: Client callback to remove
602                 @type client: callable
603                 @param names: List of full or partial event names
604                 @type names: list of string
605                 @return: Were event names specified for which the given client was not
606                         registered?
607                 @rtype: boolean
608                 """
609                 missed = False
610                 for name in names:
611                         # remove the callback for each specific event name
612                         missed |= self._unregisterClients(client, name)
613                 return missed
614
615         def registerKeystrokeListener(self, client, key_set=[], mask=0, 
616                                                                                                                                 kind=(constants.KEY_PRESSED_EVENT, 
617                                                                                                                                                         constants.KEY_RELEASED_EVENT),
618                                                                                                                                 synchronous=True, preemptive=True, 
619                                                                                                                                 global_=False):
620                 """
621                 Registers a listener for key stroke events.
622                 
623                 @param client: Callable to be invoked when the event occurs
624                 @type client: callable
625                 @param key_set: Set of hardware key codes to stop monitoring. Leave empty
626                         to indicate all keys.
627                 @type key_set: list of integer
628                 @param mask: When the mask is None, the codes in the key_set will be 
629                         monitored only when no modifier is held. When the mask is an 
630                         integer, keys in the key_set will be monitored only when the modifiers in
631                         the mask are held. When the mask is an iterable over more than one 
632                         integer, keys in the key_set will be monitored when any of the modifier
633                         combinations in the set are held.
634                 @type mask: integer, iterable, None
635                 @param kind: Kind of events to watch, KEY_PRESSED_EVENT or 
636                         KEY_RELEASED_EVENT.
637                 @type kind: list
638                 @param synchronous: Should the callback notification be synchronous, giving
639                         the client the chance to consume the event?
640                 @type synchronous: boolean
641                 @param preemptive: Should the callback be allowed to preempt / consume the
642                         event?
643                 @type preemptive: boolean
644                 @param global_: Should callback occur even if an application not supporting
645                         AT-SPI is in the foreground? (requires xevie)
646                 @type global_: boolean
647                 """
648                 try:
649                         # see if we already have an observer for this client
650                         ob = self.clients[client]
651                 except KeyError:
652                         # create a new device observer for this client
653                         ob = _DeviceObserver(self, synchronous, preemptive, global_)
654                         # store the observer to client mapping, and the inverse
655                         self.clients[ob] = client
656                         self.clients[client] = ob
657                 if mask is None:
658                         # None means all modifier combinations
659                         mask = utils.allModifiers()
660                 # register for new keystrokes on the observer
661                 ob.register(self.dev, key_set, mask, kind)
662
663         def deregisterKeystrokeListener(self, client, key_set=[], mask=0, 
664                                                                                                                                         kind=(constants.KEY_PRESSED_EVENT, 
665                                                                                                                                                                 constants.KEY_RELEASED_EVENT)):
666                 """
667                 Deregisters a listener for key stroke events.
668                 
669                 @param client: Callable to be invoked when the event occurs
670                 @type client: callable
671                 @param key_set: Set of hardware key codes to stop monitoring. Leave empty
672                         to indicate all keys.
673                 @type key_set: list of integer
674                 @param mask: When the mask is None, the codes in the key_set will be 
675                         monitored only when no modifier is held. When the mask is an 
676                         integer, keys in the key_set will be monitored only when the modifiers in
677                         the mask are held. When the mask is an iterable over more than one 
678                         integer, keys in the key_set will be monitored when any of the modifier
679                         combinations in the set are held.
680                 @type mask: integer, iterable, None
681                 @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or 
682                         KEY_RELEASED_EVENT.
683                 @type kind: list
684                 @raise KeyError: When the client isn't already registered for events
685                 """
686                 # see if we already have an observer for this client
687                 ob = self.clients[client]
688                 if mask is None:
689                         # None means all modifier combinations
690                         mask = utils.allModifiers()
691                 # register for new keystrokes on the observer
692                 ob.unregister(self.dev, key_set, mask, kind)
693
694         def generateKeyboardEvent(self, keycode, keysym, kind):
695                 """
696                 Generates a keyboard event. One of the keycode or the keysym parameters
697                 should be specified and the other should be None. The kind parameter is 
698                 required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
699                 KEY_SYM, or KEY_STRING.
700                 
701                 @param keycode: Hardware keycode or None
702                 @type keycode: integer
703                 @param keysym: Symbolic key string or None
704                 @type keysym: string
705                 @param kind: Kind of event to synthesize
706                 @type kind: integer
707                 """
708                 if keysym is None:
709                         self.dev.generateKeyboardEvent(keycode, '', kind)
710                 else:
711                         self.dev.generateKeyboardEvent(None, keysym, kind)
712         
713         def generateMouseEvent(self, x, y, name):
714                 """
715                 Generates a mouse event at the given absolute x and y coordinate. The kind
716                 of event generated is specified by the name. For example, MOUSE_B1P 
717                 (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3 
718                 double-click).
719                 
720                 @param x: Horizontal coordinate, usually left-hand oriented
721                 @type x: integer
722                 @param y: Vertical coordinate, usually left-hand oriented
723                 @type y: integer
724                 @param name: Name of the event to generate
725                 @type name: string
726                 """
727                 self.dev.generateMouseEvent(x, y, name)
728                 
729         def handleDeviceEvent(self, event, ob):
730                 """
731                 Dispatches L{event.DeviceEvent}s to registered clients. Clients are called
732                 in the order they were registered for the given AT-SPI event. If any
733                 client returns True, callbacks cease for the event for clients of this registry 
734                 instance. Clients of other registry instances and clients in other processes may 
735                 be affected depending on the values of synchronous and preemptive used when invoking
736                 L{registerKeystrokeListener}. 
737                 
738                 @note: Asynchronous dispatch of device events is not supported.
739                 
740                 @param event: AT-SPI device event
741                 @type event: L{event.DeviceEvent}
742                 @param ob: Observer that received the event
743                 @type ob: L{_DeviceObserver}
744
745                 @return: Should the event be consumed (True) or allowed to pass on to other
746                         AT-SPI observers (False)?
747                 @rtype: boolean
748                 """
749                 try:
750                         # try to get the client registered for this event type
751                         client = self.clients[ob]
752                 except KeyError:
753                         # client may have unregistered recently, ignore event
754                         return False
755                 # make the call to the client
756                 try:
757                         return client(event) or event.consume
758                 except Exception:
759                         # print the exception, but don't let it stop notification
760                         traceback.print_exc()
761  
762         def handleEvent(self, event):
763                 """             
764                 Handles an AT-SPI event by either queuing it for later dispatch when the
765                 L{Registry.async} flag is set, or dispatching it immediately.
766
767                 @param event: AT-SPI event
768                 @type event: L{event.Event}
769                 """
770                 if self.async:
771                         # queue for now
772                         self.queue.put_nowait(event)
773                 else:
774                         # dispatch immediately
775                         self._dispatchEvent(event)
776
777         def _dispatchEvent(self, event):
778                 """
779                 Dispatches L{event.Event}s to registered clients. Clients are called in
780                 the order they were registered for the given AT-SPI event. If any client
781                 returns True, callbacks cease for the event for clients of this registry 
782                 instance. Clients of other registry instances and clients in other processes 
783                 are unaffected.
784
785                 @param event: AT-SPI event
786                 @type event: L{event.Event}
787                 """
788                 et = event.type
789                 try:
790                         # try to get the client registered for this event type
791                         clients = self.clients[et.name]
792                 except KeyError:
793                         try:
794                                 # we may not have registered for the complete subtree of events
795                                 # if our tree does not list all of a certain type (e.g.
796                                 # object:state-changed:*); try again with klass and major only
797                                 if et.detail is not None:
798                                         # Strip the 'detail' field.
799                                         clients = self.clients['%s:%s:%s' % (et.klass, et.major, et.minor)]
800                                 elif et.minor is not None:
801                                         # The event could possibly be object:state-changed:*.
802                                         clients = self.clients['%s:%s' % (et.klass, et.major)]
803                         except KeyError:
804                                 # client may have unregistered recently, ignore event
805                                 return
806                 # make the call to each client
807                 consume = False
808                 for client in clients:
809                         try:
810                                 consume = client(event) or False
811                         except Exception:
812                                 # print the exception, but don't let it stop notification
813                                 traceback.print_exc()
814                         if consume or event.consume:
815                                 # don't allow further processing if a client returns True
816                                 break
817
818         def flushEvents(self):
819                 """
820                 Flushes the event queue by destroying it and recreating it.
821                 """
822                 self.queue = Queue.Queue()
823
824         def pumpQueuedEvents(self, num=-1):
825                 """
826                 Provides asynch processing of events in the queue by executeing them with 
827                 _dispatchEvent() (as is done immediately when synch processing). 
828                 This method would normally be called from a main loop or idle function.
829
830                 @param num: Number of events to pump. If number is negative it pumps
831                 the entire queue. Default is -1.
832                 @type num: integer
833                 @return: True if queue is not empty after events were pumped.
834                 @rtype: boolean
835                 """
836                 if num < 0:
837                         # Dequeue as many events as currently in the queue.
838                         num = self.queue.qsize()
839                 for i in xrange(num):
840                         try:
841                                 # get next waiting event
842                                 event = self.queue.get_nowait()
843                         except Queue.Empty:
844                                 break
845                         self._dispatchEvent(event)
846
847                 return not self.queue.empty()
848  
849         def _registerClients(self, client, name):
850                 """
851                 Internal method that recursively associates a client with AT-SPI event 
852                 names. Allows a client to incompletely specify an event name in order to 
853                 register for subevents without specifying their full names manually.
854                 
855                 @param client: Client callback to receive event notifications
856                 @type client: callable
857                 @param name: Partial or full event name
858                 @type name: string
859                 """
860                 try:
861                         # look for an event name in our event tree dictionary
862                         events = constants.EVENT_TREE[name]
863                 except KeyError:
864                         # if the event name doesn't exist, it's a leaf event meaning there are
865                         # no subtypes for that event
866                         # add this client to the list of clients already in the dictionary 
867                         # using the event name as the key; if there are no clients yet for this 
868                         # event, insert an empty list into the dictionary before appending 
869                         # the client
870                         et = event.EventType(name)
871                         clients = self.clients.setdefault(et.name, [])
872                         try:
873                                 # if this succeeds, this client is already registered for the given
874                                 # event type, so ignore the request
875                                 clients.index(client)
876                         except ValueError:
877                                 # else register the client
878                                 clients.append(client)
879                                 self._registerObserver(name)
880                 else:
881                                 # if the event name does exist in the tree, there are subevents for
882                                 # this event; loop through them calling this method again to get to
883                                 # the leaf events
884                                 for e in events:
885                                         self._registerClients(client, e)
886                         
887         def _unregisterClients(self, client, name):
888                 """
889                 Internal method that recursively unassociates a client with AT-SPI event 
890                 names. Allows a client to incompletely specify an event name in order to 
891                 unregister for subevents without specifying their full names manually.
892                 
893                 @param client: Client callback to receive event notifications
894                 @type client: callable
895                 @param name: Partial or full event name
896                 @type name: string
897                 """
898                 missed = False
899                 try:
900                         # look for an event name in our event tree dictionary
901                         events = constants.EVENT_TREE[name]
902                 except KeyError:
903                         try:
904                                 # if the event name doesn't exist, it's a leaf event meaning there are
905                                 # no subtypes for that event
906                                 # get the list of registered clients and try to remove the one provided
907                                 et = event.EventType(name)
908                                 clients = self.clients[et.name]
909                                 clients.remove(client)
910                                 self._unregisterObserver(name)
911                         except (ValueError, KeyError):
912                                 # ignore any exceptions indicating the client is not registered
913                                 missed = True
914                         return missed
915                 # if the event name does exist in the tree, there are subevents for this 
916                 # event; loop through them calling this method again to get to the leaf
917                 # events
918                 for e in events:
919                         missed |= self._unregisterClients(client, e)
920                 return missed
921         
922         def _registerObserver(self, name):
923                 """             
924                 Creates a new L{_Observer} to watch for events of the given type or
925                 returns the existing observer if one is already registered. One
926                 L{_Observer} is created for each leaf in the L{constants.EVENT_TREE} or
927                 any event name not found in the tree.
928          
929                 @param name: Raw name of the event to observe
930                 @type name: string
931                 @return: L{_Observer} object that is monitoring the event
932                 @rtype: L{_Observer}
933                 """
934                 et = event.EventType(name)
935                 try:
936                         # see if an observer already exists for this event
937                         ob = self.observers[et.name]
938                 except KeyError:
939                         # build a new observer if one does not exist
940                         ob = _EventObserver(self)
941                         # we have to register for the raw name because it may be different from
942                         # the parsed name determined by EventType (e.g. trailing ':' might be 
943                         # missing)
944                         ob.register(self.reg, name)
945                         self.observers[et.name] = ob
946                 # increase our client ref count so we know someone new is watching for the 
947                 # event
948                 ob.clientRef()
949                 return ob
950                 
951         def _unregisterObserver(self, name):
952                 """             
953                 Destroys an existing L{_Observer} for the given event type only if no
954                 clients are registered for the events it is monitoring.
955                 
956                 @param name: Name of the event to observe
957                 @type name: string
958                 @raise KeyError: When an observer for the given event is not regist
959                 """
960                 et = event.EventType(name)
961                 # see if an observer already exists for this event
962                 ob = self.observers[et.name]
963                 ob.clientUnref()
964                 if ob.getClientRefCount() == 0:
965                         ob.unregister(self.reg, name)
966                         del self.observers[et.name]