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*/