summary refs log tree commit diff stats
path: root/lib/keys.sls
diff options
context:
space:
mode:
authorCase Duckworth2024-01-17 00:24:11 -0600
committerCase Duckworth2024-01-17 00:24:11 -0600
commit8179c0025a44a769ab6ad2e5c0a58f545517280c (patch)
tree8de19b673bbddbfcbb03c7d2363ca937ccf4aa3b /lib/keys.sls
parentUpdate .gitignore (diff)
downloadacdwm-b21871cf2d002b62c52eb75f0687cf1bf8b7877f.tar.gz
acdwm-b21871cf2d002b62c52eb75f0687cf1bf8b7877f.zip
Change organization and massively refactor v001
Diffstat (limited to 'lib/keys.sls')
-rw-r--r--lib/keys.sls252
1 files changed, 252 insertions, 0 deletions
diff --git a/lib/keys.sls b/lib/keys.sls new file mode 100644 index 0000000..3be0384 --- /dev/null +++ b/lib/keys.sls
@@ -0,0 +1,252 @@
1;;; (acdwm keys) --- key (and button!) binding tools
2
3(import (chicken bitwise)
4 (chicken irregex)
5 (chicken memory)
6 (srfi 1)
7 (acdwm xlib)
8 (acdwm util))
9
10;;; Defining bindings
11
12(define-public bindings
13 (make-parameter '()))
14
15(export keybind keybind?
16 keybind-keycode keybind-modmask keybind-proc)
17(define-record keybind
18 (setter keycode)
19 (setter modmask)
20 (setter proc))
21
22(export mousebind mousebind?
23 mousebind-button mousebind-modmask mousebind-proc)
24(define-record mousebind
25 (setter button)
26 (setter modmask)
27 (setter proc))
28
29(define-public (bind kbd proc #!optional (dpy (get-display)))
30 ;;; Public interface for make-(keybind|mousebind).
31 (receive (key mods) (parse-bind-description kbd)
32 (print "binding " kbd " to " proc)
33 (let ((bind (if (<= 1 key 5)
34 (make-mousebind key mods proc)
35 (make-keybind (->keycode key dpy) mods proc))))
36 (bindings (cons bind (bindings)))
37 (print "bindings: " (bindings)))))
38
39(define-public (unbind kbd)
40 (receive (key mods) (parse-bind-description kbd)
41 #f ; TODO
42 ))
43
44;;; Matching bindings
45
46(define-public (keybind-match? kb ev #!optional (dpy (get-display)))
47 (and (or (keypress? ev) (keyrelease? ev))
48 (keybind? kb)
49 (and (= (xevent-xkey-keycode ev) (keybind-keycode kb))
50 (= (clean-mask (xevent-xkey-state ev)) (keybind-modmask kb))
51 (procedure? (keybind-proc kb)))))
52
53(define-public (find-keybinding ev)
54 (find (lambda (b) (keybind-match? b ev)) (bindings)))
55
56(define-public (mousebind-match? mb ev #!optional (dpy (get-display)))
57 (and (or (buttonpress? ev) (buttonrelease? ev))
58 (mousebind? mb)
59 (and (= (xevent-xbutton-button ev) (mousebind-button mb))
60 (= (clean-mask (xevent-xbutton-state ev)) (mousebind-modmask mb))
61 (procedure? (mousebind-proc mb)))))
62
63(define-public (find-mousebinding ev)
64 (find (lambda (b) (mousebind-match? b ev)) (bindings)))
65
66;;; Numlock & Modkeys
67
68(define numlock
69 (make-parameter 0))
70
71(define-public (update-numlock-mask #!optional (dpy (get-display)))
72 (call-with-modifier-keymap
73 (lambda (mm)
74 (let ((max-key-per-mod (xmodifierkeymap-max_keypermod mm)))
75 (do ((i 0 (add1 i)))
76 ((= i 8))
77 (do ((k 0 (add1 k)))
78 ((= k max-key-per-mod))
79 (let ((r (pointer-u8-ref ; a KeyBind is an unsigned char
80 (pointer+ (xmodifierkeymap-modifiermap mm)
81 (+ i (* max-key-per-mod k))))))
82 (when (= r (->keycode XK_NUM_LOCK))
83 (numlock (arithmetic-shift 1 i))))))))))
84
85(define-public (clean-mask mask)
86 (bitwise-and mask
87 (bitwise-not (bitwise-ior (numlock) LOCKMASK))
88 (bitwise-ior SHIFTMASK CONTROLMASK
89 MOD1MASK MOD2MASK MOD3MASK MOD4MASK MOD5MASK)))
90
91;;; Grabbing input
92
93(define *mods
94 (begin (update-numlock-mask)
95 (list 0 LOCKMASK (numlock) (bitwise-ior (numlock) LOCKMASK))))
96
97(define (grab-key key #!optional (dpy (get-display)))
98 (when (keybind? key)
99;; XXX (dprint "grabbing " )
100 (for-each (lambda (mod)
101 (xgrabkey dpy
102 (keybind-keycode key)
103 (bitwise-ior mod (keybind-modmask key))
104 (get-root dpy)
105 TRUE
106 GRABMODEASYNC
107 GRABMODEASYNC))
108 *mods)))
109
110(define (grab-button btn #!optional (dpy (get-display)))
111 (when (mousebind? btn)
112 (for-each (lambda (mod)
113 (xgrabbutton dpy
114 (mousebind-button btn)
115 (bitwise-ior mod (mousebind-modmask btn))
116 (get-root dpy)
117 TRUE
118 (bitwise-ior BUTTONPRESSMASK
119 BUTTONRELEASEMASK
120 POINTERMOTIONMASK)
121 GRABMODEASYNC
122 GRABMODEASYNC
123 NONE
124 NONE))
125 *mods)))
126
127(define-public (grab-input #!optional
128 (bindings (bindings))
129 (dpy (get-display)))
130 (xungrabkey dpy ANYKEY ANYMODIFIER (get-root dpy))
131 (for-each grab-key (filter keybind? bindings))
132 (for-each grab-button (filter mousebind? bindings)))
133
134;;; Parsing keybind descriptions
135;; `bind' does Emacs-style binding descriptions in DESC. That is, it accepts a
136;; string of the form MODS-KEY, where
137;; - MODS is one of C, S, M, 1, 2, 3, 4, 5 (M = 1) (case-insensitive)
138;; - KEY is one of
139;; - - mouse buttons b1, b2, b3, b4, b5 (case-insensitive)
140;; - - <D?XK_NAME> is passed through as a symbol
141;; - - otherwise it's figured out through the `keycaps' alist
142
143;; NOTE: parse-bind-description returns a KeySym
144(define-public (parse-bind-description desc)
145 (let* ((m (match-bind-desc desc))
146 (p (cond
147 ((not m) #f)
148 ((irregex-match-substring m 'btn) => parse-button-match)
149 ((irregex-match-substring m 'raw) => parse-raw-match)
150 ((irregex-match-substring m 'key) => parse-key-match)
151 (else #f))))
152 (if p
153 (values p (extract-modmask m))
154 (values #f #f))))
155
156(define (match-bind-desc desc)
157 (irregex-match '(: (? (=> mods (+ (or "C-" "S-" "M-"
158 "1-" "2-" "3-" "4-" "5-"))))
159 (or
160 (: (or #\B #\b) (=> btn ("12345")))
161 (: #\< (=> raw (+ (or alnum #\_))) #\>)
162 (=> key any)))
163 desc))
164
165(define (extract-modmask match)
166 (let ((m (irregex-match-substring match 'mods)))
167 (irregex-fold "[CSM12345]-"
168 (lambda (i n s)
169 (bitwise-ior s (case (string-ref
170 (irregex-match-substring n) 0)
171 ((#\C #\c) CONTROLMASK)
172 ((#\S #\s) SHIFTMASK)
173 ((#\M #\m #\1) MOD1MASK)
174 ((#\2) MOD2MASK)
175 ((#\3) MOD3MASK)
176 ((#\4) MOD4MASK)
177 ((#\5) MOD5MASK))))
178 0
179 (or m ""))))
180
181(define (parse-button-match btn)
182 (string->number btn))
183
184(define (parse-raw-match raw)
185 (string->keysym (string-append (if (irregex-match? "^D?XK_.*" raw) ""
186 "XK_")
187 (string-upcase raw))))
188
189(define (char<= . cs)
190 (apply <= (map char->integer cs)))
191
192(define (parse-key-match key)
193 (let ((ch (string-ref key 0)))
194 (cond
195 ((char<= #\a ch #\z)
196 (string->keysym (string-append "XK_LC" (string (char-upcase ch)))))
197 ((char<= #\A ch #\Z)
198 (string->keysym (string-append "XK_" (string ch))))
199 ((alist-ref ch keycaps))
200 (else
201 (let ((ks (char->integer (xstringtokeysym key))))
202 (if (zero? ks) #f ks))))))
203
204(define (string->keysym str)
205 (symbol->keysym (string->symbol str)))
206
207(define (string-upcase str)
208 (list->string (map char-upcase (string->list str))))
209
210(define keycaps
211 `((#\` . #x60) ; y no XK_GRAVE
212 (#\1 . ,XK_1)
213 (#\2 . ,XK_2)
214 (#\3 . ,XK_3)
215 (#\4 . ,XK_4)
216 (#\5 . ,XK_5)
217 (#\6 . ,XK_6)
218 (#\7 . ,XK_7)
219 (#\8 . ,XK_8)
220 (#\9 . ,XK_9)
221 (#\0 . ,XK_0)
222 (#\- . ,XK_MINUS)
223 (#\= . ,XK_EQUAL)
224 (#\[ . ,XK_BRACKETLEFT)
225 (#\] . ,XK_BRACKETRIGHT)
226 (#\\ . ,XK_BACKSLASH)
227 (#\; . ,XK_SEMICOLON)
228 (#\' . #x27) ; y no XK_APOSTROPHE
229 (#\, . ,XK_COMMA)
230 (#\. . ,XK_PERIOD)
231 (#\/ . ,XK_SLASH)
232 (#\~ . ,XK_ASCIITILDE)
233 (#\! . ,XK_EXCLAM)
234 (#\@ . ,XK_AT)
235 (#\# . ,XK_NUMBERSIGN)
236 (#\$ . ,XK_DOLLAR)
237 (#\% . ,XK_PERCENT)
238 (#\^ . ,XK_ASCIICIRCUM)
239 (#\& . ,XK_AMPERSAND)
240 (#\* . ,XK_ASTERISK)
241 (#\( . ,XK_PARENLEFT)
242 (#\) . ,XK_PARENRIGHT)
243 (#\_ . ,XK_UNDERSCORE)
244 (#\+ . ,XK_PLUS)
245 (#\{ . ,XK_BRACELEFT)
246 (#\} . ,XK_BRACERIGHT)
247 (#\| . ,XK_BAR)
248 (#\: . ,XK_COLON)
249 (#\" . ,XK_QUOTEDBL)
250 (#\< . ,XK_LESS)
251 (#\> . ,XK_GREATER)
252 (#\? . ,XK_QUESTION)))