use std::io;
use windows_win::{
raw,
Window,
Messages
};
use windows_win::sys::{
HWND,
AddClipboardFormatListener,
RemoveClipboardFormatListener,
PostMessageW,
WM_CLIPBOARDUPDATE,
};
use crate::{ClipboardHandler, CallbackResult};
const CLOSE_PARAM: isize = -1;
pub struct Shutdown {
window: HWND,
}
unsafe impl Send for Shutdown {}
impl Drop for Shutdown {
#[inline(always)]
fn drop(&mut self) {
unsafe {
PostMessageW(self.window, WM_CLIPBOARDUPDATE, 0, CLOSE_PARAM)
};
}
}
pub struct ClipboardListener(HWND);
impl ClipboardListener {
#[inline]
pub fn new(window: &Window) -> io::Result<Self> {
let window = window.inner();
unsafe {
if AddClipboardFormatListener(window) != 1 {
Err(io::Error::last_os_error())
} else {
Ok(ClipboardListener(window))
}
}
}
}
impl Drop for ClipboardListener {
fn drop(&mut self) {
unsafe {
RemoveClipboardFormatListener(self.0);
}
}
}
pub struct Master<H> {
handler: H,
window: Window,
}
impl<H: ClipboardHandler> Master<H> {
#[inline(always)]
pub fn new(handler: H) -> io::Result<Self> {
let window = match Window::from_builder(raw::window::Builder::new().class_name("STATIC").parent_message()) {
Ok(window) => window,
Err(error) => return Err(io::Error::from_raw_os_error(error.raw_code())),
};
Ok(Self {
handler,
window
})
}
#[inline(always)]
pub fn shutdown_channel(&self) -> Shutdown {
Shutdown {
window: self.window.inner()
}
}
pub fn run(&mut self) -> io::Result<()> {
let _guard = ClipboardListener::new(&self.window)?;
let mut result = Ok(());
for msg in Messages::new().window(Some(self.window.inner())).low(Some(WM_CLIPBOARDUPDATE)).high(Some(WM_CLIPBOARDUPDATE)) {
match msg {
Ok(msg) => match msg.id() {
WM_CLIPBOARDUPDATE => {
let msg = msg.inner();
if msg.lParam == CLOSE_PARAM {
break;
}
match self.handler.on_clipboard_change() {
CallbackResult::Next => (),
CallbackResult::Stop => break,
CallbackResult::StopWithError(error) => {
result = Err(error);
break;
}
}
},
_ => panic!("Unexpected message"),
},
Err(error) => {
match self.handler.on_clipboard_error(io::Error::from_raw_os_error(error.raw_code())) {
CallbackResult::Next => (),
CallbackResult::Stop => break,
CallbackResult::StopWithError(error) => {
result = Err(error);
break;
}
}
}
}
}
result
}
}