-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclipboard_sync.py
More file actions
75 lines (57 loc) · 1.94 KB
/
Copy pathclipboard_sync.py
File metadata and controls
75 lines (57 loc) · 1.94 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/env python3
import hashlib
import subprocess
import time
import sys
DISPLAYS = sys.argv[1:] if len(sys.argv) > 1 else [":0", ":1"]
if len(DISPLAYS) < 2:
print("Error: at least 2 displays required.")
sys.exit(1)
POLL_INTERVAL = 0.5
def get_clipboard(display: str) -> bytes:
try:
return subprocess.check_output(
["xclip", "-display", display, "-selection", "clipboard", "-o"],
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
return b""
def set_clipboard(display: str, data: bytes) -> None:
p = subprocess.Popen(
["xclip", "-display", display, "-selection", "clipboard"],
stdin=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
p.communicate(data)
def content_hash(data: bytes) -> str:
return hashlib.sha256(data).hexdigest()
def sync_loop() -> None:
prev: dict[str, bytes] = {d: b"" for d in DISPLAYS}
last_written: dict[str, str | None] = {d: None for d in DISPLAYS}
while True:
for src in DISPLAYS:
content = get_clipboard(src)
# Skip if empty, unchanged, or we wrote it ourselves
if not content:
continue
if content == prev[src]:
continue
if content_hash(content) == last_written[src]:
prev[src] = content
continue
# New content from this display — push to all others
h = content_hash(content)
for dst in DISPLAYS:
if dst != src:
set_clipboard(dst, content)
last_written[dst] = h
prev[dst] = content
prev[src] = content
time.sleep(POLL_INTERVAL)
if __name__ == "__main__":
print(f"Syncing clipboard across: {', '.join(DISPLAYS)}")
print("Press Ctrl+C to stop.\n")
try:
sync_loop()
except KeyboardInterrupt:
print("Stopped.")