[Emacs] Add some EXWM documentation
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			Add also some literate config
This commit is contained in:
		
							parent
							
								
									bd12a71a92
								
							
						
					
					
						commit
						99fad403f0
					
				| @ -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 | ||||
|               <<exwm-gen-buffers-rename()>> | ||||
|               ))) | ||||
| #+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-<escape>~ 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-<escape>") #'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-<return>") (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))) | ||||
|   <<exwm-buffers-name>> | ||||
| 
 | ||||
|   (general-define-key | ||||
|    :keymaps 'exwm-mode-map | ||||
|    :states 'normal | ||||
|    "i" #'exwm-input-release-keyboard) | ||||
|   (push ?\i exwm-input-prefix-keys) | ||||
|   <<exwm-advices-evil>> | ||||
|   <<exwm-prefix-keys>> | ||||
| 
 | ||||
|   (add-hook 'exwm-update-class-hook | ||||
|             (lambda () (exwm-workspace-rename-buffer exwm-class-name))) | ||||
|   <<exwm-bepo-number-row>> | ||||
|   <<exwm-workspace-keybinds>> | ||||
| 
 | ||||
|   (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-keybinds>> | ||||
| 
 | ||||
|   (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-<return>") (lambda () | ||||
|                                            (interactive) | ||||
|                                            (start-process-shell-command "kitty" nil "kitty"))) | ||||
|   (exwm-input-set-key (kbd "s-<escape>") #'exwm-input-grab-keyboard) | ||||
|   (exwm-input-set-key (kbd "s-d")        (lambda () | ||||
|                                            (interactive) | ||||
|                                            (start-process "RUN" nil (string-trim (read-shell-command "RUN: "))))) | ||||
|   <<exwm-generate-autostarts()>> | ||||
| 
 | ||||
|   (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))) | ||||
|   <<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 | ||||
|    "<XF86AudioPause>" (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 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user