device_query

repository·master·Indexed 42 days ago

https://github.com/ostrosco/device_query

A Rust library for querying keyboard and mouse input state on-demand or via event callbacks without requiring an active window. Version 4.0.1 supports Windows, Linux (X11), and macOS. It provides the DeviceState struct for polling current coordinates and pressed keys, as well as the DeviceEventsHandler for registering asynchronous callbacks for key and mouse events using a guard pattern.

Tokens
5.2K
Snippets
19
Records
20
Agent score
50%

What's inside device_query

  1. Register device event callbacks

    master

    Instead of polling state, you can use DeviceEventsHandler to register callbacks that trigger on specific device events.

    • Initialize the handler using DeviceEventsHandler::new(Duration) specifying the polling interval.
    • Use methods like on_mouse_move to register listeners.
    • The callbacks are managed via a 'guard' pattern: the callbacks are automatically deregistered when the returned guard object goes out of scope.
    extern crate device_query;
    use device_query::{DeviceEvents, DeviceEventsHandler, Keycode, MouseButton, MousePosition};
    use std::thread;
    use std::time::Duration;
    
    fn main() {
        // Initialize the event handler with a sleep duration of 10 milliseconds
        let event_handler = DeviceEventsHandler::new(Duration::from_millis(10))
            .expect("Could not initialize event loop");
    
        // Register callbacks for various events
        // The callbacks will be automatically deregistered when they go out of scope
        let _mouse_move_guard = event_handler.on_mouse_move(|position: &MousePosition| {
            println!("Mouse moved to position: {:?}", position);
        });
    
        // Keep the main thread alive to continue receiving events
        loop {
            thread::sleep(Duration::from_secs(1000));
        }
    }
  2. Install system dependencies for Linux

    master

    While Windows requires no special software, Linux users must have X11 development libraries installed to allow device_query to communicate with the OS.

    On Ubuntu/Debian:

    sudo apt install libx11-dev

    On Fedora/RHEL/CentOS:

    sudo dnf install xorg-x11-server-devel
  3. Query mouse and keyboard state on demand

    master

    Use the DeviceState struct to poll the current state of input devices without requiring a window. You can retrieve the current mouse coordinates via get_mouse() and a list of currently pressed keys via get_keys() using Keycode enums.

    use device_query::{DeviceQuery, DeviceState, MouseState, Keycode};
    
    let device_state = DeviceState::new();
    let mouse: MouseState = device_state.get_mouse();
    println!("Current Mouse Coordinates: {:?}", mouse.coords);
    let keys: Vec<Keycode> = device_state.get_keys();
    println!("Is A pressed? {}", keys.contains(Keycode::A));
  4. Fix macOS permission issues for device querying

    master

    On newer versions of macOS, you may only see meta keys (e.g., Shift, Backspace) due to privacy restrictions. To allow full access to keyboard and mouse input, you must grant Accessibility permissions to the application running the code (e.g., your terminal):

    1. Open System Preferences.
    2. Navigate to Security -> Privacy.
    3. Select Accessibility.
    4. Unlock the settings and add your application (or terminal) to the list.
  5. Listen for device events with DeviceEventsHandler

    master

    To react to input changes as they happen rather than polling, use DeviceEventsHandler. Initialize it with DeviceEventsHandler::new(duration), where duration is the polling interval (e.g., Duration::from_millis(10)).

    To register callbacks, use methods like .on_mouse_move(|position| { ... }).

    Important: The callback registration methods return a guard object. You must keep this guard in scope (e.g., by assigning it to a variable like _guard) to ensure the callback remains active. If the guard is dropped, the event listener for that specific event type will be unregistered.

    use device_query::{DeviceEvents, DeviceEventsHandler};
    use std::time::Duration;
    
    let device_state = DeviceEventsHandler::new(Duration::from_millis(10))
        .expect("Failed to start event loop");
    
    // Register a mouse move event callback.
    // The returned guard must be kept alive to keep the callback active.
    let _guard = device_state.on_mouse_move(|position| {
        println!("Mouse position: {:#?}", position);
    });
    
    // Keep the main thread alive to continue receiving events
    loop {}
  6. Initialize the device event loop

    master

    The DeviceEventsHandler::new(sleep_dur: Duration) method attempts to start the background event loop.

    • sleep_dur: A std::time::Duration representing how long the event loop should sleep between polls.
    • Return Value: Returns Some(DeviceEventsHandler) if successful, or None if the event loop is already running.
    use device_query::DeviceEventsHandler;
    use std::time::Duration;
    
    let device_events = DeviceEventsHandler::new(Duration::from_millis(10)).expect("Failed to initialize event loop");
  7. Register mouse event callbacks with MouseCallbacks

    master

    The MouseCallbacks struct allows you to register asynchronous, thread-safe callbacks for mouse movements and button actions (down and up).

    To register a callback, wrap your closure in an Arc and use the corresponding push_* method. The system uses Weak references internally, meaning callbacks will automatically be cleaned up when the original Arc is dropped, preventing memory leaks.

    Available registration methods:

    • push_mouse_move(callback: Arc<MouseMoveCallback>): Registers a handler for mouse position changes. The callback receives a reference to &MousePosition.
    • push_mouse_down(callback: Arc<MouseButtonCallback>): Registers a handler for mouse button presses. The callback receives a reference to &MouseButton.
    • push_mouse_up(callback: Arc<MouseButtonCallback>): Registers a handler for mouse button releases. The callback receives a reference to &MouseButton.
    use std::sync::Arc;
    use device_query::device_events::callback::mouse_callback::{MouseCallbacks, MouseMoveCallback};
    
    let callbacks = MouseCallbacks::default();
    
    // Create a move callback
    let move_handler: Arc<MouseMoveCallback> = Arc::new(|pos| {
        println!("Mouse moved to: {:?}", pos);
    });
    
    // Register it
    callbacks.push_mouse_move(move_handler);
  8. Register keyboard event handlers with KeyboardCallbacks

    master

    The KeyboardCallbacks struct allows you to register asynchronous, thread-safe callbacks that trigger on keyboard events. You can register handlers for both key press (push_key_down) and key release (push_key_up) events.

    Callbacks must be wrapped in an Arc to ensure they can be safely shared and downgraded to Weak references internally, allowing the system to automatically clean up callbacks when they are no longer in use.

    use std::sync::Arc;
    // Assuming KeyboardCallbacks and Keycode are available in the crate scope
    
    let callbacks = KeyboardCallbacks::default();
    
    // Create a callback for key down events
    let on_key_down = Arc::new(|key: &Keycode| {
        println!("Key pressed: {:?}", key);
    });
    
    // Register the callback
    callbacks.push_key_down(on_key_down);
  9. Register device event callbacks with EventLoop

    master

    The EventLoop allows you to register asynchronous callbacks that trigger when specific hardware events occur. You can listen for keyboard presses/releases and mouse movements/button clicks.

    Each registration method returns a CallbackGuard. You must keep this guard alive for as long as you want the callback to remain active; when the guard is dropped, the callback is automatically unregistered.

    // Example of registering callbacks
    let mut event_loop = EventLoop::new(Duration::from_millis(10));
    
    // Register key down event
    let _key_guard = event_loop.on_key_down(|key| {
        println!("Key pressed: {:?}", key);
    });
    
    // Register mouse move event
    let _mouse_guard = event_loop.on_mouse_move(|pos| {
        println!("Mouse moved to: {:?}", pos);
    });
  10. Use EventLoop methods to handle device events

    master

    The following methods are available on the EventLoop struct to subscribe to device events. All callbacks must implement Fn(...) + Send + Sync + 'static.

    // Keyboard events
    event_loop.on_key_down(|&keycode| { /* ... */ });
    event_loop.on_key_up(|&keycode| { /* ... */ });
    
    // Mouse events
    event_loop.on_mouse_down(|&button| { /* ... */ });
    event_loop.on_mouse_up(|&button| { /* ... */ });
    event_loop.on_mouse_move(|&position| { /* ... */ });
  11. Register device event callbacks with DeviceEventsHandler

    master

    Use DeviceEventsHandler to register asynchronous callbacks for keyboard and mouse events.

    To use this feature:

    1. Initialize the handler using DeviceEventsHandler::new(Duration).
    2. Call the specific event registration method (e.g., on_key_down, on_mouse_move).
    3. Crucial: Store the returned CallbackGuard in a variable. The callback remains active only as long as the guard exists. If the guard is dropped, the callback is unregistered.

    Supported events:

    • on_key_down: Triggered when a key is pressed. Receives &Keycode.
    • on_key_up: Triggered when a key is released. Receives &Keycode.
    • on_mouse_move: Triggered on mouse movement. Receives &MousePosition.
    • on_mouse_down: Triggered when a mouse button is pressed. Receives &MouseButton.
    • on_mouse_up: Triggered when a mouse button is released. Receives &MouseButton.
    use device_query::{DeviceEvents, DeviceEventsHandler, Keycode, MouseButton};
    use std::time::Duration;
    
    fn main() {
      // Initialize the handler with a sleep duration for the event loop
      let device_events = DeviceEventsHandler::new(Duration::from_millis(10)).unwrap();
    
      // Register a key down event callback.
      // The returned _guard must be kept in scope to keep the callback alive.
      let _guard = device_events.on_key_down(|key| {
        println!("Key down: {:?}", key);
      });
    
      // Keep the main thread alive to continue receiving events
      loop {}
    }
  12. Initialize the global event loop

    master

    The init_event_loop function initializes a global, lazily-initialized EVENT_LOOP. This is useful for applications that want a single, shared event processing system.

    • Returns true if the loop was successfully initialized.
    • Returns false if the loop was already initialized or if the mutex was poisoned.
    • sleep_dur: The polling interval for the background threads. A smaller duration increases responsiveness but consumes more CPU.
    use std::time::Duration;
    
    // Initialize the global loop with a 100 microsecond polling interval
    if init_event_loop(Duration::from_micros(100)) {
        println!("Event loop initialized successfully");
    } else {
        println!("Event loop already running or failed to initialize");
    }