dash-frontend: remove partially downloaded files, cancel download on Downloader view exit

This commit is contained in:
Aleksander 2026-07-29 21:53:00 +02:00 committed by galister
parent 0b4c59f1c7
commit 707e3a4927
10 changed files with 58 additions and 18 deletions

View File

@ -98,6 +98,7 @@ pub struct InitParams<'a, T> {
pub has_monado: bool,
pub theme: Rc<WguiTheme>,
pub color_palette: &'a str,
pub executor: AsyncExecutor,
}
#[derive(Clone)]
@ -202,7 +203,7 @@ impl<T: 'static> Frontend<T> {
toast_manager,
window_audio_settings: WguiWindow::default(),
view_audio_settings: None,
executor: Rc::new(smol::LocalExecutor::new()),
executor: params.executor,
sounds_to_play: Vec::new(),
};
@ -254,9 +255,6 @@ impl<T: 'static> Frontend<T> {
self.current_tab = Some(tab);
}
// process async runtime tasks
while self.executor.try_tick() {}
let res = self.tick(params)?;
self.ticks += 1;

View File

@ -117,6 +117,7 @@ async fn start_download(url: &str, allow_missing_content_length: bool) -> anyhow
std::thread::spawn(move || {
let res = thread_http_client(&url, allow_missing_content_length, sender.clone());
let _ = sender.send_blocking(HttpClientData::Ended(res));
log::debug!("thread_http_client exiting");
});
let file_size = match receiver.recv().await? {
@ -177,6 +178,20 @@ pub async fn get(mut params: GetParams<'_>) -> anyhow::Result<HttpClientResponse
Ok(HttpClientResponse { data })
}
struct FileCancelGuard {
path: std::path::PathBuf,
should_delete: bool,
}
impl Drop for FileCancelGuard {
fn drop(&mut self) {
if self.should_delete {
log::warn!("Removing partially downloaded file {:?}", self.path);
let _ = std::fs::remove_file(&self.path);
}
}
}
/// Downloads a response directly to a file.
///
/// Unlike `get`, this permits responses without a Content-Length header. In
@ -189,9 +204,14 @@ pub async fn download_to_file(mut params: GetParams<'_>, path: impl AsRef<Path>)
let DownloadStream { file_size, receiver } = start_download(params.url, true).await?;
let mut file_cancel_guard = FileCancelGuard {
path: path.clone(),
should_delete: true,
};
let mut file = smol::fs::File::create(&path)
.await
.with_context(|| format!("failed to create download file {:?}", path,))?;
.with_context(|| format!("failed to create download file {:?}", path))?;
let mut bytes_downloaded = 0_u64;
@ -234,6 +254,8 @@ pub async fn download_to_file(mut params: GetParams<'_>, path: impl AsRef<Path>)
);
}
file_cancel_guard.should_delete = false; // we're good!
Ok(())
}

View File

@ -51,6 +51,9 @@ pub struct View {
id_content: WidgetID,
on_close_request: Option<Box<dyn FnOnce()>>,
on_downloaded: Option<Box<dyn FnOnce()>>,
// will cancel on Drop
task_downloader: Option<smol::Task<Option<()>>>,
}
fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> {
@ -67,10 +70,12 @@ impl ViewTrait for View {
match task {
Task::StartDownload(url, path) => {
if let Some(on_downloaded) = self.on_downloaded.take() {
self
.executor
.spawn(View::download(self.tasks.clone(), url, path, on_downloaded))
.detach();
self.task_downloader = Some(self.executor.spawn(View::download(
self.tasks.clone(),
url,
path,
on_downloaded,
)));
}
}
Task::SetStatusText(text) => {
@ -178,6 +183,7 @@ impl View {
id_content,
on_close_request: Some(on_close_request),
on_downloaded: Some(par.on_downloaded),
task_downloader: None,
})
}
@ -208,7 +214,7 @@ impl View {
on_progress: Some(Box::new({
let tasks = tasks.clone();
move |data: ProgressFuncData| {
if tasks.len() < 50 {
if tasks.len() < 100 {
tasks.push(Task::SetStatusText(format!(
"{}/{} MiB ({}%)",
data.bytes_downloaded / 1024 / 1024,

View File

@ -3,9 +3,13 @@ use std::rc::Rc;
use crate::testbed::{Testbed, TestbedUpdateParams};
use dash_frontend::frontend::{self, FrontendUpdateParams};
use wgui::{layout::Layout, theme::WguiTheme};
use wlx_common::{dash_interface_emulated::DashInterfaceEmulated, locale::WayVRLangProvider};
use wlx_common::{
async_executor::AsyncExecutor, dash_interface_emulated::DashInterfaceEmulated,
locale::WayVRLangProvider,
};
pub struct TestbedDashboard {
executor: AsyncExecutor,
frontend: frontend::Frontend<()>,
}
@ -14,6 +18,7 @@ impl TestbedDashboard {
let interface = DashInterfaceEmulated::new();
let lang_provider = WayVRLangProvider::default();
let palette_name = std::env::var("PALETTE").unwrap_or_else(|_| "Default".to_string());
let executor = wlx_common::async_executor::create_local();
let frontend = frontend::Frontend::new(frontend::InitParams {
interface: Box::new(interface),
@ -22,8 +27,9 @@ impl TestbedDashboard {
lang_provider: &lang_provider,
theme: Rc::new(WguiTheme::default()),
color_palette: &palette_name,
executor: executor.clone(),
})?;
Ok(Self { frontend })
Ok(Self { frontend, executor })
}
}
@ -38,6 +44,8 @@ impl Testbed for TestbedDashboard {
self
.frontend
.process_update(res, params.audio_system, params.audio_sample_player)?;
while self.executor.try_tick() {}
Ok(())
}

View File

@ -1,6 +0,0 @@
#!/bin/bash
cargo run --profile=plain -- --show --replace

View File

@ -247,6 +247,7 @@ pub fn openvr_run(args: &Args) -> Result<(), BackendError> {
let universe = playspace.get_universe();
while app.executor.try_tick() {}
app.input_state.pre_update();
input_source.update(
universe.clone(),

View File

@ -267,6 +267,7 @@ pub fn openxr_run(args: &Args) -> Result<(), BackendError> {
continue 'main_loop;
}
while app.executor.try_tick() {}
app.input_state.pre_update();
input_source.update(&xr_state, &mut app)?;
app.input_state.post_update(&app.session);

View File

@ -95,6 +95,7 @@ impl DashFrontend {
has_monado: matches!(app.xr_backend, XrBackend::OpenXR),
theme: app.wgui_theme.clone(),
color_palette: &*app.session.config.color_palette,
executor: app.executor.clone(),
})?;
frontend

View File

@ -58,6 +58,8 @@ pub struct AppState {
pub wgui_shared: WSharedContext,
pub executor: wlx_common::async_executor::AsyncExecutor,
pub input_state: InputState,
pub screens: SmallVec<[ScreenMeta; 8]>,
pub anchor: Affine3A,
@ -189,6 +191,8 @@ impl AppState {
)
.ok();
let executor = wlx_common::async_executor::create_local();
let mut app_state = Self {
tasks,
gfx,
@ -209,6 +213,7 @@ impl AppState {
load_palette(&*session.config.color_palette),
)?,
wgui_theme: Rc::new(theme),
executor,
dbus,
xr_backend,
ipc_server,

View File

@ -1,3 +1,7 @@
use std::rc::Rc;
pub type AsyncExecutor = Rc<smol::LocalExecutor<'static>>;
pub fn create_local() -> AsyncExecutor {
Rc::new(smol::LocalExecutor::new())
}