b9d59a2f1c3c52b3127bfb49c0fe8bc4c3f36a2c
[clfswm.git] / src / clfswm-util.lisp
blobb9d59a2f1c3c52b3127bfb49c0fe8bc4c3f36a2c
1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
3 ;;;
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Utility
6 ;;; --------------------------------------------------------------------------
7 ;;;
8 ;;; (C) 2005-2013 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 (let ((already-warn nil))
52 (defun load-contrib (file)
53 "Load a file in the contrib directory"
54 (let ((truename (merge-pathnames file *contrib-dir*)))
55 (format t "Loading contribution file: ~A~%" truename)
56 (if (probe-file truename)
57 (load truename :verbose nil)
58 (progn
59 (format t " File not found!~%")
60 (unless already-warn
61 (setf already-warn t)
62 (format t " ~&Please, adjust the *contrib-dir* variable to the place where CLFSWM can
63 find its contrib module files. For example: /usr/local/lib/clfswm/.
64 Write (defparameter *contrib-dir* \"/usr/local/lib/clfswm/\") in ~A.~%"
65 (conf-file-name))))))))
68 (defun reload-clfswm ()
69 "Reload clfswm"
70 (format t "~&-*- Reloading CLFSWM -*-~%")
71 (asdf:oos 'asdf:load-op :clfswm)
72 (reset-clfswm))
76 ;;;----------------------------
77 ;;; Lisp image part
78 ;;;----------------------------
79 #+:ECL (require :cmp)
81 (defun build-lisp-image (dump-name)
82 #+:CLISP (ext:saveinitmem dump-name :init-function (lambda () (clfswm:main) (ext:quit)) :executable t)
83 #+:SBCL (sb-ext:save-lisp-and-die dump-name :toplevel 'clfswm:main :executable t)
84 #+:CMU (ext:save-lisp dump-name :init-function (lambda () (clfswm:main) (ext:quit)) :executable t)
85 #+:CCL (ccl:save-application dump-name :toplevel-function (lambda () (clfswm:main) (ccl:quit)) :prepend-kernel t)
86 #+:ECL (c:build-program dump-name :epilogue-code '(clfswm:main)))
91 (defun query-yes-or-no (formatter &rest args)
92 (let ((rep (query-string (apply #'format nil formatter args) "" '("Yes" "No"))))
93 (or (string= rep "")
94 (char= (char rep 0) #\y)
95 (char= (char rep 0) #\Y))))
99 (defun banish-pointer ()
100 "Move the pointer to the lower right corner of the screen"
101 (with-placement (*banish-pointer-placement* x y)
102 (xlib:warp-pointer *root* x y)))
107 ;;; Root functions utility
108 (defun show-current-root ()
109 (when *have-to-show-current-root*
110 (let ((*notify-window-placement* *show-current-root-placement*))
111 (notify-message *show-current-root-delay* *show-current-root-message*))))
113 (defun select-generic-root (fun restart-menu)
114 (no-focus)
115 (let* ((current-root (find-root (current-child)))
116 (parent (find-parent-frame (root-original current-root))))
117 (when parent
118 (setf (frame-child parent) (funcall fun (frame-child parent)))
119 (let ((new-root (find-root (frame-selected-child parent))))
120 (setf (current-child) (aif (root-current-child new-root)
122 (frame-selected-child parent))))))
123 (show-all-children t)
124 (show-current-root)
125 (if restart-menu
126 (open-menu (find-menu 'root-menu))
127 (leave-second-mode)))
129 (defun select-next-root ()
130 "Select the next root"
131 (select-generic-root #'rotate-list nil))
133 (defun select-previous-root ()
134 "Select the previous root"
135 (select-generic-root #'anti-rotate-list nil))
138 (defun select-next-root-restart-menu ()
139 "Select the next root"
140 (select-generic-root #'rotate-list t))
142 (defun select-previous-root-restart-menu ()
143 "Select the previous root"
144 (select-generic-root #'anti-rotate-list t))
147 (defun rotate-root-geometry-generic (fun restart-menu)
148 (no-focus)
149 (funcall fun)
150 (show-all-children t)
151 (show-current-root)
152 (if restart-menu
153 (open-menu (find-menu 'root-menu))
154 (leave-second-mode)))
157 (defun rotate-root-geometry-next ()
158 "Rotate root geometry to next root"
159 (rotate-root-geometry-generic #'rotate-root-geometry nil))
161 (defun rotate-root-geometry-previous ()
162 "Rotate root geometry to previous root"
163 (rotate-root-geometry-generic #'anti-rotate-root-geometry nil))
165 (defun rotate-root-geometry-next-restart-menu ()
166 "Rotate root geometry to next root"
167 (rotate-root-geometry-generic #'rotate-root-geometry t))
169 (defun rotate-root-geometry-previous-restart-menu ()
170 "Rotate root geometry to previous root"
171 (rotate-root-geometry-generic #'anti-rotate-root-geometry t))
175 (defun exchange-root-geometry-with-mouse ()
176 "Exchange two root geometry pointed with the mouse"
177 (open-notify-window '("Select the first root to exchange"))
178 (wait-no-key-or-button-press)
179 (wait-mouse-button-release)
180 (close-notify-window)
181 (multiple-value-bind (x1 y1) (xlib:query-pointer *root*)
182 (open-notify-window '("Select the second root to exchange"))
183 (wait-no-key-or-button-press)
184 (wait-mouse-button-release)
185 (close-notify-window)
186 (multiple-value-bind (x2 y2) (xlib:query-pointer *root*)
187 (exchange-root-geometry (find-root-by-coordinates x1 y1)
188 (find-root-by-coordinates x2 y2))))
189 (show-all-children)
190 (show-current-root)
191 (leave-second-mode))
193 (defun change-current-root-geometry ()
194 "Change the current root geometry"
195 (let* ((root (find-root (current-child)))
196 (x (query-number "New root X position" (root-x root)))
197 (y (query-number "New root Y position" (root-y root)))
198 (w (query-number "New root width" (root-w root)))
199 (h (query-number "New root height" (root-h root))))
200 (setf (root-x root) x (root-y root) y
201 (root-w root) w (root-h root) h)
202 (show-all-children)
203 (show-current-root)
204 (leave-second-mode)))
208 (defun display-all-frame-info ()
209 (with-all-frames (*root-frame* frame)
210 (display-frame-info frame)))
212 (defun display-all-root-frame-info ()
213 (with-all-root-child (root)
214 (display-frame-info root)))
218 (defun place-window-from-hints (window)
219 "Place a window from its hints"
220 (let* ((hints (xlib:wm-normal-hints window))
221 (min-width (or (and hints (xlib:wm-size-hints-min-width hints)) 0))
222 (min-height (or (and hints (xlib:wm-size-hints-min-height hints)) 0))
223 (max-width (or (and hints (xlib:wm-size-hints-max-width hints)) (x-drawable-width *root*)))
224 (max-height (or (and hints (xlib:wm-size-hints-max-height hints)) (x-drawable-height *root*)))
225 (rwidth (or (and hints (or (xlib:wm-size-hints-width hints) (xlib:wm-size-hints-base-width hints)))
226 (x-drawable-width window)))
227 (rheight (or (and hints (or (xlib:wm-size-hints-height hints) (xlib:wm-size-hints-base-height hints)))
228 (x-drawable-height window))))
229 (setf (x-drawable-width window) (min (max min-width rwidth *default-window-width*) max-width)
230 (x-drawable-height window) (min (max min-height rheight *default-window-height*) max-height))
231 (with-placement (*unmanaged-window-placement* x y (x-drawable-width window) (x-drawable-height window))
232 (setf (x-drawable-x window) x
233 (x-drawable-y window) y))
234 (xlib:display-finish-output *display*)))
237 (defun rename-current-child ()
238 "Rename the current child"
239 (let ((name (query-string (format nil "New child name: (last: ~A)" (child-name (current-child)))
240 (child-name (current-child)))))
241 (rename-child (current-child) name)
242 (leave-second-mode)))
245 (defun ask-child-transparency (msg child)
246 (let ((trans (query-number (format nil "New ~A transparency: (last: ~A)"
248 (* 100 (child-transparency child)))
249 (* 100 (child-transparency child)))))
250 (when (numberp trans)
251 (setf (child-transparency child) (float (/ trans 100))))))
253 (defun set-current-child-transparency ()
254 "Set the current child transparency"
255 (ask-child-transparency "child" (current-child))
256 (leave-second-mode))
259 (defun ask-child-border-size (msg child)
260 (let ((size (query-number (format nil "New ~A border size: (last: ~A)"
262 (child-border-size child))
263 (child-border-size child))))
264 (when (numberp size)
265 (setf (child-border-size child) size))))
268 (defun set-current-child-border-size ()
269 "Set the current child border size"
270 (ask-child-border-size "child" (current-child))
271 (leave-second-mode))
274 (defun renumber-current-frame ()
275 "Renumber the current frame"
276 (when (frame-p (current-child))
277 (let ((number (query-number (format nil "New child number: (last: ~A)" (frame-number (current-child)))
278 (frame-number (current-child)))))
279 (setf (frame-number (current-child)) number)
280 (leave-second-mode))))
285 (defun add-default-frame ()
286 "Add a default frame in the current frame"
287 (when (frame-p (current-child))
288 (let ((name (query-string "Frame name")))
289 (push (create-frame :name name) (frame-child (current-child)))))
290 (leave-second-mode))
292 (defun add-frame-in-parent-frame ()
293 "Add a frame in the parent frame (and reorganize parent frame)"
294 (let ((parent (find-parent-frame (current-child))))
295 (when (and parent (not (child-original-root-p (current-child))))
296 (let ((new-frame (create-frame)))
297 (pushnew new-frame (frame-child parent))
298 (awhen (child-root-p (current-child))
299 (change-root it parent))
300 (setf (current-child) parent)
301 (set-layout-once #'tile-space-layout)
302 (setf (current-child) new-frame)
303 (leave-second-mode)))))
308 (defun add-placed-frame ()
309 "Add a placed frame in the current frame"
310 (when (frame-p (current-child))
311 (let ((name (query-string "Frame name"))
312 (x (/ (query-number "Frame x in percent (%)") 100))
313 (y (/ (query-number "Frame y in percent (%)") 100))
314 (w (/ (query-number "Frame width in percent (%)" 100) 100))
315 (h (/ (query-number "Frame height in percent (%)" 100) 100)))
316 (push (create-frame :name name :x x :y y :w w :h h)
317 (frame-child (current-child)))))
318 (leave-second-mode))
322 (defun delete-focus-window-generic (close-fun)
323 (with-focus-window (window)
324 (when (child-equal-p window (current-child))
325 (setf (current-child) (find-current-root)))
326 (delete-child-and-children-in-all-frames window close-fun)))
329 (defun delete-focus-window ()
330 "Close focus window: Delete the focus window in all frames and workspaces"
331 (delete-focus-window-generic 'delete-window))
333 (defun destroy-focus-window ()
334 "Kill focus window: Destroy the focus window in all frames and workspaces"
335 (delete-focus-window-generic 'destroy-window))
337 (defun remove-focus-window ()
338 "Remove the focus window from the current frame"
339 (with-focus-window (window)
340 (setf (current-child) (find-current-root))
341 (hide-child window)
342 (remove-child-in-frame window (find-parent-frame window))
343 (show-all-children)))
346 (defun unhide-all-windows-in-current-child ()
347 "Unhide all hidden windows into the current child"
348 (dolist (window (get-hidden-windows))
349 (unhide-window window)
350 (process-new-window window)
351 (map-window window))
352 (show-all-children))
357 (defun find-child-under-mouse-in-never-managed-windows (x y)
358 "Return the child under mouse from never managed windows"
359 (let ((ret nil))
360 (dolist (win (xlib:query-tree *root*))
361 (unless (window-hidden-p win)
362 (multiple-value-bind (never-managed raise)
363 (never-managed-window-p win)
364 (when (and never-managed raise (in-window win x y))
365 (setf ret win)))))
366 ret))
368 (defun find-child-under-mouse-in-child-tree (x y)
369 (dolist (child-rect (get-displayed-child))
370 (when (in-child (child-rect-child child-rect) x y)
371 (return-from find-child-under-mouse-in-child-tree (child-rect-child child-rect)))))
375 (defun find-child-under-mouse (x y &optional also-never-managed)
376 "Return the child under the mouse"
377 (or (and also-never-managed
378 (find-child-under-mouse-in-never-managed-windows x y))
379 (find-child-under-mouse-in-child-tree x y)))
384 ;;; Selection functions
385 (defun clear-selection ()
386 "Clear the current selection"
387 (setf *child-selection* nil)
388 (display-all-root-frame-info))
390 (defun copy-current-child ()
391 "Copy the current child to the selection"
392 (pushnew (current-child) *child-selection*)
393 (display-all-root-frame-info))
396 (defun cut-current-child (&optional (show-now t))
397 "Cut the current child to the selection"
398 (unless (child-root-p (current-child))
399 (let ((parent (find-parent-frame (current-child))))
400 (hide-all (current-child))
401 (copy-current-child)
402 (remove-child-in-frame (current-child) (find-parent-frame (current-child) (find-current-root)))
403 (when parent
404 (setf (current-child) parent))
405 (when show-now
406 (show-all-children t))
407 (current-child))))
409 (defun remove-current-child ()
410 "Remove the current child from its parent frame"
411 (unless (child-root-p (current-child))
412 (let ((parent (find-parent-frame (current-child))))
413 (hide-all (current-child))
414 (remove-child-in-frame (current-child) (find-parent-frame (current-child) (find-current-root)))
415 (when parent
416 (setf (current-child) parent))
417 (show-all-children t)
418 (leave-second-mode))))
420 (defun delete-current-child ()
421 "Delete the current child and its children in all frames"
422 (unless (child-root-p (current-child))
423 (hide-all (current-child))
424 (delete-child-and-children-in-all-frames (current-child))
425 (show-all-children t)
426 (leave-second-mode)))
429 (defun paste-selection-no-clear ()
430 "Paste the selection in the current frame - Do not clear the selection after paste"
431 (when (frame-p (current-child))
432 (dolist (child *child-selection*)
433 (unless (find-child-in-parent child (current-child))
434 (pushnew child (frame-child (current-child)) :test #'child-equal-p)))
435 (show-all-children)))
437 (defun paste-selection ()
438 "Paste the selection in the current frame"
439 (when (frame-p (current-child))
440 (paste-selection-no-clear)
441 (setf *child-selection* nil)
442 (display-all-root-frame-info)))
445 (defun copy-focus-window ()
446 "Copy the focus window to the selection"
447 (with-focus-window (window)
448 (with-current-child (window)
449 (copy-current-child))))
452 (defun cut-focus-window ()
453 "Cut the focus window to the selection"
454 (with-focus-window (window)
455 (setf (current-child) (with-current-child (window)
456 (cut-current-child nil)))
457 (show-all-children t)))
464 ;;; Maximize function
465 (defun frame-toggle-maximize ()
466 "Maximize/Unmaximize the current frame in its parent frame"
467 (when (frame-p (current-child))
468 (let ((unmaximized-coords (frame-data-slot (current-child) :unmaximized-coords)))
469 (if unmaximized-coords
470 (with-slots (x y w h) (current-child)
471 (destructuring-bind (nx ny nw nh) unmaximized-coords
472 (setf (frame-data-slot (current-child) :unmaximized-coords) nil
473 x nx y ny w nw h nh)))
474 (with-slots (x y w h) (current-child)
475 (setf (frame-data-slot (current-child) :unmaximized-coords)
476 (list x y w h)
477 x 0 y 0 w 1 h 1))))
478 (show-all-children)
479 (leave-second-mode)))
489 ;;; CONFIG - Identify mode
490 (defun identify-key ()
491 "Identify a key"
492 (let* ((done nil)
493 (font (xlib:open-font *display* *identify-font-string*))
494 (window (xlib:create-window :parent *root*
495 :x 0 :y 0
496 :width (- (screen-width) (* *border-size* 2))
497 :height (* 5 (+ (xlib:max-char-ascent font) (xlib:max-char-descent font)))
498 :background (get-color *identify-background*)
499 :border-width *border-size*
500 :border (get-color *identify-border*)
501 :colormap (xlib:screen-default-colormap *screen*)
502 :event-mask '(:exposure)))
503 (gc (xlib:create-gcontext :drawable window
504 :foreground (get-color *identify-foreground*)
505 :background (get-color *identify-background*)
506 :font font
507 :line-style :solid)))
508 (setf (window-transparency window) *identify-transparency*)
509 (labels ((print-doc (msg hash-table-key pos code state)
510 (let ((function (find-key-from-code hash-table-key code state)))
511 (when (and function (fboundp (first function)))
512 (xlib:draw-glyphs *pixmap-buffer* gc 10 (+ (* pos (+ (xlib:max-char-ascent font) (xlib:max-char-descent font))) 5)
513 (format nil "~A ~A" msg (documentation (first function) 'function))))))
514 (print-key (code state keysym key modifiers)
515 (clear-pixmap-buffer window gc)
516 (setf (xlib:gcontext-foreground gc) (get-color *identify-foreground*))
517 (xlib:draw-glyphs *pixmap-buffer* gc 5 (+ (xlib:max-char-ascent font) 5)
518 (format nil "Press a key to identify. Press 'q' to stop the identify loop."))
519 (when code
520 (xlib:draw-glyphs *pixmap-buffer* gc 10 (+ (* 2 (+ (xlib:max-char-ascent font) (xlib:max-char-descent font))) 5)
521 (format nil "Code=~A KeySym=~S Key=~S Modifiers=~A"
522 code keysym key modifiers))
523 (print-doc "Main mode : " *main-keys* 3 code state)
524 (print-doc "Second mode: " *second-keys* 4 code state))
525 (copy-pixmap-buffer window gc))
526 (handle-identify-key (&rest event-slots &key root code state &allow-other-keys)
527 (declare (ignore event-slots root))
528 (let* ((modifiers (state->modifiers state))
529 (key (keycode->char code state))
530 (keysym (keysym->keysym-name (keycode->keysym code modifiers))))
531 (setf done (and (equal key #\q) (equal modifiers *default-modifiers*)))
532 (dbg code keysym key modifiers)
533 (print-key code state keysym key modifiers)
534 (force-output)))
535 (handle-identify (&rest event-slots &key display event-key &allow-other-keys)
536 (declare (ignore display))
537 (case event-key
538 (:key-press (apply #'handle-identify-key event-slots) t)
539 (:exposure (print-key nil nil nil nil nil)))
541 (xgrab-pointer *root* 92 93)
542 (map-window window)
543 (format t "~&Press 'q' to stop the identify loop~%")
544 (print-key nil nil nil nil nil)
545 (force-output)
546 (unwind-protect
547 (loop until done do
548 (with-xlib-protect (:Identify-Loop nil)
549 (when (xlib:event-listen *display* *loop-timeout*)
550 (xlib:process-event *display* :handler #'handle-identify))
551 (xlib:display-finish-output *display*)))
552 (progn
553 (xlib:destroy-window window)
554 (xlib:close-font font)
555 (xgrab-pointer *root* 66 67))))))
562 (let ((all-symbols (collect-all-symbols)))
563 (defun eval-from-query-string ()
564 "Eval a lisp form from the query input"
565 (let ((form (query-string (format nil "Eval Lisp <~A> " (package-name *package*))
566 "" all-symbols))
567 (result nil))
568 (when (and form (not (equal form "")))
569 (let ((printed-result
570 (with-output-to-string (*standard-output*)
571 (setf result (handler-case
572 (loop for i in (multiple-value-list
573 (eval (read-from-string form)))
574 collect (format nil "~S" i))
575 (error (condition)
576 (format nil "~A" condition)))))))
577 (let ((ret (info-mode (expand-newline (append (ensure-list (format nil "> ~A" form))
578 (ensure-list printed-result)
579 (ensure-list result)))
580 :width (- (screen-width) 2))))
581 (when (or (search "defparameter" form :test #'string-equal)
582 (search "defvar" form :test #'string-equal))
583 (let ((elem (split-string form)))
584 (pushnew (string-downcase (if (string= (first elem) "(") (third elem) (second elem)))
585 all-symbols :test #'string=)))
586 (when (search "in-package" form :test #'string-equal)
587 (let ((*notify-window-placement* 'middle-middle-root-placement))
588 (open-notify-window '("Collecting all symbols for Lisp REPL completion."))
589 (setf all-symbols (collect-all-symbols))
590 (close-notify-window)))
591 (when ret
592 (eval-from-query-string))))))))
598 (let ((commands (command-in-path)))
599 (defun run-program-from-query-string ()
600 "Run a program from the query input"
601 (labels ((run-program-from-query-string-fun ()
602 (multiple-value-bind (program return)
603 (query-string "Run:" "" commands)
604 (when (and (equal return :return) program (not (equal program "")))
605 (let ((cmd (concatenate 'string "cd $HOME && exec " program)))
606 (lambda ()
607 (do-shell cmd)))))))
608 (let ((fun (run-program-from-query-string-fun)))
609 (when fun
610 (if *in-second-mode*
611 (progn
612 (setf *second-mode-leave-function* fun)
613 (leave-second-mode))
614 (funcall fun)))))))
620 ;;; Frame name actions
621 (defun ask-frame-name (msg)
622 "Ask a frame name"
623 (let ((all-frame-name nil))
624 (with-all-frames (*root-frame* frame)
625 (awhen (frame-name frame) (push it all-frame-name)))
626 (query-string msg "" all-frame-name)))
629 ;;; Focus by functions
630 (defun focus-frame-by (frame)
631 (when (frame-p frame)
632 (focus-all-children frame (or (find-parent-frame frame (find-current-root))
633 (find-parent-frame frame)
634 *root-frame*))
635 (show-all-children t)))
638 (defun focus-frame-by-name ()
639 "Focus a frame by name"
640 (focus-frame-by (find-frame-by-name (ask-frame-name "Focus frame:")))
641 (leave-second-mode))
643 (defun focus-frame-by-number ()
644 "Focus a frame by number"
645 (focus-frame-by (find-frame-by-number (query-number "Focus frame by number:")))
646 (leave-second-mode))
649 ;;; Open by functions
650 (defun open-frame-by (frame)
651 (when (frame-p frame)
652 (push (create-frame :name (query-string "Frame name")) (frame-child frame))
653 (show-all-children)))
657 (defun open-frame-by-name ()
658 "Open a new frame in a named frame"
659 (open-frame-by (find-frame-by-name (ask-frame-name "Open a new frame in: ")))
660 (leave-second-mode))
662 (defun open-frame-by-number ()
663 "Open a new frame in a numbered frame"
664 (open-frame-by (find-frame-by-number (query-number "Open a new frame in the group numbered:")))
665 (leave-second-mode))
668 ;;; Delete by functions
669 (defun delete-frame-by (frame)
670 (unless (or (child-equal-p frame *root-frame*)
671 (child-root-p frame))
672 (when (child-equal-p frame (current-child))
673 (setf (current-child) (find-current-root)))
674 (remove-child-in-frame frame (find-parent-frame frame)))
675 (show-all-children t))
678 (defun delete-frame-by-name ()
679 "Delete a frame by name"
680 (delete-frame-by (find-frame-by-name (ask-frame-name "Delete frame: ")))
681 (leave-second-mode))
683 (defun delete-frame-by-number ()
684 "Delete a frame by number"
685 (delete-frame-by (find-frame-by-number (query-number "Delete frame by number:")))
686 (leave-second-mode))
689 ;;; Move by function
690 (defun move-current-child-by-name ()
691 "Move current child in a named frame"
692 (move-child-to (current-child)
693 (find-frame-by-name
694 (ask-frame-name (format nil "Move '~A' to frame: " (child-name (current-child))))))
695 (leave-second-mode))
697 (defun move-current-child-by-number ()
698 "Move current child in a numbered frame"
699 (move-child-to (current-child)
700 (find-frame-by-number
701 (query-number (format nil "Move '~A' to frame numbered:" (child-name (current-child))))))
702 (leave-second-mode))
705 ;;; Copy by function
706 (defun copy-child-to (child frame-dest)
707 (when (and child (frame-p frame-dest))
708 (pushnew child (frame-child frame-dest))
709 (focus-all-children child frame-dest)
710 (show-all-children t)))
712 (defun copy-current-child-by-name ()
713 "Copy current child in a named frame"
714 (copy-child-to (current-child)
715 (find-frame-by-name
716 (ask-frame-name (format nil "Copy '~A' to frame: " (child-name (current-child))))))
717 (leave-second-mode))
719 (defun copy-current-child-by-number ()
720 "Copy current child in a numbered frame"
721 (copy-child-to (current-child)
722 (find-frame-by-number
723 (query-number (format nil "Copy '~A' to frame numbered:" (child-name (current-child))))))
724 (leave-second-mode))
729 ;;; Show frame info
730 (defun show-all-frames-info ()
731 "Show all frames info windows"
732 (let ((*show-root-frame-p* t))
733 (show-all-children)
734 (with-all-root-child (root)
735 (with-all-frames (root frame)
736 (raise-window (frame-window frame))
737 (display-frame-info frame)))))
739 (defun hide-all-frames-info ()
740 "Hide all frames info windows"
741 (show-all-children))
743 (defun show-all-frames-info-key ()
744 "Show all frames info windows until a key is release"
745 (show-all-frames-info)
746 (wait-no-key-or-button-press)
747 (hide-all-frames-info))
750 (defun move-frame (frame parent orig-x orig-y)
751 (when (and frame parent (not (child-root-p frame)))
752 (hide-all-children frame)
753 (with-slots (window) frame
754 (move-window window orig-x orig-y #'display-frame-info (list frame))
755 (setf (frame-x frame) (x-px->fl (x-drawable-x window) parent)
756 (frame-y frame) (y-px->fl (x-drawable-y window) parent)))
757 (show-all-children)))
759 (defun resize-frame (frame parent orig-x orig-y)
760 (when (and frame parent (not (child-root-p frame)))
761 (hide-all-children frame)
762 (with-slots (window) frame
763 (resize-window window orig-x orig-y #'display-frame-info (list frame))
764 (setf (frame-w frame) (w-px->fl (anti-adj-border-wh (x-drawable-width window) frame) parent)
765 (frame-h frame) (h-px->fl (anti-adj-border-wh (x-drawable-height window) frame) parent)))
766 (show-all-children)))
770 (defun mouse-click-to-focus-generic (window root-x root-y mouse-fn)
771 "Focus the current frame or focus the current window parent
772 mouse-fun is #'move-frame or #'resize-frame"
773 (let* ((to-replay t)
774 (child (find-child-under-mouse root-x root-y))
775 (parent (find-parent-frame child))
776 (root-p (child-root-p child)))
777 (labels ((add-new-frame ()
778 (when (frame-p child)
779 (setf parent child
780 child (create-frame)
781 mouse-fn #'resize-frame
782 (current-child) child)
783 (place-frame child parent root-x root-y 10 10)
784 (map-window (frame-window child))
785 (pushnew child (frame-child parent)))))
786 (when (and root-p *create-frame-on-root*)
787 (add-new-frame))
788 (when (and (frame-p child) (not (child-root-p child))
789 (not (equal-clfswm-terminal window)))
790 (funcall mouse-fn child parent root-x root-y))
791 (when (and child parent
792 (not (equal-clfswm-terminal window))
793 (focus-all-children child parent (not (child-root-p child))))
794 (when (show-all-children)
795 (setf to-replay nil)))
796 (if to-replay
797 (replay-button-event)
798 (stop-button-event)))))
801 (defun mouse-click-to-focus-and-move (window root-x root-y)
802 "Move and focus the current frame or focus the current window parent.
803 Or do actions on corners"
804 (or (do-corner-action root-x root-y *corner-main-mode-left-button*)
805 (mouse-click-to-focus-generic window root-x root-y #'move-frame)))
807 (defun mouse-click-to-focus-and-resize (window root-x root-y)
808 "Resize and focus the current frame or focus the current window parent.
809 Or do actions on corners"
810 ;;(declare (ignore window))
811 (or (do-corner-action root-x root-y *corner-main-mode-right-button*)
812 (mouse-click-to-focus-generic window root-x root-y #'resize-frame)))
814 (defun mouse-middle-click (window root-x root-y)
815 "Do actions on corners"
816 (declare (ignore window))
817 (or (do-corner-action root-x root-y *corner-main-mode-middle-button*)
818 (replay-button-event)))
823 (defun mouse-focus-move/resize-generic (root-x root-y mouse-fn window-parent)
824 "Focus the current frame or focus the current window parent
825 mouse-fun is #'move-frame or #'resize-frame.
826 Focus child and its parents -
827 For window: set current child to window or its parent according to window-parent"
828 (labels ((move/resize-managed (child)
829 (let ((parent (find-parent-frame child)))
830 (when (and child
831 (frame-p child)
832 (child-root-p child))
833 (setf parent child
834 child (create-frame)
835 mouse-fn #'resize-frame)
836 (place-frame child parent root-x root-y 10 10)
837 (map-window (frame-window child))
838 (push child (frame-child parent)))
839 (focus-all-children child parent window-parent)
840 (show-all-children)
841 (typecase child
842 (xlib:window
843 (if (managed-window-p child parent)
844 (funcall mouse-fn parent (find-parent-frame parent) root-x root-y)
845 (funcall (cond ((or (eql mouse-fn #'move-frame)
846 (eql mouse-fn #'move-frame-constrained))
847 #'move-window)
848 ((or (eql mouse-fn #'resize-frame)
849 (eql mouse-fn #'resize-frame-constrained))
850 #'resize-window))
851 child root-x root-y)))
852 (frame (funcall mouse-fn child parent root-x root-y)))
853 (show-all-children)))
854 (move/resize-never-managed (child raise-fun)
855 (funcall raise-fun child)
856 (funcall (cond ((eql mouse-fn #'move-frame) #'move-window)
857 ((eql mouse-fn #'resize-frame) #'resize-window))
858 child root-x root-y)))
859 (let ((child (find-child-under-mouse root-x root-y t)))
860 (multiple-value-bind (never-managed raise-fun)
861 (never-managed-window-p child)
862 (if (and (xlib:window-p child) never-managed raise-fun)
863 (move/resize-never-managed child raise-fun)
864 (move/resize-managed child))))))
867 (defun test-mouse-binding (window root-x root-y)
868 (dbg window root-x root-y)
869 (replay-button-event))
873 (defun mouse-select-next-level (window root-x root-y)
874 "Select the next level in frame"
875 (declare (ignore root-x root-y))
876 (let ((frame (find-frame-window window)))
877 (when (or frame (xlib:window-equal window *root*))
878 (select-next-level))
879 (replay-button-event)))
883 (defun mouse-select-previous-level (window root-x root-y)
884 "Select the previous level in frame"
885 (declare (ignore root-x root-y))
886 (let ((frame (find-frame-window window)))
887 (when (or frame (xlib:window-equal window *root*))
888 (select-previous-level))
889 (replay-button-event)))
893 (defun mouse-enter-frame (window root-x root-y)
894 "Enter in the selected frame - ie make it the root frame"
895 (declare (ignore root-x root-y))
896 (let ((frame (find-frame-window window)))
897 (when (or frame (xlib:window-equal window *root*))
898 (enter-frame))
899 (replay-button-event)))
903 (defun mouse-leave-frame (window root-x root-y)
904 "Leave the selected frame - ie make its parent the root frame"
905 (declare (ignore root-x root-y))
906 (let ((frame (find-frame-window window)))
907 (when (or frame (xlib:window-equal window *root*))
908 (leave-frame))
909 (replay-button-event)))
913 ;;;;;,-----
914 ;;;;;| Various definitions
915 ;;;;;`-----
917 (defun show-help (&optional (browser "dillo") (tempfile "/tmp/clfswm.html"))
918 "Show current keys and buttons bindings"
919 (ignore-errors
920 (produce-doc-html-in-file tempfile))
921 (sleep 1)
922 (do-shell (format nil "~A ~A" browser tempfile)))
926 ;;; Bind or jump functions
927 (let ((key-slots (make-array 10 :initial-element nil))
928 (current-slot 1))
929 (defun reset-bind-or-jump-slots ()
930 (dotimes (i 10)
931 (setf (aref key-slots i) nil)))
933 (defun bind-on-slot (&optional (slot current-slot) child)
934 "Bind current child to slot"
935 (setf (aref key-slots slot) (if child child (current-child))))
937 (defun remove-binding-on-slot ()
938 "Remove binding on slot"
939 (setf (aref key-slots current-slot) nil))
941 (defun jump-to-slot ()
942 "Jump to slot"
943 (let ((jump-child (aref key-slots current-slot)))
944 (when (and jump-child (find-child jump-child *root-frame*))
945 (unless (find-child-in-all-root jump-child)
946 (change-root (find-root jump-child) jump-child))
947 (setf (current-child) jump-child)
948 (focus-all-children jump-child jump-child)
949 (show-all-children t))))
951 (defun bind-or-jump (n)
952 "Bind or jump to a slot (a frame or a window)"
953 (setf current-slot (- n 1))
954 (let ((default-bind `("b" bind-on-slot
955 ,(format nil "Bind slot ~A on child: ~A" n (child-fullname (current-child))))))
956 (info-mode-menu (aif (aref key-slots current-slot)
957 `(,default-bind
958 ("BackSpace" remove-binding-on-slot
959 ,(format nil "Remove slot ~A binding on child: ~A" n (child-fullname (current-child))))
960 (" - " nil " -")
961 ("Tab" jump-to-slot
962 ,(format nil "Jump to child: ~A" (aif (aref key-slots current-slot)
963 (child-fullname it)
964 "Not set - Please, bind it with 'b'")))
965 ("Return" jump-to-slot "Same thing")
966 ("space" jump-to-slot "Same thing"))
967 (list default-bind))))))
971 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
972 ;;; Useful function for the second mode ;;;
973 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
974 (defmacro with-movement (&body body)
975 `(when (frame-p (current-child))
976 (unwind-protect
977 (progn
978 ,@body)
979 (show-all-children)
980 (display-all-frame-info)
981 (draw-second-mode-window)
982 (open-menu (find-menu 'frame-movement-menu)))))
985 ;;; Pack
986 (defun current-frame-pack-up ()
987 "Pack the current frame up"
988 (with-movement (pack-frame-up (current-child) (find-parent-frame (current-child) (find-current-root)))))
990 (defun current-frame-pack-down ()
991 "Pack the current frame down"
992 (with-movement (pack-frame-down (current-child) (find-parent-frame (current-child) (find-current-root)))))
994 (defun current-frame-pack-left ()
995 "Pack the current frame left"
996 (with-movement (pack-frame-left (current-child) (find-parent-frame (current-child) (find-current-root)))))
998 (defun current-frame-pack-right ()
999 "Pack the current frame right"
1000 (with-movement (pack-frame-right (current-child) (find-parent-frame (current-child) (find-current-root)))))
1002 ;;; Center
1003 (defun center-current-frame ()
1004 "Center the current frame"
1005 (with-movement (center-frame (current-child))))
1007 ;;; Fill
1008 (defun current-frame-fill-up ()
1009 "Fill the current frame up"
1010 (with-movement (fill-frame-up (current-child) (find-parent-frame (current-child) (find-current-root)))))
1012 (defun current-frame-fill-down ()
1013 "Fill the current frame down"
1014 (with-movement (fill-frame-down (current-child) (find-parent-frame (current-child) (find-current-root)))))
1016 (defun current-frame-fill-left ()
1017 "Fill the current frame left"
1018 (with-movement (fill-frame-left (current-child) (find-parent-frame (current-child) (find-current-root)))))
1020 (defun current-frame-fill-right ()
1021 "Fill the current frame right"
1022 (with-movement (fill-frame-right (current-child) (find-parent-frame (current-child) (find-current-root)))))
1024 (defun current-frame-fill-all-dir ()
1025 "Fill the current frame in all directions"
1026 (with-movement
1027 (let ((parent (find-parent-frame (current-child) (find-current-root))))
1028 (fill-frame-up (current-child) parent)
1029 (fill-frame-down (current-child) parent)
1030 (fill-frame-left (current-child) parent)
1031 (fill-frame-right (current-child) parent))))
1033 (defun current-frame-fill-vertical ()
1034 "Fill the current frame vertically"
1035 (with-movement
1036 (let ((parent (find-parent-frame (current-child) (find-current-root))))
1037 (fill-frame-up (current-child) parent)
1038 (fill-frame-down (current-child) parent))))
1040 (defun current-frame-fill-horizontal ()
1041 "Fill the current frame horizontally"
1042 (with-movement
1043 (let ((parent (find-parent-frame (current-child) (find-current-root))))
1044 (fill-frame-left (current-child) parent)
1045 (fill-frame-right (current-child) parent))))
1048 ;;; Resize
1049 (defun current-frame-resize-up ()
1050 "Resize the current frame up to its half height"
1051 (with-movement (resize-half-height-up (current-child))))
1053 (defun current-frame-resize-down ()
1054 "Resize the current frame down to its half height"
1055 (with-movement (resize-half-height-down (current-child))))
1057 (defun current-frame-resize-left ()
1058 "Resize the current frame left to its half width"
1059 (with-movement (resize-half-width-left (current-child))))
1061 (defun current-frame-resize-right ()
1062 "Resize the current frame right to its half width"
1063 (with-movement (resize-half-width-right (current-child))))
1065 (defun current-frame-resize-all-dir ()
1066 "Resize down the current frame"
1067 (with-movement (resize-frame-down (current-child))))
1069 (defun current-frame-resize-all-dir-minimal ()
1070 "Resize down the current frame to its minimal size"
1071 (with-movement (resize-minimal-frame (current-child))))
1074 ;;; Children navigation
1075 (defun with-movement-select-next-brother ()
1076 "Select the next brother frame"
1077 (with-movement (select-next-brother-simple)))
1079 (defun with-movement-select-previous-brother ()
1080 "Select the previous brother frame"
1081 (with-movement (select-previous-brother-simple)))
1083 (defun with-movement-select-next-level ()
1084 "Select the next level"
1085 (with-movement (select-next-level)))
1087 (defun with-movement-select-previous-level ()
1088 "Select the previous levelframe"
1089 (with-movement (select-previous-level)))
1091 (defun with-movement-select-next-child ()
1092 "Select the next child"
1093 (with-movement (select-next-child-simple)))
1097 ;;; Adapt frame functions
1098 (defun adapt-current-frame-to-window-hints-generic (width-p height-p)
1099 "Adapt the current frame to the current window minimal size hints"
1100 (when (frame-p (current-child))
1101 (let ((window (first (frame-child (current-child)))))
1102 (when (xlib:window-p window)
1103 (let* ((hints (xlib:wm-normal-hints window))
1104 (min-width (and hints (xlib:wm-size-hints-min-width hints)))
1105 (min-height (and hints (xlib:wm-size-hints-min-height hints))))
1106 (when (and width-p min-width)
1107 (setf (frame-rw (current-child)) min-width))
1108 (when (and height-p min-height)
1109 (setf (frame-rh (current-child)) min-height))
1110 (fixe-real-size (current-child) (find-parent-frame (current-child)))
1111 (leave-second-mode))))))
1113 (defun adapt-current-frame-to-window-hints ()
1114 "Adapt the current frame to the current window minimal size hints"
1115 (adapt-current-frame-to-window-hints-generic t t))
1117 (defun adapt-current-frame-to-window-width-hint ()
1118 "Adapt the current frame to the current window minimal width hint"
1119 (adapt-current-frame-to-window-hints-generic t nil))
1121 (defun adapt-current-frame-to-window-height-hint ()
1122 "Adapt the current frame to the current window minimal height hint"
1123 (adapt-current-frame-to-window-hints-generic nil t))
1128 ;;; Managed window type functions
1129 (defun current-frame-manage-window-type-generic (type-list)
1130 (when (frame-p (current-child))
1131 (setf (frame-managed-type (current-child)) type-list
1132 (frame-forced-managed-window (current-child)) nil
1133 (frame-forced-unmanaged-window (current-child)) nil))
1134 (leave-second-mode))
1137 (defun current-frame-manage-window-type ()
1138 "Change window types to be managed by a frame"
1139 (when (frame-p (current-child))
1140 (let* ((type-str (query-string "Managed window type: (all, normal, transient, maxsize, desktop, dock, toolbar, menu, utility, splash, dialog)"
1141 (format nil "~{~:(~A~) ~}" (frame-managed-type (current-child)))))
1142 (type-list (loop :for type :in (split-string type-str)
1143 :collect (intern (string-upcase type) :keyword))))
1144 (current-frame-manage-window-type-generic type-list))))
1147 (defun current-frame-manage-all-window-type ()
1148 "Manage all window type"
1149 (current-frame-manage-window-type-generic '(:all)))
1151 (defun current-frame-manage-only-normal-window-type ()
1152 "Manage only normal window type"
1153 (current-frame-manage-window-type-generic '(:normal)))
1155 (defun current-frame-manage-no-window-type ()
1156 "Do not manage any window type"
1157 (current-frame-manage-window-type-generic nil))
1166 ;;; Force window functions
1167 (defun force-window-in-frame ()
1168 "Force the current window to move in the frame (Useful only for unmanaged windows)"
1169 (with-current-window
1170 (let ((parent (find-parent-frame window)))
1171 (setf (x-drawable-x window) (frame-rx parent)
1172 (x-drawable-y window) (frame-ry parent))
1173 (xlib:display-finish-output *display*)))
1174 (leave-second-mode))
1177 (defun force-window-center-in-frame ()
1178 "Force the current window to move in the center of the frame (Useful only for unmanaged windows)"
1179 (with-current-window
1180 (let ((parent (find-parent-frame window)))
1181 (setf (x-drawable-x window) (truncate (+ (frame-rx parent)
1182 (/ (- (frame-rw parent)
1183 (x-drawable-width window)) 2)))
1184 (x-drawable-y window) (truncate (+ (frame-ry parent)
1185 (/ (- (frame-rh parent)
1186 (x-drawable-height window)) 2))))
1187 (xlib:display-finish-output *display*)))
1188 (leave-second-mode))
1192 (defun display-current-window-info ()
1193 "Display information on the current window"
1194 (with-current-window
1195 (info-mode (append (list (format nil "Window: ~A" window)
1196 (format nil "Window name: ~A" (xlib:wm-name window))
1197 (format nil "Window class: ~A" (xlib:get-wm-class window))
1198 (format nil "Window type: ~:(~A~)" (window-type window))
1199 (format nil "Window id: 0x~X" (xlib:window-id window))
1200 (format nil "Window transparency: ~A" (* 100 (window-transparency window)))
1201 (format nil " X=~A Y=~A W=~A H=~A"
1202 (x-drawable-x window) (x-drawable-y window)
1203 (x-drawable-width window) (x-drawable-height window)))
1204 (split-string (format nil "~A" (xlib:wm-normal-hints window)) #\Newline))))
1205 (leave-second-mode))
1207 (defun set-current-window-transparency ()
1208 "Set the current window transparency"
1209 (with-current-window
1210 (ask-child-transparency "window" window))
1211 (leave-second-mode))
1214 (defun manage-current-window ()
1215 "Force to manage the current window by its parent frame"
1216 (with-current-window
1217 (let ((parent (find-parent-frame window)))
1218 (with-slots ((managed forced-managed-window)
1219 (unmanaged forced-unmanaged-window)) parent
1220 (setf unmanaged (child-remove window unmanaged)
1221 unmanaged (remove (xlib:wm-name window) unmanaged :test #'string-equal-p))
1222 (pushnew window managed))))
1223 (leave-second-mode))
1225 (defun unmanage-current-window ()
1226 "Force to not manage the current window by its parent frame"
1227 (with-current-window
1228 (let ((parent (find-parent-frame window)))
1229 (with-slots ((managed forced-managed-window)
1230 (unmanaged forced-unmanaged-window)) parent
1231 (setf managed (child-remove window managed)
1232 managed (remove (xlib:wm-name window) managed :test #'string-equal-p))
1233 (pushnew window unmanaged))))
1234 (leave-second-mode))
1238 ;;; Moving child with the mouse button
1239 (defun mouse-move-child-over-frame (window root-x root-y)
1240 "Move the child under the mouse cursor to another frame"
1241 (declare (ignore window))
1242 (let ((child (find-child-under-mouse root-x root-y)))
1243 (unless (child-root-p child)
1244 (hide-all child)
1245 (let ((parent (find-parent-frame child)))
1246 (remove-child-in-frame child parent)
1247 (show-all-children)
1248 (wait-mouse-button-release 50 51)
1249 (multiple-value-bind (x y)
1250 (xlib:query-pointer *root*)
1251 (let ((dest (find-child-under-mouse x y)))
1252 (when (xlib:window-p dest)
1253 (setf dest (find-parent-frame dest)))
1254 (unless (child-equal-p child dest)
1255 (move-child-to child (or dest parent))))))
1256 (show-all-children)))
1257 (stop-button-event))
1262 ;;; Hide/Show frame window functions
1263 (defun hide/show-frame-window (frame value)
1264 "Hide/show the frame window"
1265 (when (frame-p frame)
1266 (setf (frame-show-window-p (current-child)) value)
1267 (show-all-children))
1268 (leave-second-mode))
1271 (defun hide-current-frame-window ()
1272 "Hide the current frame window"
1273 (hide/show-frame-window (current-child) nil))
1275 (defun show-current-frame-window ()
1276 "Show the current frame window"
1277 (hide/show-frame-window (current-child) t))
1281 ;;; Hide/Unhide current child
1282 (defun hide-current-child ()
1283 "Hide the current child"
1284 (unless (child-root-p (current-child))
1285 (let ((parent (find-parent-frame (current-child))))
1286 (when (frame-p parent)
1287 (with-slots (child hidden-children) parent
1288 (hide-all (current-child))
1289 (setf child (child-remove (current-child) child))
1290 (pushnew (current-child) hidden-children)
1291 (setf (current-child) parent))
1292 (show-all-children)))
1293 (leave-second-mode)))
1296 (defun frame-unhide-child (hidden frame-src frame-dest)
1297 "Unhide a hidden child from frame-src in frame-dest"
1298 (with-slots (hidden-children) frame-src
1299 (setf hidden-children (child-remove hidden hidden-children)))
1300 (with-slots (child) frame-dest
1301 (pushnew hidden child)))
1305 (defun unhide-a-child ()
1306 "Unhide a child in the current frame"
1307 (when (frame-p (current-child))
1308 (with-slots (child hidden-children) (current-child)
1309 (info-mode-menu (loop :for i :from 0
1310 :for hidden :in hidden-children
1311 :collect (list (code-char (+ (char-code #\a) i))
1312 (let ((lhd hidden))
1313 (lambda ()
1314 (frame-unhide-child lhd (current-child) (current-child))))
1315 (format nil "Unhide ~A" (child-fullname hidden))))))
1316 (show-all-children))
1317 (leave-second-mode))
1320 (defun unhide-all-children ()
1321 "Unhide all current frame hidden children"
1322 (when (frame-p (current-child))
1323 (with-slots (child hidden-children) (current-child)
1324 (dolist (c hidden-children)
1325 (pushnew c child))
1326 (setf hidden-children nil))
1327 (show-all-children))
1328 (leave-second-mode))
1331 (defun unhide-a-child-from-all-frames ()
1332 "Unhide a child from all frames in the current frame"
1333 (when (frame-p (current-child))
1334 (let ((acc nil)
1335 (keynum -1))
1336 (with-all-frames (*root-frame* frame)
1337 (when (frame-hidden-children frame)
1338 (push (format nil "~A" (child-fullname frame)) acc)
1339 (dolist (hidden (frame-hidden-children frame))
1340 (push (list (code-char (+ (char-code #\a) (incf keynum)))
1341 (let ((lhd hidden))
1342 (lambda ()
1343 (frame-unhide-child lhd frame (current-child))))
1344 (format nil "Unhide ~A" (child-fullname hidden)))
1345 acc))))
1346 (info-mode-menu (nreverse acc)))
1347 (show-all-children))
1348 (leave-second-mode))
1354 (let ((last-child nil))
1355 (defun init-last-child ()
1356 (setf last-child nil))
1357 (defun switch-to-last-child ()
1358 "Store the current child and switch to the previous one"
1359 (let ((current-child (current-child)))
1360 (when last-child
1361 (change-root (find-root last-child) last-child)
1362 (setf (current-child) last-child)
1363 (focus-all-children (current-child) (current-child))
1364 (show-all-children t))
1365 (setf last-child current-child))
1366 (leave-second-mode)))
1374 ;;; Focus policy functions
1375 (defun set-focus-policy-generic (focus-policy)
1376 (when (frame-p (current-child))
1377 (setf (frame-focus-policy (current-child)) focus-policy))
1378 (leave-second-mode))
1381 (defun current-frame-set-click-focus-policy ()
1382 "Set a click focus policy for the current frame."
1383 (set-focus-policy-generic :click))
1385 (defun current-frame-set-sloppy-focus-policy ()
1386 "Set a sloppy focus policy for the current frame."
1387 (set-focus-policy-generic :sloppy))
1389 (defun current-frame-set-sloppy-strict-focus-policy ()
1390 "Set a (strict) sloppy focus policy only for windows in the current frame."
1391 (set-focus-policy-generic :sloppy-strict))
1393 (defun current-frame-set-sloppy-select-policy ()
1394 "Set a sloppy select policy for the current frame."
1395 (set-focus-policy-generic :sloppy-select))
1397 (defun current-frame-set-sloppy-select-window-policy ()
1398 "Set a sloppy select window policy for the current frame."
1399 (set-focus-policy-generic :sloppy-select-window))
1403 (defun set-focus-policy-generic-for-all (focus-policy)
1404 (with-all-frames (*root-frame* frame)
1405 (setf (frame-focus-policy frame) focus-policy))
1406 (leave-second-mode))
1409 (defun all-frames-set-click-focus-policy ()
1410 "Set a click focus policy for all frames."
1411 (set-focus-policy-generic-for-all :click))
1413 (defun all-frames-set-sloppy-focus-policy ()
1414 "Set a sloppy focus policy for all frames."
1415 (set-focus-policy-generic-for-all :sloppy))
1417 (defun all-frames-set-sloppy-strict-focus-policy ()
1418 "Set a (strict) sloppy focus policy for all frames."
1419 (set-focus-policy-generic-for-all :sloppy-strict))
1421 (defun all-frames-set-sloppy-select-policy ()
1422 "Set a sloppy select policy for all frames."
1423 (set-focus-policy-generic-for-all :sloppy-select))
1425 (defun all-frames-set-sloppy-select-window-policy ()
1426 "Set a sloppy select window policy for all frames."
1427 (set-focus-policy-generic-for-all :sloppy-select-window))
1431 ;;; Ensure unique name/number functions
1432 (defun extract-number-from-name (name)
1433 (when (stringp name)
1434 (let* ((pos (1+ (or (position #\. name :from-end t) -1)))
1435 (number (parse-integer name :junk-allowed t :start pos)))
1436 (values number
1437 (if number (subseq name 0 (1- pos)) name)))))
1442 (defun ensure-unique-name ()
1443 "Ensure that all children names are unique"
1444 (with-all-children (*root-frame* child)
1445 (multiple-value-bind (num1 name1)
1446 (extract-number-from-name (child-name child))
1447 (declare (ignore num1))
1448 (when name1
1449 (let ((acc nil))
1450 (with-all-children (*root-frame* c)
1451 (unless (child-equal-p child c))
1452 (multiple-value-bind (num2 name2)
1453 (extract-number-from-name (child-name c))
1454 (when (string-equal name1 name2)
1455 (push num2 acc))))
1456 (dbg acc)
1457 (when (> (length acc) 1)
1458 (setf (child-name child)
1459 (format nil "~A.~A" name1
1460 (1+ (find-free-number (loop for i in acc when i collect (1- i)))))))))))
1461 (leave-second-mode))
1463 (defun ensure-unique-number ()
1464 "Ensure that all children numbers are unique"
1465 (let ((num -1))
1466 (with-all-frames (*root-frame* frame)
1467 (setf (frame-number frame) (incf num))))
1468 (leave-second-mode))
1472 ;;; Standard menu functions - Based on the XDG specifications
1473 (defun um-create-xdg-section-list (menu)
1474 (dolist (section *xdg-section-list*)
1475 (add-sub-menu menu :next section (format nil "~A" section) menu))
1476 (unless (find-toplevel-menu 'Utility menu)
1477 (add-sub-menu menu :next 'Utility (format nil "~A" 'Utility) menu)))
1479 (defun um-find-submenu (menu section-list)
1480 (let ((acc nil))
1481 (dolist (section section-list)
1482 (awhen (find-toplevel-menu (intern (string-upcase section) :clfswm) menu)
1483 (push it acc)))
1484 (if acc
1486 (list (find-toplevel-menu 'Utility menu)))))
1489 (defun um-extract-value (line)
1490 (second (split-string line #\=)))
1493 (defun um-add-desktop (desktop menu)
1494 (let (name exec categories comment)
1495 (when (probe-file desktop)
1496 (with-open-file (stream desktop :direction :input)
1497 (loop for line = (ignore-errors (read-line stream nil nil))
1498 while line
1500 (cond ((first-position "Name=" line) (setf name (um-extract-value line)))
1501 ((first-position "Exec=" line) (setf exec (um-extract-value line)))
1502 ((first-position "Categories=" line) (setf categories (um-extract-value line)))
1503 ((first-position "Comment=" line) (setf comment (um-extract-value line))))
1504 (when (and name exec categories)
1505 (let* ((sub-menu (um-find-submenu menu (split-string categories #\;)))
1506 (fun-name (intern name :clfswm)))
1507 (setf (symbol-function fun-name) (let ((do-exec exec))
1508 (lambda ()
1509 (do-shell do-exec)
1510 (leave-second-mode)))
1511 (documentation fun-name 'function) (format nil "~A~A" name (if comment
1512 (format nil " - ~A" comment)
1513 "")))
1514 (dolist (m sub-menu)
1515 (add-menu-key (menu-name m) :next fun-name m)))
1516 (setf name nil exec nil categories nil comment nil)))))))
1519 (defun update-menus (&optional (menu (make-menu :name 'main :doc "Main menu")))
1520 (um-create-xdg-section-list menu)
1521 (let ((count 0)
1522 (found (make-hash-table :test #'equal)))
1523 (dolist (dir (remove-duplicates
1524 (split-string (or (getenv "XDG_DATA_DIRS") "/usr/local/share/:/usr/share/")
1525 #\:) :test #'string-equal))
1526 (dolist (desktop (directory (concatenate 'string dir "/applications/**/*.desktop")))
1527 (unless (gethash (file-namestring desktop) found)
1528 (setf (gethash (file-namestring desktop) found) t)
1529 (um-add-desktop desktop menu)
1530 (incf count))))
1531 menu))
1535 ;;; Close/Kill focused window
1537 (defun ask-close/kill-current-window ()
1538 "Close or kill the current window (ask before doing anything)"
1539 (let ((window (xlib:input-focus *display*))
1540 (*info-mode-placement* *ask-close/kill-placement*))
1541 (info-mode-menu
1542 (if (and window (not (xlib:window-equal window *no-focus-window*)))
1543 `(,(format nil "Focus window: ~A" (xlib:wm-name window))
1544 (#\s delete-focus-window "Close the focus window")
1545 (#\k destroy-focus-window "Kill the focus window")
1546 (#\x cut-focus-window)
1547 (#\c copy-focus-window)
1548 (#\v paste-selection))
1549 `(,(format nil "Focus window: None")
1550 (#\v paste-selection))))
1555 ;;; Other window manager functions
1556 (defun get-proc-list ()
1557 (let ((proc (do-shell "ps x -o pid=" nil t))
1558 (proc-list nil))
1559 (loop for line = (read-line proc nil nil)
1560 while line
1561 do (push line proc-list))
1562 (dbg proc-list)
1563 proc-list))
1565 (defun run-other-window-manager ()
1566 (let ((proc-start (get-proc-list)))
1567 (do-shell *other-window-manager* nil t :terminal)
1568 (let* ((proc-end (get-proc-list))
1569 (proc-diff (set-difference proc-end proc-start :test #'equal)))
1570 (dbg 'killing-sigterm proc-diff)
1571 (do-shell (format nil "kill ~{ ~A ~} 2> /dev/null" proc-diff) nil t :terminal)
1572 (dbg 'killing-sigkill proc-diff)
1573 (do-shell (format nil "kill -9 ~{ ~A ~} 2> /dev/null" proc-diff) nil t :terminal)
1574 (sleep 1))
1575 (setf *other-window-manager* nil)))
1578 (defun do-run-other-window-manager (window-manager)
1579 (setf *other-window-manager* window-manager)
1580 (throw 'exit-main-loop nil))
1582 (defmacro def-run-other-window-manager (name &optional definition)
1583 (let ((definition (or definition name)))
1584 `(defun ,(create-symbol "run-" name) ()
1585 ,(format nil "Run ~A" definition)
1586 (do-run-other-window-manager ,(format nil "~A" name)))))
1588 (def-run-other-window-manager "xterm")
1589 (def-run-other-window-manager "icewm")
1590 (def-run-other-window-manager "twm")
1591 (def-run-other-window-manager "gnome-session" "Gnome")
1592 (def-run-other-window-manager "startkde" "KDE")
1593 (def-run-other-window-manager "xfce4-session" "XFCE")
1595 (defun run-lxde ()
1596 "Run LXDE"
1597 (do-run-other-window-manager "( lxsession & ); xterm -e \"echo ' /----------------------------------\\' ; echo ' | CLFSWM Note: |' ; echo ' | Close this window when done. |' ; echo ' \\----------------------------------/'; echo; echo; $SHELL\""))
1599 (defun run-xfce4 ()
1600 "Run LXDE (xterm)"
1601 (do-run-other-window-manager "( xfce4-session &) ; xterm -e \"echo ' /----------------------------------\\' ; echo ' | CLFSWM Note: |' ; echo ' | Close this window when done. |' ; echo ' \\----------------------------------/'; echo; echo; $SHELL\""))
1604 (defun run-prompt-wm ()
1605 "Prompt for an other window manager"
1606 (let ((wm (query-string "Run an other window manager:" "icewm")))
1607 (do-run-other-window-manager wm)))
1610 ;;; Hide or show unmanaged windows utility.
1611 (defun set-hide-unmanaged-window ()
1612 "Hide unmanaged windows when frame is not selected"
1613 (when (frame-p (current-child))
1614 (setf (frame-data-slot (current-child) :unmanaged-window-action) :hide)
1615 (leave-second-mode)))
1617 (defun set-show-unmanaged-window ()
1618 "Show unmanaged windows when frame is not selected"
1619 (when (frame-p (current-child))
1620 (setf (frame-data-slot (current-child) :unmanaged-window-action) :show)
1621 (leave-second-mode)))
1623 (defun set-default-hide-unmanaged-window ()
1624 "Set default behaviour to hide or not unmanaged windows when frame is not selected"
1625 (when (frame-p (current-child))
1626 (setf (frame-data-slot (current-child) :unmanaged-window-action) nil)
1627 (leave-second-mode)))
1629 (defun set-globally-hide-unmanaged-window ()
1630 "Hide unmanaged windows by default. This is overriden by functions above"
1631 (setf *hide-unmanaged-window* t)
1632 (leave-second-mode))
1634 (defun set-globally-show-unmanaged-window ()
1635 "Show unmanaged windows by default. This is overriden by functions above"
1636 (setf *hide-unmanaged-window* nil)
1637 (leave-second-mode))
1640 ;;; Speed mouse movement.
1641 (let (minx miny maxx maxy history lx ly)
1642 (labels ((middle (x1 x2)
1643 (round (/ (+ x1 x2) 2)))
1644 (reset-if-moved (x y)
1645 (when (or (/= x (or lx x)) (/= y (or ly y)))
1646 (speed-mouse-reset)))
1647 (add-in-history (x y)
1648 (push (list x y) history)))
1649 (defun speed-mouse-reset ()
1650 "Reset speed mouse coordinates"
1651 (setf minx nil miny nil maxx nil maxy nil history nil lx nil ly nil))
1652 (defun speed-mouse-left ()
1653 "Speed move mouse to left"
1654 (with-x-pointer
1655 (reset-if-moved x y)
1656 (setf maxx x)
1657 (add-in-history x y)
1658 (setf lx (middle (or minx 0) maxx))
1659 (xlib:warp-pointer *root* lx y)))
1660 (defun speed-mouse-right ()
1661 "Speed move mouse to right"
1662 (with-x-pointer
1663 (reset-if-moved x y)
1664 (setf minx x)
1665 (add-in-history x y)
1666 (setf lx (middle minx (or maxx (screen-width))))
1667 (xlib:warp-pointer *root* lx y)))
1668 (defun speed-mouse-up ()
1669 "Speed move mouse to up"
1670 (with-x-pointer
1671 (reset-if-moved x y)
1672 (setf maxy y)
1673 (add-in-history x y)
1674 (setf ly (middle (or miny 0) maxy))
1675 (xlib:warp-pointer *root* x ly)))
1676 (defun speed-mouse-down ()
1677 "Speed move mouse to down"
1678 (with-x-pointer
1679 (reset-if-moved x y)
1680 (setf miny y)
1681 (add-in-history x y)
1682 (setf ly (middle miny (or maxy (screen-height))))
1683 (xlib:warp-pointer *root* x ly)))
1684 (defun speed-mouse-undo ()
1685 "Undo last speed mouse move"
1686 (when history
1687 (let ((h (pop history)))
1688 (when h
1689 (destructuring-bind (bx by) h
1690 (setf lx bx ly by
1691 minx nil maxx nil
1692 miny nil maxy nil)
1693 (xlib:warp-pointer *root* lx ly))))))
1694 (defun speed-mouse-first-history ()
1695 "Revert to the first speed move mouse"
1696 (when history
1697 (let ((h (first (last history))))
1698 (when h
1699 (setf lx (first h)
1700 ly (second h))
1701 (xlib:warp-pointer *root* lx ly)))))))
1705 ;;; Notify window functions
1706 (let (font
1707 window
1709 width height
1710 text
1711 current-child)
1712 (labels ((text-string (tx)
1713 (typecase tx
1714 (cons (first tx))
1715 (t tx)))
1716 (text-color (tx)
1717 (get-color (typecase tx
1718 (cons (second tx))
1719 (t *notify-window-foreground*)))))
1720 (defun is-notify-window-p (win)
1721 (when (and (xlib:window-p win) (xlib:window-p window))
1722 (xlib:window-equal win window)))
1724 (defun raise-notify-window ()
1725 (raise-window window))
1727 (defun refresh-notify-window ()
1728 (add-timer 0.1 #'refresh-notify-window :refresh-notify-window)
1729 (when (and window gc font)
1730 (raise-window window)
1731 (let ((text-height (- (xlib:font-ascent font) (xlib:font-descent font))))
1732 (loop for tx in text
1733 for i from 1 do
1734 (setf (xlib:gcontext-foreground gc) (text-color tx))
1735 (xlib:draw-glyphs window gc
1736 (truncate (/ (- width (* (xlib:max-char-width font) (length (text-string tx)))) 2))
1737 (* text-height i 2)
1738 (text-string tx))))))
1740 (defun close-notify-window ()
1741 (erase-timer :refresh-notify-window)
1742 (setf *never-managed-window-list*
1743 (remove (list #'is-notify-window-p 'raise-window)
1744 *never-managed-window-list* :test #'equal))
1745 (when gc
1746 (xlib:free-gcontext gc))
1747 (when window
1748 (xlib:destroy-window window))
1749 (when font
1750 (xlib:close-font font))
1751 (xlib:display-finish-output *display*)
1752 (setf window nil
1753 gc nil
1754 font nil))
1756 (defun open-notify-window (text-list)
1757 (close-notify-window)
1758 (setf font (xlib:open-font *display* *notify-window-font-string*))
1759 (let ((text-height (- (xlib:font-ascent font) (xlib:font-descent font))))
1760 (setf text text-list)
1761 (setf width (* (xlib:max-char-width font) (+ (loop for tx in text-list
1762 maximize (length (text-string tx))) 2))
1763 height (+ (* text-height (length text-list) 2) text-height))
1764 (with-placement (*notify-window-placement* x y width height)
1765 (setf window (xlib:create-window :parent *root*
1766 :x x
1767 :y y
1768 :width width
1769 :height height
1770 :background (get-color *notify-window-background*)
1771 :border-width *border-size*
1772 :border (get-color *notify-window-border*)
1773 :colormap (xlib:screen-default-colormap *screen*)
1774 :event-mask '(:exposure :key-press))
1775 gc (xlib:create-gcontext :drawable window
1776 :foreground (get-color *notify-window-foreground*)
1777 :background (get-color *notify-window-background*)
1778 :font font
1779 :line-style :solid))
1780 (setf (window-transparency window) *notify-window-transparency*)
1781 (when (frame-p (current-child))
1782 (setf current-child (current-child)))
1783 (add-in-never-managed-window-list (list 'is-notify-window-p 'raise-window))
1784 (map-window window)
1785 (refresh-notify-window)
1786 (xlib:display-finish-output *display*))))))
1788 (defun notify-message (delay &rest messages)
1789 (erase-timer :close-notify-window)
1790 (funcall #'open-notify-window messages)
1791 (add-timer delay #'close-notify-window :close-notify-window))
1794 (defun display-hello-window ()
1795 (notify-message *notify-window-delay*
1796 '("Welcome to CLFSWM" "yellow")
1797 "Press Alt+F1 for help"))
1800 ;;; Run or raise functions
1801 (defun run-or-raise (raisep run-fn &key (maximized nil))
1802 (let ((window (with-all-windows (*root-frame* win)
1803 (when (funcall raisep win)
1804 (return win)))))
1805 (if window
1806 (let ((parent (find-parent-frame window)))
1807 (setf (current-child) parent)
1808 (put-child-on-top window parent)
1809 (when maximized
1810 (change-root (find-root parent) parent))
1811 (focus-all-children window parent)
1812 (show-all-children t))
1813 (funcall run-fn))))
1815 ;;; Transparency setting
1816 (defun inc-transparency (window root-x root-y)
1817 "Increment the child under mouse transparency"
1818 (declare (ignore root-x root-y))
1819 (unless *in-second-mode* (stop-button-event))
1820 (incf (child-transparency window) 0.1))
1822 (defun dec-transparency (window root-x root-y)
1823 "Decrement the child under mouse transparency"
1824 (declare (ignore root-x root-y))
1825 (unless *in-second-mode* (stop-button-event))
1826 (decf (child-transparency window) 0.1))
1828 (defun inc-transparency-slow (window root-x root-y)
1829 "Increment slowly the child under mouse transparency"
1830 (declare (ignore root-x root-y))
1831 (unless *in-second-mode* (stop-button-event))
1832 (incf (child-transparency window) 0.01))
1834 (defun dec-transparency-slow (window root-x root-y)
1835 "Decrement slowly the child under mouse transparency"
1836 (declare (ignore root-x root-y))
1837 (unless *in-second-mode* (stop-button-event))
1838 (decf (child-transparency window) 0.01))
1841 (defun key-inc-transparency ()
1842 "Increment the current window transparency"
1843 (with-current-window
1844 (incf (child-transparency window) 0.1)))
1846 (defun key-dec-transparency ()
1847 "Decrement the current window transparency"
1848 (with-current-window
1849 (decf (child-transparency window) 0.1)))
1855 ;;; Geometry change functions
1856 (defun swap-frame-geometry ()
1857 "Swap current brother frame geometry"
1858 (when (frame-p (current-child))
1859 (let ((parent (find-parent-frame (current-child))))
1860 (when (frame-p parent)
1861 (let ((brother (second (frame-child parent))))
1862 (when (frame-p brother)
1863 (rotatef (frame-x (current-child)) (frame-x brother))
1864 (rotatef (frame-y (current-child)) (frame-y brother))
1865 (rotatef (frame-w (current-child)) (frame-w brother))
1866 (rotatef (frame-h (current-child)) (frame-h brother))
1867 (show-all-children t)
1868 (leave-second-mode)))))))
1870 (defun rotate-frame-geometry-generic (fun)
1871 "(Rotate brother frame geometry"
1872 (when (frame-p (current-child))
1873 (let ((parent (find-parent-frame (current-child))))
1874 (when (frame-p parent)
1875 (let* ((child-list (funcall fun (frame-child parent)))
1876 (first (first child-list)))
1877 (dolist (child (rest child-list))
1878 (when (and (frame-p first) (frame-p child))
1879 (rotatef (frame-x first) (frame-x child))
1880 (rotatef (frame-y first) (frame-y child))
1881 (rotatef (frame-w first) (frame-w child))
1882 (rotatef (frame-h first) (frame-h child))
1883 (setf first child)))
1884 (show-all-children t))))))
1887 (defun rotate-frame-geometry ()
1888 "Rotate brother frame geometry"
1889 (rotate-frame-geometry-generic #'identity))
1891 (defun anti-rotate-frame-geometry ()
1892 "Anti rotate brother frame geometry"
1893 (rotate-frame-geometry-generic #'reverse))