From 2720dee7de51ac7911d57b18fef81858feb14205 Mon Sep 17 00:00:00 2001 From: Peter Hayman Date: Thu, 3 Oct 2024 08:41:44 +1000 Subject: [PATCH] feat: auto keypress --- Cargo.lock | 215 +++++++++++++++++++++ crates/forky_cli/Cargo.toml | 3 + crates/forky_cli/examples/cli.rs | 6 +- crates/forky_cli/src/auto_mod/run.rs | 1 + crates/forky_cli/src/common/forky_cli.rs | 1 + crates/forky_cli/src/key/command.rs | 26 +++ crates/forky_cli/src/key/input_command.rs | 85 ++++++++ crates/forky_cli/src/key/input_sequence.rs | 57 ++++++ crates/forky_cli/src/key/mod.rs | 9 + crates/forky_cli/src/lib.rs | 10 +- crates/forky_cli/src/main.rs | 6 +- 11 files changed, 409 insertions(+), 10 deletions(-) create mode 100644 crates/forky_cli/src/key/command.rs create mode 100644 crates/forky_cli/src/key/input_command.rs create mode 100644 crates/forky_cli/src/key/input_sequence.rs create mode 100644 crates/forky_cli/src/key/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 53356291..92694d71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -647,6 +647,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f" +dependencies = [ + "block-sys", + "objc2", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -855,6 +874,46 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.14" @@ -1029,6 +1088,23 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "enigo" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0087a01fc8591217447d28005379fb5a183683cc83f0a4707af28cc6603f70fb" +dependencies = [ + "core-graphics", + "foreign-types-shared", + "icrate", + "libc", + "log", + "objc2", + "windows", + "xkbcommon", + "xkeysym", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -1147,6 +1223,33 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "forky" version = "0.2.0-rc.6" @@ -1190,6 +1293,7 @@ dependencies = [ "clap", "colorize", "cssparser", + "enigo", "extend", "forky_core 0.2.0-rc.6", "forky_fs 0.2.0-rc.6", @@ -1794,6 +1898,16 @@ dependencies = [ "want", ] +[[package]] +name = "icrate" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642" +dependencies = [ + "block2", + "objc2", +] + [[package]] name = "idna" version = "0.5.0" @@ -2132,6 +2246,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + [[package]] name = "mime" version = "0.3.17" @@ -2263,6 +2386,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + [[package]] name = "object" version = "0.36.4" @@ -3766,6 +3911,59 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3923,6 +4121,23 @@ dependencies = [ "memchr", ] +[[package]] +name = "xkbcommon" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e" +dependencies = [ + "libc", + "memmap2", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + [[package]] name = "xxhash-rust" version = "0.8.12" diff --git a/crates/forky_cli/Cargo.toml b/crates/forky_cli/Cargo.toml index 822e9b49..e9fbd48f 100644 --- a/crates/forky_cli/Cargo.toml +++ b/crates/forky_cli/Cargo.toml @@ -34,6 +34,9 @@ colorize = { workspace = true } ## style cssparser = { workspace = true } +## key +enigo = "0.2.1" + ## serve axum = { workspace = true } axum-server = { workspace = true } diff --git a/crates/forky_cli/examples/cli.rs b/crates/forky_cli/examples/cli.rs index 5f290c21..3253bdcc 100644 --- a/crates/forky_cli/examples/cli.rs +++ b/crates/forky_cli/examples/cli.rs @@ -1,4 +1,4 @@ -use forky_cli::*; -use forky_fs::*; +use ::forky_cli::prelude::*; +use forky_fs::prelude::*; -fn main() -> anyhow::Result<()> { common::ForkyCli.run_with_cli_args() } +fn main() -> anyhow::Result<()> { ForkyCli.run_with_cli_args() } diff --git a/crates/forky_cli/src/auto_mod/run.rs b/crates/forky_cli/src/auto_mod/run.rs index 53d3b6b4..fbb0b0f5 100644 --- a/crates/forky_cli/src/auto_mod/run.rs +++ b/crates/forky_cli/src/auto_mod/run.rs @@ -21,6 +21,7 @@ pub fn run() -> Result<()> { Ok(dirs) => dirs .map(|e| e.unwrap().path()) .for_each(|p| run_for_crate(p)), + // what does this do? _ => run_for_crate(env::current_dir()?), } Ok(()) diff --git a/crates/forky_cli/src/common/forky_cli.rs b/crates/forky_cli/src/common/forky_cli.rs index e07ec896..8c987caa 100644 --- a/crates/forky_cli/src/common/forky_cli.rs +++ b/crates/forky_cli/src/common/forky_cli.rs @@ -15,6 +15,7 @@ impl Subcommand for ForkyCli { Box::new(server::ServerCommand), Box::new(style::StyleCommand), Box::new(auto_mod::AutoModCommand), + Box::new(key::AutoKeyCommand), ] } } diff --git a/crates/forky_cli/src/key/command.rs b/crates/forky_cli/src/key/command.rs new file mode 100644 index 00000000..226b4215 --- /dev/null +++ b/crates/forky_cli/src/key/command.rs @@ -0,0 +1,26 @@ +use super::*; +use anyhow::Result; +use enigo::*; +use forky_fs::prelude::*; +pub struct AutoKeyCommand; + + +impl Subcommand for AutoKeyCommand { + fn name(&self) -> &'static str { "key" } + fn about(&self) -> &'static str { "automate keypresses" } + + fn run(&self, _args: &clap::ArgMatches) -> Result<()> { + InputSequence::default() + // exit dock + .input(Key::Escape) + .ulaunch(1, "r beetmash-biz") + .ulaunch(2, "r beetmash-api") + .ulaunch(3, "r beetmash-site") + .ulaunch(4, "r beetmash") + .ulaunch(5, "r beet") + // .command("code /home/pete/me/beetmash-biz") + // .command("cd ~ && code .") + .run()?; + Ok(()) + } +} diff --git a/crates/forky_cli/src/key/input_command.rs b/crates/forky_cli/src/key/input_command.rs new file mode 100644 index 00000000..45513e15 --- /dev/null +++ b/crates/forky_cli/src/key/input_command.rs @@ -0,0 +1,85 @@ +use anyhow::Result; +use enigo::Direction::*; +use enigo::*; +use std::time::Duration; + +#[derive(Debug, Clone)] +pub enum Input { + Key(Key), + Combo(Vec), + Text(String), + Delay(Duration), + Command(String), +} + +impl Into for Key { + fn into(self) -> Input { + Input::Key(self) + } +} + +impl Into for Vec { + fn into(self) -> Input { + Input::Combo(self) + } +} + +impl Into for String { + fn into(self) -> Input { + Input::Text(self) + } +} + +impl Into for &str { + fn into(self) -> Input { + Input::Text(self.to_string()) + } +} + +impl Into for Duration { + fn into(self) -> Input { + Input::Delay(self) + } +} + +impl Input { + pub fn run(&self, enigo: &mut Enigo) -> Result<()> { + match self { + Input::Key(key) => { + enigo.key(*key, Click)?; + } + Input::Combo(combo) => { + let mut held: Vec<&Key> = combo.iter().collect(); + let last = held.pop().ok_or_else(|| anyhow::anyhow!("no key"))?; + + for key in held.iter() { + enigo.key(**key, Press)?; + } + enigo.key(*last, Click)?; + for key in held.iter().rev() { + enigo.key(**key, Release)?; + } + } + Input::Text(text) => { + enigo.text(text)?; + } + Input::Delay(delay) => { + std::thread::sleep(*delay); + } + Input::Command(command) => { + // let mut args = command.split_whitespace(); + // let first = args.next().unwrap(); + let output = std::process::Command::new("sh") + // std::process::Command::new(first) + .arg("-c") + .args(command.split_whitespace()) + // .args(args) + .output()?; + println!("{:?}", output); + output.stdout.iter().for_each(|&c| print!("{}", c as char)); + output.stderr.iter().for_each(|&c| eprint!("{}", c as char)); + } + } + Ok(()) + } +} diff --git a/crates/forky_cli/src/key/input_sequence.rs b/crates/forky_cli/src/key/input_sequence.rs new file mode 100644 index 00000000..01831a28 --- /dev/null +++ b/crates/forky_cli/src/key/input_sequence.rs @@ -0,0 +1,57 @@ +use super::*; +use anyhow::Result; +use enigo::{Enigo, Key, Settings}; +use std::time::Duration; + +#[derive(Debug)] +pub struct InputSequence { + commands: Vec, + delay: Duration, +} + +impl Default for InputSequence { + fn default() -> Self { + Self { + delay: Duration::from_millis(500), + commands: Vec::new(), + } + } +} + +impl InputSequence { + pub fn new() -> Self { + Self::default() + } + + pub fn input(mut self, command: impl Into) -> Self { + self.commands.push(command.into()); + self + } + pub fn command(mut self, command: impl Into) -> Self { + self.commands.push(Input::Command(command.into())); + self + } + + pub fn ulaunch(self, workspace: usize, search: &str) -> Self { + self.input(vec![ + Key::Meta, + Key::Unicode(workspace.to_string().chars().next().unwrap()), + ]) + .input(vec![Key::Alt, Key::Space]) + .input(search) + .input(Key::UpArrow) // ensure its the last result, exact match + .input(Key::Return) + .input(Duration::from_secs(1)) + } + + pub fn run(&mut self) -> Result<()> { + self.run_with_enigo(&mut Enigo::new(&Settings::default())?) + } + pub fn run_with_enigo(&mut self, enigo: &mut Enigo) -> Result<()> { + for command in self.commands.iter() { + command.run(enigo)?; + std::thread::sleep(self.delay); + } + Ok(()) + } +} diff --git a/crates/forky_cli/src/key/mod.rs b/crates/forky_cli/src/key/mod.rs new file mode 100644 index 00000000..686a9825 --- /dev/null +++ b/crates/forky_cli/src/key/mod.rs @@ -0,0 +1,9 @@ +pub mod command; +#[allow(unused_imports)] +pub use self::command::*; +pub mod input_command; +#[allow(unused_imports)] +pub use self::input_command::*; +pub mod input_sequence; +#[allow(unused_imports)] +pub use self::input_sequence::*; diff --git a/crates/forky_cli/src/lib.rs b/crates/forky_cli/src/lib.rs index 463b0502..b73ce912 100644 --- a/crates/forky_cli/src/lib.rs +++ b/crates/forky_cli/src/lib.rs @@ -1,14 +1,16 @@ +// #![feature(never_type, never_type_fallback)] #![feature(async_closure, let_chains)] pub mod auto_mod; pub mod common; pub mod server; pub mod style; +pub mod key; pub mod watch; pub mod prelude { - pub use crate::auto_mod::*; + // pub use crate::auto_mod::*; pub use crate::common::*; - pub use crate::server::*; - pub use crate::style::*; - pub use crate::watch::*; + // pub use crate::server::*; + // pub use crate::style::*; + // pub use crate::watch::*; } diff --git a/crates/forky_cli/src/main.rs b/crates/forky_cli/src/main.rs index 5f290c21..3253bdcc 100644 --- a/crates/forky_cli/src/main.rs +++ b/crates/forky_cli/src/main.rs @@ -1,4 +1,4 @@ -use forky_cli::*; -use forky_fs::*; +use ::forky_cli::prelude::*; +use forky_fs::prelude::*; -fn main() -> anyhow::Result<()> { common::ForkyCli.run_with_cli_args() } +fn main() -> anyhow::Result<()> { ForkyCli.run_with_cli_args() }