2009-03-12 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.git] / pyatspi / registry.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 os as _os
23 import dbus as _dbus
24 import gobject as _gobject
25
26 from desktop import Desktop as _Desktop
27
28 from event import EventType as _EventType
29 from event import event_type_to_signal_reciever as _event_type_to_signal_reciever
30
31 from applicationcache import TestApplicationCache, ApplicationCache
32
33 from dbus.mainloop.glib import DBusGMainLoop as _DBusGMainLoop
34
35 from Queue import Queue
36 from deviceevent import *
37 from deviceevent import _TestDeviceEventController
38
39 _DBusGMainLoop(set_as_default=True)
40
41 #------------------------------------------------------------------------------
42
43 class _Registry(object):
44         """
45         Wraps the Accessibility.Registry to provide more Pythonic registration for
46         events.
47
48         This object should be treated as a singleton, but such treatment is not
49         enforced. You can construct another instance of this object and give it a
50         reference to the Accessibility.Registry singleton. Doing so is harmless and
51         has no point.
52
53         @ivar async: Should event dispatch to local listeners be decoupled from event
54                 receiving from the registry?
55         @type async: boolean
56         @ivar reg: Reference to the real, wrapped registry object
57         @type reg: Accessibility.Registry
58         @ivar dev: Reference to the device controller
59         @type dev: Accessibility.DeviceEventController
60         @ivar queue: Queue of events awaiting local dispatch
61         @type queue: Queue.Queue
62         @ivar clients: Map of event names to client listeners
63         @type clients: dictionary
64         @ivar observers: Map of event names to AT-SPI L{_Observer} objects
65         @type observers: dictionary
66         """
67
68         def __init__(self):
69                 """
70                 Stores a reference to the AT-SPI registry. Gets and stores a reference
71                 to the DeviceEventController.
72
73                 @param reg: Reference to the AT-SPI registry daemon
74                 @type reg: Accessibility.Registry
75                 """
76                 self._bus = _dbus.SessionBus()
77
78                 app_name = None
79                 if "ATSPI_TEST_APP_NAME" in _os.environ.keys():
80                         app_name = _os.environ["ATSPI_TEST_APP_NAME"]
81
82                 if app_name:
83                         self._app_cache = TestApplicationCache(self, self._bus, app_name)
84                         self.dev = _TestDeviceEventController()
85                 else:
86                         self._app_cache = ApplicationCache(self, self._bus)
87                         self.dev = DeviceEventController(self._bus)
88
89                 self._event_listeners = {}
90
91                 # All of this special casing is for the 'faked'
92                 # events caused by cache updates.
93
94                 self._name_type = _EventType("object:property-change:name")
95                 self._name_listeners = {}
96                 self._description_type = _EventType("object:property-change:description")
97                 self._description_listeners = {}
98                 self._parent_type = _EventType("object:property-change:parent")
99                 self._parent_listeners = {}
100                 self._children_changed_type = _EventType("object:children-changed")
101                 self._children_changed_listeners = {}
102
103                 self.clients = {}
104                 self.deviceClients = {}
105
106         def __call__(self):
107                 """
108                 @return: This instance of the registry
109                 @rtype: L{Registry}
110                 """
111                 return self
112
113         @property
114         def cache (self):
115                 """
116                 This is the accessible application cache through which
117                 all accessible objects are accessed.
118                 """
119                 return self._app_cache
120
121         def start(self, async=False, gil=True):
122                 """
123                 Enter the main loop to start receiving and dispatching events.
124
125                 @param async: Should event dispatch be asynchronous (decoupled) from 
126                         event receiving from the AT-SPI registry?
127                 @type async: boolean
128                 @param gil: Add an idle callback which releases the Python GIL for a few
129                         milliseconds to allow other threads to run? Necessary if other threads
130                         will be used in this process.
131                         Note - No Longer used.
132                 @type gil: boolean
133                 """
134                 self._loop = _gobject.MainLoop()
135                 try:
136                         self._loop.run()
137                 except KeyboardInterrupt:
138                         pass
139
140         def stop(self, *args):
141                 """Quits the main loop."""
142                 self._loop.quit()
143                 self.flushEvents()
144
145         def getDesktopCount(self):
146                 """
147                 Gets the number of available desktops.
148
149                 @return: Number of desktops
150                 @rtype: integer
151                 """
152                 return 1
153
154         def getDesktop(self, i):
155                 """
156                 Gets a reference to the i-th desktop.
157
158                 @param i: Which desktop to get
159                 @type i: integer
160                 @return: Desktop reference
161                 @rtype: Accessibility.Desktop
162                 """
163                 return _Desktop(self._app_cache)
164
165         # -------------------------------------------------------------------------------
166
167         def _callClients(self, register, event):
168                 for client in register.keys():
169                         client(event)
170
171         def _notifyNameChange(self, event):
172                 self._callClients(self._name_listeners, event)
173
174         def _notifyDescriptionChange(self, event):
175                 self._callClients(self._description_listeners, event)
176
177         def _notifyParentChange(self, event):
178                 self._callClients(self._parent_listeners, event)
179
180         def _notifyChildrenChange(self, event):
181                 self._callClients(self._children_changed_listeners, event)
182
183         def _registerFake(self, type, register, client, *names):
184                 """
185                 Registers a client from a register of clients
186                 for 'Fake' events emitted by the cache.
187                 """
188                 try:
189                         registered = register[client]
190                 except KeyError:
191                         registered = []
192                         register[client] = registered
193
194                 for name in names:
195                         new_type = _EventType(name)
196                         if new_type.is_subtype(type):
197                                 registered.append(new_type.name)
198
199                 if registered == []:
200                         del(register[client])
201
202         def _deregisterFake(self, type, register, client, *names):
203                 """
204                 Deregisters a client from a register of clients
205                 for 'Fake' events emitted by the cache.
206                 """
207                 try:
208                         registered = register[client]
209                 except KeyError:
210                         return True
211
212                 for name in names:
213                         remove_type = _EventType(name)
214
215                         copy = registered[:]
216                         for i in range(0, len(copy)):
217                                 type_name = copy[i]
218                                 registered_type = _EventType(type_name)
219
220                                 if remove_type.is_subtype(registered_type):
221                                         del(registered[i])
222
223                 if registered == []:
224                         del(register[client])
225
226         # -------------------------------------------------------------------------------
227
228         def registerEventListener(self, client, *names):
229                 """
230                 Registers a new client callback for the given event names. Supports 
231                 registration for all subevents if only partial event name is specified.
232                 Do not include a trailing colon.
233
234                 For example, 'object' will register for all object events, 
235                 'object:property-change' will register for all property change events,
236                 and 'object:property-change:accessible-parent' will register only for the
237                 parent property change event.
238
239                 Registered clients will not be automatically removed when the client dies.
240                 To ensure the client is properly garbage collected, call 
241                 L{deregisterEventListener}.
242
243                 @param client: Callable to be invoked when the event occurs
244                 @type client: callable
245                 @param names: List of full or partial event names
246                 @type names: list of string
247                 """
248                 try:
249                         registered = self._event_listeners[client]
250                 except KeyError:
251                         registered = []
252                         self._event_listeners[client] = registered
253
254                 for name in names:
255                         new_type = _EventType(name)
256                         registered.append((new_type.name,
257                                            _event_type_to_signal_reciever(self._bus, self._app_cache, client, new_type)))
258
259                 self._registerFake(self._name_type, self._name_listeners, client, *names)
260                 self._registerFake(self._description_type, self._description_listeners, client, *names)
261                 self._registerFake(self._parent_type, self._parent_listeners, client, *names)
262                 self._registerFake(self._children_changed_type, self._children_changed_listeners, client, *names)
263
264         def deregisterEventListener(self, client, *names):
265                 """
266                 Unregisters an existing client callback for the given event names. Supports 
267                 unregistration for all subevents if only partial event name is specified.
268                 Do not include a trailing colon.
269
270                 This method must be called to ensure a client registered by
271                 L{registerEventListener} is properly garbage collected.
272
273                 @param client: Client callback to remove
274                 @type client: callable
275                 @param names: List of full or partial event names
276                 @type names: list of string
277                 @return: Were event names specified for which the given client was not
278                         registered?
279                 @rtype: boolean
280                 """
281                 try:
282                         registered = self._event_listeners[client]
283                 except KeyError:
284                         # Presumably if were trying to deregister a client with
285                         # no names then the return type is always true.
286                         return True
287
288                 missing = False
289
290                 for name in names:
291                         remove_type = _EventType(name)
292                         copy = registered[:]
293                         for i in range (0, len(copy)):
294                                 type_name, signal_match = copy[i]
295                                 registered_type = _EventType(type_name)
296
297                                 if remove_type.is_subtype(registered_type):
298                                         signal_match.remove()
299                                         del(registered[i])
300                                 else:
301                                         missing = True
302
303                 if registered == []:
304                         del(self._event_listeners[client])
305
306                 #TODO Do these account for missing also?
307                 self._deregisterFake(self._name_type, self._name_listeners, client, *names)
308                 self._deregisterFake(self._description_type, self._description_listeners, client, *names)
309                 self._deregisterFake(self._parent_type, self._parent_listeners, client, *names)
310                 self._deregisterFake(self._children_changed_type, self._children_changed_listeners, client, *names)
311
312                 return missing
313
314         # -------------------------------------------------------------------------------
315
316         def registerKeystrokeListener(self,
317                                       client,
318                                       key_set=[],
319                                       mask=0,
320                                       kind=(KEY_PRESSED_EVENT, KEY_RELEASED_EVENT),
321                                       synchronous=True,
322                                       preemptive=True,
323                                       global_=False):
324                 """
325                 Registers a listener for key stroke events.
326
327                 @param client: Callable to be invoked when the event occurs
328                 @type client: callable
329                 @param key_set: Set of hardware key codes to stop monitoring. Leave empty
330                         to indicate all keys.
331                 @type key_set: list of integer
332                 @param mask: When the mask is None, the codes in the key_set will be 
333                         monitored only when no modifier is held. When the mask is an 
334                         integer, keys in the key_set will be monitored only when the modifiers in
335                         the mask are held. When the mask is an iterable over more than one 
336                         integer, keys in the key_set will be monitored when any of the modifier
337                         combinations in the set are held.
338                 @type mask: integer, iterable, None
339                 @param kind: Kind of events to watch, KEY_PRESSED_EVENT or 
340                         KEY_RELEASED_EVENT.
341                 @type kind: list
342                 @param synchronous: Should the callback notification be synchronous, giving
343                         the client the chance to consume the event?
344                 @type synchronous: boolean
345                 @param preemptive: Should the callback be allowed to preempt / consume the
346                         event?
347                 @type preemptive: boolean
348                 @param global_: Should callback occur even if an application not supporting
349                         AT-SPI is in the foreground? (requires xevie)
350                 @type global_: boolean
351                 """
352                 try:
353                         # see if we already have an observer for this client
354                         ob = self.deviceClients[client]
355                 except KeyError:
356                         # create a new device observer for this client
357                         ob = KeyboardDeviceEventListener(self, synchronous, preemptive, global_)
358                         # store the observer to client mapping, and the inverse
359                         self.deviceClients[ob] = client
360                         self.deviceClients[client] = ob
361                 if mask is None:
362                         # None means all modifier combinations
363                         mask = utils.allModifiers()
364                 # register for new keystrokes on the observer
365                 ob.register(self.dev, key_set, mask, kind)
366
367         def deregisterKeystrokeListener(self,
368                                         client,
369                                         key_set=[],
370                                         mask=0,
371                                         kind=(KEY_PRESSED_EVENT, KEY_RELEASED_EVENT)):
372                 """
373                 Deregisters a listener for key stroke events.
374
375                 @param client: Callable to be invoked when the event occurs
376                 @type client: callable
377                 @param key_set: Set of hardware key codes to stop monitoring. Leave empty
378                         to indicate all keys.
379                 @type key_set: list of integer
380                 @param mask: When the mask is None, the codes in the key_set will be 
381                         monitored only when no modifier is held. When the mask is an 
382                         integer, keys in the key_set will be monitored only when the modifiers in
383                         the mask are held. When the mask is an iterable over more than one 
384                         integer, keys in the key_set will be monitored when any of the modifier
385                         combinations in the set are held.
386                 @type mask: integer, iterable, None
387                 @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or 
388                         KEY_RELEASED_EVENT.
389                 @type kind: list
390                 @raise KeyError: When the client isn't already registered for events
391                 """
392                 # see if we already have an observer for this client
393                 ob = self.deviceClients[client]
394                 if mask is None:
395                         # None means all modifier combinations
396                         mask = utils.allModifiers()
397                 # register for new keystrokes on the observer
398                 ob.unregister(self.dev, key_set, mask, kind)
399
400         def handleDeviceEvent(self, event, ob):
401                 """
402                 Dispatches L{event.DeviceEvent}s to registered clients. Clients are called
403                 in the order they were registered for the given AT-SPI event. If any
404                 client returns True, callbacks cease for the event for clients of this registry 
405                 instance. Clients of other registry instances and clients in other processes may 
406                 be affected depending on the values of synchronous and preemptive used when invoking
407                 L{registerKeystrokeListener}. 
408
409                 @note: Asynchronous dispatch of device events is not supported.
410
411                 @param event: AT-SPI device event
412                 @type event: L{event.DeviceEvent}
413                 @param ob: Observer that received the event
414                 @type ob: L{KeyboardDeviceEventListener}
415
416                 @return: Should the event be consumed (True) or allowed to pass on to other
417                         AT-SPI observers (False)?
418                 @rtype: boolean
419                 """
420                 try:
421                         # try to get the client registered for this event type
422                         client = self.clients[ob]
423                 except KeyError:
424                         # client may have unregistered recently, ignore event
425                         return False
426                 # make the call to the client
427                 try:
428                         return client(event) or event.consume
429                 except Exception:
430                         # print the exception, but don't let it stop notification
431                         traceback.print_exc()
432
433         # -------------------------------------------------------------------------------
434
435         def pumpQueuedEvents (self):
436                 """
437                 No Longer needed all application events are asyncronous.
438                 """
439                 pass
440
441         def flushEvents (self):
442                 """
443                 No Longer needed all application events are asyncronous.
444                 """
445                 pass
446
447         # -------------------------------------------------------------------------------
448
449         def generateKeyboardEvent(self, keycode, keysym, kind):
450                 """
451                 Generates a keyboard event. One of the keycode or the keysym parameters
452                 should be specified and the other should be None. The kind parameter is 
453                 required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
454                 KEY_SYM, or KEY_STRING.
455
456                 @param keycode: Hardware keycode or None
457                 @type keycode: integer
458                 @param keysym: Symbolic key string or None
459                 @type keysym: string
460                 @param kind: Kind of event to synthesize
461                 @type kind: integer
462                 """
463                 if keysym is None:
464                         self.dev.generateKeyboardEvent(keycode, '', kind)
465                 else:
466                         self.dev.generateKeyboardEvent(None, keysym, kind)
467
468         def generateMouseEvent(self, x, y, name):
469                 """
470                 Generates a mouse event at the given absolute x and y coordinate. The kind
471                 of event generated is specified by the name. For example, MOUSE_B1P 
472                 (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3 
473                 double-click).
474
475                 @param x: Horizontal coordinate, usually left-hand oriented
476                 @type x: integer
477                 @param y: Vertical coordinate, usually left-hand oriented
478                 @type y: integer
479                 @param name: Name of the event to generate
480                 @type name: string
481                 """
482                 self.dev.generateMouseEvent(x, y, name)