"""Garantit une seule instance de WGSecure en cours d'exécution. Avec le lancement automatique au démarrage désormais activé par défaut ([[wg-autostart-reconnect]]), un second lancement manuel (icône du menu, raccourci double-cliqué) peut facilement se superposer à l'instance déjà présente en systray — deux fenêtres sur le même tunnel, deux timers de reconnexion concurrents. Le second lancement doit simplement réveiller le premier et s'arrêter. Implémenté via un socket local nommé (QLocalServer/QLocalSocket) : le mécanisme Qt standard pour ça, portable Linux/Windows sans dépendance supplémentaire. Le premier lancement écoute ; tout lancement suivant s'y connecte, signale sa présence, puis quitte. """ from PyQt6.QtNetwork import QLocalServer, QLocalSocket _SERVER_NAME = "wgsecure-single-instance" class SingleInstanceGuard: def __init__(self): self._server: QLocalServer | None = None self._on_activate = None def try_acquire(self) -> bool: """True si cette instance est la première et doit continuer. False si une autre tourne déjà : celle-ci lui a signalé sa présence et doit se terminer sans ouvrir la moindre fenêtre. """ probe = QLocalSocket() probe.connectToServer(_SERVER_NAME) if probe.waitForConnected(200): probe.write(b"activate") probe.waitForBytesWritten(200) probe.disconnectFromServer() return False # Un crash précédent peut laisser le socket enregistré sans qu'aucun # serveur ne l'écoute : listen() échouerait indéfiniment sinon. QLocalServer.removeServer(_SERVER_NAME) self._server = QLocalServer() self._server.newConnection.connect(self._handle_connection) self._server.listen(_SERVER_NAME) return True def on_activate_requested(self, callback) -> None: """`callback` (sans argument) est appelé quand un second lancement tente de démarrer — à connecter au réaffichage de la fenêtre.""" self._on_activate = callback def _handle_connection(self) -> None: sock = self._server.nextPendingConnection() if sock is None: return sock.readyRead.connect(lambda: self._consume(sock)) def _consume(self, sock: QLocalSocket) -> None: sock.readAll() if self._on_activate: self._on_activate() sock.disconnectFromServer()