mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
source code
This commit is contained in:
212
libs/scrap/src/dxgi/gdi.rs
Normal file
212
libs/scrap/src/dxgi/gdi.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use std::mem::size_of;
|
||||
use winapi::{
|
||||
shared::windef::{HBITMAP, HDC},
|
||||
um::wingdi::{
|
||||
BitBlt,
|
||||
CreateCompatibleBitmap,
|
||||
CreateCompatibleDC,
|
||||
CreateDCW,
|
||||
DeleteDC,
|
||||
DeleteObject,
|
||||
GetDIBits,
|
||||
SelectObject,
|
||||
BITMAPINFO,
|
||||
BITMAPINFOHEADER,
|
||||
BI_RGB,
|
||||
DIB_RGB_COLORS, //CAPTUREBLT,
|
||||
HGDI_ERROR,
|
||||
RGBQUAD,
|
||||
SRCCOPY,
|
||||
},
|
||||
};
|
||||
|
||||
const PIXEL_WIDTH: i32 = 4;
|
||||
|
||||
pub struct CapturerGDI {
|
||||
screen_dc: HDC,
|
||||
dc: HDC,
|
||||
bmp: HBITMAP,
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
|
||||
impl CapturerGDI {
|
||||
pub fn new(name: &[u16], width: i32, height: i32) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
/* or Enumerate monitors with EnumDisplayMonitors,
|
||||
https://stackoverflow.com/questions/34987695/how-can-i-get-an-hmonitor-handle-from-a-display-device-name
|
||||
#[no_mangle]
|
||||
pub extern "C" fn callback(m: HMONITOR, dc: HDC, rect: LPRECT, lp: LPARAM) -> BOOL {}
|
||||
*/
|
||||
/*
|
||||
shared::windef::HMONITOR,
|
||||
winuser::{GetMonitorInfoW, GetSystemMetrics, MONITORINFOEXW},
|
||||
let mut mi: MONITORINFOEXW = std::mem::MaybeUninit::uninit().assume_init();
|
||||
mi.cbSize = size_of::<MONITORINFOEXW>() as _;
|
||||
if GetMonitorInfoW(m, &mut mi as *mut MONITORINFOEXW as _) == 0 {
|
||||
return Err(format!("Failed to get monitor information of: {:?}", m).into());
|
||||
}
|
||||
*/
|
||||
unsafe {
|
||||
if name.is_empty() {
|
||||
return Err("Empty display name".into());
|
||||
}
|
||||
let screen_dc = CreateDCW(&name[0], 0 as _, 0 as _, 0 as _);
|
||||
if screen_dc.is_null() {
|
||||
return Err("Failed to create dc from monitor name".into());
|
||||
}
|
||||
|
||||
// Create a Windows Bitmap, and copy the bits into it
|
||||
let dc = CreateCompatibleDC(screen_dc);
|
||||
if dc.is_null() {
|
||||
DeleteDC(screen_dc);
|
||||
return Err("Can't get a Windows display".into());
|
||||
}
|
||||
|
||||
let bmp = CreateCompatibleBitmap(screen_dc, width, height);
|
||||
if bmp.is_null() {
|
||||
DeleteDC(screen_dc);
|
||||
DeleteDC(dc);
|
||||
return Err("Can't create a Windows buffer".into());
|
||||
}
|
||||
|
||||
let res = SelectObject(dc, bmp as _);
|
||||
if res.is_null() || res == HGDI_ERROR {
|
||||
DeleteDC(screen_dc);
|
||||
DeleteDC(dc);
|
||||
DeleteObject(bmp as _);
|
||||
return Err("Can't select Windows buffer".into());
|
||||
}
|
||||
Ok(Self {
|
||||
screen_dc,
|
||||
dc,
|
||||
bmp,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn frame(&self, data: &mut Vec<u8>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
unsafe {
|
||||
let res = BitBlt(
|
||||
self.dc,
|
||||
0,
|
||||
0,
|
||||
self.width,
|
||||
self.height,
|
||||
self.screen_dc,
|
||||
0,
|
||||
0,
|
||||
SRCCOPY, // | CAPTUREBLT, // CAPTUREBLT enable layered window but also make cursor blinking
|
||||
);
|
||||
if res == 0 {
|
||||
return Err("Failed to copy screen to Windows buffer".into());
|
||||
}
|
||||
|
||||
let stride = self.width * PIXEL_WIDTH;
|
||||
let size: usize = (stride * self.height) as usize;
|
||||
let mut data1: Vec<u8> = Vec::with_capacity(size);
|
||||
data1.set_len(size);
|
||||
data.resize(size, 0);
|
||||
|
||||
let mut bmi = BITMAPINFO {
|
||||
bmiHeader: BITMAPINFOHEADER {
|
||||
biSize: size_of::<BITMAPINFOHEADER>() as _,
|
||||
biWidth: self.width as _,
|
||||
biHeight: self.height as _,
|
||||
biPlanes: 1,
|
||||
biBitCount: (8 * PIXEL_WIDTH) as _,
|
||||
biCompression: BI_RGB,
|
||||
biSizeImage: (self.width * self.height * PIXEL_WIDTH) as _,
|
||||
biXPelsPerMeter: 0,
|
||||
biYPelsPerMeter: 0,
|
||||
biClrUsed: 0,
|
||||
biClrImportant: 0,
|
||||
},
|
||||
bmiColors: [RGBQUAD {
|
||||
rgbBlue: 0,
|
||||
rgbGreen: 0,
|
||||
rgbRed: 0,
|
||||
rgbReserved: 0,
|
||||
}],
|
||||
};
|
||||
|
||||
// copy bits into Vec
|
||||
let res = GetDIBits(
|
||||
self.dc,
|
||||
self.bmp,
|
||||
0,
|
||||
self.height as _,
|
||||
&mut data[0] as *mut u8 as _,
|
||||
&mut bmi as _,
|
||||
DIB_RGB_COLORS,
|
||||
);
|
||||
if res == 0 {
|
||||
return Err("GetDIBits failed".into());
|
||||
}
|
||||
crate::common::ARGBMirror(
|
||||
data.as_ptr(),
|
||||
stride,
|
||||
data1.as_mut_ptr(),
|
||||
stride,
|
||||
self.width,
|
||||
self.height,
|
||||
);
|
||||
crate::common::ARGBRotate(
|
||||
data1.as_ptr(),
|
||||
stride,
|
||||
data.as_mut_ptr(),
|
||||
stride,
|
||||
self.width,
|
||||
self.height,
|
||||
180,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CapturerGDI {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
DeleteDC(self.screen_dc);
|
||||
DeleteDC(self.dc);
|
||||
DeleteObject(self.bmp as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::*;
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test() {
|
||||
match Displays::new().unwrap().next() {
|
||||
Some(d) => {
|
||||
let w = d.width();
|
||||
let h = d.height();
|
||||
let c = CapturerGDI::new(d.name(), w, h).unwrap();
|
||||
let mut data = Vec::new();
|
||||
c.frame(&mut data).unwrap();
|
||||
let mut bitflipped = Vec::with_capacity((w * h * 4) as usize);
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let i = (w * 4 * y + 4 * x) as usize;
|
||||
bitflipped.extend_from_slice(&[data[i + 2], data[i + 1], data[i], 255]);
|
||||
}
|
||||
}
|
||||
repng::encode(
|
||||
std::fs::File::create("gdi_screen.png").unwrap(),
|
||||
d.width() as u32,
|
||||
d.height() as u32,
|
||||
&bitflipped,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
_ => {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
539
libs/scrap/src/dxgi/mod.rs
Normal file
539
libs/scrap/src/dxgi/mod.rs
Normal file
@@ -0,0 +1,539 @@
|
||||
use std::{io, mem, ptr, slice};
|
||||
pub mod gdi;
|
||||
pub use gdi::CapturerGDI;
|
||||
|
||||
use winapi::{
|
||||
shared::dxgi::{
|
||||
CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIResource, IDXGISurface,
|
||||
IID_IDXGIFactory1, IID_IDXGISurface, DXGI_MAP_READ, DXGI_OUTPUT_DESC,
|
||||
DXGI_RESOURCE_PRIORITY_MAXIMUM,
|
||||
},
|
||||
shared::dxgi1_2::IDXGIOutputDuplication,
|
||||
// shared::dxgiformat::{DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_420_OPAQUE},
|
||||
shared::dxgi1_2::{IDXGIOutput1, IID_IDXGIOutput1},
|
||||
shared::dxgitype::DXGI_MODE_ROTATION,
|
||||
shared::minwindef::{TRUE, UINT},
|
||||
shared::ntdef::LONG,
|
||||
shared::windef::HMONITOR,
|
||||
shared::winerror::{
|
||||
DXGI_ERROR_ACCESS_LOST, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_NOT_CURRENTLY_AVAILABLE,
|
||||
DXGI_ERROR_SESSION_DISCONNECTED, DXGI_ERROR_UNSUPPORTED, DXGI_ERROR_WAIT_TIMEOUT,
|
||||
E_ACCESSDENIED, E_INVALIDARG, S_OK,
|
||||
},
|
||||
um::d3d11::{
|
||||
D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, IID_ID3D11Texture2D,
|
||||
D3D11_CPU_ACCESS_READ, D3D11_SDK_VERSION, D3D11_USAGE_STAGING,
|
||||
},
|
||||
um::d3dcommon::D3D_DRIVER_TYPE_UNKNOWN,
|
||||
um::winnt::HRESULT,
|
||||
};
|
||||
|
||||
//TODO: Split up into files.
|
||||
|
||||
pub struct Capturer {
|
||||
device: *mut ID3D11Device,
|
||||
display: Display,
|
||||
context: *mut ID3D11DeviceContext,
|
||||
duplication: *mut IDXGIOutputDuplication,
|
||||
fastlane: bool,
|
||||
surface: *mut IDXGISurface,
|
||||
data: *const u8,
|
||||
len: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
use_yuv: bool,
|
||||
yuv: Vec<u8>,
|
||||
gdi_capturer: Option<CapturerGDI>,
|
||||
gdi_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Capturer {
|
||||
pub fn new(display: Display, use_yuv: bool) -> io::Result<Capturer> {
|
||||
let mut device = ptr::null_mut();
|
||||
let mut context = ptr::null_mut();
|
||||
let mut duplication = ptr::null_mut();
|
||||
let mut desc = unsafe { mem::MaybeUninit::uninit().assume_init() };
|
||||
let mut gdi_capturer = None;
|
||||
|
||||
let mut res = wrap_hresult(unsafe {
|
||||
D3D11CreateDevice(
|
||||
display.adapter as *mut _,
|
||||
D3D_DRIVER_TYPE_UNKNOWN,
|
||||
ptr::null_mut(), // No software rasterizer.
|
||||
0, // No device flags.
|
||||
ptr::null_mut(), // Feature levels.
|
||||
0, // Feature levels' length.
|
||||
D3D11_SDK_VERSION,
|
||||
&mut device,
|
||||
ptr::null_mut(),
|
||||
&mut context,
|
||||
)
|
||||
});
|
||||
|
||||
if res.is_err() {
|
||||
gdi_capturer = display.create_gdi();
|
||||
println!("Fallback to GDI");
|
||||
if gdi_capturer.is_some() {
|
||||
res = Ok(());
|
||||
}
|
||||
} else {
|
||||
res = wrap_hresult(unsafe {
|
||||
let hres = (*display.inner).DuplicateOutput(device as *mut _, &mut duplication);
|
||||
if hres != S_OK {
|
||||
gdi_capturer = display.create_gdi();
|
||||
println!("Fallback to GDI");
|
||||
if gdi_capturer.is_some() {
|
||||
S_OK
|
||||
} else {
|
||||
hres
|
||||
}
|
||||
} else {
|
||||
hres
|
||||
}
|
||||
// NVFBC(NVIDIA Capture SDK) which xpra used already deprecated, https://developer.nvidia.com/capture-sdk
|
||||
|
||||
// also try high version DXGI for better performance, e.g.
|
||||
// https://docs.microsoft.com/zh-cn/windows/win32/direct3ddxgi/dxgi-1-2-improvements
|
||||
// dxgi-1-6 may too high, only support win10 (2018)
|
||||
// https://docs.microsoft.com/zh-cn/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
|
||||
// DXGI_FORMAT_420_OPAQUE
|
||||
// IDXGIOutputDuplication::GetFrameDirtyRects and IDXGIOutputDuplication::GetFrameMoveRects
|
||||
// can help us update screen incrementally
|
||||
|
||||
/* // not supported on my PC, try in the future
|
||||
let format : Vec<DXGI_FORMAT> = vec![DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_420_OPAQUE];
|
||||
(*display.inner).DuplicateOutput1(
|
||||
device as *mut _,
|
||||
0 as UINT,
|
||||
2 as UINT,
|
||||
format.as_ptr(),
|
||||
&mut duplication
|
||||
)
|
||||
*/
|
||||
|
||||
// if above not work, I think below should not work either, try later
|
||||
// https://developer.nvidia.com/capture-sdk deprecated
|
||||
// examples using directx + nvideo sdk for GPU-accelerated video encoding/decoding
|
||||
// https://github.com/NVIDIA/video-sdk-samples
|
||||
});
|
||||
}
|
||||
|
||||
if let Err(err) = res {
|
||||
unsafe {
|
||||
if !device.is_null() {
|
||||
(*device).Release();
|
||||
}
|
||||
if !context.is_null() {
|
||||
(*context).Release();
|
||||
}
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
if !duplication.is_null() {
|
||||
unsafe {
|
||||
(*duplication).GetDesc(&mut desc);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Capturer {
|
||||
device,
|
||||
context,
|
||||
duplication,
|
||||
fastlane: desc.DesktopImageInSystemMemory == TRUE,
|
||||
surface: ptr::null_mut(),
|
||||
width: display.width() as usize,
|
||||
height: display.height() as usize,
|
||||
display,
|
||||
data: ptr::null(),
|
||||
len: 0,
|
||||
use_yuv,
|
||||
yuv: Vec::new(),
|
||||
gdi_capturer,
|
||||
gdi_buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_gdi(&self) -> bool {
|
||||
self.gdi_capturer.is_some()
|
||||
}
|
||||
|
||||
pub fn set_gdi(&mut self) -> bool {
|
||||
self.gdi_capturer = self.display.create_gdi();
|
||||
self.is_gdi()
|
||||
}
|
||||
|
||||
pub fn cancel_gdi(&mut self) {
|
||||
self.gdi_buffer = Vec::new();
|
||||
self.gdi_capturer.take();
|
||||
}
|
||||
|
||||
unsafe fn load_frame(&mut self, timeout: UINT) -> io::Result<()> {
|
||||
let mut frame = ptr::null_mut();
|
||||
let mut info = mem::MaybeUninit::uninit().assume_init();
|
||||
self.data = ptr::null();
|
||||
|
||||
wrap_hresult((*self.duplication).AcquireNextFrame(timeout, &mut info, &mut frame))?;
|
||||
|
||||
if *info.LastPresentTime.QuadPart() == 0 {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
|
||||
if self.fastlane {
|
||||
let mut rect = mem::MaybeUninit::uninit().assume_init();
|
||||
let res = wrap_hresult((*self.duplication).MapDesktopSurface(&mut rect));
|
||||
|
||||
(*frame).Release();
|
||||
|
||||
if let Err(err) = res {
|
||||
Err(err)
|
||||
} else {
|
||||
self.data = rect.pBits;
|
||||
self.len = self.height * rect.Pitch as usize;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
self.surface = ptr::null_mut();
|
||||
self.surface = self.ohgodwhat(frame)?;
|
||||
|
||||
let mut rect = mem::MaybeUninit::uninit().assume_init();
|
||||
wrap_hresult((*self.surface).Map(&mut rect, DXGI_MAP_READ))?;
|
||||
|
||||
self.data = rect.pBits;
|
||||
self.len = self.height * rect.Pitch as usize;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// copy from GPU memory to system memory
|
||||
unsafe fn ohgodwhat(&mut self, frame: *mut IDXGIResource) -> io::Result<*mut IDXGISurface> {
|
||||
let mut texture: *mut ID3D11Texture2D = ptr::null_mut();
|
||||
(*frame).QueryInterface(
|
||||
&IID_ID3D11Texture2D,
|
||||
&mut texture as *mut *mut _ as *mut *mut _,
|
||||
);
|
||||
|
||||
let mut texture_desc = mem::MaybeUninit::uninit().assume_init();
|
||||
(*texture).GetDesc(&mut texture_desc);
|
||||
|
||||
texture_desc.Usage = D3D11_USAGE_STAGING;
|
||||
texture_desc.BindFlags = 0;
|
||||
texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
texture_desc.MiscFlags = 0;
|
||||
|
||||
let mut readable = ptr::null_mut();
|
||||
let res = wrap_hresult((*self.device).CreateTexture2D(
|
||||
&mut texture_desc,
|
||||
ptr::null(),
|
||||
&mut readable,
|
||||
));
|
||||
|
||||
if let Err(err) = res {
|
||||
(*frame).Release();
|
||||
(*texture).Release();
|
||||
(*readable).Release();
|
||||
Err(err)
|
||||
} else {
|
||||
(*readable).SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM);
|
||||
|
||||
let mut surface = ptr::null_mut();
|
||||
(*readable).QueryInterface(
|
||||
&IID_IDXGISurface,
|
||||
&mut surface as *mut *mut _ as *mut *mut _,
|
||||
);
|
||||
|
||||
(*self.context).CopyResource(readable as *mut _, texture as *mut _);
|
||||
|
||||
(*frame).Release();
|
||||
(*texture).Release();
|
||||
(*readable).Release();
|
||||
Ok(surface)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn frame<'a>(&'a mut self, timeout: UINT) -> io::Result<&'a [u8]> {
|
||||
unsafe {
|
||||
// Release last frame.
|
||||
// No error checking needed because we don't care.
|
||||
// None of the errors crash anyway.
|
||||
let result = {
|
||||
if let Some(gdi_capturer) = &self.gdi_capturer {
|
||||
match gdi_capturer.frame(&mut self.gdi_buffer) {
|
||||
Ok(_) => &self.gdi_buffer,
|
||||
Err(err) => {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, err.to_string()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if self.fastlane {
|
||||
(*self.duplication).UnMapDesktopSurface();
|
||||
} else {
|
||||
if !self.surface.is_null() {
|
||||
(*self.surface).Unmap();
|
||||
(*self.surface).Release();
|
||||
self.surface = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
(*self.duplication).ReleaseFrame();
|
||||
self.load_frame(timeout)?;
|
||||
slice::from_raw_parts(self.data, self.len)
|
||||
}
|
||||
};
|
||||
Ok({
|
||||
if self.use_yuv {
|
||||
crate::common::bgra_to_i420(
|
||||
self.width as usize,
|
||||
self.height as usize,
|
||||
&result,
|
||||
&mut self.yuv,
|
||||
);
|
||||
&self.yuv[..]
|
||||
} else {
|
||||
result
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Capturer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.surface.is_null() {
|
||||
(*self.surface).Unmap();
|
||||
(*self.surface).Release();
|
||||
}
|
||||
if !self.duplication.is_null() {
|
||||
(*self.duplication).Release();
|
||||
}
|
||||
if !self.device.is_null() {
|
||||
(*self.device).Release();
|
||||
}
|
||||
if !self.context.is_null() {
|
||||
(*self.context).Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Displays {
|
||||
factory: *mut IDXGIFactory1,
|
||||
adapter: *mut IDXGIAdapter1,
|
||||
/// Index of the CURRENT adapter.
|
||||
nadapter: UINT,
|
||||
/// Index of the NEXT display to fetch.
|
||||
ndisplay: UINT,
|
||||
}
|
||||
|
||||
impl Displays {
|
||||
pub fn new() -> io::Result<Displays> {
|
||||
let mut factory = ptr::null_mut();
|
||||
wrap_hresult(unsafe { CreateDXGIFactory1(&IID_IDXGIFactory1, &mut factory) })?;
|
||||
|
||||
let factory = factory as *mut IDXGIFactory1;
|
||||
let mut adapter = ptr::null_mut();
|
||||
unsafe {
|
||||
// On error, our adapter is null, so it's fine.
|
||||
(*factory).EnumAdapters1(0, &mut adapter);
|
||||
};
|
||||
|
||||
Ok(Displays {
|
||||
factory,
|
||||
adapter,
|
||||
nadapter: 0,
|
||||
ndisplay: 0,
|
||||
})
|
||||
}
|
||||
|
||||
// No Adapter => Some(None)
|
||||
// Non-Empty Adapter => Some(Some(OUTPUT))
|
||||
// End of Adapter => None
|
||||
fn read_and_invalidate(&mut self) -> Option<Option<Display>> {
|
||||
// If there is no adapter, there is nothing left for us to do.
|
||||
|
||||
if self.adapter.is_null() {
|
||||
return Some(None);
|
||||
}
|
||||
|
||||
// Otherwise, we get the next output of the current adapter.
|
||||
|
||||
let output = unsafe {
|
||||
let mut output = ptr::null_mut();
|
||||
(*self.adapter).EnumOutputs(self.ndisplay, &mut output);
|
||||
output
|
||||
};
|
||||
|
||||
// If the current adapter is done, we free it.
|
||||
// We return None so the caller gets the next adapter and tries again.
|
||||
|
||||
if output.is_null() {
|
||||
unsafe {
|
||||
(*self.adapter).Release();
|
||||
self.adapter = ptr::null_mut();
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Advance to the next display.
|
||||
|
||||
self.ndisplay += 1;
|
||||
|
||||
// We get the display's details.
|
||||
|
||||
let desc = unsafe {
|
||||
let mut desc = mem::MaybeUninit::uninit().assume_init();
|
||||
(*output).GetDesc(&mut desc);
|
||||
desc
|
||||
};
|
||||
|
||||
// We cast it up to the version needed for desktop duplication.
|
||||
|
||||
let mut inner: *mut IDXGIOutput1 = ptr::null_mut();
|
||||
unsafe {
|
||||
(*output).QueryInterface(&IID_IDXGIOutput1, &mut inner as *mut *mut _ as *mut *mut _);
|
||||
(*output).Release();
|
||||
}
|
||||
|
||||
// If it's null, we have an error.
|
||||
// So we act like the adapter is done.
|
||||
|
||||
if inner.is_null() {
|
||||
unsafe {
|
||||
(*self.adapter).Release();
|
||||
self.adapter = ptr::null_mut();
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(*self.adapter).AddRef();
|
||||
}
|
||||
|
||||
Some(Some(Display {
|
||||
inner,
|
||||
adapter: self.adapter,
|
||||
desc,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Displays {
|
||||
type Item = Display;
|
||||
fn next(&mut self) -> Option<Display> {
|
||||
if let Some(res) = self.read_and_invalidate() {
|
||||
res
|
||||
} else {
|
||||
// We need to replace the adapter.
|
||||
|
||||
self.ndisplay = 0;
|
||||
self.nadapter += 1;
|
||||
|
||||
self.adapter = unsafe {
|
||||
let mut adapter = ptr::null_mut();
|
||||
(*self.factory).EnumAdapters1(self.nadapter, &mut adapter);
|
||||
adapter
|
||||
};
|
||||
|
||||
if let Some(res) = self.read_and_invalidate() {
|
||||
res
|
||||
} else {
|
||||
// All subsequent adapters will also be empty.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Displays {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(*self.factory).Release();
|
||||
if !self.adapter.is_null() {
|
||||
(*self.adapter).Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Display {
|
||||
inner: *mut IDXGIOutput1,
|
||||
adapter: *mut IDXGIAdapter1,
|
||||
desc: DXGI_OUTPUT_DESC,
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn width(&self) -> LONG {
|
||||
self.desc.DesktopCoordinates.right - self.desc.DesktopCoordinates.left
|
||||
}
|
||||
|
||||
pub fn height(&self) -> LONG {
|
||||
self.desc.DesktopCoordinates.bottom - self.desc.DesktopCoordinates.top
|
||||
}
|
||||
|
||||
pub fn attached_to_desktop(&self) -> bool {
|
||||
self.desc.AttachedToDesktop != 0
|
||||
}
|
||||
|
||||
pub fn rotation(&self) -> DXGI_MODE_ROTATION {
|
||||
self.desc.Rotation
|
||||
}
|
||||
|
||||
fn create_gdi(&self) -> Option<CapturerGDI> {
|
||||
if let Ok(res) = CapturerGDI::new(self.name(), self.width(), self.height()) {
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hmonitor(&self) -> HMONITOR {
|
||||
self.desc.Monitor
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &[u16] {
|
||||
let s = &self.desc.DeviceName;
|
||||
let i = s.iter().position(|&x| x == 0).unwrap_or(s.len());
|
||||
&s[..i]
|
||||
}
|
||||
|
||||
pub fn is_online(&self) -> bool {
|
||||
self.desc.AttachedToDesktop != 0
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> (LONG, LONG) {
|
||||
(
|
||||
self.desc.DesktopCoordinates.left,
|
||||
self.desc.DesktopCoordinates.top,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Display {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(*self.inner).Release();
|
||||
(*self.adapter).Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_hresult(x: HRESULT) -> io::Result<()> {
|
||||
use std::io::ErrorKind::*;
|
||||
Err((match x {
|
||||
S_OK => return Ok(()),
|
||||
DXGI_ERROR_ACCESS_LOST => ConnectionReset,
|
||||
DXGI_ERROR_WAIT_TIMEOUT => TimedOut,
|
||||
DXGI_ERROR_INVALID_CALL => InvalidData,
|
||||
E_ACCESSDENIED => PermissionDenied,
|
||||
DXGI_ERROR_UNSUPPORTED => ConnectionRefused,
|
||||
DXGI_ERROR_NOT_CURRENTLY_AVAILABLE => Interrupted,
|
||||
DXGI_ERROR_SESSION_DISCONNECTED => ConnectionAborted,
|
||||
E_INVALIDARG => InvalidInput,
|
||||
_ => {
|
||||
// 0x8000ffff https://www.auslogics.com/en/articles/windows-10-update-error-0x8000ffff-fixed/
|
||||
return Err(io::Error::new(Other, format!("Error code: {:#X}", x)));
|
||||
}
|
||||
})
|
||||
.into())
|
||||
}
|
||||
Reference in New Issue
Block a user