Citadel Desktop Shell

Citadel is the desktop shell for Aegis. It provides the top bar, application dock, and theme system that compose the desktop environment rendered by the Lumen compositor. Citadel also encompasses the Bastion display manager, which handles graphical login, session spawning, and screen locking.

Phase 47b note: the dock has been peeled out of libcitadel.a into a standalone /bin/citadel-dock process that talks to Lumen over the external window protocol. The top bar is still in-process: libcitadel.a (now just taskbar.c + theme.h) is linked into Lumen and drawn via the desktop callback. This split is the first step of “subsystem peeling”; terminal, file manager, and settings will follow in later phases.

v1 note: Citadel and Bastion are v1 software – functional and tested, but still evolving. Contributions are welcome – file issues or propose changes at exec/aegis.

 Desktop shell components:
 +-------------------------------------------------------+
 | Top bar (taskbar.c, in-process)        clock          |
 |  [*] Aegis                            HH:MM           |
 +-------------------------------------------------------+
 |                                                       |
 |          Desktop area (wallpaper + windows)           |
 |                                                       |
 +-------------------------------------------------------+
 | [apps] [settings] [files] [term] [calc] [edit] [grid] |
 |        citadel-dock (/bin/citadel-dock, external)     |
 +-------------------------------------------------------+

Architecture

Citadel splits across two compilation units that look different from the outside:

libcitadel.a — static library (dock.c + taskbar.c + theme.h) linked directly into Lumen. The top bar runs in Lumen’s render loop via the on_draw_desktop compositor callback. dock.c/dock.h are still compiled into libcitadel.a but the code they contain is not used by the running dock — the standalone /bin/citadel-dock binary has its own dock implementation. libcitadel.a’s dock code is retained as a reference. Depends on libglyph.a for drawing primitives and font rendering.

/bin/citadel-dock — standalone binary, vigil graphical-mode service. Connects to Lumen at /run/lumen.sock via the external window protocol, asks for a chromeless bottom-anchored panel (LUMEN_OP_CREATE_PANEL), paints into the memfd-backed pixel buffer Lumen returns, and dispatches click events back to Lumen with LUMEN_OP_INVOKE (e.g., "terminal", "applications", "settings"). Uses procedural icons from libglyph’s icons.{c,h} (glyph_icon_draw). Does not need CAP_KIND_FB; Lumen owns the framebuffer. Waits indefinitely for Lumen to bind the socket (Bastion may not authenticate for minutes on a cold boot).

 vigil (PID 1)
   ├─ spawns /bin/bastion (graphical-mode service)
   │     └─ on auth success: spawn /bin/lumen
   │           └─ links libcitadel.a → top bar via desktop callback
   │             listens on /run/lumen.sock
   │
   └─ spawns /bin/citadel-dock (graphical-mode service)
         └─ connects to /run/lumen.sock, creates panel, paints dock

Lumen integrates the in-process side via the desktop callback:

comp.on_draw_desktop = desktop_draw_cb;  /* draws top bar */

Pre-Phase-47b comp.on_draw_overlay drew the dock. Now that the dock is external, on_draw_overlay is still registered but is used solely for the context menu (overlay_draw_cb in main.c).

Top Bar (taskbar.c)

The top bar is a frosted glass strip at the top of the screen.

Rendering

void topbar_draw(surface_t *s, int screen_w, const char *clock_str);

The rendering pipeline:

  1. Box blur the top TOPBAR_HEIGHT (28px) of the backbuffer at radius 8
  2. Dark tint via draw_blend_rect with TOPBAR_BG (0x001A1A2E) at alpha 128
  3. Bottom border: 1px white blend at alpha 20 for definition
  4. Accent dot: 4px filled circle at x=14 in C_ACCENT blue
  5. “Aegis” text: white, positioned at x=24 (TTF at 14px or bitmap fallback)
  6. Clock text: right-aligned with 12px margin, TOPBAR_TEXT color (0x00C0C0D0)

The clock string is passed in from Lumen’s main loop, which updates it approximately once per second by checking clock_gettime(CLOCK_REALTIME) every 60 frames.

Hit Testing

int topbar_hit_aegis(int mx, int my, int screen_w);

Returns true if the click is within the “Aegis” clickable area: x < AEGIS_AREA_W (80px) and y < TOPBAR_HEIGHT (28px). Clicking this area opens the system context menu.

Theme Constants

Defined in theme.h:

Constant Value Purpose
TOPBAR_HEIGHT 28 Top bar height in pixels
TOPBAR_BG 0x001A1A2E Top bar tint color
TOPBAR_TEXT 0x00C0C0D0 Top bar text color (clock, etc.)
AEGIS_AREA_W 80 Width of the clickable “Aegis” text area
DESKTOP_BG 0x001A1A2E Desktop background (when no wallpaper)

Application Dock (/bin/citadel-dock)

The dock is a centered, opaque dark panel at the bottom of the screen containing application launcher icons. It runs as a standalone process (Phase 47b) and renders into a Lumen-managed memfd-backed pixel buffer.

Architectural note: the pre-Phase-47b in-process implementation rendered frosted glass by sampling the compositor’s backbuffer behind the dock and box-blurring it. The external window protocol does not give clients access to the underlying compositor pixels, so the frosted blur is dropped — the dock paints an opaque background (DOCK_BG = 0x002A2A3E) instead. The top bar, which is still in-process, retains its frosted-glass effect.

Layout

 Dock geometry:
 +-------------------------------------------+
 |  24px  | icon | 12px | icon | ... | 24px  |
 |  pad   | 48px | gap  | 48px |     | pad   |
 +-------------------------------------------+
       ^                                  ^
       DOCK_PADDING_X                     DOCK_PADDING_X

 DOCK_HEIGHT = DOCK_ICON_SIZE + 2 * DOCK_PADDING_Y = 48 + 32 = 80

Layout constants:

Constant Value
DOCK_ICON_SIZE 48 pixels
DOCK_ICON_GAP 12 pixels
DOCK_PADDING_X 24 pixels
DOCK_PADDING_Y 16 pixels
DOCK_HEIGHT 80 pixels
DOCK_CORNER_R 16 pixels
DOCK_BOTTOM_MARGIN 10 pixels

The dock is horizontally centered: dock_x = (screen_w - dock_w) / 2. Vertically it sits DOCK_BOTTOM_MARGIN above the screen bottom.

Dock Items

Seven items are defined (eight on live media); order is fixed:

Index Constant Invoke name Icon Action
0 DOCK_ITEM_APPS "applications" 2×2 colored grid Opens fullscreen launcher
1 DOCK_ITEM_SETTINGS "settings" Gear Opens Settings app
2 DOCK_ITEM_FILES "filemanager" Folder Opens Files app
3 DOCK_ITEM_TERMINAL "terminal" Dark rect + “>_” Opens standalone terminal
4 DOCK_ITEM_CALC "calculator" Calculator Opens Calculator app
5 DOCK_ITEM_EDITOR "editor" Text editor icon Opens Editor app
6 DOCK_ITEM_WIDGETS "widgets" 2×2 colored grid Opens widget test (in-process)
7 DOCK_ITEM_INSTALLER "gui-installer" Drive + arrow GUI installer (live media only)

The installer item is only shown when /proc/cmdline contains aegis_live AND /apps/gui-installer/gui-installer exists. All other items always appear. Icons are rendered by glyph_icon_draw from libglyph’s shared icons.c.

Background Rendering

 Dock paint pipeline (Phase 47b, opaque):
 +--------------------------------------------+
 | 1. draw_fill_rect(x, y, w, h, DOCK_BG)     |
 |    Solid dark background                   |
 |    DOCK_BG = 0x002A2A3E                    |
 +--------------------------------------------+
 | 2. Subtle border (top: white a=20,         |
 |    bottom: black a=40)                     |
 +--------------------------------------------+
 | 3. Draw icons with hover highlight         |
 |    Hover: white blend a=40 on icon area    |
 +--------------------------------------------+

The frosted-glass pipeline (background sampling, box blur, corner mask) was used by the in-process dock pre-47b. The external window protocol does not let clients read the underlying compositor pixels, so the blur is gone — the dock just paints an opaque background. The visual difference is small, the architectural simplification is large.

Icon Rendering

All dock icons are drawn programmatically via glyph_icon_draw in libglyph’s icons.c (no image assets). The same function is used by both the dock (48px) and the Applications launcher (72px), so every app looks identical in both places. Known ids: "applications", "settings", "filemanager" (alias "files"), "terminal", "calculator", "editor", "widgets", "gui-installer". Unknown ids get a letter-tile fallback (rounded rect in a hashed color with the first letter of the display name).

Hit Testing

int dock_hit_test(int mx, int my);  /* returns item index or -1 */

First checks if the point is within the dock bounds, then iterates items checking DOCK_ICON_SIZE x DOCK_ICON_SIZE bounding boxes.

Hover State

The hover item index is tracked as static state in /bin/citadel-dock. When the hover changes, the dock repaints into its memfd buffer and sends LUMEN_OP_DAMAGE; Lumen recomposites. Click events arrive from Lumen as LUMEN_EV_MOUSE, get hit-tested locally, and fire lumen_invoke(fd, invoke_name) — Lumen’s invoke_handler then spawns the matching app or built-in.

Context Menu

The system context menu is drawn by Lumen’s main.c (not Citadel) but uses Citadel’s theme colors. It opens when clicking the “Aegis” text in the top bar.

Index Label Action
0 “About Aegis” Opens the About window (in-process)
1 “Applications” Invokes "applications"/bin/applications launcher
2 “Files” Invokes "filemanager" → Files app
3 “Text Editor” Invokes "editor" → Editor app
4 “Calculator” Invokes "calculator" → Calculator app
5 “Settings…” Invokes "settings" → Settings app
6 “Lock Screen” Freezes input, signals Bastion via SIGUSR1
7 ”—” Separator
8 “Restart” syscall(169, 1) — system reboot
9 “Power Off” kill(1, SIGTERM) — signal vigil to shut down

The context menu uses the same frosted glass technique as the dock:

  1. Box blur at radius 10
  2. Dark tint (MENU_BG = 0x00222238, alpha 180)
  3. Rounded corner masking (radius 8)
  4. Subtle border highlights
  5. Items with hover highlight (MENU_HOVER_BG = 0x003A3A58, alpha 100)
  6. Separator rendered as a 1px line at alpha 120

Menu geometry: 160px wide, positioned at (4, TOPBAR_HEIGHT), item height 28px, separator height 10px, 8px top/bottom padding.

Bastion Display Manager

Bastion (user/bin/bastion/main.c) is the graphical login manager. It runs as a Vigil service with policy capabilities AUTH, FB, and SETUID granted via /etc/aegis/caps.d/ entries in the security policy engine.

Boot Flow

 Bastion lifecycle:
 +---------------------------+
 | Check /proc/cmdline for   |
 | "boot=graphical"          |
 | (or --force flag)         |
 +---------------------------+
          |
          v
 +---------------------------+
 | Map framebuffer (513)     |
 | Alloc backbuffer          |
 | Load logo, init fonts     |
 +---------------------------+
          |
          v
 +---------------------------+
 | Show greeter form         |
 | (username + password +    |
 |  login button)            |
 +---------------------------+
          |  (credentials)
          v
 +---------------------------+
 | auth_check() via libauth  |
 | auth_elevate_session()    |
 | auth_set_identity(uid,gid)|
 | auth_grant_shell_caps()   |
 +---------------------------+
          |
          v
 +---------------------------+
 | fork() + execve()         |
 | -> /bin/lumen             |
 +---------------------------+
          |
          v
 +---------------------------+
 | waitpid() for Lumen       |
 | Handle SIGUSR1 (lock)     |
 +---------------------------+

Login Form

The login form uses a horizontal layout: [username field] [password field] [Login button], centered at 75% screen height. The Aegis logo is displayed centered at the middle of the screen, loaded from /usr/share/logo.raw and rendered at 50% scale.

Network status is displayed in the top-left corner by querying SYS_NETCFG (syscall 500), showing IP address and MAC if available.

Session Spawn

Bastion uses fork+execve rather than sys_spawn to give Lumen an independent file descriptor table. Before execve, the child process:

  1. Calls auth_grant_shell_caps() to populate the child’s capability table with the appropriate capability kinds for shell use
  2. Sets environment variables (PATH, HOME, USER, TERM)
  3. Executes /bin/lumen

Lock Screen Protocol

  1. Lumen receives Ctrl+Alt+L or the “Lock Screen” menu item
  2. Lumen sets s_input_frozen = 1, sends SIGUSR1 to Bastion (parent)
  3. Bastion’s SIGUSR1 handler sets s_locked = 1
  4. Bastion re-enters its input loop, displaying the lock form (password only, username pre-filled)
  5. On successful re-authentication, Bastion sends SIGUSR2 to Lumen
  6. Lumen’s SIGUSR2 handler clears s_input_frozen, resuming normal operation

During lock, Bastion draws over the framebuffer directly. Lumen continues compositing in the background but discards all keyboard input.

Session Restart

When Lumen exits (process terminates), Bastion reopens /dev/kbd and /dev/mouse, restores raw terminal mode, and returns to the greeter loop. This provides automatic session recovery.

Crossfade Transitions

Both Bastion and Lumen implement pixel-by-pixel crossfade transitions:

  • Bastion -> Lumen: Lumen captures the framebuffer (Bastion’s login screen), composites the desktop, then fades from the old frame to the new over 15 steps (250ms)
  • Text console -> Bastion: Bastion captures whatever was on screen, draws the login form, then fades between them

The crossfade formula for each pixel:

result = (old_pixel * alpha + new_pixel * (255 - alpha)) / 255;

Where alpha decreases from 255 to 0 across the transition steps.

Component Relationship

 +-----------------------------------------------+
 |  Vigil (init)                                 |
 |    spawns Bastion (graphical service)         |
 |    spawns citadel-dock (graphical service)    |
 +-----------------------------------------------+
          |                    |
          v                    v
 +--------------------+  +--------------------+
 |  Bastion           |  |  citadel-dock      |
 |  user/bin/bastion/ |  |  user/bin/         |
 |  libglyph.a        |  |  citadel-dock/     |
 |  AUTH, FB, SETUID  |  |  libglyph.a        |
 +--------------------+  +--------------------+
          |  fork+execve              | connects to
          v                          v
 +-----------------------------------------------+
 |  Lumen (compositor)                           |
 |    user/bin/lumen/                            |
 |    Links: libglyph.a + libcitadel.a           |
 |    Serves /run/lumen.sock                     |
 +-----------------------------------------------+
          |  links
          v
 +-----------------------------------------------+
 |  libcitadel.a (top bar + theme)               |
 |    user/lib/citadel/                          |
 |    taskbar.c -- top bar with clock            |
 |    dock.c -- reference impl (not used live)   |
 |    theme.h -- shared visual constants         |
 +-----------------------------------------------+
          |  depends on
          v
 +-----------------------------------------------+
 |  libglyph.a (widget toolkit)                  |
 |    user/lib/glyph/                            |
 |    Drawing, fonts, widgets, window mgmt,      |
 |    glyph_term emulator core, lumen_client,    |
 |    apps scanner, shared icons                 |
 +-----------------------------------------------+

Source Files

File Path Purpose
taskbar.c user/lib/citadel/ Top bar: frosted glass, “Aegis” text, clock
taskbar.h user/lib/citadel/ Top bar API declarations
dock.c user/lib/citadel/ Legacy dock implementation (compiled into libcitadel.a but not used at runtime; reference for the standalone dock)
dock.h user/lib/citadel/ Legacy dock API, geometry constants, 4-item definitions
theme.h user/lib/citadel/ Shared theme constants (colors, dimensions)
Makefile user/lib/citadel/ Builds libcitadel.a (taskbar.c + dock.c)
main.c user/bin/citadel-dock/ Standalone dock: 7–8 items, shared glyph_icon_draw, LUMEN_OP_CREATE_PANEL
main.c user/bin/bastion/ Display manager: login, session spawn, lock screen
Makefile user/bin/bastion/ Builds bastion.elf