Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Docs/CONTROL_PROFILES.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ Model 3 game list is effectively complete.
- The core option can restrict the profile to Lightgun, Mouse, Mouse + Analog
Stick, or Analog Sticks. It is applied immediately; content does not need to
be reloaded.
- Star Wars Trilogy Arcade keeps its separately recognized analog-joystick
profile but exposes the same five input-source modes as Gun games. Lightgun
1 absolute coordinates drive the arcade yoke; its Trigger/Aux A inputs are
Trigger 1/Event 1, while Lightgun 2 supplies the cabinet's duplicated
Trigger 2/Event 2 pair. Reload and offscreen state are not game actions.
RetroMouse remains relative, while the RetroPad left stick preserves the
cabinet yoke's absolute, self-centering behavior. Upright cabinet X-axis
inversion is applied after all three sources converge on the virtual axis.
- P1 and P2 remain logically independent. Actual two-mouse or two-lightgun
operation still depends on the frontend input driver; RetroArch's macOS
`cocoa` driver currently reports neither multi-mouse nor native lightgun
Expand Down
125 changes: 94 additions & 31 deletions Src/OSD/libretro/CLibretroInputSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ bool CLibretroInputSystem::Poll()
int16_t lightgunX[2] = {};
int16_t lightgunY[2] = {};
bool lightgunOffscreen[2] = {};
bool lightgunPositionValid[2] = {};
bool lightgunPositionMoved[2] = {};
bool lightgunTrigger[2] = {};
bool lightgunAuxA[2] = {};

// ----- RetroMouse ports (Supermodel MOUSE and MOUSE2) -----
for (int port = 0; port < 2; ++port)
Expand Down Expand Up @@ -135,24 +138,41 @@ bool CLibretroInputSystem::Poll()
m_mouseAxes[dev][AXIS_Z] = 0;
m_mouseWheelDir[dev] = 0;

m_mouseButtons[dev][0] = input_state_cb(
lightgunTrigger[port] = input_state_cb(
port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER);
m_mouseButtons[dev][0] = lightgunTrigger[port];
m_mouseButtons[dev][1] = 0;
// Ocean Hunter has two independent shot triggers. AUX_A is the
// natural second trigger; Reload is also accepted because RetroArch
// binds it to the second mouse button by default. IS_OFFSCREEN is a
// position state, not a button: treating it as one leaves Shot 2 stuck.
m_mouseButtons[dev][2] = input_state_cb(
port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A) ||
lightgunAuxA[port] = input_state_cb(
port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A);
m_mouseButtons[dev][2] = lightgunAuxA[port] ||
input_state_cb(
port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD);
port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD);
m_mouseButtons[dev][3] = input_state_cb(
port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START);
m_mouseButtons[dev][4] = input_state_cb(
port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SELECT);
m_mouseIsAbsolute[dev] = true;
}

// Evaluate the raw absolute coordinates once, before any virtual cursor
// updates the previous-position snapshot. Gun and Star Wars profiles can
// then share the same validity and movement decision independently of
// their selected input modes.
for (int port = 0; port < 2; ++port)
{
lightgunPositionValid[port] =
!lightgunOffscreen[port] &&
lightgunX[port] != INT16_MIN && lightgunY[port] != INT16_MIN;
lightgunPositionMoved[port] = lightgunPositionValid[port] &&
(!m_lightgunPositionInitialized[port] ||
lightgunX[port] != m_previousLightgunX[port] ||
lightgunY[port] != m_previousLightgunY[port]);
}

const int16_t THRESHOLD = 8000;

for (int joy = 0; joy < 2; joy++)
Expand Down Expand Up @@ -359,14 +379,10 @@ bool CLibretroInputSystem::Poll()
m_gunCursorInitialized[port] = true;
}

const bool lightgunValid = allowLightgun &&
!lightgunOffscreen[port] &&
lightgunX[port] != INT16_MIN && lightgunY[port] != INT16_MIN;
const bool lightgunMoved = lightgunValid &&
(!m_lightgunPositionInitialized[port] ||
lightgunX[port] != m_previousLightgunX[port] ||
lightgunY[port] != m_previousLightgunY[port]);
lightgunPositionMoved[port] = lightgunMoved;
const bool lightgunValid =
allowLightgun && lightgunPositionValid[port];
const bool lightgunMoved =
allowLightgun && lightgunPositionMoved[port];
const bool lightgunAction = allowLightgun && lightgunValid &&
(m_mouseButtons[lightgunDev][0] ||
m_mouseButtons[lightgunDev][2] ||
Expand Down Expand Up @@ -454,11 +470,21 @@ bool CLibretroInputSystem::Poll()
// to neutral and makes the game's calibration centre unstable.
//
// Present one absolute virtual axis to Supermodel instead. Star Wars
// Trilogy Input Mode selects the accepted sources and, in Hybrid mode, the last source
// actually moved owns it. Mouse movement adjusts the current position;
// moving the stick switches back to its absolute position, including its
// stable centre when released. No values from the devices are mixed.
// Trilogy Input Mode exposes the same source matrix as Gun Input Mode,
// while retaining the cabinet yoke's absolute RetroPad semantics. In
// Standard mode, the last source actually moved owns the cursor. Lightgun
// coordinates are absolute, mouse movement is accumulated, and moving the
// stick switches back to its absolute position (including stable centre).
// Mouse + Analog Stick excludes only RetroLightgun. No source values are
// mixed.
// The original cabinet duplicates Trigger and Event for the rider's other
// hand. Keep the primary pair on port 1 and source the mirror pair from
// port 2 for both RetroMouse and RetroLightgun. This leaves each pointing
// device's extra controls available for the common Start and Coin inputs.
constexpr int analogJoystickDev = 6;
constexpr int analogSource = 0;
constexpr int mouseSource = 1;
constexpr int lightgunSource = 2;
if (!m_analogJoystickCursorInitialized)
{
m_analogJoystickCursorX = minX + static_cast<int>(m_dispW) / 2;
Expand All @@ -471,23 +497,56 @@ bool CLibretroInputSystem::Poll()
const bool analogStickMoved =
std::abs(static_cast<int>(m_joyAxes[0][AXIS_X])) > analogSwitchDeadZone ||
std::abs(static_cast<int>(m_joyAxes[0][AXIS_Y])) > analogSwitchDeadZone;
const bool analogAllowLightgun =
g_options.star_wars_input == StarWarsInput::Hybrid ||
g_options.star_wars_input == StarWarsInput::Lightgun;
// Match Gun Input Mode: Lightgun Only still accepts raw RetroMouse when
// the frontend exposes no valid absolute Lightgun coordinates.
const bool analogAllowMouse =
g_options.star_wars_input == StarWarsInput::Hybrid ||
g_options.star_wars_input == StarWarsInput::Lightgun ||
g_options.star_wars_input == StarWarsInput::MouseAnalog ||
g_options.star_wars_input == StarWarsInput::Mouse;
const bool analogAllowStick =
g_options.star_wars_input == StarWarsInput::Hybrid ||
g_options.star_wars_input == StarWarsInput::MouseAnalog ||
g_options.star_wars_input == StarWarsInput::AnalogSticks;
const bool analogLightgunValid =
analogAllowLightgun && lightgunPositionValid[0];
const bool analogLightgunMoved =
analogAllowLightgun && lightgunPositionMoved[0];
const bool analogLightgunAction = analogAllowLightgun &&
(lightgunTrigger[0] || lightgunAuxA[0] ||
lightgunTrigger[1] || lightgunAuxA[1]);
switch (g_options.star_wars_input)
{
case StarWarsInput::AnalogSticks:
m_analogJoystickSource = 0;
m_analogJoystickSource = analogSource;
break;
case StarWarsInput::Mouse:
m_analogJoystickSource = 1;
m_analogJoystickSource = mouseSource;
break;
case StarWarsInput::Hybrid:
case StarWarsInput::Lightgun:
m_analogJoystickSource = analogLightgunValid
? lightgunSource : mouseSource;
break;
case StarWarsInput::MouseAnalog:
if (analogMouseMoved)
m_analogJoystickSource = 1;
m_analogJoystickSource = mouseSource;
else if (analogStickMoved)
m_analogJoystickSource = analogSource;
break;
case StarWarsInput::Hybrid:
if (analogLightgunMoved || analogLightgunAction)
m_analogJoystickSource = lightgunSource;
else if (analogMouseMoved)
m_analogJoystickSource = mouseSource;
else if (analogStickMoved)
m_analogJoystickSource = 0;
m_analogJoystickSource = analogSource;
break;
}

if (m_analogJoystickSource == 0)
if (m_analogJoystickSource == analogSource)
{
const auto axisToDisplay = [](int16_t axis, int minimum,
unsigned extent) -> int
Expand All @@ -504,11 +563,17 @@ bool CLibretroInputSystem::Poll()
m_analogJoystickCursorY = axisToDisplay(
m_joyAxes[0][AXIS_Y], minY, m_dispH);
}
else if (m_analogJoystickSource == 1 && analogMouseMoved)
else if (m_analogJoystickSource == mouseSource && analogMouseMoved)
{
m_analogJoystickCursorX += mouseDeltaX[0];
m_analogJoystickCursorY += mouseDeltaY[0];
}
else if (m_analogJoystickSource == lightgunSource &&
analogLightgunValid)
{
m_analogJoystickCursorX = m_mouseAxes[2][AXIS_X];
m_analogJoystickCursorY = m_mouseAxes[2][AXIS_Y];
}
m_analogJoystickCursorX = std::clamp(
m_analogJoystickCursorX, minX, maxX);
m_analogJoystickCursorY = std::clamp(
Expand All @@ -524,24 +589,22 @@ bool CLibretroInputSystem::Poll()
m_mouseAxes[analogJoystickDev][AXIS_Z] = 0;
m_mouseWheelDir[analogJoystickDev] = 0;
m_mouseIsAbsolute[analogJoystickDev] = true;
const bool analogAllowMouse =
g_options.star_wars_input == StarWarsInput::Hybrid ||
g_options.star_wars_input == StarWarsInput::Mouse;
const bool analogAllowStick =
g_options.star_wars_input == StarWarsInput::Hybrid ||
g_options.star_wars_input == StarWarsInput::AnalogSticks;
m_mouseButtons[analogJoystickDev][0] =
(analogAllowLightgun && lightgunTrigger[0]) ||
(analogAllowMouse && m_mouseButtons[0][0]) ||
(analogAllowStick && m_joyButtons[0][0]);
m_mouseButtons[analogJoystickDev][1] = false;
m_mouseButtons[analogJoystickDev][2] =
(analogAllowLightgun && lightgunAuxA[0]) ||
(analogAllowMouse && m_mouseButtons[0][2]) ||
(analogAllowStick && m_joyButtons[0][1]);
m_mouseButtons[analogJoystickDev][3] =
(analogAllowMouse && m_mouseButtons[0][3]) ||
(analogAllowLightgun && lightgunTrigger[1]) ||
(analogAllowMouse && m_mouseButtons[1][0]) ||
(analogAllowStick && m_joyButtons[0][2]);
m_mouseButtons[analogJoystickDev][4] =
(analogAllowMouse && m_mouseButtons[0][4]) ||
(analogAllowLightgun && lightgunAuxA[1]) ||
(analogAllowMouse && m_mouseButtons[1][2]) ||
(analogAllowStick && m_joyButtons[0][3]);

return true;
Expand Down
2 changes: 1 addition & 1 deletion Src/OSD/libretro/CLibretroInputSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class CLibretroInputSystem : public CInputSystem
// MOUSE/MOUSE2 = RetroMouse ports 1/2
// MOUSE3/MOUSE4 = RetroLightgun ports 1/2
// MOUSE5/MOUSE6 = virtual gun cursors for players 1/2
// MOUSE7 = virtual Star Wars analog joystick (stick or relative mouse)
// MOUSE7 = virtual Star Wars analog joystick (lightgun, mouse or stick)
static constexpr int kMouseDeviceCount = 7;
int16_t m_mouseAxes[kMouseDeviceCount][NUM_MOUSE_AXES];
bool m_mouseButtons[kMouseDeviceCount][NUM_MOUSE_BUTTONS];
Expand Down
4 changes: 3 additions & 1 deletion Src/OSD/libretro/CoreOptionsTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ enum class GunInput {
};

enum class StarWarsInput {
Hybrid, // mouse or absolute RetroPad stick, last moved wins
Hybrid, // RetroLightgun, RetroMouse or absolute RetroPad stick
Lightgun, // RetroLightgun absolute screen coordinates
Mouse, // RetroMouse relative movement
MouseAnalog, // Hybrid variant that excludes RetroLightgun input
AnalogSticks // RetroPad left stick as an absolute arcade yoke
};

Expand Down
8 changes: 8 additions & 0 deletions Src/OSD/libretro/LibretroConfigProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,14 @@ namespace LibretroConfigProvider {
"KEY_D,MOUSE7_BUTTON4");
config.Set<std::string>("InputAnalogJoyEvent2",
"KEY_F,MOUSE7_BUTTON5");
config.Set<std::string>("InputStart1",
"KEY_1,JOY1_BUTTON9,MOUSE1_BUTTON4");
config.Set<std::string>("InputCoin1",
"KEY_3,JOY1_BUTTON10,MOUSE1_BUTTON5");
config.Set<std::string>("InputStart2",
"KEY_2,JOY2_BUTTON9,MOUSE2_BUTTON4");
config.Set<std::string>("InputCoin2",
"KEY_4,JOY2_BUTTON10,MOUSE2_BUTTON5");
}

inline void ApplyFishingLayout(Util::Config::Node &config)
Expand Down
105 changes: 84 additions & 21 deletions Src/OSD/libretro/libretro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1976,37 +1976,100 @@ void set_input_descriptors(const Game *game)
break;

case LibretroInputProfiles::Family::AnalogJoystick:
if (g_active_star_wars_input != StarWarsInput::Mouse)
{
const auto add_star_wars_lightgun = [&add](bool identify_source)
{
add(0, RETRO_DEVICE_LIGHTGUN, 0,
RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X,
identify_source ? "Analog Joystick X (Lightgun)" :
"Analog Joystick X");
add(0, RETRO_DEVICE_LIGHTGUN, 0,
RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y,
identify_source ? "Analog Joystick Y (Lightgun)" :
"Analog Joystick Y");
add(0, RETRO_DEVICE_LIGHTGUN, 0,
RETRO_DEVICE_ID_LIGHTGUN_TRIGGER,
identify_source ? "Trigger 1 (Lightgun)" : "Trigger 1");
add(0, RETRO_DEVICE_LIGHTGUN, 0,
RETRO_DEVICE_ID_LIGHTGUN_AUX_A,
identify_source ? "Event 1 (Lightgun)" : "Event 1");
add(1, RETRO_DEVICE_LIGHTGUN, 0,
RETRO_DEVICE_ID_LIGHTGUN_TRIGGER,
identify_source ? "Trigger 2 (Lightgun)" : "Trigger 2");
add(1, RETRO_DEVICE_LIGHTGUN, 0,
RETRO_DEVICE_ID_LIGHTGUN_AUX_A,
identify_source ? "Event 2 (Lightgun)" : "Event 2");
};
const auto add_star_wars_mouse = [&add](bool identify_source)
{
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_X,
identify_source ? "Analog Joystick X (Mouse)" :
"Analog Joystick X");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_Y,
identify_source ? "Analog Joystick Y (Mouse)" :
"Analog Joystick Y");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_LEFT,
identify_source ? "Trigger 1 (Mouse)" : "Trigger 1");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_RIGHT,
identify_source ? "Event 1 (Mouse)" : "Event 1");
add(1, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_LEFT,
identify_source ? "Trigger 2 (Mouse)" : "Trigger 2");
add(1, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_RIGHT,
identify_source ? "Event 2 (Mouse)" : "Event 2");
};
const auto add_star_wars_analog = [&add](bool identify_source)
{
add(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT,
RETRO_DEVICE_ID_ANALOG_X, "Analog Joystick X");
RETRO_DEVICE_ID_ANALOG_X,
identify_source ? "Analog Joystick X (Analog Stick)" :
"Analog Joystick X");
add(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT,
RETRO_DEVICE_ID_ANALOG_Y, "Analog Joystick Y");
RETRO_DEVICE_ID_ANALOG_Y,
identify_source ? "Analog Joystick Y (Analog Stick)" :
"Analog Joystick Y");
add(0, RETRO_DEVICE_JOYPAD, 0,
RETRO_DEVICE_ID_JOYPAD_B, "Trigger 1");
RETRO_DEVICE_ID_JOYPAD_B,
identify_source ? "Trigger 1 (Analog)" : "Trigger 1");
add(0, RETRO_DEVICE_JOYPAD, 0,
RETRO_DEVICE_ID_JOYPAD_A, "Event 1");
RETRO_DEVICE_ID_JOYPAD_A,
identify_source ? "Event 1 (Analog)" : "Event 1");
add(0, RETRO_DEVICE_JOYPAD, 0,
RETRO_DEVICE_ID_JOYPAD_Y, "Trigger 2");
RETRO_DEVICE_ID_JOYPAD_Y,
identify_source ? "Trigger 2 (Analog)" : "Trigger 2");
add(0, RETRO_DEVICE_JOYPAD, 0,
RETRO_DEVICE_ID_JOYPAD_X, "Event 2");
}
if (g_active_star_wars_input != StarWarsInput::AnalogSticks)
RETRO_DEVICE_ID_JOYPAD_X,
identify_source ? "Event 2 (Analog)" : "Event 2");
};

switch (g_active_star_wars_input)
{
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_X, "Analog Joystick X");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_Y, "Analog Joystick Y");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_LEFT, "Trigger 1");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_RIGHT, "Event 1");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_BUTTON_4, "Trigger 2");
add(0, RETRO_DEVICE_MOUSE, 0,
RETRO_DEVICE_ID_MOUSE_BUTTON_5, "Event 2");
case StarWarsInput::Hybrid:
add_star_wars_lightgun(true);
add_star_wars_mouse(true);
add_star_wars_analog(true);
break;
case StarWarsInput::Lightgun:
add_star_wars_lightgun(false);
break;
case StarWarsInput::MouseAnalog:
add_star_wars_mouse(true);
add_star_wars_analog(true);
break;
case StarWarsInput::Mouse:
add_star_wars_mouse(false);
break;
case StarWarsInput::AnalogSticks:
add_star_wars_analog(false);
break;
}
break;
}

case LibretroInputProfiles::Family::Fishing:
add(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT,
Expand Down
Loading