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