From 6b688e4281e86168f3b7a1dda51fa2553845f6b3 Mon Sep 17 00:00:00 2001 From: Ian Keane Date: Sat, 8 Aug 2026 15:01:05 -0400 Subject: [PATCH] Add predicate for persistent floating windows --- config/stumpwm/config.lisp | 55 ++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/config/stumpwm/config.lisp b/config/stumpwm/config.lisp index 77d392b..6bf8e4b 100644 --- a/config/stumpwm/config.lisp +++ b/config/stumpwm/config.lisp @@ -51,7 +51,8 @@ (define-frame-preference "Default" (0 t nil :class "Konqueror" :role "...konqueror-mainwindow") (1 t nil :class "XTerm") - (:float t t :class "alsamixer-scratch")) + (:float t t :class "alsamixer-scratch") + (:float t t :class "ncmpcpp-scratch")) (define-frame-preference "Emacs" (1 t t :restore "emacs-editing-dump" :title "...xdvi") @@ -200,49 +201,73 @@ ;; causes the window to be floated *during* group-add-window, before tiling ;; logic runs. This is the only reliable way to float a window from config — ;; attempting to call float-window from *new-window-hook* is too late, as -;; StumpWM has already tiled it. The hook IS still useful though, for -;; positioning after the float is established. +;; StumpWM has already tiled it. The hook IS still useful for positioning +;; after the float is established. +;; +;; float-window and float-window-move-resize are internal (stumpwm::), not +;; exported from the stumpwm package. ;; ;; To add a new float terminal: ;; 1. Add (:float t t :class "my-class") to the "Default" define-frame-preference ;; 2. Call (register-float-term "my-class" "command" x y width height) +;; Pass :persistent t if the window should hide on s-q instead of closing ;; 3. Define a command that calls (spawn-or-focus "my-class" "command") (defvar *float-term-rules* '()) -(defun register-float-term (class command x y width height) - "Register a floating terminal. Add a matching define-frame-preference rule too." +(defun register-float-term (class command x y width height &key persistent) + "Register a floating terminal. Add a matching define-frame-preference rule too. +If PERSISTENT is t, smart-kill will hide the window instead of killing it." (setf *float-term-rules* - (cons (list class command x y width height) + (cons (list class command x y width height :persistent persistent) (remove class *float-term-rules* :key #'car :test #'string=)))) (add-hook *new-window-hook* (lambda (win) (let ((rule (assoc (window-class win) *float-term-rules* :test #'string=))) (when rule - (destructuring-bind (class command x y width height) rule - (declare (ignore class command)) + (let ((x (nth 2 rule)) + (y (nth 3 rule)) + (width (nth 4 rule)) + (height (nth 5 rule))) (stumpwm::float-window-move-resize win :x x :y y :width width :height height)))))) (defun spawn-or-focus (class command) - "Focus existing window with CLASS or spawn COMMAND." + "Unhide or focus existing window with CLASS, or spawn COMMAND if none exists." (let ((win (find-if (lambda (w) (string= (window-class w) class)) (screen-windows (current-screen))))) - (if win (focus-window win) (run-shell-command command)))) + (cond ((null win) (run-shell-command command)) + ((eq win (current-window)) nil) + (t (focus-window win))))) -;; Registrations +(defcommand smart-kill () () + "Kill window normally, unless it's a persistent float — then hide it instead." + (let* ((win (current-window)) + (class (when win (window-class win))) + (rule (when class (assoc class *float-term-rules* :test #'string=)))) + (if (and rule (getf (nthcdr 6 rule) :persistent)) + (hide-window win) + (kill-window-or-frame)))) + +;; Registrations — add new float terminals here (register-float-term "alsamixer-scratch" "wezterm start --class alsamixer-scratch -- alsamixer" - 228 150 400 800) + 628 450 800 400) (defcommand alsamixer-float () () "Open alsamixer in a floating wezterm window." (spawn-or-focus "alsamixer-scratch" "wezterm start --class alsamixer-scratch -- alsamixer")) +(register-float-term "ncmpcpp-scratch" + "wezterm start --class ncmpcpp-scratch -- ncmpcpp" + 628 300 900 500 :persistent t) + +(defcommand ncmpcpp-float () () + "Open ncmpcpp in a persistent floating wezterm window." + (spawn-or-focus "ncmpcpp-scratch" "wezterm start --class ncmpcpp-scratch -- ncmpcpp")) ;;; Config keymap (defvar *config-map* (make-sparse-keymap)) (define-key *config-map* (kbd "a") "alsamixer-float") -(define-key *config-map* (kbd "t") "alsamixer-float") (defvar *brightness-map* (make-sparse-keymap)) (define-key *brightness-map* (kbd "1") "brightness-10") (define-key *brightness-map* (kbd "2") "brightness-20") @@ -272,6 +297,7 @@ (define-key *media-map* (kbd "v") "mpv-pick") (define-key *media-map* (kbd "m") "mpv-url") (define-key *media-map* (kbd "d") "yt-download") +(define-key *media-map* (kbd "p") "ncmpcpp-float") ;;; Top-level bindings (define-key *top-map* (kbd "s-d") "rofi-run") @@ -282,7 +308,8 @@ (define-key *top-map* (kbd "s-l") "move-focus right") (define-key *top-map* (kbd "s-p") "next-in-frame") (define-key *top-map* (kbd "s-n") "prev-in-frame") -(define-key *top-map* (kbd "s-q") "kill-window-or-frame") +(define-key *top-map* (kbd "s-q") "smart-kill") +(define-key *top-map* (kbd "s-F") "select-floating-window") (define-key *top-map* (kbd "s-f") "toggle-fullscreen") (define-key *top-map* (kbd "s-c") *config-map*) (define-key *top-map* (kbd "s--") "vsplit")