Skip to main content

pokeys_lib/
keyboard_matrix.rs

1//! Matrix keyboard support
2//!
3//! This module provides matrix keyboard functionality for PoKeys devices using the official
4//! protocol specification (command 0xCA). The implementation supports up to 16x8 matrix
5//! keyboards with proper key indexing according to the PoKeys protocol.
6//!
7//! ## Key Features
8//! - Supports matrix keyboards up to 16 rows x 8 columns
9//! - Protocol-compliant implementation using command 0xCA
10//! - Proper key indexing with 8-column internal layout
11//! - Real-time key state monitoring
12//! - Configurable pin assignments for rows and columns
13//!
14//! ## Key Indexing
15//! The PoKeys protocol uses a fixed 8-column internal layout regardless of configured width:
16//! - Row 0: keys 0-7 (only 0-width used)
17//! - Row 1: keys 8-15 (only 8-(8+width) used)  
18//! - Row 2: keys 16-23, etc.
19//!
20//! ## Example Usage
21//! ```rust,no_run
22//! use pokeys_lib::*;
23//!
24//! fn main() -> Result<()> {
25//!     let mut device = connect_to_device(0)?;
26//!
27//!     // Configure 4x4 matrix keyboard
28//!     let column_pins = [21, 22, 23, 24];
29//!     let row_pins = [13, 14, 15, 16];
30//!     device.configure_matrix_keyboard(4, 4, &column_pins, &row_pins)?;
31//!
32//!     // Read keyboard state
33//!     device.read_matrix_keyboard()?;
34//!     let key_pressed = device.matrix_keyboard.get_key_state(0, 0);
35//!     
36//!     Ok(())
37//! }
38//! ```
39
40use serde::{Deserialize, Serialize};
41
42/// Matrix keyboard configuration
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct MatrixKeyboard {
45    pub configuration: u8,
46    pub width: u8,
47    pub height: u8,
48    pub scanning_decimation: u8,
49    pub column_pins: [u8; 8],
50    pub row_pins: [u8; 16],
51    pub macro_mapping_options: Vec<u8>,
52    pub key_mapping_key_code: Vec<u8>,
53    pub key_mapping_key_modifier: Vec<u8>,
54    pub key_mapping_triggered_key: Vec<u8>,
55    pub key_mapping_key_code_up: Vec<u8>,
56    pub key_mapping_key_modifier_up: Vec<u8>,
57    pub key_values: Vec<u8>,
58}
59
60impl MatrixKeyboard {
61    pub fn new() -> Self {
62        Self {
63            configuration: 0,
64            width: 0,
65            height: 0,
66            scanning_decimation: 0,
67            column_pins: [0; 8],
68            row_pins: [0; 16],
69            macro_mapping_options: vec![0; 128],
70            key_mapping_key_code: vec![0; 128],
71            key_mapping_key_modifier: vec![0; 128],
72            key_mapping_triggered_key: vec![0; 128],
73            key_mapping_key_code_up: vec![0; 128],
74            key_mapping_key_modifier_up: vec![0; 128],
75            key_values: vec![0; 128],
76        }
77    }
78
79    pub fn is_enabled(&self) -> bool {
80        self.configuration != 0
81    }
82
83    pub fn get_key_state(&self, row: usize, col: usize) -> bool {
84        if row >= self.height as usize || col >= self.width as usize {
85            return false;
86        }
87        // Protocol uses 8-column layout internally: key_index = row * 8 + col
88        let key_index = row * 8 + col;
89        if key_index < self.key_values.len() {
90            self.key_values[key_index] != 0
91        } else {
92            false
93        }
94    }
95}
96
97impl Default for MatrixKeyboard {
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_matrix_keyboard_creation() {
109        let kb = MatrixKeyboard::new();
110        assert!(!kb.is_enabled());
111        assert_eq!(kb.width, 0);
112        assert_eq!(kb.height, 0);
113    }
114
115    #[test]
116    fn test_matrix_keyboard_configuration() {
117        let mut kb = MatrixKeyboard::new();
118
119        // Test initial state
120        assert_eq!(kb.configuration, 0);
121        assert_eq!(kb.width, 0);
122        assert_eq!(kb.height, 0);
123        assert_eq!(kb.scanning_decimation, 0);
124        assert_eq!(kb.column_pins.len(), 8);
125        assert_eq!(kb.row_pins.len(), 16);
126
127        // Test enabling
128        kb.configuration = 1;
129        kb.width = 4;
130        kb.height = 4;
131
132        assert!(kb.is_enabled());
133        assert_eq!(kb.width, 4);
134        assert_eq!(kb.height, 4);
135    }
136
137    #[test]
138    fn test_get_key_state() {
139        let mut kb = MatrixKeyboard::new();
140        kb.width = 4;
141        kb.height = 4;
142
143        // Test bounds checking
144        assert!(!kb.get_key_state(0, 0)); // Should be false initially
145        assert!(!kb.get_key_state(4, 0)); // Out of bounds
146        assert!(!kb.get_key_state(0, 4)); // Out of bounds
147
148        // Test setting key state
149        kb.key_values[0] = 1; // Row 0, Col 0
150        assert!(kb.get_key_state(0, 0));
151
152        kb.key_values[8] = 1; // Row 1, Col 0 (1*8 + 0 = 8)
153        assert!(kb.get_key_state(1, 0));
154    }
155
156    #[test]
157    fn test_key_index_calculation() {
158        let mut kb = MatrixKeyboard::new();
159        kb.width = 3;
160        kb.height = 3;
161
162        // Test key index calculation: row * 8 + col (protocol uses 8-column layout)
163        kb.key_values[0] = 1; // (0,0) = 0*8 + 0 = 0
164        kb.key_values[1] = 1; // (0,1) = 0*8 + 1 = 1
165        kb.key_values[8] = 1; // (1,0) = 1*8 + 0 = 8
166        kb.key_values[9] = 1; // (1,1) = 1*8 + 1 = 9
167
168        assert!(kb.get_key_state(0, 0));
169        assert!(kb.get_key_state(0, 1));
170        assert!(!kb.get_key_state(0, 2));
171        assert!(kb.get_key_state(1, 0));
172        assert!(kb.get_key_state(1, 1));
173        assert!(!kb.get_key_state(1, 2));
174    }
175}