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