1 '''
2 Defines a class responsible for managing all input and output devices and
3 monitors.
4
5 @author: Larry Weiss
6 @author: Peter Parente
7 @author: Brett Clippingdale
8 @author: Scott Haeger
9 @organization: IBM Corporation
10 @copyright: Copyright (c) 2005, 2007 IBM Corporation
11
12 @author: Frank Zenker
13 @author: Martina Weicht
14 @author: Ramona Bunk
15 @organization: IT Science Center Ruegen gGmbH, Germany
16 @copyright: Copyright (c) 2007, 2008 ITSC Ruegen
17
18 @license: I{The BSD License}
19 All rights reserved. This program and the accompanying materials are made
20 available under the terms of the BSD license which accompanies
21 this distribution, and is available at
22 U{http://www.opensource.org/licenses/bsd-license.php}
23 '''
24 import logging, sys
25 import AccessEngine
26 from AccessEngine import AERegistrar, AEEvent, AEOutput, AEInput, AEMonitor
27 from AEDevice.AEDeviceEvent import InputEvent, OutputEvent
28 import AEConstants
29 import SUEConstants
30 from AccessEngine.AEAccInterfaces import implements
31 from AERegistrar import DEVICE, MONITOR
32 from Tools.i18n import _
33
34 log = logging.getLogger('Device')
35
37 '''
38 Creates and manages the devices and monitors use by SUE.
39
40 Keeps a list of each and also defines a "default" for devices. Provides a
41 common interface to all output and input devices and mirrors I/O to the
42 monitors. Provides a mapping from output styles to semantic concepts in the
43 user interface to allow customization of how information is presented.
44
45 @note: RB: instance variable C{marks} is currently not used
46
47 @ivar out_devs: All output devices registered and initialized
48 @type out_devs: list
49 @ivar in_devs: All input devices registered and initialized
50 @type in_devs: list
51 @ivar in_mons: Collection of monitors to notify about input
52 @type in_mons: L{MonitorCollection <AEMonitor.MonitorCollection.MonitorCollection>}
53 @ivar out_mons: Collection of monitors to notify about output
54 @type out_mons: L{MonitorCollection <AEMonitor.MonitorCollection.MonitorCollection>}
55 @ivar marks: Maps index markers on devices
56 @type marks: dictionary
57 @ivar temp_devs: List of L{AEOutput <AEOutput.AEOutput>} and
58 L{AEInput <AEInput.AEInput>} devices last removed in
59 L{unloadDevices} and held until the next call of L{loadDevices}.
60
61 The purpose of this list is to ensure strong references to all devices exist,
62 and thus weak references are not destroyed, until the the L{AEDeviceManager}
63 is repopulated with devices.
64 @type temp_devs: list
65 '''
66
77
91
93 '''
94 Iterates over all devices in the active profile and loads ones that provide
95 functionality different from those already loaded.
96 '''
97 devs = AERegistrar.loadAssociated(DEVICE,
98 AccessEngine.AESettingsManager.getProfileName())
99
100 for d in devs:
101
102 try:
103 self.registerDevice(d)
104 except (AEOutput.AEOutputError, AEInput.AEInputError):
105 log.debug('could not initialize device: %s', d.getName())
106 except NotImplementedError, e:
107 log.debug('device %s does not provide a new interface: %s' %
108 (d.getName(), str(e)))
109
110
111 if not len(self.out_devs):
112 log.warn(_('no output available'))
113 if not len(self.in_devs):
114 log.warn(_('no input available'))
115 self.temp_devs = []
116
118 '''
119 Unloads all devices from L{out_devs} and L{in_devs}, but keeps strong
120 references to all objects in L{temp_devs} until L{loadDevices} is called
121 again.
122
123 Keeping strong references ensures weak references in L{AEScript}s
124 continue to exist until the L{AEDeviceManager} has new devices available
125 for I/O.
126 '''
127 for d in self.out_devs[:]:
128 self.temp_devs.append(d)
129 self.unregisterDevice(d)
130
131
132
134 '''
135 Shuts down this manager and all its registered
136 L{AEOutput <AEOutput.AEOutput>} and L{AEInput <AEInput.AEInput>}
137 devices and L{AEMonitor <AEMonitor.Base>}s.
138
139 Saves all style information for currently loaded devices.
140 '''
141 for d in self.out_devs[:]:
142 self.unregisterDevice(d)
143 for d in self.in_devs[:]:
144 self.unregisterDevice(d)
145 self.in_mons.clear()
146 self.out_mons.clear()
147
148 - def send(self, dev, name, value, sem, layer):
149 '''
150 Sends arbitrary data to a device. The device must recognize the name in
151 order to decide what to do with the value data.
152
153 This is a generic method which receives all content and commands to be
154 rendered and executed on a device. Standard name identifiers should be
155 used whenever possible. For instance, L{AEConstants.Output.CMD_STOP} and
156 L{AEConstants.Output.CMD_TALK}. However, device specific names and values
157 are certainly possible.
158
159 @param dev: Device that should receive the command
160 @type dev: L{AEOutput <AEOutput.AEOutput>}
161 @param name: Descriptor of the data value sent
162 @type name: object
163 @param value: Content value
164 @type value: object
165 @param sem: The semantic stream on which to send output; defaults to C{None}
166 for the default semantic.
167 @type sem: integer
168 @param layer: Layer on which the event occurred
169 @type layer: integer
170
171 @return: Return value specific to the given command
172 @rtype: object
173 '''
174
175 self.out_mons.show(OutputEvent(name), value=value, dev=dev, sem=sem,
176 layer=layer)
177
178 if dev is None: return
179 if sem is None or layer is None:
180 try:
181 return dev.send(name, value, None)
182 except NotImplementedError:
183 return None
184 else:
185 style = dev.getStyle((sem, layer))
186
187 if style.Mute: return
188
189 try:
190 rv = dev.send(name, value, style)
191 except NotImplementedError:
192
193 rv = None
194
195 style.makeClean()
196 return rv
197
199 '''
200 Sends the stop command to the referenced output device.
201
202 @param dev: Device that should receive the command
203 @type dev: L{AEOutput <AEOutput.AEOutput>}
204 @param sem: Semantic description of the information to stop or C{None} to
205 indicate stopping all output
206 @type sem: integer
207 @param layer: Layer on which the event occurred
208 @type layer: integer
209 '''
210
211 self.out_mons.show(OutputEvent(AEConstants.CMD_STOP), dev=dev, sem=sem,
212 layer=layer)
213 if dev is None: return
214 if sem is None and layer is None:
215
216 dev.send(AEConstants.CMD_STOP, None, None)
217 elif sem is None:
218
219 all = set([dev.getStyle((sem, layer)) for sem in
220 AEConstants.SEMANTIC_STYLES.keys()])
221 for style in all:
222 dev.send(AEConstants.CMD_STOP, None, style)
223 else:
224 style = dev.getStyle((sem, layer))
225 dev.send(AEConstants.CMD_STOP, None, style)
226
228 '''
229 Tells the specified output device to send buffered data.
230
231 @param dev: Device that should receive the command
232 @type dev: L{AEOutput <AEOutput.AEOutput>}
233 @param sem: Semantic information to start outputting or C{None} to indicate
234 that all buffered information should be output
235 @type sem: integer
236 @param layer: Layer on which the event occurred
237 @type layer: integer
238 '''
239
240 self.out_mons.show(OutputEvent(AEConstants.CMD_TALK), dev=dev, sem=sem,
241 layer=layer)
242 if dev is None: return
243 if sem is None:
244 style = None
245 else:
246 style = dev.getStyle((sem, layer))
247
248 dev.send(AEConstants.CMD_TALK, None, style)
249
251 '''
252 Sends the filename to the specified output device.
253
254 @param dev: Device that should receive the command
255 @type dev: L{AEOutput <AEOutput.AEOutput>}
256 @param filename: The filename to send to the referenced device
257 @type filename: string
258 @param sem: Semantic description of the text to send or C{None} to indicate
259 the information is void of any known semantic
260 @type sem: integer
261 @param layer: Layer on which the event occurred
262 @type layer: integer
263 '''
264
265
266 self.out_mons.show(OutputEvent(AEOutput.NAME_FILENAME), value=filename,
267 dev=dev, sem=sem, layer=layer)
268 if dev is None: return
269
270 style = dev.getStyle((sem, layer))
271
272 if style.Mute: return
273 try:
274 dev.send(AEConstants.CMD_FILENAME, filename, style)
275 except NotImplementedError:
276 pass
277
278 style.makeClean()
279
280 - def sendString(self, dev, text, sem, layer, por=None):
281 '''
282 Sends the string to the specified output device.
283
284 @param dev: Device that should receive the command
285 @type dev: L{AEOutput <AEOutput.AEOutput>}
286 @param text: The text to send to the referenced device
287 @type text: string
288 @param sem: Semantic description of the text to send or C{None} to indicate
289 the information is void of any known semantic
290 @type sem: integer
291 @param layer: Layer on which the event occurred
292 @type layer: integer
293 @param por: Point of regard to the start of the string if one exists or
294 C{None} if the string did not originate from an L{AEPor <AEPor.AEPor>}
295 @type por: L{AEPor <AEPor.AEPor>}
296 '''
297
298 self.out_mons.show(OutputEvent(AEConstants.CMD_STRING), value=text,
299 dev=dev, sem=sem, layer=layer)
300 if dev is None: return
301
302 style = dev.getStyle((sem, layer))
303
304 if style.Mute: return
305
306 parsed = dev.parseString(text, style, por, sem)
307
308 for word, por, new_style in parsed:
309 dev.send(AEConstants.CMD_STRING, word, new_style)
310
311 new_style.makeClean()
312
313 style.makeClean()
314
316 '''
317 Sends the referenced index marker to the referenced device.
318
319 @param dev: Device that should receive the command
320 @type dev: L{AEOutput <AEOutput.AEOutput>}
321 @param sem: Semantic description of the index text to send or C{None} to
322 indicate the information is void of any known semantic
323 @type sem: integer
324 @param layer: Layer on which the event occurred
325 @type layer: integer
326 '''
327
328 self.out_mons.show(OutputEvent(AEConstants.CMD_INDEX), dev=dev, sem=sem,
329 layer=layer)
330 if dev is None: return
331
332 style = dev.getStyle((sem, layer))
333
334 if style.Mute: return
335 try:
336
337 mark = dev.send(AEConstants.CMD_INDEX, None, style)
338 except NotImplementedError:
339
340 return
341
342
343
367
369 '''
370 Outputs the SUE goodbye message on all output devices.
371
372 Instead of just sending the message to the default output device,
373 it is now sent to all output devices.
374
375 One question remains: Is there a case where you would not want a goodbye
376 message sent to your output device?
377 '''
378 try:
379 for dev in self.out_devs:
380 st = dev.getStyle((AEConstants.SEM_ITEM, AEConstants.LAYER_FOCUS))
381 dev.send(AEConstants.CMD_STRING_SYNC, _('sue exiting'), st)
382 except IndexError:
383
384 return
385
386
388 '''
389 Checks if the given device is a duplicate in that it provides no new
390 interfaces beyond those provided by the devices already registered.
391
392 @param dev: A device class
393 @type dev: L{AEInput <AEInput.AEInput>} or L{AEOutput <AEOutput.AEOutput>} class
394 @param output: Is the device an output device (C{True}) or input (C{False})?
395 @type output: boolean
396 '''
397 if output:
398 reg_devs = self.out_devs
399 else:
400 reg_devs = self.in_devs
401
402 dev_int = dev.getCapabilities()
403
404
405 for reg_dev in reg_devs:
406 reg_int = reg_dev.getCapabilities()
407 if set(reg_int).issuperset(dev_int):
408 return True
409 return False
410
443
463
465 '''
466 Unregisters the given output device.
467
468 Provides the device with a reference to the L{AESettingsManager} so it can
469 save state. Removes this manager from the list of index listeners stored in
470 the device.
471
472 @param dev: Device to unregister
473 @type dev: L{AEOutput <AEOutput.AEOutput>}
474 '''
475 try:
476
477 dev.saveStyles()
478 except AttributeError:
479 pass
480 try:
481
482 self.out_devs.remove(dev)
483 log.debug('removed output device %s', dev.getName())
484 except ValueError:
485 pass
486 try:
487
488 dev.removeIndexListener(self._onIndex)
489 except AttributeError:
490 pass
491
493 '''
494 Registers the given device as an output device if the device implements the
495 L{AEOutput <AEOutput.AEOutput>} base class and provides some subinterface
496 not already provided by another registered output device.
497
498 For instance, if a device reports having "audio" capability, no other device
499 providing just this capability will be loaded.
500
501 @param dev: Device to attempt to register
502 @type dev: L{AEOutput <AEOutput.AEOutput>}
503 @return: C{True} if the device was registered,
504 C{False} if it is not an L{AEOutput <AEOutput.AEOutput>} device, and
505 C{None} if it provides no new interfaces
506 @rtype: boolean or None
507 '''
508 if not implements(dev, AEOutput.AEOutput):
509
510 return False
511 dev = dev.getProxy()
512
513
514
515
516
517
518 if dev not in self.in_devs:
519
520 dev.init()
521
522 try:
523 dev.addIndexListener(self._onIndex)
524 except (AttributeError, NotImplementedError):
525
526 pass
527
528 self.out_devs.append(dev)
529 log.debug('added output device %s', str(dev))
530
531
532 try:
533
534 dev.loadStyles()
535 except KeyError:
536
537 self._initOutputStyles(dev)
538
539
540 dev.postInit()
541 return True
542
544 '''
545 Initializes styles for an L{AEOutput <AEOutput.AEOutput>} device.
546
547 Calls L{AEOutput.AEOutput.createDistinctStyles
548 <AEDevice.AEOutput.Base.AEOutput.createDistinctStyles>} on the device to get
549 an initial batch of styles to use to distinguish some types of information.
550 If that method is not implemented, the exception is ignored.
551
552 Future requests to use styles per semantic tag will resort to making
553 flyweights for the default style on the device.
554
555 @param dev: Device reference
556 @type dev: L{AEOutput <AEOutput.AEOutput>}
557 '''
558
559 num_layers = len(AEConstants.LAYERS_ALL)
560 num_groups = len(AEConstants.STYLE_GROUP_ALL)
561 try:
562 styles = dev.createDistinctStyles(num_groups, num_layers)
563 except NotImplementedError:
564 return
565
566 for layer, i in enumerate(AEConstants.LAYERS_ALL):
567
568 s = styles[i*num_groups]
569 dev.setStyle((None, layer), s)
570 for sem, grp in AEConstants.SEMANTIC_STYLES.items():
571
572 s = styles[i*num_groups + (grp % num_groups)]
573 dev.setStyle((sem, layer), s)
574
576 '''
577 Registers the referenced device based on its one or more interfaces.
578
579 When the interface is determined, the C{init()} method is called on the
580 device.
581
582 @param dev: The device to register
583 @type dev: L{AEOutput <AEOutput.AEOutput>} or L{AEInput <AEInput.AEInput>}
584 @raise NotImplementedError: When the device implements none of the required
585 interfaces
586 @raise AEOutputError: When output device initialization fails
587 @raise AEInputError: When input device initialization fails
588 '''
589 is_out = self._registerOutputDevice(dev)
590 is_in = self._registerInputDevice(dev)
591
592 if is_out == False and is_in == False:
593
594 raise NotImplementedError
595
597 '''
598 Unregisters a device from both the input and output lists based on its
599 capabilities.
600
601 @param dev: The device to unregister
602 @type dev: L{AEOutput <AEOutput.AEOutput>} or L{AEInput <AEInput.AEInput>}
603 '''
604 try:
605 dev.close()
606 except Exception:
607 pass
608 self._unregisterOutputDevice(dev)
609 self._unregisterInputDevice(dev)
610
612 '''
613 Adds one or more L{AEMonitor <AEMonitor.AEMonitor>}s to the list of monitors
614 to be notified about IO events.
615
616 @param monitors: L{AEMonitor <AEMonitor.AEMonitor>}s to notify
617 @type monitors: tuple of L{AEMonitor <AEMonitor.AEMonitor>}s
618 '''
619 self.in_mons.add(InputEvent, monitors)
620 self.out_mons.add(OutputEvent, monitors)
621
623 '''
624 Gets all loaded input and output L{AEMonitor <AEMonitor.AEMonitor>}s.
625
626 @return: Collections of all loaded input and output
627 L{AEMonitor <AEMonitor.AEMonitor>}s in that order
628 @rtype: 2-tuple L{AEMonitor.MonitorCollection}
629 '''
630 return self.in_mons, self.out_mons
631
633 '''
634 Gets the first L{AEOutput <AEOutput.AEOutput>} device to successfully load.
635
636 @return: First loaded output device or C{None} if no output is available
637 @rtype: L{AEOutput <AEOutput.AEOutput>}
638 '''
639 try:
640 return self.out_devs[0]
641 except IndexError:
642 return None
643
645 '''
646 Gets the L{AEOutput <AEOutput.AEOutput>} device registered under the given
647 name.
648
649 @param name: Name of the output device
650 @type name: string
651 @return: Output device or C{None} if not registered
652 @rtype: L{AEOutput <AEOutput.AEOutput>}
653 '''
654 for dev in self.out_devs:
655 if dev.getClassName() == name:
656 return dev
657 return None
658
660 '''
661 Gets all L{AEOutput <AEOutput.AEOutput>} devices registered with the given
662 capabilities.
663
664 @note: MW: We decided not to limit output devices to unique capabilities anymore.
665 Instead roles are used to distingish the devices' funtions.
666
667 @param caps: Desired capabilities of the output device.
668 @type caps: list of string
669 @return: Output devices or C{None} if caps could not be satisfied
670 @rtype: L{AEOutput <AEOutput.AEOutput>}
671 '''
672 cap_devs = []
673 for dev in self.out_devs:
674 if set(dev.getCapabilities()).issuperset(caps):
675 cap_devs.append(dev)
676 return cap_devs
677
691
706
707 - def _onGesture(self, gesture, timestamp, **kwargs):
708 '''
709 Creates an L{AEEvent <AEEvent.AEEvent>} indicating the given gesture was
710 found on a registered input device.
711
712 When executed, the L{AEEvent <AEEvent.AEEvent>} will notify the
713 L{AETierManager} about the gesture and allow it to activate the appropriate
714 script task, which is registered to respond to the gesture in the active
715 L{AETier}.
716
717 @param gesture: Gesture seen on an input device
718 @type gesture: L{AEInput.Gesture}
719 @param timestamp: Time at which the event occurred
720 @type timestamp: float
721 @param kwargs: Additional keyword arguments to pass to the script task
722 @type kwargs: dictionary
723 '''
724 event = AEEvent.InputGesture(gesture, timestamp, **kwargs)
725 AccessEngine.AEEventManager.postEvents(event)
726
727 self.in_mons.show(InputEvent(AEConstants.CMD_GESTURE), value=gesture,
728 dev=gesture.getDevice())
729
731 '''
732 @todo: RB: Taken over from LSR. Used in L{_unregisterOutputDevice} and
733 L{_registerOutputDevice}. This function needs to be implemented or
734 removed. (Note: This function isn't implemented in any subclass either,
735 because there are no subclasses of _AEDeviceManager)
736 '''
737 pass
738
739
740
742 '''
743 Gets a list of all loaded devices.
744
745 @return: References to all loaded devices
746 @rtype: list of L{AEUserInterface <AccessEngine.AEUserInterface.AEUserInterface>}
747 '''
748 return list(set(self.in_devs).union(self.out_devs))
749
751 '''
752 Gets the style for semantic/layer of a given device.
753
754 @param dev: The device from which to get the style.
755 @type dev: L{AEOutput <AEOutput.AEOutput>}
756 @param sem: The device semantic from which to get the style, or C{None} to
757 get the device's default style.
758 @type sem: integer
759 @param layer: Layer on which the event occurred
760 @type layer: integer
761 @return: the L{AEOutput.Style} for the given semantic of a device
762 @rtype: L{AEOutput.Style}
763 @raise KeyError: When the semantic or layer is invalid
764 '''
765 if dev is None: return None
766 if sem is None or layer is None:
767 return dev.getDefaultStyle()
768 return dev.getStyle((sem, layer))
769