From 6fff51cc5f586196d0eeb26a516fedfdd7640539 Mon Sep 17 00:00:00 2001 From: Phil Hagelberg Date: Tue, 2 Jul 2019 18:11:04 -0700 Subject: [PATCH] Support fn layer and arbitrary functions in layouts. There is still a bug when you release the fn key but still have another key held down, along with a failing test case. --- Makefile | 7 ++++--- keycodes.scm | 37 +++++++++++++++++++++++++++++++--- layout.scm | 56 +++++++++++++++++++++++++++++++++++++++++++--------- menelaus.scm | 37 +++++++++++++++++++++++++--------- test.rkt | 34 +++++++++++++++++++++---------- 5 files changed, 137 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index 24c6f73..a4cb6d0 100644 --- a/Makefile +++ b/Makefile @@ -14,8 +14,9 @@ upload: $(TARGET).hex test: ; racket test.rkt -clean: - -rm -f $(TARGET){,.hex} *.o *.elf *.s +clean: ; -rm -f $(TARGET){,.hex} *.o *.elf *.s + +count: ; cloc *.scm $(TARGET).hex: $(TARGET).elf avr-size $(TARGET).elf @@ -31,4 +32,4 @@ usb_keyboard.s: usb_keyboard.h usb_keyboard.c avr-gcc -std=gnu99 -S -D F_CPU=$(F_CPU)UL -mmcu=$(MCU) -c \ -o usb_keyboard.s usb_keyboard.c -.PHONY: build upload test clean +.PHONY: build upload test clean count diff --git a/keycodes.scm b/keycodes.scm index 66e34c1..fd01419 100644 --- a/keycodes.scm +++ b/keycodes.scm @@ -26,13 +26,38 @@ (define key-x 27) (define key-y 28) (define key-z 29) -(define key-semicolon 51) +(define key-1 30) +(define key-2 31) +(define key-3 32) +(define key-4 33) +(define key-5 34) +(define key-6 35) +(define key-7 36) +(define key-8 37) +(define key-9 38) +(define key-0 39) + +(define key-up 82) +(define key-down 81) +(define key-left 80) +(define key-right 79) + +(define key-page-up 75) +(define key-page-down 78) +(define key-home 74) +(define key-end 77) +(define key-insert 73) + +(define key-semicolon 51) (define key-comma 54) (define key-period 55) (define key-slash 56) (define key-dash 45) (define key-quote 52) +(define key-equal 46) +(define key-left-bracket 47) +(define key-right-bracket 48) (define key-space 44) (define key-backspace 42) @@ -43,12 +68,18 @@ (define key-backslash 49) (define key-backtick 53) +(define key-vol-up 128) +(define key-vol-down 129) + (define (modifier? keycode) (list? keycode)) (define (modify keycode) (list keycode)) (define (unmodify keycode) (car keycode)) +;; currently you can only combo with one modifier and one normal key +(define (combo modifier keycode) (list (car modifier) keycode)) +(define (uncombo keycode) (and (= 2 (length keycode)) (car (cdr keycode)))) + (define mod-ctrl (modify #x01)) (define mod-shift (modify #x02)) (define mod-alt (modify #x04)) -(define mod-gui (modify #x08)) -(define mod-fn (modify #x16)) +(define mod-super (modify #x08)) diff --git a/layout.scm b/layout.scm index a14aa7a..66117b1 100644 --- a/layout.scm +++ b/layout.scm @@ -1,12 +1,50 @@ -(define layout - (vector key-q key-w key-e key-r key-t key-backslash - key-y key-u key-i key-o key-p +;;; this is the multidvorak layout - key-a key-s key-d key-f key-g key-backtick - key-h key-j key-k key-l key-semicolon +;; we have to declare this up front and set it later because of circularity +(define layers (vector #f #f #f)) +(define current-layer #f) +(define momentary-layer #f) - key-z key-x key-c key-v key-b mod-alt - key-n key-m key-comma key-period key-slash +(define (fn) + (set! momentary-layer (vector-ref layers 1))) - key-esc key-tab mod-gui mod-shift key-backspace mod-ctrl - key-space mod-fn key-dash key-quote key-enter)) +(define (set-layer-2) + (set! current-layer (vector-ref layers 2))) + +(define (set-layer-0) + (set! current-layer (vector-ref layers 0))) + +(vector-set! + layers 0 + (vector key-q key-w key-e key-r key-t key-backslash + key-y key-u key-i key-o key-p + + key-a key-s key-d key-f key-g key-backtick + key-h key-j key-k key-l key-semicolon + + key-z key-x key-c key-v key-b mod-alt + key-n key-m key-comma key-period key-slash + + key-esc key-tab mod-super mod-shift key-backspace mod-ctrl + key-space fn key-quote key-right-bracket key-enter)) + +(vector-set! + layers 1 + (vector (combo mod-shift key-1) (combo mod-shift key-2) key-up + (combo mod-shift key-dash) (combo mod-shift key-equal) + 0 key-page-up key-7 key-8 key-9 (combo mod-shift key-8) + + (combo mod-shift key-3) key-left key-down key-right + (combo mod-shift key-4) 0 + key-page-down key-4 key-5 key-6 (combo mod-shift key-right-bracket) + + key-dash key-equal (combo mod-shift key-9) + (combo mod-shift key-0) (combo mod-shift key-7) mod-alt + key-backtick key-1 key-2 key-3 key-backslash + + set-layer-2 key-insert mod-super mod-shift key-backspace mod-ctrl + key-space fn key-dash key-e key-0 key-right-bracket)) + +(vector-set! + layers 2 ; TODO + (vector 0)) diff --git a/menelaus.scm b/menelaus.scm index 4ea2432..fe9a0f1 100644 --- a/menelaus.scm +++ b/menelaus.scm @@ -8,7 +8,7 @@ (define max-keys 6) ; a single USB frame can only send 6 keycodes plus modifiers -;;; matrix +;;;;;;;;;;;;;;;;;;; matrix (define (offset-for row col) (+ col (* row (length columns)))) @@ -42,7 +42,7 @@ (car rows-left) columns))) (scan-matrix keys-pressed key-count (cdr rows-left))))) -;;; debouncing +;;;;;;;;;;;;;;;;;;; debouncing (define this-scan (vector 0 0 0 0 0 0 0)) (define last-scan (vector 0 0 0 0 0 0 0)) @@ -50,29 +50,46 @@ (define debounce-passes 8) (define (debounce-matrix keys-pressed last-count passes-left) - (vector-copy this-scan last-scan 0 6 0) + ;; older versions of microscheme don't have vector-copy!, only vector-copy + ;; which does the same thing but takes the arguments in a different order + (vector-copy! last-scan 0 this-scan 0 6) (if (< 0 passes-left) (let ((this-count (scan-matrix this-scan 1 rows))) (if (and (= this-count last-count) (equal? this-scan last-scan)) (debounce-matrix keys-pressed this-count (- passes-left 1)) (debounce-matrix keys-pressed this-count passes-left))) - (begin (vector-copy this-scan keys-pressed 0 6 0) + (begin (vector-copy! keys-pressed 0 this-scan 0 6) last-count))) -;;; layout +;;;;;;;;;;;;;;;;;;; layout + +(define (lookup keys-pressed which-key) + (let ((layout (or momentary-layer current-layer))) + (vector-ref layout (vector-ref keys-pressed which-key)))) (define (keycode-for keys-pressed which-key keycodes) - (let ((code (vector-ref layout (vector-ref keys-pressed which-key)))) + (let ((code (lookup keys-pressed which-key))) ;; (printf "keycode ~s ~s~n" code which-key) (if (modifier? code) (begin (vector-set! keycodes 0 (+ (vector-ref keycodes 0) (unmodify code))) - #f) - code))) + (uncombo code)) + (and (not (procedure? code)) code)))) + +(define (call-functions keys-pressed key-count) + (if (< 0 key-count) + (let ((code (lookup keys-pressed key-count))) + (if (procedure? code) + (code) + #f) + (call-functions keys-pressed (- key-count 1))) + #f)) ;; translate key numbers into specific USB keycodes (define (keycodes-for keys-pressed key-count keycodes) + ;; this happens before we look up "regular" keycodes because it changes layers + (call-functions keys-pressed key-count) (if (= 0 key-count) (vector->list keycodes) (let ((keycode (keycode-for keys-pressed key-count keycodes))) @@ -81,9 +98,10 @@ #f) (keycodes-for keys-pressed (- key-count 1) keycodes)))) -;;; showtime +;;;;;;;;;;;;;;;;;;; showtime (define (init) + (set-layer-0) (for-each-vector output row-pins) (for-each-vector high row-pins) (for-each-vector input column-pins) @@ -96,6 +114,7 @@ (call-c-func "usb_send" modifiers key1 key2 key3 key4 key5 key6)) (define (loop) + (set! momentary-layer #f) (free! (let ((keys-pressed (vector 0 0 0 0 0 0 0))) ;; scanning the matrix tells us only which physical keys were ;; pressed and how many; it doesn't tell us which keycodes to diff --git a/test.rkt b/test.rkt index 5c85788..ad4aceb 100644 --- a/test.rkt +++ b/test.rkt @@ -13,13 +13,8 @@ (define (high pin) (vector-set! pins pin #t)) (define (low pin) (vector-set! pins pin #f)) -;; microscheme has this as a separate form but it's just for -(define (for-each-vector f v) (for ([x v]) (f x))) - -(define (vector-copy src dest src-start src-finish dest-start) - ;; ignore src-start and dest-start - (for ([i (range src-finish)]) - (vector-set! dest i (vector-ref src i)))) +;; microscheme has this as a separate form +(define for-each-vector vector-map) (define last-usb-frame #f) ; save this off so we can test it @@ -49,12 +44,31 @@ (define (make-test-data) ;; have to put this in a function so we can internal-define; eww (include "keycodes.scm") - ;; pair of pins/keycodes - `(((1) . (0 ,key-w 0 0 0 0 0)) + ;; each test case is a pair of inputs->outputs + ;; inputs are a list of keys (by offset), outputs are elements of a USB frame + `(;; single key + ((3) . (0 ,key-r 0 0 0 0 0)) + ;; another single key ((2) . (0 ,key-e 0 0 0 0 0)) + ;; multiple normal keys + ((2 3) . (0 ,key-e ,key-r 0 0 0 0)) + ;; modifier keys (alt) ((27) . (4 0 0 0 0 0 0)) + ;; two modifiers (shift+alt) get ORed together + ((27 36) . (6 0 0 0 0 0 0)) + ;; modifier (shift) and normal key ((36 4) . (2 ,key-t 0 0 0 0 0)) - ((36 4 6) . (2 ,key-t ,key-y 0 0 0 0)))) + ;; modifier and multiple normal keys + ((36 4 6) . (2 ,key-t ,key-y 0 0 0 0)) + ;; fn key alone + ((40) . (0 0 0 0 0 0 0)) + ;; fn key and normal key + ((40 1) . (2 ,key-2 0 0 0 0 0)) + ;; fn key and modifier and normal key + ((40 35 2) . (8 ,key-up 0 0 0 0 0)) + ;; releasing fn should leave the previously-pressed key on the fn layer!!! + ;; ((2) . (0 ,key-up 0 0 0 0 0)) + )) (define test-data (make-test-data))