git: c0e26f7b29dd - main - hkbd/ukbd: sysctls to swap macbook kbd modifiers
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 23 Apr 2026 05:10:59 UTC
The branch main has been updated by wulf:
URL: https://cgit.FreeBSD.org/src/commit/?id=c0e26f7b29ddd2d081ac113d64807849e87737a2
commit c0e26f7b29ddd2d081ac113d64807849e87737a2
Author: ~Not Toby <0x2b@0xff.art>
AuthorDate: 2026-04-23 05:10:05 +0000
Commit: Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2026-04-23 05:10:05 +0000
hkbd/ukbd: sysctls to swap macbook kbd modifiers
Many applications, desktop environments, window managers & text editors
favor the usage of Alt or Ctrl over Super (Cmd). On a Macbook it is
quite annoying that the Super (Cmd) key gets pride of place by the spacebar.
The standard MacBook Cmd key location only really makes sense for macOS
or maybe in some tiling wm if Mod4/Super is your main modifier.
For most mainstream desktops and window managers, having Alt or Ctrl
in that location makes much much more sense.
This patch adds two sysctls for swapping either Opt(Alt) or Ctrl with Cmd(Super).
Linux has similar sysctls to this; allowing a user to make an Apple
keyboard more "orthodox"/useful at a level that takes effect independent
of typing context - ie) tty, Xorg and/or wayland.
Having a sysctl to do these swaps means that a user doesn't have to faff
about with both creating a custom vt keymap AND figure out which magic
setxkbmap incantation one needs to make one's keyboard behave as desired
across environments.
Signed-off-by: ~Not Toby <0x2b@0xff.art>
Reviewed by: wulf
MFC after: 1 week
Pull Request: https://github.com/freebsd/freebsd-src/pull/2141
---
share/man/man4/hkbd.4 | 14 +++++++++++++-
share/man/man4/ukbd.4 | 14 +++++++++++++-
sys/dev/hid/hkbd.c | 38 ++++++++++++++++++++++++++++++++++++++
sys/dev/usb/input/ukbd.c | 38 ++++++++++++++++++++++++++++++++++++++
4 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/share/man/man4/hkbd.4 b/share/man/man4/hkbd.4
index 6e936b198618..4aefad10846c 100644
--- a/share/man/man4/hkbd.4
+++ b/share/man/man4/hkbd.4
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd September 12, 2020
+.Dd April 23, 2026
.Dt HKBD 4
.Os
.Sh NAME
@@ -151,6 +151,18 @@ tunables:
Debug output level, where 0 is debugging disabled and larger values increase
debug message verbosity.
Default is 0.
+.It Va hw.hid.hkbd.apple_swap_cmd_opt
+Swap the Command & Option keys on Apple keyboards if set to 1.
+Default is 0.
+.It Va hw.hid.hkbd.apple_swap_cmd_ctl
+Swap the Command & Control keys on Apple keyboards if set to 1.
+Default is 0.
+.It Va hw.hid.hkbd.apple_fn_mode
+Direct access to media keys without holding Fn if set to 1.
+Default is 0.
+.It Va hw.hid.hkbd.no_leds
+Disables setting of keyboard LEDs if set to 1.
+Default is 0.
.El
.Sh FILES
.Bl -tag -width ".Pa /dev/input/event*" -compact
diff --git a/share/man/man4/ukbd.4 b/share/man/man4/ukbd.4
index 14d6cca51e6f..003600aac084 100644
--- a/share/man/man4/ukbd.4
+++ b/share/man/man4/ukbd.4
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 24, 2018
+.Dd April 23, 2026
.Dt UKBD 4
.Os
.Sh NAME
@@ -156,6 +156,18 @@ tunables:
Debug output level, where 0 is debugging disabled and larger values increase
debug message verbosity.
Default is 0.
+.It Va hw.usb.ukbd.apple_swap_cmd_opt
+Swap the Command & Option keys on Apple keyboards if set to 1.
+Default is 0.
+.It Va hw.usb.ukbd.apple_swap_cmd_ctl
+Swap the Command & Control keys on Apple keyboards if set to 1.
+Default is 0.
+.It Va hw.usb.ukbd.apple_fn_mode
+Direct access to media keys without holding Fn if set to 1.
+Default is 0.
+.It Va hw.usb.ukbd.no_leds
+Disables setting of keyboard LEDs if set to 1.
+Default is 0.
.El
.Sh FILES
.Bl -tag -width ".Pa /dev/kbd*" -compact
diff --git a/sys/dev/hid/hkbd.c b/sys/dev/hid/hkbd.c
index c98f4be69169..edfd3f5d4eff 100644
--- a/sys/dev/hid/hkbd.c
+++ b/sys/dev/hid/hkbd.c
@@ -100,6 +100,8 @@ static int hkbd_debug = 0;
#endif
static int hkbd_no_leds = 0;
static int hkbd_apple_fn_mode = 0;
+static int hkbd_apple_swap_cmd_ctl = 0;
+static int hkbd_apple_swap_cmd_opt = 0;
static SYSCTL_NODE(_hw_hid, OID_AUTO, hkbd, CTLFLAG_RW, 0, "USB keyboard");
#ifdef HID_DEBUG
@@ -110,6 +112,10 @@ SYSCTL_INT(_hw_hid_hkbd, OID_AUTO, no_leds, CTLFLAG_RWTUN,
&hkbd_no_leds, 0, "Disables setting of keyboard leds");
SYSCTL_INT(_hw_hid_hkbd, OID_AUTO, apple_fn_mode, CTLFLAG_RWTUN,
&hkbd_apple_fn_mode, 0, "0 = Fn + F1..12 -> media, 1 = F1..F12 -> media");
+SYSCTL_INT(_hw_hid_hkbd, OID_AUTO, apple_swap_cmd_ctl, CTLFLAG_RWTUN,
+ &hkbd_apple_swap_cmd_ctl, 0, "Swap Command & Control keys");
+SYSCTL_INT(_hw_hid_hkbd, OID_AUTO, apple_swap_cmd_opt, CTLFLAG_RWTUN,
+ &hkbd_apple_swap_cmd_opt, 0, "Swap Command & Option keys");
#define INPUT_EPOCH global_epoch_preempt
@@ -664,6 +670,30 @@ hkbd_apple_fn_media(uint32_t keycode)
}
}
+static uint32_t
+hkbd_apple_doswap_cmd_ctl(uint32_t keycode)
+{
+ switch (keycode) {
+ case 0xe3: return 0xe0; /* LCMD -> LCTL */
+ case 0xe7: return 0xe4; /* RCMD -> RCTL */
+ case 0xe0: return 0xe3; /* LCTL -> LCMD */
+ case 0xe4: return 0xe7; /* RCTL -> RCMD */
+ default: return keycode;
+ }
+}
+
+static uint32_t
+hkbd_apple_doswap_cmd_opt(uint32_t keycode)
+{
+ switch (keycode) {
+ case 0xe3: return 0xe2; /* LCMD -> ROPT */
+ case 0xe7: return 0xe6; /* RCMD -> ROPT */
+ case 0xe2: return 0xe3; /* LOPT -> LCMD */
+ case 0xe6: return 0xe7; /* ROPT -> RCMD */
+ default: return keycode;
+ }
+}
+
static uint32_t
hkbd_apple_swap(uint32_t keycode)
{
@@ -765,6 +795,10 @@ hkbd_intr_callback(void *context, void *data, hid_size_t len)
key = hkbd_apple_fn(key);
if (apply_apple_fn_media)
key = hkbd_apple_fn_media(key);
+ if (hkbd_apple_swap_cmd_ctl)
+ key = hkbd_apple_doswap_cmd_ctl(key);
+ if (hkbd_apple_swap_cmd_opt)
+ key = hkbd_apple_doswap_cmd_opt(key);
if (sc->sc_flags & HKBD_FLAG_APPLE_SWAP)
key = hkbd_apple_swap(key);
if (key == KEY_NONE || key >= HKBD_NKEYCODE)
@@ -780,6 +814,10 @@ hkbd_intr_callback(void *context, void *data, hid_size_t len)
key = hkbd_apple_fn(key);
if (apply_apple_fn_media)
key = hkbd_apple_fn_media(key);
+ if (hkbd_apple_swap_cmd_ctl)
+ key = hkbd_apple_doswap_cmd_ctl(key);
+ if (hkbd_apple_swap_cmd_opt)
+ key = hkbd_apple_doswap_cmd_opt(key);
if (sc->sc_flags & HKBD_FLAG_APPLE_SWAP)
key = hkbd_apple_swap(key);
if (key == KEY_NONE || key == KEY_ERROR || key >= HKBD_NKEYCODE)
diff --git a/sys/dev/usb/input/ukbd.c b/sys/dev/usb/input/ukbd.c
index 7a33a9ad2efe..104e51c082c3 100644
--- a/sys/dev/usb/input/ukbd.c
+++ b/sys/dev/usb/input/ukbd.c
@@ -99,6 +99,8 @@ static int ukbd_debug = 0;
static int ukbd_no_leds = 0;
static int ukbd_pollrate = 0;
static int ukbd_apple_fn_mode = 0;
+static int ukbd_apple_swap_cmd_ctl = 0;
+static int ukbd_apple_swap_cmd_opt = 0;
static SYSCTL_NODE(_hw_usb, OID_AUTO, ukbd, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"USB keyboard");
@@ -112,6 +114,10 @@ SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, pollrate, CTLFLAG_RWTUN,
&ukbd_pollrate, 0, "Force this polling rate, 1-1000Hz");
SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, apple_fn_mode, CTLFLAG_RWTUN,
&ukbd_apple_fn_mode, 0, "0 = Fn + F1..12 -> media, 1 = F1..F12 -> media");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, apple_swap_cmd_ctl, CTLFLAG_RWTUN,
+ &ukbd_apple_swap_cmd_ctl, 0, "Swap Command & Control keys");
+SYSCTL_INT(_hw_usb_ukbd, OID_AUTO, apple_swap_cmd_opt, CTLFLAG_RWTUN,
+ &ukbd_apple_swap_cmd_opt, 0, "Swap Command & Option keys");
#define UKBD_EMULATE_ATSCANCODE 1
#define UKBD_DRIVER_NAME "ukbd"
@@ -720,6 +726,30 @@ ukbd_apple_fn_media(uint32_t keycode)
}
}
+static uint32_t
+ukbd_apple_doswap_cmd_ctl(uint32_t keycode)
+{
+ switch (keycode) {
+ case 0xe3: return 0xe0; /* LCMD -> LCTL */
+ case 0xe7: return 0xe4; /* RCMD -> RCTL */
+ case 0xe0: return 0xe3; /* LCTL -> LCMD */
+ case 0xe4: return 0xe7; /* RCTL -> RCMD */
+ default: return keycode;
+ }
+}
+
+static uint32_t
+ukbd_apple_doswap_cmd_opt(uint32_t keycode)
+{
+ switch (keycode) {
+ case 0xe3: return 0xe2; /* LCMD -> ROPT */
+ case 0xe7: return 0xe6; /* RCMD -> ROPT */
+ case 0xe2: return 0xe3; /* LOPT -> LCMD */
+ case 0xe6: return 0xe7; /* ROPT -> RCMD */
+ default: return keycode;
+ }
+}
+
static uint32_t
ukbd_apple_swap(uint32_t keycode)
{
@@ -839,6 +869,10 @@ ukbd_intr_callback(struct usb_xfer *xfer, usb_error_t error)
key = ukbd_apple_fn(key);
if (apply_apple_fn_media)
key = ukbd_apple_fn_media(key);
+ if (ukbd_apple_swap_cmd_ctl)
+ key = ukbd_apple_doswap_cmd_ctl(key);
+ if (ukbd_apple_swap_cmd_opt)
+ key = ukbd_apple_doswap_cmd_opt(key);
if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP)
key = ukbd_apple_swap(key);
if (key == KEY_NONE || key >= UKBD_NKEYCODE)
@@ -853,6 +887,10 @@ ukbd_intr_callback(struct usb_xfer *xfer, usb_error_t error)
key = ukbd_apple_fn(key);
if (apply_apple_fn_media)
key = ukbd_apple_fn_media(key);
+ if (ukbd_apple_swap_cmd_ctl)
+ key = ukbd_apple_doswap_cmd_ctl(key);
+ if (ukbd_apple_swap_cmd_opt)
+ key = ukbd_apple_doswap_cmd_opt(key);
if (sc->sc_flags & UKBD_FLAG_APPLE_SWAP)
key = ukbd_apple_swap(key);
if (key == KEY_NONE || key == KEY_ERROR || key >= UKBD_NKEYCODE)