Files
kadence/desktop/kadence_desktop/integrations/autostart.py
T
2026-10-18 18:02:05 +03:00

141 lines
4.4 KiB
Python

"""Starting with the session, the way the desktop expects.
Two mechanisms, because the two desktops disagree about which is canonical:
* a `.desktop` file in `~/.config/autostart` — the XDG standard, honoured by
KDE, GNOME, XFCE and friends;
* a **systemd user unit**, which is what you actually want on a modern box: it
restarts the app if it dies and keeps its logs in the journal.
Either can be switched on alone; the app only ever touches files under the
user's own config, never system paths.
"""
from __future__ import annotations
import logging
import os
import shutil
import subprocess
import sys
from pathlib import Path
log = logging.getLogger(__name__)
DESKTOP_FILE_NAME = "kadence.desktop"
SERVICE_NAME = "kadence-desktop.service"
def _autostart_dir() -> Path:
base = os.environ.get("XDG_CONFIG_HOME") or str(Path.home() / ".config")
path = Path(base) / "autostart"
path.mkdir(parents=True, exist_ok=True)
return path
def _systemd_user_dir() -> Path:
base = os.environ.get("XDG_CONFIG_HOME") or str(Path.home() / ".config")
path = Path(base) / "systemd" / "user"
path.mkdir(parents=True, exist_ok=True)
return path
def launch_command() -> str:
"""How to start this app again, whether installed or run from a checkout."""
executable = shutil.which("kadence-desktop")
if executable:
return executable
return f"{sys.executable} -m kadence_desktop"
# --------------------------------------------------------------------------
# XDG autostart
# --------------------------------------------------------------------------
def autostart_enabled() -> bool:
return (_autostart_dir() / DESKTOP_FILE_NAME).exists()
def set_autostart(enabled: bool, *, minimized: bool = True) -> None:
path = _autostart_dir() / DESKTOP_FILE_NAME
if not enabled:
path.unlink(missing_ok=True)
return
command = launch_command() + (" --minimized" if minimized else "")
path.write_text(
"\n".join(
[
"[Desktop Entry]",
"Type=Application",
"Name=Kadence",
"Comment=Pomodoro timer, task list and focus guard",
f"Exec={command}",
"Icon=kadence",
"Terminal=false",
"Categories=Utility;Office;",
"X-GNOME-Autostart-enabled=true",
"",
]
),
"utf-8",
)
log.info("autostart entry written to %s", path)
# --------------------------------------------------------------------------
# systemd user service
# --------------------------------------------------------------------------
def service_installed() -> bool:
return (_systemd_user_dir() / SERVICE_NAME).exists()
def service_enabled() -> bool:
if not shutil.which("systemctl"):
return False
result = subprocess.run(
["systemctl", "--user", "is-enabled", SERVICE_NAME],
capture_output=True,
text=True,
check=False,
)
return result.stdout.strip() == "enabled"
def install_service(*, enable: bool = True) -> str:
"""Write (and optionally enable) the user unit. Returns a status line."""
path = _systemd_user_dir() / SERVICE_NAME
path.write_text(
"\n".join(
[
"[Unit]",
"Description=Kadence desktop client",
"PartOf=graphical-session.target",
"After=graphical-session.target",
"",
"[Service]",
"Type=simple",
f"ExecStart={launch_command()} --minimized",
"Restart=on-failure",
"RestartSec=5",
"",
"[Install]",
"WantedBy=graphical-session.target",
"",
]
),
"utf-8",
)
if not shutil.which("systemctl"):
return f"unit written to {path} (systemctl not found)"
subprocess.run(["systemctl", "--user", "daemon-reload"], check=False)
if enable:
subprocess.run(["systemctl", "--user", "enable", SERVICE_NAME], check=False)
return f"unit written to {path}" + (" and enabled" if enable else "")
def remove_service() -> None:
if shutil.which("systemctl"):
subprocess.run(["systemctl", "--user", "disable", "--now", SERVICE_NAME], check=False)
(_systemd_user_dir() / SERVICE_NAME).unlink(missing_ok=True)
if shutil.which("systemctl"):
subprocess.run(["systemctl", "--user", "daemon-reload"], check=False)