Thursday, February 16, 2012

How to handle click on app icon in Mac OS X dock in Qt application.

Couple of days ago I faced with task when I need to handle a click on application icon in Dock application.
I had very good recommendations how to do it using Objective-C here (unfortunately in Russian). Big thanks for list of such beautiful tricks to its author. But there was one inconvenience.  I did not want to add Objective-C++ file (.mm) in my project.
And I found other very good article suite from Robin Mills.(See ObjCandCandC++.pdf)

As result I got the following working bunch of code WITHOUT usage Objective-C++ and it has allowed me to reach what I wanted.
The prototype in Objective-C was very short.

Main idea was as usual to write separate Objective-C class. It should be separate compilation unit and it can be available for the code in Qt/C++.

This is callback that should be declared and registered in order to handle a click on dock's icon .
void dockClickHandler(id self, SEL _cmd)

and registration of this callback in Objective-C. 
MyPrivate::MyPrivate() :

    Class cls = [[[NSApplication sharedApplication] delegate] class];
    if (!class_addMethod(cls, 
        (IMP) dockClickHandler, "v@:"))
       NSLog(@"MyPrivate::MyPrivate() : class_addMethod failed!");
void MyPrivate::emitClick()
    emit dockClicked();
But as you remember I did not want to use Objective-C at all. I did not want to add new file in my project.

as result I got the following:

The callback was declared in the top of my .cpp file there I declared my descendant from QApplication:

#ifdef Q_OS_MAC
#include <objc/objc.h>
#include <objc/message.h>

bool dockClickHandler(id self,SEL _cmd,...)
     return true;

where Application is my descendant from QApplication.

and most interesting thing, we can register in runtime this function as new method for delegate class (NSApplicationDelegate):

#ifdef Q_OS_MAC

objc_object* cls = objc_getClass("NSApplication");
SEL sharedApplication = sel_registerName("sharedApplication");
objc_object* appInst = objc_msgSend(cls,sharedApplication);

if(appInst != NULL)
     objc_object* delegate = objc_msgSend(appInst,
     objc_object* delegateClass = objc_msgSend(delegateClass,
     constchar* tst = class_getName(delegateClass->isa);
     bool test = class_addMethod((objc_class*) delegateClass,
// "B@:" is quite tricky thing called Method Signature which allows to define C function to selector "applicationShouldHandleReopen:hasVisibleWindows:" and dockClickHandler function will be called as if it was written inside objective-c code.

     qDebug("Registration was successful");
Now, I was able to do what I want in my onClickOnDock() when a user clicks on application Menu in the dock.