#!/bin/bash # RDtop Installer — RustDesk headless display setup (dummy driver) # Usage: bash -c "$(curl -fsSL https://git.softuniq.eu/NW/RDtop/raw/branch/main/install.sh)" set -euo pipefail echo "=== RDtop Installer ===" echo "Installing RustDesk headless display fallback..." # Config paths REPO_URL="https://git.softuniq.eu/NW/RDtop" X11_CONF="/etc/X11/xorg.conf.d/20-dummy-headless.conf" BIN_DST="$HOME/.local/bin/hdmi-fallback.sh" SYSTEMD_DST="$HOME/.config/systemd/user/hdmi-fallback.service" EDID_DST="/lib/firmware/edid/samsung.bin" LOG="$HOME/.local/logs/rdtop-install.log" mkdir -p "$(dirname "$LOG")" log() { echo "$(date '+%F %T') $1" | tee -a "$LOG"; } log "Start install" # --- Check prerequisites --- if [ "$EUID" -eq 0 ]; then log "ERROR: Do not run as root. Run as regular user with sudo access." exit 1 fi # --- Check sudo works --- if ! sudo -n true 2>/dev/null; then echo "This script requires sudo privileges. Please enter your password when prompted." fi # --- Install dummy driver --- if [ -f /usr/lib/xorg/modules/drivers/dummy_drv.so ]; then log "Dummy driver already present" else log "Installing xserver-xorg-video-dummy..." if command -v apt-get >/dev/null 2>&1; then sudo apt-get update -qq && sudo apt-get install -y -qq xserver-xorg-video-dummy || true else log "WARNING: apt-get not found, cannot install dummy driver" exit 1 fi fi # --- Remove old conflicting configs --- log "Removing old Intel X11 configs" sudo rm -f /etc/X11/xorg.conf.d/20-intel-virtual.conf sudo rm -f /etc/X11/xorg.conf.d/90-fallback.conf # --- Copy EDID firmware --- log "Saving EDID firmware" if [ -f /sys/class/drm/card0-HDMI-A-1/edid ]; then sudo mkdir -p /lib/firmware/edid sudo cp /sys/class/drm/card0-HDMI-A-1/edid "$EDID_DST" sudo chmod 644 "$EDID_DST" log "EDID saved to $EDID_DST" else log "WARNING: No EDID found at /sys/class/drm/card0-HDMI-A-1/edid" fi # --- Create dummy headless X11 config --- log "Creating dummy headless X11 config at $X11_CONF" cat <<'XORG' | sudo tee "$X11_CONF" >/dev/null Section "Device" Identifier "DummyHeadless" Driver "dummy" Option "ConstantDPI" "true" VideoRam 256000 EndSection Section "Monitor" Identifier "DummyMonitor" HorizSync 28-80 VertRefresh 48-75 EndSection Section "Screen" Identifier "DummyScreen" Device "DummyHeadless" Monitor "DummyMonitor" DefaultDepth 24 SubSection "Display" Depth 24 Modes "1920x1080" "1280x720" EndSubSection EndSection XORG sudo chmod 644 "$X11_CONF" log "X11 config installed" # --- Update GRUB for kernel EDID fallback --- log "Updating GRUB cmdline for EDID fallback" if [ -f /etc/default/grub ]; then sudo sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:1920x1080@60 drm.edid_firmware=HDMI-A-1:edid/samsung.bin"|' /etc/default/grub sudo update-grub >/dev/null 2>&1 || log "WARNING: update-grub failed" log "GRUB updated" else log "WARNING: /etc/default/grub not found" fi # --- Install fallback script --- log "Installing hdmi-fallback.sh to ~/.local/bin/" mkdir -p "$HOME/.local/bin" cat > "$BIN_DST" <<'SCRIPT' #!/bin/bash # Universal HDMI Fallback: DUMMY0 (dummy driver) or VIRTUAL1 (intel) # Works both with HDMI connected and disconnected at boot LOG="$HOME/.local/logs/drm-hotplug.log" mkdir -p "$HOME/.local/logs" export DISPLAY=:0 log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG" >/dev/null } # Find available primary output (DUMMY0 or VIRTUAL1) find_primary_output() { if xrandr 2>/dev/null | grep -q "^DUMMY0"; then echo "DUMMY0" elif xrandr 2>/dev/null | grep -q "^VIRTUAL1"; then echo "VIRTUAL1" else echo "" fi } # Find HDMI output name (HDMI-1, HDMI1, etc) find_hdmi_output() { xrandr 2>/dev/null | grep -E "^HDMI-[0-9] connected" | awk '{print $1}' | head -1 } # Set up mode for virtual output setup_mode() { local OUTPUT="$1" local MODE_NAME="1920x1080" if [ "$OUTPUT" = "VIRTUAL1" ]; then local MODELINE="1920x1080_60.00" if ! xrandr 2>/dev/null | grep -q "$MODELINE"; then xrandr --newmode "$MODELINE" 173.00 1920 2048 2248 2576 1080 1083 1088 1120 2>/dev/null || true fi xrandr --addmode VIRTUAL1 "$MODELINE" 2>/dev/null || true MODE_NAME="$MODELINE" fi echo "$MODE_NAME" } log "Start monitor (user=$USER, display=$DISPLAY)" CONNECTOR="" for p in /sys/class/drm/card1-HDMI-A-1 /sys/class/drm/card0-HDMI-A-1; do [ -e "$p/status" ] && { CONNECTOR="$p"; break; } done [ -z "$CONNECTOR" ] && CONNECTOR=$(find /sys/class/drm -maxdepth 1 -name "*HDMI-A-1" -type d | head -1) if [ -z "$CONNECTOR" ] || [ ! -e "$CONNECTOR/status" ]; then log "WARNING: No HDMI-A-1 connector found, running headless only" fi # Initial state LAST_STATE=$(cat "$CONNECTOR/status" 2>/dev/null || echo unknown) log "Initial HDMI: $LAST_STATE" # Detect outputs PRIMARY=$(find_primary_output) HDMI=$(find_hdmi_output) if [ -z "$PRIMARY" ]; then log "ERROR: No virtual output found (DUMMY0 or VIRTUAL1)" exit 1 fi log "Primary: $PRIMARY, HDMI: ${HDMI:-none}" MODE=$(setup_mode "$PRIMARY") # Initial setup if [ "$LAST_STATE" = "connected" ] && [ -n "$HDMI" ]; then log "HDMI connected, setting clone: $PRIMARY primary + $HDMI same-as" xrandr --output "$PRIMARY" --mode "$MODE" --primary 2>/dev/null || true xrandr --output "$HDMI" --auto --same-as "$PRIMARY" 2>/dev/null || true else log "HDMI disconnected or not found, keeping $PRIMARY primary" xrandr --output "$PRIMARY" --mode "$MODE" --primary 2>/dev/null || true [ -n "$HDMI" ] && xrandr --output "$HDMI" --off 2>/dev/null || true fi log "Setup complete. Monitors:" xrandr --listmonitors 2>/dev/null | while read -r line; do log " $line"; done # Main loop while true; do CURRENT=$(cat "$CONNECTOR/status" 2>/dev/null || echo unknown) if [ "$CURRENT" != "$LAST_STATE" ]; then log "HDMI changed: $LAST_STATE -> $CURRENT" # Re-detect PRIMARY=$(find_primary_output) HDMI=$(find_hdmi_output) MODE=$(setup_mode "$PRIMARY") if [ -z "$PRIMARY" ]; then log "ERROR: No virtual output detected after HDMI change" sleep 5 continue fi # Always make PRIMARY primary xrandr --output "$PRIMARY" --mode "$MODE" --primary 2>/dev/null || true if [ "$CURRENT" = "connected" ] && [ -n "$HDMI" ]; then log "HDMI connected, cloning $HDMI to $PRIMARY" xrandr --output "$HDMI" --auto --same-as "$PRIMARY" 2>/dev/null || true else log "HDMI disconnected, keeping $PRIMARY primary" [ -n "$HDMI" ] && xrandr --output "$HDMI" --off 2>/dev/null || true fi LAST_STATE="$CURRENT" fi sleep 2 done SCRIPT chmod +x "$BIN_DST" log "Script installed" # --- Install systemd unit --- log "Installing systemd user unit" mkdir -p "$HOME/.config/systemd/user" cat > "$SYSTEMD_DST" <<'UNIT' [Unit] Description=HDMI to Virtual Fallback Monitor After=graphical-session.target [Service] Type=simple ExecStart=%h/.local/bin/hdmi-fallback.sh Restart=always RestartSec=5 Environment="DISPLAY=:0" [Install] WantedBy=graphical-session.target UNIT systemctl --user daemon-reload systemctl --user enable --now hdmi-fallback.service 2>/dev/null || true log "Systemd unit enabled" # --- Summary --- echo "" echo "============================================" echo "RDtop installation complete!" echo "============================================" echo "" echo "Next steps:" echo "1. REBOOT now for changes to take effect:" echo " sudo reboot" echo "" echo "2. After reboot without HDMI, check:" echo " xrandr --listmonitors" echo "" echo "3. Service status:" echo " systemctl --user status hdmi-fallback.service" echo "" echo "4. View logs:" echo " cat ~/.local/logs/drm-hotplug.log" echo "" log "Install complete"