orion-wm/src/key-grab.scm

161 lines
5.1 KiB
Scheme

(define-record-type key-grab :key-grab
(make-key-grab window keys message channel override?)
key-grab?
(window key-grab:window)
(keys key-grab:keys)
(message key-grab:message)
(channel key-grab:channel)
(override? key-grab:override?))
(define (create-grab-server dpy)
(let ((in-channel (make-channel)))
(spawn*
'grab-server
(lambda (release)
(release)
(let ((grabs '())
(event-cache '()))
(let loop ()
(let ((msg (receive in-channel)))
(case (car msg)
((add-key-grab)
(let* ((key-grab (second msg))
(first (car (key-grab:keys key-grab))))
(set! grabs (cons key-grab grabs))
(grab-key dpy (key:keycode first)
(key:modifiers first) (key-grab:window key-grab)
#f (grab-mode async) (grab-mode async))
(spawn* (list 'grabber 'on (key-grab:window key-grab))
(lambda (resume)
(call-with-event-channel
dpy (key-grab:window key-grab)
(event-mask key-press structure-notify)
(lambda (event-channel)
(resume)
(let loop ()
(let ((e (receive event-channel)))
(cond
((destroy-window-event? e)
(send in-channel (list 'remove-key-grab
key-grab)))
((eq? (event-type key-press)
(any-event-type e))
(send in-channel (list 'key-press e))
(loop))
(else (loop)))))))))))
((remove-key-grab)
(let* ((key-grab (second msg))
(first (car (key-grab:keys key-grab))))
(if (window-exists? dpy (key-grab:window key-grab))
(ungrab-key dpy (key:keycode first)
(key:modifiers first)
(key-grab:window key-grab)))
(set! grabs (filter (lambda (g) (not (eq? g key-grab)))
grabs))))
((key-press)
(let ((e (second msg))
(events (filter (lambda (x) x)
(map weak-pointer-ref event-cache))))
;; because more of these can come in a row, we
;; skip the ones already handled.
(if (memq e events)
#f
(begin
(set! event-cache
(map make-weak-pointer (cons e events)))
(let ((winner (do-grabs dpy grabs e)))
(if winner
(send (key-grab:channel winner)
(list (key-grab:message winner)
;; this event ??
(key-event-time e)))))))))
))
(loop)))))
in-channel))
(define *grab-server* #f)
(define *grab-server-lock* (make-lock))
(define (grab-shortcut dpy window keys message channel override?)
;; assert |keys| > 0 ??
(with-lock *grab-server-lock*
(lambda ()
(if (not *grab-server*)
(set! *grab-server* (create-grab-server dpy)))))
(let ((key-grab (make-key-grab window keys message channel override?)))
(send *grab-server* (list 'add-key-grab key-grab))
key-grab))
;; unregister-key ??
(define (do-grabs dpy grabs event)
(let* ((fwin (get-input-focus-window dpy)) ;; can be pointer-root and none
(path (reverse (if (or (equal? fwin none)
(equal? fwin pointer-root))
(list (default-root-window dpy))
(window-path dpy fwin))))
;; find all that match the first key and are registered for a
;; window on the path
(grabs (flatten
(map (lambda (win)
(filter
(lambda (grab)
(let ((first (car (key-grab:keys grab))))
(and (equal? win (key-grab:window grab))
(enum-set=? (key:modifiers first)
(key-event-state event))
(equal? (key:keycode first)
(key-event-keycode event)))))
grabs))
path)))
;; make an alist mapping a grab to the keys that are not entered
(grabs-rests (map (lambda (g)
(cons g (cdr (key-grab:keys g))))
grabs))
(winner? (lambda (grabs-rests)
(let ((dones (map car (filter (lambda (grab-rest)
(null? (cdr grab-rest)))
grabs-rests))))
(let loop ((dones dones))
(if (null? dones)
#f
(if (or (null? (cdr dones))
(key-grab:override? (car dones)))
(car dones)
(loop (cdr dones))))))))
(cursor (create-font-cursor dpy xc-icon))) ;; options?
(grab-keyboard dpy (default-root-window dpy) #f
(grab-mode async) (grab-mode async) ;; ??
(key-event-time event))
(define-cursor dpy (default-root-window dpy) cursor)
(let ((result
(call-with-event-channel
dpy (default-root-window dpy) (event-mask key-press)
(lambda (event-channel)
(let loop ((grabs-rests grabs-rests))
(and (not (null? grabs-rests))
(or (winner? grabs-rests)
(let ((e (receive event-channel)))
(cond
((eq? (event-type key-press)
(any-event-type e))
(let ((rest
(filter
(lambda (grab-rest)
(and (not (null? (cdr grab-rest)))
(let ((next (car (cdr grab-rest))))
(enum-set=? (key:modifiers next)
(key-event-state e))
(equal? (key:keycode next)
(key-event-keycode e)))))
grabs-rests)))
(loop (map (lambda (grab-rest)
(cons (car grab-rest)
(cdr (cdr grab-rest))))
rest))))
(else (loop grabs-rests)))))))))))
(undefine-cursor dpy (default-root-window dpy))
(free-cursor dpy cursor)
(ungrab-keyboard dpy current-time)
result)))