ScanSlicer/scanslicer/entry.py

165 lines
5.5 KiB
Python

import argparse
import os
import sys
# parse_args: function definition for ScanSlicer behavior.
def parse_args(argv=None):
parser = argparse.ArgumentParser(
prog="scanslicer",
description="ScanSlicer Qt UI (Wayland/X11)",
)
parser.add_argument(
"path",
nargs="?",
help="Optional input directory containing images",
)
parser.add_argument(
"--backend",
choices=("auto", "wayland", "x11"),
default="auto",
help="Display backend selection (default: auto)",
)
return parser.parse_args(argv)
# _detect_backend_from_qpa_env: function definition for ScanSlicer behavior.
def _detect_backend_from_qpa_env():
qpa_value = os.environ.get("QT_QPA_PLATFORM", "").strip().lower()
if not qpa_value:
return None
tokens = [token.strip() for token in qpa_value.split(";") if token.strip()]
for token in tokens:
if token == "wayland":
return "wayland"
if token in ("xcb", "x11"):
return "x11"
return None
# resolve_runtime_backend: function definition for ScanSlicer behavior.
def resolve_runtime_backend(backend):
if backend in ("wayland", "x11"):
return backend
env_backend = _detect_backend_from_qpa_env()
if env_backend is not None:
return env_backend
session_type = os.environ.get("XDG_SESSION_TYPE", "").strip().lower()
if session_type == "wayland":
return "wayland"
if session_type == "x11":
return "x11"
runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "").strip()
wayland_display = os.environ.get("WAYLAND_DISPLAY", "").strip()
if runtime_dir and wayland_display and os.path.exists(os.path.join(runtime_dir, wayland_display)):
return "wayland"
if os.environ.get("DISPLAY", "").strip():
return "x11"
# Fall back to Wayland when auto-detection has no clear signal.
return "wayland"
# configure_qt_backend: function definition for ScanSlicer behavior.
def configure_qt_backend(backend):
if backend == "wayland":
os.environ["QT_QPA_PLATFORM"] = "wayland"
# Prevent accidental X11/XWayland routing in mixed environments.
os.environ.pop("DISPLAY", None)
elif backend == "x11":
os.environ["QT_QPA_PLATFORM"] = "xcb"
else:
raise RuntimeError(f"Unsupported backend '{backend}'.")
is_appimage = bool(os.environ.get("APPIMAGE"))
# In sandboxed desktop runtimes (Flatpak/Snap), prefer portal integration
# for file dialogs and desktop services.
sandboxed = bool(
os.environ.get("FLATPAK_ID")
or os.environ.get("SNAP")
)
if sandboxed:
os.environ.setdefault("QT_QPA_PLATFORMTHEME", "xdgdesktopportal")
elif is_appimage:
# AppImage is not a strict sandbox and some portal backends can emit:
# "Connection already associated with an application ID".
# Prefer GTK theme integration and avoid forced portal registration.
os.environ.setdefault("QT_QPA_PLATFORMTHEME", "gtk3")
os.environ.setdefault("QT_NO_XDG_DESKTOP_PORTAL", "1")
# In containers, prefer GTK platform theme so Qt can follow host theme
# settings when available (for example dark/light mode).
if os.path.exists("/.dockerenv"):
os.environ.setdefault("QT_QPA_PLATFORMTHEME", "gtk3")
# validate_wayland_environment: function definition for ScanSlicer behavior.
def validate_wayland_environment():
runtime_dir = os.environ.get("XDG_RUNTIME_DIR", "").strip()
display_name = os.environ.get("WAYLAND_DISPLAY", "").strip()
if not runtime_dir or not display_name:
raise RuntimeError(
"Missing Wayland runtime environment. "
"Set XDG_RUNTIME_DIR and WAYLAND_DISPLAY."
)
socket_path = os.path.join(runtime_dir, display_name)
if not os.path.exists(socket_path):
raise RuntimeError(
f"Wayland socket not found: {socket_path}"
)
# validate_x11_environment: function definition for ScanSlicer behavior.
def validate_x11_environment():
display_name = os.environ.get("DISPLAY", "").strip()
if not display_name:
raise RuntimeError(
"Missing X11 runtime environment. Set DISPLAY or use --backend wayland."
)
# main: function definition for ScanSlicer behavior.
def main(argv=None):
args = parse_args(sys.argv[1:] if argv is None else argv)
runtime_backend = None
try:
runtime_backend = resolve_runtime_backend(args.backend)
configure_qt_backend(runtime_backend)
if runtime_backend == "wayland":
validate_wayland_environment()
elif runtime_backend == "x11":
validate_x11_environment()
from .main import run_app
return run_app(args.path)
except Exception as exc:
selected = os.environ.get("QT_QPA_PLATFORM", "<unset>")
print(
f"Failed to start ScanSlicer Qt UI (backend={runtime_backend or args.backend}, QT_QPA_PLATFORM={selected}).",
file=sys.stderr,
)
print(str(exc), file=sys.stderr)
if runtime_backend == "x11":
print(
"X11 hint: ensure DISPLAY is set and an X server is available.",
file=sys.stderr,
)
else:
print(
"Wayland hint: ensure XDG_RUNTIME_DIR and WAYLAND_DISPLAY are set and the Wayland socket is mounted.",
file=sys.stderr,
)
return 1
if __name__ == "__main__":
raise SystemExit(main())