202 lines
8.5 KiB
Markdown
202 lines
8.5 KiB
Markdown
# 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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.go` is the main entry point, calls `internal/application.Run()`
|
|
- `internal/application/application.go` contains the core application lifecycle
|
|
|
|
### Core Application Flow
|
|
1. **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
|
|
|
|
2. **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
|
|
|
|
3. **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 resources
|
|
- `HandleEvent(event *sdl.Event) error` - Handle input
|
|
- `Update(dt float32) error` - Update state
|
|
- `Render() error` - Draw to screen
|
|
- `Destroy() 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 page
|
|
- `UnwindToPage(p)` - Pop back to a specific page (or create if not in stack)
|
|
|
|
**Available Pages:**
|
|
- `Welcome` - Initial screen showing version
|
|
- `ScrollerPage` - Main proclamation player (F2)
|
|
- `Settings` - Settings menu (F3)
|
|
- `WiFiSetupPage` - Network configuration
|
|
- `LicenseKeyPage` - License key entry
|
|
- `EmailAddressPage` - Email address entry
|
|
- `AudioDevices` - Audio device selection
|
|
- `ProclamationOptions` - Proclamation selection
|
|
- `TOSPage` - Terms of Service acceptance screen (scrollable, Y/N to accept/decline)
|
|
|
|
### Key Global Keybinds
|
|
Defined in `application.initKeybinds()`:
|
|
- `F1` - Navigate to Welcome page
|
|
- `F2` - Navigate to Player (ScrollerPage)
|
|
- `F3` - Navigate to Settings
|
|
- `SHIFT+UP` - Increase volume
|
|
- `SHIFT+DOWN` - Decrease volume
|
|
- `RIGHT_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
|
|
1. Create `internal/page/<pagename>.go`
|
|
2. Implement the `page.Page` interface
|
|
3. Embed `page.Base` for common functionality
|
|
4. Register keybind in `application.initKeybinds()` if needed
|
|
5. Add navigation logic from existing pages
|
|
|
|
### Modifying Database Schema
|
|
1. Update models in `internal/db/db.go`
|
|
2. GORM AutoMigrate will handle schema changes on next run
|
|
3. Add seed data logic if needed in `ensureDefaultsExist()`, `seedDefaultProclamations()`, or `seedDefaultTOSSections()`
|
|
|
|
### Adding New Assets
|
|
1. Place files in appropriate `share/rhemawave/` subdirectory
|
|
2. They will be included automatically in Nix builds via `postInstall`
|
|
3. Access via config values (`assets_dir`) or direct paths
|
|
|
|
### Working with Shaders
|
|
1. Create `.vert` and `.frag` files in `share/rhemawave/shaders/`
|
|
2. Load via `shaderManager.GetShader("<name>")`
|
|
3. Shader must have uniforms: `u_mvpMatrix`, `u_color` or `u_textColor`, `u_texture` (if using textures)
|
|
|
|
## Important Constraints
|
|
|
|
- Must build with `CGO_ENABLED=1` for 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`), so `bin/` and `share/` must be siblings
|
|
- Version string comes from git tags (release) or commit hash (dev)
|
|
- ShaderManager, FontManager, and Logger use `sync.Once` singletons — they must be initialized (via their `Init()` or `Initialize()` functions) before calling `Get()`; calling `Get()` before initialization panics
|
|
- Deployed via NixOS-Anywhere to kiosk hardware
|