Repository: BnuuySolutions/PSVR2Toolkit
Branch: main
Commit: 6f057a1b1b04
Files: 67
Total size: 388.6 KB
Directory structure:
gitextract_hh_yerca/
├── .github/
│ └── workflows/
│ └── msbuild.yml
├── .gitignore
├── LICENSE
├── PSVR2Toolkit.sln
├── README.md
├── assets/
│ └── Icons.pdn
├── include/
│ └── psvr2_toolkit_capi.h
└── projects/
├── PSVR2Toolkit.App/
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── AssemblyInfo.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── PSVR2Toolkit.App.csproj
│ └── Resources/
│ └── .gitkeep
├── PSVR2Toolkit.IPC/
│ ├── IpcClient.cs
│ ├── IpcProtocol.cs
│ └── PSVR2Toolkit.IPC.csproj
├── UnifiedTelemetry.Client_stub/
│ ├── UnifiedTelemetry.Client_stub.vcxproj
│ ├── UnifiedTelemetry.Client_stub.vcxproj.filters
│ └── dllmain.cpp
├── UnifiedTelemetry.Model_stub/
│ ├── UnifiedTelemetry.Model_stub.vcxproj
│ ├── UnifiedTelemetry.Model_stub.vcxproj.filters
│ └── dllmain.cpp
├── UnifiedTelemetry.Service_stub/
│ ├── UnifiedTelemetry.Service_stub.vcxproj
│ ├── UnifiedTelemetry.Service_stub.vcxproj.filters
│ └── dllmain.cpp
├── libcustomshare/
│ ├── custom_share_manager.cpp
│ ├── custom_share_manager.h
│ ├── libcustomshare.vcxproj
│ └── libcustomshare.vcxproj.filters
├── psvr2_openvr_driver_ex/
│ ├── caesar_manager_hooks.cpp
│ ├── caesar_manager_hooks.h
│ ├── config.h
│ ├── device_provider_proxy.cpp
│ ├── device_provider_proxy.h
│ ├── driver_context_proxy.cpp
│ ├── driver_context_proxy.h
│ ├── driver_host_proxy.cpp
│ ├── driver_host_proxy.h
│ ├── eyelid_estimator.cpp
│ ├── eyelid_estimator.h
│ ├── hmd2_gaze.h
│ ├── hmd_device_hooks.cpp
│ ├── hmd_device_hooks.h
│ ├── hmd_driver_factory.cpp
│ ├── hmd_driver_loader.cpp
│ ├── hmd_driver_loader.h
│ ├── hmd_math.h
│ ├── hook_lib.h
│ ├── ipc_server.cpp
│ ├── ipc_server.h
│ ├── minhook/
│ │ ├── LICENSE.txt
│ │ └── include/
│ │ └── MinHook.h
│ ├── pad_trigger_effect.h
│ ├── psvr2_openvr_driver/
│ │ └── openvr/
│ │ ├── LICENSE
│ │ └── headers/
│ │ └── openvr_driver.h
│ ├── psvr2_openvr_driver_ex.vcxproj
│ ├── psvr2_openvr_driver_ex.vcxproj.filters
│ ├── trigger_effect_manager.cpp
│ ├── trigger_effect_manager.h
│ ├── usb_thread_gaze.cpp
│ ├── usb_thread_gaze.h
│ ├── usb_thread_hooks.cpp
│ ├── usb_thread_hooks.h
│ ├── util.h
│ └── vr_settings.h
└── shared/
└── ipc_protocol.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/msbuild.yml
================================================
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: MSBuild
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
env:
# Path to the solution file relative to the root of the project.
SOLUTION_FILE_PATH: .
# Configuration type to build.
# You can convert this to a build matrix if you need coverage of multiple configuration types.
# https://docs.github.com/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
BUILD_CONFIGURATION: ReleaseCI
# Directory for build artifacts
ARTIFACTS_DIR: ${{github.workspace}}/artifacts
permissions:
contents: read
jobs:
build:
runs-on: windows-2025-vs2026
steps:
- uses: actions/checkout@v4
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v1.0.2
- name: Restore NuGet packages
working-directory: ${{env.GITHUB_WORKSPACE}}
run: nuget restore ${{env.SOLUTION_FILE_PATH}}
- name: Build
working-directory: ${{env.GITHUB_WORKSPACE}}
# Add additional options to the MSBuild command line here (like platform or verbosity level).
# See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
run: msbuild /m /p:Configuration=${{env.BUILD_CONFIGURATION}} /p:OutDir=${{env.ARTIFACTS_DIR}} ${{env.SOLUTION_FILE_PATH}}
- name: Upload artifacts
id: upload-artifacts
uses: actions/upload-artifact@v4
with:
name: psvr2-toolkit-build
path: ${{env.ARTIFACTS_DIR}}
- id: optional_step_id
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: e5b00777-6877-43dd-8f53-89ac106cb9be
project-slug: PSVR2Toolkit
signing-policy-slug: test-signing
github-artifact-id: ${{ steps.upload-artifacts.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: ${{env.ARTIFACTS_DIR}}
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
# MinHook
!projects/psvr2_openvr_driver_ex/minhook/lib/*
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2026 Bnuuy Solutions
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: PSVR2Toolkit.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.12.35506.116
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psvr2_openvr_driver_ex", "projects\psvr2_openvr_driver_ex\psvr2_openvr_driver_ex.vcxproj", "{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnifiedTelemetry.Client_stub", "projects\UnifiedTelemetry.Client_stub\UnifiedTelemetry.Client_stub.vcxproj", "{A17F69A1-4CBD-4B92-BEAD-D331A378533A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnifiedTelemetry.Model_stub", "projects\UnifiedTelemetry.Model_stub\UnifiedTelemetry.Model_stub.vcxproj", "{479C51AE-D86B-433F-ADAA-DCDD1646300A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnifiedTelemetry.Service_stub", "projects\UnifiedTelemetry.Service_stub\UnifiedTelemetry.Service_stub.vcxproj", "{40ADD18D-A928-4ABF-BE63-4591F53A4801}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSVR2Toolkit.IPC", "projects\PSVR2Toolkit.IPC\PSVR2Toolkit.IPC.csproj", "{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSVR2Toolkit.App", "projects\PSVR2Toolkit.App\PSVR2Toolkit.App.csproj", "{8E6661D1-6A40-4949-93CC-2280909E2DB3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcustomshare", "projects\libcustomshare\libcustomshare.vcxproj", "{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
DebugCI|x64 = DebugCI|x64
Release|x64 = Release|x64
ReleaseCI|x64 = ReleaseCI|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.Debug|x64.ActiveCfg = Debug|x64
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.Debug|x64.Build.0 = Debug|x64
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.DebugCI|x64.ActiveCfg = DebugCI|x64
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.DebugCI|x64.Build.0 = DebugCI|x64
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.Release|x64.ActiveCfg = Release|x64
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.Release|x64.Build.0 = Release|x64
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.ReleaseCI|x64.ActiveCfg = ReleaseCI|x64
{B2A22051-5E4F-4471-A6B6-73DD9BE4AC49}.ReleaseCI|x64.Build.0 = ReleaseCI|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.Debug|x64.ActiveCfg = Debug|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.Debug|x64.Build.0 = Debug|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.DebugCI|x64.ActiveCfg = DebugCI|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.DebugCI|x64.Build.0 = DebugCI|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.Release|x64.ActiveCfg = Release|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.Release|x64.Build.0 = Release|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.ReleaseCI|x64.ActiveCfg = ReleaseCI|x64
{A17F69A1-4CBD-4B92-BEAD-D331A378533A}.ReleaseCI|x64.Build.0 = ReleaseCI|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.Debug|x64.ActiveCfg = Debug|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.Debug|x64.Build.0 = Debug|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.DebugCI|x64.ActiveCfg = DebugCI|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.DebugCI|x64.Build.0 = DebugCI|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.Release|x64.ActiveCfg = Release|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.Release|x64.Build.0 = Release|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.ReleaseCI|x64.ActiveCfg = ReleaseCI|x64
{479C51AE-D86B-433F-ADAA-DCDD1646300A}.ReleaseCI|x64.Build.0 = ReleaseCI|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.Debug|x64.ActiveCfg = Debug|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.Debug|x64.Build.0 = Debug|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.DebugCI|x64.ActiveCfg = DebugCI|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.DebugCI|x64.Build.0 = DebugCI|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.Release|x64.ActiveCfg = Release|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.Release|x64.Build.0 = Release|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.ReleaseCI|x64.ActiveCfg = ReleaseCI|x64
{40ADD18D-A928-4ABF-BE63-4591F53A4801}.ReleaseCI|x64.Build.0 = ReleaseCI|x64
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.Debug|x64.ActiveCfg = Debug|Any CPU
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.Debug|x64.Build.0 = Debug|Any CPU
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.DebugCI|x64.ActiveCfg = DebugCI|Any CPU
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.DebugCI|x64.Build.0 = DebugCI|Any CPU
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.Release|x64.ActiveCfg = Release|Any CPU
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.Release|x64.Build.0 = Release|Any CPU
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.ReleaseCI|x64.ActiveCfg = ReleaseCI|Any CPU
{48FCC06F-F55F-F3B4-0563-5CD83BA2FC85}.ReleaseCI|x64.Build.0 = ReleaseCI|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.Debug|x64.ActiveCfg = Debug|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.Debug|x64.Build.0 = Debug|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.DebugCI|x64.ActiveCfg = DebugCI|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.DebugCI|x64.Build.0 = DebugCI|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.Release|x64.ActiveCfg = Release|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.Release|x64.Build.0 = Release|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.ReleaseCI|x64.ActiveCfg = ReleaseCI|Any CPU
{8E6661D1-6A40-4949-93CC-2280909E2DB3}.ReleaseCI|x64.Build.0 = ReleaseCI|Any CPU
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.Debug|x64.ActiveCfg = Debug|x64
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.Debug|x64.Build.0 = Debug|x64
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.DebugCI|x64.ActiveCfg = Debug|x64
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.DebugCI|x64.Build.0 = Debug|x64
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.Release|x64.ActiveCfg = Release|x64
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.Release|x64.Build.0 = Release|x64
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.ReleaseCI|x64.ActiveCfg = Release|x64
{EABC1D23-54F9-455F-9FB4-C0A7A01CA592}.ReleaseCI|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4519F772-725B-47BF-BD00-0EA336012E8C}
EndGlobalSection
EndGlobal
================================================
FILE: README.md
================================================
> [!WARNING]
> If you have paid for PlayStation VR2 Toolkit, you have been scammed and you should immediately request a refund. PlayStation VR2 Toolkit is entirely free and **is intended for NON-COMMERCIAL use only**, we do not attempt to profit off of it. Additionally, eye tracking data from PlayStation VR2 Toolkit **must not** be used in commercial environments and/or for commercial purposes. \
> \
> PlayStation VR2 Toolkit is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
> [!NOTE]
> Bnuuy Solutions and PlayStation VR2 Toolkit is not affiliated with Sony Interactive Entertainment, our project is not official or endorsed by Sony Interactive Entertainment in any way. \
> \
> Sony, Sony Interactive Entertainment and PS VR2/PlayStation VR2 are trademarks or registered trademarks of Sony Interactive Entertainment LLC in the United States of America and elsewhere.
PlayStation VR2 Toolkit
[](https://opensource.org/licenses/MIT) [](https://discord.gg/jym8CgJPF3)
Unofficial modification for the official PlayStation VR2 driver/app, which aims to improve your PlayStation VR2 experience on PC.
## Features
- Eye tracking\*
- Improved controller prediction
- Improved controller haptics (w/ PCM haptics) (coming soon, see `custom-controller-sync` branch and [Custom Controller LED Sync and Enhanced Haptics
#24](https://github.com/BnuuySolutions/PSVR2Toolkit/pull/24))
- Adaptive triggers
For developers, we also have our own API library, which allows you to take full advantage of these features.
\* Eye tracking calibration is currently not available yet, we have a few things to work out before we are ready to ship this feature!
## Installation Guide
1.) Open Steam, go to the PS VR2 app, click on the cog wheel, and go to "Manage -> Browse local files". (If you are using a copy of the PS VR2 app not installed by Steam, go to that instead.)
2.) Inside the newly opened file explorer, go into "SteamVR_Plug-In", then "bin" and finally "win64".
3.) Rename "driver_playstation_vr2.dll" to "driver_playstation_vr2_orig.dll" (**IT MUST BE CALLED "driver_playstation_vr2_orig.dll", DO NOT RENAME IT TO ANYTHING ELSE, IT MUST BE EXACTLY THAT**)
4.) Download the "driver_playstation_vr2.dll" attached in a release, and copy/move it into the same folder where "driver_playstation_vr2_orig.dll" is at.
5.) Your "win64" directory should now have 2 DLL files inside it, "driver_playstation_vr2.dll" and "driver_playstation_vr2_orig.dll". If you do not have both of those files, you fucked something up.
6.) Enjoy your new features, please give us feedback in our [Discord](https://discord.gg/jym8CgJPF3).
# Contact
Have any legal complaints or questions?
- Email: `wdh at bnuuy.solutions`
- Discord: `notahopper`
================================================
FILE: include/psvr2_toolkit_capi.h
================================================
#pragma once
#include
#include
#define TRIGGER_EFFECT_CONTROL_POINT_NUM 10
enum EResultType {
Result_None = 0,
Result_NotInitialized = 1,
Result_AlreadyInitialized = 2,
};
enum EVRControllerType {
VRController_Left = 0,
VRController_Right = 1,
VRController_Both = 2,
};
#ifdef __cplusplus
extern "C" {
#endif
// Initialize the PS VR2 Toolkit CAPI library. You must call this function EXACTLY ONCE
// at the beginning of your program.
EResultType WINAPI psvr2_toolkit_init();
EResultType WINAPI psvr2_toolkit_set_trigger_effect_off(EVRControllerType controllerType);
EResultType WINAPI psvr2_toolkit_set_trigger_effect_feedback(EVRControllerType controllerType, uint8_t position, uint8_t strength);
EResultType WINAPI psvr2_toolkit_set_trigger_effect_weapon(EVRControllerType controllerType, uint8_t startPosition, uint8_t endPosition, uint8_t strength);
EResultType WINAPI psvr2_toolkit_set_trigger_effect_vibration(EVRControllerType controllerType, uint8_t position, uint8_t amplitude, uint8_t frequency);
EResultType WINAPI psvr2_toolkit_set_trigger_effect_multiple_position_feedback(EVRControllerType controllerType, uint8_t strength[TRIGGER_EFFECT_CONTROL_POINT_NUM]);
EResultType WINAPI psvr2_toolkit_set_trigger_effect_slope_feedback(EVRControllerType controllerType, uint8_t startPosition, uint8_t endPosition, uint8_t startStrength, uint8_t endStrength);
EResultType WINAPI psvr2_toolkit_set_trigger_effect_multiple_position_vibration(EVRControllerType controllerType, uint8_t frequency, uint8_t amplitude[TRIGGER_EFFECT_CONTROL_POINT_NUM]);
EResultType WINAPI psvr2_toolkit_get_gaze_state();
/* Private functions */
EResultType WINAPI psvr2_toolkit_private_set_gaze_enabled_eye();
EResultType WINAPI psvr2_toolkit_private_start_gaze_calibration();
EResultType WINAPI psvr2_toolkit_private_stop_gaze_calibration();
EResultType WINAPI psvr2_toolkit_private_set_gaze_calibration_point();
/* Unknown gaze functions */
EResultType WINAPI psvr2_toolkit_private_gaze_0x08a964bcd9e0a1f4();
EResultType WINAPI psvr2_toolkit_private_gaze_0x913203ac32f332fd();
#ifdef __cplusplus
}
#endif
================================================
FILE: projects/PSVR2Toolkit.App/App.xaml
================================================
================================================
FILE: projects/PSVR2Toolkit.App/App.xaml.cs
================================================
using System.Windows;
namespace PSVR2Toolkit.App;
///
/// Interaction logic for App.xaml
///
public partial class App : Application
{
}
================================================
FILE: projects/PSVR2Toolkit.App/AssemblyInfo.cs
================================================
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
================================================
FILE: projects/PSVR2Toolkit.App/MainWindow.xaml
================================================
================================================
FILE: projects/PSVR2Toolkit.App/MainWindow.xaml.cs
================================================
using System.Windows;
namespace PSVR2Toolkit.App;
///
/// Interaction logic for MainWindow.xaml
///
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
================================================
FILE: projects/PSVR2Toolkit.App/PSVR2Toolkit.App.csproj
================================================
WinExenet47211enabletrueDebug;Release;ReleaseCI;DebugCI
================================================
FILE: projects/PSVR2Toolkit.App/Resources/.gitkeep
================================================
================================================
FILE: projects/PSVR2Toolkit.IPC/IpcClient.cs
================================================
using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace PSVR2Toolkit.CAPI {
public class IpcClient {
private const ushort IPC_SERVER_PORT = 3364;
private const ushort k_unIpcVersion = 2;
private static IpcClient m_pInstance;
private bool m_running = false;
private TcpClient? m_client;
private NetworkStream? m_stream;
private Thread? m_receiveThread;
private readonly object m_gazeStateLock = new object();
private TaskCompletionSource? m_gazeTask;
private CancellationTokenSource m_forceShutdownToken;
private ushort m_serverIpcVersion = k_unIpcVersion;
private int m_gazePumpPeriodMs = 8; // 120Hz
private CommandDataServerGazeDataResult2? m_lastGazeState = null;
public static IpcClient Instance() {
if ( m_pInstance == null ) {
m_pInstance = new IpcClient();
}
return m_pInstance;
}
public bool Start() {
if ( m_running ) {
return false;
}
try {
m_client = new TcpClient();
m_client.Connect("127.0.0.1", IPC_SERVER_PORT);
if ( m_client.Connected ) {
m_stream = m_client.GetStream();
m_running = true;
m_forceShutdownToken = new CancellationTokenSource();
m_receiveThread = new Thread(() => ReceiveLoop(m_forceShutdownToken.Token));
m_receiveThread.Start();
return true;
}
return false;
} catch ( SocketException ex ) {
Console.WriteLine($"[IPC_CLIENT] Connection failed. LastError = {ex.SocketErrorCode}");
return false;
}
}
public void Stop() {
if ( !m_running ) {
return;
}
m_running = false;
m_forceShutdownToken.Cancel();
lock ( m_gazeStateLock ) {
m_gazeTask?.TrySetCanceled();
m_gazeTask = null;
}
try {
m_stream?.Close();
m_client?.Close();
} catch { }
if ( m_receiveThread != null && m_receiveThread.IsAlive ) {
if ( !m_receiveThread.Join(2000) ) {
m_receiveThread.Interrupt();
}
}
m_stream?.Dispose();
m_client?.Dispose();
m_forceShutdownToken.Dispose();
}
private void ReceiveLoop(CancellationToken token) {
byte[] buffer = new byte[1024];
try {
var clientSocket = m_client!.Client;
m_stream!.ReadTimeout = 1; // make the underlying stream non-blocking
CommandDataClientRequestHandshake clientHandshakeRequest = new CommandDataClientRequestHandshake() {
ipcVersion = k_unIpcVersion,
processId = ( uint ) Process.GetCurrentProcess().Id
};
SendIpcCommand(ECommandType.ClientRequestHandshake, clientHandshakeRequest);
var sw = Stopwatch.StartNew();
long nextPumpMs = sw.ElapsedMilliseconds;
while ( m_running && !token.IsCancellationRequested ) {
// query gaze state every so often
var now = sw.ElapsedMilliseconds;
if ( now >= nextPumpMs ) {
SendIpcCommand(ECommandType.ClientRequestGazeData);
nextPumpMs = now + m_gazePumpPeriodMs;
}
bool readable = clientSocket.Poll(1000 /* 1ms */, SelectMode.SelectRead);
if ( readable && clientSocket.Available > 0 ) {
int available = clientSocket.Available;
if ( available > buffer.Length ) {
buffer = new byte[Math.Max(available, buffer.Length * 2)];
}
int bytesRead = m_stream.Read(buffer, 0, Math.Min(buffer.Length, available));
if ( bytesRead <= 0 ) {
Console.WriteLine("[IPC_CLIENT] Disconnected from server.");
break;
}
if ( bytesRead < Marshal.SizeOf() ) {
Console.WriteLine("[IPC_CLIENT] Received invalid command header size.");
continue;
}
HandleIpcCommand(buffer, bytesRead);
continue;
}
Thread.Sleep(1);
}
} catch ( OperationCanceledException ) {
// nothing special, this is from shutdown most likely
} catch ( Exception ex ) {
if ( m_running ) {
Console.WriteLine($"[IPC_CLIENT] Error in receive loop: {ex.Message}");
}
}
}
private GazeEyeResult2 UpgradeGazeEyeResult(GazeEyeResult eye)
{
return new GazeEyeResult2
{
isGazeOriginValid = eye.isGazeOriginValid,
gazeOriginMm = eye.gazeOriginMm,
isGazeDirValid = eye.isGazeDirValid,
gazeDirNorm = eye.gazeDirNorm,
isPupilDiaValid = eye.isPupilDiaValid,
pupilDiaMm = eye.pupilDiaMm,
isBlinkValid = eye.isBlinkValid,
blink = eye.blink,
isOpenEnabled = false,
open = 0f
};
}
private CommandDataServerGazeDataResult2 UpgradeGazeDataResult(CommandDataServerGazeDataResult result)
{
return new CommandDataServerGazeDataResult2
{
leftEye = UpgradeGazeEyeResult(result.leftEye),
rightEye = UpgradeGazeEyeResult(result.rightEye)
};
}
private void HandleIpcCommand(byte[] pBuffer, int bytesReceived) {
CommandHeader header = ByteArrayToStructure(pBuffer, 0);
switch ( header.type ) {
case ECommandType.ServerPong: {
Console.WriteLine("[IPC_CLIENT] Received Pong from server.");
break;
}
case ECommandType.ServerHandshakeResult: {
if ( header.dataLen == Marshal.SizeOf() ) {
CommandDataServerHandshakeResult response = ByteArrayToStructure(pBuffer, Marshal.SizeOf());
m_serverIpcVersion = response.ipcVersion;
switch ( response.result ) {
case EHandshakeResult.Success: {
Console.WriteLine("[IPC_CLIENT] Handshake successful!");
break;
}
case EHandshakeResult.Failed: {
Console.WriteLine("[IPC_CLIENT] Handshake failed!");
break;
}
case EHandshakeResult.Outdated: {
Console.WriteLine($"[IPC_CLIENT] Handshake failed with reason: Outdated client. Please upgrade to an IPC version of {response.ipcVersion}");
break;
}
}
}
break;
}
case ECommandType.ServerGazeDataResult: {
if ( m_serverIpcVersion == 1 ) {
if ( header.dataLen == Marshal.SizeOf() ) {
CommandDataServerGazeDataResult response = ByteArrayToStructure(pBuffer, Marshal.SizeOf());
m_lastGazeState = UpgradeGazeDataResult(response);
}
} else {
if ( header.dataLen == Marshal.SizeOf() ) {
CommandDataServerGazeDataResult2 response = ByteArrayToStructure(pBuffer, Marshal.SizeOf());
m_lastGazeState = response;
}
}
break;
}
}
}
private void SendIpcCommand(ECommandType type, T data = default) where T : struct {
if ( !m_running )
return;
int dataLen = data.Equals(default(T)) ? 0 : Marshal.SizeOf();
int bufferLen = Marshal.SizeOf() + dataLen;
byte[] buffer = new byte[bufferLen];
CommandHeader header = new CommandHeader
{
type = type,
dataLen = dataLen
};
IntPtr headerPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
Marshal.StructureToPtr(header, headerPtr, false);
Marshal.Copy(headerPtr, buffer, 0, Marshal.SizeOf());
Marshal.FreeHGlobal(headerPtr);
if ( dataLen > 0 ) {
IntPtr dataPtr = Marshal.AllocHGlobal(dataLen);
Marshal.StructureToPtr(data, dataPtr, false);
Marshal.Copy(dataPtr, buffer, Marshal.SizeOf(), dataLen);
Marshal.FreeHGlobal(dataPtr);
}
m_stream.Write(buffer, 0, buffer.Length);
}
// no data
private void SendIpcCommand(ECommandType type) {
if ( !m_running )
return;
int bufferLen = Marshal.SizeOf();
byte[] buffer = new byte[bufferLen];
CommandHeader header = new CommandHeader
{
type = type,
dataLen = 0
};
IntPtr headerPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
Marshal.StructureToPtr(header, headerPtr, false);
Marshal.Copy(headerPtr, buffer, 0, Marshal.SizeOf());
Marshal.FreeHGlobal(headerPtr);
m_stream.Write(buffer, 0, buffer.Length);
}
private T ByteArrayToStructure(byte[] bytes, int offset) where T : struct {
int size = Marshal.SizeOf();
if ( size > bytes.Length - offset ) {
throw new ArgumentException("Byte array is too small to contain the structure.");
}
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, offset, ptr, size);
T structure = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return structure;
}
public CommandDataServerGazeDataResult2 RequestEyeTrackingData() {
if ( !m_running ) {
return new CommandDataServerGazeDataResult2();
}
return m_lastGazeState ?? new CommandDataServerGazeDataResult2();
}
public void TriggerEffectDisable(EVRControllerType controllerType) {
if ( !m_running ) {
return;
}
CommandDataClientTriggerEffectOff effectOff = new CommandDataClientTriggerEffectOff() {
controllerType = controllerType
};
SendIpcCommand(ECommandType.ClientTriggerEffectOff, effectOff);
}
public void TriggerEffectFeedback(EVRControllerType controllerType, byte position, byte strength) {
if ( !m_running ) {
return;
}
CommandDataClientTriggerEffectFeedback effectFeedback = new CommandDataClientTriggerEffectFeedback() {
controllerType = controllerType,
position = position,
strength = strength,
};
SendIpcCommand(ECommandType.ClientTriggerEffectFeedback, effectFeedback);
}
public void TriggerEffectWeapon(EVRControllerType controllerType, byte startPosition, byte endPosition, byte strength) {
if ( !m_running ) {
return;
}
CommandDataClientTriggerEffectWeapon effectWeapon = new CommandDataClientTriggerEffectWeapon() {
controllerType = controllerType,
startPosition = startPosition,
endPosition = endPosition,
strength = strength,
};
SendIpcCommand(ECommandType.ClientTriggerEffectWeapon, effectWeapon);
}
public void TriggerEffectVibration(EVRControllerType controllerType, byte position, byte amplitude, byte frequency) {
if ( !m_running ) {
return;
}
CommandDataClientTriggerEffectVibration effectVibration = new CommandDataClientTriggerEffectVibration() {
controllerType = controllerType,
position = position,
amplitude = amplitude,
frequency = frequency,
};
SendIpcCommand(ECommandType.ClientTriggerEffectVibration, effectVibration);
}
public void TriggerEffectMultiplePositionFeedback(EVRControllerType controllerType, byte[] strength) {
if ( !m_running ) {
return;
}
CommandDataClientTriggerEffectMultiplePositionFeedback effectVibration = new CommandDataClientTriggerEffectMultiplePositionFeedback() {
controllerType = controllerType,
strength = strength,
};
SendIpcCommand(ECommandType.ClientTriggerEffectMultiplePositionFeedback, effectVibration);
}
public void TriggerEffectSlopeFeedback(EVRControllerType controllerType, byte startPosition, byte endPosition, byte startStrength, byte endStrength) {
if ( !m_running ) {
return;
}
CommandDataClientTriggerEffectSlopeFeedback effectVibration = new CommandDataClientTriggerEffectSlopeFeedback() {
controllerType = controllerType,
startPosition = startPosition,
endPosition = endPosition,
startStrength = startStrength,
endStrength = endStrength,
};
SendIpcCommand(ECommandType.ClientTriggerEffectSlopeFeedback, effectVibration);
}
public void TriggerEffectMultiplePositionVibration(EVRControllerType controllerType, byte frequency, byte[] amplitude) {
if ( !m_running ) {
return;
}
CommandDataClientTriggerEffectMultiplePositionVibration effectVibration = new CommandDataClientTriggerEffectMultiplePositionVibration() {
controllerType = controllerType,
frequency = frequency,
amplitude = amplitude,
};
SendIpcCommand(ECommandType.ClientTriggerEffectMultiplePositionVibration, effectVibration);
}
}
}
================================================
FILE: projects/PSVR2Toolkit.IPC/IpcProtocol.cs
================================================
using System.Runtime.InteropServices;
namespace PSVR2Toolkit.CAPI {
public enum ECommandType : ushort {
ClientPing, // No command data.
ServerPong, // No command data.
ClientRequestHandshake, // CommandDataClientRequestHandshake
ServerHandshakeResult, // CommandDataServerHandshakeResult
ClientRequestGazeData, // No command data.
ServerGazeDataResult, // CommandDataServerGazeDataResult
ClientTriggerEffectOff, // CommandDataClientTriggerEffectOff
ClientTriggerEffectFeedback, // CommandDataClientTriggerEffectFeedback
ClientTriggerEffectWeapon, // CommandDataClientTriggerEffectWeapon
ClientTriggerEffectVibration, // CommandDataClientTriggerEffectVibration
ClientTriggerEffectMultiplePositionFeedback, // CommandDataClientTriggerEffectMultiplePositionFeedback
ClientTriggerEffectSlopeFeedback, // CommandDataClientTriggerEffectSlopeFeedback
ClientTriggerEffectMultiplePositionVibration, // CommandDataClientTriggerEffectMultiplePositionVibration
};
public enum EHandshakeResult : byte {
Failed,
Success,
Outdated,
};
public enum EVRControllerType : byte {
Left,
Right,
Both,
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientRequestHandshake {
public ushort ipcVersion; // The IPC version this client is using.
public uint processId;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataServerHandshakeResult {
public EHandshakeResult result;
public ushort ipcVersion; // The IPC version the server is using.
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GazeVector3 {
public float x, y, z;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GazeEyeResult {
[MarshalAs(UnmanagedType.I1)]
public bool isGazeOriginValid;
public GazeVector3 gazeOriginMm;
[MarshalAs(UnmanagedType.I1)]
public bool isGazeDirValid;
public GazeVector3 gazeDirNorm;
[MarshalAs(UnmanagedType.I1)]
public bool isPupilDiaValid;
public float pupilDiaMm;
[MarshalAs(UnmanagedType.I1)]
public bool isBlinkValid;
[MarshalAs(UnmanagedType.I1)]
public bool blink;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GazeEyeResult2 {
[MarshalAs(UnmanagedType.I1)]
public bool isGazeOriginValid;
public GazeVector3 gazeOriginMm;
[MarshalAs(UnmanagedType.I1)]
public bool isGazeDirValid;
public GazeVector3 gazeDirNorm;
[MarshalAs(UnmanagedType.I1)]
public bool isPupilDiaValid;
public float pupilDiaMm;
[MarshalAs(UnmanagedType.I1)]
public bool isBlinkValid;
[MarshalAs(UnmanagedType.I1)]
public bool blink;
[MarshalAs(UnmanagedType.I1)]
public bool isOpenEnabled;
public float open;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataServerGazeDataResult {
public GazeEyeResult leftEye;
public GazeEyeResult rightEye;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataServerGazeDataResult2 {
public GazeEyeResult2 leftEye;
public GazeEyeResult2 rightEye;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandHeader {
public ECommandType type;
public int dataLen;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientTriggerEffectOff {
public EVRControllerType controllerType;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientTriggerEffectFeedback {
public EVRControllerType controllerType;
public byte position;
public byte strength;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientTriggerEffectWeapon {
public EVRControllerType controllerType;
public byte startPosition;
public byte endPosition;
public byte strength;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientTriggerEffectVibration {
public EVRControllerType controllerType;
public byte position;
public byte amplitude;
public byte frequency;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientTriggerEffectMultiplePositionFeedback {
public EVRControllerType controllerType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] strength;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientTriggerEffectSlopeFeedback {
public EVRControllerType controllerType;
public byte startPosition;
public byte endPosition;
public byte startStrength;
public byte endStrength;
};
[StructLayout(LayoutKind.Sequential)]
public struct CommandDataClientTriggerEffectMultiplePositionVibration {
public EVRControllerType controllerType;
public byte frequency;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] amplitude;
};
}
================================================
FILE: projects/PSVR2Toolkit.IPC/PSVR2Toolkit.IPC.csproj
================================================
netstandard2.011Debug;Release;ReleaseCI;DebugCI
================================================
FILE: projects/UnifiedTelemetry.Client_stub/UnifiedTelemetry.Client_stub.vcxproj
================================================
DebugCIx64Debugx64ReleaseCIx64Releasex6417.0Win32Proj{a17f69a1-4cbd-4b92-bead-d331a378533a}UnifiedTelemetryClient10.0DynamicLibrarytruev145UnicodeDynamicLibrarytruev145UnicodeDynamicLibraryfalsev145trueUnicodeDynamicLibraryfalsev145trueUnicodeUnifiedTelemetry.ClientUnifiedTelemetry.ClientUnifiedTelemetry.ClientUnifiedTelemetry.ClientLevel3true_DEBUG;UNIFIEDTELEMETRYCLIENT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17WindowstruefalseLevel3true_DEBUG;UNIFIEDTELEMETRYCLIENT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17WindowstruefalseLevel3truetruetrueNDEBUG;UNIFIEDTELEMETRYCLIENT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17Windowstruetruetruefalse/pdbaltpath:%_PDB% %(AdditionalOptions)copy /Y "$(TargetPath)" "$(SolutionDir)projects\PSVR2Toolkit.App\Resources\$(TargetFileName)"Level3truetruetrueNDEBUG;UNIFIEDTELEMETRYCLIENT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17Windowstruetruetruefalse/pdbaltpath:%_PDB% %(AdditionalOptions)copy /Y "$(TargetPath)" "$(SolutionDir)projects\PSVR2Toolkit.App\Resources\$(TargetFileName)"
================================================
FILE: projects/UnifiedTelemetry.Client_stub/UnifiedTelemetry.Client_stub.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx{93995380-89BD-4b04-88EB-625FBE52EBFB}h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsdSource Files
================================================
FILE: projects/UnifiedTelemetry.Client_stub/dllmain.cpp
================================================
extern "C" __declspec(dllexport) void utCreateClient() {}
extern "C" __declspec(dllexport) void utDestroyClient() {}
extern "C" __declspec(dllexport) void utInitClient() {}
extern "C" __declspec(dllexport) void utGetUserClockTimestamp() {}
extern "C" __declspec(dllexport) void utDispatch() {}
extern "C" __declspec(dllexport) void utUserDispatch() {}
extern "C" __declspec(dllexport) void utDispatchRecord() {}
extern "C" __declspec(dllexport) void utGetCommonPropertiesObject() {}
extern "C" __declspec(dllexport) void utCreateObject() {}
extern "C" __declspec(dllexport) void utApplyObjectToEvent() {}
extern "C" __declspec(dllexport) void utSetClientTransport() {}
================================================
FILE: projects/UnifiedTelemetry.Model_stub/UnifiedTelemetry.Model_stub.vcxproj
================================================
DebugCIx64Debugx64ReleaseCIx64Releasex6417.0Win32Proj{479C51AE-D86B-433F-ADAA-DCDD1646300A}UnifiedTelemetryModel10.0DynamicLibrarytruev145UnicodeDynamicLibrarytruev145UnicodeDynamicLibraryfalsev145trueUnicodeDynamicLibraryfalsev145trueUnicodeUnifiedTelemetry.ModelUnifiedTelemetry.ModelUnifiedTelemetry.ModelUnifiedTelemetry.ModelLevel3true_DEBUG;UNIFIEDTELEMETRYMODEL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17WindowstruefalseLevel3true_DEBUG;UNIFIEDTELEMETRYMODEL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17WindowstruefalseLevel3truetruetrueNDEBUG;UNIFIEDTELEMETRYMODEL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17Windowstruetruetruefalse/pdbaltpath:%_PDB% %(AdditionalOptions)copy /Y "$(TargetPath)" "$(SolutionDir)projects\PSVR2Toolkit.App\Resources\$(TargetFileName)"Level3truetruetrueNDEBUG;UNIFIEDTELEMETRYMODEL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17Windowstruetruetruefalse/pdbaltpath:%_PDB% %(AdditionalOptions)copy /Y "$(TargetPath)" "$(SolutionDir)projects\PSVR2Toolkit.App\Resources\$(TargetFileName)"
================================================
FILE: projects/UnifiedTelemetry.Model_stub/UnifiedTelemetry.Model_stub.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx{93995380-89BD-4b04-88EB-625FBE52EBFB}h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsdSource Files
================================================
FILE: projects/UnifiedTelemetry.Model_stub/dllmain.cpp
================================================
extern "C" __declspec(dllexport) void utSetString() {}
extern "C" __declspec(dllexport) void utSetNumber() {}
extern "C" __declspec(dllexport) void utSetFloat() {}
extern "C" __declspec(dllexport) void utSetBoolean() {}
extern "C" __declspec(dllexport) void utSetDate() {}
extern "C" __declspec(dllexport) void utSetCurrentDate() {}
extern "C" __declspec(dllexport) void utSetObject() {}
extern "C" __declspec(dllexport) void utRemoveProperty() {}
extern "C" __declspec(dllexport) void utCreateEvent() {}
extern "C" __declspec(dllexport) void utGetEventJson() {}
extern "C" __declspec(dllexport) void utDestroyEvent() {}
extern "C" __declspec(dllexport) void utCreateEventObject() {}
extern "C" __declspec(dllexport) void utCreateEventArray() {}
extern "C" __declspec(dllexport) void utPushString() {}
extern "C" __declspec(dllexport) void utPushInt() {}
extern "C" __declspec(dllexport) void utPushFloat() {}
extern "C" __declspec(dllexport) void utPushObject() {}
extern "C" __declspec(dllexport) void utCreateSchemaFilter() {}
extern "C" __declspec(dllexport) void utDestroySchemaFilter() {}
extern "C" __declspec(dllexport) void utFilterEvent() {}
extern "C" __declspec(dllexport) void utGetJsTime() {}
extern "C" __declspec(dllexport) void utGetTracingId() {}
extern "C" __declspec(dllexport) void utCreateEventFromJson() {}
extern "C" __declspec(dllexport) void utGetSbahnDispatchUrl() {}
================================================
FILE: projects/UnifiedTelemetry.Service_stub/UnifiedTelemetry.Service_stub.vcxproj
================================================
DebugCIx64Debugx64ReleaseCIx64Releasex6417.0Win32Proj{40ADD18D-A928-4ABF-BE63-4591F53A4801}UnifiedTelemetryService10.0DynamicLibrarytruev145UnicodeDynamicLibrarytruev145UnicodeDynamicLibraryfalsev145trueUnicodeDynamicLibraryfalsev145trueUnicodeUnifiedTelemetry.ServiceUnifiedTelemetry.ServiceUnifiedTelemetry.ServiceUnifiedTelemetry.ServiceLevel3true_DEBUG;UNIFIEDTELEMETRYSERVICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17WindowstruefalseLevel3true_DEBUG;UNIFIEDTELEMETRYSERVICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17WindowstruefalseLevel3truetruetrueNDEBUG;UNIFIEDTELEMETRYSERVICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17Windowstruetruetruefalse/pdbaltpath:%_PDB% %(AdditionalOptions)copy /Y "$(TargetPath)" "$(SolutionDir)projects\PSVR2Toolkit.App\Resources\$(TargetFileName)"Level3truetruetrueNDEBUG;UNIFIEDTELEMETRYSERVICE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)trueNotUsingpch.hstdcpp20stdc17Windowstruetruetruefalse/pdbaltpath:%_PDB% %(AdditionalOptions)copy /Y "$(TargetPath)" "$(SolutionDir)projects\PSVR2Toolkit.App\Resources\$(TargetFileName)"
================================================
FILE: projects/UnifiedTelemetry.Service_stub/UnifiedTelemetry.Service_stub.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx{93995380-89BD-4b04-88EB-625FBE52EBFB}h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsdSource Files
================================================
FILE: projects/UnifiedTelemetry.Service_stub/dllmain.cpp
================================================
extern "C" __declspec(dllexport) void utCreateService() {}
extern "C" __declspec(dllexport) void utDestroyService() {}
extern "C" __declspec(dllexport) void utServiceInit() {}
extern "C" __declspec(dllexport) void utGetTestConfig() {}
extern "C" __declspec(dllexport) void utSetTestConfig() {}
extern "C" __declspec(dllexport) void utServiceGetEventsInQueueLength() {}
extern "C" __declspec(dllexport) void utServiceSetDeviceWs1Setting() {}
extern "C" __declspec(dllexport) void utServiceSetUserInt() {}
extern "C" __declspec(dllexport) void utServiceSetUserString() {}
extern "C" __declspec(dllexport) void utServiceSetUserBoolean() {}
extern "C" __declspec(dllexport) void utServiceRemoveUser() {}
extern "C" __declspec(dllexport) void utGetServiceState() {}
extern "C" __declspec(dllexport) void utServiceStart() {}
extern "C" __declspec(dllexport) void utServiceStop() {}
extern "C" __declspec(dllexport) void utCreateStaticServiceTransport() {}
extern "C" __declspec(dllexport) void utApplyServiceConfigEventSchema() {}
extern "C" __declspec(dllexport) void utGetServiceConfig() {}
extern "C" __declspec(dllexport) void utSetServiceConfig() {}
extern "C" __declspec(dllexport) void utServiceSetLogCallback() {}
extern "C" __declspec(dllexport) void utServiceDispatchRecord() {}
extern "C" __declspec(dllexport) void utSetAuthToken() {}
extern "C" __declspec(dllexport) void utServicePushAdobeReportSuite() {}
extern "C" __declspec(dllexport) void utServiceGetCommonPropertiesObject() {}
================================================
FILE: projects/libcustomshare/custom_share_manager.cpp
================================================
#include "custom_share_manager.h"
CustomShareManager *CustomShareManager::m_pInstance = nullptr;
void CustomShareManager::createSingleton() {
m_initialized = true;
CustomShareManager *pInstance = m_pInstance;
if (!m_pInstance) {
pInstance = new CustomShareManager;
m_pInstance = pInstance;
}
pInstance->initialize();
}
CustomShareManager *CustomShareManager::getSingleton() {
CustomShareManager *pInstance = m_pInstance;
if (!m_pInstance) {
pInstance = new CustomShareManager;
m_pInstance = pInstance;
}
return pInstance;
}
void CustomShareManager::initialize() {
const char *shareNames[2][2] = {
{"CUSTOM_SHARE_VRT2_WIN_GAZE_EVT", "CUSTOM_SHARE_VRT2_WIN_GAZE_MTX"}
};
}
================================================
FILE: projects/libcustomshare/custom_share_manager.h
================================================
#pragma once
class CustomShareManager {
public:
static void createSingleton();
static CustomShareManager *getSingleton();
private:
static CustomShareManager *m_pInstance;
static bool m_initialized;
void initialize();
};
================================================
FILE: projects/libcustomshare/libcustomshare.vcxproj
================================================
Debugx64Releasex6417.0Win32Proj{eabc1d23-54f9-455f-9fb4-c0a7a01ca592}libcustomshare10.0StaticLibrarytruev145UnicodeStaticLibraryfalsev145trueUnicodeLevel3true_DEBUG;_LIB;%(PreprocessorDefinitions)trueNotUsingpch.htrueLevel3truetruetrueNDEBUG;_LIB;%(PreprocessorDefinitions)trueNotUsingpch.htrue
================================================
FILE: projects/libcustomshare/libcustomshare.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx{93995380-89BD-4b04-88EB-625FBE52EBFB}h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsdSource FilesHeader Files
================================================
FILE: projects/psvr2_openvr_driver_ex/caesar_manager_hooks.cpp
================================================
#include "caesar_manager_hooks.h"
#include "hmd_driver_loader.h"
#include "hook_lib.h"
#include "usb_thread_gaze.h"
#include "vr_settings.h"
#include "util.h"
namespace psvr2_toolkit {
void *(*Framework__Thread__start)(void *thisptr) = nullptr;
void *(*CaesarManager__initialize)(void *, void *, void *) = nullptr;
void *CaesarManager__initializeHook(void *thisptr, void *arg1, void *arg2) {
static CaesarUsbThreadGaze* pCaesarUsbThreadGaze = CaesarUsbThreadGaze::Instance();
void* result = CaesarManager__initialize(thisptr, arg1, arg2);
(*(void (__fastcall **)(__int64, __int64))(*(__int64 *)pCaesarUsbThreadGaze + 24LL))((__int64)pCaesarUsbThreadGaze, 0);
Framework__Thread__start(pCaesarUsbThreadGaze);
return result;
}
void (*CaesarManager__shutdown)(void *) = nullptr;
void CaesarManager__shutdownHook(void *thisptr) {
static CaesarUsbThreadGaze *pCaesarUsbThreadGaze = CaesarUsbThreadGaze::Instance();
(*(void(__fastcall **)(__int64))(*(__int64 *)pCaesarUsbThreadGaze + 16LL))((__int64)pCaesarUsbThreadGaze);
CaesarManager__shutdown(thisptr);
}
void CaesarManagerHooks::InstallHooks() {
static HmdDriverLoader *pHmdDriverLoader = HmdDriverLoader::Instance();
Framework__Thread__start = decltype(Framework__Thread__start)(pHmdDriverLoader->GetBaseAddress() + 0x16B660);
if (!VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_GAZE, SETTING_DISABLE_GAZE_DEFAULT_VALUE)) {
Util::DriverLog("Enabling PSVR2 gaze tracking...");
// CaesarManager::initialize
HookLib::InstallHook(reinterpret_cast(pHmdDriverLoader->GetBaseAddress() + 0x123130),
reinterpret_cast(CaesarManager__initializeHook),
reinterpret_cast(&CaesarManager__initialize));
// CaesarManager::shutdown
HookLib::InstallHook(reinterpret_cast(pHmdDriverLoader->GetBaseAddress() + 0x128320),
reinterpret_cast(CaesarManager__shutdownHook),
reinterpret_cast(&CaesarManager__shutdown));
}
}
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/caesar_manager_hooks.h
================================================
#pragma once
namespace psvr2_toolkit {
class CaesarManagerHooks {
public:
static void InstallHooks();
};
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/config.h
================================================
#pragma once
#define DRIVER_IS_STABLE false
#define DRIVER_IS_PRERELEASE true
#define DRIVER_IS_EXPERIMENTAL false
#define DRIVER_VERSION_MAJOR 0
#define DRIVER_VERSION_MINOR 2
#define DRIVER_VERSION_PATCH 1
#if DRIVER_IS_STABLE
#define DRIVER_VERSION_BRANCH "stable"
#elif DRIVER_IS_PRERELEASE
#define DRIVER_VERSION_BRANCH "prerelease"
#elif DRIVER_IS_EXPERIMENTAL
#define DRIVER_VERSION_BRANCH "experimental"
#else
#define DRIVER_VERSION_BRANCH "development"
#endif
// Whether or not we should mock/fake running the driver on Wine.
#define MOCK_IS_RUNNING_ON_WINE false
================================================
FILE: projects/psvr2_openvr_driver_ex/device_provider_proxy.cpp
================================================
#include "device_provider_proxy.h"
#include "config.h"
#include "caesar_manager_hooks.h"
#include "driver_context_proxy.h"
#include "hmd_device_hooks.h"
#include "hmd_driver_loader.h"
#include "hook_lib.h"
#include "ipc_server.h"
#include "trigger_effect_manager.h"
#include "usb_thread_hooks.h"
#include "util.h"
#include "vr_settings.h"
#include
using namespace psvr2_toolkit::ipc;
namespace psvr2_toolkit {
DeviceProviderProxy *DeviceProviderProxy::m_pInstance = nullptr;
DeviceProviderProxy::DeviceProviderProxy()
: m_initOnce(false)
, m_pDeviceProvider(nullptr)
{}
DeviceProviderProxy *DeviceProviderProxy::Instance() {
if (!m_pInstance) {
m_pInstance = new DeviceProviderProxy;
}
return m_pInstance;
}
void DeviceProviderProxy::SetDeviceProvider(vr::IServerTrackedDeviceProvider *pDeviceProvider) {
m_pDeviceProvider = pDeviceProvider;
}
vr::EVRInitError DeviceProviderProxy::Init(vr::IVRDriverContext *pDriverContext) {
#if _DEBUG
Sleep(8000);
#endif
VR_INIT_SERVER_DRIVER_CONTEXT(pDriverContext);
if (!m_initOnce) {
InitOnce();
m_initOnce = true;
}
IpcServer::Instance()->Start();
static DriverContextProxy *pDriverContextProxy = DriverContextProxy::Instance();
pDriverContextProxy->SetDriverContext(pDriverContext);
return m_pDeviceProvider->Init(pDriverContextProxy);
}
void DeviceProviderProxy::Cleanup() {
IpcServer::Instance()->Stop();
m_pDeviceProvider->Cleanup();
VR_CLEANUP_SERVER_DRIVER_CONTEXT();
}
const char *const *DeviceProviderProxy::GetInterfaceVersions() {
return m_pDeviceProvider->GetInterfaceVersions();
}
void DeviceProviderProxy::RunFrame() {
m_pDeviceProvider->RunFrame();
}
bool DeviceProviderProxy::ShouldBlockStandbyMode() {
return m_pDeviceProvider->ShouldBlockStandbyMode();
}
void DeviceProviderProxy::EnterStandby() {
m_pDeviceProvider->EnterStandby();
}
void DeviceProviderProxy::LeaveStandby() {
m_pDeviceProvider->LeaveStandby();
}
void DeviceProviderProxy::InitOnce() {
static bool isRunningOnWine = Util::IsRunningOnWine();
// Log ourselves here to show that we're proxied.
Util::DriverLog("PlayStation VR2 Toolkit - v{}.{}.{} [{}]", DRIVER_VERSION_MAJOR, DRIVER_VERSION_MINOR, DRIVER_VERSION_PATCH, DRIVER_VERSION_BRANCH);
#if DRIVER_IS_PRERELEASE
Util::DriverLog("You are using a pre-release build of PlayStation VR2 Toolkit, please report any issues that may occur to the developers!");
#elif DRIVER_IS_EXPERIMENTAL
Util::DriverLog("You are using an experimental build of PlayStation VR2 Toolkit, please report any issues that may occur to the developers!");
#endif
if (!HookLib::Initialize()) {
MessageBoxW(nullptr, L"MinHook initialization failed, please report this to the developers!", L"PlayStation VR2 Toolkit (DriverEx)", MB_ICONERROR | MB_OK);
}
if (isRunningOnWine) {
Util::DriverLog("PlayStation VR2 Toolkit has detected itself running on Wine, compatibility patches will be applied.");
}
InitPatches();
InitSystems();
}
void DeviceProviderProxy::InitPatches() {
static HmdDriverLoader *pHmdDriverLoader = HmdDriverLoader::Instance();
static bool isRunningOnWine = Util::IsRunningOnWine();
// Remove signature checks.
INSTALL_STUB_RET0(reinterpret_cast(pHmdDriverLoader->GetBaseAddress() + 0x134FF0)); // VrDialogManager::VerifyLibrary
INSTALL_STUB(reinterpret_cast(pHmdDriverLoader->GetBaseAddress() + 0x12F830)); // VrDialogManager::CreateDashboardProcess
INSTALL_STUB(reinterpret_cast(pHmdDriverLoader->GetBaseAddress() + 0x130020)); // VrDialogManager::CreateDialogProcess
CaesarManagerHooks::InstallHooks();
HmdDeviceHooks::InstallHooks();
UsbThreadHooks::InstallHooks();
}
void DeviceProviderProxy::InitSystems() {
IpcServer::Instance()->Initialize();
TriggerEffectManager::Instance()->Initialize();
}
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/device_provider_proxy.h
================================================
#pragma once
#include
namespace psvr2_toolkit {
class DeviceProviderProxy : public vr::IServerTrackedDeviceProvider {
public:
DeviceProviderProxy();
static DeviceProviderProxy *Instance();
void SetDeviceProvider(vr::IServerTrackedDeviceProvider *pDeviceProvider);
/** IServerTrackedDeviceProvider **/
vr::EVRInitError Init(vr::IVRDriverContext *pDriverContext) override;
void Cleanup() override;
const char *const *GetInterfaceVersions() override;
void RunFrame() override;
bool ShouldBlockStandbyMode() override;
void EnterStandby() override;
void LeaveStandby() override;
private:
static DeviceProviderProxy *m_pInstance;
bool m_initOnce;
vr::IServerTrackedDeviceProvider *m_pDeviceProvider;
void InitOnce();
void InitPatches();
void InitSystems();
};
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/driver_context_proxy.cpp
================================================
#include "driver_context_proxy.h"
#include "driver_host_proxy.h"
namespace psvr2_toolkit {
DriverContextProxy *DriverContextProxy::m_pInstance = nullptr;
DriverContextProxy::DriverContextProxy()
: m_pDriverContext(nullptr)
{}
DriverContextProxy *DriverContextProxy::Instance() {
if (!m_pInstance) {
m_pInstance = new DriverContextProxy;
}
return m_pInstance;
}
void DriverContextProxy::SetDriverContext(vr::IVRDriverContext *pDriverContext) {
m_pDriverContext = pDriverContext;
}
void *DriverContextProxy::GetGenericInterface(const char *pchInterfaceVersion, vr::EVRInitError *peError) {
void *result = m_pDriverContext->GetGenericInterface(pchInterfaceVersion, peError);
// Depends on our OpenVR driver SDK version matching the one inside the PS VR2 driver.
if (strcmp(vr::IVRServerDriverHost_Version, pchInterfaceVersion) == 0) {
static DriverHostProxy *pDriverHostProxy = DriverHostProxy::Instance();
pDriverHostProxy->SetDriverHost(static_cast(result));
return pDriverHostProxy;
}
return result;
}
vr::DriverHandle_t DriverContextProxy::GetDriverHandle() {
return m_pDriverContext->GetDriverHandle();
}
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/driver_context_proxy.h
================================================
#pragma once
#include
namespace psvr2_toolkit {
class DriverContextProxy : public vr::IVRDriverContext {
public:
DriverContextProxy();
static DriverContextProxy *Instance();
void SetDriverContext(vr::IVRDriverContext *pDriverContext);
/** IVRDriverContext **/
void *GetGenericInterface(const char *pchInterfaceVersion, vr::EVRInitError *peError = nullptr) override;
vr::DriverHandle_t GetDriverHandle() override;
private:
static DriverContextProxy *m_pInstance;
vr::IVRDriverContext *m_pDriverContext;
};
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/driver_host_proxy.cpp
================================================
#include "driver_host_proxy.h"
#include "hmd_math.h"
#include "util.h"
#include "vr_settings.h"
#include
namespace psvr2_toolkit {
/* Hardcoded device indexes, in order of when they are registered inside the driver. */
static constexpr uint32_t k_unDeviceIndexHeadset = 0; // Currently not used.
static constexpr uint32_t k_unDeviceIndexSenseControllerLeft = 1;
static constexpr uint32_t k_unDeviceIndexSenseControllerRight = 2;
DriverHostProxy *DriverHostProxy::m_pInstance = nullptr;
DriverHostProxy::DriverHostProxy()
: m_pDriverHost(nullptr)
, m_pfnEventHandlers()
{}
DriverHostProxy *DriverHostProxy::Instance() {
if (!m_pInstance) {
m_pInstance = new DriverHostProxy;
}
return m_pInstance;
}
void DriverHostProxy::SetDriverHost(vr::IVRServerDriverHost *pDriverHost) {
m_pDriverHost = pDriverHost;
}
void DriverHostProxy::AddEventHandler(void (*pfnEventHandler)(vr::VREvent_t *)) {
m_pfnEventHandlers.push_back(pfnEventHandler);
}
bool DriverHostProxy::TrackedDeviceAdded(const char *pchDeviceSerialNumber, vr::ETrackedDeviceClass eDeviceClass, vr::ITrackedDeviceServerDriver *pDriver) {
if (Util::StartsWith(pchDeviceSerialNumber, "playstation_vr2_sense_controller_") &&
VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_SENSE, SETTING_DISABLE_SENSE_DEFAULT_VALUE))
{
return false;
}
return m_pDriverHost->TrackedDeviceAdded(pchDeviceSerialNumber, eDeviceClass, pDriver);
}
void DriverHostProxy::TrackedDevicePoseUpdated(uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize) {
if (unWhichDevice != k_unDeviceIndexSenseControllerLeft && unWhichDevice != k_unDeviceIndexSenseControllerRight) {
return m_pDriverHost->TrackedDevicePoseUpdated(unWhichDevice, newPose, unPoseStructSize);
}
return m_pDriverHost->TrackedDevicePoseUpdated(unWhichDevice, GetPose(unWhichDevice, newPose), unPoseStructSize);
}
void DriverHostProxy::VsyncEvent(double vsyncTimeOffsetSeconds) {
m_pDriverHost->VsyncEvent(vsyncTimeOffsetSeconds);
}
void DriverHostProxy::VendorSpecificEvent(uint32_t unWhichDevice, vr::EVREventType eventType, const vr::VREvent_Data_t &eventData, double eventTimeOffset) {
m_pDriverHost->VendorSpecificEvent(unWhichDevice, eventType, eventData, eventTimeOffset);
}
bool DriverHostProxy::IsExiting() {
return m_pDriverHost->IsExiting();
}
bool DriverHostProxy::PollNextEvent(vr::VREvent_t *pEvent, uint32_t uncbVREvent) {
if (m_pDriverHost->PollNextEvent(pEvent, uncbVREvent)) {
for (auto& m_pfnEventHandler : m_pfnEventHandlers) {
m_pfnEventHandler(pEvent);
}
return true;
}
return false;
}
void DriverHostProxy::GetRawTrackedDevicePoses(float fPredictedSecondsFromNow, vr::TrackedDevicePose_t *pTrackedDevicePoseArray, uint32_t unTrackedDevicePoseArrayCount) {
m_pDriverHost->GetRawTrackedDevicePoses(fPredictedSecondsFromNow, pTrackedDevicePoseArray, unTrackedDevicePoseArrayCount);
}
void DriverHostProxy::RequestRestart(const char *pchLocalizedReason, const char *pchExecutableToStart, const char *pchArguments, const char *pchWorkingDirectory) {
m_pDriverHost->RequestRestart(pchLocalizedReason, pchExecutableToStart, pchArguments, pchWorkingDirectory);
}
uint32_t DriverHostProxy::GetFrameTimings(vr::Compositor_FrameTiming *pTiming, uint32_t nFrames) {
return m_pDriverHost->GetFrameTimings(pTiming, nFrames);
}
void DriverHostProxy::SetDisplayEyeToHead(uint32_t unWhichDevice, const vr::HmdMatrix34_t &eyeToHeadLeft, const vr::HmdMatrix34_t &eyeToHeadRight) {
m_pDriverHost->SetDisplayEyeToHead(unWhichDevice, eyeToHeadLeft, eyeToHeadRight);
}
void DriverHostProxy::SetDisplayProjectionRaw(uint32_t unWhichDevice, const vr::HmdRect2_t &eyeLeft, const vr::HmdRect2_t &eyeRight) {
m_pDriverHost->SetDisplayProjectionRaw(unWhichDevice, eyeLeft, eyeRight);
}
void DriverHostProxy::SetRecommendedRenderTargetSize(uint32_t unWhichDevice, uint32_t nWidth, uint32_t nHeight) {
m_pDriverHost->SetRecommendedRenderTargetSize(unWhichDevice, nWidth, nHeight);
}
vr::DriverPose_t DriverHostProxy::GetPose(uint32_t unWhichDevice, const vr::DriverPose_t &originalPose) {
static vr::HmdQuaternion_t imuRotationOffset = HmdMath::EulerToQuaternion(0, 0, 0.680678427219391);
static vr::HmdQuaternion_t imuRotationOffsetInverse = HmdMath::QuaternionInverse(imuRotationOffset);
// Whether this is the left controller or not.
bool isLeft = unWhichDevice == k_unDeviceIndexSenseControllerLeft;
// Our new pose is a copy of the original pose.
vr::DriverPose_t newPose = originalPose;
// Apply inverse of imuRotationOffset to qRotation.
newPose.qRotation = HmdMath::QuaternionMultiply(newPose.qRotation, imuRotationOffsetInverse);
// PS VR2 driver pose offset.
vr::HmdVector3d_t poseOffset = {isLeft ? 0.03439270332455635 : -0.03439270332455635, 0.05370872840285301, -0.09804324805736542};
// Rotate the offset by the new rotation.
vr::HmdVector3d_t rotationOffset = HmdMath::RotateVectorByQuaternion(poseOffset, newPose.qRotation);
// Adjust position (negate the offset from the driver).
newPose.vecPosition[0] -= rotationOffset.v[0];
newPose.vecPosition[1] -= rotationOffset.v[1];
newPose.vecPosition[2] -= rotationOffset.v[2];
// Offset from the driver's root to the IMU. Given by the PS VR2 driver.
// We'll also have to factor it to make the result pose identical to the one from the driver.
vr::HmdVector3d_t imuOffset = {isLeft ? -0.00937270000576973 : 0.020072702318429947, 0.012248100712895393, 0.006003900431096554};
// Rotate IMU offset to counteract the rotation we did on qRotation. See next comment.
imuOffset = HmdMath::RotateVectorByQuaternion(imuOffset, imuRotationOffset);
poseOffset.v[0] += imuOffset.v[0];
poseOffset.v[1] += imuOffset.v[1];
poseOffset.v[2] += imuOffset.v[2];
newPose.vecDriverFromHeadTranslation[0] = poseOffset.v[0];
newPose.vecDriverFromHeadTranslation[1] = poseOffset.v[1];
newPose.vecDriverFromHeadTranslation[2] = poseOffset.v[2];
// Since qRotation was rotated by the inverse of imuRotationOffset, we'll have to counteract it.
newPose.qDriverFromHeadRotation = imuRotationOffset;
return newPose;
}
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/driver_host_proxy.h
================================================
#pragma once
#include
#include
namespace psvr2_toolkit {
class DriverHostProxy : public vr::IVRServerDriverHost {
public:
DriverHostProxy();
static DriverHostProxy *Instance();
void SetDriverHost(vr::IVRServerDriverHost *pDriverHost);
void AddEventHandler(void (*pfnEventHandler)(vr::VREvent_t *)); // Required for intercepting polled events from the PS VR2 driver.
/** IVRServerDriverHost **/
bool TrackedDeviceAdded(const char *pchDeviceSerialNumber, vr::ETrackedDeviceClass eDeviceClass, vr::ITrackedDeviceServerDriver *pDriver) override;
void TrackedDevicePoseUpdated(uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize) override;
void VsyncEvent(double vsyncTimeOffsetSeconds) override;
void VendorSpecificEvent(uint32_t unWhichDevice, vr::EVREventType eventType, const vr::VREvent_Data_t &eventData, double eventTimeOffset) override;
bool IsExiting() override;
bool PollNextEvent(vr::VREvent_t *pEvent, uint32_t uncbVREvent) override;
void GetRawTrackedDevicePoses(float fPredictedSecondsFromNow, vr::TrackedDevicePose_t *pTrackedDevicePoseArray, uint32_t unTrackedDevicePoseArrayCount) override;
void RequestRestart(const char *pchLocalizedReason, const char *pchExecutableToStart, const char *pchArguments, const char *pchWorkingDirectory) override;
uint32_t GetFrameTimings(vr::Compositor_FrameTiming *pTiming, uint32_t nFrames) override;
void SetDisplayEyeToHead(uint32_t unWhichDevice, const vr::HmdMatrix34_t &eyeToHeadLeft, const vr::HmdMatrix34_t &eyeToHeadRight) override;
void SetDisplayProjectionRaw(uint32_t unWhichDevice, const vr::HmdRect2_t &eyeLeft, const vr::HmdRect2_t &eyeRight) override;
void SetRecommendedRenderTargetSize(uint32_t unWhichDevice, uint32_t nWidth, uint32_t nHeight) override;
private:
static DriverHostProxy *m_pInstance;
vr::IVRServerDriverHost *m_pDriverHost;
std::list m_pfnEventHandlers;
// Used internally for controller pose correction.
vr::DriverPose_t GetPose(uint32_t unWhichDevice, const vr::DriverPose_t &originalPose);
};
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/eyelid_estimator.cpp
================================================
#include "eyelid_estimator.h"
#include
#include
namespace psvr2_toolkit {
// parameters
static constexpr float openLR = 0.001f; // slow open adaptation
static constexpr float closedLR = 0.01f; // faster closed adaptation
static constexpr float smoothAlpha = 0.2f; // EMA smoothing
EyelidEstimator::EyelidEstimator()
: m_openDia(4.0f)
, m_closedDia(2.0f)
, m_sensorYOpen(0.55f)
, m_sensorYClosed(0.45f)
, m_lastOpenness(1.0f)
{}
bool EyelidEstimator::IsNeutral(const Hmd2GazeEye &eye) const {
if (eye.isGazeDirValid == HMD2_BOOL_FALSE) {
return false;
}
// Forward assumed to be (0,0,1). Require within ~20deg of forward.
return eye.gazeDirNorm.z > 0.94f;
}
float EyelidEstimator::Estimate(const Hmd2GazeEye &eye) {
if (eye.isBlinkValid && eye.blink == HMD2_BOOL_TRUE) {
// Update CLOSED reference on blink
if (eye.isPupilDiaValid) {
m_closedDia = m_closedDia * (1.0f - closedLR) +
eye.pupilDiaMm * closedLR;
}
if (eye.isPupilPosInSensorValid) {
m_sensorYClosed = m_sensorYClosed * (1.0f - closedLR) +
eye.pupilPosInSensor.y * closedLR;
}
} else if (eye.isPupilDiaValid && eye.isPupilPosInSensorValid && IsNeutral(eye)) {
// Update OPEN reference only if gaze is neutral
bool stable = std::fabs(eye.pupilDiaMm - m_openDia) < 1.0f;
if (stable) {
m_openDia = m_openDia * (1.0f - openLR) +
eye.pupilDiaMm * openLR;
m_sensorYOpen = m_sensorYOpen * (1.0f - openLR) +
eye.pupilPosInSensor.y * openLR;
}
}
float opennessRaw = m_lastOpenness;
if (eye.isBlinkValid && eye.blink == HMD2_BOOL_TRUE) {
opennessRaw = 0.0f; // explicit blink
} else if (eye.isPupilDiaValid && eye.isPupilPosInSensorValid) {
float denomDia = std::max(1e-6f, m_openDia - m_closedDia);
float normDia = (eye.pupilDiaMm - m_closedDia) / denomDia;
normDia = std::clamp(normDia, 0.0f, 1.0f);
float denomPos = std::max(1e-6f, m_sensorYOpen - m_sensorYClosed);
float normPos = (eye.pupilPosInSensor.y - m_sensorYClosed) / denomPos;
normPos = std::clamp(normPos, 0.0f, 1.0f);
// Weight cues based on gaze vertical angle
float gazeWeight = 1.0f;
if (eye.isGazeDirValid) {
float vertical = eye.gazeDirNorm.y; // +up, -down
gazeWeight = std::clamp(1.0f - std::fabs(vertical) * 1.5f, 0.2f, 1.0f);
}
opennessRaw = gazeWeight * (0.65f * normDia + 0.35f * normPos)
+ (1.0f - gazeWeight) * normDia; // fallback to diameter when gaze extreme
} else {
// Missing data ? decay toward closed
opennessRaw = m_lastOpenness * 0.9f;
}
float openness = m_lastOpenness * (1.0f - smoothAlpha) +
opennessRaw * smoothAlpha;
m_lastOpenness = openness;
return openness; // 0 = closed, 1 = open
}
}
================================================
FILE: projects/psvr2_openvr_driver_ex/eyelid_estimator.h
================================================
#pragma once
#include "hmd2_gaze.h"
namespace psvr2_toolkit {
class EyelidEstimator {
public:
EyelidEstimator();
float Estimate(const Hmd2GazeEye &eye);
private:
float m_openDia;
float m_closedDia;
float m_sensorYOpen;
float m_sensorYClosed;
float m_lastOpenness;
bool IsNeutral(const Hmd2GazeEye &eye) const;
};
}
================================================
FILE: projects/psvr2_openvr_driver_ex/hmd2_gaze.h
================================================
#pragma once
#include
typedef enum {
HMD2_BOOL_FALSE = 0,
HMD2_BOOL_TRUE = 1
} Hmd2Bool;
typedef struct {
float x, y;
} Hmd2Vector2;
typedef struct {
float x, y, z;
} Hmd2Vector3;
typedef struct {
Hmd2Bool isGazeOriginValid;
Hmd2Vector3 gazeOriginMm;
Hmd2Bool isGazeDirValid;
Hmd2Vector3 gazeDirNorm;
Hmd2Bool isPupilDiaValid;
float pupilDiaMm;
Hmd2Bool isPupilPosInSensorValid;
Hmd2Vector2 pupilPosInSensor;
Hmd2Bool isPosGuideValid;
Hmd2Vector2 posGuide;
Hmd2Bool isBlinkValid;
Hmd2Bool blink;
} Hmd2GazeEye;
struct Hmd2GazeCombined {
Hmd2Bool isGazeOriginValid;
Hmd2Vector3 gazeOriginMm;
Hmd2Bool isGazeDirValid;
Hmd2Vector3 gazeDirNorm;
Hmd2Bool isValid;
uint32_t timestamp;
Hmd2Bool unk06;
float unk07;
Hmd2Bool unk08;
// All gazes seem to be repeated here?
// No obvious differences here.
Hmd2Vector3 leftGazeDirNormal;
Hmd2Vector3 rightGazeDirNormal;
Hmd2Vector3 combinedGazeDirNormal;
float unk09;
};
struct Hmd2GazeState {
char magic[2];
uint16_t version;
uint32_t size;
float unk03; // 0.700
float unk04; // 0.700
uint32_t unk05; // 0xFFFFF
// Appears to be related to some sort of timestamp?
uint32_t timestamp06;
uint32_t timestamp07;
uint32_t timestamp08;
uint32_t unk09;
float leftEyeXPosMm; // Example: -32.000 (64mm IPD)
uint32_t unk11;
uint32_t unk12;
float rightEyeXPosMm; // Example: 32.000 (64mm IPD)
uint32_t unk14;
uint32_t unk15;
uint32_t unk16;
uint32_t unk17;
uint32_t unk18;
float eyeZPosMm; // -27.000
// More unknown garbage.
uint32_t unk20;
uint32_t timestamp21;
uint32_t unk22;
uint32_t timestamp23;
Hmd2GazeEye leftEye;
Hmd2GazeEye rightEye;
Hmd2GazeCombined combined;
};
================================================
FILE: projects/psvr2_openvr_driver_ex/hmd_device_hooks.cpp
================================================
#ifdef OPENVR_EXTENSIONS_AVAILABLE
#include "psvr2_openvr_driver/openvr_ex/openvr_ex.h"
#endif
#include "driver_host_proxy.h"
#include "hmd2_gaze.h"
#include "hmd_device_hooks.h"
#include "hmd_driver_loader.h"
#include "hook_lib.h"
#include "vr_settings.h"
#include "util.h"
#include
#include
namespace psvr2_toolkit {
void* (*CaesarManager__getInstance)();
uint64_t (*CaesarManager__getIMUTimestampOffset)(void* thisptr, int64_t* hmdToHostOffset);
void* (*ShareManager__getInstance)();
void (*ShareManager__getIntConfig)(void* thisPtr, uint32_t configId, int64_t* outValue);
void (*ShareManager__setIntConfig)(void* thisPtr, uint32_t configId, int64_t* value);
#ifdef OPENVR_EXTENSIONS_AVAILABLE
void* g_pOpenVRExHandle = nullptr;
#endif
vr::VRInputComponentHandle_t eyeTrackingComponent = vr::k_ulInvalidInputComponentHandle;
int64_t currentBrightness;
vr::EVRInitError (*sie__psvr2__HmdDevice__Activate)(void *, uint32_t) = nullptr;
vr::EVRInitError sie__psvr2__HmdDevice__ActivateHook(void *thisptr, uint32_t unObjectId) {
vr::EVRInitError result = sie__psvr2__HmdDevice__Activate(thisptr, unObjectId);
vr::PropertyContainerHandle_t ulPropertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(unObjectId);
// Tell SteamVR we want the chaperone visibility disabled if we're actually disabling the chaperone.
if (VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_CHAPERONE, SETTING_DISABLE_CHAPERONE_DEFAULT_VALUE)) {
vr::VRProperties()->SetBoolProperty(ulPropertyContainer, vr::Prop_DriverProvidedChaperoneVisibility_Bool, false);
}
// Tell SteamVR to allow runtime framerate changes.
// SteamVR does not allow this feature on AMD GPUs, so this is NVIDIA-only currently.
vr::VRProperties()->SetBoolProperty(ulPropertyContainer, vr::Prop_DisplaySupportsRuntimeFramerateChange_Bool, true);
// Tell SteamVR to allow night mode setting.
vr::VRProperties()->SetBoolProperty(ulPropertyContainer, vr::Prop_DisplayAllowNightMode_Bool, true);
// Tell SteamVR we support brightness controls.
vr::VRProperties()->SetBoolProperty(ulPropertyContainer, vr::Prop_DisplaySupportsAnalogGain_Bool, true);
vr::VRProperties()->SetFloatProperty(ulPropertyContainer, vr::Prop_DisplayMinAnalogGain_Float, 0.0f);
vr::VRProperties()->SetFloatProperty(ulPropertyContainer, vr::Prop_DisplayMaxAnalogGain_Float, 1.0f);
// Fill in brightness from PSVR2 config to SteamVR settings key.
// Also, "analogGain" is stored as a gamma corrected value.
ShareManager__getIntConfig(ShareManager__getInstance(), 2, ¤tBrightness);
vr::VRSettings()->SetFloat(vr::k_pch_SteamVR_Section, "analogGain", powf(static_cast(currentBrightness) / 31.0f, 2.2f));
// Set event handler for when brightness ("analogGain") changes.
DriverHostProxy::Instance()->AddEventHandler([](vr::VREvent_t* event) {
if (event->eventType == vr::EVREventType::VREvent_SteamVRSectionSettingChanged) {
float currentFloatBrightness = powf(vr::VRSettings()->GetFloat(vr::k_pch_SteamVR_Section, "analogGain"), 1 / 2.2f);
if (static_cast(ceilf(currentFloatBrightness * 31.0f)) != currentBrightness)
{
currentBrightness = static_cast(ceilf(currentFloatBrightness * 31.0f));
ShareManager__setIntConfig(ShareManager__getInstance(), 2, ¤tBrightness);
}
}
});
// Tell SteamVR our dashboard scale.
vr::VRProperties()->SetFloatProperty(ulPropertyContainer, vr::Prop_DashboardScale_Float, .9f);
vr::VRProperties()->SetBoolProperty(ulPropertyContainer, vr::Prop_SupportsXrEyeGazeInteraction_Bool, true);
if (vr::VRDriverInput())
{
vr::EVRInputError result = (vr::VRDriverInput())->CreateEyeTrackingComponent(ulPropertyContainer, "/eyetracking", &eyeTrackingComponent);
if (result != vr::VRInputError_None) {
vr::VRDriverLog()->Log("Failed to create eye tracking component.");
}
}
else
{
vr::VRDriverLog()->Log("Failed to get driver input interface. Are you on the latest version of SteamVR?");
}
#ifdef OPENVR_EXTENSIONS_AVAILABLE
psvr2_toolkit::openvr_ex::OnHmdActivate(ulPropertyContainer, &g_pOpenVRExHandle);
#endif
return result;
}
void (*sie__psvr2__HmdDevice__Deactivate)(void *) = nullptr;
void sie__psvr2__HmdDevice__DeactivateHook(void *thisptr) {
sie__psvr2__HmdDevice__Deactivate(thisptr);
#ifdef OPENVR_EXTENSIONS_AVAILABLE
if (g_pOpenVRExHandle) {
psvr2_toolkit::openvr_ex::OnHmdDeactivate(&g_pOpenVRExHandle);
}
#endif
}
inline const int64_t GetHostTimestamp()
{
static LARGE_INTEGER frequency{};
if (frequency.QuadPart == 0)
{
QueryPerformanceFrequency(&frequency);
}
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
return static_cast((static_cast(now.QuadPart) /
static_cast(frequency.QuadPart)) * 1e6);
}
void HmdDeviceHooks::UpdateGaze(void* pData, size_t dwSize)
{
if (eyeTrackingComponent == vr::k_ulInvalidInputComponentHandle)
{
return;
}
Hmd2GazeState* pGazeState = reinterpret_cast(pData);
vr::VREyeTrackingData_t eyeTrackingData {};
bool valid = pGazeState->combined.isGazeDirValid;
eyeTrackingData.bActive = valid;
eyeTrackingData.bTracked = valid;
eyeTrackingData.bValid = valid;
auto& origin = pGazeState->combined.gazeOriginMm;
auto& direction = pGazeState->combined.gazeDirNorm;
eyeTrackingData.vGazeOrigin = vr::HmdVector3_t { -origin.x / 1000.0f, origin.y / 1000.0f, -origin.z / 1000.0f };
eyeTrackingData.vGazeTarget = vr::HmdVector3_t { -direction.x, direction.y, -direction.z };
int64_t hmdToHostOffset;
CaesarManager__getIMUTimestampOffset(CaesarManager__getInstance(), &hmdToHostOffset);
double timeOffset = ((static_cast(pGazeState->combined.timestamp) + hmdToHostOffset) - GetHostTimestamp()) / 1e6;
(vr::VRDriverInput())->UpdateEyeTrackingComponent(eyeTrackingComponent, &eyeTrackingData, timeOffset);
#ifdef OPENVR_EXTENSIONS_AVAILABLE
if (g_pOpenVRExHandle) {
psvr2_toolkit::openvr_ex::OnHmdUpdate(&g_pOpenVRExHandle, pData, dwSize);
}
#endif
}
void HmdDeviceHooks::InstallHooks() {
static HmdDriverLoader *pHmdDriverLoader = HmdDriverLoader::Instance();
// sie::psvr2::HmdDevice::Activate
HookLib::InstallHook(reinterpret_cast(pHmdDriverLoader->GetBaseAddress() + 0x19D1B0),
reinterpret_cast(sie__psvr2__HmdDevice__ActivateHook),
reinterpret_cast(&sie__psvr2__HmdDevice__Activate));
// sie::psvr2::HmdDevice::Deactivate
HookLib::InstallHook(reinterpret_cast(pHmdDriverLoader->GetBaseAddress() + 0x19EBF0),
reinterpret_cast(sie__psvr2__HmdDevice__DeactivateHook),
reinterpret_cast(&sie__psvr2__HmdDevice__Deactivate));
CaesarManager__getInstance = decltype(CaesarManager__getInstance)(pHmdDriverLoader->GetBaseAddress() + 0x124c90);
CaesarManager__getIMUTimestampOffset = decltype(CaesarManager__getIMUTimestampOffset)(pHmdDriverLoader->GetBaseAddress() + 0x1252e0);
ShareManager__getInstance = decltype(ShareManager__getInstance)(pHmdDriverLoader->GetBaseAddress() + 0x15bbd0);
ShareManager__getIntConfig = decltype(ShareManager__getIntConfig)(pHmdDriverLoader->GetBaseAddress() + 0x15d270);
ShareManager__setIntConfig = decltype(ShareManager__setIntConfig)(pHmdDriverLoader->GetBaseAddress() + 0x15f3d0);
}
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/hmd_device_hooks.h
================================================
#pragma once
#include
namespace psvr2_toolkit {
class HmdDeviceHooks {
public:
static void InstallHooks();
static void UpdateGaze(void* pData, size_t dwSize);
};
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/hmd_driver_factory.cpp
================================================
#include "device_provider_proxy.h"
#include "hmd_driver_loader.h"
#include
#include
using namespace psvr2_toolkit;
extern "C" __declspec(dllexport) void *HmdDriverFactory(const char *pInterfaceName, int *pReturnCode) {
static HmdDriverLoader *pHmdDriverLoader = HmdDriverLoader::Instance();
// Check if the HMD driver DLL is actually loaded.
if (pHmdDriverLoader->GetBaseAddress()) {
void *result = pHmdDriverLoader->pfnHmdDriverFactory(pInterfaceName, pReturnCode);
if (strcmp(vr::IServerTrackedDeviceProvider_Version, pInterfaceName) == 0 &&
*pReturnCode == vr::VRInitError_None /* no point injecting if the original driver failed to initialise */) {
static DeviceProviderProxy *pDeviceProviderProxy = DeviceProviderProxy::Instance();
pDeviceProviderProxy->SetDeviceProvider(static_cast(result));
return pDeviceProviderProxy;
}
return result;
}
MessageBoxW(nullptr, L"Loading original HMD driver failed, please report this to the developers!", L"PlayStation VR2 Toolkit (DriverEx)", MB_ICONERROR | MB_OK);
if (pReturnCode) {
*pReturnCode = vr::VRInitError_Init_InterfaceNotFound;
}
return nullptr;
}
================================================
FILE: projects/psvr2_openvr_driver_ex/hmd_driver_loader.cpp
================================================
#include "hmd_driver_loader.h"
#include
#define HMD_DLL_NAME L"driver_playstation_vr2_orig.dll"
extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace psvr2_toolkit {
// Allows the C++ standard library load the original HMD driver automatically for us.
class HmdDriverLoaderInitializer {
public:
HmdDriverLoaderInitializer() {
HmdDriverLoader::Instance();
}
};
HmdDriverLoaderInitializer __initializer;
HmdDriverLoader *HmdDriverLoader::m_pInstance = nullptr;
HmdDriverLoader::HmdDriverLoader()
: pfnHmdDriverFactory(nullptr)
, m_hModule(nullptr)
{
wchar_t pszHmdDllPath[MAX_PATH] = { 0 };
if (GetHmdDllPath(pszHmdDllPath)) {
m_hModule = LoadLibraryW(pszHmdDllPath);
if (m_hModule) {
pfnHmdDriverFactory = decltype(pfnHmdDriverFactory)(GetProcAddress(m_hModule, "HmdDriverFactory"));
}
}
}
HmdDriverLoader *HmdDriverLoader::Instance() {
if (!m_pInstance) {
m_pInstance = new HmdDriverLoader;
}
return m_pInstance;
}
uintptr_t HmdDriverLoader::GetBaseAddress() {
return reinterpret_cast(m_hModule);
}
bool HmdDriverLoader::GetHmdDllPath(wchar_t *pszHmdDllPath) {
if (!pszHmdDllPath) {
return false;
}
wchar_t pszPath[MAX_PATH] = {0};
DWORD dwLength = GetModuleFileNameW(reinterpret_cast(&__ImageBase), pszPath, MAX_PATH);
if (dwLength > 0 && dwLength < MAX_PATH) {
if (PathRemoveFileSpecW(pszPath)) {
if (PathCombineW(pszHmdDllPath, pszPath, HMD_DLL_NAME)) {
return true;
}
}
}
return false;
}
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/hmd_driver_loader.h
================================================
#pragma once
#include
#include
namespace psvr2_toolkit {
// Provides a thin interface between our proxy library and the original HMD driver.
class HmdDriverLoader {
public:
HmdDriverLoader();
static HmdDriverLoader *Instance();
uintptr_t GetBaseAddress();
void *(*pfnHmdDriverFactory)(const char *pInterfaceName, int *pReturnCode);
private:
static HmdDriverLoader *m_pInstance;
HMODULE m_hModule;
bool GetHmdDllPath(wchar_t *pszHmdDllPath);
};
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/hmd_math.h
================================================
#pragma once
#include
namespace psvr2_toolkit {
class HmdMath {
public:
static vr::HmdQuaternion_t EulerToQuaternion(double yaw, double pitch, double roll) {
double cy = cos(yaw * 0.5);
double sy = sin(yaw * 0.5);
double cp = cos(pitch * 0.5);
double sp = sin(pitch * 0.5);
double cr = cos(roll * 0.5);
double sr = sin(roll * 0.5);
return {
cr * cp * cy + sr * sp * sy,
sr * cp * cy - cr * sp * sy,
cr * sp * cy + sr * cp * sy,
cr * cp * sy - sr * sp * cy
};
}
static vr::HmdQuaternion_t QuaternionMultiply(const vr::HmdQuaternion_t &a, const vr::HmdQuaternion_t &b) {
return {
a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,
a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w
};
}
static vr::HmdQuaternion_t QuaternionInverse(const vr::HmdQuaternion_t &q) {
double normSq = q.w * q.w + q.x * q.x + q.y * q.y + q.z * q.z;
return { q.w / normSq, -q.x / normSq, -q.y / normSq, -q.z / normSq };
}
static vr::HmdVector3d_t RotateVectorByQuaternion(const vr::HmdVector3d_t &v, const vr::HmdQuaternion_t &q) {
vr::HmdQuaternion_t vQuat = { 0.0, v.v[0], v.v[1], v.v[2] };
vr::HmdQuaternion_t qv = QuaternionMultiply(q, vQuat);
vr::HmdQuaternion_t qInv = QuaternionInverse(q);
vr::HmdQuaternion_t result = QuaternionMultiply(qv, qInv);
return { result.x, result.y, result.z };
}
};
}
================================================
FILE: projects/psvr2_openvr_driver_ex/hook_lib.h
================================================
#pragma once
#include
#define INSTALL_STUB(pTarget) psvr2_toolkit::HookLib::InstallStub(pTarget)
#define INSTALL_STUB_ORIGINAL(pTarget, ppOriginal) psvr2_toolkit::HookLib::InstallStub(pTarget, ppOriginal)
#define INSTALL_STUB_RET0(pTarget) psvr2_toolkit::HookLib::InstallStubRet0(pTarget)
#define INSTALL_STUB_RET0_ORIGINAL(pTarget, ppOriginal) psvr2_toolkit::HookLib::InstallStubRet0(pTarget, ppOriginal)
namespace psvr2_toolkit {
// Provides a thin wrapper around MinHook.
class HookLib {
private:
static void Stub() {}
static __int64 StubRet0() { return 0; }
public:
static bool Initialize() {
return MH_Initialize() == MH_OK;
}
static void InstallHook(void *pTarget, void* pDetour, void **ppOriginal = nullptr) {
MH_CreateHook(pTarget, pDetour, ppOriginal);
MH_EnableHook(pTarget);
}
static void InstallStub(void* pTarget, void** ppOriginal = nullptr) {
InstallHook(pTarget, reinterpret_cast(Stub), ppOriginal);
}
static void InstallStubRet0(void *pTarget, void **ppOriginal = nullptr) {
InstallHook(pTarget, reinterpret_cast(StubRet0), ppOriginal);
}
};
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/ipc_server.cpp
================================================
#include "ipc_server.h"
#include "trigger_effect_manager.h"
#include "util.h"
#include "vr_settings.h"
#include
namespace psvr2_toolkit {
namespace ipc {
IpcServer *IpcServer::m_pInstance = nullptr;
IpcServer::IpcServer()
: m_initialized(false)
, m_running(false)
, m_doGaze(false)
, m_socket{}
, m_serverAddr{}
, m_pGazeState(nullptr)
{}
IpcServer *IpcServer::Instance() {
if (!m_pInstance) {
m_pInstance = new IpcServer;
}
return m_pInstance;
}
bool IpcServer::Initialized() {
return m_initialized;
}
void IpcServer::Initialize() {
if (m_initialized) {
return;
}
WSADATA wsaData = {};
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
Util::DriverLog("[IPC_SERVER] WSAStartup failed. Result = {}", result);
return;
}
m_initialized = true;
m_doGaze = !VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_GAZE, SETTING_DISABLE_GAZE_DEFAULT_VALUE);
m_doOpenness = VRSettings::GetBool(STEAMVR_SETTINGS_ENABLE_EYELID_ESTIMATION, SETTING_ENABLE_EYELID_ESTIMATION_DEFAULT_VALUE);
}
void IpcServer::Start() {
if (!m_initialized && m_running) {
return;
}
m_socket = socket(AF_INET, SOCK_STREAM, 0);
if (m_socket == INVALID_SOCKET) {
Util::DriverLog("[IPC_SERVER] Creating socket failed. LastError = {}", WSAGetLastError());
return;
}
m_serverAddr.sin_family = AF_INET;
m_serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
m_serverAddr.sin_port = htons(IPC_SERVER_PORT);
if (bind(m_socket, reinterpret_cast(&m_serverAddr), sizeof(m_serverAddr)) == SOCKET_ERROR) {
Util::DriverLog("[IPC_SERVER] Bind failed. LastError = {}", WSAGetLastError());
closesocket(m_socket);
return;
}
if (listen(m_socket, SOMAXCONN) == SOCKET_ERROR) {
Util::DriverLog("[IPC_SERVER] Listen failed. LastError = {}", WSAGetLastError());
closesocket(m_socket);
return;
}
m_running = true;
m_receiveThread = std::thread(&IpcServer::ReceiveLoop, this);
}
void IpcServer::Stop() {
if (!m_running) {
return;
}
m_running = false;
closesocket(m_socket);
m_receiveThread.join();
}
void IpcServer::UpdateGazeState(Hmd2GazeState *pGazeState, float leftEyelidOpenness, float rightEyelidOpenness) {
if (!m_pGazeState) {
m_pGazeState = reinterpret_cast(malloc(sizeof(Hmd2GazeState)));
}
if (m_pGazeState) {
memcpy(m_pGazeState, pGazeState, sizeof(Hmd2GazeState)); // Realistically, we should have a mutex here. Sadly, we do not have the time.
}
m_leftEyelidOpenness = leftEyelidOpenness;
m_rightEyelidOpenness = rightEyelidOpenness;
}
void IpcServer::ReceiveLoop() {
char pBuffer[1024] = {}; // This does not need to be static.
sockaddr_in clientAddr = {};
int clientAddrLen = sizeof(clientAddr);
while (m_running) {
SOCKADDR_IN clientAddr = {};
int clientAddrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(m_socket, reinterpret_cast(&clientAddr), &clientAddrLen);
if (clientSocket == INVALID_SOCKET) {
int error = WSAGetLastError();
if (m_running) {
Util::DriverLog("[IPC_SERVER] Accept failed. LastError = {}", error);
}
else {
Util::DriverLog("[IPC_SERVER] Server socket closed. Exiting receive loop.");
}
break;
}
std::thread clientThread(&IpcServer::HandleClient, this, clientSocket, clientAddr);
clientThread.detach(); // client thread shouldnt block receive thread
}
}
void IpcServer::HandleClient(SOCKET clientSocket, SOCKADDR_IN clientAddr) {
char pBuffer[1024] = {};
int clientPort = ntohs(clientAddr.sin_port);
while (m_running) {
int dwBufferSize = recv(clientSocket, pBuffer, sizeof(pBuffer), 0);
if (dwBufferSize <= 0) {
if (dwBufferSize == 0) {
Util::DriverLog("[IPC_SERVER] Client on port {} disconnected.", clientPort);
} else {
Util::DriverLog("[IPC_SERVER] Receive failed for client on port {}. LastError = {}", clientPort, WSAGetLastError());
}
break;
}
if (dwBufferSize < sizeof(CommandHeader_t)) {
Util::DriverLog("[IPC_SERVER] Received invalid command header size from client on port {}.", clientPort);
continue;
}
HandleIpcCommand(clientSocket, clientAddr, pBuffer);
}
closesocket(clientSocket);
}
void IpcServer::HandleIpcCommand(SOCKET clientSocket, const sockaddr_in &clientAddr, char *pBuffer) {
static TriggerEffectManager *pTriggerEffectManager = TriggerEffectManager::Instance();
uint16_t clientPort = ntohs(clientAddr.sin_port);
CommandHeader_t *pHeader = reinterpret_cast(pBuffer);
void *pData = pBuffer + sizeof(CommandHeader_t);
switch (pHeader->type) {
case Command_ClientPing: {
if (pHeader->dataLen == 0 && m_connections.contains(clientPort)) {
SendIpcCommand(clientSocket, Command_ServerPong); // TODO
}
break;
}
case Command_ClientRequestHandshake: {
CommandDataServerHandshakeResult_t response;
response.result = HandshakeResult_Failed;
response.ipcVersion = k_unIpcVersion; // Communicate the IPC version the server supports.
if (pHeader->dataLen == sizeof(CommandDataClientRequestHandshake_t) && !m_connections.contains(clientPort)) {
CommandDataClientRequestHandshake_t *pRequest = reinterpret_cast(pData);
// We only want real running processes to handshake with us.
if (Util::IsProcessRunning(pRequest->processId)) {
m_connections[clientPort] = {
.clientAddr = clientAddr,
.ipcVersion = pRequest->ipcVersion,
.processId = pRequest->processId
};
response.result = HandshakeResult_Success;
}
}
SendIpcCommand(clientSocket, Command_ServerHandshakeResult, &response, sizeof(response));
break;
}
case Command_ClientRequestGazeData: {
if (pHeader->dataLen == 0 && m_connections.contains(clientPort)) {
if (m_doGaze && m_pGazeState) {
// Handle old client IPC version requests here.
if (m_connections[clientPort].ipcVersion == 1) {
CommandDataServerGazeDataResult_t response = {
.leftEye = {
.isGazeOriginValid = m_pGazeState->leftEye.isGazeOriginValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeOriginMm = {
.x = m_pGazeState->leftEye.gazeOriginMm.x,
.y = m_pGazeState->leftEye.gazeOriginMm.y,
.z = m_pGazeState->leftEye.gazeOriginMm.z,
},
.isGazeDirValid = m_pGazeState->leftEye.isGazeDirValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeDirNorm {
.x = m_pGazeState->leftEye.gazeDirNorm.x,
.y = m_pGazeState->leftEye.gazeDirNorm.y,
.z = m_pGazeState->leftEye.gazeDirNorm.z,
},
.isPupilDiaValid = m_pGazeState->leftEye.isPupilDiaValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilDiaMm = m_pGazeState->leftEye.pupilDiaMm,
.isBlinkValid = m_pGazeState->leftEye.isBlinkValid == Hmd2Bool::HMD2_BOOL_TRUE,
.blink = m_pGazeState->leftEye.blink == Hmd2Bool::HMD2_BOOL_TRUE,
},
.rightEye = {
.isGazeOriginValid = m_pGazeState->rightEye.isGazeOriginValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeOriginMm = {
.x = m_pGazeState->rightEye.gazeOriginMm.x,
.y = m_pGazeState->rightEye.gazeOriginMm.y,
.z = m_pGazeState->rightEye.gazeOriginMm.z,
},
.isGazeDirValid = m_pGazeState->rightEye.isGazeDirValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeDirNorm {
.x = m_pGazeState->rightEye.gazeDirNorm.x,
.y = m_pGazeState->rightEye.gazeDirNorm.y,
.z = m_pGazeState->rightEye.gazeDirNorm.z,
},
.isPupilDiaValid = m_pGazeState->rightEye.isPupilDiaValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilDiaMm = m_pGazeState->rightEye.pupilDiaMm,
.isBlinkValid = m_pGazeState->rightEye.isBlinkValid == Hmd2Bool::HMD2_BOOL_TRUE,
.blink = m_pGazeState->rightEye.blink == Hmd2Bool::HMD2_BOOL_TRUE,
}
};
SendIpcCommand(clientSocket, Command_ServerGazeDataResult, &response, sizeof(response));
} else if (m_connections[clientPort].ipcVersion == 2) {
CommandDataServerGazeDataResult2_t response = {
.leftEye = {
.isGazeOriginValid = m_pGazeState->leftEye.isGazeOriginValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeOriginMm = {
.x = m_pGazeState->leftEye.gazeOriginMm.x,
.y = m_pGazeState->leftEye.gazeOriginMm.y,
.z = m_pGazeState->leftEye.gazeOriginMm.z,
},
.isGazeDirValid = m_pGazeState->leftEye.isGazeDirValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeDirNorm {
.x = m_pGazeState->leftEye.gazeDirNorm.x,
.y = m_pGazeState->leftEye.gazeDirNorm.y,
.z = m_pGazeState->leftEye.gazeDirNorm.z,
},
.isPupilDiaValid = m_pGazeState->leftEye.isPupilDiaValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilDiaMm = m_pGazeState->leftEye.pupilDiaMm,
.isBlinkValid = m_pGazeState->leftEye.isBlinkValid == Hmd2Bool::HMD2_BOOL_TRUE,
.blink = m_pGazeState->leftEye.blink == Hmd2Bool::HMD2_BOOL_TRUE,
.isOpenEnabled = m_doOpenness,
.open = m_doOpenness ? m_leftEyelidOpenness : 0.0f,
},
.rightEye = {
.isGazeOriginValid = m_pGazeState->rightEye.isGazeOriginValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeOriginMm = {
.x = m_pGazeState->rightEye.gazeOriginMm.x,
.y = m_pGazeState->rightEye.gazeOriginMm.y,
.z = m_pGazeState->rightEye.gazeOriginMm.z,
},
.isGazeDirValid = m_pGazeState->rightEye.isGazeDirValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeDirNorm {
.x = m_pGazeState->rightEye.gazeDirNorm.x,
.y = m_pGazeState->rightEye.gazeDirNorm.y,
.z = m_pGazeState->rightEye.gazeDirNorm.z,
},
.isPupilDiaValid = m_pGazeState->rightEye.isPupilDiaValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilDiaMm = m_pGazeState->rightEye.pupilDiaMm,
.isBlinkValid = m_pGazeState->rightEye.isBlinkValid == Hmd2Bool::HMD2_BOOL_TRUE,
.blink = m_pGazeState->rightEye.blink == Hmd2Bool::HMD2_BOOL_TRUE,
.isOpenEnabled = m_doOpenness,
.open = m_doOpenness ? m_rightEyelidOpenness : 0.0f,
}
};
SendIpcCommand(clientSocket, Command_ServerGazeDataResult, &response, sizeof(response));
} else {
CommandDataServerGazeDataResult3_t response = {
.leftEye = {
.isGazeOriginValid = m_pGazeState->leftEye.isGazeOriginValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeOriginMm = {
.x = m_pGazeState->leftEye.gazeOriginMm.x,
.y = m_pGazeState->leftEye.gazeOriginMm.y,
.z = m_pGazeState->leftEye.gazeOriginMm.z,
},
.isGazeDirValid = m_pGazeState->leftEye.isGazeDirValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeDirNorm {
.x = m_pGazeState->leftEye.gazeDirNorm.x,
.y = m_pGazeState->leftEye.gazeDirNorm.y,
.z = m_pGazeState->leftEye.gazeDirNorm.z,
},
.isPupilDiaValid = m_pGazeState->leftEye.isPupilDiaValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilDiaMm = m_pGazeState->leftEye.pupilDiaMm,
.isPupilPosInSensorValid = m_pGazeState->leftEye.isPupilPosInSensorValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilPosInSensor = {
.x = m_pGazeState->leftEye.pupilPosInSensor.x,
.y = m_pGazeState->leftEye.pupilPosInSensor.y,
},
.isPosGuideValid = m_pGazeState->leftEye.isPosGuideValid == Hmd2Bool::HMD2_BOOL_TRUE,
.posGuide = {
.x = m_pGazeState->leftEye.posGuide.x,
.y = m_pGazeState->leftEye.posGuide.y,
},
.isBlinkValid = m_pGazeState->leftEye.isBlinkValid == Hmd2Bool::HMD2_BOOL_TRUE,
.blink = m_pGazeState->leftEye.blink == Hmd2Bool::HMD2_BOOL_TRUE,
.isOpenEnabled = m_doOpenness,
.open = m_doOpenness ? m_leftEyelidOpenness : 0.0f,
},
.rightEye = {
.isGazeOriginValid = m_pGazeState->rightEye.isGazeOriginValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeOriginMm = {
.x = m_pGazeState->rightEye.gazeOriginMm.x,
.y = m_pGazeState->rightEye.gazeOriginMm.y,
.z = m_pGazeState->rightEye.gazeOriginMm.z,
},
.isGazeDirValid = m_pGazeState->rightEye.isGazeDirValid == Hmd2Bool::HMD2_BOOL_TRUE,
.gazeDirNorm {
.x = m_pGazeState->rightEye.gazeDirNorm.x,
.y = m_pGazeState->rightEye.gazeDirNorm.y,
.z = m_pGazeState->rightEye.gazeDirNorm.z,
},
.isPupilDiaValid = m_pGazeState->rightEye.isPupilDiaValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilDiaMm = m_pGazeState->rightEye.pupilDiaMm,
.isPupilPosInSensorValid = m_pGazeState->rightEye.isPupilPosInSensorValid == Hmd2Bool::HMD2_BOOL_TRUE,
.pupilPosInSensor = {
.x = m_pGazeState->rightEye.pupilPosInSensor.x,
.y = m_pGazeState->rightEye.pupilPosInSensor.y,
},
.isPosGuideValid = m_pGazeState->rightEye.isPosGuideValid == Hmd2Bool::HMD2_BOOL_TRUE,
.posGuide = {
.x = m_pGazeState->rightEye.posGuide.x,
.y = m_pGazeState->rightEye.posGuide.y,
},
.isBlinkValid = m_pGazeState->rightEye.isBlinkValid == Hmd2Bool::HMD2_BOOL_TRUE,
.blink = m_pGazeState->rightEye.blink == Hmd2Bool::HMD2_BOOL_TRUE,
.isOpenEnabled = m_doOpenness,
.open = m_doOpenness ? m_rightEyelidOpenness : 0.0f,
}
};
SendIpcCommand(clientSocket, Command_ServerGazeDataResult, &response, sizeof(response));
}
} else {
// Handle old client IPC version requests here, as well.
if (m_connections[clientPort].ipcVersion == 1) {
CommandDataServerGazeDataResult_t response = {};
SendIpcCommand(clientSocket, Command_ServerGazeDataResult, &response, sizeof(response));
} else if (m_connections[clientPort].ipcVersion == 2) {
CommandDataServerGazeDataResult2_t response = {};
SendIpcCommand(clientSocket, Command_ServerGazeDataResult, &response, sizeof(response));
} else {
CommandDataServerGazeDataResult3_t response = {};
SendIpcCommand(clientSocket, Command_ServerGazeDataResult, &response, sizeof(response));
}
}
}
break;
}
case Command_ClientTriggerEffectOff:
case Command_ClientTriggerEffectFeedback:
case Command_ClientTriggerEffectWeapon:
case Command_ClientTriggerEffectVibration:
case Command_ClientTriggerEffectMultiplePositionFeedback:
case Command_ClientTriggerEffectSlopeFeedback:
case Command_ClientTriggerEffectMultiplePositionVibration: {
if (m_connections.contains(clientPort)) {
pTriggerEffectManager->HandleIpcCommand(m_connections[clientPort].processId, pHeader, pData);
}
break;
}
}
}
void IpcServer::SendIpcCommand(SOCKET clientSocket, ECommandType type, void *pData, int dataLen) {
// Reduce the allocations by making the buffer static.
// I'm sure this isn't thread-safe, but we only have one receive thread anyways.
static char pBuffer[1024] = {};
int actualDataLen = pData ? dataLen : 0;
int actualBufferLen = sizeof(CommandHeader_t) + actualDataLen;
CommandHeader_t *pHeader = reinterpret_cast(pBuffer);
pHeader->type = type;
pHeader->dataLen = actualDataLen;
memcpy(pBuffer + sizeof(CommandHeader_t), pData, actualDataLen);
send(clientSocket, pBuffer, actualBufferLen, 0);
}
} // ipc
} // psvr2_toolkit
================================================
FILE: projects/psvr2_openvr_driver_ex/ipc_server.h
================================================
#pragma once
#include "hmd2_gaze.h"
#include "../shared/ipc_protocol.h"
#include
#include
#include