This commit is contained in:
2026-05-20 19:31:16 +02:00
parent 20f33f5694
commit 8b07e305f0
17 changed files with 1794 additions and 573 deletions

View File

@ -1,49 +1,83 @@
use crate::{database::Database, models::ClipboardEntry};
use crate::database::Database;
use crate::models::{ClipboardData, ClipboardEntry, Image};
use arboard::Clipboard;
use clipboard_master::{CallbackResult, ClipboardHandler, Master};
use std::collections::hash_map::DefaultHasher;
use std::error::Error;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};
use std::{
error::Error,
sync::mpsc::{Sender, channel},
};
use std::thread;
use std::time::{Duration, SystemTime};
use uuid::Uuid;
pub struct Handler {
pub clipboard_tx: Sender<()>,
}
impl ClipboardHandler for Handler {
fn on_clipboard_change(&mut self) -> CallbackResult {
if let Err(e) = self.clipboard_tx.send(()) {
eprintln!("{}", e);
}
CallbackResult::Next
}
fn hash_bytes(data: &[u8]) -> u64 {
let mut hasher = DefaultHasher::new();
data.hash(&mut hasher);
hasher.finish()
}
pub fn start(db: Arc<Mutex<Database>>, mut clipboard: Clipboard) -> Result<(), Box<dyn Error>> {
let (tx, rx) = channel();
println!("Clipboard monitor started (X11 polling mode)...");
let mut master = Master::new(Handler { clipboard_tx: tx })?;
std::thread::spawn(move || {
if let Err(e) = master.run() {
eprintln!("Clipboard monitor error : {}", e);
}
});
let mut last_text: Option<String> = None;
let mut last_image_hash: Option<u64> = None;
for _ in rx {
println!("Clipboard update!");
if let Ok(entry) = ClipboardEntry::new(&mut clipboard) {
let db_clone = Arc::clone(&db);
std::thread::spawn(move || {
let db_lock = db_clone.lock().unwrap();
if let Err(e) = db_lock.append(entry) {
eprintln!("SQLite writing error: {}", e);
} else {
println!("SQLite edited!");
loop {
thread::sleep(Duration::from_millis(500));
match clipboard.get_text() {
Ok(raw) => {
let text = raw.trim_end_matches('\n').to_string();
if text.is_empty() || Some(&text) == last_text.as_ref() {
continue;
}
});
last_text = Some(text.clone());
last_image_hash = None;
println!("Clipboard update (text)!");
let entry = ClipboardEntry {
content: ClipboardData::Text(text),
timestamp: SystemTime::now(),
};
spawn_db_write(Arc::clone(&db), entry);
}
Err(_) => {
let Ok(img_data) = clipboard.get_image() else {
continue;
};
let hash = hash_bytes(&img_data.bytes);
if Some(hash) == last_image_hash {
continue;
}
last_image_hash = Some(hash);
last_text = None;
println!("Clipboard update (image)!");
let entry = ClipboardEntry {
content: ClipboardData::Image(Image {
raw_pixels: Some(img_data.bytes.into_owned()),
width: img_data.width as u32,
height: img_data.height as u32,
id: Uuid::new_v4(),
}),
timestamp: SystemTime::now(),
};
spawn_db_write(Arc::clone(&db), entry);
}
}
}
Ok(())
}
fn spawn_db_write(db: Arc<Mutex<Database>>, entry: ClipboardEntry) {
thread::spawn(move || {
let db_lock = db.lock().unwrap();
if let Err(e) = db_lock.append(entry) {
eprintln!("SQLite writing error: {}", e);
} else {
println!("SQLite updated!");
}
});
}