EWM - Emacs Wayland Manager setup, dynamic tabs setup v1

This commit is contained in:
Curt Spark 2026-03-25 10:56:20 +00:00
parent dc38d0dec3
commit c52e577181
1 changed files with 196 additions and 0 deletions

196
init.el
View File

@ -865,6 +865,202 @@
:config
(setq prettier-js-use-modules-bin t))
;; Emacs desktop environment
; Emacs Wayland Manager (EWM)
(when (and (executable-find "ewm-launch") (string= (getenv "XDG_SESSION_DESKTOP") "ewm"))
; Dynamic Tab (Workspace) Setup
(use-package tab-line
:ensure nil)
(setq tab-bar-show 1) ; Hide the bar if only 1 tab exists
(setq tab-bar-new-tab-choice "*scratch*")
(defun dynamic-tab-ensure-scratch-tab ()
"Ensures there is always a tab dedicated to *scratch*."
(let* ((tabs (funcall tab-bar-tabs-function))
(tab-names (mapcar (lambda (tab) (alist-get 'name tab)) tabs))
(spare-exists (member "dynamic *scratch*" tab-names))
(is-child-frame (frame-parent)))
;; If we are currently IN the spare tab and it's not *scratch* anymore,
;; or if we just want to ensure it exists at the end:
(unless (or spare-exists is-child-frame)
(let ((current-tab (tab-bar--current-tab-index)))
(tab-bar-new-tab 999)
(tab-bar-rename-tab "dynamic *scratch*")
;; Return to original tab if we were just initializing
(tab-bar-select-tab (1+ current-tab))))))
(defun dynamic-tab-handle-tab-persistence ()
"Create new tab if current scratch tab is being used."
(when (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*")
(unless (or (string= (buffer-name) "*scratch*") (minibufferp))
;; User switched away from scratch in the spare tab, rename it
(tab-bar-rename-tab "")))
;; and create a new spare.
(dynamic-tab-ensure-scratch-tab))
(defvar already-redirecting-p nil)
(defun dynamic-tab-redirect-to-scratch-tab (&rest _)
"Redirect to dedicated scratch tab if scratch buffer accessed on other tab."
(when (and (not already-redirecting-p)
(string= (buffer-name) "*scratch*")
(not (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*")))
(setq already-redirecting-p t)
(unwind-protect
(switch-to-buffer nil)
(tab-bar-select-tab-by-name "dynamic *scratch*")
(setq already-redirecting-p nil))))
(defun dynamic-tab-handle-buffer-check-and-close-tab ()
"Check if we need to close the tab and action accordingly."
(let* ((is-last-tab (<= (length (tab-bar-tabs)) 2))
(is-last-buffer (<= (length (tab-line-tabs-window-buffers)) 2)))
(when (and (not is-last-tab) is-last-buffer)
(tab-close))))
(defun dynamic-tab-handle-tab-cleanup-kill (orig-fun &rest args)
"Free tab if all reserved in use buffers for that tab killed."
(let* ((is-last-tab (<= (length (tab-bar-tabs)) 2))
(is-child-buffer (frame-parent))
(is-mini-buffer (minibufferp))
(is-dynamic-buffer (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*"))
(is-scratch-buffer (string= (alist-get 'name (tab-bar--current-tab)) "*scratch*"))
(is-last-buffer (<= (length (tab-line-tabs-window-buffers)) 2))
(is-overriden (nth 1 args))
(is-target-focused (and (eq (current-buffer) (car args))))
(is-called-interactively (called-interactively-p 'any)))
(cond
((and (not is-overriden) (not is-called-interactively)) (apply orig-fun args))
(is-child-buffer nil)
(is-mini-buffer nil)
(is-dynamic-buffer nil)
(is-scratch-buffer nil)
((and (not is-overriden) is-called-interactively) (apply orig-fun (nbutlast args)))
((and is-last-tab is-last-buffer) (apply orig-fun (nbutlast args)))
((and is-last-tab (not is-last-buffer)) (apply orig-fun (nbutlast args)))
;((and is-last-tab (not is-last-buffer) (bury-buffer)))
((and (not is-last-tab) (not is-last-buffer) (bury-buffer)))
(t nil))
(when (and (not is-overriden) is-target-focused)
(dynamic-tab-handle-buffer-check-and-close-tab))))
(defun dynamic-tab-handle-tab-cleanup-close (&rest _)
"Free tab if last window open is attempted to be closed."
(if (and (> (length (tab-bar-tabs)) 2)
(= (length (window-list)) 1)
(not (string= (alist-get 'name (tab-bar--current-tab)) "dynamic *scratch*")))
(tab-close)
t))
;; Helper functions for EWM
; Switch monitor
(defun ewm-switch-to-monitor (target)
(interactive)
(select-frame-set-input-focus (car (last (seq-filter (lambda (f)
(string= target
(cdr (assq 'name (frame-monitor-attributes f)))))
(frame-list))))))
; EWM - Emacs Wayland Manager
(use-package ewm
:ensure nil
:custom
(ewm-output-config
'(("DP-1"
:width 1920
:height 1080
:scale 1.0
:x 0
:y 0
:refresh 240)
("HDMI-A-1"
:width 1920
:height 1080
:scale 1.0
:x 1920
:y 0
:transform 3
:refresh 75)))
(ewm-input-config
'((touchpad :natural-scroll t :tap t :dwt t)
(mouse :accel-profile "flat")
(keyboard :repeat-delay 150 :repeat-rate 35
:xkb-layouts "gb")))
;; Per-device override (exact name from libinput)
;("ELAN0676:00 04F3:3195 Touchpad" :tap nil :accel-speed -0.2)))
:bind (:map ewm-mode-map
("M-d" . ewm-launch-app)
("s-l" . ewm-lock-session)
("M-<return>" . eshell)
("<print>" . (lambda ()
(interactive)
(start-process-shell-command
"screenshot-process"
nil
"grim -g \"$(slurp)\" - | wl-copy")))
("M-w" . (lambda ()
(interactive)
(kill-buffer)))
; Monitor switching, find current focused monitor name via
;(cdr (assq 'name (frame-monitor-attributes))
("M-o" . (lambda ()
(interactive)
(ewm-switch-to-monitor "XG2431")))
("M-p" . (lambda ()
(interactive)
(ewm-switch-to-monitor "24B2W1G5")))
;;("M-f" . (lambda ()
;; (interactive)
;; (start-process-shell-command
;; "wlkbptr-process"
;; nil
;; "wl-kbptr -o modes=floating,click -o mode_floating.source=detect"))
("M-h" . tab-bar-switch-to-prev-tab)
("M-l" . tab-bar-switch-to-next-tab)
("M-k" . windmove-up)
("M-j" . windmove-down))
:config
(add-to-list 'ewm-intercept-prefixes ?\M-d)
(add-to-list 'ewm-intercept-prefixes ?\M-w)
(add-to-list 'ewm-intercept-prefixes ?\M-o)
(add-to-list 'ewm-intercept-prefixes ?\M-p)
(add-to-list 'ewm-intercept-prefixes ?\M-h)
(add-to-list 'ewm-intercept-prefixes ?\M-j)
(add-to-list 'ewm-intercept-prefixes ?\M-k)
(add-to-list 'ewm-intercept-prefixes ?\M-l)
:init
;; Dynamic Tabs/Workspaces setup
; Hooks
(advice-add 'consult-buffer
:after (lambda (&rest _)
(dynamic-tab-handle-tab-persistence)
(dynamic-tab-redirect-to-scratch-tab)))
(advice-add 'delete-window :before-while #'dynamic-tab-handle-tab-cleanup-close)
(advice-add 'kill-buffer :around #'dynamic-tab-handle-tab-cleanup-kill)
;(add-hook 'buffer-list-update-hook #'dynamic-tab-redirect-to-scratch-tab)
(add-hook 'window-configuration-change-hook #'dynamic-tab-handle-tab-persistence)
; Initialize on startup
(dynamic-tab-ensure-scratch-tab))
; Desktop notifications in Emacs
(use-package ednc
:ensure t
:init
(ednc-mode)))
;(use-package ednc-popup
; :ensure (:host git :repo "https://codeberg.org/akib/emacs-ednc-popup.git")
; :after ednc
; :config
; (add-hook 'ednc-notification-presentation-functions
; #'ednc-popup-presentation-function))
(custom-set-variables
;; custom-set-variables was added by Custom.