diff --git a/src/manager.scm b/src/manager.scm index 1d308cd..931428b 100644 --- a/src/manager.scm +++ b/src/manager.scm @@ -37,7 +37,7 @@ ((eq? type (manager-type switch)) "switch-wm") ((eq? type (manager-type move)) "move-wm"))) -(define (create-wm dpy parent options children +(define (create-wm dpy parent options default-options children type options-spec out-channel fun) (let* ((wa (get-window-attributes dpy parent)) (main-window @@ -52,8 +52,10 @@ (wm (make-wm type in-channel out-channel internal-out-channel dpy main-window colormap (create-options dpy colormap - (options-spec-union options-spec - manager-options-spec) + (spec-defaults default-options + (options-spec-union + options-spec + manager-options-spec)) options) '() #f))) @@ -118,8 +120,18 @@ (send internal-out-channel '(fit-windows))) ((focus-change-event? xevent) - ;; really send it always ?? - (send internal-out-channel '(update-manager-state))) + (if (window-exists? dpy (wm:window wm)) + (let ((mode (focus-change-event-mode xevent)) + (detail (focus-change-event-detail xevent))) + (if (and (eq? mode (notify-mode normal)) + (memq detail (list (notify-detail nonlinear) + (notify-detail nonlinear-virtual) + (notify-detail virtual) + (notify-detail ancestor)))) + (let ((focused? (eq? (event-type focus-in) + (focus-change-event-type xevent)))) + (send internal-out-channel + (list 'update-manager-state focused?))))))) ;; the manager got the focus (as a client) ((client-message-event? xevent) @@ -147,90 +159,78 @@ (let ((internal-out-channel (wm:internal-out-channel wm)) (dpy (wm:dpy wm))) (case (car msg) - ((manage-window) - (let ((window (second msg)) - (maybe-rect (third msg))) - (let ((client (create-client wm window))) - (set-wm:clients! wm (append (wm:clients wm) (list client))) - (send internal-out-channel - (list 'init-client client maybe-rect)) - ;; sync?? - ;;(if (window-exists? dpy window) - ;; (map-window dpy window)) - ;;(send internal-out-channel (list 'fit-client client)) - ;;(send internal-out-channel (list 'update-client-state client)) - ))) - - ((configure-window) - (let ((window (second msg)) - (changes (third msg))) - (send internal-out-channel - (list 'configure-window window changes)))) - - ((unmanage-window) - (let* ((window (second msg)) - (client (find (lambda (c) - (eq? window (client:window c))) - (wm:clients wm)))) - (if client - (reparent-to-root dpy window)))) - - ((destroy-manager) - ;; (send internal-out-channel '(deinit-manager)) - ;; sync ?? - (if (window-exists? dpy (wm:window wm)) - (destroy-window dpy (wm:window wm)))) - - ((deinit-client) - (let ((client (second msg))) - (set-wm:clients! wm (filter (lambda (c) (not (eq? c client))) - (wm:clients wm))) - (if (eq? (wm:current-client wm) client) - (set-wm:current-client! wm (and (not (null? (wm:clients wm))) - (car (wm:clients wm))))) - (send (wm:internal-out-channel wm) (list 'deinit-client client)) - ;; sync ?? - (destroy-window dpy (client:client-window client)))) - - ((select-client) - (let ((client (second msg)) - (time (third msg))) - (for-each (lambda (client) - (set-wm:current-client! wm client) - (raise-window dpy (client:client-window client)) - (if (window-exists? dpy (client:window client)) - (take-focus dpy (client:window client) time))) - (cons client (transients-for-client wm client))) -; (for-each (lambda (c) -; (if (not (eq? c client)) -; (grab-button dpy -; (button button1) (state-set) -; (client:client-window c) #f -; (event-mask button-press) -; (grab-mode async) (grab-mode async) -; none none))) -; (wm:clients wm)) - )) - - ))) + ((wait) + (let ((sp (second msg)) + (message (third msg))) + (handle-external-message wm exit message) + (sync-point-release sp))) + + ((manage-window) + (let ((window (second msg)) + (maybe-rect (third msg))) + (let ((client (create-client wm window))) + (set-wm:clients! wm (append (wm:clients wm) (list client))) + (send-message+wait internal-out-channel + (list 'init-client client maybe-rect))))) + + ((configure-window) + (let ((window (second msg)) + (changes (third msg))) + (send-message+wait internal-out-channel + (list 'configure-window window changes)))) + + ((unmanage-window) + (let* ((window (second msg)) + (client (find (lambda (c) + (eq? window (client:window c))) + (wm:clients wm)))) + (if client + (reparent-to-root dpy window)))) + + ((destroy-manager) + (send-message+wait internal-out-channel '(deinit-manager)) + (if (window-exists? dpy (wm:window wm)) + (destroy-window dpy (wm:window wm)))) + + ((deinit-client) + (let ((client (second msg))) + (set-wm:clients! wm (filter (lambda (c) (not (eq? c client))) + (wm:clients wm))) + (if (eq? (wm:current-client wm) client) + ;; select-client ?? + (set-wm:current-client! wm (and (not (null? (wm:clients wm))) + (car (wm:clients wm))))) + (send-message+wait (wm:internal-out-channel wm) + (list 'deinit-client client)) + (destroy-window dpy (client:client-window client)))) + + ((select-client) + (let ((client (second msg)) + (time (third msg))) + (for-each (lambda (client) + (set-wm:current-client! wm client) + (raise-window dpy (client:client-window client)) + (if (window-exists? dpy (client:window client)) + (take-focus dpy (client:window client) time))) + (cons client (transients-for-client wm client))))) + + (else (warn "unhandled manager message" wm msg))))) (define (wm-deinit-client wm client) - (mdisplay "manager deinit-client " wm " " client "\n") (send (wm:in-channel wm) (list 'deinit-client client))) ;; *** external messages ********************************************* (define (wm-manage-window wm window . rect) - (send (wm:in-channel wm) - (list 'manage-window window - (if (null? rect) - #f - (car rect)))) - ;; sync ?? - ) + (let ((maybe-rect (if (null? rect) + #f + (car rect)))) + (send-message+wait (wm:in-channel wm) + (list 'manage-window window maybe-rect)))) (define (wm-configure-window wm window changes) - (send (wm:in-channel wm) (list 'configure-window window changes))) + (send-message+wait (wm:in-channel wm) + (list 'configure-window window changes))) (define (wm-unmanage-window wm window) (send (wm:in-channel wm) (list 'unmanage-window window))) @@ -240,7 +240,7 @@ (send (wm:in-channel wm) (list 'select-client client time))))) (define (destroy-wm wm) - (send (wm:in-channel wm) '(destroy-manager))) + (send-message+wait (wm:in-channel wm) '(destroy-manager))) (define (send-root-drop wm window x y) (send (wm:out-channel wm) (list 'root-drop window x y))) @@ -248,19 +248,27 @@ ;; *** client ******************************************************** (define-record-type client :client - (make-client window client-window in-channel data) + (make-client window client-window in-channel data focused?) client? (window client:window set-client:window!) (client-window client:client-window) (in-channel client:in-channel) - (data client:data set-client:data!)) + (data client:data set-client:data!) + (focused? client:focused? set-client:focused?!)) + +(define (set-client-focused?! wm client focused?) + (let ((prev (client:focused? client))) + (if (not (eq? prev focused?)) + (begin + (set-client:focused?! client focused?) + (send (wm:internal-out-channel wm) + (list 'update-client-state client focused?)))))) (define-record-discloser :client (lambda (c) `(Client ,(client:window c) in ,(client:client-window c)))) (define (create-client wm window) - (mdisplay "creating client for " window "\n") (let* ((dpy (wm:dpy wm)) (client-window (create-simple-window dpy (wm:window wm) 0 0 @@ -270,10 +278,9 @@ (white-pixel dpy) (black-pixel dpy))) (in-channel (make-channel)) - (client (make-client window client-window in-channel #f))) + (client (make-client window client-window in-channel #f #f))) (reparent-window dpy window client-window 0 0) (create-client-handler wm client) - ;;(map-window dpy window) client)) (define (create-client-handler wm client) @@ -303,9 +310,11 @@ (lambda (msg) (case (car msg) ((restart-handler) - (mdisplay "restart-handler " wm " " client "\n") (create-client-handler wm client) - (exit 'restart))))) + (sync-point-release (second msg)) + (exit 'restart)) + (else (warn "unhandled client message" wm + client msg))))) (wrap (receive-rv client-window-channel) (lambda (xevent) (handle-client-window-xevent wm exit client xevent))) @@ -315,13 +324,11 @@ (loop))))))))))) (define (client-of-window wm window) - (let ((l (filter (lambda (client) - (equal? window (client:window client))) - (wm-clients wm)))) - (and (pair? l) (car l)))) + (find (lambda (client) + (equal? window (client:window client))) + (wm-clients wm))) (define (client-replace-window wm old-window new-window) - (mdisplay "client-replace-window: " wm " " old-window " " new-window "\n") (let ((client (client-of-window wm old-window)) (internal-out-channel (wm:internal-out-channel wm)) (dpy (wm:dpy wm))) @@ -332,16 +339,14 @@ (client:client-window client))) (reparent-window dpy new-window (client:client-window client) 0 0)) - (send (client:in-channel client) '(restart-handler)) - ;; wait ?! - ;; update everything... TODO - ;;(send internal-out-channel (list 'init-client client #f)) - (send internal-out-channel (list 'fit-client client)) - ;;(send internal-out-channel (list 'fit-windows client)) - ;; sync ?? - (map-window (wm:dpy wm) new-window) - (send internal-out-channel (list 'update-client-state client)) - ) + (let ((sp (make-sync-point))) + (send (client:in-channel client) + (list 'restart-handler sp)) + (sync-point-wait sp)) + (send-message+wait internal-out-channel (list 'fit-client client)) + (send internal-out-channel + (list 'update-client-name client (client-name dpy client))) + (map-window (wm:dpy wm) new-window)) #f))) (define (handle-client-window-xevent wm exit client xevent) @@ -370,18 +375,17 @@ changes)) (send internal-out-channel (list 'fit-client-window client))) (send-configuration dpy (client:window client))))) - ((circulate-event? xevent) - (send internal-out-channel (list 'update-client-state client))) +;; ((circulate-event? xevent) +;; (send internal-out-channel (list 'update-client-state client))) ((eq? (event-type enter-notify) type) (if (memq 'enter (get-option-value (wm:options wm) 'focus-policy)) (wm-select-client wm client (crossing-event-time xevent)))) - ((eq? (event-type button-press) type) - (if (memq 'click (get-option-value (wm:options wm) 'focus-policy)) - (wm-select-client wm client (button-event-time xevent)))) +;; ((eq? (event-type button-press) type) +;; (if (memq 'click (get-option-value (wm:options wm) 'focus-policy)) +;; (wm-select-client wm client (button-event-time xevent)))) ((destroy-window-event? xevent) - (mdisplay "client-window destroyed" wm client "\n") (exit 'destroy))))) (define (handle-client-xevent wm exit client xevent) @@ -389,29 +393,23 @@ (internal-out-channel (wm:internal-out-channel wm)) (dpy (wm:dpy wm))) (cond - ((eq? (event-type focus-out) type) + ((focus-change-event? xevent) (if (window-exists? dpy (client:window client)) (let ((mode (focus-change-event-mode xevent)) (detail (focus-change-event-detail xevent))) (if (and (eq? mode (notify-mode normal)) (memq detail (list (notify-detail nonlinear) (notify-detail nonlinear-virtual) + (notify-detail virtual) (notify-detail ancestor)))) - (uninstall-colormaps dpy (client:window client))))) - (send internal-out-channel - (list 'update-client-state client))) - - ((eq? (event-type focus-in) type) - (if (window-exists? dpy (client:window client)) - (let ((mode (focus-change-event-mode xevent)) - (detail (focus-change-event-detail xevent))) - (if (and (eq? mode (notify-mode normal)) - (memq detail (list (notify-detail nonlinear) - (notify-detail nonlinear-virtual) - (notify-detail ancestor)))) - (install-colormaps dpy (client:window client))))) - (send internal-out-channel - (list 'update-client-state client))) + (if (eq? (event-type focus-in) + (focus-change-event-type xevent)) + (begin + (install-colormaps dpy (client:window client)) + (set-client-focused?! wm client #t)) + (begin + (uninstall-colormaps dpy (client:window client)) + (set-client-focused?! wm client #f))))))) ((property-event? xevent) (if (window-exists? dpy (client:window client)) @@ -420,20 +418,18 @@ (cond ((equal? "WM_NAME" name) (send internal-out-channel - (list 'update-client-state client))) - ;; TODO: respect NORMAL_HINTS change - )))) + (list 'update-client-name client + (client-name dpy client)))))))) + ((reparent-event? xevent) (if (and (window-exists? dpy (client:window client)) (not (eq? (client:client-window client) (window-parent dpy (client:window client))))) (begin ;; window has been reparented away - (mdisplay "manager " (wm:type wm) " reparented client\n") (wm-deinit-client wm client) (exit 'reparent)))) ((destroy-window-event? xevent) - (mdisplay "destroy-window-event client " wm " " client "\n") (if (eq? (client:window client) (destroy-window-event-event xevent)) (begin (wm-deinit-client wm client) diff --git a/src/move-wm-resizer.scm b/src/move-wm-resizer.scm index 120b91b..54e65be 100644 --- a/src/move-wm-resizer.scm +++ b/src/move-wm-resizer.scm @@ -21,6 +21,7 @@ xc-bottom-side xc-bottom-left-corner default-cursor)))) (spawn* + (list 'move-wm-resizer wm client) (lambda (release) (call-with-event-channel dpy window @@ -34,13 +35,14 @@ (lambda () (let ((xevent (receive event-channel))) (cond - ((motion-event? xevent) + ((and (motion-event? xevent) (window-exists? dpy window)) (set-resize-cursor wm client cursors (motion-event-x xevent) (motion-event-y xevent)) (idle)) - ((eq? (event-type button-press) - (any-event-type xevent)) + ((and (eq? (event-type button-press) + (any-event-type xevent)) + (window-exists? dpy window)) (let* ((x (button-event-x xevent)) (y (button-event-y xevent)) (dir (resizer-direction wm client x y))) @@ -80,7 +82,11 @@ prev-rect dir))))))) (idle)))) (free-gc dpy gc) - (for-each (lambda (c) (free-cursor dpy (cdr c))) cursors))))) + (for-each (lambda (c) (free-cursor dpy (cdr c))) cursors))) + window)) + +(define (destroy-resizer dpy resizer) + (destroy-window dpy resizer)) (define (rubber-draw dpy gc rect) (draw-rectangle dpy (default-root-window dpy) gc diff --git a/src/move-wm.scm b/src/move-wm.scm index ace33c0..1b9b6e5 100644 --- a/src/move-wm.scm +++ b/src/move-wm.scm @@ -10,73 +10,103 @@ (border-colors colors '("#333333" "#dddddd")) ) -(define (create-move-wm out-channel dpy parent options . children) - (create-wm dpy parent options children +(define (create-move-wm out-channel dpy parent options default-options + . children) + (create-wm dpy parent options default-options children (manager-type move) move-wm-options-spec out-channel (lambda (wm in-channel) - (spawn* (list 'move-wm wm) - (lambda (release) - (release) - (move-wm-handler wm in-channel))) + (init-move-wm wm in-channel) wm))) -(define (move-wm-handler wm channel) +(define (init-move-wm wm channel) (let* ((dpy (wm:dpy wm)) (window (wm:window wm)) (gc (create-gc dpy window '()))) - (let loop () - (let ((msg (receive channel))) - (case (car msg) - ((draw-main-window) - (set-gc-foreground! dpy gc (black-pixel dpy)) - (fill-rectangle* dpy window gc - (clip-rectangle dpy window))) + (spawn* (list 'move-wm wm) + (lambda (release) + (release) + (call-with-current-continuation + (lambda (exit) + (let loop () + (let ((msg (receive channel))) + (handle-message wm gc exit msg) + (loop))))) + (free-gc dpy gc))))) - ((fit-windows) - (map (lambda (client) - (assert-client-visible wm client)) - (wm-clients wm))) - - ((init-client) - (init-client wm (second msg) (third msg))) - ((deinit-client) - (deinit-client wm (second msg))) - - ((configure-window) - (let ((window (second msg)) - (changes (third msg))) - ;; TODO: exact sizes ?! - (configure-window dpy window - (append (make-window-change-alist - (border-width 0)) - changes)))) +(define (handle-message wm gc exit msg) + (let ((dpy (wm:dpy wm)) + (window (wm:window wm))) + (case (car msg) + ((wait) + (let ((sp (second msg)) + (message (third msg))) + (handle-message wm gc + (lambda args + (sync-point-release sp) + (apply exit args)) + message) + (sync-point-release sp))) + + ((deinit-manager) + (exit 'deinit-manager)) - ((draw-client-window) - (draw-client-window wm (second msg) gc)) - ((fit-client) - ;; client-window changed it's size - (fit-client-windows wm (second msg))) - - ((fit-client-window) - ;; client changed it's size ?? - (fit-client-window wm (second msg))) - - ((manager-focused) #t) + ((draw-main-window) + (set-gc-foreground! dpy gc (black-pixel dpy)) + (fill-rectangle* dpy window gc + (clip-rectangle dpy window))) - ((update-client-state) - (let* ((client (second msg)) - (dpy (wm:dpy wm)) - (window (client:window client)) - (state (if (window-contains-focus? dpy window) - 'focused - 'normal)) - (titlebar (car (client:data client))) - (name (client-name (wm:dpy wm) client))) - (set-titlebar-title+state! titlebar name state))) - )) - (loop)) - (free-gc (wm:dpy wm) gc))) + ((update-manager-state) #t) + + ((fit-windows) + (map (lambda (client) + (assert-client-visible wm client)) + (wm-clients wm))) + + ((init-client) + (init-client wm (second msg) (third msg))) + + ((deinit-client) + (deinit-client wm (second msg))) + + ((configure-window) + (let ((window (second msg)) + (changes (third msg))) + ;; TODO: exact sizes ?! + (configure-window dpy window + (append (make-window-change-alist + (border-width 0)) + changes)))) + + ((draw-client-window) + (draw-client-window wm (second msg) gc)) + + ((fit-client) + ;; client-window changed it's size + (fit-client-windows wm (second msg))) + + ((fit-client-window) + ;; client changed it's size ?? + (fit-client-window wm (second msg))) + + ((manager-focused) #t) + + ((update-client-state) + (let* ((client (second msg)) + (focused? (third msg)) + (state (if focused? + 'focused + 'normal)) + (titlebar (car (client:data client)))) + (set-titlebar-state! titlebar state))) + + ((update-client-name) + (let ((client (second msg)) + (name (third msg))) + (let ((titlebar (car (client:data client)))) + (set-titlebar-title! titlebar name)))) + + (else (warn "unhandled move-wm message" wm msg))))) (define (init-client wm client maybe-rect) (let ((dpy (wm:dpy wm))) @@ -127,7 +157,8 @@ (delete-window dpy (client:window client) (second msg))) )))) ;; TODO: internal channel - (loop)))) + (loop)) + (destroy-resizer dpy resizer))) (map-titlebar titlebar) (if (window-exists? dpy (client:window client)) @@ -138,8 +169,8 @@ (let ((options (wm:options wm))) (create-titlebar channel (wm:dpy wm) (client:client-window client) (wm:colormap wm) - ;; TODO: buttons - (list (cons 'normal-colors + (list (cons 'buttons '(kill maximize)) + (cons 'normal-colors (get-option options 'titlebar-colors)) (cons 'active-colors (get-option options'titlebar-colors-focused)) @@ -222,8 +253,20 @@ (if maybe-rect maybe-rect (let* ((dpy (wm:dpy wm)) - (w.h (desired-size/hints dpy win - (maximal-size/hints dpy win 400 200))) + (default-width 400) + (default-height 200) + (w.h-1 + (let ((w.h (minimal-size/hints dpy win default-width + default-height))) + (cons (if (< default-width (car w.h)) + (car w.h) + default-width) + (if (< default-height (cdr w.h)) + (cdr w.h) + default-height)))) + (w.h-2 (maximal-size/hints dpy win (car w.h-1) (cdr w.h-1))) + (w.h (desired-size/hints dpy win w.h-2)) + ;; TODO: look for a free position ?! Transients centered? (x.y (desired-position/hints dpy win (cons 0 0)))) (make-rectangle (car x.y) (cdr x.y) (car w.h) (cdr w.h))))) diff --git a/src/split-wm.scm b/src/split-wm.scm index 7836381..79c1603 100644 --- a/src/split-wm.scm +++ b/src/split-wm.scm @@ -17,100 +17,128 @@ ;; | | | | | ;; ---------- ---------- -(define (create-split-wm external-in-channel dpy parent options . children) - (create-wm dpy parent options children +(define (create-split-wm external-in-channel dpy parent options + default-options . children) + (create-wm dpy parent options default-options children (manager-type split) split-wm-options-spec external-in-channel (lambda (wm in-channel) - (spawn* (list 'split-wm wm) - (lambda (release) - (release) - (split-wm-handler wm in-channel))) + (init-split-wm wm in-channel) wm))) -(define (split-wm? wm) - (and (wm? wm) (eq? (wm:type wm) (manager-type split)))) +(define-record-type split-data :split-data + (make-split-data resizer first-client second-client) + split-data? + (resizer data:resizer) + (first-client data:first-client set-data:first-client!) + (second-client data:second-client set-data:second-client!)) -(define (split-wm-handler wm channel) - (let ((resizer-window (create-resizer wm)) - (dpy (wm:dpy wm)) - (first-client #f) - (second-client #f)) - (map-window (wm:dpy wm) resizer-window) - (let loop () - (let ((msg (receive channel))) - (case (car msg) - ((draw-main-window) #t) - - ((fit-windows) - (fit-windows wm resizer-window first-client second-client)) - - ((init-client) - (let ((client (second msg)) - (first? (not first-client))) - (if first? - (set! first-client client) - (set! second-client client)) - - (set-window-border-width! dpy (client:window client) 0) - (fit-windows wm resizer-window first-client second-client) - - (map-window dpy (client:window client)) - (map-window dpy (client:client-window client)) +(define (init-split-wm wm channel) + (let* ((resizer (create-resizer wm)) + (data (make-split-data resizer #f #f))) + (spawn* (list 'split-wm wm) + (lambda (release) + (map-window (wm:dpy wm) resizer) + (release) + (call-with-current-continuation + (lambda (exit) + (let loop () + (let ((msg (receive channel))) + (handle-message wm channel data exit msg) + (loop))))))))) - (let ((opt (if (eq? (get-option-value (wm:options wm) - 'orientation) - 'horizontal) - (if first? 'select-right 'select-left) - (if first? 'select-down 'select-up)))) - (grab-shortcut dpy (client:client-window client) - (get-option-value (wm:options wm) opt) - (if first? 'select-second 'select-first) - channel #f)) - )) - - ((deinit-client) - (let ((client (second msg))) - (if (eq? client first-client) - (set! first-client #f)) - (if (eq? client second-client) - (set! second-client #f)) - ;; destroy split if only one client left - (if (and (not (and first-client second-client)) - (or first-client second-client)) - (let ((repl (client:window (or first-client second-client)))) - (mdisplay "destroying " wm ". with replacement " repl "\n") - (send (wm:out-channel wm) - (list 'destroy-wm wm repl)))))) - - ((draw-client-window) #t) - - ((fit-client) - ;; client-window changed it's size - (fit-client-windows wm (second msg))) - - ((fit-client-window) - ;; client changed it's size ?? - #t) +(define (handle-message wm channel data exit msg) + (let ((dpy (wm:dpy wm))) + (case (car msg) + ((wait) + (let ((sp (second msg)) + (message (third msg))) + (handle-message wm channel data + (lambda args + (sync-point-release sp) + (apply exit args)) + message) + (sync-point-release sp))) + + ((deinit-manager) + (destroy-window dpy (data:resizer data)) + (exit 'deinit-manager)) - ((manager-focused) - (let ((time (second msg)) - (cc (wm-current-client wm))) - (if cc (wm-select-client wm cc time)))) + ((draw-main-window) #t) + + ((update-manager-state) #t) + + ((fit-windows) + (fit-windows wm data)) + + ((init-client) + (let ((client (second msg)) + (first? (not (data:first-client data)))) + (if first? + (set-data:first-client! data client) + (set-data:second-client! data client)) + + (set-window-border-width! dpy (client:window client) 0) + (fit-windows wm data) + + (map-window dpy (client:window client)) + (map-window dpy (client:client-window client)) - ((update-client-state) #t) + (let ((opt (if (eq? (get-option-value (wm:options wm) + 'orientation) + 'horizontal) + (if first? 'select-right 'select-left) + (if first? 'select-down 'select-up)))) + (grab-shortcut dpy (client:client-window client) + (get-option-value (wm:options wm) opt) + (if first? 'select-second 'select-first) + channel #f)) + )) + + ((deinit-client) + (let ((client (second msg))) + (if (eq? client (data:first-client data)) + (set-data:first-client! data #f)) + (if (eq? client (data:second-client data)) + (set-data:second-client! data #f)) + ;; destroy split if only one client left. replace the + ;; wm by the remaining client. + (let ((first-client (data:first-client data)) + (second-client (data:second-client data))) + (if (and (not (and first-client second-client)) + (or first-client second-client)) + (let ((r (client:window (or first-client second-client)))) + (send (wm:out-channel wm) (list 'destroy-wm wm r))))))) + + ((draw-client-window) #t) + + ((fit-client) + ;; client-window changed it's size + (fit-client-windows wm (second msg))) + + ((fit-client-window) + ;; client changed it's size ?? + #t) - ;; Shortcuts - ((select-first) - (let ((time (second msg))) - (if first-client - (wm-select-client wm first-client time)))) - ((select-second) - (let ((time (second msg))) - (if second-client - (wm-select-client wm second-client time)))) - )) - (loop)))) + ((manager-focused) + (let ((time (second msg)) + (cc (wm-current-client wm))) + (if cc (wm-select-client wm cc time)))) + + ((update-client-state) #t) + ((update-client-name) #t) + + ;; Shortcuts + ((select-first) + (let ((time (second msg))) + (if (data:first-client data) + (wm-select-client wm (data:first-client data) time)))) + ((select-second) + (let ((time (second msg))) + (if (data:second-client data) + (wm-select-client wm (data:second-client data) time)))) + + (else (warn "unhandled split-wm message" wm msg))))) (define (calc-rectangles wm) (let* ((options (wm:options wm)) @@ -141,18 +169,21 @@ (+ (rectangle:height r1) bar-width))))) (list r1 r2 r3))))) -(define (fit-windows wm resizer-window first-client second-client) - (let* ((rects (calc-rectangles wm)) - (dpy (wm:dpy wm))) - (move-resize-window* dpy resizer-window (second rects)) - (if first-client - (move-resize-window* dpy - (client:client-window first-client) - (first rects))) - (if second-client - (move-resize-window* dpy - (client:client-window second-client) - (third rects))))) +(define (fit-windows wm data) + (let ((resizer-window (data:resizer data)) + (first-client (data:first-client data)) + (second-client (data:second-client data))) + (let* ((rects (calc-rectangles wm)) + (dpy (wm:dpy wm))) + (move-resize-window* dpy resizer-window (second rects)) + (if first-client + (move-resize-window* dpy + (client:client-window first-client) + (first rects))) + (if second-client + (move-resize-window* dpy + (client:client-window second-client) + (third rects)))))) (define (fit-client-windows wm client) (let ((dpy (wm:dpy wm))) diff --git a/src/switch-wm.scm b/src/switch-wm.scm index e6c42d6..0f6d9de 100644 --- a/src/switch-wm.scm +++ b/src/switch-wm.scm @@ -9,15 +9,13 @@ (select-previous keys "M-k p") ) -(define (create-switch-wm out-channel dpy parent options . children) - (create-wm dpy parent options children +(define (create-switch-wm out-channel dpy parent options default-options + . children) + (create-wm dpy parent options default-options children (manager-type switch) switch-wm-options-spec out-channel (lambda (wm in-channel) - (spawn* (list 'switch-wm wm) - (lambda (release) - (release) - (switch-wm-handler wm in-channel))) + (init-switch-wm wm in-channel) wm))) (define-record-type switch-wm-data :switch-wm-data @@ -26,7 +24,7 @@ (titlebars data:titlebars set-data:titlebars!) (empty-titlebar data:empty-titlebar)) -(define (switch-wm-handler wm channel) +(define (init-switch-wm wm channel) (let* ((dpy (wm:dpy wm)) (window (wm:window wm)) (options (wm:options wm)) @@ -42,74 +40,111 @@ (get-option-value options 'select-previous) 'select-previous channel #f) - (let loop () - (let ((msg (receive channel))) - (case (car msg) - ((draw-main-window) - (set-gc-foreground! dpy gc (black-pixel dpy)) - (fill-rectangle* dpy window gc - (clip-rectangle dpy window))) + (spawn* (list 'switch-wm wm) + (lambda (release) + (release) + (call-with-current-continuation + (lambda (exit) + (let loop () + (let ((msg (receive channel))) + (handle-message wm data gc exit msg) + (loop))))) + (free-gc dpy gc))))) - ((fit-windows) - (fit-titlebars wm data) - (for-each (lambda (c) - (fit-client-window wm c)) - (wm-clients wm))) - - ((init-client) - (init-client wm data (second msg) (third msg))) - ((deinit-client) - (deinit-client wm data (second msg))) +(define (handle-message wm data gc exit msg) + (let ((dpy (wm:dpy wm)) + (window (wm:window wm))) + (case (car msg) + ((wait) + (let ((sp (second msg)) + (message (third msg))) + (handle-message wm data gc + (lambda args + (sync-point-release sp) + (apply exit args)) + message) + (sync-point-release sp))) - ((configure-window) - (let ((window (second msg)) - (changes (third msg))) - ;; TODO: exact sizes ?! - (configure-window dpy window - (append (make-window-change-alist - (border-width 0)) - changes)))) - - ((draw-client-window) #f) + ((deinit-manager) + (destroy-titlebar (data:empty-titlebar data)) + (for-each (lambda (client.tb) + (destroy-titlebar (cdr client.tb))) + (data:titlebars data)) + (exit 'deinit-manager)) - ((fit-client) - ;; client-window changed it's size - (fit-client wm (second msg))) - - ((fit-client-window) - ;; client changed it's size ?? - (fit-client-window wm (second msg))) + ((draw-main-window) + (set-gc-foreground! dpy gc (black-pixel dpy)) + (fill-rectangle* dpy window gc + (clip-rectangle dpy window))) - ((update-manager-state) - (let ((state (if (window-contains-focus? dpy (wm:window wm)) - 'focused - 'active))) - (set-titlebar-state! empty-titlebar state))) + ((fit-windows) + (fit-titlebars wm data) + (for-each (lambda (c) + (fit-client-window wm c)) + (wm-clients wm))) + + ((init-client) + (init-client wm data (second msg) (third msg))) - ((manager-focused) - (let ((time (second msg)) - (cc (wm-current-client wm))) - (if cc (wm-select-client wm cc time)))) - - ((update-client-state) - (let* ((client (second msg)) - (dpy (wm:dpy wm)) - (window (client:window client))) - (if (window-exists? dpy window) - (let ((state (if (window-contains-focus? dpy window) - 'focused - (if (window-viewable? dpy window) - 'active - 'normal))) - (titlebar (assq/false client (data:titlebars data))) - (name (client-name dpy client))) - (set-titlebar-title+state! titlebar name state))))) + ((deinit-client) + (deinit-client wm data (second msg))) - ((select-next) (select-next-client wm (second msg))) - ((select-previous) (select-previous-client wm (second msg))) - )) - (loop)) - (free-gc (wm:dpy wm) gc))) + ((configure-window) + (let ((window (second msg)) + (changes (third msg))) + ;; TODO: exact sizes ?! + (configure-window dpy window + (append (make-window-change-alist + (border-width 0)) + changes)))) + + ((draw-client-window) #f) + + ((fit-client) + ;; client-window changed it's size + (fit-client wm (second msg))) + + ((fit-client-window) + ;; client changed it's size ?? + (fit-client-window wm (second msg))) + + ((update-manager-state) + (let* ((focused? (second msg)) + (state (if focused? + 'focused + 'active))) + (set-titlebar-state! (data:empty-titlebar data) state))) + + ((manager-focused) + (let ((time (second msg)) + (cc (wm-current-client wm))) + (if cc (wm-select-client wm cc time)))) + + ((update-client-state) + (let* ((client (second msg)) + (focused? (third msg)) + (dpy (wm:dpy wm)) + (window (client:window client)) + (titlebar (assq/false client (data:titlebars data)))) + (if (and titlebar (window-exists? dpy window)) + (let ((state (if focused? + 'focused + (if (window-viewable? dpy window) + 'active + 'normal)))) + (set-titlebar-state! titlebar state))))) + + ((update-client-name) + (let ((client (second msg)) + (name (third msg))) + (let ((titlebar (assq/false client (data:titlebars data)))) + (if titlebar + (set-titlebar-title! titlebar name))))) + + ((select-next) (select-next-client wm (second msg))) + ((select-previous) (select-previous-client wm (second msg))) + + (else (warn "unhandled switch-wm message" wm msg))))) (define (fit-titlebars wm data) (let* ((dpy (wm:dpy wm)) @@ -144,15 +179,16 @@ (data:titlebars data)))))) (define (init-client wm data client maybe-rect) - ;; TODO: transients! (let ((dpy (wm:dpy wm)) (options (wm:options wm))) (let* ((channel (make-channel)) (titlebar (create-client-titlebar channel wm client))) (set-data:titlebars! data (append (data:titlebars data) (list (cons client titlebar)))) + (set-titlebar-title! titlebar (client-name dpy client)) (fit-titlebars wm data) (update-titlebars wm data) + (fit-client-window wm client) (fit-client wm client) @@ -180,8 +216,7 @@ ;; from titlebar-buttons ((kill) (delete-window dpy (client:window client) (second msg))) - (else (mdisplay "unhandled client message: " msg "\n")))) - ;; TODO: internal channel + (else (warn "unhandled client message " wm client msg)))) (loop)))) (map-titlebar titlebar) @@ -193,8 +228,8 @@ (let ((options (wm:options wm))) (create-titlebar channel (wm:dpy wm) (wm:window wm) (wm:colormap wm) - ;; TODO: buttons - (list (cons 'normal-colors + (list (cons 'buttons '(kill)) + (cons 'normal-colors (get-option options 'titlebar-colors)) (cons 'active-colors (get-option options 'titlebar-colors-active)) @@ -207,7 +242,7 @@ (let* ((options (wm:options wm)) (tb (create-titlebar #f (wm:dpy wm) (wm:window wm) (wm:colormap wm) - (list ;; TODO: (cons 'draggable #f) + (list (cons 'normal-colors (get-option options 'titlebar-colors)) (cons 'active-colors