(define-module (ttn-do zzz x-kbgrunge)
#:export (mk-key
key-sym
key-mod
symbol<-keysym
keysym<-symbol
kthx-core)
#:use-module ((ice-9 optargs-kw) #:select (lambda*
define*
let-optional*
let-keywords*))
#:use-module ((srfi srfi-1) #:select (remove!
any))
#:use-module ((ttn-do zzz x-protocol) #:select ((-x . zx)
(-event-data . key-ev-data)
(-internal-data . zxint)))
#:use-module ((ttn-do zzz personally) #:select (FE whatever)))
(define K2S (make-hash-table 71))
(define S2K (make-hash-table 71))
(define (mk-key keysym modifier)
(cons keysym modifier))
(define (key-sym key)
(car key))
(define (key-mod key)
(cdr key))
(define (symbol<-keysym keysym)
(hashq-ref K2S keysym))
(define (keysym<-symbol symbol)
(hashq-ref S2K symbol))
(define upper-of (let* ((a (char->integer #\a))
(z (char->integer #\z))
(diff (- (char->integer #\A) a)))
(lambda (keysym)
(and (<= a keysym z)
(+ diff keysym)))))
(define (upcased-maybe keysym)
(or (upper-of keysym)
keysym))
(define symbolic-modifiers
(let ((masks (map (lambda (pair)
(cons (ash 1 (cdr pair)) (car pair)))
(reverse (hash-ref (zxint 'ENUMS) 'MapIndex)))))
(lambda (n)
(let loop ((ls masks) (rv '()))
(if (null? ls)
rv
(let ((pair (car ls)))
(loop (cdr ls)
(if (positive? (logand n (car pair)))
(cons (cdr pair) rv)
rv))))))))
(define (latin1<-keysym keysym control?) (let ((hi (ash keysym -8))
(lo (logand keysym #xff)))
(cond ((zero? keysym)
#f)
((not (or (zero? hi)
(and (= #xff hi)
(let ((sym (symbol<-keysym keysym)))
(define (in-range? bot top)
(<= (keysym<-symbol bot)
keysym
(keysym<-symbol top)))
(define (one-of? . list)
(memq sym list))
(or (in-range? 'BackSpace 'Clear)
(one-of? 'Return
'Escape
'KP_Space
'KP_Tab
'KP_Enter)
(in-range? 'KP_Multiply 'KP_9)
(one-of? 'KP_Equal
'Delete))))))
#f)
(else
(and (or (eq? 'KP_Space (symbol<-keysym keysym)) (= hi #xff))
(set! lo (logand lo #x7f)))
(set! lo (cond ((not control?)
lo)
((or (and (<= (char->integer #\@) lo)
(> #o177 lo))
(= (char->integer #\space) lo))
(logand #x1f lo))
((= (char->integer #\2) lo)
0)
((<= (char->integer #\3) lo (char->integer #\7))
(- lo (- (char->integer #\3) #o33)))
((= (char->integer #\8) lo)
#o177)
((= (char->integer #\/) lo)
(logand (char->integer #\_) #x1f))
(else
lo)))
(string (integer->char lo))))))
(define ALL-MODIFIERS '(Hyper_R
Hyper_L
Super_R
Super_L
Alt_R
Alt_L
Meta_R
Meta_L
Shift_Lock
Caps_Lock
Control_R
Control_L
Shift_R
Shift_L
ISO_Last_Group_Lock
ISO_Last_Group
ISO_First_Group_Lock
ISO_First_Group
ISO_Prev_Group_Lock
ISO_Prev_Group
ISO_Next_Group_Lock
ISO_Next_Group
ISO_Group_Lock
ISO_Group_Latch
ISO_Group_Shift
ISO_Level3_Lock
ISO_Level3_Latch
ISO_Level3_Shift
ISO_Level2_Latch
ISO_Lock
Mode_switch
Num_Lock))
(define* (kthx-core conn #:key
(finangle-meta? #f)
(additional-modifiers '()))
(let ((keysyms-ra (whatever))
(look (append
additional-modifiers
'((mode-switch Mode_switch)
(shift-lock Shift_Lock)
(caps-lock Caps_Lock)
(num-lock Num_Lock))
(map (lambda (x)
(let ((base (string-capitalize (symbol->string x))))
(define (with suffix)
(string->symbol (string-append base suffix)))
(list x (with "_L") (with "_R"))))
'(shift
control
meta
alt
super
hyper))))
(canon-mods (whatever))
(misc (map (lambda (x)
(cons x #f))
'(mode-switch-mod
num-lock-mod
lock-mod
meta-finangled?))))
(define (q . x)
(apply (conn #:q) x))
(define (keysyms-ref keycode j)
(array-ref keysyms-ra keycode j))
(define (nice-name ugly)
(let loop ((ls look))
(cond ((null? ls) #f)
((memq ugly (cdar ls)) (caar ls))
(else (loop (cdr ls))))))
(define (misc! name value)
(assq-set! misc name value))
(define (misc: name)
(assq-ref misc name))
(define (grok-keysyms!)
(let* ((setup (conn #:setup))
(void (keysym<-symbol 'VoidSymbol))
(min-keycode (zx 'min-keycode setup))
(max-keycode (zx 'max-keycode setup))
(count (- max-keycode min-keycode -1))
(ra (make-uniform-array 1 (+ min-keycode count) 4))
(kmapping (q 'GetKeyboardMapping
#:first-keycode min-keycode
#:count count))
(keysyms-per-keycode (zx 'keysyms-per-keycode kmapping))
(keysyms (zx 'keysyms kmapping))
(k0 #f) (k1 #f) (k2 #f) (k3 #f))
(define (pop)
(let ((rv (car keysyms)))
(set! keysyms (cdr keysyms))
rv))
(define (take-4!)
(set! k0 (pop))
(set! k1 (pop))
(set! k2 (pop))
(set! k3 (pop)))
(define more!
(case keysyms-per-keycode
((1) (lambda ()
(set! k0 (pop))
(set! k1 void)
(set! k2 void)
(set! k3 void)))
((2) (lambda ()
(set! k0 (pop))
(set! k1 (pop))
(set! k2 void)
(set! k3 void)))
((3) (lambda ()
(set! k0 (pop))
(set! k1 (pop))
(set! k2 (pop))
(set! k3 void)))
((4) take-4!)
(else (let ((dead (- keysyms-per-keycode 4)))
(lambda ()
(take-4!)
(let ignore ((dead dead))
(cond ((zero? dead))
(else (pop) (ignore (1- dead))))))))))
(do ((i min-keycode (1+ i)))
((= (1+ max-keycode) i))
(more!)
(or (symbol<-keysym k0) (set! k0 0))
(or (symbol<-keysym k1) (set! k1 0))
(or (symbol<-keysym k2) (set! k2 0))
(or (symbol<-keysym k3) (set! k3 0))
(cond ((and (zero? k3)
(zero? k2))
(cond ((zero? k1)
(set! k2 k0))
(else
(set! k2 k0)
(set! k3 k1))))
((zero? k3)
(set! k3 void)))
(and (zero? k1) (positive? k0) (set! k1 (upcased-maybe k0)))
(and (zero? k3) (positive? k2) (set! k3 (upcased-maybe k2)))
(array-set! ra k0 i 0)
(array-set! ra k1 i 1)
(array-set! ra k2 i 2)
(array-set! ra k3 i 3))
(set! keysyms-ra ra)))
(define (grok-modifiers!)
(let* ((mmapping (q 'GetModifierMapping))
(keycodes-per-modifier (zx 'keycodes-per-modifier mmapping))
(all-j (iota keycodes-per-modifier))
(all-keysyms-j (iota 4))
(keycodes (zx 'keycodes mmapping))
(canon (map list (map car look))))
(define (extract i)
(char->integer (string-ref keycodes i)))
(FE (iota (/ (string-length keycodes)
keycodes-per-modifier))
(lambda (i)
(let ((mask (ash 1 i)))
(define (grok-binding n)
(define (msym j)
(let ((keysym (keysyms-ref n j)))
(and (positive? keysym)
(symbol<-keysym keysym))))
(let ((name (and (positive? n) (any msym all-keysyms-j))))
(define (symbolic-xmod)
(car (symbolic-modifiers mask)))
(case name
((Mode_switch)
(misc! 'mode-switch-mod (symbolic-xmod)))
((Num_Lock)
(misc! 'num-lock-mod (symbolic-xmod)))
((Shift_Lock Caps_Lock)
(misc! 'lock-mod name)))
(cond ((and name (nice-name name))
=> (lambda (nice)
(let ((pair (assq nice canon)))
(and (null? (cdr pair))
(set-cdr! pair mask)))
#t))
(else #f))))
(any grok-binding
(map (lambda (j)
(extract (+ (* i keycodes-per-modifier) j)))
all-j)))))
(and finangle-meta?
(let ((alt (assq 'alt canon))
(meta (assq 'meta canon)))
(and (null? (cdr meta))
(not (null? (cdr alt)))
(begin (misc! 'meta-finangled? #t)
(set-cdr! meta (cdr alt))
(set-cdr! alt '())))))
(set! canon-mods (remove! (lambda (x)
(null? (cdr x)))
canon))))
(define (symbolic-keypress keycode state)
(define mod?
(let ((mods (symbolic-modifiers state)))
(lambda (x) (memq x mods))))
(define (group-sel a b)
(if (and=> (misc: 'mode-switch-mod) mod?)
b
a))
(define (lock-is? symbol)
(eq? symbol (misc: 'lock-mod)))
(let* ((plain (keysyms-ref keycode (group-sel 0 2)))
(shift (keysyms-ref keycode (group-sel 1 3)))
(same? (eq? plain shift))
(base (symbol<-keysym
(let ((shift? (mod? 'Shift))
(lock? (mod? 'Lock)))
(cond ((and (and=> (misc: 'num-lock-mod) mod?)
(or (<= #xff80 shift #xffbd)
(<= #x11000000 shift #x1100ffff)))
(if (or shift?
(and lock? (lock-is? 'Shift_Lock)))
plain
shift))
((and (not shift?)
(not lock?))
plain)
((and (not shift?)
(lock-is? 'Caps_Lock))
(if (upper-of plain)
shift
plain))
((and shift?
(lock-is? 'Caps_Lock))
(upcased-maybe shift))
((or shift?
(lock-is? 'Shift_Lock))
shift)
(else
plain))))))
(if (memq base ALL-MODIFIERS)
base
(let ((mods '()))
(FE canon-mods (lambda (cmod)
(or (null? (cdr cmod))
(zero? (logand (cdr cmod) state))
(and (eq? 'shift (car cmod))
(not same?))
(set! mods (cons (car cmod) mods)))))
(cons base mods)))))
(define (keypress<-event ev)
(let* ((alist (key-ev-data ev))
(detail (zx 'detail alist))
(state (zx 'state alist))
(tail (symbolic-keypress detail state)))
(cons (if (pair? tail)
(let ((keysym (keysym<-symbol (car tail)))
(control? (memq 'control (cdr tail))))
(and keysym (latin1<-keysym keysym control?)))
(and=> (nice-name tail)
(lambda (nice)
(if (and (eq? 'alt nice)
(misc: 'meta-finangled?))
'meta
nice))))
tail)))
(define (!!-invalid-modifier x)
(throw 'invalid-modifier x))
(define (!!-modifier-not-mapped x)
(throw 'modifier-not-mapped x))
(define (code/modmask<-keypress ls)
(define (keycode<-keysym keysym) (let* ((dims (array-dimensions keysyms-ra))
(max-j (cadr dims))
(max-keycode (car dims)))
(call-with-current-continuation
(lambda (return)
(do ((j 0 (1+ j)))
((= j max-j))
(do ((code 0 (1+ code)))
((= max-keycode code))
(and (= keysym (keysyms-ref code j))
(return code))))
#f))))
(cond ((not (pair? ls))
(cons (or (cond ((assq-ref look ls)
=> (lambda (maybe)
(any keycode<-keysym
(map keysym<-symbol maybe))))
((keysym<-symbol ls)
=> keycode<-keysym)
(else
(!!-invalid-modifier ls)))
(!!-modifier-not-mapped ls))
0))
((keysym<-symbol (car ls))
=> (lambda (keysym)
(let* ((c (or (keycode<-keysym keysym)
(throw 'symbol-not-mapped (car ls))))
(modsyms (cdr ls))
(plain (keysyms-ref c 0))
(shift (keysyms-ref c 1)))
(and (= keysym shift)
(or (= plain shift)
(set! modsyms (cons 'shift modsyms))))
(cons c (apply logior
(map (lambda (sym)
(or (assq sym look)
(!!-invalid-modifier sym))
(or (assq-ref canon-mods sym)
(!!-modifier-not-mapped sym)))
modsyms))))))
(else
(throw 'invalid-symbol (car ls)))))
(define (info what)
(case what
((mode-switch-mod num-lock-mod lock-mod meta-finangled?)
(misc: what))
((valid-modifiers) (map car canon-mods))
(else (error "bad info request:" what))))
(define (modifier-mask nice)
(assq-ref canon-mods nice))
(grok-keysyms!)
(grok-modifiers!)
(lambda (command arg)
((case command
((#:info) info)
((#:modifier-mask) modifier-mask)
((#:keypress<-event) keypress<-event)
((#:code/modmask<-keypress) code/modmask<-keypress)
(else (error "bad command:" command)))
arg))))
(load (in-vicinity (dirname (port-filename (current-load-port)))
"x-kbgrunge.LEARNINGS"))