Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de6cf98dcc | ||
|
|
95d602e1c4 | ||
|
|
9b6c64bd97 | ||
|
|
8a7e67b1f9 | ||
|
|
c0fca75cbc | ||
|
|
15a7cb9cf9 | ||
|
|
0234e81811 | ||
|
|
6f62c7f21c | ||
|
|
3c000b8a6e | ||
|
|
0f9b041b20 | ||
|
|
98f6ac9c48 | ||
|
|
9861d24c2a | ||
|
|
f74fc3e8ca | ||
|
|
09b2c49eb3 | ||
|
|
ab62d2a3cf | ||
|
|
fa55571118 | ||
|
|
a516cf8f59 | ||
|
|
074a654597 | ||
|
|
472cf5382c | ||
|
|
82afefe5f8 | ||
|
|
677660c1c6 | ||
|
|
e9311431f8 | ||
|
|
e5d2481d6d | ||
|
|
fc9dae95f9 | ||
|
|
dd0671440d | ||
|
|
0b1b33ea84 | ||
|
|
e3278ecf27 | ||
|
|
2401855b70 | ||
|
|
da291cefa5 | ||
|
|
e2e05748f7 | ||
|
|
c20ea98de4 | ||
|
|
d0d977ff56 | ||
|
|
cf48fc47df | ||
|
|
335d4cc9ec | ||
|
|
c7222d22bd | ||
|
|
4fffba005d | ||
|
|
b93d0f5f1e | ||
|
|
d39e669743 | ||
|
|
f9826edc24 | ||
|
|
5e645acbd0 | ||
|
|
1042639a08 | ||
|
|
ab8c6477cb | ||
|
|
0fbe8bdb39 | ||
|
|
a992efb0cb | ||
|
|
b97ccf0379 | ||
|
|
7a446364dc | ||
|
|
449a20ce9b | ||
|
|
ae4e8b9294 | ||
|
|
b4fa0edf28 | ||
|
|
e09b145f3a | ||
|
|
004094ef78 | ||
|
|
0c4e7645d0 | ||
|
|
c777e8280d | ||
|
|
2ea64d0603 | ||
|
|
dcdf2c1df3 | ||
|
|
c1841ec169 | ||
|
|
e1c77f049f | ||
|
|
fa624380b1 | ||
|
|
d8900ef80f | ||
|
|
46e63e9faa | ||
|
|
af82ffec78 | ||
|
|
b7e0ff6fd1 | ||
|
|
ced6f57dfd | ||
|
|
21e29d99c3 | ||
|
|
c31c7b92e5 | ||
|
|
0bbf0cbd9f | ||
|
|
70f6748f3c | ||
|
|
aaa41f0384 | ||
|
|
a053b4e555 | ||
|
|
36498f2acf | ||
|
|
bd84c6645a | ||
|
|
f42d80598f | ||
|
|
4004023297 | ||
|
|
9896260636 | ||
|
|
5b1fc84710 | ||
|
|
d3c82fdff8 |
No files matched your search
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
@@ -0,0 +1 @@
|
||||
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.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,23 @@
|
||||
name: 🧩 App does not work
|
||||
description: Autoscroll does not work in an app. Just tell us which one.
|
||||
title: 'App: '
|
||||
labels:
|
||||
- app
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Just add the app's name to the title. The rest is optional.
|
||||
- type: dropdown
|
||||
id: install
|
||||
attributes:
|
||||
label: How is it installed?
|
||||
description: Skip it if you are not sure.
|
||||
options:
|
||||
- Native package
|
||||
- Flatpak
|
||||
- Snap
|
||||
- AppImage
|
||||
- type: textarea
|
||||
id: more
|
||||
attributes:
|
||||
label: More info
|
||||
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
Executable
+136
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds the GitHub release notes for a tag, the way big projects such as
|
||||
# Immich lay them out: the hand-written entry from CHANGELOG.md (a welcome,
|
||||
# the highlights), a support section, then every commit since the last
|
||||
# release, sorted by kind, and a link to the full changelog.
|
||||
#
|
||||
# .github/release-notes.sh v1.2.0 the notes
|
||||
# .github/release-notes.sh --title v1.2.0 the title (checks the entry exists)
|
||||
#
|
||||
# Commit authors come from the GitHub API through gh. Without it the list
|
||||
# goes without them.
|
||||
|
||||
set -euo pipefail
|
||||
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
|
||||
|
||||
title=0
|
||||
if [[ ${1:-} == --title ]]; then
|
||||
title=1
|
||||
shift
|
||||
fi
|
||||
tag="${1:?usage: release-notes.sh [--title] vX.Y.Z}"
|
||||
|
||||
if ! grep -qx "## $tag" CHANGELOG.md; then
|
||||
echo "CHANGELOG.md has no entry for $tag. Add \"## $tag\" first." >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( title )); then
|
||||
printf '%s\n' "$tag"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
repo="${GITHUB_REPOSITORY:-$(git remote get-url origin | sed -E 's#^.*github\.com[:/]##; s#\.git$##')}"
|
||||
name="${repo#*/}"
|
||||
|
||||
# The entry: up to the next one, without the date line (GitHub shows the
|
||||
# date), one heading level up, outside code blocks.
|
||||
entry="$(awk -v h="## $tag" '
|
||||
$0 == h { on = 1; next }
|
||||
on && /^## / { exit }
|
||||
!on { next }
|
||||
/^```/ { code = !code }
|
||||
!code && /^_[0-9]{4}-[0-9]{2}-[0-9]{2}_$/ { next }
|
||||
!code && /^###/ { sub(/^#/, "") }
|
||||
{ print }
|
||||
' CHANGELOG.md | sed -e '/./,$!d')"
|
||||
|
||||
# The commits: from the last release, or from the start. A tag that does not
|
||||
# exist yet is a preview of what HEAD would become.
|
||||
if git rev-parse -q --verify "refs/tags/$tag" > /dev/null; then
|
||||
target="$tag"
|
||||
prev="$(git describe --tags --abbrev=0 --match 'v[0-9]*' "$tag^" 2>/dev/null || true)"
|
||||
else
|
||||
target="$(git rev-parse HEAD)"
|
||||
prev="$(git describe --tags --abbrev=0 --match 'v[0-9]*' HEAD 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
declare -A login=()
|
||||
if command -v gh > /dev/null; then
|
||||
if [[ -n $prev ]]; then
|
||||
api=(api "repos/$repo/compare/$prev...$target" --jq '.commits[] | [.sha, (.author.login // "")] | @tsv')
|
||||
else
|
||||
api=(api --paginate "repos/$repo/commits?sha=$target&per_page=100" --jq '.[] | [.sha, (.author.login // "")] | @tsv')
|
||||
fi
|
||||
while IFS=$'\t' read -r sha who; do
|
||||
[[ -n $who ]] && login[$sha]="$who"
|
||||
done < <(gh "${api[@]}" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
declare -A list=()
|
||||
count_maint=0
|
||||
while IFS=$'\t' read -r sha subject body; do
|
||||
# Version bumps say nothing a reader needs.
|
||||
[[ $subject =~ ^(chore:\ )?($name\ )?v?[0-9]+\.[0-9]+\.[0-9]+$ ]] && continue
|
||||
|
||||
case "$subject" in
|
||||
*!:*) kind=breaking ;;
|
||||
feat:* | feat\(*) kind=feat ;;
|
||||
fix:* | fix\(*) kind=fix ;;
|
||||
perf:* | perf\(*) kind=enh ;;
|
||||
docs:* | docs\(*) kind=docs ;;
|
||||
chore* | ci:* | ci\(* | build* | refactor* | test* | style*) kind=maint ;;
|
||||
# Older commits without a prefix, sorted by what they say.
|
||||
*README* | *readme* | *Readme*) kind=docs ;;
|
||||
Add\ * | Put\ * | Introduce\ *) kind=feat ;;
|
||||
Fix\ * | Repair\ * | Recover\ * | Stop\ *) kind=fix ;;
|
||||
*\ *) kind=enh ;;
|
||||
*) kind=maint ;;
|
||||
esac
|
||||
[[ $body == *"BREAKING CHANGE"* ]] && kind=breaking
|
||||
[[ $subject == "Initial commit" ]] && kind=maint
|
||||
|
||||
line="* $subject"
|
||||
[[ -n ${login[$sha]:-} ]] && line+=" by @${login[$sha]}"
|
||||
line+=" in https://github.com/$repo/commit/$sha"
|
||||
list[$kind]+="$line"$'\n'
|
||||
[[ $kind == maint ]] && count_maint=$((count_maint + 1))
|
||||
done < <(git log --reverse --no-merges --format='%H%x09%s%x09%b%x1e' "${prev:+$prev..}$target" | tr '\n\036' ' \n' | sed 's/^ //')
|
||||
|
||||
changes=''
|
||||
for pair in "breaking:🚨 Breaking Changes" "feat:🚀 Features" "enh:🌟 Enhancements" "fix:🐛 Bug fixes" "docs:📚 Documentation"; do
|
||||
kind="${pair%%:*}"
|
||||
[[ -n ${list[$kind]:-} ]] || continue
|
||||
changes+="### ${pair#*:}"$'\n'"${list[$kind]}"
|
||||
done
|
||||
if [[ -n ${list[maint]:-} ]]; then
|
||||
changes+=$'\n'"<details><summary>🧰 Maintenance ($count_maint)</summary>"$'\n\n'"${list[maint]}"$'\n'"</details>"$'\n'
|
||||
fi
|
||||
|
||||
if [[ -n $prev ]]; then
|
||||
full="**Full Changelog**: https://github.com/$repo/compare/$prev...$tag"
|
||||
else
|
||||
full="**Full Changelog**: https://github.com/$repo/commits/$tag"
|
||||
fi
|
||||
|
||||
# A release with highlights is a big one: it gets a heading and the support
|
||||
# section, as in Immich. A patch is a sentence or two and the list.
|
||||
if grep -q '^## Highlights$' <<< "$entry"; then
|
||||
cat <<EOF
|
||||
# 🚀 $name $tag
|
||||
|
||||
$entry
|
||||
|
||||
## ☕ Support $name
|
||||
|
||||
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://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>
|
||||
|
||||
----
|
||||
|
||||
EOF
|
||||
else
|
||||
printf '%s\n\n' "$entry"
|
||||
fi
|
||||
printf "## What's Changed\n%s\n\n%s\n" "$changes" "$full"
|
||||
@@ -0,0 +1,28 @@
|
||||
name: check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: syntax and shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
# The Makefile skips whatever is missing rather than failing, so the
|
||||
# tools it looks for have to be here or the check checks less than it
|
||||
# appears to.
|
||||
- name: Install the tools the Makefile looks for
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
shellcheck gettext scdoc
|
||||
|
||||
- run: make check
|
||||
|
||||
- name: Build the catalogs and the man page
|
||||
run: make build
|
||||
@@ -0,0 +1,31 @@
|
||||
name: issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
# The "fixed" and "not chromium" labels answer and close the issue.
|
||||
close:
|
||||
name: close on label
|
||||
if: >-
|
||||
github.event.issue.state == 'open'
|
||||
&& (github.event.label.name == 'fixed' || github.event.label.name == 'not chromium')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
LABEL: ${{ github.event.label.name }}
|
||||
NUMBER: ${{ github.event.issue.number }}
|
||||
run: |
|
||||
if [ "$LABEL" = fixed ]; then
|
||||
reason=completed
|
||||
comment='Fixed, thanks for the report! The fix comes with the next release.'
|
||||
else
|
||||
reason='not planned'
|
||||
comment='Thanks for the report! This app is not built on Chromium, Electron or CEF. So it has no autoscroll built in that this tool could turn on.'
|
||||
fi
|
||||
gh issue close "$NUMBER" --repo "$GITHUB_REPOSITORY" --reason "$reason" --comment "$comment"
|
||||
@@ -0,0 +1,264 @@
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: >-
|
||||
Build the repositories with a throwaway key and install from them,
|
||||
without publishing anything. This is how the release path gets
|
||||
exercised without cutting a tag.
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
deb:
|
||||
name: Debian package
|
||||
runs-on: ubuntu-latest
|
||||
container: debian:stable
|
||||
steps:
|
||||
- name: Install the build tools
|
||||
run: |
|
||||
apt-get update -qq
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates git make gettext scdoc dpkg-dev
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Check the tag against the Makefile
|
||||
run: packaging/check-version.sh "${{ github.ref_name }}"
|
||||
|
||||
- name: Check CHANGELOG.md has this release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: .github/release-notes.sh --title "${{ github.ref_name }}"
|
||||
|
||||
- run: packaging/build-deb.sh
|
||||
|
||||
- name: Look inside what was built
|
||||
run: |
|
||||
dpkg-deb --info dist/*.deb
|
||||
dpkg-deb --contents dist/*.deb
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: deb
|
||||
path: dist/*.deb
|
||||
if-no-files-found: error
|
||||
|
||||
rpm:
|
||||
name: RPM package
|
||||
runs-on: ubuntu-latest
|
||||
container: fedora:latest
|
||||
steps:
|
||||
- name: Install the build tools
|
||||
run: |
|
||||
dnf install -y --setopt=install_weak_deps=False \
|
||||
git make gettext scdoc tar rpm-build rpm-sign systemd-rpm-macros
|
||||
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Check the tag against the Makefile
|
||||
run: packaging/check-version.sh "${{ github.ref_name }}"
|
||||
|
||||
- run: packaging/build-rpm.sh
|
||||
|
||||
# Signed here rather than alongside the APT repository, because this is
|
||||
# the one place with a native rpm-sign. A dry run signs with a key it
|
||||
# makes on the spot, so the command itself is still exercised.
|
||||
- name: Sign the package
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
run: |
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
gpg --batch --passphrase '' --quick-generate-key \
|
||||
'dry run <dry-run@example.invalid>' rsa2048 sign never
|
||||
elif [ -n "${GPG_PRIVATE_KEY:-}" ]; then
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
else
|
||||
echo "No GPG_PRIVATE_KEY secret; leaving the package unsigned."
|
||||
exit 0
|
||||
fi
|
||||
keyid="$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ { print $5; exit }')"
|
||||
rpmsign --define "_gpg_name $keyid" --addsign dist/*.rpm
|
||||
gpg --armor --export "$keyid" > dist/rpm-signer.asc
|
||||
rpm --import dist/rpm-signer.asc
|
||||
rpm --checksig dist/*.rpm
|
||||
|
||||
- name: Look inside what was built
|
||||
run: |
|
||||
rpm -qip dist/*.rpm
|
||||
rpm -qlp dist/*.rpm
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: rpm
|
||||
path: |
|
||||
dist/*.rpm
|
||||
dist/rpm-signer.asc
|
||||
if-no-files-found: error
|
||||
|
||||
publish:
|
||||
name: Release and repositories
|
||||
needs: [deb, rpm]
|
||||
if: startsWith(github.ref, 'refs/tags/v') || inputs.dry_run
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
# All tags, for the link to the changes since the last release.
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: incoming
|
||||
merge-multiple: true
|
||||
|
||||
- name: Attach the packages to the release
|
||||
if: ${{ !inputs.dry_run }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
.github/release-notes.sh "${{ github.ref_name }}" > notes.md
|
||||
gh release create "${{ github.ref_name }}" \
|
||||
--title "$(.github/release-notes.sh --title "${{ github.ref_name }}")" \
|
||||
--notes-file notes.md \
|
||||
incoming/*.deb incoming/*.rpm \
|
||||
|| gh release upload "${{ github.ref_name }}" \
|
||||
incoming/*.deb incoming/*.rpm --clobber
|
||||
|
||||
- name: Install the repository tools
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
dpkg-dev apt-utils createrepo-c
|
||||
|
||||
- name: Get a signing key
|
||||
id: key
|
||||
env:
|
||||
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
run: |
|
||||
if [ "$DRY_RUN" = "true" ]; then
|
||||
gpg --batch --passphrase '' --quick-generate-key \
|
||||
'dry run <dry-run@example.invalid>' rsa2048 sign never
|
||||
elif [ -n "${GPG_PRIVATE_KEY:-}" ]; then
|
||||
printf '%s' "$GPG_PRIVATE_KEY" | gpg --batch --import
|
||||
else
|
||||
echo "present=no" >> "$GITHUB_OUTPUT"
|
||||
echo "::warning::No GPG_PRIVATE_KEY secret, so the apt and dnf repositories were not updated. The packages are on the release."
|
||||
exit 0
|
||||
fi
|
||||
echo "present=yes" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Check out the published repositories
|
||||
if: steps.key.outputs.present == 'yes' && !inputs.dry_run
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: gh-pages
|
||||
path: pages
|
||||
continue-on-error: true
|
||||
|
||||
- name: Update the repositories
|
||||
if: steps.key.outputs.present == 'yes'
|
||||
run: |
|
||||
# First release, or a dry run: there is no branch to start from.
|
||||
if [ ! -d pages/.git ]; then
|
||||
rm -rf pages && mkdir pages
|
||||
git -C pages init -q -b gh-pages
|
||||
git -C pages remote add origin "https://github.com/${{ github.repository }}.git"
|
||||
fi
|
||||
rm -f incoming/rpm-signer.asc
|
||||
packaging/publish-repos.sh pages incoming
|
||||
|
||||
- name: Check that what was written can be verified
|
||||
if: steps.key.outputs.present == 'yes'
|
||||
run: |
|
||||
find pages -type f -not -path '*/.git/*' | sort
|
||||
echo '--- Release ---'; cat pages/deb/Release
|
||||
echo '--- Packages ---'; cat pages/deb/Packages
|
||||
gpg --verify pages/deb/InRelease
|
||||
gpg --verify pages/deb/Release.gpg pages/deb/Release
|
||||
gpg --verify pages/rpm/repodata/repomd.xml.asc pages/rpm/repodata/repomd.xml
|
||||
|
||||
# The proof that the repository works is apt reading it: the signature,
|
||||
# the index, the dependencies and the program that comes out the far end.
|
||||
- name: Install from the repository that was just built
|
||||
if: inputs.dry_run
|
||||
run: |
|
||||
sudo install -d -m 0755 /etc/apt/keyrings
|
||||
sudo gpg --dearmor -o /etc/apt/keyrings/middleclick-autoscroll.gpg < pages/KEY.gpg
|
||||
echo "deb [signed-by=/etc/apt/keyrings/middleclick-autoscroll.gpg] file://$PWD/pages/deb ./" \
|
||||
| sudo tee /etc/apt/sources.list.d/middleclick-autoscroll.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y middleclick-autoscroll
|
||||
middleclick-autoscroll --version
|
||||
middleclick-autoscroll list
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: inputs.dry_run
|
||||
with:
|
||||
name: pages
|
||||
path: pages
|
||||
include-hidden-files: true
|
||||
|
||||
- name: Push them
|
||||
if: steps.key.outputs.present == 'yes' && !inputs.dry_run
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
cd pages
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add -A
|
||||
git diff --quiet --cached && { echo "nothing changed"; exit 0; }
|
||||
git commit -q -m "Publish ${{ github.ref_name }}"
|
||||
git push "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git" HEAD:gh-pages
|
||||
|
||||
verify-dnf:
|
||||
name: Install from the RPM repository
|
||||
needs: publish
|
||||
if: inputs.dry_run
|
||||
runs-on: ubuntu-latest
|
||||
container: fedora:latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: pages
|
||||
path: pages
|
||||
|
||||
# A real run signs the package and the repository metadata with the one
|
||||
# key from the secret. A dry run has no secret, so each job made a key of
|
||||
# its own and both public halves are needed to check both signatures.
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: rpm
|
||||
path: signer
|
||||
|
||||
- name: Install from the repository that was just built
|
||||
run: |
|
||||
rpm --import pages/KEY.gpg
|
||||
rpm --import signer/rpm-signer.asc
|
||||
cat > /etc/yum.repos.d/middleclick-autoscroll.repo <<EOF
|
||||
[middleclick-autoscroll]
|
||||
name=middleclick-autoscroll
|
||||
baseurl=file://$PWD/pages/rpm
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=file://$PWD/pages/KEY.gpg
|
||||
EOF
|
||||
# util-linux is for runuser below; the base image does not carry it,
|
||||
# and util-linux-core is not the half that has it.
|
||||
dnf install -y middleclick-autoscroll util-linux
|
||||
|
||||
# As somebody, not as root: running it as root is refused, which is
|
||||
# the point of it, and a container is root by default.
|
||||
useradd -m tester
|
||||
runuser -u tester -- middleclick-autoscroll --version
|
||||
runuser -u tester -- middleclick-autoscroll list
|
||||
@@ -0,0 +1,6 @@
|
||||
# build artifacts
|
||||
po/*.mo
|
||||
doc/*.1
|
||||
|
||||
# packages
|
||||
dist/
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
# Changelog
|
||||
|
||||
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.7.0
|
||||
|
||||
_2026-10-01_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.7.0`! Firefox gets autoscroll now, and so do LibreWolf, Zen, Floorp, Waterfox and Thunderbird. It starts working after your next login, or right away with **Re-apply everything**. Then restart Firefox.
|
||||
|
||||
### Highlights
|
||||
|
||||
- Autoscroll in Firefox
|
||||
- LibreWolf, Zen, Floorp, Waterfox and Thunderbird too
|
||||
- Flatpak and snap included
|
||||
- New Firefox profiles get it on their own
|
||||
|
||||
## 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
|
||||
|
||||
_2026-09-24_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.6.0`! The menu got quicker and simpler: search your apps, fewer settings, and no more waiting for a key. It now speaks eleven languages. The ten new ones are machine translated, so corrections are very welcome.
|
||||
|
||||
<p align="center">
|
||||
<img width="620" alt="The applications list in Konsole, with a search field on top" src="https://raw.githubusercontent.com/LoonixTools/middleclick-autoscroll/v1.6.0/res/screenshots/applications.png">
|
||||
</p>
|
||||
|
||||
### Highlights
|
||||
|
||||
- Search field in the applications list
|
||||
- Long lists scroll
|
||||
- Faster start
|
||||
- Simpler settings
|
||||
- Spotify in the applications list
|
||||
- Results show up in the menu
|
||||
- Ten more languages
|
||||
|
||||
## v1.5.4
|
||||
|
||||
_2026-09-24_
|
||||
|
||||
A small patch. For some of you Discord was still missing after `v1.5.3`, because an old result from before the update kept it out. Every update now starts with a fresh scan, so Discord shows up again after your next login, or right away with **Re-apply everything**.
|
||||
|
||||
## v1.5.3
|
||||
|
||||
_2026-09-24_
|
||||
|
||||
A small patch. Discord changed the way it installs itself and slipped out of the list, so autoscroll never reached it. It is back.
|
||||
|
||||
## v1.5.2
|
||||
|
||||
_2026-09-24_
|
||||
|
||||
A small patch. The menu now says "1 minute ago" instead of "1 minutes ago".
|
||||
|
||||
## v1.5.1
|
||||
|
||||
_2026-09-15_
|
||||
|
||||
A small patch. Autoscroll now works in Brave, which has its own name for it. AppImages are recognised too: an Electron AppImage gets autoscroll like any other app, and one that cannot autoscroll at all, like Modrinth, is left out of the list.
|
||||
|
||||
## v1.5.0
|
||||
|
||||
_2026-09-08_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.5.0`! A middle click now scrolls everywhere instead of pasting text.
|
||||
|
||||
<p align="center">
|
||||
<img width="620" alt="The middleclick-autoscroll menu in Konsole, with middle-click paste off" src="https://raw.githubusercontent.com/LoonixTools/middleclick-autoscroll/22bd4f2a3dd89c4cb90fb497c69762b5df7dc6e7/res/screenshots/menu.png">
|
||||
</p>
|
||||
|
||||
### Highlights
|
||||
|
||||
- Middle-click paste is off
|
||||
|
||||
## v1.4.0
|
||||
|
||||
_2026-09-06_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.4.0`! This one is all about Steam.
|
||||
|
||||
### Highlights
|
||||
|
||||
- Steam stops reinstalling itself
|
||||
- Scripts that start Steam
|
||||
|
||||
## v1.3.0
|
||||
|
||||
_2026-09-04_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.3.0`! Browsers look clean again, and Helium joins the list.
|
||||
|
||||
### Highlights
|
||||
|
||||
- No more warning bar in browsers
|
||||
- Autoscroll in Helium
|
||||
|
||||
## v1.2.0
|
||||
|
||||
_2026-09-01_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.2.0`! Steam keeps its autoscroll now, however you start it.
|
||||
|
||||
### Highlights
|
||||
|
||||
- Steam keeps autoscroll
|
||||
- More ways to start Steam
|
||||
|
||||
## v1.1.0
|
||||
|
||||
_2026-08-24_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.1.0`! It now works far beyond Arch, with packages that keep themselves up to date.
|
||||
|
||||
### Highlights
|
||||
|
||||
- Debian, Ubuntu, Fedora and openSUSE
|
||||
- Packages that update with your system
|
||||
- Steam fixes
|
||||
@@ -0,0 +1,76 @@
|
||||
# CLAUDE.md
|
||||
|
||||
## Style
|
||||
|
||||
- Keep everything short: replies, explanations, comments, docs.
|
||||
- Use simple English: short sentences, common words.
|
||||
- Avoid em dashes (—). Do not just swap them for "-" either. Rewrite the sentence instead, for
|
||||
example with a comma, a colon, brackets or two sentences.
|
||||
|
||||
## Commits
|
||||
|
||||
- English only.
|
||||
- Conventional Commits prefix: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `ci:`, `build:`.
|
||||
- Subject as short as possible. Imperative, lowercase, no trailing period.
|
||||
- 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.
|
||||
- Do not commit or push before I have checked the changes locally and said they are fine. Then
|
||||
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
|
||||
|
||||
Releases look like the ones of big projects such as Immich. `.github/release-notes.sh <tag>` builds
|
||||
the notes: the entry from `CHANGELOG.md` (welcome and highlights), a support section, every commit
|
||||
since the last release sorted by its prefix (`feat`, `fix`, `docs`, ...) with author and link, and
|
||||
the full changelog link. So commit subjects end up in public: keep them clear.
|
||||
|
||||
1. Read `git log <last tag>..HEAD` and pick the version: only fixes → patch, something new → minor,
|
||||
something that breaks or needs the user to act → major.
|
||||
2. Bump the version and add the entry at the top of `CHANGELOG.md`, in one commit (`chore: 1.4.0`).
|
||||
3. Push, then push the tag: `git tag v1.4.0 && git push origin main v1.4.0`. The `release` workflow
|
||||
builds the packages and creates the release. It stops before building when the entry is missing.
|
||||
4. PKGBUILDS picks up the new release for the AUR on its own.
|
||||
|
||||
A minor or major release (has `### Highlights`, gets a heading and the support section):
|
||||
|
||||
```markdown
|
||||
## v1.4.0
|
||||
|
||||
_2026-09-24_
|
||||
|
||||
Welcome to middleclick-autoscroll `v1.4.0`! One or two sentences on what this release is about.
|
||||
|
||||
<p align="center">
|
||||
<img width="480" alt="What the picture shows" src="https://raw.githubusercontent.com/LoonixTools/middleclick-autoscroll/v1.4.0/<path>">
|
||||
</p>
|
||||
|
||||
### 🚨 Breaking changes
|
||||
|
||||
- Only if there are any: what changed, and what the user has to do.
|
||||
|
||||
### Highlights
|
||||
|
||||
- First highlight, a few words
|
||||
- Second highlight
|
||||
```
|
||||
|
||||
A picture under the welcome is optional. To show the menu or another screen of the program, use a
|
||||
real screenshot of it running in Konsole, in English. Never a text copy of the screen. The same goes
|
||||
for the README. The highlights stay a plain list: no heading or text per highlight, the list of
|
||||
commits explains the rest.
|
||||
|
||||
A patch release is just a sentence or two, for example: "A small patch. The menu no longer closes
|
||||
when you press Enter." The list of commits follows on its own.
|
||||
|
||||
- Write for users: what they notice, not how the code does it. Friendly and simple.
|
||||
- Commands and settings they type go in backticks, buttons and labels in bold.
|
||||
- To change an old release: edit its entry, commit, then
|
||||
`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,29 +0,0 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBGqMCO8BEAC6rNuAIigcAQpbcTaGd2iJSP2hhaFQbE4YUxwPjxsidrkEOF0c
|
||||
cxQq0FTmtBkHRc8DfnPF5/oO0LtL9lokKTVRdzbvm5W2/DM4OwDu3oT/vYlT/yys
|
||||
shYEIylOZHj26+Gjrn9xKoQ7A920pu0P0mt5gfU01y4FEz4moydj6SUvApTWvMUI
|
||||
9TvPDwKngeulMUst6J+QftjZBZrDqj/L+fH6c+OCGU4sAOBb9nm6/7wp44byFcDV
|
||||
TtEdLDlH3CQ8qW9zUN8g1c5/3Au/eHA1cYojNYzZhhhN4g4mGgYPht9CNxaCQkir
|
||||
sr4EnTCqWfkgUHC2ZAQ7HTyq/uXyXY7pwlYy50SJXvxN88sZb7lRWqLby1jDvLjW
|
||||
fL0Zu1gmq1UXGln+YdhS3iHDrlwfyfGNZXU5NWZubWtbe1v4CUC4Em/chwMmrk9E
|
||||
34xF86AQ0PcMuvqomdNNg0eFTfPw/jU0WHLQN49SIzTcH7BFj7bCnY1g9AdmfySH
|
||||
8ENPh4ZSLFOykMQChCYK359saOI+B3yqSrE9UI8UaDH638Hu7IRezvlMXBqV5g4K
|
||||
9+Zes+5pIzYJuvweUUrFYrxo0RyWpxticGdqZFYeBbDRJf26oqReX8eua2L3ONL1
|
||||
MnoeBZF0Vmftg/GA8MqSttCq98eErJb5+hdAgsxAR8gJYIsdniG34ira9wARAQAB
|
||||
tDltaWRkbGVjbGljay1hdXRvc2Nyb2xsIHJlcG9zaXRvcnkgPGZlbGl0ZW5kb3l0
|
||||
QGdtYWlsLmNvbT6JAk4EEwEKADgWIQRjg8j0Nzst6xSS4qxVo8bfKRRMeQUCaowI
|
||||
7wIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRBVo8bfKRRMeSBrD/0fL1Mv
|
||||
pTIUixusX5sj9yvTuE6hO4XlRD1GymS4McUibFGwmS1FtLZ4REV1B37H4nMDKNJk
|
||||
vl2Mr68KEWMbI/MtUKk7vgoIIG/CTcSObl2Az41xQzMXklMV+3/nV2RS4DvPIFmD
|
||||
x1Lm/zDhEFTBN0Drasjx3DuvJU4k6ySKiO3JQpVpK6n5ACe3JC1rVaadf/i65e4J
|
||||
qeze+Ig5qOq0Dy4ii+3ixDH7sWhVDrTuqzCwcocV+f7TrZYAHGXYkySF6slQGCpQ
|
||||
Xre9ZIBTaoRNGpuBaASrVcuVbGl2SddGHfksothMarC3sCKxsDbwf+SUKLi7PjxR
|
||||
sE73Kdwv5ErV+JeBkwRv+1UvrxTqrqLZvpBw3aWtg/d0HV9wyPdF8M9/tKRrt3BW
|
||||
qYPY+3vmyuTMtFpiM5VSimHBzMTRrNtUo2w/V+BL+BFduZKFIa8jgrwC3QBYTmHV
|
||||
VU0/7HYeHkc62CIUcj+mV4UebsydQwerXz4fZgn5UcE2OcuravPu1QiqUV240J/e
|
||||
XJMpHgbGXLNrL8QE+x6e4s1irkOccehyuKUydgjTYsTzm1gyfvkM0B4fSFb9BQFF
|
||||
aCrQsUYZ7Hwsu1Vj3k2u6uchsXsM6WBsa18ysnGwxUg7Klapuhwfpvcwav3VyvYR
|
||||
rhgmwPGHTSHGNmREGwT7uE8Nk8tB0AGOdbbzBg==
|
||||
=Yng/
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
@@ -0,0 +1,137 @@
|
||||
# middleclick-autoscroll: build and install
|
||||
#
|
||||
# Everything here is plain shell; "building" only means compiling the gettext
|
||||
# catalogs and rendering the man page. Both targets degrade to a no-op when
|
||||
# msgfmt/scdoc are missing, so the tree stays usable for development without
|
||||
# the build dependencies installed.
|
||||
|
||||
# Overridable so a packager can pass the version it is actually building
|
||||
# (`make VERSION=$pkgver`). The literal below is the fallback for builds
|
||||
# straight from a checkout, and is what a release tag has to carry.
|
||||
VERSION ?= 1.7.0
|
||||
|
||||
PREFIX ?= /usr
|
||||
DESTDIR ?=
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
DATADIR ?= $(PREFIX)/share
|
||||
LIBDIR ?= $(DATADIR)/middleclick-autoscroll/lib
|
||||
LOCALEDIR ?= $(DATADIR)/locale
|
||||
MANDIR ?= $(DATADIR)/man
|
||||
|
||||
# Where systemd looks for user units. For a normal install into /usr this is
|
||||
# asked of systemd itself, because the answer is not the same everywhere. A
|
||||
# distribution that still keeps /lib separate from /usr/lib says so here. It is
|
||||
# only guessed at when there is no systemd installed to ask.
|
||||
#
|
||||
# A build with a prefix of its own keeps the units under that prefix instead,
|
||||
# and under share rather than lib. systemd searches both for a system prefix,
|
||||
# but only share for a home one: $XDG_DATA_HOME/systemd/user is a search path
|
||||
# and ~/.local/lib/systemd/user is not, so an install into ~/.local that put
|
||||
# the units in lib would leave the watcher impossible to enable. That prefix is
|
||||
# the one an atomic distribution leaves a user: there is no writing to /usr on
|
||||
# Bazzite or Silverblue without layering a package and rebooting.
|
||||
ifeq ($(PREFIX),/usr)
|
||||
USERUNITDIR ?= $(shell pkg-config --variable=systemduserunitdir systemd 2>/dev/null || echo /usr/lib/systemd/user)
|
||||
else
|
||||
USERUNITDIR ?= $(DATADIR)/systemd/user
|
||||
endif
|
||||
|
||||
LINGUAS := cs de es fr it nl pl pt_BR ru tr uk
|
||||
MOFILES := $(patsubst %,po/%.mo,$(LINGUAS))
|
||||
MANPAGE := doc/middleclick-autoscroll.1
|
||||
|
||||
LIBS := $(wildcard src/lib/*.sh)
|
||||
|
||||
MSGFMT := $(shell command -v msgfmt 2>/dev/null)
|
||||
SCDOC := $(shell command -v scdoc 2>/dev/null)
|
||||
|
||||
.PHONY: all build install uninstall check clean version
|
||||
|
||||
all: build
|
||||
|
||||
build: $(MOFILES) $(MANPAGE)
|
||||
|
||||
# The one place the version is written down, for everything that has to agree
|
||||
# with it: the packaging scripts, and the release workflow checking that the
|
||||
# tag it was handed says the same thing.
|
||||
version:
|
||||
@echo $(VERSION)
|
||||
|
||||
po/%.mo: po/%.po
|
||||
ifdef MSGFMT
|
||||
$(MSGFMT) --check --output-file=$@ $<
|
||||
else
|
||||
@echo "msgfmt not found, skipping $@"
|
||||
endif
|
||||
|
||||
$(MANPAGE): doc/middleclick-autoscroll.1.scd
|
||||
ifdef SCDOC
|
||||
$(SCDOC) < $< > $@
|
||||
else
|
||||
@echo "scdoc not found, skipping $@"
|
||||
endif
|
||||
|
||||
# Syntax-check every shell file, and run shellcheck when it is available.
|
||||
check:
|
||||
@set -e; for f in src/middleclick-autoscroll $(LIBS); do \
|
||||
bash -n "$$f" && echo "ok $$f"; \
|
||||
done
|
||||
@if command -v shellcheck >/dev/null 2>&1; then \
|
||||
shellcheck -x -e SC1090,SC1091 src/middleclick-autoscroll $(LIBS); \
|
||||
echo "ok shellcheck"; \
|
||||
else \
|
||||
echo "shellcheck not found, skipped"; \
|
||||
fi
|
||||
@if command -v desktop-file-validate >/dev/null 2>&1; then \
|
||||
echo "ok desktop-file-validate (nothing to check)"; \
|
||||
fi
|
||||
|
||||
install: build
|
||||
# executable
|
||||
install -Dm755 src/middleclick-autoscroll \
|
||||
"$(DESTDIR)$(BINDIR)/middleclick-autoscroll"
|
||||
|
||||
# shell libraries
|
||||
install -d "$(DESTDIR)$(LIBDIR)"
|
||||
install -Dm644 -t "$(DESTDIR)$(LIBDIR)" $(LIBS)
|
||||
|
||||
# the version and the resolved paths are baked in at install time
|
||||
sed -i -e 's|@VERSION@|$(VERSION)|g' \
|
||||
-e 's|@LIBDIR@|$(LIBDIR)|g' \
|
||||
-e 's|@LOCALEDIR@|$(LOCALEDIR)|g' \
|
||||
"$(DESTDIR)$(BINDIR)/middleclick-autoscroll" \
|
||||
"$(DESTDIR)$(LIBDIR)"/*.sh
|
||||
|
||||
# user units: the watcher, and the one-shot it starts
|
||||
install -Dm644 res/systemd/middleclick-autoscroll.service \
|
||||
"$(DESTDIR)$(USERUNITDIR)/middleclick-autoscroll.service"
|
||||
install -Dm644 res/systemd/middleclick-autoscroll.path \
|
||||
"$(DESTDIR)$(USERUNITDIR)/middleclick-autoscroll.path"
|
||||
sed -i -e 's|@BINDIR@|$(BINDIR)|g' \
|
||||
"$(DESTDIR)$(USERUNITDIR)/middleclick-autoscroll.service"
|
||||
|
||||
# translations
|
||||
@for l in $(LINGUAS); do \
|
||||
if [ -f "po/$$l.mo" ]; then \
|
||||
install -Dm644 "po/$$l.mo" \
|
||||
"$(DESTDIR)$(LOCALEDIR)/$$l/LC_MESSAGES/middleclick-autoscroll.mo"; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# documentation
|
||||
@if [ -f $(MANPAGE) ]; then \
|
||||
install -Dm644 $(MANPAGE) "$(DESTDIR)$(MANDIR)/man1/middleclick-autoscroll.1"; \
|
||||
fi
|
||||
install -Dm644 README.md \
|
||||
"$(DESTDIR)$(DATADIR)/doc/middleclick-autoscroll/README.md"
|
||||
|
||||
uninstall:
|
||||
rm -f "$(DESTDIR)$(BINDIR)/middleclick-autoscroll"
|
||||
rm -rf "$(DESTDIR)$(DATADIR)/middleclick-autoscroll"
|
||||
rm -f "$(DESTDIR)$(USERUNITDIR)/middleclick-autoscroll.service"
|
||||
rm -f "$(DESTDIR)$(USERUNITDIR)/middleclick-autoscroll.path"
|
||||
rm -f "$(DESTDIR)$(MANDIR)/man1/middleclick-autoscroll.1"
|
||||
rm -rf "$(DESTDIR)$(DATADIR)/doc/middleclick-autoscroll"
|
||||
|
||||
clean:
|
||||
rm -f po/*.mo $(MANPAGE)
|
||||
@@ -0,0 +1,165 @@
|
||||
<p align="center">
|
||||
<img width="200" src="res/middleclick-autoscroll.svg" alt="middleclick-autoscroll">
|
||||
</p>
|
||||
|
||||
<h1 align="center">middleclick-autoscroll</h1>
|
||||
|
||||
<h3 align="center">Middle-click autoscroll in every app that supports it.</h3>
|
||||
|
||||
<p align="center">
|
||||
Firefox, Chromium browsers, Electron apps like Discord and Spotify, Steam and anything else built on Chromium.
|
||||
</p>
|
||||
|
||||
<h5 align="center">
|
||||
<a href="#install">Install</a> |
|
||||
<a href="#how-to-use">How to use</a> |
|
||||
<a href="https://github.com/LoonixTools/middleclick-autoscroll/issues">Report a bug</a>
|
||||
</h5>
|
||||
|
||||
<p align="center">
|
||||
<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>
|
||||
|
||||
## Install
|
||||
|
||||
<details>
|
||||
<summary><b>Arch</b></summary>
|
||||
|
||||
```bash
|
||||
yay -S middleclick-autoscroll
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Fedora</b></summary>
|
||||
|
||||
```bash
|
||||
sudo curl -fsSL -o /etc/yum.repos.d/middleclick-autoscroll.repo \
|
||||
https://loonixtools.github.io/middleclick-autoscroll/middleclick-autoscroll.repo
|
||||
sudo dnf install middleclick-autoscroll
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Bazzite</b></summary>
|
||||
|
||||
```bash
|
||||
sudo curl -fsSL -o /etc/yum.repos.d/middleclick-autoscroll.repo \
|
||||
https://loonixtools.github.io/middleclick-autoscroll/middleclick-autoscroll.repo
|
||||
sudo rpm-ostree install middleclick-autoscroll
|
||||
systemctl reboot
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Debian</b>, Ubuntu</summary>
|
||||
|
||||
```bash
|
||||
sudo install -d -m 0755 /etc/apt/keyrings
|
||||
curl -fsSL https://loonixtools.github.io/middleclick-autoscroll/KEY.gpg \
|
||||
| sudo gpg --dearmor -o /etc/apt/keyrings/middleclick-autoscroll.gpg
|
||||
echo "deb [signed-by=/etc/apt/keyrings/middleclick-autoscroll.gpg] https://loonixtools.github.io/middleclick-autoscroll/deb ./" \
|
||||
| sudo tee /etc/apt/sources.list.d/middleclick-autoscroll.list
|
||||
sudo apt update && sudo apt install middleclick-autoscroll
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>openSUSE</b></summary>
|
||||
|
||||
```bash
|
||||
sudo rpm --import https://loonixtools.github.io/middleclick-autoscroll/KEY.gpg
|
||||
sudo zypper addrepo --gpgcheck --refresh \
|
||||
https://loonixtools.github.io/middleclick-autoscroll/rpm middleclick-autoscroll
|
||||
sudo zypper install middleclick-autoscroll
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## How to use
|
||||
|
||||
```bash
|
||||
middleclick-autoscroll
|
||||
```
|
||||
|
||||
<p align="center">
|
||||
<img src="res/screenshots/menu.png" alt="The middleclick-autoscroll menu in Konsole: autoscroll on, 19 of 19 applications covered" width="620">
|
||||
</p>
|
||||
|
||||
Press **1**. Autoscroll _✨just works✨_, like on Windows.
|
||||
|
||||
<details>
|
||||
<summary>Applications</summary>
|
||||
|
||||
**3** lists every app it found. Type to search, then Space turns one on or off.
|
||||
|
||||
<p align="center">
|
||||
<img src="res/screenshots/applications.png" alt="The applications list in Konsole, with a search field on top and how each app gets autoscroll" width="620">
|
||||
</p>
|
||||
|
||||
</details>
|
||||
|
||||
## More
|
||||
|
||||
<details>
|
||||
<summary>How it works</summary>
|
||||
|
||||
Chromium, Electron and CEF all have autoscroll built in. On Linux it is off, because middle click
|
||||
pastes there. One flag turns it back on:
|
||||
|
||||
```
|
||||
--enable-blink-features=MiddleClickAutoscroll
|
||||
```
|
||||
|
||||
Adding it to every app by hand is a pain, and updates can undo it. This tool does it for you:
|
||||
|
||||
- It finds every app built on Chromium. AppImages too, without unpacking or starting them.
|
||||
- It puts the flag where the app reads it: the app's flags file, or a copy of its menu entry in
|
||||
your home folder.
|
||||
- It watches for new apps and covers them on their own.
|
||||
- It only writes to your home folder, and `disable` puts everything back.
|
||||
|
||||
**Browsers** get a spelling of the flag that shows no warning bar. Brave and Helium also get their
|
||||
own name for the feature. A browser ignores names it does not know.
|
||||
|
||||
**Firefox** has autoscroll too, but as a setting, not a flag. It goes into the `user.js` of each
|
||||
profile. The same works for LibreWolf, Zen, Floorp, Waterfox and Thunderbird.
|
||||
|
||||
**Steam** builds its own command line and repairs any file that looks changed. So the flag goes into
|
||||
the script that starts its web helper, and the file keeps the size and date Steam expects. Steam
|
||||
finds nothing to repair, and autoscroll works in the store and the library. After a Steam update,
|
||||
the patch comes back on its own.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Commands</summary>
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `middleclick-autoscroll` | The menu |
|
||||
| `… enable` | Turn on |
|
||||
| `… disable` | Undo everything |
|
||||
| `… apply` | Pick up new apps |
|
||||
| `… status` | What is covered |
|
||||
| `… list` | All apps and how they are handled |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Build from source</summary>
|
||||
|
||||
```bash
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Optional: `msgfmt` for translations, `scdoc` for the man page.
|
||||
|
||||
</details>
|
||||
|
||||
GPL-3.0-or-later.
|
||||
@@ -1,42 +0,0 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA512
|
||||
|
||||
Architectures: all
|
||||
Codename: stable
|
||||
Components: main
|
||||
Date: Thu, 01 Oct 2026 07:37:48 +0000
|
||||
Label: middleclick-autoscroll
|
||||
Origin: middleclick-autoscroll
|
||||
Suite: stable
|
||||
MD5Sum:
|
||||
e086be750739f244ce337dd20f2219ff 17616 Packages
|
||||
ddfeec10d73ed399c5839968236ba3a8 2196 Packages.gz
|
||||
6dfae049a4cc1d4f4833ab56be5882d0 166 Release
|
||||
SHA1:
|
||||
7018603e49b1999b84e60003e0f6b6faf1213a65 17616 Packages
|
||||
24701103de4f7729e67fc69dca249b4b7bf17333 2196 Packages.gz
|
||||
5c6468fe52a9c25f1c38c8bc8de2021a4d3011f5 166 Release
|
||||
SHA256:
|
||||
78661a5b22e2e6ad4b081f02a905ac52ef3cc14f6a47268f0aa4ef0a42e94f39 17616 Packages
|
||||
bd3458e0c2d2f3f61dd6be69b18d5c1fdf9e15d17ff2b8058c281fe1e2876bec 2196 Packages.gz
|
||||
f4c6c1885dfa918963661f03126287e4ef8f1763a6da0d3dc6b21bea3b65a747 166 Release
|
||||
SHA512:
|
||||
1a92289fc20bb3c90922d60123491517c460c28f564c0ddcd8918c31d0aae74065381b8720b44eb5c80677e4287ba310b178ee831a8cd4ae74b1fe9cc0a3abdd 17616 Packages
|
||||
bc1d259f1e84841e561e5ac30e81c0d0e51ae46db57c3186603f66a896c0a20e4a3acc634fd15235166693245287de8c9cd189c4925a55f9c0bfff713bffa9fc 2196 Packages.gz
|
||||
09f14beab218aaccbc1dc69fc576e9f61d28e5fe517350ea8bf064cda85c9d5a75098aa97f90673729c7f0d854c37d21c560bdc99ec9001f2319cbcaafe24279 166 Release
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAEBCgAdFiEEY4PI9Dc7LesUkuKsVaPG3ykUTHkFAmq+DcwACgkQVaPG3ykU
|
||||
THlmpQ/+J/cyqBjGj0dTXZ1eAQYCxAgCZc+RGUVK6cWQLJ3+LjauZD3fI5wrjIdU
|
||||
WkiLsnZZdaShtcgwmurW1y3R+Voj40szUMQgvNBtwaK+UOKigNLvO9Z+czkGC1Ai
|
||||
3a7QIbgzaSioM2KUyjK2BQLyhiuxV52+B6w0Z3n+m53JQTI7FldYMOUyUIJVlPvP
|
||||
rvlYSbS6oi/rrfS488F1AkrHBYVD8/EENo3yXFCWrF+fIoxNpGfhTZv2jiloWVs6
|
||||
JkKasnle0QyOlS0d59tB87eS8ctAd/fiO8JjlIrkJ0QaXKaDxFJxfK7AXJdp/hgO
|
||||
F+gtg5QPABENWx4REiKF2+152kuWGOUb+RlZcKsPkcxllUX96WfokPcedT08/xX7
|
||||
m/ZUKTd16d+Fre4+bVkzQ1BB19hpH7blGoSFavxOWhQvW6LgKRFJP8YVFIM7X5Th
|
||||
yoK82guMLRnEQ9Qv0vwVoQIvXbnRyN8cn/1qFkvgee17+GQNlIX6nD+WGBoEpj/K
|
||||
jEIJqLA1jmp5QFCYnRvlwjJTfEXloaVO+0xbPS1reMxmytUswXDyJ9wg5LnAbfOm
|
||||
3SmcivJfYD1Af/rVegas5AOS5Aiegrk72/1EBibHwxYLdwT25tiGpXPDZub0ufRz
|
||||
kWC0GQTdr9i/vek2XPWA2e3PdVHYFozAigU2fJTUuD89uH3vcoI=
|
||||
=3XTy
|
||||
-----END PGP SIGNATURE-----
|
||||
-360
@@ -1,360 +0,0 @@
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.1.0
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.1.0_all.deb
|
||||
Size: 35676
|
||||
MD5sum: 412f7906e3bc7446d59a0fb9f324d43d
|
||||
SHA1: 7a81cb1b9587f319cbe518eddf671f697114b2e1
|
||||
SHA256: bb1ac805adf3d161a3b8b3c1a635a9d5b3a8f203c3ca4643e0e944f24308849c
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/Felitendo/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink - the engine inside Chromium, Electron and CEF - has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.2.0
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.2.0_all.deb
|
||||
Size: 39252
|
||||
MD5sum: 65bdc7e1bb274253370edaf3874f04b8
|
||||
SHA1: a4067d90327ad3f17ff3ce4ee1ccd15d78e80cf7
|
||||
SHA256: 3825984ca49a1faa00a3ee022115a23a48370766ef1db60533fe79eda92ad45f
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/Felitendo/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink - the engine inside Chromium, Electron and CEF - has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.3.0
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.3.0_all.deb
|
||||
Size: 40756
|
||||
MD5sum: 59a98a53aab891b3d09c61949e751eb9
|
||||
SHA1: 53638aeff20d5882363a032fecb3a86fd2209fa4
|
||||
SHA256: 846e4476e61d20949ad95cdee2d9efb950ac927b18b3926a9a78da3e72a61e04
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/Felitendo/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink - the engine inside Chromium, Electron and CEF - has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.4.0
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.4.0_all.deb
|
||||
Size: 40256
|
||||
MD5sum: e2d66a3b8f3d4e592671e52d0cfd0f24
|
||||
SHA1: b489a7e0eb6bbbd15d84ee3592a4e398cb25c732
|
||||
SHA256: f7a9f072fd3c8cbde653cd607c1ac43c8e4633b046e2156a679c09bbbaaa42dd
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/Felitendo/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink - the engine inside Chromium, Electron and CEF - has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.5.0
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.5.0_all.deb
|
||||
Size: 42404
|
||||
MD5sum: ae1dca8afb91a648a47a92d33445c470
|
||||
SHA1: fb672f06a6b3712cfa7f94c2f35e9f5f36d7562d
|
||||
SHA256: 7eeed772e8b5f50fc6f162dfa2d07d5adc03668af44d63027cfe8beadf78e180
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/Felitendo/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink - the engine inside Chromium, Electron and CEF - has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.5.1
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.5.1_all.deb
|
||||
Size: 46932
|
||||
MD5sum: 4cad3fe99518463a266cb0de3bba5e83
|
||||
SHA1: d5923ca9e1e911ff9b19ed1c3f1511b2eff8752e
|
||||
SHA256: cbe16c7d91bec102d8d1314e2245a895d065c59f281fceb65dceedd16e9c939d
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/Felitendo/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink - the engine inside Chromium, Electron and CEF - has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.5.2
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.5.2_all.deb
|
||||
Size: 46556
|
||||
MD5sum: 41863443b7c4cbc90a73e8423b7470c3
|
||||
SHA1: 8283d073de71403a27602b6fa7e67fc4db94cfe9
|
||||
SHA256: 9434c9f13980de62df08296dc8042609f7e411bb76a6d391eeb359b3fd3185f1
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.5.3
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.5.3_all.deb
|
||||
Size: 46984
|
||||
MD5sum: f3276c3151159668d8bc84c002ed0947
|
||||
SHA1: 5f022f3357f399037456e94754818d64670acf21
|
||||
SHA256: 14564200e11fc2f8592ae24fd5174acf264ebb902acd0294b0624d8de50a0d8a
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.5.4
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.5.4_all.deb
|
||||
Size: 47100
|
||||
MD5sum: 8a1f13b3e8c2da5e9b9ed8604d828ee9
|
||||
SHA1: 1c4a47f8d2f371a25eab00358f04b08dc6286c38
|
||||
SHA256: 4309516c39d5788ba7b554a5709bde347c6a9fe5a0dedbba7064946542df7e3d
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.6.0
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.6.0_all.deb
|
||||
Size: 60872
|
||||
MD5sum: 33fa1bcfc00ac72d6b34d37de5f9b540
|
||||
SHA1: 4f13817665c4685c4377ef4f17571d9aad22bb39
|
||||
SHA256: 6fdce8ec27270f800559c1d73a13705ec5b22d08c96ca44e1635066a5dcf8815
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.6.1
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.6.1_all.deb
|
||||
Size: 62180
|
||||
MD5sum: 1e5db5d9d1c300d2c0b512b57764e740
|
||||
SHA1: 778d57f5028eda99f849104dc07b5bef681b015d
|
||||
SHA256: 881b783d216cb5f15fcf9bf0751bb9af68ba74b4579fea9a02f54acff60bebcc
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Package: middleclick-autoscroll
|
||||
Version: 1.7.0
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Filename: ./middleclick-autoscroll_1.7.0_all.deb
|
||||
Size: 65424
|
||||
MD5sum: c98e6cce47723c33ced8ebda2883a89e
|
||||
SHA1: 876f386d53f142b482c68f8eebc8fa55589d04e3
|
||||
SHA256: 9faa1b2e93598594c96eefd3a107f401cd55829e5009c7559492e18f358c8b62
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Homepage: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
|
||||
Binary file not shown.
-23
@@ -1,23 +0,0 @@
|
||||
Architectures: all
|
||||
Codename: stable
|
||||
Components: main
|
||||
Date: Thu, 01 Oct 2026 07:37:48 +0000
|
||||
Label: middleclick-autoscroll
|
||||
Origin: middleclick-autoscroll
|
||||
Suite: stable
|
||||
MD5Sum:
|
||||
e086be750739f244ce337dd20f2219ff 17616 Packages
|
||||
ddfeec10d73ed399c5839968236ba3a8 2196 Packages.gz
|
||||
6dfae049a4cc1d4f4833ab56be5882d0 166 Release
|
||||
SHA1:
|
||||
7018603e49b1999b84e60003e0f6b6faf1213a65 17616 Packages
|
||||
24701103de4f7729e67fc69dca249b4b7bf17333 2196 Packages.gz
|
||||
5c6468fe52a9c25f1c38c8bc8de2021a4d3011f5 166 Release
|
||||
SHA256:
|
||||
78661a5b22e2e6ad4b081f02a905ac52ef3cc14f6a47268f0aa4ef0a42e94f39 17616 Packages
|
||||
bd3458e0c2d2f3f61dd6be69b18d5c1fdf9e15d17ff2b8058c281fe1e2876bec 2196 Packages.gz
|
||||
f4c6c1885dfa918963661f03126287e4ef8f1763a6da0d3dc6b21bea3b65a747 166 Release
|
||||
SHA512:
|
||||
1a92289fc20bb3c90922d60123491517c460c28f564c0ddcd8918c31d0aae74065381b8720b44eb5c80677e4287ba310b178ee831a8cd4ae74b1fe9cc0a3abdd 17616 Packages
|
||||
bc1d259f1e84841e561e5ac30e81c0d0e51ae46db57c3186603f66a896c0a20e4a3acc634fd15235166693245287de8c9cd189c4925a55f9c0bfff713bffa9fc 2196 Packages.gz
|
||||
09f14beab218aaccbc1dc69fc576e9f61d28e5fe517350ea8bf064cda85c9d5a75098aa97f90673729c7f0d854c37d21c560bdc99ec9001f2319cbcaafe24279 166 Release
|
||||
@@ -1,16 +0,0 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCgAdFiEEY4PI9Dc7LesUkuKsVaPG3ykUTHkFAmq+DcwACgkQVaPG3ykU
|
||||
THmXfhAAtmAYOpyp/H1yYuTpKA/W2a6d8nJt/Pzyf5nLFEmlJ63OihzjVAXxV/o7
|
||||
nlI9u3DIVtJDhMOZoxrJOLS+47C5RQ8l+18sYIKH64J2rja8oGMdc7LIyuqxzDTi
|
||||
OGGv5aUhwlLNjlM8IhmUKH4ncBj/BEdrzpe6nc3SPqs4EdkK6FeQBJjm6SQRalRn
|
||||
d0T32RRZ8NxJWpBnE+p8WrnY7SF+F25HEJ6QgRrRtEW2TIaIA78xy9SajndyZz68
|
||||
bL/7HCdzbJsINvy1NXN1SxVOEohzfMrCJ7BedIhchvAel8H6F/eZthDCNz+b9k/F
|
||||
K2M8zFWfIavk2MNyxU6zc/4J6zq43OMUblZm1XCpriS/hLIgoZd/oleAgAwf9C20
|
||||
BIr85MyHzFhToihnAxtyuR+1dLbwDPItnh5Al6fbmboLhli6d+erUWAdLmt5lV97
|
||||
SENB7dfZX5GVq2GmjlzqCEow968movFKk7whiPOw6Ng7AHQ28Cy4lfl+zKreysX+
|
||||
iIrFR5ARvz/Jal/9kgTyj2izxCZCmu8rTU5fANmJGLdNHROyiiZBy795gf3s4Dwm
|
||||
7X1lkiOOJSwpGahFwVACn9F6D3efkZjxlrRo08r4tQRbpgVNl1bMEArAZw6e4PwU
|
||||
pf15puSyNs9XvvyDiBPaQq+r5b9v63n2HkXJWhrTB0ojTFnTS+g=
|
||||
=O+oS
|
||||
-----END PGP SIGNATURE-----
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,331 @@
|
||||
middleclick-autoscroll(1)
|
||||
|
||||
# NAME
|
||||
|
||||
middleclick-autoscroll - middle-click autoscroll in every application that supports it
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*middleclick-autoscroll* [_command_]
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Blink, the engine inside Chromium, Electron and CEF, implements Windows-style
|
||||
autoscroll: hold the middle mouse button and the page scrolls with the pointer.
|
||||
On Linux it is switched off, because middle click is taken by primary-selection
|
||||
paste. It can be turned back on with *--enable-blink-features=MiddleClickAutoscroll*
|
||||
on the command line.
|
||||
|
||||
*middleclick-autoscroll* finds every Chromium-based application on the system
|
||||
and puts that argument somewhere the application will actually read it, then
|
||||
keeps doing so for anything installed later.
|
||||
|
||||
A browser is given *--enable-features=MiddleClickAutoscroll* instead. It asks
|
||||
for the same feature, because Blink generates a feature of that name for each
|
||||
of its runtime flags. But it is not on the list of flags Chromium warns about,
|
||||
so the browser does not put a bar reading "You are using an unsupported command-line
|
||||
flag" above every page. That spelling only works from Chromium 124 onwards,
|
||||
which is why everything else keeps the first one: an application that embeds an
|
||||
older Chromium, such as Steam's CEF, has no such bar to show anyway. Two
|
||||
browsers know the feature under a name of their own and ignore Chromium's:
|
||||
Helium, which is asked for *HeliumMiddleClickAutoscroll* as well, and Brave,
|
||||
which is asked for *MiddelButtonClickAutoscroll* (spelled the way Brave spells
|
||||
it). A name a browser does not know is ignored. It works on any distribution:
|
||||
which of the routes below an application takes is read off its launcher, not
|
||||
assumed from where the launcher came from.
|
||||
|
||||
Firefox and everything else built on Gecko has autoscroll as well, behind a
|
||||
setting rather than a flag. See *FIREFOX*.
|
||||
|
||||
Run without a command it shows an interactive menu. Everything it can be told
|
||||
is reachable from there; the configuration file behind it does not need to be
|
||||
edited by hand. Its applications list turns single applications on or off,
|
||||
Steam, Spotify and Firefox included. Its settings hold the rest: the desktop's
|
||||
middle-click paste, the watcher, and more Chromium arguments for every
|
||||
application.
|
||||
|
||||
Nothing outside the user's home directory is written to, and running it as root
|
||||
is refused.
|
||||
|
||||
# COMMANDS
|
||||
|
||||
*enable*
|
||||
Turn autoscroll on, apply it to everything installed, and start watching
|
||||
for new applications.
|
||||
|
||||
*disable*
|
||||
Turn it off and put every file that was changed back the way it was.
|
||||
|
||||
*apply* [*--rebuild*]
|
||||
Apply to anything that has appeared since the last run. This is what the
|
||||
watcher calls; running it by hand is only needed when the watcher is off.
|
||||
|
||||
Entries left behind by an application that has since been uninstalled are
|
||||
removed here too. The entry shadowing it lives in the user's home, where
|
||||
the package manager that removed the application cannot see it.
|
||||
|
||||
With *--rebuild* everything is taken back first and written again from
|
||||
scratch. That is the repair: it does not care what looks correct already.
|
||||
|
||||
*status*
|
||||
How many applications are covered, whether Steam is patched, and when the
|
||||
last run was.
|
||||
|
||||
*list*
|
||||
Every application that was found, and how each one is handled.
|
||||
|
||||
*-h*, *--help*
|
||||
Show a summary of the commands.
|
||||
|
||||
*-V*, *--version*
|
||||
Show the version.
|
||||
|
||||
# HOW THE ARGUMENT GETS IN
|
||||
|
||||
Two ways, chosen per application.
|
||||
|
||||
*Flag file*
|
||||
Where the launcher reads extra arguments from
|
||||
_$XDG_CONFIG_HOME/<name>-flags.conf_. This is the preferred route: it is
|
||||
the supported way to pass arguments, it survives package upgrades, and it
|
||||
applies to a launch from a terminal as much as one from the menu. A
|
||||
feature list that is already in the file is extended rather than
|
||||
duplicated. Chromium keeps only the last occurrence of such an option, so
|
||||
a second one would switch the first one off.
|
||||
|
||||
Arch's Electron and Chromium packages all wrap their binaries this way, and
|
||||
so do a number of individual vendors' launchers elsewhere. Whether a given
|
||||
launcher does is read off the launcher itself, never assumed from the
|
||||
distribution: only one that really names such a file takes this route.
|
||||
|
||||
*Desktop entry*
|
||||
For applications that ship their own binary with no wrapper, and for
|
||||
everything inside a Flatpak or a snap, a copy of the desktop entry with the
|
||||
argument appended is written to _~/.local/share/applications_, where it
|
||||
shadows the system one. Entries that already live there (AppImages, web
|
||||
app shortcuts) are edited in place, with the original kept.
|
||||
|
||||
This is the route everything takes on the distributions whose Chromium
|
||||
wrappers keep their equivalent file under _/etc_, where it is the system's
|
||||
to write and not the user's: Debian, Ubuntu, Fedora and openSUSE among
|
||||
them.
|
||||
|
||||
A generated entry is marked *X-MCA-Generated* and an entry edited in place
|
||||
*X-MCA-Patched*. The two are never confused: the first is deleted when
|
||||
undoing, the second is restored from its backup. An entry with neither
|
||||
marker belongs to somebody else and is left alone.
|
||||
|
||||
Programs that start themselves at login write their own entry into
|
||||
_~/.config/autostart_ pointing straight at their binary, bypassing the menu
|
||||
entry entirely. Those are patched in place as well, unless the application is
|
||||
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
|
||||
search path looks at that folder, so a shortcut that lives only there would
|
||||
otherwise be invisible. And Steam puts one there for every game somebody asks
|
||||
for a shortcut to. The folder's name is translated, and the name in use is read
|
||||
from _~/.config/user-dirs.dirs_ rather than guessed; the watcher is told about
|
||||
it in a drop-in written when autoscroll is turned on.
|
||||
|
||||
# STEAM
|
||||
|
||||
Steam's interface is CEF and supports the feature, but Steam builds the command
|
||||
line for its web helper itself. The only way in is the script that starts the
|
||||
helper, inside Steam's own installation:
|
||||
|
||||
~/.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:
|
||||
_~/.local/share/Steam_ for Valve's own package and Arch's,
|
||||
_~/.steam/debian-installation_ for Debian's, and the private tree of the
|
||||
sandbox for the Flatpak and the snap. All of them are looked at.
|
||||
|
||||
Steam compares the installed files against its manifest at every start (by
|
||||
size and timestamp, not by content) and restores whatever differs. So
|
||||
the patch is written to look untouched: the bytes the argument costs are taken
|
||||
back out of the script's own comments and the timestamp is put back afterwards,
|
||||
leaving a file exactly as long and exactly as old as Steam left it. A client
|
||||
that checks its files finds nothing to repair, and the argument survives
|
||||
however Steam was started: from the menu, from a game shortcut, from a
|
||||
launcher like Heroic or Lutris, or from a terminal.
|
||||
|
||||
That is what has to work, because it is the only thing that does. A patch that
|
||||
changes the length is never written, whatever else could be done to cover for
|
||||
it: Steam checks its files at the shutdown it runs itself as well as at a start
|
||||
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
|
||||
again, and a client that quits at the end of it instead of coming up.
|
||||
|
||||
The steamrt3 script has no comments to pay with. So it is copied aside as
|
||||
_steamwebhelper.sh.orig_, and a stub of the same size takes its place. The stub
|
||||
runs the copy with the argument added. Steam only checks the files it knows,
|
||||
and the copy is not one of them.
|
||||
|
||||
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
|
||||
comments were short, is written again to fit at the next apply, or taken back
|
||||
if it cannot be made to.
|
||||
|
||||
A client update brings a new version of the script. The watcher notices and
|
||||
patches it again, and the copy kept for undoing is replaced with the new
|
||||
version at the same time, so undoing puts back the script Steam last shipped
|
||||
rather than the one from before the update.
|
||||
|
||||
# SPOTIFY
|
||||
|
||||
The official client is CEF rather than Electron. Installed through
|
||||
*spotify-launcher*, it is started by a program that builds its own command line
|
||||
and has a configuration file with a slot for extra arguments; that slot is
|
||||
where the flag goes, and the launcher shows up in the applications list like
|
||||
any other application. Installed as a plain package, a Flatpak or a snap, it is
|
||||
an ordinary desktop entry and needs nothing special.
|
||||
|
||||
# FIREFOX
|
||||
|
||||
Firefox has autoscroll of its own, switched off on Linux. There is no flag for
|
||||
it, only a setting: *general.autoScroll*. It goes into _user.js_ in every
|
||||
profile, as a marked block, and Firefox reads that file at every start. The
|
||||
same goes for everything built on Gecko: LibreWolf, Zen, Floorp, Waterfox and
|
||||
Thunderbird.
|
||||
|
||||
Where the profiles are is read from the application's own _application.ini_,
|
||||
the way Firefox works it out: _~/.mozilla/firefox_, or
|
||||
_~/.config/mozilla/firefox_ for a new install of Firefox 147 or later. A Flatpak
|
||||
or a snap has the same inside its own folder. Every profile in _profiles.ini_
|
||||
is covered. The watcher looks at that file too, so a new profile is covered
|
||||
from its second start on.
|
||||
|
||||
A profile where the setting is already on, in Firefox's own settings or in
|
||||
_user.js_, is left alone and not recorded.
|
||||
|
||||
Firefox copies the setting into _prefs.js_, so *disable* takes it out of both
|
||||
files. A Firefox that is still open would write it back when it quits. So for
|
||||
that profile _user.js_ says *false* instead, and the next *disable* or
|
||||
*enable* clears it up.
|
||||
|
||||
# MIDDLE-CLICK PASTE
|
||||
|
||||
Inside a Chromium application the argument settles this on its own: with
|
||||
autoscroll on, Blink stops pasting the primary selection on middle click,
|
||||
because the button is doing something else now. Everywhere else on the desktop
|
||||
middle click goes on pasting, so the desktop's own switch for it is turned off
|
||||
too.
|
||||
|
||||
KDE has such a switch, and only for a Wayland session, where the paste is a
|
||||
protocol the compositor either offers or does not:
|
||||
|
||||
~/.config/kwinrc, [Wayland], EnablePrimarySelection=false
|
||||
|
||||
It is written with *kwriteconfig6*, the same tool System Settings uses, because
|
||||
kwinrc has a cascade behind it and entries a distribution can mark immutable;
|
||||
an edit that reaches past all that writes something KDE goes on to ignore. KWin
|
||||
reads the key once, while it starts, so the change is true from the next login
|
||||
on rather than straight away.
|
||||
|
||||
What the key said before is recorded, and *disable* puts that back. A key that
|
||||
already said *false* before any of this is left alone and not recorded either,
|
||||
so a paste the user switched off themselves is never switched back on.
|
||||
|
||||
On X11 there is nothing to switch. The primary selection is part of X itself
|
||||
and every toolkit reaches for it on its own; no one place can say no. The
|
||||
status screen leaves the row out where there is nothing to report.
|
||||
|
||||
Turn it off under *Settings* to keep the desktop's middle-click paste.
|
||||
|
||||
# APPIMAGES
|
||||
|
||||
An AppImage is the AppImage runtime, an ordinary executable, with a squashfs
|
||||
image appended to it. The payload is compressed, so none of the files that
|
||||
identify Chromium is on disk where it could be found.
|
||||
|
||||
The names are, though. squashfs keeps the name of everything it holds in one
|
||||
table of its own, a few kilobytes of it, and a name is all this question needs -
|
||||
so that table is read and decompressed in place. Nothing is unpacked, and the
|
||||
image is never run: this can happen from the watcher, and starting a program
|
||||
because it was installed is not something a scan gets to do.
|
||||
|
||||
Two kinds of image are still out of reach. One is packed with *lzo* or *lz4*,
|
||||
which squashfs stores as bare blocks that neither tool will read without the
|
||||
framing their own file formats add. The other is the original AppImage layout,
|
||||
which is an ISO9660 filesystem rather than a squashfs one. Both are listed as
|
||||
*cannot tell* and left alone until they are switched on from the applications
|
||||
screen.
|
||||
|
||||
# WHAT HAS NO FLAG AT ALL
|
||||
|
||||
Autoscroll is a Blink feature, so only an application drawn by Blink can be
|
||||
given it. An application built on WebKitGTK is not one of those. That
|
||||
includes Tauri on Linux, GNOME Web, and anything else linked against
|
||||
libwebkit2gtk. WebKit has no equivalent feature to ask for, on any command line
|
||||
or in any configuration file.
|
||||
|
||||
Such an application is identified as what it is and then left out of the list
|
||||
entirely, rather than being offered as something that could be switched on. An
|
||||
application that is absent from *list* is absent because there is nothing this
|
||||
tool could do for it.
|
||||
|
||||
# FILES
|
||||
|
||||
_~/.config/middleclick-autoscroll/config_
|
||||
Written by the program. Every option in it is reachable from the menu.
|
||||
|
||||
_~/.local/state/middleclick-autoscroll/ledger_
|
||||
One line per change, so *disable* can undo exactly what was done and
|
||||
nothing else.
|
||||
|
||||
_~/.local/state/middleclick-autoscroll/backup/_
|
||||
Copies of the files that are edited in place rather than shadowed.
|
||||
|
||||
_~/.config/kwinrc_
|
||||
KDE's own configuration. One key in it, *EnablePrimarySelection*, is
|
||||
written when the desktop's middle-click paste is turned off, and put
|
||||
back by *disable*.
|
||||
|
||||
_user.js_ in every Firefox profile
|
||||
Gets a block between *// >>> middleclick-autoscroll* and
|
||||
*// <<< middleclick-autoscroll*. Nothing else in the file is touched.
|
||||
|
||||
_~/.cache/middleclick-autoscroll/detect_
|
||||
Which programs were found to be Chromium or Gecko, keyed by size and modification
|
||||
time. Its first line says which version of the program wrote it, and a
|
||||
file from an older one is ignored rather than trusted. Otherwise an entry
|
||||
for a file that has not changed would never be looked at again.
|
||||
Safe to delete.
|
||||
|
||||
# ENVIRONMENT
|
||||
|
||||
*NO_COLOR*
|
||||
Disables colour.
|
||||
|
||||
# REQUIREMENTS
|
||||
|
||||
Bash 4.2 or newer and GNU coreutils. The watcher needs a systemd user session;
|
||||
without one everything else works and applications installed later are picked
|
||||
up at the next *apply* rather than on their own.
|
||||
|
||||
*gzip* is what reads the name table of an AppImage, and *xz* or *zstd* the ones
|
||||
packed with those instead. None of the three is required: an image whose
|
||||
compressor has no tool installed is listed as *cannot tell*, exactly like the
|
||||
two formats there is no reader for at all.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*systemctl*(1), *flatpak*(1), *snap*(8), *kwriteconfig6*(1)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Felitendo. Source and issue tracker at
|
||||
https://github.com/LoonixTools/middleclick-autoscroll
|
||||
@@ -1,7 +0,0 @@
|
||||
[middleclick-autoscroll]
|
||||
name=middleclick-autoscroll
|
||||
baseurl=https://loonixtools.github.io/middleclick-autoscroll/rpm
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=https://loonixtools.github.io/middleclick-autoscroll/KEY.gpg
|
||||
@@ -0,0 +1,90 @@
|
||||
# Packaging and releases
|
||||
|
||||
The Makefile installs everything; these only wrap what it produced. That is
|
||||
on purpose: a packaging script that lists the files again is a second
|
||||
description of the layout, and two descriptions drift.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `deb/control`, `deb/copyright` | metadata for the Debian binary package |
|
||||
| `rpm/middleclick-autoscroll.spec` | the RPM spec |
|
||||
| `build-deb.sh`, `build-rpm.sh` | build one package into `dist/` |
|
||||
| `check-version.sh` | refuses a tag that disagrees with the Makefile |
|
||||
| `publish-repos.sh` | regenerates the APT and RPM repositories |
|
||||
| `pages/` | the landing page and the `.repo` file served from GitHub Pages |
|
||||
|
||||
Neither package carries a maintainer script. The units are enabled per user by
|
||||
the program itself, and there is nothing to do as root at install time.
|
||||
|
||||
## Building one by hand
|
||||
|
||||
```bash
|
||||
packaging/build-deb.sh # needs dpkg-dev, gettext, scdoc
|
||||
packaging/build-rpm.sh # needs rpm-build, gettext, scdoc, systemd-rpm-macros
|
||||
```
|
||||
|
||||
Both take the version from `make version` unless one is passed as the first
|
||||
argument.
|
||||
|
||||
## Making a release
|
||||
|
||||
1. Bump `VERSION` in the Makefile.
|
||||
2. Add the release to `CHANGELOG.md`, in the format CLAUDE.md describes.
|
||||
3. Commit, then `git tag vX.Y.Z && git push --tags`.
|
||||
|
||||
The `release` workflow builds both packages in a Debian and a Fedora container,
|
||||
refuses the tag if it disagrees with the Makefile or has no changelog entry,
|
||||
attaches the packages to a GitHub release with the entry as its notes, and adds
|
||||
them to the APT and RPM repositories on the `gh-pages` branch. Nothing else has
|
||||
to be done by hand.
|
||||
|
||||
## Trying the release path first
|
||||
|
||||
```bash
|
||||
gh workflow run release.yml -f dry_run=true
|
||||
```
|
||||
|
||||
Builds both packages, builds both repositories with a key generated on the
|
||||
spot, checks the three signatures it wrote, and then installs the packages back
|
||||
out of the repositories (apt on the runner, dnf in a Fedora container). Nothing
|
||||
is pushed and no release is made. This is worth running after any change to the
|
||||
packaging, because the alternative is finding out from a tag.
|
||||
|
||||
## Setting up the signing, once
|
||||
|
||||
The repositories are signed, so this needs a key. Make one that exists for
|
||||
nothing else (not a personal key) and give it no passphrase. It lives as an
|
||||
encrypted repository secret, and `rpmsign` cannot be handed a passphrase
|
||||
unattended.
|
||||
|
||||
```bash
|
||||
gpg --batch --passphrase '' --quick-generate-key \
|
||||
'middleclick-autoscroll repository <felitendoyt@gmail.com>' rsa4096 sign never
|
||||
|
||||
gpg --armor --export-secret-keys 'middleclick-autoscroll repository' \
|
||||
| gh secret set GPG_PRIVATE_KEY
|
||||
```
|
||||
|
||||
Without the secret the workflow still builds both packages and attaches them to
|
||||
the release; it says so in the log and leaves the repositories alone.
|
||||
|
||||
## Pointing Pages at it, once, in this order
|
||||
|
||||
The `gh-pages` branch does not exist until a release has put something on it,
|
||||
and a branch that does not exist cannot be picked in the Pages settings. So:
|
||||
|
||||
1. Set the secret, above.
|
||||
2. Tag a release. The workflow creates the branch and fills it.
|
||||
3. *Then* set **Pages** to deploy from a branch and pick `gh-pages` at the
|
||||
root.
|
||||
|
||||
Doing it the other way round is a wall, and leaving Pages pointed at `main`
|
||||
serves the source tree at the address the install instructions name. Then the
|
||||
key and the indexes are 404 and nothing installs.
|
||||
|
||||
## What users end up with
|
||||
|
||||
The public key is published as `KEY.gpg` beside the repositories, and the
|
||||
landing page carries the setup lines for each distribution. After that, a new
|
||||
version arrives with `apt upgrade`, `dnf upgrade` or `zypper up` like anything
|
||||
else.
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds the binary package for Debian, Ubuntu and their derivatives into
|
||||
# dist/.
|
||||
#
|
||||
# Everything the package contains comes out of `make install`. This only wraps
|
||||
# what that produced, so there is exactly one description of where a file goes
|
||||
# and it is the Makefile. A packaging script that lists the files again is a
|
||||
# second description, and the two drift.
|
||||
#
|
||||
# Needs: make, dpkg-deb, msgfmt (gettext), scdoc.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
version="${1:-$(make -s -C "$here" version)}"
|
||||
name=middleclick-autoscroll
|
||||
|
||||
# A package without its man page or its translations is not a package this
|
||||
# should be quietly willing to produce: the Makefile skips both when the tools
|
||||
# are missing, and the result would look like a successful build.
|
||||
for tool in msgfmt scdoc dpkg-deb; do
|
||||
command -v "$tool" > /dev/null || { echo "$0: $tool is not installed" >&2; exit 1; }
|
||||
done
|
||||
|
||||
root="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$root"' EXIT
|
||||
|
||||
make -C "$here" install \
|
||||
DESTDIR="$root" \
|
||||
PREFIX=/usr \
|
||||
VERSION="$version" \
|
||||
USERUNITDIR=/usr/lib/systemd/user
|
||||
|
||||
install -d "$root/DEBIAN"
|
||||
sed "s|@VERSION@|$version|g" "$here/packaging/deb/control" > "$root/DEBIAN/control"
|
||||
install -Dm644 "$here/packaging/deb/copyright" \
|
||||
"$root/usr/share/doc/$name/copyright"
|
||||
|
||||
# Relative to the package root, and DEBIAN/ itself is not part of the contents.
|
||||
# Sorted in the C locale so the file comes out the same whatever the locale of
|
||||
# the machine that built it.
|
||||
( cd "$root" && find . -type f ! -path './DEBIAN/*' -printf '%P\0' \
|
||||
| LC_ALL=C sort -z | xargs -0 md5sum > DEBIAN/md5sums )
|
||||
|
||||
mkdir -p "$here/dist"
|
||||
out="$here/dist/${name}_${version}_all.deb"
|
||||
dpkg-deb --root-owner-group --build "$root" "$out" > /dev/null
|
||||
|
||||
echo "$out"
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Builds the binary package for Fedora, RHEL, openSUSE and their derivatives
|
||||
# into dist/.
|
||||
#
|
||||
# The spec takes the version as a macro rather than carrying one of its own,
|
||||
# for the same reason the Debian control file has a placeholder: the Makefile
|
||||
# is where the version is written down.
|
||||
#
|
||||
# Needs: rpmbuild, make, msgfmt (gettext), scdoc, systemd-rpm-macros.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
version="${1:-$(make -s -C "$here" version)}"
|
||||
name=middleclick-autoscroll
|
||||
|
||||
command -v rpmbuild > /dev/null || { echo "$0: rpmbuild is not installed" >&2; exit 1; }
|
||||
|
||||
top="$(mktemp -d)"
|
||||
trap 'rm -rf -- "$top"' EXIT
|
||||
mkdir -p "$top"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS}
|
||||
|
||||
# The working tree as it is, not as it was committed: a package built from a
|
||||
# checkout has to contain what is in that checkout.
|
||||
tar czf "$top/SOURCES/$name-$version.tar.gz" \
|
||||
--transform "s,^\\.,$name-$version," \
|
||||
--exclude=./.git --exclude=./dist --exclude=./po/'*.mo' \
|
||||
-C "$here" .
|
||||
|
||||
rpmbuild \
|
||||
--define "_topdir $top" \
|
||||
--define "_version $version" \
|
||||
-bb "$here/packaging/rpm/$name.spec" > /dev/null
|
||||
|
||||
mkdir -p "$here/dist"
|
||||
cp "$top"/RPMS/noarch/*.rpm "$here/dist/"
|
||||
|
||||
ls "$here/dist/$name-$version"*.rpm
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Refuses a release whose tag and Makefile disagree.
|
||||
#
|
||||
# The version is baked into the program at install time from the Makefile, and
|
||||
# the packages take theirs from the same place. But the tag is what people see
|
||||
# and what the release is named after. A tag that says something else produces
|
||||
# a package called 1.0.4 containing a program that reports 1.0.3, and nothing
|
||||
# would have complained.
|
||||
#
|
||||
# Anything that is not a v-tag (a run started by hand from a branch) is not a
|
||||
# release and has nothing to check.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ref="${1:-}"
|
||||
|
||||
case "$ref" in
|
||||
v[0-9]*) ;;
|
||||
*)
|
||||
echo "not a release tag (${ref:-none}), nothing to check against"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
tag_version="${ref#v}"
|
||||
make_version="$(make -s -C "$here" version)"
|
||||
|
||||
if [[ $tag_version != "$make_version" ]]; then
|
||||
echo "tag $ref says $tag_version, the Makefile says $make_version" >&2
|
||||
echo "Bump VERSION in the Makefile to match the tag, or retag." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$ref matches the Makefile"
|
||||
@@ -0,0 +1,24 @@
|
||||
Package: middleclick-autoscroll
|
||||
Version: @VERSION@
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Maintainer: Felitendo <felitendoyt@gmail.com>
|
||||
Depends: bash (>= 4.2), coreutils, findutils, grep, sed, mawk | gawk | original-awk
|
||||
Recommends: gettext-base, systemd, desktop-file-utils
|
||||
Homepage: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Description: middle-click autoscroll for Chromium-based applications
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
.
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to
|
||||
keep up to date: every launcher is examined, the ones running on Chromium
|
||||
underneath are identified by what they ship rather than by their name, and the
|
||||
argument goes wherever that particular application will actually read it.
|
||||
.
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that
|
||||
everything it changed is put back.
|
||||
@@ -0,0 +1,20 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: middleclick-autoscroll
|
||||
Source: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
|
||||
Files: *
|
||||
Copyright: 2026 Felitendo
|
||||
License: GPL-3+
|
||||
|
||||
License: GPL-3+
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
.
|
||||
On Debian systems the full text of the GNU General Public License version 3 can
|
||||
be found in /usr/share/common-licenses/GPL-3.
|
||||
@@ -78,21 +78,21 @@
|
||||
|
||||
<h2>Debian, Ubuntu, Linux Mint, Pop!_OS</h2>
|
||||
<pre><code>sudo install -d -m 0755 /etc/apt/keyrings
|
||||
curl -fsSL https://loonixtools.github.io/middleclick-autoscroll/KEY.gpg \
|
||||
curl -fsSL @BASEURL@/KEY.gpg \
|
||||
| sudo gpg --dearmor -o /etc/apt/keyrings/middleclick-autoscroll.gpg
|
||||
echo "deb [signed-by=/etc/apt/keyrings/middleclick-autoscroll.gpg] https://loonixtools.github.io/middleclick-autoscroll/deb ./" \
|
||||
echo "deb [signed-by=/etc/apt/keyrings/middleclick-autoscroll.gpg] @BASEURL@/deb ./" \
|
||||
| sudo tee /etc/apt/sources.list.d/middleclick-autoscroll.list
|
||||
sudo apt update
|
||||
sudo apt install middleclick-autoscroll</code></pre>
|
||||
|
||||
<h2>Fedora, RHEL, CentOS Stream</h2>
|
||||
<pre><code>sudo curl -fsSL -o /etc/yum.repos.d/middleclick-autoscroll.repo \
|
||||
https://loonixtools.github.io/middleclick-autoscroll/middleclick-autoscroll.repo
|
||||
@BASEURL@/middleclick-autoscroll.repo
|
||||
sudo dnf install middleclick-autoscroll</code></pre>
|
||||
|
||||
<h2>openSUSE</h2>
|
||||
<pre><code>sudo rpm --import https://loonixtools.github.io/middleclick-autoscroll/KEY.gpg
|
||||
sudo zypper addrepo --gpgcheck --refresh https://loonixtools.github.io/middleclick-autoscroll/rpm middleclick-autoscroll
|
||||
<pre><code>sudo rpm --import @BASEURL@/KEY.gpg
|
||||
sudo zypper addrepo --gpgcheck --refresh @BASEURL@/rpm middleclick-autoscroll
|
||||
sudo zypper install middleclick-autoscroll</code></pre>
|
||||
|
||||
<h2>Arch, CachyOS, EndeavourOS, Manjaro</h2>
|
||||
@@ -108,7 +108,7 @@ sudo zypper install middleclick-autoscroll</code></pre>
|
||||
|
||||
<footer>
|
||||
GPL-3.0-or-later. Packages are signed; the public key is
|
||||
<a href="https://loonixtools.github.io/middleclick-autoscroll/KEY.gpg">KEY.gpg</a>.
|
||||
<a href="@BASEURL@/KEY.gpg">KEY.gpg</a>.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
@@ -0,0 +1,7 @@
|
||||
[middleclick-autoscroll]
|
||||
name=middleclick-autoscroll
|
||||
baseurl=@BASEURL@/rpm
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=@BASEURL@/KEY.gpg
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Puts the packages that were just built into the APT and RPM repositories on
|
||||
# the gh-pages branch, and regenerates the indexes over everything that is
|
||||
# there.
|
||||
#
|
||||
# Old versions are kept rather than replaced. An index built over all of them
|
||||
# is what lets somebody pin a version or go back to one, and it costs a few
|
||||
# hundred kilobytes.
|
||||
#
|
||||
# Usage: publish-repos.sh <gh-pages checkout> <directory of new packages>
|
||||
#
|
||||
# Needs: dpkg-dev, apt-utils, createrepo-c, gpg, and a secret key already
|
||||
# imported. Its id is taken from the keyring.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
pages="$(cd -- "$1" && pwd)"
|
||||
incoming="$(cd -- "$2" && pwd)"
|
||||
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
base_url="${MCA_REPO_URL:-https://loonixtools.github.io/middleclick-autoscroll}"
|
||||
|
||||
keyid="$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ { print $5; exit }')"
|
||||
[[ -n $keyid ]] || { echo "$0: no secret key in the keyring" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$pages/deb" "$pages/rpm"
|
||||
cp -- "$incoming"/*.deb "$pages/deb/"
|
||||
cp -- "$incoming"/*.rpm "$pages/rpm/"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# APT
|
||||
# ---------------------------------------------------------------------------
|
||||
# A flat repository: the packages and their index sit in one directory and the
|
||||
# sources line ends in "./". There is one distribution here and it is the same
|
||||
# package for all of them, so the suite and component machinery of a pool
|
||||
# layout would describe nothing.
|
||||
(
|
||||
cd "$pages/deb"
|
||||
|
||||
# The old index must be gone before the new one is written: apt-ftparchive
|
||||
# hashes every file in the directory, and a Release that hashes the
|
||||
# previous Release is a Release that cannot be verified.
|
||||
rm -f Packages Packages.gz Release Release.gpg InRelease
|
||||
|
||||
dpkg-scanpackages --multiversion . > Packages
|
||||
gzip -9kf Packages
|
||||
|
||||
apt-ftparchive \
|
||||
-o APT::FTPArchive::Release::Origin=middleclick-autoscroll \
|
||||
-o APT::FTPArchive::Release::Label=middleclick-autoscroll \
|
||||
-o APT::FTPArchive::Release::Suite=stable \
|
||||
-o APT::FTPArchive::Release::Codename=stable \
|
||||
-o APT::FTPArchive::Release::Architectures=all \
|
||||
-o APT::FTPArchive::Release::Components=main \
|
||||
release . > Release
|
||||
|
||||
# Both signatures: InRelease is what current apt fetches, Release.gpg is
|
||||
# what an older one falls back to.
|
||||
gpg --batch --yes --local-user "$keyid" --clearsign --output InRelease Release
|
||||
gpg --batch --yes --local-user "$keyid" --detach-sign --armor --output Release.gpg Release
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# RPM
|
||||
# ---------------------------------------------------------------------------
|
||||
(
|
||||
cd "$pages/rpm"
|
||||
createrepo_c --quiet --update .
|
||||
rm -f repodata/repomd.xml.asc
|
||||
gpg --batch --yes --local-user "$keyid" --detach-sign --armor repodata/repomd.xml
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The key and the landing page
|
||||
# ---------------------------------------------------------------------------
|
||||
gpg --armor --export "$keyid" > "$pages/KEY.gpg"
|
||||
|
||||
sed "s|@BASEURL@|$base_url|g" "$here/packaging/pages/index.html" > "$pages/index.html"
|
||||
sed "s|@BASEURL@|$base_url|g" "$here/packaging/pages/middleclick-autoscroll.repo" \
|
||||
> "$pages/middleclick-autoscroll.repo"
|
||||
|
||||
# Pages would otherwise hand the whole directory to Jekyll, which drops every
|
||||
# file whose name starts with an underscore and can rewrite the rest.
|
||||
touch "$pages/.nojekyll"
|
||||
|
||||
echo "signed with $keyid"
|
||||
ls -1 "$pages/deb" "$pages/rpm"
|
||||
@@ -0,0 +1,72 @@
|
||||
# Built with `packaging/build-rpm.sh`, which passes the version in rather than
|
||||
# editing this file: the Makefile is where the version is written down, and two
|
||||
# places that have to agree are one place too many.
|
||||
%global upstream_version %{?_version}%{!?_version:1.0.3}
|
||||
|
||||
Name: middleclick-autoscroll
|
||||
Version: %{upstream_version}
|
||||
Release: 1%{?dist}
|
||||
Summary: Middle-click autoscroll for Chromium-based applications
|
||||
|
||||
License: GPL-3.0-or-later
|
||||
URL: https://github.com/LoonixTools/middleclick-autoscroll
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
|
||||
BuildArch: noarch
|
||||
BuildRequires: make
|
||||
BuildRequires: gettext
|
||||
BuildRequires: scdoc
|
||||
# For %{_userunitdir}, which is where the watcher's user units belong.
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
Requires: bash >= 4.2
|
||||
Requires: coreutils
|
||||
Requires: findutils
|
||||
Requires: grep
|
||||
Requires: sed
|
||||
Requires: gawk
|
||||
|
||||
# The interface falls back to English without gettext and picks up new
|
||||
# applications at the next `apply` without systemd, so neither is required.
|
||||
Recommends: /usr/bin/gettext
|
||||
Recommends: systemd
|
||||
Recommends: desktop-file-utils
|
||||
|
||||
%description
|
||||
Blink (the engine inside Chromium, Electron and CEF) has had Windows-style
|
||||
autoscroll for years: hold the middle mouse button, move the pointer, the page
|
||||
scrolls. On Linux it is switched off, because middle click is already taken by
|
||||
primary-selection paste.
|
||||
|
||||
This turns it back on for every application on the system that can do it, and
|
||||
for anything installed later. There is no list of supported applications to keep
|
||||
up to date: every launcher is examined, the ones running on Chromium underneath
|
||||
are identified by what they ship rather than by their name, and the argument goes
|
||||
wherever that particular application will actually read it.
|
||||
|
||||
Nothing outside the user's home directory is ever written to. Run
|
||||
"middleclick-autoscroll disable" before removing this package, so that everything
|
||||
it changed is put back.
|
||||
|
||||
%prep
|
||||
%autosetup -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
%make_build VERSION=%{upstream_version}
|
||||
|
||||
%install
|
||||
%make_install PREFIX=%{_prefix} VERSION=%{upstream_version} \
|
||||
USERUNITDIR=%{_userunitdir}
|
||||
|
||||
%find_lang %{name}
|
||||
|
||||
%files -f %{name}.lang
|
||||
%license LICENSE
|
||||
%doc %{_datadir}/doc/%{name}/README.md
|
||||
%{_bindir}/%{name}
|
||||
%{_datadir}/%{name}/
|
||||
%{_userunitdir}/%{name}.path
|
||||
%{_userunitdir}/%{name}.service
|
||||
%{_mandir}/man1/%{name}.1*
|
||||
|
||||
%changelog
|
||||
@@ -0,0 +1,268 @@
|
||||
# Czech translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Czech\n"
|
||||
"Language: cs\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "ZAPNUTO"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "VYPNUTO"
|
||||
|
||||
msgid "on"
|
||||
msgstr "zapnuto"
|
||||
|
||||
msgid "off"
|
||||
msgstr "vypnuto"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(žádné)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "soubor s příznaky"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "spouštěč"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "profil"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "nelze určit"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "zatím nepoužito"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Pokryté aplikace"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d z %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Nerozpoznáno"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (viz seznam aplikací)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Vkládání kolečkem"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Nové aplikace"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Naposledy použito"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Aplikace to převezmou při příštím spuštění."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Zapnout nebo vypnout autoscroll"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Znovu použít vše"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Aplikace"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Nastavení"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Konec"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Stiskněte libovolnou klávesu..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Okamžik..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Vypnout vkládání kolečkem"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr "Kolečko už jinde nevkládá. Platí od příštího přihlášení."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Pokrýt nové aplikace"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Později nainstalované aplikace dostanou autoscroll samy."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Další argumenty Chromia"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Předávají se každé aplikaci spolu s tím pro autoscroll."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Nahoru/Dolů: vybrat, Mezerník: změnit, q: zpět"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Argumenty oddělené mezerami, prázdné pro žádné:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Nastavení se nepodařilo uložit."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Procházím nainstalované aplikace..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Nenalezeny žádné podporované aplikace."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr "Co je označeno „nelze určit“, zůstane vypnuté, dokud to tu nezapnete."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Autoscroll je vypnutý. Toto by bylo pokryto."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Nahoru/Dolů: vybrat, Mezerník: zapnout či vypnout, q: zpět"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Hledat:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Pište pro hledání, Dolů: do seznamu, Esc: zpět"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Žádné výsledky."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Autoscroll je zapnutý. Aplikací: %d."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Již spuštěné aplikace to převezmou po restartu."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Sledování nových aplikací se nepodařilo spustit."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Chybí uživatelská relace systemd, takže nově nainstalované aplikace nebudou "
|
||||
"zachyceny samy."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Autoscroll je vypnutý a všechny změny byly vráceny."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Autoscroll je vypnutý. Nejdřív ho zapněte."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Znovu použito. Aplikací: %d."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Použito. Aplikací: %d."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Už použito. Aplikací: %d."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam spouští své rozhraní způsobem, který tato verze nedokáže změnit, aniž "
|
||||
"by si toho Steam všiml; Steam zůstává beze změny."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"Rozhraní Steamu nelze dát příznak bez souboru, který by Steam znovu "
|
||||
"nainstaloval; změna byla vrácena."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Kolečko přestane vkládat od příštího přihlášení."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Použití: middleclick-autoscroll [příkaz]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Příkazy:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Zapnout autoscroll"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Vypnout a vše vrátit"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Použít na vše nainstalované od posledního spuštění"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Vše vrátit a použít znovu"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Ukázat, co je pokryto"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Vypsat všechny nalezené aplikace"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Zobrazit tuto nápovědu"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Zobrazit verzi"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Bez příkazu se zobrazí interaktivní nabídka."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Neznámý příkaz: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "nikdy"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "právě teď"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "před 1 minutou"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "před %d min"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "před 1 hodinou"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "před %d h"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "před 1 dnem"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "před %d dny"
|
||||
@@ -0,0 +1,270 @@
|
||||
# Translation template for middleclick-autoscroll.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: Felitendo\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "AN"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "AUS"
|
||||
|
||||
msgid "on"
|
||||
msgstr "an"
|
||||
|
||||
msgid "off"
|
||||
msgstr "aus"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(keine)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "Flag-Datei"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "Startereintrag"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "Profil"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "nicht erkennbar"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "noch nicht gepatcht"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Abgedeckte Anwendungen"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d von %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Nicht erkannt"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (siehe Anwendungsliste)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Mittelklick-Einfügen"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Neue Anwendungen"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Zuletzt angewendet"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Anwendungen übernehmen das beim nächsten Start."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Autoscroll ein- oder ausschalten"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Alles neu anwenden"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Anwendungen"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Beenden"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Beliebige Taste drücken …"
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Einen Moment …"
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Mittelklick-Einfügen abschalten"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
"Mittelklick fügt auch sonst nirgends mehr ein. Ab der nächsten Anmeldung."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Neue Anwendungen abdecken"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Später installierte Anwendungen bekommen Autoscroll von selbst."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Weitere Chromium-Argumente"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Gehen zusammen mit dem Autoscroll-Argument an jede Anwendung."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Hoch/Runter: wählen, Leertaste: ändern, q: zurück"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Argumente mit Leerzeichen getrennt, leer für keine:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Einstellung konnte nicht gespeichert werden."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Installierte Anwendungen werden durchgesehen …"
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Keine unterstützten Anwendungen gefunden."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr "Was „nicht erkennbar“ ist, bleibt aus, bis es hier eingeschaltet wird."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Autoscroll ist aus. Das hier wäre abgedeckt."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Hoch/Runter: wählen, Leertaste: ein- oder ausschalten, q: zurück"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Suchen:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Tippen zum Suchen, Runter: zur Liste, Esc: zurück"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Keine Treffer."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Autoscroll ist für %d Anwendungen an."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Bereits laufende Anwendungen übernehmen das nach einem Neustart."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr ""
|
||||
"Die Überwachung neu installierter Anwendungen konnte nicht gestartet werden."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Keine systemd-Benutzersitzung, daher werden neu installierte Anwendungen "
|
||||
"nicht von selbst erfasst."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Autoscroll ist aus, alle Änderungen wurden zurückgenommen."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Autoscroll ist aus. Erst einschalten."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Erneut auf %d Anwendungen angewendet."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Auf %d Anwendungen angewendet."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Bereits auf %d Anwendungen angewendet."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam startet seine Oberfläche auf eine Art, die diese Version nicht ändern "
|
||||
"kann, ohne dass Steam es merkt; Steam bleibt unangetastet."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"Steams Oberfläche lässt sich das Flag nicht geben, ohne eine Datei zu "
|
||||
"hinterlassen, die Steam neu installieren würde; die Änderung wurde "
|
||||
"zurückgenommen."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Mittelklick fügt ab der nächsten Anmeldung nichts mehr ein."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Aufruf: middleclick-autoscroll [Befehl]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Befehle:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Autoscroll einschalten"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Ausschalten und alles zurücknehmen"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Auf alles anwenden, was seit dem letzten Lauf dazugekommen ist"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Alles zurücknehmen und neu anwenden"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Anzeigen, was abgedeckt ist"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Alle gefundenen Anwendungen auflisten"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Diese Hilfe anzeigen"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Version anzeigen"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Ohne Befehl wird ein interaktives Menü angezeigt."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Unbekannter Befehl: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "nie"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "gerade eben"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "vor 1 Minute"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "vor %d Minuten"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "vor 1 Stunde"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "vor %d Stunden"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "vor 1 Tag"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "vor %d Tagen"
|
||||
@@ -0,0 +1,269 @@
|
||||
# Spanish translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "ACTIVADO"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "DESACTIVADO"
|
||||
|
||||
msgid "on"
|
||||
msgstr "activado"
|
||||
|
||||
msgid "off"
|
||||
msgstr "desactivado"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(ninguno)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "archivo de flags"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "lanzador"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "perfil"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "no se sabe"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "aún sin aplicar"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Aplicaciones cubiertas"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d de %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Sin identificar"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (ver la lista de aplicaciones)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Pegar con clic central"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Aplicaciones nuevas"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Aplicado por última vez"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Las aplicaciones lo toman la próxima vez que se inicien."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Activar o desactivar autoscroll"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Volver a aplicar todo"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Aplicaciones"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Ajustes"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Salir"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Pulsa cualquier tecla para continuar..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Un momento..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Desactivar pegar con clic central"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
"El clic central ya no pega en ningún otro sitio. Desde el próximo inicio."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Cubrir aplicaciones nuevas"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Las aplicaciones instaladas más tarde reciben autoscroll solas."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Más argumentos de Chromium"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Se pasan a cada aplicación junto con el de autoscroll."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Arriba/Abajo: elegir, Espacio: cambiar, q: volver"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Argumentos separados por espacios, vacío para ninguno:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "No se pudo guardar el ajuste."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Revisando las aplicaciones instaladas..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "No se encontraron aplicaciones compatibles."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr "Lo marcado como «no se sabe» sigue desactivado hasta activarlo aquí."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Autoscroll está desactivado. Esto es lo que se cubriría."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Arriba/Abajo: elegir, Espacio: activar o desactivar, q: volver"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Buscar:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Escribe para buscar, Abajo: ir a la lista, Esc: volver"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Sin resultados."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Autoscroll está activado para %d aplicaciones."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Las aplicaciones que ya están abiertas lo toman al reiniciarse."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "No se pudo iniciar la vigilancia de aplicaciones nuevas."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"No hay sesión de usuario de systemd, así que las aplicaciones nuevas no se "
|
||||
"detectarán solas."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Autoscroll está desactivado y todo lo cambiado se ha restaurado."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Autoscroll está desactivado. Actívalo primero."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Aplicado de nuevo a %d aplicaciones."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Aplicado a %d aplicaciones."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Ya aplicado a %d aplicaciones."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam inicia su interfaz de una forma que esta versión no puede cambiar sin "
|
||||
"que Steam lo note; se deja como está."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"No se puede dar el flag a la interfaz de Steam sin dejar un archivo que "
|
||||
"Steam reinstalaría; el cambio se ha deshecho."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "El clic central dejará de pegar en el próximo inicio de sesión."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Uso: middleclick-autoscroll [comando]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Comandos:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Activar autoscroll"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Desactivarlo y restaurar todo"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Aplicar a todo lo instalado desde la última vez"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Deshacer todo y volver a aplicarlo"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Mostrar qué está cubierto"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Listar todas las aplicaciones encontradas"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Mostrar esta ayuda"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Mostrar la versión"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Sin comando se muestra un menú interactivo."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Comando desconocido: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "nunca"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "ahora mismo"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "hace 1 minuto"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "hace %d minutos"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "hace 1 hora"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "hace %d horas"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "hace 1 día"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "hace %d días"
|
||||
@@ -0,0 +1,272 @@
|
||||
# French translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "ACTIVÉ"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "DÉSACTIVÉ"
|
||||
|
||||
msgid "on"
|
||||
msgstr "activé"
|
||||
|
||||
msgid "off"
|
||||
msgstr "désactivé"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(aucun)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "fichier de flags"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "lanceur"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "profil"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "indéterminé"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "pas encore appliqué"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Applications couvertes"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d sur %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Non identifiées"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (voir la liste des applications)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Collage au clic du milieu"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Nouvelles applications"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Dernier passage"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Les applications le prennent en compte à leur prochain démarrage."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Activer ou désactiver l'autoscroll"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Tout réappliquer"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Applications"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Paramètres"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Quitter"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Appuyez sur une touche pour continuer..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Un instant..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Désactiver le collage au clic du milieu"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr "Le clic du milieu ne colle plus ailleurs. Dès la prochaine connexion."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Couvrir les nouvelles applications"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr ""
|
||||
"Les applications installées plus tard reçoivent l'autoscroll d'elles-mêmes."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Autres arguments Chromium"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Transmis à chaque application avec celui de l'autoscroll."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Haut/Bas : choisir, Espace : changer, q : retour"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Arguments séparés par des espaces, vide pour aucun :"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Impossible d'enregistrer le paramètre."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Examen des applications installées..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Aucune application prise en charge trouvée."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
"Ce qui est marqué « indéterminé » reste désactivé jusqu'à être activé ici."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "L'autoscroll est désactivé. Voici ce qui serait couvert."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Haut/Bas : choisir, Espace : activer ou désactiver, q : retour"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Rechercher :"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Tapez pour rechercher, Bas : aller à la liste, Échap : retour"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Aucun résultat."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "L'autoscroll est activé pour %d applications."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr ""
|
||||
"Les applications déjà ouvertes le prennent en compte après un redémarrage."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Impossible de démarrer la surveillance des nouvelles applications."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Pas de session utilisateur systemd, les nouvelles applications ne seront "
|
||||
"donc pas prises en charge d'elles-mêmes."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr ""
|
||||
"L'autoscroll est désactivé et tout ce qui avait été modifié a été rétabli."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "L'autoscroll est désactivé. Activez-le d'abord."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Réappliqué à %d applications."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Appliqué à %d applications."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Déjà appliqué à %d applications."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam lance son interface d'une façon que cette version ne peut pas modifier "
|
||||
"sans que Steam le remarque ; Steam reste tel quel."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"L'interface de Steam ne peut pas recevoir le flag sans laisser un fichier "
|
||||
"que Steam réinstallerait ; la modification a été annulée."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Le clic du milieu ne collera plus à la prochaine connexion."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Utilisation : middleclick-autoscroll [commande]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Commandes :"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Activer l'autoscroll"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Le désactiver et tout rétablir"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Appliquer à tout ce qui a été installé depuis le dernier passage"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Tout annuler et réappliquer"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Afficher ce qui est couvert"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Lister toutes les applications trouvées"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Afficher cette aide"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Afficher la version"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Sans commande, un menu interactif s'affiche."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Commande inconnue : %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "jamais"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "à l'instant"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "il y a 1 minute"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "il y a %d minutes"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "il y a 1 heure"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "il y a %d heures"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "il y a 1 jour"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "il y a %d jours"
|
||||
@@ -0,0 +1,272 @@
|
||||
# Italian translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Italian\n"
|
||||
"Language: it\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "ACCESO"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "SPENTO"
|
||||
|
||||
msgid "on"
|
||||
msgstr "acceso"
|
||||
|
||||
msgid "off"
|
||||
msgstr "spento"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(nessuno)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "file di flag"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "lanciatore"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "profilo"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "incerto"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "non ancora applicato"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Applicazioni coperte"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d di %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Non identificate"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (vedi l'elenco delle applicazioni)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Incolla con clic centrale"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Nuove applicazioni"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Applicato l'ultima volta"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Le applicazioni lo recepiscono al prossimo avvio."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Accendi o spegni l'autoscroll"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Riapplica tutto"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Applicazioni"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Impostazioni"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Esci"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Premi un tasto per continuare..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Un momento..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Disattiva incolla con clic centrale"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
"Il clic centrale non incolla più da nessun'altra parte. Dal prossimo accesso."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Copri le nuove applicazioni"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Le applicazioni installate in seguito ricevono l'autoscroll da sole."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Altri argomenti di Chromium"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Passati a ogni applicazione insieme a quello per l'autoscroll."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Su/Giù: scegli, Spazio: cambia, q: indietro"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Argomenti separati da spazi, vuoto per nessuno:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Impossibile salvare l'impostazione."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Esame delle applicazioni installate..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Nessuna applicazione supportata trovata."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
"Ciò che è segnato come «incerto» resta spento finché non viene acceso qui."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "L'autoscroll è spento. Ecco cosa verrebbe coperto."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Su/Giù: scegli, Spazio: accendi o spegni, q: indietro"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Cerca:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Scrivi per cercare, Giù: vai all'elenco, Esc: indietro"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Nessun risultato."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "L'autoscroll è acceso per %d applicazioni."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Le applicazioni già aperte lo recepiscono dopo un riavvio."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Impossibile avviare il controllo delle nuove applicazioni."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Nessuna sessione utente di systemd, quindi le nuove applicazioni non "
|
||||
"verranno rilevate da sole."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr ""
|
||||
"L'autoscroll è spento e tutto ciò che era stato modificato è stato "
|
||||
"ripristinato."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "L'autoscroll è spento. Prima accendilo."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Riapplicato a %d applicazioni."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Applicato a %d applicazioni."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Già applicato a %d applicazioni."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam avvia la sua interfaccia in un modo che questa versione non può "
|
||||
"cambiare senza che Steam se ne accorga; Steam resta com'è."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"All'interfaccia di Steam non si può dare il flag senza lasciare un file che "
|
||||
"Steam reinstallerebbe; la modifica è stata annullata."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Il clic centrale smetterà di incollare al prossimo accesso."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Uso: middleclick-autoscroll [comando]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Comandi:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Accendi l'autoscroll"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Spegnilo e ripristina tutto"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Applica a tutto ciò che è stato installato dall'ultima volta"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Annulla tutto e riapplica"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Mostra cosa è coperto"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Elenca tutte le applicazioni trovate"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Mostra questo aiuto"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Mostra la versione"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Senza comando viene mostrato un menu interattivo."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Comando sconosciuto: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "mai"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "proprio ora"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "1 minuto fa"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "%d minuti fa"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "1 ora fa"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "%d ore fa"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "1 giorno fa"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "%d giorni fa"
|
||||
@@ -0,0 +1,261 @@
|
||||
# Translation template for middleclick-autoscroll.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr ""
|
||||
|
||||
msgid "OFF"
|
||||
msgstr ""
|
||||
|
||||
msgid "on"
|
||||
msgstr ""
|
||||
|
||||
msgid "off"
|
||||
msgstr ""
|
||||
|
||||
msgid "(none)"
|
||||
msgstr ""
|
||||
|
||||
msgid "flag file"
|
||||
msgstr ""
|
||||
|
||||
msgid "launcher"
|
||||
msgstr ""
|
||||
|
||||
msgid "profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr ""
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr ""
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr ""
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr ""
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr ""
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Steam"
|
||||
msgstr ""
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr ""
|
||||
|
||||
msgid "New applications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr ""
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr ""
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr ""
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr ""
|
||||
|
||||
msgid "Applications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Quit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr ""
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr ""
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr ""
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr ""
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr ""
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr ""
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr ""
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr ""
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr ""
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr ""
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr ""
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr ""
|
||||
|
||||
msgid "Search:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr ""
|
||||
|
||||
msgid "No matches."
|
||||
msgstr ""
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr ""
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr ""
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr ""
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr ""
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr ""
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr ""
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr ""
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr ""
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr ""
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr ""
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr ""
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr ""
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr ""
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr ""
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr ""
|
||||
|
||||
msgid "just now"
|
||||
msgstr ""
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr ""
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr ""
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr ""
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr ""
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr ""
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,270 @@
|
||||
# Dutch translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Dutch\n"
|
||||
"Language: nl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "AAN"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "UIT"
|
||||
|
||||
msgid "on"
|
||||
msgstr "aan"
|
||||
|
||||
msgid "off"
|
||||
msgstr "uit"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(geen)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "flagbestand"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "starter"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "profiel"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "onbekend"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "nog niet toegepast"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Gedekte toepassingen"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d van %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Niet herkend"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (zie de lijst met toepassingen)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Plakken met middelklik"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Nieuwe toepassingen"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Laatst toegepast"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Toepassingen nemen dit over bij de volgende start."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Autoscroll aan- of uitzetten"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Alles opnieuw toepassen"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Toepassingen"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Instellingen"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Afsluiten"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Druk op een toets om door te gaan..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Een moment..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Plakken met middelklik uitzetten"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
"Middelklik plakt dan nergens anders meer. Vanaf de volgende aanmelding."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Nieuwe toepassingen meenemen"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Later geïnstalleerde toepassingen krijgen vanzelf autoscroll."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Meer Chromium-argumenten"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Gaan samen met dat voor autoscroll naar elke toepassing."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Omhoog/Omlaag: kiezen, Spatie: wijzigen, q: terug"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Argumenten gescheiden door spaties, leeg voor geen:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "De instelling kon niet worden opgeslagen."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Geïnstalleerde toepassingen worden bekeken..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Geen ondersteunde toepassingen gevonden."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
"Wat als \"onbekend\" gemarkeerd is, blijft uit tot het hier aangezet wordt."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Autoscroll staat uit. Dit zou gedekt worden."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Omhoog/Omlaag: kiezen, Spatie: aan- of uitzetten, q: terug"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Zoeken:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Typ om te zoeken, Omlaag: naar de lijst, Esc: terug"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Geen resultaten."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Autoscroll staat aan voor %d toepassingen."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Toepassingen die al draaien, nemen dit over na een herstart."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Kon het bewaken van nieuwe toepassingen niet starten."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Geen systemd-gebruikerssessie, dus nieuw geïnstalleerde toepassingen worden "
|
||||
"niet vanzelf opgepikt."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Autoscroll staat uit en alles wat veranderd was, is teruggezet."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Autoscroll staat uit. Zet het eerst aan."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Opnieuw toegepast op %d toepassingen."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Toegepast op %d toepassingen."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Al toegepast op %d toepassingen."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam start zijn interface op een manier die deze versie niet kan veranderen "
|
||||
"zonder dat Steam het merkt; Steam blijft zoals het is."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"De interface van Steam kan de flag niet krijgen zonder een bestand achter te "
|
||||
"laten dat Steam opnieuw zou installeren; de wijziging is teruggedraaid."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Middelklik plakt vanaf de volgende aanmelding niet meer."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Gebruik: middleclick-autoscroll [opdracht]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Opdrachten:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Autoscroll aanzetten"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Uitzetten en alles terugzetten"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Toepassen op alles wat sinds de vorige keer geïnstalleerd is"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Alles terugdraaien en opnieuw toepassen"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Tonen wat gedekt is"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Alle gevonden toepassingen tonen"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Deze hulp tonen"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "De versie tonen"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Zonder opdracht wordt een interactief menu getoond."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Onbekende opdracht: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "nooit"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "zojuist"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "1 minuut geleden"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "%d minuten geleden"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "1 uur geleden"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "%d uur geleden"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "1 dag geleden"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "%d dagen geleden"
|
||||
@@ -0,0 +1,269 @@
|
||||
# Polish translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Polish\n"
|
||||
"Language: pl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "WŁ."
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "WYŁ."
|
||||
|
||||
msgid "on"
|
||||
msgstr "wł."
|
||||
|
||||
msgid "off"
|
||||
msgstr "wył."
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(brak)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "plik flag"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "skrót"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "profil"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "nie wiadomo"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "jeszcze nie zastosowano"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Objęte aplikacje"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d z %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Nierozpoznane"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (zob. listę aplikacji)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Wklejanie kółkiem myszy"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Nowe aplikacje"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Ostatnio zastosowano"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Aplikacje przejmą to przy następnym uruchomieniu."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Włącz lub wyłącz autoscroll"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Zastosuj wszystko ponownie"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Aplikacje"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Ustawienia"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Zakończ"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Naciśnij dowolny klawisz, aby kontynuować..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Chwileczkę..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Wyłącz wklejanie kółkiem myszy"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr "Kółko myszy nie wkleja już nigdzie indziej. Od następnego logowania."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Obejmuj nowe aplikacje"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Aplikacje zainstalowane później same dostaną autoscroll."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Więcej argumentów Chromium"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Przekazywane każdej aplikacji razem z tym od autoscrolla."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Góra/Dół: wybierz, Spacja: zmień, q: wstecz"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Argumenty oddzielone spacjami, puste oznacza brak:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Nie udało się zapisać ustawienia."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Przeglądanie zainstalowanych aplikacji..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Nie znaleziono obsługiwanych aplikacji."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
"Pozycje „nie wiadomo” zostają wyłączone, dopóki nie włączysz ich tutaj."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Autoscroll jest wyłączony. Tyle zostałoby objęte."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Góra/Dół: wybierz, Spacja: włącz lub wyłącz, q: wstecz"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Szukaj:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Pisz, aby szukać, Dół: do listy, Esc: wstecz"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Brak wyników."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Autoscroll włączony dla %d aplikacji."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Uruchomione już aplikacje przejmą to po ponownym uruchomieniu."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Nie udało się uruchomić obserwowania nowych aplikacji."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Brak sesji użytkownika systemd, więc nowo zainstalowane aplikacje nie "
|
||||
"zostaną wykryte same."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Autoscroll jest wyłączony, a wszystkie zmiany zostały cofnięte."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Autoscroll jest wyłączony. Najpierw go włącz."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Ponownie zastosowano dla %d aplikacji."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Zastosowano dla %d aplikacji."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Już zastosowano dla %d aplikacji."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam uruchamia swój interfejs w sposób, którego ta wersja nie może zmienić "
|
||||
"bez wiedzy Steama; Steam pozostaje bez zmian."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"Interfejsowi Steama nie można dodać flagi bez zostawienia pliku, który Steam "
|
||||
"zainstalowałby ponownie; zmiana została cofnięta."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Kółko myszy przestanie wklejać od następnego logowania."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Użycie: middleclick-autoscroll [polecenie]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Polecenia:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Włącz autoscroll"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Wyłącz i cofnij wszystko"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Zastosuj do wszystkiego zainstalowanego od ostatniego razu"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Cofnij wszystko i zastosuj ponownie"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Pokaż, co jest objęte"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Wypisz wszystkie znalezione aplikacje"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Pokaż tę pomoc"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Pokaż wersję"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Bez polecenia wyświetlane jest menu interaktywne."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Nieznane polecenie: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "nigdy"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "przed chwilą"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "1 minutę temu"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "%d min temu"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "1 godzinę temu"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "%d godz. temu"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "1 dzień temu"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "%d dni temu"
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
# Brazilian Portuguese translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Brazilian Portuguese\n"
|
||||
"Language: pt_BR\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "LIGADO"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "DESLIGADO"
|
||||
|
||||
msgid "on"
|
||||
msgstr "ligado"
|
||||
|
||||
msgid "off"
|
||||
msgstr "desligado"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(nenhum)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "arquivo de flags"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "lançador"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "perfil"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "indeterminado"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "ainda não aplicado"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Aplicativos cobertos"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d de %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Não identificados"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (veja a lista de aplicativos)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Colar com clique do meio"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Novos aplicativos"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Aplicado pela última vez"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Os aplicativos passam a usar isso na próxima vez que forem abertos."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Ligar ou desligar o autoscroll"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Aplicar tudo de novo"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Aplicativos"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Configurações"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Sair"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Pressione qualquer tecla para continuar..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Um momento..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Desligar colar com clique do meio"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
"O clique do meio para de colar em outros lugares. A partir do próximo login."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Cobrir novos aplicativos"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Aplicativos instalados depois recebem o autoscroll sozinhos."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Mais argumentos do Chromium"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Passados a todo aplicativo junto com o do autoscroll."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Cima/Baixo: escolher, Espaço: mudar, q: voltar"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Argumentos separados por espaços, vazio para nenhum:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Não foi possível salvar a configuração."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Verificando os aplicativos instalados..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Nenhum aplicativo compatível encontrado."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
"O que está marcado como \"indeterminado\" fica desligado até ser ligado aqui."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "O autoscroll está desligado. Isto é o que seria coberto."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Cima/Baixo: escolher, Espaço: ligar ou desligar, q: voltar"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Buscar:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Digite para buscar, Baixo: ir para a lista, Esc: voltar"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Nenhum resultado."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "O autoscroll está ligado para %d aplicativos."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr ""
|
||||
"Aplicativos que já estão abertos passam a usar isso ao serem reiniciados."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Não foi possível iniciar o monitoramento de novos aplicativos."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Sem sessão de usuário do systemd, então novos aplicativos não serão "
|
||||
"detectados sozinhos."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "O autoscroll está desligado e tudo o que foi alterado foi restaurado."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "O autoscroll está desligado. Ligue-o primeiro."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Aplicado de novo a %d aplicativos."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Aplicado a %d aplicativos."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Já aplicado a %d aplicativos."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"O Steam inicia sua interface de um jeito que esta versão não consegue mudar "
|
||||
"sem que o Steam perceba; ele fica como está."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"A interface do Steam não pode receber o flag sem deixar um arquivo que o "
|
||||
"Steam reinstalaria; a alteração foi desfeita."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "O clique do meio para de colar no próximo login."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Uso: middleclick-autoscroll [comando]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Comandos:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Ligar o autoscroll"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Desligar e restaurar tudo"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Aplicar a tudo o que foi instalado desde a última vez"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Desfazer tudo e aplicar de novo"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Mostrar o que está coberto"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Listar todos os aplicativos encontrados"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Mostrar esta ajuda"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Mostrar a versão"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Sem comando, um menu interativo é exibido."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Comando desconhecido: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "nunca"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "agora mesmo"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "há 1 minuto"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "há %d minutos"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "há 1 hora"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "há %d horas"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "há 1 dia"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "há %d dias"
|
||||
@@ -0,0 +1,270 @@
|
||||
# Russian translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "ВКЛ"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "ВЫКЛ"
|
||||
|
||||
msgid "on"
|
||||
msgstr "вкл"
|
||||
|
||||
msgid "off"
|
||||
msgstr "выкл"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(нет)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "файл флагов"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "ярлык"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "профиль"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "неизвестно"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "ещё не применено"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Автопрокрутка"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Приложения с автопрокруткой"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d из %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Не распознано"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (см. список приложений)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Вставка средней кнопкой"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Новые приложения"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Последнее применение"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Приложения подхватят это при следующем запуске."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Включить или выключить автопрокрутку"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Применить всё заново"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Приложения"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Выход"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Нажмите любую клавишу..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Минутку..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Отключить вставку средней кнопкой"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
"Средняя кнопка больше нигде не вставляет. Со следующего входа в систему."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Охватывать новые приложения"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Приложения, установленные позже, получат автопрокрутку сами."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Другие аргументы Chromium"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Передаются каждому приложению вместе с аргументом автопрокрутки."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Вверх/Вниз: выбор, Пробел: изменить, q: назад"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Аргументы через пробел (пусто, если не нужны):"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Не удалось сохранить настройку."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Просмотр установленных приложений..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Поддерживаемые приложения не найдены."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
"Помеченное как «неизвестно» остаётся выключенным, пока его здесь не включат."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Автопрокрутка выключена. Вот что было бы охвачено."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Вверх/Вниз: выбор, Пробел: включить или выключить, q: назад"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Поиск:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Печатайте для поиска, Вниз: к списку, Esc: назад"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Ничего не найдено."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Автопрокрутка включена. Приложений: %d."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Уже запущенные приложения подхватят это после перезапуска."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Не удалось запустить отслеживание новых приложений."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Нет пользовательского сеанса systemd, поэтому новые приложения не будут "
|
||||
"подхвачены сами."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Автопрокрутка выключена, все изменения отменены."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Автопрокрутка выключена. Сначала включите её."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Применено заново. Приложений: %d."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Применено. Приложений: %d."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Уже применено. Приложений: %d."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam запускает свой интерфейс так, что эта версия не может это изменить "
|
||||
"незаметно для Steam; Steam остаётся без изменений."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"Интерфейсу Steam нельзя передать флаг, не оставив файл, который Steam "
|
||||
"переустановил бы; изменение отменено."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Средняя кнопка перестанет вставлять со следующего входа в систему."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Использование: middleclick-autoscroll [команда]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Команды:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Включить автопрокрутку"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Выключить и всё вернуть"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Применить ко всему, что установлено с прошлого запуска"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Всё отменить и применить заново"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Показать, что охвачено"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Показать все найденные приложения"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Показать эту справку"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Показать версию"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Без команды показывается интерактивное меню."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Неизвестная команда: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "никогда"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "только что"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "1 минуту назад"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "%d мин. назад"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "1 час назад"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "%d ч назад"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "1 день назад"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "%d дн. назад"
|
||||
@@ -0,0 +1,269 @@
|
||||
# Turkish translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"Language: tr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "AÇIK"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "KAPALI"
|
||||
|
||||
msgid "on"
|
||||
msgstr "açık"
|
||||
|
||||
msgid "off"
|
||||
msgstr "kapalı"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(yok)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "bayrak dosyası"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "başlatıcı"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "profil"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "belirsiz"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "henüz uygulanmadı"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Autoscroll"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Kapsanan uygulamalar"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d / %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Tanınmadı"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (uygulama listesine bakın)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Orta tıkla yapıştırma"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Yeni uygulamalar"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Son uygulanma"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Uygulamalar bunu bir sonraki açılışta alır."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Autoscroll'u aç veya kapat"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Her şeyi yeniden uygula"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Uygulamalar"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Ayarlar"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Çık"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Devam etmek için bir tuşa basın..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Bir dakika..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Orta tıkla yapıştırmayı kapat"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr ""
|
||||
"Orta tık artık başka hiçbir yerde yapıştırmaz. Bir sonraki girişten itibaren."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Yeni uygulamaları kapsa"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Sonradan kurulan uygulamalar Autoscroll'u kendiliğinden alır."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Ek Chromium argümanları"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Autoscroll argümanıyla birlikte her uygulamaya verilir."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Yukarı/Aşağı: seç, Boşluk: değiştir, q: geri"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Boşlukla ayrılmış argümanlar, hiçbiri için boş bırakın:"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Ayar kaydedilemedi."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Kurulu uygulamalara bakılıyor..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Desteklenen uygulama bulunamadı."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr "\"belirsiz\" olarak işaretlenenler burada açılana kadar kapalı kalır."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Autoscroll kapalı. Kapsanacak olanlar bunlar."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Yukarı/Aşağı: seç, Boşluk: aç veya kapat, q: geri"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Ara:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Aramak için yazın, Aşağı: listeye git, Esc: geri"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Eşleşme yok."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Autoscroll %d uygulama için açık."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Zaten çalışan uygulamalar bunu yeniden başlatıldıklarında alır."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Yeni uygulamaları izleme başlatılamadı."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"systemd kullanıcı oturumu yok, bu yüzden yeni kurulan uygulamalar "
|
||||
"kendiliğinden algılanmayacak."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Autoscroll kapalı ve değiştirilen her şey geri alındı."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Autoscroll kapalı. Önce açın."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "%d uygulamaya yeniden uygulandı."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "%d uygulamaya uygulandı."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Zaten %d uygulamaya uygulanmış."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam arayüzünü, bu sürümün Steam fark etmeden değiştiremeyeceği bir şekilde "
|
||||
"başlatıyor; olduğu gibi bırakılıyor."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"Steam arayüzüne, Steam'in yeniden kuracağı bir dosya bırakmadan bayrak "
|
||||
"verilemiyor; değişiklik geri alındı."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Orta tık bir sonraki girişte yapıştırmayı bırakır."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Kullanım: middleclick-autoscroll [komut]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Komutlar:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Autoscroll'u aç"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Kapat ve her şeyi geri al"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Son çalıştırmadan beri kurulan her şeye uygula"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Her şeyi geri al ve yeniden uygula"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Neyin kapsandığını göster"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Bulunan tüm uygulamaları listele"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Bu yardımı göster"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Sürümü göster"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Komut verilmezse etkileşimli bir menü gösterilir."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Bilinmeyen komut: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "hiç"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "az önce"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "1 dakika önce"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "%d dakika önce"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "1 saat önce"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "%d saat önce"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "1 gün önce"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "%d gün önce"
|
||||
@@ -0,0 +1,269 @@
|
||||
# Ukrainian translation for middleclick-autoscroll.
|
||||
# Machine translated. Corrections are welcome.
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# This file is distributed under the same license as middleclick-autoscroll.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: middleclick-autoscroll 1.6.0\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/LoonixTools/middleclick-autoscroll/"
|
||||
"issues\n"
|
||||
"POT-Creation-Date: 2026-09-24 00:00+0200\n"
|
||||
"PO-Revision-Date: 2026-09-24 00:00+0200\n"
|
||||
"Last-Translator: machine translation\n"
|
||||
"Language-Team: Ukrainian\n"
|
||||
"Language: uk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Status labels
|
||||
msgid "ON"
|
||||
msgstr "УВІМК"
|
||||
|
||||
msgid "OFF"
|
||||
msgstr "ВИМК"
|
||||
|
||||
msgid "on"
|
||||
msgstr "увімк"
|
||||
|
||||
msgid "off"
|
||||
msgstr "вимк"
|
||||
|
||||
msgid "(none)"
|
||||
msgstr "(немає)"
|
||||
|
||||
msgid "flag file"
|
||||
msgstr "файл прапорців"
|
||||
|
||||
msgid "launcher"
|
||||
msgstr "ярлик"
|
||||
|
||||
msgid "profile"
|
||||
msgstr "профіль"
|
||||
|
||||
msgid "cannot tell"
|
||||
msgstr "невідомо"
|
||||
|
||||
msgid "not patched yet"
|
||||
msgstr "ще не застосовано"
|
||||
|
||||
#. Status block
|
||||
msgid "Autoscroll"
|
||||
msgstr "Автопрокручування"
|
||||
|
||||
msgid "Applications covered"
|
||||
msgstr "Охоплені застосунки"
|
||||
|
||||
msgid "%d of %d"
|
||||
msgstr "%d з %d"
|
||||
|
||||
msgid "Not identified"
|
||||
msgstr "Не розпізнано"
|
||||
|
||||
msgid "%d (see the applications list)"
|
||||
msgstr "%d (див. список застосунків)"
|
||||
|
||||
msgid "Steam"
|
||||
msgstr "Steam"
|
||||
|
||||
msgid "Middle-click paste"
|
||||
msgstr "Вставлення середньою кнопкою"
|
||||
|
||||
msgid "New applications"
|
||||
msgstr "Нові застосунки"
|
||||
|
||||
msgid "Last applied"
|
||||
msgstr "Востаннє застосовано"
|
||||
|
||||
msgid "Applications pick this up the next time they are started."
|
||||
msgstr "Застосунки підхоплять це під час наступного запуску."
|
||||
|
||||
#. Menu
|
||||
msgid "Turn autoscroll on or off"
|
||||
msgstr "Увімкнути або вимкнути автопрокручування"
|
||||
|
||||
msgid "Re-apply everything"
|
||||
msgstr "Застосувати все знову"
|
||||
|
||||
msgid "Applications"
|
||||
msgstr "Застосунки"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Налаштування"
|
||||
|
||||
msgid "Quit"
|
||||
msgstr "Вийти"
|
||||
|
||||
msgid "Press any key to continue..."
|
||||
msgstr "Натисніть будь-яку клавішу..."
|
||||
|
||||
msgid "One moment..."
|
||||
msgstr "Хвилинку..."
|
||||
|
||||
#. Settings
|
||||
msgid "Turn off middle-click paste"
|
||||
msgstr "Вимкнути вставлення середньою кнопкою"
|
||||
|
||||
msgid ""
|
||||
"Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
msgstr "Середня кнопка більше ніде не вставляє. З наступного входу в систему."
|
||||
|
||||
msgid "Cover new applications"
|
||||
msgstr "Охоплювати нові застосунки"
|
||||
|
||||
msgid "Applications installed later get autoscroll on their own."
|
||||
msgstr "Застосунки, встановлені пізніше, отримають автопрокручування самі."
|
||||
|
||||
msgid "More Chromium arguments"
|
||||
msgstr "Інші аргументи Chromium"
|
||||
|
||||
msgid "Passed to every application along with the autoscroll one."
|
||||
msgstr "Передаються кожному застосунку разом з аргументом автопрокручування."
|
||||
|
||||
msgid "Up/Down: select, Space: change, q: back"
|
||||
msgstr "Вгору/Вниз: вибір, Пробіл: змінити, q: назад"
|
||||
|
||||
msgid "Arguments separated by spaces, empty for none:"
|
||||
msgstr "Аргументи через пробіл (порожньо, якщо не потрібні):"
|
||||
|
||||
msgid "Could not save the setting."
|
||||
msgstr "Не вдалося зберегти налаштування."
|
||||
|
||||
#. Applications screen
|
||||
msgid "Looking at the installed applications..."
|
||||
msgstr "Перегляд встановлених застосунків..."
|
||||
|
||||
msgid "No supported applications found."
|
||||
msgstr "Підтримуваних застосунків не знайдено."
|
||||
|
||||
msgid "Anything marked \"cannot tell\" stays off until it is turned on here."
|
||||
msgstr ""
|
||||
"Позначене як «невідомо» лишається вимкненим, доки його тут не увімкнуть."
|
||||
|
||||
msgid "Autoscroll is off. This is what would be covered."
|
||||
msgstr "Автопрокручування вимкнено. Ось що було б охоплено."
|
||||
|
||||
msgid "Up/Down: select, Space: turn on or off, q: back"
|
||||
msgstr "Вгору/Вниз: вибір, Пробіл: увімкнути чи вимкнути, q: назад"
|
||||
|
||||
msgid "Search:"
|
||||
msgstr "Пошук:"
|
||||
|
||||
msgid "Type to search, Down: go to the list, Esc: back"
|
||||
msgstr "Друкуйте для пошуку, Вниз: до списку, Esc: назад"
|
||||
|
||||
msgid "No matches."
|
||||
msgstr "Нічого не знайдено."
|
||||
|
||||
#. Results
|
||||
msgid "Autoscroll is on for %d applications."
|
||||
msgstr "Автопрокручування увімкнено. Застосунків: %d."
|
||||
|
||||
msgid ""
|
||||
"Applications that are already running pick this up when they are restarted."
|
||||
msgstr "Уже запущені застосунки підхоплять це після перезапуску."
|
||||
|
||||
msgid "Could not start the watcher for newly installed applications."
|
||||
msgstr "Не вдалося запустити стеження за новими застосунками."
|
||||
|
||||
msgid ""
|
||||
"No systemd user session, so newly installed applications will not be picked "
|
||||
"up on their own."
|
||||
msgstr ""
|
||||
"Немає користувацького сеансу systemd, тож нові застосунки не буде підхоплено "
|
||||
"самостійно."
|
||||
|
||||
msgid "Autoscroll is off and everything that was changed has been put back."
|
||||
msgstr "Автопрокручування вимкнено, усі зміни скасовано."
|
||||
|
||||
msgid "Autoscroll is off. Turn it on first."
|
||||
msgstr "Автопрокручування вимкнено. Спершу увімкніть його."
|
||||
|
||||
msgid "Applied again to %d applications."
|
||||
msgstr "Застосовано знову. Застосунків: %d."
|
||||
|
||||
msgid "Applied to %d applications."
|
||||
msgstr "Застосовано. Застосунків: %d."
|
||||
|
||||
msgid "Already applied to %d applications."
|
||||
msgstr "Уже застосовано. Застосунків: %d."
|
||||
|
||||
msgid ""
|
||||
"Steam starts its interface in a way this version cannot change without Steam "
|
||||
"noticing; leaving it alone."
|
||||
msgstr ""
|
||||
"Steam запускає свій інтерфейс так, що ця версія не може це змінити непомітно "
|
||||
"для Steam; Steam лишається без змін."
|
||||
|
||||
msgid ""
|
||||
"Steam's interface cannot be given the flag without leaving a file Steam "
|
||||
"would reinstall; the change has been taken back."
|
||||
msgstr ""
|
||||
"Інтерфейсу Steam не можна передати прапорець, не залишивши файл, який Steam "
|
||||
"перевстановив би; зміну скасовано."
|
||||
|
||||
msgid "Middle click stops pasting at the next login."
|
||||
msgstr "Середня кнопка перестане вставляти з наступного входу в систему."
|
||||
|
||||
#. Help
|
||||
msgid "Usage: middleclick-autoscroll [command]"
|
||||
msgstr "Використання: middleclick-autoscroll [команда]"
|
||||
|
||||
msgid "Commands:"
|
||||
msgstr "Команди:"
|
||||
|
||||
msgid "Turn autoscroll on"
|
||||
msgstr "Увімкнути автопрокручування"
|
||||
|
||||
msgid "Turn it off and put everything back"
|
||||
msgstr "Вимкнути й усе повернути"
|
||||
|
||||
msgid "Apply to anything installed since the last run"
|
||||
msgstr "Застосувати до всього, що встановлено з минулого запуску"
|
||||
|
||||
msgid "Take everything back and apply it again"
|
||||
msgstr "Усе скасувати й застосувати знову"
|
||||
|
||||
msgid "Show what is covered"
|
||||
msgstr "Показати, що охоплено"
|
||||
|
||||
msgid "List every application that was found"
|
||||
msgstr "Показати всі знайдені застосунки"
|
||||
|
||||
msgid "Show this help"
|
||||
msgstr "Показати цю довідку"
|
||||
|
||||
msgid "Show the version"
|
||||
msgstr "Показати версію"
|
||||
|
||||
msgid "Without a command an interactive menu is shown."
|
||||
msgstr "Без команди показується інтерактивне меню."
|
||||
|
||||
msgid "Unknown command: %s"
|
||||
msgstr "Невідома команда: %s"
|
||||
|
||||
#. Time
|
||||
msgid "never"
|
||||
msgstr "ніколи"
|
||||
|
||||
msgid "just now"
|
||||
msgstr "щойно"
|
||||
|
||||
msgid "1 minute ago"
|
||||
msgstr "1 хвилину тому"
|
||||
|
||||
msgid "%d minutes ago"
|
||||
msgstr "%d хв тому"
|
||||
|
||||
msgid "1 hour ago"
|
||||
msgstr "1 годину тому"
|
||||
|
||||
msgid "%d hours ago"
|
||||
msgstr "%d год тому"
|
||||
|
||||
msgid "1 day ago"
|
||||
msgstr "1 день тому"
|
||||
|
||||
msgid "%d days ago"
|
||||
msgstr "%d дн. тому"
|
||||
@@ -0,0 +1,27 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-8.7 17.8 437.4 437.4" width="512" height="512">
|
||||
<defs>
|
||||
<linearGradient id="shell" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#ffffff"/><stop offset="1" stop-color="#dfe7f0"/></linearGradient>
|
||||
<linearGradient id="wheel" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#5fd0ff"/><stop offset="1" stop-color="#0c62a0"/></linearGradient>
|
||||
<radialGradient id="glow" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#2aa7e8" stop-opacity=".55"/><stop offset="1" stop-color="#2aa7e8" stop-opacity="0"/></radialGradient>
|
||||
<filter id="soft" x="-20%" y="-20%" width="140%" height="150%"><feDropShadow dx="0" dy="10" stdDeviation="12" flood-color="#04213a" flood-opacity=".35"/></filter>
|
||||
<linearGradient id="disc" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#5fd0ff"/><stop offset="1" stop-color="#0c62a0"/></linearGradient>
|
||||
</defs>
|
||||
<g filter="url(#soft)">
|
||||
<circle cx="200" cy="200" r="150" fill="url(#disc)"/>
|
||||
<g fill="#ffffff">
|
||||
<path d="M200 70 L236 118 L164 118 Z"/>
|
||||
<path d="M200 330 L236 282 L164 282 Z"/>
|
||||
<path d="M70 200 L118 164 L118 236 Z" opacity=".6"/>
|
||||
<path d="M330 200 L282 164 L282 236 Z" opacity=".6"/>
|
||||
<circle cx="200" cy="200" r="22"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="translate(150 150) scale(.62) rotate(-12 256 266)" filter="url(#soft)">
|
||||
<rect x="166" y="96" width="180" height="340" rx="90" fill="url(#shell)" stroke="#c9d5e3" stroke-width="4"/>
|
||||
<path d="M170 228 C 210 240, 302 240, 342 228" fill="none" stroke="#c9d5e3" stroke-width="5" stroke-linecap="round"/>
|
||||
<path d="M256 100 L256 236" stroke="#c9d5e3" stroke-width="5"/>
|
||||
<circle cx="256" cy="168" r="70" fill="url(#glow)"/>
|
||||
<rect x="236" y="128" width="40" height="80" rx="20" fill="url(#wheel)"/>
|
||||
<g stroke="#ffffff" stroke-opacity=".55" stroke-width="5" stroke-linecap="round"><line x1="246" y1="148" x2="266" y2="148"/><line x1="246" y1="168" x2="266" y2="168"/><line x1="246" y1="188" x2="266" y2="188"/></g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
@@ -0,0 +1,48 @@
|
||||
[Unit]
|
||||
Description=Watch for newly installed applications
|
||||
Documentation=man:middleclick-autoscroll(1)
|
||||
|
||||
[Path]
|
||||
# Every way an application can turn up: a package, a Flatpak, a snap, something
|
||||
# installed by hand. Watching where the launchers land means there is no hook
|
||||
# to write per package manager: pacman, apt, dnf, zypper, flatpak, snapd and
|
||||
# an AppImage manager all end up here.
|
||||
#
|
||||
# A directory that does not exist on this system costs nothing: systemd watches
|
||||
# the nearest parent that does and picks the path up when it appears.
|
||||
PathModified=/usr/share/applications
|
||||
PathModified=/usr/local/share/applications
|
||||
PathModified=%h/.local/share/applications
|
||||
PathModified=%h/.config/autostart
|
||||
PathModified=/var/lib/flatpak/exports/share/applications
|
||||
PathModified=%h/.local/share/flatpak/exports/share/applications
|
||||
PathModified=/var/lib/snapd/desktop/applications
|
||||
|
||||
# NixOS and Guix build the search path out of profiles rather than /usr.
|
||||
PathModified=/run/current-system/sw/share/applications
|
||||
PathModified=%h/.nix-profile/share/applications
|
||||
PathModified=%h/.guix-profile/share/applications
|
||||
|
||||
# The desktop folder is watched too. Steam writes a shortcut there for every
|
||||
# game somebody asks for one, and starting a game is a Steam start. But the
|
||||
# folder's name is translated and cannot be written down here. `enable` adds it
|
||||
# in a drop-in, from the name this system uses.
|
||||
|
||||
# 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
|
||||
# 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/.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/data/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
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Apply middle-click autoscroll
|
||||
Documentation=man:middleclick-autoscroll(1)
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@BINDIR@/middleclick-autoscroll apply --quiet
|
||||
# Scanning binaries for their engine is the slow part, and it is cached; a run
|
||||
# that takes longer than this has found something pathological and should not
|
||||
# be left running at every login.
|
||||
TimeoutStartSec=180
|
||||
Nice=10
|
||||
IOSchedulingClass=idle
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<repomd xmlns="http://linux.duke.edu/metadata/repo" xmlns:rpm="http://linux.duke.edu/metadata/rpm">
|
||||
<revision>1790840268</revision>
|
||||
<data type="primary">
|
||||
<checksum type="sha256">4b563420de21c9a74471f48c37e4902fe0dea9fa3de897fb5239dfb9fa7e597b</checksum>
|
||||
<open-checksum type="sha256">b62388ee596ee8814709637203883274686ae01f265690ac3983b499e0b11139</open-checksum>
|
||||
<location href="repodata/4b563420de21c9a74471f48c37e4902fe0dea9fa3de897fb5239dfb9fa7e597b-primary.xml.gz"/>
|
||||
<timestamp>1790840268</timestamp>
|
||||
<size>2504</size>
|
||||
<open-size>29088</open-size>
|
||||
</data>
|
||||
<data type="filelists">
|
||||
<checksum type="sha256">f6259cf27cddba656fd6e0666db32dcd6a71db222654f7b2bd2281b1dc25c353</checksum>
|
||||
<open-checksum type="sha256">fa7899b8740b988a17d9f72c8ecd0ef193a38405e8071d7b64f65068fd4ff8d1</open-checksum>
|
||||
<location href="repodata/f6259cf27cddba656fd6e0666db32dcd6a71db222654f7b2bd2281b1dc25c353-filelists.xml.gz"/>
|
||||
<timestamp>1790840268</timestamp>
|
||||
<size>1138</size>
|
||||
<open-size>18029</open-size>
|
||||
</data>
|
||||
<data type="other">
|
||||
<checksum type="sha256">1344a28dfc9da86fad8e59a87a6b89e223f621c91bcb44ec45e985f8ba30324b</checksum>
|
||||
<open-checksum type="sha256">b52ce2708e105e7795317490d3ef938dde406f106f8f76712b949655e2db3403</open-checksum>
|
||||
<location href="repodata/1344a28dfc9da86fad8e59a87a6b89e223f621c91bcb44ec45e985f8ba30324b-other.xml.gz"/>
|
||||
<timestamp>1790840268</timestamp>
|
||||
<size>731</size>
|
||||
<open-size>2354</open-size>
|
||||
</data>
|
||||
<data type="primary_db">
|
||||
<checksum type="sha256">89c9eec1c2f214bf8bb46dc9f53bf5645a7b47a156c21e0f4155de3a7797d36a</checksum>
|
||||
<open-checksum type="sha256">57840c8fb2286ddd2642047a3aae97f4c6d37be75daa0a451bd031b291ad2004</open-checksum>
|
||||
<location href="repodata/89c9eec1c2f214bf8bb46dc9f53bf5645a7b47a156c21e0f4155de3a7797d36a-primary.sqlite.bz2"/>
|
||||
<timestamp>1790840268</timestamp>
|
||||
<size>6421</size>
|
||||
<open-size>122880</open-size>
|
||||
<database_version>10</database_version>
|
||||
</data>
|
||||
<data type="filelists_db">
|
||||
<checksum type="sha256">60aa9c1e6a16d73bd88849fca87a5bb98b4a8c6b0b6407d58bf75c0c811306b9</checksum>
|
||||
<open-checksum type="sha256">6d49dea4b82189307007448f86ece1fd4a148fd312fa689705ca1c578f769048</open-checksum>
|
||||
<location href="repodata/60aa9c1e6a16d73bd88849fca87a5bb98b4a8c6b0b6407d58bf75c0c811306b9-filelists.sqlite.bz2"/>
|
||||
<timestamp>1790840268</timestamp>
|
||||
<size>4827</size>
|
||||
<open-size>49152</open-size>
|
||||
<database_version>10</database_version>
|
||||
</data>
|
||||
<data type="other_db">
|
||||
<checksum type="sha256">c78f5fbc5959e285fbbed110f15a760be8fff5d2b0975267880747371e1b3fbf</checksum>
|
||||
<open-checksum type="sha256">f5eed730511defdda7bcb56d23280c80a06513b1a10fcd948af29d28584b36db</open-checksum>
|
||||
<location href="repodata/c78f5fbc5959e285fbbed110f15a760be8fff5d2b0975267880747371e1b3fbf-other.sqlite.bz2"/>
|
||||
<timestamp>1790840268</timestamp>
|
||||
<size>1406</size>
|
||||
<open-size>24576</open-size>
|
||||
<database_version>10</database_version>
|
||||
</data>
|
||||
</repomd>
|
||||
@@ -1,16 +0,0 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCgAdFiEEY4PI9Dc7LesUkuKsVaPG3ykUTHkFAmq+DcwACgkQVaPG3ykU
|
||||
THn9+A/8DbSMDJvhXoreSx3Qo75rLexIQRogCBepq0PdofvntYEVsr/yIdwun1aj
|
||||
vNjIpIPu/RSn+C3zlIW1WuGBmVx80Tg3FZVWDWftT2SZvaOQD3ab7eNbX7xdCsZJ
|
||||
3ifkYD9hoLNROYqSvxNkkTJ5J+DYF8hdGXcg2AKLGkFcivx+ZYgXX0NfXKBYXzBC
|
||||
pLAi1GFFTsI6hJvbGbPkwMxZu9nB8ptLH8Lfk7tsYWnRFN6uM+nmRAVue2SGv+ib
|
||||
VgWgD8ZYCWaTP4T/fpMkqVwyJzRpLACqMBJov4WF3KWk9bJn/H8/VPnPQ3+uix6X
|
||||
/fUNPvYpx1QgSNPbPBfLx0hkMp6n1Z3LvnaeACcx4KqusD0SByhWspdAzxIQdUZ3
|
||||
/Ai/F0MMYNaEg8jD4qwDZMWMZHl21Sa9afCa2xv4e/xvytdoCaSJ4x6YiMLgrcp5
|
||||
qohbP38aYNiCgL7inDkRlOLU+pZTsLVfvZkhnjT4NZkCdQQpk2nspaLIEj639b23
|
||||
b/Ru6d+8U9riH9yBcIGQj25phOEbWosZpsd6uN/RRd3M05dtDlFrGZP5gvkbY4Ui
|
||||
Cq6XzVRYbFnHZLvcnDLNcGz4o0ilIpqqbH6ytpL8DyKK4oW3jlyc61OJElnrObgU
|
||||
SYYV76sCoDjCwhFeRFVX/5G9av0WFfHCVIfB8vzLlKVuRQKc00g=
|
||||
=xMAp
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1,334 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Deciding what happens to each application, and doing it.
|
||||
#
|
||||
# The routing lives in mca_route and is used twice: once by mca_apply to patch
|
||||
# and once by the applications screen to show what would happen. Keeping it in
|
||||
# one function is what makes that screen trustworthy: it is not a description
|
||||
# of the behaviour, it is the behaviour.
|
||||
|
||||
MCA_CHANGES=0
|
||||
MCA_ROUTES=()
|
||||
|
||||
# mca_route <kind> <id> <program> [packaging]
|
||||
# What happens to one application, left in MCA_ROUTE:
|
||||
# flags - the launcher reads a flag file; write it there
|
||||
# desktop - no flag file; shadow or edit the desktop entry
|
||||
# steam - Steam's own two-part treatment
|
||||
# spotify - the flag goes into spotify-launcher's configuration file
|
||||
# gecko - a setting in each of the application's profiles
|
||||
# unknown - cannot tell what engine this is (an AppImage), so nothing is done
|
||||
# off - detected, but switched off
|
||||
#
|
||||
# Assigns rather than prints so the memo inside mca_has_flags_file survives:
|
||||
# from inside a command substitution it would be filled in a subshell and
|
||||
# thrown away, which is the whole point of having it.
|
||||
MCA_ROUTE=''
|
||||
|
||||
mca_route() {
|
||||
local kind="$1" id="$2" prog="$3" packaging="${4:-native}"
|
||||
|
||||
if [[ $kind == steam || $kind == spotify || $kind == gecko ]]; then
|
||||
if mca_config_list_has Skip "$id"; then
|
||||
MCA_ROUTE=off
|
||||
else
|
||||
MCA_ROUTE="$kind"
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
if ! mca_kind_wanted "$kind" "$id"; then
|
||||
[[ $kind == unknown ]] && MCA_ROUTE=unknown || MCA_ROUTE=off
|
||||
return
|
||||
fi
|
||||
|
||||
# A Flatpak or a snap carries its own copy of everything and sees none of
|
||||
# the host's wrappers, so the desktop entry is the only way in.
|
||||
if [[ $packaging != native ]]; then
|
||||
MCA_ROUTE=desktop
|
||||
return
|
||||
fi
|
||||
|
||||
if mca_has_flags_file "$prog"; then
|
||||
MCA_ROUTE=flags
|
||||
else
|
||||
MCA_ROUTE=desktop
|
||||
fi
|
||||
}
|
||||
|
||||
# mca_apply
|
||||
# Brings everything into line with the current settings. Safe to run as often
|
||||
# as it likes to be. It writes only what differs, which is what keeps the
|
||||
# watcher from chasing its own changes.
|
||||
mca_apply() {
|
||||
local i id file prog kind packaging route steam_done=0 spotify_done=0
|
||||
|
||||
MCA_CHANGES=0
|
||||
MCA_ROUTES=()
|
||||
MCA_N_ON=0; MCA_N_OFF=0; MCA_N_UNKNOWN=0
|
||||
MCA_GECKO_SEEN=(); MCA_GECKO_WATCH=()
|
||||
|
||||
mca_config_load
|
||||
|
||||
# An installation that was set up by a version whose answer was a different
|
||||
# flag still has that flag where it put it, and nothing below would touch it
|
||||
# again: a file that is already marked as patched is left alone. Taking
|
||||
# everything back once, here, is what carries such an installation over.
|
||||
if [[ "$(mca_state_read flag_scheme 1)" != "$MCA_FLAG_SCHEME" ]]; then
|
||||
mca_revert_all
|
||||
fi
|
||||
|
||||
mca_scan
|
||||
|
||||
for i in "${!MCA_IDS[@]}"; do
|
||||
id="${MCA_IDS[i]}"
|
||||
file="${MCA_FILES[i]}"
|
||||
prog="${MCA_PROGS[i]}"
|
||||
kind="${MCA_KINDS[i]}"
|
||||
packaging="${MCA_PACKAGING[i]}"
|
||||
|
||||
mca_route "$kind" "$id" "$prog" "$packaging"
|
||||
route="$MCA_ROUTE"
|
||||
MCA_ROUTES[i]="$route"
|
||||
|
||||
case "$route" in
|
||||
unknown) MCA_N_UNKNOWN=$(( MCA_N_UNKNOWN + 1 )) ;;
|
||||
off) MCA_N_OFF=$(( MCA_N_OFF + 1 )) ;;
|
||||
*) MCA_N_ON=$(( MCA_N_ON + 1 )) ;;
|
||||
esac
|
||||
|
||||
case "$route" in
|
||||
flags)
|
||||
mca_flags_apply "$prog" "$kind" \
|
||||
|| mca_desktop_apply "$id" "$file" "$(mca_flags "$kind")"
|
||||
;;
|
||||
desktop)
|
||||
mca_desktop_apply "$id" "$file" "$(mca_flags "$kind")"
|
||||
;;
|
||||
steam)
|
||||
# Nothing is done to the entry itself: what Steam needs is
|
||||
# in its own installation, and the same patch serves every
|
||||
# way of starting the client.
|
||||
if (( ! steam_done )); then
|
||||
mca_steam_apply
|
||||
steam_done=1
|
||||
fi
|
||||
;;
|
||||
spotify)
|
||||
(( spotify_done )) || mca_spotify_apply
|
||||
spotify_done=1
|
||||
;;
|
||||
gecko)
|
||||
mca_gecko_apply "${MCA_PROFILES[i]}" "$packaging" "$prog"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Steam is worth patching even when its desktop entry is missing. A user
|
||||
# who starts it from a script or a game launcher still gets the interface.
|
||||
if (( ! steam_done )) && mca_steam_wanted && mca_steam_installed; then
|
||||
mca_steam_apply
|
||||
fi
|
||||
|
||||
# Autostart entries. A Chromium application that starts itself at login
|
||||
# points straight at its binary and never reads the entry in the menu, so
|
||||
# Discord at login used to behave differently from Discord from the menu.
|
||||
mca_autostart_apply
|
||||
|
||||
# Shortcuts on the desktop itself, which nothing above has seen: the XDG
|
||||
# search path does not go there. Each entry is gated on its own, so there is
|
||||
# nothing to check out here.
|
||||
mca_shortcuts_apply
|
||||
|
||||
# Not about any one application: middle click goes on pasting everywhere
|
||||
# else on the desktop, and KDE is the one desktop that can be told not to.
|
||||
# See kde.sh.
|
||||
[[ $CFG_PASTE == yes ]] && mca_kde_paste_apply
|
||||
|
||||
mca_prune_orphans
|
||||
|
||||
if (( MCA_CHANGES )) && mca_have update-desktop-database; then
|
||||
update-desktop-database "$MCA_APPDIR" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
mca_state_write last_apply "$(date +%s)"
|
||||
mca_state_write flag_scheme "$MCA_FLAG_SCHEME"
|
||||
return 0
|
||||
}
|
||||
|
||||
# mca_revert
|
||||
# Undoes every recorded change and forgets them.
|
||||
mca_revert() {
|
||||
MCA_CHANGES=0
|
||||
mca_revert_all
|
||||
|
||||
if (( MCA_CHANGES )) && mca_have update-desktop-database; then
|
||||
update-desktop-database "$MCA_APPDIR" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The watcher
|
||||
# ---------------------------------------------------------------------------
|
||||
# A systemd user path unit watching every directory a desktop entry can appear
|
||||
# in. That covers a package from whatever the distribution's package manager
|
||||
# is, a Flatpak, a snap, an AppImage registered by hand and a Steam client
|
||||
# update, without a hook per package manager.
|
||||
|
||||
MCA_UNIT_PATH="middleclick-autoscroll.path"
|
||||
MCA_UNIT_SERVICE="middleclick-autoscroll.service"
|
||||
|
||||
# The desktop folder is watched like every other directory an entry can turn up
|
||||
# in, but its name is translated and the unit that ships with the package
|
||||
# cannot know it. So that one path is written here, into a drop-in, from the
|
||||
# name this system actually uses. The same goes for where Firefox and its forks
|
||||
# keep their profiles, which depends on what is installed.
|
||||
MCA_UNIT_DROPIN="${MCA_XDG_CONFIG}/systemd/user/${MCA_UNIT_PATH}.d"
|
||||
|
||||
mca_watch_available() {
|
||||
mca_have systemctl && [[ -n ${XDG_RUNTIME_DIR:-} || -S "/run/user/$(id -u)/bus" ]]
|
||||
}
|
||||
|
||||
mca_watch_enabled() {
|
||||
mca_watch_available || return 1
|
||||
systemctl --user is-enabled --quiet "$MCA_UNIT_PATH" 2>/dev/null
|
||||
}
|
||||
|
||||
# _mca_watch_dropin_write
|
||||
# Returns 0 when the drop-in changed and systemd has to be told, 1 when it was
|
||||
# already right. That is the same contract as mca_write_if_changed, for the
|
||||
# same reason: the watcher runs after every desktop entry that appears anywhere on
|
||||
# the system, and a daemon-reload each time would be absurd.
|
||||
_mca_watch_dropin_write() {
|
||||
local dir path content='' changed=1
|
||||
|
||||
# A per cent sign starts a specifier in a unit file and has to be doubled
|
||||
# to mean itself. Rare in a folder name, fatal when it happens.
|
||||
dir="$(mca_desktop_folder)"
|
||||
[[ -d $dir ]] && content="[Path]"$'\n'"PathModified=${dir//%/%%}"$'\n'
|
||||
_mca_watch_conf desktop.conf "$content" && changed=0
|
||||
|
||||
# Filled by the apply that ran just before. A new profile is added there.
|
||||
content=''
|
||||
for path in "${MCA_GECKO_WATCH[@]}"; do
|
||||
content+="PathChanged=${path//%/%%}"$'\n'
|
||||
done
|
||||
[[ -n $content ]] && content="[Path]"$'\n'"$content"
|
||||
_mca_watch_conf profiles.conf "$content" && changed=0
|
||||
|
||||
return "$changed"
|
||||
}
|
||||
|
||||
# _mca_watch_conf <name> <content>
|
||||
# Writes one drop-in, or removes it when the content is empty. Returns 0 when
|
||||
# it changed.
|
||||
_mca_watch_conf() {
|
||||
local file="$MCA_UNIT_DROPIN/$1"
|
||||
|
||||
if [[ -n $2 ]]; then
|
||||
mca_write_if_changed "$file" "$2"
|
||||
return
|
||||
fi
|
||||
|
||||
[[ -e $file ]] || return 1
|
||||
rm -f -- "$file"
|
||||
rmdir "$MCA_UNIT_DROPIN" 2>/dev/null || true
|
||||
return 0
|
||||
}
|
||||
|
||||
_mca_watch_dropin_remove() {
|
||||
local changed=1
|
||||
_mca_watch_conf desktop.conf '' && changed=0
|
||||
_mca_watch_conf profiles.conf '' && changed=0
|
||||
return "$changed"
|
||||
}
|
||||
|
||||
mca_watch_enable() {
|
||||
mca_watch_available || return 1
|
||||
_mca_watch_dropin_write
|
||||
systemctl --user daemon-reload > /dev/null 2>&1 || true
|
||||
systemctl --user enable --now "$MCA_UNIT_PATH" > /dev/null 2>&1 || return 1
|
||||
systemctl --user enable "$MCA_UNIT_SERVICE" > /dev/null 2>&1 || true
|
||||
return 0
|
||||
}
|
||||
|
||||
# mca_watch_refresh
|
||||
# Keeps a running watcher in step with a desktop folder that has been renamed,
|
||||
# which is what a change of session language does to it, and with the Firefox
|
||||
# profiles there are now.
|
||||
mca_watch_refresh() {
|
||||
mca_watch_available || return 0
|
||||
_mca_watch_dropin_write || return 0
|
||||
systemctl --user daemon-reload > /dev/null 2>&1 || true
|
||||
systemctl --user restart "$MCA_UNIT_PATH" > /dev/null 2>&1 || true
|
||||
return 0
|
||||
}
|
||||
|
||||
mca_watch_disable() {
|
||||
mca_watch_available || return 0
|
||||
systemctl --user disable --now "$MCA_UNIT_PATH" > /dev/null 2>&1 || true
|
||||
systemctl --user disable "$MCA_UNIT_SERVICE" > /dev/null 2>&1 || true
|
||||
_mca_watch_dropin_remove && systemctl --user daemon-reload > /dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reporting
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# mca_count_routes
|
||||
# Fills MCA_N_ON / MCA_N_OFF / MCA_N_UNKNOWN from a scan, without writing
|
||||
# anything. Used by the status block and the menu header.
|
||||
MCA_N_ON=0
|
||||
MCA_N_OFF=0
|
||||
MCA_N_UNKNOWN=0
|
||||
|
||||
mca_count_routes() {
|
||||
local i route
|
||||
|
||||
MCA_N_ON=0; MCA_N_OFF=0; MCA_N_UNKNOWN=0
|
||||
MCA_ROUTES=()
|
||||
|
||||
for i in "${!MCA_IDS[@]}"; do
|
||||
mca_route "${MCA_KINDS[i]}" "${MCA_IDS[i]}" "${MCA_PROGS[i]}" \
|
||||
"${MCA_PACKAGING[i]}"
|
||||
route="$MCA_ROUTE"
|
||||
MCA_ROUTES[i]="$route"
|
||||
case "$route" in
|
||||
unknown) MCA_N_UNKNOWN=$(( MCA_N_UNKNOWN + 1 )) ;;
|
||||
off) MCA_N_OFF=$(( MCA_N_OFF + 1 )) ;;
|
||||
*) MCA_N_ON=$(( MCA_N_ON + 1 )) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# mca_steam_wanted
|
||||
# Whether Steam gets patched, from the routes of the last scan: yes unless it
|
||||
# was turned off in the applications list. Also yes when it has no entry there
|
||||
# at all, so there is nothing to turn off.
|
||||
mca_steam_wanted() {
|
||||
local i seen=0
|
||||
|
||||
for i in "${!MCA_KINDS[@]}"; do
|
||||
[[ ${MCA_KINDS[i]} == steam ]] || continue
|
||||
[[ ${MCA_ROUTES[i]} == steam ]] && return 0
|
||||
seen=1
|
||||
done
|
||||
(( ! seen ))
|
||||
}
|
||||
|
||||
# mca_route_label <route>
|
||||
mca_route_label() {
|
||||
case "$1" in
|
||||
flags) mca_msg "flag file" ;;
|
||||
desktop) mca_msg "launcher" ;;
|
||||
steam) mca_msg "Steam" ;;
|
||||
spotify) printf 'spotify-launcher' ;;
|
||||
gecko) mca_msg "profile" ;;
|
||||
unknown) mca_msg "cannot tell" ;;
|
||||
*) mca_msg "off" ;;
|
||||
esac
|
||||
printf '\n'
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Paths, translations, output helpers and the change ledger.
|
||||
#
|
||||
# Everything here runs unprivileged. This tool only ever writes inside the
|
||||
# user's own home (flag files, desktop entries, Steam's own scripts), so there
|
||||
# is no system state to guard and nothing that needs root.
|
||||
|
||||
MCA_VERSION="@VERSION@"
|
||||
MCA_NAME="middleclick-autoscroll"
|
||||
MCA_PRETTY="Middle-Click Autoscroll"
|
||||
|
||||
# The whole point of the package. Blink implements Windows-style autoscroll
|
||||
# behind a runtime flag that Chromium does not turn on for Linux, because
|
||||
# middle click is taken by primary-selection paste there.
|
||||
#
|
||||
# There are two ways to ask for that flag, and which one is right depends on
|
||||
# what is being started:
|
||||
#
|
||||
# --enable-blink-features=MiddleClickAutoscroll is the one that works
|
||||
# everywhere, and the only one that works in the Chromium versions embedded
|
||||
# applications ship: Steam's CEF, an Electron a few years old, anything
|
||||
# before Chromium 124. A browser started with it puts a yellow bar above the
|
||||
# page saying an unsupported command line flag is in use, because Chromium
|
||||
# keeps a list of flags worth warning about and this is on it.
|
||||
#
|
||||
# --enable-features=MiddleClickAutoscroll is not on that list, so a browser
|
||||
# started with it says nothing. Blink generates a feature of the same name
|
||||
# for every one of its runtime flags, which is what makes the two the same
|
||||
# request. But only since Chromium 124. Before that the name means nothing
|
||||
# and is ignored without a word.
|
||||
#
|
||||
# So a browser, which is the only thing that shows the bar and the one thing
|
||||
# that keeps itself up to date, is given the quiet one; everything else keeps
|
||||
# the one that always works.
|
||||
MCA_FLAG="--enable-blink-features=MiddleClickAutoscroll"
|
||||
|
||||
# Two browsers do not answer to the Chromium name. Helium ships the feature
|
||||
# under its own name and sets the runtime flag from that alone. Brave takes
|
||||
# the generated feature away and switches the flag on from one of its own,
|
||||
# spelled MiddelButtonClickAutoscroll. The typo is in Brave's source, and the
|
||||
# correct spelling does nothing. So browsers are asked for all three. A name a
|
||||
# browser does not know is ignored, which is what makes one list safe to hand
|
||||
# to all of them.
|
||||
MCA_BROWSER_FLAG="--enable-features=MiddleClickAutoscroll,HeliumMiddleClickAutoscroll,MiddelButtonClickAutoscroll"
|
||||
|
||||
# The feature names on their own, for merging into a list that an application
|
||||
# (or the user) already carries, and for taking them back out again.
|
||||
MCA_FEATURE="MiddleClickAutoscroll"
|
||||
MCA_BROWSER_FEATURES="MiddleClickAutoscroll,HeliumMiddleClickAutoscroll,MiddelButtonClickAutoscroll"
|
||||
|
||||
# Bumped when the flags above change, and when what gets written where does.
|
||||
# An installation that was set up by a version with a different answer is taken
|
||||
# back and done again once, because a file that is already patched is otherwise
|
||||
# left alone. See mca_apply.
|
||||
#
|
||||
# 3 is what takes -noverifyfiles back out of the Steam entries an older version
|
||||
# put it in.
|
||||
#
|
||||
# 4 adds Brave's name to the browser list, which an entry edited in place would
|
||||
# otherwise never get.
|
||||
MCA_FLAG_SCHEME=4
|
||||
|
||||
MCA_LIBDIR="${MCA_LIBDIR:-@LIBDIR@}"
|
||||
MCA_LOCALEDIR="${MCA_LOCALEDIR:-@LOCALEDIR@}"
|
||||
|
||||
MCA_XDG_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
MCA_XDG_DATA="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
MCA_XDG_STATE="${XDG_STATE_HOME:-$HOME/.local/state}"
|
||||
MCA_XDG_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}"
|
||||
|
||||
MCA_CONFDIR="${MCA_CONFDIR:-${MCA_XDG_CONFIG}/${MCA_NAME}}"
|
||||
MCA_CONFIG="${MCA_CONFIG:-${MCA_CONFDIR}/config}"
|
||||
MCA_STATEDIR="${MCA_STATEDIR:-${MCA_XDG_STATE}/${MCA_NAME}}"
|
||||
MCA_CACHEDIR="${MCA_CACHEDIR:-${MCA_XDG_CACHE}/${MCA_NAME}}"
|
||||
|
||||
# Where generated desktop entries go. A file here shadows the one with the same
|
||||
# name in /usr/share/applications, which is how an application gets extra
|
||||
# command line arguments without touching a file the package manager owns.
|
||||
MCA_APPDIR="${MCA_XDG_DATA}/applications"
|
||||
|
||||
# Copies of every file that is edited in place rather than shadowed.
|
||||
MCA_BACKUPDIR="${MCA_STATEDIR}/backup"
|
||||
|
||||
# One line per change, so `revert` can undo exactly what was done and nothing
|
||||
# else. See mca_ledger_add.
|
||||
MCA_LEDGER="${MCA_STATEDIR}/ledger"
|
||||
|
||||
# The folder the desktop itself shows. Nothing in the XDG search path looks at
|
||||
# it, so an entry that lives only there is invisible to a scan. A game
|
||||
# shortcut dragged onto the desktop is exactly that.
|
||||
#
|
||||
# Its name is translated: Schreibtisch, Bureau, Escritorio. The name in use is
|
||||
# in the file xdg-user-dirs writes, so it is read rather than guessed, and
|
||||
# ~/Desktop is only the fallback for a system that has no such file.
|
||||
mca_desktop_folder() {
|
||||
local file="$MCA_XDG_CONFIG/user-dirs.dirs" line dir=''
|
||||
|
||||
if [[ -r $file ]]; then
|
||||
while IFS= read -r line; do
|
||||
[[ $line == XDG_DESKTOP_DIR=* ]] || continue
|
||||
dir="${line#XDG_DESKTOP_DIR=}"
|
||||
dir="${dir%\"}"; dir="${dir#\"}"
|
||||
done < "$file"
|
||||
dir="${dir/#\$HOME/$HOME}"
|
||||
fi
|
||||
|
||||
[[ $dir == /* ]] || dir="$HOME/Desktop"
|
||||
printf '%s\n' "${dir%/}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Translations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
export TEXTDOMAIN="middleclick-autoscroll"
|
||||
export TEXTDOMAINDIR="${MCA_LOCALEDIR}"
|
||||
|
||||
mca_ui_locale() {
|
||||
local l="${MCA_UI_LOCALE:-}"
|
||||
|
||||
if [[ -z $l ]]; then
|
||||
l="${LC_ALL:-}"
|
||||
[[ -z $l ]] && l="${LC_MESSAGES:-}"
|
||||
[[ -z $l ]] && l="${LANG:-}"
|
||||
fi
|
||||
|
||||
# systemd writes /etc/locale.conf and most distributions use it; Debian and
|
||||
# Ubuntu keep the same LANG= line in /etc/default/locale instead.
|
||||
if [[ -z $l ]]; then
|
||||
local f
|
||||
for f in /etc/locale.conf /etc/default/locale; do
|
||||
[[ -r $f ]] || continue
|
||||
l="$(sed -n 's/^LANG=//p' "$f" | tr -d '"' | head -n1)"
|
||||
[[ -n $l ]] && break
|
||||
done
|
||||
fi
|
||||
|
||||
printf '%s\n' "${l:-C}"
|
||||
}
|
||||
|
||||
# Every gettext lookup is a fork and the settings screen redraws a screenful of
|
||||
# labels per keypress, so results are memoized.
|
||||
declare -A MCA_MSG_CACHE=()
|
||||
|
||||
MCA_MSG_RESULT=''
|
||||
|
||||
# mca_msg_into <locale> <msgid>
|
||||
# Plain lookup with the result in MCA_MSG_RESULT and no printf formatting, for
|
||||
# callers that would otherwise pay a fork per label per frame.
|
||||
mca_msg_into() {
|
||||
local locale="$1" msgid="$2" cachekey
|
||||
cachekey="${locale}"$'\x1f'"${msgid}"
|
||||
|
||||
if [[ -n ${MCA_MSG_CACHE[$cachekey]+set} ]]; then
|
||||
MCA_MSG_RESULT="${MCA_MSG_CACHE[$cachekey]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
MCA_MSG_RESULT="$(LC_ALL="$locale" LANGUAGE="${locale%%.*}" gettext -- "$msgid" 2>/dev/null)"
|
||||
[[ -n $MCA_MSG_RESULT ]] || MCA_MSG_RESULT="$msgid"
|
||||
MCA_MSG_CACHE[$cachekey]="$MCA_MSG_RESULT"
|
||||
return 0
|
||||
}
|
||||
|
||||
# mca_msg_in <locale> <msgid> [printf args...]
|
||||
mca_msg_in() {
|
||||
local locale="$1" msgid="$2" translated cachekey
|
||||
shift 2
|
||||
|
||||
cachekey="${locale}"$'\x1f'"${msgid}"
|
||||
if [[ -n ${MCA_MSG_CACHE[$cachekey]+set} ]]; then
|
||||
translated="${MCA_MSG_CACHE[$cachekey]}"
|
||||
else
|
||||
translated="$(LC_ALL="$locale" LANGUAGE="${locale%%.*}" gettext -- "$msgid" 2>/dev/null)"
|
||||
[[ -n $translated ]] || translated="$msgid"
|
||||
MCA_MSG_CACHE[$cachekey]="$translated"
|
||||
fi
|
||||
|
||||
# With no arguments the message is plain text, not a format string. Feeding
|
||||
# it to printf anyway would turn a literal percent sign in a translation
|
||||
# into an invalid conversion.
|
||||
if (( $# == 0 )); then
|
||||
printf '%s' "$translated"
|
||||
return
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2059 # the format string is the translated message
|
||||
printf -- "$translated" "$@"
|
||||
}
|
||||
|
||||
# mca_msg <msgid> [printf args...]
|
||||
mca_msg() { mca_msg_in "$(mca_ui_locale)" "$@"; }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Output
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Decided once, while stdout is still whatever the process was started with:
|
||||
# testing -t 1 at the point of use is wrong for anything called through $(...),
|
||||
# which sees a pipe and would conclude nobody is watching.
|
||||
MCA_INTERACTIVE=''
|
||||
[[ -t 1 ]] && MCA_INTERACTIVE=1
|
||||
|
||||
if [[ -n $MCA_INTERACTIVE && -z ${NO_COLOR:-} ]]; then
|
||||
MCA_C_RESET=$'\033[0m'
|
||||
MCA_C_BOLD=$'\033[1m'
|
||||
MCA_C_DIM=$'\033[2m'
|
||||
MCA_C_BLUE=$'\033[38;2;23;147;209m'
|
||||
MCA_C_GREEN=$'\033[32m'
|
||||
MCA_C_YELLOW=$'\033[33m'
|
||||
MCA_C_RED=$'\033[31m'
|
||||
else
|
||||
MCA_C_RESET='' MCA_C_BOLD='' MCA_C_DIM='' MCA_C_BLUE=''
|
||||
MCA_C_GREEN='' MCA_C_YELLOW='' MCA_C_RED=''
|
||||
fi
|
||||
|
||||
# --quiet silences progress chatter; errors still go to stderr. Used by the
|
||||
# systemd unit, which has no terminal and logs to the journal anyway.
|
||||
MCA_QUIET=''
|
||||
|
||||
mca_say() { [[ -n $MCA_QUIET ]] || printf '%s\n' "$*"; }
|
||||
mca_head() { printf '\n%s%s%s\n\n' "$MCA_C_BOLD$MCA_C_BLUE" "$*" "$MCA_C_RESET"; }
|
||||
mca_ok() { [[ -n $MCA_QUIET ]] || printf '%s✔%s %s\n' "$MCA_C_GREEN" "$MCA_C_RESET" "$*"; }
|
||||
mca_bad() { printf '%s✘%s %s\n' "$MCA_C_RED" "$MCA_C_RESET" "$*" >&2; }
|
||||
mca_note() { [[ -n $MCA_QUIET ]] || printf '%s•%s %s\n' "$MCA_C_DIM" "$MCA_C_RESET" "$*"; }
|
||||
|
||||
mca_have() { command -v "$1" > /dev/null 2>&1; }
|
||||
|
||||
# Human-readable "x minutes ago" for a unix timestamp. Empty input yields the
|
||||
# translated "never".
|
||||
mca_time_ago() {
|
||||
local ts="$1" now delta
|
||||
|
||||
[[ $ts =~ ^[0-9]+$ ]] || { mca_msg "never"; printf '\n'; return; }
|
||||
|
||||
now="$(date +%s)"
|
||||
delta=$(( now - ts ))
|
||||
(( delta < 0 )) && delta=0
|
||||
|
||||
if (( delta < 60 )); then mca_msg "just now"
|
||||
elif (( delta < 120 )); then mca_msg "1 minute ago"
|
||||
elif (( delta < 3600 )); then mca_msg "%d minutes ago" "$(( delta / 60 ))"
|
||||
elif (( delta < 7200 )); then mca_msg "1 hour ago"
|
||||
elif (( delta < 86400 )); then mca_msg "%d hours ago" "$(( delta / 3600 ))"
|
||||
elif (( delta < 172800 )); then mca_msg "1 day ago"
|
||||
else mca_msg "%d days ago" "$(( delta / 86400 ))"
|
||||
fi
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# State
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
mca_state_read() {
|
||||
local key="$1" default="${2:-}"
|
||||
if [[ -r "$MCA_STATEDIR/$key" ]]; then
|
||||
cat "$MCA_STATEDIR/$key"
|
||||
else
|
||||
printf '%s\n' "$default"
|
||||
fi
|
||||
}
|
||||
|
||||
mca_state_write() {
|
||||
local key="$1"
|
||||
shift
|
||||
mkdir -p "$MCA_STATEDIR" 2>/dev/null || return 1
|
||||
printf '%s\n' "$*" > "$MCA_STATEDIR/$key"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The change ledger
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reverting by pattern ("delete every desktop entry that mentions the flag")
|
||||
# would also delete entries the user wrote by hand. So every change is recorded
|
||||
# instead, and `revert` replays the ledger backwards.
|
||||
#
|
||||
# Format, tab separated:
|
||||
# <kind> <path> <detail>
|
||||
#
|
||||
# shadow <generated desktop entry> <source entry it was generated from>
|
||||
# inplace <edited file> <basename of its backup copy>
|
||||
# flags <flag file> created | appended | merged
|
||||
# kwin <kwinrc> what EnablePrimarySelection said before
|
||||
# gecko <user.js in a profile> created | block
|
||||
#
|
||||
# The kinds differ in how they are undone: a backup is restored wholesale, a
|
||||
# flag file only loses the one line that was added to it, and kwinrc gets one
|
||||
# key written back. It is a file KDE writes to itself, and putting a whole
|
||||
# copy of it back would take everything else settled since with it. A user.js
|
||||
# loses its block, and prefs.js the line Firefox copied out of it.
|
||||
|
||||
mca_ledger_add() {
|
||||
local kind="$1" path="$2" detail="${3:-}"
|
||||
mkdir -p "$MCA_STATEDIR" 2>/dev/null || return 1
|
||||
|
||||
# Never record the same path twice: applying repeatedly is normal (the path
|
||||
# unit fires on every desktop file that appears) and the ledger has to stay
|
||||
# a set, not a log.
|
||||
mca_ledger_forget "$path"
|
||||
printf '%s\t%s\t%s\n' "$kind" "$path" "$detail" >> "$MCA_LEDGER"
|
||||
}
|
||||
|
||||
# mca_ledger_has <path>
|
||||
# Whether the ledger still knows about that file. Worth asking because the copy
|
||||
# kept beside it is named after the path and outlives a ledger that was lost or
|
||||
# truncated: a file nothing has a record of any more is one nothing would ever
|
||||
# look at again.
|
||||
mca_ledger_has() {
|
||||
[[ -f $MCA_LEDGER ]] || return 1
|
||||
awk -F'\t' -v p="$1" '$2 == p { found = 1 } END { exit !found }' "$MCA_LEDGER"
|
||||
}
|
||||
|
||||
# mca_ledger_detail <path>
|
||||
# The third column of that file's line, empty when the ledger has none. What it
|
||||
# means is the kind's business; for `kwin` it is the value to put back.
|
||||
mca_ledger_detail() {
|
||||
[[ -f $MCA_LEDGER ]] || return 1
|
||||
awk -F'\t' -v p="$1" '$2 == p { d = $3 } END { if (!length(d)) exit 1; print d }' \
|
||||
"$MCA_LEDGER"
|
||||
}
|
||||
|
||||
mca_ledger_forget() {
|
||||
local path="$1" tmp
|
||||
[[ -f $MCA_LEDGER ]] || return 0
|
||||
|
||||
tmp="$(mktemp "${MCA_LEDGER}.XXXXXX")" || return 1
|
||||
awk -F'\t' -v p="$path" '$2 != p' "$MCA_LEDGER" > "$tmp" 2>/dev/null \
|
||||
&& mv -f "$tmp" "$MCA_LEDGER" || rm -f "$tmp"
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# mca_backup_name <file>
|
||||
# The name a backup copy of that file is stored under. Derived from the path
|
||||
# rather than remembered, so a caller can ask whether a file has been backed up
|
||||
# before without reading the ledger.
|
||||
mca_backup_name() {
|
||||
printf '%s' "$1" | sed 's|/|%|g'
|
||||
}
|
||||
|
||||
# mca_backup <file>
|
||||
# Copies a file aside before it is edited in place, and prints the name the
|
||||
# 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
|
||||
# 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() {
|
||||
local file="$1" name
|
||||
name="$(mca_backup_name "$file")"
|
||||
|
||||
mkdir -p "$MCA_BACKUPDIR" 2>/dev/null || return 1
|
||||
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
|
||||
fi
|
||||
printf '%s\n' "$name"
|
||||
}
|
||||
|
||||
# mca_write_if_changed <path> <content>
|
||||
# Writing a file that is already correct would touch its mtime, and the systemd
|
||||
# path unit watches these directories: an unconditional write would retrigger
|
||||
# the service, which would write again, forever.
|
||||
mca_write_if_changed() {
|
||||
local path="$1" content="$2" current=''
|
||||
|
||||
[[ -r $path ]] && current="$(< "$path")"
|
||||
|
||||
# $(< file) drops trailing newlines and callers pass content that ends in
|
||||
# one, so both sides are trimmed before comparing. Getting this wrong makes
|
||||
# every file look changed on every run, which is exactly the feedback loop
|
||||
# this function exists to prevent.
|
||||
[[ "${current%"${current##*[!$'\n']}"}" == "${content%"${content##*[!$'\n']}"}" ]] && return 1
|
||||
|
||||
mkdir -p "$(dirname "$path")" 2>/dev/null || return 2
|
||||
printf '%s' "$content" > "$path" || return 2
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Reading and writing ~/.config/middleclick-autoscroll/config.
|
||||
#
|
||||
# The file exists so the tool has somewhere to remember what it was told; it is
|
||||
# not meant to be edited. Every option it holds is reachable from the interface,
|
||||
# which is why there is no commented template shipped anywhere and no reference
|
||||
# to the path in the interface itself.
|
||||
#
|
||||
# It is parsed rather than sourced. One "Key=Value" per line, '#' comments.
|
||||
|
||||
MCA_CONFIG_CACHE=''
|
||||
MCA_CONFIG_CACHED=0
|
||||
|
||||
_mca_config_slurp() {
|
||||
(( MCA_CONFIG_CACHED )) && return 0
|
||||
MCA_CONFIG_CACHE=''
|
||||
[[ -r $MCA_CONFIG ]] && MCA_CONFIG_CACHE="$(< "$MCA_CONFIG")"
|
||||
MCA_CONFIG_CACHED=1
|
||||
return 0
|
||||
}
|
||||
|
||||
# _mca_config_lookup <Key> [default]
|
||||
# Result in MCA_CONFIG_VALUE. Assigning rather than printing matters on the
|
||||
# settings screen, which reads every key on every frame: a command substitution
|
||||
# there is a fork, and forks are the whole cost of a redraw.
|
||||
MCA_CONFIG_VALUE=''
|
||||
|
||||
_mca_config_lookup() {
|
||||
local key="$1" default="${2:-}" val='' line
|
||||
|
||||
MCA_CONFIG_VALUE="$default"
|
||||
[[ -r $MCA_CONFIG ]] || return 0
|
||||
_mca_config_slurp
|
||||
|
||||
while IFS= read -r line; do
|
||||
[[ $line == *"$key"* ]] || continue
|
||||
[[ $line =~ ^[[:space:]]*"$key"[[:space:]]*=(.*)$ ]] || continue
|
||||
val="${BASH_REMATCH[1]}"
|
||||
done <<< "$MCA_CONFIG_CACHE"
|
||||
|
||||
val="${val%%#*}"
|
||||
val="${val#"${val%%[![:space:]]*}"}"
|
||||
val="${val%"${val##*[![:space:]]}"}"
|
||||
val="${val%\"}"
|
||||
val="${val#\"}"
|
||||
|
||||
[[ -n $val ]] && MCA_CONFIG_VALUE="$val"
|
||||
return 0
|
||||
}
|
||||
|
||||
# mca_config_get <Key> [default]
|
||||
mca_config_get() {
|
||||
_mca_config_lookup "$@"
|
||||
printf '%s\n' "$MCA_CONFIG_VALUE"
|
||||
}
|
||||
|
||||
# mca_config_bool <Key> <default: yes|no>
|
||||
mca_config_bool() {
|
||||
local val
|
||||
val="$(mca_config_get "$1" "$2")"
|
||||
case "${val,,}" in
|
||||
yes|y|true|1|on|enabled) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# mca_config_set <Key> <Value>
|
||||
mca_config_set() {
|
||||
local key="$1" value="$2" tmp
|
||||
|
||||
if [[ ! -e $MCA_CONFIG ]]; then
|
||||
mkdir -p "$(dirname "$MCA_CONFIG")" || return 1
|
||||
{
|
||||
printf '# %s\n' "$MCA_PRETTY"
|
||||
printf '#\n'
|
||||
printf '# Written by `%s`. Nothing here needs editing by hand -\n' "$MCA_NAME"
|
||||
printf '# every option is reachable from `%s` itself.\n' "$MCA_NAME"
|
||||
} > "$MCA_CONFIG" || return 1
|
||||
fi
|
||||
[[ -w $MCA_CONFIG ]] || return 1
|
||||
|
||||
tmp="$(mktemp "${MCA_CONFIG}.XXXXXX")" || return 1
|
||||
chmod --reference="$MCA_CONFIG" "$tmp" 2>/dev/null || chmod 0644 "$tmp"
|
||||
|
||||
if grep -qE "^[[:space:]]*#?[[:space:]]*${key}[[:space:]]*=" "$MCA_CONFIG"; then
|
||||
awk -v key="$key" -v value="$value" '
|
||||
!done && $0 ~ "^[[:space:]]*#?[[:space:]]*" key "[[:space:]]*=" {
|
||||
print key "=" value; done = 1; next
|
||||
}
|
||||
# drop any further occurrences so the file cannot grow duplicates
|
||||
$0 ~ "^[[:space:]]*" key "[[:space:]]*=" { next }
|
||||
{ print }
|
||||
' "$MCA_CONFIG" > "$tmp" || { rm -f "$tmp"; return 1; }
|
||||
else
|
||||
cat "$MCA_CONFIG" > "$tmp" || { rm -f "$tmp"; return 1; }
|
||||
printf '%s=%s\n' "$key" "$value" >> "$tmp"
|
||||
fi
|
||||
|
||||
mv -f "$tmp" "$MCA_CONFIG"
|
||||
MCA_CONFIG_CACHED=0
|
||||
}
|
||||
|
||||
# mca_config_del <Key>
|
||||
mca_config_del() {
|
||||
local key="$1" tmp
|
||||
|
||||
[[ -w $MCA_CONFIG ]] || return 1
|
||||
grep -qE "^[[:space:]]*${key}[[:space:]]*=" "$MCA_CONFIG" || return 0
|
||||
|
||||
tmp="$(mktemp "${MCA_CONFIG}.XXXXXX")" || return 1
|
||||
chmod --reference="$MCA_CONFIG" "$tmp" 2>/dev/null || chmod 0644 "$tmp"
|
||||
grep -vE "^[[:space:]]*${key}[[:space:]]*=" "$MCA_CONFIG" > "$tmp"
|
||||
mv -f "$tmp" "$MCA_CONFIG"
|
||||
MCA_CONFIG_CACHED=0
|
||||
}
|
||||
|
||||
# mca_config_list_has <Key> <item>
|
||||
# The per-application overrides are stored as space separated lists.
|
||||
mca_config_list_has() {
|
||||
local list item="$2" entry
|
||||
list="$(mca_config_get "$1" '')"
|
||||
for entry in $list; do
|
||||
[[ $entry == "$item" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# mca_config_list_add <Key> <item> / mca_config_list_del <Key> <item>
|
||||
mca_config_list_add() {
|
||||
local key="$1" item="$2" list
|
||||
mca_config_list_has "$key" "$item" && return 0
|
||||
list="$(mca_config_get "$key" '')"
|
||||
mca_config_set "$key" "${list:+$list }$item"
|
||||
}
|
||||
|
||||
mca_config_list_del() {
|
||||
local key="$1" item="$2" list entry out=''
|
||||
list="$(mca_config_get "$key" '')"
|
||||
for entry in $list; do
|
||||
[[ $entry == "$item" ]] && continue
|
||||
out="${out:+$out }$entry"
|
||||
done
|
||||
mca_config_set "$key" "$out"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Resolved settings
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
mca_config_load() {
|
||||
CFG_ENABLED=no; mca_config_bool Enabled no && CFG_ENABLED=yes
|
||||
CFG_WATCH=no; mca_config_bool WatchNewApps yes && CFG_WATCH=yes
|
||||
CFG_PASTE=no; mca_config_bool DisablePaste yes && CFG_PASTE=yes
|
||||
|
||||
CFG_EXTRA_FLAGS="$(mca_config_get ExtraFlags '')"
|
||||
|
||||
# Applications the user turned on or off by hand on the applications
|
||||
# screen, by desktop file id. Include wins over detection, Skip wins over
|
||||
# everything.
|
||||
CFG_INCLUDE="$(mca_config_get Include '')"
|
||||
CFG_SKIP="$(mca_config_get Skip '')"
|
||||
}
|
||||
|
||||
# mca_config_migrate
|
||||
# Up to 1.5 the settings had a switch per group: applications, browsers,
|
||||
# Flatpak, snap, programs at login, Steam and Spotify. The applications list
|
||||
# does that job now, one application at a time. So a group that was switched
|
||||
# off turns into a Skip for each application in it, once, and the old keys go.
|
||||
# Called at the end of a scan, because the groups come from there.
|
||||
mca_config_migrate() {
|
||||
local i key off='' group pack
|
||||
|
||||
_mca_config_slurp
|
||||
[[ $MCA_CONFIG_CACHE == *Patch[A-Z]*=* ]] || return 0
|
||||
|
||||
for key in Apps Browsers Flatpak Snap Steam Spotify; do
|
||||
mca_config_bool "Patch$key" yes || off+=" $key "
|
||||
done
|
||||
|
||||
if [[ -n $off ]]; then
|
||||
for i in "${!MCA_IDS[@]}"; do
|
||||
mca_config_list_has Include "${MCA_IDS[i]}" && continue
|
||||
group="${MCA_KINDS[i]^}"
|
||||
[[ $group == App || $group == Browser ]] && group+=s
|
||||
pack="${MCA_PACKAGING[i]^}"
|
||||
if [[ $off == *" $group "* || $off == *" $pack "* ]]; then
|
||||
mca_config_list_add Skip "${MCA_IDS[i]}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
for key in Apps Browsers Flatpak Snap Autostart Steam Spotify; do
|
||||
mca_config_del "Patch$key"
|
||||
done
|
||||
mca_config_load
|
||||
}
|
||||
|
||||
# mca_flags [kind]
|
||||
# The full argument string that gets injected. Kept in one place so the flag
|
||||
# file writer and the desktop entry writer cannot drift apart.
|
||||
#
|
||||
# A browser gets the flag that leaves it without a warning bar; everything else
|
||||
# gets the one that works in every Chromium there is. See MCA_FLAG.
|
||||
mca_flags() {
|
||||
if [[ ${1:-app} == browser ]]; then
|
||||
printf '%s' "$MCA_BROWSER_FLAG"
|
||||
else
|
||||
printf '%s' "$MCA_FLAG"
|
||||
fi
|
||||
[[ -n ${CFG_EXTRA_FLAGS:-} ]] && printf ' %s' "$CFG_EXTRA_FLAGS"
|
||||
printf '\n'
|
||||
}
|
||||
+1287
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,254 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Firefox, and everything else built on Gecko: LibreWolf, Zen, Floorp,
|
||||
# Waterfox, Thunderbird.
|
||||
#
|
||||
# Gecko has autoscroll of its own, off on Linux. There is no command line flag
|
||||
# for it, only a setting: general.autoScroll. Settings live in each profile, so
|
||||
# the line goes into the profile's user.js, which Gecko reads at every start.
|
||||
#
|
||||
# Gecko copies what user.js sets into prefs.js. So undoing takes the line out
|
||||
# of prefs.js as well. A browser that is still open would write it back when it
|
||||
# quits, so for that profile user.js says false instead, and the record stays
|
||||
# until the next undo can finish the job.
|
||||
|
||||
MCA_GECKO_BEGIN='// >>> middleclick-autoscroll'
|
||||
MCA_GECKO_END='// <<< middleclick-autoscroll'
|
||||
MCA_GECKO_ON='user_pref("general.autoScroll", true);'
|
||||
MCA_GECKO_OFF='user_pref("general.autoScroll", false);'
|
||||
|
||||
# The profile folders one apply has done already, and the profiles.ini files
|
||||
# the watcher should look at. Both are filled by mca_gecko_apply.
|
||||
declare -A MCA_GECKO_SEEN=()
|
||||
MCA_GECKO_WATCH=()
|
||||
|
||||
# Set by mca_gecko_revert when a profile is still open and has to be undone
|
||||
# again later.
|
||||
MCA_GECKO_PENDING=0
|
||||
|
||||
# _mca_gecko_rel <directory>
|
||||
# Whether a Gecko application is installed there. If so, where it keeps its
|
||||
# profiles, relative to the home or config folder, in MCA_GECKO_REL. Read from
|
||||
# application.ini the way Gecko does it: [App] Profile= when it is set, else
|
||||
# Vendor/Name, in lowercase.
|
||||
MCA_GECKO_REL=''
|
||||
|
||||
_mca_gecko_rel() {
|
||||
local dir="$1" line group='' vendor='' name='' profile='' rel=''
|
||||
|
||||
MCA_GECKO_REL=''
|
||||
[[ -e $dir/libxul.so && -r $dir/application.ini ]] || return 1
|
||||
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
line="${line%$'\r'}"
|
||||
case "$line" in
|
||||
'['*) group="$line" ;;
|
||||
Vendor=*) [[ $group == '[App]' ]] && vendor="${line#*=}" ;;
|
||||
Name=*) [[ $group == '[App]' ]] && name="${line#*=}" ;;
|
||||
Profile=*) [[ $group == '[App]' ]] && profile="${line#*=}" ;;
|
||||
esac
|
||||
done < "$dir/application.ini"
|
||||
|
||||
if [[ -n $profile ]]; then
|
||||
rel="${profile//\\//}"
|
||||
while [[ $rel == /* ]]; do rel="${rel#/}"; done
|
||||
rel="${rel#.}"
|
||||
elif [[ -n $name ]]; then
|
||||
rel="${vendor:+$vendor/}$name"
|
||||
fi
|
||||
|
||||
# Gecko lowercases in ASCII. A Turkish locale turns I into a dotless ı.
|
||||
rel="${rel,,}"
|
||||
rel="${rel//ı/i}"
|
||||
|
||||
[[ -n $rel && /$rel/ != */../* ]] || return 1
|
||||
MCA_GECKO_REL="$rel"
|
||||
}
|
||||
|
||||
# _mca_gecko_tree <root> <depth>
|
||||
# The same for a Flatpak or a snap, where libxul.so is somewhere in the tree.
|
||||
_mca_gecko_tree() {
|
||||
local lib
|
||||
|
||||
MCA_GECKO_REL=''
|
||||
[[ -d $1 ]] || return 1
|
||||
lib="$(find "$1" -maxdepth "$2" -name libxul.so -print -quit 2>/dev/null)"
|
||||
[[ -n $lib ]] && _mca_gecko_rel "${lib%/*}"
|
||||
}
|
||||
|
||||
# mca_flatpak_gecko <app id> / mca_snap_gecko <snap name>
|
||||
# Whether it is Gecko, with the folder in MCA_GECKO_REL. Memoized like the
|
||||
# Chromium check, and an empty answer means no.
|
||||
declare -A MCA_GECKO_MEMO=()
|
||||
|
||||
mca_flatpak_gecko() {
|
||||
local key="flatpak:$1"
|
||||
|
||||
if [[ -z ${MCA_GECKO_MEMO[$key]+set} ]]; then
|
||||
MCA_GECKO_REL=''
|
||||
mca_flatpak_location "$1" && _mca_gecko_tree "$MCA_FLATPAK_AT/files" 4
|
||||
MCA_GECKO_MEMO[$key]="$MCA_GECKO_REL"
|
||||
fi
|
||||
|
||||
MCA_GECKO_REL="${MCA_GECKO_MEMO[$key]}"
|
||||
[[ -n $MCA_GECKO_REL ]]
|
||||
}
|
||||
|
||||
mca_snap_gecko() {
|
||||
local key="snap:$1" d
|
||||
|
||||
if [[ -z ${MCA_GECKO_MEMO[$key]+set} ]]; then
|
||||
MCA_GECKO_REL=''
|
||||
for d in "${MCA_SNAP_DIRS[@]}"; do
|
||||
_mca_gecko_tree "$d/$1/current" 5 && break
|
||||
done
|
||||
MCA_GECKO_MEMO[$key]="$MCA_GECKO_REL"
|
||||
fi
|
||||
|
||||
MCA_GECKO_REL="${MCA_GECKO_MEMO[$key]}"
|
||||
[[ -n $MCA_GECKO_REL ]]
|
||||
}
|
||||
|
||||
# mca_gecko_roots <rel> <packaging> <name>
|
||||
# Every folder that can hold the application's profiles.ini. An older install
|
||||
# uses ~/.<rel>. A new one (Firefox 147 and later) uses $XDG_CONFIG_HOME/<rel>,
|
||||
# or ~/<rel> for a fork with a profile name of its own. A Flatpak or a snap has
|
||||
# the same inside its own folder.
|
||||
mca_gecko_roots() {
|
||||
local rel="$1" packaging="$2" name="$3" d
|
||||
|
||||
case "$packaging" in
|
||||
flatpak)
|
||||
printf '%s\n' "$HOME/.var/app/$name/.$rel" "$HOME/.var/app/$name/config/$rel"
|
||||
;;
|
||||
snap)
|
||||
for d in common current; do
|
||||
printf '%s\n' "$HOME/snap/$name/$d/.$rel" "$HOME/snap/$name/$d/.config/$rel"
|
||||
done
|
||||
;;
|
||||
*)
|
||||
printf '%s\n' "$HOME/.$rel" "$MCA_XDG_CONFIG/$rel"
|
||||
[[ $rel == */* ]] || printf '%s\n' "$HOME/$rel"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# mca_gecko_profiles <root>
|
||||
# Every profile folder that profiles.ini there lists.
|
||||
mca_gecko_profiles() {
|
||||
local root="$1" line path='' relative=1 inprofile=0
|
||||
|
||||
[[ -r $root/profiles.ini ]] || return 0
|
||||
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
line="${line%$'\r'}"
|
||||
case "$line" in
|
||||
'['*)
|
||||
_mca_gecko_profile_emit
|
||||
[[ $line == '[Profile'* ]] && inprofile=1 || inprofile=0
|
||||
path='' relative=1
|
||||
;;
|
||||
Path=*) path="${line#Path=}" ;;
|
||||
IsRelative=*) relative="${line#IsRelative=}" ;;
|
||||
esac
|
||||
done < "$root/profiles.ini"
|
||||
_mca_gecko_profile_emit
|
||||
}
|
||||
|
||||
_mca_gecko_profile_emit() {
|
||||
(( inprofile )) && [[ -n $path ]] || return 0
|
||||
[[ $relative == 1 ]] && path="$root/$path"
|
||||
[[ -d $path ]] && printf '%s\n' "$path"
|
||||
return 0
|
||||
}
|
||||
|
||||
# _mca_gecko_write <user.js> <line>
|
||||
# Puts the line into user.js as a marked block, replacing an older block.
|
||||
# Returns like mca_write_if_changed.
|
||||
_mca_gecko_write() {
|
||||
local file="$1" line="$2" content=''
|
||||
|
||||
[[ -f $file ]] && content="$(_mca_flags_drop_block "$file" "$MCA_GECKO_BEGIN" "$MCA_GECKO_END")"
|
||||
content="${content:+$content$'\n'}$MCA_GECKO_BEGIN"$'\n'"$line"$'\n'"$MCA_GECKO_END"
|
||||
mca_write_if_changed "$file" "$content"$'\n'
|
||||
}
|
||||
|
||||
# mca_gecko_apply <rel> <packaging> <name>
|
||||
# Turns autoscroll on in every profile of the application.
|
||||
mca_gecko_apply() {
|
||||
local root profile
|
||||
|
||||
while IFS= read -r root; do
|
||||
[[ -n ${MCA_GECKO_SEEN[$root]+set} ]] && continue
|
||||
MCA_GECKO_SEEN[$root]=1
|
||||
|
||||
# A new profile is written into profiles.ini, so the watcher can pick
|
||||
# it up.
|
||||
MCA_GECKO_WATCH+=("$root/profiles.ini")
|
||||
|
||||
while IFS= read -r profile; do
|
||||
_mca_gecko_profile_apply "$profile"
|
||||
done < <(mca_gecko_profiles "$root")
|
||||
done < <(mca_gecko_roots "$@")
|
||||
}
|
||||
|
||||
_mca_gecko_profile_apply() {
|
||||
local dir="$1" file="$1/user.js" detail=created
|
||||
|
||||
# A user.js that links somewhere else is part of a setup of its own.
|
||||
[[ -L $file ]] && return 0
|
||||
[[ -f $file ]] && detail=block
|
||||
|
||||
if ! grep -qxF -- "$MCA_GECKO_BEGIN" "$file" 2>/dev/null; then
|
||||
# Set by the user already, in user.js or in the browser's own
|
||||
# settings. Their choice: nothing is written and nothing recorded.
|
||||
grep -qE '^[[:space:]]*user_pref\([[:space:]]*"general\.autoScroll"' \
|
||||
-- "$file" 2>/dev/null && return 0
|
||||
grep -qxF -- "$MCA_GECKO_ON" "$dir/prefs.js" 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
_mca_gecko_write "$file" "$MCA_GECKO_ON"
|
||||
case $? in
|
||||
0) MCA_CHANGES=$(( MCA_CHANGES + 1 )) ;;
|
||||
2) return 0 ;;
|
||||
esac
|
||||
|
||||
# A file this program created stays one it created.
|
||||
mca_ledger_has "$file" && detail="$(mca_ledger_detail "$file")"
|
||||
mca_ledger_add gecko "$file" "$detail"
|
||||
}
|
||||
|
||||
# mca_gecko_revert <user.js> <created|block>
|
||||
# Returns 0 when something changed. Sets MCA_GECKO_PENDING when the profile is
|
||||
# open and the record has to stay.
|
||||
mca_gecko_revert() {
|
||||
local file="$1" detail="$2" dir="${1%/*}" prefs content tmp rc=0
|
||||
|
||||
MCA_GECKO_PENDING=0
|
||||
[[ -f $file && ! -L $file ]] || return 1
|
||||
grep -qxF -- "$MCA_GECKO_BEGIN" "$file" 2>/dev/null || return 1
|
||||
|
||||
# The lock link only exists while the profile is open.
|
||||
if [[ -L $dir/lock ]]; then
|
||||
MCA_GECKO_PENDING=1
|
||||
_mca_gecko_write "$file" "$MCA_GECKO_OFF"
|
||||
return
|
||||
fi
|
||||
|
||||
content="$(_mca_flags_drop_block "$file" "$MCA_GECKO_BEGIN" "$MCA_GECKO_END")"
|
||||
if [[ $detail == created && -z ${content//[[:space:]]/} ]]; then
|
||||
rm -f -- "$file"
|
||||
else
|
||||
printf '%s\n' "$content" > "$file"
|
||||
fi
|
||||
|
||||
prefs="$dir/prefs.js"
|
||||
if [[ -f $prefs && ! -L $prefs ]] && grep -qxF -- "$MCA_GECKO_ON" "$prefs"; then
|
||||
tmp="$(mktemp "$prefs.XXXXXX")" || return 0
|
||||
chmod --reference="$prefs" -- "$tmp" 2>/dev/null || chmod 600 -- "$tmp"
|
||||
# grep says 1 when no line is left, which is fine here.
|
||||
grep -vxF -- "$MCA_GECKO_ON" "$prefs" > "$tmp" || rc=$?
|
||||
if (( rc <= 1 )); then mv -f -- "$tmp" "$prefs"; else rm -f -- "$tmp"; fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# KDE's middle-click paste.
|
||||
#
|
||||
# Inside a Chromium application the flag settles this on its own: with
|
||||
# autoscroll on, Blink stops pasting the primary selection on middle click,
|
||||
# because the button is doing something else now. Everywhere else on the
|
||||
# desktop middle click goes on pasting, which is the behaviour this whole
|
||||
# program exists to get away from. So the desktop's own switch for it is
|
||||
# turned off too.
|
||||
#
|
||||
# KWin has such a switch, and only for a Wayland session, where the paste is a
|
||||
# protocol the compositor either offers or does not:
|
||||
#
|
||||
# ~/.config/kwinrc, [Wayland], EnablePrimarySelection=false
|
||||
#
|
||||
# On X11 there is nothing to switch. The primary selection is part of X itself
|
||||
# and every toolkit reaches for it on its own; no one place can say no.
|
||||
#
|
||||
# KWin reads the key once, while it starts, so what is written here is true
|
||||
# from the next login on rather than straight away.
|
||||
|
||||
MCA_KWINRC="${MCA_XDG_CONFIG}/kwinrc"
|
||||
MCA_KWIN_GROUP="Wayland"
|
||||
MCA_KWIN_KEY="EnablePrimarySelection"
|
||||
|
||||
# The kconfig command line tools, whichever generation this session has. They
|
||||
# are what System Settings writes kwinrc with, and worth the fork: the file has
|
||||
# a cascade behind it (/etc/xdg, the kdedefaults directory) and entries a
|
||||
# distribution can mark immutable, and an edit that reaches past all that with
|
||||
# sed writes something KDE goes on to ignore.
|
||||
MCA_KDE_TOOL=''
|
||||
MCA_KDE_TOOL_LOOKED=0
|
||||
|
||||
_mca_kde_tool() {
|
||||
local gen
|
||||
|
||||
if (( ! MCA_KDE_TOOL_LOOKED )); then
|
||||
MCA_KDE_TOOL_LOOKED=1
|
||||
for gen in 6 5; do
|
||||
if mca_have "kreadconfig$gen" && mca_have "kwriteconfig$gen"; then
|
||||
MCA_KDE_TOOL="$gen"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
[[ -n $MCA_KDE_TOOL ]] || return 1
|
||||
printf '%s\n' "$MCA_KDE_TOOL"
|
||||
}
|
||||
|
||||
# mca_kde_session
|
||||
# Whether this is a KWin Wayland session.
|
||||
mca_kde_session() {
|
||||
local desktop="${XDG_CURRENT_DESKTOP:-}"
|
||||
|
||||
# The watcher is a systemd service, and one started before the session
|
||||
# imported its environment sees none of these variables. A running
|
||||
# kwin_wayland answers both halves of the question at once, and is what
|
||||
# such a run ends up going by.
|
||||
if [[ -z $desktop && -z ${KDE_FULL_SESSION:-} ]]; then
|
||||
mca_have pgrep || return 1
|
||||
pgrep -u "$(id -u)" -x kwin_wayland > /dev/null 2>&1
|
||||
return
|
||||
fi
|
||||
|
||||
# XDG_CURRENT_DESKTOP is the one every desktop sets, and the one to go by
|
||||
# wherever it is set; KDE_FULL_SESSION is what answers when it is not.
|
||||
[[ -z $desktop || ${desktop^^} == *KDE* ]] || return 1
|
||||
|
||||
[[ ${XDG_SESSION_TYPE:-} == wayland || -n ${WAYLAND_DISPLAY:-} ]]
|
||||
}
|
||||
|
||||
mca_kde_available() {
|
||||
mca_kde_session && _mca_kde_tool > /dev/null
|
||||
}
|
||||
|
||||
# mca_kde_read [file]
|
||||
# What the key says now, lowercased, empty when it is not set at all.
|
||||
mca_kde_read() {
|
||||
local file="${1:-$MCA_KWINRC}" gen val
|
||||
gen="$(_mca_kde_tool)" || return 1
|
||||
|
||||
# kreadconfig creates the file it is pointed at, and the status screen
|
||||
# (which asks on every redraw) has no business writing anything. A file
|
||||
# that is not there has no value in it to read anyway.
|
||||
[[ -f $file ]] || return 0
|
||||
|
||||
val="$("kreadconfig$gen" --file "$file" --group "$MCA_KWIN_GROUP" \
|
||||
--key "$MCA_KWIN_KEY" 2>/dev/null)"
|
||||
printf '%s\n' "${val,,}"
|
||||
}
|
||||
|
||||
# mca_kde_write [file] <value>
|
||||
# An empty value takes the key back out rather than writing nothing into it -
|
||||
# a key that is missing and a key that says nothing are not the same thing to
|
||||
# KDE, and only the first one is what an untouched kwinrc looks like.
|
||||
mca_kde_write() {
|
||||
local file="$1" value="$2" gen
|
||||
gen="$(_mca_kde_tool)" || return 1
|
||||
|
||||
if [[ -z $value ]]; then
|
||||
"kwriteconfig$gen" --file "$file" --group "$MCA_KWIN_GROUP" \
|
||||
--key "$MCA_KWIN_KEY" --delete 2>/dev/null
|
||||
else
|
||||
"kwriteconfig$gen" --file "$file" --group "$MCA_KWIN_GROUP" \
|
||||
--key "$MCA_KWIN_KEY" "$value" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# mca_kde_paste_apply
|
||||
# Turns the paste off, and remembers what the key said before so that undoing
|
||||
# puts that back rather than a guess.
|
||||
#
|
||||
# A key that already says false without the ledger knowing about it was set by
|
||||
# the user or by the distribution. That one is left alone and nothing is
|
||||
# recorded, so `disable` cannot switch a paste back on that this program never
|
||||
# switched off.
|
||||
mca_kde_paste_apply() {
|
||||
local current previous changed=0
|
||||
|
||||
mca_kde_available || return 1
|
||||
current="$(mca_kde_read)" || return 1
|
||||
|
||||
if mca_ledger_has "$MCA_KWINRC"; then
|
||||
# Ours already. What goes back is the value recorded the first time
|
||||
# round, not what reading the file now would say. That is false, and
|
||||
# false is what is being undone.
|
||||
previous="$(mca_ledger_detail "$MCA_KWINRC")" || previous=unset
|
||||
elif [[ $current == false ]]; then
|
||||
return 0
|
||||
else
|
||||
previous="${current:-unset}"
|
||||
fi
|
||||
|
||||
if [[ $current != false ]]; then
|
||||
mca_kde_write "$MCA_KWINRC" false || return 1
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
changed=1
|
||||
fi
|
||||
|
||||
mca_ledger_add kwin "$MCA_KWINRC" "$previous"
|
||||
|
||||
(( changed )) && mca_note "$(mca_msg "Middle click stops pasting at the next login.")"
|
||||
return 0
|
||||
}
|
||||
|
||||
# mca_kde_paste_revert <file> <previous value>
|
||||
mca_kde_paste_revert() {
|
||||
local file="$1" previous="$2"
|
||||
|
||||
_mca_kde_tool > /dev/null || return 1
|
||||
[[ -f $file ]] || return 1
|
||||
|
||||
# Only what is still recognisably ours. A key somebody has since put a
|
||||
# value of their own into is theirs now.
|
||||
[[ "$(mca_kde_read "$file")" == false ]] || return 1
|
||||
|
||||
[[ $previous == unset ]] && previous=''
|
||||
mca_kde_write "$file" "$previous" || return 1
|
||||
|
||||
# Nothing was in the file but the key, which means there was no file before
|
||||
# this program wrote one. An empty kwinrc says nothing that its absence
|
||||
# does not say better.
|
||||
[[ -s $file ]] || rm -f -- "$file"
|
||||
return 0
|
||||
}
|
||||
+669
@@ -0,0 +1,669 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# The interactive front end.
|
||||
#
|
||||
# This is the whole configuration interface. There is a file behind it, but no
|
||||
# part of the program ever asks anyone to open it: one switch on the front
|
||||
# screen, a settings list for the categories, and a list of the applications
|
||||
# themselves for the cases where the automatic answer is not the wanted one.
|
||||
|
||||
# Terminal mode.
|
||||
#
|
||||
# bash flips the terminal into non-canonical mode for each `read -sn1` and back
|
||||
# out again in between. That gap matters: in canonical mode DEL is the ERASE
|
||||
# character, so the line discipline eats it instead of delivering it, and a
|
||||
# backspace typed while the interface was between reads simply vanishes.
|
||||
# Holding non-canonical mode for the whole interface removes the gap.
|
||||
MCA_TERM_SAVED=''
|
||||
|
||||
mca_ui_term_raw() {
|
||||
mca_have stty || return 0
|
||||
[[ -t 0 ]] || return 0
|
||||
[[ -n $MCA_TERM_SAVED ]] && return 0
|
||||
|
||||
MCA_TERM_SAVED="$(stty -g 2>/dev/null)" || { MCA_TERM_SAVED=''; return 0; }
|
||||
stty -icanon -echo min 1 time 0 2>/dev/null || true
|
||||
}
|
||||
|
||||
mca_ui_term_restore() {
|
||||
[[ -n $MCA_TERM_SAVED ]] || return 0
|
||||
stty "$MCA_TERM_SAVED" 2>/dev/null || true
|
||||
MCA_TERM_SAVED=''
|
||||
}
|
||||
|
||||
# What the last action printed. The menu shows it under the status until the
|
||||
# next key, so nothing has to wait for a key on a screen of its own.
|
||||
MCA_UI_MSG=''
|
||||
|
||||
# Set while the menu runs. Its status block already says that running
|
||||
# applications need a restart, so the actions leave that line out there.
|
||||
MCA_IN_MENU=''
|
||||
|
||||
# mca_ui_run <command...>
|
||||
# Runs an action in this shell (so what it scans and loads stays) with its
|
||||
# output kept for the menu. None of them asks anything.
|
||||
mca_ui_run() {
|
||||
local tmp
|
||||
|
||||
printf ' %s%s%s\n' "$MCA_C_DIM" "$(mca_msg "One moment...")" "$MCA_C_RESET"
|
||||
tmp="$(mktemp)" || { "$@"; return; }
|
||||
"$@" > "$tmp" 2>&1
|
||||
MCA_UI_MSG="$(< "$tmp")"
|
||||
rm -f -- "$tmp"
|
||||
}
|
||||
|
||||
# mca_read_key
|
||||
# One keypress, resolved to a symbolic name. Arrow keys arrive as ESC [ A, so
|
||||
# the tail of the sequence is consumed here rather than being mistaken for
|
||||
# three separate presses.
|
||||
mca_read_key() {
|
||||
local k rest
|
||||
|
||||
IFS= read -rsn1 k || return 1
|
||||
|
||||
case "$k" in
|
||||
$'\e')
|
||||
if IFS= read -rsn2 -t 0.05 rest; then
|
||||
case "$rest" in
|
||||
'[A') printf 'up\n' ;;
|
||||
'[B') printf 'down\n' ;;
|
||||
'[C') printf 'right\n' ;;
|
||||
'[D') printf 'left\n' ;;
|
||||
*) printf 'escape\n' ;;
|
||||
esac
|
||||
else
|
||||
printf 'escape\n'
|
||||
fi
|
||||
;;
|
||||
''|$'\r') printf 'enter\n' ;;
|
||||
$'\x7f'|$'\b') printf 'backspace\n' ;;
|
||||
' ') printf 'space\n' ;;
|
||||
*) printf '%s\n' "$k" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# mca_ui_read_line <initial>
|
||||
# A minimal line editor built on mca_read_key, with the result in
|
||||
# MCA_LINE_RESULT. This exists instead of bash's own `read -r` because mixing
|
||||
# line mode into a single-key interface breaks it: after one cooked-mode read
|
||||
# the following `read -sn1` stops receiving keystrokes entirely.
|
||||
MCA_LINE_RESULT=''
|
||||
|
||||
mca_ui_read_line() {
|
||||
local buf="${1:-}" key
|
||||
|
||||
MCA_LINE_RESULT=''
|
||||
printf '%s' "$buf"
|
||||
|
||||
while true; do
|
||||
key="$(mca_read_key)" || { printf '\n'; return 1; }
|
||||
|
||||
case "$key" in
|
||||
enter)
|
||||
printf '\n'
|
||||
MCA_LINE_RESULT="$buf"
|
||||
return 0
|
||||
;;
|
||||
escape)
|
||||
printf '\n'
|
||||
return 1
|
||||
;;
|
||||
backspace)
|
||||
if [[ -n $buf ]]; then
|
||||
buf="${buf%?}"
|
||||
printf '\b \b'
|
||||
fi
|
||||
;;
|
||||
space)
|
||||
buf+=' '
|
||||
printf ' '
|
||||
;;
|
||||
up|down|left|right) ;;
|
||||
*)
|
||||
[[ ${#key} -eq 1 ]] || continue
|
||||
buf+="$key"
|
||||
printf '%s' "$key"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
mca_pause() {
|
||||
printf '\n %s' "$(mca_msg "Press any key to continue...")"
|
||||
read -rsn1 _ || true
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
# _mca_row <label> <value>
|
||||
# printf's %-28s pads by bytes, so a label containing "ü" comes out one column
|
||||
# short. ${#s} counts characters in a UTF-8 locale, so the padding is computed
|
||||
# here instead. It is applied inline, because command substitution would eat
|
||||
# the trailing spaces again.
|
||||
_mca_row() {
|
||||
local label="$1" value="$2" pad
|
||||
pad=$(( 30 - ${#label} ))
|
||||
(( pad < 0 )) && pad=0
|
||||
printf ' %s%*s %s\n' "$label" "$pad" '' "$value"
|
||||
}
|
||||
|
||||
_mca_onoff() {
|
||||
if [[ $1 == yes ]]; then
|
||||
printf '%s%s%s' "$MCA_C_GREEN" "$(mca_msg "ON")" "$MCA_C_RESET"
|
||||
else
|
||||
printf '%s%s%s' "$MCA_C_DIM" "$(mca_msg "OFF")" "$MCA_C_RESET"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Status
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# mca_ui_status
|
||||
# Shared by the `status` subcommand and the menu header. Scans, so the numbers
|
||||
# describe what is on disk right now rather than what was true at the last run.
|
||||
mca_ui_status() {
|
||||
local last covered
|
||||
|
||||
mca_scan_once
|
||||
mca_count_routes
|
||||
|
||||
last="$(mca_state_read last_apply '')"
|
||||
|
||||
_mca_row "$(mca_msg "Autoscroll")" "$(_mca_onoff "$CFG_ENABLED")"
|
||||
printf '\n'
|
||||
|
||||
if [[ $CFG_ENABLED == yes ]]; then
|
||||
covered="$MCA_N_ON"
|
||||
else
|
||||
covered=0
|
||||
fi
|
||||
|
||||
_mca_row "$(mca_msg "Applications covered")" \
|
||||
"$(mca_msg "%d of %d" "$covered" "$(( MCA_N_ON + MCA_N_OFF + MCA_N_UNKNOWN ))")"
|
||||
|
||||
if (( MCA_N_UNKNOWN )); then
|
||||
_mca_row "$(mca_msg "Not identified")" \
|
||||
"${MCA_C_DIM}$(mca_msg "%d (see the applications list)" "$MCA_N_UNKNOWN")${MCA_C_RESET}"
|
||||
fi
|
||||
|
||||
if mca_steam_installed; then
|
||||
if mca_steam_patched; then
|
||||
_mca_row "$(mca_msg "Steam")" "$(_mca_onoff yes)"
|
||||
elif [[ $CFG_ENABLED == yes ]] && mca_steam_wanted; then
|
||||
_mca_row "$(mca_msg "Steam")" \
|
||||
"${MCA_C_YELLOW}$(mca_msg "not patched yet")${MCA_C_RESET}"
|
||||
else
|
||||
_mca_row "$(mca_msg "Steam")" "$(_mca_onoff no)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if mca_watch_available; then
|
||||
if mca_watch_enabled; then
|
||||
_mca_row "$(mca_msg "New applications")" "$(_mca_onoff yes)"
|
||||
else
|
||||
_mca_row "$(mca_msg "New applications")" "$(_mca_onoff no)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Not an ON/OFF like the rows around it: what is being reported is the state
|
||||
# of the paste, not of a switch, and off is the state this program is after.
|
||||
if mca_kde_available; then
|
||||
if [[ "$(mca_kde_read)" == false ]]; then
|
||||
_mca_row "$(mca_msg "Middle-click paste")" \
|
||||
"${MCA_C_GREEN}$(mca_msg "off")${MCA_C_RESET}"
|
||||
else
|
||||
_mca_row "$(mca_msg "Middle-click paste")" \
|
||||
"${MCA_C_DIM}$(mca_msg "on")${MCA_C_RESET}"
|
||||
fi
|
||||
fi
|
||||
|
||||
_mca_row "$(mca_msg "Last applied")" "$(mca_time_ago "$last")"
|
||||
|
||||
if [[ $CFG_ENABLED == yes ]]; then
|
||||
printf '\n %s%s%s\n' "$MCA_C_DIM" \
|
||||
"$(mca_msg "Applications pick this up the next time they are started.")" \
|
||||
"$MCA_C_RESET"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Settings
|
||||
# ---------------------------------------------------------------------------
|
||||
# Only what is not about one application. Which applications get autoscroll is
|
||||
# decided in the applications list.
|
||||
#
|
||||
# Format: Key|type|default|check|label-msgid|help-msgid
|
||||
# type is bool or text. The row is only shown when the check passes: a switch
|
||||
# for something this system does not have is only noise.
|
||||
MCA_SETTINGS=(
|
||||
"DisablePaste|bool|yes|mca_kde_available|Turn off middle-click paste|Stops middle click from pasting anywhere else. Starts with the next login."
|
||||
"WatchNewApps|bool|yes|mca_watch_available|Cover new applications|Applications installed later get autoscroll on their own."
|
||||
"ExtraFlags|text|||More Chromium arguments|Passed to every application along with the autoscroll one."
|
||||
)
|
||||
|
||||
_mca_is_true() {
|
||||
case "${1,,}" in
|
||||
yes|y|true|1|on|enabled) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
MCA_SETTING_SHOWN=''
|
||||
MCA_LBL_ON=''
|
||||
MCA_LBL_OFF=''
|
||||
MCA_LBL_NONE=''
|
||||
|
||||
_mca_setting_display() {
|
||||
local type="$1" value="$2"
|
||||
|
||||
case "$type" in
|
||||
bool)
|
||||
if _mca_is_true "$value"; then
|
||||
MCA_SETTING_SHOWN="${MCA_C_GREEN}${MCA_LBL_ON}${MCA_C_RESET}"
|
||||
else
|
||||
MCA_SETTING_SHOWN="${MCA_C_DIM}${MCA_LBL_OFF}${MCA_C_RESET}"
|
||||
fi
|
||||
;;
|
||||
text)
|
||||
if [[ -n $value ]]; then
|
||||
MCA_SETTING_SHOWN="$value"
|
||||
else
|
||||
MCA_SETTING_SHOWN="${MCA_C_DIM}${MCA_LBL_NONE}${MCA_C_RESET}"
|
||||
fi
|
||||
;;
|
||||
*) MCA_SETTING_SHOWN="$value" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# mca_ui_settings
|
||||
# A cursor list rather than a numbered menu. The frame is assembled in memory
|
||||
# and written once, and everything constant is resolved before the loop:
|
||||
# drawing the naive way costs a command substitution per label per frame, which
|
||||
# is slow enough that arrow keys feel like the console is reloading.
|
||||
#
|
||||
# Returns 0 when something was changed, so the caller knows to re-apply.
|
||||
mca_ui_settings() {
|
||||
local -a names=() types=() defaults=() labels=() helps=() values=()
|
||||
local spec name type default check label help locale i key frame row pad
|
||||
local count width=0 dirty=1 cursor=0 touched=0
|
||||
|
||||
locale="$(mca_ui_locale)"
|
||||
mca_msg_into "$locale" "ON"; MCA_LBL_ON="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "OFF"; MCA_LBL_OFF="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "(none)"; MCA_LBL_NONE="$MCA_MSG_RESULT"
|
||||
|
||||
for spec in "${MCA_SETTINGS[@]}"; do
|
||||
IFS='|' read -r name type default check label help <<< "$spec"
|
||||
[[ -z $check ]] || "$check" || continue
|
||||
names+=("$name"); types+=("$type"); defaults+=("$default")
|
||||
mca_msg_into "$locale" "$label"
|
||||
labels+=("$MCA_MSG_RESULT")
|
||||
(( ${#MCA_MSG_RESULT} > width )) && width=${#MCA_MSG_RESULT}
|
||||
mca_msg_into "$locale" "$help"
|
||||
helps+=("$MCA_MSG_RESULT")
|
||||
done
|
||||
count=${#names[@]}
|
||||
|
||||
local title hint
|
||||
mca_msg_into "$locale" "Settings"; title="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "Up/Down: select, Space: change, q: back"
|
||||
hint="$MCA_MSG_RESULT"
|
||||
|
||||
local clearseq
|
||||
clearseq="$(clear 2>/dev/null)" || clearseq=$'\033[H\033[2J'
|
||||
|
||||
while true; do
|
||||
if (( dirty )); then
|
||||
for i in "${!names[@]}"; do
|
||||
_mca_config_lookup "${names[i]}" "${defaults[i]}"
|
||||
values[i]="$MCA_CONFIG_VALUE"
|
||||
done
|
||||
dirty=0
|
||||
fi
|
||||
|
||||
frame="$clearseq"$'\n'"${MCA_C_BOLD}${MCA_C_BLUE} ${title}${MCA_C_RESET}"$'\n\n'
|
||||
|
||||
local marker selected="${MCA_C_BLUE}▸${MCA_C_RESET} "
|
||||
for i in "${!names[@]}"; do
|
||||
_mca_setting_display "${types[i]}" "${values[i]}"
|
||||
pad=$(( width + 3 - ${#labels[i]} ))
|
||||
(( pad < 0 )) && pad=0
|
||||
if (( i == cursor )); then marker="$selected"; else marker=' '; fi
|
||||
printf -v row ' %s%s%*s %s' \
|
||||
"$marker" "${labels[i]}" "$pad" '' "$MCA_SETTING_SHOWN"
|
||||
frame+="$row"$'\n'
|
||||
done
|
||||
|
||||
frame+=$'\n'" ${helps[cursor]}"$'\n'
|
||||
frame+=$'\n'" ${MCA_C_DIM}${hint}${MCA_C_RESET}"$'\n'
|
||||
printf '%s' "$frame"
|
||||
|
||||
key="$(mca_read_key)" || return "$(( ! touched ))"
|
||||
|
||||
type="${types[cursor]}"
|
||||
name="${names[cursor]}"
|
||||
|
||||
case "$key" in
|
||||
up|k) cursor=$(( (cursor - 1 + count) % count )) ;;
|
||||
down|j) cursor=$(( (cursor + 1) % count )) ;;
|
||||
space|enter|right|l|left|h)
|
||||
if [[ $type == text ]]; then
|
||||
[[ $key == left || $key == h ]] && continue
|
||||
mca_ui_edit_text "$name" "${values[cursor]}" && touched=1
|
||||
else
|
||||
if _mca_is_true "${values[cursor]}"; then
|
||||
mca_config_set "$name" no
|
||||
else
|
||||
mca_config_set "$name" yes
|
||||
fi || { mca_bad "$(mca_msg "Could not save the setting.")"; mca_pause; }
|
||||
touched=1
|
||||
fi
|
||||
dirty=1
|
||||
;;
|
||||
q|Q|escape) return "$(( ! touched ))" ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# mca_ui_edit_text <key> <current>
|
||||
mca_ui_edit_text() {
|
||||
local name="$1" current="$2"
|
||||
|
||||
printf '\n %s\n' "$(mca_msg "Arguments separated by spaces, empty for none:")"
|
||||
printf ' > '
|
||||
|
||||
if mca_ui_read_line "$current"; then
|
||||
mca_config_set "$name" "$MCA_LINE_RESULT" \
|
||||
|| { mca_bad "$(mca_msg "Could not save the setting.")"; mca_pause; }
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Applications
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# mca_ui_apps
|
||||
# Every application that was found, what will happen to it, and a key to
|
||||
# change that. This is where an AppImage that could not be identified gets
|
||||
# turned on, and where a single application gets left out without having to
|
||||
# switch off its whole category.
|
||||
#
|
||||
# The screen opens on a search field above the list, so typing filters it
|
||||
# right away. The field is row -1: Down leaves it, Up from the top returns.
|
||||
#
|
||||
# Returns 0 when something was changed.
|
||||
mca_ui_apps() {
|
||||
local count key frame row pad i idx cursor=-1 dirty=1 touched=0 locale query='' needle
|
||||
local footer flines size rows height top=0
|
||||
local -a labels=() lower=() states=() matches=()
|
||||
|
||||
locale="$(mca_ui_locale)"
|
||||
|
||||
local title hint legend warn
|
||||
mca_msg_into "$locale" "Applications"; title="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "Up/Down: select, Space: turn on or off, q: back"
|
||||
hint="$MCA_MSG_RESULT"
|
||||
local l_search hint_search nomatch
|
||||
mca_msg_into "$locale" "Search:"; l_search="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "Type to search, Down: go to the list, Esc: back"
|
||||
hint_search="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "No matches."; nomatch="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" 'Anything marked "cannot tell" stays off until it is turned on here.'
|
||||
legend="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "Autoscroll is off. This is what would be covered."
|
||||
warn="$MCA_MSG_RESULT"
|
||||
|
||||
# The per-row labels are resolved once, here. Looking them up inside the
|
||||
# drawing loop is a fork per row per keypress, and that is enough to make
|
||||
# the arrow keys feel like the screen is reloading.
|
||||
local l_off l_cannot l_on l_steam l_flagfile l_launcher l_profile
|
||||
mca_msg_into "$locale" "off"; l_off="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "cannot tell"; l_cannot="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "on"; l_on="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "Steam"; l_steam="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "flag file"; l_flagfile="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "launcher"; l_launcher="$MCA_MSG_RESULT"
|
||||
mca_msg_into "$locale" "profile"; l_profile="$MCA_MSG_RESULT"
|
||||
|
||||
local s_off="${MCA_C_DIM}${l_off}${MCA_C_RESET}"
|
||||
local s_cannot="${MCA_C_DIM}${l_cannot}${MCA_C_RESET}"
|
||||
local s_on="${MCA_C_GREEN}${l_on}${MCA_C_RESET}"
|
||||
local s_steam="${s_on} ${MCA_C_DIM}(${l_steam})${MCA_C_RESET}"
|
||||
local s_flags="${s_on} ${MCA_C_DIM}(${l_flagfile})${MCA_C_RESET}"
|
||||
local s_desktop="${s_on} ${MCA_C_DIM}(${l_launcher})${MCA_C_RESET}"
|
||||
local s_spotify="${s_on} ${MCA_C_DIM}(spotify-launcher)${MCA_C_RESET}"
|
||||
local s_gecko="${s_on} ${MCA_C_DIM}(${l_profile})${MCA_C_RESET}"
|
||||
|
||||
local clearseq
|
||||
clearseq="$(clear 2>/dev/null)" || clearseq=$'\033[H\033[2J'
|
||||
|
||||
printf '%s\n %s\n' "$clearseq" "$(mca_msg "Looking at the installed applications...")"
|
||||
mca_config_load
|
||||
mca_scan_once
|
||||
count=${#MCA_IDS[@]}
|
||||
|
||||
if (( count == 0 )); then
|
||||
MCA_UI_MSG="$(mca_msg "No supported applications found.")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
while true; do
|
||||
if (( dirty )); then
|
||||
mca_config_load
|
||||
mca_count_routes
|
||||
labels=(); lower=(); states=()
|
||||
for i in "${!MCA_IDS[@]}"; do
|
||||
labels+=("${MCA_NAMES[i]}")
|
||||
row="${MCA_NAMES[i],,}"
|
||||
lower+=("${row//ı/i}")
|
||||
states+=("${MCA_ROUTES[i]}")
|
||||
done
|
||||
dirty=0
|
||||
fi
|
||||
|
||||
# In a Turkish locale I lowercases to a dotless ı, so "office" would
|
||||
# not find ONLYOFFICE. Both sides fold it back to i.
|
||||
needle="${query,,}"
|
||||
needle="${needle//ı/i}"
|
||||
matches=()
|
||||
for i in "${!labels[@]}"; do
|
||||
[[ ${lower[i]} == *"$needle"* ]] && matches+=("$i")
|
||||
done
|
||||
count=${#matches[@]}
|
||||
(( cursor >= count )) && cursor=$(( count - 1 ))
|
||||
|
||||
# The footer is built first: the list gets the rows it leaves over.
|
||||
footer='' flines=1
|
||||
# Without this the list reads as a list of what is switched on, which
|
||||
# it is not while the whole thing is off.
|
||||
if [[ $CFG_ENABLED != yes ]]; then
|
||||
footer+=" ${MCA_C_YELLOW}${warn}${MCA_C_RESET}"$'\n'
|
||||
flines=$(( flines + 1 ))
|
||||
fi
|
||||
# Only needed while there is something it explains.
|
||||
if (( MCA_N_UNKNOWN )); then
|
||||
footer+=" ${MCA_C_DIM}${legend}${MCA_C_RESET}"$'\n'
|
||||
flines=$(( flines + 1 ))
|
||||
fi
|
||||
if (( cursor < 0 )); then
|
||||
footer+=" ${MCA_C_DIM}${hint_search}${MCA_C_RESET}"$'\n'
|
||||
# Puts the terminal cursor at the end of the query (row 4).
|
||||
printf -v row '\033[4;%dH' $(( 6 + ${#l_search} + ${#query} ))
|
||||
footer+="$row"
|
||||
else
|
||||
footer+=" ${MCA_C_DIM}${hint}${MCA_C_RESET}"$'\n'
|
||||
fi
|
||||
|
||||
# A list longer than the terminal would scroll the search field off
|
||||
# the top. So only a window of it is drawn, and it follows the cursor.
|
||||
# Five rows above the list, one below it, the footer, and the row the
|
||||
# terminal cursor ends up on.
|
||||
# A terminal that does not know its size says 0; then all of it is drawn.
|
||||
size="$(stty size 2>/dev/null)"
|
||||
rows="${size%% *}"
|
||||
[[ $rows =~ ^[1-9][0-9]*$ ]] || rows=10000
|
||||
height=$(( rows - 7 - flines ))
|
||||
(( height < 1 )) && height=1
|
||||
(( top > count - height )) && top=$(( count - height ))
|
||||
(( top < 0 )) && top=0
|
||||
if (( cursor >= 0 )); then
|
||||
(( cursor < top )) && top=$cursor
|
||||
(( cursor >= top + height )) && top=$(( cursor - height + 1 ))
|
||||
fi
|
||||
|
||||
frame="$clearseq"$'\n'"${MCA_C_BOLD}${MCA_C_BLUE} ${title}${MCA_C_RESET}"$'\n\n'
|
||||
|
||||
local marker selected="${MCA_C_BLUE}▸${MCA_C_RESET} " shown
|
||||
if (( cursor < 0 )); then marker="$selected"; else marker=' '; fi
|
||||
frame+=" ${marker}${l_search} ${query}"$'\n\n'
|
||||
|
||||
for (( i = top; i < count && i < top + height; i++ )); do
|
||||
idx=${matches[i]}
|
||||
case "${states[idx]}" in
|
||||
off) shown="$s_off" ;;
|
||||
unknown) shown="$s_cannot" ;;
|
||||
steam) shown="$s_steam" ;;
|
||||
flags) shown="$s_flags" ;;
|
||||
spotify) shown="$s_spotify" ;;
|
||||
gecko) shown="$s_gecko" ;;
|
||||
*) shown="$s_desktop" ;;
|
||||
esac
|
||||
pad=$(( 34 - ${#labels[idx]} ))
|
||||
(( pad < 0 )) && pad=0
|
||||
if (( i == cursor )); then marker="$selected"; else marker=' '; fi
|
||||
printf -v row ' %s%s%*s %s' "$marker" "${labels[idx]}" "$pad" '' "$shown"
|
||||
frame+="$row"$'\n'
|
||||
done
|
||||
(( count )) || frame+=" ${MCA_C_DIM}${nomatch}${MCA_C_RESET}"$'\n'
|
||||
|
||||
# Where the window is, in the row under it, when there is more.
|
||||
if (( count > height )); then
|
||||
frame+=" ${MCA_C_DIM}$(( top + 1 ))–$(( i )) / ${count}${MCA_C_RESET}"$'\n'
|
||||
else
|
||||
frame+=$'\n'
|
||||
fi
|
||||
frame+="$footer"
|
||||
printf '%s' "$frame"
|
||||
|
||||
key="$(mca_read_key)" || return "$(( ! touched ))"
|
||||
|
||||
# In the search field every key that can be typed goes into the query,
|
||||
# q and the vi keys too.
|
||||
if (( cursor < 0 )); then
|
||||
case "$key" in
|
||||
down|enter) (( count )) && cursor=0 ;;
|
||||
up) cursor=$(( count - 1 )) ;;
|
||||
backspace) query="${query%?}" ;;
|
||||
space) query+=' ' ;;
|
||||
escape)
|
||||
[[ -z $query ]] && return "$(( ! touched ))"
|
||||
query=''
|
||||
;;
|
||||
left|right) ;;
|
||||
*) [[ $key == [[:print:]] ]] && query+="$key" ;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
|
||||
idx=${matches[cursor]}
|
||||
case "$key" in
|
||||
up|k) cursor=$(( cursor - 1 )) ;;
|
||||
down|j) cursor=$(( cursor + 1 < count ? cursor + 1 : -1 )) ;;
|
||||
space|enter|right|left|l|h)
|
||||
case "${states[idx]}" in
|
||||
off|unknown)
|
||||
mca_config_list_del Skip "${MCA_IDS[idx]}"
|
||||
mca_config_list_add Include "${MCA_IDS[idx]}"
|
||||
;;
|
||||
*)
|
||||
mca_config_list_del Include "${MCA_IDS[idx]}"
|
||||
mca_config_list_add Skip "${MCA_IDS[idx]}"
|
||||
;;
|
||||
esac
|
||||
touched=1
|
||||
dirty=1
|
||||
;;
|
||||
q|Q|escape) return "$(( ! touched ))" ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The menu
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
mca_ui_menu() {
|
||||
local choice line
|
||||
|
||||
MCA_IN_MENU=1
|
||||
mca_ui_term_raw
|
||||
trap 'mca_ui_term_restore' EXIT INT TERM
|
||||
|
||||
while true; do
|
||||
mca_config_load
|
||||
|
||||
clear 2>/dev/null || true
|
||||
mca_head " $MCA_PRETTY"
|
||||
mca_ui_status
|
||||
printf '\n'
|
||||
if [[ -n $MCA_UI_MSG ]]; then
|
||||
while IFS= read -r line; do
|
||||
printf ' %s\n' "$line"
|
||||
done <<< "$MCA_UI_MSG"
|
||||
printf '\n'
|
||||
MCA_UI_MSG=''
|
||||
fi
|
||||
printf ' [1] %s\n' "$(mca_msg "Turn autoscroll on or off")"
|
||||
printf ' [2] %s\n' "$(mca_msg "Re-apply everything")"
|
||||
printf ' [3] %s\n' "$(mca_msg "Applications")"
|
||||
printf ' [4] %s\n' "$(mca_msg "Settings")"
|
||||
printf ' [q] %s\n' "$(mca_msg "Quit")"
|
||||
printf '\n > '
|
||||
|
||||
choice="$(mca_read_key)" || {
|
||||
printf '\n'; mca_ui_term_restore; trap - EXIT INT TERM; return 0
|
||||
}
|
||||
case "$choice" in
|
||||
enter|space|up|down|left|right|escape) choice='' ;;
|
||||
esac
|
||||
printf '%s\n' "$choice"
|
||||
|
||||
case "$choice" in
|
||||
1)
|
||||
if [[ $CFG_ENABLED == yes ]]; then
|
||||
mca_ui_run mca_do_disable
|
||||
else
|
||||
mca_ui_run mca_do_enable
|
||||
fi
|
||||
;;
|
||||
# Not a plain apply: with the watcher running there is never
|
||||
# anything left for one to do, and a menu entry that answers
|
||||
# "already done" every time is not an action. This is the repair -
|
||||
# everything is taken back and written again from scratch, which is
|
||||
# what fixes an application that drifted, a flag file somebody
|
||||
# edited, or Steam after it restored its own script.
|
||||
2)
|
||||
mca_ui_run mca_do_apply --rebuild
|
||||
;;
|
||||
3)
|
||||
if mca_ui_apps; then
|
||||
[[ $CFG_ENABLED == yes ]] && mca_ui_run mca_do_apply --rebuild
|
||||
fi
|
||||
;;
|
||||
4)
|
||||
if mca_ui_settings; then
|
||||
[[ $CFG_ENABLED == yes ]] && mca_ui_run mca_do_apply --rebuild
|
||||
fi
|
||||
;;
|
||||
q|Q) mca_ui_term_restore; trap - EXIT INT TERM; return 0 ;;
|
||||
# Anything else (Enter, arrow keys, stray characters) just
|
||||
# redraws. Escape is deliberately not a quit key, so a mistyped
|
||||
# arrow key cannot close the menu.
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,692 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Putting the flag where the application will read it, and taking it back out.
|
||||
#
|
||||
# There are two ways in, and the order matters:
|
||||
#
|
||||
# 1. A flag file. Some distributions wrap Electron and Chromium in a
|
||||
# launcher script that reads extra arguments from
|
||||
# $XDG_CONFIG_HOME/<name>-flags.conf. Arch and its derivatives do, and
|
||||
# their packages follow that convention. Where it exists, this is the good
|
||||
# way in: it is the supported way to pass arguments, it survives package upgrades
|
||||
# untouched, and it applies however the program is started, including from
|
||||
# a terminal.
|
||||
#
|
||||
# Nothing is assumed about which distribution this is. The launcher itself
|
||||
# is read, and the route is taken only for a launcher that really does
|
||||
# name such a file. On Debian, Ubuntu, Fedora and openSUSE the
|
||||
# equivalent file lives under /etc and is the system's to write. There is
|
||||
# no flag file to use there, and route 2 answers for everything.
|
||||
#
|
||||
# 2. A desktop entry. For applications that ship their own binary with no
|
||||
# wrapper, and for everything a Flatpak or a snap contains, there is
|
||||
# nowhere else to put an argument, so a copy of the entry with the flag
|
||||
# appended goes into ~/.local/share/applications, where it shadows the
|
||||
# system one.
|
||||
#
|
||||
# Nothing outside $HOME is ever written to.
|
||||
|
||||
MCA_MARK_BEGIN='# >>> middleclick-autoscroll'
|
||||
MCA_MARK_END='# <<< middleclick-autoscroll'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Feature lists
|
||||
# ---------------------------------------------------------------------------
|
||||
# Both --enable-features and --enable-blink-features take a comma separated
|
||||
# list, and Chromium keeps the last one it is given and drops the rest. A
|
||||
# second one of the same kind would therefore silently switch off whatever the
|
||||
# first one turned on, so an option that is already there is extended instead.
|
||||
|
||||
# _mca_list_merge <list> <wanted>
|
||||
# The two lists as one, in the order they came and without repeating anything.
|
||||
_mca_list_merge() {
|
||||
local out="$1" item
|
||||
for item in ${2//,/ }; do
|
||||
[[ ",$out," == *",$item,"* ]] && continue
|
||||
out="${out:+$out,}$item"
|
||||
done
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
# _mca_list_strip <list> <unwanted>
|
||||
# The list without any of the named entries.
|
||||
_mca_list_strip() {
|
||||
local out='' item
|
||||
for item in ${1//,/ }; do
|
||||
[[ ",$2," == *",$item,"* ]] && continue
|
||||
out="${out:+$out,}$item"
|
||||
done
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
# The two options, as a regular expression branch, for the places that have to
|
||||
# recognise either of them.
|
||||
MCA_FEATURE_OPTS='--enable-blink-features|--enable-features'
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Command lines
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# _mca_exec_insert_at <exec line>
|
||||
# Where an argument has to go: after the program and its own arguments, but
|
||||
# before the first field code (%U and friends) or Flatpak's @@ markers, which
|
||||
# are placeholders the launcher expands rather than arguments.
|
||||
#
|
||||
# A bare "--" stops it too. That is where Chromium stops reading switches and
|
||||
# starts treating everything as an address, so a flag put after it is read as a
|
||||
# page to open and the feature never comes on. Discord's entry is exactly that
|
||||
# shape: `discord --url -- %u`.
|
||||
_mca_exec_insert_at() {
|
||||
local rest="$1" tok idx=0
|
||||
|
||||
while [[ -n $rest ]]; do
|
||||
while [[ $rest == ' '* ]]; do rest="${rest# }"; idx=$(( idx + 1 )); done
|
||||
[[ -n $rest ]] || break
|
||||
tok="${rest%% *}"
|
||||
case "$tok" in
|
||||
%[a-zA-Z]|@@|@@u|--) printf '%s\n' "$idx"; return 0 ;;
|
||||
esac
|
||||
rest="${rest:${#tok}}"
|
||||
idx=$(( idx + ${#tok} ))
|
||||
done
|
||||
|
||||
printf '%s\n' "$(( ${#1} ))"
|
||||
}
|
||||
|
||||
# _mca_exec_after_program <exec line>
|
||||
# The position just after the program name, for arguments that a program only
|
||||
# accepts before its positional ones.
|
||||
_mca_exec_after_program() {
|
||||
local rest="$1" idx=0 tok
|
||||
|
||||
while [[ $rest == ' '* ]]; do rest="${rest# }"; idx=$(( idx + 1 )); done
|
||||
tok="${rest%% *}"
|
||||
printf '%s\n' "$(( idx + ${#tok} ))"
|
||||
}
|
||||
|
||||
# mca_exec_inject <exec line> [flags] [position: before-fields|after-program]
|
||||
# The same line with the given flags added, defaulting to the configured ones.
|
||||
# Prints nothing and returns 1 when there was nothing to change, so callers can
|
||||
# tell a real edit from a no-op without diffing.
|
||||
#
|
||||
# A feature list that is already on the line is extended rather than repeated.
|
||||
# See _mca_list_merge.
|
||||
mca_exec_inject() {
|
||||
local line="$1" flagstr="${2:-}" where="${3:-before-fields}"
|
||||
local flag opt value existing merged re at changed=0
|
||||
local -a flags
|
||||
|
||||
[[ -n $flagstr ]] || flagstr="$(mca_flags)"
|
||||
read -r -a flags <<< "$flagstr"
|
||||
|
||||
for flag in "${flags[@]}"; do
|
||||
case "$flag" in
|
||||
--enable-blink-features=*|--enable-features=*)
|
||||
opt="${flag%%=*}"
|
||||
value="${flag#*=}"
|
||||
re="(^|[[:space:]])$opt=([^[:space:]]*)"
|
||||
if [[ $line =~ $re ]]; then
|
||||
existing="${BASH_REMATCH[2]}"
|
||||
merged="$(_mca_list_merge "$existing" "$value")"
|
||||
[[ $merged == "$existing" ]] && continue
|
||||
line="${line/$opt=$existing/$opt=$merged}"
|
||||
changed=1
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
[[ " $line " == *" $flag "* ]] && continue
|
||||
|
||||
if [[ $where == after-program ]]; then
|
||||
at="$(_mca_exec_after_program "$line")"
|
||||
line="${line:0:$at} $flag${line:$at}"
|
||||
else
|
||||
at="$(_mca_exec_insert_at "$line")"
|
||||
if (( at >= ${#line} )); then
|
||||
line="${line% } $flag"
|
||||
else
|
||||
line="${line:0:$at}$flag ${line:$at}"
|
||||
fi
|
||||
fi
|
||||
changed=1
|
||||
done
|
||||
|
||||
(( changed )) || return 1
|
||||
printf '%s\n' "$line"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Flag files
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# mca_flags_apply <program> [kind]
|
||||
# Writes the flags into the file the program's launcher reads. Returns 0 when
|
||||
# the program has such a file (whether or not anything needed changing), 1 when
|
||||
# it has none and the desktop entry route has to be used instead.
|
||||
mca_flags_apply() {
|
||||
local prog="$1" kind="${2:-app}" name target='' existing_target=''
|
||||
local -a candidates=()
|
||||
|
||||
mapfile -t candidates < <(mca_flags_candidates "$prog" | awk '!seen[$0]++')
|
||||
(( ${#candidates[@]} )) || return 1
|
||||
|
||||
# A wrapper that offers several files reads the first one that exists and
|
||||
# ignores the rest, so an existing file is always the right one to edit.
|
||||
for name in "${candidates[@]}"; do
|
||||
if [[ -f "$MCA_XDG_CONFIG/$name" ]]; then
|
||||
existing_target="$name"
|
||||
_mca_flags_write "$MCA_XDG_CONFIG/$name" "$kind"
|
||||
fi
|
||||
done
|
||||
[[ -n $existing_target ]] && return 0
|
||||
|
||||
# None exists yet. The last candidate is the wrapper's own fallback (the
|
||||
# generic electron-flags.conf rather than electron43-flags.conf). So
|
||||
# creating that one also covers every other application using the same
|
||||
# shared Electron.
|
||||
target="${candidates[-1]}"
|
||||
_mca_flags_write "$MCA_XDG_CONFIG/$target" "$kind"
|
||||
return 0
|
||||
}
|
||||
|
||||
# _mca_flags_write <file> [kind]
|
||||
# Adds the flags as a marked block, or merges into a feature list that is
|
||||
# already in the file.
|
||||
_mca_flags_write() {
|
||||
local file="$1" kind="${2:-app}"
|
||||
local content='' line flag opt want existing merged=0 block=''
|
||||
local -a flags
|
||||
local -A merged_opts=()
|
||||
local created=0
|
||||
|
||||
read -r -a flags <<< "$(mca_flags "$kind")"
|
||||
|
||||
[[ -f $file ]] || created=1
|
||||
if (( ! created )); then
|
||||
content="$(< "$file")"
|
||||
# Already ours: the block is rewritten below, so drop the old one first.
|
||||
if [[ $content == *"$MCA_MARK_BEGIN"* ]]; then
|
||||
content="$(_mca_flags_drop_block "$file")"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Merge into a feature list the user (or a distribution) put there, rather
|
||||
# than write a second line of the same option further down.
|
||||
if [[ $content == *--enable-* ]]; then
|
||||
local out=''
|
||||
while IFS= read -r line; do
|
||||
if [[ $line =~ ^[[:space:]]*($MCA_FEATURE_OPTS)=([^[:space:]]*)[[:space:]]*$ ]]; then
|
||||
opt="${BASH_REMATCH[1]}"
|
||||
existing="${BASH_REMATCH[2]}"
|
||||
want=''
|
||||
for flag in "${flags[@]}"; do
|
||||
[[ $flag == "$opt="* ]] && want="${flag#*=}"
|
||||
done
|
||||
if [[ -n $want ]]; then
|
||||
line="$opt=$(_mca_list_merge "$existing" "$want")"
|
||||
merged_opts["$opt"]=1
|
||||
merged=1
|
||||
fi
|
||||
fi
|
||||
out+="$line"$'\n'
|
||||
done <<< "$content"
|
||||
content="${out%$'\n'}"
|
||||
fi
|
||||
|
||||
for flag in "${flags[@]}"; do
|
||||
[[ -n ${merged_opts["${flag%%=*}"]:-} ]] && continue
|
||||
block+="$flag"$'\n'
|
||||
done
|
||||
|
||||
if [[ -n $block ]]; then
|
||||
content="${content:+$content$'\n'}$MCA_MARK_BEGIN"$'\n'"$block$MCA_MARK_END"
|
||||
fi
|
||||
|
||||
if mca_write_if_changed "$file" "$content"$'\n'; then
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
|
||||
if (( created )); then
|
||||
mca_ledger_add flags "$file" created
|
||||
elif (( merged )) && [[ -z $block ]]; then
|
||||
mca_ledger_add flags "$file" merged
|
||||
elif (( merged )); then
|
||||
mca_ledger_add flags "$file" merged+block
|
||||
else
|
||||
mca_ledger_add flags "$file" block
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# _mca_flags_drop_block <file> [begin marker] [end marker]
|
||||
_mca_flags_drop_block() {
|
||||
awk -v b="${2:-$MCA_MARK_BEGIN}" -v e="${3:-$MCA_MARK_END}" '
|
||||
$0 == b { skip = 1; next }
|
||||
$0 == e { skip = 0; next }
|
||||
!skip
|
||||
' "$1" 2>/dev/null
|
||||
}
|
||||
|
||||
# mca_flags_revert <file> <detail>
|
||||
mca_flags_revert() {
|
||||
local file="$1" detail="$2" content line out='' opt existing new
|
||||
|
||||
[[ -f $file ]] || return 0
|
||||
|
||||
case "$detail" in
|
||||
created)
|
||||
# Only remove the file if nothing but our block ended up in it;
|
||||
# anything else means the user started using it in the meantime.
|
||||
content="$(_mca_flags_drop_block "$file")"
|
||||
if [[ -z ${content//[[:space:]]/} ]]; then
|
||||
rm -f -- "$file"
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
content="$(_mca_flags_drop_block "$file")"
|
||||
|
||||
if [[ $detail == merged* ]]; then
|
||||
while IFS= read -r line; do
|
||||
if [[ $line =~ ^[[:space:]]*($MCA_FEATURE_OPTS)=([^[:space:]]*)[[:space:]]*$ ]]; then
|
||||
opt="${BASH_REMATCH[1]}"
|
||||
existing="${BASH_REMATCH[2]}"
|
||||
# Either name, whichever version of this program put it there.
|
||||
new="$(_mca_list_strip "$existing" "$MCA_BROWSER_FEATURES")"
|
||||
[[ -z $new ]] && continue
|
||||
line="$opt=$new"
|
||||
fi
|
||||
out+="$line"$'\n'
|
||||
done <<< "$content"
|
||||
content="${out%$'\n'}"
|
||||
fi
|
||||
|
||||
if [[ -z ${content//[[:space:]]/} ]]; then
|
||||
rm -f -- "$file"
|
||||
else
|
||||
printf '%s\n' "$content" > "$file"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Desktop entries
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# A generated entry and an entry that was edited in place look similar and must
|
||||
# never be confused: the first one is ours to delete, the second one is the
|
||||
# user's file with one line changed and has to be restored from its backup. They
|
||||
# carry different markers so that the scan, the "already done" checks and the
|
||||
# undo can all tell them apart.
|
||||
MCA_MARK_SHADOW='X-MCA-Generated'
|
||||
MCA_MARK_INPLACE='X-MCA-Patched'
|
||||
|
||||
# _mca_desktop_transform <file> <marker> [flags] [position]
|
||||
# The whole file with every Exec line rewritten: the main one and the one in
|
||||
# each Desktop Action, because those are the right-click menu entries and a
|
||||
# user who starts Steam from "Library" expects the same behaviour there.
|
||||
_mca_desktop_transform() {
|
||||
local file="$1" marker="$2" flags="${3:-}" where="${4:-before-fields}"
|
||||
local line rest new marked=0
|
||||
|
||||
while IFS= read -r line || [[ -n $line ]]; do
|
||||
case "$line" in
|
||||
Exec=*)
|
||||
rest="${line#Exec=}"
|
||||
new="$(mca_exec_inject "$rest" "$flags" "$where")" && line="Exec=$new"
|
||||
;;
|
||||
# A D-Bus activated entry is started through its service file and
|
||||
# the Exec line is never used, so the flag would go nowhere.
|
||||
DBusActivatable=true)
|
||||
line='DBusActivatable=false'
|
||||
;;
|
||||
# A marker from an older version; the current one is written below.
|
||||
X-MCA-*)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
printf '%s\n' "$line"
|
||||
|
||||
if (( ! marked )) && [[ $line =~ ^[[:space:]]*\[Desktop\ Entry\][[:space:]]*$ ]]; then
|
||||
printf '%s=%s\n' "$marker" "$MCA_VERSION"
|
||||
marked=1
|
||||
fi
|
||||
done < "$file"
|
||||
}
|
||||
|
||||
# mca_desktop_apply <id> <source entry> [flags] [position]
|
||||
# Puts a patched copy in ~/.local/share/applications, or patches the entry in
|
||||
# place when it already lives there.
|
||||
mca_desktop_apply() {
|
||||
local id="$1" src="$2" flags="${3:-}" where="${4:-before-fields}"
|
||||
local target="$MCA_APPDIR/$id.desktop" content backup
|
||||
|
||||
if [[ "$src" == "$target" ]]; then
|
||||
# The user's own entry, like an AppImage, a web app shortcut or
|
||||
# something installed by hand. There is nowhere to shadow it from, so it is
|
||||
# edited directly and the original is kept.
|
||||
grep -q "^$MCA_MARK_INPLACE=" "$src" 2>/dev/null && return 0
|
||||
|
||||
content="$(_mca_desktop_transform "$src" "$MCA_MARK_INPLACE" "$flags" "$where")"
|
||||
[[ -n $content ]] || return 1
|
||||
[[ "$content" == "$(< "$src")" ]] && return 0
|
||||
|
||||
backup="$(mca_backup "$src")" || return 1
|
||||
if _mca_entry_write "$src" "$content"$'\n'; then
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
mca_ledger_add inplace "$src" "$backup"
|
||||
return 0
|
||||
fi
|
||||
|
||||
content="$(_mca_desktop_transform "$src" "$MCA_MARK_SHADOW" "$flags" "$where")"
|
||||
[[ -n $content ]] || return 1
|
||||
|
||||
mkdir -p "$MCA_APPDIR" 2>/dev/null || return 1
|
||||
|
||||
# Something is already shadowing this entry. Unless it is a shadow of ours,
|
||||
# it is the user's own file (possibly one we edited in place earlier), and
|
||||
# overwriting it here would lose it.
|
||||
if [[ -e $target ]] && ! grep -q "^$MCA_MARK_SHADOW=" "$target" 2>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
if mca_write_if_changed "$target" "$content"$'\n'; then
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
mca_ledger_add shadow "$target" "$src"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Entries outside the search path
|
||||
# ---------------------------------------------------------------------------
|
||||
# Two directories hold desktop entries that no part of the XDG search path
|
||||
# looks at, so nothing the scan does can reach them:
|
||||
#
|
||||
# ~/.config/autostart, where applications that start themselves at login
|
||||
# write their own entry, pointing straight at their binary. Those bypass the
|
||||
# entry in the menu completely, which is why Discord launched at login used
|
||||
# to behave differently from Discord launched from the menu.
|
||||
#
|
||||
# The desktop folder, where a shortcut somebody dragged out of the menu
|
||||
# (or asked Steam for) lives and nowhere else.
|
||||
#
|
||||
# Neither can be shadowed from anywhere, so both are edited where they stand,
|
||||
# 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>
|
||||
# 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
|
||||
# under its own file name, which is the name of the application's own entry.
|
||||
_mca_entry_patch_inplace() {
|
||||
local file="$1"
|
||||
local id prog packaging=native content backup kind copy
|
||||
|
||||
# Already ours, and normally that is the end of it: what is in there is what
|
||||
# this version put there. Not so for a file the ledger has no record of -
|
||||
# nothing would ever look at that one again, neither the undo a change of
|
||||
# flags goes through nor this, which leaves a marked file alone, and it would
|
||||
# keep an answer from an older version for good. The copy taken before the
|
||||
# edit is named after the path and is still there, so the file goes back to
|
||||
# what it was and is written again below.
|
||||
if grep -q "^$MCA_MARK_INPLACE=" "$file" 2>/dev/null; then
|
||||
mca_ledger_has "$file" && return 0
|
||||
copy="$MCA_BACKUPDIR/$(mca_backup_name "$file")"
|
||||
[[ -f $copy ]] || return 0
|
||||
cp -p -- "$copy" "$file" 2>/dev/null || return 0
|
||||
fi
|
||||
|
||||
_mca_desktop_read "$file"
|
||||
[[ -n $DE_EXEC ]] || return 0
|
||||
[[ $DE_HIDDEN == true ]] && return 0
|
||||
|
||||
mca_exec_program "$DE_EXEC" || return 0
|
||||
prog="$MCA_PROG"
|
||||
|
||||
# A Flatpak or a snap entry names the wrapper rather than the application.
|
||||
# The scan resolves those into an id; outside the scan the same has to
|
||||
# happen here, or every Flatpak looks like the flatpak program itself.
|
||||
if [[ ${prog##*/} == flatpak ]]; then
|
||||
mca_exec_flatpak_id "$DE_EXEC" || return 0
|
||||
prog="flatpak:$MCA_PROG"
|
||||
packaging=flatpak
|
||||
elif mca_snap_name "$prog"; then
|
||||
prog="snap:$MCA_PROG"
|
||||
packaging=snap
|
||||
fi
|
||||
|
||||
# An entry that starts Steam is left where it is. Steam takes nothing
|
||||
# from a command line that would help, and what it does take belongs to
|
||||
# the web helper script in its own installation.
|
||||
mca_prog_is_steam "$prog" && return 0
|
||||
|
||||
case "$packaging" in
|
||||
flatpak) mca_flatpak_is_chromium "${prog#flatpak:}" || return 0 ;;
|
||||
snap) mca_snap_is_chromium "${prog#snap:}" || return 0 ;;
|
||||
*) mca_is_chromium "$prog" || return 0 ;;
|
||||
esac
|
||||
|
||||
mca_desktop_is_browser && kind=browser || kind=app
|
||||
|
||||
id="${file##*/}"; id="${id%.desktop}"
|
||||
mca_kind_wanted "$kind" "$id" || return 0
|
||||
|
||||
content="$(_mca_desktop_transform "$file" "$MCA_MARK_INPLACE" \
|
||||
"$(mca_flags "$kind")")"
|
||||
|
||||
[[ -n $content ]] || return 0
|
||||
[[ "$content" == "$(< "$file")" ]] && return 0
|
||||
|
||||
backup="$(mca_backup "$file")" || return 0
|
||||
if _mca_entry_write "$file" "$content"$'\n'; then
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
mca_ledger_add inplace "$file" "$backup"
|
||||
}
|
||||
|
||||
mca_autostart_apply() {
|
||||
local dir="$MCA_XDG_CONFIG/autostart" file
|
||||
|
||||
[[ -d $dir ]] || return 0
|
||||
|
||||
for file in "$dir"/*.desktop; do
|
||||
[[ -f $file ]] || continue
|
||||
_mca_entry_patch_inplace "$file"
|
||||
done
|
||||
}
|
||||
|
||||
mca_shortcuts_apply() {
|
||||
local dir file
|
||||
dir="$(mca_desktop_folder)"
|
||||
|
||||
[[ -d $dir ]] || return 0
|
||||
|
||||
for file in "$dir"/*.desktop; do
|
||||
[[ -f $file ]] || continue
|
||||
_mca_entry_patch_inplace "$file"
|
||||
done
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Spotify
|
||||
# ---------------------------------------------------------------------------
|
||||
# The official client is CEF rather than Electron and, when installed through
|
||||
# spotify-launcher, is started by a program that builds its own command line.
|
||||
# That launcher has a configuration file with a slot for exactly this.
|
||||
|
||||
mca_spotify_apply() {
|
||||
local file="$MCA_XDG_CONFIG/spotify-launcher.conf" content line out='' backup
|
||||
local found_section=0 found_args=0
|
||||
|
||||
mca_have spotify-launcher || return 0
|
||||
|
||||
if [[ -f $file ]]; then
|
||||
content="$(< "$file")"
|
||||
while IFS= read -r line; do
|
||||
if [[ $line =~ ^[[:space:]]*\[spotify\][[:space:]]*$ ]]; then
|
||||
found_section=1
|
||||
elif [[ $line =~ ^[[:space:]]*extra_arguments[[:space:]]*=[[:space:]]*\[(.*)\][[:space:]]*$ ]]; then
|
||||
local args="${BASH_REMATCH[1]}"
|
||||
found_args=1
|
||||
if [[ $args != *"$MCA_FEATURE"* ]]; then
|
||||
line="extra_arguments = [${args:+$args, }\"$MCA_FLAG\"]"
|
||||
fi
|
||||
fi
|
||||
out+="$line"$'\n'
|
||||
done <<< "$content"
|
||||
content="${out%$'\n'}"
|
||||
fi
|
||||
|
||||
if (( ! found_args )); then
|
||||
(( found_section )) || content="${content:+$content$'\n'}[spotify]"
|
||||
content="${content:+$content$'\n'}extra_arguments = [\"$MCA_FLAG\"]"
|
||||
fi
|
||||
|
||||
[[ -f $file ]] && backup="$(mca_backup "$file")"
|
||||
|
||||
if mca_write_if_changed "$file" "$content"$'\n'; then
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
mca_ledger_add inplace "$file" "${backup:-}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Leftovers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# mca_prune_orphans
|
||||
# Forgets changes there is no longer anything to undo.
|
||||
#
|
||||
# Uninstalling something deletes its entry from /usr/share/applications, but the
|
||||
# copy shadowing it is in the user's home and pacman knows nothing about it. It
|
||||
# would sit in the menu forever, offering to start a program that is no longer
|
||||
# installed. And the watcher would not notice, because a plain apply only ever
|
||||
# looks at what is there now. A file that was edited where it stood and has
|
||||
# since been deleted is the same problem from the other side: its record would
|
||||
# have `disable` put the file back.
|
||||
mca_prune_orphans() {
|
||||
local kind path source line
|
||||
local -a lines=()
|
||||
|
||||
[[ -f $MCA_LEDGER ]] || return 0
|
||||
mapfile -t lines < "$MCA_LEDGER"
|
||||
|
||||
for line in "${lines[@]}"; do
|
||||
IFS=$'\t' read -r kind path source <<< "$line"
|
||||
|
||||
# An entry that was edited where it stood and has since been deleted -
|
||||
# a game shortcut dragged to the wastebasket, an autostart entry the
|
||||
# application removed. There is nothing left to put back, and keeping
|
||||
# the record would have `disable` recreate a file the user threw away.
|
||||
if [[ $kind == inplace && ! -e $path ]]; then
|
||||
[[ -n $source ]] && rm -f -- "$MCA_BACKUPDIR/$source"
|
||||
mca_ledger_forget "$path"
|
||||
continue
|
||||
fi
|
||||
|
||||
[[ $kind == shadow && -n $source ]] || continue
|
||||
[[ -e $source ]] && continue
|
||||
|
||||
# Only ever remove something still recognisably ours.
|
||||
if [[ -f $path ]] && grep -q "^$MCA_MARK_SHADOW=" "$path" 2>/dev/null; then
|
||||
rm -f -- "$path"
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
mca_ledger_forget "$path"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Undo
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
mca_revert_all() {
|
||||
local kind path detail backup
|
||||
local -a keep=()
|
||||
|
||||
[[ -f $MCA_LEDGER ]] || return 0
|
||||
|
||||
# Backwards, so a file that was recorded twice ends up at its oldest state.
|
||||
while IFS=$'\t' read -r kind path detail; do
|
||||
[[ -n $path ]] || continue
|
||||
case "$kind" in
|
||||
shadow)
|
||||
# Only remove what is still recognisably ours.
|
||||
if [[ -f $path ]] && grep -q "^$MCA_MARK_SHADOW=" "$path" 2>/dev/null; then
|
||||
rm -f -- "$path"
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
;;
|
||||
inplace)
|
||||
backup="$MCA_BACKUPDIR/$detail"
|
||||
if [[ -n $detail ]] && [[ -f $backup || -L $backup ]]; then
|
||||
if [[ -e $path ]] || [[ -d "$(dirname -- "$path")" ]]; then
|
||||
# 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 ))
|
||||
fi
|
||||
rm -f -- "$backup"
|
||||
elif [[ -f $path ]]; then
|
||||
# No backup: the file did not exist before we wrote it.
|
||||
rm -f -- "$path"
|
||||
MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
fi
|
||||
;;
|
||||
flags)
|
||||
mca_flags_revert "$path" "$detail" \
|
||||
&& MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
;;
|
||||
steam)
|
||||
mca_steam_revert "$path" "$detail" \
|
||||
&& MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
;;
|
||||
kwin)
|
||||
mca_kde_paste_revert "$path" "$detail" \
|
||||
&& MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
;;
|
||||
gecko)
|
||||
mca_gecko_revert "$path" "$detail" \
|
||||
&& MCA_CHANGES=$(( MCA_CHANGES + 1 ))
|
||||
# Still open, so not done yet. See gecko.sh.
|
||||
(( MCA_GECKO_PENDING )) && keep+=("$kind"$'\t'"$path"$'\t'"$detail")
|
||||
;;
|
||||
esac
|
||||
done < <(tac "$MCA_LEDGER" 2>/dev/null)
|
||||
|
||||
rm -f -- "$MCA_LEDGER"
|
||||
(( ${#keep[@]} )) && printf '%s\n' "${keep[@]}" > "$MCA_LEDGER"
|
||||
rmdir "$MCA_BACKUPDIR" 2>/dev/null || true
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
# shellcheck shell=bash
|
||||
#
|
||||
# Steam.
|
||||
#
|
||||
# Steam's interface is CEF, so the feature is there. But Steam builds the
|
||||
# command line for its web helper itself and offers no way to add to it. The
|
||||
# only place to get an argument in is the shell script that starts the helper,
|
||||
# which lives inside Steam's own installation:
|
||||
#
|
||||
# ~/.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
|
||||
# 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
|
||||
# back out of the script's comments and the timestamp is put back afterwards,
|
||||
# which leaves a file that is byte-for-byte the size Steam recorded and as old
|
||||
# as Steam left it. A client that verifies its files finds nothing to repair
|
||||
# and the flag survives however Steam was started: from the menu, from a game
|
||||
# shortcut, from a launcher like Heroic, or from a terminal.
|
||||
#
|
||||
# A patch that changes the length is not an option, and no switch covers for
|
||||
# one. Steam checks its files at a start it was given arguments for, but also
|
||||
# at the shutdown it runs itself. That one has no -noverifyfiles on its
|
||||
# command line, whatever the session was started with. What a single wrong
|
||||
# length costs is the whole client package downloaded, extracted and installed
|
||||
# again, and a client that quits at the end of it instead of coming up. So the
|
||||
# flag either fits in the space the comments give back, or it is not written at
|
||||
# all: no autoscroll in the interface is a smaller thing than a client that
|
||||
# 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
|
||||
# patch when that happens.
|
||||
|
||||
# Every place a Steam installation is known to live, resolved and de-duplicated
|
||||
# because ~/.steam/steam is normally a symlink into ~/.local/share.
|
||||
#
|
||||
# Which of these is the real one depends on how Steam was installed: Valve's
|
||||
# own package and the Arch one use ~/.local/share/Steam, Debian's puts it in
|
||||
# ~/.steam/debian-installation, and the Flatpak and the snap each keep it
|
||||
# inside the private tree their sandbox gives them.
|
||||
mca_steam_roots() {
|
||||
local candidates=(
|
||||
"$MCA_XDG_DATA/Steam"
|
||||
"$HOME/.steam/steam"
|
||||
"$HOME/.steam/root"
|
||||
"$HOME/.steam/debian-installation"
|
||||
"$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam"
|
||||
"$HOME/.var/app/com.valvesoftware.Steam/data/Steam"
|
||||
"$HOME/snap/steam/common/.local/share/Steam"
|
||||
)
|
||||
local dir real
|
||||
local -A seen=()
|
||||
|
||||
for dir in "${candidates[@]}"; do
|
||||
[[ -d $dir ]] || continue
|
||||
real="$(readlink -f -- "$dir" 2>/dev/null)" || continue
|
||||
[[ -n ${seen[$real]+set} ]] && continue
|
||||
seen[$real]=1
|
||||
printf '%s\n' "$real"
|
||||
done
|
||||
}
|
||||
|
||||
# The scripts to patch inside a Steam installation, one per client. The default
|
||||
# client starts the helper through a wrapper inside the container runtime;
|
||||
# older ones exec it from steamwebhelper.sh directly, and that one forwards its
|
||||
# arguments too. The steamrt3 client has its own. Both clients are patched
|
||||
# where both are there, so switching between them keeps autoscroll.
|
||||
mca_steam_scripts() {
|
||||
local root="$1" f found=1
|
||||
for f in \
|
||||
"$root/ubuntu12_64/steamwebhelper_sniper_wrap.sh" \
|
||||
"$root/ubuntu12_64/steamwebhelper.sh"
|
||||
do
|
||||
[[ -f $f && -w $f ]] && { printf '%s\n' "$f"; found=0; break; }
|
||||
done
|
||||
f="$root/steamrt64/steamwebhelper.sh"
|
||||
[[ -f $f && -w $f ]] && { printf '%s\n' "$f"; found=0; }
|
||||
return "$found"
|
||||
}
|
||||
|
||||
mca_steam_installed() {
|
||||
local root
|
||||
while IFS= read -r root; do
|
||||
mca_steam_scripts "$root" > /dev/null && return 0
|
||||
done < <(mca_steam_roots)
|
||||
return 1
|
||||
}
|
||||
|
||||
# mca_steam_script_patched <script>
|
||||
mca_steam_script_patched() {
|
||||
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() {
|
||||
local root script found=0
|
||||
while IFS= read -r root; do
|
||||
while IFS= read -r script; do
|
||||
mca_steam_script_patched "$script" || return 1
|
||||
found=1
|
||||
done < <(mca_steam_scripts "$root")
|
||||
done < <(mca_steam_roots)
|
||||
(( found ))
|
||||
}
|
||||
|
||||
# _mca_steam_exec_line <script>
|
||||
# The line number of the command that starts the web helper: the last
|
||||
# uncommented line that both names steamwebhelper and forwards "$@". Both the
|
||||
# current layout (exec ./steamwebhelper "$@" inside the container wrapper) and
|
||||
# the older one (steamwebhelper.sh handing the arguments to the runtime entry
|
||||
# point) end in exactly that.
|
||||
_mca_steam_exec_line() {
|
||||
awk '
|
||||
/^[[:space:]]*#/ { next }
|
||||
/steamwebhelper/ && /"\$@"/ { n = NR }
|
||||
END { if (n) print n }
|
||||
' "$1" 2>/dev/null
|
||||
}
|
||||
|
||||
# _mca_steam_build <script> <flags>
|
||||
# The patched script on stdout: the flags appended to the line that starts the
|
||||
# web helper, and the same number of bytes taken back out of the script's
|
||||
# comments, so that the file Steam finds is the length Steam wrote down.
|
||||
# Everything else is left alone, like setting up the container runtime or
|
||||
# deciding whether the sandbox can be used.
|
||||
#
|
||||
# Comments are eaten from the bottom up, so the header that says what the
|
||||
# script is for is the last thing to lose anything, and the '#' itself always
|
||||
# stays: a line that loses it stops being a comment and starts being a command.
|
||||
# Fails when the comments are too short to pay for the flags.
|
||||
_mca_steam_build() {
|
||||
local script="$1" flags="$2" lineno need
|
||||
|
||||
lineno="$(_mca_steam_exec_line "$script")"
|
||||
[[ -n $lineno ]] || return 1
|
||||
|
||||
need="$(printf '%s' " $flags" | wc -c)"
|
||||
need="${need//[^0-9]/}"
|
||||
|
||||
# LC_ALL=C so awk counts bytes: a comment with an accent in it is shorter
|
||||
# in characters than it is on disk, and it is the disk that Steam measures.
|
||||
LC_ALL=C awk -v n="$lineno" -v f=" $flags" -v need="$need" '
|
||||
{ lines[NR] = (NR == n ? $0 f : $0) }
|
||||
END {
|
||||
for (i = NR; i >= 2 && need > 0; i--) {
|
||||
if (i == n) continue
|
||||
p = index(lines[i], "#")
|
||||
if (p == 0) continue
|
||||
# Only a line that is nothing but a comment; a trailing one
|
||||
# sits behind a command that has to stay intact.
|
||||
if (substr(lines[i], 1, p - 1) ~ /[^ \t]/) continue
|
||||
can = length(lines[i]) - p
|
||||
if (can <= 0) continue
|
||||
if (can > need) can = need
|
||||
lines[i] = substr(lines[i], 1, length(lines[i]) - can)
|
||||
need -= can
|
||||
}
|
||||
if (need > 0) exit 1
|
||||
for (i = 1; i <= NR; i++) print lines[i]
|
||||
}
|
||||
' "$script"
|
||||
}
|
||||
|
||||
# _mca_steam_same_size <script> <content>
|
||||
# Whether writing that content leaves the file at the size it has now. Measured
|
||||
# rather than worked out: a script without a final newline gains one on the way
|
||||
# through awk, and one byte is all it takes for Steam to notice.
|
||||
_mca_steam_same_size() {
|
||||
local script="$1" content="$2"
|
||||
(( $(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>
|
||||
# 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
|
||||
# script is unpatched, so the copy that replaces it is Steam's own.
|
||||
_mca_steam_refresh_backup() {
|
||||
local script="$1" name
|
||||
name="$(mca_backup_name "$script")"
|
||||
|
||||
[[ -e "$MCA_BACKUPDIR/$name" ]] || return 0
|
||||
cmp -s -- "$script" "$MCA_BACKUPDIR/$name" && return 0
|
||||
cp -p -- "$script" "$MCA_BACKUPDIR/$name" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# _mca_steam_settle <script> <flags>
|
||||
# The script is already patched. Whether that is a patch Steam can live with is
|
||||
# a question of length: an earlier version of this program appended the flag
|
||||
# where the comments could not pay for it, which leaves the script longer than
|
||||
# the file Steam wrote down. A client that checks its files reinstalls itself
|
||||
# over that, then quits instead of starting.
|
||||
#
|
||||
# Rebuilt from the untouched original kept when the script was first patched,
|
||||
# never from the patched file, the flag either fits this time or the original
|
||||
# goes back and Steam is left with a client it recognises.
|
||||
_mca_steam_settle() {
|
||||
local script="$1" flags="$2" name copy out
|
||||
|
||||
name="$(mca_backup_name "$script")"
|
||||
copy="$MCA_BACKUPDIR/$name"
|
||||
|
||||
# Nothing to compare against, so nothing can be said about the length.
|
||||
[[ -f $copy ]] || return 0
|
||||
|
||||
# Already the length Steam expects: the patch is a good one. Unless it is
|
||||
# a stub that lost Steam's script next to it, and would start nothing.
|
||||
if (( $(wc -c < "$script") == $(wc -c < "$copy") )); then
|
||||
if _mca_steam_is_stub "$script" && [[ ! -f $script$MCA_STEAM_ORIG ]]; then
|
||||
cp -p -- "$copy" "$script$MCA_STEAM_ORIG" 2>/dev/null || true
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
if out="$(_mca_steam_text "$copy" "$flags")" \
|
||||
&& _mca_steam_write "$script" "$copy" "$out"
|
||||
then
|
||||
mca_ledger_add steam "$script" "$name"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mca_steam_revert "$script" "$name"
|
||||
mca_ledger_forget "$script"
|
||||
mca_note "$(mca_msg "Steam's interface cannot be given the flag without leaving a file Steam would reinstall; the change has been taken back.")"
|
||||
return 0
|
||||
}
|
||||
|
||||
# mca_steam_apply
|
||||
# Puts the flags into every Steam installation that has the script, and only
|
||||
# where that can be done without changing how long the file is. Returns 1 when
|
||||
# there is no Steam to patch at all.
|
||||
mca_steam_apply() {
|
||||
local root script backup flags found=0 out
|
||||
|
||||
flags="$(mca_flags)"
|
||||
|
||||
while IFS= read -r root; do
|
||||
while IFS= read -r script; do
|
||||
found=1
|
||||
|
||||
if mca_steam_script_patched "$script"; then
|
||||
_mca_steam_settle "$script" "$flags"
|
||||
continue
|
||||
fi
|
||||
|
||||
_mca_steam_refresh_backup "$script"
|
||||
|
||||
# Either the file keeps its length or nothing is written. A
|
||||
# longer file is one Steam reinstalls itself over; a client
|
||||
# update that changed how the helper is started leaves no line
|
||||
# to patch at all. Both end here, and this script is what starts
|
||||
# Steam's entire interface, so leaving it alone is the only safe
|
||||
# answer to either.
|
||||
if ! out="$(_mca_steam_text "$script" "$flags")"; then
|
||||
mca_note "$(mca_msg "Steam starts its interface in a way this version cannot change without Steam noticing; leaving it alone.")"
|
||||
continue
|
||||
fi
|
||||
|
||||
backup="$(mca_backup "$script")" || continue
|
||||
_mca_steam_write "$script" "$MCA_BACKUPDIR/$backup" "$out" || continue
|
||||
mca_ledger_add steam "$script" "$backup"
|
||||
done < <(mca_steam_scripts "$root")
|
||||
done < <(mca_steam_roots)
|
||||
|
||||
(( found ))
|
||||
}
|
||||
|
||||
# mca_steam_revert <script> <backup name>
|
||||
mca_steam_revert() {
|
||||
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 [[ -e $script ]]; then
|
||||
cp -p -- "$MCA_BACKUPDIR/$backup" "$script" 2>/dev/null || true
|
||||
chmod +x -- "$script" 2>/dev/null || true
|
||||
fi
|
||||
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
|
||||
fi
|
||||
|
||||
# The copy goes once the stub that ran it is gone.
|
||||
if (( stub )) && ! _mca_steam_is_stub "$script"; then
|
||||
rm -f -- "$orig"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# middleclick-autoscroll: middle-click autoscroll for Chromium-based
|
||||
# applications and Firefox on Linux
|
||||
#
|
||||
# Blink has had Windows-style autoscroll for years, behind a runtime flag that
|
||||
# is not turned on for Linux because middle click is taken by primary-selection
|
||||
# paste there. Turning it on is a matter of one command line argument. The
|
||||
# work is finding every application that needs it, putting the argument
|
||||
# somewhere it survives an update, and doing the same for the two programs that
|
||||
# do not take arguments at all. Firefox has the same feature behind a setting.
|
||||
#
|
||||
# Copyright (C) 2026 Felitendo
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
MCA_LIBDIR="${MCA_LIBDIR:-@LIBDIR@}"
|
||||
|
||||
for _mod in common config detect patch steam gecko kde apply menu; do
|
||||
# shellcheck source=/dev/null
|
||||
if ! source "$MCA_LIBDIR/$_mod.sh"; then
|
||||
printf 'middleclick-autoscroll: cannot load %s/%s.sh\n' "$MCA_LIBDIR" "$_mod" >&2
|
||||
exit 14
|
||||
fi
|
||||
done
|
||||
unset _mod
|
||||
|
||||
# Nothing here writes outside $HOME, so running it as root would patch root's
|
||||
# own applications and leave the actual user without any.
|
||||
if [[ $EUID -eq 0 && -z ${MCA_ALLOW_ROOT:-} ]]; then
|
||||
printf 'middleclick-autoscroll: run this as your own user, not as root.\n' >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Commands
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
mca_do_enable() {
|
||||
mca_config_set Enabled yes || {
|
||||
mca_bad "$(mca_msg "Could not save the setting.")"
|
||||
return 1
|
||||
}
|
||||
mca_config_load
|
||||
|
||||
mca_apply
|
||||
|
||||
if [[ $CFG_WATCH == yes ]]; then
|
||||
if mca_watch_available; then
|
||||
mca_watch_enable || mca_note "$(mca_msg "Could not start the watcher for newly installed applications.")"
|
||||
else
|
||||
mca_note "$(mca_msg "No systemd user session, so newly installed applications will not be picked up on their own.")"
|
||||
fi
|
||||
fi
|
||||
|
||||
mca_ok "$(mca_msg "Autoscroll is on for %d applications." "$MCA_N_ON")"
|
||||
|
||||
[[ -n $MCA_IN_MENU ]] \
|
||||
|| mca_note "$(mca_msg "Applications that are already running pick this up when they are restarted.")"
|
||||
}
|
||||
|
||||
mca_do_disable() {
|
||||
mca_config_set Enabled no || {
|
||||
mca_bad "$(mca_msg "Could not save the setting.")"
|
||||
return 1
|
||||
}
|
||||
|
||||
mca_revert
|
||||
mca_watch_disable
|
||||
|
||||
mca_ok "$(mca_msg "Autoscroll is off and everything that was changed has been put back.")"
|
||||
}
|
||||
|
||||
mca_do_apply() {
|
||||
local rebuild=0 arg
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--rebuild) rebuild=1 ;;
|
||||
--quiet) MCA_QUIET=1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
mca_config_load
|
||||
|
||||
if [[ $CFG_ENABLED != yes ]]; then
|
||||
# Called by the watcher while switched off: there is nothing to do, and
|
||||
# nothing to complain about either.
|
||||
[[ -n $MCA_QUIET ]] && return 0
|
||||
mca_note "$(mca_msg "Autoscroll is off. Turn it on first.")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Start from a clean slate. Needed after a settings change, which can take
|
||||
# an application out of scope as well as bring one in and where only a full
|
||||
# undo can tell the difference, and it is what makes the repair a repair:
|
||||
# whatever drifted is written again rather than left because it is "already
|
||||
# applied".
|
||||
#
|
||||
# The watcher never does this. Rewriting files that are already correct
|
||||
# would change their timestamps, and it is watching those directories.
|
||||
if (( rebuild )); then
|
||||
mca_revert
|
||||
fi
|
||||
|
||||
mca_apply
|
||||
|
||||
if (( rebuild )); then
|
||||
mca_ok "$(mca_msg "Applied again to %d applications." "$MCA_N_ON")"
|
||||
[[ -n $MCA_IN_MENU ]] \
|
||||
|| mca_say " $(mca_msg "Applications that are already running pick this up when they are restarted.")"
|
||||
elif (( MCA_CHANGES )); then
|
||||
mca_ok "$(mca_msg "Applied to %d applications." "$MCA_N_ON")"
|
||||
[[ -n $MCA_IN_MENU ]] \
|
||||
|| mca_say " $(mca_msg "Applications that are already running pick this up when they are restarted.")"
|
||||
else
|
||||
mca_ok "$(mca_msg "Already applied to %d applications." "$MCA_N_ON")"
|
||||
fi
|
||||
|
||||
if [[ $CFG_WATCH == yes ]] && mca_watch_available; then
|
||||
if mca_watch_enabled; then
|
||||
mca_watch_refresh
|
||||
else
|
||||
mca_watch_enable || true
|
||||
fi
|
||||
elif [[ $CFG_WATCH != yes ]] && mca_watch_enabled; then
|
||||
mca_watch_disable
|
||||
fi
|
||||
}
|
||||
|
||||
mca_do_status() {
|
||||
mca_config_load
|
||||
mca_head " $MCA_PRETTY"
|
||||
mca_ui_status
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
mca_do_list() {
|
||||
local i route label pad
|
||||
|
||||
mca_config_load
|
||||
mca_scan_once
|
||||
mca_count_routes
|
||||
|
||||
if (( ${#MCA_IDS[@]} == 0 )); then
|
||||
mca_note "$(mca_msg "No supported applications found.")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mca_head " $(mca_msg "Applications")"
|
||||
|
||||
for i in "${!MCA_IDS[@]}"; do
|
||||
route="${MCA_ROUTES[i]}"
|
||||
label="${MCA_NAMES[i]}"
|
||||
pad=$(( 34 - ${#label} ))
|
||||
(( pad < 0 )) && pad=0
|
||||
printf ' %s%*s %s\n' "$label" "$pad" '' "$(mca_route_label "$route")"
|
||||
done
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
mca_do_help() {
|
||||
cat <<- EOF
|
||||
$MCA_PRETTY $MCA_VERSION
|
||||
|
||||
$(mca_msg "Usage: middleclick-autoscroll [command]")
|
||||
|
||||
$(mca_msg "Commands:")
|
||||
enable $(mca_msg "Turn autoscroll on")
|
||||
disable $(mca_msg "Turn it off and put everything back")
|
||||
apply $(mca_msg "Apply to anything installed since the last run")
|
||||
apply --rebuild $(mca_msg "Take everything back and apply it again")
|
||||
status $(mca_msg "Show what is covered")
|
||||
list $(mca_msg "List every application that was found")
|
||||
-h, --help $(mca_msg "Show this help")
|
||||
-V, --version $(mca_msg "Show the version")
|
||||
|
||||
$(mca_msg "Without a command an interactive menu is shown.")
|
||||
EOF
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Dispatch
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
main() {
|
||||
local cmd="${1:-}"
|
||||
[[ $# -gt 0 ]] && shift
|
||||
|
||||
case "$cmd" in
|
||||
enable) mca_do_enable "$@" ;;
|
||||
disable) mca_do_disable "$@" ;;
|
||||
apply|refresh) mca_do_apply "$@" ;;
|
||||
status) mca_do_status "$@" ;;
|
||||
list) mca_do_list "$@" ;;
|
||||
|
||||
-h|--help|help) mca_do_help ;;
|
||||
-V|--version) printf '%s %s\n' "$MCA_NAME" "$MCA_VERSION" ;;
|
||||
|
||||
'') mca_ui_menu ;;
|
||||
*)
|
||||
mca_bad "$(mca_msg "Unknown command: %s" "$cmd")"
|
||||
printf '\n'
|
||||
mca_do_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in new issue
Block a user