browser-media-keys icon indicating copy to clipboard operation
browser-media-keys copied to clipboard

Add support for Mac OS X

Open zjays opened this issue 8 years ago • 90 comments

From what I can tell, the add-on currently doesn't support media keys within Mac OS X (I tested it with Youtube on Firefox 39.0, running OS X 10.8.5).

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/25342256-add-support-for-mac-os-x?utm_campaign=plugin&utm_content=tracker%2F7600490&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F7600490&utm_medium=issues&utm_source=github).

zjays avatar Aug 04 '15 05:08 zjays

Thanks for letting us know about this. I'll try to look into it on my friend's Mac next week.

carlin-q-scott avatar Aug 08 '15 02:08 carlin-q-scott

This might help: https://github.com/nightingale-media-player/nightingale-hacking/tree/sb-trunk-oldxul/extensions/apple-mediakeys http://wiki.getnightingale.com/doku.php?id=add-ons#apple_keyboard_media_key_support

It is an extension for Nightingale Media Player (XULRunner-based application) that allows the use of media keys on Mac OS X. The extension needs to be compiled with XCode.

zjays avatar Aug 11 '15 07:08 zjays

Thanks for the links; I will check them out, probably next week some time.

I tested the plugin on my friend's MacBook air and found that iTunes was hogging all the media key actions so that's probably why it's not working at all.

carlin-q-scott avatar Aug 13 '15 05:08 carlin-q-scott

Here is how it was done with binary components: https://github.com/mstange/mediakeysappleremotesimfy/issues/1

That same issues shows how to do it with Objective-C, although this method may require accessibility. It is likely also possible to accomplish this with Carbon's RegisterEventHotKey. This Carbon routine has not been deprecated as no alternative exists. Here is a topic that shows how: http://stackoverflow.com/questions/4807319/register-hotkey

This is a great repo showing how to tap into CoreFoundation and Carbon routines: https://github.com/philikon/osxtypes

This is some great work!

Noitidart avatar Oct 31 '15 06:10 Noitidart

Hey @carlin-q-scott I'm real excited to see OSX support for this, from my last post how feasible does this look? I love how you do all the work from ChromeWorkers!

Noitidart avatar Nov 14 '15 08:11 Noitidart

I looked at it a little bit and determined that it was too much of a commitment for me. If someone else such as yourself, @Noitidart wants to implement this I'd gladly accept the pull request.

carlin-q-scott avatar Nov 20 '15 05:11 carlin-q-scott

Aw man, I have a lot of projects going on, and have requests from others to help them out. I can add this in line, and will work on it if I have to. It's very close to being done as you did excellent work on the Linux/Windows part.

Noitidart avatar Nov 20 '15 06:11 Noitidart

Here's me setting up event tap on mouse from js-ctypes: https://github.com/Noitidart/MouseControl/blob/master/modules/workers/MMSyncWorker.js#L937-L1144

What I do know is that, the obj-c method cannot run from a chromeworker, that has to be on the mainthread.

What I did above seems to be exact same as what is being done here: https://github.com/mstange/SPMediaKeyTap/blob/master/SPMediaKeyTap.m#L75

The documentation on this says: https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/index.html#//apple_ref/c/func/CGEventTapCreate

Discussion

Event taps receive key up and key down events if one of the following conditions is true:

The current process is running as the root user.

Access for assistive devices is enabled. In OS X v10.4, you can enable this feature using System Preferences, Universal Access panel, Keyboard view.

Firefox runs as root so we're gold here.

As it seems you will have to use CoreFoundation (with maybe mix of some objc) this will come in handy: https://github.com/philikon/osxtypes/

This screenshot procedure of mine uses CoreFoundation and Objc mix: https://github.com/Noitidart/NativeShot/blob/master/modules/workers/MainWorker.js#L1424

If you do get a chance to work on this before I do, if you need help setting up a mac vm i can help with setting up osx 10.10.1 on oracle virtualbox.

Noitidart avatar Nov 20 '15 07:11 Noitidart

Thanks for all the info @Noitidart and I understand that your time is also valuable. I considered setting up OSX on a VM but a friend of mine is willing to lend me his Macbook Air so that won't be necessary.

carlin-q-scott avatar Nov 23 '15 04:11 carlin-q-scott

Wow thanks so much if you are able to do any of the OS X stuff - I really really appreciate it. I can very much help out. I can also make this a droppable into other peoples addons. :) Via npm and normal bootstrap.

Noitidart avatar Nov 23 '15 06:11 Noitidart

I needed to add global hotkey support for an addon of mine. The hotkey i need, is if user hits the usual print screen button from anywhere it will pull up my screenshot addon. On Mac the combo for screenshot is Cmd + Shift + 3.

I made great progress on Mac. But I'm stuck I keep getting error on RegisterEventHotKey which means "invalid parameter(s)". I was wondering if you have some time it would be nice if you could put your eyes on this :)

https://github.com/Noitidart/NativeShot/blob/12b460cdf23236cc8ad1dcb762bba66cb0c634f5/modules/hotkey/HotkeyWorker.js#L290

Noitidart avatar Mar 09 '16 08:03 Noitidart

Hey @carlin-q-scott I split it to another repository so its not cluttered by other code: https://github.com/Noitidart/System-Hotkey

The code we need to edit is in - https://github.com/Noitidart/System-Hotkey/blob/master/modules/hotkey/HotkeyWorker.js

To edit the types module that is in this repository - https://github.com/Noitidart/ostypes

Would be absolutely fantastic if you could help.

The linux vesion isn't working either, but I'm trying to use XCB for that, not DBus as you did in Media Keys.

Windows works great, I used your old technique. :)

I got a headache from debugging the Linux and OS X stuff for the past few days so posted for help - https://discourse.mozilla-community.org/t/system-wide-global-hotkey-help-on-xcb-linux-and-osx-carbon/7579

Noitidart avatar Mar 13 '16 22:03 Noitidart

Wooohoo carlin here is mac support!! All thanks to @arai-a and @KenThomasses - So easy. But unfortunaately I couldn't get it to work from ChromeWorker, so this has to run on mainthread:

https://gist.github.com/Noitidart/7fd5569cd64a000e3027966a2ca90a36

The hotkey in that gist is command + shift + spacebar. The number 49 is key for spacebar. To get code for anything else, run this gist:

https://gist.github.com/Noitidart/8645b47b0e46a0eb284e

And then with firefox focused hit any key it will log it.

Super cool stuff!! :)

Noitidart avatar Apr 09 '16 12:04 Noitidart

Any news? @Noitidart @carlin-q-scott

gaelfoppolo avatar Jun 06 '16 17:06 gaelfoppolo

Somehow I missed Noitdart's last update. I can try finishing off his Gist sometime this week I think.

carlin-q-scott avatar Jun 06 '16 18:06 carlin-q-scott

@carlin-q-scott That gist is complete. I am using it in my addon NativeShot too hook into printscrn across all systems (mac doesnt have print screen they use cmd + 3), check it out - https://github.com/Noitidart/NativeShot/blob/master/bootstrap.js#L3149

Fully functional.

Noitidart avatar Jun 06 '16 23:06 Noitidart

@Noitidart Well it's complete in that it registers a hotkey but I need to register a set of hotkeys and bind them to my app logic. I will also take a stab at using a ChromeWorker as I'd prefer it to run independent of the Firefox UI.

Thank you for sharing such complete code and a helper to figure out the key codes.

carlin-q-scott avatar Jun 07 '16 02:06 carlin-q-scott

My pleasure! For Mac my tests showed it had to be on main thread. I asked around but didn't get a definitive answer. If you can get the mac portion to work from a ChromWorker that would superb! I would definitely change my method to yours!

On Mon, Jun 6, 2016 at 7:12 PM, Carlin Scott [email protected] wrote:

@Noitidart https://github.com/Noitidart Well it's complete in that it registers a hotkey but I need to register a set of hotkeys and bind them to my app logic. I will also take a stab at using a ChromeWorker as I'd prefer it to run independent of the Firefox UI.

Thank you for sharing such complete code and a helper to figure out the key codes.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/carlin-q-scott/browser-media-keys/issues/17#issuecomment-224145379, or mute the thread https://github.com/notifications/unsubscribe/AGE8iavnLchxYyKb7Q-IPrlAs6mvm4ROks5qJNOJgaJpZM4FlD8T .

Noitidart avatar Jun 07 '16 02:06 Noitidart

@Noitidart So media key events are not key events but system events which don't have the keyCode merthod but rather a keycode property. So when your code tries to get the keyCode it crashes Firefox. At least that's my interpretation of the Objective-C docs and this Swift code I found that handles media keys.

Here's my modifications to your keycode capture gist: https://gist.github.com/carlin-q-scott/5a5890a9f6d3fd7902164b44b10ab9a7. I would modified yours but thought you'd want to share it elsewhere without the change in the event type being captured.

I'm not finding the Objective-C docs terribly readable so I'm not sure how to modify the code that gets the keycode to use the new class member signature.

carlin-q-scott avatar Jun 23 '16 04:06 carlin-q-scott

Wow very interesting find. Thanks Carlin.

So to get it to work for media key events, you just have to change NSKeyDownMask to NSSystemDefinedMask on this line:

var rez_add = objc_msgSend(NSEvent, addLocalMonitorForEventsMatchingMask, TYPES.NSEventMask(CONST.NSKeyDownMask), myBlock_c.address());

?

It looks like you are still accessing keyCode though. Or does the C have to be c?

In Objective-C if the selector has no colons (:) its a prop. If it has colons then its a method.

Noitidart avatar Jun 23 '16 04:06 Noitidart

I linked your gist for media keys from my original gist, thanks brother this was some awesome team work:

 Note To use with Media keys change NSKeyDownMask to NSSystemDefinedMask thanks to @Carlin-Q-Scott - copy paste gist - https://gist.github.com/carlin-q-scott/5a5890a9f6d3fd7902164b44b10ab9a7

Noitidart avatar Jun 23 '16 05:06 Noitidart

@Noitidart Yeah, I figured out the NSSystemDefinedMask bit but getting the keyCode doesn't work, even with that slight casing change.

carlin-q-scott avatar Jun 24 '16 00:06 carlin-q-scott

Try addGlobal instead of addLocalMonitorForEventsMatchingMask it's just a guess that system events may not be local.

Noitidart avatar Jun 24 '16 12:06 Noitidart

This is an excellet article I think all we need is here - http://weblog.rogueamoeba.com/2007/09/29/

You need to check subtype too and ensure that it is 8 then it should have a keyCode for the media key but not in the keyCode field it will be in the data1 field.

    if( [event type] == NSSystemDefined && [event subtype] == 8 )
    {
        int keyCode = (([event data1] & 0xFFFF0000) >> 16);
        int keyFlags = ([event data1] & 0x0000FFFF);
        int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
        int keyRepeat = (keyFlags & 0x1);

This makes total sense, as it follows that only NSKeyEvents would have keyCode. So NSSystemDefined would be obtained via data1. Cool stuff.

This article also states a very important note:

One other thing to note is that these keys act as “global hot keys”, every application receives events for them, not merely just the foreground application. This whole public domain sample code is available in a file here.

Meaning we have no need for RegisterHotKeyEvent for a global, as by default these are global events. Very cool.

Honestly that swift stuff doesn't make sense too me. I haven't took the time to learn it yet.

Noitidart avatar Jun 24 '16 13:06 Noitidart

Here are the media key magic numbers you need:

# hidsystem/ev_keymap.h
NX_KEYTYPE_SOUND_UP = 0
NX_KEYTYPE_SOUND_DOWN = 1
NX_KEYTYPE_PLAY = 16
NX_KEYTYPE_NEXT = 17
NX_KEYTYPE_PREVIOUS = 18
NX_KEYTYPE_FAST = 19
NX_KEYTYPE_REWIND = 20

Noitidart avatar Jun 24 '16 13:06 Noitidart

I spent hours trying to figure out how to de-refence the pointers to get the data1 value. I didn't realize that the cast function de-references pointers and that I had to access the value property on the result to get the native js value. I've updated the gist I posted with these changes so that it's printing out all the important values mentioned by @Noitidart.

Maybe i can wrap this up tomorrow but realistically this weekend.

carlin-q-scott avatar Jun 28 '16 06:06 carlin-q-scott

This is looking like its coming out nicely! Awesome team work! Yeah the cast method is pretty cool but no tutorial is going to specifically tell you when you need to cast. It's just based on need based on what CData values we get in our ctypes (which ultimate depends on how we set up the types and how js-ctypes reads them as UInt64 or etc). That's why I console.log a lot of stuff.

In the code:

cEventType = ctypes.cast(cEventType, TYPES.NSUInteger).value;
var eventData = objc_msgSend(objc_arg1__aNSEventPtr, data1);

I think you should do a test on cEventType before trying to get the data1 field. As I'm not sure if all event types will have data1. If it doesn't, it will probably crash, or give a null pointer (0x0) but with objc my usual experience is it crashes.

Noitidart avatar Jun 28 '16 09:06 Noitidart

Haha, yes, it usually crashes if I do anything wrong :(.

In the actual code I'm only going to handle subtype 8 but I'm also trying to figure out if I can use that EventTypeSpec you used for the hotkeys to filter the events.

carlin-q-scott avatar Jun 29 '16 03:06 carlin-q-scott

Superior work brother! If you can get media keys working with the carbon method (EventTypeSpec/RegisterHotKeyEvent) that would be ideal because this doesn't have the overhead of triggering for all the other keys/system events.

Re getting the objc method or the carbon method into ChromeWorker's - I'm not sure if either method would be easier. I was not able to get either method working from a worker. If you can pull that off that would rock!

Noitidart avatar Jun 29 '16 05:06 Noitidart

So it looks like iTunes gets the events as well, even when I return null from the callback. The official documentation implies that returning nil will kill the event chain but maybe iTunes is called before Firefox.

I've checked in what i have so far into my master branch.

carlin-q-scott avatar Jun 29 '16 05:06 carlin-q-scott