Skip to content
Closed
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
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@ the contract is "it works on RS50 and G Pro as listed here".

## Unreleased

**`logi-launch` starts the `logi-tf-sim` it reports.** The daemon runs as a
user service, and the user manager looked the binary up on its own PATH, so
a copy earlier on the game's PATH was named in the log's versions line while
the installed one ran. The launcher now resolves the binary itself.

**An experimental route for the game's own TrueForce that skips Wine's
device path.** Assetto Corsa EVO and ACC stutter under `logi-launch` on
some direct-drive setups ([#74](../../issues/74), [#121](../../issues/121)).
A USB capture showed the wire clean, and measured in isolation the cause is
Logitech's library driving the wheel through Proton's raw HID: 1,000
packets a second, each answered by the wheel, every one crossing Wine's
device process and wineserver, which roughly doubles wineserver's load.
The raw joystick path costs the same as the normal one and is not it. A
new `tfroute=capture` games.conf key keeps raw HID off, so the library
never opens the wheel, and carries the game's own TrueForce another way:
the SDK proxy copies the samples the game hands the library to
`logi-tf-sim`, which streams them natively with the driver's force merged
in. Force feedback reaches the driver the normal way. A new session marker
tells the daemon this game's TrueForce arrives captured on a direct-drive
wheel and that it must synthesise nothing, so what the wheel gets is the
game's own haptics or silence. Experimental until it has been felt against
the default route.

**The DiRT Rally 2.0 recipe is written down, end to end.** With the proxy
turned on (`690790 ffb=proxy`), the telemetry set to `extradata="3"`, and a
one-line `device_defines.xml` entry that gives the stand-in wheel an
Expand Down
1 change: 1 addition & 0 deletions docs/LAUNCH_OPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ built-in plan asks for). To force something off, state it: `texture=none`,
| `tfsim` | `1`, `0` | run `logi-tf-sim`. Set `0` for a game whose own TrueForce already reaches your wheel. Asking for it alongside `texture=merge` is a combination only a hand-written line can produce, and it works: the two read the same telemetry port, so whichever holds it forwards to the other and both are fed (see "One socket gets the datagrams, every reader gets the telemetry" in [SHARED_MEMORY_RELAY.md](SHARED_MEMORY_RELAY.md)) |
| `texture` | `merge`, `none` | mix the driver's engine-note texture into the game's own TrueForce on the wheel. `merge` makes `logi-launch` stage the dinput8 escape proxy into the game's directory, start `logi-rpm-bridge` and switch `wheel_tf_merge` on, undoing all of it when the game exits. The same chain also lights the rev strip from the game's own telemetry: the proxy relays live rpm, first-shift-light rpm and redline, and `logi-rpm-bridge` drives `wheel_rev_level` with them (full bar by default, `LOGI_REV_MODE=shift` for the dash band). Only does anything for a direct-drive wheel in an SDK title with the TrueForce files installed |
| `revleds` | `bar`, `shift` | how `logi-rpm-bridge` maps the rev strip while `texture=merge` drives it: `bar` (the default) lights LED 1 as soon as the engine turns and all 10 at the limiter, `shift` is the dash band (dark below the car's first shift light, level 1 exactly there). The apps persist this choice in `~/.config/logi-wheel/launch.conf` and it shows on a merge title's Setup card; a games.conf line overrides it per game like any other key |
| `tfroute` | `capture`, `sdk` | how a game's own TrueForce reaches a direct-drive wheel. `sdk` (the default) lets Logitech's library drive the wheel through raw HID. `capture` keeps raw HID off and carries the same TrueForce another way: the SDK proxy (`logi-shim --proxy`, required in the game's prefix) copies what the game hands the library to `logi-tf-sim`, which streams it natively, with the driver's force merged in, and synthesises nothing. It exists because the library's 1,000 packets a second, each answered by the wheel, all cross Wine's device process and wineserver, and on some setups that stalls the game (#74, #121). Setting it also sets `hidraw=0`, `texture=none`, `tfsim=1` and the game's relay for the rev lights. Experimental: feel it against the default before relying on it |

A line that works for you is also exactly the report needed to add the game
properly, so please open an issue with it.
Expand Down
65 changes: 61 additions & 4 deletions tools/logi-launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,45 @@
# (LOGI_REV_MODE=shift in the bridge's environment, below). The app
# persists the choice in launch.conf and states it in the plan.
want_revleds=$(plan_get revleds)
# The capture route (`tfroute=capture`, a games.conf key for now): the
# game's own TrueForce, carried to the wheel without Logitech's library.
#
# On the default route that library drives the wheel through Proton's raw
# HID: a stream of 1,000 packets a second, each answered by the wheel, and
# every packet and answer crosses Wine's device process and wineserver.
# Measured on an RS50 setup that roughly doubles wineserver's load, and a
# busy wineserver stalls the game's own requests: the stutter in #74 and
# #121. The raw joystick path is not the cost; that library's stream is.
#
# On this route raw HID stays off, so the library never opens the wheel and
# none of that traffic exists. Force feedback reaches the driver's engine
# the normal way, through DirectInput. The TrueForce the game hands the
# library is copied, sample block by sample block, by the SDK proxy
# (install-tf-shim.sh --proxy) to logi-tf-sim over a local socket, and the
# daemon streams it natively, with the engine's force merged into the same
# packets. The game's own haptics, not a synthesis: the daemon is told so
# by a session marker and synthesises nothing for this game.
want_tfroute=$(plan_get tfroute)
if [ "$want_tfroute" = "capture" ]; then

Check failure on line 271 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY6y95ZM9aPdyqU&open=AaDTjaY6y95ZM9aPdyqU&pullRequest=122
want_hidraw=0
want_texture=none
want_tfsim=1
# The relay supplies the telemetry for the rev lights and the screen.
# The default direct-drive plan has none for an SDK title, since the
# library drove the lights there; on this route nothing else does.
if [ -z "$want_relay" ] || [ "$want_relay" = "none" ]; then

Check failure on line 278 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqW&open=AaDTjaY7y95ZM9aPdyqW&pullRequest=122

Check failure on line 278 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY6y95ZM9aPdyqV&open=AaDTjaY6y95ZM9aPdyqV&pullRequest=122
want_relay=$(relay_game_for "${SteamAppId:-${SteamGameId:-0}}")
[ -n "$want_relay" ] || want_relay=none

Check failure on line 280 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqX&open=AaDTjaY7y95ZM9aPdyqX&pullRequest=122
fi
export LOGI_TF_CAPTURE=1
fi
# The tfsim default is 1 in BOTH places it is read (here and at the start
# below), because a plan that states nothing means no plan was produced at
# all, and an unidentified game still gets the daemon: it idles when nothing
# is streaming, and withholding it would leave every UDP-telemetry title
# unserved (`LaunchPlan::unknown`). This line said 0 while the code did 1,
# so the log contradicted the behaviour for exactly those games.
say "plan: wheel=$(plan_get wheel) game=$(plan_get game) hidraw=${want_hidraw:-unset} ffb=${want_ffb:-native} relay=${want_relay:-none} tfsim=${want_tfsim:-1} texture=${want_texture:-none} revleds=${want_revleds:-bar}"
say "plan: wheel=$(plan_get wheel) game=$(plan_get game) hidraw=${want_hidraw:-unset} ffb=${want_ffb:-native} relay=${want_relay:-none} tfsim=${want_tfsim:-1} texture=${want_texture:-none} revleds=${want_revleds:-bar} tfroute=${want_tfroute:-sdk}"

# TrueForce in an SDK title needs the game to reach the wheel's raw HID
# interface. Set here so nobody has to remember it, and NEVER guessed: on a
Expand Down Expand Up @@ -302,6 +334,15 @@
[ -f "$f" ] && have_tf_proxy=1 && break
done
fi
# The capture route copies the game's TrueForce out of the SDK proxy; with
# Logitech's library alone in the prefix there is nothing to copy it, and
# the session runs with force feedback but no TrueForce. Said plainly
# rather than left for someone to notice on track.
if [ "$want_tfroute" = "capture" ] && [ -n "$prefix_root" ] && [ "$have_tf_proxy" = "0" ]; then

Check failure on line 341 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqa&open=AaDTjaY7y95ZM9aPdyqa&pullRequest=122

Check failure on line 341 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqY&open=AaDTjaY7y95ZM9aPdyqY&pullRequest=122

Check failure on line 341 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqZ&open=AaDTjaY7y95ZM9aPdyqZ&pullRequest=122
say "capture route: the SDK proxy is not installed in this prefix, so the game's"
say "TrueForce has nothing to carry it; force feedback still works. Install it once:"
say " logi-shim --prefix \"$prefix_root/pfx\" --proxy"
fi

# Nonzero when the plan granted the game raw HID access (an SDK title):
# those sessions can leave the wheel's TrueForce engine started, so they
Expand Down Expand Up @@ -746,6 +787,7 @@
# Written whenever raw HID is granted, whether or not this launch is the
# one starting the daemon: the marker describes the session, not that.
native_marker=""
captured_marker=""
if [ -n "${LOGI_WHEEL_RUNTIME_DIR:-}" ]; then
marker_dir="$LOGI_WHEEL_RUNTIME_DIR"
elif [ -n "${XDG_RUNTIME_DIR:-}" ]; then
Expand All @@ -764,6 +806,16 @@
want_relay_marker=1
rm -f "$marker_dir/native.$safe_id" 2>/dev/null
fi
rm -f "$marker_dir/captured.$safe_id" 2>/dev/null
if [ "$want_tfroute" = "capture" ] && [ "$want_relay_marker" = 1 ]; then

Check failure on line 810 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqb&open=AaDTjaY7y95ZM9aPdyqb&pullRequest=122

Check failure on line 810 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqc&open=AaDTjaY7y95ZM9aPdyqc&pullRequest=122
mkdir -p "$marker_dir" 2>/dev/null
if : > "$marker_dir/captured.$safe_id" 2>/dev/null; then
captured_marker="$marker_dir/captured.$safe_id"
say "marked this session as the capture route ($captured_marker); the daemon streams the game's own TrueForce and synthesises nothing"
else
say "could not write $marker_dir/captured.$safe_id; the daemon will keep the captured TrueForce off this wheel"
fi
fi
if [ -n "$hidraw_granted" ] && [ "$want_relay_marker" = 1 ]; then
mkdir -p "$marker_dir" 2>/dev/null
if : > "$marker_dir/native.$safe_id" 2>/dev/null; then
Expand Down Expand Up @@ -795,17 +847,21 @@
# Steam, a system without a user manager), the daemon runs as a child
# of this wrapper instead and is stopped when the game exits, which
# is the honest alternative: a later session starts it again.
# The service is started by the user manager, which searches its own
# PATH, not this session's; the binary is resolved here so the daemon
# that runs is the one the versions line above reported.
start_tf_sim() {
tfsim_bin=$(command -v logi-tf-sim 2>/dev/null || echo logi-tf-sim)
if command -v systemd-run >/dev/null 2>&1 && \
systemd-run --user --quiet --collect \
--description="logi-tf-sim (started by logi-launch)" \
--property=StandardOutput=append:"$LOG" \
--property=StandardError=append:"$LOG" \
env "$@" logi-tf-sim 2>/dev/null; then
env "$@" "$tfsim_bin" 2>/dev/null; then
say "logi-tf-sim runs as a user service, outside Steam's process tree"
return 0
fi
setsid env "$@" logi-tf-sim >>"$LOG" 2>&1 </dev/null &
setsid env "$@" "$tfsim_bin" >>"$LOG" 2>&1 </dev/null &
tfsim_child_pid=$!
say "logi-tf-sim runs as a child of this session (no user service manager here); it stops when the game exits"
}
Expand Down Expand Up @@ -1156,9 +1212,10 @@
# process behind.
if [ -n "$rpm_bridge_pid" ] || [ -n "$merge_attrs" ] || \
[ -n "$hidraw_granted" ] || [ -n "$helper_group_pid" ] || \
[ -n "$tfsim_child_pid" ]; then
[ -n "$tfsim_child_pid" ] || [ -n "$captured_marker" ]; then

Check failure on line 1215 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqd&open=AaDTjaY7y95ZM9aPdyqd&pullRequest=122

Check failure on line 1215 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqe&open=AaDTjaY7y95ZM9aPdyqe&pullRequest=122
session_cleanup() {
[ -n "$native_marker" ] && rm -f "$native_marker" 2>/dev/null
[ -n "$captured_marker" ] && rm -f "$captured_marker" 2>/dev/null

Check failure on line 1218 in tools/logi-launch.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDTjaY7y95ZM9aPdyqf&open=AaDTjaY7y95ZM9aPdyqf&pullRequest=122
[ -n "$rpm_bridge_pid" ] && kill "$rpm_bridge_pid" 2>/dev/null
if [ -n "$tfsim_child_pid" ]; then
kill "$tfsim_child_pid" 2>/dev/null
Expand Down
Loading
Loading