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;
const DIB_RGB_COLORS: DWORD = 0;
const ERROR_INCORRECT_SIZE: DWORD = 1462;
const CP_UTF8: DWORD = 65001;
use error_code::ErrorCode;
use core::{slice, mem, ptr, cmp, str, hint};
use core::num::{NonZeroUsize, NonZeroU32};
use alloc::string::String;
use alloc::borrow::ToOwned;
use alloc::format;
use crate::{SysResult, html, formats};
use crate::utils::{unlikely_empty_size_result, RawMem};
#[cold]
#[inline(never)]
fn invalid_data() -> ErrorCode {
ErrorCode::new_system(13)
}
#[inline(always)]
fn free_dc(data: HDC) {
unsafe {
ReleaseDC(ptr::null_mut(), data);
}
}
struct AnonymousTokenImpersonator {
must_revert: bool,
}
impl AnonymousTokenImpersonator {
#[inline]
pub fn new() -> Self {
Self {
must_revert: unsafe { ImpersonateAnonymousToken(GetCurrentThread()) != 0 },
}
}
}
impl Drop for AnonymousTokenImpersonator {
#[inline]
fn drop(&mut self) {
if self.must_revert {
unsafe {
RevertToSelf();
}
}
}
}
#[inline(always)]
pub fn open() -> SysResult<()> {
open_for(ptr::null_mut())
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[inline]
pub fn open_for(owner: HWND) -> SysResult<()> {
match unsafe { OpenClipboard(owner) } {
0 => Err(ErrorCode::last_system()),
_ => Ok(()),
}
}
#[inline]
pub fn close() -> SysResult<()> {
let _guard = AnonymousTokenImpersonator::new();
match unsafe { CloseClipboard() } {
0 => Err(ErrorCode::last_system()),
_ => Ok(()),
}
}
#[inline]
pub fn empty() -> SysResult<()> {
match unsafe { EmptyClipboard() } {
0 => Err(ErrorCode::last_system()),
_ => Ok(()),
}
}
#[inline]
pub fn seq_num() -> Option<NonZeroU32> {
unsafe { NonZeroU32::new(GetClipboardSequenceNumber()) }
}
#[inline]
pub unsafe fn size_unsafe(format: u32) -> Option<NonZeroUsize> {
let clipboard_data = GetClipboardData(format);
match clipboard_data.is_null() {
false => NonZeroUsize::new(GlobalSize(clipboard_data) as usize),
true => None,
}
}
#[inline]
pub fn size(format: u32) -> Option<NonZeroUsize> {
let clipboard_data = unsafe {GetClipboardData(format)};
if clipboard_data.is_null() {
return None
}
unsafe {
if GlobalLock(clipboard_data).is_null() {
return None;
}
let result = NonZeroUsize::new(GlobalSize(clipboard_data) as usize);
GlobalUnlock(clipboard_data);
result
}
}
#[inline(always)]
pub fn get_clipboard_data(format: c_uint) -> SysResult<ptr::NonNull<c_void>> {
let ptr = unsafe {
GetClipboardData(format)
};
match ptr::NonNull::new(ptr) {
Some(ptr) => Ok(ptr),
None => Err(ErrorCode::last_system()),
}
}
#[inline(always)]
pub fn is_format_avail(format: c_uint) -> bool {
unsafe { IsClipboardFormatAvailable(format) != 0 }
}
#[inline(always)]
pub fn which_format_avail(formats: &[c_uint]) -> Option<NonZeroU32> {
let result = unsafe {
GetPriorityClipboardFormat(formats.as_ptr(), formats.len() as _)
};
if result < 0 {
None
} else {
NonZeroU32::new(result as _)
}
}
#[inline]
pub fn count_formats() -> Option<usize> {
let result = unsafe { CountClipboardFormats() };
if result == 0 {
if ErrorCode::last_system().raw_code() != 0 {
return None
}
}
Some(result as usize)
}
pub fn get(format: u32, out: &mut [u8]) -> SysResult<usize> {
let size = out.len();
if size == 0 {
return Ok(unlikely_empty_size_result());
}
let out_ptr = out.as_mut_ptr();
let ptr = RawMem::from_borrowed(get_clipboard_data(format)?);
let result = unsafe {
let (data_ptr, _lock) = ptr.lock()?;
let data_size = cmp::min(GlobalSize(ptr.get()) as usize, size);
ptr::copy_nonoverlapping(data_ptr.as_ptr() as *const u8, out_ptr, data_size);
data_size
};
Ok(result)
}
pub fn get_vec(format: u32, out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
let ptr = RawMem::from_borrowed(get_clipboard_data(format)?);
let result = unsafe {
let (data_ptr, _lock) = ptr.lock()?;
let data_size = GlobalSize(ptr.get()) as usize;
out.reserve(data_size as usize);
let storage_cursor = out.len();
let storage_ptr = out.as_mut_ptr().add(out.len()) as *mut _;
ptr::copy_nonoverlapping(data_ptr.as_ptr() as *const u8, storage_ptr, data_size);
out.set_len(storage_cursor + data_size as usize);
data_size
};
Ok(result)
}
pub fn get_html(format: u32, out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
let ptr = RawMem::from_borrowed(get_clipboard_data(format)?);
let result = unsafe {
let (data_ptr, _lock) = ptr.lock()?;
let data_size = GlobalSize(ptr.get()) as usize;
let data = match str::from_utf8(slice::from_raw_parts(data_ptr.as_ptr() as *const u8, data_size)) {
Ok(data) => data,
Err(_) => return Err(invalid_data()),
};
let mut start_idx = 0usize;
let mut end_idx = data.len();
for line in data.lines() {
let mut split = line.split(html::SEP);
let key = match split.next() {
Some(key) => key,
None => hint::unreachable_unchecked(),
};
let value = match split.next() {
Some(value) => value,
None => break
};
match key {
html::START_FRAGMENT => match value.trim_start_matches('0').parse() {
Ok(value) => {
start_idx = value;
continue;
}
Err(_) => break,
},
html::END_FRAGMENT => match value.trim_start_matches('0').parse() {
Ok(value) => {
end_idx = value;
continue;
}
Err(_) => break,
},
_ => continue,
}
}
let size = match end_idx.checked_sub(start_idx) {
Some(size) => size,
None => return Err(invalid_data()),
};
if size > data_size {
return Err(invalid_data())
}
out.reserve(size);
let out_cursor = out.len();
ptr::copy_nonoverlapping(data.as_ptr().add(start_idx), out.spare_capacity_mut().as_mut_ptr().add(out_cursor) as _, size);
out.set_len(out_cursor + size);
size
};
Ok(result)
}
pub fn set_html_with<C: Clearing>(format: u32, html: &str, _is_clear: C) -> SysResult<()> {
set_html_inner(format, html, C::EMPTY_FN)
}
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()
+ html::END_HTML.len() + html::LEN_SIZE + 1 + html::NEWLINE.len()
+ html::START_FRAGMENT.len() + html::LEN_SIZE + 1 + html::NEWLINE.len()
+ html::END_FRAGMENT.len() + html::LEN_SIZE + 1 + html::NEWLINE.len();
const FRAGMENT_OFFSET: usize = HEADER_SIZE + html::BODY_HEADER.len();
let total_size = FRAGMENT_OFFSET + html::BODY_FOOTER.len() + html.len();
let mut len_buffer = html::LengthBuffer::new();
let mem = RawMem::new_global_mem(total_size)?;
unsafe {
use core::fmt::Write;
let (ptr, _lock) = mem.lock()?;
let out = slice::from_raw_parts_mut(ptr.as_ptr() as *mut mem::MaybeUninit<u8>, total_size);
let mut cursor = 0;
macro_rules! write_out {
($input:expr) => {
let input = $input;
ptr::copy_nonoverlapping(input.as_ptr() as *const u8, out.as_mut_ptr().add(cursor) as _, input.len());
cursor += input.len();
};
}
write_out!(html::VERSION);
write_out!(VERSION_VALUE);
write_out!(html::NEWLINE);
let _ = write!(&mut len_buffer, "{:0>10}", HEADER_SIZE);
write_out!(html::START_HTML);
write_out!([html::SEP as u8]);
write_out!(&len_buffer);
write_out!(html::NEWLINE);
let _ = write!(&mut len_buffer, "{:0>10}", total_size);
write_out!(html::END_HTML);
write_out!([html::SEP as u8]);
write_out!(&len_buffer);
write_out!(html::NEWLINE);
let _ = write!(&mut len_buffer, "{:0>10}", FRAGMENT_OFFSET);
write_out!(html::START_FRAGMENT);
write_out!([html::SEP as u8]);
write_out!(&len_buffer);
write_out!(html::NEWLINE);
let _ = write!(&mut len_buffer, "{:0>10}", total_size - html::BODY_FOOTER.len());
write_out!(html::END_FRAGMENT);
write_out!([html::SEP as u8]);
write_out!(&len_buffer);
write_out!(html::NEWLINE);
debug_assert_eq!(HEADER_SIZE, cursor);
write_out!(html::BODY_HEADER);
debug_assert_eq!(FRAGMENT_OFFSET, cursor);
write_out!(html);
debug_assert_eq!(total_size - html::BODY_FOOTER.len(), cursor);
write_out!(html::BODY_FOOTER);
debug_assert_eq!(cursor, total_size);
}
let _ = (empty)();
if unsafe { !SetClipboardData(format, mem.get()).is_null() } {
mem.release();
Ok(())
} else {
Err(ErrorCode::last_system())
}
}
fn set_inner(format: u32, data: &[u8], clear: EmptyFn) -> SysResult<()> {
let size = data.len();
if size == 0 {
#[allow(clippy::unit_arg)]
return Ok(unlikely_empty_size_result());
}
let mem = RawMem::new_global_mem(size)?;
{
let (ptr, _lock) = mem.lock()?;
unsafe { ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr() as _, size) };
}
let _ = (clear)();
if unsafe { !SetClipboardData(format, mem.get()).is_null() } {
mem.release();
return Ok(());
}
Err(ErrorCode::last_system())
}
pub fn set(format: u32, data: &[u8]) -> SysResult<()> {
set_inner(format, data, options::DoClear::EMPTY_FN)
}
pub fn set_without_clear(format: u32, data: &[u8]) -> SysResult<()> {
set_inner(format, data, options::NoClear::EMPTY_FN)
}
pub fn get_string(out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
let ptr = RawMem::from_borrowed(get_clipboard_data(formats::CF_UNICODETEXT)?);
let result = unsafe {
let (data_ptr, _lock) = ptr.lock()?;
let data_size = GlobalSize(ptr.get()) as usize / mem::size_of::<u16>();
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());
if storage_req_size == 0 {
return Err(ErrorCode::last_system());
}
let storage_cursor = out.len();
out.reserve(storage_req_size as usize);
let storage_ptr = out.as_mut_ptr().add(storage_cursor) as *mut _;
WideCharToMultiByte(CP_UTF8, 0, data_ptr.as_ptr() as _, data_size as _, storage_ptr, storage_req_size, ptr::null(), ptr::null_mut());
out.set_len(storage_cursor + storage_req_size as usize);
if let Some(null_idx) = out.iter().skip(storage_cursor).position(|b| *b == b'\0') {
out.set_len(storage_cursor + null_idx);
}
out.len() - storage_cursor
};
Ok(result)
}
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)
};
if size != 0 || data.is_empty() {
let mem = RawMem::new_global_mem((mem::size_of::<u16>() * (size as usize + 1)) as _)?;
{
let (ptr, _lock) = mem.lock()?;
let ptr = ptr.as_ptr() as *mut u16;
unsafe {
MultiByteToWideChar(CP_UTF8, 0, data.as_ptr() as *const _, data.len() as _, ptr, size);
ptr::write(ptr.offset(size as isize), 0);
}
}
let _ = (clear)();
if unsafe { !SetClipboardData(formats::CF_UNICODETEXT, mem.get()).is_null() } {
mem.release();
return Ok(());
}
}
Err(ErrorCode::last_system())
}
#[inline(always)]
pub fn set_string(data: &str) -> SysResult<()> {
set_string_inner(data, options::DoClear::EMPTY_FN)
}
#[inline(always)]
pub fn set_string_with<C: Clearing>(data: &str, _is_clear: C) -> SysResult<()> {
set_string_inner(data, C::EMPTY_FN)
}
#[cfg(feature = "std")]
pub fn get_file_list_path(out: &mut alloc::vec::Vec<std::path::PathBuf>) -> SysResult<usize> {
use std::os::windows::ffi::OsStringExt;
let clipboard_data = RawMem::from_borrowed(get_clipboard_data(formats::CF_HDROP)?);
let (_data_ptr, _lock) = clipboard_data.lock()?;
let num_files = unsafe { DragQueryFileW(clipboard_data.get() as _, u32::MAX, ptr::null_mut(), 0) };
out.reserve(num_files as usize);
let mut buffer = alloc::vec::Vec::new();
for idx in 0..num_files {
let required_size_no_null = unsafe { DragQueryFileW(clipboard_data.get() as _, idx, ptr::null_mut(), 0) };
if required_size_no_null == 0 {
return Err(ErrorCode::last_system());
}
let required_size = required_size_no_null + 1;
buffer.reserve(required_size as usize);
if unsafe { DragQueryFileW(clipboard_data.get() as _, idx, buffer.as_mut_ptr(), required_size) == 0 } {
return Err(ErrorCode::last_system());
}
unsafe {
buffer.set_len(required_size_no_null as usize);
}
out.push(std::ffi::OsString::from_wide(&buffer).into())
}
Ok(num_files as usize)
}
pub fn get_file_list(out: &mut alloc::vec::Vec<alloc::string::String>) -> SysResult<usize> {
let clipboard_data = RawMem::from_borrowed(get_clipboard_data(formats::CF_HDROP)?);
let (_data_ptr, _lock) = clipboard_data.lock()?;
let num_files = unsafe { DragQueryFileW(clipboard_data.get() as _, u32::MAX, ptr::null_mut(), 0) };
out.reserve(num_files as usize);
let mut buffer = alloc::vec::Vec::new();
for idx in 0..num_files {
let required_size_no_null = unsafe { DragQueryFileW(clipboard_data.get() as _, idx, ptr::null_mut(), 0) };
if required_size_no_null == 0 {
return Err(ErrorCode::last_system());
}
let required_size = required_size_no_null + 1;
buffer.reserve(required_size as usize);
if unsafe { DragQueryFileW(clipboard_data.get() as _, idx, buffer.as_mut_ptr(), required_size) == 0 } {
return Err(ErrorCode::last_system());
}
unsafe {
buffer.set_len(required_size_no_null as usize);
}
out.push(alloc::string::String::from_utf16_lossy(&buffer));
}
Ok(num_files as usize)
}
pub fn get_bitmap(out: &mut alloc::vec::Vec<u8>) -> SysResult<usize> {
let clipboard_data = get_clipboard_data(formats::CF_BITMAP)?;
let mut bitmap = BITMAP {
bmType: 0,
bmWidth: 0,
bmHeight: 0,
bmWidthBytes: 0,
bmPlanes: 0,
bmBitsPixel: 0,
bmBits: ptr::null_mut(),
};
if unsafe { GetObjectW(clipboard_data.as_ptr(), mem::size_of::<BITMAP>() as _, &mut bitmap as *mut BITMAP as _) } == 0 {
return Err(ErrorCode::last_system());
}
let clr_bits = bitmap.bmPlanes * bitmap.bmBitsPixel;
let clr_bits = if clr_bits == 1 {
1
} else if clr_bits <= 4 {
4
} else if clr_bits <= 8 {
8
} else if clr_bits <= 16 {
16
} else if clr_bits <= 24 {
24
} else {
32
};
let header_storage = RawMem::new_rust_mem(if clr_bits < 24 {
mem::size_of::<BITMAPINFOHEADER>() + mem::size_of::<RGBQUAD>() * (1 << clr_bits)
} else {
mem::size_of::<BITMAPINFOHEADER>()
})?;
let header = unsafe {
&mut *(header_storage.get() as *mut BITMAPINFO)
};
header.bmiHeader.biSize = mem::size_of::<BITMAPINFOHEADER>() as _;
header.bmiHeader.biWidth = bitmap.bmWidth;
header.bmiHeader.biHeight = bitmap.bmHeight;
header.bmiHeader.biPlanes = bitmap.bmPlanes;
header.bmiHeader.biBitCount = bitmap.bmBitsPixel;
header.bmiHeader.biCompression = BI_RGB;
if clr_bits < 24 {
header.bmiHeader.biClrUsed = 1 << clr_bits;
}
header.bmiHeader.biSizeImage = ((((header.bmiHeader.biWidth * clr_bits + 31) & !31) / 8) * header.bmiHeader.biHeight) as _;
header.bmiHeader.biClrImportant = 0;
let img_size = header.bmiHeader.biSizeImage as usize;
let out_before = out.len();
let dc = crate::utils::Scope(unsafe { GetDC(ptr::null_mut()) }, free_dc);
let mut buffer = alloc::vec![0; img_size];
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 {
return Err(ErrorCode::last_system());
}
out.extend_from_slice(&u16::to_le_bytes(0x4d42));
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));
out.extend_from_slice(&u32::to_le_bytes(0)); 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));
out.extend_from_slice(&header.bmiHeader.biSize.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biWidth.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biHeight.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biPlanes.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biBitCount.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biCompression.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biSizeImage.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biXPelsPerMeter.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biYPelsPerMeter.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biClrUsed.to_le_bytes());
out.extend_from_slice(&header.bmiHeader.biClrImportant.to_le_bytes());
for color in unsafe { slice::from_raw_parts(header.bmiColors.as_ptr(), header.bmiHeader.biClrUsed as _) } {
out.push(color.rgbBlue);
out.push(color.rgbGreen);
out.push(color.rgbRed);
out.push(color.rgbReserved);
}
out.extend_from_slice(&buffer);
Ok(out.len() - out_before)
}
#[inline(always)]
#[doc(hidden)]
pub fn set_bitamp(data: &[u8]) -> SysResult<()> {
set_bitmap(data)
}
pub fn set_bitmap(data: &[u8]) -> SysResult<()> {
set_bitmap_inner(data, options::NoClear::EMPTY_FN)
}
pub fn set_bitmap_with<C: Clearing>(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::<BITMAPFILEHEADER>();
const INFO_HEADER_LEN: usize = mem::size_of::<BITMAPINFOHEADER>();
if data.len() <= (FILE_HEADER_LEN + INFO_HEADER_LEN) {
return Err(ErrorCode::new_system(ERROR_INCORRECT_SIZE as _));
}
let mut file_header = mem::MaybeUninit::<BITMAPFILEHEADER>::uninit();
let mut info_header = mem::MaybeUninit::<BITMAPINFOHEADER>::uninit();
let (file_header, info_header) = unsafe {
ptr::copy_nonoverlapping(data.as_ptr(), file_header.as_mut_ptr() as _, FILE_HEADER_LEN);
ptr::copy_nonoverlapping(data.as_ptr().add(FILE_HEADER_LEN), info_header.as_mut_ptr() as _, INFO_HEADER_LEN);
(file_header.assume_init(), info_header.assume_init())
};
if data.len() <= file_header.bfOffBits as usize {
return Err(ErrorCode::new_system(ERROR_INCORRECT_SIZE as _));
}
let bitmap = &data[file_header.bfOffBits as _..];
if bitmap.len() < info_header.biSizeImage as usize {
return Err(ErrorCode::new_system(ERROR_INCORRECT_SIZE as _));
}
let dc = crate::utils::Scope(unsafe { GetDC(ptr::null_mut()) }, free_dc);
let handle = unsafe {
CreateDIBitmap(dc.0, &info_header as _, CBM_INIT, bitmap.as_ptr() as _, &info_header as *const _ as *const BITMAPINFO, DIB_RGB_COLORS)
};
if handle.is_null() {
return Err(ErrorCode::last_system());
}
let _ = (clear)();
if unsafe { SetClipboardData(formats::CF_BITMAP, handle as _).is_null() } {
return Err(ErrorCode::last_system());
}
Ok(())
}
#[inline(always)]
pub fn set_file_list(paths: &[impl AsRef<str>]) -> SysResult<()> {
set_file_list_inner(paths, options::NoClear::EMPTY_FN)
}
#[inline(always)]
pub fn set_file_list_with<C: Clearing>(paths: &[impl AsRef<str>], _is_clear: C) -> SysResult<()> {
set_file_list_inner(paths, C::EMPTY_FN)
}
fn set_file_list_inner(paths: &[impl AsRef<str>], empty: EmptyFn) -> SysResult<()> {
#[repr(C, packed(1))]
pub struct DROPFILES {
pub p_files: u32,
pub pt: POINT,
pub f_nc: c_int,
pub f_wide: c_int,
}
const DROPFILES_SIZE: DWORD = core::mem::size_of::<DROPFILES>() as DWORD;
let mut file_list_size = 0;
for path in paths {
let path = path.as_ref();
unsafe {
file_list_size += MultiByteToWideChar(CP_UTF8, 0, path.as_ptr() as *const _, path.len() as _, ptr::null_mut(), 0) + 1
}
}
if file_list_size == 0 {
return Err(ErrorCode::last_system());
}
let dropfiles = DROPFILES {
p_files: DROPFILES_SIZE,
pt: POINT { x: 0, y: 0 },
f_nc: 0,
f_wide: 1,
};
let mem_size = DROPFILES_SIZE as usize + (file_list_size as usize * 2) + 2; let mem = crate::utils::RawMem::new_global_mem(mem_size)?;
{
let (ptr, _lock) = mem.lock()?;
let ptr = ptr.as_ptr() as *mut u8;
unsafe {
(ptr as *mut DROPFILES).write(dropfiles);
let mut ptr = ptr.add(DROPFILES_SIZE as usize) as *mut u16;
for path in paths {
let path = path.as_ref();
let written = MultiByteToWideChar(CP_UTF8, 0, path.as_ptr() as *const _, path.len() as _, ptr, file_list_size);
ptr = ptr.offset(written as isize);
ptr.write(0);
ptr = ptr.add(1);
file_list_size -= written - 1;
}
ptr.write(0);
}
}
let _ = (empty)();
if unsafe { !SetClipboardData(formats::CF_HDROP, mem.get()).is_null() } {
mem.release();
Ok(())
} else {
Err(ErrorCode::last_system())
}
}
pub struct EnumFormats {
idx: u32
}
impl EnumFormats {
pub fn new() -> EnumFormats {
EnumFormats { idx: 0 }
}
pub fn from(format: u32) -> EnumFormats {
EnumFormats { idx: format }
}
pub fn reset(&mut self) -> &EnumFormats {
self.idx = 0;
self
}
}
impl Iterator for EnumFormats {
type Item = u32;
fn next(&mut self) -> Option<u32> {
self.idx = unsafe { EnumClipboardFormats(self.idx) };
if self.idx == 0 {
None
} else {
Some(self.idx)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, count_formats())
}
}
macro_rules! match_format_name_big {
( $name:expr, $( $f:ident ),* ) => {
match $name {
$( formats::$f => Some(stringify!($f).to_owned()),)*
formats::CF_GDIOBJFIRST ..= formats::CF_GDIOBJLAST => Some(format!("CF_GDIOBJ{}", $name - formats::CF_GDIOBJFIRST)),
formats::CF_PRIVATEFIRST ..= formats::CF_PRIVATELAST => Some(format!("CF_PRIVATE{}", $name - formats::CF_PRIVATEFIRST)),
_ => {
let mut format_buff = [0u16; 256];
unsafe {
let buff_p = format_buff.as_mut_ptr() as *mut u16;
match GetClipboardFormatNameW($name, buff_p, format_buff.len() as c_int) {
0 => None,
size => Some(String::from_utf16_lossy(&format_buff[..size as usize])),
}
}
}
}
}
}
macro_rules! match_format_name {
( $name:expr => $out:ident, $( $f:ident ),* ) => {
use core::fmt::Write;
match $name {
$( formats::$f => {
let _ = $out.push_str(stringify!($f));
},)*
formats::CF_GDIOBJFIRST ..= formats::CF_GDIOBJLAST => {
let _ = write!($out, "CF_GDIOBJ{}", $name - formats::CF_GDIOBJFIRST);
},
formats::CF_PRIVATEFIRST ..= formats::CF_PRIVATELAST => {
let _ = write!($out, "CF_PRIVATE{}", $name - formats::CF_PRIVATEFIRST);
},
_ => {
let mut format_buff = [0u16; 256];
unsafe {
let buff_p = format_buff.as_mut_ptr() as *mut u16;
match GetClipboardFormatNameW($name, buff_p, format_buff.len() as c_int) {
0 => return None,
len => match WideCharToMultiByte(CP_UTF8, 0, format_buff.as_ptr(), len, $out.as_mut_ptr() as *mut i8, $out.remaining() as i32, ptr::null(), ptr::null_mut()) {
0 => return None,
len => $out.set_len(len as _),
}
}
}
}
}
return $out.as_str()
}
}
pub fn format_name(format: u32, mut out: Buffer<'_>) -> Option<&'_ str> {
match_format_name!(format => out,
CF_BITMAP,
CF_DIB,
CF_DIBV5,
CF_DIF,
CF_DSPBITMAP,
CF_DSPENHMETAFILE,
CF_DSPMETAFILEPICT,
CF_DSPTEXT,
CF_ENHMETAFILE,
CF_HDROP,
CF_LOCALE,
CF_METAFILEPICT,
CF_OEMTEXT,
CF_OWNERDISPLAY,
CF_PALETTE,
CF_PENDATA,
CF_RIFF,
CF_SYLK,
CF_TEXT,
CF_WAVE,
CF_TIFF,
CF_UNICODETEXT);
}
pub fn format_name_big(format: u32) -> Option<String> {
match_format_name_big!(format,
CF_BITMAP,
CF_DIB,
CF_DIBV5,
CF_DIF,
CF_DSPBITMAP,
CF_DSPENHMETAFILE,
CF_DSPMETAFILEPICT,
CF_DSPTEXT,
CF_ENHMETAFILE,
CF_HDROP,
CF_LOCALE,
CF_METAFILEPICT,
CF_OEMTEXT,
CF_OWNERDISPLAY,
CF_PALETTE,
CF_PENDATA,
CF_RIFF,
CF_SYLK,
CF_TEXT,
CF_WAVE,
CF_TIFF,
CF_UNICODETEXT)
}
#[inline]
pub unsafe fn register_raw_format(name: &[u16]) -> Option<NonZeroU32> {
if name.is_empty() || name[name.len()-1] != b'\0' as u16 {
return unlikely_empty_size_result()
}
NonZeroU32::new(RegisterClipboardFormatW(name.as_ptr()) )
}
pub fn register_format(name: &str) -> Option<NonZeroU32> {
let size = unsafe {
MultiByteToWideChar(CP_UTF8, 0, name.as_ptr() as *const _, name.len() as c_int, ptr::null_mut(), 0)
};
if size == 0 {
return unlikely_empty_size_result()
}
if size > 52 {
let mut buffer = alloc::vec::Vec::with_capacity(size as usize);
let size = unsafe {
MultiByteToWideChar(CP_UTF8, 0, name.as_ptr() as *const _, name.len() as c_int, buffer.as_mut_ptr(), size)
};
unsafe {
buffer.set_len(size as usize);
buffer.push(0);
register_raw_format(&buffer)
}
} else {
let mut buffer = mem::MaybeUninit::<[u16; 52]>::zeroed();
let size = unsafe {
MultiByteToWideChar(CP_UTF8, 0, name.as_ptr() as *const _, name.len() as c_int, buffer.as_mut_ptr() as *mut u16, 51)
};
unsafe {
ptr::write((buffer.as_mut_ptr() as *mut u16).offset(size as isize), 0);
register_raw_format(slice::from_raw_parts(buffer.as_ptr() as *const u16, size as usize + 1))
}
}
}
#[inline(always)]
pub fn get_owner() -> Option<ptr::NonNull::<c_void>> {
ptr::NonNull::new(unsafe {
GetClipboardOwner()
})
}