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

## Unreleased

**The SDK proxy is no longer recommended where it cannot load, and the
tools catch it.** ACC and AC EVO check the signature of Logitech's library
before loading it, and the proxy `logi-shim --proxy` puts in its place is
unsigned, so there the game drops TrueForce and every force the library
carries, silently. The README, the game setup guide and the apps' setup
hints recommended the proxy to G923 owners in exactly those two games; they
now point to Simulated TrueForce. `logi-shim --proxy` warns when it is
installed for either game, `setup.sh doctor` fails on a prefix that has it,
and `logi-launch` no longer grants raw HID on the strength of it there,
since raw HID would then take away the DirectInput force feedback as well.
It prints the two commands that put Logitech's own library back.

**`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.

**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
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -434,10 +434,9 @@ generated from the same registry the app uses, so the two cannot disagree.
Proton prefix (the app's Setup page, or `tools/install-tf-shim.sh`), and
launch with `logi-launch %command%`, which turns raw HID on for the
wheels that want it and stages the proxy that answers the SDK's rotation
question. Set `PROTON_ENABLE_HIDRAW=1` by hand only with something
answering that question (`tools/install-tf-shim.sh --proxy`): with
Logitech's stock library alone the game's steering and force feedback
stop on track, and the launcher refuses to set it in that state. The
question. Leave `PROTON_ENABLE_HIDRAW` to the launcher: with Logitech's
stock library alone the game's steering and force feedback stop on
track, and the launcher refuses to set it in that state. The
DLLs are the one part nobody can automate, because Logitech's files cannot
be redistributed. The one-time recipe is on the
[Force feedback in games](https://github.com/mescon/logitech-trueforce-linux-driver/wiki/Force-Feedback-in-Games)
Expand All @@ -447,12 +446,12 @@ generated from the same registry the app uses, so the two cannot disagree.
**On a G923 the recipe is different, not absent.** That wheel does not
answer the TrueForce SDK, so `PROTON_ENABLE_HIDRAW` must stay unset: there
it does not add TrueForce, it takes away the force feedback you already
had. Install the shim with `--proxy` instead. That puts this project's own
SDK proxy in the game's path, where it copies the TrueForce the game is
already producing and streams it to the wheel directly, so a G923 gets the
game's real haptics without the SDK needing to cooperate. The same proxy
answers the game's rotation question, which is the packaged fix for the
90-degree clamp (#27). See [G923 support](#g923-support).
had. For haptics, turn the game on under Simulated TrueForce instead:
`logi-tf-sim` synthesizes an engine note from the game's own telemetry,
confirmed working on a G923 in ACC and AC EVO. The shim's `--proxy`
option does not help in those two: both check the library's signature
and refuse the unsigned proxy, so it carries nothing there. See
[G923 support](#g923-support).

**Where the SDK files go.** Copy the `Logi` folder out of a Windows G HUB
install into `~/.local/share/logitech-trueforce/sdk`, keeping its own
Expand Down
14 changes: 6 additions & 8 deletions docs/GAME_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,11 @@ game's own telemetry, read out of its shared memory by a small relay
(`docs/SHARED_MEMORY_RELAY.md`). Confirmed working on a G923 in
Assetto Corsa Competizione and EVO.

There is a second route that carries the real thing: installing the
shim with `--proxy` puts this project's own SDK proxy in the game's
path, where it copies the TrueForce the game is already producing
and streams it to the wheel. It is the same proxy that ships the
packaged fix for the 90-degree rotation clamp, so it loads and
works; the apps do not install it yet, so
`tools/install-tf-shim.sh --proxy` is how to turn it on.
The shim's `--proxy` option is not a second route in these games.
Both check the library's signature before loading it and refuse the
unsigned proxy, so it carries nothing, and on a wheel that uses
Logitech's library it costs the game that library's force feedback
too. `./tools/setup.sh doctor` flags a prefix that has it.

Launch options go in Steam under the game's Properties. Paste them
exactly, `%command%` included: it is the placeholder Steam replaces
Expand Down Expand Up @@ -136,7 +134,7 @@ app's Setup page afterwards.
## What each recipe means

- **Install the shim.** Stage Logitech's signed SDK DLLs into the game's Proton prefix, from the app's Setup page or `tools/install-tf-shim.sh`. Install the TrueForce shim once, from the app's Setup page; launch options `logi-launch %command%` (it turns raw HID on, stages the proxy that answers the SDK's rotation question, without which the stock library stops steering and force on track, and everything else this game needs); turn Steam Input off. No engine-note texture merge here: ACC produces its own TrueForce audio, so the merge is not wired for it.
- **On a wheel with no SDK TrueForce.** Leave PROTON_ENABLE_HIDRAW unset: on this wheel it costs you force feedback. For haptics, turn this game on under Simulated TrueForce; logi-launch puts the telemetry relay in the game's prefix and starts the daemon for you, and that route is confirmed working on a G923. Installing the shim WITH --proxy carries the game's own TrueForce to the wheel instead, the same proxy that ships the 90-degree rotation fix; the apps install the plain shim, so use tools/install-tf-shim.sh --proxy. Steam Input off.
- **On a wheel with no SDK TrueForce.** Leave PROTON_ENABLE_HIDRAW unset: on this wheel it costs you force feedback. For haptics, turn this game on under Simulated TrueForce; logi-launch puts the telemetry relay in the game's prefix and starts the daemon for you, and that route is confirmed working on a G923. Steam Input off.
- **Launch via logi-ffb.** Launch options `logi-launch %command%` (it keeps raw HID off, runs the logi-ffb helper and puts the telemetry relay in the game's prefix for you); Steam Input off. Simulated TrueForce also needs the community rF2SharedMemoryMapPlugin installed in the game.
- **Nothing to do.** The wheel is an ordinary Linux force feedback device and the game drives it directly.

Expand Down
26 changes: 18 additions & 8 deletions tools/install-tf-shim.sh
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,15 @@
--sdk-dir <path> Directory holding your Logitech SDK DLLs
(default: \$LOGITECH_TRUEFORCE_SDK_DIR, the repo
sdk/ tree, or $(default_sdk_dir))
--proxy Also install this project's SDK proxy. It does
two things: answers the rotation question so a
game stops clamping the wheel to 90 degrees
(#27), and carries the game's own TrueForce to
a wheel Logitech's SDK will not drive, which is
how a G923 gets TrueForce in ACC and AC EVO.
Spelled --range-proxy before it did the second
job; both names still work.
--proxy Also install this project's SDK proxy, which
answers the rotation question (#27) and copies
TrueForce samples a game hands the library to
logi-tf-sim. It is unsigned, and games that
check the library's signature refuse it: ACC
and AC EVO then run with no TrueForce and no
force from the library at all, so do not use
it for them. Spelled --range-proxy before it
did the second job; both names still work.
--oem-ffb Also install G HUB's DirectInput force-feedback
driver (hidpp_forcefeedback), from a "Logitech"
folder beside the "Logi" one. Windows routes
Expand Down Expand Up @@ -377,6 +378,15 @@
mv -f "$tf_dir/trueforce_sdk_x64.dll" "$tf_dir/trueforce_real.dll"
install -m 0644 "$proxy" "$tf_dir/trueforce_sdk_x64.dll"
echo " rotation shim installed in $prefix"
# These two verify the library's signature before loading it,
# and the proxy is unsigned (docs/DINPUT_ESCAPE.md).
case $prefix in

Check failure on line 383 in tools/install-tf-shim.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a default case (*) to handle unexpected values.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDT8q126XxauI7g_pl8&open=AaDT8q126XxauI7g_pl8&pullRequest=123
*/compatdata/805550/pfx|*/compatdata/805550/pfx/|*/compatdata/3058630/pfx|*/compatdata/3058630/pfx/)
echo " WARNING: this game checks the library's signature and refuses the proxy:" >&2
echo " it will run with no TrueForce and no force from the library." >&2
echo " Undo: $0 --uninstall-prefix \"$prefix\", then --prefix without --proxy." >&2
;;
esac
else
echo " WARNING: --range-proxy asked for but tf-range-proxy.dll was not found" >&2
echo " in the checkout or /usr/share/logitech-trueforce/." >&2
Expand Down
29 changes: 26 additions & 3 deletions tools/logi-launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,16 @@
[ -f "$f" ] && have_tf_proxy=1 && break
done
fi
# ACC and AC EVO check the library's signature before loading it and
# refuse the unsigned proxy, so there it answers nothing: the SDK never
# loads, the game falls back to DirectInput, and raw HID would then take
# that force feedback away as well (AC EVO, 2026-09-24).
tf_proxy_refused=0
if [ "$have_tf_proxy" = "1" ]; then

Check failure on line 310 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=AaDT8qui6XxauI7g_pl3&open=AaDT8qui6XxauI7g_pl3&pullRequest=123
case "${SteamAppId:-${SteamGameId:-0}}" in

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

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a default case (*) to handle unexpected values.

See more on https://sonarcloud.io/project/issues?id=mescon_logitech-rs50-linux-driver&issues=AaDT8qui6XxauI7g_pl4&open=AaDT8qui6XxauI7g_pl4&pullRequest=123
805550|3058630) tf_proxy_refused=1; have_tf_proxy=0 ;;
esac
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 @@ -360,7 +370,16 @@
[ -r "$(share_file dinput8-escape.dll 2>/dev/null || echo /nonexistent)" ]; then
can_stage_proxy=1
fi
if [ "$have_tf_files" = "1" ] && [ "$have_tf_proxy" = "0" ] && \
if [ "$tf_proxy_refused" = "1" ]; then

Check failure on line 373 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=AaDT8qui6XxauI7g_pl5&open=AaDT8qui6XxauI7g_pl5&pullRequest=123
export PROTON_ENABLE_HIDRAW=0
say "NOT setting PROTON_ENABLE_HIDRAW: this prefix has the SDK proxy"
say "(logi-shim --proxy), and this game checks the library's"
say "signature and refuses it, so the SDK never loads here and raw HID"
say "would only take away the force feedback you have. To get the"
say "game's TrueForce back, reinstall the shim without the proxy:"
say " logi-shim --uninstall-prefix \"$prefix_root/pfx\""
say " logi-shim --prefix \"$prefix_root/pfx\" (tools/install-tf-shim.sh from a checkout)"
elif [ "$have_tf_files" = "1" ] && [ "$have_tf_proxy" = "0" ] && \

Check failure on line 382 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=AaDT8qui6XxauI7g_pl7&open=AaDT8qui6XxauI7g_pl7&pullRequest=123

Check failure on line 382 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=AaDT8qui6XxauI7g_pl6&open=AaDT8qui6XxauI7g_pl6&pullRequest=123
[ "$can_stage_proxy" = "0" ]; then
export PROTON_ENABLE_HIDRAW=0
say "NOT setting PROTON_ENABLE_HIDRAW: Logitech's TrueForce files are in"
Expand Down Expand Up @@ -795,17 +814,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
23 changes: 20 additions & 3 deletions tools/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,13 @@ doctor() {
# that, the four calls the shim answers itself keep working and
# the fifty-four it forwards do not, which is a wheel that steers
# to full lock and produces no force at all (issue #27).
local proxied=0 orphaned=0
#
# ACC and AC EVO check the library's signature before loading it
# and refuse the unsigned proxy outright, so there the shim costs
# the game its TrueForce and every force the library would have
# sent, with nothing in any log to say why (measured on AC EVO,
# 2026-09-24; docs/DINPUT_ESCAPE.md).
local proxied=0 orphaned=0 refused="" refused_n=0
while IFS= read -r root; do
for appid in $SDK_SIM_APPIDS; do
local d; d=$(ls -d "$root/steamapps/compatdata/$appid/pfx/$TF_PFX_DIR_REL/"*/ 2>/dev/null | tail -1)
Expand All @@ -636,12 +642,23 @@ doctor() {
grep -aq "trueforce_real" "$d/trueforce_sdk_x64.dll" 2>/dev/null || continue
proxied=$((proxied+1))
[ -f "$d/trueforce_real.dll" ] || orphaned=$((orphaned+1))
case $appid in
805550|3058630)
refused_n=$((refused_n+1))
refused="$refused$root/steamapps/compatdata/$appid/pfx
" ;;
esac
done
done <<< "$roots"
local pfx
while IFS= read -r pfx; do
[ -n "$pfx" ] || continue
bad "rotation shim installed in $pfx, a game that refuses it: no TrueForce and no force from Logitech's library there (fix: ./tools/install-tf-shim.sh --uninstall-prefix \"$pfx\", then --prefix \"$pfx\" without --proxy)"
done <<< "$refused"
if [ "$orphaned" -gt 0 ]; then
bad "rotation shim installed in $orphaned prefix(es) without Logitech's library beside it - those games get no force feedback (re-run: ./tools/install-tf-shim.sh --all-steam --range-proxy)"
elif [ "$proxied" -gt 0 ]; then
ok "rotation shim installed in $proxied SDK sim(s), with Logitech's library beside it"
elif [ "$proxied" -gt "$refused_n" ]; then
ok "rotation shim installed in $((proxied-refused_n)) SDK sim(s), with Logitech's library beside it"
fi
fi

Expand Down
10 changes: 2 additions & 8 deletions userspace/logi-wheel/crates/logi-wheel-core/src/games.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,9 +502,7 @@ impl GameCompat {
// has nothing to synthesise from, so it must not be offered.
"Leave PROTON_ENABLE_HIDRAW unset: on this wheel it costs you \
force feedback, and this game publishes no telemetry yet, so \
there is no simulated TrueForce to turn on either. Installing \
the shim WITH --proxy carries the game's own TrueForce to the \
wheel instead (tools/install-tf-shim.sh --proxy). Steam Input \
there is no simulated TrueForce to turn on either. Steam Input \
off."
} else if self.ffb == Ffb::TrueForceShim && !caps.sdk_trueforce {
// Worded as "not available on this wheel" rather than "this
Expand All @@ -515,11 +513,7 @@ impl GameCompat {
force feedback. For haptics, turn this game on under Simulated \
TrueForce; logi-launch puts the telemetry relay in the game's \
prefix and starts the daemon for you, and that route is \
confirmed working on a G923. Installing the shim WITH --proxy \
carries the game's own TrueForce to the wheel instead, the same \
proxy that ships the 90-degree rotation fix; the apps install \
the plain shim, so use tools/install-tf-shim.sh --proxy. Steam \
Input off."
confirmed working on a G923. Steam Input off."
} else {
self.setup
}
Expand Down
4 changes: 2 additions & 2 deletions userspace/logi-wheel/crates/logi-wheel-core/src/steam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,8 @@ pub fn resolve_sdk_dir(field: &str, installer: Option<&Path>) -> Option<PathBuf>
/// The plain shim, without the SDK proxy: the rotation question the SDK
/// must have answered under Proton is answered by the dinput8 escape
/// proxy `logi-launch` stages for every native-TrueForce session. The SDK
/// proxy (`--proxy`) stays a by-hand route; ACC on an RS50 would not load
/// it at all (2026-09-05), while it is what carries TrueForce to a G923.
/// proxy (`--proxy`) stays a by-hand route: it is unsigned, and ACC and
/// AC EVO check the library's signature and refuse it outright.
pub fn shim_install_args(prefix: &str, sdk_dir: Option<&Path>) -> Vec<String> {
let mut args = vec!["--prefix".to_string(), prefix.to_string()];
if let Some(dir) = sdk_dir {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,11 @@ fn render() -> String {
game's own telemetry, read out of its shared memory by a small relay\n\
(`docs/SHARED_MEMORY_RELAY.md`). Confirmed working on a G923 in\n\
Assetto Corsa Competizione and EVO.\n\n\
There is a second route that carries the real thing: installing the\n\
shim with `--proxy` puts this project's own SDK proxy in the game's\n\
path, where it copies the TrueForce the game is already producing\n\
and streams it to the wheel. It is the same proxy that ships the\n\
packaged fix for the 90-degree rotation clamp, so it loads and\n\
works; the apps do not install it yet, so\n\
`tools/install-tf-shim.sh --proxy` is how to turn it on.\n\n",
The shim's `--proxy` option is not a second route in these games.\n\
Both check the library's signature before loading it and refuse the\n\
unsigned proxy, so it carries nothing, and on a wheel that uses\n\
Logitech's library it costs the game that library's force feedback\n\
too. `./tools/setup.sh doctor` flags a prefix that has it.\n\n",
);
out.push_str(
"Launch options go in Steam under the game's Properties. Paste them\n\
Expand Down
Loading