Search code examples
objective-cmacoscocoawindowmainwindow

Check if main window of application is open and loaded


Part of my app is opening applications and preserving the work space that they have opened in. My problem is checking if the main application window has opened so I can go and switch to the next work space. Normally, I could just check if A window if that app was open, but for some apps such as IntelliJ IDEA, there is a loading window that is not main, but still shows up in CGWindowListCopyWindowInfo(). I check if a window of the app is open, then check if it has a title. This works for the IntelliJ IDEA apps, but applications like calculator that have main window of the apps without a window title, the code goes into an infinite wait loop. I have googled around and have not come up with anything, and this is one of the last things that needs to be fixed in my app.

Will add code later (when I have my computer)

TL;DR:

I need to check if the main window of an app is open. (Apps like INtelliJ IDEA have a loading window, and the window needs to be preserved in a work space.


Solution

  • After much testing, I have come up with an answer if anyone else needs it.

    I used the accessibility framework, and the code is as follows:

    - (NSInteger)numberOfWindowsOpenFromApplicationWithPID:(NSInteger)PID
    {
        NSInteger count = 0; // For return.
        AXUIElementRef anApp = AXUIElementCreateApplication((pid_t)PID);
        CFTypeRef aChildren;
        AXUIElementCopyAttributeValue(anApp, kAXChildrenAttribute, &aChildren);
    
        SafeCFRelease(anApp);
        if (aChildren) {
            for (NSInteger i = 0; i < CFArrayGetCount(aChildren); i++) {
                AXUIElementRef aChild = CFArrayGetValueAtIndex(aChildren, i);
                // Get subrole
                CFTypeRef aSubrole;
                AXUIElementCopyAttributeValue(aChild, kAXSubroleAttribute, &aSubrole);
    
                NSString *aSubroleString = CFBridgingRelease(aSubrole);
                if (aSubroleString) {
                    // Not a menu :3
                    if ([aSubroleString isEqualToString:@"AXStandardWindow"]) {
                        count++;
                    }
                }
            }
    
            SafeCFRelease(aChildren);
        } else {
            return 0;
        }
        return count;
    }
    

    This code return the number of (main) windows open for an app with PID in the current workspace. I then get the AXApplication from the PID, and then I get the children from it. As seen in the Accessibility Inspector:

    inspector

    The children are the windows and the menubar. The main windows have a subrole of AXStandardWindow. My code loops through the children, and then checks the subrole.