diff options
author | Paul Rouget <me@paulrouget.com> | 2019-08-06 05:02:48 +0200 |
---|---|---|
committer | Paul Rouget <me@paulrouget.com> | 2019-08-19 07:12:16 +0200 |
commit | 663ec48e00636ffaf6cb39054c3cfc5580f569eb (patch) | |
tree | faf47840e877bb5b5cf88ec9b021fe83b8ef329b /support/hololens | |
parent | 3658a8cc591ef4ca827ce1cda9565a1bca7d7b3c (diff) | |
download | servo-663ec48e00636ffaf6cb39054c3cfc5580f569eb.tar.gz servo-663ec48e00636ffaf6cb39054c3cfc5580f569eb.zip |
Move Servo into a Control Template
Diffstat (limited to 'support/hololens')
-rw-r--r-- | support/hololens/ServoApp/BrowserPage.cpp | 266 | ||||
-rw-r--r-- | support/hololens/ServoApp/BrowserPage.h | 59 | ||||
-rw-r--r-- | support/hololens/ServoApp/BrowserPage.xaml | 13 | ||||
-rw-r--r-- | support/hololens/ServoApp/Servo.cpp | 32 | ||||
-rw-r--r-- | support/hololens/ServoApp/Servo.h | 31 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoApp.vcxproj | 6 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoApp.vcxproj.filters | 31 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoControl.cpp | 257 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoControl.h | 110 | ||||
-rw-r--r-- | support/hololens/ServoApp/ServoControl.idl | 20 | ||||
-rw-r--r-- | support/hololens/ServoApp/Themes/Generic.xaml | 19 |
11 files changed, 516 insertions, 328 deletions
diff --git a/support/hololens/ServoApp/BrowserPage.cpp b/support/hololens/ServoApp/BrowserPage.cpp index 62b3b3a9a72..fd1d984621d 100644 --- a/support/hololens/ServoApp/BrowserPage.cpp +++ b/support/hololens/ServoApp/BrowserPage.cpp @@ -9,259 +9,77 @@ #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; -using namespace servo; +using namespace winrt::Windows::ApplicationModel::Core; namespace winrt::ServoApp::implementation { BrowserPage::BrowserPage() { - log("BrowserPage::BrowserPage()"); InitializeComponent(); - InitializeConditionVariable(&mGLCondVar); - InitializeCriticalSection(&mGLLock); - Loaded(std::bind(&BrowserPage::OnPageLoaded, this, _1, _2)); - Window::Current().CoreWindow().VisibilityChanged( - std::bind(&BrowserPage::OnVisibilityChanged, this, _1, _2)); + BindServoEvents(); } -void BrowserPage::Shutdown() { - log("BrowserPage::Shutdown()"); - - if (mServo != nullptr) { - if (!mLooping) { - // FIXME: this should not happen. In that case, we can't send the - // shutdown event to Servo. - } else { - RunOnGLThread([=] { mServo->RequestShutdown(); }); - mLoopTask->wait(); - mLoopTask.reset(); - mServo.reset(); - } - } -} - -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::OnVisibilityChanged(CoreWindow const &, - VisibilityChangedEventArgs const &args) { - auto visible = args.Visible(); - - // 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 && !mLooping) { - // StartRenderLoop(); - //} - // if (!visible && mLooping) { - // 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(); -} - -/**** GL THREAD LOOP ****/ - -void BrowserPage::Loop() { - log("BrowserPage::Loop(). GL thread: %i", GetCurrentThreadId()); - - mOpenGLES.MakeCurrent(mRenderSurface); - - EGLint panelWidth = 0; - EGLint panelHeight = 0; - mOpenGLES.GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight); - glViewport(0, 0, 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); - - while (true) { - EnterCriticalSection(&mGLLock); - while (mTasks.size() == 0 && !mAnimating && mLooping) { - SleepConditionVariableCS(&mGLCondVar, &mGLLock, INFINITE); - } - if (!mLooping) { - LeaveCriticalSection(&mGLLock); - break; - } - for (auto &&task : mTasks) { - task(); - } - mTasks.clear(); - LeaveCriticalSection(&mGLLock); - mServo->PerformUpdates(); - } - mServo->DeInit(); - cancel_current_task(); -} // namespace winrt::ServoApp::implementation - -void BrowserPage::StartRenderLoop() { - if (mLooping) { -#if defined _DEBUG - throw winrt::hresult_error(E_FAIL, L"GL thread is already looping"); -#else - return; -#endif - } - mLooping = true; - log("BrowserPage::StartRenderLoop(). UI thread: %i", GetCurrentThreadId()); - auto task = Concurrency::create_task([=] { Loop(); }); - mLoopTask = std::make_unique<Concurrency::task<void>>(task); -} - -void BrowserPage::StopRenderLoop() { - if (mLooping) { - EnterCriticalSection(&mGLLock); - mLooping = false; - LeaveCriticalSection(&mGLLock); - WakeConditionVariable(&mGLCondVar); - mLoopTask->wait(); - mLoopTask.reset(); - } -} - -/**** SERVO CALLBACKS ****/ - -void BrowserPage::OnLoadStarted() { - RunOnUIThread([=] { +void BrowserPage::BindServoEvents() { + servoControl().OnURLChanged( + [=](const auto &, hstring url) { urlTextbox().Text(url); }); + servoControl().OnTitleChanged([=](const auto &, hstring title) {}); + servoControl().OnHistoryChanged([=](bool back, bool forward) { + backButton().IsEnabled(back); + forwardButton().IsEnabled(forward); + }); + servoControl().OnLoadStarted([=] { reloadButton().IsEnabled(false); stopButton().IsEnabled(true); }); -} - -void BrowserPage::OnLoadEnded() { - RunOnUIThread([=] { + servoControl().OnLoadEnded([=] { reloadButton().IsEnabled(true); stopButton().IsEnabled(false); }); } -void BrowserPage::OnHistoryChanged(bool back, bool forward) { - RunOnUIThread([=] { - backButton().IsEnabled(back); - forwardButton().IsEnabled(forward); - }); -} - -void BrowserPage::OnShutdownComplete() { - EnterCriticalSection(&mGLLock); - mLooping = false; - LeaveCriticalSection(&mGLLock); -} - -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() { - RunOnGLThread([=] { }); -} - -bool BrowserPage::OnAllowNavigation(std::wstring) { return true; } - -void BrowserPage::OnAnimatingChanged(bool animating) { - EnterCriticalSection(&mGLLock); - mAnimating = animating; - LeaveCriticalSection(&mGLLock); - WakeConditionVariable(&mGLCondVar); -} - -template <typename Callable> void BrowserPage::RunOnUIThread(Callable cb) { - swapChainPanel().Dispatcher().RunAsync( - Windows::UI::Core::CoreDispatcherPriority::High, cb); +void BrowserPage::Shutdown() { + servoControl().Shutdown(); } /**** USER INTERACTIONS WITH UI ****/ void BrowserPage::OnBackButtonClicked(IInspectable const &, RoutedEventArgs const &) { - RunOnGLThread([=] { mServo->GoBack(); }); + servoControl().GoBack(); } void BrowserPage::OnForwardButtonClicked(IInspectable const &, RoutedEventArgs const &) { - RunOnGLThread([=] { mServo->GoForward(); }); + servoControl().GoForward(); } void BrowserPage::OnReloadButtonClicked(IInspectable const &, RoutedEventArgs const &) { - RunOnGLThread([=] { mServo->Reload(); }); + servoControl().Reload(); } void BrowserPage::OnStopButtonClicked(IInspectable const &, RoutedEventArgs const &) { - RunOnGLThread([=] { mServo->Stop(); }); + servoControl().Stop(); +} + +void BrowserPage::OnURLEdited(IInspectable const &sender, + Input::KeyRoutedEventArgs const &e) { + if (e.Key() == Windows::System::VirtualKey::Enter) { + servoControl().Focus(FocusState::Programmatic); + auto input = urlTextbox().Text(); + auto uri = servoControl().LoadURIOrSearch(input); + urlTextbox().Text(uri.ToString()); + } } void BrowserPage::OnImmersiveButtonClicked(IInspectable const &, RoutedEventArgs const &) { if (HolographicSpace::IsAvailable()) { log("Holographic space available"); - auto v = - winrt::Windows::ApplicationModel::Core::CoreApplication::CreateNewView( - mImmersiveViewSource); + auto v = CoreApplication::CreateNewView(mImmersiveViewSource); auto parentId = ApplicationView::GetForCurrentView().Id(); v.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [=] { auto winId = ApplicationView::GetForCurrentView().Id(); @@ -273,30 +91,4 @@ void BrowserPage::OnImmersiveButtonClicked(IInspectable const &, } } -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; - RunOnGLThread([=] { mServo->Scroll(dx, dy, x, y); }); - 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; - RunOnGLThread([=] { mServo->Click(x, y); }); - e.Handled(true); -} - -void BrowserPage::RunOnGLThread(std::function<void()> task) { - EnterCriticalSection(&mGLLock); - mTasks.push_back(task); - LeaveCriticalSection(&mGLLock); - WakeConditionVariable(&mGLCondVar); -} - } // namespace winrt::ServoApp::implementation diff --git a/support/hololens/ServoApp/BrowserPage.h b/support/hololens/ServoApp/BrowserPage.h index 982b082579b..b52731fed70 100644 --- a/support/hololens/ServoApp/BrowserPage.h +++ b/support/hololens/ServoApp/BrowserPage.h @@ -6,13 +6,12 @@ #include "BrowserPage.g.h" #include "ImmersiveView.h" -#include "OpenGLES.h" -#include "Servo.h" +#include "ServoControl.h" + namespace winrt::ServoApp::implementation { -struct BrowserPage : BrowserPageT<BrowserPage>, - public servo::ServoDelegate { +struct BrowserPage : BrowserPageT<BrowserPage> { public: BrowserPage(); @@ -26,59 +25,13 @@ public: Windows::UI::Xaml::RoutedEventArgs const &); void OnStopButtonClicked(Windows::Foundation::IInspectable const &, Windows::UI::Xaml::RoutedEventArgs const &); - void - OnSurfaceClicked(Windows::Foundation::IInspectable const &, - Windows::UI::Xaml::Input::PointerRoutedEventArgs const &); - - void BrowserPage::OnSurfaceManipulationDelta( - IInspectable const &, - Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs const &e); - - template <typename Callable> void RunOnUIThread(Callable); - void RunOnGLThread(std::function<void()>); + void OnURLEdited(Windows::Foundation::IInspectable const &, + Windows::UI::Xaml::Input::KeyRoutedEventArgs const &); void Shutdown(); - virtual void WakeUp(); - virtual void OnLoadStarted(); - virtual void OnLoadEnded(); - virtual void OnHistoryChanged(bool, bool); - virtual void OnShutdownComplete(); - virtual void OnTitleChanged(std::wstring); - virtual void OnAlert(std::wstring); - virtual void OnURLChanged(std::wstring); - virtual void Flush(); - virtual void MakeCurrent(); - virtual bool OnAllowNavigation(std::wstring); - virtual void OnAnimatingChanged(bool); - private: - void OnVisibilityChanged( - Windows::UI::Core::CoreWindow const &, - Windows::UI::Core::VisibilityChangedEventArgs const &args); - void OnPageLoaded(Windows::Foundation::IInspectable const &, - Windows::UI::Xaml::RoutedEventArgs const &); - void CreateRenderSurface(); - void DestroyRenderSurface(); - void RecoverFromLostDevice(); - - void StartRenderLoop(); - void StopRenderLoop(); - void Loop(); - - std::unique_ptr<Concurrency::task<void>> mLoopTask; winrt::ServoApp::ImmersiveViewSource mImmersiveViewSource; - EGLSurface mRenderSurface{EGL_NO_SURFACE}; - std::unique_ptr<servo::Servo> mServo; - - std::vector<std::function<void()>> mTasks; - - CRITICAL_SECTION mGLLock; - CONDITION_VARIABLE mGLCondVar; - - bool mAnimating = false; - bool mLooping = false; - - OpenGLES mOpenGLES; // FIXME: shared pointer + void BindServoEvents(); }; } // namespace winrt::ServoApp::implementation diff --git a/support/hololens/ServoApp/BrowserPage.xaml b/support/hololens/ServoApp/BrowserPage.xaml index f6e6e227318..76b59cc7c1d 100644 --- a/support/hololens/ServoApp/BrowserPage.xaml +++ b/support/hololens/ServoApp/BrowserPage.xaml @@ -20,17 +20,16 @@ <ColumnDefinition Width="auto"/> </Grid.ColumnDefinitions> <StackPanel Orientation="Horizontal" Grid.Column="0"> - <Button x:Name="backButton" IsEnabled="false" Content="Back" Click="OnBackButtonClicked"/> - <Button x:Name="forwardButton" IsEnabled="false" Content="Forward" Click="OnForwardButtonClicked"/> - <Button x:Name="reloadButton" IsEnabled="false" Content="reload" Click="OnReloadButtonClicked"/> - <Button x:Name="stopButton" IsEnabled="false" Content="stop" Click="OnStopButtonClicked"/> + <Button x:Name="backButton" IsTabStop="true" IsEnabled="false" Content="Back" Click="OnBackButtonClicked"/> + <Button x:Name="forwardButton" IsTabStop="true" IsEnabled="false" Content="Forward" Click="OnForwardButtonClicked"/> + <Button x:Name="reloadButton" IsTabStop="true" IsEnabled="false" Content="reload" Click="OnReloadButtonClicked"/> + <Button x:Name="stopButton" IsTabStop="true" IsEnabled="false" Content="stop" Click="OnStopButtonClicked"/> </StackPanel> - <TextBox Text="" AcceptsReturn="True" PlaceholderText="Type a URL" x:Name="urlTextbox" Grid.Column="1" IsReadOnly="True"/> + <TextBox Text="" IsTabStop="true" PlaceholderText="Type a URL" x:Name="urlTextbox" Grid.Column="1" KeyUp="OnURLEdited"/> <StackPanel Orientation="Horizontal" Grid.Column="2"> <Button x:Name="immersiveButton" Click="OnImmersiveButtonClicked">Run Immersive</Button> </StackPanel> </Grid> - <SwapChainPanel x:Name="swapChainPanel" MinHeight="200" MinWidth="200" Grid.Row="1" ManipulationMode="All"> - </SwapChainPanel> + <local:ServoControl TabIndex="0" x:Name="servoControl" Grid.Row="1"/> </Grid> </Page> diff --git a/support/hololens/ServoApp/Servo.cpp b/support/hololens/ServoApp/Servo.cpp index 43350aa54be..fbc6d7d9c24 100644 --- a/support/hololens/ServoApp/Servo.cpp +++ b/support/hololens/ServoApp/Servo.cpp @@ -1,31 +1,31 @@ #include "pch.h" #include "Servo.h" -namespace servo { +namespace winrt::servo { -void on_load_started() { sServo->Delegate().OnLoadStarted(); } -void on_load_ended() { sServo->Delegate().OnLoadEnded(); } +void on_load_started() { sServo->Delegate().OnServoLoadStarted(); } +void on_load_ended() { sServo->Delegate().OnServoLoadEnded(); } void on_history_changed(bool back, bool forward) { - sServo->Delegate().OnHistoryChanged(back, forward); + sServo->Delegate().OnServoHistoryChanged(back, forward); } -void on_shutdown_complete() { sServo->Delegate().OnShutdownComplete(); } +void on_shutdown_complete() { sServo->Delegate().OnServoShutdownComplete(); } void on_alert(const char *message) { - sServo->Delegate().OnAlert(char2w(message)); + sServo->Delegate().OnServoAlert(char2hstring(message)); } void on_title_changed(const char *title) { - sServo->Delegate().OnTitleChanged(char2w(title)); + sServo->Delegate().OnServoTitleChanged(char2hstring(title)); } void on_url_changed(const char *url) { - sServo->Delegate().OnURLChanged(char2w(url)); + sServo->Delegate().OnServoURLChanged(char2hstring(url)); } void flush() { sServo->Delegate().Flush(); } void make_current() { sServo->Delegate().MakeCurrent(); } void wakeup() { sServo->Delegate().WakeUp(); } bool on_allow_navigation(const char *url) { - return sServo->Delegate().OnAllowNavigation(char2w(url)); + return sServo->Delegate().OnServoAllowNavigation(char2hstring(url)); }; void on_animating_changed(bool aAnimating) { - sServo->Delegate().OnAnimatingChanged(aAnimating); + sServo->Delegate().OnServoAnimatingChanged(aAnimating); } Servo::Servo(GLsizei width, GLsizei height, ServoDelegate &aDelegate) @@ -60,14 +60,14 @@ Servo::Servo(GLsizei width, GLsizei height, ServoDelegate &aDelegate) Servo::~Servo() { sServo = nullptr; } -std::wstring char2w(const char *c_str) { +winrt::hstring char2hstring(const char *c_str) { + // FIXME: any better way of doing this? auto str = std::string(c_str); - int size_needed = - MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); + int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0); std::wstring str2(size_needed, 0); - MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &str2[0], - size_needed); - return str2; + MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &str2[0], size_needed); + winrt::hstring str3 {str2}; + return str3; } } // namespace servo diff --git a/support/hololens/ServoApp/Servo.h b/support/hololens/ServoApp/Servo.h index d505d46b493..c0ae8298226 100644 --- a/support/hololens/ServoApp/Servo.h +++ b/support/hololens/ServoApp/Servo.h @@ -6,8 +6,9 @@ #include "pch.h" #include "logs.h" +#include <stdlib.h> -namespace servo { +namespace winrt::servo { namespace capi { extern "C" { @@ -20,17 +21,17 @@ public: // Called from any thread virtual void WakeUp() = 0; // Called from GL thread - virtual void OnLoadStarted() = 0; - virtual void OnLoadEnded() = 0; - virtual void OnHistoryChanged(bool, bool) = 0; - virtual void OnShutdownComplete() = 0; - virtual void OnTitleChanged(std::wstring) = 0; - virtual void OnAlert(std::wstring) = 0; - virtual void OnURLChanged(std::wstring) = 0; + virtual void OnServoLoadStarted() = 0; + virtual void OnServoLoadEnded() = 0; + virtual void OnServoHistoryChanged(bool, bool) = 0; + virtual void OnServoShutdownComplete() = 0; + virtual void OnServoTitleChanged(hstring) = 0; + virtual void OnServoAlert(hstring) = 0; + virtual void OnServoURLChanged(hstring) = 0; + virtual bool OnServoAllowNavigation(hstring) = 0; + virtual void OnServoAnimatingChanged(bool) = 0; virtual void Flush() = 0; virtual void MakeCurrent() = 0; - virtual bool OnAllowNavigation(std::wstring) = 0; - virtual void OnAnimatingChanged(bool) = 0; protected: virtual ~ServoDelegate(){}; @@ -51,6 +52,14 @@ public: void Click(float x, float y) { capi::click(x, y); } void Reload() { capi::reload(); } void Stop() { capi::stop(); } + void LoadUri(hstring uri) { + const wchar_t* wc = uri.c_str(); + size_t size = uri.size() + 1; + char* str = new char[size]; + size_t converted = 0; + wcstombs_s(&converted, str, size, wc, uri.size()); + capi::load_uri(str); + } void Scroll(float dx, float dy, float x, float y) { capi::scroll(dx, dy, x, y); } @@ -73,6 +82,6 @@ private: // the Servo instance. See https://github.com/servo/servo/issues/22967 static Servo *sServo = nullptr; -std::wstring char2w(const char *c_str); +hstring char2hstring(const char *c_str); } // namespace servo diff --git a/support/hololens/ServoApp/ServoApp.vcxproj b/support/hololens/ServoApp/ServoApp.vcxproj index df1d72a2ce6..4ff7de52ce8 100644 --- a/support/hololens/ServoApp/ServoApp.vcxproj +++ b/support/hololens/ServoApp/ServoApp.vcxproj @@ -160,6 +160,7 @@ <DependentUpon>BrowserPage.xaml</DependentUpon> </ClInclude> <ClInclude Include="Servo.h" /> + <ClInclude Include="ServoControl.h" /> </ItemGroup> <ItemGroup> <ApplicationDefinition Include="App.xaml"> @@ -168,6 +169,9 @@ <Page Include="BrowserPage.xaml"> <SubType>Designer</SubType> </Page> + <Page Include="Themes\Generic.xaml"> + <SubType>Designer</SubType> + </Page> </ItemGroup> <ItemGroup> <AppxManifest Include="Package.appxmanifest"> @@ -307,6 +311,7 @@ <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" /> <ClCompile Include="logs.cpp" /> <ClCompile Include="Servo.cpp" /> + <ClCompile Include="ServoControl.cpp" /> </ItemGroup> <ItemGroup> <Midl Include="App.idl"> @@ -315,6 +320,7 @@ <Midl Include="BrowserPage.idl"> <DependentUpon>BrowserPage.xaml</DependentUpon> </Midl> + <Midl Include="ServoControl.idl" /> </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/support/hololens/ServoApp/ServoApp.vcxproj.filters b/support/hololens/ServoApp/ServoApp.vcxproj.filters index 134e6e7cc95..0d01f93b0a2 100644 --- a/support/hololens/ServoApp/ServoApp.vcxproj.filters +++ b/support/hololens/ServoApp/ServoApp.vcxproj.filters @@ -3,6 +3,9 @@ <ItemGroup> <Midl Include="BrowserPage.idl" /> <Midl Include="App.idl" /> + <Midl Include="ServoControl.idl"> + <Filter>ServoControl</Filter> + </Midl> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp" /> @@ -10,8 +13,6 @@ <ClCompile Include="logs.cpp" /> <ClCompile Include="ImmersiveView.cpp" /> <ClCompile Include="BrowserPage.cpp" /> - <ClCompile Include="Servo.cpp" /> - <ClCompile Include="OpenGLES.cpp" /> <ClCompile Include="App.cpp" /> <ClCompile Include="Common\CameraResources.cpp"> <Filter>Common</Filter> @@ -26,14 +27,21 @@ <Filter>Content</Filter> </ClCompile> <ClCompile Include="ImmersiveMain.cpp" /> + <ClCompile Include="ServoControl.cpp"> + <Filter>ServoControl</Filter> + </ClCompile> + <ClCompile Include="Servo.cpp"> + <Filter>ServoControl</Filter> + </ClCompile> + <ClCompile Include="OpenGLES.cpp"> + <Filter>ServoControl</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="pch.h" /> <ClInclude Include="logs.h" /> <ClInclude Include="ImmersiveView.h" /> <ClInclude Include="BrowserPage.h" /> - <ClInclude Include="Servo.h" /> - <ClInclude Include="OpenGLES.h" /> <ClInclude Include="App.h" /> <ClInclude Include="Common\CameraResources.h"> <Filter>Common</Filter> @@ -57,6 +65,15 @@ <Filter>Content</Filter> </ClInclude> <ClInclude Include="ImmersiveMain.h" /> + <ClInclude Include="ServoControl.h"> + <Filter>ServoControl</Filter> + </ClInclude> + <ClInclude Include="Servo.h"> + <Filter>ServoControl</Filter> + </ClInclude> + <ClInclude Include="OpenGLES.h"> + <Filter>ServoControl</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Image Include="Assets\Wide310x150Logo.scale-200.png"> @@ -191,12 +208,18 @@ <Filter Include="ReleaseARM64ServoDLLs"> <UniqueIdentifier>{384b4019-d076-4301-994d-a891969a3036}</UniqueIdentifier> </Filter> + <Filter Include="ServoControl"> + <UniqueIdentifier>{d21a959c-19d1-4a54-b942-692c27e5b3a6}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> </ItemGroup> <ItemGroup> <Page Include="BrowserPage.xaml" /> + <Page Include="Themes\Generic.xaml"> + <Filter>ServoControl</Filter> + </Page> </ItemGroup> <ItemGroup> <FxCompile Include="Content\GeometryShader.hlsl"> diff --git a/support/hololens/ServoApp/ServoControl.cpp b/support/hololens/ServoApp/ServoControl.cpp new file mode 100644 index 00000000000..bc2823d37af --- /dev/null +++ b/support/hololens/ServoApp/ServoControl.cpp @@ -0,0 +1,257 @@ +#include "pch.h" +#include "ServoControl.h" +#include "ServoControl.g.cpp" +#include <stdlib.h> + +using namespace std::placeholders; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::Foundation; +using namespace concurrency; +using namespace winrt::servo; + +namespace winrt::ServoApp::implementation { + +ServoControl::ServoControl() { + DefaultStyleKey(winrt::box_value(L"ServoApp.ServoControl")); + Loaded(std::bind(&ServoControl::OnLoaded, this, _1, _2)); +} + +void ServoControl::Shutdown() { + if (mServo != nullptr) { + if (!mLooping) { + // FIXME: this should not happen. In that case, we can't send the + // shutdown event to Servo. + } else { + RunOnGLThread([=] { mServo->RequestShutdown(); }); + mLoopTask->wait(); + mLoopTask.reset(); + mServo.reset(); + } + } +} + +void ServoControl::OnLoaded(IInspectable const &, RoutedEventArgs const &) { + Panel().PointerReleased( + std::bind(&ServoControl::OnSurfaceClicked, this, _1, _2)); + Panel().ManipulationDelta( + std::bind(&ServoControl::OnSurfaceManipulationDelta, this, _1, _2)); + InitializeConditionVariable(&mGLCondVar); + InitializeCriticalSection(&mGLLock); + CreateRenderSurface(); + StartRenderLoop(); +} + +Controls::SwapChainPanel ServoControl::Panel() { + // FIXME: is there a better way of doing this? + return GetTemplateChild(L"swapChainPanel") + .as<Controls::SwapChainPanel>(); +} + +void ServoControl::CreateRenderSurface() { + if (mRenderSurface == EGL_NO_SURFACE) { + mRenderSurface = mOpenGLES.CreateSurface(Panel()); + } +} + +void ServoControl::DestroyRenderSurface() { + mOpenGLES.DestroySurface(mRenderSurface); + mRenderSurface = EGL_NO_SURFACE; +} + +void ServoControl::RecoverFromLostDevice() { + StopRenderLoop(); + DestroyRenderSurface(); + mOpenGLES.Reset(); + CreateRenderSurface(); + StartRenderLoop(); +} + +void ServoControl::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; + RunOnGLThread([=] { mServo->Scroll(dx, dy, x, y); }); + e.Handled(true); +} + +void ServoControl::OnSurfaceClicked(IInspectable const &, + Input::PointerRoutedEventArgs const &e) { + auto coords = e.GetCurrentPoint(Panel()); + auto x = coords.Position().X; + auto y = coords.Position().Y; + RunOnGLThread([=] { mServo->Click(x, y); }); + e.Handled(true); +} + +void ServoControl::GoBack() { + RunOnGLThread([=] { mServo->GoBack(); }); +} +void ServoControl::GoForward() { + RunOnGLThread([=] { mServo->GoForward(); }); +} +void ServoControl::Reload() { + RunOnGLThread([=] { mServo->Reload(); }); +} +void ServoControl::Stop() { + RunOnGLThread([=] { mServo->Stop(); }); +} +Uri ServoControl::LoadURIOrSearch(hstring input) { + auto uri = TryParseURI(input); + if (uri == std::nullopt) { + bool has_dot = wcsstr(input.c_str(), L".") != nullptr; + hstring input2 = L"https://" + input; + uri = TryParseURI(input2); + if (uri == std::nullopt || !has_dot) { + hstring input3 = L"https://duckduckgo.com/html/?q=" + Uri::EscapeComponent(input); + uri = TryParseURI(input3); + } + } + auto finalUri = uri.value(); + RunOnGLThread([=] { mServo->LoadUri(finalUri.ToString()); }); + return finalUri; +} + +void ServoControl::RunOnGLThread(std::function<void()> task) { + EnterCriticalSection(&mGLLock); + mTasks.push_back(task); + LeaveCriticalSection(&mGLLock); + WakeConditionVariable(&mGLCondVar); +} + +/**** GL THREAD LOOP ****/ + +void ServoControl::Loop() { + log("BrowserPage::Loop(). GL thread: %i", GetCurrentThreadId()); + + mOpenGLES.MakeCurrent(mRenderSurface); + + EGLint panelWidth = 0; + EGLint panelHeight = 0; + mOpenGLES.GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight); + glViewport(0, 0, 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); + + while (true) { + EnterCriticalSection(&mGLLock); + while (mTasks.size() == 0 && !mAnimating && mLooping) { + SleepConditionVariableCS(&mGLCondVar, &mGLLock, INFINITE); + } + if (!mLooping) { + LeaveCriticalSection(&mGLLock); + break; + } + for (auto &&task : mTasks) { + task(); + } + mTasks.clear(); + LeaveCriticalSection(&mGLLock); + mServo->PerformUpdates(); + } + mServo->DeInit(); + cancel_current_task(); +} + +void ServoControl::StartRenderLoop() { + if (mLooping) { +#if defined _DEBUG + throw winrt::hresult_error(E_FAIL, L"GL thread is already looping"); +#else + return; +#endif + } + mLooping = true; + log("BrowserPage::StartRenderLoop(). UI thread: %i", GetCurrentThreadId()); + auto task = Concurrency::create_task([=] { Loop(); }); + mLoopTask = std::make_unique<Concurrency::task<void>>(task); +} + +void ServoControl::StopRenderLoop() { + if (mLooping) { + EnterCriticalSection(&mGLLock); + mLooping = false; + LeaveCriticalSection(&mGLLock); + WakeConditionVariable(&mGLCondVar); + mLoopTask->wait(); + mLoopTask.reset(); + } +} + +/**** SERVO CALLBACKS ****/ + +void ServoControl::OnServoLoadStarted() { + RunOnUIThread([=] { mOnLoadStartedEvent(); }); +} + +void ServoControl::OnServoLoadEnded() { + RunOnUIThread([=] { mOnLoadEndedEvent(); }); +} + +void ServoControl::OnServoHistoryChanged(bool back, bool forward) { + RunOnUIThread([=] { mOnHistoryChangedEvent(back, forward); }); +} + +void ServoControl::OnServoShutdownComplete() { + EnterCriticalSection(&mGLLock); + mLooping = false; + LeaveCriticalSection(&mGLLock); +} + +void ServoControl::OnServoAlert(hstring message) { + // FIXME: make this sync + RunOnUIThread([=] { + Windows::UI::Popups::MessageDialog msg{message}; + msg.ShowAsync(); + }); +} + +void ServoControl::OnServoTitleChanged(hstring title) { + RunOnUIThread([=] { mOnTitleChangedEvent(*this, title); }); +} + +void ServoControl::OnServoURLChanged(hstring url) { + RunOnUIThread([=] { mOnURLChangedEvent(*this, url); }); +} + +void ServoControl::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 ServoControl::MakeCurrent() { mOpenGLES.MakeCurrent(mRenderSurface); } + +void ServoControl::WakeUp() { + RunOnGLThread([=] {}); +} + +bool ServoControl::OnServoAllowNavigation(hstring) { return true; } + +void ServoControl::OnServoAnimatingChanged(bool animating) { + EnterCriticalSection(&mGLLock); + mAnimating = animating; + LeaveCriticalSection(&mGLLock); + WakeConditionVariable(&mGLCondVar); +} + +template <typename Callable> void ServoControl::RunOnUIThread(Callable cb) { + Dispatcher().RunAsync(CoreDispatcherPriority::High, cb); +} + +} // namespace winrt::ServoApp::implementation diff --git a/support/hololens/ServoApp/ServoControl.h b/support/hololens/ServoApp/ServoControl.h new file mode 100644 index 00000000000..e5448a7f9ae --- /dev/null +++ b/support/hololens/ServoApp/ServoControl.h @@ -0,0 +1,110 @@ +#pragma once +#include "ServoControl.g.h" +#include "OpenGLES.h" +#include "Servo.h" + +namespace winrt::ServoApp::implementation { +struct ServoControl : ServoControlT<ServoControl>, public servo::ServoDelegate { + + ServoControl(); + + void GoBack(); + void GoForward(); + void Reload(); + void Stop(); + void Shutdown(); + Windows::Foundation::Uri LoadURIOrSearch(hstring); + + void OnLoaded(IInspectable const &, Windows::UI::Xaml::RoutedEventArgs const &); + + winrt::event_token + OnURLChanged(Windows::Foundation::EventHandler<hstring> const &handler){ + return mOnURLChangedEvent.add(handler); + }; + void OnURLChanged(winrt::event_token const& token) noexcept { mOnURLChangedEvent.remove(token); } + + winrt::event_token + OnTitleChanged(Windows::Foundation::EventHandler<hstring> const &handler){ + return mOnTitleChangedEvent.add(handler); + }; + void OnTitleChanged(winrt::event_token const& token) noexcept { mOnTitleChangedEvent.remove(token); } + + winrt::event_token OnHistoryChanged(HistoryChangedDelegate const &handler){ + return mOnHistoryChangedEvent.add(handler); + }; + void OnHistoryChanged(winrt::event_token const& token) noexcept { mOnHistoryChangedEvent.remove(token); } + + winrt::event_token OnLoadStarted(LoadStatusChangedDelegate const &handler){ + return mOnLoadStartedEvent.add(handler); + }; + void OnLoadStarted(winrt::event_token const& token) noexcept { mOnLoadStartedEvent.remove(token); } + + winrt::event_token OnLoadEnded(LoadStatusChangedDelegate const &handler){ + return mOnLoadEndedEvent.add(handler); + }; + void OnLoadEnded(winrt::event_token const& token) noexcept { mOnLoadEndedEvent.remove(token); } + + virtual void WakeUp(); + virtual void OnServoLoadStarted(); + virtual void OnServoLoadEnded(); + virtual void OnServoHistoryChanged(bool, bool); + virtual void OnServoShutdownComplete(); + virtual void OnServoTitleChanged(winrt::hstring); + virtual void OnServoAlert(winrt::hstring); + virtual void OnServoURLChanged(winrt::hstring); + virtual void Flush(); + virtual void MakeCurrent(); + virtual bool OnServoAllowNavigation(winrt::hstring); + virtual void OnServoAnimatingChanged(bool); + +private: + winrt::event<Windows::Foundation::EventHandler<hstring>> mOnURLChangedEvent; + winrt::event<Windows::Foundation::EventHandler<hstring>> mOnTitleChangedEvent; + winrt::event<HistoryChangedDelegate> mOnHistoryChangedEvent; + winrt::event<LoadStatusChangedDelegate> mOnLoadStartedEvent; + winrt::event<LoadStatusChangedDelegate> mOnLoadEndedEvent; + + Windows::UI::Xaml::Controls::SwapChainPanel ServoControl::Panel(); + void CreateRenderSurface(); + void DestroyRenderSurface(); + void RecoverFromLostDevice(); + + void StartRenderLoop(); + void StopRenderLoop(); + void Loop(); + + std::optional<Windows::Foundation::Uri> TryParseURI(hstring input) { + try { + return Windows::Foundation::Uri(input); + } catch (hresult_invalid_argument const &e) { + return {}; + } + } + + void + OnSurfaceClicked(IInspectable const &, + Windows::UI::Xaml::Input::PointerRoutedEventArgs const &); + + void OnSurfaceManipulationDelta( + IInspectable const &, + Windows::UI::Xaml::Input::ManipulationDeltaRoutedEventArgs const &e); + + template <typename Callable> void RunOnUIThread(Callable); + void RunOnGLThread(std::function<void()>); + + std::unique_ptr<servo::Servo> mServo; + EGLSurface mRenderSurface{EGL_NO_SURFACE}; + OpenGLES mOpenGLES; + bool mAnimating = false; + bool mLooping = false; + std::vector<std::function<void()>> mTasks; + CRITICAL_SECTION mGLLock; + CONDITION_VARIABLE mGLCondVar; + std::unique_ptr<Concurrency::task<void>> mLoopTask; +}; +} // namespace winrt::ServoApp::implementation + +namespace winrt::ServoApp::factory_implementation { +struct ServoControl + : ServoControlT<ServoControl, implementation::ServoControl> {}; +} // namespace winrt::ServoApp::factory_implementation diff --git a/support/hololens/ServoApp/ServoControl.idl b/support/hololens/ServoApp/ServoControl.idl new file mode 100644 index 00000000000..dcdc9e051f3 --- /dev/null +++ b/support/hololens/ServoApp/ServoControl.idl @@ -0,0 +1,20 @@ +namespace ServoApp { + + delegate void LoadStatusChangedDelegate(); + delegate void HistoryChangedDelegate(Boolean back, Boolean forward); + + runtimeclass ServoControl : Windows.UI.Xaml.Controls.Control { + ServoControl(); + void GoBack(); + void GoForward(); + void Reload(); + void Stop(); + Windows.Foundation.Uri LoadURIOrSearch(String url); + void Shutdown(); + event LoadStatusChangedDelegate OnLoadStarted; + event LoadStatusChangedDelegate OnLoadEnded; + event HistoryChangedDelegate OnHistoryChanged; + event Windows.Foundation.EventHandler<String> OnTitleChanged; + event Windows.Foundation.EventHandler<String> OnURLChanged; +} +} // namespace ServoApp diff --git a/support/hololens/ServoApp/Themes/Generic.xaml b/support/hololens/ServoApp/Themes/Generic.xaml new file mode 100644 index 00000000000..c4969c9fd11 --- /dev/null +++ b/support/hololens/ServoApp/Themes/Generic.xaml @@ -0,0 +1,19 @@ +<!-- \Themes\Generic.xaml --> +<ResourceDictionary + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:local="using:ServoApp"> + + <Style TargetType="local:ServoControl" > + <Setter Property="Template"> + <Setter.Value> + <ControlTemplate TargetType="local:ServoControl"> + <Grid> + <SwapChainPanel x:Name="swapChainPanel" MinHeight="200" MinWidth="200" ManipulationMode="All"> + </SwapChainPanel> + </Grid> + </ControlTemplate> + </Setter.Value> + </Setter> + </Style> +</ResourceDictionary> |