MacOS – Linux on Mac hardware: How to remove caps lock delay on Apple MacBook Pro aluminum keyboard when booted to Linux

keyboardmacbook promacosunix

Apple's MacBook Pro has a slight delay on the caps lock key.
That is, the caps lock key has to be held down slightly longer
than normal to register the key press required to engage the caps lock.

This is super annoying. Does anyone know how to stop it from happening?

(Above copied over from stackoverflow as it was closed there as "off topic".)

To elaborate further:
Apple deems this a feature, and their knowledge base article does not disclose how to disable this delay.

However, I have reason to believe it is possible to do so.

Notably, I have found that, at least in my experience, if you remap the caps-lock key on Mac OS X (in System Preferences .. Keyboard .. Modifier Keys), and e.g. map it to Control, then the delay goes away while I am logged into Mac OS X.

My problem is that the delay remains when I boot into Ubuntu Linux, and in that context, even when I remap the Caps Lock key to Control, the delay is still present.

So, the question is: How is Apple disabling the delay, and more importantly, how can one replicate that act in the context of a Linux installation atop the laptop?

Update: There is a thread on superuser that may provide workarounds. I have not yet tried the suggestions there (namely: (1) toggling CapsLock-NoAction off/on, and (2) a firmware upgrade). I cannot tell from the context of that thread whether the workarounds have been tested on an Ubuntu installation.

Best Answer

I've figured out how to do this. In short, you must send a "Feature Report" consisting of the bytes 0x9, 0x0, 0x0, 0x0 to the appropriate hidraw device as root.

You can find the right hidraw device with this command:

dmesg | grep Apple | grep Keyboard | grep input0 | tail -1 | sed -e 's/.*hidraw\([[:digit:]]\+\).*/\/dev\/hidraw\1/'

The code to send the magic control packet is below. Compiles with gcc, takes the hidraw device as parameter. So the entire flow is:

  1. save the code below as disable-capslock-delay.c
  2. gcc -o disable-capslock-delay disable-capslock-delay.c
  3. HIDDEVICE=$(dmesg | grep Apple | grep Keyboard | grep input0 | tail -1 | sed -e 's/.*hidraw\([[:digit:]]\+\).*/\/dev\/hidraw\1/')
  4. sudo ./disable-capslock-delay $HIDDEVICE

Steps 3 and 4 have to be done every time you reboot (or unplug and re-plug the keyboard); you can put them into /etc/rc.local (or your distro's equivalent) to execute them at boot (you don't need sudo in that case; and you might want to move the compiled binary into /usr/local/sbin/ or something).

I've put in some safety checks for vendor ID, device ID, and report descriptor length. You may have to change the latter two if your model differs from mine.


#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) { if (argc != 2 || strcmp(argv[1], "-h") == 0) { printf("Pass a hidraw device as the first and only parameter!\n"); printf("You may find the right device with:\n"); printf(" dmesg | grep Apple | grep Keyboard | grep input0 | tail -1 | " "sed -e 's/.hidraw\([[:digit:]]\+\)./\/dev\/hidraw\1/'\n"); return 1; } int fd, i, res, desc_size = 0; char buf[256]; struct hidraw_devinfo info; char *device = argv[1]; fd = open(device, O_RDWR | O_NONBLOCK); if (fd < 0) { perror("Unable to open device"); return 1; } memset(&info, 0, sizeof(info)); memset(buf, 0, sizeof(buf)); // Get Report Descriptor Size res = ioctl(fd, HIDIOCGRDESCSIZE, &desc_size); if (res < 0) { perror("HIDIOCGRDESCSIZE"); } if (desc_size != 75) { printf("Error: unexpected descriptor size %d; you've probably got " "the wrong hidraw device!\n", desc_size); return 1; } // Get Raw Info res = ioctl(fd, HIDIOCGRAWINFO, &info); if (res < 0) { perror("HIDIOCGRAWINFO"); } else { if (info.vendor != 0x05ac) { printf("Error: Wrong vendor ID, make sure you got the right " "hidraw device!\n"); return 1; } if (info.product != 0x0250) { printf("Warning: Unknown product ID 0x%x!\n", info.product); } } // Get Feature buf[0] = 0x09; // Report Number res = ioctl(fd, HIDIOCGFEATURE(256), buf); if (res < 0) { perror("HIDIOCGFEATURE"); } else { printf("HID Feature Report (before change):\n\t"); for (i = 0; i < res; i++) printf("%hhx ", buf[i]); puts("\n"); } // Set Feature buf[0] = 0x09; // Report Number buf[1] = 0x00; // Report data buf[2] = 0x00; // padding buf[3] = 0x00; // padding res = ioctl(fd, HIDIOCSFEATURE(4), buf); if (res < 0) { perror("HIDIOCSFEATURE"); } else { printf("Caps lock delay disabled.\n"); } // Get Feature buf[0] = 0x09; // Report Number res = ioctl(fd, HIDIOCGFEATURE(256), buf); if (res < 0) { perror("HIDIOCGFEATURE"); } else { printf("HID Feature Report (after change):\n\t"); for (i = 0; i < res; i++) printf("%hhx ", buf[i]); puts("\n"); } close(fd); return 0; }