diff --git a/CMakeLists.txt b/CMakeLists.txt index 52914c4ee..323f252b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index 4e06dc47d..a0f61665f 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -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; } } diff --git a/core/hw/maple/maple_jvs.cpp b/core/hw/maple/maple_jvs.cpp index aa91f04d3..50d3164cc 100644 --- a/core/hw/maple/maple_jvs.cpp +++ b/core/hw/maple/maple_jvs.cpp @@ -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; diff --git a/core/hw/naomi/naomi_roms.cpp b/core/hw/naomi/naomi_roms.cpp index 574ae63f1..aca831f8b 100644 --- a/core/hw/naomi/naomi_roms.cpp +++ b/core/hw/naomi/naomi_roms.cpp @@ -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 { diff --git a/core/hw/naomi/naomi_roms_input.h b/core/hw/naomi/naomi_roms_input.h index 082f35008..0b9c7a276 100644 --- a/core/hw/naomi/naomi_roms_input.h +++ b/core/hw/naomi/naomi_roms_input.h @@ -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"); diff --git a/core/input/gamepad.h b/core/input/gamepad.h index 5bbb958eb..7c821e40a 100644 --- a/core/input/gamepad.h +++ b/core/input/gamepad.h @@ -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, }; diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index df1b7c4da..a63b5fb19 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -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(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(port, key, pressed, joyx[port]); break; + case DC_AXIS2_UP: + case DC_AXIS2_DOWN: + buttonToAnalogInput(port, key, pressed, joyry[port]); + break; + case DC_AXIS2_LEFT: + case DC_AXIS2_RIGHT: + buttonToAnalogInput(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; } diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index 02ad06e25..2fcee12bd 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -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 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(); } - 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 input_mapper; - std::map axis_min_values; - std::map 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 + 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; diff --git a/core/input/keyboard_device.h b/core/input/keyboard_device.h index 36d00f544..5155b1feb 100644 --- a/core/input/keyboard_device.h +++ b/core/input/keyboard_device.h @@ -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 diff --git a/core/input/mapping.cpp b/core/input/mapping.cpp index 83110abc4..e246bd6fe 100644 --- a/core/input/mapping.cpp +++ b/core/input/mapping.cpp @@ -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> 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 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 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::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); diff --git a/core/input/mapping.h b/core/input/mapping.h index 514c5e252..78e19843a 100644 --- a/core/input/mapping.h +++ b/core/input/mapping.h @@ -24,6 +24,10 @@ #include #include +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 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 buttons[4]; - std::map axes[4]; - std::map axes_inverted[4]; + std::map, DreamcastKey> axes[4]; static std::map> 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); } }; diff --git a/core/linux-dist/evdev_gamepad.h b/core/linux-dist/evdev_gamepad.h index 3bccfa679..2a1e3e1dc 100644 --- a/core/linux-dist/evdev_gamepad.h +++ b/core/linux-dist/evdev_gamepad.h @@ -5,6 +5,7 @@ #include #include #include +#include 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 axis_min_values; + std::map axis_ranges; static std::map> evdev_gamepads; }; diff --git a/core/linux-dist/joystick.cpp b/core/linux-dist/joystick.cpp deleted file mode 100644 index 4f99bb6c3..000000000 --- a/core/linux-dist/joystick.cpp +++ /dev/null @@ -1,159 +0,0 @@ -#include "joystick.h" - -#if defined(USE_JOYSTICK) -#include -#include -#include -#include - - 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 diff --git a/core/linux-dist/joystick.h b/core/linux-dist/joystick.h deleted file mode 100644 index 432207e1c..000000000 --- a/core/linux-dist/joystick.h +++ /dev/null @@ -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); diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index ad8b3f0e1..cf8d0890c 100644 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -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 diff --git a/core/network/ggpo.cpp b/core/network/ggpo.cpp index 357e4f55e..d9b9c6f43 100644 --- a/core/network/ggpo.cpp +++ b/core/network/ggpo.cpp @@ -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 #include "imgui/imgui.h" #include "miniupnp.h" +#include "hw/naomi/naomi_cart.h" //#define SYNC_TEST 1 @@ -77,6 +81,7 @@ static time_point 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 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 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); diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index ed544a007..66c3c77dc 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -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 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& 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 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& gamepad, DreamcastKey key) +{ + std::shared_ptr 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 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& gamepad) { fullScreenWindow(true); @@ -742,7 +879,7 @@ static void controller_mapping_popup(const std::shared_ptr& 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& 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& 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& 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; diff --git a/core/sdl/sdl_gamepad.h b/core/sdl/sdl_gamepad.h index 153e5b182..9cd448fe2 100644 --- a/core/sdl/sdl_gamepad.h +++ b/core/sdl/sdl_gamepad.h @@ -3,6 +3,7 @@ #include "sdl.h" #include "rend/gui.h" +template 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(joystick_idx); + input_mapper = std::make_shared>(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>(sdl_controller); + else + input_mapper = std::make_shared>(sdl_controller); + } + else + input_mapper = std::make_shared>(sdl_controller); + } + + static void AddSDLGamepad(std::shared_ptr 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_gamepads; }; diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index f1d0444a5..eed983d6e 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -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) diff --git a/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h b/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h index d5ec973d9..654234ec9 100644 --- a/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h +++ b/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h @@ -64,32 +64,13 @@ enum { KEYCODE_BUTTON_MODE = 110, }; +class AndroidGamepadDevice; + +template 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(); - 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 getDefaultMapping() override { if (_name == "SHIELD Remote") return std::make_shared(); - std::shared_ptr mapping = std::make_shared(); - 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>(*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>(*this); + else + input_mapper = std::make_shared>(*this); } else - { - axis_min_values[axis] = -32768; - axis_ranges[axis] = 65535; - } + input_mapper = std::make_shared>(*this); } + static const int VIRTUAL_GAMEPAD_ID = 0x12345678; // must match the Java definition + private: int android_id; static std::map> android_gamepads; @@ -356,6 +310,126 @@ private: std::map> AndroidGamepadDevice::android_gamepads; +template +inline DefaultInputMapping::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: diff --git a/shell/apple/emulator-ios/emulator/PadViewController.mm b/shell/apple/emulator-ios/emulator/PadViewController.mm index 00f606454..f0a7c7def 100644 --- a/shell/apple/emulator-ios/emulator/PadViewController.mm +++ b/shell/apple/emulator-ios/emulator/PadViewController.mm @@ -116,8 +116,8 @@ pos.y = std::max(std::min(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; } } diff --git a/shell/apple/emulator-ios/emulator/ios_gamepad.h b/shell/apple/emulator-ios/emulator/ios_gamepad.h index 1050dd98d..18f0484b6 100644 --- a/shell/apple/emulator-ios/emulator/ios_gamepad.h +++ b/shell/apple/emulator-ios/emulator/ios_gamepad.h @@ -64,16 +64,54 @@ enum IOSAxis { static NSString *GCInputXboxShareButton = @"Button Share"; +template 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 getDefaultMapping() override { - return std::make_shared(); + return std::make_shared>(); } 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>(); + else + input_mapper = std::make_shared>(); + } + else + input_mapper = std::make_shared>(); + } + 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 getDefaultMapping() override { - return std::make_shared(); + return std::make_shared>(); } 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;