This repository has been archived by the owner on Apr 9, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
/
undot.m
119 lines (97 loc) · 4.42 KB
/
undot.m
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#import <AppKit/AppKit.h>
static AXObserverRef observer = NULL;
CF_IMPLICIT_BRIDGING_ENABLED
void yeetWindow(AXUIElementRef window) {
CFTypeRef sizeValue;
if (AXUIElementCopyAttributeValue(window, kAXSizeAttribute, &sizeValue) != kAXErrorSuccess)
return;
CFAutorelease(sizeValue);
CGSize size;
if (!AXValueGetValue(sizeValue, kAXValueTypeCGSize, &size))
return;
if (!NSEqualSizes(size, CGSizeMake(8, 8)))
return;
CFTypeRef currentPositionValue;
if (AXUIElementCopyAttributeValue(window, kAXPositionAttribute, ¤tPositionValue) != kAXErrorSuccess)
return;
CFAutorelease(currentPositionValue);
CGPoint currentPosition;
if (!AXValueGetValue(currentPositionValue, kAXValueTypeCGPoint, ¤tPosition))
return;
const CGPoint newPosition = CGPointMake(-999999, -999999);
if (NSEqualPoints(currentPosition, newPosition))
return;
AXValueRef positionValue = AXValueCreate(kAXValueTypeCGPoint, &newPosition);
AXUIElementSetAttributeValue(window, kAXPositionAttribute, positionValue);
CFRelease(positionValue);
}
void handleNewWindow(AXUIElementRef window) {
AXObserverAddNotification(observer, window, kAXWindowMovedNotification, NULL);
AXObserverAddNotification(observer, window, kAXUIElementDestroyedNotification, NULL);
yeetWindow(window);
}
void handleNotification(AXObserverRef observer, AXUIElementRef element, CFStringRef notification, __unused void *refcon) {
if (CFEqual(notification, kAXWindowCreatedNotification)) {
handleNewWindow(element);
} else if (CFEqual(notification, kAXUIElementDestroyedNotification)) {
AXObserverRemoveNotification(observer, element, kAXWindowMovedNotification);
AXObserverRemoveNotification(observer, element, kAXUIElementDestroyedNotification);
}
yeetWindow(element);
}
CF_IMPLICIT_BRIDGING_DISABLED
@interface LaunchObserver: NSObject
@property(strong) NSString* bundleIdentifier;
@property(strong) void (^block)(NSRunningApplication *);
@end
@implementation LaunchObserver
+ (instancetype)launchObserverFor:(NSString *)bundleIdentifier block:(void(^)(NSRunningApplication *app))block
{
LaunchObserver *launchObserver = [LaunchObserver new];
launchObserver.bundleIdentifier = bundleIdentifier;
launchObserver.block = block;
[NSWorkspace.sharedWorkspace addObserver:launchObserver
forKeyPath:@"runningApplications"
options:NSKeyValueObservingOptionNew
context:NULL];
NSRunningApplication *currentlyRunningApplication = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundleIdentifier].firstObject;
if (currentlyRunningApplication)
block(currentlyRunningApplication);
return launchObserver;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
context:(void *)context
{
NSArray *newRunningApplications = change[NSKeyValueChangeNewKey];
for (NSRunningApplication *app in newRunningApplications) {
if ([self.bundleIdentifier isEqualToString:app.bundleIdentifier])
self.block(app);
}
}
@end
int main() {
if (!AXIsProcessTrustedWithOptions((CFDictionaryRef)@{
(NSString*)kAXTrustedCheckOptionPrompt: @YES,
}))
exit(0);
__unused LaunchObserver *launchObserver = [LaunchObserver launchObserverFor:@"com.apple.controlcenter" block:^(NSRunningApplication *app){
if (observer)
CFRelease(observer);
// This is the one little hack I'm leaving alone for right now: wait for Control Center to finish launching.
// I'm sure there's a good notification to wait for instead; poke me if you know.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
AXObserverCreate(app.processIdentifier, handleNotification, &observer);
AXUIElementRef controlCenter = AXUIElementCreateApplication(app.processIdentifier);
AXObserverAddNotification(observer, controlCenter, kAXWindowCreatedNotification, NULL);
CFRunLoopAddSource(CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(observer), kCFRunLoopDefaultMode);
CFTypeRef cfWindows;
AXUIElementCopyAttributeValue(controlCenter, kAXWindowsAttribute, &cfWindows);
NSArray *windows = CFBridgingRelease(cfWindows);
for (id window in windows)
handleNewWindow((AXUIElementRef)window);
});
}];
[NSApplication.sharedApplication run];
}