feat: work on distributions other than Arch

The README asked for "Arch or an Arch derivative" and the code had two
reasons for it. Neither of them was the mechanism, which is why this is
mostly a matter of not assuming.

The first was the flag file. Arch wraps Electron and Chromium in launchers
that read $XDG_CONFIG_HOME/<name>-flags.conf, and that route is the good
one - it survives upgrades and applies to a launch from a terminal. Debian,
Ubuntu, Fedora and openSUSE keep the equivalent under /etc, where it is the
system's file and not the user's, so there is nothing to write and those
applications have to go through their desktop entry instead. That already
worked, because a launcher is read rather than assumed - but only if the
launcher was recognised as Chromium at all, and it was not:

    APPNAME=chromium
    LIBDIR=/usr/lib/chromium
    exec -a "$APPNAME" "$LIBDIR/$APPNAME" $CHROMIUM_FLAGS "$@"

is the shape every one of those wrappers has, and following it needs the
assignments above resolved and -a understood as renaming the process rather
than naming the program. Both are done now, and the wrappers that still
cannot be followed are caught by CHROMIUM_FLAGS and CHROME_WRAPPER, which
nothing but a Chromium launcher sets. Two applications on the machine this
was written on turn out to have been missed for the same reason: Helium,
whose wrapper is followed to a payload full of markers, and ONLYOFFICE,
which ships libcef.so.

Resolving more wrappers made an old inference dangerous. Any launcher that
could be followed also had "<target>-flags.conf" invented for it, on the
theory that a wrapper builds that name from a variable at runtime. For an
application that simply execs its own binary that file is read by nobody:
the flag would have gone to ~/.config/DesktopEditors-flags.conf and the
desktop entry that would have worked was skipped. The name is now derived
only once the target has shown it reads a flag file at all.

The second reason was snaps. /snap/bin/<name> is a symlink to snapd, so
following it lands on /usr/bin/snap and says nothing; the payload is in the
mounted revision, and that tree takes the same marker check as anything
else. They get a settings switch of their own next to Flatpak, and the
launcher entry as their only way in.

Packaging is now a gate in front of the category rather than a category
beside it. A Chromium installed as a snap or a Flatpak was filed as neither
an application nor a browser, so turning browsers off did not reach it -
which on Ubuntu means the default browser. It is a browser that happens to
be packaged as a snap, and both switches apply.

The rest is the same not-assuming: Steam is found in Debian's
~/.steam/debian-installation and in the snap's private tree, the Flatpak
and snap export directories are scanned even when a session started before
they were installed left them out of XDG_DATA_DIRS, /usr/lib/x86_64-linux-gnu
counts as a shared directory the way /usr/lib does, the watcher covers
snapd's export directory and the NixOS and Guix profiles, LANG is read from
/etc/default/locale as well as /etc/locale.conf, and the systemd user unit
directory is asked of systemd instead of guessed - while still following a
PREFIX that was asked for.
This commit is contained in:
Felitendo committed 2026-08-24 10:23:47 +02:00
1 parent bd84c6645a
commit 36498f2acf
13 files changed
+450 -76

No files matched your search

+13
View File
@@ -17,7 +17,20 @@ DATADIR ?= $(PREFIX)/share
LIBDIR ?= $(DATADIR)/middleclick-autoscroll/lib LIBDIR ?= $(DATADIR)/middleclick-autoscroll/lib
LOCALEDIR ?= $(DATADIR)/locale LOCALEDIR ?= $(DATADIR)/locale
MANDIR ?= $(DATADIR)/man 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 - and
# 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.
# systemd searches $(PREFIX)/lib/systemd/user as well, and a file outside the
# prefix it was asked for is not this build's to place.
ifeq ($(PREFIX),/usr)
USERUNITDIR ?= $(shell pkg-config --variable=systemduserunitdir systemd 2>/dev/null || echo /usr/lib/systemd/user)
else
USERUNITDIR ?= $(PREFIX)/lib/systemd/user USERUNITDIR ?= $(PREFIX)/lib/systemd/user
endif
LINGUAS := de LINGUAS := de
MOFILES := $(patsubst %,po/%.mo,$(LINGUAS)) MOFILES := $(patsubst %,po/%.mo,$(LINGUAS))
+60 -13
View File
@@ -10,10 +10,14 @@ underneath are identified by what they ship rather than by their name, and each
of them is handled. of them is handled.
```bash ```bash
paru -S middleclick-autoscroll paru -S middleclick-autoscroll # Arch and its derivatives
middleclick-autoscroll enable middleclick-autoscroll enable
``` ```
There is no package for anything else yet, so elsewhere it is `make && sudo
make install` from a checkout and then the same one command. See
[Building from source](#building-from-source).
That is the whole setup. Nothing else has to be configured, and no file has to That is the whole setup. Nothing else has to be configured, and no file has to
be edited. be edited.
@@ -31,9 +35,10 @@ back on:
Getting that argument into one application is a five-minute job. Getting it into Getting that argument into one application is a five-minute job. Getting it into
all of them, in a way that survives the next package upgrade, is not: all of them, in a way that survives the next package upgrade, is not:
- Some applications read a flag file, some don't. - Some applications read a flag file, some don't - and which do depends on the
distribution as much as on the application.
- Some ship their own copy of Electron, some use the system one. - Some ship their own copy of Electron, some use the system one.
- Flatpaks see none of the host's configuration. - Flatpaks and snaps see none of the host's configuration.
- An application that starts itself at login uses a different entry than the one - An application that starts itself at login uses a different entry than the one
in the menu, and Discord launched at login used to behave differently from in the menu, and Discord launched at login used to behave differently from
Discord launched by hand. Discord launched by hand.
@@ -83,7 +88,7 @@ on — with the space bar:
``` ```
**[4] Settings** has the categories — Electron and CEF applications, browsers, **[4] Settings** has the categories — Electron and CEF applications, browsers,
Flatpaks, autostart entries, Steam, Spotify, whether to watch for new Flatpaks, snaps, autostart entries, Steam, Spotify, whether to watch for new
applications, and a field for extra Chromium arguments if you want any. applications, and a field for extra Chromium arguments if you want any.
There is a configuration file behind all of this. You are never asked to open There is a configuration file behind all of this. You are never asked to open
@@ -95,8 +100,12 @@ Two routes, picked per application.
| | | | | |
|---|---| |---|---|
| **Flag file** | Arch's Electron and Chromium wrappers read extra arguments from `~/.config/<name>-flags.conf`. This is the good one: it is the supported way to pass arguments, it survives package upgrades untouched, and it applies to a launch from a terminal as much as one from the menu. | | **Flag file** | Where the launcher reads extra arguments from `~/.config/<name>-flags.conf`. This is the good one: it is the supported way to pass arguments, it survives package upgrades untouched, and it applies to a launch from a terminal as much as one from the menu. Arch's Electron and Chromium packages all work this way, and a number of individual vendors' launchers do everywhere else. |
| **Desktop entry** | For applications that ship their own binary with no wrapper, and for Flatpaks, a copy of the entry with the argument appended goes into `~/.local/share/applications`, where it shadows the system one. | | **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 entry with the argument appended goes into `~/.local/share/applications`, where it shadows the system one. |
Which of the two an application ends up on is decided by reading its launcher,
never by knowing which distribution this is. Nothing here has a list of
distributions in it any more than it has a list of applications.
Entries that already live in `~/.local/share/applications` — AppImages, web app Entries that already live in `~/.local/share/applications` — AppImages, web app
shortcuts — are edited in place and the original is kept. So are the entries in shortcuts — are edited in place and the original is kept. So are the entries in
@@ -148,10 +157,15 @@ started once, at the start.
## Applications installed later ## Applications installed later
A systemd user path unit watches every directory a launcher can appear in — A systemd user path unit watches every directory a launcher can appear in —
`/usr/share/applications`, the Flatpak exports, `~/.local/share/applications`, `/usr/share/applications`, the Flatpak exports, snapd's export directory, the
`~/.config/autostart` — plus Steam's helper script. Anything new is handled NixOS and Guix profiles, `~/.local/share/applications`, `~/.config/autostart` —
within a second of being installed, whether it came from pacman, the AUR, plus Steam's helper script. Anything new is handled within a second of being
Flatpak or an AppImage manager. There is no hook to install per package manager. installed, whether it came from pacman, apt, dnf, zypper, the AUR, Flatpak,
snapd or an AppImage manager. There is no hook to install per package manager,
which is the only reason one program can cover all of them.
Without systemd nothing breaks; new applications are picked up the next time
`middleclick-autoscroll apply` runs instead of on their own.
## What it will not guess ## What it will not guess
@@ -191,11 +205,34 @@ Run this before uninstalling the package.
See `man middleclick-autoscroll` for the details. See `man middleclick-autoscroll` for the details.
## Distributions
Any of them. Nothing here is keyed to a distribution name — what differs is
which of the two routes above an application ends up on, and that is read off
its launcher.
On Arch and its derivatives most Electron and Chromium packages ship a wrapper
that reads a flag file, so most applications take that route. On Debian,
Ubuntu, Fedora and openSUSE the equivalent file lives under `/etc` and belongs
to the system rather than to you, so there is no flag file to write and those
applications go through their launcher entry instead. Both work. The flag file
is only the nicer of the two, because it applies to a launch from a terminal as
well.
Snaps are handled the way Flatpaks are: what a snap ships lives in its own
mounted tree, `/snap/bin/<name>` is a shim into snapd and says nothing about
what is behind it, and the launcher entry is the only way in. Each has a switch
of its own in the settings.
Steam is found wherever the installation actually is — `~/.local/share/Steam`
for Valve's own package and Arch's, `~/.steam/debian-installation` for
Debian's, and inside the private tree for the Flatpak and the snap.
## Requirements ## Requirements
Arch or an Arch derivative (CachyOS, EndeavourOS, Manjaro), bash, systemd for Bash 4.2 or newer, GNU coreutils, and systemd for the watcher — that is all,
the watcher. Nothing outside your home directory is ever written to, and running and it is what a desktop Linux install already has. Nothing outside your home
it as root is refused. directory is ever written to, and running it as root is refused.
## Building from source ## Building from source
@@ -204,6 +241,16 @@ make
sudo make install sudo make install
``` ```
`make` needs `msgfmt` (gettext) for the translations and `scdoc` for the man
page; both are optional and skipped with a note when missing. `make install`
puts the systemd user units where systemd itself says they go, and honours the
usual `PREFIX` and `DESTDIR`:
```bash
make PREFIX=/usr/local
sudo make PREFIX=/usr/local install
```
`make check` runs `bash -n` and, if installed, `shellcheck` over every script. `make check` runs `bash -n` and, if installed, `shellcheck` over every script.
## License ## License
+32 -9
View File
@@ -18,7 +18,9 @@ on the command line.
*middleclick-autoscroll* finds every Chromium-based application on the system *middleclick-autoscroll* finds every Chromium-based application on the system
and puts that argument somewhere the application will actually read it, then and puts that argument somewhere the application will actually read it, then
keeps doing so for anything installed later. keeps doing so for anything installed later. 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.
Run without a command it shows an interactive menu. Everything it can be told 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 is reachable from there; the configuration file behind it does not need to be
@@ -66,7 +68,7 @@ is refused.
Two ways, chosen per application. Two ways, chosen per application.
*Flag file* *Flag file*
Arch's Electron and Chromium wrappers read extra arguments from Where the launcher reads extra arguments from
_$XDG_CONFIG_HOME/<name>-flags.conf_. This is the preferred route: it is _$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 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. An applies to a launch from a terminal as much as one from the menu. An
@@ -74,12 +76,22 @@ Two ways, chosen per application.
rather than duplicated - Chromium keeps only the last occurrence of that rather than duplicated - Chromium keeps only the last occurrence of that
option, so a second one would switch the first one off. 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* *Desktop entry*
For applications that ship their own binary with no wrapper, and for For applications that ship their own binary with no wrapper, and for
Flatpaks, a copy of the desktop entry with the argument appended is written everything inside a Flatpak or a snap, a copy of the desktop entry with the
to _~/.local/share/applications_, where it shadows the system one. Entries argument appended is written to _~/.local/share/applications_, where it
that already live there - AppImages, web app shortcuts - are edited in shadows the system one. Entries that already live there - AppImages, web
place, with the original kept. 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 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 *X-MCA-Patched*. The two are never confused: the first is deleted when
@@ -98,6 +110,11 @@ helper, inside Steam's own installation:
~/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh ~/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
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 Steam compares the installed files against its manifest at every start - by
size, not by content - and restores whatever differs, so its launcher entry size, not by content - and restores whatever differs, so its launcher entry
gets *-noverifyfiles*. So does its entry in _~/.config/autostart_, which Steam gets *-noverifyfiles*. So does its entry in _~/.config/autostart_, which Steam
@@ -125,8 +142,8 @@ anyway.
The official client is CEF rather than Electron. Installed through The official client is CEF rather than Electron. Installed through
*spotify-launcher*, it is started by a program that builds its own command line *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 and has a configuration file with a slot for extra arguments; that slot is
where the flag goes. Installed as a plain package, it is an ordinary desktop where the flag goes. Installed as a plain package, a Flatpak or a snap, it is
entry and needs nothing special. an ordinary desktop entry and needs nothing special.
# WHAT CANNOT BE DETECTED # WHAT CANNOT BE DETECTED
@@ -155,9 +172,15 @@ _~/.cache/middleclick-autoscroll/detect_
*NO_COLOR* *NO_COLOR*
Disables colour. 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.
# SEE ALSO # SEE ALSO
*systemctl*(1), *flatpak*(1) *systemctl*(1), *flatpak*(1), *snap*(8)
# AUTHORS # AUTHORS
+3
View File
@@ -106,6 +106,9 @@ msgstr "Chromium-basierte Browser"
msgid "Flatpak applications" msgid "Flatpak applications"
msgstr "Flatpak-Anwendungen" msgstr "Flatpak-Anwendungen"
msgid "Snap applications"
msgstr "Snap-Anwendungen"
msgid "Programs that start themselves at login" msgid "Programs that start themselves at login"
msgstr "Programme, die sich beim Anmelden selbst starten" msgstr "Programme, die sich beim Anmelden selbst starten"
+3
View File
@@ -106,6 +106,9 @@ msgstr ""
msgid "Flatpak applications" msgid "Flatpak applications"
msgstr "" msgstr ""
msgid "Snap applications"
msgstr ""
msgid "Programs that start themselves at login" msgid "Programs that start themselves at login"
msgstr "" msgstr ""
+16 -4
View File
@@ -3,21 +3,33 @@ Description=Watch for newly installed Chromium-based applications
Documentation=man:middleclick-autoscroll(1) Documentation=man:middleclick-autoscroll(1)
[Path] [Path]
# Every way an application can turn up: a package, a Flatpak, something # 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 # installed by hand. Watching where the launchers land means there is no hook
# to write per package manager - pacman, flatpak and an AppImage manager all # to write per package manager - pacman, apt, dnf, zypper, flatpak, snapd and
# end up here. # 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/share/applications
PathModified=/usr/local/share/applications PathModified=/usr/local/share/applications
PathModified=%h/.local/share/applications PathModified=%h/.local/share/applications
PathModified=%h/.config/autostart PathModified=%h/.config/autostart
PathModified=/var/lib/flatpak/exports/share/applications PathModified=/var/lib/flatpak/exports/share/applications
PathModified=%h/.local/share/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
# Steam replaces the script that starts its interface on every client update, # Steam replaces the script that starts its interface on every client update,
# which takes the patch with it. # which takes the patch with it. Where that installation lives depends on how
# Steam was installed.
PathChanged=%h/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh PathChanged=%h/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/.steam/debian-installation/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh PathChanged=%h/.var/app/com.valvesoftware.Steam/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
PathChanged=%h/snap/steam/common/.local/share/Steam/ubuntu12_64/steamwebhelper_sniper_wrap.sh
Unit=middleclick-autoscroll.service Unit=middleclick-autoscroll.service
+14 -12
View File
@@ -10,7 +10,7 @@
MCA_CHANGES=0 MCA_CHANGES=0
MCA_ROUTES=() MCA_ROUTES=()
# mca_route <kind> <id> <program> # mca_route <kind> <id> <program> [packaging]
# What happens to one application, left in MCA_ROUTE: # What happens to one application, left in MCA_ROUTE:
# flags - the launcher reads a flag file; write it there # flags - the launcher reads a flag file; write it there
# desktop - no flag file; shadow or edit the desktop entry # desktop - no flag file; shadow or edit the desktop entry
@@ -24,7 +24,7 @@ MCA_ROUTES=()
MCA_ROUTE='' MCA_ROUTE=''
mca_route() { mca_route() {
local kind="$1" id="$2" prog="$3" local kind="$1" id="$2" prog="$3" packaging="${4:-native}"
if [[ $kind == steam ]]; then if [[ $kind == steam ]]; then
if mca_config_list_has Skip "$id" || [[ $CFG_STEAM != yes ]]; then if mca_config_list_has Skip "$id" || [[ $CFG_STEAM != yes ]]; then
@@ -35,14 +35,14 @@ mca_route() {
return return
fi fi
if ! mca_kind_wanted "$kind" "$id"; then if ! mca_kind_wanted "$kind" "$id" "$packaging"; then
[[ $kind == unknown ]] && MCA_ROUTE=unknown || MCA_ROUTE=off [[ $kind == unknown ]] && MCA_ROUTE=unknown || MCA_ROUTE=off
return return
fi fi
# A Flatpak has its own copy of everything and none of the host's wrappers, # A Flatpak or a snap carries its own copy of everything and sees none of
# so the desktop entry is the only way in. # the host's wrappers, so the desktop entry is the only way in.
if [[ $kind == flatpak ]]; then if [[ $packaging != native ]]; then
MCA_ROUTE=desktop MCA_ROUTE=desktop
return return
fi fi
@@ -59,7 +59,7 @@ mca_route() {
# as it likes to be - it writes only what differs, which is what keeps the # as it likes to be - it writes only what differs, which is what keeps the
# watcher from chasing its own changes. # watcher from chasing its own changes.
mca_apply() { mca_apply() {
local i id file prog kind route steam_done=0 local i id file prog kind packaging route steam_done=0
MCA_CHANGES=0 MCA_CHANGES=0
MCA_ROUTES=() MCA_ROUTES=()
@@ -73,8 +73,9 @@ mca_apply() {
file="${MCA_FILES[i]}" file="${MCA_FILES[i]}"
prog="${MCA_PROGS[i]}" prog="${MCA_PROGS[i]}"
kind="${MCA_KINDS[i]}" kind="${MCA_KINDS[i]}"
packaging="${MCA_PACKAGING[i]}"
mca_route "$kind" "$id" "$prog" mca_route "$kind" "$id" "$prog" "$packaging"
route="$MCA_ROUTE" route="$MCA_ROUTE"
MCA_ROUTES[i]="$route" MCA_ROUTES[i]="$route"
@@ -153,9 +154,9 @@ mca_revert() {
# The watcher # The watcher
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# A systemd user path unit watching every directory a desktop entry can appear # A systemd user path unit watching every directory a desktop entry can appear
# in. That covers a package installed with pacman, a Flatpak, an AppImage # in. That covers a package from whatever the distribution's package manager
# registered by hand and a Steam client update, without a hook per package # is, a Flatpak, a snap, an AppImage registered by hand and a Steam client
# manager. # update, without a hook per package manager.
MCA_UNIT_PATH="middleclick-autoscroll.path" MCA_UNIT_PATH="middleclick-autoscroll.path"
MCA_UNIT_SERVICE="middleclick-autoscroll.service" MCA_UNIT_SERVICE="middleclick-autoscroll.service"
@@ -201,7 +202,8 @@ mca_count_routes() {
MCA_ROUTES=() MCA_ROUTES=()
for i in "${!MCA_IDS[@]}"; do for i in "${!MCA_IDS[@]}"; do
mca_route "${MCA_KINDS[i]}" "${MCA_IDS[i]}" "${MCA_PROGS[i]}" mca_route "${MCA_KINDS[i]}" "${MCA_IDS[i]}" "${MCA_PROGS[i]}" \
"${MCA_PACKAGING[i]}"
route="$MCA_ROUTE" route="$MCA_ROUTE"
MCA_ROUTES[i]="$route" MCA_ROUTES[i]="$route"
case "$route" in case "$route" in
+10 -3
View File
@@ -34,7 +34,7 @@ MCA_CACHEDIR="${MCA_CACHEDIR:-${MCA_XDG_CACHE}/${MCA_NAME}}"
# Where generated desktop entries go. A file here shadows the one with the same # 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 # name in /usr/share/applications, which is how an application gets extra
# command line arguments without touching anything pacman owns. # command line arguments without touching a file the package manager owns.
MCA_APPDIR="${MCA_XDG_DATA}/applications" MCA_APPDIR="${MCA_XDG_DATA}/applications"
# Copies of every file that is edited in place rather than shadowed. # Copies of every file that is edited in place rather than shadowed.
@@ -60,8 +60,15 @@ mca_ui_locale() {
[[ -z $l ]] && l="${LANG:-}" [[ -z $l ]] && l="${LANG:-}"
fi fi
if [[ -z $l && -r /etc/locale.conf ]]; then # systemd writes /etc/locale.conf and most distributions use it; Debian and
l="$(sed -n 's/^LANG=//p' /etc/locale.conf | tr -d '"' | head -n1)" # 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 fi
printf '%s\n' "${l:-C}" printf '%s\n' "${l:-C}"
+1
View File
@@ -139,6 +139,7 @@ mca_config_load() {
CFG_APPS=no; mca_config_bool PatchApps yes && CFG_APPS=yes CFG_APPS=no; mca_config_bool PatchApps yes && CFG_APPS=yes
CFG_BROWSERS=no; mca_config_bool PatchBrowsers yes && CFG_BROWSERS=yes CFG_BROWSERS=no; mca_config_bool PatchBrowsers yes && CFG_BROWSERS=yes
CFG_FLATPAK=no; mca_config_bool PatchFlatpak yes && CFG_FLATPAK=yes CFG_FLATPAK=no; mca_config_bool PatchFlatpak yes && CFG_FLATPAK=yes
CFG_SNAP=no; mca_config_bool PatchSnap yes && CFG_SNAP=yes
CFG_AUTOSTART=no; mca_config_bool PatchAutostart yes && CFG_AUTOSTART=yes CFG_AUTOSTART=no; mca_config_bool PatchAutostart yes && CFG_AUTOSTART=yes
CFG_STEAM=no; mca_config_bool PatchSteam yes && CFG_STEAM=yes CFG_STEAM=no; mca_config_bool PatchSteam yes && CFG_STEAM=yes
CFG_SPOTIFY=no; mca_config_bool PatchSpotify yes && CFG_SPOTIFY=yes CFG_SPOTIFY=no; mca_config_bool PatchSpotify yes && CFG_SPOTIFY=yes
+271 -27
View File
@@ -27,9 +27,23 @@ MCA_MARKERS=(
MCA_SYSTEM_DIRS=( MCA_SYSTEM_DIRS=(
/ /bin /lib /lib32 /lib64 /sbin /usr /usr/bin /usr/lib /usr/lib32 / /bin /lib /lib32 /lib64 /sbin /usr /usr/bin /usr/lib /usr/lib32
/usr/lib64 /usr/libexec /usr/sbin /usr/local /usr/local/bin /usr/lib64 /usr/libexec /usr/sbin /usr/local /usr/local/bin
/usr/local/lib /opt /usr/local/lib /usr/local/libexec /opt
) )
# The same thing for the layouts that put a machine triplet in the path.
# Debian and Ubuntu keep the shared libraries in /usr/lib/x86_64-linux-gnu
# rather than /usr/lib, so that directory is every bit as shared as /usr/lib is
# elsewhere and a marker sitting in it belongs to nobody in particular.
MCA_SYSTEM_DIR_GLOBS=(
'/usr/lib/*-linux-gnu*' '/usr/lib32/*-linux-gnu*'
'/usr/lib64/*-linux-gnu*' '/usr/local/lib/*-linux-gnu*'
)
# Where snapd mounts the installed snaps. /snap is the usual place and the one
# the shims point into; distributions that keep /snap free of a top-level
# directory use the second.
MCA_SNAP_DIRS=(/snap /var/lib/snapd/snap)
# Strings in a launcher script that mean it starts a Chromium or Electron # Strings in a launcher script that mean it starts a Chromium or Electron
# process, for the wrappers whose command line is assembled out of variables and # process, for the wrappers whose command line is assembled out of variables and
# cannot be followed from the outside. # cannot be followed from the outside.
@@ -38,23 +52,51 @@ MCA_SYSTEM_DIRS=(
# "chromium" on its own is not one of them: /usr/bin/xdg-open lists every # "chromium" on its own is not one of them: /usr/bin/xdg-open lists every
# browser it knows how to start, and that is not a browser. # browser it knows how to start, and that is not a browser.
# #
# The flag file convention is an Arch packaging habit and says nothing about the # The flag file convention is one distribution's packaging habit and says
# engine either, so it is not in here. # nothing about the engine either, so it is not in here.
MCA_SCRIPT_HINTS='ELECTRON_|app\.asar|chrome-sandbox|libcef|enable-blink-features|ozone-platform-hint' #
# CHROMIUM_FLAGS and CHROME_WRAPPER earn their place: they are the variables
# the Debian, Fedora and openSUSE Chromium wrappers and Google's own Chrome
# wrapper build their command line out of, and nothing else sets them.
MCA_SCRIPT_HINTS='ELECTRON_|app\.asar|chrome-sandbox|libcef|enable-blink-features|ozone-platform-hint|CHROMIUM_FLAGS|CHROME_WRAPPER|CHROME_VERSION_EXTRA'
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Desktop entries # Desktop entries
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Where Flatpak and snapd put the launchers they export. Both add these to
# XDG_DATA_DIRS themselves, through a file in /etc/profile.d - but only for a
# session that was started after they were installed, and only for a session
# manager that reads it at all. They are appended, after everything XDG names,
# so a directory that is already in the search path keeps its own position and
# the ones that were missing are still scanned.
mca_extra_desktop_dirs() {
printf '%s\n' \
"$MCA_XDG_DATA/flatpak/exports/share/applications" \
/var/lib/flatpak/exports/share/applications \
/var/lib/snapd/desktop/applications
}
# The directories a desktop entry can come from, most specific first - which is # The directories a desktop entry can come from, most specific first - which is
# also XDG lookup order, so the first file found for an id is the one that is # also XDG lookup order, so the first file found for an id is the one that is
# actually used. # actually used.
mca_desktop_dirs() { mca_desktop_dirs() {
local dirs="${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" d local dirs="${XDG_DATA_DIRS:-/usr/local/share:/usr/share}" d
local -A seen=()
while IFS= read -r d; do
[[ -n $d ]] || continue
d="${d%/}"
[[ -n ${seen[$d]+set} ]] && continue
seen[$d]=1
printf '%s\n' "$d"
done < <(
printf '%s\n' "$MCA_APPDIR" printf '%s\n' "$MCA_APPDIR"
while IFS= read -r -d: d; do while IFS= read -r -d: d; do
[[ -n $d ]] && printf '%s/applications\n' "${d%/}" [[ -n $d ]] && printf '%s/applications\n' "${d%/}"
done <<< "${dirs}:" done <<< "${dirs}:"
mca_extra_desktop_dirs
)
} }
# mca_desktop_get <file> <key> # mca_desktop_get <file> <key>
@@ -192,7 +234,7 @@ mca_exec_is_steam_link() {
case "${prog##*/}" in case "${prog##*/}" in
steam|steam-runtime) return 0 ;; steam|steam-runtime) return 0 ;;
esac esac
[[ $prog == flatpak:com.valvesoftware.Steam ]] [[ $prog == flatpak:com.valvesoftware.Steam || $prog == snap:steam ]]
} }
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -265,6 +307,11 @@ _mca_has_markers() {
for s in "${MCA_SYSTEM_DIRS[@]}"; do for s in "${MCA_SYSTEM_DIRS[@]}"; do
[[ $dir == "$s" ]] && return 1 [[ $dir == "$s" ]] && return 1
done done
for s in "${MCA_SYSTEM_DIR_GLOBS[@]}"; do
# Unquoted on purpose - these are patterns, not names.
# shellcheck disable=SC2053
[[ $dir == $s ]] && return 1
done
for m in "${MCA_MARKERS[@]}"; do for m in "${MCA_MARKERS[@]}"; do
[[ -e "$dir/$m" ]] && return 0 [[ -e "$dir/$m" ]] && return 0
@@ -273,23 +320,118 @@ _mca_has_markers() {
return 1 return 1
} }
# The plain assignments the script made before it handed over, for
# _mca_script_subst to read. A variable of its own rather than something passed
# around: every caller of _mca_script_target reads it through a command
# substitution, so each call already works on a copy and there is nothing here
# that two of them could collide over.
declare -A MCA_SCRIPT_VARS=()
# _mca_script_subst <text>
# The text with $NAME and ${NAME} replaced by what the script assigned to them,
# left in MCA_SUBST.
#
# Fails as soon as something turns up that only a running shell could work out
# - a positional parameter, a name the script never set, a default value. That
# is the point: an unresolvable path has to come out as no path at all, never
# as a wrong one.
MCA_SUBST=''
_mca_script_subst() {
local text="$1" out='' rest name
while [[ $text == *'$'* ]]; do
out+="${text%%\$*}"
rest="${text#*\$}"
if [[ $rest == '{'* ]]; then
[[ $rest == *'}'* ]] || return 1
name="${rest%%\}*}"; name="${name#\{}"
rest="${rest#*\}}"
else
name="${rest%%[!A-Za-z0-9_]*}"
rest="${rest:${#name}}"
fi
[[ $name =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || return 1
[[ -n ${MCA_SCRIPT_VARS[$name]+set} ]] || return 1
out+="${MCA_SCRIPT_VARS[$name]}"
text="$rest"
done
MCA_SUBST="$out$text"
return 0
}
# _mca_script_target <script> # _mca_script_target <script>
# The program a wrapper script hands over to, so a chain like # The program a wrapper script hands over to, so a chain like
# heroic -> electron43 -> /usr/lib/electron43/electron can be followed. # heroic -> electron43 -> /usr/lib/electron43/electron can be followed.
#
# The assignments above the exec line are followed as well, because that is the
# shape the Chromium wrappers outside Arch have: Debian, Ubuntu, Fedora and
# openSUSE all set the directory and the program name into variables at the top
# of the script and end on `exec -a "$APPNAME" "$LIBDIR/$APPNAME"`. Without
# resolving those there is nothing to follow, and the answer would have to come
# from the hint scan - a weaker kind of evidence than finding the binary and
# its markers.
_mca_script_target() { _mca_script_target() {
local script="$1" line tok local script="$1" line tok name val skip=0
local -a tokens local -a tokens
local re_assign='^[[:space:]]*(export[[:space:]]+)?([A-Za-z_][A-Za-z0-9_]*)=([^[:space:];|&()`]*)[[:space:]]*$'
MCA_SCRIPT_VARS=()
while IFS= read -r line; do while IFS= read -r line; do
if [[ $line =~ $re_assign ]]; then
name="${BASH_REMATCH[2]}"
val="${BASH_REMATCH[3]}"
# One pair of quotes around the whole value is ordinary and means
# nothing here. Single quotes also mean the value is literal, so
# there is nothing left to expand.
if [[ $val == \'*\' ]]; then
MCA_SCRIPT_VARS[$name]="${val:1:${#val}-2}"
continue
fi
[[ $val == \"*\" ]] && val="${val:1:${#val}-2}"
if _mca_script_subst "$val"; then
MCA_SCRIPT_VARS[$name]="$MCA_SUBST"
else
# Not resolvable, so anything built from it must not be either.
unset "MCA_SCRIPT_VARS[$name]"
fi
continue
fi
[[ $line =~ ^[[:space:]]*exec[[:space:]]+(.*)$ ]] || continue [[ $line =~ ^[[:space:]]*exec[[:space:]]+(.*)$ ]] || continue
read -r -a tokens <<< "${BASH_REMATCH[1]}" read -r -a tokens <<< "${BASH_REMATCH[1]}"
skip=0
for tok in "${tokens[@]}"; do for tok in "${tokens[@]}"; do
[[ $tok == env ]] && continue (( skip )) && { skip=0; continue; }
case "$tok" in
env) continue ;;
# `exec -a NAME PROG` renames the process. NAME is not a
# program, and it is usually the wrapper's own name, so
# following it would lead straight back here.
-a|--argv0) skip=1; continue ;;
-*) continue ;;
esac
[[ $tok == *=* && $tok != /* ]] && continue [[ $tok == *=* && $tok != /* ]] && continue
[[ $tok == -* ]] && continue
# Anything still carrying a shell variable cannot be resolved from tok="${tok%\"}"; tok="${tok#\"}"
# the outside; the hint scan has to answer for those. tok="${tok%\'}"; tok="${tok#\'}"
[[ $tok == *'$'* ]] && return 1
# Anything still carrying a shell variable is resolved from the
# assignments above, or not at all; the hint scan answers for the
# wrappers that build their command line some other way.
if [[ $tok == *'$'* ]]; then
_mca_script_subst "$tok" || return 1
tok="$MCA_SUBST"
fi
[[ -n $tok ]] || return 1
if [[ $tok == /* ]]; then if [[ $tok == /* ]]; then
printf '%s\n' "$tok" printf '%s\n' "$tok"
else else
@@ -302,13 +444,15 @@ _mca_script_target() {
} }
# mca_flags_candidates <program> # mca_flags_candidates <program>
# The flag files a launcher reads, in the order it reads them. Arch's Electron # The flag files a launcher reads, in the order it reads them, taken from the
# and Chromium wrappers all take extra arguments from # launcher itself rather than assumed from the distribution. Where the wrappers
# $XDG_CONFIG_HOME/<name>-flags.conf, which is a far better place to inject than # follow that convention - Arch's Electron and Chromium packages do, and take
# a desktop entry: it survives package updates and it applies to a launch from # extra arguments from $XDG_CONFIG_HOME/<name>-flags.conf - it is a far better
# the terminal too. # place to inject than a desktop entry: it survives package updates and it
# applies to a launch from the terminal too. Where they do not, this finds
# nothing and the caller falls back to the desktop entry.
mca_flags_candidates() { mca_flags_candidates() {
local prog="$1" depth="${2:-0}" target local prog="$1" depth="${2:-0}" target inherited
(( depth > 3 )) && return 0 (( depth > 3 )) && return 0
[[ -r $prog ]] || return 0 [[ -r $prog ]] || return 0
head -c2 -- "$prog" 2>/dev/null | grep -q '#!' || return 0 head -c2 -- "$prog" 2>/dev/null | grep -q '#!' || return 0
@@ -316,10 +460,22 @@ mca_flags_candidates() {
grep -oE '[A-Za-z0-9_.+-]+-flags\.conf' -- "$prog" 2>/dev/null grep -oE '[A-Za-z0-9_.+-]+-flags\.conf' -- "$prog" 2>/dev/null
# A wrapper that only execs another wrapper (heroic -> electron43) inherits # A wrapper that only execs another wrapper (heroic -> electron43) inherits
# that one's flag files, plus the specific name it builds from a variable. # that one's flag files, plus the specific name it builds from a variable
# at runtime and therefore never writes down - which is why that one is
# derived from the target's own name rather than found.
#
# It is derived only once the target has shown that it reads a flag file at
# all. Every launcher that can be followed is not a launcher that reads
# one: the Chromium wrappers outside Arch do not, and neither does an
# ordinary program that simply execs its own binary. Naming a file for
# those would put the flag somewhere nothing ever looks and skip the
# desktop entry that would have worked.
if target="$(_mca_script_target "$prog")" && [[ -n $target ]]; then if target="$(_mca_script_target "$prog")" && [[ -n $target ]]; then
inherited="$(mca_flags_candidates "$target" $(( depth + 1 )))"
if [[ -n $inherited ]]; then
printf '%s-flags.conf\n' "${target##*/}" printf '%s-flags.conf\n' "${target##*/}"
mca_flags_candidates "$target" $(( depth + 1 )) printf '%s\n' "$inherited"
fi
fi fi
} }
@@ -413,8 +569,9 @@ _mca_detect_uncached() {
MCA_IDS=() # desktop file id, without the .desktop suffix MCA_IDS=() # desktop file id, without the .desktop suffix
MCA_FILES=() # the desktop entry that is in effect for that id MCA_FILES=() # the desktop entry that is in effect for that id
MCA_NAMES=() # display name MCA_NAMES=() # display name
MCA_PROGS=() # resolved program, or a flatpak app id MCA_PROGS=() # resolved program, or a Flatpak app id or a snap name
MCA_KINDS=() # app | browser | flatpak | steam | unknown | no MCA_KINDS=() # app | browser | steam | unknown | no
MCA_PACKAGING=() # native | flatpak | snap
# The shortcuts Steam writes for single games. Not applications of their own - a # The shortcuts Steam writes for single games. Not applications of their own - a
# game is whatever engine it was built with, and none of those reads a Chromium # game is whatever engine it was built with, and none of those reads a Chromium
@@ -435,11 +592,12 @@ mca_scan_once() {
} }
mca_scan() { mca_scan() {
local dir file id name exec_line prog kind i local dir file id name exec_line prog kind packaging i
local -A seen=() local -A seen=()
local -a c_ids=() c_files=() c_names=() c_progs=() c_browser=() c_stat=() local -a c_ids=() c_files=() c_names=() c_progs=() c_browser=() c_stat=()
MCA_IDS=(); MCA_FILES=(); MCA_NAMES=(); MCA_PROGS=(); MCA_KINDS=() MCA_IDS=(); MCA_FILES=(); MCA_NAMES=(); MCA_PROGS=(); MCA_KINDS=()
MCA_PACKAGING=()
MCA_STEAM_LINKS=(); MCA_STEAM_LINK_FILES=() MCA_STEAM_LINKS=(); MCA_STEAM_LINK_FILES=()
# Pass one: read the entries and work out what each of them starts. No # Pass one: read the entries and work out what each of them starts. No
@@ -476,6 +634,8 @@ mca_scan() {
if [[ ${prog##*/} == flatpak ]]; then if [[ ${prog##*/} == flatpak ]]; then
mca_exec_flatpak_id "$exec_line" || continue mca_exec_flatpak_id "$exec_line" || continue
prog="flatpak:$MCA_PROG" prog="flatpak:$MCA_PROG"
elif mca_snap_name "$prog"; then
prog="snap:$MCA_PROG"
fi fi
if mca_exec_is_steam_link "$exec_line" "$prog"; then if mca_exec_is_steam_link "$exec_line" "$prog"; then
@@ -494,14 +654,28 @@ mca_scan() {
MCA_STAT=() MCA_STAT=()
_mca_stat_batch "${c_stat[@]}" _mca_stat_batch "${c_stat[@]}"
# Pass two: decide what each one is. # Pass two: decide what each one is. What it does - an application or a
# browser - and how it was packaged are two separate questions: a Chromium
# installed as a snap is still a browser, and somebody who has turned
# browsers off means that one too.
for i in "${!c_ids[@]}"; do for i in "${!c_ids[@]}"; do
prog="${c_progs[i]}" prog="${c_progs[i]}"
kind=no kind=no
packaging=native
if [[ $prog == flatpak:* ]]; then if [[ $prog == flatpak:* ]]; then
prog="${prog#flatpak:}" prog="${prog#flatpak:}"
mca_flatpak_is_chromium "$prog" && kind=flatpak packaging=flatpak
mca_flatpak_is_chromium "$prog" \
&& { (( c_browser[i] )) && kind=browser || kind=app; }
elif [[ $prog == snap:* ]]; then
prog="${prog#snap:}"
packaging=snap
if [[ $prog == steam ]]; then
kind=steam
elif mca_snap_is_chromium "$prog"; then
(( c_browser[i] )) && kind=browser || kind=app
fi
elif [[ ${prog##*/} == steam || ${prog##*/} == steam-runtime ]]; then elif [[ ${prog##*/} == steam || ${prog##*/} == steam-runtime ]]; then
# Steam is Chromium inside, but nothing about it can be changed # Steam is Chromium inside, but nothing about it can be changed
# from a command line argument; it has its own module. # from a command line argument; it has its own module.
@@ -519,6 +693,7 @@ mca_scan() {
MCA_NAMES+=("${c_names[i]}") MCA_NAMES+=("${c_names[i]}")
MCA_PROGS+=("$prog") MCA_PROGS+=("$prog")
MCA_KINDS+=("$kind") MCA_KINDS+=("$kind")
MCA_PACKAGING+=("$packaging")
done done
mca_cache_flush mca_cache_flush
@@ -557,6 +732,66 @@ mca_desktop_is_browser() {
return 1 return 1
} }
# mca_snap_name <program>
# The snap an executable belongs to, left in MCA_PROG - assigned rather than
# printed for the same reason mca_exec_program is: the scan asks this about
# every desktop entry on the system, and a command substitution per entry is a
# fork per entry.
#
# /snap/bin/<name> is the shim snapd puts in PATH and is a symlink to snapd
# itself, so following it lands on /usr/bin/snap and says nothing whatever
# about the application. The name is the only thing that carries information,
# and it is what leads to the mounted tree below.
mca_snap_name() {
local prog="$1" rest d
for d in "${MCA_SNAP_DIRS[@]}"; do
if [[ $prog == "$d/bin/"* ]]; then
rest="${prog#"$d/bin/"}"
# A snap that ships several programs names them <snap>.<app>.
MCA_PROG="${rest%%.*}"
return 0
fi
if [[ $prog == "$d/"* ]]; then
rest="${prog#"$d/"}"
MCA_PROG="${rest%%/*}"
return 0
fi
done
return 1
}
# mca_snap_is_chromium <snap name>
# A snap keeps everything it ships inside its own mounted revision, so the
# marker check works the same way there as anywhere else once that tree has
# been located. "current" is the symlink snapd keeps pointing at the revision
# that will actually be started.
mca_snap_is_chromium() {
local name="$1" d root
if [[ -n ${MCA_DETECT_MEMO[snap:$name]+set} ]]; then
[[ ${MCA_DETECT_MEMO[snap:$name]} == yes ]]
return $?
fi
for d in "${MCA_SNAP_DIRS[@]}"; do
root="$d/$name/current"
[[ -d $root ]] || continue
if [[ -n "$(find "$root" -maxdepth 5 \
\( -name 'chrome_crashpad_handler' -o -name 'app.asar' \
-o -name 'libcef.so' -o -name 'v8_context_snapshot.bin' \
-o -name 'chrome-sandbox' \) \
-print -quit 2>/dev/null)" ]]
then
MCA_DETECT_MEMO[snap:$name]=yes
return 0
fi
done
MCA_DETECT_MEMO[snap:$name]=no
return 1
}
# mca_flatpak_is_chromium <app id> # mca_flatpak_is_chromium <app id>
# Flatpak keeps every application in its own tree, so the marker check works the # Flatpak keeps every application in its own tree, so the marker check works the
# same way once that tree has been located. # same way once that tree has been located.
@@ -584,19 +819,28 @@ mca_flatpak_is_chromium() {
return 1 return 1
} }
# mca_kind_wanted <kind> <id> # mca_kind_wanted <kind> <id> [packaging]
# Whether the current settings say this entry should be patched. Skip beats # Whether the current settings say this entry should be patched. Skip beats
# everything, an explicit include beats detection, and detection beats nothing. # everything, an explicit include beats detection, and detection beats nothing.
#
# Packaging is a gate in front of the category rather than a category of its
# own: a Flatpak or a snap sees none of the host's configuration and is worth
# switching off as a group, but it is still an application or a browser and
# whichever of those the user turned off applies to it too.
mca_kind_wanted() { mca_kind_wanted() {
local kind="$1" id="$2" local kind="$1" id="$2" packaging="${3:-native}"
mca_config_list_has Skip "$id" && return 1 mca_config_list_has Skip "$id" && return 1
mca_config_list_has Include "$id" && return 0 mca_config_list_has Include "$id" && return 0
case "$packaging" in
flatpak) [[ $CFG_FLATPAK == yes ]] || return 1 ;;
snap) [[ $CFG_SNAP == yes ]] || return 1 ;;
esac
case "$kind" in case "$kind" in
app) [[ $CFG_APPS == yes ]] ;; app) [[ $CFG_APPS == yes ]] ;;
browser) [[ $CFG_BROWSERS == yes ]] ;; browser) [[ $CFG_BROWSERS == yes ]] ;;
flatpak) [[ $CFG_FLATPAK == yes && $CFG_APPS == yes ]] ;;
*) return 1 ;; *) return 1 ;;
esac esac
} }
+1
View File
@@ -215,6 +215,7 @@ MCA_SETTINGS=(
"PatchApps|bool|yes|Electron and CEF applications" "PatchApps|bool|yes|Electron and CEF applications"
"PatchBrowsers|bool|yes|Chromium-based browsers" "PatchBrowsers|bool|yes|Chromium-based browsers"
"PatchFlatpak|bool|yes|Flatpak applications" "PatchFlatpak|bool|yes|Flatpak applications"
"PatchSnap|bool|yes|Snap applications"
"PatchAutostart|bool|yes|Programs that start themselves at login" "PatchAutostart|bool|yes|Programs that start themselves at login"
"PatchSteam|bool|yes|Steam" "PatchSteam|bool|yes|Steam"
"PatchSpotify|bool|yes|Spotify" "PatchSpotify|bool|yes|Spotify"
+17 -7
View File
@@ -4,15 +4,25 @@
# #
# There are two ways in, and the order matters: # There are two ways in, and the order matters:
# #
# 1. A flag file. Arch's Electron and Chromium wrappers read extra arguments # 1. A flag file. Where the distribution wraps Electron and Chromium in a
# from $XDG_CONFIG_HOME/<name>-flags.conf. This is the good one - it is the # launcher script that reads extra arguments from
# supported way to pass arguments, it survives package upgrades untouched, # $XDG_CONFIG_HOME/<name>-flags.conf - Arch and its derivatives do, and it
# and it applies however the program is started, including from a terminal. # is the convention their packages follow - this is the good one: 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. Elsewhere - Debian, Ubuntu, Fedora, openSUSE, where
# the equivalent file lives under /etc and is the system's to write -
# there is no flag file to use and route 2 answers for everything.
# #
# 2. A desktop entry. For applications that ship their own binary with no # 2. A desktop entry. For applications that ship their own binary with no
# wrapper - most vendor packages, every Flatpak - there is nowhere else to # wrapper, and for everything a Flatpak or a snap contains, there is
# put an argument, so a copy of the entry with the flag appended goes into # nowhere else to put an argument, so a copy of the entry with the flag
# ~/.local/share/applications, where it shadows the system one. # appended goes into ~/.local/share/applications, where it shadows the
# system one.
# #
# Nothing outside $HOME is ever written to. # Nothing outside $HOME is ever written to.
+9 -1
View File
@@ -30,6 +30,11 @@ MCA_STEAM_LAUNCH_FLAG='-noverifyfiles'
# Every place a Steam installation is known to live, resolved and de-duplicated # Every place a Steam installation is known to live, resolved and de-duplicated
# because ~/.steam/steam is normally a symlink into ~/.local/share. # 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() { mca_steam_roots() {
local candidates=( local candidates=(
"$MCA_XDG_DATA/Steam" "$MCA_XDG_DATA/Steam"
@@ -37,6 +42,8 @@ mca_steam_roots() {
"$HOME/.steam/root" "$HOME/.steam/root"
"$HOME/.steam/debian-installation" "$HOME/.steam/debian-installation"
"$HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam" "$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 dir real
local -A seen=() local -A seen=()
@@ -79,7 +86,8 @@ mca_steam_running() {
local f pid local f pid
for f in \ for f in \
"$HOME/.steam/steam.pid" \ "$HOME/.steam/steam.pid" \
"$HOME/.var/app/com.valvesoftware.Steam/.steam/steam.pid" "$HOME/.var/app/com.valvesoftware.Steam/.steam/steam.pid" \
"$HOME/snap/steam/common/.steam/steam.pid"
do do
[[ -r $f ]] || continue [[ -r $f ]] || continue
pid="$(< "$f")" pid="$(< "$f")"