8.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
RhemaWave is a Go-based fullscreen SDL2/OpenGL application designed to run on Linux kiosk systems. It displays proclamations with audio playback and animated visual patterns. The application uses a page-based navigation system and includes network connectivity validation, license key management, and an auto-update mechanism.
Build & Development
Development Workflow
nix develop # enter dev shell (provides Go, SDL2, etc.)
go build -o bin/rhemawave rhemawave.go # build binary
./bin/rhemawave # run (requires display server + audio)
./dev_scripts/reset.sh # wipe local database and start fresh
The binary at bin/rhemawave resolves assets via ../share/rhemawave relative to itself, so the repo layout (bin/ + share/rhemawave/) works out of the box.
Nix Build
nix build # produces ./result/bin/rhemawave + ./result/share/rhemawave/
The flake's postInstall copies share/ into $out/, preserving the same relative layout.
Testing
There are currently no automated tests in this codebase.
Architecture
Application Entry Point
rhemawave.gois the main entry point, callsinternal/application.Run()internal/application/application.gocontains the core application lifecycle
Core Application Flow
-
Initialization
- Create application data directory (
~/.local/share/rhemawave) - Initialize SQLite database (
data.db) with GORM - Initialize SDL2 subsystems (VIDEO, AUDIO)
- Initialize SDL_ttf for font rendering
- Create fullscreen OpenGL 3.3 Core Profile window
- Initialize shader and font managers
- Hide cursor and enable relative mouse mode
- Create application data directory (
-
Validation Phase (startup page stack, top → bottom)
- WiFi setup page (if no internet connection detected) — shown first
- TOS sections from database (unaccepted only, ordered by section number)
- Email address page (if missing)
- License key page (if missing)
- Welcome page — always at bottom
- License key is also validated against the API
-
Event Loop (60 FPS target)
- Poll SDL events
- Handle keyboard events via keybinds system
- Update current page with delta time
- Render current page
- Apply pending page transitions
Page System
Pages implement the page.Page interface:
Init(app ApplicationInterface) error- Setup resourcesHandleEvent(event *sdl.Event) error- Handle inputUpdate(dt float32) error- Update stateRender() error- Draw to screenDestroy() error- Cleanup resources
Pages can embed page.Base for common functionality (OpenGL quad rendering, projection matrix, screen dimensions).
Page Navigation:
PushPage(p)- Add page to stack (creates history)SwitchPage(p)- Replace current pageUnwindToPage(p)- Pop back to a specific page (or create if not in stack)
Available Pages:
Welcome- Initial screen showing versionScrollerPage- Main proclamation player (F2)Settings- Settings menu (F3)WiFiSetupPage- Network configurationLicenseKeyPage- License key entryEmailAddressPage- Email address entryAudioDevices- Audio device selectionProclamationOptions- Proclamation selectionTOSPage- Terms of Service acceptance screen (scrollable, Y/N to accept/decline)
Key Global Keybinds
Defined in application.initKeybinds():
F1- Navigate to Welcome pageF2- Navigate to Player (ScrollerPage)F3- Navigate to SettingsSHIFT+UP- Increase volumeSHIFT+DOWN- Decrease volumeRIGHT_CTRL+Q- Quit application
Package Structure
internal/application - Core application lifecycle, event loop, page management, validation
internal/page - Page interface and all page implementations
internal/db - Database layer using GORM with SQLite
- Models:
Config,Proclamations,Log,TOSSection - Manages configuration key-value pairs
- Seeds default proclamations from
share/rhemawave/proclamations/ - Seeds TOS sections from
share/rhemawave/tos/*.html(filename format:{NN} - {Title}.html)
internal/audio - SDL2 audio subsystem integration (currently minimal wrapper)
internal/mixer - Audio playback manager for music and proclamations
internal/graphics - OpenGL utilities and display orientation handling
internal/shaderManager - Loads and manages OpenGL shaders from share/rhemawave/shaders/
internal/fontManager - Loads and manages TrueType fonts from share/rhemawave/fonts/
internal/keybinds - Global keyboard shortcut registration and dispatch
internal/network - Network connectivity manager (uses D-Bus for NetworkManager on Linux)
internal/logger - Logging system that writes to database and stdout
internal/colors - Color utilities
internal/pattern - Visual pattern generation (line patterns, color patterns)
Repository Layout
bin/ # build output (gitignored)
rhemawave
share/rhemawave/ # assets (checked in)
audio/ # Audio files (audio.wav, etc.)
fonts/ # TrueType fonts
shaders/ # GLSL shader files
proclamations/ # Proclamation text files
home/ # Non-commercial proclamations
commercial/ # Commercial proclamations
tos/ # TOS HTML files (seeded into DB on first run)
packaging/ # deployment configs (kiosk, updater, polkit)
kiosk/ # kiosk system configs
rhemawave-updater.py # auto-updater script
internal/ # Go source packages
dev_scripts/
reset.sh # deletes ~/.local/share/rhemawave/
flake.nix # Nix build and dev shell
Important Implementation Details
Asset Resolution: resolveAssetsDir() computes filepath.Join(filepath.Dir(exe), "..", "share", "rhemawave") — works in both dev (bin/rhemawave → share/rhemawave/) and Nix install ($out/bin/rhemawave → $out/share/rhemawave/).
OpenGL Context: Uses OpenGL 3.3 Core Profile with VSync enabled. The page base class sets up a reusable quad VAO/VBO for texture and solid color rendering.
Database Location: ~/.local/share/rhemawave/data.db - Contains configuration, proclamations, and log entries
Fullscreen Mode: Uses SDL_WINDOW_FULLSCREEN_DESKTOP to match native display resolution
Display Orientation: Handled by graphics.SetDisplayOrientation() which swaps width/height for portrait displays
Version Injection: Git version is injected at build time via ldflags: -X 'rhemawavetech.com/rhemawave/internal/page.GitVersion=${VERSION}'
Network Manager: Uses D-Bus to communicate with NetworkManager for WiFi configuration and connectivity status
Development Workflow
Adding a New Page
- Create
internal/page/<pagename>.go - Implement the
page.Pageinterface - Embed
page.Basefor common functionality - Register keybind in
application.initKeybinds()if needed - Add navigation logic from existing pages
Modifying Database Schema
- Update models in
internal/db/db.go - GORM AutoMigrate will handle schema changes on next run
- Add seed data logic if needed in
ensureDefaultsExist(),seedDefaultProclamations(), orseedDefaultTOSSections()
Adding New Assets
- Place files in appropriate
share/rhemawave/subdirectory - They will be included automatically in Nix builds via
postInstall - Access via config values (
assets_dir) or direct paths
Working with Shaders
- Create
.vertand.fragfiles inshare/rhemawave/shaders/ - Load via
shaderManager.GetShader("<name>") - Shader must have uniforms:
u_mvpMatrix,u_colororu_textColor,u_texture(if using textures)
Important Constraints
- Must build with
CGO_ENABLED=1for SDL2 bindings - Target platform is Linux (uses D-Bus for NetworkManager)
- Application expects to run as kiosk user with polkit rules for system operations
- Assets are resolved relative to the binary (
../share/rhemawave), sobin/andshare/must be siblings - Version string comes from git tags (release) or commit hash (dev)
- ShaderManager, FontManager, and Logger use
sync.Oncesingletons — they must be initialized (via theirInit()orInitialize()functions) before callingGet(); callingGet()before initialization panics - Deployed via NixOS-Anywhere to kiosk hardware