1 /*****************************************************************************
2 * gestures.c: control vlc with mouse gestures
3 *****************************************************************************
4 * Copyright (C) 2004-2009 the VideoLAN team
7 * Authors: Sigmund Augdal Helberg <dnumgis@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_interface.h>
36 #include <vlc_aout_intf.h>
37 #include <vlc_playlist.h>
40 /*****************************************************************************
41 * intf_sys_t: description and status of interface
42 *****************************************************************************/
46 input_thread_t *p_input;
47 vout_thread_t *p_vout;
48 bool b_button_pressed;
49 int i_last_x, i_last_y;
50 unsigned int i_pattern;
56 /*****************************************************************************
58 *****************************************************************************/
64 #define GESTURE( a, b, c, d ) (a | ( b << 4 ) | ( c << 8 ) | ( d << 12 ))
66 static int Open ( vlc_object_t * );
67 static void Close ( vlc_object_t * );
69 /*****************************************************************************
71 *****************************************************************************/
72 #define THRESHOLD_TEXT N_( "Motion threshold (10-100)" )
73 #define THRESHOLD_LONGTEXT N_( \
74 "Amount of movement required for a mouse gesture to be recorded." )
76 #define BUTTON_TEXT N_( "Trigger button" )
77 #define BUTTON_LONGTEXT N_( \
78 "Trigger button for mouse gestures." )
80 #if defined (HAVE_MAEMO)
81 # define BUTTON_DEFAULT "left"
83 # define BUTTON_DEFAULT "right"
86 static const char *const button_list[] = { "left", "middle", "right" };
87 static const char *const button_list_text[] =
88 { N_("Left"), N_("Middle"), N_("Right") };
91 set_shortname( N_("Gestures"))
92 set_category( CAT_INTERFACE )
93 set_subcategory( SUBCAT_INTERFACE_CONTROL )
94 add_integer( "gestures-threshold", 30,
95 THRESHOLD_TEXT, THRESHOLD_LONGTEXT, true )
96 add_string( "gestures-button", BUTTON_DEFAULT,
97 BUTTON_TEXT, BUTTON_LONGTEXT, false )
98 change_string_list( button_list, button_list_text )
99 set_description( N_("Mouse gestures control interface") )
101 set_capability( "interface", 0 )
102 set_callbacks( Open, Close )
105 static int PlaylistEvent( vlc_object_t *, char const *,
106 vlc_value_t, vlc_value_t, void * );
107 static int InputEvent( vlc_object_t *, char const *,
108 vlc_value_t, vlc_value_t, void * );
109 static int MovedEvent( vlc_object_t *, char const *,
110 vlc_value_t, vlc_value_t, void * );
111 static int ButtonEvent( vlc_object_t *, char const *,
112 vlc_value_t, vlc_value_t, void * );
114 /*****************************************************************************
115 * OpenIntf: initialize interface
116 *****************************************************************************/
117 static int Open ( vlc_object_t *p_this )
119 intf_thread_t *p_intf = (intf_thread_t *)p_this;
121 /* Allocate instance and initialize some members */
122 intf_sys_t *p_sys = p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
123 if( unlikely(p_sys == NULL) )
126 // Configure the module
127 vlc_mutex_init( &p_sys->lock );
128 p_sys->p_input = NULL;
129 p_sys->p_vout = NULL;
130 p_sys->b_button_pressed = false;
131 p_sys->i_threshold = var_InheritInteger( p_intf, "gestures-threshold" );
133 // Choose the tight button to use
134 char *psz_button = var_InheritString( p_intf, "gestures-button" );
135 if( psz_button && !strcmp( psz_button, "left" ) )
136 p_sys->i_button_mask = 1;
137 else if( psz_button && !strcmp( psz_button, "middle" ) )
138 p_sys->i_button_mask = 2;
139 else // psz_button == "right"
140 p_sys->i_button_mask = 4;
143 p_sys->i_pattern = 0;
144 p_sys->i_num_gestures = 0;
146 var_AddCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
151 /*****************************************************************************
152 * gesture: return a subpattern within a pattern
153 *****************************************************************************/
154 static int gesture( int i_pattern, int i_num )
156 return ( i_pattern >> ( i_num * 4 ) ) & 0xF;
159 /*****************************************************************************
160 * CloseIntf: destroy dummy interface
161 *****************************************************************************/
162 static void Close ( vlc_object_t *p_this )
164 intf_thread_t *p_intf = (intf_thread_t *)p_this;
165 intf_sys_t *p_sys = p_intf->p_sys;
167 /* Destroy the callbacks (the order matters!) */
168 var_DelCallback( pl_Get(p_intf), "input-current", PlaylistEvent, p_intf );
172 var_DelCallback( p_sys->p_input, "intf-event", InputEvent, p_intf );
173 vlc_object_release( p_sys->p_input );
178 var_DelCallback( p_sys->p_vout, "mouse-moved", MovedEvent, p_intf );
179 var_DelCallback( p_sys->p_vout, "mouse-button-down",
180 ButtonEvent, p_intf );
181 vlc_object_release( p_sys->p_vout );
184 /* Destroy structure */
185 vlc_mutex_destroy( &p_sys->lock );
189 static void ProcessGesture( intf_thread_t *p_intf )
191 intf_sys_t *p_sys = p_intf->p_sys;
192 playlist_t *p_playlist = pl_Get( p_intf );
195 /* If you modify this, please try to follow this convention:
196 Start with LEFT, RIGHT for playback related commands
197 and UP, DOWN, for other commands */
198 switch( p_sys->i_pattern )
202 msg_Dbg( p_intf, "Go backward in the movie!" );
204 input_thread_t *p_input = playlist_CurrentInput( p_playlist );
205 if( p_input == NULL )
208 int it = var_InheritInteger( p_intf , "short-jump-size" );
210 var_SetTime( p_input, "time-offset", -CLOCK_FREQ * it );
211 vlc_object_release( p_input );
217 msg_Dbg( p_intf, "Go forward in the movie!" );
219 input_thread_t *p_input = playlist_CurrentInput( p_playlist );
220 if( p_input == NULL )
223 int it = var_InheritInteger( p_intf , "short-jump-size" );
225 var_SetTime( p_input, "time-offset", CLOCK_FREQ * it );
226 vlc_object_release( p_input );
230 case GESTURE(LEFT,UP,NONE,NONE):
231 msg_Dbg( p_intf, "Going slower." );
232 var_TriggerCallback( p_playlist, "rate-slower" );
235 case GESTURE(RIGHT,UP,NONE,NONE):
236 msg_Dbg( p_intf, "Going faster." );
237 var_TriggerCallback( p_playlist, "rate-faster" );
240 case GESTURE(LEFT,RIGHT,NONE,NONE):
241 case GESTURE(RIGHT,LEFT,NONE,NONE):
243 msg_Dbg( p_intf, "Play/Pause" );
245 input_thread_t *p_input = playlist_CurrentInput( p_playlist );
246 if( p_input == NULL )
249 int i_state = var_GetInteger( p_input, "state" );
250 i_state = (i_state == PLAYING_S) ? PAUSE_S : PLAYING_S;
251 var_SetInteger( p_input, "state", i_state );
252 vlc_object_release( p_input );
256 case GESTURE(LEFT,DOWN,NONE,NONE):
257 playlist_Prev( p_playlist );
260 case GESTURE(RIGHT,DOWN,NONE,NONE):
261 playlist_Next( p_playlist );
265 msg_Dbg(p_intf, "Louder");
266 aout_VolumeUp( p_playlist, 1, NULL );
270 msg_Dbg(p_intf, "Quieter");
271 aout_VolumeDown( p_playlist, 1, NULL );
274 case GESTURE(UP,DOWN,NONE,NONE):
275 case GESTURE(DOWN,UP,NONE,NONE):
276 msg_Dbg( p_intf, "Mute sound" );
277 aout_MuteToggle( p_playlist );
280 case GESTURE(UP,RIGHT,NONE,NONE):
282 input_thread_t *p_input = playlist_CurrentInput( p_playlist );
283 if( p_input == NULL )
286 vlc_value_t list, list2;
287 var_Change( p_input, "audio-es", VLC_VAR_GETCHOICES,
290 if( list.p_list->i_count > 1 )
292 int i_audio_es = var_GetInteger( p_input, "audio-es" );
295 for( i = 0; i < list.p_list->i_count; i++ )
296 if( i_audio_es == list.p_list->p_values[i].i_int )
298 /* value of audio-es was not in choices list */
299 if( i == list.p_list->i_count )
302 "invalid current audio track, selecting 0" );
305 else if( i == list.p_list->i_count - 1 )
309 var_SetInteger( p_input, "audio-es",
310 list.p_list->p_values[i].i_int );
312 var_FreeList( &list, &list2 );
313 vlc_object_release( p_input );
317 case GESTURE(DOWN,RIGHT,NONE,NONE):
319 input_thread_t *p_input = playlist_CurrentInput( p_playlist );
320 if( p_input == NULL )
323 vlc_value_t list, list2;
324 var_Change( p_input, "spu-es", VLC_VAR_GETCHOICES,
327 if( list.p_list->i_count > 1 )
329 int i_audio_es = var_GetInteger( p_input, "spu-es" );
332 for( i = 0; i < list.p_list->i_count; i++ )
333 if( i_audio_es == list.p_list->p_values[i].i_int )
335 /* value of audio-es was not in choices list */
336 if( i == list.p_list->i_count )
339 "invalid current subtitle track, selecting 0" );
342 else if( i == list.p_list->i_count - 1 )
346 var_SetInteger( p_input, "audio-es",
347 list.p_list->p_values[i].i_int );
349 var_FreeList( &list, &list2 );
350 vlc_object_release( p_input );
354 case GESTURE(UP,LEFT,NONE,NONE):
356 bool val = var_ToggleBool( pl_Get( p_intf ), "fullscreen" );
358 var_SetBool( p_sys->p_vout, "fullscreen", val );
362 case GESTURE(DOWN,LEFT,NONE,NONE):
363 /* FIXME: Should close the vout!"*/
364 libvlc_Quit( p_intf->p_libvlc );
367 case GESTURE(DOWN,LEFT,UP,RIGHT):
368 case GESTURE(UP,RIGHT,DOWN,LEFT):
369 msg_Dbg( p_intf, "a square was drawn!" );
373 p_sys->i_num_gestures = 0;
374 p_sys->i_pattern = 0;
377 static int MovedEvent( vlc_object_t *p_this, char const *psz_var,
378 vlc_value_t oldval, vlc_value_t newval, void *p_data )
380 intf_thread_t *p_intf = (intf_thread_t *)p_data;
381 intf_sys_t *p_sys = p_intf->p_sys;
383 (void) p_this; (void) psz_var; (void) oldval;
385 vlc_mutex_lock( &p_sys->lock );
386 if( p_sys->b_button_pressed )
388 int i_horizontal = newval.coords.x - p_sys->i_last_x;
389 int i_vertical = newval.coords.y - p_sys->i_last_y;
392 i_horizontal = i_horizontal / p_sys->i_threshold;
393 i_vertical = i_vertical / p_sys->i_threshold;
395 if( i_horizontal < 0 )
397 msg_Dbg( p_intf, "left gesture (%d)", i_horizontal );
400 else if( i_horizontal > 0 )
402 msg_Dbg( p_intf, "right gesture (%d)", i_horizontal );
407 msg_Dbg( p_intf, "up gesture (%d)", i_vertical );
410 else if( i_vertical > 0 )
412 msg_Dbg( p_intf, "down gesture (%d)", i_vertical );
418 p_sys->i_last_x = newval.coords.x;
419 p_sys->i_last_y = newval.coords.y;
420 if( gesture( p_sys->i_pattern, p_sys->i_num_gestures - 1 )
423 p_sys->i_pattern |= pattern << ( p_sys->i_num_gestures * 4 );
424 p_sys->i_num_gestures++;
429 vlc_mutex_unlock( &p_sys->lock );
434 static int ButtonEvent( vlc_object_t *p_this, char const *psz_var,
435 vlc_value_t oldval, vlc_value_t val, void *p_data )
437 intf_thread_t *p_intf = p_data;
438 intf_sys_t *p_sys = p_intf->p_sys;
440 (void) psz_var; (void) oldval;
442 vlc_mutex_lock( &p_sys->lock );
443 if( val.i_int & p_sys->i_button_mask )
445 if( !p_sys->b_button_pressed )
447 p_sys->b_button_pressed = true;
448 var_GetCoords( p_this, "mouse-moved",
449 &p_sys->i_last_x, &p_sys->i_last_y );
454 if( p_sys->b_button_pressed )
456 p_sys->b_button_pressed = false;
457 ProcessGesture( p_intf );
460 vlc_mutex_unlock( &p_sys->lock );
465 static int InputEvent( vlc_object_t *p_this, char const *psz_var,
466 vlc_value_t oldval, vlc_value_t val, void *p_data )
468 input_thread_t *p_input = (input_thread_t *)p_this;
469 intf_thread_t *p_intf = p_data;
470 intf_sys_t *p_sys = p_intf->p_sys;
472 (void) psz_var; (void) oldval;
476 case INPUT_EVENT_DEAD:
477 vlc_object_release( p_input );
478 p_sys->p_input = NULL; /* FIXME: locking!! */
481 case INPUT_EVENT_VOUT:
482 /* intf-event is serialized against itself and is the sole user of
483 * p_sys->p_vout. So there is no need to acquire the lock currently. */
484 if( p_sys->p_vout != NULL )
485 { /* /!\ Beware of lock inversion with var_DelCallback() /!\ */
486 var_DelCallback( p_sys->p_vout, "mouse-moved", MovedEvent,
488 var_DelCallback( p_sys->p_vout, "mouse-clicked", ButtonEvent,
490 vlc_object_release( p_sys->p_vout );
493 p_sys->p_vout = input_GetVout( p_input );
494 if( p_sys->p_vout != NULL )
496 var_AddCallback( p_sys->p_vout, "mouse-moved", MovedEvent,
498 var_AddCallback( p_sys->p_vout, "mouse-clicked", ButtonEvent,
506 static int PlaylistEvent( vlc_object_t *p_this, char const *psz_var,
507 vlc_value_t oldval, vlc_value_t val, void *p_data )
509 intf_thread_t *p_intf = p_data;
510 intf_sys_t *p_sys = p_intf->p_sys;
511 input_thread_t *p_input = val.p_address;
513 (void) p_this; (void) psz_var; (void) oldval;
515 var_AddCallback( p_input, "intf-event", InputEvent, p_intf );
516 assert( p_sys->p_input == NULL );
517 p_sys->p_input = vlc_object_hold( p_input );