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.

 Citadel desktop shell components:
 +-------------------------------------------------------+
 | Top bar (taskbar.c)                    clock          |
 |  [*] Aegis                            HH:MM           |
 +-------------------------------------------------------+
 |                                                       |
 |          Desktop area (wallpaper + windows)           |
 |                                                       |
 |                                                       |
 +-------------------------------------------------------+
 |        +--[dock.c: frosted glass dock]--+             |
 |        | [gear] [folder] [>_] [grid]   |              |
 |        +-------------------------------+              |
 +-------------------------------------------------------+

Architecture

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

libcitadel.a — small static library (taskbar.c + theme.h) linked directly into Lumen. The top bar runs in Lumen’s render loop via a compositor callback. The pre-Phase-47b dock.c and dock.h files are still in the source tree but no longer compiled into anything that runs; they’re left as a reference for the standalone dock that supplanted them. Depends on libglyph.a for drawing primitives and font rendering.

/bin/citadel-dock — standalone binary, vigil graphical-mode service, spawned in parallel with Bastion. 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 (“terminal”, “widgets”, etc.). Doesn’t need CAP_KIND_FB; Lumen owns the framebuffer.

 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 there was a parallel comp.on_draw_overlay for the dock; that’s gone now because the dock isn’t part of Lumen’s render loop anymore.

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

Four items are defined:

Index Constant Key Icon Action
0 DOCK_ITEM_SETTINGS "settings" Gear (circles + rectangles) Stub (no action)
1 DOCK_ITEM_FILES "files" Folder (rounded rects) Stub (no action)
2 DOCK_ITEM_TERMINAL "terminal" Terminal (dark rect + “>_”) Spawns terminal window
3 DOCK_ITEM_WIDGETS "widgets" 2x2 colored grid Opens widget test window

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

Each dock icon is drawn programmatically (no image assets):

  • Settings (gear): Filled accent circle (r=18) with 8 rectangular teeth + hollow center
  • Files (folder): Two overlapping rounded rectangles in amber/brown
  • Terminal: Dark rounded rectangle with “>_” text in terminal foreground color
  • Widgets: Four small colored rounded rectangles arranged as a 2x2 grid (blue, green, orange, purple)

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

void dock_set_hover(int item);  /* -1 for no hover */

The hover item index is tracked as static state. When the hover changes, citadel-dock repaints into its memfd buffer and sends LUMEN_OP_DAMAGE for the affected rect; Lumen recomposites. Click events arrive from Lumen as LUMEN_EV_MOUSE, get hit-tested locally, and fire one of: lumen_invoke(fd, "terminal"), lumen_invoke(fd, "widgets"), etc. — Lumen’s invoke_handler then spawns the matching built-in or external client.

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
1 “Settings…” Stub (no action)
2 “Lock Screen” Freezes input, signals Bastion
3 ”—” Separator
4 “Restart” syscall(169, 1) – system reboot
5 “Power Off” kill(1, SIGTERM) – signal init

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 as a graphical service      |
 +-----------------------------------------------+
          |
          v
 +-----------------------------------------------+
 |  Bastion (display manager)                    |
 |    user/bin/bastion/main.c                    |
 |    Links: libglyph.a (drawing + fonts)        |
 |    Policy capabilities: AUTH, FB, SETUID      |
 +-----------------------------------------------+
          |  fork+execve on login
          v
 +-----------------------------------------------+
 |  Lumen (compositor)                           |
 |    user/bin/lumen/                            |
 |    Links: libglyph.a + libcitadel.a           |
 |    Baseline capabilities (FB inherited)       |
 +-----------------------------------------------+
          |  uses
          v
 +-----------------------------------------------+
 |  libcitadel.a (desktop shell)                 |
 |    user/lib/citadel/                          |
 |    dock.c -- frosted glass dock               |
 |    taskbar.c -- top bar with clock            |
 |    theme.h -- shared visual constants         |
 +-----------------------------------------------+
          |  depends on
          v
 +-----------------------------------------------+
 |  libglyph.a (widget toolkit)                  |
 |    user/lib/glyph/                            |
 |    Drawing primitives, font rendering,        |
 |    widget tree, window management             |
 +-----------------------------------------------+

Source Files

File Path Purpose
dock.c user/lib/citadel/ Application dock: layout, frosted glass, icons, hit testing
dock.h user/lib/citadel/ Dock API, geometry constants, item definitions
taskbar.c user/lib/citadel/ Top bar: frosted glass, “Aegis” text, clock
taskbar.h user/lib/citadel/ Top bar API declarations
theme.h user/lib/citadel/ Shared theme constants (colors, dimensions)
Makefile user/lib/citadel/ Builds libcitadel.a
main.c user/bin/bastion/ Display manager: login, session spawn, lock screen
Makefile user/bin/bastion/ Builds bastion.elf