Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gdigrab display enumeration #36

Merged
merged 5 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 129 additions & 48 deletions SeeShark/Device/DisplayManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using SeeShark.Interop.Windows;
using SeeShark.Interop.X11;

namespace SeeShark.Device
Expand Down Expand Up @@ -34,64 +36,143 @@ public override Display GetDevice(DisplayInfo info, VideoInputOptions? options =
/// </summary>
protected override DisplayInfo[] EnumerateDevices()
{
if (InputFormat == DeviceInputFormat.X11Grab)
switch (InputFormat)
{
unsafe
{
IntPtr display = XLib.XOpenDisplay(null);
IntPtr rootWindow = XLib.XDefaultRootWindow(display);
XRRMonitorInfo[] monitors = getXRandrDisplays(display, rootWindow);
case DeviceInputFormat.X11Grab:
return enumerateDevicesX11();
case DeviceInputFormat.GdiGrab:
return enumerateDevicesGdi();
default:
return base.EnumerateDevices();
}
}

DisplayInfo[] info = new DisplayInfo[monitors.Length + 1];
private DisplayInfo[] enumerateDevicesX11()
{
unsafe
{
IntPtr display = XLib.XOpenDisplay(null);
IntPtr rootWindow = XLib.XDefaultRootWindow(display);
XRRMonitorInfo[] monitors = getXRandrDisplays(display, rootWindow);

int compositeLeft = int.MaxValue;
int compositeRight = 0;
int compositeTop = int.MaxValue;
int compositeBottom = 0;
DisplayInfo[] info = new DisplayInfo[monitors.Length + 1];

for (int i = 0; i < monitors.Length; i++)
{
XRRMonitorInfo monitor = monitors[i];
info[i + 1] = new DisplayInfo
{
Name = $"Display {i}",
Path = ":0",
X = monitor.X,
Y = monitor.Y,
Width = monitor.Width,
Height = monitor.Height,
Primary = monitor.Primary > 0,
};

if (monitor.X < compositeLeft)
compositeLeft = monitor.X;

if (monitor.X + monitor.Width > compositeRight)
compositeRight = monitor.X + monitor.Width;

if (monitor.Y < compositeTop)
compositeTop = monitor.Y;

if (monitor.Y + monitor.Height > compositeBottom)
compositeBottom = monitor.Y + monitor.Height;
}

info[0] = new DisplayInfo
int compositeLeft = int.MaxValue;
int compositeRight = int.MinValue;
int compositeTop = int.MaxValue;
int compositeBottom = int.MinValue;

for (int i = 0; i < monitors.Length; i++)
{
XRRMonitorInfo monitor = monitors[i];
info[i + 1] = new DisplayInfo
{
Name = $"Composite X11 Display",
Name = $"Display {i}",
Path = ":0",
X = compositeLeft,
Y = compositeTop,
Width = compositeRight - compositeLeft,
Height = compositeBottom - compositeTop,
Primary = false,
IsComposite = true
X = monitor.X,
Y = monitor.Y,
Width = monitor.Width,
Height = monitor.Height,
Primary = monitor.Primary > 0,
};
return info;

if (monitor.X < compositeLeft)
compositeLeft = monitor.X;

if (monitor.X + monitor.Width > compositeRight)
compositeRight = monitor.X + monitor.Width;

if (monitor.Y < compositeTop)
compositeTop = monitor.Y;

if (monitor.Y + monitor.Height > compositeBottom)
compositeBottom = monitor.Y + monitor.Height;
}

info[0] = new DisplayInfo
{
Name = $"Composite X11 Display",
Path = ":0",
X = compositeLeft,
Y = compositeTop,
Width = compositeRight - compositeLeft,
Height = compositeBottom - compositeTop,
Primary = false,
IsComposite = true
};
return info;
}
}

private DisplayInfo[] enumerateDevicesGdi()
{
var displayInfo = new List<DisplayInfo>();

int count = 0;

int compositeLeft = int.MaxValue;
int compositeRight = int.MinValue;
int compositeTop = int.MaxValue;
int compositeBottom = int.MinValue;

bool MonitorDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData)
{
var monitorInfo = new MonitorInfoEx();
monitorInfo.size = (uint)Marshal.SizeOf(monitorInfo);

if (User32.GetMonitorInfo(hMonitor, ref monitorInfo))
{
var info = new DevMode();
User32.EnumDisplaySettings(monitorInfo.deviceName, -1, ref info);

var d = new DISPLAY_DEVICE();
d.cb = Marshal.SizeOf(d);
User32.EnumDisplayDevices(monitorInfo.deviceName, 0, ref d, 0);

displayInfo.Add(new DisplayInfo
{
Name = d.DeviceString,
Path = "desktop",
X = info.dmPositionX,
Y = info.dmPositionY,
Width = info.dmPelsWidth,
Height = info.dmPelsHeight,
Primary = count == 0
});
count++;

if (info.dmPositionX < compositeLeft)
compositeLeft = info.dmPositionX;

if (info.dmPositionX + info.dmPelsWidth > compositeRight)
compositeRight = (info.dmPositionX + info.dmPelsWidth);

if (info.dmPositionY < compositeTop)
compositeTop = info.dmPositionY;

if (info.dmPositionY + info.dmPelsHeight > compositeBottom)
compositeBottom = (info.dmPositionY + info.dmPelsHeight);

}
return true;
}

return base.EnumerateDevices();
User32.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, MonitorDelegate, IntPtr.Zero);

displayInfo.Insert(0, new DisplayInfo
{
Name = $"Composite GDI Display",
Path = "desktop",
X = compositeLeft,
Y = compositeTop,
Width = compositeRight - compositeLeft,
Height = compositeBottom - compositeTop,
Primary = false,
IsComposite = true
});

return displayInfo.ToArray();
//return CollectionsMarshal.AsSpan(displayInfo);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this comment for?

}

private unsafe XRRMonitorInfo[] getXRandrDisplays(IntPtr display, IntPtr rootWindow)
Expand Down
149 changes: 149 additions & 0 deletions SeeShark/Interop/Windows/User32.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright (c) The Vignette Authors
// This file is part of SeeShark.
// SeeShark is licensed under the BSD 3-Clause License. See LICENSE for details.

using System;
using System.Runtime.InteropServices;

namespace SeeShark.Interop.Windows
{
[StructLayout(LayoutKind.Sequential)]
internal struct DevMode
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
internal string dmDeviceName;
internal short dmSpecVersion;
internal short dmDriverVersion;
internal short dmSize;
internal short dmDriverExtra;
internal int dmFields;
internal int dmPositionX;
internal int dmPositionY;
internal int dmDisplayOrientation;
internal int dmDisplayFixedOutput;
internal short dmColor;
internal short dmDuplex;
internal short dmYResolution;
internal short dmTTOption;
internal short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
internal string dmFormName;
internal short dmLogPixels;
internal int dmBitsPerPel;
internal int dmPelsWidth;
internal int dmPelsHeight;
internal int dmDisplayFlags;
internal int dmDisplayFrequency;
internal int dmICMMethod;
internal int dmICMIntent;
internal int dmMediaType;
internal int dmDitherType;
internal int dmReserved1;
internal int dmReserved2;
internal int dmPanningWidth;
internal int dmPanningHeight;
}

[StructLayout(LayoutKind.Sequential)]
internal struct Rect
{
internal int left;
internal int top;
internal int right;
internal int bottom;
}

internal enum MONITORINFOF : ulong
{
PRIMARY = 1
}

[StructLayout(LayoutKind.Sequential)]
internal struct MonitorInfoEx
{
internal uint size;
internal Rect monitor;
internal Rect work;
internal uint flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
internal string deviceName;
}

internal enum DpiType
{
Effective,
Angular,
Raw
}


[Flags]
internal enum DisplayDeviceStateFlags : int
{
/// <summary>
/// The device is part of the desktop.
/// </summary>
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
/// <summary>The device is part of the desktop.</summary>
PrimaryDevice = 0x4,
/// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
MirroringDriver = 0x8,
/// <summary>The device is VGA compatible.</summary>
VGACompatible = 0x10,
/// <summary>The device is removable; it cannot be the primary display.</summary>
Removable = 0x20,
/// <summary>The device has more display modes than its output devices support.</summary>
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000
}


#pragma warning disable CA1815

// This is never compared in the code, so we can suppress the warning.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct DISPLAY_DEVICE
{
[MarshalAs(UnmanagedType.U4)]
internal int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
internal string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string DeviceString;
[MarshalAs(UnmanagedType.U4)]
internal DisplayDeviceStateFlags StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
internal string DeviceKey;
}
#pragma warning restore CA1815

internal static partial class User32
{
internal delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr dwData);

[DllImport("user32.dll")]
internal static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);

[DllImport("user32.dll")]
internal static extern bool GetMonitorInfo(IntPtr hmon, ref MonitorInfoEx mi);

#pragma warning disable CA2101
[DllImport("user32.dll", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DevMode devMode);

[DllImport("user32.dll", CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);

#pragma warning restore CA2101

[DllImport("Shcore.dll")]
internal static extern int GetDpiForMonitor(IntPtr hmon, DpiType dpiType, out uint dpiX, out uint dpiY);

[DllImport("Shcore.dll")]
internal static extern int SetProcessDpiAwareness(int awareness);
}
}