diff --git a/.gitignore b/.gitignore index 64f74b14..ceb6b70b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ build **/__pycache__/ /compile_commands.json /.cache +/.venv diff --git a/README.md b/README.md index 9d4b1dd4..c960af2e 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,8 @@ For more details on the internal file structures, see the [file formats document Contributions are very welcome! +If you are new to the codebase, start with the [contributing docs](./docs/contributing/README.md). + If you're looking for a way to help out, take a look at the [ideas discussion board](https://github.com/crosspoint-reader/crosspoint-reader/discussions/categories/ideas). If there's something there you'd like to work on, leave a comment so that we can avoid duplicated effort. diff --git a/bin/clang-format-fix b/bin/clang-format-fix index 4ad33f48..d4902eb8 100755 --- a/bin/clang-format-fix +++ b/bin/clang-format-fix @@ -1,17 +1,32 @@ #!/usr/bin/env bash -# Check if clang-format is availible -command -v clang-format >/dev/null 2>&1 || { - printf "'clang-format' not found in current environment\n" - printf "install 'clang', 'clang-tools', or 'clang-format' depending on your distro/os and tooling requirements\n" - exit 1 -} +# Check if clang-format is available and pick the preferred binary. +if command -v clang-format-21 >/dev/null 2>&1; then + CLANG_FORMAT_BIN="clang-format-21" +elif command -v clang-format >/dev/null 2>&1; then + CLANG_FORMAT_BIN="clang-format" +else + printf "'clang-format' not found in current environment\n" + printf "Install clang-format-21 (recommended), clang, clang-tools, or clang-format depending on your distro/os and tooling requirements\n" + exit 1 +fi + +set -euo pipefail GIT_LS_FILES_FLAGS="" -if [[ "$1" == "-g" ]]; then +if [[ "${1:-}" == "-g" ]]; then GIT_LS_FILES_FLAGS="--modified" fi +CLANG_FORMAT_VERSION_RAW="$(${CLANG_FORMAT_BIN} --version)" +CLANG_FORMAT_MAJOR="$(printf '%s\n' "${CLANG_FORMAT_VERSION_RAW}" | grep -oE '[0-9]+' | head -n1)" + +if [[ -z "${CLANG_FORMAT_MAJOR}" || "${CLANG_FORMAT_MAJOR}" -lt 21 ]]; then + echo "Error: ${CLANG_FORMAT_BIN} is too old: ${CLANG_FORMAT_VERSION_RAW}" + echo "This repository's .clang-format requires clang-format 21 or newer." + echo "Install clang-format-21 and rerun ./bin/clang-format-fix" + exit 1 +fi # --- Main Logic --- @@ -27,4 +42,4 @@ git ls-files --exclude-standard ${GIT_LS_FILES_FLAGS} \ | grep -v -E '^lib/EpdFont/builtinFonts/' \ | grep -v -E '^lib/Epub/Epub/hyphenation/generated/' \ | grep -v -E '^lib/uzlib/' \ - | xargs -r clang-format -style=file -i + | xargs -r "${CLANG_FORMAT_BIN}" -style=file -i diff --git a/docs/contributing/README.md b/docs/contributing/README.md new file mode 100644 index 00000000..eae9334f --- /dev/null +++ b/docs/contributing/README.md @@ -0,0 +1,11 @@ +# Contributing Docs + +This section is a lightweight contributor guide for CrossPoint Reader. +It is written for software developers who may be new to embedded development. + +- [Getting Started](./getting-started.md) +- [Architecture Overview](./architecture.md) +- [Development Workflow](./development-workflow.md) +- [Testing and Debugging](./testing-debugging.md) + +If you are new, start with [Getting Started](./getting-started.md). diff --git a/docs/contributing/architecture.md b/docs/contributing/architecture.md new file mode 100644 index 00000000..f5478115 --- /dev/null +++ b/docs/contributing/architecture.md @@ -0,0 +1,199 @@ +# Architecture Overview + +CrossPoint is firmware for the Xteink X4 (unaffiliated with Xteink), built with PlatformIO targeting the ESP32-C3 microcontroller. + +At a high level, it is firmware that uses an activity-driven application architecture loop with persistent settings/state, SD-card-first caching, and a rendering pipeline optimized for e-ink constraints. + +## System at a glance + +```mermaid +graph TD + A[Hardware: ESP32-C3 + SD + E-ink + Buttons] --> B[open-x4-sdk HAL] + B --> C[src/main.cpp runtime loop] + C --> D[Activities layer] + C --> E[State and settings] + D --> F[Reader flows] + D --> G[Home/Library/Settings flows] + D --> H[Network/Web server flows] + F --> I[lib/Epub parsing + layout + hyphenation] + I --> J[SD cache in .crosspoint] + D --> K[GfxRenderer] + K --> L[E-ink display buffer] +``` + +## Runtime lifecycle + +Primary entry point is `src/main.cpp`. + +```mermaid +flowchart TD + A[Boot] --> B[Init GPIO and optional serial] + B --> C[Init SD storage] + C --> D[Load settings and app state] + D --> E[Init display and fonts] + E --> F{Resume reader?} + F -->|No| G[Enter Home activity] + F -->|Yes| H[Enter Reader activity] + G --> I[Main loop] + H --> I + I --> J[Poll input and run current activity] + J --> K{Sleep condition met?} + K -->|No| I + K -->|Yes| L[Persist state and enter deep sleep] +``` + +In each loop iteration, the firmware updates input, runs the active activity, handles auto-sleep/power behavior, and applies a short delay policy to balance responsiveness and power. + +## Activity model + +Activities are screen-level controllers deriving from `src/activities/Activity.h`. +Some flows use `src/activities/ActivityWithSubactivity.h` to host nested activities. + +- `onEnter()` and `onExit()` manage setup/teardown +- `loop()` handles per-frame behavior +- `skipLoopDelay()` and `preventAutoSleep()` are used by long-running flows (for example web server mode) + +Top-level activity groups: + +- `src/activities/home/`: home and library navigation +- `src/activities/reader/`: EPUB/XTC/TXT reading flows +- `src/activities/settings/`: settings menus and configuration +- `src/activities/network/`: WiFi selection, AP/STA mode, file transfer server +- `src/activities/boot_sleep/`: boot and sleep transitions + +## Reader and content pipeline + +Reader orchestration starts in `src/activities/reader/ReaderActivity.h` and dispatches to format-specific readers. +EPUB processing is implemented in `lib/Epub/`. + +```mermaid +flowchart LR + A[Select book] --> B[ReaderActivity] + B --> C{Format} + C -->|EPUB| D[lib/Epub/Epub] + C -->|XTC| E[lib/Xtc reader] + C -->|TXT| F[lib/Txt reader] + D --> G[Parse OPF/TOC/CSS] + G --> H[Layout pages/sections] + H --> I[Write section and metadata caches] + I --> J[Render current page via GfxRenderer] +``` + +Why caching matters: + +- RAM is limited on ESP32-C3, so expensive parsed/layout data is persisted to SD +- repeat opens/page navigation can reuse cached data instead of full reparsing + +## Reader internals call graph + +This diagram zooms into the EPUB path to show the main control and data flow from activity entry to on-screen draw. + +```mermaid +flowchart TD + A[ReaderActivity onEnter] --> B{File type} + B -->|EPUB| C[Create Epub object] + B -->|XTC/TXT| Z[Use format-specific reader] + + C --> D[Epub load] + D --> E[Locate container and OPF] + E --> F[Build or load BookMetadataCache] + F --> G[Load TOC and spine] + G --> H[Load or parse CSS rules] + + H --> I[EpubReaderActivity] + I --> J{Section cache exists for current settings?} + J -->|Yes| K[Read section bin from SD cache] + J -->|No| L[Parse chapter HTML and layout text] + L --> M[Apply typography settings and hyphenation] + M --> N[Write section cache bin] + + K --> O[Build page model] + N --> O + O --> P[GfxRenderer draw calls] + P --> Q[HAL display framebuffer update] + Q --> R[E-ink refresh policy] + + S[SETTINGS singleton] -. influences .-> J + S -. influences .-> M + T[APP_STATE singleton] -. persists .-> U[Reading progress and resume context] + U -. used by .-> I +``` + +Notes: + +- "section cache exists" depends on cache-busting parameters such as font and layout-related settings +- rendering favors reusing precomputed layout data to keep page turns responsive on constrained hardware +- progress/session state is persisted so the reader can reopen at the last position after reboot/sleep + +## State and persistence + +Two singletons are central: + +- `src/CrossPointSettings.h` (`SETTINGS`): user preferences and behavior flags +- `src/CrossPointState.h` (`APP_STATE`): runtime/session state such as current book and sleep context + +Typical persisted areas on SD: + +```text +/.crosspoint/ + epub_/ + book.bin + progress.bin + cover.bmp + sections/*.bin + settings.bin + state.bin +``` + +For binary cache formats, see `docs/file-formats.md`. + +## Networking architecture + +Network file transfer is controlled by `src/activities/network/CrossPointWebServerActivity.h` and served by `src/network/CrossPointWebServer.h`. + +Modes: + +- STA: join existing WiFi network +- AP: create hotspot + +Server behavior: + +- HTTP server on port 80 +- WebSocket upload server on port 81 +- file operations backed by SD storage +- activity requests faster loop responsiveness while server is running + +Endpoint reference: `docs/webserver-endpoints.md`. + +## Build-time generated assets + +Some sources are generated and should not be edited manually. + +- `scripts/build_html.py` generates `src/network/html/*.generated.h` from HTML files +- `scripts/generate_hyphenation_trie.py` generates hyphenation headers under `lib/Epub/Epub/hyphenation/generated/` + +When editing related source assets, regenerate via normal build steps/scripts. + +## Key directories + +- `src/`: app orchestration, settings/state, and activity implementations +- `src/network/`: web server and OTA/update networking +- `src/components/`: theming and shared UI components +- `lib/Epub/`: EPUB parser, layout, CSS handling, and hyphenation +- `lib/`: supporting libraries (fonts, text, filesystem helpers, etc.) +- `open-x4-sdk/`: hardware SDK submodule (display, input, storage, battery) +- `docs/`: user and technical documentation + +## Embedded constraints that shape design + +- constrained RAM drives SD-first caching and careful allocations +- e-ink refresh cost drives render/update batching choices +- main loop responsiveness matters for input, power handling, and watchdog safety +- background/network flows must cooperate with sleep and loop timing logic + +## Scope guardrails + +Before implementing larger ideas, check: + +- [SCOPE.md](../../SCOPE.md) +- [GOVERNANCE.md](../../GOVERNANCE.md) diff --git a/docs/contributing/development-workflow.md b/docs/contributing/development-workflow.md new file mode 100644 index 00000000..66a18917 --- /dev/null +++ b/docs/contributing/development-workflow.md @@ -0,0 +1,43 @@ +# Development Workflow + +This page defines the expected local workflow before opening a pull request. + +## 1) Fork and create a focused branch + +- Fork the repository to your own GitHub account +- Clone your fork locally and add the upstream repository if needed + +- Branch from `master` +- Keep each PR focused on one fix or feature area + +## 2) Implement with scope in mind + +- Confirm your idea is in project scope: [SCOPE.md](../../SCOPE.md) +- Prefer incremental changes over broad refactors + +## 3) Run local checks + +```sh +./bin/clang-format-fix +pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high +pio run +``` + +CI enforces formatting, static analysis, and build checks. +Use clang-format 21+ locally to match CI. +If `clang-format` is missing or too old locally, see [Getting Started](./getting-started.md). + +## 4) Open the PR + +- Use a semantic title (example: `fix: avoid crash when opening malformed epub`) +- Fill out `.github/PULL_REQUEST_TEMPLATE.md` +- Describe the problem, approach, and any tradeoffs +- Include reproduction and verification steps for bug fixes + +## 5) Review etiquette + +- Be explicit and concise in responses +- Keep discussions technical and respectful +- Assume good intent and focus on code-level feedback + +For community expectations, see [GOVERNANCE.md](../../GOVERNANCE.md). diff --git a/docs/contributing/getting-started.md b/docs/contributing/getting-started.md new file mode 100644 index 00000000..715ab18a --- /dev/null +++ b/docs/contributing/getting-started.md @@ -0,0 +1,80 @@ +# Getting Started + +This guide helps you build and run CrossPoint locally. + +## Prerequisites + +- PlatformIO Core (`pio`) or VS Code + PlatformIO IDE +- Python 3.8+ +- `clang-format` 21+ in your `PATH` (CI uses clang-format 21) +- USB-C cable +- Xteink X4 device for hardware testing + +If `./bin/clang-format-fix` fails with either of these errors, install clang-format 21: + +- `clang-format: No such file or directory` +- `.clang-format: error: unknown key 'AlignFunctionDeclarations'` + +Examples: + +```sh +# Debian/Ubuntu (try this first) +sudo apt-get update && sudo apt-get install -y clang-format-21 + +# If the package is unavailable, add LLVM apt repo and retry +wget https://apt.llvm.org/llvm.sh +chmod +x llvm.sh +sudo ./llvm.sh 21 +sudo apt-get update +sudo apt-get install -y clang-format-21 + +# macOS (Homebrew) +brew install clang-format +``` + +Then verify: + +```sh +clang-format-21 --version +``` + +The reported major version must be 21 or newer. + +## Clone and initialize + +```sh +git clone --recursive https://github.com/crosspoint-reader/crosspoint-reader +cd crosspoint-reader +``` + +If you already cloned without submodules: + +```sh +git submodule update --init --recursive +``` + +## Build + +```sh +pio run +``` + +## Flash + +```sh +pio run --target upload +``` + +## First checks before opening a PR + +```sh +./bin/clang-format-fix +pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high +pio run +``` + +## What to read next + +- [Architecture Overview](./architecture.md) +- [Development Workflow](./development-workflow.md) +- [Testing and Debugging](./testing-debugging.md) diff --git a/docs/contributing/testing-debugging.md b/docs/contributing/testing-debugging.md new file mode 100644 index 00000000..180e939f --- /dev/null +++ b/docs/contributing/testing-debugging.md @@ -0,0 +1,48 @@ +# Testing and Debugging + +CrossPoint runs on real hardware, so debugging usually combines local build checks and on-device logs. + +## Local checks + +Make sure `clang-format` 21+ is installed and available in `PATH` before running the formatting step. +If needed, see [Getting Started](./getting-started.md). + +```sh +./bin/clang-format-fix +pio check --fail-on-defect low --fail-on-defect medium --fail-on-defect high +pio run +``` + +## Flash and monitor + +Flash firmware: + +```sh +pio run --target upload +``` + +Open serial monitor: + +```sh +pio device monitor +``` + +Optional enhanced monitor: + +```sh +python3 -m pip install pyserial colorama matplotlib +python3 scripts/debugging_monitor.py +``` + +## Useful bug report contents + +- Firmware version and build environment +- Exact steps to reproduce +- Expected vs actual behavior +- Serial logs from boot through failure +- Whether issue reproduces after clearing `.crosspoint/` cache on SD card + +## Common troubleshooting references + +- [User Guide troubleshooting section](../../USER_GUIDE.md#7-troubleshooting-issues--escaping-bootloop) +- [Webserver troubleshooting](../troubleshooting.md)