Skip to content

Instantly share code, notes, and snippets.

@fauxpark
Last active October 21, 2024 11:36
Show Gist options
  • Save fauxpark/010dcf5d6377c3a71ac98ce37414c6c4 to your computer and use it in GitHub Desktop.
Save fauxpark/010dcf5d6377c3a71ac98ce37414c6c4 to your computer and use it in GitHub Desktop.
QMK Apple Fn

QMK Apple Fn Key

This patch adds support for the Apple Fn key, which unlike most keyboards with Fn keys, is actually sent over the wire. It works by repurposing the reserved byte in the keyboard report to represent the KeyboardFn usage of the AppleVendor Top Case usage page. When the Fn key is pressed, the value of this byte becomes 1.

To apply this patch, download the below file, cd to your qmk_firmware repository in your preferred terminal, and run git apply /path/to/applefn.patch. Then, add the QK_APPLE_FN keycode (or AP_FN for short) to your keymap.

There are a couple of caveats to this implementation that are important to be aware of. Firstly, it is not compatible with NKRO, as QMK's NKRO report format has no reserved byte - it is part of the 6KRO report for compatibility with the HID boot protocol. Thus you must set NKRO_ENABLE = no in your keymap's rules.mk. You will also need to redefine the USB Vendor and Product IDs in your keymap's config.h to that of a genuine Apple keyboard* in order for macOS to recognise the Fn key:

#undef VENDOR_ID
#define VENDOR_ID 0x05AC
#undef PRODUCT_ID
#define PRODUCT_ID <pid>

This is the primary reason this patch has not been integrated into upstream QMK - Apple would probably not be too happy about others using their vendor ID, and a feature that relies on the VID/PID pair being set to a specific value is not particularly ideal anyway.

See qmk/qmk_firmware#2179 for a little more info and discussion.

* It appears that the functionality of certain F keys can differ depending on the PID, likely because they have evolved over time on real Apple keyboards.

diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index 18f8b0bbfc..4ef3e230e4 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -878,6 +878,10 @@ ifeq ($(strip $(JOYSTICK_ENABLE)), yes)
endif
endif
+ifeq ($(strip $(APPLE_FN_ENABLE)), yes)
+ OPT_DEFS += -DAPPLE_FN_ENABLE
+endif
+
USBPD_ENABLE ?= no
VALID_USBPD_DRIVER_TYPES = custom vendor
USBPD_DRIVER ?= vendor
diff --git a/data/constants/keycodes/keycodes_0.0.2_applefn.hjson b/data/constants/keycodes/keycodes_0.0.2_applefn.hjson
new file mode 100644
index 0000000000..1378413a9e
--- /dev/null
+++ b/data/constants/keycodes/keycodes_0.0.2_applefn.hjson
@@ -0,0 +1,11 @@
+{
+ "keycodes": {
+ "0x5300": {
+ "group": "apple_fn",
+ "key": "QK_APPLE_FN",
+ "aliases": [
+ "AP_FN"
+ ]
+ }
+ }
+}
diff --git a/quantum/action.c b/quantum/action.c
index 6368f7398c..d9bd34f681 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -555,6 +555,18 @@ void process_action(keyrecord_t *record, action_t action) {
}
break;
#endif // EXTRAKEY_ENABLE
+#ifdef APPLE_FN_ENABLE
+ /* Apple Fn */
+ case ACT_APPLE_FN:
+ if (event.pressed) {
+ add_apple_fn(keyboard_report);
+ send_keyboard_report();
+ } else {
+ del_apple_fn(keyboard_report);
+ send_keyboard_report();
+ }
+ break;
+#endif
/* Mouse key */
case ACT_MOUSEKEY:
register_mouse(action.key.code, event.pressed);
@@ -1196,6 +1208,9 @@ void debug_action(action_t action) {
case ACT_USAGE:
ac_dprintf("ACT_USAGE");
break;
+ case ACT_APPLE_FN:
+ dprint("ACT_APPLE_FN");
+ break;
case ACT_MOUSEKEY:
ac_dprintf("ACT_MOUSEKEY");
break;
diff --git a/quantum/action_code.h b/quantum/action_code.h
index d9a575b518..f347010c8d 100644
--- a/quantum/action_code.h
+++ b/quantum/action_code.h
@@ -53,7 +53,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
* ACT_SWAP_HANDS(0110):
* 0110|xxxx| keycode Swap hands (keycode on tap, or options)
*
- * 0111|xxxx xxxx xxxx (reserved)
+ * ACT_APPLE_FN(0111):
+ * 0111|0000|0000|0000 Apple Fn
*
* Layer Actions(10xx)
* -------------------
@@ -95,6 +96,8 @@ enum action_kind_id {
ACT_MOUSEKEY = 0b0101,
/* One-hand Support */
ACT_SWAP_HANDS = 0b0110,
+ /* Apple Fn */
+ ACT_APPLE_FN = 0b0111,
/* Layer Actions */
ACT_LAYER = 0b1000,
ACT_LAYER_MODS = 0b1001,
@@ -182,6 +185,7 @@ enum usage_pages {
#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id))
#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id))
+#define ACTION_APPLE_FN() ACTION(ACT_APPLE_FN, 0)
#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key)
/** \brief Layer Actions
diff --git a/quantum/keycodes.h b/quantum/keycodes.h
index bbf10da36d..b8e894a399 100644
--- a/quantum/keycodes.h
+++ b/quantum/keycodes.h
@@ -310,6 +310,7 @@ enum qk_keycode_defines {
KC_RIGHT_SHIFT = 0x00E5,
KC_RIGHT_ALT = 0x00E6,
KC_RIGHT_GUI = 0x00E7,
+ QK_APPLE_FN = 0x5300,
QK_SWAP_HANDS_TOGGLE = 0x56F0,
QK_SWAP_HANDS_TAP_TOGGLE = 0x56F1,
QK_SWAP_HANDS_MOMENTARY_ON = 0x56F2,
@@ -938,6 +939,7 @@ enum qk_keycode_defines {
KC_RGUI = KC_RIGHT_GUI,
KC_RCMD = KC_RIGHT_GUI,
KC_RWIN = KC_RIGHT_GUI,
+ AP_FN = QK_APPLE_FN,
SH_TOGG = QK_SWAP_HANDS_TOGGLE,
SH_TT = QK_SWAP_HANDS_TAP_TOGGLE,
SH_MON = QK_SWAP_HANDS_MOMENTARY_ON,
@@ -1406,6 +1408,7 @@ enum qk_keycode_defines {
#define IS_CONSUMER_KEYCODE(code) ((code) >= KC_AUDIO_MUTE && (code) <= KC_LAUNCHPAD)
#define IS_MOUSE_KEYCODE(code) ((code) >= KC_MS_UP && (code) <= KC_MS_ACCEL2)
#define IS_MODIFIER_KEYCODE(code) ((code) >= KC_LEFT_CTRL && (code) <= KC_RIGHT_GUI)
+#define IS_APPLE_FN_KEYCODE(code) ((code) >= QK_APPLE_FN && (code) <= QK_APPLE_FN)
#define IS_SWAP_HANDS_KEYCODE(code) ((code) >= QK_SWAP_HANDS_TOGGLE && (code) <= QK_SWAP_HANDS_ONE_SHOT)
#define IS_MAGIC_KEYCODE(code) ((code) >= QK_MAGIC_SWAP_CONTROL_CAPS_LOCK && (code) <= QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK)
#define IS_MIDI_KEYCODE(code) ((code) >= QK_MIDI_ON && (code) <= QK_MIDI_PITCH_BEND_UP)
diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c
index 9a67fad278..36a0b309be 100644
--- a/quantum/keymap_common.c
+++ b/quantum/keymap_common.c
@@ -70,6 +70,11 @@ action_t action_for_keycode(uint16_t keycode) {
case KC_AUDIO_MUTE ... KC_LAUNCHPAD:
action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(keycode));
break;
+#endif
+#ifdef APPLE_FN_ENABLE
+ case QK_APPLE_FN:
+ action.code = ACTION_APPLE_FN();
+ break;
#endif
case KC_MS_UP ... KC_MS_ACCEL2:
action.code = ACTION_MOUSEKEY(keycode);
diff --git a/tmk_core/protocol/report.c b/tmk_core/protocol/report.c
index 1ba3be4604..b53d3f687b 100644
--- a/tmk_core/protocol/report.c
+++ b/tmk_core/protocol/report.c
@@ -299,3 +299,13 @@ __attribute__((weak)) bool has_mouse_report_changed(report_mouse_t* new_report,
return changed;
}
#endif
+
+#ifdef APPLE_FN_ENABLE
+void add_apple_fn(report_keyboard_t* keyboard_report) {
+ keyboard_report->reserved = 1;
+}
+
+void del_apple_fn(report_keyboard_t* keyboard_report) {
+ keyboard_report->reserved = 0;
+}
+#endif
diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h
index 9d415a3bfd..8c65b40386 100644
--- a/tmk_core/protocol/report.h
+++ b/tmk_core/protocol/report.h
@@ -348,6 +348,11 @@ void clear_keys_from_report(report_keyboard_t* keyboard_report);
bool has_mouse_report_changed(report_mouse_t* new_report, report_mouse_t* old_report);
#endif
+#ifdef APPLE_FN_ENABLE
+void add_apple_fn(report_keyboard_t* keyboard_report);
+void del_apple_fn(report_keyboard_t* keyboard_report);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c
index e215c90900..e38c0d37f7 100644
--- a/tmk_core/protocol/usb_descriptor.c
+++ b/tmk_core/protocol/usb_descriptor.c
@@ -75,10 +75,22 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] = {
HID_RI_REPORT_COUNT(8, 0x08),
HID_RI_REPORT_SIZE(8, 0x01),
HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+
+#ifdef APPLE_FN_ENABLE
+ HID_RI_USAGE_PAGE(8, 0xFF), // AppleVendor Top Case
+ HID_RI_USAGE(8, 0x03), // KeyboardFn
+ HID_RI_LOGICAL_MINIMUM(8, 0x00),
+ HID_RI_LOGICAL_MAXIMUM(8, 0x01),
+ HID_RI_REPORT_COUNT(8, 0x01),
+ HID_RI_REPORT_SIZE(8, 0x08),
+ HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),
+#else
// Reserved (1 byte)
HID_RI_REPORT_COUNT(8, 0x01),
HID_RI_REPORT_SIZE(8, 0x08),
HID_RI_INPUT(8, HID_IOF_CONSTANT),
+#endif
+
// Keycodes (6 bytes)
HID_RI_USAGE_PAGE(8, 0x07), // Keyboard/Keypad
HID_RI_USAGE_MINIMUM(8, 0x00),
diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c
index d74f375f66..2ade1350ad 100644
--- a/tmk_core/protocol/vusb/vusb.c
+++ b/tmk_core/protocol/vusb/vusb.c
@@ -417,10 +417,22 @@ const PROGMEM uchar keyboard_hid_report[] = {
0x95, 0x08, // Report Count (8)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
+
+#ifdef APPLE_FN_ENABLE
+ 0x05, 0xFF, // Usage Page (AppleVendor Top Case)
+ 0x09, 0x03, // Usage (KeyboardFn)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x95, 0x01, // Report Count (1)
+ 0x75, 0x08, // Report Size (8)
+ 0x81, 0x02, // Input (Data, Variable, Absolute)
+#else
// Reserved (1 byte)
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x03, // Input (Constant)
+#endif
+
// Keycodes (6 bytes)
0x05, 0x07, // Usage Page (Keyboard/Keypad)
0x19, 0x00, // Usage Minimum (0)
@drashna
Copy link

drashna commented Oct 12, 2023

To clarify, karabiner does pick up the event. globe+n works, globe+e works, globe+h, and some of the listed behavior doesn't seem to work for me. (with and without karabiner installed)
I tested on several different boards (STM32F411, STM32F303, and RP2040). and seemed to work.

The main different between my implementation and fauxpark's PR is that I've added the code to support it as a keycode

@marceliwac
Copy link

@drashna Did you observe any changes in this behaviour with PID/VID changes? I'm aware you have mentioned this wasn't necessary, but I'm curious to know if that's the limiting factor for the globe+h functionality you described.

@HVR88
Copy link

HVR88 commented Oct 13, 2023

I just took a virtual axe to all the keychron-specific files in the source tree and removed every bit of cruft and keychron logic/functions I could see.

Issues mentioned previously persist. Really at a loss why others are seeing different behavior. Unfortunately this is the only QMK compatible board I own so I can't test another.

@fauxpark
Copy link
Author

So for reference, here is my entire setup.

  • I'm testing on a GH60 Satan, with the custom keycode above, replacing left control. My F-keys are on a second layer but this is largely irrelevant.
  • My repo is up to date with current master.
  • I have an M1 Pro MBP 14" 2021 (MacbookPro18,3).

Without an Apple VID/PID:
Usage 0x29D is recognised by macOS as Fn (seen in QMK Toolbox key tester), but does not respond to any combinations (Globe+N, Globe+F1-12)

Now I add the following to my keymap's config.h:

#undef VENDOR_ID
#undef PRODUCT_ID
#define VENDOR_ID  0x05AC // Apple
#define PRODUCT_ID 0x0220 // Aluminum Keyboard (ANSI)

With this change, the F-keys now operate as they should (brightness etc), although the functionality is slightly different as it is determined by the PID - F4 triggers Launchpad instead of Spotlight as the builtin keyboard does, for example. Globe+N still does not work.

I had a suspicion last night after looking at ZMK's report descriptors. I commented out the VID/PID override and added the following to my keymap's rules.mk:

KEYBOARD_SHARED_EP = yes

By default the keyboard and extrakeys (media+power) reports are sent to separate endpoints. As you might know, macOS treats each keyboard (possibly each HID device) as a separate entity, even if they are part of the same physical device. If you press Caps Lock on one keyboard, only that keyboard's LED will light, and not any others attached to the computer (as a side note this makes Caps Lock state difficult to sync when switching between 6KRO and NKRO on a Mac).

The above flag merges the keyboard report descriptor into the "shared" endpoint where the NKRO and extrakeys descriptors are. Now Globe+N works...but the F-keys do not.

One last try with this and the VID/PID override... surprisingly, no change. The Aluminum Keyboard is pre-Globe key, so it would be interesting to see if the pair from a newer keyboard makes a difference, though I don't actually know whether they can be used wired anymore or if they are purely Bluetooth these days.

@drashna
Copy link

drashna commented Oct 13, 2023

Ah, that makes a lot of sense. I do have shared EP enabled, since some of my boards are blackpills, which have a very low number of endpoints available.

@HVR88
Copy link

HVR88 commented Oct 13, 2023

Without an Apple VID/PID:
Usage 0x29D is recognised by macOS as Fn (seen in QMK Toolbox key tester), but does not respond to any combinations (Globe+N, Globe+F1-12)

OK, that now makes sense - which is what I've been seeing. The exception being that on my MBA M1, the key is recognized as Globe in System Settings. But I'm confident that makes no difference as other tools like Key Codes just call it Fn.

KEYBOARD_SHARED_EP = yes

So now we're getting somewhere. I hope this is the silver bullet, because at this point I've ripped everything out of the code that makes this Keychron a Keychron and still no joy.

The Aluminum Keyboard is pre-Globe key, so it would be interesting to see if the pair from a newer keyboard makes a difference

I was this close || yesterday to going out and buying a corded Apple keyboard from FB marketplace to run some tests.

Going to restore all the source files, make that rule addition and try this all again.

@HVR88
Copy link

HVR88 commented Oct 13, 2023

I've just tested with @drashna's patch. That was it, SHARED_EP gets me the same behavior you've seen. Globe/Fn + OTHER_KEYS works as mostly as expected. With F1-12 not working and Globe/Fn + Backspace not performing a DEL. This is also how this was described in the ZMK threads.

As we stand now, the only thing different in my setup seems to be that without the SHARED_EP and with Apple VID/PID, my F1-12 still never work the same as with an Apple keyboard using the Globe key. This is with those keys defined as simple F1-12 in the keymap.

Apart from some keys not responding in COMBO the same way as a real Apple keyboard, the other difference of note is something I mentioned previously. Pressing + holding and then releasing Globe/Fn still brings up the Emoji picker - on a real Apple board, the act of holding cancels the action and nothing should happen.

Interestingly, they both look exactly the same in Key Code viewer:

image

@real-felix
Copy link

Hello, I'm writing my own keyboard firmware, and I think I did all correctly, but I cannot emulate the "globe key"/"fn key". What I tried:

  • I use the Apple vendor ID: 0x05AC
  • I my report descriptor, I replaced the part for the reserved byte with this one:
    0x05, 0xFF,         //   Usage Page (AppleVendor Top Case)
    0x09, 0x03,         //   Usage (KeyboardFn)
    0x15, 0x00,         //   Logical Minimum (0)
    0x25, 0x01,         //   Logical Maximum (1)
    0x95, 0x01,         //   Report Count (1)
    0x75, 0x08,         //   Report Size (8)
    0x81, 0x02,         //   Input (Data, Variable, Absolute)
    
  • When I press the fake fn key, I set the reserved byte to 1. For example, if I press it only, it looks like 00 01 00 00 00 00 00 00. I tested with a HID debugging tool, this report is sent when I press the key.

I'm lost at that moment. I wonder what I forgot to do to make it work.

@fauxpark
Copy link
Author

Just the VID is not enough, you also need to use a PID of an Apple keyboard with a Fn key.

@HVR88
Copy link

HVR88 commented Oct 15, 2023

Is there a special procedure to be able to use a custom key code inside LT()?

When trying to use the globe key within LT(1,KC_GLB) for example, it gets replaced with KC_F7 after compiling.

@fauxpark
Copy link
Author

fauxpark commented Oct 16, 2023

Layer-Tap and Mod-Tap only support basic keycodes.
https://docs.qmk.fm/#/keycodes

@HVR88
Copy link

HVR88 commented Oct 16, 2023

Right, I recall reading that, but some of the basic keys are consumer page, so I assumed incorrectly it should work. Is it a case of it only working with codes that are defined within qmk's own code then?

@fauxpark
Copy link
Author

The usage page is irrelevant. Only basic keycodes are supported.

@HVR88
Copy link

HVR88 commented Oct 16, 2023

The usage page is irrelevant. Only basic keycodes are supported.

I understand, this is what I was asking. Would it work if the KC_GLB were added to qmk as a basic keycode? (spoiler, it does). Which is what @drashna's change does.

I had been compiling from Master, but just switched over to @drashna's branch and can confirm it works as expected.

Using layers is the only way I can access clean function keys, so combining Globe with a layer tap I'm able to use one key for both. The caveat is that I have to include all the Globe modifier shortcuts into a layer instead of letting the OS handle it alone.

@HVR88
Copy link

HVR88 commented Oct 18, 2023

Decided to take another look at the whole F-key thing using Apple VID/PID and ran through the 4 PIDs I was able to find for Apple external keyboards with FN/Globe keys - mainly because I wasn't previously able to get the F-keys to produce their special functions. SPOILER: PID 0x0267 won't work on my system.

  • Using @drashna's code base with the consumer code for the Globe key.
  • Testing on M1 Macbook Air 2020 (first release), running macOS 14 (Sonoma - latest Beta 14.1)
  • this Macbook Air has a microphone icon on the F4 key of its built-in keyboard which brings up Spotlight

I'm not specifying any version string along with VID/PID Maybe that makes a difference to some of this.

QMK on Keychron Q3 - disabled Keychron's key processing hackery

Keychron VID/PID - no special functions on F-Keys. They come up as regular F-Keys

Apple 0x0267 (Magic Keyboard ANSI) - same as above - doesn't work with any special functions

Apple 0x0220 (Aluminum Keyboard ANSI) - All F-keys produce their special functions

  • F1/F2 Brightness work on Internal and External display - (internal only when external not connected)
  • Globe + F-Key does not produce a regular F-Key - it just continues to do the special function
  • Globe + N shows notification center
  • Globe + H shows desktop
  • Globe + E shows emoji picker
  • F4 brings up LaunchPad (not spotlight like internal keyboard)

Apple 0x024f (Aluminum Keyboard ANSI) - Works as above
Apple 0x021d (Aluminum MINI Keyboard ANSI) - Works as above

Other keyboards
Microsoft Internet Keyboard c. 1999 - no special functions on F-Keys. They come up as regular F-Keys
Random Logitec - same as above, normal F-keys

An interesting note:
The QMK built-in keycodes for Brightness (KC_BRID and KC_BRIU, consumer codes 0x006F and 0x0070) don't work on my external monitor, only the built-in display of the MBA.

@lordpixel23
Copy link

Having just read the entire thread can I ask if @drashna 's patch in on track to be integrated into the upstream main/master?

If I am following then it seems like it can be used along with SHARED_EP to activate some basic functionality such as Globe+E for Emojis and/or it can be used as a layer activation key along with a layer which "emulates" the other functionality a "real" fn key provides.

This seems worth landing?

@HVR88
Copy link

HVR88 commented Nov 1, 2023

@fauxpark's PR was merged to the develop branch here: qmk/qmk_firmware#22256

If I am following then it seems like it can be used along with SHARED_EP to activate some basic functionality such as Globe+E for Emojis

Yes.

and/or it can be used as a layer activation key along with a layer which "emulates" the other functionality a "real" fn key provides.

Not by simply using LT alone, because it doesn't include some of the changes @drashna made. Namely, the globe key isn't defined as a basic keycode. You should be able to do it by including a custom condition in process_record_user() to check for the two key press types (tap and hold) on a placeholder, and then manually send the globe key on the tap.

Example placeholder:

#define KC_MYGLOBELAYERKEY LT(0, KC_ESC)

@lordpixel23
Copy link

Right. That's my question. I believe @fauxpark made reference to not defining a KC_ code yet and @drashna did take that additional step in their patch. So I am trying to understand if the plan is to make a second merge with additional changes or if there is some reason for not adding this as a basic keycode?

@fauxpark
Copy link
Author

fauxpark commented Nov 2, 2023

I didn't add a keycode because this usage only really does something useful on macOS, and even then you still need an Apple VID/PID pair for it to work properly, as has been mentioned several times already, and in my PR. So it's not like you can use the Configurator due to this restriction anyway; for now you can make a custom keycode like so.

@lordpixel23
Copy link

Thanks for the summary. I did read the whole history back to 2017 so I have a decent handle on it I think. I get that if you want a true Apple fn key which works like a native keyboard the VID/PID issue is still there and I suppose that means support in the GUI tools is not appropriate as the restrictions will confuse people who haven't followed the whole context. I do see value in being able to do the Globe+E shortcuts etc.

Anyway, thanks for the link to that comment link. That makes things clearer.

@HVR88
Copy link

HVR88 commented Nov 3, 2023

I didn't add a keycode because this usage only really does something useful on macOS, and even then you still need an Apple VID/PID pair for it to work properly,

There are many other codes already in QMK's basic list that only work on one platform, as a comparison.

IMO, the VID/PID issue isn't really relevant to the Globe/Fn key - it's relevant to Mac OS's support of Function keys (F1 to F12). Globe/Fn shortcuts work without Apple VID/PID (Emoji without combo, Globe-E, Globe-H, Globe-N etc.) It's the function keys that don't work without Apple's VID/PID - F1/F2 for brightness, etc. And with or without Apple VID/PID, you can't get the same behavior as a real Apple keyboard where Globe/Fn+F1 = real F1 so that's moot.

This is why Apple's accessories implementation document spells out the consumer code for others to use as Globe, but doesn't mention anything about its use with the Function keys.

I'm actually using Apple VID/PID on my keyboard right now and for only one reason. To make sure that screen brightness control on F1 and F2 work with both internal and external display. Otherwise everything else can be done with codes built into QMK and layer shifts.

@lordpixel23
Copy link

I wasn't going to say anything further but I think @HVR88 makes the case well. There is value in adding the key code to to make Globe available as an extra key code separately from the issue of VID/PID and function keys. It enables one to access most of the functionality.

@chrismanderson
Copy link

I was catching up on this thread and wanted to sum up a bit as it's exciting to see some progress being made, but understandably a bit tougher to follow the current state of this issue.

  1. Recently, Apple added a consumer code for the Globe Key (0x029D) to the keyboard consumer page for keyboards on Apple devices. This means that there is now an actual key code for the Globe key that can be used in QMK and that macOS can recognize. This means that the original patch in this gist is no longer required.

  2. To use the new Globe key key code,

    1. Apply the code provided by @drashna (drashna/qmk_firmware@fb94bc4)
    2. Set KEYBOARD_SHARED_EP = yes in your rules.mk file for your specific keyboard.
    3. Add KC_GLOBE to your keymap file
  3. In doing so, some of the Globe/Fn keyboard shortcuts will work properly, but not all. Namely, the F1-12 keys do not work as expected. There are other additional not well understood quirks, such as the emoji picker displaying no matter how long you hold the custom Globe key (where with a 'real' Globe key, the picker does not display if you hold it)

  4. In order to support the special functions of the F-keys, you still need to use the custom VID/PID linked at the top of the gist.

  5. Some of the functionality provided by @drashna has been merged into QMK's develop branch (qmk/qmk_firmware#22256). Notably, this does not include the actual key code you can use in your keymap.

  6. You cannot use KC_GLOBE as a layer modifier key because layer-tap/mod-tap only support basic key codes.

@HVR88
Copy link

HVR88 commented Nov 7, 2023

Mostly.

  1. To use the new Globekey code,
    i. Apply the code provided by @fauxpark or pull the master branch clone provided by @drashna
    iii. Add KC_GLOBE to your keymap file only for @drashna's branch

  2. Fn shortcuts work - it would be good to find/list any that don't (F1-12 not included, see #4). Emoji picker shows up on key-release after a long hold of the Fn key, which doesn't match behavior of real Apple keyboard (VID/PID has no effect on this)

  3. F1-12 keys don't work at all without Apple VID/PID and this has nothing to do with the Fn/Globe functionality

  4. @fauxpark's code has been merged into QMK's development branch

  5. You can't use KC_GLOBE as a layer modifier with @fauxpark's merged code because there is no KC_GLOBE defined as a basic keycode. You can use KC_GLOBE as a layer modifier with @drashna's branch because that keycode is defined.

  6. The overall behavior using 0x029D seems to be working as Apple intended - macOS bugs notwithstanding.

@volsk
Copy link

volsk commented Oct 18, 2024

Decided to take another look at the whole F-key thing using Apple VID/PID and ran through the 4 PIDs I was able to find for Apple external keyboards with FN/Globe keys - mainly because I wasn't previously able to get the F-keys to produce their special functions. SPOILER: PID 0x0267 won't work on my system.

  • Using @drashna's code base with the consumer code for the Globe key.
  • Testing on M1 Macbook Air 2020 (first release), running macOS 14 (Sonoma - latest Beta 14.1)
  • this Macbook Air has a microphone icon on the F4 key of its built-in keyboard which brings up Spotlight

I'm not specifying any version string along with VID/PID Maybe that makes a difference to some of this.

QMK on Keychron Q3 - disabled Keychron's key processing hackery

Keychron VID/PID - no special functions on F-Keys. They come up as regular F-Keys

Apple 0x0267 (Magic Keyboard ANSI) - same as above - doesn't work with any special functions

Apple 0x0220 (Aluminum Keyboard ANSI) - All F-keys produce their special functions

  • F1/F2 Brightness work on Internal and External display - (internal only when external not connected)
  • Globe + F-Key does not produce a regular F-Key - it just continues to do the special function
  • Globe + N shows notification center
  • Globe + H shows desktop
  • Globe + E shows emoji picker
  • F4 brings up LaunchPad (not spotlight like internal keyboard)

Apple 0x024f (Aluminum Keyboard ANSI) - Works as above Apple 0x021d (Aluminum MINI Keyboard ANSI) - Works as above

Other keyboards Microsoft Internet Keyboard c. 1999 - no special functions on F-Keys. They come up as regular F-Keys Random Logitec - same as above, normal F-keys

An interesting note: The QMK built-in keycodes for Brightness (KC_BRID and KC_BRIU, consumer codes 0x006F and 0x0070) don't work on my external monitor, only the built-in display of the MBA.

This all works, with capslock mapped to KC_GLOBE, but window tiling with Globe + Ctrl + Arrow does not work. Interestingly Globe + Ctrl + F does work for full window. Any suggestions?

@lordpixel23
Copy link

I just got a new keyboard yesterday with room for two function keys and by coincidence this thread comes back to life. I am still trying to wrap my head around why nothing has been upstreamed in the last year. Are you saying all o the above can work with @drashna's patch alone?

@lordpixel23
Copy link

So I've updated Drasha's patch to work with the latest code from master branch. I can post a gist later if anyone is interested.

I bound it to a fn key on my keyboard and I can see the OS recognizes it. Fn lights up in Keyboard Viewer and if I tap it quickly it starts dictation. But that's about all it does. I have not defined a VID/PID so I was not expecting F Key support. But nothing is working at all. e.g. Globe+N does nothing but it does on my laptop's built in keyboard. Which is an old intel MacBook Pro.

Did I miss anything obvious?

@HVR88
Copy link

HVR88 commented Oct 20, 2024

Did you miss this comment earlier in the thread about SHARED_EP?

I've just tested with @drashna's patch. That was it, SHARED_EP gets me the same behavior you've seen. Globe/Fn + OTHER_KEYS works as mostly as expected. With F1-12 not working and Globe/Fn + Backspace not performing a DEL. This is also how this was described in the ZMK threads.

@lordpixel23
Copy link

Yes, thank you. SHARED_EP was the missing step. Now working as expected. I will make an up to date gist later.

Now, if only I could get the keyboard setting to let me use this thing as a generic modifier key.

@lordpixel23
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment