diff options
Diffstat (limited to 'gfx/vr/VRShMem.cpp')
-rw-r--r-- | gfx/vr/VRShMem.cpp | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/gfx/vr/VRShMem.cpp b/gfx/vr/VRShMem.cpp new file mode 100644 index 0000000000..778a27e54c --- /dev/null +++ b/gfx/vr/VRShMem.cpp @@ -0,0 +1,736 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "VRShMem.h" + +#ifdef MOZILLA_INTERNAL_API +# include "nsString.h" +# include "nsXULAppAPI.h" +#endif + +#include "gfxVRMutex.h" + +#if defined(XP_MACOSX) +# include <sys/mman.h> +# include <sys/stat.h> /* For mode constants */ +# include <fcntl.h> /* For O_* constants */ +#elif defined(MOZ_WIDGET_ANDROID) +# include "GeckoVRManager.h" +#endif + +#if !defined(XP_WIN) +# include <unistd.h> // for ::sleep +#endif + +using namespace mozilla::gfx; + +#ifdef XP_WIN +static const char* kShmemName = "moz.gecko.vr_ext." SHMEM_VERSION; +static LPCTSTR kMutexName = TEXT("mozilla::vr::ShmemMutex" SHMEM_VERSION); +#elif defined(XP_MACOSX) +static const char* kShmemName = "/moz.gecko.vr_ext." SHMEM_VERSION; +#endif // XP_WIN + +#if !defined(MOZ_WIDGET_ANDROID) +namespace { +void YieldThread() { +# if defined(XP_WIN) + ::Sleep(0); +# else + ::sleep(0); +# endif +} +} // anonymous namespace +#endif // !defined(MOZ_WIDGET_ANDROID) + +VRShMem::VRShMem(volatile VRExternalShmem* aShmem, bool aRequiresMutex) + : mExternalShmem(aShmem), + mIsSharedExternalShmem(aShmem != nullptr) +#if defined(XP_WIN) + , + mRequiresMutex(aRequiresMutex) +#endif +#if defined(XP_MACOSX) + , + mShmemFD(0) +#elif defined(XP_WIN) + , + mShmemFile(nullptr), + mMutex(nullptr) +#endif +{ + // Regarding input parameters, + // - aShmem is null for VRManager or for VRService in multi-proc + // - aShmem is !null for VRService in-proc (i.e., no VR proc) +} + +// Note: This function should only be called for in-proc scenarios, where the +// shared memory is only shared within the same proc (rather than across +// processes). Also, this local heap memory's lifetime is tied to the class. +// Callers to this must ensure that its reference doesn't outlive the owning +// VRShMem instance. +volatile VRExternalShmem* VRShMem::GetExternalShmem() const { +#if defined(XP_MACOSX) + MOZ_ASSERT(mShmemFD == 0); +#elif defined(XP_WIN) + MOZ_ASSERT(mShmemFile == nullptr); +#endif + return mExternalShmem; +} + +bool VRShMem::IsDisplayStateShutdown() const { + // adapted from VRService::Refresh + // Does this need the mutex for getting .shutdown? + return mExternalShmem != nullptr && + mExternalShmem->state.displayState.shutdown; +} + +// This method returns true when there is a Shmem struct allocated and +// when there is a shmem handle from the OS. This implies that the struct +// is mapped to shared memory rather than being allocated on the heap by +// this process. +bool VRShMem::IsCreatedOnSharedMemory() const { +#if defined(XP_MACOSX) + return HasExternalShmem() && (mShmemFD != 0); +#elif defined(XP_WIN) + return HasExternalShmem() && (mShmemFile != nullptr); +#else + // VRShMem does not support system shared memory on remaining platformss + return false; +#endif +} + +// CreateShMem allocates system shared memory for mExternalShmem and +// synchronization primitives to protect it. +// Callers/Processes to CreateShMem should followup with CloseShMem +void VRShMem::CreateShMem(bool aCreateOnSharedMemory) { + if (HasExternalShmem()) { + MOZ_ASSERT(mIsSharedExternalShmem && !IsCreatedOnSharedMemory()); + return; + } +#if defined(XP_WIN) + if (mMutex == nullptr) { + mMutex = CreateMutex(nullptr, // default security descriptor + false, // mutex not owned + kMutexName); // object name + if (mMutex == nullptr) { +# ifdef MOZILLA_INTERNAL_API + nsAutoCString msg; + msg.AppendPrintf("VRManager CreateMutex error \"%lu\".", GetLastError()); + NS_WARNING(msg.get()); +# endif + MOZ_ASSERT(false); + return; + } + // At xpcshell extension tests, it creates multiple VRManager + // instances in plug-contrainer.exe. It causes GetLastError() return + // `ERROR_ALREADY_EXISTS`. However, even though `ERROR_ALREADY_EXISTS`, it + // still returns the same mutex handle. + // + // https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa + MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS); + } +#endif // XP_WIN +#if !defined(MOZ_WIDGET_ANDROID) + // The VR Service accesses all hardware from a separate process + // and replaces the other VRManager when enabled. + // If the VR process is not enabled, create an in-process VRService. + if (!aCreateOnSharedMemory) { + MOZ_ASSERT(mExternalShmem == nullptr); + // If the VR process is disabled, attempt to create a + // VR service within the current process on the heap + mExternalShmem = new VRExternalShmem(); + ClearShMem(); + return; + } +#endif + + MOZ_ASSERT(aCreateOnSharedMemory); + +#if defined(XP_MACOSX) + if (mShmemFD == 0) { + mShmemFD = + shm_open(kShmemName, O_RDWR, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH); + } + if (mShmemFD <= 0) { + mShmemFD = 0; + return; + } + + struct stat sb; + fstat(mShmemFD, &sb); + off_t length = sb.st_size; + if (length < (off_t)sizeof(VRExternalShmem)) { + // TODO - Implement logging (Bug 1558912) + CloseShMem(); + return; + } + + mExternalShmem = (VRExternalShmem*)mmap(NULL, length, PROT_READ | PROT_WRITE, + MAP_SHARED, mShmemFD, 0); + if (mExternalShmem == MAP_FAILED) { + // TODO - Implement logging (Bug 1558912) + mExternalShmem = NULL; + CloseShMem(); + return; + } + +#elif defined(XP_WIN) + if (mShmemFile == nullptr) { + mShmemFile = + CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, + sizeof(VRExternalShmem), kShmemName); + MOZ_ASSERT(GetLastError() == 0 || GetLastError() == ERROR_ALREADY_EXISTS); + MOZ_ASSERT(mShmemFile); + if (mShmemFile == nullptr) { + // TODO - Implement logging (Bug 1558912) + CloseShMem(); + return; + } + } + + LARGE_INTEGER length; + length.QuadPart = sizeof(VRExternalShmem); + mExternalShmem = (VRExternalShmem*)MapViewOfFile( + mShmemFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, 0, length.QuadPart); + + if (mExternalShmem == nullptr) { + // TODO - Implement logging (Bug 1558912) + CloseShMem(); + return; + } +#elif defined(MOZ_WIDGET_ANDROID) + MOZ_ASSERT(false, + "CreateShMem should not be called for Android. Use " + "CreateShMemForAndroid instead"); +#endif +} + +// This function sets mExternalShmem in the Android/GeckoView +// scenarios where the host creates it in-memory and VRShMem +// accesses it via GeckVRManager. +void VRShMem::CreateShMemForAndroid() { +#if defined(MOZ_WIDGET_ANDROID) && defined(MOZILLA_INTERNAL_API) + mExternalShmem = + (VRExternalShmem*)mozilla::GeckoVRManager::GetExternalContext(); + if (!mExternalShmem) { + return; + } else { + mIsSharedExternalShmem = true; + } + + int32_t version = -1; + int32_t size = 0; + if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == + 0) { + version = mExternalShmem->version; + size = mExternalShmem->size; + pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex)); + } else { + return; + } + if (version != kVRExternalVersion) { + mExternalShmem = nullptr; + return; + } + if (size != sizeof(VRExternalShmem)) { + mExternalShmem = nullptr; + return; + } +#endif +} + +void VRShMem::ClearShMem() { + if (mExternalShmem != nullptr) { +#ifdef MOZILLA_INTERNAL_API + // VRExternalShmem is asserted to be POD + mExternalShmem->Clear(); +#else + memset((void*)mExternalShmem, 0, sizeof(VRExternalShmem)); +#endif + } +} + +// The cleanup corresponding to CreateShMem +void VRShMem::CloseShMem() { +#if !defined(MOZ_WIDGET_ANDROID) + if (!IsCreatedOnSharedMemory()) { + MOZ_ASSERT(!mIsSharedExternalShmem); + if (mExternalShmem) { + delete mExternalShmem; + mExternalShmem = nullptr; + } + return; + } +#endif +#if defined(XP_MACOSX) + if (mExternalShmem) { + munmap((void*)mExternalShmem, sizeof(VRExternalShmem)); + mExternalShmem = NULL; + } + if (mShmemFD) { + close(mShmemFD); + mShmemFD = 0; + } +#elif defined(XP_WIN) + if (mExternalShmem) { + UnmapViewOfFile((void*)mExternalShmem); + mExternalShmem = nullptr; + } + if (mShmemFile) { + CloseHandle(mShmemFile); + mShmemFile = nullptr; + } +#elif defined(MOZ_WIDGET_ANDROID) + mExternalShmem = NULL; +#endif + +#if defined(XP_WIN) + if (mMutex) { + MOZ_ASSERT(mRequiresMutex); + CloseHandle(mMutex); + mMutex = nullptr; + } +#endif +} + +// Called to use an existing shmem instance created by another process +// Callers to JoinShMem should call LeaveShMem for cleanup +bool VRShMem::JoinShMem() { +#if defined(XP_WIN) + if (!mMutex && mRequiresMutex) { + // Check that there are no errors before making system calls + MOZ_ASSERT(GetLastError() == 0); + + mMutex = OpenMutex(MUTEX_ALL_ACCESS, // request full access + false, // handle not inheritable + kMutexName); // object name + + if (mMutex == nullptr) { +# ifdef MOZILLA_INTERNAL_API + nsAutoCString msg; + msg.AppendPrintf("VRService OpenMutex error \"%lu\".", GetLastError()); + NS_WARNING(msg.get()); +# endif + return false; + } + MOZ_ASSERT(GetLastError() == 0); + } +#endif + + if (HasExternalShmem()) { + // An ExternalShmem is already set. No need to override and rejoin + return true; + } + +#if defined(XP_WIN) + // Opening a file-mapping object by name + base::ProcessHandle targetHandle = + OpenFileMappingA(FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + kShmemName); // name of mapping object + + MOZ_ASSERT(GetLastError() == 0); + + LARGE_INTEGER length; + length.QuadPart = sizeof(VRExternalShmem); + mExternalShmem = (VRExternalShmem*)MapViewOfFile( + reinterpret_cast<base::ProcessHandle>( + targetHandle), // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, 0, length.QuadPart); + MOZ_ASSERT(GetLastError() == 0); + + // TODO - Implement logging (Bug 1558912) + mShmemFile = targetHandle; + if (!mExternalShmem) { + MOZ_ASSERT(mExternalShmem); + return false; + } +#else + // TODO: Implement shmem for other platforms. + // + // TODO: ** Does this mean that ShMem only works in Windows for now? If so, + // let's delete the code from other platforms (Bug 1563234) + MOZ_ASSERT(false, "JoinShMem not implemented"); +#endif + return true; +} + +// The cleanup corresponding to JoinShMem +void VRShMem::LeaveShMem() { +#if defined(XP_WIN) + // Check that there are no errors before making system calls + MOZ_ASSERT(GetLastError() == 0); + + if (mShmemFile) { + ::CloseHandle(mShmemFile); + mShmemFile = nullptr; + } +#endif + + if (mExternalShmem != nullptr) { +#if defined(XP_WIN) + if (IsCreatedOnSharedMemory()) { + UnmapViewOfFile((void*)mExternalShmem); + MOZ_ASSERT(GetLastError() == 0); + } + // Otherwise, if not created on shared memory, simply null the shared + // reference to the heap object. The call to CloseShMem will appropriately + // free the allocation. +#endif + mExternalShmem = nullptr; + } +#if defined(XP_WIN) + if (mMutex) { + MOZ_ASSERT(mRequiresMutex); + CloseHandle(mMutex); + mMutex = nullptr; + } +#endif +} + +void VRShMem::PushBrowserState(VRBrowserState& aBrowserState, + bool aNotifyCond) { + if (!mExternalShmem) { + return; + } +#if defined(MOZ_WIDGET_ANDROID) + if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) == + 0) { + memcpy((void*)&(mExternalShmem->geckoState), (void*)&aBrowserState, + sizeof(VRBrowserState)); + if (aNotifyCond) { + pthread_cond_signal((pthread_cond_t*)&(mExternalShmem->geckoCond)); + } + pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)); + } +#else + bool status = true; +# if defined(XP_WIN) + WaitForMutex lock(mMutex); + status = lock.GetStatus(); +# endif // defined(XP_WIN) + if (status) { + mExternalShmem->geckoGenerationA++; + memcpy((void*)&(mExternalShmem->geckoState), (void*)&aBrowserState, + sizeof(VRBrowserState)); + mExternalShmem->geckoGenerationB++; + } +#endif // defined(MOZ_WIDGET_ANDROID) +} + +void VRShMem::PullBrowserState(mozilla::gfx::VRBrowserState& aState) { + if (!mExternalShmem) { + return; + } + // Copying the browser state from the shmem is non-blocking + // on x86/x64 architectures. Arm requires a mutex that is + // locked for the duration of the memcpy to and from shmem on + // both sides. + // On x86/x64 It is fallable -- If a dirty copy is detected by + // a mismatch of geckoGenerationA and geckoGenerationB, + // the copy is discarded and will not replace the last known + // browser state. + +#if defined(MOZ_WIDGET_ANDROID) + // TODO: This code is out-of-date and fails to compile, as + // VRService isn't compiled for Android (Bug 1563234) + /* + if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)) == + 0) { + memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState)); + pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->geckoMutex)); + } + */ + MOZ_ASSERT(false, "PullBrowserState not implemented"); +#else + bool status = true; +# if defined(XP_WIN) + if (mRequiresMutex) { + // TODO: Is this scoped lock okay? Seems like it should allow some + // race condition (Bug 1563234) + WaitForMutex lock(mMutex); + status = lock.GetStatus(); + } +# endif // defined(XP_WIN) + if (status) { + VRExternalShmem tmp; + if (mExternalShmem->geckoGenerationA != mBrowserGeneration) { + // TODO - (void *) cast removes volatile semantics. + // The memcpy is not likely to be optimized out, but is theoretically + // possible. Suggest refactoring to either explicitly enforce memory + // order or to use locks. + memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem)); + if (tmp.geckoGenerationA == tmp.geckoGenerationB && + tmp.geckoGenerationA != 0) { + memcpy(&aState, &tmp.geckoState, sizeof(VRBrowserState)); + mBrowserGeneration = tmp.geckoGenerationA; + } + } + } +#endif // defined(MOZ_WIDGET_ANDROID) +} + +void VRShMem::PushSystemState(const mozilla::gfx::VRSystemState& aState) { + if (!mExternalShmem) { + return; + } + // Copying the VR service state to the shmem is atomic, infallable, + // and non-blocking on x86/x64 architectures. Arm requires a mutex + // that is locked for the duration of the memcpy to and from shmem on + // both sides. + +#if defined(MOZ_WIDGET_ANDROID) + // TODO: This code is out-of-date and fails to compile, as + // VRService isn't compiled for Android (Bug 1563234) + MOZ_ASSERT(false, "JoinShMem not implemented"); + /* + if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == + 0) { + // We are casting away the volatile keyword, which is not accepted by + // memcpy. It is possible (although very unlikely) that the compiler + // may optimize out the memcpy here as memcpy isn't explicitly safe for + // volatile memory in the C++ standard. + memcpy((void*)&mExternalShmem->state, &aState, sizeof(VRSystemState)); + pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex)); + } + */ +#else + bool lockState = true; +# if defined(XP_WIN) + if (mRequiresMutex) { + // TODO: Is this scoped lock okay? Seems like it should allow some + // race condition (Bug 1563234) + WaitForMutex lock(mMutex); + lockState = lock.GetStatus(); + } +# endif // defined(XP_WIN) + if (lockState) { + mExternalShmem->generationA++; + memcpy((void*)&mExternalShmem->state, &aState, sizeof(VRSystemState)); + mExternalShmem->generationB++; + } +#endif // defined(MOZ_WIDGET_ANDROID) +} + +#if defined(MOZ_WIDGET_ANDROID) +void VRShMem::PullSystemState( + VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState, + VRControllerState (&aControllerState)[kVRControllerMaxCount], + bool& aEnumerationCompleted, + const std::function<bool()>& aWaitCondition /* = nullptr */) { + if (!mExternalShmem) { + return; + } + bool done = false; + while (!done) { + if (pthread_mutex_lock((pthread_mutex_t*)&(mExternalShmem->systemMutex)) == + 0) { + while (true) { + memcpy(&aDisplayState, (void*)&(mExternalShmem->state.displayState), + sizeof(VRDisplayState)); + memcpy(&aSensorState, (void*)&(mExternalShmem->state.sensorState), + sizeof(VRHMDSensorState)); + memcpy(aControllerState, + (void*)&(mExternalShmem->state.controllerState), + sizeof(VRControllerState) * kVRControllerMaxCount); + aEnumerationCompleted = mExternalShmem->state.enumerationCompleted; + if (!aWaitCondition || aWaitCondition()) { + done = true; + break; + } + // Block current thead using the condition variable until data + // changes + pthread_cond_wait((pthread_cond_t*)&mExternalShmem->systemCond, + (pthread_mutex_t*)&mExternalShmem->systemMutex); + } // while (true) + pthread_mutex_unlock((pthread_mutex_t*)&(mExternalShmem->systemMutex)); + } else if (!aWaitCondition) { + // pthread_mutex_lock failed and we are not waiting for a condition to + // exit from PullState call. + return; + } + } // while (!done) { +} +#else +void VRShMem::PullSystemState( + VRDisplayState& aDisplayState, VRHMDSensorState& aSensorState, + VRControllerState (&aControllerState)[kVRControllerMaxCount], + bool& aEnumerationCompleted, + const std::function<bool()>& aWaitCondition /* = nullptr */) { + MOZ_ASSERT(mExternalShmem); + if (!mExternalShmem) { + return; + } + while (true) { + { // Scope for WaitForMutex +# if defined(XP_WIN) + bool status = true; + WaitForMutex lock(mMutex); + status = lock.GetStatus(); + if (status) { +# endif // defined(XP_WIN) + VRExternalShmem tmp; + memcpy(&tmp, (void*)mExternalShmem, sizeof(VRExternalShmem)); + bool isCleanCopy = + tmp.generationA == tmp.generationB && tmp.generationA != 0; + if (isCleanCopy) { + memcpy(&aDisplayState, &tmp.state.displayState, + sizeof(VRDisplayState)); + memcpy(&aSensorState, &tmp.state.sensorState, + sizeof(VRHMDSensorState)); + memcpy(aControllerState, + (void*)&(mExternalShmem->state.controllerState), + sizeof(VRControllerState) * kVRControllerMaxCount); + aEnumerationCompleted = mExternalShmem->state.enumerationCompleted; + // Check for wait condition + if (!aWaitCondition || aWaitCondition()) { + return; + } + } else if (!aWaitCondition) { + // We did not get a clean copy, and we are not waiting for a condition + // to exit from PullState call. + return; + } + // Yield the thread while polling + YieldThread(); +# if defined(XP_WIN) + } else if (!aWaitCondition) { + // WaitForMutex failed and we are not waiting for a condition to + // exit from PullState call. + return; + } +# endif // defined(XP_WIN) + } // End: Scope for WaitForMutex + // Yield the thread while polling + YieldThread(); + } // while (!true) +} +#endif // defined(MOZ_WIDGET_ANDROID) + +void VRShMem::PushWindowState(VRWindowState& aState) { +#if defined(XP_WIN) + if (!mExternalShmem) { + return; + } + + bool status = true; + WaitForMutex lock(mMutex); + status = lock.GetStatus(); + if (status) { + memcpy((void*)&(mExternalShmem->windowState), (void*)&aState, + sizeof(VRWindowState)); + } +#endif // defined(XP_WIN) +} + +void VRShMem::PullWindowState(VRWindowState& aState) { +#if defined(XP_WIN) + if (!mExternalShmem) { + return; + } + + bool status = true; + WaitForMutex lock(mMutex); + status = lock.GetStatus(); + if (status) { + memcpy((void*)&aState, (void*)&(mExternalShmem->windowState), + sizeof(VRWindowState)); + } +#endif // defined(XP_WIN) +} + +void VRShMem::PushTelemetryState(VRTelemetryState& aState) { +#if defined(XP_WIN) + if (!mExternalShmem) { + return; + } + + bool status = true; + WaitForMutex lock(mMutex); + status = lock.GetStatus(); + if (status) { + memcpy((void*)&(mExternalShmem->telemetryState), (void*)&aState, + sizeof(VRTelemetryState)); + } +#endif // defined(XP_WIN) +} +void VRShMem::PullTelemetryState(VRTelemetryState& aState) { +#if defined(XP_WIN) + if (!mExternalShmem) { + return; + } + + bool status = true; + WaitForMutex lock(mMutex); + status = lock.GetStatus(); + if (status) { + memcpy((void*)&aState, (void*)&(mExternalShmem->telemetryState), + sizeof(VRTelemetryState)); + } +#endif // defined(XP_WIN) +} + +void VRShMem::SendEvent(uint64_t aWindowID, + mozilla::gfx::VRFxEventType aEventType, + mozilla::gfx::VRFxEventState aEventState) { + MOZ_ASSERT(!HasExternalShmem()); + if (JoinShMem()) { + mozilla::gfx::VRWindowState windowState = {0}; + PullWindowState(windowState); + windowState.windowID = aWindowID; + windowState.eventType = aEventType; + windowState.eventState = aEventState; + PushWindowState(windowState); + LeaveShMem(); + +#if defined(XP_WIN) + // Notify the waiting host process that the data is now available + HANDLE hSignal = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess + FALSE, // bInheritHandle + windowState.signalName // lpName + ); + ::SetEvent(hSignal); + ::CloseHandle(hSignal); +#endif // defined(XP_WIN) + } +} + +void VRShMem::SendIMEState(uint64_t aWindowID, + mozilla::gfx::VRFxEventState aEventState) { + SendEvent(aWindowID, mozilla::gfx::VRFxEventType::IME, aEventState); +} + +void VRShMem::SendFullscreenState(uint64_t aWindowID, bool aFullscreen) { + SendEvent(aWindowID, mozilla::gfx::VRFxEventType::FULLSCREEN, + aFullscreen ? mozilla::gfx::VRFxEventState::FULLSCREEN_ENTER + : mozilla::gfx::VRFxEventState::FULLSCREEN_EXIT); +} + +// Note: this should be called from the VRShMem instance that created +// the external shmem rather than joined it. +void VRShMem::SendShutdowmState(uint64_t aWindowID) { + MOZ_ASSERT(HasExternalShmem()); + + mozilla::gfx::VRWindowState windowState = {0}; + PullWindowState(windowState); + windowState.windowID = aWindowID; + windowState.eventType = mozilla::gfx::VRFxEventType::SHUTDOWN; + PushWindowState(windowState); + +#if defined(XP_WIN) + // Notify the waiting host process that the data is now available + HANDLE hSignal = ::OpenEventA(EVENT_ALL_ACCESS, // dwDesiredAccess + FALSE, // bInheritHandle + windowState.signalName // lpName + ); + ::SetEvent(hSignal); + ::CloseHandle(hSignal); +#endif // defined(XP_WIN) +} |