Fix a problem with macro with-timer
[clfswm.git] / src / clfswm.lisp
blob52d2459b117c6999f05c524b441ea2f59e3ed181
1 ;;; --------------------------------------------------------------------------
2 ;;; CLFSWM - FullScreen Window Manager
3 ;;;
4 ;;; --------------------------------------------------------------------------
5 ;;; Documentation: Main functions
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)
28 (defparameter *clfswm-initializing* nil)
30 (define-handler main-mode :key-press (code state)
31 (funcall-key-from-code *main-keys* code state))
33 (define-handler main-mode :button-press (code state window root-x root-y)
34 (unless (funcall-button-from-code *main-mouse* code state window root-x root-y *fun-press*)
35 (replay-button-event)))
37 (define-handler main-mode :button-release (code state window root-x root-y)
38 (unless (funcall-button-from-code *main-mouse* code state window root-x root-y *fun-release*)
39 (replay-button-event)))
41 (define-handler main-mode :motion-notify (window root-x root-y)
42 (unless (compress-motion-notify)
43 (funcall-button-from-code *main-mouse* 'motion
44 (modifiers->state *default-modifiers*)
45 window root-x root-y *fun-press*)))
48 (define-handler main-mode :configure-request (stack-mode window x y width height border-width value-mask)
49 (let ((change nil))
50 (labels ((has-x (mask) (= 1 (logand mask 1)))
51 (has-y (mask) (= 2 (logand mask 2)))
52 (has-w (mask) (= 4 (logand mask 4)))
53 (has-h (mask) (= 8 (logand mask 8)))
54 (has-bw (mask) (= 16 (logand mask 16)))
55 (has-stackmode (mask) (= 64 (logand mask 64)))
56 (adjust-from-request ()
57 (when (has-x value-mask) (setf (x-drawable-x window) x
58 change :moved))
59 (when (has-y value-mask) (setf (x-drawable-y window) y
60 change :moved))
61 (when (has-h value-mask) (setf (x-drawable-height window) height
62 change :resized))
63 (when (has-w value-mask) (setf (x-drawable-width window) width
64 change :resized))))
65 (when window
66 (xlib:with-state (window)
67 (let ((current-root (find-current-root)))
68 (if (find-child window current-root)
69 (let ((parent (find-parent-frame window current-root)))
70 (if (and parent (managed-window-p window parent))
71 (setf change (adapt-child-to-parent window parent))
72 (adjust-from-request)))
73 (adjust-from-request)))
74 (when (has-bw value-mask)
75 (setf (x-drawable-border-width window) border-width
76 change :resized))
77 (when (has-stackmode value-mask)
78 (case stack-mode
79 (:above
80 (when (or (child-equal-p window (current-child))
81 (is-in-current-child-p window))
82 (setf change (or change :moved))
83 (when *steal-focus*
84 (focus-window window)
85 (when (focus-all-children window (find-parent-frame window (find-current-root)))
86 (show-all-children))))))))
87 (unless (eq change :resized)
88 ;; To be ICCCM compliant, send a fake configuration notify event only when
89 ;; the window has moved and not when it has been resized or the border width has changed.
90 (send-configuration-notify window (x-drawable-x window) (x-drawable-y window)
91 (x-drawable-width window) (x-drawable-height window)
92 (x-drawable-border-width window)))))))
95 (define-handler main-mode :map-request (window send-event-p)
96 (unless send-event-p
97 (unless (find-child window *root-frame*)
98 (unhide-window window)
99 (process-new-window window)
100 (map-window window)
101 (multiple-value-bind (never-managed raise)
102 (never-managed-window-p window)
103 (unless (and never-managed raise)
104 (show-all-children))))))
108 (define-handler main-mode :unmap-notify (send-event-p event-window window)
109 (unless (and (not send-event-p)
110 (not (xlib:window-equal window event-window)))
111 (when (find-child window *root-frame*)
112 (setf (window-state window) +withdrawn-state+)
113 (remove-child-in-all-frames window)
114 ;;(xlib:unmap-window window)
115 (show-all-children))))
118 (define-handler main-mode :destroy-notify (send-event-p event-window window)
119 (unless (or send-event-p
120 (xlib:window-equal window event-window))
121 (when (find-child window *root-frame*)
122 (delete-child-in-all-frames window)
123 (xlib:destroy-window window)
124 (show-all-children))))
127 (define-handler main-mode :enter-notify (window root-x root-y)
128 (unless (and (> root-x (- (screen-width) 3))
129 (> root-y (- (screen-height) 3)))
130 (manage-focus window root-x root-y)))
133 (define-handler main-mode :focus-in (window)
134 (unless (child-equal-p window (focused-window))
135 (set-focus-to-current-child)))
138 (define-handler main-mode :exposure (window)
139 (awhen (find-frame-window window)
140 (display-frame-info it)))
143 (define-handler main-mode :configure-notify (window)
144 (when (child-equal-p window *root*)
145 (unless (eql (place-frames-from-xinerama-infos) :update)
146 (finish-configuring-root))
147 (show-all-children)
148 (call-hook *root-size-change-hook*)))
151 (defun error-handler (display error-key &rest key-vals &key asynchronous &allow-other-keys)
152 "Handle X errors"
153 (cond
154 ;; ignore asynchronous window errors
155 ((and asynchronous
156 (find error-key '(xlib:window-error xlib:drawable-error xlib:match-error)))
157 #+:xlib-debug (format t "~&Ignoring XLib asynchronous error: ~s~%" error-key))
158 ((eq error-key 'xlib:access-error)
159 (if *clfswm-initializing*
160 (progn
161 (format t "~3&Another window manager is running. Exiting...~%")
162 (throw 'exit-clfswm nil))
163 #+:xlib-debug
164 (format t "~&Ignoring XLib asynchronous access error: ~s~%" error-key)))
165 ;; all other asynchronous errors are printed.
166 (asynchronous
167 #+:xlib-debug (format t "~&Caught Asynchronous X Error: ~s ~s" error-key key-vals))
168 ;;((find error-key '(xlib:window-error xlib:drawable-error xlib:match-error))
169 ;; (format t "~&Ignoring Xlib error: ~S ~S~%" error-key key-vals))
171 (apply 'error error-key :display display :error-key error-key key-vals))))
176 (defun main-loop ()
177 (loop
178 (with-xlib-protect (:main-loop nil)
179 (call-hook *loop-hook*)
180 (process-timers)
181 (when (xlib:event-listen *display* *loop-timeout*)
182 (xlib:process-event *display* :handler #'handle-event))
183 (xlib:display-finish-output *display*)
184 (setf *x-error-count* 0))))
189 (defun open-display (display-str protocol)
190 (multiple-value-bind (host display-num) (parse-display-string display-str)
191 (setf *display* (xlib:open-display host :display display-num :protocol protocol)
192 (xlib:display-error-handler *display*) 'error-handler
193 (getenv "DISPLAY") display-str)))
197 (defun default-init-hook ()
198 (place-frames-from-xinerama-infos)
199 (finish-configuring-root))
202 (defun init-display ()
203 (reset-root-list)
204 (reset-last-head-size)
205 (reset-bind-or-jump-slots)
206 (reset-open-menu)
207 (fill-handle-event-fun-symbols)
208 (assoc-keyword-handle-event 'main-mode)
209 (setf *screen* (first (xlib:display-roots *display*))
210 *root* (xlib:screen-root *screen*)
211 *no-focus-window* (xlib:create-window :parent *root* :x 0 :y 0 :width 1 :height 1)
212 *default-font* (xlib:open-font *display* *default-font-string*)
213 *pixmap-buffer* (xlib:create-pixmap :width (screen-width)
214 :height (screen-height)
215 :depth (xlib:screen-root-depth *screen*)
216 :drawable *root*)
217 *in-second-mode* nil
218 *x-error-count* 0
219 *expose-current-number* 0
220 *expose-child-list* nil)
221 (store-root-background)
222 (init-modifier-list)
223 (xgrab-init-pointer)
224 (xgrab-init-keyboard)
225 (init-last-child)
226 (call-hook *binding-hook*)
227 (clear-timers)
228 (map-window *no-focus-window*)
229 (dbg *display*)
230 (setf (xlib:window-event-mask *root*) (xlib:make-event-mask :substructure-redirect
231 :substructure-notify
232 :structure-notify
233 :property-change
234 ;;:resize-redirect
235 :exposure
236 :button-press
237 :button-release
238 :pointer-motion))
239 (xlib:display-finish-output *display*)
240 ;;(intern-atoms *display*)
241 (netwm-set-properties)
242 (xlib:display-force-output *display*)
243 (setf *child-selection* nil)
244 (setf *root-frame* (create-frame :name "Root" :number 0)
245 (current-child) *root-frame*)
246 (call-hook *init-hook*)
247 (process-existing-windows *screen*)
248 (show-all-children)
249 (grab-main-keys)
250 (xlib:display-finish-output *display*)
251 (optimize-event-hook))
256 (defun read-conf-file ()
257 (let* ((conf (conf-file-name)))
258 (if conf
259 (handler-case (load conf)
260 (error (c)
261 (format t "~2%*** Error loading configuration file: ~A ***~&~A~%" conf c)
262 (values nil (format nil "~s" c) conf))
263 (:no-error (&rest args)
264 (declare (ignore args))
265 (values t nil conf)))
266 (values t nil nil))))
273 (defun exit-clfswm ()
274 "Exit clfswm"
275 (throw 'exit-clfswm nil))
277 (defun reset-clfswm ()
278 "Reset clfswm"
279 (throw 'exit-main-loop nil))
284 (defun main-unprotected (&key (display (or (getenv "DISPLAY") ":0")) protocol
285 (read-conf-file-p t) (alternate-conf nil)
286 error-msg)
287 (setf *clfswm-initializing* t)
288 (conf-file-name alternate-conf)
289 (when read-conf-file-p
290 (read-conf-file))
291 (create-configuration-menu :clear t)
292 (call-hook *main-entrance-hook*)
293 (handler-case
294 (open-display display protocol)
295 (xlib:access-error (c)
296 (format t "~&~A~&Maybe another window manager is running. [1]~%" c)
297 (force-output)
298 (exit-clfswm)))
299 (handler-case
300 (init-display)
301 (xlib:access-error (c)
302 (ungrab-main-keys)
303 (xlib:destroy-window *no-focus-window*)
304 (xlib:close-display *display*)
305 (format t "~&~A~&Maybe another window manager is running. [2]~%" c)
306 (force-output)
307 (exit-clfswm)))
308 (when error-msg
309 (info-mode error-msg))
310 (setf *clfswm-initializing* nil)
311 (catch 'exit-main-loop
312 (unwind-protect
313 (main-loop)
314 (progn
315 (ungrab-main-keys)
316 (xlib:destroy-window *no-focus-window*)
317 (xlib:free-pixmap *pixmap-buffer*)
318 (destroy-all-frames-window)
319 (call-hook *close-hook*)
320 (clear-event-hooks)
321 (xlib:close-display *display*)
322 #+:event-debug
323 (format t "~2&Unhandled events: ~A~%" *unhandled-events*)))))
327 (defun main (&key (display (or (getenv "DISPLAY") ":0")) protocol
328 (read-conf-file-p t)
329 (alternate-conf nil))
330 (let (error-msg)
331 (catch 'exit-clfswm
332 (loop
333 (handler-case
334 (if *other-window-manager*
335 (run-other-window-manager)
336 (main-unprotected :display display :protocol protocol
337 :read-conf-file-p read-conf-file-p
338 :alternate-conf alternate-conf
339 :error-msg error-msg))
340 (error (c)
341 (let ((msg (format nil "CLFSWM Error: ~A." c)))
342 (format t "~&~A~%Reinitializing...~%" msg)
343 (setf error-msg (list (list msg *info-color-title*)
344 "Reinitializing...")))))))))