Files
middleclick-autoscroll/src/lib/config.sh
T
Felitendo 36498f2acf 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.
2026-08-24 10:23:47 +02:00

164 lines
5.0 KiB
Bash

# 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_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_APPS=no; mca_config_bool PatchApps yes && CFG_APPS=yes
CFG_BROWSERS=no; mca_config_bool PatchBrowsers yes && CFG_BROWSERS=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_STEAM=no; mca_config_bool PatchSteam yes && CFG_STEAM=yes
CFG_SPOTIFY=no; mca_config_bool PatchSpotify yes && CFG_SPOTIFY=yes
CFG_WATCH=no; mca_config_bool WatchNewApps yes && CFG_WATCH=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 '')"
}
# The full argument string that gets injected. Kept in one place so the flag
# file writer and the desktop entry writer cannot drift apart.
mca_flags() {
printf '%s' "$MCA_FLAG"
[[ -n ${CFG_EXTRA_FLAGS:-} ]] && printf ' %s' "$CFG_EXTRA_FLAGS"
printf '\n'
}