1 #Copyright (C) 2008 Codethink Ltd
2 #copyright: Copyright (c) 2005, 2007 IBM Corporation
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.
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.
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}
20 #authors: Peter Parente, Mark Doffman
24 import gobject as _gobject
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
32 from dbus.mainloop.glib import DBusGMainLoop as _DBusGMainLoop
33 _DBusGMainLoop(set_as_default=True)
35 #------------------------------------------------------------------------------
37 class PressedEventType(_Enum):
39 0:'KEY_PRESSED_EVENT',
40 1:'KEY_RELEASED_EVENT',
41 2:'BUTTON_PRESSED_EVENT',
42 3:'BUTTON_RELEASED_EVENT',
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 #------------------------------------------------------------------------------
51 class KeyEventType(_Enum):
56 KEY_PRESSED = KeyEventType(0)
57 KEY_RELEASED = KeyEventType(1)
59 #------------------------------------------------------------------------------
61 class KeySynthType(_Enum):
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)
76 #------------------------------------------------------------------------------
78 class ModifierType(_Enum):
81 1:'MODIFIER_SHIFTLOCK',
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)
99 #------------------------------------------------------------------------------
101 class _Registry(object):
103 Wraps the Accessibility.Registry to provide more Pythonic registration for
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
111 @ivar async: Should event dispatch to local listeners be decoupled from event
112 receiving from the registry?
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
126 _REGISTRY_NAME = 'org.freedesktop.atspi.Registry'
130 Stores a reference to the AT-SPI registry. Gets and stores a reference
131 to the DeviceEventController.
133 @param reg: Reference to the AT-SPI registry daemon
134 @type reg: Accessibility.Registry
136 self._bus = _dbus.SessionBus()
139 if "ATSPI_TEST_APP_NAME" in _os.environ.keys():
140 app_name = _os.environ["ATSPI_TEST_APP_NAME"]
142 self._app_name = app_name
143 self._appcache = TestApplicationCache(self, self._bus, app_name)
145 self._event_listeners = {}
147 # All of this special casing is for the 'faked'
148 # events caused by cache updates.
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 = {}
161 @return: This instance of the registry
166 def start(self, async=False, gil=True):
168 Enter the main loop to start receiving and dispatching events.
170 @param async: Should event dispatch be asynchronous (decoupled) from
171 event receiving from the AT-SPI registry?
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.
179 self._loop = _gobject.MainLoop()
182 except KeyboardInterrupt:
185 def stop(self, *args):
186 """Quits the main loop."""
190 def getDesktopCount(self):
192 Gets the number of available desktops.
194 @return: Number of desktops
199 def getDesktop(self, i):
201 Gets a reference to the i-th desktop.
203 @param i: Which desktop to get
205 @return: Desktop reference
206 @rtype: Accessibility.Desktop
208 return _Desktop(self._appcache)
210 def _callClients(self, register, event):
211 for client in register.keys():
214 def _notifyNameChange(self, event):
215 self._callClients(self._name_listeners, event)
217 def _notifyDescriptionChange(self, event):
218 self._callClients(self._description_listeners, event)
220 def _notifyParentChange(self, event):
221 self._callClients(self._parent_listeners, event)
223 def _notifyChildenChange(self, event):
224 self._callClients(self._children_changed_listeners, event)
226 def _registerFake(self, type, register, client, *names):
228 Registers a client from a register of clients
229 for 'Fake' events emitted by the cache.
232 registered = register[client]
235 register[client] = registered
238 new_type = _EventType(name)
239 if new_type.is_subtype(type):
240 registered.append(new_type.name)
242 def _deregisterFake(self, type, register, client, *names):
244 Deregisters a client from a register of clients
245 for 'Fake' events emitted by the cache.
248 registered = register[client]
253 remove_type = _EventType(name)
255 for i in range(0, len(registered) - 1):
256 type_name = registered[i]
257 registered_type = _EventType(type_name)
259 if remove_type.is_subtype(registered_type):
263 del(register[client])
265 def registerEventListener(self, client, *names):
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.
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.
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}.
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
286 registered = self._event_listeners[client]
289 self._event_listeners[client] = registered
292 new_type = _EventType(name)
293 registered.append((new_type.name,
294 _event_type_to_signal_reciever(self._bus, self._appcache, client, new_type)))
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)
301 def deregisterEventListener(self, client, *names):
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.
307 This method must be called to ensure a client registered by
308 L{registerEventListener} is properly garbage collected.
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
319 registered = self._event_listeners[client]
321 # Presumably if were trying to deregister a client with
322 # no names then the return type is always true.
328 remove_type = _EventType(name)
330 for i in range(0, len(registered) - 1):
331 (type_name, signal_match) = registered[i]
332 registered_type = _EventType(type_name)
334 if remove_type.is_subtype(registered_type):
335 signal_match.remove()
341 del(self._event_listeners[client])
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)
351 def registerKeystrokeListener(self,
355 kind=(KEY_PRESSED_EVENT, KEY_RELEASED_EVENT),
360 Registers a listener for key stroke events.
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
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
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
389 def deregisterKeystrokeListener(self,
393 kind=(KEY_PRESSED_EVENT, KEY_RELEASED_EVENT)):
395 Deregisters a listener for key stroke events.
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
412 @raise KeyError: When the client isn't already registered for events
416 def generateKeyboardEvent(self, keycode, keysym, kind):
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.
423 @param keycode: Hardware keycode or None
424 @type keycode: integer
425 @param keysym: Symbolic key string or None
427 @param kind: Kind of event to synthesize
432 def generateMouseEvent(self, x, y, name):
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
439 @param x: Horizontal coordinate, usually left-hand oriented
441 @param y: Vertical coordinate, usually left-hand oriented
443 @param name: Name of the event to generate
448 def handleDeviceEvent(self, event, ob):
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}.
457 @note: Asynchronous dispatch of device events is not supported.
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}
464 @return: Should the event be consumed (True) or allowed to pass on to other
465 AT-SPI observers (False)?
470 def handleEvent(self, event):
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.
475 @param event: AT-SPI event
476 @type event: L{event.Event}
480 def flushEvents(self):
482 Flushes the event queue by destroying it and recreating it.
486 def pumpQueuedEvents(self, num=-1):
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.
492 @param num: Number of events to pump. If number is negative it pumps
493 the entire queue. Default is -1.
495 @return: True if queue is not empty after events were pumped.