Repository navigation
Add Steam Controller 2026 support and per-game mappings - #731
Conversation
v0lumehi
left a comment
There was a problem hiding this comment.
Thanks @thehobbit85 for the Steam Controller work and for the careful description of what you could and could not test.
This one is too big to take as one PR (about 6.3k added lines), and the body says it awaits a milestone issue. It changes controls in the launcher and the engine for every Mac game, so it needs an issue first.
To take it up again:
- Open an issue for Steam Controller 2026 support and per-game mappings. It needs a milestone that we agree on before the code.
- Wait for seedds/nxnative#221 to be reviewed or merged, then the engine part comes in from there. Please also fix the patch author line, which names seedds while #221 is yours.
- Split the launcher editor from the engine carrier patch.
- Test at least a few games on a Mac with the controller, and say which ones.
- Add French, Italian and Swedish texts, and keep
controls.appliedandcontrols.notein those languages.
Next: I will look again when the issue is agreed. It is not for v1.1.2.
|
@v0lumehi, landing the controller support in nxnative first was my intention from the start. I opened seedds/nxnative#221 on October 25 and have kept it updated since then, including addressing the stick-deadzone/gyro findings and incorporating the shared-controls contract discussed in that thread. It is still open without a formal review, and I do not have a clear next step from the maintainers. I am concerned that it has stalled. That is why I tried carrying the implementation here through the patch queue. I understand the requirements for taking this up in NX2Apple: an agreed issue and milestone, separating the launcher editor from the engine carrier, named Mac gameplay tests, and completing the French, Italian and Swedish texts while retaining the existing keys. The carrier description also incorrectly attributes the PR to seedds, and that wording needs correcting. The Bluetooth probe documented here is not a replacement for the gameplay testing you requested. Could you point me to the right upstream maintainer or help establish a concrete review path for #221? What technical changes or additional test evidence are still needed before it can be reviewed and merged? If its size is the obstacle upstream too, would a smaller controller transport/motion/rumble PR followed by the mapping UI be preferable? I am willing to work through a specific checklist, but keeping the PR current without knowing what is blocking review leaves me unsure how to move it forward. A later agreed NX2Apple milestone is fine. My goal is to get the controller implementation accepted upstream first, then build the appropriately scoped launcher support on that foundation. |
|
Thank you @thehobbit85. I understand your point about landing the engine part in nxnative first, and I see seedds/nxnative#221 is open and updated. We want to take this into a release. I opened #764 for it and put it into v1.2.1. An issue in a milestone is the go for this PR. The point is that neither of us has a Steam Controller (2026), so we cannot test it. We can take it if you stay with it. This is what I ask:
If you can do this, I merge it when CI is green and the points are done. |
|
@thehobbit85, thank you for the work you have sent to us and to seedds/nxnative. I read a sample of it. The causes are right, the tests are there, and you say what you did not test. @LNPANDY and I are looking for a few strong people to build this with us. Would you be interested? If yes, tell us how you like to be reached (Discord, Telegram or here) and we talk. |
|
@v0lumehi Thank you so so much for the offer. I am VERY interested in joining. Both Discord and Telegram works for me. If you could please send me an email to thehobbit85@gmail.com, and I will send you my contact information. P.S, The #221 PR for seedds/nxnative was tested by both me and another contributor using Legends ZA and Totk, but as there are a few changes/addition between that PR and the one I am opening here, I am currently in the process of building a couple of games to run tests for the PR here. I am going to report the result of my findings very very soon, just waiting on some builds to finish. |
|
@thehobbit85 I sent you an email |
|
@v0lumehi, following up with the requested gameplay results. I tested the Steam Controller (2026) on a Mac with the local build from this PR (
Both games worked successfully with all three connection methods. The Pokémon version is confirmed by the built app's metadata. One caveat from Pokémon: holding the middle Steam button opens the quit-game prompt as intended, but its A/B actions use the default physical Steam layout rather than the game's custom mapping. I had Steam A mapped to Switch B and Steam B mapped to Switch A in-game, while the quit prompt still used Steam A as A and Steam B as B. I am documenting this separately and preserving that behavior for now. The PR description now links the approved #764 / v1.2.1 milestone and replaces the earlier no-gameplay/USB-C-and-Puck-untested statements with these results. The branch refresh is now published as All required local hooks passed: native arm64 Mac build/tests (1,105 tests, 29 skipped), Qt (325, 36 skipped), tools (211, 22 skipped), triage (158), and the 47-patch queue. Refreshed engine host/controller/motion/haptics checks, the complete Mac host build, both iOS compile modes (265 files each), and 77 packaging/docs tests also passed. GitHub CI is running for the new head. The manual gameplay results above belong to the supplied |
The Steam Controller and global controller texts from LNPANDY#731 were English in fr, it and sv. They are translated now. The French test lists the Steam names that stay as they are.
Fix or feature
Implements #764, approved for the v1.2.1 milestone. Carries the still-open seedds/nxnative#221 through the patch queue.
What and why
Games built with NX2Apple's pinned engine cannot use the Steam Controller (2026) directly. Carry the controller implementation from upstream PR #221 at 5e2b1c7276e0745825f17d7875ec0e56b6913f7b as patch 0078 on current main's engine pin
7249ee9f95ed728aff9a86395330d10d4c91fb5c. It includes USB-C, Bluetooth and Puck report handling, buttons and sticks, rumble, six-axis motion, trackpad modes and shared button mappings. The carrier excludes that PR's unrelated lint-workflow and shader-test changes.Resolve the input-map and current-main conflicts while preserving fork window/screen/cursor behavior, split Joy-Con support, per-half haptics and iOS mapping consumers. Mark newly packed Mac apps with
DMControllerMappings=1. The launcher exposes per-game keyboard/button mappings and Steam extras in the same preferences domain as the in-game mapping panel. Global layouts seed capable games once, so later in-game changes and resets survive the next launch. Older apps keep their existing environment-based controls. Explicit launch overrides retain precedence, and disabling saved choices prevents migration and editor writes. Document the macOS Input Monitoring setup needed for direct HID access. Update the Windows packer's source-list contract to recognize and exclude the Mac-only controller source.Existing game apps must be rebuilt to receive the new engine. Direct raw Steam support is in the built game's host. Library/console navigation still uses Apple's GameController framework. Rumble follows the upstream amplitude translation and does not reproduce exact Switch HD waveforms.
Checked
Original implementation validation on an Apple Silicon Mac, macOS 26.6, Xcode 27.2 and Swift 6.4.1. Pre-push checks for the original test build passed on
9b04757b1e9df5b2c5c04fbf2d32d676d5f9c07b. The full Swift suite passed for arm64 before the final text/documentation follow-up and for x86_64 on the final head. Final German localization and capture checks also passed natively:cd frontend && swift build && swift test: passed, 1,091 tests, 28 skipped, zero failures. Tests cover migration, malformed mapping rejection, old-build compatibility, manual override precedence, disabled saving (including retained UI actions), actual gamepad navigation into/out of the editor, and visible persistence errors.python3 -m unittest discover -s frontend-qt/tests: passed, 292 tests, 26 skipped. An isolated test PATH avoids the locally installed linker interfering with discovery fixtures. The four normal-PATH failures reproduce unchanged on the base revision.python3 -m unittest discover tools/tests: passed, 207 tests, 21 skipped.python3 tools/engine.py check: the original 42-patch queue applied cleanly to its777a5f6c618a38cd2d7f795f465466fb56920534pin. Pristine sync and stale-state check passed. Refreshed-queue results are reported below.make -C dm/host/tests GAME=010015100B514000 -j3 run: passed, including input-map, Steam report/ownership/routing, motion and haptics fixtures. Fulldmhostbuild passed.GAME=010015100B514000 python3 dm/ios/tools/compile_check.py: passed, 265 files in each of default and framework modes. Mac packaging tests (66) and documentation tests (11) passed. clang-tidy is unavailable locally.Bluetooth hardware check: a bounded standalone probe compiled the exact integrated
controllers_mac.mand decoder with real IOHID APIs and logging sinks instead of a game. With macOS Input Monitoring enabled, it claimed one raw28de:1303BLE endpoint and observed buttons, both sticks, both trackpads, trackpad clicks, touch publication and changing gyro/accelerometer data. The 45-second active run recorded 82 mapped-button transitions and 359 touch events. All 35 feature/output writes succeeded, including the light 150 ms rumble pulse and both zero-output writes. The user confirmed feeling the rumble. The probe exited normally and released ownership. Initial probes could not discover the endpoint while macOS denied Input Monitoring. No product code was changed to obtain the successful result. This validates the driver path, not game-side consumption or calibrated motion accuracy.Current-main refresh
Refreshed against main
a19205e0f5338a146b8b88b728f27059404553a8in merge commit13d6eef6458e9a9ff020c26056150ff68907f946. GitHub reports no merge conflicts. The PR remains ready for review, and the local branch tracks the published fork branch with no divergence. GitHub CI is running for this head, so the results below are local checks.The Steam carrier is now patch 0078, following main's 46 unchanged patches. Its source remains upstream PR #221 at
5e2b1c7276e0745825f17d7875ec0e56b6913f7b, with the carrier attribution corrected. The integration preserves main's split Joy-Con input, motion and per-half haptics through the shared controller registry. Added regression coverage checks two split Joy-Cons alongside a raw Steam controller, mapping/focus/Home behavior, native/locality haptics transitions, and launcher propagation ofDM_JOYCON_SPLIT. The Steam report decoder, mapping and motion headers remain identical to the manually tested version.All required commit/push hooks passed on the refreshed head:
7249ee9f95ed728aff9a86395330d10d4c91fb5c. Pristine sync and stale-state checks also passed.swift build && swift test: 1,105 tests, 29 skipped, zero failures.The other review requests, including PR separation and the remaining translations, are outside this testing/conflict refresh and are not claimed as resolved.
Manual gameplay results
Tested by the author with a Steam Controller (2026) on a Mac using the branch's NX2Apple 1.1.1-local test build from
9b04757b1e9df5b2c5c04fbf2d32d676d5f9c07b:The author reported that the controller worked in gameplay over all three connection methods in both games. Pokémon's version was verified from the built game's
Info.plistin the supplied game folder. These manual results apply to the supplied test build, before the current main-branch refresh. They are separate from the standalone Bluetooth probe above and from automated checks of the refreshed branch.Known mapping caveat, observed in Pokémon: holding the middle Steam button correctly opens the quit-game prompt, but that prompt uses the default physical Steam A/B layout rather than the customized in-game mapping. With Steam A mapped to Switch B and Steam B mapped to Switch A in the game, the quit prompt still uses Steam A as A and Steam B as B. This is recorded as a remaining limitation. No behavior change is being made for it in this refresh.
Screenshots
Global Controls before and after (the change explains global layouts as defaults for new per-game mappings):
Per-game launch options before and after:
The per-game mapping editor is new. Default, customized and save-error states:
Scope
dm/,scripts/,docs/,ref/) is added at the repository root. The engine change is a patch inpatches/, itsUpstream-Statusis recorded, and the queue applies cleanly.forwarded seedds/nxnative#221.