When the warning level is set to 4, some unreferenced parameter warnings can appear This commit resolves some of those warnings.
344 lines
12 KiB
C++
344 lines
12 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup GHOST
|
|
*/
|
|
|
|
#include <cmath>
|
|
|
|
#include "GHOST_Debug.h"
|
|
#include "GHOST_TrackpadWin32.h"
|
|
|
|
GHOST_DirectManipulationHelper::GHOST_DirectManipulationHelper(
|
|
HWND hWnd,
|
|
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager,
|
|
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager,
|
|
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport,
|
|
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
|
|
directManipulationEventHandler,
|
|
DWORD directManipulationViewportHandlerCookie,
|
|
bool isScrollDirectionInverted)
|
|
: m_hWnd(hWnd),
|
|
m_scrollDirectionRegKey(NULL),
|
|
m_scrollDirectionChangeEvent(NULL),
|
|
m_directManipulationManager(directManipulationManager),
|
|
m_directManipulationUpdateManager(directManipulationUpdateManager),
|
|
m_directManipulationViewport(directManipulationViewport),
|
|
m_directManipulationEventHandler(directManipulationEventHandler),
|
|
m_directManipulationViewportHandlerCookie(directManipulationViewportHandlerCookie),
|
|
m_isScrollDirectionInverted(isScrollDirectionInverted)
|
|
{
|
|
}
|
|
|
|
GHOST_DirectManipulationHelper *GHOST_DirectManipulationHelper::create(HWND hWnd, uint16_t dpi)
|
|
{
|
|
#define DM_CHECK_RESULT_AND_EXIT_EARLY(hr, failMessage) \
|
|
{ \
|
|
if (!SUCCEEDED(hr)) { \
|
|
GHOST_PRINT(failMessage); \
|
|
return nullptr; \
|
|
} \
|
|
}
|
|
|
|
Microsoft::WRL::ComPtr<IDirectManipulationManager> directManipulationManager;
|
|
HRESULT hr = ::CoCreateInstance(CLSID_DirectManipulationManager,
|
|
nullptr,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_PPV_ARGS(&directManipulationManager));
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager create failed\n");
|
|
|
|
/* Since we want to use fake viewport, we need to send fake updates to UpdateManager. */
|
|
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> directManipulationUpdateManager;
|
|
hr = directManipulationManager->GetUpdateManager(IID_PPV_ARGS(&directManipulationUpdateManager));
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Get UpdateManager failed\n");
|
|
|
|
Microsoft::WRL::ComPtr<IDirectManipulationViewport> directManipulationViewport;
|
|
hr = directManipulationManager->CreateViewport(
|
|
nullptr, hWnd, IID_PPV_ARGS(&directManipulationViewport));
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport create failed\n");
|
|
|
|
DIRECTMANIPULATION_CONFIGURATION configuration =
|
|
DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
|
|
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
|
|
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
|
|
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_INERTIA |
|
|
DIRECTMANIPULATION_CONFIGURATION_SCALING;
|
|
|
|
hr = directManipulationViewport->ActivateConfiguration(configuration);
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ActivateConfiguration failed\n");
|
|
|
|
/* Since we are using fake viewport and only want to use Direct Manipulation for touchpad, we
|
|
* need to use MANUALUPDATE option. */
|
|
hr = directManipulationViewport->SetViewportOptions(
|
|
DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE);
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set ViewportOptions failed\n");
|
|
|
|
/* We receive Direct Manipulation transform updates in IDirectManipulationViewportEventHandler
|
|
* callbacks. */
|
|
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
|
|
directManipulationEventHandler =
|
|
Microsoft::WRL::Make<GHOST_DirectManipulationViewportEventHandler>(dpi);
|
|
DWORD directManipulationViewportHandlerCookie;
|
|
directManipulationViewport->AddEventHandler(
|
|
hWnd, directManipulationEventHandler.Get(), &directManipulationViewportHandlerCookie);
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport add EventHandler failed\n");
|
|
|
|
/* Set default rect for viewport before activating. */
|
|
RECT rect = {0, 0, 10000, 10000};
|
|
hr = directManipulationViewport->SetViewportRect(&rect);
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport set rect failed\n");
|
|
|
|
hr = directManipulationManager->Activate(hWnd);
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "DirectManipulationManager activate failed\n");
|
|
|
|
hr = directManipulationViewport->Enable();
|
|
DM_CHECK_RESULT_AND_EXIT_EARLY(hr, "Viewport enable failed\n");
|
|
|
|
directManipulationEventHandler->resetViewport(directManipulationViewport.Get());
|
|
|
|
bool isScrollDirectionInverted = getScrollDirectionFromReg();
|
|
|
|
auto instance = new GHOST_DirectManipulationHelper(hWnd,
|
|
directManipulationManager,
|
|
directManipulationUpdateManager,
|
|
directManipulationViewport,
|
|
directManipulationEventHandler,
|
|
directManipulationViewportHandlerCookie,
|
|
isScrollDirectionInverted);
|
|
|
|
instance->registerScrollDirectionChangeListener();
|
|
|
|
return instance;
|
|
|
|
#undef DM_CHECK_RESULT_AND_EXIT_EARLY
|
|
}
|
|
|
|
bool GHOST_DirectManipulationHelper::getScrollDirectionFromReg()
|
|
{
|
|
DWORD scrollDirectionRegValue, pcbData;
|
|
HRESULT hr = HRESULT_FROM_WIN32(
|
|
RegGetValueW(HKEY_CURRENT_USER,
|
|
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
|
|
L"ScrollDirection",
|
|
RRF_RT_REG_DWORD,
|
|
NULL,
|
|
&scrollDirectionRegValue,
|
|
&pcbData));
|
|
if (!SUCCEEDED(hr)) {
|
|
GHOST_PRINT("Failed to get scroll direction from registry\n");
|
|
return false;
|
|
}
|
|
|
|
return scrollDirectionRegValue == 0;
|
|
}
|
|
|
|
void GHOST_DirectManipulationHelper::registerScrollDirectionChangeListener()
|
|
{
|
|
|
|
if (!m_scrollDirectionRegKey) {
|
|
HRESULT hr = HRESULT_FROM_WIN32(
|
|
RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PrecisionTouchPad\\",
|
|
0,
|
|
KEY_NOTIFY,
|
|
&m_scrollDirectionRegKey));
|
|
if (!SUCCEEDED(hr)) {
|
|
GHOST_PRINT("Failed to open scroll direction registry key\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!m_scrollDirectionChangeEvent) {
|
|
m_scrollDirectionChangeEvent = CreateEventW(NULL, true, false, NULL);
|
|
}
|
|
else {
|
|
ResetEvent(m_scrollDirectionChangeEvent);
|
|
}
|
|
HRESULT hr = HRESULT_FROM_WIN32(RegNotifyChangeKeyValue(m_scrollDirectionRegKey,
|
|
true,
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
m_scrollDirectionChangeEvent,
|
|
true));
|
|
if (!SUCCEEDED(hr)) {
|
|
GHOST_PRINT("Failed to register scroll direction change listener\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
void GHOST_DirectManipulationHelper::onPointerHitTest(UINT32 pointerId)
|
|
{
|
|
[[maybe_unused]] HRESULT hr = m_directManipulationViewport->SetContact(pointerId);
|
|
GHOST_ASSERT(SUCCEEDED(hr), "Viewport set contact failed\n");
|
|
|
|
if (WaitForSingleObject(m_scrollDirectionChangeEvent, 0) == WAIT_OBJECT_0) {
|
|
m_isScrollDirectionInverted = getScrollDirectionFromReg();
|
|
registerScrollDirectionChangeListener();
|
|
}
|
|
}
|
|
|
|
void GHOST_DirectManipulationHelper::update()
|
|
{
|
|
if (m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_RUNNING ||
|
|
m_directManipulationEventHandler->dm_status == DIRECTMANIPULATION_INERTIA) {
|
|
[[maybe_unused]] HRESULT hr = m_directManipulationUpdateManager->Update(nullptr);
|
|
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationUpdateManager update failed\n");
|
|
}
|
|
}
|
|
|
|
void GHOST_DirectManipulationHelper::setDPI(uint16_t dpi)
|
|
{
|
|
m_directManipulationEventHandler->dpi = dpi;
|
|
}
|
|
|
|
GHOST_TTrackpadInfo GHOST_DirectManipulationHelper::getTrackpadInfo()
|
|
{
|
|
GHOST_TTrackpadInfo result = m_directManipulationEventHandler->accumulated_values;
|
|
result.isScrollDirectionInverted = m_isScrollDirectionInverted;
|
|
|
|
m_directManipulationEventHandler->accumulated_values = {0, 0, 0};
|
|
return result;
|
|
}
|
|
|
|
GHOST_DirectManipulationHelper::~GHOST_DirectManipulationHelper()
|
|
{
|
|
HRESULT hr;
|
|
hr = m_directManipulationViewport->Stop();
|
|
GHOST_ASSERT(SUCCEEDED(hr), "Viewport stop failed\n");
|
|
|
|
hr = m_directManipulationViewport->RemoveEventHandler(m_directManipulationViewportHandlerCookie);
|
|
GHOST_ASSERT(SUCCEEDED(hr), "Viewport remove event handler failed\n");
|
|
|
|
hr = m_directManipulationViewport->Abandon();
|
|
GHOST_ASSERT(SUCCEEDED(hr), "Viewport abandon failed\n");
|
|
|
|
hr = m_directManipulationManager->Deactivate(m_hWnd);
|
|
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationManager deactivate failed\n");
|
|
|
|
if (m_scrollDirectionChangeEvent) {
|
|
CloseHandle(m_scrollDirectionChangeEvent);
|
|
m_scrollDirectionChangeEvent = NULL;
|
|
}
|
|
if (m_scrollDirectionRegKey) {
|
|
RegCloseKey(m_scrollDirectionRegKey);
|
|
m_scrollDirectionRegKey = NULL;
|
|
}
|
|
}
|
|
|
|
GHOST_DirectManipulationViewportEventHandler::GHOST_DirectManipulationViewportEventHandler(
|
|
uint16_t dpi)
|
|
: accumulated_values({0, 0, 0}), dpi(dpi), dm_status(DIRECTMANIPULATION_BUILDING)
|
|
{
|
|
}
|
|
|
|
void GHOST_DirectManipulationViewportEventHandler::resetViewport(
|
|
IDirectManipulationViewport *viewport)
|
|
{
|
|
if (gesture_state != GESTURE_NONE) {
|
|
[[maybe_unused]] HRESULT hr = viewport->ZoomToRect(0.0f, 0.0f, 10000.0f, 10000.0f, FALSE);
|
|
GHOST_ASSERT(SUCCEEDED(hr), "Viewport reset failed\n");
|
|
}
|
|
|
|
gesture_state = GESTURE_NONE;
|
|
|
|
last_scale = PINCH_SCALE_FACTOR;
|
|
last_x = 0.0f;
|
|
last_y = 0.0f;
|
|
}
|
|
|
|
HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportStatusChanged(
|
|
IDirectManipulationViewport *viewport,
|
|
DIRECTMANIPULATION_STATUS current,
|
|
DIRECTMANIPULATION_STATUS previous)
|
|
{
|
|
dm_status = current;
|
|
|
|
if (current == previous) {
|
|
return S_OK;
|
|
}
|
|
|
|
if (previous == DIRECTMANIPULATION_ENABLED || current == DIRECTMANIPULATION_READY ||
|
|
(previous == DIRECTMANIPULATION_INERTIA && current != DIRECTMANIPULATION_INERTIA)) {
|
|
resetViewport(viewport);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT GHOST_DirectManipulationViewportEventHandler::OnViewportUpdated(
|
|
IDirectManipulationViewport * /*viewport*/)
|
|
{
|
|
/* Nothing to do here. */
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT GHOST_DirectManipulationViewportEventHandler::OnContentUpdated(
|
|
IDirectManipulationViewport * /*viewport*/, IDirectManipulationContent *content)
|
|
{
|
|
float transform[6];
|
|
HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
|
|
GHOST_ASSERT(SUCCEEDED(hr), "DirectManipulationContent get transform failed\n");
|
|
|
|
const float device_scale_factor = dpi / 96.0f;
|
|
|
|
const float scale = transform[0] * PINCH_SCALE_FACTOR;
|
|
const float x = transform[4] / device_scale_factor;
|
|
const float y = transform[5] / device_scale_factor;
|
|
|
|
const float EPS = 3e-5;
|
|
|
|
/* Ignore repeating or incorrect input. */
|
|
if ((fabs(scale - last_scale) <= EPS && fabs(x - last_x) <= EPS && fabs(y - last_y) <= EPS) ||
|
|
scale == 0.0f) {
|
|
GHOST_PRINT("Ignoring touchpad input\n");
|
|
return hr;
|
|
}
|
|
|
|
/* Assume that every gesture is a pan in the beginning.
|
|
* If it's a pinch, the gesture will be changed below. */
|
|
if (gesture_state == GESTURE_NONE) {
|
|
gesture_state = GESTURE_PAN;
|
|
}
|
|
|
|
/* DM doesn't always immediately recognize pinch gestures,
|
|
* so allow transition from pan to pinch. */
|
|
if (gesture_state == GESTURE_PAN) {
|
|
if (fabs(scale - PINCH_SCALE_FACTOR) > EPS) {
|
|
gesture_state = GESTURE_PINCH;
|
|
}
|
|
}
|
|
|
|
/* This state machine is used here because:
|
|
* 1. Pinch and pan gestures must be differentiated and cannot be processed at the same time
|
|
* because XY transform values become nonsensical during pinch gesture.
|
|
* 2. GHOST requires delta values for events while DM provides transformation matrix of the
|
|
* current gesture.
|
|
* 3. GHOST events accept integer values while DM values are non-integer.
|
|
* Truncated fractional parts are accumulated and accounted for in following updates.
|
|
*/
|
|
switch (gesture_state) {
|
|
case GESTURE_PINCH: {
|
|
int32_t dscale = roundf(scale - last_scale);
|
|
|
|
last_scale += dscale;
|
|
|
|
accumulated_values.scale += dscale;
|
|
break;
|
|
}
|
|
case GESTURE_PAN: {
|
|
int32_t dx = roundf(x - last_x);
|
|
int32_t dy = roundf(y - last_y);
|
|
|
|
last_x += dx;
|
|
last_y += dy;
|
|
|
|
accumulated_values.x += dx;
|
|
accumulated_values.y += dy;
|
|
break;
|
|
}
|
|
case GESTURE_NONE:
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|