use std::io;
use std::sync::mpsc::{self, SyncSender, Receiver, sync_channel};
use crate::{ClipboardHandler, CallbackResult};
use objc2::{msg_send, rc, ClassType};
use objc2_app_kit::NSPasteboard;
#[link(name = "AppKit", kind = "framework")]
extern "C" {}
pub struct Shutdown {
sender: SyncSender<()>,
}
impl Drop for Shutdown {
#[inline(always)]
fn drop(&mut self) {
let _ = self.sender.send(());
}
}
pub struct Master<H> {
handler: H,
sender: SyncSender<()>,
recv: Receiver<()>
}
impl<H: ClipboardHandler> Master<H> {
#[inline(always)]
pub fn new(handler: H) -> io::Result<Self> {
let (sender, recv) = sync_channel(0);
Ok(Self {
handler,
sender,
recv,
})
}
#[inline(always)]
pub fn shutdown_channel(&self) -> Shutdown {
Shutdown {
sender: self.sender.clone()
}
}
pub fn run(&mut self) -> io::Result<()> {
let pasteboard: Option<rc::Retained<NSPasteboard>> = unsafe { msg_send![NSPasteboard::class(), generalPasteboard] };
let pasteboard = match pasteboard {
Some(pasteboard) => pasteboard,
None => return Err(io::Error::new(io::ErrorKind::Other, "Unable to create mac pasteboard")),
};
let mut prev_count = pasteboard.changeCount();
let mut result = Ok(());
loop {
let count: isize = pasteboard.changeCount();
if count == prev_count {
match self.recv.recv_timeout(self.handler.sleep_interval()) {
Ok(()) => break,
Err(mpsc::RecvTimeoutError::Timeout) => continue,
Err(mpsc::RecvTimeoutError::Disconnected) => break,
}
}
prev_count = count;
match self.handler.on_clipboard_change() {
CallbackResult::Next => (),
CallbackResult::Stop => break,
CallbackResult::StopWithError(error) => {
result = Err(error);
break;
}
}
match self.recv.try_recv() {
Ok(()) => break,
Err(mpsc::TryRecvError::Empty) => continue,
Err(mpsc::TryRecvError::Disconnected) => break,
}
}
result
}
}