2007-04-18 Li Yuan <li.yuan@sun.com>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / registry.py
1 '''
2 Registry that hides some of the details of registering for AT-SPI events and
3 starting and stopping the main program loop.
4
5 @todo: PP: when to destroy device listener?
6
7 @author: Peter Parente
8 @organization: IBM Corporation
9 @copyright: Copyright (c) 2005, 2007 IBM Corporation
10 @license: LGPL
11
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Library General Public
14 License as published by the Free Software Foundation; either
15 version 2 of the License, or (at your option) any later version.
16
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 Library General Public License for more details.
21
22 You should have received a copy of the GNU Library General Public
23 License along with this library; if not, write to the
24 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 Boston, MA 02111-1307, USA.
26
27 Portions of this code originally licensed and copyright (c) 2005, 2007
28 IBM Corporation under the BSD license, available at
29 U{http://www.opensource.org/licenses/bsd-license.php}
30 '''
31 import signal
32 import time
33 import weakref
34 import Queue
35 import traceback
36 import ORBit
37 import bonobo
38 import gobject
39 import Accessibility
40 import Accessibility__POA
41 import utils
42 import constants
43 import event
44
45 class _Observer(object):
46   '''
47   Parent class for all event observers. Dispatches all received events to the 
48   L{Registry} that created this L{_Observer}. Provides basic reference counting
49   functionality needed by L{Registry} to determine when an L{_Observer} can be
50   released for garbage collection. 
51   
52   The reference counting provided by this class is independent of the reference
53   counting used by CORBA. Keeping the counts separate makes it easier for the
54   L{Registry} to detect when an L{_Observer} can be freed in the 
55   L{Registry._unregisterObserver} method.
56   
57   @ivar registry: Reference to the L{Registry} that created this L{_Observer}
58   @type registry: weakref.proxy to L{Registry}
59   @ivar ref_count: Reference count on this L{_Observer}
60   @type ref_count: integer
61   '''
62   def __init__(self, registry):
63     '''
64     Stores a reference to the creating L{Registry}. Intializes the reference
65     count on this object to zero.
66     
67     @param registry: The L{Registry} that created this observer
68     @type registry: weakref.proxy to L{Registry}
69     '''
70     self.registry = weakref.proxy(registry)
71     self.ref_count = 0
72
73   def clientRef(self):
74     '''
75     Increments the Python reference count on this L{_Observer} by one. This
76     method is called when a new client is registered in L{Registry} to receive
77     notification of an event type monitored by this L{_Observer}.
78     '''
79     self.ref_count += 1
80     
81   def clientUnref(self):
82     '''    
83     Decrements the pyatspi reference count on this L{_Observer} by one. This
84     method is called when a client is unregistered in L{Registry} to stop
85     receiving notifications of an event type monitored by this L{_Observer}.
86     '''
87     self.ref_count -= 1
88     
89   def getClientRefCount(self):
90     '''
91     @return: Current Python reference count on this L{_Observer}
92     @rtype: integer
93     '''
94     return self.ref_count
95   
96   def ref(self): 
97     '''Required by CORBA. Does nothing.'''
98     pass
99     
100   def unref(self): 
101     '''Required by CORBA. Does nothing.'''
102     pass
103
104 class _DeviceObserver(_Observer, Accessibility__POA.DeviceEventListener):
105   '''
106   Observes keyboard press and release events.
107   
108   @ivar registry: The L{Registry} that created this observer
109   @type registry: L{Registry}
110   @ivar key_set: Set of keys to monitor
111   @type key_set: list of integer
112   @ivar mask: Watch for key events while these modifiers are held
113   @type mask: integer
114   @ivar kind: Kind of events to monitor
115   @type kind: integer
116   @ivar mode: Keyboard event mode
117   @type mode: Accessibility.EventListenerMode
118   '''
119   def __init__(self, registry, synchronous, preemptive, global_):
120     '''
121     Creates a mode object that defines when key events will be received from 
122     the system. Stores all other information for later registration.
123     
124     @param registry: The L{Registry} that created this observer
125     @type registry: L{Registry}
126     @param synchronous: Handle the key event synchronously?
127     @type synchronous: boolean
128     @param preemptive: Allow event to be consumed?
129     @type preemptive: boolean
130     @param global_: Watch for events on inaccessible applications too?
131     @type global_: boolean
132     '''
133     _Observer.__init__(self, registry)   
134     self.mode = Accessibility.EventListenerMode()
135     self.mode.preemptive = preemptive
136     self.mode.synchronous = synchronous
137     self.mode._global = global_    
138    
139   def register(self, dc, key_set, mask, kind):
140     '''
141     Starts keyboard event monitoring.
142     
143     @param dc: Reference to a device controller
144     @type dc: Accessibility.DeviceEventController
145     @param key_set: Set of keys to monitor
146     @type key_set: list of integer
147     @param mask: Integer modifier mask or an iterable over multiple masks to
148       unapply all at once
149     @type mask: integer or iterable
150     @param kind: Kind of events to monitor
151     @type kind: integer
152     '''
153     try:
154       # check if the mask is iterable
155       iter(mask)
156     except TypeError:
157       # register a single integer if not
158       dc.registerKeystrokeListener(self._this(), key_set, mask, kind, 
159                                    self.mode)
160     else:
161       for m in mask:
162         dc.registerKeystrokeListener(self._this(), key_set, m, kind, self.mode)
163
164   def unregister(self, dc, key_set, mask, kind):
165     '''
166     Stops keyboard event monitoring.
167     
168     @param dc: Reference to a device controller
169     @type dc: Accessibility.DeviceEventController
170     @param key_set: Set of keys to monitor
171     @type key_set: list of integer
172     @param mask: Integer modifier mask or an iterable over multiple masks to
173       unapply all at once
174     @type mask: integer or iterable
175     @param kind: Kind of events to monitor
176     @type kind: integer
177     '''
178     try:
179       # check if the mask is iterable
180       iter(mask)
181     except TypeError:
182       # unregister a single integer if not
183       dc.deregisterKeystrokeListener(self._this(), key_set, mask, kind, 
184                                      self.mode)
185     else:
186       for m in mask:
187         dc.deregisterKeystrokeListener(self._this(), key_set, m, kind, 
188                                        self.mode)
189       
190   def queryInterface(self, repo_id):
191     '''
192     Reports that this class only implements the AT-SPI DeviceEventListener 
193     interface. Required by AT-SPI.
194     
195     @param repo_id: Request for an interface 
196     @type repo_id: string
197     @return: The underlying CORBA object for the device event listener
198     @rtype: Accessibility.EventListener
199     '''
200     if repo_id == utils.getInterfaceIID(Accessibility.DeviceEventListener):
201       return self._this()
202     else:
203       return None
204
205   def notifyEvent(self, ev):
206     '''
207     Notifies the L{Registry} that an event has occurred. Wraps the raw event 
208     object in our L{Event} class to support automatic ref and unref calls. An
209     observer can set the L{Event} consume flag to True to indicate this event
210     should not be allowed to pass to other AT-SPI observers or the underlying
211     application.
212     
213     @param ev: Keyboard event
214     @type ev: Accessibility.DeviceEvent
215     @return: Should the event be consumed (True) or allowed to pass on to other
216       AT-SPI observers (False)?
217     @rtype: boolean
218     '''
219     # wrap the device event
220     ev = event.DeviceEvent(ev)
221     self.registry.handleDeviceEvent(ev, self)
222     return ev.consume
223
224 class _EventObserver(_Observer, Accessibility__POA.EventListener):
225   '''
226   Observes all non-keyboard AT-SPI events. Can be reused across event types.
227   '''
228   def register(self, reg, name):
229     '''
230     Starts monitoring for the given event.
231     
232     @param name: Name of the event to start monitoring
233     @type name: string
234     @param reg: Reference to the raw registry object
235     @type reg: Accessibility.Registry
236     '''
237     reg.registerGlobalEventListener(self._this(), name)
238     
239   def unregister(self, reg, name):
240     '''
241     Stops monitoring for the given event.
242     
243     @param name: Name of the event to stop monitoring
244     @type name: string
245     @param reg: Reference to the raw registry object
246     @type reg: Accessibility.Registry
247     '''
248     reg.deregisterGlobalEventListener(self._this(), name)
249
250   def queryInterface(self, repo_id):
251     '''
252     Reports that this class only implements the AT-SPI DeviceEventListener 
253     interface. Required by AT-SPI.
254
255     @param repo_id: Request for an interface 
256     @type repo_id: string
257     @return: The underlying CORBA object for the device event listener
258     @rtype: Accessibility.EventListener
259     '''
260     if repo_id == utils.getInterfaceIID(Accessibility.EventListener):
261       return self._this()
262     else:
263       return None
264
265   def notifyEvent(self, ev):
266     '''
267     Notifies the L{Registry} that an event has occurred. Wraps the raw event 
268     object in our L{Event} class to support automatic ref and unref calls.
269     Aborts on any exception indicating the event could not be wrapped.
270     
271     @param ev: AT-SPI event signal (anything but keyboard)
272     @type ev: Accessibility.Event
273     '''
274     # wrap raw event so ref counts are correct before queueing
275     ev = event.Event(ev)
276     self.registry.handleEvent(ev)
277
278 class Registry(object):
279   '''
280   Wraps the Accessibility.Registry to provide more Pythonic registration for
281   events. 
282   
283   This object should be treated as a singleton, but such treatment is not
284   enforced. You can construct another instance of this object and give it a
285   reference to the Accessibility.Registry singleton. Doing so is harmless and
286   has no point.
287   
288   @ivar async: Should event dispatch to local listeners be decoupled from event
289     receiving from the registry?
290   @type async: boolean
291   @ivar reg: Reference to the real, wrapped registry object
292   @type reg: Accessibility.Registry
293   @ivar dev: Reference to the device controller
294   @type dev: Accessibility.DeviceEventController
295   @ivar queue: Queue of events awaiting local dispatch
296   @type queue: Queue.Queue
297   @ivar clients: Map of event names to client listeners
298   @type clients: dictionary
299   @ivar observers: Map of event names to AT-SPI L{_Observer} objects
300   @type observers: dictionary
301   '''
302   def __init__(self, reg):
303     '''
304     Stores a reference to the AT-SPI registry. Gets and stores a reference
305     to the DeviceEventController.
306     
307     @param reg: Reference to the AT-SPI registry daemon
308     @type reg: Accessibility.Registry
309     '''
310     self.async = None
311     self.reg = reg
312     self.dev = self.reg.getDeviceEventController()
313     self.queue = Queue.Queue()
314     self.clients = {}
315     self.observers = {}
316     
317   def __call__(self):
318     '''
319     @return: This instance of the registry
320     @rtype: L{Registry}
321     '''
322     return self
323   
324   def start(self, async=False, gil=True):
325     '''
326     Enter the main loop to start receiving and dispatching events.
327     
328     @param async: Should event dispatch be asynchronous (decoupled) from 
329       event receiving from the AT-SPI registry?
330     @type async: boolean
331     @param gil: Add an idle callback which releases the Python GIL for a few
332       milliseconds to allow other threads to run? Necessary if other threads
333       will be used in this process.
334     @type gil: boolean
335     '''
336     self.async = async
337     
338     # register a signal handler for gracefully killing the loop
339     signal.signal(signal.SIGINT, self.stop)
340     signal.signal(signal.SIGTERM, self.stop)
341   
342     if gil:
343       def releaseGIL():
344         time.sleep(1e-5)
345         return True
346       i = gobject.idle_add(releaseGIL)
347       
348     # enter the main loop
349     bonobo.main()
350     
351     if gil:
352       gobject.source_remove(i)
353     
354   def stop(self, *args):
355     '''Quits the main loop.'''
356     try:
357       bonobo.main_quit()
358     except RuntimeError:
359       # ignore errors when quitting (probably already quitting)
360       pass
361     
362   def getDesktopCount(self):
363     '''
364     Gets the number of available desktops.
365     
366     @return: Number of desktops
367     @rtype: integer
368     @raise LookupError: When the count cannot be retrieved
369     '''
370     try:
371       return self.reg.getDesktopCount()
372     except Exception:
373       raise LookupError
374     
375   def getDesktop(self, i):
376     '''
377     Gets a reference to the i-th desktop.
378     
379     @param i: Which desktop to get
380     @type i: integer
381     @return: Desktop reference
382     @rtype: Accessibility.Desktop
383     @raise LookupError: When the i-th desktop cannot be retrieved
384     '''
385     try:
386       return self.reg.getDesktop(i)
387     except Exception, e:
388       raise LookupError(e)
389     
390   def registerEventListener(self, client, *names):
391     '''
392     Registers a new client callback for the given event names. Supports 
393     registration for all subevents if only partial event name is specified.
394     Do not include a trailing colon.
395     
396     For example, 'object' will register for all object events, 
397     'object:property-change' will register for all property change events,
398     and 'object:property-change:accessible-parent' will register only for the
399     parent property change event.
400     
401     Registered clients will not be automatically removed when the client dies.
402     To ensure the client is properly garbage collected, call 
403     L{deregisterEventListener}.
404
405     @param client: Callable to be invoked when the event occurs
406     @type client: callable
407     @param names: List of full or partial event names
408     @type names: list of string
409     '''
410     for name in names:
411       # store the callback for each specific event name
412       self._registerClients(client, name)
413
414   def deregisterEventListener(self, client, *names):
415     '''
416     Unregisters an existing client callback for the given event names. Supports 
417     unregistration for all subevents if only partial event name is specified.
418     Do not include a trailing colon.
419     
420     This method must be called to ensure a client registered by
421     L{registerEventListener} is properly garbage collected.
422
423     @param client: Client callback to remove
424     @type client: callable
425     @param names: List of full or partial event names
426     @type names: list of string
427     @return: Were event names specified for which the given client was not
428       registered?
429     @rtype: boolean
430     '''
431     missed = False
432     for name in names:
433       # remove the callback for each specific event name
434       missed |= self._unregisterClients(client, name)
435     return missed
436
437   def registerKeystrokeListener(self, client, key_set=[], mask=0, 
438                                 kind=(constants.KEY_PRESSED_EVENT, 
439                                       constants.KEY_RELEASED_EVENT),
440                                 synchronous=True, preemptive=True, 
441                                 global_=False):
442     '''
443     Registers a listener for key stroke events.
444     
445     @param client: Callable to be invoked when the event occurs
446     @type client: callable
447     @param key_set: Set of hardware key codes to stop monitoring. Leave empty
448       to indicate all keys.
449     @type key_set: list of integer
450     @param mask: When the mask is None, the codes in the key_set will be 
451       monitored only when no modifier is held. When the mask is an 
452       integer, keys in the key_set will be monitored only when the modifiers in
453       the mask are held. When the mask is an iterable over more than one 
454       integer, keys in the key_set will be monitored when any of the modifier
455       combinations in the set are held.
456     @type mask: integer
457     @param kind: Kind of events to watch, KEY_PRESSED_EVENT or 
458       KEY_RELEASED_EVENT.
459     @type kind: list
460     @param synchronous: Should the callback notification be synchronous, giving
461       the client the chance to consume the event?
462     @type synchronous: boolean
463     @param preemptive: Should the callback be allowed to preempt / consume the
464       event?
465     @type preemptive: boolean
466     @param global_: Should callback occur even if an application not supporting
467       AT-SPI is in the foreground? (requires xevie)
468     @type global_: boolean
469     '''
470     try:
471       # see if we already have an observer for this client
472       ob = self.clients[client]
473     except KeyError:
474       # create a new device observer for this client
475       ob = _DeviceObserver(self, synchronous, preemptive, global_)
476       # store the observer to client mapping, and the inverse
477       self.clients[ob] = client
478       self.clients[client] = ob
479     # register for new keystrokes on the observer
480     ob.register(self.dev, key_set, mask, kind)
481
482   def deregisterKeystrokeListener(self, client, key_set=[], mask=0, 
483                                   kind=(constants.KEY_PRESSED_EVENT, 
484                                         constants.KEY_RELEASED_EVENT)):
485     '''
486     Deregisters a listener for key stroke events.
487     
488     @param client: Callable to be invoked when the event occurs
489     @type client: callable
490     @param key_set: Set of hardware key codes to stop monitoring. Leave empty
491       to indicate all keys.
492     @type key_set: list of integer
493     @param mask: When the mask is None, the codes in the key_set will be 
494       monitored only when no modifier is held. When the mask is an 
495       integer, keys in the key_set will be monitored only when the modifiers in
496       the mask are held. When the mask is an iterable over more than one 
497       integer, keys in the key_set will be monitored when any of the modifier
498       combinations in the set are held.
499     @type mask: integer
500     @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or 
501       KEY_RELEASED_EVENT.
502     @type kind: list
503     @raise KeyError: When the client isn't already registered for events
504     '''
505     # see if we already have an observer for this client
506     ob = self.clients[client]
507     # register for new keystrokes on the observer
508     ob.unregister(self.dev, key_set, mask, kind)
509
510   def generateKeyboardEvent(self, keycode, keysym, kind):
511     '''
512     Generates a keyboard event. One of the keycode or the keysym parameters
513     should be specified and the other should be None. The kind parameter is 
514     required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
515     KEY_SYM, or KEY_STRING.
516     
517     @param keycode: Hardware keycode or None
518     @type keycode: integer
519     @param keysym: Symbolic key string or None
520     @type keysym: string
521     @param kind: Kind of event to synthesize
522     @type kind: integer
523     '''
524     if keysym is None:
525       self.dev.generateKeyboardEvent(keycode, '', kind)
526     else:
527       self.dev.generateKeyboardEvent(None, keysym, kind)
528   
529   def generateMouseEvent(self, x, y, name):
530     '''
531     Generates a mouse event at the given absolute x and y coordinate. The kind
532     of event generated is specified by the name. For example, MOUSE_B1P 
533     (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3 
534     double-click).
535     
536     @param x: Horizontal coordinate, usually left-hand oriented
537     @type x: integer
538     @param y: Vertical coordinate, usually left-hand oriented
539     @type y: integer
540     @param name: Name of the event to generate
541     @type name: string
542     '''
543     self.dev.generateMouseEvent(x, y, name)
544     
545   def handleDeviceEvent(self, event, ob):
546     '''
547     Dispatches L{event.DeviceEvent}s to registered clients. Clients are called
548     in the order they were registered for the given AT-SPI event. If any
549     client sets the L{event.DeviceEvent.consume} flag to True, callbacks cease
550     for the event for clients of this registry instance. Clients of other
551     registry instances and clients in other processes may be affected
552     depending on the values of synchronous and preemptive used when invoking
553     L{registerKeystrokeListener}. 
554     
555     @note: Asynchronous dispatch of device events is not supported.
556     
557     @param event: AT-SPI device event
558     @type event: L{event.DeviceEvent}
559     @param ob: Observer that received the event
560     @type ob: L{_DeviceObserver}
561     '''
562     try:
563       # try to get the client registered for this event type
564       client = self.clients[ob]
565     except KeyError:
566       # client may have unregistered recently, ignore event
567       return
568     # make the call to the client
569     try:
570       client(event)
571     except Exception:
572       # print the exception, but don't let it stop notification
573       traceback.print_exc()
574  
575   def handleEvent(self, event):
576     '''    
577     Handles an AT-SPI event by either queuing it for later dispatch when the
578     L{Registry.async} flag is set, or dispatching it immediately.
579
580     @param event: AT-SPI event
581     @type event: L{event.Event}
582     '''
583     if self.async:
584       # queue for now
585       self.queue.put_nowait(event)
586     else:
587       # dispatch immediately
588       self._dispatchEvent(event)
589
590   def _dispatchEvent(self, event):
591     '''
592     Dispatches L{event.Event}s to registered clients. Clients are called in
593     the order they were registered for the given AT-SPI event. If any client
594     sets the L{Event} consume flag to True, callbacks cease for the event for
595     clients of this registry instance. Clients of other registry instances and
596     clients in other processes are unaffected.
597
598     @param event: AT-SPI event
599     @type event: L{event.Event}
600     '''
601     try:
602       # try to get the client registered for this event type
603       clients = self.clients[event.type.name]
604     except KeyError:
605       # client may have unregistered recently, ignore event
606       return
607     # make the call to each client
608     for client in clients:
609       try:
610         client(event)
611       except Exception:
612         # print the exception, but don't let it stop notification
613         traceback.print_exc()
614       if event.consume:
615         # don't allow further processing if the consume flag is set
616         break
617
618   def _registerClients(self, client, name):
619     '''
620     Internal method that recursively associates a client with AT-SPI event 
621     names. Allows a client to incompletely specify an event name in order to 
622     register for subevents without specifying their full names manually.
623     
624     @param client: Client callback to receive event notifications
625     @type client: callable
626     @param name: Partial or full event name
627     @type name: string
628     '''
629     try:
630       # look for an event name in our event tree dictionary
631       events = constants.EVENT_TREE[name]
632     except KeyError:
633       # if the event name doesn't exist, it's a leaf event meaning there are
634       # no subtypes for that event
635       # add this client to the list of clients already in the dictionary 
636       # using the event name as the key; if there are no clients yet for this 
637       # event, insert an empty list into the dictionary before appending 
638       # the client
639       et = event.EventType(name)
640       clients = self.clients.setdefault(et.name, [])
641       try:
642         # if this succeeds, this client is already registered for the given
643         # event type, so ignore the request
644         clients.index(client)
645       except ValueError:
646         # else register the client
647         clients.append(client)
648         self._registerObserver(name)
649     else:
650         # if the event name does exist in the tree, there are subevents for
651         # this event; loop through them calling this method again to get to
652         # the leaf events
653         for e in events:
654           self._registerClients(client, e)
655       
656   def _unregisterClients(self, client, name):
657     '''
658     Internal method that recursively unassociates a client with AT-SPI event 
659     names. Allows a client to incompletely specify an event name in order to 
660     unregister for subevents without specifying their full names manually.
661     
662     @param client: Client callback to receive event notifications
663     @type client: callable
664     @param name: Partial or full event name
665     @type name: string
666     '''
667     missed = False
668     try:
669       # look for an event name in our event tree dictionary
670       events = constants.EVENT_TREE[name]
671     except KeyError:
672       try:
673         # if the event name doesn't exist, it's a leaf event meaning there are
674         # no subtypes for that event
675         # get the list of registered clients and try to remove the one provided
676         et = event.EventType(name)
677         clients = self.clients[et.name]
678         clients.remove(client)
679         self._unregisterObserver(name)
680       except (ValueError, KeyError):
681         # ignore any exceptions indicating the client is not registered
682         missed = True
683       return missed
684     # if the event name does exist in the tree, there are subevents for this 
685     # event; loop through them calling this method again to get to the leaf
686     # events
687     for e in events:
688       missed |= self._unregisterClients(client, e)
689     return missed
690   
691   def _registerObserver(self, name):
692     '''    
693     Creates a new L{_Observer} to watch for events of the given type or
694     returns the existing observer if one is already registered. One
695     L{_Observer} is created for each leaf in the L{constants.EVENT_TREE} or
696     any event name not found in the tree.
697    
698     @param name: Raw name of the event to observe
699     @type name: string
700     @return: L{_Observer} object that is monitoring the event
701     @rtype: L{_Observer}
702     '''
703     et = event.EventType(name)
704     try:
705       # see if an observer already exists for this event
706       ob = self.observers[et.name]
707     except KeyError:
708       # build a new observer if one does not exist
709       ob = _EventObserver(self)
710       # we have to register for the raw name because it may be different from
711       # the parsed name determined by EventType (e.g. trailing ':' might be 
712       # missing)
713       ob.register(self.reg, name)
714       self.observers[et.name] = ob
715     # increase our client ref count so we know someone new is watching for the 
716     # event
717     ob.clientRef()
718     return ob
719     
720   def _unregisterObserver(self, name):
721     '''    
722     Destroys an existing L{_Observer} for the given event type only if no
723     clients are registered for the events it is monitoring.
724     
725     @param name: Name of the event to observe
726     @type name: string
727     @raise KeyError: When an observer for the given event is not regist
728     '''
729     et = event.EventType(name)
730     # see if an observer already exists for this event
731     ob = self.observers[et.name]
732     ob.clientUnref()
733     if ob.getClientRefCount() == 0:
734       ob.unregister(self.registry, name)
735       del self.observers[et.name]