Linux_desktopicons

2026-10-29

Backup and restore your desktop icon layout on Cinnamon/Nemo and XFCE. Cinnamon and XFCE backups are kept completely separate, and no attempt is made to translate icon positions between the two environments. The script can also recover missing ~/Desktop items from the matching environment's snapshot.

How to use it

Save the script below as Linux_desktopicons.sh and make it executable:

chmod +x Linux_desktopicons.sh

Back up your current layout

Run this while logged into the desktop you want to capture. It auto-detects Cinnamon vs. XFCE:

./Linux_desktopicons.sh backup

Restore the layout

Run this after icons get scrambled (e.g. a monitor change or a login glitch). It also auto-detects the current desktop:

./Linux_desktopicons.sh restore

Running the script with no argument is the same as restore.

Being explicit about the environment

If auto-detection fails, or you want to target a specific environment, use the explicit commands:

./Linux_desktopicons.sh backup-cinnamon
./Linux_desktopicons.sh restore-cinnamon
./Linux_desktopicons.sh backup-xfce
./Linux_desktopicons.sh restore-xfce

Where backups are stored

The backup root defaults to /media/$USER/storage/LinuxSaved/backup/DesktopIcons (derived from the logged-in user, not a hardcoded name). Override it by setting the DESKTOP_ICONS_BACKUP_DIR environment variable, e.g.:

DESKTOP_ICONS_BACKUP_DIR="$HOME/backups/DesktopIcons" ./Linux_desktopicons.sh backup
Environment Backup location
Cinnamon / Nemo <backup-root>/cinnamon_nemo
XFCE <backup-root>/xfce

Each backup captures icon positions, the relevant desktop settings, and a full snapshot of ~/Desktop so missing items can be recovered on restore.

The script

#!/bin/bash
#desc: restores the icon locations in cinnamon or xfce
# Backup and restore desktop icon layout + basic desktop item recovery for:
#   - Cinnamon (Nemo managing desktop)
#   - XFCE (xfdesktop managing desktop)
#
# Goals:
#   1) Keep Cinnamon/Nemo and XFCE backups completely separate.
#   2) Do NOT translate icon positions between environments.
#   3) Optionally recover missing ~/Desktop items from the matching environment backup.

set -euo pipefail

DESKTOP_DIR="$HOME/Desktop"
# Backup root. Override with DESKTOP_ICONS_BACKUP_DIR; otherwise derive from the
# current user rather than hardcoding a username.
BASE_BACKUP_DIR="${DESKTOP_ICONS_BACKUP_DIR:-/media/$USER/storage/LinuxSaved/backup/DesktopIcons}"

NEMO_BACKUP_DIR="$BASE_BACKUP_DIR/cinnamon_nemo"
XFCE_BACKUP_DIR="$BASE_BACKUP_DIR/xfce"

# Cinnamon/Nemo artifacts
NEMO_ICONS_FILE="$NEMO_BACKUP_DIR/icons.backup"
NEMO_DESKTOP_DCONF="$NEMO_BACKUP_DIR/org.nemo.desktop.backup"
NEMO_ICONVIEW_DCONF="$NEMO_BACKUP_DIR/org.nemo.icon-view.backup"
NEMO_DESKTOP_METADATA="$NEMO_BACKUP_DIR/desktop-metadata.backup"

# XFCE artifacts
XFCE_DESKTOP_DIR="$HOME/.config/xfce4/desktop"
XFCE_XML_DIR="$HOME/.config/xfce4/xfconf/xfce-perchannel-xml"
XFCE_XML_FILE="xfce4-desktop.xml"

log() { printf "%s\n" "$*"; }

ensure_dir() {
  local d="$1"
  [ -d "$d" ] || mkdir -p "$d"
}

command_exists() { command -v "$1" >/dev/null 2>&1; }


detect_desktop() {
  # Prefer environment variables; fall back to running processes.
  # Returns: "cinnamon" or "xfce"
  local xdg="${XDG_CURRENT_DESKTOP:-}"
  local session="${DESKTOP_SESSION:-}"
  local gdm="${GDMSESSION:-}"

  local probe
  probe="$(printf '%s
%s
%s
' "$xdg" "$session" "$gdm" | tr '[:upper:]' '[:lower:]')"

  if printf '%s' "$probe" | grep -q 'cinnamon'; then
    printf '%s
' "cinnamon"
    return 0
  fi
  if printf '%s' "$probe" | grep -q 'xfce'; then
    printf '%s
' "xfce"
    return 0
  fi

  # Process-based fallback
  if pgrep -x xfdesktop >/dev/null 2>&1; then
    printf '%s
' "xfce"
    return 0
  fi
  if pgrep -x nemo-desktop >/dev/null 2>&1; then
    printf '%s
' "cinnamon"
    return 0
  fi
  if pgrep -x cinnamon >/dev/null 2>&1; then
    printf '%s
' "cinnamon"
    return 0
  fi

  return 1
}

backup_current() {
  local de
  if ! de="$(detect_desktop)"; then
    echo "Error: Unable to detect current desktop environment (expected Cinnamon or XFCE)." >&2
    echo "Hint: run with an explicit command (backup-cinnamon / backup-xfce)." >&2
    exit 2
  fi

  case "$de" in
    cinnamon) backup_nemo ;;
    xfce)     backup_xfce ;;
    *)
      echo "Error: Unsupported detected desktop environment: $de" >&2
      exit 2
      ;;
  esac
}

restore_current() {
  local de
  if ! de="$(detect_desktop)"; then
    echo "Error: Unable to detect current desktop environment (expected Cinnamon or XFCE)." >&2
    echo "Hint: run with an explicit command (restore-cinnamon / restore-xfce)." >&2
    exit 2
  fi

  case "$de" in
    cinnamon) restore_nemo ;;
    xfce)     restore_xfce ;;
    *)
      echo "Error: Unsupported detected desktop environment: $de" >&2
      exit 2
      ;;
  esac
}

backup_desktop_items() {
  # Full snapshot of ~/Desktop for best-effort recovery within the same environment.
  local target_dir="$1/desktop-items"
  ensure_dir "$1"
  rm -rf "$target_dir"
  ensure_dir "$target_dir"

  if [ -d "$DESKTOP_DIR" ]; then
    if command_exists rsync; then
      # Preserve permissions, symlinks, times; omit device/special files.
      rsync -a --delete --exclude '.Trash*' "$DESKTOP_DIR"/ "$target_dir"/
    else
      # Fallback: copy everything (including hidden), best-effort.
      ( shopt -s dotglob nullglob
        cp -a "$DESKTOP_DIR"/* "$target_dir"/ 2>/dev/null || true
      )
    fi
  fi
}

restore_missing_desktop_items() {
  local source_dir="$1/desktop-items"
  if [ ! -d "$source_dir" ]; then
    log "Note: No desktop-items snapshot found at: $source_dir"
    return 0
  fi
  ensure_dir "$DESKTOP_DIR"

  ( shopt -s dotglob nullglob
    for item in "$source_dir"/*; do
      [ -e "$item" ] || continue
      local name
      name="$(basename "$item")"
      if [ ! -e "$DESKTOP_DIR/$name" ]; then
        log "Recovering missing Desktop item: $name"
        cp -a "$item" "$DESKTOP_DIR/$name" 2>/dev/null || true
      fi
    done
  )
}

############################################
# Cinnamon / Nemo
############################################
backup_nemo() {
  ensure_dir "$NEMO_BACKUP_DIR"

  : > "$NEMO_ICONS_FILE"

  # Include hidden files; exclude '.' and '..'
  ( shopt -s dotglob nullglob
    for file in "$DESKTOP_DIR"/*; do
      [ -e "$file" ] || continue
      local filename gio_output position
      filename="$(basename "$file")"

      # Nemo stores icon coordinates as gio metadata
      gio_output="$(gio info "$file" 2>/dev/null || true)"
      position="$(printf "%s\n" "$gio_output" | grep -F "metadata::nemo-icon-position:" | awk '{print $2}' || true)"

      if [ -n "${position:-}" ]; then
        printf "%s:%s\n" "$filename" "$position" >> "$NEMO_ICONS_FILE"
      fi
    done
  )

  # Save Nemo desktop settings
  dconf dump /org/nemo/desktop/ > "$NEMO_DESKTOP_DCONF" || true
  dconf dump /org/nemo/icon-view/ > "$NEMO_ICONVIEW_DCONF" || true

  # Save Nemo desktop-metadata file (if present)
  if [ -f "$HOME/.config/nemo/desktop-metadata" ]; then
    cp -f -p "$HOME/.config/nemo/desktop-metadata" "$NEMO_DESKTOP_METADATA"
  fi

  # Snapshot the Desktop contents for recovery
  backup_desktop_items "$NEMO_BACKUP_DIR"

  log "Cinnamon/Nemo backup complete:"
  log "  $NEMO_BACKUP_DIR"
}

restore_nemo() {
  if [ ! -f "$NEMO_DESKTOP_DCONF" ] || [ ! -f "$NEMO_ICONVIEW_DCONF" ]; then
    log "Error: Nemo dconf backups not found in: $NEMO_BACKUP_DIR"
    exit 1
  fi

  # Restore desktop items (best-effort)
  restore_missing_desktop_items "$NEMO_BACKUP_DIR"

  # Restore Nemo settings
  dconf load /org/nemo/desktop/ < "$NEMO_DESKTOP_DCONF" || true
  dconf load /org/nemo/icon-view/ < "$NEMO_ICONVIEW_DCONF" || true

  # Restore desktop-metadata (if present)
  if [ -f "$NEMO_DESKTOP_METADATA" ]; then
    ensure_dir "$HOME/.config/nemo"
    cp -f -p "$NEMO_DESKTOP_METADATA" "$HOME/.config/nemo/desktop-metadata"
  fi

  # Restore icon positions (only if we have them)
  if [ -f "$NEMO_ICONS_FILE" ]; then
    while IFS=: read -r filename position; do
      [ -n "${filename:-}" ] || continue
      [ -n "${position:-}" ] || continue
      local filepath="$DESKTOP_DIR/$filename"
      if [ -e "$filepath" ]; then
        gio set "$filepath" metadata::nemo-icon-position "$position" >/dev/null 2>&1 || true
      fi
    done < "$NEMO_ICONS_FILE"
  fi

  # Restart nemo-desktop to apply changes
  if pgrep -x nemo-desktop >/dev/null 2>&1; then
    killall nemo-desktop >/dev/null 2>&1 || true
  fi
  nohup nemo-desktop >/dev/null 2>&1 & disown || true

  log "Cinnamon/Nemo restore complete."
}

############################################
# XFCE / xfdesktop
############################################
backup_xfce() {
  ensure_dir "$XFCE_BACKUP_DIR"

  # Save xfdesktop icon layout files (multi-monitor friendly)
  if [ -d "$XFCE_DESKTOP_DIR" ]; then
    ensure_dir "$XFCE_BACKUP_DIR/xfce-desktop"
    cp -a "$XFCE_DESKTOP_DIR"/icons.screen*.rc "$XFCE_BACKUP_DIR/xfce-desktop/" 2>/dev/null || true
  fi

  # Save main desktop config (contains icon settings among other things)
  if [ -f "$XFCE_XML_DIR/$XFCE_XML_FILE" ]; then
    ensure_dir "$XFCE_BACKUP_DIR/xfconf"
    cp -a "$XFCE_XML_DIR/$XFCE_XML_FILE" "$XFCE_BACKUP_DIR/xfconf/$XFCE_XML_FILE" 2>/dev/null || true
  fi

  # Snapshot the Desktop contents for recovery
  backup_desktop_items "$XFCE_BACKUP_DIR"

  log "XFCE backup complete:"
  log "  $XFCE_BACKUP_DIR"
}

restore_xfce() {
  # Restore desktop items (best-effort)
  restore_missing_desktop_items "$XFCE_BACKUP_DIR"

  # Restore layout files
  if [ -d "$XFCE_BACKUP_DIR/xfce-desktop" ]; then
    ensure_dir "$XFCE_DESKTOP_DIR"
    cp -a "$XFCE_BACKUP_DIR/xfce-desktop"/icons.screen*.rc "$XFCE_DESKTOP_DIR/" 2>/dev/null || true
  fi

  # Restore main config file
  if [ -f "$XFCE_BACKUP_DIR/xfconf/$XFCE_XML_FILE" ]; then
    ensure_dir "$XFCE_XML_DIR"
    cp -a "$XFCE_BACKUP_DIR/xfconf/$XFCE_XML_FILE" "$XFCE_XML_DIR/$XFCE_XML_FILE" 2>/dev/null || true
  fi

  # Reload xfdesktop (preferred) or restart it
  if command_exists xfdesktop; then
    xfdesktop --reload >/dev/null 2>&1 || true
  fi
  if pgrep -x xfdesktop >/dev/null 2>&1; then
    pkill xfdesktop >/dev/null 2>&1 || true
  fi
  nohup xfdesktop >/dev/null 2>&1 & disown || true

  log "XFCE restore complete."
}

usage() {
  cat <<'USAGE'
Usage:
  desktopicons_with_restore.sh backup
  desktopicons_with_restore.sh restore

  desktopicons_with_restore.sh backup-current
  desktopicons_with_restore.sh restore-current

  desktopicons_with_restore.sh backup-cinnamon
  desktopicons_with_restore.sh restore-cinnamon
  desktopicons_with_restore.sh backup-xfce
  desktopicons_with_restore.sh restore-xfce

Notes:
  - 'backup'/'restore' automatically detect your current desktop (Cinnamon vs XFCE).
  - Backup root defaults to /media/$USER/storage/LinuxSaved/backup/DesktopIcons
    (override with the DESKTOP_ICONS_BACKUP_DIR environment variable).
  - Cinnamon backups are stored under: <backup-root>/cinnamon_nemo
  - XFCE backups are stored under:      <backup-root>/xfce
  - Restores are environment-specific; no cross-desktop translation is attempted.
USAGE
}
case "${1:-}" in
  "")                       restore_current ;;
  backup|backup-current)     backup_current ;;
  restore|restore-current)   restore_current ;;
  backup-cinnamon)           backup_nemo ;;
  restore-cinnamon)          restore_nemo ;;
  backup-xfce)               backup_xfce ;;
  restore-xfce)              restore_xfce ;;
  -h|--help|help)            usage; exit 0 ;;
  *)
    echo "Error: Invalid argument: $1" >&2
    usage
    exit 1
    ;;
esac