diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 723d06a..9ee4df2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -40,7 +40,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y pkg-config libssl-dev + sudo apt-get install -y pkg-config libssl-dev openssl - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: @@ -75,7 +75,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y pkg-config libssl-dev + sudo apt-get install -y pkg-config libssl-dev openssl - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: diff --git a/Cargo.lock b/Cargo.lock index 21cf1e8..87fbca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,6 +129,25 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b81e1519b0d82120d2fd469d5bfb2919a9361c48b02d82d04befc1cdd2002452" +[[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 = "built" version = "0.7.4" @@ -223,12 +242,80 @@ dependencies = [ "windows-sys", ] +[[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" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +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 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "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 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -269,12 +356,40 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "either" 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 0.23.2", + "foreign-types-shared", + "icrate", + "libc", + "log", + "objc2", + "windows 0.56.0", + "xkbcommon", + "xkeysym", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -325,6 +440,33 @@ dependencies = [ "spin", ] +[[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 = "getrandom" version = "0.2.15" @@ -379,7 +521,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -391,6 +533,16 @@ dependencies = [ "cc", ] +[[package]] +name = "icrate" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642" +dependencies = [ + "block2", + "objc2", +] + [[package]] name = "image" version = "0.25.2" @@ -533,6 +685,16 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "libfuzzer-sys" version = "0.4.7" @@ -639,6 +801,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 = "memoffset" version = "0.7.1" @@ -729,6 +900,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -779,6 +959,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 = "once_cell" version = "1.19.0" @@ -828,6 +1030,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-utils" version = "0.1.0" @@ -983,6 +1191,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.37" @@ -1239,6 +1456,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -1264,6 +1495,7 @@ version = "0.1.0" dependencies = [ "asciicast", "colored", + "enigo", "image", "nix 0.29.0", "ptyprocess", @@ -1275,6 +1507,7 @@ dependencies = [ "ssh2", "toml", "vte", + "xcap", ] [[package]] @@ -1502,6 +1735,36 @@ 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 0.56.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -1511,6 +1774,137 @@ dependencies = [ "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 0.56.0", + "windows-interface 0.56.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings", + "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-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +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-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +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-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -1650,6 +2044,52 @@ dependencies = [ "memchr", ] +[[package]] +name = "xcap" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a584e18d74df1db4bd35947d61e70c25c81a73db48add6eb4e00ad2227a51258" +dependencies = [ + "core-foundation 0.10.0", + "core-graphics 0.24.0", + "dbus", + "image", + "log", + "percent-encoding", + "sysinfo", + "thiserror", + "windows 0.58.0", + "xcb", +] + +[[package]] +name = "xcb" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e75181b5a62b6eeaa72f303d3cef7dbb841e22885bf6d3e66fe23e88c55dc6" +dependencies = [ + "bitflags 1.3.2", + "libc", + "quick-xml", +] + +[[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 = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 9a0749c..87ab1e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ nix = { version = "0.29.0", features = ["fs", "process", "signal", "term"] } ptyprocess = "0.4.1" vte = "0.13.0" image = "0.25.2" +xcap = "0.0.13" +enigo = "0.2.1" [toolchain] channel = "nightly" diff --git a/src/gui/mod.rs b/src/gui/mod.rs new file mode 100644 index 0000000..84d3939 --- /dev/null +++ b/src/gui/mod.rs @@ -0,0 +1,12 @@ +//! This module contains all the GUI related code. +//! +//! The concept of GUI can be seen as an interactive [`screen`]: +//! - Has a specific 2D size +//! - Can read the contents of the screen as an image(RgbImage, GrayImage should be converted to RgbImage) +//! - Can move the cursor to a specific position +//! - Can click the left, right, or middle mouse button +//! - Can scroll up or down (The scroll will always be biological direction) +//! - Can write text (e.g. typing in a text box) + +pub mod screen; +pub mod monitor; \ No newline at end of file diff --git a/src/gui/monitor.rs b/src/gui/monitor.rs new file mode 100644 index 0000000..687b1d5 --- /dev/null +++ b/src/gui/monitor.rs @@ -0,0 +1,93 @@ +//! Capture your computer's monitor + +use std::error::Error; + +use enigo::{Axis, Button, Coordinate, Direction, Enigo, Keyboard, Mouse, Settings}; +use image::RgbaImage; + +use crate::util::anybase::AnyBase; + +use super::screen::Screen; + +pub struct Monitor { + inner: xcap::Monitor, + input: Enigo, +} + +impl Monitor { + /// Build a new `Monitor` instance. + /// + /// The default monitor is the primary monitor. + /// Or you can specify the monitor by the `id`. + pub fn build(id: Option) -> Result> { + let enigo = Enigo::new(&Settings::default())?; + let monitors = xcap::Monitor::all()?; + for monitor in monitors { + if (id.is_some_and(|x| monitor.id() == x)) || monitor.is_primary() { + return Ok(Monitor { + inner: monitor, + input: enigo, + }); + } + } + Err("No primary monitor found".into()) + } +} + +impl AnyBase for Monitor { + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + fn into_any(self: Box) -> Box { + self + } +} + +impl Screen for Monitor { + fn size(&self) -> (u32, u32) { + (self.inner.width(), self.inner.height()) + } + fn read(&mut self) -> Result> { + let img = self.inner.capture_image()?; + Ok(img) + } + fn move_to(&mut self, x: u32, y: u32) -> Result<(), Box> { + self.input.move_mouse(x as i32, y as i32, Coordinate::Abs)?; + Ok(()) + } + fn click_left(&mut self) -> Result<(), Box> { + self.input.button(Button::Left, Direction::Click)?; + Ok(()) + } + fn click_right(&mut self) -> Result<(), Box> { + self.input.button(Button::Right, Direction::Click)?; + Ok(()) + } + fn click_middle(&mut self) -> Result<(), Box> { + self.input.button(Button::Middle, Direction::Click)?; + Ok(()) + } + fn scroll_up(&mut self, len: u32) -> Result<(), Box> { + self.input.scroll(len as i32, Axis::Vertical)?; + Ok(()) + } + fn scroll_down(&mut self, len: u32) -> Result<(), Box> { + self.input.scroll(-(len as i32), Axis::Vertical)?; + todo!() + } + fn write(&mut self, data: String) -> Result<(), Box> { + self.input.text(&data)?; + Ok(()) + } + fn hold(&mut self, key: u16) -> Result<(), Box> { + self.input.raw(key, Direction::Press)?; + Ok(()) + } + fn release(&mut self, key: u16) -> Result<(), Box> { + self.input.raw(key, Direction::Release)?; + Ok(()) + } +} diff --git a/src/gui/screen.rs b/src/gui/screen.rs new file mode 100644 index 0000000..79f8928 --- /dev/null +++ b/src/gui/screen.rs @@ -0,0 +1,84 @@ +//! This module contains the `Screen` trait and its related types. +//! +//! The `Screen` trait is used to interact with a Screen on the screen. +//! It can be defined as follows: +//! - Has a specific 2D size +//! - Can read the contents of the screen as an image(RgbaImage, GrayImage should be converted to RgbaImage) +//! - Can move the cursor to a specific position +//! - Can click the left, right, or middle mouse button +//! - Can scroll up or down (The scroll will always be biological direction) +//! - Can write text (e.g. typing in a text box) +//! +//! The `WrapperScreen` trait is used to wrap a `Screen` and provide additional functionality. +//! The `InnerScreen` trait is used to access the inner `Screen` of a `WrapperScreen`. + +use std::error::Error; + +use image::RgbaImage; + +use crate::util::anybase::AnyBase; + +/// A Screen trait, used to interact with a screen +pub trait Screen: AnyBase { + //! Get the size of the Screen + fn size(&self) -> (u32, u32); + + /// Read the contents of the Screen as an image + fn read(&mut self) -> Result>; + + /// Move the cursor to the specified position + /// Note: (0, 0) is always the top-left corner + fn move_to(&mut self, x: u32, y: u32) -> Result<(), Box>; + + /// Click the left mouse button + fn click_left(&mut self) -> Result<(), Box>; + + /// Click the right mouse button + fn click_right(&mut self) -> Result<(), Box>; + + /// Click the middle mouse button + fn click_middle(&mut self) -> Result<(), Box>; + + /// Scroll up + fn scroll_up(&mut self, len: u32) -> Result<(), Box>; + + /// Scroll down + fn scroll_down(&mut self, len: u32) -> Result<(), Box>; + + /// Write text + fn write(&mut self, data: String) -> Result<(), Box>; + + /// Hold a key, identified by keycode + fn hold(&mut self, key: u16) -> Result<(), Box>; + + /// Release a key, identified by keycode + fn release(&mut self, key: u16) -> Result<(), Box>; + + /// Snipper for moving the cursor to a specific position and clicking the left mouse button + fn click_left_at(&mut self, x: u32, y: u32) -> Result<(), Box> { + self.move_to(x, y)?; + self.click_left() + } + + /// Snipper for moving the cursor to a specific position and clicking the right mouse button + fn click_right_at(&mut self, x: u32, y: u32) -> Result<(), Box> { + self.move_to(x, y)?; + self.click_right() + } +} + +/// A dynamic Screen type +pub type DynScreen = Box; + +/// A trait for wrapping a `Screen` and providing additional functionality +pub trait WrapperScreen: Screen { + /// Exit the Screen and return the inner Screen + fn exit(self) -> DynScreen; +} + +/// A trait for accessing the inner `Screen` of a `WrapperScreen` +pub trait InnerScreen: WrapperScreen { + /// Get a reference to the inner Screen + fn inner_ref(&self) -> &DynScreen; + fn inner_mut(&mut self) -> &mut DynScreen; +}