tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — 132 miembros, 79 widgets, la galería vuelve y licencia dual
1//! **El protocolo de dormir, del lado del runtime.** El contrato vive en
2//! `llimphi-dormir`; acá se lo cose al bucle: el hilo que escucha a mirada le
3//! pasa el pedido al hilo del modelo por un [`UserEvent::Dormir`], y el arranque
4//! se reconstruye desde el estado guardado si la app fue despertada.
5//!
6//! Todo pasa sólo si la app dice [`App::sabe_dormir`]: una app que no lo dice no
7//! abre socket, y para mirada es una app ajena más.
8
9use crate::{App, Handle};
10
11/// El modelo inicial: [`App::despertar`] si la app fue despertada con un estado
12/// y lo entiende; [`App::init`] en cualquier otro caso.
13pub(crate) fn init_o_despertar<A: App>(handle: &Handle<A::Msg>) -> A::Model {
14 #[cfg(unix)]
15 if A::sabe_dormir() {
16 if let Some(estado) = llimphi_dormir::tomar_estado() {
17 if let Some(modelo) = A::despertar(&estado, handle) {
18 return modelo;
19 }
20 llimphi_hal::avisar!(
21 "llimphi: el estado de despertar ({} bytes) no se pudo leer — arranco de cero",
22 estado.len()
23 );
24 }
25 }
26 A::init(handle)
27}
28
29/// El estado de la app guardado en disco, o `None` si dijo «ahora no» o no se
30/// pudo escribir (en cuyo caso tampoco se duerme: sin estado guardado, irse
31/// sería perder lo abierto).
32pub(crate) fn dormir_modelo<A: App>(model: &mut A::Model) -> Option<std::path::PathBuf> {
33 #[cfg(unix)]
34 {
35 let estado = A::dormir(model)?;
36 let nombre = A::app_id().unwrap_or(A::title());
37 match llimphi_dormir::guardar_estado(nombre, &estado) {
38 Ok(ruta) => Some(ruta),
39 Err(e) => {
40 llimphi_hal::avisar!("llimphi: no pude guardar el estado para dormir: {e}");
41 None
42 }
43 }
44 }
45 #[cfg(not(unix))]
46 {
47 let _ = model;
48 None
49 }
50}
51
52#[cfg(unix)]
53static ESCUCHA: std::sync::Mutex<Option<llimphi_dormir::Escucha>> = std::sync::Mutex::new(None);
54
55/// Abre el socket del protocolo si la app sabe dormir. El pedido de mirada se
56/// lleva al hilo del modelo y se espera la respuesta hasta 10 s; si el bucle no
57/// contesta en ese plazo, la respuesta es «ahora no».
58pub(crate) fn escuchar<A: App>(handle: &Handle<A::Msg>) {
59 #[cfg(unix)]
60 {
61 if !A::sabe_dormir() {
62 return;
63 }
64 let crate::HandleInner::Real(proxy) = &handle.inner else {
65 return;
66 };
67 let proxy = proxy.clone();
68 let escucha = llimphi_dormir::escuchar(move || {
69 let (tx, rx) = std::sync::mpsc::channel();
70 proxy.send_event(crate::UserEvent::Dormir(tx)).ok()?;
71 rx.recv_timeout(std::time::Duration::from_secs(10)).ok().flatten()
72 });
73 match escucha {
74 Ok(e) => *ESCUCHA.lock().unwrap_or_else(|p| p.into_inner()) = Some(e),
75 Err(e) => llimphi_hal::avisar!("llimphi: no pude abrir el socket de dormir: {e}"),
76 }
77 }
78 #[cfg(not(unix))]
79 let _ = handle;
80}
81
82/// Cierra el socket (el bucle se va).
83pub(crate) fn soltar() {
84 #[cfg(unix)]
85 drop(ESCUCHA.lock().unwrap_or_else(|p| p.into_inner()).take());
86}