;;; jao-exwm.el --- exwm utilities -*- lexical-binding: t; -*- ;; Copyright (C) 2026 jao ;; Author: jao ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . (require 'jao-minibuffer) ;;; Helpers (defvar jao-exwm-use-afio t) (defun jao-exwm-send-str (str) (dolist (k (string-to-list str)) (exwm-input--fake-key k))) ;;; Minibuffer and system tray (setq jao-minibuffer-frame-width 271) (add-hook 'exwm-workspace-switch-hook #'jao-minibuffer-refresh) (require 'exwm-systemtray) (exwm-systemtray-mode 1) (defun jao-exwm--watch-tray (sym newval op where) (setq jao-minibuffer-right-margin (/ newval (string-pixel-width "a"))) (jao-minibuffer-refresh)) (add-variable-watcher 'exwm-systemtray-width #'jao-exwm--watch-tray) ;;; Buffers (defvar-local jao-exwm--name nil) (defvar jao-exwm--current-name nil) (defun jao-exwm--check-name (name) (or (string= jao-exwm--name name) (string= (buffer-name) name) (string= exwm-class-name name) (string= exwm-title name))) (defun jao-exwm-find-class-buffer (cln) (if (jao-exwm--check-name cln) (current-buffer) (let* ((cur-buff (current-buffer)) (bfs (seq-filter (lambda (b) (and (not (eq b cur-buff)) (with-current-buffer b (jao-exwm--check-name cln)))) (buffer-list)))) (when (car bfs) (car (reverse bfs)))))) (defun jao-exwm-switch-to-class/title (cln) (interactive) (when cln (if (jao-exwm--check-name cln) (current-buffer) (when-let* ((b (jao-exwm-find-class-buffer cln))) (pop-to-buffer b))))) (defun jao-exwm-switch-to-next-class () (interactive) (jao-exwm-switch-to-class/title exwm-class-name)) (defun jao-exwm-switch-to-next-x () (interactive) (let ((bfs (seq-filter (lambda (b) (with-current-buffer b (derived-mode-p 'exwm-mode))) (buffer-list (window-frame))))) (when (car bfs) (switch-to-buffer (car (reverse bfs)))))) ;;; Buffer names (defun jao-exwm--use-title-p () (and exwm-title (not (string-blank-p exwm-title)))) (defun jao-exwm-rename-buffer/class () (unless (jao-exwm--use-title-p) (exwm-workspace-rename-buffer exwm-class-name))) (defun jao-exwm-rename-buffer/title () (cond ((or (not exwm-instance-name) (jao-exwm--use-title-p)) (exwm-workspace-rename-buffer exwm-title)) ((string= "Zathura" exwm-class-name) (exwm-workspace-rename-buffer (format "zathura: %s" (file-name-nondirectory exwm-title)))))) (defun jao-exwm--set-exwm-name () (when (not jao-exwm--name) (setq jao-exwm--name jao-exwm--current-name jao-exwm--current-name nil))) (add-hook 'exwm-mode-hook 'jao-exwm--set-exwm-name) (add-hook 'exwm-update-class-hook 'jao-exwm-rename-buffer/class) (add-hook 'exwm-update-title-hook 'jao-minibuffer-refresh) (add-hook 'exwm-update-title-hook 'jao-exwm-rename-buffer/title) ;;; Float windows (defvar jao-exwm-max-x (x-display-pixel-width)) (defvar jao-exwm-max-y (x-display-pixel-height)) (defun jao-exwm--float-to (x y &optional w h) (let* ((w (or w (frame-pixel-width))) (h (or h (frame-pixel-height))) (x (if (< x 0) (- jao-exwm-max-x (- x) w) x)) (y (if (< y 0) (- jao-exwm-max-y (- y) h) y)) (p (or (frame-parameter nil 'jao-position) (frame-position)))) (exwm-floating-move (- x (car p)) (- y (cdr p))) (exwm-layout-enlarge-window-horizontally (- w (frame-pixel-width))) (exwm-layout-enlarge-window (- h (frame-pixel-height))) (set-frame-parameter nil 'jao-position (cons x y)))) (defun jao-exwm--center-float (&optional w h) (interactive) (let* ((mx jao-exwm-max-x) (my jao-exwm-max-y) (w (or w (frame-pixel-width))) (h (or h (/ (* w my) mx)))) (jao-exwm--float-to (/ (- mx w) 2) (/ (- my h) 2) w h))) (defun jao-exwm--setup-float () (set-frame-parameter nil 'jao-position nil) (cond ((member exwm-class-name '("firefox" "Firefox" "Sofice")) (jao-exwm--center-float 900 600)) ((member exwm-class-name '("mpv" "vlc")) (jao-exwm--center-float 1200)))) (defvar jao-exwm-floating-classes '("mpv" "vlc")) (setq jao-exwm-floating-classes nil) (defun jao-exwm--maybe-float () (when (member exwm-class-name jao-exwm-floating-classes) (when (not exwm--floating-frame) (exwm-floating-toggle-floating)))) (add-hook 'exwm-floating-setup-hook #'jao-exwm--setup-float) (add-hook 'exwm-manage-finish-hook #'jao-exwm--maybe-float) ;;; App runners ;;; App runner helpers (defun jao-exwm-run (command) (interactive (list (read-shell-command "$ " (if current-prefix-arg (cons (concat " " (buffer-file-name)) 0) "")))) (setq jao-exwm--current-name nil) (start-process-shell-command command nil command)) (defmacro jao-exwm-runner (&rest args) `(lambda () (interactive) (start-process "" nil ,@args))) (defun jao-exwm-workspace (n) (if jao-exwm-use-afio (jao-afio-goto-nth n) (exwm-workspace-switch-create n))) (defmacro jao-def-exwm-runner (name ws class &rest args) `(defun ,name (&rest other-args) (interactive) ,@(when ws `((jao-exwm-workspace ,ws))) (if (jao-exwm-switch-to-class/title ,class) ,(cond ((equal ws -1) '(delete-other-windows)) ((stringp (car args)) (cdr args)) (t args)) (setq jao-exwm--current-name ,class) ,(if (stringp (car args)) `(start-process-shell-command ,(car args) "* exwm - console *" (string-join (append (list ,@args) other-args) " ")) args)))) (defmacro jao-def-exwm-toggler (name ws class &rest args) (let ((toggler (intern (format "%s*" name))) (arg (gensym))) `(progn (jao-def-exwm-runner ,name ,ws ,class ,@args) (defun ,toggler (,arg) (interactive "P") (if (and (not ,arg) (equal exwm-class-name ,class)) (jao-afio-toggle) (,name)))))) (provide 'jao-exwm) ;;; jao-exwm.el ends here