rusty_duplication/
scanner.rs

1use crate::{Error, Monitor, Result};
2use std::ptr::null_mut;
3use windows::{
4  core::Interface,
5  Win32::{
6    Foundation::HMODULE,
7    Graphics::{
8      Direct3D::D3D_DRIVER_TYPE_UNKNOWN,
9      Direct3D11::{
10        D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, D3D11_CREATE_DEVICE_FLAG,
11        D3D11_SDK_VERSION,
12      },
13      Dxgi::{CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIOutput1},
14    },
15  },
16};
17
18/// Scan for [`Monitor`]s.
19/// # Examples
20/// ```
21/// use rusty_duplication::Scanner;
22///
23/// // create a new scanner
24/// let mut scanner = Scanner::new().unwrap();
25/// // get the next available monitor
26/// let ctx = scanner.next().unwrap();
27/// ```
28#[derive(Debug, Clone)]
29pub struct Scanner {
30  next_adapter_index: u32,
31  next_output_index: u32,
32  factory: IDXGIFactory1,
33  adapter: IDXGIAdapter1,
34  device: ID3D11Device,
35  device_context: ID3D11DeviceContext,
36}
37
38impl Scanner {
39  /// Try to create a new scanner.
40  /// Return [`Err`] if no adapter is found.
41  pub fn new() -> Result<Self> {
42    let factory = unsafe { CreateDXGIFactory1::<IDXGIFactory1>() }
43      .map_err(Error::from_win_err(stringify!(CreateDXGIFactory1)))?;
44
45    let adapter_index = 0;
46    let (adapter, device, device_context) = get_adapter(&factory, adapter_index)?;
47
48    Ok(Self {
49      next_adapter_index: adapter_index + 1,
50      next_output_index: 0,
51      factory,
52      adapter,
53      device,
54      device_context,
55    })
56  }
57
58  fn get_current_ctx(&mut self) -> Option<Monitor> {
59    let output_index = self.next_output_index;
60    self.next_output_index += 1;
61
62    // TODO: add debug log for Result::ok()
63    let output = unsafe { self.adapter.EnumOutputs(output_index) }.ok()?;
64    let output = output.cast::<IDXGIOutput1>().unwrap();
65    let output_duplication = unsafe { output.DuplicateOutput(&self.device) }.ok()?;
66    Some(Monitor::new(
67      self.device.clone(),
68      self.device_context.clone(),
69      output,
70      output_duplication,
71    ))
72  }
73}
74
75impl Iterator for Scanner {
76  type Item = Monitor;
77
78  fn next(&mut self) -> Option<Self::Item> {
79    loop {
80      if let Some(ctx) = self.get_current_ctx() {
81        return Some(ctx);
82      }
83
84      // no more available outputs, try next adapter
85      let adapter_index = self.next_adapter_index;
86      self.next_adapter_index += 1;
87      let Ok((adapter, device, device_context)) = get_adapter(&self.factory, adapter_index) else {
88        break;
89      };
90      self.adapter = adapter;
91      self.device = device;
92      self.device_context = device_context;
93      self.next_output_index = 0;
94    }
95
96    None
97  }
98}
99
100fn get_adapter(
101  factory: &IDXGIFactory1,
102  adapter_index: u32,
103) -> Result<(IDXGIAdapter1, ID3D11Device, ID3D11DeviceContext)> {
104  let adapter = unsafe { factory.EnumAdapters1(adapter_index) }
105    .map_err(Error::from_win_err(stringify!(IDXGIFactory1.EnumAdapters1)))?;
106
107  let mut device: Option<ID3D11Device> = None;
108  let mut device_context: Option<ID3D11DeviceContext> = None;
109
110  unsafe {
111    D3D11CreateDevice(
112      &adapter,
113      D3D_DRIVER_TYPE_UNKNOWN,
114      HMODULE(null_mut()),
115      D3D11_CREATE_DEVICE_FLAG(0),
116      None,
117      D3D11_SDK_VERSION,
118      Some(&mut device),
119      None,
120      Some(&mut device_context),
121    )
122  }
123  .map_err(Error::from_win_err(stringify!(D3D11CreateDevice)))?;
124
125  Ok((adapter, device.unwrap(), device_context.unwrap()))
126}
127
128#[cfg(test)]
129mod tests {
130  use super::*;
131  use serial_test::serial;
132
133  #[test]
134  #[serial]
135  fn manager() {
136    let mut factory = Scanner::new().unwrap();
137    assert!(factory.next().is_some());
138  }
139}