Architecture
Cardagain is a Rust application using SDL2 for rendering and input. It’s designed for simplicity and low resource usage on handheld devices.
Project structure
src/
├── main.rs # Entry point: SDL2 init, panic hooks, main loop
├── app.rs # Screen state machine (MainMenu → DeckSelect → Study → Stats)
├── data.rs # Card, Deck, Review models + SM-2 scheduler
├── import.rs # CSV/JSON deck import logic
├── input.rs # Keyboard + gamecontroller input mapping
├── render.rs # SDL2 canvas, font rendering, UI primitives
└── study.rs # Study session state machine (Front → Flip → Rate)
fonts/ # Bundled fonts (copied into release zip)
gameinfo.xml # EmulationStation metadata
scripts/
├── build-aarch64.sh # Cross-compile script
├── docker-build.sh # Docker-based aarch64 build
├── Dockerfile.build # Ubuntu 20.04 + SDL2 build image
└── package-tools.sh # Create release zip
book.toml # mdbook configuration
justfile # Build recipes (native, aarch64, package)
Data flow
Startup
→ Load data.json (or create demo deck)
→ Scan for .csv/.json deck files
→ Import new decks
→ Save data.json
Main loop (60 FPS)
→ Poll input (keyboard + controller)
→ Update app state
→ Render current screen
→ Sleep to cap at ~30 FPS (save battery)
Study session
→ Build due card queue (new first, then by due date)
→ Show front → wait for flip
→ Show back → wait for rating
→ Apply SM-2 schedule
→ Save after each rating
Key design decisions
- No allocations in hot path: Textures are recreated per frame (simple but not optimal; acceptable for a flashcard app)
- Immediate-mode UI: No widget tree; draw calls happen directly in
draw_*methods - Blocking save:
data.jsonis rewritten synchronously after each rating. Cards are small; this is fast enough - No threading: Single-threaded; SDL2 event pump drives everything
Screen state machine
[MainMenu] --A(Study)--> [DeckSelect] --A(start session)--> [Study]
B(back) |
|
[MainMenu] <----------------------B(abandon)----------------------|
| |
|----------------A(Stats)----------------> [Stats] <---------|
Memory usage
Typical footprint on RG35XXSP:
| Component | Size |
|---|---|
| Binary | ~500 KB |
| Heap (fonts + textures) | ~5-10 MB |
| Data (1000 cards) | ~200 KB JSON |
| Total | < 20 MB |
Well within the RG35XXSP’s 1GB RAM.