theobserver 0.2.0

A library to read pen pressure using evdev
Documentation
use std::{
    sync::mpsc::{channel, Receiver},
    thread,
};

use error_utils_derive::Errors;
use evdev::{AbsoluteAxisType, Device, InputEvent, InputEventKind};
use log::info;

#[derive(Debug, Errors)]
pub enum PenError {
    #[error("No compatible device was found")]
    NoDeviceFound,
    #[error("Device listener thread lost connection to main thread")]
    SenderLostConnection,
}

pub struct PenDevice {
    receiver: Receiver<InputEvent>,
    x_tilt: i32,
    y_tilt: i32,
    pressure: i32,
    pub max_pressure: i32,
}

pub struct DeviceQuery {
    with_position: bool,
    with_tilt: bool,
    with_pressure: bool,
}

impl DeviceQuery {
    pub fn new() -> DeviceQuery {
        DeviceQuery {
            with_position: false,
            with_tilt: false,
            with_pressure: false,
        }
    }
    pub fn with_position(mut self) -> Self {
        self.with_position = true;
        self
    }
    pub fn with_tilt(mut self) -> Self {
        self.with_tilt = true;
        self
    }
    pub fn with_pressure(mut self) -> Self {
        self.with_pressure = true;
        self
    }
    pub fn query(self) -> Vec<Device> {
        info!("Searching for inputs with pen pressure and tilt");
        let mut inputs = vec![];
        for (path, item) in evdev::enumerate() {
            if let Some(axes) = item.supported_absolute_axes() {
                if !self.with_pressure || axes.contains(AbsoluteAxisType::ABS_PRESSURE) {
                    if !self.with_position
                        || (axes.contains(AbsoluteAxisType::ABS_X)
                            && axes.contains(AbsoluteAxisType::ABS_Y))
                    {
                        if !self.with_tilt
                            || (axes.contains(AbsoluteAxisType::ABS_TILT_X)
                                && axes.contains(AbsoluteAxisType::ABS_TILT_Y))
                        {
                            info!("Found input device at {:?}", path);
                            inputs.push(item);
                        }
                    }
                }
            }
        }

        inputs
    }
    pub fn first(self) -> Result<Device, PenError> {
        let mut devices = self.query();
        if devices.len() == 0 {
            Err(PenError::NoDeviceFound)
        } else {
            Ok(devices.remove(0))
        }
    }
}

pub fn watch_device(mut backend: Device) -> PenDevice {
    let (send, recv) = channel();

    thread::spawn(move || -> Result<(), PenError> {
        while let Ok(events) = backend.fetch_events() {
            for event in events {
                send.send(event)
                    .map_err(|_| PenError::SenderLostConnection)?;
            }
        }
        Ok(())
    });

    PenDevice {
        receiver: recv,
        x_tilt: 0,
        y_tilt: 0,
        pressure: 0,
        max_pressure: 65535,
    }
}

impl PenDevice {
    pub fn process_events(&mut self) {
        while let Ok(event) = self.receiver.try_recv() {
            match event.kind() {
                InputEventKind::AbsAxis(AbsoluteAxisType::ABS_TILT_X) => {
                    self.x_tilt = event.value();
                }
                InputEventKind::AbsAxis(AbsoluteAxisType::ABS_TILT_Y) => {
                    self.y_tilt = event.value();
                }
                InputEventKind::AbsAxis(AbsoluteAxisType::ABS_PRESSURE) => {
                    self.pressure = event.value();
                }
                _ => {}
            }
        }
    }
    pub fn x_tilt(&self) -> i32 {
        self.x_tilt
    }
    pub fn y_tilt(&self) -> i32 {
        self.y_tilt
    }
    pub fn pressure(&self) -> f32 {
        self.pressure as f32 / self.max_pressure as f32
    }
}