1use std::collections::{HashMap, VecDeque};
2use std::io::{Read, Write};
3
4use crate::binding::{Behavior, BehaviorRole, role_from_display_name};
5use crate::framing::FrameDecoder;
6use crate::hid_usage::HidUsage;
7use crate::proto::zmk;
8use crate::proto::zmk::studio;
9use crate::protocol::{ProtocolError, decode_responses, encode_request};
10#[cfg(feature = "ble")]
11use crate::transport::ble::{BleTransport, BleTransportError};
12#[cfg(feature = "serial")]
13use crate::transport::serial::{SerialTransport, SerialTransportError};
14
15#[derive(Debug)]
17pub enum ClientError {
18 Io(std::io::Error),
19 Protocol(ProtocolError),
20 Meta(zmk::meta::ErrorConditions),
21 NoResponse,
22 MissingResponseType,
23 MissingSubsystem,
24 UnexpectedSubsystem(&'static str),
25 UnexpectedRequestId { expected: u32, actual: u32 },
26 UnknownEnumValue { field: &'static str, value: i32 },
27 SetLayerBindingFailed(zmk::keymap::SetLayerBindingResponse),
28 SaveChangesFailed(zmk::keymap::SaveChangesErrorCode),
29 SetActivePhysicalLayoutFailed(zmk::keymap::SetActivePhysicalLayoutErrorCode),
30 MoveLayerFailed(zmk::keymap::MoveLayerErrorCode),
31 AddLayerFailed(zmk::keymap::AddLayerErrorCode),
32 RemoveLayerFailed(zmk::keymap::RemoveLayerErrorCode),
33 RestoreLayerFailed(zmk::keymap::RestoreLayerErrorCode),
34 SetLayerPropsFailed(zmk::keymap::SetLayerPropsResponse),
35 InvalidLayerOrPosition { layer_id: u32, key_position: i32 },
36 MissingBehaviorRole(&'static str),
37 BehaviorIdOutOfRange { behavior_id: u32 },
38}
39
40impl std::fmt::Display for ClientError {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match self {
43 Self::Io(err) => write!(f, "I/O error: {err}"),
44 Self::Protocol(err) => write!(f, "Protocol error: {err}"),
45 Self::Meta(cond) => write!(f, "Device returned meta error: {}", cond.as_str_name()),
46 Self::NoResponse => write!(f, "Device returned no response"),
47 Self::MissingResponseType => write!(f, "Response was missing type"),
48 Self::MissingSubsystem => write!(f, "Request response was missing subsystem"),
49 Self::UnexpectedSubsystem(expected) => {
50 write!(f, "Unexpected subsystem in response; expected {expected}")
51 }
52 Self::UnexpectedRequestId { expected, actual } => {
53 write!(
54 f,
55 "Unexpected request ID in response: expected {expected}, got {actual}"
56 )
57 }
58 Self::UnknownEnumValue { field, value } => {
59 write!(f, "Unknown enum value for {field}: {value}")
60 }
61 Self::SetLayerBindingFailed(code) => {
62 write!(f, "Set layer binding failed: {}", code.as_str_name())
63 }
64 Self::SaveChangesFailed(code) => {
65 write!(f, "Save changes failed: {}", code.as_str_name())
66 }
67 Self::SetActivePhysicalLayoutFailed(code) => {
68 write!(
69 f,
70 "Set active physical layout failed: {}",
71 code.as_str_name()
72 )
73 }
74 Self::MoveLayerFailed(code) => write!(f, "Move layer failed: {}", code.as_str_name()),
75 Self::AddLayerFailed(code) => write!(f, "Add layer failed: {}", code.as_str_name()),
76 Self::RemoveLayerFailed(code) => {
77 write!(f, "Remove layer failed: {}", code.as_str_name())
78 }
79 Self::RestoreLayerFailed(code) => {
80 write!(f, "Restore layer failed: {}", code.as_str_name())
81 }
82 Self::SetLayerPropsFailed(code) => {
83 write!(f, "Set layer properties failed: {}", code.as_str_name())
84 }
85 Self::InvalidLayerOrPosition {
86 layer_id,
87 key_position,
88 } => write!(
89 f,
90 "Invalid layer/position: layer_id={layer_id}, key_position={key_position}"
91 ),
92 Self::MissingBehaviorRole(role) => {
93 write!(f, "Missing required behavior role in firmware: {role}")
94 }
95 Self::BehaviorIdOutOfRange { behavior_id } => {
96 write!(f, "Behavior ID is out of i32 range: {behavior_id}")
97 }
98 }
99 }
100}
101
102impl std::error::Error for ClientError {
103 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
104 match self {
105 Self::Io(err) => Some(err),
106 Self::Protocol(err) => Some(err),
107 _ => None,
108 }
109 }
110}
111
112impl From<std::io::Error> for ClientError {
113 fn from(value: std::io::Error) -> Self {
114 Self::Io(value)
115 }
116}
117
118impl From<ProtocolError> for ClientError {
119 fn from(value: ProtocolError) -> Self {
120 Self::Protocol(value)
121 }
122}
123
124pub struct StudioClient<T> {
129 io: T,
130 next_request_id: u32,
131 decoder: FrameDecoder,
132 read_buffer: Vec<u8>,
133 responses: VecDeque<studio::Response>,
134 notifications: VecDeque<studio::Notification>,
135 behavior_role_by_id: HashMap<u32, BehaviorRole>,
136 behavior_id_by_role: HashMap<BehaviorRole, u32>,
137}
138
139impl<T: Read + Write> StudioClient<T> {
140 pub fn new(io: T) -> Self {
141 Self::with_read_buffer(io, 256)
142 }
143
144 fn with_read_buffer(io: T, read_buffer_size: usize) -> Self {
145 Self {
146 io,
147 next_request_id: 0,
148 decoder: FrameDecoder::new(),
149 read_buffer: vec![0; read_buffer_size.max(1)],
150 responses: VecDeque::new(),
151 notifications: VecDeque::new(),
152 behavior_role_by_id: HashMap::new(),
153 behavior_id_by_role: HashMap::new(),
154 }
155 }
156
157 pub fn next_notification(&mut self) -> Option<studio::Notification> {
159 self.notifications.pop_front()
160 }
161
162 pub fn read_notification_blocking(&mut self) -> Result<studio::Notification, ClientError> {
164 loop {
165 if let Some(notification) = self.next_notification() {
166 return Ok(notification);
167 }
168
169 let _ = self.read_next_response()?;
170 }
171 }
172
173 pub fn get_device_info(&mut self) -> Result<zmk::core::GetDeviceInfoResponse, ClientError> {
175 let response = self.call_core(zmk::core::request::RequestType::GetDeviceInfo(true))?;
176 match response.response_type {
177 Some(zmk::core::response::ResponseType::GetDeviceInfo(info)) => Ok(info),
178 _ => Err(ClientError::MissingResponseType),
179 }
180 }
181
182 pub fn get_lock_state(&mut self) -> Result<zmk::core::LockState, ClientError> {
184 let response = self.call_core(zmk::core::request::RequestType::GetLockState(true))?;
185 match response.response_type {
186 Some(zmk::core::response::ResponseType::GetLockState(state)) => {
187 zmk::core::LockState::try_from(state).map_err(|_| ClientError::UnknownEnumValue {
188 field: "core.get_lock_state",
189 value: state,
190 })
191 }
192 _ => Err(ClientError::MissingResponseType),
193 }
194 }
195
196 pub fn reset_settings(&mut self) -> Result<bool, ClientError> {
200 let response = self.call_core(zmk::core::request::RequestType::ResetSettings(true))?;
201 match response.response_type {
202 Some(zmk::core::response::ResponseType::ResetSettings(ok)) => Ok(ok),
203 _ => Err(ClientError::MissingResponseType),
204 }
205 }
206
207 pub fn list_all_behaviors(&mut self) -> Result<Vec<u32>, ClientError> {
209 let response =
210 self.call_behaviors(zmk::behaviors::request::RequestType::ListAllBehaviors(true))?;
211 match response.response_type {
212 Some(zmk::behaviors::response::ResponseType::ListAllBehaviors(items)) => {
213 Ok(items.behaviors)
214 }
215 _ => Err(ClientError::MissingResponseType),
216 }
217 }
218
219 pub fn get_behavior_details(
221 &mut self,
222 behavior_id: u32,
223 ) -> Result<zmk::behaviors::GetBehaviorDetailsResponse, ClientError> {
224 let request = zmk::behaviors::GetBehaviorDetailsRequest { behavior_id };
225 let response = self.call_behaviors(
226 zmk::behaviors::request::RequestType::GetBehaviorDetails(request),
227 )?;
228 match response.response_type {
229 Some(zmk::behaviors::response::ResponseType::GetBehaviorDetails(details)) => {
230 Ok(details)
231 }
232 _ => Err(ClientError::MissingResponseType),
233 }
234 }
235
236 pub fn get_keymap(&mut self) -> Result<zmk::keymap::Keymap, ClientError> {
238 let response = self.call_keymap(zmk::keymap::request::RequestType::GetKeymap(true))?;
239 match response.response_type {
240 Some(zmk::keymap::response::ResponseType::GetKeymap(keymap)) => Ok(keymap),
241 _ => Err(ClientError::MissingResponseType),
242 }
243 }
244
245 pub fn get_physical_layouts(&mut self) -> Result<zmk::keymap::PhysicalLayouts, ClientError> {
247 let response =
248 self.call_keymap(zmk::keymap::request::RequestType::GetPhysicalLayouts(true))?;
249 match response.response_type {
250 Some(zmk::keymap::response::ResponseType::GetPhysicalLayouts(layouts)) => Ok(layouts),
251 _ => Err(ClientError::MissingResponseType),
252 }
253 }
254
255 pub fn set_layer_binding(
257 &mut self,
258 layer_id: u32,
259 key_position: i32,
260 binding: zmk::keymap::BehaviorBinding,
261 ) -> Result<(), ClientError> {
262 let request = zmk::keymap::SetLayerBindingRequest {
263 layer_id,
264 key_position,
265 binding: Some(binding),
266 };
267
268 let response =
269 self.call_keymap(zmk::keymap::request::RequestType::SetLayerBinding(request))?;
270
271 match response.response_type {
272 Some(zmk::keymap::response::ResponseType::SetLayerBinding(raw)) => {
273 let code = zmk::keymap::SetLayerBindingResponse::try_from(raw).map_err(|_| {
274 ClientError::UnknownEnumValue {
275 field: "keymap.set_layer_binding",
276 value: raw,
277 }
278 })?;
279
280 if code == zmk::keymap::SetLayerBindingResponse::SetLayerBindingRespOk {
281 Ok(())
282 } else {
283 Err(ClientError::SetLayerBindingFailed(code))
284 }
285 }
286 _ => Err(ClientError::MissingResponseType),
287 }
288 }
289
290 pub fn get_key_at(
292 &mut self,
293 layer_id: u32,
294 key_position: i32,
295 ) -> Result<Behavior, ClientError> {
296 self.ensure_behavior_catalog()?;
297
298 let keymap = self.get_keymap()?;
299 let binding = binding_at(&keymap, layer_id, key_position).ok_or(
300 ClientError::InvalidLayerOrPosition {
301 layer_id,
302 key_position,
303 },
304 )?;
305
306 Ok(self.resolve_binding(&binding))
307 }
308
309 pub fn resolve_keymap(&mut self) -> Result<Vec<Vec<Behavior>>, ClientError> {
315 self.ensure_behavior_catalog()?;
316 let keymap = self.get_keymap()?;
317
318 let layers = keymap
319 .layers
320 .iter()
321 .map(|layer| {
322 layer
323 .bindings
324 .iter()
325 .map(|binding| self.resolve_binding(binding))
326 .collect()
327 })
328 .collect();
329
330 Ok(layers)
331 }
332
333 fn resolve_binding(&self, binding: &zmk::keymap::BehaviorBinding) -> Behavior {
334 let Ok(binding_behavior_id) = u32::try_from(binding.behavior_id) else {
335 return Behavior::Unknown {
336 behavior_id: binding.behavior_id,
337 param1: binding.param1,
338 param2: binding.param2,
339 };
340 };
341 let Some(role) = self.behavior_role_by_id.get(&binding_behavior_id).copied() else {
342 return Behavior::Unknown {
343 behavior_id: binding.behavior_id,
344 param1: binding.param1,
345 param2: binding.param2,
346 };
347 };
348
349 match role {
350 BehaviorRole::KeyPress => Behavior::KeyPress(HidUsage::from_encoded(binding.param1)),
351 BehaviorRole::KeyToggle => Behavior::KeyToggle(HidUsage::from_encoded(binding.param1)),
352 BehaviorRole::LayerTap => Behavior::LayerTap {
353 layer_id: binding.param1,
354 tap: HidUsage::from_encoded(binding.param2),
355 },
356 BehaviorRole::ModTap => Behavior::ModTap {
357 hold: HidUsage::from_encoded(binding.param1),
358 tap: HidUsage::from_encoded(binding.param2),
359 },
360 BehaviorRole::StickyKey => Behavior::StickyKey(HidUsage::from_encoded(binding.param1)),
361 BehaviorRole::StickyLayer => Behavior::StickyLayer {
362 layer_id: binding.param1,
363 },
364 BehaviorRole::MomentaryLayer => Behavior::MomentaryLayer {
365 layer_id: binding.param1,
366 },
367 BehaviorRole::ToggleLayer => Behavior::ToggleLayer {
368 layer_id: binding.param1,
369 },
370 BehaviorRole::ToLayer => Behavior::ToLayer {
371 layer_id: binding.param1,
372 },
373 BehaviorRole::Bluetooth => Behavior::Bluetooth {
374 command: binding.param1,
375 value: binding.param2,
376 },
377 BehaviorRole::ExternalPower => Behavior::ExternalPower {
378 value: binding.param1,
379 },
380 BehaviorRole::OutputSelection => Behavior::OutputSelection {
381 value: binding.param1,
382 },
383 BehaviorRole::Backlight => Behavior::Backlight {
384 command: binding.param1,
385 value: binding.param2,
386 },
387 BehaviorRole::Underglow => Behavior::Underglow {
388 command: binding.param1,
389 value: binding.param2,
390 },
391 BehaviorRole::MouseKeyPress => Behavior::MouseKeyPress {
392 value: binding.param1,
393 },
394 BehaviorRole::MouseMove => Behavior::MouseMove {
395 value: binding.param1,
396 },
397 BehaviorRole::MouseScroll => Behavior::MouseScroll {
398 value: binding.param1,
399 },
400 BehaviorRole::CapsWord => Behavior::CapsWord,
401 BehaviorRole::KeyRepeat => Behavior::KeyRepeat,
402 BehaviorRole::Reset => Behavior::Reset,
403 BehaviorRole::Bootloader => Behavior::Bootloader,
404 BehaviorRole::SoftOff => Behavior::SoftOff,
405 BehaviorRole::StudioUnlock => Behavior::StudioUnlock,
406 BehaviorRole::GraveEscape => Behavior::GraveEscape,
407 BehaviorRole::Transparent => Behavior::Transparent,
408 BehaviorRole::None => Behavior::None,
409 }
410 }
411
412 pub fn set_key_at(
416 &mut self,
417 layer_id: u32,
418 key_position: i32,
419 behavior: Behavior,
420 ) -> Result<(), ClientError> {
421 self.ensure_behavior_catalog()?;
422 let binding = match behavior {
423 Behavior::KeyPress(key) => zmk::keymap::BehaviorBinding {
424 behavior_id: self.behavior_id_for(BehaviorRole::KeyPress, "Key Press")?,
425 param1: key.to_hid_usage(),
426 param2: 0,
427 },
428 Behavior::KeyToggle(key) => zmk::keymap::BehaviorBinding {
429 behavior_id: self.behavior_id_for(BehaviorRole::KeyToggle, "Key Toggle")?,
430 param1: key.to_hid_usage(),
431 param2: 0,
432 },
433 Behavior::LayerTap {
434 layer_id: hold_layer_id,
435 tap,
436 } => zmk::keymap::BehaviorBinding {
437 behavior_id: self.behavior_id_for(BehaviorRole::LayerTap, "Layer-Tap")?,
438 param1: hold_layer_id,
439 param2: tap.to_hid_usage(),
440 },
441 Behavior::ModTap { hold, tap } => zmk::keymap::BehaviorBinding {
442 behavior_id: self.behavior_id_for(BehaviorRole::ModTap, "Mod-Tap")?,
443 param1: hold.to_hid_usage(),
444 param2: tap.to_hid_usage(),
445 },
446 Behavior::StickyKey(key) => zmk::keymap::BehaviorBinding {
447 behavior_id: self.behavior_id_for(BehaviorRole::StickyKey, "Sticky Key")?,
448 param1: key.to_hid_usage(),
449 param2: 0,
450 },
451 Behavior::StickyLayer {
452 layer_id: target_layer_id,
453 } => zmk::keymap::BehaviorBinding {
454 behavior_id: self.behavior_id_for(BehaviorRole::StickyLayer, "Sticky Layer")?,
455 param1: target_layer_id,
456 param2: 0,
457 },
458 Behavior::MomentaryLayer {
459 layer_id: hold_layer_id,
460 } => zmk::keymap::BehaviorBinding {
461 behavior_id: self
462 .behavior_id_for(BehaviorRole::MomentaryLayer, "Momentary Layer")?,
463 param1: hold_layer_id,
464 param2: 0,
465 },
466 Behavior::ToggleLayer {
467 layer_id: target_layer_id,
468 } => zmk::keymap::BehaviorBinding {
469 behavior_id: self.behavior_id_for(BehaviorRole::ToggleLayer, "Toggle Layer")?,
470 param1: target_layer_id,
471 param2: 0,
472 },
473 Behavior::ToLayer {
474 layer_id: target_layer_id,
475 } => zmk::keymap::BehaviorBinding {
476 behavior_id: self.behavior_id_for(BehaviorRole::ToLayer, "To Layer")?,
477 param1: target_layer_id,
478 param2: 0,
479 },
480 Behavior::Bluetooth { command, value } => zmk::keymap::BehaviorBinding {
481 behavior_id: self.behavior_id_for(BehaviorRole::Bluetooth, "Bluetooth")?,
482 param1: command,
483 param2: value,
484 },
485 Behavior::ExternalPower { value } => zmk::keymap::BehaviorBinding {
486 behavior_id: self.behavior_id_for(BehaviorRole::ExternalPower, "External Power")?,
487 param1: value,
488 param2: 0,
489 },
490 Behavior::OutputSelection { value } => zmk::keymap::BehaviorBinding {
491 behavior_id: self
492 .behavior_id_for(BehaviorRole::OutputSelection, "Output Selection")?,
493 param1: value,
494 param2: 0,
495 },
496 Behavior::Backlight { command, value } => zmk::keymap::BehaviorBinding {
497 behavior_id: self.behavior_id_for(BehaviorRole::Backlight, "Backlight")?,
498 param1: command,
499 param2: value,
500 },
501 Behavior::Underglow { command, value } => zmk::keymap::BehaviorBinding {
502 behavior_id: self.behavior_id_for(BehaviorRole::Underglow, "Underglow")?,
503 param1: command,
504 param2: value,
505 },
506 Behavior::MouseKeyPress { value } => zmk::keymap::BehaviorBinding {
507 behavior_id: self
508 .behavior_id_for(BehaviorRole::MouseKeyPress, "Mouse Key Press")?,
509 param1: value,
510 param2: 0,
511 },
512 Behavior::MouseMove { value } => zmk::keymap::BehaviorBinding {
513 behavior_id: self.behavior_id_for(BehaviorRole::MouseMove, "Mouse Move")?,
514 param1: value,
515 param2: 0,
516 },
517 Behavior::MouseScroll { value } => zmk::keymap::BehaviorBinding {
518 behavior_id: self.behavior_id_for(BehaviorRole::MouseScroll, "Mouse Scroll")?,
519 param1: value,
520 param2: 0,
521 },
522 Behavior::CapsWord => zmk::keymap::BehaviorBinding {
523 behavior_id: self.behavior_id_for(BehaviorRole::CapsWord, "Caps Word")?,
524 param1: 0,
525 param2: 0,
526 },
527 Behavior::KeyRepeat => zmk::keymap::BehaviorBinding {
528 behavior_id: self.behavior_id_for(BehaviorRole::KeyRepeat, "Key Repeat")?,
529 param1: 0,
530 param2: 0,
531 },
532 Behavior::Reset => zmk::keymap::BehaviorBinding {
533 behavior_id: self.behavior_id_for(BehaviorRole::Reset, "Reset")?,
534 param1: 0,
535 param2: 0,
536 },
537 Behavior::Bootloader => zmk::keymap::BehaviorBinding {
538 behavior_id: self.behavior_id_for(BehaviorRole::Bootloader, "Bootloader")?,
539 param1: 0,
540 param2: 0,
541 },
542 Behavior::SoftOff => zmk::keymap::BehaviorBinding {
543 behavior_id: self.behavior_id_for(BehaviorRole::SoftOff, "Soft Off")?,
544 param1: 0,
545 param2: 0,
546 },
547 Behavior::StudioUnlock => zmk::keymap::BehaviorBinding {
548 behavior_id: self.behavior_id_for(BehaviorRole::StudioUnlock, "Studio Unlock")?,
549 param1: 0,
550 param2: 0,
551 },
552 Behavior::GraveEscape => zmk::keymap::BehaviorBinding {
553 behavior_id: self.behavior_id_for(BehaviorRole::GraveEscape, "Grave/Escape")?,
554 param1: 0,
555 param2: 0,
556 },
557 Behavior::Transparent => zmk::keymap::BehaviorBinding {
558 behavior_id: self.behavior_id_for(BehaviorRole::Transparent, "Transparent")?,
559 param1: 0,
560 param2: 0,
561 },
562 Behavior::None => zmk::keymap::BehaviorBinding {
563 behavior_id: self.behavior_id_for(BehaviorRole::None, "None")?,
564 param1: 0,
565 param2: 0,
566 },
567 Behavior::Unknown {
568 behavior_id,
569 param1,
570 param2,
571 } => zmk::keymap::BehaviorBinding {
572 behavior_id,
573 param1,
574 param2,
575 },
576 };
577
578 self.set_layer_binding(layer_id, key_position, binding)
579 }
580
581 pub fn check_unsaved_changes(&mut self) -> Result<bool, ClientError> {
583 let response =
584 self.call_keymap(zmk::keymap::request::RequestType::CheckUnsavedChanges(true))?;
585 match response.response_type {
586 Some(zmk::keymap::response::ResponseType::CheckUnsavedChanges(has_changes)) => {
587 Ok(has_changes)
588 }
589 _ => Err(ClientError::MissingResponseType),
590 }
591 }
592
593 pub fn save_changes(&mut self) -> Result<(), ClientError> {
597 let response = self.call_keymap(zmk::keymap::request::RequestType::SaveChanges(true))?;
598 match response.response_type {
599 Some(zmk::keymap::response::ResponseType::SaveChanges(save)) => match save.result {
600 Some(zmk::keymap::save_changes_response::Result::Ok(_)) => Ok(()),
601 Some(zmk::keymap::save_changes_response::Result::Err(raw)) => {
602 let err = zmk::keymap::SaveChangesErrorCode::try_from(raw).map_err(|_| {
603 ClientError::UnknownEnumValue {
604 field: "keymap.save_changes",
605 value: raw,
606 }
607 })?;
608 Err(ClientError::SaveChangesFailed(err))
609 }
610 None => Err(ClientError::MissingResponseType),
611 },
612 _ => Err(ClientError::MissingResponseType),
613 }
614 }
615
616 pub fn discard_changes(&mut self) -> Result<bool, ClientError> {
620 let response = self.call_keymap(zmk::keymap::request::RequestType::DiscardChanges(true))?;
621 match response.response_type {
622 Some(zmk::keymap::response::ResponseType::DiscardChanges(discarded)) => Ok(discarded),
623 _ => Err(ClientError::MissingResponseType),
624 }
625 }
626
627 pub fn set_active_physical_layout(
629 &mut self,
630 index: u32,
631 ) -> Result<zmk::keymap::Keymap, ClientError> {
632 let response = self.call_keymap(
633 zmk::keymap::request::RequestType::SetActivePhysicalLayout(index),
634 )?;
635 match response.response_type {
636 Some(zmk::keymap::response::ResponseType::SetActivePhysicalLayout(resp)) => {
637 match resp.result {
638 Some(zmk::keymap::set_active_physical_layout_response::Result::Ok(keymap)) => {
639 Ok(keymap)
640 }
641 Some(zmk::keymap::set_active_physical_layout_response::Result::Err(raw)) => {
642 let err = zmk::keymap::SetActivePhysicalLayoutErrorCode::try_from(raw)
643 .map_err(|_| ClientError::UnknownEnumValue {
644 field: "keymap.set_active_physical_layout",
645 value: raw,
646 })?;
647 Err(ClientError::SetActivePhysicalLayoutFailed(err))
648 }
649 None => Err(ClientError::MissingResponseType),
650 }
651 }
652 _ => Err(ClientError::MissingResponseType),
653 }
654 }
655
656 pub fn move_layer(
658 &mut self,
659 start_index: u32,
660 dest_index: u32,
661 ) -> Result<zmk::keymap::Keymap, ClientError> {
662 let request = zmk::keymap::MoveLayerRequest {
663 start_index,
664 dest_index,
665 };
666 let response = self.call_keymap(zmk::keymap::request::RequestType::MoveLayer(request))?;
667 match response.response_type {
668 Some(zmk::keymap::response::ResponseType::MoveLayer(resp)) => match resp.result {
669 Some(zmk::keymap::move_layer_response::Result::Ok(keymap)) => Ok(keymap),
670 Some(zmk::keymap::move_layer_response::Result::Err(raw)) => {
671 let err = zmk::keymap::MoveLayerErrorCode::try_from(raw).map_err(|_| {
672 ClientError::UnknownEnumValue {
673 field: "keymap.move_layer",
674 value: raw,
675 }
676 })?;
677 Err(ClientError::MoveLayerFailed(err))
678 }
679 None => Err(ClientError::MissingResponseType),
680 },
681 _ => Err(ClientError::MissingResponseType),
682 }
683 }
684
685 pub fn add_layer(&mut self) -> Result<zmk::keymap::AddLayerResponseDetails, ClientError> {
687 let response = self.call_keymap(zmk::keymap::request::RequestType::AddLayer(
688 zmk::keymap::AddLayerRequest {},
689 ))?;
690 match response.response_type {
691 Some(zmk::keymap::response::ResponseType::AddLayer(resp)) => match resp.result {
692 Some(zmk::keymap::add_layer_response::Result::Ok(details)) => Ok(details),
693 Some(zmk::keymap::add_layer_response::Result::Err(raw)) => {
694 let err = zmk::keymap::AddLayerErrorCode::try_from(raw).map_err(|_| {
695 ClientError::UnknownEnumValue {
696 field: "keymap.add_layer",
697 value: raw,
698 }
699 })?;
700 Err(ClientError::AddLayerFailed(err))
701 }
702 None => Err(ClientError::MissingResponseType),
703 },
704 _ => Err(ClientError::MissingResponseType),
705 }
706 }
707
708 pub fn remove_layer(&mut self, layer_index: u32) -> Result<(), ClientError> {
710 let request = zmk::keymap::RemoveLayerRequest { layer_index };
711 let response = self.call_keymap(zmk::keymap::request::RequestType::RemoveLayer(request))?;
712 match response.response_type {
713 Some(zmk::keymap::response::ResponseType::RemoveLayer(resp)) => match resp.result {
714 Some(zmk::keymap::remove_layer_response::Result::Ok(_)) => Ok(()),
715 Some(zmk::keymap::remove_layer_response::Result::Err(raw)) => {
716 let err = zmk::keymap::RemoveLayerErrorCode::try_from(raw).map_err(|_| {
717 ClientError::UnknownEnumValue {
718 field: "keymap.remove_layer",
719 value: raw,
720 }
721 })?;
722 Err(ClientError::RemoveLayerFailed(err))
723 }
724 None => Err(ClientError::MissingResponseType),
725 },
726 _ => Err(ClientError::MissingResponseType),
727 }
728 }
729
730 pub fn restore_layer(
732 &mut self,
733 layer_id: u32,
734 at_index: u32,
735 ) -> Result<zmk::keymap::Layer, ClientError> {
736 let request = zmk::keymap::RestoreLayerRequest { layer_id, at_index };
737 let response =
738 self.call_keymap(zmk::keymap::request::RequestType::RestoreLayer(request))?;
739 match response.response_type {
740 Some(zmk::keymap::response::ResponseType::RestoreLayer(resp)) => match resp.result {
741 Some(zmk::keymap::restore_layer_response::Result::Ok(layer)) => Ok(layer),
742 Some(zmk::keymap::restore_layer_response::Result::Err(raw)) => {
743 let err = zmk::keymap::RestoreLayerErrorCode::try_from(raw).map_err(|_| {
744 ClientError::UnknownEnumValue {
745 field: "keymap.restore_layer",
746 value: raw,
747 }
748 })?;
749 Err(ClientError::RestoreLayerFailed(err))
750 }
751 None => Err(ClientError::MissingResponseType),
752 },
753 _ => Err(ClientError::MissingResponseType),
754 }
755 }
756
757 pub fn set_layer_props(
759 &mut self,
760 layer_id: u32,
761 name: impl Into<String>,
762 ) -> Result<(), ClientError> {
763 let request = zmk::keymap::SetLayerPropsRequest {
764 layer_id,
765 name: name.into(),
766 };
767 let response =
768 self.call_keymap(zmk::keymap::request::RequestType::SetLayerProps(request))?;
769 match response.response_type {
770 Some(zmk::keymap::response::ResponseType::SetLayerProps(raw)) => {
771 let code = zmk::keymap::SetLayerPropsResponse::try_from(raw).map_err(|_| {
772 ClientError::UnknownEnumValue {
773 field: "keymap.set_layer_props",
774 value: raw,
775 }
776 })?;
777
778 if code == zmk::keymap::SetLayerPropsResponse::SetLayerPropsRespOk {
779 Ok(())
780 } else {
781 Err(ClientError::SetLayerPropsFailed(code))
782 }
783 }
784 _ => Err(ClientError::MissingResponseType),
785 }
786 }
787
788 fn behavior_id_for(
789 &self,
790 role: BehaviorRole,
791 display_name: &'static str,
792 ) -> Result<i32, ClientError> {
793 let behavior_id = self
794 .behavior_id_by_role
795 .get(&role)
796 .copied()
797 .ok_or(ClientError::MissingBehaviorRole(display_name))?;
798 i32::try_from(behavior_id).map_err(|_| ClientError::BehaviorIdOutOfRange { behavior_id })
799 }
800
801 fn ensure_behavior_catalog(&mut self) -> Result<(), ClientError> {
802 if !self.behavior_role_by_id.is_empty() {
803 return Ok(());
804 }
805
806 let ids = self.list_all_behaviors()?;
807 for id in ids {
808 let details = self.get_behavior_details(id)?;
809 let role = role_from_display_name(&details.display_name);
810 if let Some(role) = role {
811 self.behavior_role_by_id.insert(id, role);
812 self.behavior_id_by_role.entry(role).or_insert(id);
813 }
814 }
815
816 Ok(())
817 }
818
819 fn call_core(
820 &mut self,
821 request_type: zmk::core::request::RequestType,
822 ) -> Result<zmk::core::Response, ClientError> {
823 let request = zmk::core::Request {
824 request_type: Some(request_type),
825 };
826 let rr = self.call(studio::request::Subsystem::Core(request))?;
827
828 match rr.subsystem {
829 Some(studio::request_response::Subsystem::Core(resp)) => Ok(resp),
830 Some(_) => Err(ClientError::UnexpectedSubsystem("core")),
831 None => Err(ClientError::MissingSubsystem),
832 }
833 }
834
835 fn call_behaviors(
836 &mut self,
837 request_type: zmk::behaviors::request::RequestType,
838 ) -> Result<zmk::behaviors::Response, ClientError> {
839 let request = zmk::behaviors::Request {
840 request_type: Some(request_type),
841 };
842 let rr = self.call(studio::request::Subsystem::Behaviors(request))?;
843
844 match rr.subsystem {
845 Some(studio::request_response::Subsystem::Behaviors(resp)) => Ok(resp),
846 Some(_) => Err(ClientError::UnexpectedSubsystem("behaviors")),
847 None => Err(ClientError::MissingSubsystem),
848 }
849 }
850
851 fn call_keymap(
852 &mut self,
853 request_type: zmk::keymap::request::RequestType,
854 ) -> Result<zmk::keymap::Response, ClientError> {
855 let request = zmk::keymap::Request {
856 request_type: Some(request_type),
857 };
858 let rr = self.call(studio::request::Subsystem::Keymap(request))?;
859
860 match rr.subsystem {
861 Some(studio::request_response::Subsystem::Keymap(resp)) => Ok(resp),
862 Some(_) => Err(ClientError::UnexpectedSubsystem("keymap")),
863 None => Err(ClientError::MissingSubsystem),
864 }
865 }
866
867 fn call(
868 &mut self,
869 subsystem: studio::request::Subsystem,
870 ) -> Result<studio::RequestResponse, ClientError> {
871 let request_id = self.next_request_id;
872 self.next_request_id = self.next_request_id.wrapping_add(1);
873
874 let request = studio::Request {
875 request_id,
876 subsystem: Some(subsystem),
877 };
878 let bytes = encode_request(&request);
879 self.io.write_all(&bytes)?;
880
881 loop {
882 let response = self.read_next_response()?;
883 match response.r#type {
884 Some(studio::response::Type::Notification(notification)) => {
885 self.notifications.push_back(notification);
886 }
887 Some(studio::response::Type::RequestResponse(rr)) => {
888 if rr.request_id != request_id {
889 return Err(ClientError::UnexpectedRequestId {
890 expected: request_id,
891 actual: rr.request_id,
892 });
893 }
894
895 if let Some(studio::request_response::Subsystem::Meta(meta)) = &rr.subsystem {
896 match meta.response_type {
897 Some(zmk::meta::response::ResponseType::NoResponse(true)) => {
898 return Err(ClientError::NoResponse);
899 }
900 Some(zmk::meta::response::ResponseType::SimpleError(raw)) => {
901 let cond =
902 zmk::meta::ErrorConditions::try_from(raw).map_err(|_| {
903 ClientError::UnknownEnumValue {
904 field: "meta.simple_error",
905 value: raw,
906 }
907 })?;
908 return Err(ClientError::Meta(cond));
909 }
910 _ => return Err(ClientError::MissingResponseType),
911 }
912 }
913
914 return Ok(rr);
915 }
916 None => return Err(ClientError::MissingResponseType),
917 }
918 }
919 }
920
921 fn read_next_response(&mut self) -> Result<studio::Response, ClientError> {
922 if let Some(response) = self.responses.pop_front() {
923 return Ok(response);
924 }
925
926 loop {
927 let read = self.io.read(&mut self.read_buffer)?;
928 if read == 0 {
929 return Err(ClientError::Io(std::io::Error::new(
930 std::io::ErrorKind::UnexpectedEof,
931 "Transport reached EOF",
932 )));
933 }
934
935 let decoded = decode_responses(&mut self.decoder, &self.read_buffer[..read])?;
936 self.responses.extend(decoded);
937
938 if let Some(response) = self.responses.pop_front() {
939 return Ok(response);
940 }
941 }
942 }
943}
944
945fn binding_at(
946 keymap: &zmk::keymap::Keymap,
947 layer_id: u32,
948 key_position: i32,
949) -> Option<zmk::keymap::BehaviorBinding> {
950 let pos = usize::try_from(key_position).ok()?;
951 let layer = keymap.layers.iter().find(|l| l.id == layer_id)?;
952 layer.bindings.get(pos).copied()
953}
954
955#[cfg(feature = "serial")]
956impl StudioClient<SerialTransport> {
957 pub fn open_serial(path: &str) -> Result<Self, SerialTransportError> {
959 Ok(Self::new(SerialTransport::open(path)?))
960 }
961}
962
963#[cfg(feature = "ble")]
964impl StudioClient<BleTransport> {
965 pub fn connect_ble() -> Result<Self, BleTransportError> {
967 Ok(Self::new(BleTransport::connect_first()?))
968 }
969}