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}