aboutsummaryrefslogtreecommitdiffstats
path: root/support/hololens
diff options
context:
space:
mode:
authorPaul Rouget <me@paulrouget.com>2019-08-06 05:02:48 +0200
committerPaul Rouget <me@paulrouget.com>2019-08-19 07:12:16 +0200
commit663ec48e00636ffaf6cb39054c3cfc5580f569eb (patch)
treefaf47840e877bb5b5cf88ec9b021fe83b8ef329b /support/hololens
parent3658a8cc591ef4ca827ce1cda9565a1bca7d7b3c (diff)
downloadservo-663ec48e00636ffaf6cb39054c3cfc5580f569eb.tar.gz
servo-663ec48e00636ffaf6cb39054c3cfc5580f569eb.zip
Move Servo into a Control Template
Diffstat (limited to 'support/hololens')
-rw-r--r--support/hololens/ServoApp/BrowserPage.cpp266
-rw-r--r--support/hololens/ServoApp/BrowserPage.h59
-rw-r--r--support/hololens/ServoApp/BrowserPage.xaml13
-rw-r--r--support/hololens/ServoApp/Servo.cpp32
-rw-r--r--support/hololens/ServoApp/Servo.h31
-rw-r--r--support/hololens/ServoApp/ServoApp.vcxproj6
-rw-r--r--support/hololens/ServoApp/ServoApp.vcxproj.filters31
-rw-r--r--support/hololens/ServoApp/ServoControl.cpp257
-rw-r--r--support/hololens/ServoApp/ServoControl.h110
-rw-r--r--support/hololens/ServoApp/ServoControl.idl20
-rw-r--r--support/hololens/ServoApp/Themes/Generic.xaml19
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>