Compare commits

..

3 Commits

Author SHA1 Message Date
e348c79113 Add GIMP source file for controls spritesheet
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 00:29:31 +01:00
b8d7a2e405 Transparent controls spritesheet, red focus highlight
Enable alpha blending on the controls texture so sprite icons
float on any background without white cell artifacts. Regenerate
skin template with transparent cells and magenta gutters. Change
focus highlight from blue to red.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 00:29:09 +01:00
8a638acdd8 Skin template system, separate prev sprite, reorganize device scripts
Add tools/gen_skin_template.py to generate a labeled 642x420 PNG template
for creating custom spritesheets. Move rg35xx device scripts from tools/
to device/rg35xx/. Point prev_sprite at its own cell (bottom-center) so
Prev and Next can have distinct icons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 23:47:21 +01:00
11 changed files with 778 additions and 2118 deletions

View File

@ -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)
@ -56,3 +56,4 @@ 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()`
- 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 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: 33 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

BIN
assets/skin_template.png Normal file

Binary file not shown.

After

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
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:**

View File

@ -7,7 +7,7 @@
| Version | 1.0 |
| Status | Draft |
| Created | 2026-02-10 |
| Updated | 2026-02-14 |
| Updated | 2026-02-15 |
## 1. Purpose
@ -43,6 +43,17 @@ This document specifies the functional requirements for an SDL2 based media play
## 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
- **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 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 */
@ -699,6 +699,7 @@ int main(int argc, char** argv) {
if (!controls_texture) {
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) */
signal(SIGTERM, signal_handler);
@ -957,11 +958,11 @@ int main(int argc, char** argv) {
SDL_RenderCopy(renderer, controls_texture, &ff_sprite, &ff_btn);
SDL_RenderCopy(renderer, controls_texture, &next_sprite, &next_btn);
/* Focus highlight — 3px blue border around focused element */
/* Focus highlight — 3px red border around focused element */
{
const SDL_Rect r = *focus_rects[focus_index];
const int t = 3;
SDL_SetRenderDrawColor(renderer, 0x00, 0x80, 0xFF, 0xFF);
SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
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 lft = {r.x - t, r.y, t, r.h};

View File

@ -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 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()