So as you might know, with high end QMK / Vial keyboards you can install custom firmware. Typically you power it on or reboot it into firmware load mode where you can mount the device as a file system. At this point it's not really a keyboard, it's a USB storage device. You copy your firmware, it reboots and now it's a keyboard again.
Well I noticed that exwm is freezing with weird errors when this happens:
slot-value(nil present)
#f(compiled-function (obj device-id &optional first-keytype count) "Update key types.\n\nFIRST-KEYTYPE and count specify the range of key types to update." #<bytecode 0x1188a8a544cf439b>)(#<xcb:connection xcb:connection-1031da4f817b> 18 0 27)
apply(#f(compiled-function (obj device-id &optional first-keytype count) "Update key types.\n\nFIRST-KEYTYPE and count specify the range of key types to update." #<bytecode 0x1188a8a544cf439b>) #<xcb:connection xcb:connection-1031da4f817b> (18 0 27))
xcb:keysyms:-update-keytypes(#<xcb:connection xcb:connection-1031da4f817b> 18 0 27)
#f(compiled-function (obj data) "Handle \\='MapNotify' event." #<bytecode -0x42d47f7fd2694ec>)(#<xcb:connection xcb:connection-1031da4f817b> [85 1 255 23 254 103 5 0 18 0 243 0 8 255 0 27 8 248 8 196 8 248 8 248 8 248 64 10 63 0 0 0])
apply(#f(compiled-function (obj data) "Handle \\='MapNotify' event." #<bytecode -0x42d47f7fd2694ec>) #<xcb:connection xcb:connection-1031da4f817b> [85 1 255 23 254 103 5 0 18 0 243 0 8 255 0 27 8 248 8 196 8 248 8 248 8 248 64 10 63 0 0 0])
xcb:keysyms:-on-MapNotify(#<xcb:connection xcb:connection-1031da4f817b> [85 1 255 23 254 103 5 0 18 0 243 0 8 255 0 27 8 248 8 196 8 248 8 248 8 248 64 10 63 0 0 0])
#f(compiled-function (data _) #<bytecode 0x1f6499d2dca92ad8>)([85 1 255 23 254 103 5 0 18 0 243 0 8 255 0 27 8 248 8 196 8 248 8 248 8 248 64 10 63 0 0 0] nil)
#f(compiled-function (conn) "Process cached events." #<bytecode -0x599268f184c832e>)(#<xcb:connection xcb:connection-1031da4f817b>)
Apparently, because it's a hybrid device, sometimes it's a keyboard, sometimes it isn't, the code crashes. This is what the AI explains it as:
The Request: (xcb:+request-unchecked+reply obj (make-instance 'xcb:xkb:GetMap ...)) is sent to the X server for the new device.
The Server's Reply: Because the RP2040 is a weird hybrid device, the X server returns a GetMap reply where present is 0 (or otherwise invalid for KeyTypes), yet EXWM proceeded to the cl-assert.
The Failure: The cl-assert (or a subsequent logic step) expects data that didn't arrive. When (xcb:-get-extra-plist obj 'keysyms deviceID) returns nil (because the device wasn't fully initialized), and (make-instance 'xcb:keysyms:-device) isn't properly guarded, the code later attempts to access slots on a nil object.
(setq device (or (xcb:-get-extra-plist obj 'keysyms deviceID)
(make-instance 'xcb:keysyms:-device)))
(with-slots (keytypes) device
(when (or (/= 0 full)
(not keytypes))
(setf keytypes (make-vector totalTypes nil))) ;; <--- THE CRASH IS HERE
So apparently this xcb code should be checking if it gets nil here, and behaving better.
(cl-defmethod xcb:keysyms:-update-keytypes ((obj xcb:connection) device-id
&optional first-keytype count)
"Update key types.
FIRST-KEYTYPE and count specify the range of key types to update."
(let (device full partial)
(if (and first-keytype count)
(setq full 0
partial xcb:xkb:MapPart:KeyTypes)
(setq full xcb:xkb:MapPart:KeyTypes
partial 0
first-keytype 0
count 0))
(with-slots (deviceID present firstType nTypes totalTypes types-rtrn)
(xcb:+request-unchecked+reply obj
(make-instance 'xcb:xkb:GetMap
:deviceSpec device-id
:full full
:partial partial
:firstType first-keytype
:nTypes count
:firstKeySym 0
:nKeySyms 0
:firstKeyAction 0
:nKeyActions 0
:firstKeyBehavior 0
:nKeyBehaviors 0
:virtualMods 0
:firstKeyExplicit 0
:nKeyExplicit 0
:firstModMapKey 0
:nModMapKeys 0
:firstVModMapKey 0
:nVModMapKeys 0))
(cl-assert (/= 0 (logand present xcb:xkb:MapPart:KeyTypes)))
(setq device (or (xcb:-get-extra-plist obj 'keysyms deviceID)
(make-instance 'xcb:keysyms:-device)))
(with-slots (keytypes) device
(when (or (/= 0 full)
(not keytypes))
(setf keytypes (make-vector totalTypes nil)))
(setf keytypes (vconcat (substring keytypes 0 firstType)
types-rtrn
(substring keytypes (min (+ firstType nTypes)
totalTypes)))))
(xcb:-set-extra-plist obj 'keysyms deviceID device)
deviceID)))
AI says: Here is why this crashes:
The device is nil: If xcb:-get-extra-plist returns nil and (make-instance 'xcb:keysyms:-device) somehow fails to return a valid instance (or returns nil due to a separate XCB failure that the constructor didn't catch), device becomes nil.
with-slots is blind: The with-slots macro in Common Lisp (and EIEIO) expands into slot-value calls on the device object. When it expands, it essentially calls (slot-value nil 'keytypes).
The Error: That is precisely why you see slot-value(nil present) in your backtrace. The code is trying to access the 'present' slot (or 'keytypes' slot) on a variable (device) that holds nil because the initialization logic failed to produce a valid XCB object for your RP2040 device.
If you want to stop the crash without disabling keyboard features, you should wrap the with-slots call in a null-check:
(setq device (or (xcb:-get-extra-plist obj 'keysyms deviceID)
(make-instance 'xcb:keysyms:-device)))
(when device ;; <--- Add this guard
(with-slots (keytypes) device
(when (or (/= 0 full)
(not keytypes))
(setf keytypes (make-vector totalTypes nil)))
;; ... rest of logic
))
The "Death Spiral" of an Event Loop
When you plug in the device, the X server sends a MapNotify event. This triggers a chain reaction:
The Event is Received: xcb:-connection-filter picks up the event.
The Crash: It calls your function, which hits the nil error.
The Uncaught Exception: Because this is inside the event filter, the error isn't caught. It propagates up, causing the XELB event handler to abort the current execution, but—and this is critical—the X11 event remains unacknowledged in the X11 queue.
The Infinite Loop: Because the event wasn't cleared, the X server assumes the client (EXWM) didn't receive it, or it simply keeps broadcasting the state. The next "refresh" cycle or the next event tick sees the same event waiting. It tries to process it again, crashes again, and the X server continues to hammer the connection with the same event.
So as you might know, with high end QMK / Vial keyboards you can install custom firmware. Typically you power it on or reboot it into firmware load mode where you can mount the device as a file system. At this point it's not really a keyboard, it's a USB storage device. You copy your firmware, it reboots and now it's a keyboard again.
Well I noticed that exwm is freezing with weird errors when this happens:
Apparently, because it's a hybrid device, sometimes it's a keyboard, sometimes it isn't, the code crashes. This is what the AI explains it as:
So apparently this xcb code should be checking if it gets nil here, and behaving better.