Handle SIGTERM/SIGINT for clean shutdown, add device wrapper script
sdlamp2 now catches SIGTERM and SIGINT via a sig_atomic_t flag checked in the main loop, ensuring position and volume are saved before exit. Previously, a kill signal would terminate instantly without saving. New tools/rg35xx-wrapper.sh replaces sdlamp2 as the dmenu_ln CMD on the RG35XX Plus. Skeleton includes placeholders for WiFi hotspot and power button monitoring (TBD after on-device investigation). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a5b04fcd08
commit
0f653d4395
@ -87,21 +87,22 @@ The `dmenu_ln` script already supports switching the startup binary via config f
|
|||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
|
|
||||||
1. **Copy the binary** to the device:
|
1. **Copy the binary and wrapper** to the device:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
scp build/sdlamp2 root@<device>:/mnt/vendor/bin/sdlamp2
|
scp build/sdlamp2 root@<device>:/mnt/vendor/bin/sdlamp2
|
||||||
|
scp tools/rg35xx-wrapper.sh root@<device>:/mnt/vendor/bin/rg35xx-wrapper.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
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:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
if [ -f "/mnt/vendor/sdlamp2.ini" ];then
|
if [ -f "/mnt/vendor/sdlamp2.ini" ];then
|
||||||
CMD="/mnt/vendor/bin/sdlamp2 /mnt/sdcard/Music"
|
CMD="/mnt/vendor/bin/rg35xx-wrapper.sh"
|
||||||
fi
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
The unquoted `$CMD` in `app_scheduling` undergoes word splitting, so the path argument is passed correctly.
|
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:**
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ Everything else in the boot chain continues to work:
|
|||||||
|
|
||||||
- **Charging mode** — handled in `loadapp.sh` before the restart loop
|
- **Charging mode** — handled in `loadapp.sh` before the restart loop
|
||||||
- **LED/backlight control** — `brightCtrl.bin` started by `launcher.sh`
|
- **LED/backlight control** — `brightCtrl.bin` started by `launcher.sh`
|
||||||
- **Clean shutdown** — system shutdown scripts unaffected
|
- **Clean shutdown** — sdlamp2 handles SIGTERM/SIGINT, saving position and volume before exit. The wrapper script can trigger `poweroff` after sdlamp2 exits
|
||||||
- **Restart on exit** — if sdlamp2 exits cleanly (status 0), the restart loop in `loadapp.sh` re-launches it immediately
|
- **Restart on exit** — if sdlamp2 exits cleanly (status 0), the restart loop in `loadapp.sh` re-launches it immediately
|
||||||
- **Crash recovery** — if sdlamp2 crashes (non-zero exit), `app_scheduling` sleeps 30s then the loop retries
|
- **Crash recovery** — if sdlamp2 crashes (non-zero exit), `app_scheduling` sleeps 30s then the loop retries
|
||||||
- **Easy revert** — removing `sdlamp2.ini` restores the stock menu on next boot
|
- **Easy revert** — removing `sdlamp2.ini` restores the stock menu on next boot
|
||||||
|
|||||||
@ -43,6 +43,10 @@ This document specifies the functional requirements for an SDL2 based media play
|
|||||||
|
|
||||||
## 6. Changelog
|
## 6. Changelog
|
||||||
|
|
||||||
|
### 2026-02-13 — Clean shutdown on SIGTERM/SIGINT
|
||||||
|
|
||||||
|
- **Signal handling**: sdlamp2 now catches SIGTERM and SIGINT via a `sig_atomic_t` flag checked in the main loop. On signal, the existing cleanup path runs (save position, save volume, close decoder, SDL_Quit) instead of being killed instantly. This ensures position is saved when the system shuts down or the process is terminated by a wrapper script.
|
||||||
|
|
||||||
### 2026-02-13 — Combine Play/Stop, add Previous Cassette button
|
### 2026-02-13 — Combine Play/Stop, add Previous Cassette button
|
||||||
|
|
||||||
- **Play/Stop combined**: The separate Stop and Play buttons are merged into a single toggle button that shows the play icon (▶) when paused and the stop icon (■) when playing.
|
- **Play/Stop combined**: The separate Stop and Play buttons are merged into a single toggle button that shows the play icon (▶) when paused and the stop icon (■) when playing.
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <libswresample/swresample.h>
|
#include <libswresample/swresample.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -38,6 +39,15 @@ typedef struct {
|
|||||||
int art_height;
|
int art_height;
|
||||||
} Decoder;
|
} Decoder;
|
||||||
|
|
||||||
|
/* --- Signal handling --- */
|
||||||
|
|
||||||
|
static volatile sig_atomic_t got_signal = 0;
|
||||||
|
|
||||||
|
static void signal_handler(int sig) {
|
||||||
|
(void)sig;
|
||||||
|
got_signal = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Globals --- */
|
/* --- Globals --- */
|
||||||
|
|
||||||
static SDL_Window* window = NULL;
|
static SDL_Window* window = NULL;
|
||||||
@ -656,6 +666,10 @@ int main(int argc, char** argv) {
|
|||||||
panic_and_abort("Could not create controls texture!", SDL_GetError());
|
panic_and_abort("Could not create controls texture!", SDL_GetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle SIGTERM/SIGINT for clean shutdown (save position before exit) */
|
||||||
|
signal(SIGTERM, signal_handler);
|
||||||
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
/* Scan directory and open first file */
|
/* Scan directory and open first file */
|
||||||
scan_audio_files(audio_dir);
|
scan_audio_files(audio_dir);
|
||||||
volume = load_volume();
|
volume = load_volume();
|
||||||
@ -669,6 +683,8 @@ int main(int argc, char** argv) {
|
|||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
|
if (got_signal) running = SDL_FALSE;
|
||||||
|
|
||||||
/* --- Event handling --- */
|
/* --- Event handling --- */
|
||||||
while (SDL_PollEvent(&e)) {
|
while (SDL_PollEvent(&e)) {
|
||||||
/* Debug: log all input-related events */
|
/* Debug: log all input-related events */
|
||||||
|
|||||||
63
tools/rg35xx-wrapper.sh
Executable file
63
tools/rg35xx-wrapper.sh
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# RG35XX Plus wrapper for sdlamp2
|
||||||
|
#
|
||||||
|
# Launched by dmenu_ln instead of sdlamp2 directly. Handles device-specific
|
||||||
|
# concerns that don't belong in the player binary:
|
||||||
|
# 1. Start WiFi hotspot (AP mode for SSH access)
|
||||||
|
# 2. Launch sdlamp2 as the foreground process
|
||||||
|
# 3. Monitor power button and trigger clean shutdown
|
||||||
|
#
|
||||||
|
# Install: copy to /mnt/vendor/bin/rg35xx-wrapper.sh
|
||||||
|
# Config: set CMD in dmenu_ln to point here instead of sdlamp2 directly
|
||||||
|
|
||||||
|
SDLAMP2="/mnt/vendor/bin/sdlamp2"
|
||||||
|
AUDIO_DIR="/mnt/sdcard/Music"
|
||||||
|
|
||||||
|
# --- WiFi hotspot ---
|
||||||
|
# TODO: Investigate how the stock menu starts AP mode.
|
||||||
|
# Likely candidates: hostapd, nmcli, or a vendor script in /mnt/vendor/ctrl/.
|
||||||
|
# Run these on the device to find out:
|
||||||
|
# grep -r 'hostapd\|hotspot\|ap_mode\|wifi' /mnt/vendor/ctrl/
|
||||||
|
# systemctl list-units | grep -i net
|
||||||
|
# nmcli general status && nmcli connection show
|
||||||
|
#
|
||||||
|
# Placeholder — uncomment/replace once the mechanism is known:
|
||||||
|
# nmcli connection up hotspot 2>/dev/null || true
|
||||||
|
|
||||||
|
# --- Power button monitor ---
|
||||||
|
# TODO: Investigate power button input device.
|
||||||
|
# Run on device:
|
||||||
|
# cat /proc/bus/input/devices (find the power button event device)
|
||||||
|
# evtest /dev/input/eventN (confirm KEY_POWER event code)
|
||||||
|
# cat /etc/systemd/logind.conf (check HandlePowerKey setting)
|
||||||
|
#
|
||||||
|
# Strategy: read key events from the power button input device in background.
|
||||||
|
# When KEY_POWER (code 116) is detected, send SIGTERM to sdlamp2 and poweroff.
|
||||||
|
#
|
||||||
|
# POWER_EVENT_DEV="/dev/input/eventN" # TBD: set after investigation
|
||||||
|
#
|
||||||
|
# monitor_power_button() {
|
||||||
|
# # evtest writes one line per event; filter for KEY_POWER press (value 1)
|
||||||
|
# evtest "$POWER_EVENT_DEV" 2>/dev/null | while read -r line; do
|
||||||
|
# case "$line" in
|
||||||
|
# *"code 116"*"value 1"*)
|
||||||
|
# kill -TERM "$SDLAMP2_PID" 2>/dev/null
|
||||||
|
# sleep 1
|
||||||
|
# poweroff
|
||||||
|
# ;;
|
||||||
|
# esac
|
||||||
|
# done
|
||||||
|
# }
|
||||||
|
# monitor_power_button &
|
||||||
|
# MONITOR_PID=$!
|
||||||
|
|
||||||
|
# --- Launch sdlamp2 ---
|
||||||
|
"$SDLAMP2" "$AUDIO_DIR"
|
||||||
|
SDLAMP2_EXIT=$?
|
||||||
|
|
||||||
|
# --- Cleanup ---
|
||||||
|
# Kill the power button monitor if it's still running
|
||||||
|
# kill "$MONITOR_PID" 2>/dev/null
|
||||||
|
|
||||||
|
exit "$SDLAMP2_EXIT"
|
||||||
Loading…
x
Reference in New Issue
Block a user