1use crate::{Error, FrameInfoExt, Result};
2use windows::{
3 core::Interface,
4 Win32::Graphics::{
5 Direct3D11::{
6 ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, D3D11_BIND_FLAG, D3D11_CPU_ACCESS_READ,
7 D3D11_RESOURCE_MISC_FLAG, D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING,
8 },
9 Dxgi::{
10 Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC},
11 IDXGIOutput1, IDXGIOutputDuplication, IDXGIResource, DXGI_OUTDUPL_DESC,
12 DXGI_OUTDUPL_FRAME_INFO, DXGI_OUTDUPL_POINTER_SHAPE_INFO, DXGI_OUTPUT_DESC,
13 DXGI_RESOURCE_PRIORITY_MAXIMUM,
14 },
15 Gdi::{GetMonitorInfoW, MONITORINFO},
16 },
17};
18
19#[derive(Debug, Clone)]
24pub struct Monitor {
25 device: ID3D11Device,
26 device_context: ID3D11DeviceContext,
27 output: IDXGIOutput1,
28 output_duplication: IDXGIOutputDuplication,
29}
30
31impl Monitor {
32 #[inline]
33 pub(crate) fn new(
34 device: ID3D11Device,
35 device_context: ID3D11DeviceContext,
36 output: IDXGIOutput1,
37 output_duplication: IDXGIOutputDuplication,
38 ) -> Self {
39 Self {
40 device,
41 device_context,
42 output,
43 output_duplication,
44 }
45 }
46
47 pub fn monitor_info(&self) -> Result<MONITORINFO> {
56 let h_monitor = self.dxgi_output_desc()?.Monitor;
57 let mut info = MONITORINFO {
58 cbSize: size_of::<MONITORINFO>() as u32,
59 ..Default::default()
60 };
61 if unsafe { GetMonitorInfoW(h_monitor, &mut info).as_bool() } {
62 Ok(info)
63 } else {
64 Err(Error::last_win_err(stringify!(GetMonitorInfoW)))
65 }
66 }
67
68 #[inline]
78 pub fn dxgi_output_desc(&self) -> Result<DXGI_OUTPUT_DESC> {
79 unsafe { self.output.GetDesc() }
80 .map_err(Error::from_win_err(stringify!(DXGI_OUTPUT_DESC.GetDesc)))
81 }
82
83 #[inline]
85 pub fn dxgi_outdupl_desc(&self) -> DXGI_OUTDUPL_DESC {
86 unsafe { self.output_duplication.GetDesc() }
87 }
88
89 pub(crate) fn create_texture(
90 &self,
91 dupl_desc: &DXGI_OUTDUPL_DESC,
92 output_desc: &DXGI_OUTPUT_DESC,
93 ) -> Result<(ID3D11Texture2D, D3D11_TEXTURE2D_DESC)> {
94 let texture_desc = D3D11_TEXTURE2D_DESC {
96 BindFlags: D3D11_BIND_FLAG::default().0 as u32,
97 CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
98 MiscFlags: D3D11_RESOURCE_MISC_FLAG::default().0 as u32,
99 Usage: D3D11_USAGE_STAGING, Width: match output_desc.Rotation.0 {
101 2 | 4 => dupl_desc.ModeDesc.Height,
102 _ => dupl_desc.ModeDesc.Width,
103 },
104 Height: match output_desc.Rotation.0 {
105 2 | 4 => dupl_desc.ModeDesc.Width,
106 _ => dupl_desc.ModeDesc.Height,
107 },
108 MipLevels: 1,
109 ArraySize: 1,
110 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
111 SampleDesc: DXGI_SAMPLE_DESC {
112 Count: 1,
113 Quality: 0,
114 },
115 };
116
117 let mut texture: Option<ID3D11Texture2D> = None;
119 unsafe {
120 self
121 .device
122 .CreateTexture2D(&texture_desc, None, Some(&mut texture))
123 }
124 .map_err(Error::from_win_err(stringify!(
125 ID3D11Device.CreateTexture2D
126 )))?;
127 let texture = texture.unwrap();
128 unsafe { texture.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM.0) };
132
133 Ok((texture, texture_desc))
134 }
135
136 fn process_next_frame<R>(
138 &self,
139 timeout_ms: u32,
140 texture: &ID3D11Texture2D,
141 cb: impl FnOnce(DXGI_OUTDUPL_FRAME_INFO) -> R,
142 ) -> Result<R> {
143 let mut frame_info = DXGI_OUTDUPL_FRAME_INFO::default();
145 let mut resource: Option<IDXGIResource> = None;
146 unsafe {
147 self
148 .output_duplication
149 .AcquireNextFrame(timeout_ms, &mut frame_info, &mut resource)
150 }
151 .map_err(Error::from_win_err(stringify!(
152 IDXGIOutputDuplication.AcquireNextFrame
153 )))?;
154 let new_texture: ID3D11Texture2D = resource.unwrap().cast().unwrap();
155
156 unsafe { self.device_context.CopyResource(texture, &new_texture) };
158
159 let r = cb(frame_info);
160
161 unsafe { self.output_duplication.ReleaseFrame() }.map_err(Error::from_win_err(stringify!(
162 IDXGIOutputDuplication.ReleaseFrame
163 )))?;
164
165 Ok(r)
166 }
167
168 #[inline]
172 pub(crate) fn next_frame(
173 &self,
174 timeout_ms: u32,
175 texture: &ID3D11Texture2D,
176 ) -> Result<DXGI_OUTDUPL_FRAME_INFO> {
177 self.process_next_frame(timeout_ms, texture, |r| r)
178 }
179
180 pub(crate) fn next_frame_with_pointer_shape(
183 &self,
184 timeout_ms: u32,
185 texture: &ID3D11Texture2D,
186 pointer_shape_buffer: &mut Vec<u8>,
187 ) -> Result<(
188 DXGI_OUTDUPL_FRAME_INFO,
189 Option<DXGI_OUTDUPL_POINTER_SHAPE_INFO>,
190 )> {
191 self
192 .process_next_frame(timeout_ms, texture, |frame_info| {
193 if !frame_info.pointer_shape_updated() {
194 return Ok((frame_info, None));
195 }
196
197 let pointer_shape_buffer_size = frame_info.PointerShapeBufferSize as usize;
199 if pointer_shape_buffer.len() < pointer_shape_buffer_size {
200 pointer_shape_buffer.resize(pointer_shape_buffer_size, 0);
201 }
202
203 let mut size: u32 = 0;
205 let mut pointer_shape_info = DXGI_OUTDUPL_POINTER_SHAPE_INFO::default();
206 unsafe {
207 self.output_duplication.GetFramePointerShape(
208 pointer_shape_buffer.len() as u32,
209 pointer_shape_buffer.as_mut_ptr() as *mut _,
210 &mut size,
211 &mut pointer_shape_info,
212 )
213 }
214 .map_err(Error::from_win_err(stringify!(
215 IDXGIOutputDuplication.GetFramePointerShape
216 )))?;
217 pointer_shape_buffer.truncate(size as usize);
219
220 Ok((frame_info, Some(pointer_shape_info)))
221 })
222 .and_then(|r| r)
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use crate::{MonitorInfoExt, Scanner};
229 use serial_test::serial;
230
231 #[test]
232 #[serial]
233 fn monitor() {
234 let contexts = Scanner::new().unwrap().collect::<Vec<_>>();
235
236 let mut primary_monitor_count = 0;
238 for c in &contexts {
239 if c.monitor_info().unwrap().is_primary() {
240 primary_monitor_count += 1;
241 }
242 }
243 assert_eq!(primary_monitor_count, 1);
244 }
245}