3f7088f79dbfe56397ac89dbc78124e108520806
[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         def _deregisterFake(self, type, register, client, *names):
200                 """
201                 Deregisters a client from a register of clients
202                 for 'Fake' events emitted by the cache.
203                 """
204                 try:
205                         registered = register[client]
206                 except KeyError:
207                         return True
208
209                 for name in names:
210                         remove_type = _EventType(name)
211
212                         for i in range(0, len(registered) - 1):
213                                 type_name = registered[i]
214                                 registered_type = _EventType(type_name)
215
216                                 if remove_type.is_subtype(registered_type):
217                                         del(registered[i])
218
219                 if registered == []:
220                         del(register[client])
221
222         # -------------------------------------------------------------------------------
223
224         def registerEventListener(self, client, *names):
225                 """
226                 Registers a new client callback for the given event names. Supports 
227                 registration for all subevents if only partial event name is specified.
228                 Do not include a trailing colon.
229
230                 For example, 'object' will register for all object events, 
231                 'object:property-change' will register for all property change events,
232                 and 'object:property-change:accessible-parent' will register only for the
233                 parent property change event.
234
235                 Registered clients will not be automatically removed when the client dies.
236                 To ensure the client is properly garbage collected, call 
237                 L{deregisterEventListener}.
238
239                 @param client: Callable to be invoked when the event occurs
240                 @type client: callable
241                 @param names: List of full or partial event names
242                 @type names: list of string
243                 """
244                 try:
245                         registered = self._event_listeners[client]
246                 except KeyError:
247                         registered = []
248                         self._event_listeners[client] = registered
249
250                 for name in names:
251                         new_type = _EventType(name)
252                         registered.append((new_type.name,
253                                            _event_type_to_signal_reciever(self._bus, self._app_cache, client, new_type)))
254
255                 self._registerFake(self._name_type, self._name_listeners, client, *names)
256                 self._registerFake(self._description_type, self._description_listeners, client, *names)
257                 self._registerFake(self._parent_type, self._parent_listeners, client, *names)
258                 self._registerFake(self._children_changed_type, self._children_changed_listeners, client, *names)
259
260         def deregisterEventListener(self, client, *names):
261                 """
262                 Unregisters an existing client callback for the given event names. Supports 
263                 unregistration for all subevents if only partial event name is specified.
264                 Do not include a trailing colon.
265
266                 This method must be called to ensure a client registered by
267                 L{registerEventListener} is properly garbage collected.
268
269                 @param client: Client callback to remove
270                 @type client: callable
271                 @param names: List of full or partial event names
272                 @type names: list of string
273                 @return: Were event names specified for which the given client was not
274                         registered?
275                 @rtype: boolean
276                 """
277                 try:
278                         registered = self._event_listeners[client]
279                 except KeyError:
280                         # Presumably if were trying to deregister a client with
281                         # no names then the return type is always true.
282                         return True
283
284                 missing = False
285
286                 for name in names:
287                         remove_type = _EventType(name)
288                         for i in range (0, len(registered)):
289                                 type_name, signal_match = registered[i]
290                                 registered_type = _EventType(type_name)
291
292                                 if remove_type.is_subtype(registered_type):
293                                         signal_match.remove()
294                                         del(registered[i])
295                                 else:
296                                         missing = True
297
298                 if registered == []:
299                         del(self._event_listeners[client])
300
301                 #TODO Do these account for missing also?
302                 self._deregisterFake(self._name_type, self._name_listeners, client, *names)
303                 self._deregisterFake(self._description_type, self._description_listeners, client, *names)
304                 self._deregisterFake(self._parent_type, self._parent_listeners, client, *names)
305                 self._deregisterFake(self._children_changed_type, self._children_changed_listeners, client, *names)
306
307                 return missing
308
309         # -------------------------------------------------------------------------------
310
311         def registerKeystrokeListener(self,
312                                       client,
313                                       key_set=[],
314                                       mask=0,
315                                       kind=(KEY_PRESSED_EVENT, KEY_RELEASED_EVENT),
316                                       synchronous=True,
317                                       preemptive=True,
318                                       global_=False):
319                 """
320                 Registers a listener for key stroke events.
321
322                 @param client: Callable to be invoked when the event occurs
323                 @type client: callable
324                 @param key_set: Set of hardware key codes to stop monitoring. Leave empty
325                         to indicate all keys.
326                 @type key_set: list of integer
327                 @param mask: When the mask is None, the codes in the key_set will be 
328                         monitored only when no modifier is held. When the mask is an 
329                         integer, keys in the key_set will be monitored only when the modifiers in
330                         the mask are held. When the mask is an iterable over more than one 
331                         integer, keys in the key_set will be monitored when any of the modifier
332                         combinations in the set are held.
333                 @type mask: integer, iterable, None
334                 @param kind: Kind of events to watch, KEY_PRESSED_EVENT or 
335                         KEY_RELEASED_EVENT.
336                 @type kind: list
337                 @param synchronous: Should the callback notification be synchronous, giving
338                         the client the chance to consume the event?
339                 @type synchronous: boolean
340                 @param preemptive: Should the callback be allowed to preempt / consume the
341                         event?
342                 @type preemptive: boolean
343                 @param global_: Should callback occur even if an application not supporting
344                         AT-SPI is in the foreground? (requires xevie)
345                 @type global_: boolean
346                 """
347                 try:
348                         # see if we already have an observer for this client
349                         ob = self.deviceClients[client]
350                 except KeyError:
351                         # create a new device observer for this client
352                         ob = KeyboardDeviceEventListener(self, synchronous, preemptive, global_)
353                         # store the observer to client mapping, and the inverse
354                         self.deviceClients[ob] = client
355                         self.deviceClients[client] = ob
356                 if mask is None:
357                         # None means all modifier combinations
358                         mask = utils.allModifiers()
359                 # register for new keystrokes on the observer
360                 ob.register(self.dev, key_set, mask, kind)
361
362         def deregisterKeystrokeListener(self,
363                                         client,
364                                         key_set=[],
365                                         mask=0,
366                                         kind=(KEY_PRESSED_EVENT, KEY_RELEASED_EVENT)):
367                 """
368                 Deregisters a listener for key stroke events.
369
370                 @param client: Callable to be invoked when the event occurs
371                 @type client: callable
372                 @param key_set: Set of hardware key codes to stop monitoring. Leave empty
373                         to indicate all keys.
374                 @type key_set: list of integer
375                 @param mask: When the mask is None, the codes in the key_set will be 
376                         monitored only when no modifier is held. When the mask is an 
377                         integer, keys in the key_set will be monitored only when the modifiers in
378                         the mask are held. When the mask is an iterable over more than one 
379                         integer, keys in the key_set will be monitored when any of the modifier
380                         combinations in the set are held.
381                 @type mask: integer, iterable, None
382                 @param kind: Kind of events to stop watching, KEY_PRESSED_EVENT or 
383                         KEY_RELEASED_EVENT.
384                 @type kind: list
385                 @raise KeyError: When the client isn't already registered for events
386                 """
387                 # see if we already have an observer for this client
388                 ob = self.deviceClients[client]
389                 if mask is None:
390                         # None means all modifier combinations
391                         mask = utils.allModifiers()
392                 # register for new keystrokes on the observer
393                 ob.unregister(self.dev, key_set, mask, kind)
394
395         def handleDeviceEvent(self, event, ob):
396                 """
397                 Dispatches L{event.DeviceEvent}s to registered clients. Clients are called
398                 in the order they were registered for the given AT-SPI event. If any
399                 client returns True, callbacks cease for the event for clients of this registry 
400                 instance. Clients of other registry instances and clients in other processes may 
401                 be affected depending on the values of synchronous and preemptive used when invoking
402                 L{registerKeystrokeListener}. 
403
404                 @note: Asynchronous dispatch of device events is not supported.
405
406                 @param event: AT-SPI device event
407                 @type event: L{event.DeviceEvent}
408                 @param ob: Observer that received the event
409                 @type ob: L{KeyboardDeviceEventListener}
410
411                 @return: Should the event be consumed (True) or allowed to pass on to other
412                         AT-SPI observers (False)?
413                 @rtype: boolean
414                 """
415                 try:
416                         # try to get the client registered for this event type
417                         client = self.clients[ob]
418                 except KeyError:
419                         # client may have unregistered recently, ignore event
420                         return False
421                 # make the call to the client
422                 try:
423                         return client(event) or event.consume
424                 except Exception:
425                         # print the exception, but don't let it stop notification
426                         traceback.print_exc()
427
428         # -------------------------------------------------------------------------------
429
430         def pumpQueuedEvents (self):
431                 """
432                 No Longer needed all application events are asyncronous.
433                 """
434                 pass
435
436         def flushEvents (self):
437                 """
438                 No Longer needed all application events are asyncronous.
439                 """
440                 pass
441
442         # -------------------------------------------------------------------------------
443
444         def generateKeyboardEvent(self, keycode, keysym, kind):
445                 """
446                 Generates a keyboard event. One of the keycode or the keysym parameters
447                 should be specified and the other should be None. The kind parameter is 
448                 required and should be one of the KEY_PRESS, KEY_RELEASE, KEY_PRESSRELEASE,
449                 KEY_SYM, or KEY_STRING.
450
451                 @param keycode: Hardware keycode or None
452                 @type keycode: integer
453                 @param keysym: Symbolic key string or None
454                 @type keysym: string
455                 @param kind: Kind of event to synthesize
456                 @type kind: integer
457                 """
458                 if keysym is None:
459                         self.dev.generateKeyboardEvent(keycode, '', kind)
460                 else:
461                         self.dev.generateKeyboardEvent(None, keysym, kind)
462
463         def generateMouseEvent(self, x, y, name):
464                 """
465                 Generates a mouse event at the given absolute x and y coordinate. The kind
466                 of event generated is specified by the name. For example, MOUSE_B1P 
467                 (button 1 press), MOUSE_REL (relative motion), MOUSE_B3D (butten 3 
468                 double-click).
469
470                 @param x: Horizontal coordinate, usually left-hand oriented
471                 @type x: integer
472                 @param y: Vertical coordinate, usually left-hand oriented
473                 @type y: integer
474                 @param name: Name of the event to generate
475                 @type name: string
476                 """
477                 self.dev.generateMouseEvent(x, y, name)