Skip to content

Instantly share code, notes, and snippets.

@mcorrigan
Last active July 14, 2022 02:15
Show Gist options
  • Save mcorrigan/4b8ff830122d1a31bf99ccc4bd1aeba4 to your computer and use it in GitHub Desktop.
Save mcorrigan/4b8ff830122d1a31bf99ccc4bd1aeba4 to your computer and use it in GitHub Desktop.
SteamVR Headless Mode Toggle Python Script
#!python
# coding: utf-8
# This is a simple script to quickly switch between HMD mode and headless mode with Vive in SteamVR
# It uses symlinks to ensure that SteamVR will continue to run and always preserves the original files,
# This should be able to be rerun following updates (assuming the configs haven't structurally changes
# along with anything else) -- symlinks require running with elevated priviledges
import sys, os, ctypes, time, json, shutil
from winreg import QueryValueEx, HKEY_CURRENT_USER, OpenKey
kdll = ctypes.windll.LoadLibrary("kernel32.dll")
steamVR_launch = r"steam://rungameid/250820"
steamVRRegEntry = r"SOFTWARE\Valve\Steam\Apps\250820"
steam_root = U"C:\\Program Files (x86)\\Steam"
steamvr_null_driver_settings = U"\\steamapps\\common\\SteamVR\\drivers\\null\\resources\\settings\\default.vrsettings"
steamvr_resource_settings = U"\\steamapps\\common\\SteamVR\\resources\\settings\\default.vrsettings"
def runAsAdmin(argv=None, debug=False):
shell32 = ctypes.windll.shell32
if argv is None and shell32.IsUserAnAdmin():
return True
if argv is None:
argv = sys.argv
if hasattr(sys, '_MEIPASS'):
# Support pyinstaller wrapped program.
arguments = map(str, argv[1:])
else:
arguments = map(str, argv)
argument_line = u' '.join(arguments)
executable = str(sys.executable)
if debug:
print ('Command line: ', executable, argument_line)
ret = shell32.ShellExecuteW(None, u"runas", executable, argument_line, None, 1)
if int(ret) <= 32:
return False
return None
if __name__ == '__main__':
ret = runAsAdmin()
if ret is True:
# we have elevated permissions
# verify steam is installed installation
if not os.path.exists(steam_root):
print(f'\n Steam does not seem to be installed ({steam_root})\n')
time.sleep(2)
exit
# check if steamvr is running, if so, wait for them to close steamvr
isRunning = False
with OpenKey(HKEY_CURRENT_USER, steamVRRegEntry) as key:
(value, type) = QueryValueEx(key, 'Running')
isRunning = value == 1 # Running: 1 yes, 0 no
if isRunning:
print(f'\n Please exit SteamVR to continue... ')
while isRunning:
with OpenKey(HKEY_CURRENT_USER, steamVRRegEntry) as key:
(value, type) = QueryValueEx(key, 'Running')
isRunning = value == 1
time.sleep(1)
print(f'ready.')
# FIRST FILE
sourceFile = f"{steam_root}{steamvr_null_driver_settings}"
if not os.path.islink(sourceFile):
# if the symlink was not there, we may are being first-time run or there was an update
if os.path.exists(f"{sourceFile}.headless"):
os.rename(f"{sourceFile}.headless", f"{sourceFile}.headless_{time.time()}")
# create a copy of the file
shutil.copy(sourceFile, f"{sourceFile}.headless")
# edit the copy to be how we want it
# In the driver_null object, change the enable property value from false to true.
file_data = {}
with open(f"{sourceFile}.headless", 'r+') as f:
data = json.load(f)
null_body = data['driver_null']
null_body['enable'] = True
data['driver_null'] = null_body
json.dump(data, f, indent=4)
f.truncate() # remove remaining part
if os.path.exists(f"{sourceFile}.original"):
os.rename(f"{sourceFile}.original", f"{sourceFile}.original_{time.time()}")
# rename the original file if it does not already exist
os.rename (sourceFile, f"{sourceFile}.original")
# symlink the original
kdll.CreateSymbolicLinkW(sourceFile, f"{sourceFile}.original", 0)
# SECOND FILE
sourceFile = f"{steam_root}{steamvr_resource_settings}"
if not os.path.islink(sourceFile):
# if the symlink was not there, we may are being first-time run or there was an update
if os.path.exists(f"{sourceFile}.headless"):
os.rename(f"{sourceFile}.headless", f"{sourceFile}.headless_{time.time()}")
shutil.copy(sourceFile, f"{sourceFile}.headless")
# edit the copy to be how we want it
# In the driver_null object, change the enable property value from false to true.
file_data = {}
with open(f"{sourceFile}.headless", 'r+') as f:
data = json.load(f)
section_body = data['steamvr']
section_body['requireHmd'] = False
section_body['forcedDriver'] = "null"
section_body['activateMultipleDrivers'] = True
data['steamvr'] = section_body
json.dump(data, f, indent=4)
f.truncate() # remove remaining part
if os.path.exists(f"{sourceFile}.original"):
os.rename(f"{sourceFile}.original", f"{sourceFile}.original_{time.time()}")
# rename the original file if it does not already exist
os.rename (sourceFile, f"{sourceFile}.original")
# symlink the original
kdll.CreateSymbolicLinkW(sourceFile, f"{sourceFile}.original", 0)
# which way are we switching? headless => HMD, HMD => headless
driverSrcFile = f"{steam_root}{steamvr_null_driver_settings}"
resourceSrcFile = f"{steam_root}{steamvr_resource_settings}"
if os.readlink(driverSrcFile).endswith('.original'):
print ('\n Switching to HEADLESS mode...\n')
os.remove(driverSrcFile)
kdll.CreateSymbolicLinkW(driverSrcFile, f"{driverSrcFile}.headless", 0)
os.remove(resourceSrcFile)
kdll.CreateSymbolicLinkW(resourceSrcFile, f"{resourceSrcFile}.headless", 0)
else:
print ('\n Switching to HMD mode...\n')
os.remove(driverSrcFile)
kdll.CreateSymbolicLinkW(driverSrcFile, f"{driverSrcFile}.original", 0)
os.remove(resourceSrcFile)
kdll.CreateSymbolicLinkW(resourceSrcFile, f"{resourceSrcFile}.original", 0)
print('Complete.\n')
print ("(You may be required to calibrate your setup again)\n")
print('Restarting SteamVR...\n')
os.system(f"start {steamVR_launch}")
time.sleep(10)
elif ret is None:
# allowed to enter elevated priviledge mode
pass
else:
# note elevated and elevation request denied
print ('\n Elevated permissions are required for this tool for work properly.\n')
# Notes:
# symlinks -- https://stackoverflow.com/questions/1447575/symlinks-on-windows
# steam registry -- https://steamcommunity.com/discussions/forum/1/1621726179573666840/
# files to change -- https://docs.unrealengine.com/4.26/en-US/AnimatingObjects/SkeletalMeshAnimation/LiveLinkPlugin/Livelinkxr/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment