-
Notifications
You must be signed in to change notification settings - Fork 265
/
Copy pathdynamic-window.lisp
141 lines (132 loc) · 6.14 KB
/
dynamic-window.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
(in-package :stumpwm)
(defvar *expose-n-max* 26
"Maximum number of windows to display in the expose")
(defvar *expose-auto-tile-fn* 'expose-tile
"Function to call to tile current windows.")
(defun frame-area (frames)
"Calculate the area of each frame and store the result in a list."
(mapcar (lambda (f)
(* (frame-width f) (frame-height f)))
frames))
(defun recursive-tile (n group)
"Find the largest (by area) frame in the group, split it in half
vertically or horizontally depending on which is dimension is larger.
Repeat until there is only one window left."
(unless (<= n 1)
(let* ((frames (group-frames group))
(areas (frame-area frames))
(idx (position (reduce #'max areas) areas))
(frame (nth idx frames))
(w (frame-width frame))
(h (frame-height frame)))
(focus-frame group frame)
(if (< w h)
(vsplit)
(hsplit)))
(recursive-tile (- n 1) group)))
(defun expose-tile (win &optional (group (current-group)))
"First make only one frame, then recursively split the frame until
there are no more hidden windows. Tiling is done by splitting in the
direction that is widest, and choosing the frame that has the largest
area."
(declare (ignore win))
(let* ((windows (group-windows group))
(num-win (length windows)))
;; Only try to make this the only frame if it isn't already.
(unless (only-one-frame-p)
(only))
(recursive-tile (min *expose-n-max* num-win) group)))
(defgeneric invoke-expose (group))
(defmethod invoke-expose ((group tile-group))
(funcall *expose-auto-tile-fn* nil
(current-group (current-screen)))
;; have the user select a window
(unless (only-one-frame-p)
(run-commands "fselect"))
;; maximize that window
(only))
(defmethod invoke-expose ((group float-group))
(let ((curwin (group-current-window group))
(wincount (length (group-windows group)))
(width (head-width (group-current-head group)))
(height (head-height (group-current-head group)))
(old-win-positions nil))
(labels ((collect-x-y (wincount)
(let ((factors (loop for i from 1 to (1+ wincount)
when (= (mod wincount i) 0)
collect i)))
(cond ((or (= (length factors) 1) ; prime number
(= (length factors) 2))
(collect-x-y (1+ wincount)))
(t (nthcdr (floor (length factors) 2) factors))))))
(unless (= wincount 1)
(destructuring-bind (ycount xcount &rest ignore)
(let ((f (collect-x-y wincount)))
(let* ((x (car f))
(y (ceiling wincount x)))
(list y x)))
(declare (ignore ignore))
(block done
(let ((w (floor width xcount))
(h (floor height ycount))
(y 0)
(x 0)
(wins (group-windows group)))
(dotimes (i ycount)
(setf y (* i h))
(dotimes (j xcount)
(setf x (* j w))
(let ((win (pop wins)))
(if win
(progn
(push (list win :x (window-x win)
:y (window-y win)
:width (window-width win)
:height (window-height win))
old-win-positions)
(float-window-move-resize win :x x
:y y
:width w
:height h))
(return-from done nil)))))))
(let* ((ws (draw-window-numbers group))
(focuswin
(or (unwind-protect
(multiple-value-bind (has-click ch x y)
(read-one-char-or-click group)
(if has-click
(let ((winner))
(dolist (f (group-windows group))
(when (and (> x (window-x f))
(> y (window-y f)))
(if winner
(when (or (> (window-x f)
(window-x winner))
(> (window-y f)
(window-y winner)))
(setf winner f))
(setf winner f))))
(ungrab-pointer)
winner)
(when ch
(let ((num
(read-from-string (string ch) nil nil)))
(dformat 3 "read ~S ~S~%" ch num)
(find num (group-windows group)
:test #'=
:key 'window-number)))))
(mapc #'xlib:destroy-window ws)
(mapc (lambda (spec)
(apply #'float-window-move-resize spec))
old-win-positions)
(xlib:display-finish-output *display*))
curwin)))
(when focuswin
(focus-all focuswin))))))))
(defcommand expose () ()
"Automagically lay out all windows in a grid and let the user select one, making
that window the focused window. Set the variable `*expose-auto-tile-fn*' to
another tiling function if a different layout is desired for tile groups. Set
`*expose-n-max*' to the maximum number of windows to be displayed for choosing
when in a tile group."
(invoke-expose (current-group (current-screen))))