kll_core/lib.rs
1// Copyright 2021-2023 Jacob Alexander
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8#![no_std]
9#![feature(let_chains)]
10
11#[macro_use]
12extern crate static_assertions;
13#[macro_use]
14extern crate enum_primitive_derive;
15extern crate num_traits;
16
17mod converters;
18pub mod layout;
19pub mod macros;
20pub use kll_hid;
21
22#[cfg(feature = "defmt")]
23use defmt::{error, trace, warn};
24#[cfg(not(feature = "defmt"))]
25use log::{error, trace, warn};
26
27pub mod hid {
28 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
29 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
30 #[repr(u8)]
31 pub enum Protocol {
32 /// HID boot mode protocol
33 Boot = 0,
34 /// HID Application / NKRO mode protocol
35 Application = 1,
36 /// Toggle between Boot and Application modes
37 Toggle = 3,
38 }
39
40 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
41 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
42 #[repr(u8)]
43 pub enum State {
44 /// Control is enabled / pressed
45 Active = 0,
46 /// Control is disabled / released
47 Inactive = 1,
48 }
49}
50
51pub mod layer {
52 use core::ops::{BitAnd, BitAndAssign, BitOrAssign, Not};
53 use num_traits::FromPrimitive;
54
55 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
56 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
57 #[repr(u8)]
58 pub enum Direction {
59 /// Next layer
60 Next = 0,
61 /// Previous layer
62 Previous = 1,
63 }
64
65 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Primitive)]
66 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
67 #[repr(u8)]
68 pub enum State {
69 /// No layer state
70 Off = 0x00,
71 /// Shift state
72 Shift = 0x01,
73 /// Latch state
74 Latch = 0x02,
75 /// Shift+Latch state
76 ShiftLatch = 0x03,
77 /// Lock state
78 Lock = 0x04,
79 /// Shift+Lock state
80 ShiftLock = 0x05,
81 /// Latch+Lock state
82 LatchLock = 0x06,
83 /// Shift+Latch+Lock state
84 ShiftLatchLock = 0x07,
85 }
86
87 impl State {
88 /// Adds the given state to this state
89 /// This is a bitwise or operation
90 pub fn add(mut self, state: State) {
91 self |= state;
92 }
93
94 /// Removes the given state from this state
95 /// This is a bitwise nand operation
96 pub fn remove(mut self, state: State) {
97 self &= !(state);
98 }
99
100 /// Determine if the given state is present in this state
101 pub fn is_set(&self, state: State) -> bool {
102 if state != State::Off {
103 *self & state != State::Off
104 } else {
105 *self == state
106 }
107 }
108
109 /// Check if layer is active (e.g. not Off)
110 pub fn active(&self) -> bool {
111 *self != State::Off
112 }
113
114 /// Effective state
115 /// If the state is Off or two states are set, set as disabled
116 /// (e.g. Lock + Shift disables the state)
117 pub fn effective(&self) -> bool {
118 match self {
119 State::Off => false,
120 State::Shift => true,
121 State::Latch => true,
122 State::Lock => true,
123 State::ShiftLatch => false,
124 State::ShiftLock => false,
125 State::LatchLock => false,
126 State::ShiftLatchLock => true,
127 }
128 }
129 }
130
131 impl BitAnd for State {
132 type Output = Self;
133
134 fn bitand(self, rhs: Self) -> Self::Output {
135 State::from_u32(self as u32 & rhs as u32).unwrap()
136 }
137 }
138
139 impl BitAndAssign for State {
140 fn bitand_assign(&mut self, rhs: Self) {
141 *self = State::from_u32(*self as u32 & rhs as u32).unwrap()
142 }
143 }
144
145 impl BitOrAssign for State {
146 fn bitor_assign(&mut self, rhs: Self) {
147 *self = State::from_u32(*self as u32 | rhs as u32).unwrap()
148 }
149 }
150
151 impl Not for State {
152 type Output = Self;
153
154 fn not(self) -> Self::Output {
155 State::from_u32(!(self as u32)).unwrap()
156 }
157 }
158}
159
160pub mod pixel {
161 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
162 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
163 #[repr(u8)]
164 pub enum GammaControl {
165 /// Disable gamma correction
166 Disable = 0,
167 /// Enable gamma correction
168 Enable = 1,
169 /// Toggle gamma correction
170 Toggle = 3,
171 }
172
173 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
174 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
175 #[repr(u8)]
176 pub enum AnimationControl {
177 /// Toggles between pause/resume
178 /// Only pauses if the state is either Forward or ForwardOne
179 PauseResume = 0,
180 /// Iterate a single frame
181 ForwardOne = 1,
182 /// Play animations
183 Forward = 2,
184 /// Stop all animations and clear all state
185 Stop = 3,
186 /// Restarts all animations
187 Reset = 4,
188 /// Pause all animations and clear all pixel state
189 WipePause = 5,
190 /// Pause
191 Pause = 6,
192 /// Clear all pixels (does not stop or pause)
193 Clear = 7,
194 }
195
196 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
197 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
198 #[repr(u8)]
199 pub enum FadeCommand {
200 /// Resets fade settings to default
201 Reset = 0,
202 /// Resets all profiles to defaults
203 ResetAll = 1,
204 /// Set fade brightness
205 BrightnessSet = 2,
206 /// Set fade brightness increment
207 BrightnessIncrement = 3,
208 /// Set fade brightness decrement
209 BrightnessDecrement = 4,
210 /// Reset to brightness default
211 BrightnessDefault = 5,
212 }
213
214 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
215 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
216 #[repr(u8)]
217 pub enum PixelTest {
218 /// Disable pixel test mode
219 Off = 0,
220 /// Enable a single channel
221 ChannelSingle = 1,
222 /// Enable a single channel rotate forward (index is used for jump amount)
223 ChannelSingleRotate = 2,
224 /// Enable a single channel rotate in reverse (index is used for jump amount)
225 ChannelSingleRotateReverse = 3,
226 ChannelFlashAll = 4,
227 ChannelRoll = 5,
228 ChannelAllOn = 6,
229 PixelSingle = 7,
230 PixelSingleRotate = 8,
231 PixelSingleRotateReverse = 9,
232 PixelFlashAll = 10,
233 PixelRoll = 11,
234 PixelAllOn = 12,
235 ScanCodeSingle = 13,
236 ScanCodeSingleRotate = 14,
237 ScanCodeSingleRotateReverse = 15,
238 ScanCodeFlashAll = 16,
239 ScanCodeRoll = 17,
240 ScanCodeAllOn = 18,
241 PositionSingle = 19,
242 PositionSingleRotate = 20,
243 PositionSingleRotateReverse = 21,
244 PositionFlashAll = 22,
245 PositionRoll = 23,
246 PositionAllOn = 24,
247 }
248
249 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
250 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
251 #[repr(u8)]
252 pub enum LedControl {
253 /// Decrease LED brightness
254 BrightnessDecrease = 0,
255 /// Increase LED brightness
256 BrightnessIncrease = 1,
257 /// Set brightness
258 BrightnessSet = 2,
259 /// Default brightness
260 BrightnessDefault = 3,
261 /// Enable LEDs
262 EnableLeds = 4,
263 /// Disable LEDs
264 DisableLeds = 5,
265 /// Toggle LEDs On/Off
266 ToggleLeds = 6,
267 /// Set FPS target
268 FpsSet = 7,
269 /// Increase FPS target
270 FpsIncrease = 8,
271 /// Decrease FPS target
272 FpsDecrease = 9,
273 /// Default FPS target
274 FpsDefault = 10,
275 }
276}
277
278/// Global capability list for KLL
279/// NOTE: Changing parameters and removing entries will require a firmware reflash.
280/// At worst, KLL file and compiler definitions may also need to be updated.
281/// Please avoid these kinds of changes if possible.
282/// Adding new entries is safe.
283#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
284#[cfg_attr(feature = "defmt", derive(defmt::Format))]
285#[repr(u8)]
286pub enum Capability {
287 /// No-op / None action
288 /// 4 bytes
289 NoOp {
290 /// Capability state
291 state: CapabilityState,
292 /// Scanning loop condition (number of scanning loops attached to state condition)
293 /// Lookup index
294 loop_condition_index: u16,
295 } = 0,
296 /// Rotation event trigger
297 /// 6 bytes
298 Rotate {
299 /// Capability state
300 state: CapabilityState,
301 /// Scanning loop condition (number of scanning loops attached to state condition)
302 /// Lookup index
303 loop_condition_index: u16,
304 index: u8,
305 increment: i8,
306 } = 1,
307
308 /// Clears all layer states
309 /// NOTE: Does not send trigger events
310 /// 4 bytes
311 LayerClear {
312 /// Capability state
313 state: CapabilityState,
314 /// Scanning loop condition (number of scanning loops attached to state condition)
315 /// Lookup index
316 loop_condition_index: u16,
317 } = 2,
318 /// Updates layer to the specified state
319 /// 6 bytes
320 LayerState {
321 /// Capability state
322 state: CapabilityState,
323 /// Scanning loop condition (number of scanning loops attached to state condition)
324 /// Lookup index
325 loop_condition_index: u16,
326 layer: u8,
327 layer_state: layer::State,
328 } = 3,
329 /// Rotates through possible layers given the direction
330 /// Uses internal state to keep track of the current layer
331 /// 5 bytes
332 LayerRotate {
333 /// Capability state
334 state: CapabilityState,
335 /// Scanning loop condition (number of scanning loops attached to state condition)
336 /// Lookup index
337 loop_condition_index: u16,
338 direction: layer::Direction,
339 } = 4,
340
341 /// HID Protocol Mode
342 /// 5 bytes
343 HidProtocol {
344 /// Capability state
345 state: CapabilityState,
346 /// Scanning loop condition (number of scanning loops attached to state condition)
347 /// Lookup index
348 loop_condition_index: u16,
349 mode: hid::Protocol,
350 } = 5,
351 /// USB HID keyboard event
352 /// Handles press/released based on incoming state
353 /// 5 bytes
354 HidKeyboard {
355 /// Capability state
356 state: CapabilityState,
357 /// Scanning loop condition (number of scanning loops attached to state condition)
358 /// Lookup index
359 loop_condition_index: u16,
360 id: kll_hid::Keyboard,
361 } = 6,
362 /// USB HID keyboard event
363 /// Force state event
364 /// 6 bytes
365 HidKeyboardState {
366 /// Capability state
367 state: CapabilityState,
368 /// Scanning loop condition (number of scanning loops attached to state condition)
369 /// Lookup index
370 loop_condition_index: u16,
371 id: kll_hid::Keyboard,
372 key_state: hid::State,
373 } = 7,
374 /// USB HID Consumer Control Event
375 /// Handles press/released based on incoming state
376 /// 6 bytes
377 HidConsumerControl {
378 /// Capability state
379 state: CapabilityState,
380 /// Scanning loop condition (number of scanning loops attached to state condition)
381 /// Lookup index
382 loop_condition_index: u16,
383 id: kll_hid::ConsumerControl,
384 } = 8,
385 /// USB HID System Control Event
386 /// Handles press/released based on incoming state
387 /// 5 bytes
388 HidSystemControl {
389 /// Capability state
390 state: CapabilityState,
391 /// Scanning loop condition (number of scanning loops attached to state condition)
392 /// Lookup index
393 loop_condition_index: u16,
394 id: kll_hid::SystemControl,
395 } = 9,
396
397 // TODO Mouse Control
398 // TODO Joystick Control
399 /// Enter Flash Mode
400 /// Usually jumps to the bootloader
401 /// 4 bytes
402 McuFlashMode {
403 /// Capability state
404 state: CapabilityState,
405 /// Scanning loop condition (number of scanning loops attached to state condition)
406 /// Lookup index
407 loop_condition_index: u16,
408 } = 10,
409
410 /// Overall animation control
411 /// 5 bytes
412 PixelAnimationControl {
413 /// Capability state
414 state: CapabilityState,
415 /// Scanning loop condition (number of scanning loops attached to state condition)
416 /// Lookup index
417 loop_condition_index: u16,
418 mode: pixel::AnimationControl,
419 },
420 /// Activates the given indexed Animation
421 /// 6 bytes
422 PixelAnimationIndex {
423 /// Capability state
424 state: CapabilityState,
425 /// Scanning loop condition (number of scanning loops attached to state condition)
426 /// Lookup index
427 loop_condition_index: u16,
428 index: u16,
429 },
430 /// Fade control
431 /// 7 bytes
432 PixelFadeControl {
433 /// Capability state
434 state: CapabilityState,
435 /// Scanning loop condition (number of scanning loops attached to state condition)
436 /// Lookup index
437 loop_condition_index: u16,
438 profile: u8,
439 command: pixel::FadeCommand,
440 arg: u8,
441 },
442 /// Layer fade
443 /// 5 bytes
444 PixelFadeLayer {
445 /// Capability state
446 state: CapabilityState,
447 /// Scanning loop condition (number of scanning loops attached to state condition)
448 /// Lookup index
449 loop_condition_index: u16,
450 layer: u8,
451 },
452 /// Fade set profile
453 /// 7 bytes
454 PixelFadeSet {
455 /// Capability state
456 state: CapabilityState,
457 /// Scanning loop condition (number of scanning loops attached to state condition)
458 /// Lookup index
459 loop_condition_index: u16,
460 profile: u8,
461 config: u8,
462 period: u8,
463 },
464 /// Enable/Disable/Toggle gamma correction
465 /// 5 bytes
466 PixelGammaControl {
467 /// Capability state
468 state: CapabilityState,
469 /// Scanning loop condition (number of scanning loops attached to state condition)
470 /// Lookup index
471 loop_condition_index: u16,
472 mode: pixel::GammaControl,
473 },
474 /// LED Control
475 /// 6 bytes
476 PixelLedControl {
477 /// Capability state
478 state: CapabilityState,
479 /// Scanning loop condition (number of scanning loops attached to state condition)
480 /// Lookup index
481 loop_condition_index: u16,
482 mode: pixel::LedControl,
483 amount: u8,
484 },
485 /// Pixel test
486 /// 7 bytes
487 PixelTest {
488 /// Capability state
489 state: CapabilityState,
490 /// Scanning loop condition (number of scanning loops attached to state condition)
491 /// Lookup index
492 loop_condition_index: u16,
493 test: pixel::PixelTest,
494 index: u16,
495 },
496
497 /// Sends URL (using index stored unicode string) to host computer web browser
498 /// 6 bytes
499 HidioOpenUrl {
500 /// Capability state
501 state: CapabilityState,
502 /// Scanning loop condition (number of scanning loops attached to state condition)
503 /// Lookup index
504 loop_condition_index: u16,
505 index: u16,
506 },
507 /// Sends Unicode string (using index stored unicode string) to host computer
508 /// 6 bytes
509 HidioUnicodeString {
510 /// Capability state
511 state: CapabilityState,
512 /// Scanning loop condition (number of scanning loops attached to state condition)
513 /// Lookup index
514 loop_condition_index: u16,
515 index: u16,
516 },
517 /// Sends Unicode character with state (Press or Release)
518 /// 8 bytes
519 HidioUnicodeState {
520 /// Capability state
521 state: CapabilityState,
522 /// Scanning loop condition (number of scanning loops attached to state condition)
523 /// Lookup index
524 loop_condition_index: u16,
525 unicode: char,
526 },
527}
528
529impl Capability {
530 /// Generate a CapabilityRun using a Capability + TriggerEvent
531 /// The TriggerEvent is only important when CapabilityState::Passthrough is set.
532 pub fn generate(&self, event: TriggerEvent, _loop_condition_lookup: &[u32]) -> CapabilityRun {
533 // TODO: Handle loop_condition_index
534 match self {
535 Capability::NoOp { state, .. } => CapabilityRun::NoOp {
536 state: state.event(event),
537 },
538 Capability::HidKeyboard { state, id, .. } => CapabilityRun::HidKeyboard {
539 state: state.event(event),
540 id: *id,
541 },
542 _ => {
543 panic!(
544 "Missing implementation for Capability::generate: {:?}",
545 self
546 );
547 }
548 }
549 }
550
551 /// Lookup loop_condition_index
552 pub fn loop_condition_index(&self) -> u16 {
553 match self {
554 Capability::NoOp {
555 loop_condition_index,
556 ..
557 } => *loop_condition_index,
558 Capability::Rotate {
559 loop_condition_index,
560 ..
561 } => *loop_condition_index,
562 Capability::LayerClear {
563 loop_condition_index,
564 ..
565 } => *loop_condition_index,
566 Capability::LayerState {
567 loop_condition_index,
568 ..
569 } => *loop_condition_index,
570 Capability::LayerRotate {
571 loop_condition_index,
572 ..
573 } => *loop_condition_index,
574 Capability::HidProtocol {
575 loop_condition_index,
576 ..
577 } => *loop_condition_index,
578 Capability::HidKeyboard {
579 loop_condition_index,
580 ..
581 } => *loop_condition_index,
582 Capability::HidKeyboardState {
583 loop_condition_index,
584 ..
585 } => *loop_condition_index,
586 Capability::HidConsumerControl {
587 loop_condition_index,
588 ..
589 } => *loop_condition_index,
590 Capability::HidSystemControl {
591 loop_condition_index,
592 ..
593 } => *loop_condition_index,
594 Capability::McuFlashMode {
595 loop_condition_index,
596 ..
597 } => *loop_condition_index,
598 Capability::PixelAnimationControl {
599 loop_condition_index,
600 ..
601 } => *loop_condition_index,
602 Capability::PixelAnimationIndex {
603 loop_condition_index,
604 ..
605 } => *loop_condition_index,
606 Capability::PixelFadeControl {
607 loop_condition_index,
608 ..
609 } => *loop_condition_index,
610 Capability::PixelFadeLayer {
611 loop_condition_index,
612 ..
613 } => *loop_condition_index,
614 Capability::PixelFadeSet {
615 loop_condition_index,
616 ..
617 } => *loop_condition_index,
618 Capability::PixelGammaControl {
619 loop_condition_index,
620 ..
621 } => *loop_condition_index,
622 Capability::PixelLedControl {
623 loop_condition_index,
624 ..
625 } => *loop_condition_index,
626 Capability::PixelTest {
627 loop_condition_index,
628 ..
629 } => *loop_condition_index,
630 Capability::HidioOpenUrl {
631 loop_condition_index,
632 ..
633 } => *loop_condition_index,
634 Capability::HidioUnicodeString {
635 loop_condition_index,
636 ..
637 } => *loop_condition_index,
638 Capability::HidioUnicodeState {
639 loop_condition_index,
640 ..
641 } => *loop_condition_index,
642 }
643 }
644}
645
646/// CapabilityRun
647/// Used to run capabilities rather than map them out in a result guide
648#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
649#[cfg_attr(feature = "defmt", derive(defmt::Format))]
650#[repr(u8)]
651pub enum CapabilityRun {
652 /// No-op / None action
653 /// 4 bytes
654 NoOp { state: CapabilityEvent } = 0,
655 /// Rotation event trigger
656 /// 6 bytes
657 Rotate {
658 state: CapabilityEvent,
659 index: u8,
660 increment: i8,
661 } = 1,
662
663 /// Clears all layer states
664 /// NOTE: Does not send trigger events
665 /// 4 bytes
666 LayerClear { state: CapabilityEvent } = 2,
667 /// Updates layer to the specified state
668 /// 6 bytes
669 LayerState {
670 state: CapabilityEvent,
671 layer: u8,
672 layer_state: layer::State,
673 } = 3,
674 /// Rotates through possible layers given the direction
675 /// Uses internal state to keep track of the current layer
676 /// 5 bytes
677 LayerRotate {
678 state: CapabilityEvent,
679 direction: layer::Direction,
680 } = 4,
681
682 /// HID Protocol Mode
683 /// 5 bytes
684 HidProtocol {
685 state: CapabilityEvent,
686 mode: hid::Protocol,
687 } = 5,
688 /// USB HID keyboard event
689 /// Handles press/released based on incoming state
690 /// 5 bytes
691 HidKeyboard {
692 state: CapabilityEvent,
693 id: kll_hid::Keyboard,
694 } = 6,
695 /// USB HID keyboard event
696 /// Force state event
697 /// 6 bytes
698 HidKeyboardState {
699 state: CapabilityEvent,
700 id: kll_hid::Keyboard,
701 key_state: hid::State,
702 } = 7,
703 /// USB HID Consumer Control Event
704 /// Handles press/released based on incoming state
705 /// 6 bytes
706 HidConsumerControl {
707 state: CapabilityEvent,
708 id: kll_hid::ConsumerControl,
709 } = 8,
710 /// USB HID System Control Event
711 /// Handles press/released based on incoming state
712 /// 5 bytes
713 HidSystemControl {
714 state: CapabilityEvent,
715 id: kll_hid::SystemControl,
716 } = 9,
717
718 // TODO Mouse Control
719 // TODO Joystick Control
720 /// Enter Flash Mode
721 /// Usually jumps to the bootloader
722 /// 4 bytes
723 McuFlashMode { state: CapabilityEvent } = 10,
724
725 /// USB HID Led event
726 /// Handles press/released based on incoming state
727 /// 5 bytes
728 HidLed {
729 state: CapabilityEvent,
730 id: kll_hid::LedIndicator,
731 } = 11,
732
733 /// Overall animation control
734 /// 5 bytes
735 PixelAnimationControl {
736 state: CapabilityEvent,
737 mode: pixel::AnimationControl,
738 },
739 /// Activates the given indexed Animation
740 /// 6 bytes
741 PixelAnimationIndex { state: CapabilityEvent, index: u16 },
742 /// Fade control
743 /// 7 bytes
744 PixelFadeControl {
745 state: CapabilityEvent,
746 profile: u8,
747 command: pixel::FadeCommand,
748 arg: u8,
749 },
750 /// Layer fade
751 /// 5 bytes
752 PixelFadeLayer { state: CapabilityEvent, layer: u8 },
753 /// Fade set profile
754 /// 7 bytes
755 PixelFadeSet {
756 state: CapabilityEvent,
757 profile: u8,
758 config: u8,
759 period: u8,
760 },
761 /// Enable/Disable/Toggle gamma correction
762 /// 5 bytes
763 PixelGammaControl {
764 state: CapabilityEvent,
765 mode: pixel::GammaControl,
766 },
767 /// LED Control
768 /// 6 bytes
769 PixelLedControl {
770 state: CapabilityEvent,
771 mode: pixel::LedControl,
772 amount: u8,
773 },
774 /// Pixel test
775 /// 7 bytes
776 PixelTest {
777 state: CapabilityEvent,
778 test: pixel::PixelTest,
779 index: u16,
780 },
781
782 /// Analog Event
783 /// Always a passthrough event
784 /// 4 bytes
785 Analog { state: CapabilityEvent },
786
787 /// Sends URL (using index stored unicode string) to host computer web browser
788 /// 6 bytes
789 HidioOpenUrl { state: CapabilityEvent, index: u16 },
790 /// Sends Unicode string (using index stored unicode string) to host computer
791 /// 6 bytes
792 HidioUnicodeString { state: CapabilityEvent, index: u16 },
793 /// Sends Unicode character with state (Press or Release)
794 /// 8 bytes
795 HidioUnicodeState {
796 state: CapabilityEvent,
797 unicode: char,
798 },
799}
800
801impl CapabilityRun {
802 pub fn state(&self) -> CapabilityEvent {
803 match self {
804 CapabilityRun::NoOp { state } => *state,
805 CapabilityRun::Rotate { state, .. } => *state,
806 CapabilityRun::LayerClear { state, .. } => *state,
807 CapabilityRun::LayerState { state, .. } => *state,
808 CapabilityRun::LayerRotate { state, .. } => *state,
809 CapabilityRun::HidProtocol { state, .. } => *state,
810 CapabilityRun::HidKeyboard { state, .. } => *state,
811 CapabilityRun::HidKeyboardState { state, .. } => *state,
812 CapabilityRun::HidConsumerControl { state, .. } => *state,
813 CapabilityRun::HidSystemControl { state, .. } => *state,
814 CapabilityRun::McuFlashMode { state, .. } => *state,
815 CapabilityRun::HidLed { state, .. } => *state,
816 CapabilityRun::PixelAnimationControl { state, .. } => *state,
817 CapabilityRun::PixelFadeControl { state, .. } => *state,
818 CapabilityRun::PixelFadeLayer { state, .. } => *state,
819 CapabilityRun::PixelFadeSet { state, .. } => *state,
820 CapabilityRun::PixelGammaControl { state, .. } => *state,
821 CapabilityRun::PixelLedControl { state, .. } => *state,
822 CapabilityRun::PixelTest { state, .. } => *state,
823 CapabilityRun::Analog { state } => *state,
824 CapabilityRun::HidioOpenUrl { state, .. } => *state,
825 CapabilityRun::HidioUnicodeString { state, .. } => *state,
826 CapabilityRun::HidioUnicodeState { state, .. } => *state,
827 _ => {
828 panic!("CapabilityRun type not handled for state({:?})", self)
829 }
830 }
831 }
832}
833
834// Size validation for Capability
835// DO NOT CHANGE THIS: Will invalidate existing generated KLL layouts
836const_assert_eq!(core::mem::size_of::<Capability>(), 8);
837
838// NOTE: It's not possible to make this a trait (yet)
839impl Capability {
840 /// Convert enum to an array of bytes
841 /// # Safety
842 pub const unsafe fn bytes(&self) -> &[u8] {
843 &*core::ptr::slice_from_raw_parts(
844 (self as *const Self) as *const u8,
845 core::mem::size_of::<Self>(),
846 )
847 }
848
849 /// Convert array of bytes to enum
850 /// # Safety
851 pub const unsafe fn from_byte_array(bytes: [u8; core::mem::size_of::<Self>()]) -> Self {
852 core::mem::transmute(bytes)
853 }
854
855 /// Convert slice of bytes to enum
856 /// Aggressively casts the provide u8 slice to retrieve a Capability
857 /// # Safety
858 pub const unsafe fn from_bytes(bytes: &[u8]) -> Self {
859 core::ptr::read(bytes.as_ptr() as *const &[u8] as *const Self)
860 }
861}
862
863pub enum Vote {
864 /// Successful comparison
865 Positive,
866 /// Negative comparison, should stop this and all future voting for this trigger guide
867 Negative,
868 /// No match, but doesn't exclude future comparisons (e.g. hold event)
869 Insufficient,
870 /// Indicate that this is an off state that need to be processed separately (or later)
871 OffState,
872}
873
874pub mod trigger {
875 use super::*;
876 use num_traits::FromPrimitive;
877
878 /// PHRO - Press/Hold/Release/Off
879 /// Generally used for momentary switches
880 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
881 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
882 #[repr(u8)]
883 pub enum Phro {
884 Press = 1,
885 Hold = 2,
886 Release = 3,
887 Off = 0,
888
889 /// Passthrough TriggerEvent state
890 /// Only used for TriggerConditions
891 Passthrough = 8,
892 }
893
894 impl Phro {
895 /// Given the previous state and current state determine the correct Phro state
896 pub fn from_state(prev_state: bool, cur_state: bool) -> Self {
897 // Off -> On
898 if !prev_state && cur_state {
899 Phro::Press
900 // On -> On
901 } else if prev_state && cur_state {
902 Phro::Hold
903 // On -> Off
904 } else if prev_state && !cur_state {
905 Phro::Release
906 // Off -> Off
907 } else {
908 Phro::Off
909 }
910 }
911
912 /// Compare states including time base
913 /// Used when comparing TriggerEvents to TriggerConditions and whether the event
914 /// satisfies the condition
915 pub fn compare(&self, cond_time: u32, event_state: Self, event_time: u32) -> Vote {
916 // Make sure states match
917 if *self != event_state {
918 // When the condition is an Off state and the event is not
919 // We need to return this status back so we can do a reverse lookup to retrieve
920 // any off state events
921 if *self == Phro::Off {
922 return Vote::OffState;
923 } else {
924 return Vote::Insufficient;
925 }
926 }
927
928 // Evaluate timing
929 match self {
930 Phro::Press => {
931 if event_time >= cond_time {
932 Vote::Positive
933 } else {
934 Vote::Negative
935 }
936 }
937 Phro::Hold => {
938 if event_time >= cond_time {
939 Vote::Positive
940 } else {
941 Vote::Insufficient
942 }
943 }
944 Phro::Release => {
945 if event_time <= cond_time {
946 Vote::Positive
947 } else {
948 Vote::Negative
949 }
950 }
951 Phro::Off => {
952 if event_time >= cond_time {
953 Vote::Positive
954 } else {
955 Vote::Negative
956 }
957 }
958 // Not enough information to determine a resolution
959 _ => Vote::Insufficient,
960 }
961 }
962 }
963
964 /// AODO - Activate/On/Deactivate/Off
965 /// Generally used for maintained switches
966 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
967 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
968 #[repr(u8)]
969 pub enum Aodo {
970 Activate = 1,
971 On = 2,
972 Deactivate = 3,
973 Off = 0,
974
975 /// Passthrough TriggerEvent state
976 /// Only used for TriggerConditions
977 Passthrough = 8,
978 }
979
980 impl Aodo {
981 /// Given the previous state and current state determine the correct Aodo state
982 pub fn from_state(prev_state: bool, cur_state: bool) -> Self {
983 // Off -> On
984 if !prev_state && cur_state {
985 Aodo::Activate
986 // On -> On
987 } else if prev_state && cur_state {
988 Aodo::On
989 // On -> Off
990 } else if prev_state && !cur_state {
991 Aodo::Deactivate
992 // Off -> Off
993 } else {
994 Aodo::Off
995 }
996 }
997 }
998
999 /// DRO - Done/Repeat/Off
1000 /// Generally used for an abstract process, such as an animation sequence.
1001 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1002 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1003 #[repr(u8)]
1004 pub enum Dro {
1005 Off = 0,
1006 Done = 1,
1007 Repeat = 3,
1008
1009 /// Passthrough TriggerEvent state
1010 /// Only used for TriggerConditions
1011 Passthrough = 8,
1012 }
1013
1014 /// LayerState - AODO + Layer Info
1015 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Primitive)]
1016 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1017 #[repr(u8)]
1018 pub enum LayerState {
1019 ShiftActivate = 0x11,
1020 LatchActivate = 0x21,
1021 ShiftLatchActivate = 0x31,
1022 LockActivate = 0x41,
1023 ShiftLockActivate = 0x51,
1024 LatchLockActivate = 0x61,
1025 ShiftLatchLockActivate = 0x71,
1026
1027 ShiftOn = 0x12,
1028 LatchOn = 0x22,
1029 ShiftLatchOn = 0x32,
1030 LockOn = 0x42,
1031 ShiftLockOn = 0x52,
1032 LatchLockOn = 0x62,
1033 ShiftLatchLockOn = 0x72,
1034
1035 ShiftDeactivate = 0x13,
1036 LatchDeactivate = 0x23,
1037 ShiftLatchDeactivate = 0x33,
1038 LockDeactivate = 0x43,
1039 ShiftLockDeactivate = 0x53,
1040 LatchLockDeactivate = 0x63,
1041 ShiftLatchLockDeactivate = 0x73,
1042
1043 ShiftOff = 0x10,
1044 LatchOff = 0x20,
1045 ShiftLatchOff = 0x30,
1046 LockOff = 0x40,
1047 ShiftLockOff = 0x50,
1048 LatchLockOff = 0x60,
1049 ShiftLatchLockOff = 0x70,
1050
1051 /// Passthrough TriggerEvent state
1052 /// Only used for TriggerConditions
1053 Passthrough = 0x08,
1054 }
1055
1056 impl LayerState {
1057 /// Mergers layer::State and Aodo for TriggerEvent::LayerState
1058 pub fn from_layer(layer_state: layer::State, activity_state: Aodo) -> Self {
1059 LayerState::from_u32(((layer_state as u32) << 1) | activity_state as u32).unwrap()
1060 }
1061 }
1062}
1063
1064/// Trigger event definitions
1065///
1066/// last_state is an incrementing counter that increases on every scan loop while the state has not
1067/// changed (e.g. holding a key).
1068#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1069#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1070#[repr(u8)]
1071pub enum TriggerEvent {
1072 None = 0,
1073 Switch {
1074 /// Switch state
1075 state: trigger::Phro,
1076 /// Switch identification index
1077 index: u16,
1078 /// Scanning loops since the last state change (can be 0 if the state just changed)
1079 last_state: u32,
1080 } = 1,
1081 HidLed {
1082 /// LED state
1083 state: trigger::Aodo,
1084 /// HID LED identification (from USB HID spec, e.g. CapsLock)
1085 index: u8,
1086 /// Scanning loops since the last state change (can be 0 if the state just changed)
1087 last_state: u32,
1088 } = 2,
1089 AnalogDistance {
1090 index: u16,
1091 val: i16,
1092 } = 3,
1093 AnalogVelocity {
1094 index: u16,
1095 val: i16,
1096 } = 4,
1097 AnalogAcceleration {
1098 index: u16,
1099 val: i16,
1100 } = 5,
1101 AnalogJerk {
1102 index: u16,
1103 val: i16,
1104 } = 6,
1105 Layer {
1106 state: trigger::LayerState,
1107 layer: u8,
1108 /// Scanning loops since the last state change (can be 0 if the state just changed)
1109 last_state: u32,
1110 } = 7,
1111 Animation {
1112 state: trigger::Dro,
1113 index: u16,
1114 /// Scanning loops since the last state change (can be 0 if the state just changed)
1115 last_state: u32,
1116 } = 8,
1117 Sleep {
1118 state: trigger::Aodo,
1119 /// Scanning loops since the last state change (can be 0 if the state just changed)
1120 last_state: u32,
1121 } = 9,
1122 Resume {
1123 state: trigger::Aodo,
1124 /// Scanning loops since the last state change (can be 0 if the state just changed)
1125 last_state: u32,
1126 } = 10,
1127 Inactive {
1128 state: trigger::Aodo,
1129 /// Scanning loops since the last state change (can be 0 if the state just changed)
1130 last_state: u32,
1131 } = 11,
1132 Active {
1133 state: trigger::Aodo,
1134 /// Scanning loops since the last state change (can be 0 if the state just changed)
1135 last_state: u32,
1136 } = 12,
1137 Rotation {
1138 index: u8,
1139 position: i8,
1140 /// Scanning loops since the last state change (can be 0 if the state just changed)
1141 last_state: u32,
1142 } = 13,
1143}
1144
1145impl TriggerEvent {
1146 /// Convert enum to an array of bytes
1147 /// # Safety
1148 pub const unsafe fn bytes(&self) -> &[u8] {
1149 &*core::ptr::slice_from_raw_parts(
1150 (self as *const Self) as *const u8,
1151 core::mem::size_of::<Self>(),
1152 )
1153 }
1154
1155 /// Convert array of bytes to enum
1156 /// # Safety
1157 pub const unsafe fn from_byte_array(bytes: [u8; core::mem::size_of::<Self>()]) -> Self {
1158 core::mem::transmute(bytes)
1159 }
1160
1161 /// Convert slice of bytes to enum
1162 /// Aggressively casts the provide u8 slice to retrieve a TriggerEvent
1163 /// # Safety
1164 pub const unsafe fn from_bytes(bytes: &[u8]) -> Self {
1165 core::ptr::read(bytes.as_ptr() as *const &[u8] as *const Self)
1166 }
1167
1168 /// Attempts to determine the index value of the event
1169 /// If an index is not valid, return 0 instead (index may not have any meaning)
1170 pub fn index(&self) -> u16 {
1171 match self {
1172 TriggerEvent::None => 0,
1173 TriggerEvent::Switch { index, .. } => *index,
1174 TriggerEvent::HidLed { index, .. } => (*index).into(),
1175 TriggerEvent::AnalogDistance { index, .. } => *index,
1176 TriggerEvent::AnalogVelocity { index, .. } => *index,
1177 TriggerEvent::AnalogAcceleration { index, .. } => *index,
1178 TriggerEvent::AnalogJerk { index, .. } => *index,
1179 TriggerEvent::Layer { layer, .. } => (*layer).into(),
1180 TriggerEvent::Animation { index, .. } => *index,
1181 TriggerEvent::Sleep { .. } => 0,
1182 TriggerEvent::Resume { .. } => 0,
1183 TriggerEvent::Inactive { .. } => 0,
1184 TriggerEvent::Active { .. } => 0,
1185 TriggerEvent::Rotation { index, .. } => (*index).into(),
1186 }
1187 }
1188}
1189
1190// Size validation for TriggerEvent
1191// Less important than TriggerCondition size, but to serve as a check when updating the enum fields
1192// NOTE: TriggerEvents are passed directly to HID-IO, changing the structure and orders will break
1193// apis, so be carefut with modifications.
1194const_assert_eq!(core::mem::size_of::<TriggerEvent>(), 8);
1195
1196/// Trigger condition definitions
1197///
1198/// XXX (HaaTa): Field order is extremely important. Rust will optimize field packing
1199/// if done correctly. Static assertions are included to prevent bad mistakes.
1200/// Changing the enum size is an API breaking change (requires KLL compiler
1201/// updates).
1202#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1203#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1204#[repr(u8)]
1205pub enum TriggerCondition {
1206 None = 0,
1207 /// 6 bytes
1208 Switch {
1209 /// Switch state
1210 state: trigger::Phro,
1211 /// Switch identification index
1212 index: u16,
1213 /// Scanning loop condition (number of scanning loops attached to state condition)
1214 /// Lookup index
1215 loop_condition_index: u16,
1216 } = 1,
1217 /// 5 bytes
1218 HidLed {
1219 /// LED state
1220 state: trigger::Aodo,
1221 /// Scanning loop condition (number of scanning loops attached to state condition)
1222 /// Lookup index
1223 loop_condition_index: u16,
1224 /// HID LED identification (from USB HID spec, e.g. CapsLock)
1225 index: u8,
1226 } = 2,
1227 /// 6 bytes
1228 AnalogDistance {
1229 /// Needed focontiguous packing
1230 reserved: u8,
1231 /// Switch identification index
1232 index: u16,
1233 /// Analog distance, units depend on the keyboard, KLL compiler handles unit conversion
1234 val: i16,
1235 } = 3,
1236 /// 6 bytes
1237 AnalogVelocity {
1238 /// Needed for contiguous packing
1239 reserved: u8,
1240 /// Switch identification index
1241 index: u16,
1242 /// Analog velocity, units depend on the keyboard, KLL compiler handles unit conversion
1243 val: i16,
1244 } = 4,
1245 /// 6 bytes
1246 AnalogAcceleration {
1247 /// Needed for contiguous packing
1248 reserved: u8,
1249 /// Switch identification index
1250 index: u16,
1251 /// Analog acceleration, units depend on the keyboard, KLL compiler handles unit conversion
1252 val: i16,
1253 } = 5,
1254 /// 6 bytes
1255 AnalogJerk {
1256 /// Needed for contiguous packing
1257 reserved: u8,
1258 /// Switch identification index
1259 index: u16,
1260 /// Analog jerk, units depend on the keyboard, KLL compiler handles unit conversion
1261 val: i16,
1262 } = 6,
1263 /// 5 bytes
1264 Layer {
1265 /// Layer state
1266 state: trigger::LayerState,
1267 /// Scanning loop condition (number of scanning loops attached to state condition)
1268 /// Lookup index
1269 loop_condition_index: u16,
1270 /// Layer index (layer 0 is the default state and does not have events)
1271 layer: u8,
1272 } = 7,
1273 /// 6 bytes
1274 Animation {
1275 /// Animation state
1276 state: trigger::Dro,
1277 /// Animation index
1278 index: u16,
1279 /// Scanning loop condition (number of scanning loops attached to state condition)
1280 /// Lookup index
1281 loop_condition_index: u16,
1282 } = 8,
1283 /// Sleep events are always index 0
1284 /// 4 bytes
1285 Sleep {
1286 /// Sleep state
1287 state: trigger::Aodo,
1288 /// Scanning loop condition (number of scanning loops attached to state condition)
1289 /// Lookup index
1290 loop_condition_index: u16,
1291 } = 9,
1292 /// Resume events are always index 0
1293 /// 4 bytes
1294 Resume {
1295 /// Resume state
1296 state: trigger::Aodo,
1297 /// Scanning loop condition (number of scanning loops attached to state condition)
1298 /// Lookup index
1299 loop_condition_index: u16,
1300 } = 10,
1301 /// Inactive events are always index 0
1302 /// 4 bytes
1303 Inactive {
1304 /// Inactive state
1305 state: trigger::Aodo,
1306 /// Scanning loop condition (number of scanning loops attached to state condition)
1307 /// Lookup index
1308 loop_condition_index: u16,
1309 } = 11,
1310 /// Active events are always index 0
1311 /// 4 bytes
1312 Active {
1313 /// Active state
1314 state: trigger::Aodo,
1315 /// Scanning loop condition (number of scanning loops attached to state condition)
1316 /// Lookup index
1317 loop_condition_index: u16,
1318 } = 12,
1319 /// 5 bytes
1320 Rotation {
1321 /// Rotation index
1322 index: u8,
1323 /// Scanning loop condition (number of scanning loops attached to state condition)
1324 /// Lookup index
1325 loop_condition_index: u16,
1326 /// Rotation direction (-1,+1)
1327 position: i8,
1328 } = 13,
1329}
1330
1331// Size validation for TriggerCondition
1332// DO NOT CHANGE THIS: Will invalidate existing generated KLL layouts
1333const_assert_eq!(core::mem::size_of::<TriggerCondition>(), 6);
1334
1335// NOTE: It's not possible to make this a trait (yet)
1336impl TriggerCondition {
1337 /// Convert enum to an array of bytes
1338 /// # Safety
1339 pub const unsafe fn bytes(&self) -> &[u8] {
1340 &*core::ptr::slice_from_raw_parts(
1341 (self as *const Self) as *const u8,
1342 core::mem::size_of::<Self>(),
1343 )
1344 }
1345
1346 /// Convert array of bytes to enum
1347 /// # Safety
1348 pub const unsafe fn from_byte_array(bytes: [u8; core::mem::size_of::<Self>()]) -> Self {
1349 core::mem::transmute(bytes)
1350 }
1351
1352 /// Convert slice of bytes to enum
1353 /// Aggressively casts the provide u8 slice to retrieve a TriggerCondition
1354 /// # Safety
1355 pub const unsafe fn from_bytes(bytes: &[u8]) -> Self {
1356 core::ptr::read(bytes.as_ptr() as *const &[u8] as *const Self)
1357 }
1358
1359 /// Attempts to determine the index value of the condition
1360 /// If an index is not valid, return 0 instead (index may not have any meaning)
1361 pub fn index(&self) -> u16 {
1362 match self {
1363 TriggerCondition::None => 0,
1364 TriggerCondition::Switch { index, .. } => *index,
1365 TriggerCondition::HidLed { index, .. } => (*index).into(),
1366 TriggerCondition::AnalogDistance { index, .. } => *index,
1367 TriggerCondition::AnalogVelocity { index, .. } => *index,
1368 TriggerCondition::AnalogAcceleration { index, .. } => *index,
1369 TriggerCondition::AnalogJerk { index, .. } => *index,
1370 TriggerCondition::Layer { layer, .. } => (*layer).into(),
1371 TriggerCondition::Animation { index, .. } => *index,
1372 TriggerCondition::Sleep { .. } => 0,
1373 TriggerCondition::Resume { .. } => 0,
1374 TriggerCondition::Inactive { .. } => 0,
1375 TriggerCondition::Active { .. } => 0,
1376 TriggerCondition::Rotation { index, .. } => (*index).into(),
1377 }
1378 }
1379
1380 /// Compare TriggerEvent to TriggerCondition
1381 /// NOTE: This is not a direct equivalent comparison each type and state can influence
1382 /// how the loop_condition_index is evaluated.
1383 /// In a way, this is similar to the voting scheme of the older C KLL implementation.
1384 pub fn evaluate(&self, event: TriggerEvent, loop_condition_lookup: &[u32]) -> Vote {
1385 // Make sure the Id's match
1386 if u8::from(*self) != u8::from(event) {
1387 return Vote::Insufficient;
1388 }
1389
1390 // Make sure the indices match
1391 if self.index() != event.index() {
1392 return Vote::Insufficient;
1393 }
1394
1395 // We only need to compare like events as they must match
1396 match self {
1397 TriggerCondition::None => Vote::Positive,
1398 TriggerCondition::Switch {
1399 state,
1400 loop_condition_index,
1401 ..
1402 } => {
1403 if let TriggerEvent::Switch {
1404 state: e_state,
1405 last_state,
1406 ..
1407 } = event
1408 {
1409 state.compare(
1410 loop_condition_lookup[*loop_condition_index as usize],
1411 e_state,
1412 last_state,
1413 )
1414 } else {
1415 Vote::Insufficient
1416 }
1417 }
1418 _ => {
1419 panic!("Unknown condition! Please fix.");
1420 }
1421 }
1422 }
1423}
1424
1425/// CapabilityState
1426/// After voting with the indicated TriggerConditions, the CapabilityState is used by the Result
1427/// capabilities to evaluate a generic decision.
1428/// This mirrors CapabilityEvent, except that the Passthrough event is not stored as it is not
1429/// known at compile time.
1430/// If passthrough has been specified the final element of the last combo will be sent instead
1431#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1432#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1433#[repr(u8)]
1434pub enum CapabilityState {
1435 /// Invalid, ignore this event
1436 None = 0,
1437 /// Initial state (e.g. press)
1438 Initial = 1,
1439 /// Last state (e.g. release)
1440 Last = 2,
1441 /// Any activation (Initial+Last)
1442 Any = 3,
1443 /// Event passthrough
1444 Passthrough = 4,
1445}
1446
1447impl CapabilityState {
1448 /// Using a CapabilityState and TriggerEvent, generate a CapabilityEvent
1449 pub fn event(&self, event: TriggerEvent) -> CapabilityEvent {
1450 match self {
1451 CapabilityState::None => CapabilityEvent::None,
1452 CapabilityState::Initial => CapabilityEvent::Initial,
1453 CapabilityState::Last => CapabilityEvent::Last,
1454 CapabilityState::Any => CapabilityEvent::Any,
1455 CapabilityState::Passthrough => CapabilityEvent::Passthrough(event),
1456 }
1457 }
1458}
1459
1460/// CapabilityEvent
1461/// After voting with the indicated TriggerConditions, the CapabilityEvent is used by the Result
1462/// capabilities to evaluate a generic decision.
1463/// Mirrors CapabilityState, except that Passthrough contains the TriggerEvent to pass through
1464/// to the corresponding Capability (see ResultGuide).
1465/// If passthrough has been specified the final element of the last combo will be sent instead
1466#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
1467#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1468#[repr(u8)]
1469pub enum CapabilityEvent {
1470 /// Invalid, ignore this event
1471 None = 0,
1472 /// Initial state (e.g. press)
1473 Initial = 1,
1474 /// Last state (e.g. release)
1475 Last = 2,
1476 /// Any activation (Initial+Last)
1477 Any = 3,
1478 /// TriggerEvent passthrough
1479 Passthrough(TriggerEvent) = 4,
1480}
1481
1482/*
1483/// Position
1484/// Each position has 6 dimensions
1485/// Units are in mm
1486pub struct Position {
1487 /// x position
1488 x: f32,
1489 /// y position
1490 y: f32,
1491 /// z position
1492 z: f32,
1493 /// Rotation x direction
1494 rx: f32,
1495 /// Rotation y direction
1496 ry: f32,
1497 /// Rotation z direction
1498 rz: f32,
1499}
1500*/