Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation and refactor (at least for macOS) #24

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
//! ```
//!
//! ## Linux disclaimer
//! If you're running into problems building on linux you need to install libxdo-dev.
//! If you're running into problems building on Linux, try installing libxdo-dev.
//!
//! #### Ubuntu
//! ### Ubuntu
//! ```bash
//! sudo apt-get install libxdo-dev
//! ```
Expand Down
99 changes: 73 additions & 26 deletions src/mouse.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
use crate::sys;
use crate::types::keys::Keys;
use crate::{sys, types::Point};
use crate::types::Point;

/// Struct for the mouse
/// Represents the user's mouse pointer.
///
/// This struct represents a mouse and doesn't hold any values
/// This is an abstraction of the internal [`sys::Mouse`] struct.
pub struct Mouse(sys::Mouse);

#[allow(unreachable_code, unused_variables)]
impl Mouse {
/// This method creates a new mouse instance, must always be run before anything else
pub fn new() -> Mouse {
Mouse(sys::Mouse::new())
#[must_use]
/// Create a new [`Mouse`].
pub const fn new() -> Self {
Self(sys::Mouse::new())
}

/// This method moves the mouse around
/// Move the mouse to the position `(x, y)`, where the origin `(0, 0)` is the top-left of the screen.
///
/// # Examples
///
/// ```no_run
/// ```rust, no_run
/// use mouse_rs::{types::keys::*, Mouse};
///
/// fn move_mouse() {
Expand All @@ -26,17 +27,21 @@ impl Mouse {
/// }
///
/// ```
///
/// # Errors
///
/// Fails if the mouse cannot move to the position.
pub fn move_to(&self, x: i32, y: i32) -> Result<(), Box<dyn std::error::Error>> {
self.0.move_to(x, y)
sys::Mouse::move_to(x, y)
}

/// This method presses the given button in
/// Press a button on the mouse and hold it.
///
/// *NOTE: This doesn't release the button so it will keep pressing*
/// _NOTE: This doesn't release the button so it will be kept held until [`Self::release`] is called._
///
/// # Examples
///
/// ```no_run
/// ```rust, no_run
/// use mouse_rs::{types::keys::Keys, Mouse};
///
/// fn press_button() {
Expand All @@ -50,16 +55,24 @@ impl Mouse {
/// mouse.release(&Keys::RIGHT).expect("Unable to release button"); // This will press the right mouse quickly
/// }
/// ```
///
/// # Errors
///
/// Fails if the button cannot be pressed.
pub fn press<'a>(&self, button: &'a Keys) -> Result<(), Box<dyn std::error::Error + 'a>> {
self.0.press(button)
sys::Mouse::press(button)
}

/// This will release the button as noted above
/// Release a button. Normally used after [`Self::press`].
///
/// # Errors
///
/// Fails if the button cannot be released.
pub fn release<'a>(&self, button: &'a Keys) -> Result<(), Box<dyn std::error::Error + 'a>> {
self.0.release(button)
sys::Mouse::release(button)
}

/// This gets the current mouse position
/// Return the current mouse position.
///
/// # Example
///
Expand All @@ -72,13 +85,17 @@ impl Mouse {
/// println!("X = {}, Y = {}", pos.x, pos.y)
/// }
/// ```
///
/// # Errors
///
/// Fails if the mouse position cannot be retrieved.
pub fn get_position(&self) -> Result<Point, Box<dyn std::error::Error>> {
self.0.get_position()
sys::Mouse::get_position()
}

/// This will scroll the mouse,
/// Scroll the mouse.
///
/// For scrolling down use negative values, for scrolling up use positive values
/// For scrolling down use negative values, for scrolling up use positive values.
///
/// # Examples
///
Expand All @@ -95,18 +112,48 @@ impl Mouse {
/// mouse.wheel(-1);
/// }
/// ```
///
/// # Errors
///
/// Fails if the mouse cannot be scrolled.
pub fn wheel(&self, delta: i32) -> Result<(), Box<dyn std::error::Error>> {
self.0.wheel(delta)
sys::Mouse::wheel(delta)
}

/// This is the exact same as wheel
/// Alias for [`Self::wheel`].
///
/// # Errors
///
/// See [`Self::wheel`].
pub fn scroll(&self, delta: i32) -> Result<(), Box<dyn std::error::Error>> {
self.0.wheel(delta)
sys::Mouse::wheel(delta)
}

// Does the exact same thing as press and release combined, but into one function
/// Click a mouse button by pressing then releasing.
///
/// # Errors
///
/// Fails if [`Self::press`] or [`Self::release`] fails.
pub fn click<'a>(&self, button: &'a Keys) -> Result<(), Box<dyn std::error::Error + 'a>> {
self.0.press(button).unwrap_or(());
self.0.release(button)
sys::Mouse::press(button)?;
sys::Mouse::release(button)?;
Ok(())
}

/// Move the mouse, relative to the current position.
///
/// # Errors
///
/// Fails if [`Self::get_position`] or [`Self::move_to`] fails.
pub fn move_by(&self, delta_x: i32, delta_y: i32) -> Result<(), Box<dyn std::error::Error>> {
let position = self.get_position()?;
self.move_to(position.x + delta_x, position.y + delta_y)?;
Ok(())
}
}

impl Default for Mouse {
fn default() -> Self {
Self::new()
}
}
44 changes: 23 additions & 21 deletions src/sys/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,24 @@ use core_graphics::{
geometry::CGPoint,
};

use crate::types::{keys::*, Point};
use crate::types::{keys::Keys, Point};

impl From<CGPoint> for Point {
fn from(other: CGPoint) -> Point {
Point {
#[allow(clippy::cast_possible_truncation)]
fn from(other: CGPoint) -> Self {
Self {
x: other.x as _,
y: other.y as _,
}
}
}

impl Into<CGPoint> for Point {
fn into(self) -> CGPoint {
CGPoint::new(self.x as _, self.y as _)
impl From<Point> for CGPoint {
fn from(value: Point) -> Self {
Self {
x: value.x.into(),
y: value.y.into(),
}
}
}

Expand All @@ -39,7 +43,7 @@ impl<'a> fmt::Display for Error<'a> {
Error::CGEventNotCreated => write!(f, "CGEvent could not be created"),
Error::CGEventSourceStateInvalid => write!(f, "invalid CGEventSourceStateID"),

Error::InvalidButtonStr(button) => write!(f, "invalid button str: {:?}", button),
Error::InvalidButtonStr(button) => write!(f, "invalid button str: {button:?}"),
}
}
}
Expand All @@ -48,18 +52,16 @@ pub struct Mouse;

impl Mouse {
fn event_source<'a>() -> Result<CGEventSource, Error<'a>> {
Ok(
CGEventSource::new(CGEventSourceStateID::CombinedSessionState)
.or(Err(Error::CGEventSourceStateInvalid))?,
)
CGEventSource::new(CGEventSourceStateID::CombinedSessionState)
.or(Err(Error::CGEventSourceStateInvalid))
}

pub fn new() -> Mouse {
Mouse
pub const fn new() -> Self {
Self
}

pub fn move_to(&self, x: i32, y: i32) -> Result<(), Box<dyn error::Error>> {
let point = CGPoint::new(x as _, y as _);
pub fn move_to(x: i32, y: i32) -> Result<(), Box<dyn error::Error>> {
let point = CGPoint::new(x.into(), y.into());

CGEvent::new_mouse_event(
Self::event_source()?,
Expand All @@ -73,7 +75,7 @@ impl Mouse {
Ok(())
}

pub fn press<'a>(&self, button: &'a Keys) -> Result<(), Box<dyn error::Error + 'a>> {
pub fn press<'a>(button: &'a Keys) -> Result<(), Box<dyn error::Error + 'a>> {
let (event_type, mouse_button) = match button {
Keys::LEFT => Ok((CGEventType::LeftMouseDown, CGMouseButton::Left)),
Keys::MIDDLE => Ok((CGEventType::OtherMouseDown, CGMouseButton::Center)),
Expand All @@ -84,7 +86,7 @@ impl Mouse {
CGEvent::new_mouse_event(
Self::event_source()?,
event_type,
self.get_position()?.into(),
Self::get_position()?.into(),
mouse_button,
)
.or(Err(Error::CGEventNotCreated))?
Expand All @@ -93,7 +95,7 @@ impl Mouse {
Ok(())
}

pub fn release<'a>(&self, button: &'a Keys) -> Result<(), Box<dyn error::Error + 'a>> {
pub fn release<'a>(button: &'a Keys) -> Result<(), Box<dyn error::Error + 'a>> {
let (event_type, mouse_button) = match button {
Keys::LEFT => Ok((CGEventType::LeftMouseUp, CGMouseButton::Left)),
Keys::WHEEL | Keys::MIDDLE => Ok((CGEventType::OtherMouseUp, CGMouseButton::Center)),
Expand All @@ -104,7 +106,7 @@ impl Mouse {
CGEvent::new_mouse_event(
Self::event_source()?,
event_type,
self.get_position()?.into(),
Self::get_position()?.into(),
mouse_button,
)
.or(Err(Error::CGEventNotCreated))?
Expand All @@ -113,14 +115,14 @@ impl Mouse {
Ok(())
}

pub fn get_position(&self) -> Result<Point, Box<dyn error::Error>> {
pub fn get_position() -> Result<Point, Box<dyn error::Error>> {
Ok(CGEvent::new(Self::event_source()?)
.or(Err(Error::CGEventNotCreated))?
.location()
.into())
}

pub fn wheel(&self, delta: i32) -> Result<(), Box<dyn error::Error>> {
pub fn wheel(delta: i32) -> Result<(), Box<dyn error::Error>> {
CGEvent::new_scroll_event(Self::event_source()?, ScrollEventUnit::LINE, 1, delta, 0, 0)
.or(Err(Error::CGEventNotCreated))?
.post(CGEventTapLocation::HID);
Expand Down
14 changes: 6 additions & 8 deletions tests/main.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
#[cfg(test)]
#[allow(unused_must_use)]
mod mouse {
use mouse_rs::{types::keys::Keys, Mouse};

#[test]
fn move_and_press() {
let mouse = Mouse::new();
mouse.move_to(500, 500);
let pos = mouse.get_position().unwrap();
mouse.move_to(500, 500).expect("Unable to move mouse");
mouse.press(&Keys::RIGHT).expect("Unable to press button");
mouse.release(&Keys::RIGHT).expect("Something went wrong");
mouse.click(&Keys::WHEEL).expect("Something went wrong");
mouse.release(&Keys::RIGHT).expect("Unable to release button");
mouse.click(&Keys::WHEEL).expect("Unable to click button");
}

#[test]
fn scroll_wheel() {
let mouse = Mouse::new();
mouse.wheel(1);
mouse.wheel(1).expect("Unable to scroll mouse");
}

#[test]
fn press_button() {
let mouse = Mouse::new();
mouse.press(&Keys::MIDDLE);
mouse.release(&Keys::MIDDLE);
mouse.press(&Keys::MIDDLE).expect("Unable to press button");
mouse.release(&Keys::MIDDLE).expect("Unable to release button");
}

// #[test]
Expand Down