Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions Build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ echo "[1/7] Building Init Orchestrator (init)..."
echo "[2/7] Building Window Server (compositor)..."
(cd apps/compositor && $BUILD_CMD)

# The Meridian shell (Nyx-ui/design/ver3.0). Built EVERY time, even when the compositor is the one
# being shipped: keeping it compiling is what stops it rotting while the apps around it change, and
# it costs seconds. Which of the two becomes WindowServer.nyx is $WINDOW_SERVER, applied further down.
echo "[2b] Building Meridian shell (shell)..."
(cd apps/shell && $BUILD_CMD)

# [3] Terminal is now a std target_os=nyx binary (D3) — built via Build-std.sh below (step [9f]),
# not the no_std $BUILD_CMD path.

Expand Down Expand Up @@ -109,9 +115,25 @@ mkdir -p build_initrd/apps/PosixProbe.nyx/scratch
mkdir -p build_initrd/apps/HelloC.nyx

# 2. Copy the compiled binaries into the folders as 'run.bin'
# Notice the binary name for WindowServer is now 'compositor'
cp target/x86_64-nyx/release/nyx-init build_initrd/apps/Init.nyx/run.bin
cp target/x86_64-nyx/release/compositor build_initrd/apps/WindowServer.nyx/run.bin

# WindowServer.nyx is whichever shell $WINDOW_SERVER names — `compositor` (the shipping desktop) or
# `shell` (Meridian). Defaults to the compositor, because testing on this machine is bare metal only
# and a shell that does not come up looks exactly like a kernel freeze from the outside. Reverting is
# this one variable, not a code change:
#
# WINDOW_SERVER=shell ./Build.sh
#
# apps/init execs the PATH, not a binary name, and nothing else refers to either binary — but note
# the window server is PID 4 (`COMPOSITOR_PID` in libs/gui/src/app.rs) purely by fork order, so it
# must stay the same position in init's spawn list.
WINDOW_SERVER="${WINDOW_SERVER:-compositor}"
if [ ! -f "target/x86_64-nyx/release/$WINDOW_SERVER" ]; then
echo " !! WINDOW_SERVER=$WINDOW_SERVER has no built binary; expected target/x86_64-nyx/release/$WINDOW_SERVER" >&2
exit 1
fi
echo " -> WindowServer.nyx = $WINDOW_SERVER"
cp "target/x86_64-nyx/release/$WINDOW_SERVER" build_initrd/apps/WindowServer.nyx/run.bin
# D3: terminal is a std binary now — its output lives under apps/terminal/target/ (workspace-excluded),
# not the root target dir.
cp apps/terminal/target/x86_64-unknown-nyx/release/nyx-terminal build_initrd/apps/Terminal.nyx/run.bin
Expand Down
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,25 @@ members = [
# Tools
"tools/compiler",
"tools/runner",
# Rasterizes the Meridian icon set out of the design document into a coverage table. Host-only,
# run by hand — see the module docs for why it is not a build.rs.
"tools/icons",

# Libraries
"libs/api",
"libs/gui",
# Meridian's foundation: tokens, the multi-size/multi-face glyph + icon atlas, shapes. A
# workspace member specifically so `cargo test -p nyx-meridian` runs on the host. Atlas packing
# and glyph metrics are exactly the kind of thing that looks fine and is subtly wrong, and this
# machine has no QEMU — a bug found here costs seconds, the same bug found on hardware costs a
# power cycle.
"libs/meridian",
# The Entity's creature: seed, genome, evolution, appearance. Independent of the shell and of
# every driver, so it is exercised entirely on the host — and it has to be, because its output is
# cross-checked cell for cell against the JavaScript generator in the design document. A creature
# that renders differently on the machine than in the document is not a rendering bug; it means
# the two implementations have silently forked and every Entity in existence has changed.
"libs/entity",
"libs/image",
"libs/toolchains",
# HTTP/TLS transport. A workspace member so `cargo test -p nyx-net` can exercise the URL and
Expand All @@ -22,6 +37,10 @@ members = [
# Applications
"apps/init",
"apps/compositor",
# The Meridian window server. Built ALONGSIDE apps/compositor, never in place of it: testing here
# is bare metal only, so a bad Meridian frame must not cost a working desktop. `Build.sh` picks
# which of the two becomes WindowServer.nyx.
"apps/shell",
# apps/terminal is now a std target_os=nyx binary (D3) — built standalone via Build-std.sh, so it
# lives in `exclude` below, not here.
"apps/settings",
Expand Down
58 changes: 31 additions & 27 deletions apps/compositor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ fn upload_arrow_cursor() {
for (r, row) in ARROW_HW.iter().enumerate() {
for (c, &p) in row.iter().enumerate() {
let color = match p {
1 => Color::TEXT_DARK, // already 0xFF-alpha ARGB
1 => Color::CURSOR_OUTLINE, // already 0xFF-alpha ARGB
2 => Color::WHITE,
_ => 0, // transparent
};
Expand All @@ -243,7 +243,8 @@ fn upload_arrow_cursor() {
}
}
}
sys_cursor_set_image(&img);
// The arrow's tip IS its top-left pixel, so the hotspot is the origin.
sys_cursor_set_image(&img, 0, 0);
}

// ── U7: resize-from-any-edge ────────────────────────────────────────────────
Expand Down Expand Up @@ -318,7 +319,7 @@ fn double_arrow(img: &mut [u32; 64 * 64], ux: f32, uy: f32) {
let (t1x, t1y) = (cx - ux * l, cy - uy * l);
let (t2x, t2y) = (cx + ux * l, cy + uy * l);
// Outline pass (dark, r=3) then fill pass (white, r=2).
for &(color, r) in &[(Color::TEXT_DARK, 3i32), (Color::WHITE, 2i32)] {
for &(color, r) in &[(Color::CURSOR_OUTLINE, 3i32), (Color::WHITE, 2i32)] {
thick_line(img, t1x as i32, t1y as i32, t2x as i32, t2y as i32, r, color);
// arrowhead barbs at tip2 (pointing +u) and tip1 (pointing -u)
let (bx, by) = (t2x - ux * bh, t2y - uy * bh);
Expand All @@ -342,16 +343,19 @@ fn upload_cursor(t: CursorType) {
CursorType::ResizeNESW => double_arrow(&mut img, 0.7071, -0.7071),
_ => { upload_arrow_cursor(); return; }
}
sys_cursor_set_image(&img);
// `double_arrow` centres its art on (16,16), which is where the pointer actually is. Until the
// driver carried a hotspot that offset was simply lost and these arrows sat 16px down-right of
// the edge they were reporting; now it is declared and subtracted.
sys_cursor_set_image(&img, 16, 16);
}

/// Draw the bottom taskbar strip: the opaque bar and its top border, nothing else. The Start button,
/// window buttons, clock and tray are drawn by the main pass, which has the compositor state (icon
/// bitmaps, link status, hover) this function doesn't.
fn render_taskbar(canvas: &mut Canvas, screen_stride: usize, screen_h: usize) {
let start_y = screen_h - BAR_H;
canvas.fill_rect(0, start_y, screen_stride, BAR_H, 0xFF_FFFFFF); // Opaque white taskbar
canvas.fill_rect(0, start_y, screen_stride, 1, 0xFF_D1D1D1); // Border
canvas.fill_rect(0, start_y, screen_stride, BAR_H, Color::SURFACE); // Taskbar strip
canvas.fill_rect(0, start_y, screen_stride, 1, Color::LINE); // Border
}

/// Signal strength to show for a link state, as (lit bars, colour). We have no RSSI from the driver,
Expand All @@ -360,9 +364,9 @@ fn render_taskbar(canvas: &mut Canvas, screen_stride: usize, screen_h: usize) {
fn wifi_indicator(state: u32) -> (usize, u32) {
match state {
WIFI_CONNECTED => (3, Color::ACCENT_GREEN),
WIFI_CONNECTING => (2, Color::ACCENT_PRIMARY),
WIFI_CONNECTING => (2, Color::ACCENT),
WIFI_AUTH_FAILED | WIFI_NO_LEASE | WIFI_HW_FAILED => (1, 0xFF_C0392B),
_ => (0, Color::TEXT_MUTED),
_ => (0, Color::FG_MUTED),
}
}

Expand All @@ -374,14 +378,14 @@ fn draw_wifi_glyph(canvas: &mut Canvas, x: usize, y: usize, state: u32) {
for i in 0..3 {
let h = 6 + i * 5;
let bx = x + i * 7;
canvas.fill_rect(bx, base - h, 5, h, 0xFF_DCDCD6);
canvas.fill_rect(bx, base - h, 5, h, Color::LINE);
if i < lit {
canvas.fill_rect(bx, base - h, 5, h, color);
}
}
// Not-connected reads as "off" rather than "weak": strike the track through.
if lit == 0 {
canvas.fill_rect(x, base - 4, 19, 2, Color::TEXT_MUTED);
canvas.fill_rect(x, base - 4, 19, 2, Color::FG_MUTED);
}
}

Expand Down Expand Up @@ -1278,9 +1282,9 @@ pub extern "C" fn _start() -> ! {
// 1. Submit GPU background fill (D1: whole screen on a full frame — byte-identical to before;
// only the damage rect on a partial frame). The fill re-lays wallpaper under the composite.
if full_frame {
sys_gpu_fill_rect(0, 0, screen_stride, screen_h, Color::WARM_BG);
sys_gpu_fill_rect(0, 0, screen_stride, screen_h, Color::SURFACE);
} else {
sys_gpu_fill_rect(dmg_x, dmg_y, dmg_w, dmg_h, Color::WARM_BG);
sys_gpu_fill_rect(dmg_x, dmg_y, dmg_w, dmg_h, Color::SURFACE);
}

// 2. Synchronize! Wait for GPU wallpaper clear to finish before CPU starts drawing
Expand Down Expand Up @@ -1417,9 +1421,9 @@ pub extern "C" fn _start() -> ! {
let is_hovered = state.hover_tb_btn == Some(ci);
// Active button accented; a hovered (non-active) one gets a soft accent tint;
// minimized dimmed; otherwise it sits flush with the bar.
let bg = if is_active { 0xFF_FDEBD8 }
else if is_hovered { 0xFF_F2F2EF }
else { 0xFF_FFFFFF };
let bg = if is_active { Color::LINE }
else if is_hovered { Color::RAISED }
else { Color::SURFACE };
fill_round_rect(&mut canvas, bx, btn_y, TB_BTN_W, TB_BTN_H, 6, bg);

match &client.icon {
Expand Down Expand Up @@ -1448,16 +1452,16 @@ pub extern "C" fn _start() -> ! {
let mut buf = [0u8; 4];
let s = ch.encode_utf8(&mut buf);
let tw = Canvas::text_width(s, 1);
let tcol = if win.is_minimized { Color::TEXT_MUTED } else { Color::TEXT_DARK };
let tcol = if win.is_minimized { Color::FG_MUTED } else { Color::FG };
canvas.print_str(bx + (TB_BTN_W.saturating_sub(tw)) / 2, btn_y + 6, s, tcol, 1);
}
}

// Running/active underline — the only cue left once the label is gone.
if is_active {
canvas.fill_rect(bx + 8, btn_y + TB_BTN_H - 2, TB_BTN_W - 16, 2, Color::ACCENT_PRIMARY);
canvas.fill_rect(bx + 8, btn_y + TB_BTN_H - 2, TB_BTN_W - 16, 2, Color::ACCENT);
} else if !win.is_minimized {
canvas.fill_rect(bx + 13, btn_y + TB_BTN_H - 2, TB_BTN_W - 26, 2, 0xFF_C8C8C2);
canvas.fill_rect(bx + 13, btn_y + TB_BTN_H - 2, TB_BTN_W - 26, 2, 0xFF_3A3E43);
}
}
}
Expand All @@ -1466,7 +1470,7 @@ pub extern "C" fn _start() -> ! {
// menu is open, so the button reflects the menu's state rather than being inert.
{
let (bx, by, bw, bh) = state.start_button_rect();
let bg = if state.start_menu_open { Color::ACCENT_HOVER } else { Color::ACCENT_PRIMARY };
let bg = if state.start_menu_open { Color::ACCENT_HOVER } else { Color::ACCENT };
fill_round_rect(&mut canvas, bx, by, bw, bh, 6, bg);
if let Some(logo) = &state.logo {
canvas.blit_rgba(bx + 8, by + (bh - LOGO_BTN) / 2, logo, LOGO_BTN, LOGO_BTN, None);
Expand Down Expand Up @@ -1571,7 +1575,7 @@ pub extern "C" fn _start() -> ! {
let (sx, sy, sw, sh) = state.menu_search_rect();
let ty = sy + sh / 2 - 8;
if state.search_len == 0 {
menu_text.push((sx + 30, ty, "Search apps", Color::TEXT_MUTED));
menu_text.push((sx + 30, ty, "Search apps", Color::FG_MUTED));
} else {
// The query is ASCII by construction (only 0x20..0x7F is accepted), so this is
// always valid UTF-8; the unwrap_or keeps a bad byte from panicking the shell.
Expand All @@ -1585,7 +1589,7 @@ pub extern "C" fn _start() -> ! {
let (gx, gy, _, _) = state.menu_tile_rect(0);
let w = Canvas::text_width(msg, 1);
menu_text.push((gx + (MENU_COLS * MENU_TILE_W).saturating_sub(w) / 2,
gy + 24, msg, Color::TEXT_MUTED));
gy + 24, msg, Color::FG_MUTED));
}
for (slot, &i) in hits.iter().enumerate() {
let (tx, ty, tw, th) = state.menu_tile_rect(slot);
Expand All @@ -1611,9 +1615,9 @@ pub extern "C" fn _start() -> ! {
let mut chrome_drawn = false;
if let Some(font) = &gpu_font {
let mut glyphs: Vec<GlyphQuad> = Vec::new();
glyphs.extend(font.layout_str(4, 2, ov_str, 1, Color::TEXT_MUTED));
glyphs.extend(font.layout_str(time_x as i32, ck_y as i32, clk_str, 1, Color::TEXT_DARK));
glyphs.extend(font.layout_str(date_x as i32, date_y as i32, date_str, 1, Color::TEXT_MUTED));
glyphs.extend(font.layout_str(4, 2, ov_str, 1, Color::FG_MUTED));
glyphs.extend(font.layout_str(time_x as i32, ck_y as i32, clk_str, 1, Color::FG));
glyphs.extend(font.layout_str(date_x as i32, date_y as i32, date_str, 1, Color::FG_MUTED));
glyphs.extend(font.layout_str(nyx_x as i32, nyx_y as i32, "NYX", 1, Color::WHITE));
for &(tx, ty, text, col) in menu_text.iter() {
glyphs.extend(font.layout_str(tx as i32, ty as i32, text, 1, col));
Expand All @@ -1625,9 +1629,9 @@ pub extern "C" fn _start() -> ! {
}
if !chrome_drawn {
// CPU fallback — identical layout/colours (never leave chrome blank).
canvas.print_str(4, 2, ov_str, Color::TEXT_MUTED, 1);
canvas.print_str(time_x, ck_y, clk_str, Color::TEXT_DARK, 1);
canvas.print_str(date_x, date_y, date_str, Color::TEXT_MUTED, 1);
canvas.print_str(4, 2, ov_str, Color::FG_MUTED, 1);
canvas.print_str(time_x, ck_y, clk_str, Color::FG, 1);
canvas.print_str(date_x, date_y, date_str, Color::FG_MUTED, 1);
canvas.print_str(nyx_x, nyx_y, "NYX", Color::WHITE, 1);
for &(tx, ty, text, col) in menu_text.iter() {
canvas.print_str(tx, ty, text, col, 1);
Expand Down
Loading
Loading