Compare commits

...
8 Commits
Author SHA1 Message Date
Felitendo 15a7cb9cf9 chore: 1.6.1
release / Debian package (push) Failing after 14s
release / RPM package (push) Failing after 21s
release / Release and repositories (push) Skipped
release / Install from the RPM repository (push) Skipped
2026-09-29 08:16:25 +02:00
Felitendo 0234e81811 fix: patch symlinked entries instead of writing through them 2026-09-29 08:15:38 +02:00
Felitendo 6f62c7f21c fix: patch the steamrt3 Steam client 2026-09-29 08:15:33 +02:00
Felitendo 3c000b8a6e docs: show new ui as screenshots in the readme 2026-09-28 15:08:16 +02:00
Felitendo 0f9b041b20 chore: show feature requests before bug reports 2026-09-26 21:19:26 +02:00
Felitendo 98f6ac9c48 chore: commit different topics separately 2026-09-25 09:30:38 +02:00
Felitendo 9861d24c2a chore: switch to ko-fi 2026-09-24 13:21:21 +02:00
Felitendo f74fc3e8ca chore: add issue templates 2026-09-24 12:39:52 +02:00
14 changed files with 355 additions and 77 deletions

No files matched your search

+1 -1
View File
@@ -1 +1 @@
buy_me_a_coffee: felitendo ko_fi: felitendo
@@ -0,0 +1,35 @@
name: 💡 Feature request
description: An idea to make middleclick-autoscroll better.
labels:
- enhancement
body:
- type: checkboxes
id: checks
attributes:
label: Before you start
options:
- label: I searched the issues and this is not requested yet.
required: true
- type: textarea
id: problem
attributes:
label: What problem would this solve?
description: What are you trying to do, and what gets in the way?
validations:
required: true
- type: textarea
id: idea
attributes:
label: What would you like?
description: How it could work. A rough idea is fine.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Other ways you thought of
- type: textarea
id: more
attributes:
label: Anything else?
description: Mockups, screenshots, or other tools that do it well.
+85
View File
@@ -0,0 +1,85 @@
name: 🐛 Bug report
description: Something does not work as it should.
labels:
- bug
body:
- type: markdown
attributes:
value: Thanks for taking the time! The more you fill in, the faster it can be fixed.
- type: checkboxes
id: checks
attributes:
label: Before you start
options:
- label: I searched the issues and this is not reported yet.
required: true
- label: I use the latest version.
required: true
- type: textarea
id: what
attributes:
label: What happened?
description: What did you do, and what went wrong?
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1.
2.
3.
- type: textarea
id: expected
attributes:
label: What did you expect?
validations:
required: true
- type: input
id: app
attributes:
label: App
description: 'Which app, and how it is installed: native package, Flatpak, Snap or AppImage.'
placeholder: Discord, Flatpak
- type: input
id: version
attributes:
label: Version
description: The output of `middleclick-autoscroll --version`.
placeholder: middleclick-autoscroll 1.6.0
validations:
required: true
- type: dropdown
id: install
attributes:
label: How did you install it?
options:
- AUR (Arch, CachyOS, EndeavourOS, Manjaro)
- Fedora package
- Bazzite
- Debian or Ubuntu package
- openSUSE package
- Built from source
- Other
validations:
required: true
- type: input
id: system
attributes:
label: System
description: Distribution, desktop and session (Wayland or X11).
placeholder: CachyOS, KDE Plasma 6.4, Wayland
validations:
required: true
- type: textarea
id: logs
attributes:
label: Status and logs
description: The output of `middleclick-autoscroll status` and `middleclick-autoscroll list`.
render: shell
- type: textarea
id: more
attributes:
label: Anything else?
description: Screenshots, videos or anything else that could help.
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: false
+1 -1
View File
@@ -125,7 +125,7 @@ $entry
If $name is useful to you, you can buy me a coffee. It keeps these tools going. Found a bug or have an idea? Tell me in the [issues](https://github.com/$repo/issues). If $name is useful to you, you can buy me a coffee. It keeps these tools going. Found a bug or have an idea? Tell me in the [issues](https://github.com/$repo/issues).
<a href="https://buymeacoffee.com/felitendo"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="48"></a> <a href="https://ko-fi.com/felitendo"><img src="https://storage.ko-fi.com/cdn/kofi5.png?v=6" alt="Buy me a coffee on Ko-fi" height="48"></a>
---- ----
+6
View File
@@ -2,6 +2,12 @@
What each release brings, newest first. The [GitHub releases](https://github.com/LoonixTools/middleclick-autoscroll/releases) add every commit that went into it. Versions before `v1.1.0` came without notes. What each release brings, newest first. The [GitHub releases](https://github.com/LoonixTools/middleclick-autoscroll/releases) add every commit that went into it. Versions before `v1.1.0` came without notes.
## v1.6.1
_2026-09-29_
A small patch. Autoscroll now works in Steam with **Use experimental SteamRT3 Steam Client** turned on. And apps that start at login through a link, like Mullvad VPN, get autoscroll now instead of an error.
## v1.6.0 ## v1.6.0
_2026-09-24_ _2026-09-24_
+8 -1
View File
@@ -15,7 +15,8 @@
- Body only when something genuinely cannot be inferred from the diff. - Body only when something genuinely cannot be inferred from the diff.
- Never add `Co-Authored-By`, "Generated with" or any other AI attribution to commits or PR descriptions. - Never add `Co-Authored-By`, "Generated with" or any other AI attribution to commits or PR descriptions.
- Do not commit or push before I have checked the changes locally and said they are fine. Then - Do not commit or push before I have checked the changes locally and said they are fine. Then
commit the result once and push. No commits for attempts that did not work. This also goes for commit and push. Several attempts at the same thing make one commit, and attempts that did not
work make none. Different things done in one session get a commit each. This also goes for
releases and tags. releases and tags.
## Releases ## Releases
@@ -67,3 +68,9 @@ when you press Enter." The list of commits follows on its own.
- Commands and settings they type go in backticks, buttons and labels in bold. - Commands and settings they type go in backticks, buttons and labels in bold.
- To change an old release: edit its entry, commit, then - To change an old release: edit its entry, commit, then
`gh release edit <tag> --title <tag> --notes "$(.github/release-notes.sh <tag>)"`. `gh release edit <tag> --title <tag> --notes "$(.github/release-notes.sh <tag>)"`.
## README
- New UI (a window, a screen, a menu, a setting, a notification) gets a screenshot in the README,
next to the text about it. Like for releases: a real screenshot of it running, in English.
- When a screen changes, take its screenshot again. The README never shows an old one.
+1 -1
View File
@@ -8,7 +8,7 @@
# Overridable so a packager can pass the version it is actually building # Overridable so a packager can pass the version it is actually building
# (`make VERSION=$pkgver`). The literal below is the fallback for builds # (`make VERSION=$pkgver`). The literal below is the fallback for builds
# straight from a checkout, and is what a release tag has to carry. # straight from a checkout, and is what a release tag has to carry.
VERSION ?= 1.6.0 VERSION ?= 1.6.1
PREFIX ?= /usr PREFIX ?= /usr
DESTDIR ?= DESTDIR ?=
+1 -1
View File
@@ -17,7 +17,7 @@
</h5> </h5>
<p align="center"> <p align="center">
<a href="https://buymeacoffee.com/felitendo"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="48"></a> <a href="https://ko-fi.com/felitendo"><img src="https://storage.ko-fi.com/cdn/kofi5.png?v=6" alt="Buy me a coffee on Ko-fi" height="48"></a>
</p> </p>
## Install ## Install
+19 -6
View File
@@ -116,7 +116,9 @@ Two ways, chosen per application.
Programs that start themselves at login write their own entry into Programs that start themselves at login write their own entry into
_~/.config/autostart_ pointing straight at their binary, bypassing the menu _~/.config/autostart_ pointing straight at their binary, bypassing the menu
entry entirely. Those are patched in place as well, unless the application is entry entirely. Those are patched in place as well, unless the application is
turned off in the applications list. turned off in the applications list. An entry that is a symlink, often to an
application's entry in _/usr/share/applications_, is replaced by a patched
file rather than written through, and undoing puts the link back.
Shortcuts on the desktop itself are patched in place too. Nothing in the XDG Shortcuts on the desktop itself are patched in place too. Nothing in the XDG
search path looks at that folder, so a shortcut that lives only there would search path looks at that folder, so a shortcut that lives only there would
@@ -133,6 +135,12 @@ helper, inside Steam's own installation:
~/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh ~/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
The steamrt3 client, a beta for now, has a script of its own:
~/.local/share/Steam/steamrt64/steamwebhelper.sh
Both are patched where both are there, so switching clients keeps autoscroll.
Where that installation is depends on how Steam was installed: Where that installation is depends on how Steam was installed:
_~/.local/share/Steam_ for Valve's own package and Arch's, _~/.local/share/Steam_ for Valve's own package and Arch's,
_~/.steam/debian-installation_ for Debian's, and the private tree of the _~/.steam/debian-installation_ for Debian's, and the private tree of the
@@ -154,11 +162,16 @@ it was given arguments for, and that one carries no arguments of anybody's. One
wrong length costs the whole client package downloaded, extracted and installed wrong length costs the whole client package downloaded, extracted and installed
again, and a client that quits at the end of it instead of coming up. again, and a client that quits at the end of it instead of coming up.
So where the comments cannot pay for the argument, or a client update has The steamrt3 script has no comments to pay with. So it is copied aside as
changed how the helper is started, nothing is written and the status screen _steamwebhelper.sh.orig_, and a stub of the same size takes its place. The stub
says Steam is not patched. Nothing goes on Steam's launcher entry, its runs the copy with the argument added. Steam only checks the files it knows,
autostart entry or the shortcuts it writes for single games either, and Steam and the copy is not one of them.
goes on repairing its own installation.
So where neither fits, or a client update has changed how the helper is
started, nothing is written and the status screen says Steam is not patched.
Nothing goes on Steam's launcher entry, its autostart entry or the shortcuts it
writes for single games either, and Steam goes on repairing its own
installation.
An installation patched by an earlier version, which appended where the An installation patched by an earlier version, which appended where the
comments were short, is written again to fit at the next apply, or taken back comments were short, is written again to fit at the next apply, or taken back
+6 -1
View File
@@ -30,12 +30,17 @@ PathModified=%h/.guix-profile/share/applications
# Steam replaces the script that starts its interface on every client update, # Steam replaces the script that starts its interface on every client update,
# which takes the patch with it. Where that installation lives depends on how # which takes the patch with it. Where that installation lives depends on how
# Steam was installed. # Steam was installed. The steamrt3 client has a script of its own.
PathChanged=%h/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh PathChanged=%h/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/.steam/debian-installation/ubuntu12_64/steamwebhelper_sniper_wrap.sh PathChanged=%h/.steam/debian-installation/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh PathChanged=%h/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/.var/app/com.valvesoftware.Steam/data/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh PathChanged=%h/.var/app/com.valvesoftware.Steam/data/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/snap/steam/common/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh PathChanged=%h/snap/steam/common/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/.local/share/Steam/steamrt64/steamwebhelper.sh
PathChanged=%h/.steam/debian-installation/steamrt64/steamwebhelper.sh
PathChanged=%h/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamrt64/steamwebhelper.sh
PathChanged=%h/.var/app/com.valvesoftware.Steam/data/Steam/steamrt64/steamwebhelper.sh
PathChanged=%h/snap/steam/common/.local/share/Steam/steamrt64/steamwebhelper.sh
Unit=middleclick-autoscroll.service Unit=middleclick-autoscroll.service
+7 -1
View File
@@ -343,12 +343,18 @@ mca_backup_name() {
# copy was stored under. Existing backups are never overwritten: the first copy # copy was stored under. Existing backups are never overwritten: the first copy
# is the pristine one, and a second apply must not replace it with an already # is the pristine one, and a second apply must not replace it with an already
# patched version. # patched version.
#
# A symlink is kept as a symlink, so undo can put the link back. It is taken
# again every time: this program never writes a link, so one is always pristine.
mca_backup() { mca_backup() {
local file="$1" name local file="$1" name
name="$(mca_backup_name "$file")" name="$(mca_backup_name "$file")"
mkdir -p "$MCA_BACKUPDIR" 2>/dev/null || return 1 mkdir -p "$MCA_BACKUPDIR" 2>/dev/null || return 1
if [[ ! -e "$MCA_BACKUPDIR/$name" ]]; then if [[ -L $file ]]; then
rm -f -- "$MCA_BACKUPDIR/$name"
cp -P -p -- "$file" "$MCA_BACKUPDIR/$name" 2>/dev/null || return 1
elif [[ ! -e "$MCA_BACKUPDIR/$name" && ! -L "$MCA_BACKUPDIR/$name" ]]; then
cp -p -- "$file" "$MCA_BACKUPDIR/$name" 2>/dev/null || return 1 cp -p -- "$file" "$MCA_BACKUPDIR/$name" 2>/dev/null || return 1
fi fi
printf '%s\n' "$name" printf '%s\n' "$name"
+33 -6
View File
@@ -375,7 +375,7 @@ mca_desktop_apply() {
[[ "$content" == "$(< "$src")" ]] && return 0 [[ "$content" == "$(< "$src")" ]] && return 0
backup="$(mca_backup "$src")" || return 1 backup="$(mca_backup "$src")" || return 1
if mca_write_if_changed "$src" "$content"$'\n'; then if _mca_entry_write "$src" "$content"$'\n'; then
MCA_CHANGES=$(( MCA_CHANGES + 1 )) MCA_CHANGES=$(( MCA_CHANGES + 1 ))
fi fi
mca_ledger_add inplace "$src" "$backup" mca_ledger_add inplace "$src" "$backup"
@@ -418,6 +418,30 @@ mca_desktop_apply() {
# Neither can be shadowed from anywhere, so both are edited where they stand, # Neither can be shadowed from anywhere, so both are edited where they stand,
# with the original kept. # with the original kept.
# _mca_entry_write <file> <content>
# Writes an entry edited in place, like mca_write_if_changed. A symlink is
# replaced by the edited file instead of written through: it usually points at
# a package's entry in /usr, which is not ours to change. Undo puts the link
# back from the backup.
_mca_entry_write() {
local file="$1" content="$2" tmp
[[ -L $file ]] || { mca_write_if_changed "$file" "$content"; return; }
tmp="$(mktemp "$file.XXXXXX")" || return 2
chmod --reference="$file" -- "$tmp" 2>/dev/null || chmod 644 -- "$tmp"
# A link into /usr/share/applications is trusted for where it points. A
# copy on the desktop is only trusted by KDE when it can be run.
chmod u+x -- "$tmp" 2>/dev/null || true
if printf '%s' "$content" > "$tmp" && mv -f -- "$tmp" "$file"; then
return 0
fi
rm -f -- "$tmp"
return 2
}
# _mca_entry_patch_inplace <file> # _mca_entry_patch_inplace <file>
# One desktop entry that lives outside the XDG search path, edited where it is # One desktop entry that lives outside the XDG search path, edited where it is
# because there is nowhere to shadow it from. It follows the applications list # because there is nowhere to shadow it from. It follows the applications list
@@ -482,7 +506,7 @@ _mca_entry_patch_inplace() {
[[ "$content" == "$(< "$file")" ]] && return 0 [[ "$content" == "$(< "$file")" ]] && return 0
backup="$(mca_backup "$file")" || return 0 backup="$(mca_backup "$file")" || return 0
if mca_write_if_changed "$file" "$content"$'\n'; then if _mca_entry_write "$file" "$content"$'\n'; then
MCA_CHANGES=$(( MCA_CHANGES + 1 )) MCA_CHANGES=$(( MCA_CHANGES + 1 ))
fi fi
mca_ledger_add inplace "$file" "$backup" mca_ledger_add inplace "$file" "$backup"
@@ -607,7 +631,7 @@ mca_prune_orphans() {
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
mca_revert_all() { mca_revert_all() {
local kind path detail local kind path detail backup
[[ -f $MCA_LEDGER ]] || return 0 [[ -f $MCA_LEDGER ]] || return 0
@@ -623,12 +647,15 @@ mca_revert_all() {
fi fi
;; ;;
inplace) inplace)
if [[ -n $detail && -f "$MCA_BACKUPDIR/$detail" ]]; then backup="$MCA_BACKUPDIR/$detail"
if [[ -n $detail ]] && [[ -f $backup || -L $backup ]]; then
if [[ -e $path ]] || [[ -d "$(dirname -- "$path")" ]]; then if [[ -e $path ]] || [[ -d "$(dirname -- "$path")" ]]; then
cp -p -- "$MCA_BACKUPDIR/$detail" "$path" 2>/dev/null \ # A link goes back as a link, not written through.
[[ -L $backup ]] && rm -f -- "$path"
cp -P -p -- "$backup" "$path" 2>/dev/null \
&& MCA_CHANGES=$(( MCA_CHANGES + 1 )) && MCA_CHANGES=$(( MCA_CHANGES + 1 ))
fi fi
rm -f -- "$MCA_BACKUPDIR/$detail" rm -f -- "$backup"
elif [[ -f $path ]]; then elif [[ -f $path ]]; then
# No backup: the file did not exist before we wrote it. # No backup: the file did not exist before we wrote it.
rm -f -- "$path" rm -f -- "$path"
+151 -58
View File
@@ -9,6 +9,10 @@
# #
# ~/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh # ~/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
# #
# The steamrt3 client, a beta for now, has a script of its own:
#
# ~/.local/share/Steam/steamrt64/steamwebhelper.sh
#
# Steam compares the installed files against its manifest at every start (by # Steam compares the installed files against its manifest at every start (by
# size and timestamp, not by content) and restores whatever differs. # size and timestamp, not by content) and restores whatever differs.
# So the patch is written to look untouched: the bytes the flag costs are taken # So the patch is written to look untouched: the bytes the flag costs are taken
@@ -28,6 +32,10 @@
# all: no autoscroll in the interface is a smaller thing than a client that # all: no autoscroll in the interface is a smaller thing than a client that
# reinstalls itself. # reinstalls itself.
# #
# The steamrt3 script has no comments to give back. So it is copied aside, as
# it is, and replaced by a stub of the same size that runs the copy with the
# flag. See _mca_steam_stub.
#
# The file comes back on every client update, and the watcher re-applies the # The file comes back on every client update, and the watcher re-applies the
# patch when that happens. # patch when that happens.
@@ -60,24 +68,28 @@ mca_steam_roots() {
done done
} }
# The script to patch inside a Steam installation. Newer clients start the # The scripts to patch inside a Steam installation, one per client. The default
# helper through a wrapper inside the container runtime; older ones exec it # client starts the helper through a wrapper inside the container runtime;
# from steamwebhelper.sh directly, and that one forwards its arguments too. # older ones exec it from steamwebhelper.sh directly, and that one forwards its
mca_steam_script() { # arguments too. The steamrt3 client has its own. Both clients are patched
local root="$1" f # where both are there, so switching between them keeps autoscroll.
mca_steam_scripts() {
local root="$1" f found=1
for f in \ for f in \
"$root/ubuntu12_64/steamwebhelper_sniper_wrap.sh" \ "$root/ubuntu12_64/steamwebhelper_sniper_wrap.sh" \
"$root/ubuntu12_64/steamwebhelper.sh" "$root/ubuntu12_64/steamwebhelper.sh"
do do
[[ -f $f && -w $f ]] && { printf '%s\n' "$f"; return 0; } [[ -f $f && -w $f ]] && { printf '%s\n' "$f"; found=0; break; }
done done
return 1 f="$root/steamrt64/steamwebhelper.sh"
[[ -f $f && -w $f ]] && { printf '%s\n' "$f"; found=0; }
return "$found"
} }
mca_steam_installed() { mca_steam_installed() {
local root local root
while IFS= read -r root; do while IFS= read -r root; do
mca_steam_script "$root" > /dev/null && return 0 mca_steam_scripts "$root" > /dev/null && return 0
done < <(mca_steam_roots) done < <(mca_steam_roots)
return 1 return 1
} }
@@ -87,13 +99,17 @@ mca_steam_script_patched() {
grep -q -- "$MCA_FEATURE" "$1" 2>/dev/null grep -q -- "$MCA_FEATURE" "$1" 2>/dev/null
} }
# Whether every script there is patched. One that is not is a client that
# starts without autoscroll.
mca_steam_patched() { mca_steam_patched() {
local root script local root script found=0
while IFS= read -r root; do while IFS= read -r root; do
script="$(mca_steam_script "$root")" || continue while IFS= read -r script; do
mca_steam_script_patched "$script" && return 0 mca_steam_script_patched "$script" || return 1
found=1
done < <(mca_steam_scripts "$root")
done < <(mca_steam_roots) done < <(mca_steam_roots)
return 1 (( found ))
} }
# _mca_steam_exec_line <script> # _mca_steam_exec_line <script>
@@ -163,6 +179,81 @@ _mca_steam_same_size() {
(( $(wc -c < "$script") == $(printf '%s\n' "$content" | wc -c) )) (( $(wc -c < "$script") == $(printf '%s\n' "$content" | wc -c) ))
} }
# Where a stub keeps Steam's own script, and the line that runs it from there.
MCA_STEAM_ORIG=.orig
MCA_STEAM_STUB="exec \"\$0$MCA_STEAM_ORIG\" \"\$@\""
# _mca_steam_stub <script> <flags>
# The stub on stdout, for a script with no comments to pay for the flags. It
# is exactly as long as the script and runs Steam's own script, kept next to
# it, with the flags added. That script hands its arguments on to the helper.
# Steam only checks the files in its manifest, and the copy is not one of them.
# Fails when the script does not hand its arguments on, has no #! line, or is
# too short to hold the stub.
_mca_steam_stub() {
local script="$1" flags="$2" head body pad
[[ -n "$(_mca_steam_exec_line "$script")" ]] || return 1
head="$(head -n 1 -- "$script")"
[[ $head == '#!'* ]] || return 1
body="$MCA_STEAM_STUB $flags"
pad=$(( $(wc -c < "$script") - $(printf '%s\n%s\n' "$head" "$body" | wc -c) ))
(( pad >= 0 )) || return 1
# The bytes left over go into a comment line.
printf '%s\n' "$head"
if (( pad == 1 )); then
printf '\n'
elif (( pad > 1 )); then
printf '#%*s\n' $(( pad - 2 )) ''
fi
printf '%s' "$body"
}
# _mca_steam_is_stub <script>
_mca_steam_is_stub() {
grep -qF -- "$MCA_STEAM_STUB" "$1" 2>/dev/null
}
# _mca_steam_text <script> <flags>
# The patched script on stdout: from the comments if they can pay for the
# flags, as a stub if not. Fails when neither keeps the size.
_mca_steam_text() {
local script="$1" flags="$2" out
if ! { out="$(_mca_steam_build "$script" "$flags")" \
&& _mca_steam_same_size "$script" "$out"; }
then
out="$(_mca_steam_stub "$script" "$flags")" \
&& _mca_steam_same_size "$script" "$out" || return 1
fi
printf '%s' "$out"
}
# _mca_steam_write <script> <backup> <content>
# Writes the patched script. <backup> is the untouched copy kept for undoing.
_mca_steam_write() {
local script="$1" backup="$2" content="$3"
# A stub needs Steam's script next to it before it can start anything.
if [[ $content == *"$MCA_STEAM_STUB"* ]]; then
cp -p -- "$backup" "$script$MCA_STEAM_ORIG" 2>/dev/null || return 1
chmod +x -- "$script$MCA_STEAM_ORIG" 2>/dev/null || true
fi
if mca_write_if_changed "$script" "$content"$'\n'; then
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
fi
chmod +x -- "$script" 2>/dev/null || true
# Steam looks at when the file was last written as well as at how big
# it is, and the copy taken before the edit still carries the original
# timestamp. Putting it back costs nothing and removes the other half
# of what the client would notice.
touch -r "$backup" -- "$script" 2>/dev/null || true
}
# _mca_steam_refresh_backup <script> # _mca_steam_refresh_backup <script>
# A client update brings a new version of the script, and the copy kept from # A client update brings a new version of the script, and the copy kept from
# before it is no longer what putting it back means. Only ever done while the # before it is no longer what putting it back means. Only ever done while the
@@ -195,15 +286,18 @@ _mca_steam_settle() {
# Nothing to compare against, so nothing can be said about the length. # Nothing to compare against, so nothing can be said about the length.
[[ -f $copy ]] || return 0 [[ -f $copy ]] || return 0
# Already the length Steam expects: the patch is a good one. # Already the length Steam expects: the patch is a good one. Unless it is
(( $(wc -c < "$script") == $(wc -c < "$copy") )) && return 0 # a stub that lost Steam's script next to it, and would start nothing.
if (( $(wc -c < "$script") == $(wc -c < "$copy") )); then
if out="$(_mca_steam_build "$copy" "$flags")" && _mca_steam_same_size "$copy" "$out"; then if _mca_steam_is_stub "$script" && [[ ! -f $script$MCA_STEAM_ORIG ]]; then
if mca_write_if_changed "$script" "$out"$'\n'; then cp -p -- "$copy" "$script$MCA_STEAM_ORIG" 2>/dev/null || true
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
fi fi
chmod +x -- "$script" 2>/dev/null || true return 0
touch -r "$copy" -- "$script" 2>/dev/null || true fi
if out="$(_mca_steam_text "$copy" "$flags")" \
&& _mca_steam_write "$script" "$copy" "$out"
then
mca_ledger_add steam "$script" "$name" mca_ledger_add steam "$script" "$name"
return 0 return 0
fi fi
@@ -224,41 +318,31 @@ mca_steam_apply() {
flags="$(mca_flags)" flags="$(mca_flags)"
while IFS= read -r root; do while IFS= read -r root; do
script="$(mca_steam_script "$root")" || continue while IFS= read -r script; do
found=1 found=1
if mca_steam_script_patched "$script"; then if mca_steam_script_patched "$script"; then
_mca_steam_settle "$script" "$flags" _mca_steam_settle "$script" "$flags"
continue continue
fi fi
_mca_steam_refresh_backup "$script" _mca_steam_refresh_backup "$script"
# Either the comments can pay for the flag or nothing is written. # Either the file keeps its length or nothing is written. A
# A longer file is one Steam reinstalls itself over; a client update # longer file is one Steam reinstalls itself over; a client
# that changed how the helper is started leaves no line to patch at # update that changed how the helper is started leaves no line
# all. Both end here, and this script is what starts Steam's entire # to patch at all. Both end here, and this script is what starts
# interface, so leaving it alone is the only safe answer to either. # Steam's entire interface, so leaving it alone is the only safe
if ! { out="$(_mca_steam_build "$script" "$flags")" \ # answer to either.
&& _mca_steam_same_size "$script" "$out"; } if ! out="$(_mca_steam_text "$script" "$flags")"; then
then mca_note "$(mca_msg "Steam starts its interface in a way this version cannot change without Steam noticing; leaving it alone.")"
mca_note "$(mca_msg "Steam starts its interface in a way this version cannot change without Steam noticing; leaving it alone.")" continue
continue fi
fi
backup="$(mca_backup "$script")" || continue backup="$(mca_backup "$script")" || continue
if mca_write_if_changed "$script" "$out"$'\n'; then _mca_steam_write "$script" "$MCA_BACKUPDIR/$backup" "$out" || continue
MCA_CHANGES=$(( MCA_CHANGES + 1 )) mca_ledger_add steam "$script" "$backup"
fi done < <(mca_steam_scripts "$root")
chmod +x -- "$script" 2>/dev/null || true
# Steam looks at when the file was last written as well as at how big
# it is, and the copy taken before the edit still carries the original
# timestamp. Putting it back costs nothing and removes the other half
# of what the client would notice.
touch -r "$MCA_BACKUPDIR/$backup" -- "$script" 2>/dev/null || true
mca_ledger_add steam "$script" "$backup"
done < <(mca_steam_roots) done < <(mca_steam_roots)
(( found )) (( found ))
@@ -266,7 +350,9 @@ mca_steam_apply() {
# mca_steam_revert <script> <backup name> # mca_steam_revert <script> <backup name>
mca_steam_revert() { mca_steam_revert() {
local script="$1" backup="$2" local script="$1" backup="$2" orig="$1$MCA_STEAM_ORIG" stub=1 tmp
_mca_steam_is_stub "$script" || stub=0
if [[ -n $backup && -f "$MCA_BACKUPDIR/$backup" ]]; then if [[ -n $backup && -f "$MCA_BACKUPDIR/$backup" ]]; then
if [[ -e $script ]]; then if [[ -e $script ]]; then
@@ -274,14 +360,21 @@ mca_steam_revert() {
chmod +x -- "$script" 2>/dev/null || true chmod +x -- "$script" 2>/dev/null || true
fi fi
rm -f -- "$MCA_BACKUPDIR/$backup" rm -f -- "$MCA_BACKUPDIR/$backup"
elif (( stub )) && [[ -f $orig ]]; then
# No backup, but a stub keeps Steam's own script right next to it.
cp -p -- "$orig" "$script" 2>/dev/null || return 1
else
# No backup to fall back on, so take the flags back out of the exec line.
[[ -f $script ]] || return 1
tmp="$(mktemp "${script}.XXXXXX")" || return 1
sed "s| $MCA_FLAG||g" "$script" > "$tmp" && mv -f "$tmp" "$script" || { rm -f "$tmp"; return 1; }
chmod +x -- "$script" 2>/dev/null || true
return 0 return 0
fi fi
# No backup to fall back on, so take the flags back out of the exec line. # The copy goes once the stub that ran it is gone.
[[ -f $script ]] || return 1 if (( stub )) && ! _mca_steam_is_stub "$script"; then
local tmp rm -f -- "$orig"
tmp="$(mktemp "${script}.XXXXXX")" || return 1 fi
sed "s| $MCA_FLAG||g" "$script" > "$tmp" && mv -f "$tmp" "$script" || { rm -f "$tmp"; return 1; }
chmod +x -- "$script" 2>/dev/null || true
return 0 return 0
} }