Refactor first-boot rotation setup to use kernel command line for persistent display rotation. Update documentation to clarify the new method of setting screen orientation (90° clockwise) via cmdline.txt, eliminating the need for one-shot scripts. Enhance logging and user guidance for rotation and wallpaper configuration during first boot.

This commit is contained in:
nearxos
2026-02-20 20:05:23 +02:00
parent 90296498f5
commit 2777811b32
6 changed files with 88 additions and 22 deletions

View File

@@ -0,0 +1,5 @@
[Desktop Entry]
Type=Application
Name=Set rotation at login
Exec=/home/pi/set-rotation-at-login.sh
X-GNOME-Autostart-enabled=true

View File

@@ -119,14 +119,13 @@ These changes take effect after a reboot.
---
## Screen rotation (portrait → landscape, “Left”)
## Screen rotation (portrait → landscape, 90° clockwise)
The reTerminal DM default is portrait. Rotation is set using **wlr-randr** (labwc/Wayland), so nothing is written to `/boot/firmware/config.txt`.
The reTerminal DM default is portrait. Rotation is set **persistently** via the kernel command line (KMS driver), so it survives reboots and does not depend on the desktop session.
- **One-shot autostart** — A small script runs once when user `pi` first logs into the graphical session:
- **`/home/pi/set-rotation-once.sh`** — Waits 5 seconds, detects the output name with `wlr-randr`, then runs `wlr-randr --output <name> --transform 270` (Left / 90° counter-clockwise). Falls back to `DSI-1` if detection fails.
- **`/home/pi/.config/autostart/set-rotation-once.desktop`** — Autostart entry that runs the script once at first login. Both the script and this desktop file **delete themselves** after a successful run, so rotation is applied only on first boot and never again.
- **Effect** — Same as choosing “Left” in display settings. Rotation applies after the first login; no reboot needed for rotation (only for the Seeed drivers).
- **Kernel cmdline** — First-boot appends **`video=DSI-1:rotate=90`** to **`/boot/firmware/cmdline.txt`** (or `/boot/cmdline.txt`). The file must remain **one single line** with no line breaks.
- **90° clockwise** — The value `90` gives 90° clockwise rotation. Other valid values: `180`, `270` (90° counter-clockwise).
- **Effect** — Rotation is applied by the kernel at boot; no one-shot script or wlr-randr needed.
---
@@ -144,7 +143,7 @@ If **`rpi-eeprom-config`** and **`rpi-eeprom-update`** are present (Pi 4/CM4), t
## Reboot
Runs **`reboot`** so the kernel and display stack load the new Seeed drivers. After reboot, the screen and touch work; on **pi**s first login the one-shot sets rotation to “Left” (landscape), and the Chromium kiosk and Maliit start via autostart.
Runs **`reboot`** so the kernel and display stack load the new Seeed drivers and the cmdline rotation. After reboot, the screen and touch work with rotation applied (video=DSI-1:rotate=90), and the Chromium kiosk and Maliit start via autostart on **pi**'s login.
---
@@ -153,5 +152,5 @@ Runs **`reboot`** so the kernel and display stack load the new Seeed drivers. Af
- **File server** — Edit `FILE_SERVER` if your assets are served from another host/port. Host all files listed in **`files-from-guard/README.md`** and **`config-files/README.md`** (kiosk, splash, Plymouth, LightDM, Maliit, one-shots and their .desktop files).
- **Kiosk URL** — The URL Chromium opens is defined in `start-chromium.sh` on your file server (e.g. `--app=http://127.0.0.1:8080`); change it there.
- **User** — If you use a user other than `pi`, replace `pi` in this script and in the files on the file server (paths and ownership).
- **Screen rotation** — The one-shot runs `wlr-randr --output <name> --transform 270` (Left). To use another orientation, change `270` to `90` (right), `180` (inverted), or `normal`.
- **Screen rotation** — Set in `/boot/firmware/cmdline.txt`: `video=DSI-1:rotate=90` (90° clockwise). Use `180` or `270` for other orientations. Keep the file one single line.
- **Desktop wallpaper** — First-boot writes `wallpaper=` and `wallpaper_mode=crop` into pcmanfms `desktop-items-0.conf`. To change later: `pcmanfm --set-wallpaper /path/to/image --wallpaper-mode=crop`. **Other pcmanfm options:** `pcmanfm --desktop` (start desktop manager), `pcmanfm --desktop-pref` (open desktop preferences GUI), `pcmanfm --desktop-off` (stop desktop manager), `pcmanfm -w FILE` (short form of --set-wallpaper). Wallpaper modes: `crop`, `stretch`, `fit`, `center`, `tile`, `screen`, `color`.

View File

@@ -182,12 +182,19 @@ fi
log "Running update-initramfs to apply Plymouth theme ..."
update-initramfs -u -k all 2>/dev/null || true
# --- 6b2. Kernel cmdline: swiotlb for vc4-drm (avoids "swiotlb buffer is full" / blank DSI on CM4) ---
# --- 6b2. Kernel cmdline: swiotlb + DSI rotation (KMS, persistent across reboots) ---
CMDLINE_PATH="/boot/firmware/cmdline.txt"
[[ -f "$CMDLINE_PATH" ]] || CMDLINE_PATH="/boot/cmdline.txt"
if [[ -f "$CMDLINE_PATH" ]] && ! grep -q 'swiotlb=' "$CMDLINE_PATH"; then
if [[ -f "$CMDLINE_PATH" ]]; then
if ! grep -q 'swiotlb=' "$CMDLINE_PATH"; then
sed -i 's/rootwait/rootwait swiotlb=65536/' "$CMDLINE_PATH"
log "Added swiotlb=65536 to kernel cmdline (vc4-drm / DSI)"
fi
# Persistent rotation for DSI-1 (KMS): append at end of single line. 90 = 90° clockwise.
if ! grep -q 'video=DSI-1:rotate=' "$CMDLINE_PATH"; then
sed -i 's/$/ video=DSI-1:rotate=90/' "$CMDLINE_PATH"
log "Added video=DSI-1:rotate=90 to kernel cmdline (DSI rotation)"
fi
fi
# --- 6c. CM4: enable rpi-eeprom-update so boot order can be set ---
@@ -274,10 +281,10 @@ SVCEOF
systemctl enable set-cm4-boot-order-once.service 2>/dev/null && log "Enabled set-cm4-boot-order-once.service to set boot order after next boot"
fi
# --- 7. One-shots (rotation at first login; wallpaper already set in pcmanfm config above) ---
log "--- One-shot scripts (run at pi first login) ---"
install_oneshot set-rotation-once || true
log "One-shots will append to $LOGFILE when they run at first login"
# --- 7. One-shots (wallpaper already set in pcmanfm config above; rotation is via cmdline.txt) ---
log "--- One-shot scripts (if any) ---"
# Rotation is set persistently in cmdline.txt (video=DSI-1:rotate=90), not via one-shot script.
log "Rotation is set via kernel cmdline (video=DSI-1:rotate=90)"
# --- 8. Allow pi to append to first-boot.log (for one-shot scripts) ---
chmod 666 "$LOGFILE"

View File

@@ -26,11 +26,18 @@ if [[ -f /etc/plymouth/plymouthd.conf ]]; then
fi
update-initramfs -u -k all 2>/dev/null || true
echo "=== Rotation and wallpaper (rpd-labwc / labwc Wayland) ==="
echo "To set rotation and wallpaper now (in a labwc session), run as $PI_USER:"
echo " wlr-randr --output \$(wlr-randr | awk '/^[A-Za-z0-9_-]+ /{print \$1; exit}') --transform 270"
echo "=== Rotation (persistent via kernel cmdline) ==="
CMDLINE_PATH="/boot/firmware/cmdline.txt"
[[ -f "$CMDLINE_PATH" ]] || CMDLINE_PATH="/boot/cmdline.txt"
if [[ -f "$CMDLINE_PATH" ]] && ! grep -q 'video=DSI-1:rotate=' "$CMDLINE_PATH"; then
sed -i 's/$/ video=DSI-1:rotate=90/' "$CMDLINE_PATH"
echo "Added video=DSI-1:rotate=90 to $CMDLINE_PATH (90° clockwise). Reboot to apply."
else
echo "Rotation already set in cmdline or file missing. Current: $(grep -o 'video=DSI-1:rotate=[^ ]*' "$CMDLINE_PATH" 2>/dev/null || true)"
fi
echo ""
echo "=== Wallpaper (in labwc session, as $PI_USER) ==="
echo " pcmanfm --set-wallpaper /usr/share/rpd-wallpaper/splash.png --wallpaper-mode=crop"
echo ""
echo "Or ensure one-shots run at next login (they are in autostart if still present)."
echo "=== Reboot to apply splash and initramfs ==="
echo "=== Reboot to apply splash, initramfs and rotation ==="
echo " sudo reboot"

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# Set reTerminal DM (labwc/Wayland) rotation to Left at every login.
# Runs from autostart when user pi logs in; does not remove itself.
# Use this when wlr-randr transform does not persist across reboots.
sleep 5
OUTPUT=""
if command -v wlr-randr &>/dev/null; then
OUTPUT=$(wlr-randr 2>/dev/null | awk '/^[A-Za-z0-9_-]+ /{print $1; exit}')
fi
[[ -z "$OUTPUT" ]] && OUTPUT="DSI-1"
if [[ -n "$OUTPUT" ]] && command -v wlr-randr &>/dev/null; then
wlr-randr --output "$OUTPUT" --transform 270
fi

View File

@@ -20,7 +20,13 @@ from flask import Flask, render_template, jsonify, request, send_file, redirect,
from werkzeug.security import generate_password_hash, check_password_hash
try:
from itsdangerous import BadSignature
except ImportError:
BadSignature = Exception # noqa: disable invalid-name
app = Flask(__name__)
# Set CM4_DASHBOARD_SECRET_KEY in env (e.g. via /opt/cm4-provisioning/dashboard.env) so sessions persist across restarts
app.secret_key = os.environ.get("CM4_DASHBOARD_SECRET_KEY", os.urandom(24).hex())
# --- Paths ---
@@ -90,7 +96,16 @@ def admin_log(action, details=None):
def require_admin(f):
@wraps(f)
def wrapped(*args, **kwargs):
if not session.get("admin_logged_in"):
try:
logged_in = session.get("admin_logged_in")
except (BadSignature, ValueError, OSError):
# Invalid/rotated secret key or corrupted session cookie → treat as not logged in
try:
session.clear()
except Exception:
pass
logged_in = False
if not logged_in:
if request.is_json or request.path.startswith("/api/"):
return jsonify({"ok": False, "error": "Login required"}), 401
return redirect(url_for("login", next=request.url))
@@ -154,6 +169,26 @@ def no_cache(response):
response.headers["Pragma"] = "no-cache"
return response
@app.errorhandler(500)
def handle_500(err):
"""Log 500 and return a simple response so the user is not left with a blank error."""
import traceback
try:
traceback.print_exc()
except Exception:
pass
if request.path.startswith("/api/"):
return jsonify({"ok": False, "error": "Internal server error"}), 500
return (
"<!DOCTYPE html><html><head><meta charset='utf-8'><title>Error</title></head><body>"
"<h1>Something went wrong</h1><p>Try <a href='/login'>logging in again</a> or <a href='/'>going home</a>.</p>"
"</body></html>",
500,
{"Content-Type": "text/html; charset=utf-8"},
)
# Default cloud-init user-data for Raspberry Pi OS (NoCloud on boot partition)
DEFAULT_USER_DATA = """#cloud-config
package_update: true