Copyright date and mail update
[clfswm.git] / src / clfswm-util.lisp
blob5a4ff37a79f20cb10f566eff05aa45fc5fa49a67
1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
3 ;;;
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Utility
6 ;;; --------------------------------------------------------------------------
7 ;;;
8 ;;; (C) 2012 Philippe Brochard <pbrochard@common-lisp.net>
9 ;;;
10 ;;; This program is free software; you can redistribute it and/or modify
11 ;;; it under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or
13 ;;; (at your option) any later version.
14 ;;;
15 ;;; This program is distributed in the hope that it will be useful,
16 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with this program; if not, write to the Free Software
22 ;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 ;;;
24 ;;; --------------------------------------------------------------------------
26 (in-package :clfswm)
29 ;;; Configuration file
30 (defun xdg-config-home ()
31 (aif (getenv "XDG_CONFIG_HOME")
32 (pathname-directory (concatenate 'string it "/"))
33 (append (pathname-directory (user-homedir-pathname)) '(".config"))))
36 (let ((saved-conf-name nil))
37 (defun conf-file-name (&optional alternate-name)
38 (unless (and saved-conf-name (not alternate-name))
39 (let* ((user-conf (probe-file (merge-pathnames (user-homedir-pathname) #p".clfswmrc")))
40 (etc-conf (probe-file #p"/etc/clfswmrc"))
41 (config-user-conf (probe-file (make-pathname :directory (append (xdg-config-home) '("clfswm"))
42 :name "clfswmrc")))
43 (alternate-conf (and alternate-name (probe-file alternate-name))))
44 (setf saved-conf-name (or alternate-conf config-user-conf user-conf etc-conf))))
45 (print saved-conf-name)
46 saved-conf-name))
51 (defun load-contrib (file)
52 "Load a file in the contrib directory"
53 (let ((truename (merge-pathnames file *contrib-dir*)))
54 (format t "Loading contribution file: ~A~%" truename)
55 (when (probe-file truename)
56 (load truename :verbose nil))))
59 (defun reload-clfswm ()
60 "Reload clfswm"
61 (format t "~&-*- Reloading CLFSWM -*-~%")
62 (asdf:oos 'asdf:load-op :clfswm)
63 (reset-clfswm))
67 (defun query-yes-or-no (formatter &rest args)
68 (let ((rep (query-string (apply #'format nil formatter args) "" '("Yes" "No"))))
69 (or (string= rep "")
70 (char= (char rep 0) #\y)
71 (char= (char rep 0) #\Y))))
75 (defun banish-pointer ()
76 "Move the pointer to the lower right corner of the screen"
77 (with-placement (*banish-pointer-placement* x y)
78 (xlib:warp-pointer *root* x y)))
83 ;;; Root functions utility
84 (defun select-generic-root (fun restart-menu)
85 (no-focus)
86 (let* ((current-root (find-root (current-child)))
87 (parent (find-parent-frame (root-original current-root))))
88 (when parent
89 (setf (frame-child parent) (funcall fun (frame-child parent)))
90 (let ((new-root (find-root (frame-selected-child parent))))
91 (setf (current-child) (aif (root-current-child new-root)
93 (frame-selected-child parent))))))
94 (show-all-children t)
95 (if restart-menu
96 (open-menu (find-menu 'root-menu))
97 (leave-second-mode)))
99 (defun select-next-root ()
100 "Select the next root"
101 (select-generic-root #'rotate-list nil))
103 (defun select-previous-root ()
104 "Select the previous root"
105 (select-generic-root #'anti-rotate-list nil))
108 (defun select-next-root-restart-menu ()
109 "Select the next root"
110 (select-generic-root #'rotate-list t))
112 (defun select-previous-root-restart-menu ()
113 "Select the previous root"
114 (select-generic-root #'anti-rotate-list t))
117 (defun rotate-root-geometry-generic (fun restart-menu)
118 (no-focus)
119 (funcall fun)
120 (show-all-children t)
121 (if restart-menu
122 (open-menu (find-menu 'root-menu))
123 (leave-second-mode)))
126 (defun rotate-root-geometry-next ()
127 "Rotate root geometry to next root"
128 (rotate-root-geometry-generic #'rotate-root-geometry nil))
130 (defun rotate-root-geometry-previous ()
131 "Rotate root geometry to previous root"
132 (rotate-root-geometry-generic #'anti-rotate-root-geometry nil))
134 (defun rotate-root-geometry-next-restart-menu ()
135 "Rotate root geometry to next root"
136 (rotate-root-geometry-generic #'rotate-root-geometry t))
138 (defun rotate-root-geometry-previous-restart-menu ()
139 "Rotate root geometry to previous root"
140 (rotate-root-geometry-generic #'anti-rotate-root-geometry t))
144 (defun exchange-root-geometry-with-mouse ()
145 "Exchange two root geometry pointed with the mouse"
146 (open-notify-window '("Select the first root to exchange"))
147 (wait-no-key-or-button-press)
148 (wait-mouse-button-release)
149 (close-notify-window)
150 (multiple-value-bind (x1 y1) (xlib:query-pointer *root*)
151 (open-notify-window '("Select the second root to exchange"))
152 (wait-no-key-or-button-press)
153 (wait-mouse-button-release)
154 (close-notify-window)
155 (multiple-value-bind (x2 y2) (xlib:query-pointer *root*)
156 (exchange-root-geometry (find-root-by-coordinates x1 y1)
157 (find-root-by-coordinates x2 y2))))
158 (show-all-children)
159 (leave-second-mode))
161 (defun change-current-root-geometry ()
162 "Change the current root geometry"
163 (let* ((root (find-root (current-child)))
164 (x (query-number "New root X position" (root-x root)))
165 (y (query-number "New root Y position" (root-y root)))
166 (w (query-number "New root width" (root-w root)))
167 (h (query-number "New root height" (root-h root))))
168 (setf (root-x root) x (root-y root) y
169 (root-w root) w (root-h root) h)
170 (show-all-children)
171 (leave-second-mode)))
175 (defun place-window-from-hints (window)
176 "Place a window from its hints"
177 (let* ((hints (xlib:wm-normal-hints window))
178 (min-width (or (and hints (xlib:wm-size-hints-min-width hints)) 0))
179 (min-height (or (and hints (xlib:wm-size-hints-min-height hints)) 0))
180 (max-width (or (and hints (xlib:wm-size-hints-max-width hints)) (x-drawable-width *root*)))
181 (max-height (or (and hints (xlib:wm-size-hints-max-height hints)) (x-drawable-height *root*)))
182 (rwidth (or (and hints (or (xlib:wm-size-hints-width hints) (xlib:wm-size-hints-base-width hints)))
183 (x-drawable-width window)))
184 (rheight (or (and hints (or (xlib:wm-size-hints-height hints) (xlib:wm-size-hints-base-height hints)))
185 (x-drawable-height window))))
186 (setf (x-drawable-width window) (min (max min-width rwidth *default-window-width*) max-width)
187 (x-drawable-height window) (min (max min-height rheight *default-window-height*) max-height))
188 (with-placement (*unmanaged-window-placement* x y (x-drawable-width window) (x-drawable-height window))
189 (setf (x-drawable-x window) x
190 (x-drawable-y window) y))
191 (xlib:display-finish-output *display*)))
194 (defun rename-current-child ()
195 "Rename the current child"
196 (let ((name (query-string (format nil "New child name: (last: ~A)" (child-name (current-child)))
197 (child-name (current-child)))))
198 (rename-child (current-child) name)
199 (leave-second-mode)))
202 (defun ask-child-transparency (msg child)
203 (let ((trans (query-number (format nil "New ~A transparency: (last: ~A)"
205 (* 100 (child-transparency child)))
206 (* 100 (child-transparency child)))))
207 (when (numberp trans)
208 (setf (child-transparency child) (float (/ trans 100))))))
210 (defun set-current-child-transparency ()
211 "Set the current child transparency"
212 (ask-child-transparency "child" (current-child))
213 (leave-second-mode))
216 (defun ask-child-border-size (msg child)
217 (let ((size (query-number (format nil "New ~A border size: (last: ~A)"
219 (child-border-size child))
220 (child-border-size child))))
221 (when (numberp size)
222 (setf (child-border-size child) size))))
225 (defun set-current-child-border-size ()
226 "Set the current child border size"
227 (ask-child-border-size "child" (current-child))
228 (leave-second-mode))
231 (defun renumber-current-frame ()
232 "Renumber the current frame"
233 (when (frame-p (current-child))
234 (let ((number (query-number (format nil "New child number: (last: ~A)" (frame-number (current-child)))
235 (frame-number (current-child)))))
236 (setf (frame-number (current-child)) number)
237 (leave-second-mode))))
242 (defun add-default-frame ()
243 "Add a default frame in the current frame"
244 (when (frame-p (current-child))
245 (let ((name (query-string "Frame name")))
246 (push (create-frame :name name) (frame-child (current-child)))))
247 (leave-second-mode))
249 (defun add-frame-in-parent-frame ()
250 "Add a frame in the parent frame (and reorganize parent frame)"
251 (let ((parent (find-parent-frame (current-child))))
252 (when (and parent (not (child-original-root-p (current-child))))
253 (let ((new-frame (create-frame)))
254 (pushnew new-frame (frame-child parent))
255 (awhen (child-root-p (current-child))
256 (change-root it parent))
257 (setf (current-child) parent)
258 (set-layout-once #'tile-space-layout)
259 (setf (current-child) new-frame)
260 (leave-second-mode)))))
265 (defun add-placed-frame ()
266 "Add a placed frame in the current frame"
267 (when (frame-p (current-child))
268 (let ((name (query-string "Frame name"))
269 (x (/ (query-number "Frame x in percent (%)") 100))
270 (y (/ (query-number "Frame y in percent (%)") 100))
271 (w (/ (query-number "Frame width in percent (%)" 100) 100))
272 (h (/ (query-number "Frame height in percent (%)" 100) 100)))
273 (push (create-frame :name name :x x :y y :w w :h h)
274 (frame-child (current-child)))))
275 (leave-second-mode))
279 (defun delete-focus-window-generic (close-fun)
280 (with-focus-window (window)
281 (when (child-equal-p window (current-child))
282 (setf (current-child) (find-current-root)))
283 (delete-child-and-children-in-all-frames window close-fun)))
285 (defun delete-focus-window ()
286 "Close focus window: Delete the focus window in all frames and workspaces"
287 (delete-focus-window-generic 'delete-window))
289 (defun destroy-focus-window ()
290 "Kill focus window: Destroy the focus window in all frames and workspaces"
291 (delete-focus-window-generic 'destroy-window))
293 (defun remove-focus-window ()
294 "Remove the focus window from the current frame"
295 (with-focus-window (window)
296 (setf (current-child) (find-current-root))
297 (hide-child window)
298 (remove-child-in-frame window (find-parent-frame window))
299 (show-all-children)))
302 (defun unhide-all-windows-in-current-child ()
303 "Unhide all hidden windows into the current child"
304 (dolist (window (get-hidden-windows))
305 (unhide-window window)
306 (process-new-window window)
307 (map-window window))
308 (show-all-children))
313 (defun find-window-under-mouse (x y)
314 "Return the child window under the mouse"
315 (let ((win *root*))
316 (with-all-windows-frames-and-parent (*root-frame* child parent)
317 (when (and (or (managed-window-p child parent) (child-equal-p parent (current-child)))
318 (not (window-hidden-p child))
319 (in-window child x y))
320 (setf win child))
321 (when (in-frame child x y)
322 (setf win (frame-window child))))
323 win))
328 (defun find-child-under-mouse-in-never-managed-windows (x y)
329 "Return the child under mouse from never managed windows"
330 (let ((ret nil))
331 (dolist (win (xlib:query-tree *root*))
332 (unless (window-hidden-p win)
333 (multiple-value-bind (never-managed raise)
334 (never-managed-window-p win)
335 (when (and never-managed raise (in-window win x y))
336 (setf ret win)))))
337 ret))
340 (defun find-child-under-mouse-in-child-tree (x y &optional first-foundp)
341 "Return the child under the mouse"
342 (let ((ret nil))
343 (with-all-windows-frames-and-parent (*root-frame* child parent)
344 (when (and (not (window-hidden-p child))
345 (or (managed-window-p child parent) (child-equal-p parent (current-child)))
346 (in-window child x y))
347 (if first-foundp
348 (return-from find-child-under-mouse-in-child-tree child)
349 (setf ret child)))
350 (when (in-frame child x y)
351 (if first-foundp
352 (return-from find-child-under-mouse-in-child-tree child)
353 (setf ret child))))
354 ret))
357 (defun find-child-under-mouse (x y &optional first-foundp also-never-managed)
358 "Return the child under the mouse"
359 (or (and also-never-managed
360 (find-child-under-mouse-in-never-managed-windows x y))
361 (find-child-under-mouse-in-child-tree x y first-foundp)))
367 ;;; Selection functions
368 (defun clear-selection ()
369 "Clear the current selection"
370 (setf *child-selection* nil)
371 (display-all-root-frame-info))
373 (defun copy-current-child ()
374 "Copy the current child to the selection"
375 (pushnew (current-child) *child-selection*)
376 (display-all-root-frame-info))
379 (defun cut-current-child (&optional (show-now t))
380 "Cut the current child to the selection"
381 (unless (child-root-p (current-child))
382 (let ((parent (find-parent-frame (current-child))))
383 (hide-all (current-child))
384 (copy-current-child)
385 (remove-child-in-frame (current-child) (find-parent-frame (current-child) (find-current-root)))
386 (when parent
387 (setf (current-child) parent))
388 (when show-now
389 (show-all-children t))
390 (current-child))))
392 (defun remove-current-child ()
393 "Remove the current child from its parent frame"
394 (unless (child-root-p (current-child))
395 (let ((parent (find-parent-frame (current-child))))
396 (hide-all (current-child))
397 (remove-child-in-frame (current-child) (find-parent-frame (current-child) (find-current-root)))
398 (when parent
399 (setf (current-child) parent))
400 (show-all-children t)
401 (leave-second-mode))))
403 (defun delete-current-child ()
404 "Delete the current child and its children in all frames"
405 (unless (child-root-p (current-child))
406 (hide-all (current-child))
407 (delete-child-and-children-in-all-frames (current-child))
408 (show-all-children t)
409 (leave-second-mode)))
412 (defun paste-selection-no-clear ()
413 "Paste the selection in the current frame - Do not clear the selection after paste"
414 (when (frame-p (current-child))
415 (dolist (child *child-selection*)
416 (unless (find-child-in-parent child (current-child))
417 (pushnew child (frame-child (current-child)) :test #'child-equal-p)))
418 (show-all-children)))
420 (defun paste-selection ()
421 "Paste the selection in the current frame"
422 (when (frame-p (current-child))
423 (paste-selection-no-clear)
424 (setf *child-selection* nil)
425 (display-all-root-frame-info)))
428 (defun copy-focus-window ()
429 "Copy the focus window to the selection"
430 (with-focus-window (window)
431 (with-current-child (window)
432 (copy-current-child))))
435 (defun cut-focus-window ()
436 "Cut the focus window to the selection"
437 (with-focus-window (window)
438 (setf (current-child) (with-current-child (window)
439 (cut-current-child nil)))
440 (show-all-children t)))
447 ;;; Maximize function
448 (defun frame-toggle-maximize ()
449 "Maximize/Unmaximize the current frame in its parent frame"
450 (when (frame-p (current-child))
451 (let ((unmaximized-coords (frame-data-slot (current-child) :unmaximized-coords)))
452 (if unmaximized-coords
453 (with-slots (x y w h) (current-child)
454 (destructuring-bind (nx ny nw nh) unmaximized-coords
455 (setf (frame-data-slot (current-child) :unmaximized-coords) nil
456 x nx y ny w nw h nh)))
457 (with-slots (x y w h) (current-child)
458 (setf (frame-data-slot (current-child) :unmaximized-coords)
459 (list x y w h)
460 x 0 y 0 w 1 h 1))))
461 (show-all-children)
462 (leave-second-mode)))
472 ;;; CONFIG - Identify mode
473 (defun identify-key ()
474 "Identify a key"
475 (let* ((done nil)
476 (font (xlib:open-font *display* *identify-font-string*))
477 (window (xlib:create-window :parent *root*
478 :x 0 :y 0
479 :width (- (xlib:screen-width *screen*) (* *border-size* 2))
480 :height (* 5 (+ (xlib:max-char-ascent font) (xlib:max-char-descent font)))
481 :background (get-color *identify-background*)
482 :border-width *border-size*
483 :border (get-color *identify-border*)
484 :colormap (xlib:screen-default-colormap *screen*)
485 :event-mask '(:exposure)))
486 (gc (xlib:create-gcontext :drawable window
487 :foreground (get-color *identify-foreground*)
488 :background (get-color *identify-background*)
489 :font font
490 :line-style :solid)))
491 (setf (window-transparency window) *identify-transparency*)
492 (labels ((print-doc (msg hash-table-key pos code state)
493 (let ((function (find-key-from-code hash-table-key code state)))
494 (when (and function (fboundp (first function)))
495 (xlib:draw-glyphs *pixmap-buffer* gc 10 (+ (* pos (+ (xlib:max-char-ascent font) (xlib:max-char-descent font))) 5)
496 (format nil "~A ~A" msg (documentation (first function) 'function))))))
497 (print-key (code state keysym key modifiers)
498 (clear-pixmap-buffer window gc)
499 (setf (xlib:gcontext-foreground gc) (get-color *identify-foreground*))
500 (xlib:draw-glyphs *pixmap-buffer* gc 5 (+ (xlib:max-char-ascent font) 5)
501 (format nil "Press a key to identify. Press 'q' to stop the identify loop."))
502 (when code
503 (xlib:draw-glyphs *pixmap-buffer* gc 10 (+ (* 2 (+ (xlib:max-char-ascent font) (xlib:max-char-descent font))) 5)
504 (format nil "Code=~A KeySym=~S Key=~S Modifiers=~A"
505 code keysym key modifiers))
506 (print-doc "Main mode : " *main-keys* 3 code state)
507 (print-doc "Second mode: " *second-keys* 4 code state))
508 (copy-pixmap-buffer window gc))
509 (handle-identify-key (&rest event-slots &key root code state &allow-other-keys)
510 (declare (ignore event-slots root))
511 (let* ((modifiers (state->modifiers state))
512 (key (keycode->char code state))
513 (keysym (keysym->keysym-name (keycode->keysym code modifiers))))
514 (setf done (and (equal key #\q) (equal modifiers *default-modifiers*)))
515 (dbg code keysym key modifiers)
516 (print-key code state keysym key modifiers)
517 (force-output)))
518 (handle-identify (&rest event-slots &key display event-key &allow-other-keys)
519 (declare (ignore display))
520 (case event-key
521 (:key-press (apply #'handle-identify-key event-slots) t)
522 (:exposure (print-key nil nil nil nil nil)))
524 (xgrab-pointer *root* 92 93)
525 (map-window window)
526 (format t "~&Press 'q' to stop the identify loop~%")
527 (print-key nil nil nil nil nil)
528 (force-output)
529 (unwind-protect
530 (loop until done do
531 (when (xlib:event-listen *display* *loop-timeout*)
532 (xlib:process-event *display* :handler #'handle-identify))
533 (xlib:display-finish-output *display*))
534 (xlib:destroy-window window)
535 (xlib:close-font font)
536 (xgrab-pointer *root* 66 67)))))
543 (let ((all-symbols (collect-all-symbols)))
544 (defun eval-from-query-string ()
545 "Eval a lisp form from the query input"
546 (let ((form (query-string (format nil "Eval Lisp <~A> " (package-name *package*))
547 "" all-symbols))
548 (result nil))
549 (when (and form (not (equal form "")))
550 (let ((printed-result
551 (with-output-to-string (*standard-output*)
552 (setf result (handler-case
553 (loop for i in (multiple-value-list
554 (eval (read-from-string form)))
555 collect (format nil "~S" i))
556 (error (condition)
557 (format nil "~A" condition)))))))
558 (let ((ret (info-mode (expand-newline (append (ensure-list (format nil "> ~A" form))
559 (ensure-list printed-result)
560 (ensure-list result)))
561 :width (- (xlib:screen-width *screen*) 2))))
562 (when (or (search "defparameter" form :test #'string-equal)
563 (search "defvar" form :test #'string-equal))
564 (let ((elem (split-string form)))
565 (pushnew (string-downcase (if (string= (first elem) "(") (third elem) (second elem)))
566 all-symbols :test #'string=)))
567 (when (search "in-package" form :test #'string-equal)
568 (setf all-symbols (collect-all-symbols)))
569 (when ret
570 (eval-from-query-string))))))))
576 (let ((commands (command-in-path)))
577 (defun run-program-from-query-string ()
578 "Run a program from the query input"
579 (multiple-value-bind (program return)
580 (query-string "Run:" "" commands)
581 (when (and (equal return :return) program (not (equal program "")))
582 (setf *second-mode-leave-function* (let ((cmd (concatenate 'string "cd $HOME && " program)))
583 (lambda ()
584 (do-shell cmd))))
585 (leave-second-mode)))))
590 ;;; Frame name actions
591 (defun ask-frame-name (msg)
592 "Ask a frame name"
593 (let ((all-frame-name nil))
594 (with-all-frames (*root-frame* frame)
595 (awhen (frame-name frame) (push it all-frame-name)))
596 (query-string msg "" all-frame-name)))
599 ;;; Focus by functions
600 (defun focus-frame-by (frame)
601 (when (frame-p frame)
602 (focus-all-children frame (or (find-parent-frame frame (find-current-root))
603 (find-parent-frame frame)
604 *root-frame*))
605 (show-all-children t)))
608 (defun focus-frame-by-name ()
609 "Focus a frame by name"
610 (focus-frame-by (find-frame-by-name (ask-frame-name "Focus frame:")))
611 (leave-second-mode))
613 (defun focus-frame-by-number ()
614 "Focus a frame by number"
615 (focus-frame-by (find-frame-by-number (query-number "Focus frame by number:")))
616 (leave-second-mode))
619 ;;; Open by functions
620 (defun open-frame-by (frame)
621 (when (frame-p frame)
622 (push (create-frame :name (query-string "Frame name")) (frame-child frame))
623 (show-all-children)))
627 (defun open-frame-by-name ()
628 "Open a new frame in a named frame"
629 (open-frame-by (find-frame-by-name (ask-frame-name "Open a new frame in: ")))
630 (leave-second-mode))
632 (defun open-frame-by-number ()
633 "Open a new frame in a numbered frame"
634 (open-frame-by (find-frame-by-number (query-number "Open a new frame in the group numbered:")))
635 (leave-second-mode))
638 ;;; Delete by functions
639 (defun delete-frame-by (frame)
640 (unless (or (child-equal-p frame *root-frame*)
641 (child-root-p frame))
642 (when (child-equal-p frame (current-child))
643 (setf (current-child) (find-current-root)))
644 (remove-child-in-frame frame (find-parent-frame frame)))
645 (show-all-children t))
648 (defun delete-frame-by-name ()
649 "Delete a frame by name"
650 (delete-frame-by (find-frame-by-name (ask-frame-name "Delete frame: ")))
651 (leave-second-mode))
653 (defun delete-frame-by-number ()
654 "Delete a frame by number"
655 (delete-frame-by (find-frame-by-number (query-number "Delete frame by number:")))
656 (leave-second-mode))
659 ;;; Move by function
660 (defun move-child-to (child frame-dest)
661 (when (and child (frame-p frame-dest))
662 (remove-child-in-frame child (find-parent-frame child))
663 (pushnew child (frame-child frame-dest))
664 (focus-all-children child frame-dest)
665 (show-all-children t)))
667 (defun move-current-child-by-name ()
668 "Move current child in a named frame"
669 (move-child-to (current-child)
670 (find-frame-by-name
671 (ask-frame-name (format nil "Move '~A' to frame: " (child-name (current-child))))))
672 (leave-second-mode))
674 (defun move-current-child-by-number ()
675 "Move current child in a numbered frame"
676 (move-child-to (current-child)
677 (find-frame-by-number
678 (query-number (format nil "Move '~A' to frame numbered:" (child-name (current-child))))))
679 (leave-second-mode))
682 ;;; Copy by function
683 (defun copy-child-to (child frame-dest)
684 (when (and child (frame-p frame-dest))
685 (pushnew child (frame-child frame-dest))
686 (focus-all-children child frame-dest)
687 (show-all-children t)))
689 (defun copy-current-child-by-name ()
690 "Copy current child in a named frame"
691 (copy-child-to (current-child)
692 (find-frame-by-name
693 (ask-frame-name (format nil "Copy '~A' to frame: " (child-name (current-child))))))
694 (leave-second-mode))
696 (defun copy-current-child-by-number ()
697 "Copy current child in a numbered frame"
698 (copy-child-to (current-child)
699 (find-frame-by-number
700 (query-number (format nil "Copy '~A' to frame numbered:" (child-name (current-child))))))
701 (leave-second-mode))
706 ;;; Show frame info
707 (defun show-all-frames-info ()
708 "Show all frames info windows"
709 (let ((*show-root-frame-p* t))
710 (show-all-children)
711 (dolist (root (all-root-child))
712 (with-all-frames (root frame)
713 (raise-window (frame-window frame))
714 (display-frame-info frame)))))
716 (defun hide-all-frames-info ()
717 "Hide all frames info windows"
718 (show-all-children))
720 (defun show-all-frames-info-key ()
721 "Show all frames info windows until a key is release"
722 (show-all-frames-info)
723 (wait-no-key-or-button-press)
724 (hide-all-frames-info))
727 (defun move-frame (frame parent orig-x orig-y)
728 (when (and frame parent (not (child-root-p frame)))
729 (hide-all-children frame)
730 (with-slots (window) frame
731 (move-window window orig-x orig-y #'display-frame-info (list frame))
732 (setf (frame-x frame) (x-px->fl (x-drawable-x window) parent)
733 (frame-y frame) (y-px->fl (x-drawable-y window) parent)))
734 (show-all-children)))
736 (defun resize-frame (frame parent orig-x orig-y)
737 (when (and frame parent (not (child-root-p frame)))
738 (hide-all-children frame)
739 (with-slots (window) frame
740 (resize-window window orig-x orig-y #'display-frame-info (list frame))
741 (setf (frame-w frame) (w-px->fl (anti-adj-border-wh (x-drawable-width window) frame) parent)
742 (frame-h frame) (h-px->fl (anti-adj-border-wh (x-drawable-height window) frame) parent)))
743 (show-all-children)))
747 (defun mouse-click-to-focus-generic (root-x root-y mouse-fn)
748 "Focus the current frame or focus the current window parent
749 mouse-fun is #'move-frame or #'resize-frame"
750 (let* ((to-replay t)
751 (child (find-child-under-mouse root-x root-y))
752 (parent (find-parent-frame child))
753 (root-p (child-root-p child)))
754 (labels ((add-new-frame ()
755 (when (frame-p child)
756 (setf parent child
757 child (create-frame)
758 mouse-fn #'resize-frame
759 (current-child) child)
760 (place-frame child parent root-x root-y 10 10)
761 (map-window (frame-window child))
762 (pushnew child (frame-child parent)))))
763 (when (and root-p *create-frame-on-root*)
764 (add-new-frame))
765 (when (and (frame-p child) (not (child-root-p child)))
766 (funcall mouse-fn child parent root-x root-y))
767 (when (and child parent
768 (focus-all-children child parent (not (child-root-p child))))
769 (when (show-all-children)
770 (setf to-replay nil)))
771 (if to-replay
772 (replay-button-event)
773 (stop-button-event)))))
776 (defun mouse-click-to-focus-and-move (window root-x root-y)
777 "Move and focus the current frame or focus the current window parent.
778 Or do actions on corners"
779 (declare (ignore window))
780 (or (do-corner-action root-x root-y *corner-main-mode-left-button*)
781 (mouse-click-to-focus-generic root-x root-y #'move-frame)))
783 (defun mouse-click-to-focus-and-resize (window root-x root-y)
784 "Resize and focus the current frame or focus the current window parent.
785 Or do actions on corners"
786 (declare (ignore window))
787 (or (do-corner-action root-x root-y *corner-main-mode-right-button*)
788 (mouse-click-to-focus-generic root-x root-y #'resize-frame)))
790 (defun mouse-middle-click (window root-x root-y)
791 "Do actions on corners"
792 (declare (ignore window))
793 (or (do-corner-action root-x root-y *corner-main-mode-middle-button*)
794 (replay-button-event)))
799 (defun mouse-focus-move/resize-generic (root-x root-y mouse-fn window-parent)
800 "Focus the current frame or focus the current window parent
801 mouse-fun is #'move-frame or #'resize-frame.
802 Focus child and its parents -
803 For window: set current child to window or its parent according to window-parent"
804 (labels ((move/resize-managed (child)
805 (let ((parent (find-parent-frame child)))
806 (when (and child
807 (frame-p child)
808 (child-root-p child))
809 (setf parent child
810 child (create-frame)
811 mouse-fn #'resize-frame)
812 (place-frame child parent root-x root-y 10 10)
813 (map-window (frame-window child))
814 (push child (frame-child parent)))
815 (focus-all-children child parent window-parent)
816 (show-all-children)
817 (typecase child
818 (xlib:window
819 (if (managed-window-p child parent)
820 (funcall mouse-fn parent (find-parent-frame parent) root-x root-y)
821 (funcall (cond ((or (eql mouse-fn #'move-frame)
822 (eql mouse-fn #'move-frame-constrained))
823 #'move-window)
824 ((or (eql mouse-fn #'resize-frame)
825 (eql mouse-fn #'resize-frame-constrained))
826 #'resize-window))
827 child root-x root-y)))
828 (frame (funcall mouse-fn child parent root-x root-y)))
829 (show-all-children)))
830 (move/resize-never-managed (child raise-fun)
831 (funcall raise-fun child)
832 (funcall (cond ((eql mouse-fn #'move-frame) #'move-window)
833 ((eql mouse-fn #'resize-frame) #'resize-window))
834 child root-x root-y)))
835 (let ((child (find-child-under-mouse root-x root-y nil t)))
836 (multiple-value-bind (never-managed raise-fun)
837 (never-managed-window-p child)
838 (if (and (xlib:window-p child) never-managed raise-fun)
839 (move/resize-never-managed child raise-fun)
840 (move/resize-managed child))))))
843 (defun test-mouse-binding (window root-x root-y)
844 (dbg window root-x root-y)
845 (replay-button-event))
849 (defun mouse-select-next-level (window root-x root-y)
850 "Select the next level in frame"
851 (declare (ignore root-x root-y))
852 (let ((frame (find-frame-window window)))
853 (when (or frame (xlib:window-equal window *root*))
854 (select-next-level))
855 (replay-button-event)))
859 (defun mouse-select-previous-level (window root-x root-y)
860 "Select the previous level in frame"
861 (declare (ignore root-x root-y))
862 (let ((frame (find-frame-window window)))
863 (when (or frame (xlib:window-equal window *root*))
864 (select-previous-level))
865 (replay-button-event)))
869 (defun mouse-enter-frame (window root-x root-y)
870 "Enter in the selected frame - ie make it the root frame"
871 (declare (ignore root-x root-y))
872 (let ((frame (find-frame-window window)))
873 (when (or frame (xlib:window-equal window *root*))
874 (enter-frame))
875 (replay-button-event)))
879 (defun mouse-leave-frame (window root-x root-y)
880 "Leave the selected frame - ie make its parent the root frame"
881 (declare (ignore root-x root-y))
882 (let ((frame (find-frame-window window)))
883 (when (or frame (xlib:window-equal window *root*))
884 (leave-frame))
885 (replay-button-event)))
889 ;;;;;,-----
890 ;;;;;| Various definitions
891 ;;;;;`-----
893 (defun show-help (&optional (browser "dillo") (tempfile "/tmp/clfswm.html"))
894 "Show current keys and buttons bindings"
895 (ignore-errors
896 (produce-doc-html-in-file tempfile))
897 (sleep 1)
898 (do-shell (format nil "~A ~A" browser tempfile)))
902 ;;; Bind or jump functions
903 (let ((key-slots (make-array 10 :initial-element nil))
904 (current-slot 1))
905 (defun reset-bind-or-jump-slots ()
906 (dotimes (i 10)
907 (setf (aref key-slots i) nil)))
909 (defun bind-on-slot (&optional (slot current-slot))
910 "Bind current child to slot"
911 (setf (aref key-slots slot) (current-child)))
913 (defun remove-binding-on-slot ()
914 "Remove binding on slot"
915 (setf (aref key-slots current-slot) nil))
917 (defun jump-to-slot ()
918 "Jump to slot"
919 (let ((jump-child (aref key-slots current-slot)))
920 (when (and jump-child (find-child jump-child *root-frame*))
921 (unless (find-child-in-all-root jump-child)
922 (change-root (find-root jump-child) jump-child))
923 (setf (current-child) jump-child)
924 (focus-all-children jump-child jump-child)
925 (show-all-children t))))
927 (defun bind-or-jump (n)
928 "Bind or jump to a slot (a frame or a window)"
929 (setf current-slot (- n 1))
930 (let ((default-bind `("b" bind-on-slot
931 ,(format nil "Bind slot ~A on child: ~A" n (child-fullname (current-child))))))
932 (info-mode-menu (aif (aref key-slots current-slot)
933 `(,default-bind
934 ("BackSpace" remove-binding-on-slot
935 ,(format nil "Remove slot ~A binding on child: ~A" n (child-fullname (current-child))))
936 (" - " nil " -")
937 ("Tab" jump-to-slot
938 ,(format nil "Jump to child: ~A" (aif (aref key-slots current-slot)
939 (child-fullname it)
940 "Not set - Please, bind it with 'b'")))
941 ("Return" jump-to-slot "Same thing")
942 ("space" jump-to-slot "Same thing"))
943 (list default-bind))))))
947 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
948 ;;; Useful function for the second mode ;;;
949 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
950 (defmacro with-movement (&body body)
951 `(when (frame-p (current-child))
952 ,@body
953 (show-all-children)
954 (display-all-frame-info)
955 (draw-second-mode-window)
956 (open-menu (find-menu 'frame-movement-menu))))
959 ;;; Pack
960 (defun current-frame-pack-up ()
961 "Pack the current frame up"
962 (with-movement (pack-frame-up (current-child) (find-parent-frame (current-child) (find-current-root)))))
964 (defun current-frame-pack-down ()
965 "Pack the current frame down"
966 (with-movement (pack-frame-down (current-child) (find-parent-frame (current-child) (find-current-root)))))
968 (defun current-frame-pack-left ()
969 "Pack the current frame left"
970 (with-movement (pack-frame-left (current-child) (find-parent-frame (current-child) (find-current-root)))))
972 (defun current-frame-pack-right ()
973 "Pack the current frame right"
974 (with-movement (pack-frame-right (current-child) (find-parent-frame (current-child) (find-current-root)))))
976 ;;; Center
977 (defun center-current-frame ()
978 "Center the current frame"
979 (with-movement (center-frame (current-child))))
981 ;;; Fill
982 (defun current-frame-fill-up ()
983 "Fill the current frame up"
984 (with-movement (fill-frame-up (current-child) (find-parent-frame (current-child) (find-current-root)))))
986 (defun current-frame-fill-down ()
987 "Fill the current frame down"
988 (with-movement (fill-frame-down (current-child) (find-parent-frame (current-child) (find-current-root)))))
990 (defun current-frame-fill-left ()
991 "Fill the current frame left"
992 (with-movement (fill-frame-left (current-child) (find-parent-frame (current-child) (find-current-root)))))
994 (defun current-frame-fill-right ()
995 "Fill the current frame right"
996 (with-movement (fill-frame-right (current-child) (find-parent-frame (current-child) (find-current-root)))))
998 (defun current-frame-fill-all-dir ()
999 "Fill the current frame in all directions"
1000 (with-movement
1001 (let ((parent (find-parent-frame (current-child) (find-current-root))))
1002 (fill-frame-up (current-child) parent)
1003 (fill-frame-down (current-child) parent)
1004 (fill-frame-left (current-child) parent)
1005 (fill-frame-right (current-child) parent))))
1007 (defun current-frame-fill-vertical ()
1008 "Fill the current frame vertically"
1009 (with-movement
1010 (let ((parent (find-parent-frame (current-child) (find-current-root))))
1011 (fill-frame-up (current-child) parent)
1012 (fill-frame-down (current-child) parent))))
1014 (defun current-frame-fill-horizontal ()
1015 "Fill the current frame horizontally"
1016 (with-movement
1017 (let ((parent (find-parent-frame (current-child) (find-current-root))))
1018 (fill-frame-left (current-child) parent)
1019 (fill-frame-right (current-child) parent))))
1022 ;;; Resize
1023 (defun current-frame-resize-up ()
1024 "Resize the current frame up to its half height"
1025 (with-movement (resize-half-height-up (current-child))))
1027 (defun current-frame-resize-down ()
1028 "Resize the current frame down to its half height"
1029 (with-movement (resize-half-height-down (current-child))))
1031 (defun current-frame-resize-left ()
1032 "Resize the current frame left to its half width"
1033 (with-movement (resize-half-width-left (current-child))))
1035 (defun current-frame-resize-right ()
1036 "Resize the current frame right to its half width"
1037 (with-movement (resize-half-width-right (current-child))))
1039 (defun current-frame-resize-all-dir ()
1040 "Resize down the current frame"
1041 (with-movement (resize-frame-down (current-child))))
1043 (defun current-frame-resize-all-dir-minimal ()
1044 "Resize down the current frame to its minimal size"
1045 (with-movement (resize-minimal-frame (current-child))))
1048 ;;; Children navigation
1049 (defun with-movement-select-next-brother ()
1050 "Select the next brother frame"
1051 (with-movement (select-next-brother-simple)))
1053 (defun with-movement-select-previous-brother ()
1054 "Select the previous brother frame"
1055 (with-movement (select-previous-brother-simple)))
1057 (defun with-movement-select-next-level ()
1058 "Select the next level"
1059 (with-movement (select-next-level)))
1061 (defun with-movement-select-previous-level ()
1062 "Select the previous levelframe"
1063 (with-movement (select-previous-level)))
1065 (defun with-movement-select-next-child ()
1066 "Select the next child"
1067 (with-movement (select-next-child-simple)))
1071 ;;; Adapt frame functions
1072 (defun adapt-current-frame-to-window-hints-generic (width-p height-p)
1073 "Adapt the current frame to the current window minimal size hints"
1074 (when (frame-p (current-child))
1075 (let ((window (first (frame-child (current-child)))))
1076 (when (xlib:window-p window)
1077 (let* ((hints (xlib:wm-normal-hints window))
1078 (min-width (and hints (xlib:wm-size-hints-min-width hints)))
1079 (min-height (and hints (xlib:wm-size-hints-min-height hints))))
1080 (when (and width-p min-width)
1081 (setf (frame-rw (current-child)) min-width))
1082 (when (and height-p min-height)
1083 (setf (frame-rh (current-child)) min-height))
1084 (fixe-real-size (current-child) (find-parent-frame (current-child)))
1085 (leave-second-mode))))))
1087 (defun adapt-current-frame-to-window-hints ()
1088 "Adapt the current frame to the current window minimal size hints"
1089 (adapt-current-frame-to-window-hints-generic t t))
1091 (defun adapt-current-frame-to-window-width-hint ()
1092 "Adapt the current frame to the current window minimal width hint"
1093 (adapt-current-frame-to-window-hints-generic t nil))
1095 (defun adapt-current-frame-to-window-height-hint ()
1096 "Adapt the current frame to the current window minimal height hint"
1097 (adapt-current-frame-to-window-hints-generic nil t))
1102 ;;; Managed window type functions
1103 (defun current-frame-manage-window-type-generic (type-list)
1104 (when (frame-p (current-child))
1105 (setf (frame-managed-type (current-child)) type-list
1106 (frame-forced-managed-window (current-child)) nil
1107 (frame-forced-unmanaged-window (current-child)) nil))
1108 (leave-second-mode))
1111 (defun current-frame-manage-window-type ()
1112 "Change window types to be managed by a frame"
1113 (when (frame-p (current-child))
1114 (let* ((type-str (query-string "Managed window type: (all, normal, transient, maxsize, desktop, dock, toolbar, menu, utility, splash, dialog)"
1115 (format nil "~{~:(~A~) ~}" (frame-managed-type (current-child)))))
1116 (type-list (loop :for type :in (split-string type-str)
1117 :collect (intern (string-upcase type) :keyword))))
1118 (current-frame-manage-window-type-generic type-list))))
1121 (defun current-frame-manage-all-window-type ()
1122 "Manage all window type"
1123 (current-frame-manage-window-type-generic '(:all)))
1125 (defun current-frame-manage-only-normal-window-type ()
1126 "Manage only normal window type"
1127 (current-frame-manage-window-type-generic '(:normal)))
1129 (defun current-frame-manage-no-window-type ()
1130 "Do not manage any window type"
1131 (current-frame-manage-window-type-generic nil))
1140 ;;; Force window functions
1141 (defun force-window-in-frame ()
1142 "Force the current window to move in the frame (Useful only for unmanaged windows)"
1143 (with-current-window
1144 (let ((parent (find-parent-frame window)))
1145 (setf (x-drawable-x window) (frame-rx parent)
1146 (x-drawable-y window) (frame-ry parent))
1147 (xlib:display-finish-output *display*)))
1148 (leave-second-mode))
1151 (defun force-window-center-in-frame ()
1152 "Force the current window to move in the center of the frame (Useful only for unmanaged windows)"
1153 (with-current-window
1154 (let ((parent (find-parent-frame window)))
1155 (setf (x-drawable-x window) (truncate (+ (frame-rx parent)
1156 (/ (- (frame-rw parent)
1157 (x-drawable-width window)) 2)))
1158 (x-drawable-y window) (truncate (+ (frame-ry parent)
1159 (/ (- (frame-rh parent)
1160 (x-drawable-height window)) 2))))
1161 (xlib:display-finish-output *display*)))
1162 (leave-second-mode))
1166 (defun display-current-window-info ()
1167 "Display information on the current window"
1168 (with-current-window
1169 (info-mode (list (format nil "Window: ~A" window)
1170 (format nil "Window name: ~A" (xlib:wm-name window))
1171 (format nil "Window class: ~A" (xlib:get-wm-class window))
1172 (format nil "Window type: ~:(~A~)" (window-type window))
1173 (format nil "Window id: 0x~X" (xlib:window-id window))
1174 (format nil "Window transparency: ~A" (* 100 (window-transparency window))))))
1175 (leave-second-mode))
1177 (defun set-current-window-transparency ()
1178 "Set the current window transparency"
1179 (with-current-window
1180 (ask-child-transparency "window" window))
1181 (leave-second-mode))
1184 (defun manage-current-window ()
1185 "Force to manage the current window by its parent frame"
1186 (with-current-window
1187 (let ((parent (find-parent-frame window)))
1188 (with-slots ((managed forced-managed-window)
1189 (unmanaged forced-unmanaged-window)) parent
1190 (setf unmanaged (child-remove window unmanaged)
1191 unmanaged (remove (xlib:wm-name window) unmanaged :test #'string-equal-p))
1192 (pushnew window managed))))
1193 (leave-second-mode))
1195 (defun unmanage-current-window ()
1196 "Force to not manage the current window by its parent frame"
1197 (with-current-window
1198 (let ((parent (find-parent-frame window)))
1199 (with-slots ((managed forced-managed-window)
1200 (unmanaged forced-unmanaged-window)) parent
1201 (setf managed (child-remove window managed)
1202 managed (remove (xlib:wm-name window) managed :test #'string-equal-p))
1203 (pushnew window unmanaged))))
1204 (leave-second-mode))
1208 ;;; Moving child with the mouse button
1209 (defun mouse-move-child-over-frame (window root-x root-y)
1210 "Move the child under the mouse cursor to another frame"
1211 (declare (ignore window))
1212 (let ((child (find-child-under-mouse root-x root-y)))
1213 (unless (child-root-p child)
1214 (hide-all child)
1215 (remove-child-in-frame child (find-parent-frame child))
1216 (wait-mouse-button-release 50 51)
1217 (multiple-value-bind (x y)
1218 (xlib:query-pointer *root*)
1219 (let ((dest (find-child-under-mouse x y)))
1220 (when (xlib:window-p dest)
1221 (setf dest (find-parent-frame dest)))
1222 (unless (child-equal-p child dest)
1223 (move-child-to child dest)
1224 (show-all-children))))))
1225 (stop-button-event))
1230 ;;; Hide/Show frame window functions
1231 (defun hide/show-frame-window (frame value)
1232 "Hide/show the frame window"
1233 (when (frame-p frame)
1234 (setf (frame-show-window-p (current-child)) value)
1235 (show-all-children))
1236 (leave-second-mode))
1239 (defun hide-current-frame-window ()
1240 "Hide the current frame window"
1241 (hide/show-frame-window (current-child) nil))
1243 (defun show-current-frame-window ()
1244 "Show the current frame window"
1245 (hide/show-frame-window (current-child) t))
1249 ;;; Hide/Unhide current child
1250 (defun hide-current-child ()
1251 "Hide the current child"
1252 (unless (child-root-p (current-child))
1253 (let ((parent (find-parent-frame (current-child))))
1254 (when (frame-p parent)
1255 (with-slots (child hidden-children) parent
1256 (hide-all (current-child))
1257 (setf child (child-remove (current-child) child))
1258 (pushnew (current-child) hidden-children)
1259 (setf (current-child) parent))
1260 (show-all-children)))
1261 (leave-second-mode)))
1264 (defun frame-unhide-child (hidden frame-src frame-dest)
1265 "Unhide a hidden child from frame-src in frame-dest"
1266 (with-slots (hidden-children) frame-src
1267 (setf hidden-children (child-remove hidden hidden-children)))
1268 (with-slots (child) frame-dest
1269 (pushnew hidden child)))
1273 (defun unhide-a-child ()
1274 "Unhide a child in the current frame"
1275 (when (frame-p (current-child))
1276 (with-slots (child hidden-children) (current-child)
1277 (info-mode-menu (loop :for i :from 0
1278 :for hidden :in hidden-children
1279 :collect (list (code-char (+ (char-code #\a) i))
1280 (let ((lhd hidden))
1281 (lambda ()
1282 (frame-unhide-child lhd (current-child) (current-child))))
1283 (format nil "Unhide ~A" (child-fullname hidden))))))
1284 (show-all-children))
1285 (leave-second-mode))
1288 (defun unhide-all-children ()
1289 "Unhide all current frame hidden children"
1290 (when (frame-p (current-child))
1291 (with-slots (child hidden-children) (current-child)
1292 (dolist (c hidden-children)
1293 (pushnew c child))
1294 (setf hidden-children nil))
1295 (show-all-children))
1296 (leave-second-mode))
1299 (defun unhide-a-child-from-all-frames ()
1300 "Unhide a child from all frames in the current frame"
1301 (when (frame-p (current-child))
1302 (let ((acc nil)
1303 (keynum -1))
1304 (with-all-frames (*root-frame* frame)
1305 (when (frame-hidden-children frame)
1306 (push (format nil "~A" (child-fullname frame)) acc)
1307 (dolist (hidden (frame-hidden-children frame))
1308 (push (list (code-char (+ (char-code #\a) (incf keynum)))
1309 (let ((lhd hidden))
1310 (lambda ()
1311 (frame-unhide-child lhd frame (current-child))))
1312 (format nil "Unhide ~A" (child-fullname hidden)))
1313 acc))))
1314 (info-mode-menu (nreverse acc)))
1315 (show-all-children))
1316 (leave-second-mode))
1322 (let ((last-child nil))
1323 (defun init-last-child ()
1324 (setf last-child nil))
1325 (defun switch-to-last-child ()
1326 "Store the current child and switch to the previous one"
1327 (let ((current-child (current-child)))
1328 (when last-child
1329 (change-root (find-root last-child) last-child)
1330 (setf (current-child) last-child)
1331 (focus-all-children (current-child) (current-child))
1332 (show-all-children t))
1333 (setf last-child current-child))
1334 (leave-second-mode)))
1342 ;;; Focus policy functions
1343 (defun set-focus-policy-generic (focus-policy)
1344 (when (frame-p (current-child))
1345 (setf (frame-focus-policy (current-child)) focus-policy))
1346 (leave-second-mode))
1349 (defun current-frame-set-click-focus-policy ()
1350 "Set a click focus policy for the current frame."
1351 (set-focus-policy-generic :click))
1353 (defun current-frame-set-sloppy-focus-policy ()
1354 "Set a sloppy focus policy for the current frame."
1355 (set-focus-policy-generic :sloppy))
1357 (defun current-frame-set-sloppy-strict-focus-policy ()
1358 "Set a (strict) sloppy focus policy only for windows in the current frame."
1359 (set-focus-policy-generic :sloppy-strict))
1361 (defun current-frame-set-sloppy-select-policy ()
1362 "Set a sloppy select policy for the current frame."
1363 (set-focus-policy-generic :sloppy-select))
1367 (defun set-focus-policy-generic-for-all (focus-policy)
1368 (with-all-frames (*root-frame* frame)
1369 (setf (frame-focus-policy frame) focus-policy))
1370 (leave-second-mode))
1373 (defun all-frames-set-click-focus-policy ()
1374 "Set a click focus policy for all frames."
1375 (set-focus-policy-generic-for-all :click))
1377 (defun all-frames-set-sloppy-focus-policy ()
1378 "Set a sloppy focus policy for all frames."
1379 (set-focus-policy-generic-for-all :sloppy))
1381 (defun all-frames-set-sloppy-strict-focus-policy ()
1382 "Set a (strict) sloppy focus policy for all frames."
1383 (set-focus-policy-generic-for-all :sloppy-strict))
1385 (defun all-frames-set-sloppy-select-policy ()
1386 "Set a sloppy select policy for all frames."
1387 (set-focus-policy-generic-for-all :sloppy-select))
1391 ;;; Ensure unique name/number functions
1392 (defun extract-number-from-name (name)
1393 (when (stringp name)
1394 (let* ((pos (1+ (or (position #\. name :from-end t) -1)))
1395 (number (parse-integer name :junk-allowed t :start pos)))
1396 (values number
1397 (if number (subseq name 0 (1- pos)) name)))))
1402 (defun ensure-unique-name ()
1403 "Ensure that all children names are unique"
1404 (with-all-children (*root-frame* child)
1405 (multiple-value-bind (num1 name1)
1406 (extract-number-from-name (child-name child))
1407 (declare (ignore num1))
1408 (when name1
1409 (let ((acc nil))
1410 (with-all-children (*root-frame* c)
1411 (unless (child-equal-p child c))
1412 (multiple-value-bind (num2 name2)
1413 (extract-number-from-name (child-name c))
1414 (when (string-equal name1 name2)
1415 (push num2 acc))))
1416 (dbg acc)
1417 (when (> (length acc) 1)
1418 (setf (child-name child)
1419 (format nil "~A.~A" name1
1420 (1+ (find-free-number (loop for i in acc when i collect (1- i)))))))))))
1421 (leave-second-mode))
1423 (defun ensure-unique-number ()
1424 "Ensure that all children numbers are unique"
1425 (let ((num -1))
1426 (with-all-frames (*root-frame* frame)
1427 (setf (frame-number frame) (incf num))))
1428 (leave-second-mode))
1432 ;;; Standard menu functions - Based on the XDG specifications
1433 (defun um-create-xdg-section-list (menu)
1434 (dolist (section *xdg-section-list*)
1435 (add-sub-menu menu :next section (format nil "~A" section) menu))
1436 (unless (find-toplevel-menu 'Utility menu)
1437 (add-sub-menu menu :next 'Utility (format nil "~A" 'Utility) menu)))
1439 (defun um-find-submenu (menu section-list)
1440 (let ((acc nil))
1441 (dolist (section section-list)
1442 (awhen (find-toplevel-menu (intern (string-upcase section) :clfswm) menu)
1443 (push it acc)))
1444 (if acc
1446 (list (find-toplevel-menu 'Utility menu)))))
1449 (defun um-extract-value (line)
1450 (second (split-string line #\=)))
1453 (defun um-add-desktop (desktop menu)
1454 (let (name exec categories comment)
1455 (when (probe-file desktop)
1456 (with-open-file (stream desktop :direction :input)
1457 (loop for line = (read-line stream nil nil)
1458 while line
1460 (cond ((first-position "Name=" line) (setf name (um-extract-value line)))
1461 ((first-position "Exec=" line) (setf exec (um-extract-value line)))
1462 ((first-position "Categories=" line) (setf categories (um-extract-value line)))
1463 ((first-position "Comment=" line) (setf comment (um-extract-value line))))
1464 (when (and name exec categories)
1465 (let* ((sub-menu (um-find-submenu menu (split-string categories #\;)))
1466 (fun-name (intern name :clfswm)))
1467 (setf (symbol-function fun-name) (let ((do-exec exec))
1468 (lambda ()
1469 (do-shell do-exec)
1470 (leave-second-mode)))
1471 (documentation fun-name 'function) (format nil "~A~A" name (if comment
1472 (format nil " - ~A" comment)
1473 "")))
1474 (dolist (m sub-menu)
1475 (add-menu-key (menu-name m) :next fun-name m)))
1476 (setf name nil exec nil categories nil comment nil)))))))
1479 (defun update-menus (&optional (menu (make-menu :name 'main :doc "Main menu")))
1480 (um-create-xdg-section-list menu)
1481 (let ((count 0)
1482 (found (make-hash-table :test #'equal)))
1483 (dolist (dir (remove-duplicates
1484 (split-string (or (getenv "XDG_DATA_DIRS") "/usr/local/share/:/usr/share/")
1485 #\:) :test #'string-equal))
1486 (dolist (desktop (directory (concatenate 'string dir "/applications/**/*.desktop")))
1487 (unless (gethash (file-namestring desktop) found)
1488 (setf (gethash (file-namestring desktop) found) t)
1489 (um-add-desktop desktop menu)
1490 (incf count))))
1491 menu))
1495 ;;; Close/Kill focused window
1497 (defun ask-close/kill-current-window ()
1498 "Close or kill the current window (ask before doing anything)"
1499 (let ((window (xlib:input-focus *display*))
1500 (*info-mode-placement* *ask-close/kill-placement*))
1501 (info-mode-menu
1502 (if (and window (not (xlib:window-equal window *no-focus-window*)))
1503 `(,(format nil "Focus window: ~A" (xlib:wm-name window))
1504 (#\s delete-focus-window "Close the focus window")
1505 (#\k destroy-focus-window "Kill the focus window")
1506 (#\x cut-focus-window)
1507 (#\c copy-focus-window)
1508 (#\v paste-selection))
1509 `(,(format nil "Focus window: None")
1510 (#\v paste-selection))))
1515 ;;; Other window manager functions
1516 (defun get-proc-list ()
1517 (let ((proc (do-shell "ps x -o pid=" nil t))
1518 (proc-list nil))
1519 (loop for line = (read-line proc nil nil)
1520 while line
1521 do (push line proc-list))
1522 (dbg proc-list)
1523 proc-list))
1525 (defun run-other-window-manager ()
1526 (let ((proc-start (get-proc-list)))
1527 (do-shell *other-window-manager* nil t :terminal)
1528 (let* ((proc-end (get-proc-list))
1529 (proc-diff (set-difference proc-end proc-start :test #'equal)))
1530 (dbg 'killing-sigterm proc-diff)
1531 (do-shell (format nil "kill ~{ ~A ~} 2> /dev/null" proc-diff) nil t :terminal)
1532 (dbg 'killing-sigkill proc-diff)
1533 (do-shell (format nil "kill -9 ~{ ~A ~} 2> /dev/null" proc-diff) nil t :terminal)
1534 (sleep 1))
1535 (setf *other-window-manager* nil)))
1538 (defun do-run-other-window-manager (window-manager)
1539 (setf *other-window-manager* window-manager)
1540 (throw 'exit-main-loop nil))
1542 (defmacro def-run-other-window-manager (name &optional definition)
1543 (let ((definition (or definition name)))
1544 `(defun ,(create-symbol "run-" name) ()
1545 ,(format nil "Run ~A" definition)
1546 (do-run-other-window-manager ,(format nil "~A" name)))))
1548 (def-run-other-window-manager "xterm")
1549 (def-run-other-window-manager "icewm")
1550 (def-run-other-window-manager "twm")
1551 (def-run-other-window-manager "gnome-session" "Gnome")
1552 (def-run-other-window-manager "startkde" "KDE")
1553 (def-run-other-window-manager "xfce4-session" "XFCE")
1555 (defun run-lxde ()
1556 "Run LXDE"
1557 (do-run-other-window-manager "( lxsession & ); xterm -e \"echo ' /----------------------------------\\' ; echo ' | CLFSWM Note: |' ; echo ' | Close this window when done. |' ; echo ' \\----------------------------------/'; echo; echo; $SHELL\""))
1559 (defun run-xfce4 ()
1560 "Run LXDE (xterm)"
1561 (do-run-other-window-manager "( xfce4-session &) ; xterm -e \"echo ' /----------------------------------\\' ; echo ' | CLFSWM Note: |' ; echo ' | Close this window when done. |' ; echo ' \\----------------------------------/'; echo; echo; $SHELL\""))
1564 (defun run-prompt-wm ()
1565 "Prompt for an other window manager"
1566 (let ((wm (query-string "Run an other window manager:" "icewm")))
1567 (do-run-other-window-manager wm)))
1570 ;;; Hide or show unmanaged windows utility.
1571 (defun set-hide-unmanaged-window ()
1572 "Hide unmanaged windows when frame is not selected"
1573 (when (frame-p (current-child))
1574 (setf (frame-data-slot (current-child) :unmanaged-window-action) :hide)
1575 (leave-second-mode)))
1577 (defun set-show-unmanaged-window ()
1578 "Show unmanaged windows when frame is not selected"
1579 (when (frame-p (current-child))
1580 (setf (frame-data-slot (current-child) :unmanaged-window-action) :show)
1581 (leave-second-mode)))
1583 (defun set-default-hide-unmanaged-window ()
1584 "Set default behaviour to hide or not unmanaged windows when frame is not selected"
1585 (when (frame-p (current-child))
1586 (setf (frame-data-slot (current-child) :unmanaged-window-action) nil)
1587 (leave-second-mode)))
1589 (defun set-globally-hide-unmanaged-window ()
1590 "Hide unmanaged windows by default. This is overriden by functions above"
1591 (setf *hide-unmanaged-window* t)
1592 (leave-second-mode))
1594 (defun set-globally-show-unmanaged-window ()
1595 "Show unmanaged windows by default. This is overriden by functions above"
1596 (setf *hide-unmanaged-window* nil)
1597 (leave-second-mode))
1600 ;;; Speed mouse movement.
1601 (let (minx miny maxx maxy history lx ly)
1602 (labels ((middle (x1 x2)
1603 (round (/ (+ x1 x2) 2)))
1604 (reset-if-moved (x y)
1605 (when (or (/= x (or lx x)) (/= y (or ly y)))
1606 (speed-mouse-reset)))
1607 (add-in-history (x y)
1608 (push (list x y) history)))
1609 (defun speed-mouse-reset ()
1610 "Reset speed mouse coordinates"
1611 (setf minx nil miny nil maxx nil maxy nil history nil lx nil ly nil))
1612 (defun speed-mouse-left ()
1613 "Speed move mouse to left"
1614 (with-x-pointer
1615 (reset-if-moved x y)
1616 (setf maxx x)
1617 (add-in-history x y)
1618 (setf lx (middle (or minx 0) maxx))
1619 (xlib:warp-pointer *root* lx y)))
1620 (defun speed-mouse-right ()
1621 "Speed move mouse to right"
1622 (with-x-pointer
1623 (reset-if-moved x y)
1624 (setf minx x)
1625 (add-in-history x y)
1626 (setf lx (middle minx (or maxx (xlib:screen-width *screen*))))
1627 (xlib:warp-pointer *root* lx y)))
1628 (defun speed-mouse-up ()
1629 "Speed move mouse to up"
1630 (with-x-pointer
1631 (reset-if-moved x y)
1632 (setf maxy y)
1633 (add-in-history x y)
1634 (setf ly (middle (or miny 0) maxy))
1635 (xlib:warp-pointer *root* x ly)))
1636 (defun speed-mouse-down ()
1637 "Speed move mouse to down"
1638 (with-x-pointer
1639 (reset-if-moved x y)
1640 (setf miny y)
1641 (add-in-history x y)
1642 (setf ly (middle miny (or maxy (xlib:screen-height *screen*))))
1643 (xlib:warp-pointer *root* x ly)))
1644 (defun speed-mouse-undo ()
1645 "Undo last speed mouse move"
1646 (when history
1647 (let ((h (pop history)))
1648 (when h
1649 (destructuring-bind (bx by) h
1650 (setf lx bx ly by
1651 minx nil maxx nil
1652 miny nil maxy nil)
1653 (xlib:warp-pointer *root* lx ly))))))
1654 (defun speed-mouse-first-history ()
1655 "Revert to the first speed move mouse"
1656 (when history
1657 (let ((h (first (last history))))
1658 (when h
1659 (setf lx (first h)
1660 ly (second h))
1661 (xlib:warp-pointer *root* lx ly)))))))
1665 ;;; Notify window functions
1666 (let (font
1667 window
1669 width height
1670 text
1671 current-child)
1672 (labels ((text-string (tx)
1673 (typecase tx
1674 (cons (first tx))
1675 (t tx)))
1676 (text-color (tx)
1677 (get-color (typecase tx
1678 (cons (second tx))
1679 (t *notify-window-foreground*)))))
1680 (defun is-notify-window-p (win)
1681 (when (and (xlib:window-p win) (xlib:window-p window))
1682 (xlib:window-equal win window)))
1684 (defun refresh-notify-window ()
1685 (add-timer 0.1 #'refresh-notify-window :refresh-notify-window)
1686 (raise-window window)
1687 (let ((text-height (- (xlib:font-ascent font) (xlib:font-descent font))))
1688 (loop for tx in text
1689 for i from 1 do
1690 (setf (xlib:gcontext-foreground gc) (text-color tx))
1691 (xlib:draw-glyphs window gc
1692 (truncate (/ (- width (* (xlib:max-char-width font) (length (text-string tx)))) 2))
1693 (* text-height i 2)
1694 (text-string tx)))))
1696 (defun close-notify-window ()
1697 (erase-timer :refresh-notify-window)
1698 (setf *never-managed-window-list*
1699 (remove (list #'is-notify-window-p 'raise-window)
1700 *never-managed-window-list* :test #'equal))
1701 (when gc
1702 (xlib:free-gcontext gc))
1703 (when window
1704 (xlib:destroy-window window))
1705 (when font
1706 (xlib:close-font font))
1707 (xlib:display-finish-output *display*)
1708 (setf window nil
1709 gc nil
1710 font nil))
1712 (defun open-notify-window (text-list)
1713 (close-notify-window)
1714 (setf font (xlib:open-font *display* *notify-window-font-string*))
1715 (let ((text-height (- (xlib:font-ascent font) (xlib:font-descent font))))
1716 (setf text text-list)
1717 (setf width (* (xlib:max-char-width font) (+ (loop for tx in text-list
1718 maximize (length (text-string tx))) 2))
1719 height (+ (* text-height (length text-list) 2) text-height))
1720 (with-placement (*notify-window-placement* x y width height)
1721 (setf window (xlib:create-window :parent *root*
1722 :x x
1723 :y y
1724 :width width
1725 :height height
1726 :background (get-color *notify-window-background*)
1727 :border-width *border-size*
1728 :border (get-color *notify-window-border*)
1729 :colormap (xlib:screen-default-colormap *screen*)
1730 :event-mask '(:exposure :key-press))
1731 gc (xlib:create-gcontext :drawable window
1732 :foreground (get-color *notify-window-foreground*)
1733 :background (get-color *notify-window-background*)
1734 :font font
1735 :line-style :solid))
1736 (setf (window-transparency window) *notify-window-transparency*)
1737 (when (frame-p (current-child))
1738 (setf current-child (current-child)))
1739 (push (list #'is-notify-window-p 'raise-window) *never-managed-window-list*)
1740 (map-window window)
1741 (refresh-notify-window)
1742 (xlib:display-finish-output *display*))))))
1744 (defun notify-message (delay &rest messages)
1745 (erase-timer :close-notify-window)
1746 (funcall #'open-notify-window messages)
1747 (add-timer delay #'close-notify-window :close-notify-window))
1750 (defun display-hello-window ()
1751 (notify-message *notify-window-delay*
1752 '("Welcome to CLFSWM" "yellow")
1753 "Press Alt+F1 for help"))
1756 ;;; Run or raise functions
1757 (defun run-or-raise (raisep run-fn &key (maximized nil))
1758 (let ((window (with-all-windows (*root-frame* win)
1759 (when (funcall raisep win)
1760 (return win)))))
1761 (if window
1762 (let ((parent (find-parent-frame window)))
1763 (setf (current-child) parent)
1764 (put-child-on-top window parent)
1765 (when maximized
1766 (change-root (find-root parent) parent))
1767 (focus-all-children window parent)
1768 (show-all-children t))
1769 (funcall run-fn))))
1771 ;;; Transparency setting
1772 (defun inc-transparency (window root-x root-y)
1773 "Increment the child under mouse transparency"
1774 (declare (ignore root-x root-y))
1775 (unless *in-second-mode* (stop-button-event))
1776 (incf (child-transparency window) 0.1))
1778 (defun dec-transparency (window root-x root-y)
1779 "Decrement the child under mouse transparency"
1780 (declare (ignore root-x root-y))
1781 (unless *in-second-mode* (stop-button-event))
1782 (decf (child-transparency window) 0.1))
1784 (defun inc-transparency-slow (window root-x root-y)
1785 "Increment slowly the child under mouse transparency"
1786 (declare (ignore root-x root-y))
1787 (unless *in-second-mode* (stop-button-event))
1788 (incf (child-transparency window) 0.01))
1790 (defun dec-transparency-slow (window root-x root-y)
1791 "Decrement slowly the child under mouse transparency"
1792 (declare (ignore root-x root-y))
1793 (unless *in-second-mode* (stop-button-event))
1794 (decf (child-transparency window) 0.01))
1797 (defun key-inc-transparency ()
1798 "Increment the current window transparency"
1799 (with-current-window
1800 (incf (child-transparency window) 0.1)))
1802 (defun key-dec-transparency ()
1803 "Decrement the current window transparency"
1804 (with-current-window
1805 (decf (child-transparency window) 0.1)))
1811 ;;; Geometry change functions
1812 (defun swap-frame-geometry ()
1813 "Swap current brother frame geometry"
1814 (when (frame-p (current-child))
1815 (let ((parent (find-parent-frame (current-child))))
1816 (when (frame-p parent)
1817 (let ((brother (second (frame-child parent))))
1818 (when (frame-p brother)
1819 (rotatef (frame-x (current-child)) (frame-x brother))
1820 (rotatef (frame-y (current-child)) (frame-y brother))
1821 (rotatef (frame-w (current-child)) (frame-w brother))
1822 (rotatef (frame-h (current-child)) (frame-h brother))
1823 (show-all-children t)
1824 (leave-second-mode)))))))
1826 (defun rotate-frame-geometry-generic (fun)
1827 "(Rotate brother frame geometry"
1828 (when (frame-p (current-child))
1829 (let ((parent (find-parent-frame (current-child))))
1830 (when (frame-p parent)
1831 (let* ((child-list (funcall fun (frame-child parent)))
1832 (first (first child-list)))
1833 (dolist (child (rest child-list))
1834 (when (and (frame-p first) (frame-p child))
1835 (rotatef (frame-x first) (frame-x child))
1836 (rotatef (frame-y first) (frame-y child))
1837 (rotatef (frame-w first) (frame-w child))
1838 (rotatef (frame-h first) (frame-h child))
1839 (setf first child)))
1840 (show-all-children t))))))
1843 (defun rotate-frame-geometry ()
1844 "Rotate brother frame geometry"
1845 (rotate-frame-geometry-generic #'identity))
1847 (defun anti-rotate-frame-geometry ()
1848 "Anti rotate brother frame geometry"
1849 (rotate-frame-geometry-generic #'reverse))