Skip to content

Instantly share code, notes, and snippets.

@liesen
Created May 15, 2009 12:07
Show Gist options
  • Save liesen/112185 to your computer and use it in GitHub Desktop.
Save liesen/112185 to your computer and use it in GitHub Desktop.
/* Cocoa accessibility framework for intercepting Spotlight user input. */
#import <Cocoa/Cocoa.h>
#import <AppKit/NSAccessibility.h>
static void searchFieldCallback(AXObserverRef observer,
AXUIElementRef element,
CFStringRef notification,
void *data) {
CFTypeRef subrole, value;
/**
* Check that the component in question is the search field.
* If so, print the current value.
*/
if (AXUIElementCopyAttributeValue(element, kAXSubroleAttribute, &subrole) == kAXErrorSuccess &&
CFGetTypeID(subrole) == CFStringGetTypeID() &&
CFStringCompare(kAXSearchFieldSubrole, (CFStringRef)subrole, 0) == kCFCompareEqualTo &&
AXUIElementCopyAttributeValue(element, kAXValueAttribute, &value) == kAXErrorSuccess &&
CFGetTypeID(value) == CFStringGetTypeID()) {
NSLog(@"> %@", (NSString *) value);
}
if (subrole) {
CFRelease(subrole);
}
if (value) {
CFRelease(value);
}
}
// Figure out the PID for a bundle identifier
static OSStatus FindProcessIDForBundleIdentifier(CFStringRef identifier, pid_t *pid) {
ProcessSerialNumber psn = {0, kNoProcess};
OSStatus status;
while ((status = GetNextProcess(&psn)) == noErr) {
CFDictionaryRef info = ProcessInformationCopyDictionary(
&psn, kProcessDictionaryIncludeAllInformationMask);
CFStringRef bundle;
if (CFDictionaryGetValueIfPresent(info, kCFBundleIdentifierKey, (const void **) &bundle) &&
CFStringCompare(bundle, identifier, 0) == kCFCompareEqualTo) {
return GetProcessPID(&psn, pid);
}
}
return status;
}
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (!AXAPIEnabled()) {
NSLog(@"The accessibility API isn't enabled!");
return 1;
}
if (argc != 2) {
NSLog(@"Usage: %s BUNDLE_IDENTIFIER\n", argv[0]);
return EXIT_FAILURE;
}
CFStringRef bundle_identifier = (CFStringRef)[NSString stringWithUTF8String:argv[1]];
pid_t process_pid;
OSStatus status = FindProcessIDForBundleIdentifier(bundle_identifier, &process_pid);
if (status != noErr) {
NSLog(@"Could not find PID for bundle identifier \"%@\"", (NSString *)bundle_identifier);
return status;
}
AXObserverRef observer;
AXError err = AXObserverCreate(process_pid, searchFieldCallback, &observer);
if (err != kAXErrorSuccess) {
NSLog(@"Failed to create observer");
return err;
}
// Sign up for notifications on the process ID for the specified bundle
AXUIElementRef element = AXUIElementCreateApplication(process_pid);
if (!element) {
NSLog(@"Couldn't register for element changes");
return EXIT_FAILURE;
}
err = AXObserverAddNotification(observer, element, kAXValueChangedNotification, NULL);
CFRelease(element);
if (err != kAXErrorSuccess) {
NSLog(@"Failed adding observer");
return err;
}
CFRunLoopAddSource(CFRunLoopGetMain(),
AXObserverGetRunLoopSource(observer),
kCFRunLoopCommonModes);
[NSApplication sharedApplication];
[NSApp run];
[pool drain];
}
/* Cocoa accessibility framework for intercepting Spotlight user input. */
#import <Cocoa/Cocoa.h>
#import <AppKit/NSAccessibility.h>
static void searchFieldCallback(AXObserverRef observer,
AXUIElementRef element,
CFStringRef notification,
void *data) {
CFTypeRef query1, query2;
/**
* Check that the component in question is the search field.
* If so, print the current value.
*/
if (AXUIElementCopyAttributeValue(element, kAXSubroleAttribute, &query1) == kAXErrorSuccess &&
CFGetTypeID(query1) == CFStringGetTypeID() &&
CFStringCompare(kAXSearchFieldSubrole, (CFStringRef) query1, 0) == kCFCompareEqualTo &&
AXUIElementCopyAttributeValue(element, kAXValueAttribute, &query2) == kAXErrorSuccess &&
CFGetTypeID(query2) == CFStringGetTypeID()) {
NSLog(@"> %@", (NSString *)query2);
}
if (query1)
CFRelease(query1);
if (query2)
CFRelease(query2);
}
// Figure out the PID for a bundle identifier
static OSStatus FindProcessIDForBundleIdentifier(CFStringRef identifier, pid_t *pid) {
ProcessSerialNumber psn = {0, kNoProcess};
OSStatus status;
while ((status = GetNextProcess(&psn)) == noErr) {
CFDictionaryRef info = ProcessInformationCopyDictionary(
&psn, kProcessDictionaryIncludeAllInformationMask);
CFStringRef bundle;
if (CFDictionaryGetValueIfPresent(info, kCFBundleIdentifierKey, (const void **) &bundle) &&
CFStringCompare(bundle, identifier, 0) == kCFCompareEqualTo) {
return GetProcessPID(&psn, pid);
}
}
return status;
}
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (!AXAPIEnabled()) {
NSLog(@"The accessibility API isn't enabled!");
return 1;
}
if (argc != 2) {
NSLog(@"Usage: %s BUNDLE_IDENTIFIER\n", argv[0]);
return EXIT_FAILURE;
}
CFStringRef bundle_identifier = (CFStringRef)[NSString stringWithUTF8String:argv[1]];
pid_t process_pid;
OSStatus status = FindProcessIDForBundleIdentifier(bundle_identifier, &process_pid);
if (status != noErr) {
NSLog(@"Could not find PID for bundle identifier \"%@\"", (NSString *)bundle_identifier);
return status;
}
AXObserverRef observer;
AXError err = AXObserverCreate(process_pid, searchFieldCallback, &observer);
if (err != kAXErrorSuccess) {
NSLog(@"Failed to create observer");
return err;
}
// Sign up for notifications on the process ID for the specified bundle
AXUIElementRef element = AXUIElementCreateApplication(process_pid);
if (!element) {
NSLog(@"Couldn't register for element changes");
return EXIT_FAILURE;
}
err = AXObserverAddNotification(observer, element, kAXValueChangedNotification, NULL);
CFRelease(element);
if (err != kAXErrorSuccess) {
NSLog(@"Failed adding observer");
return err;
}
CFRunLoopAddSource(CFRunLoopGetMain(),
AXObserverGetRunLoopSource(observer),
kCFRunLoopCommonModes);
[NSApplication sharedApplication];
[NSApp run];
[pool drain];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment