raw img + background png convert

This commit is contained in:
2026-03-08 15:17:30 +01:00
parent 05ced3b7ea
commit e038981d7f
7 changed files with 86 additions and 57 deletions

View File

@ -1,28 +1,27 @@
use arboard::{Clipboard, ImageData}; use arboard::Clipboard;
use image::{ExtendedColorType, ImageEncoder, codecs::png::PngEncoder};
use std::error::Error; use std::error::Error;
use std::time::SystemTime; use std::time::SystemTime;
use uuid::Uuid; use uuid::Uuid;
use crate::models::{ClipboardData, ClipboardEntry, Image}; use crate::models::{ClipboardData, ClipboardEntry, Image};
pub trait ImageDataExt { // pub trait ImageDataExt {
fn to_png(&self) -> Result<Vec<u8>, Box<dyn Error>>; // fn to_png(&self) -> Result<Vec<u8>, Box<dyn Error>>;
} // }
//
impl ImageDataExt for ImageData<'_> { // impl ImageDataExt for ImageData<'_> {
fn to_png(&self) -> Result<Vec<u8>, Box<dyn Error>> { // fn to_png(&self) -> Result<Vec<u8>, Box<dyn Error>> {
let mut buffer = Vec::new(); // let mut buffer = Vec::new();
let encoder = PngEncoder::new(&mut buffer); // let encoder = PngEncoder::new(&mut buffer);
encoder.write_image( // encoder.write_image(
&self.bytes, // &self.bytes,
self.width as u32, // self.width as u32,
self.height as u32, // self.height as u32,
ExtendedColorType::Rgba8, // ExtendedColorType::Rgba8,
)?; // )?;
Ok(buffer) // Ok(buffer)
} // }
} // }
impl ClipboardEntry { impl ClipboardEntry {
pub fn new(clipboard: &mut Clipboard) -> Result<ClipboardEntry, Box<dyn Error>> { pub fn new(clipboard: &mut Clipboard) -> Result<ClipboardEntry, Box<dyn Error>> {
@ -32,7 +31,9 @@ impl ClipboardEntry {
Ok(image) => { Ok(image) => {
let id = Uuid::new_v4(); let id = Uuid::new_v4();
Some(ClipboardData::Image(Image { Some(ClipboardData::Image(Image {
bytes: Some(image.to_png()?), raw_pixels: Some(image.bytes.into_owned()),
width: image.width as u32,
height: image.height as u32,
id, id,
})) }))
} }

View File

@ -1,4 +1,6 @@
use crate::models::{ClipboardData, ClipboardEntry, Image}; use crate::models::{ClipboardData, ClipboardEntry, Image};
use image::codecs::png::PngEncoder;
use image::{ExtendedColorType, ImageEncoder};
use rusqlite::Connection; use rusqlite::Connection;
use std::error::Error; use std::error::Error;
use std::fs; use std::fs;
@ -43,11 +45,17 @@ impl Database {
let (entry_type, content) = match &entry.content { let (entry_type, content) = match &entry.content {
ClipboardData::Text(text) => ("text", text.clone()), ClipboardData::Text(text) => ("text", text.clone()),
ClipboardData::Image(img) => { ClipboardData::Image(img) => {
if let Some(bytes) = &img.bytes { if let Some(raw_pixels) = &img.raw_pixels {
let img_path = Path::new(&self.dir_path) let img_path = img.file_path(&self.dir_path);
.join("images")
.join(format!("{}.png", img.id)); let file = fs::File::create(&img_path)?;
fs::write(&img_path, bytes)?; let encoder = PngEncoder::new(file);
encoder.write_image(
raw_pixels,
img.width,
img.height,
ExtendedColorType::Rgba8,
)?;
} }
("image", img.id.to_string()) ("image", img.id.to_string())
} }
@ -83,7 +91,12 @@ impl Database {
ClipboardData::Text(content) ClipboardData::Text(content)
} else { } else {
let id = Uuid::parse_str(&content)?; let id = Uuid::parse_str(&content)?;
ClipboardData::Image(Image { id, bytes: None }) ClipboardData::Image(Image {
id,
raw_pixels: None,
width: 0,
height: 0,
})
}; };
entries.push(ClipboardEntry { entries.push(ClipboardEntry {

View File

@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use crate::database::Database; use crate::database::Database;
use arboard::Clipboard; use arboard::Clipboard;
@ -9,12 +11,11 @@ mod ws;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let clipboard = Clipboard::new()?; let clipboard = Clipboard::new()?;
let dir_path = "clipboard"; let dir_path = "clipboard";
let db = Database::init(dir_path)?; let db = Arc::new(Mutex::new(Database::init(dir_path)?));
// println!("{:#?}", db.read_history()); // println!("{:#?}", db.lock().unwrap().read_history());
monitor::start(db, clipboard)?; monitor::start(db, clipboard)?;

View File

@ -17,7 +17,9 @@ pub enum ClipboardData {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Image { pub struct Image {
pub bytes: Option<Vec<u8>>, pub raw_pixels: Option<Vec<u8>>,
pub width: u32,
pub height: u32,
pub id: Uuid, pub id: Uuid,
} }
@ -29,9 +31,6 @@ impl Image {
} }
pub fn load_bytes(&self, dir_path: &str) -> io::Result<Vec<u8>> { pub fn load_bytes(&self, dir_path: &str) -> io::Result<Vec<u8>> {
if let Some(b) = &self.bytes {
return Ok(b.clone());
}
fs::read(self.file_path(dir_path)) fs::read(self.file_path(dir_path))
} }
} }

View File

@ -1,11 +1,12 @@
use crate::database::Database; use crate::database::Database;
use arboard::Clipboard; use arboard::Clipboard;
use std::error::Error; use std::error::Error;
use std::sync::{Arc, Mutex};
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
use crate::ws; use crate::ws;
#[cfg(feature = "x11")] #[cfg(feature = "x11")]
pub fn start(db: Database, clipboard: Clipboard) -> Result<(), Box<dyn Error>> { pub fn start(db: Arc<Mutex<Database>>, clipboard: Clipboard) -> Result<(), Box<dyn Error>> {
ws::x11::start(db, clipboard)?; ws::x11::start(db, clipboard)?;
Ok(()) Ok(())
} }
@ -13,12 +14,12 @@ pub fn start(db: Database, clipboard: Clipboard) -> Result<(), Box<dyn Error>> {
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
use crate::ws; use crate::ws;
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn start(db: Database, clipboard: Clipboard) -> Result<(), Box<dyn Error>> { pub fn start(db: Arc<Mutex<Database>>, clipboard: Clipboard) -> Result<(), Box<dyn Error>> {
ws::wayland::start(db, clipboard)?; ws::wayland::start(db, clipboard)?;
Ok(()) Ok(())
} }
#[cfg(not(any(feature = "x11", feature = "wayland")))] #[cfg(not(any(feature = "x11", feature = "wayland")))]
pub fn start(_db: Database, _clipboard: Clipboard) -> Result<(), Box<dyn Error>> { pub fn start(_db: Arc<Mutex<Database>>, _clipboard: Clipboard) -> Result<(), Box<dyn Error>> {
Err("No window system feature enabled".into()) Err("No window system feature enabled".into())
} }

View File

@ -1,10 +1,14 @@
use crate::{database::Database, models::ClipboardEntry}; use crate::{database::Database, models::ClipboardEntry};
use arboard::Clipboard; use arboard::Clipboard;
use std::time::Duration; use std::time::Duration;
use std::{error::Error, sync::mpsc::channel}; use std::{
error::Error,
sync::mpsc::channel,
sync::{Arc, Mutex},
};
use wayland_clipboard_listener::{WlClipboardPasteStream, WlListenType}; use wayland_clipboard_listener::{WlClipboardPasteStream, WlListenType};
pub fn start(db: Database, mut clipboard: Clipboard) -> Result<(), Box<dyn Error>> { pub fn start(db: Arc<Mutex<Database>>, mut clipboard: Clipboard) -> Result<(), Box<dyn Error>> {
let (tx, rx) = channel(); let (tx, rx) = channel();
std::thread::spawn( std::thread::spawn(
@ -26,18 +30,24 @@ pub fn start(db: Database, mut clipboard: Clipboard) -> Result<(), Box<dyn Error
for _ in rx { for _ in rx {
println!("Clipboard update!"); println!("Clipboard update!");
// if let Ok(entry) = ClipboardEntry::new(&mut clipboard) { if let Ok(entry) = ClipboardEntry::new(&mut clipboard) {
// if let Err(e) = db.append(entry) { let db_clone = Arc::clone(&db);
// eprintln!("SQLite writing error: {}", e);
// } else { std::thread::spawn(move || {
// println!("SQLite edited!"); let db_lock = db_clone.lock().unwrap();
// }
// } if let Err(e) = db_lock.append(entry) {
// eprintln!("SQLite writing error: {}", e);
match ClipboardEntry::new(&mut clipboard) { } else {
Ok(entry) => db.append(entry)?, println!("SQLite edited!");
Err(e) => eprintln!("{}", e), }
});
} }
// match ClipboardEntry::new(&mut clipboard) {
// Ok(entry) => db.append(entry)?,
// Err(e) => eprintln!("{}", e),
// }
} }
Ok(()) Ok(())

View File

@ -1,12 +1,12 @@
use crate::{database::Database, models::ClipboardEntry};
use arboard::Clipboard; use arboard::Clipboard;
use clipboard_master::{CallbackResult, ClipboardHandler, Master}; use clipboard_master::{CallbackResult, ClipboardHandler, Master};
use std::sync::{Arc, Mutex};
use std::{ use std::{
error::Error, error::Error,
sync::mpsc::{Sender, channel}, sync::mpsc::{Sender, channel},
}; };
use crate::{database::Database, models::ClipboardEntry};
pub struct Handler { pub struct Handler {
pub clipboard_tx: Sender<()>, pub clipboard_tx: Sender<()>,
} }
@ -20,7 +20,7 @@ impl ClipboardHandler for Handler {
} }
} }
pub fn start(db: Database, mut clipboard: Clipboard) -> Result<(), Box<dyn Error>> { pub fn start(db: Arc<Mutex<Database>>, mut clipboard: Clipboard) -> Result<(), Box<dyn Error>> {
let (tx, rx) = channel(); let (tx, rx) = channel();
let mut master = Master::new(Handler { clipboard_tx: tx })?; let mut master = Master::new(Handler { clipboard_tx: tx })?;
@ -33,11 +33,15 @@ pub fn start(db: Database, mut clipboard: Clipboard) -> Result<(), Box<dyn Error
for _ in rx { for _ in rx {
println!("Clipboard update!"); println!("Clipboard update!");
if let Ok(entry) = ClipboardEntry::new(&mut clipboard) { if let Ok(entry) = ClipboardEntry::new(&mut clipboard) {
if let Err(e) = db.append(entry) { let db_clone = Arc::clone(&db);
eprintln!("SQLite writing error: {}", e); std::thread::spawn(move || {
} else { let db_lock = db_clone.lock().unwrap();
// println!("SQLite edited!"); if let Err(e) = db_lock.append(entry) {
} eprintln!("SQLite writing error: {}", e);
} else {
println!("SQLite edited!");
}
});
} }
} }