Ryan
Ryan

Reputation: 41

Unable to move windows on Sonoma using private APIs in Swift

I'm trying to move windows around from other spaces to the current window and have wasted many hours not understanding why nothing is being moved..

I've tried various applications (hardcoding some screens now to test). I ensure window level is 0 and various other things, but the key thing is that the private API is not migrating the window. Common libs on the market today are using these calls on this Mac release, but I'm worried there is an additional step in the flow I'm missing.

Demo repo: https://github.com/Schachte/mac-os-graphics

I've tried thread sleeps between windows, etc. The main snippet is this:

static func addInitialRunningApplicationsWindows() {
    let cgsMainConnectionId = CGSMainConnectionID()
    let otherSpaces = [UInt64(213)]
    print("Other spaces found: \(otherSpaces)")
    
    guard otherSpaces.count > 0 else {
        print("No other spaces found")
        return
    }
    
    let windowsOnCurrentSpace = getWindowsInSpace([currentSpaceId])
    print("Windows on current space: \(windowsOnCurrentSpace)")
    
    let windowsOnOtherSpaces = getWindowsInSpace(otherSpaces)
    print("Windows on other spaces: \(windowsOnOtherSpaces)")
    
    let windowsOnlyOnOtherSpaces = Array(Set(windowsOnOtherSpaces).subtracting(windowsOnCurrentSpace))
    print("Windows to move: \(windowsOnlyOnOtherSpaces)")
    
    guard windowsOnlyOnOtherSpaces.count > 0 else {
        print("No windows to move")
        return
    }
    
    // Split windows into chunks to avoid overwhelming the API
    let chunkSize = 5
    let windowChunks = stride(from: 0, to: windowsOnlyOnOtherSpaces.count, by: chunkSize).map {
        Array(windowsOnlyOnOtherSpaces[$0..<min($0 + chunkSize, windowsOnlyOnOtherSpaces.count)])
    }
    
    for chunk in windowChunks {
        print("Processing chunk of \(chunk.count) windows")
        
        // First add windows to the current space
        CGSAddWindowsToSpaces(cgsMainConnectionId,
                             chunk as NSArray,
                             [currentSpaceId] as NSArray)
        
        // Give the system a moment to process
        Thread.sleep(forTimeInterval: 0.1)
        
        // Then remove from other spaces
        for spaceId in otherSpaces {
            CGSRemoveWindowsFromSpaces(cgsMainConnectionId,
                                     chunk as NSArray,
                                     [spaceId] as NSArray)
            
            // Give the system a moment to process
            Thread.sleep(forTimeInterval: 0.1)
        }
        
        // Verify the move for this chunk
        let windowsStillOnOtherSpaces = getWindowsInSpace(otherSpaces)
        let unmovedInChunk = chunk.filter { windowsStillOnOtherSpaces.contains($0) }
        
        if unmovedInChunk.count > 0 {
            print("Warning: Failed to move \(unmovedInChunk.count) windows in current chunk")
            
            // Try alternative method for unmoved windows
            for windowId in unmovedInChunk {
                if canMoveWindow(windowId) {
                    print("Attempting alternative move for window \(windowId)")
                    CGSMoveWindowsToManagedSpace(cgsMainConnectionId,
                                               [windowId] as NSArray,
                                               currentSpaceId)
                    Thread.sleep(forTimeInterval: 0.1)
                }
            }
        }
    }

Output

5
Other spaces found: [213]
Windows on current space: [115, 99, 98, 94, 26, 100, 25, 27, 24, 22, 38, 12159, 10, 116, 942, 12493, 12557, 13406]
Windows on other spaces: [13749, 16723, 13747, 116, 13328]
Windows to move: [16723, 13749, 13747, 13328]
Processing chunk of 4 windows
Warning: Failed to move 4 windows in current chunk
Attempting alternative move for window 16723
Warning: Total failed moves: 4 windows
Unmoved window IDs: [16723, 13749, 13747, 13328]
5

Using inspiration from https://github.com/lwouis/alt-tab-macos

Thread sleeps, different windows, running as root, disabling SIP

Upvotes: 2

Views: 56

Answers (0)

Related Questions