Skip to content

Implement controller monitoring on Windows and fix Windows CI - #519

Open
adangert wants to merge 3 commits into
thp:masterfrom
adangert:windows-monitor
Open

Implement controller monitoring on Windows and fix Windows CI#519
adangert wants to merge 3 commits into
thp:masterfrom
adangert:windows-monitor

Conversation

@adangert

@adangert adangert commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Dynamic controller monitoring was added for Linux and macOS in previous commits. Windows did not have a moved_monitor implementation, so this functionality was disabled. This PR adds a Windows monitor using device notifications and periodic HID rescans, and fixes some issues with the Windows CI builds.

Implementation

  • Adds moved_monitor_windows.cpp
  • Registers for Windows device-interface notifications through CfgMgr32
  • Uses notifications to request an immediate HID rescan
  • Performs periodic rescans as a fallback when notifications are unavailable or missed
  • Detects both ZCM1 and ZCM2 controllers
  • Handles the multiple HID collections exposed for each controller on Windows
  • Uses case-insensitive device-path comparisons on Windows
  • Enables monitor construction, polling, and cleanup in PSMoveAPI on Windows
  • Pins Windows CI to windows-2022, the build currently targets Visual Studio 2022, and breaks with windows-latest which pulls in Visual Studio 2026
  • Restores the PS3Eye MSVC build by including winsock2.h; newer libusb headers no longer supply the timeval definition that PS3EYEDriver relied on

The monitor reports controller additions and removals through the same moved_monitor interface already used on Linux and macOS.

Testing

  • Built successfully with Visual Studio 2022 x64 in Debug and Release
  • Tested USB controller connection and pairing while the API was running
  • Tested Bluetooth connection, removal, and reconnection without restarting the application
  • Tested with multiple ZCM1 and ZCM2 controllers
  • Tested through JoustMania's new Python ctypes integration on Windows 11

@adangert adangert changed the title Add Windows controller hotplug monitoring Implement controller monitoring on Windows Jul 23, 2026
@adangert adangert changed the title Implement controller monitoring on Windows Implement controller monitoring on Windows & Fix Windows CI Jul 23, 2026
@adangert adangert changed the title Implement controller monitoring on Windows & Fix Windows CI Implement controller monitoring on Windows and fix Windows CI Jul 23, 2026
@adangert
adangert marked this pull request as ready for review July 23, 2026 05:53
Comment on lines +106 to +111
const bool sony = normalized.find(L"vid_054c") != std::wstring::npos ||
normalized.find(L"vid&0002054c") != std::wstring::npos;
const bool move = normalized.find(L"pid_03d5") != std::wstring::npos ||
normalized.find(L"pid&03d5") != std::wstring::npos ||
normalized.find(L"pid_0c5e") != std::wstring::npos ||
normalized.find(L"pid&0c5e") != std::wstring::npos;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to have: Use PSMOVE_VID / PSMOVE_PID / PSMOVE_PS4_PID here to build wide strings instead of hardcoding those values, e.g. by formatting into a wide string "vid_%04x", etc..

@adangert adangert Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! will try to get to these all (and retest) soon!

continue;
}

candidates.push_back({

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be emplace_back to potentially avoid extra copies.

if ((action == CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL ||
action == CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL) &&
is_move_interface_path(event_data->u.DeviceInterface.SymbolicLink)) {
monitor->rescan_requested.store(true);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style nitpick: assignment (monitor->rescan_requested = true;) probably also works, but it's maybe more obvious to use .store(true) here that we're dealing with an atomic.

monitor->notification = nullptr;
monitor->unregister_notification = nullptr;

if (monitor->cfgmgr32 != nullptr) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If monitor->cfgmgr32 == null, should we delete the monitor object again and return nullptr, to make it obvious to the caller that monitoring is not supported? Or if nothing else, log a warning?

Comment on lines +232 to +233
if (register_notification != nullptr &&
monitor->unregister_notification != nullptr) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above if any of the two function pointers happen to be nullptr, should we then free the object (and at this stage, also the library handle) and return nullptr to the caller?

psmove_return_if_fail(monitor != nullptr);

if (monitor->notification != nullptr &&
monitor->unregister_notification != nullptr) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming the above "return nullptr if the unregister_notification function pointer is nullptr" suggestion is implemented, we can further assume here that monitor->unregister_notification != null (and the same below, we can assume that monitor->cfgmgr32 != nullptr).

@@ -0,0 +1,317 @@
/**
* PS Move API - An interface for the PS Move Motion Controller
* Copyright (c) 2012 Thomas Perl <m@thp.io>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to add 2026 + Aaron Angert here, as you are the author of this file.

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.

2 participants