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);
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.
In Mac OS X, applications and windows are completely separate things, with applications containing windows; they are not formerly-the-same-thing as in Microsoft Windows. You need to detect the activation and deactivation of each application.
You'll do that by observing for kAXApplicationActivatedNotification
and kAXApplicationDeactivatedNotification
. The object of these notifications is the application being activated and deactivated. You'll also need to detect applications launching and quitting; you can do this using the Process Manager or NSWorkspace. Both of those APIs can give you a process ID, which you can use to create an AXApplication object.
Take a look at iChatStatusFromApplication example in the developer documentation. It's exactly what you need :)
What about NSAccessibilityFocusedUIElementChangedNotification?