DisplayMagician/HeliosPlus.Shared/ProfileRepository.cs
Terry MacDonald ca7c696b06 Bypassed NVIDIA Grid Topology if not needed
Added some logic to check if either the from or to
profile conatins an NVIDIA surround screen. If not,
then there is no need to apply a GridTopology as it
is all single devices! Saves up to 15 seconds on
swap over.
2020-10-07 21:58:05 +13:00

936 lines
39 KiB
C#

using HeliosPlus.Shared;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Drawing.IconLib;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using WindowsDisplayAPI.DisplayConfig;
using HeliosPlus.Shared.Resources;
using Newtonsoft.Json;
using NvAPIWrapper.Mosaic;
using NvAPIWrapper.Native.Mosaic;
using HeliosPlus.Shared.Topology;
using System.Drawing;
using System.Drawing.Imaging;
using WindowsDisplayAPI;
using System.Diagnostics;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Resources;
using System.Net.NetworkInformation;
using NvAPIWrapper.Mosaic;
using NvAPIWrapper.Native.Mosaic;
using HeliosPlus.Shared.Topology;
namespace HeliosPlus.Shared
{
public static class ProfileRepository
{
#region Class Variables
// Common items to the class
private static List<ProfileItem> _allProfiles = new List<ProfileItem>();
private static bool _profilesLoaded = false;
public static Version Version = new Version(1, 0, 0);
// Other constants that are useful
public static string AppDataPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "HeliosPlus");
public static string AppIconPath = System.IO.Path.Combine(AppDataPath, $"Icons");
public static string AppHeliosPlusIconFilename = System.IO.Path.Combine(AppIconPath, @"HeliosPlus.ico");
private static string AppProfileStoragePath = System.IO.Path.Combine(AppDataPath, $"Profiles");
private static string _profileStorageJsonFileName = System.IO.Path.Combine(AppProfileStoragePath, $"DisplayProfiles_{Version.ToString(2)}.json");
private static uint _lastProfileId;
private static ProfileItem _currentProfile;
//private static List<Display> _availableDisplays;
//private static List<UnAttachedDisplay> _unavailableDisplays;
#endregion
#region Class Constructors
static ProfileRepository()
{
// Initialise the the NVIDIA NvAPIWrapper
try
{
NvAPIWrapper.NVIDIA.Initialize();
// Create the Profile Storage Path if it doesn't exist so that it's avilable for all the program
if (!Directory.Exists(AppProfileStoragePath))
{
Directory.CreateDirectory(AppProfileStoragePath);
}
}
catch (Exception ex)
{
Console.WriteLine($"ShortcutItem/Instansiation exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
// ignored
}
// Load the Profiles from storage
LoadProfiles();
}
#endregion
#region Class Properties
public static List<ProfileItem> AllProfiles
{
get
{
if (!_profilesLoaded)
// Load the Profiles from storage if they need to be
LoadProfiles();
return _allProfiles;
}
}
public static ProfileItem CurrentProfile
{
get
{
UpdateActiveProfile();
return _currentProfile;
}
set
{
if (value is ProfileItem)
{
_currentProfile = value;
// And if we have the _originalBitmap we can also save the Bitmap overlay, but only if the ProfileToUse is set
//if (_originalBitmap is Bitmap)
// _shortcutBitmap = ToBitmapOverlay(_originalBitmap, ProfileToUse.ProfileTightestBitmap, 256, 256);
}
}
}
public static int ProfileCount
{
get
{
if (!_profilesLoaded)
// Load the Profiles from storage if they need to be
LoadProfiles();
return _allProfiles.Count;
}
}
#endregion
#region Class Methods
public static bool AddProfile(ProfileItem profile)
{
if (!(profile is ProfileItem))
return false;
// Doublecheck if it already exists
// Because then we just update the one that already exists
if (!ContainsProfile(profile))
{
// Add the Profile to the list of Profiles
_allProfiles.Add(profile);
// Generate the Profile Icon ready to be used
SaveProfileIconToCache(profile);
profile.PreSave();
// Save the Profiles JSON as it's different
SaveProfiles();
}
//Doublecheck it's been added
if (ContainsProfile(profile))
{
return true;
}
else
return false;
}
public static bool RemoveProfile(ProfileItem Profile)
{
if (!(Profile is ProfileItem))
return false;
// Remove the Profile Icons from the Cache
List<ProfileItem> ProfilesToRemove = _allProfiles.FindAll(item => item.UUID.Equals(Profile.UUID));
foreach (ProfileItem ProfileToRemove in ProfilesToRemove)
{
try
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/RemoveProfile exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
// TODO check and report
}
}
// Remove the Profile from the list.
int numRemoved = _allProfiles.RemoveAll(item => item.UUID.Equals(Profile.UUID));
if (numRemoved == 1)
{
SaveProfiles();
return true;
}
else if (numRemoved == 0)
return false;
else
throw new ProfileRepositoryException();
}
public static bool RemoveProfile(string ProfileName)
{
if (String.IsNullOrWhiteSpace(ProfileName))
return false;
// Remove the Profile Icons from the Cache
List<ProfileItem> ProfilesToRemove = _allProfiles.FindAll(item => item.Name.Equals(ProfileName));
foreach (ProfileItem ProfileToRemove in ProfilesToRemove)
{
try
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/RemoveProfile exception 2: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
// TODO check and report
}
}
// Remove the Profile from the list.
int numRemoved = _allProfiles.RemoveAll(item => item.Name.Equals(ProfileName));
if (numRemoved == 1)
{
SaveProfiles();
return true;
}
else if (numRemoved == 0)
return false;
else
throw new ProfileRepositoryException();
}
public static bool RemoveProfile(uint ProfileId)
{
if (ProfileId == 0)
return false;
// Remove the Profile Icons from the Cache
List<ProfileItem> ProfilesToRemove = _allProfiles.FindAll(item => item.UUID.Equals(ProfileId));
foreach (ProfileItem ProfileToRemove in ProfilesToRemove)
{
try
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/RemoveProfile exception 3: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
// TODO check and report
}
}
// Remove the Profile from the list.
int numRemoved = _allProfiles.RemoveAll(item => item.UUID.Equals(ProfileId));
if (numRemoved == 1)
{
SaveProfiles();
return true;
}
else if (numRemoved == 0)
return false;
else
throw new ProfileRepositoryException();
}
public static bool ContainsProfile(ProfileItem Profile)
{
if (!(Profile is ProfileItem))
return false;
foreach (ProfileItem testProfile in _allProfiles)
{
if (testProfile.UUID.Equals(Profile.UUID))
return true;
}
return false;
}
public static bool ContainsProfile(string ProfileNameOrId)
{
if (String.IsNullOrWhiteSpace(ProfileNameOrId))
return false;
if (ProfileItem.IsValidUUID(ProfileNameOrId))
foreach (ProfileItem testProfile in _allProfiles)
{
if (testProfile.UUID.Equals(ProfileNameOrId))
return true;
}
else
foreach (ProfileItem testProfile in _allProfiles)
{
if (testProfile.Name.Equals(ProfileNameOrId))
return true;
}
return false;
}
public static ProfileItem GetProfile(string ProfileNameOrId)
{
if (String.IsNullOrWhiteSpace(ProfileNameOrId))
return null;
if (ProfileItem.IsValidUUID(ProfileNameOrId))
foreach (ProfileItem testProfile in _allProfiles)
{
if (testProfile.UUID.Equals(ProfileNameOrId))
return testProfile;
}
else
foreach (ProfileItem testProfile in _allProfiles)
{
if (testProfile.Name.Equals(ProfileNameOrId))
return testProfile;
}
return null;
}
public static bool RenameProfile(ProfileItem profile, string renamedName)
{
if (!(profile is ProfileItem))
return false;
if (!IsValidFilename(renamedName))
return false;
profile.Name = GetValidFilename(renamedName);
// If it's been added to the list of AllProfiles
// then we also need to reproduce the Icons
if (ContainsProfile(profile))
{
// rename the old Profile Icon to the new name
//string newSavedProfileIconCacheFilename = Path.Combine(_profileStorageJsonPath, String.Concat(@"profile-", profile.UUID, @".ico"));
//File.Move(profile.SavedProfileIconCacheFilename, newSavedProfileIconCacheFilename);
// Then update the profile too
//profile.SavedProfileIconCacheFilename = newSavedProfileIconCacheFilename;
// Save the Profiles JSON as it's different now
SaveProfiles();
return true;
}
else
return false;
}
public static void UpdateActiveProfile()
{
ProfileItem activeProfile = new ProfileItem
{
Name = "Current Display Profile",
Paths = PathInfo.GetActivePaths().Select(info => new HeliosPlus.Shared.Topology.Path(info)).ToArray()
};
activeProfile.ProfileIcon = new ProfileIcon(activeProfile);
activeProfile.ProfileBitmap = activeProfile.ProfileIcon.ToBitmap(256, 256);
if (_profilesLoaded && _allProfiles.Count > 0)
{
foreach (ProfileItem loadedProfile in ProfileRepository.AllProfiles)
{
if (activeProfile.Equals(loadedProfile))
{
_currentProfile = loadedProfile;
return;
}
}
}
_currentProfile = activeProfile;
}
public static ProfileItem GetActiveProfile()
{
UpdateActiveProfile();
if (!(_currentProfile is ProfileItem))
return null;
return _currentProfile;
}
public static bool IsActiveProfile(ProfileItem profile)
{
UpdateActiveProfile();
if (!(_currentProfile is ProfileItem))
return false;
if (!(profile is ProfileItem))
return false;
if (profile.Equals(_currentProfile))
return true;
return false;
}
public static bool IsPossibleProfile(ProfileItem profile)
{
if (!(_currentProfile is ProfileItem))
return false;
if (!(profile is ProfileItem))
return false;
// Check each display in this profile and make sure it's currently available
int validDisplayCount = 0;
foreach (string profileDisplayIdentifier in profile.ProfileDisplayIdentifiers)
{
// If this profile has a display that isn't currently available then we need to say it's a no!
if (_currentProfile.ProfileDisplayIdentifiers.Contains(profileDisplayIdentifier))
validDisplayCount++;
}
if (validDisplayCount == profile.ProfileDisplayIdentifiers.Count)
return true;
else
return false;
}
private static bool LoadProfiles()
{
if (File.Exists(_profileStorageJsonFileName))
{
var json = File.ReadAllText(_profileStorageJsonFileName, Encoding.Unicode);
if (!string.IsNullOrWhiteSpace(json))
{
//List<ProfileItem> profiles = new List<ProfileItem>();
try
{
_allProfiles = JsonConvert.DeserializeObject<List<ProfileItem>>(json, new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include,
TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace
});
}
catch (Exception ex)
{
// ignored
Console.WriteLine($"ProfileRepository/LoadProfiles exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
Console.WriteLine($"Unable to load Profiles from JSON file {_profileStorageJsonFileName}: " + ex.Message);
}
ProfileItem myCurrentProfile = new ProfileItem
{
Name = "Current Display Profile",
Paths = PathInfo.GetActivePaths().Select(info => new HeliosPlus.Shared.Topology.Path(info)).ToArray()
};
_currentProfile = myCurrentProfile;
// Lookup all the Profile Names in the Saved Profiles
foreach (ProfileItem loadedProfile in _allProfiles)
{
// Save a profile Icon to the profile
/* loadedProfile.ProfileIcon = new ProfileIcon(loadedProfile);
loadedProfile.ProfileBitmap = loadedProfile.ProfileIcon.ToBitmap(256, 256);
*/
if (ProfileRepository.IsActiveProfile(loadedProfile))
_currentProfile = loadedProfile;
}
}
} else
{
// If we get here, then we don't have any profiles saved!
// So we gotta start from scratch
// Create a new profile based on our current display settings
ProfileItem myCurrentProfile = new ProfileItem
{
Name = "Current Display Profile",
Paths = PathInfo.GetActivePaths().Select(info => new HeliosPlus.Shared.Topology.Path(info)).ToArray()
};
_currentProfile = myCurrentProfile;
// Save a profile Icon to the profile
_currentProfile.ProfileIcon = new ProfileIcon(_currentProfile);
_currentProfile.ProfileBitmap = _currentProfile.ProfileIcon.ToBitmap(256, 256);
}
_profilesLoaded = true;
return true;
}
public static bool SaveProfiles()
{
if (!Directory.Exists(AppProfileStoragePath))
{
try
{
Directory.CreateDirectory(AppProfileStoragePath);
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/SaveProfiles exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
Console.WriteLine($"Unable to create Profile folder {AppProfileStoragePath}: " + ex.Message);
}
}
try
{
var json = JsonConvert.SerializeObject(_allProfiles, Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Populate,
TypeNameHandling = TypeNameHandling.Auto
});
if (!string.IsNullOrWhiteSpace(json))
{
File.WriteAllText(_profileStorageJsonFileName, json, Encoding.Unicode);
return true;
}
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/SaveProfiles exception 2: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
Console.WriteLine($"Unable to save Profile JSON file {_profileStorageJsonFileName}: " + ex.Message);
}
return false;
}
private static void SaveProfileIconToCache(ProfileItem profile)
{
// Work out the name of the Profile we'll save.
profile.SavedProfileIconCacheFilename = System.IO.Path.Combine(AppProfileStoragePath, string.Concat(@"profile-", profile.UUID, @".ico"));
MultiIcon ProfileIcon;
try
{
ProfileIcon = profile.ProfileIcon.ToIcon();
ProfileIcon.Save(profile.SavedProfileIconCacheFilename, MultiIconFormat.ICO);
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/SaveProfileIconToCache exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
// If we fail to create an icon based on the Profile, then we use the standard HeliosPlus profile one.
// Which is created on program startup.
File.Copy(AppHeliosPlusIconFilename, profile.SavedProfileIconCacheFilename);
}
}
public static List<string> GenerateProfileDisplayIdentifiers()
{
List<string> displayIdentifiers = new List<string>();
// If the Video Card is an NVidia, then we should generate specific NVidia displayIdentifiers
NvAPIWrapper.GPU.LogicalGPU[] myLogicalGPUs = NvAPIWrapper.GPU.LogicalGPU.GetLogicalGPUs();
if (myLogicalGPUs.Length > 0)
{
foreach (NvAPIWrapper.GPU.LogicalGPU myLogicalGPU in myLogicalGPUs)
{
NvAPIWrapper.GPU.PhysicalGPU[] myPhysicalGPUs = myLogicalGPU.CorrespondingPhysicalGPUs;
foreach (NvAPIWrapper.GPU.PhysicalGPU myPhysicalGPU in myPhysicalGPUs)
{
// get a list of all physical outputs attached to the GPUs
NvAPIWrapper.GPU.GPUOutput[] myGPUOutputs = myPhysicalGPU.ActiveOutputs;
foreach (NvAPIWrapper.GPU.GPUOutput aGPUOutput in myGPUOutputs)
{
// Figure out the displaydevice attached to the output
NvAPIWrapper.Display.DisplayDevice aConnectedDisplayDevice = myPhysicalGPU.GetDisplayDeviceByOutput(aGPUOutput);
// Create an array of all the important display info we need to record
string[] displayInfo = {
"NVIDIA",
myLogicalGPU.ToString(),
myPhysicalGPU.ToString(),
myPhysicalGPU.ArchitectInformation.ShortName.ToString(),
myPhysicalGPU.ArchitectInformation.Revision.ToString(),
myPhysicalGPU.Board.ToString(),
myPhysicalGPU.Foundry.ToString(),
myPhysicalGPU.GPUId.ToString(),
myPhysicalGPU.GPUType.ToString(),
aGPUOutput.OutputId.ToString(),
aConnectedDisplayDevice.ConnectionType.ToString(),
aConnectedDisplayDevice.DisplayId.ToString()
};
// Create a display identifier out of it
string displayIdentifier = String.Join("|", displayInfo);
// Add it to the list of display identifiers so we can return it
displayIdentifiers.Add(displayIdentifier);
}
}
}
}
// else videocard is not NVIdia so we just use the WindowsAPI access method
// Note: This won't support any special AMD EyeFinity profiles unfortunately.....
// TODO: Add the detection and generation of the device ids using an AMD library
// so that we can match valid AMD Eyefinity profiles with valid AMD standard profiles.
else
{
// Then go through the adapters we have running using the WindowsDisplayAPI
List<DisplayAdapter> allDisplayAdapters = DisplayAdapter.GetDisplayAdapters().ToList();
foreach (DisplayAdapter displayAdapter in allDisplayAdapters)
{
PathDisplayAdapter pathDisplayAdapter = displayAdapter.ToPathDisplayAdapter();
List<DisplayDevice> displayDevices = displayAdapter.GetDisplayDevices().Where(da => da.IsAvailable).ToList();
// skip this DisplayAdapter if the number of available DisplayDevices is 0
if (displayDevices.Count == 0)
continue;
Debug.WriteLine($"DP : {displayAdapter.DevicePath}");
Debug.WriteLine($"DK : {displayAdapter.DeviceKey}");
Debug.WriteLine($"DN : {displayAdapter.DeviceName}");
Debug.WriteLine($"DK : {displayAdapter.DeviceKey}");
Debug.WriteLine($"AI : {pathDisplayAdapter.AdapterId}");
Debug.WriteLine($"AIDP : {pathDisplayAdapter.DevicePath}");
Debug.WriteLine($"AIII : {pathDisplayAdapter.IsInvalid}");
foreach (DisplayDevice displayDevice in displayDevices)
{
PathDisplaySource pathDisplaySource = displayDevice.ToPathDisplaySource();
PathDisplayTarget pathDisplayTarget = displayDevice.ToPathDisplayTarget();
// skip this DisplayDevice if it isn't available
if (!displayDevice.IsAvailable)
continue;
Console.WriteLine($"DDA : {displayDevice.Adapter}");
Debug.WriteLine($"DDDK : {displayDevice.DeviceKey}");
Debug.WriteLine($"DDDN : {displayDevice.DeviceName}");
Debug.WriteLine($"DDDP : {displayDevice.DevicePath}");
Debug.WriteLine($"DDDiFN : {displayDevice.DisplayFullName}");
Debug.WriteLine($"DDDiN : {displayDevice.DisplayName}");
Debug.WriteLine($"DDDiIA : {displayDevice.IsAvailable}");
Debug.WriteLine($"DDDiIV : {displayDevice.IsValid}");
Debug.WriteLine($"PDSA : {pathDisplaySource.Adapter}");
Debug.WriteLine($"PDSCDS : {pathDisplaySource.CurrentDPIScale}");
Debug.WriteLine($"PDSDN : {pathDisplaySource.DisplayName}");
Debug.WriteLine($"PDSMDS : {pathDisplaySource.MaximumDPIScale}");
Debug.WriteLine($"PDSRDS : {pathDisplaySource.RecommendedDPIScale}");
Debug.WriteLine($"PDSSI : {pathDisplaySource.SourceId}");
Debug.WriteLine($"PDTA : {pathDisplayTarget.Adapter}");
Debug.WriteLine($"PDTCI : {pathDisplayTarget.ConnectorInstance}");
Debug.WriteLine($"PDTDP : {pathDisplayTarget.DevicePath}");
Debug.WriteLine($"PDTEMC : {pathDisplayTarget.EDIDManufactureCode}");
Debug.WriteLine($"PDTEMI : {pathDisplayTarget.EDIDManufactureId}");
Debug.WriteLine($"PDTEPC : {pathDisplayTarget.EDIDProductCode}");
Debug.WriteLine($"PDTFN : {pathDisplayTarget.FriendlyName}");
Debug.WriteLine($"PDTIA : {pathDisplayTarget.IsAvailable}");
Debug.WriteLine($"PDTPR : {pathDisplayTarget.PreferredResolution}");
Debug.WriteLine($"PDTPSM : {pathDisplayTarget.PreferredSignalMode}");
Debug.WriteLine($"PDTTI : {pathDisplayTarget.TargetId}");
Debug.WriteLine($"PDTVRS : {pathDisplayTarget.VirtualResolutionSupport}");
// Create an array of all the important display info we need to record
string[] displayInfo = {
"WINAPI",
displayAdapter.DeviceName.ToString(),
pathDisplayAdapter.AdapterId.ToString(),
pathDisplaySource.SourceId.ToString(),
pathDisplayTarget.ConnectorInstance.ToString(),
pathDisplayTarget.FriendlyName,
pathDisplayTarget.EDIDManufactureCode.ToString(),
pathDisplayTarget.EDIDManufactureId.ToString(),
pathDisplayTarget.EDIDProductCode.ToString(),
pathDisplayTarget.TargetId.ToString(),
};
// Create a display identifier out of it
string displayIdentifier = String.Join("|", displayInfo);
// Add it to the list of display identifiers so we can return it
displayIdentifiers.Add(displayIdentifier);
}
}
}
return displayIdentifiers;
}
public static List<string> GenerateAllDisplayIdentifiers()
{
List<string> displayIdentifiers = new List<string>();
// If the Video Card is an NVidia, then we should generate specific NVidia displayIdentifiers
NvAPIWrapper.GPU.LogicalGPU[] myLogicalGPUs = NvAPIWrapper.GPU.LogicalGPU.GetLogicalGPUs();
if (myLogicalGPUs.Length > 0)
{
foreach (NvAPIWrapper.GPU.LogicalGPU myLogicalGPU in myLogicalGPUs)
{
NvAPIWrapper.GPU.PhysicalGPU[] myPhysicalGPUs = myLogicalGPU.CorrespondingPhysicalGPUs;
foreach (NvAPIWrapper.GPU.PhysicalGPU myPhysicalGPU in myPhysicalGPUs)
{
// get a list of all physical outputs attached to the GPUs
NvAPIWrapper.Display.DisplayDevice[] allDisplayDevices = myPhysicalGPU.GetDisplayDevices();
foreach (NvAPIWrapper.Display.DisplayDevice aDisplayDevice in allDisplayDevices)
{
// Create an array of all the important display info we need to record
string[] displayInfo = {
"NVIDIA",
myLogicalGPU.ToString(),
myPhysicalGPU.ToString(),
myPhysicalGPU.ArchitectInformation.ShortName.ToString(),
myPhysicalGPU.ArchitectInformation.Revision.ToString(),
myPhysicalGPU.Board.ToString(),
myPhysicalGPU.Foundry.ToString(),
myPhysicalGPU.GPUId.ToString(),
myPhysicalGPU.GPUType.ToString(),
aDisplayDevice.Output.OutputId.ToString(),
aDisplayDevice.ConnectionType.ToString(),
aDisplayDevice.DisplayId.ToString(),
};
// Create a display identifier out of it
string displayIdentifier = String.Join("|", displayInfo);
// Add it to the list of display identifiers so we can return it
displayIdentifiers.Add(displayIdentifier);
}
}
}
}
// else videocard is not NVIdia so we just use the WindowsAPI access method
// Note: This won't support any special AMD EyeFinity profiles unfortunately.....
// TODO: Add the detection and generation of the device ids using an AMD library
// so that we can match valid AMD Eyefinity profiles with valid AMD standard profiles.
else
{
// Then go through the adapters we have running using the WindowsDisplayAPI
List<DisplayAdapter> allDisplayAdapters = DisplayAdapter.GetDisplayAdapters().ToList();
foreach (DisplayAdapter displayAdapter in allDisplayAdapters)
{
PathDisplayAdapter pathDisplayAdapter = displayAdapter.ToPathDisplayAdapter();
List<DisplayDevice> displayDevices = displayAdapter.GetDisplayDevices().ToList();
Debug.WriteLine($"DP : {displayAdapter.DevicePath}");
Debug.WriteLine($"DK : {displayAdapter.DeviceKey}");
Debug.WriteLine($"DN : {displayAdapter.DeviceName}");
Debug.WriteLine($"DK : {displayAdapter.DeviceKey}");
Debug.WriteLine($"AI : {pathDisplayAdapter.AdapterId}");
Debug.WriteLine($"AIDP : {pathDisplayAdapter.DevicePath}");
Debug.WriteLine($"AIII : {pathDisplayAdapter.IsInvalid}");
foreach (DisplayDevice displayDevice in displayDevices)
{
PathDisplaySource pathDisplaySource = displayDevice.ToPathDisplaySource();
PathDisplayTarget pathDisplayTarget = displayDevice.ToPathDisplayTarget();
Debug.WriteLine($"DDA : {displayDevice.Adapter}");
Debug.WriteLine($"DDDK : {displayDevice.DeviceKey}");
Debug.WriteLine($"DDDN : {displayDevice.DeviceName}");
Debug.WriteLine($"DDDP : {displayDevice.DevicePath}");
Debug.WriteLine($"DDDiFN : {displayDevice.DisplayFullName}");
Debug.WriteLine($"DDDiN : {displayDevice.DisplayName}");
Debug.WriteLine($"DDDiIA : {displayDevice.IsAvailable}");
Debug.WriteLine($"DDDiIV : {displayDevice.IsValid}");
Debug.WriteLine($"PDSA : {pathDisplaySource.Adapter}");
Debug.WriteLine($"PDSCDS : {pathDisplaySource.CurrentDPIScale}");
Debug.WriteLine($"PDSDN : {pathDisplaySource.DisplayName}");
Debug.WriteLine($"PDSMDS : {pathDisplaySource.MaximumDPIScale}");
Debug.WriteLine($"PDSRDS : {pathDisplaySource.RecommendedDPIScale}");
Debug.WriteLine($"PDSSI : {pathDisplaySource.SourceId}");
Debug.WriteLine($"PDTA : {pathDisplayTarget.Adapter}");
Debug.WriteLine($"PDTCI : {pathDisplayTarget.ConnectorInstance}");
Debug.WriteLine($"PDTDP : {pathDisplayTarget.DevicePath}");
Debug.WriteLine($"PDTEMC : {pathDisplayTarget.EDIDManufactureCode}");
Debug.WriteLine($"PDTEMI : {pathDisplayTarget.EDIDManufactureId}");
Debug.WriteLine($"PDTEPC : {pathDisplayTarget.EDIDProductCode}");
Debug.WriteLine($"PDTFN : {pathDisplayTarget.FriendlyName}");
Debug.WriteLine($"PDTIA : {pathDisplayTarget.IsAvailable}");
Debug.WriteLine($"PDTPR : {pathDisplayTarget.PreferredResolution}");
Debug.WriteLine($"PDTPSM : {pathDisplayTarget.PreferredSignalMode}");
Debug.WriteLine($"PDTTI : {pathDisplayTarget.TargetId}");
Debug.WriteLine($"PDTVRS : {pathDisplayTarget.VirtualResolutionSupport}");
// Create an array of all the important display info we need to record
string[] displayInfo = {
"WINAPI",
displayAdapter.DeviceName.ToString(),
pathDisplayAdapter.AdapterId.ToString(),
pathDisplaySource.SourceId.ToString(),
pathDisplayTarget.ConnectorInstance.ToString(),
pathDisplayTarget.FriendlyName,
pathDisplayTarget.EDIDManufactureCode.ToString(),
pathDisplayTarget.EDIDManufactureId.ToString(),
pathDisplayTarget.EDIDProductCode.ToString(),
pathDisplayTarget.TargetId.ToString(),
};
// Create a display identifier out of it
string displayIdentifier = String.Join("|", displayInfo);
// Add it to the list of display identifiers so we can return it
displayIdentifiers.Add(displayIdentifier);
}
}
}
return displayIdentifiers;
}
public static bool ApplyNVIDIAGridTopology(ProfileItem profile)
{
Debug.Print("ProfileRepository.ApplyTopology()");
if (!(profile is ProfileItem))
return false;
try
{
var surroundTopologies =
profile.Paths.SelectMany(paths => paths.TargetDisplays)
.Select(target => target.SurroundTopology)
.Where(topology => topology != null)
.Select(topology => topology.ToGridTopology())
.ToArray();
if (surroundTopologies.Length == 0)
{
// The profile we're changing to does not use NVIDIA Surround
// So we need to set the Grid Topologies to individual screens
// in preparation for the PathInfo step later
var currentTopologies = GridTopology.GetGridTopologies();
if (currentTopologies.Any(topology => topology.Rows * topology.Columns > 1))
{
surroundTopologies =
GridTopology.GetGridTopologies()
.SelectMany(topology => topology.Displays)
.Select(displays => new GridTopology(1, 1, new[] { displays }))
.ToArray();
GridTopology.SetGridTopologies(surroundTopologies, SetDisplayTopologyFlag.MaximizePerformance);
}
} else if (surroundTopologies.Length > 0)
{
// This profile is an NVIDIA Surround profile
GridTopology.SetGridTopologies(surroundTopologies, SetDisplayTopologyFlag.MaximizePerformance);
}
return true;
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/ApplyTopology exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
return false;
}
}
public static bool ApplyWindowsDisplayPathInfo(ProfileItem profile)
{
Debug.Print("ProfileRepository.ApplyPathInfo()");
if (!(profile is ProfileItem))
return false;
try
{
var pathInfos = profile.Paths.Select(paths => paths.ToPathInfo()).Where(info => info != null).ToArray();
PathInfo.ApplyPathInfos(pathInfos, true, true, true);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"ProfileRepository/ApplyPathInfo exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
return false;
}
}
public static bool IsValidFilename(string testName)
{
string strTheseAreInvalidFileNameChars = new string(System.IO.Path.GetInvalidFileNameChars());
Regex regInvalidFileName = new Regex("[" + Regex.Escape(strTheseAreInvalidFileNameChars) + "]");
if (regInvalidFileName.IsMatch(testName)) { return false; };
return true;
}
public static string GetValidFilename(string uncheckedFilename)
{
string invalid = new string(System.IO.Path.GetInvalidFileNameChars()) + new string(System.IO.Path.GetInvalidPathChars());
foreach (char c in invalid)
{
uncheckedFilename = uncheckedFilename.Replace(c.ToString(), "");
}
return uncheckedFilename;
}
#endregion
}
[global::System.Serializable]
public class ProfileRepositoryException : Exception
{
public ProfileRepositoryException() { }
public ProfileRepositoryException(string message) : base(message) { }
public ProfileRepositoryException(string message, Exception inner) : base(message, inner) { }
protected ProfileRepositoryException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}