feat(live): v1.6.3 Plymouth 3D theme + kiosk fix

Plymouth:
- New secubox-3d theme with nested cubes + triple lemniscate
- 3 cubes (outer/middle/inner) with desync rotation
- AUTH/MIND/MESH colored lemniscates with glowing heads
- SecuBox color palette integration

Kiosk:
- Fix session script: use --start-fullscreen (not --kiosk)
- Allows Ctrl+Alt+Fn VT switching
- Clean exec chromium (no background process issues)
- Consistent session between build script and launcher

Build:
- Version bump to 1.6.3
- Plymouth theme set to secubox-3d

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-04-12 15:55:18 +02:00
parent 5dcc0619ed
commit aae1e2c73a
19 changed files with 1286 additions and 144 deletions

View File

@ -14,7 +14,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_DIR="$(dirname "$SCRIPT_DIR")"
# ── Version & Build Info ──────────────────────────────────────────
SECUBOX_VERSION="1.6.1"
SECUBOX_VERSION="1.6.3"
BUILD_TIMESTAMP=$(date '+%Y-%m-%d %H:%M')
BUILD_DATE=$(date '+%Y%m%d')
@ -260,11 +260,12 @@ DefaultTimeoutStartSec=30s
DefaultTimeoutStopSec=30s
EOF
# Disable console spam
# Disable console spam + enable SysRq for emergency recovery
cat > "${ROOTFS}/etc/sysctl.d/99-secubox.conf" <<EOF
kernel.consoleblank=0
net.ipv4.conf.all.log_martians=0
kernel.printk=1 1 1 1
kernel.sysrq=1
EOF
# ── Hardware Check Service ──────────────────────────────────────────
@ -738,23 +739,34 @@ chroot "${ROOTFS}" systemctl enable secubox-net-fallback.service 2>/dev/null ||
chroot "${ROOTFS}" systemctl disable systemd-networkd-wait-online.service 2>/dev/null || true
chroot "${ROOTFS}" systemctl mask systemd-networkd-wait-online.service 2>/dev/null || true
# ── Plymouth Boot Splash Theme (SecuBox Cube) ──────────────────────
log "Installing Plymouth SecuBox-Cube boot splash..."
# ── Plymouth Boot Splash Theme (SecuBox 3DS) ──────────────────────
log "Installing Plymouth SecuBox-3DS boot splash..."
# Create SecuBox-Cube Plymouth theme directory
PLYMOUTH_DIR="${ROOTFS}/usr/share/plymouth/themes/secubox-cube"
# Create SecuBox-3DS Plymouth theme directory (primary theme)
PLYMOUTH_DIR="${ROOTFS}/usr/share/plymouth/themes/secubox-3d"
mkdir -p "${PLYMOUTH_DIR}"
# Copy theme assets from source
# Copy 3DS theme assets from source
DS_SRC="${SCRIPT_DIR}/plymouth/secubox-3d"
if [[ -d "${DS_SRC}" ]]; then
cp "${DS_SRC}/secubox-3d.plymouth" "${PLYMOUTH_DIR}/"
cp "${DS_SRC}/secubox-3d.script" "${PLYMOUTH_DIR}/"
cp "${DS_SRC}"/*.png "${PLYMOUTH_DIR}/" 2>/dev/null || true
log " Copied 3DS theme assets from ${DS_SRC}"
fi
# Also install legacy cube theme as fallback
CUBE_DIR="${ROOTFS}/usr/share/plymouth/themes/secubox-cube"
mkdir -p "${CUBE_DIR}"
CUBE_SRC="${SCRIPT_DIR}/plymouth/secubox-cube"
if [[ -d "${CUBE_SRC}" ]]; then
cp "${CUBE_SRC}/secubox-cube.plymouth" "${PLYMOUTH_DIR}/"
cp "${CUBE_SRC}/secubox-cube.script" "${PLYMOUTH_DIR}/"
cp "${CUBE_SRC}/logo.png" "${PLYMOUTH_DIR}/"
cp "${CUBE_SRC}/scanlines.png" "${PLYMOUTH_DIR}/"
cp "${CUBE_SRC}/progress-bg.png" "${PLYMOUTH_DIR}/"
cp "${CUBE_SRC}/progress-fg.png" "${PLYMOUTH_DIR}/"
cp "${CUBE_SRC}"/icon-*.png "${PLYMOUTH_DIR}/"
cp "${CUBE_SRC}/secubox-cube.plymouth" "${CUBE_DIR}/"
cp "${CUBE_SRC}/secubox-cube.script" "${CUBE_DIR}/"
cp "${CUBE_SRC}/logo.png" "${CUBE_DIR}/"
cp "${CUBE_SRC}/scanlines.png" "${CUBE_DIR}/"
cp "${CUBE_SRC}/progress-bg.png" "${CUBE_DIR}/"
cp "${CUBE_SRC}/progress-fg.png" "${CUBE_DIR}/"
cp "${CUBE_SRC}"/icon-*.png "${CUBE_DIR}/"
log " Copied cube theme assets from ${CUBE_SRC}"
else
warn "Cube theme source not found at ${CUBE_SRC}, creating minimal theme..."
@ -813,19 +825,19 @@ Plymouth.SetMessageFunction(message_callback);
PLYSCRIPT
fi
# Set SecuBox-Cube theme as default
# Set SecuBox-3DS theme as default
mkdir -p "${ROOTFS}/etc/plymouth"
cat > "${ROOTFS}/etc/plymouth/plymouthd.conf" <<EOF
[Daemon]
Theme=secubox-cube
Theme=secubox-3d
ShowDelay=0
DeviceTimeout=8
EOF
# Update alternatives to use our theme
chroot "${ROOTFS}" plymouth-set-default-theme secubox-cube 2>/dev/null || true
chroot "${ROOTFS}" plymouth-set-default-theme secubox-3d 2>/dev/null || true
ok "Plymouth SecuBox-Cube theme installed"
ok "Plymouth SecuBox-3DS theme installed"
ok "Base configuration complete"
@ -1242,7 +1254,8 @@ lapi_key = ""
interface = "wg0"
listen_port = 51820
SECUBOXCONF
chmod 640 "${ROOTFS}/etc/secubox/secubox.conf"
chmod 644 "${ROOTFS}/etc/secubox/secubox.conf"
chroot "${ROOTFS}" chown root:secubox /etc/secubox/secubox.conf 2>/dev/null || true
log "Created default secubox.conf (admin/secubox)"
# Create users.json for portal authentication
@ -1264,7 +1277,8 @@ cat > "${ROOTFS}/etc/secubox/users.json" <<EOF
}
}
EOF
chmod 640 "${ROOTFS}/etc/secubox/users.json"
chmod 644 "${ROOTFS}/etc/secubox/users.json"
chroot "${ROOTFS}" chown root:secubox /etc/secubox/users.json 2>/dev/null || true
log "Created users.json (admin/secubox, root/secubox)"
# ── Restore real systemctl ─────────────────────────────────────────
@ -1626,27 +1640,33 @@ XCONF
mkdir -p "${ROOTFS}/home/secubox-kiosk/.config"
cat > "${ROOTFS}/home/secubox-kiosk/.xsession" <<'XSESSION'
#!/bin/bash
# SecuBox Kiosk X Session - Minimal version
exec 2>&1 | logger -t secubox-kiosk &
# SecuBox Kiosk X Session v1.6.3
export DISPLAY=${DISPLAY:-:0}
echo "Starting kiosk session..."
logger -t secubox-kiosk "Starting kiosk session..."
# Basic X settings (ignore errors)
xset s off 2>/dev/null
xset -dpms 2>/dev/null
xset s off 2>/dev/null || true
xset -dpms 2>/dev/null || true
xset s noblank 2>/dev/null || true
# URL to display - use localhost for universal compatibility (works without network)
# URL to display - use localhost for universal compatibility
URL="https://localhost/"
# Try Chromium with minimal flags
# Chromium with VT-switch-friendly flags (--start-fullscreen, not --kiosk)
# exec replaces shell with chromium - on exit, X session ends
exec chromium \
--kiosk \
--start-fullscreen \
--no-first-run \
--no-sandbox \
--disable-gpu \
--disable-pinch \
--noerrdialogs \
--disable-translate \
--ignore-certificate-errors \
--disable-features=TranslateUI \
--window-position=0,0 \
"$URL" || exec xterm -fullscreen -e "echo 'Chromium failed'; sleep 999"
"$URL"
XSESSION
chmod +x "${ROOTFS}/home/secubox-kiosk/.xsession"
# Create symlink for xinit compatibility
@ -1726,9 +1746,10 @@ XCONF
xinit /bin/bash -c "
xset s off 2>/dev/null
xset -dpms 2>/dev/null
exec chromium --kiosk --no-first-run --no-sandbox --disable-gpu \
exec chromium --start-fullscreen --no-first-run --no-sandbox --disable-gpu \
--disable-pinch --noerrdialogs --disable-translate \
--ignore-certificate-errors --window-position=0,0 '$URL'
" -- :0 vt7 -nolisten tcp 2>/dev/null &
" -- :0 vt7 -nolisten tcp -keeptty 2>/dev/null &
sleep 3
chvt 7 2>/dev/null
) &
@ -2140,48 +2161,46 @@ rm -rf "${ROOTFS}/tmp"/*
# ── CRT-Style Boot Banner with Colors and Emojis ──────────────────────────────
log "Creating colorful boot banners..."
# Pre-login banner (/etc/issue) - shown before login prompt
cat > "${ROOTFS}/etc/issue" <<ISSUE
[38;5;214m
# Pre-login banner (/etc/issue) - use printf %b for real escape sequences
printf '%b' "\e[38;5;214m
██████ ███████ ██████ ██ ██ ██████ ██████ ██ ██
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
███████ █████ ██ ██ ██ ██████ ██ ██ ███
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
███████ ███████ ██████ ██████ ██████ ██████ ██ ██
[0m
[38;5;45m ⚡ CyberMind Security Platform[0m [38;5;82mv${SECUBOX_VERSION}[0m [38;5;242m\l @ \n[0m
[38;5;242m Build: ${BUILD_TIMESTAMP}[0m
\e[0m
\e[38;5;45m ⚡ CyberMind Security Platform\e[0m \e[38;5;82mv${SECUBOX_VERSION}\e[0m \e[38;5;242m\\l @ \\n\e[0m
\e[38;5;242m Build: ${BUILD_TIMESTAMP}\e[0m
[38;5;250m 🔐 Default: [38;5;214mroot[38;5;250m / [38;5;214msecubox[0m
[38;5;250m 🌐 Web UI: [38;5;45mhttps://<IP>:9443[0m
[38;5;250m 📡 SSH: [38;5;45mport 22[0m
\e[38;5;250m 🔐 Default: \e[38;5;214mroot\e[38;5;250m / \e[38;5;214msecubox\e[0m
\e[38;5;250m 🌐 Web UI: \e[38;5;45mhttps://<IP>:9443\e[0m
\e[38;5;250m 📡 SSH: \e[38;5;45mport 22\e[0m
[38;5;242m─────────────────────────────────────────────────────────────[0m
\e[38;5;242m─────────────────────────────────────────────────────────────\e[0m
ISSUE
" > "${ROOTFS}/etc/issue"
# Post-login MOTD with dynamic info (/etc/motd)
cat > "${ROOTFS}/etc/motd" <<MOTD
[38;5;214m
# Post-login MOTD - use printf %b for real escape sequences
printf '%b' "\e[38;5;214m
╔═══════════════════════════════════════════════════════════════╗
[38;5;45m ███████╗███████╗ ██████╗██╗ ██╗██████╗ ██████╗ ██╗ ██╗ [38;5;214m║
[38;5;45m ██╔════╝██╔════╝██╔════╝██║ ██║██╔══██╗██╔═══██╗╚██╗██╔╝ [38;5;214m║
[38;5;45m ███████╗█████╗ ██║ ██║ ██║██████╔╝██║ ██║ ╚███╔╝ [38;5;214m║
[38;5;45m ╚════██║██╔══╝ ██║ ██║ ██║██╔══██╗██║ ██║ ██╔██╗ [38;5;214m║
[38;5;45m ███████║███████╗╚██████╗╚██████╔╝██████╔╝╚██████╔╝██╔╝ ██╗ [38;5;214m║
[38;5;45m ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ [38;5;214m║
[38;5;82m ⚡ LIVE USB MODE ⚡ v${SECUBOX_VERSION} [38;5;214m║
╚═══════════════════════════════════════════════════════════════╝[0m
\e[38;5;45m ███████╗███████╗ ██████╗██╗ ██╗██████╗ ██████╗ ██╗ ██╗ \e[38;5;214m║
\e[38;5;45m ██╔════╝██╔════╝██╔════╝██║ ██║██╔══██╗██╔═══██╗╚██╗██╔╝ \e[38;5;214m║
\e[38;5;45m ███████╗█████╗ ██║ ██║ ██║██████╔╝██║ ██║ ╚███╔╝ \e[38;5;214m║
\e[38;5;45m ╚════██║██╔══╝ ██║ ██║ ██║██╔══██╗██║ ██║ ██╔██╗ \e[38;5;214m║
\e[38;5;45m ███████║███████╗╚██████╗╚██████╔╝██████╔╝╚██████╔╝██╔╝ ██╗ \e[38;5;214m║
\e[38;5;45m ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ \e[38;5;214m║
\e[38;5;82m ⚡ LIVE USB MODE ⚡ v${SECUBOX_VERSION} \e[38;5;214m║
╚═══════════════════════════════════════════════════════════════╝\e[0m
[38;5;242m Build: ${BUILD_TIMESTAMP}[0m
\e[38;5;242m Build: ${BUILD_TIMESTAMP}\e[0m
[38;5;250m 🌐 Web UI: [38;5;45mhttps://<IP>:9443[0m
[38;5;250m 🔐 Credentials: [38;5;214mroot[38;5;250m / [38;5;214msecubox[0m
[38;5;250m 📖 Docs: [38;5;45mhttps://secubox.in/docs[0m
\e[38;5;250m 🌐 Web UI: \e[38;5;45mhttps://<IP>:9443\e[0m
\e[38;5;250m 🔐 Credentials: \e[38;5;214mroot\e[38;5;250m / \e[38;5;214msecubox\e[0m
\e[38;5;250m 📖 Docs: \e[38;5;45mhttps://secubox.in/docs\e[0m
[38;5;242m Type [38;5;82msecubox-status[38;5;242m for system overview[0m
\e[38;5;242m Type \e[38;5;82msecubox-status\e[38;5;242m for system overview\e[0m
MOTD
" > "${ROOTFS}/etc/motd"
# Dynamic status script for interactive use
cat > "${ROOTFS}/usr/bin/secubox-status" <<'STATUS_SCRIPT'
@ -2261,6 +2280,34 @@ echo ""
STATUS_SCRIPT
chmod +x "${ROOTFS}/usr/bin/secubox-status"
# Password reset script for TTY recovery
cat > "${ROOTFS}/usr/bin/secubox-passwd" <<'PASSWD_SCRIPT'
#!/bin/bash
# SecuBox Password Reset
set -e
USERS_FILE="/etc/secubox/users.json"
CONF_FILE="/etc/secubox/secubox.conf"
echo "SecuBox Password Reset"
[[ $EUID -ne 0 ]] && echo "Error: Run as root" && exit 1
read -sp "New password for admin: " NEW_PASS && echo
read -sp "Confirm password: " CONFIRM && echo
[[ "$NEW_PASS" != "$CONFIRM" ]] && echo "Passwords don't match" && exit 1
[[ ${#NEW_PASS} -lt 4 ]] && echo "Password too short" && exit 1
NEW_HASH=$(echo -n "$NEW_PASS" | sha256sum | cut -d' ' -f1)
if [[ -f "$USERS_FILE" ]]; then
sed -i "s/\"password_hash\": \"[^\"]*\"/\"password_hash\": \"${NEW_HASH}\"/g" "$USERS_FILE"
echo "Updated users.json"
fi
if [[ -f "$CONF_FILE" ]]; then
sed -i "s/^password = .*/password = \"${NEW_PASS}\"/" "$CONF_FILE"
echo "Updated secubox.conf"
fi
systemctl restart secubox-portal secubox-hub 2>/dev/null || true
echo "Password reset complete!"
PASSWD_SCRIPT
chmod +x "${ROOTFS}/usr/bin/secubox-passwd"
ok "Password reset script installed (secubox-passwd)"
# Also add a boot-time banner display script
cat > "${ROOTFS}/usr/sbin/secubox-boot-banner" <<'BOOT_BANNER'
#!/bin/bash

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

View File

@ -0,0 +1,8 @@
[Plymouth Theme]
Name=SecuBox 3D
Description=Triple lemniscate + nested cubes boot splash
ModuleName=script
[script]
ImageDir=/usr/share/plymouth/themes/secubox-3d
ScriptFile=/usr/share/plymouth/themes/secubox-3d/secubox-3d.script

View File

@ -0,0 +1,429 @@
/* SecuBox 3D - Plymouth Theme Script
* Triple Lemniscate + Nested Cubes (gigognes)
* Matching secubox_v5_stereo.html aesthetic
* CyberMind - https://cybermind.fr
*/
/* ═══════════════════════════════════════════════════════════════
* SecuBox Color Palette (from FD[] face definitions)
* ═══════════════════════════════════════════════════════════════ */
/* AUTH #FF6910 */ col_auth_r = 1.000; col_auth_g = 0.412; col_auth_b = 0.063;
/* WALL #FFB300 */ col_wall_r = 1.000; col_wall_g = 0.702; col_wall_b = 0.000;
/* BOOT #FF3333 */ col_boot_r = 1.000; col_boot_g = 0.200; col_boot_b = 0.200;
/* MIND #9B7EFF */ col_mind_r = 0.608; col_mind_g = 0.494; col_mind_b = 1.000;
/* ROOT #33FF33 */ col_root_r = 0.200; col_root_g = 1.000; col_root_b = 0.200;
/* MESH #3AB4FF */ col_mesh_r = 0.227; col_mesh_g = 0.706; col_mesh_b = 1.000;
/* ═══════════════════════════════════════════════════════════════
* Screen Setup - Dark cyber background (#07080f)
* ═══════════════════════════════════════════════════════════════ */
Window.SetBackgroundTopColor(0.027, 0.031, 0.059);
Window.SetBackgroundBottomColor(0.012, 0.016, 0.035);
screen_width = Window.GetWidth();
screen_height = Window.GetHeight();
center_x = screen_width / 2;
center_y = screen_height / 2;
/* ═══════════════════════════════════════════════════════════════
* Load Assets
* ═══════════════════════════════════════════════════════════════ */
progress_bg = Image("progress-bg.png");
progress_fg = Image("progress-fg.png");
/* ═══════════════════════════════════════════════════════════════
* 3 Nested Cubes Configuration (gigognes)
* Outer=0, Middle=1, Inner=2
* ═══════════════════════════════════════════════════════════════ */
/* Cube sizes (matching web: 256/2, 128/2, 64/2 scaled for plymouth) */
cube_size[0] = 110; /* Outer - largest */
cube_size[1] = 70; /* Middle */
cube_size[2] = 40; /* Inner - smallest */
/* Desynchronized rotation speeds (BASE_SPD from web) */
/* Outer: slowest, most majestic */
spd_x[0] = 0.004; spd_y[0] = 0.006; spd_z[0] = 0.003;
/* Middle: medium */
spd_x[1] = 0.007; spd_y[1] = 0.005; spd_z[1] = 0.006;
/* Inner: fastest, most dynamic */
spd_x[2] = 0.012; spd_y[2] = 0.010; spd_z[2] = 0.008;
/* Rotation angles (offset for visual interest at start) */
rot_x[0] = 0.0; rot_y[0] = 0.0; rot_z[0] = 0.0;
rot_x[1] = 0.8; rot_y[1] = 0.4; rot_z[1] = 0.2;
rot_x[2] = 1.6; rot_y[2] = 1.2; rot_z[2] = 0.6;
/* Cube edge colors (gradient from outer to inner) */
/* Outer: dim green-cyan */
cube_r[0] = 0.15; cube_g[0] = 0.45; cube_b[0] = 0.35;
/* Middle: brighter cyan */
cube_r[1] = 0.20; cube_g[1] = 0.60; cube_b[1] = 0.50;
/* Inner: bright SecuBox green */
cube_r[2] = 0.25; cube_g[2] = 0.85; cube_b[2] = 0.45;
/* 8 vertices per cube × 3 cubes = 24 vertex sprites */
for (c = 0; c < 3; c++) {
for (v = 0; v < 8; v++) {
idx = c * 8 + v;
cube_verts[idx] = Sprite();
cube_verts[idx].SetZ(10 + c * 3);
}
}
/* 12 edges per cube × 3 cubes = 36 edge midpoint sprites */
for (c = 0; c < 3; c++) {
for (e = 0; e < 12; e++) {
idx = c * 12 + e;
cube_edges[idx] = Sprite();
cube_edges[idx].SetZ(9 + c * 3);
}
}
/* ═══════════════════════════════════════════════════════════════
* Triple Lemniscate Configuration
* 3 branches: AUTH (orange), MIND (purple), MESH (blue)
* ═══════════════════════════════════════════════════════════════ */
lemniscate_scale = 95;
/* Branch colors */
lem_r[0] = col_auth_r; lem_g[0] = col_auth_g; lem_b[0] = col_auth_b; /* AUTH orange */
lem_r[1] = col_mind_r; lem_g[1] = col_mind_g; lem_b[1] = col_mind_b; /* MIND purple */
lem_r[2] = col_mesh_r; lem_g[2] = col_mesh_g; lem_b[2] = col_mesh_b; /* MESH blue */
/* Head speeds (desynchronized) */
lem_speed[0] = 0.028;
lem_speed[1] = 0.022;
lem_speed[2] = 0.019;
/* Head positions (start offset for visual variety) */
lem_pos[0] = 0.0;
lem_pos[1] = 2.094; /* 2π/3 */
lem_pos[2] = 4.188; /* 4π/3 */
/* Trail dots per branch */
trail_len = 15;
/* Lemniscate sprites: 3 branches × (trail_len + 1 head) */
for (b = 0; b < 3; b++) {
for (d = 0; d < trail_len; d++) {
idx = b * trail_len + d;
lem_trail[idx] = Sprite();
lem_trail[idx].SetZ(5);
}
lem_head[b] = Sprite();
lem_head[b].SetZ(8);
}
/* ═══════════════════════════════════════════════════════════════
* Progress Bar
* ═══════════════════════════════════════════════════════════════ */
progress_bg_sprite = Sprite();
progress_fg_sprite = Sprite();
if (progress_bg) {
progress_bg_sprite.SetImage(progress_bg);
}
progress_bg_sprite.SetX(center_x - 130);
progress_bg_sprite.SetY(center_y + 170);
progress_bg_sprite.SetZ(50);
if (progress_fg) {
progress_fg_sprite.SetImage(progress_fg);
}
progress_fg_sprite.SetX(center_x - 128);
progress_fg_sprite.SetY(center_y + 172);
progress_fg_sprite.SetZ(51);
/* Status message */
message_sprite = Sprite();
message_sprite.SetZ(52);
/* ═══════════════════════════════════════════════════════════════
* Animation State
* ═══════════════════════════════════════════════════════════════ */
frame = 0;
boot_progress = 0;
/* ═══════════════════════════════════════════════════════════════
* Unit Cube Vertices (±1, ±1, ±1)
* ═══════════════════════════════════════════════════════════════ */
uv_x[0] = -1; uv_y[0] = -1; uv_z[0] = -1;
uv_x[1] = 1; uv_y[1] = -1; uv_z[1] = -1;
uv_x[2] = 1; uv_y[2] = 1; uv_z[2] = -1;
uv_x[3] = -1; uv_y[3] = 1; uv_z[3] = -1;
uv_x[4] = -1; uv_y[4] = -1; uv_z[4] = 1;
uv_x[5] = 1; uv_y[5] = -1; uv_z[5] = 1;
uv_x[6] = 1; uv_y[6] = 1; uv_z[6] = 1;
uv_x[7] = -1; uv_y[7] = 1; uv_z[7] = 1;
/* Edge definitions (vertex pairs) */
edge_a[0]=0; edge_b[0]=1; edge_a[1]=1; edge_b[1]=2; edge_a[2]=2; edge_b[2]=3; edge_a[3]=3; edge_b[3]=0;
edge_a[4]=4; edge_b[4]=5; edge_a[5]=5; edge_b[5]=6; edge_a[6]=6; edge_b[6]=7; edge_a[7]=7; edge_b[7]=4;
edge_a[8]=0; edge_b[8]=4; edge_a[9]=1; edge_b[9]=5; edge_a[10]=2; edge_b[10]=6; edge_a[11]=3; edge_b[11]=7;
/* ═══════════════════════════════════════════════════════════════
* 3D Rotation Function
* ═══════════════════════════════════════════════════════════════ */
fun rotate3d(px, py, pz, ax, ay, az) {
/* Rotate X */
cx = Math.Cos(ax); sx = Math.Sin(ax);
y1 = py * cx - pz * sx;
z1 = py * sx + pz * cx;
/* Rotate Y */
cy = Math.Cos(ay); sy = Math.Sin(ay);
x2 = px * cy + z1 * sy;
z2 = -px * sy + z1 * cy;
/* Rotate Z */
cz = Math.Cos(az); sz = Math.Sin(az);
x3 = x2 * cz - y1 * sz;
y3 = x2 * sz + y1 * cz;
result[0] = x3;
result[1] = y3;
result[2] = z2;
return result;
}
/* ═══════════════════════════════════════════════════════════════
* Lemniscate Parametric (Bernoulli)
* x = a * cos(t) / (1 + sin²(t))
* y = a * sin(t) * cos(t) / (1 + sin²(t))
* ═══════════════════════════════════════════════════════════════ */
fun lem_x(t, scale) {
st = Math.Sin(t);
ct = Math.Cos(t);
d = 1 + st * st;
return scale * ct / d;
}
fun lem_y(t, scale) {
st = Math.Sin(t);
ct = Math.Cos(t);
d = 1 + st * st;
return scale * st * ct / d;
}
/* ═══════════════════════════════════════════════════════════════
* Draw Single Cube
* ═══════════════════════════════════════════════════════════════ */
fun draw_cube(cube_idx) {
ax = global.rot_x[cube_idx];
ay = global.rot_y[cube_idx];
az = global.rot_z[cube_idx];
size = global.cube_size[cube_idx] / 2;
cr = global.cube_r[cube_idx];
cg = global.cube_g[cube_idx];
cb = global.cube_b[cube_idx];
/* Compute projected vertices */
for (v = 0; v < 8; v++) {
px = global.uv_x[v] * size;
py = global.uv_y[v] * size;
pz = global.uv_z[v] * size;
rot = rotate3d(px, py, pz, ax, ay, az);
/* Store projected coords */
proj_x[v] = global.center_x + rot[0];
proj_y[v] = global.center_y + rot[1];
proj_z[v] = rot[2];
/* Depth-based brightness */
depth = (rot[2] + size) / (size * 2);
bright = 0.4 + depth * 0.6;
/* Draw vertex dot */
idx = cube_idx * 8 + v;
dot_img = Image.Text("●", cr * bright, cg * bright, cb * bright, 1);
global.cube_verts[idx].SetImage(dot_img);
global.cube_verts[idx].SetX(proj_x[v] - 4);
global.cube_verts[idx].SetY(proj_y[v] - 4);
global.cube_verts[idx].SetOpacity(0.6 + depth * 0.4);
}
/* Draw edges (midpoints) */
for (e = 0; e < 12; e++) {
va = global.edge_a[e];
vb = global.edge_b[e];
/* Edge midpoint */
mx = (proj_x[va] + proj_x[vb]) / 2;
my = (proj_y[va] + proj_y[vb]) / 2;
mz = (proj_z[va] + proj_z[vb]) / 2;
depth = (mz + size) / (size * 2);
bright = 0.3 + depth * 0.5;
idx = cube_idx * 12 + e;
edge_img = Image.Text("·", cr * bright, cg * bright, cb * bright, 1);
global.cube_edges[idx].SetImage(edge_img);
global.cube_edges[idx].SetX(mx - 2);
global.cube_edges[idx].SetY(my - 2);
global.cube_edges[idx].SetOpacity(0.4 + depth * 0.3);
}
}
/* ═══════════════════════════════════════════════════════════════
* Draw Triple Lemniscate
* ═══════════════════════════════════════════════════════════════ */
fun draw_lemniscates() {
/* Gentle twist animation */
twist = Math.Sin(global.frame * 0.008) * 0.3;
for (b = 0; b < 3; b++) {
/* Update head position */
global.lem_pos[b] += global.lem_speed[b];
if (global.lem_pos[b] > 6.28318) {
global.lem_pos[b] -= 6.28318;
}
lr = global.lem_r[b];
lg = global.lem_g[b];
lb = global.lem_b[b];
/* Draw trail */
for (d = 0; d < global.trail_len; d++) {
t = global.lem_pos[b] - d * 0.25;
/* 3D lemniscate with twist */
lx = lem_x(t, global.lemniscate_scale);
ly = lem_y(t, global.lemniscate_scale);
lz = Math.Sin(t * 2 + twist) * 15; /* Z oscillation */
/* Simple rotation for 3D effect */
rot = rotate3d(lx, ly, lz, twist * 0.5, global.frame * 0.003, 0);
sx = global.center_x + rot[0];
sy = global.center_y + rot[1];
/* Fade trail */
fade = 1.0 - (d / global.trail_len);
idx = b * global.trail_len + d;
dot_img = Image.Text("•", lr * fade, lg * fade, lb * fade, 1);
global.lem_trail[idx].SetImage(dot_img);
global.lem_trail[idx].SetX(sx - 3);
global.lem_trail[idx].SetY(sy - 3);
global.lem_trail[idx].SetOpacity(fade * 0.7);
}
/* Draw glowing head */
ht = global.lem_pos[b];
hx = lem_x(ht, global.lemniscate_scale);
hy = lem_y(ht, global.lemniscate_scale);
hz = Math.Sin(ht * 2 + twist) * 15;
hrot = rotate3d(hx, hy, hz, twist * 0.5, global.frame * 0.003, 0);
hsx = global.center_x + hrot[0];
hsy = global.center_y + hrot[1];
/* Pulsing glow */
pulse = 0.85 + Math.Sin(global.frame * 0.15 + b) * 0.15;
head_img = Image.Text("◉", lr * pulse, lg * pulse, lb * pulse, 1);
global.lem_head[b].SetImage(head_img);
global.lem_head[b].SetX(hsx - 5);
global.lem_head[b].SetY(hsy - 5);
global.lem_head[b].SetOpacity(1.0);
}
}
/* ═══════════════════════════════════════════════════════════════
* Main Animation Loop
* ═══════════════════════════════════════════════════════════════ */
fun refresh_callback() {
global.frame++;
/* Update cube rotations (desynchronized) */
for (c = 0; c < 3; c++) {
global.rot_x[c] += global.spd_x[c];
global.rot_y[c] += global.spd_y[c];
global.rot_z[c] += global.spd_z[c];
if (global.rot_x[c] > 6.28318) global.rot_x[c] -= 6.28318;
if (global.rot_y[c] > 6.28318) global.rot_y[c] -= 6.28318;
if (global.rot_z[c] > 6.28318) global.rot_z[c] -= 6.28318;
draw_cube(c);
}
/* Draw triple lemniscate */
draw_lemniscates();
}
Plymouth.SetRefreshFunction(refresh_callback);
/* ═══════════════════════════════════════════════════════════════
* Plymouth Callbacks
* ═══════════════════════════════════════════════════════════════ */
fun boot_progress_callback(duration, progress_val) {
global.boot_progress = progress_val;
if (progress_fg) {
bar_width = Math.Int(progress_val * 256);
if (bar_width > 0) {
scaled = progress_fg.Scale(bar_width, 12);
progress_fg_sprite.SetImage(scaled);
}
}
}
Plymouth.SetBootProgressFunction(boot_progress_callback);
fun message_callback(text) {
/* SecuBox green text */
msg_img = Image.Text(text, 0.2, 0.85, 0.4, "Mono 10");
message_sprite.SetImage(msg_img);
message_sprite.SetX(center_x - msg_img.GetWidth() / 2);
message_sprite.SetY(center_y + 195);
}
Plymouth.SetMessageFunction(message_callback);
fun display_normal_callback() {
/* Normal boot display */
}
fun display_password_callback(prompt, bullets) {
/* LUKS password prompt */
prompt_img = Image.Text(prompt, col_mesh_r, col_mesh_g, col_mesh_b, "Sans Bold 11");
message_sprite.SetImage(prompt_img);
message_sprite.SetX(center_x - prompt_img.GetWidth() / 2);
message_sprite.SetY(center_y + 150);
}
Plymouth.SetDisplayNormalFunction(display_normal_callback);
Plymouth.SetDisplayPasswordFunction(display_password_callback);
fun quit_callback() {
/* Fade out */
for (c = 0; c < 3; c++) {
for (v = 0; v < 8; v++) {
idx = c * 8 + v;
global.cube_verts[idx].SetOpacity(0);
}
for (e = 0; e < 12; e++) {
idx = c * 12 + e;
global.cube_edges[idx].SetOpacity(0);
}
}
for (b = 0; b < 3; b++) {
for (d = 0; d < global.trail_len; d++) {
idx = b * global.trail_len + d;
global.lem_trail[idx].SetOpacity(0);
}
global.lem_head[b].SetOpacity(0);
}
progress_bg_sprite.SetOpacity(0);
progress_fg_sprite.SetOpacity(0);
message_sprite.SetOpacity(0);
}
Plymouth.SetQuitFunction(quit_callback);

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

View File

@ -0,0 +1,8 @@
[Plymouth Theme]
Name=SecuBox 3DS
Description=Nintendo 3DS dual-screen aesthetic with nested cubes and lemniscates
ModuleName=script
[script]
ImageDir=/usr/share/plymouth/themes/secubox-3ds
ScriptFile=/usr/share/plymouth/themes/secubox-3ds/secubox-3ds.script

View File

@ -0,0 +1,506 @@
/* SecuBox 3DS - Plymouth Theme Script
* Nintendo 3DS dual-screen aesthetic
* Upper: 3 nested cubes + 2 lemniscates
* Lower: Kernel log + progress bar
* CyberMind - https://cybermind.fr
*/
/* ═══════════════════════════════════════════════════════════════
* 3DS Color Palette
* ═══════════════════════════════════════════════════════════════ */
/* Deep blue gradient: #0033aa (dark) → #1a6fff (bright) */
blue_dark_r = 0.000; blue_dark_g = 0.200; blue_dark_b = 0.667; /* #0033aa */
blue_mid_r = 0.082; blue_mid_g = 0.333; blue_mid_b = 0.835; /* #1555d5 */
blue_bright_r = 0.102; blue_bright_g = 0.435; blue_bright_b = 1.0; /* #1a6fff */
/* Log colors */
log_blue_r = 0.4; log_blue_g = 0.6; log_blue_b = 1.0; /* info */
log_green_r = 0.4; log_green_g = 1.0; log_green_b = 0.6; /* ok */
log_amber_r = 1.0; log_amber_g = 0.8; log_amber_b = 0.3; /* warn */
/* ═══════════════════════════════════════════════════════════════
* Screen Setup - Dual screen simulation
* ═══════════════════════════════════════════════════════════════ */
Window.SetBackgroundTopColor(0.02, 0.02, 0.04); /* Near black */
Window.SetBackgroundBottomColor(0.01, 0.01, 0.02);
screen_width = Window.GetWidth();
screen_height = Window.GetHeight();
/* 3DS aspect simulation - upper screen takes 55% height */
upper_height = screen_height * 0.55;
lower_height = screen_height * 0.45;
divider_y = upper_height;
/* Centers for each "screen" */
upper_center_x = screen_width / 2;
upper_center_y = upper_height / 2;
lower_center_x = screen_width / 2;
lower_center_y = divider_y + (lower_height / 2);
/* ═══════════════════════════════════════════════════════════════
* Load Assets
* ═══════════════════════════════════════════════════════════════ */
/* Cube wireframe edges - rendered as sprites */
cube_edge = Image("cube-edge.png");
cube_corner = Image("cube-corner.png");
lemniscate_dot = Image("lemniscate-dot.png");
divider_line = Image("divider.png");
progress_bg = Image("progress-bg.png");
progress_fg = Image("progress-fg.png");
/* ═══════════════════════════════════════════════════════════════
* Screen Divider (3DS hinge simulation)
* ═══════════════════════════════════════════════════════════════ */
divider_sprite = Sprite();
divider_img = Image.Text("", 1, 1, 1, 1); /* Placeholder if no image */
if (divider_line) {
divider_sprite.SetImage(divider_line);
} else {
/* Create a simple dark blue line */
divider_sprite.SetImage(divider_img);
}
divider_sprite.SetX(0);
divider_sprite.SetY(divider_y - 2);
divider_sprite.SetZ(50);
divider_sprite.SetOpacity(0.8);
/* ═══════════════════════════════════════════════════════════════
* UPPER SCREEN - 3 Nested Cubes
* ═══════════════════════════════════════════════════════════════ */
/* Cube sizes (nested: large → medium → small) */
cube_sizes[0] = 100; /* Outer */
cube_sizes[1] = 65; /* Middle */
cube_sizes[2] = 35; /* Inner */
/* Rotation speeds (desynchronized) */
cube_speed_x[0] = 0.004; cube_speed_y[0] = 0.007; cube_speed_z[0] = 0.003;
cube_speed_x[1] = 0.009; cube_speed_y[1] = 0.005; cube_speed_z[1] = 0.008;
cube_speed_x[2] = 0.015; cube_speed_y[2] = 0.012; cube_speed_z[2] = 0.006;
/* Rotation angles */
cube_angle_x[0] = 0; cube_angle_y[0] = 0; cube_angle_z[0] = 0;
cube_angle_x[1] = 1.0; cube_angle_y[1] = 0.5; cube_angle_z[1] = 0.3;
cube_angle_x[2] = 2.0; cube_angle_y[2] = 1.5; cube_angle_z[2] = 1.0;
/* Cube edge sprites (12 edges × 3 cubes = 36 sprites) */
/* We'll draw simplified: 8 corner dots per cube */
for (c = 0; c < 3; c++) {
for (v = 0; v < 8; v++) {
idx = c * 8 + v;
cube_vertices[idx] = Sprite();
cube_vertices[idx].SetZ(20 + c * 5);
}
}
/* ═══════════════════════════════════════════════════════════════
* UPPER SCREEN - 2 Lemniscates (∞ curves)
* ═══════════════════════════════════════════════════════════════ */
/* Lemniscate parameters */
lemniscate_scale = 80; /* Size of the infinity symbol */
lemniscate_y_offset[0] = -30; /* Above center */
lemniscate_y_offset[1] = 30; /* Below center */
/* Each lemniscate has multiple trail dots + 1 bright head */
dots_per_curve = 12;
head_speed[0] = 0.025;
head_speed[1] = 0.018; /* Different speed for visual interest */
head_pos[0] = 0;
head_pos[1] = 3.14159; /* Start opposite */
/* Lemniscate dot sprites */
for (l = 0; l < 2; l++) {
for (d = 0; d < dots_per_curve; d++) {
idx = l * dots_per_curve + d;
lem_dots[idx] = Sprite();
lem_dots[idx].SetZ(15);
}
/* Bright head */
lem_heads[l] = Sprite();
lem_heads[l].SetZ(18);
}
/* ═══════════════════════════════════════════════════════════════
* LOWER SCREEN - Kernel Log Area
* ═══════════════════════════════════════════════════════════════ */
/* Log line sprites (scrolling buffer) */
max_log_lines = 8;
for (i = 0; i < max_log_lines; i++) {
log_sprites[i] = Sprite();
log_sprites[i].SetZ(30);
}
log_texts[0] = "";
log_texts[1] = "";
log_texts[2] = "";
log_texts[3] = "";
log_texts[4] = "";
log_texts[5] = "";
log_texts[6] = "";
log_texts[7] = "";
log_index = 0;
/* Progress bar */
progress_bg_sprite = Sprite();
progress_fg_sprite = Sprite();
if (progress_bg) {
progress_bg_sprite.SetImage(progress_bg);
}
progress_bg_sprite.SetX(lower_center_x - 120);
progress_bg_sprite.SetY(lower_center_y + 70);
progress_bg_sprite.SetZ(35);
if (progress_fg) {
progress_fg_sprite.SetImage(progress_fg);
}
progress_fg_sprite.SetX(lower_center_x - 118);
progress_fg_sprite.SetY(lower_center_y + 72);
progress_fg_sprite.SetZ(36);
/* Phase message */
phase_sprite = Sprite();
phase_sprite.SetZ(40);
current_phase = "Initializing...";
/* ═══════════════════════════════════════════════════════════════
* Animation State
* ═══════════════════════════════════════════════════════════════ */
frame = 0;
boot_progress = 0;
/* ═══════════════════════════════════════════════════════════════
* 3D Math - Cube vertex projection
* ═══════════════════════════════════════════════════════════════ */
/* Unit cube vertices (±1, ±1, ±1) */
unit_cube_x[0] = -1; unit_cube_y[0] = -1; unit_cube_z[0] = -1;
unit_cube_x[1] = 1; unit_cube_y[1] = -1; unit_cube_z[1] = -1;
unit_cube_x[2] = 1; unit_cube_y[2] = 1; unit_cube_z[2] = -1;
unit_cube_x[3] = -1; unit_cube_y[3] = 1; unit_cube_z[3] = -1;
unit_cube_x[4] = -1; unit_cube_y[4] = -1; unit_cube_z[4] = 1;
unit_cube_x[5] = 1; unit_cube_y[5] = -1; unit_cube_z[5] = 1;
unit_cube_x[6] = 1; unit_cube_y[6] = 1; unit_cube_z[6] = 1;
unit_cube_x[7] = -1; unit_cube_y[7] = 1; unit_cube_z[7] = 1;
fun rotate_point(px, py, pz, ax, ay, az) {
/* Rotate around X */
cos_x = Math.Cos(ax);
sin_x = Math.Sin(ax);
y1 = py * cos_x - pz * sin_x;
z1 = py * sin_x + pz * cos_x;
/* Rotate around Y */
cos_y = Math.Cos(ay);
sin_y = Math.Sin(ay);
x2 = px * cos_y + z1 * sin_y;
z2 = -px * sin_y + z1 * cos_y;
/* Rotate around Z */
cos_z = Math.Cos(az);
sin_z = Math.Sin(az);
x3 = x2 * cos_z - y1 * sin_z;
y3 = x2 * sin_z + y1 * cos_z;
result[0] = x3;
result[1] = y3;
result[2] = z2;
return result;
}
/* ═══════════════════════════════════════════════════════════════
* Lemniscate parametric curve (Bernoulli lemniscate)
* x = a * cos(t) / (1 + sin²(t))
* y = a * sin(t) * cos(t) / (1 + sin²(t))
* ═══════════════════════════════════════════════════════════════ */
fun lemniscate_x(t, scale) {
sin_t = Math.Sin(t);
cos_t = Math.Cos(t);
denom = 1 + sin_t * sin_t;
return scale * cos_t / denom;
}
fun lemniscate_y(t, scale) {
sin_t = Math.Sin(t);
cos_t = Math.Cos(t);
denom = 1 + sin_t * sin_t;
return scale * sin_t * cos_t / denom;
}
/* ═══════════════════════════════════════════════════════════════
* Animation Functions
* ═══════════════════════════════════════════════════════════════ */
fun draw_cube(cube_idx) {
ax = global.cube_angle_x[cube_idx];
ay = global.cube_angle_y[cube_idx];
az = global.cube_angle_z[cube_idx];
size = global.cube_sizes[cube_idx] / 2;
/* Brightness based on cube layer (inner = brightest) */
brightness = 0.3 + (2 - cube_idx) * 0.3;
for (v = 0; v < 8; v++) {
/* Get unit cube vertex */
px = global.unit_cube_x[v] * size;
py = global.unit_cube_y[v] * size;
pz = global.unit_cube_z[v] * size;
/* Rotate */
rotated = rotate_point(px, py, pz, ax, ay, az);
/* Project to screen (simple orthographic) */
screen_x = global.upper_center_x + rotated[0];
screen_y = global.upper_center_y + rotated[1];
/* Depth-based opacity */
depth = (rotated[2] + size) / (size * 2);
opacity = brightness * (0.5 + depth * 0.5);
/* Create vertex dot */
idx = cube_idx * 8 + v;
dot_size = 4 + (2 - cube_idx) * 2; /* Inner cube has bigger dots */
dot_img = Image.Text("*", global.blue_mid_r * brightness,
global.blue_mid_g * brightness,
global.blue_bright_b * brightness, 1);
global.cube_vertices[idx].SetImage(dot_img);
global.cube_vertices[idx].SetX(screen_x - dot_size/2);
global.cube_vertices[idx].SetY(screen_y - dot_size/2);
global.cube_vertices[idx].SetOpacity(opacity);
}
}
fun draw_lemniscates() {
for (l = 0; l < 2; l++) {
/* Update head position */
global.head_pos[l] += global.head_speed[l];
if (global.head_pos[l] > 6.28318) {
global.head_pos[l] -= 6.28318;
}
y_off = global.lemniscate_y_offset[l];
/* Draw trail dots */
for (d = 0; d < global.dots_per_curve; d++) {
/* Position along curve (behind the head) */
t = global.head_pos[l] - d * 0.4;
lx = lemniscate_x(t, global.lemniscate_scale);
ly = lemniscate_y(t, global.lemniscate_scale);
screen_x = global.upper_center_x + lx;
screen_y = global.upper_center_y + y_off + ly;
/* Fade trail */
fade = 1.0 - (d / global.dots_per_curve) * 0.8;
idx = l * global.dots_per_curve + d;
dot_img = Image.Text(".", global.blue_dark_r + fade * 0.1,
global.blue_dark_g + fade * 0.2,
global.blue_bright_b * fade, 1);
global.lem_dots[idx].SetImage(dot_img);
global.lem_dots[idx].SetX(screen_x - 2);
global.lem_dots[idx].SetY(screen_y - 2);
global.lem_dots[idx].SetOpacity(fade * 0.7);
}
/* Draw bright head */
hx = lemniscate_x(global.head_pos[l], global.lemniscate_scale);
hy = lemniscate_y(global.head_pos[l], global.lemniscate_scale);
head_screen_x = global.upper_center_x + hx;
head_screen_y = global.upper_center_y + y_off + hy;
/* Pulsing head */
pulse = 0.8 + Math.Sin(global.frame * 0.2) * 0.2;
head_img = Image.Text("O", global.blue_bright_r * pulse,
global.blue_bright_g * pulse,
global.blue_bright_b, 1);
global.lem_heads[l].SetImage(head_img);
global.lem_heads[l].SetX(head_screen_x - 4);
global.lem_heads[l].SetY(head_screen_y - 4);
global.lem_heads[l].SetOpacity(1.0);
}
}
fun update_log_display() {
line_height = 16;
start_y = global.divider_y + 15;
left_margin = 30;
for (i = 0; i < global.max_log_lines; i++) {
if (global.log_texts[i] != "") {
/* Simple color based on content */
if (String(global.log_texts[i]).Find("OK") >= 0 ||
String(global.log_texts[i]).Find("Started") >= 0) {
r = global.log_green_r;
g = global.log_green_g;
b = global.log_green_b;
} else if (String(global.log_texts[i]).Find("warn") >= 0 ||
String(global.log_texts[i]).Find("WARN") >= 0) {
r = global.log_amber_r;
g = global.log_amber_g;
b = global.log_amber_b;
} else {
r = global.log_blue_r;
g = global.log_blue_g;
b = global.log_blue_b;
}
txt_img = Image.Text(global.log_texts[i], r, g, b, "Mono 9");
global.log_sprites[i].SetImage(txt_img);
global.log_sprites[i].SetX(left_margin);
global.log_sprites[i].SetY(start_y + i * line_height);
global.log_sprites[i].SetOpacity(0.9);
}
}
}
fun update_phase() {
phase_img = Image.Text(global.current_phase,
global.blue_bright_r,
global.blue_bright_g,
global.blue_bright_b, "Sans Bold 11");
global.phase_sprite.SetImage(phase_img);
global.phase_sprite.SetX(global.lower_center_x - phase_img.GetWidth() / 2);
global.phase_sprite.SetY(global.lower_center_y + 50);
}
/* ═══════════════════════════════════════════════════════════════
* Main Animation Loop
* ═══════════════════════════════════════════════════════════════ */
fun refresh_callback() {
global.frame++;
/* Update cube rotations */
for (c = 0; c < 3; c++) {
global.cube_angle_x[c] += global.cube_speed_x[c];
global.cube_angle_y[c] += global.cube_speed_y[c];
global.cube_angle_z[c] += global.cube_speed_z[c];
/* Keep in bounds */
if (global.cube_angle_x[c] > 6.28318) global.cube_angle_x[c] -= 6.28318;
if (global.cube_angle_y[c] > 6.28318) global.cube_angle_y[c] -= 6.28318;
if (global.cube_angle_z[c] > 6.28318) global.cube_angle_z[c] -= 6.28318;
draw_cube(c);
}
/* Draw lemniscates */
draw_lemniscates();
/* Update phase display */
update_phase();
}
Plymouth.SetRefreshFunction(refresh_callback);
/* ═══════════════════════════════════════════════════════════════
* Plymouth Callbacks
* ═══════════════════════════════════════════════════════════════ */
fun boot_progress_callback(duration, progress_val) {
global.boot_progress = progress_val;
/* Update progress bar */
if (progress_fg) {
bar_width = Math.Int(progress_val * 236);
if (bar_width > 0) {
scaled = progress_fg.Scale(bar_width, 12);
progress_fg_sprite.SetImage(scaled);
}
}
/* Update phase message based on progress */
if (progress_val < 0.15) {
global.current_phase = "Loading kernel modules...";
} else if (progress_val < 0.30) {
global.current_phase = "Initializing hardware...";
} else if (progress_val < 0.50) {
global.current_phase = "Starting system services...";
} else if (progress_val < 0.70) {
global.current_phase = "Configuring network...";
} else if (progress_val < 0.90) {
global.current_phase = "Starting SecuBox modules...";
} else {
global.current_phase = "System ready";
}
}
Plymouth.SetBootProgressFunction(boot_progress_callback);
fun message_callback(text) {
/* Add to log buffer (scrolling) */
/* Shift existing lines up */
for (i = global.max_log_lines - 1; i > 0; i--) {
global.log_texts[i] = global.log_texts[i-1];
}
/* Add timestamp prefix */
timestamp = Math.Int(global.frame / 60);
ts_str = "[" + timestamp + "] ";
global.log_texts[0] = ts_str + text;
update_log_display();
}
Plymouth.SetMessageFunction(message_callback);
fun display_normal_callback() {
/* Normal boot display */
}
fun display_password_callback(prompt, bullets) {
/* LUKS password prompt */
prompt_img = Image.Text(prompt,
global.blue_bright_r,
global.blue_bright_g,
global.blue_bright_b, "Sans Bold 12");
global.phase_sprite.SetImage(prompt_img);
global.phase_sprite.SetX(global.lower_center_x - prompt_img.GetWidth() / 2);
global.phase_sprite.SetY(global.lower_center_y + 30);
/* Show bullet dots for password */
bullet_str = "";
for (i = 0; i < bullets; i++) {
bullet_str += "*";
}
bullet_img = Image.Text(bullet_str, 1.0, 1.0, 1.0, "Mono 14");
/* Could add another sprite for bullets */
}
Plymouth.SetDisplayNormalFunction(display_normal_callback);
Plymouth.SetDisplayPasswordFunction(display_password_callback);
fun quit_callback() {
/* Fade out animation */
for (c = 0; c < 3; c++) {
for (v = 0; v < 8; v++) {
idx = c * 8 + v;
global.cube_vertices[idx].SetOpacity(0);
}
}
for (l = 0; l < 2; l++) {
for (d = 0; d < global.dots_per_curve; d++) {
idx = l * global.dots_per_curve + d;
global.lem_dots[idx].SetOpacity(0);
}
global.lem_heads[l].SetOpacity(0);
}
global.phase_sprite.SetOpacity(0);
progress_bg_sprite.SetOpacity(0);
progress_fg_sprite.SetOpacity(0);
divider_sprite.SetOpacity(0);
for (i = 0; i < global.max_log_lines; i++) {
global.log_sprites[i].SetOpacity(0);
}
}
Plymouth.SetQuitFunction(quit_callback);

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

After

Width:  |  Height:  |  Size: 166 B

View File

@ -1,127 +1,266 @@
/* SecuBox Cube - Plymouth Theme Script
* 3D Rotating Cube with Module Icons
/* SecuBox Dice 3D - Plymouth Theme Script
* Rotating 3D Cube with Module Icons on Faces
* Nintendo 3DS aesthetic - dark blue autostereoscopic style
* CyberMind - https://cybermind.fr
*/
/* Screen setup */
Window.SetBackgroundTopColor(0.0, 0.0, 0.0);
Window.SetBackgroundBottomColor(0.02, 0.04, 0.02);
/* ═══════════════════════════════════════════════════════════════
* Screen Setup - 3DS dark blue gradient background
* ═══════════════════════════════════════════════════════════════ */
Window.SetBackgroundTopColor(0.063, 0.082, 0.165); /* #101529 - 3DS top screen dark */
Window.SetBackgroundBottomColor(0.039, 0.055, 0.118); /* #0a0e1e - 3DS deeper blue */
screen_width = Window.GetWidth();
screen_height = Window.GetHeight();
center_x = screen_width / 2;
center_y = screen_height / 2;
/* Load images */
/* ═══════════════════════════════════════════════════════════════
* Load Assets
* ═══════════════════════════════════════════════════════════════ */
logo = Image("logo.png");
scanlines = Image("scanlines.png");
/* Module icons - 6 faces of the cube */
icon_root = Image("icon-root.png");
icon_boot = Image("icon-boot.png");
icon_mesh = Image("icon-mesh.png");
icon_wall = Image("icon-wall.png");
icon_auth = Image("icon-auth.png");
icon_mind = Image("icon-mind.png");
/* Progress bar images */
progress_bg = Image("progress-bg.png");
progress_fg = Image("progress-fg.png");
/* Create sprites */
/* 6 face icons - one per cube face */
face_images[0] = Image("icon-root.png"); /* Front - Root/System */
face_images[1] = Image("icon-wall.png"); /* Back - Firewall */
face_images[2] = Image("icon-boot.png"); /* Top - Boot */
face_images[3] = Image("icon-mesh.png"); /* Bottom - Mesh Network */
face_images[4] = Image("icon-auth.png"); /* Right - Auth */
face_images[5] = Image("icon-mind.png"); /* Left - CyberMind */
/* ═══════════════════════════════════════════════════════════════
* Create Sprites
* ═══════════════════════════════════════════════════════════════ */
/* Logo at top */
logo_sprite = Sprite(logo);
logo_sprite.SetX(center_x - logo.GetWidth() / 2);
logo_sprite.SetY(center_y - 180);
logo_sprite.SetY(center_y - 200);
logo_sprite.SetZ(10);
/* Scanline overlay */
/* Scanline overlay - subtle 3DS parallax barrier simulation */
scanline_sprite = Sprite(scanlines);
scanline_sprite.SetX(0);
scanline_sprite.SetY(0);
scanline_sprite.SetZ(100);
scanline_sprite.SetOpacity(0.15);
scanline_sprite.SetOpacity(0.04); /* Lighter for 3DS glass effect */
/* Module icon sprites - arranged in hexagon */
icon_size = 64;
radius = 100;
icons[0] = Sprite(icon_root);
icons[1] = Sprite(icon_boot);
icons[2] = Sprite(icon_mesh);
icons[3] = Sprite(icon_wall);
icons[4] = Sprite(icon_auth);
icons[5] = Sprite(icon_mind);
/* Initial positions */
/* Face sprites - we'll show up to 3 visible faces */
for (i = 0; i < 6; i++) {
icons[i].SetZ(5);
icons[i].SetOpacity(0.8);
face_sprites[i] = Sprite(face_images[i]);
face_sprites[i].SetOpacity(0);
face_sprites[i].SetZ(5);
}
/* Edge glow sprites - green wireframe effect */
/* We'll draw edges as thin colored lines using scaled images */
/* Progress bar */
progress_bg_sprite = Sprite(progress_bg);
progress_bg_sprite.SetX(center_x - 150);
progress_bg_sprite.SetY(center_y + 140);
progress_bg_sprite.SetY(center_y + 160);
progress_bg_sprite.SetZ(8);
progress_sprite = Sprite(progress_fg);
progress_sprite.SetX(center_x - 148);
progress_sprite.SetY(center_y + 142);
progress_sprite.SetY(center_y + 162);
progress_sprite.SetZ(9);
/* Status text */
status_text = "";
/* Status message */
message_sprite = Sprite();
message_sprite.SetPosition(center_x, center_y + 180, 10);
message_sprite.SetPosition(center_x, center_y + 195, 10);
/* Animation state */
angle = 0;
/* ═══════════════════════════════════════════════════════════════
* Animation State
* ═══════════════════════════════════════════════════════════════ */
angle_x = 0;
angle_y = 0;
angle_z = 0;
frame = 0;
progress = 0;
/* Animation function - rotate icons in 3D-like pattern */
fun animate_cube() {
global.angle += 0.02;
global.frame++;
/* Cube size */
cube_size = 80;
for (i = 0; i < 6; i++) {
/* Calculate 3D-like position */
base_angle = global.angle + (i * 3.14159 / 3);
/* Rotation speeds - 3DS-style smooth, elegant rotation */
speed_x = 0.006;
speed_y = 0.012;
speed_z = 0.004;
/* X position - circular motion */
x = center_x + Math.Sin(base_angle) * radius - icon_size / 2;
/* ═══════════════════════════════════════════════════════════════
* 3D Math Functions
* ═══════════════════════════════════════════════════════════════ */
/* Y position - elliptical for 3D effect */
y = center_y + Math.Cos(base_angle) * (radius * 0.4) - icon_size / 2;
/* Face normals in local space (which way each face points) */
/* Face 0: Front (+Z) */
/* Face 1: Back (-Z) */
/* Face 2: Top (+Y) */
/* Face 3: Bottom (-Y) */
/* Face 4: Right (+X) */
/* Face 5: Left (-X) */
/* Z-depth simulation via opacity and scale */
depth = Math.Cos(base_angle);
opacity = 0.4 + (depth + 1) * 0.3;
fun get_face_visibility(face_index, ax, ay, az) {
/* Calculate if face is visible (normal pointing toward camera) */
/* Camera is at +Z looking at origin */
icons[i].SetX(x);
icons[i].SetY(y);
icons[i].SetOpacity(opacity);
/* Simplified: calculate rotated normal Z component */
/* If > 0, face is visible (pointing toward us) */
/* Bring front icons to top */
if (depth > 0) {
icons[i].SetZ(6);
} else {
icons[i].SetZ(4);
}
cos_x = Math.Cos(ax);
sin_x = Math.Sin(ax);
cos_y = Math.Cos(ay);
sin_y = Math.Sin(ay);
cos_z = Math.Cos(az);
sin_z = Math.Sin(az);
/* Normal vectors for each face */
if (face_index == 0) {
/* Front (+Z): normal = (0, 0, 1) */
nz = cos_x * cos_y;
} else if (face_index == 1) {
/* Back (-Z): normal = (0, 0, -1) */
nz = -cos_x * cos_y;
} else if (face_index == 2) {
/* Top (+Y): normal = (0, 1, 0) */
nz = sin_x;
} else if (face_index == 3) {
/* Bottom (-Y): normal = (0, -1, 0) */
nz = -sin_x;
} else if (face_index == 4) {
/* Right (+X): normal = (1, 0, 0) */
nz = -sin_y * cos_x;
} else {
/* Left (-X): normal = (-1, 0, 0) */
nz = sin_y * cos_x;
}
return nz;
}
/* Progress callback */
fun get_face_center(face_index, ax, ay, size) {
/* Calculate projected 2D center of each face */
cos_x = Math.Cos(ax);
sin_x = Math.Sin(ax);
cos_y = Math.Cos(ay);
sin_y = Math.Sin(ay);
half = size / 2;
if (face_index == 0) {
/* Front: center at (0, 0, +half) */
x = sin_y * half;
y = -sin_x * cos_y * half;
z = cos_x * cos_y * half;
} else if (face_index == 1) {
/* Back: center at (0, 0, -half) */
x = -sin_y * half;
y = sin_x * cos_y * half;
z = -cos_x * cos_y * half;
} else if (face_index == 2) {
/* Top: center at (0, +half, 0) */
x = 0;
y = cos_x * half;
z = sin_x * half;
} else if (face_index == 3) {
/* Bottom: center at (0, -half, 0) */
x = 0;
y = -cos_x * half;
z = -sin_x * half;
} else if (face_index == 4) {
/* Right: center at (+half, 0, 0) */
x = cos_y * half;
y = 0;
z = -sin_y * half;
} else {
/* Left: center at (-half, 0, 0) */
x = -cos_y * half;
y = 0;
z = sin_y * half;
}
/* Return as pseudo-struct (array) */
result[0] = x;
result[1] = y;
result[2] = z;
return result;
}
/* ═══════════════════════════════════════════════════════════════
* Animation Loop
* ═══════════════════════════════════════════════════════════════ */
fun animate_cube() {
/* Update rotation angles */
global.angle_x += global.speed_x;
global.angle_y += global.speed_y;
global.angle_z += global.speed_z;
global.frame++;
/* Keep angles in bounds */
if (global.angle_x > 6.28318) global.angle_x -= 6.28318;
if (global.angle_y > 6.28318) global.angle_y -= 6.28318;
if (global.angle_z > 6.28318) global.angle_z -= 6.28318;
/* Update each face */
for (i = 0; i < 6; i++) {
/* Check visibility */
visibility = get_face_visibility(i, global.angle_x, global.angle_y, global.angle_z);
if (visibility > 0) {
/* Face is visible */
pos = get_face_center(i, global.angle_x, global.angle_y, global.cube_size);
/* Project to screen (orthographic with slight perspective feel) */
depth_scale = 0.8 + (pos[2] / global.cube_size) * 0.2;
/* Scale icon based on depth */
icon_scale = 0.6 + visibility * 0.4;
icon_size = 64 * icon_scale * depth_scale;
/* Calculate screen position */
screen_x = center_x + pos[0] * 1.5 - icon_size / 2;
screen_y = center_y + pos[1] * 1.5 - icon_size / 2;
/* Scale the image */
scaled = face_images[i].Scale(icon_size, icon_size);
face_sprites[i].SetImage(scaled);
/* Position */
face_sprites[i].SetX(screen_x);
face_sprites[i].SetY(screen_y);
/* Opacity based on how directly it faces us + green tint simulation */
opacity = 0.5 + visibility * 0.5;
face_sprites[i].SetOpacity(opacity);
/* Z-order based on depth */
face_sprites[i].SetZ(5 + pos[2] / 10);
} else {
/* Face not visible */
face_sprites[i].SetOpacity(0);
}
}
/* 3DS-style gentle glow pulse - slower, more elegant */
pulse = 0.90 + Math.Sin(global.frame * 0.03) * 0.10;
logo_sprite.SetOpacity(pulse);
}
/* ═══════════════════════════════════════════════════════════════
* Plymouth Callbacks
* ═══════════════════════════════════════════════════════════════ */
fun refresh_callback() {
animate_cube();
}
Plymouth.SetRefreshFunction(refresh_callback);
/* Boot progress */
fun boot_progress_callback(duration, progress_val) {
global.progress = progress_val;
/* Scale progress bar */
if (progress_fg) {
scaled = progress_fg.Scale(progress_val * 296, 16);
progress_sprite.SetImage(scaled);
@ -130,26 +269,23 @@ fun boot_progress_callback(duration, progress_val) {
Plymouth.SetBootProgressFunction(boot_progress_callback);
/* Message display */
fun message_callback(text) {
global.status_text = text;
msg_image = Image.Text(text, 0, 1, 0.61, "JetBrains Mono");
/* 3DS cyan/light blue text */
msg_image = Image.Text(text, 0.400, 0.753, 0.925, "JetBrains Mono"); /* #66c0ec */
message_sprite.SetImage(msg_image);
message_sprite.SetX(center_x - msg_image.GetWidth() / 2);
message_sprite.SetY(center_y + 180);
message_sprite.SetY(center_y + 195);
}
Plymouth.SetMessageFunction(message_callback);
/* Display normal/password prompts */
fun display_normal_callback() {
/* Nothing special */
/* Normal display mode */
}
fun display_password_callback(prompt, bullets) {
/* Show password prompt */
prompt_image = Image.Text(prompt, 0, 1, 0.61, "JetBrains Mono");
/* Password prompt - 3DS cyan accent */
prompt_image = Image.Text(prompt, 0.400, 0.753, 0.925, "JetBrains Mono"); /* #66c0ec */
message_sprite.SetImage(prompt_image);
message_sprite.SetX(center_x - prompt_image.GetWidth() / 2);
}
@ -157,13 +293,13 @@ fun display_password_callback(prompt, bullets) {
Plymouth.SetDisplayNormalFunction(display_normal_callback);
Plymouth.SetDisplayPasswordFunction(display_password_callback);
/* Quit callback */
fun quit_callback() {
/* Fade out */
/* Fade out all elements */
for (i = 0; i < 6; i++) {
icons[i].SetOpacity(0);
face_sprites[i].SetOpacity(0);
}
logo_sprite.SetOpacity(0);
scanline_sprite.SetOpacity(0);
}
Plymouth.SetQuitFunction(quit_callback);

View File

@ -133,25 +133,33 @@ create_session() {
chown -R "$KIOSK_USER:$KIOSK_USER" "$KIOSK_HOME/.config"
# Minimal session script - just Chromium, no optional tools
# Use --start-fullscreen (not --kiosk) for better VT switching compatibility
cat > "$KIOSK_HOME/.xsession" <<XSESSION
#!/bin/bash
# SecuBox Kiosk - Minimal X Session
# SecuBox Kiosk - Minimal X Session (v1.6.3)
export DISPLAY=\${DISPLAY:-:0}
# Log
# Log startup
logger -t secubox-kiosk "Starting Chromium kiosk: $url"
# Disable screensaver (ignore errors)
# Disable screensaver and power management (ignore errors)
xset s off 2>/dev/null || true
xset -dpms 2>/dev/null || true
xset s noblank 2>/dev/null || true
# Launch Chromium directly
# Chromium flags for kiosk mode
# --start-fullscreen: allows Ctrl+Alt+Fn VT switching (better than --kiosk)
# --disable-gpu: avoid GPU issues in VMs
# --no-sandbox: required in chroot/container environments
# exec replaces shell with chromium - on exit, X session ends
exec chromium \\
--kiosk \\
--start-fullscreen \\
--no-first-run \\
--no-sandbox \\
--disable-gpu \\
--disable-software-rasterizer \\
--disable-pinch \\
--noerrdialogs \\
--disable-translate \\
--ignore-certificate-errors \\
--disable-features=TranslateUI \\
--window-position=0,0 \\
@ -201,7 +209,7 @@ start_display() {
# ══════════════════════════════════════════════════════════════════
main() {
log "════════════════════════════════════════════════"
log "SecuBox Kiosk Launcher v2.0"
log "SecuBox Kiosk Launcher v2.1 (1.6.3)"
log "════════════════════════════════════════════════"
check_prerequisites