diff --git a/docs/setup.md b/docs/setup.md index 50a2698..aac51b9 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -37,6 +37,38 @@ systemctl --user disable blitztext-linux ``` +## Compatibility matrix (diagnostic, not a support promise) + +BlitztextLinux is developed and tested on **Kubuntu (KDE Plasma, Wayland)**. +The systems below share the Ubuntu/Debian package base, so `install.sh` runs on +them, but only Kubuntu is systematically tested. This table documents expected +behavior and known risks — it is **not** an official support statement. + +| Target system | Expected session | Main risks | Status | +| :--- | :--- | :--- | :--- | +| Kubuntu 26.04, Plasma | Wayland | ydotool ≥ 1.0 via apt available; lowest risk | tested | +| Ubuntu 24.04, GNOME | Wayland | apt ydotool 0.1.x is client-only → no auto-paste (clipboard mode works); GNOME tray needs the AppIndicator extension | untested | +| Ubuntu 26.04, GNOME | Wayland | GNOME tray needs the AppIndicator extension; Python 3.13/3.14: torch/openai-whisper wheels may lag behind new Python releases | untested | +| Linux Mint 22.x, Cinnamon | X11 (default) | `xclip` is the required clipboard backend, not `wl-copy`; apt ydotool 0.1.x → no auto-paste | untested | +| Lubuntu 24.04, LXQt | X11 (default) | same as Mint: `xclip` required; Qt tray via StatusNotifier usually fine | untested | +| Lubuntu 26.04, LXQt | X11 or Wayland | session type decides the clipboard backend — run `scripts/verify.sh` to see which one applies | untested | + +Xfce and MATE are expected to behave like the X11 rows above, but are not +tracked here. + +What works everywhere, regardless of session type: + +- Transcription and clipboard copy (no `ydotool`, no `input` group needed). +- Window/tray operation without global hotkeys (`install.sh` offers this mode; + `BLITZTEXT_NO_HOTKEY=1 bash scripts/install.sh` selects it non-interactively). + +What is environment-dependent: + +- **Auto-paste** needs a working `ydotoold` (ydotool ≥ 1.0). +- **Global hotkeys** need evdev access via the `input` group. +- **Clipboard backend**: `wl-copy` on Wayland, `xclip` on plain X11. + `scripts/verify.sh` prints the detected session and which backend is required. + ## Desktop session notes BlitztextLinux is developed for KDE Plasma on Wayland, with X11 fallbacks where the diff --git a/scripts/verify.sh b/scripts/verify.sh index 358df52..6a11443 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -51,11 +51,41 @@ ydotoold_provider_exists() { command -v ydotoold >/dev/null 2>&1 || ydotool_user_service_exists } +# ─── Session-Erkennung ──────────────────────────────────────────────────────── +# Die App wählt das Clipboard-Backend nach Umgebung: wl-copy, wenn +# WAYLAND_DISPLAY gesetzt ist oder gar kein DISPLAY existiert; nur in einer +# reinen X11-Session (DISPLAY ohne WAYLAND_DISPLAY) ist xclip das Pflicht-Backend. +if [[ -z "${WAYLAND_DISPLAY:-}" && -n "${DISPLAY:-}" ]]; then + CLIPBOARD_REQUIRED="xclip" + CLIPBOARD_OPTIONAL="wl-copy" +else + CLIPBOARD_REQUIRED="wl-copy" + CLIPBOARD_OPTIONAL="xclip" +fi + +clipboard_install_hint() { + if [[ "$1" == "wl-copy" ]]; then + echo "sudo apt install wl-clipboard" + else + echo "sudo apt install xclip" + fi +} + echo "" echo -e "${BOLD}══════════════════════════════════════════════════════${RESET}" echo -e "${BOLD} BlitztextLinux — Abhängigkeits-Check${RESET}" echo -e "${BOLD} Verzeichnis: ${BLITZTEXT_DIR}${RESET}" echo -e "${BOLD}══════════════════════════════════════════════════════${RESET}" +echo "" +echo -e "${BOLD}── Session & Desktop ────────────────────────────────${RESET}" + +# Rein informativ — keine harten Entscheidungen anhand dieser Werte. +info "XDG_SESSION_TYPE: ${XDG_SESSION_TYPE:-nicht gesetzt}" +info "XDG_CURRENT_DESKTOP: ${XDG_CURRENT_DESKTOP:-nicht gesetzt}" +info "WAYLAND_DISPLAY: ${WAYLAND_DISPLAY:-nicht gesetzt}" +info "DISPLAY: ${DISPLAY:-nicht gesetzt}" +info "Clipboard-Backend für diese Session: ${CLIPBOARD_REQUIRED} (Pflicht), ${CLIPBOARD_OPTIONAL} (Fallback)" + echo "" echo -e "${BOLD}── Systembinaries ───────────────────────────────────${RESET}" @@ -66,25 +96,26 @@ else fail "parec nicht gefunden — 'sudo apt install pulseaudio-utils'" fi -# wl-copy (wl-clipboard) -if command -v wl-copy &>/dev/null; then - pass "wl-copy gefunden: $(command -v wl-copy)" +# Clipboard-Backend (Pflicht für die aktuelle Session) +if command -v "${CLIPBOARD_REQUIRED}" &>/dev/null; then + pass "${CLIPBOARD_REQUIRED} gefunden: $(command -v "${CLIPBOARD_REQUIRED}")" else - fail "wl-copy nicht gefunden — 'sudo apt install wl-clipboard'" + fail "${CLIPBOARD_REQUIRED} nicht gefunden — '$(clipboard_install_hint "${CLIPBOARD_REQUIRED}")'" fi -# xclip (X11 clipboard fallback) -if command -v xclip &>/dev/null; then - pass "xclip gefunden: $(command -v xclip)" +# Clipboard-Fallback (optional für die aktuelle Session) +if command -v "${CLIPBOARD_OPTIONAL}" &>/dev/null; then + pass "${CLIPBOARD_OPTIONAL} gefunden: $(command -v "${CLIPBOARD_OPTIONAL}")" else - warn "xclip nicht gefunden — X11-Clipboard-Fallback fehlt ('sudo apt install xclip')" + warn "${CLIPBOARD_OPTIONAL} nicht gefunden — Clipboard-Fallback fehlt ('$(clipboard_install_hint "${CLIPBOARD_OPTIONAL}")')" fi -# ydotool +# ydotool (optional: nur für Auto-Paste nötig, Clipboard-Kopie geht ohne) if command -v ydotool &>/dev/null; then pass "ydotool gefunden: $(command -v ydotool)" else - fail "ydotool nicht gefunden — 'sudo apt install ydotool'" + warn "ydotool nicht gefunden — Auto-Paste nicht verfügbar ('sudo apt install ydotool')" + warn " Transkription und Clipboard-Kopie funktionieren auch ohne ydotool" fi # ffmpeg @@ -186,6 +217,7 @@ elif groups "$(whoami)" 2>/dev/null | grep -qw "input"; then else warn "Benutzer NICHT in Gruppe 'input' — evdev-Hotkeys funktionieren nicht" warn " Behebung: sudo usermod -aG input \$USER (dann Re-Login)" + info " Fenster/Tray-Bedienung funktioniert auch ohne die Gruppe 'input'" fi echo "" diff --git a/tests/test_verify_script.py b/tests/test_verify_script.py index ef865c6..de295ee 100644 --- a/tests/test_verify_script.py +++ b/tests/test_verify_script.py @@ -1,10 +1,17 @@ from __future__ import annotations import os +import re import shutil import subprocess from pathlib import Path +_ANSI_RE = re.compile(r"\x1b\[[0-9;]*m") + + +def _strip_ansi(text: str) -> str: + return _ANSI_RE.sub("", text) + # NOTE: tests/conftest.py's autouse `_block_real_notifications` fixture patches # `subprocess.run` process-wide (it patches the shared `subprocess` module via # `app.notify.subprocess.run`). Resolving real tool paths must therefore use @@ -66,7 +73,13 @@ def _prepare_verify_script( return script_path, stub_bin -def _run_script(script_path: Path, home_dir: Path, runtime_dir: Path, stub_bin: Path) -> subprocess.CompletedProcess[str]: +def _run_script( + script_path: Path, + home_dir: Path, + runtime_dir: Path, + stub_bin: Path, + extra_env: dict[str, str] | None = None, +) -> subprocess.CompletedProcess[str]: env = os.environ.copy() env.update( { @@ -77,6 +90,8 @@ def _run_script(script_path: Path, home_dir: Path, runtime_dir: Path, stub_bin: "DISPLAY": "", } ) + if extra_env: + env.update(extra_env) proc = subprocess.Popen( [BASH, str(script_path)], cwd=str(script_path.parent), @@ -184,3 +199,78 @@ def test_verify_all_checked_binaries_present_removes_their_fail_lines(tmp_path: for tool in checked_tools: assert f"{tool} nicht gefunden" not in result.stdout assert f"{tool} gefunden" in result.stdout + + +def test_verify_prints_session_and_desktop_info(tmp_path: Path): + repo_root = Path(__file__).resolve().parents[1] + script_path, stub_bin = _prepare_verify_script(tmp_path, repo_root) + home_dir = tmp_path / "home" + runtime_dir = tmp_path / "runtime" + runtime_dir.mkdir(parents=True, exist_ok=True) + + result = _run_script( + script_path, + home_dir, + runtime_dir, + stub_bin, + extra_env={"XDG_SESSION_TYPE": "wayland", "XDG_CURRENT_DESKTOP": "KDE"}, + ) + stdout = _strip_ansi(result.stdout) + + assert "XDG_SESSION_TYPE: wayland" in stdout + assert "XDG_CURRENT_DESKTOP: KDE" in stdout + + +def test_verify_x11_session_requires_xclip_not_wl_copy(tmp_path: Path): + """In einer reinen X11-Session (Mint/Lubuntu-Default) ist xclip Pflicht.""" + repo_root = Path(__file__).resolve().parents[1] + script_path, stub_bin = _prepare_verify_script(tmp_path, repo_root, present_checked_tools=()) + home_dir = tmp_path / "home" + runtime_dir = tmp_path / "runtime" + runtime_dir.mkdir(parents=True, exist_ok=True) + + result = _run_script( + script_path, home_dir, runtime_dir, stub_bin, extra_env={"DISPLAY": ":0"} + ) + stdout = _strip_ansi(result.stdout) + + assert "[FAIL] xclip nicht gefunden" in stdout + assert "[WARN] wl-copy nicht gefunden" in stdout + assert "[FAIL] wl-copy nicht gefunden" not in stdout + + +def test_verify_wayland_session_requires_wl_copy_not_xclip(tmp_path: Path): + repo_root = Path(__file__).resolve().parents[1] + script_path, stub_bin = _prepare_verify_script(tmp_path, repo_root, present_checked_tools=()) + home_dir = tmp_path / "home" + runtime_dir = tmp_path / "runtime" + runtime_dir.mkdir(parents=True, exist_ok=True) + + result = _run_script( + script_path, + home_dir, + runtime_dir, + stub_bin, + extra_env={"WAYLAND_DISPLAY": "wayland-0", "DISPLAY": ":0"}, + ) + stdout = _strip_ansi(result.stdout) + + assert "[FAIL] wl-copy nicht gefunden" in stdout + assert "[WARN] xclip nicht gefunden" in stdout + assert "[FAIL] xclip nicht gefunden" not in stdout + + +def test_verify_missing_ydotool_is_warn_not_fail(tmp_path: Path): + """ydotool ist optional: ohne ydotool fehlt nur Auto-Paste, kein Hard-Fail.""" + repo_root = Path(__file__).resolve().parents[1] + script_path, stub_bin = _prepare_verify_script(tmp_path, repo_root, present_checked_tools=()) + home_dir = tmp_path / "home" + runtime_dir = tmp_path / "runtime" + runtime_dir.mkdir(parents=True, exist_ok=True) + + result = _run_script(script_path, home_dir, runtime_dir, stub_bin) + stdout = _strip_ansi(result.stdout) + + assert "[WARN] ydotool nicht gefunden" in stdout + assert "[FAIL] ydotool nicht gefunden" not in stdout + assert "funktionieren auch ohne ydotool" in stdout