Skip to content

Expose opt-in automatic discovery for native controller reconnect - #71

Merged
jmonster merged 2 commits into
mainfrom
feature/native-automatic-discovery
Sep 18, 2026
Merged

jmonster merged 2 commits into
mainfrom
feature/native-automatic-discovery

Conversation

@jmonster

@jmonster jmonster commented Sep 18, 2026

Copy link
Copy Markdown
Owner

Completion status

Merged as 38c57c5dcdfbfc42993345d50172a2001b39f6a6. All eight workflows for corrected feature head a9d43b1f63d94f8844755a510bf6ecc876bc8c51 completed successfully. The previously failing native fixture and formerly pending native consumers, Linux Dolphin/Cemu builds, macOS application builds, and separate launch/shutdown checks pass.

Companion Dolphin #3 is also merged and pins the corrected SDK commit. The SDK change is now on main, so it is no longer an outstanding PR dependency for that host feature. Both merges were observed through GitHub; the final completion audit verified CI and updated this description but did not execute either merge operation or make additional code changes.

Problem and implementation

Dolphin's finite 60-second native discovery window can expire while a controller is off during a long pause. The Swift engine already has an event-driven automatic policy; this PR exposes that policy to native hosts.

  • Add s2k_set_automatic_discovery(context, enabled) as an additive ABI-v1 symbol. Existing layouts, on-demand defaults, and old consumers remain unchanged.
  • Forward the opt-in to the existing .automatic transport policy: no host polling timer or repeated Find calls. Handshake serialization, capacity limits and bounded transport retries remain in force.
  • Validate 0/1, serialize with lifecycle operations, and make repeated configuration idempotent. Configuration never starts Bluetooth, revives a stopped context, consumes input or replaces ready sessions. Return BUSY during asynchronous stop.
  • Keep the preference across stop/start on the same context. Hosts own consent, persistence and main-thread creation.
  • Add four C-facade regressions, including calls through the C declaration, a simulated long-absence policy regression, and native-host guidance.

The Dolphin companion supplies the persistent opt-in checkbox, main-thread startup and authoritative manual Disconnect. Automatic discovery admits available supported controllers, not arbitrary Bluetooth devices, and is not a remembered-device allowlist.

Native-fixture correction

The original change added ControllerSource.setAutomaticDiscovery(_:) but missed the separate SDLTestSource in tests/sdl-inprocess/Fixture.swift. The investigated emulator-host, Apple Silicon SDL, Intel SDL, and Linux-consumer failures reported that missing protocol conformance after the production library built.

The published correction implements the method in the actual shared fixture, recording policy changes under its mutex without starting a radio or changing ready sessions. A new Switch2KitSDLFixtureTests SwiftPM test target compiles that same file during ordinary swift test; native CMake continues to compile it separately. Three additional regressions cover C-entry-point validation/idempotence/radio-free configuration, preserved sessions and queued input, and asynchronous stop/restart.

No production protocol fallback, disabled check, relaxed warning policy or fabricated status was added. Distribution products are unchanged. The correction touches only the fixture, its new tests and their package-test registration.

Completed corrected-head CI

Workflow Final result
Linux BlueZ and native hosts PASS: package/radio/portable tests, release tests, relocated facade/C++ consumers, real SDL consumer, complete Dolphin build/install, and complete Cemu build/install/relocation
macOS build and regressions PASS
Swift 6 concurrency PASS
Swift source and native C distribution PASS
SDL input regressions PASS
In-process SDL3 native consumers PASS
Packaged macOS runtime qualification PASS
Emulator integration PASS: host tests, full Dolphin/Cemu builds and inspections on arm64 and x86_64, and all four separate launch/shutdown jobs

The completed Linux and emulator job/step results were inspected. These are results for the corrected feature head, not the failing predecessor or newly executed post-merge checks. No submitted reviews or unresolved review threads were present at the pre-merge completion check.

Test-source provenance and previously recorded local validation

The retained corrected-head CI source archive was previously checked: ZIP SHA-256 7b99eb4e74fdf141e59b821ef2ed56b80b372618c8925be024691a4d85c4903f; reconstructed tree df0adbaccb951bb014bcb037a87f4e217d6bcbd3; synthetic test merge 53db7db74d73aff64ca02982e982ece0a4465d7b. This was the CI merge with the then-advanced base, not a claim that its entire tree equals the feature head. Main incorporated #68 before this PR was merged; the runs above are not relabeled as new CI on the final combined main tree.

Previously recorded on Linux / Swift 6.2.1:

  • 100 debug / 100 release Swift tests passed, warnings treated as errors, including all three native-fixture tests.
  • Negative control: removing only the new fixture method makes ordinary swift test --filter SDLFixtureTests fail with the original conformance error. Restoring it passes all three tests.
  • Independently compile the actual Fixture.swift as a shared library with the CMake package-identity/module-map helper, warnings as errors; link and execute a C++ consumer against that fixture and the actual facade. Policy validation, radio-free configuration, preserved connected input, and stopped-state fencing pass.
  • Eight fixture package-identity helper checks and the production ownership-boundary check pass.

Those local results were recorded during implementation, not newly executed in this final audit. The first original debug invocation hit the execution limit; its completed incremental rerun passed all 100 tests. No incomplete run is counted as passing. Earlier syntax, public C11/C++20 declaration, simulated 48-hour policy, and whitespace checks remain supplemental.

Hardware boundary

No physical controller/radio or gameplay test was performed for this change. Acceptance must include more than 60 seconds of absence, paused gameplay, repeated wake cycles, Bluetooth off/on, two controllers, and explicit Disconnect staying stopped. Native builds and fixture regressions do not establish physical reconnection. Linux retains its experimental qualification boundary. No release was published by the completion audit.

Prepared with AI assistance.

Reuse the existing event-driven automatic discovery policy so native hosts can recover controllers after timed discovery expires. Preserve radio-free creation, explicit stop, frozen ABI-v1 layouts, and ready-session/input-reader state. Add C-declaration, lifecycle, concurrency and long-absence policy regressions.

AI-assisted implementation. Physical Bluetooth recovery remains to be validated.

Copy link
Copy Markdown
Owner Author

Companion Dolphin implementation is open at jmonster/dolphin#3 and pins this PR's exact head ef26a17eb018ab6db6c944c33153ced332e2de91.

It adds a saved, default-off Automatically connect Switch 2 controllers option, one-shot main-thread startup after SDL, continuous listening status, and an explicit-stop fence that polling/settings refresh cannot bypass. Turning discovery off retains ready controllers and mappings. The production Dolphin host wrapper passed local Clang and GCC ASan/UBSan checks; four new UI/startup wiring checks also passed.

On this SDK head, Swift concurrency and the Linux debug/release package-test steps have completed successfully. Remaining workflow/app/hardware qualification is separate; no physical Mac/controller recovery test has been performed. Merge order: SDK first, then the pinned Dolphin change.

Implement the new ControllerSource requirement in the shared SDLTestSource, recording policy changes without simulating a radio or disturbing input. Compile that exact fixture in a new SwiftPM test target and cover C-entry-point validation, idempotence, preserved queued input, and explicit stop/restart behavior.

This addresses the native SDL, emulator-host, and Linux consumer build failures in #71. Production behavior and existing CI gates are unchanged. AI-assisted correction; native CI results remain separate from hardware qualification.

Copy link
Copy Markdown
Owner Author

The reported native-consumer failures exposed an omission in my original change: ControllerSource gained setAutomaticDiscovery(_:), but the separate tests/sdl-inprocess/Fixture.swift implementation did not. Both the native SDL and emulator-host logs fail on that exact conformance error after building the production C facade.

Pushed correction a9d43b1f63d94f8844755a510bf6ecc876bc8c51 to this PR (fast-forward, no merge):

  • Implement the method in the actual shared SDLTestSource, recording policy changes under its mutex without starting a radio or replacing input/session state.
  • Compile that same file in the regular SwiftPM test suite, rather than checking only the separate TestSource used by the existing C tests.
  • Add three fixture regressions for C-symbol validation/idempotence/radio-free configuration, preserving ready sessions and queued input, and authoritative asynchronous stop/restart.

No production protocol fallback/no-op, CI bypass, or disabled assertion was added. Native CMake tests still compile the actual fixture separately with warnings as errors. Distribution products are unchanged. Local Swift syntax and manifest/target checks passed; fresh native CI is now the required validation and is not yet claimed green. I am also updating Dolphin #3 to pin this corrected SDK commit.

@jmonster
jmonster merged commit 38c57c5 into main Sep 18, 2026
22 checks passed
@jmonster
jmonster deleted the feature/native-automatic-discovery branch September 18, 2026 04:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant