zng-app 0.22.5

Part of the zng project.
Documentation
//! Events directly from view-process not targeting any windows.
//!
//! These events get emitted only if the app [`APP.device_events_filter`] allows. When enabled they
//! can be used like [`raw_events`].
//!
//! [`APP.device_events_filter`]: crate::APP::device_events_filter
//! [`raw_events`]: crate::view_process::raw_events

use std::{collections::HashMap, fmt};

use crate::event::*;

use zng_layout::unit::euclid;
use zng_var::{Var, var};
use zng_view_api::{
    keyboard::{KeyCode, KeyState},
    mouse::{ButtonId, ButtonState, MouseScrollDelta},
};

pub use zng_view_api::AxisId;
pub use zng_view_api::raw_input::{InputDeviceCapability, InputDeviceInfo};

use once_cell::sync::Lazy;

zng_unique_id::unique_id_64! {
    /// Unique identifier of an input device event source.
    pub struct InputDeviceId;
}
zng_unique_id::impl_unique_id_bytemuck!(InputDeviceId);
impl InputDeviceId {
    /// Virtual keyboard ID used in keyboard events generated by code.
    pub fn virtual_keyboard() -> InputDeviceId {
        static ID: Lazy<InputDeviceId> = Lazy::new(InputDeviceId::new_unique);
        *ID
    }

    /// Virtual mouse ID used in mouse events generated by code.
    pub fn virtual_mouse() -> InputDeviceId {
        static ID: Lazy<InputDeviceId> = Lazy::new(InputDeviceId::new_unique);
        *ID
    }

    /// Virtual generic device ID used in device events generated by code.
    pub fn virtual_generic() -> InputDeviceId {
        static ID: Lazy<InputDeviceId> = Lazy::new(InputDeviceId::new_unique);
        *ID
    }
}
impl fmt::Debug for InputDeviceId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_struct("InputDeviceId")
                .field("id", &self.get())
                .field("sequential", &self.sequential())
                .finish()
        } else {
            write!(f, "InputDeviceId({})", self.sequential())
        }
    }
}
impl fmt::Display for InputDeviceId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "InputDeviceId({})", self.get())
    }
}

event_args! {
    /// Arguments for [`INPUT_DEVICES_CHANGED_EVENT`].
    pub struct InputDevicesChangedArgs {
        /// New list of available devices.
        pub devices: HashMap<InputDeviceId, InputDeviceInfo>,

        ..

        /// Broadcast to all widgets.
        fn is_in_target(&self, id: WidgetId) -> bool {
            true
        }
    }

    /// Arguments for [`POINTER_MOTION_EVENT`].
    pub struct PointerMotionArgs {
        /// Device that generated the event.
        pub device_id: InputDeviceId,

        /// Motion (x, y) delta.
        pub delta: euclid::Vector2D<f64, ()>,

        ..

        /// Broadcast to all widgets.
        fn is_in_target(&self, id: WidgetId) -> bool {
            true
        }
    }

    /// Arguments for [`SCROLL_MOTION_EVENT`].
    pub struct ScrollMotionArgs {
        /// Device that generated the event.
        pub device_id: InputDeviceId,

        /// Wheel motion delta, value is in pixels if the *wheel* is a touchpad.
        pub delta: MouseScrollDelta,

        ..

        /// Broadcast to all widgets.
        fn is_in_target(&self, id: WidgetId) -> bool {
            true
        }
    }

    /// Arguments for [`AXIS_MOTION_EVENT`].
    pub struct AxisMotionArgs {
        /// Device that generated the event.
        pub device_id: InputDeviceId,

        /// Analog axis.
        pub axis: AxisId,

        /// Motion amount.
        pub value: f64,

        ..

        /// Broadcast to all widgets.
        fn is_in_target(&self, id: WidgetId) -> bool {
            true
        }
    }

    /// Arguments for the [`BUTTON_EVENT`].
    pub struct ButtonArgs {
        /// Device that generated the event.
        pub device_id: InputDeviceId,

        /// Button raw id.
        pub button: ButtonId,

        /// If the button was pressed or released.
        pub state: ButtonState,

        ..

        /// Broadcast to all widgets.
        fn is_in_target(&self, id: WidgetId) -> bool {
            true
        }
    }

    /// Arguments for the [`KEY_EVENT`].
    pub struct KeyArgs {
        /// Keyboard device that generated the event.
        pub device_id: InputDeviceId,

        /// Physical key.
        pub key_code: KeyCode,

        /// If the key was pressed or released.
        pub state: KeyState,

        ..

        /// Broadcast to all widgets.
        fn is_in_target(&self, id: WidgetId) -> bool {
            true
        }
    }
}

event! {
    /// Raw input devices have been added or removed from the system.
    pub static INPUT_DEVICES_CHANGED_EVENT: InputDevicesChangedArgs;

    /// Device unfiltered 2D move delta.
    pub static POINTER_MOTION_EVENT: PointerMotionArgs;

    /// Mouse device unfiltered wheel motion delta.
    pub static SCROLL_MOTION_EVENT: ScrollMotionArgs;

    /// Motion on some analog axis.
    ///
    /// This event will be reported for all arbitrary input devices that the view-process supports on this platform,
    /// including mouse devices. If the device is a mouse device then this will be reported alongside the [`POINTER_MOTION_EVENT`].
    pub static AXIS_MOTION_EVENT: AxisMotionArgs;

    /// Button press/release from a device, probably a mouse.
    pub static BUTTON_EVENT: ButtonArgs;

    /// Keyboard device key press.
    pub static KEY_EVENT: KeyArgs;
}

/// Input devices info service.
///
/// Note that this service data will depend on what [`APP.device_events_filter`] is set.
///
/// [`APP.device_events_filter`]: crate::APP::device_events_filter
#[allow(non_camel_case_types)]
pub struct INPUT_DEVICES;

impl INPUT_DEVICES {
    /// Read-only variable that tracks the current input devices.
    pub fn available_devices(&self) -> Var<HashMap<InputDeviceId, InputDeviceInfo>> {
        INPUT_DEVICES_SV.read().devices.read_only()
    }

    /// Read-only variable that tracks the input device info.
    ///
    /// If the `id` is unknown returns an "Unknown Input Device" info.
    pub fn device(&self, id: InputDeviceId) -> Var<InputDeviceInfo> {
        INPUT_DEVICES_SV.read().devices.map(move |m| {
            m.get(&id)
                .cloned()
                .unwrap_or_else(|| InputDeviceInfo::new("Unknown Input Device", InputDeviceCapability::empty()))
        })
    }

    pub(crate) fn update(&self, devices: HashMap<InputDeviceId, InputDeviceInfo>) {
        INPUT_DEVICES_SV.read().devices.set(devices);
    }
}

struct InputDevicesSv {
    devices: Var<HashMap<InputDeviceId, InputDeviceInfo>>,
}
app_local! {
    static INPUT_DEVICES_SV: InputDevicesSv = InputDevicesSv {
        devices: var(HashMap::new()),
    };
}