Skip to content
Merged
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
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,21 @@ jobs:
- name: Check formatting
if: runner.os == 'Linux'
run: cargo fmt --check
- name: Test
run: cargo test --locked
- name: Clippy
run: cargo clippy --locked --all-targets -- -D warnings
- name: Test installer script
if: runner.os == 'Linux'
run: |
bash -n install-linux.sh
bash tests/linux_startup.sh "${{ runner.temp }}"
bash install-linux.sh --help
bash -c 'export HOME=/root OPENMOUSE_INSTALL_SOURCED=1; source ./install-linux.sh; D=$(mktemp -d); write_udev_rule "$D/rule"; bodies=$(grep -c "SUBSYSTEM" "$D/rule"); vids=$(printf "%s" "$VENDOR_IDS" | wc -w); [ "$bodies" = "$vids" ] || exit 1; write_autostart "$D/a.desktop" "/opt/x/b"; grep -q "Exec=\"/opt/x/b\"" "$D/a.desktop"; write_launcher "$D/l.desktop" "/opt/x/b" "/opt/x/i.png"; grep -q "^Name=OpenMouse Bridge$" "$D/l.desktop"; grep -q "^Terminal=false$" "$D/l.desktop"; for pm in apt dnf pacman zypper apk; do pm_packages "$pm" | grep -q curl || exit 1; done; rm -rf "$D"'

windows-build:
name: Windows release artifact
needs: check
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -71,7 +77,8 @@ jobs:
- name: Package
shell: pwsh
run: |
Compress-Archive -Path target/release/openmouse-bridge.exe,README.md,native-hid -DestinationPath openmouse-bridge-windows-x64.zip
Copy-Item assets/fonts/DM-Sans-OFL.txt DM-Sans-OFL.txt
Compress-Archive -Path target/release/openmouse-bridge.exe,README.md,DM-Sans-OFL.txt,native-hid -DestinationPath openmouse-bridge-windows-x64.zip
(Get-FileHash openmouse-bridge-windows-x64.zip -Algorithm SHA256).Hash.ToLower() + " openmouse-bridge-windows-x64.zip" | Set-Content openmouse-bridge-windows-x64.zip.sha256
- uses: actions/upload-artifact@v4
with:
Expand Down
20 changes: 17 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: native-hid/package-lock.json
- run: cargo fmt --check
- run: cargo test --locked
- run: cargo clippy --locked --all-targets -- -D warnings
- run: cargo build --release --locked
- name: Sign executable
if: env.WINDOWS_CERTIFICATE_BASE64 != ''
Expand All @@ -55,7 +59,8 @@ jobs:
- name: Package
shell: pwsh
run: |
Compress-Archive -Path target/release/openmouse-bridge.exe,README.md,native-hid -DestinationPath openmouse-bridge-windows-x64.zip
Copy-Item assets/fonts/DM-Sans-OFL.txt DM-Sans-OFL.txt
Compress-Archive -Path target/release/openmouse-bridge.exe,README.md,DM-Sans-OFL.txt,native-hid -DestinationPath openmouse-bridge-windows-x64.zip
(Get-FileHash openmouse-bridge-windows-x64.zip -Algorithm SHA256).Hash.ToLower() + " openmouse-bridge-windows-x64.zip" | Set-Content openmouse-bridge-windows-x64.zip.sha256
- uses: actions/upload-artifact@v4
with:
Expand All @@ -71,14 +76,17 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
targets: aarch64-apple-darwin,x86_64-apple-darwin
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: native-hid/package-lock.json
- run: cargo fmt --check
- run: cargo test --locked
- run: cargo clippy --locked --all-targets -- -D warnings
- run: cargo build --release --locked --target aarch64-apple-darwin
- run: cargo build --release --locked --target x86_64-apple-darwin
- name: Prepare native-hid (deps + bundled Node.js runtime)
Expand All @@ -104,7 +112,8 @@ jobs:
target/x86_64-apple-darwin/release/openmouse-bridge \
-output openmouse-bridge
chmod +x openmouse-bridge
zip -r openmouse-bridge-macos-universal.zip openmouse-bridge README.md native-hid
cp assets/fonts/DM-Sans-OFL.txt ./DM-Sans-OFL.txt
zip -r openmouse-bridge-macos-universal.zip openmouse-bridge README.md DM-Sans-OFL.txt native-hid
shasum -a 256 openmouse-bridge-macos-universal.zip > openmouse-bridge-macos-universal.zip.sha256
- uses: actions/upload-artifact@v4
with:
Expand All @@ -120,13 +129,17 @@ jobs:
- name: Install Linux HID and GUI dependencies
run: sudo apt-get update && sudo apt-get install -y libudev-dev libgtk-3-dev libayatana-appindicator3-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libgl1-mesa-dev pkg-config
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
cache-dependency-path: native-hid/package-lock.json
- run: cargo fmt --check
- run: cargo test --locked
- run: cargo clippy --locked --all-targets -- -D warnings
- run: cargo build --release --locked
- name: Prepare native-hid (deps + bundled Node.js runtime)
run: |
Expand All @@ -145,7 +158,8 @@ jobs:
cp target/release/openmouse-bridge ./openmouse-bridge
chmod +x openmouse-bridge
cp assets/openmouse-app-icon.png ./openmouse-app-icon.png
zip -r openmouse-bridge-linux-x64.zip openmouse-bridge openmouse-app-icon.png README.md native-hid
cp assets/fonts/DM-Sans-OFL.txt ./DM-Sans-OFL.txt
zip -r openmouse-bridge-linux-x64.zip openmouse-bridge openmouse-app-icon.png README.md DM-Sans-OFL.txt native-hid
rm openmouse-bridge openmouse-app-icon.png
sha256sum openmouse-bridge-linux-x64.zip > openmouse-bridge-linux-x64.zip.sha256
- uses: actions/upload-artifact@v4
Expand Down
55 changes: 27 additions & 28 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ tower-http = { version = "0.6", features = ["cors", "set-header", "trace"] }
tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
zip = { version = "4.6", default-features = false, features = ["deflate"] }
zip = { version = "9.0", default-features = false, features = ["deflate"] }

[target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies]
eframe = { version = "0.36.1", default-features = false, features = ["accesskit", "default_fonts", "glow", "wayland", "x11"] }
Expand Down
81 changes: 73 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ explicit configuration file, `OPENMOUSE_BRIDGE_GAMES_URL` can override the
catalog endpoint, and `OPENMOUSE_BRIDGE_UPDATE_API_URL` can override the stable
release API endpoint.

## Local games and apps

OpenMouse presets remain the centrally maintained catalog. Personal targets live
separately in `localApplications` in Bridge's local config, so catalog refreshes
cannot replace them. Open Games & apps in the control panel, choose Add game or
app, then pick a running app or enter its executable name/full path. No request,
Discord account, approval or upload is involved. A connected mouse is not needed
to add a target; select one later to save its mouse settings.

Use Game to retain its profile while the matching process is running, including
when you alt-tab away. Use App to apply only while the app is in front. A full
executable path restricts matching to that installation; it never falls back to
another process with the same filename. With only a filename, all matching
executables are eligible. Windows paths ignore case and separator differences;
POSIX paths remain case-sensitive. Bridge detects processes; it does not launch
or execute the saved inputs. Foreground-only apps on Linux require X11.

Each local target has a stable `local:` ID, referenced by a mouse profile's
`application.localId`. Renaming/editing the target preserves linked mouse
settings. Removing it removes only profiles linked to that ID, not a same-name
OpenMouse preset or another target. Clearing a target's mouse settings does not
remove the target. Local data persists across Bridge restarts and is separate
from browser storage. Use the updated control panel and Bridge together; older
Bridges do not implement the local-target endpoints.

## Updates

The tray panel can check for a stable Bridge release on demand. Automatic
Expand Down Expand Up @@ -112,18 +137,28 @@ enumeration details.
OpenMouse client has completed a recent handshake.
- `PUT /v1/handshake` renews OpenMouse's 20-second connection lease. The client
sends this heartbeat every five seconds while connected.
- `GET /v1/games` returns the full executable catalog currently being tracked.
- `GET /v1/applications` lists running games and identifies the foreground
game. Only applications from the registered catalog are
returned. Each item includes an `iconId`; requesting
- `GET /v1/games` returns presets (`source: "preset"`) and local targets
(`source: "local"`, with `id`, `path` and `kind`).
- `GET /v1/local-applications` reads personal targets. `PUT` accepts
`{ applications: [{ id, name, executable, path, kind }] }` and atomically
validates/persists the complete local list. Limit: 256 targets, names up to 80
characters, executable leaf names up to 255, full paths up to 1024. Invalid
input returns 400; failed persistence returns 500 without changing live state.
- `GET /v1/applications` lists relevant visible applications from the preset
catalog or local targets and identifies the foreground application. Each item
includes an `iconId`; requesting
`GET /v1/applications/{iconId}/icon` returns its extracted icon as a PNG.
- `GET /v1/running-applications` lists visible running apps for the local picker,
including apps outside the preset catalog.
- `PUT /v1/default-profile` keeps Bridge synchronized with the mouse and
settings currently selected in OpenMouse. Bridge shows this profile whenever
no game-specific profile is active.
- `GET /v1/profiles` reads saved application profiles; `PUT /v1/profiles`
replaces and persists them.
- `PUT /v1/battery` accepts `{ deviceId, deviceName, percent, charging }` and
applies the notification threshold and cooldown.
- `DELETE /v1/battery/{deviceId}` forgets a cached battery report. Stale reports
can also be removed from the native panel's connection details.
- `GET /v1/hid` upgrades to the native HID WebSocket transport. It enumerates
only vendor IDs requested by OpenMouse, reports parsed HID collections, and
supports open, close, input, output, and feature-report operations. This lets
Expand All @@ -135,10 +170,40 @@ The listener binds only to loopback, never to a LAN or public interface.

## Current boundary

The HID WebSocket is active only while the control panel is connected. Battery
readings still come from that panel, so true battery alerts while the browser is
closed require a background native protocol reader. Game detection and saved
profile switching already run independently in the background.
The HID WebSocket is active only while the control panel is connected. Bridge
also reads battery levels every five minutes for devices in saved profiles when
no native HID client session is active. This requires a supported native battery
reader; Pulsar currently has no background battery reader. Readings older than
twelve minutes are marked as last known rather than live.

Game detection and saved profile switching run independently in the background.
Native profile application writes DPI and polling rate for supported devices.
Full settings snapshots are stored by Bridge but applied and restored by an open
control panel. A selected profile is not proof that a hardware write succeeded.

## Tray panel

The native tray panel uses OpenMouse's lit surfaces, inset controls, raised
actions, trailing switches, Emerald palette and embedded DM Sans. Home starts at
360 by 280 logical points; Settings is 360 by 420. Real content can grow Home up
to 340 points high. Extended connection and battery reports live behind the info
controls rather than filling the default view.
Home separates the Bridge service, browser connection, live HID sessions, saved
profile selection, and battery readings instead of treating them as one status.
Settings keeps startup, battery alerts, notification sound, updates, and logs in
one scrollable view.

Drag the header beside the OpenMouse name, or beside the Settings title, to move
the borderless window. Settings and Back leave its position unchanged. Clicking
the tray icon opens the panel near that icon, constrained to the monitor work
area on Windows. The titlebar minimize and close controls hide to the tray;
background operation continues. Escape also dismisses the panel. Quit Bridge
stops the process.

`OPENMOUSE_BRIDGE_TEST_POSITION=x,y` starts the panel at an explicit logical
position without requesting focus, for isolated secondary-monitor UI checks.
Use `OPENMOUSE_BRIDGE_CONFIG` with a disposable configuration and set
`autostartConfigured` to `true` to avoid first-run startup registration in tests.

## Verify

Expand Down
Loading
Loading