From 1dcf63058492b71da140ca5b69c2486e322b8176 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Oct 2025 13:46:27 +1000 Subject: [PATCH] plugins: add an example plugin on how to reconfigure the Copilot key As Microsoft has decreed, that key sends LEFTMETA + LEFTSHIFT + F23 (usually across multiple frames) and the then the same in reverse when released. xkeyboard-config 2.44 maps this sequence by default to XF86Assistant but for the use-cases where this does not work reshuffling the whole event sequence is the best approach and that can easily be done with a plugin. Note that this is the minimum effort plugin - it works for one keyboard at at time (duplicate the plugin if two keyboards are needed, or remove the vid/pid check) and it does *not* intercept the meta/shift key presses and delay them, it simply releases them on F23 and then replays the new sequence. Good enough for an example plugin. Example sequence produces shift + a on press and releases a + shift on release. Holding the key will thus produce AAAAAAAAA which is an excellent summary of how this key was designed. Part-of: --- plugins/10-copilot-key-override.lua | 71 +++++++++++++++++++++++++++++ plugins/meson.build | 1 + 2 files changed, 72 insertions(+) create mode 100644 plugins/10-copilot-key-override.lua diff --git a/plugins/10-copilot-key-override.lua b/plugins/10-copilot-key-override.lua new file mode 100644 index 00000000..0212213d --- /dev/null +++ b/plugins/10-copilot-key-override.lua @@ -0,0 +1,71 @@ +-- SPDX-License-Identifier: MIT +-- +-- This is an example libinput plugin +-- +-- This plugin detects the Copilot key on the keyboard with +-- the given VID/PID and replaces it with a different key (sequence). + +-- UNCOMMENT THIS LINE TO ACTIVATE THE PLUGIN +-- libinput:register({1}) + +-- Replace this with your keyboard's VID/PID +KEYBOARD_VID = 0x046d +KEYBOARD_PID = 0x4088 + +meta_is_down = false +shift_is_down = false + +-- shift-A, because you can never have enough screaming +replacement_sequence = { evdev.KEY_LEFTSHIFT, evdev.KEY_A } + +function frame(device, frame, _) + for _, v in ipairs(frame) do + if v.value ~= 2 then -- ignore key repeats + if v.usage == evdev.KEY_LEFTMETA then + meta_is_down = v.value == 1 + elseif v.usage == evdev.KEY_LEFTSHIFT then + shift_is_down = v.value == 1 + elseif v.usage == evdev.KEY_F23 and meta_is_down and shift_is_down then + -- We know from the MS requirements that F23 for copilot is + -- either last key (on press) or the first key (on release) + -- of the three-key sequence, and no other keys are + -- within this frame. + if v.value == 1 then + -- Release our modifiers first + device:prepend_frame({ + { usage = evdev.KEY_LEFTSHIFT, value = 0 }, + { usage = evdev.KEY_LEFTMETA, value = 0 }, + }) + -- Insert our replacement press sequence + local replacement_frame = {} + for _, rv in ipairs(replacement_sequence) do + table.insert(replacement_frame, { usage = rv, value = 1 }) + end + device:append_frame(replacement_frame) + else + -- Insert our replacement release sequence + local replacement_frame = {} + for idx = #replacement_sequence, 1, -1 do + table.insert(replacement_frame, { usage = replacement_sequence[idx], value = 0 }) + end + device:append_frame(replacement_frame) + + -- we don't care about re-pressing shift/meta because the + -- rest of the stack will filter the release for an + -- unpressed key anyway. + end + + return {} -- discard this frame + end + end + end +end + +function device_new(device) + local info = device:info() + if info.vid == KEYBOARD_VID and info.pid == KEYBOARD_PID then + device:connect("evdev-frame", frame) + end +end + +libinput:connect("new-evdev-device", device_new) diff --git a/plugins/meson.build b/plugins/meson.build index 8464a014..b502bce8 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -6,6 +6,7 @@ plugins = [ '10-pointer-go-slower.lua', '10-delay-motion.lua', '10-disable-feature.lua', + '10-copilot-key-override.lua', ] fs = import('fs')