5

かなり簡単な質問があります。ミッション コントロールで見つかったワークスペースをプログラムで追加/削除するにはどうすればよいですか。プログラムで別のスペースに変更することについてのこの投稿をここでCGSPrivate.h見ました。プライベート フレームワークはアプリ ストアにないため、心配する必要はありません。

編集: ワークスペースの変更と追加に関する投稿も見ましたcom.apple.spaces.plistが、dict には UUID などがあるため、それを追加する方法がわかりません。

4

3 に答える 3

2

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);
}
于 2016-02-06T13:38:43.887 に答える
1

同様に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ファイルから取得し、変数を設定しますworkspacesToRemovefor-loopこの変数は、メソッド チェーンがある場合 (私が呼び出しているように) を実行するのが難しいため、ループにとって重要です。

次に、 を実行してミッション コントロールを開くメソッドを呼び出しますCGEvents。次に、マウスを画面の上隅に移動して、ワークスペース アイコンが適切に中央に配置されていることを確認します。

次に、コードはrectForWorkspacesメソッドを使用して、一番右のワークスペースの閉じるボタンの位置を決定します。

これは非常に単純な方法ですが、何が起こるかの主要部分です。

ワークスペースがミッション コントロール内にある場所の四角形を計算しました。計算内容を表す画像は次のとおりです。 Rect計算

次に、この rect から 145 (ワークスペース アイコンの幅) を引いて、表示された [閉じる] ボタンをクリックします。

この部分は、すべてのワークスペース (1 を除く) が閉じられるまでループします。

FWI: 多くのメソッドに分割されている理由は、特定のメソッドにループバックし、スレッドをブロックすることなく遅延後にメソッドを実行できるようにするためです。

やった複雑なクローズオーバー!

ワークスペースの追加ははるかに簡単です。

これは 1 つの方法 ( openWorkspaces:(NSInteger)numberToOpen) であり、ミッション コントロールを開き、マウスを所定の位置に移動し、すべてのワークスペースが追加されるまで何度もクリックします。とてもシンプルです。

于 2016-02-06T16:27:12.440 に答える
0

私が見つけた最も近い解決策は、Appleスクリプトを介してこれを達成することでした.

ミッション コントロールにスペースをプログラムで追加するにはどうすればよいですか?

于 2016-02-01T19:12:33.077 に答える