- Rust 98.3%
- Shell 1.7%
| packaging | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| GUIDE.md | ||
| LICENSE | ||
| README.md | ||
myslideshow
A small, fullscreen image slideshow for a Raspberry Pi driving a TV, for example in a waiting room. Point it at a directory of pictures and leave it running. Written in Rust on SDL2. Target: Debian / Raspberry Pi OS Bookworm.
Features
- Smooth transitions: fade, fade through black, slide, zoom, or none.
- Slow pan and zoom on every image (on by default;
--pan 0 --zoom 0turns it off). Images never expose their edges while moving. - With pan/zoom off, CPU use while an image is on screen is near zero. With
it on, the screen is redrawn every frame; use
--fps 30on slower boards. Images are decoded and downscaled in a background thread, with one image prefetched, so transitions stay smooth. - Works with X11, Wayland, or straight on the display via KMSDRM (Pi OS Lite, no desktop needed). SDL picks the backend automatically.
- The directory is rescanned at the end of each cycle, so you can add or
remove pictures while it runs (Samba share,
scp, USB copy...). If the directory is empty, it is rescanned every 5 seconds. - Unreadable or corrupt files are skipped. EXIF orientation is honoured. Hidden files are ignored.
- Formats: jpg, jpeg, png, webp, bmp, gif (first frame only), tif, tiff.
Usage
myslideshow [OPTIONS] <DIR>
| Option | Default | Description |
|---|---|---|
<DIR> |
(required) | Directory containing the images |
-d, --duration <DURATION> |
10 |
Seconds each image stays on screen (excluding the transition) |
-t, --transition <TRANSITION> |
fade |
Transition effect: fade, fade-black, slide, zoom, none, random |
-T, --transition-time <TRANSITION_TIME> |
1.5 |
Transition length in seconds |
-f, --fit <FIT> |
contain |
How images fit the screen: contain (whole image, black bars if needed) or cover (fill the screen, crop the edges) |
-p, --pan <PERCENT> |
8 |
Slow pan: the image is enlarged by this much and drifts across the extra area, from a random start towards the farthest corner (the longest straight path), at a speed matched to the display time. 0 disables |
-z, --zoom <PERCENT> |
6 |
Slow zoom: further scaling over the display time, in or out at random, combined with the pan. 0 disables |
--fps <FPS> |
60 |
Frame-rate cap while animating; lower it (e.g. 30) to save CPU on slow boards. Rounded to the refresh rate divided by a whole number (60 Hz: 60, 30, 20...) so motion stays even |
-s, --shuffle |
off | Random order, reshuffled every cycle |
-r, --recursive |
off | Include images from subdirectories |
--windowed |
off | Run in a window instead of fullscreen (for testing) |
-h, --help / -V, --version |
Help / version |
Transitions: fade is a smooth crossfade, fade-black fades out to black then
in, slide pushes the new image in from the right, zoom is a crossfade with
a gentle zoom, none is an instant cut, and random picks one of the effects
each time.
Example:
myslideshow --shuffle --duration 15 --transition random ~/Pictures
Keyboard
| Key | Action |
|---|---|
| Right / Space / PageDown | Next image |
| Left / PageUp | Previous image |
| P | Pause / resume |
| Esc / Q | Quit |
Building
On the Pi
sudo apt install build-essential libsdl2-dev curl
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo build --release # binary: target/release/myslideshow
Cross-compiling from x86
Use cross (needs Docker or Podman). The
default cross image has no SDL2 for the target, so add a Cross.toml:
[target.aarch64-unknown-linux-gnu]
pre-build = [
"dpkg --add-architecture $CROSS_DEB_ARCH",
"apt-get update && apt-get install --assume-yes libsdl2-dev:$CROSS_DEB_ARCH",
]
cargo install cross
cross build --release --target aarch64-unknown-linux-gnu
For a 32-bit Raspberry Pi OS use armv7-unknown-linux-gnueabihf instead (and
the same pre-build block under that target). Check which one you have with
uname -m on the Pi: aarch64 means 64-bit, armv7l means 32-bit.
Installing the .deb
Build the package with cargo-deb:
cargo install cargo-deb
cargo deb # native build
cargo deb --target aarch64-unknown-linux-gnu # after a cross build (use --no-build to reuse it)
Copy target/debian/myslideshow_*.deb (or target/<target>/debian/) to the Pi
and install it:
sudo apt install ./myslideshow_0.1.0-1_arm64.deb
The package installs:
/usr/bin/myslideshow/lib/systemd/system/myslideshow.service(not enabled automatically)/etc/default/myslideshow(config file, kept across upgrades)- this README in
/usr/share/doc/myslideshow/
It also creates a slideshow system user (member of video, render,
input) and the directory /srv/slideshow (group-writable, owned by
slideshow).
Running as a kiosk
(a) Pi OS Lite, no desktop (systemd + KMSDRM)
-
Install the .deb (above). Put images in
/srv/slideshow, for instance overscp, or a Samba share pointing at that directory. -
Edit
/etc/default/myslideshowto setSLIDESHOW_DIRandSLIDESHOW_ARGS(for exampleSLIDESHOW_ARGS="--shuffle --duration 15"). -
Enable and start:
sudo systemctl enable --now myslideshow journalctl -u myslideshow -f # logs sudo systemctl restart myslideshow # after changing the config
The unit runs /usr/bin/myslideshow $SLIDESHOW_ARGS ${SLIDESHOW_DIR} as the
slideshow user with SDL_VIDEODRIVER=kmsdrm, restarts it on failure, and
adds the video, render and input groups. Do not run it on a system where
a desktop already owns the screen; use (b) there.
Without the .deb: copy packaging/myslideshow.service to
/etc/systemd/system/, packaging/default to /etc/default/myslideshow,
create the user with
sudo adduser --system --group --home /srv/slideshow slideshow (or change
User= in the unit to an existing user such as pi), then run
sudo systemctl daemon-reload and enable it as above.
(b) Pi OS with desktop (autostart)
On Bookworm the default compositor is labwc (Wayland). Add to
~/.config/labwc/autostart:
/usr/bin/myslideshow --shuffle /home/pi/Pictures &
On X11 sessions (or if you switched to X11 with raspi-config), create
~/.config/autostart/myslideshow.desktop:
[Desktop Entry]
Type=Application
Name=myslideshow
Exec=/usr/bin/myslideshow --shuffle /home/pi/Pictures
For a real kiosk also enable desktop auto-login (raspi-config > System
Options > Boot / Auto Login > Desktop Autologin), and do not enable the systemd
service from (a) on the same machine.
Tips (optional)
Keep the screen from blanking. On the desktop: sudo raspi-config >
Display Options > Screen Blanking > No. On Lite, add consoleblank=0 to the
single line in /boot/firmware/cmdline.txt and reboot. The slideshow itself
also asks SDL not to start a screensaver.
Turn the TV off at night. Pick whichever your setup supports, and run it
from cron (crontab -e), for example off at 19:00 and on at 07:30, Mon-Fri:
0 19 * * 1-5 vcgencmd display_power 0
30 7 * * 1-5 vcgencmd display_power 1
vcgencmd display_power 0|1works on Lite with KMSDRM on some Pi models and setups; it may have no effect with the KMS driver, in which case use CEC.- Under labwc on the desktop use
wlr-randr --output HDMI-A-1 --off/--on(list outputs withwlr-randr; cron needsWAYLAND_DISPLAYandXDG_RUNTIME_DIRset). - HDMI-CEC (TV is switched off or on properly, and works on Lite): install
cec-utilsand runecho 'standby 0' | cec-client -s -d 1to turn the TV off, andecho 'on 0' | cec-client -s -d 1to turn it on. The TV must have CEC enabled.
Troubleshooting
- Black screen, or "Could not initialize EGL" / "kmsdrm not available" under
systemd. Check the service user is in
video,renderandinput(id slideshow; the unit adds them, a hand-run user needs them too). Checkjournalctl -u myslideshowfor the real error. Try forcing a driver withSDL_VIDEODRIVER=kmsdrm, orx11/waylandon a desktop. Make sure no other program (a desktop, another instance) holds the display. - Keyboard does not work under KMSDRM. Keys are read from
/dev/input, so the user must be in theinputgroup. Log out and in again (or restart the service) after changing groups. - Out of memory or very slow start with huge photos. Large images (for example 48 MP phone photos) are fully decoded in RAM before being scaled down, which can use hundreds of MB. On 1 GB Pis, resize such photos beforehand (e.g. to 1920x1080 or 3840x2160) or avoid the biggest ones.
- A picture does not show up. It may be unreadable, hidden (name starts
with
.), in a subdirectory (use--recursive), or an unsupported format. New files are picked up at the end of the current cycle. Skipped files are logged to stderr. - Testing on a PC. Use
--windowed.
License
MIT, see LICENSE.