9f08c4b0820c7fc8652f2311207d16112660553d
[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 L{pyLinAcc} reference count on this L{_Observer} by one.
84     This 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 reg: Reference to the raw registry object
144     @type reg: Accessibility.Registry
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 reg: Reference to the raw registry object
169     @type reg: Accessibility.Registry
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
289   @type async: boolean
290   @ivar reg:
291   @type reg: Accessibility.Registry
292   @ivar dev:
293   @type dev: Accessibility.DeviceEventController
294   @ivar queue:
295   @type queue: Queue.Queue
296   @ivar clients:
297   @type clients: dictionary
298   @ivar observers: 
299   @type observers: dictionary
300   '''
301   def __init__(self, reg):
302     '''
303     Stores a reference to the AT-SPI registry. Gets and stores a reference
304     to the DeviceEventController.
305     
306     @param reg: Reference to the AT-SPI registry daemon
307     @type reg: Accessibility.Registry
308     '''
309     self.async = None
310     self.reg = reg
311     self.dev = self.reg.getDeviceEventController()
312     self.queue = Queue.Queue()
313     self.clients = {}
314     self.observers = {}
315     
316   def __call__(self):
317     '''
318     @return: This instance of the registry
319     @rtype: L{Registry}
320     '''
321     return self
322   
323   def start(self, async=False, gil=True):
324     '''
325     Enter the main loop to start receiving and dispatching events.
326     
327     @param async: Should event dispatch be asynchronous (decoupled) from 
328       event receiving from the AT-SPI registry?
329     @type async: boolean
330     @param gil: Add an idle callback which releases the Python GIL for a few
331       milliseconds to allow other threads to run? Necessary if other threads
332       will be used in this process.
333     @type gil: boolean
334     '''
335     self.async = async
336     
337     # register a signal handler for gracefully killing the loop
338     signal.signal(signal.SIGINT, self.stop)
339     signal.signal(signal.SIGTERM, self.stop)
340   
341     if gil:
342       def releaseGIL():
343         time.sleep(1e-5)
344         return True
345       i = gobject.idle_add(releaseGIL)
346       
347     # enter the main loop
348     bonobo.main()
349     
350     if gil:
351       gobject.source_remove(i)
352     
353   def stop(self, *args):
354     '''Quits the main loop.'''
355     try:
356       bonobo.main_quit()
357     except RuntimeError:
358       # ignore errors when quitting (probably already quitting)
359       pass
360     
361   def getDesktopCount(self):
362     '''
363     Gets the number of available desktops.
364     
365     @return: Number of desktops
366     @rtype: integer
367     @raise LookupError: When the count cannot be retrieved
368     '''
369     try:
370       return self.reg.getDesktopCount()
371     except Exception:
372       raise LookupError
373     
374   def getDesktop(self, i):
375     '''
376     Gets a reference to the i-th desktop.
377     
378     @param i: Which desktop to get
379     @type i: integer
380     @return: Desktop reference
381     @rtype: Accessibility.Desktop
382     @raise LookupError: When the i-th desktop cannot be retrieved
383     '''
384     try:
385       return self.reg.getDesktop(i)
386     except Exception, e:
387       raise LookupError(e)
388     
389   def registerEventListener(self, client, *names):
390     '''
391     Registers a new client callback for the given event names. Supports 
392     registration for all subevents if only partial event name is specified.
393     Do not include a trailing colon.
394     
395     For example, 'object' will register for all object events, 
396     'object:property-change' will register for all property change events,
397     and 'object:property-change:accessible-parent' will register only for the
398     parent property change event.
399     
400     Registered clients will not be automatically removed when the client dies.
401     To ensure the client is properly garbage collected, call 
402     L{Manager.removeClient}.
403
404     @param client: Callable to be invoked when the event occurs
405     @type client: callable
406     @param names: List of full or partial event names
407     @type names: list of string
408     '''
409     for name in names:
410       # store the callback for each specific event name
411       self._registerClients(client, name)
412
413   def deregisterEventListener(self, client, *names):
414     '''
415     Unregisters an existing client callback for the given event names. Supports 
416     unregistration for all subevents if only partial event name is specified.
417     Do not include a trailing colon.
418     
419     This method must be called to ensure a client registered by 
420     L{Manager.addClient} is properly garbage collected.
421
422     @param client: Client callback to remove
423     @type client: callable
424     @param names: List of full or partial event names
425     @type names: list of string
426     @return: Were event names specified for which the given client was not
427       registered?
428     @rtype: boolean
429     '''
430     missed = False
431     for name in names:
432       # remove the callback for each specific event name
433       missed |= self._unregisterClients(client, name)
434     return missed
435
436   def registerKeystrokeListener(self, client, key_set=[], mask=0, 
437                                 kind=(constants.KEY_PRESSED_EVENT, 
438                                       constants.KEY_RELEASED_EVENT),
439                                 synchronous=True, preemptive=True, 
440                                 global_=False):
441     '''
442     Registers a listener for key stroke events.
443     
444     @param client: Callable to be invoked when the event occurs
445     @type client: callable
446     @param key_set: Set of hardware key codes to stop monitoring. Leave empty
447       to indicate all keys.
448     @type key_set: list of integer
449     @param mask: When the mask is None, the codes in the key_set will be 
450       monitored only when no modifier is held. When the mask is an 
451       integer, keys in the key_set will be monitored only when the modifiers in
452       the mask are held. When the mask is an iterable over more than one 
453       integer, keys in the key_set will be monitored when any of the modifier
454       combinations in the set are held.
455     @type mask: integer
456     @param kind: Kind of events to watch, KEY_PRESSED_EVENT or 
457       KEY_RELEASED_EVENT.
458     @type kind: list
459     @param synchronous: Should the callback notification be synchronous, giving
460       the client the chance to consume the event?
461     @type synchronous: boolean
462     @param preemptive: Should the callback be allowed to preempt / consume the
463       event?
464     @type preemptive: boolean
465     @param global_: Should callback occur even if an application not supporting
466       AT-SPI is in the foreground? (requires xevie)
467     @type global_: boolean
468     '''
469     try:
470       # see if we already have an observer for this client
471       ob = self.clients[client]
472     except KeyError:
473       # create a new device observer for this client
474       ob = _DeviceObserver(self, synchronous, preemptive, global_)
475       # store the observer to client mapping, and the inverse
476       self.clients[ob] = client
477       self.clients[client] = ob
478     # register for new keystrokes on the observer
479     ob.register(self.dev, key_set, mask, kind)
480
481   def deregisterKeystrokeListener(self, client, key_set=[], mask=0, 
482                                   kind=(constants.KEY_PRESSED_EVENT, 
483                                         constants.KEY_RELEASED_EVENT)):
484     '''
485     Deregisters a listener for key stroke events.
486     
487     @param client: Callable to be invoked when the event occurs
488     @type client: callable
489     @param key_set: Set of hardware key codes to stop monitoring. Leave empty
490       to indicate all keys.
491     @type key_set: list of integer
492     @param mask: When the mask is None, the codes in the key_set will be 
493       monitored only when no modifier is held. When the mask is an 
494       integer, keys in the key_set will be monitored only when the modifiers in
495       the mask are held. When the mask is an iterable over more than one 
496       integer, keys in the key_set will be monitored when any of the modifier
497       combinations in the set are held.
498     @type mask: integer
499     @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or 
500       KEY_RELEASED_EVENT.
501     @type kind: list
502     @raise KeyError: When the client isn't already registered for events
503     '''
504     # see if we already have an observer for this client
505     ob = self.clients[client]
506     # register for new keystrokes on the observer
507     ob.unregister(self.dev, key_set, mask, kind)
508
509   def generateKeyboardEvent(self, keycode, keysym, kind):
510     '''
511     Generates a keyboard event. One of the keycode or the keysym parameters
512     should be specified and the other should be None. The kind parameter is 
513     required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
514     KEY_SYM, or KEY_STRING.
515     
516     @param keycode: Hardware keycode or None
517     @type keycode: integer
518     @param keysym: Symbolic key string or None
519     @type keysym: string
520     @param kind: Kind of event to synthesize
521     @type kind: integer
522     '''
523     if keysym is None:
524       self.dev.generateKeyboardEvent(keycode, '', kind)
525     else:
526       self.dev.generateKeyboardEvent(None, keysym, kind)
527   
528   def generateMouseEvent(self, x, y, name):
529     '''
530     Generates a mouse event at the given absolute x and y coordinate. The kind
531     of event generated is specified by the name. For example, MOUSE_B1P 
532     (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3 
533     double-click).
534     
535     @param x: Horizontal coordinate, usually left-hand oriented
536     @type x: integer
537     @param y: Vertical coordinate, usually left-hand oriented
538     @type y: integer
539     @param name: Name of the event to generate
540     @type name: string
541     '''
542     self.dev.generateMouseEvent(x, y, name)
543     
544   def handleDeviceEvent(self, event, ob):
545     '''
546     Dispatches L{event.DeviceEvent}s to registered clients. Clients are called
547     in the order they were registered for the given AT-SPI event. If any
548     client sets the L{event.DeviceEvent.consume} flag to True, callbacks cease
549     for the event for clients of this registry instance. Clients of other
550     registry instances and clients in other processes may be affected
551     depending on the values of synchronous and preemptive used when invoking
552     L{registerKeystrokeListener}. 
553     
554     @note: Asynchronous dispatch of device events is not supported.
555     
556     @param event: AT-SPI device event
557     @type event: L{event.DeviceEvent}
558     @param ob: Observer that received the event
559     @type ob: L{_DeviceObserver}
560     '''
561     try:
562       # try to get the client registered for this event type
563       client = self.clients[ob]
564     except KeyError:
565       # client may have unregistered recently, ignore event
566       return
567     # make the call to the client
568     try:
569       client(event)
570     except Exception:
571       # print the exception, but don't let it stop notification
572       traceback.print_exc()
573  
574   def handleEvent(self, event):
575     '''    
576     Handles an AT-SPI event by either queuing it for later dispatch when the
577     L{async} flag is set, or dispatching it immediately.
578
579     @param event: AT-SPI event
580     @type event: L{event.Event}
581     '''
582     if self.async:
583       # queue for now
584       self.queue.put_nowait(event)
585     else:
586       # dispatch immediately
587       self._dispatchEvent(event)
588
589   def _dispatchEvent(self, event):
590     '''
591     Dispatches L{event.Event}s to registered clients. Clients are called in
592     the order they were registered for the given AT-SPI event. If any client
593     sets the L{Event} consume flag to True, callbacks cease for the event for
594     clients of this registry instance. Clients of other registry instances and
595     clients in other processes are unaffected.
596
597     @param event: AT-SPI event
598     @type event: L{event.Event}
599     '''
600     try:
601       # try to get the client registered for this event type
602       clients = self.clients[event.type.name]
603     except KeyError:
604       # client may have unregistered recently, ignore event
605       return
606     # make the call to each client
607     for client in clients:
608       try:
609         client(event)
610       except Exception:
611         # print the exception, but don't let it stop notification
612         traceback.print_exc()
613       if event.consume:
614         # don't allow further processing if the consume flag is set
615         break
616
617   def _registerClients(self, client, name):
618     '''
619     Internal method that recursively associates a client with AT-SPI event 
620     names. Allows a client to incompletely specify an event name in order to 
621     register for subevents without specifying their full names manually.
622     
623     @param client: Client callback to receive event notifications
624     @type client: callable
625     @param name: Partial or full event name
626     @type name: string
627     '''
628     try:
629       # look for an event name in our event tree dictionary
630       events = constants.EVENT_TREE[name]
631     except KeyError:
632       # if the event name doesn't exist, it's a leaf event meaning there are
633       # no subtypes for that event
634       # add this client to the list of clients already in the dictionary 
635       # using the event name as the key; if there are no clients yet for this 
636       # event, insert an empty list into the dictionary before appending 
637       # the client
638       et = event.EventType(name)
639       clients = self.clients.setdefault(et.name, [])
640       try:
641         # if this succeeds, this client is already registered for the given
642         # event type, so ignore the request
643         clients.index(client)
644       except ValueError:
645         # else register the client
646         clients.append(client)
647         self._registerObserver(name)
648     else:
649         # if the event name does exist in the tree, there are subevents for
650         # this event; loop through them calling this method again to get to
651         # the leaf events
652         for e in events:
653           self._registerClients(client, e)
654       
655   def _unregisterClients(self, client, name):
656     '''
657     Internal method that recursively unassociates a client with AT-SPI event 
658     names. Allows a client to incompletely specify an event name in order to 
659     unregister for subevents without specifying their full names manually.
660     
661     @param client: Client callback to receive event notifications
662     @type client: callable
663     @param name: Partial or full event name
664     @type name: string
665     '''
666     missed = False
667     try:
668       # look for an event name in our event tree dictionary
669       events = constants.EVENT_TREE[name]
670     except KeyError:
671       try:
672         # if the event name doesn't exist, it's a leaf event meaning there are
673         # no subtypes for that event
674         # get the list of registered clients and try to remove the one provided
675         et = event.EventType(name)
676         clients = self.clients[et.name]
677         clients.remove(client)
678         self._unregisterObserver(name)
679       except (ValueError, KeyError):
680         # ignore any exceptions indicating the client is not registered
681         missed = True
682       return missed
683     # if the event name does exist in the tree, there are subevents for this 
684     # event; loop through them calling this method again to get to the leaf
685     # events
686     for e in events:
687       missed |= self._unregisterClients(client, e)
688     return missed
689   
690   def _registerObserver(self, name):
691     '''    
692     Creates a new L{_Observer} to watch for events of the given type or
693     returns the existing observer if one is already registered. One
694     L{_Observer} is created for each leaf in the L{constants.EVENT_TREE} or
695     any event name not found in the tree.
696    
697     @param name: Raw name of the event to observe
698     @type name: string
699     @return: L{_Observer} object that is monitoring the event
700     @rtype: L{_Observer}
701     '''
702     et = event.EventType(name)
703     try:
704       # see if an observer already exists for this event
705       ob = self.observers[et.name]
706     except KeyError:
707       # build a new observer if one does not exist
708       ob = _EventObserver(self)
709       # we have to register for the raw name because it may be different from
710       # the parsed name determined by EventType (e.g. trailing ':' might be 
711       # missing)
712       ob.register(self.reg, name)
713       self.observers[et.name] = ob
714     # increase our client ref count so we know someone new is watching for the 
715     # event
716     ob.clientRef()
717     return ob
718     
719   def _unregisterObserver(self, name):
720     '''
721     Destroys an existing L{Observer} for the given event type only if no clients 
722     are registered for the events it is monitoring.
723     
724     @param name: Name of the event to observe
725     @type name: string
726     @raise KeyError: When an observer for the given event is not regist
727     '''
728     et = event.EventType(name)
729     # see if an observer already exists for this event
730     ob = self.observers[et.name]
731     ob.clientUnref()
732     if ob.getClientRefCount() == 0:
733       ob.unregister(self.registry, name)
734       del self.observers[et.name]