gestures.c 21 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_common.h>
33
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
34 35
#include <vlc_interface.h>
#include <vlc_vout.h>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
36
#include <vlc_aout.h>
Clément Stenac's avatar
Clément Stenac committed
37
#include <vlc_playlist.h>
38

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

43 44 45 46 47 48
/*****************************************************************************
 * intf_sys_t: description and status of interface
 *****************************************************************************/
struct intf_sys_t
{
    vlc_object_t *      p_vout;
49 50
    bool          b_got_gesture;
    bool          b_button_pressed;
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    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 ))

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
69 70
int  Open   ( vlc_object_t * );
void Close  ( vlc_object_t * );
71
static int  InitThread     ( intf_thread_t *p_intf );
72
static void EndThread      ( intf_thread_t *p_intf );
73 74 75 76 77 78 79 80 81
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
82
#define THRESHOLD_TEXT N_( "Motion threshold (10-100)" )
83
#define THRESHOLD_LONGTEXT N_( \
84
    "Amount of movement required for a mouse gesture to be recorded." )
85

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

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

vlc_module_begin();
95
    set_shortname( N_("Gestures"));
Clément Stenac's avatar
Clément Stenac committed
96 97
    set_category( CAT_INTERFACE );
    set_subcategory( SUBCAT_INTERFACE_CONTROL );
98
    add_integer( "gestures-threshold", 30, NULL,
99
                 THRESHOLD_TEXT, THRESHOLD_LONGTEXT, true );
100
    add_string( "gestures-button", "right", NULL,
101
                BUTTON_TEXT, BUTTON_LONGTEXT, false );
102
        change_string_list( button_list, button_list_text, 0 );
103
    set_description( N_("Mouse gestures control interface") );
104 105

    set_capability( "interface", 0 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
106
    set_callbacks( Open, Close );
107 108 109 110 111
vlc_module_end();

/*****************************************************************************
 * OpenIntf: initialize interface
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
112
int Open ( vlc_object_t *p_this )
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
{
    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;
}
135

136 137 138 139 140 141
/*****************************************************************************
 * 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 )
{
142
    return playlist_CurrentInput( p_playlist );
143 144
}

145 146 147
/*****************************************************************************
 * CloseIntf: destroy dummy interface
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
148
void Close ( vlc_object_t *p_this )
149 150 151 152 153 154 155 156 157 158 159 160 161 162
{
    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;
163
    int canc = vlc_savecancel();
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
164

165 166 167 168
    vlc_mutex_lock( &p_intf->change_lock );
    p_intf->p_sys->p_vout = NULL;
    vlc_mutex_unlock( &p_intf->change_lock );

169 170
    if( InitThread( p_intf ) < 0 )
    {
Antoine Cellerier's avatar
Antoine Cellerier committed
171
        msg_Err( p_intf, "can't initialize interface thread" );
172 173
        return;
    }
174
    msg_Dbg( p_intf, "interface thread initialized" );
175 176

    /* Main loop */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
177
    while( vlc_object_alive( p_intf ) )
178 179 180
    {
        vlc_mutex_lock( &p_intf->change_lock );

181
        /*
182 183 184 185
         * mouse cursor
         */
        if( p_intf->p_sys->b_got_gesture )
        {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
186 187
            vlc_value_t val;
            int i_interval = 0;
188
            /* Do something */
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
189 190 191
            /* If you modify this, please try to follow this convention:
               Start with LEFT, RIGHT for playback related commands
               and UP, DOWN, for other commands */
192 193 194
            switch( p_intf->p_sys->i_pattern )
            {
            case LEFT:
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
                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):
                {
221
                    input_thread_t * p_input;
Antoine Cellerier's avatar
Antoine Cellerier committed
222
                    p_playlist = pl_Hold( p_intf );
223

224
                    p_input = input_from_playlist( p_playlist );
225
                    vlc_object_release( p_playlist );
226
 
227 228
                    if( !p_input )
                        break;
229
 
230 231
                    val.i_int = PLAYING_S;
                    if( p_input )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
232
                    {
233 234 235 236 237 238 239 240 241 242
                        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
243
                    }
244 245
                    msg_Dbg(p_intf, "Play/Pause");
                    vlc_object_release( p_input );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
246 247 248
                }
                break;
            case GESTURE(LEFT,DOWN,NONE,NONE):
Antoine Cellerier's avatar
Antoine Cellerier committed
249
                p_playlist = pl_Hold( p_intf );
250

251 252 253
                playlist_Prev( p_playlist );
                vlc_object_release( p_playlist );
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
254
            case GESTURE(RIGHT,DOWN,NONE,NONE):
Antoine Cellerier's avatar
Antoine Cellerier committed
255
                p_playlist = pl_Hold( p_intf );
256

257 258 259
                playlist_Next( p_playlist );
                vlc_object_release( p_playlist );
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
            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;
282
            case GESTURE(UP,RIGHT,NONE,NONE):
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
283
                {
284
                   input_thread_t * p_input;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
285 286
                   vlc_value_t val, list, list2;
                   int i_count, i;
287

Antoine Cellerier's avatar
Antoine Cellerier committed
288
                    p_playlist = pl_Hold( p_intf );
289 290 291

                    p_input = input_from_playlist( p_playlist );

292 293
                    vlc_object_release( p_playlist );

294 295 296
                    if( !p_input )
                        break;

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
297 298 299 300 301 302
                   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 )
                   {
303
                       vlc_object_release( p_input );
304
                       break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
                   }
                   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++;
                   }
334
                   vlc_object_release( p_input );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
335 336 337 338
                }
                break;
            case GESTURE(DOWN,RIGHT,NONE,NONE):
                {
339
                    input_thread_t * p_input;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
340 341
                    vlc_value_t val, list, list2;
                    int i_count, i;
342

Antoine Cellerier's avatar
Antoine Cellerier committed
343
                    p_playlist = pl_Hold( p_intf );
344 345

                    p_input = input_from_playlist( p_playlist );
346
                    vlc_object_release( p_playlist );
347 348 349

                    if( !p_input )
                        break;
350

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
351 352 353 354 355 356 357
                    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 )
                    {
358
                        vlc_object_release( p_input );
359
                        break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
                    }
                    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
                    {
383
                        var_Set( p_input, "spu-es",
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
384 385 386
                                list.p_list->p_values[i+1] );
                        i = i + 1;
                    }
387
                    vlc_object_release( p_input );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
388 389 390
                }
                break;
            case GESTURE(UP,LEFT,NONE,NONE):
391
                if (p_intf->p_sys->p_vout )
392
                {
393 394
                    ((vout_thread_t *)p_intf->p_sys->p_vout)->i_changes |=
                        VOUT_FULLSCREEN_CHANGE;
395 396
                }
                break;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
397
            case GESTURE(DOWN,LEFT,NONE,NONE):
398
                /* FIXME: Should close the vout!"*/
399
                vlc_object_kill( p_intf->p_libvlc );
400
                break;
401
            case GESTURE(DOWN,LEFT,UP,RIGHT):
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
402
            case GESTURE(UP,RIGHT,DOWN,LEFT):
403
                msg_Dbg(p_intf, "a square was drawn!" );
404 405 406 407 408 409
                break;
            default:
                break;
            }
            p_intf->p_sys->i_num_gestures = 0;
            p_intf->p_sys->i_pattern = 0;
410
            p_intf->p_sys->b_got_gesture = false;
411
        }
412 413

        /*
414 415
         * video output
         */
416
        if( p_intf->p_sys->p_vout && !vlc_object_alive (p_intf->p_sys->p_vout) )
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
        {
            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 );
            }
        }

439 440
        vlc_mutex_unlock( &p_intf->change_lock );

441 442 443 444
        /* Wait a bit */
        msleep( INTF_IDLE_SLEEP );
    }

445
    EndThread( p_intf );
446
    vlc_restorecancel( canc );
447 448 449 450 451 452 453 454 455
}

/*****************************************************************************
 * InitThread:
 *****************************************************************************/
static int InitThread( intf_thread_t * p_intf )
{
    char *psz_button;
    /* we might need some locking here */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
456
    if( vlc_object_alive( p_intf ) )
457
    {
458 459 460 461 462 463
        /* 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 );
464

465 466
        p_intf->p_sys->b_got_gesture = false;
        p_intf->p_sys->b_button_pressed = false;
467 468
        p_intf->p_sys->i_threshold =
                     config_GetInt( p_intf, "gestures-threshold" );
469 470 471 472 473 474 475 476 477 478 479 480 481
        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;
        }
482
        free( psz_button );
483 484 485 486 487 488 489 490 491 492 493 494 495

        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;
    }
}

496 497 498 499 500
/*****************************************************************************
 * EndThread:
 *****************************************************************************/
static void EndThread( intf_thread_t * p_intf )
{
501 502
    vlc_mutex_lock( &p_intf->change_lock );

503 504 505 506 507 508 509 510 511
    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 );
    }

512
    vlc_mutex_unlock( &p_intf->change_lock );
513 514
}

515 516 517 518 519 520
/*****************************************************************************
 * 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
521
    VLC_UNUSED(p_this); VLC_UNUSED(oldval);
522 523
    vlc_value_t val;
    int pattern = 0;
524

525 526 527
    signed int i_horizontal, i_vertical;
    intf_thread_t *p_intf = (intf_thread_t *)p_data;

528 529
    vlc_mutex_lock( &p_intf->change_lock );

530
    /* don't process new gestures before the last events are processed */
531 532
    if( p_intf->p_sys->b_got_gesture )
    {
533
        vlc_mutex_unlock( &p_intf->change_lock );
534 535 536
        return VLC_SUCCESS;
    }

537 538
    if( !strcmp(psz_var, "mouse-moved" ) && p_intf->p_sys->b_button_pressed )
    {
539 540 541 542 543 544 545 546 547 548
        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;
549 550

        if( i_horizontal < 0 )
551
        {
552
            msg_Dbg( p_intf, "left gesture (%d)", i_horizontal );
553 554
            pattern = LEFT;
        }
555
        else if( i_horizontal > 0 )
556
        {
557
            msg_Dbg( p_intf, "right gesture (%d)", i_horizontal );
558 559
            pattern = RIGHT;
        }
560
        if( i_vertical < 0 )
561
        {
562
            msg_Dbg( p_intf, "up gesture (%d)", i_vertical );
563 564
            pattern = UP;
        }
565
        else if( i_vertical > 0 )
566
        {
567
            msg_Dbg( p_intf, "down gesture (%d)", i_vertical );
568 569
            pattern = DOWN;
        }
570
        if( pattern )
571 572 573
        {
            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;
574 575
            if( gesture( p_intf->p_sys->i_pattern,
                         p_intf->p_sys->i_num_gestures - 1 ) != pattern )
576
            {
577 578
                p_intf->p_sys->i_pattern |=
                    pattern << ( p_intf->p_sys->i_num_gestures * 4 );
579 580 581 582 583
                p_intf->p_sys->i_num_gestures++;
            }
        }

    }
584 585
    if( !strcmp( psz_var, "mouse-button-down" )
        && newval.i_int & p_intf->p_sys->i_button_mask
586 587
        && !p_intf->p_sys->b_button_pressed )
    {
588
        p_intf->p_sys->b_button_pressed = true;
589 590 591 592 593
        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;
    }
594 595
    if( !strcmp( psz_var, "mouse-button-down" )
        && !( newval.i_int & p_intf->p_sys->i_button_mask )
596 597
        && p_intf->p_sys->b_button_pressed )
    {
598 599
        p_intf->p_sys->b_button_pressed = false;
        p_intf->p_sys->b_got_gesture = true;
600 601 602 603 604 605
    }

    vlc_mutex_unlock( &p_intf->change_lock );

    return VLC_SUCCESS;
}