summaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
authorjao <jao@gnu.org>2026-10-16 21:00:54 +0100
committerjao <jao@gnu.org>2026-10-16 21:04:03 +0100
commitf71a3084532ee7f959fc0b2cac384d0c2a14d8a1 (patch)
treeeee342032d4259f8c71afc3bf61bfb41d72cc405 /lib
parent2276be2575999fa174b910e371814bb712a11ea5 (diff)
downloadelibs-f71a3084532ee7f959fc0b2cac384d0c2a14d8a1.tar.gz
elibs-f71a3084532ee7f959fc0b2cac384d0c2a14d8a1.tar.bz2
exwm lib
Diffstat (limited to 'lib')
-rw-r--r--lib/eos/jao-wayland.el182
-rw-r--r--lib/readme.org1
-rw-r--r--lib/wm/jao-exwm-zathura.el152
-rw-r--r--lib/wm/jao-exwm.el199
4 files changed, 352 insertions, 182 deletions
diff --git a/lib/eos/jao-wayland.el b/lib/eos/jao-wayland.el
deleted file mode 100644
index e743eb0..0000000
--- a/lib/eos/jao-wayland.el
+++ /dev/null
@@ -1,182 +0,0 @@
-;;; jao-wayland.el --- interacting with wayland compositors -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2022, 2023 jao
-
-;; Author: jao <mail@jao.io>
-;; Keywords: convenience
-
-;; 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 <https://www.gnu.org/licenses/>.
-
-(require 'browse-url)
-(require 'jao-shell)
-(require 'jao-pdf)
-(require 'jao-tracking)
-
-;;; wayland
-(defvar jao-wayland-enabled
- (string= "wayland" (or (getenv "XDG_SESSION_TYPE") "")))
-
-(defsubst jao-wayland-type (&rest args)
- (apply #'jao-shell-exec* t "wtype" args))
-
-;;; river
-(defvar jao-river-enabled (jao-shell-running-p "river"))
-(defun jao-river-enabled-p () jao-river-enabled)
-
-(defsubst jao-river-to-ws (n)
- (jao-wayland-type "-M" "win" (format "%s" n)))
-
-(defsubst jao-river-window-list ()
- (alist-get 'toplevels
- (jao-shell-output "lswt -j"
- (lambda ()
- (let ((json-false nil)) (json-read))))))
-
-(defun jao-river-focused ()
- (seq-some (lambda (w) (and (alist-get 'activated w) w))
- (jao-river-window-list)))
-
-(defsubst jao-river-get-focused-title ()
- (alist-get 'title (jao-river-focused)))
-
-(defsubst jao-river-get-focused-app-id ()
- (alist-get 'app-id (jao-river-focused)))
-
-(defun jao-river-focus-window (title &optional rx)
- (let* ((ws (jao-river-window-list))
- (fltr (if rx #'string-match-p #'string=))
- (w (seq-find (lambda (w)
- (or (funcall fltr title (alist-get 'app_id w ""))
- (funcall fltr title (alist-get 'title w ""))))
- ws)))
- (or (alist-get 'activated w)
- (seq-some (lambda (_ignored)
- (jao-shell-exec "riverctl focus-view next" t)
- (or (funcall fltr title (jao-river-get-focused-app-id))
- (funcall fltr title (jao-river-get-focused-title))))
- (and w ws)))))
-
-(defun jao-river-zathura-to-org ()
- (let ((title (jao-river-get-focused-title)))
- (jao-river-to-ws 1)
- (jao-org-open-from-zathura title t)))
-
-(defun jao-river-zathura-kill-link ()
- (when-let* ((title (jao-river-get-focused-title))
- (lnk (jao-pdf-zathura-org-link title)))
- (jao-river-to-ws 1)
- (kill-new lnk)
- (message "Link to %s killed" title)))
-
-(defun jao-river-find-zathura-window (file)
- (let ((frx (regexp-quote (file-name-nondirectory file))))
- (seq-some (lambda (w)
- (and (string-suffix-p ".zathura" (alist-get 'app_id w ""))
- (string-match-p frx (alist-get 'title w ""))
- w))
- (jao-river-window-list))))
-
-(defun jao-river-open-with-zathura (file page)
- (let ((wd (jao-river-find-zathura-window file)))
- (jao-river-to-ws 3)
- (or (and wd (jao-river-focus-window (alist-get 'title wd)))
- (jao-shell-exec* "riverctl" "spawn" (jao-pdf-zathura-open-cmd file page)))
- (when page (sit-for 0.2) (jao-wayland-type (format "%dg" page)))))
-
-(defun jao-river-set-wallpaper (f)
- (jao-shell-kill-p "swaybg")
- (jao-shell-exec* "riverctl" "spawn" (concat "swaybg -m fill -i " f)))
-
-(defun jao-river-restart-i3bar ()
- (interactive)
- (jao-shell-kill-p "i3bar-river")
- (jao-shell-exec "riverctl spawn i3bar-river")
- (sit-for 0.2)
- (jao-tracking-set-log ""))
-
-(defun jao-river-toggle-emacs ()
- (let ((erx "^p?emacs\\(client\\)?\\|\\(.* - emacs\\)"))
- (if (or (string-match-p erx (jao-river-get-focused-title))
- (string-match-p erx (jao-river-get-focused-app-id)))
- (jao-shell-exec "riverctl focus-previous-tags")
- (jao-river-to-ws 1)
- (unless (jao-river-focus-window erx t)
- (jao-shell-exec* "riverctl" "spawn" "efoot")))))
-
-(defun jao-river-toggle-firefox ()
- (if (string-match-p "Firefox" (or (jao-river-get-focused-app-id) ""))
- (jao-river-to-ws 1)
- (jao-river-to-ws 2)
- (unless (jao-river-focus-window "Firefox")
- (jao-shell-exec* "riverctl" "spawn" "firefox"))))
-
-;;; sway
-(defun jao-sway-msg (msg)
- (shell-command (format "swaymsg '%s' >/dev/null" msg)))
-
-(defmacro jao-def-swaymsg (name msg)
- `(defun ,(intern (format "jao-sway-%s" name)) ()
- (interactive)
- (jao-sway-msg ,msg)))
-
-(jao-def-swaymsg firefox "[app_id=Firefox] focus")
-(jao-def-swaymsg pemacs "[app_id=pemacs] focus")
-
-(defvar jao-sway-enabled (jao-shell-running-p "sway"))
-
-(defconst jao-sway-get-active-title
- "swaymsg -t get_tree | jq '.. | select(.type?) | select(.focused==true).name'")
-
-(defconst jao-sway-get-active-app
- "swaymsg -t get_tree | jq '.. | select(.type?) | select(.focused==true).app_id'")
-
-(defun jao-sway-get-active-title ()
- (let ((tl (jao-shell-string jao-sway-get-active-title)))
- (and (string-match "\"\\(.+\\)\"" tl) (match-string 1 tl))))
-
-(defun jao-sway-get-active-app ()
- (let ((tl (jao-shell-string jao-sway-get-active-app)))
- (and (string-match "\"\\(.+\\)\"" tl) (match-string 1 tl))))
-
-(defun jao-sway-zathura-org ()
- (jao-org-open-from-zathura (jao-sway-get-active-title) t))
-
-(defun jao-sway-open-with-zathura (file page)
- (let* ((n (file-name-nondirectory file))
- (m (format "[title=\"%s\" app_id=\".*zathura\"] focus" n)))
- (jao-sway-msg "workspace number 3")
- (unless (= 0 (jao-sway-msg m))
- (jao-shell-exec (jao-pdf-zathura-open-cmd file page)))
- (when page (sit-for 0.2) (jao-wayland-type (format "%dg" page)))))
-
-(defun jao-sway-set-wallpaper (f)
- (jao-sway-msg (format "output * bg %s fill" f)))
-
-(defun jao-sway-run-or-focus (cmd &optional ws)
- (if (jao-shell-running-p "firefox")
- (jao-sway-msg (format "[app_id=%s] focus" cmd))
- (jao-sway-msg (format "workspace %s" (or ws 2)))
- (start-process-shell-command cmd nil cmd)))
-
-(defun jao-sway-run-or-focus-firefox ()
- (interactive)
- (jao-sway-run-or-focus "firefox"))
-
-(defun jao-sway-toggle-emacs ()
- (if (string-match-p "p?emacs" (jao-sway-get-active-app))
- (jao-sway-firefox)
- (jao-sway-pemacs)))
-
-(provide 'jao-wayland)
-;;; jao-wayland.el ends here
diff --git a/lib/readme.org b/lib/readme.org
index 60b1942..35faccb 100644
--- a/lib/readme.org
+++ b/lib/readme.org
@@ -5,4 +5,5 @@
- [[./media][media]] utilities for music players and the like
- [[./prog][prog]] utilities for compilation and programming modes
- [[./net][net]] utilities for networking (w3m, weather &c.)
+ - [[./wm][wm]] utilities for interacting with window managers
- [[./skels][skels]] a collection of emacs skeletons
diff --git a/lib/wm/jao-exwm-zathura.el b/lib/wm/jao-exwm-zathura.el
new file mode 100644
index 0000000..9da0a25
--- /dev/null
+++ b/lib/wm/jao-exwm-zathura.el
@@ -0,0 +1,152 @@
+;;; jao-exwm-zathura.el --- running zathura as doc manager -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2026 jao
+
+;; Author: jao <mail@jao.io>
+;; Keywords: docs
+
+;; 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 <https://www.gnu.org/licenses/>.
+
+(require 'jao-exwm)
+(require 'jao-pdf)
+(require 'jao-doc-view)
+(require 'jao-afio)
+(require 'jao-org-notes)
+
+;;; Zathura support
+(defun jao-zathura--buffer-p (b)
+ (string= "Zathura" (or (buffer-local-value 'exwm-class-name b) "")))
+
+(defun jao-zathura--buffers ()
+ (seq-filter #'jao-zathura--buffer-p (buffer-list)))
+
+(defun jao-zathura--file-info (b)
+ (with-current-buffer b
+ (jao-pdf-zathura-file-info (or exwm-title ""))))
+
+(defun jao-zathura-goto-page (page-no)
+ (jao-exwm-send-str (format "%sg" page-no)))
+
+;;;###autoload
+(defun jao-zathura-open-doc (&optional file-name page-no _height)
+ (interactive)
+ (let* ((file-name (expand-file-name (or file-name (buffer-file-name))))
+ (buffer (seq-find `(lambda (b)
+ (string= ,file-name
+ (car (jao-zathura--file-info b))))
+ (jao-zathura--buffers)))
+ (page-no (or page-no (jao-doc-view-current-page))))
+ (when jao-exwm-use-afio (jao-afio-goto-docs))
+ (if (not buffer)
+ (jao-exwm-run (if page-no
+ (format "zathura -P %s '%s'" page-no file-name)
+ (format "zathura '%s'" file-name)))
+ (pop-to-buffer buffer)
+ (when page-no (jao-zathura-goto-page page-no)))
+ (current-buffer)))
+
+(defun jao-exwm--zathura-setup ()
+ (when (and (string= exwm-class-name "Zathura")
+ (not jao-pdf--outline))
+ (let ((info (jao-zathura--file-info (current-buffer))))
+ (jao-doc-session-mark (car info))
+ (jao-doc-session-save)
+ (doc-view-imenu-index (car info) #'jao-zathura-goto-page))))
+
+(add-hook 'exwm-update-title-hook #'jao-exwm--zathura-setup t)
+
+;;;###autoload
+(defun jao-exwm-pdf-zathura-close-all ()
+ (interactive)
+ (dolist (b (jao-zathura--buffers))
+ (ignore-errors
+ (switch-to-buffer b)
+ (jao-exwm-send-str "q")))
+ t)
+
+;;;###autoload
+(defun jao-exwm-zathura-goto-org (&optional arg)
+ (interactive "P")
+ (when-let* ((info (jao-zathura--file-info (current-buffer)))
+ (file (jao-org-find-for-pdf (car info))))
+ (let ((newp (not (file-exists-p file))))
+ (when (or arg newp) (org-store-link nil t))
+ (find-file-other-window file)
+ (when newp
+ (jao-org-insert-doc-skeleton)
+ (org-insert-link)))))
+
+;;;###autoload
+(defun jao-exwm-zathura-goto-org* ()
+ (interactive)
+ (jao-exwm-zathura-goto-org t))
+
+;;;###autoload
+(defun jao-exwm-org-store-zathura-link ()
+ (when-let* ((info (jao-zathura--file-info (current-buffer))))
+ (let* ((file-name (car info))
+ (page (cadr info))
+ (desc (jao-pdf-section-title page file-name)))
+ (jao-org-links-store-pdf-link file-name page desc))))
+
+;;;###autoload
+(defun jao-exwm-pdf-enable-zathura ()
+ (interactive)
+ (add-hook 'kill-emacs-query-functions #'jao-exwm-pdf-zathura-close-all t)
+ (setq jao-pdf-open-in-emacs nil)
+ (setq jao-org-open-pdf-fun #'jao-zathura-open-doc)
+ (setq jao-org-links-pdf-store-fun #'jao-exwm-org-store-zathura-link)
+ (setq jao-open-doc-fun #'jao-zathura-open-doc))
+
+;;;###autoload
+(defun jao-exwm-pdf-disable-zathura ()
+ (interactive)
+ (remove-hook 'kill-emacs-query-functions #'jao-exwm-pdf-zathura-close-all)
+ (setq jao-pdf-open-in-emacs t)
+ (setq jao-org-open-pdf-fun #'jao-find-or-open)
+ (setq jao-org-links-pdf-store-fun nil)
+ (setq jao-open-doc-fun #'jao-find-or-open))
+
+;;;###autoload
+(defun jao-exwm-org-to-pdf-file ()
+ (expand-file-name (concat "doc/" (file-name-base buffer-file-name) ".pdf")
+ (file-name-directory jao-org-notes-dir)))
+
+;;;###autoload
+(defun jao-exwm-zathura-goto-pdf ()
+ (interactive)
+ (if jao-pdf-open-in-emacs
+ (jao-org-goto-pdf)
+ (when-let* ((pdf (jao-exwm-org-to-pdf-file)))
+ (jao-zathura-open-doc pdf))))
+
+(with-eval-after-load "org"
+ (define-key org-mode-map (kbd "C-c o") #'jao-exwm-zathura-goto-pdf))
+
+(when (not jao-pdf-open-in-emacs)
+ (jao-exwm-pdf-enable-zathura))
+
+(defun jao-exwm-select-pdf ()
+ (interactive)
+ (let ((b (read-buffer "Document: " nil t
+ (lambda (b)
+ (let ((b (cdr b)))
+ (or (jao-zathura--buffer-p b)
+ (member (buffer-local-value 'major-mode b)
+ '(pdf-view-mode doc-view-mode))))))))
+ (jao-afio-goto-docs)
+ (pop-to-buffer b)))
+
+(provide 'jao-exwm-zathura)
+;;; jao-exwm-zathura.el ends here
diff --git a/lib/wm/jao-exwm.el b/lib/wm/jao-exwm.el
new file mode 100644
index 0000000..52a2e34
--- /dev/null
+++ b/lib/wm/jao-exwm.el
@@ -0,0 +1,199 @@
+;;; jao-exwm.el --- exwm utilities -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2026 jao
+
+;; Author: jao <mail@jao.io>
+
+;; 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 <https://www.gnu.org/licenses/>.
+
+(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