aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rwxr-xr-xbuild.sh20
-rw-r--r--docker/AppRun.sh131
-rw-r--r--docker/Dockerfile23
-rwxr-xr-xdocker/build-inner.sh133
-rw-r--r--libs/archive/src/library.h2
-rw-r--r--libs/plugin_python/src/proxy/proxypython.cpp13
-rw-r--r--libs/uibase/src/utility.cpp11
-rw-r--r--src/src/envshortcut.cpp26
-rw-r--r--src/src/mainwindow.cpp4
-rw-r--r--src/src/moapplication.cpp2
-rw-r--r--src/src/nxmhandler_linux.cpp8
-rw-r--r--src/src/prefixsetuprunner.cpp12
-rw-r--r--src/src/protonlauncher.cpp37
-rw-r--r--src/src/shared/appconfig.h12
15 files changed, 59 insertions, 383 deletions
diff --git a/.gitignore b/.gitignore
index f247e19..1b7b4ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,9 +7,6 @@ build-container-2404-gcc14/
# Rust build artifacts
target/
-# AppImage build artifacts
-*.AppImage
-
# Logs
*.log
@@ -23,11 +20,8 @@ compile_commands.json
.DS_Store
Thumbs.db
-# AppImage extraction/build leftovers
-squashfs-root/
+# Build leftovers
build-source/
-flatpak-repo/
-*.flatpak
# Python bytecode
__pycache__/
diff --git a/build.sh b/build.sh
index 27afc98..b57d296 100755
--- a/build.sh
+++ b/build.sh
@@ -7,8 +7,7 @@ set -euo pipefail
# ./build.sh # Build portable .tar.gz (default)
# ./build.sh tarball # Build portable .tar.gz only
# ./build.sh installer # Build self-extracting .bin installer only
-# ./build.sh appimage # Build AppImage (legacy)
-# ./build.sh all # Build tarball + AppImage (if available)
+# ./build.sh all # Build tarball + installer
# ./build.sh shell # Drop into the build container for debugging
#
# Prerequisites: Docker or Podman
@@ -32,27 +31,20 @@ cd "${SCRIPT_DIR}"
# Determine build mode from first argument
BUILD_MODE="${1:-tarball}"
case "${BUILD_MODE}" in
- tarball|installer|appimage|all|shell) ;;
+ tarball|installer|all|shell) ;;
*)
- echo "Usage: ./build.sh [tarball|installer|appimage|all|shell]"
+ echo "Usage: ./build.sh [tarball|installer|all|shell]"
echo ""
echo " tarball Build portable .tar.gz"
echo " installer Build self-extracting .bin installer"
- echo " appimage Build AppImage (legacy)"
- echo " all Build tarball (+ AppImage if available)"
+ echo " all Build tarball + installer"
echo " shell Drop into build container"
exit 1
;;
esac
-# Build the Docker image if it doesn't exist or Dockerfile changed.
-# Pass BUILD_APPIMAGE=1 only when AppImage builds are requested.
echo "=== Ensuring build image is up to date ==="
-DOCKER_BUILD_ARGS=()
-if [ "${BUILD_MODE}" = "appimage" ] || [ "${BUILD_MODE}" = "all" ]; then
- DOCKER_BUILD_ARGS+=(--build-arg BUILD_APPIMAGE=1)
-fi
-${DOCKER} build "${DOCKER_BUILD_ARGS[@]}" -t "${IMAGE_NAME}" docker/
+${DOCKER} build -t "${IMAGE_NAME}" docker/
# Persistent ccache directory for faster rebuilds.
CCACHE_DIR="${HOME}/.cache/fluorine-ccache"
@@ -92,5 +84,5 @@ ${DOCKER} run --rm \
echo ""
echo "=== Done ==="
echo "Build outputs:"
-ls -lh build/fluorine-manager.tar.gz build/fluorine-manager.bin build/*.AppImage 2>/dev/null || echo " (none found)"
+ls -ldh build/fluorine-manager build/fluorine-manager.bin 2>/dev/null || echo " (none found)"
echo "Staging: build/staging/"
diff --git a/docker/AppRun.sh b/docker/AppRun.sh
deleted file mode 100644
index 145ac61..0000000
--- a/docker/AppRun.sh
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/bin/bash
-HERE="$(dirname "$(readlink -f "$0")")"
-APPIMAGE_DIR="$(dirname "$(readlink -f "${APPIMAGE:-$0}")")"
-
-cleanup_stale_fuse_mounts() {
- local root="$1"
- [ -n "${root}" ] || return 0
- [ -r /proc/mounts ] || return 0
-
- local src mp fstype rest mount_path stat_out
- while IFS=' ' read -r src mp fstype rest; do
- # Decode escaped paths from /proc/mounts.
- mount_path="${mp//\\040/ }"
- mount_path="${mount_path//\\011/$'\t'}"
- mount_path="${mount_path//\\012/$'\n'}"
-
- case "${mount_path}" in
- "${root}"/*) ;;
- *) continue ;;
- esac
-
- case "${fstype}" in
- fuse*|fuse.*) ;;
- *) continue ;;
- esac
-
- stat_out="$(LC_ALL=C stat "${mount_path}" 2>&1 || true)"
- if printf "%s" "${stat_out}" | grep -Eq "Transport endpoint is not connected|Stale file handle|Input/output error"; then
- echo "[Fluorine] Recovering stale FUSE mount: ${mount_path}"
- fusermount3 -uz "${mount_path}" >/dev/null 2>&1 || \
- fusermount -uz "${mount_path}" >/dev/null 2>&1 || \
- umount -l "${mount_path}" >/dev/null 2>&1 || true
- fi
- done < /proc/mounts
-}
-
-cleanup_stale_fuse_mounts "${APPIMAGE_DIR}"
-
-sync_dir_overwrite_files() {
- local name="$1"
- local src="${HERE}/usr/share/fluorine/${name}"
- local dst="${APPIMAGE_DIR}/${name}"
-
- [ -d "${src}" ] || return 0
-
- if [ ! -d "${dst}" ]; then
- echo "[Fluorine] First run: extracting ${name}/ next to AppImage..."
- else
- echo "[Fluorine] Updating ${name}/ bundled files..."
- fi
- mkdir -p "${dst}"
- # Merge bundled payload while skipping VCS metadata (.git, etc.).
- (
- cd "${src}"
- tar --exclude-vcs -cf - .
- ) | (
- cd "${dst}"
- tar -xf -
- )
-}
-
-# ── Extract/sync writable dirs ──
-sync_dir_overwrite_files "plugins"
-sync_dir_overwrite_files "lib"
-sync_dir_overwrite_files "python"
-
-# Some existing Windows portable setups include plugins/plugin_python with
-# .pyd/.dll payload. On Linux, the proxy will prefer this folder if present,
-# which can hide the correct Linux mobase module. Overlay Linux runtime files
-# into that folder to keep compatibility without deleting user content.
-if [ -d "${APPIMAGE_DIR}/plugins/plugin_python" ]; then
- if find "${APPIMAGE_DIR}/plugins/plugin_python" -type f \( -name '*.pyd' -o -name 'python*.dll' \) | grep -q .; then
- echo "[Fluorine] Detected Windows plugin_python payload, overlaying Linux runtime files..."
- mkdir -p "${APPIMAGE_DIR}/plugins/plugin_python/libs"
- [ -d "${APPIMAGE_DIR}/plugins/libs" ] && cp -a "${APPIMAGE_DIR}/plugins/libs/." "${APPIMAGE_DIR}/plugins/plugin_python/libs/"
- PYTHON_ROOT_CANDIDATE="${HERE}/usr/share/fluorine/python"
- [ -d "${PYTHON_ROOT_CANDIDATE}/lib" ] || PYTHON_ROOT_CANDIDATE="${APPIMAGE_DIR}/python"
- if [ -d "${PYTHON_ROOT_CANDIDATE}/lib" ]; then
- PYVER_DIR="$(find "${PYTHON_ROOT_CANDIDATE}/lib" -mindepth 1 -maxdepth 1 -type d -name 'python3.*' | head -n 1)"
- if [ -n "${PYVER_DIR}" ]; then
- SITE_DIR="${PYVER_DIR}/site-packages"
- [ -d "${SITE_DIR}/PyQt6" ] && cp -a "${SITE_DIR}/PyQt6" "${APPIMAGE_DIR}/plugins/plugin_python/libs/"
- [ -d "${SITE_DIR}/PyQt6_sip" ] && cp -a "${SITE_DIR}/PyQt6_sip" "${APPIMAGE_DIR}/plugins/plugin_python/libs/"
- [ -d "${SITE_DIR}/sip" ] && cp -a "${SITE_DIR}/sip" "${APPIMAGE_DIR}/plugins/plugin_python/libs/"
- fi
- fi
- fi
-fi
-
-# ── Environment ──
-# Save original LD_LIBRARY_PATH so child processes (xdg-open, kde-open, etc.)
-# can use host libraries instead of the bundled (potentially older) ones.
-export FLUORINE_ORIG_LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}"
-export PATH="${HERE}/usr/bin:${HERE}/usr/libexec:${PATH}"
-export LD_LIBRARY_PATH="${HERE}/usr/lib:${HERE}/usr/libexec:${LD_LIBRARY_PATH:-}"
-
-# Qt plugins (read-only, inside AppImage)
-export QT_PLUGIN_PATH="${HERE}/usr/plugins"
-export QT_QPA_PLATFORM_PLUGIN_PATH="${HERE}/usr/plugins/platforms"
-if [ -x "${HERE}/usr/libexec/QtWebEngineProcess" ]; then
- export QTWEBENGINEPROCESS_PATH="${HERE}/usr/libexec/QtWebEngineProcess"
-fi
-if [ -d "${HERE}/usr/resources" ]; then
- export QTWEBENGINE_RESOURCES_PATH="${HERE}/usr/resources"
-fi
-if [ -d "${HERE}/usr/translations/qtwebengine_locales" ]; then
- export QTWEBENGINE_LOCALES_PATH="${HERE}/usr/translations/qtwebengine_locales"
-fi
-
-# Tell the app to use the writable dirs next to the AppImage.
-# MO2_BASE_DIR overrides qApp->applicationDirPath() for plugin/library discovery.
-export MO2_BASE_DIR="${APPIMAGE_DIR}"
-export MO2_PLUGINS_DIR="${APPIMAGE_DIR}/plugins"
-export MO2_LIBS_DIR="${APPIMAGE_DIR}/lib"
-MO2_PYTHON_BUNDLED="${HERE}/usr/share/fluorine/python"
-if [ -d "${MO2_PYTHON_BUNDLED}/lib" ]; then
- export MO2_PYTHON_DIR="${MO2_PYTHON_BUNDLED}"
-else
- export MO2_PYTHON_DIR="${APPIMAGE_DIR}/python"
-fi
-# Do not export PYTHONHOME/PYTHONPATH globally here. MO2 sets Python runtime
-# internally for plugin_python, while child processes (NaK/launchers) must
-# use their own system Python environment.
-unset PYTHONHOME PYTHONPATH PYTHONNOUSERSITE
-
-# Raise open file descriptor limit — large modlists with FUSE VFS
-# can easily exceed the default 1024
-ulimit -n 65536 2>/dev/null
-
-cd "${APPIMAGE_DIR}"
-exec "${HERE}/usr/bin/ModOrganizer.bin" "$@"
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 827d45b..643074c 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -74,27 +74,4 @@ RUN /opt/python-bundled/bin/pip3 install --no-cache-dir \
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --default-toolchain stable --profile minimal
-# ── Pre-download linuxdeploy tooling (optional, only for AppImage builds) ──
-# Set BUILD_APPIMAGE=1 to include linuxdeploy in the image.
-# Without it, only tarball and installer builds are available.
-ARG BUILD_APPIMAGE=0
-RUN if [ "${BUILD_APPIMAGE}" = "1" ]; then \
- mkdir -p /opt/linuxdeploy && \
- curl -L --retry 3 -o /opt/linuxdeploy/linuxdeploy-x86_64.AppImage \
- https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage && \
- curl -L --retry 3 -o /opt/linuxdeploy/linuxdeploy-plugin-qt-x86_64.AppImage \
- https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage && \
- chmod +x /opt/linuxdeploy/linuxdeploy-x86_64.AppImage \
- /opt/linuxdeploy/linuxdeploy-plugin-qt-x86_64.AppImage && \
- cd /opt/linuxdeploy && \
- ./linuxdeploy-plugin-qt-x86_64.AppImage --appimage-extract >/dev/null && \
- mv squashfs-root linuxdeploy-plugin-qt.AppDir && \
- chmod +x linuxdeploy-plugin-qt.AppDir/AppRun && \
- ln -sf /opt/linuxdeploy/linuxdeploy-plugin-qt.AppDir/AppRun \
- /opt/linuxdeploy/linuxdeploy-plugin-qt && \
- chmod -x /opt/linuxdeploy/linuxdeploy-plugin-qt-x86_64.AppImage; \
- else \
- echo "Skipping linuxdeploy (BUILD_APPIMAGE not set)"; \
- fi
-
WORKDIR /build
diff --git a/docker/build-inner.sh b/docker/build-inner.sh
index fb5497b..7adab23 100755
--- a/docker/build-inner.sh
+++ b/docker/build-inner.sh
@@ -523,7 +523,7 @@ cp -f /src/data/icons/com.fluorine.manager.metainfo.xml "${OUT_DIR}/icons/"
echo "Wrote manifest: $(wc -l < "${OUT_DIR}/fluorine-manifest.txt") entries"
# ── Determine build mode ──
-# BUILD_MODE is passed from build.sh: tarball (default), installer, appimage, all
+# BUILD_MODE is passed from build.sh: tarball (default), installer, all
BUILD_MODE="${BUILD_MODE:-tarball}"
# ── Build portable distribution (directory) ──
@@ -657,126 +657,6 @@ INSTALLER_HEADER
ls -lh "/src/build/${TARBALL_NAME}.bin"
}
-# ── Build AppImage (legacy, optional) ──
-build_appimage() {
- echo ""
- echo "=== Building AppImage ==="
-
- if [ ! -d /opt/linuxdeploy ]; then
- echo "ERROR: linuxdeploy not available. Rebuild Docker image with --build-arg BUILD_APPIMAGE=1"
- return 1
- fi
-
- APPDIR="/src/build/AppDir"
- rm -rf "${APPDIR}"
- mkdir -p "${APPDIR}/usr/bin" "${APPDIR}/usr/lib" "${APPDIR}/usr/share/applications" \
- "${APPDIR}/usr/plugins" \
- "${APPDIR}/usr/share/icons/hicolor/256x256/apps" \
- "${APPDIR}/usr/share/metainfo"
-
- cp -a "${OUT_DIR}"/. "${APPDIR}/usr/bin/"
- mv "${APPDIR}/usr/bin/lib"/* "${APPDIR}/usr/lib/" 2>/dev/null || true
- rmdir "${APPDIR}/usr/bin/lib" 2>/dev/null || true
- if [ -d "${APPDIR}/usr/bin/qt6plugins" ]; then
- cp -a "${APPDIR}/usr/bin/qt6plugins"/. "${APPDIR}/usr/plugins/"
- fi
-
- cp -f "${OUT_DIR}/icons/com.fluorine.manager.desktop" "${APPDIR}/usr/share/applications/"
- cp -f "${OUT_DIR}/icons/com.fluorine.manager.png" "${APPDIR}/usr/share/icons/hicolor/256x256/apps/"
- cp -f "${OUT_DIR}/icons/com.fluorine.manager.metainfo.xml" "${APPDIR}/usr/share/metainfo/"
-
- mkdir -p "${APPDIR}/usr/share/icons"
- for theme_dir in /usr/share/icons/*; do
- [ -d "${theme_dir}" ] || continue
- cp -a "${theme_dir}" "${APPDIR}/usr/share/icons/"
- done
- if [ -f "/usr/share/icons/default/index.theme" ]; then
- mkdir -p "${APPDIR}/usr/share/icons/default"
- cp -f "/usr/share/icons/default/index.theme" "${APPDIR}/usr/share/icons/default/"
- fi
-
- patchelf --force-rpath --set-rpath '$ORIGIN/../lib' "${APPDIR}/usr/bin/ModOrganizer-core"
- find "${APPDIR}/usr/bin/plugins" -name "*.so" -exec patchelf --force-rpath --set-rpath '$ORIGIN/../../lib' {} \; 2>/dev/null || true
- find "${APPDIR}/usr/lib" -name "*.so" -exec patchelf --force-rpath --set-rpath '$ORIGIN' {} \; 2>/dev/null || true
-
- cat > "${APPDIR}/AppRun" <<'APPRUN'
-#!/usr/bin/env bash
-set -euo pipefail
-SELF="$(readlink -f "$0")"
-HERE="$(cd "$(dirname "$SELF")" && pwd)"
-BIN="${HERE}/usr/bin"
-APPIMAGE_DIR="$(dirname "$(readlink -f "${APPIMAGE:-$0}")")"
-
-export FLUORINE_ORIG_LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}"
-export FLUORINE_ORIG_LD_PRELOAD="${LD_PRELOAD:-}"
-export FLUORINE_ORIG_PATH="${PATH}"
-export FLUORINE_ORIG_XDG_DATA_DIRS="${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
-export FLUORINE_ORIG_QT_PLUGIN_PATH="${QT_PLUGIN_PATH:-}"
-
-# Steam injects 32-bit gameoverlayrenderer.so via LD_PRELOAD — clear it.
-unset LD_PRELOAD
-
-# Suppress Qt debug logging by default (see comment in main launcher).
-: "${QT_LOGGING_RULES:=default.debug=false}"
-export QT_LOGGING_RULES
-
-export PATH="${BIN}:${PATH}"
-# Replace (not append) LD_LIBRARY_PATH — Steam game mode injects its runtime
-# libs which break Python/Qt. RPATH handles the binary's own deps.
-export LD_LIBRARY_PATH="${HERE}/usr/lib"
-
-export MO2_BASE_DIR="${APPIMAGE_DIR}"
-export MO2_PLUGINS_DIR="${BIN}/plugins"
-export MO2_LIBS_DIR="${BIN}/lib"
-
-unset PYTHONPATH PYTHONNOUSERSITE PYTHONHOME MO2_PYTHON_DIR
-
-export QT_PLUGIN_PATH="${HERE}/usr/plugins"
-export QT_QPA_PLATFORM_PLUGIN_PATH="${HERE}/usr/plugins/platforms"
-export XDG_DATA_DIRS="${HERE}/usr/share:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}"
-export QT_ICON_THEME_NAME="${QT_ICON_THEME_NAME:-breeze}"
-export QT_STYLE_OVERRIDE="${QT_STYLE_OVERRIDE:-Breeze}"
-export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:-KDE}"
-
-# Raise open file descriptor limit — large modlists with FUSE VFS
-# can easily exceed the default 1024
-ulimit -n 65536 2>/dev/null
-
-cd "${APPIMAGE_DIR}"
-exec "${BIN}/ModOrganizer-core" "$@"
-APPRUN
- chmod +x "${APPDIR}/AppRun"
-
- ln -sf usr/share/applications/com.fluorine.manager.desktop "${APPDIR}/com.fluorine.manager.desktop"
- ln -sf usr/share/icons/hicolor/256x256/apps/com.fluorine.manager.png "${APPDIR}/com.fluorine.manager.png"
- ln -sf usr/share/icons/hicolor/256x256/apps/com.fluorine.manager.png "${APPDIR}/.DirIcon"
-
- DEPLOY_DIR="/tmp/linuxdeploy-extract"
- if [ ! -d "${DEPLOY_DIR}" ]; then
- cd /opt/linuxdeploy
- ./linuxdeploy-x86_64.AppImage --appimage-extract >/dev/null
- mv squashfs-root "${DEPLOY_DIR}"
- chmod +x "${DEPLOY_DIR}/AppRun"
- fi
-
- export ARCH=x86_64
- "${DEPLOY_DIR}/AppRun" \
- --appdir "${APPDIR}" \
- --output appimage \
- --desktop-file "${APPDIR}/usr/share/applications/com.fluorine.manager.desktop" \
- --icon-file "${APPDIR}/usr/share/icons/hicolor/256x256/apps/com.fluorine.manager.png" \
- 2>&1 || {
- echo "WARNING: linuxdeploy AppImage generation failed"
- }
-
- APPIMAGE_FILE=$(ls -1 Fluorine*.AppImage 2>/dev/null || ls -1 *.AppImage 2>/dev/null || true)
- if [ -n "${APPIMAGE_FILE}" ]; then
- mv "${APPIMAGE_FILE}" "/src/build/"
- echo "AppImage: /src/build/${APPIMAGE_FILE}"
- ls -lh "/src/build/"*.AppImage
- fi
-}
-
# ── Execute requested build mode ──
case "${BUILD_MODE}" in
tarball)
@@ -785,17 +665,12 @@ case "${BUILD_MODE}" in
installer)
build_installer
;;
- appimage)
- build_appimage
- ;;
all)
build_tarball
- if [ -d /opt/linuxdeploy ]; then
- build_appimage
- fi
+ build_installer
;;
*)
- echo "ERROR: Unknown BUILD_MODE '${BUILD_MODE}'. Use: tarball, installer, appimage, all"
+ echo "ERROR: Unknown BUILD_MODE '${BUILD_MODE}'. Use: tarball, installer, all"
exit 1
;;
esac
@@ -805,4 +680,4 @@ echo "=== Build Summary ==="
du -sh "${OUT_DIR}"/*/ "${OUT_DIR}"/ModOrganizer-core 2>/dev/null | sort -rh
echo ""
echo "Build outputs:"
-ls -dh /src/build/fluorine-manager/ /src/build/fluorine-manager.bin /src/build/*.AppImage 2>/dev/null || echo " (none found)"
+ls -dh /src/build/fluorine-manager/ /src/build/fluorine-manager.bin 2>/dev/null || echo " (none found)"
diff --git a/libs/archive/src/library.h b/libs/archive/src/library.h
index a0656f2..e182f08 100644
--- a/libs/archive/src/library.h
+++ b/libs/archive/src/library.h
@@ -94,7 +94,7 @@ private:
exeDir = exeDir.substr(0, slash);
}
- // AppImage: check MO2_LIBS_DIR env var first (writable dir next to AppImage)
+ // Honor MO2_LIBS_DIR (set by the fluorine-manager launcher) first.
std::string envLibs;
const char* envVal = std::getenv("MO2_LIBS_DIR");
if (envVal && envVal[0] != '\0') {
diff --git a/libs/plugin_python/src/proxy/proxypython.cpp b/libs/plugin_python/src/proxy/proxypython.cpp
index 6e4acad..56d9dae 100644
--- a/libs/plugin_python/src/proxy/proxypython.cpp
+++ b/libs/plugin_python/src/proxy/proxypython.cpp
@@ -167,9 +167,10 @@ bool ProxyPython::init(IOrganizer* moInfo)
std::filesystem::path{IOrganizer::getPluginDataPath().toStdWString()}};
m_Runner->initialize(paths);
#else
- // On Linux, rely on the unpacked stdlib from PYTHONHOME (AppRun sets
- // MO2_PYTHON_DIR/PYTHONHOME). Do not prepend pythoncore.zip here:
- // forcing zipimport can fail when zlib is unavailable in embedded mode.
+ // On Linux, rely on the unpacked stdlib from PYTHONHOME (the
+ // fluorine-manager launcher sets MO2_PYTHON_DIR/PYTHONHOME). Do not
+ // prepend pythoncore.zip here: forcing zipimport can fail when zlib
+ // is unavailable in embedded mode.
std::vector<fs::path> paths{
libpath,
std::filesystem::path{IOrganizer::getPluginDataPath().toStdWString()}};
@@ -184,9 +185,9 @@ bool ProxyPython::init(IOrganizer* moInfo)
}
}
- // Allow portable/AppImage builds to ship Python packages next to the app.
- // MO2_PYTHON_DIR (set by AppRun) points to the writable python/ dir
- // next to the AppImage; fall back to <exe_dir>/python.
+ // Allow portable builds to ship Python packages next to the app.
+ // MO2_PYTHON_DIR (set by the fluorine-manager launcher) points to the
+ // writable python/ dir; fall back to <exe_dir>/python.
fs::path pythonDir;
const char* envPy = std::getenv("MO2_PYTHON_DIR");
if (envPy && envPy[0] != '\0') {
diff --git a/libs/uibase/src/utility.cpp b/libs/uibase/src/utility.cpp
index 40774ae..4304561 100644
--- a/libs/uibase/src/utility.cpp
+++ b/libs/uibase/src/utility.cpp
@@ -396,16 +396,16 @@ namespace shell
}
}
- // Launch an external host process with AppImage environment variables
- // cleaned up, so tools like xdg-open/kde-open use the host's own
- // libraries and Qt plugins instead of the bundled ones.
+ // Launch an external host process with bundled-runtime environment
+ // variables cleaned up, so tools like xdg-open/kde-open use the host's
+ // own libraries and Qt plugins instead of the ones we ship.
static bool startDetachedHostProcess(const QString& program,
const QStringList& args)
{
QProcess proc;
auto env = QProcessEnvironment::systemEnvironment();
- // Restore original LD_LIBRARY_PATH (saved by AppRun.sh)
+ // Restore original LD_LIBRARY_PATH (saved by the fluorine-manager launcher).
if (env.contains(QStringLiteral("FLUORINE_ORIG_LD_LIBRARY_PATH"))) {
const auto orig = env.value(QStringLiteral("FLUORINE_ORIG_LD_LIBRARY_PATH"));
if (orig.isEmpty())
@@ -414,7 +414,8 @@ namespace shell
env.insert(QStringLiteral("LD_LIBRARY_PATH"), orig);
}
- // Remove AppImage-specific Qt/path variables that would confuse host apps
+ // Remove Qt/path variables pointing at our bundled runtime that would
+ // confuse host apps.
env.remove(QStringLiteral("QT_PLUGIN_PATH"));
env.remove(QStringLiteral("QT_QPA_PLATFORM_PLUGIN_PATH"));
env.remove(QStringLiteral("QTWEBENGINEPROCESS_PATH"));
diff --git a/src/src/envshortcut.cpp b/src/src/envshortcut.cpp
index 60cd33a..60786d2 100644
--- a/src/src/envshortcut.cpp
+++ b/src/src/envshortcut.cpp
@@ -17,17 +17,11 @@
namespace env
{
-// Returns the path to the AppImage file itself, or falls back to the
-// running binary if not running from an AppImage.
-static QString appImageOrBinary()
+// Prefer the fluorine-manager launcher script over the bare ModOrganizer-core
+// binary — the launcher sets up bundled library paths, Qt plugin paths, etc.
+// Without it, shortcuts fail on systems that don't have all deps in PATH.
+static QString launcherOrBinary()
{
- QString appImage = QProcessEnvironment::systemEnvironment().value("APPIMAGE");
- if (!appImage.isEmpty() && QFile::exists(appImage)) {
- return appImage;
- }
- // Prefer the fluorine-manager launcher script over the bare ModOrganizer-core
- // binary — the launcher sets up bundled library paths, Qt plugin paths, etc.
- // Without it, shortcuts fail on systems that don't have all deps in PATH.
const QString appDir = QCoreApplication::applicationDirPath();
const QString launcher = appDir + "/fluorine-manager";
if (QFile::exists(launcher)) {
@@ -281,16 +275,10 @@ static QImage extractIconFromExe(const QString& exePath)
return img;
}
-// Return the path to the bundled Fluorine icon inside the AppImage,
-// or the installed hicolor copy. Empty string if neither exists.
+// Return the path to the installed hicolor copy of the Fluorine icon.
+// Empty string if not present.
static QString bundledFluorineIcon()
{
- QString const appDir = QProcessEnvironment::systemEnvironment().value("APPDIR");
- if (!appDir.isEmpty()) {
- QString bundled = appDir + "/usr/share/icons/hicolor/256x256/apps/com.fluorine.manager.png";
- if (QFile::exists(bundled))
- return bundled;
- }
QString hicolor = QDir::homePath() +
"/.local/share/icons/hicolor/256x256/apps/com.fluorine.manager.png";
if (QFile::exists(hicolor))
@@ -362,7 +350,7 @@ Shortcut::Shortcut(const Executable& exe) : Shortcut()
m_name = exe.title();
m_instanceName = i.displayName();
- m_target = appImageOrBinary();
+ m_target = launcherOrBinary();
// For portable instances, use the absolute directory path so MO2 can
// find it (line 595 in instancemanager.cpp handles abs path lookup).
diff --git a/src/src/mainwindow.cpp b/src/src/mainwindow.cpp
index ee32208..a928c6d 100644
--- a/src/src/mainwindow.cpp
+++ b/src/src/mainwindow.cpp
@@ -636,8 +636,8 @@ void MainWindow::resetActionIcons()
void MainWindow::resetButtonIcons()
{
- // Some icon-only QPushButtons can lose their icons after stylesheet repolish on
- // Linux/AppImage. Re-apply explicit resource icons to keep these controls visible.
+ // Some icon-only QPushButtons can lose their icons after stylesheet repolish
+ // on Linux. Re-apply explicit resource icons to keep these controls visible.
ui->listOptionsBtn->setIcon(QIcon::fromTheme(
"preferences-system", QIcon(":/MO/gui/settings")));
ui->openFolderMenu->setIcon(
diff --git a/src/src/moapplication.cpp b/src/src/moapplication.cpp
index 0c50509..aac387f 100644
--- a/src/src/moapplication.cpp
+++ b/src/src/moapplication.cpp
@@ -190,7 +190,7 @@ MOApplication::MOApplication(int& argc, char** argv) : QApplication(argc, argv)
TimeThis const tt("MOApplication()");
// Ensure the app name is always "ModOrganizer" regardless of the binary
- // filename (e.g. "ModOrganizer.bin" inside an AppImage).
+ // filename (settings/profile lookups key off this).
setApplicationName("ModOrganizer");
setDesktopFileName(QStringLiteral("com.fluorine.manager"));
setWindowIcon(QIcon(":/MO/gui/app_icon"));
diff --git a/src/src/nxmhandler_linux.cpp b/src/src/nxmhandler_linux.cpp
index 3c9ef2a..c7db527 100644
--- a/src/src/nxmhandler_linux.cpp
+++ b/src/src/nxmhandler_linux.cpp
@@ -198,13 +198,7 @@ void NxmHandlerLinux::registerHandler()
const QString wrapperPath = localBin + "/mo2-nxm-handler";
// Determine a stable executable path for the wrapper script.
- // QCoreApplication::applicationFilePath() returns the temp FUSE mount for
- // AppImages (/tmp/.mount_XXXXX/...) which changes every launch. Use the
- // APPIMAGE env var (the actual .AppImage file) when available.
- QString executable = qEnvironmentVariable("APPIMAGE");
- if (executable.isEmpty()) {
- executable = QCoreApplication::applicationFilePath();
- }
+ const QString executable = QCoreApplication::applicationFilePath();
const QString wrapper =
QString("#!/bin/sh\nexec \"%1\" nxm-handle \"$@\"\n").arg(executable);
diff --git a/src/src/prefixsetuprunner.cpp b/src/src/prefixsetuprunner.cpp
index cd2fcf4..a3f782f 100644
--- a/src/src/prefixsetuprunner.cpp
+++ b/src/src/prefixsetuprunner.cpp
@@ -564,18 +564,16 @@ QProcess* PrefixSetupRunner::buildWrappedProcess(
{
auto* proc = new QProcess(this);
- // Start from the system environment and clean AppImage vars.
+ // Start from the system environment and clean Fluorine bundling vars.
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
- // Remove AppImage/Fluorine vars that can confuse Wine.
+ // Remove Fluorine vars that can confuse Wine.
for (const char* var : {"QT_QPA_PLATFORM_PLUGIN_PATH", "MO2_PLUGINS_DIR",
- "MO2_LIBS_DIR", "MO2_PYTHON_DIR", "MO2_BASE_DIR",
- "APPIMAGE", "APPDIR", "OWD", "ARGV0",
- "APPIMAGE_ORIGINAL_EXEC", "DESKTOPINTEGRATION"}) {
+ "MO2_LIBS_DIR", "MO2_PYTHON_DIR", "MO2_BASE_DIR"}) {
env.remove(var);
}
- // Restore pre-AppImage environment if available.
+ // Restore pre-launcher environment if available.
auto restoreOrStrip = [&env](const QString& var, const QString& origVar) {
if (env.contains(origVar)) {
const QString orig = env.value(origVar);
@@ -719,7 +717,7 @@ int PrefixSetupRunner::runHostProcess(const QString& exe,
proc.setArguments(args);
proc.setProcessChannelMode(QProcess::MergedChannels);
- // Clean AppImage/Fluorine env so host tools find their system libraries.
+ // Clean Fluorine env so host tools find their system libraries.
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
auto restoreOrStrip = [&env](const QString& var, const QString& origVar) {
if (env.contains(origVar)) {
diff --git a/src/src/protonlauncher.cpp b/src/src/protonlauncher.cpp
index 9c6bba8..d8b1289 100644
--- a/src/src/protonlauncher.cpp
+++ b/src/src/protonlauncher.cpp
@@ -14,32 +14,22 @@
namespace
{
-// Restore the pre-AppImage environment for child processes (Proton, Wine).
-// The AppRun script saves FLUORINE_ORIG_* vars before
-// modifying PATH, LD_LIBRARY_PATH, etc. We restore from those saved values
-// so game processes get a clean host environment without AppImage library paths.
-void cleanAppImageEnv(QProcessEnvironment& env)
+// Restore the pre-launcher environment for child processes (Proton, Wine).
+// The fluorine-manager launcher script saves FLUORINE_ORIG_* before
+// modifying PATH, LD_LIBRARY_PATH, etc., so game processes get a clean
+// host environment without the bundled-library paths leaking through.
+void cleanFluorineEnv(QProcessEnvironment& env)
{
- // Remove Fluorine/AppImage-specific vars that should never leak to game processes.
+ // Remove Fluorine-specific vars that should never leak to game processes.
env.remove("QT_QPA_PLATFORM_PLUGIN_PATH");
env.remove("MO2_PLUGINS_DIR");
env.remove("MO2_LIBS_DIR");
env.remove("MO2_PYTHON_DIR");
env.remove("MO2_BASE_DIR");
- // AppImage runtime injects these — they can confuse Proton/Wine.
- env.remove("APPIMAGE");
- env.remove("APPDIR");
- env.remove("OWD");
- env.remove("ARGV0");
- env.remove("APPIMAGE_ORIGINAL_EXEC");
-
- env.remove("DESKTOPINTEGRATION");
-
- // Restore saved pre-AppImage values. AppRun sets FLUORINE_ORIG_* before
- // modifying PATH, LD_LIBRARY_PATH, etc. If those vars exist, use them to
- // restore the original host environment. If not (standalone/non-AppImage),
- // fall back to stripping known AppImage patterns.
+ // Restore saved pre-launcher values from FLUORINE_ORIG_*. If those
+ // vars are missing (e.g. someone invoked ModOrganizer-core directly),
+ // fall back to stripping bundled-runtime paths by pattern.
auto restoreOrStrip = [](const QString& var, const QString& origVar,
QProcessEnvironment& e) {
if (e.contains(origVar)) {
@@ -51,12 +41,11 @@ void cleanAppImageEnv(QProcessEnvironment& env)
}
e.remove(origVar);
} else {
- // Fallback: strip AppImage mount paths by pattern.
const QString value = e.value(var);
if (value.isEmpty()) return;
QStringList kept;
for (const QString& p : value.split(':')) {
- if (p.contains(".mount_Fluori") || p.contains("/fluorine/python")) {
+ if (p.contains("/fluorine/python")) {
continue;
}
kept.append(p);
@@ -76,7 +65,7 @@ void cleanAppImageEnv(QProcessEnvironment& env)
restoreOrStrip("XDG_DATA_DIRS", "FLUORINE_ORIG_XDG_DATA_DIRS", env);
restoreOrStrip("QT_PLUGIN_PATH", "FLUORINE_ORIG_QT_PLUGIN_PATH", env);
- MOBase::log::debug("cleanAppImageEnv: {} (LD_LIBRARY_PATH='{}')",
+ MOBase::log::debug("cleanFluorineEnv: {} (LD_LIBRARY_PATH='{}')",
hasOrigVars ? "restored from FLUORINE_ORIG_*" : "pattern-strip fallback",
env.value("LD_LIBRARY_PATH", "<unset>"));
}
@@ -577,7 +566,7 @@ bool ProtonLauncher::launchWithProton(qint64& pid) const
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.remove("PYTHONHOME");
- cleanAppImageEnv(env);
+ cleanFluorineEnv(env);
// Prepend fluorine's bin dir to PATH so the container sees our injected
// xrandr (steamrt4 ships without it; Proton-GE protonfixes require it).
@@ -819,7 +808,7 @@ bool ProtonLauncher::launchDirect(qint64& pid) const
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.remove("PYTHONHOME");
- cleanAppImageEnv(env);
+ cleanFluorineEnv(env);
for (auto it = m_wrapperEnvVars.cbegin(); it != m_wrapperEnvVars.cend(); ++it) {
env.insert(it.key(), it.value());
}
diff --git a/src/src/shared/appconfig.h b/src/src/shared/appconfig.h
index efbc169..e7e94b1 100644
--- a/src/src/shared/appconfig.h
+++ b/src/src/shared/appconfig.h
@@ -31,15 +31,13 @@ namespace AppConfig
#define APPPARAM(partype, parid, value) partype parid();
#include "appconfig.inc"
-// Returns the application base directory. On Linux, if the MO2_BASE_DIR
-// environment variable is set (e.g. by an AppImage wrapper) that value is
-// returned; otherwise falls back to QCoreApplication::applicationDirPath().
+// Returns the application base directory. Respects MO2_BASE_DIR if set
+// (the fluorine-manager launcher exports it), otherwise falls back to
+// QCoreApplication::applicationDirPath().
QString basePath();
-// Returns the directory containing MO2 plugins. On Linux, if the
-// MO2_PLUGINS_DIR environment variable is set (e.g. when plugins live
-// inside a read-only AppImage squashfs) that value is used; otherwise
-// falls back to basePath() + "/plugins".
+// Returns the directory containing MO2 plugins. Respects MO2_PLUGINS_DIR
+// if set, otherwise falls back to basePath() + "/plugins".
QString pluginsPath();
// Returns the directory containing bundled Linux libraries (7z.so, etc.).