RetroArch/tools/retroarch-joyconfig.c
Jose Ernesto Ramirez Ramirez 09f3c3ca77 Fixed nunchuk controller support, it was not correctly detected.
Fixed classic controller's analogs, were not working at all.
Restored sixaxis controller support, now with completely rewroten library.
Changed detection order (GC, Sixaxis, Wiimote/Nunchuck/Classic, left wiimote at the end, otherwise we needed to disconnect it to be able to use GC controller or sixaxis controller.
2015-07-13 22:58:01 -05:00

539 lines
15 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
*
* RetroArch 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 Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch 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 RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <file/config_file.h>
#include <stdio.h>
#include <stdlib.h>
#include <compat/getopt.h>
#include <compat/posix_string.h>
#include <boolean.h>
#include "../input/input_common.h"
#include "../input/input_joypad.h"
#include "../input/input_autodetect.h"
#include "../general.h"
#include "../runloop.h"
#include <assert.h>
/* 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_config;
struct global g_extern;
driver_t driver;
static int g_player = 1;
static int g_joypad = 0;
static int g_timeout = 0;
static char *g_in_path = NULL;
static char *g_out_path = NULL;
static char *g_auto_path = NULL;
static char *g_driver = NULL;
static unsigned g_meta_level = 0;
bool rarch_main_verbosity(void)
{
return true;
}
settings_t *config_get_ptr(void)
{
return &g_config;
}
driver_t *driver_get_ptr(void)
{
return &driver;
}
static void print_help(void)
{
puts("=====================");
puts(" retroarch-joyconfig");
puts("=====================");
puts("Usage: retroarch-joyconfig [ options ... ]");
puts("");
puts("-p/--player: Which player to configure for (1 up to and including 8).");
puts("-j/--joypad: Which joypad to use when configuring (first joypad is 0).");
puts("-i/--input: Input file to configure with. Binds will be added on or overwritten.");
puts("\tIf 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.");
puts("-a/--autoconfig: Outputs an autoconfig file for joypad which was configured.");
puts("-M/--allmisc: Also configure various keybinds that are not directly libretro related.");
puts("\tThese 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.");
puts("-t/--timeout: Adds a timeout of N seconds to each bind. If timed out, the bind will not be used.");
puts("-d/--driver: Uses a specific joypad driver.");
puts("-h/--help: This help.");
}
#define MAX_BUTTONS 64
#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 input_device_driver_t *driver,
unsigned pad,
struct poll_data *data)
{
unsigned i;
if (driver)
driver->poll();
for (i = 0; i < MAX_BUTTONS; i++)
data->buttons[i] = input_joypad_button_raw(driver, pad, i);
for (i = 0; i < MAX_AXES; i++)
data->axes[i] = input_joypad_axis_raw(driver, pad, i);
for (i = 0; i < MAX_HATS; i++)
{
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;
}
}
static void get_binds(config_file_t *conf, config_file_t *auto_conf,
int player, int joypad)
{
int i, timeout_cnt;
const input_device_driver_t *driver = input_joypad_init_driver(g_driver, NULL);
const char *joypad_name;
int16_t initial_axes[MAX_AXES] = {0};
struct poll_data old_poll = {{0}};
struct poll_data new_poll = {{0}};
int last_axis = -1;
bool block_axis = false;
int timeout_ticks = g_timeout * 100;
if (!driver)
{
fprintf(stderr, "Cannot find any valid input driver.\n");
exit(1);
}
if (!driver->query_pad(joypad))
{
fprintf(stderr, "Couldn't open joystick #%d.\n", joypad);
exit(1);
}
fprintf(stderr, "Found joypad driver: %s\n", driver->ident);
joypad_name = input_joypad_name(driver, joypad);
fprintf(stderr, "Using joypad: %s\n", joypad_name ? joypad_name : "Unknown");
if (joypad_name && auto_conf)
{
config_set_string(auto_conf, "input_device", joypad_name);
config_set_string(auto_conf, "input_driver", driver->ident);
}
poll_joypad(driver, joypad, &old_poll);
fprintf(stderr, "\nJoypads tend to have stale state after opened.\nPress some buttons and move some axes around to make sure joypad state is completely neutral before proceeding.\nWhen done, press Enter ... ");
getchar();
poll_joypad(driver, joypad, &old_poll);
for (i = 0; i < MAX_AXES; i++)
{
int16_t initial = input_joypad_axis_raw(driver, joypad, i);
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)
fprintf(stderr, "Axis %d is defaulted to %s axis value of %d.\n", i, initial > 0 ? "positive" : "negative", initial);
initial_axes[i] = initial;
}
for (i = 0; i < MAX_BUTTONS; i++)
{
if (old_poll.buttons[i])
fprintf(stderr, "Button %d was initially pressed. This indicates broken initial state.\n", i);
}
fprintf(stderr, "Configuring binds for player #%d on joypad #%d.\n\n",
player + 1, joypad);
for (i = 0, timeout_cnt = 0; input_config_bind_map[i].valid; i++, timeout_cnt = 0)
{
unsigned meta_level;
unsigned player_index;
int j;
if (i == RARCH_TURBO_ENABLE)
continue;
meta_level = input_config_bind_map[i].meta;
if (meta_level > g_meta_level)
continue;
fprintf(stderr, "%s\n", input_config_bind_map[i].desc);
player_index = input_config_bind_map[i].meta ? 0 : player;
for (;;)
{
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);
if (timeout_ticks)
{
timeout_cnt++;
if (timeout_cnt >= timeout_ticks)
{
fprintf(stderr, "\tTimed out ...\n");
break;
}
}
poll_joypad(driver, joypad, &new_poll);
for (j = 0; j < MAX_BUTTONS; j++)
{
if (new_poll.buttons[j] && !old_poll.buttons[j])
{
char key[64] = {0};
fprintf(stderr, "\tJoybutton pressed: %d\n", j);
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);
}
goto out;
}
}
for (j = 0; j < MAX_AXES; j++)
{
int16_t value;
bool same_axis;
bool require_negative;
bool require_positive;
if (new_poll.axes[j] == old_poll.axes[j])
continue;
value = new_poll.axes[j];
same_axis = last_axis == j;
require_negative = initial_axes[j] > 0;
require_positive = initial_axes[j] < 0;
/* Block the axis config until we're sure
* axes have returned to their neutral state. */
if (same_axis)
{
if (abs(value) < 10000 ||
(require_positive && value < 0) ||
(require_negative && value > 0))
block_axis = false;
}
/* 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;
if (abs(value) > 20000)
{
char buf[8] = {0};
char key[64] = {0};
last_axis = j;
fprintf(stderr, "\tJoyaxis moved: Axis %d, Value %d\n",
j, value);
snprintf(buf, sizeof(buf),
value > 0 ? "+%d" : "-%d", j);
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);
config_set_string(conf, key, buf);
if (auto_conf)
{
snprintf(key, sizeof(key), "input_%s_axis",
input_config_bind_map[i].base);
config_set_string(auto_conf, key, buf);
}
block_axis = true;
goto out;
}
}
for (j = 0; j < MAX_HATS; j++)
{
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)
{
char buf[16] = {0};
char key[64] = {0};
fprintf(stderr, "\tJoyhat moved: Hat %d, direction %s\n", j, quark);
snprintf(buf, sizeof(buf), "h%d%s", j, quark);
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);
}
goto out;
}
}
}
out:
old_poll = new_poll;
}
}
static void parse_input(int argc, char *argv[])
{
char optstring[] = "i:o:a:p:j:t:hmMd:";
struct option opts[] = {
{ "input", 1, NULL, 'i' },
{ "output", 1, NULL, 'o' },
{ "autoconfig", 1, NULL, 'a' },
{ "player", 1, NULL, 'p' },
{ "joypad", 1, NULL, 'j' },
{ "help", 0, NULL, 'h' },
{ "misc", 0, NULL, 'm' },
{ "allmisc", 0, NULL, 'M' },
{ "timeout", 1, NULL, 't' },
{ "driver", 1, NULL, 'd' },
{ NULL, 0, NULL, 0 }
};
int option_index = 0;
for (;;)
{
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;
case 't':
g_timeout = strtol(optarg, NULL, 0);
break;
case 'd':
g_driver = strdup(optarg);
break;
case 'o':
g_out_path = strdup(optarg);
break;
case 'a':
g_auto_path = strdup(optarg);
break;
case 'm':
g_meta_level = 1;
break;
case 'M':
g_meta_level = 2;
break;
case 'j':
g_joypad = strtol(optarg, NULL, 0);
if (g_joypad < 0)
{
fprintf(stderr, "Joypad number can't be negative.\n");
exit(1);
}
break;
case 'p':
g_player = strtol(optarg, NULL, 0);
if (g_player < 1)
{
fprintf(stderr, "User number must be at least 1.\n");
exit(1);
}
else if (g_player > MAX_USERS)
{
fprintf(stderr, "User number must be from 1 to %d.\n",
MAX_USERS);
exit(1);
}
break;
default:
break;
}
}
if (optind < argc)
{
print_help();
exit(1);
}
}
bool input_config_autoconfigure_joypad(autoconfig_params_t *params)
{
(void)params;
return false;
}
/* Need SDL_main on OSX. */
#ifndef __APPLE__
#undef main
#endif
int main(int argc, char *argv[])
{
config_file_t *conf;
config_file_t *auto_conf = NULL;
const char *index_list[] = {
"input_player1_joypad_index",
"input_player2_joypad_index",
"input_player3_joypad_index",
"input_player4_joypad_index",
"input_player5_joypad_index",
"input_player6_joypad_index",
"input_player7_joypad_index",
"input_player8_joypad_index",
};
parse_input(argc, argv);
conf = config_file_new(g_in_path);
if (!conf)
{
fprintf(stderr, "Couldn't open config file ...\n");
return 1;
}
config_set_int(conf, index_list[g_player - 1], g_joypad);
if (g_auto_path)
auto_conf = config_file_new(NULL);
get_binds(conf, auto_conf, g_player - 1, g_joypad);
config_file_write(conf, g_out_path);
config_file_free(conf);
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);
return 0;
}
void rarch_main_msg_queue_push(const char *msg, unsigned prio, unsigned duration,
bool flush)
{
}
bool video_driver_viewport_info(struct video_viewport *vp)
{
return false;
}
bool video_driver_read_viewport(uint8_t *buffer)
{
return false;
}
void input_config_autoconfigure_disconnect(unsigned id, const char *msg)
{
}