mirror of
https://github.com/servo/servo.git
synced 2025-08-08 06:55:31 +01:00
move control to its own directory
This commit is contained in:
parent
663ec48e00
commit
92f57b66d1
12 changed files with 17 additions and 19 deletions
270
support/hololens/ServoApp/ServoControl/OpenGLES.cpp
Normal file
270
support/hololens/ServoApp/ServoControl/OpenGLES.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "pch.h"
|
||||
#include "logs.h"
|
||||
#include "OpenGLES.h"
|
||||
|
||||
using namespace winrt::Windows::UI::Xaml::Controls;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
|
||||
OpenGLES::OpenGLES()
|
||||
: mEglConfig(nullptr), mEglDisplay(EGL_NO_DISPLAY),
|
||||
mEglContext(EGL_NO_CONTEXT) {
|
||||
log("OpenGLES::OpenGLES()");
|
||||
Initialize();
|
||||
}
|
||||
|
||||
OpenGLES::~OpenGLES() { Cleanup(); }
|
||||
|
||||
void OpenGLES::Initialize() {
|
||||
const EGLint configAttributes[] = {EGL_RENDERABLE_TYPE,
|
||||
EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8,
|
||||
EGL_DEPTH_SIZE,
|
||||
24,
|
||||
EGL_STENCIL_SIZE,
|
||||
8,
|
||||
EGL_NONE};
|
||||
|
||||
const EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
||||
|
||||
// Based on Angle MS template.
|
||||
|
||||
const EGLint defaultDisplayAttributes[] = {
|
||||
// These are the default display attributes, used to request ANGLE's D3D11
|
||||
// renderer.
|
||||
// eglInitialize will only succeed with these attributes if the hardware
|
||||
// supports D3D11 Feature Level 10_0+.
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
||||
|
||||
// EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER is an optimization that
|
||||
// can have large performance benefits on
|
||||
// mobile devices. Its syntax is subject to change, though. Please update
|
||||
// your Visual Studio templates if you
|
||||
// experience compilation issues with it.
|
||||
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER,
|
||||
EGL_TRUE,
|
||||
|
||||
// EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE is an option that
|
||||
// enables ANGLE to automatically call
|
||||
// the IDXGIDevice3::Trim method on behalf of the application when it gets
|
||||
// suspended.
|
||||
// Calling IDXGIDevice3::Trim when an application is suspended is a
|
||||
// Windows Store application certification
|
||||
// requirement.
|
||||
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
|
||||
EGL_TRUE,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
const EGLint fl9_3DisplayAttributes[] = {
|
||||
// These can be used to request ANGLE's D3D11 renderer, with D3D11 Feature
|
||||
// Level 9_3.
|
||||
// These attributes are used if the call to eglInitialize fails with the
|
||||
// default display attributes.
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
|
||||
9,
|
||||
EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
|
||||
3,
|
||||
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER,
|
||||
EGL_TRUE,
|
||||
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
|
||||
EGL_TRUE,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
const EGLint warpDisplayAttributes[] = {
|
||||
// These attributes can be used to request D3D11 WARP.
|
||||
// They are used if eglInitialize fails with both the default display
|
||||
// attributes and the 9_3 display attributes.
|
||||
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
|
||||
EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE,
|
||||
EGL_ANGLE_DISPLAY_ALLOW_RENDER_TO_BACK_BUFFER,
|
||||
EGL_TRUE,
|
||||
EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE,
|
||||
EGL_TRUE,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
// eglGetPlatformDisplayEXT is an alternative to eglGetDisplay.
|
||||
// It allows us to pass in display attributes, used to configure D3D11.
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
|
||||
reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
|
||||
eglGetProcAddress("eglGetPlatformDisplayEXT"));
|
||||
if (!eglGetPlatformDisplayEXT) {
|
||||
throw winrt::hresult_error(
|
||||
E_FAIL, L"Failed to get function eglGetPlatformDisplayEXT");
|
||||
}
|
||||
|
||||
//
|
||||
// To initialize the display, we make three sets of calls to
|
||||
// eglGetPlatformDisplayEXT and eglInitialize, with varying parameters passed
|
||||
// to eglGetPlatformDisplayEXT: 1) The first calls uses
|
||||
// "defaultDisplayAttributes" as a parameter. This corresponds to D3D11
|
||||
// Feature Level 10_0+. 2) If eglInitialize fails for step 1 (e.g. because
|
||||
// 10_0+ isn't supported by the default GPU), then we try again
|
||||
// using "fl9_3DisplayAttributes". This corresponds to D3D11 Feature Level
|
||||
// 9_3.
|
||||
// 3) If eglInitialize fails for step 2 (e.g. because 9_3+ isn't supported by
|
||||
// the default GPU), then we try again
|
||||
// using "warpDisplayAttributes". This corresponds to D3D11 Feature Level
|
||||
// 11_0 on WARP, a D3D11 software rasterizer.
|
||||
//
|
||||
|
||||
// This tries to initialize EGL to D3D11 Feature Level 10_0+. See above
|
||||
// comment for details.
|
||||
mEglDisplay = eglGetPlatformDisplayEXT(
|
||||
EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, defaultDisplayAttributes);
|
||||
if (mEglDisplay == EGL_NO_DISPLAY) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to get EGL display");
|
||||
}
|
||||
|
||||
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) {
|
||||
// This tries to initialize EGL to D3D11 Feature Level 9_3, if 10_0+ is
|
||||
// unavailable (e.g. on some mobile devices).
|
||||
mEglDisplay = eglGetPlatformDisplayEXT(
|
||||
EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, fl9_3DisplayAttributes);
|
||||
if (mEglDisplay == EGL_NO_DISPLAY) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to get EGL display");
|
||||
}
|
||||
|
||||
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) {
|
||||
// This initializes EGL to D3D11 Feature Level 11_0 on WARP, if 9_3+ is
|
||||
// unavailable on the default GPU.
|
||||
mEglDisplay = eglGetPlatformDisplayEXT(
|
||||
EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, warpDisplayAttributes);
|
||||
if (mEglDisplay == EGL_NO_DISPLAY) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to get EGL display");
|
||||
}
|
||||
|
||||
if (eglInitialize(mEglDisplay, NULL, NULL) == EGL_FALSE) {
|
||||
// If all of the calls to eglInitialize returned EGL_FALSE then an error
|
||||
// has occurred.
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to initialize EGL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EGLint numConfigs = 0;
|
||||
if ((eglChooseConfig(mEglDisplay, configAttributes, &mEglConfig, 1,
|
||||
&numConfigs) == EGL_FALSE) ||
|
||||
(numConfigs == 0)) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to choose first EGLConfig");
|
||||
}
|
||||
|
||||
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT,
|
||||
contextAttributes);
|
||||
if (mEglContext == EGL_NO_CONTEXT) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to create EGL context");
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLES::Cleanup() {
|
||||
if (mEglDisplay != EGL_NO_DISPLAY && mEglContext != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(mEglDisplay, mEglContext);
|
||||
mEglContext = EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
if (mEglDisplay != EGL_NO_DISPLAY) {
|
||||
eglTerminate(mEglDisplay);
|
||||
mEglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLES::Reset() {
|
||||
Cleanup();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
// FIXME: simplify this. Duplicating code with CreateSurface(SwapChainPanel)
|
||||
EGLSurface
|
||||
OpenGLES::CreateSurface(winrt::Windows::UI::Core::CoreWindow const &cwin) {
|
||||
EGLSurface surface = EGL_NO_SURFACE;
|
||||
|
||||
const EGLint surfaceAttributes[] = {EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER,
|
||||
EGL_TRUE, EGL_NONE};
|
||||
|
||||
PropertySet surfaceCreationProperties;
|
||||
|
||||
surfaceCreationProperties.Insert(EGLNativeWindowTypeProperty, cwin);
|
||||
// How to set size and or scale:
|
||||
// Insert(EGLRenderSurfaceSizeProperty),
|
||||
// PropertyValue::CreateSize(*renderSurfaceSize));
|
||||
// Insert(EGLRenderResolutionScaleProperty),
|
||||
// PropertyValue::CreateSingle(*resolutionScale));
|
||||
|
||||
EGLNativeWindowType win = static_cast<EGLNativeWindowType>(
|
||||
winrt::get_abi(surfaceCreationProperties));
|
||||
surface =
|
||||
eglCreateWindowSurface(mEglDisplay, mEglConfig, win, surfaceAttributes);
|
||||
|
||||
if (surface == EGL_NO_SURFACE) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to create EGL surface");
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
EGLSurface OpenGLES::CreateSurface(SwapChainPanel const &panel) {
|
||||
EGLSurface surface = EGL_NO_SURFACE;
|
||||
|
||||
const EGLint surfaceAttributes[] = {EGL_ANGLE_SURFACE_RENDER_TO_BACK_BUFFER,
|
||||
EGL_TRUE, EGL_NONE};
|
||||
|
||||
PropertySet surfaceCreationProperties;
|
||||
|
||||
surfaceCreationProperties.Insert(EGLNativeWindowTypeProperty, panel);
|
||||
// How to set size and or scale:
|
||||
// Insert(EGLRenderSurfaceSizeProperty),
|
||||
// PropertyValue::CreateSize(*renderSurfaceSize));
|
||||
// Insert(EGLRenderResolutionScaleProperty),
|
||||
// PropertyValue::CreateSingle(*resolutionScale));
|
||||
|
||||
EGLNativeWindowType win = static_cast<EGLNativeWindowType>(
|
||||
winrt::get_abi(surfaceCreationProperties));
|
||||
surface =
|
||||
eglCreateWindowSurface(mEglDisplay, mEglConfig, win, surfaceAttributes);
|
||||
|
||||
if (surface == EGL_NO_SURFACE) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to create EGL surface");
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
void OpenGLES::GetSurfaceDimensions(const EGLSurface surface, EGLint *width,
|
||||
EGLint *height) {
|
||||
eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
|
||||
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
|
||||
}
|
||||
|
||||
void OpenGLES::DestroySurface(const EGLSurface surface) {
|
||||
if (mEglDisplay != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) {
|
||||
eglDestroySurface(mEglDisplay, surface);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLES::MakeCurrent(const EGLSurface surface) {
|
||||
if (eglMakeCurrent(mEglDisplay, surface, surface, mEglContext) == EGL_FALSE) {
|
||||
throw winrt::hresult_error(E_FAIL, L"Failed to make EGLSurface current");
|
||||
}
|
||||
}
|
||||
|
||||
EGLBoolean OpenGLES::SwapBuffers(const EGLSurface surface) {
|
||||
return (eglSwapBuffers(mEglDisplay, surface));
|
||||
}
|
31
support/hololens/ServoApp/ServoControl/OpenGLES.h
Normal file
31
support/hololens/ServoApp/ServoControl/OpenGLES.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#pragma once
|
||||
|
||||
class OpenGLES {
|
||||
public:
|
||||
OpenGLES();
|
||||
~OpenGLES();
|
||||
|
||||
EGLSurface
|
||||
CreateSurface(winrt::Windows::UI::Xaml::Controls::SwapChainPanel const &);
|
||||
EGLSurface CreateSurface(winrt::Windows::UI::Core::CoreWindow const &);
|
||||
|
||||
void GetSurfaceDimensions(const EGLSurface surface, EGLint *width,
|
||||
EGLint *height);
|
||||
void DestroySurface(const EGLSurface surface);
|
||||
void MakeCurrent(const EGLSurface surface);
|
||||
EGLBoolean SwapBuffers(const EGLSurface surface);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
void Initialize();
|
||||
void Cleanup();
|
||||
|
||||
private:
|
||||
EGLDisplay mEglDisplay = EGL_NO_DISPLAY;
|
||||
EGLContext mEglContext = nullptr;
|
||||
EGLConfig mEglConfig = nullptr;
|
||||
};
|
73
support/hololens/ServoApp/ServoControl/Servo.cpp
Normal file
73
support/hololens/ServoApp/ServoControl/Servo.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "pch.h"
|
||||
#include "Servo.h"
|
||||
|
||||
namespace winrt::servo {
|
||||
|
||||
void on_load_started() { sServo->Delegate().OnServoLoadStarted(); }
|
||||
void on_load_ended() { sServo->Delegate().OnServoLoadEnded(); }
|
||||
void on_history_changed(bool back, bool forward) {
|
||||
sServo->Delegate().OnServoHistoryChanged(back, forward);
|
||||
}
|
||||
void on_shutdown_complete() { sServo->Delegate().OnServoShutdownComplete(); }
|
||||
void on_alert(const char *message) {
|
||||
sServo->Delegate().OnServoAlert(char2hstring(message));
|
||||
}
|
||||
void on_title_changed(const char *title) {
|
||||
sServo->Delegate().OnServoTitleChanged(char2hstring(title));
|
||||
}
|
||||
void on_url_changed(const char *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().OnServoAllowNavigation(char2hstring(url));
|
||||
};
|
||||
void on_animating_changed(bool aAnimating) {
|
||||
sServo->Delegate().OnServoAnimatingChanged(aAnimating);
|
||||
}
|
||||
|
||||
Servo::Servo(GLsizei width, GLsizei height, ServoDelegate &aDelegate)
|
||||
: mWindowHeight(height), mWindowWidth(width), mDelegate(aDelegate) {
|
||||
|
||||
capi::CInitOptions o;
|
||||
o.args = "--pref dom.webxr.enabled";
|
||||
o.url = "https://servo.org";
|
||||
o.width = mWindowWidth;
|
||||
o.height = mWindowHeight;
|
||||
o.density = 1.0;
|
||||
o.enable_subpixel_text_antialiasing = false;
|
||||
o.vr_pointer = NULL;
|
||||
|
||||
sServo = this; // FIXME;
|
||||
|
||||
capi::CHostCallbacks c;
|
||||
c.flush = &flush;
|
||||
c.make_current = &make_current;
|
||||
c.on_alert = &on_alert;
|
||||
c.on_load_started = &on_load_started;
|
||||
c.on_load_ended = &on_load_ended;
|
||||
c.on_title_changed = &on_title_changed;
|
||||
c.on_url_changed = &on_url_changed;
|
||||
c.on_history_changed = &on_history_changed;
|
||||
c.on_animating_changed = &on_animating_changed;
|
||||
c.on_shutdown_complete = &on_shutdown_complete;
|
||||
c.on_allow_navigation = &on_allow_navigation;
|
||||
|
||||
init_with_egl(o, &wakeup, c);
|
||||
}
|
||||
|
||||
Servo::~Servo() { sServo = nullptr; }
|
||||
|
||||
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);
|
||||
std::wstring str2(size_needed, 0);
|
||||
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &str2[0], size_needed);
|
||||
winrt::hstring str3 {str2};
|
||||
return str3;
|
||||
}
|
||||
|
||||
} // namespace servo
|
87
support/hololens/ServoApp/ServoControl/Servo.h
Normal file
87
support/hololens/ServoApp/ServoControl/Servo.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "logs.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace winrt::servo {
|
||||
|
||||
namespace capi {
|
||||
extern "C" {
|
||||
#include <simpleservo.h>
|
||||
}
|
||||
} // namespace capi
|
||||
|
||||
class ServoDelegate {
|
||||
public:
|
||||
// Called from any thread
|
||||
virtual void WakeUp() = 0;
|
||||
// Called from GL thread
|
||||
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;
|
||||
|
||||
protected:
|
||||
virtual ~ServoDelegate(){};
|
||||
};
|
||||
|
||||
class Servo {
|
||||
public:
|
||||
Servo(GLsizei, GLsizei, ServoDelegate &);
|
||||
~Servo();
|
||||
ServoDelegate &Delegate() { return mDelegate; }
|
||||
|
||||
void PerformUpdates() { capi::perform_updates(); }
|
||||
void DeInit() { capi::deinit(); }
|
||||
void RequestShutdown() { capi::request_shutdown(); }
|
||||
void SetBatchMode(bool mode) { capi::set_batch_mode(mode); }
|
||||
void GoForward() { capi::go_forward(); }
|
||||
void GoBack() { capi::go_back(); }
|
||||
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);
|
||||
}
|
||||
void SetSize(GLsizei width, GLsizei height) {
|
||||
if (width != mWindowWidth || height != mWindowHeight) {
|
||||
mWindowWidth = width;
|
||||
mWindowHeight = height;
|
||||
capi::resize(mWindowWidth, mWindowHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ServoDelegate &mDelegate;
|
||||
GLsizei mWindowWidth;
|
||||
GLsizei mWindowHeight;
|
||||
};
|
||||
|
||||
// This is sad. We need a static pointer to Servo because we use function
|
||||
// pointer as callback in Servo, and these functions need a way to get
|
||||
// the Servo instance. See https://github.com/servo/servo/issues/22967
|
||||
static Servo *sServo = nullptr;
|
||||
|
||||
hstring char2hstring(const char *c_str);
|
||||
|
||||
} // namespace servo
|
257
support/hololens/ServoApp/ServoControl/ServoControl.cpp
Normal file
257
support/hololens/ServoApp/ServoControl/ServoControl.cpp
Normal file
|
@ -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
|
110
support/hololens/ServoApp/ServoControl/ServoControl.h
Normal file
110
support/hololens/ServoApp/ServoControl/ServoControl.h
Normal file
|
@ -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
|
20
support/hololens/ServoApp/ServoControl/ServoControl.idl
Normal file
20
support/hololens/ServoApp/ServoControl/ServoControl.idl
Normal file
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue