DaVinci-Resolve-Omarchy/Omarchy_resolve_v2.sh
28allday d45f1db30d Add detailed comments to script and comprehensive README
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 11:43:00 +00:00

440 lines
20 KiB
Bash
Executable file

#!/usr/bin/env bash
# ==============================================================================
# DaVinci Resolve Installer for Omarchy (Arch Linux + Hyprland + NVIDIA)
#
# DaVinci Resolve is a professional video editing suite by Blackmagic Design.
# It's distributed as a self-extracting AppImage-style .run file inside a ZIP.
# This script automates the entire installation process on Omarchy, handling
# all the quirks and workarounds needed to get Resolve running on Arch Linux.
#
# Why this script exists:
# Resolve is built for CentOS/RHEL and bundles its own versions of many
# libraries. On Arch Linux, some of these bundled libraries conflict with
# system libraries (especially glib), while others (libc++, libc++abi)
# MUST be kept because Resolve was compiled against specific ABI versions.
# Getting this balance right is tricky — this script handles it automatically.
#
# What this script does:
# 1. Finds the Resolve ZIP in ~/Downloads/
# 2. Installs system dependencies (codecs, GPU libs, legacy compat libs)
# 3. Extracts the ZIP → .run → squashfs-root (AppImage payload)
# 4. Replaces bundled glib/gio/gmodule with system versions (ABI-safe)
# 5. Keeps vendor libc++/libc++abi (removing these breaks Resolve)
# 6. Installs to /opt/resolve with RPATH patching for all ELF binaries
# 7. Ensures legacy libcrypt.so.1 is available (Arch dropped it)
# 8. Installs desktop entries, icons, and udev rules
# 9. Creates an XWayland wrapper script for Hyprland compatibility
#
# Prerequisites:
# - Omarchy (Arch Linux) with NVIDIA drivers installed and working
# - DaVinci Resolve Linux ZIP downloaded to ~/Downloads/
# - Internet connection (for installing packages)
# ==============================================================================
set -euo pipefail
# Logging helpers with visual indicators for easy scanning of output
log(){ echo -e "$*"; }
warn(){ echo -e "⚠️ $*" >&2; }
err(){ echo -e "$*" >&2; exit 1; }
# Find the Resolve ZIP file in ~/Downloads/. The user must download it
# manually from https://www.blackmagicdesign.com/products/davinciresolve
# because Blackmagic requires filling out a form (no direct download link).
# If multiple ZIPs exist (e.g. different versions), we use the newest one.
ZIP_DIR="${HOME}/Downloads"
shopt -s nullglob
ZIP_FILES=("${ZIP_DIR}"/DaVinci_Resolve*_Linux.zip)
shopt -u nullglob
if [[ ${#ZIP_FILES[@]} -eq 0 ]]; then
err "Put the official DaVinci Resolve Linux ZIP in ${ZIP_DIR}"
fi
# Sort by modification time, newest first
RESOLVE_ZIP="$(ls -1t "${ZIP_FILES[@]}" 2>/dev/null | head -n1)"
[[ -n "${RESOLVE_ZIP}" ]] || err "Could not determine newest ZIP file"
log "Using installer ZIP: ${RESOLVE_ZIP}"
# ==================== Package Installation ====================
#
# System upgrade is opt-in because a full -Syu can update the kernel or
# NVIDIA driver stack, which might break things or require a reboot in
# the middle of the install. Set RESOLVE_FULL_UPGRADE=1 if you want it.
# Otherwise we just sync the package database (-Sy) so pacman knows
# what's available without actually upgrading anything.
if [[ "${RESOLVE_FULL_UPGRADE:-0}" == "1" ]]; then
log "Updating system packages (RESOLVE_FULL_UPGRADE=1)..."
sudo pacman -Syu --noconfirm
else
log "Skipping full system upgrade (set RESOLVE_FULL_UPGRADE=1 to enable)"
# Just sync package database without upgrading
sudo pacman -Sy --noconfirm
fi
# Build/extraction tools:
# unzip: Extracts the Resolve ZIP archive
# patchelf: Modifies RPATH in ELF binaries (tells them where to find libs)
# libarchive: Archive handling library (dependency for extraction)
# xdg-user-dirs: Ensures standard user directories exist (~/Downloads, etc.)
# desktop-file-utils: Provides update-desktop-database for app menu integration
# file: Identifies file types (used to find ELF binaries for patching)
# gtk-update-icon-cache: Refreshes the icon cache so Resolve's icon appears
log "Installing required tools..."
if ! sudo pacman -S --needed --noconfirm unzip patchelf libarchive xdg-user-dirs desktop-file-utils file gtk-update-icon-cache; then
warn "Some optional tools failed to install, continuing anyway..."
fi
# Runtime dependencies that Resolve needs but doesn't bundle:
# libxcrypt-compat: Provides legacy libcrypt.so.1 (Arch moved to libxcrypt v2)
# ffmpeg4.4: Older FFmpeg version that Resolve links against
# glu: OpenGL Utility Library (3D rendering support)
# gtk2: GTK2 toolkit (Resolve's UI uses some GTK2 components)
# fuse2: Filesystem in Userspace v2 (for AppImage compatibility)
#
# IMPORTANT: We deliberately do NOT replace Resolve's bundled libc++/libc++abi
# with system versions. Resolve was compiled against specific C++ ABI versions
# and swapping them causes crashes. Only glib/gio/gmodule get replaced (later).
log "Installing runtime dependencies..."
if ! sudo pacman -S --needed --noconfirm libxcrypt-compat ffmpeg4.4 glu gtk2 fuse2; then
warn "Some runtime dependencies failed to install (may affect functionality)"
fi
# Resolve's built-in extras downloader expects TLS certificates at the
# Red Hat/CentOS path (/etc/pki/tls) rather than the Arch path (/etc/ssl).
# This symlink lets it find the system certificates.
if [[ ! -e /etc/pki/tls ]]; then
sudo mkdir -p /etc/pki
sudo ln -sf /etc/ssl /etc/pki/tls
fi
# ==================== Extraction ====================
#
# The Resolve download is a ZIP containing a .run file. The .run file is a
# self-extracting AppImage-style archive containing a squashfs filesystem.
# We extract it in stages: ZIP → .run → squashfs-root (the actual app files).
#
# This needs about 10GB of free space for the temporary extraction.
# Everything is cleaned up automatically when the script exits (via trap).
NEEDED_GB=10
FREE_KB=$(df --output=avail -k "${ZIP_DIR}" | tail -n1); FREE_GB=$((FREE_KB/1024/1024))
(( FREE_GB >= NEEDED_GB )) || err "Not enough free space in ${ZIP_DIR}: ${FREE_GB} GiB < ${NEEDED_GB} GiB"
# Create a temporary directory for extraction. Using mktemp ensures a unique
# name so multiple runs don't conflict. The cleanup trap removes it when the
# script exits (whether it succeeds, fails, or is interrupted with Ctrl+C).
WORKDIR="$(mktemp -d -p "${ZIP_DIR}" .resolve-extract-XXXXXXXX)"
cleanup() {
if [[ -n "${WORKDIR:-}" && -d "${WORKDIR}" ]]; then
log "Cleaning up temporary directory..."
rm -rf "${WORKDIR}" 2>/dev/null || true
fi
}
trap cleanup EXIT
log "Unpacking ZIP to ${WORKDIR}"
unzip -q "${RESOLVE_ZIP}" -d "${WORKDIR}"
# Find the .run installer inside the extracted ZIP. It's a self-extracting
# archive that contains the actual application files in a squashfs image.
# --appimage-extract tells it to just extract without trying to run anything.
RUN_FILE="$(find "${WORKDIR}" -maxdepth 2 -type f -name 'DaVinci_Resolve_*_Linux.run' | head -n1 || true)"
[[ -n "${RUN_FILE}" ]] || err "Could not find the .run installer in the ZIP"
chmod +x "${RUN_FILE}"
EX_DIR="$(dirname "${RUN_FILE}")"
log "Extracting AppImage payload…"
if ! ( cd "${EX_DIR}" && "./$(basename "${RUN_FILE}")" --appimage-extract >/dev/null ); then
err "Failed to extract AppImage payload"
fi
APPDIR="${EX_DIR}/squashfs-root"
[[ -d "${APPDIR}" ]] || err "Extraction failed (no squashfs-root)"
# Normalize perms
chmod -R u+rwX,go+rX,go-w "${APPDIR}" || warn "Could not normalize all permissions"
# Minimal validation
[[ -s "${APPDIR}/bin/resolve" ]] || err "resolve binary missing or zero-size"
# ==================== ABI-Safe Library Replacement ====================
#
# This is the most delicate part of the install. Resolve bundles its own
# copies of many libraries, but some of them are too old for Arch and cause
# crashes or segfaults. The trick is knowing WHICH ones to replace:
#
# REPLACE with system versions (these are safe to swap):
# - libglib-2.0.so.0 — GLib core library
# - libgio-2.0.so.0 — GLib I/O library
# - libgmodule-2.0.so.0 — GLib module loading
# These are stable C libraries with a very consistent ABI.
#
# KEEP bundled versions (replacing these breaks Resolve):
# - libc++.so — C++ standard library (LLVM)
# - libc++abi.so — C++ ABI support library
# Resolve was compiled with a specific libc++ version. Using the system
# version causes ABI mismatches and immediate crashes.
pushd "${APPDIR}" >/dev/null
# Verify system libraries exist before replacing bundled ones
declare -A GLIB_LIBS=(
["/usr/lib/libglib-2.0.so.0"]="libs/libglib-2.0.so.0"
["/usr/lib/libgio-2.0.so.0"]="libs/libgio-2.0.so.0"
["/usr/lib/libgmodule-2.0.so.0"]="libs/libgmodule-2.0.so.0"
)
for syslib in "${!GLIB_LIBS[@]}"; do
target="${GLIB_LIBS[$syslib]}"
if [[ -e "${syslib}" ]]; then
rm -f "${target}" || true
ln -sf "${syslib}" "${target}" || warn "Failed to symlink ${syslib}"
else
warn "System library ${syslib} not found, keeping bundled version"
fi
done
# Extract DaVinci control panel libraries from a bundled tarball and move
# them into the main libs/ directory so they're found at runtime. These
# support Blackmagic's hardware control surfaces (DaVinci Resolve Editor
# Keyboard, Mini Panel, Micro Panel, etc.).
if [[ -d "share/panels" ]]; then
pushd "share/panels" >/dev/null
tar -zxf dvpanel-framework-linux-x86_64.tgz 2>/dev/null || true
mkdir -p "${APPDIR}/libs"
find . -maxdepth 1 -type f -name '*.so' -exec mv -f {} "${APPDIR}/libs" \; 2>/dev/null || true
if [[ -d lib ]]; then
find lib -type f -name '*.so*' -exec mv -f {} "${APPDIR}/libs" \; 2>/dev/null || true
fi
popd >/dev/null
fi
# Clean up AppImage launcher files and installer leftovers — we don't need
# them since we're installing to /opt/resolve directly, not running as an AppImage.
rm -f "AppRun" "AppRun*" 2>/dev/null || true
rm -rf "installer" "installer*" 2>/dev/null || true
mkdir -p "bin"
ln -sf "../BlackmagicRAWPlayer/BlackmagicRawAPI" "bin/" 2>/dev/null || true
popd >/dev/null
# ==================== Install to /opt/resolve ====================
#
# Copy the extracted application to its final location. /opt/ is the
# standard Linux directory for third-party software that doesn't come
# from the package manager. Using rsync (if available) is faster for
# re-installs because it only copies changed files.
log "Installing Resolve to /opt/resolve…"
sudo rm -rf /opt/resolve
sudo mkdir -p /opt/resolve
if command -v rsync >/dev/null 2>&1; then
sudo rsync -a --delete "${APPDIR}/" /opt/resolve/
else
sudo cp -a "${APPDIR}/." /opt/resolve/
fi
sudo mkdir -p /opt/resolve/.license
# RPATH Patching
#
# RPATH is a field inside ELF binaries that tells the dynamic linker where
# to search for shared libraries. Resolve's binaries have RPATHs pointing
# to the original AppImage extraction paths, which don't exist anymore.
#
# We patch EVERY ELF binary (executables and shared objects) to search
# /opt/resolve/libs/ and all its subdirectories. This includes large files
# like libQt5WebEngineCore.so (~200MB) — skipping them causes "library not
# found" errors because they link to other Resolve libs.
#
# This step can take a minute or two due to the number of files.
log "Applying RPATH with patchelf (this may take a while for large libraries)…"
RPATH_DIRS=( "libs" "libs/plugins/sqldrivers" "libs/plugins/xcbglintegrations" "libs/plugins/imageformats"
"libs/plugins/platforms" "libs/Fusion" "plugins" "bin"
"BlackmagicRAWSpeedTest/BlackmagicRawAPI" "BlackmagicRAWSpeedTest/plugins/platforms"
"BlackmagicRAWSpeedTest/plugins/imageformats" "BlackmagicRAWSpeedTest/plugins/mediaservice"
"BlackmagicRAWSpeedTest/plugins/audio" "BlackmagicRAWSpeedTest/plugins/xcbglintegrations"
"BlackmagicRAWSpeedTest/plugins/bearer"
"BlackmagicRAWPlayer/BlackmagicRawAPI" "BlackmagicRAWPlayer/plugins/mediaservice"
"BlackmagicRAWPlayer/plugins/imageformats" "BlackmagicRAWPlayer/plugins/audio"
"BlackmagicRAWPlayer/plugins/platforms" "BlackmagicRAWPlayer/plugins/xcbglintegrations"
"BlackmagicRAWPlayer/plugins/bearer"
"Onboarding/plugins/xcbglintegrations" "Onboarding/plugins/qtwebengine"
"Onboarding/plugins/platforms" "Onboarding/plugins/imageformats"
"DaVinci Control Panels Setup/plugins/platforms"
"DaVinci Control Panels Setup/plugins/imageformats"
"DaVinci Control Panels Setup/plugins/bearer"
"DaVinci Control Panels Setup/AdminUtility/PlugIns/DaVinciKeyboards"
"DaVinci Control Panels Setup/AdminUtility/PlugIns/DaVinciPanels" )
RPATH_ABS=""; for p in "${RPATH_DIRS[@]}"; do RPATH_ABS+="/opt/resolve/${p}:"; done; RPATH_ABS+="\$ORIGIN"
if command -v patchelf >/dev/null 2>&1; then
PATCH_COUNT=0
PATCH_FAIL=0
PATCH_SKIP=0
# Process all ELF files regardless of size
while IFS= read -r -d '' f; do
FILE_INFO="$(file -b "$f" 2>/dev/null)"
if [[ "${FILE_INFO}" =~ ELF.*executable ]] || [[ "${FILE_INFO}" =~ ELF.*shared\ object ]]; then
# Skip if file already has correct RPATH (optimization for re-runs)
CURRENT_RPATH="$(patchelf --print-rpath "$f" 2>/dev/null || true)"
if [[ "${CURRENT_RPATH}" == "${RPATH_ABS}" ]]; then
((PATCH_SKIP++)) || true
continue
fi
if sudo patchelf --set-rpath "${RPATH_ABS}" "$f" 2>/dev/null; then
((PATCH_COUNT++)) || true
else
((PATCH_FAIL++)) || true
# Log failures for large files specifically as they're more critical
FILE_SIZE=$(stat -c%s "$f" 2>/dev/null || echo 0)
if (( FILE_SIZE > 33554432 )); then # >32M
warn "Failed to patch large file: ${f##/opt/resolve/}"
fi
fi
fi
done < <(find /opt/resolve -type f -print0)
log "Patched RPATH: ${PATCH_COUNT} files (${PATCH_FAIL} failures, ${PATCH_SKIP} already correct)"
else
warn "patchelf not found, skipping RPATH patching"
fi
# Legacy libcrypt Fix
#
# Arch Linux moved from libcrypt.so.1 to libcrypt.so.2 (via libxcrypt).
# Resolve still links against the old .so.1 version. libxcrypt-compat
# provides it, and we symlink it into Resolve's libs directory as a
# fallback in case the system-wide version isn't found in the search path.
sudo pacman -S --needed --noconfirm libxcrypt-compat || true
sudo ldconfig || true
if [[ -e /usr/lib/libcrypt.so.1 ]]; then
sudo ln -sf /usr/lib/libcrypt.so.1 /opt/resolve/libs/libcrypt.so.1
fi
# ==================== Desktop Integration ====================
#
# Install .desktop files (app menu entries), icons, and udev rules so
# Resolve integrates properly with the desktop environment. The .desktop
# files go to /usr/share/applications/ (system-wide) and icons go to
# the hicolor icon theme at standard sizes.
log "Installing desktop entries and icons..."
declare -A DESKTOP_FILES=(
["/opt/resolve/share/DaVinciResolve.desktop"]="/usr/share/applications/DaVinciResolve.desktop"
["/opt/resolve/share/DaVinciControlPanelsSetup.desktop"]="/usr/share/applications/DaVinciControlPanelsSetup.desktop"
["/opt/resolve/share/blackmagicraw-player.desktop"]="/usr/share/applications/blackmagicraw-player.desktop"
["/opt/resolve/share/blackmagicraw-speedtest.desktop"]="/usr/share/applications/blackmagicraw-speedtest.desktop"
)
for src in "${!DESKTOP_FILES[@]}"; do
dest="${DESKTOP_FILES[$src]}"
if [[ -f "${src}" ]]; then
sudo install -D -m 0644 "${src}" "${dest}"
else
warn "Desktop file not found: ${src}"
fi
done
# Icons (ensure hicolor sizes present so menus show right icon)
declare -A ICON_FILES=(
["/opt/resolve/graphics/DV_Resolve.png"]="/usr/share/icons/hicolor/128x128/apps/davinci-resolve.png"
["/opt/resolve/graphics/DV_Panels.png"]="/usr/share/icons/hicolor/128x128/apps/davinci-resolve-panels-setup.png"
["/opt/resolve/graphics/blackmagicraw-player_256x256_apps.png"]="/usr/share/icons/hicolor/256x256/apps/blackmagicraw-player.png"
["/opt/resolve/graphics/blackmagicraw-speedtest_256x256_apps.png"]="/usr/share/icons/hicolor/256x256/apps/blackmagicraw-speedtest.png"
)
for src in "${!ICON_FILES[@]}"; do
dest="${ICON_FILES[$src]}"
if [[ -f "${src}" ]]; then
sudo install -D -m 0644 "${src}" "${dest}"
else
warn "Icon file not found: ${src}"
fi
done
sudo update-desktop-database >/dev/null 2>&1 || true
sudo gtk-update-icon-cache -f /usr/share/icons/hicolor >/dev/null 2>&1 || true
# Udev rules — these give Resolve permission to access Blackmagic hardware
# devices (capture cards, control panels, editing keyboards) without root.
# Without these rules, the devices would only be accessible as root.
for r in 99-BlackmagicDevices.rules 99-ResolveKeyboardHID.rules 99-DavinciPanel.rules; do
if [[ -f "/opt/resolve/share/etc/udev/rules.d/${r}" ]]; then
sudo install -D -m 0644 "/opt/resolve/share/etc/udev/rules.d/${r}" "/usr/lib/udev/rules.d/${r}"
fi
done
sudo udevadm control --reload-rules && sudo udevadm trigger || true
# ==================== XWayland Wrapper Script ====================
#
# DaVinci Resolve does NOT support native Wayland — it only works under
# X11 or XWayland. Hyprland (Omarchy's compositor) provides XWayland
# compatibility, but Resolve needs to be told to use it explicitly.
#
# This wrapper script:
# 1. Clears stale Qt lockfiles that can prevent Resolve from starting
# (happens when Resolve crashes or is killed without clean shutdown)
# 2. Forces QT_QPA_PLATFORM=xcb (tells Qt to use X11/XWayland, not Wayland)
# 3. Enables Qt's auto screen scaling for HiDPI displays
# 4. Launches the actual Resolve binary
#
# For hybrid NVIDIA laptops (Optimus), you can uncomment the PRIME render
# offload lines to force Resolve onto the discrete GPU.
cat << 'EOF' | sudo tee /usr/local/bin/resolve-nvidia-open >/dev/null
#!/usr/bin/env bash
set -euo pipefail
# Clear stale single-instance Qt lockfiles (only if we have permission)
if [[ -r /tmp ]]; then
for lockfile in /tmp/qtsingleapp-DaVinci*lockfile; do
[[ -f "$lockfile" ]] && rm -f "$lockfile" 2>/dev/null || true
done
fi
# Force XWayland under Hyprland/Wayland
export QT_QPA_PLATFORM=xcb
export QT_AUTO_SCREEN_SCALE_FACTOR=1
# For hybrid laptops, optionally force dGPU:
# export __NV_PRIME_RENDER_OFFLOAD=1
# export __GLX_VENDOR_LIBRARY_NAME=nvidia
exec /opt/resolve/bin/resolve "$@"
EOF
sudo chmod +x /usr/local/bin/resolve-nvidia-open
# Create a convenience symlink at /usr/bin/davinci-resolve so users can
# launch Resolve by typing "davinci-resolve" in any terminal. Points to
# the wrapper script so XWayland settings are always applied.
if [[ ! -e /usr/bin/davinci-resolve ]]; then
if [[ -x /usr/local/bin/resolve-nvidia-open ]]; then
echo -e '#!/usr/bin/env bash\nexec /usr/local/bin/resolve-nvidia-open "$@"' | sudo tee /usr/bin/davinci-resolve >/dev/null
else
echo -e '#!/usr/bin/env bash\nexec /opt/resolve/bin/resolve "$@"' | sudo tee /usr/bin/davinci-resolve >/dev/null
fi
sudo chmod +x /usr/bin/davinci-resolve
fi
# Update the system .desktop files to use our wrapper instead of launching
# Resolve directly. This ensures XWayland mode is always used regardless
# of how Resolve is launched (app menu, file association, etc.).
WRAPPER="/usr/local/bin/resolve-nvidia-open"
if [[ -f /usr/share/applications/DaVinciResolve.desktop ]]; then
sudo sed -i "s|^Exec=.*|Exec=${WRAPPER} %U|" /usr/share/applications/DaVinciResolve.desktop
fi
if [[ -f /usr/share/applications/DaVinciResolveCaptureLogs.desktop ]]; then
sudo sed -i "s|^Exec=.*|Exec=${WRAPPER} %U|" /usr/share/applications/DaVinciResolveCaptureLogs.desktop
fi
sudo update-desktop-database >/dev/null 2>&1 || true
# Create a user-level .desktop entry in ~/.local/share/applications/.
# User-level entries take precedence over system-level ones, so this
# ensures the wrapper is always used even if a system update overwrites
# the system .desktop file. Also sets StartupWMClass=resolve so Hyprland
# can properly identify the window for window rules and taskbar grouping.
mkdir -p "${HOME}/.local/share/applications"
cat > "${HOME}/.local/share/applications/davinci-resolve-wrapper.desktop" << EOF
[Desktop Entry]
Type=Application
Name=DaVinci Resolve
Comment=DaVinci Resolve via XWayland wrapper (NVIDIA-Open)
Exec=${WRAPPER} %U
TryExec=${WRAPPER}
Terminal=false
Icon=davinci-resolve
Categories=AudioVideo;Video;Audio;Graphics;
StartupWMClass=resolve
X-GNOME-UsesNotifications=true
EOF
update-desktop-database "${HOME}/.local/share/applications" >/dev/null 2>&1 || true
sudo gtk-update-icon-cache -f /usr/share/icons/hicolor >/dev/null 2>&1 || true
echo
echo "✅ DaVinci Resolve installed to /opt/resolve"
echo " (vendor libc++ kept; libcrypt.so.1 ensured)"
echo " Launch from your app menu, or run: resolve-nvidia-open"
echo " Logs: ~/.local/share/DaVinciResolve/logs/ResolveDebug.txt"
echo