feat(mirada): mirada-layout no_std — primer núcleo compartible con renaser
mirada-layout (el motor de teselado del compositor) pasa a `no_std +
alloc` para poder compilarse también en bare-metal — es el primer
crate-núcleo que brahman y renaser compartirán.
- `#![cfg_attr(not(test), no_std)]` + `extern crate alloc`: usa
`alloc::{vec, collections::BTreeMap}` en vez de `std`.
- Matemática de punto flotante vía `libm` (`sqrt`/`ceil`/`round` viven
en `std`, no en `core`).
- `serde` pasa a feature opcional: los consumidores Linux
(mirada-protocol/brain) la activan; un consumidor bare-metal no
necesita (de)serializar el layout.
- Deps declaradas directas (no `workspace = true`): un núcleo que
cruzará fronteras de workspace se mantiene autocontenido.
Verificado: `cargo build --target x86_64-unknown-none` compila;
32 tests verdes; mirada-protocol/brain sin regresión.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Generated
+1
@@ -7889,6 +7889,7 @@ version = "0.1.0"
|
|||||||
name = "mirada-layout"
|
name = "mirada-layout"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"libm",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ publish.workspace = true
|
|||||||
description = "mirada — orquestador de escritorio del compositor: mantiene salidas, escritorios virtuales, ventanas y foco; consume BodyEvent y produce BrainCommand. Agnóstico de GPUI y de smithay."
|
description = "mirada — orquestador de escritorio del compositor: mantiene salidas, escritorios virtuales, ventanas y foco; consume BodyEvent y produce BrainCommand. Agnóstico de GPUI y de smithay."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mirada-layout = { path = "../mirada-layout" }
|
# `serde` activa los `derive` de los tipos de layout (este crate
|
||||||
|
# serializa el estado del escritorio a RON).
|
||||||
|
mirada-layout = { path = "../mirada-layout", features = ["serde"] }
|
||||||
mirada-protocol = { path = "../mirada-protocol" }
|
mirada-protocol = { path = "../mirada-protocol" }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
ron = { workspace = true }
|
ron = { workspace = true }
|
||||||
|
|||||||
@@ -8,4 +8,18 @@ publish.workspace = true
|
|||||||
description = "mirada — motor de teselado del compositor Wayland: reparte la pantalla entre ventanas según el modo de layout. Agnóstico de Wayland y de smithay."
|
description = "mirada — motor de teselado del compositor Wayland: reparte la pantalla entre ventanas según el modo de layout. Agnóstico de Wayland y de smithay."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
# Deps declaradas directas (no `workspace = true`): mirada-layout es un
|
||||||
|
# crate-núcleo compartido entre dos workspaces de Cargo —brahman y
|
||||||
|
# renaser— así que mantiene sus dependencias autocontenidas.
|
||||||
|
#
|
||||||
|
# `libm`: `sqrt`/`ceil`/`round` viven en `std`, no en `core`; este crate
|
||||||
|
# es `no_std`, así que la matemática de punto flotante pasa por `libm`.
|
||||||
|
libm = "0.2"
|
||||||
|
# `serde` es opcional: el motor de teselado no necesita (de)serializar
|
||||||
|
# para funcionar. Los consumidores Linux (mirada-protocol/brain) activan
|
||||||
|
# la feature; el kernel bare-metal de renaser no. `default-features =
|
||||||
|
# false` evita arrastrar `std` aun con la feature activa.
|
||||||
|
serde = { version = "1", optional = true, default-features = false, features = ["derive", "alloc"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
serde = ["dep:serde"]
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
//! Geometría — el rectángulo en coordenadas de pantalla.
|
//! Geometría — el rectángulo en coordenadas de pantalla.
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Un rectángulo en píxeles de pantalla. El origen `(0,0)` es la
|
/// Un rectángulo en píxeles de pantalla. El origen `(0,0)` es la
|
||||||
/// esquina superior-izquierda; `x` crece a la derecha, `y` hacia abajo.
|
/// esquina superior-izquierda; `x` crece a la derecha, `y` hacia abajo.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
pub y: i32,
|
pub y: i32,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
//! Modos de teselado — cómo se reparte la pantalla entre ventanas.
|
//! Modos de teselado — cómo se reparte la pantalla entre ventanas.
|
||||||
|
|
||||||
|
use alloc::{vec, vec::Vec};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::geometry::{split, Rect};
|
use crate::geometry::{split, Rect};
|
||||||
@@ -8,8 +11,9 @@ use crate::geometry::{split, Rect};
|
|||||||
///
|
///
|
||||||
/// Las variantes nuevas se añaden **al final** para no mover los índices
|
/// Las variantes nuevas se añaden **al final** para no mover los índices
|
||||||
/// con que `postcard` las serializa en el API de control.
|
/// con que `postcard` las serializa en el API de control.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
|
||||||
pub enum LayoutMode {
|
pub enum LayoutMode {
|
||||||
/// Una ventana maestra a la izquierda; el resto apiladas a la derecha.
|
/// Una ventana maestra a la izquierda; el resto apiladas a la derecha.
|
||||||
MasterStack,
|
MasterStack,
|
||||||
@@ -49,7 +53,8 @@ impl LayoutMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parámetros del teselado.
|
/// Parámetros del teselado.
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct LayoutParams {
|
pub struct LayoutParams {
|
||||||
pub mode: LayoutMode,
|
pub mode: LayoutMode,
|
||||||
/// Fracción del ancho para la ventana maestra en `MasterStack` y
|
/// Fracción del ancho para la ventana maestra en `MasterStack` y
|
||||||
@@ -108,7 +113,9 @@ fn columns(screen: Rect, count: usize) -> Vec<Rect> {
|
|||||||
|
|
||||||
/// Rejilla `cols × rows` lo más cuadrada posible.
|
/// Rejilla `cols × rows` lo más cuadrada posible.
|
||||||
fn grid(screen: Rect, count: usize) -> Vec<Rect> {
|
fn grid(screen: Rect, count: usize) -> Vec<Rect> {
|
||||||
let cols = (count as f64).sqrt().ceil() as usize;
|
// `libm` en vez de los métodos de `f64`: `sqrt`/`ceil` viven en
|
||||||
|
// `std`, no en `core` — y este crate es `no_std`.
|
||||||
|
let cols = libm::ceil(libm::sqrt(count as f64)) as usize;
|
||||||
let rows = count.div_ceil(cols);
|
let rows = count.div_ceil(cols);
|
||||||
let col_parts = split(screen.w, cols);
|
let col_parts = split(screen.w, cols);
|
||||||
let row_parts = split(screen.h, rows);
|
let row_parts = split(screen.h, rows);
|
||||||
@@ -162,7 +169,7 @@ fn centered_master(screen: Rect, count: usize, ratio: f32, master_count: usize)
|
|||||||
return master_stack(screen, count, ratio, master_count);
|
return master_stack(screen, count, ratio, master_count);
|
||||||
}
|
}
|
||||||
let ratio = ratio.clamp(0.05, 0.95);
|
let ratio = ratio.clamp(0.05, 0.95);
|
||||||
let master_w = (screen.w as f32 * ratio).round() as i32;
|
let master_w = libm::roundf(screen.w as f32 * ratio) as i32;
|
||||||
let sides = split(screen.w - master_w, 2);
|
let sides = split(screen.w - master_w, 2);
|
||||||
let (left_w, right_w) = (sides[0].1, sides[1].1);
|
let (left_w, right_w) = (sides[0].1, sides[1].1);
|
||||||
let left_n = stack / 2;
|
let left_n = stack / 2;
|
||||||
@@ -194,7 +201,7 @@ fn master_stack(screen: Rect, count: usize, ratio: f32, master_count: usize) ->
|
|||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
let ratio = ratio.clamp(0.05, 0.95);
|
let ratio = ratio.clamp(0.05, 0.95);
|
||||||
let master_w = (screen.w as f32 * ratio).round() as i32;
|
let master_w = libm::roundf(screen.w as f32 * ratio) as i32;
|
||||||
let stack_x = screen.x + master_w;
|
let stack_x = screen.x + master_w;
|
||||||
let stack_w = screen.w - master_w;
|
let stack_w = screen.w - master_w;
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,14 @@
|
|||||||
//! Todo es determinista y testeable sin un servidor gráfico: la misma
|
//! Todo es determinista y testeable sin un servidor gráfico: la misma
|
||||||
//! pantalla y las mismas ventanas dan siempre la misma distribución.
|
//! pantalla y las mismas ventanas dan siempre la misma distribución.
|
||||||
|
|
||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
|
// Lógica pura sobre `core` + `alloc`: sin `std`. Así el mismo motor de
|
||||||
|
// teselado compila para Linux y para el kernel bare-metal de renaser
|
||||||
|
// (`x86_64-unknown-none`); el allocator lo aporta el consumidor.
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
//! `Workspace` — un conjunto de ventanas, su foco y su modo de teselado.
|
//! `Workspace` — un conjunto de ventanas, su foco y su modo de teselado.
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
// El macro `vec!` sólo lo usan los tests de este módulo.
|
||||||
|
#[cfg(test)]
|
||||||
|
use alloc::vec;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::geometry::Rect;
|
use crate::geometry::Rect;
|
||||||
@@ -11,7 +16,8 @@ use crate::layout::{tile, LayoutMode, LayoutParams};
|
|||||||
pub type WindowId = u64;
|
pub type WindowId = u64;
|
||||||
|
|
||||||
/// Un escritorio: ventanas en orden de teselado + la enfocada + el modo.
|
/// Un escritorio: ventanas en orden de teselado + la enfocada + el modo.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
/// Ventanas en orden de teselado (la 0 es la maestra en `MasterStack`).
|
/// Ventanas en orden de teselado (la 0 es la maestra en `MasterStack`).
|
||||||
windows: Vec<WindowId>,
|
windows: Vec<WindowId>,
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ publish.workspace = true
|
|||||||
description = "mirada — contrato Cerebro↔Cuerpo del compositor: comandos de geometría que el Cerebro (GPUI) envía y eventos de hardware/superficies que el Cuerpo (smithay) reporta. Marco postcard con prefijo de longitud."
|
description = "mirada — contrato Cerebro↔Cuerpo del compositor: comandos de geometría que el Cerebro (GPUI) envía y eventos de hardware/superficies que el Cuerpo (smithay) reporta. Marco postcard con prefijo de longitud."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
mirada-layout = { path = "../mirada-layout" }
|
# `serde` activa los `derive` de los tipos de layout — este crate los
|
||||||
|
# (de)serializa con postcard.
|
||||||
|
mirada-layout = { path = "../mirada-layout", features = ["serde"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
postcard = { workspace = true }
|
postcard = { workspace = true }
|
||||||
|
|||||||
Reference in New Issue
Block a user