diff options
Diffstat (limited to 'support/hololens/ServoApp/BrowserPage.cpp')
-rw-r--r-- | support/hololens/ServoApp/BrowserPage.cpp | 332 |
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 |