| // 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 |