Last active
October 21, 2021 09:48
-
-
Save rawiriblundell/7e0a302b0ebfe121fedeedb22f521d87 to your computer and use it in GitHub Desktop.
Script to switch between light and dark themes for Cinammon running on Mint
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# lightswitch - Switch themes between light and dark mode | |
# This is intended for Linux hosts using the Cinnamon desktop e.g. Mint. | |
export prog_name="${0##*/}" | |
export app_filename="/usr/share/applications/${prog_name}.desktop" | |
# Set our times to switch between light and dark themes (24hr time) | |
lights_on="${lights_on:-0900}" | |
lights_off="${lights_off:-2000}" | |
current_time=$(date +%H%M) | |
# Force these variables into decimal mode to prevent octal interpretation | |
lights_on=$(( 10#${lights_on} )) | |
lights_off=$(( 10#${lights_off} )) | |
current_time=$(( 10#${current_time} )) | |
# We set these here for consumption by set_theme_light|dark() and flip_theme() | |
dark_wmborders="${dark_wmborders:-CBlack}" | |
dark_icons="${dark_icons:-Papirus-Dark}" | |
dark_controls="${dark_controls:-CBlack}" | |
dark_mouse="${dark_mouse:-DMZ-White}" | |
dark_desktop="${dark_desktop:-CBlack}" | |
light_wmborders="${light_wmborders:-Cinnamox-Heather}" | |
light_icons="${light_icons:-Papirus-Light}" | |
light_controls="${light_controls:-ChromeOS}" | |
light_mouse="${light_mouse:-DMZ-White}" | |
light_desktop="${light_desktop:-Chinvat}" | |
# Define a full set of var validation functions | |
var_is_set() { [ "${1+x}" = "x" ] && [ "${#1}" -gt "0" ]; } # set and not null | |
var_is_unset() { [ -z "${1+x}" ]; } # unset | |
var_is_empty() { [ "${1+x}" = "x" ] && [ "${#1}" -eq "0" ]; } # set and null | |
var_is_blank() { var_is_unset "${1}" || var_is_empty "${1}"; } # unset, or set and null | |
create_desktop_shortcuts() { | |
cat << EOF > "${HOME}/Desktop/Light Mode.desktop" | |
[Desktop Entry] | |
Name=Light Mode | |
Exec=lightswitch -t light | |
Comment= | |
Terminal=false | |
Icon=sunny | |
Type=Application | |
EOF | |
cat << EOF > "${HOME}/Desktop/Dark Mode.desktop" | |
[Desktop Entry] | |
Name=Dark Mode | |
Exec=lightswitch -t dark | |
Comment= | |
Terminal=false | |
Icon=night-light-symbolic | |
Type=Application | |
EOF | |
chmod 755 "${HOME}/Desktop/Light Mode.desktop" | |
chmod 755 "${HOME}/Desktop/Dark Mode.desktop" | |
} | |
create_panel_shortcut() { | |
if [[ ! -f "${app_filename}" ]]; then | |
cat << EOF > "${app_filename}" | |
[Desktop Entry] | |
Name=${prog_name} | |
Comment=Switch between light and dark themes | |
GenericName=Theme changer | |
Exec=lightswitch -f | |
Terminal=false | |
Icon=sunny | |
Type=Application | |
EOF | |
fi | |
chown 0:"$(whoami)" "${app_filename}" | |
chmod 664 "${app_filename}" | |
} | |
add_panel_shortcut() { | |
( | |
cd "${HOME}/.cinnamon/configs/[email protected]" || return 1 | |
# Google and my own system tells me this should be 3.json, but just in case | |
# TO-DO: Build in some smarts for ensuring the correct file is edited | |
local json_file="3.json" | |
# Ensure we don't already have an entry | |
if grep -q "${prog_name}.desktop" "${json_file}"; then | |
return 0 | |
fi | |
# Back up the json file | |
cp "${json_file}" "${json_file}.bak.$(date +%Y%m%d)" | |
if command -v jq >/dev/null 2>&1; then | |
jq \ | |
--arg prog_name "${prog_name}.desktop" \ | |
-e \ | |
'."pinned-apps".value=[$prog_name]+."pinned-apps".value' \ | |
"${json_file}" > "${json_file}".tmp | |
mv "${json_file}".tmp "${json_file}" | |
# This 'sed' invocation will match the shortcut to nemo and insert it to the left | |
# TO-DO: make this match only the second instance of 'nemo.desktop' | |
else | |
sed -i "/\"nemo.desktop\",/i \ \"${prog_name}.desktop\",/" "${json_file}" | |
fi | |
printf -- '%s\n' \ | |
"${prog_name}: Added shortcut to panel. To restore, run the following:" \ | |
"mv ${PWD}/${json_file}.bak.$(date +%Y%m%d) ${PWD}/${json_file}" | |
) | |
} | |
get_cinnamon_spice() { | |
if ! command -v git >/dev/null 2>&1; then | |
printf -- '%s\n' "${prog_name} ERROR: 'git' is required to perform this" | |
exit 1 | |
fi | |
[[ ! -d "${HOME}"/.cinnamon_spices_themes ]] && mkdir -p "${HOME}"/.cinnamon_spices_themes | |
if [[ ! -f "${HOME}"/.cinnamon_spices_themes/README.md ]]; then | |
git clone https://github.com/linuxmint/cinnamon-spices-themes.git "${HOME}"/.cinnamon_spices_themes/ | |
fi | |
( | |
cd "${HOME}"/.cinnamon_spices_themes || return 1 | |
git pull | |
for theme in *; do | |
case "${theme}" in | |
(README.md|.git|.github) : ;; | |
(*) | |
cp -R "${theme}/files/${theme}/" "${HOME}/.themes/" | |
;; | |
esac | |
done | |
) | |
} | |
get_desktop() { gsettings get org.cinnamon.theme name; } | |
get_icons() { gsettings get org.cinnamon.desktop.interface icon-theme; } | |
get_mouse() { gsettings get org.cinnamon.desktop.interface cursor-theme; } | |
get_controls() { gsettings get org.cinnamon.desktop.interface gtk-theme; } | |
get_wallpaper() { gsettings get org.cinnamon.desktop.background picture-uri; } | |
get_wmborders() { gsettings get org.cinnamon.desktop.wm.preferences theme; } | |
list_themes() { | |
printf -- '>>>> %s\n' "Current settings:" | |
printf -- '%s\n' \ | |
"Window Borders: $(get_wmborders)" \ | |
"Icons: $(get_icons)" \ | |
"Controls: $(get_controls)" \ | |
"Mouse Pointer: $(get_mouse)" \ | |
"Desktop: $(get_desktop)" \ | |
"Wallpaper: $(get_wallpaper)" | |
printf -- '>>>> %s\n' "Available themes:" | |
find /usr/share/themes/ "${HOME}"/.themes/ -maxdepth 1 -printf '%P\n' | | |
grep . | | |
sort | | |
column | |
} | |
set_desktop() { | |
gsettings set org.cinnamon.theme name "${1:?No theme declared}" | |
} | |
set_icons() { | |
gsettings set org.cinnamon.desktop.interface icon-theme "${1:?No theme declared}" | |
} | |
set_mouse() { | |
gsettings set org.cinnamon.desktop.interface cursor-theme "${1:?No theme declared}" | |
} | |
set_controls() { | |
gsettings set org.cinnamon.desktop.interface gtk-theme "${1:?No theme declared}" | |
} | |
set_wallpaper() { | |
if [[ ! -r "${1:?No wallpaper declared}" ]]; then | |
printf -- '%s\n' "${prog_name} ERROR: Could not find or read '${1}'" >&2 | |
exit 1 | |
fi | |
gsettings set org.cinnamon.desktop.background picture-uri "file://${1}" | |
} | |
set_wmborders() { | |
gsettings set org.cinnamon.desktop.wm.preferences theme "${1:?No theme declared}" | |
} | |
set_dark_theme_vars() { | |
var_is_set "${dark_wmborders}" && wmborders="${dark_wmborders}" | |
var_is_set "${dark_icons}" && icons="${dark_icons}" | |
var_is_set "${dark_controls}" && controls="${dark_controls}" | |
var_is_set "${dark_mouse}" && mouse="${dark_mouse}" | |
var_is_set "${dark_desktop}" && desktop="${dark_desktop}" | |
} | |
set_light_theme_vars() { | |
var_is_set "${light_wmborders}" && wmborders="${light_wmborders}" | |
var_is_set "${light_icons}" && icons="${light_icons}" | |
var_is_set "${light_controls}" && controls="${light_controls}" | |
var_is_set "${light_mouse}" && mouse="${light_mouse}" | |
var_is_set "${light_desktop}" && desktop="${light_desktop}" | |
} | |
set_theme_vars_by_time() { | |
if (( current_time > lights_off ))||(( current_time < lights_on )); then | |
set_dark_theme_vars | |
else | |
set_light_theme_vars | |
fi | |
} | |
set_theme() { | |
[[ "$(get_wmborders)" != "${wmborders}" ]] && set_wmborders "${wmborders}" | |
[[ "$(get_icons)" != "${icons}" ]] && set_icons "${icons}" | |
[[ "$(get_controls)" != "${controls}" ]] && set_controls "${controls}" | |
[[ "$(get_mouse)" != "${mouse}" ]] && set_mouse "${mouse}" | |
[[ "$(get_desktop)" != "${desktop}" ]] && set_desktop "${desktop}" | |
if var_is_set "${wallpaper}"; then | |
[[ "$(get_wallpaper)" != "${wallpaper}" ]] && set_wallpaper "${wallpaper}" | |
fi | |
} | |
set_theme_light() { | |
set_light_theme_vars | |
if [ -f "${app_filename}" ]; then | |
sed 's/^Icon.*/Icon=sunny/' "${app_filename}" > /tmp/.lightswitch.light | |
cp /tmp/.lightswitch.light "${app_filename}" | |
fi | |
set_theme | |
} | |
set_theme_dark() { | |
set_dark_theme_vars | |
if [ -f "${app_filename}" ]; then | |
if dpkg -l | grep -q -- "papirus-icon-theme"; then | |
sed 's|^Icon.*|Icon=palemoon|' "${app_filename}" > /tmp/.lightswitch.dark | |
else | |
sed 's/^Icon.*/Icon=night-light-symbolic/' "${app_filename}" > /tmp/.lightswitch.dark | |
fi | |
cp /tmp/.lightswitch.dark "${app_filename}" | |
fi | |
set_theme | |
} | |
set_theme_auto() { | |
if (( current_time > lights_off ))||(( current_time < lights_on )); then | |
set_theme_dark | |
else | |
set_theme_light | |
fi | |
} | |
flip_theme() { | |
found_theme=$(get_controls | tr -d "\'") | |
if [[ "${found_theme}" = "${dark_controls}" ]]; then | |
set_theme_light | |
exit 0 | |
elif [[ "${found_theme}" = "${light_controls}" ]]; then | |
set_theme_dark | |
exit 0 | |
else | |
set_theme_auto | |
fi | |
} | |
setup_papirus() { | |
add-apt-repository ppa:papirus/papirus | |
apt-get update | |
apt-get install papirus-icon-theme | |
} | |
validate_config() { | |
local conf_file conf_errcount | |
conf_file="${1:?No config file defined}" | |
[[ -r "${conf_file}" ]] || { | |
printf -- '%s\n' "Could not read ${conf_file}" >&2 | |
exit 1 | |
} | |
[[ -s "${conf_file}" ]] || { | |
printf -- '%s\n' "${conf_file} appears to be empty" >&2 | |
exit 1 | |
} | |
# TO-DO: Update the filter to cater for configs that don't use dark_ or light_ vars | |
conf_errcount="$(grep -Evc '^dark_.*=|^light_.*=|^lights_.*=|^$|^#' "${conf_file}")" | |
(( conf_errcount > 0 )) && { | |
printf -- '%s\n' "Invalid config found in ${conf_file}" >&2 | |
exit 1 | |
} | |
} | |
write_var() { | |
printf -- '%s="%s"\n' "${1:?No key supplied}" "${2:?No value supplied}" | |
} | |
# TO-DO: plug this into getopts | |
write_config() { | |
local output_file | |
output_file="${1:?No output path defined}" | |
{ | |
printf -- '%s\n' "# Set our times to switch between light and dark themes (24hr time)" | |
write_var "lights_on" "${lights_on:-0900}" | |
write_var "lights_off" "${lights_off:-2000}" | |
printf -- '%s\n' "" "# Define our theme settings" | |
var_is_set "${wmborders}" && write_var "wmborders" "${wmborders}" | |
var_is_set "${icons}" && write_var "icons" "${icons}" | |
var_is_set "${controls}" && write_var "controls" "${controls}" | |
var_is_set "${mouse}" && write_var "mouse" "${mouse}" | |
var_is_set "${desktop}" && write_var "desktop" "${desktop}" | |
var_is_set "${wallpaper}" && write_var "desktop" "${wallpaper}" | |
} > "${output_file}" | |
} | |
usage() { | |
printf '%s\n' "${prog_name} - Switch themes between light and dark themes" \ | |
"Also includes ability to set individual theme elements" "" | |
printf '\t%s\n' "Options:" \ | |
" -b set window borders to the given theme" \ | |
" -c clone https://github.com/linuxmint/cinnamon-spices-themes into ~/.themes/" \ | |
" -C read supplied config file" \ | |
" -d set desktop panels to the given theme" \ | |
" -f flip between light and dark themes" \ | |
" -g get papirus icon themes" \ | |
" -h usage" \ | |
" -i set icons to the given theme" \ | |
" -l list current theme, set elements and available themes" \ | |
" -m set mouse pointer to the given theme" \ | |
" -p add a shortcut for the flip mode to the panel" \ | |
" -s create desktop shortcuts for light and dark modes" \ | |
" -t set theme. Can be one of the following arguments:" \ | |
" a|auto - selects the '${prog_name}' light/dark theme based on the time" \ | |
" dark|Dark - the '${prog_name}' dark theme" \ | |
" light|Light - the '${prog_name}' light theme" \ | |
" [anything else] - will attempt to load a gtk theme. See '-l'." \ | |
" -w set wallpaper to the given file" | |
exit 0 | |
} | |
main() { | |
# If no args are given, assume auto mode | |
if (( "${#}" == 0 )); then | |
set_theme_auto | |
exit 0 | |
fi | |
# Otherwise, we process our args using getopts | |
while getopts ":b:cC:d:fghi:lm:pst:w:" optFlags; do | |
case "${optFlags}" in | |
(b) wmborders="${OPTARG}" ;; | |
(c) get_cinnamon_spice ;; | |
(C) | |
read_config=true | |
config_file="${OPTARG}" | |
;; | |
(d) desktop="${OPTARG}" ;; | |
(f) flip_theme ;; | |
(g) | |
sudo -E bash -c "$(declare -f setup_papirus); setup_papirus" | |
sudo -k | |
;; | |
(h) usage ;; | |
(i) icons="${OPTARG}" ;; | |
(l) list_themes ;; | |
(m) mouse="${OPTARG}" ;; | |
(p) | |
sudo -E bash -c "$(declare -f create_panel_shortcut); create_panel_shortcut" | |
sudo -k | |
add_panel_shortcut | |
flip_theme | |
;; | |
(s) create_desktop_shortcuts ;; | |
(t) | |
case "${OPTARG}" in | |
(a|auto) set_theme_auto ;; | |
([dD]ark) set_theme_dark ;; | |
([lL]ight) set_theme_light ;; | |
(*) | |
wmborders="${OPTARG}" | |
icons="${OPTARG}" | |
controls="${OPTARG}" | |
mouse="${OPTARG}" | |
desktop="${OPTARG}" | |
;; | |
esac | |
;; | |
(w) wallpaper="${OPTARG}" ;; | |
(\?) | |
printf -- '%s\n' "${prog_name} ERROR: Invalid option: '-${OPTARG}'" >&2 | |
exit 1 | |
(:) | |
printf -- '%s\n' "${prog_name} ERROR: Option '-${OPTARG}' requires an argument" >&2 | |
exit 1 | |
;; | |
esac | |
done | |
# If we have a config file, read it now to ensure that its settings take precedence | |
if [[ "${read_config}" = "true" ]]; then | |
validate_config "${config_file}" | |
# shellcheck disable=SC1090 | |
source "${config_file}" | |
# If the config file contains dark_* and/or light_* vars | |
# This function will map them relative to the current time | |
set_theme_vars_by_time | |
# Force these variables into decimal mode to prevent octal interpretation | |
lights_on=$(( 10#${lights_on} )) | |
lights_off=$(( 10#${lights_off} )) | |
fi | |
set_theme | |
} | |
main "${@}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment