2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2013-01-01 00:37:37 +00:00
* Copyright ( C ) 2010 - 2013 - Hans - Kristian Arntzen
2011-01-09 14:44:35 +00:00
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software : you can redistribute it and / or modify it under the terms
2011-01-09 14:44:35 +00:00
* of the GNU General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ;
2011-01-09 14:44:35 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE . See the GNU General Public License for more details .
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch .
2011-01-09 14:44:35 +00:00
* If not , see < http : //www.gnu.org/licenses/>.
*/
2011-01-09 14:58:49 +00:00
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
2011-01-09 14:44:35 +00:00
# include "conf/config_file.h"
# include <stdio.h>
# include <stdlib.h>
2012-09-29 19:57:03 +00:00
# include "../compat/getopt_rarch.h"
2011-12-24 12:46:12 +00:00
# include "../boolean.h"
2012-09-29 19:57:03 +00:00
# include "../input/input_common.h"
# include "../general.h"
2011-12-24 12:46:12 +00:00
# include <assert.h>
2012-03-16 22:26:57 +00:00
# include "../compat/posix_string.h"
2011-01-09 14:58:49 +00:00
2012-09-29 19:57:03 +00:00
// Need to be present for build to work, but it's not *really* used.
// Better than having to build special versions of lots of objects with special #ifdefs.
struct settings g_settings ;
struct global g_extern ;
driver_t driver ;
2011-01-09 14:44:35 +00:00
static int g_player = 1 ;
2011-01-15 15:52:45 +00:00
static int g_joypad = 0 ;
2013-04-29 13:28:41 +00:00
static int g_timeout = 0 ;
2011-01-09 14:44:35 +00:00
static char * g_in_path = NULL ;
static char * g_out_path = NULL ;
2013-04-26 11:25:40 +00:00
static char * g_auto_path = NULL ;
2013-05-04 08:22:31 +00:00
static char * g_driver = NULL ;
2013-05-01 19:56:38 +00:00
static unsigned g_meta_level = 0 ;
2011-01-09 14:44:35 +00:00
static void print_help ( void )
{
2013-04-29 19:11:29 +00:00
puts ( " ===================== " ) ;
puts ( " retroarch-joyconfig " ) ;
puts ( " ===================== " ) ;
puts ( " Usage: retroarch-joyconfig [ options ... ] " ) ;
2011-01-09 14:44:35 +00:00
puts ( " " ) ;
2013-04-26 11:25:40 +00:00
puts ( " -p/--player: Which player to configure for (1 up to and including 8). " ) ;
2011-01-15 15:50:26 +00:00
puts ( " -j/--joypad: Which joypad to use when configuring (first joypad is 0). " ) ;
2011-01-09 14:44:35 +00:00
puts ( " -i/--input: Input file to configure with. Binds will be added on or overwritten. " ) ;
puts ( " \t If not selected, an empty config will be used as a base. " ) ;
puts ( " -o/--output: Output file to write to. If not selected, config file will be dumped to stdout. " ) ;
2013-04-26 11:25:40 +00:00
puts ( " -a/--autoconfig: Outputs an autoconfig file for joypad which was configured. " ) ;
2013-04-29 19:11:29 +00:00
puts ( " -M/--allmisc: Also configure various keybinds that are not directly libretro related. " ) ;
puts ( " \t These configurations are for player 1 only. " ) ;
puts ( " -m/--misc: Same as --allmisc, but exposes a smaller subset of misc binds which are deemed most useful for regular use. " ) ;
2013-04-29 13:28:41 +00:00
puts ( " -t/--timeout: Adds a timeout of N seconds to each bind. If timed out, the bind will not be used. " ) ;
2013-05-04 08:22:31 +00:00
puts ( " -d/--driver: Uses a specific joypad driver. " ) ;
2011-01-09 14:44:35 +00:00
puts ( " -h/--help: This help. " ) ;
}
2012-09-29 19:57:03 +00:00
# define MAX_BUTTONS 32
# define MAX_AXES 32
# define MAX_HATS 32
struct poll_data
{
bool buttons [ MAX_BUTTONS ] ;
int16_t axes [ MAX_AXES ] ;
uint16_t hats [ MAX_HATS ] ;
} ;
static void poll_joypad ( const rarch_joypad_driver_t * driver ,
unsigned pad ,
struct poll_data * data )
2011-01-09 14:44:35 +00:00
{
2013-10-22 19:26:33 +00:00
unsigned i ;
2012-09-29 19:57:03 +00:00
input_joypad_poll ( driver ) ;
2013-10-22 19:26:33 +00:00
for ( i = 0 ; i < MAX_BUTTONS ; i + + )
2012-09-29 19:57:03 +00:00
data - > buttons [ i ] = input_joypad_button_raw ( driver , pad , i ) ;
2013-10-22 19:26:33 +00:00
for ( i = 0 ; i < MAX_AXES ; i + + )
2012-09-29 19:57:03 +00:00
data - > axes [ i ] = input_joypad_axis_raw ( driver , pad , i ) ;
2013-10-22 19:26:33 +00:00
for ( i = 0 ; i < MAX_HATS ; i + + )
2011-01-09 14:44:35 +00:00
{
2012-09-29 19:57:03 +00:00
uint16_t hat = 0 ;
hat | = input_joypad_hat_raw ( driver , pad , HAT_UP_MASK , i ) < < HAT_UP_SHIFT ;
hat | = input_joypad_hat_raw ( driver , pad , HAT_DOWN_MASK , i ) < < HAT_DOWN_SHIFT ;
hat | = input_joypad_hat_raw ( driver , pad , HAT_LEFT_MASK , i ) < < HAT_LEFT_SHIFT ;
hat | = input_joypad_hat_raw ( driver , pad , HAT_RIGHT_MASK , i ) < < HAT_RIGHT_SHIFT ;
data - > hats [ i ] = hat ;
2011-01-09 14:44:35 +00:00
}
2012-09-29 19:57:03 +00:00
}
2012-06-28 15:57:50 +00:00
2013-04-26 11:25:40 +00:00
static void get_binds ( config_file_t * conf , config_file_t * auto_conf , int player , int joypad )
2012-09-29 19:57:03 +00:00
{
2013-10-22 19:26:33 +00:00
int i , timeout_cnt ;
2013-05-04 08:22:31 +00:00
const rarch_joypad_driver_t * driver = input_joypad_init_driver ( g_driver ) ;
2012-09-29 19:57:03 +00:00
if ( ! driver )
2011-01-09 14:44:35 +00:00
{
2012-09-29 19:57:03 +00:00
fprintf ( stderr , " Cannot find any valid input driver. \n " ) ;
2011-01-09 14:44:35 +00:00
exit ( 1 ) ;
}
2012-09-29 19:57:03 +00:00
if ( ! driver - > query_pad ( joypad ) )
2011-01-09 14:44:35 +00:00
{
2012-09-29 19:57:03 +00:00
fprintf ( stderr , " Couldn't open joystick #%u. \n " , joypad ) ;
2011-01-09 14:44:35 +00:00
exit ( 1 ) ;
}
2012-09-29 19:57:03 +00:00
fprintf ( stderr , " Found joypad driver: %s \n " , driver - > ident ) ;
2013-04-26 11:25:40 +00:00
const char * joypad_name = input_joypad_name ( driver , joypad ) ;
fprintf ( stderr , " Using joypad: %s \n " , joypad_name ? joypad_name : " Unknown " ) ;
if ( joypad_name & & auto_conf )
2013-04-26 11:34:41 +00:00
{
2013-04-26 11:25:40 +00:00
config_set_string ( auto_conf , " input_device " , joypad_name ) ;
2013-04-26 11:34:41 +00:00
config_set_string ( auto_conf , " input_driver " , driver - > ident ) ;
}
2013-04-26 11:32:11 +00:00
2012-09-29 19:57:03 +00:00
int16_t initial_axes [ MAX_AXES ] = { 0 } ;
struct poll_data old_poll = { { 0 } } ;
struct poll_data new_poll = { { 0 } } ;
int last_axis = - 1 ;
2012-06-28 15:57:50 +00:00
bool block_axis = false ;
2013-04-29 13:28:41 +00:00
unsigned timeout_ticks = g_timeout * 100 ;
2013-04-26 11:32:11 +00:00
poll_joypad ( driver , joypad , & old_poll ) ;
2013-04-26 11:39:03 +00:00
fprintf ( stderr , " \n Joypads tend to have stale state after opened. \n Press some buttons and move some axes around to make sure joypad state is completely neutral before proceeding. \n When done, press Enter ... " ) ;
2013-04-26 11:32:11 +00:00
getchar ( ) ;
2012-09-29 19:57:03 +00:00
poll_joypad ( driver , joypad , & old_poll ) ;
2011-01-30 19:36:45 +00:00
2013-10-22 19:26:33 +00:00
for ( i = 0 ; i < MAX_AXES ; i + + )
2012-06-28 15:57:50 +00:00
{
2012-09-29 19:57:03 +00:00
int16_t initial = input_joypad_axis_raw ( driver , joypad , i ) ;
2012-06-28 15:57:50 +00:00
if ( abs ( initial ) < 20000 )
initial = 0 ;
// Certain joypads (such as XBox360 controller on Linux) has a default negative axis for shoulder triggers,
// which makes configuration very awkward.
// If default negative, we can't trigger on the negative axis, and similar with defaulted positive axes.
if ( initial )
2013-04-26 11:34:41 +00:00
fprintf ( stderr , " Axis %d is defaulted to %s axis value of %d. \n " , i , initial > 0 ? " positive " : " negative " , initial ) ;
2012-06-28 15:57:50 +00:00
initial_axes [ i ] = initial ;
}
2011-01-09 14:44:35 +00:00
2013-10-22 19:26:33 +00:00
for ( i = 0 ; i < MAX_BUTTONS ; i + + )
2013-04-26 11:39:03 +00:00
{
if ( old_poll . buttons [ i ] )
fprintf ( stderr , " Button %d was initially pressed. This indicates broken initial state. \n " , i ) ;
}
2012-09-29 19:57:03 +00:00
fprintf ( stderr , " Configuring binds for player #%d on joypad #%d. \n \n " ,
player + 1 , joypad ) ;
2011-01-09 14:44:35 +00:00
2013-10-22 19:26:33 +00:00
for ( i = 0 , timeout_cnt = 0 ; input_config_bind_map [ i ] . valid ; i + + , timeout_cnt = 0 )
2011-01-09 14:44:35 +00:00
{
2013-10-22 19:26:33 +00:00
int j ;
2013-04-26 11:25:40 +00:00
if ( i = = RARCH_TURBO_ENABLE )
continue ;
2013-04-29 19:11:29 +00:00
unsigned meta_level = input_config_bind_map [ i ] . meta ;
if ( meta_level > g_meta_level )
continue ;
2013-04-26 11:25:40 +00:00
fprintf ( stderr , " %s \n " , input_config_bind_map [ i ] . desc ) ;
2011-01-09 14:44:35 +00:00
2013-04-26 11:25:40 +00:00
unsigned player_index = input_config_bind_map [ i ] . meta ? 0 : player ;
2011-01-09 14:44:35 +00:00
2012-09-29 19:57:03 +00:00
for ( ; ; )
2011-01-09 14:44:35 +00:00
{
2012-09-29 19:57:03 +00:00
old_poll = new_poll ;
// To avoid pegging CPU.
// Ideally use an event-based joypad scheme,
// but it adds far more complexity, so, meh.
rarch_sleep ( 10 ) ;
2013-04-29 13:28:41 +00:00
if ( timeout_ticks )
{
timeout_cnt + + ;
if ( timeout_cnt > = timeout_ticks )
{
fprintf ( stderr , " \t Timed out ... \n " ) ;
break ;
}
}
2012-09-29 19:57:03 +00:00
poll_joypad ( driver , joypad , & new_poll ) ;
2013-10-22 19:26:33 +00:00
for ( j = 0 ; j < MAX_BUTTONS ; j + + )
2011-01-09 14:44:35 +00:00
{
2012-09-29 19:57:03 +00:00
if ( new_poll . buttons [ j ] & & ! old_poll . buttons [ j ] )
{
fprintf ( stderr , " \t Joybutton pressed: %u \n " , j ) ;
2013-04-26 11:25:40 +00:00
char key [ 64 ] ;
snprintf ( key , sizeof ( key ) , " %s_%s_btn " ,
input_config_get_prefix ( player_index , input_config_bind_map [ i ] . meta ) , input_config_bind_map [ i ] . base ) ;
config_set_int ( conf , key , j ) ;
if ( auto_conf )
{
snprintf ( key , sizeof ( key ) , " input_%s_btn " ,
input_config_bind_map [ i ] . base ) ;
config_set_int ( auto_conf , key , j ) ;
}
2012-09-29 19:57:03 +00:00
goto out ;
}
}
2011-01-09 14:44:35 +00:00
2013-10-22 19:26:33 +00:00
for ( j = 0 ; j < MAX_AXES ; j + + )
2012-09-29 19:57:03 +00:00
{
2013-04-26 11:25:40 +00:00
if ( new_poll . axes [ j ] = = old_poll . axes [ j ] )
continue ;
int16_t value = new_poll . axes [ j ] ;
bool same_axis = last_axis = = j ;
bool require_negative = initial_axes [ j ] > 0 ;
bool require_positive = initial_axes [ j ] < 0 ;
// Block the axis config until we're sure axes have returned to their neutral state.
if ( same_axis )
2012-06-28 15:57:50 +00:00
{
2013-04-26 11:25:40 +00:00
if ( abs ( value ) < 10000 | |
( require_positive & & value < 0 ) | |
( require_negative & & value > 0 ) )
block_axis = false ;
}
2012-06-28 15:57:50 +00:00
2013-04-26 11:25:40 +00:00
// If axes are in their neutral state, we can't allow it.
if ( require_negative & & value > = 0 )
continue ;
if ( require_positive & & value < = 0 )
continue ;
if ( block_axis )
continue ;
2012-06-28 15:57:50 +00:00
2013-04-26 11:25:40 +00:00
if ( abs ( value ) > 20000 )
{
last_axis = j ;
fprintf ( stderr , " \t Joyaxis moved: Axis %d, Value %d \n " , j , value ) ;
2012-06-28 15:57:50 +00:00
2013-04-26 11:25:40 +00:00
char buf [ 8 ] ;
snprintf ( buf , sizeof ( buf ) ,
value > 0 ? " +%d " : " -%d " , j ) ;
2012-06-28 15:57:50 +00:00
2013-04-26 11:25:40 +00:00
char key [ 64 ] ;
snprintf ( key , sizeof ( key ) , " %s_%s_axis " ,
input_config_get_prefix ( player_index , input_config_bind_map [ i ] . meta ) , input_config_bind_map [ i ] . base ) ;
2011-01-09 14:44:35 +00:00
2013-04-26 11:25:40 +00:00
config_set_string ( conf , key , buf ) ;
2012-09-29 19:57:03 +00:00
2013-04-26 11:25:40 +00:00
if ( auto_conf )
{
snprintf ( key , sizeof ( key ) , " input_%s_axis " ,
input_config_bind_map [ i ] . base ) ;
config_set_string ( auto_conf , key , buf ) ;
2011-01-09 14:44:35 +00:00
}
2013-04-26 11:25:40 +00:00
block_axis = true ;
goto out ;
2012-06-28 15:57:50 +00:00
}
2012-09-29 19:57:03 +00:00
}
2011-01-09 14:44:35 +00:00
2013-10-22 19:26:33 +00:00
for ( j = 0 ; j < MAX_HATS ; j + + )
2012-09-29 19:57:03 +00:00
{
const char * quark = NULL ;
uint16_t value = new_poll . hats [ j ] ;
uint16_t old_value = old_poll . hats [ j ] ;
if ( ( value & HAT_UP_MASK ) & & ! ( old_value & HAT_UP_MASK ) )
quark = " up " ;
else if ( ( value & HAT_LEFT_MASK ) & & ! ( old_value & HAT_LEFT_MASK ) )
quark = " left " ;
else if ( ( value & HAT_RIGHT_MASK ) & & ! ( old_value & HAT_RIGHT_MASK ) )
quark = " right " ;
else if ( ( value & HAT_DOWN_MASK ) & & ! ( old_value & HAT_DOWN_MASK ) )
quark = " down " ;
if ( quark )
{
fprintf ( stderr , " \t Joyhat moved: Hat %d, direction %s \n " , j , quark ) ;
2011-01-09 14:44:35 +00:00
char buf [ 16 ] ;
2012-09-29 19:57:03 +00:00
snprintf ( buf , sizeof ( buf ) , " h%d%s " , j , quark ) ;
2013-04-26 11:25:40 +00:00
char key [ 64 ] ;
snprintf ( key , sizeof ( key ) , " %s_%s_btn " ,
input_config_get_prefix ( player_index , input_config_bind_map [ i ] . meta ) , input_config_bind_map [ i ] . base ) ;
config_set_string ( conf , key , buf ) ;
if ( auto_conf )
{
snprintf ( key , sizeof ( key ) , " input_%s_btn " ,
input_config_bind_map [ i ] . base ) ;
config_set_string ( auto_conf , key , buf ) ;
}
2012-09-29 19:57:03 +00:00
goto out ;
}
2011-01-09 14:44:35 +00:00
}
}
2012-09-29 19:57:03 +00:00
out :
old_poll = new_poll ;
2011-01-09 14:44:35 +00:00
}
}
static void parse_input ( int argc , char * argv [ ] )
{
2013-05-04 08:22:31 +00:00
char optstring [ ] = " i:o:a:p:j:t:hmMd: " ;
2011-01-09 14:44:35 +00:00
struct option opts [ ] = {
{ " input " , 1 , NULL , ' i ' } ,
{ " output " , 1 , NULL , ' o ' } ,
2013-04-26 11:25:40 +00:00
{ " autoconfig " , 1 , NULL , ' a ' } ,
2011-01-09 14:44:35 +00:00
{ " player " , 1 , NULL , ' p ' } ,
{ " joypad " , 1 , NULL , ' j ' } ,
{ " help " , 0 , NULL , ' h ' } ,
2011-01-30 19:36:45 +00:00
{ " misc " , 0 , NULL , ' m ' } ,
2013-04-29 19:11:29 +00:00
{ " allmisc " , 0 , NULL , ' M ' } ,
2013-04-29 13:28:41 +00:00
{ " timeout " , 1 , NULL , ' t ' } ,
2013-05-04 08:22:31 +00:00
{ " driver " , 1 , NULL , ' d ' } ,
2011-01-09 14:44:35 +00:00
{ NULL , 0 , NULL , 0 }
} ;
int option_index = 0 ;
2011-10-15 10:56:48 +00:00
for ( ; ; )
2011-01-09 14:44:35 +00:00
{
int c = getopt_long ( argc , argv , optstring , opts , & option_index ) ;
if ( c = = - 1 )
break ;
switch ( c )
{
case ' h ' :
print_help ( ) ;
exit ( 0 ) ;
case ' i ' :
g_in_path = strdup ( optarg ) ;
break ;
2013-04-29 13:28:41 +00:00
case ' t ' :
g_timeout = strtol ( optarg , NULL , 0 ) ;
break ;
2013-05-04 08:22:31 +00:00
case ' d ' :
g_driver = strdup ( optarg ) ;
break ;
2011-01-09 14:44:35 +00:00
case ' o ' :
g_out_path = strdup ( optarg ) ;
break ;
2013-04-26 11:25:40 +00:00
case ' a ' :
g_auto_path = strdup ( optarg ) ;
break ;
2011-01-30 19:36:45 +00:00
case ' m ' :
2013-04-29 19:11:29 +00:00
g_meta_level = 1 ;
break ;
case ' M ' :
g_meta_level = 2 ;
2011-01-30 19:36:45 +00:00
break ;
2011-01-09 14:44:35 +00:00
case ' j ' :
g_joypad = strtol ( optarg , NULL , 0 ) ;
2011-01-15 15:50:26 +00:00
if ( g_joypad < 0 )
2011-01-09 14:44:35 +00:00
{
2012-02-11 20:11:36 +00:00
fprintf ( stderr , " Joypad number can't be negative. \n " ) ;
2011-01-09 14:44:35 +00:00
exit ( 1 ) ;
}
break ;
case ' p ' :
g_player = strtol ( optarg , NULL , 0 ) ;
if ( g_player < 1 )
{
2012-02-11 20:11:36 +00:00
fprintf ( stderr , " Player number must be at least 1. \n " ) ;
2011-01-09 14:44:35 +00:00
exit ( 1 ) ;
}
2011-01-10 15:53:37 +00:00
else if ( g_player > MAX_PLAYERS )
2011-01-09 14:44:35 +00:00
{
2011-01-10 15:53:37 +00:00
fprintf ( stderr , " Player number must be from 1 to %d. \n " , MAX_PLAYERS ) ;
2011-01-09 14:44:35 +00:00
exit ( 1 ) ;
}
break ;
default :
break ;
}
}
if ( optind < argc )
{
print_help ( ) ;
exit ( 1 ) ;
}
}
2012-01-05 16:53:30 +00:00
// Need SDL_main on OSX.
# ifndef __APPLE__
# undef main
2011-01-09 18:37:55 +00:00
# endif
2012-01-05 16:53:30 +00:00
int main ( int argc , char * argv [ ] )
2011-01-09 14:44:35 +00:00
{
parse_input ( argc , argv ) ;
config_file_t * conf = config_file_new ( g_in_path ) ;
2011-01-10 07:55:34 +00:00
if ( ! conf )
{
fprintf ( stderr , " Couldn't open config file ... \n " ) ;
return 1 ;
}
2011-01-10 15:53:37 +00:00
const char * index_list [ ] = {
" input_player1_joypad_index " ,
" input_player2_joypad_index " ,
" input_player3_joypad_index " ,
" input_player4_joypad_index " ,
2012-04-21 21:31:57 +00:00
" input_player5_joypad_index " ,
" input_player6_joypad_index " ,
" input_player7_joypad_index " ,
" input_player8_joypad_index " ,
2011-01-10 15:53:37 +00:00
} ;
2011-01-15 15:50:26 +00:00
config_set_int ( conf , index_list [ g_player - 1 ] , g_joypad ) ;
2011-01-10 07:55:34 +00:00
2013-04-26 11:25:40 +00:00
config_file_t * auto_conf = NULL ;
if ( g_auto_path )
auto_conf = config_file_new ( NULL ) ;
get_binds ( conf , auto_conf , g_player - 1 , g_joypad ) ;
2011-01-09 14:44:35 +00:00
config_file_write ( conf , g_out_path ) ;
config_file_free ( conf ) ;
2013-04-26 11:25:40 +00:00
if ( auto_conf )
{
fprintf ( stderr , " Writing autoconfig profile to: %s. \n " , g_auto_path ) ;
config_file_write ( auto_conf , g_auto_path ) ;
config_file_free ( auto_conf ) ;
}
free ( g_in_path ) ;
free ( g_out_path ) ;
free ( g_auto_path ) ;
2011-01-09 16:24:13 +00:00
return 0 ;
2011-01-09 14:44:35 +00:00
}