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

11 KiB

Aner's Emacs EXWM Configuration

Base setup

Require

(require 'azos-emacs-station)
(require 'azos-emacs-base)

EXWM

We execute the following code only if started with EXWM argument

EXWM Setup

(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)
    ))

Smart buffer naming

(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)))))

)))

Basic keybindings

Global keybindings can be defined with `exwm-input-global-keys'. Here are a few examples:

(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))))

Better modeline

This currently does nothing and I am not sure why.

(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")

RANDR screen settings

Enabling randr. Automatic mapping of randr screens to workspaces.

(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))))

Prefix keys

Sending simulated keys to X windows

(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)))))

Desktop environment

(use-package desktop-environment :after exwm)

Bluetooth

(use-package bluetooth :after exwm)

Pulse

(use-package pulseaudio-control :after exwm)

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

(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")))))

In the event xbacklight doesn't work, the following command can be run:

xrandr --output eDP1 --brightness 0.5

TODO Input languages

(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)

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.

(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)
(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))))))

EXWM Ending

End the execute only if EXWM block.

(add-to-list 'command-switch-alist '("--start-exwm" . azos/exwm/load-exwm))

Provide

(provide 'azos-emacs-exwm)
(add-hook 'after-init-hook (lambda () (require 'azos-emacs-exwm)))