5

Having read through various posts and threads that lead me nowhere I need your help.

I do have a Qt Application for Mac OS X that at some point of use will be in the background and not active. When this is the case I want to add a global hotkey so that the user can easily turn certain features on or off by clicking pre-defined hotkeys. The following isn't working while the app is in the background and not focused.

QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_F12), parent);
shortcut->setContext(Qt::ApplicationShortcut);

So far I found Qxt which happens to be outdated for Qt 5.5. Then there is DDHotkey which requires a certain compiler which we can not use for various reasons. Lastly, I found the solution of adding a global AppleScript which registers an event, again, not what I am looking for.

tell application "System Events" to tell process "myApp"
    click menu item "myButton" of menu 1 of menu bar item "Menu" of menu bar 1
end tell

Is there a way to use objective-c or cocoa to accomplish exactly what I am looking for? Please lead me in the right direction if I may have missed something.

Thanks in advance!


To those who seek a more Qt way, check the following repository: https://github.com/ddqd/qxtglobalshortcut5

It makes use of the outdated qxt library but gets it working again. The person tested it until Qt 5.4, we use it successfully under Qt 5.5.

Philipp Meissner
  • 5,273
  • 5
  • 34
  • 59

1 Answers1

5

This might be what you're looking for https://github.com/jaz303/JFHotkeyManager

You could also look at this example from Apple, using the RegisterEventHotKey API call which I think will point you in the right direction. https://developer.apple.com/library/prerelease/mac/samplecode/FunkyOverlayWindow/Listings/FunkyOverlayWindow_OverlayWindow_m.html#//apple_ref/doc/uid/DTS10000391-FunkyOverlayWindow_OverlayWindow_m-DontLinkElementID_8

Or you could try this code

#import <Carbon/Carbon.h>

EventHandlerUPP hotKeyFunction;

pascal OSStatus hotKeyHandler(EventHandlerCallRef nextHandler,EventRef theEvent, void *userData)
{    
    Notify *obj =  userData;
    [obj foo];    
    return noErr;
}

@implementation Notify

- (id)init
{
    self = [super init];
    if (self) {
        //handler
        hotKeyFunction = NewEventHandlerUPP(hotKeyHandler);
        EventTypeSpec eventType;
        eventType.eventClass = kEventClassKeyboard;
        eventType.eventKind = kEventHotKeyReleased;
        InstallApplicationEventHandler(hotKeyFunction,1,&eventType,self,NULL);
        //hotkey
        UInt32 keyCode = 80; //F19    
        EventHotKeyRef theRef = NULL;
        EventHotKeyID keyID;
        keyID.signature = 'FOO '; //arbitrary string
        keyID.id = 1;
        RegisterEventHotKey(keyCode,0,keyID,GetApplicationEventTarget(),0,&theRef);

    }        
    return self;
}

- (void)foo
{

}

@end

And the header

#include "notify.mm"
@interface Notify
 - (id)init;
 - (void)foo;
@end

Simply this is just a object with a method and a constructor, in objective-c this is called init, or initialize, and variants. Calling it should be straight forward with "new".

E.x

#include "notify.h"
int main(){
  Notify* object = new Notify();
}

However, some basic understanding of Objective-C is needed. It's mostly syntax differences in my opinion. But I'm no Objective-C expert myself. Anyway, there is a lot of ways to solve it, this might not be the best idea. You can also call Objective-C code from inside of a C++ class of yours. Take a look at the links bellow for a great example of how that's done.

https://el-tramo.be/blog/mixing-cocoa-and-qt/

https://github.com/remko/mixing-cocoa-and-qt/

http://philjordan.eu/article/mixing-objective-c-c++-and-objective-c++

Meeh
  • 2,538
  • 3
  • 18
  • 20
  • Unfortunately I don't understand how to put this code into a .mm file for Mac and call it from my usual C++ code. – Philipp Meissner Dec 03 '15 at 15:00
  • This might point you in the right direction: http://stackoverflow.com/questions/23404158/qt-run-object-c-code You could also split your project and build a dylib and just load it as a library. (like dll on windows, and so files on linux) – Meeh Dec 04 '15 at 12:11
  • 1
    You just need to make a header file that has the object or functions in your .mm objective-c code file, and then include that in your QT project, then you can call your functions or objects as any other QT/C++ function/object. – Meeh Dec 04 '15 at 12:23
  • We will try to implement this on Monday. I'll mark your answer as correct when we tried it :) – Philipp Meissner Dec 04 '15 at 12:28
  • Okay. Could you possibly help me out here? Let's assume I want to name my class "Hotkey". I add a corresponding `hotkey.h` as well as a `hotkey.mm`. Inside the `.mm` file I add the code you pasted. Now, since I don't understand the Objective-C code provided, what do I have to add into my `.h` file? Could you possibly provide a more detailed answer with a seperated header and `.mm` file? – Philipp Meissner Dec 07 '15 at 10:44
  • 1
    I've updated the example. I renamed FooBar to Notify. That's the class name. What's happening in the class is that the hotkey is registered at the constructor, and sets the hotKeyHandler to trigger when the hotkey is called, which would then execute the foo method in Notify. If you're not familiar with Objective-C I'll suggest maybe moving the logic out of the Objective-C class to a C++ class, see the github project at the bottom of my answer for a example, and notice the file extension is mm even a C++ class is written. – Meeh Dec 07 '15 at 12:51
  • Thank you, you're a beast! I accepted the answer. Thanks for your calmness and help! Further added another solution we found today. It's also a more Qt way ;) – Philipp Meissner Dec 07 '15 at 14:09