diff --git a/wallpapers-bing/wallpapers_bing.vala b/wallpapers-bing/wallpapers_bing.vala index aa95003..002562e 100644 --- a/wallpapers-bing/wallpapers_bing.vala +++ b/wallpapers-bing/wallpapers_bing.vala @@ -12,12 +12,20 @@ public void peas_register_types(TypeModule module) { namespace WallpapersBing { + private const uint REFRESH_SECONDS = 86400; + private const uint MAX_BACKOFF_SECONDS = 3600; + public class Provider : WallpaperHelperProvider, WallpaperProvider { // Set by choices() from the helper's own answer, never re-derived // from the config file -- see WallpaperBing.CONSOLIDATED_ID. False // until the first choices() call, so the name starts as plain "Bing" // and the browser refreshes the label once the helper has replied. private bool combined = false; + private bool prefetching = false; + private int failures = 0; + private uint refresh_source = 0; + private ulong network_handler = 0; + private Cancellable cancel_all = new Cancellable(); public string id { get { return WallpaperBing.PROVIDER_ID; } } // Matches the collection label the rotator helper writes for the // same de-duplicated view, so the online browser and the wallpaper @@ -28,6 +36,54 @@ namespace WallpapersBing { public bool requires_credentials { get { return false; } } public bool supports_search { get { return false; } } public Provider() { base("singularity-wallpaper-bing"); } + public void start_prefetch() { + cancel_all = new Cancellable(); + if (network_handler == 0) + network_handler = NetworkMonitor.get_default().network_changed.connect(() => { + if (failures > 0 || refresh_source == 0) prefetch(); + }); + prefetch(); + } + + public void stop_prefetch() { + cancel_all.cancel(); + if (refresh_source != 0) Source.remove(refresh_source); + refresh_source = 0; + if (network_handler != 0) NetworkMonitor.get_default().disconnect(network_handler); + network_handler = 0; + } + + private void prefetch() { + var monitor = NetworkMonitor.get_default(); + if (prefetching || cancel_all.is_cancelled() || !monitor.network_available || monitor.network_metered) + return; + prefetching = true; + run_prefetch.begin(); + } + + private async void run_prefetch() { + // Hold this run's own Cancellable: stop_prefetch() followed by + // start_prefetch() replaces cancel_all while a fetch is in flight. + var cancel = cancel_all; + bool ok = false; + try { + yield command({helper, "fetch", "--if-stale"}, cancel, 900); + ok = true; + } catch (Error e) { + if (!cancel.is_cancelled()) warning("wallpapers-bing: prefetch failed: %s", e.message); + } + prefetching = false; + if (cancel.is_cancelled()) return; + failures = ok ? 0 : failures + 1; + uint delay = ok ? REFRESH_SECONDS : uint.min(30U << uint.min(failures - 1, 7U), MAX_BACKOFF_SECONDS); + if (refresh_source != 0) Source.remove(refresh_source); + refresh_source = Timeout.add_seconds(delay, () => { + refresh_source = 0; + prefetch(); + return Source.REMOVE; + }); + } + public async ArrayList choices(string index, Cancellable? cancel) throws Error { var loaded = WallpaperBing.markets(yield command({helper, "markets"}, cancel, 30)); // The helper is expected to always advertise the combined view @@ -64,10 +120,12 @@ public class WallpapersBingPlugin : Object, Singularity.Plugin { this.context = ctx; provider = new WallpapersBing.Provider(); context.add_wallpaper_provider(provider); + provider.start_prefetch(); } public void deactivate() { if (provider != null) { + provider.stop_prefetch(); context.remove_wallpaper_provider(provider); provider = null; }