1 '''
2 Defines a class that uses a binding to the accessible platform and supports
3 L{AEEvent <AEvent.AEEvent>}s.
4
5 @var MAX_EVENTS: Maximum number of raw or L{AEEvent <AEvent.AEEvent>}s to
6 process in a single callback from the main loop
7 @type MAX_EVENTS: integer
8 @var EVENT_INTERVAL: Number of ms between calls to pump the event queues
9 @type EVENT_INTERVAL: integer
10
11 @author: Peter Parente
12 @author: Pete Brunet
13 @organization: IBM Corporation
14 @copyright: Copyright (c) 2005, 2007 IBM Corporation
15
16 @author: Frank Zenker
17 @author: Ramona Bunk
18 @organization: IT Science Center Ruegen gGmbH, Germany
19 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
20
21 @license: I{The BSD License}
22 All rights reserved. This program and the accompanying materials are made
23 available under the terms of the BSD license which accompanies
24 this distribution, and is available at
25 U{http://www.opensource.org/licenses/bsd-license.php}
26 '''
27
28 import pyatspi
29 import Queue, time, logging
30 import AccessEngine
31 from AccessEngine import AERegistrar, AEEvent, AEMonitor
32 import AEConstants
33 from AccessEngine.AEAccInterfaces import implements
34 from AERegistrar import MONITOR
35 from Tools.i18n import _
36
37 log = logging.getLogger('Event')
38 EVENT_INTERVAL = 10
39 MAX_EVENTS = 10
40
42 '''
43 Watches for raw events in order to report them to observers and maintains a
44 queue of L{AEEvent <AEvent.AEEvent>} that are executed on a set interval.
45
46 @ivar ae_queue: FIFO queue of L{AEEvent <AEvent.AEEvent>} to be executed
47 @type ae_queue: Queue.Queue
48 @ivar monitors: collection of L{AEMonitor <AEMonitor.AEMonitor>}s to be
49 notified about events.
50 @type monitors: L{AEMonitor.MonitorCollection}
51 @ivar last_event: the last executed event
52 @type last_event: L{AEEvent <AEvent.AEEvent>}
53 @ivar acc_reg: Reference to the B{Accessibility.Registry}
54 @type acc_reg: type of B{Accessibility.Registry}
55 '''
72
87
89 '''
90 Enter the main loop to start recieving and dispatching events.
91
92 @param async: Should event dispatch be asynchronous (decoupled) from
93 event recieving from the AT-SPI registry?
94 @type async: boolean
95 @param gil: Add an idle callback which releses the Python GIL for a few
96 milliseconds to allow other threads to run? Necessary if other threads
97 will be used in this process.
98 @type gil: boolean
99 '''
100 return self.acc_reg.start(async, gil)
101
103 '''
104 Quits the main loop.
105 '''
106 return self.acc_reg.stop()
107
109 '''
110 Throws away all events in the L{ae_queue} and stops the event loop.
111 '''
112 self.acc_reg.stop()
113 self.ae_queue = Queue.Queue()
114 self.monitors.clear()
115
117 '''
118 Adds one or more AEMonitors to the list of monitors
119 to be notified about events.
120
121 @param monitors: AEMonitors to notify
122 @type monitors: tuple of L{AEMonitor <AEMonitor.AEMonitor>}s
123 '''
124 self.monitors.add(pyatspi.event.Event, monitors)
125
127 '''
128 Gets all loaded AEMonitors.
129
130 @return: Collection of all loaded L{AEMonitor <AEMonitor.AEMonitor>}s
131 @rtype: L{AEMonitor.MonitorCollection}
132 '''
133 return self.monitors
134
136 '''
137 Informs L{AEMonitor <AEMonitor.AEMonitor>}s added via L{addMonitors} of a
138 raw event.
139
140 @param event: Raw accessibility event
141 @type event: C{pyatspi.event.Event}
142 '''
143 self.monitors.show(event=event)
144
146 '''
147 Executes the provided L{AEEvent <AEvent.AEEvent>}.
148
149 Catches and logs all exceptions to prevent bad events from getting re-queued
150 for later execution.
151
152 @param event: Event to execute
153 @type event: L{AEEvent <AEvent.AEEvent>}
154 @return: Did the event execute properly or does it need to be re-queued for
155 later execution?
156 @rtype: True
157 '''
158
159 try:
160 return event.execute()
161 except LookupError:
162
163 return True
164 except Exception, e:
165
166 log.exception(str(e))
167 return True
168
170 '''
171 Flushes the event queue by destroying it and recreating it.
172 '''
173 self.ae_queue = Queue.Queue()
174
175 - def postEvents(self, *events):
176 '''
177 Add L{AEEvent <AEvent.AEEvent>}s to the queue.
178
179 Events can be added by any part of the system. Events marked with immediate
180 priority are executed now instead of being added to the queue. Immediate
181 execution is necessary for some kinds of events.
182
183 @param events: Events to queue for dispatch
184 @type events: tuple of L{AEEvent <AEvent.AEEvent>}s
185 '''
186 for e in events:
187 if e is not None:
188 if e.priority == AEConstants.EXEC_IMMEDIATE:
189
190 if self._executeEvent(e) == False:
191
192 log.debug('re-queuing immediate AEEvent')
193 self.ae_queue.put_no_wait(e)
194 else:
195 self.ae_queue.put_nowait(e)
196
198 '''
199 Pumps all C{pyatspi.event.Event}s in the C{pyatspi.Registry.queue}.
200
201 Calls the C{pyatspi.Regitry._dispatchEvent} method to forward the event to
202 all registered clients. Catches and logs all exceptions to prevent them
203 from propogating to the main loop that called this method.
204
205 This method is called on a set interval by the main event loop in
206 L{AEMain.run <AEMain._AEMain>}.
207 It is registered for callbacks in the constructor of this class.
208
209 @return: C{True} to continue receiving notifications
210 @rtype: boolean
211 '''
212 queue = self.acc_reg.queue
213 for i in xrange(MAX_EVENTS):
214 try:
215
216 event = queue.get_nowait()
217 except Queue.Empty:
218 break
219
220 self.showEvent(event)
221 try:
222 self.acc_reg._dispatchEvent(event)
223 except LookupError:
224 break
225 except Exception:
226
227 log.exception('Raw event exception')
228 return True
229
231 '''
232 Pumps all L{AEEvent <AEvent.AEEvent>}s in the L{ae_queue}.
233
234 Executes each event pumped. Re-queues events for later execution that return
235 C{False} from their execute methods.
236
237 This method is called on a set interval by the main event loop in
238 L{AEMain.run <AEMain._AEMain>}. It is registered for callbacks in the
239 constructor of this class.
240
241 @return: C{True} to continue receiving notifications
242 @rtype: boolean
243 '''
244 later = []
245 for i in xrange(MAX_EVENTS):
246 try:
247 event = self.ae_queue.get_nowait()
248 except Queue.Empty:
249 break
250 rv = self._executeEvent(event)
251 if rv == False:
252
253 later.append(event)
254 log.debug('re-queuing AEEvent')
255 self.last_event = event
256
257 map(self.postEvents, later)
258 return True
259