1 /*****************************************************************************
2 * win32touch.c: touch gestures recognition
3 *****************************************************************************
4 * Copyright © 2013-2014 VideoLAN
5 *
6 * Authors: Ludovic Fauvet <etix@videolan.org>
7 * Jean-Baptiste Kempf <jb@videolan.org>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include "win32touch.h"
29
30 #include <vlc_actions.h>
31
32 #include <assert.h>
33
34 static BOOL DecodeGestureAction( vlc_object_t *p_this, win32_gesture_sys_t *p_gesture, const GESTUREINFO* p_gi );
35 static BOOL DecodeGestureProjection( vlc_object_t *p_this, win32_gesture_sys_t *p_gesture, const GESTUREINFO* p_gi );
36
DecodeGesture(vlc_object_t * p_this,win32_gesture_sys_t * p_gesture,HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)37 LRESULT DecodeGesture( vlc_object_t *p_this, win32_gesture_sys_t *p_gesture,
38 HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
39 {
40 if( !p_gesture )
41 return DefWindowProc( hWnd, message, wParam, lParam );
42
43 GESTUREINFO gi;
44 ZeroMemory( &gi, sizeof( GESTUREINFO ) );
45 gi.cbSize = sizeof( GESTUREINFO );
46
47 BOOL bResult = p_gesture->OurGetGestureInfo((HGESTUREINFO)lParam, &gi);
48 BOOL bHandled = FALSE; /* Needed to release the handle */
49
50 if( bResult )
51 bHandled = p_gesture->DecodeGestureImpl(p_this, p_gesture, &gi);
52 else
53 {
54 DWORD dwErr = GetLastError();
55 if( dwErr > 0 )
56 msg_Err( p_this, "Could not retrieve a valid GESTUREINFO structure" );
57 }
58
59 if( bHandled )
60 {
61 /* Close the Handle, if we handled the gesture, a contrario
62 * from the doc example */
63 p_gesture->OurCloseGestureInfoHandle((HGESTUREINFO)lParam);
64 return 0;
65 }
66 else
67 return DefWindowProc( hWnd, message, wParam, lParam );
68 }
69
DecodeGestureAction(vlc_object_t * p_this,win32_gesture_sys_t * p_gesture,const GESTUREINFO * p_gi)70 static BOOL DecodeGestureAction( vlc_object_t *p_this, win32_gesture_sys_t *p_gesture, const GESTUREINFO* p_gi )
71 {
72 BOOL bHandled = FALSE; /* Needed to release the handle */
73 switch ( p_gi->dwID )
74 {
75 case GID_BEGIN:
76 /* Set the win32_gesture_sys_t values */
77 p_gesture->i_beginx = p_gi->ptsLocation.x;
78 p_gesture->i_beginy = p_gi->ptsLocation.y;
79 p_gesture->i_lasty = p_gesture->i_beginy;
80 p_gesture->b_2fingers = false;
81 break;
82 case GID_END:
83 if( p_gesture->i_type != 0 &&
84 p_gesture->i_action == GESTURE_ACTION_JUMP )
85 {
86 int action_id;
87 if( p_gesture->i_beginx > p_gi->ptsLocation.x )
88 {
89 if( p_gesture->b_2fingers )
90 action_id = ACTIONID_JUMP_BACKWARD_MEDIUM;
91 else
92 action_id = ACTIONID_JUMP_BACKWARD_SHORT;
93 }
94 else
95 {
96 if( p_gesture->b_2fingers )
97 action_id = ACTIONID_JUMP_FORWARD_MEDIUM;
98 else
99 action_id = ACTIONID_JUMP_FORWARD_SHORT;
100 }
101 var_SetInteger( p_this->obj.libvlc, "key-action", action_id );
102 }
103 /* Reset the values */
104 p_gesture->i_action = GESTURE_ACTION_UNDEFINED;
105 p_gesture->i_type = p_gesture->i_beginx = p_gesture->i_beginy = -1;
106 p_gesture->b_2fingers = false;
107 break;
108 case GID_PAN:
109 p_gesture->i_type = GID_PAN;
110 bHandled = TRUE;
111
112 if (p_gi->dwFlags & GF_BEGIN) {
113 p_gesture->i_beginx = p_gi->ptsLocation.x;
114 p_gesture->i_beginy = p_gi->ptsLocation.y;
115 }
116
117 if( (DWORD)p_gi->ullArguments > 0 )
118 p_gesture->b_2fingers = true;
119
120 if( p_gesture->i_action == GESTURE_ACTION_UNDEFINED )
121 {
122 if( abs( p_gesture->i_beginx - p_gi->ptsLocation.x ) +
123 abs( p_gesture->i_beginy - p_gi->ptsLocation.y ) > 50 )
124 {
125 if( abs( p_gesture->i_beginx - p_gi->ptsLocation.x ) >
126 abs( p_gesture->i_beginy - p_gi->ptsLocation.y ) )
127 p_gesture->i_action = GESTURE_ACTION_JUMP;
128 else if ( p_gesture->b_2fingers )
129 p_gesture->i_action = GESTURE_ACTION_BRIGHTNESS;
130 else
131 p_gesture->i_action = GESTURE_ACTION_VOLUME;
132 }
133 }
134
135 if( p_gesture->i_action == GESTURE_ACTION_VOLUME )
136 {
137 int offset = p_gesture->i_lasty - p_gi->ptsLocation.y;
138
139 if( offset > 100)
140 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_VOL_UP );
141 else if( offset < -100)
142 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_VOL_DOWN );
143 else
144 break;
145
146 p_gesture->i_lasty = p_gi->ptsLocation.y;
147 }
148 else if ( p_gesture->i_action == GESTURE_ACTION_BRIGHTNESS )
149 {
150 /* Currently unimplemented
151 if( p_gesture->i_lasty == -1 )
152 p_gesture->i_lasty = p_gesture->i_beginy;
153
154 if( p_gesture->i_lasty - p_gesture->i_beginy > 80 )
155 {
156 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_BRIGHTNESS_DOWN );
157 p_gesture->i_lasty = p_gi->ptsLocation.y;
158 }
159 else if ( p_gesture->i_lasty - p_gesture->i_beginy < 80 )
160 {
161 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_BRIGHTNESS_UP );
162 p_gesture->i_lasty = p_gi->ptsLocation.y;
163 } */
164 }
165 break;
166 case GID_TWOFINGERTAP:
167 p_gesture->i_type = GID_TWOFINGERTAP;
168 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_PLAY_PAUSE );
169 bHandled = TRUE;
170 break;
171 case GID_ZOOM:
172 p_gesture->i_type = GID_ZOOM;
173 switch( p_gi->dwFlags )
174 {
175 case GF_BEGIN:
176 p_gesture->i_ullArguments = p_gi->ullArguments;
177 break;
178 case GF_END:
179 {
180 double k = (double)(p_gi->ullArguments) /
181 (double)(p_gesture->i_ullArguments);
182 if( k > 1 )
183 var_SetInteger( p_this->obj.libvlc, "key-action",
184 ACTIONID_TOGGLE_FULLSCREEN );
185 else
186 var_SetInteger( p_this->obj.libvlc, "key-action",
187 ACTIONID_LEAVE_FULLSCREEN );
188 }
189 break;
190 default:
191 msg_Err( p_this, "Unmanaged dwFlag: %lx", p_gi->dwFlags );
192 }
193 bHandled = TRUE;
194 break;
195 case WM_VSCROLL:
196 bHandled = TRUE;
197 break;
198 default:
199 break;
200 }
201 return bHandled;
202 }
203
204
DecodeGestureProjection(vlc_object_t * p_this,win32_gesture_sys_t * p_gesture,const GESTUREINFO * p_gi)205 static BOOL DecodeGestureProjection( vlc_object_t *p_this, win32_gesture_sys_t *p_gesture, const GESTUREINFO* p_gi )
206 {
207 //vout_display_t *vd = (vout_display_t *)p_this;
208
209 BOOL bHandled = FALSE; /* Needed to release the handle */
210 switch ( p_gi->dwID )
211 {
212 case GID_BEGIN:
213 /* Set the win32_gesture_sys_t values */
214 p_gesture->i_beginx = p_gi->ptsLocation.x;
215 p_gesture->i_beginy = p_gi->ptsLocation.y;
216 p_gesture->i_lasty = p_gesture->i_beginy;
217 p_gesture->b_2fingers = false;
218 break;
219 case GID_END:
220 if( p_gesture->i_type != 0 &&
221 p_gesture->i_action == GESTURE_ACTION_JUMP )
222 {
223 int action_id;
224 if( p_gesture->b_2fingers )
225 {
226 if( p_gesture->i_beginx > p_gi->ptsLocation.x )
227 action_id = ACTIONID_JUMP_BACKWARD_SHORT;
228 else
229 action_id = ACTIONID_JUMP_FORWARD_SHORT;
230 var_SetInteger( p_this->obj.libvlc, "key-action", action_id );
231 }
232 }
233 /* Reset the values */
234 p_gesture->i_action = GESTURE_ACTION_UNDEFINED;
235 p_gesture->i_type = p_gesture->i_beginx = p_gesture->i_beginy = -1;
236 p_gesture->b_2fingers = false;
237 break;
238 case GID_PAN:
239 //vd->cfg->display.width;
240 p_gesture->i_type = GID_PAN;
241 bHandled = TRUE;
242 if (p_gi->dwFlags & GF_BEGIN) {
243 p_gesture->i_beginx = p_gi->ptsLocation.x;
244 p_gesture->i_beginy = p_gi->ptsLocation.y;
245 }
246
247 if( (DWORD)p_gi->ullArguments > 0 )
248 p_gesture->b_2fingers = true;
249
250 if( p_gesture->b_2fingers && p_gesture->i_action == GESTURE_ACTION_UNDEFINED )
251 {
252 if( abs( p_gesture->i_beginx - p_gi->ptsLocation.x ) +
253 abs( p_gesture->i_beginy - p_gi->ptsLocation.y ) > 50 )
254 {
255 if( abs( p_gesture->i_beginx - p_gi->ptsLocation.x ) >
256 abs( p_gesture->i_beginy - p_gi->ptsLocation.y ) )
257 p_gesture->i_action = GESTURE_ACTION_JUMP;
258 else
259 p_gesture->i_action = GESTURE_ACTION_VOLUME;
260 }
261 }
262
263 if( p_gesture->i_action == GESTURE_ACTION_VOLUME )
264 {
265 int offset = p_gesture->i_lasty - p_gi->ptsLocation.y;
266
267 if( offset > 100)
268 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_VOL_UP );
269 else if( offset < -100)
270 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_VOL_DOWN );
271 else
272 break;
273
274 p_gesture->i_lasty = p_gi->ptsLocation.y;
275 }
276 break;
277 case GID_TWOFINGERTAP:
278 p_gesture->i_type = GID_TWOFINGERTAP;
279 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_PLAY_PAUSE );
280 bHandled = TRUE;
281 break;
282 case GID_ZOOM:
283 p_gesture->i_type = GID_ZOOM;
284 bHandled = TRUE;
285 switch( p_gi->dwFlags )
286 {
287 case GF_BEGIN:
288 p_gesture->i_ullArguments = p_gi->ullArguments;
289 p_gesture->f_lastzoom = 1.0;
290 break;
291 default:
292 {
293 double k = (double)(p_gi->ullArguments) /
294 (double)(p_gesture->i_ullArguments);
295
296 if (k > p_gesture->f_lastzoom * 1.01)
297 {
298 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_VIEWPOINT_FOV_IN );
299 p_gesture->f_lastzoom = k;
300 }
301 else if (k < p_gesture->f_lastzoom * 0.99)
302 {
303 var_SetInteger( p_this->obj.libvlc, "key-action", ACTIONID_VIEWPOINT_FOV_OUT );
304 p_gesture->f_lastzoom = k;
305 }
306 break;
307 }
308 }
309 break;
310 case WM_VSCROLL:
311 bHandled = TRUE;
312 break;
313 default:
314 break;
315 }
316 return bHandled;
317 }
318
InitGestures(HWND hwnd,win32_gesture_sys_t ** pp_gesture,bool b_isProjected)319 BOOL InitGestures( HWND hwnd, win32_gesture_sys_t **pp_gesture, bool b_isProjected )
320 {
321 BOOL result = FALSE;
322 GESTURECONFIG config = { 0, 0, 0 };
323 if (b_isProjected)
324 {
325 //don't handle single finger pan in projected mode, it will be interpreted
326 //as mouse events.
327 config.dwID = GID_PAN;
328 config.dwWant = GC_PAN;
329 config.dwBlock = GC_PAN_WITH_INERTIA;
330 }
331 else
332 {
333 config.dwID = GID_PAN;
334 config.dwWant = GC_PAN |
335 GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY |
336 GC_PAN_WITH_SINGLE_FINGER_VERTICALLY;
337 config.dwBlock = GC_PAN_WITH_INERTIA;
338 }
339
340 win32_gesture_sys_t *p_gesture = malloc( sizeof(win32_gesture_sys_t) );
341 if( !p_gesture )
342 {
343 *pp_gesture = NULL;
344 return FALSE;
345 }
346
347 HINSTANCE h_user32_dll = LoadLibrary(TEXT("user32.dll"));
348 if( !h_user32_dll )
349 {
350 *pp_gesture = NULL;
351 free( p_gesture );
352 return FALSE;
353 }
354
355 BOOL (WINAPI *OurSetGestureConfig) (HWND, DWORD, UINT, PGESTURECONFIG, UINT);
356 OurSetGestureConfig = (void *)GetProcAddress(h_user32_dll, "SetGestureConfig");
357
358 p_gesture->OurCloseGestureInfoHandle =
359 (void *)GetProcAddress(h_user32_dll, "CloseGestureInfoHandle" );
360 p_gesture->OurGetGestureInfo =
361 (void *)GetProcAddress(h_user32_dll, "GetGestureInfo");
362 if( OurSetGestureConfig )
363 {
364 result = OurSetGestureConfig(
365 hwnd,
366 0,
367 1,
368 &config,
369 sizeof( GESTURECONFIG )
370 );
371 }
372 if (b_isProjected)
373 p_gesture->DecodeGestureImpl = DecodeGestureProjection;
374 else
375 p_gesture->DecodeGestureImpl = DecodeGestureAction;
376
377 p_gesture->i_type = 0;
378 p_gesture->b_2fingers = false;
379 p_gesture->i_action = GESTURE_ACTION_UNDEFINED;
380 p_gesture->i_beginx = p_gesture->i_beginy = -1;
381 p_gesture->i_lasty = -1;
382 p_gesture->huser_dll = h_user32_dll;
383
384 *pp_gesture = p_gesture;
385 return result;
386 }
387
CloseGestures(win32_gesture_sys_t * p_gesture)388 void CloseGestures( win32_gesture_sys_t *p_gesture )
389 {
390 if (p_gesture && p_gesture->huser_dll )
391 FreeLibrary( p_gesture->huser_dll );
392 free( p_gesture );
393 }
394