Last active
July 27, 2024 23:24
-
-
Save logic/cec621d3d5cf165863dd to your computer and use it in GitHub Desktop.
GNOME background rotation script
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
[Unit] | |
Description=Rotate GNOME wallpaper | |
[Service] | |
Type=oneshot | |
ExecStart=/bin/bash ${HOME}/bin/rotate_background.sh |
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
[Unit] | |
Description=Rotate GNOME wallpaper every five minutes | |
[Timer] | |
OnCalendar=*:0/5 | |
Persistent=true | |
[Install] | |
WantedBy=timers.target |
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 | |
# GNOME wallpaper rotation script | |
# Where to find the wallpapers. | |
WALLPAPERS="${HOME}/Pictures/Wallpapers" | |
# Percentage off the width and height of the screen that constitutes a | |
# "small" image, ie. one that should just be centered on the screen rather | |
# than stretched to fit. On lower-DPI screens, setting this lower is a good | |
# idea, or you'll end up with "fuzzy" images; on high-DPI displays, you can | |
# bump it up a bit. | |
SMALL="0.4" | |
# Percentage of our screen's height/width ratio that we'll allow an image | |
# to be stretched to fit. Set this too large, and you'll end up with very | |
# distorted backgrounds. | |
CLOSE="0.0045" | |
# Percentage of vertical image height we're willing to sacrifice before we | |
# resort to letterboxing. Top/bottom letterboxing is often a pleasing visual | |
# effect, and losing vertical resolution is usually more of a loss than | |
# horizontal trimming, so setting this lower is a good idea. | |
VERTICAL="0.1" | |
# Percentage of horizontal image width we're willing to sacrifice before we | |
# resort to letterboxing. "Sidebars" are often very visually distracting, so | |
# setting this higher than $VERTICAL is often a good call. | |
HORIZONTAL="0.6" | |
cmd_or_exit() { | |
type "${1}" > /dev/null 2>&1 || ( \ | |
echo "Missing ${1} command" && \ | |
exit 1 | |
) | |
} | |
cmd_or_exit gm | |
cmd_or_exit grep | |
cmd_or_exit gsettings | |
cmd_or_exit pgrep | |
cmd_or_exit python | |
cmd_or_exit shuf | |
cmd_or_exit xwininfo | |
# Echo the filename of a random wallpaper. | |
pick_wallpaper() { | |
find "${WALLPAPERS}" -type f \ | |
-iname '*.jpg' -o -iname '*.png' -o -iname '*.gif' \ | |
| shuf -n1 | |
} | |
# Given a filename of an image, echo the height and width of it. | |
image_size() { | |
gm identify -ping -format '%h %w\n' "${1}" | |
} | |
# Echo the height and width of the current display. | |
screen_size() { | |
[ -z "$DISPLAY" ] && exit 0 # No X means no reason to run. | |
local wininfo | |
wininfo="$(xwininfo -root -stats)" | |
( | |
echo "${wininfo}" | grep -F ' Height: ' | |
echo "${wininfo}" | grep -F ' Width: ' | |
) | awk '{ print $2 }' | |
} | |
# Given two numbers, calculate the ratio to four decimal places. | |
calc_ratio() { | |
# My kingdom for floating-point math. | |
python -c 'print int(1.0*'"$1"'/'"$2"'*10000)' | |
} | |
# Given a value and a percentage, echo the value minus that percentage. | |
percent_off() { | |
python -c 'print int('"$1"'-('"$1"'*'"$2"'))' | |
} | |
# Given a value and a percentage, echo the value plus that percentage. | |
percent_plus() { | |
python -c 'print int('"$1"'+('"$1"'*'"$2"'))' | |
} | |
# Given a "<num> <value>" list as stdin, echo the largest <num>'s <value>. | |
pick() { | |
sort -n | awk '{ print $2 }' | tail -1 | |
} | |
# Given a filename and a geometry, echo the frequency of the most popular | |
# color, and the color value. | |
extract_edge() { | |
gm convert "${1}" -crop "${2}" txt:- | \ | |
sed -e '/^#/d' | \ | |
awk '{ print $3 }' | \ | |
uniq -c | \ | |
sort -n | \ | |
tail -1 | |
} | |
# Given a filename, height, and width, echo the frequency of the most popular | |
# color on the left edge, and it's color value. | |
# | |
# Note that we don't take the outside edges, but instead take from the | |
# second pixel row into the image; this ensures we don't get bitten by | |
# JPEG edge artifacts. | |
left_edge() { | |
extract_edge "${1}" "1x${2}+2" | |
} | |
# Given a filename, height, and width, echo the frequency of the most popular | |
# color on the right edge, and it's color value. | |
right_edge() { | |
extract_edge "${1}" "1x${2}+$(( $3 - 2 ))" | |
} | |
# Given a filename, height, and width, echo the frequency of the most popular | |
# color on the top edge, and it's color value. | |
top_edge() { | |
extract_edge "${1}" "${3}x1+1+2" | |
} | |
# Given a filename, height, and width, echo the frequency of the most popular | |
# color on the bottom edge, and it's color value. | |
bottom_edge() { | |
extract_edge "${1}" "${3}x1+1+$(( $2 - 2 ))" | |
} | |
# Given a filename, height, and width, echo the most popular color along the | |
# top abd bottom edges. | |
letterbox_topbottom() { | |
( | |
top_edge "$1" "$2" "$3" | |
bottom_edge "$1" "$2" "$3" | |
) | pick | |
} | |
# Given a filename, height, and width, echo the most popular color along the | |
# left and right edges. | |
letterbox_leftright() { | |
( | |
left_edge "$1" "$2" "$3" | |
right_edge "$1" "$2" "$3" | |
) | pick | |
} | |
# Given a filename, height, and width, echo the most popular color along | |
# any edge. | |
letterbox_all() { | |
( | |
top_edge "$1" "$2" "$3" | |
bottom_edge "$1" "$2" "$3" | |
left_edge "$1" "$2" "$3" | |
right_edge "$1" "$2" "$3" | |
) | pick | |
} | |
# Echo the address of the gnome-session dbus endpoint. | |
dbus_address() { | |
local gspid | |
gspid="$(pgrep -u "${USER}" gnome-session)" | |
[ -z "${gspid}" ] && exit 0 # No gnome-session, no reason to run. | |
grep -z '^DBUS_SESSION_BUS_ADDRESS=' "/proc/${gspid}/environ" \ | |
| cut -d= -f2- | tr '\0' '\n' | |
} | |
# Given a filename, background color, and picture distortion option, set | |
# the background. | |
update_background() { | |
# gsettings needs to know how to communicate with gnome-session. | |
export DBUS_SESSION_BUS_ADDRESS | |
DBUS_SESSION_BUS_ADDRESS="$(dbus_address)" | |
[ -z "${DBUS_SESSION_BUS_ADDRESS}" ] && exit 0 # Can't run without it. | |
# clear the previous picture and set the new background color before | |
# changing the picture option, and set the picture option before setting | |
# the new image, so you don't get visually-jarring resizing animations | |
gsettings set org.gnome.desktop.background picture-uri '' | |
gsettings set org.gnome.desktop.background primary-color "$2" | |
gsettings set org.gnome.desktop.background picture-options "$3" | |
gsettings set org.gnome.desktop.background picture-uri "file://$1" | |
} | |
main() { | |
local image | |
if [[ -z $1 ]]; then | |
image=$(pick_wallpaper) | |
else | |
image=$1 | |
fi | |
# shellcheck disable=SC2046 | |
set $(image_size "${image}") | |
local height width ratio | |
height=$1 | |
width=$2 | |
ratio="$(calc_ratio "$height" "$width")" | |
# shellcheck disable=SC2046 | |
set $(screen_size) | |
local sc_height sc_width sc_ratio | |
sc_height=$1 | |
sc_width=$2 | |
sc_ratio="$(calc_ratio "$sc_height" "$sc_width")" | |
local small_width small_height | |
small_width="$(percent_off "$sc_width" "$SMALL")" | |
small_width="$(percent_off "$sc_height" "$SMALL")" | |
local ratio_close ratio_vertical | |
ratio_close="$(percent_off "$sc_ratio" "$CLOSE")" | |
ratio_vertical="$(percent_off "$sc_ratio" "$VERTICAL")" | |
local ratio_close_wide | |
ratio_close_wide="$(percent_plus "$sc_ratio" "$CLOSE")" | |
ratio_horiz_wide="$(percent_plus "$sc_ratio" "$HORIZONTAL")" | |
local bgcolor | |
local opt | |
if [[ $width -lt $small_width && $height -lt $small_height ]]; then | |
# tinypic, center. | |
bgcolor="$(letterbox_all "$image" "$height" "$width")" | |
opt='centered' | |
elif [[ $ratio -lt $ratio_close ]]; then | |
# lower than our ratio | |
if [[ $ratio -lt $ratio_vertical ]]; then | |
bgcolor="$(letterbox_topbottom "$image" "$height" "$width")" | |
opt='scaled' | |
else | |
bgcolor="$(letterbox_leftright "$image" "$height" "$width")" | |
opt='zoom' | |
fi | |
elif [[ $ratio -gt $ratio_close_wide ]]; then | |
# higher than our ratio | |
if [[ $ratio -gt $ratio_horiz_wide ]]; then | |
bgcolor="$(letterbox_topbottom "$image" "$height" "$width")" | |
opt='scaled' | |
else | |
bgcolor="$(letterbox_leftright "$image" "$height" "$width")" | |
opt='zoom' | |
fi | |
else | |
# ~= ratio and big enough, stretch to fit. | |
bgcolor="$(letterbox_all "$image" "$height" "$width")" | |
opt='stretched' | |
fi | |
update_background "$image" "$bgcolor" "$opt" | |
} | |
main "$@" | |
# EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This was originally written for Fedora 23+, and requires
GraphicsMagick
(forgm convert
andgm identify
),xorg-x11-utils
(forxwininfo
), andpython
(for the percentage and ratio calculations).Place both the
.service
and.timer
files in${HOME}/.config/systemd/user
, and run:Put the
rotate_background.sh
script in${HOME}/bin
, creating the directory if necessary. To test that it's working, run:If you make changes to either the
.service
or.timer
files (changing the time span, for example), you'll need to reload them with: