move control to its own directory

This commit is contained in:
Paul Rouget 2019-08-16 07:30:45 +02:00
parent 663ec48e00
commit 92f57b66d1
12 changed files with 17 additions and 19 deletions

View 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));
}

View 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;
};

View 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

View 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

View 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

View 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

View 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