diff --git a/system_files/shared/usr/lib/systemd/user/bluefin-dynamic-wallpaper.service b/system_files/shared/usr/lib/systemd/user/bluefin-dynamic-wallpaper.service new file mode 100644 index 00000000000..fc51dd730c8 --- /dev/null +++ b/system_files/shared/usr/lib/systemd/user/bluefin-dynamic-wallpaper.service @@ -0,0 +1,11 @@ +[Unit] +Description=Bluefin Dynamic Wallpaper +After=graphical-session.target +PartOf=graphical-session.target + +[Service] +Type=oneshot +ExecStart=/usr/libexec/bluefin-dynamic-wallpaper + +[Install] +WantedBy=graphical-session.target diff --git a/system_files/shared/usr/lib/systemd/user/bluefin-dynamic-wallpaper.timer b/system_files/shared/usr/lib/systemd/user/bluefin-dynamic-wallpaper.timer new file mode 100644 index 00000000000..4282f20aad1 --- /dev/null +++ b/system_files/shared/usr/lib/systemd/user/bluefin-dynamic-wallpaper.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Run Bluefin Dynamic Wallpaper daily +PartOf=graphical-session.target + +[Timer] +OnBootSec=1min +OnUnitActiveSec=24h +Persistent=true + +[Install] +WantedBy=graphical-session.target diff --git a/system_files/shared/usr/libexec/bluefin-dynamic-wallpaper b/system_files/shared/usr/libexec/bluefin-dynamic-wallpaper new file mode 100755 index 00000000000..9ae5e2fddc2 --- /dev/null +++ b/system_files/shared/usr/libexec/bluefin-dynamic-wallpaper @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Set the appropriate wallpaper based on month and hemisphere +# This will be run at login and periodically to update the wallpaper + +readonly WALLPAPER_BASE_PATH="/usr/share/backgrounds/bluefin" + +# Don't clobber custom wallpaper +CURRENT_WALLPAPER="$(gsettings get org.gnome.desktop.background picture-uri)" +CURRENT_WALLPAPER="${CURRENT_WALLPAPER#\'file://}" +CURRENT_WALLPAPER="${CURRENT_WALLPAPER%\'}" + +if [[ "$CURRENT_WALLPAPER" != "${WALLPAPER_BASE_PATH}"/*-bluefin.xml ]]; then + echo "User has a personal wallpaper set. Not changing it." + exit 0 +fi + + +MONTH="$(date +%m)" +MONTH_NUM="${MONTH#0}" # Remove leading zero if present + +LATITUDE="$(/usr/libexec/get-geoclue-latitude.py)" +LOCATION_EXIT=$? +if [[ $LOCATION_EXIT -eq 2 ]]; then + echo "Location services disabled or denied, not touching wallpaper" + exit 0 +elif [[ $LOCATION_EXIT -ne 0 ]]; then + echo "Error getting location, not touching wallpaper" + exit 1 +else + if ! [[ "$LATITUDE" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then + echo "Invalid latitude format: $LATITUDE" >&2 + exit 1 + fi + + # Determine hemisphere + if [[ "${LATITUDE:0:1}" == "-" ]]; then + HEMISPHERE="south" + # Add 6 months and wrap around if needed + MONTH_NUM=$(( (MONTH_NUM + 6 - 1) % 12 + 1 )) + else + HEMISPHERE="north" + fi +fi + +# Format month with leading zero if needed +if [[ $MONTH_NUM -lt 10 ]]; then + MONTH_PADDED="0$MONTH_NUM" +else + MONTH_PADDED="$MONTH_NUM" +fi + +WALLPAPER_PATH="${WALLPAPER_BASE_PATH}/${MONTH_PADDED}-bluefin.xml" + +if [[ ! -f "$WALLPAPER_PATH" ]]; then + echo "Wallpaper file not found: $WALLPAPER_PATH" >&2 + exit 1 +fi + +# Apply the wallpaper +if gsettings set org.gnome.desktop.background picture-uri "file://$WALLPAPER_PATH" && \ + gsettings set org.gnome.desktop.background picture-uri-dark "file://$WALLPAPER_PATH"; then + echo "Successfully set wallpaper to $WALLPAPER_PATH (adjusted month: $MONTH_NUM, hemisphere: $HEMISPHERE)" +else + echo "Failed to set wallpaper" >&2 + exit 1 +fi diff --git a/system_files/shared/usr/libexec/get-geoclue-latitude.py b/system_files/shared/usr/libexec/get-geoclue-latitude.py new file mode 100755 index 00000000000..42efdab3010 --- /dev/null +++ b/system_files/shared/usr/libexec/get-geoclue-latitude.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +import time +import sys +from pydbus import SystemBus + +LOCATION_TIMEOUT_SECONDS = 30 +POLL_INTERVAL_SECONDS = 0.5 + +def get_geoclue_client(): + bus = SystemBus() + manager = bus.get("org.freedesktop.GeoClue2", "/org/freedesktop/GeoClue2/Manager") + client_path = manager.CreateClient() + client = bus.get("org.freedesktop.GeoClue2", client_path) + + # Set client properties + client.DesktopId = "bluefin-dynamic-wallpaper" + client.RequestedAccuracyLevel = 1 + client.Start() + + return bus, client + +def wait_for_location(bus, client): + """Wait for location data to become available.""" + max_attempts = int(LOCATION_TIMEOUT_SECONDS / POLL_INTERVAL_SECONDS) + + for attempt in range(max_attempts): + try: + if hasattr(client, 'Location') and client.Location: + location_obj = bus.get("org.freedesktop.GeoClue2", client.Location) + if hasattr(location_obj, 'Latitude') and location_obj.Latitude is not None: + return location_obj.Latitude + except Exception as e: + # Ignore "object does not export any interfaces" errors during initialization + if "object does not export any interfaces" not in str(e): + print(f"Unexpected error while waiting for location: {e}", file=sys.stderr) + sys.exit(1) + + time.sleep(POLL_INTERVAL_SECONDS) + + # Timeout reached + print("error: location unavailable after timeout", file=sys.stderr) + sys.exit(1) + +def main(): + try: + bus, client = get_geoclue_client() + latitude = wait_for_location(bus, client) + print(latitude) + sys.exit(0) + + except Exception as e: + if "org.freedesktop.DBus.Error.AccessDenied" in str(e): + print("Location services disabled or denied", file=sys.stderr) + sys.exit(2) + else: + print(f"error: {e}", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/system_files/shared/usr/share/applications/bluefin-dynamic-wallpaper.desktop b/system_files/shared/usr/share/applications/bluefin-dynamic-wallpaper.desktop new file mode 100644 index 00000000000..0bdf24aff7d --- /dev/null +++ b/system_files/shared/usr/share/applications/bluefin-dynamic-wallpaper.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Name=Bluefin Dynamic Wallpaper +Exec=/usr/libexec/bluefin-dynamic-wallpaper +Icon=preferences-desktop-wallpaper +Type=Application diff --git a/system_files/shared/usr/share/ublue-os/user-setup.hooks.d/20-dynamic-wallpaper.sh b/system_files/shared/usr/share/ublue-os/user-setup.hooks.d/20-dynamic-wallpaper.sh new file mode 100644 index 00000000000..dcbd04a88ff --- /dev/null +++ b/system_files/shared/usr/share/ublue-os/user-setup.hooks.d/20-dynamic-wallpaper.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +source /usr/lib/ublue/setup-services/libsetup.sh + +version-script dynamic-wallpaper user 1 || exit 0 + +set -euo pipefail + +# Enable dynamic wallpaper service and timer +echo "Enabling dynamic wallpaper service and timer" +systemctl --user enable --now bluefin-dynamic-wallpaper.service +systemctl --user enable --now bluefin-dynamic-wallpaper.timer