1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """\
24 X2goPulseAudio class - a Pulseaudio daemon guardian thread.
25
26 """
27
28 __NAME__ = 'x2gopulseaudio-pylib'
29
30 from defaults import X2GOCLIENT_OS as _X2GOCLIENT_OS
31 if _X2GOCLIENT_OS == 'Windows':
32 import wmi
33 import win32process
34 import win32con
35 import win32event
36
37
38 import os
39 import subprocess
40 import threading
41 import gevent
42 import copy
43
44
45 import log
46
48 """
49 This class controls the Pulse Audio daemon.
50 """
51
53 """\
54 Initialize a Pulse Audio daemon instance.
55
56 @param path: full path to pulseaudio.exe
57 @type path: C{str}
58 @param client_instance: the calling L{X2goClient} instance
59 @type client_instance: L{X2goClient} instance
60 @param logger: you can pass an L{X2goLogger} object to the L{X2goClientXConfig} constructor
61 @type logger: C{instance}
62 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be
63 constructed with the given loglevel
64 @type loglevel: C{int}
65
66 """
67 if _X2GOCLIENT_OS not in ("Windows"):
68 import exceptions
69 class OSNotSupportedException(exceptions.StandardError): pass
70 raise OSNotSupportedException('classes of x2go.pulseaudio module are for Windows only')
71
72 if logger is None:
73 self.logger = log.X2goLogger(loglevel=loglevel)
74 else:
75 self.logger = copy.deepcopy(logger)
76 self.logger.tag = __NAME__
77
78 self.path = path
79 self.client_instance = client_instance
80 self._keepalive = None
81
82 threading.Thread.__init__(self)
83 self.daemon = True
84 self.start()
85
87 """\
88 This method is called once the C{X2goPulseAudio.start()} method has been called. To tear
89 down the Pulseaudio daemon call the L{X2goPulseAudio.stop_thread()} method.
90
91 """
92 self._keepalive = True
93 cmd = 'pulseaudio.exe'
94 cmd_options = [
95 '-n',
96 '-L module-native-protocol-tcp port=4713',
97 '-L module-esound-protocol-tcp port=16001',
98 '-L module-waveout',
99 ]
100 cmd_options = " %s" % " ".join(cmd_options)
101 self.logger('starting PulseAudio server with command line: %s%s' % (cmd, cmd_options), loglevel=log.loglevel_DEBUG)
102
103 si = win32process.STARTUPINFO()
104 p_info = win32process.CreateProcess(None,
105 '%s\\%s %s' % (self.path, cmd, cmd_options),
106 None,
107 None,
108 0,
109 win32con.CREATE_NO_WINDOW|win32process.NORMAL_PRIORITY_CLASS,
110 None,
111 None,
112 si,
113 )
114 (hProcess, hThread, processId, threadId) = p_info
115
116 gevent.sleep(5)
117 rc = win32event.WaitForMultipleObjects([hProcess],
118 1,
119 1,
120 )
121 _is_alive = ( rc != win32event.WAIT_OBJECT_0 )
122 if self.client_instance and not _is_alive:
123 if os.environ.has_key('CLIENTNAME'):
124 self.client_instance.HOOK_pulseaudio_not_supported_in_RDPsession()
125 else:
126 self.client_instance.HOOK_pulseaudio_server_startup_failed()
127
128 while self._keepalive and _is_alive:
129 gevent.sleep(1)
130 rc = win32event.WaitForMultipleObjects([hProcess],
131 1,
132 1,
133 )
134 _is_alive = ( rc != win32event.WAIT_OBJECT_0 )
135 if self.client_instance and not _is_alive:
136 self.client_instance.HOOK_pulseaudio_server_died()
137
138 self.logger('terminating running PulseAudio server', loglevel=log.loglevel_DEBUG)
139
140
141 self.logger('PulseAudio process ID to terminate: %s' % processId, loglevel=log.loglevel_DEBUG)
142 try:
143 win32process.TerminateProcess(hProcess, 0)
144 except win32process.error:
145 pass
146
148 """\
149 Tear down a running Pulseaudio daemon.
150
151 """
152 self.logger('stop_thread() method has been called', loglevel=log.loglevel_DEBUG)
153 self._keepalive = False
154