141 lines
4.4 KiB
Python
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)
|