1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
pub(super) use self::clipboard_format::ClipboardFormat;
mod clipboard_format;

use std::ffi::c_void;
use std::io;
use std::marker::PhantomData;
use std::ops::Range;
use std::slice;

use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::Foundation::{FALSE, HANDLE, HWND};
use windows_sys::Win32::System::DataExchange::GetUpdatedClipboardFormats;
use windows_sys::Win32::System::DataExchange::{CloseClipboard, GetClipboardData, OpenClipboard};
use windows_sys::Win32::System::Memory::{GlobalLock, GlobalSize, GlobalUnlock};

/// An open clipboard handle.
pub(crate) struct Clipboard;

impl Clipboard {
    /// Construct a new clipboard around the given window.
    ///
    /// # Safety
    ///
    /// The window handle must be valid and no other component must've acquired the clipboard.
    pub(super) unsafe fn new(handle: HWND) -> io::Result<Self> {
        if OpenClipboard(handle) == FALSE {
            return Err(io::Error::last_os_error());
        }

        Ok(Self)
    }

    /// Enumerate available clipboard formats.
    pub(super) fn updated_formats<const N: usize>() -> UpdatedFormats<N> {
        unsafe {
            let mut formats = [0u32; N];
            let mut actual = 0;
            GetUpdatedClipboardFormats(formats.as_mut_ptr(), 16, &mut actual);

            UpdatedFormats {
                formats,
                range: 0..actual as usize,
            }
        }
    }

    /// Acquire data with the specified format.
    pub(crate) fn data(&self, format: ClipboardFormat) -> io::Result<Data<'_>> {
        // SAFETY: This is safe as long as construction is correct.
        unsafe {
            let handle = GetClipboardData(format.as_u16() as u32);

            if handle == 0 || handle == INVALID_HANDLE_VALUE {
                return Err(io::Error::last_os_error());
            }

            Ok(Data {
                handle,
                _marker: PhantomData,
            })
        }
    }
}

impl Drop for Clipboard {
    fn drop(&mut self) {
        unsafe {
            let _ = CloseClipboard();
        }
    }
}

/// A clipboard data handle.
pub(super) struct Data<'a> {
    handle: HANDLE,
    _marker: PhantomData<&'a Clipboard>,
}

impl<'c> Data<'c> {
    pub(super) fn lock(&self) -> io::Result<Lock<'_>> {
        // SAFETY: Construction of Clipboard ensures that this is used
        // correctly.
        unsafe {
            let handle = GlobalLock(self.handle as *mut _);

            if handle.is_null() {
                return Err(io::Error::last_os_error());
            }

            Ok(Lock {
                handle,
                _marker: PhantomData,
            })
        }
    }
}

pub(super) struct Lock<'a> {
    handle: *mut c_void,
    _marker: PhantomData<&'a ()>,
}

impl Lock<'_> {
    /// Coerce locked data into a byte slice.
    pub(super) fn as_slice(&self) -> &[u8] {
        // SAFETY: Lock has been correctly acquired.
        unsafe {
            let len = GlobalSize(self.handle) as usize;
            slice::from_raw_parts(self.handle.cast(), len)
        }
    }

    /// Coerce locked data into a wide slice.
    pub(super) fn as_wide_slice(&self) -> &[u16] {
        // SAFETY: Lock has been correctly acquired.
        unsafe {
            let len = GlobalSize(self.handle) as usize;
            debug_assert!(len % 2 == 0, "a wide slice must be a multiple of two");
            slice::from_raw_parts(self.handle.cast(), len / 2)
        }
    }
}

impl Drop for Lock<'_> {
    fn drop(&mut self) {
        // SAFETY: Lock has been correctly acquired.
        unsafe {
            let _ = GlobalUnlock(self.handle);
        }
    }
}

/// An iterator over clipboard formats.
pub(super) struct UpdatedFormats<const N: usize> {
    formats: [u32; N],
    range: Range<usize>,
}

impl<const N: usize> Iterator for UpdatedFormats<N> {
    type Item = ClipboardFormat;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        let index = self.range.next()?;
        let format = self.formats[index];
        Some(ClipboardFormat::new(format as u16))
    }
}