contrib/toolbar.lisp: Add a clickable label module for toolbar. Add a clickable entry...
[clfswm.git] / src / clfswm.lisp
blobf8429ce5e91c4de38a481f90933b5aa9923e6b4e
1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
3 ;;;
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Main functions
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 (define-handler main-mode :key-press (code state)
30 (funcall-key-from-code *main-keys* code state))
32 (define-handler main-mode :button-press (code state window root-x root-y)
33 (unless (funcall-button-from-code *main-mouse* code state window root-x root-y *fun-press*)
34 (replay-button-event)))
36 (define-handler main-mode :button-release (code state window root-x root-y)
37 (unless (funcall-button-from-code *main-mouse* code state window root-x root-y *fun-release*)
38 (replay-button-event)))
40 (define-handler main-mode :motion-notify (window root-x root-y)
41 (unless (compress-motion-notify)
42 (funcall-button-from-code *main-mouse* 'motion
43 (modifiers->state *default-modifiers*)
44 window root-x root-y *fun-press*)))
46 (define-handler main-mode :configure-request (stack-mode window x y width height border-width value-mask)
47 (labels ((has-x (mask) (= 1 (logand mask 1)))
48 (has-y (mask) (= 2 (logand mask 2)))
49 (has-w (mask) (= 4 (logand mask 4)))
50 (has-h (mask) (= 8 (logand mask 8)))
51 (has-bw (mask) (= 16 (logand mask 16)))
52 (has-stackmode (mask) (= 64 (logand mask 64)))
53 (adjust-from-request ()
54 (when (has-x value-mask) (setf (x-drawable-x window) x))
55 (when (has-y value-mask) (setf (x-drawable-y window) y))
56 (when (has-h value-mask) (setf (x-drawable-height window) height))
57 (when (has-w value-mask) (setf (x-drawable-width window) width))))
58 (xlib:with-state (window)
59 (when (has-bw value-mask)
60 (setf (x-drawable-border-width window) border-width))
61 (let ((current-root (find-current-root)))
62 (if (find-child window current-root)
63 (let ((parent (find-parent-frame window current-root)))
64 (if (and parent (managed-window-p window parent))
65 (adapt-child-to-parent window parent)
66 (adjust-from-request)))
67 (adjust-from-request)))
68 (send-configuration-notify window (x-drawable-x window) (x-drawable-y window)
69 (x-drawable-width window) (x-drawable-height window)
70 (x-drawable-border-width window))
71 (when (has-stackmode value-mask)
72 (case stack-mode
73 (:above
74 (unless (null-size-window-p window)
75 (when (or (child-equal-p window (current-child))
76 (is-in-current-child-p window))
77 (raise-window window)
78 (focus-window window)
79 (focus-all-children window (find-parent-frame window (find-current-root)))))))))))
82 (define-handler main-mode :map-request (window send-event-p)
83 (unless send-event-p
84 (unhide-window window)
85 (process-new-window window)
86 (map-window window)
87 (unless (null-size-window-p window)
88 (multiple-value-bind (never-managed raise)
89 (never-managed-window-p window)
90 (unless (and never-managed raise)
91 (show-all-children))))))
94 (define-handler main-mode :unmap-notify (send-event-p event-window window)
95 (unless (and (not send-event-p)
96 (not (xlib:window-equal window event-window)))
97 (when (find-child window *root-frame*)
98 (clean-windows-in-all-frames)
99 (show-all-children)
100 (delete-child-in-all-frames window)
101 (show-all-children))))
104 (define-handler main-mode :destroy-notify (send-event-p event-window window)
105 (unless (or send-event-p
106 (xlib:window-equal window event-window))
107 (when (find-child window *root-frame*)
108 (clean-windows-in-all-frames)
109 (show-all-children)
110 (delete-child-in-all-frames window)
111 (show-all-children))))
113 (define-handler main-mode :enter-notify (window root-x root-y)
114 (unless (and (> root-x (- (xlib:screen-width *screen*) 3))
115 (> root-y (- (xlib:screen-height *screen*) 3)))
116 (case (if (frame-p (current-child))
117 (frame-focus-policy (current-child))
118 *default-focus-policy*)
119 (:sloppy (focus-window window))
120 (:sloppy-strict (when (and (frame-p (current-child))
121 (child-member window (frame-child (current-child))))
122 (focus-window window)))
123 (:sloppy-select (let* ((child (find-child-under-mouse root-x root-y))
124 (parent (find-parent-frame child)))
125 (unless (or (child-root-p child)
126 (equal (typecase child
127 (xlib:window parent)
128 (t child))
129 (current-child)))
130 (focus-all-children child parent)
131 (show-all-children)))))))
133 (define-handler main-mode :exposure (window)
134 (awhen (find-frame-window window)
135 (display-frame-info it)))
137 (define-handler main-mode :resize-request (window)
138 (dbg :resize-request window))
141 (defun error-handler (display error-key &rest key-vals &key asynchronous &allow-other-keys)
142 "Handle X errors"
143 (cond
144 ;; ignore asynchronous window errors
145 ((and asynchronous
146 (find error-key '(xlib:window-error xlib:drawable-error xlib:match-error)))
147 (format t "Ignoring XLib asynchronous error: ~s~%" error-key))
148 ((eq error-key 'xlib:access-error)
149 (write-line "Another window manager is running.")
150 (throw 'exit-clfswm nil))
151 ;; all other asynchronous errors are printed.
152 (asynchronous
153 (format t "Caught Asynchronous X Error: ~s ~s" error-key key-vals))
155 (apply 'error error-key :display display :error-key error-key key-vals))))
158 (defun main-loop ()
159 (loop
160 (call-hook *loop-hook*)
161 (process-timers)
162 (with-xlib-protect ()
163 (when (xlib:event-listen *display* *loop-timeout*)
164 (xlib:process-event *display* :handler #'handle-event))
165 (xlib:display-finish-output *display*))))
169 (defun open-display (display-str protocol)
170 (multiple-value-bind (host display-num) (parse-display-string display-str)
171 (setf *display* (xlib:open-display host :display display-num :protocol protocol)
172 (xlib:display-error-handler *display*) 'error-handler
173 (getenv "DISPLAY") display-str)))
177 (defun default-init-hook ()
178 (let ((frame (add-frame (create-frame :name "Default"
179 :layout nil :x 0.05 :y 0.05
180 :w 0.9 :h 0.9)
181 *root-frame*)))
182 (setf (current-child) frame)))
185 (defun init-display ()
186 (reset-root-list)
187 (reset-bind-or-jump-slots)
188 (reset-open-menu)
189 (fill-handle-event-fun-symbols)
190 (assoc-keyword-handle-event 'main-mode)
191 (setf *screen* (first (xlib:display-roots *display*))
192 *root* (xlib:screen-root *screen*)
193 *no-focus-window* (xlib:create-window :parent *root* :x 0 :y 0 :width 1 :height 1)
194 *default-font* (xlib:open-font *display* *default-font-string*)
195 *pixmap-buffer* (xlib:create-pixmap :width (xlib:screen-width *screen*)
196 :height (xlib:screen-height *screen*)
197 :depth (xlib:screen-root-depth *screen*)
198 :drawable *root*)
199 *in-second-mode* nil)
200 (store-root-background)
201 (init-modifier-list)
202 (xgrab-init-pointer)
203 (xgrab-init-keyboard)
204 (init-last-child)
205 (call-hook *binding-hook*)
206 (clear-timers)
207 (map-window *no-focus-window*)
208 (dbg *display*)
209 (setf (xlib:window-event-mask *root*) (xlib:make-event-mask :substructure-redirect
210 :substructure-notify
211 :property-change
212 :resize-redirect
213 :exposure
214 :button-press
215 :button-release
216 :pointer-motion))
217 ;;(intern-atoms *display*)
218 (netwm-set-properties)
219 (xlib:display-force-output *display*)
220 (setf *child-selection* nil)
221 (setf *root-frame* (create-frame :name "Root" :number 0)
222 (current-child) *root-frame*)
223 (call-hook *init-hook*)
224 (unsure-at-least-one-root)
225 (process-existing-windows *screen*)
226 (show-all-children)
227 (grab-main-keys)
228 (xlib:display-finish-output *display*)
229 (optimize-event-hook))
234 (defun read-conf-file ()
235 (let* ((conf (conf-file-name)))
236 (if conf
237 (handler-case (load conf)
238 (error (c)
239 (format t "~2%*** Error loading configuration file: ~A ***~&~A~%" conf c)
240 (values nil (format nil "~s" c) conf))
241 (:no-error (&rest args)
242 (declare (ignore args))
243 (values t nil conf)))
244 (values t nil nil))))
251 (defun exit-clfswm ()
252 "Exit clfswm"
253 (throw 'exit-clfswm nil))
255 (defun reset-clfswm ()
256 "Reset clfswm"
257 (throw 'exit-main-loop nil))
262 (defun main-unprotected (&key (display (or (getenv "DISPLAY") ":0")) protocol
263 (base-dir (asdf:system-source-directory :clfswm))
264 (read-conf-file-p t) (alternate-conf nil)
265 error-msg)
266 (setf *contrib-dir* (merge-pathnames "contrib/" base-dir))
267 (conf-file-name alternate-conf)
268 (when read-conf-file-p
269 (read-conf-file))
270 (create-configuration-menu :clear t)
271 (call-hook *main-entrance-hook*)
272 (handler-case
273 (open-display display protocol)
274 (xlib:access-error (c)
275 (format t "~&~A~&Maybe another window manager is running. [1]~%" c)
276 (force-output)
277 (exit-clfswm)))
278 (handler-case
279 (init-display)
280 (xlib:access-error (c)
281 (ungrab-main-keys)
282 (xlib:destroy-window *no-focus-window*)
283 (xlib:close-display *display*)
284 (format t "~&~A~&Maybe another window manager is running. [2]~%" c)
285 (force-output)
286 (exit-clfswm)))
287 (when error-msg
288 (info-mode error-msg))
289 (catch 'exit-main-loop
290 (unwind-protect
291 (main-loop)
292 (ungrab-main-keys)
293 (xlib:destroy-window *no-focus-window*)
294 (xlib:free-pixmap *pixmap-buffer*)
295 (destroy-all-frames-window)
296 (call-hook *close-hook*)
297 (clear-event-hooks)
298 (xlib:close-display *display*)
299 #+:event-debug
300 (format t "~2&Unhandled events: ~A~%" *unhandled-events*))))
304 (defun main (&key (display (or (getenv "DISPLAY") ":0")) protocol
305 (base-dir (asdf:system-source-directory :clfswm))
306 (read-conf-file-p t)
307 (alternate-conf nil))
308 (let (error-msg)
309 (catch 'exit-clfswm
310 (loop
311 (handler-case
312 (if *other-window-manager*
313 (run-other-window-manager)
314 (main-unprotected :display display :protocol protocol :base-dir base-dir
315 :read-conf-file-p read-conf-file-p
316 :alternate-conf alternate-conf
317 :error-msg error-msg))
318 (error (c)
319 (let ((msg (format nil "CLFSWM Error: ~A." c)))
320 (format t "~&~A~%Reinitializing...~%" msg)
321 (setf error-msg (list (list msg *info-color-title*)
322 "Reinitializing...")))))))))