buttplug/server/device/protocol/
hgod.rs

1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8use crate::server::device::configuration::ProtocolCommunicationSpecifier;
9use crate::{
10  core::{
11    errors::ButtplugDeviceError,
12    message::{ActuatorType, Endpoint},
13  },
14  server::device::{
15    configuration::{UserDeviceDefinition, UserDeviceIdentifier},
16    hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
17    protocol::{
18      generic_protocol_initializer_setup,
19      ProtocolHandler,
20      ProtocolIdentifier,
21      ProtocolInitializer,
22    },
23  },
24  util::{async_manager, sleep},
25};
26use async_trait::async_trait;
27use std::{
28  sync::{
29    atomic::{AtomicU8, Ordering},
30    Arc,
31  },
32  time::Duration,
33};
34
35// Time between Hgod update commands, in milliseconds.
36const HGOD_COMMAND_DELAY_MS: u64 = 100;
37
38generic_protocol_initializer_setup!(Hgod, "hgod");
39
40#[derive(Default)]
41pub struct HgodInitializer {}
42
43#[async_trait]
44impl ProtocolInitializer for HgodInitializer {
45  async fn initialize(
46    &mut self,
47    hardware: Arc<Hardware>,
48    _: &UserDeviceDefinition,
49  ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
50    Ok(Arc::new(Hgod::new(hardware)))
51  }
52}
53
54pub struct Hgod {
55  last_command: Arc<AtomicU8>,
56}
57
58impl Hgod {
59  fn new(hardware: Arc<Hardware>) -> Self {
60    let last_command = Arc::new(AtomicU8::new(0));
61
62    let last_command_clone = last_command.clone();
63    async_manager::spawn(async move {
64      send_hgod_updates(hardware, last_command_clone).await;
65    });
66
67    Self { last_command }
68  }
69}
70
71// HGod toys vibes only last ~100ms seconds.
72async fn send_hgod_updates(device: Arc<Hardware>, data: Arc<AtomicU8>) {
73  loop {
74    let speed = data.load(Ordering::SeqCst);
75    let command = vec![0x55, 0x04, 0, 0, 0, speed];
76    if speed > 0 {
77      if let Err(e) = device
78        .write_value(&HardwareWriteCmd::new(Endpoint::Tx, command, false))
79        .await
80      {
81        error!(
82          "Got an error from a hgod device, exiting control loop: {:?}",
83          e
84        );
85        break;
86      }
87    }
88    sleep(Duration::from_millis(HGOD_COMMAND_DELAY_MS)).await;
89  }
90}
91
92impl ProtocolHandler for Hgod {
93  fn handle_scalar_cmd(
94    &self,
95    commands: &[Option<(ActuatorType, u32)>],
96  ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
97    if let Some(cmd) = commands[0] {
98      self.last_command.store(cmd.1 as u8, Ordering::SeqCst);
99    }
100    Ok(vec![])
101  }
102}