diff --git a/src/ImageCache.vala b/src/ImageCache.vala index fee9d65..4bdfb4d 100644 --- a/src/ImageCache.vala +++ b/src/ImageCache.vala @@ -4,6 +4,7 @@ using Gdk; using Json; private struct CachedImage { + public string uri; public int size; @@ -16,24 +17,30 @@ private struct CachedImage { } public static bool equal(CachedImage? a, CachedImage? b) { - if (a == null || b == null) { return false; } + if (a == null || b == null) + return false; return a.size == b.size && a.uri == b.uri; } + } public delegate void PixbufCallback (Gdk.Pixbuf pb); public class Tootle.ImageCache : GLib.Object { + private GLib.HashTable in_progress; private GLib.HashTable pixbufs; private uint total_size_est; private uint size_limit; + private string cache_path; construct { pixbufs = new GLib.HashTable(CachedImage.hash, CachedImage.equal); in_progress = new GLib.HashTable(CachedImage.hash, CachedImage.equal); total_size_est = 0; - Tootle.settings.changed.connect (on_settings_changed); + cache_path = "%s/%s".printf (GLib.Environment.get_user_cache_dir (), app.application_id); + + settings.changed.connect (on_settings_changed); on_settings_changed (); } @@ -41,37 +48,35 @@ public class Tootle.ImageCache : GLib.Object { GLib.Object(); } - // adopts cache size limit from settings private void on_settings_changed () { // assume 32BPP (divide bytes by 4 to get # pixels) and raw, overhead-free storage // cache_size setting is number of megabytes - size_limit = (1024 * 1024 * Tootle.settings.cache_size / 4); - enforce_size_limit (); + size_limit = (1024 * 1024 * settings.cache_size) / 4; + if (settings.cache) + enforce_size_limit (); + else + remove_all (); } - // remove all cached images public void remove_all () { - GLib.debug("image cache cleared"); + debug("Image cache cleared"); pixbufs.remove_all (); total_size_est = 0; } - // remove any entry for the given uri and size from the cache public void remove_one (string uri, int size) { - GLib.debug("image cache removing %s", uri); CachedImage ci = CachedImage (uri, size); bool removed = pixbufs.remove(ci); if (removed) { assert (total_size_est >= size * size); total_size_est -= size * size; - GLib.debug("image cache removed %s; size est. is %zd", uri, total_size_est); + debug("Cache usage: %zd", total_size_est); } } - // delete the pixbuf with the smallest reference count from the cache + //TODO: fix me + // remove least used image private void remove_least_used () { - GLib.debug("image cache removing least-used"); - // for now, dummy implementation: just remove the first pixbuf var keys = pixbufs.get_keys(); if (keys.first() != null) { var ci = keys.first().data; @@ -80,15 +85,14 @@ public class Tootle.ImageCache : GLib.Object { } private void enforce_size_limit () { - GLib.debug("image cache enforcing size limit (%zd/%zd)", total_size_est, size_limit); - while (total_size_est > size_limit && pixbufs.size() > 0) { + debug("Updating size limit (%zd/%zd)", total_size_est, size_limit); + while (total_size_est > size_limit && pixbufs.size() > 0) remove_least_used (); - } + assert (total_size_est <= size_limit); } private void store_pixbuf (CachedImage ci, Gdk.Pixbuf pixbuf) { - GLib.debug("image cache inserting %s@%d", ci.uri, ci.size); assert (!pixbufs.contains (ci)); pixbufs.insert (ci, pixbuf); in_progress.remove (ci); @@ -103,13 +107,12 @@ public class Tootle.ImageCache : GLib.Object { cb (pb); return; } - GLib.debug("image cache miss for %s@%d", uri, size); Soup.Message? msg = in_progress.get(ci); if (msg == null) { msg = new Soup.Message("GET", uri); msg.finished.connect(() => { - GLib.debug("image cache about to insert %s@%d", uri, size); + debug("Caching %s@%d", uri, size); var data = msg.response_body.data; var stream = new MemoryInputStream.from_data (data); var pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); @@ -117,11 +120,9 @@ public class Tootle.ImageCache : GLib.Object { cb(pixbuf); }); in_progress[ci] = msg; - Tootle.network.queue (msg); + network.queue_custom (msg); } else { - GLib.debug("found in-progress request for %s@%d", uri, size); msg.finished.connect_after(() => { - GLib.debug("in-progress request finished for %s@%d", uri, size); cb(pixbufs[ci]); }); } @@ -138,4 +139,5 @@ public class Tootle.ImageCache : GLib.Object { public void load_scaled_image (string uri, Gtk.Image image, int size = 64) { get_image.begin(uri, size, image.set_from_pixbuf); } + } diff --git a/src/NetManager.vala b/src/NetManager.vala index 462a199..fe1136a 100644 --- a/src/NetManager.vala +++ b/src/NetManager.vala @@ -14,12 +14,8 @@ public class Tootle.NetManager : GLib.Object { private int requests_processing = 0; private Soup.Session session; - private Soup.Cache cache; - public string cache_path; construct { - cache_path = "%s/%s".printf (GLib.Environment.get_user_cache_dir (), Tootle.app.application_id); - cache = new Soup.Cache (cache_path, Soup.CacheType.SINGLE_USER); session = new Soup.Session (); session.ssl_strict = true; session.ssl_use_system_ca_file = true; @@ -31,12 +27,6 @@ public class Tootle.NetManager : GLib.Object { finished (); }); - Tootle.app.shutdown.connect (() => { - cache.dump (); - }); - Tootle.settings.changed.connect (on_settings_changed); - on_settings_changed (); - // Soup.Logger logger = new Soup.Logger (Soup.LoggerLogLevel.BODY, -1); // session.add_feature (logger); } @@ -45,23 +35,6 @@ public class Tootle.NetManager : GLib.Object { GLib.Object(); } - private void on_settings_changed () { - // cache.set_max_size (1024 * 1024 * Tootle.settings.cache_size); - // var has_cache = session.has_feature (cache.get_type ()); - // if (Tootle.settings.cache) { - // if (!has_cache) { - // debug ("Turning on cache"); - // session.add_feature (cache); - // } - // } - // else { - // if (has_cache) { - // debug ("Turning off cache"); - // session.remove_feature (cache); - // } - // } - } - public async WebsocketConnection stream (Soup.Message msg) { return yield session.websocket_connect_async (msg, null, null, null); } @@ -108,7 +81,7 @@ public class Tootle.NetManager : GLib.Object { return msg; } - public void queue_custom (Soup.Message msg, owned Soup.SessionCallback cb) { + public void queue_custom (Soup.Message msg, owned Soup.SessionCallback? cb = null) { session.queue_message (msg, cb); } @@ -133,34 +106,49 @@ public class Tootle.NetManager : GLib.Object { } public void load_avatar (string url, Granite.Widgets.Avatar avatar, int size = 32){ + if (settings.cache) { + image_cache.load_avatar (url, avatar, size); + return; + } + var msg = new Soup.Message("GET", url); msg.finished.connect(() => { - var data = msg.response_body.data; - var stream = new MemoryInputStream.from_data (data); - var pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); - avatar.pixbuf = pixbuf; + var data = msg.response_body.data; + var stream = new MemoryInputStream.from_data (data); + var pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); + avatar.pixbuf = pixbuf; }); Tootle.network.queue (msg); } public void load_image (string url, Gtk.Image image) { + if (settings.cache) { + image_cache.load_image (url, image); + return; + } + var msg = new Soup.Message("GET", url); msg.finished.connect(() => { - var data = msg.response_body.data; - var stream = new MemoryInputStream.from_data (data); - var pixbuf = new Gdk.Pixbuf.from_stream (stream); - image.set_from_pixbuf (pixbuf); + var data = msg.response_body.data; + var stream = new MemoryInputStream.from_data (data); + var pixbuf = new Gdk.Pixbuf.from_stream (stream); + image.set_from_pixbuf (pixbuf); }); Tootle.network.queue (msg); } public void load_scaled_image (string url, Gtk.Image image, int size = 64) { + if (settings.cache) { + image_cache.load_scaled_image (url, image, size); + return; + } + var msg = new Soup.Message("GET", url); msg.finished.connect(() => { - var data = msg.response_body.data; - var stream = new MemoryInputStream.from_data (data); - var pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); - image.set_from_pixbuf (pixbuf); + var data = msg.response_body.data; + var stream = new MemoryInputStream.from_data (data); + var pixbuf = new Gdk.Pixbuf.from_stream_at_scale (stream, size, size, true); + image.set_from_pixbuf (pixbuf); }); Tootle.network.queue (msg); } diff --git a/src/Views/AccountView.vala b/src/Views/AccountView.vala index fe46b79..4813cec 100644 --- a/src/Views/AccountView.vala +++ b/src/Views/AccountView.vala @@ -142,7 +142,7 @@ public class Tootle.AccountView : TimelineView { username.label = "@" + account.acct; note.label = Utils.simplify_html (account.note); button_follow.visible = !account.is_self (); - Tootle.image_cache.load_avatar (account.avatar, avatar, 128); + Tootle.network.load_avatar (account.avatar, avatar, 128); menu_edit.visible = account.is_self (); diff --git a/src/Widgets/AccountsButton.vala b/src/Widgets/AccountsButton.vala index 74f039c..d7a2e34 100644 --- a/src/Widgets/AccountsButton.vala +++ b/src/Widgets/AccountsButton.vala @@ -44,7 +44,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{ } public AccountView (){ - button.clicked.connect (() => Tootle.accounts.remove (id)); + button.clicked.connect (() => accounts.remove (id)); } } @@ -62,15 +62,15 @@ public class Tootle.AccountsButton : Gtk.MenuButton{ item_refresh = new Gtk.ModelButton (); item_refresh.text = _("Refresh"); - item_refresh.clicked.connect (() => Tootle.app.refresh ()); + item_refresh.clicked.connect (() => app.refresh ()); item_favs = new Gtk.ModelButton (); item_favs.text = _("Favorites"); - item_favs.clicked.connect (() => Tootle.window.open_view (new FavoritesView ())); + item_favs.clicked.connect (() => window.open_view (new FavoritesView ())); item_search = new Gtk.ModelButton (); item_search.text = _("Search"); - item_search.clicked.connect (() => Tootle.window.open_view (new SearchView ())); + item_search.clicked.connect (() => window.open_view (new SearchView ())); item_settings = new Gtk.ModelButton (); item_settings.text = _("Settings"); @@ -107,7 +107,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{ if (widget.id == Tootle.settings.current_account) return; else - Tootle.accounts.switch_account (widget.id); + accounts.switch_account (widget.id); }); } @@ -135,7 +135,7 @@ public class Tootle.AccountsButton : Gtk.MenuButton{ if (account == null) avatar.show_default (24); else - image_cache.load_avatar (account.avatar, avatar, 24); + network.load_avatar (account.avatar, avatar, 24); } private void update_selection () { diff --git a/src/Widgets/AttachmentWidget.vala b/src/Widgets/AttachmentWidget.vala index fc1e1b1..3d2873c 100644 --- a/src/Widgets/AttachmentWidget.vala +++ b/src/Widgets/AttachmentWidget.vala @@ -49,9 +49,9 @@ public class Tootle.AttachmentWidget : Gtk.EventBox { image.valign = Gtk.Align.CENTER; image.show (); if (editable) - Tootle.image_cache.load_scaled_image (attachment.preview_url, image); + Tootle.network.load_scaled_image (attachment.preview_url, image); else - Tootle.image_cache.load_image (attachment.preview_url, image); + Tootle.network.load_image (attachment.preview_url, image); grid.attach (image, 0, 0); label.hide (); break; diff --git a/src/Widgets/StatusWidget.vala b/src/Widgets/StatusWidget.vala index f1816e8..23b7993 100644 --- a/src/Widgets/StatusWidget.vala +++ b/src/Widgets/StatusWidget.vala @@ -222,7 +222,7 @@ public class Tootle.StatusWidget : Gtk.EventBox { reblog.tooltip_text = _("This post can't be boosted"); } - Tootle.image_cache.load_avatar (formal.account.avatar, avatar, avatar_size); + Tootle.network.load_avatar (formal.account.avatar, avatar, avatar_size); } public bool is_spoiler () {