Conversation
…dpoint # Conflicts: # src/main.cpp # src/steam/SteamController.cpp
The shared-access fallback fixed a real problem — something in Windows holds a compatible write handle on the live puck collection even with Steam closed — but applied unconditionally it also swallowed the signal the Steam handoff depends on. A failed exclusive claim is precisely how the app detects that Steam is holding the controller: game mode failing to activate is what escalates TryAcquireController to the device cycle that takes the handle back. With the fallback always succeeding, the app would instead report success and drive the controller alongside Steam, both sending competing lizard-mode feature reports, and would never cycle. None of the PR's validation would catch it, since every check was run with Steam closed. Gate it on whether steam.exe is running. TrayApp already watches that for the auto modes, so it now pushes presence into ControllerManager from the WM_STEAMSTATE handler — outside ApplySteamState, which returns early in Manual mode, because this decision matters in every mode. Also cheapen the state-report probe. It costs the full 250ms timeout on an empty puck slot and runs per slot, while the acquire path retries in a burst of eleven — three empty slots would have blocked the UI thread for roughly eight seconds. Slots found silent are skipped for 1.5s, so the burst probes each at most once. And WaitForStateReport now stops when a read returns instantly rather than timing out, instead of spinning on a dead handle for the rest of the window. The skip message no longer says "puck", since the gate applies to every transport. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
I asked it to move the changes to this branch to save me some time and it decided to put a summary at the top instead of me :) Also it took my feedback on the uninstall stuff way more seriously than I meant. Less worried about it if we need to go that path. Claude makes it seem like it's the end of the world. |
|
Ok, finally got the puck and other high priority parts merged in and tested. As I mentioned in the other PR, It'd be great if you could uninstall/reinstall with 1.9 and from there, see if you're still seeing the issue with release and requisition not happening. If you are, let's keep digging in here and figure out the right path forward. Like you, I don't have a ton of folks with Steam Controllers but I've tested on 3 different machines and had one user that's reached out take a peek at the update and it's working for them as well. Hopefully you're just encountering the same issue that I was when I was building out BT support and this resolves it! Also as a heads up, it can take up to 10s to get the controller back when you close a steam game, there's logic to poll the open slots on the puck and it can take a bit. |
|
codex Retested exactly as requested against a genuinely clean v1.9 install, and the release/requisition issue still reproduces on this machine. Clean baseline
Results
The important part is that this is no longer the stale/no-op helper problem. Immediately after each One timing detail may matter: cycling all four puck interfaces takes about 10 seconds here. The app's 4-second retry fires “attempt 2” while the first scheduled task is still running, so the log counts three attempts but only two helper invocations actually run. Even after the completed So my result is: release works; requisition while Steam remains running still fails on this puck/machine with clean 1.9. Steamless only recovers once |
|
codex Correction/extra control run: I found that an earlier I killed every probe, stopped both Steam and Steamless, and reran the checks:
So the stale probe affected timing but not the result. The corrected finding is: the device cycle is real and succeeds, but the live endpoint is already opened again before Steamless can claim it. Separately, exclusive access is unavailable on this machine even with both applications stopped, so I am now tracing which Windows/system service owns the remaining shared HID handles. |
|
I'm on Windows 11 25H2 build 26200, Microsoft.GameInput 3.3.221.0. |
|
Looks like GameInput 3.4.218 is available. Upgrading to that, seeing if that fixes the issue. |
|
Yeah I'm hitting a dead end on other options / root causes - what Windows versions are you on? |
|
I'm on 25H2, build 26200, so it should be the same as yours :/ |
|
One random thing for you to try while I try this out on other machines. Can you try plugging the controller in via USB and see if SteamlessController reclaims the device from Steam after you exit Megabonk? I wonder if part of this is timing. When I reboot the device internally, there is a bit of a race to claim it, but (at least unless something changed) I used to grab immediately after release so we'd always beat Steam to getting it back. I wonder if with the changes for enumerating the puck we're somehow slower now and can't snag it before Steam. USB should be super fast though. |
|
Ok, while I was digging in here, I genuinely found an issue with the release/reclaim logic for controllers. I've got two paths internally, a result of the fact that I added the AUTO modes later. I pushed some updates to main that unifies the logic. Would you be able to reboot your machine, and try the new version v1.10 (or MAIN)? I also found an issue where ViGEm was leaking devices and it could clog up the device list (why I earlier asked about that screen in Steam). If this is still happening with these changes, can you attach your logs and I'll take a peek? I added some more logging to the builds. |
|
One more datapoint! I went ahead and added some more logging. When SteamlessController isn't able to take the handle away from whatever app has it, I go ahead and walk the tree of active apps and see who has a handle to the controller and print that in the logs (I also print that in the popup that shows up when we're unsuccessful. Let me know what it says when it's unable to take the controller away. From all my testing, Steam never takes exclusive ownership of the controller, so I wonder if something else is running and is much more aggressive. |
|
Hey @danbosscher I just put up version 1.18 tonight, and I think it might fix the issue here. I went through a lot of iterations, and the issue below was that I wasn't grabbing the controller as aggressively as I could have. For some more details for the more technically minded. I was waiting on the signal for the device showing up to snag it (Steam likely is doing the same thing). I instead start grabbing from the moment I cycle the device - which makes it much faster generally). Also, I cycled all the devices which includes all 4 heads on the puck. This can end up causing a lot of delay, when I really should have just been cycling only the controllers that are attached, and trying to grab those slots again. No reason to assume someone's going to plug in another controller right when I'm cycling. With all that, I've dropped slot acquisition down from 100s of ms or even over a second in some cases down to single digit, low double digits. And on 3 attempts, we saw the app winning all the time even on another device that's hitting the same issues you were. Give it a shot and see if it's working for you! |

Draft — not for merge. Splitting @danbosscher's Steam coexistence work out
of #44 so the puck activation fix can land on its own, and so this can be
discussed at its own pace. All eight commits are preserved verbatim with
original authorship.
The problem being solved
Since the firmware update, "could not get an exclusive handle" is no longer
reliable proof that Steam owns the controller — something in Windows keeps a
compatible write handle open on the live collection even with Steam closed.
Sharing the handle with a running Steam produced duplicate input or a virtual
pad that received nothing, depending on startup order.
What's in here
Steam, reserve the controller via Steam's
controller_blacklist, or launchSteam with
-nojoycurrent-session proof Steam cannot see it, yielding immediately if that
proof disappears
config.vdfread/modify/write with a backup and atomic replace, plusuninstall-time restoration
0x1305support, sleep handling, extradiagnostics
Open questions before any of this lands
1. Is exclusivity actually unobtainable? This design rests on that premise,
but two bugs were making the device cycle a silent no-op: the scheduled task
pointed at a stale helper that didn't know the current PIDs, and
DIF_PROPERTYCHANGEreports success while not restarting a devnode that has anopen handle. Both are fixed on
main— cycling is disable/enable now, andcycle.logdistinguishesCYCLEDfromFELL BACK TO RESTART. Testing wasdone from a build folder, which almost certainly had no registered cycle task
at all. Worth re-measuring before designing around the premise.
2. It removes the handoff mode that we want to make default in the future.
SetSteamMayOwnControlleris true whenever Steam runs, idle or in-game, and a shared claim is then
refused — so "Steamless while Steam idles, hand off during games" stops working
unless the user blacklists the controller.
3. Writing
config.vdfis persistent state outside our app. Theimplementation is careful, but if the app dies between apply and restore, the
user's Steam silently can't see their controller with nothing pointing back at
us. Documenting Steam's own blacklist UI may be the better trade.
4. Uninstall must not be blocked.
Aborton failed restoration means a userwhose Steam won't shut down cleanly can't remove the app — worse than leaving
the setting behind.
5. What's actually causing the doubled D-pad? Three candidates with different
fixes: Steam reading the same physical device (inherent to sharing, since HID
delivers reports to every open handle); Steam re-processing our ViGEm pad via
Xbox Configuration Support; or lizard mode oscillating because both processes
hold write access and our keepalive fights Steam's mappings every 2s.
Separating them: press D-pad in Notepad, count devices in
joy.cpl, and toggleoff Xbox Configuration Support.
Rebasing
This currently overlaps with #44 because both share the puck fix. Once #44
merges, replay just the coexistence commits onto main:
🤖 Generated with Claude Code