Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
elementaryOS 6 has great dark themes, but they only apply to "curated" appcenter apps. This script forces the system theme to work with regular Debian packages as well as non-curated Flatpak apps. It also optionally changes the wallpaper based on the theme. It also supports changing themes for the Mailspring email app (both .deb and Snap versions).
#!/usr/bin/bash
# =======================================
# eOS-Universal-Dark-Style
# =======================================
# elementaryOS 6 has great dark themes, but they only apply to "curated" appcenter apps
# This script forces the system theme to work with regular Debian packages as well as non-curated Flatpak apps
# It also optionally changes the wallpaper based on the theme
# It also supports changing themes for the Mailspring email app (both .deb and Snap versions)
# =======================================
# Instructions
# =======================================
# 1. Save this script and make it executable
# 2. Change the configuration variables below as needed
# 3. Use a cron job to run the script automatically on a desired schedule; you can learn about cron by running `man crontab` in a terminal
# Note that system updates can reset the forced Flatpak theming, so running the script frequently (every few minutes) is recommended
# =======================================
# Configuration
# =======================================
LIGHT_THEME_WALLPAPER='/usr/share/backgrounds/odin.jpg' # Leave empty to disable wallpaper switching on light theme
DARK_THEME_WALLPAPER='/usr/share/backgrounds/odin-dark.jpg' # Leave empty to disable wallpaper switching on dark theme
MAILSPRING_LIGHT_THEME='ui-light' # Leave empty to disable Mailspring theme switching on light theme
MAILSPRING_DARK_THEME='ui-dark' # Leave empty to disable Mailspring theme switching on dark theme
SHOW_DEBUG_MESSAGES=0 # If debug logs should be printed, set to 1
# =======================================
# Script functions
# =======================================
# Echoes provided string with a date prefix. If $2 is 1, redirects to stderr with ERROR prefix. If $3 is 1, echoes a crash message to stderr and exits with code 1
# If $4 is 1, the message is treated as a debug log (only shown if the relevant configuration variable above is set)
log() {
if [ "$2" == 1 ]; then
echo -e "ERROR: $(date): $1" >&2
elif [ "$4" == 1 ]; then
if [ "$SHOW_DEBUG_MESSAGES" == 1 ]; then
echo -e "DEBUG: $(date): $1"
fi
else
echo -e "MESSG: $(date): $1"
fi
if [ "$3" == 1 ]; then
echo -e "CRASHED." >&2
exit 1
fi
}
# Echoes 1 if the system dark style is set, 0 otherwise
getSystemDarkStylePref() {
[ "$(gsettings get org.freedesktop prefers-color-scheme)" = "'dark'" ] && echo 1 || echo 0
}
# Changes the universal GTK dark theme preference based on the provided argument (if they are different)
changeGTKUniversalDarkPrefIfNeeded() {
# Ensure the provided argument is valid before making the change
[ "$1" == 0 ] || [ "$1" == 1 ] || log "GTK universal dark preference can only be set to 0 or 1" 1 1
# The location of the universal GTK dark preference config file
GTK_UNIVERSAL_PREF_FILE=~/.config/gtk-3.0/settings.ini
# Ensure the file exists (if not, create it)
if [ ! -f "$GTK_UNIVERSAL_PREF_FILE" ]; then
echo -e '[Settings]\ngtk-application-prefer-dark-theme=1\n' > "$GTK_UNIVERSAL_PREF_FILE"
fi
# Grab the pref
GTK_UNIVERSAL_PREF_IS_DARK="$(cat "$GTK_UNIVERSAL_PREF_FILE" | grep -o [0-1])"
# Ensure it is 0 or 1; if not, log the error and exit
[ "$GTK_UNIVERSAL_PREF_IS_DARK" == 0 ] || [ "$GTK_UNIVERSAL_PREF_IS_DARK" == 1 ] || log "$GTK_UNIVERSAL_PREF_FILE is not valid" 1 1
# If needed, make the change
if [ "$GTK_UNIVERSAL_PREF_IS_DARK" != "$1" ]; then
sed -i "s/gtk-application-prefer-dark-theme=.*/gtk-application-prefer-dark-theme=$1/" "$GTK_UNIVERSAL_PREF_FILE"
log "GTK universal dark preference has been set to $1"
else
log "GTK universal dark preference is already $1, no changes made" 0 0 1
fi
}
# Changes the wallpaper to the one given in the argument (if not already set)
# If the argument is empty, does nothing (does NOT throw an error)
changeWallpaperIfNeeded() {
if [ ! -z "$1" ]; then
CURRENT_WALLPAPER="$(gsettings get org.gnome.desktop.background picture-uri | head -c -2 | tail -c +9)"
if [ "$CURRENT_WALLPAPER" != "$1" ]; then
# This if statement allows the wallpaper change to work even when running from a cron job
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
PID=$(pgrep gnome-session)
export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/$PID/environ | tr '\0' '\n' | cut -d= -f2-)
fi
gsettings set org.gnome.desktop.background picture-uri file://"$1"
log "Wallpaper has been set to $1"
else
log "Wallpaper is already $1, no changes made" 0 0 1
fi
else
log "Wallpaper argument is empty, no changes made" 0 0 1
fi
}
# Changes the Mailspring theme to the provided string if a Mailspring config can be found (indicating it is installed)
# If not installed or if no theme provided, does nothing (does NOT throw an error)
# Warning: This abruptly kills the Mailspring process and restarts it in the background
changeMailSpringThemeIfNeeded() {
if [ ! -z "$1" ]; then
MAILSPRING_CONFIG_FILE=~/.config/Mailspring/config.json
if [ ! -f "$MAILSPRING_CONFIG_FILE" ]; then MAILSPRING_CONFIG_FILE=~/snap/mailspring/common/config.json; fi
if [ -f "$MAILSPRING_CONFIG_FILE" ] && [ ! -z "$1" ]; then
CURRENT_MAILSPRING_THEME="$(cat "$MAILSPRING_CONFIG_FILE" | grep \"theme\": | tr -d ' ' | tail -c +10 | head -c -3)"
if [ "$CURRENT_MAILSPRING_THEME" != "$1" ]; then
sed -i "s/\"theme\":.*/\"theme\": \"$1\",/g" "$MAILSPRING_CONFIG_FILE"
log "Mailspring theme has been set to $1"
pkill -f mailspring > /dev/null 2>&1
log "Mailspring process has been killed"
mailspring -b </dev/null &>/dev/null &
log "Mailspring process has been started in background"
else
log "Mailspring theme is already $1, no changes made" 0 0 1
fi
else
log "Mailspring not installed, no changes made" 0 0 1
fi
else
log "Mailspring theme argument is empty, no changes made" 0 0 1
fi
}
# First, installs the current system theme into the Flatpak runtimes used by all non-curated (non-appcenter) apps (if not already installed)
# If the current theme is an elementary theme (io.elementary.stylesheet.*) and if the $1 argument is 1, it makes these changes before installing the theme:
# - Renames it by adding a -dark suffix
# - Modifies it so the default stylesheet is dark themed; this is needed as elementary themes do not have traditional separate dark variants
# Then, forces all the apps to use the current theme by adding an environment variable override (if not already set)
changeNonElementaryFlatpakThemesIfNeeded() {
# The location of Flatpak runtimes
FLATPAK_RUNTIME_DIR=~/.local/share/flatpak/runtime
# Where themes are stored in a Flatpak runtime
RUNTIME_THEME_SUBDIR='active/files/share/themes'
# Grab the current system theme
THEME="$(gsettings get org.gnome.desktop.interface gtk-theme | head -c -2 | tail -c +2)"
# Set the variable that indicates whether to add the '-dark' prefix to the theme based on the $1 argument and whether the current theme is an elementary one
USE_CUSTOM_ELEMENTARY_DARK="$([ "$1" == 1 ] && [ ! -z "$(echo "$THEME" | grep -e "^io\.elementary\.stylesheet\..*")" ] && echo 1 || echo 0)"
# Grab the list of app IDs for all non-appcenter installed Flatpaks
TARGET_APP_IDS=($(flatpak list --columns=application,origin,runtime | awk -F "\t" '{if ($2 != "appcenter" && $3 != "") print $1}'))
# For each target app ID...
for APP_ID in "${TARGET_APP_IDS[@]}"; do
# Grab the appropriate Flatpak runtime
RUNTIME="$(flatpak info --show-runtime "$APP_ID")"
# Ensure the runtime has a theme directory
RUNTIME_THEME_DIR="$FLATPAK_RUNTIME_DIR"/"$RUNTIME"/"$RUNTIME_THEME_SUBDIR"
if [ ! -d "$RUNTIME_THEME_DIR" ]; then mkdir -p "$RUNTIME_THEME_DIR"; fi
# Check to see if the current theme (or the current theme plus the '-dark' suffix if needed) is already installed in the runtime
THEME_IN_RUNTIME_SEARCH_RESULT="$([ "$USE_CUSTOM_ELEMENTARY_DARK" -eq 1 ] && echo "$(ls "$RUNTIME_THEME_DIR" | grep "$THEME"-dark)" || echo "$(ls "$RUNTIME_THEME_DIR" | grep "$THEME")")"
# If not, find where the theme is stored (in the system or user directory) and copy it into the runtime theme folder
# Add '-dark' suffix to it if needed and make the necessary changes
if [ -z "$THEME_IN_RUNTIME_SEARCH_RESULT" ]; then
# Find where the theme is stored
TARGET_THEME_LOCATION=''
POSSIBLE_THEME_LOCATIONS=(/usr/share/themes ~/.local/share/themes)
for LOCATION in "${POSSIBLE_THEME_LOCATIONS[@]}"; do
if [ ! -z "$(ls "$LOCATION" | grep -e ^"$THEME"$)" ]; then TARGET_THEME_LOCATION="$LOCATION"/"$THEME"; fi
done
[ ! -z "$TARGET_THEME_LOCATION" ] || log "Location of the currently set theme - $THEME - could not be found" 1 1
# If there is not need to make the '-dark' modifications, copy the theme into the Flatpak runtime; else, modify it in a temporary directory first, then copy it
if [ ! "$USE_CUSTOM_ELEMENTARY_DARK" -eq 1 ]; then
cp -r "$TARGET_THEME_LOCATION" "$RUNTIME_THEME_DIR"
log "$THEME theme has been installed into $RUNTIME Flatpak runtime"
else
TEMP_DIR="$(mktemp -d)"
cp -r "$TARGET_THEME_LOCATION" "$TEMP_DIR"
cp -r "$TEMP_DIR"/"$THEME" "$TEMP_DIR"/"$THEME"-dark
mv "$TEMP_DIR"/"$THEME"-dark/plank "$TEMP_DIR"/"$THEME"-dark/plank-light
mv "$TEMP_DIR"/"$THEME"-dark/plank-dark "$TEMP_DIR"/"$THEME"-dark/plank
mv "$TEMP_DIR"/"$THEME"-dark/gtk-3.0/gtk.css "$TEMP_DIR"/"$THEME"-dark/gtk-3.0/gtk-light.css
mv "$TEMP_DIR"/"$THEME"-dark/gtk-3.0/gtk-dark.css "$TEMP_DIR"/"$THEME"-dark/gtk-3.0/gtk.css
cp -r "$TEMP_DIR"/"$THEME"-dark "$RUNTIME_THEME_DIR"
rm -r "$TEMP_DIR"
log "$THEME theme has been modified to $THEME-dark and installed into $RUNTIME Flatpak runtime"
fi
else
log "$THEME theme is already installed in the $RUNTIME Flatpak runtime, no changes made" 0 0 1
fi
# Now that the theme is installed, do the override if needed
CURRENT_APP_THEME="$(flatpak override --user --show "$APP_ID" | grep GTK_THEME= | tail -c +11)"
FINAL_OVERRIDE_THEME_NAME="$([ "$USE_CUSTOM_ELEMENTARY_DARK" == 1 ] && echo "$(echo "$THEME" | head -c -1 | tail -c +1)-dark" || echo "$THEME")"
if [ "$CURRENT_APP_THEME" != "$FINAL_OVERRIDE_THEME_NAME" ]; then
flatpak override --user --env=GTK_THEME="$FINAL_OVERRIDE_THEME_NAME" "$APP_ID"
log "Flatpak environment variable override 'GTK_THEME=$FINAL_OVERRIDE_THEME_NAME' has been added for $APP_ID"
else
log "Flatpak environment variable override 'GTK_THEME=$FINAL_OVERRIDE_THEME_NAME' is already set for $APP_ID, no changes made" 0 0 1
fi
done
}
# =======================================
# Actual execution
# =======================================
# 1. Change the universal GTK dark theme preference if needed
# 2. Change the wallpaper if needed
# 3. Change the Mailspring theme if needed
# 4. Change the theme of all non-elementary Flatpak apps if needed
SYSTEM_DARK_PREF="$(getSystemDarkStylePref)"
changeGTKUniversalDarkPrefIfNeeded "$SYSTEM_DARK_PREF"
changeWallpaperIfNeeded "$([ "$SYSTEM_DARK_PREF" == 1 ] && echo "$DARK_THEME_WALLPAPER" || echo "$LIGHT_THEME_WALLPAPER")"
changeMailSpringThemeIfNeeded "$([ "$SYSTEM_DARK_PREF" == 1 ] && echo "$MAILSPRING_DARK_THEME" || echo "$MAILSPRING_LIGHT_THEME")"
changeNonElementaryFlatpakThemesIfNeeded "$SYSTEM_DARK_PREF"
@Destroyer616
Copy link

Destroyer616 commented Aug 18, 2021

I am not able to apply it
Screenshot from 2021-08-18 10 25 58
This is what i am getting.
can u tell me how to fix this.
I very much appreciate u work. Have a good day.

@ImranR98
Copy link
Author

ImranR98 commented Aug 18, 2021

can u tell me how to fix this.

Should be fixed now.

@akhayev60
Copy link

akhayev60 commented Aug 18, 2021

im getting the same error too

@ImranR98
Copy link
Author

ImranR98 commented Aug 21, 2021

im getting the same error too

Should be fixed now. It should now create the settings.ini file if needed.

@quienn
Copy link

quienn commented Aug 28, 2021

It looks like it stopped detecting theme changes. :(

Also, sometimes it will show me this error:

ERROR: vie 27 ago 2021 20:14:07 CDT: /home/mrtn/.config/gtk-3.0/settings.ini is not valid
CRASHED.

What about making this script more cron-friendly? Better than doing the checks in an infinite while loop.

Edit: About the error, it looks like sometimes it will randomly append a 0 to the dark preference theme, so that's when it gets triggered.

@ImranR98
Copy link
Author

ImranR98 commented Aug 28, 2021

What about making this script more cron-friendly? Better than doing the checks in an infinite while loop.

Edit: About the error, it looks like sometimes it will randomly append a 0 to the dark preference theme, so that's when it gets triggered.

Yep, turns out extra newlines in the settings.ini file were causing problems. Fixed now, thanks for pointing that out.
Also you're right about the while loop; no point making a custom timing function when Linux already has a widely used solution. Have made the changes.

@klascano
Copy link

klascano commented Nov 23, 2021

still not working, test on eos 6.

@ImranR98
Copy link
Author

ImranR98 commented Nov 23, 2021

Moving forward, elementaryOS's dark theme preference will be natively supported by GTK apps. Not sure what the status of that is since I'm not using eOS anymore, but you could look into that. Was working fine for me on eOS 6 a few months ago, so I'm not sure what changed. Probably won't make any more changes to this script.

@klascano
Copy link

klascano commented Nov 23, 2021

Moving forward, elementaryOS's dark theme preference will be natively supported by GTK apps. Not sure what the status of that is since I'm not using eOS anymore, but you could look into that. Was working fine for me on eOS 6 a few months ago, so I'm not sure what changed. Probably won't make any more changes to this script.

thanks for your repply. Eos6 still not support dark mode in flatpak apps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment