diff --git a/src/openxr_data.rs b/src/openxr_data.rs index 63601926..e1fc7b32 100644 --- a/src/openxr_data.rs +++ b/src/openxr_data.rs @@ -1,10 +1,11 @@ use crate::{ clientcore::{Injected, Injector}, graphics_backends::{GraphicsBackend, VulkanData, supported_apis_enum}, + system::System, }; use derive_more::Deref; use glam::f32::{Quat, Vec3}; -use log::{info, warn}; +use log::{debug, info, warn}; use openvr as vr; use openxr as xr; use std::mem::ManuallyDrop; @@ -47,6 +48,7 @@ pub struct OpenXrData { /// should only be externally accessed for testing pub(crate) input: Injected>, pub(crate) compositor: Injected, + pub(crate) system: Injected, } impl Drop for OpenXrData { @@ -187,6 +189,7 @@ impl OpenXrData { enabled_extensions: exts, input: injector.inject(), compositor: injector.inject(), + system: injector.inject(), }) } @@ -206,6 +209,25 @@ impl OpenXrData { xr::Event::SessionStateChanged(event) => { state = Some(event.state()); info!("OpenXR session state changed: {:?}", event.state()); + if let Some(system) = self.system.get().filter(|_| { + (cfg!(test) || session_data.temp_vulkan.is_none()) + && state == Some(xr::SessionState::STOPPING) + }) { + debug!("pushing Quit event"); + system.push_event(vr::VREvent_t { + eventType: vr::EVREventType::Quit as u32, + trackedDeviceIndex: vr::k_unTrackedDeviceIndexInvalid, + eventAgeSeconds: 0.0, + data: vr::VREvent_Data_t { + process: vr::VREvent_Process_t { + pid: u32::MAX, + oldPid: u32::MAX, + bForced: false, + bConnectionLost: false, + }, + }, + }); + } } xr::Event::InteractionProfileChanged(_) => { if let Some(input) = self.input.get() { diff --git a/src/system.rs b/src/system.rs index 0b94dd1d..1d3d814f 100644 --- a/src/system.rs +++ b/src/system.rs @@ -9,6 +9,7 @@ use glam::{Mat3, Quat, Vec3}; use log::{debug, error, trace, warn}; use openvr as vr; use openxr as xr; +use std::collections::VecDeque; use std::ffi::{CStr, CString}; use std::sync::{Arc, Mutex}; @@ -152,6 +153,7 @@ impl ViewCache { #[versions(023, 022, 021, 020, 019, 017, 016, 015, 014, 012, 011, 009)] pub struct System { openxr: Arc, // We don't need to test session restarting. + events: Mutex>, input: Injected>, overlay: Injected, vtables: Vtables, @@ -166,6 +168,7 @@ impl System { pub fn new(openxr: Arc, injector: &Injector) -> Self { Self { openxr, + events: Mutex::default(), input: injector.inject(), overlay: injector.inject(), vtables: Default::default(), @@ -192,6 +195,11 @@ impl System { let mut views = self.views.lock().unwrap(); views.get_views(&session, self.openxr.display_time.get(), ty) } + + pub fn push_event(&self, event: vr::VREvent_t) { + let mut events = self.events.lock().unwrap(); + events.push_back(event); + } } impl vr::IVRSystem023_Interface for System { @@ -301,9 +309,7 @@ impl vr::IVRSystem023_Interface for System { fn GetAppContainerFilePaths(&self, _: *mut std::os::raw::c_char, _: u32) -> u32 { todo!() } - fn AcknowledgeQuit_Exiting(&self) { - todo!() - } + fn AcknowledgeQuit_Exiting(&self) {} fn PerformFirmwareUpdate(&self, _: vr::TrackedDeviceIndex_t) -> vr::EVRFirmwareError { todo!() } @@ -483,6 +489,31 @@ impl vr::IVRSystem023_Interface for System { size: u32, pose: *mut vr::TrackedDevicePose_t, ) -> bool { + if let Some(queued) = self.events.lock().unwrap().pop_front() { + unsafe { + (&raw mut (*event).eventType).write(queued.eventType); + (&raw mut (*event).eventAgeSeconds).write(queued.eventAgeSeconds); + match vr::EVREventType::try_from(queued.eventType).unwrap() { + vr::EVREventType::Quit => { + const CONNECTION_LOST_OFFSET: usize = + std::mem::offset_of!(vr::VREvent_t, data) + + std::mem::offset_of!(vr::VREvent_Process_t, bConnectionLost); + + (&raw mut (*event).data.process.pid).write(queued.data.process.pid); + (&raw mut (*event).data.process.oldPid).write(queued.data.process.oldPid); + (&raw mut (*event).data.process.bForced).write(queued.data.process.bForced); + if size > CONNECTION_LOST_OFFSET as u32 { + (&raw mut (*event).data.process.bConnectionLost) + .write(queued.data.process.bConnectionLost); + } + + return true; + } + _ => todo!(), + } + } + } + let Some(input) = self.input.get() else { return false; }; @@ -974,32 +1005,35 @@ impl vr::IVRSystem009On011 for System { self, origin, &mut e, - std::mem::size_of_val(&event) as u32, + std::mem::size_of_val(&e) as u32, pose, ); if ret && !event.is_null() { - let event = unsafe { event.as_mut() }.unwrap(); - event.eventType = if let Ok(t) = vr::EVREventType::try_from(e.eventType) { - t - } else { + let Ok(event_type) = vr::EVREventType::try_from(e.eventType) else { error!("Unhandled event type for 0.9.12: {}", e.eventType); return false; }; - event.trackedDeviceIndex = e.trackedDeviceIndex; - event.data = match e.eventType { - x if x == vr::EVREventType::ButtonPress as u32 - || x == vr::EVREventType::ButtonUnpress as u32 - || x == vr::EVREventType::ButtonTouch as u32 - || x == vr::EVREventType::ButtonUntouch as u32 => - { - vr::vr_0_9_12::VREvent_Data_t { - controller: unsafe { e.data.controller }, + + unsafe { + (&raw mut (*event).eventType).write(event_type); + (&raw mut (*event).trackedDeviceIndex).write(e.trackedDeviceIndex); + match event_type { + vr::EVREventType::ButtonPress + | vr::EVREventType::ButtonUnpress + | vr::EVREventType::ButtonTouch + | vr::EVREventType::ButtonUntouch => { + (&raw mut (*event).data.controller).write(e.data.controller); + } + vr::EVREventType::Quit => { + (&raw mut (*event).data.process.pid).write(e.data.process.pid); + (&raw mut (*event).data.process.oldPid).write(e.data.process.oldPid); + (&raw mut (*event).data.process.bForced).write(e.data.process.bForced); + } + other => { + error!("Unhandled event type data for 0.9.12: {other:?}"); + return false; } - } - other => { - error!("Unhandled event type data for 0.9.12: {other:?}"); - return false; } } } @@ -1056,4 +1090,24 @@ mod tests { test_prop(vr::ETrackedDeviceProperty::ManufacturerName_String); test_prop(vr::ETrackedDeviceProperty::ControllerType_String); } + + #[test] + fn test_session_stop() { + let xr = Arc::new(OpenXrData::new(&Injector::default()).unwrap()); + let injector = Injector::default(); + let system = Arc::new(System::new(xr.clone(), &injector)); + + xr.system.set(Arc::downgrade(&system)); + + xr.session_data.get().session.request_exit().unwrap(); + xr.poll_events(); + let mut event = vr::VREvent_t::default(); + assert!(system.PollNextEventWithPose( + vr::ETrackingUniverseOrigin::Standing, + &mut event, + std::mem::size_of_val(&event) as u32, + std::ptr::null_mut() + )); + assert_eq!(event.eventType, vr::EVREventType::Quit as u32); + } }