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
#import <Cocoa/Cocoa.h>
static NSArray *orderedMembership(id raw) {
if (![raw isKindOfClass:NSArray.class] || ![raw count] || [raw count] > 64) return nil;
NSMutableSet *ids = [NSMutableSet set];
for (id value in raw) {
if (![value isKindOfClass:NSNumber.class] ||
CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID() ||
!([value doubleValue] > 0) ||
[value compare:@([value unsignedLongLongValue])] != NSOrderedSame ||
[ids containsObject:value]) return nil;
[ids addObject:value];
}
return [[ids allObjects] sortedArrayUsingSelector:@selector(compare:)];
}
static BOOL spaceMembershipMatches(id expected, id actual) {
NSArray *before = orderedMembership(expected), *current = orderedMembership(actual);
return before && current && [before isEqual:current];
}
static NSArray *visibleSpaces(NSArray *displays) {
if (!displays.count) return nil;
NSMutableSet *visible = [NSMutableSet set];
for (NSDictionary *display in displays) {
if (![display isKindOfClass:NSDictionary.class]) return nil;
NSDictionary *currentSpace = display[@"Current Space"];
if (![currentSpace isKindOfClass:NSDictionary.class]) return nil;
NSNumber *current = currentSpace[@"ManagedSpaceID"];
if (![current isKindOfClass:NSNumber.class] ||
CFGetTypeID((__bridge CFTypeRef)current) == CFBooleanGetTypeID() ||
!(current.doubleValue > 0) ||
[current compare:@(current.unsignedLongLongValue)] != NSOrderedSame) return nil;
[visible addObject:current];
}
return [[visible allObjects] sortedArrayUsingSelector:@selector(compare:)];
}
static BOOL inactiveUserSpace(NSArray *displays, NSNumber *destination) {
NSArray *visible = visibleSpaces(displays);
if (!visible || [visible containsObject:destination]) return NO;
for (NSDictionary *display in displays) {
for (NSDictionary *space in display[@"Spaces"]) {
if ([space[@"ManagedSpaceID"] isEqual:destination] &&
space[@"type"] && [space[@"type"] intValue] == 0) return YES;
}
}
return NO;
}
static BOOL catalogIdentityMatches(NSDictionary *row, NSDictionary *expected) {
CGRect frame;
NSDictionary *bounds = expected[@"bounds"];
NSDictionary *raw = row[(id)kCGWindowBounds];
if (!row || !bounds || ![raw isKindOfClass:NSDictionary.class] || !CGRectMakeWithDictionaryRepresentation(
(__bridge CFDictionaryRef)raw, &frame)) return NO;
return [row[(id)kCGWindowNumber] isEqual:expected[@"window_id"]] &&
[row[(id)kCGWindowOwnerPID] isEqual:expected[@"owner_pid"]] &&
[row[(id)kCGWindowName] isEqual:expected[@"title"]] &&
(NSInteger)frame.origin.x == [bounds[@"x"] integerValue] &&
(NSInteger)frame.origin.y == [bounds[@"y"] integerValue] &&
(NSInteger)frame.size.width == [bounds[@"width"] integerValue] &&
(NSInteger)frame.size.height == [bounds[@"height"] integerValue];
}