Skip to content
medranocalvo edited this page Nov 10, 2022 · 133 revisions
Clone this wiki locally

EXWM User Guide

This guide serves as an introduction for new users to EXWM as well as a reference for advanced users. It mainly discusses major conceptions, usage and configurations of EXWM. It will be updated accordingly as EXWM evolves. Please consider helping improve this document.

Note: Tiling window managers like EXWM are usually configured as standalone applications without desktop environments / session managers. However it's possible to substitute the window managers of certain desktop environments (e.g. LXDE) with EXWM, or even with recent Gnome (e.g. exwm-gnome-flashback). Most contents in this document still apply there, but have a look at the section on logging out with LXDE.

Note: As of 0.17 EXWM provides a Customize interface and most configuration options discussed here are also available there.


There's no specific prerequisite indeed. But if there have to be some, here they are (should be easily satisfied):

  • GNU Emacs with a version higher than 24.4, preferably 25 or higher versions. Also 64-bit build or 32-bit build with --with-wide-int set is preferred. You should probably hide menu-bar, tool-bar, scroll-bar etc to make more room for X windows.
  • X11 server (a recent version please).
  • (optional) dbus-launch. (NOTE: Do not use on Arch Linux. See
  • (optional) gnome-settings-daemon (or other setting daemons).
  • to add exwm to the list of available wm or de in your dm (like gdm or slim) create file /usr/share/xsessions/emacs.desktop and put in it something like this
    [Desktop Entry]


Install from GNU ELPA

To install EXWM and all its dependencies, simply invoke M-x package-install RET exwm RET.

Install from source

You should first checkout the code of XELB and EXWM, then add their source directories to load-path like:

(add-to-list 'load-path "/path/to/xelb/")
(add-to-list 'load-path "/path/to/exwm/")

Note: Emacs 24 users should install the cl-generic package from GNU ELPA as well.


For those impatient: you might start trying EXWM with the following minimal steps. You should revisit this document later to tweak EXWM if you decide to use it. Also, there is an example configuration that you might find it useful as a starting point.

  1. Add following lines to your .emacs:

    (require 'exwm)
    (require 'exwm-config)
  2. Link or copy xinitrc (from source directory) to ~/.xinitrc.

  3. Start EXWM from a console (e.g. tty1) with

    xinit -- vt01

Note: Do not use (exwm-config-example) in your .emacs when you have a customized configuration.

P.S. New users often wonder how to properly launch an application in EXWM. The answer is it doesn't matter because EXWM is a serious X window manager and knows how to do it correctly. Do not launch applications with M-! (shell-command) though as it will block Emacs and therefore freeze EXWM. The default/example configuration provides s-& to do this conveniently.


This section discusses some conventions which you should make clear before going through the rest of this document.

Note: EXWM must be enabled with (exwm-enable) (this is already taken care of in (exwm-config-default)) before the first Emacs frame is spawned. Thus you should add (exwm-enable) to your .emacs. It's always safe to call exwm-enable: EXWM will just complain it's unable to start if there is another window manager. It is also possible to start EXWM from the Emacs daemon: emacs --daemon -f exwm-enable.

An X window in this document exclusively refers to a top-level X window created by an application. An application may create multiple such X windows; EXWM just manages them respectively. That is, what EXWM sees are merely top-level X windows; it does not care about which application creates them.

exwm-mode is a major mode provided by EXWM to manage X windows. A buffer created in exwm-mode records all information about the corresponding X window. exwm-mode also provides useful features to interact with that X window. Things dedicated to an exwm-mode buffer are said to be local; otherwise, they are global.

An exwm-mode buffer has two input modes: line-mode and char-mode (these phrases are borrowed from ansi-mode). They define how key events should be processed. In line-mode, EXWM intercepts all key events and sends applications only keys not explicitly assigned. Whereas in char-mode, EXWM does no interception but only responds to those globally grabbed key sequences. exwm-mode buffers are created in line-mode by default. We will discuss how to switch input mode later.

Every exwm-mode buffer also has a major mode menu, from which most commands or other features are accessible. However it has the limitation that you can not access it from other buffers, so use it as a reference/reminder rather than totally relying on it.

As a tiling X window manger, EXWM manages X windows in a tiling layout by default. However, if an X window explicitly requires it, EXWM can alternatively manage it in a floating (or stacking) layout. There are several ways for a user to switch the layout of an X window, as will be discussed later in this document.


EXWM is a keyboard-driven X window manager, thus it's of great importance to understand key event representations and learn how to specify/modify key bindings. Fortunately, EXWM uses the same syntax as Emacs itself to denote key events, so you should feel it quite comfortable to configure it. Besides, it's rather simple to find the representation of a key event by evaluating (read-key) or (read-event) when you are not sure.

As with Emacs, EXWM uses a key sequence (or key for short) rather than a single key event to make a key binding. Indeed, key sequences in EXWM are registered to either global or local keymaps just like what is normally done in Emacs; what EXWM does is to forward those key sequences to Emacs if they are originally intended for other X windows.

One thing worth mentioning is the s- (Super) modifier key. Key sequences consisting of this modifier key (or at least some of them) are usually unusable to Emacs if you are working in other X window managers. However, in EXWM you have full access to any key sequence (though there might still be some rare exceptions like Ctrl Alt F1). The s- modifier key will be frequently used in this document.

Global key bindings

Global key bindings are available in both line-mode and char-mode. They even work in normal buffers; take this into account when you make a global key binding.

Global key bindings can be defined by customizing exwm-input-global-keys.

By default there is no global key biding.

Key bindings in the following table are recommended however:

Key Command Remark
s-r exwm-reset Switch to line-mode;
exit fullscreen mode;
refresh layout
s-w exwm-workspace-switch Interactively switch workspace
s-N (exwm-workspace-switch-create N) Switch to workspace N (N is a digit)

If you decide to set exwm-input-global-keys directly, here is an example:

(setq exwm-input-global-keys
      `(([?\s-r] . exwm-reset)
        ([?\s-w] . exwm-workspace-switch)
        ,@(mapcar (lambda (i)
                    `(,(kbd (format "s-%d" i)) .
                      (lambda ()
                        (exwm-workspace-switch-create ,i))))
                  (number-sequence 0 9))))

Note: customizing exwm-input-global-keys may introduce a bit of a quirk or two on GNU-Linux operating system (described below), so one may prefer setting the exwm-input-prefix-keys instead to avoid this quirk. However, exwm-input-prefix-keys works only in EXWM line-mode and not in EXWM char-mode.

The quirks of exwm-input-global-keys one may experience are:

  • customization of exwm-input-global-keys should be done before calling exwm-enable, any changing to exwm-input-global-keys after EXWM has finished instantialization won't take effect.
  • customize exwm-input-global-keys may make starting EXWM a bit slower than usual.

Local key bindings

Local key bindings are only available in line-mode. The only way to make a local key binding is to modify exwm-mode-map with e.g. define-key.

(define-key exwm-mode-map [?\C-q] 'exwm-input-send-next-key)

When defining local keys prefixed with C-c, you're only allowed to use letters in the remaining of the sequence. Other keys are considered mode-specific and are reserved for either EXWM or other minor modes. Below is a list of mode-specific keys currently found in EXWM:

Key Command Remark
C-c C-f exwm-layout-set-fullscreen Enter fullscreen mode
C-c C-h exwm-floating-hide Hide a floating X window
C-c C-k exwm-input-release-keyboard Switch to char-mode
C-c C-m exwm-workspace-move-window Move X window to another workspace
C-c C-q exwm-input-send-next-key Send a single key to the X window;
can be prefixed with C-u to send multiple keys
C-c C-t C-f exwm-floating-toggle-floating Toggle between tiling and floating mode
C-c C-t C-m exwm-layout-toggle-mode-line Toggle mode-line

The following snippet can be used to completely disable mode-specific keys:

(define-key exwm-mode-map (kbd "C-c") nil)

Simulation keys

A simulation key exploits a local key binding to map one key sequence to another and send it to the X window. There are no predefined simulation keys in EXWM. One may customize exwm-input-simulation-keys to easily define them. One may also set its value directly:

(setq exwm-input-simulation-keys
      '(([?\C-b] . [left])
        ([?\C-f] . [right])
        ([?\C-p] . [up])
        ([?\C-n] . [down])
        ([?\C-a] . [home])
        ([?\C-e] . [end])
        ([?\M-v] . [prior])
        ([?\C-v] . [next])
        ([?\C-d] . [delete])
        ([?\C-k] . [S-end delete])))

This example enables Emacs-like line-editing keys for normal applications. Likewise, one should use e.g. C-c C-q C-v to send Ctrl v since it's mapped to next (PgDn).

Note: Simulation keys are currently sent using SendEvent X requests, so they will not work for a minority of applications. Some of them can be configured to accept synthetic events however. For example, you can enable the allowSendEvents X resource of xterm to achieve this.

Simulation keys defined in exwm-input-simulation-keys are shared among all applications. To make this X window-specific, i.e. buffer-local, exwm-input-set-local-simulation-keys can be used instead. For example, the following lines will disable simulation keys for Firefox:

(add-hook 'exwm-manage-finish-hook
          (lambda ()
            (when (and exwm-class-name
                       (string= exwm-class-name "Firefox"))
              (exwm-input-set-local-simulation-keys nil))))

Button-related key bindings

Button-related key bindings are only used when moving/resizing an X window. Currently there are only two such use cases:

Key Variable Remark
s-<down-mouse-1> exwm-input-move-event Move X window
s-<down-mouse-3> exwm-input-resize-event Resize X window

You may change the behaviors by setting the corresponding variables. Be sure to include a button-down event (e.g. <down-mouse-1>) in the key sequence to make it sense.

Input Method

After setting up EXWM, you can use Emacs built-in input method, e.g. C-\ and choose chinese-py. However, the input method will only work in the emacs buffer. If you switch to another buffer like LibreOffice, C-\ will not work in line-mode.

In order to make the input method work for each buffer, you can use exwm-xim. There is emacs init setup example under

Generally, the following will work:

#+BEGIN_SRC emacs-lisp
  (require 'exwm)

  ;; simple system tray
  (require 'exwm-systemtray)

  (require 'exwm-config)

  ;; using xim input
  (require 'exwm-xim)
  (push ?\C-\\ exwm-input-prefix-keys)   ;; use Ctrl + \ to switch input method

If you have problem, please refer to the issue

GPG pinentry

Some people may find problems in using GPG under EXWM, especially the pinentry is not working properly.

It is recommended to not use the external pinentry, rather, we can setup to use the emacs internal pinentry. The following works for me.

  1. emacs config
#+BEGIN_SRC emacs-lisp
    ;; let's get encryption established
    (setenv "GPG_AGENT_INFO" nil)  ;; use emacs pinentry
    (setq auth-source-debug t)

    (setq epg-gpg-program "gpg2")  ;; not necessary
    (require 'epa-file)
    (setq epa-pinentry-mode 'loopback)
    (setq epg-pinentry-mode 'loopback)

    (require 'org-crypt)
  1. ~/.gnupg/gpg-agent.conf
  1. restart gpg
#+BEGIN_SRC shell
gpgconf --reload gpg-agent

Let us know if you have further problem.

There is also a pinentry-tty tutorial on emacs, which depends on pinentry.el.

  1. emacs config
#+BEGIN_SRC emacs-lisp
;You can use your favorite package manager to install pinentry, and then start pinentry when you start emacs. 
(use-package pinentry
  :ensure t
  (pinentry-start) )
  1. ~/.gnupg/gpg-agent.conf
pinentry-program /usr/bin/pinentry-tty
  1. ~/.xprofile
export GPG_TTY=$(tty)
  1. restart gpg
#+BEGIN_SRC shell
gpgconf --reload gpg-agent

Layout modes

There are three layout modes supported in EXWM, i.e. tiling, floating and (inaccurately) fullscreen.

There is nothing special about the tiling mode. An X window is shown where its underlying buffer is displayed. You can use C-x b, C-x 1, C-x 2, C-x 3 or whatever you normally use to switch buffer / resize Emacs window.

The floating mode is a bit different. An extra Emacs frame is created to hold the X window. By default, a floating X window can be moved (or resized) by holding s-<down-mouse-1> (or s-<down-mouse-3>) when dragging the mouse. You can alternatively move the window with exwm-floating-move or resize it with exwm-layout-{enlarge,shrink}-window[-horizontally] which by default are not bound.

Note: X windows will automatically be made floating whenever appropriate (e.g. the applications explicitly request), unless exwm-manage-force-tiling is set to non-nil.

We regard fullscreen as a third layout mode here. An X window in either tiling or floating mode can be made fullscreen explicitly by invoking C-c C-f. If the X window provides other approaches (typically like pressing F11 or clicking a certain menu entry), they should also work. One can leave fullscreen mode with the versatile exwm-reset.


EXWM supports workspaces and they can be created or removed on-the-fly. EXWM by default creates 1 initial workspace. You may change the number to e.g. 4 with

(setq exwm-workspace-number 4)

exwm-workspace-switch, when called with no argument, allows you to switch workspace interactively. You will be provided with a prompt like

Switch to [+/-]: 0 [1] 2  3 

where [1] indicates you are currently working in Workspace 1 (the index is zero-based). You may now switch to another workspace by pressing the corresponding index, or moving [ ] with line-editing keys followed by <return>. exwm-workspace-switch optionally accepts an argument to directly switch to the target workspace.

There're several ways to add and/or remove workspaces:

  • Some workspace related commands, such as exwm-workspace-switch, when called interactively provide prompts including the tag [+/-]. You may then press + to create an empty workspace at the end, or - to remove the selected workspace.
  • The command exwm-workspace-switch-create, similar toexwm-workspace-switch, automatically creates missing workspaces when given an out-of-range index.
  • The commands exwm-workspace-add/exwm-workspace-delete allow you to add/delete a workspace at a certain position. When called interactively, exwm-workspace-add adds a workspace at the end (and switch to it), whereas exwm-workspace-delete deletes the current workspace.
  • Emacs frame keys (prefixed with C-x 5) also provide a way to manage workspaces. However, since there're other kinds of Emacs frames in EXWM such as those used in floating X windows, be careful not to mess them up.

A workspace can be moved to another position with exwm-workspace-move, and the positions of two workspaces can be interchanged with exwm-workspace-swap.

X window handling among workspaces

An X window can be moved to another workspace with C-c C-m, or exwm-workspace-move-window. An X window on another workspace can be brought to the current one with exwm-workspace-switch-to-buffer.

Note: EXWM only shows X windows belonging to the current workspace by default. You may alter this behavior by assigning exwm-workspace-show-all-buffers a non-nil value. Also, you might want to set exwm-layout-show-all-buffers to t to allow automatically moving X windows from inactive workspaces by switching to their associated buffers.

Note: You are not supposed to move floating X windows when exwm-layout-show-all-buffers is non-nil.

Autohide minibuffer & echo area

As with normal Emacs frames, the minibuffer and echo area (sharing the same mini-window) are fixed at the bottom of each workspace by default. They can be detached and automatically shown/hidden/resized by setting exwm-workspace-minibuffer-position to either 'bottom or 'top, which indicates the position of the minibuffer & echo area when they appear. The minibuffer is shown when it's entered and hidden when exited. The echo area is shown there're new messages and hidden after a certain amount of time, which is controlled by exwm-workspace-display-echo-area-timeout (in seconds).

The autohide minibuffer & echo area can be attached back with exwm-workspace-attach-minibuffer, and then detached again with exwm-workspace-detach-minibuffer.

Note: exwm-workspace-attach-minibuffer requires no dock/panel at the same place to work. For example, if exwm-workspace-minibuffer-position is set to 'bottom, then there shouldn't be any dock/panel at the bottom of the screen if you want to attach the autohide minibuffer & echo area.

Emacs Frame & Workspaces

EXWM creates a new workspace for every new Emacs Frame. Otherwise, Emacs will hang.

RandR (multi-screen)

The RandR support is optional and disabled by default. To enable it, add the following code to your .emacs:

(require 'exwm-randr)
(setq exwm-randr-workspace-output-plist '(0 "VGA1"))
(add-hook 'exwm-randr-screen-change-hook
          (lambda ()
             "xrandr" nil "xrandr --output VGA1 --left-of LVDS1 --auto")))

The second line actually configures the multiple screens support. The variable exwm-randr-workspace-output-plist is a property list of the form (workspace-number-1 output-name-1 workspace-number-2 output-name-2 ...). You can find a list of output names together with their info by invoking xrandr utility with no argument.

The third line puts output VGA1 on the left of LVDS1 and automatically resizes the screen every time a monitor is attached/detached. Please refer to xrandr(1) for the detailed usage of xrandr.

If you are looking for an automated behavior to only enable the connected external screen (and automatically revert to the internal screen after disconnection), here is an example that can be added to exwm-randr-screen-change-hook:

(defun exwm-change-screen-hook ()
  (let ((xrandr-output-regexp "\n\\([^ ]+\\) connected ")
      (call-process "xrandr" nil t nil)
      (goto-char (point-min))
      (re-search-forward xrandr-output-regexp nil 'noerror)
      (setq default-output (match-string 1))
      (if (not (re-search-forward xrandr-output-regexp nil 'noerror))
          (call-process "xrandr" nil nil nil "--output" default-output "--auto")
         "xrandr" nil nil nil
         "--output" (match-string 1) "--primary" "--auto"
         "--output" default-output "--off")
        (setq exwm-randr-workspace-output-plist (list 0 (match-string 1)))))))

Compositing manager

As of 0.17 EXWM supports third-party compositing managers and the builtin one are no longer provided.

System tray

EXWM provides a simple system tray. It's disabled by default and can be enabled with the following lines:

(require 'exwm-systemtray)

Note: The system tray is displayed on active workspace only.

Query EXWM for window information

Getting process ID of an EXWM buffer


(let* ((buf (or buffer-or-name (current-buffer)))
       (id (exwm--buffer->id (get-buffer buf)))) ; ID of X window being displayed
     (if id
          (slot-value (xcb:+request-unchecked+reply
                           (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))
        (user-error "Target buffer %S is not an X window managed by EXWM!"

One last thing

EXWM cannot make an X window manager by itself; you must tell X to do it. So first in your ~/.xinitrc, put a line

exec emacs

to launch Emacs when X starts. Alternatively, if you want to run the daemon:

exec emacsclient -a "" -c

And if you want to leave (exwm-enable) out of your config (which does no harm anyway):

emacs --daemon -f exwm-enable
exec emacsclient -c

Sometimes you should disable access control by prepending a line to this file if you get a No protocol specified error:

xhost +SI:localuser:$USER

Then EXWM can be launched in console with

xinit -- vt01

where vt01 indicates you are starting X from tty1.

Quitting EXWM is as easy as how you do in Emacs (C-x C-c perhaps). You can also restart EXWM in place with exwm-restart.

EXWM on top of another WM

One can run EXWM on top of another window manager (eg. XFCE4) as well. start the "other" window manager, then start Emacs in order to start EXWM, then EXWM should be running on top of the "other" window manager.



How to report a bug?

  1. Make sure you are using the most updated code (including XELB).

  2. Check the issues tracker to see whether this has been reported/fixed.

  3. Insert the following snippet into your .emacs (comment out the second line if this bug makes Emacs frozen, then use C-g to quit):

    (setq debug-on-error t)
    ;; (setq debug-on-quit t)
    (setq edebug-all-forms t)
  4. Turn on exwm-debug minor mode and try to reproduce it. The result would be in the *XELB-DEBUG* buffer. You can use C-c C-d C-t to toggle debugging, C-c C-d C-m to clear the entire log and C-c C-d C-m to add a mark (^L) for the current log position.

  5. Try reproducing the issue with a minimal configuration:

    emacs -Q --eval "(progn (package-initialize) (require 'exwm-config) (exwm-config-default))"
  6. Open an issue with a descriptive title, the bug label selected, and the following contents:

    • A detailed description of the problem, perhaps with contents from *Backtrace* and *XELB-DEBUG* buffers. Remember to redact the contents of both buffers before posting, as they might contain private information (e.g., in window or buffer names).
    • Minimal steps to reproduce it
    • The possible cause of the problem (like some special configuration)
    • Emacs version, architecture, UI toolkit and system info
    • X server version (Xorg -version)

Efficiency? Concurrency?

XELB/EXWM are efficient enough to handle most X11 transactions. They also run concurrently.

How to send keyboard-quit (C-g) when the input is stuck?

You can switch to a TTY and send the signal specified in debug-on-event (defaults to SIGUSR2) to the emacs process. For instance:

pkill -USR2  emacs

Make sure not to kill emacsclient if you run it.

How to send C-c to *Term*?

C-c is frequently used in terminal emulators, but since it's by default a prefix key in EXWM, it won't get received by applications normally. Here are some workarounds:

  • Use ansi-term instead.

  • Work with terminal emulators in char-mode.

  • Use exwm-input-send-next-key (bound to C-c C-q by default) to send C-c.

    Note: See also Simulation keys for the note on XTerm.

  • Configure EXWM to send C-c with C-c C-c. An example for XTerm:

    (add-hook 'exwm-manage-finish-hook
              (lambda ()
                (when (and exwm-class-name
                           (string= exwm-class-name "XTerm"))
                  (exwm-input-set-local-simulation-keys '(([?\C-c ?\C-c] . ?\C-c))))))

    Note: This approach does not work with Emacs 25 due to a bug of Emacs which is fixed in Emacs 26.2.

    Note: See also Simulation keys for the note on XTerm.

How to make focus follow mouse?

You will need to set mouse-autoselect-window and focus-follows-mouse BEFORE loading EXWM, i.e.

(setq mouse-autoselect-window t
      focus-follows-mouse t)




(require 'exwm)

Font size too small on HiDPI displays

This is not an X window manager issue. Please refer to this ArchWiki on how to fix this problem.

Java applications not drawing properly

Java assumes most WMs reparent X windows. To make Java applications aware that EXWM is a non-reparenting WM, please add the following line to ~/.xinitrc:


Will there be a Wayland port?

Perhaps, but Emacs itself should have a Wayland port first.

As with X11, Wayland is also a network protocol and can be implemented as Elisp libraries just like XELB. Indeed, I (@ch11ng) once made some (unpublished) POC code, but there is very little I can do with it. I turns out it's not possible to implement a Wayland compositor (server) with pure Elisp; we have to find a workaround.

What about a status bar?

EXWM does not provide a status bar itself but it is known to run well together with dzen2 or xf-panel.

For dzen, you can put something like this in your .xinitrc:

watch date | dzen2 -dock -p &
exec emacs

Alternatively, Emacs has various mode-line / minibuffer monitors, such as the builtins display-time and display-battery-mode or the symon package.

How to set the default input mode to char-mode?

You can simply do

(setq exwm-manage-configurations '((t char-mode t))).

Known issues

Issues caused by the single-threaded nature of Emacs

EXWM runs concurrently, but it is only true when the event loop of Emacs is not blocked. However, because Emacs is currently single-threaded, this seems inevitable in some situations especially when Emacs tries to display some UI widgets:

  • Widgets such as menus which have OverrideRedirect set may have a chance to work.
  • Other widgets like dialog boxes probably can not work at all: they would keep waiting for responses from the X window manager but unfortunately EXWM would have been blocked by that time.

The workaround to this issue is to avoid the use of these features or turn to their text-based alternatives. For instance, most menus can be accessed through M-`.

Unable to resize an X window by dragging its right edge

This issue occurs with non-floating X windows. If you are used to resize Emacs windows this way, please enable window-divider-mode as a workaround:

(setq window-divider-default-right-width 1)

Minor issues related to the autohide echo area

The autohide echo area can not be activated in certain circumstances:

  • Emacs by default echoes keystrokes on echo area on slow input. There seems to be no way to detect such behavior so the autohide echo area feature may cause problems for some users. You can disable keystroke echoing by setting echo-keystrokes to 0.
  • When tooltip-mode is disabled, Emacs displays tooltips on echo area, which again is hard to detect. You're encouraged to keep tooltip-mode enabled (the default behavior) if you use the autohide minibuffer feature.

Note you can always use exwm-workspace-attach-minibuffer and exwm-workspace-detach-minibuffer to temporarily pin the echo area (and also the minibuffer).

An issue with ido-mode

When switching to a buffer (not in exwm-mode) currently displayed on another workspace (frame), ido-mode would raise that workspace instead of displaying the buffer in the selected Emacs window. This is probably an unwanted behavior and can be disabled by adding


to your .emacs.

An issue with ediff

ediff on graphical displays creates new frames. EXWM opens any new frame in workspace of it's own. As a result, you will see that the window displaying your source files are on one workspace and the *"*Ediff Control Panel"* is on a new workspace. You will also notice that the *Ediff Control Panel* is not filled.

You can workaround this issue by two means

  1. Customize ediff to open the *Ediff Control Panel* in a new window instead of a new frame.
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
  1. Modify
(with-eval-after-load 'ediff-wind
  (setq ediff-control-frame-parameters
	(cons '(unsplittable . t) ediff-control-frame-parameters)))

Use option (2) if you prefer your *Ediff Control Panel* as a floating frame, rather than as an Emacs window.


Desktop-save-mode does not save on exit

This depends on how you fire up Emacs (e.g. as a daemon). C-x C-c is bound to save-buffers-kill-terminal, which will exit the client but not the daemon. The latter might be force-killed before it has time to run through all the hooks in kill-emacs-hook (desktop-kill among others).

A simple fix is to call save-buffers-kill-emacs instead:

(global-set-key (kbd "C-x C-c") 'save-buffers-kill-emacs)

Logging out with LXDE

The standard way to close emacs is with C-x C-c (save-buffers-kill-terminal). When running exwm with LXDE however, this does not work since closing emacs does not (AFAIK) allow to close LXDE cleanly. Running lxsession-logout on the other hand does not give emacs time to cleanly close.

The Arch Linux wiki suggests to use this function to logout:

(defun exwm-logout ()
  (start-process-shell-command "logout" nil "lxsession-logout"))


Bookmarks not saved on exit

Emacs also runs a number of functions attached to kill-emacs-hook and, by default, it also saves bookmarks on exit. It is unknown to me whether the Arch Linux wiki function (see above) runs the kill-emacs-hooks, but it does not save the bookmarks. Adding (bookmark-save) to that function should, in theory, solve this problem. But in my case, it does not, probably because the logout process kicks in too quickly and does not give emacs enough time to perform that command. This makes me question whether the other commands always have time to complete...

A way around this is to run the function:

(defun exwm-logout ()

(add anything else you want to it) and then logging out by calling lxsession-logout (by binding it to a 2nd function in emacs or by any other way you usually run programs in LXDE). This 2 step process assures that emacs has time to complete all commands properly.

If the only problem is with bookmarks, another option is to simply save bookmarks with M-x bookmark-save or to customize the variable bookmark-save-flag and use the Arch Linux wiki function. I personally remain worried that some of the commands may not always successfully complete before logout and prefer the 2 steps method.

Note: I have not tried playing with exwm-exit-hook and do not know whether adding bookmark-save to it would work or whether the LXDE logout process would still interfere with the command being properly executed.

Issues caused by using --with-x-toolkit=athena and xcape

It has been reported that remapping keys with xcape does not work correctly when using Emac's --with-x-toolkit=athena toolkit. This can be solved by using other toolkit when building Emacs, for example --with-x-toolkit=no or --with-x-toolkit=gtk.

Issues with Xmodmap

It's found that there will be a long delay in response if Xmodmap is used in changing keyboard mapping. Because Xmodmap is inefficient in handling a file with too many lines. It's built around Xlib and would make an individual call to XChangeKeyboardMapping (which is synchronous) for each effective line beginning with keycode. This may not be noticeable for the C implementation but really a challenge for XELB.

In addition, Xmodmap and xcape mentioned above won't work in the console and neither in a virtual machine guest (vmware). Two better alternatives (works every where, no need to specify hardware ids) will be

Android Emulator window is flickering in tiling mode

Android Emulator is wrongfully detected to be able to work in tiling mode. Select it's window and press C-c C-t C-f to enable floating mode. You can force it to be created in floating mode with the following configuration:

(setq exwm-manage-configurations '(((string-match-p "^ ?Android Emulator -" exwm-title)
                                    floating t)))

Issues with screen tearing

If you encounter issues with screen tearing, one possible way to deal with this is to install a compositor like compton or picom. There are a couple of configuration values you can set in order to try to mitigate it.

Adding ForceFullCompositionPipeline for nvidia users

If you have an nvidia graphics card and adding and configurating a compositor did not help with screen tearing, you may be able to turn on ForceFullcompositionPipeline to deal with it. The parameters we need to add are setup-specific. In general, there are three things you need:

  1. The amount of monitors you use
  2. The identifier of each monitor
  3. The offset of each monitor

You can find out all of that running the xrandr command:

Screen 0: minimum 8 x 8, current 5120 x 1440, maximum 32767 x 32767
DP-0.8 connected primary 2560x1440+0+0 (normal left inverted right x axis y axis) 553mm x 311mm
^^^^^^ Identifier                 ^^^^ Offset
   2560x1440     59.95*+
   2048x1080     60.00    24.00  
   1920x1080     60.00    59.94    50.00  
   1600x1200     60.00  
   1280x1024     75.02    60.02  
   1280x720      60.00    59.94    50.00  
   1152x864      75.00  
   1024x768      75.03    60.00  
   800x600       75.00    60.32  
   720x576       50.00  
   720x480       59.94  
   640x480       75.00    59.94    59.93  
DP-0.1.8 connected 2560x1440+2560+0 (normal left inverted right x axis y axis) 553mm x 311mm
^^^^^^^^ Identifier         ^^^^^^^ Offset
   2560x1440     59.95*+
   2048x1080     60.00    24.00  
   1920x1080     60.00    59.94    50.00  
   1600x1200     60.00  
   1280x1024     75.02    60.02  
   1280x720      60.00    59.94    50.00  
   1152x864      75.00  
   1024x768      75.03    60.00  
   800x600       75.00    60.32  
   720x576       50.00  
   720x480       59.94  
   640x480       75.00    59.94    59.93  
DVI-D-0 disconnected (normal left inverted right x axis y axis)
HDMI-0 disconnected (normal left inverted right x axis y axis)
DP-0 disconnected (normal left inverted right x axis y axis)
DP-1 disconnected (normal left inverted right x axis y axis)
HDMI-1 disconnected (normal left inverted right x axis y axis)

I have two monitors connected. The identifiers and offset are marked in the snippet above. In my case, that means:

  1. monitor: DP-0.8, offset: +0+0
  2. monitor: DP-0.1.8, offset: +2560+0

That's all the information you need. Open your X11 config file, usually located at /etc/X11/xorg.conf.d/20-nvidia.conf. If you don't have one yet, simply create it. The name of the file does not matter as long as it is intuitive to you.

Paste the following code into it:

Section "Device"
        Identifier "Nvidia Card"
        Driver "nvidia"
        VendorName "NVIDIA Corporation"
        Option "NoLogo" "True"
        Option "TripleBuffer" "True"

Now we need to the the option MetaModes below the TripleBuffer option. It takes the following shape:

Option "MetaModes" "<id of monitor 1>: nvidia-auto-select <offset of monitor 1> { ForceFullCompositionPipeline = On }"

You can add multiple monitors by simply concatenating with a ,. In my case that would be:

Option "MetaModes" "DP-0.8: nvidia-auto-select +0+0 { ForceFullCompositionPipeline = On },
                    DP-0.1.8: nvidia-auto-select +2560+0 { ForceFullCompositionPipeline = On }"

The whole config file should now look similar to this:

Section "Device"
        Identifier "Nvidia Card"
        Driver "nvidia"
        VendorName "NVIDIA Corporation"
        Option "NoLogo" "True"
        Option "TripleBuffer" "True"
        Option "MetaModes" "DP-0.8: nvidia-auto-select +0+0 { ForceFullCompositionPipeline = On }, DP-0.1.8: 
        nvidia-auto-select +2560+0 { ForceFullCompositionPipeline = On }"

You're done. Restart your PC for the changes to take effect.

Public interfaces

This section contains lists of public interfaces that you might find useful when customizing EXWM. Please refer to their documentations for more details.

List of commands/functions

  • exwm-config-example
  • exwm-config-ido
  • exwm-config-misc
  • exwm-debug-log-time
  • exwm-debug-log-uptime
  • exwm-enable
  • exwm-exit
  • exwm-floating-hide
  • exwm-floating-move
  • exwm-floating-toggle-floating
  • exwm-init
  • exwm-input-grab-keyboard
  • exwm-input-release-keyboard
  • exwm-input-send-next-key
  • exwm-input-send-simulation-key
  • exwm-input-set-key
  • exwm-input-set-simulation-key
  • exwm-input-set-simulation-keys
  • exwm-input-toggle-keyboard
  • exwm-layout-enlarge-window
  • exwm-layout-enlarge-window-horizontally
  • exwm-layout-hide-mode-line
  • exwm-layout-set-fullscreen
  • exwm-layout-show-mode-line
  • exwm-layout-shrink-window
  • exwm-layout-shrink-window-horizontally
  • exwm-layout-toggle-fullscreen
  • exwm-layout-toggle-mode-line
  • exwm-layout-unset-fullscreen
  • exwm-randr-enable
  • exwm-randr-refresh
  • exwm-reset
  • exwm-restart
  • exwm-systemtray-enable
  • exwm-workspace-add
  • exwm-workspace-attach-minibuffer
  • exwm-workspace-delete
  • exwm-workspace-detach-minibuffer
  • exwm-workspace-move
  • exwm-workspace-move-window
  • exwm-workspace-rename-buffer
  • exwm-workspace-swap
  • exwm-workspace-switch
  • exwm-workspace-switch-create
  • exwm-workspace-switch-to-buffer
  • exwm-workspace-toggle-minibuffer
  • exwm-xim-enable

List of user options

  • exwm-blocking-subrs
  • exwm-debug-log-time-function
  • exwm-exit-hook
  • exwm-floating-border-color
  • exwm-floating-border-width
  • exwm-floating-exit-hook
  • exwm-floating-setup-hook
  • exwm-init-hook
  • exwm-input-global-keys
  • exwm-input-input-mode-change-hook
  • exwm-input-line-mode-passthrough
  • exwm-input-move-event
  • exwm-input-prefix-keys
  • exwm-input-pre-post-command-blacklist
  • exwm-input-resize-event
  • exwm-input-simulation-keys
  • exwm-layout-auto-iconify
  • exwm-layout-show-all-buffers
  • exwm-manage-configurations
  • exwm-manage-finish-hook
  • exwm-manage-force-tiling
  • exwm-manage-ping-timeout
  • exwm-randr-refresh-hook
  • exwm-randr-screen-change-hook
  • exwm-randr-workspace-monitor-plist
  • exwm-replace
  • exwm-systemtray-background-color
  • exwm-systemtray-height
  • exwm-systemtray-icon-gap
  • exwm-update-class-hook
  • exwm-update-title-hook
  • exwm-workspace-display-echo-area-timeout
  • exwm-workspace-index-map
  • exwm-workspace-list-change-hook
  • exwm-workspace-minibuffer-position
  • exwm-workspace-number
  • exwm-workspace-show-all-buffers
  • exwm-workspace-switch-create-limit
  • exwm-workspace-switch-hook
  • exwm-workspace-warp-cursor

List of global variables (other than user options)

  • exwm-mode-map
  • exwm-workspace-current-index

List of buffer-local variables

  • exwm-class-name
  • exwm-instance-name
  • exwm-state
  • exwm-title
  • exwm-transient-for
  • exwm-window-type


Both XELB and EXWM are dual-hosted on GitHub and GNU Savannah. GNU Emacs developers may bypass me and make changes to these projects directly; I will keep both repositories in sync. However, due to copyright issues we cannot accept significant changes from other developers. If you want to contribute though, you still have several options:

  • Complete the copyright assignment paperwork and become a GNU contributor.
  • Instead of implementing a feature, you may introduce the idea.
  • You might as well present a draft implementation so that we'll reimplement it later.

Note that the copyright restriction does not apply to minor changes or modifications of the wiki pages here.

Third-party extensions

  • Helm-EXWM: Helm sources and functions for browsing and switching to EXWM buffers.
  • GPastel: The Emacs package gpastel makes sure that every copied text in GPaste is also in the Emacs kill-ring. When using EXWM (the Emacs X Window Manager), gpastel makes it possible for the user to use the kill-ring from external applications.
  • Desktop-environment: This package lets you control your computer with standard keys. For example, <XF86MonBrightnessUp> raises brightness, <XF86AudioRaiseVolume> raises volume, <print> takes screenshots and s-l locks your screen.
  • Edit EXWM allows you to edit things in a separate Emacs buffer


This is a brief record of major changes made to each release.


Major fixes:

  • Support 32-bit visuals (Emacs' alpha-background).
  • Systemtray supports adjusting its background color to Emacs' background color.
  • Hide tab-bar in floating windows.
  • Improvements to focus support on Emacs 29


  • Systemtray does not support transparency when Emacs uses 32-bit visuals. Help wanted!

A warm welcome to new EXWM contributors Elijah Malaby (@djeis97), Manuel Giraud (@mgi) and James (@j4m3s-s). Thank you!

Special thanks to long-time contributor Steven Allen (@Stebalien), and to everyone helping each other in the tickets. Thank you as well!


What's new:

  • Cache querying of frame parameters for improved performance.
  • Let third party libraries detect XIM buffers (exwm-xim-buffer-p).

A warm welcome to new EXWM contributors Feng Shu (@tumashu) and Matt Beshara (@mattbeshara). Thank you!


What's new:

  • New (acting) maintainer: Adrián Medraño Calvo (@medranocalvo).

Major fixes:

  • Make winner-mode aware of windows where only mouse event happened.
  • Reset fullscreen state when unsetting fullscreen mode.


What's new:

  • Support accessing keymaps in char-mode (exwm-input-invoke-factory).
  • Support customizing systemtray background color (exwm-systemtray-background-color).
  • New hook run when input mode changes (exwm-input-input-mode-change-hook).

Major fixes:

  • Various display issues with menu-bar-mode/tool-bar-mode on.
  • winner-mode not working properly on exwm-mode buffers.


What's new:

  • The RandR module now supports monitor mirroring.
  • Various user options improved.

Major fixes:

  • XIM module drops keys.
  • The new hook after-focus-change-function not triggered.
  • For key combinations corresponding to a same Emacs event, only one of them is likely to work.
  • Systemtray / floating X windows are positioned incorrectly with menu-bar/tool-bar enabled.
  • Impossible to answer questions from Emacs in char-mode.
  • False positive 'application not responding' alerts.


What's new:

  • New exwm-xim module for using Emacs builtin input methods in X windows.
  • Keep auto-hide echo area displayed when no input is received.

Major fixes:

  • User customized options overridden by exwm-config-default.
  • X windows started before EXWM lose input focus.


What's new:

  • Support for RandR 1.5 'monitor'.
  • New exwm-randr-refresh public interface.
  • EXWM now automatically adjusts to screen changes initiated by external tools.
  • Support for 'managed' per-application configuration to allow users to specify whether to manage an X window.

Major fixes:

  • Start with a single workspace may fail.
  • Keyboard grab state not restored after entering/exiting full screen mode.
  • Floating X windows may be incorrectly stacked below tiling ones.
  • Performance degradation after triggering a global keybinding.


What's new:

  • Support making binding for button events.
  • New exwm-debug minor mode for debugging.

Major fixes:

  • X windows occasionally rendered on wrong workspaces.
  • X windows on active workspaces occasionally not rendered.


What's new:

  • Allow specifying the initial workspace for each application
  • Automatically warp cursor after switching workspace (exwm-workspace-warp-cursor)

Major fixes:

  • Local simulation keys inaccessible
  • line-mode incompatible with Xorg server 1.20
  • Full screen X windows hidden after switching buffer
  • X windows not properly rendered after resizing


What's new:

  • Add support for replacing / being replaced by other window managers. exwm-init and exwm-exit can also be used to manually start/stop EXWM.
  • The Customize interface now supports customizing global keys.
  • Add per-application support for tweaking the initial states of an individual application. It can be accessed via the exwm-manage-configurations user option.
  • Mode-specific keys are now enabled by default without the need to add their prefix keys to exwm-input-prefix-keys.
  • exwm-workspace-switch, exwm-workspace-switch-create, exwm-workspace-move and exwm-workspace-move-window now support prefix arguments.

Major fixes:

  • X windows not being hidden after switching workspace.
  • Fullscreen X window misbehaves after switching workspace.
  • display-buffer-in-side-window not working with X windows.


What's new:

  • Make EXWM a non-reparenting window manager. As a result third-party compositing managers are now supported and the built-in one (exwm-cm) is thus deprecated.
  • Add a new Customize interface for accessing most features. It can be accessed via M-x customize RET and then selecting Applications / EXWM.
  • Add support for 'focus follows mouse' (requiring mouse-autoselect-window and focus-follows-mouse both being set).
  • Add support for input-decode-map, local-function-key-map and key-translation-map which can be used for tweaking line-mode.
  • Add support for displaying floating X windows on all workspaces (by setting _NET_WM_DESKTOP to 0xffffffff).

Major fixes:

  • Wrong context-menu position for Firefox.
  • Global keys not updated after keyboard update.
  • Docks not accessible after quitting fullscreen mode.
  • Emacs crashes after resizing the width/height of a floating X window to 0.