Package x2go :: Module pulseaudio

[frames] | no frames]

# Source Code for Module x2go.pulseaudio

      1  # -*- coding: utf-8 -*-
      2
      3  # Copyright (C) 2010-2016 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
      4  #
      5  # Python X2Go is free software; you can redistribute it and/or modify
      6  # it under the terms of the GNU Affero General Public License as published by
      7  # the Free Software Foundation; either version 3 of the License, or
      8  # (at your option) any later version.
      9  #
     10  # Python X2Go is distributed in the hope that it will be useful,
     11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  # GNU Affero General Public License for more details.
     14  #
     15  # You should have received a copy of the GNU Affero General Public License
     16  # along with this program; if not, write to the
     17  # Free Software Foundation, Inc.,
     18  # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
     19  #
     20  # Other contributors:
     21  #       none so far
     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  # modules
     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  # Python X2Go modules
     47  import log
     48
     49  import exceptions
     50 -class OSNotSupportedException(exceptions.StandardError): pass
     51  """ Exception denoting that this operating system is not supported. """
     52
     53 -class X2GoPulseAudio(threading.Thread):
     54      """
     55      This class controls the Pulse Audio daemon.
     56      """
     57
     58 -    def __init__(self, path=None, client_instance=None, logger=None, loglevel=log.loglevel_DEFAULT):
     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
     92 -    def run(self):
     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          # Fix for python-x2go bug #537.
    109          # Works Around PulseAudio bug #80772.
    110          # Tested with PulseAudio 5.0.
    111          # This argument will not cause PulseAudio 1.1 to fail to launch.
    112          # However, PulseAudio 1.1 ignores it for some reason.
    113          # So yes, the fact that 1.1 ignores it would be a bug in python-x2go if
    114          # we ever ship 1.1 again.
    115          #
    116          # wv.major == 5 matches 2000, XP, and Server 2003 (R2).
    117          # (Not that we support 2000.)
    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, # wait just one millisec
    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, # wait just one millisec
    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          # there is no real kill command on Windows...
    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
    174 -    def stop_thread(self):
    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