Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 55 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ the left trigger is pulled (aiming down sights) or while a chosen button is held
also work as a switch: one press turns the gyro on, the next turns it off. The game still gets the
button. A half turn a second is the stick all the way at 100 % sensitivity; a small deadzone keeps
a still hand still, a filter smooths slow movement without delaying fast turns, and the stick
keeps working. Each friend aims with their own controller, which a gyro mapped to the mouse would
not allow.
keeps working. Each friend aims with their own controller; the separate Flick Stick option
instead uses the shared desktop mouse.

The aim can be tuned further: a sensitivity of its own while the left trigger is pulled, a slower
or faster up and down, and a button that pauses the gyro while held, to put the controller back
Expand Down Expand Up @@ -279,6 +279,59 @@ already opened it keeps it after it goes back to Xbox, so it would read it twice
without a serial number shares its settings with every controller of its model, and so does this
choice. With "Hide original controllers" off, native only removes the Xbox controller.

The Light page can explicitly opt into light-bar control while input stays native. This is off
by default; a game that also sets the light can compete with it. Custom colours accept a full
`#RRGGBB` value as well as the colour swatches.

The preview follows both touchpad contacts, including movement without clicking, with
frame-timed smoothing and a short fading trail in the focused detailed preview. Unfocused
windows and reduced-motion mode keep direct feedback instead; losing focus clears old trails.
Both contacts and trails follow the configured LED colour, with the theme accent as a fallback
when the LED colour is black or off. This only affects drawing, never controller input. On Windows,
the DualSense microphone indicator shows the actual mute state of the default recording device.
Its separate, opt-in mic-button switch toggles that device; another binding for the button takes
priority. Bluetooth may use the PC microphone, since it need not expose a DualSense recording
device. This reads the endpoint's mute flag and never records audio.

Windows uses one application-drawn title bar with the usual window controls.

### Controller Lab

The controller's Lab page has seven optional tools, saved with each profile and off by default:

- **Trigger Lab:** independent DualSense/Edge L2 and R2 resistance, vibration and trigger-break
effects. Adjust the start/end zones, force and frequency, try them for five seconds, stop
them immediately or opt into persistent profile effects. A trial restores the profile after
its timeout; disconnect and normal exit clear effects owned by OpenController. Output goes
through SDL, including USB/Bluetooth framing and checksums.
- **Touchpad mouse (Windows):** relative movement, tap-to-click, physical left/right clicks,
two-finger scrolling and a trackball mode with adjustable inertia decay. This is shared
desktop input; disable it in the Lab when a game should handle the touchpad itself.
- **Flick Stick (Windows, mapping mode):** the right stick's direction starts a turn, rotation
around its rim continues it, and the gyro can provide fine aim. Counts per full turn, flick
duration, activation threshold and gyro sensitivity are adjustable. Calibrate counts against
the game's mouse sensitivity. The virtual right stick is consumed while this is enabled.
- **Trigger mapping (mapping mode):** Hair Trigger uses short pull/release reversals; two-stage
mapping assigns buttons to soft/full pulls; Hip Fire delays the soft action so a quick full
pull can bypass it. The chosen buttons also use their existing keyboard/gamepad assignments.
- **Button debounce (mapping mode):** require a stable press/release for 0–100 ms before output.
Timers settle without another controller report. Zero preserves the original latency.
- **DSU motion:** a single loopback-only UDP server at `127.0.0.1:26760`, implementing Cemuhook
protocol 1001 for up to four enabled controllers, subscriptions and their expiry, CRC32,
touch, buttons, gyro in degrees/second and acceleration in g. Add a DSU controller in the
emulator and enable motion. Binding failures and client counts are shown in the Lab.
- **Dynamic lighting:** rainbow, breathing, rumble-linked colour and optional idle fade. These
respect the Light page's brightness and native-mode output opt-in, and stop when lights are
turned off. The drawing and touch feedback follow the output colour.

Native input stays native: only explicitly enabled desktop, sensor-sharing and hardware-effect
tools run there. Flick Stick, trigger remapping and debounce need mapping mode. Use matching
resident/UI binaries (IPC protocol 2). The input loop does not wait on UDP clients or mouse output.

Protocol references: [SDL's gamepad effect example](https://github.com/libsdl-org/SDL/blob/main/test/testcontroller.c),
[DualSense trigger encoding](https://gist.github.com/Nielk1/6d54cc2c00d2201ccb8c2720ad7538db),
and the [Cemuhook protocol](https://v1993.github.io/cemuhook-protocol/).

## Supported controllers

Controllers are read through [SDL 3](https://libsdl.org), which speaks each one's own protocol,
Expand Down
1 change: 1 addition & 0 deletions crates/open-controller-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ sdl3-sys = { version = "0.7.2", default-features = false, features = [
] }

[target.'cfg(windows)'.dependencies]
windows = { version = "0.62.2", features = ["Win32_Media_Audio", "Win32_Media_Audio_Endpoints", "Win32_System_Com", "Win32_System_Com_StructuredStorage", "Win32_System_Variant"] }
# Checks a downloaded driver installer against the hash it had when this version was made.
sha2 = "0.10"
windows-sys = { workspace = true, features = [
Expand Down
Loading
Loading