Search code examples
swiftmacoskotlinkotlin-multiplatform

Kotlin Native use AXUIElement


I try to get all the application's windows.

fun main() = memScoped {
    NSWorkspace.sharedWorkspace.runningApplications()
        .map { it as NSRunningApplication }
        .filter { it.active }
        .forEach {
            val applicationRef = AXUIElementCreateApplication(it.processIdentifier)
            ...
        }
}

But i cant find the AXUIElement in kotlin native library but AXUIElementRef and AXUIElementRefVar. Many artical show the swift code to get AXUIElement, and i dont know how to change the code to kotlin.

The swift code like this:

if let pid = proToolsApp?.processIdentifier {
    var result = [AXUIElement]()
    var windowList: AnyObject? = nil // [AXUIElement]

    let appRef = AXUIElementCreateApplication(pid)
    if AXUIElementCopyAttributeValue(appRef, "AXWindows" as CFString, &windowList) == .success {
            result = windowList as! [AXUIElement]
    }

    var docRef: AnyObject? = nil
    if AXUIElementCopyAttributeValue(result.first!, "AXDocument" as CFString, &docRef) == .success {
        let result = docRef as! AXUIElement
        print("Found Document: \(result)")
        let filePath = result as! String
        print(filePath)
    }
}

Solution

  • AXUIElement is just a swift interpreted version of AXUIElementRef (https://developer.apple.com/documentation/applicationservices/axuielementref?language=objc)

    This should look something like this in kotlin:

    fun windowTest() = memScoped {
        NSWorkspace.sharedWorkspace.runningApplications()
            .map { it as NSRunningApplication }
            .filter { it.active }
            .forEach {
                val applicationRef = AXUIElementCreateApplication(it.processIdentifier)!!
                val output = alloc<CFTypeRefVar>()
                var result = AXUIElementCopyAttributeValue(
                    applicationRef,
                    attribute = kAXWindowsAttribute.toCFString(),
                    value = output.ptr,
                )
                if (result != kAXErrorSuccess) {
                    println("error $result")
                    return@forEach
                }
                val firstOutput = CFArrayGetValueAtIndex(output.value as CFArrayRef, 0)!!
                result = AXUIElementCopyAttributeValue(
                    element = firstOutput.reinterpret(),
                    attribute = kAXDocumentAttribute.toCFString(),
                    value = output.ptr,
                )
                println("$result ${output.value}")
            }
    }
    
    
    fun String.toCFString(): CFStringRef = CFBridgingRetain(this)!!.reinterpret()
    

    The second AXUIElementCopyAttributeValue returns -25212 = kAXErrorNoValue in my case, I'm not sure what's the problem, but I'm getting the same result in both swift/kotlin code