2017-02-26 19:23:31 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using WindowsDisplayAPI.DisplayConfig;
|
|
|
|
|
using NvAPIWrapper.Mosaic;
|
|
|
|
|
using NvAPIWrapper.Native.Interfaces.Mosaic;
|
|
|
|
|
|
|
|
|
|
namespace HeliosDisplayManagement.Shared.NVIDIA
|
|
|
|
|
{
|
|
|
|
|
public class SurroundTopology : IEquatable<SurroundTopology>
|
|
|
|
|
{
|
|
|
|
|
public SurroundTopology(GridTopology topology)
|
|
|
|
|
{
|
|
|
|
|
Rows = topology.Rows;
|
|
|
|
|
Columns = topology.Columns;
|
|
|
|
|
Resolution = new Size(topology.Resolution.Width, topology.Resolution.Height);
|
|
|
|
|
ColorDepth = topology.Resolution.ColorDepth;
|
|
|
|
|
Frequency = topology.Frequency;
|
|
|
|
|
Displays =
|
|
|
|
|
topology.Displays.Where(
|
|
|
|
|
display =>
|
2018-10-20 00:27:25 +00:00
|
|
|
|
Resolution.Width > display.Overlap.HorizontalOverlap &&
|
|
|
|
|
Resolution.Height > display.Overlap.VerticalOverlap)
|
2017-02-26 19:23:31 +00:00
|
|
|
|
.Select(display => new SurroundTopologyDisplay(display))
|
|
|
|
|
.ToArray();
|
|
|
|
|
ApplyWithBezelCorrectedResolution = topology.ApplyWithBezelCorrectedResolution;
|
|
|
|
|
ImmersiveGaming = topology.ImmersiveGaming;
|
|
|
|
|
BaseMosaicPanoramic = topology.BaseMosaicPanoramic;
|
|
|
|
|
DriverReloadAllowed = topology.DriverReloadAllowed;
|
|
|
|
|
AcceleratePrimaryDisplay = topology.AcceleratePrimaryDisplay;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SurroundTopology()
|
|
|
|
|
{
|
2017-08-10 14:21:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool AcceleratePrimaryDisplay { get; set; }
|
|
|
|
|
public bool ApplyWithBezelCorrectedResolution { get; set; }
|
|
|
|
|
public bool BaseMosaicPanoramic { get; set; }
|
|
|
|
|
public int ColorDepth { get; set; }
|
|
|
|
|
public int Columns { get; set; }
|
|
|
|
|
public SurroundTopologyDisplay[] Displays { get; set; }
|
|
|
|
|
public bool DriverReloadAllowed { get; set; }
|
|
|
|
|
public int Frequency { get; set; }
|
|
|
|
|
public bool ImmersiveGaming { get; set; }
|
|
|
|
|
public Size Resolution { get; set; }
|
|
|
|
|
public int Rows { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public bool Equals(SurroundTopology other)
|
|
|
|
|
{
|
2018-10-20 00:27:25 +00:00
|
|
|
|
if (ReferenceEquals(null, other))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ReferenceEquals(this, other))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return AcceleratePrimaryDisplay == other.AcceleratePrimaryDisplay &&
|
|
|
|
|
ApplyWithBezelCorrectedResolution == other.ApplyWithBezelCorrectedResolution &&
|
|
|
|
|
BaseMosaicPanoramic == other.BaseMosaicPanoramic &&
|
|
|
|
|
ColorDepth == other.ColorDepth &&
|
|
|
|
|
Columns == other.Columns &&
|
|
|
|
|
Displays.Length == other.Displays.Length &&
|
2017-08-10 14:21:45 +00:00
|
|
|
|
Displays.All(display => other.Displays.Contains(display)) &&
|
2018-10-20 00:27:25 +00:00
|
|
|
|
DriverReloadAllowed == other.DriverReloadAllowed &&
|
|
|
|
|
Frequency == other.Frequency &&
|
|
|
|
|
ImmersiveGaming == other.ImmersiveGaming &&
|
|
|
|
|
Resolution.Equals(other.Resolution) &&
|
|
|
|
|
Rows == other.Rows;
|
2017-02-26 19:23:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-20 00:20:18 +00:00
|
|
|
|
// ReSharper disable once ExcessiveIndentation
|
2017-02-26 19:23:31 +00:00
|
|
|
|
public static SurroundTopology FromPathTargetInfo(PathTargetInfo pathTargetInfo)
|
|
|
|
|
{
|
|
|
|
|
// We go through the code if only the path belongs to a NVIDIA virtual surround display
|
2018-10-20 00:20:18 +00:00
|
|
|
|
// Bug: Sometimes fails to detect as Surround display. Probably a bug on the NVAPI part. Should we try to resolve every target info to be sure?
|
|
|
|
|
if (pathTargetInfo.DisplayTarget.EDIDManufactureCode != "NVS" &&
|
|
|
|
|
pathTargetInfo.DisplayTarget.FriendlyName != "NV Surround")
|
|
|
|
|
{
|
2017-02-26 19:23:31 +00:00
|
|
|
|
return null;
|
2018-10-20 00:20:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var correspondingWindowsPathInfo =
|
2017-08-10 14:21:45 +00:00
|
|
|
|
PathInfo.GetAllPaths()
|
|
|
|
|
.FirstOrDefault(
|
|
|
|
|
info =>
|
|
|
|
|
info.TargetsInfo.Any(
|
2018-10-20 00:27:25 +00:00
|
|
|
|
targetInfo =>
|
|
|
|
|
targetInfo.DisplayTarget?.Equals(pathTargetInfo.DisplayTarget) == true));
|
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
if (correspondingWindowsPathInfo != null)
|
|
|
|
|
{
|
|
|
|
|
// If position is same, then the two paths are equal, after all position is whats important in path sources
|
|
|
|
|
var correspondingNvidiaPathInfo =
|
|
|
|
|
NvAPIWrapper.Display.PathInfo.GetDisplaysConfig()
|
|
|
|
|
.FirstOrDefault(
|
|
|
|
|
info =>
|
2018-10-20 00:27:25 +00:00
|
|
|
|
info.Position.X == correspondingWindowsPathInfo.Position.X &&
|
|
|
|
|
info.Position.Y == correspondingWindowsPathInfo.Position.Y &&
|
|
|
|
|
info.Resolution.Width == correspondingWindowsPathInfo.Resolution.Width &&
|
|
|
|
|
info.Resolution.Height == correspondingWindowsPathInfo.Resolution.Height);
|
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
if (correspondingNvidiaPathInfo != null)
|
|
|
|
|
{
|
2018-10-20 00:20:18 +00:00
|
|
|
|
// We now assume that there is only one target for a NVS path, in an other word, for now, it is not possible to have a cloned surround display
|
2017-02-26 19:23:31 +00:00
|
|
|
|
var correspondingNvidiaTargetInfo = correspondingNvidiaPathInfo.TargetsInfo.FirstOrDefault();
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
if (correspondingNvidiaTargetInfo != null)
|
|
|
|
|
{
|
2018-10-20 00:20:18 +00:00
|
|
|
|
// We also assume that the NVS monitor uses a similar display id to one of real physical monitors
|
2017-02-26 19:23:31 +00:00
|
|
|
|
var correspondingNvidiaTopology =
|
|
|
|
|
GridTopology.GetGridTopologies()
|
|
|
|
|
.FirstOrDefault(
|
2018-10-20 00:20:18 +00:00
|
|
|
|
topology => topology.Displays.Any(display =>
|
|
|
|
|
display.DisplayDevice.DisplayId ==
|
|
|
|
|
correspondingNvidiaTargetInfo.DisplayDevice.DisplayId));
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
if (correspondingNvidiaTopology != null)
|
2018-10-20 00:27:25 +00:00
|
|
|
|
{
|
2017-02-26 19:23:31 +00:00
|
|
|
|
return new SurroundTopology(correspondingNvidiaTopology);
|
2018-10-20 00:27:25 +00:00
|
|
|
|
}
|
2017-02-26 19:23:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
// ignored
|
|
|
|
|
}
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
return null;
|
|
|
|
|
}
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
public static bool operator ==(SurroundTopology left, SurroundTopology right)
|
|
|
|
|
{
|
2018-10-20 00:17:32 +00:00
|
|
|
|
return Equals(left, right) || left?.Equals(right) == true;
|
2017-02-26 19:23:31 +00:00
|
|
|
|
}
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
public static bool operator !=(SurroundTopology left, SurroundTopology right)
|
|
|
|
|
{
|
2018-10-20 00:17:32 +00:00
|
|
|
|
return !(left == right);
|
2017-02-26 19:23:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
{
|
2018-10-20 00:27:25 +00:00
|
|
|
|
if (ReferenceEquals(null, obj))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ReferenceEquals(this, obj))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (obj.GetType() != GetType())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
return Equals((SurroundTopology) obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public override int GetHashCode()
|
|
|
|
|
{
|
|
|
|
|
unchecked
|
|
|
|
|
{
|
|
|
|
|
var hashCode = AcceleratePrimaryDisplay.GetHashCode();
|
2018-10-20 00:27:25 +00:00
|
|
|
|
hashCode = (hashCode * 397) ^ ApplyWithBezelCorrectedResolution.GetHashCode();
|
|
|
|
|
hashCode = (hashCode * 397) ^ BaseMosaicPanoramic.GetHashCode();
|
|
|
|
|
hashCode = (hashCode * 397) ^ ColorDepth;
|
|
|
|
|
hashCode = (hashCode * 397) ^ Columns;
|
|
|
|
|
hashCode = (hashCode * 397) ^ (Displays?.GetHashCode() ?? 0);
|
|
|
|
|
hashCode = (hashCode * 397) ^ DriverReloadAllowed.GetHashCode();
|
|
|
|
|
hashCode = (hashCode * 397) ^ Frequency;
|
|
|
|
|
hashCode = (hashCode * 397) ^ ImmersiveGaming.GetHashCode();
|
|
|
|
|
hashCode = (hashCode * 397) ^ Resolution.GetHashCode();
|
|
|
|
|
hashCode = (hashCode * 397) ^ Rows;
|
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
return hashCode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return $"SurroundTopology[{Rows}, {Columns}] ({Resolution.Width}, {Resolution.Height}) @ {Frequency}";
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-20 00:08:30 +00:00
|
|
|
|
// ReSharper disable once ExcessiveIndentation
|
2017-02-26 19:23:31 +00:00
|
|
|
|
public GridTopology ToGridTopology()
|
|
|
|
|
{
|
|
|
|
|
var gridTopology = new GridTopology(Rows, Columns,
|
|
|
|
|
Displays.Select(display => display.ToGridTopologyDisplay()).ToArray())
|
|
|
|
|
{
|
|
|
|
|
ApplyWithBezelCorrectedResolution = ApplyWithBezelCorrectedResolution,
|
|
|
|
|
ImmersiveGaming = ImmersiveGaming,
|
|
|
|
|
BaseMosaicPanoramic = BaseMosaicPanoramic,
|
|
|
|
|
DriverReloadAllowed = DriverReloadAllowed,
|
|
|
|
|
AcceleratePrimaryDisplay = AcceleratePrimaryDisplay
|
|
|
|
|
};
|
|
|
|
|
IDisplaySettings bestDisplaySettings = null;
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
foreach (var displaySetting in gridTopology.GetPossibleDisplaySettings())
|
2018-10-20 00:27:25 +00:00
|
|
|
|
{
|
|
|
|
|
if (displaySetting.Width == Resolution.Width &&
|
|
|
|
|
displaySetting.Height == Resolution.Height)
|
2017-02-26 19:23:31 +00:00
|
|
|
|
{
|
|
|
|
|
if (displaySetting.BitsPerPixel == ColorDepth)
|
|
|
|
|
{
|
|
|
|
|
if (displaySetting.Frequency == Frequency)
|
|
|
|
|
{
|
|
|
|
|
bestDisplaySettings = displaySetting;
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2018-10-20 00:27:25 +00:00
|
|
|
|
|
|
|
|
|
if (bestDisplaySettings == null || displaySetting.Frequency > bestDisplaySettings.Frequency)
|
|
|
|
|
{
|
2017-02-26 19:23:31 +00:00
|
|
|
|
bestDisplaySettings = displaySetting;
|
2018-10-20 00:27:25 +00:00
|
|
|
|
}
|
2017-02-26 19:23:31 +00:00
|
|
|
|
}
|
2018-10-20 00:27:25 +00:00
|
|
|
|
else if (bestDisplaySettings == null ||
|
|
|
|
|
displaySetting.BitsPerPixel > bestDisplaySettings.BitsPerPixel)
|
2017-02-26 19:23:31 +00:00
|
|
|
|
{
|
|
|
|
|
bestDisplaySettings = displaySetting;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-20 00:27:25 +00:00
|
|
|
|
else if (bestDisplaySettings == null ||
|
|
|
|
|
displaySetting.Width * displaySetting.Height >
|
|
|
|
|
bestDisplaySettings.Width * bestDisplaySettings.Height)
|
2017-02-26 19:23:31 +00:00
|
|
|
|
{
|
|
|
|
|
bestDisplaySettings = displaySetting;
|
|
|
|
|
}
|
2018-10-20 00:27:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
if (bestDisplaySettings != null)
|
2018-10-20 00:27:25 +00:00
|
|
|
|
{
|
2017-02-26 19:23:31 +00:00
|
|
|
|
gridTopology.SetDisplaySettings(bestDisplaySettings);
|
2018-10-20 00:27:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-26 19:23:31 +00:00
|
|
|
|
return gridTopology;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|