blob: 72e676a1b32833d0d208898b65c97ae4e016e219 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/fml/logging.h"
#include <algorithm>
#include "flutter/shell/platform/windows/direct_manipulation.h"
#include "flutter/shell/platform/windows/window.h"
#include "flutter/shell/platform/windows/window_binding_handler_delegate.h"
#define RETURN_IF_FAILED(operation) \
if (FAILED(operation)) { \
FML_LOG(ERROR) << #operation << " failed"; \
manager_ = nullptr; \
updateManager_ = nullptr; \
viewport_ = nullptr; \
return -1; \
}
#define WARN_IF_FAILED(operation) \
if (FAILED(operation)) { \
FML_LOG(ERROR) << #operation << " failed"; \
}
namespace flutter {
int32_t DirectManipulationEventHandler::GetDeviceId() {
return (int32_t) reinterpret_cast<int64_t>(this);
}
STDMETHODIMP DirectManipulationEventHandler::QueryInterface(REFIID iid,
void** ppv) {
if ((iid == IID_IUnknown) ||
(iid == IID_IDirectManipulationViewportEventHandler)) {
*ppv = static_cast<IDirectManipulationViewportEventHandler*>(this);
AddRef();
return S_OK;
} else if (iid == IID_IDirectManipulationInteractionEventHandler) {
*ppv = static_cast<IDirectManipulationInteractionEventHandler*>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
DirectManipulationEventHandler::GestureData
DirectManipulationEventHandler::ConvertToGestureData(float transform[6]) {
// DirectManipulation provides updates with very high precision. If the user
// holds their fingers steady on a trackpad, DirectManipulation sends
// jittery updates. This calculation will reduce the precision of the scale
// value of the event to avoid jitter.
const int mantissa_bits_chop = 2;
const float factor = (1 << mantissa_bits_chop) + 1;
float c = factor * transform[0];
return GestureData{
c - (c - transform[0]), // scale
transform[4], // pan_x
transform[5], // pan_y
};
}
HRESULT DirectManipulationEventHandler::OnViewportStatusChanged(
IDirectManipulationViewport* viewport,
DIRECTMANIPULATION_STATUS current,
DIRECTMANIPULATION_STATUS previous) {
if (during_synthesized_reset_) {
during_synthesized_reset_ = current != DIRECTMANIPULATION_READY;
return S_OK;
}
during_inertia_ = current == DIRECTMANIPULATION_INERTIA;
if (current == DIRECTMANIPULATION_RUNNING) {
IDirectManipulationContent* content;
HRESULT hr = viewport->GetPrimaryContent(IID_PPV_ARGS(&content));
if (SUCCEEDED(hr)) {
float transform[6];
hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
if (SUCCEEDED(hr)) {
initial_gesture_data_ = ConvertToGestureData(transform);
} else {
FML_LOG(ERROR) << "GetContentTransform failed";
}
} else {
FML_LOG(ERROR) << "GetPrimaryContent failed";
}
if (owner_->binding_handler_delegate) {
owner_->binding_handler_delegate->OnPointerPanZoomStart(GetDeviceId());
}
} else if (previous == DIRECTMANIPULATION_RUNNING) {
// Reset deltas to ensure only inertia values will be compared later.
last_pan_delta_x_ = 0.0;
last_pan_delta_y_ = 0.0;
if (owner_->binding_handler_delegate) {
owner_->binding_handler_delegate->OnPointerPanZoomEnd(GetDeviceId());
}
} else if (previous == DIRECTMANIPULATION_INERTIA) {
if (owner_->binding_handler_delegate &&
(std::max)(std::abs(last_pan_delta_x_), std::abs(last_pan_delta_y_)) >
0.01) {
owner_->binding_handler_delegate->OnScrollInertiaCancel(GetDeviceId());
}
// Need to reset the content transform to its original position
// so that we are ready for the next gesture.
// Use during_synthesized_reset_ flag to prevent sending reset also to the
// framework.
during_synthesized_reset_ = true;
last_pan_x_ = 0.0;
last_pan_y_ = 0.0;
last_pan_delta_x_ = 0.0;
last_pan_delta_y_ = 0.0;
RECT rect;
HRESULT hr = viewport->GetViewportRect(&rect);
if (FAILED(hr)) {
FML_LOG(ERROR) << "Failed to get the current viewport rect";
return E_FAIL;
}
hr = viewport->ZoomToRect(rect.left, rect.top, rect.right, rect.bottom,
false);
if (FAILED(hr)) {
FML_LOG(ERROR) << "Failed to reset the gesture using ZoomToRect";
return E_FAIL;
}
}
return S_OK;
}
HRESULT DirectManipulationEventHandler::OnViewportUpdated(
IDirectManipulationViewport* viewport) {
return S_OK;
}
HRESULT DirectManipulationEventHandler::OnContentUpdated(
IDirectManipulationViewport* viewport,
IDirectManipulationContent* content) {
float transform[6];
HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
if (FAILED(hr)) {
FML_LOG(ERROR) << "GetContentTransform failed";
return S_OK;
}
if (!during_synthesized_reset_) {
GestureData data = ConvertToGestureData(transform);
float scale = data.scale / initial_gesture_data_.scale;
float pan_x = data.pan_x - initial_gesture_data_.pan_x;
float pan_y = data.pan_y - initial_gesture_data_.pan_y;
last_pan_delta_x_ = pan_x - last_pan_x_;
last_pan_delta_y_ = pan_y - last_pan_y_;
last_pan_x_ = pan_x;
last_pan_y_ = pan_y;
if (owner_->binding_handler_delegate && !during_inertia_) {
owner_->binding_handler_delegate->OnPointerPanZoomUpdate(
GetDeviceId(), pan_x, pan_y, scale, 0);
}
}
return S_OK;
}
HRESULT DirectManipulationEventHandler::OnInteraction(
IDirectManipulationViewport2* viewport,
DIRECTMANIPULATION_INTERACTION_TYPE interaction) {
return S_OK;
}
ULONG STDMETHODCALLTYPE DirectManipulationEventHandler::AddRef() {
RefCountedThreadSafe::AddRef();
return 0;
}
ULONG STDMETHODCALLTYPE DirectManipulationEventHandler::Release() {
RefCountedThreadSafe::Release();
return 0;
}
DirectManipulationOwner::DirectManipulationOwner(Window* window)
: window_(window) {}
int DirectManipulationOwner::Init(unsigned int width, unsigned int height) {
RETURN_IF_FAILED(CoCreateInstance(CLSID_DirectManipulationManager, nullptr,
CLSCTX_INPROC_SERVER,
IID_IDirectManipulationManager, &manager_));
RETURN_IF_FAILED(manager_->GetUpdateManager(
IID_IDirectManipulationUpdateManager, &updateManager_));
RETURN_IF_FAILED(manager_->CreateViewport(nullptr, window_->GetWindowHandle(),
IID_IDirectManipulationViewport,
&viewport_));
DIRECTMANIPULATION_CONFIGURATION configuration =
DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
DIRECTMANIPULATION_CONFIGURATION_SCALING |
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA;
RETURN_IF_FAILED(viewport_->ActivateConfiguration(configuration));
RETURN_IF_FAILED(viewport_->SetViewportOptions(
DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE));
handler_ = fml::MakeRefCounted<DirectManipulationEventHandler>(this);
RETURN_IF_FAILED(viewport_->AddEventHandler(
window_->GetWindowHandle(), handler_.get(), &viewportHandlerCookie_));
RECT rect = {0, 0, (LONG)width, (LONG)height};
RETURN_IF_FAILED(viewport_->SetViewportRect(&rect));
RETURN_IF_FAILED(manager_->Activate(window_->GetWindowHandle()));
RETURN_IF_FAILED(viewport_->Enable());
RETURN_IF_FAILED(updateManager_->Update(nullptr));
return 0;
}
void DirectManipulationOwner::ResizeViewport(unsigned int width,
unsigned int height) {
if (viewport_) {
RECT rect = {0, 0, (LONG)width, (LONG)height};
WARN_IF_FAILED(viewport_->SetViewportRect(&rect));
}
}
void DirectManipulationOwner::Destroy() {
if (handler_) {
handler_->owner_ = nullptr;
}
if (viewport_) {
WARN_IF_FAILED(viewport_->Disable());
WARN_IF_FAILED(viewport_->Disable());
WARN_IF_FAILED(viewport_->RemoveEventHandler(viewportHandlerCookie_));
WARN_IF_FAILED(viewport_->Abandon());
}
if (window_ && manager_) {
WARN_IF_FAILED(manager_->Deactivate(window_->GetWindowHandle()));
}
handler_ = nullptr;
viewport_ = nullptr;
updateManager_ = nullptr;
manager_ = nullptr;
window_ = nullptr;
}
void DirectManipulationOwner::SetContact(UINT contactId) {
if (viewport_) {
viewport_->SetContact(contactId);
}
}
void DirectManipulationOwner::SetBindingHandlerDelegate(
WindowBindingHandlerDelegate* delegate) {
binding_handler_delegate = delegate;
}
void DirectManipulationOwner::Update() {
if (updateManager_) {
HRESULT hr = updateManager_->Update(nullptr);
if (FAILED(hr)) {
FML_LOG(ERROR) << "updateManager_->Update failed";
auto error = GetLastError();
FML_LOG(ERROR) << error;
LPWSTR message = nullptr;
size_t size = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, NULL);
FML_LOG(ERROR) << message;
}
}
}
} // namespace flutter