rpi_led_matrix/
options.rs

1use libc::c_int;
2use std::ffi::CString;
3
4use crate::ffi;
5
6/// Options related to the LED matrix, like rows/cols/HW mapping
7#[derive(Debug)]
8pub struct LedMatrixOptions(pub(crate) ffi::CLedMatrixOptions);
9
10/// Options related to how the runtime operates, like GPIO slowdown or daemon/sudo privileges
11#[derive(Debug)]
12pub struct LedRuntimeOptions(pub(crate) ffi::CLedRuntimeOptions);
13
14type LedMatrixOptionsResult = Result<(), &'static str>;
15
16impl LedMatrixOptions {
17    /// Creates a new `LedMatrixOptions` struct with the default parameters.
18    ///
19    /// ```
20    /// use rpi_led_matrix::{LedMatrix,LedMatrixOptions};
21    /// let mut options = LedMatrixOptions::new();
22    /// options.set_hardware_mapping("adafruit-hat-pwm");
23    /// let matrix = LedMatrix::new(Some(options), None).unwrap();
24    /// ```
25    ///
26    /// # Panics
27    /// If for some reason the conversion from constant literal strings to `CString` fails.
28    /// It should never fail but we do `.unwrap()` it.
29    #[must_use]
30    pub fn new() -> Self {
31        Self(ffi::CLedMatrixOptions {
32            hardware_mapping: CString::new("regular").unwrap().into_raw(),
33            rows: 32,
34            cols: 32,
35            chain_length: 1,
36            parallel: 1,
37            pwm_bits: 11,
38            pwm_lsb_nanoseconds: 1000,
39            pwm_dither_bits: 1,
40            brightness: 100,
41            scan_mode: 0,
42            row_address_type: 0,
43            multiplexing: 0,
44            led_rgb_sequence: CString::new("RGB").unwrap().into_raw(),
45            pixel_mapper_config: CString::new("").unwrap().into_raw(),
46            panel_type: CString::new("").unwrap().into_raw(),
47            disable_hardware_pulsing: 1,
48            show_refresh_rate: 1,
49            inverse_colors: 0,
50            limit_refresh_rate_hz: 0,
51        })
52    }
53
54    /// Sets the type of GPIO mapping used (e.g., "adafruit-hat-pwm").
55    ///
56    /// # Panics
57    /// If the given `mapping` string fails to convert to a `CString`. This can
58    /// occur when there is a null character mid way in the string.
59    pub fn set_hardware_mapping(&mut self, mapping: &str) {
60        unsafe {
61            let _ = CString::from_raw(self.0.hardware_mapping);
62            self.0.hardware_mapping = CString::new(mapping)
63                .expect("given string failed to convert into a CString")
64                .into_raw();
65        }
66    }
67
68    /// Sets the number of rows on the panels being used. Typically 8, 16, 32 or 64.
69    pub fn set_rows(&mut self, rows: u32) {
70        self.0.rows = rows as c_int;
71    }
72
73    /// Sets the number of columns on the panels being used. Typically 32 or 64.
74    pub fn set_cols(&mut self, cols: u32) {
75        self.0.cols = cols as c_int;
76    }
77
78    /// Sets the number of panels daisy-chained together.
79    pub fn set_chain_length(&mut self, chain_length: u32) {
80        self.0.chain_length = chain_length as c_int;
81    }
82
83    /// Sets the number of parallel chains. Valid range: \[1,3\].
84    pub fn set_parallel(&mut self, parallel: u32) {
85        self.0.parallel = parallel as c_int;
86    }
87
88    /// Sets the number of PWM bits to use. Valid range: \[0,11\].
89    ///
90    /// # Errors
91    /// If the given `pwm_bits` is outside the valid range
92    pub fn set_pwm_bits(&mut self, pwm_bits: u8) -> LedMatrixOptionsResult {
93        if pwm_bits > 11 {
94            Err("Pwm bits can only have value between 0 and 11 inclusive")
95        } else {
96            self.0.pwm_bits = c_int::from(pwm_bits);
97            Ok(())
98        }
99    }
100
101    /// Sets the number of nanoseconds of delay for our LSB
102    pub fn set_pwm_lsb_nanoseconds(&mut self, pwm_lsb_nanoseconds: u32) {
103        self.0.pwm_lsb_nanoseconds = pwm_lsb_nanoseconds as c_int;
104    }
105
106    /// Sets the panel brightness in percent.
107    ///
108    /// # Errors
109    /// If the given `brightness` is not in the range \[1,100\].
110    pub fn set_brightness(&mut self, brightness: u8) -> LedMatrixOptionsResult {
111        if (1..=100).contains(&brightness) {
112            self.0.brightness = c_int::from(brightness);
113            Ok(())
114        } else {
115            Err("Brightness can only have value between 1 and 100 inclusive")
116        }
117    }
118
119    /// Sets the scan mode. 0: progressive, 1: interlaced.
120    pub fn set_scan_mode(&mut self, scan_mode: u32) {
121        self.0.scan_mode = scan_mode as c_int;
122    }
123
124    /// Sets the ordering of the LEDs on your panel.
125    ///
126    /// # Panics
127    /// If the given `sequence` string fails to convert to a `CString`. This can
128    /// occur when there is a null character mid way in the string.
129    pub fn set_led_rgb_sequence(&mut self, sequence: &str) {
130        unsafe {
131            let _ = CString::from_raw(self.0.led_rgb_sequence);
132            self.0.led_rgb_sequence = CString::new(sequence)
133                .expect("given string failed to convert into a CString")
134                .into_raw();
135        }
136    }
137
138    /// Semicolon-separated list of pixel-mappers to arrange pixels (e.g. "U-mapper;Rotate:90").
139    ///
140    /// Valid mapping options
141    ///
142    /// * `Mirror`
143    /// * `Rotate:<Angle>`
144    /// * `U-mapper`
145    /// * `V-mapper`
146    ///
147    /// # Panics
148    /// If the given `mapper` string fails to convert to a `CString`. This can
149    /// occur when there is a null character mid way in the string.
150    pub fn set_pixel_mapper_config(&mut self, mapper: &str) {
151        unsafe {
152            let _ = CString::from_raw(self.0.pixel_mapper_config);
153            self.0.pixel_mapper_config = CString::new(mapper)
154                .expect("given string failed to convert into a CString")
155                .into_raw();
156        }
157    }
158
159    /// Sets if hardware pin-pulse generation should be used.
160    pub fn set_hardware_pulsing(&mut self, enable: bool) {
161        if enable {
162            self.0.disable_hardware_pulsing = 0;
163        } else {
164            self.0.disable_hardware_pulsing = 1;
165        }
166    }
167
168    /// Configures if the current refresh rate should be printed by the C++ library.
169    pub fn set_refresh_rate(&mut self, enable: bool) {
170        if enable {
171            self.0.show_refresh_rate = 1;
172        } else {
173            self.0.show_refresh_rate = 0;
174        }
175    }
176
177    /// If set, invert the color displayed.
178    pub fn set_inverse_colors(&mut self, enable: bool) {
179        if enable {
180            self.0.inverse_colors = 1;
181        } else {
182            self.0.inverse_colors = 0;
183        }
184    }
185
186    /// Sets the type of multiplexing used.
187    ///
188    /// 0.  `direct`
189    /// 1.  `Stripe`
190    /// 2.  `Checkered`
191    /// 3.  `Spiral`
192    /// 4.  `ZStripe`
193    /// 5.  `ZnMirrorZStripe`
194    /// 6.  `coreman`
195    /// 7.  `Kaler2Scan`
196    /// 8.  `ZStripeUneven`
197    /// 9.  `P10-128x4-Z`
198    /// 10. `QiangLiQ8`
199    /// 11. `InversedZStripe`
200    /// 12. `P10Outdoor1R1G1-1`
201    /// 13. `P10Outdoor1R1G1-2`
202    /// 14. `P10Outdoor1R1G1-3`
203    /// 15. `P10CoremanMapper`
204    /// 16. `P8Outdoor1R1G1`
205    pub fn set_multiplexing(&mut self, multiplexing: u32) {
206        self.0.multiplexing = multiplexing as c_int;
207    }
208
209    /// Sets the type of row addressing to be used.
210    ///
211    /// 0. default
212    /// 1. AB-addressed panels
213    /// 2. direct row select
214    /// 3. ABC-addressed panels
215    /// 4. ABC Shift + DE direct
216    pub fn set_row_addr_type(&mut self, row_addr_type: u32) {
217        self.0.row_address_type = row_addr_type as c_int;
218    }
219
220    /// Limit refresh rate to this frequency in Hz. (0 = no limit)
221    ///
222    /// Useful to keep a constant refresh rate on loaded system.
223    pub fn set_limit_refresh(&mut self, limit_refresh: u32) {
224        self.0.limit_refresh_rate_hz = limit_refresh as c_int;
225    }
226
227    /// Configures how many bits to use for time-based dithering.
228    pub fn set_pwm_dither_bits(&mut self, pwm_dither_bits: u32) {
229        self.0.pwm_dither_bits = pwm_dither_bits as c_int;
230    }
231
232    /// Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'
233    ///
234    /// # Panics
235    /// If the given `panel_type` string fails to convert to a `CString`. This can
236    /// occur when there is a null character mid way in the string.
237    pub fn set_panel_type(&mut self, panel_type: &str) {
238        unsafe {
239            let _ = CString::from_raw(self.0.panel_type);
240            self.0.panel_type = CString::new(panel_type)
241                .expect("given string failed to convert into a CString")
242                .into_raw();
243        }
244    }
245}
246
247impl Default for LedMatrixOptions {
248    fn default() -> Self {
249        Self::new()
250    }
251}
252
253impl Drop for LedMatrixOptions {
254    fn drop(&mut self) {
255        unsafe {
256            let _ = CString::from_raw(self.0.hardware_mapping);
257            let _ = CString::from_raw(self.0.led_rgb_sequence);
258            let _ = CString::from_raw(self.0.panel_type);
259        }
260    }
261}
262
263impl LedRuntimeOptions {
264    /// Creates a new `LedRuntimeOptions` struct with the default parameters.
265    ///
266    /// ```
267    /// use rpi_led_matrix::{LedMatrix, LedRuntimeOptions};
268    /// let mut rt_options = LedRuntimeOptions::new();
269    /// rt_options.set_gpio_slowdown(3);
270    /// let matrix = LedMatrix::new(None, Some(rt_options)).unwrap();
271    /// ```
272    #[must_use]
273    pub const fn new() -> Self {
274        Self(ffi::CLedRuntimeOptions {
275            gpio_slowdown: 1,
276            daemon: 0,
277            drop_privileges: 1,
278            do_gpio_init: true,
279        })
280    }
281
282    /// Sets the GPIO slowdown, in . Needed for faster Pis/slower panels
283    pub fn set_gpio_slowdown(&mut self, gpio_slowdown: u32) {
284        self.0.gpio_slowdown = gpio_slowdown as i32;
285    }
286
287    /// If True, make the process run in the background as daemon.
288    pub fn set_daemon(&mut self, daemon: bool) {
289        self.0.daemon = if daemon { 1 } else { 0 };
290    }
291
292    /// If True, drop privileges from 'root' after initializing the hardware.
293    pub fn set_drop_privileges(&mut self, drop_privileges: bool) {
294        self.0.drop_privileges = if drop_privileges { 1 } else { 0 };
295    }
296
297    /// You almost definitely want this to be left as True. Use this if you know what you're doing.
298    pub fn set_do_gpio_init(&mut self, do_gpio_init: bool) {
299        self.0.do_gpio_init = do_gpio_init;
300    }
301}
302
303impl Default for LedRuntimeOptions {
304    fn default() -> Self {
305        Self::new()
306    }
307}