aboutsummaryrefslogtreecommitdiffstats
path: root/support/hololens/ServoApp/BrowserPage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'support/hololens/ServoApp/BrowserPage.cpp')
-rw-r--r--support/hololens/ServoApp/BrowserPage.cpp236
1 files changed, 236 insertions, 0 deletions
diff --git a/support/hololens/ServoApp/BrowserPage.cpp b/support/hololens/ServoApp/BrowserPage.cpp
new file mode 100644
index 00000000000..6130574aaef
--- /dev/null
+++ b/support/hololens/ServoApp/BrowserPage.cpp
@@ -0,0 +1,236 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#include "pch.h"
+#include "logs.h"
+#include "BrowserPage.h"
+#include "BrowserPage.g.cpp"
+#include "ImmersiveView.h"
+#include "OpenGLES.h"
+
+using namespace std::placeholders;
+using namespace winrt::Windows::UI::Xaml;
+using namespace winrt::Windows::UI::Core;
+using namespace winrt::Windows::UI::ViewManagement;
+using namespace winrt::Windows::Foundation;
+using namespace winrt::Windows::Graphics::Holographic;
+using namespace concurrency;
+
+static char sWakeupEvent[] = "SIGNAL_WAKEUP";
+
+namespace winrt::ServoApp::implementation {
+BrowserPage::BrowserPage() {
+ InitializeComponent();
+ log("BrowserPage::BrowserPage()");
+ Loaded(std::bind(&BrowserPage::OnPageLoaded, this, _1, _2));
+ Window::Current().CoreWindow().VisibilityChanged(
+ std::bind(&BrowserPage::OnVisibilityChanged, this, _1, _2));
+}
+
+void BrowserPage::OnPageLoaded(IInspectable const &, RoutedEventArgs const &) {
+ log("BrowserPage::OnPageLoaded()");
+ CreateRenderSurface();
+ StartRenderLoop();
+
+ swapChainPanel().PointerReleased(
+ std::bind(&BrowserPage::OnSurfaceClicked, this, _1, _2));
+}
+
+void BrowserPage::OnSurfaceClicked(IInspectable const &,
+ Input::PointerRoutedEventArgs const &e) {
+ auto coords = e.GetCurrentPoint(swapChainPanel());
+ auto x = coords.Position().X;
+ auto y = coords.Position().Y;
+
+ SendEventToServo({{Event::CLICK}, {x, y}});
+
+ e.Handled(true);
+}
+
+void BrowserPage::SendEventToServo(Event event) {
+ mEventsMutex.lock();
+ mEvents.push_back(event);
+ mEventsMutex.unlock();
+ Servo::sWakeUp();
+}
+
+void BrowserPage::OnBackButtonClicked(IInspectable const &,
+ RoutedEventArgs const &) {
+ SendEventToServo({{Event::BACK}});
+}
+
+void BrowserPage::OnForwardButtonClicked(IInspectable const &,
+ RoutedEventArgs const &) {
+ SendEventToServo({{Event::FORWARD}});
+}
+void BrowserPage::OnImmersiveButtonClicked(IInspectable const &,
+ RoutedEventArgs const &) {
+ if (HolographicSpace::IsAvailable()) {
+ log("Holographic space available");
+ auto v =
+ winrt::Windows::ApplicationModel::Core::CoreApplication::CreateNewView(
+ mImmersiveViewSource);
+ auto parentId = ApplicationView::GetForCurrentView().Id();
+ v.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [=]() {
+ auto winId = ApplicationView::GetForCurrentView().Id();
+ ApplicationViewSwitcher::SwitchAsync(winId, parentId);
+ log("Immersive view started");
+ });
+ } else {
+ log("Holographic space not available");
+ }
+}
+
+void BrowserPage::OnVisibilityChanged(CoreWindow const &,
+ VisibilityChangedEventArgs const &args) {
+ auto visible = args.Visible();
+ if (visible && !IsLoopRunning()) {
+ StartRenderLoop();
+ }
+ if (!visible && IsLoopRunning()) {
+ StopRenderLoop();
+ }
+}
+
+void BrowserPage::CreateRenderSurface() {
+ if (mRenderSurface == EGL_NO_SURFACE) {
+ mRenderSurface = mOpenGLES.CreateSurface(swapChainPanel());
+ }
+}
+
+void BrowserPage::DestroyRenderSurface() {
+ mOpenGLES.DestroySurface(mRenderSurface);
+ mRenderSurface = EGL_NO_SURFACE;
+}
+
+void BrowserPage::RecoverFromLostDevice() {
+ StopRenderLoop();
+ DestroyRenderSurface();
+ mOpenGLES.Reset();
+ CreateRenderSurface();
+ StartRenderLoop();
+}
+
+bool BrowserPage::IsLoopRunning() {
+ return mLoopTask != nullptr && !mLoopTask->is_done();
+}
+
+void BrowserPage::Loop(cancellation_token cancel) {
+ log("BrowserPage::Loop(). GL thread: %i", GetCurrentThreadId());
+
+ HANDLE hEvent = ::CreateEventA(nullptr, FALSE, FALSE, sWakeupEvent);
+
+ Servo::sOnAlert = [=](std::wstring message) {
+ // FIXME: make this sync
+ swapChainPanel().Dispatcher().RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::High, [=]() {
+ Windows::UI::Popups::MessageDialog msg{message};
+ msg.ShowAsync();
+ });
+ };
+
+ Servo::sOnTitleChanged = [=](std::wstring title) {
+ swapChainPanel().Dispatcher().RunAsync(CoreDispatcherPriority::High, [=]() {
+ ApplicationView::GetForCurrentView().Title(title);
+ });
+ };
+
+ Servo::sOnURLChanged = [=](std::wstring url) {
+ swapChainPanel().Dispatcher().RunAsync(CoreDispatcherPriority::High,
+ [=]() { urlTextbox().Text(url); });
+ };
+
+ Servo::sMakeCurrent = [=]() {
+ /* EGLint panelWidth = 0; */
+ /* EGLint panelHeight = 0; */
+ /* mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth,
+ * &panelHeight); */
+ /* glViewport(0, 0, panelWidth, panelHeight); */
+ /* mServo->SetSize(panelWidth, panelHeight); */
+ mOpenGLES.MakeCurrent(mRenderSurface);
+ };
+
+ Servo::sFlush = [=]() {
+ if (mOpenGLES.SwapBuffers(mRenderSurface) != GL_TRUE) {
+ // The call to eglSwapBuffers might not be successful (i.e. due to Device
+ // Lost) If the call fails, then we must reinitialize EGL and the GL
+ // resources.
+ swapChainPanel().Dispatcher().RunAsync(
+ CoreDispatcherPriority::High, [this]() { RecoverFromLostDevice(); });
+ }
+ };
+
+ mOpenGLES.MakeCurrent(mRenderSurface);
+
+ EGLint panelWidth = 0;
+ EGLint panelHeight = 0;
+ mOpenGLES.GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
+ glViewport(0, 0, panelWidth, panelHeight);
+ mServo = std::make_unique<Servo>(panelWidth, panelHeight);
+
+ // mServo->SetBatchMode(true);
+ // FIXME: ^ this should be necessary as call perform_update
+ // ourself. But enabling batch mode will make clicking a link
+ // not working because during the click, this thread is not
+ // waiting on the hEvent object. See the "wakeup" comment.
+
+ while (!cancel.is_canceled()) {
+ // Block until Servo::sWakeUp is called.
+ // Or run full speed if animating (see on_animating_changed),
+ // it will endup blocking on SwapBuffers to limit rendering to 60FPS
+ if (!Servo::sAnimating) {
+ ::WaitForSingleObject(hEvent, INFINITE);
+ }
+ mEventsMutex.lock();
+ for (auto &&e : mEvents) {
+ switch (e.type) {
+ case Event::CLICK: {
+ auto [x, y] = e.coords;
+ mServo->Click(x, y);
+ break;
+ }
+ case Event::FORWARD:
+ mServo->GoForward();
+ break;
+ case Event::BACK:
+ mServo->GoBack();
+ break;
+ }
+ }
+ mEvents.clear();
+ mEventsMutex.unlock();
+ mServo->PerformUpdates();
+ }
+ cancel_current_task();
+}
+
+void BrowserPage::StartRenderLoop() {
+ if (IsLoopRunning()) {
+ return;
+ }
+
+ auto token = mLoopCancel.get_token();
+
+ Servo::sWakeUp = []() {
+ // FIXME: this won't work if it's triggered while the thread is not
+ // waiting. We need a better looping logic.
+ HANDLE hEvent = ::OpenEventA(EVENT_ALL_ACCESS, FALSE, sWakeupEvent);
+ ::SetEvent(hEvent);
+ };
+
+ log("BrowserPage::StartRenderLoop(). UI thread: %i", GetCurrentThreadId());
+
+ mLoopTask = std::make_unique<Concurrency::task<void>>(
+ Concurrency::create_task([=] { Loop(token); }, token));
+}
+
+void BrowserPage::StopRenderLoop() {
+ if (IsLoopRunning()) {
+ mLoopCancel.cancel();
+ mLoopTask->wait();
+ mLoopTask.reset();
+ }
+}
+
+} // namespace winrt::ServoApp::implementation