Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
b2809fd
feat: factory LUT grayscale rendering engine and perf optimizations
Apr 19, 2026
5e0976b
feat: PXC/XTC viewers, sleep screen, and EPUB factory rendering
Apr 19, 2026
53e300c
feat: grayscale screenshot capture for factory LUT renders
Apr 19, 2026
29e8d75
fix: remove contrast boost from grayscale image quantization
Apr 22, 2026
a405dd0
fix: scope contrast boost to 1-bit thumbnails only, not 2-bit image q…
Apr 22, 2026
a65c4bd
chore: clang-format
Apr 22, 2026
1d135f4
chore: point open-x4-sdk to community-sdk PR#33 (factory LUT grayscale)
Apr 23, 2026
edea5a4
Use HALF_REFRESH instead of FULL_REFRESH for Blank and Logo sleep scr…
pablohc Apr 23, 2026
548c84b
Use direct XTCH plane rendering for 2-bit sleep screen covers
pablohc Apr 23, 2026
228ac94
Use direct XTC page rendering for 1-bit sleep screen covers
pablohc Apr 23, 2026
c791f09
fix: add HALF_REFRESH pre-conditioning for XTCH 2-bit sleep cover fro…
pablohc Apr 23, 2026
bb69393
fix: restore 'Entering Sleep' overlay in sleep screen
pablohc Apr 23, 2026
ee67faa
fix: Atkinson 1-bit dithering for XTCH 2-bit home cover thumbnails
pablohc Apr 23, 2026
2bab76c
fix: Atkinson 1-bit dithering for XTC 1-bit home cover thumbnails
pablohc Apr 23, 2026
565c77f
refactor: use Atkinson1BitDitherer and computeSrcRange helper
pablohc Apr 23, 2026
50bb37e
style: apply clang-format to Xtc.cpp
pablohc Apr 23, 2026
9f3bb61
fix: resolve leftover conflict markers in SleepActivity.cpp
pablohc Apr 23, 2026
b8fa515
fix: unify XTCH 2-bit thumb generation to sequential dithered 1-bit BMP
pablohc Apr 24, 2026
f9be6d9
fix: use area-averaged strip processing for XTCH 2-bit thumb generation
pablohc Apr 24, 2026
56f5e90
fix: validate I/O return values in 2-bit cover BMP generation
pablohc Apr 24, 2026
7fa2b3d
fix: reject oversized PXC files to prevent out-of-bounds write
pablohc Apr 24, 2026
3c06bc0
fix: correct BMP header array size from 70 to 74 bytes
pablohc Apr 24, 2026
c47074b
fix: remove truncated thumbnail on failure in 2-bit thumb generation
pablohc Apr 24, 2026
1233a12
refactor: replace duplicated clamp-and-scale with computeSrcRange in …
pablohc Apr 24, 2026
16fec5c
Remove X3 device-specific grayscale mode handling
itsthisjustin Apr 27, 2026
f0a032f
Disable factory gray mode for X3 devices
itsthisjustin Apr 27, 2026
763d06b
Remove outdated display mode comments
itsthisjustin Apr 27, 2026
8ac04d0
fix: gate 1-bit XTC sleep pre-flash on lastSleepFromReader
Apr 27, 2026
adb7ab2
fix: tighten PXC validation and screenshot BW restore
Apr 27, 2026
2b831ec
docs: explain why sleep screenshot leaves controller desynced
Apr 27, 2026
265fb12
fix: correct Bayer dither thresholds for 4-level palette
Apr 28, 2026
1d72e23
fix: raise EPUB image dither thresholds for factory LUT
Apr 28, 2026
5c14403
Revert "fix: raise EPUB image dither thresholds for factory LUT"
Apr 28, 2026
423c086
fix: adjust dither thresholds and add soft-shoulder for factory LUT
Apr 28, 2026
0e425fd
Merge branch 'master' into feat/lut-pxc
osteotek Apr 30, 2026
97cf9ed
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
May 2, 2026
65599c6
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
May 4, 2026
b3fc76e
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
May 4, 2026
a871656
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
zgredex May 5, 2026
e2ffc4b
fix: reduce Bayer dither soft-shoulder and rebalance thresholds for f…
pablohc May 5, 2026
fcebe9f
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
zgredex May 6, 2026
bfb07c6
Merge branch 'feat/lut-pxc' of github.com:zgredex/crosspoint-reader i…
zgredex May 6, 2026
f6a6ddb
fix: PXC sleep screens always use factory LUT grayscale
zgredex May 8, 2026
c0bea86
chore: update SDK to fix X3 factory fast mode fallback
zgredex May 8, 2026
52ddb1f
chore: update SDK to fix grayscaleRevert logic bug
zgredex May 8, 2026
f4960a3
fix: resolve PR1614 xtc status bar merge
zgredex May 8, 2026
1df0060
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
zgredex May 8, 2026
7ee4f6f
chore: apply clang-format fix for xtc reader
zgredex May 8, 2026
07309bf
merge: resolve conflicts with upstream/master
zgredex May 9, 2026
9d03cae
fix: declare gray cleanup intent + drop redundant RED RAM rebase
zgredex May 9, 2026
63e1dad
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
zgredex May 9, 2026
368b83b
feat: PXC viewer sibling navigation and set-sleep-cover
zgredex May 9, 2026
783b3c0
add 2-bit XTC render quality setting (Speed/Quality)
zgredex May 12, 2026
4bc9dd2
add prev/next labels to BMP/PXC viewers + Left/Right bindings
zgredex May 12, 2026
843b213
Merge remote-tracking branch 'upstream/master' into feat/lut-pxc
zgredex May 12, 2026
f22edb4
fix: clean sleep and XTC transitions
zgredex May 13, 2026
53a86f5
chore: point SDK to combined grayscale fixes
zgredex May 13, 2026
f687b03
fix: clean factory grayscale transitions
zgredex May 13, 2026
30baf8f
fix: full preflash grayscale sleep screens
zgredex May 13, 2026
c0a6a2a
fix: use SDK factory gray sleep handling
zgredex May 14, 2026
38bcba4
fix: stabilize factory gray sleep screens
zgredex May 15, 2026
9266211
refactor: GrayscaleDriveMode + stock-V5.5.9 factory experiments
zgredex May 16, 2026
400b0bc
chore: bump open-x4-sdk to 4e949dc (VCOM restore in factory Activate)
zgredex May 17, 2026
40bd133
chore: bump open-x4-sdk to 7c8afd0 (revert VCOM restore for stock-match)
zgredex May 17, 2026
ba72549
feat: re-enable precondition with stock-match (CTRL2=0xF7)
zgredex May 17, 2026
f024256
chore: bump open-x4-sdk to b4b9d39 (revert byte-exact LUT experiment)
zgredex May 17, 2026
57b56c6
feat: extend stock-match LUT-before-RAM order to all factory paths
zgredex May 17, 2026
0bd54cc
fix: set displayState=FactoryLut after factory activate in all render…
pablohc May 18, 2026
2123fce
chore: update SDK to merged-sdk (factory LUT + power button hold time)
pablohc May 18, 2026
44b71b7
feat: port crossink 'read book move' feature to crosspoint (#2032)
mcrosson May 18, 2026
fe308d9
feat: Themed reader menus (#1072)
CaptainFrito May 18, 2026
f896238
chore: add 3-minute sleep option (#1948)
WuTofu May 17, 2026
576f807
feat: add deadline-based cover generation cancellation pipeline
pablohc Apr 23, 2026
ebee2a3
feat: add theme support for cover settings with classical book cover …
pablohc Apr 23, 2026
1dffa6a
fix: compilation errors in deadline pipeline and theme cover rendering
pablohc Apr 25, 2026
6cd4556
style: fix clang-format for LLVM 21 CI
pablohc Apr 25, 2026
5616d2d
fix: jump page on hold in font family and language selection (#1925)
zgredex May 12, 2026
bf18f4b
refactor: Removed SdCardFontGlobals.h (#1962)
znelson May 13, 2026
70a4ced
fix: KOSync authentication with Calibre-Web-Automated (#1951)
drbourbon May 13, 2026
8633799
refactor: Added utils for non-throwing memory allocation and scoped c…
znelson May 14, 2026
e01632e
chore: Removed unused icon header files (#1975)
znelson May 14, 2026
3ffd6a6
feat: Add Polish hyphenation support (#1590)
IjonFryderyk May 14, 2026
e76ebb2
fix: Prepare SD card font caches from txt reader (#1973)
znelson May 15, 2026
8b93fb3
fix: Add documentation for USB-locked Xteink devices (#1990)
itsthisjustin May 15, 2026
9601a79
fix: update README.md to reflect the current state of crosspoint (#1812)
Uri-Tauber May 15, 2026
49bffae
chore: Update version to 1.3.0 (#1827)
znelson May 15, 2026
f8c7dfc
fix: silent-reboot on wifi activity exit to clear heap fragmentation …
jeremydk May 16, 2026
d712b45
fix: update Italian translation (#1970)
alan0ford May 16, 2026
1afd8b1
fix: several QoL updates for SD font's UI (#1965)
WuTofu May 16, 2026
493b8f9
feat: Add swedish hyphenation (#1637)
steka May 16, 2026
32c76a4
fix: use power button held time for shutdown logic (#1890)
marcinoktawian May 16, 2026
9ceeaaa
fix: prevent card overflow on screens (#1943)
Kirillka8996 May 16, 2026
66fd083
fix: update URL-encoded image during EPUB optimization (#1985)
SimoneFelici May 16, 2026
4d883ed
chore: Update spanish.yaml (#2011)
mvidelatraduc May 16, 2026
2a76281
feat: add the Domitian font family (#2016)
matteoscopel May 17, 2026
c0e6f78
fix: harden EPUB optimiser UI gating, size reporting, and picker tear…
zgredex May 17, 2026
9037b8f
feat: update Russian translation (#2017)
muhas May 17, 2026
ea54af4
feat: apply custom cross logo to boot and sleep screens
pablohc May 17, 2026
b32e818
style: reformat CrossLarge.h byte array layout
pablohc May 17, 2026
8ade649
feat: add power button action menu
pablohc May 15, 2026
d4f36f4
fix: wrap action menu screenshot in RenderLock to prevent persistent …
pablohc May 15, 2026
13c456d
fix: resolve cppcheck knownConditionTrueFalse false positive
pablohc May 15, 2026
130612c
refactor: separate menu action state and position popup at 40% height
pablohc May 15, 2026
ed1f342
refactor: remove border drawing from bottom and side button hints
pablohc Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 59 additions & 23 deletions .skills/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ find src -name "*.cpp" -o -name "*.h" | xargs clang-format -i
6. `constexpr` First: Compile-time constants and lookup tables must be `constexpr`, not just `static const`. This moves computation to compile time, enables dead-branch elimination, and guarantees flash placement. Use `static constexpr` for class-level constants.
7. `std::vector` Pre-allocation: Always call `.reserve(N)` before any `push_back()` loop. Each growth event allocates a new block (2×), copies all elements, then frees the old one — three heap operations that fragment DRAM. When the final size is unknown, estimate conservatively.
8. SPIFFS Write Throttling: Never write a settings file on every user interaction. Guard all writes with a value-change check (`if (newVal == _current) return;`). Progress saves during reading must be debounced — write on activity exit or every N page turns, not on every turn. SPIFFS sectors have a finite erase cycle limit.
9. `new` is not nothrow on ESP32: With `-fno-exceptions`, bare `new` that fails calls `abort()` — it does NOT return `nullptr`. Always use `new (std::nothrow)` and null-check the result, or use `makeUniqueNoThrow<T>()` from `lib/Memory/Memory.h`. Never write bare `new` for any fallible allocation.

---

Expand Down Expand Up @@ -266,43 +267,78 @@ When a template is necessary, limit instantiations: use explicit template instan

**Rules**: NO exceptions, NO abort(), ALWAYS log before error return

### Acceptable malloc/free Patterns
### Heap Buffer Allocation

**Source**: [src/activities/home/HomeActivity.cpp:166](../src/activities/home/HomeActivity.cpp), [lib/GfxRenderer/GfxRenderer.cpp:439-440](../lib/GfxRenderer/GfxRenderer.cpp)
**Prefer `makeUniqueNoThrow` over `malloc`.** Both are nothrow (return `nullptr` on OOM rather than calling `abort()`), but `malloc` requires a manual `free` on every return path — a common source of leaks. `makeUniqueNoThrow<uint8_t[]>(size)` from `lib/Memory/Memory.h` frees automatically when it goes out of scope.

Despite "prefer stack allocation," malloc is acceptable for:
1. **Large temporary buffers** (> 256 bytes, won't fit on stack)
2. **One-time allocations** during activity initialization
3. **Bitmap rendering buffers** (variable size, used briefly)

**Pattern**:
**Preferred pattern**:
```cpp
// Allocate
auto* buffer = static_cast<uint8_t*>(malloc(bufferSize));
#include <Memory.h>

auto buffer = makeUniqueNoThrow<uint8_t[]>(bufferSize);
if (!buffer) {
LOG_ERR("MODULE", "malloc failed: %d bytes", bufferSize);
return false; // Handle allocation failure
LOG_ERR("MODULE", "OOM: %d bytes", bufferSize);
return false;
}

// Use buffer
processData(buffer, bufferSize);
processData(buffer.get(), bufferSize);
// freed automatically — no manual free needed, no leak on early return
```

// Free immediately after use
free(buffer);
buffer = nullptr;
**`malloc` or `new (std::nothrow)` are still acceptable** when the buffer must be passed to a C API that takes ownership and frees it itself (e.g., certain SDK callbacks). In that case follow the manual pattern:
```cpp
auto* buffer = static_cast<uint8_t*>(malloc(bufferSize)); // or new (std::nothrow) uint8_t[bufferSize]
if (!buffer) {
LOG_ERR("MODULE", "OOM: %d bytes", bufferSize);
return false;
}
sdkApiThatTakesOwnership(buffer, bufferSize); // SDK calls free() / delete[]
```

**Rules**:
- **ALWAYS check for nullptr** after malloc
- **Free immediately** after use (don't hold across multiple operations)
- **Set to nullptr** after free (avoid use-after-free)
- **Document size**: Comment why stack allocation was rejected
- **Prefer `makeUniqueNoThrow`** — automatic cleanup eliminates leak risk on error paths
- **ALWAYS check for nullptr** after any allocation and `LOG_ERR` before returning false
- **Raw allocation only** when a C API takes ownership; document why in a comment

**Examples in codebase**:
- Memory utilities: [Memory.h](../lib/Memory/Memory.h) (`makeUniqueNoThrow`)
- Cover image buffers: [HomeActivity.cpp:166](../src/activities/home/HomeActivity.cpp)
- Text chunk buffers: [TxtReaderActivity.cpp:259](../src/activities/reader/TxtReaderActivity.cpp)
- Bitmap rendering: [GfxRenderer.cpp:439-440](../lib/GfxRenderer/GfxRenderer.cpp)
- OTA update buffer: [OtaUpdater.cpp:40](../src/network/OtaUpdater.cpp)

### Heap Allocation with `new`: Always Use `makeUniqueNoThrow`

**CRITICAL**: With `-fno-exceptions`, bare `new` on OOM calls `abort()` — it does NOT return `nullptr`. Always use `makeUniqueNoThrow` from `lib/Memory/Memory.h`, which wraps `new (std::nothrow)` and returns a `std::unique_ptr` that is null on OOM and automatically frees on scope exit.

**Preferred pattern**:
```cpp
#include <Memory.h>

auto obj = makeUniqueNoThrow<MyClass>(args);
if (!obj) { LOG_ERR("MOD", "OOM: MyClass"); return false; }

auto buf = makeUniqueNoThrow<uint8_t[]>(size);
if (!buf) { LOG_ERR("MOD", "OOM: %d bytes", size); return false; }

// Pass to C APIs via .get(); unique_ptr frees automatically on return
someApi(buf.get(), size);
```

**`new (std::nothrow)` directly is acceptable** when the object must be passed to a C API that takes ownership and calls `delete` itself:
```cpp
auto* obj = new (std::nothrow) MyClass(args);
if (!obj) { LOG_ERR("MOD", "OOM: MyClass"); return false; }
sdkApiThatTakesOwnership(obj); // SDK calls delete
```

**Rules**:
- **Prefer `makeUniqueNoThrow`** — automatic cleanup eliminates leak risk on error paths
- **NEVER use bare `new`** — always `makeUniqueNoThrow` or `new (std::nothrow)`
- **ALWAYS `LOG_ERR` before returning false** on OOM
- **Use `.get()`** to pass the raw pointer to C-style APIs; ownership stays with the `unique_ptr`
- **`new (std::nothrow)` directly only** when a C API takes ownership; document why in a comment

**Examples in codebase**:
- Memory utilities: [Memory.h](../lib/Memory/Memory.h) (`makeUniqueNoThrow`)

---

Expand Down
Loading
Loading