1 '''
2 Defines classes for monitoring and wrapping AT-SPI events. Hides most of the
3 details of registering and unregistering for events behind a simple interface.
4
5 @author: Peter Parente
6 @organization: IBM Corporation
7 @copyright: Copyright (c) 2005, 2007 IBM Corporation
8 @license: The BSD License
9
10 All rights reserved. This program and the accompanying materials are made
11 available under the terms of the BSD license which accompanies
12 this distribution, and is available at
13 U{http://www.opensource.org/licenses/bsd-license.php}
14 '''
15
16
17
18 import Accessibility, Accessibility__POA
19 import pyLinAcc, Constants, Interfaces
20 import weakref, traceback
21
23 '''
24 Wraps an AT-SPI event with a more Pythonic interface. Both AT-SPI device
25 events (e.g. keyboard) and "normal" AT-SPI events (everything else) are
26 handled by this class.
27
28 @note: All unmarked attributes of this class should be considered public
29 readable and writable as the class is acting as a record object.
30 @ivar consume: Should this event be consumed and not allowed to pass on to
31 observers further down the dispatch chain?
32 @type consume: boolean
33 @ivar type: The type of the AT-SPI event
34 @type type: L{EventType}
35 @ivar detail1: First AT-SPI event parameter
36 @type detail1: integer
37 @ivar detail2: Second AT-SPI event parameter
38 @type detail2: integer
39 @ivar any_data: Extra AT-SPI data payload
40 @type any_data: object
41 @ivar source: Source of the event
42 @type source: Accessibility.Accessible
43 '''
45 '''
46 Extracts information from the provided event. If the event is a "normal"
47 event, pulls the detail1, detail2, any_data, and source values out of the
48 given object and stores it in this object. If the event is a device event,
49 key ID is stored in detail1, scan code is stored in detail2, key name,
50 key modifiers (e.g. ALT, CTRL, etc.), is text flag, and timestamp are
51 stored as a 4-tuple in any_data, and source is None (since key events are
52 global).
53
54 @param event: Event from an AT-SPI callback
55 @type event: Accessibility.Event or Accessibility.DeviceEvent
56 '''
57
58 self.consume = False
59 try:
60
61 self.type = EventType(event.type)
62 self.detail1 = event.detail1
63 self.detail2 = event.detail2
64
65
66
67 self.source = event.source
68 self.source.ref()
69
70 self.any_data = event.any_data.value()
71 try:
72 self.any_data = self.any_data.any_data.value()
73 except Exception:
74 pass
75 try:
76
77
78 self.any_data.ref()
79 except AttributeError:
80 pass
81 except AttributeError:
82
83 self.type = EventType(Constants.device_type_to_name[event.type])
84 self.detail1 = event.id
85 self.detail2 = event.hw_code
86
87 self.any_data = (event.event_string, event.modifiers, event.is_text,
88 event.timestamp)
89
90 self.source = None
91
93 '''
94 Builds a human readable representation of the event including event type,
95 parameters, and, optionally, source info. This method is used by an
96 L{AEMonitor} to buffer string representations of events.
97
98 @return: Event description
99 @rtype: string
100 '''
101 try:
102 s = '%s(%s, %s, %s)\n\tsource: %s\n\tapplication: %s' % \
103 (self.type.asString(), self.detail1, self.detail2, self.any_data,
104 self.source, self.source.getApplication())
105 except Exception:
106 if self.any_data is None:
107 s = '%s(%s, %s, None)' % \
108 (self.type.asString(), self.detail1, self.detail2)
109 else:
110 s = '%s(%s, %s, %s, %s, %s, %s)' % \
111 ((self.type.asString(), self.detail1, self.detail2)+self.any_data)
112 return s
113
115 '''
116 Wraps the AT-SPI event type string so its components can be accessed
117 individually as klass (can't use the keyword class), major, minor, and detail
118 (klass:major:minor:detail).
119
120 @note: All attributes of an instance of this class should be considered public
121 readable as it is acting a a struct.
122 @ivar klass: Most general event type identifier (object, window, mouse, etc.)
123 @type klass: string
124 @ivar major: Second level event type description
125 @type major: string
126 @ivar minor: Third level event type description
127 @type minor: string
128 @ivar detail: Lowest level event type description
129 @type detail: string
130 @ivar name: Full, unparsed event name as received from AT-SPI
131 @type name: string
132 @cvar format: Names of the event string components
133 @type format: 4-tuple of string
134 '''
135 format = ('klass', 'major', 'minor', 'detail')
137 '''
138 Parses the full AT-SPI event name into its components
139 (klass:major:minor:detail). If the provided event name is an integer instead
140 of a string, then the event is really a device event. An exception is raised
141 in this case so L{Event} knows to convert the device event integer constant
142 to a keyboard event name string.
143
144 @param name: Full AT-SPI event name
145 @type name: string
146 @raise AttributeError: When the given event name is not a valid string
147 '''
148
149 self.name = name.strip(':')
150 self.klass = None
151 self.major = None
152 self.minor = None
153 self.detail = None
154
155
156 split = self.name.split(':')
157
158 for i in xrange(len(split)):
159
160 setattr(self, self.format[i], split[i])
161
163 '''
164 @return: Full event name as human readable representation of this event type
165 @rtype: string
166 '''
167 return self.asString()
168
170 '''
171 Builds a human readable representation of the event type.
172
173 @return: Event type description
174 @rtype: string
175 '''
176 return self.name
177
179 '''
180 Factory function that builds the right kind of observer based on whether
181 events to monitor are at the device level or not.
182
183 @param manager: Manager that will own this observer
184 @type manager: L{Manager}
185 @param et: Type of event the created observer will monitor
186 @type et: L{EventType}
187 @return: Created observer object
188 @rtype: L{Observer}
189 '''
190 if et.klass == 'keyboard':
191
192 ob = DeviceObserver(manager)
193 else:
194
195 ob = EventObserver(manager)
196 return ob
197
199 '''
200 Parent class for all event observers. Dispatches all received events to the
201 L{Manager} that created this L{Observer}. Provides basic reference counting
202 functionality needed by L{Manager} to determine when an L{Observer} can be
203 released for garbage collection.
204
205 The reference counting provided by this class is independent of the reference
206 counting used by CORBA. Keeping the counts separate makes it easier for the
207 L{Manager} to detect when an L{Observer} can be freed in the
208 L{Manager._unregisterObserver} method.
209
210 @ivar manager: Reference to the L{Manager} that created this L{Observer}
211 @type manager: L{Manager}
212 @ivar ref_count: Reference count on this L{Observer}
213 @type ref_count: integer
214 '''
216 '''
217 Stores a reference to the creating L{Manager}. Intializes the reference
218 count on this object to zero.
219
220 @param manager: The L{Manager} that created this observer
221 @type manager: L{Manager}
222 '''
223 self.manager = manager
224 self.ref_count = 0
225
227 '''
228 Increments the L{pyLinAcc} reference count on this L{Observer} by one. This
229 method is called when a new client is registered in L{Manager} to receive
230 notification of an event type monitored by this L{Observer}.
231 '''
232 self.ref_count += 1
233
235 '''
236 Decrements the L{pyLinAcc} reference count on this L{Observer} by one. This
237 method is called when a client is unregistered in L{Manager} to stop
238 receiving notifications of an event type monitored by this L{Observer}.
239 '''
240 self.ref_count -= 1
241
243 '''
244 @return: Current L{pyLinAcc} reference count on this L{Observer}
245 @rtype: integer
246 '''
247 return self.ref_count
248
250 '''Required by CORBA. Does nothing.'''
251 pass
252
254 '''Required by CORBA. Does nothing.'''
255 pass
256
257 -class DeviceObserver(Observer, Accessibility__POA.DeviceEventListener):
258 '''
259 Observes keyboard press and release events. The settings for the listener
260 mode are hard coded so that key presses and releases can be consumed by any
261 client registered with the L{Manager} to observe these events.
262
263 @ivar mode: Keyboard event mode (currently hardwired as non-preemptive and
264 asynchronous)
265 @type mode: Accessibility.EventListenerMode
266 '''
268 '''
269 Creates a mode object that defines when key events will be received from
270 the system.
271
272 @param manager: The L{Manager} that created this observer
273 @type manager: L{Manager}
274 '''
275 Observer.__init__(self, manager)
276 self.mode = Accessibility.EventListenerMode()
277 self.mode.preemptive = True
278 self.mode.synchronous = True
279 self.mode._global = False
280
282 '''
283 Starts keyboard event monitoring on all keys and key combinations. The
284 event type to be monitored (key press or key release) is given by the event
285 name. See L{Manager.addClient} for valid event names.
286
287 @param registry: Registry where the device event controller is defined
288 @type registry: Accessibility.Registry
289 @param name: L{pyLinAcc.Constants} keyboard event name
290 @type name: string
291 '''
292 mask = 0
293
294 type = [Constants.name_to_device_type[name]]
295
296 dc = registry.getDeviceEventController()
297
298 while mask <= (1 << Accessibility.MODIFIER_NUMLOCK):
299 dc.registerKeystrokeListener(self._this(), [], mask, type, self.mode)
300 mask += 1
301
303 '''
304 Stops keyboard event monitoring on all keys and key combinations. The event
305 type to stop monitoring (key press or key release) is given by the event
306 name. See L{Manager.addClient} for possible event names.
307
308 @param registry: Registry where the device event controller is defined
309 @type registry: Accessibility.Registry
310 @param name: Pretty AT-SPI event name
311 @type name: string
312 '''
313 mask = 0
314 type = [Constants.name_to_device_type[name]]
315 dc = registry.getDeviceEventController()
316
317 while mask <= (1 << Accessibility.MODIFIER_NUMLOCK):
318 dc.deregisterKeystrokeListener(self._this(), [], mask, type)
319 mask += 1
320
322 '''
323 Reports that this class only implements the AT-SPI DeviceEventListener
324 interface. Required by AT-SPI.
325
326 @param repo_id: Request for an interface
327 @type repo_id: string
328 @return: The underlying CORBA object for the device event listener
329 @rtype: Accessibility.EventListener
330 '''
331 if repo_id == Constants.DEVICEEVENT_LISTENER_IDL:
332 return self._this()
333 else:
334 return None
335
337 '''
338 Notifies the L{Manager} that an event has occurred. Wraps the raw event
339 object in our L{Event} class to support automatic ref and unref calls. An
340 observer can set the L{Event} consume flag to True to indicate this event
341 should not be allowed to pass to other AT-SPI observers or the underlying
342 application.
343
344 @param event: Low-level AT-SPI event (keyboard, mouse)
345 @type event: Accessibility.DeviceEvent
346 @return: Should the event be consumed (True) or allowed to pass on to other
347 AT-SPI observers (False)?
348 @rtype: boolean
349 '''
350 wrap = Event(event)
351 try:
352 self.manager.handleEvent(wrap)
353 except Exception:
354 pass
355 return wrap.consume
356
358 '''
359 Observes all non-keyboard AT-SPI events.
360 '''
362 '''
363 Starts monitoring for the given event. See L{Manager.addClient} for
364 possible event names.
365
366 @param registry: Registry on which the listener will be registered
367 @type registry: Accessibility.Registry
368 @param name: AT-SPI event name
369 @type name: string
370 '''
371 registry.registerGlobalEventListener(self._this(), name)
372
374 '''
375 Stops monitoring for the given event. See L{Manager.addClient} for possible
376 event names.
377
378 @param registry: Registry on which the listener will be registered
379 @type registry: Accessibility.Registry
380 @param name: AT-SPI event name
381 @type name: string
382 '''
383 registry.deregisterGlobalEventListener(self._this(), name)
384
386 '''
387 Reports that this class only implements the AT-SPI DeviceEventListener
388 interface. Required by AT-SPI.
389
390 @param repo_id: Request for an interface
391 @type repo_id: string
392 @return: The underlying CORBA object for the device event listener
393 @rtype: Accessibility.EventListener
394 '''
395 if repo_id == EVENT_LISTENER_IDL:
396 return self._this()
397 else:
398 return None
399
401 '''
402 Notifies the L{Manager} that an event has occurred. Wraps the raw event
403 object in our L{Event} class to support automatic ref and unref calls.
404 Aborts on any exception indicating the event could not be wrapped.
405
406 @param event: High-level AT-SPI event (anything but keyboard, mouse)
407 @type event: Accessibility.Event
408 '''
409 try:
410 ev = Event(event)
411 except Exception:
412 return
413 try:
414 self.manager.handleEvent(ev)
415 except Exception:
416 pass
417
419 '''
420 Manages all event L{Observer}s. Allows clients to register and unregister
421 callbacks for observed events. Acts as a point of serialization for events so
422 events of the same klass (see L{EventType}) can be gauranteed to be delivered
423 to the registered clients in the order they were registered. No such guarantee
424 is made across event klasses.
425
426 @ivar registry: Reference to the Gnome Accessibility.Registry
427 @type registry: Accessibility.Registry
428 @ivar clients: Registered Clients to be notified about events
429 @type clients: dictionary {event name : client callable}
430 @ivar observers: L{Observer} objects registered to monitor events
431 @type observers: dictionary {event klass : L{Observer}}
432 '''
434 '''
435 Stores references to the Accessibility.Registery and creates the empty
436 client and observer dictionaries.
437 '''
438 self.registry = pyLinAcc.Registry
439 self.dev = self.registry.getDeviceEventController()
440 self.clients = {}
441 self.observers = {}
442
444 '''
445 Internal method that recursively associates a client with AT-SPI event
446 names. Allows a clientto incompletely specify an event name in order to
447 register for subevents without specifying their full names manually. See
448 L{Manager.addClient} for valid event names.
449
450 @param client: Client callback to receive event notifications
451 @type client: callable
452 @param name: Partial or full event name
453 @type name: string
454 '''
455 try:
456
457 events = Constants.event_tree[name]
458 except KeyError:
459
460
461
462
463
464
465 et = EventType(name)
466 clients = self.clients.setdefault(et.name, [])
467 try:
468 clients.index(client)
469 except ValueError:
470
471 clients.append(client)
472 self._registerObserver(name)
473 return
474
475
476
477 for e in events:
478 self._registerClients(client, e)
479
481 '''
482 Internal method that recursively unassociates a client with AT-SPI event
483 names. Allows a client to incompletely specify an event name in order to
484 unregister for subevents without specifying their full names manually.
485
486 @param client: Client callback to receive event notifications
487 @type client: callable
488 @param name: Partial or full event name
489 @type name: string
490 '''
491 missed = False
492 try:
493
494 events = Constants.event_tree[name]
495 except KeyError:
496 try:
497
498
499
500 et = EventType(name)
501 clients = self.clients[et.name]
502 clients.remove(client)
503 self._unregisterObserver(name)
504 except (ValueError, KeyError):
505
506 missed = True
507 return missed
508
509
510
511 for e in events:
512 missed |= self._unregisterClients(client, e)
513 return missed
514
516 '''
517 Creates a new L{Observer} to watch for events of the given type or returns
518 the existing observer if one is already registered. One L{Observer}
519 is created for each event type which has no subevents.
520
521 @param name: Name of the event to observe
522 @type name: string
523 @return: L{Observer} object that is monitoring the event
524 @rtype: L{Observer}
525 '''
526 et = EventType(name)
527 try:
528
529 ob = self.observers[et.name]
530 except KeyError:
531
532 ob = _createObserver(self, et)
533
534
535
536 ob.register(self.registry, name)
537 self.observers[et.name] = ob
538 ob.clientRef()
539 return ob
540
542 '''
543 Destroys an existing L{Observer} for the given event type only if no clients
544 are registered for the events it is monitoring.
545
546 @param name: Name of the event to observe
547 @type name: string
548 @raise KeyError: When an observer for the given event is not regist
549 '''
550 et = EventType(name)
551
552 ob = self.observers[et.name]
553 ob.clientUnref()
554 if ob.getClientRefCount() == 0:
555 ob.unregister(self.registry, name)
556 del self.observers[et.name]
557
559 '''
560 Shuts down the L{Manager} by destroying all observers.
561 '''
562 for name, ob in self.observers.items():
563 ob.unregister(self.registry, name)
564
566 '''
567 Registers a new client callback for the given event names. Supports
568 registration for all subevents if only partial event name is specified.
569 Do not include a trailing colon. The valid event names are the following:
570
571 terminal
572 terminal:line-changed
573 terminal:columncount-changed
574 terminal:linecount-changed
575 terminal:application-changed',
576 terminal:charwidth-changed
577 document:load-complete
578 document:reload
579 document:load-stopped
580 document:content-changed
581 document:attributes-changed
582 object
583 object:bounds-changed
584 object:link-selected
585 object:property-change
586 object:state-changed
587 object:children-changed
588 object:visible-data-changed
589 object:selection-changed
590 object:model-changed
591 object:active-descendant-changed
592 object:row-inserted
593 object:row-reordered
594 object:row-deleted
595 object:column-inserted
596 object:column-reordered
597 object:column-deleted
598 object:attributes-changed
599 object:text-attributes-changed
600 object:text-selection-changed
601 object:text-caret-moved
602 object:text-changed
603 object:text-changed:insert
604 object:text-changed:delete
605 object:property-change
606 object:property-change:accessible-parent
607 object:property-change:accessible-name
608 object:property-change:accessible-description
609 object:property-change:accessible-value
610 object:property-change:accessible-role
611 object:property-change:accessible-table-caption
612 object:property-change:accessible-table-column-description
613 object:property-change:accessible-table-column-header
614 object:property-change:accessible-table-row-description
615 object:property-change:accessible-table-row-header
616 object:property-change:accessible-table-summary
617 object:children-changed
618 object:children-changed:add
619 object:children-changed:remove
620 object:state-changed
621 object:state-changed:active
622 object:state-changed:armed
623 object:state-changed:busy
624 object:state-changed:checked
625 object:state-changed:collapsed
626 object:state-changed:defunct
627 object:state-changed:editable
628 object:state-changed:enabled
629 object:state-changed:expandable
630 object:state-changed:expanded
631 object:state-changed:focusable
632 object:state-changed:focused
633 object:state-changed:has-tooltip
634 object:state-changed:horizontal
635 object:state-changed:iconified
636 object:state-changed:indeterminate
637 object:state-changed:invalid
638 object:state-changed:last-defined
639 object:state-changed:manages-descendants
640 object:state-changed:modal
641 object:state-changed:multi-line
642 object:state-changed:multiselectable
643 object:state-changed:opaque
644 object:state-changed:pressed
645 object:state-changed:resizable
646 object:state-changed:selectable
647 object:state-changed:selected
648 object:state-changed:sensitive
649 object:state-changed:showing
650 object:state-changed:single-line
651 object:state-changed:stale
652 object:state-changed:transient
653 object:state-changed:vertical
654 object:state-changed:visible
655 ...
656 mouse
657 mouse:abs
658 mouse:rel
659 mouse:button
660 mouse:button
661 mouse:button:1p
662 mouse:button:1r
663 mouse:button:2p
664 mouse:button:2r
665 mouse:button:3p
666 mouse:button:3r
667 window
668 window:minimize
669 window:maximize
670 window:restore
671 window:close
672 window:create
673 window:reparent
674 window:desktop-create
675 window:desktop-destroy
676 window:activate
677 window:deactivate
678 window:raise
679 window:lower
680 window:move
681 window:resize
682 window:shade
683 window:unshade
684 window:restyle
685 focus
686 keyboard
687 keyboard:press
688 keyboard:release
689
690 For example, 'object' will register for all object events,
691 'object:property-change' will register for all property change events,
692 and 'object:property-change:accessible-parent' will register only for the
693 parent property change event.
694
695 Registered clients will not be automatically removed when the client dies.
696 To ensure the client is properly garbage collected, call
697 L{Manager.removeClient}.
698
699 @param client: Callable to be invoked when the event occurs
700 @type client: callable
701 @param names: List of full or partial event names
702 @type names: list of string
703 '''
704 for name in names:
705
706 self._registerClients(client, name)
707
709 '''
710 Unregisters an existing client callback for the given event names. Supports
711 unregistration for all subevents if only partial event name is specified.
712 Do not include a trailing colon.
713
714 This method must be called to ensure a client registered by
715 L{Manager.addClient} is properly garbage collected.
716
717 @param client: Client callback to remove
718 @type client: callable
719 @param names: List of full or partial event names
720 @type names: list of string
721 @return: Were event names specified for which the given client was not
722 registered?
723 @rtype: boolean
724 '''
725 missed = False
726 for name in names:
727
728 missed |= self._unregisterClients(client, name)
729 return missed
730
732 '''
733 Synthesizes a key press or key release to be sent to the foreground window
734 with the input focus.
735
736 @param key_code: Key code representing the physical key on the keyboard to
737 press
738 @type key_code: integer
739 @param press: True to send a key press, False to send a key release
740 @type press: boolean
741 '''
742 if press:
743 kind = Constants.KEY_PRESS
744 else:
745 kind = Constants.KEY_RELEASE
746 self.dev.generateKeyboardEvent(key_code, '', kind)
747
749 '''
750 Handles an AT-SPI event. The default implementation immediately calls
751 L{_dispatchEvent} to send the event on to the registered clients. Override
752 this method to implement some other default behavior (e.g. queueing events
753 for later processing.
754
755 As of 9/12/05, the faster this method returns, the better, as AT-SPI event
756 handling appears to be done synchronously across the Gnome desktop. If this
757 method stalls, the entire desktop will freeze.
758
759 @param event: Wrapped AT-SPI event
760 @type event: L{Event}
761 '''
762 self._dispatchEvent(event)
763
765 '''
766 Dispatches L{Event}s to registered clients. Clients are called in the order
767 they were registered for the given AT-SPI event. If any client sets the
768 L{Event} consume flag to True, callbacks cease immediately for that event.
769 For keyboard events, this also implies that no other observer registered
770 with AT-SPI across the system will receive the event nor will the
771 application with the input focus.
772
773 @param event: Wrapped AT-SPI event
774 @type event: L{Event}
775 '''
776 try:
777
778 clients = self.clients[event.type.name]
779 except KeyError:
780
781
782 try:
783 clients = self.clients[Constants.device_type_to_name[event.type.name]]
784 except KeyError:
785 return
786
787 for client in clients:
788 try:
789 client(event)
790 except Exception:
791 traceback.print_exc()
792 if event.consume:
793
794 break
795