Skip to content

Commit d8cfd07

Browse files
committed
5.4.0: Add API to customize clipboard setting behavior
1 parent a8d2d10 commit d8cfd07

File tree

5 files changed

+118
-21
lines changed

5 files changed

+118
-21
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "clipboard-win"
3-
version = "5.3.1"
3+
version = "5.4.0"
44
authors = ["Douman <douman@gmx.se>"]
55
description = "Provides simple way to interact with Windows clipboard."
66
license = "BSL-1.0"

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ extern crate std;
8888

8989
extern crate alloc;
9090

91+
pub mod options;
9192
mod sys;
9293
pub mod types;
9394
pub mod formats;

src/options.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//! Configuration options
2+
3+
use crate::SysResult;
4+
use crate::raw::empty;
5+
6+
///Function type to empty clipboard
7+
pub type EmptyFn = fn() -> SysResult<()>;
8+
9+
///Clearing parameter
10+
pub trait Clearing {
11+
///Empty behavior definition
12+
const EMPTY_FN: EmptyFn;
13+
}
14+
15+
#[derive(Copy, Clone)]
16+
///Performs no clearing of clipboard
17+
pub struct NoClear;
18+
19+
fn noop() -> SysResult<()> {
20+
Ok(())
21+
}
22+
23+
impl Clearing for NoClear {
24+
const EMPTY_FN: EmptyFn = noop;
25+
}
26+
27+
#[derive(Copy, Clone)]
28+
///Performs clearing of clipboard before pasting
29+
pub struct DoClear;
30+
31+
impl Clearing for DoClear {
32+
const EMPTY_FN: EmptyFn = empty;
33+
}

src/raw.rs

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use crate::types::*;
1414
use crate::sys::*;
1515
use crate::utils::Buffer;
16+
use crate::options::{self, EmptyFn, Clearing};
1617

1718
const CBM_INIT: DWORD = 0x04;
1819
const BI_RGB: DWORD = 0;
@@ -383,8 +384,21 @@ pub fn get_html(format: u32, out: &mut alloc::vec::Vec<u8>) -> SysResult<usize>
383384
Ok(result)
384385
}
385386

387+
///Sets HTML using format code created by `register_raw_format` or `register_format` with argument `HTML Format`
388+
///
389+
///Allows to customize clipboard setting behavior
390+
///
391+
///- `C` - Specifies clearing behavior
392+
pub fn set_html_with<C: Clearing>(format: u32, html: &str, _is_clear: C) -> SysResult<()> {
393+
set_html_inner(format, html, C::EMPTY_FN)
394+
}
395+
386396
///Sets HTML using format code created by `register_raw_format` or `register_format` with argument `HTML Format`
387397
pub fn set_html(format: u32, html: &str) -> SysResult<()> {
398+
set_html_inner(format, html, options::NoClear::EMPTY_FN)
399+
}
400+
401+
fn set_html_inner(format: u32, html: &str, empty: EmptyFn) -> SysResult<()> {
388402
const VERSION_VALUE: &str = ":0.9";
389403
const HEADER_SIZE: usize = html::VERSION.len() + VERSION_VALUE.len() + html::NEWLINE.len()
390404
+ html::START_HTML.len() + html::LEN_SIZE + 1 + html::NEWLINE.len()
@@ -459,6 +473,7 @@ pub fn set_html(format: u32, html: &str) -> SysResult<()> {
459473
debug_assert_eq!(cursor, total_size);
460474
}
461475

476+
let _ = (empty)();
462477
if unsafe { !SetClipboardData(format, mem.get()).is_null() } {
463478
//SetClipboardData takes ownership
464479
mem.release();
@@ -468,18 +483,7 @@ pub fn set_html(format: u32, html: &str) -> SysResult<()> {
468483
}
469484
}
470485

471-
/// Copies raw bytes onto clipboard with specified `format`, returning whether it was successful.
472-
///
473-
/// This function empties the clipboard before setting the data.
474-
pub fn set(format: u32, data: &[u8]) -> SysResult<()> {
475-
let _ = empty();
476-
set_without_clear(format, data)
477-
}
478-
479-
/// Copies raw bytes onto the clipboard with the specified `format`, returning whether it was successful.
480-
///
481-
/// This function does not empty the clipboard before setting the data.
482-
pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> {
486+
fn set_inner(format: u32, data: &[u8], clear: EmptyFn) -> SysResult<()> {
483487
let size = data.len();
484488
if size == 0 {
485489
#[allow(clippy::unit_arg)]
@@ -493,6 +497,7 @@ pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> {
493497
unsafe { ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr() as _, size) };
494498
}
495499

500+
let _ = (clear)();
496501
if unsafe { !SetClipboardData(format, mem.get()).is_null() } {
497502
//SetClipboardData takes ownership
498503
mem.release();
@@ -501,6 +506,19 @@ pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> {
501506

502507
Err(ErrorCode::last_system())
503508
}
509+
/// Copies raw bytes onto clipboard with specified `format`, returning whether it was successful.
510+
///
511+
/// This function empties the clipboard before setting the data.
512+
pub fn set(format: u32, data: &[u8]) -> SysResult<()> {
513+
set_inner(format, data, options::DoClear::EMPTY_FN)
514+
}
515+
516+
/// Copies raw bytes onto the clipboard with the specified `format`, returning whether it was successful.
517+
///
518+
/// This function does not empty the clipboard before setting the data.
519+
pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> {
520+
set_inner(format, data, options::NoClear::EMPTY_FN)
521+
}
504522

505523
///Copies raw bytes from clipboard with specified `format`, appending to `out` buffer.
506524
///
@@ -535,9 +553,7 @@ pub fn get_string(out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
535553
Ok(result)
536554
}
537555

538-
///Copies unicode string onto clipboard, performing necessary conversions, returning true on
539-
///success.
540-
pub fn set_string(data: &str) -> SysResult<()> {
556+
fn set_string_inner(data: &str, clear: EmptyFn) -> SysResult<()> {
541557
let size = unsafe {
542558
MultiByteToWideChar(CP_UTF8, 0, data.as_ptr() as *const _, data.len() as _, ptr::null_mut(), 0)
543559
};
@@ -554,8 +570,7 @@ pub fn set_string(data: &str) -> SysResult<()> {
554570
}
555571
}
556572

557-
let _ = empty();
558-
573+
let _ = (clear)();
559574
if unsafe { !SetClipboardData(formats::CF_UNICODETEXT, mem.get()).is_null() } {
560575
//SetClipboardData takes ownership
561576
mem.release();
@@ -566,6 +581,24 @@ pub fn set_string(data: &str) -> SysResult<()> {
566581
Err(ErrorCode::last_system())
567582
}
568583

584+
#[inline(always)]
585+
///Copies unicode string onto clipboard, performing necessary conversions, returning true on
586+
///success.
587+
pub fn set_string(data: &str) -> SysResult<()> {
588+
set_string_inner(data, options::DoClear::EMPTY_FN)
589+
}
590+
591+
#[inline(always)]
592+
///Copies unicode string onto clipboard, performing necessary conversions, returning true on
593+
///success.
594+
///
595+
///Allows to customize clipboard setting behavior
596+
///
597+
///- `C` - Specifies clearing behavior
598+
pub fn set_string_with<C: Clearing>(data: &str, _is_clear: C) -> SysResult<()> {
599+
set_string_inner(data, C::EMPTY_FN)
600+
}
601+
569602
#[cfg(feature = "std")]
570603
///Retrieves file list from clipboard, appending each element to the provided storage.
571604
///
@@ -752,6 +785,24 @@ pub fn set_bitamp(data: &[u8]) -> SysResult<()> {
752785
///
753786
///Returns `ERROR_INCORRECT_SIZE` if size of data is not valid
754787
pub fn set_bitmap(data: &[u8]) -> SysResult<()> {
788+
//Bitmap format cannot really overlap with much so there is no risk of having non-empty clipboard
789+
//Also it is backward compatible beahvior.
790+
//To be changed in 6.x
791+
set_bitmap_inner(data, options::NoClear::EMPTY_FN)
792+
}
793+
794+
///Sets bitmap (header + RGB) onto clipboard, from raw bytes.
795+
///
796+
///Returns `ERROR_INCORRECT_SIZE` if size of data is not valid
797+
///
798+
///Allows to customize clipboard setting behavior
799+
///
800+
///- `C` - Specifies clearing behavior
801+
pub fn set_bitmap_with<C: Clearing>(data: &[u8], _is_clear: C) -> SysResult<()> {
802+
set_bitmap_inner(data, C::EMPTY_FN)
803+
}
804+
805+
fn set_bitmap_inner(data: &[u8], clear: EmptyFn) -> SysResult<()> {
755806
const FILE_HEADER_LEN: usize = mem::size_of::<BITMAPFILEHEADER>();
756807
const INFO_HEADER_LEN: usize = mem::size_of::<BITMAPINFOHEADER>();
757808

@@ -788,7 +839,7 @@ pub fn set_bitmap(data: &[u8]) -> SysResult<()> {
788839
return Err(ErrorCode::last_system());
789840
}
790841

791-
let _ = empty();
842+
let _ = (clear)();
792843
if unsafe { SetClipboardData(formats::CF_BITMAP, handle as _).is_null() } {
793844
return Err(ErrorCode::last_system());
794845
}
@@ -797,8 +848,20 @@ pub fn set_bitmap(data: &[u8]) -> SysResult<()> {
797848
}
798849

799850

851+
#[inline(always)]
800852
///Set list of file paths to clipboard.
801853
pub fn set_file_list(paths: &[impl AsRef<str>]) -> SysResult<()> {
854+
//See set_bitmap for reasoning of NoClear
855+
set_file_list_inner(paths, options::NoClear::EMPTY_FN)
856+
}
857+
858+
#[inline(always)]
859+
///Set list of file paths to clipboard.
860+
pub fn set_file_list_with<C: Clearing>(paths: &[impl AsRef<str>], _is_clear: C) -> SysResult<()> {
861+
set_file_list_inner(paths, C::EMPTY_FN)
862+
}
863+
864+
fn set_file_list_inner(paths: &[impl AsRef<str>], empty: EmptyFn) -> SysResult<()> {
802865
#[repr(C, packed(1))]
803866
pub struct DROPFILES {
804867
pub p_files: u32,
@@ -852,8 +915,7 @@ pub fn set_file_list(paths: &[impl AsRef<str>]) -> SysResult<()> {
852915
}
853916
}
854917

855-
let _ = empty();
856-
918+
let _ = (empty)();
857919
if unsafe { !SetClipboardData(formats::CF_HDROP, mem.get()).is_null() } {
858920
//SetClipboardData now has ownership of `mem`.
859921
mem.release();

tests/test_clip.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ macro_rules! run {
163163

164164
#[test]
165165
fn clipboard_should_work() {
166+
166167
run!(should_work_with_bitmap);
167168
assert!(is_format_avail(CF_BITMAP));
168169
run!(should_work_with_string);

0 commit comments

Comments
 (0)