From d8cfd073ad245697e0834ba85626f23376ce65f3 Mon Sep 17 00:00:00 2001 From: DoumanAsh Date: Sun, 7 Jul 2024 15:15:13 +0900 Subject: [PATCH] 5.4.0: Add API to customize clipboard setting behavior --- Cargo.toml | 2 +- src/lib.rs | 1 + src/options.rs | 33 +++++++++++++++ src/raw.rs | 102 ++++++++++++++++++++++++++++++++++++--------- tests/test_clip.rs | 1 + 5 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 src/options.rs diff --git a/Cargo.toml b/Cargo.toml index 0bbc605..6395013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clipboard-win" -version = "5.3.1" +version = "5.4.0" authors = ["Douman "] description = "Provides simple way to interact with Windows clipboard." license = "BSL-1.0" diff --git a/src/lib.rs b/src/lib.rs index 1e95f2f..ab1d30c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,6 +88,7 @@ extern crate std; extern crate alloc; +pub mod options; mod sys; pub mod types; pub mod formats; diff --git a/src/options.rs b/src/options.rs new file mode 100644 index 0000000..abae91e --- /dev/null +++ b/src/options.rs @@ -0,0 +1,33 @@ +//! Configuration options + +use crate::SysResult; +use crate::raw::empty; + +///Function type to empty clipboard +pub type EmptyFn = fn() -> SysResult<()>; + +///Clearing parameter +pub trait Clearing { + ///Empty behavior definition + const EMPTY_FN: EmptyFn; +} + +#[derive(Copy, Clone)] +///Performs no clearing of clipboard +pub struct NoClear; + +fn noop() -> SysResult<()> { + Ok(()) +} + +impl Clearing for NoClear { + const EMPTY_FN: EmptyFn = noop; +} + +#[derive(Copy, Clone)] +///Performs clearing of clipboard before pasting +pub struct DoClear; + +impl Clearing for DoClear { + const EMPTY_FN: EmptyFn = empty; +} diff --git a/src/raw.rs b/src/raw.rs index cd771f3..7a6414e 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -13,6 +13,7 @@ use crate::types::*; use crate::sys::*; use crate::utils::Buffer; +use crate::options::{self, EmptyFn, Clearing}; const CBM_INIT: DWORD = 0x04; const BI_RGB: DWORD = 0; @@ -383,8 +384,21 @@ pub fn get_html(format: u32, out: &mut alloc::vec::Vec) -> SysResult Ok(result) } +///Sets HTML using format code created by `register_raw_format` or `register_format` with argument `HTML Format` +/// +///Allows to customize clipboard setting behavior +/// +///- `C` - Specifies clearing behavior +pub fn set_html_with(format: u32, html: &str, _is_clear: C) -> SysResult<()> { + set_html_inner(format, html, C::EMPTY_FN) +} + ///Sets HTML using format code created by `register_raw_format` or `register_format` with argument `HTML Format` pub fn set_html(format: u32, html: &str) -> SysResult<()> { + set_html_inner(format, html, options::NoClear::EMPTY_FN) +} + +fn set_html_inner(format: u32, html: &str, empty: EmptyFn) -> SysResult<()> { const VERSION_VALUE: &str = ":0.9"; const HEADER_SIZE: usize = html::VERSION.len() + VERSION_VALUE.len() + html::NEWLINE.len() + html::START_HTML.len() + html::LEN_SIZE + 1 + html::NEWLINE.len() @@ -459,6 +473,7 @@ pub fn set_html(format: u32, html: &str) -> SysResult<()> { debug_assert_eq!(cursor, total_size); } + let _ = (empty)(); if unsafe { !SetClipboardData(format, mem.get()).is_null() } { //SetClipboardData takes ownership mem.release(); @@ -468,18 +483,7 @@ pub fn set_html(format: u32, html: &str) -> SysResult<()> { } } -/// Copies raw bytes onto clipboard with specified `format`, returning whether it was successful. -/// -/// This function empties the clipboard before setting the data. -pub fn set(format: u32, data: &[u8]) -> SysResult<()> { - let _ = empty(); - set_without_clear(format, data) -} - -/// Copies raw bytes onto the clipboard with the specified `format`, returning whether it was successful. -/// -/// This function does not empty the clipboard before setting the data. -pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> { +fn set_inner(format: u32, data: &[u8], clear: EmptyFn) -> SysResult<()> { let size = data.len(); if size == 0 { #[allow(clippy::unit_arg)] @@ -493,6 +497,7 @@ pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> { unsafe { ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr() as _, size) }; } + let _ = (clear)(); if unsafe { !SetClipboardData(format, mem.get()).is_null() } { //SetClipboardData takes ownership mem.release(); @@ -501,6 +506,19 @@ pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> { Err(ErrorCode::last_system()) } +/// Copies raw bytes onto clipboard with specified `format`, returning whether it was successful. +/// +/// This function empties the clipboard before setting the data. +pub fn set(format: u32, data: &[u8]) -> SysResult<()> { + set_inner(format, data, options::DoClear::EMPTY_FN) +} + +/// Copies raw bytes onto the clipboard with the specified `format`, returning whether it was successful. +/// +/// This function does not empty the clipboard before setting the data. +pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> { + set_inner(format, data, options::NoClear::EMPTY_FN) +} ///Copies raw bytes from clipboard with specified `format`, appending to `out` buffer. /// @@ -535,9 +553,7 @@ pub fn get_string(out: &mut alloc::vec::Vec) -> SysResult { Ok(result) } -///Copies unicode string onto clipboard, performing necessary conversions, returning true on -///success. -pub fn set_string(data: &str) -> SysResult<()> { +fn set_string_inner(data: &str, clear: EmptyFn) -> SysResult<()> { let size = unsafe { MultiByteToWideChar(CP_UTF8, 0, data.as_ptr() as *const _, data.len() as _, ptr::null_mut(), 0) }; @@ -554,8 +570,7 @@ pub fn set_string(data: &str) -> SysResult<()> { } } - let _ = empty(); - + let _ = (clear)(); if unsafe { !SetClipboardData(formats::CF_UNICODETEXT, mem.get()).is_null() } { //SetClipboardData takes ownership mem.release(); @@ -566,6 +581,24 @@ pub fn set_string(data: &str) -> SysResult<()> { Err(ErrorCode::last_system()) } +#[inline(always)] +///Copies unicode string onto clipboard, performing necessary conversions, returning true on +///success. +pub fn set_string(data: &str) -> SysResult<()> { + set_string_inner(data, options::DoClear::EMPTY_FN) +} + +#[inline(always)] +///Copies unicode string onto clipboard, performing necessary conversions, returning true on +///success. +/// +///Allows to customize clipboard setting behavior +/// +///- `C` - Specifies clearing behavior +pub fn set_string_with(data: &str, _is_clear: C) -> SysResult<()> { + set_string_inner(data, C::EMPTY_FN) +} + #[cfg(feature = "std")] ///Retrieves file list from clipboard, appending each element to the provided storage. /// @@ -752,6 +785,24 @@ pub fn set_bitamp(data: &[u8]) -> SysResult<()> { /// ///Returns `ERROR_INCORRECT_SIZE` if size of data is not valid pub fn set_bitmap(data: &[u8]) -> SysResult<()> { + //Bitmap format cannot really overlap with much so there is no risk of having non-empty clipboard + //Also it is backward compatible beahvior. + //To be changed in 6.x + set_bitmap_inner(data, options::NoClear::EMPTY_FN) +} + +///Sets bitmap (header + RGB) onto clipboard, from raw bytes. +/// +///Returns `ERROR_INCORRECT_SIZE` if size of data is not valid +/// +///Allows to customize clipboard setting behavior +/// +///- `C` - Specifies clearing behavior +pub fn set_bitmap_with(data: &[u8], _is_clear: C) -> SysResult<()> { + set_bitmap_inner(data, C::EMPTY_FN) +} + +fn set_bitmap_inner(data: &[u8], clear: EmptyFn) -> SysResult<()> { const FILE_HEADER_LEN: usize = mem::size_of::(); const INFO_HEADER_LEN: usize = mem::size_of::(); @@ -788,7 +839,7 @@ pub fn set_bitmap(data: &[u8]) -> SysResult<()> { return Err(ErrorCode::last_system()); } - let _ = empty(); + let _ = (clear)(); if unsafe { SetClipboardData(formats::CF_BITMAP, handle as _).is_null() } { return Err(ErrorCode::last_system()); } @@ -797,8 +848,20 @@ pub fn set_bitmap(data: &[u8]) -> SysResult<()> { } +#[inline(always)] ///Set list of file paths to clipboard. pub fn set_file_list(paths: &[impl AsRef]) -> SysResult<()> { + //See set_bitmap for reasoning of NoClear + set_file_list_inner(paths, options::NoClear::EMPTY_FN) +} + +#[inline(always)] +///Set list of file paths to clipboard. +pub fn set_file_list_with(paths: &[impl AsRef], _is_clear: C) -> SysResult<()> { + set_file_list_inner(paths, C::EMPTY_FN) +} + +fn set_file_list_inner(paths: &[impl AsRef], empty: EmptyFn) -> SysResult<()> { #[repr(C, packed(1))] pub struct DROPFILES { pub p_files: u32, @@ -852,8 +915,7 @@ pub fn set_file_list(paths: &[impl AsRef]) -> SysResult<()> { } } - let _ = empty(); - + let _ = (empty)(); if unsafe { !SetClipboardData(formats::CF_HDROP, mem.get()).is_null() } { //SetClipboardData now has ownership of `mem`. mem.release(); diff --git a/tests/test_clip.rs b/tests/test_clip.rs index 72bc560..21c9087 100644 --- a/tests/test_clip.rs +++ b/tests/test_clip.rs @@ -163,6 +163,7 @@ macro_rules! run { #[test] fn clipboard_should_work() { + run!(should_work_with_bitmap); assert!(is_format_avail(CF_BITMAP)); run!(should_work_with_string);