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