Begin implementing SET_CONTROLLER_INFO.

This commit is contained in:
Themaister 2014-04-12 13:22:24 +02:00
parent 063bf83a5f
commit b4eaf81fed
7 changed files with 157 additions and 113 deletions

View File

@ -133,31 +133,17 @@ If set explicitly here, overrides config file for that port.
Disconnects an input device from port number PORT. Possible values for PORT are 1 to 8. This may be needed for some odd games to run properly. Disconnects an input device from port number PORT. Possible values for PORT are 1 to 8. This may be needed for some odd games to run properly.
If set explicitly here, overrides config file for that port. If set explicitly here, overrides config file for that port.
.TP
\fB--scope, -p\fR
Connects a Super Scope into port 2 of an emulated SNES. It is controlled with your mouse.
If set explicitly here, overrides config file for that port.
.TP
\fB--justifier, -j\fR
Connects a Konami Justifier into port 2 of an emulated SNES. It is controlled with your mouse.
If set explicitly here, overrides config file for that port.
.TP
\fB--justifiers, -J\fR
Connects two Konami Justifier into port 2 of an emulated SNES. Currently, only player 1 is controlled with the mouse.
If set explicitly here, overrides config file for that port.
.TP
\fB--multitap, -4\fR
Connects a four-way multitap into an emulated SNES. This allows for up to 5 players.
If set explicitly here, overrides config file for that port.
.TP .TP
\fB--dualanalog PORT, -A PORT\fR \fB--dualanalog PORT, -A PORT\fR
Connects a DualAnalog controller into port PORT. Possible values are 1 to 8. Connects a DualAnalog controller into port PORT. Possible values are 1 to 8.
If set explicitly here, overrides config file for that port. If set explicitly here, overrides config file for that port.
.TP
\fB--device PORT:ID, -d PORT:ID\fR
Connects a generic input device ID into port PORT. Possible values for port are 1 to 8.
If set explicitly here, overrides config file for that port.
ID is an unsigned number corresponding to the device for a libretro core.
.TP .TP
\fB--record PATH, -r PATH\fR \fB--record PATH, -r PATH\fR
Activates video recording of gameplay into PATH. Using .mkv extension is recommended. Activates video recording of gameplay into PATH. Using .mkv extension is recommended.

View File

@ -254,6 +254,18 @@ const struct retro_subsystem_info *libretro_find_subsystem_info(const struct ret
return NULL; return NULL;
} }
const struct retro_controller_description *libretro_find_controller_description(const struct retro_controller_info *info, unsigned id)
{
unsigned i;
for (i = 0; i < info->num_types; i++)
{
if (info->types[i].id == id)
return &info->types[i];
}
return NULL;
}
static void load_symbols(bool is_dummy) static void load_symbols(bool is_dummy)
{ {
if (is_dummy) if (is_dummy)
@ -434,6 +446,7 @@ void uninit_libretro_sym(void)
// No longer valid. // No longer valid.
free(g_extern.system.special); free(g_extern.system.special);
free(g_extern.system.ports);
memset(&g_extern.system, 0, sizeof(g_extern.system)); memset(&g_extern.system, 0, sizeof(g_extern.system));
#ifdef HAVE_CAMERA #ifdef HAVE_CAMERA
g_extern.camera_active = false; g_extern.camera_active = false;
@ -907,16 +920,6 @@ bool rarch_environment_cb(unsigned cmd, void *data)
break; break;
} }
// Private extensions for internal use, not part of libretro API.
case RETRO_ENVIRONMENT_SET_LIBRETRO_PATH:
RARCH_LOG("Environ (Private) SET_LIBRETRO_PATH.\n");
if (path_file_exists((const char*)data))
strlcpy(g_settings.libretro, (const char*)data, sizeof(g_settings.libretro));
else
return false;
break;
case RETRO_ENVIRONMENT_GET_CONTENT_DIRECTORY: case RETRO_ENVIRONMENT_GET_CONTENT_DIRECTORY:
{ {
const char **dir = (const char**)data; const char **dir = (const char**)data;
@ -959,6 +962,38 @@ bool rarch_environment_cb(unsigned cmd, void *data)
break; break;
} }
case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO:
{
RARCH_LOG("Environ SET_CONTROLLER_INFO.\n");
unsigned i, j;
const struct retro_controller_info *info = (const struct retro_controller_info*)data;
for (i = 0; info[i].types; i++)
{
RARCH_LOG("Controller port: %u\n", i);
for (j = 0; j < info[i].num_types; j++)
RARCH_LOG(" %s (ident: %s, ID: %u)\n", info[i].types[j].desc, info[i].types[j].ident, info[i].types[j].id);
}
free(g_extern.system.ports);
g_extern.system.ports = (struct retro_controller_info*)calloc(i, sizeof(*g_extern.system.ports));
if (!g_extern.system.ports)
return false;
memcpy(g_extern.system.ports, info, i * sizeof(*g_extern.system.ports));
g_extern.system.num_ports = i;
break;
}
// Private extensions for internal use, not part of libretro API.
case RETRO_ENVIRONMENT_SET_LIBRETRO_PATH:
RARCH_LOG("Environ (Private) SET_LIBRETRO_PATH.\n");
if (path_file_exists((const char*)data))
strlcpy(g_settings.libretro, (const char*)data, sizeof(g_settings.libretro));
else
return false;
break;
case RETRO_ENVIRONMENT_EXEC: case RETRO_ENVIRONMENT_EXEC:
case RETRO_ENVIRONMENT_EXEC_ESCAPE: case RETRO_ENVIRONMENT_EXEC_ESCAPE:

View File

@ -65,6 +65,7 @@ void libretro_free_system_info(struct retro_system_info *info);
void libretro_get_current_core_pathname(char *name, size_t size); void libretro_get_current_core_pathname(char *name, size_t size);
const struct retro_subsystem_info *libretro_find_subsystem_info(const struct retro_subsystem_info *info, unsigned num_info, const char *ident); const struct retro_subsystem_info *libretro_find_subsystem_info(const struct retro_subsystem_info *info, unsigned num_info, const char *ident);
const struct retro_controller_description *libretro_find_controller_description(const struct retro_controller_info *info, unsigned id);
extern void (*pretro_init)(void); extern void (*pretro_init)(void);
extern void (*pretro_deinit)(void); extern void (*pretro_deinit)(void);

View File

@ -934,10 +934,6 @@ int menu_set_settings(void *data, unsigned setting, unsigned action)
RETRO_DEVICE_JOYPAD, RETRO_DEVICE_JOYPAD,
RETRO_DEVICE_ANALOG, RETRO_DEVICE_ANALOG,
RETRO_DEVICE_MOUSE, RETRO_DEVICE_MOUSE,
RETRO_DEVICE_JOYPAD_MULTITAP,
RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE,
RETRO_DEVICE_LIGHTGUN_JUSTIFIER,
RETRO_DEVICE_LIGHTGUN_JUSTIFIERS,
}; };
unsigned current_device, current_index, i; unsigned current_device, current_index, i;
@ -2211,11 +2207,7 @@ void menu_set_settings_label(char *type_str, size_t type_str_size, unsigned *w,
case RETRO_DEVICE_NONE: name = "None"; break; case RETRO_DEVICE_NONE: name = "None"; break;
case RETRO_DEVICE_JOYPAD: name = "Joypad"; break; case RETRO_DEVICE_JOYPAD: name = "Joypad"; break;
case RETRO_DEVICE_ANALOG: name = "Joypad w/ Analog"; break; case RETRO_DEVICE_ANALOG: name = "Joypad w/ Analog"; break;
case RETRO_DEVICE_JOYPAD_MULTITAP: name = "Multitap"; break;
case RETRO_DEVICE_MOUSE: name = "Mouse"; break; case RETRO_DEVICE_MOUSE: name = "Mouse"; break;
case RETRO_DEVICE_LIGHTGUN_JUSTIFIER: name = "Justifier"; break;
case RETRO_DEVICE_LIGHTGUN_JUSTIFIERS: name = "Justifiers"; break;
case RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE: name = "SuperScope"; break;
default: name = "Unknown"; break; default: name = "Unknown"; break;
} }

View File

@ -449,6 +449,9 @@ struct global
struct retro_subsystem_info *special; struct retro_subsystem_info *special;
unsigned num_special; unsigned num_special;
struct retro_controller_info *ports;
unsigned num_ports;
} system; } system;
struct struct

View File

@ -46,8 +46,22 @@ extern "C" {
// It is not incremented for compatible changes to the API. // It is not incremented for compatible changes to the API.
#define RETRO_API_VERSION 1 #define RETRO_API_VERSION 1
// Libretro's fundamental device abstractions. //
#define RETRO_DEVICE_MASK 0xff // Libretros fundamental device abstractions.
/////////
//
// Libretros input system consists of some standardized device types such as a joypad (with/without analog),
// mouse, keyboard, lightgun and a pointer. The functionality of these devices are fixed, and individual cores map
// their own concept of a controller to libretros abstractions.
// This makes it possible for frontends to map the abstract types to a real input device,
// and not having to worry about binding input correctly to arbitrary controller layouts.
#define RETRO_DEVICE_TYPE_SHIFT 8
#define RETRO_DEVICE_MASK ((1 << RETRO_DEVICE_TYPE_SHIFT) - 1)
#define RETRO_DEVICE_SUBCLASS(base, id) (((id + 1) << RETRO_DEVICE_TYPE_SHIFT) | base)
// Input disabled.
#define RETRO_DEVICE_NONE 0 #define RETRO_DEVICE_NONE 0
// The JOYPAD is called RetroPad. It is essentially a Super Nintendo controller, // The JOYPAD is called RetroPad. It is essentially a Super Nintendo controller,
@ -62,6 +76,7 @@ extern "C" {
// KEYBOARD device lets one poll for raw key pressed. // KEYBOARD device lets one poll for raw key pressed.
// It is poll based, so input callback will return with the current pressed state. // It is poll based, so input callback will return with the current pressed state.
// For event/text based keyboard input, see RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK.
#define RETRO_DEVICE_KEYBOARD 3 #define RETRO_DEVICE_KEYBOARD 3
// Lightgun X/Y coordinates are reported relatively to last poll, similar to mouse. // Lightgun X/Y coordinates are reported relatively to last poll, similar to mouse.
@ -87,7 +102,7 @@ extern "C" {
// //
// To check if the pointer coordinates are valid (e.g. a touch display actually being touched), // To check if the pointer coordinates are valid (e.g. a touch display actually being touched),
// PRESSED returns 1 or 0. // PRESSED returns 1 or 0.
// If using a mouse, PRESSED will usually correspond to the left mouse button. // If using a mouse on a desktop, PRESSED will usually correspond to the left mouse button, but this is a frontend decision.
// PRESSED will only return 1 if the pointer is inside the game screen. // PRESSED will only return 1 if the pointer is inside the game screen.
// //
// For multi-touch, the index variable can be used to successively query more presses. // For multi-touch, the index variable can be used to successively query more presses.
@ -96,16 +111,6 @@ extern "C" {
// Eventually _PRESSED will return false for an index. No further presses are registered at this point. // Eventually _PRESSED will return false for an index. No further presses are registered at this point.
#define RETRO_DEVICE_POINTER 6 #define RETRO_DEVICE_POINTER 6
// These device types are specializations of the base types above.
// They should only be used in retro_set_controller_type() to inform libretro implementations
// about use of a very specific device type.
//
// In input state callback, however, only the base type should be used in the 'device' field.
#define RETRO_DEVICE_JOYPAD_MULTITAP ((1 << 8) | RETRO_DEVICE_JOYPAD)
#define RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE ((1 << 8) | RETRO_DEVICE_LIGHTGUN)
#define RETRO_DEVICE_LIGHTGUN_JUSTIFIER ((2 << 8) | RETRO_DEVICE_LIGHTGUN)
#define RETRO_DEVICE_LIGHTGUN_JUSTIFIERS ((3 << 8) | RETRO_DEVICE_LIGHTGUN)
// Buttons for the RetroPad (JOYPAD). // Buttons for the RetroPad (JOYPAD).
// The placement of these is equivalent to placements on the Super Nintendo controller. // The placement of these is equivalent to placements on the Super Nintendo controller.
// L2/R2/L3/R3 buttons correspond to the PS1 DualShock. // L2/R2/L3/R3 buttons correspond to the PS1 DualShock.
@ -611,7 +616,47 @@ enum retro_mod
// and this environment call allows a libretro core to expose which subsystems are supported for use with retro_load_game_special(). // and this environment call allows a libretro core to expose which subsystems are supported for use with retro_load_game_special().
// A core passes an array of retro_game_special_info which is terminated with a zeroed out retro_game_special_info struct. // A core passes an array of retro_game_special_info which is terminated with a zeroed out retro_game_special_info struct.
// //
// If a core wants to use this functionality, SET_SPECIAL_GAME_TYPES **MUST** be called from within retro_set_environment(). // If a core wants to use this functionality, SET_SUBSYSTEM_INFO **MUST** be called from within retro_set_environment().
//
#define RETRO_ENVIRONMENT_SET_CONTROLLER_INFO 35
// const struct retro_controller_info * --
// This environment call lets a libretro core tell the frontend which
// controller types are recognized in calls to retro_set_controller_port_device().
//
// Some emulators such as Super Nintendo
// support multiple lightgun types which must be specifically selected from.
// It is therefore sometimes necessary for a frontend to be able to tell
// the core about a special kind of input device which is not covered by the
// libretro input API.
//
// In order for a frontend to understand the workings of an input device,
// it must be a specialized type
// of the generic device types already defined in the libretro API.
//
// Which devices are supported can vary per input port.
// The core must pass an array of const struct retro_controller_info which is terminated with
// a blanked out struct. Each element of the struct corresponds to an ascending port index to retro_set_controller_port_device().
// Even if special device types are set in the libretro core, libretro should only poll input based on the base input device types.
struct retro_controller_description
{
// Human-readable description of the controller. Even if using a generic input device type, this can be
// set to the particular device type the core uses.
const char *desc;
// A computer-friendly short string identifier ([a-z]).
const char *ident;
// Device type passed to retro_set_controller_port_device(). If the device type is a sub-class of a generic input device type,
// use the RETRO_DEVICE_SUBCLASS macro to create an ID. E.g. RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1).
unsigned id;
};
struct retro_controller_info
{
const struct retro_controller_description *types;
unsigned num_types;
};
struct retro_subsystem_memory_info struct retro_subsystem_memory_info
{ {
@ -1208,6 +1253,8 @@ void retro_get_system_info(struct retro_system_info *info);
void retro_get_system_av_info(struct retro_system_av_info *info); void retro_get_system_av_info(struct retro_system_av_info *info);
// Sets device to be used for player 'port'. // Sets device to be used for player 'port'.
// By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all available ports.
// Setting a particular device type is not a guarantee that libretro cores will only poll input based on that particular device type. It is only a hint to the libretro core when a core cannot automatically detect the appropriate input device type on its own. It is also relevant when a core can change its behavior depending on device type.
void retro_set_controller_port_device(unsigned port, unsigned device); void retro_set_controller_port_device(unsigned port, unsigned device);
// Resets the current game. // Resets the current game.

View File

@ -783,11 +783,9 @@ static void print_help(void)
printf("\t-N/--nodevice: Disconnects controller device connected to port (1 to %d).\n", MAX_PLAYERS); printf("\t-N/--nodevice: Disconnects controller device connected to port (1 to %d).\n", MAX_PLAYERS);
printf("\t-A/--dualanalog: Connect a DualAnalog controller to port (1 to %d).\n", MAX_PLAYERS); printf("\t-A/--dualanalog: Connect a DualAnalog controller to port (1 to %d).\n", MAX_PLAYERS);
printf("\t-m/--mouse: Connect a mouse into port of the device (1 to %d).\n", MAX_PLAYERS); printf("\t-m/--mouse: Connect a mouse into controller port (1 to %d).\n", MAX_PLAYERS);
puts("\t-p/--scope: Connect a virtual SuperScope into port 2. (SNES specific)."); printf("\t-d/--device: Connect a generic device into port of the device (1 to %d).\n", MAX_PLAYERS);
puts("\t-j/--justifier: Connect a virtual Konami Justifier into port 2. (SNES specific)."); puts("\t\tFormat is port:ID, where ID is an unsigned number corresponding to the particular device.\n");
puts("\t-J/--justifiers: Daisy chain two virtual Konami Justifiers into port 2. (SNES specific).");
puts("\t-4/--multitap: Connect a SNES multitap to port 2. (SNES specific).");
#ifdef HAVE_BSV_MOVIE #ifdef HAVE_BSV_MOVIE
puts("\t-P/--bsvplay: Playback a BSV movie file."); puts("\t-P/--bsvplay: Playback a BSV movie file.");
@ -933,12 +931,9 @@ static void parse_input(int argc, char *argv[])
{ "appendconfig", 1, &val, 'C' }, { "appendconfig", 1, &val, 'C' },
{ "mouse", 1, NULL, 'm' }, { "mouse", 1, NULL, 'm' },
{ "nodevice", 1, NULL, 'N' }, { "nodevice", 1, NULL, 'N' },
{ "scope", 0, NULL, 'p' },
{ "justifier", 0, NULL, 'j' },
{ "justifiers", 0, NULL, 'J' },
{ "dualanalog", 1, NULL, 'A' }, { "dualanalog", 1, NULL, 'A' },
{ "device", 1, NULL, 'd' },
{ "savestate", 1, NULL, 'S' }, { "savestate", 1, NULL, 'S' },
{ "multitap", 0, NULL, '4' },
#ifdef HAVE_BSV_MOVIE #ifdef HAVE_BSV_MOVIE
{ "bsvplay", 1, NULL, 'P' }, { "bsvplay", 1, NULL, 'P' },
{ "bsvrecord", 1, NULL, 'R' }, { "bsvrecord", 1, NULL, 'R' },
@ -989,7 +984,7 @@ static void parse_input(int argc, char *argv[])
#define BSV_MOVIE_ARG #define BSV_MOVIE_ARG
#endif #endif
const char *optstring = "hs:fvS:m:p4jJA:c:U:DN:" BSV_MOVIE_ARG NETPLAY_ARG DYNAMIC_ARG FFMPEG_RECORD_ARG; const char *optstring = "hs:fvS:m:A:c:U:DN:d:" BSV_MOVIE_ARG NETPLAY_ARG DYNAMIC_ARG FFMPEG_RECORD_ARG;
for (;;) for (;;)
{ {
@ -1006,25 +1001,28 @@ static void parse_input(int argc, char *argv[])
print_help(); print_help();
exit(0); exit(0);
case '4':
g_settings.input.libretro_device[1] = RETRO_DEVICE_JOYPAD_MULTITAP;
g_extern.has_set_libretro_device[1] = true;
break;
case 'j':
g_settings.input.libretro_device[1] = RETRO_DEVICE_LIGHTGUN_JUSTIFIER;
g_extern.has_set_libretro_device[1] = true;
break;
case 'J':
g_settings.input.libretro_device[1] = RETRO_DEVICE_LIGHTGUN_JUSTIFIERS;
g_extern.has_set_libretro_device[1] = true;
break;
case 'Z': case 'Z':
strlcpy(g_extern.subsystem, optarg, sizeof(g_extern.subsystem)); strlcpy(g_extern.subsystem, optarg, sizeof(g_extern.subsystem));
break; break;
case 'd':
{
struct string_list *list = string_split(optarg, ":");
port = (list && list->size == 2) ? strtol(list->elems[0].data, NULL, 0) : 0;
unsigned id = (list && list->size == 2) ? strtoul(list->elems[1].data, NULL, 0) : 0;
string_list_free(list);
if (port < 1 || port > MAX_PLAYERS)
{
RARCH_ERR("Connect device to a valid port.\n");
print_help();
rarch_fail(1, "parse_input()");
}
g_settings.input.libretro_device[port - 1] = id;
g_extern.has_set_libretro_device[port - 1] = true;
break;
}
case 'A': case 'A':
port = strtol(optarg, NULL, 0); port = strtol(optarg, NULL, 0);
if (port < 1 || port > MAX_PLAYERS) if (port < 1 || port > MAX_PLAYERS)
@ -1079,11 +1077,6 @@ static void parse_input(int argc, char *argv[])
g_extern.has_set_libretro_device[port - 1] = true; g_extern.has_set_libretro_device[port - 1] = true;
break; break;
case 'p':
g_settings.input.libretro_device[1] = RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE;
g_extern.has_set_libretro_device[1] = true;
break;
case 'c': case 'c':
strlcpy(g_extern.config_path, optarg, sizeof(g_extern.config_path)); strlcpy(g_extern.config_path, optarg, sizeof(g_extern.config_path));
break; break;
@ -1274,39 +1267,26 @@ static void init_controllers(void)
pretro_set_controller_port_device(i, device); pretro_set_controller_port_device(i, device);
switch (device) const struct retro_controller_description *desc = NULL;
if (i < g_extern.system.num_ports)
desc = libretro_find_controller_description(&g_extern.system.ports[i], device);
const char *ident = desc ? desc->desc : NULL;
if (!ident)
{ {
case RETRO_DEVICE_NONE: switch (device)
RARCH_LOG("Disconnecting device from port %u.\n", i + 1); {
break; case RETRO_DEVICE_MOUSE: ident = "mouse"; break;
case RETRO_DEVICE_ANALOG: ident = "analog"; break;
case RETRO_DEVICE_ANALOG: default: ident = "Unknown"; break;
RARCH_LOG("Connecting dualanalog to port %u.\n", i + 1); }
break;
case RETRO_DEVICE_MOUSE:
RARCH_LOG("Connecting mouse to port %u.\n", i + 1);
break;
case RETRO_DEVICE_LIGHTGUN_JUSTIFIER:
RARCH_LOG("Connecting Justifier to port %u.\n", i + 1);
break;
case RETRO_DEVICE_LIGHTGUN_JUSTIFIERS:
RARCH_LOG("Connecting Justifiers to port %u.\n", i + 1);
break;
case RETRO_DEVICE_JOYPAD_MULTITAP:
RARCH_LOG("Connecting Multitap to port %u.\n", i + 1);
break;
case RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE:
RARCH_LOG("Connecting scope to port %u.\n", i + 1);
break;
default:
break;
} }
if (device == RETRO_DEVICE_NONE)
RARCH_LOG("Disconnecting device from port %u.\n", i + 1);
else
RARCH_LOG("Connecting %s to port %u.\n", ident, i + 1);
} }
} }