161 lines
5.1 KiB
Scheme
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)))
|