use bevy::prelude::*;
use crossbeam_channel::{unbounded, Receiver, Sender};
use mouce::{common::MouseEvent, Mouse};
use std::thread;
pub(crate) struct MousePosProvider;
pub type GlobalMouseButton = mouce::common::MouseButton;
pub type GlobalScrollDirection = mouce::common::ScrollDirection;
#[derive(Debug, Deref, Event)]
pub struct GlobalMouseEvents(mouce::common::MouseEvent);
#[derive(Debug, Deref, Event)]
pub struct GlobalScrollEvents(GlobalScrollDirection);
#[derive(Debug, Deref, Event)]
pub struct GlobalMouseButtonEvents(GlobalMouseButton);
impl Plugin for MousePosProvider {
fn build(&self, app: &mut App) {
app.init_resource::<GlobalMousePos>()
.add_event::<MouseControl>()
.add_event::<GlobalMouseEvents>()
.add_event::<GlobalScrollEvents>()
.add_event::<GlobalMouseButtonEvents>()
.add_systems(Startup, setup_hook)
.add_systems(Startup, setup_mover)
.add_systems(Update, store_last_pos.after(read_stream))
.add_systems(Update, split_events.after(read_stream))
.add_systems(Update, mover_events)
.add_systems(Update, read_stream);
}
}
#[derive(Resource, Default, Debug)]
pub struct GlobalMousePos {
pub x: i32,
pub y: i32,
}
#[derive(Resource, Deref)]
struct StreamReceiver(Receiver<MouseEvent>);
fn setup_hook(mut commands: Commands) {
let (tx, rx) = unbounded::<MouseEvent>();
thread::spawn(move || {
let mut manager = Mouse::new();
let hook_result = manager.hook(Box::new(move |e| tx.send(*e).unwrap_or(())));
match hook_result {
Ok(_) => {}
Err(err) => if mouce::error::Error::PermissionDenied == err {},
}
loop {
thread::sleep(std::time::Duration::from_millis(100));
}
});
commands.insert_resource(StreamReceiver(rx));
}
fn read_stream(receiver: Res<StreamReceiver>, mut events: EventWriter<GlobalMouseEvents>) {
for from_stream in receiver.try_iter() {
events.send(GlobalMouseEvents(from_stream));
}
}
fn split_events(
mut events: EventReader<GlobalMouseEvents>,
mut scrolls: EventWriter<GlobalScrollEvents>,
mut clicks: EventWriter<GlobalMouseButtonEvents>,
) {
for event in events.iter() {
match event {
GlobalMouseEvents(e) => match e {
MouseEvent::Scroll(scroll) => scrolls.send(GlobalScrollEvents(*scroll)),
MouseEvent::Press(click) => clicks.send(GlobalMouseButtonEvents(*click)),
_ => {}
},
}
}
}
fn store_last_pos(mut events: EventReader<GlobalMouseEvents>, mut mouse: ResMut<GlobalMousePos>) {
let abs_move = events
.iter()
.filter_map(|e| match e.0 {
MouseEvent::AbsoluteMove(x, y) => Some((x, y)),
_ => None,
})
.last();
match abs_move {
Some(coords) => {
mouse.x = coords.0;
mouse.y = coords.1;
}
None => {
let manager = Mouse::new();
match manager.get_position() {
Ok(pos) => {
mouse.x = pos.0;
mouse.y = pos.1;
}
Err(_) => {
mouse.x = 0;
mouse.y = 0;
}
}
}
}
}
#[derive(Copy, Clone, Debug, Event)]
pub enum MouseControl {
MoveTo(usize, usize),
MoveRelative(i32, i32),
PressButton(GlobalMouseButton),
ReleaseButton(GlobalMouseButton),
ClickButton(GlobalMouseButton),
ScrollWheel(GlobalScrollDirection),
}
fn setup_mover(mut commands: Commands) {
let (tx, rx) = unbounded::<MouseControl>();
thread::spawn(move || {
let manager = Mouse::new();
for event in rx.iter() {
match event {
MouseControl::MoveTo(x, y) => {
manager.move_to(x, y).ok();
}
MouseControl::MoveRelative(x_offset, y_offset) => {
manager.move_relative(x_offset, y_offset).ok();
}
MouseControl::PressButton(button) => {
manager.press_button(&button).ok();
}
MouseControl::ReleaseButton(button) => {
manager.release_button(&button).ok();
}
MouseControl::ClickButton(button) => {
manager.click_button(&button).ok();
}
MouseControl::ScrollWheel(direction) => {
manager.scroll_wheel(&direction).ok();
}
};
}
});
commands.insert_resource(ControlEventSender(tx));
}
#[derive(Resource, Deref)]
struct ControlEventSender(Sender<MouseControl>);
fn mover_events(mut ev: EventReader<MouseControl>, tx: ResMut<ControlEventSender>) {
for event in ev.iter() {
tx.send(*event).ok();
}
}