fix: retain the lower player slot when joining Joy-Cons
This commit is contained in:
parent
06a89b5ae1
commit
b0a896997b
9 changed files with 61 additions and 1 deletions
|
|
@ -678,6 +678,13 @@ all chord buttons must release before retry. Confirmation is bounded to the
|
|||
participating halves. Disconnect/reuse or a changed saved default clears the
|
||||
override, without writing a per-pair preference.
|
||||
|
||||
Explicit gesture joins retain the lower participating player slot rather than
|
||||
always retaining the left half's slot. A regression reproduced the old player-2
|
||||
result with the left half ready first but occupying the higher transport/player
|
||||
index. Both ready orders and an earlier unrelated player are covered. Hardware
|
||||
with right on player 1 and left on player 2 now joins on player 1; all profile
|
||||
banks remain unchanged.
|
||||
|
||||
All 349 tests and all five builds passed. Real hardware exercised both live
|
||||
mode transitions, Individual boot persistence, physical join/split pulses,
|
||||
unchanged configuration generations during gestures, and automatic default
|
||||
|
|
|
|||
|
|
@ -237,6 +237,9 @@ uv run switch-pico-config joycon-mode paired --json
|
|||
pulse, then **release all four buttons**.
|
||||
|
||||
Two solo halves join; an existing pair splits. Other controllers are untouched.
|
||||
An explicit join keeps the lower of the two participating player slots, not
|
||||
whichever slot belongs to the left half. Both LEDs follow that retained slot;
|
||||
the other slot is neutralized. It never takes a slot from an unrelated player.
|
||||
The shortcut does not change the saved default: its connection-only override
|
||||
ends when either participating half disconnects, or when the adapter default
|
||||
changes. Existing solo/pair banks and active selections are reused.
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -2665,8 +2665,14 @@ void process_joycon_gestures(uint32_t now_ms) {
|
|||
if (!mature) continue;
|
||||
// Latch success AND failure before any enrollment I/O. A failed seed
|
||||
// must not retry at the timer cadence or undo either participant.
|
||||
// Player-slot ownership is independent of physical handedness.
|
||||
// Keep the lower occupied slot, even when the left half owns the higher one.
|
||||
const bool keep_left_slot = owner_slot < right_slot;
|
||||
const bool changed = joining
|
||||
? merge_joycon_slots(owner_slot, right_slot, right, true)
|
||||
? merge_joycon_slots(
|
||||
keep_left_slot ? owner_slot : right_slot,
|
||||
keep_left_slot ? right_slot : owner_slot,
|
||||
keep_left_slot ? right : gesture.device, true)
|
||||
: split_joycon_slot(static_cast<uint8_t>(owner_slot), true);
|
||||
if (changed) recompute_connection_status();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1679,6 +1679,41 @@ void test_switch2_gesture_timing() {
|
|||
"connection gestures must never write the saved adapter preference");
|
||||
}
|
||||
|
||||
void test_switch2_gesture_slot_order(bool left_first, uint8_t lower_slot) {
|
||||
runtime_configuration.joycon_mode = JoyConMode::kIndividual;
|
||||
start_backend();
|
||||
auto ordinary = device(0, true, UNI_BT_CONN_PROTOCOL_BR_EDR);
|
||||
auto left = switch2_device(lower_slot + 1, UNI_SW2_JOYCON_L_PID);
|
||||
auto right = switch2_device(lower_slot, UNI_SW2_JOYCON_R_PID);
|
||||
if (lower_slot != 0) ready_switch2(ordinary);
|
||||
// Transport indices can put the left half in the higher player slot,
|
||||
// even when its ready callback arrives first.
|
||||
ready_switch2(left_first ? left : right);
|
||||
ready_switch2(left_first ? right : left);
|
||||
const auto lower_before = slot_snapshot(lower_slot);
|
||||
const auto higher_before = slot_snapshot(lower_slot + 1);
|
||||
const auto unrelated_before = slot_snapshot(0);
|
||||
hold_gesture(left, right, 2000);
|
||||
require(live_pair_slot(left, right) == lower_slot &&
|
||||
!slot_snapshot(lower_slot + 1).active,
|
||||
"gesture join must retain the lower participating player slot regardless of side or ready order");
|
||||
const auto joined = slot_snapshot(lower_slot);
|
||||
require_pair_owner(joined.identity, left, right);
|
||||
require(joined.connection_generation != lower_before.connection_generation &&
|
||||
slot_snapshot(lower_slot + 1).connection_generation != higher_before.connection_generation &&
|
||||
slot_snapshot(lower_slot + 1).state.left_stick_x == 0 &&
|
||||
slot_snapshot(lower_slot + 1).state.right_stick_x == 0 &&
|
||||
left.player_leds == (1u << lower_slot) &&
|
||||
right.player_leds == (1u << lower_slot),
|
||||
"join must invalidate old player epochs, neutralize the retired slot and light both halves for the retained player");
|
||||
if (lower_slot != 0) {
|
||||
require(slot_snapshot(0).active &&
|
||||
slot_snapshot(0).connection_generation == unrelated_before.connection_generation &&
|
||||
controller_identity_equal(slot_snapshot(0).identity, unrelated_before.identity),
|
||||
"gesture join must not take an earlier slot occupied by an unrelated controller");
|
||||
}
|
||||
}
|
||||
|
||||
void test_switch2_gesture_stale() {
|
||||
runtime_configuration.joycon_mode = JoyConMode::kIndividual;
|
||||
start_backend();
|
||||
|
|
@ -5377,6 +5412,12 @@ int main(int argc, char** argv) {
|
|||
test_switch2_hd_pro();
|
||||
} else if (scenario == "switch2-gesture-timing") {
|
||||
test_switch2_gesture_timing();
|
||||
} else if (scenario == "switch2-gesture-slot-left-first") {
|
||||
test_switch2_gesture_slot_order(true, 0);
|
||||
} else if (scenario == "switch2-gesture-slot-right-first") {
|
||||
test_switch2_gesture_slot_order(false, 0);
|
||||
} else if (scenario == "switch2-gesture-slot-occupied") {
|
||||
test_switch2_gesture_slot_order(true, 1);
|
||||
} else if (scenario == "switch2-gesture-stale") {
|
||||
test_switch2_gesture_stale();
|
||||
} else if (scenario == "switch2-gesture-clock-wrap") {
|
||||
|
|
|
|||
|
|
@ -108,6 +108,9 @@ def test_bluepad32_backend_lifecycle_native(tmp_path: Path) -> None:
|
|||
"switch2-mode-seed-failure",
|
||||
"switch2-mode-identity-failure",
|
||||
"switch2-gesture-timing",
|
||||
"switch2-gesture-slot-left-first",
|
||||
"switch2-gesture-slot-right-first",
|
||||
"switch2-gesture-slot-occupied",
|
||||
"switch2-gesture-stale",
|
||||
"switch2-gesture-clock-wrap",
|
||||
"switch2-gesture-masking-epochs",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue