33#include < winrt/Windows.Foundation.Collections.h>
44#include < winrt/Windows.Gaming.Input.h>
55#include < array>
6+ #include < cstdint>
67#include < mutex>
78#include < optional>
89#include < string>
10+ #include < utility>
911
1012namespace
1113{
@@ -15,6 +17,12 @@ namespace
1517
1618 constexpr int kMaxSlots = 4 ;
1719
20+ /* *
21+ * @brief Converts a WinRT GamepadButtons bitmask into the internal 16-bit button mask.
22+ *
23+ * @param wgi Bitfield of WinRT GamepadButtons flags to map.
24+ * @return uint16_t Bitmask where each set bit corresponds to the internal Button enum value for a pressed button.
25+ */
1826 uint16_t MapGamepadButtons (GamepadButtons wgi)
1927 {
2028 uint16_t b = 0 ;
@@ -35,6 +43,63 @@ namespace
3543 if ((wgi & GamepadButtons::RightThumbstick) != GamepadButtons::None) b |= std::to_underlying (RightThumb);
3644 return b;
3745 }
46+
47+ /* *
48+ * @brief Obtains a human-friendly display name for a gamepad, falling back to a numbered default.
49+ *
50+ * Attempts to retrieve the controller's DisplayName from its RawGameController representation.
51+ * If a display name is unavailable or retrieval fails, returns "Gamepad N" where N is
52+ * slotIndex + 1.
53+ *
54+ * @param pad WinRT Gamepad instance to query.
55+ * @param slotIndex Zero-based slot index used to generate the fallback name.
56+ * @return std::string The resolved display name or a fallback like "Gamepad 1".
57+ */
58+ std::string GetDisplayNameForGamepad (Gamepad const & pad, int slotIndex)
59+ {
60+ try
61+ {
62+ auto raw = RawGameController::FromGameController (pad);
63+ if (raw)
64+ {
65+ auto name = raw.DisplayName ();
66+ if (!name.empty ())
67+ return winrt::to_string (name);
68+ }
69+ }
70+ catch (...) {}
71+ return " Gamepad " + std::to_string (slotIndex + 1 );
72+ }
73+
74+ /* *
75+ * @brief Retrieve the hardware vendor and product IDs for a gamepad.
76+ *
77+ * Attempts to obtain the controller's hardware vendor and product identifiers
78+ * and writes them to the provided pointers. If retrieval fails or the
79+ * identifiers are unavailable, writes 0 to the corresponding outputs.
80+ * If both output pointers are null, the function does nothing.
81+ *
82+ * @param pad The gamepad to query.
83+ * @param vendorId Pointer to receive the vendor ID, or nullptr to skip.
84+ * @param productId Pointer to receive the product ID, or nullptr to skip.
85+ */
86+ void GetDeviceIdsForGamepad (Gamepad const & pad, uint16_t * vendorId, uint16_t * productId)
87+ {
88+ if (!vendorId && !productId) return ;
89+ try
90+ {
91+ auto raw = RawGameController::FromGameController (pad);
92+ if (raw)
93+ {
94+ if (vendorId) *vendorId = raw.HardwareVendorId ();
95+ if (productId) *productId = raw.HardwareProductId ();
96+ return ;
97+ }
98+ }
99+ catch (...) {}
100+ if (vendorId) *vendorId = 0 ;
101+ if (productId) *productId = 0 ;
102+ }
38103}
39104
40105struct WgiBackend ::Impl
@@ -43,10 +108,20 @@ struct WgiBackend::Impl
43108 std::array<std::optional<Gamepad>, kMaxSlots > slotGamepads{};
44109 GamepadState states[kMaxSlots ]{};
45110 std::array<std::string, kMaxSlots > slotDisplayNames;
111+ std::array<uint16_t , kMaxSlots > slotVendorIds{};
112+ std::array<uint16_t , kMaxSlots > slotProductIds{};
46113
47114 winrt::event_token addedToken;
48115 winrt::event_token removedToken;
49116
117+ /* *
118+ * @brief Assigns a newly connected gamepad to the first available backend slot and records its metadata.
119+ *
120+ * If a free slot exists, stores the provided gamepad handle in that slot and populates the slot's
121+ * display name and vendor/product IDs. If all slots are occupied, the added gamepad is ignored.
122+ *
123+ * @param pad The gamepad that was added.
124+ */
50125 void OnGamepadAdded (IInspectable const &, Gamepad const & pad)
51126 {
52127 std::lock_guard lock (mutex);
@@ -55,12 +130,21 @@ struct WgiBackend::Impl
55130 if (!slotGamepads[i])
56131 {
57132 slotGamepads[i] = pad;
58- slotDisplayNames[i] = " Gamepad " + std::to_string (i);
133+ slotDisplayNames[i] = GetDisplayNameForGamepad (pad, i);
134+ GetDeviceIdsForGamepad (pad, &slotVendorIds[i], &slotProductIds[i]);
59135 break ;
60136 }
61137 }
62138 }
63139
140+ /* *
141+ * @brief Handles a gamepad removal event by clearing the corresponding slot.
142+ *
143+ * Searches the tracked slots for the given gamepad handle; when a match is found,
144+ * removes the stored handle and resets the slot's vendor and product IDs to zero.
145+ *
146+ * @param pad The gamepad that was removed.
147+ */
64148 void OnGamepadRemoved (IInspectable const &, Gamepad const & pad)
65149 {
66150 std::lock_guard lock (mutex);
@@ -69,6 +153,8 @@ struct WgiBackend::Impl
69153 if (slotGamepads[i] && slotGamepads[i] == pad)
70154 {
71155 slotGamepads[i].reset ();
156+ slotVendorIds[i] = 0 ;
157+ slotProductIds[i] = 0 ;
72158 break ;
73159 }
74160 }
@@ -86,6 +172,13 @@ WgiBackend::~WgiBackend()
86172 }
87173}
88174
175+ /* *
176+ * @brief Initialize the WinRT gamepad backend, enumerate currently connected gamepads, and subscribe to add/remove events.
177+ *
178+ * Initializes the WinRT apartment once per thread, acquires the internal mutex, populates up to kMaxSlots with already-connected gamepads (storing each slot's handle, display name, and vendor/product IDs), and registers handlers for future GamepadAdded and GamepadRemoved events, saving their subscription tokens.
179+ *
180+ * @param hwnd Window handle provided for initialization context; currently accepted for API compatibility and not otherwise used.
181+ */
89182void WgiBackend::Init (HWND )
90183{
91184 thread_local static bool winrtInitialized = false ;
@@ -102,8 +195,10 @@ void WgiBackend::Init(HWND)
102195 {
103196 try
104197 {
105- impl_->slotGamepads [i] = gamepads.GetAt (static_cast <uint32_t >(i));
106- impl_->slotDisplayNames [i] = " Gamepad " + std::to_string (i);
198+ auto pad = gamepads.GetAt (static_cast <uint32_t >(i));
199+ impl_->slotGamepads [i] = pad;
200+ impl_->slotDisplayNames [i] = GetDisplayNameForGamepad (pad, i);
201+ GetDeviceIdsForGamepad (pad, &impl_->slotVendorIds [i], &impl_->slotProductIds [i]);
107202 }
108203 catch (winrt::hresult_out_of_bounds const &)
109204 {
@@ -156,12 +251,42 @@ const GamepadState& WgiBackend::GetState(int slot) const
156251
157252const char * WgiBackend::GetName () const { return Name; }
158253
254+ /* *
255+ * @brief Retrieves the human-readable display name for a gamepad slot.
256+ *
257+ * @param slot Zero-based slot index.
258+ * @return const char* Pointer to a null-terminated C string containing the display name for the given slot, or `nullptr` if the slot index is out of range or no gamepad is present in that slot. The returned pointer is valid until the next call to GetSlotDisplayName on the same thread.
259+ */
159260const char * WgiBackend::GetSlotDisplayName (int slot) const
160261{
161262 if (slot < 0 || slot >= kMaxSlots ) return nullptr ;
162263 std::lock_guard lock (impl_->mutex );
163264 if (!impl_->slotGamepads [slot]) return nullptr ;
164- thread_local static std::string result;
165- result = impl_->slotDisplayNames [slot];
166- return result.c_str ();
265+ thread_local static std::array<std::string, kMaxSlots > slotResultBuffers;
266+ slotResultBuffers[slot] = impl_->slotDisplayNames [slot];
267+ return slotResultBuffers[slot].c_str ();
268+ }
269+
270+ /* *
271+ * @brief Retrieve the vendor and product identifiers for a slot's gamepad.
272+ *
273+ * Writes the 16-bit vendor and product IDs for the gamepad in the given slot into the provided output pointers. If the slot index is out of range or no gamepad is present in that slot, `0` is written for each ID. Passing a `nullptr` for either output pointer suppresses writing that value.
274+ *
275+ * @param slot Slot index (0 .. kMaxSlots - 1) to query.
276+ * @param vendorId Pointer that receives the vendor ID, or `nullptr` to ignore.
277+ * @param productId Pointer that receives the product ID, or `nullptr` to ignore.
278+ */
279+ void WgiBackend::GetSlotDeviceIds (int slot, uint16_t * vendorId, uint16_t * productId) const
280+ {
281+ if (slot < 0 || slot >= kMaxSlots )
282+ {
283+ if (vendorId) *vendorId = 0 ;
284+ if (productId) *productId = 0 ;
285+ return ;
286+ }
287+ std::lock_guard lock (impl_->mutex );
288+ uint16_t vid = impl_->slotGamepads [slot] ? impl_->slotVendorIds [slot] : 0 ;
289+ uint16_t pid = impl_->slotGamepads [slot] ? impl_->slotProductIds [slot] : 0 ;
290+ if (vendorId) *vendorId = vid;
291+ if (productId) *productId = pid;
167292}
0 commit comments