简体   繁体   中英

How to press keys with `enigo` in rust?

There is a test.json :

[
    [1000,800,1],
    [1500,0,2],
    [1600,0,3],
    [2500,300,4]
]

The first element is start time (millisecond).
The second element is the length of time (millisecond).
The third element is key,1\2\3\4 represent a\b\c\d respectively.

I want to press the corresponding keys when the start time reaches the value of the first element, and keep the length of the second element.

Take the first group of data as an example:
When the time comes to 1000 milliseconds, press key 'a' for 800 milliseconds.

I prepare to use crate delay_timer , enigo , serde_json ,and below is my code:

There is only a little custom code. Search with the keyword Here is my code start . The other code is template from delay_timer example.

Cargo.toml

[dependencies]
delay_timer = "0.10.1"
enigo = "0.0.14"
serde_json = "1.0"
anyhow = "1.0.51"
smol = "1.2.5"

main.rs

use anyhow::Result;
use delay_timer::prelude::*;
use enigo::*;
use serde_json::{Result as SResult, Value};
use smol::Timer;
use std::fs::File;
use std::io::BufReader;
use std::thread;
use std::time::Duration;

fn main() -> Result<()> {
    let delay_timer = DelayTimerBuilder::default().build();
    // Create the task
    // A chain of task instances.
    let task_instance_chain = delay_timer
        .insert_task(build_task_async_print().unwrap())
        .unwrap();

    // Get the running instance of task 1.
    let task_instance = task_instance_chain.next_with_wait().unwrap();

    // This while loop avoids removing the task before it completes
    while task_instance.get_state() != delay_timer::prelude::instance::COMPLETED {}

    // Remove task which id is 1.
    delay_timer.remove_task(1).unwrap();
    Ok(())
}

fn build_task_async_print() -> Result<Task, TaskError> {
    let mut task_builder = TaskBuilder::default();

    let body = create_async_fn_body!({
        //Here is my code start
        let json_data = get_json().unwrap();

        for element in json_data.as_array().unwrap().iter() {
            Timer::after(Duration::from_millis(element[0].as_f64().unwrap() as u64)).await;
            println!("the value is: {}", element[0]);

            //How to complete the code that uses enigo to press the corresponding keys?
            let mut enigo = Enigo::new();
            enigo.key_down(Key::Layout('a'));
            thread::sleep(Duration::from_millis(element[1].as_f64().unwrap() as u64));
            enigo.key_up(Key::Layout('a'));
        }
        //Here is my code end
    });

    // Build the task and assign 1 to it
    task_builder.set_task_id(1).spawn(body)
}

//Get json data from json file
fn get_json() -> SResult<Value> {
    let file = File::open("test.json").expect("file should open read only");
    let reader = BufReader::new(file);
    let mut v: Value = serde_json::from_reader(reader)?;
    Ok(v.take())
}

Question:
How to complete the code that uses enigo to press the corresponding keys?

Quick and dirty

Under the assumption that you will only use lowercase ASCII chars, you can do this:

use anyhow::Result;
use delay_timer::prelude::*;
use enigo::*;
use serde_json::{Result as SResult, Value};
use smol::Timer;
use std::fs::File;
use std::io::BufReader;
use std::thread;
use std::time::Duration;

fn main() -> Result<()> {
    let delay_timer = DelayTimerBuilder::default().build();
    // Create the task
    // A chain of task instances.
    let task_instance_chain = delay_timer
        .insert_task(build_task_async_print().unwrap())
        .unwrap();

    // Get the running instance of task 1.
    let task_instance = task_instance_chain.next_with_wait().unwrap();

    // This while loop avoids removing the task before it completes
    while task_instance.get_state() != delay_timer::prelude::instance::COMPLETED {}

    // Remove task which id is 1.
    delay_timer.remove_task(1).unwrap();
    Ok(())
}

fn build_task_async_print() -> Result<Task, TaskError> {
    let mut task_builder = TaskBuilder::default();

    let body = create_async_fn_body!({
        //Here is my code start
        let json_data = get_json().unwrap();

        for element in json_data.as_array().unwrap().iter() {
            Timer::after(Duration::from_millis(element[0].as_f64().unwrap() as u64)).await;
            println!("the value is: {}", element[0]);

            //How to complete the code that uses enigo to press the corresponding keys?
            let mut enigo = Enigo::new();
            enigo.key_down(Key::Layout(((element[2].as_u64().unwrap() + 96) as u8) as char));
            thread::sleep(Duration::from_millis(element[1].as_f64().unwrap() as u64));
            enigo.key_up(Key::Layout(((element[2].as_u64().unwrap() + 96) as u8) as char));
        }
        //Here is my code end
    });

    // Build the task and assign 1 to it
    task_builder.set_task_id(1).spawn(body)
}

//Get json data from json file
fn get_json() -> SResult<Value> {
    let file = File::open("test.json").expect("file should open read only");
    let reader = BufReader::new(file);
    let mut v: Value = serde_json::from_reader(reader)?;
    Ok(v.take())
}

Better Method

If you have control over the JSON, I would definetly just put the chars right into the array (or at least use their decimal ASCII codes) or even better use JSON objects that can easily be parsed into rust structs.

So my proposed JSON would look like this:

[
    {
        "startTime": 1000,
        "duration": 800,
        "key": "a"
    },
    {
        "startTime": 1500,
        "duration": 0,
        "key": "b"
    },
    {
        "startTime": 1600,
        "duration": 0,
        "key": "c"
    },
    {
        "startTime": 2500,
        "duration": 300,
        "key": "d"
    }
]

and then you can easily parse it like so:

use anyhow::Result;
use delay_timer::prelude::*;
use enigo::*;
use serde::Deserialize;
use smol::Timer;
use std::thread;
use std::time::Duration;

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct EnigoCommand {
    pub start_time: u64,
    pub duration: u64,
    pub key: char
}

fn main() -> Result<()> {
    let delay_timer = DelayTimerBuilder::default().build();

    let task_instance_chain = delay_timer
        .insert_task(build_task_async_print().unwrap())
        .unwrap();
    let task_instance = task_instance_chain.next_with_wait().unwrap();

    while task_instance.get_state() != delay_timer::prelude::instance::COMPLETED {}

    delay_timer.remove_task(1).unwrap();
    Ok(())
}

fn build_task_async_print() -> Result<Task, TaskError> {
    let mut task_builder = TaskBuilder::default();

    let body = create_async_fn_body!({
        let commands = get_commands().expect("Failed to read commands from json file.");

        for command in commands.iter() {
            Timer::after(Duration::from_millis(command.start_time)).await;

            let mut enigo = Enigo::new();
            enigo.key_down(Key::Layout(command.key));
            thread::sleep(Duration::from_millis(command.duration));
            enigo.key_up(Key::Layout(command.key));
        }
    });
    task_builder.set_task_id(1).spawn(body)
}

fn get_commands() -> Result<Vec<EnigoCommand>, Box<dyn std::error::Error>> {
    let file = std::fs::read_to_string("test.json")?;
    let commands: Vec<EnigoCommand> = serde_json::from_str(file.as_str())?;
    Ok(commands)
}

Note that this will also need the serde dependency with the derive feature:

[dependencies]
delay_timer = "0.10.1"
enigo = "0.0.14"
serde={version = "1.0", features = ["derive"] }
serde_json = "1.0"
anyhow = "1.0.51"
smol = "1.2.5"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM