Commit ce09ec77 authored by zorglub's avatar zorglub

modules/control/joystick.c: Joystick control module (linux only)

disabled by default, use --enable-joystick & --extraintf joystick

actions are:
	left-right: variable speed seek
	up-down: browse the playlist
	button 1: play/pause
	button 2: fullscreen
parent d523d1af
dnl Autoconf settings for vlc
dnl $Id: configure.ac,v 1.36 2003/07/19 14:14:22 sigmunau Exp $
dnl $Id: configure.ac,v 1.37 2003/07/20 08:30:41 zorglub Exp $
AC_INIT(vlc,0.6.0)
......@@ -2897,6 +2897,19 @@ then
fi
fi
dnl
dnl Joystick plugin
dnl
AC_ARG_ENABLE(joystick,
[ --enable-joystick joystick control (default disabled)])
if test "${enable_joystick}" = "yes"
then
AC_CHECK_HEADER( linux/joystick.h,
[AX_ADD_PLUGINS([joystick])]
)
fi
dnl
dnl corba (ORBit) plugin
dnl
......
SOURCES_gestures = gestures.c
SOURCES_http = http.c
SOURCES_joystick = joystick.c
/*****************************************************************************
* joystick.c: control vlc with a joystick
*****************************************************************************
* Copyright (C) 2002 VideoLAN
* $Id: joystick.c,v 1.1 2003/07/20 08:30:41 zorglub Exp $
*
* Authors: Clément Stenac <zorglub@via.ecp.fr>
*
* 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.
*
* 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
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#include <stdlib.h> /* malloc(), free() */
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <errno.h>
#include <fcntl.h>
#include <vlc/vlc.h>
#include <vlc/intf.h>
#include <vlc/vout.h>
#include <linux/joystick.h>
#include "audio_output.h"
#include "stream_control.h"
#include "input_ext-intf.h"
/* Default values for parameters */
#define DEFAULT_MAX_SEEK 10 /* seconds */
#define DEFAULT_REPEAT 100000
#define DEFAULT_WAIT 500000
#define DEFAULT_DEVICE "/dev/input/js0"
#define DEFAULT_THRESHOLD 12000 /* 0 -> 32767 */
/* Actions
* Available actions are: Next,Prev, Forward,Back,Play,Fullscreen,dummy */
#define AXE_0_UP_ACTION Forward
#define AXE_0_DOWN_ACTION Back
#define AXE_1_UP_ACTION Next
#define AXE_1_DOWN_ACTION Prev
#define BUTTON_1_PRESS_ACTION Play
#define BUTTON_1_RELEASE_ACTION dummy
#define BUTTON_2_PRESS_ACTION Fullscreen
#define BUTTON_2_RELEASE_ACTION dummy
/*****************************************************************************
* intf_sys_t: description and status of interface
*****************************************************************************/
typedef int (*action)(intf_thread_t *p_intf);
struct joy_axis_t
{
int b_trigered; /* Are we in the trigger zone ? */
int i_value; /* Value of movement */
int b_dowork; /* Do we have to do the action ? */
action pf_actup; /* Action when axis is up */
action pf_actdown; /* Action when axis is down */
mtime_t l_time; /* When did the axis enter the trigger
* zone ? */
};
struct joy_button_t
{
action pf_actup; /* What to do when button is released */
action pf_actdown;/* What to do when button is pressed */
};
struct intf_sys_t
{
fd_set fds; /* File descriptor set (select) */
int i_fd; /* File descriptor for joystick */
struct timeval timeout; /* Select timeout */
int i_threshold; /* motion threshold */
int i_wait; /* How much to wait before repeat */
int i_repeat; /* Repeat time */
int i_maxseek; /* Maximum seek time */
struct joy_axis_t axes[3]; /* Axes descriptor */
struct joy_button_t buttons[2]; /* Buttons descriptor */
input_thread_t *p_input; /* Input thread (for seeking) */
float f_seconds; /* How much to seek */
};
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
int Open ( vlc_object_t * );
void Close ( vlc_object_t * );
static int Init ( intf_thread_t *p_intf );
int handle_event ( intf_thread_t *p_intf, struct js_event event);
/* Actions */
int Next (intf_thread_t *p_intf);
int Prev (intf_thread_t *p_intf);
int Back (intf_thread_t *p_intf);
int Forward (intf_thread_t *p_intf);
int Play (intf_thread_t *p_intf);
int Fullscreen (intf_thread_t *p_intf);
int dummy (intf_thread_t *p_intf);
/* Exported functions */
static void Run ( intf_thread_t *p_intf );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define THRESHOLD_TEXT N_( "Motion threshold" )
#define THRESHOLD_LONGTEXT N_( \
"the amount of joystick movement required for a movement to be" \
"recorded" )
#define DEVICE_TEXT N_( "Joystick device" )
#define DEVICE_LONGTEXT N_( \
"the device for the joystick (usually /dev/jsX or /dev/input/jsX" \
"with X the number of the joystick" )
#define REPEAT_TEXT N_( "Repeat time" )
#define REPEAT_LONGTEXT N_( \
"the time waited before the action is repeated if it is still trigered" \
"in miscroseconds" )
#define WAIT_TEXT N_( "Wait before repeat time")
#define WAIT_LONGTEXT N_(\
" the time waited before the repeat starts, in microseconds")
#define SEEK_TEXT N_( "Max seek interval")
#define SEEK_LONGTEXT N_(\
" the number of seconds that will be seeked if the axis "\
"is pushed at its maximum" )
vlc_module_begin();
add_category_hint( N_( "Joystick" ), NULL, VLC_FALSE );
add_integer( "motion-threshold", 15000, NULL,
THRESHOLD_TEXT, THRESHOLD_LONGTEXT, VLC_TRUE );
add_string( "joystick-device", DEFAULT_DEVICE , NULL,
DEVICE_TEXT, DEVICE_LONGTEXT, VLC_TRUE );
add_integer ("joystick-repeat", DEFAULT_REPEAT,NULL,
REPEAT_TEXT, REPEAT_LONGTEXT, VLC_TRUE );
add_integer ("joystick-wait", DEFAULT_WAIT,NULL,
WAIT_TEXT, WAIT_LONGTEXT, VLC_TRUE );
add_integer ("joystick-max-seek",DEFAULT_MAX_SEEK,NULL,
SEEK_TEXT, SEEK_LONGTEXT, VLC_TRUE );
set_description( _("joystick control interface") );
set_capability( "interface", 0 );
set_callbacks( Open, Close );
vlc_module_end();
/*****************************************************************************
* OpenIntf: initialize interface
*****************************************************************************/
int 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 = Run;
return 0 ;
}
/*****************************************************************************
* CloseIntf: destroy the interface
*****************************************************************************/
void Close ( vlc_object_t *p_this )
{
intf_thread_t *p_intf = (intf_thread_t *)p_this;
/* Destroy structure */
if(p_intf->p_sys)
free( p_intf->p_sys );
}
/*****************************************************************************
* Run: main loop
*****************************************************************************/
static void Run( intf_thread_t *p_intf )
{
int i_sel_res = 0;
int i_read = 0;
int i_axe = 0;
struct js_event event;
if( Init( p_intf ) < 0 )
{
msg_Err( p_intf, "can't initialize intf" );
return;
}
msg_Dbg( p_intf, "intf initialized" );
/* Main loop */
while( !p_intf->b_die )
{
vlc_mutex_lock( &p_intf->change_lock );
FD_ZERO( &p_intf->p_sys->fds );
FD_SET( p_intf->p_sys->i_fd , &p_intf->p_sys->fds );
p_intf->p_sys->timeout.tv_sec = 0;
p_intf->p_sys->timeout.tv_usec = p_intf->p_sys->i_repeat;
i_sel_res = select ( p_intf->p_sys->i_fd+1,
&p_intf->p_sys->fds,
NULL,
NULL,
&p_intf->p_sys->timeout );
p_intf->p_sys->p_input = (input_thread_t *)
vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
if( i_sel_res == -1 && errno != EINTR )
{
msg_Err( p_intf, "select error: %s",strerror(errno) );
}
else if(i_sel_res > 0 && FD_ISSET( p_intf->p_sys->i_fd,
&p_intf->p_sys->fds))
{ /* We got an event */
memset(&event,0,sizeof(struct js_event));
i_read = read( p_intf->p_sys->i_fd, &event ,
sizeof(struct js_event));
handle_event( p_intf , event ) ;
}
else if(i_sel_res == 0)
/*We have no event, but check if we have an action to repeat */
{
for(i_axe=0;i_axe<=1;i_axe++)
{
if( p_intf->p_sys->axes[i_axe].b_trigered &&
mdate()-p_intf->p_sys->axes[i_axe].l_time >
p_intf->p_sys->i_wait &&
p_intf->p_sys->axes[i_axe].i_value > 0 )
{
p_intf->p_sys->axes[i_axe].pf_actup(p_intf);
}
if( p_intf->p_sys->axes[i_axe].b_trigered &&
mdate()-p_intf->p_sys->axes[i_axe].l_time >
p_intf->p_sys->i_wait &&
p_intf->p_sys->axes[i_axe].i_value < 0 )
{
p_intf->p_sys->axes[i_axe].pf_actdown(p_intf);
}
}
}
if(p_intf->p_sys->p_input)
vlc_object_release (p_intf->p_sys->p_input);
vlc_mutex_unlock ( &p_intf->change_lock );
}
}
/*****************************************************************************
* InitThread: Initialize the interface
*****************************************************************************/
static int Init( intf_thread_t * p_intf )
{
char *psz_device;
if( !p_intf->b_die )
{
vlc_mutex_lock( &p_intf->change_lock );
psz_device=config_GetPsz( p_intf, "joystick-device");
if(!psz_device) /* strange... */
psz_device = strdup( DEFAULT_DEVICE );
p_intf->p_sys->i_fd = open ( psz_device , O_RDONLY|O_NONBLOCK );
if( p_intf->p_sys->i_fd == -1 )
{
msg_Warn( p_intf, "Unable to open %s for reading: %s"
,psz_device,strerror(errno));
return -1;
}
p_intf->p_sys->i_repeat =
config_GetInt( p_intf, "joystick-repeat");
p_intf->p_sys->i_wait =
config_GetInt( p_intf, "joystick-wait");
p_intf->p_sys->i_threshold =
config_GetInt( p_intf, "motion-threshold" );
if(p_intf->p_sys->i_threshold > 32767 ||
p_intf->p_sys->i_threshold < 0 )
p_intf->p_sys->i_threshold = DEFAULT_THRESHOLD;
p_intf->p_sys->i_maxseek =
config_GetInt( p_intf, "joystick-max-seek" );
p_intf->p_sys->axes[0].pf_actup = AXE_0_UP_ACTION;
p_intf->p_sys->axes[0].pf_actdown = AXE_0_DOWN_ACTION;
p_intf->p_sys->axes[0].b_trigered = VLC_FALSE;
p_intf->p_sys->axes[0].l_time = 0;
p_intf->p_sys->axes[1].pf_actup = AXE_1_UP_ACTION;
p_intf->p_sys->axes[1].pf_actdown = AXE_1_DOWN_ACTION;
p_intf->p_sys->axes[1].b_trigered = VLC_FALSE;
p_intf->p_sys->axes[1].l_time = 0;
p_intf->p_sys->buttons[0].pf_actdown = BUTTON_1_PRESS_ACTION;
p_intf->p_sys->buttons[0].pf_actup = BUTTON_1_RELEASE_ACTION;
p_intf->p_sys->buttons[1].pf_actdown = BUTTON_2_PRESS_ACTION;
p_intf->p_sys->buttons[1].pf_actup = BUTTON_2_RELEASE_ACTION;
vlc_mutex_unlock( &p_intf->change_lock );
return 0;
}
else
{
return -1;
}
}
/*****************************************************************************
* handle_event : parse a joystick event and takes the appropriate action *
*****************************************************************************/
int handle_event ( intf_thread_t *p_intf, struct js_event event)
{
unsigned int i_axe;
if( event.type == JS_EVENT_AXIS )
{
/* Third axe is supposed to behave in a different way:
* it is a throttle, and will set a value, without
* triggering something */
if( event.number == 2 &&
/* Try to avoid Parkinson joysticks */
abs(event.value - p_intf->p_sys->axes[2].i_value) > 10 )
{
p_intf->p_sys->axes[2].i_value = event.value;
msg_Dbg( p_intf , "Updating volume" );
/* This way, the volume is between 0 and 1024 */
aout_VolumeSet( p_intf, (32767-event.value)/64 );
return 0;
}
p_intf->p_sys->axes[event.number].b_dowork = VLC_FALSE;
p_intf->p_sys->axes[event.number].i_value = event.value;
if( abs(event.value) > p_intf->p_sys->i_threshold &&
p_intf->p_sys->axes[event.number].b_trigered == VLC_FALSE)
{
/* The axis entered the trigger zone. Start the event */
p_intf->p_sys->axes[event.number].b_trigered = VLC_TRUE;
p_intf->p_sys->axes[event.number].b_dowork = VLC_TRUE;
p_intf->p_sys->axes[event.number].l_time = mdate();
}
else if(abs(event.value) > p_intf->p_sys->i_threshold &&
p_intf->p_sys->axes[event.number].b_trigered == VLC_TRUE)
{
/* The axis moved but remained in the trigger zone
* Do nothing at this time */
}
else if ( abs(event.value) < p_intf->p_sys->i_threshold )
{
/* The axis is not in the trigger zone */
p_intf->p_sys->axes[event.number].b_trigered = VLC_FALSE;
}
/* Special for seeking */
p_intf->p_sys->f_seconds =
(abs(event.value)-p_intf->p_sys->i_threshold)*
p_intf->p_sys->i_maxseek/
(32767-p_intf->p_sys->i_threshold);
/* Handle the first two axes. */
for(i_axe = 0; i_axe <= 1 ; i_axe ++)
{
if(p_intf->p_sys->axes[i_axe].b_dowork == VLC_TRUE)
{
if( p_intf->p_sys->axes[i_axe].i_value
> p_intf->p_sys->i_threshold )
{
msg_Dbg(p_intf,"Up for axis %i\n",i_axe);
p_intf->p_sys->axes[i_axe].pf_actup(p_intf);
}
else if( p_intf->p_sys->axes[i_axe].i_value
< -p_intf->p_sys->i_threshold )
{
msg_Dbg(p_intf,"Down for axis %i\n",i_axe);
p_intf->p_sys->axes[i_axe].pf_actdown(p_intf);
}
}
}
}
else if( event.type == JS_EVENT_BUTTON)
{
msg_Dbg(p_intf,"Button %i %s",event.number,
event.value ? "pressed":"released");
if(event.number >1) return 0; /* Only trigger 2 buttons */
if(event.value == 1) /* Button pressed */
{
if(p_intf->p_sys->buttons[event.number].pf_actdown)
p_intf->p_sys->buttons[event.number].pf_actdown(p_intf);
}
else /* Button released */
if(p_intf->p_sys->buttons[event.number].pf_actup)
p_intf->p_sys->buttons[event.number].pf_actup(p_intf);
}
return 0;
}
/****************************************************************************
* The possible actions
****************************************************************************/
int Next( intf_thread_t *p_intf)
{
playlist_t *p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
if( p_playlist == NULL )
{
return -1;
}
playlist_Next( p_playlist );
vlc_object_release( p_playlist );
return 0;
}
int Prev( intf_thread_t *p_intf)
{
playlist_t *p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
FIND_ANYWHERE );
if( p_playlist == NULL )
{
return -1;
}
playlist_Prev( p_playlist );
vlc_object_release( p_playlist );
return 0;
}
int Forward(intf_thread_t *p_intf)
{
msg_Dbg(p_intf,"Seeking %f seconds",p_intf->p_sys->f_seconds);
input_Seek( p_intf->p_sys->p_input, p_intf->p_sys->f_seconds,
INPUT_SEEK_SECONDS | INPUT_SEEK_CUR);
return 0;
}
int Back(intf_thread_t *p_intf)
{
msg_Dbg(p_intf,"Seeking %f seconds",p_intf->p_sys->f_seconds);
input_Seek( p_intf->p_sys->p_input, -(p_intf->p_sys->f_seconds),
INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
return 0;
}
int Play(intf_thread_t *p_intf)
{
input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PAUSE );
return 0;
}
int Fullscreen(intf_thread_t *p_intf)
{
vout_thread_t * p_vout=vlc_object_find(p_intf,
VLC_OBJECT_VOUT, FIND_ANYWHERE );
if(p_vout)
{
p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
vlc_object_release(p_vout);
}
return 0;
}
/* dummy event. Use it if you don't wan't anything to happen */
int dummy(intf_thread_t *p_intf)
{
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment