class Object

Constants

V_DBG
V_INFO

Public Instance Methods

Help() click to toggle source

Purpose: Print the help output

# File wwine, line 776
def Help ()
        puts "wwine "+$version
        puts ""
        puts "Usage: wwine (WWINE PARAMETERS) PROGRAM -- [PROGRAM ARGUMENTS]"
        puts ""
        printHelp('-h','--help','Display this help text')
        printHelp('-w','--wine','Select the wine to use. Any wine listed in "wwine --list" or the path to a wine binary or crossover installation directory.')
        printHelp('-b','--bottle','Use the selected bottle (~/.wwinebottles/[NAME] for wine, CX bottle or cedega folder, depending on the --wine in use). The bottle will be created if it does not exist.')
        printHelp('','--list-proton-bottles','Output a list of all available Proton/Steam game bottles')
        printHelp('','--debug','Enable wine debugging output with the supplied levels (using the format of the WINEDEBUG environment variable)')
        printHelp('','--env','Set the WINE and WINEPREFIX environment variables to match the parameters provided to wwine and optionally run a program with those variables set.')
        printHelp('','--arch','Set the wine architecture. Accepts win32 and win64. The default is win32 if --wine does not end with "64"')
        printHelp('-l','--list','List all available wine versions')
        printHelp('-k','--kill','Attempt to kill all running wine processes')
        printHelp('','--drykill','Print what --kill would have done, but don\'t actually do anything')
        printHelp('','--gamemode','Enable Feral GameMode')
        printHelp('','--no-gamemode','Disable Feral GameMode')
        printHelp('','--wrap','Write a wrapper script of your current wwine command to the path supplied')
        printHelp('','--wrapdir','Use the supplied directory as the working directory for the script created by --wrap (default: current directory)')
        printHelp('-s','--from','Load parameters and program from the wrapper script supplied. Command-line arguments overrides settings from wrapper script.')
        printHelp('-v','--verbose','Enable verbose mode. Supply multiple times to further increase verbosity.')
        printHelp('','--man','Show the wwine manpage')
end
debugInfo() click to toggle source

Purpose: Print debugging info

# File wwine, line 953
def debugInfo ()
        outFormat = '%-30s: %s'+"\n"
        puts "wwine "+$version
        # Load a --from file if it is set
        if $wwineDataFrom
                loadWwineDataFromFile($wwineDataFrom,true)
        end
        # Fetch the md5sum of us. This will at least give a certain clue about the revision
        # of wwine used. Primarily useful for identifying older git clones in bug reports.
        begin
                if inPath('md5sum')
                        outStr = nil
                        out = IO.popen('md5sum '+File.expand_path($0))
                        outStr = out.readline
                        outStr.sub!(/\s+.*$/,'')
                        outStr.chomp!
                        printf(outFormat,'wwine md5sum',outStr)
                else
                        printf(outFormat,'wwine md5sum','(md5sum command missing)')
                end
        rescue
                printf(outFormat,'wwine md5sum','(exception: '+$!+')');
        end

        listWines(true)
        exit(0)
end
emulateVersionOutput(wine) click to toggle source

Purpose: Emulate –version for nonstandard wine

# File wwine, line 486
def emulateVersionOutput(wine)
        # For CX* we can actually output a useful version number, namely the
        # wine version that CX* release was based on
        if wine == 'cxgames' || wine == 'cxoffice' || wine == 'crossover'
                cxgBin = getCXwithParams(wine,nil,false)
                cxVer, cxwVer = getCXVersionsFrom( cxgBin )
                # CX* wine --versions often contain git revisions and other additional
                # information in the version number, this strips that out.
                cxwVer.sub!(/-.*/,'')
                puts 'wine-'+cxwVer
        # For proton we use the proton version number
        elsif wine =~ /^proton-/
                version = wine.sub(/^proton-(\d+.\d+)\D*/,'\1')
                puts 'wine-'+version
        # For cedega we just output an invalid version number in the same syntax
        # as vanilla wine would have done
        elsif resolveWine(wine) == 'gametree'
                puts 'wine-0.0.0'
        end
end
envPut(name,value) click to toggle source

Purpose: Set an environment variable

# File wwine, line 766
def envPut (name,value)
        if value == nil
                puts "ERROR: Tried to set nil env var: '"+name+"'"
                exit 1
        end
        vputs(V_DBG,'Set '+name+'='+value)
        ENV[name] = value;
end
generateWrapper(wine,bottle,targetFile,wineCommand,args,type = nil) click to toggle source

Purpose: Prepare to generate a wrapper script

# File wwine, line 451
def generateWrapper (wine,bottle,targetFile,wineCommand,args,type = nil)

        if File.exists?(targetFile)
                puts(targetFile+': already exists. Bailing out')
                puts('Remove the target file first, then try again')
                exit(1)
        end

        wwineCmd, wwineInfo = generateWwineCmd(wine,bottle,wineCommand,type)

        # Append -- if it isn't already
        if ! args.join(' ').match(/ -- /)
                wwineCmd.push('--')
        end

        wwineCmd.concat(args)

        if $wrapperCwd == nil
                $wrapperCwd = Dir.pwd
        end

        wwineInfo['Dir'] = $wrapperCwd

        wineprefix = nil

        # For wine there is no --bottle definition in the wine command itself,
        # so we need to export a wineprefix in the launch script
        if (type == 'wine' || type =~ /^(pol|lutris)[-:]/) && ENV['WINEPREFIX']
                wineprefix = ENV['WINEPREFIX'].dup
        end

        writeWrapper(targetFile ,wwineCmd, wineCommand, $wrapperCwd, wwineInfo, args, wineprefix)
end
generateWwineCmd(wine,bottle,wineCommand,type = nil) click to toggle source

Purpose: Generate a wwine command and wwineInfo array

# File wwine, line 414
def generateWwineCmd (wine,bottle,wineCommand,type = nil)
        wwineCmd = []
        wwineInfo = {
                'Dir' => 'nil',
                'Bottle' => 'nil',
                'Wine' => 'nil',
                'GameMode' => 'nil',
        }

        # Generate a wwine command-line
        if $verbosity > 0
                wwineCmd.push('--verbose')
        end
        if $wineDebug
                wwineCmd.push('--debug',$wineDebug)
                wwineInfo['Debug'] = $wineDebug
        end
        if $wineArch
                wwineCmd.push('--arch',$wineArch)
                wwineInfo['Arch'] = $wineArch
        end
        if $preloadGameMode
                wwineCmd.push('--gamemode')
                wwineInfo['GameMode'] = 'true'
        end
        if wine
                wwineCmd.push('--wine',wine)
                wwineInfo['Wine'] = wine
        end
        if bottle
                wwineCmd.push('--bottle',bottle)
                wwineInfo['Bottle'] = bottle;
        end
        return wwineCmd, wwineInfo
end
getAutoWithParams(bottle, missingIsFatal = true, returnFlavour = false) click to toggle source

Purpose: Detect which wine to use and get parameters for that one Returns: type,cmd

# File wwine, line 1574
def getAutoWithParams (bottle, missingIsFatal = true, returnFlavour = false)
        cmd = getWineWithParams('wine',bottle,false)
        type = 'wine'
        if cmd == nil
                cmd = getCXwithParams('cxgames',bottle,false)
                type = 'cxgames'
        end
        if cmd == nil
                cmd = getCXwithParams('cxoffice',bottle,false)
                type = 'cxoffice'
        end
        if cmd == nil
                cmd = getGameTreeWithParams('gametree',bottle,false)
                type = 'gametree'
        end
        if cmd == nil
                if missingIsFatal
                        puts 'Failed to detect any wine version at all.'
                        exit(1)
                else
                        return nil
                end
        else
                vputs(V_DBG,'Autodetected wine command: '+cmd.join(' '))
                return type,cmd
        end
end
getCXVersionsFrom(source,wantString = false) click to toggle source

Purpose: Get cx* version

# File wwine, line 909
def getCXVersionsFrom (source,wantString = false)
        cxVer = nil
        cxwVer = nil
        if source != nil
                begin
                        # Store the binfile for use later
                        binFile = source[0]
                        # pop off the dummy command
                        source.pop
                        # Add version
                        source.push('--version')
                        source = source.join(' ')

                        out = IO.popen(source)
                        # The info we need is on line two
                        cxVer = out.readline
                        cxVer = out.readline
                        # Get the actual version number by stripping away any leading nondigit text
                        cxVer.sub!(/^\D+/,'')
                        cxVer.chomp!

                        cxwVer = getWineVersion(binFile.sub(/cxstart$/,'wineserver'),true,'-v')
                rescue
                        cxwVer = nil
                        cxVer = nil
                end
        end
        if wantString
                # If a string has been requested, generate one and return it
                if cxVer != nil
                        if cxwVer != nil
                                return cxVer+' (based on wine '+cxwVer+')'
                        else
                                return cxVer
                        end
                else
                        return nil
                end
        else
                return cxVer,cxwVer
        end
end
getCXwithParams(wine,bottle, missingIsFatal = true, getBottleDir = false, getCXpath = false) click to toggle source

Purpose: Get parameters for crossover

# File wwine, line 1195
def getCXwithParams (wine,bottle, missingIsFatal = true, getBottleDir = false, getCXpath = false)
        final = []
        cxdir = nil
        pathMode = nil

        if File.directory?(wine)
                if Dir.glob(wine+'/etc/cxgames*.conf').length > 0
                        installName = 'cxgames'
                        mode = 'cxgames'
                else
                        installName = 'cxoffice'
                        mode = 'crossover'
                end
                pathMode = wine
                wines = [ wine ]
        else
                installName = wine
                if wine == 'crossover'
                        installName = 'cxoffice'
                end
                mode = wine

                # Various crossover install paths
                wines = ['/opt/'+installName, ENV['HOME']+'/'+installName, ENV['HOME']+'/.local/'+installName, ENV['HOME']+'/games/'+installName]
        end
        bottleDir = '.'+installName

        wines.each do |path|
                if File.exists?(path)
                        if ( File.executable?(path+'/bin/cximportbottles') || File.executable?(path+'/bin/crossover') ) and installName != 'cxgames'
                                # Perform additional detection of dotDir, as that one tends to change
                                dotDir = Dir.glob(path+'/etc/cxoffice*.conf').shift
                                if( dotDir != nil && dotDir.sub!(/^.*(cxoffice\d*)\.conf$/,'\1') )
                                        mode = 'crossover'
                                        cxdir = path
                                        bottleDir = '.'+dotDir
                                        break
                                end
                        elsif File.executable?(path+'/bin/cxstart')
                                cxdir = path
                                break
                        end
                end
        end

        # If we're using $cx*Path, perform some additional detection
        if pathMode != nil and mode != 'crossover'
                if File.exists?(pathMode+'/etc/'+installName+'.conf')
                        cxdir = pathMode
                else
                        cxdir = nil
                end
        end

        # If the dir does not exist, give up
        if not cxdir
                if !missingIsFatal
                        vputs(V_DBG,'Failed to detect cxdir for '+mode)
                        return nil
                end
                if pathMode != nil
                        puts('Could not find a directory named "'+installName+'" in '+pathMode)
                        puts('and '+pathMode+' does not appear to be a '+installName+' install directory')
                        if installName == 'cxgames'
                                reverse = 'cxoffice'
                        else
                                reverse = 'cxgames'
                        end
                else
                        puts('Failed to locate '+wine+ ' (cx)')
                        puts('You could try to explicitly tell wwine where '+wine+' is installed')
                        puts('by supplying the full path to --wine')
                end
                exit(1)
        elsif getCXpath
                return cxdir
        end

        if bottle =~ /\//
                puts wine+' (cx) does not support bottles that exist outside of ~/'+bottleDir
                exit 1
        end

        final.push(cxdir+'/bin/cxstart')
        if(bottle != nil && bottle.length > 0)
                final.push('--bottle',bottle)
        end
        if $wineDebug
                final.push('--debugmsg',$wineDebug)
        end
        final.push('--');

        # Create the bottle if it does not exist
        if bottle != nil && !File.exists?(ENV['HOME']+'/'+bottleDir+'/'+bottle)
                vputs(V_DBG,ENV['HOME']+'/'+bottleDir+'/'+bottle+' did not exist')
                puts 'The bottle '+bottle+' did not exist, creating...'
                runcmd([ cxdir+'/bin/cxbottle', '--bottle',bottle,'--create','--template','winxp'],'system')
                if ! File.exists?(ENV['HOME']+'/'+bottleDir+'/'+bottle)
                        puts 'Bottle creation failed.'
                        exit 1
                end
        end

        if getBottleDir
                return ENV['HOME']+'/'+bottleDir+'/'+bottle
        else
                vputs(V_DBG,'Detected cx: '+final.join(' '))
                return final
        end
end
getCustomwithParams(wine,bottle, type = 'POL', missingIsFatal = true, returnDetected = false) click to toggle source

Purpose: Get parameters for PlayOnLinux or Lutris

# File wwine, line 1343
def getCustomwithParams(wine,bottle, type = 'POL', missingIsFatal = true, returnDetected = false)
        winePaths = []
        polMode = true
        if type =~ /^pol/i
               winePaths = [ ENV['HOME']+'/.PlayOnLinux/wine/linux-x86', ENV['HOME']+'/.PlayOnLinux/wine/linux-amd64' ]
        else
                polMode = false
                winePaths = [ ENV['HOME']+'/.local/share/lutris/runners/wine/' ]
        end
        detectedWine = nil
        detectedWineDir = nil
        winePaths.each do |path|
                if File.exists?(path+'/'+wine+'/bin/wine')
                        detectedWine = path+'/'+wine+'/bin/wine'
                        detectedWineDir = path+'/'+wine
                end
        end

        if returnDetected
                return detectedWine
        end

        if not detectedWine
                if !missingIsFatal
                        vputs(V_DBG,'Failed to locate wine '+wine)
                        return nil
                end
                puts('There does not appear to be a wine named "'+wine+'"')
                puts('installed via PlayOnLinux. See "wwine --list" for a list of available wine versions.')
                exit(1)
        end

        envPut('PATH',detectedWineDir+'/bin/:'+ENV['PATH'])
        envPut('WINESERVER',detectedWineDir+'/bin/wineserver')
        envPut('WINELOADER',detectedWine)
        envPut('WINEDLLPATH',detectedWineDir+'/lib/wine:'+ENV['WINEDLLPATH'].to_s)
        envPut('LD_LIBRARY_PATH',detectedWineDir+'/lib:'+ENV['LD_LIBRARY_PATH'].to_s)
        return getWineWithParams(detectedWine,bottle,missingIsFatal,polMode)
end
getFromEnviron(path) click to toggle source

Purpose: Get information from /proc/PID/environ

# File wwine, line 189
def getFromEnviron(path)
        info = Hash.new
        info['bottle'] = '(?)'
        info['wine'] = '(?)'
        if File.readable?(path)
                source = File.open(path)
                env = Hash.new
                source.each("\0") do |entry|
                        key = entry.sub(/^([^=]+)=.*$/,'\1').chomp
                        value = entry.sub(/^[^=]+=(.*)$/,'\1').sub(/\0/,'').chomp
                        env[key] = value
                end

                if env['__WWINE_INT_XWINE']
                        info['wine'] = env['__WWINE_INT_XWINE']
                elsif env['CX_ROOT'] && env['CX_ROOT'].match(/cxoffice/)
                        info['wine'] = 'crossover'
                elsif env['CX_ROOT'] && env['CX_ROOT'].match(/cxgames/)
                        info['wine'] = 'cxgames'
                elsif env['WINE']
                        info['wine'] = env['WINE']
                elsif env['WINELOADER']
                        info['wine'] = 'wine'
                elsif info['bottle'] == '.wine-pipelight'
                        info['wine'] = 'pipelight'
                        info['bottle'] = '(pipelight)'
                elsif env['WINEDLLPATH'] =~ /Proton/
                        proton = env['WINEDLLPATH'].sub(/.*Proton\s+([^\/]+).*/,'proton-\1')
                        if proton !~ /\//
                                info['wine'] = proton.downcase.sub(/\s/,'')
                        end
                end
                if env['CX_BOTTLE']
                        info['bottle'] = env['CX_BOTTLE']
                elsif info['wine'] =~ /proton/i
                        bottle = env['__WWINE_INT_XBOTTLE'] || env['WINEPREFIX'] || env['STEAM_COMPAT_DATA_PATH']
                        if bottle
                                if File.basename(bottle) == 'pfx'
                                        bottle = File.realpath(bottle+'/../')
                                end
                                bottleId = File.basename(bottle)
                                if bottleId =~ /\D/
                                        info['bottle'] = '(?)'
                                else
                                        info['bottle'] = 'steam:'+bottleId
                                end
                        end
                elsif env['__WWINE_INT_XBOTTLE']
                        info['bottle'] = env['__WWINE_INT_XBOTTLE']
                elsif env['WINEPREFIX']
                        info['bottle'] = File.basename(env['WINEPREFIX'])
                        if info['bottle'] == 'pfx' && info['wine'] =~ /proton/i
                                info['bottle'] = File.basename(File.dirname(ENV['WINEPREFIX']))
                        end
                else
                        info['bottle'] = '(default)'
                end
        end
        return info
end
getGameTreeVersionString() click to toggle source

Purpose: Get cedega version string

# File wwine, line 861
def getGameTreeVersionString ()
        cedegaVer = nil

        if inPath('cedega') || inPath('gametree')
                begin
                        # We need the HOME variable
                        if ENV.has_key?('HOME')
                                # The cedega RC file
                                rcFile = ENV['HOME']+'/.cedega/.cedegarc'
                                # The cedega build number file
                                uiBuildFile = ENV['HOME']+'/.cedega/.ui/BUILDNUMBER'
                                # If we have the rc file, open it and fetch the current default
                                # engine version from the 'default' setting. It is the closest we are
                                # going to get to a current version number without jumping through a lot
                                # of hoops.
                                if File.exists?(rcFile)
                                        ind = File.open(rcFile)
                                        ind.each do |line|
                                                if line.match(/^default=/)
                                                        line.sub!(/^default=/,'')
                                                        line.chomp!
                                                        cedegaVer = line
                                                        break
                                                end
                                        end
                                end
                                # If we have a default ver, fetch the UI version as well, as we have to pass
                                # through the UI, that number can be useful
                                if cedegaVer != nil && File.exists?(uiBuildFile)
                                        ind = File.open(uiBuildFile)
                                        buildNo = ind.gets(nil)
                                        buildNo.chomp!
                                        if buildNo != nil
                                                cedegaVer.concat(' (UI build '+buildNo+')');
                                        end
                                end
                        end
                rescue
                end
                if cedegaVer == nil
                        cedegaVer = 'present'
                end
        end

        return cedegaVer
end
getGameTreeWithParams(wine,bottle, missingIsFatal = true) click to toggle source

Purpose: Get parameters for gametree

# File wwine, line 1307
def getGameTreeWithParams (wine,bottle, missingIsFatal = true)
        final = []
        gametreeBin = nil

        puts("WARNING: gametree/cedega support will be deprecated in wwine 0.8")

        if inPath('gametree')
                gametreeBin = 'gametree'
        elsif inPath('gametree')
                gametreeBin = 'cedega'
        else
                if missingIsFatal
                        puts('gametree does not appear to be installed')
                        exit(1)
                else
                        return nil
                end
        end

        final.push(gametreeBin,'--install')

        if $wineDebug
                finalpush('--debugmsg',$wineDebug)
        end

        # Use wwineFolder as the folder if no bottle was supplied
        if (bottle == nil || bottle.length == 0)
                bottle = 'wwineFolder'
        end
        final.push(bottle)

        vputs(V_DBG,'Detected gametree: '+final.join(' '))
        return final
end
getProtonBaseDirs() click to toggle source

Purpose: Get a list of Proton base directory locations

# File wwine, line 1477
def getProtonBaseDirs ()
        dirs = [ ENV['HOME']+'/.steam/root/SteamApps/', ENV['HOME']+'/.steam/root/steamapps/' ]
        steamConfigFile = ENV['HOME']+'/.steam/root/config/config.vdf'
        if File.exists?(steamConfigFile)
                source = File.open(steamConfigFile)
                source.each("\n") do |entry|
                        if entry =~ /\s*\"BaseInstallFolder_\d+\"\s*/
                                dir = entry.sub(/^\s*\"BaseInstallFolder_\d+\"\s*\"(.*)\"/,'\1').chomp
                                dirs.push(dir+'/steamapps/')
                        end
                end
        end
        return dirs
end
getProtonBottleName(id) click to toggle source

Purpose: Get the name of the game that owns a Proton bottle (converts ID to name)

# File wwine, line 1494
def getProtonBottleName (id)
        dirs = getProtonBaseDirs()
        dirs.each do |entry|
                manifestFile = entry+'/appmanifest_'+id+'.acf';
                if File.exists?(manifestFile)
                        manifest = File.open(manifestFile)
                        manifest.each("\n") do |entry|
                                if entry =~ /\s*\"(name|installdir)\"\s*/
                                        return entry.sub(/^\s*\"(name|installdir)\"\s*\"(.*)\"/,'\2').chomp
                                end
                        end
                end
        end
        return nil
end
getProtonBottles() click to toggle source

Purpose: Get a list of Proton bottles Returns a hash, mapping the key (ID) to another hash. The inner hash kontains the keys “directory” (path to the bottle) and “name” (name of the game owning the bottle)

# File wwine, line 1514
def getProtonBottles ()
        dirs = getProtonBaseDirs()

        bottles = {}
        dirs.each do |entry|
                Dir.glob(entry+'/compatdata/*') do |bottle|
                        if Dir.exists?(bottle+'/pfx')
                                bottle = File.realpath(bottle)
                                id = File.basename(bottle)
                                name = getProtonBottleName(id)
                                if name == nil
                                        name = 'uninstalled game ('+bottle+')'
                                end
                                bottles[id] = {
                                        "name" => name,
                                        "directory" => bottle+'/pfx'
                                }
                        end
                end
        end
        return bottles
end
getProtonWithParams(wine,bottle, missingIsFatal = true, multiReturn = false) click to toggle source

Purpose: Get parameters for Proton if multiReturn is true it will return cmd,bottle

# File wwine, line 1385
def getProtonWithParams(wine,bottle, missingIsFatal = true, multiReturn = false)
        command = []
        protons = getProtons()
        wine = wine.downcase
        if !protons[wine]
                if missingIsFatal
                        puts("Could not find a proton version matching \""+wine+"\"")
                        exit(1)
                else
                        return nil
                end
        end

        if bottle == nil
                bottle = 'wwine-proton-default-bottle'
        end

        runtimeWrapper = ENV['HOME']+'/.steam/root/ubuntu12_32/steam-runtime/run.sh'

        if ENV.has_key?('STEAM_RUNTIME') && ENV['STEAM_RUNTIME'] == "0"
                vputs(V_DBG,"Skipping Steam runtime for proton because of the STEAM_RUNTIME env var")
        elsif !File.exists?(runtimeWrapper)
                vputs(V_DBG, "Did not find Steam runtime wrapper script, skipping steam runtime")
        else
                command.push(runtimeWrapper)
        end

        command.push( protons[wine]+'/proton','run')

        if File.exists?(bottle) && File.basename(bottle) == 'pfx'
                bottle = File.realpath(bottle+'/../')
        end

        bottleRoot = ENV['HOME']+'/.wwinebottles/'
        if bottle =~ /^\// && File.exists?(bottle+'/pfx')
                # Proton uses STEAM_COMPAT_DATA_PATH to point to the bottle location
                envPut('STEAM_COMPAT_DATA_PATH',bottle)
        else
                if !Dir.exists?(bottleRoot)
                        vputs(V_INFO,bottleRoot+': does not exist, creating')
                        begin
                                Dir.mkdir(bottleRoot)
                        rescue SystemCallError
                                puts('Failed to create directory "'+bottleRoot+": "+$!.message)
                                puts('Unable to continue.')
                                exit(1)
                        end
                end
                if !Dir.exists?(bottleRoot+bottle)
                        vputs(V_INFO,bottleRoot+bottle+': does not exist, creating')
                        begin
                                Dir.mkdir(bottleRoot+bottle)
                        rescue SystemCallError
                                puts('Failed to create directory "'+bottleRoot+": "+$!.message)
                                puts('Unable to continue.')
                                exit(1)
                        end
                end
                # Proton always uses /pfx, we make that a symlink to the base bottle
                if !File.exists?(bottleRoot+bottle+'/pfx')
                        begin
                                FileUtils.ln_s(bottleRoot+bottle,bottleRoot+bottle+'/pfx')
                        rescue SystemCallError
                                puts('Failed to create pfx symlink in '+bottleRoot+bottle+': '+$!.message)
                                puts('Unable to continue')
                                exit(1)
                        end
                end
                # Proton uses STEAM_COMPAT_DATA_PATH to point to the bottle location
                envPut('STEAM_COMPAT_DATA_PATH',bottleRoot+bottle)
        end

        # Disable wineArch support. Proton uses its own.
        if $wineArch
                puts "Warning: proton does not support --arch, ignoring."
                $wineArch = nil
        end

        # Enable debugging if requested
        if $wineDebug
                puts "Notice: proton does not support --debug, ignoring."
        end

        vputs(V_DBG,'Detected proton: '+command.join(' '))
        if multiReturn
                return command,bottle+'/pfx'
        else
                return command
        end
end
getProtons() click to toggle source

Purpose: Get a list of installed proton versions

# File wwine, line 1553
def getProtons ()
        dirs = getProtonBaseDirs()
        # Proton gets installed in the same "steam library" directory that the first game
        # that used said version of Proton is installed to. Thus we need to parse out where
        # all of Steam's library directories are to be able to produce a complete list.
        protons = {}
        # Note: In the case of several identical proton versions we'll just use the last one
        # we find
        dirs.each do |entry|
                Dir.glob(entry+'/common/Proton*') do |instance|
                        if File.executable?(instance+'/proton')
                                version = instance.sub(/.*Proton\s+(\d+\.\d+)\s*(.*)?/,'\1\2').sub(/\s/,'').downcase
                                protons['proton-'+version] = instance
                        end
                end
        end
        return protons
end
getWineVersion(source = 'wine', fromStderr = false, cmdOpt = '--version') click to toggle source

Purpose: Get wine version

# File wwine, line 835
def getWineVersion (source = 'wine', fromStderr = false, cmdOpt = '--version')
        wineVer = nil

        # Don't try to get it if we have no wine to run
        if inPath(source) || File.executable?(source)
                begin
                        cmd = [ source,cmdOpt ]
                        Open3.popen3(*cmd) { |stdin, stdout, stderr|
                                # Fetch the line from either stderr or out
                                if fromStderr
                                        wineVer = stderr.gets(nil)
                                else
                                        wineVer = stdout.gets(nil)
                                end
                        }
                        # Get the actual version number by stripping away any leading nondigit text
                        wineVer.sub!(/^\D+/,'')
                        wineVer.chomp!
                rescue
                        wineVer = nil
                end
        end
        return wineVer
end
getWineWithParams(wine,bottle, missingIsFatal = true, playOnLinux = false, proton = false) click to toggle source

Purpose: Get parameters for wine

# File wwine, line 1094
def getWineWithParams (wine,bottle, missingIsFatal = true, playOnLinux = false, proton = false)
        final = []
        possibleWines = []
        if wine =~ /^wine(64|32|-unstable(32|64)?)$/
                possibleWines = [ wine ]
        else
                if $wineArch && $wineArch == 'win64'
                        possibleWines = [ 'wine64','wine-unstable64','wine','wine.bin','wine.real','wine-unstable' ]
                else
                        possibleWines = [ 'wine32','wine','wine64','wine.bin','wine.real','wine-unstable','wine32-unstable' ]
                end
        end
        # Manual search path, in the odd case none of the above are in $PATH.
        # Ie. /usr/lib32/wine/wine.bin is the real wine binary on Debian
        searchPath = [ '/usr/lib32/wine/','/usr/bin','/usr/lib/i386-linux-gnu/wine-unstable','/usr/lib/i386-linux-gnu/wine', ]
        # If wine is not a path or does not exist, check for wine in the path
        if !File.exists?(wine)
                wine = nil
                possibleWines.each do |w|
                        if inPath(w)
                                wine = w
                                break
                        end
                end

                if wine == nil
                        searchPath.each do |p|
                                possibleWines.each do |w|
                                        if File.executable?(p+'/'+w) && File.file?(p+'/'+w)
                                                wine = p+'/'+w
                                                break
                                        end
                                end
                                if wine != nil
                                        break
                                end
                        end
                end

                if wine == nil
                        if missingIsFatal == true
                                puts('"wine" does not appear to be installed')
                                exit(1)
                        else
                                return nil
                        end
                end
        end
        final.push(wine)

        # wwine's bottle storage
        wwine_bottleRoot = ENV['HOME']+'/.wwinebottles/'
        # PlayOnLinux's bottle storage
        playOnLinux_bottleRoot = ENV['HOME']+'/.PlayOnLinux/wineprefix/'
        # wwine is the default
        bottleRoot = wwine_bottleRoot
        # If a POL bottle exists and we are using a POL wine, then use the
        # POL bottle, otherwise use the wwine bottle
        if playOnLinux && bottle != nil
                if File.exists?(playOnLinux_bottleRoot+bottle)
                        bottleRoot = playOnLinux_bottleRoot
                        vputs(V_INFO,'Using the PlayOnLinux bottle path since a POL bottle named "'+bottle+'" exists')
                else
                        vputs(V_DBG,'Using the wwine bottle location for POL since no POL bottle named "'+bottle+'" exists')
                end
        end

        # Set WINEPREFIX for bottle support
        if(bottle != nil && bottle.length > 0)
                if ENV.has_key?('WINEPREFIX') && ENV['WINEPREFIX'].length > 0
                        warn 'WINEPREFIX= was already set, overriding it.'
                end
                if ! (bottle =~ /^\.?\//)
                        bottle = bottleRoot+bottle
                        if !File.exists?(bottleRoot)
                                vputs(V_INFO,bottleRoot+': does not exist, creating')
                                begin
                                        Dir.mkdir(bottleRoot)
                                rescue SystemCallError
                                        puts('Failed to create directory "'+bottleRoot+": "+$!)
                                        puts('Unable to continue.')
                                        exit(1)
                                end
                        end
                end
                envPut('WINEPREFIX',bottle)
        end

        # Enable debugging if requested
        if $wineDebug
                envPut('WINEDEBUG',$wineDebug)
                # Default to no debugging output if WINEDEBUG is not set yet
        elsif ! ENV.has_key?('WINEDEBUG')
                envPut('WINEDEBUG','-all')
        end

        vputs(V_DBG,'Detected wine: '+final.join(' '))
        return final
end
handleException(ex) click to toggle source

Purpose: Handle an exception

# File wwine, line 1079
def handleException(ex)
        puts('---')
        puts('Exception: '+ex.to_s)
        puts('Backtrace: '+"\n"+ex.backtrace.join("\n"))
        puts('---')
        puts()
        puts('An exception has occurred and wwine can not continue.')
        puts('This almost certainly reflects a bug in wwine.')
        puts('Please check that you have the latest version of wwine,')
        puts('and if you do, report this issue along with the text between the "---" above');
        puts('to http://random.zerodogg.org/wwine/bugs')
        exit(1)
end
inPath(exec) click to toggle source

Purpose: Check for a file in path

# File wwine, line 740
def inPath(exec)
        ENV['PATH'].split(/:/).each do |part|
                if File.executable?(part+'/'+exec) and not File.directory?(part+'/'+exec)
                        return true
                end
        end
        return false
end
killWine(signal) click to toggle source

Purpose: Attempt to kill all running wine processes

# File wwine, line 55
def killWine (signal)
        dryRun = false
        if signal == 9999
                dryRun = true
                signal = 15
        end
        # Open a pipe to ps to read processes
        wines = IO.popen('ps uxw')
        # The result of our ps parsing
        wineResult = []
        # The length of the "wine" name, used to format the output well
        wineLength = 10
        # The name of the signal being sent
        sigName = 'SIGTERM'
        # Get an integer version of signal parameter
        begin
                signal = Integer(signal)
        rescue
                signal = nil
        end
        # If the signal parameter is invalid, error out
        if signal == nil
                signal = 15
        elsif signal > 15 || signal < 0
                puts('Argument to --kill must be an integer between 0-15 (or none at all)')
                exit(1)
        end
        # Map of number => name
        sigNames = { 9 => 'SIGKILL', 15 => 'SIGTERM', 2 => 'SIGINT' }
        if sigNames[signal] != nil
                sigName = sigNames[signal]
        else
                sigName = 'signal '+signal
        end
        if !dryRun
                print 'Sending '+sigName+' to the following processes:'+"\n\n"
        end
        # Go through each line in the 'ps' output
        wines.each do |line|
                # True if we *require* /proc to be read for this process.
                # This is for cases where, judging by the process name we shouldn't
                # kill that process, but it's close enough to something we could kill
                # that we want to check if /proc can provide a more definitive answer.
                # (helps kill defunct processes)
                requireProc = false

                line.chomp!
                # Ignore the header
                if line =~ /USER\s*PID/
                        next
                end
                pid = String.new(line)
                name = String.new(line)
                # Parse the PID
                pid.sub!(/^\S+\s*(\d+)\s+.*/,'\1')
                # Parse the name
                name.sub!(/^\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+/,'')
                # If either of these are true some part of the parsing failed, and we
                # should thus simply skip this line
                if pid == name || pid == line || name == line
                        next
                end
                # Ignore processes that are not wine-related
                if name =~ /(wine|windows|[A-Za-z]:\\|\.(exe|dll))/i
                        # If wine is not a part of the process name, check if the
                        # name either includes a *:\ (which most likely means it is a windows
                        # path because it has already matched one of the above) or exe/dll.
                        if name !~ /wine/i && (name !~ /[A-Za-z]:\\/ || name !~ /\.(exe|dll)/i)
                                requireProc = true
                        end
                else
                        next
                end
                # At least on Linux kernels, /proc/ will include useful info, we use it to
                # check if the exe actually is wine

                        # /exe test
                if File.readable?('/proc/'+pid+'/exe') and File.symlink?('/proc/'+pid+'/exe')
                        if File.readlink('/proc/'+pid+'/exe') !~ /\/[^\/]*wine[^\/]*$/i
                                next
                        end
                        # /cmdline test (for when /exe isn't readable)
                elsif File.readable?('/proc/'+pid+'/cmdline')
                        cmdline = IO.read('/proc/'+pid+'/cmdline')
                        cmdline.sub!(/\0.*/,'');
                        if cmdline !~ /\/[^\/]*wine[^\/]*$/i && cmdline !~ /^[A-Za-z]:\\[^\\]+\\/
                                next
                        end
                elsif requireProc
                        next
                end
                # Clean up the name
                name.sub!(/\s*$/,'')
                extra = getFromEnviron('/proc/'+pid+'/environ')
                if $wine && resolveWine($wine) != extra['wine']
                        next
                elsif $bottle && $bottle != extra['bottle']
                        next
                end
                resolvedWine = resolveWine(extra['wine'])
                # Output information
                wineResult.push({
                        'wine' => resolvedWine,
                        'bottle' => extra['bottle'],
                        'pid' => pid,
                        'name' => name
                })

                if resolvedWine.length > wineLength
                        wineLength = resolvedWine.length
                end
        end
        # The format of the output
        format = "%-"+wineLength.to_s+"s %-12s %-8s %-20s\n"
        # Header
        printf(format,'Wine','Bottle','PID','Command')
        # Display or send signals to each entry
        wineResult.each do |entry|
                printf(format,entry['wine'],entry['bottle'],entry['pid'],entry['name'])
                # Send SIGTERM (or output information if in dryRun mode)
                if !dryRun
                        begin
                                Process.kill(signal,entry['pid'].to_i)
                        rescue
                                puts('Error while attempting to send signal to '+entry['pid']+': '+$!)
                        end
                end
        end
        if wineResult.length < 0
                puts 'No wine processes found.'
        end
end
listProtonBottles() click to toggle source

Purpose: Output a list of Proton bottles

# File wwine, line 1538
def listProtonBottles ()
        outFormat = '%-15s: %s'+"\n"
        bottles = getProtonBottles()
        begin
                printf(outFormat,'Bottle','Description')
                bottles.each do |bottle,meta|
                        printf(outFormat,'steam:'+bottle,meta['name'])
                        vputs(V_DBG,'steam:'+bottle+' is at '+meta['directory'])
                end
        rescue => ex
                handleException(ex)
        end
end
listWines(debug = false) click to toggle source

Purpose: List available wine versions

# File wwine, line 982
def listWines(debug = false)
        outFormat = '%-30s: %s'+"\n"
        begin
                defaultWine,defaultWCMD = getAutoWithParams(nil,false)
                if !debug
                        puts('Available wine versions:')
                end
                if defaultWine == nil
                        defaultWine = '(no wine found)'
                end
                if defaultWine != 'wine'
                        printf(outFormat,'(default wine)',defaultWine)
                end

                if inPath('wine')
                        wineVer = getWineVersion()
                        printf(outFormat,'wine',wineVer)
                        if defaultWine == 'wine' && !debug
                                puts "   (default when no --wine is specified)"
                        end
                end

                if $wine != nil and File.executable?($wine) and not File.directory?($wine)
                        otherWver = getWineVersion($wine)
                        printf(outFormat,'--wine',otherWver+' ('+$wine+')')
                end

                cxgBin = getCXwithParams('cxgames',nil,false)
                cxgVer = getCXVersionsFrom(cxgBin,true)

                cxoBin = getCXwithParams('cxoffice',nil,false)
                cxoVer = getCXVersionsFrom(cxoBin,true)

                if (cxgBin || !cxoBin || debug)
                        printf(outFormat,'cxgames',cxgVer || '(not present)')
                        if !debug
                                puts "   (aliases for cxgames: cxg, cxgames)"
                        end
                end

                if cxoVer || debug
                        printf(outFormat,'crossover',cxoVer)
                        if !debug
                                if !cxgBin
                                        puts "   (aliases for crossover: cx, cxo, cxg, cxoffice, cxgames)"
                                else
                                        puts "   (aliases for crossover: cx, cxo, cxoffice)"
                                end
                        end
                end

                cedegaVer = getGameTreeVersionString()
                if cedegaVer || debug
                        printf(outFormat,'gametree',cedegaVer || '(not present)')
                        if ! debug
                                puts "   (aliases for gametree: cedega)"
                        end
                end

                Dir.glob(ENV['HOME']+'/.PlayOnLinux/wine/linux-*/*') do |dir|
                        if File.executable?(dir+'/bin/wine')
                                name = dir.sub(/^.*\/([^\/]+)/,'\1')
                                printf(outFormat,'pol-'+name,getWineVersion(dir+'/bin/wine'))
                        end
                end

                Dir.glob(ENV['HOME']+'/.local/share/lutris/runners/wine/*') do |dir|
                        if File.executable?(dir+'/bin/wine')
                                name = dir.sub(/^.*\/([^\/]+)/,'\1')
                                printf(outFormat,'lutris-'+name,getWineVersion(dir+'/bin/wine'))
                        end
                end

                protons = getProtons()
                protons.each do |entry,path|
                  version = entry.sub(/^proton-/,'')
                  printf(outFormat,'proton-'+version,getWineVersion(path+'/dist/bin/wine'))
                end

                if $wine != nil && debug
                        version = nil
                        if File.directory?($wine)
                                cxBin = getCXwithParams($wine,nil,false)
                                version = getCXVersionsFrom(cxBin,true)
                        elsif File.executable?($wine)
                                version = getWineVersion($wine)
                        end
                        if version != nil
                                printf(outFormat,$wine,version)
                        end
                end
        rescue => ex
                handleException(ex)
        end
end
loadWwineDataFromFile(file,dryRun = false) click to toggle source

Purpose: Load wwine data from a wrapper script

# File wwine, line 570
def loadWwineDataFromFile (file,dryRun = false)
        begin
                if !File.exists?(file)
                        puts file+': does not exist'
                        exit(1)
                elsif ! File.readable?(file)
                        puts file+': is not readable'
                        exit(1)
                end

                # Don't output any info-messages when in --env mode
                if $envMode
                        outFormat = ''
                else
                        puts 'Loading settings from '+file+':'
                        outFormat = '%-20s: %s'+"\n"
                end
                source = File.open(file)

                if $wrapperPath
                        outMessage = ''
                else
                        outMessage = 'Using '
                end

                info = Hash.new

                source.each do |line|
                        if line.match(/^#\s+wwine/)
                                key = line.sub(/^#\s+wwine(\S+):\s*(.+)$/,'\1').chomp
                                value = line.sub(/^#\s+wwine(\S+):\s*(.+)$/,'\2').chomp
                                info[key] = value
                        end
                end

                if ! info['Info']
                        puts "The wwine header in this file is broken or missing."
                        puts "Unable to continue."
                        exit(0)
                end

                info['Version'] = info['Info'].sub(/^\s*v(\d+)\s*.*/,'\1').to_i

                if info['Version'] > 1 && info['Version'] <= 6
                        vputs(V_DBG,'File contains wwine metadata '+info['Info'])
                        info = parseWwineData(info);
                elsif info['Version'] == 1
                        puts "ERROR: Unsupported wwine metadata format (v1)"
                        puts ""
                        puts "This format is no longer supported. You will have to create a new wrapper script"
                        puts "using --wrap."
                        exit(1)
                else
                        version = info['Info'].sub(/^(v\S+).+/,'\1').chomp
                        puts "This version of wwine supports info format v2, v3, v4, v5 and v6."
                        puts "This file is version "+version
                        puts "Unable to continue."
                        exit(0)
                end

                if info['Cmd'].length < 1
                        puts "The wwine header in this file is broken."
                        puts "Unable to continue."
                        exit(0)
                end

                # Get the directory
                if info['Dir'] != 'nil' && !$wrapperCwd && !$envMode
                        if $wrapperPath
                                switchMessage = '--wrapdir'
                        else
                                switchMessage = 'Switching to directory'
                        end
                        printf(outFormat,switchMessage,info['Dir'])
                        if File.exists?(info['Dir'])
                                if $wrapperPath
                                        $wrapperCwd = info['Dir']
                                else
                                        Dir.chdir(info['Dir'])
                                end
                        else
                                puts info['Dir']+': does not exist, giving up.'
                                exit(0)
                        end
                end

                # Get the --wine to use
                if $wine == nil && info['Wine'] != 'nil'
                        $wine = info['Wine']
                        # Convert Cx*installdir to $wine if present in v2/v3 files
                        if info['Version'] < 4
                                # Support for legacy Cx*installdir
                                if info['Cxinstalldir'] != 'nil' && info['Cxinstalldir'] != nil
                                        vputs(V_DBG,'Converting Cxinstalldir entry to a Wine entry')
                                        $wine = info['Cxinstalldir']
                                end
                                if info['CxGamesinstalldir'] != 'nil' && info['CxGamesinstalldir'] != nil
                                        vputs(V_DBG,'Converting CxGamesinstalldir entry to a Wine entry')
                                        $wine = info['CxGamesinstalldir']
                                end
                        end
                        printf(outFormat,outMessage+'--wine',$wine)
                end

                # Get the --bottle to use
                if $bottle == nil && info['Bottle'] != 'nil'
                        $bottle = info['Bottle']
                        printf(outFormat,outMessage+'--bottle',$bottle)
                end

                # Set wineArch
                if info['Arch']
                        if info['Arch'] == 'win32' || info['Arch'] == 'win64'
                                $wineArch = info['Arch']
                                printf(outFormat,outMessage+'--arch ',$wineArch)
                        else
                                puts('WARNING: Arch: '+info['Arch']+' is invalid, ignoring!')
                        end
                end

                # Enable GameMode if needed
                if info['GameMode'] && info['GameMode'] == 'true'
                        $preloadGameMode = true
                        # FIXME: Drop the 'true'
                        printf(outFormat,outMessage+'--gamemode','true')
                end

                # Set the command to run
                if ! dryRun && !$envMode && !$killMode
                        if $wrapperPath
                                out = 'Program command'
                        else
                                out = 'Will now run'
                        end
                        printf(outFormat,out,info['Cmd'].join(' '))
                        ARGV.unshift(*info['Cmd'])
                end
        rescue => ex
                handleException(ex)
        end
end
parseWwineData(data) click to toggle source

Parses wwine metadata version 2+ (wwine 0.2+)

# File wwine, line 713
def parseWwineData (data)
        cmd = []
        currNo = 0
        while currNo < 999
                if data['Cmd['+currNo.to_s+']'] != nil
                        cmd.push(data['Cmd['+currNo.to_s+']'])
                else
                        break
                end
                currNo += 1
        end
        data['Cmd'] = cmd
        return data
end
printHelp(short,long,description) click to toggle source

Purpose: Print formatted –help output Usage: printHelp('-shortoption', '–longoption', 'description');

Description will be reformatted to fit within a normal terminal
# File wwine, line 253
def printHelp (short,long,description)
        maxlen = 80
        optionlen = 26
        # Check if the short/long are LONGER than optionlen, if so, we need
        # to do some additional magic to take up only $maxlen.
        # The +1 here is because we always add a space between them, no matter what
        if (short.length + long.length + 1) > optionlen
                optionlen = short.length + long.length + 1;
        end

        generatedDesc = ''
        currdesc = ''

        description.split(/ /).each do |part|
                if(generatedDesc.length > 0)
                        if (currdesc.length + part.length + 1 + 26) > maxlen
                                generatedDesc.concat("\n")
                                currdesc = ''
                        else
                                currdesc.concat(' ')
                                generatedDesc.concat(' ')
                        end
                end
                currdesc.concat(part)
                generatedDesc.concat(part)
        end
        if !(generatedDesc.length > 0)
                raise("Option mismatch")
        end
        generatedDesc.split(/\n/).each do |descr|
                printf("%-4s %-21s %s\n",short,long,descr)
                short = ''; long = ''
        end
end
resolveWine(wine) click to toggle source

Purpose: Resolve a wine to its proper name

# File wwine, line 1775
def resolveWine(wine)
        map = Hash.new()
        map['cx'] = 'crossover'
        map['cxo'] = 'crossover'
        map['cxoffice'] = 'crossover'
        map['cxg'] = 'cxgames'
        map['cedega'] = 'gametree'
        if(map[wine])
                wine = map[wine]
        end
        if wine =~ /\.local\/share\/lutris/
                remapped = wine.sub!(/^.*\.local\/share\/lutris\/runners\/wine\/([^\/]+).*/,'\1')
                return 'lutris-'+remapped
        end
        return wine
end
runWine(wine,bottle,args) click to toggle source

Purpose: Run the wine specified, using the bottle specified and arguments specified

# File wwine, line 1603
def runWine (wine,bottle,args)
        cmd = nil
        type = nil

        if bottle =~ /^steam:/
                bottleID = bottle.sub(/^steam:(\d+)/,'\1')

                protonBottles = getProtonBottles()

                if protonBottles[bottleID]
                        bottle = protonBottles[bottleID]['directory']
                else
                        puts "Unable to find a steam bottle with the ID \""+bottleID+"\""
                        exit 1
                end
        end

        if wine == nil
                type,cmd = getAutoWithParams(bottle)
                wine = type
        else
                # Expand cx*
                wine = resolveWine(wine)
                type = wine

                if File.directory?(wine)
                        if File.executable?(wine+'/bin/cxstart')
                                vputs(V_DBG,wine+': looks like a cx directory, assuming type=cx')
                                type = 'crossover'
                        end
                end

                if type == 'crossover' || wine == 'cxgames' || wine == 'cxoffice' || wine == 'crossover'
                        type = 'crossover'
                        missingIsFatal = true
                        if wine == 'cxgames'
                                missingIsFatal = false
                        end
                        cmd = getCXwithParams(wine,bottle,missingIsFatal)
                        if wine == 'cxgames' && cmd == nil
                                cmd = getCXwithParams('crossover',bottle)
                                if cmd != nil
                                        puts('Converting --wine cxgames to --wine crossover since cxgames is not installed')
                                end
                        end
                elsif wine =~ /^wine(64|32|-unstable(32|64)?)?$/ || ( File.executable?(wine) && !File.directory?(wine) )
                        type = 'wine'
                        cmd = getWineWithParams(wine,bottle)
                elsif resolveWine(wine) == 'gametree'
                        cmd = getGameTreeWithParams(wine,bottle)
                elsif wine =~ /^(pol|lutris)[-:]?/i
                        realWine = wine.sub(/^(pol|lutris)[-:]?/i,'')
                        cmd = getCustomwithParams(realWine,bottle,wine)
                elsif wine =~ /^proton-/
                        cmd,bottle = getProtonWithParams(wine,bottle,true,true)
                else
                        puts('Unknown --wine: '+wine)
                        if File.directory?(wine)
                                puts(wine+' does not appear to be the path to a crossover installation directory')
                                puts('')
                        end
                        puts('Must be one of the wine versions listed by `wwine --list` or the path to either a')
                        puts('wine executeable or a crossover installation directory')
                        exit(1)
                end
        end
        if cmd == nil
                puts('Command detection failed. This is a bug')
                exit(1)
        end

        if wine =~ /^proton-/ && args[0] !~ /(\/|\\)/ && !File.exists?(args[0])
                orig = args[0]
                ['syswow64','system32'].each do |subdir|
                        if File.exists?(bottle+'/drive_c/windows/'+subdir+'/'+args[0]+'.exe')
                                orig = args[0]
                                args[0] = bottle+'/drive_c/windows/'+subdir+'/'+args[0]+'.exe'
                                vputs(V_DBG,'Resolving for Proton: '+orig+' -> '+orig+'.exe'+' -> '+args[0])
                                break
                        elsif File.exists?(bottle+'/drive_c/windows/'+subdir+'/'+args[0])
                                orig = args[0]
                                args[0] = bottle+'/drive_c/windows/'+subdir+'/'+args[0]
                                vputs(V_DBG,'Resolving for Proton: '+orig+' -> '+args[0])
                                break
                        end
                end
                if args[0] != orig
                        vputs(V_INFO,'Proton can\'t find Wine commands, therefore '+orig+' is converted to '+args[0])
                        puts 'wwine: Resolved '+orig+' to '+args[0]
                end
        end

        cmd.concat(args)

        # If args is a single parameter, that parameter is --version, and we're not using
        # vanilla wine, then we emulate vanilla wine's --version output. This helps
        # utilities like winetricks when it is running under "wwine --env"
        if args.length == 1 && args[0] == '--version'
                emulateVersionOutput(wine)
                exit
        end

        # If we're in envMode, then we let setEnvAndExec handle the rest
        if $envMode
                setEnvAndExec(wine,bottle,cmd,args,type)
        # If wrapperPath is set, then we're suppose to generate a wrapper script instead
        # of actually executing it.
        elsif $wrapperPath != nil
                generateWrapper(wine,bottle,$wrapperPath,cmd,args,type)
        else
                wine = resolveWine(wine)
                envPut('__WWINE_INT_XWINE',wine);
                if bottle
                        envPut('__WWINE_INT_XBOTTLE',bottle);
                end
                if $wineArch
                        envPut('WINEARCH',$wineArch)
                end
                if $preloadGameMode
                        preload = '/usr/$LIB/libgamemodeauto.so'
                        if ENV['LD_PRELOAD']
                                preload = preload+':'+ENV['LD_PRELOAD']
                        end
                        envPut('LD_PRELOAD',preload)
                end
                runcmd(cmd)
        end
end
runcmd(cmd, type = 'exec') click to toggle source

Purpose: Run a command, outputting info of it in verbose mode

# File wwine, line 729
def runcmd(cmd, type = 'exec')
        if(type == 'exec')
                vputs(V_INFO,'Executing: '+cmd.join(' '))
                exec *cmd
        else
                vputs(V_INFO,'Running: '+cmd.join(' '))
                return system(*cmd)
        end
end
setEnvAndExec(wine,bottle,cmd,args,type) click to toggle source

Purpose: Sets WINE and WINEPREFIX, and then either outputs them to STDOUT, or

executes whatever native command was supplied (the logic that handles --env)
# File wwine, line 509
def setEnvAndExec (wine,bottle,cmd,args,type)
        if wine == nil
                type,cmd = getAutoWithParams(bottle)
        end
        envPut('__WWINE_INT_WINE',wine)

        if type == 'crossover'
                cxdir = getCXwithParams(wine,bottle,true,false,true)
                if File.executable?(cxdir+'/bin/wineserver')
                        envPut('WINESERVER',cxdir+'/bin/wineserver')
                end
        end

        if !bottle
                puts("--env requires a --bottle")
                exit(1)
        end
        envPut('__WWINE_INT_BOTTLE',bottle);
        envPut('__WWINE_INT_ENVMODE',$version)
        if $verbosity > 0
                envPut('__WWINE_INT_VERBOSITY',$verbosity.to_s)
        end

        if type == 'wine' || wine == 'wine' || (File.exists?(wine) && !File.directory?(wine)) || wine =~ /^(pol|lutris)[-:]?/i
                vputs(V_DBG,"Used ENV/WINEPREFIX method to get bottle")
                bottlePath = ENV['WINEPREFIX']
        elsif type == 'proton' || wine =~ /^proton-/
                vputs(V_DBG,"Used STEAM_COMPAT_DATA_PATH method to get bottle")
                bottlePath = ENV['STEAM_COMPAT_DATA_PATH']
                if File.exists?(bottlePath+'/pfx')
                        bottlePath = bottlePath+'/pfx'
                end
        elsif resolveWine(wine) == 'gametree'
                vputs(V_DBG,"Used ENV/cedega method to get bottle")
                bottlePath = ENV['HOME']+'/.cedega/'+bottle
        else
                vputs(V_DBG,"Used CX method to get bottle")
                bottlePath = getCXwithParams(wine,bottle,true,true)
        end
        if $wineArch
                envPut('WINEARCH',$wineArch)
                envPut('__WWINE_INT_WINEARCH',$wineArch)
        end

        if bottlePath == nil
                puts "ERROR: setEnvAndExec() has no bottlePath"
                exit 1
        end
        envPut('WINEPREFIX',bottlePath)
        envPut('WINE',File.expand_path($0))
        if args.length > 0
                puts("Executing "+args.join(' '))
                exec *args
        else
                puts("WINE="+shellQuote(ENV['WINE']))
                puts("WINEPREFIX="+shellQuote(ENV['WINEPREFIX']))
        end
        exit
end
shellQuote(target) click to toggle source

Purpose: Quote a string (or array of strings) for use in the shell

# File wwine, line 289
def shellQuote(target)
        if target.class == Array
                newTarget = []
                target.each do |subt|
                        subt = shellQuote(subt)
                        newTarget.push(subt)
                end
                return newTarget
        end

        fixedTarget = target.gsub('\\') { '\\\\' }
        fixedTarget.gsub!(/'/,"'\"'\"'")

        finalTarget = "'"
        finalTarget.concat(fixedTarget)
        finalTarget.concat("'");
        return finalTarget
end
showManPage() click to toggle source

Purpose: Show the manpage

# File wwine, line 801
def showManPage ()
        if ! inPath('man')
                puts
                puts "You don't appear to have the 'man' program installed."
                puts "Please install it, then re-run wwine --man"
                exit(0)
        end
        mySelf = File.expand_path($0)
        while File.symlink?(mySelf)
                mySelf = File.readlink(mySelf)
        end
        sourceDir = '.'
        if mySelf != nil
                sourceDir = File.dirname(mySelf)
        end
        dirs = [sourceDir]
        if ENV['MANPATH']
                dirs.concat(ENV['MANPATH'].split(':'))
        end
        dirs.push('./')
        dirs.each do |dir|
                [ 'wwine.1','man1/wwine.1','man1/wwine.1.gz','man1/wwine.1.bz2','man1/wwine.1.lzma'].each do |manFile|
                        if File.exists?(dir+'/'+manFile)
                                exec('man',dir+'/'+manFile)
                        end
                end
        end
        puts
        puts 'wwine failed to locate its manpage.'
        puts 'Run the following command to view it:'
        puts '\curl -s "http://github.com/zerodogg/wwine/raw/master/wwine.1" |  groff -T utf8 -man | \less'
end
unShellQuote(target) click to toggle source

Purpose: Unquote a string generated by shellQuote

# File wwine, line 309
def unShellQuote(target)
        fixedTarget = target.gsub("\\'","'")
        fixedTarget.gsub!('\\\\') { '\\' }
        return fixedTarget
end
vputs(level,str) click to toggle source

Purpose: Output a string if in verbose mode

# File wwine, line 750
def vputs (level,str)
        if(level == V_DBG)
                str = 'Debug: '+str
        end
        if $verbosity >= level
                # Don't enable debugging output at all if STDOUT is not a tty and we're
                # running in env mode. In these cases the caller might rely upon parsing
                # specific output, and including debugging info can cause the caller to fail.
                if !STDOUT.isatty && ENV['__WWINE_INT_ENVMODE'] == $version
                        return
                end
                puts(str)
        end
end
writeWrapper(targetFile, wwineCommand, otherCommand, cwd, wwineInfo, args, wineprefix = nil) click to toggle source

Purpose: Write a wrapper script

# File wwine, line 316
def writeWrapper (targetFile, wwineCommand, otherCommand, cwd, wwineInfo, args, wineprefix = nil)
        # Open the file for writing
        begin
                file = File.open(targetFile,'w')
        rescue
                puts('Failed to open "'+targetFile+'" for writing: '+$!)
                exit(1)
        end

        # --gamemode requires v6, --arch requires v5, otherwise we are v4 compatible
        if $preloadGameMode
                metaFormat = 'v6'
        elsif $wineArch
                metaFormat = 'v5'
        else
                metaFormat = 'v4'
        end

        file.puts('#!/bin/sh')
        file.puts('# Script autogenerated by wwine '+$version+' (http://random.zerodogg.org/wwine)')
        file.puts('# Licensed under the GNU General Public License version 3 or later')
        file.puts('#')
        file.puts('# You should have received a copy of the GNU General Public License')
        file.puts('# along with wwine. If not, see <http://www.gnu.org/licenses/>.')
        file.puts('#')
        file.puts('# -- Begin wwine metadata --')
        file.puts('# wwineInfo: '+metaFormat)
        wwineInfo.each do |key,value|
                file.puts('# wwine'+key+': '+value)
        end

        subN = 0
        args.each do |sub|
                file.puts("# wwineCmd["+subN.to_s+"]: "+sub)
                subN += 1
        end

        file.puts('# -- End wwine metadata  --')
        file.puts('')
        file.puts("cd "+shellQuote(cwd)+" || exit 1")
        file.puts('wwine='+shellQuote(File.expand_path($0)))
        file.puts('if [ ! -x "$wwine" ]; then')
        file.puts("\t"+'wwine="`which wwine 2> /dev/null`"')
        file.puts('fi')
        file.puts('if [ "x$wwine" != "x" ]; then')
        # _WWINE_SCRIPT is currently just used to detect if a wrapper script is
        # being used. But is intended to help wwine use '--from' automatically in
        # the future if for some reason there are backwards-incompatible changes to
        # one of the command-line parameters used by scripts.
        file.puts("\t"+'export _WWINE_SCRIPT="$0"')
        file.puts("\t"+'exec "$wwine" '+shellQuote(wwineCommand).join(" ")+' "$@"')
        file.puts('else')
        # Add wineprefix if needed. This is used when --wine is wine because wine
        # has no support for specifying a bottle on the command-line.
        if wineprefix != nil
                wineprefix.sub!(/'/,'\\')
                file.puts("\t"+'export WINEPREFIX='+shellQuote(wineprefix))
        end
        if $wineArch
                file.puts("\t"+'export WINEARCH='+shellQuote($wineArch))
        end
        if wwineInfo['Wine'] =~ /^(pol|lutris)/i
                type = 'POL'
                if wwineInfo['Wine'] =~ /^lutris/
                        type = 'lutris'
                end
                path = getCustomwithParams(wwineInfo['Wine'].sub(/^(pol|lutris)[-:]?/i,''),nil,type,false,true)
                if path == nil
                        abort("getCustomwithParams returned nil wine. This is a bug")
                end
                if ENV['WINEDEBUG'] != nil
                        file.puts("\t"+"export WINEDEBUG="+shellQuote(ENV['WINEDEBUG']))
                end
                file.puts("\t"+'export WINESERVER='+shellQuote(path+'/bin/wineserver'))
                file.puts("\t"+'export WINELOADER='+shellQuote(path+'/bin/wine'))
                file.puts("\t"+'export WINEDLLPATH='+shellQuote(path+'/lib/wine'))
        elsif wwineInfo['Wine'] =~ /^proton/
                file.puts("\t"+'export STEAM_COMPAT_DATA_PATH='+shellQuote(ENV['STEAM_COMPAT_DATA_PATH']))
        elsif wwineInfo['Wine'] == 'wine' || wineprefix
                if ENV['WINEDEBUG'] != nil
                        file.puts("\t"+"export WINEDEBUG="+shellQuote(ENV['WINEDEBUG']))
                end
        end
        if $preloadGameMode
                file.puts("\t"+'export LD_PRELOAD="/usr/\$LIB/libgamemodeauto.so:$LD_PRELOAD"')
        end
        file.puts("\t"+"exec "+shellQuote(otherCommand).join(" ")+' "$@"')
        file.puts('fi')
        file.puts('echo "Launch script failed. No wwine found and wine command failed to exec."')
        file.puts('exit 1')

        file.chmod(0755)
        file.close

        puts('Wrote wrapper script to '+targetFile)
end