From 99fad403f03c7d59518c0f08c2c80b4ab57df50e Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Sun, 28 Nov 2021 02:43:24 +0100 Subject: [PATCH] [Emacs] Add some EXWM documentation Add also some literate config --- org/config/emacs.org | 364 +++++++++++++++++++++++++++++++++---------- 1 file changed, 279 insertions(+), 85 deletions(-) diff --git a/org/config/emacs.org b/org/config/emacs.org index b72c75d..be55638 100644 --- a/org/config/emacs.org +++ b/org/config/emacs.org @@ -3081,21 +3081,227 @@ I’ll also create a fuction for connecting to this new Tramp protocol: :PROPERTIES: :CUSTOM_ID: Packages-Configuration-EXWM-pr14yxs09aj0 :END: -#+begin_src emacs-lisp -(defvar phundrak/with-exwm (and (eq window-system 'x) - (seq-contains command-line-args "--use-exwm"))) +So, I’m finally slowly getting back to EXWM. I tried it a couple of +years ago, but that was with the SpacemacsOS layer on Spacemacs, on a +laptop which got accidentaly formatted before I could save my config +and all… So it got me some time to come back. I’m still a bit worried +about Emacs being single threaded, so if I get one blocking function +blocking Emacs, my whole desktop will hang, but for now I haven’t had +this issue. +First, I need to install the /X protocol Emacs Lisp Bindings/. It +doesn’t seem to be available in any repo, so I’ll install it directly +from Git. +#+begin_src emacs-lisp (use-package xelb :straight (xelb :build t :type git :host github :repo "ch11ng/xelb")) +#+end_src -;; config.daviwil.com/desktop +Next is a function I’ve +stolen+ copied from Daviwil’s [[https://config.daviwil.com/desktop][desktop +configuration]]. This allows to launch software in the background +easily. +#+begin_src emacs-lisp (defun exwm/run-in-background (command &optional once) (let ((command-parts (split-string command " +"))) (apply #'call-process `(,(car command-parts) nil 0 nil ,@(cdr command-parts))))) +#+end_src +*** EXWM itself +:PROPERTIES: +:CUSTOM_ID: Packages-Configuration-EXWM-EXWM-itself-hhgexz61aaj0 +:END: +Now we come to the plat de résistance. Like with ~xelb~, I’m using its +Git source to install it to make sure I get the right version --- the +version available on the GNU ELPA is from the same source, true, but I +don’t know at which rate it is updated. And more packages down the +line will depend on this Git repository, so I might as well just clone +it right now. + +As you can see, in the ~:config~ secion I added to two hooks functions +so buffers are accurately renamed. While the average X window will +simply get the name of the current X window, I want Firefox and +Qutebrowser to be prefixed with the name of the browser. Actually, all +these will be renamed this way: +#+name: exwm-renamed-buffers-list +- Discord +- Firefox +- Qutebrowser + +#+name: exwm-gen-buffers-rename +#+headers: :exports none :tangle no +#+begin_src emacs-lisp :var buffers=exwm-renamed-buffers-list :cache yes +(mapconcat (lambda (buffer) + (let ((buffer-name (car buffer))) + (format "(\"%s\" %S)" + (downcase buffer-name) + `(exwm-workspace-rename-buffer (concat ,(concat buffer-name " - %s") + exwm-title))))) + buffers + "\n") +#+end_src + +#+RESULTS[53d4805340ff070b477ea5d3725105c488ffb1fd]: exwm-gen-buffers-rename +: ("discord" (exwm-workspace-rename-buffer (concat "Discord - %s" exwm-title))) +: ("firefox" (exwm-workspace-rename-buffer (concat "Firefox - %s" exwm-title))) +: ("qutebrowser" (exwm-workspace-rename-buffer (concat "Qutebrowser - %s" exwm-title))) + +#+name: exwm-buffers-name +#+begin_src emacs-lisp :tangle no +(add-hook 'exwm-update-class-hook + (lambda () (exwm-workspace-rename-buffer exwm-class-name))) + +(add-hook 'exwm-update-title-hook + (lambda () + (pcase exwm-class-name + <> + ))) +#+end_src + +As you can see below, in the ~:config~ section I added two advices and one +hook in order to correctly integrate evil with EXWM. When I’m in an X +window, I want to be in insert-mode so I can type however I want. +However, when I exit one, I want to default back to normal-mode. +#+name: exwm-advices-evil +#+begin_src emacs-lisp :tangle no +(add-hook 'exwm-manage-finish-hook (lambda () (call-interactively #'exwm-input-release-keyboard))) +(advice-add #'exwm-input-grab-keyboard :after (lambda (&optional id) (evil-normal-state))) +(advice-add #'exwm-input-release-keyboard :after (lambda (&optional id) (evil-insert-state))) +#+end_src + +Secondly, I add ~i~, ~C-SPC~, and ~M-m~ as exwm prefix keys so they aren’t +sent directly to the X windows but caught by Emacs (and EXWM). I’ll +use the ~i~ key in normal-mode to enter ~insert-mode~ and have Emacs +release the keyboard so the X window can grab it. ~s-I~ will also toggle +between releasing to and grabing the keyboard from X windows. +~s-~ will be only useful for grabing back the keyboard, a bit +like exiting the insert state from evil back to the normal state. +#+name: exwm-prefix-keys +#+begin_src emacs-lisp :tangle no +(general-define-key + :keymaps 'exwm-mode-map + :states 'normal + "i" #'exwm-input-release-keyboard) + +(exwm-input-set-key (kbd "s-I") #'exwm-input-toggle-keyboard) +(exwm-input-set-key (kbd "s-") #'exwm-input-grab-keyboard) + +(push ?\i exwm-input-prefix-keys) +(push (kbd "C-SPC") exwm-input-prefix-keys) +(push (kbd "M-m") exwm-input-prefix-keys) +#+end_src + +As stated a couple of times in my different configuration files, I’m +using the bépo layout, which means the default keys in the number row +are laid as follow: +#+name: exwm-bepo-number-row +#+begin_src emacs-lisp :tangle no +(defconst exwm-workspace-keys '("\"" "«" "»" "(" ")" "@" "+" "-" "/" "*")) +#+end_src + +With this, we can create keybinds for going or sending X windows to +workspaces 0 to 9. +#+name: exwm-workspace-keybinds +#+begin_src emacs-lisp :tangle no +(setq exwm-input-global-keys + `(,@exwm-input-global-keys + ,@(mapcar (lambda (i) + `(,(kbd (format "s-%s" (nth i exwm-workspace-keys))) . + (lambda () + (interactive) + (exwm-workspace-switch-create ,i)))) + (number-sequence 0 9)) + ,@(mapcar (lambda (i) + `(,(kbd (format "s-%d" i)) . + (lambda () + (interactive) + (exwm-workspace-move-window ,i)))) + (number-sequence 0 9)))) +#+end_src + +You can then see the list of the keybinds I have set for EXWM, which +are all prefixed with ~SPC x~ in normal mode (and ~C-SPC x~ in insert +mode), with the exception of ~s-RET~ which opens an eshell terminal. +#+name: exwm-keybinds +#+begin_src emacs-lisp :tangle no +(exwm-input-set-key (kbd "s-") (lambda () + (interactive) + (eshell))) + +(phundrak/leader-key + :infix "x" + "" '(:ignore t :which-key "EXWM") + "k" #'exwm-input-send-next-key + "l" '((lambda () + (interactive) + (start-process "" nil "plock")) + :which-key "lock") + "r" '(:ignore t :wk "rofi") + "rr" '((lambda () (interactive) + (with-temp-buffer + (shell-command "rofi -show drun" (current-buffer) (current-buffer)))) + :wk "drun") + "rw" '((lambda () (interactive) + (with-temp-buffer "rofi -show window" (current-buffer) (current-buffer))) + :wk "windows") + "R" '(:ignore t :wk "restart") + "Rr" #'exwm-reset + "RR" #'exwm-restart + "t" '(:ignore t :which-key "toggle") + "tf" #'exwm-layout-toggle-fullscreen + "tF" #'exwm-floating-toggle-floating + "tm" #'exwm-layout-toggle-mode-line + "w" '(:ignore t :which-key "workspaces") + "wa" #'exwm-workspace-add + "wd" #'exwm-workspace-delete + "ws" #'exwm-workspace-switch + "x" '((lambda () + (interactive) + (let ((command (string-trim (read-shell-command "RUN: ")))) + (start-process command nil command))) + :which-key "run") + "RET" #'eshell-new) +#+end_src + +A couple of commands are also automatically executed: +#+name: exwm-autostart-list +- ~mpc stop~ :: stops any music played by MPD (if any) when Emacs is + launched. +- ~pumopm~ :: my power manager, you can see its code source [[https://labs.phundrak.com/phundrak/pumopm][here]]. +- ~xss-lock plock~ :: automatically lock the desktop with my script [[file:bin.org::#Lock-635fcb38][plock]]. +- ~xrdb -merge $HOME/.Xresources~ :: use the settings from my xresources + file + +#+name: exwm-generate-autostarts +#+headers: :exports results :tangle no +#+begin_src emacs-lisp :var autostarts=exwm-autostart-list :cache yes +(mapconcat (lambda (command) + (let* ((whitespace (rx (+ space))) + (raw-command (car (split-string (car command) "::" t whitespace))) + (command (replace-regexp-in-string (regexp-quote "~") "" raw-command))) + (format "%S" `(exwm/run-in-background ,command)))) + autostarts + "\n") +#+end_src + +#+RESULTS[c480df0f806b0007d125e570ab95c7e17dc2199d]: exwm-generate-autostarts +: (exwm/run-in-background "mpc stop") +: (exwm/run-in-background "pumopm") +: (exwm/run-in-background "xss-lock plock") +: (exwm/run-in-background "xrdb -merge $HOME/.Xresources") + +Finally, let’s only initialize and start EXWM once functions from +exwm-randr ran, because otherwise having multiple monitors don’t work. +#+name: exwm-init +#+begin_src emacs-lisp :tangle no +(with-eval-after-load 'exwm-randr + (exwm-init)) +#+end_src + +The complete configuration for the ~exwm~ package can be found below. +#+begin_src emacs-lisp :noweb yes (use-package exwm :straight (exwm :build t :type git @@ -3108,81 +3314,27 @@ I’ll also create a fuction for connecting to this new Tramp protocol: (require 'exwm-config) (setq exwm-workspace-number 3) :config - (add-hook 'exwm-manage-finish-hook (lambda () (call-interactively #'exwm-input-release-keyboard))) - (advice-add #'exwm-input-grab-keyboard :after (lambda (&optional id) (evil-normal-state))) - (advice-add #'exwm-input-release-keyboard :after (lambda (&optional id) (evil-insert-state))) + <> - (general-define-key - :keymaps 'exwm-mode-map - :states 'normal - "i" #'exwm-input-release-keyboard) - (push ?\i exwm-input-prefix-keys) + <> + <> - (add-hook 'exwm-update-class-hook - (lambda () (exwm-workspace-rename-buffer exwm-class-name))) + <> + <> - (defconst exwm-workspace-keys '("*" "\"" "«" "»" "(" ")" "@" "+" "-" "/")) - (setq exwm-input-global-keys - `(,@exwm-input-global-keys - ,@(mapcar (lambda (i) - `(,(kbd (format "s-%s" (nth i exwm-workspace-keys))) . - (lambda () - (interactive) - (exwm-workspace-switch-create ,i)))) - (number-sequence 0 9)) - ,@(mapcar (lambda (i) - `(,(kbd (format "s-%d" i)) . - (lambda () - (interactive) - (exwm-workspace-move-window ,i)))) - (number-sequence 0 9)))) + <> - (exwm-input-set-key (kbd "C-q") #'exwm-input-send-next-key) - (exwm-input-set-key (kbd "s-I") #'exwm-input-toggle-keyboard) - (exwm-input-set-key (kbd "s-l") (lambda () (start-process "" nil "plock"))) - (exwm-input-set-key (kbd "s-r") #'exwm-reset) - (exwm-input-set-key (kbd "s-R") #'exwm-restart) - (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch) - (exwm-input-set-key (kbd "s-") (lambda () - (interactive) - (start-process-shell-command "kitty" nil "kitty"))) - (exwm-input-set-key (kbd "s-") #'exwm-input-grab-keyboard) - (exwm-input-set-key (kbd "s-d") (lambda () - (interactive) - (start-process "RUN" nil (string-trim (read-shell-command "RUN: "))))) + <> - (phundrak/leader-key - :infix "x" - "" '(:ignore t :which-key "EXWM") - "k" #'exwm-input-send-next-key - "l" '((lambda () - (interactive) - (start-process "" nil "plock")) - :which-key "lock") - "r" #'exwm-reset - "R" #'exwm-restart - "t" '(:ignore t :which-key "toggle") - "tf" #'exwm-layout-toggle-fullscreen - "tF" #'exwm-floating-toggle-floating - "tm" #'exwm-layout-toggle-mode-line - "w" '(:ignore t :which-key "workspaces") - "wa" #'exwm-workspace-add - "wd" #'exwm-workspace-delete - "x" '((lambda () - (interactive) - (let ((command (string-trim (read-shell-command "RUN: ")))) - (start-process command nil command))) - :which-key "run")) - - (exwm/run-in-background "mpc stop") - (exwm/run-in-background "pumopm") - ;; (exwm/run-in-background "nm-applet") - (exwm/run-in-background "xss-lock") - (exwm/run-in-background "xrdb -merge $HOME/.Xresources") - - (after! exwm-randr - (exwm-init))) + <> + ) +#+end_src +*** EXWM-Evil integration +:PROPERTIES: +:CUSTOM_ID: Packages-Configuration-EXWM-EXWM-Evil-integration-kwlexz61aaj0 +:END: +#+begin_src emacs-lisp (use-package evil-exwm-state :defer t :after exwm @@ -3190,19 +3342,13 @@ I’ll also create a fuction for connecting to this new Tramp protocol: :type git :host github :repo "domenzain/evil-exwm-state")) +#+end_src -(use-package desktop-environment - :defer t - :straight (desktop-environment :build t - :type git - :host github - :repo "DamienCassou/desktop-environment") - :diminish t - :config - (setq desktop-environment-update-exwm-global-keys :prefix) - (setq exwm-layout-show-al-buffers t)) - - +*** Multimonitor support +:PROPERTIES: +:CUSTOM_ID: Packages-Configuration-EXWM-Multimonitor-support-l5pexz61aaj0 +:END: +#+begin_src emacs-lisp (use-package exwm-randr :after exwm :straight (exwm-randr :build t @@ -3220,6 +3366,54 @@ I’ll also create a fuction for connecting to this new Tramp protocol: (exwm-randr-enable)) #+end_src +*** Keybinds for a desktop environment +:PROPERTIES: +:CUSTOM_ID: Packages-Configuration-EXWM-Keybinds-for-a-desktop-environment-q2sexz61aaj0 +:END: +#+begin_src emacs-lisp +(use-package desktop-environment + :defer t + :straight (desktop-environment :build t + :type git + :host github + :repo "DamienCassou/desktop-environment") + :diminish t + :config + (setq desktop-environment-update-exwm-global-keys :prefix + exwm-layout-show-al-buffers t) + + (setq desktop-environment-bluetooth-command "bluetoothctl" + + desktop-environment-brightness-get-command "xbacklight -get" + desktop-environment-brightness-get-regexp (rx line-start (group (+ digit))) + desktop-environment-brightness-set-command "xbacklight %s" + desktop-environment-brightness-normal-increment "-inc 5" + desktop-environment-brightness-normal-decrement "-dec 5" + desktop-environment-brightness-small-increment "-inc 2" + desktop-environment-brightness-small-decrement "-dec 2" + + desktop-environment-volume-normal-decrement "5%-" + desktop-environment-volume-normal-increment "5%+" + desktop-environment-volume-small-decrement "2%-" + desktop-environment-volume-small-increment "2%+" + desktop-environment-volume-set-command "amixer -q Master %s unmute" + + desktop-environment-screenshot-directory "~/Pictures/Screenshots" + desktop-environment-screenlock-command "plock" + + desktop-environment-music-toggle-command "mpc toggle" + desktop-environment-music-previous-command "mpc prev" + desktop-environment-music-next-command "mpc next" + desktop-environment-music-stop-command "mpc stop") + + (general-define-key + "" (lambda () (interactive) + (with-temp-buffer + (shell-command "mpc pause" (current-buffer) (current-buffer))))) + + (desktop-environment-mode)) +#+end_src + *** Bluetooth :PROPERTIES: :CUSTOM_ID: Packages-Configuration-EXWM-Bluetooth-k0zhpda0aaj0