Add/remove 工作区以编程方式更改为 mac
Add/remove workspace to mac programmatically
我有一个相当简单的问题。我将如何以编程方式add/remove 在任务控制中找到工作space。我已经看到这个 post here 关于以编程方式更改为另一个 space,我认为它可能与使用 CGSPrivate.h
的答案类似。我不需要担心私有框架,因为它不会出现在应用商店中。
编辑:我还看到了关于修改 com.apple.spaces.plist
和添加工作 space 的 post,但我不知道如何添加它,因为字典有 UUID和其他东西。
我能为您找到的最接近的解决方案是通过 apple 脚本实现此目的 - 请参阅 Ѻşeallү 在下面的回答 link:
How can I programmatically add a space to mission control?
在 Mission Control 中,这是 Dock 的辅助功能层次结构(在我的 Mac、OS X 10.10 上):
Role Position Title Value Description
AXList 632.000000, 1136.000000 (null) (null) (null)
AXDockItem 636.300049, 1138.000000 Finder (null) (null)
AXDockItem 688.300049, 1138.000000 Firefox (null) (null)
…
AXDockItem 1231.699951, 1138.000000 Trash (null) (null)
AXGroup 0.000000, 0.000000 (null) (null) (null)
AXGroup 20.000000, 227.000000 (null) (null) exposéd windows
AXList 0.000000, -2.000000 (null) (null) (null)
AXButton 592.000000, 20.000000 Desktop 1 (null) select Desktop 1
AXButton 864.000000, 20.000000 Desktop 2 (null) select Desktop 2
AXButton 1136.000000, 20.000000 Desktop 3 (null) select Desktop 3
AXButton 1824.000000, 20.000000 (null) (null) add desktop
工作区按钮的位置在删除按钮的中间。
我的测试应用:
- (AXUIElementRef)copyAXUIElementFrom:(AXUIElementRef)theContainer role:(CFStringRef)theRole atIndex:(NSInteger)theIndex {
AXUIElementRef aResultElement = NULL;
CFTypeRef aChildren;
AXError anAXError = AXUIElementCopyAttributeValue(theContainer, kAXChildrenAttribute, &aChildren);
if (anAXError == kAXErrorSuccess) {
NSUInteger anIndex = -1;
for (id anElement in (__bridge NSArray *)aChildren) {
if (theRole) {
CFTypeRef aRole;
anAXError = AXUIElementCopyAttributeValue((__bridge AXUIElementRef)anElement, kAXRoleAttribute, &aRole);
if (anAXError == kAXErrorSuccess) {
if (CFStringCompare(aRole, theRole, 0) == kCFCompareEqualTo)
anIndex++;
CFRelease(aRole);
}
}
else
anIndex++;
if (anIndex == theIndex) {
aResultElement = (AXUIElementRef)CFRetain((__bridge CFTypeRef)(anElement));
break;
}
}
CFRelease(aChildren);
}
return aResultElement;
}
- (IBAction)addWorkspace:(id)sender {
if (!AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt:@YES}))
return;
// type control-arrow-up
CGEventRef anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, true);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, false);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// click on the + button
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
CFTypeRef aGroup = [self copyAXUIElementFrom:anAXDockApp role:kAXGroupRole atIndex:0];
CFTypeRef aButton = [self copyAXUIElementFrom:aGroup role:kAXButtonRole atIndex:0];
CFRelease(aGroup);
if (aButton) {
AXError anAXError = AXUIElementPerformAction(aButton, kAXPressAction);
CFRelease(aButton);
}
// option up
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, false);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// type escape
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, true);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, false);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
}
- (IBAction)removeWorkspace:(id)sender {
if (!AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt:@YES}))
return;
// type control-arrow-up
CGEventRef anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, true);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, false);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// move mouse to the top of the screen
CGPoint aPoint;
aPoint.x = 10.0;
aPoint.y = 10.0;
anEvent = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, aPoint, 0);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// click at the location of the workspace
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
CFTypeRef aGroup = [self copyAXUIElementFrom:anAXDockApp role:kAXGroupRole atIndex:0];
CFTypeRef aList = [self copyAXUIElementFrom:aGroup role:kAXListRole atIndex:0];
CFRelease(aGroup);
CFTypeRef aButton = [self copyAXUIElementFrom:aList role:kAXButtonRole atIndex:1]; // index of the workspace
CFRelease(aList);
if (aButton) {
CFTypeRef aPosition;
AXError anAXError = AXUIElementCopyAttributeValue(aButton, kAXPositionAttribute, &aPosition);
if (anAXError == kAXErrorSuccess) {
AXValueGetValue(aPosition, kAXValueCGPointType, &aPoint);
CFRelease(aPosition);
// click
anEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, aPoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, aPoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
CFRelease(aButton);
}
}
// option up
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, false);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// type escape
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, true);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, false);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
}
与Willeke
类似,经过许多小时的代码我才能够完成此操作。这是我的代码,然后我会解释它对以后遇到此问题的人的作用。
In .h
My code is in AppDelegate (it is a menubar app).
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
...
// Workspace mutations vars
NSInteger workspacesToRemove; // Used in removing workspaces (as
loop)
}
// Define constants for sizes
#define kWORKSPACE_WIDTH 145
#define kWORKSPACE_HEIGHT 90
#define kWORKSPACE_SPACING 30
In .m
- (void)removeAllWorkspaces
{
NSDictionary *spacesPlist = [NSDictionary dictionaryWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.spaces.plist"]];
NSDictionary *spacesDisplayConfig = [spacesPlist objectForKey:[[spacesPlist allKeys] objectAtIndex:0]];
NSArray *spaceProperties = [spacesDisplayConfig objectForKey:@"Space Properties"];
NSInteger numberOfWorkspaces = [spaceProperties count];
NSLog(@"Number of workspaces: %ld", (long)numberOfWorkspaces);
// Set counter
workspacesToRemove = numberOfWorkspaces;
[self openMissionControl];
}
#pragma mark Open/Close step methods
- (void)openMissionControl
{
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
/*
*/
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
[self performSelector:@selector(moveMouseToUpdateMissionControl) withObject:nil afterDelay:1];
}
- (void)moveMouseToUpdateMissionControl
{
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
[self performSelector:@selector(moveMouseToCloseRightmostWorkspace) withObject:nil afterDelay:1];
}
- (void)moveMouseToCloseRightmostWorkspace
{
NSRect workspaceRect = [self rectForWorkspaces];
NSInteger closeX = (workspaceRect.origin.x + workspaceRect.size.width) - kWORKSPACE_WIDTH;
CGPoint closePoint = CGPointMake(closeX, workspaceRect.origin.y);
// Move mouse to point
CGEventRef mouseMove = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, closePoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouseMove);
CFRelease(mouseMove);
// Click
[self performSelector:@selector(clickMouseAtPoint:) withObject:[NSValue valueWithPoint:closePoint] afterDelay:2]; // Must be equal or greater 1.5
}
- (void)clickMouseAtPoint:(NSValue *)pointValue
{
CGPoint clickPoint = [pointValue pointValue];
// Click
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, clickPoint, kCGMouseButtonLeft));
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, clickPoint, kCGMouseButtonLeft));
workspacesToRemove--;
NSLog(@"%ld", (long)workspacesToRemove);
if (workspacesToRemove > 1) {
[self performSelector:@selector(moveMouseToCloseRightmostWorkspace) withObject:nil afterDelay:2];
} else {
[self performSelector:@selector(closeMissionControl) withObject:nil afterDelay:1];
}
}
- (void)closeMissionControl
{
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
}
#pragma mark
#pragma mark Adding Workspaces
- (void)openWorkspaces:(NSInteger)numberToOpen
{
// Open Mission control
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
/*
*/
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
[NSThread sleepForTimeInterval:2];
// Move mouse to point
CGEventRef mouseMove = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouseMove);
CFRelease(mouseMove);
for (NSInteger i = 0; i < numberToOpen; i++) {
// Add as many times as needed
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
[NSThread sleepForTimeInterval:1];
}
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
}
- (NSRect)rectForWorkspaces
{
NSDictionary *spacesPlist = [NSDictionary dictionaryWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.spaces.plist"]];
NSDictionary *spacesDisplayConfig = [spacesPlist objectForKey:[[spacesPlist allKeys] objectAtIndex:0]];
NSArray *spaceProperties = [spacesDisplayConfig objectForKey:@"Space Properties"];
NSInteger numberOfWorkspaces = [spaceProperties count];
NSInteger totalSpacing = (numberOfWorkspaces - 1) * kWORKSPACE_SPACING;
NSInteger totalLengthOfWorkspaces = numberOfWorkspaces * kWORKSPACE_WIDTH;
NSInteger totalRectWidth = totalSpacing + totalLengthOfWorkspaces;
NSRect workspaceRect = NSMakeRect(0, 0, totalRectWidth, kWORKSPACE_HEIGHT);
// Calculate center x or screen
NSInteger screenCenter = [[NSScreen mainScreen] frame].size.width / 2;
workspaceRect.origin.x = screenCenter - (workspaceRect.size.width / 2);
workspaceRect.origin.y = kWORKSPACE_SPACING;
return workspaceRect;
}
现在让我们一步步看代码
对于删除工作区,第一种方法 removeAllWorkspaces
是起点。
此代码从 com.apple.spaces.plist
文件中获取打开的工作区数量,然后设置变量 workspacesToRemove
。这个变量对于循环很重要,因为当有方法链(我这样称呼它们)时很难执行 for-loop
。
接下来,我通过执行 CGEvents
来调用打开任务控制的方法。然后我将鼠标移到屏幕的上角以确保工作区图标正确居中。
接下来,代码使用rectForWorkspaces
方法确定最右侧工作区的关闭按钮的位置。
这是一个非常简单的方法,但它是所发生事情的主要部分。
它计算了任务控制中工作区所在的矩形。这是代表它计算的内容的图像:
然后我将这个矩形减去 145(工作区图标宽度),然后在弹出的关闭按钮中单击它。
这部分循环直到关闭所有工作区(1 除外)。
FWI:它被分成许多方法的原因是我可以循环回到特定的方法并在延迟后执行这些方法而不会阻塞线程。
好复杂的结束!
添加工作区更容易。
只有一种方法(openWorkspaces:(NSInteger)numberToOpen
),打开任务控制,移动鼠标到位置,点击次数,直到添加完所有工作区。很简单的。
我有一个相当简单的问题。我将如何以编程方式add/remove 在任务控制中找到工作space。我已经看到这个 post here 关于以编程方式更改为另一个 space,我认为它可能与使用 CGSPrivate.h
的答案类似。我不需要担心私有框架,因为它不会出现在应用商店中。
编辑:我还看到了关于修改 com.apple.spaces.plist
和添加工作 space 的 post,但我不知道如何添加它,因为字典有 UUID和其他东西。
我能为您找到的最接近的解决方案是通过 apple 脚本实现此目的 - 请参阅 Ѻşeallү 在下面的回答 link:
How can I programmatically add a space to mission control?
在 Mission Control 中,这是 Dock 的辅助功能层次结构(在我的 Mac、OS X 10.10 上):
Role Position Title Value Description
AXList 632.000000, 1136.000000 (null) (null) (null)
AXDockItem 636.300049, 1138.000000 Finder (null) (null)
AXDockItem 688.300049, 1138.000000 Firefox (null) (null)
…
AXDockItem 1231.699951, 1138.000000 Trash (null) (null)
AXGroup 0.000000, 0.000000 (null) (null) (null)
AXGroup 20.000000, 227.000000 (null) (null) exposéd windows
AXList 0.000000, -2.000000 (null) (null) (null)
AXButton 592.000000, 20.000000 Desktop 1 (null) select Desktop 1
AXButton 864.000000, 20.000000 Desktop 2 (null) select Desktop 2
AXButton 1136.000000, 20.000000 Desktop 3 (null) select Desktop 3
AXButton 1824.000000, 20.000000 (null) (null) add desktop
工作区按钮的位置在删除按钮的中间。
我的测试应用:
- (AXUIElementRef)copyAXUIElementFrom:(AXUIElementRef)theContainer role:(CFStringRef)theRole atIndex:(NSInteger)theIndex {
AXUIElementRef aResultElement = NULL;
CFTypeRef aChildren;
AXError anAXError = AXUIElementCopyAttributeValue(theContainer, kAXChildrenAttribute, &aChildren);
if (anAXError == kAXErrorSuccess) {
NSUInteger anIndex = -1;
for (id anElement in (__bridge NSArray *)aChildren) {
if (theRole) {
CFTypeRef aRole;
anAXError = AXUIElementCopyAttributeValue((__bridge AXUIElementRef)anElement, kAXRoleAttribute, &aRole);
if (anAXError == kAXErrorSuccess) {
if (CFStringCompare(aRole, theRole, 0) == kCFCompareEqualTo)
anIndex++;
CFRelease(aRole);
}
}
else
anIndex++;
if (anIndex == theIndex) {
aResultElement = (AXUIElementRef)CFRetain((__bridge CFTypeRef)(anElement));
break;
}
}
CFRelease(aChildren);
}
return aResultElement;
}
- (IBAction)addWorkspace:(id)sender {
if (!AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt:@YES}))
return;
// type control-arrow-up
CGEventRef anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, true);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, false);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// click on the + button
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
CFTypeRef aGroup = [self copyAXUIElementFrom:anAXDockApp role:kAXGroupRole atIndex:0];
CFTypeRef aButton = [self copyAXUIElementFrom:aGroup role:kAXButtonRole atIndex:0];
CFRelease(aGroup);
if (aButton) {
AXError anAXError = AXUIElementPerformAction(aButton, kAXPressAction);
CFRelease(aButton);
}
// option up
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, false);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// type escape
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, true);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, false);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
}
- (IBAction)removeWorkspace:(id)sender {
if (!AXIsProcessTrustedWithOptions((__bridge CFDictionaryRef)@{(__bridge NSString *)kAXTrustedCheckOptionPrompt:@YES}))
return;
// type control-arrow-up
CGEventRef anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, true);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x7E, false);
CGEventSetFlags(anEvent, kCGEventFlagMaskControl);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// move mouse to the top of the screen
CGPoint aPoint;
aPoint.x = 10.0;
aPoint.y = 10.0;
anEvent = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, aPoint, 0);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// option down
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, true);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// click at the location of the workspace
NSArray *anArray = [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"];
AXUIElementRef anAXDockApp = AXUIElementCreateApplication([[anArray objectAtIndex:0] processIdentifier]);
CFTypeRef aGroup = [self copyAXUIElementFrom:anAXDockApp role:kAXGroupRole atIndex:0];
CFTypeRef aList = [self copyAXUIElementFrom:aGroup role:kAXListRole atIndex:0];
CFRelease(aGroup);
CFTypeRef aButton = [self copyAXUIElementFrom:aList role:kAXButtonRole atIndex:1]; // index of the workspace
CFRelease(aList);
if (aButton) {
CFTypeRef aPosition;
AXError anAXError = AXUIElementCopyAttributeValue(aButton, kAXPositionAttribute, &aPosition);
if (anAXError == kAXErrorSuccess) {
AXValueGetValue(aPosition, kAXValueCGPointType, &aPoint);
CFRelease(aPosition);
// click
anEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, aPoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, aPoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
CFRelease(aButton);
}
}
// option up
anEvent = CGEventCreateKeyboardEvent(NULL, 0x3A, false);
CGEventPost(kCGHIDEventTap, anEvent);
[NSThread sleepForTimeInterval:0.05];
// type escape
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, true);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
[NSThread sleepForTimeInterval:0.05];
anEvent = CGEventCreateKeyboardEvent(NULL, 0x35, false);
CGEventPost(kCGHIDEventTap, anEvent);
CFRelease(anEvent);
}
与Willeke
类似,经过许多小时的代码我才能够完成此操作。这是我的代码,然后我会解释它对以后遇到此问题的人的作用。
In .h
My code is in AppDelegate (it is a menubar app).
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
...
// Workspace mutations vars
NSInteger workspacesToRemove; // Used in removing workspaces (as
loop)
}
// Define constants for sizes
#define kWORKSPACE_WIDTH 145
#define kWORKSPACE_HEIGHT 90
#define kWORKSPACE_SPACING 30
In .m
- (void)removeAllWorkspaces
{
NSDictionary *spacesPlist = [NSDictionary dictionaryWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.spaces.plist"]];
NSDictionary *spacesDisplayConfig = [spacesPlist objectForKey:[[spacesPlist allKeys] objectAtIndex:0]];
NSArray *spaceProperties = [spacesDisplayConfig objectForKey:@"Space Properties"];
NSInteger numberOfWorkspaces = [spaceProperties count];
NSLog(@"Number of workspaces: %ld", (long)numberOfWorkspaces);
// Set counter
workspacesToRemove = numberOfWorkspaces;
[self openMissionControl];
}
#pragma mark Open/Close step methods
- (void)openMissionControl
{
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
/*
*/
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
[self performSelector:@selector(moveMouseToUpdateMissionControl) withObject:nil afterDelay:1];
}
- (void)moveMouseToUpdateMissionControl
{
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
[self performSelector:@selector(moveMouseToCloseRightmostWorkspace) withObject:nil afterDelay:1];
}
- (void)moveMouseToCloseRightmostWorkspace
{
NSRect workspaceRect = [self rectForWorkspaces];
NSInteger closeX = (workspaceRect.origin.x + workspaceRect.size.width) - kWORKSPACE_WIDTH;
CGPoint closePoint = CGPointMake(closeX, workspaceRect.origin.y);
// Move mouse to point
CGEventRef mouseMove = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, closePoint, kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouseMove);
CFRelease(mouseMove);
// Click
[self performSelector:@selector(clickMouseAtPoint:) withObject:[NSValue valueWithPoint:closePoint] afterDelay:2]; // Must be equal or greater 1.5
}
- (void)clickMouseAtPoint:(NSValue *)pointValue
{
CGPoint clickPoint = [pointValue pointValue];
// Click
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, clickPoint, kCGMouseButtonLeft));
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, clickPoint, kCGMouseButtonLeft));
workspacesToRemove--;
NSLog(@"%ld", (long)workspacesToRemove);
if (workspacesToRemove > 1) {
[self performSelector:@selector(moveMouseToCloseRightmostWorkspace) withObject:nil afterDelay:2];
} else {
[self performSelector:@selector(closeMissionControl) withObject:nil afterDelay:1];
}
}
- (void)closeMissionControl
{
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
}
#pragma mark
#pragma mark Adding Workspaces
- (void)openWorkspaces:(NSInteger)numberToOpen
{
// Open Mission control
CGEventSourceRef src =
CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
CGEventRef cntd = CGEventCreateKeyboardEvent(src, 0x3B, YES);
CGEventRef cntu = CGEventCreateKeyboardEvent(src, 0x3B, NO);
CGEventRef upd = CGEventCreateKeyboardEvent(src, 0x7E, YES);
CGEventRef upu = CGEventCreateKeyboardEvent(src, 0x7E, NO);
/*
*/
CGEventSetFlags(upd, kCGEventFlagMaskControl);
CGEventSetFlags(upu, kCGEventFlagMaskControl);
CGEventTapLocation loc = kCGHIDEventTap; // kCGSessionEventTap also works
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
[NSThread sleepForTimeInterval:2];
// Move mouse to point
CGEventRef mouseMove = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft);
CGEventPost(kCGHIDEventTap, mouseMove);
CFRelease(mouseMove);
for (NSInteger i = 0; i < numberToOpen; i++) {
// Add as many times as needed
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
CGEventPost(kCGHIDEventTap, CGEventCreateMouseEvent(NULL, kCGEventLeftMouseUp, CGPointMake([[NSScreen mainScreen] frame].size.width - 10, 10), kCGMouseButtonLeft));
[NSThread sleepForTimeInterval:1];
}
CGEventPost(loc, cntd);
CGEventPost(loc, upd);
CGEventPost(loc, upu);
CGEventPost(loc, cntu);
CFRelease(cntd);
CFRelease(cntu);
CFRelease(upd);
CFRelease(upu);
}
- (NSRect)rectForWorkspaces
{
NSDictionary *spacesPlist = [NSDictionary dictionaryWithContentsOfFile:[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Preferences/com.apple.spaces.plist"]];
NSDictionary *spacesDisplayConfig = [spacesPlist objectForKey:[[spacesPlist allKeys] objectAtIndex:0]];
NSArray *spaceProperties = [spacesDisplayConfig objectForKey:@"Space Properties"];
NSInteger numberOfWorkspaces = [spaceProperties count];
NSInteger totalSpacing = (numberOfWorkspaces - 1) * kWORKSPACE_SPACING;
NSInteger totalLengthOfWorkspaces = numberOfWorkspaces * kWORKSPACE_WIDTH;
NSInteger totalRectWidth = totalSpacing + totalLengthOfWorkspaces;
NSRect workspaceRect = NSMakeRect(0, 0, totalRectWidth, kWORKSPACE_HEIGHT);
// Calculate center x or screen
NSInteger screenCenter = [[NSScreen mainScreen] frame].size.width / 2;
workspaceRect.origin.x = screenCenter - (workspaceRect.size.width / 2);
workspaceRect.origin.y = kWORKSPACE_SPACING;
return workspaceRect;
}
现在让我们一步步看代码
对于删除工作区,第一种方法 removeAllWorkspaces
是起点。
此代码从 com.apple.spaces.plist
文件中获取打开的工作区数量,然后设置变量 workspacesToRemove
。这个变量对于循环很重要,因为当有方法链(我这样称呼它们)时很难执行 for-loop
。
接下来,我通过执行 CGEvents
来调用打开任务控制的方法。然后我将鼠标移到屏幕的上角以确保工作区图标正确居中。
接下来,代码使用rectForWorkspaces
方法确定最右侧工作区的关闭按钮的位置。
这是一个非常简单的方法,但它是所发生事情的主要部分。
它计算了任务控制中工作区所在的矩形。这是代表它计算的内容的图像:
然后我将这个矩形减去 145(工作区图标宽度),然后在弹出的关闭按钮中单击它。
这部分循环直到关闭所有工作区(1 除外)。
FWI:它被分成许多方法的原因是我可以循环回到特定的方法并在延迟后执行这些方法而不会阻塞线程。
好复杂的结束!
添加工作区更容易。
只有一种方法(openWorkspaces:(NSInteger)numberToOpen
),打开任务控制,移动鼠标到位置,点击次数,直到添加完所有工作区。很简单的。