mod builder;
mod inner;
mod key;
mod proc;
mod state;
use std::sync::atomic::{self, AtomicU64};
use std::sync::{Arc, Weak};
pub use builder::{
InputCharacterBuilder, InputCursorBuilder, InputEnterBuilder, InputFocusBuilder,
InputHoldBuilder, InputHookBuilder, InputMotionBuilder, InputPressBuilder, InputScrollBuilder,
};
use flume::Sender;
use inner::LoopEvent;
pub use key::{Char, Key, KeyCombo, MouseButton, Qwerty};
use state::HookState;
pub use state::{LocalCursorState, LocalKeyState, WindowState};
use crate::interface::{Bin, BinID, Interface};
use crate::interval::Interval;
use crate::window::{Window, WindowID};
const NO_HOOK_WEIGHT: i16 = i16::min_value();
const BIN_FOCUS_KEY: Key = Key::Mouse(MouseButton::Left);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InputHookID(u64);
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum InputHookTarget {
None,
Window(Arc<Window>),
Bin(Arc<Bin>),
}
impl InputHookTarget {
fn id(&self) -> InputHookTargetID {
match self {
Self::None => InputHookTargetID::None,
Self::Window(win) => InputHookTargetID::Window(win.id()),
Self::Bin(bin) => InputHookTargetID::Bin(bin.id()),
}
}
fn weak(&self) -> InputHookTargetWeak {
match self {
Self::None => InputHookTargetWeak::None,
Self::Window(win) => InputHookTargetWeak::Window(Arc::downgrade(win)),
Self::Bin(bin) => InputHookTargetWeak::Bin(Arc::downgrade(bin)),
}
}
pub fn into_bin(self) -> Option<Arc<Bin>> {
match self {
InputHookTarget::Bin(bin) => Some(bin),
_ => None,
}
}
pub fn into_window(self) -> Option<Arc<Window>> {
match self {
InputHookTarget::Window(win) => Some(win),
_ => None,
}
}
}
impl PartialEq for InputHookTarget {
fn eq(&self, other: &Self) -> bool {
match self {
Self::None => matches!(other, Self::None),
Self::Window(window) => {
match other {
Self::Window(other_window) => window.id() == other_window.id(),
_ => false,
}
},
Self::Bin(bin) => {
match other {
Self::Bin(other_bin) => bin == other_bin,
_ => false,
}
},
}
}
}
impl Eq for InputHookTarget {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum InputHookCtrl {
#[default]
Retain,
RetainNoPass,
Remove,
RemoveNoPass,
}
#[derive(Debug, Clone)]
pub(crate) enum InputEvent {
Press { win: WindowID, key: Key },
Release { win: WindowID, key: Key },
Character { win: WindowID, c: char },
Cursor { win: WindowID, x: f32, y: f32 },
Scroll { win: WindowID, v: f32, h: f32 },
Enter { win: WindowID },
Leave { win: WindowID },
Focus { win: WindowID },
FocusLost { win: WindowID },
Motion { x: f32, y: f32 },
CursorCapture { win: WindowID, captured: bool },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputError {
NoKeys,
NoMethod,
NoTarget,
NoTrigger,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
enum InputHookTargetID {
#[default]
None,
Window(WindowID),
Bin(BinID),
}
enum InputHookTargetWeak {
None,
Window(Weak<Window>),
Bin(Weak<Bin>),
}
impl InputHookTargetWeak {
fn upgrade(&self) -> Option<InputHookTarget> {
match self {
Self::None => Some(InputHookTarget::None),
Self::Window(wk) => wk.upgrade().map(InputHookTarget::Window),
Self::Bin(wk) => wk.upgrade().map(InputHookTarget::Bin),
}
}
}
struct Hook {
target_id: InputHookTargetID,
target_wk: InputHookTargetWeak,
state: HookState,
}
impl Hook {
fn is_for_window_id(&self, win_id: WindowID) -> bool {
match &self.target_id {
InputHookTargetID::Window(self_win_id) => *self_win_id == win_id,
_ => false,
}
}
fn is_for_bin_id(&self, bin_id: BinID) -> bool {
match &self.target_id {
InputHookTargetID::Bin(self_bin_id) => *self_bin_id == bin_id,
_ => false,
}
}
fn bin_id(&self) -> Option<BinID> {
match &self.target_id {
InputHookTargetID::Bin(bin_id) => Some(*bin_id),
_ => None,
}
}
}
pub struct Input {
event_send: Sender<LoopEvent>,
current_id: AtomicU64,
interval: Arc<Interval>,
}
impl Input {
pub(crate) fn new(interface: Arc<Interface>, interval: Arc<Interval>) -> Self {
let (event_send, event_recv) = flume::unbounded();
inner::begin_loop(interface, interval.clone(), event_send.clone(), event_recv);
Self {
event_send,
interval,
current_id: AtomicU64::new(0),
}
}
pub(in crate::input) fn event_send(&self) -> Sender<LoopEvent> {
self.event_send.clone()
}
pub(in crate::input) fn interval(&self) -> Arc<Interval> {
self.interval.clone()
}
pub fn hook(&self) -> InputHookBuilder {
InputHookBuilder::start(self)
}
pub fn remove_hook(&self, id: InputHookID) {
self.event_send.send(LoopEvent::Remove(id)).unwrap();
}
pub fn set_bin_focused(&self, bin: &Arc<Bin>) {
let win = match bin.window() {
Some(some) => some.id(),
None => return,
};
self.event_send
.send(LoopEvent::FocusBin {
win,
bin: Some(bin.id()),
})
.unwrap();
}
pub(crate) fn send_event(&self, event: InputEvent) {
self.event_send.send(LoopEvent::Normal(event)).unwrap();
}
fn add_hook(&self, hook: Hook) -> InputHookID {
let id = InputHookID(self.current_id.fetch_add(1, atomic::Ordering::SeqCst));
self.event_send
.send(LoopEvent::Add {
id,
hook,
})
.unwrap();
id
}
pub(in crate::input) fn add_hook_with_id(&self, id: InputHookID, hook: Hook) {
self.event_send
.send(LoopEvent::Add {
id,
hook,
})
.unwrap();
}
pub(in crate::input) fn next_id(&self) -> InputHookID {
InputHookID(self.current_id.fetch_add(1, atomic::Ordering::SeqCst))
}
}