keyflow 0.1.1

Cross-platform input simulation library for keyboard, mouse and hotkeys.
Documentation
use crate::hotkey::{Hotkey, HotkeyCallback, HotkeyId};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::time::Duration;
use strum_macros::EnumIter;

/// Key representation
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, EnumIter)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Key {
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,

    Digit1,
    Digit2,
    Digit3,
    Digit4,
    Digit5,
    Digit6,
    Digit7,
    Digit8,
    Digit9,
    Digit0,

    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,

    ShiftLeft,
    ShiftRight,
    ControlLeft,
    ControlRight,
    AltLeft,
    AltRight,

    /// Windows/Super/Command key
    MetaLeft,
    MetaRight,

    Escape,
    Space,
    Enter,
    Tab,
    Backspace,
    CapsLock,
    NumLock,
    ScrollLock,

    Backquote,
    Minus,
    Equal,
    BracketLeft,
    BracketRight,
    Backslash,
    Semicolon,
    Quote,
    Comma,
    Period,
    Slash,

    /// Extra key on ISO keyboards (between Left Shift and Z)
    IsoBackslash,

    Insert,
    Delete,
    Home,
    End,
    PageUp,
    PageDown,

    ArrowUp,
    ArrowDown,
    ArrowLeft,
    ArrowRight,

    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,
    NumpadAdd,
    NumpadSubtract,
    NumpadMultiply,
    NumpadDivide,
    NumpadDecimal,
    NumpadEnter,
    NumpadEqual,
    NumpadComma,

    PrintScreen,
    Pause,
    ContextMenu,
    Power,
    Sleep,
    Wake,

    VolumeMute,
    VolumeDown,
    VolumeUp,
    MediaPlayPause,
    MediaStop,
    MediaTrackNext,
    MediaTrackPrevious,

    BrowserBack,
    BrowserForward,
    BrowserRefresh,
    BrowserStop,
    BrowserSearch,
    BrowserFavorites,
    BrowserHome,

    LaunchMail,
    LaunchMediaPlayer,
    LaunchApp1,
    LaunchApp2,

    Help,
    Undo,
    Redo,
    Cut,
    Copy,
    Paste,
    Find,
}

/// Mouse button representation
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Button {
    Left,
    Right,
    Middle,
    Extra1,
    Extra2,
    Extra3,
    Extra4,
}

/// Input event action
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Action {
    Press,
    Release,
    Click,
}

/// Mouse movement type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Movement {
    Relative { dx: i32, dy: i32 },
    Absolute { x: i32, y: i32 },
}

/// Mouse scroll
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Scroll {
    Vertical(i32),
    Horizontal(i32),
}

/// Input event types
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum InputEvent {
    KeyEvent {
        key: Key,
        action: Action,
    },
    ButtonEvent {
        button: Button,
        action: Action,
    },
    MouseMove(Movement),
    MouseScroll(Scroll),
    #[cfg_attr(feature = "serde", serde(with = "serde_duration"))]
    Delay(Duration),
}

pub(crate) enum InternalMessage {
    SimulateEvent(InputEvent),
    BatchEvents(Vec<InputEvent>),
    RegisterHotkey {
        id: HotkeyId,
        combo: Hotkey,
        callback: HotkeyCallback,
    },
    UnregisterHotkey {
        id: HotkeyId,
    },
}

/// Define the Screen limits
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Screen {
    /// The most left edge of all screens
    ///
    /// * 1 screen with 1920x1080 => 0
    /// * 2 screens with 1920x1080, primary screen left => 0
    /// * 2 screen with 1920x1080, primary screen right => -1920
    /// * 2 screen with 1920x1080, primary screen top or bottom => 0
    pub left: i32,
    /// The most top edge of all screens
    ///
    /// See [`Self::left`]
    pub top: i32,
    /// The total width of all screens
    /// * 1 screen with 1920x1080 => 1920
    /// * 2 screens with 1920x1080 side by side => 3840
    /// * 2 screens with 1920x1080 on top of each other => 1920
    pub width: i32,
    /// The total height of all screens
    /// * 1 screen with 1920x1080 => 1080
    /// * 2 screens with 1920x1080 side by side => 1080
    /// * 2 screens with 1920x1080 on top of each other => 2160
    pub height: i32,
}

impl Screen {
    pub fn new(left: i32, top: i32, width: i32, height: i32) -> Self {
        Self {
            left,
            top,
            width,
            height,
        }
    }
}

impl Default for Screen {
    /// Returns a default screen with 1920 x 1080.
    fn default() -> Self {
        Self::new(0, 0, 1920, 1080)
    }
}

#[derive(PartialEq, Debug)]
pub(crate) enum KeyState {
    Released,
    Pressed,
    Repeated,
}

impl From<u16> for KeyState {
    fn from(state: u16) -> Self {
        match state {
            0 => KeyState::Released,
            1 => KeyState::Pressed,
            2 => KeyState::Repeated,
            _ => unreachable!(),
        }
    }
}

impl From<i32> for KeyState {
    fn from(state: i32) -> Self {
        match state {
            0 => KeyState::Released,
            1 => KeyState::Pressed,
            2 => KeyState::Repeated,
            _ => unreachable!(),
        }
    }
}

#[cfg(feature = "serde")]
mod serde_duration {
    use serde::{Deserialize, Deserializer, Serializer};
    use std::time::Duration;

    pub fn serialize<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_u64(duration.as_millis() as u64)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
    where
        D: Deserializer<'de>,
    {
        let millis = u64::deserialize(deserializer)?;
        Ok(Duration::from_millis(millis))
    }
}