stumpwm.stumpwm-contrib/util/swm-gaps/swm-gaps.lisp
Abhinav Tushar 72c508fcb2
Update swm-gaps to fix crash
After certain updates in stumpwm, the older code around frame parameters was not
working, leading to crash. This patch fixes that and works without crash for
current stumpwm master.
2026-05-15 12:47:10 +05:30

190 lines
7.8 KiB
Common Lisp

;;;; swm-gaps.lisp
(in-package #:swm-gaps)
(export '(*inner-gaps-size* *outer-gaps-size* *head-gaps-size* *gaps-on* toggle-gaps toggle-gaps-on toggle-gaps-off))
(defvar *inner-gaps-size* 5)
(defvar *outer-gaps-size* 10)
(defvar *head-gaps-size* 0)
(defvar *gaps-on* nil)
(defun apply-gaps-p (win)
"Tell if gaps should be applied to this window"
(and *gaps-on* (not (stumpwm::window-transient-p win)) (not (window-fullscreen win))))
(defun window-edging-p (win direction)
"Tell if the window is touching the head in the given direction."
(let* ((frame (stumpwm::window-frame win))
(head (stumpwm::frame-head (stumpwm:window-group win) frame))
(offset (nth-value 2 (stumpwm::get-edge frame direction))))
(ecase direction
(:top
(= offset (stumpwm::head-y head)))
(:bottom
(= offset (+ (stumpwm::head-y head) (stumpwm::head-height head))))
(:left
(= offset (stumpwm::head-x head)))
(:right
(= offset (+ (stumpwm::head-x head) (stumpwm::head-width head)))))))
(defun gaps-offsets (win)
"Return gap offset values for the window. X and Y values are added. WIDTH and
HEIGHT are subtracted."
(let ((x *inner-gaps-size*)
(y *inner-gaps-size*)
(width (* 2 *inner-gaps-size*))
(height (* 2 *inner-gaps-size*)))
(if (window-edging-p win :top)
(setf y (+ y *outer-gaps-size*)
height (+ height *outer-gaps-size*)))
(if (window-edging-p win :bottom)
(setf height (+ height *outer-gaps-size*)))
(if (window-edging-p win :left)
(setf x (+ x *outer-gaps-size*)
width (+ width *outer-gaps-size*)))
(if (window-edging-p win :right)
(setf width (+ width *outer-gaps-size*)))
(values x y width height)))
(defun stumpwm::maximize-window (win)
"Redefined gaps aware maximize function."
(multiple-value-bind (x y wx wy width height border stick)
(stumpwm::geometry-hints win)
(let ((ox 0) (oy 0) (ow 0) (oh 0)
(frame (stumpwm::window-frame win)))
(if (apply-gaps-p win)
(multiple-value-setq (ox oy ow oh) (gaps-offsets win)))
;; Only do width or height subtraction if result will be positive,
;; otherwise stumpwm will crash. Also, only modify window dimensions
;; if needed (i.e. window at least fills frame minus gap).
(when (and (< ow width)
(>= width (- (stumpwm::frame-display-width (window-group win) frame) ow)))
(setf width (- width ow)))
(when (and (< oh height)
(>= height (- (stumpwm::frame-display-height (window-group win) frame) oh)))
(setf height (- height oh)))
(setf x (+ x ox)
y (+ y oy))
;; This is the only place a window's geometry should change
(set-window-geometry win :x wx :y wy :width width :height height :border-width 0)
(xlib:with-state ((window-parent win))
;; FIXME: updating the border doesn't need to be run everytime
;; the window is maximized, but only when the border style or
;; window type changes. The overhead is probably minimal,
;; though.
(setf (xlib:drawable-x (window-parent win)) x
(xlib:drawable-y (window-parent win)) y
(xlib:drawable-border-width (window-parent win)) border)
;; the parent window should stick to the size of the window
;; unless it isn't being maximized to fill the frame.
(if (or stick
(find *window-border-style* '(:tight :none)))
(setf (xlib:drawable-width (window-parent win)) (window-width win)
(xlib:drawable-height (window-parent win)) (window-height win))
(let ((frame (stumpwm::window-frame win)))
(setf (xlib:drawable-width (window-parent win)) (- (stumpwm::frame-display-width (window-group win) frame)
(* 2 (xlib:drawable-border-width (window-parent win)))
ow)
(xlib:drawable-height (window-parent win)) (- (stumpwm::frame-display-height (window-group win) frame)
(* 2 (xlib:drawable-border-width (window-parent win)))
oh))))
;; update the "extents"
(xlib:change-property (window-xwin win) :_NET_FRAME_EXTENTS
(list wx
(- (xlib:drawable-width (window-parent win)) width wx)
wy
(- (xlib:drawable-height (window-parent win)) height wy))
:cardinal 32))
(stumpwm::update-decoration win)
(update-configuration win))))
(defun reset-all-windows ()
"Reset the size for all tiled windows"
(mapcar #'stumpwm::maximize-window
(stumpwm::only-tile-windows (stumpwm:screen-windows (current-screen)))))
;; Redefined neighbour for working with head gaps
(defun stumpwm::neighbour (direction frame frameset)
"Returns the best neighbour of FRAME in FRAMESET on the DIRECTION edge.
Valid directions are :UP, :DOWN, :LEFT, :RIGHT.
eg: (NEIGHBOUR :UP F FS) finds the frame in FS that is the 'best'
neighbour above F."
(let ((src-edge (ecase direction
(:up :top)
(:down :bottom)
(:left :left)
(:right :right)))
(opposite (ecase direction
(:up :bottom)
(:down :top)
(:left :right)
(:right :left)))
(best-frame nil)
(best-overlap 0)
(nearest-edge-diff nil))
(multiple-value-bind (src-s src-e src-offset)
(stumpwm::get-edge frame src-edge)
;; Get the edge distance closest in the required direction
(dolist (f frameset)
(multiple-value-bind (s e offset)
(stumpwm::get-edge f opposite)
(let ((offset-diff (abs (- src-offset offset))))
(if nearest-edge-diff
(if (< offset-diff nearest-edge-diff)
(setf nearest-edge-diff offset-diff))
(setf nearest-edge-diff offset-diff)))))
(dolist (f frameset)
(multiple-value-bind (s e offset)
(stumpwm::get-edge f opposite)
(let ((overlap (- (min src-e e)
(max src-s s))))
;; Two edges are neighbours if they have the same offset (after
;; accounting for gaps) and their starts and ends overlap. We want
;; to find the neighbour that overlaps the most.
(when (and (= (abs (- src-offset offset)) nearest-edge-diff)
(> overlap best-overlap))
(setf best-frame f)
(setf best-overlap overlap))))))
best-frame))
(defun add-head-gaps ()
"Add extra gap to the head boundary"
(mapcar (lambda (head)
(let* ((height (stumpwm::head-height head))
(width (stumpwm::head-width head))
(x (stumpwm::head-x head))
(y (stumpwm::head-y head))
(gap *head-gaps-size*)
(new-height (- height (* 2 gap)))
(new-width (- width (* 2 gap))))
(stumpwm::resize-head
(stumpwm::head-number head)
(+ x gap) (+ y gap)
new-width new-height)))
(screen-heads (current-screen))))
(defcommand toggle-gaps () ()
"Toggle gaps"
(if (null *gaps-on*)
(toggle-gaps-on)
(toggle-gaps-off)))
(defcommand toggle-gaps-on () ()
"Turn gaps on"
(setf *gaps-on* t)
(progn
(add-head-gaps)
(reset-all-windows)))
(defcommand toggle-gaps-off () ()
"Turn gaps off"
(setf *gaps-on* nil)
(stumpwm:refresh-heads))