complete-computing-environment/exwm.org

152 lines
7.3 KiB
Org Mode

:PROPERTIES:
:ID: cce/exwm
:END:
#+TITLE: EXWM
#+filetags: :Emacs:CCE:EXWM:Mind:Bending:
#+PROPERTY: header-args :mkdirp yes :results none
#+ARROYO_EMACS_MODULE: exwm
#+ARCOLOGY_KEY: cce/exwm
#+ARCOLOGY_ALLOW_CRAWL: t
#+begin_src emacs-lisp :tangle ~/org/cce/exwm.el
(provide 'cce/exwm)
#+end_src
The [[https://github.com/ch11ng/exwm/][Emacs X11 Window Manager]] is a desktop window manager written in and for [[id:cce/emacs][Emacs]]. EXWM takes over my desktop's window decorations, opting instead to treat windows as if they were Emacs buffers, able to exist within windows carved out of frames[fn:1:[[id:modern_interface_terms][Modern Interface Terms]]].
Right now, I use EXWM everywhere that I can get away with using it. It's more than simply an argument that "everything should run in Emacs because it should" that a lot of people get trapped in to thinking this is about. Emacs is actually a surprisingly facile window manager once I gain facility with it. Stacking window managers are, for me, a great way of organizing applications on a system, and the Emacs windowing system exposes a really decent stacking window manager. The keybindings for this system are designed such that it's even a serviceable window manager on small devices like a [[id:6834cb8f-319f-4dd9-bade-2521417f584b][GPD Pocket]]
It's configured with some simple quality-of-life improvements, outlined in short below:
- Buffers are named in Emacs based on both the window class and title, so that it's reasonable to find them again.
- There are two workspaces, that's all. I sometimes will create extra workspaces, if I am working with long-running desktop applications, simply using =C-x 5 2= aka =make-frame-command=. Switching between them is as easy as hitting =S-j=. Switching windows is bound close to this at =S-k=.
- =S-p= brings up a simple shell command runner, I'd like to add =.desktop= support eventually.
- EXWM runs in =ido= support mode, which allows it to work with other things that interact with the minibuffer.
#+begin_src emacs-lisp :noweb yes :tangle ~/org/cce/exwm.el
(use-package exwm
:init
(setq exwm-debug nil)
:commands (exwm-enable)
:config
(setq exwm-workspace-number 6)
;; rename buffers to match window title
(defun cce/exwm-rename-buffer ()
(interactive)
(exwm-workspace-rename-buffer
(format "%s:%s" exwm-class-name
(if (<= (length exwm-title) 50) exwm-title
(concat (substring exwm-title 0 49) "...")))))
;; basic keybindings
(defun cce/exwm-workspace-next (&optional reverse)
(interactive "P")
(let ((fn (if reverse #'- #'+)))
(exwm-workspace-switch (mod (apply fn (list 1 exwm-workspace-current-index))
exwm-workspace-number))))
(exwm-input-set-key (kbd "s-j") #'cce/exwm-workspace-next)
(exwm-input-set-key (kbd "s-k") #'other-window)
(exwm-input-set-key (kbd "s-r") #'exwm-reset)
(exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch)
; (exwm-input-set-key (kbd "s-o") #'evil-window-rotate-downwards)
(exwm-input-set-key (kbd "s-o") #'other-window)
(push (kbd "s-j") exwm-input-prefix-keys)
(push (kbd "s-k") exwm-input-prefix-keys)
(push (kbd "s-r") exwm-input-prefix-keys)
(push (kbd "s-o") exwm-input-prefix-keys)
(mapcar (lambda (it)
(exwm-input-set-key (kbd (format "s-%d" it))
`(lambda ()
(interactive)
(exwm-workspace-switch-create ,it))))
(number-sequence 0 exwm-workspace-number))
;; go to previously selected workspace
(defun cce/exwm-record-before-workspace-change (ws &optional force)
(setq cce-exwm-last-workspace exwm-workspace--current))
(advice-add #'exwm-workspace-switch :before #'cce/exwm-record-before-workspace-change)
(defun cce-exwm-workspace-back ()
(interactive)
(exwm-workspace-switch cce-exwm-last-workspace))
(exwm-input-set-key (kbd "s-<tab>") #'cce-exwm-workspace-back)
;; switch buffers on different displays
(setq exwm-workspace-show-all-buffers t)
(setq exwm-layout-show-all-buffers t)
;; xrandr
<<exwm-xrandr>>
;; manage configurations
<<exwm-manage-configurations>>
;; startup handling
(defcustom cce-start-exwm nil
"Whether Emacs should try to start EXWM on startup"
:type 'boolean)
(defun cce/exwm-init-command-line-args ()
"hook for command-line-functions"
(if (setq command-line-args (delete command-line-args "--no-exwm"))
(setq cce-start-exwm nil)
(setq cce-start-exwm t))
(if (setq command-line-args (delete command-line-args "--exwm"))
(setq cce-start-exwm t)
(setq cce-start-exwm nil)))
(add-to-list 'command-line-functions #'cce/exwm-init-command-line-args)
(defun cce/exwm-init (&rest args)
(when cce-start-exwm
(apply #'exwm-init args)))
(exwm-input--update-global-prefix-keys)
:hook
(exwm-update-class . cce/exwm-rename-buffer)
(exwm-update-title . cce/exwm-rename-buffer)
(after-init . cce/exwm-init))
#+end_src
EXWM also supports multiple monitors, using the fairly well supported =XRANDR= protocols. When I am in multi-monitor configurations, I prefer to spread my two workspaces across those desktops, rather than multiplying. =S-j= in this mode of operation will swap between the two displays and this can be enhanced later on with things like =mff-mode=. When my display status changes, when I plug or unplug devices, EXWM will run =cce/refresh-display-scale= defined in [[id:cce/appearance][Themeing my Emacs]]. This is run in the =use-package= =config= stage, it is embedded in the =use-package= statement using [[id:cce/literate_programming][Literate Programming]] [[id:09779ac0-4d5f-40db-a340-49595c717e03][noweb syntax]].
#+name: exwm-xrandr
#+begin_src emacs-lisp :tangle no
(require 'exwm-randr)
(defun cce/exwm-zip-connected-displays ()
(setq exwm-randr-workspace-monitor-plist
(pcase (system-name)
("kusanagi" '(0 "DP-4" 1 "DP-2"))
(_ (cce/exwm-connected-displays)))))
(add-hook 'exwm-randr-screen-change-hook #'cce/exwm-zip-connected-displays)
(add-hook 'exwm-randr-screen-change-hook #'cce/refresh-display-scale)
(exwm-randr-enable)
#+end_src
#+ARROYO_MODULE_WANTS: cce/external_display_test_functions.org
- [[id:cce/super_p_runs_commands][Super-P runs commands]]
- [[id:cce/exwm_input_simulation][EXWM Input Simulation]]
- [[id:cce/exwm_should_ignore_plasma_notifications][EXWM should ignore Plasma notifications]]
- [[id:cce/picom][compton on EXWM startup]]
- [[id:20230221T102917.352842][Consult Buffer Source for EXWM Windows]]
* Rules for new Windows
#+begin_src emacs-lisp :noweb-ref exwm-manage-configurations
(unless (boundp 'exwm-manage-configurations) (setq exwm-manage-configurations nil))
(mapcar (lambda (it)
(add-to-list 'exwm-manage-configurations it))
(list
'((equal exwm-class-name "Cantata")
workspace 3)
'((equal exwm-class-name "Signal")
workspace 2)
'((equal exwm-class-name "Element")
workspace 2)
'((equal exwm-class-name "virt-manager")
workspace 5)
'((s-contains? "Computers :(" exwm-title)
workspace 2)
'((s-contains? "Picture-in-Picture" exwm-title)
floating nil)))
#+end_src
* Deeper Web Integration with Emacs
eventually move this in to the firefox integration page
#+begin_src emacs-lisp :tangle ~/org/cce/exwm.el
(use-package exwm-edit)
#+end_src