#[cfg(feature = "extras")]
use std::time::Duration;
use crate::device::Device;
use crate::error::{MakcuError, Result};
use crate::protocol::{builder as proto_builder, constants};
use crate::types::{Button, LockTarget};
#[cfg(feature = "extras")]
type ExtrasStepFn = Box<dyn FnOnce(&Device) -> Result<()> + Send>;
enum BatchStep {
Native(Vec<u8>),
#[cfg(feature = "extras")]
Extras(ExtrasStepFn),
}
pub struct BatchBuilder<'d> {
device: &'d Device,
steps: Vec<BatchStep>,
error: Option<MakcuError>,
}
impl<'d> BatchBuilder<'d> {
pub(crate) fn new(device: &'d Device) -> Self {
Self {
device,
steps: Vec::new(),
error: None,
}
}
fn push_built(mut self, result: Result<proto_builder::CommandBuf>) -> Self {
match result {
Ok(cmd) => self.steps.push(BatchStep::Native(cmd.as_bytes().to_vec())),
Err(e) => {
if self.error.is_none() {
self.error = Some(e);
}
}
}
self
}
pub fn move_xy(self, x: i32, y: i32) -> Self {
self.push_built(proto_builder::build_move(x, y))
}
pub fn silent_move(self, x: i32, y: i32) -> Self {
self.push_built(proto_builder::build_silent_move(x, y))
}
pub fn button_down(mut self, button: Button) -> Self {
self.steps.push(BatchStep::Native(
constants::button_down_cmd(button).to_vec(),
));
self
}
pub fn button_up(mut self, button: Button) -> Self {
self.steps
.push(BatchStep::Native(constants::button_up_cmd(button).to_vec()));
self
}
pub fn button_up_force(mut self, button: Button) -> Self {
self.steps.push(BatchStep::Native(
constants::button_force_up_cmd(button).to_vec(),
));
self
}
pub fn wheel(self, delta: i32) -> Self {
self.push_built(proto_builder::build_wheel(delta))
}
pub fn set_lock(mut self, target: LockTarget, locked: bool) -> Self {
self.steps.push(BatchStep::Native(
constants::lock_set_cmd(target, locked).to_vec(),
));
self
}
pub fn enable_catch(mut self, button: Button) -> Self {
self.steps.push(BatchStep::Native(
constants::catch_enable_cmd(button).to_vec(),
));
self
}
pub fn enable_button_stream(mut self) -> Self {
self.steps
.push(BatchStep::Native(constants::CMD_BUTTONS_ON.to_vec()));
self
}
pub fn disable_button_stream(mut self) -> Self {
self.steps
.push(BatchStep::Native(constants::CMD_BUTTONS_OFF.to_vec()));
self
}
pub fn execute(self) -> Result<()> {
if let Some(e) = self.error {
return Err(e);
}
let mut native_buf: Vec<u8> = Vec::new();
for step in self.steps {
match step {
BatchStep::Native(data) => {
native_buf.extend_from_slice(&data);
}
#[cfg(feature = "extras")]
BatchStep::Extras(f) => {
if !native_buf.is_empty() {
flush_native(self.device, &mut native_buf)?;
}
f(self.device)?;
}
}
}
if !native_buf.is_empty() {
flush_native(self.device, &mut native_buf)?;
}
Ok(())
}
}
fn flush_native(device: &Device, buf: &mut Vec<u8>) -> Result<()> {
let data = std::mem::take(buf);
device.transport().send_command(
data,
true, device.timeout(),
)?;
Ok(())
}
#[cfg(feature = "extras")]
impl<'d> BatchBuilder<'d> {
pub fn start_catch(mut self, button: Button) -> Self {
self.steps.push(BatchStep::Native(
constants::lock_set_cmd(constants::button_to_lock_target(button), true).to_vec(),
));
self.steps.push(BatchStep::Native(
constants::catch_enable_cmd(button).to_vec(),
));
self
}
pub fn stop_catch(mut self, button: Button) -> Self {
self.steps.push(BatchStep::Native(
constants::lock_set_cmd(constants::button_to_lock_target(button), false).to_vec(),
));
self
}
pub fn click(mut self, button: Button, hold: Duration) -> Self {
self.steps.push(BatchStep::Extras(Box::new(move |dev| {
dev.click(button, hold)
})));
self
}
pub fn click_sequence(
mut self,
button: Button,
hold: Duration,
count: u32,
interval: Duration,
) -> Self {
self.steps.push(BatchStep::Extras(Box::new(move |dev| {
dev.click_sequence(button, hold, count, interval)
})));
self
}
pub fn move_smooth(mut self, x: i32, y: i32, steps: u32, interval: Duration) -> Self {
self.steps.push(BatchStep::Extras(Box::new(move |dev| {
dev.move_smooth(x, y, steps, interval)
})));
self
}
pub fn move_pattern(
mut self,
waypoints: Vec<(i32, i32)>,
steps: u32,
interval: Duration,
) -> Self {
self.steps.push(BatchStep::Extras(Box::new(move |dev| {
dev.move_pattern(&waypoints, steps, interval)
})));
self
}
pub fn drag(mut self, button: Button, x: i32, y: i32, steps: u32, interval: Duration) -> Self {
self.steps.push(BatchStep::Extras(Box::new(move |dev| {
dev.drag(button, x, y, steps, interval)
})));
self
}
}
#[cfg(feature = "async")]
use crate::device::AsyncDevice;
#[cfg(feature = "async")]
enum AsyncBatchStep {
Native(Vec<u8>),
#[cfg(feature = "extras")]
Click {
button: Button,
hold: Duration,
},
#[cfg(feature = "extras")]
ClickSequence {
button: Button,
hold: Duration,
count: u32,
interval: Duration,
},
#[cfg(feature = "extras")]
MoveSmooth {
x: i32,
y: i32,
steps: u32,
interval: Duration,
},
#[cfg(feature = "extras")]
MovePattern {
waypoints: Vec<(i32, i32)>,
steps: u32,
interval: Duration,
},
#[cfg(feature = "extras")]
Drag {
button: Button,
x: i32,
y: i32,
steps: u32,
interval: Duration,
},
}
#[cfg(feature = "async")]
pub struct AsyncBatchBuilder<'d> {
device: &'d AsyncDevice,
steps: Vec<AsyncBatchStep>,
error: Option<MakcuError>,
}
#[cfg(feature = "async")]
impl<'d> AsyncBatchBuilder<'d> {
pub(crate) fn new(device: &'d AsyncDevice) -> Self {
Self {
device,
steps: Vec::new(),
error: None,
}
}
fn push_built(mut self, result: Result<proto_builder::CommandBuf>) -> Self {
match result {
Ok(cmd) => self
.steps
.push(AsyncBatchStep::Native(cmd.as_bytes().to_vec())),
Err(e) => {
if self.error.is_none() {
self.error = Some(e);
}
}
}
self
}
pub fn move_xy(self, x: i32, y: i32) -> Self {
self.push_built(proto_builder::build_move(x, y))
}
pub fn silent_move(self, x: i32, y: i32) -> Self {
self.push_built(proto_builder::build_silent_move(x, y))
}
pub fn button_down(mut self, button: Button) -> Self {
self.steps.push(AsyncBatchStep::Native(
constants::button_down_cmd(button).to_vec(),
));
self
}
pub fn button_up(mut self, button: Button) -> Self {
self.steps.push(AsyncBatchStep::Native(
constants::button_up_cmd(button).to_vec(),
));
self
}
pub fn button_up_force(mut self, button: Button) -> Self {
self.steps.push(AsyncBatchStep::Native(
constants::button_force_up_cmd(button).to_vec(),
));
self
}
pub fn wheel(self, delta: i32) -> Self {
self.push_built(proto_builder::build_wheel(delta))
}
pub fn set_lock(mut self, target: LockTarget, locked: bool) -> Self {
self.steps.push(AsyncBatchStep::Native(
constants::lock_set_cmd(target, locked).to_vec(),
));
self
}
pub fn enable_catch(mut self, button: Button) -> Self {
self.steps.push(AsyncBatchStep::Native(
constants::catch_enable_cmd(button).to_vec(),
));
self
}
pub fn enable_button_stream(mut self) -> Self {
self.steps
.push(AsyncBatchStep::Native(constants::CMD_BUTTONS_ON.to_vec()));
self
}
pub fn disable_button_stream(mut self) -> Self {
self.steps
.push(AsyncBatchStep::Native(constants::CMD_BUTTONS_OFF.to_vec()));
self
}
pub async fn execute(self) -> Result<()> {
if let Some(e) = self.error {
return Err(e);
}
let mut native_buf: Vec<u8> = Vec::new();
for step in self.steps {
match step {
AsyncBatchStep::Native(data) => {
native_buf.extend_from_slice(&data);
}
#[cfg(feature = "extras")]
extras_step => {
if !native_buf.is_empty() {
async_flush_native(self.device, &mut native_buf).await?;
}
execute_extras_step(self.device, extras_step).await?;
}
}
}
if !native_buf.is_empty() {
async_flush_native(self.device, &mut native_buf).await?;
}
Ok(())
}
}
#[cfg(feature = "async")]
async fn async_flush_native(device: &AsyncDevice, buf: &mut Vec<u8>) -> Result<()> {
let data = std::mem::take(buf);
device
.transport()
.send_command_async(data, true, device.timeout())
.await?;
Ok(())
}
#[cfg(all(feature = "async", feature = "extras"))]
async fn execute_extras_step(device: &AsyncDevice, step: AsyncBatchStep) -> Result<()> {
match step {
AsyncBatchStep::Native(_) => unreachable!(),
AsyncBatchStep::Click { button, hold } => device.click(button, hold).await,
AsyncBatchStep::ClickSequence {
button,
hold,
count,
interval,
} => device.click_sequence(button, hold, count, interval).await,
AsyncBatchStep::MoveSmooth {
x,
y,
steps,
interval,
} => device.move_smooth(x, y, steps, interval).await,
AsyncBatchStep::MovePattern {
waypoints,
steps,
interval,
} => device.move_pattern(&waypoints, steps, interval).await,
AsyncBatchStep::Drag {
button,
x,
y,
steps,
interval,
} => device.drag(button, x, y, steps, interval).await,
}
}
#[cfg(all(feature = "async", feature = "extras"))]
impl<'d> AsyncBatchBuilder<'d> {
pub fn start_catch(mut self, button: Button) -> Self {
self.steps.push(AsyncBatchStep::Native(
constants::lock_set_cmd(constants::button_to_lock_target(button), true).to_vec(),
));
self.steps.push(AsyncBatchStep::Native(
constants::catch_enable_cmd(button).to_vec(),
));
self
}
pub fn stop_catch(mut self, button: Button) -> Self {
self.steps.push(AsyncBatchStep::Native(
constants::lock_set_cmd(constants::button_to_lock_target(button), false).to_vec(),
));
self
}
pub fn click(mut self, button: Button, hold: Duration) -> Self {
self.steps.push(AsyncBatchStep::Click { button, hold });
self
}
pub fn click_sequence(
mut self,
button: Button,
hold: Duration,
count: u32,
interval: Duration,
) -> Self {
self.steps.push(AsyncBatchStep::ClickSequence {
button,
hold,
count,
interval,
});
self
}
pub fn move_smooth(mut self, x: i32, y: i32, steps: u32, interval: Duration) -> Self {
self.steps.push(AsyncBatchStep::MoveSmooth {
x,
y,
steps,
interval,
});
self
}
pub fn move_pattern(
mut self,
waypoints: Vec<(i32, i32)>,
steps: u32,
interval: Duration,
) -> Self {
self.steps.push(AsyncBatchStep::MovePattern {
waypoints,
steps,
interval,
});
self
}
pub fn drag(mut self, button: Button, x: i32, y: i32, steps: u32, interval: Duration) -> Self {
self.steps.push(AsyncBatchStep::Drag {
button,
x,
y,
steps,
interval,
});
self
}
}
#[cfg(all(feature = "async", not(feature = "extras")))]
async fn execute_extras_step(_device: &AsyncDevice, _step: AsyncBatchStep) -> Result<()> {
unreachable!()
}