orion-wm/src/switch-wm.scm

318 lines
9.8 KiB
Scheme

(define-options-spec switch-wm-options-spec
(titlebar-colors colors '("#aaaaaa" "#eeeeee" "#777777" "black"))
(titlebar-colors-focused colors '("#666699" "#aaaacc" "#333366" "#eeeeee"))
(titlebar-colors-active colors '("#9999aa" "#eeeeff" "#777788" "black"))
(titlebar-height int 18)
(titlebar-style symbol 'raised)
(font font "-*-helvetica-medium-r-normal-*-12-*-*-*-*-*-*-*")
(select-next keys "M-k n")
(select-previous keys "M-k p")
)
(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)
(init-switch-wm wm in-channel)
wm)))
(define-record-type switch-wm-data :switch-wm-data
(make-switch-wm-data titlebars empty-titlebar)
switch-wm-data?
(titlebars data:titlebars set-data:titlebars!)
(empty-titlebar data:empty-titlebar))
(define (init-switch-wm wm channel)
(let* ((dpy (wm:dpy wm))
(window (wm:window wm))
(options (wm:options wm))
(gc (create-gc dpy window '()))
(empty-titlebar (create-empty-titlebar wm))
(data (make-switch-wm-data '() empty-titlebar)))
(update-titlebars wm data)
(grab-shortcut dpy window
(get-option-value options 'select-next)
'select-next channel #f)
(grab-shortcut dpy window
(get-option-value options 'select-previous)
'select-previous channel #f)
(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)))))
(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)))
((deinit-manager)
(destroy-titlebar (data:empty-titlebar data))
(for-each (lambda (client.tb)
(destroy-titlebar (cdr client.tb)))
(data:titlebars data))
(exit 'deinit-manager))
((draw-main-window)
(set-gc-foreground! dpy gc (black-pixel dpy))
(fill-rectangle* dpy window gc
(clip-rectangle dpy window)))
((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)))
((iconify-client maximize-client) #t)
((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)))
((show-clients)
(let ((clients (second msg)))
;; it's a list of a client and it's transients.
(let ((cc (wm-current-client wm))
(top (last clients)))
(if (and cc (window-mapped? dpy (client:client-window cc)))
(unmap-window dpy (client:client-window cc)))
(map-window dpy (client:client-window top)))))
(else (warn "unhandled switch-wm message" wm msg)))))
(define (fit-titlebars wm data)
(let* ((dpy (wm:dpy wm))
(width (window-width dpy (wm:window wm)))
(height (window-height dpy (wm:window wm)))
(titlebar-height (get-option-value (wm:options wm) 'titlebar-height)))
(if (> titlebar-height 0)
(begin
(move-resize-titlebar (data:empty-titlebar data)
(make-rectangle 0 0 width titlebar-height))
(let* ((titlebars (map cdr (data:titlebars data)))
(n (length titlebars))
(widths (if (zero? n) '()
(let ((dw (quotient width n)))
(append (map (lambda (_) dw) (iota (- n 1)))
(list (- width (* dw (- n 1)))))))))
(for-each (lambda (i width titlebar)
(move-resize-titlebar
titlebar
(make-rectangle (* i width) 0
width titlebar-height)))
(iota n) widths titlebars))))))
(define (update-titlebars wm data)
(if (> (get-option-value (wm:options wm) 'titlebar-height) 0)
(if (null? (data:titlebars data))
(map-titlebar (data:empty-titlebar data))
(begin
(unmap-titlebar (data:empty-titlebar data))
(for-each (lambda (c.t)
(map-titlebar (cdr c.t)))
(data:titlebars data))))))
(define (init-client wm data client maybe-rect)
(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)
(install-dragging-control channel dpy
(titlebar:window titlebar)
(titlebar:window titlebar))
(spawn*
(list 'switch-wm-client-handler wm client)
(lambda (release)
(release)
(let loop ()
(let ((msg (receive channel)))
(case (car msg)
((drop)
;; check if outside...
(let ((root-x (fourth msg))
(root-y (fifth msg)))
(let ((r (root-rectangle dpy (wm:window wm))))
(if (not (point-in-rectangle? r root-x root-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)))
(else (warn "unhandled client message " wm client msg))))
(loop))))
(map-titlebar titlebar)
(if (window-exists? dpy (client:window client))
(map-window dpy (client:window client)))
(wm-select-client wm client current-time))))
(define (create-client-titlebar channel wm client)
(let ((options (wm:options wm)))
(create-titlebar channel (wm:dpy wm) (wm:window wm)
(wm:colormap wm)
(list (cons 'buttons '(kill))
(cons 'normal-colors
(get-option options 'titlebar-colors))
(cons 'active-colors
(get-option options 'titlebar-colors-active))
(cons 'focused-colors
(get-option options 'titlebar-colors-focused))
(cons 'border-style
(get-option options 'titlebar-style))))))
(define (create-empty-titlebar wm)
(let* ((options (wm:options wm))
(tb
(create-titlebar #f (wm:dpy wm) (wm:window wm) (wm:colormap wm)
(list
(cons 'normal-colors
(get-option options 'titlebar-colors))
(cons 'active-colors
(get-option options 'titlebar-colors-active))
(cons 'focused-colors
(get-option options 'titlebar-colors-focused))
(cons 'border-style
(get-option options 'titlebar-style))
(cons 'buttons '())))))
(set-titlebar-title! tb "<empty frame>")
tb))
(define (deinit-client wm data client)
(let ((dpy (wm:dpy wm))
(tb (assq/false client (data:titlebars data))))
(set-data:titlebars! data (filter (lambda (c.t)
(not (eq? (car c.t) client)))
(data:titlebars data)))
(if tb (destroy-titlebar tb))
(fit-titlebars wm data)
(update-titlebars wm data)
(if (eq? client (wm-current-client wm))
(if (null? (wm-clients wm))
(set-input-focus dpy (wm:window wm) (revert-to parent)
current-time)
(wm-select-client wm (car (wm-clients wm)) current-time)))))
;; ***
(define (fit-client wm client)
(let ((window (client:window client))
(dpy (wm:dpy wm)))
(if (window-exists? dpy window)
(maximize-window dpy window))))
(define (fit-client-window wm client)
(let* ((dpy (wm:dpy wm))
(w (wm:window wm))
(options (wm:options wm))
(titlebar-height (get-option-value options 'titlebar-height)))
(move-resize-window dpy (client:client-window client)
0 titlebar-height
(window-width dpy w) (- (window-height dpy w)
titlebar-height))))
;; ***
(define (select-next-client* wm clients time)
(let ((cc (wm-current-client wm)))
(let loop ((l (append clients clients)))
(and (not (null? l))
(if (eq? (car l) cc)
(and (not (null? (cdr l)))
(wm-select-client wm (cadr l) time))
(loop (cdr l)))))))
(define (select-next-client wm time)
(select-next-client* wm (wm-clients wm) time))
(define (select-previous-client wm time)
(select-next-client* wm (reverse (wm-clients wm)) time))