Skip to content

Commit d7bdaf4

Browse files
committed
Modify to allow setting macros from code
1 parent 7b154bb commit d7bdaf4

File tree

3 files changed

+227
-16
lines changed

3 files changed

+227
-16
lines changed

keyboard.go

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ type Device struct {
1919
Keyboard UpDowner
2020
Mouse Mouser
2121
Override [][]Keycode
22-
Macros [2048]byte
2322
Combos [32][5]Keycode
23+
Macros [16]int
2424

2525
Debug bool
2626
flashCh chan bool
@@ -40,6 +40,8 @@ type Device struct {
4040
combosKey uint32
4141
combosFounds []Keycode
4242

43+
MacroBuf [2048]byte
44+
4345
tapOrHold map[uint32]time.Time
4446

4547
pressToReleaseBuf []uint32
@@ -123,7 +125,7 @@ func (d *Device) Init() error {
123125
keys := d.GetMaxKeyCount()
124126

125127
// TODO: refactor
126-
rbuf := make([]byte, 4+layers*keyboards*keys*2+len(device.Macros)+
128+
rbuf := make([]byte, 4+layers*keyboards*keys*2+len(device.MacroBuf)+
127129
len(device.Combos)*len(device.Combos[0])*2)
128130
_, err := machine.Flash.ReadAt(rbuf, 0)
129131
if err != nil {
@@ -147,12 +149,24 @@ func (d *Device) Init() error {
147149
}
148150
}
149151

150-
macroSize := len(device.Macros)
151-
for i, b := range rbuf[offset : offset+macroSize] {
152-
if b == 0xFF {
153-
b = 0
152+
macroSize := len(device.MacroBuf)
153+
allFF := true
154+
for _, b := range rbuf[offset : offset+macroSize] {
155+
if b != 0xFF {
156+
allFF = false
157+
}
158+
}
159+
160+
if !allFF {
161+
for i, b := range rbuf[offset : offset+macroSize] {
162+
device.MacroBuf[i] = b
163+
}
164+
macros := bytes.SplitN(d.MacroBuf[:], []byte{0x00}, 16)
165+
ofs := 0
166+
for i, v := range macros {
167+
d.Macros[i] = ofs + len(v)
168+
ofs += len(v) + 1
154169
}
155-
device.Macros[i] = b
156170
}
157171
offset += macroSize
158172

@@ -642,9 +656,13 @@ func (d *Device) Tick() error {
642656
}
643657

644658
func (d *Device) RunMacro(no uint8) error {
645-
macros := bytes.SplitN(d.Macros[:], []byte{0x00}, 16)
659+
d.resetMacros()
646660

647-
macro := macros[no]
661+
macro := d.MacroBuf[d.Macros[no]:]
662+
idx := bytes.Index(macro, []byte{0x00})
663+
if idx >= 0 {
664+
macro = macro[:idx]
665+
}
648666

649667
for i := 0; i < len(macro); {
650668
if macro[i] == 0x01 {
@@ -659,10 +677,10 @@ func (d *Device) RunMacro(no uint8) error {
659677
sz := 3
660678
if p[1] > 0x04 {
661679
kc = Keycode(p[2]) + Keycode(p[3])<<8
680+
kc = keycodeViaToTGK(kc)
662681
sz += 1
663682
}
664683
i += sz
665-
kc = keycodeViaToTGK(kc)
666684

667685
switch p[1] {
668686
case 0x01, 0x05:

macros.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
//go:build tinygo
2+
3+
package keyboard
4+
5+
import (
6+
"bytes"
7+
"fmt"
8+
"time"
9+
)
10+
11+
type MacroDown Keycode
12+
13+
type MacroUp Keycode
14+
15+
// resetMacros resets the macro index table based on the current macro buffer.
16+
// It updates the `d.Macros` array so that each index points to the start of a macro in `d.MacroBuf`.
17+
// Macros are stored sequentially in `d.MacroBuf`, each ending with a null byte (0x00).
18+
func (d *Device) resetMacros() {
19+
offset := 0
20+
for i := range d.Macros {
21+
d.Macros[i] = offset
22+
next := bytes.IndexByte(d.MacroBuf[offset:], 0x00)
23+
if next < 0 {
24+
break
25+
}
26+
offset += next + 1
27+
}
28+
}
29+
30+
// calcMacroSize calculates the required buffer size to store the given macro sequence.
31+
// The macro sequence consists of various types: strings, keycodes, time durations, etc.
32+
// Note: Originally, we wanted to store macros using TGK keycodes, but due to the complexity
33+
// of handling interactions with Vial, we abandoned that approach.
34+
func calcMacroSize(m ...any) int {
35+
sz := 0
36+
for _, a := range m {
37+
switch v := a.(type) {
38+
case string:
39+
sz += len(v)
40+
case time.Duration:
41+
sz += 4
42+
case int:
43+
kc := keycodeTGKtoVia(Keycode(v))
44+
sz += 3
45+
if kc > 0xFF {
46+
sz++
47+
}
48+
case Keycode:
49+
kc := keycodeTGKtoVia(Keycode(v))
50+
sz += 3
51+
if kc > 0xFF {
52+
sz++
53+
}
54+
case MacroDown:
55+
kc := keycodeTGKtoVia(Keycode(v))
56+
sz += 3
57+
if kc > 0xFF {
58+
sz++
59+
}
60+
case MacroUp:
61+
kc := keycodeTGKtoVia(Keycode(v))
62+
sz += 3
63+
if kc > 0xFF {
64+
sz++
65+
}
66+
default:
67+
// Skip unsupported types
68+
}
69+
}
70+
return sz
71+
}
72+
73+
// SetMacros sets a macro at the specified index in `d.MacroBuf`.
74+
// It first validates the index, then updates the macro buffer while ensuring
75+
// proper alignment of stored macros. If the new macro size differs from the old one,
76+
// it shifts the subsequent macros accordingly.
77+
// Note: Originally, we wanted to store macros using TGK keycodes, but due to the complexity
78+
// of handling interactions with Vial, we abandoned that approach.
79+
func (d *Device) SetMacro(index int, m ...any) error {
80+
if index < 0 || len(d.Macros) <= index {
81+
return fmt.Errorf("invalid macro index: %d", index)
82+
}
83+
84+
d.resetMacros()
85+
86+
macro := d.MacroBuf[d.Macros[index]:]
87+
totalSize := 0
88+
for _, v := range d.Macros {
89+
idx := bytes.IndexByte(d.MacroBuf[v:], 0x00)
90+
if idx >= 0 {
91+
totalSize += idx
92+
}
93+
}
94+
95+
size := calcMacroSize(m...)
96+
oldSize := bytes.IndexByte(macro, 0x00)
97+
if size > oldSize {
98+
// If the new macro is larger, shift the subsequent macros backward
99+
if index < len(d.Macros) {
100+
for i := totalSize - 1; i >= d.Macros[index] && (i+size) < len(d.MacroBuf); i-- {
101+
d.MacroBuf[i+size] = d.MacroBuf[i+oldSize]
102+
}
103+
}
104+
d.resetMacros()
105+
} else if size < oldSize {
106+
// If the new macro is smaller, shift the subsequent macros forward
107+
if index < len(d.Macros) {
108+
for i := d.Macros[index]; i < totalSize; i++ {
109+
d.MacroBuf[i+size] = d.MacroBuf[i+oldSize]
110+
}
111+
}
112+
d.resetMacros()
113+
}
114+
115+
ofs := 0
116+
for _, a := range m {
117+
switch v := a.(type) {
118+
case string:
119+
copy(macro[ofs:], []byte(v))
120+
ofs += len(v)
121+
case int:
122+
kc := keycodeTGKtoVia(Keycode(v))
123+
macro[ofs+0] = 0x01
124+
macro[ofs+1] = 0x01
125+
macro[ofs+2] = byte(kc)
126+
if kc <= 0x00FF {
127+
ofs += 3
128+
} else {
129+
macro[ofs+1] += 4
130+
macro[ofs+3] = byte(kc >> 8)
131+
ofs += 4
132+
}
133+
case time.Duration:
134+
ms := v.Milliseconds()
135+
if ms == 0 && v > 0 {
136+
ms = 1
137+
}
138+
macro[ofs+0] = 0x01
139+
macro[ofs+1] = 0x04
140+
macro[ofs+2] = byte(ms%255 + 1)
141+
macro[ofs+3] = byte(ms/255 + 1)
142+
ofs += 4
143+
case Keycode:
144+
kc := keycodeTGKtoVia(Keycode(v))
145+
macro[ofs+0] = 0x01
146+
macro[ofs+1] = 0x01
147+
macro[ofs+2] = byte(kc)
148+
if kc <= 0x00FF {
149+
ofs += 3
150+
} else {
151+
macro[ofs+1] += 4
152+
macro[ofs+3] = byte(kc >> 8)
153+
ofs += 4
154+
}
155+
case MacroDown:
156+
kc := keycodeTGKtoVia(Keycode(v))
157+
macro[ofs+0] = 0x01
158+
macro[ofs+1] = 0x02
159+
macro[ofs+2] = byte(kc)
160+
if kc <= 0x00FF {
161+
ofs += 3
162+
} else {
163+
macro[ofs+1] += 4
164+
macro[ofs+3] = byte(kc >> 8)
165+
ofs += 4
166+
}
167+
case MacroUp:
168+
kc := keycodeTGKtoVia(Keycode(v))
169+
macro[ofs+0] = 0x01
170+
macro[ofs+1] = 0x03
171+
macro[ofs+2] = byte(kc)
172+
if kc <= 0x00FF {
173+
ofs += 3
174+
} else {
175+
macro[ofs+1] += 4
176+
macro[ofs+3] = byte(kc >> 8)
177+
ofs += 4
178+
}
179+
default:
180+
// Skip unsupported types
181+
}
182+
}
183+
184+
macro[ofs] = 0x00
185+
ofs++
186+
187+
// Update macro index table
188+
for i := index + 1; i < len(d.Macros); i++ {
189+
d.Macros[i] += ofs
190+
}
191+
192+
return nil
193+
}

via.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,19 +154,19 @@ func rxHandler2(b []byte) bool {
154154
}
155155

156156
case viaCommandDynamicKeymapMacroGetBufferSize:
157-
sz := len(device.Macros)
157+
sz := len(device.MacroBuf)
158158
txb[1] = byte(sz >> 8)
159159
txb[2] = byte(sz)
160160
case viaCommandDynamicKeymapMacroGetCount:
161161
txb[1] = 0x10
162162
case viaCommandDynamicKeymapMacroGetBuffer:
163163
offset := (uint16(b[1]) << 8) + uint16(b[2])
164164
sz := b[3]
165-
copy(txb[4:4+sz], device.Macros[offset:])
165+
copy(txb[4:4+sz], device.MacroBuf[offset:])
166166
case viaCommandDynamicKeymapMacroSetBuffer:
167167
offset := (uint16(b[1]) << 8) + uint16(b[2])
168168
sz := b[3]
169-
copy(device.Macros[offset:], txb[4:4+sz])
169+
copy(device.MacroBuf[offset:], txb[4:4+sz])
170170
device.flashCh <- true
171171
case viaCommandGetKeyboardValue:
172172
Changed = false
@@ -289,7 +289,7 @@ func Save() error {
289289
keyboards := device.GetKeyboardCount()
290290

291291
cnt := device.GetMaxKeyCount()
292-
wbuf := make([]byte, 4+layers*keyboards*cnt*2+len(device.Macros)+
292+
wbuf := make([]byte, 4+layers*keyboards*cnt*2+len(device.MacroBuf)+
293293
len(device.Combos)*len(device.Combos[0])*2)
294294
needed := int64(len(wbuf)) / machine.Flash.EraseBlockSize()
295295
if needed == 0 {
@@ -319,8 +319,8 @@ func Save() error {
319319
}
320320
}
321321

322-
macroSize := len(device.Macros)
323-
copy(wbuf[offset:offset+macroSize], device.Macros[:])
322+
macroSize := len(device.MacroBuf)
323+
copy(wbuf[offset:offset+macroSize], device.MacroBuf[:])
324324
offset += macroSize
325325

326326
for _, combo := range device.Combos {

0 commit comments

Comments
 (0)