about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--third_party/exwm/.elpaignore1
-rw-r--r--third_party/exwm/exwm-background.el12
-rw-r--r--third_party/exwm/exwm-cm.el50
-rw-r--r--third_party/exwm/exwm-config.el2
-rw-r--r--third_party/exwm/exwm-core.el9
-rw-r--r--third_party/exwm/exwm-floating.el11
-rw-r--r--third_party/exwm/exwm-input.el92
-rw-r--r--third_party/exwm/exwm-layout.el15
-rw-r--r--third_party/exwm/exwm-manage.el45
-rw-r--r--third_party/exwm/exwm-randr.el10
-rw-r--r--third_party/exwm/exwm-systemtray.el3
-rw-r--r--third_party/exwm/exwm-workspace.el3
-rw-r--r--third_party/exwm/exwm-xim.el2
-rw-r--r--third_party/exwm/exwm-xsettings.el336
-rw-r--r--third_party/exwm/exwm.el44
-rw-r--r--third_party/overlays/tvl.nix13
16 files changed, 484 insertions, 164 deletions
diff --git a/third_party/exwm/.elpaignore b/third_party/exwm/.elpaignore
index b43bf86b50..f0f644ee08 100644
--- a/third_party/exwm/.elpaignore
+++ b/third_party/exwm/.elpaignore
@@ -1 +1,2 @@
+LICENSE
 README.md
diff --git a/third_party/exwm/exwm-background.el b/third_party/exwm/exwm-background.el
index 9c9bc5e352..fa663d8fe6 100644
--- a/third_party/exwm/exwm-background.el
+++ b/third_party/exwm/exwm-background.el
@@ -1,6 +1,6 @@
 ;;; exwm-background.el --- X Background Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2022-2024 Free Software Foundation, Inc.
 
 ;; Author: Steven Allen <steven@stebalien.com>
 
@@ -27,7 +27,7 @@
 ;;   (require 'exwm-background)
 ;;   (exwm-background-enable)
 ;;
-;; By default, this will apply the theme's background color. However, that
+;; By default, this will apply the theme's background color.  However, that
 ;; color can be customized via the `exwm-background-color' setting.
 
 ;;; Code:
@@ -47,13 +47,13 @@
 
 (defconst exwm-background--properties '("_XROOTPMAP_ID" "_XSETROOT_ID" "ESETROOT_PMAP_ID")
   "The background properties to set.
-We can't need to set these so that compositing window managers can correctly display the background
-color.")
+We can't need to set these so that compositing window managers
+can correctly display the background color.")
 
 (defvar exwm-background--connection nil
   "The X connection used for setting the background.
-We use a separate connection as other background-setting tools may kill this connection when they
-replace it.")
+We use a separate connection as other background-setting tools
+may kill this connection when they replace it.")
 
 (defvar exwm-background--pixmap nil
   "Cached background pixmap.")
diff --git a/third_party/exwm/exwm-cm.el b/third_party/exwm/exwm-cm.el
deleted file mode 100644
index d2d04cbf92..0000000000
--- a/third_party/exwm/exwm-cm.el
+++ /dev/null
@@ -1,50 +0,0 @@
-;;; exwm-cm.el --- Compositing Manager for EXWM  -*- lexical-binding: t -*-
-
-;; Copyright (C) 2016-2023 Free Software Foundation, Inc.
-
-;; Author: Chris Feng <chris.w.feng@gmail.com>
-
-;; This file is part of GNU Emacs.
-
-;; GNU Emacs is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; GNU Emacs is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module is obsolete since EXWM now supports third-party compositors.
-
-;;; Code:
-
-(make-obsolete-variable 'exwm-cm-opacity
-                        "This variable should no longer be used." "26")
-
-(defun exwm-cm-set-opacity (&rest _args)
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-enable ()
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-start ()
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-stop ()
-  (declare (obsolete nil "26")))
-
-(defun exwm-cm-toggle ()
-  (declare (obsolete nil "26")))
-
-
-
-(provide 'exwm-cm)
-
-;;; exwm-cm.el ends here
diff --git a/third_party/exwm/exwm-config.el b/third_party/exwm/exwm-config.el
index f3357807b5..a9f21e9c8c 100644
--- a/third_party/exwm/exwm-config.el
+++ b/third_party/exwm/exwm-config.el
@@ -1,6 +1,6 @@
 ;;; exwm-config.el --- Predefined configurations  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
diff --git a/third_party/exwm/exwm-core.el b/third_party/exwm/exwm-core.el
index 4df57e7381..e0d644d941 100644
--- a/third_party/exwm/exwm-core.el
+++ b/third_party/exwm/exwm-core.el
@@ -1,6 +1,6 @@
 ;;; exwm-core.el --- Core definitions  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -33,6 +33,10 @@
 (require 'xcb-ewmh)
 (require 'xcb-debug)
 
+(defgroup exwm-debug nil
+  "Debugging."
+  :group 'exwm)
+
 (defcustom exwm-debug-log-time-function #'exwm-debug-log-uptime
   "Function used for generating timestamps in `exwm-debug' logs.
 
@@ -40,7 +44,6 @@ Here are some predefined candidates:
 `exwm-debug-log-uptime': Display the uptime of this Emacs instance.
 `exwm-debug-log-time': Display time of day.
 `nil': Disable timestamp."
-  :group 'exwm-debug
   :type `(choice (const :tag "Emacs uptime" ,#'exwm-debug-log-uptime)
                  (const :tag "Time of day" ,#'exwm-debug-log-time)
                  (const :tag "Off" nil)
@@ -203,7 +206,7 @@ If FRAME is null, use selected frame."
   "Convert COLOR to PIXEL (index in TrueColor colormap)."
   (when (and color
              (eq (x-display-visual-class) 'true-color))
-    (let ((rgb (x-color-values color)))
+    (let ((rgb (color-values color)))
       (logior (ash (ash (pop rgb) -8) 16)
               (ash (ash (pop rgb) -8) 8)
               (ash (pop rgb) -8)))))
diff --git a/third_party/exwm/exwm-floating.el b/third_party/exwm/exwm-floating.el
index def7f43e53..34d06a30db 100644
--- a/third_party/exwm/exwm-floating.el
+++ b/third_party/exwm/exwm-floating.el
@@ -1,6 +1,6 @@
 ;;; exwm-floating.el --- Floating Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -31,17 +31,16 @@
 
 (defgroup exwm-floating nil
   "Floating."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-floating-setup-hook nil
-  "Normal hook run when an X window has been made floating, in the
-context of the corresponding buffer."
+  "Normal hook run when an X window has been made floating.
+This hook runs in the context of the corresponding buffer."
   :type 'hook)
 
 (defcustom exwm-floating-exit-hook nil
-  "Normal hook run when an X window has exited floating state, in the
-context of the corresponding buffer."
+  "Normal hook run when an X window has exited floating state.
+This hook runs in the context of the corresponding buffer."
   :type 'hook)
 
 (defcustom exwm-floating-border-color "navy"
diff --git a/third_party/exwm/exwm-input.el b/third_party/exwm/exwm-input.el
index 4c762c40fe..f1f035c91a 100644
--- a/third_party/exwm/exwm-input.el
+++ b/third_party/exwm/exwm-input.el
@@ -1,6 +1,6 @@
 ;;; exwm-input.el --- Input Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -40,7 +40,6 @@
 
 (defgroup exwm-input nil
   "Input."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-input-prefix-keys
@@ -102,6 +101,13 @@ defined in `exwm-mode-map' here."
 (defconst exwm-input--update-focus-interval 0.01
   "Time interval (in seconds) for accumulating input focus update requests.")
 
+(defconst exwm-input--passthrough-functions '(read-char
+                                              read-char-exclusive
+                                              read-key-sequence-vector
+                                              read-key-sequence
+                                              read-event)
+  "Low-level read functions that must be exempted from EXWM input handling.")
+
 (defvar exwm-input--during-command nil
   "Indicate whether between `pre-command-hook' and `post-command-hook'.")
 
@@ -129,11 +135,14 @@ defined in `exwm-mode-map' here."
 
 (defvar exwm-input--timestamp-window nil)
 
+(defvar exwm-input--update-focus-timer nil
+  "Timer for deferring the update of input focus.")
+
 (defvar exwm-input--update-focus-lock nil
   "Lock for solving input focus update contention.")
 
-(defvar exwm-input--update-focus-timer nil
-  "Timer for deferring the update of input focus.")
+(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused.
+This value should always be overwritten.")
 
 (defvar exwm-input--echo-area-timer nil "Timer for detecting echo area dirty.")
 
@@ -291,38 +300,38 @@ ARGS are additional arguments to CALLBACK."
   "Run in `buffer-list-update-hook' to track input focus."
   (when (and          ; this hook is called incesantly; place cheap tests on top
          (not exwm-input--skip-buffer-list-update)
-         (exwm--terminal-p))      ; skip other terminals, e.g. TTY client frames
+         (exwm--terminal-p) ; skip other terminals, e.g. TTY client frames
+         (not (frame-parameter nil 'no-accept-focus)))
     (exwm--log "current-buffer=%S selected-window=%S"
                (current-buffer) (selected-window))
+    (redirect-frame-focus (selected-frame) nil)
+    (setq exwm-input--update-focus-window (selected-window))
     (exwm-input--update-focus-defer)))
 
 (defun exwm-input--update-focus-defer ()
-  "Defer updating input focus."
-  (redirect-frame-focus (selected-frame) nil)
+  "Schedule a deferred update to input focus.
+Instead of immediately focusing the current window, it defers the focus change
+until the selected window stops changing (debouncing input focus updates)."
   (when exwm-input--update-focus-timer
     (cancel-timer exwm-input--update-focus-timer))
   (setq exwm-input--update-focus-timer
         ;; Attempt to accumulate successive events close enough.
         (run-with-timer exwm-input--update-focus-interval
                         nil
-                        #'exwm-input--update-focus-commit
-                        (selected-window))))
-
-(defun exwm-input--update-focus-commit (window)
-  "Commit updating input focus."
-  (let ((cwin (selected-window)))
-    (if exwm-input--update-focus-lock
-        (unless (eq exwm-input--update-focus-lock cwin)
-          (exwm-input--update-focus-defer))
-      (if (and cwin window (eq cwin window))
-          (let ((exwm-input--update-focus-lock cwin))
-            (exwm-input--update-focus window))
-        (exwm-input--update-focus-defer)))))
+                        #'exwm-input--update-focus-commit)))
+
+(defun exwm-input--update-focus-commit ()
+  "Attempt to update the window focus.
+If we're currently updating the window focus, re-schedule a focus update
+attempt later."
+  (if exwm-input--update-focus-lock
+      (exwm-input--update-focus-defer)
+    (let ((exwm-input--update-focus-lock t))
+      (exwm-input--update-focus exwm-input--update-focus-window))))
 
 (defun exwm-input--update-focus (window)
-  "Update input focus."
-  (when  (and (window-live-p window)
-              (not (frame-parameter (window-frame window) 'no-accept-focus)))
+  "Update input focus to WINDOW."
+  (when (window-live-p window)
     (exwm--log "focus-window=%s focus-buffer=%s" window (window-buffer window))
     (with-current-buffer (window-buffer window)
       (if (derived-mode-p 'exwm-mode)
@@ -573,20 +582,10 @@ instead."
   (when (called-interactively-p 'any)
     (exwm-input--update-global-prefix-keys)))
 
-;; Putting (t . EVENT) into `unread-command-events' does not really work
-;; as documented for Emacs < 26.2.
-(eval-and-compile
-  (if (or (< emacs-major-version 26)
-          (and (= emacs-major-version 26)
-               (< emacs-minor-version 2)))
-      (defsubst exwm-input--unread-event (event)
-        (declare (indent defun))
-        (setq unread-command-events
-              (append unread-command-events (list event))))
-    (defsubst exwm-input--unread-event (event)
-      (declare (indent defun))
-      (setq unread-command-events
-            (append unread-command-events `((t . ,event)))))))
+(defsubst exwm-input--unread-event (event)
+  (declare (indent defun))
+  (setq unread-command-events
+        (append unread-command-events `((t . ,event)))))
 
 (defun exwm-input--mimic-read-event (event)
   "Process EVENT as if it were returned by `read-event'."
@@ -747,7 +746,7 @@ Current buffer must be an `exwm-mode' buffer."
 (defun exwm-input--on-ButtonPress-line-mode (buffer button-event)
   "Handle button events in line mode.
 BUFFER is the `exwm-mode' buffer the event was generated
-on. BUTTON-EVENT is the X event converted into an Emacs event.
+on.  BUTTON-EVENT is the X event converted into an Emacs event.
 
 The return value is used as event_mode to release the original
 button event."
@@ -970,11 +969,6 @@ multiple keys.  If END-KEY is non-nil, stop sending keys if it's pressed."
                    #'exwm-input-send-simulation-key))))
            exwm-input--simulation-keys))
 
-(defun exwm-input-set-simulation-keys (simulation-keys)
-  "Please customize or set `exwm-input-simulation-keys' instead."
-  (declare (obsolete nil "26"))
-  (exwm-input--set-simulation-keys simulation-keys))
-
 (defcustom exwm-input-simulation-keys nil
   "Simulation keys.
 
@@ -1151,6 +1145,11 @@ One use is to access the keymap bound to KEYS (as prefix keys) in `char-mode'."
     (exwm--log)
     (exwm-input--on-minibuffer-exit)))
 
+(defun exwm-input--call-with-passthrough (function &rest args)
+  "Bind `exwm-input-line-mode-passthrough' and call FUNCTION with ARGS."
+  (let ((exwm-input-line-mode-passthrough t))
+    (apply function args)))
+
 (defun exwm-input--init ()
   "Initialize the keyboard module."
   (exwm--log)
@@ -1206,7 +1205,10 @@ One use is to access the keymap bound to KEYS (as prefix keys) in `char-mode'."
         (run-with-idle-timer 0 t #'exwm-input--on-echo-area-dirty))
   (add-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear)
   ;; Update focus when buffer list updates
-  (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update))
+  (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)
+
+  (dolist (fun exwm-input--passthrough-functions)
+    (advice-add fun :around #'exwm-input--call-with-passthrough)))
 
 (defun exwm-input--post-init ()
   "The second stage in the initialization of the input module."
@@ -1216,6 +1218,8 @@ One use is to access the keymap bound to KEYS (as prefix keys) in `char-mode'."
 (defun exwm-input--exit ()
   "Exit the input module."
   (exwm--log)
+  (dolist (fun exwm-input--passthrough-functions)
+    (advice-remove fun #'exwm-input--call-with-passthrough))
   (exwm-input--unset-simulation-keys)
   (remove-hook 'pre-command-hook #'exwm-input--on-pre-command)
   (remove-hook 'post-command-hook #'exwm-input--on-post-command)
diff --git a/third_party/exwm/exwm-layout.el b/third_party/exwm/exwm-layout.el
index 0cfc47f8a3..8649c11ffd 100644
--- a/third_party/exwm/exwm-layout.el
+++ b/third_party/exwm/exwm-layout.el
@@ -1,6 +1,6 @@
 ;;; exwm-layout.el --- Layout Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -29,7 +29,6 @@
 
 (defgroup exwm-layout nil
   "Layout."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-layout-auto-iconify t
@@ -146,8 +145,8 @@ See variable `exwm-layout-auto-iconify'."
       (exwm--set-geometry id x y width height)
       (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id))
       (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)
-      ;; (setq exwm--ewmh-state
-      ;;       (delq xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state))
+      (setq exwm--ewmh-state
+            (delq xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state))
       (exwm-layout--set-ewmh-state id)
       (exwm-layout--auto-iconify)))
   (xcb:flush exwm--connection))
@@ -157,7 +156,8 @@ See variable `exwm-layout-auto-iconify'."
   (with-current-buffer (exwm--id->buffer id)
     (unless (or (exwm-layout--iconic-state-p)
                 (and exwm--floating-frame
-                     (eq 4294967295. exwm--desktop)))
+                     exwm--desktop
+                     (= 4294967295. exwm--desktop)))
       (exwm--log "Hide #x%x" id)
       (when exwm--floating-frame
         (let* ((container (frame-parameter exwm--floating-frame
@@ -182,7 +182,7 @@ See variable `exwm-layout-auto-iconify'."
                          :window id :value-mask xcb:CW:EventMask
                          :event-mask (exwm--get-client-event-mask)))
       (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState)
-      ;; (cl-pushnew xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state)
+      (cl-pushnew xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state)
       (exwm-layout--set-ewmh-state id)
       (exwm-layout--auto-iconify)
       (xcb:flush exwm--connection))))
@@ -207,8 +207,7 @@ See variable `exwm-layout-auto-iconify'."
                                            xcb:ConfigWindow:StackMode)
                        :border-width 0
                        :stack-mode xcb:StackMode:Above))
-    ;; commented out to work around https://github.com/ch11ng/exwm/issues/759
-    ;; (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
+    (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)
     (exwm-layout--set-ewmh-state exwm--id)
     (xcb:flush exwm--connection)
     (set-window-dedicated-p (get-buffer-window) t)
diff --git a/third_party/exwm/exwm-manage.el b/third_party/exwm/exwm-manage.el
index 4771c3c088..ab66e298ac 100644
--- a/third_party/exwm/exwm-manage.el
+++ b/third_party/exwm/exwm-manage.el
@@ -1,7 +1,7 @@
 ;;; exwm-manage.el --- Window Management Module for  -*- lexical-binding: t -*-
 ;;;                    EXWM
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -30,7 +30,6 @@
 
 (defgroup exwm-manage nil
   "Manage."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-manage-finish-hook nil
@@ -235,6 +234,23 @@ Override current hinds if FORCE is non-nil."
                           (elt value 2))) ;MotifWmHints.decorations
               (setq exwm--mwm-hints-decorations nil))))))))
 
+(defun exwm-manage--update-default-directory (id)
+  "Update the `default-directory' of X window ID.
+Sets the `default-directory' of the EXWM buffer associated with X window to
+match its current working directory.
+
+This only works when procfs is mounted, which may not be the case on some BSDs."
+  (with-current-buffer (exwm--id->buffer id)
+    (if-let* ((response (xcb:+request-unchecked+reply exwm--connection
+                            (make-instance 'xcb:ewmh:get-_NET_WM_PID
+                                           :window id)))
+              (pid (slot-value response 'value))
+              (cwd (file-symlink-p (format "/proc/%d/cwd" pid)))
+              ((file-accessible-directory-p cwd)))
+        (setq default-directory (file-name-as-directory cwd))
+      (setq default-directory (expand-file-name "~/")))))
+
+
 (defun exwm-manage--set-client-list ()
   "Set _NET_CLIENT_LIST."
   (exwm--log)
@@ -394,23 +410,20 @@ Override current hinds if FORCE is non-nil."
       (if (plist-get exwm--configurations 'char-mode)
           (exwm-input-release-keyboard id)
         (exwm-input-grab-keyboard id))
-      (let ((simulation-keys (plist-get exwm--configurations 'simulation-keys))
-            (prefix-keys (plist-get exwm--configurations 'prefix-keys)))
-        (with-current-buffer (exwm--id->buffer id)
-          (when simulation-keys
-            (exwm-input-set-local-simulation-keys simulation-keys))
-          (when prefix-keys
-            (setq-local exwm-input-prefix-keys prefix-keys))))
+      (when-let ((simulation-keys (plist-get exwm--configurations 'simulation-keys)))
+        (exwm-input-set-local-simulation-keys simulation-keys))
+      (when-let ((prefix-keys (plist-get exwm--configurations 'prefix-keys)))
+        (setq-local exwm-input-prefix-keys prefix-keys))
       (setq exwm-workspace--switch-history-outdated t)
       (exwm--update-desktop id)
       (exwm-manage--update-ewmh-state id)
-      (with-current-buffer (exwm--id->buffer id)
-        (when (or (plist-get exwm--configurations 'fullscreen)
-                  (exwm-layout--fullscreen-p))
-          (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN
-                                       exwm--ewmh-state))
-          (exwm-layout-set-fullscreen id))
-        (run-hooks 'exwm-manage-finish-hook)))))
+      (exwm-manage--update-default-directory id)
+      (when (or (plist-get exwm--configurations 'fullscreen)
+                (exwm-layout--fullscreen-p))
+        (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN
+                                     exwm--ewmh-state))
+        (exwm-layout-set-fullscreen id))
+      (run-hooks 'exwm-manage-finish-hook))))
 
 (defun exwm-manage--unmanage-window (id &optional withdraw-only)
   "Unmanage window ID.
diff --git a/third_party/exwm/exwm-randr.el b/third_party/exwm/exwm-randr.el
index 8d1824babb..7f0e50559b 100644
--- a/third_party/exwm/exwm-randr.el
+++ b/third_party/exwm/exwm-randr.el
@@ -1,6 +1,6 @@
 ;;; exwm-randr.el --- RandR Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -56,7 +56,6 @@
 
 (defgroup exwm-randr nil
   "RandR."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-randr-refresh-hook nil
@@ -91,10 +90,6 @@ corresponding monitors whenever the monitors are active.
   \\='(1 \"HDMI-1\" 3 \"DP-1\")"
   :type '(plist :key-type integer :value-type string))
 
-(with-no-warnings
-  (define-obsolete-variable-alias 'exwm-randr-workspace-output-plist
-    'exwm-randr-workspace-monitor-plist "27.1"))
-
 (defvar exwm-randr--last-timestamp 0 "Used for debouncing events.")
 
 (defvar exwm-randr--prev-screen-change-seqnum nil
@@ -269,9 +264,6 @@ In a mirroring setup some monitors overlap and should be treated as one."
       (xcb:flush exwm--connection)
       (run-hooks 'exwm-randr-refresh-hook))))
 
-(define-obsolete-function-alias 'exwm-randr--refresh #'exwm-randr-refresh
-  "27.1")
-
 (defun exwm-randr--on-ScreenChangeNotify (data _synthetic)
   "Handle `ScreenChangeNotify' event.
 
diff --git a/third_party/exwm/exwm-systemtray.el b/third_party/exwm/exwm-systemtray.el
index 7f2b268df9..9e57dae4eb 100644
--- a/third_party/exwm/exwm-systemtray.el
+++ b/third_party/exwm/exwm-systemtray.el
@@ -1,7 +1,7 @@
 ;;; exwm-systemtray.el --- System Tray Module for  -*- lexical-binding: t -*-
 ;;;                        EXWM
 
-;; Copyright (C) 2016-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2016-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -57,7 +57,6 @@
 
 (defgroup exwm-systemtray nil
   "System tray."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-systemtray-height nil
diff --git a/third_party/exwm/exwm-workspace.el b/third_party/exwm/exwm-workspace.el
index 61e2f69d28..89be697159 100644
--- a/third_party/exwm/exwm-workspace.el
+++ b/third_party/exwm/exwm-workspace.el
@@ -1,6 +1,6 @@
 ;;; exwm-workspace.el --- Workspace Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 1015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
@@ -31,7 +31,6 @@
 
 (defgroup exwm-workspace nil
   "Workspace."
-  :version "25.3"
   :group 'exwm)
 
 (defcustom exwm-workspace-switch-hook nil
diff --git a/third_party/exwm/exwm-xim.el b/third_party/exwm/exwm-xim.el
index aea0eb53fb..1f0c9c460b 100644
--- a/third_party/exwm/exwm-xim.el
+++ b/third_party/exwm/exwm-xim.el
@@ -1,6 +1,6 @@
 ;;; exwm-xim.el --- XIM Module for EXWM  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2019-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2019-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
 
diff --git a/third_party/exwm/exwm-xsettings.el b/third_party/exwm/exwm-xsettings.el
new file mode 100644
index 0000000000..99d6b9c4ac
--- /dev/null
+++ b/third_party/exwm/exwm-xsettings.el
@@ -0,0 +1,336 @@
+;;; exwm-xsettings.el --- XSETTINGS Module for EXWM -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022-2024 Free Software Foundation, Inc.
+
+;; Author: Steven Allen <steven@stebalien.com>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Implements the XSETTINGS protocol, allowing Emacs to manage the system theme,
+;; fonts, icons, etc.
+;;
+;; This package can be configured as follows:
+;;
+;;   (require 'exwm-xsettings)
+;;   (setq exwm-xsettings-theme '("Adwaita" . "Adwaita-dark") ;; light/dark
+;;         exwm-xsettings `(("Xft/HintStyle" . "hintslight")
+;;                          ("Xft/RGBA" . "rgb")
+;;                          ("Xft/lcdfilter" . "lcddefault")
+;;                          ("Xft/Antialias" . 1)
+;;                          ;; DPI is in 1024ths of an inch, so this is a DPI of
+;;                          ;; 144, equivalent to ;; a scaling factor of 1.5
+;;                          ;; (144 = 1.5 * 96).
+;;                          ("Xft/DPI" . ,(* 144 1024))
+;;                          ("Xft/Hinting" . 1)))
+;;   (exwm-xsettings-enable)
+;;
+;; To modify these settings at runtime, customize them with
+;; `custom-set-variables' or `setopt' (Emacs 29+).  E.g., the following will
+;; immediately change the icon theme to "Papirus" at runtime, even in running
+;; applications:
+;;
+;;   (setopt exwm-xsettings-icon-theme "Papirus")
+
+;;; Code:
+
+(require 'xcb-ewmh)
+(require 'xcb-xsettings)
+(require 'exwm-core)
+
+(defvar exwm-xsettings--connection nil)
+(defvar exwm-xsettings--XSETTINGS_SETTINGS-atom nil)
+(defvar exwm-xsettings--XSETTINGS_S0-atom nil)
+(defvar exwm-xsettings--selection-owner-window nil)
+(defvar exwm-xsettings--serial 0)
+
+(defun exwm-xsettings--rgba-match (_widget value)
+  "Return t if VALUE is a valid RGBA color."
+  (and (numberp value) (<= 0 value 1)))
+
+(defun exwm-xsettings--custom-set (symbol value)
+  "Setter used by `exwm-xsettings' customization options.
+
+SYMBOL is the setting being updated and VALUE is the new value."
+  (set-default-toplevel-value symbol value)
+  (exwm-xsettings--update-settings))
+
+(defgroup exwm-xsettings nil
+  "XSETTINGS."
+  :group 'exwm)
+
+(defcustom exwm-xsettings nil
+  "Alist of custom XSETTINGS.
+These settings take precedence over `exwm-xsettings-theme' and
+`exwm-xsettings-icon-theme'."
+  :type '(alist :key-type (string :tag "Name")
+                :value-type (choice :tag "Value"
+                              (string :tag "String")
+                              (integer :tag "Integer")
+                              (list :tag "Color"
+                                (number :tag "Red"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match)
+                                (number :tag "Green"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match)
+                                (number :tag "Blue"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match)
+                                (number :tag "Alpha"
+                                        :type-error
+                                        "This field should contain a number between 0 and 1."
+                                       :match exwm-xsettings--rgba-match
+                                       :value 1.0))))
+  :initialize #'custom-initialize-default
+  :set #'exwm-xsettings--custom-set)
+
+(defcustom exwm-xsettings-theme nil
+  "The system-wide theme."
+  :type '(choice (string :tag "Theme")
+                 (cons (string :tag "Light Theme")
+                       (string :tag "Dark Theme")))
+  :initialize #'custom-initialize-default
+  :set #'exwm-xsettings--custom-set)
+
+(defcustom exwm-xsettings-icon-theme nil
+  "The system-wide icon theme."
+  :type '(choice (string :tag "Icon Theme")
+                 (cons (string :tag "Light Icon Theme")
+                       (string :tag "Dark Icon Theme")))
+  :initialize #'custom-initialize-default
+  :set #'exwm-xsettings--custom-set)
+
+(defalias 'exwm-xsettings--color-dark-p
+  (if (eval-when-compile (< emacs-major-version 29))
+      ;; Borrowed from Emacs 29.
+      (lambda (rgb)
+        "Whether RGB is more readable against white than black."
+        (unless (<= 0 (apply #'min rgb) (apply #'max rgb) 1)
+          (error "RGB components %S not in [0,1]" rgb))
+        (let* ((r (expt (nth 0 rgb) 2.2))
+               (g (expt (nth 1 rgb) 2.2))
+               (b (expt (nth 2 rgb) 2.2))
+               (y (+ (* r 0.2126) (* g 0.7152) (* b 0.0722))))
+          (< y 0.325)))
+    'color-dark-p))
+
+(defun exwm-xsettings--pick-theme (theme)
+  "Pick a light or dark theme from the given THEME.
+If THEME is a string, it's returned directly.
+If THEME is a cons of (LIGHT . DARK), the appropriate theme is picked based on
+the default face's background color."
+  (pcase theme
+    ((cl-type string) theme)
+    (`(,(cl-type string) . ,(cl-type string))
+     (if (exwm-xsettings--color-dark-p (color-name-to-rgb (face-background 'default)))
+         (cdr theme) (car theme)))
+    (_ (error "Expected theme to be a string or a pair of strings"))))
+
+(defun exwm-xsettings--get-settings ()
+  "Get the current settings.
+Combines `exwm-xsettings', `exwm-xsettings-theme' (if set), and
+`exwm-xsettings-icon-theme' (if set)."
+  (cl-remove-duplicates
+   (append
+    exwm-xsettings
+    (when exwm-xsettings-theme
+      (list (cons "Net/ThemeName" (exwm-xsettings--pick-theme exwm-xsettings-theme))))
+    (when exwm-xsettings-icon-theme
+      (list (cons "Net/IconThemeName" (exwm-xsettings--pick-theme exwm-xsettings-icon-theme)))))
+   :key 'car
+   :test 'string=))
+
+(defun exwm-xsettings--make-settings (settings serial)
+  "Construct a new settings object.
+SETTINGS is an alist of key/value pairs.
+SERIAL is a sequence number."
+  (make-instance 'xcb:xsettings:-Settings
+                 :byte-order (if xcb:lsb 0 1)
+                 :serial serial
+                 :settings-len (length settings)
+                 :settings
+                 (mapcar
+                  (lambda (prop)
+                    (let* ((name (car prop))
+                           (value (cdr prop))
+                           (common (list :name name
+                                         :name-len (length name)
+                                         :last-change-serial serial)))
+                      (pcase value
+                        ((cl-type string)
+                         (apply #'make-instance 'xcb:xsettings:-SETTING_STRING
+                                :value-len (length value)
+                                :value value
+                                common))
+                        ((cl-type integer)
+                         (apply #'make-instance 'xcb:xsettings:-SETTING_INTEGER
+                                :value value common))
+                        ((and (cl-type list) (app length (or 3 4)))
+                         ;; Convert from RGB(A) to 16bit integers.
+                         (setq value (mapcar (lambda (x) (round (* x #xffff))) value))
+                         (apply #'make-instance 'xcb:xsettings:-SETTING_COLOR
+                                :red (pop value)
+                                :green (pop value)
+                                :blue (pop value)
+                                :alpha (or (pop value) #xffff)))
+                        (_ (error "Setting value must be a string, integer, or length 3-4 list")))))
+                  settings)))
+
+(defun exwm-xsettings--update-settings ()
+  "Update the xsettings."
+  (when exwm-xsettings--connection
+    (setq exwm-xsettings--serial (1+ exwm-xsettings--serial))
+    (let* ((settings (exwm-xsettings--get-settings))
+           (bytes (xcb:marshal (exwm-xsettings--make-settings settings exwm-xsettings--serial))))
+      (xcb:+request exwm-xsettings--connection
+          (make-instance 'xcb:ChangeProperty
+                         :mode xcb:PropMode:Replace
+                         :window exwm-xsettings--selection-owner-window
+                         :property exwm-xsettings--XSETTINGS_SETTINGS-atom
+                         :type exwm-xsettings--XSETTINGS_SETTINGS-atom
+                         :format 8
+                         :data-len (length bytes)
+                         :data bytes)))
+    (xcb:flush exwm-xsettings--connection)))
+
+(defun exwm-xsettings--on-theme-change (&rest _)
+  "Called when the Emacs theme is changed."
+  ;; We only bother updating the xsettings if changing the theme could effect
+  ;; the settings.
+  (when (or (consp exwm-xsettings-theme) (consp exwm-xsettings-icon-theme))
+    (exwm-xsettings--update-settings)))
+
+(defun exwm-xsettings--on-SelectionClear (_data _synthetic)
+  "Called when another xsettings daemon takes over."
+  (exwm--log "XSETTINGS manager has been replaced.")
+  (exwm-xsettings--exit))
+
+(cl-defun exwm-xsettings--init ()
+  "Initialize the XSETTINGS module."
+  (exwm--log)
+
+  (cl-assert (not exwm-xsettings--connection))
+
+  ;; Connect
+  (setq exwm-xsettings--connection (xcb:connect))
+  (set-process-query-on-exit-flag (slot-value exwm-xsettings--connection
+                                              'process)
+                                  nil)
+
+  ;; Intern the atoms.
+  (setq exwm-xsettings--XSETTINGS_SETTINGS-atom
+        (exwm--intern-atom "_XSETTINGS_SETTINGS" exwm-xsettings--connection)
+
+        exwm-xsettings--XSETTINGS_S0-atom
+        (exwm--intern-atom "_XSETTINGS_S0" exwm-xsettings--connection))
+
+  ;; Detect running XSETTINGS managers.
+  (with-slots (owner)
+      (xcb:+request-unchecked+reply exwm-xsettings--connection
+          (make-instance 'xcb:GetSelectionOwner
+                         :selection exwm-xsettings--XSETTINGS_S0-atom))
+    (when (/= owner xcb:Window:None)
+      (xcb:disconnect exwm-xsettings--connection)
+      (setq exwm-xsettings--connection nil)
+      (warn "[EXWM] Other XSETTINGS manager detected")
+      (cl-return-from exwm-xsettings--init)))
+
+  (let ((id(xcb:generate-id exwm-xsettings--connection)))
+    (setq exwm-xsettings--selection-owner-window id)
+
+    ;; Create a settings window.
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:CreateWindow
+                       :wid id
+                       :parent exwm--root
+                       :class xcb:WindowClass:InputOnly
+                       :x 0
+                       :y 0
+                       :width 1
+                       :height 1
+                       :border-width 0
+                       :depth 0
+                       :visual 0
+                       :value-mask xcb:CW:OverrideRedirect
+                       :override-redirect 1))
+
+    ;; Set _NET_WM_NAME.
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:ewmh:set-_NET_WM_NAME
+                       :window id
+                       :data "EXWM: exwm-xsettings--selection-owner-window"))
+
+    ;; Apply the XSETTINGS properties.
+    (exwm-xsettings--update-settings)
+
+    ;; Take ownership and notify.
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:SetSelectionOwner
+                       :owner id
+                       :selection exwm-xsettings--XSETTINGS_S0-atom
+                       :time xcb:Time:CurrentTime))
+    (xcb:+request exwm-xsettings--connection
+        (make-instance 'xcb:SendEvent
+                       :propagate 0
+                       :destination exwm--root
+                       :event-mask xcb:EventMask:StructureNotify
+                       :event (xcb:marshal
+                               (make-instance 'xcb:xsettings:-ClientMessage
+                                              :window exwm--root
+                                              :time xcb:Time:CurrentTime
+                                              :selection exwm-xsettings--XSETTINGS_S0-atom
+                                              :owner id)
+                               exwm-xsettings--connection)))
+
+    ;; Detect loss of XSETTINGS ownership.
+    (xcb:+event exwm-xsettings--connection 'xcb:SelectionClear
+                #'exwm-xsettings--on-SelectionClear)
+
+    (xcb:flush exwm-xsettings--connection))
+
+  ;; Update the xsettings if/when the theme changes.
+  (add-hook 'enable-theme-functions #'exwm-xsettings--on-theme-change)
+  (add-hook 'disable-theme-functions #'exwm-xsettings--on-theme-change))
+
+(defun exwm-xsettings--exit ()
+  "Exit the XSETTINGS module."
+  (exwm--log)
+
+  (when exwm-xsettings--connection
+    (remove-hook 'enable-theme-functions #'exwm-xsettings--on-theme-change)
+    (remove-hook 'disable-theme-functions #'exwm-xsettings--on-theme-change)
+
+    (xcb:disconnect exwm-xsettings--connection)
+
+    (setq exwm-xsettings--connection nil
+          exwm-xsettings--XSETTINGS_SETTINGS-atom nil
+          exwm-xsettings--XSETTINGS_S0-atom nil
+          exwm-xsettings--selection-owner-window nil)))
+
+(defun exwm-xsettings-enable ()
+  "Enable xsettings support for EXWM."
+  (exwm--log)
+  (add-hook 'exwm-init-hook #'exwm-xsettings--init)
+  (add-hook 'exwm-exit-hook #'exwm-xsettings--exit))
+
+(provide 'exwm-xsettings)
+
+;;; exwm-xsettings.el ends here
diff --git a/third_party/exwm/exwm.el b/third_party/exwm/exwm.el
index 4870d4bc42..c4900eab48 100644
--- a/third_party/exwm/exwm.el
+++ b/third_party/exwm/exwm.el
@@ -1,13 +1,13 @@
 ;;; exwm.el --- Emacs X Window Manager  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2015-2023 Free Software Foundation, Inc.
+;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
 
 ;; Author: Chris Feng <chris.w.feng@gmail.com>
-;; Maintainer: Adrián Medraño Calvo <adrian@medranocalvo.com>
+;; Maintainer: Adrián Medraño Calvo <adrian@medranocalvo.com>, Steven Allen <steven@stebalien.com>, Daniel Mendler <mail@daniel-mendler.de>
 ;; Version: 0.28
-;; Package-Requires: ((xelb "0.18"))
+;; Package-Requires: ((emacs "27.1") (xelb "0.18"))
 ;; Keywords: unix
-;; URL: https://github.com/ch11ng/exwm
+;; URL: https://github.com/emacs-exwm/exwm
 
 ;; This file is part of GNU Emacs.
 
@@ -29,14 +29,18 @@
 ;; Overview
 ;; --------
 ;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager
-;; for Emacs built on top of [XELB](https://github.com/ch11ng/xelb).
+;; for Emacs built on top of [XELB](https://github.com/emacs-exwm/xelb).
 ;; It features:
 ;; + Fully keyboard-driven operations
 ;; + Hybrid layout modes (tiling & stacking)
 ;; + Dynamic workspace support
 ;; + ICCCM/EWMH compliance
-;; + (Optional) RandR (multi-monitor) support
-;; + (Optional) Built-in system tray
+;; Optional features:
+;; + RandR (multi-monitor) support
+;; + System tray
+;; + Input method
+;; + Background setting support
+;; + XSETTINGS server
 
 ;; Installation & configuration
 ;; ----------------------------
@@ -54,7 +58,7 @@
 ;;    xinit -- vt01
 ;;
 ;; You should additionally hide the menu-bar, tool-bar, etc to increase the
-;; usable space.  Please check the wiki (https://github.com/ch11ng/exwm/wiki)
+;; usable space.  Please check the wiki (https://github.com/emacs-exwm/exwm/wiki)
 ;; for more detailed instructions on installation, configuration, usage, etc.
 
 ;; References:
@@ -77,7 +81,6 @@
 (defgroup exwm nil
   "Emacs X Window Manager."
   :tag "EXWM"
-  :version "25.3"
   :group 'applications
   :prefix "exwm-")
 
@@ -97,7 +100,10 @@
   "Normal hook run when window title is updated."
   :type 'hook)
 
-(defcustom exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font)
+(defcustom exwm-blocking-subrs
+  ;; `x-file-dialog' and `x-select-font' are missing on some Emacs builds, for
+  ;; example on the X11 Lucid build.
+  '(x-file-dialog x-popup-dialog x-select-font message-box message-or-box)
   "Subrs (primitives) that would normally block EXWM."
   :type '(repeat function))
 
@@ -110,6 +116,10 @@
 (defconst exwm--server-name "server-exwm"
   "Name of the subordinate Emacs server.")
 
+(defvar exwm--server-timeout 1
+  "Number of seconds to wait for the subordinate Emacs server to exit.
+After this time, the server will be killed.")
+
 (defvar exwm--server-process nil "Process of the subordinate Emacs server.")
 
 (defun exwm-reset ()
@@ -166,7 +176,7 @@ Argument XWIN contains the X window of the `exwm-mode' buffer."
       (when reply
         (setq desktop (slot-value reply 'value))
         (cond
-         ((eq desktop 4294967295.)
+         ((and desktop (= desktop 4294967295.))
           (unless (or (not exwm--floating-frame)
                       (eq exwm--frame exwm-workspace--current)
                       (and exwm--desktop
@@ -482,9 +492,6 @@ RAW-DATA contains unmarshalled ClientMessage event data."
       (exwm-workspace-switch (elt data 0)))
      ;; _NET_ACTIVE_WINDOW.
      ((= type xcb:Atom:_NET_ACTIVE_WINDOW)
-      (dolist (f exwm-workspace--list)
-        (when (eq id (frame-parameter f 'exwm-outer-id))
-          (x-focus-frame f t)))
       (let ((buffer (exwm--id->buffer id))
             iconic window)
         (if (buffer-live-p buffer)
@@ -1002,8 +1009,13 @@ FRAME, if given, indicates the X display EXWM should manage."
 (defun exwm--server-stop ()
   "Stop the subordinate Emacs server."
   (exwm--log)
-  (server-force-delete exwm--server-name)
   (when exwm--server-process
+    (when (process-live-p exwm--server-process)
+      (cl-loop
+       initially (signal-process exwm--server-process 'TERM)
+       while     (process-live-p exwm--server-process)
+       repeat    (* 10 exwm--server-timeout)
+       do        (sit-for 0.1)))
     (delete-process exwm--server-process)
     (setq exwm--server-process nil)))
 
@@ -1020,7 +1032,7 @@ FUNCTION is the function to be evaluated, ARGS are the arguments."
                          (car command-line-args) ;The executable file
                          "-d" (frame-parameter nil 'display)
                          "-Q"
-                         (concat "--daemon=" exwm--server-name)
+                         (concat "--fg-daemon=" exwm--server-name)
                          "--eval"
                          ;; Create an invisible frame
                          "(make-frame '((window-system . x) (visibility)))"))
diff --git a/third_party/overlays/tvl.nix b/third_party/overlays/tvl.nix
index 6614709143..861c66694a 100644
--- a/third_party/overlays/tvl.nix
+++ b/third_party/overlays/tvl.nix
@@ -49,6 +49,19 @@ depot.nix.readTree.drvTargets {
           self.git
         ];
       });
+
+      # Pin xelb to a newer one until the new maintainers do a release.
+      xelb = eself.trivialBuild {
+        pname = "xelb";
+        version = "0.19-dev"; # invented version, last actual release was 0.18
+
+        src = self.fetchFromGitHub {
+          owner = "emacs-exwm";
+          repo = "xelb";
+          rev = "86089eba2de6c818bfa2fac075cb7ad876262798";
+          sha256 = "1mmlrd2zpcwiv8gh10y7lrpflnbmsycdascrxjr3bfcwa8yx7901";
+        };
+      };
     })
   );