gestures.c 21.8 KB
Newer Older
1
/*****************************************************************************
Anil Daoud's avatar
Anil Daoud committed
2
 * gestures.c: control vlc with mouse gestures
3
 *****************************************************************************
4
 * Copyright (C) 2004 the VideoLAN team
Laurent Aimar's avatar
Laurent Aimar committed
5
 * $Id$
6
 *
7
 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
8 9 10 11 12
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
Clément Stenac's avatar
Clément Stenac committed
13
 *
14 15 16 17 18 19 20
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26 27
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

28 29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

32
#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
33 34
#include <vlc_interface.h>
#include <vlc_vout.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
35
#include <vlc_aout.h>
Clément Stenac's avatar
Clément Stenac committed
36
#include <vlc_playlist.h>
37

38 39 40 41
#ifdef HAVE_UNISTD_H
#    include <unistd.h>
#endif

42 43 44 45 46 47
/*****************************************************************************
 * intf_sys_t: description and status of interface
 *****************************************************************************/
struct intf_sys_t
{
    vlc_object_t *      p_vout;
48 49
    bool          b_got_gesture;
    bool          b_button_pressed;
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
    int                 i_mouse_x, i_mouse_y;
    int                 i_last_x, i_last_y;
    unsigned int        i_pattern;
    int                 i_num_gestures;
    int                 i_threshold;
    int                 i_button_mask;
};

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
#define NONE 0
#define GESTURE( a, b, c, d ) (a | ( b << 4 ) | ( c << 8 ) | ( d << 12 ))

int  E_(Open)   ( vlc_object_t * );
void E_(Close)  ( vlc_object_t * );
static int  InitThread     ( intf_thread_t *p_intf );
71
static void EndThread      ( intf_thread_t *p_intf );
72 73 74 75 76 77 78 79 80
static int  MouseEvent     ( vlc_object_t *, char const *,
                             vlc_value_t, vlc_value_t, void * );

/* Exported functions */
static void RunIntf        ( intf_thread_t *p_intf );

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Anil Daoud's avatar
Anil Daoud committed
81
#define THRESHOLD_TEXT N_( "Motion threshold (10-100)" )
82
#define THRESHOLD_LONGTEXT N_( \
83
    "Amount of movement required for a mouse gesture to be recorded." )
84

Anil Daoud's avatar
Anil Daoud committed
85
#define BUTTON_TEXT N_( "Trigger button" )
86
#define BUTTON_LONGTEXT N_( \
87
    "Trigger button for mouse gestures." )
88

Clément Stenac's avatar
Clément Stenac committed
89 90 91
static const char *button_list[] = { "left", "middle", "right" };
static const char *button_list_text[] =
                                   { N_("Left"), N_("Middle"), N_("Right") };
92 93

vlc_module_begin();
94
    set_shortname( _("Gestures"));
Clément Stenac's avatar
Clément Stenac committed
95 96
    set_category( CAT_INTERFACE );
    set_subcategory( SUBCAT_INTERFACE_CONTROL );
97
    add_integer( "gestures-threshold", 30, NULL,
98
                 THRESHOLD_TEXT, THRESHOLD_LONGTEXT, true );
99
    add_string( "gestures-button", "right", NULL,
100
                BUTTON_TEXT, BUTTON_LONGTEXT, false );
101
        change_string_list( button_list, button_list_text, 0 );
Anil Daoud's avatar
Anil Daoud committed
102
    set_description( _("Mouse gestures control interface") );
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

    set_capability( "interface", 0 );
    set_callbacks( E_(Open), E_(Close) );
vlc_module_end();

/*****************************************************************************
 * OpenIntf: initialize interface
 *****************************************************************************/
int E_(Open) ( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;

    /* Allocate instance and initialize some members */
    p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
    if( p_intf->p_sys == NULL )
    {
        return( 1 );
    };

    p_intf->pf_run = RunIntf;

    return( 0 );
}

/*****************************************************************************
 * gesture: return a subpattern within a pattern
 *****************************************************************************/
static int gesture( int i_pattern, int i_num )
{
    return ( i_pattern >> ( i_num * 4 ) ) & 0xF;
}
134

135 136 137 138 139 140 141 142
/*****************************************************************************
 * input_from_playlist: don't forget to release the return value
 *  Also this function should really be available from core.
 *****************************************************************************/
static input_thread_t * input_from_playlist ( playlist_t *p_playlist )
{
    input_thread_t * p_input;

143 144 145 146
    PL_LOCK;
    p_input = p_playlist->p_input;
    if( p_input )
        vlc_object_yield( p_input );
147 148 149 150 151
    PL_UNLOCK;

    return p_input;
}

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
/*****************************************************************************
 * CloseIntf: destroy dummy interface
 *****************************************************************************/
void E_(Close) ( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;

    /* Destroy structure */
    free( p_intf->p_sys );
}


/*****************************************************************************
 * RunIntf: main loop
 *****************************************************************************/
static void RunIntf( intf_thread_t *p_intf )
{
    playlist_t * p_playlist = NULL;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
170

171 172 173 174
    vlc_mutex_lock( &p_intf->change_lock );
    p_intf->p_sys->p_vout = NULL;
    vlc_mutex_unlock( &p_intf->change_lock );

175 176
    if( InitThread( p_intf ) < 0 )
    {
Antoine Cellerier's avatar
Antoine Cellerier committed
177
        msg_Err( p_intf, "can't initialize interface thread" );
178 179
        return;
    }
180
    msg_Dbg( p_intf, "interface thread initialized" );
181 182

    /* Main loop */
183
    while( !intf_ShouldDie( p_intf ) )
184 185 186
    {
        vlc_mutex_lock( &p_intf->change_lock );

187
        /*
188 189 190 191
         * mouse cursor
         */
        if( p_intf->p_sys->b_got_gesture )
        {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
192 193
            vlc_value_t val;
            int i_interval = 0;
194
            /* Do something */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
195 196 197
            /* If you modify this, please try to follow this convention:
               Start with LEFT, RIGHT for playback related commands
               and UP, DOWN, for other commands */
198 199 200
            switch( p_intf->p_sys->i_pattern )
            {
            case LEFT:
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
                i_interval = config_GetInt( p_intf , "short-jump-size" );
                if ( i_interval > 0 ) {
                    val.i_time = ( (mtime_t)( -i_interval ) * 1000000L);
                    var_Set( p_intf, "time-offset", val );
                }
                msg_Dbg(p_intf, "Go backward in the movie!");
                break;
            case RIGHT:
                i_interval = config_GetInt( p_intf , "short-jump-size" );
                if ( i_interval > 0 ) {
                    val.i_time = ( (mtime_t)( i_interval ) * 1000000L);
                    var_Set( p_intf, "time-offset", val );
                }
                msg_Dbg(p_intf, "Go forward in the movie!");
                break;
            case GESTURE(LEFT,UP,NONE,NONE):
                /*FIXME BF*/
                msg_Dbg(p_intf, "Going slower.");
                break;
            case GESTURE(RIGHT,UP,NONE,NONE):
                /*FIXME FF*/
                msg_Dbg(p_intf, "Going faster.");
                break;
            case GESTURE(LEFT,RIGHT,NONE,NONE):
            case GESTURE(RIGHT,LEFT,NONE,NONE):
                {
227 228 229 230
                    input_thread_t * p_input;
                    p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                              FIND_ANYWHERE );

231 232 233
                   if( !p_playlist )
                        break;

234
                    p_input = input_from_playlist( p_playlist );
235
                    vlc_object_release( p_playlist );
236
 
237 238
                    if( !p_input )
                        break;
239
 
240 241
                    val.i_int = PLAYING_S;
                    if( p_input )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
242
                    {
243 244 245 246 247 248 249 250 251 252
                        var_Get( p_input, "state", &val);
                        if( val.i_int == PAUSE_S )
                        {
                            val.i_int = PLAYING_S;
                        }
                        else
                        {
                            val.i_int = PAUSE_S;
                        }
                        var_Set( p_input, "state", val);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
253
                    }
254 255
                    msg_Dbg(p_intf, "Play/Pause");
                    vlc_object_release( p_input );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
256 257 258
                }
                break;
            case GESTURE(LEFT,DOWN,NONE,NONE):
259 260 261 262 263 264
                p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                              FIND_ANYWHERE );
                if( p_playlist == NULL )
                {
                    break;
                }
265

266 267 268
                playlist_Prev( p_playlist );
                vlc_object_release( p_playlist );
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
269
            case GESTURE(RIGHT,DOWN,NONE,NONE):
270 271 272 273 274 275
                p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                              FIND_ANYWHERE );
                if( p_playlist == NULL )
                {
                    break;
                }
276

277 278 279
                playlist_Next( p_playlist );
                vlc_object_release( p_playlist );
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
            case UP:
                {
                    audio_volume_t i_newvol;
                    aout_VolumeUp( p_intf, 1, &i_newvol );
                    msg_Dbg(p_intf, "Louder");
                }
                break;
            case DOWN:
                {
                    audio_volume_t i_newvol;
                    aout_VolumeDown( p_intf, 1, &i_newvol );
                    msg_Dbg(p_intf, "Quieter");
                }
                break;
            case GESTURE(UP,DOWN,NONE,NONE):
            case GESTURE(DOWN,UP,NONE,NONE):
                {
                    audio_volume_t i_newvol = -1;
                    aout_VolumeMute( p_intf, &i_newvol );
                    msg_Dbg(p_intf, "Mute sound");
                }
                break;
302
            case GESTURE(UP,RIGHT,NONE,NONE):
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
303
                {
304
                   input_thread_t * p_input;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
305 306
                   vlc_value_t val, list, list2;
                   int i_count, i;
307 308 309 310 311 312 313 314 315

                    p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                              FIND_ANYWHERE );

                   if( !p_playlist )
                        break;

                    p_input = input_from_playlist( p_playlist );

316 317
                    vlc_object_release( p_playlist );

318 319 320
                    if( !p_input )
                        break;

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
321 322 323 324 325 326
                   var_Get( p_input, "audio-es", &val );
                   var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
                               &list, &list2 );
                   i_count = list.p_list->i_count;
                   if( i_count <= 1 )
                   {
327
                       vlc_object_release( p_input );
328
                       break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
                   }
                   for( i = 0; i < i_count; i++ )
                   {
                       if( val.i_int == list.p_list->p_values[i].i_int )
                       {
                           break;
                       }
                   }
                   /* value of audio-es was not in choices list */
                   if( i == i_count )
                   {
                       msg_Warn( p_input,
                               "invalid current audio track, selecting 0" );
                       var_Set( p_input, "audio-es",
                               list.p_list->p_values[0] );
                       i = 0;
                   }
                   else if( i == i_count - 1 )
                   {
                       var_Set( p_input, "audio-es",
                               list.p_list->p_values[1] );
                       i = 1;
                   }
                   else
                   {
                       var_Set( p_input, "audio-es",
                               list.p_list->p_values[i+1] );
                       i++;
                   }
358
                   vlc_object_release( p_input );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
359 360 361 362
                }
                break;
            case GESTURE(DOWN,RIGHT,NONE,NONE):
                {
363
                    input_thread_t * p_input;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
364 365
                    vlc_value_t val, list, list2;
                    int i_count, i;
366 367 368 369 370 371 372 373

                    p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                              FIND_ANYWHERE );

                    if( !p_playlist )
                        break;

                    p_input = input_from_playlist( p_playlist );
374
                    vlc_object_release( p_playlist );
375 376 377

                    if( !p_input )
                        break;
378

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
379 380 381 382 383 384 385
                    var_Get( p_input, "spu-es", &val );

                    var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
                            &list, &list2 );
                    i_count = list.p_list->i_count;
                    if( i_count <= 1 )
                    {
386
                        vlc_object_release( p_input );
387
                        break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
                    }
                    for( i = 0; i < i_count; i++ )
                    {
                        if( val.i_int == list.p_list->p_values[i].i_int )
                        {
                            break;
                        }
                    }
                    /* value of spu-es was not in choices list */
                    if( i == i_count )
                    {
                        msg_Warn( p_input,
                                "invalid current subtitle track, selecting 0" );
                        var_Set( p_input, "spu-es", list.p_list->p_values[0] );
                        i = 0;
                    }
                    else if( i == i_count - 1 )
                    {
                        var_Set( p_input, "spu-es", list.p_list->p_values[0] );
                        i = 0;
                    }
                    else
                    {
411
                        var_Set( p_input, "spu-es",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
412 413 414
                                list.p_list->p_values[i+1] );
                        i = i + 1;
                    }
415
                    vlc_object_release( p_input );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
416 417 418
                }
                break;
            case GESTURE(UP,LEFT,NONE,NONE):
419
                if (p_intf->p_sys->p_vout )
420
                {
421 422
                    ((vout_thread_t *)p_intf->p_sys->p_vout)->i_changes |=
                        VOUT_FULLSCREEN_CHANGE;
423 424
                }
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
425
            case GESTURE(DOWN,LEFT,NONE,NONE):
426
                /* FIXME: Should close the vout!"*/
427
                vlc_object_kill( p_intf->p_libvlc );
428
                break;
429
            case GESTURE(DOWN,LEFT,UP,RIGHT):
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
430
            case GESTURE(UP,RIGHT,DOWN,LEFT):
431
                msg_Dbg(p_intf, "a square was drawn!" );
432 433 434 435 436 437
                break;
            default:
                break;
            }
            p_intf->p_sys->i_num_gestures = 0;
            p_intf->p_sys->i_pattern = 0;
438
            p_intf->p_sys->b_got_gesture = false;
439
        }
440 441

        /*
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
         * video output
         */
        if( p_intf->p_sys->p_vout && p_intf->p_sys->p_vout->b_die )
        {
            var_DelCallback( p_intf->p_sys->p_vout, "mouse-moved",
                             MouseEvent, p_intf );
            var_DelCallback( p_intf->p_sys->p_vout, "mouse-button-down",
                             MouseEvent, p_intf );
            vlc_object_release( p_intf->p_sys->p_vout );
            p_intf->p_sys->p_vout = NULL;
        }

        if( p_intf->p_sys->p_vout == NULL )
        {
            p_intf->p_sys->p_vout = vlc_object_find( p_intf,
                                      VLC_OBJECT_VOUT, FIND_ANYWHERE );
            if( p_intf->p_sys->p_vout )
            {
                var_AddCallback( p_intf->p_sys->p_vout, "mouse-moved",
                                 MouseEvent, p_intf );
                var_AddCallback( p_intf->p_sys->p_vout, "mouse-button-down",
                                 MouseEvent, p_intf );
            }
        }

467 468
        vlc_mutex_unlock( &p_intf->change_lock );

469 470 471 472
        /* Wait a bit */
        msleep( INTF_IDLE_SLEEP );
    }

473
    EndThread( p_intf );
474 475 476 477 478 479 480 481 482
}

/*****************************************************************************
 * InitThread:
 *****************************************************************************/
static int InitThread( intf_thread_t * p_intf )
{
    char *psz_button;
    /* we might need some locking here */
483
    if( !intf_ShouldDie( p_intf ) )
484
    {
485 486 487 488 489 490
        /* p_intf->change_lock locking strategy:
         * - Every access to p_intf->p_sys are locked threw p_intf->change_lock
         * - make sure there won't be  cross increment/decrement ref count
         *   of p_intf->p_sys members p_intf->change_lock should be locked
         *   during those operations */
        vlc_mutex_lock( &p_intf->change_lock );
491

492 493
        p_intf->p_sys->b_got_gesture = false;
        p_intf->p_sys->b_button_pressed = false;
494 495
        p_intf->p_sys->i_threshold =
                     config_GetInt( p_intf, "gestures-threshold" );
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
        psz_button = config_GetPsz( p_intf, "gestures-button" );
        if ( !strcmp( psz_button, "left" ) )
        {
            p_intf->p_sys->i_button_mask = 1;
        }
        else if ( !strcmp( psz_button, "middle" ) )
        {
            p_intf->p_sys->i_button_mask = 2;
        }
        else if ( !strcmp( psz_button, "right" ) )
        {
            p_intf->p_sys->i_button_mask = 4;
        }

        p_intf->p_sys->i_pattern = 0;
        p_intf->p_sys->i_num_gestures = 0;
        vlc_mutex_unlock( &p_intf->change_lock );

        return 0;
    }
    else
    {
        return -1;
    }
}

522 523 524 525 526
/*****************************************************************************
 * EndThread:
 *****************************************************************************/
static void EndThread( intf_thread_t * p_intf )
{
527 528
    vlc_mutex_lock( &p_intf->change_lock );

529 530 531 532 533 534 535 536 537
    if( p_intf->p_sys->p_vout )
    {
        var_DelCallback( p_intf->p_sys->p_vout, "mouse-moved",
                         MouseEvent, p_intf );
        var_DelCallback( p_intf->p_sys->p_vout, "mouse-button-down",
                         MouseEvent, p_intf );
        vlc_object_release( p_intf->p_sys->p_vout );
    }

538
    vlc_mutex_unlock( &p_intf->change_lock );
539 540
}

541 542 543 544 545 546
/*****************************************************************************
 * MouseEvent: callback for mouse events
 *****************************************************************************/
static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
                       vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
Rafaël Carré's avatar
Rafaël Carré committed
547
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
548 549
    vlc_value_t val;
    int pattern = 0;
550

551 552 553
    signed int i_horizontal, i_vertical;
    intf_thread_t *p_intf = (intf_thread_t *)p_data;

554 555
    vlc_mutex_lock( &p_intf->change_lock );

556
    /* don't process new gestures before the last events are processed */
557 558
    if( p_intf->p_sys->b_got_gesture )
    {
559
        vlc_mutex_unlock( &p_intf->change_lock );
560 561 562
        return VLC_SUCCESS;
    }

563 564
    if( !strcmp(psz_var, "mouse-moved" ) && p_intf->p_sys->b_button_pressed )
    {
565 566 567 568 569 570 571 572 573 574
        var_Get( p_intf->p_sys->p_vout, "mouse-x", &val );
        p_intf->p_sys->i_mouse_x = val.i_int;
        var_Get( p_intf->p_sys->p_vout, "mouse-y", &val );
        p_intf->p_sys->i_mouse_y = val.i_int;
        i_horizontal = p_intf->p_sys->i_mouse_x -
            p_intf->p_sys->i_last_x;
        i_horizontal = i_horizontal / p_intf->p_sys->i_threshold;
        i_vertical = p_intf->p_sys->i_mouse_y
            - p_intf->p_sys->i_last_y;
        i_vertical = i_vertical / p_intf->p_sys->i_threshold;
575 576

        if( i_horizontal < 0 )
577
        {
578
            msg_Dbg( p_intf, "left gesture (%d)", i_horizontal );
579 580
            pattern = LEFT;
        }
581
        else if( i_horizontal > 0 )
582
        {
583
            msg_Dbg( p_intf, "right gesture (%d)", i_horizontal );
584 585
            pattern = RIGHT;
        }
586
        if( i_vertical < 0 )
587
        {
588
            msg_Dbg( p_intf, "up gesture (%d)", i_vertical );
589 590
            pattern = UP;
        }
591
        else if( i_vertical > 0 )
592
        {
593
            msg_Dbg( p_intf, "down gesture (%d)", i_vertical );
594 595
            pattern = DOWN;
        }
596
        if( pattern )
597 598 599
        {
            p_intf->p_sys->i_last_y = p_intf->p_sys->i_mouse_y;
            p_intf->p_sys->i_last_x = p_intf->p_sys->i_mouse_x;
600 601
            if( gesture( p_intf->p_sys->i_pattern,
                         p_intf->p_sys->i_num_gestures - 1 ) != pattern )
602
            {
603 604
                p_intf->p_sys->i_pattern |=
                    pattern << ( p_intf->p_sys->i_num_gestures * 4 );
605 606 607 608 609
                p_intf->p_sys->i_num_gestures++;
            }
        }

    }
610 611
    if( !strcmp( psz_var, "mouse-button-down" )
        && newval.i_int & p_intf->p_sys->i_button_mask
612 613
        && !p_intf->p_sys->b_button_pressed )
    {
614
        p_intf->p_sys->b_button_pressed = true;
615 616 617 618 619
        var_Get( p_intf->p_sys->p_vout, "mouse-x", &val );
        p_intf->p_sys->i_last_x = val.i_int;
        var_Get( p_intf->p_sys->p_vout, "mouse-y", &val );
        p_intf->p_sys->i_last_y = val.i_int;
    }
620 621
    if( !strcmp( psz_var, "mouse-button-down" )
        && !( newval.i_int & p_intf->p_sys->i_button_mask )
622 623
        && p_intf->p_sys->b_button_pressed )
    {
624 625
        p_intf->p_sys->b_button_pressed = false;
        p_intf->p_sys->b_got_gesture = true;
626 627 628 629 630 631
    }

    vlc_mutex_unlock( &p_intf->change_lock );

    return VLC_SUCCESS;
}