Skip to content
Merged
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
50 changes: 40 additions & 10 deletions OpenPuck/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ uint16_t g_audioSpeaker = 0;
uint8_t g_hapticLimitKnee = HAPTIC_LIMIT_KNEE_DEFAULT;
uint8_t g_hapticLift = HAPTIC_LIFT_DEFAULT;
uint8_t g_trigHaptics = TRIG_HAPTICS_DEFAULT;
uint8_t g_dpadClick = DPAD_CLICK_DEFAULT;
uint8_t g_padClick = PAD_CLICK_DEFAULT;
uint8_t g_padTick = PAD_TICK_DEFAULT;
uint8_t g_padTickGain = PAD_TICKGAIN_DEFAULT;
Expand Down Expand Up @@ -101,6 +102,9 @@ uint8_t g_typeHaptic[HAP_COUNT][ET_COUNT] = {
/* HAP_TRIG */
{ TRIG_HAPTICS_DEFAULT, TRIG_HAPTICS_DEFAULT, TRIG_HAPTICS_DEFAULT,
TRIG_HAPTICS_DEFAULT },
/* HAP_DPAD */
{ DPAD_CLICK_DEFAULT, DPAD_CLICK_DEFAULT, DPAD_CLICK_DEFAULT,
DPAD_CLICK_DEFAULT },
};
struct TypeHapticSpec {
uint8_t min, max;
Expand All @@ -114,12 +118,21 @@ static const TypeHapticSpec kTypeHaptic[HAP_COUNT] = {
/* HAP_TICKGAIN */ { PAD_CLICK_MIN, PAD_CLICK_MAX, 0xF },
/* HAP_LIFT */ { 0, HAPTIC_LIFT_MAX, (1u << ET_SWITCH) | (1u << ET_DS5) },
/* HAP_TRIG */ { 0, TRIG_HAPTICS_BOTH, 1u << ET_DS5 },
/* HAP_DPAD */ { 0, 1, 0xF },
};
static bool typeHapticInRange(uint8_t setting, uint8_t value)
{
return value >= kTypeHaptic[setting].min &&
value <= kTypeHaptic[setting].max;
}
bool typeQamSelectSet(uint8_t et, uint8_t value)
{
if (et >= ET_COUNT || value > QAM_SELECT_MAX)
return false;
g_typeQamSelect[et] = value;
applyActiveType();
return true;
}
bool typeHapticSet(uint8_t setting, uint8_t et, uint8_t value)
{
if (setting >= HAP_COUNT || et >= ET_COUNT ||
Expand All @@ -134,8 +147,9 @@ uint8_t g_createAsTouch = 0;
uint8_t g_trigInner = 0, g_trigOuter = 100;
uint8_t g_ledBright = 0;

uint8_t g_swDpadHaptics = 1;
uint8_t g_swQamSelect = 18;
// no type has a target until one is chosen, and the chord does nothing without one
uint8_t g_typeQamSelect[ET_COUNT] = { 0, 0, 0, 0 };
uint8_t g_qamSelect = 0;
// Default = the original shortcut arrangement: all four back buttons, controller modes, no D-pad haptic
// shortcuts, no confirmation pulses, no Quick Access + Select capture.
uint8_t g_shortcutFlags = SHORTCUT_ENABLED;
Expand All @@ -159,8 +173,7 @@ void captureFeedbackChord(uint8_t slot, uint32_t buttons)
static bool held[NSLOT] = {};
if (slot >= NSLOT)
return;
bool active = g_usbMode == MODE_SW_PRO && g_swQamSelect &&
(g_shortcutFlags & SHORTCUT_CAPTURE) &&
bool active = g_qamSelect &&
(buttons & (TB_QAM | TB_MENU)) == (TB_QAM | TB_MENU);
if (active && !held[slot])
hapticShortcutFeedback(slot, 1);
Expand All @@ -177,6 +190,7 @@ void applyActiveType()
g_back[2] = 7;
g_back[3] = 8;
g_qamMap = 0;
g_qamSelect = 0;
g_abSwap = 0;
g_padHaptics = 1;
g_rumble = 1;
Expand Down Expand Up @@ -206,6 +220,8 @@ void applyActiveType()
g_hapticLimitKnee = g_typeHaptic[HAP_KNEE][g_etype];
g_hapticLift = g_typeHaptic[HAP_LIFT][g_etype];
g_trigHaptics = g_typeHaptic[HAP_TRIG][ET_DS5];
g_qamSelect = g_typeQamSelect[g_etype];
g_dpadClick = g_typeHaptic[HAP_DPAD][g_etype];
g_audioHapticStyle = g_typeHaptic[HAP_STYLE][ET_DS5];
g_padClick = g_typeHaptic[HAP_CLICK][g_etype];
g_padTick = g_typeHaptic[HAP_TICK][g_etype];
Expand Down Expand Up @@ -274,6 +290,9 @@ struct Cfg {
// per-type haptic settings, g_typeHaptic's grid. 0xFF or out of range (an older file) -> the default, with the
// limiter and the DualSense style taken from the legacy shared ext bytes 14 and 11
uint8_t typeHaptic[HAP_COUNT][ET_COUNT];
// per-type Quick Access + Select target, g_typeQamSelect. 0xFF (older file) -> the Switch's from swQamSelect
// when its enable switch was on, the rest none
uint8_t typeQamSelect[ET_COUNT];
}; // rsvd0 = ex-padSmooth, now the one-shot debug-CDC arm

// Shortest cfg.bin we still accept: the layout as of CFG_MAGIC 0xCF, i.e. everything before the appended tail.
Expand Down Expand Up @@ -354,14 +373,14 @@ void saveCfg()
g_suspendOff,
{},
{},
g_swDpadHaptics,
g_typeHaptic[HAP_DPAD][ET_SWITCH],
(uint8_t)(g_hdPadScale / 2),
{ 0xFF, 0xFF },
0xFF,
{ 0xFF, 0xFF },
0xFF,
0xFF,
g_swQamSelect,
g_typeQamSelect[ET_SWITCH],
g_shortcutFlags };
memcpy(c.ext, g_cfgExt, sizeof c.ext);
c.createAsTouch = g_createAsTouch;
Expand All @@ -372,6 +391,7 @@ void saveCfg()
c.typeRumbleScale2[i] = (uint8_t)(g_typeRumbleScale[i] / 2);
for (int k = 0; k < HAP_COUNT; k++)
c.typeHaptic[k][i] = g_typeHaptic[k][i];
c.typeQamSelect[i] = g_typeQamSelect[i];
c.padStick[i][0] = g_padStickCfg[i][0];
c.padStick[i][1] = g_padStickCfg[i][1];
}
Expand Down Expand Up @@ -552,9 +572,10 @@ void loadCfg()
g_typeHaptic[k][et] = g_hapticLimitKnee;
else if (k == HAP_STYLE && et == ET_DS5)
g_typeHaptic[k][et] = g_audioHapticStyle;
else if (k == HAP_DPAD && et == ET_SWITCH &&
c.swDpadHaptics <= 1)
g_typeHaptic[k][et] = c.swDpadHaptics;
}
if (c.swDpadHaptics <= 1)
g_swDpadHaptics = c.swDpadHaptics;
if (c.hdPadScale2 <= 250)
g_hdPadScale = (uint16_t)c.hdPadScale2 * 2;
// per-type grip strength; a file from before it seeds every type with the legacy global value, unless that
Expand All @@ -568,8 +589,17 @@ void loadCfg()
g_typeRumbleScaleDefault[et] :
g_rumbleScale;
}
if (c.swQamSelect <= 20)
g_swQamSelect = c.swQamSelect;
for (uint8_t et = 0; et < ET_COUNT; et++) {
if (c.typeQamSelect[et] <= QAM_SELECT_MAX)
g_typeQamSelect[et] = c.typeQamSelect[et];
// the shortcut was Switch-only and needed its enable switch: a file from then keeps the Switch's target
// only if that switch was on
else if (et == ET_SWITCH && c.shortcutFlags <= 63 &&
(c.shortcutFlags & SHORTCUT_CAPTURE))
g_typeQamSelect[et] = c.swQamSelect <= QAM_SELECT_MAX ?
c.swQamSelect :
18;
}
// bits 1 and 2 were the removed profiles and D-pad haptic shortcut behaviors
if (c.shortcutFlags <= 63)
g_shortcutFlags = c.shortcutFlags & ~6u;
Expand Down
15 changes: 13 additions & 2 deletions OpenPuck/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,15 @@ extern uint8_t
// applyActiveType() at boot and after any edit to the active type.
extern uint8_t g_abSwap; // 1 = swap A/B and X/Y (Nintendo face-button layout)
extern uint8_t g_back[4];
extern uint8_t g_swDpadHaptics;

extern uint8_t g_qamMap;
extern uint8_t g_swQamSelect;
// What Quick Access + Select (Menu) acts as, a remap target code (0 none; 18 is the Switch's Capture), per
// controller type; g_qamSelect is the active type's copy. None until chosen: the chord needs no other switch.
extern uint8_t g_typeQamSelect[ET_COUNT];
extern uint8_t g_qamSelect;
#define QAM_SELECT_MAX 20
// Store a target for a type (false when out of range) and refresh the live copy.
bool typeQamSelectSet(uint8_t et, uint8_t value);
extern uint8_t g_shortcutFlags;
void shortcutModeRequest(uint8_t mode, uint8_t slot);
void shortcutModeTask();
Expand Down Expand Up @@ -252,6 +257,9 @@ extern uint16_t g_audioSpeaker;
// by up to 1.5x / 2x / 3x / 4x (hapticLiftGain). Switch and DualSense. g_hapticLift is the active type's copy.
// HAP_TRIG: where the DualSense adaptive-trigger effects are felt (TRIG_HAPTICS_*): off, the trackpads, the
// grips, or both. DualSense only. g_trigHaptics is the live copy.
// HAP_DPAD: 1 = a click on a trackpad in a D-pad mode pulses that pad while Trackpad haptics is off
// (padDpadClickFeedback). Every type; the Switch's starts from the old Switch-only setting.
// g_dpadClick is the active type's copy.
// Click, tick and tick gain apply to every type.
enum {
HAP_KNEE,
Expand All @@ -261,6 +269,7 @@ enum {
HAP_TICKGAIN,
HAP_LIFT,
HAP_TRIG,
HAP_DPAD,
HAP_COUNT
};
// Measured on the controller's IMU (Switch HD and DualSense wave), the grips stay linear to ~0.7 of full scale
Expand All @@ -279,10 +288,12 @@ enum {
#define TRIG_HAPTICS_GRIPS 2
#define TRIG_HAPTICS_BOTH 3
#define TRIG_HAPTICS_DEFAULT TRIG_HAPTICS_PADS
#define DPAD_CLICK_DEFAULT 1
extern uint8_t g_typeHaptic[HAP_COUNT][ET_COUNT];
extern uint8_t g_hapticLimitKnee;
extern uint8_t g_hapticLift;
extern uint8_t g_trigHaptics;
extern uint8_t g_dpadClick;
// Store a value if it is in range and the type has the setting, and refresh the live mirrors.
bool typeHapticSet(uint8_t setting, uint8_t et, uint8_t value);
// Live copies of the active type's click strength and tick level (the defaults in puck modes)
Expand Down
133 changes: 107 additions & 26 deletions OpenPuck/gamepad_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,33 +203,42 @@ void padStickBlend(uint32_t b, int16_t lpx, int16_t lpy, int16_t rpx,
}
}

uint32_t padDpadButtons(const PuckInput &in)
static bool padIsDpad(uint8_t p)
{
if (shortcutHeld(in.buttons))
return g_padStick[p] == PS_DPAD_TOUCH || g_padStick[p] == PS_DPAD_CLICK;
}

// The D-pad directions pad p (0 left, 1 right) is pointing at, or 0 when it isn't touched, is not on a D-pad
// mode, or (on click) isn't clicked.
static uint32_t padDpadOne(uint8_t p, uint32_t raw, int16_t px, int16_t py)
{
static const uint32_t touched[2] = { TB_LPADT, TB_RPADT };
static const uint32_t clicked[2] = { TB_LPADC, TB_RPADC };
uint8_t mode = g_padStick[p];
if (!padIsDpad(p) || !(raw & touched[p]))
return 0;
if (mode == PS_DPAD_CLICK && !(raw & clicked[p]))
return 0;
int32_t x = px, y = py;
int32_t ax = x < 0 ? -x : x, ay = y < 0 ? -y : y;
if (ax < 6000 && ay < 6000 && ax * ax + ay * ay < 36000000)
return 0;
const int16_t px[2] = { in.lpx, in.rpx };
const int16_t py[2] = { in.lpy, in.rpy };
const uint32_t touched[2] = { TB_LPADT, TB_RPADT };
const uint32_t clicked[2] = { TB_LPADC, TB_RPADC };
uint32_t out = 0;
for (int p = 0; p < 2; p++) {
uint8_t mode = g_padStick[p];
if (mode != PS_DPAD_TOUCH && mode != PS_DPAD_CLICK)
continue;
if (!(in.buttons & touched[p]))
continue;
if (mode == PS_DPAD_CLICK && !(in.buttons & clicked[p]))
continue;
int32_t x = px[p], y = py[p];
int32_t ax = x < 0 ? -x : x, ay = y < 0 ? -y : y;
if (ax < 6000 && ay < 6000 && ax * ax + ay * ay < 36000000)
continue;
// tan(22.5 degrees) separates cardinal sectors from diagonals.
if (ax * 1000 >= ay * 414)
out |= x < 0 ? TB_DLF : TB_DRT;
if (ay * 1000 >= ax * 414)
out |= y < 0 ? TB_DDN : TB_DUP;
}
// tan(22.5 degrees) separates cardinal sectors from diagonals.
if (ax * 1000 >= ay * 414)
out |= x < 0 ? TB_DLF : TB_DRT;
if (ay * 1000 >= ax * 414)
out |= y < 0 ? TB_DDN : TB_DUP;
return out;
}

uint32_t padDpadBits(uint32_t raw, int16_t lpx, int16_t lpy, int16_t rpx,
int16_t rpy)
{
if (shortcutHeld(raw))
return 0;
uint32_t out = padDpadOne(0, raw, lpx, lpy) |
padDpadOne(1, raw, rpx, rpy);
// Opposing pads must not produce an invalid D-pad combination.
if ((out & (TB_DLF | TB_DRT)) == (TB_DLF | TB_DRT))
out &= ~(TB_DLF | TB_DRT);
Expand All @@ -238,6 +247,62 @@ uint32_t padDpadButtons(const PuckInput &in)
return out;
}

uint32_t padDpadApply(uint32_t raw, int16_t lpx, int16_t lpy, int16_t rpx,
int16_t rpy)
{
uint32_t out = raw | padDpadBits(raw, lpx, lpy, rpx, rpy);
if (shortcutHeld(raw))
return out;
if (g_padStick[0] == PS_DPAD_CLICK)
out &= ~TB_LPADC;
if (g_padStick[1] == PS_DPAD_CLICK)
out &= ~TB_RPADC;
return out;
}

void padDpadClickFeedback(uint8_t slot)
{
static uint32_t held[NSLOT] = {};
static uint32_t dir[NSLOT][2] = {};
if (slot >= NSLOT)
return;
const PuckInput &in = g_in[slot];
uint32_t buttons = in.buttons;
uint32_t clicks = buttons & (TB_LPADC | TB_RPADC);
uint32_t pressed = clicks & ~held[slot];
held[slot] = clicks;
bool held4 = shortcutHeld(buttons);
bool moved[2];
for (int p = 0; p < 2; p++) {
uint32_t now = held4 ? 0 :
padDpadOne(p, buttons,
p ? in.rpx : in.lpx,
p ? in.rpy : in.lpy);
// A new direction pulses, so sliding from down to down-right is felt without lifting. Going from none
// to a direction on click is the click itself, which the click pulse below covers.
moved[p] = now && now != dir[slot][p] &&
!(g_padStick[p] == PS_DPAD_CLICK && !dir[slot][p]);
dir[slot][p] = now;
}
if (!g_dpadClick || held4 || haptic82Blocked(slot))
return;
// With Trackpad haptics on, every click already pulses (the controller's own, or the puck's), so a click
// pulse here would play twice; direction changes are not clicks, so they always pulse.
bool left = moved[0], right = moved[1];
if (g_padHaptics == PAD_HAPTICS_OFF) {
left |= (pressed & TB_LPADC) && (buttons & TB_LPADT) &&
padIsDpad(0);
right |= (pressed & TB_RPADC) && (buttons & TB_RPADT) &&
padIsDpad(1);
}
if (left || right) {
const uint8_t pulse[3] = { hsidePads(left, right), 2,
padClickGain() };
// ahead of any queued PCM stream frames, which would delay the click by their length
relayEnqueueFront(0x82, pulse, sizeof pulse, true, slot);
}
}

void slotSticks(uint8_t slot, int16_t *lx, int16_t *ly, int16_t *rx,
int16_t *ry)
{
Expand Down Expand Up @@ -378,9 +443,25 @@ void steamPadsToTouch(uint32_t b, uint16_t touchH, int16_t lpx, int16_t lpy,
}
}

uint32_t psButtonsFromSteam(uint32_t raw)
uint32_t remapHostButtons(uint8_t slot, uint32_t raw, bool *capture)
{
return remapButtons(shortcutHostButtons(raw));
uint32_t b = raw;
bool qamSelect = qamSelectShortcut(slot, b);
b = shortcutHostButtons(b);
// QAM + Select is its own shortcut: QAM does not also act as its own mapping
if (qamSelect)
b &= ~(uint32_t)TB_QAM;
bool cap = false;
b = remapButtons(b, &cap);
if (qamSelect) {
const RemapTarget t =
remapTarget(g_qamSelect, remapFacesSwapped(g_btnMap));
b |= t.tb;
cap |= t.capture;
}
if (capture)
*capture = cap;
return b;
}
// DualSense / DS4 buttons[1]: L1..R3, Create(Share), Options(Start). The analog trigger values come from
// the per-slot `g_in[slot]` (pass lt/rt explicitly so a single shoulder byte never leaks across slots).
Expand Down
Loading
Loading