1 //!Raw bindings to Windows clipboard.
2 //!
3 //!## General information
4 //!
5 //!All pre & post conditions are stated in description of functions.
6 //!
7 //!### Open clipboard
8 //! To access any information inside clipboard it is necessary to open it by means of
9 //! [open()](fn.open.html).
10 //!
11 //! After that Clipboard cannot be opened any more until [close()](fn.close.html) is called.
12 
13 use winapi::um::winuser::{OpenClipboard, CloseClipboard, EmptyClipboard, GetClipboardSequenceNumber, GetClipboardData, IsClipboardFormatAvailable, CountClipboardFormats, EnumClipboardFormats, GetClipboardFormatNameW, RegisterClipboardFormatW, SetClipboardData, GetDC, ReleaseDC, GetClipboardOwner};
14 use winapi::um::winbase::{GlobalSize, GlobalLock, GlobalUnlock};
15 use winapi::ctypes::{c_int, c_uint, c_void};
16 use winapi::um::stringapiset::{MultiByteToWideChar, WideCharToMultiByte};
17 use winapi::um::winnls::CP_UTF8;
18 use winapi::um::shellapi::{DragQueryFileW};
19 use winapi::um::wingdi::{GetObjectW, GetDIBits, CreateDIBitmap, BITMAP, BITMAPINFO, BITMAPINFOHEADER, RGBQUAD, BI_RGB, DIB_RGB_COLORS, BITMAPFILEHEADER, CBM_INIT};
20 use winapi::shared::windef::{HDC};
21 use winapi::shared::winerror::ERROR_INCORRECT_SIZE;
22 
23 use str_buf::StrBuf;
24 use error_code::SystemError;
25 
26 use core::{slice, mem, ptr, cmp};
27 use core::num::{NonZeroUsize, NonZeroU32};
28 
29 use alloc::string::String;
30 use alloc::borrow::ToOwned;
31 use alloc::format;
32 
33 use crate::{SysResult, formats};
34 use crate::utils::{RawMem};
35 
36 #[inline(always)]
free_dc(data: HDC)37 fn free_dc(data: HDC) {
38     unsafe {
39         ReleaseDC(ptr::null_mut(), data);
40     }
41 }
42 
43 #[inline(always)]
44 ///Opens clipboard.
45 ///
46 ///Wrapper around ```OpenClipboard```.
47 ///
48 ///# Pre-conditions:
49 ///
50 ///* Clipboard is not opened yet.
51 ///
52 ///# Post-conditions (if successful):
53 ///
54 ///* Clipboard can be accessed for read and write operations.
open() -> SysResult<()>55 pub fn open() -> SysResult<()> {
56     open_for(ptr::null_mut())
57 }
58 
59 #[inline]
60 ///Opens clipboard associating it with specified window handle.
61 ///
62 ///Unless [empty](fn.empty.html) is called, `owner` would be `None`.
63 ///
64 ///Wrapper around ```OpenClipboard```.
65 ///
66 ///# Pre-conditions:
67 ///
68 ///* Clipboard is not opened yet.
69 ///
70 ///# Post-conditions (if successful):
71 ///
72 ///* Clipboard can be accessed for read and write operations.
open_for(owner: winapi::shared::windef::HWND) -> SysResult<()>73 pub fn open_for(owner: winapi::shared::windef::HWND) -> SysResult<()> {
74     match unsafe { OpenClipboard(owner) } {
75         0 => Err(SystemError::last()),
76         _ => Ok(()),
77     }
78 }
79 
80 #[inline]
81 ///Closes clipboard.
82 ///
83 ///Wrapper around ```CloseClipboard```.
84 ///
85 ///# Pre-conditions:
86 ///
87 ///* [open()](fn.open.html) has been called.
close() -> SysResult<()>88 pub fn close() -> SysResult<()> {
89     match unsafe { CloseClipboard() } {
90         0 => Err(SystemError::last()),
91         _ => Ok(()),
92     }
93 }
94 
95 #[inline]
96 ///Empties clipboard.
97 ///
98 ///Wrapper around ```EmptyClipboard```.
99 ///
100 ///# Pre-conditions:
101 ///
102 ///* [open()](fn.open.html) has been called.
empty() -> SysResult<()>103 pub fn empty() -> SysResult<()> {
104     match unsafe { EmptyClipboard() } {
105         0 => Err(SystemError::last()),
106         _ => Ok(()),
107     }
108 }
109 
110 #[inline]
111 ///Retrieves clipboard sequence number.
112 ///
113 ///Wrapper around ```GetClipboardSequenceNumber```.
114 ///
115 ///# Returns:
116 ///
117 ///* ```Some``` Contains return value of ```GetClipboardSequenceNumber```.
118 ///* ```None``` In case if you do not have access. It means that zero is returned by system.
seq_num() -> Option<NonZeroU32>119 pub fn seq_num() -> Option<NonZeroU32> {
120     unsafe { NonZeroU32::new(GetClipboardSequenceNumber()) }
121 }
122 
123 #[inline]
124 ///Retrieves size of clipboard data for specified format.
125 ///
126 ///# Pre-conditions:
127 ///
128 ///* [open()](fn.open.html) has been called.
129 ///
130 ///# Returns:
131 ///
132 ///Size in bytes if format presents on clipboard.
133 ///
134 ///# Unsafety:
135 ///
136 ///In some cases, clipboard content might be so invalid that it crashes on `GlobalSize` (e.g.
137 ///Bitmap)
138 ///
139 ///Due to that function is marked as unsafe
size_unsafe(format: u32) -> Option<NonZeroUsize>140 pub unsafe fn size_unsafe(format: u32) -> Option<NonZeroUsize> {
141     let clipboard_data = GetClipboardData(format);
142 
143     match clipboard_data.is_null() {
144         false => NonZeroUsize::new(GlobalSize(clipboard_data) as usize),
145         true => None,
146     }
147 }
148 
149 #[inline]
150 ///Retrieves size of clipboard data for specified format.
151 ///
152 ///# Pre-conditions:
153 ///
154 ///* [open()](fn.open.html) has been called.
155 ///
156 ///# Returns:
157 ///
158 ///Size in bytes if format is presents on clipboard.
size(format: u32) -> Option<NonZeroUsize>159 pub fn size(format: u32) -> Option<NonZeroUsize> {
160     let clipboard_data = unsafe {GetClipboardData(format)};
161 
162     if clipboard_data.is_null() {
163         return None
164     }
165 
166     unsafe {
167         if GlobalLock(clipboard_data).is_null() {
168             return None;
169         }
170 
171         let result = NonZeroUsize::new(GlobalSize(clipboard_data) as usize);
172 
173         GlobalUnlock(clipboard_data);
174 
175         result
176     }
177 }
178 
179 #[inline(always)]
180 ///Retrieves raw pointer to clipboard data.
181 ///
182 ///Wrapper around ```GetClipboardData```.
183 ///
184 ///# Pre-conditions:
185 ///
186 ///* [open()](fn.open.html) has been called.
get_clipboard_data(format: c_uint) -> SysResult<ptr::NonNull<c_void>>187 pub fn get_clipboard_data(format: c_uint) -> SysResult<ptr::NonNull<c_void>> {
188     let ptr = unsafe { GetClipboardData(format) as *mut c_void };
189     match ptr::NonNull::new(ptr) {
190         Some(ptr) => Ok(ptr),
191         None => Err(SystemError::last()),
192     }
193 }
194 
195 #[inline(always)]
196 ///Determines whenever provided clipboard format is available on clipboard or not.
is_format_avail(format: c_uint) -> bool197 pub fn is_format_avail(format: c_uint) -> bool {
198     unsafe { IsClipboardFormatAvailable(format) != 0 }
199 }
200 
201 #[inline]
202 ///Retrieves number of currently available formats on clipboard.
203 ///
204 ///Returns `None` if `CountClipboardFormats` failed.
count_formats() -> Option<usize>205 pub fn count_formats() -> Option<usize> {
206     let result = unsafe { CountClipboardFormats() };
207 
208     if result == 0 {
209         if !SystemError::last().is_zero() {
210             return None
211         }
212     }
213 
214     Some(result as usize)
215 }
216 
217 ///Copies raw bytes from clipboard with specified `format`
218 ///
219 ///Returns number of copied bytes on success, otherwise 0.
220 ///
221 ///It is safe to pass uninit memory
get(format: u32, out: &mut [u8]) -> SysResult<usize>222 pub fn get(format: u32, out: &mut [u8]) -> SysResult<usize> {
223     let size = out.len();
224     debug_assert!(size > 0);
225     let out_ptr = out.as_mut_ptr();
226 
227     let ptr = RawMem::from_borrowed(get_clipboard_data(format)?);
228 
229     let result = unsafe {
230         let (data_ptr, _lock) = ptr.lock()?;
231         let data_size = cmp::min(GlobalSize(ptr.get()) as usize, size);
232         ptr::copy_nonoverlapping(data_ptr.as_ptr() as *const u8, out_ptr, data_size);
233         data_size
234     };
235 
236     Ok(result)
237 }
238 
239 ///Copies raw bytes from clipboard with specified `format`, appending to `out` buffer.
240 ///
241 ///Returns number of copied bytes on success, otherwise 0.
get_vec(format: u32, out: &mut alloc::vec::Vec<u8>) -> SysResult<usize>242 pub fn get_vec(format: u32, out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
243     let ptr = RawMem::from_borrowed(get_clipboard_data(format)?);
244 
245     let result = unsafe {
246         let (data_ptr, _lock) = ptr.lock()?;
247         let data_size = GlobalSize(ptr.get()) as usize;
248 
249         out.reserve(data_size as usize);
250         let storage_cursor = out.len();
251         let storage_ptr = out.as_mut_ptr().add(out.len()) as *mut _;
252 
253         ptr::copy_nonoverlapping(data_ptr.as_ptr() as *const u8, storage_ptr, data_size);
254         out.set_len(storage_cursor + data_size as usize);
255 
256         data_size
257     };
258 
259     Ok(result)
260 }
261 
262 ///Copies raw bytes onto clipboard with specified `format`, returning whether it was successful.
set(format: u32, data: &[u8]) -> SysResult<()>263 pub fn set(format: u32, data: &[u8]) -> SysResult<()> {
264     let size = data.len();
265     debug_assert!(size > 0);
266 
267     let mem = RawMem::new_global_mem(size)?;
268 
269     {
270         let (ptr, _lock) = mem.lock()?;
271         unsafe { ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr() as _, size) };
272     }
273 
274     let _ = empty();
275 
276     if unsafe { !SetClipboardData(format, mem.get()).is_null() } {
277         //SetClipboardData takes ownership
278         mem.release();
279         return Ok(());
280     }
281 
282     Err(error_code::SystemError::last())
283 }
284 
285 ///Copies raw bytes from clipboard with specified `format`, appending to `out` buffer.
286 ///
287 ///Returns number of copied bytes on success, otherwise 0.
get_string(out: &mut alloc::vec::Vec<u8>) -> SysResult<usize>288 pub fn get_string(out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
289     let ptr = RawMem::from_borrowed(get_clipboard_data(formats::CF_UNICODETEXT)?);
290 
291     let result = unsafe {
292         let (data_ptr, _lock) = ptr.lock()?;
293         let data_size = GlobalSize(ptr.get()) as usize / mem::size_of::<u16>();
294         let storage_req_size = WideCharToMultiByte(CP_UTF8, 0, data_ptr.as_ptr() as _, data_size as _, ptr::null_mut(), 0, ptr::null(), ptr::null_mut());
295 
296         if storage_req_size == 0 {
297             return Err(SystemError::last());
298         }
299 
300         let storage_cursor = out.len();
301         out.reserve(storage_req_size as usize);
302         let storage_ptr = out.as_mut_ptr().add(storage_cursor) as *mut _;
303         WideCharToMultiByte(CP_UTF8, 0, data_ptr.as_ptr() as _, data_size as _, storage_ptr, storage_req_size, ptr::null(), ptr::null_mut());
304         out.set_len(storage_cursor + storage_req_size as usize);
305 
306         //It seems WinAPI always supposed to have at the end null char.
307         //But just to be safe let's check for it and only then remove.
308         if let Some(null_idx) = out.iter().skip(storage_cursor).position(|b| *b == b'\0') {
309             out.set_len(storage_cursor + null_idx);
310         }
311 
312         out.len() - storage_cursor
313     };
314 
315     Ok(result)
316 }
317 
318 ///Copies unicode string onto clipboard, performing necessary conversions, returning true on
319 ///success.
set_string(data: &str) -> SysResult<()>320 pub fn set_string(data: &str) -> SysResult<()> {
321     debug_assert!(data.len() > 0);
322 
323     let size = unsafe {
324         MultiByteToWideChar(CP_UTF8, 0, data.as_ptr() as *const _, data.len() as _, ptr::null_mut(), 0)
325     };
326 
327     if size != 0 {
328         let mem = RawMem::new_global_mem((mem::size_of::<u16>() * (size as usize + 1)) as _)?;
329         {
330             let (ptr, _lock) = mem.lock()?;
331             let ptr = ptr.as_ptr() as *mut u16;
332             unsafe {
333                 MultiByteToWideChar(CP_UTF8, 0, data.as_ptr() as *const _, data.len() as _, ptr, size);
334                 ptr::write(ptr.offset(size as isize), 0);
335             }
336         }
337 
338         let _ = empty();
339 
340         if unsafe { !SetClipboardData(formats::CF_UNICODETEXT, mem.get()).is_null() } {
341             //SetClipboardData takes ownership
342             mem.release();
343             return Ok(());
344         }
345     }
346 
347     Err(error_code::SystemError::last())
348 }
349 
350 ///Retrieves file list from clipboard, appending each element to the provided storage.
351 ///
352 ///Returns number of appended file names.
get_file_list(out: &mut alloc::vec::Vec<alloc::string::String>) -> SysResult<usize>353 pub fn get_file_list(out: &mut alloc::vec::Vec<alloc::string::String>) -> SysResult<usize> {
354     let clipboard_data = RawMem::from_borrowed(get_clipboard_data(formats::CF_HDROP)?);
355 
356     let (_data_ptr, _lock) = clipboard_data.lock()?;
357 
358     let num_files = unsafe { DragQueryFileW(clipboard_data.get() as _, u32::MAX, ptr::null_mut(), 0) };
359     out.reserve(num_files as usize);
360 
361     let mut buffer = alloc::vec::Vec::new();
362 
363     for idx in 0..num_files {
364         let required_size_no_null = unsafe { DragQueryFileW(clipboard_data.get() as _, idx, ptr::null_mut(), 0) };
365         if required_size_no_null == 0 {
366             return Err(SystemError::last());
367         }
368 
369         let required_size = required_size_no_null + 1;
370         buffer.reserve(required_size as usize);
371 
372         if unsafe { DragQueryFileW(clipboard_data.get() as _, idx, buffer.as_mut_ptr(), required_size) == 0 } {
373             return Err(SystemError::last());
374         }
375 
376         unsafe {
377             buffer.set_len(required_size_no_null as usize);
378         }
379         out.push(alloc::string::String::from_utf16_lossy(&buffer));
380     }
381 
382     Ok(num_files as usize)
383 }
384 
385 ///Reads bitmap image, appending image to the `out` vector and returning number of bytes read on
386 ///success.
387 ///
388 ///Output will contain header following by RGB
get_bitmap(out: &mut alloc::vec::Vec<u8>) -> SysResult<usize>389 pub fn get_bitmap(out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
390     let clipboard_data = get_clipboard_data(formats::CF_BITMAP)?;
391 
392     //Thanks @matheuslessarodrigues
393     let mut bitmap = BITMAP {
394         bmType: 0,
395         bmWidth: 0,
396         bmHeight: 0,
397         bmWidthBytes: 0,
398         bmPlanes: 0,
399         bmBitsPixel: 0,
400         bmBits: ptr::null_mut(),
401     };
402 
403     if unsafe { GetObjectW(clipboard_data.as_ptr(), mem::size_of::<BITMAP>() as _, &mut bitmap as *mut BITMAP as _) } == 0 {
404         return Err(SystemError::last());
405     }
406 
407     let clr_bits = bitmap.bmPlanes * bitmap.bmBitsPixel;
408     let clr_bits = if clr_bits == 1 {
409         1
410     } else if clr_bits <= 4 {
411         4
412     } else if clr_bits <= 8 {
413         8
414     } else if clr_bits <= 16 {
415         16
416     } else if clr_bits <= 24 {
417         24
418     } else {
419         32
420     };
421 
422     let header_storage = RawMem::new_rust_mem(if clr_bits < 24 {
423         mem::size_of::<BITMAPINFOHEADER>() + mem::size_of::<RGBQUAD>() * (1 << clr_bits)
424     } else {
425         mem::size_of::<BITMAPINFOHEADER>()
426     });
427 
428     let header = unsafe {
429         &mut *(header_storage.get() as *mut BITMAPINFO)
430     };
431 
432     header.bmiHeader.biSize = mem::size_of::<BITMAPINFOHEADER>() as _;
433     header.bmiHeader.biWidth = bitmap.bmWidth;
434     header.bmiHeader.biHeight = bitmap.bmHeight;
435     header.bmiHeader.biPlanes = bitmap.bmPlanes;
436     header.bmiHeader.biBitCount = bitmap.bmBitsPixel;
437     header.bmiHeader.biCompression = BI_RGB;
438     if clr_bits < 24 {
439         header.bmiHeader.biClrUsed = 1 << clr_bits;
440     }
441 
442     header.bmiHeader.biSizeImage = ((((header.bmiHeader.biWidth * clr_bits + 31) & !31) / 8) * header.bmiHeader.biHeight) as _;
443     header.bmiHeader.biClrImportant = 0;
444 
445     let img_size = header.bmiHeader.biSizeImage as usize;
446     let out_before = out.len();
447 
448     let dc = crate::utils::Scope(unsafe { GetDC(ptr::null_mut()) }, free_dc);
449     let mut buffer = alloc::vec::Vec::new();
450     buffer.resize(img_size, 0u8);
451 
452     if unsafe { GetDIBits(dc.0, clipboard_data.as_ptr() as _, 0, bitmap.bmHeight as _, buffer.as_mut_ptr() as _, header_storage.get() as _, DIB_RGB_COLORS) } == 0 {
453         return Err(SystemError::last());
454     }
455 
456     //Write header
457     out.extend_from_slice(&u16::to_le_bytes(0x4d42));
458     out.extend_from_slice(&u32::to_le_bytes(mem::size_of::<BITMAPFILEHEADER>() as u32 + header.bmiHeader.biSize + header.bmiHeader.biClrUsed * mem::size_of::<RGBQUAD>() as u32 + header.bmiHeader.biSizeImage));
459     out.extend_from_slice(&u32::to_le_bytes(0)); //2 * u16 of 0
460     out.extend_from_slice(&u32::to_le_bytes(mem::size_of::<BITMAPFILEHEADER>() as u32 + header.bmiHeader.biSize + header.bmiHeader.biClrUsed * mem::size_of::<RGBQUAD>() as u32));
461 
462     out.extend_from_slice(&header.bmiHeader.biSize.to_le_bytes());
463     out.extend_from_slice(&header.bmiHeader.biWidth.to_le_bytes());
464     out.extend_from_slice(&header.bmiHeader.biHeight.to_le_bytes());
465     out.extend_from_slice(&header.bmiHeader.biPlanes.to_le_bytes());
466     out.extend_from_slice(&header.bmiHeader.biBitCount.to_le_bytes());
467     out.extend_from_slice(&header.bmiHeader.biCompression.to_le_bytes());
468     out.extend_from_slice(&header.bmiHeader.biSizeImage.to_le_bytes());
469     out.extend_from_slice(&header.bmiHeader.biXPelsPerMeter.to_le_bytes());
470     out.extend_from_slice(&header.bmiHeader.biYPelsPerMeter.to_le_bytes());
471     out.extend_from_slice(&header.bmiHeader.biClrUsed.to_le_bytes());
472     out.extend_from_slice(&header.bmiHeader.biClrImportant.to_le_bytes());
473 
474     for color in unsafe { slice::from_raw_parts(header.bmiColors.as_ptr(), header.bmiHeader.biClrUsed as _) } {
475         out.push(color.rgbBlue);
476         out.push(color.rgbGreen);
477         out.push(color.rgbRed);
478         out.push(color.rgbReserved);
479     }
480 
481     out.extend_from_slice(&buffer);
482 
483     Ok(out.len() - out_before)
484 }
485 
486 #[inline(always)]
487 #[doc(hidden)]
set_bitamp(data: &[u8]) -> SysResult<()>488 pub fn set_bitamp(data: &[u8]) -> SysResult<()> {
489     set_bitmap(data)
490 }
491 
492 ///Sets bitmap (header + RGB) onto clipboard, from raw bytes.
493 ///
494 ///Returns `ERROR_INCORRECT_SIZE` if size of data is not valid
set_bitmap(data: &[u8]) -> SysResult<()>495 pub fn set_bitmap(data: &[u8]) -> SysResult<()> {
496     const FILE_HEADER_LEN: usize = mem::size_of::<BITMAPFILEHEADER>();
497     const INFO_HEADER_LEN: usize = mem::size_of::<BITMAPINFOHEADER>();
498 
499     if data.len() <= (FILE_HEADER_LEN + INFO_HEADER_LEN) {
500         return Err(SystemError::new(ERROR_INCORRECT_SIZE as _));
501     }
502 
503     let mut file_header = mem::MaybeUninit::<BITMAPFILEHEADER>::uninit();
504     let mut info_header = mem::MaybeUninit::<BITMAPINFOHEADER>::uninit();
505 
506     let (file_header, info_header) = unsafe {
507         ptr::copy_nonoverlapping(data.as_ptr(), file_header.as_mut_ptr() as _, FILE_HEADER_LEN);
508         ptr::copy_nonoverlapping(data.as_ptr().add(FILE_HEADER_LEN), info_header.as_mut_ptr() as _, INFO_HEADER_LEN);
509         (file_header.assume_init(), info_header.assume_init())
510     };
511 
512     if data.len() <= file_header.bfOffBits as usize {
513         return Err(SystemError::new(ERROR_INCORRECT_SIZE as _));
514     }
515 
516     let bitmap = &data[file_header.bfOffBits as _..];
517 
518     if bitmap.len() < info_header.biSizeImage as usize {
519         return Err(SystemError::new(ERROR_INCORRECT_SIZE as _));
520     }
521 
522     let dc = crate::utils::Scope(unsafe { GetDC(ptr::null_mut()) }, free_dc);
523 
524     let handle = unsafe {
525         CreateDIBitmap(dc.0, &info_header as _, CBM_INIT, bitmap.as_ptr() as _, &info_header as *const _ as *const BITMAPINFO, DIB_RGB_COLORS)
526     };
527 
528     if handle.is_null() {
529         return Err(SystemError::last());
530     }
531 
532     let _ = empty();
533     if unsafe { SetClipboardData(formats::CF_BITMAP, handle as _).is_null() } {
534         return Err(SystemError::last());
535     }
536 
537     Ok(())
538 }
539 
540 ///Enumerator over available clipboard formats.
541 ///
542 ///# Pre-conditions:
543 ///
544 ///* [open()](fn.open.html) has been called.
545 pub struct EnumFormats {
546     idx: u32
547 }
548 
549 impl EnumFormats {
550     /// Constructs enumerator over all available formats.
new() -> EnumFormats551     pub fn new() -> EnumFormats {
552         EnumFormats { idx: 0 }
553     }
554 
555     /// Constructs enumerator that starts from format.
from(format: u32) -> EnumFormats556     pub fn from(format: u32) -> EnumFormats {
557         EnumFormats { idx: format }
558     }
559 
560     /// Resets enumerator to list all available formats.
reset(&mut self) -> &EnumFormats561     pub fn reset(&mut self) -> &EnumFormats {
562         self.idx = 0;
563         self
564     }
565 }
566 
567 impl Iterator for EnumFormats {
568     type Item = u32;
569 
570     /// Returns next format on clipboard.
571     ///
572     /// In case of failure (e.g. clipboard is closed) returns `None`.
next(&mut self) -> Option<u32>573     fn next(&mut self) -> Option<u32> {
574         self.idx = unsafe { EnumClipboardFormats(self.idx) };
575 
576         if self.idx == 0 {
577             None
578         } else {
579             Some(self.idx)
580         }
581     }
582 
583     /// Relies on `count_formats` so it is only reliable
584     /// when hinting size for enumeration of all formats.
585     ///
586     /// Doesn't require opened clipboard.
size_hint(&self) -> (usize, Option<usize>)587     fn size_hint(&self) -> (usize, Option<usize>) {
588         (0, count_formats())
589     }
590 }
591 
592 macro_rules! match_format_name_big {
593     ( $name:expr, $( $f:ident ),* ) => {
594         match $name {
595             $( formats::$f => Some(stringify!($f).to_owned()),)*
596             formats::CF_GDIOBJFIRST ..= formats::CF_GDIOBJLAST => Some(format!("CF_GDIOBJ{}", $name - formats::CF_GDIOBJFIRST)),
597             formats::CF_PRIVATEFIRST ..= formats::CF_PRIVATELAST => Some(format!("CF_PRIVATE{}", $name - formats::CF_PRIVATEFIRST)),
598             _ => {
599                 let format_buff = [0u16; 256];
600                 unsafe {
601                     let buff_p = format_buff.as_ptr() as *mut u16;
602 
603                     match GetClipboardFormatNameW($name, buff_p, format_buff.len() as c_int) {
604                         0 => None,
605                         size => Some(String::from_utf16_lossy(&format_buff[..size as usize])),
606                     }
607                 }
608             }
609         }
610     }
611 }
612 
613 macro_rules! match_format_name {
614     ( $name:expr, $( $f:ident ),* ) => {
615         use core::fmt::Write;
616         let mut result = StrBuf::<[u8; 52]>::new();
617 
618         match $name {
619             $( formats::$f => {
620                 let _ = result.push_str(stringify!($f));
621             },)*
622             formats::CF_GDIOBJFIRST ..= formats::CF_GDIOBJLAST => {
623                 let _ = write!(result, "CF_GDIOBJ{}", $name - formats::CF_GDIOBJFIRST);
624             },
625             formats::CF_PRIVATEFIRST ..= formats::CF_PRIVATELAST => {
626                 let _ = write!(result, "CF_PRIVATE{}", $name - formats::CF_PRIVATEFIRST);
627             },
628             _ => {
629                 let mut format_buff = [0u16; 52];
630                 unsafe {
631                     let buff_p = format_buff.as_mut_ptr() as *mut u16;
632                     match GetClipboardFormatNameW($name, buff_p, format_buff.len() as c_int) {
633                         0 => return None,
634                         len => match WideCharToMultiByte(winapi::um::winnls::CP_UTF8, 0, format_buff.as_ptr(), len, result.as_ptr() as *mut i8, result.remaining() as i32, ptr::null(), ptr::null_mut()) {
635                             0 => return None,
636                             len => result.set_len(len as u8),
637                         }
638                     }
639                 }
640             }
641         }
642 
643         return Some(result)
644     }
645 }
646 
647 ///Returns format name based on it's code.
648 ///
649 ///# Parameters:
650 ///
651 ///* ```format``` clipboard format code.
652 ///
653 ///# Return result:
654 ///
655 ///* ```Some``` Name of valid format.
656 ///* ```None``` Format is invalid or doesn't exist.
format_name(format: u32) -> Option<StrBuf<[u8; 52]>>657 pub fn format_name(format: u32) -> Option<StrBuf<[u8; 52]>> {
658     match_format_name!(format,
659                        CF_BITMAP,
660                        CF_DIB,
661                        CF_DIBV5,
662                        CF_DIF,
663                        CF_DSPBITMAP,
664                        CF_DSPENHMETAFILE,
665                        CF_DSPMETAFILEPICT,
666                        CF_DSPTEXT,
667                        CF_ENHMETAFILE,
668                        CF_HDROP,
669                        CF_LOCALE,
670                        CF_METAFILEPICT,
671                        CF_OEMTEXT,
672                        CF_OWNERDISPLAY,
673                        CF_PALETTE,
674                        CF_PENDATA,
675                        CF_RIFF,
676                        CF_SYLK,
677                        CF_TEXT,
678                        CF_WAVE,
679                        CF_TIFF,
680                        CF_UNICODETEXT);
681 }
682 
683 ///Returns format name based on it's code (allocating variant suitable for big names)
684 ///
685 ///# Parameters:
686 ///
687 ///* ```format``` clipboard format code.
688 ///
689 ///# Return result:
690 ///
691 ///* ```Some``` Name of valid format.
692 ///* ```None``` Format is invalid or doesn't exist.
format_name_big(format: u32) -> Option<String>693 pub fn format_name_big(format: u32) -> Option<String> {
694     match_format_name_big!(format,
695                            CF_BITMAP,
696                            CF_DIB,
697                            CF_DIBV5,
698                            CF_DIF,
699                            CF_DSPBITMAP,
700                            CF_DSPENHMETAFILE,
701                            CF_DSPMETAFILEPICT,
702                            CF_DSPTEXT,
703                            CF_ENHMETAFILE,
704                            CF_HDROP,
705                            CF_LOCALE,
706                            CF_METAFILEPICT,
707                            CF_OEMTEXT,
708                            CF_OWNERDISPLAY,
709                            CF_PALETTE,
710                            CF_PENDATA,
711                            CF_RIFF,
712                            CF_SYLK,
713                            CF_TEXT,
714                            CF_WAVE,
715                            CF_TIFF,
716                            CF_UNICODETEXT)
717 }
718 
719 #[inline]
720 ///Registers a new clipboard format with specified name as C wide string (meaning it must have null
721 ///char at the end).
722 ///
723 ///# Returns:
724 ///
725 ///Newly registered format identifier, if successful.
726 ///
727 ///# Note:
728 ///
729 ///Custom format identifier is in range `0xC000...0xFFFF`.
register_raw_format(name: &[u16]) -> Option<NonZeroU32>730 pub unsafe fn register_raw_format(name: &[u16]) -> Option<NonZeroU32> {
731     debug_assert_eq!(name[name.len()-1], b'\0' as u16);
732     NonZeroU32::new(RegisterClipboardFormatW(name.as_ptr()) )
733 }
734 
735 ///Registers a new clipboard format with specified name.
736 ///
737 ///# Returns:
738 ///
739 ///Newly registered format identifier, if successful.
740 ///
741 ///# Note:
742 ///
743 ///Custom format identifier is in range `0xC000...0xFFFF`.
register_format(name: &str) -> Option<NonZeroU32>744 pub fn register_format(name: &str) -> Option<NonZeroU32> {
745     let size = unsafe {
746         MultiByteToWideChar(CP_UTF8, 0, name.as_ptr() as *const _, name.len() as c_int, ptr::null_mut(), 0)
747     };
748 
749     if size == 0 {
750         return None;
751     }
752 
753     if size > 52 {
754         let mut buffer = alloc::vec::Vec::with_capacity(size as usize);
755         let size = unsafe {
756             MultiByteToWideChar(CP_UTF8, 0, name.as_ptr() as *const _, name.len() as c_int, buffer.as_mut_ptr(), size)
757         };
758         unsafe {
759             buffer.set_len(size as usize);
760             buffer.push(0);
761             register_raw_format(&buffer)
762         }
763     } else {
764         let mut buffer = mem::MaybeUninit::<[u16; 52]>::uninit();
765         let size = unsafe {
766             MultiByteToWideChar(CP_UTF8, 0, name.as_ptr() as *const _, name.len() as c_int, buffer.as_mut_ptr() as *mut u16, 51)
767         };
768         unsafe {
769             ptr::write((buffer.as_mut_ptr() as *mut u16).offset(size as isize), 0);
770             register_raw_format(slice::from_raw_parts(buffer.as_ptr() as *const u16, size as usize + 1))
771         }
772     }
773 }
774 
775 #[inline(always)]
776 ///Retrieves the window handle of the current owner of the clipboard.
777 ///
778 ///Returns `None` if clipboard is not owned.
get_owner() -> Option<ptr::NonNull::<winapi::shared::windef::HWND__>>779 pub fn get_owner() -> Option<ptr::NonNull::<winapi::shared::windef::HWND__>> {
780     ptr::NonNull::new(unsafe {
781         GetClipboardOwner()
782     })
783 }
784