We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Wiki: scrub pseudo-colon em-dashes across user + dev pages
Touchpad: drop the residual $P-recognizer jargon in Tier 3 A prior cleanup pulled $P out of the article but missed the In-Box Gestures Tier 3 line. Replaced the algorithm name with the property users actually care about — scale, position, and rotation invariance — so 'small in the corner matches large in the centre' is plain. Threshold semantics unchanged.
Home.md 3.3 section header: cover all four highlights, not just touchpad After adding the Motion Passthrough and per-device Copy/Paste paragraphs to the section last iteration, the heading still claimed only touchpad work. Updated to reflect every 3.3 highlight the body now lists.
Wiki: tidy two small dev-doc seams Architecture-Overview Mermaid graph: DSU_CLIENT and BROWSER node labels were using a bare '.' as the separator between names (Cemu . Dolphin, Phone . tablet). Changed to ' / ' to match the rest of the graph and extended the DSU client list to the real four (Cemu / Dolphin / Yuzu / Ryujinx) the wiki documents elsewhere. ViewModels.md Per-Device Test Routing row: dropped the reference to the private Claude memory file (feedback_per_device_test_isolation.md); restated the rule in-doc and noted the same gate covers the dispatcher / routing branches that emit physical effects from those tabs.
Controller-Slots: complete the 'What a slot owns' list for v3.2 + v3.3 Old list was five bullets and stopped at Force Feedback / Macros. Slots have grown a lot since: Impulse Triggers (v3.2), Adaptive Triggers, Lighting, Gyro (with Motion Passthrough in v3.3), Touchpad outputs + gesture engine (v3.3), Shift Layers, and the multi-source mapping engine on the Button-and-Axis-Mappings link. Note that Pad-page Copy / Paste carries every per-device tuning on the list, with a link to the matching rules in the mappings page.
Gyro.md: drop version qualifier from Engage-gate sentence The Easy Aim + Aim Engage gates shipped in 3.2 and have not changed since. Anchoring the sentence to a release number reads as if the feature only exists in that release. The historical fact is fine in release notes; in evergreen wiki prose it becomes stale wording.
Wiki landing + Gyro: include Motion Passthrough + per-device Copy/Paste in 3.3 highlights Home.md What's new section gains two paragraphs in the 3.3 block: - Motion Passthrough card story (slot-wide toggle, off by default, both the virtual controller motion report and the DSU motion server gate on it, calibration drift correction is unconditional). - Per-device Copy/Paste/Copy From story (snapshots every assigned device's tuning, InstanceGuid then ProductGuid match, unmatched target devices left alone). Gyro.md DSU client list: Citra replaced with Dolphin / Yuzu / Ryujinx. Citra was discontinued March 2024; the active DSU clients on the wiki's own Motion-Server page are Cemu, Dolphin, Yuzu, Ryujinx (and Lime3DS, the Citra fork).
Wiki: CopyFromDialog notes now reflect per-device tuning carry-along Architecture-Overview source-tree comment: the dialog copies mappings plus per-device tuning, not just the table. XAML-Views Copy From Dialog (slot-level section) and CopyFromDialog (dialog-detail section) both gain the per-device payload story: each source device is matched to a target device by InstanceGuid then ProductGuid, mapping table replaced wholesale, and per-device tuning (deadzones, sensitivity, FFB, impulse triggers, adaptive triggers, lighting, gyro, TouchpadSettings) is carried along through the InputService BuildPerDeviceSettingsSnapshot / ApplyPerDeviceSettingsToSlot pair documented one page over.
Wiki dev pages: cover v3.3 per-device Copy/Paste payload + TouchpadSettings round-trip Engine-Library.md: PadSetting utility-methods table refreshed. - ToJson now lists every key it actually emits: layout metadata, the four mapping dicts, the typed __TouchpadSettings sub-tree, and the clipboard-only per-slot payloads. - FromJson notes that the typed TouchpadSettings and the payloads are reattached on deserialization. - CopyFrom mentions the TouchpadSettings deep-copy alongside mapping arrays. Services-Layer.md: Copy/Paste section gained BuildPerDeviceSettingsSnapshot and ApplyPerDeviceSettingsToSlot subsections. These are the v3.3 hooks that snapshot every assigned device's PadSetting on Copy and reapply on Paste with InstanceGuid -> ProductGuid matching. The old single- device helpers stay as-is for the row-by-row Apply path.
Settings-and-Serialization: CopyablePropertyNames table refreshed for v3.2 + v3.3 Old table listed 79 properties. Actual array is 129. Added the missing categories: ButtonShare, Impulse triggers (4), Constant trigger force (3), Audio bass trigger rumble (5), Constant force (3), Gyro tuning (25), Touchpad descriptors (7), Motion passthrough markers (2). Excluded section now lists TouchpadSettings (the v3.3 typed sub-tree) and the clipboard-only XmlIgnore payloads (SlotMultiSourceRows, DeviceScopedMultiSourceRows, SlotPerDeviceSettingsJson, SlotPlayStationConfigsJson, SlotExtendedConfigJson, SlotMidiConfigJson). Usage section reflects the __TouchpadSettings + __SlotPerDeviceSettings JSON keys that ToJson now emits.
Doc loop: cover Motion Passthrough card + per-device Copy/Paste Gyro.md: new section between intro and Calibration explaining the slot-wide Motion Passthrough toggle. Off by default, gates whether the Gyro tab's discretionary tuning reaches the virtual controller's motion report and the DSU motion server. Calibration drift correction always applies. Screenshot caption updated to mention the new card. Button-and-Axis-Mappings.md: Copy/Paste/Copy From entries now describe that the clipboard payload covers every assigned device's tuning, not just the one selected at copy time. Matching is InstanceGuid first, then ProductGuid as a fallback for the same model on a different physical unit. Unmatched target devices are left alone.
pad-gyro.png: recapture for default-off passthrough + card spacing fix Refreshed from a 5-slot capture session (PS / Xbox / Extended / MIDI / KBM) with DualSense + Xbox Series both assigned to slot 0. Shows the Motion Passthrough checkbox unchecked by default and the new bottom margin between Motion Passthrough and Calibration cards.
Services-Layer: document v3.3 Touchpad custom-gesture Provider/Applier hooks SettingsService grew TouchpadGesturesProvider + TouchpadGesturesApplier callbacks in v3.3 (SettingsService.cs:48-82) to bridge to InputService without a reverse reference. The startup-order pending-flush detail matters because LoadFromFile runs before StartEngine wires the applier, so loaded gestures get stashed in _pendingTouchpadGesturesToApply and the property setter auto-flushes on first assignment. Added a short section between LoadMacros and Save to capture this. The SettingsService section had no Touchpad coverage at all before this.
ViewModels: note PadViewModel.Touchpad.cs partial-class file PadViewModel is split across PadViewModel.cs (main) and PadViewModel.Touchpad.cs (v3.3 Touchpad-tab properties: TouchpadGesturesEnabled, TouchpadJoystickMaxRadius, TouchpadMouseSensitivityX/Y, etc.). The wiki page header listed only PadViewModel.cs. Updated to mention both files + the responsibilities list now includes Touchpad-tab settings.
Wiki: bump HIDMaestro version refs from 1.3.12 to 1.3.13 The shipping HIDMaestro.Core.dll at Resources\HIDMaestro\ has FileVersion 1.3.13.0 (confirmed via Get-Item VersionInfo). Two wiki pages still cited the prior 1.3.12 tag: - HIDMaestro-Deep-Dive.md:18 'PadForge ships with HM 1.3.12...' - Driver-Installation-Internals.md:86 'PadForge currently ships HIDMaestro 1.3.12.' Both updated to 1.3.13.
Touchpad: fix custom-gesture descriptor format (Custom_<name>, not CustomGesture <name>) The page said custom-gesture descriptors take the form 'Touchpad N CustomGesture <name>'. Source-verified: InputService.cs emits them as `Descriptor = $"Touchpad {padIdx} Custom_{cg.Name}"` (underscore between Custom and the gesture's user-chosen name, no 'Gesture' suffix). Updated to match.
Force-Feedback: drop the dev-detail BaseHighBrush note from Test Rumble The Test Rumble user-facing description ended with 'The label uses BaseHighBrush so it stays readable in both light and dark themes.' Two reasons to drop it: (1) the brush name is stale — PadPage.xaml uses TextFillColorPrimaryBrush across the board now, not BaseHighBrush. (2) it's a WPF-internal detail that doesn't help anyone tuning rumble. Removed the sentence entirely; theme-readability is implicit in the WPF-UI Fluent style used everywhere else.
Settings: polling-interval control is a NumberBox, not a slider Page said 'Slider range: 1-16 ms' but SettingsPage.xaml:70 uses a ui:NumberBox (spin-button input), not a Slider. Reworded to describe the actual control type. Range value 1-16 was correct (matches the NumberBox's Minimum / Maximum attributes).
Web-Controller: correct the WebSocket message wire format Page showed TouchpadClick as `{type: "button", code: 16}`. Wrong shape — WebControllerServer.cs:402-411 dispatches on a wrapping `type: "input"` top-level field with a nested `kind: "button"` discriminator. The actual message the JS client sends is: {type: "input", kind: "button", code: 16, value: 1} // press {type: "input", kind: "button", code: 16, value: 0} // release The source-side comment at WebControllerServer.cs:420-421 explicitly spells this out: "controller_client.js emits a standard {type:\"input\", kind:\"button\", code:16, ...} message". Wiki was a generation behind that refactor (touchpad-click was a special-case payload before). Same correction applies to all button / axis / POV events — every input event carries that wrapping shape.
Settings-and-Serialization: add 2 missing ComputeChecksum sections Walked PadSetting.ComputeChecksum (lines 984-1244) and found two sections appended to the checksum that weren't documented: - Motion passthrough markers (2): MotionGyro, MotionAccel, between Threshold (1) and the dictionary sections. - Touchpad per-(device, pad) settings (~35 fields per entry), tagged v3.3, prefixed `TPS:`, sorted by (DeviceGuid, TouchpadIndex), serializes the full Touchpad-tab knob set (gesture toggles + thresholds + Stick / D-Pad output + Mouse output). The PadSetting.cs comment above the block explicitly calls out why it has to be in the checksum: two devices with identical mappings but different touchpad-tab settings would collide on dedup-by-checksum, silently dropping one device's per-pad toggles. Renumbered sections 16-19 to 17-20 to make room. Added the Touchpad block as section 21.
Settings-and-Serialization: fix Gyro tuning field count + names Section 13 listed "~24, v3.2" and omitted three Gyro fields that ARE in the checksum at PadSetting.cs:1127-1130: - GyroAimEngageMode (drives Hold vs Toggle for Aim Engage) - GyroApplyTuningToPassthrough (controls whether per-pad tuning affects passthrough output) - GyroInvertYawRoll (renamed property; wiki had the old XML name GyroInvertYaw) Bumped count to 25 (matches the actual checksum field list), added the two missing, corrected the Invert property name, noted the XmlElement back-compat alias. Tagged the section v3.2 + v3.3 since AimEngageMode and ApplyTuningToPassthrough landed later.
Architecture-Overview: drop two dead file refs from repo tree InputEventArgs.cs and InputException.cs no longer exist anywhere in the repo (refactored out). InputExceptionEventArgs.cs is the surviving one. Updated its description to mention what it actually wraps (an exception raised on the polling thread).
Driver-Management troubleshooting: drop the 'copy the whole release zip' advice The line told users 'If you only copied the EXE, copy the whole release zip' — wrong since the OpenXInput shim is bundled in the single-file EXE and extracts to %TEMP% on launch. PadForge ships as one EXE. Replaced with the actual troubleshooting paths: AV / cleaner wiping %TEMP%\.net\PadForge mid-session, and Steam's launch-time controller enumeration cache.
Build-and-Publish: align Native-DLLs section with single-file reality Same fix as commit 82e945f (Architecture-Overview). The section header said native DLLs are 'Not embedded inside the single-file exe.' That contradicts the project's actual publish flags: PublishSingleFile=true SelfContained=true IncludeNativeLibrariesForSelfExtract=true The combo means Content items are folded into the single-file bundle and extracted to %TEMP% on launch. PadForge ships as one EXE — the deploy recipe and every release confirm this. Reworded to explain both modes (non-single-file leaves them adjacent; single-file bundles + extracts).
Architecture-Overview: SDL3.dll + libusb ARE bundled in single-file EXE The page said "They are not embedded in the single-file exe. They must sit adjacent to PadForge.exe." Both wrong: - PadForge.App.csproj sets PublishSingleFile=true, SelfContained=true, AND IncludeNativeLibrariesForSelfExtract=true. The IncludeNativeLibrariesForSelfExtract flag (.NET 5+) folds <Content> native DLLs into the single-file bundle. They get extracted to %TEMP%\.net\PadForge\<hash>\ on first launch. - PadForge.exe is shipped standalone (the deploy recipe in feedback_deploy_recipe.md copies ONLY PadForge.exe to the install dir; HM Maestro stays installed and SDL3/libusb extract from the bundle on next launch). Re-stated the mechanism: <Content> declarations are what cause the build to pick them up; IncludeNativeLibrariesForSelfExtract is what bundles them; ship-time copy is just PadForge.exe.
Virtual-Controllers: align See-also blurb with no-HIDMaestro-uninstall Same fix as commit fd7e151 — link text claimed 'HIDMaestro... driver install / uninstall' which is misleading since HIDMaestro has no uninstall path in PadForge or in HM's design itself.
HIDMaestro-Deep-Dive: align See-also Driver Management blurb Tail-end follow-up to commit 6013b44. The cross-reference said 'install / uninstall / status flow for HIDMaestro, HidHide, MIDI Services' which implied HIDMaestro has an uninstall path. It does not (Driver-Management.md HIDMaestro Uninstall subsection at line 88-90: 'There is no in-app Uninstall button for HIDMaestro'). Reworded to keep the link useful without re-asserting the wrong verb on the wrong driver.
Wiki: align top-line driver claims with the no-uninstall-HIDMaestro rule Three places implied HIDMaestro could be uninstalled like the other two drivers; each contradicted Driver-Installation-Internals.md:178 (and the project memory rule "HIDMaestro is not designed to be removed like an ordinary user-mode driver"). - Driver-Management.md tagline said "Install and uninstall every one from Settings." Now: "HIDMaestro auto-installs on first launch and has no uninstall path. Install or uninstall the optional two from Settings." - Driver-Management.md admin-rights section said "Driver install, driver uninstall, and HidHide whitelist edits all need administrator." The HIDMaestro "Uninstall" subsection further down already said there's no uninstall path, so the top-of-section line was the wrong one. Replaced with the three explicit ops (HM registration first- launch, HidHide install/uninstall, MIDI Services install/uninstall). - Installation.md UAC-section had the same "HIDMaestro driver install/uninstall" phrasing. Same fix applied. No code or rule change; just bringing the user-facing pages into line with what the in-app driver-cards page actually does (Settings shows HIDMaestro as status-only — no Install or Uninstall button).
Web-Controller: clarify the button code 16 is PadForge-internal The page said TouchpadClick code 16 is "the canonical SDL touchpad-button code, matching SDL_GAMEPAD_BUTTON_TOUCHPAD." That conflates two things: PadForge's internal state.Buttons[] layout (where TouchpadClick is index 16) and SDL3's native SDL_GamepadButton enum (where SDL_GAMEPAD_BUTTON_TOUCHPAD = 20, not 16). CustomInputState in the engine reorders SDL3's enum so the four DPad slots are synthesized into POV[0] instead of taking buttons 11-14. That collapses 21 down to 16 for the touchpad button. Rephrased so the two concepts are clearly distinct.
Impulse-Triggers: correct the DualSense visibility claim The tab is gated on the active source device's HasRumbleTriggers flag. Source-trace through SdlDeviceWrapper.cs:260 and XboxControllerIdentity.cs:22-35: HasRumbleTriggers is true only for Microsoft VID 0x045E PIDs in the impulse-trigger set (Xbox One / Elite Series 1 / Elite Series 2 / Series X|S in various wired / wireless / Bluetooth variants). DualSense VID 0x054C is not in the set, and SDL3's trigger-rumble property is false for it. Earlier wording "DualSense pads show the tab" was wrong. DualSense trigger-motor passthrough still works — it routes through the AT tab's Vibration mode, not Impulse Triggers. Added a paragraph explaining that, and replaced the broad "trigger motors" tagline with the exact PID-family list so future visibility questions resolve from the doc. Verified this turn during screenshot capture: had to switch the source picker from DualSense to Xbox Series X to make the tab render.