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