feat: image loading on different thread
Some checks failed
CI / Rustfmt (push) Waiting to run
CI / Flatpak (push) Failing after 5m3s

This commit is contained in:
Tine Jozelj 2024-01-11 19:02:35 +01:00
parent 83c478cfd2
commit b63e6a8892
Signed by: mentos1386
SSH key fingerprint: SHA256:MNtTsLbihYaWF8j1fkOHfkKNlnN1JQfxEU/rBU8nCGw
9 changed files with 191 additions and 23 deletions

97
Cargo.lock generated
View file

@ -362,6 +362,21 @@ dependencies = [
"slab",
]
[[package]]
name = "gdk"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646"
dependencies = [
"cairo-rs",
"gdk-pixbuf",
"gdk-sys",
"gio",
"glib",
"libc",
"pango",
]
[[package]]
name = "gdk-pixbuf"
version = "0.18.5"
@ -388,6 +403,23 @@ dependencies = [
"system-deps",
]
[[package]]
name = "gdk-sys"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"pango-sys",
"pkg-config",
"system-deps",
]
[[package]]
name = "gdk4"
version = "0.7.3"
@ -878,6 +910,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.20"
@ -940,13 +982,16 @@ version = "0.1.0"
dependencies = [
"bytes",
"futures",
"gdk",
"gdk-pixbuf",
"gettext-rs",
"glib",
"gsettings-macro",
"gtk4",
"libadwaita",
"reqwest",
"serde",
"tokio",
"tracing",
"tracing-subscriber",
]
@ -1108,6 +1153,29 @@ dependencies = [
"system-deps",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.48.5",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -1323,6 +1391,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.9.2"
@ -1413,6 +1487,15 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.9"
@ -1581,11 +1664,25 @@ dependencies = [
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.48.0",
]
[[package]]
name = "tokio-macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"

View file

@ -18,6 +18,9 @@ gsettings-macro = "0.1.20"
gdk-pixbuf = "0.18.5"
bytes = "1.5.0"
futures = "0.3.30"
tokio = { version = "1.35.1", features = ["full"] }
glib = "0.18.5"
gdk = "0.18.0"
[dependencies.adw]
package = "libadwaita"

View file

@ -1,4 +1,4 @@
.card image {
min-width: 500px;
min-height: 500px;
min-width: 200px;
min-height: 200px;
}

View file

@ -5,7 +5,9 @@ template $Card: Box {
orientation: horizontal;
styles ["card"]
Image image {}
Image image {
icon-name: "image-loading";
}
Box contents {
orientation: vertical;

View file

@ -0,0 +1,54 @@
use gdk_pixbuf::traits::PixbufLoaderExt;
use gdk_pixbuf::{Pixbuf, PixbufLoader};
use std::io::{Error, ErrorKind, Write};
use std::thread;
use tokio::sync::oneshot;
use crate::api::*;
// A wrapper to be able to implement the Write trait on a PixbufLoader
struct LocalPixbufLoader<'a>(&'a PixbufLoader);
impl<'a> Write for LocalPixbufLoader<'a> {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
self.0
.write(buf)
.map_err(|e| Error::new(ErrorKind::Other, format!("glib error: {e}")))?;
Ok(buf.len())
}
fn flush(&mut self) -> Result<(), Error> {
self.0
.close()
.map_err(|e| Error::new(ErrorKind::Other, format!("glib error: {e}")))?;
Ok(())
}
}
pub struct ImageLoader {}
impl ImageLoader {
pub fn new() -> Self {
Self {}
}
pub async fn from_url(&self, url: String) -> Option<Pixbuf> {
let (sender, receiver) = oneshot::channel();
let pixbuf_loader = PixbufLoader::new();
let mut loader = LocalPixbufLoader(&pixbuf_loader);
thread::spawn(move || {
let client = base::get_curse_forge_client().unwrap();
let response = client.get(url).send();
let bytes = response.unwrap().bytes().ok().unwrap();
sender.send(bytes).unwrap();
});
let bytes = receiver.await.ok()?;
loader.write_all(&bytes).ok()?;
pixbuf_loader.close().ok()?;
pixbuf_loader.pixbuf()
}
}

View file

@ -22,3 +22,17 @@ pub fn get_curse_forge_client() -> Result<reqwest::blocking::Client, reqwest::Er
.default_headers(headers)
.build()
}
pub fn get_async_curse_forge_client() -> Result<reqwest::Client, reqwest::Error> {
let mut api_key_header =
reqwest::header::HeaderValue::from_str(crate::config::API_KEY).unwrap();
api_key_header.set_sensitive(true);
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("x-api-key", api_key_header);
reqwest::Client::builder()
.user_agent(format!("dev.mnts.ModManager/{}", crate::config::VERSION))
.default_headers(headers)
.build()
}

View file

@ -1,10 +1,8 @@
// Inspired by https://github.com/xou816/spot/blob/aa1c57842c3f4e424eb62c69365438f904b2dc63/src/app/loader.rs
// Without the caching.
//
use bytes::Bytes;
use gdk_pixbuf::traits::PixbufLoaderExt;
use gdk_pixbuf::{Pixbuf, PixbufLoader};
use std::io::{Error, ErrorKind, Write};
use std::thread;
use tokio::sync::oneshot;
use crate::api::*;
@ -34,20 +32,21 @@ impl ImageLoader {
Self {}
}
fn get_image(url: &str) -> Option<Bytes> {
let client = base::get_curse_forge_client().unwrap();
let response = client.get(url).send().ok();
response.unwrap().bytes().ok()
}
pub async fn from_url(&self, url: String) -> Option<Pixbuf> {
let (sender, receiver) = oneshot::channel();
pub fn load_remote(&self, url: &str, width: i32, height: i32) -> Option<Pixbuf> {
let pixbuf_loader = PixbufLoader::new();
pixbuf_loader.set_size(width, height);
let mut loader = LocalPixbufLoader(&pixbuf_loader);
if let Some(mut resp) = Self::get_image(url) {
loader.write_all(&resp).ok()?;
}
thread::spawn(move || {
let client = base::get_curse_forge_client().unwrap();
let response = client.get(url).send();
let bytes = response.unwrap().bytes().ok().unwrap();
sender.send(bytes).unwrap();
});
let bytes = receiver.await.ok()?;
loader.write_all(&bytes).ok()?;
pixbuf_loader.close().ok()?;
pixbuf_loader.pixbuf()

View file

@ -1,5 +1,4 @@
use adw::subclass::prelude::*;
use glib::clone;
use gtk::CompositeTemplate;
use gtk::{gio, glib};
@ -64,10 +63,11 @@ impl Card {
let url = image_url.to_string();
worker.send_local_task(clone!(@weak widget => async move {
let image = widget.image.clone();
worker.send_local_task(async move {
let loader = ImageLoader::new();
let pixbuf = loader.load_remote(url.as_str(), 800, 800);
widget.image.set_from_pixbuf(pixbuf.as_ref());
}));
let pixbuf = loader.from_url(url).await;
image.set_from_pixbuf(pixbuf.as_ref());
});
}
}

View file

@ -24,7 +24,6 @@ mod settings;
mod windows;
use self::application::ModManagerApplication;
use self::dispatch::spawn_task_handler;
use config::{GETTEXT_PACKAGE, LOCALEDIR, RESOURCES_FILE};
use gettextrs::{gettext, LocaleCategory};