diff options
author | Case Duckworth | 2024-01-17 00:24:11 -0600 |
---|---|---|
committer | Case Duckworth | 2024-01-17 00:24:11 -0600 |
commit | 8179c0025a44a769ab6ad2e5c0a58f545517280c (patch) | |
tree | 8de19b673bbddbfcbb03c7d2363ca937ccf4aa3b /lib/keys.sls | |
parent | Update .gitignore (diff) | |
download | acdwm-b21871cf2d002b62c52eb75f0687cf1bf8b7877f.tar.gz acdwm-b21871cf2d002b62c52eb75f0687cf1bf8b7877f.zip |
Change organization and massively refactor v001
Diffstat (limited to 'lib/keys.sls')
-rw-r--r-- | lib/keys.sls | 252 |
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))) | ||