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.cpp332
1 files changed, 206 insertions, 126 deletions
diff --git a/support/hololens/ServoApp/BrowserPage.cpp b/support/hololens/ServoApp/BrowserPage.cpp
index 2c2c6c6420b..c6fe3d9b969 100644
--- a/support/hololens/ServoApp/BrowserPage.cpp
+++ b/support/hololens/ServoApp/BrowserPage.cpp
@@ -16,96 +16,59 @@ 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";
+using namespace servo;
namespace winrt::ServoApp::implementation {
BrowserPage::BrowserPage() {
- InitializeComponent();
log("BrowserPage::BrowserPage()");
+ InitializeComponent();
Loaded(std::bind(&BrowserPage::OnPageLoaded, this, _1, _2));
Window::Current().CoreWindow().VisibilityChanged(
std::bind(&BrowserPage::OnVisibilityChanged, this, _1, _2));
}
+void BrowserPage::Shutdown() {
+ log("BrowserPage::Shutdown()");
+
+ if (mServo != nullptr) {
+ if (!IsLoopRunning()) {
+ // FIXME: this should not happen. In that case, we can't send the
+ // shutdown event to Servo.
+ } else {
+ HANDLE hEvent = ::CreateEventA(nullptr, FALSE, FALSE, sShutdownEvent);
+ SendEventToServo({{Event::SHUTDOWN}});
+ log("Waiting for Servo to shutdown");
+ ::WaitForSingleObject(hEvent, INFINITE);
+ StopRenderLoop();
+ mServo.reset(); // will call servo::deinit
+ }
+ }
+}
+
void BrowserPage::OnPageLoaded(IInspectable const &, RoutedEventArgs const &) {
log("BrowserPage::OnPageLoaded()");
CreateRenderSurface();
StartRenderLoop();
-
swapChainPanel().PointerReleased(
std::bind(&BrowserPage::OnSurfaceClicked, this, _1, _2));
-
swapChainPanel().ManipulationDelta(
std::bind(&BrowserPage::OnSurfaceManipulationDelta, this, _1, _2));
}
-void BrowserPage::OnSurfaceManipulationDelta(
- IInspectable const &, Input::ManipulationDeltaRoutedEventArgs const &e) {
- auto x = e.Position().X;
- auto y = e.Position().Y;
- auto dx = e.Delta().Translation.X;
- auto dy = e.Delta().Translation.Y;
- Event event = {{Event::SCROLL}};
- event.scrollCoords = {dx, dy, x, y};
- SendEventToServo(event);
- e.Handled(true);
-}
-
-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();
- }
+
+ // FIXME: for now, this is disabled as we get this message before shutdown,
+ // stopping the event loop, which we can't recover from yet (see comment in
+ // Loop())
+
+ // if (visible && !IsLoopRunning()) {
+ // StartRenderLoop();
+ //}
+ // if (!visible && IsLoopRunning()) {
+ // StopRenderLoop();
+ //}
}
void BrowserPage::CreateRenderSurface() {
@@ -127,6 +90,8 @@ void BrowserPage::RecoverFromLostDevice() {
StartRenderLoop();
}
+/**** GL THREAD LOOP ****/
+
bool BrowserPage::IsLoopRunning() {
return mLoopTask != nullptr && !mLoopTask->is_done();
}
@@ -136,65 +101,35 @@ void BrowserPage::Loop(cancellation_token cancel) {
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);
+
+ if (mServo == nullptr) {
+ log("Entering loop");
+ ServoDelegate *sd = static_cast<ServoDelegate *>(this);
+ mServo = std::make_unique<Servo>(panelWidth, panelHeight, *sd);
+ } else {
+ // FIXME: this will fail since create_task didn't pick the thread
+ // where Servo was running initially.
+ throw winrt::hresult_error(E_FAIL, L"Recovering loop unimplemented");
+ }
// mServo->SetBatchMode(true);
- // FIXME: ^ this should be necessary as call perform_update
+ // FIXME: ^ this should be necessary as we 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.
+ log("Entering loop");
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) {
+ // Block until wakeup is called.
+ // Or run full speed if animating (see OnAnimatingChanged),
+ // it will endup blocking on Flush to limit rendering to 60FPS
+ if (!mAnimating) {
::WaitForSingleObject(hEvent, INFINITE);
}
mEventsMutex.lock();
@@ -216,31 +151,36 @@ void BrowserPage::Loop(cancellation_token cancel) {
case Event::BACK:
mServo->GoBack();
break;
+ case Event::RELOAD:
+ mServo->Reload();
+ break;
+ case Event::STOP:
+ mServo->Stop();
+ break;
+ case Event::SHUTDOWN:
+ log("Requesting Servo to shutdown");
+ mServo->RequestShutdown();
+ break;
}
}
mEvents.clear();
mEventsMutex.unlock();
mServo->PerformUpdates();
}
+ log("Leaving loop");
cancel_current_task();
-}
+} // namespace winrt::ServoApp::implementation
void BrowserPage::StartRenderLoop() {
if (IsLoopRunning()) {
+#if defined _DEBUG
+ throw winrt::hresult_error(E_FAIL, L"GL thread is already looping");
+#else
return;
+#endif
}
-
- 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());
-
+ auto token = mLoopCancel.get_token();
mLoopTask = std::make_unique<Concurrency::task<void>>(
Concurrency::create_task([=] { Loop(token); }, token));
}
@@ -253,4 +193,144 @@ void BrowserPage::StopRenderLoop() {
}
}
+/**** SERVO CALLBACKS ****/
+
+void BrowserPage::OnLoadStarted() {
+ RunOnUIThread([=] {
+ reloadButton().IsEnabled(false);
+ stopButton().IsEnabled(true);
+ });
+}
+
+void BrowserPage::OnLoadEnded() {
+ RunOnUIThread([=] {
+ reloadButton().IsEnabled(true);
+ stopButton().IsEnabled(false);
+ });
+}
+
+void BrowserPage::OnHistoryChanged(bool back, bool forward) {
+ RunOnUIThread([=] {
+ backButton().IsEnabled(back);
+ forwardButton().IsEnabled(forward);
+ });
+}
+
+void BrowserPage::OnShutdownComplete() {
+ log("Servo notified ShutdownComplete");
+ HANDLE hEvent = ::OpenEventA(EVENT_ALL_ACCESS, FALSE, sShutdownEvent);
+ ::SetEvent(hEvent);
+}
+
+void BrowserPage::OnAlert(std::wstring message) {
+ // FIXME: make this sync
+ RunOnUIThread([=] {
+ Windows::UI::Popups::MessageDialog msg{message};
+ msg.ShowAsync();
+ });
+}
+
+void BrowserPage::OnTitleChanged(std::wstring title) {
+ RunOnUIThread([=] { ApplicationView::GetForCurrentView().Title(title); });
+}
+
+void BrowserPage::OnURLChanged(std::wstring url) {
+ RunOnUIThread([=] { urlTextbox().Text(url); });
+}
+
+void BrowserPage::Flush() {
+ 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.
+ RunOnUIThread([=] { RecoverFromLostDevice(); });
+ }
+}
+
+void BrowserPage::MakeCurrent() { mOpenGLES.MakeCurrent(mRenderSurface); }
+
+void BrowserPage::WakeUp() {
+ // 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);
+}
+
+bool BrowserPage::OnAllowNavigation(std::wstring) { return true; }
+
+void BrowserPage::OnAnimatingChanged(bool animating) { mAnimating = animating; }
+
+template <typename Callable> void BrowserPage::RunOnUIThread(Callable cb) {
+ swapChainPanel().Dispatcher().RunAsync(
+ Windows::UI::Core::CoreDispatcherPriority::High, cb);
+}
+
+/**** USER INTERACTIONS WITH UI ****/
+
+void BrowserPage::OnBackButtonClicked(IInspectable const &,
+ RoutedEventArgs const &) {
+ SendEventToServo({{Event::BACK}});
+}
+
+void BrowserPage::OnForwardButtonClicked(IInspectable const &,
+ RoutedEventArgs const &) {
+ SendEventToServo({{Event::FORWARD}});
+}
+
+void BrowserPage::OnReloadButtonClicked(IInspectable const &,
+ RoutedEventArgs const &) {
+ SendEventToServo({{Event::RELOAD}});
+}
+
+void BrowserPage::OnStopButtonClicked(IInspectable const &,
+ RoutedEventArgs const &) {
+ SendEventToServo({{Event::STOP}});
+}
+
+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::OnSurfaceManipulationDelta(
+ IInspectable const &, Input::ManipulationDeltaRoutedEventArgs const &e) {
+ auto x = e.Position().X;
+ auto y = e.Position().Y;
+ auto dx = e.Delta().Translation.X;
+ auto dy = e.Delta().Translation.Y;
+ Event event = {{Event::SCROLL}};
+ event.scrollCoords = {dx, dy, x, y};
+ SendEventToServo(event);
+ e.Handled(true);
+}
+
+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();
+ WakeUp();
+}
+
} // namespace winrt::ServoApp::implementation