Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 23 Apr 2026 05:10:59 +0000
From:      Vladimir Kondratyev <wulf@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Cc:        ~Not Toby <0x2b@0xff.art>
Subject:   git: c0e26f7b29dd - main - hkbd/ukbd: sysctls to swap macbook kbd modifiers
Message-ID:  <69e9a9e3.44079.76d25369@gitrepo.freebsd.org>

index | next in thread | raw e-mail

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)


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69e9a9e3.44079.76d25369>