KDE Plasma + Brave on Debian

  This is the “how do I make Brave do what I want” note --- especially when Brave profile UI is broken and KDE is strict about .desktop launchers. 1) Know what you’re running. Check where Brave comes from. which brave-browser If it returns /usr/bin/brave-browser , you’re on the APT-installed build (good, predictable). Also note that on Debian you often have both commands available. brave-browser is commonly a wrapper. brave-browser-stable is commonly the actual binary. 2) Where Brave stores its data. Default Brave user-data root (APT install). ~/.config/BraveSoftware/Brave-Browser/ If you only see Default/ , then you effectively have a single Brave “profile” in that directory. 3) Multiple isolated Brave sessions without Brave profiles. This is the clean workaround: run separate user-data directories . Create a new isolated environment. mkdir -p ~/.config/BraveSoftware/Brave-RDT Launch Brave using that directory. brave-browser-stable --user-data-dir= ...

Auto-Rotation on KDE Plasma ( Wayland ) – Lenovo ThinkPad X1 Tablet Gen 3

Getting reliable auto-rotation on a ThinkPad X1 Tablet Gen 3 running KDE Plasma on Wayland, using a systemd user service and raw accelerometer readings from /sys/bus/iio/ instead of relying on DBus orientation.


Context

  • OS: Debian 13 (Trixie).

  • Desktop: KDE Plasma on Wayland.

  • Device: Lenovo ThinkPad X1 Tablet Gen 3.

  • Issue: iio-sensor-proxy detects the accelerometer, but orientation is not reliably consumed/published for Plasma auto-rotation.

  • Approach: Read raw accelerometer values from IIO sysfs and apply rotations via kscreen-doctor.


Why this setup works

  • iio:deviceN can change across reboot/resume, so we autodetect the accelerometer directory each run.

  • kscreen-doctor is the correct Wayland-friendly tool for KScreen outputs (instead of xrandr).

  • Running as a systemd user service is more robust than KDE Autostart for a daemon-style loop: restart-on-failure, journald logs, consistent lifecycle.

  • Plasma Wayland environment variables must be synced into systemd --user, otherwise GUI tools may misbehave when started by systemd.


Requirements

  • kscreen-doctor must be installed.

sudo apt update sudo apt install kscreen
  • Accelerometer must expose raw axes under IIO sysfs.

ls /sys/bus/iio/devices/iio:device*/in_accel_*_raw
  • Your internal panel output should be eDP-1 (typical on this hardware).

kscreen-doctor -o

Directory and file layout

  • ~/.config/plasma-workspace/env/10-systemd-user-env.sh.

  • ~/.local/bin/auto-rotate-kde.

  • ~/.config/systemd/user/auto-rotate.service.

  • Optional lock file: ~/.config/autorotate.lock.


1) Sync Plasma session env into systemd user manager

This ensures systemd --user services see WAYLAND_DISPLAY, XDG_RUNTIME_DIR, DBUS_SESSION_BUS_ADDRESS, etc.

mkdir -p ~/.config/plasma-workspace/env cat > ~/.config/plasma-workspace/env/10-systemd-user-env.sh <<'EOF' #!/usr/bin/env bash dbus-update-activation-environment --systemd --all EOF chmod 755 ~/.config/plasma-workspace/env/10-systemd-user-env.sh

Reboot or log out/in once.

Verification.

systemctl --user show-environment | grep -E 'WAYLAND_DISPLAY|XDG_RUNTIME_DIR|DBUS_SESSION_BUS_ADDRESS'

2) The rotation script

Notes.

  • OUTPUT_ID is hard-set to output.eDP-1 (stable for internal panel on this model).

  • IIO_DIR is autodetected each run because iio:deviceN can change.

  • Thresholds are tuned for large raw-value scale (often > 1,000,000).

  • Lock file pauses rotation instantly.

  • Touch/pen mapping is handled by Wayland automatically, so no xinput remapping is needed.

Create the script.

mkdir -p ~/.local/bin cat > ~/.local/bin/auto-rotate-kde <<'EOF' #!/usr/bin/env bash # Auto-rotate for KDE Plasma (Wayland). # ThinkPad X1 Tablet Gen 3. # Pause rotation with: touch ~/.config/autorotate.lock. # Debug logs: set AUTOROTATE_DEBUG=1 in systemd user environment. INTERVAL=1 STABILITY_COUNT=3 # Tuned for large raw-value accelerometer scale (often > 1,000,000). THRESHOLD=120000 Z_DOMINANCE=800000 # Stable internal panel output on this model. OUTPUT_ID="output.eDP-1" set -euo pipefail ulimit -c 0 DEBUG="${AUTOROTATE_DEBUG:-0}" log(){ echo "auto-rotate: $*" >&2; } dbg(){ [[ "$DEBUG" == "1" ]] && log "$*"; } command -v kscreen-doctor >/dev/null || { log "kscreen-doctor missing (sudo apt install kscreen)."; exit 1; } detect_iio_dir() { local d name local candidates=() for d in /sys/bus/iio/devices/iio:device*; do [[ -r "$d/in_accel_x_raw" && -r "$d/in_accel_y_raw" && -r "$d/in_accel_z_raw" ]] || continue candidates+=("$d") done (( ${#candidates[@]} == 0 )) && return 1 if (( ${#candidates[@]} == 1 )); then printf '%s\n' "${candidates[0]}" return 0 fi for d in "${candidates[@]}"; do name="$(cat "$d/name" 2>/dev/null || true)" if [[ "$name" =~ (accel|Accelerometer|bma|lis|kxc|icm|mpu|accel_3d) ]]; then printf '%s\n' "$d" return 0 fi done printf '%s\n' "${candidates[0]}" } read_axis(){ cat "$IIO_DIR/in_accel_${1}_raw" 2>/dev/null || echo ""; } apply_rotation(){ local rot="$1" if kscreen-doctor "${OUTPUT_ID}.rotation.${rot}" >/dev/null 2>&1; then log "Applied rotation -- ${rot} on ${OUTPUT_ID}." else log "FAILED rotation -- ${rot} on ${OUTPUT_ID}." fi } decide_orientation(){ local x="$1" y="$2" z="$3" local ax="${x#-}" ay="${y#-}" az="${z#-}" # Hold when flat on a table (Z dominant). (( az > Z_DOMINANCE && az > ax && az > ay )) && { echo hold; return; } # Hold when tilt is too small (noise / micro-movements). (( ax < THRESHOLD && ay < THRESHOLD )) && { echo hold; return; } # Decide based on dominant horizontal axis. if (( ax > ay )); then (( x > 0 )) && echo left || echo right else (( y < 0 )) && echo normal || echo inverted fi } IIO_DIR="" last_vote="" vote_count=0 current_applied="" log "Using output -- ${OUTPUT_ID}." while :; do [[ -f "$HOME/.config/autorotate.lock" ]] && { sleep "$INTERVAL"; continue; } if [[ -z "$IIO_DIR" ]]; then IIO_DIR="$(detect_iio_dir || true)" [[ -n "$IIO_DIR" ]] && log "Detected accelerometer -- ${IIO_DIR} ($(cat "$IIO_DIR/name" 2>/dev/null || echo unknown))." fi [[ -z "$IIO_DIR" ]] && { dbg "Waiting for IIO_DIR."; sleep "$INTERVAL"; continue; } X="$(read_axis x)"; Y="$(read_axis y)"; Z="$(read_axis z)" if [[ -z "$X" || -z "$Y" || -z "$Z" ]]; then dbg "Missing axis read, forcing IIO re-detect." IIO_DIR="" sleep "$INTERVAL" continue fi vote="$(decide_orientation "$X" "$Y" "$Z")" dbg "X=$X Y=$Y Z=$Z vote=$vote." [[ "$vote" == hold ]] && { sleep "$INTERVAL"; continue; } if [[ "$vote" == "$last_vote" ]]; then ((vote_count++)) else last_vote="$vote" vote_count=1 fi if (( vote_count >= STABILITY_COUNT )) && [[ "$vote" != "$current_applied" ]]; then apply_rotation "$vote" current_applied="$vote" fi sleep "$INTERVAL" done EOF chmod 700 ~/.local/bin/auto-rotate-kde

3) systemd user service unit

Create the unit.

mkdir -p ~/.config/systemd/user cat > ~/.config/systemd/user/auto-rotate.service <<'EOF' [Unit] Description=Auto screen rotation (KDE Wayland) After=plasma-workspace.target graphical-session.target Wants=graphical-session.target PartOf=graphical-session.target [Service] ExecStart=%h/.local/bin/auto-rotate-kde Restart=always RestartSec=2 Environment=XDG_RUNTIME_DIR=%t Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=%t/bus Environment=QT_QPA_PLATFORM=wayland [Install] WantedBy=default.target EOF systemctl --user daemon-reload systemctl --user enable --now auto-rotate.service

Rotation lock

Pause rotation.

touch ~/.config/autorotate.lock

Resume rotation.

rm ~/.config/autorotate.lock

Verification

Manual compositor-level test.

kscreen-doctor output.eDP-1.rotation.left sleep 1 kscreen-doctor output.eDP-1.rotation.normal

Service status.

systemctl --user status auto-rotate.service --no-pager

Logs.

journalctl --user -u auto-rotate.service -b -n 80 --no-pager

Debug mode

Enable debug for the service (properly via systemd user manager env).

systemctl --user set-environment AUTOROTATE_DEBUG=1 systemctl --user restart auto-rotate.service journalctl --user -u auto-rotate.service -f

Disable debug.

systemctl --user unset-environment AUTOROTATE_DEBUG systemctl --user restart auto-rotate.service

Sensor scale notes and tuning

  • If your values exceed 1,000,000, that is fine — thresholds must match that scale.

  • Recommended tuning starting point (already used above).

    • THRESHOLD=120000.

    • Z_DOMINANCE=800000.

  • If rotation is too “nervous”, increase STABILITY_COUNT to 4.

  • If rotation is too slow, reduce STABILITY_COUNT to 2.

Raw value watch (device index may change, pick the one that exists).

watch -n0.2 'paste -d" " \ /sys/bus/iio/devices/iio:device*/in_accel_x_raw \ /sys/bus/iio/devices/iio:device*/in_accel_y_raw \ /sys/bus/iio/devices/iio:device*/in_accel_z_raw | head -n 3'

Clean-up notes

  • The directory ~/.config/plasma-workspace/env/ is still needed because Plasma uses it to run the environment sync script.

  • A ~/.config/systemd/user/auto-rotate.service.d/ drop-in directory is not required in this final setup.

Remove the drop-in directory if it exists.

rm -rf ~/.config/systemd/user/auto-rotate.service.d systemctl --user daemon-reload systemctl --user restart auto-rotate.service

Full rollback (restore previous state)

Disable and remove everything.

systemctl --user disable --now auto-rotate.service rm -f ~/.config/systemd/user/auto-rotate.service rm -f ~/.local/bin/auto-rotate-kde rm -f ~/.config/plasma-workspace/env/10-systemd-user-env.sh rm -f ~/.config/autorotate.lock systemctl --user daemon-reload

Remove now-empty directories if desired.

rmdir ~/.config/plasma-workspace/env 2>/dev/null || true rmdir ~/.config/plasma-workspace 2>/dev/null || true

Final result

  • Auto-rotation works reliably on KDE Plasma Wayland with a supervised systemd --user service.

  • Accelerometer device reallocation is handled automatically.

  • Session env is propagated cleanly via Plasma’s env hook.

  • Manual lock is available for instant override.

Comments