feat: add OpenVR service and autostart with SteamVR option

This commit is contained in:
Fracas 2024-12-12 19:51:56 -06:00
parent 52e7ee0fd8
commit 549217c2e4
7 changed files with 823 additions and 558 deletions

View File

@ -0,0 +1,100 @@
import openvr
import os
import sys
import json
from logging import getLogger, ERROR, WARN
from config import EyeTrackConfig
from eye import EyeId
from colorama import Fore
import PySimpleGUI as sg
class OpenVRService:
appKey: str = "etvr.etvrapp"
manifest = {
"source": "builtin",
"applications": [
{
"app_key": appKey,
"launch_type": "binary",
"binary_path_windows": sys.executable,
"is_dashboard_overlay": True,
"strings": {
"en_us": {
"name": "EyeTrackVR",
"description": "EyeTrackVR allow translating a camera feed into face tracking datas."
}
}
}
]
}
manifestPath: str = os.path.abspath("app.vrmanifest")
# Write openvr manifest file
def write_manifest() -> None:
with open(OpenVRService.manifestPath, 'w') as f:
json.dump(OpenVRService.manifest, f)
def __init__(self) -> None:
self.is_initialized: bool = False
self.logger = getLogger(__name__)
# Initialize the openvr connection if it wasn't already
def initialize(self) -> bool:
if self.is_initialized:
return True
try:
openvr.init(openvr.VRApplication_Background)
return True
except openvr.error_code.InitError_Init_NoServerForBackgroundApp:
return False
def set_autostart(self, enabled: bool) -> bool:
if enabled:
if not self.initialize():
return False
self.app = openvr.IVRApplications()
# Write the manifest dynamically depending on current executable path
OpenVRService.write_manifest()
try:
self.app.addApplicationManifest(OpenVRService.manifestPath)
self.app.setApplicationAutoLaunch(OpenVRService.appKey, True)
except openvr.error_code.ApplicationError_UnknownApplication:
self.logger.log(ERROR, f"{Fore.RED}[ERROR] Could not register app in SteamVR")
return False
else:
if os.path.exists(OpenVRService.manifestPath):
if self.initialize():
try:
self.app.removeApplicationManifest(OpenVRService.manifestPath)
except openvr.error_code.ApplicationError_UnknownApplication:
pass
# App won't autostart if the manifest doesn't exit anymore when SteamVR starts.
os.remove(OpenVRService.manifestPath)
return True
# Called when general settings get updated in case autostart option is modified
def on_config_update(self, data):
if "gui_openvr_autostart" in data:
if not self.set_autostart(data["gui_openvr_autostart"]):
# Uncheck the autostart option if we failed to toggle it on
self.window[f"-OPENVRAUTOSTART{EyeId.SETTINGS}-"].update(False)
self.logger.log(WARN, f"{Fore.YELLOW}[WARN] SteamVR must be running to turn on auto start!")
sg.popup_ok(
"SteamVR must be running to turn on auto start!",
title="Warning",
text_color="#ffae42",
background_color="#292929"
)
openvr_service = OpenVRService()
if EyeTrackConfig.load().settings.gui_openvr_autostart:
# Try to initialize OpenVR when app start with autostart enabled
# Allow the app to be closed when SteamVR closes
openvr_service.initialize()

View File

@ -214,6 +214,8 @@ class EyeTrackSettingsConfig(BaseModel):
gui_OutputMultiplier: float = 1
gui_use_module: bool = False
gui_openvr_autostart: bool = False
class EyeTrackConfig(BaseModel):
version: int = 1

View File

@ -38,6 +38,7 @@ from settings.algo_settings_widget import AlgoSettingsWidget
from osc.osc import OSCManager
from osc.OSCMessage import OSCMessage
from utils.misc_utils import is_nt, resource_path
from OVR.OpenVRService import openvr_service
import cv2
import numpy as np
import uuid
@ -283,6 +284,7 @@ def main():
config.register_listener_callback(osc_manager.update)
config.register_listener_callback(eyes[0].on_config_update)
config.register_listener_callback(eyes[1].on_config_update)
config.register_listener_callback(openvr_service.on_config_update)
osc_manager.register_listeners(
config.settings.gui_osc_recenter_address,
@ -332,7 +334,9 @@ def main():
# First off, check for any events from the GUI
window = create_window(config, settings, eyes)
# Allow openvr service to access the windows to dynamically update the settings (uncheck autostart box)
openvr_service.window = window
while True:
event, values = window.read(timeout=tint) # this higher timeout saves some cpu usage

View File

@ -1,6 +1,7 @@
# -*- mode: python ; coding: utf-8 -*-
import sys ; sys.setrecursionlimit(sys.getrecursionlimit() * 5)
import openvr
block_cipher = None
@ -9,7 +10,10 @@ resources=[("Audio/*", "Audio"), ("Images/*", "Images/"), ("pye3d/refraction_mod
a = Analysis(
['eyetrackapp.py'],
pathex=[],
binaries=[],
binaries=[
(os.path.abspath(openvr.__file__ + "\\..\\libopenvr_api_32.dll"), "openvr"),
(os.path.abspath(openvr.__file__ + "\\..\\libopenvr_api_64.dll"), "openvr"),
],
datas=resources,
hiddenimports=['cv2', 'numpy', 'PySimpleGui', 'pkg_resources.extern'],
hookspath=[],

View File

@ -12,6 +12,7 @@ class GeneralSettingsValidationModel(BaseValidationModel):
gui_right_eye_dominant: bool
gui_left_eye_dominant: bool
gui_eye_dominant_diff_thresh: float
gui_openvr_autostart: bool
class GeneralSettingsModule(BaseSettingsModule):
@ -26,6 +27,7 @@ class GeneralSettingsModule(BaseSettingsModule):
self.gui_left_eye_dominant = f"-LEFTEYEDOMINANT{widget_id}-"
self.gui_right_eye_dominant = f"-RIGHTEYEDOMINANT{widget_id}-"
self.gui_update_check = f"-UPDATECHECK{widget_id}-"
self.gui_openvr_autostart = f"-OPENVRAUTOSTART{widget_id}-"
# gui_right_eye_dominant: bool = False
# gui_left_eye_dominant: bool = False
@ -69,6 +71,15 @@ class GeneralSettingsModule(BaseSettingsModule):
tooltip="Toggle update check on launch.",
),
],
[
sg.Checkbox(
"Start and stop with SteamVR",
default=self.config.gui_openvr_autostart,
key=self.gui_openvr_autostart,
background_color="#424042",
tooltip="Start the EyeTrackVR app when SteamVR starts, Stop the EyeTrackVRApp when SteamVR stops. Needs SteamVR running to be enabled",
),
],
[
sg.Text("Eye Falloff Settings:", background_color="#242224"),
],

1255
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@ colorama = "^0.4.6"
taskipy = "^1.10.4"
pytest = "^8.0.0"
pytest-cov = "^4.1.0"
openvr = "^2.5.101"
[tool.poetry.group.dev.dependencies]
black = "^22.10.0"