KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wxmsw/touchpad.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <kiplatform/touchpad.h>
21
22// On mingw/msys2, the touchpad code does not compile (it is specific to MSVC)
23// So use a dummy code similar to gtk an osx ports
24#if defined( __MINGW32__ )
26{
27 return false;
28}
29
30
31std::unique_ptr<KIPLATFORM::UI::TOUCHPAD_GESTURE_HANDLER>
32KIPLATFORM::UI::CreateTouchpadGestureHandler( wxWindow*, TOUCHPAD_GESTURE_CALLBACK )
33{
34 return nullptr;
35}
36#else
37
38#include <InteractionContext.h>
39#include <windows.h>
40#include <commctrl.h>
41
42#include <limits>
43#include <utility>
44#include <vector>
45
46#include <wx/window.h>
47
48
49namespace
50{
58class MSW_TOUCHPAD_GESTURE_HANDLER final : public KIPLATFORM::UI::TOUCHPAD_GESTURE_HANDLER
59{
60public:
61 MSW_TOUCHPAD_GESTURE_HANDLER( wxWindow* aInputWindow, KIPLATFORM::UI::TOUCHPAD_GESTURE_CALLBACK aCallback ) :
62 m_inputWindow( aInputWindow ),
63 m_callback( std::move( aCallback ) )
64 {
65 initialize();
66 }
67
68 ~MSW_TOUCHPAD_GESTURE_HANDLER() override
69 {
70 if( m_registered )
71 m_registerTouchpadCapableWindow( getHwnd(), FALSE );
72
73 if( m_subclassed )
74 m_removeWindowSubclass( getHwnd(), windowProc, SUBCLASS_ID );
75
76 if( m_context )
77 m_destroyInteractionContext( m_context );
78
79 if( m_ninput )
80 FreeLibrary( m_ninput );
81
82 if( m_comctl32 )
83 FreeLibrary( m_comctl32 );
84 }
85
86 bool IsActive() const { return m_registered && m_subclassed; }
87
88private:
89 using REGISTER_TOUCHPAD_CAPABLE_WINDOW = BOOL( WINAPI* )( HWND, BOOL );
90 using GET_POINTER_FRAME_TOUCHPAD_INFO_HISTORY = BOOL( WINAPI* )( UINT32, UINT32*, UINT32*, POINTER_TOUCH_INFO* );
91 using CREATE_INTERACTION_CONTEXT = HRESULT( WINAPI* )( HINTERACTIONCONTEXT* );
92 using DESTROY_INTERACTION_CONTEXT = HRESULT( WINAPI* )( HINTERACTIONCONTEXT );
93 using REGISTER_OUTPUT_CALLBACK = HRESULT( WINAPI* )( HINTERACTIONCONTEXT, INTERACTION_CONTEXT_OUTPUT_CALLBACK,
94 void* );
95 using SET_INTERACTION_CONFIGURATION = HRESULT( WINAPI* )( HINTERACTIONCONTEXT, UINT32,
96 const INTERACTION_CONTEXT_CONFIGURATION* );
97 using SET_INTERACTION_PROPERTY = HRESULT( WINAPI* )( HINTERACTIONCONTEXT, INTERACTION_CONTEXT_PROPERTY, UINT32 );
98 using PROCESS_POINTER_FRAMES_INTERACTION_CONTEXT2 = HRESULT( WINAPI* )( HINTERACTIONCONTEXT, UINT32, UINT32,
99 const POINTER_TYPE_INFO* );
100 using SET_WINDOW_SUBCLASS = BOOL( WINAPI* )( HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR );
101 using REMOVE_WINDOW_SUBCLASS = BOOL( WINAPI* )( HWND, SUBCLASSPROC, UINT_PTR );
102 using DEF_SUBCLASS_PROC = LRESULT( WINAPI* )( HWND, UINT, WPARAM, LPARAM );
103
104#ifdef _MSC_VER
105#pragma warning( push )
106#pragma warning( disable : 4191 )
107#endif
108
109 template <typename T>
110 static T loadNamedProc( HMODULE aModule, const char* aName )
111 {
112 return reinterpret_cast<T>( GetProcAddress( aModule, aName ) );
113 }
114
115 template <typename T>
116 static T loadOrdinalProc( HMODULE aModule, WORD aOrdinal )
117 {
118 return reinterpret_cast<T>( GetProcAddress( aModule, MAKEINTRESOURCEA( aOrdinal ) ) );
119 }
120
121#ifdef _MSC_VER
122#pragma warning( pop )
123#endif
124
125 HWND getHwnd() const { return reinterpret_cast<HWND>( m_inputWindow->GetHandle() ); }
126
127 void initialize()
128 {
129 HMODULE user32 = GetModuleHandleW( L"user32.dll" );
130 m_ninput = LoadLibraryExW( L"ninput.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32 );
131 m_comctl32 = LoadLibraryExW( L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32 );
132
133 if( !user32 || !m_ninput || !m_comctl32 )
134 return;
135
136 m_registerTouchpadCapableWindow = loadOrdinalProc<REGISTER_TOUCHPAD_CAPABLE_WINDOW>( user32, 2689 );
137 m_getPointerFrameTouchpadInfoHistory = loadOrdinalProc<GET_POINTER_FRAME_TOUCHPAD_INFO_HISTORY>( user32, 2694 );
138 m_processPointerFramesInteractionContext2 =
139 loadOrdinalProc<PROCESS_POINTER_FRAMES_INTERACTION_CONTEXT2>( m_ninput, 2507 );
140 m_createInteractionContext = loadNamedProc<CREATE_INTERACTION_CONTEXT>( m_ninput, "CreateInteractionContext" );
141 m_destroyInteractionContext =
142 loadNamedProc<DESTROY_INTERACTION_CONTEXT>( m_ninput, "DestroyInteractionContext" );
143 m_registerOutputCallback =
144 loadNamedProc<REGISTER_OUTPUT_CALLBACK>( m_ninput, "RegisterOutputCallbackInteractionContext" );
145 m_setInteractionConfiguration = loadNamedProc<SET_INTERACTION_CONFIGURATION>(
146 m_ninput, "SetInteractionConfigurationInteractionContext" );
147 m_setInteractionProperty = loadNamedProc<SET_INTERACTION_PROPERTY>( m_ninput, "SetPropertyInteractionContext" );
148 m_setWindowSubclass = loadNamedProc<SET_WINDOW_SUBCLASS>( m_comctl32, "SetWindowSubclass" );
149 m_removeWindowSubclass = loadNamedProc<REMOVE_WINDOW_SUBCLASS>( m_comctl32, "RemoveWindowSubclass" );
150 m_defSubclassProc = loadNamedProc<DEF_SUBCLASS_PROC>( m_comctl32, "DefSubclassProc" );
151
152 if( !m_registerTouchpadCapableWindow || !m_getPointerFrameTouchpadInfoHistory
153 || !m_processPointerFramesInteractionContext2 || !m_createInteractionContext || !m_destroyInteractionContext
154 || !m_registerOutputCallback || !m_setInteractionConfiguration || !m_setInteractionProperty
155 || !m_setWindowSubclass || !m_removeWindowSubclass || !m_defSubclassProc )
156 {
157 return;
158 }
159
160 if( FAILED( m_createInteractionContext( &m_context ) ) )
161 return;
162
163 if( FAILED( m_registerOutputCallback( m_context, outputCallback, this ) ) )
164 return;
165
166 INTERACTION_CONTEXT_CONFIGURATION configuration = {
167 INTERACTION_ID_MANIPULATION, INTERACTION_CONFIGURATION_FLAG_MANIPULATION
168 | INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_X
169 | INTERACTION_CONFIGURATION_FLAG_MANIPULATION_TRANSLATION_Y
170 | INTERACTION_CONFIGURATION_FLAG_MANIPULATION_SCALING
171 | INTERACTION_CONFIGURATION_FLAG_MANIPULATION_RAILS_X
172 | INTERACTION_CONFIGURATION_FLAG_MANIPULATION_RAILS_Y
173 | INTERACTION_CONFIGURATION_FLAG_MANIPULATION_MULTIPLE_FINGER_PANNING
174 };
175
176 if( FAILED( m_setInteractionConfiguration( m_context, 1, &configuration ) ) )
177 return;
178
179 // Device-space output is stable across display scale factors. It is converted to the
180 // input window's effective DPI in onOutput().
181 if( FAILED( m_setInteractionProperty( m_context, INTERACTION_CONTEXT_PROPERTY_MEASUREMENT_UNITS, 0 ) )
182 || FAILED( m_setInteractionProperty( m_context, INTERACTION_CONTEXT_PROPERTY_FILTER_POINTERS, FALSE ) ) )
183 {
184 return;
185 }
186
187 m_registered = m_registerTouchpadCapableWindow( getHwnd(), TRUE ) != FALSE;
188
189 if( !m_registered )
190 return;
191
192 if( !m_setWindowSubclass( getHwnd(), windowProc, SUBCLASS_ID, reinterpret_cast<DWORD_PTR>( this ) ) )
193 {
194 m_registerTouchpadCapableWindow( getHwnd(), FALSE );
195 m_registered = false;
196 return;
197 }
198
199 m_subclassed = true;
200 }
201
202 bool handleMessage( UINT aMessage, WPARAM aWParam )
203 {
204 if( !m_registered
205 || ( aMessage != WM_POINTERDOWN && aMessage != WM_POINTERUPDATE && aMessage != WM_POINTERUP ) )
206 {
207 return false;
208 }
209
210 const UINT32 pointerId = GET_POINTERID_WPARAM( aWParam );
211 POINTER_INFO pointerInfo = {};
212
213 if( !GetPointerInfo( pointerId, &pointerInfo ) || pointerInfo.pointerType != PT_TOUCHPAD )
214 return false;
215
216 UINT32 frameCount = 0;
217 UINT32 pointerCount = 0;
218
219 if( !m_getPointerFrameTouchpadInfoHistory( pointerId, &frameCount, &pointerCount, nullptr ) || frameCount == 0
220 || pointerCount == 0 || frameCount > std::numeric_limits<size_t>::max() / pointerCount )
221 {
222 return false;
223 }
224
225 const size_t entryCount = static_cast<size_t>( frameCount ) * pointerCount;
226
227 // A normal touchpad frame has only a handful of contacts. Keep corrupt input from
228 // turning an input message into an unbounded allocation.
229 if( entryCount > 65536 )
230 return false;
231
232 std::vector<POINTER_TOUCH_INFO> touchInfos( entryCount );
233
234 if( !m_getPointerFrameTouchpadInfoHistory( pointerId, &frameCount, &pointerCount, touchInfos.data() ) )
235 return false;
236
237 const size_t filledEntryCount = static_cast<size_t>( frameCount ) * pointerCount;
238
239 if( filledEntryCount > touchInfos.size() )
240 return false;
241
242 std::vector<POINTER_TYPE_INFO> typeInfos( filledEntryCount );
243
244 for( size_t i = 0; i < filledEntryCount; ++i )
245 {
246 typeInfos[i].type = touchInfos[i].pointerInfo.pointerType;
247 typeInfos[i].touchInfo = touchInfos[i];
248 }
249
250 if( FAILED( m_processPointerFramesInteractionContext2( m_context, frameCount, pointerCount,
251 typeInfos.data() ) ) )
252 {
253 return false;
254 }
255
256 // The entire frame was processed above; suppress its remaining per-contact messages.
257 SkipPointerFrameMessages( pointerId );
258 return true;
259 }
260
261 static LRESULT CALLBACK windowProc( HWND aHwnd, UINT aMessage, WPARAM aWParam, LPARAM aLParam, UINT_PTR,
262 DWORD_PTR aReferenceData )
263 {
264 auto* handler = reinterpret_cast<MSW_TOUCHPAD_GESTURE_HANDLER*>( aReferenceData );
265
266 try
267 {
268 if( handler && handler->handleMessage( aMessage, aWParam ) )
269 return 0;
270 }
271 catch( ... )
272 {
273 // C++ exceptions must not escape a native window procedure.
274 }
275
276 if( handler && handler->m_defSubclassProc )
277 return handler->m_defSubclassProc( aHwnd, aMessage, aWParam, aLParam );
278
279 return DefWindowProcW( aHwnd, aMessage, aWParam, aLParam );
280 }
281
282 static void CALLBACK outputCallback( void* aClientData, const INTERACTION_CONTEXT_OUTPUT* aOutput )
283 {
284 try
285 {
286 static_cast<MSW_TOUCHPAD_GESTURE_HANDLER*>( aClientData )->onOutput( aOutput );
287 }
288 catch( ... )
289 {
290 // C++ exceptions must not escape an Interaction Context callback.
291 }
292 }
293
294 void onOutput( const INTERACTION_CONTEXT_OUTPUT* aOutput )
295 {
296 if( !aOutput || aOutput->interactionId != INTERACTION_ID_MANIPULATION || aOutput->inputType != PT_TOUCHPAD )
297 {
298 return;
299 }
300
301 const wxSize dpi = m_inputWindow->GetDPI();
302 constexpr double HIMETRIC_PER_INCH = 2540.0;
303 const double pixelsPerHimetricX = ( dpi.x > 0 ? dpi.x : 96 ) / HIMETRIC_PER_INCH;
304 const double pixelsPerHimetricY = ( dpi.y > 0 ? dpi.y : 96 ) / HIMETRIC_PER_INCH;
305 const MANIPULATION_TRANSFORM& delta = aOutput->arguments.manipulation.delta;
306
307 POINT cursorPosition = {};
308
309 if( !GetCursorPos( &cursorPosition ) || !ScreenToClient( getHwnd(), &cursorPosition ) )
310 return;
311
312 m_callback( { delta.translationX * pixelsPerHimetricX, delta.translationY * pixelsPerHimetricY, delta.scale,
313 wxPoint( cursorPosition.x, cursorPosition.y ) } );
314 }
315
316 static constexpr UINT_PTR SUBCLASS_ID = 1;
317
318 wxWindow* m_inputWindow = nullptr;
320 HMODULE m_ninput = nullptr;
321 HMODULE m_comctl32 = nullptr;
322 HINTERACTIONCONTEXT m_context = nullptr;
323 REGISTER_TOUCHPAD_CAPABLE_WINDOW m_registerTouchpadCapableWindow = nullptr;
324 GET_POINTER_FRAME_TOUCHPAD_INFO_HISTORY m_getPointerFrameTouchpadInfoHistory = nullptr;
325 CREATE_INTERACTION_CONTEXT m_createInteractionContext = nullptr;
326 DESTROY_INTERACTION_CONTEXT m_destroyInteractionContext = nullptr;
327 REGISTER_OUTPUT_CALLBACK m_registerOutputCallback = nullptr;
328 SET_INTERACTION_CONFIGURATION m_setInteractionConfiguration = nullptr;
329 SET_INTERACTION_PROPERTY m_setInteractionProperty = nullptr;
330 PROCESS_POINTER_FRAMES_INTERACTION_CONTEXT2 m_processPointerFramesInteractionContext2 = nullptr;
331 SET_WINDOW_SUBCLASS m_setWindowSubclass = nullptr;
332 REMOVE_WINDOW_SUBCLASS m_removeWindowSubclass = nullptr;
333 DEF_SUBCLASS_PROC m_defSubclassProc = nullptr;
334 bool m_registered = false;
335 bool m_subclassed = false;
336};
337} // namespace
338
339
341{
342 HMODULE user32 = GetModuleHandleW( L"user32.dll" );
343 HMODULE ninput = LoadLibraryExW( L"ninput.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32 );
344 HMODULE comctl32 = LoadLibraryExW( L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32 );
345
346 const bool available =
347 user32 && ninput && comctl32 && GetProcAddress( user32, MAKEINTRESOURCEA( 2689 ) )
348 && GetProcAddress( user32, MAKEINTRESOURCEA( 2694 ) ) && GetProcAddress( ninput, MAKEINTRESOURCEA( 2507 ) )
349 && GetProcAddress( ninput, "CreateInteractionContext" )
350 && GetProcAddress( ninput, "DestroyInteractionContext" )
351 && GetProcAddress( ninput, "RegisterOutputCallbackInteractionContext" )
352 && GetProcAddress( ninput, "SetInteractionConfigurationInteractionContext" )
353 && GetProcAddress( ninput, "SetPropertyInteractionContext" )
354 && GetProcAddress( comctl32, "SetWindowSubclass" ) && GetProcAddress( comctl32, "RemoveWindowSubclass" )
355 && GetProcAddress( comctl32, "DefSubclassProc" );
356
357 if( ninput )
358 FreeLibrary( ninput );
359
360 if( comctl32 )
361 FreeLibrary( comctl32 );
362
363 return available;
364}
365
366
367std::unique_ptr<KIPLATFORM::UI::TOUCHPAD_GESTURE_HANDLER>
368KIPLATFORM::UI::CreateTouchpadGestureHandler( wxWindow* aInputWindow, TOUCHPAD_GESTURE_CALLBACK aCallback )
369{
370 if( !aInputWindow || !aCallback )
371 return nullptr;
372
373 auto handler = std::make_unique<MSW_TOUCHPAD_GESTURE_HANDLER>( aInputWindow, std::move( aCallback ) );
374
375 if( !handler->IsActive() )
376 return nullptr;
377
378 return handler;
379}
380
381#endif
Owns a platform touchpad input registration for the lifetime of an input window.
Definition touchpad.h:50
bool IsNativeTouchpadGestureAvailable()
Check whether this port provides the native touchpad gesture APIs.
std::unique_ptr< TOUCHPAD_GESTURE_HANDLER > CreateTouchpadGestureHandler(wxWindow *aInputWindow, TOUCHPAD_GESTURE_CALLBACK aCallback)
Register a window for native touchpad pan and pinch gestures when the port supports it.
std::function< void(const TOUCHPAD_GESTURE &)> TOUCHPAD_GESTURE_CALLBACK
Definition touchpad.h:56
constexpr uint32_t POINT
Definition writer.cpp:91
#define CALLBACK
The default number of points for circle approximation.
Definition opengl_gal.h:45
int delta