input: merge buttons and axes

Default mappings for arcade and console
Default mappings for arcade stick/hitbox
Proper handling of inverted axes
ggpo: automatic analog setting for arcade games
This commit is contained in:
Flyinghead 2021-09-19 18:27:21 +02:00
parent 50a67dbd3e
commit 65956dbc8d
22 changed files with 1449 additions and 1160 deletions

View File

@ -762,8 +762,6 @@ if(NOT LIBRETRO)
core/linux-dist/evdev.cpp
core/linux-dist/evdev.h
core/linux-dist/icon.h
core/linux-dist/joystick.cpp
core/linux-dist/joystick.h
core/linux-dist/x11.cpp
core/linux-dist/x11.h
core/linux-dist/x11_keyboard.h)

View File

@ -13,7 +13,7 @@ static u8 GetBtFromSgn(s8 val)
}
u32 awave_button_mapping[32] = {
AWAVE_SERVICE_KEY, // DC_BTN_C
AWAVE_BTN2_KEY, // DC_BTN_C
AWAVE_BTN1_KEY, // DC_BTN_B
AWAVE_BTN0_KEY, // DC_BTN_A
AWAVE_START_KEY, // DC_BTN_START
@ -21,18 +21,18 @@ u32 awave_button_mapping[32] = {
AWAVE_DOWN_KEY, // DC_DPAD_DOWN
AWAVE_LEFT_KEY, // DC_DPAD_LEFT
AWAVE_RIGHT_KEY, // DC_DPAD_RIGHT
AWAVE_TEST_KEY, // DC_BTN_Z
AWAVE_BTN3_KEY, // DC_BTN_Y
AWAVE_BTN2_KEY, // DC_BTN_X
AWAVE_BTN4_KEY, // DC_BTN_Z (duplicated)
AWAVE_BTN4_KEY, // DC_BTN_Y
AWAVE_BTN3_KEY, // DC_BTN_X
AWAVE_COIN_KEY, // DC_BTN_D
AWAVE_BTN4_KEY, // DC_DPAD2_UP
0, // DC_DPAD2_DOWN
AWAVE_SERVICE_KEY, // DC_DPAD2_UP
AWAVE_TEST_KEY, // DC_DPAD2_DOWN
0, // DC_DPAD2_LEFT
0, // DC_DPAD2_RIGHT
};
u32 awavelg_button_mapping[32] = {
AWAVE_SERVICE_KEY, // DC_BTN_C
AWAVE_BTN1_KEY, // DC_BTN_C
AWAVE_BTN0_KEY, // DC_BTN_B
AWAVE_TRIGGER_KEY, // DC_BTN_A
AWAVE_START_KEY, // DC_BTN_START
@ -40,12 +40,12 @@ u32 awavelg_button_mapping[32] = {
AWAVE_DOWN_KEY, // DC_DPAD_DOWN
AWAVE_LEFT_KEY, // DC_DPAD_LEFT
AWAVE_RIGHT_KEY, // DC_DPAD_RIGHT
AWAVE_TEST_KEY, // DC_BTN_Z
AWAVE_BTN2_KEY, // DC_BTN_Y
AWAVE_BTN1_KEY, // DC_BTN_X
AWAVE_BTN4_KEY, // DC_BTN_Z
AWAVE_BTN3_KEY, // DC_BTN_Y
AWAVE_BTN2_KEY, // DC_BTN_X
AWAVE_COIN_KEY, // DC_BTN_D
AWAVE_BTN3_KEY, // DC_DPAD2_UP
AWAVE_BTN4_KEY, // DC_DPAD2_DOWN
AWAVE_SERVICE_KEY, // DC_DPAD2_UP
AWAVE_TEST_KEY, // DC_DPAD2_DOWN
0, // DC_DPAD2_LEFT
0, // DC_DPAD2_RIGHT
@ -64,8 +64,7 @@ void MapleConfigMap::SetVibration(float power, float inclination, u32 duration_m
void MapleConfigMap::GetInput(PlainJoystickState* pjs)
{
u32 player_num = playerNum();
const MapleInputState& inputState = mapleInputState[player_num];
const MapleInputState& inputState = mapleInputState[playerNum()];
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
{
@ -126,7 +125,7 @@ void MapleConfigMap::GetInput(PlainJoystickState* pjs)
pjs->joy[axis] = inputState.halfAxes[PJTI_L];
break;
default:
pjs->joy[axis] = 0x80;
pjs->joy[axis] = 0;
break;
}
}

View File

@ -54,7 +54,7 @@ void load_naomi_eeprom()
}
const u32 naomi_button_mapping[32] = {
NAOMI_SERVICE_KEY, // DC_BTN_C
NAOMI_BTN2_KEY, // DC_BTN_C
NAOMI_BTN1_KEY, // DC_BTN_B
NAOMI_BTN0_KEY, // DC_BTN_A
NAOMI_START_KEY, // DC_BTN_START
@ -62,12 +62,12 @@ const u32 naomi_button_mapping[32] = {
NAOMI_DOWN_KEY, // DC_DPAD_DOWN
NAOMI_LEFT_KEY, // DC_DPAD_LEFT
NAOMI_RIGHT_KEY, // DC_DPAD_RIGHT
NAOMI_TEST_KEY, // DC_BTN_Z
NAOMI_BTN3_KEY, // DC_BTN_Y
NAOMI_BTN2_KEY, // DC_BTN_X
NAOMI_BTN5_KEY, // DC_BTN_Z
NAOMI_BTN4_KEY, // DC_BTN_Y
NAOMI_BTN3_KEY, // DC_BTN_X
NAOMI_COIN_KEY, // DC_BTN_D
NAOMI_BTN4_KEY, // DC_DPAD2_UP
NAOMI_BTN5_KEY, // DC_DPAD2_DOWN
NAOMI_SERVICE_KEY, // DC_DPAD2_UP
NAOMI_TEST_KEY, // DC_DPAD2_DOWN
NAOMI_BTN6_KEY, // DC_DPAD2_LEFT
NAOMI_BTN7_KEY, // DC_DPAD2_RIGHT
@ -79,7 +79,7 @@ extern u32 awavelg_button_mapping[32];
const char *GetCurrentGameButtonName(DreamcastKey key)
{
if (NaomiGameInputs == nullptr || key == EMU_BTN_NONE)
if (NaomiGameInputs == nullptr || key == EMU_BTN_NONE || key > DC_BTN_RELOAD)
return nullptr;
u32 pos = 0;
u32 val = (u32)key;
@ -116,33 +116,36 @@ const char *GetCurrentGameAxisName(DreamcastKey axis)
for (int i = 0; NaomiGameInputs->axes[i].name != nullptr; i++)
{
DreamcastKey cur_axis;
switch (NaomiGameInputs->axes[i].axis)
{
case 0:
cur_axis = DC_AXIS_X;
if (axis != DC_AXIS_LEFT && axis != DC_AXIS_RIGHT)
continue;
break;
case 1:
cur_axis = DC_AXIS_Y;
if (axis != DC_AXIS_UP && axis != DC_AXIS_DOWN)
continue;
break;
case 2:
cur_axis = DC_AXIS_X2;
if (axis != DC_AXIS2_LEFT && axis != DC_AXIS2_RIGHT)
continue;
break;
case 3:
cur_axis = DC_AXIS_Y2;
if (axis != DC_AXIS2_UP && axis != DC_AXIS2_DOWN)
continue;
break;
case 4:
cur_axis = DC_AXIS_RT;
if (axis != DC_AXIS_RT)
continue;
break;
case 5:
cur_axis = DC_AXIS_LT;
if (axis != DC_AXIS_LT)
continue;
break;
default:
cur_axis = EMU_BTN_NONE;
break;
continue;
}
if (cur_axis == axis)
return NaomiGameInputs->axes[i].name;
return NaomiGameInputs->axes[i].name;
}
return nullptr;

View File

@ -206,8 +206,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
nullptr,
// ATACK, HOLD, THROW, MOVE
// same as ggram2
&giant_gram_inputs,
},
// Kick '4' Cash (Export)
{
@ -959,7 +958,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
NULL,
&capsnk_inputs
&capcom_4btn_inputs
},
// Capcom Vs. SNK Millennium Fight 2000 (Rev A)
{
@ -984,7 +983,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
NULL,
&capsnk_inputs
&capcom_4btn_inputs
},
// Capcom Vs. SNK Millennium Fight 2000
{
@ -1009,7 +1008,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
NULL,
&capsnk_inputs
&capcom_4btn_inputs
},
// Crackin' DJ
{
@ -1810,8 +1809,9 @@ Game Games[] =
{ "mpr-21830.ic10", 0x5000000, 0x0800000 },
{ "mpr-21831.ic11", 0x5800000, 0x0800000 },
{ NULL, 0, 0 },
}
// same as gram2000
},
nullptr,
&giant_gram_inputs,
},
// Guilty Gear X (JPN)
{
@ -1988,7 +1988,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
NULL,
&shot1234_inputs,
&hmgeo_input,
hmgeo_eeprom_dump
},
// House of the Dead 2
@ -2596,7 +2596,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
nullptr,
&pjustic_inputs,
&capcom_4btn_inputs,
},
// Power Stone
{
@ -2623,7 +2623,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
nullptr,
&shot123_inputs,
&pstone_inputs,
},
// Power Stone 2
{
@ -2650,7 +2650,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
nullptr,
&shot123_inputs,
&pstone2_inputs,
pstone2_eeprom_dump
},
// Power Stone 2 (bootleg)
@ -2678,7 +2678,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
nullptr,
&shot123_inputs,
&pstone2_inputs,
pstone2_eeprom_dump
},
// Puyo Puyo Da! (Japan)
@ -3588,7 +3588,9 @@ Game Games[] =
{ "trf1ma14.6m", 0xe000000, 0x1000000 },
{ "trf1ma15.6l", 0xf000000, 0x1000000 },
{ NULL, 0, 0 },
}
},
nullptr,
&toukon4_input,
},
// Toy Fighter
{
@ -4360,8 +4362,9 @@ Game Games[] =
{ "ic13.bin", 0x14000000, 0x4000000 },
{ "317-5133-jpn.ic3", 0, 0x800, 0x0000000, Key }, // pic_readout
{ NULL, 0, 0 },
}
// BUTTON A/B/C/D/E
},
nullptr,
&meltyb_input,
},
// Melty Blood Actress Again (Japan) (Clone)
{
@ -4383,7 +4386,9 @@ Game Games[] =
{ "ic13.bin", 0x14000000, 0x4000000 },
{ "317-5133-jpn.ic3", 0, 0x800, 0x0000000, Key }, // pic_readout
{ NULL, 0, 0 },
}
},
nullptr,
&meltyb_input,
},
// Mushiking The King Of Beetles - Mushiking II / III / III+ (Ver. 1.001) (World)
{
@ -4704,7 +4709,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0004",
// SHOT1/2/na/4/5
&capcom_4btn_inputs,
},
// Capcom Vs. SNK 2 Mark Of The Millennium 2001 (GDL-0008)
// ver 010804
@ -4724,7 +4729,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0008",
&cvs2_inputs,
&capcom_6btn_inputs,
},
// Capcom Vs. SNK 2 Millionaire Fighting 2001 (Rev A) (GDL-0007A)
// ver 010705
@ -4743,7 +4748,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0007a",
&cvs2_inputs,
&capcom_6btn_inputs,
},
// Dragon Treasure (Rev B) (GDS-0030B)
{
@ -4850,7 +4855,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0011",
// KICK, SLASH, HIGH SLASH, PUNCH, DUST ATTACK
&guilty_gear_input,
},
// Guilty Gear XX Accent Core (Japan) (GDL-0041)
{
@ -4868,7 +4873,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0041",
// same KICK, SLASH, HIGH SLASH, PUNCH, DUST ATTACK
&guilty_gear_input,
},
// Guilty Gear XX #Reload (Japan, Rev A) (GDL-0019A)
{
@ -4886,7 +4891,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0019a",
// same KICK, SLASH, HIGH SLASH, PUNCH, DUST ATTACK
&guilty_gear_input,
},
// Guilty Gear XX #Reload (Japan) (GDL-0019)
{
@ -4904,6 +4909,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0019",
&guilty_gear_input,
},
// Guilty Gear XX Slash (Japan, Rev A) (GDL-0033A)
{
@ -4921,7 +4927,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0033a",
// same KICK, SLASH, HIGH SLASH, PUNCH, DUST ATTACK
&guilty_gear_input,
},
// Mobile Suit Gundam: Federation Vs. Zeon (GDL-0001)
{
@ -5120,7 +5126,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0039a",
// BUTTON A/B/C/D/E
&meltyb_input,
},
// Melty Blood Act Cadenza Ver. A (Japan) (GDL-0028C)
{
@ -5138,6 +5144,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0028c",
&meltyb_input,
},
// Melty Blood Act Cadenza (Japan) (GDL-0028)
{
@ -5155,6 +5162,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0028",
&meltyb_input,
},
// Melty Blood Act Cadenza Version B (Japan) (GDL-0039)
{
@ -5172,6 +5180,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0039",
&meltyb_input,
},
// Moeru Casinyo (Japan) (GDL-0013)
{
@ -5427,6 +5436,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0038",
&senkosp_input,
},
// Street Fighter Zero 3 Upper (Japan) (GDL-0002)
{
@ -5445,7 +5455,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
"gdl-0002",
&sfz3ugd_inputs
&capcom_6btn_inputs,
},
// Shakatto Tambourine (Rev B) (GDS-0002B)
{
@ -6063,7 +6073,9 @@ Game Games[] =
{ "ax1906m01.ic16", 0x6000000, 0x1000000 },
{ "ax1907m01.ic17", 0x7000000, 0x1000000 },
{ NULL, 0, 0 },
}
},
nullptr,
&fotns_input,
},
// Faster Than Speed
{
@ -6111,7 +6123,9 @@ Game Games[] =
{ "ax1207m01.ic16", 0x6000000, 0x1000000 },
{ "ax1208m01.ic17", 0x7000000, 0x1000000 },
{ NULL, 0, 0 },
}
},
nullptr,
&guilty_gear_aw_input,
},
// Guilty Gear X ver. 1.5
{
@ -6158,7 +6172,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
NULL,
&kofnw_inputs
&kofnw_input,
},
// The King of Fighters Neowave (Japan)
{
@ -6183,7 +6197,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
NULL,
&kofnw_inputs
&kofnw_input,
},
// The King of Fighters XI
{
@ -6208,7 +6222,7 @@ Game Games[] =
{ NULL, 0, 0 },
},
NULL,
&kofxi_inputs
&kofxi_input,
},
// Knights of Valour - The Seven Spirits
{
@ -6274,7 +6288,9 @@ Game Games[] =
{ "ax3003m01.mrom3", 0x6000000, 0x2000000 },
{ "ax3004m01.mrom4", 0xa000000, 0x2000000 },
{ NULL, 0, 0 },
}
},
nullptr,
&mslug6_input,
},
// NeoGeo Battle Coliseum
{
@ -6368,7 +6384,9 @@ Game Games[] =
{ "ax1806m01.ic16", 0x6000000, 0x1000000 },
{ "ax1807m01.ic17", 0x7000000, 0x1000000 },
{ NULL, 0, 0 },
}
},
nullptr,
&rumblef_input,
},
// The Rumble Fish (prototype)
{
@ -6399,7 +6417,9 @@ Game Games[] =
{ "ic26", 0x07000000, 0x00800000 },
// IC27 populated, empty
{ NULL, 0, 0 },
}
},
nullptr,
&rumblef_input,
},
// The Rumble Fish 2
{
@ -6420,7 +6440,9 @@ Game Games[] =
{ "ax3404m01.mrom4", 0xa000000, 0x2000000 },
{ "ax3405m01.mrom5", 0xc000000, 0x2000000 },
{ NULL, 0, 0 },
}
},
nullptr,
&rumblef_input,
},
// The Rumble Fish 2 (prototype)
{
@ -6451,7 +6473,9 @@ Game Games[] =
{ "ic26", 0x07000000, 0x00800000 },
// IC27 populated, empty
{ NULL, 0, 0 },
}
},
nullptr,
&rumblef_input,
},
// Net Select: Salaryman Kintaro
{

View File

@ -35,47 +35,91 @@
{ \
{ NAOMI_BTN0_KEY, btn0 }, \
{ NAOMI_BTN1_KEY, btn1 }, \
{ NAOMI_UP_KEY, "" }, \
{ NAOMI_DOWN_KEY, "" }, \
{ NAOMI_LEFT_KEY, "" }, \
{ NAOMI_RIGHT_KEY, "" }, \
NAO_START_DESC \
NAO_BASE_BTN_DESC \
{ 0 }, \
} \
}; \
}
#define INPUT_3_BUTTONS(btn0, btn1, btn2) { \
{ \
{ NAOMI_BTN0_KEY, btn0 }, \
{ NAOMI_BTN1_KEY, btn1 }, \
{ NAOMI_BTN2_KEY, btn2 }, \
{ NAOMI_UP_KEY, "" }, \
{ NAOMI_DOWN_KEY, "" }, \
{ NAOMI_LEFT_KEY, "" }, \
{ NAOMI_RIGHT_KEY, "" }, \
NAO_START_DESC \
NAO_BASE_BTN_DESC \
} \
}
#define INPUT_4_BUTTONS(btn0, btn1, btn2, btn3) { \
{ \
{ NAOMI_BTN0_KEY, btn0 }, \
{ NAOMI_BTN1_KEY, btn1 }, \
{ NAOMI_BTN2_KEY, btn2 }, \
{ NAOMI_BTN3_KEY, btn3 }, \
{ NAOMI_UP_KEY, "" }, \
{ NAOMI_DOWN_KEY, "" }, \
{ NAOMI_LEFT_KEY, "" }, \
{ NAOMI_RIGHT_KEY, "" }, \
NAO_START_DESC \
NAO_BASE_BTN_DESC \
} \
}
#define INPUT_5_BUTTONS(btn0, btn1, btn2, btn3, btn4) { \
{ \
{ NAOMI_BTN0_KEY, btn0 }, \
{ NAOMI_BTN1_KEY, btn1 }, \
{ NAOMI_BTN2_KEY, btn2 }, \
{ NAOMI_BTN3_KEY, btn3 }, \
{ NAOMI_BTN5_KEY, btn4, NAOMI_BTN4_KEY }, \
{ NAOMI_UP_KEY, "" }, \
{ NAOMI_DOWN_KEY, "" }, \
{ NAOMI_LEFT_KEY, "" }, \
{ NAOMI_RIGHT_KEY, "" }, \
NAO_START_DESC \
NAO_BASE_BTN_DESC \
} \
}
static InputDescriptors _18wheelr_inputs = {
{
{ NAOMI_BTN0_KEY, "HORN" },
{ NAOMI_DOWN_KEY, "VIEW" },
{ NAOMI_BTN1_KEY, "SHIFT L", 0, NAOMI_DOWN_KEY }, // This button uses P2 inputs for P1
{ NAOMI_BTN2_KEY, "SHIFT H", 0, NAOMI_UP_KEY }, // This button uses P2 inputs for P1
{ NAOMI_BTN2_KEY, "SHIFT L", 0, NAOMI_DOWN_KEY }, // This button uses P2 inputs for P1
{ NAOMI_BTN1_KEY, "SHIFT H", 0, NAOMI_UP_KEY }, // This button uses P2 inputs for P1
{ NAOMI_BTN3_KEY, "SHIFT R", 0, NAOMI_LEFT_KEY | NAOMI_DOWN_KEY },
// This button uses P2 inputs for P1
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "HANDLE", Full, 0 },
{ "ACCEL", Half, 4 },
{ "BRAKE", Half, 5 },
{ NULL },
},
};
static InputDescriptors alienfnt_inputs = {
{
{ NAOMI_BTN0_KEY, "LEFT SHOT" },
{ NAOMI_BTN3_KEY, "ROTATION R", NAOMI_BTN1_KEY },
{ NAOMI_BTN5_KEY, "ROTATION R", NAOMI_BTN1_KEY },
{ NAOMI_BTN1_KEY, "RIGHT SHOT", NAOMI_BTN2_KEY },
{ NAOMI_BTN2_KEY, "ROTATION L", NAOMI_BTN3_KEY },
{ NAOMI_BTN4_KEY, "ROTATION L", NAOMI_BTN3_KEY },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "WHEEL", Full, 0 },
{ "RIGHT PEDAL", Half, 4 },
{ "LEFT PEDAL", Half, 5 },
{ NULL },
},
};
@ -86,7 +130,6 @@ static InputDescriptors alpilot_inputs = {
{ NAOMI_BTN2_KEY, "FLAP SWITCH" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "ELEVATOR", Full, 1 },
@ -95,26 +138,38 @@ static InputDescriptors alpilot_inputs = {
{ "RUDDER PEDAL", Full, 2 },
{ "THRUST LEVER L", Half, 5 },
{ "THRUST LEVER R", Half, 4 },
{ NULL },
},
};
static InputDescriptors capsnk_inputs = {
static InputDescriptors capcom_4btn_inputs = {
{
{ NAOMI_BTN0_KEY, "LIGHT PUNCH" },
{ NAOMI_BTN1_KEY, "STRONG PUNCH" },
{ NAOMI_BTN1_KEY, "HEAVY PUNCH" },
{ NAOMI_BTN3_KEY, "LIGHT KICK" },
{ NAOMI_BTN4_KEY, "STRONG KICK" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
{ NAOMI_BTN4_KEY, "HEAVY KICK" },
{ NAOMI_UP_KEY, "" },
{ NAOMI_DOWN_KEY, "" },
{ NAOMI_LEFT_KEY, "" },
{ NAOMI_RIGHT_KEY, "" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
};
static InputDescriptors capcom_6btn_inputs = {
{
{ NULL },
{ NAOMI_BTN0_KEY, "LIGHT PUNCH" },
{ NAOMI_BTN1_KEY, "MEDIUM PUNCH" },
{ NAOMI_BTN2_KEY, "HEAVY PUNCH" },
{ NAOMI_BTN3_KEY, "LIGHT KICK" },
{ NAOMI_BTN4_KEY, "MEDIUM KICK" },
{ NAOMI_BTN5_KEY, "HEAVY KICK" },
{ NAOMI_UP_KEY, "" },
{ NAOMI_DOWN_KEY, "" },
{ NAOMI_LEFT_KEY, "" },
{ NAOMI_RIGHT_KEY, "" },
NAO_START_DESC
NAO_BASE_BTN_DESC
},
};
@ -124,47 +179,26 @@ static InputDescriptors crzytaxi_inputs = {
{ NAOMI_DOWN_KEY, "REVERSE GEAR" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "HANDLE", Full, 0 },
{ "ACCEL", Half, 4 },
{ "BRAKE", Half, 5 },
{ NULL },
},
};
static InputDescriptors cspike_inputs = {
{
{ NAOMI_BTN0_KEY, "SHOT1" },
{ NAOMI_BTN1_KEY, "SHOT2" },
{ NAOMI_BTN2_KEY, "SHOT3" },
{ NAOMI_BTN3_KEY, "SHOT4" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ NULL },
},
};
static InputDescriptors cspike_inputs = INPUT_3_BUTTONS("Shoot", "Attack", "Mark");
static InputDescriptors trigger_inputs = {
{
{ NAOMI_BTN0_KEY, "TRIGGER" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ NULL },
},
};
static InputDescriptors giant_gram_inputs = INPUT_4_BUTTONS("Attack", "Hold", "Throw", "Move");
static InputDescriptors gunsur2_inputs = {
{
{ NAOMI_BTN0_KEY, "GUN BUTTON" },
@ -173,13 +207,11 @@ static InputDescriptors gunsur2_inputs = {
{ NAOMI_DOWN_KEY, "SELECT DOWN" },
{ NAOMI_START_KEY, "ENTER" },
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "PITCH", Full, 1 },
{ "ROLL", Full, 2, true },
{ "YAW", Full, 0 },
{ NULL },
},
};
@ -189,13 +221,11 @@ static InputDescriptors jambo_inputs = {
{ NAOMI_BTN0_KEY, "LEVER DOWN", 0, NAOMI_UP_KEY }, // This button uses P2 inputs for P1
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "HANDLE", Full, 0 },
{ "ACCEL", Half, 4 },
{ "BRAKE", Half, 5 },
{ NULL },
},
};
@ -207,37 +237,12 @@ static InputDescriptors mvsc2_inputs = {
{ NAOMI_BTN3_KEY, "LIGHT KICK" },
{ NAOMI_BTN4_KEY, "STRONG KICK" },
{ NAOMI_BTN5_KEY, "ASSIST B" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
{ NAOMI_UP_KEY, "" },
{ NAOMI_DOWN_KEY, "" },
{ NAOMI_LEFT_KEY, "" },
{ NAOMI_RIGHT_KEY, "" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ NULL },
},
};
static InputDescriptors sfz3ugd_inputs = {
{
{ NAOMI_BTN0_KEY, "LIGHT PUNCH" },
{ NAOMI_BTN1_KEY, "MEDIUM PUNCH" },
{ NAOMI_BTN2_KEY, "STRONG PUNCH" },
{ NAOMI_BTN3_KEY, "LIGHT KICK" },
{ NAOMI_BTN4_KEY, "MEDIUM KICK" },
{ NAOMI_BTN5_KEY, "STRONG KICK" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ NULL },
},
};
@ -249,10 +254,6 @@ static InputDescriptors ninjaslt_inputs = {
{ NAOMI_UP_KEY, "SELECT UP" },
{ NAOMI_DOWN_KEY, "SELECT DOWN" },
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ NULL },
},
};
@ -275,118 +276,29 @@ static InputDescriptors vonot_inputs = {
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ NULL },
},
};
static InputDescriptors shot12_inputs = {
{
{ NAOMI_BTN0_KEY, "SHOT1" },
{ NAOMI_BTN1_KEY, "SHOT2" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
},
};
static InputDescriptors shot12_inputs = INPUT_2_BUTTONS("SHOT1", "SHOT2");
static InputDescriptors shot123_inputs = {
{
{ NAOMI_BTN0_KEY, "SHOT1" },
{ NAOMI_BTN1_KEY, "SHOT2" },
{ NAOMI_BTN2_KEY, "SHOT3" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
},
};
static InputDescriptors pstone_inputs = INPUT_3_BUTTONS("Punch", "Kick", "Jump");
static InputDescriptors shot1234_inputs = {
{
{ NAOMI_BTN0_KEY, "SHOT1" },
{ NAOMI_BTN1_KEY, "SHOT2" },
{ NAOMI_BTN2_KEY, "SHOT3" },
{ NAOMI_BTN3_KEY, "SHOT4" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
},
};
static InputDescriptors pstone2_inputs = INPUT_3_BUTTONS("Punch", "Jump", "Attack");
static InputDescriptors shot1234_inputs = INPUT_4_BUTTONS("SHOT1", "SHOT2", "SHOT3", "SHOT4");
static InputDescriptors monkeyba_inputs = {
{
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "STICK V", Full, 1 },
{ "STICK H", Full, 0 },
{ NULL },
},
};
static InputDescriptors cvs2_inputs = {
{
{ NAOMI_BTN0_KEY, "LIGHT PUNCH" },
{ NAOMI_BTN1_KEY, "MEDIUM PUNCH" },
{ NAOMI_BTN2_KEY, "STRONG PUNCH" },
{ NAOMI_BTN3_KEY, "LIGHT KICK" },
{ NAOMI_BTN4_KEY, "MEDIUM KICK" },
{ NAOMI_BTN5_KEY, "STRONG KICK" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ NULL },
},
};
static InputDescriptors pjustic_inputs = {
{
{ NAOMI_BTN0_KEY, "SHOT1" },
{ NAOMI_BTN1_KEY, "SHOT2" },
{ NAOMI_BTN2_KEY, "SHOT3", NAOMI_BTN3_KEY },
{ NAOMI_BTN3_KEY, "SHOT4", NAOMI_BTN4_KEY },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
},
};
static InputDescriptors slashout_inputs = {
{
{ NAOMI_BTN0_KEY, "BLADE" },
{ NAOMI_BTN1_KEY, "CHARGE" },
{ NAOMI_BTN2_KEY, "JUMP" },
{ NAOMI_BTN3_KEY, "SHIFT" },
{ NAOMI_UP_KEY, "UP" },
{ NAOMI_DOWN_KEY, "DOWN" },
{ NAOMI_LEFT_KEY, "LEFT" },
{ NAOMI_RIGHT_KEY, "RIGHT" },
NAO_START_DESC
NAO_BASE_BTN_DESC
},
};
static InputDescriptors slashout_inputs = INPUT_4_BUTTONS("Blade", "Charge", "Jump", "Shift");
static InputDescriptors tokyobus_inputs = {
{
@ -395,19 +307,17 @@ static InputDescriptors tokyobus_inputs = {
{ NAOMI_LEFT_KEY, "ANNOUNCE" },
{ NAOMI_RIGHT_KEY, "DOOR CLOSE" },
// These buttons uses P2 inputs for P1
{ NAOMI_BTN1_KEY, "WINKER RIGHT", 0, NAOMI_BTN0_KEY },
{ NAOMI_BTN2_KEY, "WINKER LEFT", 0, NAOMI_BTN1_KEY },
{ NAOMI_BTN3_KEY, "SHIFT FRONT", 0, NAOMI_UP_KEY },
{ NAOMI_BTN4_KEY, "SHIFT REVERSE", 0, NAOMI_DOWN_KEY },
{ NAOMI_BTN5_KEY, "WINKER RIGHT", 0, NAOMI_BTN0_KEY },
{ NAOMI_BTN4_KEY, "WINKER LEFT", 0, NAOMI_BTN1_KEY },
{ NAOMI_BTN2_KEY, "SHIFT FRONT", 0, NAOMI_UP_KEY },
{ NAOMI_BTN1_KEY, "SHIFT REVERSE", 0, NAOMI_DOWN_KEY },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "HANDLE", Full, 0 },
{ "ACCEL", Half, 4 },
{ "BRAKE", Half, 5 },
{ NULL },
},
};
@ -416,14 +326,12 @@ static InputDescriptors wrungp_inputs = {
{ NAOMI_UP_KEY, "VIEW" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "HANDLE BAR", Full, 0 },
{ "THROTTLE LEVER", Half, 4, true },
{ "ROLL", Full, 2 },
{ "PITCH", Full, 3 },
{ NULL },
},
};
@ -433,7 +341,6 @@ static InputDescriptors marine_fishing_inputs = {
{ NAOMI_START_KEY, "CAST" },
{ NAOMI_UP_KEY, "LURE" },
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "ROD Y", Full, 1 },
@ -441,7 +348,6 @@ static InputDescriptors marine_fishing_inputs = {
{ "STICK X", Full, 2 },
{ "STICK Y", Full, 3 },
{ "REEL SPEED", Half, 4 },
{ NULL },
},
};
@ -466,7 +372,6 @@ static InputDescriptors f355_inputs = {
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "ACCEL", Half, 4 },
@ -474,23 +379,24 @@ static InputDescriptors f355_inputs = {
{ "CLUTCH", Full, 2 }, // Deluxe only
{ "unused", Full, 4 },
{ "HANDLE", Full, 0 },
{ NULL },
},
};
static InputDescriptors zombie_inputs = {
{
{ NAOMI_BTN0_KEY, "BTN0" },
{ NAOMI_BTN1_KEY, "BTN1" },
{ NAOMI_BTN2_KEY, "BTN2" },
{ NAOMI_BTN0_KEY, "L" },
{ NAOMI_BTN1_KEY, "R" },
{ NAOMI_BTN2_KEY, "G" },
{ NAOMI_UP_KEY, "" },
{ NAOMI_DOWN_KEY, "" },
{ NAOMI_LEFT_KEY, "" },
{ NAOMI_RIGHT_KEY, "" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "UP - DOWN", Full, 1, true },
{ "LEFT - RIGHT", Full, 0, true },
{ NULL },
},
};
@ -502,14 +408,12 @@ static InputDescriptors wsbb_inputs = {
{ NAOMI_BTN1_KEY, "BTN B" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "STICK Y", Full, 1, true },
{ "STICK X", Full, 0, true },
{ "BAT", Half, 4, true },
{ "", Half, 5 }, // unused but P2 starts at axis 4
{ NULL },
},
};
@ -519,12 +423,10 @@ static InputDescriptors ringout_inputs = {
{ NAOMI_BTN1_KEY, "BACK" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "STEER", Full, 0 },
{ "ACCEL", Half, 4 },
{ NULL },
},
};
@ -536,16 +438,38 @@ static InputDescriptors sstrkfgt_inputs = {
{ NAOMI_BTN3_KEY, "VIEW CHANGE" },
NAO_START_DESC
NAO_BASE_BTN_DESC
{ 0 },
},
{
{ "ELEVATOR", Full, 1 },
{ "AILERON", Full, 0 },
{ "THRUST LEVER", Half, 4 },
{ "RUDDER PEDAL", Full, 2 },
{ NULL },
},
};
static InputDescriptors guilty_gear_input = INPUT_5_BUTTONS("KICK", "SLASH", "HSLASH", "PUNCH", "DUST ATTACK");
static InputDescriptors senkosp_input = INPUT_5_BUTTONS("MAIN", "SUB", "MAIN+SUB", "ACTION", "OVER DRIVE");
static InputDescriptors meltyb_input = INPUT_5_BUTTONS("LAttack", "MAttack", "HAttack", "Guard", "Quick Action");
static InputDescriptors toukon4_input = INPUT_5_BUTTONS("X", "Y", "R", "A", "B");
static InputDescriptors hmgeo_input = {
{
{ NAOMI_BTN0_KEY, "Fire" },
{ NAOMI_BTN1_KEY, "Attack" },
{ NAOMI_BTN3_KEY, "Jump" },
{ NAOMI_BTN4_KEY, "Target" },
{ NAOMI_UP_KEY, "" },
{ NAOMI_DOWN_KEY, "" },
{ NAOMI_LEFT_KEY, "" },
{ NAOMI_RIGHT_KEY, "" },
NAO_START_DESC
NAO_BASE_BTN_DESC
},
};
//
// AtomisWave games
//
@ -555,6 +479,24 @@ static InputDescriptors sstrkfgt_inputs = {
{ AWAVE_SERVICE_KEY, "SERVICE" },
#define AW_START_DESC { AWAVE_START_KEY, "START" },
#define AW_5_BUTTONS(btn0, btn1, btn2, btn3, btn4) { \
{ \
{ AWAVE_BTN0_KEY, btn0 }, \
{ AWAVE_BTN1_KEY, btn1 }, \
{ AWAVE_BTN2_KEY, btn2 }, \
{ AWAVE_BTN3_KEY, btn3 }, \
{ AWAVE_BTN4_KEY, btn4 }, \
{ AWAVE_UP_KEY, "" }, \
{ AWAVE_DOWN_KEY, "" }, \
{ AWAVE_LEFT_KEY, "" }, \
{ AWAVE_RIGHT_KEY, "" }, \
AW_START_DESC \
AW_BASE_BTN_DESC \
} \
}
static InputDescriptors guilty_gear_aw_input = AW_5_BUTTONS("KICK", "SLASH", "HSLASH", "PUNCH", "DUST ATTACK");
static InputDescriptors ftspeed_inputs = {
{
{ AWAVE_BTN0_KEY, "BOOST" },
@ -562,49 +504,17 @@ static InputDescriptors ftspeed_inputs = {
{ AWAVE_DOWN_KEY, "LOW GEAR" },
AW_START_DESC
AW_BASE_BTN_DESC
{ 0 },
},
{
{ "STEERING WHEEL", Full, 0 },
{ "GAS PEDAL", Half, 4 },
{ "BRAKE PEDAL", Half, 5 },
{ NULL },
},
};
static InputDescriptors kofnw_inputs = {
{
{ AWAVE_BTN0_KEY, "LIGHT PUNCH" },
{ AWAVE_BTN1_KEY, "STRONG PUNCH" },
{ AWAVE_BTN2_KEY, "HEAT MODE" },
{ AWAVE_BTN3_KEY, "LIGHT KICK" },
{ AWAVE_BTN4_KEY, "STRONG KICK" },
{ AWAVE_UP_KEY, "UP" },
{ AWAVE_DOWN_KEY, "DOWN" },
{ AWAVE_LEFT_KEY, "LEFT" },
{ AWAVE_RIGHT_KEY, "RIGHT" },
AW_START_DESC
AW_BASE_BTN_DESC
{ 0 },
}
};
static InputDescriptors kofnw_input = AW_5_BUTTONS("LP", "SP", "Heat mode", "LK", "SK");
static InputDescriptors kofxi_inputs = {
{
{ AWAVE_BTN0_KEY, "LIGHT PUNCH" },
{ AWAVE_BTN1_KEY, "STRONG PUNCH" },
{ AWAVE_BTN2_KEY, "SPECIAL ATTACK" },
{ AWAVE_BTN3_KEY, "LIGHT KICK" },
{ AWAVE_BTN4_KEY, "STRONG KICK" },
{ AWAVE_UP_KEY, "UP" },
{ AWAVE_DOWN_KEY, "DOWN" },
{ AWAVE_LEFT_KEY, "LEFT" },
{ AWAVE_RIGHT_KEY, "RIGHT" },
AW_START_DESC
AW_BASE_BTN_DESC
{ 0 },
}
};
static InputDescriptors kofxi_input = AW_5_BUTTONS("LP", "SP", "Blow-off", "LK", "SK");
static InputDescriptors maxspeed_inputs = {
{
@ -612,61 +522,33 @@ static InputDescriptors maxspeed_inputs = {
{ AWAVE_DOWN_KEY, "LOW SHIFT" },
AW_START_DESC
AW_BASE_BTN_DESC
{ 0 },
},
{
{ "STEERING", Full, 0 },
{ "ACCELERATOR", Half, 4 },
{ "BRAKE", Half, 5 },
{ NULL },
},
};
static InputDescriptors ngbc_inputs = {
{
{ AWAVE_BTN0_KEY, "LIGHT PUNCH" },
{ AWAVE_BTN1_KEY, "STRONG PUNCH" },
{ AWAVE_BTN2_KEY, "SWAP CHARACTERS" },
{ AWAVE_BTN3_KEY, "LIGHT KICK" },
{ AWAVE_BTN4_KEY, "STRONG KICK" },
{ AWAVE_UP_KEY, "UP" },
{ AWAVE_DOWN_KEY, "DOWN" },
{ AWAVE_LEFT_KEY, "LEFT" },
{ AWAVE_RIGHT_KEY, "RIGHT" },
AW_START_DESC
AW_BASE_BTN_DESC
{ 0 },
}
};
static InputDescriptors ngbc_inputs = AW_5_BUTTONS("LP", "SP", "SWAP", "LK", "SK");
static InputDescriptors samsptk_inputs = {
{
{ AWAVE_BTN0_KEY, "LIGHT SLASH" },
{ AWAVE_BTN1_KEY, "MEDIUM SLASH" },
{ AWAVE_BTN2_KEY, "STRONG SLASH" },
{ AWAVE_BTN3_KEY, "KICK" },
{ AWAVE_BTN4_KEY, "SPECIAL EVASION" },
{ AWAVE_UP_KEY, "UP" },
{ AWAVE_DOWN_KEY, "DOWN" },
{ AWAVE_LEFT_KEY, "LEFT" },
{ AWAVE_RIGHT_KEY, "RIGHT" },
AW_START_DESC
AW_BASE_BTN_DESC
{ 0 },
}
};
static InputDescriptors samsptk_inputs = AW_5_BUTTONS("LIGHT SLASH", "MEDIUM SLASH", "STRONG SLASH", "KICK", "SPECIAL EVASION");
static InputDescriptors blockpong_inputs = {
{
AW_START_DESC
AW_BASE_BTN_DESC
{ 0 },
},
{
{ "ANALOG X", Full, 0, true },
{ "ANALOG Y", Full, 1 },
{ "ANALOG X", Full, 0 }, // for P2
{ "ANALOG Y", Full, 1 }, // for P2
{ NULL },
},
};
static InputDescriptors fotns_input = AW_5_BUTTONS("LP", "HP", "BOOST", "LK", "HK");
static InputDescriptors mslug6_input = AW_5_BUTTONS("Shoot", "Jump", "Switch", "Grenade", "Mslug Attack");
static InputDescriptors rumblef_input = AW_5_BUTTONS("LP", "SP", "Dodge", "LK", "SK");

View File

@ -39,44 +39,28 @@ enum DreamcastKey
// System buttons
EMU_BTN_NONE = 0,
EMU_BTN_TRIGGER_LEFT = 1 << 17,
EMU_BTN_TRIGGER_RIGHT = 1 << 18,
EMU_BTN_MENU = 1 << 19,
EMU_BTN_FFORWARD = 1 << 20,
EMU_BTN_ANA_UP = 1 << 21,
EMU_BTN_ANA_DOWN = 1 << 22,
EMU_BTN_ANA_LEFT = 1 << 23,
EMU_BTN_ANA_RIGHT = 1 << 24,
EMU_BTN_ESCAPE = 1 << 25,
DC_BTN_GROUP_MASK = 0xF000000,
EMU_BUTTONS = 0x3000000,
EMU_BTN_MENU,
EMU_BTN_FFORWARD,
EMU_BTN_ESCAPE,
// Real axes
DC_AXIS_LT = 0x10000,
DC_AXIS_RT = 0x10001,
DC_AXIS_X = 0x20000,
DC_AXIS_Y = 0x20001,
DC_AXIS_X2 = 0x20002,
DC_AXIS_Y2 = 0x20003,
DC_AXIS_TRIGGERS = 0x1000000,
DC_AXIS_LT,
DC_AXIS_RT,
DC_AXIS_STICKS = 0x2000000,
DC_AXIS_LEFT,
DC_AXIS_RIGHT,
DC_AXIS_UP,
DC_AXIS_DOWN,
DC_AXIS2_LEFT,
DC_AXIS2_RIGHT,
DC_AXIS2_UP,
DC_AXIS2_DOWN,
// System axes
EMU_AXIS_NONE = 0x00000,
EMU_AXIS_DPAD1_X = DC_DPAD_LEFT,
EMU_AXIS_DPAD1_Y = DC_DPAD_UP,
EMU_AXIS_DPAD2_X = DC_DPAD2_LEFT,
EMU_AXIS_DPAD2_Y = DC_DPAD2_UP,
EMU_AXIS_BTN_A = 0x40000 | DC_BTN_A,
EMU_AXIS_BTN_B = 0x40000 | DC_BTN_B,
EMU_AXIS_BTN_C = 0x40000 | DC_BTN_C,
EMU_AXIS_BTN_D = 0x40000 | DC_BTN_D,
EMU_AXIS_BTN_X = 0x40000 | DC_BTN_X,
EMU_AXIS_BTN_Y = 0x40000 | DC_BTN_Y,
EMU_AXIS_BTN_Z = 0x40000 | DC_BTN_Z,
EMU_AXIS_BTN_START = 0x40000 | DC_BTN_START,
EMU_AXIS_DPAD_UP = 0x40000 | DC_DPAD_UP,
EMU_AXIS_DPAD_DOWN = 0x40000 | DC_DPAD_DOWN,
EMU_AXIS_DPAD_LEFT = 0x40000 | DC_DPAD_LEFT,
EMU_AXIS_DPAD_RIGHT = 0x40000 | DC_DPAD_RIGHT,
EMU_AXIS_DPAD2_UP = 0x40000 | DC_DPAD2_UP,
EMU_AXIS_DPAD2_DOWN = 0x40000 | DC_DPAD2_DOWN,
EMU_AXIS_DPAD2_LEFT = 0x40000 | DC_DPAD2_LEFT,
EMU_AXIS_DPAD2_RIGHT = 0x40000 | DC_DPAD2_RIGHT,
EMU_AXIS_NONE = 0,
};

View File

@ -53,7 +53,7 @@ bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed)
if (_input_detected != nullptr && _detecting_button
&& os_GetSeconds() >= _detection_start_time && pressed)
{
_input_detected(code);
_input_detected(code, false, false);
_input_detected = nullptr;
return true;
}
@ -92,44 +92,30 @@ bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed)
if (pressed && !gui_is_open())
settings.input.fastForwardMode = !settings.input.fastForwardMode && !settings.online;
break;
case EMU_BTN_TRIGGER_LEFT:
case DC_AXIS_LT:
lt[port] = pressed ? 255 : 0;
break;
case EMU_BTN_TRIGGER_RIGHT:
case DC_AXIS_RT:
rt[port] = pressed ? 255 : 0;
break;
case EMU_BTN_ANA_UP:
case EMU_BTN_ANA_DOWN:
{
if (pressed)
digitalToAnalogState[port] |= key;
else
digitalToAnalogState[port] &= ~key;
const u32 upDown = digitalToAnalogState[port] & (EMU_BTN_ANA_UP | EMU_BTN_ANA_DOWN);
if (upDown == 0 || upDown == (EMU_BTN_ANA_UP | EMU_BTN_ANA_DOWN))
joyy[port] = 0;
else if (upDown == EMU_BTN_ANA_UP)
joyy[port] = -128;
else
joyy[port] = 127;
}
case DC_AXIS_UP:
case DC_AXIS_DOWN:
buttonToAnalogInput<DC_AXIS_UP, DIGANA_UP, DIGANA_DOWN>(port, key, pressed, joyy[port]);
break;
case EMU_BTN_ANA_LEFT:
case EMU_BTN_ANA_RIGHT:
{
if (pressed)
digitalToAnalogState[port] |= key;
else
digitalToAnalogState[port] &= ~key;
const u32 leftRight = digitalToAnalogState[port] & (EMU_BTN_ANA_LEFT | EMU_BTN_ANA_RIGHT);
if (leftRight == 0 || leftRight == (EMU_BTN_ANA_LEFT | EMU_BTN_ANA_RIGHT))
joyx[port] = 0;
else if (leftRight == EMU_BTN_ANA_LEFT)
joyx[port] = -128;
else
joyx[port] = 127;
}
case DC_AXIS_LEFT:
case DC_AXIS_RIGHT:
buttonToAnalogInput<DC_AXIS_LEFT, DIGANA_LEFT, DIGANA_RIGHT>(port, key, pressed, joyx[port]);
break;
case DC_AXIS2_UP:
case DC_AXIS2_DOWN:
buttonToAnalogInput<DC_AXIS2_UP, DIGANA2_UP, DIGANA2_DOWN>(port, key, pressed, joyry[port]);
break;
case DC_AXIS2_LEFT:
case DC_AXIS2_RIGHT:
buttonToAnalogInput<DC_AXIS2_LEFT, DIGANA2_LEFT, DIGANA2_RIGHT>(port, key, pressed, joyrx[port]);
break;
default:
return false;
}
@ -158,70 +144,71 @@ bool GamepadDevice::gamepad_btn_input(u32 code, bool pressed)
return rc;
}
//
// value must be >= -32768 and <= 32767 for full axes
// and 0 to 32767 for half axes/triggers
//
bool GamepadDevice::gamepad_axis_input(u32 code, int value)
{
s32 v;
if (input_mapper->get_axis_inverted(0, code))
v = (get_axis_min_value(code) + get_axis_range(code) - value) * 255 / get_axis_range(code) - 128;
else
v = (value - get_axis_min_value(code)) * 255 / get_axis_range(code) - 128; //-128 ... +127 range
if (_input_detected != NULL && !_detecting_button
&& os_GetSeconds() >= _detection_start_time && (v >= 64 || v <= -64))
bool positive = value >= 0;
if (_input_detected != NULL && _detecting_axis
&& os_GetSeconds() >= _detection_start_time && std::abs(value) >= 16384)
{
_input_detected(code);
_input_detected = NULL;
_input_detected(code, true, positive);
_input_detected = nullptr;
return true;
}
if (!input_mapper || _maple_port < 0 || _maple_port > 4)
return false;
auto handle_axis = [&](u32 port, DreamcastKey key)
auto handle_axis = [&](u32 port, DreamcastKey key, int v)
{
if ((int)key < 0x10000)
if ((key & DC_BTN_GROUP_MASK) == DC_AXIS_TRIGGERS) // Triggers
{
kcode[port] |= key | (key << 1);
if (v <= -64)
kcode[port] &= ~key;
else if (v >= 64)
kcode[port] &= ~(key << 1);
// printf("Mapped to %d %d %d\n",mo,kcode[port]&mo,kcode[port]&(mo*2));
}
else if (((int)key >> 16) == 1) // Triggers
{
//printf("T-AXIS %d Mapped to %d -> %d\n",key, value, v + 128);
//printf("T-AXIS %d Mapped to %d -> %d\n", key, value, std::min(std::abs(v) >> 7, 255));
if (key == DC_AXIS_LT)
lt[port] = (u8)(v + 128);
lt[port] = std::min(std::abs(v) >> 7, 255);
else if (key == DC_AXIS_RT)
rt[port] = (u8)(v + 128);
rt[port] = std::min(std::abs(v) >> 7, 255);
else
return false;
}
else if (((int)key >> 16) == 2) // Analog axes
else if ((key & DC_BTN_GROUP_MASK) == DC_AXIS_STICKS) // Analog axes
{
//printf("AXIS %d Mapped to %d -> %d\n", key, value, v);
s8 *this_axis;
s8 *other_axis;
int axisDirection = -1;
switch (key)
{
case DC_AXIS_X:
case DC_AXIS_RIGHT:
axisDirection = 1;
//no break
case DC_AXIS_LEFT:
this_axis = &joyx[port];
other_axis = &joyy[port];
break;
case DC_AXIS_Y:
case DC_AXIS_DOWN:
axisDirection = 1;
//no break
case DC_AXIS_UP:
this_axis = &joyy[port];
other_axis = &joyx[port];
break;
case DC_AXIS_X2:
case DC_AXIS2_RIGHT:
axisDirection = 1;
//no break
case DC_AXIS2_LEFT:
this_axis = &joyrx[port];
other_axis = &joyry[port];
break;
case DC_AXIS_Y2:
case DC_AXIS2_DOWN:
axisDirection = 1;
//no break
case DC_AXIS2_UP:
this_axis = &joyry[port];
other_axis = &joyrx[port];
break;
@ -231,20 +218,31 @@ bool GamepadDevice::gamepad_axis_input(u32 code, int value)
}
// Radial dead zone
// FIXME compute both axes at the same time
v = std::min(127, std::abs(v >> 8));
if ((float)(v * v + *other_axis * *other_axis) < input_mapper->dead_zone * input_mapper->dead_zone * 128.f * 128.f)
{
*this_axis = 0;
*other_axis = 0;
}
else
*this_axis = (s8)v;
*this_axis = v * axisDirection;
}
else if (((int)key >> 16) == 4) // Map triggers to digital buttons
else if (key != EMU_BTN_NONE && key <= DC_BTN_RELOAD) // Map triggers to digital buttons
{
if (v <= -64)
kcode[port] |= (key & ~0x40000); // button released
else if (v >= 64)
kcode[port] &= ~(key & ~0x40000); // button pressed
//printf("B-AXIS %d Mapped to %d -> %d\n", key, value, v);
// TODO hysteresis?
if (std::abs(v) < 16384)
kcode[port] |= key; // button released
else
kcode[port] &= ~key; // button pressed
}
else if ((key & DC_BTN_GROUP_MASK) == EMU_BUTTONS) // Map triggers to emu buttons
{
// TODO hysteresis?
if (std::abs(v) < 16384)
gamepad_btn_input(key, false); // button released
else
gamepad_btn_input(key, true); // button pressed
}
else
return false;
@ -257,41 +255,24 @@ bool GamepadDevice::gamepad_axis_input(u32 code, int value)
{
for (u32 port = 0; port < 4; port++)
{
DreamcastKey key = input_mapper->get_axis_id(port, code);
rc = handle_axis(port, key) || rc;
DreamcastKey key = input_mapper->get_axis_id(port, code, !positive);
handle_axis(port, key, 0);
key = input_mapper->get_axis_id(port, code, positive);
rc = handle_axis(port, key, value) || rc;
}
}
else
{
DreamcastKey key = input_mapper->get_axis_id(0, code);
rc = handle_axis(_maple_port, key);
DreamcastKey key = input_mapper->get_axis_id(0, code, !positive);
// Reset opposite axis to 0
handle_axis(_maple_port, key, 0);
key = input_mapper->get_axis_id(0, code, positive);
rc = handle_axis(_maple_port, key, value);
}
return rc;
}
int GamepadDevice::get_axis_min_value(u32 axis) {
auto it = axis_min_values.find(axis);
if (it == axis_min_values.end()) {
load_axis_min_max(axis);
it = axis_min_values.find(axis);
if (it == axis_min_values.end())
return INT_MIN;
}
return it->second;
}
unsigned int GamepadDevice::get_axis_range(u32 axis) {
auto it = axis_ranges.find(axis);
if (it == axis_ranges.end()) {
load_axis_min_max(axis);
it = axis_ranges.find(axis);
if (it == axis_ranges.end())
return UINT_MAX;
}
return it->second;
}
std::string GamepadDevice::make_mapping_filename(bool instance)
{
std::string mapping_file = api_name() + "_" + name();
@ -321,11 +302,13 @@ void GamepadDevice::verify_or_create_system_mappings()
if (!file_exists(arcade_path))
{
resetMappingToDefault(true, true);
save_mapping(2);
input_mapper->ClearMappings();
}
if (!file_exists(dc_path))
{
resetMappingToDefault(false, false);
save_mapping(0);
input_mapper->ClearMappings();
}
@ -454,6 +437,7 @@ void GamepadDevice::detect_btn_input(input_detected_cb button_pressed)
{
_input_detected = button_pressed;
_detecting_button = true;
_detecting_axis = false;
_detection_start_time = os_GetSeconds() + 0.2;
}
@ -461,6 +445,15 @@ void GamepadDevice::detect_axis_input(input_detected_cb axis_moved)
{
_input_detected = axis_moved;
_detecting_button = false;
_detecting_axis = true;
_detection_start_time = os_GetSeconds() + 0.2;
}
void GamepadDevice::detectButtonOrAxisInput(input_detected_cb input_changed)
{
_input_detected = input_changed;
_detecting_button = true;
_detecting_axis = true;
_detection_start_time = os_GetSeconds() + 0.2;
}

View File

@ -29,7 +29,7 @@
class GamepadDevice
{
public:
typedef void (*input_detected_cb)(u32 code);
typedef void (*input_detected_cb)(u32 code, bool analog, bool positive);
const std::string& api_name() { return _api_name; }
const std::string& name() { return _name; }
@ -37,13 +37,13 @@ public:
virtual void set_maple_port(int port) { _maple_port = port; }
const std::string& unique_id() { return _unique_id; }
virtual bool gamepad_btn_input(u32 code, bool pressed);
bool gamepad_axis_input(u32 code, int value);
virtual bool gamepad_axis_input(u32 code, int value);
virtual ~GamepadDevice() = default;
virtual void detect_btn_input(input_detected_cb button_pressed);
void detect_btn_input(input_detected_cb button_pressed);
void detect_axis_input(input_detected_cb axis_moved);
virtual void cancel_detect_input()
{
void detectButtonOrAxisInput(input_detected_cb input_changed);
void cancel_detect_input() {
_input_detected = nullptr;
}
std::shared_ptr<InputMapping> get_input_mapping() { return input_mapper; }
@ -71,6 +71,10 @@ public:
static void load_system_mappings(int system = settings.platform.system);
bool find_mapping(int system);
virtual void resetMappingToDefault(bool arcade, bool gamepad) {
input_mapper = getDefaultMapping();
}
protected:
GamepadDevice(int maple_port, const char *api_name, bool remappable = true)
: _api_name(api_name), _maple_port(maple_port), _input_detected(nullptr), _remappable(remappable),
@ -87,25 +91,50 @@ protected:
return std::make_shared<IdentityInputMapping>();
}
virtual void load_axis_min_max(u32 axis) {}
bool is_detecting_input() { return _input_detected != nullptr; }
std::string _name;
std::string _unique_id;
std::shared_ptr<InputMapping> input_mapper;
std::map<u32, int> axis_min_values;
std::map<u32, unsigned int> axis_ranges;
bool _rumble_enabled = true;
private:
int get_axis_min_value(u32 axis);
unsigned int get_axis_range(u32 axis);
std::string make_mapping_filename(bool instance = false);
std::string make_mapping_filename(bool instance, int system);
enum DigAnalog {
DIGANA_LEFT = 1 << 0,
DIGANA_RIGHT = 1 << 1,
DIGANA_UP = 1 << 2,
DIGANA_DOWN = 1 << 3,
DIGANA2_LEFT = 1 << 4,
DIGANA2_RIGHT = 1 << 5,
DIGANA2_UP = 1 << 6,
DIGANA2_DOWN = 1 << 7,
};
template<DreamcastKey DcNegDir, DigAnalog NegDir, DigAnalog PosDir>
void buttonToAnalogInput(u32 port, DreamcastKey key, bool pressed, s8& joystick)
{
DigAnalog axis = key == DcNegDir ? NegDir : PosDir;
if (pressed)
digitalToAnalogState[port] |= axis;
else
digitalToAnalogState[port] &= ~axis;
const u32 socd = digitalToAnalogState[port] & (NegDir | PosDir);
if (socd == 0 || socd == (NegDir | PosDir))
joystick = 0;
else if (socd == NegDir)
joystick = -128;
else
joystick = 127;
}
std::string _api_name;
int _maple_port;
bool _detecting_button = false;
bool _detecting_axis = false;
double _detection_start_time = 0.0;
input_detected_cb _input_detected;
bool _remappable;

View File

@ -37,8 +37,8 @@ public:
set_button(DC_DPAD_LEFT, 80);
set_button(DC_DPAD_RIGHT, 79);
set_button(DC_BTN_START, 40); // Return
set_button(EMU_BTN_TRIGGER_LEFT, 9); // F
set_button(EMU_BTN_TRIGGER_RIGHT, 25); // V
set_button(DC_AXIS_LT, 9); // F
set_button(DC_AXIS_RT, 25); // V
set_button(EMU_BTN_MENU, 43); // TAB
set_button(EMU_BTN_FFORWARD, 44); // Space

View File

@ -48,12 +48,12 @@ button_list[] =
{ EMU_BTN_ESCAPE, "emulator", "btn_escape" },
{ EMU_BTN_MENU, "emulator", "btn_menu" },
{ EMU_BTN_FFORWARD, "emulator", "btn_fforward" },
{ EMU_BTN_TRIGGER_LEFT, "compat", "btn_trigger_left" },
{ EMU_BTN_TRIGGER_RIGHT, "compat", "btn_trigger_right" },
{ EMU_BTN_ANA_UP, "compat", "btn_analog_up" },
{ EMU_BTN_ANA_DOWN, "compat", "btn_analog_down" },
{ EMU_BTN_ANA_LEFT, "compat", "btn_analog_left" },
{ EMU_BTN_ANA_RIGHT, "compat", "btn_analog_right" },
{ DC_AXIS_LT, "compat", "btn_trigger_left" },
{ DC_AXIS_RT, "compat", "btn_trigger_right" },
{ DC_AXIS_UP, "compat", "btn_analog_up" },
{ DC_AXIS_DOWN, "compat", "btn_analog_down" },
{ DC_AXIS_LEFT, "compat", "btn_analog_left" },
{ DC_AXIS_RIGHT, "compat", "btn_analog_right" },
{ DC_BTN_RELOAD, "dreamcast", "reload" },
};
@ -67,37 +67,49 @@ static struct
}
axis_list[] =
{
{ DC_AXIS_X, "dreamcast", "axis_x", "compat", "axis_x_inverted" },
{ DC_AXIS_Y, "dreamcast", "axis_y", "compat", "axis_y_inverted" },
// v3
{ DC_AXIS_LEFT, "", "axis_left", "", "" },
{ DC_AXIS_RIGHT, "", "axis_right", "", "" },
{ DC_AXIS_UP, "", "axis_up", "", "" },
{ DC_AXIS_DOWN, "", "axis_down", "", "" },
{ DC_AXIS2_LEFT, "", "axis2_left", "", "" },
{ DC_AXIS2_RIGHT, "", "axis2_right", "", "" },
{ DC_AXIS2_UP, "", "axis2_up", "", "" },
{ DC_AXIS2_DOWN, "", "axis2_down", "", "" },
{ DC_AXIS_LT, "dreamcast", "axis_trigger_left", "compat", "axis_trigger_left_inverted" },
{ DC_AXIS_RT, "dreamcast", "axis_trigger_right", "compat", "axis_trigger_right_inverted" },
{ DC_AXIS_X2, "dreamcast", "axis_right_x", "compat", "axis_right_x_inverted" },
{ DC_AXIS_Y2, "dreamcast", "axis_right_y", "compat", "axis_right_y_inverted" },
{ EMU_AXIS_DPAD1_X, "compat", "axis_dpad1_x", "compat", "axis_dpad1_x_inverted" },
{ EMU_AXIS_DPAD1_Y, "compat", "axis_dpad1_y", "compat", "axis_dpad1_y_inverted" },
{ EMU_AXIS_DPAD2_X, "compat", "axis_dpad2_x", "compat", "axis_dpad2_x_inverted" },
{ EMU_AXIS_DPAD2_Y, "compat", "axis_dpad2_y", "compat", "axis_dpad2_y_inverted" },
{ EMU_AXIS_BTN_A, "compat", "axis_btn_a", "compat", "axis_btn_a_inverted" },
{ EMU_AXIS_BTN_B, "compat", "axis_btn_b", "compat", "axis_btn_b_inverted" },
{ EMU_AXIS_BTN_C, "compat", "axis_btn_c", "compat", "axis_btn_c_inverted" },
{ EMU_AXIS_BTN_D, "compat", "axis_btn_d", "compat", "axis_btn_d_inverted" },
{ EMU_AXIS_BTN_X, "compat", "axis_btn_x", "compat", "axis_btn_x_inverted" },
{ EMU_AXIS_BTN_Y, "compat", "axis_btn_y", "compat", "axis_btn_y_inverted" },
{ EMU_AXIS_BTN_Z, "compat", "axis_btn_z", "compat", "axis_btn_z_inverted" },
{ EMU_AXIS_BTN_START, "compat", "axis_btn_start", "compat", "axis_btn_start_inverted" },
{ EMU_AXIS_DPAD_LEFT, "compat", "axis_dpad1_left", "compat", "axis_dpad1_left_inverted" },
{ EMU_AXIS_DPAD_RIGHT, "compat", "axis_dpad1_right", "compat", "axis_dpad1_right_inverted" },
{ EMU_AXIS_DPAD_UP, "compat", "axis_dpad1_up", "compat", "axis_dpad1_up_inverted" },
{ EMU_AXIS_DPAD_DOWN, "compat", "axis_dpad1_down", "compat", "axis_dpad1_down_inverted" },
{ EMU_AXIS_DPAD2_LEFT, "compat", "axis_dpad2_left", "compat", "axis_dpad2_left_inverted" },
{ EMU_AXIS_DPAD2_RIGHT, "compat", "axis_dpad2_right", "compat", "axis_dpad2_right_inverted" },
{ EMU_AXIS_DPAD2_UP, "compat", "axis_dpad2_up", "compat", "axis_dpad2_up_inverted" },
{ EMU_AXIS_DPAD2_DOWN, "compat", "axis_dpad2_down", "compat", "axis_dpad2_down_inverted" }
// legacy (v2)
{ DC_AXIS_RIGHT, "dreamcast", "axis_x", "compat", "axis_x_inverted" },
{ DC_AXIS_DOWN, "dreamcast", "axis_y", "compat", "axis_y_inverted" },
{ DC_AXIS2_RIGHT, "dreamcast", "axis_right_x", "compat", "axis_right_x_inverted" },
{ DC_AXIS2_DOWN, "dreamcast", "axis_right_y", "compat", "axis_right_y_inverted" },
{ DC_DPAD_LEFT, "compat", "axis_dpad1_x", "compat", "axis_dpad1_x_inverted" },
{ DC_DPAD_UP, "compat", "axis_dpad1_y", "compat", "axis_dpad1_y_inverted" },
{ DC_DPAD2_LEFT, "compat", "axis_dpad2_x", "compat", "axis_dpad2_x_inverted" },
{ DC_DPAD2_UP, "compat", "axis_dpad2_y", "compat", "axis_dpad2_y_inverted" },
{ DC_BTN_A, "compat", "axis_btn_a", "compat", "axis_btn_a_inverted" },
{ DC_BTN_B, "compat", "axis_btn_b", "compat", "axis_btn_b_inverted" },
{ DC_BTN_C, "compat", "axis_btn_c", "compat", "axis_btn_c_inverted" },
{ DC_BTN_D, "compat", "axis_btn_d", "compat", "axis_btn_d_inverted" },
{ DC_BTN_X, "compat", "axis_btn_x", "compat", "axis_btn_x_inverted" },
{ DC_BTN_Y, "compat", "axis_btn_y", "compat", "axis_btn_y_inverted" },
{ DC_BTN_Z, "compat", "axis_btn_z", "compat", "axis_btn_z_inverted" },
{ DC_BTN_START, "compat", "axis_btn_start", "compat", "axis_btn_start_inverted" },
{ DC_DPAD_LEFT, "compat", "axis_dpad1_left", "compat", "axis_dpad1_left_inverted" },
{ DC_DPAD_RIGHT, "compat", "axis_dpad1_right", "compat", "axis_dpad1_right_inverted" },
{ DC_DPAD_UP, "compat", "axis_dpad1_up", "compat", "axis_dpad1_up_inverted" },
{ DC_DPAD_DOWN, "compat", "axis_dpad1_down", "compat", "axis_dpad1_down_inverted" },
{ DC_DPAD2_LEFT, "compat", "axis_dpad2_left", "compat", "axis_dpad2_left_inverted" },
{ DC_DPAD2_RIGHT, "compat", "axis_dpad2_right", "compat", "axis_dpad2_right_inverted" },
{ DC_DPAD2_UP, "compat", "axis_dpad2_up", "compat", "axis_dpad2_up_inverted" },
{ DC_DPAD2_DOWN, "compat", "axis_dpad2_down", "compat", "axis_dpad2_down_inverted" },
};
std::map<std::string, std::shared_ptr<InputMapping>> InputMapping::loaded_mappings;
void InputMapping::clear_button(u32 port, DreamcastKey id, u32 code)
void InputMapping::clear_button(u32 port, DreamcastKey id)
{
if (id != EMU_BTN_NONE)
{
@ -107,8 +119,8 @@ void InputMapping::clear_button(u32 port, DreamcastKey id, u32 code)
if (code == (u32)-1)
break;
buttons[port][code] = EMU_BTN_NONE;
dirty = true;
}
dirty = true;
}
}
@ -116,52 +128,52 @@ void InputMapping::set_button(u32 port, DreamcastKey id, u32 code)
{
if (id != EMU_BTN_NONE)
{
while (true)
{
u32 code = get_button_code(port, id);
if (code == (u32)-1)
break;
buttons[port][code] = EMU_BTN_NONE;
}
clear_button(port, id);
buttons[port][code] = id;
dirty = true;
}
}
void InputMapping::clear_axis(u32 port, DreamcastKey id, u32 code)
void InputMapping::clear_axis(u32 port, DreamcastKey id)
{
if (id != EMU_AXIS_NONE)
{
while (true)
{
u32 code = get_axis_code(port, id);
if (code == (u32)-1)
std::pair<u32, bool> code = get_axis_code(port, id);
if (code.first == (u32)-1)
break;
axes[port][code] = EMU_AXIS_NONE;
dirty = true;
}
dirty = true;
}
}
void InputMapping::set_axis(u32 port, DreamcastKey id, u32 code, bool is_inverted)
void InputMapping::set_axis(u32 port, DreamcastKey id, u32 code, bool positive)
{
if (id != EMU_AXIS_NONE)
{
while (true)
{
u32 code = get_axis_code(port, id);
if (code == (u32)-1)
break;
axes[port][code] = EMU_AXIS_NONE;
}
axes[port][code] = id;
axes_inverted[port][code] = is_inverted;
clear_axis(port, id);
axes[port][std::make_pair(code, positive)] = id;
dirty = true;
}
}
using namespace emucfg;
static DreamcastKey getKeyId(const std::string& name)
{
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
if (name == button_list[i].option)
return button_list[i].id;
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
if (name == axis_list[i].option)
return axis_list[i].id;
WARN_LOG(INPUT, "Unknown key/axis: %s", name.c_str());
return EMU_BTN_NONE;
}
void InputMapping::load(FILE* fp)
{
ConfigFile mf;
@ -172,10 +184,76 @@ void InputMapping::load(FILE* fp)
int dz = mf.get_int("emulator", "dead_zone", 10);
dz = std::min(dz, 100);
dz = std::max(dz, 0);
version = mf.get_int("emulator", "version", 1);
this->dead_zone = (float)dz / 100.f;
version = mf.get_int("emulator", "version", 1);
if (version < 3)
{
loadv1(mf);
return;
}
int bindIndex = 0;
while (true)
{
std::string s = mf.get("digital", "bind" + std::to_string(bindIndex++), "");
if (s.empty())
break;
size_t colon = s.find(':');
if (colon == std::string::npos || colon == 0)
{
WARN_LOG(INPUT, "Invalid bind entry: %s", s.c_str());
break;
}
u32 code = atoi(s.substr(0, colon).c_str());
std::string key = s.substr(colon + 1);
if (key.empty())
{
WARN_LOG(INPUT, "Invalid bind entry: %s", s.c_str());
break;
}
int port = 0;
if (key[key.size() - 1] >= '1' && key[key.size() - 1] <= '3')
{
port = key[key.size() - 1] - '0';
key = key.substr(0, key.size() - 1);
}
DreamcastKey id = getKeyId(key);
set_button(port, id, code);
}
bindIndex = 0;
while (true)
{
std::string s = mf.get("analog", "bind" + std::to_string(bindIndex++), "");
if (s.empty())
break;
size_t colon = s.find(':');
if (colon == std::string::npos || colon < 2)
{
WARN_LOG(INPUT, "Invalid bind entry: %s", s.c_str());
break;
}
bool positive = s[colon - 1] == '+';
u32 code = atoi(s.substr(0, colon - 1).c_str());
std::string key = s.substr(colon + 1);
if (key.empty())
{
WARN_LOG(INPUT, "Invalid bind entry: %s", s.c_str());
break;
}
int port = 0;
if (key[key.size() - 1] >= '1' && key[key.size() - 1] <= '3')
{
port = key[key.size() - 1] - '0';
key = key.substr(0, key.size() - 1);
}
DreamcastKey id = getKeyId(key);
set_axis(port, id, code, positive);
}
dirty = false;
}
void InputMapping::loadv1(ConfigFile& mf)
{
for (int port = 0; port < 4; port++)
{
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
@ -187,7 +265,15 @@ void InputMapping::load(FILE* fp)
option = button_list[i].option + std::to_string(port);
int button_code = mf.get_int(button_list[i].section, option, -1);
if (button_code >= 0)
this->set_button(port, button_list[i].id, button_code);
{
DreamcastKey id = button_list[i].id;
// remap service and test buttons to their new aliases
if (id == DC_BTN_C)
id = DC_DPAD2_UP;
else if (id == DC_BTN_Z)
id = DC_DPAD2_DOWN;
this->set_button(port, id, button_code);
}
}
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
@ -204,11 +290,22 @@ void InputMapping::load(FILE* fp)
option = axis_list[i].option_inverted;
else
option = axis_list[i].option_inverted + std::to_string(port);
this->set_axis(port, axis_list[i].id, axis_code, mf.get_bool(axis_list[i].section_inverted, option, false));
bool inverted = mf.get_bool(axis_list[i].section_inverted, option, false);
this->set_axis(port, axis_list[i].id, axis_code, !inverted);
if (axis_list[i].id == DC_AXIS_RIGHT)
this->set_axis(port, DC_AXIS_LEFT, axis_code, inverted);
else if (axis_list[i].id == DC_AXIS_DOWN)
this->set_axis(port, DC_AXIS_UP, axis_code, inverted);
else if (axis_list[i].id == DC_AXIS2_RIGHT)
this->set_axis(port, DC_AXIS2_LEFT, axis_code, inverted);
else if (axis_list[i].id == DC_AXIS2_DOWN)
this->set_axis(port, DC_AXIS2_UP, axis_code, inverted);
}
}
}
dirty = false;
dirty = true;
}
u32 InputMapping::get_button_code(u32 port, DreamcastKey key)
@ -221,14 +318,14 @@ u32 InputMapping::get_button_code(u32 port, DreamcastKey key)
return -1;
}
u32 InputMapping::get_axis_code(u32 port, DreamcastKey key)
std::pair<u32, bool> InputMapping::get_axis_code(u32 port, DreamcastKey key)
{
for (auto& it : axes[port])
{
if (it.second == key)
return it.first;
}
return -1;
return std::make_pair((u32)-1, false);
}
void InputMapping::ClearMappings()
@ -251,6 +348,31 @@ std::shared_ptr<InputMapping> InputMapping::LoadMapping(const char *name)
std::fclose(fp);
loaded_mappings[name] = mapping;
if (mapping->is_dirty())
{
// Make a backup of the current mapping file
FILE *out = nowide::fopen((path + ".save").c_str(), "w");
if (out == nullptr)
WARN_LOG(INPUT, "Can't backup controller mapping file %s", path.c_str());
else
{
fp = nowide::fopen(path.c_str(), "r");
if (fp != nullptr)
{
u8 buf[4096];
while (true)
{
size_t n = fread(buf, 1, sizeof(buf), fp);
if (n <= 0)
break;
fwrite(buf, 1, n, out);
}
std::fclose(fp);
}
std::fclose(out);
}
}
return mapping;
}
@ -259,6 +381,19 @@ void InputMapping::set_dirty()
dirty = true;
}
static const char *getKeyName(DreamcastKey key)
{
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
if (key == button_list[i].id)
return button_list[i].option.c_str();
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
if (key == axis_list[i].id)
return axis_list[i].option.c_str();
ERROR_LOG(INPUT, "Invalid key %x", key);
die("Invalid key");
return "?";
}
bool InputMapping::save(const char *name)
{
if (!dirty)
@ -277,46 +412,41 @@ bool InputMapping::save(const char *name)
mf.set("emulator", "mapping_name", this->name);
mf.set_int("emulator", "dead_zone", (int)std::round(this->dead_zone * 100.f));
mf.set_int("emulator", "version", version);
mf.set_int("emulator", "version", 3);
int bindIndex = 0;
for (int port = 0; port < 4; port++)
{
for (u32 i = 0; i < ARRAY_SIZE(button_list); i++)
for (const auto& pair : buttons[port])
{
if (pair.second == EMU_BTN_NONE)
continue;
const char *keyName = getKeyName(pair.second);
std::string option;
if (port == 0)
option = button_list[i].option;
option = keyName;
else
option = button_list[i].option + std::to_string(port);
for (auto& it : buttons[port])
{
if (it.second == button_list[i].id)
mf.set_int(button_list[i].section, option, (int)it.first);
}
option = keyName + std::to_string(port);
mf.set("digital", "bind" + std::to_string(bindIndex), std::to_string(pair.first) + ":" + option);
bindIndex++;
}
for (u32 i = 0; i < ARRAY_SIZE(axis_list); i++)
}
bindIndex = 0;
for (int port = 0; port < 4; port++)
{
for (const auto& pair : axes[port])
{
if (pair.second == EMU_BTN_NONE)
continue;
const char *keyName = getKeyName(pair.second);
std::string option;
if (port == 0)
option = axis_list[i].option;
option = keyName;
else
option = axis_list[i].option + std::to_string(port);
std::string option_inverted;
if (port == 0)
option_inverted = axis_list[i].option_inverted;
else
option_inverted = axis_list[i].option_inverted + std::to_string(port);
for (auto& it : axes[port])
{
if (it.second == axis_list[i].id)
{
mf.set_int(axis_list[i].section, option, (int)it.first);
mf.set_bool(axis_list[i].section_inverted, option_inverted, axes_inverted[port][it.first]);
}
}
option = keyName + std::to_string(port);
mf.set("analog", "bind" + std::to_string(bindIndex),
std::to_string(pair.first.first) + (pair.first.second ? "+" : "-") + ":" + option);
bindIndex++;
}
}
mf.save(fp);

View File

@ -24,6 +24,10 @@
#include <map>
#include <memory>
namespace emucfg {
struct ConfigFile;
}
class InputMapping
{
public:
@ -35,13 +39,12 @@ public:
{
buttons[port] = other.buttons[port];
axes[port] = other.axes[port];
axes_inverted[port] = other.axes_inverted[port];
}
}
std::string name;
float dead_zone = 0.1f;
int version = 2;
int version = 3;
DreamcastKey get_button_id(u32 port, u32 code)
{
@ -51,31 +54,24 @@ public:
else
return EMU_BTN_NONE;
}
void clear_button(u32 port, DreamcastKey id, u32 code);
void clear_button(u32 port, DreamcastKey id);
void set_button(u32 port, DreamcastKey id, u32 code);
void set_button(DreamcastKey id, u32 code) { set_button(0, id, code); }
u32 get_button_code(u32 port, DreamcastKey key);
DreamcastKey get_axis_id(u32 port, u32 code)
DreamcastKey get_axis_id(u32 port, u32 code, bool pos)
{
auto it = axes[port].find(code);
auto it = axes[port].find(std::make_pair(code, pos));
if (it != axes[port].end())
return it->second;
else
return EMU_AXIS_NONE;
}
bool get_axis_inverted(u32 port, u32 code)
{
auto it = axes_inverted[port].find(code);
if (it != axes_inverted[port].end())
return it->second;
else
return false;
}
u32 get_axis_code(u32 port, DreamcastKey key);
void clear_axis(u32 port, DreamcastKey id, u32 code);
void set_axis(u32 port, DreamcastKey id, u32 code, bool inverted);
void set_axis(DreamcastKey id, u32 code, bool inverted) { set_axis(0, id, code, inverted); }
std::pair<u32, bool> get_axis_code(u32 port, DreamcastKey key);
void clear_axis(u32 port, DreamcastKey id);
void set_axis(u32 port, DreamcastKey id, u32 code, bool positive);
void set_axis(DreamcastKey id, u32 code, bool positive) { set_axis(0, id, code, positive); }
void load(FILE* fp);
bool save(const char *name);
@ -92,9 +88,10 @@ protected:
bool dirty = false;
private:
void loadv1(emucfg::ConfigFile& mf);
std::map<u32, DreamcastKey> buttons[4];
std::map<u32, DreamcastKey> axes[4];
std::map<u32, bool> axes_inverted[4];
std::map<std::pair<u32, bool>, DreamcastKey> axes[4];
static std::map<std::string, std::shared_ptr<InputMapping>> loaded_mappings;
};
@ -108,11 +105,15 @@ public:
for (int i = 0; i < 32; i++)
set_button(0, (DreamcastKey)(1 << i), 1 << i);
set_axis(0, DC_AXIS_X, DC_AXIS_X, false);
set_axis(0, DC_AXIS_Y, DC_AXIS_Y, false);
set_axis(0, DC_AXIS_LT, DC_AXIS_LT, false);
set_axis(0, DC_AXIS_RT, DC_AXIS_RT, false);
set_axis(0, DC_AXIS_X2, DC_AXIS_X2, false);
set_axis(0, DC_AXIS_Y2, DC_AXIS_Y2, false);
set_axis(0, DC_AXIS_LEFT, DC_AXIS_LEFT, true);
set_axis(0, DC_AXIS_RIGHT, DC_AXIS_RIGHT, true);
set_axis(0, DC_AXIS_UP, DC_AXIS_UP, true);
set_axis(0, DC_AXIS_DOWN, DC_AXIS_DOWN, true);
set_axis(0, DC_AXIS_LT, DC_AXIS_LT, true);
set_axis(0, DC_AXIS_RT, DC_AXIS_RT, true);
set_axis(0, DC_AXIS2_LEFT, DC_AXIS2_LEFT, true);
set_axis(0, DC_AXIS2_RIGHT, DC_AXIS2_RIGHT, true);
set_axis(0, DC_AXIS2_UP, DC_AXIS2_UP, true);
set_axis(0, DC_AXIS2_DOWN, DC_AXIS2_DOWN, true);
}
};

View File

@ -5,6 +5,7 @@
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <climits>
class DefaultEvdevInputMapping : public InputMapping
{
@ -24,12 +25,16 @@ public:
set_button(DC_DPAD_RIGHT, BTN_DPAD_RIGHT);
set_button(EMU_BTN_MENU, BTN_SELECT);
set_axis(DC_AXIS_X, ABS_X, false);
set_axis(DC_AXIS_Y, ABS_Y, false);
set_axis(DC_AXIS_LT, ABS_Z, false);
set_axis(DC_AXIS_RT, ABS_RZ, false);
set_axis(DC_AXIS_X2, ABS_RX, false);
set_axis(DC_AXIS_Y2, ABS_RY, false);
set_axis(DC_AXIS_LEFT, ABS_X, false);
set_axis(DC_AXIS_RIGHT, ABS_X, true);
set_axis(DC_AXIS_UP, ABS_Y, false);
set_axis(DC_AXIS_DOWN, ABS_Y, true);
set_axis(DC_AXIS_LT, ABS_Z, true);
set_axis(DC_AXIS_RT, ABS_RZ, true);
set_axis(DC_AXIS2_LEFT, ABS_RX, false);
set_axis(DC_AXIS2_RIGHT, ABS_RX, true);
set_axis(DC_AXIS_UP, ABS_RY, false);
set_axis(DC_AXIS_DOWN, ABS_RY, true);
}
};
@ -225,8 +230,32 @@ public:
GamepadDevice::Unregister(gamepad);
}
protected:
void load_axis_min_max(u32 axis) override
private:
int get_axis_min_value(u32 axis)
{
auto it = axis_min_values.find(axis);
if (it == axis_min_values.end()) {
load_axis_min_max(axis);
it = axis_min_values.find(axis);
if (it == axis_min_values.end())
return INT_MIN;
}
return it->second;
}
unsigned int get_axis_range(u32 axis)
{
auto it = axis_ranges.find(axis);
if (it == axis_ranges.end()) {
load_axis_min_max(axis);
it = axis_ranges.find(axis);
if (it == axis_ranges.end())
return UINT_MAX;
}
return it->second;
}
void load_axis_min_max(u32 axis)
{
struct input_absinfo abs;
if (ioctl(_fd, EVIOCGABS(axis), &abs))
@ -241,7 +270,6 @@ protected:
DEBUG_LOG(INPUT, "evdev: range of axis %d is from %d to %d", axis, axis_min_values[axis], axis_min_values[axis] + axis_ranges[axis]);
}
private:
void read_input()
{
update_rumble();
@ -256,7 +284,12 @@ private:
break;
case EV_ABS:
gamepad_axis_input(ie.code, ie.value);
{
// TODO no way to distinguish between half and full axes
int min = get_axis_min_value(ie.code);
unsigned range = get_axis_range(ie.code);
gamepad_axis_input(ie.code, (ie.value - min) * 65535 / range - 32768);
}
break;
}
}
@ -303,6 +336,8 @@ private:
int _rumble_effect_id = -1;
float vib_inclination = 0;
double vib_stop_time = 0;
std::map<u32, int> axis_min_values;
std::map<u32, unsigned int> axis_ranges;
static std::map<std::string, std::shared_ptr<EvdevGamepadDevice>> evdev_gamepads;
};

View File

@ -1,159 +0,0 @@
#include "joystick.h"
#if defined(USE_JOYSTICK)
#include <fcntl.h>
#include <linux/joystick.h>
#include <sys/types.h>
#include <unistd.h>
const u32 joystick_map_btn_usb[JOYSTICK_MAP_SIZE] = { DC_BTN_Y, DC_BTN_B, DC_BTN_A, DC_BTN_X, 0, 0, 0, 0, 0, DC_BTN_START };
const u32 joystick_map_axis_usb[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, 0, 0, 0, 0, 0, 0, 0, 0 };
const u32 joystick_map_btn_xbox360[JOYSTICK_MAP_SIZE] = { DC_BTN_A, DC_BTN_B, DC_BTN_X, DC_BTN_Y, 0, 0, 0, DC_BTN_START, 0, 0 };
const u32 joystick_map_axis_xbox360[JOYSTICK_MAP_SIZE] = { DC_AXIS_X, DC_AXIS_Y, DC_AXIS_LT, 0, 0, DC_AXIS_RT, DC_DPAD_LEFT, DC_DPAD_UP, 0, 0 };
const u32* joystick_map_btn = joystick_map_btn_usb;
const u32* joystick_map_axis = joystick_map_axis_usb;
int input_joystick_init(const char* device)
{
int axis_count = 0;
int button_count = 0;
char name[128] = "Unknown";
printf("joystick: Trying to open device at '%s'\n", device);
int fd = open(device, O_RDONLY);
if(fd >= 0)
{
fcntl(fd, F_SETFL, O_NONBLOCK);
ioctl(fd, JSIOCGAXES, &axis_count);
ioctl(fd, JSIOCGBUTTONS, &button_count);
ioctl(fd, JSIOCGNAME(sizeof(name)), &name);
printf("joystick: Found '%s' with %d axis and %d buttons at '%s'.\n", name, axis_count, button_count, device);
if (strcmp(name, "Microsoft X-Box 360 pad") == 0 ||
strcmp(name, "Xbox Gamepad (userspace driver)") == 0 ||
strcmp(name, "Xbox 360 Wireless Receiver (XBOX)") == 0)
{
joystick_map_btn = joystick_map_btn_xbox360;
joystick_map_axis = joystick_map_axis_xbox360;
printf("joystick: Using Xbox 360 map\n");
}
}
else
{
perror("joystick open");
}
return fd;
}
bool input_joystick_handle(int fd, u32 port)
{
// Joystick must be connected
if(fd < 0) {
return false;
}
struct js_event JE;
while(read(fd, &JE, sizeof(JE)) == sizeof(JE))
if (JE.number < JOYSTICK_MAP_SIZE)
{
switch(JE.type & ~JS_EVENT_INIT)
{
case JS_EVENT_AXIS:
{
u32 mt = joystick_map_axis[JE.number] >> 16;
u32 mo = joystick_map_axis[JE.number] & 0xFFFF;
//printf("AXIS %d,%d\n",JE.number,JE.value);
s8 v=(s8)(JE.value/256); //-127 ... + 127 range
if (mt == 0)
{
kcode[port] |= mo;
kcode[port] |= mo*2;
if (v<-64)
{
kcode[port] &= ~mo;
}
else if (v>64)
{
kcode[port] &= ~(mo*2);
}
//printf("Mapped to %d %d %d\n",mo,kcode[port]&mo,kcode[port]&(mo*2));
}
else if (mt == 1)
{
if (v >= 0)
{
v++; //up to 255
}
//printf("AXIS %d,%d Mapped to %d %d %d\n",JE.number,JE.value,mo,v,v+127);
if (mo == 0)
{
lt[port] = (v + 127);
}
else if (mo == 1)
{
rt[port] = (v + 127);
}
}
else if (mt == 2)
{
// printf("AXIS %d,%d Mapped to %d %d [%d]",JE.number,JE.value,mo,v);
if (mo == 0)
{
joyx[port] = v;
}
else if (mo == 1)
{
joyy[port] = v;
}
}
}
break;
case JS_EVENT_BUTTON:
{
u32 mt = joystick_map_btn[JE.number] >> 16;
u32 mo = joystick_map_btn[JE.number] & 0xFFFF;
// printf("BUTTON %d,%d\n",JE.number,JE.value);
if (mt == 0)
{
// printf("Mapped to %d\n",mo);
if (JE.value)
{
kcode[port] &= ~mo;
}
else
{
kcode[port] |= mo;
}
}
else if (mt == 1)
{
// printf("Mapped to %d %d\n",mo,JE.value?255:0);
if (mo==0)
{
lt[port] = JE.value ? 255 : 0;
}
else if (mo==1)
{
rt[port] = JE.value ? 255 : 0;
}
}
}
break;
}
}
return true;
}
#endif

View File

@ -1,10 +0,0 @@
#pragma once
#include "types.h"
#define JOYSTICK_DEVICE_STRING "/dev/input/js%d"
#define JOYSTICK_DEFAULT_DEVICE_ID -1
#define JOYSTICK_MAP_SIZE 32
extern int input_joystick_init(const char* device);
extern bool input_joystick_handle(int fd, u32 port);

View File

@ -35,16 +35,6 @@
#include "evdev.h"
#endif
#if defined(USE_JOYSTICK)
#include "cfg/cfg.h"
#include "joystick.h"
#endif
#if defined(USE_JOYSTICK)
/* legacy joystick input */
static int joystick_fd = -1; // Joystick file descriptor
#endif
#ifdef USE_BREAKPAD
#include "client/linux/handler/exception_handler.h"
#endif
@ -55,21 +45,6 @@ void os_SetupInput()
input_evdev_init();
#endif
#if defined(USE_JOYSTICK)
int joystick_device_id = cfgLoadInt("input", "joystick_device_id", JOYSTICK_DEFAULT_DEVICE_ID);
if (joystick_device_id < 0) {
INFO_LOG(INPUT, "Legacy Joystick input disabled by config.");
}
else
{
int joystick_device_length = snprintf(NULL, 0, JOYSTICK_DEVICE_STRING, joystick_device_id);
char* joystick_device = (char*)malloc(joystick_device_length + 1);
sprintf(joystick_device, JOYSTICK_DEVICE_STRING, joystick_device_id);
joystick_fd = input_joystick_init(joystick_device);
free(joystick_device);
}
#endif
#if defined(SUPPORT_X11)
input_x11_init();
#endif
@ -81,10 +56,6 @@ void os_SetupInput()
void UpdateInputState()
{
#if defined(USE_JOYSTICK)
input_joystick_handle(joystick_fd, 0);
#endif
#if defined(USE_EVDEV)
input_evdev_handle();
#endif

View File

@ -23,6 +23,9 @@
namespace ggpo
{
constexpr u32 BTN_TRIGGER_LEFT = DC_BTN_RELOAD << 1;
constexpr u32 BTN_TRIGGER_RIGHT = DC_BTN_RELOAD << 2;
static void getLocalInput(MapleInputState inputState[4])
{
for (int player = 0; player < 4; player++)
@ -55,6 +58,7 @@ static void getLocalInput(MapleInputState inputState[4])
#include <xxhash.h>
#include "imgui/imgui.h"
#include "miniupnp.h"
#include "hw/naomi/naomi_cart.h"
//#define SYNC_TEST 1
@ -77,6 +81,7 @@ static time_point<steady_clock> lastFrameTime;
static int msPerFrameAvg;
static bool _endOfFrame;
static MiniUPnP miniupnp;
static int analogAxes;
struct MemPages
{
@ -367,9 +372,27 @@ void startSession(int localPort, int localPlayerNum)
player.player_num = (1 - localPlayerNum) + 1;
result = ggpo_add_player(ggpoSession, &player, &remotePlayer);
synchronized = true;
analogAxes = 0;
NOTICE_LOG(NETWORK, "GGPO synctest session started");
#else
u32 inputSize = sizeof(kcode[0]) + config::GGPOAnalogAxes;
if (settings.platform.system == DC_PLATFORM_DREAMCAST)
analogAxes = config::GGPOAnalogAxes;
else
{
analogAxes = 0;
if (NaomiGameInputs != nullptr)
{
for (const auto& axis : NaomiGameInputs->axes)
{
if (axis.name == nullptr)
break;
if (axis.type == Full)
analogAxes = std::max(analogAxes, (int)axis.axis + 1);
}
}
NOTICE_LOG(NETWORK, "GGPO: Using %d full analog axes", analogAxes);
}
u32 inputSize = sizeof(kcode[0]) + analogAxes;
GGPOErrorCode result = ggpo_start_session(&ggpoSession, &cb, config::Settings::instance().getGameId().c_str(), MAX_PLAYERS, inputSize, localPort);
if (result != GGPO_OK)
{
@ -447,7 +470,7 @@ void getInput(MapleInputState inputState[4])
for (int player = 0; player < 4; player++)
inputState[player] = {};
u32 inputSize = sizeof(u32) + config::GGPOAnalogAxes;
u32 inputSize = sizeof(u32) + analogAxes;
std::vector<u8> inputs(inputSize * MAX_PLAYERS);
// should not call any callback
GGPOErrorCode error = ggpo_synchronize_input(ggpoSession, (void *)&inputs[0], inputs.size(), nullptr);
@ -461,14 +484,14 @@ void getInput(MapleInputState inputState[4])
{
MapleInputState& state = inputState[player];
state.kcode = ~(*(u32 *)&inputs[player * inputSize]);
if (config::GGPOAnalogAxes > 0)
if (analogAxes > 0)
{
state.fullAxes[PJAI_X1] = inputs[player * inputSize + 4];
if (config::GGPOAnalogAxes == 2)
if (analogAxes >= 2)
state.fullAxes[PJAI_Y1] = inputs[player * inputSize + 5];
}
state.halfAxes[PJTI_R] = (state.kcode & EMU_BTN_TRIGGER_RIGHT) == 0 ? 255 : 0;
state.halfAxes[PJTI_L] = (state.kcode & EMU_BTN_TRIGGER_LEFT) == 0 ? 255 : 0;
state.halfAxes[PJTI_R] = (state.kcode & BTN_TRIGGER_RIGHT) == 0 ? 255 : 0;
state.halfAxes[PJTI_L] = (state.kcode & BTN_TRIGGER_LEFT) == 0 ? 255 : 0;
}
}
@ -508,24 +531,21 @@ bool nextFrame()
// may call save_game_state
do {
u32 input = ~kcode[localPlayerNum];
if (settings.platform.system != DC_PLATFORM_NAOMI)
{
if (rt[localPlayerNum] >= 64)
input |= EMU_BTN_TRIGGER_RIGHT;
else
input &= ~EMU_BTN_TRIGGER_RIGHT;
if (lt[localPlayerNum] >= 64)
input |= EMU_BTN_TRIGGER_LEFT;
else
input &= ~EMU_BTN_TRIGGER_LEFT;
}
u32 inputSize = sizeof(input) + config::GGPOAnalogAxes;
if (rt[localPlayerNum] >= 64)
input |= BTN_TRIGGER_RIGHT;
else
input &= ~BTN_TRIGGER_RIGHT;
if (lt[localPlayerNum] >= 64)
input |= BTN_TRIGGER_LEFT;
else
input &= ~BTN_TRIGGER_LEFT;
u32 inputSize = sizeof(input) + analogAxes;
std::vector<u8> allInput(inputSize);
*(u32 *)&allInput[0] = input;
if (config::GGPOAnalogAxes > 0)
if (analogAxes > 0)
{
allInput[4] = joyx[localPlayerNum];
if (config::GGPOAnalogAxes == 2)
if (analogAxes >= 2)
allInput[5] = joyy[localPlayerNum];
}
GGPOErrorCode result = ggpo_add_local_input(ggpoSession, localPlayer, &allInput[0], inputSize);

View File

@ -566,6 +566,13 @@ static void gui_display_commands()
ImGui::End();
}
inline static void header(const char *title)
{
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.f, 0.5f)); // Left
ImGui::ButtonEx(title, ImVec2(-1, 0), ImGuiButtonFlags_Disabled);
ImGui::PopStyleVar();
}
const char *maple_device_types[] = { "None", "Sega Controller", "Light Gun", "Keyboard", "Mouse", "Twin Stick", "Ascii Stick" };
const char *maple_expansion_device_types[] = { "None", "Sega VMU", "Purupuru", "Microphone" };
@ -630,46 +637,100 @@ static const char *maple_expansion_device_name(MapleDeviceType type)
}
const char *maple_ports[] = { "None", "A", "B", "C", "D", "All" };
const DreamcastKey button_keys[] = {
DC_BTN_START, DC_BTN_A, DC_BTN_B, DC_BTN_X, DC_BTN_Y, DC_DPAD_UP, DC_DPAD_DOWN, DC_DPAD_LEFT, DC_DPAD_RIGHT,
EMU_BTN_MENU, EMU_BTN_ESCAPE, EMU_BTN_FFORWARD, EMU_BTN_TRIGGER_LEFT, EMU_BTN_TRIGGER_RIGHT,
DC_BTN_C, DC_BTN_D, DC_BTN_Z, DC_DPAD2_UP, DC_DPAD2_DOWN, DC_DPAD2_LEFT, DC_DPAD2_RIGHT,
DC_BTN_RELOAD,
EMU_BTN_ANA_UP, EMU_BTN_ANA_DOWN, EMU_BTN_ANA_LEFT, EMU_BTN_ANA_RIGHT
struct Mapping {
DreamcastKey key;
const char *name;
};
const char *button_names[] = {
"Start", "A", "B", "X", "Y", "DPad Up", "DPad Down", "DPad Left", "DPad Right",
"Menu", "Exit", "Fast-forward", "Left Trigger", "Right Trigger",
"C", "D", "Z", "Right Dpad Up", "Right DPad Down", "Right DPad Left", "Right DPad Right",
"Reload",
"Left Stick Up", "Left Stick Down", "Left Stick Left", "Left Stick Right"
const Mapping dcButtons[] = {
{ EMU_BTN_NONE, "Directions" },
{ DC_DPAD_UP, "Up" },
{ DC_DPAD_DOWN, "Down" },
{ DC_DPAD_LEFT, "Left" },
{ DC_DPAD_RIGHT, "Right" },
{ DC_AXIS_UP, "Thumbstick Up" },
{ DC_AXIS_DOWN, "Thumbstick Down" },
{ DC_AXIS_LEFT, "Thumbstick Left" },
{ DC_AXIS_RIGHT, "Thumbstick Right" },
{ DC_DPAD2_UP, "DPad2 Up" },
{ DC_DPAD2_DOWN, "DPad2 Down" },
{ DC_DPAD2_LEFT, "DPad2 Left" },
{ DC_DPAD2_RIGHT, "DPad2 Right" },
{ EMU_BTN_NONE, "Buttons" },
{ DC_BTN_A, "A" },
{ DC_BTN_B, "B" },
{ DC_BTN_X, "X" },
{ DC_BTN_Y, "Y" },
{ DC_BTN_C, "C" },
{ DC_BTN_D, "D" },
{ DC_BTN_Z, "Z" },
{ EMU_BTN_NONE, "Triggers" },
{ DC_AXIS_LT, "Left Trigger" },
{ DC_AXIS_RT, "Right Trigger" },
{ EMU_BTN_NONE, "System Buttons" },
{ DC_BTN_START, "Start" },
{ DC_BTN_RELOAD, "Reload" },
{ EMU_BTN_NONE, "Emulator" },
{ EMU_BTN_MENU, "Menu" },
{ EMU_BTN_ESCAPE, "Exit" },
{ EMU_BTN_FFORWARD, "Fast-forward" },
{ EMU_BTN_NONE, nullptr }
};
const char *arcade_button_names[] = {
"Start", "Button 1", "Button 2", "Button 3", "Button 4", "Up", "Down", "Left", "Right",
"Menu", "Exit", "Fast-forward", "N/A", "N/A",
"Service", "Coin", "Test", "Button 5", "Button 6", "Button 7", "Button 8",
"Reload",
"N/A", "N/A", "N/A", "N/A"
const Mapping arcadeButtons[] = {
{ EMU_BTN_NONE, "Directions" },
{ DC_DPAD_UP, "Up" },
{ DC_DPAD_DOWN, "Down" },
{ DC_DPAD_LEFT, "Left" },
{ DC_DPAD_RIGHT, "Right" },
{ DC_AXIS_UP, "Thumbstick Up" },
{ DC_AXIS_DOWN, "Thumbstick Down" },
{ DC_AXIS_LEFT, "Thumbstick Left" },
{ DC_AXIS_RIGHT, "Thumbstick Right" },
{ DC_AXIS2_UP, "R.Thumbstick Up" },
{ DC_AXIS2_DOWN, "R.Thumbstick Down" },
{ DC_AXIS2_LEFT, "R.Thumbstick Left" },
{ DC_AXIS2_RIGHT, "R.Thumbstick Right" },
{ EMU_BTN_NONE, "Buttons" },
{ DC_BTN_A, "Button 1" },
{ DC_BTN_B, "Button 2" },
{ DC_BTN_C, "Button 3" },
{ DC_BTN_X, "Button 4" },
{ DC_BTN_Y, "Button 5" },
{ DC_BTN_Z, "Button 6" },
{ DC_DPAD2_LEFT, "Button 7" },
{ DC_DPAD2_RIGHT, "Button 8" },
// { DC_DPAD2_RIGHT, "Button 9" }, // TODO
{ EMU_BTN_NONE, "Triggers" },
{ DC_AXIS_LT, "Left Trigger" },
{ DC_AXIS_RT, "Right Trigger" },
{ EMU_BTN_NONE, "System Buttons" },
{ DC_BTN_START, "Start" },
{ DC_BTN_RELOAD, "Reload" },
{ DC_BTN_D, "Coin" },
{ DC_DPAD2_UP, "Service" },
{ DC_DPAD2_DOWN, "Test" },
{ EMU_BTN_NONE, "Emulator" },
{ EMU_BTN_MENU, "Menu" },
{ EMU_BTN_ESCAPE, "Exit" },
{ EMU_BTN_FFORWARD, "Fast-forward" },
{ EMU_BTN_NONE, nullptr }
};
const DreamcastKey axis_keys[] = {
DC_AXIS_X, DC_AXIS_Y, DC_AXIS_LT, DC_AXIS_RT, DC_AXIS_X2, DC_AXIS_Y2, EMU_AXIS_DPAD1_X, EMU_AXIS_DPAD1_Y,
EMU_AXIS_DPAD2_X, EMU_AXIS_DPAD2_Y, EMU_AXIS_BTN_START, EMU_AXIS_BTN_A, EMU_AXIS_BTN_B, EMU_AXIS_BTN_X, EMU_AXIS_BTN_Y,
EMU_AXIS_BTN_C, EMU_AXIS_BTN_D, EMU_AXIS_BTN_Z, EMU_AXIS_DPAD2_UP, EMU_AXIS_DPAD2_DOWN, EMU_AXIS_DPAD2_LEFT, EMU_AXIS_DPAD2_RIGHT
};
const char *axis_names[] = {
"Left Stick X", "Left Stick Y", "Left Trigger", "Right Trigger", "Right Stick X", "Right Stick Y", "DPad X", "DPad Y",
"Right DPad X", "Right DPad Y", "Start", "A", "B", "X", "Y",
"C", "D", "Z", "N/A", "N/A", "N/A", "N/A"
};
const char *arcade_axis_names[] = {
"Left Stick X", "Left Stick Y", "Left Trigger", "Right Trigger", "Right Stick X", "Right Stick Y", "DPad X", "DPad Y",
"Right DPad X", "Right DPad Y", "Start", "Button 1", "Button 2", "Button 3", "Button 4",
"Service", "Coin", "Test", "Button 5", "Button 6", "Button 7", "Button 8"
};
static_assert(ARRAY_SIZE(button_keys) == ARRAY_SIZE(button_names), "invalid size");
static_assert(ARRAY_SIZE(button_keys) == ARRAY_SIZE(arcade_button_names), "invalid size");
static_assert(ARRAY_SIZE(axis_keys) == ARRAY_SIZE(axis_names), "invalid size");
static_assert(ARRAY_SIZE(axis_keys) == ARRAY_SIZE(arcade_axis_names), "invalid size");
static MapleDeviceType maple_expansion_device_type_from_index(int idx)
{
@ -689,20 +750,66 @@ static MapleDeviceType maple_expansion_device_type_from_index(int idx)
static std::shared_ptr<GamepadDevice> mapped_device;
static u32 mapped_code;
static bool analogAxis;
static bool positiveDirection;
static double map_start_time;
static bool arcade_button_mode;
static u32 gamepad_port;
static void detect_input_popup(int index, bool analog)
static void unmapControl(const std::shared_ptr<InputMapping>& mapping, u32 gamepad_port, DreamcastKey key)
{
mapping->clear_button(gamepad_port, key);
mapping->clear_axis(gamepad_port, key);
}
static DreamcastKey getOppositeDirectionKey(DreamcastKey key)
{
switch (key)
{
case DC_DPAD_UP:
return DC_DPAD_DOWN;
case DC_DPAD_DOWN:
return DC_DPAD_UP;
case DC_DPAD_LEFT:
return DC_DPAD_RIGHT;
case DC_DPAD_RIGHT:
return DC_DPAD_LEFT;
case DC_DPAD2_UP:
return DC_DPAD2_DOWN;
case DC_DPAD2_DOWN:
return DC_DPAD2_UP;
case DC_DPAD2_LEFT:
return DC_DPAD2_RIGHT;
case DC_DPAD2_RIGHT:
return DC_DPAD2_LEFT;
case DC_AXIS_UP:
return DC_AXIS_DOWN;
case DC_AXIS_DOWN:
return DC_AXIS_UP;
case DC_AXIS_LEFT:
return DC_AXIS_RIGHT;
case DC_AXIS_RIGHT:
return DC_AXIS_LEFT;
case DC_AXIS2_UP:
return DC_AXIS2_DOWN;
case DC_AXIS2_DOWN:
return DC_AXIS2_UP;
case DC_AXIS2_LEFT:
return DC_AXIS2_RIGHT;
case DC_AXIS2_RIGHT:
return DC_AXIS2_LEFT;
default:
return EMU_BTN_NONE;
}
}
static void detect_input_popup(const Mapping *mapping)
{
ImVec2 padding = ImVec2(20 * scaling, 20 * scaling);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, padding);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, padding);
if (ImGui::BeginPopupModal(analog ? "Map Axis" : "Map Button", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
if (ImGui::BeginPopupModal("Map Control", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
{
ImGui::Text("Waiting for %s '%s'...", analog ? "axis" : "button",
analog ? arcade_button_mode ? arcade_axis_names[index] : axis_names[index]
: arcade_button_mode ? arcade_button_names[index] : button_names[index]);
ImGui::Text("Waiting for control '%s'...", mapping->name);
double now = os_GetSeconds();
ImGui::Text("Time out in %d s", (int)(5 - (now - map_start_time)));
if (mapped_code != (u32)-1)
@ -710,17 +817,21 @@ static void detect_input_popup(int index, bool analog)
std::shared_ptr<InputMapping> input_mapping = mapped_device->get_input_mapping();
if (input_mapping != NULL)
{
if (analog)
unmapControl(input_mapping, gamepad_port, mapping->key);
if (analogAxis)
{
u32 previous_mapping = input_mapping->get_axis_code(gamepad_port, axis_keys[index]);
bool inverted = false;
if (previous_mapping != (u32)-1)
inverted = input_mapping->get_axis_inverted(gamepad_port, previous_mapping);
// FIXME Allow inverted to be set
input_mapping->set_axis(gamepad_port, axis_keys[index], mapped_code, inverted);
input_mapping->set_axis(gamepad_port, mapping->key, mapped_code, positiveDirection);
DreamcastKey opposite = getOppositeDirectionKey(mapping->key);
// Map the axis opposite direction to the corresponding opposite dc button or axis,
// but only if the opposite direction axis isn't used and the dc button or axis isn't mapped.
if (opposite != EMU_BTN_NONE
&& input_mapping->get_axis_id(gamepad_port, mapped_code, !positiveDirection) == EMU_BTN_NONE
&& input_mapping->get_axis_code(gamepad_port, opposite).first == (u32)-1
&& input_mapping->get_button_code(gamepad_port, opposite) == (u32)-1)
input_mapping->set_axis(gamepad_port, opposite, mapped_code, !positiveDirection);
}
else
input_mapping->set_button(gamepad_port, button_keys[index], mapped_code);
input_mapping->set_button(gamepad_port, mapping->key, mapped_code);
}
mapped_device = NULL;
ImGui::CloseCurrentPopup();
@ -735,6 +846,32 @@ static void detect_input_popup(int index, bool analog)
ImGui::PopStyleVar(2);
}
static void displayLabelOrCode(const char *label, u32 code, const char *suffix = "")
{
if (label != nullptr)
ImGui::Text("%s%s", label, suffix);
else
ImGui::Text("[%d]%s", code, suffix);
}
static void displayMappedControl(const std::shared_ptr<GamepadDevice>& gamepad, DreamcastKey key)
{
std::shared_ptr<InputMapping> input_mapping = gamepad->get_input_mapping();
u32 code = input_mapping->get_button_code(gamepad_port, key);
if (code != (u32)-1)
{
displayLabelOrCode(gamepad->get_button_name(code), code);
return;
}
std::pair<u32, bool> pair = input_mapping->get_axis_code(gamepad_port, key);
code = pair.first;
if (code != (u32)-1)
{
displayLabelOrCode(gamepad->get_axis_name(code), code, pair.second ? "+" : "-");
return;
}
}
static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamepad)
{
fullScreenWindow(true);
@ -742,7 +879,7 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
if (ImGui::BeginPopupModal("Controller Mapping", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
{
const ImGuiStyle& style = ImGui::GetStyle();
const float width = (ImGui::GetIO().DisplaySize.x - insetLeft - insetRight - style.ItemSpacing.x) / 2 - style.WindowBorderSize - style.WindowPadding.x;
const float width = ImGui::GetIO().DisplaySize.x - insetLeft - insetRight - (style.WindowBorderSize + style.WindowPadding.x) * 2;
const float col_width = (width - style.GrabMinSize - style.ItemSpacing.x
- (ImGui::CalcTextSize("Map").x + style.FramePadding.x * 2.0f + style.ItemSpacing.x)
- (ImGui::CalcTextSize("Unmap").x + style.FramePadding.x * 2.0f + style.ItemSpacing.x)) / 2;
@ -774,35 +911,76 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
}
ImGui::PopItemWidth();
}
ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize("Dreamcast Controls").x
- ImGui::GetStyle().FramePadding.x * 3.0f - ImGui::GetStyle().ItemSpacing.x * 3.0f);
float comboWidth = ImGui::CalcTextSize("Dreamcast Controls").x + ImGui::GetStyle().ItemSpacing.x * 3.0f + ImGui::GetFontSize();
ImGui::SameLine(0, ImGui::GetContentRegionAvail().x - comboWidth - ImGui::GetStyle().ItemSpacing.x - 100 * scaling * 2);
ImGui::AlignTextToFramePadding();
if (ImGui::Button("Reset...", ImVec2(100 * scaling, 30 * scaling)))
ImGui::OpenPopup("Confirm Reset");
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20 * scaling, 20 * scaling));
if (ImGui::BeginPopupModal("Confirm Reset", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
{
ImGui::Text("Are you sure you want to reset the mappings to default?");
static bool hitbox;
if (arcade_button_mode)
{
ImGui::Text("Controller Type:");
if (ImGui::RadioButton("Gamepad", !hitbox))
hitbox = false;
ImGui::SameLine();
if (ImGui::RadioButton("Arcade / Hit Box", hitbox))
hitbox = true;
}
ImGui::NewLine();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(20 * scaling, ImGui::GetStyle().ItemSpacing.y));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10 * scaling, 10 * scaling));
if (ImGui::Button("Yes"))
{
gamepad->resetMappingToDefault(arcade_button_mode, !hitbox);
gamepad->save_mapping(map_system);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No"))
ImGui::CloseCurrentPopup();
ImGui::PopStyleVar(2);;
ImGui::EndPopup();
}
ImGui::PopStyleVar(1);;
ImGui::SameLine();
const char* items[] = { "Dreamcast Controls", "Arcade Controls" };
static int item_current_map_idx = 0;
static int last_item_current_map_idx = 2;
// Here our selection data is an index.
ImGui::PushItemWidth(ImGui::CalcTextSize("Dreamcast Controls").x + ImGui::GetStyle().ItemSpacing.x * 2.0f * 3);
ImGui::Combo("", &item_current_map_idx, items, IM_ARRAYSIZE(items));
ImGui::PushItemWidth(comboWidth);
// Make the combo height the same as the Done and Reset buttons
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(ImGui::GetStyle().FramePadding.x, (30 * scaling - ImGui::GetFontSize()) / 2));
ImGui::Combo("##arcadeMode", &item_current_map_idx, items, IM_ARRAYSIZE(items));
ImGui::PopStyleVar();
ImGui::PopItemWidth();
if (item_current_map_idx != last_item_current_map_idx)
{
gamepad->save_mapping(map_system);
}
const Mapping *systemMapping = dcButtons;
if (item_current_map_idx == 0)
{
arcade_button_mode = false;
map_system = DC_PLATFORM_DREAMCAST;
systemMapping = dcButtons;
}
else if (item_current_map_idx == 1)
{
arcade_button_mode = true;
map_system = DC_PLATFORM_NAOMI;
systemMapping = arcadeButtons;
}
if (item_current_map_idx != last_item_current_map_idx)
@ -814,55 +992,55 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
}
char key_id[32];
ImGui::BeginGroup();
ImGui::Text(" Buttons ");
ImGui::BeginChildFrame(ImGui::GetID("buttons"), ImVec2(width, 0), ImGuiWindowFlags_None);
ImGui::Columns(3, "bindings", false);
ImGui::SetColumnWidth(0, col_width);
ImGui::SetColumnWidth(1, col_width);
ImGui::BeginChildFrame(ImGui::GetID("buttons"), ImVec2(0, 0), ImGuiWindowFlags_None);
gamepad->find_mapping(map_system);
for (u32 j = 0; j < ARRAY_SIZE(button_keys); j++)
for (; systemMapping->name != nullptr; systemMapping++)
{
sprintf(key_id, "key_id%d", j);
if (systemMapping->key == EMU_BTN_NONE)
{
ImGui::Columns(1, nullptr, false);
header(systemMapping->name);
ImGui::Columns(3, "bindings", false);
ImGui::SetColumnWidth(0, col_width);
ImGui::SetColumnWidth(1, col_width);
continue;
}
sprintf(key_id, "key_id%d", systemMapping->key);
ImGui::PushID(key_id);
const char *btn_name = arcade_button_mode ? arcade_button_names[j] : button_names[j];
const char *game_btn_name = GetCurrentGameButtonName(button_keys[j]);
if (game_btn_name != nullptr)
ImGui::Text("%s - %s", btn_name, game_btn_name);
const char *game_btn_name = GetCurrentGameButtonName(systemMapping->key);
if (game_btn_name == nullptr)
game_btn_name = GetCurrentGameAxisName(systemMapping->key);
if (game_btn_name != nullptr && game_btn_name[0] != '\0')
ImGui::Text("%s - %s", systemMapping->name, game_btn_name);
else
ImGui::Text("%s", btn_name);
ImGui::Text("%s", systemMapping->name);
ImGui::NextColumn();
u32 code = input_mapping->get_button_code(gamepad_port, button_keys[j]);
if (code != (u32)-1)
{
const char *label = gamepad->get_button_name(code);
if (label != nullptr)
ImGui::Text("%s", label);
else
ImGui::Text("[%d]", code);
}
displayMappedControl(gamepad, systemMapping->key);
ImGui::NextColumn();
if (ImGui::Button("Map"))
{
map_start_time = os_GetSeconds();
ImGui::OpenPopup("Map Button");
ImGui::OpenPopup("Map Control");
mapped_device = gamepad;
mapped_code = -1;
gamepad->detect_btn_input([](u32 code)
gamepad->detectButtonOrAxisInput([](u32 code, bool analog, bool positive)
{
mapped_code = code;
analogAxis = analog;
positiveDirection = positive;
});
}
detect_input_popup(j, false);
detect_input_popup(systemMapping);
ImGui::SameLine();
if (ImGui::Button("Unmap"))
{
input_mapping = gamepad->get_input_mapping();
input_mapping->clear_button(gamepad_port, button_keys[j], j);
unmapControl(input_mapping, gamepad_port, systemMapping->key);
}
ImGui::NextColumn();
ImGui::PopID();
@ -872,66 +1050,6 @@ static void controller_mapping_popup(const std::shared_ptr<GamepadDevice>& gamep
windowDragScroll();
ImGui::EndChildFrame();
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text(" Analog Axes ");
ImGui::BeginChildFrame(ImGui::GetID("analog"), ImVec2(width, 0), ImGuiWindowFlags_None);
ImGui::Columns(3, "anabindings", false);
ImGui::SetColumnWidth(0, col_width);
ImGui::SetColumnWidth(1, col_width);
for (u32 j = 0; j < ARRAY_SIZE(axis_keys); j++)
{
sprintf(key_id, "axis_id%d", j);
ImGui::PushID(key_id);
const char *axis_name = arcade_button_mode ? arcade_axis_names[j] : axis_names[j];
const char *game_axis_name = GetCurrentGameAxisName(axis_keys[j]);
if (game_axis_name != nullptr)
ImGui::Text("%s - %s", axis_name, game_axis_name);
else
ImGui::Text("%s", axis_name);
ImGui::NextColumn();
u32 code = input_mapping->get_axis_code(gamepad_port, axis_keys[j]);
if (code != (u32)-1)
{
const char *label = gamepad->get_axis_name(code);
if (label != nullptr)
ImGui::Text("%s", label);
else
ImGui::Text("[%d]", code);
}
ImGui::NextColumn();
if (ImGui::Button("Map"))
{
map_start_time = os_GetSeconds();
ImGui::OpenPopup("Map Axis");
mapped_device = gamepad;
mapped_code = -1;
gamepad->detect_axis_input([](u32 code)
{
mapped_code = code;
});
}
detect_input_popup(j, true);
ImGui::SameLine();
if (ImGui::Button("Unmap"))
{
input_mapping = gamepad->get_input_mapping();
input_mapping->clear_axis(gamepad_port, axis_keys[j], j);
}
ImGui::NextColumn();
ImGui::PopID();
}
ImGui::Columns(1, nullptr, false);
scrollWhenDraggingOnVoid();
windowDragScroll();
ImGui::EndChildFrame();
ImGui::EndGroup();
ImGui::EndPopup();
}
ImGui::PopStyleVar();
@ -1019,13 +1137,6 @@ static void contentpath_warning_popup()
}
}
inline static void header(const char *title)
{
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.f, 0.5f)); // Left
ImGui::ButtonEx(title, ImVec2(-1, 0), ImGuiButtonFlags_Disabled);
ImGui::PopStyleVar();
}
static void gui_display_settings()
{
static bool maple_devices_changed;

View File

@ -3,6 +3,7 @@
#include "sdl.h"
#include "rend/gui.h"
template<bool Arcade = false, bool Gamepad = false>
class DefaultInputMapping : public InputMapping
{
public:
@ -15,23 +16,26 @@ public:
set_button(DC_BTN_X, 3);
set_button(DC_BTN_START, 9);
set_axis(DC_AXIS_X, 0, false);
set_axis(DC_AXIS_Y, 1, false);
set_axis(DC_AXIS_X2, 2, false);
set_axis(DC_AXIS_Y2, 3, false);
set_axis(0, DC_AXIS_LEFT, 0, false);
set_axis(0, DC_AXIS_RIGHT, 0, true);
set_axis(0, DC_AXIS_UP, 1, false);
set_axis(0, DC_AXIS_DOWN, 1, true);
set_axis(0, DC_AXIS2_LEFT, 2, false);
set_axis(0, DC_AXIS2_RIGHT, 2, true);
set_axis(0, DC_AXIS2_UP, 3, false);
set_axis(0, DC_AXIS2_DOWN, 3, true);
dirty = false;
}
DefaultInputMapping(int joystick_idx) : DefaultInputMapping()
DefaultInputMapping(SDL_GameController *sdlController) : DefaultInputMapping()
{
if (SDL_IsGameController(joystick_idx))
if (sdlController != nullptr)
{
SDL_GameController *sdl_controller = SDL_GameControllerOpen(joystick_idx);
name = SDL_GameControllerName(sdl_controller);
name = SDL_GameControllerName(sdlController);
INFO_LOG(INPUT, "SDL: using SDL game controller mappings for '%s'", name.c_str());
auto map_button = [&](SDL_GameControllerButton sdl_btn, DreamcastKey dc_btn) {
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForButton(sdl_controller, sdl_btn);
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForButton(sdlController, sdl_btn);
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON)
set_button(dc_btn, bind.value.button);
else if (bind.bindType == SDL_CONTROLLER_BINDTYPE_HAT)
@ -57,21 +61,8 @@ public:
set_button(dc_btn, ((bind.value.hat.hat + 1) << 8) | dir);
}
};
map_button(SDL_CONTROLLER_BUTTON_A, DC_BTN_A);
map_button(SDL_CONTROLLER_BUTTON_B, DC_BTN_B);
map_button(SDL_CONTROLLER_BUTTON_X, DC_BTN_X);
map_button(SDL_CONTROLLER_BUTTON_Y, DC_BTN_Y);
map_button(SDL_CONTROLLER_BUTTON_START, DC_BTN_START);
map_button(SDL_CONTROLLER_BUTTON_DPAD_UP, DC_DPAD_UP);
map_button(SDL_CONTROLLER_BUTTON_DPAD_DOWN, DC_DPAD_DOWN);
map_button(SDL_CONTROLLER_BUTTON_DPAD_LEFT, DC_DPAD_LEFT);
map_button(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, DC_DPAD_RIGHT);
map_button(SDL_CONTROLLER_BUTTON_BACK, EMU_BTN_MENU);
map_button(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, DC_BTN_C); // service
map_button(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, DC_BTN_Z); // test
auto map_axis = [&](SDL_GameControllerAxis sdl_axis, DreamcastKey dc_axis) {
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForAxis(sdl_controller, sdl_axis);
auto map_axis = [&](SDL_GameControllerAxis sdl_axis, DreamcastKey dc_axis, bool positive) {
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForAxis(sdlController, sdl_axis);
if (bind.bindType != SDL_CONTROLLER_BINDTYPE_AXIS)
return false;
@ -79,19 +70,76 @@ public:
const char *s = SDL_GameControllerGetStringForAxis(sdl_axis);
if (s != nullptr && s[strlen(s) - 1] == '~')
invert_axis = true;
set_axis(dc_axis, bind.value.axis, invert_axis);
set_axis(dc_axis, bind.value.axis, invert_axis ^ positive);
return true;
};
map_axis(SDL_CONTROLLER_AXIS_LEFTX, DC_AXIS_X);
map_axis(SDL_CONTROLLER_AXIS_LEFTY, DC_AXIS_Y);
map_axis(SDL_CONTROLLER_AXIS_RIGHTX, DC_AXIS_X2);
map_axis(SDL_CONTROLLER_AXIS_RIGHTY, DC_AXIS_Y2);
if (!map_axis(SDL_CONTROLLER_AXIS_TRIGGERLEFT, DC_AXIS_LT))
map_button(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, EMU_BTN_TRIGGER_LEFT);
if (!map_axis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, DC_AXIS_RT))
map_button(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, EMU_BTN_TRIGGER_RIGHT);
SDL_GameControllerClose(sdl_controller);
if (Arcade)
{
if (Gamepad)
{
// 1 2 3 4 5 6
// A B X Y L R
map_button(SDL_CONTROLLER_BUTTON_A, DC_BTN_A);
map_button(SDL_CONTROLLER_BUTTON_B, DC_BTN_B);
map_button(SDL_CONTROLLER_BUTTON_X, DC_BTN_C);
map_button(SDL_CONTROLLER_BUTTON_Y, DC_BTN_X);
if (!map_axis(SDL_CONTROLLER_AXIS_TRIGGERLEFT, DC_AXIS_LT, true))
map_button(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, DC_AXIS_LT);
else
map_button(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, DC_BTN_Y);
if (!map_axis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, DC_AXIS_RT, true))
map_button(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, DC_AXIS_RT);
else
map_button(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, DC_BTN_Z);
}
else
{
// Hitbox
// 1 2 3 4 5 6 7 8
// X Y R1 A B R2 L1 L2
map_button(SDL_CONTROLLER_BUTTON_X, DC_BTN_A);
map_button(SDL_CONTROLLER_BUTTON_Y, DC_BTN_B);
map_button(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, DC_BTN_C); // R1
map_button(SDL_CONTROLLER_BUTTON_A, DC_BTN_X);
map_button(SDL_CONTROLLER_BUTTON_B, DC_BTN_Y);
map_axis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, DC_BTN_Z, true); // R2
map_button(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, DC_DPAD2_LEFT); // L1 (Naomi button 7)
map_axis(SDL_CONTROLLER_AXIS_TRIGGERLEFT, DC_DPAD2_RIGHT, true); // L2 (Naomi button 8)
}
}
else
{
map_button(SDL_CONTROLLER_BUTTON_A, DC_BTN_A);
map_button(SDL_CONTROLLER_BUTTON_B, DC_BTN_B);
map_button(SDL_CONTROLLER_BUTTON_X, DC_BTN_X);
map_button(SDL_CONTROLLER_BUTTON_Y, DC_BTN_Y);
if (!map_axis(SDL_CONTROLLER_AXIS_TRIGGERLEFT, DC_AXIS_LT, true))
map_button(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, DC_AXIS_LT);
else
map_button(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, DC_BTN_Z);
if (!map_axis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, DC_AXIS_RT, true))
map_button(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, DC_AXIS_RT);
else
map_button(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, DC_BTN_C);
}
map_button(SDL_CONTROLLER_BUTTON_START, DC_BTN_START);
map_button(SDL_CONTROLLER_BUTTON_DPAD_UP, DC_DPAD_UP);
map_button(SDL_CONTROLLER_BUTTON_DPAD_DOWN, DC_DPAD_DOWN);
map_button(SDL_CONTROLLER_BUTTON_DPAD_LEFT, DC_DPAD_LEFT);
map_button(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, DC_DPAD_RIGHT);
map_button(SDL_CONTROLLER_BUTTON_BACK, EMU_BTN_MENU);
map_axis(SDL_CONTROLLER_AXIS_LEFTX, DC_AXIS_LEFT, false);
map_axis(SDL_CONTROLLER_AXIS_LEFTX, DC_AXIS_RIGHT, true);
map_axis(SDL_CONTROLLER_AXIS_LEFTY, DC_AXIS_UP, false);
map_axis(SDL_CONTROLLER_AXIS_LEFTY, DC_AXIS_DOWN, true);
map_axis(SDL_CONTROLLER_AXIS_RIGHTX, DC_AXIS2_LEFT, false);
map_axis(SDL_CONTROLLER_AXIS_RIGHTX, DC_AXIS2_RIGHT, true);
map_axis(SDL_CONTROLLER_AXIS_RIGHTY, DC_AXIS2_UP, false);
map_axis(SDL_CONTROLLER_AXIS_RIGHTY, DC_AXIS2_DOWN, true);
dirty = false;
}
else
@ -110,8 +158,19 @@ public:
_unique_id = "sdl_joystick_" + std::to_string(sdl_joystick_instance);
INFO_LOG(INPUT, "SDL: Opened joystick %d on port %d: '%s' unique_id=%s", sdl_joystick_instance, maple_port, _name.c_str(), _unique_id.c_str());
if (SDL_IsGameController(joystick_idx))
{
sdl_controller = SDL_GameControllerOpen(joystick_idx);
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForAxis(sdl_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT);
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_AXIS)
leftTrigger = bind.value.axis;
bind = SDL_GameControllerGetBindForAxis(sdl_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_AXIS)
rightTrigger = bind.value.axis;
}
if (!find_mapping())
input_mapper = std::make_shared<DefaultInputMapping>(joystick_idx);
input_mapper = std::make_shared<DefaultInputMapping<>>(sdl_controller);
else
INFO_LOG(INPUT, "using custom mapping '%s'", input_mapper->name.c_str());
sdl_haptic = SDL_HapticOpenFromJoystick(sdl_joystick);
@ -122,6 +181,13 @@ public:
}
}
bool gamepad_axis_input(u32 code, int value) override
{
if (code == leftTrigger || code == rightTrigger)
value = (u16)(value + 32768) / 2;
return GamepadDevice::gamepad_axis_input(code, value);
}
void rumble(float power, float inclination, u32 duration_ms) override
{
if (sdl_haptic != NULL)
@ -149,13 +215,126 @@ public:
void close()
{
INFO_LOG(INPUT, "SDL: Joystick '%s' on port %d disconnected", _name.c_str(), maple_port());
if (sdl_haptic != NULL)
if (sdl_haptic != nullptr)
SDL_HapticClose(sdl_haptic);
if (sdl_controller != nullptr)
SDL_GameControllerClose(sdl_controller);
SDL_JoystickClose(sdl_joystick);
GamepadDevice::Unregister(sdl_gamepads[sdl_joystick_instance]);
sdl_gamepads.erase(sdl_joystick_instance);
}
const char *get_button_name(u32 code) override
{
static struct
{
const char *sdlButton;
const char *label;
} buttonsTable[] =
{
{ "a", "A" },
{ "b", "B" },
{ "x", "X" },
{ "y", "Y" },
{ "back", "Back" },
{ "guide", "Guide" },
{ "start", "Start" },
{ "leftstick", "L3" },
{ "rightstick", "R3" },
{ "leftshoulder", "L1" },
{ "rightshoulder", "R1" },
{ "dpup", "DPad Up" },
{ "dpdown", "DPad Down" },
{ "dpleft", "DPad Left" },
{ "dpright", "DPad Right" },
{ "misc1", "Misc" },
{ "paddle1", "Paddle 1" },
{ "paddle2", "Paddle 2" },
{ "paddle3", "Paddle 3" },
{ "paddle4", "Paddle 4" },
{ "touchpad", "Touchpad" },
};
for (SDL_GameControllerButton button = SDL_CONTROLLER_BUTTON_A; button < SDL_CONTROLLER_BUTTON_MAX; button = (SDL_GameControllerButton)(button + 1))
{
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForButton(sdl_controller, button);
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON && bind.value.button == (int)code)
{
const char *sdlButton = SDL_GameControllerGetStringForButton(button);
if (sdlButton == nullptr)
return nullptr;
for (const auto& button : buttonsTable)
if (!strcmp(button.sdlButton, sdlButton))
return button.label;
return sdlButton;
}
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_HAT && (code >> 8) - 1 == (u32)bind.value.hat.hat)
{
switch (bind.value.hat.hat_mask)
{
case SDL_HAT_UP:
return "DPad Up";
case SDL_HAT_DOWN:
return "DPad Down";
case SDL_HAT_LEFT:
return "DPad Left";
case SDL_HAT_RIGHT:
return "DPad Right";
default:
break;
}
}
}
return nullptr;
}
const char *get_axis_name(u32 code) override
{
static struct
{
const char *sdlAxis;
const char *label;
} axesTable[] =
{
{ "leftx", "Left Stick X" },
{ "lefty", "Left Stick Y" },
{ "rightx", "Right Stick X" },
{ "righty", "Right Stick Y" },
{ "lefttrigger", "L2" },
{ "righttrigger", "R2" },
};
for (SDL_GameControllerAxis axis = SDL_CONTROLLER_AXIS_LEFTX; axis < SDL_CONTROLLER_AXIS_MAX; axis = (SDL_GameControllerAxis)(axis + 1))
{
SDL_GameControllerButtonBind bind = SDL_GameControllerGetBindForAxis(sdl_controller, axis);
if (bind.bindType == SDL_CONTROLLER_BINDTYPE_AXIS && bind.value.axis == (int)code)
{
const char *sdlAxis = SDL_GameControllerGetStringForAxis(axis);
if (sdlAxis == nullptr)
return nullptr;
for (const auto& axis : axesTable)
if (!strcmp(axis.sdlAxis, sdlAxis))
return axis.label;
return sdlAxis;
}
}
return nullptr;
}
void resetMappingToDefault(bool arcade, bool gamepad) override
{
NOTICE_LOG(INPUT, "Resetting SDL gamepad to default: %d %d", arcade, gamepad);
if (arcade)
{
if (gamepad)
input_mapper = std::make_shared<DefaultInputMapping<true, true>>(sdl_controller);
else
input_mapper = std::make_shared<DefaultInputMapping<true, false>>(sdl_controller);
}
else
input_mapper = std::make_shared<DefaultInputMapping<false, false>>(sdl_controller);
}
static void AddSDLGamepad(std::shared_ptr<SDLGamepad> gamepad)
{
sdl_gamepads[gamepad->sdl_joystick_instance] = gamepad;
@ -175,19 +354,15 @@ public:
pair.second->update_rumble();
}
protected:
void load_axis_min_max(u32 axis) override
{
axis_min_values[axis] = -32768;
axis_ranges[axis] = 65535;
}
private:
SDL_Joystick* sdl_joystick;
SDL_JoystickID sdl_joystick_instance;
SDL_Haptic *sdl_haptic;
float vib_inclination = 0;
double vib_stop_time = 0;
SDL_GameController *sdl_controller;
u32 leftTrigger = ~0;
u32 rightTrigger = ~0;
static std::map<SDL_JoystickID, std::shared_ptr<SDLGamepad>> sdl_gamepads;
};

View File

@ -19,15 +19,19 @@ public:
set_button(DC_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_LEFT);
set_button(DC_DPAD_RIGHT, XINPUT_GAMEPAD_DPAD_RIGHT);
set_button(DC_BTN_START, XINPUT_GAMEPAD_START);
set_button(EMU_BTN_TRIGGER_LEFT, XINPUT_GAMEPAD_LEFT_SHOULDER);
set_button(EMU_BTN_TRIGGER_RIGHT, XINPUT_GAMEPAD_RIGHT_SHOULDER);
set_button(DC_AXIS_LT, XINPUT_GAMEPAD_LEFT_SHOULDER);
set_button(DC_AXIS_RT, XINPUT_GAMEPAD_RIGHT_SHOULDER);
set_button(EMU_BTN_MENU, XINPUT_GAMEPAD_BACK);
set_axis(DC_AXIS_LT, 0, false);
set_axis(DC_AXIS_RT, 1, false);
set_axis(DC_AXIS_X, 2, false);
set_axis(DC_AXIS_Y, 3, true);
set_axis(DC_AXIS_X2, 4, false);
set_axis(DC_AXIS_Y2, 5, true);
set_axis(DC_AXIS_LEFT, 2, false);
set_axis(DC_AXIS_RIGHT, 2, true);
set_axis(DC_AXIS_UP, 3, true);
set_axis(DC_AXIS_DOWN, 3, false);
set_axis(DC_AXIS2_LEFT, 4, false);
set_axis(DC_AXIS2_RIGHT, 4, true);
set_axis(DC_AXIS2_UP, 5, true);
set_axis(DC_AXIS2_DOWN, 5, false);
dirty = false;
}
};
@ -67,12 +71,12 @@ public:
if (state.Gamepad.bLeftTrigger != last_left_trigger)
{
gamepad_axis_input(0, state.Gamepad.bLeftTrigger);
gamepad_axis_input(0, (state.Gamepad.bLeftTrigger << 7) + (state.Gamepad.bLeftTrigger >> 1));
last_left_trigger = state.Gamepad.bLeftTrigger;
}
if (state.Gamepad.bRightTrigger != last_right_trigger)
{
gamepad_axis_input(1, state.Gamepad.bRightTrigger);
gamepad_axis_input(1, (state.Gamepad.bRightTrigger << 7) + (state.Gamepad.bRightTrigger >> 1));
last_right_trigger = state.Gamepad.bRightTrigger;
}
if (state.Gamepad.sThumbLX != last_left_thumb_x)

View File

@ -64,32 +64,13 @@ enum {
KEYCODE_BUTTON_MODE = 110,
};
class AndroidGamepadDevice;
template<bool Arcade = false, bool Gamepad = false>
class DefaultInputMapping : public InputMapping
{
public:
DefaultInputMapping()
{
name = "Default";
set_button(DC_BTN_Y, KEYCODE_BUTTON_Y);
set_button(DC_BTN_B, KEYCODE_BUTTON_B);
set_button(DC_BTN_A, KEYCODE_BUTTON_A);
set_button(DC_BTN_X, KEYCODE_BUTTON_X);
set_button(DC_BTN_START, KEYCODE_BUTTON_START);
set_button(DC_DPAD_UP, KEYCODE_DPAD_UP);
set_button(DC_DPAD_DOWN, KEYCODE_DPAD_DOWN);
set_button(DC_DPAD_LEFT, KEYCODE_DPAD_LEFT);
set_button(DC_DPAD_RIGHT, KEYCODE_DPAD_RIGHT);
set_button(EMU_BTN_MENU, KEYCODE_BACK);
set_axis(DC_AXIS_X, AXIS_X, false);
set_axis(DC_AXIS_Y, AXIS_Y, false);
set_axis(DC_AXIS_LT, AXIS_LTRIGGER, false);
set_axis(DC_AXIS_RT, AXIS_RTRIGGER, false);
set_axis(DC_AXIS_X2, AXIS_Z, false);
set_axis(DC_AXIS_Y2, AXIS_RZ, false);
dirty = false;
}
DefaultInputMapping(const AndroidGamepadDevice& gamepad);
};
class ShieldRemoteInputMapping : public InputMapping
@ -123,14 +104,6 @@ public:
if (id == VIRTUAL_GAMEPAD_ID)
{
input_mapper = std::make_shared<IdentityInputMapping>();
axis_min_values[DC_AXIS_X] = -128;
axis_ranges[DC_AXIS_X] = 255;
axis_min_values[DC_AXIS_Y] = -128;
axis_ranges[DC_AXIS_Y] = 255;
axis_min_values[DC_AXIS_LT] = 0;
axis_ranges[DC_AXIS_LT] = 255;
axis_min_values[DC_AXIS_RT] = 0;
axis_ranges[DC_AXIS_RT] = 255;
}
else
{
@ -146,34 +119,8 @@ public:
virtual std::shared_ptr<InputMapping> getDefaultMapping() override {
if (_name == "SHIELD Remote")
return std::make_shared<ShieldRemoteInputMapping>();
std::shared_ptr<InputMapping> mapping = std::make_shared<DefaultInputMapping>();
auto ltAxis = std::find(halfAxes.begin(), halfAxes.end(), AXIS_LTRIGGER);
auto rtAxis = std::find(halfAxes.begin(), halfAxes.end(), AXIS_RTRIGGER);
if (ltAxis != halfAxes.end() && rtAxis != halfAxes.end())
{
mapping->set_axis(DC_AXIS_LT, *ltAxis, false);
mapping->set_axis(DC_AXIS_RT, *rtAxis, false);
}
else
{
ltAxis = std::find(halfAxes.begin(), halfAxes.end(), AXIS_BRAKE);
rtAxis = std::find(halfAxes.begin(), halfAxes.end(), AXIS_GAS);
if (ltAxis != halfAxes.end() && rtAxis != halfAxes.end())
{
mapping->set_axis(DC_AXIS_LT, *ltAxis, false);
mapping->set_axis(DC_AXIS_RT, *rtAxis, false);
}
else
{
// Xbox controller?
mapping->set_axis(DC_AXIS_LT, AXIS_Z, false);
mapping->set_axis(DC_AXIS_RT, AXIS_RZ, false);
mapping->set_axis(DC_AXIS_X2, AXIS_RX, false);
mapping->set_axis(DC_AXIS_Y2, AXIS_RY, false);
}
}
return mapping;
return std::make_shared<DefaultInputMapping<>>(*this);
}
virtual const char *get_button_name(u32 code) override
@ -304,20 +251,26 @@ public:
// RT + A -> D (coin)
kcode &= ~DC_BTN_D;
if ((kcode & DC_BTN_B) == 0)
// RT + B -> C (service)
kcode &= ~DC_BTN_C;
// RT + B -> Service
kcode &= ~DC_DPAD2_UP;
if ((kcode & DC_BTN_X) == 0)
// RT + X -> Z (test)
kcode &= ~DC_BTN_Z;
// RT + X -> Test
kcode &= ~DC_DPAD2_DOWN;
}
u32 changes = kcode ^ previous_kcode;
for (int i = 0; i < 32; i++)
if (changes & (1 << i))
gamepad_btn_input(1 << i, (kcode & (1 << i)) == 0);
gamepad_axis_input(DC_AXIS_X, joyx);
gamepad_axis_input(DC_AXIS_Y, joyy);
gamepad_axis_input(DC_AXIS_LT, lt);
gamepad_axis_input(DC_AXIS_RT, rt);
if (joyx >= 0)
gamepad_axis_input(DC_AXIS_RIGHT, joyx | (joyx << 8));
else
gamepad_axis_input(DC_AXIS_LEFT, -joyx | (-joyx << 8));
if (joyy >= 0)
gamepad_axis_input(DC_AXIS_DOWN, joyy | (joyy << 8));
else
gamepad_axis_input(DC_AXIS_UP, -joyy | (-joyy << 8));
gamepad_axis_input(DC_AXIS_LT, lt == 0 ? 0 : 0x7fff);
gamepad_axis_input(DC_AXIS_RT, rt == 0 ? 0 : 0x7fff);
previous_kcode = kcode;
}
@ -328,24 +281,25 @@ public:
}
bool is_virtual_gamepad() override { return android_id == VIRTUAL_GAMEPAD_ID; }
static const int VIRTUAL_GAMEPAD_ID = 0x12345678; // must match the Java definition
bool hasHalfAxis(int axis) const { return std::find(halfAxes.begin(), halfAxes.end(), axis) != halfAxes.end(); }
bool hasFullAxis(int axis) const { return std::find(fullAxes.begin(), fullAxes.end(), axis) != fullAxes.end(); }
protected:
virtual void load_axis_min_max(u32 axis) override
void resetMappingToDefault(bool arcade, bool gamepad) override
{
auto axisIt = std::find(halfAxes.begin(), halfAxes.end(), axis);
if (axisIt != halfAxes.end())
NOTICE_LOG(INPUT, "Resetting Android gamepad to default: %d %d", arcade, gamepad);
if (arcade)
{
axis_min_values[axis] = 0;
axis_ranges[axis] = 32767;
if (gamepad)
input_mapper = std::make_shared<DefaultInputMapping<true, true>>(*this);
else
input_mapper = std::make_shared<DefaultInputMapping<true, false>>(*this);
}
else
{
axis_min_values[axis] = -32768;
axis_ranges[axis] = 65535;
}
input_mapper = std::make_shared<DefaultInputMapping<false, false>>(*this);
}
static const int VIRTUAL_GAMEPAD_ID = 0x12345678; // must match the Java definition
private:
int android_id;
static std::map<int, std::shared_ptr<AndroidGamepadDevice>> android_gamepads;
@ -356,6 +310,126 @@ private:
std::map<int, std::shared_ptr<AndroidGamepadDevice>> AndroidGamepadDevice::android_gamepads;
template<bool Arcade, bool Gamepad>
inline DefaultInputMapping<Arcade, Gamepad>::DefaultInputMapping(const AndroidGamepadDevice& gamepad)
{
name = Arcade ? Gamepad ? "Arcade Gamepad" : "Arcade Hitbox" : "Default";
int ltAxis = AXIS_LTRIGGER;
int rtAxis = AXIS_RTRIGGER;
int rightStickX = AXIS_Z;
int rightStickY = AXIS_RZ;
if (!gamepad.hasHalfAxis(AXIS_LTRIGGER) || !gamepad.hasHalfAxis(AXIS_RTRIGGER))
{
if (gamepad.hasHalfAxis(AXIS_BRAKE) && gamepad.hasHalfAxis(AXIS_GAS))
{
ltAxis = AXIS_BRAKE;
rtAxis = AXIS_GAS;
}
else if (gamepad.hasHalfAxis(AXIS_Z) && gamepad.hasHalfAxis(AXIS_RZ))
{
ltAxis = AXIS_Z;
rtAxis = AXIS_RZ;
rightStickX = AXIS_RX;
rightStickY = AXIS_RY;
}
else
{
ltAxis = -1;
rtAxis = -1;
}
}
if (!gamepad.hasFullAxis(rightStickX) || !gamepad.hasFullAxis(rightStickY))
{
rightStickX = -1;
rightStickY = -1;
}
else
{
set_axis(DC_AXIS2_LEFT, rightStickX, false);
set_axis(DC_AXIS2_RIGHT, rightStickX, true);
set_axis(DC_AXIS2_UP, rightStickY, false);
set_axis(DC_AXIS2_DOWN, rightStickY, true);
}
if (Arcade)
{
if (Gamepad)
{
// 1 2 3 4 5 6
// A B X Y L R
set_button(DC_BTN_A, KEYCODE_BUTTON_A);
set_button(DC_BTN_B, KEYCODE_BUTTON_B);
set_button(DC_BTN_C, KEYCODE_BUTTON_X);
set_button(DC_BTN_X, KEYCODE_BUTTON_Y);
if (ltAxis != -1)
{
set_axis(DC_AXIS_LT, ltAxis, true);
set_button(DC_BTN_Y, KEYCODE_BUTTON_L1);
}
else
set_button(DC_AXIS_LT, KEYCODE_BUTTON_L1);
if (rtAxis != -1)
{
set_axis(DC_AXIS_RT, rtAxis, true);
set_button(DC_BTN_Z, KEYCODE_BUTTON_R1);
}
else
set_button(DC_AXIS_RT, KEYCODE_BUTTON_R1);
}
else
{
// Hitbox
// 1 2 3 4 5 6 7 8
// X Y R1 A B R2 L1 L2
set_button(DC_BTN_A, KEYCODE_BUTTON_X);
set_button(DC_BTN_B, KEYCODE_BUTTON_Y);
set_button(DC_BTN_C, KEYCODE_BUTTON_R1);
set_button(DC_BTN_X, KEYCODE_BUTTON_A);
set_button(DC_BTN_Y, KEYCODE_BUTTON_B);
if (rtAxis != -1)
set_axis(DC_BTN_Z, rtAxis, true);
set_button(DC_DPAD2_LEFT, KEYCODE_BUTTON_L1); // L1 (Naomi button 7)
if (ltAxis != -1)
set_axis(DC_DPAD2_RIGHT, ltAxis, true); // L2 (Naomi button 8)
}
}
else
{
set_button(DC_BTN_A, KEYCODE_BUTTON_A);
set_button(DC_BTN_B, KEYCODE_BUTTON_B);
set_button(DC_BTN_X, KEYCODE_BUTTON_X);
set_button(DC_BTN_Y, KEYCODE_BUTTON_Y);
if (rtAxis != -1)
{
set_axis(DC_AXIS_RT, rtAxis, true);
set_button(DC_BTN_C, KEYCODE_BUTTON_R1);
}
else
set_button(DC_AXIS_RT, KEYCODE_BUTTON_R1);
if (ltAxis != -1)
{
set_axis(DC_AXIS_LT, ltAxis, true);
set_button(DC_BTN_Z, KEYCODE_BUTTON_L1);
}
else
set_button(DC_AXIS_LT, KEYCODE_BUTTON_L1);
}
set_button(DC_BTN_START, KEYCODE_BUTTON_START);
set_button(DC_DPAD_UP, KEYCODE_DPAD_UP);
set_button(DC_DPAD_DOWN, KEYCODE_DPAD_DOWN);
set_button(DC_DPAD_LEFT, KEYCODE_DPAD_LEFT);
set_button(DC_DPAD_RIGHT, KEYCODE_DPAD_RIGHT);
set_button(EMU_BTN_MENU, KEYCODE_BUTTON_SELECT);
set_axis(DC_AXIS_LEFT, AXIS_X, false);
set_axis(DC_AXIS_RIGHT, AXIS_X, true);
set_axis(DC_AXIS_UP, AXIS_Y, false);
set_axis(DC_AXIS_DOWN, AXIS_Y, true);
dirty = false;
}
class AndroidMouse : public SystemMouse
{
public:

View File

@ -116,8 +116,8 @@
pos.y = std::max<CGFloat>(std::min<CGFloat>(25.0, pos.y), -25.0);
self.joyXConstraint.constant = pos.x;
self.joyYConstraint.constant = pos.y;
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, (s8)std::round(pos.x * 127.0 / 25.0));
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, (s8)std::round(pos.y * 127.0 / 25.0));
virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, (s8)std::round(pos.x * 32767.0 / 25.0));
virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, (s8)std::round(pos.y * 32767.0 / 25.0));
break;
}
}

View File

@ -64,16 +64,54 @@ enum IOSAxis {
static NSString *GCInputXboxShareButton = @"Button Share";
template<bool Arcade = false, bool Gamepad = false>
class DefaultIOSMapping : public InputMapping
{
public:
DefaultIOSMapping()
{
name = "Default";
set_button(DC_BTN_A, IOS_BTN_A);
set_button(DC_BTN_B, IOS_BTN_B);
set_button(DC_BTN_X, IOS_BTN_X);
set_button(DC_BTN_Y, IOS_BTN_Y);
name = Arcade ? Gamepad ? "Arcade Gamepad" : "Arcade Hitbox" : "Default";
if (Arcade)
{
if (Gamepad)
{
// 1 2 3 4 5 6
// A B X Y L R
set_button(DC_BTN_A, IOS_BTN_A);
set_button(DC_BTN_B, IOS_BTN_B);
set_button(DC_BTN_C, IOS_BTN_X);
set_button(DC_BTN_X, IOS_BTN_Y);
set_button(DC_BTN_Y, IOS_BTN_L1);
set_button(DC_BTN_Z, IOS_BTN_R1);
set_axis(DC_AXIS_LT, IOS_AXIS_L2, true);
set_axis(DC_AXIS_RT, IOS_AXIS_R2, true);
}
else
{
// Hitbox
// 1 2 3 4 5 6 7 8
// X Y R1 A B R2 L1 L2
set_button(DC_BTN_A, IOS_BTN_X);
set_button(DC_BTN_B, IOS_BTN_Y);
set_button(DC_BTN_C, IOS_BTN_R1);
set_button(DC_BTN_X, IOS_BTN_A);
set_button(DC_BTN_Y, IOS_BTN_B);
set_axis(DC_BTN_Z, IOS_AXIS_R2, true);
set_button(DC_DPAD2_LEFT, KEYCODE_BUTTON_L1); // L1 (Naomi button 7)
set_axis(DC_DPAD2_RIGHT, IOS_AXIS_L2, true); // L2 (Naomi button 8)
}
}
else
{
set_button(DC_BTN_A, IOS_BTN_A);
set_button(DC_BTN_B, IOS_BTN_B);
set_button(DC_BTN_X, IOS_BTN_X);
set_button(DC_BTN_Y, IOS_BTN_Y);
set_axis(DC_AXIS_RT, IOS_AXIS_R2, true);
set_button(DC_BTN_C, KEYCODE_BUTTON_R1);
set_axis(DC_AXIS_LT, IOS_AXIS_L2, true);
set_button(DC_BTN_Z, KEYCODE_BUTTON_L1);
}
set_button(DC_DPAD_UP, IOS_BTN_UP);
set_button(DC_DPAD_DOWN, IOS_BTN_DOWN);
set_button(DC_DPAD_LEFT, IOS_BTN_LEFT);
@ -81,12 +119,14 @@ public:
set_button(DC_BTN_START, IOS_BTN_MENU);
set_button(EMU_BTN_MENU, IOS_BTN_OPTIONS);
set_axis(DC_AXIS_X, IOS_AXIS_LX, false);
set_axis(DC_AXIS_Y, IOS_AXIS_LY, false);
set_axis(DC_AXIS_X2, IOS_AXIS_RX, false);
set_axis(DC_AXIS_Y2, IOS_AXIS_RY, false);
set_axis(DC_AXIS_LT, IOS_AXIS_L2, false);
set_axis(DC_AXIS_RT, IOS_AXIS_R2, false);
set_axis(DC_AXIS_LEFT, IOS_AXIS_LX, false);
set_axis(DC_AXIS_RIGHT, IOS_AXIS_LX, true);
set_axis(DC_AXIS_UP, IOS_AXIS_LY, false);
set_axis(DC_AXIS_DOWN, IOS_AXIS_LY, true);
set_axis(DC_AXIS2_LEFT, IOS_AXIS_RX, false);
set_axis(DC_AXIS2_RIGHT, IOS_AXIS_RX, true);
set_axis(DC_AXIS2_UP, IOS_AXIS_RY, false);
set_axis(DC_AXIS2_DOWN, IOS_AXIS_RY, true);
dirty = false;
}
};
@ -166,23 +206,23 @@ public:
[gcController.extendedGamepad.rightTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
gamepad_btn_input(IOS_BTN_R2, pressed);
gamepad_axis_input(IOS_AXIS_R2, (int)std::roundf(255.f * value));
gamepad_axis_input(IOS_AXIS_R2, (int)std::roundf(32767.f * value));
}];
[gcController.extendedGamepad.leftTrigger setValueChangedHandler:^(GCControllerButtonInput *button, float value, BOOL pressed) {
gamepad_btn_input(IOS_BTN_L2, pressed);
gamepad_axis_input(IOS_AXIS_L2, (int)std::roundf(255.f * value));
gamepad_axis_input(IOS_AXIS_L2, (int)std::roundf(32767.f * value));
}];
[gcController.extendedGamepad.leftThumbstick.xAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
gamepad_axis_input(IOS_AXIS_LX, (int)std::roundf(127.f * value));
gamepad_axis_input(IOS_AXIS_LX, (int)std::roundf(32767.f * value));
}];
[gcController.extendedGamepad.leftThumbstick.yAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
gamepad_axis_input(IOS_AXIS_LY, (int)std::roundf(-127.f * value));
gamepad_axis_input(IOS_AXIS_LY, (int)std::roundf(-32767.f * value));
}];
[gcController.extendedGamepad.rightThumbstick.xAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
gamepad_axis_input(IOS_AXIS_RX, (int)std::roundf(127.f * value));
gamepad_axis_input(IOS_AXIS_RX, (int)std::roundf(32767.f * value));
}];
[gcController.extendedGamepad.rightThumbstick.yAxis setValueChangedHandler:^(GCControllerAxisInput *axis, float value) {
gamepad_axis_input(IOS_AXIS_RY, (int)std::roundf(-127.f * value));
gamepad_axis_input(IOS_AXIS_RY, (int)std::roundf(-32767.f * value));
}];
}
@ -369,7 +409,7 @@ public:
}
std::shared_ptr<InputMapping> getDefaultMapping() override {
return std::make_shared<DefaultIOSMapping>();
return std::make_shared<DefaultIOSMapping<>>();
}
void rumble(float power, float inclination, u32 duration_ms) override
@ -394,6 +434,20 @@ public:
}
}
void resetMappingToDefault(bool arcade, bool gamepad) override
{
NOTICE_LOG(INPUT, "Resetting iOS gamepad to default: %d %d", arcade, gamepad);
if (arcade)
{
if (gamepad)
input_mapper = std::make_shared<DefaultIOSMapping<true, true>>();
else
input_mapper = std::make_shared<DefaultIOSMapping<true, false>>();
}
else
input_mapper = std::make_shared<DefaultIOSMapping<false, false>>();
}
static void addController(GCController *controller)
{
if (controllers.count(controller) > 0)
@ -418,21 +472,6 @@ public:
return !controllers.empty();
}
protected:
void load_axis_min_max(u32 axis) override
{
if (axis == IOS_AXIS_L1 || axis == IOS_AXIS_R1 || axis == IOS_AXIS_L2 || axis == IOS_AXIS_R2)
{
axis_min_values[axis] = 0;
axis_ranges[axis] = 0xff;
}
else
{
axis_min_values[axis] = -127;
axis_ranges[axis] = 254;
}
}
private:
GCController * __weak gcController = nullptr;
CHHapticEngine *hapticEngine = nullptr;
@ -452,7 +491,7 @@ public:
bool is_virtual_gamepad() override { return true; }
std::shared_ptr<InputMapping> getDefaultMapping() override {
return std::make_shared<DefaultIOSMapping>();
return std::make_shared<DefaultIOSMapping<>>();
}
bool gamepad_btn_input(u32 code, bool pressed) override
@ -464,12 +503,12 @@ public:
switch (code)
{
case IOS_BTN_L2:
gamepad_axis_input(IOS_AXIS_L2, pressed ? 0xff : 0);
gamepad_axis_input(IOS_AXIS_L2, pressed ? 0x7fff : 0);
return true;
case IOS_BTN_R2:
if (!pressed && maple_port() >= 0 && maple_port() <= 3)
kcode[maple_port()] |= DC_BTN_C | DC_BTN_D | DC_BTN_Z;
gamepad_axis_input(IOS_AXIS_R2, pressed ? 0xff : 0);
gamepad_axis_input(IOS_AXIS_R2, pressed ? 0x7fff : 0);
return true;
default:
if ((buttonState & ((1 << IOS_BTN_UP) | (1 << IOS_BTN_DOWN))) == ((1 << IOS_BTN_UP) | (1 << IOS_BTN_DOWN))
@ -493,12 +532,12 @@ public:
keycode = pressed ? keycode & ~DC_BTN_D : keycode | DC_BTN_D;
break;
case IOS_BTN_B:
// RT + B -> C (service)
keycode = pressed ? keycode & ~DC_BTN_C : keycode | DC_BTN_C;
// RT + B -> Service
keycode = pressed ? keycode & ~DC_DPAD2_UP : keycode | DC_DPAD2_UP;
break;
case IOS_BTN_X:
// RT + X -> Z (test)
keycode = pressed ? keycode & ~DC_BTN_Z : keycode | DC_BTN_Z;
// RT + X -> Test
keycode = pressed ? keycode & ~DC_DPAD2_DOWN : keycode | DC_DPAD2_DOWN;
break;
default:
break;
@ -508,20 +547,6 @@ public:
return GamepadDevice::gamepad_btn_input(code, pressed);
}
}
protected:
void load_axis_min_max(u32 axis) override
{
if (axis == IOS_AXIS_L1 || axis == IOS_AXIS_R1 || axis == IOS_AXIS_L2 || axis == IOS_AXIS_R2)
{
axis_min_values[axis] = 0;
axis_ranges[axis] = 0xff;
}
else
{
axis_min_values[axis] = -127;
axis_ranges[axis] = 254;
}
}
private:
u32 buttonState = 0;