this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: offload clipboard read + PNG encode to OS thread on paste

arboard is not Send on Linux so spawn_blocking is unavailable; use
std::thread + tokio oneshot to keep the async runtime unblocked during
the slow X11 selection transfer and image encode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

+26 -21
+26 -21
crates/tala/src/editor.rs
··· 316 316 format!("Pasted-image-{y:04}{m:02}{d:02}-{h:02}{min:02}{s:02}.png") 317 317 } 318 318 319 - fn handle_pasted_image( 320 - mut source: Signal<String>, 321 - active_idx: Signal<usize>, 322 - mut save_status: Signal<SaveStatus>, 323 - ) { 324 - // Read image from OS clipboard (bypasses webkit2gtk's empty clipboardData). 325 - let img_data = match arboard::Clipboard::new().and_then(|mut c| c.get_image()) { 326 - Ok(d) => d, 327 - Err(_) => return, // no image in clipboard 328 - }; 329 - 330 - // Encode RGBA pixels to PNG. 319 + /// Reads an image from the OS clipboard and encodes it as PNG bytes. 320 + /// Intended to run on a dedicated OS thread (arboard is not Send on Linux). 321 + fn read_clipboard_png() -> Option<(Vec<u8>, String)> { 322 + let img_data = arboard::Clipboard::new().and_then(|mut c| c.get_image()).ok()?; 331 323 let rgba = image::RgbaImage::from_raw( 332 324 img_data.width as u32, 333 325 img_data.height as u32, 334 326 img_data.bytes.into_owned(), 335 - ); 336 - let Some(rgba) = rgba else { return }; 327 + )?; 337 328 let mut png_bytes: Vec<u8> = Vec::new(); 338 - if image::DynamicImage::ImageRgba8(rgba) 329 + image::DynamicImage::ImageRgba8(rgba) 339 330 .write_to(&mut std::io::Cursor::new(&mut png_bytes), image::ImageFormat::Png) 340 - .is_err() 341 - { 342 - return; 343 - } 331 + .ok()?; 332 + Some((png_bytes, paste_image_filename())) 333 + } 344 334 345 - let filename = paste_image_filename(); 335 + fn apply_pasted_image( 336 + mut source: Signal<String>, 337 + active_idx: Signal<usize>, 338 + mut save_status: Signal<SaveStatus>, 339 + png_bytes: Vec<u8>, 340 + filename: String, 341 + ) { 346 342 let stem = filename.trim_end_matches(".png").to_string(); 347 343 if std::fs::write(card_dir().join("images").join(&filename), &png_bytes).is_err() { 348 344 return; ··· 1035 1031 "#); 1036 1032 loop { 1037 1033 match eval.recv::<i32>().await { 1038 - Ok(_) => handle_pasted_image(source, active_idx, save_status), 1034 + Ok(_) => { 1035 + // Offload the blocking clipboard read + PNG encode to an OS 1036 + // thread (arboard is not Send on Linux, so spawn_blocking won't 1037 + // work). A oneshot channel bridges the result back. 1038 + let (tx, rx) = tokio::sync::oneshot::channel::<Option<(Vec<u8>, String)>>(); 1039 + std::thread::spawn(move || { let _ = tx.send(read_clipboard_png()); }); 1040 + if let Ok(Some((png_bytes, filename))) = rx.await { 1041 + apply_pasted_image(source, active_idx, save_status, png_bytes, filename); 1042 + } 1043 + } 1039 1044 Err(_) => break, 1040 1045 } 1041 1046 }