We are working on a Qt project, and there is some Mac specific code that we need to add. We need to register for an event, in a sample program we did that by using:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(notificationHandler:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
Since we can use that directly in our mm file on Qt, we are taking the approach of doing something like:
MyClass::MyClass() : {
// do other setup ...
CFNotificationCenterAddObserver
(
CFNotificationCenterGetLocalCenter(),
this,
¬ificationHandler,
CFSTR("???"),
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately
);
}
whats the string for "NSWorkspaceDidDeactivateApplicationNotification"?? Or how do we attatch ourselves to this particular notification?
We tried NSGod's approach, but since no Objective-C code can be added in a .h with Qt, then we added a private member which its class is defined in the mm file, that containes the actual logic. like this:
SelectedStuffManager.h
class MacWrap;
class SelectedStuffManager
{
public:
....
doSomething();
MacWrap* d;
private:
....
};
SelectedStuffManager.mm
@class MDWorkspaceWatcher;
class MacWrap
{
public:
MacWrap();
~MacWrap();
void applicationDeactivated(NSNotification * notification);
SystemEventsApplication *systemApplication;
NSRunningApplication *runApp;
private:
MDWorkspaceWatcher *workspaceWatcher;
};
MacWrap::MacWrap() {
this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}
MacWrap::~MacWrap() {
[this->workspaceWatcher release];
}
void MacWrap::applicationDeactivated(NSNotification* notification)
{
// guardar el id del proceso para utilizarlo luego
runApp = [[notification userInfo] valueForKey:@"NSWorkspaceApplicationKey"];
NSString *systemEventsASppName = [runApp bundleIdentifier];
if( [ systemEventsASppName isNotEqualTo:@"com.yo.SelectedText"])
{
systemApplication = [SBApplication applicationWithBundleIdentifier:systemEventsASppName];
NSLog(@"Launched. %@",systemEventsASppName);
}
}
@interface MDWorkspaceWatcher : NSObject {
MacWrap *manager;
}
- (id)initWithMyClass:(MacWrap*)obj;
- (void)didDeactivateApp:(NSNotification *)notification; @end
@implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MacWrap*)obj {
if ((self = [super init])) {
manager = obj;
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(didDeactivateApp:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)didDeactivateApp:(NSNotification *)notification {
manager->applicationDeactivated(notification);
}
@end
SelectedStuffManager::SelectedStuffManager()
{
d = new MacWrap();
}
SelectedStuffManager::doSomething()
{
if ([[d->runApp localizedName] isEqualTo: @"something"]) --> here it fails, bad memory access
{
...
}
}
It seems like someone is freeing both runApp and systemApplication, so we get a null pointer or bad memory. How or why could this be happening?
I don't believe you can just do like you're hoping to. First of all, NSWorkspace
uses its own NSNotificationCenter
, which is different than the default NSNotificationCenter
returned with +defaultCenter
.
I don't believe there's a strict CF-equivalent to those NSWorkspace
calls. There are likely high-level Carbon-based equivalents, but they're not available in 64-bit, and should likely be avoided.
You should be able to accomplish what you want using a small Objective-C helper class to receive the notifications and forward them to your C++ class like in the following code:
EDIT: updated to remove any Objective-C from header files. Just use generic void *
pointers, which you can cast inside the .mm file.
.h:
//@class MDWorkspaceWatcher;
class MyClass {
private:
// MDWorkspaceWatcher *workspaceWatcher;
void *workspaceWatcher;
public:
MyClass();
~MyClass();
// const void didActivateApp(NSNotification *notification) const;
// const void didDeactivateApp(NSNotification *notification) const;
const void didActivateApp(void *anNSnotification) const;
const void didDeactivateApp(void *anNSnotification) const;
};
.mm:
Objective-C part:
@interface MDWorkspaceWatcher : NSObject {
MyClass *myClass;
}
- (id)initWithMyClass:(MyClass *)aMyClass;
@end
@implementation MDWorkspaceWatcher
- (id)initWithMyClass:(MyClass *)aMyClass {
if ((self = [super init])) {
myClass = aMyClass;
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(didActivateApp:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:@selector(didDeactivateApp:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
}
return self;
}
// very important:
- (void)dealloc {
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[super dealloc];
}
- (void)didActivateApp:(NSNotification *)notification {
myClass->didActivateApp(notification);
}
- (void)didDeactivateApp:(NSNotification *)notification {
myClass->didDeactivateApp(notification);
}
@end
C++ part:
MyClass::MyClass() {
this->workspaceWatcher = [[MDWorkspaceWatcher alloc] initWithMyClass:this];
}
MyClass::~MyClass() {
[(MDWorkspaceWatcher *)this->workspaceWatcher release];
}
MyClass::didActivateApp(void *anNSnotification) {
NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];
NSLog(@"appInfo == %@", appInfo);
}
MyClass::didDeactivateApp(void *anNSnotification) {
NSDictionary *appInfo = [(NSNotification *)anNSnotification userInfo];
NSLog(@"appInfo == %@", appInfo);
}
Note that an NSDictionary
is toll-free-bridged with CFDictionaryRef
, so you can simply cast the appInfo
NSDictionary
to a CFDictionaryRef
and then call the CF
functions to get at the contents of the dictionary if you prefer C over Objective-C.
Note that the notification center owns the appInfo
dictionary (in other words, it will be autoreleased), so you shouldn't call CFRelease()
on it like you might with CFCreate*
/CFCopy*
-related code.