aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2019-08-20 02:21:22 -0400
committerGitHub <noreply@github.com>2019-08-20 02:21:22 -0400
commit17f423723c0206c934f104eb3352f39a9533aa41 (patch)
tree7cbf1706421576e207962289c2c282c1ebda3449
parent171a08270dc3f5eaa238570898d67f422519534b (diff)
parentd09692664e357a05d7238c370099eede682a3a4a (diff)
downloadservo-17f423723c0206c934f104eb3352f39a9533aa41.tar.gz
servo-17f423723c0206c934f104eb3352f39a9533aa41.zip
Auto merge of #23981 - paulrouget:servocontrol, r=manishearth
Implement Servo Control Template This wraps the swapchain in its own control, making it possible to better control the widget behavior. In the future, we could package that control into a nuget package allowing any app to use Servo. Fix #23894 Fix #23916 <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23981) <!-- Reviewable:end -->
-rw-r--r--support/hololens/ServoApp/BrowserPage.cpp267
-rw-r--r--support/hololens/ServoApp/BrowserPage.h59
-rw-r--r--support/hololens/ServoApp/BrowserPage.xaml13
-rw-r--r--support/hololens/ServoApp/ImmersiveView.h1
-rw-r--r--support/hololens/ServoApp/ServoApp.vcxproj16
-rw-r--r--support/hololens/ServoApp/ServoApp.vcxproj.filters33
-rw-r--r--support/hololens/ServoApp/ServoControl/OpenGLES.cpp (renamed from support/hololens/ServoApp/OpenGLES.cpp)0
-rw-r--r--support/hololens/ServoApp/ServoControl/OpenGLES.h (renamed from support/hololens/ServoApp/OpenGLES.h)0
-rw-r--r--support/hololens/ServoApp/ServoControl/Servo.cpp (renamed from support/hololens/ServoApp/Servo.cpp)32
-rw-r--r--support/hololens/ServoApp/ServoControl/Servo.h (renamed from support/hololens/ServoApp/Servo.h)31
-rw-r--r--support/hololens/ServoApp/ServoControl/ServoControl.cpp257
-rw-r--r--support/hololens/ServoApp/ServoControl/ServoControl.h110
-rw-r--r--support/hololens/ServoApp/ServoControl/ServoControl.idl20
-rw-r--r--support/hololens/ServoApp/Themes/Generic.xaml19
14 files changed, 522 insertions, 336 deletions
diff --git a/support/hololens/ServoApp/BrowserPage.cpp b/support/hololens/ServoApp/BrowserPage.cpp
index 62b3b3a9a72..0233308fa75 100644
--- a/support/hololens/ServoApp/BrowserPage.cpp
+++ b/support/hololens/ServoApp/BrowserPage.cpp
@@ -7,261 +7,78 @@
#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;
-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 +90,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..69515931976 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\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..8195bb3a294 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" InputScope="Url" 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/ImmersiveView.h b/support/hololens/ServoApp/ImmersiveView.h
index 806d267ca59..574d0685850 100644
--- a/support/hololens/ServoApp/ImmersiveView.h
+++ b/support/hololens/ServoApp/ImmersiveView.h
@@ -4,7 +4,6 @@
#pragma once
-#include "OpenGLES.h"
#include "Common/DeviceResources.h"
#include "ImmersiveMain.h"
diff --git a/support/hololens/ServoApp/ServoApp.vcxproj b/support/hololens/ServoApp/ServoApp.vcxproj
index df1d72a2ce6..045d042ba10 100644
--- a/support/hololens/ServoApp/ServoApp.vcxproj
+++ b/support/hololens/ServoApp/ServoApp.vcxproj
@@ -151,7 +151,6 @@
<ClInclude Include="ImmersiveMain.h" />
<ClInclude Include="ImmersiveView.h" />
<ClInclude Include="logs.h" />
- <ClInclude Include="OpenGLES.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="App.h">
<DependentUpon>App.xaml</DependentUpon>
@@ -159,7 +158,9 @@
<ClInclude Include="BrowserPage.h">
<DependentUpon>BrowserPage.xaml</DependentUpon>
</ClInclude>
- <ClInclude Include="Servo.h" />
+ <ClInclude Include="ServoControl\OpenGLES.h" />
+ <ClInclude Include="ServoControl\Servo.h" />
+ <ClInclude Include="ServoControl\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">
@@ -294,7 +298,6 @@
<ClCompile Include="Content\SpinningCubeRenderer.cpp" />
<ClCompile Include="ImmersiveMain.cpp" />
<ClCompile Include="ImmersiveView.cpp" />
- <ClCompile Include="OpenGLES.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
@@ -306,7 +309,9 @@
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
<ClCompile Include="logs.cpp" />
- <ClCompile Include="Servo.cpp" />
+ <ClCompile Include="ServoControl\OpenGLES.cpp" />
+ <ClCompile Include="ServoControl\Servo.cpp" />
+ <ClCompile Include="ServoControl\ServoControl.cpp" />
</ItemGroup>
<ItemGroup>
<Midl Include="App.idl">
@@ -315,6 +320,7 @@
<Midl Include="BrowserPage.idl">
<DependentUpon>BrowserPage.xaml</DependentUpon>
</Midl>
+ <Midl Include="ServoControl\ServoControl.idl" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@@ -352,4 +358,4 @@
<Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.190620.2\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.190620.2\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\packages\ANGLE.WindowsStore.Servo.2.1.13\build\native\ANGLE.WindowsStore.Servo.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ANGLE.WindowsStore.Servo.2.1.13\build\native\ANGLE.WindowsStore.Servo.targets'))" />
</Target>
-</Project> \ No newline at end of file
+</Project>
diff --git a/support/hololens/ServoApp/ServoApp.vcxproj.filters b/support/hololens/ServoApp/ServoApp.vcxproj.filters
index 134e6e7cc95..3fa4f04b149 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\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\OpenGLES.cpp">
+ <Filter>ServoControl</Filter>
+ </ClCompile>
+ <ClCompile Include="ServoControl\Servo.cpp">
+ <Filter>ServoControl</Filter>
+ </ClCompile>
+ <ClCompile Include="ServoControl\ServoControl.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\OpenGLES.h">
+ <Filter>ServoControl</Filter>
+ </ClInclude>
+ <ClInclude Include="ServoControl\Servo.h">
+ <Filter>ServoControl</Filter>
+ </ClInclude>
+ <ClInclude Include="ServoControl\ServoControl.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">
@@ -215,4 +238,4 @@
<ItemGroup>
<ApplicationDefinition Include="App.xaml" />
</ItemGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/support/hololens/ServoApp/OpenGLES.cpp b/support/hololens/ServoApp/ServoControl/OpenGLES.cpp
index 31467a4ed60..31467a4ed60 100644
--- a/support/hololens/ServoApp/OpenGLES.cpp
+++ b/support/hololens/ServoApp/ServoControl/OpenGLES.cpp
diff --git a/support/hololens/ServoApp/OpenGLES.h b/support/hololens/ServoApp/ServoControl/OpenGLES.h
index 6d17798acf1..6d17798acf1 100644
--- a/support/hololens/ServoApp/OpenGLES.h
+++ b/support/hololens/ServoApp/ServoControl/OpenGLES.h
diff --git a/support/hololens/ServoApp/Servo.cpp b/support/hololens/ServoApp/ServoControl/Servo.cpp
index 43350aa54be..fbc6d7d9c24 100644
--- a/support/hololens/ServoApp/Servo.cpp
+++ b/support/hololens/ServoApp/ServoControl/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/ServoControl/Servo.h
index d505d46b493..c0ae8298226 100644
--- a/support/hololens/ServoApp/Servo.h
+++ b/support/hololens/ServoApp/ServoControl/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/ServoControl/ServoControl.cpp b/support/hololens/ServoApp/ServoControl/ServoControl.cpp
new file mode 100644
index 00000000000..bc2823d37af
--- /dev/null
+++ b/support/hololens/ServoApp/ServoControl/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/ServoControl.h b/support/hololens/ServoApp/ServoControl/ServoControl.h
new file mode 100644
index 00000000000..e5448a7f9ae
--- /dev/null
+++ b/support/hololens/ServoApp/ServoControl/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/ServoControl.idl b/support/hololens/ServoApp/ServoControl/ServoControl.idl
new file mode 100644
index 00000000000..dcdc9e051f3
--- /dev/null
+++ b/support/hololens/ServoApp/ServoControl/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>