Repository navigation
Expand file tree
/
Copy pathsingle_instance.py
More file actions
57 lines (49 loc) · 1.64 KB
/
Copy pathsingle_instance.py
File metadata and controls
57 lines (49 loc) · 1.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
"""
Ensure only one instance of the app runs at a time.
The primary instance binds a local TCP socket. Any subsequent instance
connects to that socket, asks it to show its window, then exits.
"""
import socket
import sys
import threading
_PORT = 47832
_server_sock: socket.socket | None = None
def claim() -> bool:
"""
Try to become the primary instance.
Returns True if we are the primary instance, False if another is already running.
On False the existing instance has already been asked to show its window.
"""
global _server_sock
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0)
try:
sock.bind(("127.0.0.1", _PORT))
sock.listen(5)
_server_sock = sock
return True
except OSError:
# Another instance is running — signal it and give up
try:
c = socket.create_connection(("127.0.0.1", _PORT), timeout=2)
c.sendall(b"show\n")
c.close()
except Exception:
pass
return False
def start_listener(on_show: callable):
"""Start the background thread that listens for 'show' signals."""
if _server_sock is None:
return
t = threading.Thread(target=_listen, args=(_server_sock, on_show), daemon=True)
t.start()
def _listen(server_sock: socket.socket, on_show: callable):
while True:
try:
conn, _ = server_sock.accept()
data = conn.recv(16).decode(errors="ignore").strip()
if data == "show":
on_show()
conn.close()
except Exception:
break