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.
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
\fB--dualanalog PORT, -A PORT\fR
Connects a DualAnalog controller into port PORT. Possible values are 1 to 8.
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
\fB--record PATH, -r PATH\fR
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;
}
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)
{
if (is_dummy)
@ -434,6 +446,7 @@ void uninit_libretro_sym(void)
// No longer valid.
free(g_extern.system.special);
free(g_extern.system.ports);
memset(&g_extern.system, 0, sizeof(g_extern.system));
#ifdef HAVE_CAMERA
g_extern.camera_active = false;
@ -907,16 +920,6 @@ bool rarch_environment_cb(unsigned cmd, void *data)
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:
{
const char **dir = (const char**)data;
@ -959,6 +962,38 @@ bool rarch_environment_cb(unsigned cmd, void *data)
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_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);
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_deinit)(void);

View File

@ -934,10 +934,6 @@ int menu_set_settings(void *data, unsigned setting, unsigned action)
RETRO_DEVICE_JOYPAD,
RETRO_DEVICE_ANALOG,
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;
@ -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_JOYPAD: name = "Joypad"; 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_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;
}

View File

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

View File

@ -46,8 +46,22 @@ extern "C" {
// It is not incremented for compatible changes to the API.
#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
// 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.
// 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
// 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),
// 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.
//
// 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.
#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).
// The placement of these is equivalent to placements on the Super Nintendo controller.
// 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().
// 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
{
@ -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);
// 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);
// 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-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);
puts("\t-p/--scope: Connect a virtual SuperScope into port 2. (SNES specific).");
puts("\t-j/--justifier: Connect a virtual Konami Justifier into port 2. (SNES specific).");
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).");
printf("\t-m/--mouse: Connect a mouse into controller port (1 to %d).\n", MAX_PLAYERS);
printf("\t-d/--device: Connect a generic device into port of the device (1 to %d).\n", MAX_PLAYERS);
puts("\t\tFormat is port:ID, where ID is an unsigned number corresponding to the particular device.\n");
#ifdef HAVE_BSV_MOVIE
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' },
{ "mouse", 1, NULL, 'm' },
{ "nodevice", 1, NULL, 'N' },
{ "scope", 0, NULL, 'p' },
{ "justifier", 0, NULL, 'j' },
{ "justifiers", 0, NULL, 'J' },
{ "dualanalog", 1, NULL, 'A' },
{ "device", 1, NULL, 'd' },
{ "savestate", 1, NULL, 'S' },
{ "multitap", 0, NULL, '4' },
#ifdef HAVE_BSV_MOVIE
{ "bsvplay", 1, NULL, 'P' },
{ "bsvrecord", 1, NULL, 'R' },
@ -989,7 +984,7 @@ static void parse_input(int argc, char *argv[])
#define BSV_MOVIE_ARG
#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 (;;)
{
@ -1006,25 +1001,28 @@ static void parse_input(int argc, char *argv[])
print_help();
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':
strlcpy(g_extern.subsystem, optarg, sizeof(g_extern.subsystem));
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':
port = strtol(optarg, NULL, 0);
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;
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':
strlcpy(g_extern.config_path, optarg, sizeof(g_extern.config_path));
break;
@ -1274,39 +1267,26 @@ static void init_controllers(void)
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:
RARCH_LOG("Disconnecting device from port %u.\n", i + 1);
break;
case RETRO_DEVICE_ANALOG:
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;
switch (device)
{
case RETRO_DEVICE_MOUSE: ident = "mouse"; break;
case RETRO_DEVICE_ANALOG: ident = "analog"; break;
default: ident = "Unknown"; 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);
}
}