Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use Cocoa's Accessibility API to detect that a window is brought to front?

I'm using the Accessibility API to detect when a certain application opens windows, closes windows, when the windows are moved or resized, or made main and/or focused. However the client app seems to move a window to front without an Accessibility API notification being fired.

How can my application detect when another application brings a window to front, without making it key?

I'm hoping to find a solution that works on OS X 10.4 and 10.5

More info: I'm using these statements at the moment. They work fine when the user manually selects a window to bring it to front. But it doens't work when the app itself is bringing the window to the front.

AXObserverAddNotification(observer, element, kAXMainWindowChangedNotification, 0);
AXObserverAddNotification(observer, element, kAXFocusedWindowChangedNotification, 0);
like image 913
Steve McLeod Avatar asked Dec 07 '08 08:12

Steve McLeod


1 Answers

I've been unable to subscribe to current window changes, but you can ask the accessibility API for the current application, and the current applications most foreground window.

Imagine you have a class called CurrentAppData, with the following data:

@interface CurrentAppData : NSObject {
    NSString* _title;
    AXUIElementRef _systemWide;
    AXUIElementRef _app;
    AXUIElementRef _window;
}

The code to find the current application looks something like this:

-(void) updateCurrentApplication {
   // get the currently active application  
   _app = (AXUIElementRef)[CurrentAppData
                           valueOfExistingAttribute:kAXFocusedApplicationAttribute 
                                        ofUIElement:_systemWide];

   // Get the window that has focus for this application
   _window = (AXUIElementRef)[CurrentAppData 
                              valueOfExistingAttribute:kAXFocusedWindowAttribute 
                                           ofUIElement:_app];

   NSString* appName = [CurrentAppData descriptionOfValue:_window
                                             beingVerbose:TRUE];    

   [self setTitle:appName];
}

In this example the _systemWide variable is initialized in the classes init function as: _system = AXUIElementCreateSystemWide();

The class function valueOfExistingAttribute looks like this:

// -------------------------------------------------------------------------------
//  valueOfExistingAttribute:attribute:element
//
//  Given a uiElement and its attribute, return the value of an accessibility
//  object's attribute.
// -------------------------------------------------------------------------------
+ (id)valueOfExistingAttribute:(CFStringRef)attribute ofUIElement:(AXUIElementRef)element
{
    id result = nil;
    NSArray *attrNames;

    if (AXUIElementCopyAttributeNames(element, (CFArrayRef *)&attrNames) == kAXErrorSuccess) 
    {
        if ( [attrNames indexOfObject:(NSString *)attribute] != NSNotFound
                &&
            AXUIElementCopyAttributeValue(element, attribute, (CFTypeRef *)&result) == kAXErrorSuccess
        ) 
        {
            [result autorelease];
        }
        [attrNames release];
    }
    return result;
}

The previous function was taken from the Apple UIElementInspector example, which is also a great resource for learning about the Accessibility API.

like image 151
Nick Haddad Avatar answered Sep 16 '22 17:09

Nick Haddad