-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathinsert_text.py
More file actions
executable file
·102 lines (80 loc) · 2.73 KB
/
Copy pathinsert_text.py
File metadata and controls
executable file
·102 lines (80 loc) · 2.73 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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/env python3
"""Paste text while preserving the full macOS pasteboard payload."""
import os
import select
import sys
import time
import warnings
from objc import ObjCPointerWarning
from AppKit import NSPasteboard, NSPasteboardItem, NSStringPboardType
from Quartz import (
CGEventCreateKeyboardEvent,
CGEventPost,
CGEventSetFlags,
kCGEventFlagMaskCommand,
kCGHIDEventTap,
)
warnings.filterwarnings("ignore", category=ObjCPointerWarning)
RESTORE_TIMEOUT = float(os.environ.get("BOLO_INSERT_RESTORE_TIMEOUT", "0.35"))
def snapshot_pasteboard(pasteboard):
items = []
for item in pasteboard.pasteboardItems() or []:
data_by_type = {}
for item_type in item.types() or []:
data = item.dataForType_(item_type)
if data is not None:
data_by_type[item_type] = bytes(data)
if data_by_type:
items.append(data_by_type)
return items
def restore_pasteboard(pasteboard, snapshot):
pasteboard.clearContents()
restored = []
for data_by_type in snapshot:
item = NSPasteboardItem.alloc().init()
for item_type, data in data_by_type.items():
item.setData_forType_(data, item_type)
restored.append(item)
if restored:
pasteboard.writeObjects_(restored)
def post_cmd_v():
key_code_v = 9
down = CGEventCreateKeyboardEvent(None, key_code_v, True)
up = CGEventCreateKeyboardEvent(None, key_code_v, False)
CGEventSetFlags(down, kCGEventFlagMaskCommand)
CGEventSetFlags(up, kCGEventFlagMaskCommand)
CGEventPost(kCGHIDEventTap, down)
CGEventPost(kCGHIDEventTap, up)
def wait_for_pasteboard_to_change(pasteboard, temporary_change_count, text):
deadline = time.monotonic() + RESTORE_TIMEOUT
while time.monotonic() < deadline:
if pasteboard.changeCount() != temporary_change_count:
current = pasteboard.stringForType_(NSStringPboardType)
if current != text:
return True
time.sleep(0.025)
return False
def main():
text = sys.stdin.read()
if not text:
return 0
pasteboard = NSPasteboard.generalPasteboard()
snapshot = snapshot_pasteboard(pasteboard)
pasteboard.clearContents()
if not pasteboard.setString_forType_(text, NSStringPboardType):
return 2
temporary_change_count = pasteboard.changeCount()
post_cmd_v()
externally_changed = wait_for_pasteboard_to_change(
pasteboard,
temporary_change_count,
text,
)
if not externally_changed:
restore_pasteboard(pasteboard, snapshot)
ready, _, _ = select.select([sys.stdin], [], [], 0)
if ready:
sys.stdin.read()
return 0
if __name__ == "__main__":
raise SystemExit(main())