use super::{
live_compute_target_from_index, remote_samples_exponent_to_count,
worker_settings::from_worker_item,
};
use crate::{
MainWindow, RemoteWorkerModel, SettingsModel, WorkerItem,
bridge::{remote::remote_render, render_thread::RenderContext},
gui::{remote::refresh_worker_options, show_toast},
settings::{SettingsPersister, WorkerSettings},
};
use slint::ComponentHandle;
use std::sync::{Arc, Mutex, PoisonError};
pub fn setup_worker_callbacks(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
settings_store: &Arc<SettingsPersister>,
) {
setup_save_worker_callback(ui, settings_store);
setup_remove_worker_callback(ui, settings_store);
setup_denoise_toggle_callback(ui, render_ctx, settings_store);
setup_live_compute_target_callback(ui, render_ctx, settings_store);
setup_remote_render_samples_callback(ui, render_ctx, settings_store);
setup_claim_token_callback(ui);
setup_test_worker_connection_callback(ui);
setup_cert_dir_picker_callback(ui);
}
fn setup_save_worker_callback(ui: &MainWindow, settings_store: &Arc<SettingsPersister>) {
let settings_store_save = settings_store.clone();
let ui_weak_save = ui.as_weak();
ui.global::<RemoteWorkerModel>()
.on_save_worker(move |idx: i32, item: WorkerItem| {
let Some(ui) = ui_weak_save.upgrade() else {
return;
};
let worker = from_worker_item(&item);
let idx = idx as usize;
let mut result = Ok(());
settings_store_save.update(|s| {
result = if idx < s.settings.remote_workers.len() {
s.settings.update_worker(idx, worker.clone())
} else {
s.settings.add_worker(worker.clone());
Ok(())
};
});
match result {
Ok(()) => {
refresh_worker_options(
&ui,
&settings_store_save.snapshot().settings.remote_workers,
);
show_toast(&ui, "Remote worker saved.", "success");
}
Err(err) => show_toast(&ui, &err, "error"),
}
});
}
fn setup_remove_worker_callback(ui: &MainWindow, settings_store: &Arc<SettingsPersister>) {
let settings_store_remove = settings_store.clone();
let ui_weak_remove = ui.as_weak();
ui.global::<RemoteWorkerModel>()
.on_remove_worker(move |idx: i32| {
let Some(ui) = ui_weak_remove.upgrade() else {
return;
};
let mut result = Ok(());
settings_store_remove.update(|s| result = s.settings.remove_worker(idx as usize));
match result {
Ok(()) => {
refresh_worker_options(
&ui,
&settings_store_remove.snapshot().settings.remote_workers,
);
show_toast(&ui, "Remote worker removed.", "info");
}
Err(err) => show_toast(&ui, &err, "error"),
}
});
}
fn setup_denoise_toggle_callback(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
settings_store: &Arc<SettingsPersister>,
) {
let settings_store_denoise = settings_store.clone();
let render_ctx_denoise = render_ctx.clone();
ui.global::<RemoteWorkerModel>()
.on_denoise_toggled(move |enabled: bool| {
render_ctx_denoise
.lock()
.unwrap_or_else(PoisonError::into_inner)
.denoise_enabled = enabled;
settings_store_denoise.update(|s| s.settings.denoise_enabled = enabled);
});
}
fn setup_test_worker_connection_callback(ui: &MainWindow) {
let ui_weak_test = ui.as_weak();
ui.global::<RemoteWorkerModel>().on_test_worker_connection(
move |idx: i32, address: slint::SharedString, cert_dir: slint::SharedString| {
let Some(ui) = ui_weak_test.upgrade() else {
return;
};
ui.global::<RemoteWorkerModel>()
.set_testing_worker_index(idx);
let worker = WorkerSettings {
address: address.to_string(),
cert_dir: cert_dir.to_string(),
..WorkerSettings::default()
};
let ui_weak_result = ui.as_weak();
std::thread::spawn(move || {
let result = remote_render::test_connection(&worker);
let _ = ui_weak_result.upgrade_in_event_loop(move |ui| {
ui.global::<RemoteWorkerModel>()
.set_testing_worker_index(-1);
ui.global::<RemoteWorkerModel>()
.set_test_connection_result_index(idx);
match result {
Ok(info) => {
ui.global::<RemoteWorkerModel>()
.set_test_connection_is_error(false);
ui.global::<RemoteWorkerModel>().set_test_connection_result(
format!(
"Compatible -- {} (protocol v{})",
backend_label(info.render.as_ref()),
info.protocol_version
)
.into(),
);
}
Err(err) => {
ui.global::<RemoteWorkerModel>()
.set_test_connection_is_error(true);
ui.global::<RemoteWorkerModel>()
.set_test_connection_result(err.to_string().into());
}
}
});
});
},
);
}
fn setup_cert_dir_picker_callback(ui: &MainWindow) {
ui.global::<RemoteWorkerModel>()
.on_pick_cert_dir(|current: slint::SharedString| {
let mut dialog = rfd::FileDialog::new();
if let Some(dir) = crate::gui::starting_dir_from_picker_field(current.as_str()) {
dialog = dialog.set_directory(dir);
}
dialog
.pick_folder()
.map_or(current, |path| path.display().to_string().into())
});
}
fn setup_live_compute_target_callback(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
settings_store: &Arc<SettingsPersister>,
) {
let settings_store_compute = settings_store.clone();
let render_ctx_compute = render_ctx.clone();
ui.global::<SettingsModel>()
.on_live_compute_target_changed(move |index: i32| {
let target = live_compute_target_from_index(index);
render_ctx_compute
.lock()
.unwrap_or_else(PoisonError::into_inner)
.live_compute_target = target;
settings_store_compute.update(|s| s.settings.live_compute_target = target);
});
}
fn setup_remote_render_samples_callback(
ui: &MainWindow,
render_ctx: &Arc<Mutex<RenderContext>>,
settings_store: &Arc<SettingsPersister>,
) {
let render_ctx_remote_samples = render_ctx.clone();
let settings_store_remote_samples = settings_store.clone();
ui.global::<RemoteWorkerModel>()
.on_render_samples_changed(move |exponent: i32| {
let samples = remote_samples_exponent_to_count(u32::try_from(exponent).unwrap_or(0));
render_ctx_remote_samples
.lock()
.unwrap_or_else(PoisonError::into_inner)
.remote_render_samples = samples;
settings_store_remote_samples.update(|s| s.settings.remote_render_samples = samples);
});
}
fn setup_claim_token_callback(ui: &MainWindow) {
let ui_weak = ui.as_weak();
ui.global::<RemoteWorkerModel>().on_claim_token(
move |index: i32,
worker_name: slint::SharedString,
enroll_addr: slint::SharedString,
token: slint::SharedString| {
let Some(ui) = ui_weak.upgrade() else {
return;
};
ui.global::<RemoteWorkerModel>().set_claiming_token(true);
ui.global::<RemoteWorkerModel>()
.set_claim_result_index(index);
ui.global::<RemoteWorkerModel>()
.set_claim_result_text("".into());
ui.global::<RemoteWorkerModel>()
.set_claimed_cert_dir("".into());
let settings_dir = crate::settings::store::default_settings_path()
.parent()
.map(std::path::Path::to_path_buf)
.unwrap_or_default();
let worker_name = worker_name.to_string();
let enroll_addr = enroll_addr.to_string();
let token = token.to_string();
let ui_weak_result = ui.as_weak();
std::thread::spawn(move || {
let bundle_dir =
crate::bridge::remote::enroll::bundle_dir_for(&settings_dir, &worker_name);
let result = crate::bridge::remote::enroll::claim_and_write_bundle(
&token,
&enroll_addr,
&bundle_dir,
);
let _ = ui_weak_result.upgrade_in_event_loop(move |ui| {
ui.global::<RemoteWorkerModel>().set_claiming_token(false);
match result {
Ok(dir) => {
ui.global::<RemoteWorkerModel>()
.set_claim_result_is_error(false);
ui.global::<RemoteWorkerModel>().set_claim_result_text(
"Token redeemed -- certificate folder filled in.".into(),
);
ui.global::<RemoteWorkerModel>()
.set_claimed_cert_dir(dir.display().to_string().into());
}
Err(message) => {
ui.global::<RemoteWorkerModel>()
.set_claim_result_is_error(true);
ui.global::<RemoteWorkerModel>()
.set_claim_result_text(message.into());
}
}
});
});
},
);
}
pub(super) fn backend_label(render: Option<&indicatrix_net::messages::RenderCapability>) -> String {
match render.map(|r| &r.backend) {
Some(indicatrix_net::messages::Backend::Cpu { threads }) => {
format!("CPU, {threads} threads")
}
Some(indicatrix_net::messages::Backend::Gpu { adapter }) => format!("GPU ({adapter})"),
None => "library only (no render capacity)".to_string(),
}
}