Compare commits

..

No commits in common. "e348c7911395757c24b70d5c9806a596c3b3242c" and "02ab142d9602b601e1a729d24f9716a249dc6f4e" have entirely different histories.

11 changed files with 2118 additions and 778 deletions

View File

@ -34,7 +34,7 @@ No test suite, no linter.
## Architecture ## Architecture
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/`. 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).
Key sections in order: Key sections in order:
- **Decoder struct** — holds all FFmpeg state (format/codec contexts, swr resampler, album art texture) - **Decoder struct** — holds all FFmpeg state (format/codec contexts, swr resampler, album art texture)
@ -56,4 +56,3 @@ Uses SDL2 (not SDL3). Uses `#if LIBAVUTIL_VERSION_INT` preprocessor checks to su
- Non-fatal errors go to stderr and continue; fatal errors (SDL init failures) abort via `panic_and_abort()` - Non-fatal errors go to stderr and continue; fatal errors (SDL init failures) abort via `panic_and_abort()`
- Update the changelog in `docs/sdlamp2-fsd.md` when making changes - Update the changelog in `docs/sdlamp2-fsd.md` when making changes
- Never run privileged Docker containers or make system-wide changes without explicit approval; explain what's needed and let the owner do it manually - Never run privileged Docker containers or make system-wide changes without explicit approval; explain what's needed and let the owner do it manually
- Never install global Python packages; use a temporary venv in `/tmp` when Python dependencies are needed (e.g. `python3 -m venv /tmp/venv && source /tmp/venv/bin/activate && pip install ...`)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -143,8 +143,8 @@ The `dmenu_ln` script already supports switching the startup binary via config f
```sh ```sh
scp build/sdlamp2 root@rg35xx:/mnt/vendor/bin/sdlamp2 scp build/sdlamp2 root@rg35xx:/mnt/vendor/bin/sdlamp2
scp device/rg35xx/rg35xx-wrapper.sh root@rg35xx:/mnt/vendor/bin/rg35xx-wrapper.sh scp tools/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 scp tools/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: 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 fi
``` ```
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. 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.
3. **Enable sdlamp2 on boot:** 3. **Enable sdlamp2 on boot:**

View File

@ -7,7 +7,7 @@
| Version | 1.0 | | Version | 1.0 |
| Status | Draft | | Status | Draft |
| Created | 2026-02-10 | | Created | 2026-02-10 |
| Updated | 2026-02-15 | | Updated | 2026-02-14 |
## 1. Purpose ## 1. Purpose
@ -43,17 +43,6 @@ This document specifies the functional requirements for an SDL2 based media play
## 6. Changelog ## 6. Changelog
### 2026-02-15 — Transparent controls spritesheet support
- **Alpha blending on controls texture**: `SDL_SetTextureBlendMode(controls_texture, SDL_BLENDMODE_BLEND)` enables alpha transparency for the controls spritesheet. Sprite icons now float cleanly on any background color instead of showing white cell backgrounds.
- **Transparent skin template**: `gen_skin_template.py` now generates cells with transparent backgrounds (RGBA) instead of white. Gutters use bright magenta (`#FF00FF`) so they're clearly distinguishable from transparent content areas.
### 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 ### 2026-02-14 — Softer background, remove panel divider
- **Background color**: Changed from white (`#FFFFFF`) to a medium gray (`#979797`) for a gentler appearance. - **Background color**: Changed from white (`#FFFFFF`) to a medium gray (`#979797`) for a gentler appearance.

File diff suppressed because it is too large Load Diff

View File

@ -612,7 +612,7 @@ int main(int argc, char** argv) {
const SDL_Rect play_sprite = {220, 0, 200, 200}; const SDL_Rect play_sprite = {220, 0, 200, 200};
const SDL_Rect ff_sprite = {440, 0, 200, 200}; const SDL_Rect ff_sprite = {440, 0, 200, 200};
const SDL_Rect stop_sprite = {0, 220, 200, 200}; const SDL_Rect stop_sprite = {0, 220, 200, 200};
const SDL_Rect prev_sprite = {220, 220, 200, 200}; const SDL_Rect prev_sprite = {440, 220, 200, 200};
const SDL_Rect next_sprite = {440, 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 */ const SDL_Rect circle_sprite = {440, 220, 200, 200}; /* placeholder for no-art */
@ -699,7 +699,6 @@ int main(int argc, char** argv) {
if (!controls_texture) { if (!controls_texture) {
panic_and_abort("Could not create controls texture!", SDL_GetError()); panic_and_abort("Could not create controls texture!", SDL_GetError());
} }
SDL_SetTextureBlendMode(controls_texture, SDL_BLENDMODE_BLEND);
/* Handle SIGTERM/SIGINT for clean shutdown (save position before exit) */ /* Handle SIGTERM/SIGINT for clean shutdown (save position before exit) */
signal(SIGTERM, signal_handler); signal(SIGTERM, signal_handler);
@ -958,11 +957,11 @@ int main(int argc, char** argv) {
SDL_RenderCopy(renderer, controls_texture, &ff_sprite, &ff_btn); SDL_RenderCopy(renderer, controls_texture, &ff_sprite, &ff_btn);
SDL_RenderCopy(renderer, controls_texture, &next_sprite, &next_btn); SDL_RenderCopy(renderer, controls_texture, &next_sprite, &next_btn);
/* Focus highlight — 3px red border around focused element */ /* Focus highlight — 3px blue border around focused element */
{ {
const SDL_Rect r = *focus_rects[focus_index]; const SDL_Rect r = *focus_rects[focus_index];
const int t = 3; const int t = 3;
SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF); SDL_SetRenderDrawColor(renderer, 0x00, 0x80, 0xFF, 0xFF);
SDL_Rect top = {r.x - t, r.y - t, r.w + 2 * t, t}; SDL_Rect top = {r.x - t, r.y - t, r.w + 2 * t, t};
SDL_Rect bot = {r.x - t, r.y + r.h, r.w + 2 * t, t}; SDL_Rect bot = {r.x - t, r.y + r.h, r.w + 2 * t, t};
SDL_Rect lft = {r.x - t, r.y, t, r.h}; SDL_Rect lft = {r.x - t, r.y, t, r.h};

View File

@ -1,96 +0,0 @@
#!/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 transparent (ready to draw on). The 20px gutters between
cells are a bright magenta so they're clearly distinguishable from content areas.
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 = (0, 0, 0, 0)
GUTTER_COLOR = (255, 0, 255, 255)
TEXT_COLOR = (255, 255, 255, 255)
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("RGBA", (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()