use std::fmt;
use std::ffi::CString;
use windows::Win32::{
Foundation::{HANDLE, HGLOBAL},
System::Memory::{GlobalAlloc, GlobalLock, GlobalUnlock, GMEM_MOVEABLE},
};
#[derive(Debug)]
pub enum Error {
HtmlTemplateCreationError,
OpenClipboardError,
EmptyClipboardError,
SetClipboardError,
CloseClipboardError,
MemoryAllocationError,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::HtmlTemplateCreationError => write!(f, "Failed to create HTML template."),
Error::OpenClipboardError => write!(f, "Failed to open clipboard."),
Error::EmptyClipboardError => write!(f, "Failed to empty clipboard."),
Error::SetClipboardError => write!(f, "Failed to set clipboard."),
Error::CloseClipboardError => write!(f, "Failed to close clipboard."),
Error::MemoryAllocationError => write!(f, "Failed to allocate memory."),
}
}
}
pub fn set_clipboard_html(html: String) -> Result<(), Error> {
let fragment = html;
let start_html = 391;
let start_fragment = 454;
let start_selection = Some(start_fragment);
let end_fragment = start_fragment + fragment.len() - 1;
let end_selection = Some(end_fragment);
let end_html = end_fragment + 37;
let mut document = String::new();
document.push_str(format!("Version:{}\n", "0.9").as_str());
document.push_str(format!("StartHTML:{:0>50}\n", start_html).as_str());
document.push_str(format!("EndHTML:{:0>50}\n", end_html).as_str());
document.push_str(format!("StartFragment:{:0>50}\n", start_fragment).as_str());
document.push_str(format!("EndFragment:{:0>50}\n", end_fragment).as_str());
if let (Some(start_selection), Some(end_selection)) = (start_selection, end_selection) {
document.push_str(format!("StartSelection:{:0>50}\n", start_selection).as_str());
document.push_str(format!("EndSelection:{:0>50}\n", end_selection).as_str());
}
document.push_str(
r#"<!DOCTYPE>
<HTML>
<HEAD>
</HEAD>
<BODY>
<!-- StartFragment -->
"#,
);
document.push_str(&fragment);
document.push_str(
r#"
<!-- EndFragment -->
</BODY>
</HTML>"#,
);
let cstring = CString::new(document)
.map_err(|_| Error::HtmlTemplateCreationError)?
.into_bytes_with_nul();
#[allow(non_snake_case)]
let CF_HTML;
unsafe {
let format_name: Vec<u16> = "HTML Format\0".encode_utf16().collect();
let pcwstr = windows::core::PCWSTR(format_name.as_ptr());
let uint = windows::Win32::System::DataExchange::RegisterClipboardFormatW(pcwstr);
CF_HTML = uint;
}
unsafe {
windows::Win32::System::DataExchange::OpenClipboard(None)
.map_err(|_| Error::OpenClipboardError)?;
}
unsafe {
windows::Win32::System::DataExchange::EmptyClipboard()
.map_err(|_| Error::EmptyClipboardError)?;
}
unsafe {
let mem_alloc: HGLOBAL =
GlobalAlloc(GMEM_MOVEABLE, cstring.len() * std::mem::size_of::<u16>())
.map_err(|_| Error::MemoryAllocationError)?;
let mem_lock = GlobalLock(mem_alloc);
std::ptr::copy_nonoverlapping(cstring.as_ptr(), mem_lock as *mut u8, cstring.len());
let _ = GlobalUnlock(mem_alloc);
let handle = HANDLE(mem_alloc.0 as isize);
if windows::Win32::System::DataExchange::SetClipboardData(CF_HTML, handle).is_err() {
panic!("Failed to set clipboard.");
}
}
unsafe {
windows::Win32::System::DataExchange::CloseClipboard()
.map_err(|_| Error::CloseClipboardError)?;
}
Ok(())
}