System Crafters

EXWM Hacking Notes

This is a set of notes that are being compiled as a part of the Hack Sessions series of streams!


Find a good EXWM bug to investigate for next time

Repository Links

Here are the official EXWM repo links:

If you'd like to contribute to my fork, you can check it out here:

EXWM Overview

The files:


exwm-init is the entrypoint for setting up EXWM and connecting to the X Window System server.

Many update functions used internally

Part of the initialization process is to initialize modules like ICCCM and EWMH via functions like xcb:ewmh:init, these seem to be focused around fetching the IDs for well-known Atoms. Once they've been fetched, those subsystems are considered to be initialized.

Other files exwm-cm.el exwm-config.el exwm-core.el exwm-floating.el exwm-input.el exwm-layout.el exwm-manage.el exwm-randr.el exwm-systemtray.el exwm-workspace.el exwm-xim.el xinitrc

XELB Overview

Generates elisp libraries for the X11 protocol with the el_client.el file, driven by the Makefile

A small portion of the files are generated but many are helper libraries for specific parts of the X11 protocol.


xcb xcb tutorial XML-XCB format for describing the X Window System protocol

X Window System

This is the client/server system which handles drawing to the screen, mouse/keyboard input, etc.

You communicate with it via an XML-based protocol.


This seems to be a set of conventions for commuication between clients in the X Window System

Describes window manager interactions at a low level


Extended Window Manager Hints

An extended set of conventions for modern desktop environments (see Non-ICCCM features section)

Assumption: the window manager is the "root window" and anything in this document that describes messages handled by the root window are actually handled by the window manager.

Testing with Xephyr


Here's a simple script that can help with testing:

Xephyr :1 -ac -dpi 180 -screen 1920x1080 &
DISPLAY=:1 emacs
pkill Xephyr

I'm also using this Emacs configuration for testing purposes:

(add-to-list 'load-path (expand-file-name "/home/daviwil/Projects/Code/xelb"))
(add-to-list 'load-path (expand-file-name "/home/daviwil/Projects/Code/exwm"))

(load-theme 'deeper-blue t)

(require 'exwm)

;; Allow C+lmb to move windows
(setq exwm-input-move-event 'C-down-mouse-1)

;; Set some global keys
(setq exwm-input-global-keys
      `(([?\C-c ?x ?r] . exwm-reset)
        ([?\C-c ?x ?i] . exwm-input-toggle-keyboard)
        ([?\C-c ?x ?f] . exwm-layout-toggle-fullscreen)
        ([?\C-c ?x ?l] . (lambda (command)
                           (interactive (list (read-shell-command "$ ")))
                           (start-process-shell-command command nil command)))
        ([?\C-c ?x ?k] . (lambda () (interactive) (kill-buffer)))
        ,@(mapcar (lambda (i)
                    `(,(kbd (format "C-c x %d" i)) .
                      (lambda ()
                        (exwm-workspace-switch-create ,i))))
                  (number-sequence 0 9))))

;; Initialize EXWM

;; Turn EXWM and XELB logging (check *XELB-DEBUG* buffer)

;; Enter the debugger when an error is encountered

(defun dw/exwm-fake-command-wrapper (orig-fn &rest args)
  (debug-on-variable-change 'last-command)
  (apply orig-fn args)
  (cancel-debug-on-variable-change 'last-command))

;; NOTE: This isn't being used now, leaving it here for reference
;; (advice-add 'exwm-input--fake-last-command :around #'dw/exwm-fake-command-wrapper)

;; Kudos to Timor for this suggestion:
(defun xcb-debug:message (format-string &rest objects)
  "Print a message to `xcb-debug:buffer'.

The FORMAT-STRING argument follows the speficies how to print each of
the passed OBJECTS.  See `format' for details."
  (let ((str (apply #'format format-string objects)))
     (insert str))
    (princ str 'external-debugging-output)))

;; NOTE: To save logs to a file, use:
;;    emacs 2>exwm.log

;; Log each window that get opened
(add-hook 'exwm-manage-finish-hook
          (lambda ()
            (exwm--log "WINDOW DISPLAYED: %s (%s)" exwm-class-name exwm-title)))

(other-window 1)
;; (start-process-shell-command "gnome-mahjongg" nil "gnome-mahjongg")
;; (start-process-shell-command "calibre" nil "calibre")

Bug Investigations

FIXED - #842: Hang when clicking on X window after isearch



This was caused by a misbehaving function in isearch which was being added to the pre-command-hook that gets invoked by exwm-input--fake-last-command. We fixed the issue by wrapping the run-hooks calls with a condition-case macro to prevent errors from breaking the EXWM session.