|  | // 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/flutter_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(FlutterWindow* 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 |