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
123
#![cfg(windows)]
//! Clipboard master
//!
//! Provides simple way to track updates of clipboard.
//!
//! ## Example:
//!
//! ```rust
//! fn callback() -> CallbackResult {
//!     println!("Clipboard change happened!");
//!     CallbackResult::Next
//! }
//!
//! fn error_callback(error: io::Error) -> CallbackResult {
//!     println!("Error: {}", error);
//!     CallbackResult::Next
//! }
//!
//! fn main() {
//!     let _ = Master::new(callback, error_callback).run()
//! }
//! ```
extern crate windows_win;
extern crate clipboard_win;
extern crate winapi;

use std::io;
use std::ops::FnMut;

use windows_win::{
    raw,
    Window,
    Messages
};

use winapi::windef::{
    HWND
};

use std::os::raw::{
    c_int
};

extern "system" {
    fn AddClipboardFormatListener(hWnd: HWND) -> c_int;
    fn RemoveClipboardFormatListener(hWnd: HWND) -> c_int;
}

///Possible return values of callback.
pub enum CallbackResult {
    ///Wait for next clipboard change.
    Next,
    ///Stop handling messages.
    Stop,
    ///Special variant to propagate IO Error from callback.
    StopWithError(io::Error)
}

///Default error callback that stops Master and propagates error in return value of `run()`
pub fn default_error(error: io::Error) -> CallbackResult {
    CallbackResult::StopWithError(error)
}

///Clipboard master.
///
///Tracks changes of clipboard and invokes corresponding callbacks.
pub struct Master<OK, ERR>
    where OK: FnMut() -> CallbackResult,
          ERR: FnMut(io::Error) -> CallbackResult
{
    cb_ok: OK,
    cb_err: ERR
}

impl<OK, ERR> Master<OK, ERR>
    where OK: FnMut() -> CallbackResult,
          ERR: FnMut(io::Error) -> CallbackResult
{
    ///Creates new instance.
    pub fn new(cb: OK, cb_err: ERR) -> Self {
        Master {
            cb_ok: cb,
            cb_err: cb_err
        }
    }

    ///Starts Master by creating dummy window and listening clipboard update messages.
    pub fn run(&mut self) -> io::Result<()> {
        let window = Window::from_builder(raw::window::Builder::new().class_name("STATIC").parent_message())?;

        unsafe {
            if AddClipboardFormatListener(window.inner()) != 1 {
                return Err(io::Error::last_os_error());
            };
        }

        for msg in Messages::new().window(Some(window.inner())).low(Some(797)).high(Some(797)) {
            match msg {
                Ok(_) => {
                    match (self.cb_ok)() {
                        CallbackResult::Next => (),
                        CallbackResult::Stop => break,
                        CallbackResult::StopWithError(error) => return Err(error),

                    }
                },
                Err(error) => {
                    match (self.cb_err)(error) {
                        CallbackResult::Next => (),
                        CallbackResult::Stop => break,
                        CallbackResult::StopWithError(error) => return Err(error),
                    }
                }
            }
        }

        unsafe {
            RemoveClipboardFormatListener(window.inner());
        }

        Ok(())
    }
}