Skip to content

Paint: fix stuck pen, add Move/Draw/Erase modes and canvas persistence - #238

Merged
mishamyte merged 2 commits into
devfrom
fix/paint-stuck-pen
Jul 23, 2026
Merged

Paint: fix stuck pen, add Move/Draw/Erase modes and canvas persistence#238
mishamyte merged 2 commits into
devfrom
fix/paint-stuck-pen

Conversation

@mishamyte

Copy link
Copy Markdown
Collaborator

Closes #14

The bug

Pressing Ok together with a direction key easily turns into an accidental long Ok press, which toggled a hidden isDrawing flag with zero visual indication. While it was set, every short Ok press toggled the cell and then immediately force-repainted it, so short presses could neither lift the pen nor erase anything - the pen looked permanently stuck.

The fix

Implements the mode switch the issue author asked for, keeping "push to paint":

  • Long Ok cycles Move -> Draw -> Erase. The cursor is drawn in XOR (always visible on both painted and empty cells) and its shape shows the active mode: small dot = Move, full cell = Draw, hollow frame = Erase. An accidental long press is now immediately visible.
  • Short Ok toggles the cell under the cursor in Move mode, and in Draw/Erase mode lifts the pen or eraser (one tap to recover - the literal "can't lift pencil" fix).
  • Arrows react to press, long and repeat events, so holding a key keeps moving; with the pen down this draws or erases a line. Strokes mark both the cell being left and the cell entered, so they include their starting cell. Previously held keys did nothing.
  • Hold Back clears the canvas and lifts the pen; Back saves and exits.

Save/load (also requested in #14)

The canvas is saved on exit to /ext/apps_data/paint/canvas.bin (magic header + board) and restored on the next start. Hardened per review: written to a temp file with sync + rename so a failed write cannot destroy the previous save; a failed save shows the standard storage error dialog instead of silently losing the drawing; a corrupt or foreign file is rejected and loaded bytes are normalized to valid booleans.

Cleanups

  • PaintData was never initialized after malloc
  • The event loop mutated state without taking the mutex the draw callback locks
  • RECORD_NOTIFICATION was closed without ever being opened
  • Event-queue leak on the mutex allocation failure path
  • Viewport is now torn down before saving, so a hanging SD write cannot park the GUI thread on a full event queue
  • Dead code in the clear handler, wrong screen size in a comment

Adds CHANGELOG.md backfilled from repo history (1.0 initial, 1.1-1.3 catalog bumps), bumps fap_version to 1.4. Builds clean under standalone ufbt (API 88.0), ufbt format applied. Reviewed with the code/comments/silent-failure agents and the simplifier pass.

🤖 Generated with Claude Code

Fixes #14

The pen appeared stuck because a long Ok press toggled a hidden
isDrawing flag with no visual indication, and while it was set every
short Ok press force-repainted the cell it had just toggled, so the
pen could never be lifted or a dot erased.

- Replace the hidden flag with three modes cycled by a long Ok press:
  Move, Draw, Erase; the XOR cursor shape shows the active mode
  (small dot / full cell / hollow frame), so an accidental long press
  is visible and one short press away from recovery
- Short Ok toggles the cell in Move mode and lifts the pen or eraser
  in Draw/Erase modes
- Arrows now react to press, long and repeat events, so holding a key
  keeps moving (and draws or erases a line with the pen down);
  strokes mark both the cell left and the cell entered
- Save the canvas on exit (temp file + sync + rename so a failed
  write cannot destroy the previous save) and restore it on start;
  a failed save shows a storage error dialog instead of silently
  losing the drawing
- Tear down the viewport before saving so a hanging SD write cannot
  freeze the GUI thread on a full event queue
- Fix uninitialized PaintData, missing mutex use in the event loop,
  RECORD_NOTIFICATION closed without being opened, an event-queue
  leak on the mutex failure path and dead code in the clear handler
- Add CHANGELOG.md backfilled from repo history; bump to 1.4

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added pack/catalog App in apps_source_code (catalog) category/games App category: games labels Jul 23, 2026
Review/simplify follow-up. The hand-rolled save/load reimplemented
lib/toolbox/saved_struct, the established persistence helper used by
sibling apps (ir_intervalometer, air_arkanoid). Route through it:

- saved_struct_save/saved_struct_load replace the manual open, read,
  write, close and size-check plumbing; they embed a magic, version and
  checksum, so the 4-byte magic constant and the 0/1 byte-normalization
  loop are gone (the checksum rejects a corrupt payload wholesale).
- Keep the deliberate crash-safety: save to the temp file then rename it
  over the real save, so a failed write cannot destroy the previous
  canvas. Still memset the board to an empty canvas on any load failure.

No behavior change: save on exit, restore on start, reject foreign or
corrupt files (now checksum-backed), show the storage error dialog when
the save cannot be written. Net -18 lines. Builds clean under ufbt
(API 88.0), ufbt format applied.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mishamyte mishamyte self-assigned this Jul 23, 2026
@mishamyte mishamyte added this to the unlshd-090 milestone Jul 23, 2026
@mishamyte
mishamyte merged commit 13ed824 into dev Jul 23, 2026
2 checks passed
@mishamyte
mishamyte deleted the fix/paint-stuck-pen branch July 23, 2026 20:27
Repository owner locked as resolved and limited conversation to collaborators Jul 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

category/games App category: games pack/catalog App in apps_source_code (catalog)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

paint: cant lift pencile after pressing two buttons at once

1 participant