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 win32process
33 import win32con
34 import win32event
35
36
37 import os
38 import sys
39 import threading
40 import gevent
41 import copy
42 import socket
43
44 from defaults import LOCAL_HOME as _LOCAL_HOME
45
46
47 import log
48
49 import exceptions
51 """ Exception denoting that this operating system is not supported. """
52
54 """
55 This class controls the Pulse Audio daemon.
56 """
57
59 """\
60 Initialize a Pulse Audio daemon instance.
61
62 @param path: full path to pulseaudio.exe
63 @type path: C{str}
64 @param client_instance: the calling L{X2GoClient} instance
65 @type client_instance: L{X2GoClient} instance
66 @param logger: you can pass an L{X2GoLogger} object to the L{X2GoClientXConfig} constructor
67 @type logger: C{obj}
68 @param loglevel: if no L{X2GoLogger} object has been supplied a new one will be
69 constructed with the given loglevel
70 @type loglevel: C{int}
71
72 @raise OSNotSupportedException: on non-Windows platforms Python X2Go presumes that pulseaudio is already launched
73
74 """
75 if _X2GOCLIENT_OS not in ("Windows"):
76 raise OSNotSupportedException('classes of x2go.pulseaudio module are for Windows only')
77
78 if logger is None:
79 self.logger = log.X2GoLogger(loglevel=loglevel)
80 else:
81 self.logger = copy.deepcopy(logger)
82 self.logger.tag = __NAME__
83
84 self.path = path
85 self.client_instance = client_instance
86 self._keepalive = None
87
88 threading.Thread.__init__(self)
89 self.daemon = True
90 self.start()
91
93 """\
94 This method is called once the C{X2GoPulseAudio.start()} method has been called. To tear
95 down the Pulseaudio daemon call the L{X2GoPulseAudio.stop_thread()} method.
96
97 """
98 self._keepalive = True
99 cmd = 'pulseaudio.exe'
100 cmd_options = [
101 '-n',
102 '--exit-idle-time=-1',
103 '-L "module-native-protocol-tcp port=4713 auth-cookie=\\\\.pulse-cookie"',
104 '-L "module-esound-protocol-tcp port=16001"',
105 '-L module-waveout',
106 ]
107
108
109
110
111
112
113
114
115
116
117
118 wv = sys.getwindowsversion()
119 if (wv.major==5):
120 self.logger('Windows XP or Server 2003 (R2) detected.', loglevel=log.loglevel_DEBUG)
121 self.logger('Setting PulseAudio to "Normal" CPU priority.', loglevel=log.loglevel_DEBUG)
122 cmd_options.insert(0,"--high-priority=no")
123
124 cmd_options = " %s" % " ".join(cmd_options)
125
126 if not os.path.isdir(os.path.join(_LOCAL_HOME, '.pulse', '%s-runtime' % socket.gethostname())):
127 os.makedirs(os.path.join(_LOCAL_HOME, '.pulse', '%s-runtime' % socket.gethostname()))
128 self.logger('starting PulseAudio server with command line: %s%s' % (cmd, cmd_options), loglevel=log.loglevel_DEBUG)
129
130 si = win32process.STARTUPINFO()
131 p_info = win32process.CreateProcess(None,
132 '%s\\%s %s' % (self.path, cmd, cmd_options),
133 None,
134 None,
135 0,
136 win32con.CREATE_NO_WINDOW|win32process.NORMAL_PRIORITY_CLASS,
137 None,
138 None,
139 si,
140 )
141 (hProcess, hThread, processId, threadId) = p_info
142
143 gevent.sleep(5)
144 rc = win32event.WaitForMultipleObjects([hProcess],
145 1,
146 1,
147 )
148 _is_alive = ( rc != win32event.WAIT_OBJECT_0 )
149 if self.client_instance and not _is_alive:
150 if os.environ.has_key('CLIENTNAME'):
151 self.client_instance.HOOK_pulseaudio_not_supported_in_RDPsession()
152 else:
153 self.client_instance.HOOK_pulseaudio_server_startup_failed()
154
155 while self._keepalive and _is_alive:
156 gevent.sleep(1)
157 rc = win32event.WaitForMultipleObjects([hProcess],
158 1,
159 1,
160 )
161 _is_alive = ( rc != win32event.WAIT_OBJECT_0 )
162 if self.client_instance and not _is_alive:
163 self.client_instance.HOOK_pulseaudio_server_died()
164
165 self.logger('terminating running PulseAudio server', loglevel=log.loglevel_DEBUG)
166
167
168 self.logger('PulseAudio process ID to terminate: %s' % processId, loglevel=log.loglevel_DEBUG)
169 try:
170 win32process.TerminateProcess(hProcess, 0)
171 except win32process.error:
172 pass
173
175 """\
176 Tear down a running Pulseaudio daemon.
177
178 """
179 self.logger('stop_thread() method has been called', loglevel=log.loglevel_DEBUG)
180 self._keepalive = False
181