1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
use array2d::Array2D;
use crate::{
Color, Command, Controller, Led, OpenRgbError, OpenRgbResult, ZoneType,
client::segment::Segment,
data::{SegmentData, ZoneData},
};
/// A zone in a controller, which contains one or more LEDs.
///
/// Zones can also contain segments, which are user-created subdivisions of the zone.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Zone<'c> {
controller: &'c Controller,
zone_data: &'c ZoneData,
}
impl<'c> Zone<'c> {
pub(crate) fn new(controller: &'c Controller, zone_data: &'c ZoneData) -> Self {
Self {
controller,
zone_data,
}
}
/// Returns the ID of the controller this zone belongs to.
pub fn controller_id(&self) -> usize {
self.controller.id()
}
/// Returns the ID of this zone.
pub fn zone_id(&self) -> usize {
self.zone_data.id()
}
delegate::delegate! {
to self.zone_data {
/// Returns the ID of this zone.
pub fn id(&self) -> usize;
/// Returns the name of this zone.
pub fn name(&self) -> &str;
/// Returns the type of this zone.
pub fn zone_type(&self) -> ZoneType;
/// Returns the minimum number of LEDs for this zone if it is resizable.
pub fn leds_min(&self) -> usize;
/// Returns the maximum number of LEDs for this zone if it is resizable.
pub fn leds_max(&self) -> usize;
/// Returns the number of LEDs in this zone.
#[call(leds_count)]
pub fn num_leds(&self) -> usize;
#[call(segments)]
pub(crate) fn segment_data(&self) -> Option<&[SegmentData]>;
#[expect(unused, reason = "Api not finalised yet")]
#[call(matrix)]
pub(crate) fn matrix_data(&self) -> Option<&Array2D<u32>>;
}
}
/// Returns the segment with the given `segment_id`.
pub fn get_segment(&'c self, segment_id: usize) -> OpenRgbResult<Segment<'c>> {
let Some(segments) = self.segment_data() else {
return Err(OpenRgbError::CommandError(
"Segments not supported in protocol version < 4".to_owned(),
));
};
let data = segments.get(segment_id).ok_or_else(|| {
OpenRgbError::CommandError(format!(
"Segment with id {segment_id} not found in zone {}",
self.name()
))
})?;
Ok(Segment::new(self, data))
}
/// Returns the offset of this zone in the controller's LED array.
pub fn offset(&self) -> usize {
self.controller
.get_zone_led_offset(self.zone_id())
.expect("Zone id should be valid")
}
/// Returns an iterator over the Leds of this zone
pub fn led_iter(&self) -> impl Iterator<Item = Led<'c>> {
self.controller
.led_iter()
.skip(self.offset())
.take(self.num_leds())
}
/// Creates a new [`Command`] for the controller of this zone.
///
/// The command must be executed by calling `.execute()`
#[must_use]
pub fn cmd(&self) -> Command<'c> {
Command::new(self.controller)
}
/// Creates a new [`Command`] for the controller of this zone
/// and sets the LED colors using the provided closure.
#[must_use]
pub fn cmd_with_leds<F>(&self, led_clr: F) -> Command<'c>
where
F: Fn(Led<'c>) -> Color,
{
let mut cmd = self.cmd();
for led in self.led_iter() {
cmd.set_led(led.id(), led_clr(led))
.expect("Led index incorrect");
}
cmd
}
/// Returns a command to update the LEDs for this Zone to `colors`.
/// Equivalent to `cmd().set_zone_leds(self.zone_id(), colors)`.
///
/// The command must be executed by calling `.execute()`
pub fn cmd_with_set_leds<C: Into<Color>>(
&'c self,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<Command<'c>> {
let mut cmd = self.cmd();
cmd.set_zone_leds(self.zone_id(), colors)?;
Ok(cmd)
}
/// Sets a single LED in this zone to the given `color`.
///
/// # Errors
///
/// Returns an error if the index is out of bounds for this zone.
pub async fn set_led<C: Into<Color>>(&self, idx: usize, color: C) -> OpenRgbResult<()> {
if idx >= self.num_leds() {
return Err(OpenRgbError::CommandError(format!(
"Index {idx} out of bounds for zone {} with {} LEDs",
self.name(),
self.num_leds()
)));
}
let idx = self.offset() + idx;
self.controller.set_led(idx, color).await
}
/// Sets all LEDs in this zone to the given `color`.
pub async fn set_all_leds<C: Into<Color>>(&self, color: C) -> OpenRgbResult<()> {
let color = color.into();
let colors = (0..self.num_leds()).map(|_| color);
self.set_leds(colors).await
}
/// Sets the LEDs in this zone to the given colors.
pub async fn set_leds<C: Into<Color>>(
&self,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<()> {
let mut color_v = colors.into_iter().map(Into::into).collect::<Vec<_>>();
if color_v.len() >= self.num_leds() {
tracing::warn!(
"Zone {} for controller {} was given {} colors, while its length is {}. This might become a hard error in the future.",
self.name(),
self.controller.name(),
color_v.len(),
self.num_leds()
);
} else if color_v.len() < self.num_leds() {
color_v.extend((color_v.len()..self.num_leds()).map(|_| Color::default()))
}
self.controller.set_zone_leds(self.zone_id(), color_v).await
}
/// Adds a segment to this zone.
///
/// Controller data must be resynced using [`Controller::sync_controller_data()`]
pub async fn add_segment(
&self,
name: impl Into<String>,
start_idx: usize,
led_count: usize,
) -> OpenRgbResult<()> {
if start_idx + led_count > self.num_leds() {
return Err(OpenRgbError::CommandError(format!(
"Segment start index {} + count {} exceeds zone LED count {}",
start_idx,
led_count,
self.num_leds()
)));
}
let data = SegmentData::new(name.into(), start_idx as u32, led_count as u32);
self.controller
.proto()
.add_segment(self.controller.id() as u32, self.zone_id() as u32, &data)
.await
}
/// Clears the segments for this CONTROLLER.
/// This clears all segments for all zones of the controller, not just this zone.
///
/// Controller data must be resynced using [`Controller::sync_controller_data()`]
pub async fn clear_segments(&self) -> OpenRgbResult<()> {
self.controller.clear_segments().await
}
/// Returns an iterator over all segments in this zone.
pub fn segment_iter(&'c self) -> impl Iterator<Item = Segment<'c>> {
self.segment_data()
.into_iter()
.flatten()
.map(move |s| Segment::new(self, s))
}
/// Resizes this zone to a new size.
///
/// Controller data must be resynced using [`Controller::sync_controller_data()`]
pub async fn resize(&self, new_size: usize) -> OpenRgbResult<()> {
self.controller
.proto()
.resize_zone(
self.controller.id() as u32,
self.zone_id() as u32,
new_size as u32,
)
.await
}
}