DisplayMagician/DisplayMagicianShared/ProfileItem.cs

1809 lines
87 KiB
C#
Raw Normal View History

2017-02-26 19:23:31 +00:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using DisplayMagicianShared.Resources;
using Newtonsoft.Json;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using IWshRuntimeLibrary;
using DisplayMagicianShared.AMD;
using DisplayMagicianShared.NVIDIA;
using DisplayMagicianShared.Windows;
using System.Runtime.InteropServices;
using System.ComponentModel;
2017-02-26 19:23:31 +00:00
namespace DisplayMagicianShared
2017-02-26 19:23:31 +00:00
{
public struct ScreenPosition
{
public int ScreenX;
public int ScreenY;
public int ScreenWidth;
public int ScreenHeight;
public string Name;
public string AdapterName;
public string Library;
public bool IsPrimary;
public bool IsClone;
public int ClonedCopies;
public Color Colour;
public string DisplayConnector;
internal bool HDRSupported;
internal bool HDREnabled;
public List<string> Features;
// If the screen is AMD Eyefinity or NVIDIA Surround or similar, it has screens that are part of it
// These fields indicate this. The spanned screens are added to the SpannedScreens field as required
public bool IsSpanned;
public List<SpannedScreenPosition> SpannedScreens;
public int SpannedColumns;
public int SpannedRows;
public TaskBarLayout.TaskBarEdge TaskBarEdge;
}
public struct SpannedScreenPosition
{
public int ScreenX;
public int ScreenY;
public int ScreenWidth;
public int ScreenHeight;
public string Name;
public Color Colour;
public List<string> Features;
public int Column;
public int Row;
}
public class ProfileItem : IComparable<ProfileItem>, IEquatable<ProfileItem>
2017-02-26 19:23:31 +00:00
{
private static List<ProfileItem> _allSavedProfiles = new List<ProfileItem>();
private ProfileIcon _profileIcon;
private Bitmap _profileBitmap, _profileShortcutBitmap;
private List<string> _profileDisplayIdentifiers = new List<string>();
private List<ScreenPosition> _screens = new List<ScreenPosition>();
private NVIDIA_DISPLAY_CONFIG _nvidiaDisplayConfig;
private AMD_DISPLAY_CONFIG _amdDisplayConfig;
private WINDOWS_DISPLAY_CONFIG _windowsDisplayConfig;
2017-02-26 19:23:31 +00:00
internal static string AppDataPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DisplayMagician");
2021-08-27 09:15:53 +00:00
private static string AppWallpaperPath = Path.Combine(AppDataPath, $"Wallpaper");
2021-01-28 09:20:00 +00:00
private static readonly string uuidV4Regex = @"(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$";
private string _uuid = "";
private bool _isPossible = false;
2021-04-28 10:14:54 +00:00
private Keys _hotkey = Keys.None;
2021-08-27 09:15:53 +00:00
private string _wallpaperBitmapFilename = "";
#region JsonConverterBitmap
internal class CustomBitmapConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
//convert from byte to bitmap (deserialize)
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string image = (string)reader.Value;
byte[] byteBuffer = Convert.FromBase64String(image);
2021-01-28 09:03:02 +00:00
MemoryStream memoryStream = new MemoryStream(byteBuffer)
{
Position = 0
};
return (Bitmap)Bitmap.FromStream(memoryStream);
}
//convert bitmap to byte (serialize)
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Bitmap bitmap = (Bitmap)value;
ImageConverter converter = new ImageConverter();
writer.WriteValue((byte[])converter.ConvertTo(bitmap, typeof(byte[])));
}
public static System.Drawing.Imaging.ImageFormat GetImageFormat(Bitmap bitmap)
{
ImageFormat img = bitmap.RawFormat;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Jpeg))
return System.Drawing.Imaging.ImageFormat.Jpeg;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Bmp))
return System.Drawing.Imaging.ImageFormat.Bmp;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Png))
return System.Drawing.Imaging.ImageFormat.Png;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Emf))
return System.Drawing.Imaging.ImageFormat.Emf;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Exif))
return System.Drawing.Imaging.ImageFormat.Exif;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Gif))
return System.Drawing.Imaging.ImageFormat.Gif;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Icon))
return System.Drawing.Imaging.ImageFormat.Icon;
if (img.Equals(System.Drawing.Imaging.ImageFormat.MemoryBmp))
return System.Drawing.Imaging.ImageFormat.MemoryBmp;
if (img.Equals(System.Drawing.Imaging.ImageFormat.Tiff))
return System.Drawing.Imaging.ImageFormat.Tiff;
else
return System.Drawing.Imaging.ImageFormat.Wmf;
}
}
#endregion
public ProfileItem()
2017-02-26 19:23:31 +00:00
{
// Fill out a new NVIDIA and AMD object when a profile is being created
// so that it will save correctly. Json.NET will save null references by default
// unless we fill them up first, and that in turn causes NullReference errors when
// loading the DisplayProfiles_2.0.json into DisplayMagician next time.
// We cannot make the structs themselves create the default entry, so instead, we
// make each library create the default.
_nvidiaDisplayConfig = NVIDIALibrary.GetLibrary().CreateDefaultConfig();
_amdDisplayConfig = AMDLibrary.GetLibrary().CreateDefaultConfig();
2017-02-26 19:23:31 +00:00
}
public static Version Version = new Version(2, 1);
#region Instance Properties
[DefaultValue("")]
public string UUID
2017-02-26 19:23:31 +00:00
{
get
{
if (String.IsNullOrWhiteSpace(_uuid))
_uuid = Guid.NewGuid().ToString("D");
return _uuid;
}
set
{
Match match = Regex.Match(value, uuidV4Regex, RegexOptions.IgnoreCase);
if (match.Success)
_uuid = value;
2017-02-26 19:23:31 +00:00
}
}
[JsonIgnore]
public virtual bool IsPossible
2017-02-26 19:23:31 +00:00
{
get
{
// Return the cached answer
return _isPossible;
}
set
{
_isPossible = value;
}
}
[JsonIgnore]
public virtual bool IsActive
{
get
{
if (this.Equals(ProfileRepository.CurrentProfile))
return true;
else
return false;
2017-02-26 19:23:31 +00:00
}
}
[DefaultValue(VIDEO_MODE.WINDOWS)]
public virtual VIDEO_MODE VideoMode { get; set; } = VIDEO_MODE.WINDOWS;
[DefaultValue(Keys.None)]
2021-04-28 10:14:54 +00:00
public Keys Hotkey {
get
{
return _hotkey;
}
set
{
2021-04-28 10:14:54 +00:00
_hotkey = value;
}
}
[DefaultValue("")]
public virtual string Name { get; set; }
2017-02-26 19:23:31 +00:00
[JsonRequired]
public NVIDIA_DISPLAY_CONFIG NVIDIADisplayConfig
{
get
{
return _nvidiaDisplayConfig;
}
set
{
_nvidiaDisplayConfig = value;
}
}
[JsonRequired]
public AMD_DISPLAY_CONFIG AMDDisplayConfig
{
get
{
return _amdDisplayConfig;
}
set
{
_amdDisplayConfig = value;
}
}
[JsonRequired]
public WINDOWS_DISPLAY_CONFIG WindowsDisplayConfig
{
get
{
return _windowsDisplayConfig;
}
set
{
_windowsDisplayConfig = value;
}
}
[JsonIgnore]
public virtual ProfileIcon ProfileIcon
{
2020-05-12 10:46:23 +00:00
get
{
if (_profileIcon != null)
return _profileIcon;
else
{
_profileIcon = new ProfileIcon(this);
return _profileIcon;
}
}
set
{
_profileIcon = value;
}
}
[JsonIgnore]
public virtual List<ScreenPosition> Screens
{
get
{
if (_screens.Count == 0 && ProfileRepository.ProfilesLoaded)
{
_screens = GetScreenPositions();
}
return _screens;
}
set
{
_screens = value;
}
}
[DefaultValue("")]
public string SavedProfileIconCacheFilename { get; set; }
[DefaultValue(Wallpaper.Mode.DoNothing)]
public Wallpaper.Mode WallpaperMode { get; set; }
2021-08-27 09:15:53 +00:00
[DefaultValue(Wallpaper.Style.Fill)]
2021-08-27 10:26:12 +00:00
public Wallpaper.Style WallpaperStyle { get; set; }
[DefaultValue("")]
2021-08-27 09:15:53 +00:00
public string WallpaperBitmapFilename{
get
{
return _wallpaperBitmapFilename;
}
set
{
_wallpaperBitmapFilename = value;
}
}
[DefaultValue(default(List<string>))]
public virtual List<string> ProfileDisplayIdentifiers
{
get
{
if (_profileDisplayIdentifiers.Count == 0)
{
_profileDisplayIdentifiers = ProfileRepository.GetCurrentDisplayIdentifiers();
}
return _profileDisplayIdentifiers;
}
set
{
if (value is List<string>)
_profileDisplayIdentifiers = value;
}
}
[DefaultValue(default(Bitmap))]
[JsonConverter(typeof(CustomBitmapConverter))]
public virtual Bitmap ProfileBitmap
{
2020-05-12 10:46:23 +00:00
get
{
if (_profileBitmap != null)
return _profileBitmap;
else
{
_profileBitmap = this.ProfileIcon.ToBitmap(256, 256);
2020-05-12 10:46:23 +00:00
return _profileBitmap;
}
}
set
{
_profileBitmap = value;
}
}
2017-02-26 19:23:31 +00:00
[DefaultValue(default(Bitmap))]
[JsonConverter(typeof(CustomBitmapConverter))]
public virtual Bitmap ProfileTightestBitmap
{
get
{
2022-02-11 20:18:03 +00:00
if (ProfileRepository.ProfilesLoaded)
{
if (_profileShortcutBitmap != null)
return _profileShortcutBitmap;
else
{
//_profileShortcutBitmap = this.ProfileIcon.ToTightestBitmap();
_profileShortcutBitmap = this.ProfileIcon.ToTightestBitmap();
2022-02-11 20:18:03 +00:00
return _profileShortcutBitmap;
}
}
else
{
2022-02-11 20:18:03 +00:00
return null;
}
}
set
{
_profileShortcutBitmap = value;
}
}
#endregion
2017-02-26 19:23:31 +00:00
public static bool IsValidName(string testName)
{
foreach (ProfileItem loadedProfile in _allSavedProfiles)
{
if (loadedProfile.Name == testName)
{
return false;
}
}
return true;
}
public static bool IsValidUUID(string testId)
{
Match match = Regex.Match(testId, uuidV4Regex, RegexOptions.IgnoreCase);
if (match.Success)
return true;
else
return false;
}
public bool IsValid()
{
if (VideoMode == VIDEO_MODE.NVIDIA)
{
if (!NVIDIALibrary.GetLibrary().IsValidConfig(_nvidiaDisplayConfig))
{
SharedLogger.logger.Error($"ProfileItem/IsValid: The profile {Name} has an invalid NVIDIA display config");
return false;
}
}
else if (VideoMode == VIDEO_MODE.AMD)
{
if (!AMDLibrary.GetLibrary().IsValidConfig(_amdDisplayConfig))
{
SharedLogger.logger.Error($"ProfileItem/IsValid: The profile {Name} has an invalid AMD display config");
return false;
}
}
else if (VideoMode == VIDEO_MODE.WINDOWS)
{
if (!WinLibrary.GetLibrary().IsValidConfig(_windowsDisplayConfig))
{
SharedLogger.logger.Error($"ProfileItem/IsValid: The profile {Name} has an invalid Windows CCD display config");
return false;
}
}
else
{
SharedLogger.logger.Error($"ProfileItem/IsValid: The profile {Name} has an unknown video mode!");
}
// The rest of the
if (ProfileIcon is ProfileIcon &&
System.IO.File.Exists(SavedProfileIconCacheFilename) &&
ProfileBitmap is Bitmap &&
ProfileTightestBitmap is Bitmap &&
ProfileDisplayIdentifiers.Count > 0)
return true;
else
return false;
}
2018-10-20 00:27:25 +00:00
public virtual bool CopyTo(ProfileItem profile, bool overwriteId = true)
{
if (overwriteId == true)
profile.UUID = UUID;
// Copy all our profile data over to the other profile
profile.Name = Name;
profile.AMDDisplayConfig = AMDDisplayConfig;
profile.NVIDIADisplayConfig = NVIDIADisplayConfig;
profile.WindowsDisplayConfig = WindowsDisplayConfig;
profile.ProfileIcon = ProfileIcon;
profile.SavedProfileIconCacheFilename = SavedProfileIconCacheFilename;
profile.ProfileBitmap = ProfileBitmap;
profile.ProfileTightestBitmap = ProfileTightestBitmap;
profile.ProfileDisplayIdentifiers = ProfileDisplayIdentifiers;
profile.WallpaperMode = WallpaperMode;
2021-08-27 09:15:53 +00:00
profile.WallpaperBitmapFilename = WallpaperBitmapFilename;
2021-08-27 10:26:12 +00:00
profile.WallpaperStyle = WallpaperStyle;
return true;
2017-02-26 19:23:31 +00:00
}
public virtual bool PreSave()
{
// Prepare our profile data for saving
// Disabling as this should never happen now
/*if (_profileDisplayIdentifiers.Count == 0)
{
_profileDisplayIdentifiers = ProfileRepository.GetCurrentDisplayIdentifiers();
}*/
// Return if it is valid and we should continue
return IsValid();
}
public bool CreateProfileFromCurrentDisplaySettings(bool fastScan = true)
{
// Calling the 3 different libraries automatically gets the different configs from each of the 3 video libraries.
// If the video library isn't in use then it also fills in the defaults so that the JSON file can save properly
// (C# Structs populate with default values which mean that arrays start with null)
try
{
//await Program.AppBackgroundTaskSemaphoreSlim.WaitAsync(0);
NVIDIALibrary nvidiaLibrary = NVIDIALibrary.GetLibrary();
AMDLibrary amdLibrary = AMDLibrary.GetLibrary();
WinLibrary winLibrary = WinLibrary.GetLibrary();
// For a library update to the latest version so that we pick up any new changes since the last update
if (VideoMode == VIDEO_MODE.NVIDIA && nvidiaLibrary.IsInstalled)
{
nvidiaLibrary.UpdateActiveConfig();
winLibrary.UpdateActiveConfig(fastScan);
}
else if (VideoMode == VIDEO_MODE.AMD && amdLibrary.IsInstalled)
{
amdLibrary.UpdateActiveConfig();
winLibrary.UpdateActiveConfig(fastScan);
}
else
{
winLibrary.UpdateActiveConfig(fastScan);
}
// Grab the profile data from the current stored config (that we just updated)
_nvidiaDisplayConfig = nvidiaLibrary.ActiveDisplayConfig;
_amdDisplayConfig = amdLibrary.ActiveDisplayConfig;
_windowsDisplayConfig = winLibrary.ActiveDisplayConfig;
_profileDisplayIdentifiers = nvidiaLibrary.CurrentDisplayIdentifiers;
// Now, since the ActiveProfile has changed, we need to regenerate screen positions
_screens = GetScreenPositions();
return true;
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"ProfileItem/CreateProfileFromCurrentDisplaySettings: Exception getting the config settings!");
return false;
}
}
// ReSharper disable once FunctionComplexityOverflow
// ReSharper disable once CyclomaticComplexity
public bool CreateShortcut(string shortcutFileName)
{
string shortcutDescription = string.Empty;
string shortcutIconFileName;
var shortcutArgs = new List<string>
{
// Add the SwitchProfile command as the first argument to start to switch to another profile
$"{DisplayMagicianStartupAction.ChangeProfile}",
$"\"{UUID}\""
};
// Prepare text for the shortcut description field
shortcutDescription = $"Change to the '{Name}' DisplayMagician Display Profile.";
// Now we are ready to create a shortcut based on the filename the user gave us
shortcutFileName = System.IO.Path.ChangeExtension(shortcutFileName, @"lnk");
// And we use the Icon from the shortcutIconCache
shortcutIconFileName = SavedProfileIconCacheFilename;
// If the user supplied a file
if (shortcutFileName != null)
{
try
{
// Remove the old file if it exists to replace it
if (System.IO.File.Exists(shortcutFileName))
{
System.IO.File.Delete(shortcutFileName);
}
// Actually create the shortcut!
//var wshShellType = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8"));
//dynamic wshShell = Activator.CreateInstance(wshShellType);
WshShell shell = new WshShell();
IWshShortcut shortcut = shell.CreateShortcut(shortcutFileName) as IWshShortcut;
shortcut.TargetPath = Application.ExecutablePath;
shortcut.Arguments = string.Join(" ", shortcutArgs);
shortcut.Description = shortcutDescription;
shortcut.WorkingDirectory = System.IO.Path.GetDirectoryName(Application.ExecutablePath) ??
string.Empty;
shortcut.IconLocation = shortcutIconFileName;
shortcut.Save();
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"ProfileItem/CreateShortcut: Execption while creating desktop shortcut!");
// Clean up a failed attempt
if (System.IO.File.Exists(shortcutFileName))
{
System.IO.File.Delete(shortcutFileName);
}
}
}
// Return a status on how it went
// true if it was a success or false if it was not
return shortcutFileName != null && System.IO.File.Exists(shortcutFileName);
}
2021-06-27 02:44:10 +00:00
public virtual void RefreshPossbility()
{
// Check whether this profile is the same as the video mode, otherwise it's not possible
if (ProfileRepository.CurrentVideoMode != VideoMode)
{
SharedLogger.logger.Debug($"ProfileItem/IsPossibleRefresh: The NVIDIA profile {Name} is NOT possible!");
_isPossible = false;
return;
}
// Otherwise actually check the possibility
if (ProfileRepository.CurrentVideoMode == VIDEO_MODE.NVIDIA && NVIDIALibrary.GetLibrary().IsInstalled)
{
if (NVIDIALibrary.GetLibrary().IsPossibleConfig(_nvidiaDisplayConfig))
{
SharedLogger.logger.Debug($"ProfileItem/IsPossibleRefresh: The NVIDIA profile {Name} is possible!");
_isPossible = true;
}
else
{
SharedLogger.logger.Debug($"ProfileItem/IsPossibleRefresh: The NVIDIA profile {Name} is NOT possible!");
_isPossible = false;
}
}
else if (ProfileRepository.CurrentVideoMode == VIDEO_MODE.AMD && AMDLibrary.GetLibrary().IsInstalled)
{
if (AMDLibrary.GetLibrary().IsPossibleConfig(_amdDisplayConfig))
{
SharedLogger.logger.Debug($"ProfileItem/IsPossibleRefresh: The AMD profile {Name} is possible!");
_isPossible = true;
}
else
{
SharedLogger.logger.Debug($"ProfileItem/IsPossibleRefresh: The AMD profile {Name} is NOT possible!");
_isPossible = false;
}
}
else if (ProfileRepository.CurrentVideoMode == VIDEO_MODE.WINDOWS && WinLibrary.GetLibrary().IsInstalled)
{
if (WinLibrary.GetLibrary().IsPossibleConfig(_windowsDisplayConfig))
{
SharedLogger.logger.Debug($"ProfileItem/IsPossibleRefresh: The Windows CCD profile {Name} is possible!");
_isPossible = true;
}
else
{
SharedLogger.logger.Debug($"ProfileItem/IsPossibleRefresh: The Windows CCD profile {Name} is NOT possible!");
_isPossible = false;
}
}
else
{
SharedLogger.logger.Warn($"ProfileItem/IsPossibleRefresh: We have a current video mode we don't understand, or it's not installed! The current video mode is {ProfileRepository.CurrentVideoMode}. The profile {Name} has a {VideoMode.ToString("G")} video mode and NVIDIALibrary IsInstalled is {NVIDIALibrary.GetLibrary().IsInstalled}, AMDLibrary IsInstalled is {AMDLibrary.GetLibrary().IsInstalled} and WinLibrary IsInstalled is {WinLibrary.GetLibrary().IsInstalled} ");
_isPossible = false;
}
}
// Actually set this profile active
public bool SetActive()
{
if (VideoMode == VIDEO_MODE.NVIDIA && NVIDIALibrary.GetLibrary().IsInstalled)
{
NVIDIALibrary nvidiaLibrary = NVIDIALibrary.GetLibrary();
WinLibrary winLibrary = WinLibrary.GetLibrary();
// Add in an extra dealy to let Windows catchup, but only if NVIDIA Surround is on.
// The delay is only needed when going from a surround profile to a non-surround profile.
/*bool extraDelayNeeded = false;
if (ProfileRepository.CurrentProfile.NVIDIADisplayConfig.MosaicConfig.IsMosaicEnabled)
{
extraDelayNeeded = true;
}*/
if (nvidiaLibrary.IsInstalled)
{
if (!winLibrary.IsActiveConfig(_windowsDisplayConfig) || !nvidiaLibrary.IsActiveConfig(_nvidiaDisplayConfig))
{
if (nvidiaLibrary.IsPossibleConfig(_nvidiaDisplayConfig))
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The NVIDIA display settings within profile {Name} are possible to use right now, so we'll use attempt to use them.");
bool itWorkedforNVIDIA = nvidiaLibrary.SetActiveConfig(_nvidiaDisplayConfig);
if (itWorkedforNVIDIA)
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The NVIDIA display settings within profile {Name} were successfully applied.");
SharedLogger.logger.Trace($"ProfileItem/SetActive: Waiting 0.5 seconds to let the NVIDIA display change take place before setting the Windows CCD display settings");
System.Threading.Thread.Sleep(500);
/*if (extraDelayNeeded)
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: Waiting 1.5 seconds longer to let the NVIDIA display change take place so wWindow can catchup...");
System.Threading.Thread.Sleep(1500);
}*/
// Lets update the screens so Windows knows whats happening
// NVIDIA makes such large changes to the available screens in windows, we need to do this.
winLibrary.UpdateActiveConfig();
2022-01-30 07:24:36 +00:00
// Then let's try to also apply the windows changes
// Note: we are unable to check if the Windows CCD display config is possible, as it won't match if either the current display config is a Mosaic config,
2022-01-30 07:24:36 +00:00
// or if the display config we want to change to is a Mosaic config. So we just have to assume that it will work
bool itWorkedforWindows = winLibrary.SetActiveConfig(_windowsDisplayConfig);
if (itWorkedforWindows)
{
bool itWorkedforNVIDIAColor = nvidiaLibrary.SetActiveConfigOverride(_nvidiaDisplayConfig);
if (itWorkedforNVIDIAColor)
{
Fixed error when returning from Mosaic layouts in WIndows 10 There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters. The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue. WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell. The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal. This should (fingers crossed) fix #119! This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
2022-07-14 10:41:59 +00:00
// Lets update the screen config again for the final time.
nvidiaLibrary.UpdateActiveConfig();
winLibrary.UpdateActiveConfig();
SharedLogger.logger.Trace($"ProfileItem/SetActive: The NVIDIA display settings that override windows within the profile {Name} were successfully applied.");
return true;
}
else
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The NVIDIA display settings that override windows within the profile {Name} were NOT applied correctly.");
}
}
else
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The Windows CCD display settings within profile {Name} were NOT applied correctly.");
}
}
else
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The NVIDIA display settings within profile {Name} were NOT applied correctly.");
}
}
else
{
SharedLogger.logger.Error($"ProfileItem/SetActive: ERROR - Cannot apply the NVIDIA display config in profile {Name} as it is not currently possible to use it.");
}
}
else
{
SharedLogger.logger.Info($"ProfileItem/SetActive: The display settings in profile {Name} are already installed. No need to install them again. Exiting.");
}
}
}
else if (VideoMode == VIDEO_MODE.AMD && AMDLibrary.GetLibrary().IsInstalled)
{
AMDLibrary amdLibrary = AMDLibrary.GetLibrary();
WinLibrary winLibrary = WinLibrary.GetLibrary();
if (amdLibrary.IsInstalled)
{
if (!winLibrary.IsActiveConfig(_windowsDisplayConfig) || !amdLibrary.IsActiveConfig(_amdDisplayConfig))
{
if (amdLibrary.IsPossibleConfig(_amdDisplayConfig))
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The AMD display settings within profile {Name} are possible to use right now, so we'll use attempt to use them.");
bool itWorkedforAMD = amdLibrary.SetActiveConfig(_amdDisplayConfig);
if (itWorkedforAMD)
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The AMD display settings within profile {Name} were successfully applied.");
/*SharedLogger.logger.Trace($"ProfileItem/SetActive: Waiting 0.5 seconds to let the AMD display change take place before setting the Windows CCD display settings");
System.Threading.Thread.Sleep(500);
*/
// Lets update the screens so Windows knows whats happening
// AMD makes such large changes to the available screens in windows, we need to do this.
winLibrary.UpdateActiveConfig();
// Then let's try to also apply the windows changes
// Note: we are unable to check if the Windows CCD display config is possible, as it won't match if either the current display config is an Eyefinity config,
// or if the display config we want to change to is an Eyefinity config. So we just have to assume that it will work!
bool itWorkedforWindows = winLibrary.SetActiveConfig(_windowsDisplayConfig);
if (itWorkedforWindows)
{
bool itWorkedforAMDColor = amdLibrary.SetActiveConfigOverride(_amdDisplayConfig);
if (itWorkedforAMDColor)
{
Fixed error when returning from Mosaic layouts in WIndows 10 There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters. The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue. WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell. The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal. This should (fingers crossed) fix #119! This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
2022-07-14 10:41:59 +00:00
// Lets update the screen config again for the final time.
amdLibrary.UpdateActiveConfig();
winLibrary.UpdateActiveConfig();
SharedLogger.logger.Trace($"ProfileItem/SetActive: The AMD display settings that override windows within the profile {Name} were successfully applied.");
return true;
}
else
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The AMD display settings that override windows within the profile {Name} were NOT applied correctly.");
}
}
else
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The Windows CCD display settings within profile {Name} were NOT applied correctly.");
}
}
else
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The AMD display settings within profile {Name} were NOT applied correctly.");
}
}
else
{
SharedLogger.logger.Error($"ProfileItem/SetActive: ERROR - Cannot apply the AMD display config in profile {Name} as it is not currently possible to use it.");
}
}
else
{
SharedLogger.logger.Info($"ProfileItem/SetActive: The display settings in profile {Name} are already installed. No need to install them again. Exiting.");
}
}
}
else if (VideoMode == VIDEO_MODE.WINDOWS)
{
WinLibrary winLibrary = WinLibrary.GetLibrary();
if (winLibrary.IsInstalled)
{
if (!winLibrary.IsActiveConfig(_windowsDisplayConfig))
{
if (winLibrary.SetActiveConfig(_windowsDisplayConfig))
{
Fixed error when returning from Mosaic layouts in WIndows 10 There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters. The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue. WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell. The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal. This should (fingers crossed) fix #119! This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
2022-07-14 10:41:59 +00:00
// Lets update the screen config again for the final time.
winLibrary.UpdateActiveConfig();
SharedLogger.logger.Trace($"ProfileItem/SetActive: The Windows CCD display settings within profile {Name} were successfully applied.");
return true;
}
else
{
SharedLogger.logger.Trace($"ProfileItem/SetActive: The Windows CCD display settings within profile {Name} were NOT applied correctly.");
}
}
else
{
SharedLogger.logger.Info($"ProfileItem/SetActive: The display settings in profile {Name} are already installed. No need to install them again. Exiting.");
}
}
}
return false;
}
public List<ScreenPosition> GetScreenPositions()
{
if (VideoMode == VIDEO_MODE.NVIDIA)
{
return GetNVIDIAScreenPositions();
}
else if (VideoMode == VIDEO_MODE.AMD)
{
return GetAMDScreenPositions();
}
else if (VideoMode == VIDEO_MODE.WINDOWS)
{
return GetWindowsScreenPositions();
}
return new List<ScreenPosition>();
}
private List<ScreenPosition> GetNVIDIAScreenPositions()
{
// Set up some colours
Color primaryScreenColor = Color.FromArgb(0, 174, 241); // represents Primary screen blue
Color spannedScreenColor = Color.FromArgb(118, 185, 0); // represents NVIDIA Green
Color normalScreenColor = Color.FromArgb(155, 155, 155); // represents normal screen colour (gray)
// Now we create the screens structure from the AMD profile information
_screens = new List<ScreenPosition>();
int pathCount = _windowsDisplayConfig.DisplayConfigPaths.Length;
// First of all we need to figure out how many display paths we have.
if (pathCount < 1)
{
// Return an empty screen if we have no Display Config Paths to use!
return _screens;
}
// Now we need to check for Spanned screens (Surround)
if (_nvidiaDisplayConfig.MosaicConfig.IsMosaicEnabled && _nvidiaDisplayConfig.MosaicConfig.MosaicGridCount > 0)
{
for (int i = 0; i < _nvidiaDisplayConfig.MosaicConfig.MosaicGridCount; i++)
{
ScreenPosition screen = new ScreenPosition();
screen.Library = "NVIDIA";
if (_nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].DisplayCount > 1)
{
// It's a spanned screen!
// Set some basics about the screen
screen.SpannedScreens = new List<SpannedScreenPosition>();
screen.Name = "NVIDIA Surround/Mosaic";
screen.IsSpanned = true;
screen.SpannedRows = (int)_nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Rows;
screen.SpannedColumns = (int)_nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Columns;
screen.Colour = spannedScreenColor;
// This is a combined surround/mosaic screen
// We need to build the size of the screen to match it later so we check the MosaicViewports
uint minX = 0;
uint minY = 0;
uint maxX = 0;
uint maxY = 0;
uint overallX = 0;
uint overallY = 0;
int overallWidth = 0;
int overallHeight = 0;
for (int j = 0; j < _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].DisplayCount; j++)
{
SpannedScreenPosition spannedScreen = new SpannedScreenPosition();
spannedScreen.Name = _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Displays[j].DisplayId.ToString();
spannedScreen.Colour = spannedScreenColor;
// Calculate screen size
NV_RECT viewRect = _nvidiaDisplayConfig.MosaicConfig.MosaicViewports[i][j];
if (viewRect.Left < minX)
{
minX = viewRect.Left;
}
if (viewRect.Top < minY)
{
minY = viewRect.Top;
}
if (viewRect.Right > maxX)
{
maxX = viewRect.Right;
}
if (viewRect.Bottom > maxY)
{
maxY = viewRect.Bottom;
}
uint width = viewRect.Right - viewRect.Left + 1;
uint height = viewRect.Bottom - viewRect.Top + 1;
spannedScreen.ScreenX = (int)viewRect.Left;
spannedScreen.ScreenY = (int)viewRect.Top;
spannedScreen.ScreenWidth = (int)width;
spannedScreen.ScreenHeight = (int)height;
// Figure out the overall figures for the screen
if (viewRect.Left < overallX)
{
overallX = viewRect.Left;
}
if (viewRect.Top < overallY)
{
overallY = viewRect.Top;
}
overallWidth = (int)maxX - (int)minX + 1;
overallHeight = (int)maxY - (int)minY + 1;
spannedScreen.Row = i + 1;
spannedScreen.Column = j + 1;
// Add the spanned screen to the screen
screen.SpannedScreens.Add(spannedScreen);
}
// Need to look for the Windows layout details now we know the size of this display
// Set some basics about the screen
try
{
UInt32 displayId = _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Displays[0].DisplayId;
List<NV_DISPLAYCONFIG_PATH_INFO_V2> displaySources = _nvidiaDisplayConfig.DisplayConfigs;
bool breakOuterLoop = false;
foreach (var displaySource in displaySources)
{
foreach (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 targetInfo in displaySource.TargetInfo)
{
if (targetInfo.DisplayId == displayId)
{
screen.Name = displayId.ToString();
screen.ScreenX = displaySource.SourceModeInfo.Position.X;
screen.ScreenY = displaySource.SourceModeInfo.Position.Y;
screen.ScreenWidth = (int)displaySource.SourceModeInfo.Resolution.Width;
screen.ScreenHeight = (int)displaySource.SourceModeInfo.Resolution.Height;
breakOuterLoop = true;
break;
}
}
if (breakOuterLoop)
{
break;
}
}
}
catch (KeyNotFoundException ex)
{
// Thrown if the Windows display doesn't match the NVIDIA display.
// Typically happens during configuration of a new Mosaic mode.
// If we hit this issue, then we just want to skip over it, as we can update it later when the user pushes the button.
// This only happens due to the auto detection stuff functionality we have built in to try and update as quickly as we can.
// So its something that we can safely ignore if we hit this exception as it is part of the expect behaviour
continue;
}
catch (Exception ex)
{
// Some other exception has occurred and we need to report it.
//screen.Name = targetId.ToString();
//screen.DisplayConnector = displayMode.DisplayConnector;
screen.ScreenX = (int)overallX;
screen.ScreenY = (int)overallY;
screen.ScreenWidth = (int)overallWidth;
screen.ScreenHeight = (int)overallHeight;
}
}
else
{
// It's a standalone screen
screen.SpannedScreens = new List<SpannedScreenPosition>();
screen.Name = _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Displays[0].DisplayId.ToString();
screen.IsSpanned = false;
screen.SpannedRows = 1;
screen.SpannedColumns = 1;
screen.Colour = normalScreenColor;
try
{
UInt32 displayId = _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Displays[0].DisplayId;
List<NV_DISPLAYCONFIG_PATH_INFO_V2> displaySources = _nvidiaDisplayConfig.DisplayConfigs;
bool breakOuterLoop = false;
foreach (var displaySource in displaySources)
{
foreach (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 targetInfo in displaySource.TargetInfo)
{
if (targetInfo.DisplayId == displayId)
{
screen.Name = displayId.ToString();
screen.ScreenX = displaySource.SourceModeInfo.Position.X;
screen.ScreenY = displaySource.SourceModeInfo.Position.Y;
screen.ScreenWidth = (int)displaySource.SourceModeInfo.Resolution.Width;
screen.ScreenHeight = (int)displaySource.SourceModeInfo.Resolution.Height;
breakOuterLoop = true;
break;
}
}
if (breakOuterLoop)
{
break;
}
}
}
catch (KeyNotFoundException ex)
{
// Thrown if the Windows display doesn't match the NVIDIA display.
// Typically happens during configuration of a new Mosaic mode.
// If we hit this issue, then we just want to skip over it, as we can update it later when the user pushes the button.
// This only happens due to the auto detection stuff functionality we have built in to try and update as quickly as we can.
// So its something that we can safely ignore if we hit this exception as it is part of the expect behaviour
continue;
}
catch (Exception ex)
{
// Some other exception has occurred and we need to report it.
SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Unable to get the non-mosaic screen size for a secondary screen to a surround screen.");
}
}
// If we're at the 0,0 coordinate then we're the primary monitor
if (screen.ScreenX == 0 && screen.ScreenY == 0)
{
// Record we're primary screen
screen.IsPrimary = true;
// Change the colour to be the primary colour, but only if it isn't a surround screen
if (screen.Colour != spannedScreenColor)
{
screen.Colour = primaryScreenColor;
}
}
// Force the taskbar edge to the bottom as it is an NVIDIA surround screen
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Added a new NVIDIA Spanned Screen {screen.Name} ({screen.ScreenWidth}x{screen.ScreenHeight}) at position {screen.ScreenX},{screen.ScreenY}.");
_screens.Add(screen);
}
}
else
{
// If mosaic isn't enabled then we use the NVIDIA DisplayConfig structure to find the details
try
{
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Mosaic isn't enabled so using the DisplayConfig based screen details.");
List<NV_DISPLAYCONFIG_PATH_INFO_V2> displaySources = _nvidiaDisplayConfig.DisplayConfigs;
foreach (var displaySource in displaySources)
{
int targetInfoIndex = 0;
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Processing screen source index #{targetInfoIndex}.");
foreach (NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 targetInfo in displaySource.TargetInfo)
{
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Processing target screen ID:{targetInfo.DisplayId}.");
ScreenPosition screen = new ScreenPosition();
screen.Library = "NVIDIA";
// Find out if we're a cloned screen
if (_nvidiaDisplayConfig.IsCloned && displaySource.TargetInfoCount > 1)
{
if (targetInfoIndex == 0)
{
// Show that this window has clones, and show how many there are.
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: The screen ID:{targetInfo.DisplayId} is the source of a cloned group.");
screen.IsClone = true;
screen.ClonedCopies = (int)displaySource.TargetInfoCount;
}
else
{
// Skip getting layout details from the clones themselves, as we have no idea where they are!
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: The screen ID:{targetInfo.DisplayId} is part of a cloned group (but we don'tt need to show it so skipping).");
continue;
}
}
else
{
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: The screen ID:{targetInfo.DisplayId} is NOT part of a cloned group.");
}
// It's a normal screen
screen.SpannedScreens = new List<SpannedScreenPosition>();
screen.Name = targetInfo.DisplayId.ToString();
screen.IsSpanned = false;
screen.SpannedRows = 1;
screen.SpannedColumns = 1;
screen.Colour = normalScreenColor;
screen.ScreenX = displaySource.SourceModeInfo.Position.X;
screen.ScreenY = displaySource.SourceModeInfo.Position.Y;
screen.ScreenWidth = (int)displaySource.SourceModeInfo.Resolution.Width;
screen.ScreenHeight = (int)displaySource.SourceModeInfo.Resolution.Height;
if (screen.ScreenWidth == 0)
{
SharedLogger.logger.Error($"ProfileItem/GetNVIDIAScreenPositions: The screen width is 0 and it shouldn't be! Skipping this display id #{targetInfo.DisplayId.ToString()}.");
}
if (screen.ScreenHeight == 0)
{
SharedLogger.logger.Error($"ProfileItem/GetNVIDIAScreenPositions: The screen height is 0 and it shouldn't be! Skipping this display id #{targetInfo.DisplayId.ToString()}.");
}
// If we're at the 0,0 coordinate then we're the primary monitor
if (screen.ScreenX == 0 && screen.ScreenY == 0)
{
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: NVIDIA Screen {screen.Name} is the primary monitor.");
// Record we're primary screen
screen.IsPrimary = true;
// Change the colour to be the primary colour, but only if it isn't a surround screen
if (screen.Colour != spannedScreenColor)
{
screen.Colour = primaryScreenColor;
}
}
try
{
if (_nvidiaDisplayConfig.DisplayNames.ContainsKey(targetInfo.DisplayId.ToString()))
{
string windowsDisplayName = _nvidiaDisplayConfig.DisplayNames[targetInfo.DisplayId.ToString()];
UInt32 windowsUID = _windowsDisplayConfig.DisplaySources[windowsDisplayName].First().TargetId;
// IMPORTANT: This lookup WILL DEFINITELY CAUSE AN EXCEPTION right after windows changes back from
// NVIDIA Surround to a non-surround profile. This is expected, as it is caused bythe way Windows is SOOOO slow to update
// the taskbar locations in memory (it takes up to 15 seconds!). NOthing I can do, except put this protection in place :( .
KeyValuePair<string, TaskBarLayout> matchingDisplayEntry = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains($"UID{windowsUID }"));
screen.TaskBarEdge = matchingDisplayEntry.Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Position of the taskbar on display {targetInfo.DisplayId} is on the {screen.TaskBarEdge } of the screen.");
}
else
{
SharedLogger.logger.Warn($"ProfileItem/GetNVIDIAScreenPositions: Couldn't get the position of the taskbar on display {targetInfo.DisplayId} so assuming its at the bottom.");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Warn(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetInfo.DisplayId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: (2) Added a non-surround NVIDIA Screen {screen.Name} ({screen.ScreenWidth}x{screen.ScreenHeight}) at position {screen.ScreenX},{screen.ScreenY}.");
_screens.Add(screen);
targetInfoIndex++;
}
}
}
catch (Exception ex)
{
// Some other exception has occurred and we need to report it.
SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception while trying to get the screen details. (#2) Mosaic isn't enabled, but unable to get the screen details. ");
}
}
// Now we also need to try and find if there are any other displays connected that don't use an NVIDIA card
try
{
// Get the list of Windows screens
List<ScreenPosition> windowsScreens = FindAllWindowsScreens();
// Now look through the list of Windows Screens to see if there are any we haven't already go from NVIDIA.
// If there are new ones, then add them to the list we're returning
foreach (ScreenPosition windowsScreen in windowsScreens)
{
// Check if we already have this screen information via NVIDIA driver
if (_screens.Any(scr => scr.ScreenX == windowsScreen.ScreenX && scr.ScreenY == windowsScreen.ScreenY && scr.ScreenWidth == windowsScreen.ScreenWidth && scr.ScreenHeight == windowsScreen.ScreenHeight)){
// If the WindowsScreen is already recorded via NVIDIA, then we just ignore it and skip it.
continue;
}
// If we get here then it's a screen that we don't have via NVIDIA, so we need to add it
_screens.Add(windowsScreen);
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: (3) Added a Windows Screen {windowsScreen.Name} ({windowsScreen.ScreenWidth}x{windowsScreen.ScreenHeight}) at position {windowsScreen.ScreenX},{windowsScreen.ScreenY}.");
}
}
catch (Exception ex)
{
// Some other exception has occurred and we need to report it.
SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception while trying to find any additional windows screens that the NVIDIA driver didn't tell us about.");
}
return _screens;
}
private List<ScreenPosition> GetAMDScreenPositions()
{
// Set up some colours
Color primaryScreenColor = Color.FromArgb(0, 174, 241); // represents Primary screen blue
Color spannedScreenColor = Color.FromArgb(221, 0, 49); // represents AMD Red
Color normalScreenColor = Color.FromArgb(155, 155, 155); // represents normal screen colour (gray)
// Now we create the screens structure from the AMD profile information
_screens = new List<ScreenPosition>();
int pathCount = _windowsDisplayConfig.DisplayConfigPaths.Length;
// First of all we need to figure out how many display paths we have.
if (pathCount < 1)
{
// Return an empty screen if we have no Display Config Paths to use!
return _screens;
}
// Go through the AMD Eyefinity screens
if (_amdDisplayConfig.SlsConfig.IsSlsEnabled)
{
for (int i = 0; i < _amdDisplayConfig.DisplayMaps.Count; i++)
{
ScreenPosition screen = new ScreenPosition();
screen.Library = "AMD";
screen.Colour = normalScreenColor;
// This is multiple screens
screen.SpannedScreens = new List<SpannedScreenPosition>();
screen.Name = "AMD Eyefinity";
//screen.IsSpanned = true;
screen.SpannedRows = _amdDisplayConfig.SlsConfig.SLSMapConfigs[i].SLSMap.Grid.SLSGridRow;
screen.SpannedColumns = _amdDisplayConfig.SlsConfig.SLSMapConfigs[i].SLSMap.Grid.SLSGridColumn;
screen.Colour = spannedScreenColor;
//screen.Name = targetId.ToString();
//screen.DisplayConnector = displayMode.DisplayConnector;
screen.ScreenX = _amdDisplayConfig.DisplayMaps[i].DisplayMode.XPos;
screen.ScreenY = _amdDisplayConfig.DisplayMaps[i].DisplayMode.YPos;
screen.ScreenWidth = _amdDisplayConfig.DisplayMaps[i].DisplayMode.XRes;
screen.ScreenHeight = _amdDisplayConfig.DisplayMaps[i].DisplayMode.YRes;
// If we're at the 0,0 coordinate then we're the primary monitor
if (screen.ScreenX == 0 && screen.ScreenY == 0)
{
// Record we're primary screen
screen.IsPrimary = true;
// Change the colour to be the primary colour, but only if it isn't a surround screen
if (screen.Colour != spannedScreenColor)
{
screen.Colour = primaryScreenColor;
}
}
// Set the taskbar location for this screen at the bottom
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
SharedLogger.logger.Trace($"ProfileItem/GetAMDScreenPositions: Added a new AMD Spanned Screen {screen.Name} ({screen.ScreenWidth}x{screen.ScreenHeight}) at position {screen.ScreenX},{screen.ScreenY}.");
_screens.Add(screen);
}
}
// Next, go through the screens as Windows knows them, and then enhance the info with Eyefinity data if it applies
foreach (var path in _windowsDisplayConfig.DisplayConfigPaths)
{
// For each path we go through and get the relevant info we need.
if (_windowsDisplayConfig.DisplayConfigPaths.Length > 0)
{
UInt64 adapterId = path.SourceInfo.AdapterId.Value;
UInt32 sourceId = path.SourceInfo.Id;
UInt32 targetId = path.TargetInfo.Id;
// Set some basics about the screen
ScreenPosition screen = new ScreenPosition();
screen.Library = "AMD";
//screen.AdapterName = adapterId.ToString();
screen.IsSpanned = false;
screen.Colour = normalScreenColor; // this is the default unless overridden by the primary screen
screen.IsClone = false;
screen.ClonedCopies = 0;
try
{
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains($"UID{targetId}")).Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetNVIDIAScreenPositions: Position of the taskbar on display {targetId} is on the {screen.TaskBarEdge } of the screen.");
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Warn(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
// Find out if this source is cloned
foreach (var displaySource in _windowsDisplayConfig.DisplaySources)
{
// All of the items in the Value array are the same source, so we can just check the first one in the array!
if (displaySource.Value[0].SourceId == sourceId)
{
// If there is more than one item in the array, then it's a cloned source!
if (displaySource.Value.Count > 1)
{
// We have a cloned display
screen.IsClone = true;
screen.ClonedCopies = displaySource.Value.Count;
}
break;
}
}
// Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies
foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes)
{
// Find the matching Display Config Source Mode
if (displayMode.InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE && displayMode.Id == sourceId && displayMode.AdapterId.Value == adapterId)
{
screen.Name = targetId.ToString();
//screen.DisplayConnector = displayMode.DisplayConnector;
screen.ScreenX = displayMode.SourceMode.Position.X;
screen.ScreenY = displayMode.SourceMode.Position.Y;
screen.ScreenWidth = (int)displayMode.SourceMode.Width;
screen.ScreenHeight = (int)displayMode.SourceMode.Height;
// If we're at the 0,0 coordinate then we're the primary monitor
if (screen.ScreenX == 0 && screen.ScreenY == 0)
{
screen.IsPrimary = true;
screen.Colour = primaryScreenColor;
}
break;
}
}
// Decide if this screen is one we've had earlier, and if so, skip it
if (_screens.Any(s => s.ScreenX == screen.ScreenX && s.ScreenY == screen.ScreenY && s.ScreenWidth == screen.ScreenWidth && s.ScreenHeight == screen.ScreenHeight))
{
SharedLogger.logger.Trace($"ProfileItem/GetAMDScreenPositions: We've already got the {screen.Name} ({screen.ScreenWidth}x{screen.ScreenHeight}) screen from the AMD driver, so skipping it from the Windows driver.");
continue;
}
if (_windowsDisplayConfig.DisplayHDRStates.Count > 0)
{
foreach (ADVANCED_HDR_INFO_PER_PATH hdrInfo in _windowsDisplayConfig.DisplayHDRStates)
{
// Find the matching HDR information
if (hdrInfo.Id == targetId)
{
// HDR information
if (hdrInfo.AdvancedColorInfo.AdvancedColorSupported)
{
screen.HDRSupported = true;
if (hdrInfo.AdvancedColorInfo.AdvancedColorEnabled)
{
screen.HDREnabled = true;
}
else
{
screen.HDREnabled = false;
}
}
else
{
screen.HDRSupported = false;
screen.HDREnabled = false;
}
break;
}
}
}
SharedLogger.logger.Trace($"ProfileItem/GetAMDScreenPositions: Added a new Screen {screen.Name} ({screen.ScreenWidth}x{screen.ScreenHeight}) at position {screen.ScreenX},{screen.ScreenY}.");
_screens.Add(screen);
}
}
// Now we also need to try and find if there are any other displays connected that don't use an AMD card
try
{
// Get the list of Windows screens
List<ScreenPosition> windowsScreens = FindAllWindowsScreens();
// Now look through the list of Windows Screens to see if there are any we haven't already go from AMD.
// If there are new ones, then add them to the list we're returning
foreach (ScreenPosition windowsScreen in windowsScreens)
{
// Check if we already have this screen information via AMD driver
if (_screens.Any(scr => scr.ScreenX == windowsScreen.ScreenX && scr.ScreenY == windowsScreen.ScreenY && scr.ScreenWidth == windowsScreen.ScreenWidth && scr.ScreenHeight == windowsScreen.ScreenHeight)){
// If the WindowsScreen is already recorded via AMD, then we just ignore it and skip it.
continue;
}
// If we get here then it's a screen that we don't have via AMD, so we need to add it
_screens.Add(windowsScreen);
SharedLogger.logger.Trace($"ProfileItem/GetAMDScreenPositions: (3) Added a Windows Screen {windowsScreen.Name} ({windowsScreen.ScreenWidth}x{windowsScreen.ScreenHeight}) at position {windowsScreen.ScreenX},{windowsScreen.ScreenY}.");
}
}
catch (Exception ex)
{
// Some other exception has occurred and we need to report it.
SharedLogger.logger.Error(ex, $"ProfileItem/GetAMDScreenPositions: Exception while trying to find any additional windows screens that the AMD driver didn't tell us about.");
}
return _screens;
}
private List<ScreenPosition> GetWindowsScreenPositions()
{
_screens = FindAllWindowsScreens();
return _screens;
}
private List<ScreenPosition> FindAllWindowsScreens()
{
// Set up some colours
Color primaryScreenColor = Color.FromArgb(0, 174, 241); // represents Primary screen blue
Color normalScreenColor = Color.FromArgb(155, 155, 155); // represents normal screen colour (gray)
// Now we create the screens structure from the AMD profile information
List<ScreenPosition> windowsScreens = new List<ScreenPosition>();
int pathCount = _windowsDisplayConfig.DisplayConfigPaths.Length;
// First of all we need to figure out how many display paths we have.
if (pathCount < 1)
{
// Return an empty screen if we have no Display Config Paths to use!
return windowsScreens;
}
foreach (var path in _windowsDisplayConfig.DisplayConfigPaths)
{
// For each path we go through and get the relevant info we need.
if (_windowsDisplayConfig.DisplayConfigPaths.Length > 0)
{
UInt64 adapterId = path.SourceInfo.AdapterId.Value;
UInt32 sourceId = path.SourceInfo.Id;
UInt32 targetId = path.TargetInfo.Id;
// Set some basics about the screen
ScreenPosition screen = new ScreenPosition();
screen.Library = "WINDOWS";
//screen.AdapterName = adapterId.ToString();
screen.IsSpanned = false;
screen.Colour = normalScreenColor; // this is the default unless overridden by the primary screen
screen.IsClone = false;
screen.ClonedCopies = 0;
try
{
// IMPORTANT: This lookup WILL DEFINITELY CAUSE AN EXCEPTION right after windows changes back from
// NVIDIA Surround to a non-surround profile. This is expected, as it is caused bythe way Windows is SOOOO slow to update
// the taskbar locations in memory (it takes up to 15 seconds!). Nothing I can do, except put this protection in place :( .
Fixed error when returning from Mosaic layouts in WIndows 10 There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters. The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue. WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell. The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal. This should (fingers crossed) fix #119! This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
2022-07-14 10:41:59 +00:00
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue != null && tbr.Value.RegKeyValue.Contains($"UID{targetId}")).Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on display {targetId} is on the {screen.TaskBarEdge } of the screen.");
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Warn(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
// Find out if this source is cloned
foreach (var displaySource in _windowsDisplayConfig.DisplaySources)
{
// All of the items in the Value array are the same source, so we can just check the first one in the array!
if (displaySource.Value[0].SourceId == sourceId)
{
// If there is more than one item in the array, then it's a cloned source!
if (displaySource.Value.Count > 1)
{
// We have a cloned display
screen.IsClone = true;
screen.ClonedCopies = displaySource.Value.Count;
}
break;
}
}
// Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies
foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes)
{
// Find the matching Display Config Source Mode
if (displayMode.InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE && displayMode.Id == sourceId && displayMode.AdapterId.Value == adapterId)
{
screen.Name = targetId.ToString();
//screen.DisplayConnector = displayMode.DisplayConnector;
screen.ScreenX = displayMode.SourceMode.Position.X;
screen.ScreenY = displayMode.SourceMode.Position.Y;
screen.ScreenWidth = (int)displayMode.SourceMode.Width;
screen.ScreenHeight = (int)displayMode.SourceMode.Height;
// If we're at the 0,0 coordinate then we're the primary monitor
if (screen.ScreenX == 0 && screen.ScreenY == 0)
{
screen.IsPrimary = true;
screen.Colour = primaryScreenColor;
}
break;
}
}
foreach (ADVANCED_HDR_INFO_PER_PATH hdrInfo in _windowsDisplayConfig.DisplayHDRStates)
{
// Find the matching HDR information
if (hdrInfo.Id == targetId)
{
// HDR information
if (hdrInfo.AdvancedColorInfo.AdvancedColorSupported)
{
screen.HDRSupported = true;
if (hdrInfo.AdvancedColorInfo.AdvancedColorEnabled)
{
screen.HDREnabled = true;
}
else
{
screen.HDREnabled = false;
}
}
else
{
screen.HDRSupported = false;
screen.HDREnabled = false;
}
break;
}
}
2022-02-11 20:18:03 +00:00
// Now we try to set the taskbar positions
if (screen.IsPrimary)
{
// If the screen is the primary screen, then we check if we need to use the StuckRect 'Settings' reg keys
// rather than the MMStuckRect reg keys
try
{
Fixed error when returning from Mosaic layouts in WIndows 10 There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters. The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue. WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell. The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal. This should (fingers crossed) fix #119! This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
2022-07-14 10:41:59 +00:00
if (_windowsDisplayConfig.TaskBarLayout.Count(tbr => tbr.Value.RegKeyValue != null && tbr.Value.RegKeyValue.Contains("Settings")) > 0)
{
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains("Settings")).Value.Edge;
Fixed error when returning from Mosaic layouts in WIndows 10 There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters. The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue. WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell. The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal. This should (fingers crossed) fix #119! This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
2022-07-14 10:41:59 +00:00
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on the primary display {targetId} is on the {screen.TaskBarEdge} of the screen.");
}
else
{
SharedLogger.logger.Warn($"ProfileItem/GetWindowsScreenPositions: Problem trying to get the position of the taskbar on primary display {targetId}. Assuming it's on the bottom edge.");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
2022-02-11 20:18:03 +00:00
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Warn(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on primary display {targetId}");
2022-03-29 21:56:24 +00:00
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
2022-02-11 20:18:03 +00:00
}
}
else
{
try
{
Fixed error when returning from Mosaic layouts in WIndows 10 There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters. The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue. WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell. The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal. This should (fingers crossed) fix #119! This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
2022-07-14 10:41:59 +00:00
int numMatches = _windowsDisplayConfig.TaskBarLayout.Count(tbr => tbr.Value.RegKeyValue != null && tbr.Value.RegKeyValue.Contains($"UID{targetId}"));
if (numMatches > 1)
{
var matchingTbls = (from tbl in _windowsDisplayConfig.TaskBarLayout where tbl.Value.RegKeyValue.Contains($"UID{targetId}") select tbl.Value).ToList();
bool foundIt = false;
foreach (var matchingTbl in matchingTbls)
{
// find display source that matches.
foreach (var displaySource in _windowsDisplayConfig.DisplaySources)
{
foreach (var displayDevice in displaySource.Value)
{
// We want to find the displaydevice that has the same adapter id
if (displayDevice.AdapterId.Value == adapterId && displayDevice.DevicePath.Contains(matchingTbl.RegKeyValue))
{
// This is the actual display we want!
foundIt = true;
screen.TaskBarEdge = matchingTbl.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on display {targetId} is on the {screen.TaskBarEdge } of the screen.");
break;
}
}
// If we've found it already then stop looking
if (foundIt)
{
break;
}
}
}
if (!foundIt)
{
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains($"UID{targetId}")).Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Couldn't find the taskbar location for display {targetId} when it had multiple matching UIDs. Assuming the screen edge is at the bottom of the screen.");
}
}
else if (numMatches == 1)
{
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains($"UID{targetId}")).Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on display {targetId} is on the {screen.TaskBarEdge } of the screen.");
}
else
{
SharedLogger.logger.Warn($"ProfileItem/GetWindowsScreenPositions: Problem trying to get the position of the taskbar on display {targetId} as UID doesn't exist. Assuming it's on the bottom edge.");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
2022-02-11 20:18:03 +00:00
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Warn(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
2022-03-29 21:56:24 +00:00
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
2022-02-11 20:18:03 +00:00
}
}
2022-02-11 20:18:03 +00:00
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Added a new Screen {screen.Name} ({screen.ScreenWidth}x{screen.ScreenHeight}) at position {screen.ScreenX},{screen.ScreenY}.");
windowsScreens.Add(screen);
}
}
return windowsScreens;
}
public int CompareTo(ProfileItem other)
{
int result = CompareToValues(other);
// If comparison based solely on values
// returns zero, indicating that two instances
// are equal in those fields they have in common,
// only then we break the tie by comparing
// data types of the two instances.
if (result == 0)
result = CompareTypes(other);
return result;
}
protected virtual int CompareToValues(ProfileItem other)
{
if (object.ReferenceEquals(other, null))
return 1; // All instances are greater than null
// Base class simply compares Mark properties
return Name.CompareTo(other.Name);
}
protected int CompareTypes(ProfileItem other)
{
// Base type is considered less than derived type
// when two instances have the same values of
// base fields.
// Instances of two distinct derived types are
// ordered by comparing full names of their
// types when base fields are equal.
// This is consistent comparison rule for all
// instances of the two derived types.
int result = 0;
Type thisType = this.GetType();
Type otherType = other.GetType();
if (otherType.IsSubclassOf(thisType))
result = -1; // other is subclass of this class
else if (thisType.IsSubclassOf(otherType))
result = 1; // this is subclass of other class
else if (thisType != otherType)
result = thisType.FullName.CompareTo(otherType.FullName);
// cut the tie with a test that returns
// the same value for all objects
return result;
}
// The object specific Equals
public bool Equals(ProfileItem other)
{
// Check references
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
// Check the object fields
// ProfileDisplayIdentifiers may be the same but in different order within the array, so we need to handle
// that fact.
return NVIDIADisplayConfig.Equals(other.NVIDIADisplayConfig) &&
AMDDisplayConfig.Equals(other.AMDDisplayConfig) &&
WindowsDisplayConfig.Equals(other.WindowsDisplayConfig) &&
ProfileDisplayIdentifiers.SequenceEqual (other.ProfileDisplayIdentifiers);
}
// The public override for the Object.Equals
public override bool Equals(Object obj)
{
// Check references
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
// If different types then can't be true
if (obj.GetType() == this.GetType()) return false;
if (!(obj is ProfileItem)) return false;
// Check the object fields as this must the same object as obj, and we need to test in more detail
return Equals((ProfileItem) obj);
}
// If Equals() returns true for this object compared to another
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
// Calculate the hash code for the product.
return (NVIDIADisplayConfig, AMDDisplayConfig, WindowsDisplayConfig, ProfileDisplayIdentifiers).GetHashCode();
}
public static bool operator ==(ProfileItem lhs, ProfileItem rhs)
{
/*if (object.ReferenceEquals(lhs, rhs))
return true;
if (!object.ReferenceEquals(lhs, null) &&
!object.ReferenceEquals(rhs, null) &&
lhs.Equals(rhs))
return true;
return false;*/
return Equals(lhs, rhs);
}
public static bool operator !=(ProfileItem lhs, ProfileItem rhs)
{
return !Equals(lhs, rhs);
}
// IMPORTANT - This ProfileItem ToString function is required to make the Profile ImageListView work properly! DO NOT DELETE!
public override string ToString()
{
return (Name ?? Language.UN_TITLED_PROFILE);
}
public string CreateCommand()
{
return $"{Application.ExecutablePath} {DisplayMagicianStartupAction.ChangeProfile} \"{UUID}\"";
}
}
}