diff --git a/CLAUDE.md b/CLAUDE.md index e471005..7d48925 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ No test suite, no linter. ## Architecture -Single-file C program: `src/sdlamp2.c` (~650 lines). One generated header: `src/controls_png.h` (embedded PNG byte array — regenerate with `./tools/embed_png.py assets/controls.png src/controls_png.h` if the spritesheet changes). +Single-file C program: `src/sdlamp2.c` (~650 lines). One generated header: `src/controls_png.h` (embedded PNG byte array — regenerate with `python3 tools/embed_png.py assets/controls.png src/controls_png.h` if the spritesheet changes). Skin template: `python3 tools/gen_skin_template.py [output.png]` generates a labeled grid template for creating custom spritesheets (requires Pillow). Device-specific scripts live in `device/rg35xx/`. Key sections in order: - **Decoder struct** — holds all FFmpeg state (format/codec contexts, swr resampler, album art texture) diff --git a/assets/skin_template.png b/assets/skin_template.png new file mode 100644 index 0000000..3ebd381 Binary files /dev/null and b/assets/skin_template.png differ diff --git a/tools/rg35xx-screen-monitor.py b/device/rg35xx/rg35xx-screen-monitor.py similarity index 100% rename from tools/rg35xx-screen-monitor.py rename to device/rg35xx/rg35xx-screen-monitor.py diff --git a/tools/rg35xx-wrapper.sh b/device/rg35xx/rg35xx-wrapper.sh similarity index 100% rename from tools/rg35xx-wrapper.sh rename to device/rg35xx/rg35xx-wrapper.sh diff --git a/docs/rg35xx-plus.md b/docs/rg35xx-plus.md index b986759..d033fb0 100644 --- a/docs/rg35xx-plus.md +++ b/docs/rg35xx-plus.md @@ -143,8 +143,8 @@ The `dmenu_ln` script already supports switching the startup binary via config f ```sh scp build/sdlamp2 root@rg35xx:/mnt/vendor/bin/sdlamp2 - scp tools/rg35xx-wrapper.sh root@rg35xx:/mnt/vendor/bin/rg35xx-wrapper.sh - scp tools/rg35xx-screen-monitor.py root@rg35xx:/mnt/vendor/bin/rg35xx-screen-monitor.py + scp device/rg35xx/rg35xx-wrapper.sh root@rg35xx:/mnt/vendor/bin/rg35xx-wrapper.sh + scp device/rg35xx/rg35xx-screen-monitor.py root@rg35xx:/mnt/vendor/bin/rg35xx-screen-monitor.py ``` 2. **Add the config check** to `/mnt/vendor/ctrl/dmenu_ln`. In the section where `CMD` overrides are checked (after the existing `muos.ini` / `vpRun.ini` checks, before the `app_scheduling` call), add: @@ -155,7 +155,7 @@ The `dmenu_ln` script already supports switching the startup binary via config f fi ``` - The wrapper script handles device-specific concerns (WiFi hotspot, power button monitoring) and launches sdlamp2 as its main foreground process. See `tools/rg35xx-wrapper.sh` for details. + The wrapper script handles device-specific concerns (WiFi hotspot, power button monitoring) and launches sdlamp2 as its main foreground process. See `device/rg35xx/rg35xx-wrapper.sh` for details. 3. **Enable sdlamp2 on boot:** diff --git a/docs/sdlamp2-fsd.md b/docs/sdlamp2-fsd.md index aa174e2..ded41ea 100644 --- a/docs/sdlamp2-fsd.md +++ b/docs/sdlamp2-fsd.md @@ -43,6 +43,12 @@ This document specifies the functional requirements for an SDL2 based media play ## 6. Changelog +### 2026-02-14 — Skin template system and device script reorganization + +- **Skin template generator**: New `tools/gen_skin_template.py` (requires Pillow) generates a 642x420 PNG template showing the sprite grid layout with labeled gutters. Skin creators can draw over the white 200x200 cells; the 20px gray gutters (never rendered by the app) identify each cell's purpose. +- **Separate Prev sprite**: `prev_sprite` now uses the bottom-center cell `{220, 220}` instead of sharing the bottom-right cell with `next_sprite`. This gives Prev and Next distinct sprites in the spritesheet. +- **Device scripts moved**: `rg35xx-wrapper.sh` and `rg35xx-screen-monitor.py` moved from `tools/` to `device/rg35xx/`, separating device-specific scripts from dev tools. + ### 2026-02-14 — Softer background, remove panel divider - **Background color**: Changed from white (`#FFFFFF`) to a medium gray (`#979797`) for a gentler appearance. diff --git a/src/sdlamp2.c b/src/sdlamp2.c index d9db19e..126a555 100644 --- a/src/sdlamp2.c +++ b/src/sdlamp2.c @@ -612,7 +612,7 @@ int main(int argc, char** argv) { const SDL_Rect play_sprite = {220, 0, 200, 200}; const SDL_Rect ff_sprite = {440, 0, 200, 200}; const SDL_Rect stop_sprite = {0, 220, 200, 200}; - const SDL_Rect prev_sprite = {440, 220, 200, 200}; + const SDL_Rect prev_sprite = {220, 220, 200, 200}; const SDL_Rect next_sprite = {440, 220, 200, 200}; const SDL_Rect circle_sprite = {440, 220, 200, 200}; /* placeholder for no-art */ diff --git a/tools/gen_skin_template.py b/tools/gen_skin_template.py new file mode 100644 index 0000000..dd58a1a --- /dev/null +++ b/tools/gen_skin_template.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +""" +gen_skin_template.py — Generate a skin template PNG for sdlamp2. + +Creates a 642x420 PNG showing the sprite grid layout with labeled gutters. +Each 200x200 cell is blank white (ready to draw on). The 20px gutters between +cells are gray with small text labels identifying adjacent cells. + +Grid layout: + Col 0 (0-199) Col 1 (220-419) Col 2 (440-639) + Row 0: REWIND PLAY FF + ----gutter y=200-219 with labels---- + Row 1: STOP PREV NEXT + +Two extra pixels on the right (640-641) are gutter fill to reach 642px width, +matching the spritesheet dimensions. + +Usage: python3 tools/gen_skin_template.py [output.png] + +Requires Pillow. +""" +import sys + +from PIL import Image, ImageDraw, ImageFont + +CELL = 200 +GAP = 20 +COLS = 3 +ROWS = 2 +WIDTH = COLS * CELL + (COLS - 1) * GAP + 2 # 642 +HEIGHT = ROWS * CELL + (ROWS - 1) * GAP # 420 + +CELL_COLOR = (255, 255, 255) +GUTTER_COLOR = (180, 180, 180) +TEXT_COLOR = (80, 80, 80) + +LABELS = [ + ["REWIND", "PLAY", "FF"], + ["STOP", "PREV", "NEXT"], +] + + +def cell_x(col): + return col * (CELL + GAP) + + +def cell_y(row): + return row * (CELL + GAP) + + +def main(): + output_path = sys.argv[1] if len(sys.argv) > 1 else "skin_template.png" + + img = Image.new("RGB", (WIDTH, HEIGHT), GUTTER_COLOR) + draw = ImageDraw.Draw(img) + + # Try to load a small font for labels + try: + font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 11) + except OSError: + try: + font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 11) + except OSError: + font = ImageFont.load_default() + + # Draw white cells + for row in range(ROWS): + for col in range(COLS): + x = cell_x(col) + y = cell_y(row) + draw.rectangle([x, y, x + CELL - 1, y + CELL - 1], fill=CELL_COLOR) + + # Label the horizontal gutter (y = 200..219) + gutter_y = CELL # 200 + for col in range(COLS): + cx = cell_x(col) + CELL // 2 + + # Row 0 label above center of gutter + label_0 = LABELS[0][col] + bbox = draw.textbbox((0, 0), label_0, font=font) + tw = bbox[2] - bbox[0] + draw.text((cx - tw // 2, gutter_y + 1), label_0, fill=TEXT_COLOR, font=font) + + # Row 1 label below center of gutter + label_1 = LABELS[1][col] + bbox = draw.textbbox((0, 0), label_1, font=font) + tw = bbox[2] - bbox[0] + th = bbox[3] - bbox[1] + draw.text((cx - tw // 2, gutter_y + GAP - th - 2), label_1, fill=TEXT_COLOR, font=font) + + img.save(output_path) + print(f"Skin template saved to {output_path} ({WIDTH}x{HEIGHT})") + + +if __name__ == "__main__": + main()