feat(renaser): Fases 8b y 8c — el escritorio interactivo
El compositor de la 8a teselaba, pero era inmovil. Las 8b/8c lo hacen vivo: el teclado reordena el escritorio y mueve el foco en caliente. - Cache de fotogramas: cada ventana guarda en RAM del kernel su ultimo fotograma —reservada una vez, acotada al lienzo natural—. Al re-teselar o mover el foco, el kernel recompone desde la cache: las apps que solo pintan en init (cronista) conservan su imagen sin enterarse del cambio. - compositor: el registro ESCRITORIO (ventanas, marcos, caches, modo); presentar_fotograma, desalojar, atender_mandos, ciclar_layout, mover_foco. Foco en un AtomicUsize, mandos en una cola lock-free. - teclado: la IRQ1 deja de difundir. Alt es el modificador del sistema — Alt+Espacio cicla el teselado, Alt+J/K mueven el foco—; una tecla ordinaria va SOLO a la app enfocada (CANALES reindexado por indice_app). - consola: borde de foco (indigo / gris) en cada marco. Guardarrail anti-interbloqueo: la IRQ1 jamas bloquea ESCRITORIO; se comunica por dos atomicos y una cola lock-free. Las caches se reservan una sola vez, al tamaño natural — sin asignacion en el bucle del reactor. Verificado en QEMU (screendump + sendkey): arranque teselado con hola enfocada; Alt+Espacio cicla a CenteredMaster y las apps estaticas conservan su contenido; Alt+J mueve el foco; las teclas llegan solo a la app enfocada. Cierra la Fase 8 — el compositor teselante e interactivo. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -29,18 +29,15 @@ use wasmi::{Caller, Error, Extern, Linker, Memory, StoreLimits};
|
||||
|
||||
use crate::almacen::Hash;
|
||||
use crate::async_system::teclado::CanalTeclado;
|
||||
use crate::grafico::RegionPantalla;
|
||||
|
||||
/// El estado del host adscrito al `Store` de una aplicacion: cuanto necesita
|
||||
/// una capacidad para servir a ESA app y a ninguna otra — su region de pantalla,
|
||||
/// su canal de teclado y sus cuotas de recursos. Dos apps jamas comparten nada.
|
||||
pub(crate) struct ContextoCapacidades {
|
||||
/// El marco que el compositor (Fase 8) asigno a la app — el rectangulo de
|
||||
/// pantalla donde vive. El kernel centra en el el fotograma de la app.
|
||||
pub(crate) marco: RegionPantalla,
|
||||
/// El tamaño natural del lienzo de la app, en pixeles. El fotograma que
|
||||
/// entrega `sys_render_frame` mide exactamente `natural_ancho × natural_alto`;
|
||||
/// el compositor lo coloca, sin deformarlo, dentro del `marco`.
|
||||
/// el compositor lo cachea y lo compone, sin deformarlo, en el marco que el
|
||||
/// teselado le asigno.
|
||||
pub(crate) natural_ancho: usize,
|
||||
pub(crate) natural_alto: usize,
|
||||
/// El canal de teclado propio de la aplicacion.
|
||||
@@ -48,9 +45,9 @@ pub(crate) struct ContextoCapacidades {
|
||||
/// El techo de recursos de la aplicacion — hoy, su memoria lineal maxima.
|
||||
/// `wasmi` lo consulta en cada `memory.grow` via `Store::limiter`.
|
||||
pub(crate) limites: StoreLimits,
|
||||
/// El indice de esta app en el Manifiesto de Genesis — su identidad. Las
|
||||
/// capacidades de estado (Fase 7c) lo usan para hallar la `EntradaApp`
|
||||
/// correcta: cada app persiste en SU ranura, jamas en la de otra.
|
||||
/// El indice de esta app — su identidad. La usan las capacidades de estado
|
||||
/// (Fase 7c) para hallar su `EntradaApp` del manifiesto, y el compositor
|
||||
/// (Fase 8) para hallar su ventana en el escritorio: jamas la de otra.
|
||||
pub(crate) indice_app: usize,
|
||||
}
|
||||
|
||||
@@ -98,7 +95,7 @@ pub(crate) fn enlazar_capacidades(
|
||||
"renaser",
|
||||
"sys_render_frame",
|
||||
|caller: Caller<'_, ContextoCapacidades>, ptr: u32, len: u32| -> Result<(), Error> {
|
||||
let marco = caller.data().marco;
|
||||
let indice = caller.data().indice_app;
|
||||
let nat_ancho = caller.data().natural_ancho;
|
||||
let nat_alto = caller.data().natural_alto;
|
||||
|
||||
@@ -124,9 +121,10 @@ pub(crate) fn enlazar_capacidades(
|
||||
"WASM :: sys_render_frame desbordo la memoria lineal del modulo",
|
||||
)?;
|
||||
|
||||
// Limites verificados: el compositor centra el fotograma natural
|
||||
// de la app dentro del marco que el teselado le asigno.
|
||||
crate::volcar_marco_wasm(marco, nat_ancho, nat_alto, fotograma);
|
||||
// Limites verificados: el compositor cachea el fotograma —para
|
||||
// poder recomponerlo si el escritorio se re-tesela— y lo compone,
|
||||
// centrado, en el marco que el teselado asigno a esta app.
|
||||
crate::compositor::presentar_fotograma(indice, fotograma);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -20,7 +20,7 @@ use wasmi::{
|
||||
CompilationMode, Config, Engine, Linker, Module, Store, StoreLimitsBuilder, TrapCode, TypedFunc,
|
||||
};
|
||||
|
||||
use crate::grafico::{Color, RegionPantalla};
|
||||
use crate::grafico::Color;
|
||||
use env::ContextoCapacidades;
|
||||
|
||||
/// Combustible concedido a `init`. Cubre con holgura el pintado inicial del
|
||||
@@ -64,34 +64,28 @@ impl FallaApp {
|
||||
/// aqui la instancia se conserva y el kernel la hace avanzar `tick` a `tick`.
|
||||
pub struct AplicacionWasm {
|
||||
/// El almacen: todo el estado de ESTA instancia — su memoria lineal, sus
|
||||
/// globales y el contexto de capacidades con su region de pantalla.
|
||||
/// globales y el contexto de capacidades con su identidad e indice.
|
||||
almacen: Store<ContextoCapacidades>,
|
||||
/// El punto de entrada de fotograma, ya resuelto y con seguridad de tipos.
|
||||
/// `TypedFunc` es un asa autosuficiente dentro del `Store`: conservada esta,
|
||||
/// el handle de la `Instance` no aporta nada y no se retiene.
|
||||
func_tick: TypedFunc<(), ()>,
|
||||
/// El marco que el compositor asigno a la app — su ventana en pantalla, y
|
||||
/// donde se tatua su baliza de desalojo si llega a fallar.
|
||||
marco: RegionPantalla,
|
||||
}
|
||||
|
||||
impl AplicacionWasm {
|
||||
/// Carga, valida, instancia y arranca una aplicacion WASM aislada, ligada a
|
||||
/// una region de pantalla. Si algo falla en el camino, se devuelve la falla
|
||||
/// en lugar de incendiar el kernel.
|
||||
/// Carga, valida, instancia y arranca una aplicacion WASM aislada. Si algo
|
||||
/// falla en el camino, se devuelve la falla en lugar de incendiar el kernel.
|
||||
///
|
||||
/// El nuevo ABI del userspace exige dos exportaciones: `init` —invocada una
|
||||
/// sola vez, aqui— y `tick` —un fotograma de trabajo, invocada despues por
|
||||
/// el reactor en cada pulso del reloj.
|
||||
/// El ABI del userspace exige dos exportaciones: `init` —invocada una sola
|
||||
/// vez, aqui— y `tick` —un fotograma de trabajo, invocada despues por el
|
||||
/// reactor en cada pulso del reloj.
|
||||
///
|
||||
/// `marco` es el rectangulo que el compositor (Fase 8) asigno a la app;
|
||||
/// `natural_ancho`/`natural_alto`, el tamaño de su lienzo. `techo_memoria`
|
||||
/// es su cuota de memoria lineal —la dicta su `EntradaApp` del manifiesto—,
|
||||
/// e `indice_app` su posicion en el: su identidad para las capacidades de
|
||||
/// estado persistido (Fase 7c).
|
||||
/// `natural_ancho`/`natural_alto` son el tamaño del lienzo de la app;
|
||||
/// `techo_memoria`, su cuota de memoria lineal —la dicta su `EntradaApp` del
|
||||
/// manifiesto—; e `indice_app`, su identidad: la posicion con que el
|
||||
/// compositor halla su ventana y las capacidades de estado su ranura.
|
||||
pub fn cargar(
|
||||
bytecode: &[u8],
|
||||
marco: RegionPantalla,
|
||||
natural_ancho: usize,
|
||||
natural_alto: usize,
|
||||
techo_memoria: usize,
|
||||
@@ -108,10 +102,10 @@ impl AplicacionWasm {
|
||||
// 2. Validar y traducir el modulo — ya instrumentado con fuel.
|
||||
let modulo = Module::new(&motor, bytecode).map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 3. El almacen, con el contexto de capacidades de ESTA app: su region
|
||||
// de pantalla, su canal de teclado y su techo de memoria. El canal
|
||||
// se crea ahora pero se inscribe en la difusion de la IRQ1 al final,
|
||||
// ya con la app cargada: una carga fallida no deja canales huerfanos.
|
||||
// 3. El almacen, con el contexto de capacidades de ESTA app: su lienzo
|
||||
// natural, su canal de teclado y su techo de memoria. El canal se
|
||||
// crea ahora pero se inscribe en el censo de la IRQ1 al final, ya con
|
||||
// la app cargada: una carga fallida no deja canales huerfanos.
|
||||
let canal = crate::async_system::teclado::crear_canal();
|
||||
let limites = StoreLimitsBuilder::new()
|
||||
.memory_size(techo_memoria)
|
||||
@@ -122,7 +116,6 @@ impl AplicacionWasm {
|
||||
let mut almacen = Store::new(
|
||||
&motor,
|
||||
ContextoCapacidades {
|
||||
marco,
|
||||
natural_ancho,
|
||||
natural_alto,
|
||||
canal,
|
||||
@@ -161,14 +154,11 @@ impl AplicacionWasm {
|
||||
.map_err(|_| FallaApp::Carga)?;
|
||||
|
||||
// 8. Con la app ya cargada e instanciada, inscribir su canal de teclado
|
||||
// en la difusion de la IRQ1: desde aqui recibe cada pulsacion.
|
||||
crate::async_system::teclado::registrar_canal(&almacen.data().canal);
|
||||
// en el censo de la IRQ1, en la ranura de su `indice_app`: desde
|
||||
// aqui recibe las teclas cuando el compositor le da el foco.
|
||||
crate::async_system::teclado::registrar_canal(indice_app, &almacen.data().canal);
|
||||
|
||||
Ok(AplicacionWasm {
|
||||
almacen,
|
||||
func_tick,
|
||||
marco,
|
||||
})
|
||||
Ok(AplicacionWasm { almacen, func_tick })
|
||||
}
|
||||
|
||||
/// Hace avanzar la aplicacion un fotograma. Recarga su presupuesto de
|
||||
@@ -194,10 +184,11 @@ impl AplicacionWasm {
|
||||
}
|
||||
}
|
||||
|
||||
/// El marco de pantalla que el compositor asigno a la aplicacion — donde
|
||||
/// se tatua su baliza si el kernel llega a desalojarla.
|
||||
pub fn marco(&self) -> RegionPantalla {
|
||||
self.marco
|
||||
/// El indice de la aplicacion — su identidad en el escritorio del
|
||||
/// compositor. Lo usa la tarea de la app para decirle al compositor que
|
||||
/// ventana desalojar si la app llega a fallar.
|
||||
pub fn indice(&self) -> usize {
|
||||
self.almacen.data().indice_app
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +198,6 @@ impl AplicacionWasm {
|
||||
/// empujando scancodes a una cola muerta: una fuga lenta pero segura.
|
||||
impl Drop for AplicacionWasm {
|
||||
fn drop(&mut self) {
|
||||
crate::async_system::teclado::cerrar_canal(&self.almacen.data().canal);
|
||||
crate::async_system::teclado::cerrar_canal(self.almacen.data().indice_app);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user