Files
azos/pkgs/elisp/azos-emacs-exwm.org
T
2024-11-18 09:11:55 +02:00

377 lines
11 KiB
Org Mode

#+title: Aner's Emacs EXWM Configuration
#+property: header-args :results silent
* Base setup
** Require
#+begin_src emacs-lisp
(require 'azos-emacs-station)
(require 'azos-emacs-base)
#+end_src
* EXWM
We execute the following code only if started with EXWM argument
** EXWM Setup
#+begin_src emacs-lisp
(defvar azos/exwm/load-hook nil "Load EXWM hook")
(defun azos/exwm/load-exwm (switch) "Loads EXWM"
(use-package exwm
:ensure t
:config
(require 'exwm)
(require 'exwm-randr)
;Workspaces
(setq exwm-workspace-number 4
exwm-layout-show-all-buffers t
exwm-workspace-show-all-buffers t)
;System tray
(require 'exwm-systemtray)
(setq exwm-systemtray-background-color "LightYellow3")
(exwm-systemtray-enable)
;In EXWM mode, no evil
(add-to-list 'evil-emacs-state-modes 'exwm-mode)
(run-hooks 'azos/exwm/load-hook)
;Enable
(exwm-randr-enable)
(exwm-enable)
))
#+end_src
** Smart buffer naming
#+begin_src emacs-lisp
(add-hook 'azos/exwm/load-hook (lambda () (progn
(add-hook 'exwm-update-class-hook
(lambda ()
(unless (or (string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(string-prefix-p "qute" exwm-instance-name)
(exwm-workspace-rename-buffer exwm-class-name))))
(add-hook 'exwm-update-title-hook
(lambda ()
(when (or (not exwm-instance-name)
(string-prefix-p "sun-awt-X11-" exwm-instance-name)
(string-prefix-p "qute" exwm-instance-name)
(string= "gimp" exwm-instance-name))
(exwm-workspace-rename-buffer exwm-title))))
(add-hook 'exwm-update-class-hook
(lambda ()
(when (or (not exwm-instance-name)
(string-prefix-p "mpv" exwm-class-name))
(exwm-workspace-rename-buffer (concat "mpv | " exwm-title)))))
)))
#+end_src
** Basic keybindings
Global keybindings can be defined with `exwm-input-global-keys'.
Here are a few examples:
#+begin_src emacs-lisp
(add-hook 'azos/exwm/load-hook (lambda ()
(setq exwm-input-global-keys
`(
;; Bind "s-<f2>" to "slock", a simple X display locker.
([s-f2] . (lambda ()
(interactive)
(start-process "" nil "/usr/bin/slock")))
([s-<tab>] . persp-switch)
;; Bind "s-r" to exit char-mode and fullscreen mode.
([?\s-r] . exwm-reset)
;; Bind "s-w" to switch workspace interactively.
([?\s-w] . exwm-workspace-switch)
;; Bind "s-0" to "s-9" to switch to a workspace by its index.
,@(mapcar (lambda (i)
`(,(kbd (format "s-%d" i)) .
(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
(number-sequence 0 9))
;; Bind "s-&" to launch applications ('M-&' also works if the output
;; buffer does not bother you).
([?\s-&] . (lambda (command)
(interactive (list (read-shell-command "$ ")))
(start-process-shell-command command nil command)))
))))
(defun azos/exwm/take-screenshot ()
(interactive)
(shell-command "flameshot gui")
)
(defun azos/exwm/start-qutebrowser ()
(interactive)
(start-process "qutebrowser" nil "qutebrowser"))
(add-hook 'azos/exwm/load-hook (lambda () (progn
(define-key azos/global-minor-mode/open-keymap
(kbd "q") 'azos/exwm/start-qutebrowser)
(define-key azos/global-minor-mode/keymap
(kbd "<print>") 'azos/exwm/take-screenshot))))
#+end_src
** Better modeline
This currently does nothing and I am not sure why.
#+begin_src emacs-lisp
(defvar azos/exwm/modeline-hash-table (make-hash-table)
"Table to store relative face change cookies in")
(defface azos/exwm/modeline-remap-style
(list (list t (list :background azos/evil-color-insert)))
"Make the backgrounds pop to green")
(defun azos/exwm/input-mode-modeline () "Changes modeline based on input mode"
(let ((currbuff (current-buffer)))
(if (eq exwm--input-mode 'char-mode)
;;line
(let ((remap-cookie (gethash currbuff
azos/exwm/modeline-hash-table)))
(if remap-cookie
(progn
(face-remap-remove-relative remap-cookie)
(remhash currbuff
azos/exwm/modeline-hash-table))))
;;char
(puthash
currbuff
(face-remap-add-relative 'mode-line
'azos/exwm/modeline-remap-style)
azos/exwm/modeline-hash-table)
)))
(add-hook 'exwm-input-input-mode-change-hook 'azos/exwm/input-mode-modeline)
;; (set-face-attribute 'mode-line nil :box nil :background "AliceBlue")
;; (set-face-attribute 'mode-line-inactive nil :box nil :background "LightYellow3")
#+end_src
** RANDR screen settings
Enabling randr. Automatic mapping of randr screens to workspaces.
#+begin_src emacs-lisp
(defun azos/exwm/get-monitor-list ()
(mapcar (lambda (x) (match-string (string-match "^[A-Za-z]+-*[0-9]+" x) x))
(azos/re-seq "^[A-Za-z]+-*[0-9]+ connected"
(shell-command-to-string "xrandr"))))
(defun azos/exwm/add-indexes (list)
(azos/exwm/add-indexes-i list 1)
)
(defun azos/exwm/add-indexes-i (list i)
(if list
(cons i (cons (car list) (azos/exwm/add-indexes-i (cdr list) (+ i 1))))
nil))
(defun azos/exwm/update-randr-monitor-plist ()
(interactive)
(progn
(start-process
"xlayoutdisplay" nil "xlayoutdisplay")
(setq exwm-randr-workspace-monitor-plist
(azos/exwm/add-indexes (azos/exwm/get-monitor-list)))
(exwm-randr-refresh))
)
(add-hook 'azos/exwm/load-hook (lambda () (progn
(add-hook 'exwm-randr-screen-change-hook
'azos/exwm/update-randr-monitor-plist)
(define-key azos/global-minor-mode/keymap
(kbd "s-x") 'azos/exwm/update-randr-monitor-plist))))
#+end_src
** Prefix keys
Sending simulated keys to X windows
#+begin_src emacs-lisp
(add-hook 'azos/exwm/load-hook (lambda () (progn
(setq exwm-input-prefix-keys
'(?\C-x ?\C-u ?\C-h ?\M-x ?\M-& ?\M-: ?\s-d
?\s-m ?\s-r ?\s-s ?\s-q ?\H-l ?\C-w)))))
#+end_src
** Desktop environment
#+begin_src emacs-lisp
(use-package desktop-environment :after exwm)
#+end_src
** Bluetooth
#+begin_src emacs-lisp
(use-package bluetooth :after exwm)
#+end_src
** Pulse
#+begin_src emacs-lisp
(use-package pulseaudio-control :after exwm)
#+end_src
** TODO Dunst
Used to have to run a service 'dunst'. Don't do that now, handled by OS.
** TODO Flameshot
Also Flameshot
** Media keys
https://gist.github.com/ajyoon/5323b999a01dce8db2d4456da1740fe3
#+begin_src emacs-lisp
(add-hook 'azos/exwm/load-hook (lambda () (progn
(dolist (k '(XF86AudioLowerVolume
XF86AudioRaiseVolume
XF86AudioPlay
XF86AudioStop
XF86AudioPrev
XF86AudioNext))
(push k exwm-input-prefix-keys))
(exwm-input-set-key
(kbd "<XF86AudioRaiseVolume>")
(lambda ()
(interactive) (start-process
"pactl" nil "pactl" "set-sink-volume" "0" "+5%")))
(exwm-input-set-key
(kbd "<XF86AudioLowerVolume>")
(lambda ()
(interactive) (start-process
"pactl" nil "pactl" "set-sink-volume" "0" "-5%")))
(exwm-input-set-key
(kbd "<XF86AudioMute>")
(lambda ()
(interactive) (start-process
"pactl" nil "pactl" "set-sink-mute" "0" "toggle")))
(exwm-input-set-key
(kbd "<XF86AudioPlay>")
'desktop-environment-toggle-music)
(exwm-input-set-key
(kbd "<XF86AudioNext>")
'desktop-environment-music-next)
(exwm-input-set-key
(kbd "<XF86AudioPrev>")
'desktop-environment-music-previous)
(exwm-input-set-key
(kbd "<XF86AudioStop>")
'desktop-environment-music-stop)
(exwm-input-set-key
(kbd "<XF86AudioPause>")
'desktop-environment-toggle-music)
(exwm-input-set-key
(kbd "<XF86MonBrightnessUp>")
(lambda ()
(interactive) (start-process
"light" nil "light" "-A" "5")))
(exwm-input-set-key
(kbd "<XF86MonBrightnessDown>")
(lambda ()
(interactive) (start-process
"light" nil "light" "-U" "5")))))
#+end_src
In the event =xbacklight= doesn't work, the following command can be run:
#+begin_example
xrandr --output eDP1 --brightness 0.5
#+end_example
** TODO Input languages
#+begin_src emacs-lisp
(defun azos/exwm/enable-heb ()
(interactive)
(start-process-shell-command "heb" nil
"setxkbmap -layout us,il && setxkbmap -option 'grp:alt_shift_toggle'"))
())
(add-hook 'azos/exwm/load-hook 'azos/exwm/enable-heb)
#+end_src
I think this can be handled by OS
** Dedicated processes
We'd want to be able to quickly map processes (Spotify, etc.) to
keybindings, and have dedicated buffers for them (so they don't reopen).
We first define variables to be used later, and a function that checks, for each
new process, should it be displayed in a new buffer/tab.
#+begin_src emacs-lisp
(defvar azos/exwm/startproc-regex-buffname-list nil
"Match between buffer and tab.")
(defun azos/exwm/startproc-check-for-buff-entry ()
"On new buffer, check if requested to display differently"
(let ((entry (cdr (car (seq-filter
(lambda (e) (string-match (car e) (buffer-name)))
azos/exwm/startproc-regex-buffname-list
)))))
(if entry
(let ((rn (nth 0 entry)) (tn (nth 1 entry)) (buff (current-buffer)))
;; Too much complication for renaming
;; (if requested-name (rename-buffer requested-name))
(progn
(if tn (tab-bar-switch-to-tab tn))
(switch-to-buffer buff)
)))))
(add-hook 'exwm-manage-finish-hook 'azos/exwm/startproc-check-for-buff-entry)
#+end_src
#+begin_src emacs-lisp
(defun azos/exwm/start-proc-dedicated
(name tab-name procregex proc &rest args)
(let ((buff (car (seq-filter (lambda (b
)
(string-match procregex (buffer-name b)))
(buffer-list)))))
(if buff
;; Found buffer, display
(progn
(if tab-name (tab-bar-switch-to-tab tab-name))
(switch-to-buffer buff)
)
;;No known buffer, add entry to alist
(let ((new-entry `(,procregex ,tab-name ,name))) (progn
(add-to-list 'azos/exwm/startproc-regex-buffname-list
new-entry)
(apply 'start-process name nil proc
args))))))
#+end_src
** EXWM Ending
End the execute only if EXWM block.
#+begin_src emacs-lisp
(add-to-list 'command-switch-alist '("--start-exwm" . azos/exwm/load-exwm))
#+end_src
* Provide
#+begin_src emacs-lisp
(provide 'azos-emacs-exwm)
(add-hook 'after-init-hook (lambda () (require 'azos-emacs-exwm)))
#+end_src