gecko-dev/gfx/vr/vrhost/vrhostapi.cpp
Daosheng Mu 3512e86bbb Bug 1590911 - Forwarding fullscreen events to VR host API. r=thomasmo,kip,smaug,PhilipLamb
We wanna forward fullscreen enter/exit events to VR host.

Differential Revision: https://phabricator.services.mozilla.com/D50371

--HG--
extra : moz-landing-system : lando
2019-10-25 07:43:27 +00:00

297 lines
9.5 KiB
C++

/* -*- 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/. */
// vrhostapi.cpp
// Definition of functions that are exported from this dll
#include "vrhostex.h"
#include "VRShMem.h"
#include <stdio.h>
#include <string.h>
#include <random>
#include "windows.h"
// VRWindowManager adds a level of indirection so that system HWND isn't exposed
// outside of these APIs
class VRWindowManager {
public:
HWND GetHWND(uint32_t nId) {
if (nId == nWindow) {
return hWindow;
} else {
return nullptr;
}
}
uint32_t GetId(HWND hwnd) {
if (hwnd == hWindow) {
return nWindow;
} else {
return 0;
}
}
HANDLE GetProc(uint32_t nId) {
if (nId == nWindow) {
return hProc;
} else {
return nullptr;
}
}
HANDLE GetEvent() { return hEvent; }
uint32_t SetHWND(HWND hwnd, HANDLE hproc, HANDLE hevent) {
if (hWindow == nullptr) {
MOZ_ASSERT(hwnd != nullptr && hproc != nullptr);
hWindow = hwnd;
hProc = hproc;
hEvent = hevent;
nWindow = GetRandomUInt();
#if defined(DEBUG) && defined(NIGHTLY_BUILD)
printf("VRWindowManager: Storing HWND: 0x%p as ID: 0x%X\n", hWindow,
nWindow);
#endif
return nWindow;
} else {
return -1;
}
}
uint32_t GetRandomUInt() { return randomGenerator(); }
static VRWindowManager* GetManager() {
if (Instance == nullptr) {
Instance = new VRWindowManager();
}
return Instance;
}
private:
static VRWindowManager* Instance;
// For now, simply store the ID and HWND, and expand
// to a map when multiple windows/instances are supported.
uint32_t nWindow = 0;
HWND hWindow = nullptr;
HANDLE hProc = nullptr;
HANDLE hEvent = nullptr;
std::random_device randomGenerator;
};
VRWindowManager* VRWindowManager::Instance = nullptr;
// Struct to send params to StartFirefoxThreadProc
struct StartFirefoxParams {
char* firefoxFolder;
char* firefoxProfileFolder;
HANDLE hProcessFx;
};
// Helper threadproc function for CreateVRWindow
DWORD StartFirefoxThreadProc(_In_ LPVOID lpParameter) {
char cmd[] = "%sfirefox.exe -wait-for-browser -profile %s --fxr";
StartFirefoxParams* params = static_cast<StartFirefoxParams*>(lpParameter);
char cmdWithPath[MAX_PATH + MAX_PATH] = {0};
int err = sprintf_s(cmdWithPath, ARRAYSIZE(cmdWithPath), cmd,
params->firefoxFolder, params->firefoxProfileFolder);
if (err != -1) {
PROCESS_INFORMATION procFx = {0};
STARTUPINFO startupInfoFx = {0};
#if defined(DEBUG) && defined(NIGHTLY_BUILD)
printf("Starting Firefox via: %s\n", cmdWithPath);
#endif
// Start Firefox
bool fCreateContentProc = ::CreateProcess(nullptr, // lpApplicationName,
cmdWithPath,
nullptr, // lpProcessAttributes,
nullptr, // lpThreadAttributes,
TRUE, // bInheritHandles,
0, // dwCreationFlags,
nullptr, // lpEnvironment,
nullptr, // lpCurrentDirectory,
&startupInfoFx, &procFx);
if (!fCreateContentProc) {
printf("Failed to create Firefox process");
}
params->hProcessFx = procFx.hProcess;
}
return 0;
}
class VRShmemInstance {
public:
VRShmemInstance() = delete;
VRShmemInstance(const VRShmemInstance& aRHS) = delete;
static mozilla::gfx::VRShMem& GetInstance() {
static mozilla::gfx::VRShMem shmem(nullptr, true /*aRequiresMutex*/);
return shmem;
}
};
// This export is responsible for starting up a new VR window in Firefox and
// returning data related to its creation back to the caller.
// See nsFxrCommandLineHandler::Handle for more details about the bootstrapping
// process with Firefox.
void CreateVRWindow(char* firefoxFolderPath, char* firefoxProfilePath,
uint32_t dxgiAdapterID, uint32_t widthHost,
uint32_t heightHost, uint32_t* windowId, void** hTex,
uint32_t* width, uint32_t* height) {
mozilla::gfx::VRWindowState windowState = {0};
int err = sprintf_s(windowState.signalName, ARRAYSIZE(windowState.signalName),
"fxr::CreateVRWindow::%X",
VRWindowManager::GetManager()->GetRandomUInt());
if (err > 0) {
HANDLE hEvent = ::CreateEventA(nullptr, // attributes
FALSE, // bManualReset
FALSE, // bInitialState
windowState.signalName);
if (hEvent != nullptr) {
// Create Shmem and push state
VRShmemInstance::GetInstance().CreateShMem(
true /*aCreateOnSharedMemory*/);
VRShmemInstance::GetInstance().PushWindowState(windowState);
// Start Firefox in another thread so that this thread can wait for the
// window state to be updated during Firefox startup
StartFirefoxParams fxParams = {0};
fxParams.firefoxFolder = firefoxFolderPath;
fxParams.firefoxProfileFolder = firefoxProfilePath;
DWORD dwTid = 0;
HANDLE hThreadFx = CreateThread(nullptr, 0, StartFirefoxThreadProc,
&fxParams, 0, &dwTid);
if (hThreadFx != nullptr) {
// Wait for Firefox to populate rest of window state
::WaitForSingleObject(hEvent, INFINITE);
// Update local WindowState with data from Firefox
VRShmemInstance::GetInstance().PullWindowState(windowState);
(*hTex) = windowState.textureFx;
(*windowId) = VRWindowManager::GetManager()->SetHWND(
(HWND)windowState.hwndFx, fxParams.hProcessFx, hEvent);
(*width) = windowState.widthFx;
(*height) = windowState.heightFx;
} else {
// How do I failfast?
}
}
}
}
// Keep track of when WaitForVREvent is running to manage shutdown of
// this vrhost. See CloseVRWindow for more details.
volatile bool s_WaitingForVREvent = false;
// Blocks until a new event is set on the shmem.
// Note: this function can be called from any thread.
void WaitForVREvent(uint32_t& nVRWindowID, uint32_t& eventType,
uint32_t& eventData1, uint32_t& eventData2) {
MOZ_ASSERT(!s_WaitingForVREvent);
s_WaitingForVREvent = true;
// Initialize all of the out params
nVRWindowID = 0;
eventType = 0;
eventData1 = 0;
eventData2 = 0;
if (VRShmemInstance::GetInstance().HasExternalShmem()) {
HANDLE evt = VRWindowManager::GetManager()->GetEvent();
const DWORD waitResult = ::WaitForSingleObject(evt, INFINITE);
if (waitResult != WAIT_OBJECT_0) {
MOZ_ASSERT(false && "Error WaitForVREvent().\n");
return;
}
mozilla::gfx::VRWindowState windowState = {0};
VRShmemInstance::GetInstance().PullWindowState(windowState);
nVRWindowID =
VRWindowManager::GetManager()->GetId((HWND)windowState.hwndFx);
if (nVRWindowID != 0) {
eventType = (uint32_t)windowState.eventType;
mozilla::gfx::VRFxEventType fxEvent =
mozilla::gfx::VRFxEventType(eventType);
switch (fxEvent) {
case mozilla::gfx::VRFxEventType::IME:
eventData1 = (uint32_t)windowState.eventState;
break;
case mozilla::gfx::VRFxEventType::FULLSCREEN:
eventData1 = (uint32_t)windowState.eventState;
break;
case mozilla::gfx::VRFxEventType::SHUTDOWN:
VRShmemInstance::GetInstance().CloseShMem();
break;
default:
MOZ_ASSERT(false && "Undefined VR Fx event.");
break;
}
}
}
s_WaitingForVREvent = false;
}
// Sends a message to the VR window to close.
void CloseVRWindow(uint32_t nVRWindowID, bool waitForTerminate) {
HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID);
if (hwnd != nullptr) {
::SendMessage(hwnd, WM_CLOSE, 0, 0);
if (waitForTerminate) {
// Wait for Firefox main process to exit
::WaitForSingleObject(VRWindowManager::GetManager()->GetProc(nVRWindowID),
INFINITE);
}
}
// If a thread is currently blocked on WaitForVREvent, then defer closing the
// shmem to that thread by sending an async event.
// If WaitForVREvent is not running, it is safe to close the shmem now.
// Subsequent calls to WaitForVREvent will return early because it does not
// have an external shmem.
if (s_WaitingForVREvent) {
VRShmemInstance::GetInstance().SendShutdowmState(nVRWindowID);
} else {
VRShmemInstance::GetInstance().CloseShMem();
}
}
// Forwards Win32 UI window messages to the Firefox widget/window associated
// with nVRWindowID. Note that not all message type is supported (only those
// allowed in the switch block below).
void SendUIMessageToVRWindow(uint32_t nVRWindowID, uint32_t msg,
uint64_t wparam, uint64_t lparam) {
HWND hwnd = VRWindowManager::GetManager()->GetHWND(nVRWindowID);
if (hwnd != nullptr) {
switch (msg) {
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_MOUSEWHEEL:
case WM_CHAR:
case WM_KEYDOWN:
case WM_KEYUP:
::PostMessage(hwnd, msg, wparam, lparam);
break;
default:
break;
}
}
}