Add 3 new features to the Joystick Mapping command strings in the core config parameters, based on work that Simon Lang submitted to the mupen64plus google group: 1. Allow for multiple joystick actions (any of which can activate the command) by using a comma-separated list. 2. Allow for any joystick to be specified in a single action phrase by using the * character for joystick number. 3. Allow for hotkey functionality by requiring two simultaneous joystick actions (separate by slash character in an action phrase) before the command is enabled. Update wiki documentation for new features as well.

This commit is contained in:
Richard Goedeken 2015-10-04 16:23:23 -07:00
parent 9f986e8e21
commit 14bf40b236
2 changed files with 193 additions and 110 deletions

View file

@ -138,7 +138,15 @@ These configuration parameters are used in the Core's event loop to detect keybo
These configuration parameters are used in the Core's event loop to detect joystick commands.
The command strings use a simple format described here. For commands activated by pressing a joystick axis, the format is "J'''x'''A'''y'''+" or "J'''x'''A'''y'''-", where '''x''' is the SDL joystick number (must be between 0 and 9) and '''y''' is the axis number. For the last character, '''+''' represents movement in the positive direction, while '''-''' represents movement in the negative direction. For commands activated by pressing a button, the format is "J'''x'''B'''y'''", where '''x''' is the SDL joystick number (must be between 0 and 9) and '''y''' is the button number. For commands activated by pressing a ''hat'' (a directional switch) on the joystick, the format is "J'''x'''H'''y'''V'''z'''", where '''x''' is the SDL joystick number (must be between 0 and 9), '''y''' is the hat number, and '''z''' is the hat value. The hat value corresponds with the SDL_HAT_ enumerated types: Up is 1, Right is 2, Down is 4, and Left is 8. For diagonal directions, these values may be ''or''d together.
The command strings use a simple format described here. Note that you cannot have any spaces in the command string. For commands activated by pressing a joystick axis, the format is "J'''x'''A'''y'''+" or "J'''x'''A'''y'''-", where '''x''' is the SDL joystick number (must be between 0 and 9) and '''y''' is the axis number. For the last character, '''+''' represents movement in the positive direction, while '''-''' represents movement in the negative direction. For commands activated by pressing a button, the format is "J'''x'''B'''y'''", where '''x''' is the SDL joystick number (must be between 0 and 9) and '''y''' is the button number. For commands activated by pressing a ''hat'' (a directional switch) on the joystick, the format is "J'''x'''H'''y'''V'''z'''", where '''x''' is the SDL joystick number (must be between 0 and 9), '''y''' is the hat number, and '''z''' is the hat value. The hat value corresponds with the SDL_HAT_ enumerated types: Up is 1, Right is 2, Down is 4, and Left is 8. For diagonal directions, these values may be ''or''d together.
After the 2.5 release, several new features were added to these Joystick Mapping command strings. You can now specify more than one joystick action which will activate a given command. To specify several actions, use a comma-separated string containing multiple action phrases as given above ("J'''x'''A'''y'''+" or "J'''x'''B'''y'''" or "J'''x'''H'''y'''V'''z'''"). Make sure there are no spaces in your command string. Another new feature is that the "*" character can be used as a wildcard in the '''x''' joystick number field in order to activate this command for any joystick. Finally, you can also use a hotkey specifier to require that two distinct joystick actions must be pressed simultaneously in order to activate a command. The format for the hoykey feature is like "J'''x'''''Action''/''Action''" where ''Action'' is either "A'''y'''+", "A'''y'''-", "B'''y'''", or "H'''y'''V'''z'''".
Here's an example of a joystick command parameter which uses all three of these new features:
<pre>Joy Mapping Stop = "J0B6,J*B9,J1B5/A0-"</pre>
With this joystick event string, the emulator will shut down when joystick 0 button 6 is pressed, or when button 9 is pressed on any joystick, or when button 5 is pressed on joystick 1 at the same time as axis 0 is moved left.
{| border="1"
!Parameter Name!!Type!!Usage

View file

@ -128,7 +128,8 @@ static const char *JoyCmdName[] = { "Joy Mapping Fullscreen",
static const int NumJoyCommands = sizeof(JoyCmdName) / sizeof(const char *);
static int JoyCmdActive[16]; /* if extra joystick commands are added above, make sure there is enough room in this array */
static int JoyCmdActive[16][2]; /* if extra joystick commands are added above, make sure there is enough room in this array */
/* [i][0] is Command Active, [i][1] is Hotkey Active */
static int GamesharkActive = 0;
@ -146,110 +147,143 @@ static int GamesharkActive = 0;
*/
static int MatchJoyCommand(const SDL_Event *event, eJoyCommand cmd)
{
const char *event_str = ConfigGetParamString(l_CoreEventsConfig, JoyCmdName[cmd]);
const char *multi_event_str = ConfigGetParamString(l_CoreEventsConfig, JoyCmdName[cmd]);
const int orig_cmd_value = (JoyCmdActive[cmd][1] << 1) | JoyCmdActive[cmd][0];
int dev_number, input_number, input_value;
char axis_direction;
/* Empty string or non-joystick command */
if (event_str == NULL || strlen(event_str) < 4 || event_str[0] != 'J')
if (multi_event_str == NULL || strlen(multi_event_str) < 4 || multi_event_str[0] != 'J')
return 0;
/* Evaluate event based on type of joystick input expected by the given command */
switch (event_str[2])
/* Make copy of the event config string that we can hack it up with strtok */
char tokenized_event_str[128];
strncpy(tokenized_event_str, multi_event_str, 127);
tokenized_event_str[127] = '\0';
/* Iterate over comma-separated config phrases */
char *phrase_str = strtok(tokenized_event_str, ",");
while (phrase_str != NULL)
{
/* Axis */
case 'A':
if (event->type != SDL_JOYAXISMOTION)
return 0;
if (sscanf(event_str, "J%dA%d%c", &dev_number, &input_number, &axis_direction) != 3)
return 0;
int iHotkey, has_hotkey = 0;
/* Check for invalid phrase */
if (strlen(phrase_str) < 4 || phrase_str[0] != 'J')
{
phrase_str = strtok(NULL, ",");
continue;
}
/* Get device (joystick) number */
if (phrase_str[1] == '*')
{
dev_number = -1;
}
else if (phrase_str[1] >= '0' && phrase_str[1] <= '9')
{
dev_number = phrase_str[1] - '0';
#if SDL_VERSION_ATLEAST(2,0,0)
dev_number = l_iJoyInstanceID[dev_number];
dev_number = l_iJoyInstanceID[dev_number];
#endif
if (dev_number != event->jaxis.which || input_number != event->jaxis.axis)
return 0;
if (axis_direction == '+')
{
if (event->jaxis.value >= 15000 && JoyCmdActive[cmd] == 0)
{
JoyCmdActive[cmd] = 1;
return 1;
}
else if (event->jaxis.value <= 8000 && JoyCmdActive[cmd] == 1)
{
JoyCmdActive[cmd] = 0;
return -1;
}
return 0;
}
else if (axis_direction == '-')
{
if (event->jaxis.value <= -15000 && JoyCmdActive[cmd] == 0)
{
JoyCmdActive[cmd] = 1;
return 1;
}
else if (event->jaxis.value >= -8000 && JoyCmdActive[cmd] == 1)
{
JoyCmdActive[cmd] = 0;
return -1;
}
return 0;
}
else return 0; /* invalid axis direction in configuration parameter */
break;
/* Hat */
case 'H':
if (event->type != SDL_JOYHATMOTION)
return 0;
if (sscanf(event_str, "J%dH%dV%d", &dev_number, &input_number, &input_value) != 3)
return 0;
#if SDL_VERSION_ATLEAST(2,0,0)
dev_number = l_iJoyInstanceID[dev_number];
#endif
if (dev_number != event->jhat.which || input_number != event->jhat.hat)
return 0;
if ((event->jhat.value & input_value) == input_value && JoyCmdActive[cmd] == 0)
{
JoyCmdActive[cmd] = 1;
return 1;
}
else if ((event->jhat.value & input_value) != input_value && JoyCmdActive[cmd] == 1)
{
JoyCmdActive[cmd] = 0;
return -1;
}
return 0;
break;
/* Button. */
case 'B':
if (event->type != SDL_JOYBUTTONDOWN && event->type != SDL_JOYBUTTONUP)
return 0;
if (sscanf(event_str, "J%dB%d", &dev_number, &input_number) != 2)
return 0;
#if SDL_VERSION_ATLEAST(2,0,0)
dev_number = l_iJoyInstanceID[dev_number];
#endif
if (dev_number != event->jbutton.which || input_number != event->jbutton.button)
return 0;
if (event->type == SDL_JOYBUTTONDOWN && JoyCmdActive[cmd] == 0)
{
JoyCmdActive[cmd] = 1;
return 1;
}
else if (event->type == SDL_JOYBUTTONUP && JoyCmdActive[cmd] == 1)
{
JoyCmdActive[cmd] = 0;
return -1;
}
return 0;
break;
default:
/* bad configuration parameter */
return 0;
}
}
else
{
phrase_str = strtok(NULL, ",");
continue;
}
/* impossible to reach this point */
/* Iterate over JoyAction/JoyHotkey action fields */
for (iHotkey = 0; iHotkey < 2; iHotkey++)
{
const char *action_str = NULL;
if (iHotkey == 0)
{
action_str = phrase_str + 2;
}
else
{
char *hotkey_str = strchr(phrase_str, '/');
if (hotkey_str == NULL || strlen(hotkey_str) < 3)
{
break;
}
action_str = hotkey_str+1;
has_hotkey = 1;
}
/* Evaluate action based on type of joystick input expected */
switch (action_str[0])
{
/* Axis */
case 'A':
if (event->type != SDL_JOYAXISMOTION)
break;
if (sscanf(action_str, "A%d%c", &input_number, &axis_direction) != 2)
break;
if ((dev_number != 1 && dev_number != event->jaxis.which) || input_number != event->jaxis.axis)
break;
if (axis_direction == '+')
{
if (event->jaxis.value >= 15000)
JoyCmdActive[cmd][iHotkey] = 1;
else if (event->jaxis.value <= 8000)
JoyCmdActive[cmd][iHotkey] = 0;
break;
}
else if (axis_direction == '-')
{
if (event->jaxis.value <= -15000)
JoyCmdActive[cmd][iHotkey] = 1;
else if (event->jaxis.value >= -8000)
JoyCmdActive[cmd][iHotkey] = 0;
break;
}
break;
/* Hat */
case 'H':
if (event->type != SDL_JOYHATMOTION)
break;
if (sscanf(action_str, "H%dV%d", &input_number, &input_value) != 2)
break;
if ((dev_number != -1 && dev_number != event->jhat.which) || input_number != event->jhat.hat)
break;
if ((event->jhat.value & input_value) == input_value)
JoyCmdActive[cmd][iHotkey] = 1;
else if ((event->jhat.value & input_value) != input_value)
JoyCmdActive[cmd][iHotkey] = 0;
break;
/* Button. */
case 'B':
if (event->type != SDL_JOYBUTTONDOWN && event->type != SDL_JOYBUTTONUP)
break;
if (sscanf(action_str, "B%d", &input_number) != 1)
break;
if ((dev_number != -1 && dev_number != event->jbutton.which) || input_number != event->jbutton.button)
break;
if (event->type == SDL_JOYBUTTONDOWN)
JoyCmdActive[cmd][iHotkey] = 1;
else if (event->type == SDL_JOYBUTTONUP)
JoyCmdActive[cmd][iHotkey] = 0;
break;
default:
/* bad configuration parameter */
break;
}
} /* Iterate over JoyAction/JoyHotkey action fields */
/* Detect changes in command activation status */
const int new_cmd_value = (JoyCmdActive[cmd][1] << 1) | JoyCmdActive[cmd][0];
const int active_mask = has_hotkey ? 3 : 1;
if ((orig_cmd_value & active_mask) != active_mask && (new_cmd_value & active_mask) == active_mask)
return 1;
if ((orig_cmd_value & active_mask) == active_mask && (new_cmd_value & active_mask) != active_mask)
return -1;
/* get next comma-separated command phrase from event string */
phrase_str = strtok(NULL, ",");
} /* Iterate over comma-separated config phrases */
/* nothing found */
return 0;
}
@ -353,7 +387,7 @@ static int SDLCALL event_sdl_filter(void *userdata, SDL_Event *event)
break;
}
return 1;
return 1; // add this event to SDL queue
}
/*********************************************************************************************************
@ -362,29 +396,70 @@ static int SDLCALL event_sdl_filter(void *userdata, SDL_Event *event)
void event_initialize(void)
{
const char *event_str = NULL;
int i;
int i, j;
/* set initial state of all joystick commands to 'off' */
for (i = 0; i < NumJoyCommands; i++)
JoyCmdActive[i] = 0;
for (j = 0; j < 2; j++)
JoyCmdActive[i][j] = 0;
/* activate any joysticks which are referenced in the joystick event command strings */
for (i = 0; i < NumJoyCommands; i++)
const int NumJoysticks = SDL_NumJoysticks();
if (NumJoysticks > 0)
{
event_str = ConfigGetParamString(l_CoreEventsConfig, JoyCmdName[i]);
if (event_str != NULL && strlen(event_str) >= 4 && event_str[0] == 'J' && event_str[1] >= '0' && event_str[1] <= '9')
for (i = 0; i < NumJoyCommands; i++)
{
int device = event_str[1] - '0';
if (!SDL_WasInit(SDL_INIT_JOYSTICK))
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
const char *multi_event_str = ConfigGetParamString(l_CoreEventsConfig, JoyCmdName[i]);
/* Empty string or invalid command */
if (multi_event_str == NULL || strlen(multi_event_str) < 4 || multi_event_str[0] != 'J')
continue;
/* Make copy of the event config string that we can hack it up with strtok */
char tokenized_event_str[128];
strncpy(tokenized_event_str, multi_event_str, 127);
tokenized_event_str[127] = '\0';
/* Iterate over comma-separated config phrases */
char *phrase_str = strtok(tokenized_event_str, ",");
while (phrase_str != NULL)
{
/* Check for invalid phrase */
if (strlen(phrase_str) < 4 || phrase_str[0] != 'J')
{
phrase_str = strtok(NULL, ",");
continue;
}
/* Get device (joystick) number(s) to initialize */
int joy_min, joy_max;
if (phrase_str[1] == '*')
{
joy_min = 0;
joy_max = NumJoysticks;
}
else if (phrase_str[1] >= '0' && phrase_str[1] <= '9')
{
joy_min = joy_max = phrase_str[1] - '0';
}
else
{
phrase_str = strtok(NULL, ",");
continue;
}
/* initialize them */
int device;
for (device = joy_min; device <= joy_max; device++)
{
if (!SDL_WasInit(SDL_INIT_JOYSTICK))
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
#if SDL_VERSION_ATLEAST(2,0,0)
SDL_Joystick *thisJoy = SDL_JoystickOpen(device);
l_iJoyInstanceID[device] = SDL_JoystickInstanceID(thisJoy);
SDL_Joystick *thisJoy = SDL_JoystickOpen(device);
l_iJoyInstanceID[device] = SDL_JoystickInstanceID(thisJoy);
#else
if (!SDL_JoystickOpened(device))
SDL_JoystickOpen(device);
if (!SDL_JoystickOpened(device))
SDL_JoystickOpen(device);
#endif
}
phrase_str = strtok(NULL, ",");
} /* Iterate over comma-separated config phrases */
}
}