1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::Desktop;
use crate::DesktopEventThread;
use crate::Error;
use windows::Win32::Foundation::HWND;

#[derive(Clone)]
pub enum DesktopEventSender<T>
where
    T: 'static,
{
    Std(std::sync::mpsc::Sender<T>),

    #[cfg(feature = "crossbeam-channel")]
    Crossbeam(crossbeam_channel::Sender<T>),

    #[cfg(feature = "winit")]
    Winit(winit::event_loop::EventLoopProxy<T>),
}

// From STD Sender
impl<T> From<std::sync::mpsc::Sender<T>> for DesktopEventSender<T>
where
    T: From<DesktopEvent> + Clone + Send + 'static,
{
    fn from(sender: std::sync::mpsc::Sender<T>) -> Self {
        DesktopEventSender::Std(sender)
    }
}

// From Crossbeam Sender
#[cfg(feature = "crossbeam-channel")]
impl<T> From<crossbeam_channel::Sender<T>> for DesktopEventSender<T>
where
    T: From<DesktopEvent> + Clone + Send + 'static,
{
    fn from(sender: crossbeam_channel::Sender<T>) -> Self {
        DesktopEventSender::Crossbeam(sender)
    }
}

// From Winit Sender
#[cfg(feature = "winit")]
impl<T> From<winit::event_loop::EventLoopProxy<T>> for DesktopEventSender<T>
where
    T: From<DesktopEvent> + Clone + Send + 'static,
{
    fn from(sender: winit::event_loop::EventLoopProxy<T>) -> Self {
        DesktopEventSender::Winit(sender)
    }
}

impl<T> DesktopEventSender<T> {
    pub fn try_send(&self, event: T) {
        match self {
            DesktopEventSender::Std(sender) => {
                let _ = sender.send(event);
            }

            #[cfg(feature = "crossbeam-channel")]
            DesktopEventSender::Crossbeam(sender) => {
                let _ = sender.try_send(event);
            }

            #[cfg(feature = "winit")]
            DesktopEventSender::Winit(sender) => {
                let _ = sender.send_event(event);
            }
        }
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DesktopEvent {
    DesktopCreated(Desktop),
    DesktopDestroyed {
        destroyed: Desktop,
        fallback: Desktop,
    },
    DesktopChanged {
        new: Desktop,
        old: Desktop,
    },
    DesktopNameChanged(Desktop, String),
    DesktopWallpaperChanged(Desktop, String),
    DesktopMoved {
        desktop: Desktop,
        old_index: i64,
        new_index: i64,
    },
    WindowChanged(HWND),
}

/// Create event sending thread, give this `crossbeam_channel::Sender<T>`,
/// `winit::event_loop::EventLoopProxy<T>`, or `std::sync::mpsc::Sender<T>`.
///
/// `DesktopEvent` must be convertible to your message type `T`.
///
/// This function returns `DesktopEventThread`, which must be kept alive. When
/// the value is dropped the listener is closed and thread joined.
///
/// # Example
///
/// ```rust
/// let (tx, rx) = std::sync::mpsc::channel::<DesktopEvent>();
/// let _notifications_thread = listen_desktop_events(tx);
/// // Do with receiver something
/// for item in rx {
///    println!("{:?}", item);
/// }
/// // When `_notifications_thread` is dropped the thread is joined and listener closed.
/// ```
///
/// Additionally you can pass crossbeam-channel sender, or winit eventloop proxy
/// to the function.
///
pub fn listen_desktop_events<T, S>(sender: S) -> Result<DesktopEventThread, Error>
where
    T: From<DesktopEvent> + Clone + Send + 'static,
    S: Into<DesktopEventSender<T>> + Clone,
{
    DesktopEventThread::new(sender.into())
}