How to make hotkeys

Hi All,

I have made an Mac app for myself which has several menu items that can be triggered by a keyboard shortcut (as usual) when this app is the foreground .
I would like for very few of those to be triggered by a keyboard combo even if the app is not the frontmost app, in other word to have those as ‘hotkeys’.
Can anybody share the corresponding cose, or give me a hist on how to achoive this?
I amk using Xojo 2022 v1.1 on mac.

Thanks in advance !
Luciano

As far as I know this can only be done with CarbonApplicationEvents (MBS plugins has this).
Sadly this is deprecated by Apple (for some time now). And there is no new real API replacement.
So as long Apple is keeping this very old Carbon API, it can work. But it’s not future proof though.

You can check our HotkeyMBS class in MBS Xojo Plugins.

I have done that very simply by polling the Keyboard class from a timer.

https://documentation.xojo.com/api/hardware/keyboard.html#keyboard

You could try this:

It’s very basic, and Mac only, but it’s kept me happy enough over the years.

Edited to add: But Michel’s suggestion works beautifully and does not require anything extra outside of Xojo.

Thanks to all of rthe suggestions.
@Charlie_Robin:
I tried your code (I build an app using your global hotkeys test app.xojo_project.
Then copied the unzipped “libglobal-hotkeys-shim-for-xojo.dylib” into the:
global hotkeys test app.app/Contents/Frameworks".
The run the app, but none of the 2 pre-set hotkeys worked.
Any suggestions?
Thanks in advance!

Hello Luciano,

Could be a few things. One is that the dylib is only for the “old” Mac architecture and not the new chips, as I am still a diehard Catalina user, so that’s what I tend to build for. Another possibility is some kind of security issue, as I know that with each OS issue Apple has fairly relentlessly upped the security for apps. If I get a moment I’ll take a quick look at it. What macOS version and platform are you using?

Thanks ! I am using 12.3.1.
In the meantime I am testing the “Timer” approach.
I have the code below in a Timer with a period of 500 millisecs:

If Keyboard.AsyncControlKey And Keyboard.AsyncOptionKey and Keyboard.AsyncShiftKey Then

If Keyboard.AsyncKeyDown(&h7C) Then // right arrow
me.Period= 2000
textArea1.text= “Right Arrow”
End If

If Keyboard.AsyncKeyDown(&h00) Then // A
me.Period= 2000
textArea1.text= “A”
End If

If Keyboard.AsyncKeyDown(&h7A) Then // Function key #1
me.Period= 2000
textArea1.text= “F1”
End If

// handle the keyboard event here…
'MessageBox(“keyboard event detected…”)
End If

In brief, the hotkeys are triggered when I press ControlKey and OptionKey and ShiftKey together with Left arrow or with Function key #1 or “a”.
If I do this when the app is in foreground then all work fine with the execution of an annoying system beep… I guess something triggers an error.

When the app is in background (and either the Finder or the Terminal are in the foreground) then I still get the “system beep” error sound, but the pressing the “a” key on my keyboard does not trigger anything, even not the error sound if the Finder is in the front, and not even the sound error if the Terminal is the front.

I can “survive” with the sound error, but I would like the app to respond also when I press ControlKey and OptionKey and ShiftKey + “A”

Any suggestions.

Thanks
L.

You get the beep because you need to add a keydown event handler to the window.

Keep in mind this will eat CPU cycles all the time. Not sure if that approach is advisable.
Using the macOS API is imo the only correct solution.

2 Likes

I believe you that using API is much more efficient

Do you have any initial code that I can use as a starting point ?

MBS has some examples.

Hello Luciano,

Try this: it’s the same source code, but recompiled on XCode Monterey, and (hopefully) for both Intel and Apple Silicon architectures. (The dylib size has increased, and inspecting it with MachOExplorer confirms that it looks like it’s now a fat binary.)

It works as expected on my Catalina 10.15.7, also works on Monterey with Xojo 2022 Release 1.1.

Regards

Charlie

1 Like

Thanks a lot, Charlie ! I just tested it and I get this error in the System Report
Non-fatal error enumerating at , continuing: Error Domain=NSCocoaErrorDomain Code=260 “The file “PlugIns” couldn’t be opened because there is no such file.” UserInfo={NSURL=PlugIns/ – file:///Users/ldicroce/Dropbox%20(CRG%20ADV)/Programming/041_Xojo/000_Examples%20from%20others/GlobalHotkeysTest/Builds%20-%20global%20hotkeys%20test%20app/macOS%2064%20bit/global%20hotkeys%20test%20app.app/Contents/, NSFilePath=/Users/ldicroce/Dropbox (CRG ADV)/Programming/041_Xojo/000_Examples from others/GlobalHotkeysTest/Builds - global hotkeys test app/macOS 64 bit/global hotkeys test app.app/Contents/PlugIns, NSUnderlyingError=0x7fc833d761c0 {Error Domain=NSPOSIXErrorDomain Code=2 “No such file or directory”}}

Luciano

I imagine the the code to add in the window keydown event handler is:

return true

Or something more … ?

Hello Luciano,

You might need to check/change the build/platform settings for that demo project, as don’t forget I’m on Intel, so I turn off all the Apple Silicon (ARM) options. Aside from that, it’s not too complicated, there are no plugins involved, just the dylib that needs to be copied into the app frameworks folder (which is what the build step is for). Also that demo project is from a long time ago, all the base classes for windows and controls are the “old” Xojo framework, not the new desktop ones that were introduced, so I suppose it is possible that this is a factor. (Although I don’t really know that much about it.)

Also, I notice you are working in a Dropbox folder, I’ve noticed that sometimes the interaction of Xojo building/creating folders together with Dropbox immediately starting to try to sync these happenings, causes problems: files and folders become locked etc.

So perhaps try moving the project to a non-Dropbox location and then running/building there.

Aside from those things, I’m not sure what else it could be. It works okay here, and I’ve just also tried it on a Big Sur virtual machine, and it’s okay there also.

1 Like

Return true would suffice.