use super::{parse_mouse_mode, MouseMode, ScrollParams};
use crate::mouse::send_input;
use crate::script_engine::instruction::{
InstructionData, InstructionHandler, InstructionMetadata, ScriptError,
};
use crate::script_engine::VMContext;
pub struct ScrollUpHandler;
impl InstructionHandler for ScrollUpHandler {
fn name(&self) -> &str {
"scrollup"
}
#[inline]
fn parse(&self, args: &[&str]) -> Result<InstructionData, ScriptError> {
let (mode, mode_offset) = parse_mouse_mode(args, MouseMode::Send)?;
let value_args = &args[..args.len() - mode_offset];
let (x, y, times) = match value_args.len() {
0 => (None, None, 1), 1 => {
let times = value_args[0].parse::<u32>().map_err(|e| {
ScriptError::ParseError(format!("Invalid times '{}': {}", value_args[0], e))
})?;
if times < 1 || times > 100 {
return Err(ScriptError::ParseError(
"Scroll times must be between 1 and 100".into(),
));
}
(None, None, times)
}
2 => {
let x = value_args[0].parse::<i32>().map_err(|e| {
ScriptError::ParseError(format!(
"Invalid x coordinate '{}': {}",
value_args[0], e
))
})?;
let y = value_args[1].parse::<i32>().map_err(|e| {
ScriptError::ParseError(format!(
"Invalid y coordinate '{}': {}",
value_args[1], e
))
})?;
(Some(x), Some(y), 1)
}
3 => {
let x = value_args[0].parse::<i32>().map_err(|e| {
ScriptError::ParseError(format!(
"Invalid x coordinate '{}': {}",
value_args[0], e
))
})?;
let y = value_args[1].parse::<i32>().map_err(|e| {
ScriptError::ParseError(format!(
"Invalid y coordinate '{}': {}",
value_args[1], e
))
})?;
let times = value_args[2].parse::<u32>().map_err(|e| {
ScriptError::ParseError(format!("Invalid times '{}': {}", value_args[2], e))
})?;
if times < 1 || times > 100 {
return Err(ScriptError::ParseError(
"Scroll times must be between 1 and 100".into(),
));
}
(Some(x), Some(y), times)
}
_ => {
return Err(ScriptError::ParseError(
"Invalid number of arguments. Usage: scrollup [x] [y] [times] [mode]".into(),
));
}
};
if mode == MouseMode::Post && x.is_some() && y.is_none() {
return Err(ScriptError::ParseError(
"PostMessage scroll requires both x and y coordinates".into(),
));
}
let send_inputs = if mode == MouseMode::Send {
vec![send_input::build_scroll_up(120)] } else {
vec![]
};
Ok(InstructionData::Custom(Box::new(ScrollParams {
x,
y,
delta: times as i32, mode,
mode_specified: mode_offset > 0,
send_inputs,
})))
}
#[inline]
fn execute(
&self,
vm: &mut VMContext,
data: &InstructionData,
_metadata: Option<&InstructionMetadata>,
) -> Result<(), ScriptError> {
let params = data.extract_custom::<ScrollParams>("Invalid scroll parameters")?;
let effective_mode = if params.mode_specified {
params.mode
} else {
match super::get_input_mode(vm).as_str() {
"post" => MouseMode::Post,
_ => MouseMode::Send,
}
};
match effective_mode {
MouseMode::Send => {
let screen_coords = if let (Some(x), Some(y)) = (params.x, params.y) {
#[cfg(feature = "script_process_context")]
{
if vm.process.has_hwnd() {
Some(super::convert_to_window_coords(vm, x, y)?)
} else {
Some((x, y))
}
}
#[cfg(not(feature = "script_process_context"))]
{
Some((x, y))
}
} else {
None
};
if let Some((screen_x, screen_y)) = screen_coords {
send_input::set_cursor_pos(screen_x, screen_y).map_err(|e| {
ScriptError::ExecutionError(format!("SetCursorPos failed: {:?}", e))
})?;
for _ in 0..params.delta {
send_input::execute_inputs(¶ms.send_inputs).map_err(|e| {
ScriptError::ExecutionError(format!("Scroll up failed: {:?}", e))
})?;
}
} else {
for _ in 0..params.delta {
send_input::execute_inputs(¶ms.send_inputs).map_err(|e| {
ScriptError::ExecutionError(format!("Scroll up failed: {:?}", e))
})?;
}
}
}
MouseMode::Post => {
#[cfg(feature = "script_process_context")]
{
use crate::mouse::post_message;
let window_x = params.x.unwrap_or(0);
let window_y = params.y.unwrap_or(0);
let (client_x, client_y) =
super::convert_to_client_coords(vm, window_x, window_y)?;
for _ in 0..params.delta {
post_message::post_scroll_up_atomic(
vm.process.get_hwnd_or_err()?,
client_x,
client_y,
120,
);
}
}
#[cfg(not(feature = "script_process_context"))]
{
return Err(ScriptError::ExecutionError(
"PostMessage mode requires 'script_process_context' feature. \
Enable it in Cargo.toml: features = [\"scripts_mouse_with_post\"] \
or use SendInput mode (default)."
.into(),
));
}
}
}
Ok(())
}
}