Complete Computing Environment: EXWM Configuration

Table of Contents

EXWM

EXWM is a window manager written in Emacs. Some crazy cat went off and implemented XCB in Emacs-Lisp and then wrote a window manager with it. It works surprisingly well, and you can do some incredibly neat things with it, since it's adhering really nicely to the X11 standard stuffs.

(provide 'cce-exwm)

Install slock, my lockscreen of choice

- name: slock installed
  dnf:
    name: slock
    state: installed
  when: ansible_pkg_mgr == "dnf"

- name: slock installed
  apt:
    name: slock
    state: installed
  when: ansible_pkg_mgr == "apt"

Install exwm using MELPA.

(install-pkgs '(cl-generic exwm))

Load EXWM

(setq exwm-debug-on nil)
(require 'xcb)
(require 'exwm)

GIMP needs to be floating.

(add-hook 'exwm-update-class-hook
          (lambda ()
            (unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
                        (string= "gimp" exwm-instance-name))
              (rename-buffer exwm-class-name t))))
(add-hook 'exwm-update-title-hook
          (lambda ()
            (when (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
                      (string= "gimp" exwm-instance-name))
              (rename-buffer exwm-title t))))

Rename buffers to match the X11 window class or title:

(defun exwm-rename-buffer ()
  (interactive)
  (exwm-workspace-rename-buffer
   (concat exwm-class-name ":"
           (if (<= (length exwm-title) 50) exwm-title
             (concat (substring exwm-title 0 49) "...")))))

;; Add these hooks in a suitable place (e.g., as done in exwm-config-default)
(add-hook 'exwm-update-class-hook 'exwm-rename-buffer)
(add-hook 'exwm-update-title-hook 'exwm-rename-buffer)

Set up keybindings; S-r will reset the window manager if you wedge it, S-w will bring up an interactive workspace switcher, and S-0 through S-4 will switch through virtual workspaces.

(exwm-input-set-key (kbd "s-r") 'exwm-reset)
(exwm-input-set-key (kbd "s-w") 'exwm-workspace-switch)
(setq exwm-workspace-number 2)

(defun exwm-workspace-next ()
  (interactive)
  (let ((next-numb (mod (+ 1 exwm-workspace-current-index) exwm-workspace-number)))
    (exwm-workspace-switch next-numb)))
(exwm-input-set-key (kbd "s-s") 'exwm-workspace-next)
(exwm-input-set-key (kbd "s-j") 'exwm-workspace-next)
(push ?\s-j exwm-input-prefix-keys)

(exwm-input-set-key (kbd "s-o") 'other-window)
(exwm-input-set-key (kbd "s-k") 'other-window)
(push ?\s-o exwm-input-prefix-keys)
(push ?\s-k exwm-input-prefix-keys)

(defmacro exwm-switch-to-workspace-key (ws-num)
  `(progn (exwm-input-set-key (kbd (concat "s-" ,(number-to-string ws-num)))
                              (lambda ()
                                (interactive)
                                (exwm-workspace-switch ,ws-num)))
          (let ((key-num (if (eq 0 ,ws-num)
                             10
                           ,ws-num)))
            (exwm-input-set-key (kbd (concat "s-<f" (number-to-string key-num) ">"))
                                (lambda ()
                                  (interactive)
                                  (exwm-workspace-switch ,ws-num))))))
(exwm-switch-to-workspace-key 1)
(exwm-switch-to-workspace-key 0)

Super-p to run commands without dumping output to a buffer.

(defun background-shell-command (command)
  (interactive (list (read-shell-command "$ ")))
  (with-temp-buffer
   (async-shell-command command (current-buffer))))

(exwm-input-set-key (kbd "s-p")
                    #'background-shell-command)

Super-l to lock screen. This is disabled right now since I'm using KDE's Session Manager to handle these things.

slock
- name: lock script is installed
  become: yes
  become_user: rrix
  copy:
    src: out/lock.sh
    dest: ~/bin/lock.sh
    mode: 0775
(exwm-input-set-key (kbd "s-l")
                    (lambda ()
                      (interactive)
                      (start-process "lock" (get-buffer-create "*tmp*")
                                     "bash" "-c" "/home/rrix/bin/lock.sh")))
(require 'exwm-config)
(exwm-config-ido)
(push ?\C-q exwm-input-prefix-keys)
(define-key exwm-mode-map [?\C-q] 'exwm-input-send-next-key)

(exwm-input-set-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)))
(message "Enabling EXWM.")
(exwm-enable)

EXWM supports multiple desktops using XRANDR; I pin EXWM workspaces to particular outputs, assuming they're attached this will "just work". What it means in practice is that by default at work my left monitor is workspace 0, and my right monitor is workspace 1. If these aren't attached, they fall back to the active display, which is nice.

(require 'exwm-randr)
(setq exwm-randr-workspace-output-plist '(1 "DP-1" 0 "DP-2" 2 "HDMI2"))
(exwm-randr-enable)

Author: Ryan Rix

Created: 2018-04-11 Wed 11:54

Validate XHTML 1.0