feat: rework the settings

This commit is contained in:
Felitendo committed 2026-09-24 10:44:52 +02:00
1 parent 677660c1c6
commit 82afefe5f8
8 files changed
+151 -116

No files matched your search

+28 -11
View File
@@ -15,6 +15,7 @@ MCA_ROUTES=()
# 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
# unknown - cannot tell what engine this is (an AppImage), so nothing is done
# off - detected, but switched off
#
@@ -26,16 +27,16 @@ MCA_ROUTE=''
mca_route() {
local kind="$1" id="$2" prog="$3" packaging="${4:-native}"
if [[ $kind == steam ]]; then
if mca_config_list_has Skip "$id" || [[ $CFG_STEAM != yes ]]; then
if [[ $kind == steam || $kind == spotify ]]; then
if mca_config_list_has Skip "$id"; then
MCA_ROUTE=off
else
MCA_ROUTE=steam
MCA_ROUTE="$kind"
fi
return
fi
if ! mca_kind_wanted "$kind" "$id" "$packaging"; then
if ! mca_kind_wanted "$kind" "$id"; then
[[ $kind == unknown ]] && MCA_ROUTE=unknown || MCA_ROUTE=off
return
fi
@@ -59,7 +60,7 @@ mca_route() {
# 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
local i id file prog kind packaging route steam_done=0 spotify_done=0
MCA_CHANGES=0
MCA_ROUTES=()
@@ -111,29 +112,29 @@ mca_apply() {
steam_done=1
fi
;;
spotify)
(( spotify_done )) || mca_spotify_apply
spotify_done=1
;;
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 [[ $CFG_STEAM == yes ]] && (( ! steam_done )) && mca_steam_installed; then
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.
if [[ $CFG_AUTOSTART == yes ]]; then
mca_autostart_apply
fi
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
[[ $CFG_SPOTIFY == yes ]] && mca_spotify_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.
@@ -272,12 +273,28 @@ mca_count_routes() {
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' ;;
unknown) mca_msg "cannot tell" ;;
*) mca_msg "off" ;;
esac
+48 -7
View File
@@ -101,6 +101,20 @@ mca_config_set() {
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() {
@@ -136,13 +150,6 @@ mca_config_list_del() {
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_PASTE=no; mca_config_bool DisablePaste yes && CFG_PASTE=yes
@@ -155,6 +162,40 @@ mca_config_load() {
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.
+11 -20
View File
@@ -989,7 +989,7 @@ MCA_IDS=() # desktop file id, without the .desktop suffix
MCA_FILES=() # the desktop entry that is in effect for that id
MCA_NAMES=() # display name
MCA_PROGS=() # resolved program, or a Flatpak app id or a snap name
MCA_KINDS=() # app | browser | steam | unknown | no
MCA_KINDS=() # app | browser | steam | spotify | unknown | no
MCA_PACKAGING=() # native | flatpak | snap
# A scan reads every desktop entry on the system, so the menu does it once and
@@ -1094,6 +1094,10 @@ mca_scan() {
# Steam is Chromium inside, but nothing about it can be changed
# from a command line argument; it has its own module.
kind=steam
elif [[ ${prog##*/} == spotify-launcher ]]; then
# Not Chromium itself, but it starts Spotify, which is CEF. It
# takes the flag from its own configuration file.
kind=spotify
else
mca_detect_verdict "$prog"
case "$MCA_VERDICT" in
@@ -1114,6 +1118,7 @@ mca_scan() {
mca_cache_flush
MCA_SCANNED=1
mca_config_migrate
}
# mca_has_flags_file <program>
@@ -1224,28 +1229,14 @@ mca_flatpak_is_chromium() {
return 1
}
# mca_kind_wanted <kind> <id> [packaging]
# Whether the current settings say this entry should be patched. Skip beats
# 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 <kind> <id>
# Whether this entry should be patched. Skip beats everything, an explicit
# include beats detection, and detection beats nothing.
mca_kind_wanted() {
local kind="$1" id="$2" packaging="${3:-native}"
local kind="$1" id="$2"
mca_config_list_has Skip "$id" && return 1
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
app) [[ $CFG_APPS == yes ]] ;;
browser) [[ $CFG_BROWSERS == yes ]] ;;
*) return 1 ;;
esac
[[ $kind == app || $kind == browser ]]
}
+24 -20
View File
@@ -178,7 +178,7 @@ mca_ui_status() {
if mca_steam_installed; then
if mca_steam_patched; then
_mca_row "$(mca_msg "Steam")" "$(_mca_onoff yes)"
elif [[ $CFG_STEAM == yes && $CFG_ENABLED == yes ]]; then
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
@@ -218,19 +218,16 @@ mca_ui_status() {
# ---------------------------------------------------------------------------
# Settings
# ---------------------------------------------------------------------------
# Format: Key|type|default|label-msgid
# type is bool or text.
# 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=(
"PatchApps|bool|yes|Electron and CEF applications"
"PatchBrowsers|bool|yes|Chromium-based browsers"
"PatchFlatpak|bool|yes|Flatpak applications"
"PatchSnap|bool|yes|Snap applications"
"PatchAutostart|bool|yes|Programs that start themselves at login"
"PatchSteam|bool|yes|Steam"
"PatchSpotify|bool|yes|Spotify"
"DisablePaste|bool|yes|Turn off KDE's middle-click paste"
"WatchNewApps|bool|yes|Apply to newly installed applications"
"ExtraFlags|text||Additional Chromium arguments"
"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() {
@@ -275,10 +272,9 @@ _mca_setting_display() {
#
# Returns 0 when something was changed, so the caller knows to re-apply.
mca_ui_settings() {
local count=${#MCA_SETTINGS[@]}
local -a names=() types=() defaults=() labels=() values=()
local spec name type default label locale i key frame row pad dirty=1 cursor=0
local touched=0
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"
@@ -286,15 +282,20 @@ mca_ui_settings() {
mca_msg_into "$locale" "(none)"; MCA_LBL_NONE="$MCA_MSG_RESULT"
for spec in "${MCA_SETTINGS[@]}"; do
IFS='|' read -r name type default label <<< "$spec"
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 or Right: change, q: back"
mca_msg_into "$locale" "Up/Down: select, Space: change, q: back"
hint="$MCA_MSG_RESULT"
local clearseq
@@ -314,7 +315,7 @@ mca_ui_settings() {
local marker selected="${MCA_C_BLUE}▸${MCA_C_RESET} "
for i in "${!names[@]}"; do
_mca_setting_display "${types[i]}" "${values[i]}"
pad=$(( 46 - ${#labels[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' \
@@ -322,6 +323,7 @@ mca_ui_settings() {
frame+="$row"$'\n'
done
frame+=$'\n'" ${helps[cursor]}"$'\n'
frame+=$'\n'" ${MCA_C_DIM}${hint}${MCA_C_RESET}"$'\n'
printf '%s' "$frame"
@@ -419,6 +421,7 @@ mca_ui_apps() {
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 clearseq
clearseq="$(clear 2>/dev/null)" || clearseq=$'\033[H\033[2J'
@@ -467,6 +470,7 @@ mca_ui_apps() {
unknown) shown="$s_cannot" ;;
steam) shown="$s_steam" ;;
flags) shown="$s_flags" ;;
spotify) shown="$s_spotify" ;;
*) shown="$s_desktop" ;;
esac
pad=$(( 34 - ${#labels[idx]} ))
+8 -16
View File
@@ -418,16 +418,12 @@ mca_desktop_apply() {
# Neither can be shadowed from anywhere, so both are edited where they stand,
# with the original kept.
# _mca_entry_patch_inplace <file> <gate: autostart|apps>
# _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.
#
# What decides whether an application here is in scope differs by where the
# entry came from, which is what the gate says: an autostart entry follows the
# autostart setting, a shortcut follows the same rules as the application it is
# a shortcut to.
# 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" gate="$2"
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
@@ -476,12 +472,8 @@ _mca_entry_patch_inplace() {
mca_desktop_is_browser && kind=browser || kind=app
if [[ $gate == autostart ]]; then
[[ $CFG_AUTOSTART == yes ]] || return 0
else
id="${file##*/}"; id="${id%.desktop}"
mca_kind_wanted "$kind" "$id" "$packaging" || return 0
fi
id="${file##*/}"; id="${id%.desktop}"
mca_kind_wanted "$kind" "$id" || return 0
content="$(_mca_desktop_transform "$file" "$MCA_MARK_INPLACE" \
"$(mca_flags "$kind")")"
@@ -503,7 +495,7 @@ mca_autostart_apply() {
for file in "$dir"/*.desktop; do
[[ -f $file ]] || continue
_mca_entry_patch_inplace "$file" autostart
_mca_entry_patch_inplace "$file"
done
}
@@ -515,7 +507,7 @@ mca_shortcuts_apply() {
for file in "$dir"/*.desktop; do
[[ -f $file ]] || continue
_mca_entry_patch_inplace "$file" apps
_mca_entry_patch_inplace "$file"
done
}