mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
Renamed app to HeliosPlus namespace so that the updated changes don't interfere with HeliosDisplayManagement if that is also installed. The fact that I've changed so much of the app means that my changes would be unlikely to be accepted by Soroush, so I'm best to release this work in a similar way to other projects like Notepad++, by keeping the same root name, and adding a plus. I've also changed the Shortcut form to put all the games in a single list to reduce the number of clicks a user has to do in order for them to create a shortcut. I have begun to prepare the form so that it will support multiple game libraries, but now I am at a point that I need to fix the Steam and Uplay game detection mechanisms so that they report the correct information for the lv_games list view.
265 lines
11 KiB
C#
265 lines
11 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using WindowsDisplayAPI.DisplayConfig;
|
|
using NvAPIWrapper.Mosaic;
|
|
using NvAPIWrapper.Native.Interfaces.Mosaic;
|
|
|
|
namespace HeliosPlus.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 =>
|
|
Resolution.Width > display.Overlap.HorizontalOverlap &&
|
|
Resolution.Height > display.Overlap.VerticalOverlap)
|
|
.Select(display => new SurroundTopologyDisplay(display))
|
|
.ToArray();
|
|
ApplyWithBezelCorrectedResolution = topology.ApplyWithBezelCorrectedResolution;
|
|
ImmersiveGaming = topology.ImmersiveGaming;
|
|
BaseMosaicPanoramic = topology.BaseMosaicPanoramic;
|
|
DriverReloadAllowed = topology.DriverReloadAllowed;
|
|
AcceleratePrimaryDisplay = topology.AcceleratePrimaryDisplay;
|
|
}
|
|
|
|
public SurroundTopology()
|
|
{
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 &&
|
|
Displays.All(display => other.Displays.Contains(display)) &&
|
|
DriverReloadAllowed == other.DriverReloadAllowed &&
|
|
Frequency == other.Frequency &&
|
|
ImmersiveGaming == other.ImmersiveGaming &&
|
|
Resolution.Equals(other.Resolution) &&
|
|
Rows == other.Rows;
|
|
}
|
|
|
|
// ReSharper disable once ExcessiveIndentation
|
|
public static SurroundTopology FromPathTargetInfo(PathTargetInfo pathTargetInfo)
|
|
{
|
|
// We go through the code if only the path belongs to a NVIDIA virtual surround display
|
|
// TODO: Should we try to resolve every target info to be sure?
|
|
if (!string.Equals(
|
|
pathTargetInfo.DisplayTarget.EDIDManufactureCode,
|
|
"NVS",
|
|
StringComparison.InvariantCultureIgnoreCase
|
|
) &&
|
|
!string.Equals(
|
|
pathTargetInfo.DisplayTarget.FriendlyName,
|
|
"NV Surround",
|
|
StringComparison.InvariantCultureIgnoreCase
|
|
) &&
|
|
!pathTargetInfo.DisplayTarget.DevicePath.ToLower().Contains("&UID5120".ToLower()))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Get parent DisplayConfig PathInfo by checking display targets
|
|
var correspondingWindowsPathInfo =
|
|
PathInfo.GetActivePaths()
|
|
.FirstOrDefault(
|
|
info =>
|
|
info.TargetsInfo.Any(
|
|
targetInfo =>
|
|
targetInfo.DisplayTarget == pathTargetInfo.DisplayTarget));
|
|
|
|
if (correspondingWindowsPathInfo != null)
|
|
{
|
|
// Get corresponding NvAPI PathInfo
|
|
// 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 =>
|
|
info.Position.X == correspondingWindowsPathInfo.Position.X &&
|
|
info.Position.Y == correspondingWindowsPathInfo.Position.Y &&
|
|
info.Resolution.Width == correspondingWindowsPathInfo.Resolution.Width &&
|
|
info.Resolution.Height == correspondingWindowsPathInfo.Resolution.Height);
|
|
|
|
if (correspondingNvidiaPathInfo != null)
|
|
{
|
|
// Get corresponding NvAPI PathTargetInfo
|
|
// We now assume that there is only one target for a NvAPI PathInfo, in an other word, for now, it is not possible to have a cloned surround display
|
|
var correspondingNvidiaTargetInfo = correspondingNvidiaPathInfo.TargetsInfo.FirstOrDefault();
|
|
|
|
if (correspondingNvidiaTargetInfo != null)
|
|
{
|
|
// Get corresponding NvAPI Grid Topology
|
|
// We also assume that the NVS monitor uses a similar display id to one of real physical monitors
|
|
var correspondingNvidiaTopology =
|
|
GridTopology.GetGridTopologies()
|
|
.FirstOrDefault(
|
|
topology => topology.Displays.Any(display =>
|
|
display.DisplayDevice == correspondingNvidiaTargetInfo.DisplayDevice));
|
|
|
|
if (correspondingNvidiaTopology != null)
|
|
{
|
|
return new SurroundTopology(correspondingNvidiaTopology);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// ignored
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static bool operator ==(SurroundTopology left, SurroundTopology right)
|
|
{
|
|
return Equals(left, right) || left?.Equals(right) == true;
|
|
}
|
|
|
|
public static bool operator !=(SurroundTopology left, SurroundTopology right)
|
|
{
|
|
return !(left == right);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(null, obj))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (ReferenceEquals(this, obj))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (obj.GetType() != GetType())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return Equals((SurroundTopology) obj);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override int GetHashCode()
|
|
{
|
|
unchecked
|
|
{
|
|
var hashCode = AcceleratePrimaryDisplay.GetHashCode();
|
|
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;
|
|
|
|
return hashCode;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override string ToString()
|
|
{
|
|
return $"SurroundTopology[{Rows}, {Columns}] ({Resolution.Width}, {Resolution.Height}) @ {Frequency}";
|
|
}
|
|
|
|
// ReSharper disable once ExcessiveIndentation
|
|
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;
|
|
|
|
foreach (var displaySetting in gridTopology.GetPossibleDisplaySettings())
|
|
{
|
|
if (displaySetting.Width == Resolution.Width &&
|
|
displaySetting.Height == Resolution.Height)
|
|
{
|
|
if (displaySetting.BitsPerPixel == ColorDepth)
|
|
{
|
|
if (displaySetting.Frequency == Frequency)
|
|
{
|
|
bestDisplaySettings = displaySetting;
|
|
|
|
break;
|
|
}
|
|
|
|
if (bestDisplaySettings == null || displaySetting.Frequency > bestDisplaySettings.Frequency)
|
|
{
|
|
bestDisplaySettings = displaySetting;
|
|
}
|
|
}
|
|
else if (bestDisplaySettings == null ||
|
|
displaySetting.BitsPerPixel > bestDisplaySettings.BitsPerPixel)
|
|
{
|
|
bestDisplaySettings = displaySetting;
|
|
}
|
|
}
|
|
else if (bestDisplaySettings == null ||
|
|
displaySetting.Width * displaySetting.Height >
|
|
bestDisplaySettings.Width * bestDisplaySettings.Height)
|
|
{
|
|
bestDisplaySettings = displaySetting;
|
|
}
|
|
}
|
|
|
|
if (bestDisplaySettings != null)
|
|
{
|
|
gridTopology.SetDisplaySettings(bestDisplaySettings);
|
|
}
|
|
|
|
return gridTopology;
|
|
}
|
|
}
|
|
} |