GHOST: Add support for precision touchpad gestures on Windows

This patch adds support for precision touchpad gestures on Windows 8.1
and newer using Direct Manipulation API. Gestures work exactly like on
macOS, with full support for pan/pinch and inertia. This works by
creating a viewport with a fake scrollable which is reset after every
gesture and converts any changes to the content's transform into GHOST
trackpad events (as explained [here](https://bugzilla.mozilla.org/show_bug.cgi?id=890878)).
The code is based on the implementation from the [Chromium project](https://chromium.googlesource.com/chromium/src/+/refs/heads/master/content/browser/renderer_host/direct_manipulation_helper_win.cc).

Tested on Windows 10.

Fixes {T70754}, {T69264}.

Demo:{F8520272}

Reviewed By: nicholas_rishel

Differential Revision: https://developer.blender.org/D7660
This commit is contained in:
Andrii Symkin 2022-04-06 18:17:33 -07:00 committed by Nicholas Rishel
parent 4a4f0a70eb
commit e58b18888c
Notes: blender-bot 2023-02-14 05:59:31 +01:00
Referenced by issue #97925, No option to restore previous trackpad behavior on Windows since multitouch support
7 changed files with 617 additions and 0 deletions

View File

@ -376,6 +376,7 @@ elseif(WIN32)
intern/GHOST_DisplayManagerWin32.cpp
intern/GHOST_DropTargetWin32.cpp
intern/GHOST_SystemWin32.cpp
intern/GHOST_TrackpadWin32.cpp
intern/GHOST_WindowWin32.cpp
intern/GHOST_Wintab.cpp
@ -384,6 +385,7 @@ elseif(WIN32)
intern/GHOST_DropTargetWin32.h
intern/GHOST_SystemWin32.h
intern/GHOST_TaskbarWin32.h
intern/GHOST_TrackpadWin32.h
intern/GHOST_WindowWin32.h
intern/GHOST_Wintab.h
)

View File

@ -8,6 +8,7 @@
#include "GHOST_SystemWin32.h"
#include "GHOST_ContextD3D.h"
#include "GHOST_EventDragnDrop.h"
#include "GHOST_EventTrackpad.h"
#ifndef _WIN32_IE
# define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
@ -415,6 +416,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
hasEventHandled = true;
}
driveTrackpad();
// Process all the events waiting for us
while (::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) != 0) {
// TranslateMessage doesn't alter the message, and doesn't change our raw keyboard data.
@ -424,6 +427,8 @@ bool GHOST_SystemWin32::processEvents(bool waitForEvent)
hasEventHandled = true;
}
processTrackpad();
/* PeekMessage above is allowed to dispatch messages to the wndproc without us
* noticing, so we need to check the event manager here to see if there are
* events waiting in the queue.
@ -1417,6 +1422,52 @@ bool GHOST_SystemWin32::processNDOF(RAWINPUT const &raw)
}
#endif // WITH_INPUT_NDOF
void GHOST_SystemWin32::driveTrackpad()
{
GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
getWindowManager()->getActiveWindow());
if (active_window) {
active_window->updateDirectManipulation();
}
}
void GHOST_SystemWin32::processTrackpad()
{
GHOST_WindowWin32 *active_window = static_cast<GHOST_WindowWin32 *>(
getWindowManager()->getActiveWindow());
if (!active_window) {
return;
}
GHOST_TTrackpadInfo trackpad_info = active_window->getTrackpadInfo();
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
int32_t cursor_x, cursor_y;
system->getCursorPosition(cursor_x, cursor_y);
if (trackpad_info.x != 0 || trackpad_info.y != 0) {
system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
active_window,
GHOST_kTrackpadEventScroll,
cursor_x,
cursor_y,
trackpad_info.x,
trackpad_info.y,
trackpad_info.isScrollDirectionInverted));
}
if (trackpad_info.scale != 0) {
system->pushEvent(new GHOST_EventTrackpad(system->getMilliSeconds(),
active_window,
GHOST_kTrackpadEventMagnify,
cursor_x,
cursor_y,
trackpad_info.scale,
0,
false));
}
}
LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
GHOST_Event *event = NULL;
@ -1969,6 +2020,8 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
suggestedWindowRect->right - suggestedWindowRect->left,
suggestedWindowRect->bottom - suggestedWindowRect->top,
SWP_NOZORDER | SWP_NOACTIVATE);
window->updateDPI();
}
break;
case WM_DISPLAYCHANGE: {
@ -2063,6 +2116,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* In GHOST, we let DefWindowProc call the timer callback.
*/
break;
case DM_POINTERHITTEST:
/* The DM_POINTERHITTEST message is sent to a window, when pointer input is first
* detected, in order to determine the most probable input target for Direct
* Manipulation. */
window->onPointerHitTest(wParam);
break;
}
}
else {

View File

@ -406,6 +406,16 @@ class GHOST_SystemWin32 : public GHOST_System {
bool processNDOF(RAWINPUT const &raw);
#endif
/**
* Drives Direct Manipulation update.
*/
void driveTrackpad();
/**
* Creates trackpad events for the active window.
*/
void processTrackpad();
/**
* Returns the local state of the modifier keys (from the message queue).
* \param keys: The state of the keys.

View File

@ -0,0 +1,343 @@
/* 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 proccessed 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;
}

View File

@ -0,0 +1,138 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup GHOST
* Declaration of GHOST DirectManipulation classes.
*/
#pragma once
#ifndef WIN32
# error WIN32 only!
#endif // WIN32
#include "GHOST_Types.h"
#include <directmanipulation.h>
#include <wrl.h>
#define PINCH_SCALE_FACTOR 125.0f
typedef struct {
int32_t x, y, scale;
bool isScrollDirectionInverted;
} GHOST_TTrackpadInfo;
class GHOST_DirectManipulationHelper;
class GHOST_DirectManipulationViewportEventHandler
: public Microsoft::WRL::RuntimeClass<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
Microsoft::WRL::Implements<
Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>,
Microsoft::WRL::FtmBase,
IDirectManipulationViewportEventHandler>> {
public:
GHOST_DirectManipulationViewportEventHandler(uint16_t dpi);
/*
* Resets viewport and tracked touchpad state.
*/
void resetViewport(IDirectManipulationViewport *viewport);
/* DirectManipulation callbacks. */
HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport *viewport,
DIRECTMANIPULATION_STATUS current,
DIRECTMANIPULATION_STATUS previous) override;
HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport *viewport) override;
HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport *viewport,
IDirectManipulationContent *content) override;
private:
enum { GESTURE_NONE, GESTURE_PAN, GESTURE_PINCH } gesture_state;
int32_t last_x, last_y, last_scale;
GHOST_TTrackpadInfo accumulated_values;
uint16_t dpi;
DIRECTMANIPULATION_STATUS dm_status;
friend class GHOST_DirectManipulationHelper;
};
class GHOST_DirectManipulationHelper {
public:
/*
* Creates a GHOST_DirectManipulationHelper for the provided window.
* \param hWnd: The window receiving DirectManipulation events.
* \param dpi: The current DPI.
* \return Pointer to the new GHOST_DirectManipulationHelper if created, nullptr if there was an
* error.
*/
static GHOST_DirectManipulationHelper *create(HWND hWnd, uint16_t dpi);
~GHOST_DirectManipulationHelper();
/*
* Drives the DirectManipulation context.
* DirectManipulation's intended use is to tie user input into DirectComposition's compositor
* scaling and translating. We are not using DirectComposition and therefore must drive
* DirectManipulation manually.
*/
void update();
/*
* Sets pointer in contact with the DirectManipulation context.
* \param pointerId: ID of the pointer in contact.
*/
void onPointerHitTest(UINT32 pointerId);
/*
* Updates DPI information for touchpad scaling.
* \param dpi: The new DPI.
*/
void setDPI(uint16_t dpi);
/*
* Retrieves trackpad input.
* \return The accumulated trackpad translation and scale since last call.
*/
GHOST_TTrackpadInfo getTrackpadInfo();
private:
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);
/*
* Retrieves the scroll direction from the registry.
* \return True if scroll direction is inverted.
*/
static bool getScrollDirectionFromReg();
/*
* Registers listener for registry scroll direction entry changes.
*/
void registerScrollDirectionChangeListener();
HWND m_hWnd;
HKEY m_scrollDirectionRegKey;
HANDLE m_scrollDirectionChangeEvent;
Microsoft::WRL::ComPtr<IDirectManipulationManager> m_directManipulationManager;
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> m_directManipulationUpdateManager;
Microsoft::WRL::ComPtr<IDirectManipulationViewport> m_directManipulationViewport;
Microsoft::WRL::ComPtr<GHOST_DirectManipulationViewportEventHandler>
m_directManipulationEventHandler;
DWORD m_directManipulationViewportHandlerCookie;
bool m_isScrollDirectionInverted;
};

View File

@ -68,6 +68,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_normal_state(GHOST_kWindowStateNormal),
m_user32(::LoadLibrary("user32.dll")),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : HWND_DESKTOP),
m_directManipulationHelper(NULL),
m_debug_context(is_debug)
{
DWORD style = parentwindow ?
@ -204,6 +205,42 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
/* Allow the showing of a progress bar on the taskbar. */
CoCreateInstance(
CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
/* Initialize Direct Manipulation. */
m_directManipulationHelper = GHOST_DirectManipulationHelper::create(m_hWnd, getDPIHint());
}
void GHOST_WindowWin32::updateDirectManipulation()
{
if (!m_directManipulationHelper) {
return;
}
m_directManipulationHelper->update();
}
void GHOST_WindowWin32::onPointerHitTest(WPARAM wParam)
{
/* Only DM_POINTERHITTEST can be the first message of input sequence of touchpad input. */
if (!m_directManipulationHelper) {
return;
}
UINT32 pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INPUT_TYPE pointerType;
if (GetPointerType(pointerId, &pointerType) && pointerType == PT_TOUCHPAD) {
m_directManipulationHelper->onPointerHitTest(pointerId);
}
}
GHOST_TTrackpadInfo GHOST_WindowWin32::getTrackpadInfo()
{
if (!m_directManipulationHelper) {
return {0, 0, 0};
}
return m_directManipulationHelper->getTrackpadInfo();
}
GHOST_WindowWin32::~GHOST_WindowWin32()
@ -253,6 +290,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
::DestroyWindow(m_hWnd);
m_hWnd = 0;
}
delete m_directManipulationHelper;
m_directManipulationHelper = NULL;
}
void GHOST_WindowWin32::adjustWindowRectForClosestMonitor(LPRECT win_rect,
@ -1035,6 +1075,13 @@ void GHOST_WindowWin32::ThemeRefresh()
}
}
void GHOST_WindowWin32::updateDPI()
{
if (m_directManipulationHelper) {
m_directManipulationHelper->setDPI(getDPIHint());
}
}
uint16_t GHOST_WindowWin32::getDPIHint()
{
if (m_user32) {

View File

@ -13,6 +13,7 @@
#endif // WIN32
#include "GHOST_TaskbarWin32.h"
#include "GHOST_TrackpadWin32.h"
#include "GHOST_Window.h"
#include "GHOST_Wintab.h"
#ifdef WITH_INPUT_IME
@ -286,6 +287,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
return GHOST_kFailure;
}
void updateDPI();
uint16_t getDPIHint() override;
/** True if the mouse is either over or captured by the window. */
@ -308,6 +311,19 @@ class GHOST_WindowWin32 : public GHOST_Window {
void endIME();
#endif /* WITH_INPUT_IME */
/*
* Drive DirectManipulation context.
*/
void updateDirectManipulation();
/*
* Handle DM_POINTERHITTEST events.
* \param wParam: wParam from the event.
*/
void onPointerHitTest(WPARAM wParam);
GHOST_TTrackpadInfo getTrackpadInfo();
private:
/**
* \param type: The type of rendering context create.
@ -391,6 +407,8 @@ class GHOST_WindowWin32 : public GHOST_Window {
HWND m_parentWindowHwnd;
GHOST_DirectManipulationHelper *m_directManipulationHelper;
#ifdef WITH_INPUT_IME
/** Handle input method editors event */
GHOST_ImeWin32 m_imeInput;