123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- #!/bin/bash
- # Hard dependencies:
- # - bash
- # - inotifywait
- # - find
- # - pidof
- # - killall
- # All other dependencies are optional:
- #
- # - xrdb (for Xresources), dmenu, conky, tint2, openbox & xmlstarlet, geany
- # If the above executables are not found in PATH the according section is skipped.
- #
- # - sed: replace color_scheme in geany.conf
- ############ USER CONFIG BEGIN #############
- #
- tmpdir=/tmp # no spaces or funny characters! You have been warned! (see trap further down)
- tmpdir_cleanup=1 # set to 0 to keep logs
- # let's call these **subthemes**
- # They refer to subdirectories of the current theme directory
- # setting the "boolean" vars to 1 means the script will try its best to find
- # config files & take appropiate action.
- # Anything other than 1 means FALSE!
- # A subdir called conky, relevant files must include the string conky
- conky=1
- # For those that use a different conky executable:
- conky_cmd=conky
- conky_fallback=( "$HOME/.config/conky/sys-vert.conkyrc" "$HOME/.config/conky/journal.conkyrc" )
- # how long to wait before conky(s) is/are started, in s:
- conkydelay=3
- # A subdir called dmenu, the only relevant file must be executable and called dmenu
- dmenu=1
- dmenu_fallback="$HOME/.config/dmenu/dmenu_fallback"
- # A subdir called unst, the only relevant file must be called dunstrc
- dunst=1
- # A subdir called geany, relevant files (a color scheme) must end in conf
- geany=1
- # A subdir called openbox-3, the only relevant file must be called themerc
- openbox=1
- # For example LXDE users might need to change this
- ob_rc="${XDG_CONFIG_HOME-"$HOME/.config"}/openbox/rc.xml"
- # A subdir called tint2, relevant files must end in tint2rc
- tint2=1
- tint2_fallback=("$HOME/.config/tint2/tint2rc")
- # A subdir called Xresources, relevant files must end in .xres
- xresources=1
- #
- ############ USER CONFIG END #############
- me="${0##*/}"
- configdir="${XDG_CONFIG_HOME-"$HOME/.config"}/$me"
- mkdir -p "$configdir"
- source "$configdir/config"
- conky_restarter_pid=nothing
- # deliberately test without quotes, because I don't know how to use variables
- # with spaces in trap
- ! [ -d $tmpdir ] && echo "$tmpdir is not a valid temporary directory, exiting." && exit 1
- # OK. From now on:
- tmpdir="$tmpdir/$me"
- mkdir -p "$tmpdir"
- [[ "$tmpdir_cleanup" == 1 ]] && trap "rm -r $tmpdir;killall $me" EXIT || trap "killall $me" EXIT
- notify_file="$tmpdir/notifications"
- notify() {
- echo "$1" >> "$notify_file"
- echo -e "$1"
- }
- files() {
- # simply checks if these files exist, because 'test -r' can only check for 1 file
- # mywiki.wooledge.org/BashFAQ/004
- shopt -s nullglob dotglob
- files=($@)
- (( ${#files[*]} )) && return 0 || return 1
- }
- conky_restarter() {
- if grep '^[[:space:]]*background[[:space:]]=[[:space:]]true' "$1"; then
- # If your conky forks to the background, just start it normally and drop the respawn
- "$conky_cmd" -c "$config" & return
- fi
- log="$tmpdir/${file##*/}.log"
- restarts=0
- while :; do
- time="$SECONDS"
- "$conky_cmd" -c "$1" >>"$log" 2>&1
- printf "\nConky apparently crashed at %($TIMESTAMP_FORMAT)T\n\n" >> "$log"
- ((restarts++))
- time=$(( SECONDS - time ))
- if (( time < 10 )) && (( restarts > 10 )); then
- printf "Too many restarts and less than 10s between start and crash - not good. Exiting." >> "$log"
- return
- fi
- sleep 1
- done
- }
- strip_ansi() {
- shopt -s extglob # function uses extended globbing
- while read -r line; do
- echo "${line//$'\e'\[*([0-9;])m/}"
- done
- }
- # Works only on systems that have GTK3 rc files.
- # Should Work (tm) on LXDE, LXQt and custom desktops
- # If however you set your theme with e.g. XFCE4's Settings dialog, no such
- # rc files are created.
- watchfile="${XDG_CONFIG_HOME-"$HOME/.config"}/gtk-3.0/settings.ini"
- [ ! -r "$watchfile" ] && echo "No readable gtk settings file to watch. Exiting." && exit 1
- [ ! -r "$ob_rc" ] && openbox=0 && unset ob_rc
- geany_dir="${XDG_CONFIG_HOME-"$HOME/.config"}/geany"
- [ -d "$geany_dir" ] && [ -x "$geany_dir" ] || { geany=0; unset geany_dir; }
- # for each subtheme: the first folder found wins.
- # That way we can extend & override system themes in $HOME
- themepath=( "$HOME/.local/share/themes" "/usr/share/themes" )
- # will be set to 0 at the end of the first loop
- firstrun=1
- while :; do
-
- rm -f "$notify_file"
- CURRENT_THEME="$(/usr/bin/grep gtk-theme-name "$watchfile")"
- CURRENT_THEME="${CURRENT_THEME##*=}"
- CURRENT_THEME="${CURRENT_THEME#*\"}"
- CURRENT_THEME="${CURRENT_THEME%\"*}"
-
- if [[ "x$CURRENT_THEME" == "x" ]]; then
- notify "Could not extract current theme name from $watchfile."
- else
- echo "$CURRENT_THEME" > "$configdir/current_theme"
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/gtk-2.0/gtkrc"
- if [ -r "$path" ]; then
- file="$configdir/gtk2-color-scheme"
- echo "$CURRENT_THEME" > "$file.current"
- grep gtk.color.scheme "$path" | cut -d\" -f2 | sed 's/\\n/\n/g' >> "$file.current"
- cp "$file.current" "$file.$CURRENT_THEME"
- break
- fi
- done
- # lxappearance uses the folder name to name the theme, NOT what is in
- # index.theme (and both gtk2 and gtk3 recognize it), so we just assume that
- # CURRENT_THEME is the actual name of the theme directory
- if [[ "$conky" == "1" ]] && which "$conky_cmd" >/dev/null; then
- sleep "$conkydelay"
- done=0
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/conky"
- if [ -d "$path" ] && [ -x "$path" ] && files "$path"/*conky*; then
- done=1
- killall "$conky_cmd"
- # make sure it's 10x dead:
- for ((i=0;i<10;i++)); do
- sleep 0.1
- pidof "$conky_cmd" >/dev/null || break
- killall -9 "$conky_cmd"
- done
- for file in "$path"/*conky*; do
- sleep 0.1
- "$conky_cmd" -c "$file" > "$tmpdir/${file##*/}".log 2>&1 &
- notify "Started $conky_cmd -c $file"
- done
- break # because the first path wins
- fi
- done
- # fallback scenario
- if [[ "$done" == 0 ]] && (( ${#conky_fallback[@]} > 0 )); then
- killall -q "$conky_cmd"
- # make sure it's 10x dead:
- for ((i=0;i<10;i++)); do
- sleep 0.1
- pidof "$conky_cmd" >/dev/null || break
- killall -9 "$conky_cmd"
- done
- for file in "${conky_fallback[@]}"; do
- "$conky_cmd" -c "$file" > "$tmpdir/${file##*/}".log 2>&1 &
- notify "Started $conky_cmd -c $file"
- done
- fi
- fi & conkypid=$!
-
- if [[ "$dmenu" == "1" ]] && [[ "$firstrun" == 0 ]]; then
- done=0
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/dmenu"
- if [ -d "$path" ] && [ -x "$path" ] && [ -x "$path"/dmenu ]; then
- [ -e "$HOME/bin/dmenu" ] && \
- [[ "$(file -b --mime-type "$HOME/bin/dmenu")" != *symlink* ]] && \
- mv "$HOME/bin/dmenu" "$HOME/bin/dmenu.$me.bak"
- ln -sf "$path/dmenu" $HOME/bin/dmenu
- notify "Symlinked $path/dmenu to $HOME/bin"
- done=1
- break # because the first path wins
- fi
- done
- # fallback scenario
- if [[ "$done" == 0 ]] && [ -n "$dmenu_fallback" ]; then
- [ -e "$HOME/bin/dmenu" ] && \
- [[ "$(file -b --mime-type "$HOME/bin/dmenu")" != *symlink* ]] && \
- mv "$HOME/bin/dmenu" "$HOME/bin/dmenu.$me.bak"
- ln -sf "$dmenu_fallback" $HOME/bin/dmenu
- notify "Symlinked $dmenu_fallback to $HOME/bin"
- fi
- fi & dmenupid=$!
-
- if [[ "$dunst" == "1" ]] && [[ "$firstrun" == 0 ]]; then
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/dunst"
- if [ -d "$path" ] && [ -x "$path" ] && [ -r "$path"/dunstrc ]; then
- dest="${XDG_CONFIG_HOME-"$HOME/.config"}/dunst/dunstrc"
- [ -e "$dest" ] && \
- [[ "$(file -b --mime-type "$dest")" != *symlink* ]] && \
- mv "$dest" "$dest.$me.bak"
- ln -sf "$path/dunstrc" $dest
- killall -q dunst
- notify "Symlinked $path/dunstrc to $dest"
- break # because the first path wins
- fi
- done
- fi & dunstpid=$!
-
- if [[ "$geany" == "1" ]] && [[ "$firstrun" == 0 ]]; then
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/geany"
- if [ -d "$path" ] && [ -x "$path" ] && files "$path"/*conf; then
- mkdir -p "$geany_dir/colorschemes"
- schemefile=""
- for file in "$path"/*conf; do
- schemefile="${me}_${CURRENT_THEME}_${file##*/}"
- ln -s "$file" "$geany_dir/colorschemes/$schemefile"
- notify "Symlinked $schemefile to $geanydir/colorschemes"
- if [ -w "$geany_dir/geany.conf" ] && which sed >/dev/null; then
- cp -f "$geany_dir/geany.conf" "$geany_dir/geany.conf.$me"
- sed -i "s/^color_scheme=.*$/color_scheme=$schemefile/" "$geany_dir/geany.conf"
- notify "Set color_scheme=$schemefile in $geany_dir/geany.conf.\nNot effective while geany is running."
- fi
- done
- if [[ "$schemefile" != "" ]] && [ -w "$geany_dir/geany.conf" ] && which sed >/dev/null; then
- cp -f "$geany_dir/geany.conf" "$geany_dir/geany.conf.$me"
- sed -i "s/^color_scheme=.*$/color_scheme=$schemefile/" "$geany_dir/geany.conf"
- fi
- break # because the first path wins
- fi
- done
- fi & geanypid=$!
-
- if [[ "$openbox" == "1" ]] && [[ "$firstrun" == 0 ]] && which openbox >/dev/null && which xmlstarlet >/dev/null; then
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/openbox-3"
- if [ -d "$path" ] && [ -x "$path" ] && [ -r "$path"/themerc ]; then
- cp -f "$ob_rc" "$ob_rc"."$me"
- # priceless piece of information: https://superuser.com/a/508143
- xmlstarlet ed -L -N o="http://openbox.org/3.4/rc" -u '/o:openbox_config/o:theme/o:name' -v "$CURRENT_THEME" "$ob_rc"
- pidof openbox >/dev/null && sleep 1 && openbox --reconfigure
- notify "Set openbox theme to $CURRENT_THEME"
- break # because the first path wins
- fi
- done
- fi & obpid=$!
- if [[ "$xresources" == "1" ]] && which xrdb >/dev/null; then
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/Xresources"
- if [ -d "$path" ] && [ -x "$path" ] && files "$path"/*xres; then
- while pidof xrdb >/dev/null; do sleep 0.1; done
- for file in "$path"/*xres; do
- xrdb -merge "$file"
- notify "Merged $file into Xrdb"
- done
- break # because the first path wins
- fi
- done
- fi & xrespid=$!
- if [[ "$tint2" == "1" ]] && which tint2 >/dev/null; then
- done=0
- sleep 2 # otherwise tint2 triggers a restart because "configuration change
- # in the root window" - no idea why, I tested thoroughly
- for path in "${themepath[@]}"; do
- path="$path/$CURRENT_THEME/tint2"
- if [ -d "$path" ] && [ -x "$path" ] && files "$path"/*tint2rc; then
- killall tint2 >/dev/null && sleep 0.1
- for file in "$path"/*tint2rc; do
- tint2 -c "$file" 2>&1 | strip_ansi > "$tmpdir/${file##*/}".log &
- notify "Started tint2 -c $file"
- done
- done=1
- break # because the first path wins
- fi
- done
- # fallback scenario
- if [[ "$done" == 0 ]] && (( ${#tint2_fallback[@]} > 0 )); then
- killall tint2 >/dev/null && sleep 0.1
- for file in "${tint2_fallback[@]}"; do
- tint2 -c "$file" 2>&1 | strip_ansi > "$tmpdir/${file##*/}".log &
- notify "Started tint2 -c $file"
- done
- fi
- fi & tint2pid=$!
- fi
- wait $conkypid $dmenupid $dunstpid $geanypid $obpid $tint2pid $xrespid
- echo "Waited for $conkypid $dmenupid $dunstpid $geanypid $obpid $tint2pid $xrespid"
-
- ((firstrun<1)) && notify-send -t 0 "$me" "$(< "$notify_file")"
- sleep 1
- echo "Waiting for changes..."
- inotifywait -e modify "$watchfile"
- firstrun=0
- done
|