Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
[Some information relates to pre-released product which may be substantially modified before it's commercially released. Microsoft makes no warranties, express or implied, with respect to the information provided here.]
This guide demonstrates three key scenarios for working with the Precision Touchpad APIs: receiving two-finger touchpad gestures and reporting inertia, adding handlers for global touchpad gestures, and injecting touchpad input into the system.
Receiving two-finger touchpad gestures and reporting inertia
This sample demonstrates how touchpad input can be received for two-finger gestures, and how inertia can be reported based on gesture recognition.
C++ example
class PointerInputSample :
public winrt::implements<PointerInputSample,
winrt::Windows::Foundation::IInspectable>
{
public:
void InitializeComponent();
private:
static LRESULT CALLBACK WndProcStatic(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static void CALLBACK OutputCallbackStatic(
PVOID pvClient, const INTERACTION_CONTEXT_OUTPUT* pOutput);
void OutputCallback(const INTERACTION_CONTEXT_OUTPUT* pOutput);
void InitializeInteractionContext();
void ProcessPointerInput(WPARAM wParam);
void ProcessTimer();
void ProcessInertia(UINT message);
private:
wil::unique_hwnd m_hwnd = nullptr;
using unique_interaction_context =
wil::unique_any<HINTERACTIONCONTEXT,
decltype(&::DestroyInteractionContext),
::DestroyInteractionContext>;
unique_interaction_context m_interactionContext;
UINT_PTR m_inertiaTimer = 0;
INTERACTION_FLAGS m_lastInteractionFlags = INTERACTION_FLAG_NONE;
};
void
PointerInputSample::InitializeComponent()
{
// Create and show a window.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.hCursor = ::LoadCursor(nullptr, IDC_ARROW);
wcex.lpszClassName = L"PointerInputSampleClass";
wcex.lpfnWndProc = WndProcStatic;
m_hwnd.reset(::CreateWindow(
MAKEINTATOM(::RegisterClassEx(&wcex)),
L"Touchpad Pointer Input Sample",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
50, 50, 450, 150,
nullptr, nullptr, nullptr, this));
THROW_LAST_ERROR_IF_NULL(m_hwnd);
// Register the window as being able to receive WM_POINTER messages for touchpads.
THROW_IF_WIN32_BOOL_FALSE(::RegisterTouchpadCapableWindow(
m_hwnd.get(),
TRUE /*fEnable*/));
// Create and configure an Interaction Context that will be fed the touchpad input.
InitializeInteractionContext();
}
void
PointerInputSample::InitializeInteractionContext()
{
THROW_IF_FAILED(::CreateInteractionContext(&m_interactionContext));
THROW_IF_FAILED(::RegisterOutputCallbackInteractionContext(
m_interactionContext.get(),
OutputCallbackStatic,
this));
// Configure the Interaction Context with railed panning support with inertia.
INTERACTION_CONTEXT_CONFIGURATION cfg[] =
{
{INTERACTION_ID_MANIPULATION,
INTERACTION_CONFIGURATION_FLAG_MANIPULATION |
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_X |
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_Y |
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_INERTIA |
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_RAILS_X |
INTERACTION_CONFIGURATION_FLAG_MANIPULATION_RAILS_Y},
};
THROW_IF_FAILED(::SetInteractionConfigurationInteractionContext(
m_interactionContext.get(),
ARRAYSIZE(cfg),
cfg));
// By using himetric measurement units, the output will be in the coordinate
// space of the device. Screen measurement units are special for touchpads
// because the same gesture can result in different screen output, depending
// on the scale factor of the display the gesture is performed on.
THROW_IF_FAILED(::SetPropertyInteractionContext(
m_interactionContext.get(),
INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS,
0 /*himetric*/));
// By setting this property, contacts fed to the IC are implicitly assumed to
// be part of the interaction, rather than requiring explicit inclusion via
// AddPointerInteractionContext.
THROW_IF_FAILED(::SetPropertyInteractionContext(
m_interactionContext.get(),
INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS,
FALSE));
}
LRESULT CALLBACK
PointerInputSample::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
auto pThis = reinterpret_cast<PointerInputSample*>(pCreate->lpCreateParams);
::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
break;
}
case WM_DESTROY:
{
::PostQuitMessage(0);
break;
}
default:
{
auto pThis = reinterpret_cast<PointerInputSample*>(
::GetWindowLongPtr(hWnd, GWLP_USERDATA));
return pThis->WndProc(hWnd, message, wParam, lParam);
}
}
return 0;
}
LRESULT CALLBACK
PointerInputSample::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
try
{
switch (message)
{
case WM_POINTERDOWN:
case WM_POINTERUPDATE:
case WM_POINTERUP:
ProcessPointerInput(wParam);
break;
case WM_TIMER:
ProcessTimer();
break;
case WM_STOPINERTIA:
case WM_ENDINERTIA:
ProcessInertia(message);
break;
default:
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
}
catch (...)
{
return 0;
}
return 0;
}
void
PointerInputSample::ProcessPointerInput(WPARAM wParam)
{
UINT32 const pointerId = GET_POINTERID_WPARAM(wParam);
POINTER_INFO info;
THROW_IF_WIN32_BOOL_FALSE(::GetPointerInfo(pointerId, &info));
// Filter to only touchpad pointer input.
if (info.pointerType != PT_TOUCHPAD)
{
return;
}
// The full touchpad info can be queried via GetPointerTouchpadInfo, but
// since we're just feeding the input into our InteractionContext, we want
// to retrieve the basic POINTER_INFO data.
UINT32 cFrames, cPointers;
THROW_IF_WIN32_BOOL_FALSE(::GetPointerFrameTouchpadInfoHistory(
pointerId,
&cFrames,
&cPointers,
nullptr));
auto pointerTouchInfos = std::make_unique<POINTER_TOUCH_INFO[]>(cFrames * cPointers);
THROW_IF_WIN32_BOOL_FALSE(::GetPointerFrameTouchpadInfoHistory(
pointerId,
&cFrames,
&cPointers,
pointerTouchInfos.get()));
// Copy the input frames into POINTER_TYPE_INFO structs.
auto pointerTypeInfos = std::make_unique<POINTER_TYPE_INFO[]>(cFrames * cPointers);
for (DWORD idx = 0; idx != cFrames * cPointers; ++idx)
{
pointerTypeInfos[idx].type = pointerTouchInfos[idx].pointerInfo.pointerType;
pointerTypeInfos[idx].touchInfo = pointerTouchInfos[idx];
}
// Feed the input into our InteractionContext for gesture recognition.
THROW_IF_FAILED(::ProcessPointerFramesInteractionContext2(
m_interactionContext.get(),
cFrames,
cPointers,
pointerTypeInfos.get()));
// Skip the pointer messages associated with the rest of the frame.
THROW_IF_WIN32_BOOL_FALSE(::SkipPointerFrameMessages(pointerId));
}
void
PointerInputSample::ProcessTimer()
{
// When the inertia timer fires, we tell our IC to process inertia.
THROW_IF_FAILED(::ProcessInertiaInteractionContext(m_interactionContext.get()));
}
void
PointerInputSample::ProcessInertia(UINT message)
{
// WM_STOPINERTIA => the inertia animation should be stopped, but the content
// being manipulated can remain in an inertial state for the purposes of
// continuing the manipulation upon further input.
// WM_ENDINERTIA => the inertia animation should be stopped and the content should
// no longer be in an inertial state.
std::printf("INERTIA MESSAGE: %s\n",
(message == WM_STOPINERTIA) ? "WM_STOPINERTIA" : "WM_ENDINERTIA");
THROW_IF_FAILED(::StopInteractionContext(m_interactionContext.get()));
}
void CALLBACK
PointerInputSample::OutputCallbackStatic(
PVOID pvClient,
const INTERACTION_CONTEXT_OUTPUT* pOutput)
{
reinterpret_cast<PointerInputSample*>(pvClient)->OutputCallback(pOutput);
}
void
PointerInputSample::OutputCallback(const INTERACTION_CONTEXT_OUTPUT* pOutput)
{
// Log the interaction flags to keep track of the manipulation's state.
if (pOutput->interactionFlags != m_lastInteractionFlags)
{
m_lastInteractionFlags = pOutput->interactionFlags;
if (m_lastInteractionFlags != INTERACTION_FLAG_NONE)
{
bool addedFlag = false;
std::stringstream flags;
auto checkFlag = [&](INTERACTION_FLAGS flag, std::string text)
{
if (WI_IsAnyFlagSet(m_lastInteractionFlags, flag))
{
if (addedFlag)
{
flags << " | ";
}
addedFlag = true;
flags << text;
}
};
checkFlag(INTERACTION_FLAG_BEGIN, "BEGIN");
checkFlag(INTERACTION_FLAG_END, "END");
checkFlag(INTERACTION_FLAG_CANCEL, "CANCEL");
checkFlag(INTERACTION_FLAG_INERTIA, "INERTIA");
std::printf("INTERACTION OUTPUT: %s\n", flags.str().c_str());
}
}
// If inertia is present in the output, ensure that our inertia timer
// is running and that the inertia is reported to the OS.
if (WI_IsFlagSet(pOutput->interactionFlags, INTERACTION_FLAG_INERTIA) &&
(m_inertiaTimer == 0))
{
std::printf("<REPORTING INERTIA START>\n");
m_inertiaTimer = ::SetTimer(m_hwnd.get(), 1, 15, nullptr);
::ReportWindowContentInertia(m_hwnd.get(), true);
}
// If the interaction is ending or no longer in inertia, cancel the
// inertia timer and report the end of inertia to the OS.
if ((m_inertiaTimer != 0) &&
(WI_IsFlagSet(pOutput->interactionFlags, INTERACTION_FLAG_END) ||
!WI_IsFlagSet(pOutput->interactionFlags, INTERACTION_FLAG_INERTIA)))
{
std::printf("<REPORTING INERTIA STOP>\n");
::KillTimer(m_hwnd.get(), m_inertiaTimer);
m_inertiaTimer = 0;
::ReportWindowContentInertia(m_hwnd.get(), false);
}
}
Adding handlers for global touchpad gestures
This sample demonstrates how to listen for global touchpad gestures when in foreground.
C++/WinRT example
class GlobalGesturesSample :
public winrt::implements<GlobalGesturesSample,
winrt::Windows::Foundation::IInspectable>
{
public:
void InitializeComponent();
void final_release();
private:
void OnGlobalActionPerformed(
const winrt::Windows::UI::Input::TouchpadGesturesController& sender,
const winrt::Windows::UI::Input::TouchpadGlobalActionEventArgs& args);
void OnPointerPressed(
const winrt::Windows::UI::Input::TouchpadGesturesController& sender,
const winrt::Windows::UI::Core::PointerEventArgs& args);
void OnPointerMoved(
const winrt::Windows::UI::Input::TouchpadGesturesController& sender,
const winrt::Windows::UI::Core::PointerEventArgs& args);
void OnPointerReleased(
const winrt::Windows::UI::Input::TouchpadGesturesController& sender,
const winrt::Windows::UI::Core::PointerEventArgs& args);
void OnManipulationCompleted(
const winrt::Windows::UI::Input::PhysicalGestureRecognizer& sender,
const winrt::Windows::UI::Input::ManipulationCompletedEventArgs& args);
static LRESULT CALLBACK WndProcStatic(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
private:
winrt::Windows::UI::Input::TouchpadGesturesController m_controller = nullptr;
winrt::Windows::UI::Input::PhysicalGestureRecognizer m_recognizer = nullptr;
winrt::event_token m_globalActionEvent{};
winrt::event_token m_pointerPressedEvent{};
winrt::event_token m_pointerMovedEvent{};
winrt::event_token m_pointerReleasedEvent{};
winrt::event_token m_manipulationCompletedEvent{};
wil::unique_hwnd m_hwnd = nullptr;
};
void
GlobalGesturesSample::InitializeComponent()
{
// Create the TouchpadGesturesController and configure it to receive 3-finger
// actions (taps and button presses) and manipulations (finger motion).
THROW_HR_IF_FALSE(E_ABORT, winrt::TouchpadGesturesController::IsSupported());
m_controller = winrt::TouchpadGesturesController::CreateForProcess();
m_controller.Enabled(true);
m_controller.SupportedGestures(
winrt::TouchpadGlobalGestureKinds::ThreeFingerActions |
winrt::TouchpadGlobalGestureKinds::ThreeFingerManipulations);
m_globalActionEvent = m_controller.GlobalActionPerformed(
{ get_weak(), &GlobalGesturesSample::OnGlobalActionPerformed });
m_pointerPressedEvent = m_controller.PointerPressed(
{ get_weak(), &GlobalGesturesSample::OnPointerPressed });
m_pointerMovedEvent = m_controller.PointerMoved(
{ get_weak(), &GlobalGesturesSample::OnPointerMoved });
m_pointerReleasedEvent = m_controller.PointerReleased(
{ get_weak(), &GlobalGesturesSample::OnPointerReleased });
// Create the PhysicalGestureRecognizer. It will be used to recognize the pointer
// input received for 3-finger manipulations.
m_recognizer = winrt::PhysicalGestureRecognizer();
m_recognizer.GestureSettings(
winrt::GestureSettings::ManipulationTranslateX |
winrt::GestureSettings::ManipulationTranslateY |
winrt::GestureSettings::ManipulationTranslateRailsX |
winrt::GestureSettings::ManipulationTranslateRailsY);
m_manipulationCompletedEvent = m_recognizer.ManipulationCompleted(
{ get_weak(), &GlobalGesturesSample::OnManipulationCompleted });
// Create and show a window. The TouchpadGesturesController can only receive
// events if the current process is in foreground.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.hCursor = ::LoadCursor(nullptr, IDC_ARROW);
wcex.lpszClassName = L"GlobalGesturesSampleClass";
wcex.lpfnWndProc = WndProcStatic;
m_hwnd.reset(::CreateWindow(
MAKEINTATOM(::RegisterClassEx(&wcex)),
L"Touchpad Global Gestures Sample",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
50, 250, 450, 150,
nullptr, nullptr, nullptr, this));
THROW_LAST_ERROR_IF_NULL(m_hwnd);
}
void
GlobalGesturesSample::final_release()
{
m_controller.GlobalActionPerformed(m_globalActionEvent);
m_controller.PointerPressed(m_globalActionEvent);
m_controller.PointerMoved(m_globalActionEvent);
m_controller.PointerReleased(m_globalActionEvent);
m_recognizer.ManipulationCompleted(m_manipulationCompletedEvent);
}
LRESULT CALLBACK
GlobalGesturesSample::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
auto pThis = reinterpret_cast<GlobalGesturesSample*>(pCreate->lpCreateParams);
::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pThis));
break;
}
case WM_DESTROY:
{
::PostQuitMessage(0);
break;
}
default:
{
auto pThis = reinterpret_cast<GlobalGesturesSample*>(
::GetWindowLongPtr(hWnd, GWLP_USERDATA));
return pThis->WndProc(hWnd, message, wParam, lParam);
}
}
return 0;
}
LRESULT CALLBACK
GlobalGesturesSample::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
void
GlobalGesturesSample::OnGlobalActionPerformed(
const winrt::TouchpadGesturesController& /* sender */,
const winrt::TouchpadGlobalActionEventArgs& args)
{
winrt::TouchpadGlobalAction action = args.Action();
switch (action)
{
case winrt::TouchpadGlobalAction::ThreeFingerTap:
std::printf("<TAP>\n");
break;
case winrt::TouchpadGlobalAction::ThreeFingerPress:
std::printf("<PRESS DOWN>\n");
break;
case winrt::TouchpadGlobalAction::ThreeFingerRelease:
std::printf("<PRESS UP>\n");
break;
}
}
void
GlobalGesturesSample::OnPointerPressed(
const winrt::Windows::UI::Input::TouchpadGesturesController& /* sender */,
const winrt::Windows::UI::Core::PointerEventArgs& args)
{
m_recognizer.ProcessDownEvent(args.CurrentPoint());
}
void
GlobalGesturesSample::OnPointerMoved(
const winrt::Windows::UI::Input::TouchpadGesturesController& /* sender */,
const winrt::Windows::UI::Core::PointerEventArgs& args)
{
m_recognizer.ProcessMoveEvents(args.GetIntermediatePoints());
}
void
GlobalGesturesSample::OnPointerReleased(
const winrt::Windows::UI::Input::TouchpadGesturesController& /* sender */,
const winrt::Windows::UI::Core::PointerEventArgs& args)
{
m_recognizer.ProcessUpEvent(args.CurrentPoint());
}
void
GlobalGesturesSample::OnManipulationCompleted(
const winrt::Windows::UI::Input::PhysicalGestureRecognizer& /* sender */,
const winrt::Windows::UI::Input::ManipulationCompletedEventArgs& args)
{
auto motion = args.Cumulative();
if (abs(motion.Translation.X) > abs(motion.Translation.Y))
{
std::printf((motion.Translation.X < 0) ? "<SWIPE LEFT>\n" : "<SWIPE RIGHT>\n");
}
else
{
std::printf((motion.Translation.Y < 0) ? "<SWIPE UP>\n" : "<SWIPE DOWN>\n");
}
}
Injecting touchpad input
This sample demonstrates how to inject touchpad input into the system.
C++ example
class InputInjectionSample :
public winrt::implements<InputInjectionSample,
winrt::Windows::Foundation::IInspectable>
{
public:
void InitializeComponent();
void InjectPan();
void InjectTap();
void Inject3FingerPressRelease();
private:
void InjectInput(
HSYNTHETICPOINTERDEVICE device,
_In_reads_(cPoints) POINT* pStartingLocations,
_In_reads_(cPoints) POINT* pStoppingLocations,
DWORD cPoints,
DWORD steps,
DWORD durationMsPerStep);
private:
using unique_synthetic_pointer_device =
wil::unique_any<HSYNTHETICPOINTERDEVICE,
decltype(&::DestroySyntheticPointerDevice),
::DestroySyntheticPointerDevice>;
unique_synthetic_pointer_device m_touchpadGestureInjector;
unique_synthetic_pointer_device m_touchpadGenericInjector;
TOUCHPAD_PARAMETERS m_touchpadParams;
};
void
InputInjectionSample::InitializeComponent()
{
// Create a touchpad injection device that's "gesture-only" - its input can
// only be recognized as panning, zooming, or global manipulation-style gestures.
SYNTHETIC_DEVICE_CREATION_PARAMS creationParams;
creationParams.pointerType = PT_TOUCHPAD;
creationParams.maxCount = 5;
creationParams.feedbackMode = POINTER_FEEDBACK_NONE;
creationParams.hMonitor = nullptr;
creationParams.deviceWidth = 10000;
creationParams.deviceHeight = 6000;
creationParams.options = SDCO_PHYSICAL_SIZE |
SDCO_TOUCHPAD_GESTURE_ONLY;
m_touchpadGestureInjector.reset(::CreateSyntheticPointerDevice2(&creationParams));
THROW_LAST_ERROR_IF_NULL(m_touchpadGestureInjector);
// Create another touchpad with no gesturing restrictions.
creationParams.options = SDCO_PHYSICAL_SIZE;
m_touchpadGenericInjector.reset(::CreateSyntheticPointerDevice2(&creationParams));
THROW_LAST_ERROR_IF_NULL(m_touchpadGenericInjector);
// Check the user's current touchpad settings.
TOUCHPAD_PARAMETERS params = {};
params.versionNumber = TOUCHPAD_PARAMETERS_LATEST_VERSION;
THROW_IF_WIN32_BOOL_FALSE(::SystemParametersInfo(
SPI_GETTOUCHPADPARAMETERS, sizeof(params), ¶ms, 0));
}
void
InputInjectionSample::InjectInput(
HSYNTHETICPOINTERDEVICE device,
_In_reads_(cPoints) POINT* pStartingLocations,
_In_reads_(cPoints) POINT* pStoppingLocations,
DWORD cPoints,
DWORD steps,
DWORD durationMsPerStep)
{
auto injectedInfos = std::make_unique<POINTER_TYPE_INFO[]>(cPoints);
for (DWORD idx = 0; idx != cPoints; ++idx)
{
auto& info = injectedInfos.get()[idx];
auto& pointerInfo = info.touchInfo.pointerInfo;
info.type = PT_TOUCHPAD;
pointerInfo.pointerId = idx;
pointerInfo.pointerFlags = POINTER_FLAG_INRANGE |
POINTER_FLAG_INCONTACT |
POINTER_FLAG_CONFIDENCE;
pointerInfo.ptHimetricLocation.x = pStartingLocations[idx].x;
pointerInfo.ptHimetricLocation.y = pStartingLocations[idx].y;
info.touchInfo.pointerInfo.dwTime = 1;
}
THROW_IF_WIN32_BOOL_FALSE(::InjectSyntheticPointerInput(
device, injectedInfos.get(), cPoints));
for (DWORD step = 0; step != steps; ++step)
{
for (DWORD idx = 0; idx != cPoints; ++idx)
{
const auto xDist = pStoppingLocations[idx].x - pStartingLocations[idx].x;
const auto yDist = pStoppingLocations[idx].y - pStartingLocations[idx].y;
auto& pointerInfo = injectedInfos.get()[idx].touchInfo.pointerInfo;
pointerInfo.ptHimetricLocation.x =
pStartingLocations[idx].x + MulDiv(xDist, step + 1, steps);
pointerInfo.ptHimetricLocation.y =
pStartingLocations[idx].y + MulDiv(yDist, step + 1, steps);
pointerInfo.dwTime += durationMsPerStep;
}
::Sleep(durationMsPerStep);
THROW_IF_WIN32_BOOL_FALSE(::InjectSyntheticPointerInput(
device, injectedInfos.get(), cPoints));
}
for (DWORD idx = 0; idx != cPoints; ++idx)
{
auto& pointerInfo = injectedInfos.get()[idx].touchInfo.pointerInfo;
pointerInfo.pointerFlags = POINTER_FLAG_CONFIDENCE;
pointerInfo.dwTime += durationMsPerStep;
}
::Sleep(durationMsPerStep);
THROW_IF_WIN32_BOOL_FALSE(::InjectSyntheticPointerInput(
device, injectedInfos.get(), cPoints));
}
void
InputInjectionSample::InjectPan()
{
POINT* gestureStart;
POINT* gestureStop;
POINT gestureBottom[] = { POINT{ 3000, 5000 }, POINT{ 7000, 5000 } };
POINT gestureTop[] = { POINT{ 3000, 1000 }, POINT{ 7000, 1000 } };
if (m_touchpadParams.scrollDirectionReversed)
{
gestureStart = gestureTop;
gestureStop = gestureBottom;
}
else
{
gestureStart = gestureBottom;
gestureStop = gestureTop;
}
InjectInput(m_touchpadGestureInjector.get(),
gestureStart, gestureStop,
2, 10, 10);
}
void
InputInjectionSample::InjectTap()
{
POINT tapStartStop = POINT{ 5000, 3000 };
InjectInput(m_touchpadGenericInjector.get(),
&tapStartStop, &tapStartStop,
1, 5, 10);
}
void
InputInjectionSample::Inject3FingerPressRelease()
{
THROW_IF_WIN32_BOOL_FALSE(::InjectTouchpadAction(
m_touchpadGenericInjector.get(), TA_3FINGER_PRESS));
THROW_IF_WIN32_BOOL_FALSE(::InjectTouchpadAction(
m_touchpadGenericInjector.get(), TA_3FINGER_RELEASE));
}