feat(renaser): Fase 14 — identidad del escritorio (nombres + barra de tareas)
Las ventanas eran anónimas: el escritorio no sabía nombrar lo que mostraba. Esta fase le pone un nombre a cada cuarto y una barra al pie con la lista de quienes lo habitan. - Cada `Ventana` lleva un `nombre: String` —del manifiesto, o del orquestador al engendrarla en vivo—. `Plantilla` lo guarda para las copias que `Alt+N` instancia. - Franja `FRANJA_TASKBAR=40px` reservada al pie. `area_apps` la descuenta — las ventanas teselan y flotan sin tapar la barra. - `consola`: tipos `Taskbar` / `CeldaTaskbar` + métodos `pintar_taskbar` y `pintar_etiqueta` (rasteriza una cadena en (x, base_y) sobre un fondo conocido, sin tocar la pluma). La pestaña enfocada se pinta con el índigo del foco, las desalojadas con su color de baliza, el resto con el slate del panel. - `compositor::recomponer` arma las celdas y las pasa junto a las capas a la consola; un único repintado, una única presentación. - `atender_raton`: si el clic cae en la franja de la barra, `celda_taskbar_en` localiza la pestaña pulsada y la enfoca (sin iniciar arrastre). Verificado en QEMU: al arrancar, la barra al pie muestra las 7 pestañas con sus nombres; `tonada` enfocada en índigo, `discola` y `glotona` en sus colores de baliza. Un clic sobre `pulso` traslada el foco al instante — el borde del compositor envuelve `pulso` y su pestaña se ilumina. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -946,3 +946,65 @@ enfocar y arrastre del marco de las ventanas flotantes.
|
|||||||
envuelve a `pulso`. `Alt+F` la flota; el ratón la agarra y la arrastra de
|
envuelve a `pulso`. `Alt+F` la flota; el ratón la agarra y la arrastra de
|
||||||
la esquina superior izquierda al centro-abajo de la pantalla con el botón
|
la esquina superior izquierda al centro-abajo de la pantalla con el botón
|
||||||
sostenido. El kernel sigue estable a través de los gestos.
|
sostenido. El kernel sigue estable a través de los gestos.
|
||||||
|
|
||||||
|
## Mapeador MMIO en el kernel — 2026-05-23
|
||||||
|
|
||||||
|
OVMF aloja los BAR prefetchables 64-bit de virtio en la ventana PCI de 64
|
||||||
|
bits, que el `bootloader_api` no mapea. `KernelHal::mmio_phys_to_virt`
|
||||||
|
devolvía `phys + offset` a ciegas y el primer registro MMIO leído reventaba
|
||||||
|
con #PF, dejando una franja roja sin pista visible. La autopsia: el commit
|
||||||
|
anterior añadió el dump del panic-handler por COM1, que sacó a la luz el IP
|
||||||
|
en `unchecked_mul::precondition_check` y la dirección de fallo
|
||||||
|
`offset + 0x800000014` — phys 32 GiB, donde OVMF había puesto el BAR.
|
||||||
|
|
||||||
|
### Añadido
|
||||||
|
- `memory::mmio`: envuelve la tabla L4 activa (vía `Cr3` + el mapeo de
|
||||||
|
memoria física del cargador) en un `OffsetPageTable`. `mapear(fisica,
|
||||||
|
tam)` abre páginas hacia la región pedida con `PRESENT | WRITABLE |
|
||||||
|
NO_CACHE | WRITE_THROUGH`, tratando `PageAlreadyMapped` y
|
||||||
|
`ParentEntryHugePage` como éxito silencioso.
|
||||||
|
- Los marcos para tablas intermedias salen del banco DMA del disco
|
||||||
|
(`asignar_marco_para_tabla`, sin pánico).
|
||||||
|
- `KernelHal::mmio_phys_to_virt` llama a `memory::mmio::mapear` antes de
|
||||||
|
devolver el puntero, asegurándose de que el BAR sea accesible.
|
||||||
|
|
||||||
|
### Cambiado
|
||||||
|
- `baliza::Serie` es ahora `pub(crate)`: cualquier módulo puede dejar
|
||||||
|
trazas por COM1. El panic-handler y el OOM-handler dejan en COM1 su
|
||||||
|
mensaje y ubicación además de pintar la franja. `kernel_main` traza
|
||||||
|
cada hito del arranque para diagnóstico remoto.
|
||||||
|
|
||||||
|
## Fase 14 — La identidad del escritorio: nombres y barra de tareas — 2026-05-23
|
||||||
|
|
||||||
|
Las ventanas eran anónimas: el escritorio reía con sus colores pero no sabía
|
||||||
|
cómo se llama lo que muestra. La Fase 14 le da nombre a cada cuarto y una
|
||||||
|
barra al pie con la lista de quien vive en la casa.
|
||||||
|
|
||||||
|
### Añadido
|
||||||
|
- Cada `Ventana` lleva un `nombre: String` —el que dicta su `EntradaApp` del
|
||||||
|
manifiesto, o el que el orquestador pasa al engendrarla en vivo (Fase 10)—.
|
||||||
|
`Plantilla` se acuerda del nombre para los lanzamientos posteriores.
|
||||||
|
- **`FRANJA_TASKBAR`** (40 px al pie) y `area_taskbar`: la barra de tareas
|
||||||
|
vive en su propia franja. `area_apps` la descuenta — las ventanas teselan
|
||||||
|
y flotan SIN tapar la barra ni la consola de la cima.
|
||||||
|
- `consola`: tipos `Taskbar` y `CeldaTaskbar`; métodos `pintar_taskbar`
|
||||||
|
(fondo + línea divisoria + una pestaña por ventana viva) y
|
||||||
|
`pintar_etiqueta` (rasteriza una cadena en un punto fijo, sin tocar la
|
||||||
|
pluma de la consola). La pestaña enfocada lleva el índigo del foco; las
|
||||||
|
desalojadas, su color de baliza; las demás, el slate del panel.
|
||||||
|
- `compositor::recomponer` arma la lista de celdas y la pasa a la consola
|
||||||
|
junto con las capas: un único repintado, una única presentación.
|
||||||
|
- `atender_raton`: un clic dentro de la franja de la barra busca su pestaña
|
||||||
|
con `celda_taskbar_en` y enfoca la ventana correspondiente (sin iniciar
|
||||||
|
arrastre). El comportamiento sobre el área de apps queda intacto.
|
||||||
|
|
||||||
|
### Cambiado
|
||||||
|
- `compositor::fundar` y `nacer_ventana` exigen un nombre para cada ventana.
|
||||||
|
`cargar_userspace` lo toma de `EntradaApp.nombre`; `lanzar_app`, de
|
||||||
|
`Plantilla.nombre`.
|
||||||
|
|
||||||
|
### Verificado
|
||||||
|
- QEMU. Tras arrancar se ve la barra al pie con las 7 pestañas (`tonada`
|
||||||
|
enfocada, índigo), las desalojadas en sus colores de baliza. Un clic
|
||||||
|
sobre `pulso` cambia el foco al instante: el borde índigo del compositor
|
||||||
|
deja la maestra y envuelve a `pulso`, y la pestaña `pulso` se ilumina.
|
||||||
|
|||||||
+5
-3
@@ -81,9 +81,11 @@ COMPLETA —orden-Z y ventanas flotantes: composición con solapamiento (`Alt+F`
|
|||||||
la Fase 10 COMPLETA —alta y baja de aplicaciones en vivo (`Alt+N` / `Alt+Q`)—,
|
la Fase 10 COMPLETA —alta y baja de aplicaciones en vivo (`Alt+N` / `Alt+Q`)—,
|
||||||
la Fase 11 COMPLETA —el reloj del sistema como capacidad de host
|
la Fase 11 COMPLETA —el reloj del sistema como capacidad de host
|
||||||
(`sys_tiempo_mono`) + la app `pulso`—, la Fase 12 COMPLETA —la bocina del PC
|
(`sys_tiempo_mono`) + la app `pulso`—, la Fase 12 COMPLETA —la bocina del PC
|
||||||
como capacidad de host (`sys_tono`) + la app `tonada`— y la Fase 13 COMPLETA
|
como capacidad de host (`sys_tono`) + la app `tonada`—, la Fase 13 COMPLETA
|
||||||
—ratón PS/2, puntero, clic-para-enfocar y arrastre de ventanas flotantes—.
|
—ratón PS/2, puntero, clic-para-enfocar y arrastre de ventanas flotantes—,
|
||||||
Todo verificado en QEMU. Ver `ROADMAP.md`.
|
infraestructura `memory::mmio` (mapeador propio de regiones MMIO en la tabla
|
||||||
|
L4) y la Fase 14 COMPLETA —nombres en cada ventana y barra de tareas con
|
||||||
|
clic-para-enfocar—. Todo verificado en QEMU. Ver `ROADMAP.md`.
|
||||||
|
|
||||||
## Flujo de trabajo
|
## Flujo de trabajo
|
||||||
|
|
||||||
|
|||||||
@@ -496,6 +496,23 @@ maceta de un balcón al otro. La mano suelta, el cuarto se queda donde lo dejó.
|
|||||||
Por fin la disposición no la dicta sólo la casa: también la conversación entre
|
Por fin la disposición no la dicta sólo la casa: también la conversación entre
|
||||||
quien vive dentro y quien habita fuera.
|
quien vive dentro y quien habita fuera.
|
||||||
|
|
||||||
|
## Los nombres — un cartel en cada puerta
|
||||||
|
|
||||||
|
La casa tenía buen olfato para colorear, pero un vicio antiguo: nunca decía cómo
|
||||||
|
se llamaba lo que mostraba. Las ventanas eran rectángulos sin etiqueta —«¿cuál
|
||||||
|
era la que cantaba?», «¿cuál la que cuenta los arranques?»—. La casa entendía,
|
||||||
|
nosotros adivinábamos. Hoy, al pie de la pantalla, le ha brotado un zócalo
|
||||||
|
estrecho con un cartel por cada inquilino: el nombre escrito, ordenadito, de
|
||||||
|
izquierda a derecha. La que tiene la mirada de la casa luce su cartel con el
|
||||||
|
índigo del foco; las que se rompieron, con su color de luto —violeta o crema
|
||||||
|
según la causa—; las demás, en gris discreto, esperando turno.
|
||||||
|
|
||||||
|
Y la barra no sólo dice: también ESCUCHA. Tocar un cartel con el dedo es ir al
|
||||||
|
cuarto que ese cartel nombra. La casa entiende sin más explicaciones: cambia la
|
||||||
|
mirada al inquilino elegido, su borde se ilumina, y el escritorio se recoloca
|
||||||
|
para honrarlo. Por fin la casa no se navega sólo a tientas con flechas: tiene un
|
||||||
|
directorio en su umbral.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*El diario continúa. La próxima página la escribirá la próxima jornada.*
|
*El diario continúa. La próxima página la escribirá la próxima jornada.*
|
||||||
|
|||||||
@@ -228,6 +228,33 @@ clic-para-enfocar y arrastre del marco de las flotantes. Verificada en QEMU
|
|||||||
cualquier ventana viva); si la enfocada flota, arranca un ARRASTRE con el
|
cualquier ventana viva); si la enfocada flota, arranca un ARRASTRE con el
|
||||||
desfase de agarre; el botón sostenido la sigue al puntero; al soltar, fin.
|
desfase de agarre; el botón sostenido la sigue al puntero; al soltar, fin.
|
||||||
|
|
||||||
|
## Fase 14 — la identidad del escritorio: nombres y barra de tareas (completada)
|
||||||
|
|
||||||
|
Las ventanas eran anónimas; el escritorio no sabía nombrar lo que mostraba. La
|
||||||
|
Fase 14 le pone un nombre a cada cuarto y una barra al pie con la lista de
|
||||||
|
quienes lo habitan. Verificada en QEMU.
|
||||||
|
|
||||||
|
- Cada `Ventana` lleva un `nombre: String` —el del manifiesto, o el del
|
||||||
|
orquestador al engendrarla en vivo—. `Plantilla` lo guarda para las copias.
|
||||||
|
- Una franja de 40 px al pie reservada como `area_taskbar`. `area_apps` la
|
||||||
|
descuenta — las ventanas teselan y flotan sin tapar la barra ni la consola.
|
||||||
|
- `consola` gana los tipos `Taskbar` / `CeldaTaskbar` y los métodos
|
||||||
|
`pintar_taskbar` y `pintar_etiqueta`. La pestaña enfocada lleva el índigo del
|
||||||
|
foco; la desalojada, su color de baliza; el resto, el slate.
|
||||||
|
- Un clic dentro de la barra enfoca la ventana de la pestaña pulsada, sin
|
||||||
|
iniciar arrastre. El compositor encuentra la celda con `celda_taskbar_en`.
|
||||||
|
|
||||||
|
## Mejora estructural — mapeador MMIO en el kernel (entre fases 13 y 14)
|
||||||
|
|
||||||
|
Los BAR prefetchables 64-bit que OVMF aloja en la ventana PCI de 64 bits NO
|
||||||
|
los mapea `bootloader_api`. El kernel gana `memory::mmio`, que envuelve la
|
||||||
|
tabla L4 activa y abre páginas hacia las regiones MMIO que virtio-drivers le
|
||||||
|
pide. `KernelHal::mmio_phys_to_virt` llama al mapeador antes de devolver el
|
||||||
|
puntero. Trata `PageAlreadyMapped` y `ParentEntryHugePage` como éxito
|
||||||
|
silencioso. Los marcos para tablas intermedias salen del banco DMA. Esta
|
||||||
|
mejora resolvió el #PF inexplicable en máquinas con un OVMF que coloca el BAR
|
||||||
|
del virtio-blk fuera de los primeros 4 GiB.
|
||||||
|
|
||||||
Líneas abiertas posteriores: reciclado de las ranuras de ventana cerradas;
|
Líneas abiertas posteriores: reciclado de las ranuras de ventana cerradas;
|
||||||
audio con varias voces (PCM) más allá del tono único de la bocina.
|
audio con varias voces (PCM) más allá del tono único de la bocina.
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
use alloc::string::{String, ToString};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ use crossbeam_queue::ArrayQueue;
|
|||||||
use mirada_layout::{tile, LayoutMode, LayoutParams, Rect};
|
use mirada_layout::{tile, LayoutMode, LayoutParams, Rect};
|
||||||
use spin::{Mutex, Once};
|
use spin::{Mutex, Once};
|
||||||
|
|
||||||
use crate::consola::{self, Capa, Contenido};
|
use crate::consola::{self, Capa, CeldaTaskbar, Contenido, Taskbar};
|
||||||
use crate::grafico::{Color, RegionPantalla};
|
use crate::grafico::{Color, RegionPantalla};
|
||||||
|
|
||||||
/// Altura del strip superior reservado a la consola; las apps teselan debajo.
|
/// Altura del strip superior reservado a la consola; las apps teselan debajo.
|
||||||
@@ -62,6 +63,17 @@ use crate::grafico::{Color, RegionPantalla};
|
|||||||
/// hasta la sonda asincrona de disco— legible sobre el teselado.
|
/// hasta la sonda asincrona de disco— legible sobre el teselado.
|
||||||
const FRANJA_CONSOLA: usize = 296;
|
const FRANJA_CONSOLA: usize = 296;
|
||||||
|
|
||||||
|
/// Altura de la barra de tareas inferior (Fase 14): cada ventana viva tiene
|
||||||
|
/// ahi una pestaña con su nombre, que el clic enfoca.
|
||||||
|
const FRANJA_TASKBAR: usize = 40;
|
||||||
|
|
||||||
|
/// Anchura de cada celda de la barra de tareas, en pixeles.
|
||||||
|
const CELDA_TASKBAR_ANCHO: usize = 156;
|
||||||
|
/// Hueco entre celdas adyacentes de la barra.
|
||||||
|
const CELDA_TASKBAR_HUECO: usize = 6;
|
||||||
|
/// Margen izquierdo de la primera celda.
|
||||||
|
const CELDA_TASKBAR_MARGEN: usize = 16;
|
||||||
|
|
||||||
/// El modo de teselado con que arranca el escritorio. El teclado lo cicla.
|
/// El modo de teselado con que arranca el escritorio. El teclado lo cicla.
|
||||||
const MODO_INICIAL: LayoutMode = LayoutMode::MasterStack;
|
const MODO_INICIAL: LayoutMode = LayoutMode::MasterStack;
|
||||||
|
|
||||||
@@ -116,6 +128,9 @@ struct Arrastre {
|
|||||||
|
|
||||||
/// Una ventana del escritorio: una app, su geometria y su ultimo fotograma.
|
/// Una ventana del escritorio: una app, su geometria y su ultimo fotograma.
|
||||||
struct Ventana {
|
struct Ventana {
|
||||||
|
/// Nombre legible de la app — el que dicta su `EntradaApp` del manifiesto.
|
||||||
|
/// Aparece en la pestaña de la barra de tareas (Fase 14).
|
||||||
|
nombre: String,
|
||||||
/// Tamaño natural del lienzo de la app — lo que sabe pintar, fijo.
|
/// Tamaño natural del lienzo de la app — lo que sabe pintar, fijo.
|
||||||
natural_ancho: usize,
|
natural_ancho: usize,
|
||||||
natural_alto: usize,
|
natural_alto: usize,
|
||||||
@@ -190,12 +205,13 @@ static PARTOS: AtomicUsize = AtomicUsize::new(0);
|
|||||||
/// Funda el escritorio: crea una ventana por app, con su marco teselado inicial
|
/// Funda el escritorio: crea una ventana por app, con su marco teselado inicial
|
||||||
/// y su cache de respaldo ya reservada al tamaño natural. `naturales` da el
|
/// y su cache de respaldo ya reservada al tamaño natural. `naturales` da el
|
||||||
/// `(ancho, alto)` del lienzo de cada app, en el orden del manifiesto.
|
/// `(ancho, alto)` del lienzo de cada app, en el orden del manifiesto.
|
||||||
pub fn fundar(ancho: usize, alto: usize, naturales: &[(usize, usize)]) {
|
pub fn fundar(ancho: usize, alto: usize, naturales: &[(usize, usize, &str)]) {
|
||||||
MANDOS.call_once(|| ArrayQueue::new(CAPACIDAD_MANDOS));
|
MANDOS.call_once(|| ArrayQueue::new(CAPACIDAD_MANDOS));
|
||||||
|
|
||||||
let mut ventanas = Vec::with_capacity(naturales.len());
|
let mut ventanas = Vec::with_capacity(naturales.len());
|
||||||
for &(nat_ancho, nat_alto) in naturales {
|
for &(nat_ancho, nat_alto, nombre) in naturales {
|
||||||
ventanas.push(Ventana {
|
ventanas.push(Ventana {
|
||||||
|
nombre: nombre.to_string(),
|
||||||
natural_ancho: nat_ancho,
|
natural_ancho: nat_ancho,
|
||||||
natural_alto: nat_alto,
|
natural_alto: nat_alto,
|
||||||
// Marco provisional; `aplicar_teselado` lo fija enseguida.
|
// Marco provisional; `aplicar_teselado` lo fija enseguida.
|
||||||
@@ -581,7 +597,77 @@ fn recomponer(escritorio: &Escritorio) {
|
|||||||
enfocada: indice == foco,
|
enfocada: indice == foco,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
consola::recomponer(area, &capas);
|
|
||||||
|
// FASE 14 :: armar las celdas de la barra de tareas. Una pestaña por
|
||||||
|
// ventana viva (no cerrada), de izquierda a derecha, con el nombre de la
|
||||||
|
// app; la enfocada lleva el color indigo del foco, las desalojadas su
|
||||||
|
// baliza, las demas el slate del panel. El clic sobre una pestaña enfoca
|
||||||
|
// su ventana.
|
||||||
|
let area_bar = area_taskbar(escritorio.ancho, escritorio.alto);
|
||||||
|
let mut celdas: Vec<CeldaTaskbar> = Vec::new();
|
||||||
|
let mut cx = area_bar.x + CELDA_TASKBAR_MARGEN;
|
||||||
|
let cy = area_bar.y + 4;
|
||||||
|
let calto = area_bar.alto.saturating_sub(8);
|
||||||
|
for (indice, ventana) in escritorio.ventanas.iter().enumerate() {
|
||||||
|
if ventana.cerrada {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if cx + CELDA_TASKBAR_ANCHO > area_bar.x + area_bar.ancho {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let fondo = match ventana.baliza {
|
||||||
|
Some(color) => color,
|
||||||
|
None if indice == foco => Color::FOCO,
|
||||||
|
None => Color::PANEL,
|
||||||
|
};
|
||||||
|
celdas.push(CeldaTaskbar {
|
||||||
|
region: RegionPantalla {
|
||||||
|
x: cx,
|
||||||
|
y: cy,
|
||||||
|
ancho: CELDA_TASKBAR_ANCHO,
|
||||||
|
alto: calto,
|
||||||
|
},
|
||||||
|
nombre: &ventana.nombre,
|
||||||
|
fondo,
|
||||||
|
tinta: Color::TEXTO,
|
||||||
|
});
|
||||||
|
cx += CELDA_TASKBAR_ANCHO + CELDA_TASKBAR_HUECO;
|
||||||
|
}
|
||||||
|
let taskbar = Taskbar {
|
||||||
|
area: area_bar,
|
||||||
|
celdas: &celdas,
|
||||||
|
};
|
||||||
|
consola::recomponer(area, &capas, &taskbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Localiza la celda de la barra de tareas bajo la coordenada x: itera las
|
||||||
|
/// ventanas vivas en orden de creacion y devuelve la N-esima donde la N es la
|
||||||
|
/// posicion en la barra. `None` si el clic cae en un hueco entre celdas, antes
|
||||||
|
/// del margen, o pasada la ultima.
|
||||||
|
fn celda_taskbar_en(escritorio: &Escritorio, x: usize) -> Option<usize> {
|
||||||
|
let area_bar = area_taskbar(escritorio.ancho, escritorio.alto);
|
||||||
|
let margen_izq = area_bar.x + CELDA_TASKBAR_MARGEN;
|
||||||
|
if x < margen_izq {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let rel = x - margen_izq;
|
||||||
|
let paso = CELDA_TASKBAR_ANCHO + CELDA_TASKBAR_HUECO;
|
||||||
|
let posicion = rel / paso;
|
||||||
|
let offset = rel % paso;
|
||||||
|
if offset >= CELDA_TASKBAR_ANCHO {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut k = 0;
|
||||||
|
for (indice, ventana) in escritorio.ventanas.iter().enumerate() {
|
||||||
|
if ventana.cerrada {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if k == posicion {
|
||||||
|
return Some(indice);
|
||||||
|
}
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -642,13 +728,14 @@ fn cerrar() {
|
|||||||
/// con su cache de respaldo al tamaño natural, la añade al final del orden de
|
/// con su cache de respaldo al tamaño natural, la añade al final del orden de
|
||||||
/// teselado, recalcula el teselado y recompone. La invoca el orquestador del
|
/// teselado, recalcula el teselado y recompone. La invoca el orquestador del
|
||||||
/// kernel justo antes de instanciar el WASM de la app, que necesita ese indice.
|
/// kernel justo antes de instanciar el WASM de la app, que necesita ese indice.
|
||||||
pub fn nacer_ventana(nat_ancho: usize, nat_alto: usize) -> usize {
|
pub fn nacer_ventana(nat_ancho: usize, nat_alto: usize, nombre: &str) -> usize {
|
||||||
let Some(escritorio) = ESCRITORIO.get() else {
|
let Some(escritorio) = ESCRITORIO.get() else {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
let mut escritorio = escritorio.lock();
|
let mut escritorio = escritorio.lock();
|
||||||
let indice = escritorio.ventanas.len();
|
let indice = escritorio.ventanas.len();
|
||||||
escritorio.ventanas.push(Ventana {
|
escritorio.ventanas.push(Ventana {
|
||||||
|
nombre: nombre.to_string(),
|
||||||
natural_ancho: nat_ancho,
|
natural_ancho: nat_ancho,
|
||||||
natural_alto: nat_alto,
|
natural_alto: nat_alto,
|
||||||
marco: RegionPantalla {
|
marco: RegionPantalla {
|
||||||
@@ -716,8 +803,24 @@ pub fn atender_raton() {
|
|||||||
let y = evento.y as usize;
|
let y = evento.y as usize;
|
||||||
let izq_antes = escritorio.raton_izq;
|
let izq_antes = escritorio.raton_izq;
|
||||||
if izq && !izq_antes {
|
if izq && !izq_antes {
|
||||||
// Boton bajó: un CLIC sobre el punto (x, y).
|
// Boton bajó: un CLIC. Si cae en la barra de tareas, enfocar la
|
||||||
if let Some(v) = ventana_en(&escritorio, x, y) {
|
// pestaña pulsada SIN iniciar arrastre. Si no, comportamiento
|
||||||
|
// habitual: enfocar la ventana topmost bajo el puntero.
|
||||||
|
let area_bar = area_taskbar(escritorio.ancho, escritorio.alto);
|
||||||
|
if y >= area_bar.y && y < area_bar.y + area_bar.alto {
|
||||||
|
if let Some(v) = celda_taskbar_en(&escritorio, x) {
|
||||||
|
let viva = {
|
||||||
|
let w = &escritorio.ventanas[v];
|
||||||
|
w.baliza.is_none() && !w.cerrada
|
||||||
|
};
|
||||||
|
if viva {
|
||||||
|
FOCO.store(v, Ordering::Relaxed);
|
||||||
|
crate::drivers::altavoz::tono(0);
|
||||||
|
alzar_si_flota(&mut escritorio, v);
|
||||||
|
cambio = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(v) = ventana_en(&escritorio, x, y) {
|
||||||
let viva = {
|
let viva = {
|
||||||
let w = &escritorio.ventanas[v];
|
let w = &escritorio.ventanas[v];
|
||||||
w.baliza.is_none() && !w.cerrada
|
w.baliza.is_none() && !w.cerrada
|
||||||
@@ -835,13 +938,26 @@ pub fn refrescar_puntero() {
|
|||||||
// =============================================================================
|
// =============================================================================
|
||||||
|
|
||||||
/// El area de pantalla que el compositor tesela: toda la pantalla menos la
|
/// El area de pantalla que el compositor tesela: toda la pantalla menos la
|
||||||
/// franja de la consola en la cima.
|
/// franja de la consola en la cima y la barra de tareas al pie.
|
||||||
pub fn area_apps(ancho_pantalla: usize, alto_pantalla: usize) -> RegionPantalla {
|
pub fn area_apps(ancho_pantalla: usize, alto_pantalla: usize) -> RegionPantalla {
|
||||||
|
let cabeza = FRANJA_CONSOLA.min(alto_pantalla);
|
||||||
|
let pie = FRANJA_TASKBAR.min(alto_pantalla.saturating_sub(cabeza));
|
||||||
RegionPantalla {
|
RegionPantalla {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: FRANJA_CONSOLA.min(alto_pantalla),
|
y: cabeza,
|
||||||
ancho: ancho_pantalla,
|
ancho: ancho_pantalla,
|
||||||
alto: alto_pantalla.saturating_sub(FRANJA_CONSOLA),
|
alto: alto_pantalla.saturating_sub(cabeza).saturating_sub(pie),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// El area de la barra de tareas: una franja al pie de la pantalla.
|
||||||
|
fn area_taskbar(ancho_pantalla: usize, alto_pantalla: usize) -> RegionPantalla {
|
||||||
|
let pie = FRANJA_TASKBAR.min(alto_pantalla);
|
||||||
|
RegionPantalla {
|
||||||
|
x: 0,
|
||||||
|
y: alto_pantalla.saturating_sub(pie),
|
||||||
|
ancho: ancho_pantalla,
|
||||||
|
alto: pie,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,24 @@ pub(crate) struct Capa<'a> {
|
|||||||
pub(crate) enfocada: bool,
|
pub(crate) enfocada: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Una pestaña de la barra de tareas (Fase 14): una región de pantalla, el
|
||||||
|
/// nombre de la app y los colores con que pintarla. Las arma el compositor
|
||||||
|
/// con una pestaña por ventana viva.
|
||||||
|
pub(crate) struct CeldaTaskbar<'a> {
|
||||||
|
pub(crate) region: RegionPantalla,
|
||||||
|
pub(crate) nombre: &'a str,
|
||||||
|
/// Color de fondo: indigo del foco, slate del panel, o color de baliza.
|
||||||
|
pub(crate) fondo: Color,
|
||||||
|
/// Color de la tinta del texto.
|
||||||
|
pub(crate) tinta: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// La barra de tareas del escritorio (Fase 14): su area y sus pestañas.
|
||||||
|
pub(crate) struct Taskbar<'a> {
|
||||||
|
pub(crate) area: RegionPantalla,
|
||||||
|
pub(crate) celdas: &'a [CeldaTaskbar<'a>],
|
||||||
|
}
|
||||||
|
|
||||||
/// La consola grafica de renaser: doble bufer, pantalla fisica y pluma.
|
/// La consola grafica de renaser: doble bufer, pantalla fisica y pluma.
|
||||||
pub(crate) struct Consola {
|
pub(crate) struct Consola {
|
||||||
lienzo: Lienzo,
|
lienzo: Lienzo,
|
||||||
@@ -224,7 +242,7 @@ impl Consola {
|
|||||||
/// flotantes se resuelve por si solo, sin recortes ni mascaras. Cada capa
|
/// flotantes se resuelve por si solo, sin recortes ni mascaras. Cada capa
|
||||||
/// pinta primero su panel —el cromo de la ventana— y, encima, su contenido;
|
/// pinta primero su panel —el cromo de la ventana— y, encima, su contenido;
|
||||||
/// una sola presentacion cierra la pasada.
|
/// una sola presentacion cierra la pasada.
|
||||||
fn recomponer(&mut self, area: RegionPantalla, capas: &[Capa]) {
|
fn recomponer(&mut self, area: RegionPantalla, capas: &[Capa], taskbar: &Taskbar) {
|
||||||
self.lienzo.rellenar_rect(
|
self.lienzo.rellenar_rect(
|
||||||
area.x,
|
area.x,
|
||||||
area.y,
|
area.y,
|
||||||
@@ -252,9 +270,72 @@ impl Consola {
|
|||||||
}
|
}
|
||||||
self.dibujar_borde(m, capa.enfocada);
|
self.dibujar_borde(m, capa.enfocada);
|
||||||
}
|
}
|
||||||
|
self.pintar_taskbar(taskbar);
|
||||||
self.presentar();
|
self.presentar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pinta la barra de tareas como ultima capa del escritorio (Fase 14): el
|
||||||
|
/// fondo de la franja, una linea fina arriba que la separa de las apps, y
|
||||||
|
/// las pestañas —cada una su rectángulo y su nombre—.
|
||||||
|
fn pintar_taskbar(&mut self, taskbar: &Taskbar) {
|
||||||
|
// Fondo de la barra y linea de separacion.
|
||||||
|
self.lienzo.rellenar_rect(
|
||||||
|
taskbar.area.x,
|
||||||
|
taskbar.area.y,
|
||||||
|
taskbar.area.ancho,
|
||||||
|
taskbar.area.alto,
|
||||||
|
Color::PANEL,
|
||||||
|
);
|
||||||
|
self.lienzo
|
||||||
|
.rellenar_rect(taskbar.area.x, taskbar.area.y, taskbar.area.ancho, 1, Color::SIN_FOCO);
|
||||||
|
// Las pestañas.
|
||||||
|
for celda in taskbar.celdas {
|
||||||
|
let r = celda.region;
|
||||||
|
self.lienzo
|
||||||
|
.rellenar_rect(r.x, r.y, r.ancho, r.alto, celda.fondo);
|
||||||
|
// El nombre, alineado a la izquierda de la pestaña, vertical-
|
||||||
|
// centrado a la altura visible de la franja.
|
||||||
|
let base_y = r.y + (r.alto + 14) / 2;
|
||||||
|
self.pintar_etiqueta(r.x + 10, base_y, celda.nombre, 16.0, celda.fondo, celda.tinta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rasteriza una cadena de texto a un tamaño dado, en (x, base_y), sobre
|
||||||
|
/// un fondo conocido —del que toma la mezcla por cobertura del glifo—. Es
|
||||||
|
/// la version sin estado de la pluma: el llamante decide donde escribir.
|
||||||
|
fn pintar_etiqueta(
|
||||||
|
&mut self,
|
||||||
|
x: usize,
|
||||||
|
base_y: usize,
|
||||||
|
texto: &str,
|
||||||
|
tamaño: f32,
|
||||||
|
fondo: Color,
|
||||||
|
tinta: Color,
|
||||||
|
) {
|
||||||
|
let mut cursor = x;
|
||||||
|
for caracter in texto.chars() {
|
||||||
|
let (metricas, cobertura) = texto::rasterizar(caracter, tamaño);
|
||||||
|
let inicio_x = cursor as isize + metricas.xmin as isize;
|
||||||
|
let inicio_y = base_y as isize - metricas.ymin as isize - metricas.height as isize;
|
||||||
|
for fila in 0..metricas.height {
|
||||||
|
for col in 0..metricas.width {
|
||||||
|
let opacidad = cobertura[fila * metricas.width + col];
|
||||||
|
if opacidad == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let px = inicio_x + col as isize;
|
||||||
|
let py = inicio_y + fila as isize;
|
||||||
|
if px < 0 || py < 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let color = mezclar(fondo, tinta, opacidad);
|
||||||
|
self.lienzo.pintar_pixel(px as usize, py as usize, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor += metricas.advance_width as usize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Inunda una region entera con un color plano —la baliza de desalojo: una
|
/// Inunda una region entera con un color plano —la baliza de desalojo: una
|
||||||
/// app que falla tatua su marco de purpura— y le traza su borde de foco.
|
/// app que falla tatua su marco de purpura— y le traza su borde de foco.
|
||||||
fn pintar_region(&mut self, region: RegionPantalla, color: Color, enfocada: bool) {
|
fn pintar_region(&mut self, region: RegionPantalla, color: Color, enfocada: bool) {
|
||||||
@@ -328,9 +409,9 @@ pub(crate) fn volcar_marco(
|
|||||||
/// La invoca `compositor` al arrancar y siempre que hay ventanas flotantes: el
|
/// La invoca `compositor` al arrancar y siempre que hay ventanas flotantes: el
|
||||||
/// solapamiento obliga a repintar el escritorio en bloque, no ventana a
|
/// solapamiento obliga a repintar el escritorio en bloque, no ventana a
|
||||||
/// ventana. Las capas llegan ya ordenadas de atras hacia adelante.
|
/// ventana. Las capas llegan ya ordenadas de atras hacia adelante.
|
||||||
pub(crate) fn recomponer(area: RegionPantalla, capas: &[Capa]) {
|
pub(crate) fn recomponer(area: RegionPantalla, capas: &[Capa], taskbar: &Taskbar) {
|
||||||
if let Some(consola) = CONSOLA.get() {
|
if let Some(consola) = CONSOLA.get() {
|
||||||
consola.lock().recomponer(area, capas);
|
consola.lock().recomponer(area, capas, taskbar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ fn traza(rotulo: &str) {
|
|||||||
/// su bytecode —cacheado en RAM al arrancar, para no volver al disco despues—
|
/// su bytecode —cacheado en RAM al arrancar, para no volver al disco despues—
|
||||||
/// y la geometria y la cuota de memoria con que instanciarla.
|
/// y la geometria y la cuota de memoria con que instanciarla.
|
||||||
struct Plantilla {
|
struct Plantilla {
|
||||||
|
/// Nombre legible de la app — el del manifiesto, que la barra de tareas
|
||||||
|
/// (Fase 14) muestra en la pestaña.
|
||||||
|
nombre: alloc::string::String,
|
||||||
bytecode: Vec<u8>,
|
bytecode: Vec<u8>,
|
||||||
nat_ancho: usize,
|
nat_ancho: usize,
|
||||||
nat_alto: usize,
|
nat_alto: usize,
|
||||||
@@ -227,6 +230,7 @@ fn encender_app(
|
|||||||
// un molde en RAM con que `Alt+N` instanciara copias en vivo, sin volver al
|
// un molde en RAM con que `Alt+N` instanciara copias en vivo, sin volver al
|
||||||
// disco —que la E/S por sondeo en mitad del reactor seria un mal vecino—.
|
// disco —que la E/S por sondeo en mitad del reactor seria un mal vecino—.
|
||||||
Some(Plantilla {
|
Some(Plantilla {
|
||||||
|
nombre: entrada.nombre.clone(),
|
||||||
bytecode,
|
bytecode,
|
||||||
nat_ancho: natural.ancho,
|
nat_ancho: natural.ancho,
|
||||||
nat_alto: natural.alto,
|
nat_alto: natural.alto,
|
||||||
@@ -252,7 +256,7 @@ fn lanzar_app() {
|
|||||||
|
|
||||||
// La ventana nace primero: el compositor le entrega su indice —su
|
// La ventana nace primero: el compositor le entrega su indice —su
|
||||||
// identidad—, que el WASM necesita para hallar su ventana y su canal.
|
// identidad—, que el WASM necesita para hallar su ventana y su canal.
|
||||||
let indice = compositor::nacer_ventana(plantilla.nat_ancho, plantilla.nat_alto);
|
let indice = compositor::nacer_ventana(plantilla.nat_ancho, plantilla.nat_alto, &plantilla.nombre);
|
||||||
match wasm::AplicacionWasm::cargar(
|
match wasm::AplicacionWasm::cargar(
|
||||||
&plantilla.bytecode,
|
&plantilla.bytecode,
|
||||||
plantilla.nat_ancho,
|
plantilla.nat_ancho,
|
||||||
@@ -318,10 +322,10 @@ fn cargar_userspace(ejecutor: &mut Executor, ancho_pantalla: usize, alto_pantall
|
|||||||
// con su cache de respaldo y su marco teselado por `mirada-layout`— y
|
// con su cache de respaldo y su marco teselado por `mirada-layout`— y
|
||||||
// pintar el escenario antes de encender las apps: el teselado se ve
|
// pintar el escenario antes de encender las apps: el teselado se ve
|
||||||
// aunque alguna app no llegue a pintar su primer fotograma.
|
// aunque alguna app no llegue a pintar su primer fotograma.
|
||||||
let naturales: Vec<(usize, usize)> = m
|
let naturales: Vec<(usize, usize, &str)> = m
|
||||||
.apps
|
.apps
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| (e.region_ancho as usize, e.region_alto as usize))
|
.map(|e| (e.region_ancho as usize, e.region_alto as usize, e.nombre.as_str()))
|
||||||
.collect();
|
.collect();
|
||||||
compositor::fundar(ancho_pantalla, alto_pantalla, &naturales);
|
compositor::fundar(ancho_pantalla, alto_pantalla, &naturales);
|
||||||
compositor::componer_escenario();
|
compositor::componer_escenario();
|
||||||
|
|||||||
Reference in New Issue
Block a user