Subject: 27.2; XELB under EXWM stops processing all keyboard events

From: Maze <[email-masked]>
Date: Mon, 25 Apr 2022 01:16:49 +0800

Sorry if this is not be the right place to report: this is a bug of
GNU ELPA package XELB running under EXWM, and not a bug of vanilla
emacs. However it seems the official place to report EXWM is on Github,
and, unless I missed something, it won't let me report a bug there
unless I create a github account, which I won't.

Description: after hitting a key from the number row, EXWM acting as the
X Window Manager stops processing all keyboard input. Emacs and
all other graphical programs lose keyboard input. For me this happens
always after hitting any key from the numbers row of my
keyboard. Specifics may very possibly vary for other users but my
informed guess is that the risk is existing for all keyboard
configurations, as I will try to explain.

Cause: a backtrace is generated showing that an array of xkb keytypes
was referenced out of its range in the method xcb:keysyms:keycode->keysym.
I included this backtrace at the end of this message.

This situation develops for the following reason:
When initializing EXWM, XELB creates a keyboard representation including
2 arrays for keysyms and keytypes. After I hit a first key and start using the
WM, XELB processes an XkbNewKeyBoardNotify event. XELB reacts to this
event by creating a new keysyms array without creating a new keytypes
array. Because the original keyboard had 28 keytypes but the new
keyboard has 29 keytypes in Xkb, and since my number row keys get associated
to the highest index keytype of the new keyboard, this causes the legacy
keytypes array to be accessed out of range by one index after I hit one
of the number row keys.

Solution: I propose a patch. I'm not sure it's absolutely the best thing
to do but it is tested on my configuration and I think it probably
should not cause breaks for other users: calling the method 
xcb:keysyms:-update-keytypes with correct parameters is in effect
retrieving information from Xkb and putting XELB in sync. My guess 
is that the logic which drives this condition of
xcb:keysyms:-update-keycodes to be called alone without
calling xcb:keysyms:-update-keytype is not sanely selecting only
cases where it would be a good idea to skip the call... At this 
stage of my understanding, calling both methods has to be safe in all 
cases, and solves the severe crash of the WM that I experience. 

 * in method xcb:keysyms:-on-NewKeyboardNotify, update keytypes of 
   new keyboard in order to prevent the WM from crashing when 
   processing keycodes

diff --git a/xcb-keysyms.el b/xcb-keysyms.el
index af4f97b..2b7f89f 100644
--- a/xcb-keysyms.el
+++ b/xcb-keysyms.el
@@ -161,6 +161,7 @@ This method must be called before using any other method in this module."
             (setq device (xcb:-get-extra-plist obj 'keysyms deviceID))
             (when (and device
                        (not (slot-value device 'updated)))
+              (xcb:keysyms:-update-keytypes obj deviceID)
               (xcb:keysyms:-update-keycodes obj deviceID)
               (when (= deviceID device-id)
                 (setq updated t)

Below is the backtrace that I obtained when I hit a key from the numbers
row while EXWM is managing X windows:

window-list xcb-debug:buffer t ...))) (while --dolist-tail-- (let (...)
(if ... ...) (setq --dolist-tail-- ...)))) (save-excursion (goto-char
(point-max)) (let ((standard-output ...)) (backtrace))) (let
((--dolist-tail-- windows-eob)) (while --dolist-tail-- (let (...)
(set-window-point w ...) (setq --dolist-tail-- ...)))))))(error
(args-out-of-range [#<xcb:xkb:KeyType xcb:xkb:KeyType-d97a0c>
#<xcb:xkb:KeyType xcb:xkb:KeyType-d97a20> #<xcb:xkb:KeyType
xcb:xkb:KeyType-c84518> #<xcb:xkb:KeyType xcb:xkb:KeyType-ca101c>
#<xcb:xkb:KeyType xcb:xkb:KeyType-c4e190> #<xcb:xkb:KeyType
xcb:xkb:KeyType-cbc07c> #<xcb:xkb:KeyType xcb:xkb:KeyType-c4e150>
#<xcb:xkb:KeyType xcb:xkb:KeyType-c8d79c> #<xcb:xkb:KeyType
xcb:xkb:KeyType-c96104> #<xcb:xkb:KeyType xcb:xkb:KeyType-c9207c>
#<xcb:xkb:KeyType xcb:xkb:KeyType-d0c5b8> #<xcb:xkb:KeyType
xcb:xkb:KeyType-d9620c> #<xcb:xkb:KeyType xcb:xkb:KeyType-d96220>
#<xcb:xkb:KeyType xcb:xkb:KeyType-c8d774> #<xcb:xkb:KeyType
xcb:xkb:KeyType-c14764> #<xcb:xkb:KeyType xcb:xkb:KeyType-be9f50>
#<xcb:xkb:KeyType xcb:xkb:KeyType-ce2990> #<xcb:xkb:KeyType
xcb:xkb:KeyType-c149b0> #<xcb:xkb:KeyType xcb:xkb:KeyType-c142dc>
#<xcb:xkb:KeyType xcb:xkb:KeyType-cbbd98> #<xcb:xkb:KeyType
xcb:xkb:KeyType-d97430> #<xcb:xkb:KeyType xcb:xkb:KeyType-be8d74>
#<xcb:xkb:KeyType xcb:xkb:KeyType-be8e00> #<xcb:xkb:KeyType
xcb:xkb:KeyType-b9aa80> #<xcb:xkb:KeyType xcb:xkb:KeyType-c4e424>
#<xcb:xkb:KeyType xcb:xkb:KeyType-cde514> #<xcb:xkb:KeyType
xcb:xkb:KeyType-c37118> #<xcb:xkb:KeyType xcb:xkb:KeyType-d11e10>] 28))
  aref([#<xcb:xkb:KeyType xcb:xkb:KeyType-d97a0c> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-d97a20> #<xcb:xkb:KeyType xcb:xkb:KeyType-c84518>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-ca101c> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-c4e190> #<xcb:xkb:KeyType xcb:xkb:KeyType-cbc07c>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-c4e150> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-c8d79c> #<xcb:xkb:KeyType xcb:xkb:KeyType-c96104>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-c9207c> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-d0c5b8> #<xcb:xkb:KeyType xcb:xkb:KeyType-d9620c>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-d96220> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-c8d774> #<xcb:xkb:KeyType xcb:xkb:KeyType-c14764>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-be9f50> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-ce2990> #<xcb:xkb:KeyType xcb:xkb:KeyType-c149b0>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-c142dc> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-cbbd98> #<xcb:xkb:KeyType xcb:xkb:KeyType-d97430>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-be8d74> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-be8e00> #<xcb:xkb:KeyType xcb:xkb:KeyType-b9aa80>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-c4e424> #<xcb:xkb:KeyType
  xcb:xkb:KeyType-cde514> #<xcb:xkb:KeyType xcb:xkb:KeyType-c37118>
  #<xcb:xkb:KeyType xcb:xkb:KeyType-d11e10>] 28)
    (setq keytype (aref (slot-value object 'keytypes) (elt (slot-value
    keycode 'kt-index) group)))
      (catch 'return (if (<= (slot-value object 'min-keycode) keycode
      (slot-value object 'max-keycode)) nil (throw 'return '(0 . 0)))
      (setq keycode (aref (slot-value object 'keycodes) (- keycode
      (slot-value object 'min-keycode))) group-info (slot-value keycode
      'groupInfo) group-number (logand group-info 15)) (if (=
      group-number 0) (progn (throw 'return '(0 . 0)))) (setq group (if
      (null modifiers) 0 (logand (lsh modifiers -13) 3))) (if (>= group
      group-number) (progn (let* ((val (logand group-info 192))) (cond
      ((eq val 'xcb:xkb:GroupsWrap:RedirectIntoRange) (setq group
      (logand 255 ...)) (if (>= group group-number) (progn ...))) ((eq
      val 'xcb:xkb:GroupsWrap:ClampIntoRange) (setq group (1-
      group-number))) (t (setq group (% group group-number))))))) (setq
      index (* group (slot-value keycode 'width))) (progn (message
      "KEYSTROKE keysym: %s kt-index: %s" (slot-value keycode 'syms)
      (slot-value keycode 'kt-index)) (let* ((object keycode)) (message
      "KEYSTROKE keysym: %s kt-index: %s" (slot-value object 'syms)
      (slot-value object 'kt-index))) (message "number of keysyms: %s"
      (length (slot-value object 'keycodes))) (message "number of
      keytypes: %s" (length (slot-value object 'keytypes))) (let*
      ((--cl-vec-- (slot-value object 'keycodes)) (--cl-idx-- -1) (code
      nil)) (while (< (setq --cl-idx-- (1+ --cl-idx--)) (length
      --cl-vec--)) (setq code (aref --cl-vec-- --cl-idx--)) (if code
      (progn (let* (...) (if ... ...))))) nil)) (setq keytype (aref
      (slot-value object 'keytypes) (elt (slot-value keycode 'kt-index)
      group))) (let* ((object keytype)) (if (null modifiers) (delq nil
      (mapcar #'(lambda (entry) (if ... ...)) (slot-value object 'map)))
      (catch 'break (let ((--dolist-tail-- (slot-value object ...)))
      (while --dolist-tail-- (let (...) (let* ... ...) (setq
      --dolist-tail-- ...))))) (cons (elt (slot-value keycode 'syms)
      index) (logand (slot-value object 'mods-mask) (lognot
      preserve))))))
        (let* ((object (xcb:keysyms:-get-current-device obj))) (catch
        'return (if (<= (slot-value object 'min-keycode) keycode
        (slot-value object 'max-keycode)) nil (throw 'return '(0 . 0)))
        (setq keycode (aref (slot-value object 'keycodes) (- keycode
        (slot-value object 'min-keycode))) group-info (slot-value
        keycode 'groupInfo) group-number (logand group-info 15)) (if (=
        group-number 0) (progn (throw 'return '(0 . 0)))) (setq group
        (if (null modifiers) 0 (logand (lsh modifiers -13) 3))) (if (>=
        group group-number) (progn (let* ((val (logand group-info 192)))
        (cond ((eq val ...) (setq group ...) (if ... ...)) ((eq val ...)
        (setq group ...)) (t (setq group ...)))))) (setq index (* group
        (slot-value keycode 'width))) (progn (message "KEYSTROKE keysym:
        %s kt-index: %s" (slot-value keycode 'syms) (slot-value keycode
        'kt-index)) (let* ((object keycode)) (message "KEYSTROKE keysym:
        %s kt-index: %s" (slot-value object 'syms) (slot-value object
        'kt-index))) (message "number of keysyms: %s" (length
        (slot-value object 'keycodes))) (message "number of keytypes:
        %s" (length (slot-value object 'keytypes))) (let* ((--cl-vec--
        (slot-value object 'keycodes)) (--cl-idx-- -1) (code nil))
        (while (< (setq --cl-idx-- (1+ --cl-idx--)) (length --cl-vec--))
        (setq code (aref --cl-vec-- --cl-idx--)) (if code (progn (let*
        ... ...)))) nil)) (setq keytype (aref (slot-value object
        'keytypes) (elt (slot-value keycode 'kt-index) group))) (let*
        ((object keytype)) (if (null modifiers) (delq nil (mapcar
        #'(lambda ... ...) (slot-value object 'map))) (catch 'break (let
        ((--dolist-tail-- ...)) (while --dolist-tail-- (let
        ... ... ...)))) (cons (elt (slot-value keycode 'syms) index)
        (logand (slot-value object 'mods-mask) (lognot preserve)))))))
          (let ((preserve 0) group group-info group-number index
          keytype) (let* ((object (xcb:keysyms:-get-current-device
          obj))) (catch 'return (if (<= (slot-value object 'min-keycode)
          keycode (slot-value object 'max-keycode)) nil (throw 'return
          '(0 . 0))) (setq keycode (aref (slot-value object 'keycodes)
          (- keycode (slot-value object 'min-keycode))) group-info
          (slot-value keycode 'groupInfo) group-number (logand
          group-info 15)) (if (= group-number 0) (progn (throw 'return
          '(0 . 0)))) (setq group (if (null modifiers) 0 (logand (lsh
          modifiers -13) 3))) (if (>= group group-number) (progn (let*
          ((val ...)) (cond (... ... ...) (... ...) (t ...))))) (setq
          index (* group (slot-value keycode 'width))) (progn (message
          "KEYSTROKE keysym: %s kt-index: %s" (slot-value keycode 'syms)
          (slot-value keycode 'kt-index)) (let* ((object keycode))
          (message "KEYSTROKE keysym: %s kt-index: %s" (slot-value
          object 'syms) (slot-value object 'kt-index))) (message "number
          of keysyms: %s" (length (slot-value object 'keycodes)))
          (message "number of keytypes: %s" (length (slot-value object
          'keytypes))) (let* ((--cl-vec-- (slot-value object ...))
          (--cl-idx-- -1) (code nil)) (while (< (setq --cl-idx-- ...)
          (length --cl-vec--)) (setq code (aref --cl-vec-- --cl-idx--))
          (if code (progn ...))) nil)) (setq keytype (aref (slot-value
          object 'keytypes) (elt (slot-value keycode 'kt-index) group)))
          (let* ((object keytype)) (if (null modifiers) (delq nil
          (mapcar #'... (slot-value object ...))) (catch 'break (let
          (...) (while --dolist-tail-- ...))) (cons (elt (slot-value
          keycode ...) index) (logand (slot-value object ...) (lognot
          preserve))))))))
            (progn (let ((preserve 0) group group-info group-number
            index keytype) (let* ((object
            (xcb:keysyms:-get-current-device obj))) (catch 'return (if
            (<= (slot-value object 'min-keycode) keycode (slot-value
            object 'max-keycode)) nil (throw 'return '(0 . 0))) (setq
            keycode (aref (slot-value object 'keycodes) (- keycode
            (slot-value object ...))) group-info (slot-value keycode
            'groupInfo) group-number (logand group-info 15)) (if (=
            group-number 0) (progn (throw 'return '...))) (setq group
            (if (null modifiers) 0 (logand (lsh modifiers -13) 3))) (if
            (>= group group-number) (progn (let* (...) (cond
            ... ... ...)))) (setq index (* group (slot-value keycode
            'width))) (progn (message "KEYSTROKE keysym: %s kt-index:
            %s" (slot-value keycode 'syms) (slot-value keycode
            'kt-index)) (let* ((object keycode)) (message "KEYSTROKE
            keysym: %s kt-index: %s" (slot-value object ...) (slot-value
            object ...))) (message "number of keysyms: %s" (length
            (slot-value object ...))) (message "number of keytypes: %s"
            (length (slot-value object ...))) (let* ((--cl-vec-- ...)
            (--cl-idx-- -1) (code nil)) (while (< ... ...) (setq code
            ...) (if code ...)) nil)) (setq keytype (aref (slot-value
            object 'keytypes) (elt (slot-value keycode ...) group)))
            (let* ((object keytype)) (if (null modifiers) (delq nil
            (mapcar ... ...)) (catch 'break (let ... ...)) (cons (elt
            ... index) (logand ... ...))))))))
              (closure (t) (obj keycode modifiers) "Convert KEYCODE to
              keysym or get possible modifier..." (progn (let ((preserve
              0) group group-info group-number index keytype) (let*
              ((object (xcb:keysyms:-get-current-device obj))) (catch
              'return (if (<= ... keycode ...) nil (throw ... ...))
              (setq keycode (aref ... ...) group-info (slot-value
              keycode ...) group-number (logand group-info 15)) (if (=
              group-number 0) (progn ...)) (setq group (if ... 0 ...))
              (if (>= group group-number) (progn ...)) (setq index (*
              group ...)) (progn (message "KEYSTROKE keysym: %s
              kt-index: %s" ... ...) (let* ... ...) (message "number of
              keysyms: %s" ...) (message "number of keytypes: %s" ...)
              (let* ... ... nil)) (setq keytype (aref ... ...)) (let*
              (...) (if ... ... ... ...)))))))(#<xcb:connection
              xcb:connection-ca0fd0> 20 16)
                apply((closure (t) (obj keycode modifiers) "Convert
                KEYCODE to keysym or get possible modifier..." (progn
                (let ((preserve 0) group group-info group-number index
                keytype) (let* ((object (xcb:keysyms:-get-current-device
                obj))) (catch 'return (if (<= ... keycode ...) nil
                (throw ... ...)) (setq keycode (aref ... ...) group-info
                (slot-value keycode ...) group-number (logand group-info
                15)) (if (= group-number 0) (progn ...)) (setq group (if
                ... 0 ...)) (if (>= group group-number) (progn ...))
                (setq index (* group ...)) (progn (message "KEYSTROKE
                keysym: %s kt-index: %s" ... ...) (let* ... ...)
                (message "number of keysyms: %s" ...) (message "number
                of keytypes: %s" ...) (let* ... ... nil)) (setq keytype
                (aref ... ...)) (let* (...) (if ... ... ... ...)))))))
                #<xcb:connection xcb:connection-ca0fd0> (20 16))
                  xcb:keysyms:keycode->keysym(#<xcb:connection
                  xcb:connection-ca0fd0> 20 16)