How can my app detect a change to another app's window? - objective-c

In Cocoa on the Mac, I'd like to detect when a window belonging to another app is moved, resized, or repainted. How can I do this?

You would need to use the Accessibility APIs, which are plain-C, located inside the ApplicationServices framework. For instance:
First you create an application object:
AXUIElementRef app = AXUIElementCreateApplication( targetApplicationProcessID );
Then you get the window from this. You can request the window list and enumerate, or you can get the frontmost window (look in AXAttributeConstants.h for all the attribute names you'd use).
AXUIElementRef frontWindow = NULL;
AXError err = AXUIElementCopyAttributeValue( app, kAXMainWindowAttribute, &frontWindow );
if ( err != kAXErrorSuccess )
// it failed -- maybe no main window (yet)
Now you can request notification via a C callback function when a property of this window changes. This is a four-step process:
First you need a callback function to receive the notifications:
void MyAXObserverCallback( AXObserverRef observer, AXUIElementRef element,
CFStringRef notificationName, void * contextData )
{
// handle the notification appropriately
// when using ObjC, your contextData might be an object, therefore you can do:
SomeObject * obj = (SomeObject *) contextData;
// now do something with obj
}
Next you need an AXObserverRef, which manages the callback routine. This requires the same process ID you used to create the 'app' element above:
AXObserverRef observer = NULL;
AXError err = AXObserverCreate( applicationProcessID, MyObserverCallback, &observer );
if ( err != kAXErrorSuccess )
// handle the error
Having got your observer, the next step is to request notification of certain things. See AXNotificationConstants.h for the full list, but for window changes you'll probably only need these two:
AXObserverAddNotification( observer, frontWindow, kAXMovedNotification, self );
AXObserverAddNotification( observer, frontWindow, kAXResizedNotification, self );
Note that the last parameter there is passing an assumed 'self' object as the contextData. This is not retained, so it's important to call AXObserverRemoveNotification when this object goes away.
Having got your observer and added notification requests, you now want to attach the observer to your runloop so you can be sent these notifications in an asynchronous manner (or indeed at all):
CFRunLoopAddSource( [[NSRunLoop currentRunLoop] getCFRunLoop],
AXObserverGetRunLoopSource(observer),
kCFRunLoopDefaultMode );
AXUIElementRefs are CoreFoundation-style objects, so you need to use CFRelease() to dispose of them cleanly. For cleanliness here, for example, you would use CFRelease(app) once you've obtained the frontWindow element, since you'll no longer need the app.
A note about Garbage-Collection: To keep an AXUIElementRef as a member variable, declare it like so:
__strong AXUIElementRef frontWindow;
This instructs the garbage collector to keep track of this reference to it. When assigning it, for compatibility with GC and non-GC, use this:
frontWindow = (AXUIElementRef) CFMakeCollectable( CFRetain(theElement) );

Further research turned up "Quartz Display Services"
The interesting function for my needs is CGRegisterScreenRefreshCallback.

Related

Working with events for processing global hotkeys on Mac OS X

What I want:
I have a program running. When the program is in the tray and out of focus, I want to have a couple of global shortcuts set up to send messages to the program. What do I mean by "send messages"? Well, inside my program, all I want is to have an access to some flag, which would indicate the state the specified key-pair (fired or not). I would poll the flag in the loop and take a decision from there.
What I found:
System-wide hotkey for an application
system wide shortcut for Mac OS X
What I do not understand:
From the links above it looks like I have to pass a handler when registering a hotkey. On a hotkey press, OS calls the handler. It that right? What I do not understand is how in the world the system would call a handler inside my program if my program is running.
I think your main problem is that you do not understand how Mac programming was done in the days before objective C and Cocoa became the norm. Before that, most programming was done in C (or C++) using Carbon. This name was used for a library that was supposed to be a "carbon" copy of a more modern set of APIs during the transition between Mac OS (Classic) and Mac OS X.
Another thing you have to understand is that the registration of hotkeys as given in the examples you give above must be paired by a registration of a Carbon Event handler that will be invoked when you hit that hotkey combination.
That said, I think you should read this legacy document about the Carbon Event Manager:
https://developer.apple.com/legacy/library/documentation/Carbon/Conceptual/Carbon_Event_Manager/CarbonEvents.pdf
And pay particular attention to how Carbon Events are supposed to be registered. I particularly use:
OSStatus InstallEventHandler(EventTargetRef target,
EventHandlerUPP handlerProc,
UInt32 numTypes,
const EventTypeSpec* typeList,
void* userData,
EventHandlerRef* handlerRef);
The way I use it is that I made an objective C wrapper in which I basically do the following:
This is a part of a class, let's call it MyOwnEventHandler:
- (EventHandlerRef)handlerRef {
if ( handlerRef == nil ) {
NSAssert( InstallEventHandler(GetApplicationEventTarget(),
&EventHandler,
0,
nil,
self,
&handlerRef ) == noErr, #"handlerRef" );
}
return handlerRef;
}
// this is a Carbon callback that the OS invokes when your app gets
// a hotkey event that must be handled by you
OSStatus EventHandler( EventHandlerCallRef inHandler,
EventRef inEvent,
void* inUserData )
{
EventHotKeyID hotKeyID;
GetEventParameter( inEvent,
kEventParamDirectObject,
typeEventHotKeyID,
nil,
sizeof(EventHotKeyID),
nil,
&hotKeyID );
// use this to get your MyOwnEventHandler object back if need be
// the reason why we get this is because we passed self in InstallEventHandler
// in Carbon event callbacks you cannot access self directly
// because this is a C callback, not an objective C method
MyOwnEventHandler* handler = (MyOwnEventHandler *)inUserData;
// handle the hotkey here - I usually store the id of the EventHotKeyID struct
// in a objective C hotkey object to look up events in an array of registered hotkeys
return eventNotHandledErr; // return this error for other handlers to handle this event as well
}
// call this objective C wrapper method to register your Carbon Event handler
- (void)registerForGettingHotKeyEvents {
const EventTypeSpec kHotKeysEvent[] = {{ kEventClassKeyboard, kEventHotKeyPressed }};
AddEventTypesToHandler( [self handlerRef], GetEventTypeCount(kHotKeysEvent), kHotKeysEvent );
}
// call this objective C wrapper method to unregister your Carbon Event handler
- (void)unregisterFromGettingHotKeyEvents {
const EventTypeSpec kHotKeysEvent[] = {{ kEventClassKeyboard, kEventHotKeyPressed }};
RemoveEventTypesFromHandler( [self handlerRef], GetEventTypeCount(kHotKeysEvent), kHotKeysEvent );
}
I hope this helps. If you are stuck somewhere let me know and I will try to help you.

qt5 proxy model updating too soon before main model update is done

I have a setup with a main model (QStandardModel), a proxy model which changes the output of the DisplayRole, and a separate tableview displaying each model. Inside the main model data is a user role that stores a pointer to another QObject which is used by the proxy model to get the desired display value.
I'm running into problems when the object pointed to by that variable is deleted. I am handling deletion in the main model via the destroyed(QObject*) signal. Inside the slot, I search through the model looking for any items that are pointing to the object and delete the reference.
That part works fine on its own but I also have connected to the onDataChanged(...) signal of the proxy model, where I call resizeColumnsToContents() on the proxy model. This then calls the proxy's data() function. Here I check to see if the item has a pointer and, if it does, get some information from the object for display.
The result of all this becomes:
Object about to be deleted triggers destroyed(...) signal
Main model looks for any items using the deleted object and calls setData to remove the reference
Tableview catches onDataChanged signal for the proxy model and resizes columns
Proxy model's data(...) is called. It checks if the item in the main model has the object pointer and, if so, displays a value from the object. If not, it displays something else.
The problem is, at step 4 the item from the main model apparently still hasn't been deleted; the pointer address is still stored. The object the pointer was referencing, though, has been deleted by this point resulting in a segfault.
How can I fix my setup to make sure the main model is finished deleting pointer references before the proxy model tries to update?
Also, here is pseudo-code for the relevant sections:
// elsewhere
Object *someObject = new QObject();
QModelIndex index = mainModel->index(0,0);
mainModel->setData(index, someObject, ObjectPtrRole);
// do stuff
delete someObject; // Qt is actually doing this, I'm not doing it explicitly
// MainModel
void MainModel::onObjectDestroyed(QObject *obj)
{
// iterating over all model items
// if item has pointer to obj
item->setData(QVariant::fromValue(NULL), ObjectPtrRole));
}
// receives onDataChanged signal
void onProxyModelDataChanged(...)
{
ui->tblProxyView->reseizeColumnsToContents();
}
void ProxyModel::data(const QModelIndex &index, int role) const
{
QModelIndex srcIndex = mapToSource(index);
if(role == Qt::DisplayRole)
{
QVariant v = sourceModel()->data(srcIndex, ObjectPtrRole);
Object *ptr = qvariant_cast<Object*>(v);
if(ptr != NULL)
return ptr->getDisplayData();
else
return sourceModel->data(srcIndex, role);
}
}
The problem is ptr is not NULL, but the referenced object is deleted, at the time ProxyModel::data(...) is called so I end up with a segfault.
To avoid dangling pointer dereferences with instances of QObject, you can do one of two things:
Use object->deleteLater - the object will be deleted once the control returns to the event loop. Such functionality is also known as autorelease pools.
Use a QPointer. It will set itself to null upon deletion of the object, so you can check it before use.

How would you write fetching a collection the "Reactive Cocoa" way?

The client I'm building is using Reactive Cocoa with Octokit and so far it has been going very well. However now I'm at a point where I want to fetch a collection of repositories and am having trouble wrapping my head around doing this the "RAC way"
// fire this when an authenticated client is set
[[RACAbleWithStart([GHDataStore sharedStore], client)
filter:^BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
subscribeNext:^(OCTClient *client) {
[[[client fetchUserRepositories] deliverOn:RACScheduler.mainThreadScheduler]
subscribeNext:^(OCTRepository *fetchedRepo) {
NSLog(#" Received new repo: %#",fetchedRepo.name);
}
error:^(NSError *error) {
NSLog(#"Error fetching repos: %#",error.localizedDescription);
}];
} completed:^{
NSLog(#"Completed fetching repos");
}];
I originally assumed that -subscribeNext: would pass an NSArray, but now understand that it sends the message every "next" object returned, which in this case is an OCTRepository.
Now I could do something like this:
NSMutableArray *repos = [NSMutableArray array];
// most of that code above
subscribeNext:^(OCTRepository *fetchedRepo) {
[repos addObject:fetchedRepo];
}
// the rest of the code above
Sure, this works, but it doesn't seem to follow the functional principles that RAC enables. I'm really trying to stick to conventions here. Any light on capabilities of RAC/Octokit are greatly appreciated!
It largely depends on what you want to do with the repositories afterward. It seems like you want to do something once you have all the repositories, so I'll set up an example that does that.
// Watch for the client to change
RAC(self.repositories) = [[[[[RACAbleWithStart([GHDataStore sharedStore], client)
// Ignore clients that aren't authenticated
filter:^ BOOL (OCTClient *client) {
return client != nil && client.authenticated;
}]
// For each client, execute the block. Returns a signal that sends a signal
// to fetch the user repositories whenever a new client comes in. A signal of
// of signals is often used to do some work in response to some other work.
// Often times, you'd want to use `-flattenMap:`, but we're using `-map:` with
// `-switchToLatest` so the resultant signal will only send repositories for
// the most recent client.
map:^(OCTClient *client) {
// -collect will send a single value--an NSArray with all of the values
// that were send on the original signal.
return [[client fetchUserRepositories] collect];
}]
// Switch to the latest signal that was returned from the map block.
switchToLatest]
// Execute a block when an error occurs, but don't alter the values sent on
// the original signal.
doError:^(NSError *error) {
NSLog(#"Error fetching repos: %#",error.localizedDescription);
}]
deliverOn:RACScheduler.mainThreadScheduler];
Now self.repositories will change (and fire a KVO notification) whenever the repositories are updated from the client.
A couple things to note about this:
It's best to avoid subscribeNext: whenever possible. Using it steps outside of the functional paradigm (as do doNext: and doError:, but they're also helpful tools at times). In general, you want to think about how you can transform the signal into something that does what you want.
If you want to chain one or more pieces of work together, you often want to use flattenMap:. More generally, you want to start thinking about signals of signals--signals that send other signals that represent the other work.
You often want to wait as long as possible to move work back to the main thread.
When thinking through a problem, it's sometimes valuable to start by writing out each individual signal to think about a) what you have, b) what you want, and c) how to get from one to the other.
EDIT: Updated to address #JustinSpahrSummers' comment below.
There is a -collect operator that should do exactly what you're looking for.
// Collect all receiver's `next`s into a NSArray. nil values will be converted
// to NSNull.
//
// This corresponds to the `ToArray` method in Rx.
//
// Returns a signal which sends a single NSArray when the receiver completes
// successfully.
- (RACSignal *)collect;

AXObserverCallback element reference

I have an AXObserver registered like so:
AXObserverCreate(pid, axObserverCallback, &_observer);
This is the callback method:
static void axObserverCallback(AXObserverRef observer, AXUIElementRef elementRef, CFStringRef notification, void *self) { .. }
And this is how I add a notification:
AXObserverAddNotification(_observer, app, kAXFocusedWindowChangedNotification, self);
What I'm noticing is that the element added to the observer (for notifying) (in the above case "app") does not seem to correspond to the one set in the observer callback ("elementRef"). It looks like it's a copy? At least I can't compare them with == and if I NSLog them they show a different address.
Is there any way to compare AXUIElementRefs that are copies? (And is this the normal behavior to return a copy instead of the original?)
Like all Core Foundation objects, AXUIElementRefs should be compared using CFEqual(), not by checking pointer equality.

How to suppress console messages generated by CFUserNotificationDisplayAlert

If I call CFUserNotificationDisplayAlert() to display an alert box, it prints the following message in the console:
CFUserNotificationDisplayAlert: called from main application thread, will block waiting for a response.
I don't want this message printed. Is there any way to disable it? Or, is there a better way to go about this? Thanks!
CFUserNotificationDisplayAlert() is a convenience function that always blocks the main thread while waiting for user input. If you don't want to block the main thread, you'll have to create the CFUserNotification yourself and attach it to the main thread's runloop:
// First, add member variables in your class to store the user notification and runloop source, like this. You'll need to be able to access these variables later, from your callback method:
CFUserNotificationRef _userNotification;
CFRunLoopSourceRef _runLoopSource;
// When you want to show the alert, you will create it, create a runloop source for it, then attach the runloop source to the runloop:
_userNotification= CFUserNotificationCreate(... set this up the way you want to ...);
_runLoopSource = CFUserNotificationCreateRunLoopSource(NULL, userNotification, YourUserNotificationCallback, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopCommonModes);
// ...elsewhere, you'll need to define your callback function, something like this:
void YourUserNotificationCallback(CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
{
// Handle the user's input here.
...
// Release your notification and runloop source:
CFRunLoopRemoveSource(CFRunLoopGetMain(), _runLoopSource, kCFRunLoopCommonModes);
CFRelease(_runLoopSource);
CFRelease(_userNotification);
}