(define-options-spec move-wm-options-spec (titlebar-colors colors '("#aaaaaa" "#eeeeee" "#777777" "black")) (titlebar-colors-focused colors '("#666699" "#aaaacc" "#333366" "#eeeeee")) (titlebar-height int 18) (titlebar-style symbol 'flat) (font font "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*") (border-width int 3) (corner-width int 10) (border-style symbol 'raised) ;; raised | sunken | flat (border-colors colors '("#333333" "#dddddd")) ) (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) (init-move-wm wm in-channel) wm))) (define (init-move-wm wm channel) (let* ((dpy (wm:dpy wm)) (window (wm:window wm)) (gc (create-gc 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))))) (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-main-window) (set-gc-foreground! dpy gc (black-pixel dpy)) (fill-rectangle* dpy window gc (clip-rectangle dpy window))) ((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))) (let* ((r (initial-client-rect wm (client:window client) maybe-rect)) (channel (make-channel)) (titlebar (create-client-titlebar channel wm client)) (resizer (create-resizer wm client)) (options (wm:options wm))) (set-client:data! client (list titlebar resizer)) (set-titlebar-title! titlebar (client-name dpy client)) (let ((bw (get-option-value options 'border-width)) (th (get-option-value options 'titlebar-height))) (move-resize-window dpy (client:client-window client) (rectangle:x r) (rectangle:y r) (+ (rectangle:width r) (* 2 bw)) (+ (rectangle:height r) (* 2 bw) th))) (fit-client-windows wm client) (install-dragging-control channel dpy (titlebar:window titlebar) (client:client-window client)) (spawn* (list 'move-wm-client-handler wm client) (lambda (release) (release) (let loop () (select* (wrap (receive-rv channel) (lambda (msg) (case (car msg) ((drop) ;; check if outside... (let ((window-x (second msg)) (window-y (third msg)) (root-x (fourth msg)) (root-y (fifth msg))) (let ((r (root-rectangle dpy (wm:window wm)))) (if (point-in-rectangle? r root-x root-y) (move-window dpy (client:client-window client) window-x window-y) (send (wm:out-channel wm) (list 'root-drop (client:window client) root-x root-y)))))) ((click) (wm-select-client wm client (fourth msg))) ;; from titlebar-buttons ((kill) (delete-window dpy (client:window client) (second msg))) )))) ;; TODO: internal channel (loop)) (destroy-resizer dpy resizer))) (map-titlebar titlebar) (if (window-exists? dpy (client:window client)) (map-window dpy (client:window client))) (map-window dpy (client:client-window client))))) (define (create-client-titlebar channel wm client) (let ((options (wm:options wm))) (create-titlebar channel (wm:dpy wm) (client:client-window client) (wm:colormap wm) (list (cons 'buttons '(kill maximize)) (cons 'normal-colors (get-option options 'titlebar-colors)) (cons 'active-colors (get-option options'titlebar-colors-focused)) (cons 'focused-colors (get-option options 'titlebar-colors-focused)) (cons 'border-style (get-option options 'titlebar-style)))))) (define (deinit-client wm client) (let ((dpy (wm:dpy wm))) #t)) ;; *** (define (fit-client-windows wm client) (let* ((dpy (wm:dpy wm)) (options (wm:options wm)) (border-width (get-option-value options 'border-width)) (titlebar-height (get-option-value options 'titlebar-height)) (wa (get-window-attributes dpy (client:client-window client)))) ;; TODO: is called much too often (if (window-exists? dpy (client:window client)) (move-resize-window dpy (client:window client) border-width (+ border-width titlebar-height) (- (window-attribute:width wa) (* 2 border-width)) (- (window-attribute:height wa) (+ (* 2 border-width) titlebar-height)))) (move-resize-titlebar (car (client:data client)) (make-rectangle border-width border-width (- (window-attribute:width wa) (* 2 border-width)) titlebar-height)))) (define (fit-client-window wm client) (let* ((dpy (wm:dpy wm)) (options (wm:options wm)) (border-width (get-option-value options 'border-width)) (titlebar-height (get-option-value options 'titlebar-height))) (if (window-exists? dpy (client:window client)) (let ((wa (get-window-attributes dpy (client:window client)))) (resize-window dpy (client:client-window client) (+ (window-attribute:width wa) (* 2 border-width)) (+ (window-attribute:height wa) (* 2 border-width) titlebar-height)))))) (define (assert-client-visible wm client) (let* ((dpy (wm:dpy wm)) (win (client:client-window client)) (x (window-x dpy win)) (y (window-y dpy win))) #t)) ;; ... TODO (define (draw-client-window wm client gc) (let* ((options (wm:options wm)) (colors (get-option-value options 'border-colors)) (window (client:client-window client)) (dpy (wm:dpy wm)) (border-style (get-option-value options 'border-style)) (border-width (get-option-value options 'border-width)) (clip-rect (clip-rectangle dpy window))) (if (not (eq? border-style 'flat)) (let ((light (if (eq? border-style 'sunken) (car colors) (cadr colors))) (dark (if (eq? border-style 'sunken) (cadr colors) (car colors)))) (for-each (lambda (i) (let ((r (make-rectangle (+ i (rectangle:x clip-rect)) (+ i (rectangle:y clip-rect)) (- (rectangle:width clip-rect) (* i 2)) (- (rectangle:height clip-rect) (* i 2))))) (draw-shadow-rectangle dpy window gc r light dark))) (iota border-width)))))) (define (initial-client-rect wm win maybe-rect) (if maybe-rect maybe-rect (let* ((dpy (wm:dpy wm)) (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)))))