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