Merged v2.4.0 release into branch

This commit is contained in:
Terry MacDonald 2022-07-01 14:04:27 +12:00
commit 8122295716
37 changed files with 4666 additions and 2220 deletions

View File

@ -1,75 +0,0 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.Toolkit.Uwp.Notifications;
using Microsoft.QueryStringDotNET;
using System.Windows.Forms;
using DisplayMagician.UIForms;
using DisplayMagicianShared;
namespace DisplayMagician
{
// The GUID must be unique to your app. Create a new GUID if copying this code.
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(INotificationActivationCallback))]
[Guid(Program.AppActivationId), ComVisible(true)]
public class DesktopNotificationActivator : NotificationActivator
{
public override void OnActivated(string arguments, NotificationUserInput userInput, string appUserModelId)
{
// Invoke the code we're running on the UI Thread to avoid
// cross thread exceptions
Program.AppMainForm.Invoke((MethodInvoker)delegate
{
// This code is running on the main UI thread!
// Parse the query string (using NuGet package QueryString.NET)
QueryString args = QueryString.Parse(arguments);
foreach (QueryStringParameter myArg in args)
{
if (myArg.Name.Equals("action",StringComparison.OrdinalIgnoreCase))
{
// See what action is being requested
switch (args["action"])
{
// Open the Main window
case "open":
// Open the Main DisplayMagician Window
Program.AppMainForm.openApplicationWindow();
break;
// Exit the application
case "exit":
// Exit the application (overriding the close restriction)
Program.AppMainForm.exitApplication();
break;
// Stop waiting so that the monitoring stops, and the UI becomes free
case "stopWaiting":
Program.AppCancellationTokenSource.Cancel();
break;
default:
break;
}
}
}
});
}
private void OpenWindowIfNeeded()
{
// Make sure we have a window open (in case user clicked toast while app closed)
if (Program.AppMainForm == null)
{
Program.AppMainForm = new MainForm();
}
// Activate the window, bringing it to focus
Program.AppMainForm.openApplicationWindow();
}
}
}

View File

@ -96,7 +96,6 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="DesktopNotificationActivator.cs" />
<Compile Include="GameLibraries\XboxGame.cs" />
<Compile Include="GameLibraries\GOGGame.cs" />
<Compile Include="GameLibraries\XboxLibrary.cs" />
@ -311,7 +310,7 @@
<Version>4.0.0-alpha5</Version>
</PackageReference>
<PackageReference Include="Autoupdater.NET.Official">
<Version>1.7.0</Version>
<Version>1.7.3</Version>
</PackageReference>
<PackageReference Include="BootMeUp">
<Version>1.2.0</Version>
@ -347,10 +346,10 @@
<Version>2.1.0</Version>
</PackageReference>
<PackageReference Include="NLog">
<Version>4.7.15</Version>
<Version>5.0.1</Version>
</PackageReference>
<PackageReference Include="protobuf-net">
<Version>3.1.0</Version>
<Version>3.1.17</Version>
</PackageReference>
<PackageReference Include="QueryString.NET">
<Version>1.0.0</Version>
@ -409,6 +408,15 @@
<Isolated>False</Isolated>
<EmbedInteropTypes>False</EmbedInteropTypes>
</COMReference>
<COMReference Include="NETWORKLIST">
<Guid>{DCB00D01-570F-4A9B-8D69-199FDBA5723B}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
<COMReference Include="Shell32">
<Guid>{50A7E9B0-70EF-11D1-B75A-00A0C90564FE}</Guid>
<VersionMajor>1</VersionMajor>

View File

@ -175,6 +175,7 @@ namespace DisplayMagician.GameLibraries
}
catch (Exception ex)
{
logger.Warn(ex, $"EpicLibrary/IsRunning: Exception while trying to get the Epic Library processes with names: {_epicProcessList.ToString()}");
return false;
}
}

View File

@ -178,7 +178,8 @@ namespace DisplayMagician.GameLibraries
else
return false;
}
catch (Exception ex) {
catch (Exception ex) {
logger.Warn(ex, $"GogLibrary/IsRunning: Exception while trying to get the GOG Library processes with names: {_gogProcessList.ToString()}");
return false;
}
}

View File

@ -8,12 +8,12 @@ namespace DisplayMagician.GameLibraries
{
public enum SupportedGameLibraryType
{
Unknown,
Steam,
Uplay,
Origin,
Epic,
GOG
Unknown = 0,
Steam = 1,
Uplay = 2,
Origin = 3,
Epic = 4,
GOG = 5
}
public class GameLibrary

View File

@ -176,6 +176,7 @@ namespace DisplayMagician.GameLibraries
}
catch (Exception ex)
{
logger.Warn(ex, $"OriginLibrary/IsRunning: Exception while trying to get the Origin Library processes with names: {_originProcessList.ToString()}");
return false;
}
}

View File

@ -193,6 +193,7 @@ namespace DisplayMagician.GameLibraries
}
catch (Exception ex)
{
logger.Warn(ex, $"SteamLibrary/IsRunning: Exception while trying to get the steam library processes matching process names: {_steamProcessList.ToString()}.");
return false;
}
}
@ -705,86 +706,105 @@ namespace DisplayMagician.GameLibraries
{
// Work out the path to the appmanifests for this steamLibrary
string steamLibraryAppManifestPath = Path.Combine(steamLibraryPath, @"steamapps");
// Get the names of the App Manifests for the games installed in this SteamLibrary
string[] steamLibraryAppManifestFilenames = Directory.GetFiles(steamLibraryAppManifestPath, "appmanifest_*.acf");
// Go through each app and extract it's details
foreach (string steamLibraryAppManifestFilename in steamLibraryAppManifestFilenames)
try
{
logger.Trace($"SteamLibrary/LoadInstalledGames: Found {steamLibraryAppManifestFilename} app manifest within steam library {steamLibraryPath}");
// Read in the contents of the file
string steamLibraryAppManifestText = File.ReadAllText(steamLibraryAppManifestFilename);
// Grab the appid from the file
Regex appidRegex = new Regex(@"""appid""\s+""(\d+)""", RegexOptions.IgnoreCase);
Match appidMatches = appidRegex.Match(steamLibraryAppManifestText);
if (appidMatches.Success)
// Get the names of the App Manifests for the games installed in this SteamLibrary
string[] steamLibraryAppManifestFilenames = Directory.GetFiles(steamLibraryAppManifestPath, "appmanifest_*.acf");
// Go through each app and extract it's details
foreach (string steamLibraryAppManifestFilename in steamLibraryAppManifestFilenames)
{
if (!String.IsNullOrWhiteSpace(appidMatches.Groups[1].Value))
logger.Trace($"SteamLibrary/LoadInstalledGames: Found {steamLibraryAppManifestFilename} app manifest within steam library {steamLibraryPath}");
// Read in the contents of the file
string steamLibraryAppManifestText = File.ReadAllText(steamLibraryAppManifestFilename);
// Grab the appid from the file
Regex appidRegex = new Regex(@"""appid""\s+""(\d+)""", RegexOptions.IgnoreCase);
Match appidMatches = appidRegex.Match(steamLibraryAppManifestText);
if (appidMatches.Success)
{
string steamGameId = appidMatches.Groups[1].Value;
logger.Trace($"SteamLibrary/LoadInstalledGames: Found Steam Game ID {steamGameId} within {steamLibraryAppManifestFilename} steam app manifest within steam library {steamLibraryPath}");
// Check if this game is one that was installed
if (steamAppInfo.ContainsKey(steamGameId))
if (!String.IsNullOrWhiteSpace(appidMatches.Groups[1].Value))
{
logger.Trace($"SteamLibrary/LoadInstalledGames: Steam Game ID {steamGameId} is installed within steam library {steamLibraryPath}!");
// This game is an installed game! so we start to populate it with data!
string steamGameExe = "";
string steamGameName = steamAppInfo[steamGameId].GameName;
// Construct the full path to the game dir from the appInfo and libraryAppManifest data
string steamGameInstallDir = Path.Combine(steamLibraryPath, @"steamapps", @"common", steamAppInfo[steamGameId].GameInstallDir);
logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game ID {steamGameId} at {steamGameInstallDir }");
// And finally we try to populate the 'where', to see what gets run
// And so we can extract the process name
if (steamAppInfo[steamGameId].GameExes.Count > 0)
string steamGameId = appidMatches.Groups[1].Value;
logger.Trace($"SteamLibrary/LoadInstalledGames: Found Steam Game ID {steamGameId} within {steamLibraryAppManifestFilename} steam app manifest within steam library {steamLibraryPath}");
// Check if this game is one that was installed
if (steamAppInfo.ContainsKey(steamGameId))
{
foreach (string gameExe in steamAppInfo[steamGameId].GameExes)
logger.Trace($"SteamLibrary/LoadInstalledGames: Steam Game ID {steamGameId} is installed within steam library {steamLibraryPath}!");
// This game is an installed game! so we start to populate it with data!
string steamGameExe = "";
string steamGameName = steamAppInfo[steamGameId].GameName;
// Construct the full path to the game dir from the appInfo and libraryAppManifest data
string steamGameInstallDir = Path.Combine(steamLibraryPath, @"steamapps", @"common", steamAppInfo[steamGameId].GameInstallDir);
logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game ID {steamGameId} at {steamGameInstallDir }");
// And finally we try to populate the 'where', to see what gets run
// And so we can extract the process name
if (steamAppInfo[steamGameId].GameExes.Count > 0)
{
steamGameExe = Path.Combine(steamGameInstallDir, gameExe);
logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
// If the game executable exists, then we can proceed
if (File.Exists(steamGameExe))
foreach (string gameExe in steamAppInfo[steamGameId].GameExes)
{
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
break;
steamGameExe = Path.Combine(steamGameInstallDir, gameExe);
logger.Trace($"SteamLibrary/LoadInstalledGames: Looking for Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
// If the game executable exists, then we can proceed
if (File.Exists(steamGameExe))
{
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Exe {steamGameExe} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
break;
}
}
}
}
// Next, we need to get the Icons we want to use, and make sure it's the latest one.
string steamGameIconPath = "";
// First of all, we attempt to use the Icon that Steam has cached, if it's available, as that will be updated to the latest
if (File.Exists(steamAppInfo[steamGameId].GameIconPath) && steamAppInfo[steamGameId].GameIconPath.EndsWith(".ico"))
{
steamGameIconPath = steamAppInfo[steamGameId].GameIconPath;
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
// Next, we need to get the Icons we want to use, and make sure it's the latest one.
string steamGameIconPath = "";
// First of all, we attempt to use the Icon that Steam has cached, if it's available, as that will be updated to the latest
if (File.Exists(steamAppInfo[steamGameId].GameIconPath) && steamAppInfo[steamGameId].GameIconPath.EndsWith(".ico"))
{
steamGameIconPath = steamAppInfo[steamGameId].GameIconPath;
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
}
// If there isn't an icon for us to use, then we need to extract one from the Game Executables
else if (!String.IsNullOrEmpty(steamGameExe))
{
steamGameIconPath = steamGameExe;
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
}
// The absolute worst case means we don't have an icon to use. SO we use the Steam one.
else
{
// And we have to make do with a Steam Icon
logger.Debug($"SteamLibrary/LoadInstalledGames: Couldn't find Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} so using default Steam Icon");
steamGameIconPath = _steamPath;
}
// And we add the Game to the list of games we have!
SteamGame gameToAdd = new SteamGame(steamGameId, steamGameName, steamGameExe, steamGameIconPath);
_allSteamGames.Add(gameToAdd);
logger.Debug($"SteamLibrary/LoadInstalledGames: Adding Steam Game with game id {steamGameId}, name {steamGameName}, game exe {steamGameExe} and icon path {steamGameIconPath}");
}
// If there isn't an icon for us to use, then we need to extract one from the Game Executables
else if (!String.IsNullOrEmpty(steamGameExe))
{
steamGameIconPath = steamGameExe;
logger.Debug($"SteamLibrary/LoadInstalledGames: Found Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} at {steamGameInstallDir }");
}
// The absolute worst case means we don't have an icon to use. SO we use the Steam one.
else
{
// And we have to make do with a Steam Icon
logger.Debug($"SteamLibrary/LoadInstalledGames: Couldn't find Steam Game Icon Path {steamGameIconPath} for Steam Game ID {steamGameId} so using default Steam Icon");
steamGameIconPath = _steamPath;
}
// And we add the Game to the list of games we have!
SteamGame gameToAdd = new SteamGame(steamGameId, steamGameName, steamGameExe, steamGameIconPath);
_allSteamGames.Add(gameToAdd);
logger.Debug($"SteamLibrary/LoadInstalledGames: Adding Steam Game with game id {steamGameId}, name {steamGameName}, game exe {steamGameExe} and icon path {steamGameIconPath}");
}
}
}
}
}
catch (DirectoryNotFoundException ex)
{
logger.Warn(ex, $"SteamLibrary/LoadInstalledGames: Exception: Steam Library {steamLibraryPath} doesn't contain 'steamapps' subfolder . This is most likely because the Steam Library doesn't have any games installed in it.");
}
catch (PathTooLongException ex)
{
logger.Warn(ex, $"SteamLibrary/LoadInstalledGames: Exception: Steam Library {steamLibraryPath} is too long for Windows to access. Please make the path shorter by moving the directory.");
}
catch (UnauthorizedAccessException ex)
{
logger.Warn(ex, $"SteamLibrary/LoadInstalledGames: Exception: We don't have permission to access Steam Library {steamLibraryPath}. Please check you are able to access {steamLibraryPath} in Windows Explorer.");
}
catch (Exception ex)
{
logger.Warn(ex, $"SteamLibrary/LoadInstalledGames: Exception: General exception while trying to scan Steam Library {steamLibraryPath} for games.");
}
}
logger.Info($"SteamLibrary/LoadInstalledGames: Found {_allSteamGames.Count} installed Steam games");
}

View File

@ -174,6 +174,7 @@ namespace DisplayMagician.GameLibraries
}
catch (Exception ex)
{
logger.Warn(ex, $"UplayLibrary/IsRunning: Exception while trying to get the Uplay Library processes with names: {_uplayProcessList.ToString()}");
return false;
}
@ -589,245 +590,253 @@ namespace DisplayMagician.GameLibraries
// Now we'll try to sort out the rest of the game data!
// We first look for the online executable information
if (root.start_game.online.executables.Count > 0)
if (root.GetType().GetProperty("start_game") != null)
{
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay game {gameName} has some online executables to process! ");
// First up we look at the online games, cause they're just better!
foreach (var executable in root.start_game.online.executables)
if (root.start_game.GetType().GetProperty("online") != null && root.start_game.online.executables.Count > 0)
{
string exePath = "";
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay game {gameName} has some online executables to process! ");
// Check if its a full path or a relative path
if (!String.IsNullOrEmpty(executable.path.relative))
// First up we look at the online games, cause they're just better!
foreach (var executable in root.start_game.online.executables)
{
if (executable.working_directory.register.StartsWith("HKEY_LOCAL_MACHINE"))
{
// This copes with relative files using a HKEY_LOCAL_MACHINE registry
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
if (this.GetInstallDirFromRegKey(regKeyText, out exePath))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses local machine registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].Value;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}
/*else if (executable.working_directory.register.StartsWith("HKEY_CURRENT_USER"))
{
// This copes with relative files using a HKEY_CURRENT_USER registry
string exePath = "";
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
if (this.GetInstallDirFromRegKey(executable.working_directory.register, out exePath))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses current user registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].Value;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}*/
else if (!String.IsNullOrEmpty(executable.working_directory.append))
// Check if its a full path or a relative path
if (!String.IsNullOrEmpty(executable.path.relative))
{
// This copes with relative files using an appended path
gameExePath = Path.Combine(executable.working_directory.append, executable.path.relative);
gameIconPath = Path.Combine(executable.working_directory.append, executable.icon_image);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses appended file path: {executable.working_directory.append} ");
gameId = productInfo.uplay_id.ToString();
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
if (executable.working_directory.register.StartsWith("HKEY_LOCAL_MACHINE"))
{
// This copes with relative files using a HKEY_LOCAL_MACHINE registry
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
if (this.GetInstallDirFromRegKey(regKeyText, out exePath))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses local machine registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].Value;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}
/*else if (executable.working_directory.register.StartsWith("HKEY_CURRENT_USER"))
{
// This copes with relative files using a HKEY_CURRENT_USER registry
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
if (this.GetInstallDirFromRegKey(executable.working_directory.register, out exePath))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses current user registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].Value;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}*/
else if (!String.IsNullOrEmpty(executable.working_directory.append))
{
// This copes with relative files using an appended path
gameExePath = Path.Combine(executable.working_directory.append, executable.path.relative);
gameIconPath = Path.Combine(executable.working_directory.append, executable.icon_image);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses appended file path: {executable.working_directory.append} ");
gameId = productInfo.uplay_id.ToString();
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}
else
{
// Problem!
logger.Error($"UplayLibrary/LoadInstalledGames: Found relative GameExePath {executable.path.relative} for Uplay game {gameName} but no registry key or appended file path! Skipping this game.");
continue;
}
}
else
{
// Problem!
logger.Error($"UplayLibrary/LoadInstalledGames: Found relative GameExePath {executable.path.relative} for Uplay game {gameName} but no registry key or appended file path! Skipping this game.");
// This should cope with full pathed files, but we have no examples to test! So log it
logger.Error($"UplayLibrary/LoadInstalledGames: Found non-relative GameExePath {executable.path} for Uplay game {gameName} but we've not seen it before so no idea how to handle it! Skipping this game.");
logger.Error($"UplayLibrary/LoadInstalledGames: executable.path for troubleshooting: {executable.path}");
continue;
}
}
else
{
// This should cope with full pathed files, but we have no examples to test! So log it
logger.Error($"UplayLibrary/LoadInstalledGames: Found non-relative GameExePath {executable.path} for Uplay game {gameName} but we've not seen it before so no idea how to handle it! Skipping this game.");
logger.Error($"UplayLibrary/LoadInstalledGames: executable.path for troubleshooting: {executable.path}");
continue;
}
// We should check the exe file exists, and if it doesn't then we need to do the next exe
if (!File.Exists(gameExePath))
{
logger.Error($"UplayLibrary/LoadInstalledGames: Couldn't find the GameExePath {gameExePath} for Uplay game {gameName} so skipping this exe, and trying the next one.");
continue;
}
// Now try to get the Uplay game icon
if (!String.IsNullOrEmpty(root.icon_image))
{
gameIconPath = Path.Combine(_uplayPath, "data", "games", root.icon_image);
// If the icon file isn't actually there, then use the game exe instead.
if (!File.Exists(gameIconPath))
// We should check the exe file exists, and if it doesn't then we need to do the next exe
if (!File.Exists(gameExePath))
{
gameIconPath = gameExePath;
logger.Error($"UplayLibrary/LoadInstalledGames: Couldn't find the GameExePath {gameExePath} for Uplay game {gameName} so skipping this exe, and trying the next one.");
continue;
}
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Found GameExePath {exePath} and Icon Path {gameIconPath} for Uplay game {gameName}.");
// We do a final check to make sure that we do have a GameName, and if not we use the shortcut
if (String.IsNullOrEmpty(gameName) && !String.IsNullOrEmpty(executable.shortcut_name))
{
gameName = executable.shortcut_name;
logger.Trace($"UplayLibrary/LoadInstalledGames: Game Name was still empty, so we're using the shortcut name as a last resort: {executable.shortcut_name} ");
// Now try to get the Uplay game icon
if (!String.IsNullOrEmpty(root.icon_image))
{
gameIconPath = Path.Combine(_uplayPath, "data", "games", root.icon_image);
// If the icon file isn't actually there, then use the game exe instead.
if (!File.Exists(gameIconPath))
{
gameIconPath = gameExePath;
}
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Found GameExePath {exePath} and Icon Path {gameIconPath} for Uplay game {gameName}.");
// We do a final check to make sure that we do have a GameName, and if not we use the shortcut
if (String.IsNullOrEmpty(gameName) && !String.IsNullOrEmpty(executable.shortcut_name))
{
gameName = executable.shortcut_name;
logger.Trace($"UplayLibrary/LoadInstalledGames: Game Name was still empty, so we're using the shortcut name as a last resort: {executable.shortcut_name} ");
}
// Now we need to save the game name, cause if we're here then we're good enough to save
// Then we have the gameID, the thumbimage, the icon, the name, the exe path
// And we add the Game to the list of games we have!
_allGames.Add(new UplayGame(gameId, gameName, gameExePath, gameIconPath));
logger.Trace($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {productInfo.uplay_id}, name {gameName}, game exe {gameExePath} and icon path {gameIconPath}");
break;
}
// Now we need to save the game name, cause if we're here then we're good enough to save
// Then we have the gameID, the thumbimage, the icon, the name, the exe path
// And we add the Game to the list of games we have!
_allGames.Add(new UplayGame(gameId, gameName, gameExePath, gameIconPath));
logger.Trace($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {productInfo.uplay_id}, name {gameName}, game exe {gameExePath} and icon path {gameIconPath}");
break;
}
}
// This is the offline exes
else if (root.start_game.offline.executables.Count > 0)
{
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay game {gameName} has some offline executables to process! ");
// we look at the offline games, cause there weren't any online ones
foreach (var executable in root.start_game.offline.executables)
// This is the offline exes
else if (root.start_game.GetType().GetProperty("offline") != null && root.start_game.offline.executables.Count > 0)
{
string exePath = "";
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay game {gameName} has some offline executables to process! ");
// Check if its a full path or a relative path
if (!String.IsNullOrEmpty(executable.path.relative))
// we look at the offline games, cause there weren't any online ones
foreach (var executable in root.start_game.offline.executables)
{
if (executable.working_directory.register.StartsWith("HKEY_LOCAL_MACHINE"))
string exePath = "";
// Check if its a full path or a relative path
if (!String.IsNullOrEmpty(executable.path.relative))
{
// This copes with relative files using a HKEY_LOCAL_MACHINE registry
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
if (this.GetInstallDirFromRegKey(regKeyText, out exePath))
if (executable.working_directory.register.StartsWith("HKEY_LOCAL_MACHINE"))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses local machine registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].Value;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
// This copes with relative files using a HKEY_LOCAL_MACHINE registry
}
/*else if (executable.working_directory.register.StartsWith("HKEY_CURRENT_USER"))
{
// This copes with relative files using a HKEY_CURRENT_USER registry
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
if (this.GetInstallDirFromRegKey(regKeyText, out exePath))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses local machine registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].Value;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
if (this.GetInstallDirFromRegKey(executable.working_directory.register, out exePath))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses current user registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
/*else if (executable.working_directory.register.StartsWith("HKEY_CURRENT_USER"))
{
gameId = mc[0].Groups[1].Value;
// This copes with relative files using a HKEY_CURRENT_USER registry
string regKeyText = executable.working_directory.register;
regKeyText = regKeyText.Replace(@"\InstallDir", "");
regKeyText = regKeyText.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyText}");
if (this.GetInstallDirFromRegKey(executable.working_directory.register, out exePath))
{
gameExePath = Path.Combine(exePath, executable.path.relative);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses current user registry key: {executable.working_directory.register} ");
}
// Get the GameID from the reg key
string pattern = @"Installs\\(\d+)\\InstallDir";
MatchCollection mc = Regex.Matches(executable.working_directory.register, pattern);
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].Value;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}*/
else if (!String.IsNullOrEmpty(executable.working_directory.append))
{
// This copes with relative files using an appended path
gameExePath = Path.Combine(executable.working_directory.append, executable.path.relative);
gameIconPath = Path.Combine(executable.working_directory.append, executable.icon_image);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses appended file path: {executable.working_directory.append} ");
gameId = productInfo.uplay_id.ToString();
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}
else
{
// Problem!
logger.Error($"UplayLibrary/LoadInstalledGames: Found relative GameExePath {executable.path.relative} for Uplay game {gameName} but no registry key or appended file path! Skipping this game.");
continue;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}*/
else if (!String.IsNullOrEmpty(executable.working_directory.append))
{
// This copes with relative files using an appended path
gameExePath = Path.Combine(executable.working_directory.append, executable.path.relative);
gameIconPath = Path.Combine(executable.working_directory.append, executable.icon_image);
logger.Trace($"UplayLibrary/LoadInstalledGames: Relative executable uses appended file path: {executable.working_directory.append} ");
gameId = productInfo.uplay_id.ToString();
logger.Trace($"UplayLibrary/LoadInstalledGames: Got uplay Game ID: {gameId} ");
}
else
{
// Problem!
logger.Error($"UplayLibrary/LoadInstalledGames: Found relative GameExePath {executable.path.relative} for Uplay game {gameName} but no registry key or appended file path! Skipping this game.");
// This should cope with full pathed files, but we have no examples to test! So log it
logger.Error($"UplayLibrary/LoadInstalledGames: Found non-relative GameExePath {executable.path} for Uplay game {gameName} but we've not seen it before so no idea how to handle it! Skipping this game.");
logger.Error($"UplayLibrary/LoadInstalledGames: executable.path for troubleshooting: {executable.path}");
continue;
}
}
else
{
// This should cope with full pathed files, but we have no examples to test! So log it
logger.Error($"UplayLibrary/LoadInstalledGames: Found non-relative GameExePath {executable.path} for Uplay game {gameName} but we've not seen it before so no idea how to handle it! Skipping this game.");
logger.Error($"UplayLibrary/LoadInstalledGames: executable.path for troubleshooting: {executable.path}");
continue;
}
// We should check the exe file exists, and if it doesn't then we need to do the next exe
if (!File.Exists(gameExePath))
{
logger.Error($"UplayLibrary/LoadInstalledGames: Couldn't find the GameExePath {gameExePath} for Uplay game {gameName} so skipping this exe, and trying the next one.");
continue;
}
// Now try to get the Uplay game icon
if (!String.IsNullOrEmpty(root.icon_image))
{
gameIconPath = Path.Combine(_uplayPath, "data", "games", root.icon_image);
// If the icon file isn't actually there, then use the game exe instead.
if (!File.Exists(gameIconPath))
// We should check the exe file exists, and if it doesn't then we need to do the next exe
if (!File.Exists(gameExePath))
{
gameIconPath = gameExePath;
logger.Error($"UplayLibrary/LoadInstalledGames: Couldn't find the GameExePath {gameExePath} for Uplay game {gameName} so skipping this exe, and trying the next one.");
continue;
}
// Now try to get the Uplay game icon
if (!String.IsNullOrEmpty(root.icon_image))
{
gameIconPath = Path.Combine(_uplayPath, "data", "games", root.icon_image);
// If the icon file isn't actually there, then use the game exe instead.
if (!File.Exists(gameIconPath))
{
gameIconPath = gameExePath;
}
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Found GameExePath {exePath} and Icon Path {gameIconPath} for Uplay game {gameName}.");
// We do a final check to make sure that we do have a GameName, and if not we use the shortcut
if (String.IsNullOrEmpty(gameName) && !String.IsNullOrEmpty(executable.shortcut_name))
{
gameName = executable.shortcut_name;
logger.Trace($"UplayLibrary/LoadInstalledGames: Game Name was still empty, so we're using the shortcut name as a last resort: {executable.shortcut_name} ");
}
// Now we need to save the game name, cause if we're here then we're good enough to save
// Then we have the gameID, the thumbimage, the icon, the name, the exe path
// And we add the Game to the list of games we have!
_allGames.Add(new UplayGame(gameId, gameName, gameExePath, gameIconPath));
logger.Trace($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {productInfo.uplay_id}, name {gameName}, game exe {gameExePath} and icon path {gameIconPath}");
break;
}
logger.Trace($"UplayLibrary/LoadInstalledGames: Found GameExePath {exePath} and Icon Path {gameIconPath} for Uplay game {gameName}.");
// We do a final check to make sure that we do have a GameName, and if not we use the shortcut
if (String.IsNullOrEmpty(gameName) && !String.IsNullOrEmpty(executable.shortcut_name))
{
gameName = executable.shortcut_name;
logger.Trace($"UplayLibrary/LoadInstalledGames: Game Name was still empty, so we're using the shortcut name as a last resort: {executable.shortcut_name} ");
}
// Now we need to save the game name, cause if we're here then we're good enough to save
// Then we have the gameID, the thumbimage, the icon, the name, the exe path
// And we add the Game to the list of games we have!
_allGames.Add(new UplayGame(gameId, gameName, gameExePath, gameIconPath));
logger.Trace($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {productInfo.uplay_id}, name {gameName}, game exe {gameExePath} and icon path {gameIconPath}");
break;
}
}
else
{
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Entry {gameName} doesn't have any online or offline executable associated with it! We have to skip adding this game.");
continue;
}
}
else
{
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Entry {gameName} doesn't have any executables associated with it! We have to skip adding this game.");
@ -840,12 +849,12 @@ namespace DisplayMagician.GameLibraries
// If we get an error processing the game YAML, lets try and skip this game and try the next one. It might work!
if (item.GameInfo.StartsWith("root:"))
{
logger.Warn($"UplayLibrary/LoadInstalledGames: Problem deserialising the YAML embedded in the Uplay configuration file {uplayConfigFilePath}. Cannot process this Uplay game! (Uplay ID:{item.UplayId}): {item.GameInfo}");
logger.Warn(ex, $"UplayLibrary/LoadInstalledGames: Problem deserialising the YAML embedded in the Uplay configuration file {uplayConfigFilePath}. Cannot process this Uplay game! (Uplay ID:{item.UplayId}): {item.GameInfo}");
continue;
}
else
{
logger.Trace($"UplayLibrary/LoadInstalledGames: This Uplay entry (Uplay ID:{item.UplayId}) in the Uplay configuration file {uplayConfigFilePath} is not a YAML config so skipping: {item.GameInfo}");
logger.Trace(ex, $"UplayLibrary/LoadInstalledGames: This Uplay entry (Uplay ID:{item.UplayId}) in the Uplay configuration file {uplayConfigFilePath} is not a YAML config so skipping: {item.GameInfo}");
}
}
@ -855,7 +864,7 @@ namespace DisplayMagician.GameLibraries
catch (Exception ex)
{
// We can't do anything if we hit here.
logger.Error($"UplayLibrary/LoadInstalledGames: Problem deserialising the protobuf Uplay configuration file {uplayConfigFilePath}. Cannot process any Uplay games!");
logger.Error(ex, $"UplayLibrary/LoadInstalledGames: Problem deserialising the protobuf Uplay configuration file {uplayConfigFilePath}. Cannot process any Uplay games!");
return false;
}
}

View File

@ -175,17 +175,17 @@ namespace DisplayMagician.Processes
}
catch (ArgumentException ex)
{
logger.Trace($"ProcessUtils/ProcessExited: {process.Id} is not running, and the process ID has expired. This means the process has finished!");
logger.Trace(ex, $"ProcessUtils/ProcessExited: {process.Id} is not running, and the process ID has expired. This means the process has finished!");
return true;
}
catch (InvalidOperationException ex)
{
logger.Warn($"ProcessUtils/ProcessExited: {process.Id} was not started by this process object. This likely means the process has finished!");
logger.Warn(ex, $"ProcessUtils/ProcessExited: {process.Id} was not started by this process object. This likely means the process has finished!");
return true;
}
catch (Exception ex)
{
logger.Trace($"ProcessUtils/ProcessExited: Exception when checking if {process.Id} is still running, so assuming the process has finished!");
logger.Trace(ex, $"ProcessUtils/ProcessExited: Exception when checking if {process.Id} is still running, so assuming the process has finished!");
return true;
}
}
@ -530,7 +530,8 @@ namespace DisplayMagician.Processes
UseShellExecute = true,
Verb = "Runas",
CreateNoWindow = false,
RedirectStandardOutput = false
RedirectStandardOutput = false,
WorkingDirectory = Path.GetDirectoryName(executable)
};
}
else
@ -539,7 +540,8 @@ namespace DisplayMagician.Processes
{
UseShellExecute = false,
CreateNoWindow = false,
RedirectStandardOutput = false
RedirectStandardOutput = false,
WorkingDirectory = Path.GetDirectoryName(executable)
};
}
@ -552,7 +554,8 @@ namespace DisplayMagician.Processes
UseShellExecute = true,
Verb = "Open",
CreateNoWindow = false,
RedirectStandardOutput = false
RedirectStandardOutput = false,
WorkingDirectory = Path.GetDirectoryName(executable)
};
}
@ -675,6 +678,33 @@ namespace DisplayMagician.Processes
}
}
public static ProcessPriority TranslateNameToPriority(string processPriorityName)
{
ProcessPriority wantedPriority = ProcessPriority.Normal;
switch (processPriorityName.ToLowerInvariant())
{
case "high":
wantedPriority = ProcessPriority.High;
break;
case "abovenormal":
wantedPriority = ProcessPriority.AboveNormal;
break;
case "normal":
wantedPriority = ProcessPriority.Normal;
break;
case "belownormal":
wantedPriority = ProcessPriority.BelowNormal;
break;
case "idle":
wantedPriority = ProcessPriority.Idle;
break;
default:
wantedPriority = ProcessPriority.Normal;
break;
}
return wantedPriority;
}
public static ProcessPriorityClass TranslatePriorityToClass(ProcessPriority processPriorityClass)
{
ProcessPriorityClass wantedPriorityClass = ProcessPriorityClass.Normal;
@ -728,7 +758,7 @@ namespace DisplayMagician.Processes
}
return wantedPriorityClass;
}
}
}

View File

@ -12,6 +12,7 @@ using DisplayMagicianShared;
using DisplayMagician.UIForms;
using DisplayMagician.GameLibraries;
using System.Text.RegularExpressions;
using System.Net.NetworkInformation;
using System.Drawing;
using System.Runtime.Serialization;
using NLog.Config;
@ -21,6 +22,7 @@ using Newtonsoft.Json;
using System.Threading;
using Microsoft.Win32;
using DisplayMagician.Processes;
using NETWORKLIST;
namespace DisplayMagician {
@ -178,19 +180,46 @@ namespace DisplayMagician {
// NOTE: This had to be moved up from the later state
// Copy the old Settings file to the new v2 name
bool upgradedSettingsFile = false;
string oldSettingsFile = Path.Combine(AppDataPath, "Settings_1.0.json");
string newSettingsFile = Path.Combine(AppDataPath, "Settings_2.0.json");
string targetSettingsFile = ProgramSettings.programSettingsStorageJsonFileName;
try
{
if (File.Exists(oldSettingsFile) && !File.Exists(newSettingsFile))
{
if (!File.Exists(targetSettingsFile))
{
File.Copy(oldSettingsFile, newSettingsFile, true);
upgradedSettingsFile = true;
string oldv1SettingsFile = Path.Combine(AppDataPath, "Settings_1.0.json");
string oldv2SettingsFile = Path.Combine(AppDataPath, "Settings_2.0.json");
string oldv23SettingsFile = Path.Combine(AppDataPath, "Settings_2.3.json");
if (File.Exists(oldv23SettingsFile))
{
File.Copy(oldv23SettingsFile, targetSettingsFile, true);
upgradedSettingsFile = true;
}
else if (File.Exists(oldv2SettingsFile))
{
File.Copy(oldv2SettingsFile, targetSettingsFile, true);
upgradedSettingsFile = true;
}
else if (File.Exists(oldv1SettingsFile))
{
File.Copy(oldv1SettingsFile, targetSettingsFile, true);
upgradedSettingsFile = true;
}
}
// Now we rename all the currently listed Settings files as they aren't needed any longer.
// NOTE: This is outside the File Exidsts above to fix all the partially renamed files performed in previous upgrades
if (RenameOldFileVersions(AppDataPath, "Settings_*.json", targetSettingsFile))
{
logger.Trace($"Program/Main: Old DisplayMagician Shortcut files were successfully renamed");
}
else
{
logger.Error($"Program/Main: Error while renaming old Shortcut files.");
}
}
catch (Exception ex)
{
logger.Error(ex, $"Program/Main: Exception upgrading v1 settings file {oldSettingsFile} to v2 settings file {ProgramSettings.programSettingsStorageJsonFileName}.");
logger.Error(ex, $"Program/Main: Exception upgrading old settings file to v2.4 settings file {ProgramSettings.programSettingsStorageJsonFileName}.");
}
// Load the program settings
@ -232,7 +261,8 @@ namespace DisplayMagician {
var logfile = new NLog.Targets.FileTarget("logfile")
{
FileName = AppLogFilename,
DeleteOldFileOnStartup = true
DeleteOldFileOnStartup = true,
Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}|${onexception:EXCEPTION OCCURRED \\:${exception::format=toString,Properties,Data}"
};
// Create a logging rule to use the log file target
@ -245,7 +275,7 @@ namespace DisplayMagician {
// Create the log console target
var logconsole = new NLog.Targets.ColoredConsoleTarget("logconsole")
{
Layout = "${date:format=HH\\:MM\\:ss} ${logger} ${message}",
Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}|${onexception:EXCEPTION OCCURRED \\:${exception::format=toString,Properties,Data}"
};
// Create a logging rule to use the log console target
@ -272,45 +302,65 @@ namespace DisplayMagician {
try
{
Directory.CreateDirectory(AppIconPath);
logger.Trace($"Program/Main: Created the Application Icon Folder {AppIconPath}");
}
catch (Exception ex)
{
logger.Error(ex, $"Program/Main: exception: Cannot create the Application Icon Folder {AppLogPath}");
logger.Error(ex, $"Program/Main: exception: Cannot create the Application Icon Folder {AppIconPath}");
}
}
else
{
logger.Trace($"Program/Main: Application Icon Folder {AppIconPath} already exists so skipping creating it");
}
if (!Directory.Exists(AppProfilePath))
{
try
{
Directory.CreateDirectory(AppProfilePath);
logger.Trace($"Program/Main: Created the Application Profile Folder {AppProfilePath}");
}
catch (Exception ex)
{
logger.Error(ex, $"Program/Main: exception: Cannot create the Application Profile Folder {AppProfilePath}");
}
}
else
{
logger.Trace($"Program/Main: Application Profile Folder {AppProfilePath} already exists so skipping creating it");
}
if (!Directory.Exists(AppShortcutPath))
{
try
{
Directory.CreateDirectory(AppShortcutPath);
logger.Trace($"Program/Main: Created the Application Shortcut Folder {AppShortcutPath}");
}
catch (Exception ex)
{
logger.Error(ex, $"Program/Main: exception: Cannot create the Application Shortcut Folder {AppShortcutPath}");
}
}
else
{
logger.Trace($"Program/Main: Application Shortcut Folder {AppShortcutPath} already exists so skipping creating it");
}
if (!Directory.Exists(AppWallpaperPath))
{
try
{
Directory.CreateDirectory(AppWallpaperPath);
logger.Trace($"Program/Main: Created the Application Wallpaper Folder {AppWallpaperPath}");
}
catch (Exception ex)
{
logger.Error(ex, $"Program/Main: exception: Cannot create the Application Wallpaper Folder {AppWallpaperPath}");
}
}
else
{
logger.Trace($"Program/Main: Application Wallpaper Folder {AppWallpaperPath} already exists so skipping creating it");
}
// Write the Application Name
Console.WriteLine($"{Application.ProductName} v{Application.ProductVersion}");
@ -321,7 +371,7 @@ namespace DisplayMagician {
Console.WriteLine("=");
Console.WriteLine($"Copyright © Terry MacDonald 2020-{DateTime.Today.Year}");
Console.WriteLine(@"Derived from Helios Display Management - Copyright © Soroush Falahati 2017-2020");
logger.Trace($"Program/Main: Setting visual styles and rendering mode");
//Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
@ -331,91 +381,98 @@ namespace DisplayMagician {
// Check if it's an upgrade from DisplayMagician v2.x to v2.2
// and if it is then copy the old configs to the new filenames and
// explain to the user what they need to do.
string dp23 = Path.Combine(AppProfilePath, "DisplayProfiles_2.3.json");
string dp22 = Path.Combine(AppProfilePath, "DisplayProfiles_2.2.json");
string dp21 = Path.Combine(AppProfilePath, "DisplayProfiles_2.1.json");
string dp20 = Path.Combine(AppProfilePath, "DisplayProfiles_2.0.json");
string dp10 = Path.Combine(AppProfilePath, "DisplayProfiles_1.0.json");
logger.Trace($"Program/Main: Attempting to upgrade earlier DisplayMagician Display Profile files if required");
// This is the latest displayprofile config file
string targetdp = dp23;
string targetdp = ProfileRepository.ProfileStorageFileName;
if (File.Exists(dp22) && !File.Exists(Path.Combine(AppProfilePath, targetdp)))
try
{
logger.Info($"Program/Main: This is an upgrade from DisplayMagician v2.1 to DisplayMagician v2.3, so informing the user.");
// Warn the user about the fact we need them to recreate their Display Profiles again!
StartMessageForm myMessageWindow = new StartMessageForm();
myMessageWindow.MessageMode = "rtf";
myMessageWindow.URL = "https://displaymagician.littlebitbig.com/messages/DisplayMagicianRecreateProfiles.rtf";
myMessageWindow.HeadingText = "You need to recreate your Display Profiles";
myMessageWindow.ButtonText = "&Close";
myMessageWindow.ShowDialog();
}
else if (File.Exists(dp21) && !File.Exists(Path.Combine(AppProfilePath, targetdp)))
{
logger.Info($"Program/Main: This is an upgrade from DisplayMagician v2.1 to DisplayMagician v2.3, so informing the user.");
// Warn the user about the fact we need them to recreate their Display Profiles again!
StartMessageForm myMessageWindow = new StartMessageForm();
myMessageWindow.MessageMode = "rtf";
myMessageWindow.URL = "https://displaymagician.littlebitbig.com/messages/DisplayMagicianRecreateProfiles.rtf";
myMessageWindow.HeadingText = "You need to recreate your Display Profiles";
myMessageWindow.ButtonText = "&Close";
myMessageWindow.ShowDialog();
}
else if (File.Exists(dp20) && !File.Exists(Path.Combine(AppProfilePath, targetdp)))
{
logger.Info($"Program/Main: This is an upgrade from DisplayMagician v2.0 to DisplayMagician v2.3, so informing the user.");
// Warn the user about the fact we need them to recreate their Display Profiles again!
StartMessageForm myMessageWindow = new StartMessageForm();
myMessageWindow.MessageMode = "rtf";
myMessageWindow.URL = "https://displaymagician.littlebitbig.com/messages/DisplayMagicianRecreateProfiles.rtf";
myMessageWindow.HeadingText = "You need to recreate your Display Profiles";
myMessageWindow.ButtonText = "&Close";
myMessageWindow.ShowDialog();
}
// Check if it's an upgrade from DisplayMagician v1 to v2
// and if it is then copy the old configs to the new filenames and
// explain to the user what they need to do.
// e.g. DisplayProfiles_1.0.json exists, but DisplayProfiles_2.2.json doesn't
else if (File.Exists(Path.Combine(AppProfilePath, "DisplayProfiles_1.0.json")) && !File.Exists(Path.Combine(AppProfilePath, targetdp)))
{
logger.Info($"Program/Main: This is an upgrade from DisplayMagician v1.0 to DisplayMagician v2.2, so performing some upgrade steps.");
// Note whether we copied the old Settings file to the new v2 name earlier (before the logging was enabled)
if (upgradedSettingsFile)
// Only run this code if there isn't a current Display Profile file.
// If this happens then it is an error!
if (!File.Exists(targetdp))
{
logger.Info($"Program/Main: Upgraded v1.0 settings file {oldSettingsFile} to v2.2 settings file {newSettingsFile} earlier in loading process (before logging service was available).");
}
logger.Info($"Program/Main: This is an upgrade from an earlier DisplayMagician Display Profile format to the current DisplayMagician Display Profile format, so it requires the user manual recreate the display profiles.");
// Copy the old Game Shortcuts file to the new v2 name
string oldShortcutsFile = Path.Combine(AppShortcutPath, "Shortcuts_1.0.json");
string newShortcutsFile = Path.Combine(AppShortcutPath, "Shortcuts_2.2.json");
try
{
if (File.Exists(oldShortcutsFile) && !File.Exists(newShortcutsFile))
{
logger.Info($"Program/Main: Upgrading v1 shortcut file {oldShortcutsFile} to v2 shortcut file {newShortcutsFile }.");
File.Copy(oldShortcutsFile, newShortcutsFile);
}
}
catch(Exception ex)
// Warn the user about the fact we need them to recreate their Display Profiles again!
StartMessageForm myMessageWindow = new StartMessageForm();
myMessageWindow.MessageMode = "rtf";
myMessageWindow.URL = "https://displaymagician.littlebitbig.com/messages/DisplayMagicianRecreateProfiles.rtf";
myMessageWindow.HeadingText = "You need to recreate your Display Profiles";
myMessageWindow.ButtonText = "&Close";
myMessageWindow.ShowDialog();
}
else
{
logger.Error(ex, $"Program/Main: Exception upgrading v1.0 shortcut file {oldShortcutsFile} to v2.2 shortcut file {ShortcutRepository.ShortcutStorageFileName}.");
logger.Trace($"Program/Main: DisplayMagician Display Profile files do not require upgrading so skipping");
}
// Warn the user about the fact we need a new DisplayProfiles_2.0.json
StartMessageForm myMessageWindow = new StartMessageForm();
myMessageWindow.MessageMode = "rtf";
myMessageWindow.URL = "https://displaymagician.littlebitbig.com/messages/DisplayMagicianRecreateProfiles.rtf";
myMessageWindow.HeadingText = "You need to recreate your Display Profiles";
myMessageWindow.ButtonText = "&Close";
myMessageWindow.ShowDialog();
// Now we rename all the currently listed Display Profile files as they aren't needed any longer.
// NOTE: This is outside the File Exidsts above to fix all the partially renamed files performed in previous upgrades
if (RenameOldFileVersions(AppProfilePath, "DisplayProfiles_*.json", targetdp))
{
logger.Trace($"Program/Main: Old DisplayMagician Display Profile files were successfully renamed");
}
else
{
logger.Error($"Program/Main: Error while renaming old Display Profiles files.");
}
}
logger.Debug($"Program/Main: Setting up commandline processing configuration");
catch (Exception ex)
{
logger.Error(ex, $"Program/Main: Exception upgrading old Display Profile files to v2.2 shortcut file {targetdp}.");
}
// Note whether we copied the old Settings file to the new v2 name earlier (before the logging was enabled)
if (upgradedSettingsFile)
{
logger.Info($"Program/Main: Upgraded old settings file to settings file {targetSettingsFile} earlier in loading process (before logging service was available).");
}
// Also upgrade the Shortcuts file if it's needed
string targetShortcutsFile = ShortcutRepository.ShortcutStorageFileName;
try
{
if (!File.Exists(targetShortcutsFile))
{
string oldv1ShortcutsFile = Path.Combine(AppShortcutPath, "Shortcuts_1.0.json");
string oldv2ShortcutsFile = Path.Combine(AppShortcutPath, "Shortcuts_2.0.json");
if (File.Exists(oldv2ShortcutsFile))
{
logger.Info($"Program/Main: Upgrading v1 shortcut file {oldv2ShortcutsFile} to v2.2 shortcut file {targetShortcutsFile}.");
File.Copy(oldv2ShortcutsFile, targetShortcutsFile);
}
else if (File.Exists(oldv1ShortcutsFile))
{
logger.Info($"Program/Main: Upgrading v1 shortcut file {oldv1ShortcutsFile} to v2.2 shortcut file {targetShortcutsFile}.");
File.Copy(oldv1ShortcutsFile, targetShortcutsFile);
}
}
else
{
logger.Trace($"Program/Main: DisplayMagician Shortcut files do not require upgrading so skipping");
}
// Now we rename all the currently listed Display Profile files as they aren't needed any longer.
// NOTE: This is outside the File Exists above to fix all the partially renamed files performed in previous upgrades
if (RenameOldFileVersions(AppShortcutPath, "Shortcuts_*.json", targetShortcutsFile))
{
logger.Trace($"Program/Main: Old DisplayMagician Shortcut files were successfully renamed");
}
else
{
logger.Error($"Program/Main: Error while renaming old Shortcut files.");
}
}
catch (Exception ex)
{
logger.Error(ex, $"Program/Main: Exception upgrading old shortcut file to latest shortcut file {targetShortcutsFile}.");
}
logger.Trace($"Program/Main: Setting up commandline processing configuration");
var app = new CommandLineApplication
{
AllowArgumentSeparator = true,
@ -439,12 +496,16 @@ namespace DisplayMagician {
CommandOption trace = app.Option("--trace", "Generate a DisplayMagician.log trace-level log file", CommandOptionType.NoValue);
CommandOption forcedVideoLibrary = app.Option("--force-video-library", "Bypass the normal video detection logic to force a particular video library (AMD, NVIDIA, Windows)", CommandOptionType.SingleValue);
logger.Trace($"Program/Main: Preparing the RunShortcut command...");
// This is the RunShortcut command
app.Command(DisplayMagicianStartupAction.RunShortcut.ToString(), (runShortcutCmd) =>
{
logger.Trace($"Program/Main: Processing the {runShortcutCmd} command...");
// Try to load all the games in parallel to this process
//Task.Run(() => LoadGamesInBackground());
// Set the --trace or --debug options if supplied
if (trace.HasValue())
{
@ -511,7 +572,7 @@ namespace DisplayMagician {
GameLibrary.LoadGamesInBackground();
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
ERRORLEVEL errLevel = RunShortcut(argumentShortcut.Value);
@ -520,9 +581,13 @@ namespace DisplayMagician {
});
});
logger.Trace($"Program/Main: Preparing the ChangeProfile command...");
// This is the ChangeProfile command
app.Command(DisplayMagicianStartupAction.ChangeProfile.ToString(), (runProfileCmd) =>
{
logger.Trace($"Program/Main: Processing the {runProfileCmd} command...");
// Set the --trace or --debug options if supplied
if (trace.HasValue())
{
@ -587,7 +652,7 @@ namespace DisplayMagician {
AppMainForm.Load += MainForm_LoadCompleted;
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
try
@ -605,9 +670,13 @@ namespace DisplayMagician {
});
});
logger.Trace($"Program/Main: Preparing the CreateProfile command...");
// This is the CreateProfile command
app.Command(DisplayMagicianStartupAction.CreateProfile.ToString(), (createProfileCmd) =>
{
logger.Trace($"Program/Main: Processing the {createProfileCmd} command...");
// Set the --trace or --debug options if supplied
if (trace.HasValue())
{
@ -670,10 +739,13 @@ namespace DisplayMagician {
});
logger.Trace($"Program/Main: Preparing the CurrentProfile command...");
// This is the CurrentProfile command
// This will output the current display profile if one matches, or 'Unknown'
app.Command(DisplayMagicianStartupAction.CurrentProfile.ToString(), (currentProfileCmd) =>
{
logger.Trace($"Program/Main: Processing the {currentProfileCmd} command...");
// Set the --trace or --debug options if supplied
if (trace.HasValue())
{
@ -734,8 +806,12 @@ namespace DisplayMagician {
});
});
logger.Trace($"Program/Main: Preparing the default command...");
app.OnExecute(() =>
{
logger.Trace($"Program/Main: Starting the app normally as there was no command supplied...");
// Set the --trace or --debug options if supplied
if (trace.HasValue())
{
@ -804,22 +880,23 @@ namespace DisplayMagician {
}
}
logger.Info("Program/Main: Starting Normally...");
// Try to load all the games in parallel to this process
//Task.Run(() => LoadGamesInBackground());
logger.Debug($"Program/Main: Try to load all the Games in the background to avoid locking the UI");
GameLibrary.LoadGamesInBackground();
// Set up the AppMainForm variable that we need to use later
AppMainForm = new MainForm();
AppMainForm.Load += MainForm_LoadCompletedAndOpenApp;
// Try to load all the games in parallel to this process
//Task.Run(() => LoadGamesInBackground());
logger.Debug($"Program/Main: Try to load all the Games in the background to avoid locking the UI");
GameLibrary.LoadGamesInBackground();
ERRORLEVEL errLevel = StartUpApplication();
DeRegisterDisplayMagicianWithWindows();
return (int)errLevel;
});
logger.Trace($"Program/Main: Showing the splashscreen if requested");
if (AppProgramSettings.ShowSplashScreen)
{
//Show Splash Form
@ -832,14 +909,14 @@ namespace DisplayMagician {
try
{
logger.Debug($"Starting commandline processing");
logger.Debug($"Invoking commandline processing");
// This begins the actual execution of the application
app.Execute(args);
}
catch (CommandParsingException ex)
{
logger.Error(ex, $"Program/Main exception parsing the Commands passed to the program");
Console.WriteLine("Didn't recognise the supplied commandline options: {0}", ex.Message);
logger.Error(ex, $"Program/Main exception parsing the Commands passed to the program: ");
Console.WriteLine($"Didn't recognise the supplied commandline options: - {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
return (int)ERRORLEVEL.ERROR_UNKNOWN_COMMAND;
}
catch (Exception ex)
@ -848,22 +925,26 @@ namespace DisplayMagician {
// You'll always want to catch this exception, otherwise it will generate a messy and confusing error for the end user.
// the message will usually be something like:
// "Unrecognized command or argument '<invalid-command>'"
logger.Error(ex, $"Program/Main general exception during app.Execute(args)");
logger.Error(ex, $"Program/Main general exception during app.Execute(args): ");
Console.WriteLine($"Program/Main exception: Unable to execute application - {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
}
logger.Debug($"Beginning to shutdown");
logger.Debug($"Beginning to shutdown as the app command has finished executing.");
// Close the splash screen if it's still open (happens with some errors)
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
{
logger.Trace($"Closing the SplashScreen as it may still be open");
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
}
logger.Debug($"Program/Main: Clearing all previous windows toast notifications as they aren't needed any longer");
logger.Trace($"Program/Main: Clearing all previous windows toast notifications as they aren't needed any longer");
// Remove all the notifications we have set as they don't matter now!
ToastNotificationManagerCompat.History.Clear();
// Shutdown NLog
logger.Debug($"Program/Main: Stopping logging processes");
logger.Trace($"Program/Main: Stopping logging processes");
NLog.LogManager.Shutdown();
// Dispose of the CancellationTokenSource
@ -881,7 +962,7 @@ namespace DisplayMagician {
try
{
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
// Run the program with directly showing CreateProfile form
@ -968,13 +1049,13 @@ namespace DisplayMagician {
private static void MainForm_LoadCompleted(object sender, EventArgs e)
{
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
}
private static void MainForm_LoadCompletedAndOpenApp(object sender, EventArgs e)
{
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
AppMainForm.TopMost = true;
AppMainForm.Activate();
@ -990,7 +1071,7 @@ namespace DisplayMagician {
ShortcutItem shortcutToRun = null;
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
// Match the ShortcutName to the actual shortcut listed in the shortcut library
@ -1022,7 +1103,7 @@ namespace DisplayMagician {
logger.Trace($"Program/CurrentProfile: Finding the current profile in use");
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
// Lookup the profile
@ -1057,7 +1138,7 @@ namespace DisplayMagician {
ERRORLEVEL errLevel = ERRORLEVEL.OK;
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
if (AppProgramSettings.ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
@ -1094,6 +1175,67 @@ namespace DisplayMagician {
return true;
}
public static bool RenameOldFileVersions(string path, string searchPattern, string skipFilename)
{
try
{
if (String.IsNullOrWhiteSpace(path))
{
logger.Error($"Program/RenameOldFileVersions: We were passed an empty path, so returning an empty list of matching files.");
return false;
}
string[] filesToRename;
if (String.IsNullOrWhiteSpace(searchPattern))
{
filesToRename = Directory.GetFiles(path);
}
else
{
filesToRename = Directory.GetFiles(path, searchPattern);
}
if (filesToRename.Length > 0)
{
logger.Trace($"Program/RenameOldFileVersions: Found {filesToRename.Length} files matching the '{searchPattern}' pattern in {path}");
foreach (var filename in filesToRename)
{
// If this is the file we should skip, then let's skip it
if (filename.Equals(skipFilename, StringComparison.InvariantCultureIgnoreCase))
{
logger.Trace($"Program/RenameOldFileVersions: Skipping renaming {filename} to {filename}.old as we want to keep the {skipFilename} file.");
continue;
}
try
{
logger.Trace($"Program/RenameOldFileVersions: Attempting to rename {filename} to {filename}.old");
File.Move(filename, $"{filename}.old");
}
catch (Exception ex2)
{
logger.Error(ex2,$"Program/RenameOldFileVersions: Exception while trying to rename {filename} to {filename}.old. Skipping this rename.");
}
}
}
else
{
logger.Trace($"Program/RenameOldFileVersions: We tried looking for all files that matched the pattern '{searchPattern}' in path {path} and couldn't find any. skipping processing.");
}
return true;
}
catch (Exception ex)
{
logger.Error(ex, $"Program/RenameOldFileVersions: Exception while trying to RenameOldFileVersions. Unable to rename the files.");
return false;
}
}
//public async static Task<RunShortcutResult> RunShortcutTask(ShortcutItem shortcutToUse, NotifyIcon notifyIcon = null)
public static RunShortcutResult RunShortcutTask(ShortcutItem shortcutToUse)
{
@ -1146,7 +1288,7 @@ namespace DisplayMagician {
}
catch (OperationCanceledException ex)
{
logger.Trace($"Program/RunShortcutTask: User cancelled the running the shortcut {shortcutToUse.Name}.");
logger.Trace(ex, $"Program/RunShortcutTask: User cancelled the running the shortcut {shortcutToUse.Name}.");
}
catch (Exception ex)
{
@ -1198,7 +1340,7 @@ namespace DisplayMagician {
}
catch (OperationCanceledException ex)
{
logger.Trace($"Program/ApplyProfileTask: User cancelled the ApplyProfile {profile.Name}.");
logger.Trace(ex, $"Program/ApplyProfileTask: User cancelled the ApplyProfile {profile.Name}.");
}
catch( Exception ex)
{
@ -1291,16 +1433,14 @@ namespace DisplayMagician {
logger.Error(ex, $"Program/ShowMessages: Exception while trying to load the messages index from {indexUrl}.");
return;
}
ProgramSettings programSettings = ProgramSettings.LoadSettings();
foreach (MessageItem message in messageIndex)
{
// Skip if we've already shown it
if (message.Id <= programSettings.LastMessageIdRead)
if (message.Id <= AppProgramSettings.LastMessageIdRead)
{
// Unless it's one coming up that we're monitoring
if (!programSettings.MessagesToMonitor.Contains(message.Id))
if (!AppProgramSettings.MessagesToMonitor.Contains(message.Id))
{
continue;
}
@ -1354,10 +1494,10 @@ namespace DisplayMagician {
if (!(DateTime.Now >= startTime))
{
logger.Debug($"Program/ShowMessages: Message start date for \"{message.HeadingText}\" (#{message.Id}) not yet reached so not ready to show message.");
if (!programSettings.MessagesToMonitor.Contains(message.Id))
if (!AppProgramSettings.MessagesToMonitor.Contains(message.Id))
{
programSettings.MessagesToMonitor.Add(message.Id);
programSettings.SaveSettings();
AppProgramSettings.MessagesToMonitor.Add(message.Id);
AppProgramSettings.SaveSettings();
}
continue;
}
@ -1386,16 +1526,16 @@ namespace DisplayMagician {
myMessageWindow.ButtonText = message.ButtonText;
myMessageWindow.ShowDialog();
// If this the list of messages is still trying to monitor this message, then remove it if we've shown it to the user.
if (programSettings.MessagesToMonitor.Contains(message.Id))
if (AppProgramSettings.MessagesToMonitor.Contains(message.Id))
{
programSettings.MessagesToMonitor.Remove(message.Id);
programSettings.SaveSettings();
AppProgramSettings.MessagesToMonitor.Remove(message.Id);
AppProgramSettings.SaveSettings();
}
// Update the latest message id to keep track of where we're up to
if (message.Id > programSettings.LastMessageIdRead)
if (message.Id > AppProgramSettings.LastMessageIdRead)
{
programSettings.LastMessageIdRead = message.Id;
AppProgramSettings.LastMessageIdRead = message.Id;
}
}
@ -1404,6 +1544,26 @@ namespace DisplayMagician {
public static void CheckForUpdates()
{
// First of all, check to see if there is any way to get to the internet on this computer.
// If not, then why bother!
try
{
INetworkListManager networkListManager = new NetworkListManager();
//dynamic networkListManager = Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}")));
bool isConnected = networkListManager.IsConnectedToInternet;
if (!isConnected)
{
logger.Warn($"Program/CheckForUpdates: No internet detected. Skipping the auto update.");
return;
}
}
catch (Exception ex)
{
logger.Warn(ex, $"Program/CheckForUpdates: Exception while trying to get all the network interfaces to make sure we have internet connectivity. Attempting to auto update anyway.");
}
//Run the AutoUpdater to see if there are any updates available.
//FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(Application.ExecutablePath);
//AutoUpdater.InstalledVersion = new Version(fvi.FileVersion);
@ -1661,6 +1821,7 @@ namespace DisplayMagician {
}
catch (Exception ex)
{
logger.Warn(ex, $"Program/IsInstalledVersion: DisplayMagician InstallDir isn't in registry! This DisplayMagician isn't installed.");
return false;
}
}

View File

@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
@ -29,13 +30,29 @@ namespace DisplayMagician
private bool _installedDesktopContextMenu = true;
private int _lastMessageIdRead = 0;
private List<int> _messagesToMonitor = new List<int>();
private string _logLevel = NLog.LogLevel.Info.ToString();
private string _logLevel = NLog.LogLevel.Trace.ToString();
private string _displayMagicianVersion = null;
private Keys _hotkeyMainWindow = Keys.None;
private Keys _hotkeyDisplayProfileWindow = Keys.None;
private Keys _hotkeyShortcutLibraryWindow = Keys.None;
#endregion
#region Class Properties
public string DisplayMagicianVersion
{
get
{
if (_displayMagicianVersion == null)
{
return Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
return _displayMagicianVersion;
}
set
{
_displayMagicianVersion = value;
}
}
public bool StartOnBootUp
{
get
@ -45,11 +62,6 @@ namespace DisplayMagician
set
{
_startOnBootUp = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -62,11 +74,6 @@ namespace DisplayMagician
set
{
_showSplashScreen = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -79,11 +86,6 @@ namespace DisplayMagician
set
{
_showMinimiseMessageInActionCenter = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -95,12 +97,7 @@ namespace DisplayMagician
}
set
{
_showStatusMessageInActionCenter = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
_showStatusMessageInActionCenter = value;
}
}
@ -113,11 +110,6 @@ namespace DisplayMagician
set
{
_upgradeToPrereleases = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -129,11 +121,6 @@ namespace DisplayMagician
set
{
_minimiseOnStart = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -146,11 +133,6 @@ namespace DisplayMagician
set
{
_installedDesktopContextMenu = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -163,11 +145,6 @@ namespace DisplayMagician
set
{
_lastMessageIdRead = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -180,11 +157,6 @@ namespace DisplayMagician
set
{
_messagesToMonitor = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -222,11 +194,6 @@ namespace DisplayMagician
break;
}
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
@ -239,13 +206,6 @@ namespace DisplayMagician
set
{
_hotkeyMainWindow = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
{
SaveSettings();
}
}
}
@ -258,13 +218,6 @@ namespace DisplayMagician
set
{
_hotkeyDisplayProfileWindow = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
{
SaveSettings();
}
}
}
@ -277,19 +230,19 @@ namespace DisplayMagician
set
{
_hotkeyShortcutLibraryWindow = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
{
SaveSettings();
}
}
}
#endregion
#region Class Methods
~ProgramSettings()
{
// Save the program settings on program exit
SaveSettings();
}
public static ProgramSettings LoadSettings()
{
// NOTE: This function gets called before NLog has setup the logger, meaning
@ -297,6 +250,7 @@ namespace DisplayMagician
// loglevel settings so we know what level to configure the logger to write!
// This means we have to only use console.write in this function....
ProgramSettings programSettings = null;
_programSettingsLoaded = false;
if (File.Exists(programSettingsStorageJsonFileName))
{
@ -325,6 +279,10 @@ namespace DisplayMagician
{
Console.WriteLine($"ProgramSettings/LoadSettings: Tried to parse the JSON file {programSettingsStorageJsonFileName} but the JsonConvert threw an exception. {ex}");
}
if (programSettings.DisplayMagicianVersion == null) {
programSettings.DisplayMagicianVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
}
else
@ -347,6 +305,9 @@ namespace DisplayMagician
logger.Debug($"ProgramSettings/SaveSettings: Attempting to save the program settings to the {programSettingsStorageJsonFileName}.");
// Force the PreviousDisplayMagicianVersion to this version just before we save the settings.
DisplayMagicianVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
try
{
var json = JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings

View File

@ -26,8 +26,8 @@ using System.Resources;
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
// Version information
[assembly: AssemblyVersion("2.3.1.67")]
[assembly: AssemblyFileVersion("2.3.1.67")]
[assembly: AssemblyVersion("2.4.0.88")]
[assembly: AssemblyFileVersion("2.4.0.88")]
[assembly: NeutralResourcesLanguageAttribute( "en" )]
[assembly: CLSCompliant(true)]

View File

@ -40,7 +40,7 @@ namespace DisplayMagician
//private static bool _cancelWait = false;
// Other constants that are useful
private static string AppShortcutStoragePath = Path.Combine(Program.AppDataPath, $"Shortcuts");
private static string _shortcutStorageJsonFileName = Path.Combine(AppShortcutStoragePath, $"Shortcuts_2.0.json");
private static string _shortcutStorageJsonFileName = Path.Combine(AppShortcutStoragePath, $"Shortcuts_2.2.json");
private static string uuidV4Regex = @"(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$";
private static CoreAudioController _audioController = null;
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
@ -411,6 +411,8 @@ namespace DisplayMagician
logger.Debug($"ShortcutRepository/LoadShortcuts: Loading shortcuts from {_shortcutStorageJsonFileName} into the Shortcut Repository");
_shortcutsLoaded = false;
if (File.Exists(_shortcutStorageJsonFileName))
{
string json = "";
@ -1181,7 +1183,7 @@ namespace DisplayMagician
}
catch (Exception ex)
{
logger.Error($"ShortcutRepository/RunShortcut: Exception while trying to find the user supplied executable to monitor: {shortcutToUse.DifferentExecutableToMonitor}.");
logger.Error(ex, $"ShortcutRepository/RunShortcut: Exception while trying to find the user supplied executable to monitor: {shortcutToUse.DifferentExecutableToMonitor}.");
foundSomethingToMonitor = false;
}
}
@ -2095,7 +2097,6 @@ namespace DisplayMagician
{
// At the moment we only allow one stop program
StopProgram stopProg = shortcutToUse.StopPrograms[0];
uint processID = 0;
try
{
// Only start if not disabled

View File

@ -226,11 +226,13 @@ namespace DisplayMagician
}
catch (PlatformNotSupportedException ex)
{
Console.WriteLine($"SingleInstance/NamedPipeServerCreateServer: Cannot create a named pipe server. This NamedPipeServerStream function does not support this platform.");
//Console.WriteLine($"SingleInstance/NamedPipeServerCreateServer: Cannot create a named pipe server. This NamedPipeServerStream function does not support this platform.");
logger.Warn(ex, $"SingleInstance/NamedPipeServerCreateServer: Cannot create a named pipe server. This NamedPipeServerStream function does not support this platform.");
}
catch (Exception ex)
{
Console.WriteLine($"SingleInstance/NamedPipeServerCreateServer: Exception - Source: {ex.Source} {ex.TargetSite} - {ex.Message} - {ex.StackTrace}");
logger.Warn(ex, $"SingleInstance/NamedPipeServerCreateServer: Exception - Source: {ex.Source} {ex.TargetSite} - {ex.Message} - {ex.StackTrace}");
}
// Begin async wait for connections
_namedPipeServerStream.BeginWaitForConnection(NamedPipeServerConnectionCallback, _namedPipeServerStream);

View File

@ -38,6 +38,8 @@ namespace DisplayMagician.UIForms
ilv_saved_profiles.AllowDrag = false;
ilv_saved_profiles.AllowDrop = false;
ilv_saved_profiles.SetRenderer(new ProfileILVRenderer());
// Center the form on the primary screen
Utils.CenterOnPrimaryScreen(this);
}
public DisplayProfileForm(ProfileItem profileToLoad) : this()
@ -59,7 +61,8 @@ namespace DisplayMagician.UIForms
return;
}
ProfileRepository.UserChangingProfiles = true;
// Apply the Profile
//if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Successful)
ApplyProfileResult result = Program.ApplyProfileTask(_selectedProfile);
@ -81,9 +84,20 @@ namespace DisplayMagician.UIForms
logger.Error($"DisplayProfileForm/Apply_Click: Error applying the Profile {_selectedProfile.Name}. Unable to change the display layout.");
}
// Recenter the Window
RecenterWindow();
ProfileRepository.UserChangingProfiles = false;
}
private void RecenterWindow()
{
// Center the MainAppForm
Utils.CenterOnPrimaryScreen(Program.AppMainForm);
//Program.AppMainForm.Activate();
// Bring the window back to the front
Visible = true;
Activate();
Utils.ActivateCenteredOnPrimaryScreen(this);
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
@ -530,6 +544,7 @@ namespace DisplayMagician.UIForms
private void btn_view_current_Click(object sender, EventArgs e)
{
ProfileRepository.UserChangingProfiles = true;
// Refresh the profiles to see whats valid
ProfileRepository.IsPossibleRefresh();
// Reload the profiles in case we swapped to another program to change it
@ -538,14 +553,17 @@ namespace DisplayMagician.UIForms
ChangeSelectedProfile(ProfileRepository.GetActiveProfile());
// Refresh the Profile UI
RefreshDisplayProfileUI();
// Recenter the Window
RecenterWindow();
ProfileRepository.UserChangingProfiles = false;
}
private void txt_profile_save_name_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter))
{
MessageBox.Show("Click works!", "Click works", MessageBoxButtons.OK, MessageBoxIcon.Information);
//MessageBox.Show("Click works!", "Click works", MessageBoxButtons.OK, MessageBoxIcon.Information);
btn_save.PerformClick();
}
}
@ -578,7 +596,10 @@ namespace DisplayMagician.UIForms
case WM_DISPLAYCHANGE:
logger.Trace($"DisplayProfileForm/WndProc: Windows just sent a msg telling us the display has changed. Updating the current view by running btn_view_current.");
btn_view_current.PerformClick();
if (!ProfileRepository.UserChangingProfiles)
{
btn_view_current.PerformClick();
}
break;
// This auto taskbar detection logic just doesn't work at the moment

View File

@ -35,10 +35,17 @@ namespace DisplayMagician.UIForms
btn_setup_display_profiles.Parent = splitContainer1.Panel1;
btn_setup_game_shortcuts.Parent = splitContainer1.Panel2;
lbl_version.Text = string.Format(lbl_version.Text, Assembly.GetExecutingAssembly().GetName().Version);
// Refresh all possible profiles and shortcuts
ProfileRepository.IsPossibleRefresh();
ShortcutRepository.IsValidRefresh();
// Update the system tray menus
notifyIcon.Visible = true;
notifyIcon.ContextMenuStrip = mainContextMenuStrip;
RefreshNotifyIconMenus();
try
{
if (Program.AppProgramSettings.HotkeyMainWindow != Keys.None)
@ -201,7 +208,7 @@ namespace DisplayMagician.UIForms
notifyIcon.Text = $"DisplayMagician ({shortProfileName })";
Application.DoEvents();
}
// If we've been handed a Form of some kind, then open it straight away
if (formToOpen is DisplayProfileForm)
{
@ -218,9 +225,11 @@ namespace DisplayMagician.UIForms
// Make this window top most if we're not minimised
if (!Program.AppProgramSettings.MinimiseOnStart)
{
this.TopMost = true;
this.Activate();
this.TopMost = false;
// Center the MainAppForm
Utils.CenterOnPrimaryScreen(Program.AppMainForm);
// Bring the window back to the front
Utils.ActivateCenteredOnPrimaryScreen(this);
}
}
@ -330,10 +339,7 @@ namespace DisplayMagician.UIForms
}
private void MainForm_Load(object sender, EventArgs e)
{
// Update the Notify Icon menu
RefreshNotifyIconMenus();
{
EnableShortcutButtonIfProfiles();
logger.Trace($"MainForm/MainForm_Load: Main Window has loaded.");
@ -490,14 +496,18 @@ namespace DisplayMagician.UIForms
public void openApplicationWindow()
{
allowVisible = true;
/*
this.Restore();
this.Show();
this.Focus();
this.BringToFront();
this.TopMost = true;
this.Activate();
this.TopMost = false;
this.TopMost = false;*/
allowVisible = true;
// Center the form on the primary screen
Utils.ActivateCenteredOnPrimaryScreen(this);
}
public void exitApplication()

View File

@ -216,7 +216,7 @@ namespace DisplayMagician.UIForms
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"ProfileSettingsForm/btn_clear_Click: Exception while deleting wallpaper bitmap file {Profile.WallpaperBitmapFilename}");
}
// Empty the file name in the Profile

View File

@ -355,6 +355,9 @@ namespace DisplayMagician.UIForms
Program.AppProgramSettings.UpgradeToPreReleases = false;
logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully stopped DisplayMagician from upgrading to pre-release versions of software");
}
// Save ProgramSettings
Program.AppProgramSettings.SaveSettings();
}
private void btn_back_Click(object sender, EventArgs e)
@ -591,23 +594,45 @@ namespace DisplayMagician.UIForms
File.Delete(zipFilePath);
}
NLog.LogManager.SuspendLogging();
ZipArchive archive = ZipFile.Open(zipFilePath, ZipArchiveMode.Create);
// Get the list of files we want to look for to zip (they may or may not exist)
List<string> listOfFiles = new List<string> {
// Try to copy the logs if they exist
Path.Combine(Program.AppLogPath,"DisplayMagician.log"),
Path.Combine(Program.AppLogPath,"DisplayMagician1.log"),
Path.Combine(Program.AppLogPath,"DisplayMagician2.log"),
Path.Combine(Program.AppLogPath,"DisplayMagician3.log"),
Path.Combine(Program.AppLogPath,"DisplayMagician4.log"),
// Also try to copy the new configs if they exist
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.4.json"),
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.3.json"),
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.2.json"),
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.1.json"),
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.0.json"),
Path.Combine(Program.AppShortcutPath,"Shortcuts_1.0.json"),
Path.Combine(Program.AppShortcutPath,"Shortcuts_2.0.json"),
Path.Combine(Program.AppShortcutPath,"Shortcuts_2.2.json"),
Path.Combine(Program.AppDataPath,"Settings_1.0.json"),
Path.Combine(Program.AppDataPath,"Settings_2.0.json"),
Path.Combine(Program.AppDataPath,"Settings_2.4.json")
Path.Combine(Program.AppDataPath,"Settings_2.3.json"),
Path.Combine(Program.AppDataPath,"Settings_2.4.json"),
// Also try to copy the old configs if they exist
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.3.json.old"),
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.2.json.old"),
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.1.json.old"),
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.0.json.old"),
Path.Combine(Program.AppShortcutPath,"Shortcuts_1.0.json.old"),
Path.Combine(Program.AppShortcutPath,"Shortcuts_2.0.json.old"),
Path.Combine(Program.AppShortcutPath,"Shortcuts_2.2.json.old"),
Path.Combine(Program.AppDataPath,"Settings_1.0.json.old"),
Path.Combine(Program.AppDataPath,"Settings_2.0.json.old"),
Path.Combine(Program.AppDataPath,"Settings_2.3.json.old"),
Path.Combine(Program.AppDataPath,"Settings_2.4.json.old")
};
foreach (string filename in listOfFiles)
{
try
@ -639,8 +664,11 @@ namespace DisplayMagician.UIForms
}
archive.Dispose();
NLog.LogManager.ResumeLogging();
SharedLogger.logger.Trace($"SettingsForm/btn_create_support_package_Click: Finished creating support zip file at {zipFilePath}.");
MessageBox.Show($"Created DisplayMagician Support ZIP file {zipFilePath}. You can now attach this file to your GitHub issue.");
MessageBox.Show($"Created DisplayMagician Support ZIP file {zipFilePath}. You can now attach this file to your GitHub issue using your Web Browser.");
}
}
}

View File

@ -112,6 +112,9 @@ namespace DisplayMagician.UIForms
logger.Warn(ex, $"ShortcutForm/ShortcutForm: Exception while trying to initialise CoreAudioController in ShortcutForm. Audio Chipset on your computer is not supported. You will be unable to set audio settings.");
}
// Center the form on the primary screen
Utils.CenterOnPrimaryScreen(this);
}
public ShortcutItem Shortcut
@ -3142,7 +3145,7 @@ namespace DisplayMagician.UIForms
}
catch(Exception ex)
{
logger.Warn(ex, $"ShortcutForm/ilv_games_ItemClick: Exception while attempting to suggest shortcut name.");
}
try
@ -3151,7 +3154,7 @@ namespace DisplayMagician.UIForms
}
catch (Exception ex)
{
logger.Warn(ex, $"ShortcutForm/ilv_games_ItemClick: Exception while figuring out if the save button shoud be enabled.");
}
}

View File

@ -32,6 +32,8 @@ namespace DisplayMagician.UIForms
ilv_saved_shortcuts.AllowDrag = false;
ilv_saved_shortcuts.AllowDrop = false;
ilv_saved_shortcuts.SetRenderer(new ShortcutILVRenderer());
// Center the form on the primary screen
Utils.CenterOnPrimaryScreen(this);
}
private void btn_back_Click(object sender, EventArgs e)
@ -301,8 +303,8 @@ namespace DisplayMagician.UIForms
private void ShowShortcutLoadingWindow()
{
Program.AppShortcutLoadingSplashScreen = new ShortcutLoadingForm();
Program.AppShortcutLoadingSplashScreen.Title = "Preparing images...";
Program.AppShortcutLoadingSplashScreen.Description = "Preparing images before showing you the Shortcut information. You will be able to swap your shortcut icon to any image you want, or choose one from a list.";
Program.AppShortcutLoadingSplashScreen.Title = "Preparing Shortcut...";
Program.AppShortcutLoadingSplashScreen.Description = "Preparing the Shortcut ready for you to edit. You will be able to swap your shortcut icon to any image you want, or choose one from a list.";
int resultX = this.DesktopLocation.X + ((this.Width - Program.AppShortcutLoadingSplashScreen.Width) / 2);
int resultY = this.DesktopLocation.Y + ((this.Height - Program.AppShortcutLoadingSplashScreen.Height) / 2);
Program.AppShortcutLoadingSplashScreen.WantedLocation = new Point(resultX, resultY);
@ -512,9 +514,10 @@ namespace DisplayMagician.UIForms
btn_cancel.Visible = false;
btn_cancel.Enabled = false;
// Bring the window back to the front
Visible = true;
Activate();
// Center the MainAppForm
Utils.CenterOnPrimaryScreen(Program.AppMainForm);
// Bring the window back to the front
Utils.ActivateCenteredOnPrimaryScreen(this);
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DisplayMagician.Processes;
namespace DisplayMagician.UIForms
{
@ -287,7 +288,7 @@ namespace DisplayMagician.UIForms
private void cbx_start_program_priority_SelectedIndexChanged(object sender, EventArgs e)
{
myStartProgram.ProcessPriority = (ProcessPriority)cbx_start_program_priority.SelectedValue;
myStartProgram.ProcessPriority = ProcessUtils.TranslateNameToPriority(cbx_start_program_priority.SelectedValue.ToString());
}
private void cb_run_as_administrator_CheckedChanged(object sender, EventArgs e)

View File

@ -62,20 +62,28 @@ namespace DisplayMagician
}
}
public static void ShowCentered(this Form frm, Form owner)
public static void ActivateCenteredOnPrimaryScreen(this Form frm)
{
Rectangle ownerRect = GetOwnerRect(frm, owner);
frm.Location = new Point(ownerRect.Left + (ownerRect.Width - frm.Width) / 2,
ownerRect.Top + (ownerRect.Height - frm.Height) / 2);
frm.Show(owner);
CenterOnPrimaryScreen(frm);
frm.Visible = true;
frm.Activate();
frm.BringToFront();
}
public static void CenterParent(this Form frm, Rectangle ownerRect)
public static void ShowCenteredOnPrimaryScreen(this Form frm)
{
frm.Location = new Point(ownerRect.Left + (ownerRect.Width - frm.Width) / 2,
ownerRect.Top + (ownerRect.Height - frm.Height) / 2);
CenterOnPrimaryScreen(frm);
frm.Show();
}
public static void CenterOnPrimaryScreen(this Form frm)
{
frm.Top = (Screen.PrimaryScreen.Bounds.Height - frm.Height) / 2;
frm.Left = (Screen.PrimaryScreen.Bounds.Width - frm.Width) / 2;
}
public static void ShowDialogCentered(this Form frm, Form owner)
{
Rectangle ownerRect = GetOwnerRect(frm, owner);

View File

@ -167,6 +167,7 @@ namespace DisplayMagicianShared.AMD
private IntPtr _adlContextHandle = IntPtr.Zero;
private AMD_DISPLAY_CONFIG _activeDisplayConfig;
public List<ADL_DISPLAY_CONNECTION_TYPE> SkippedColorConnectionTypes;
public List<string> _allConnectedDisplayIdentifiers;
static AMDLibrary() { }
public AMDLibrary()
@ -205,6 +206,7 @@ namespace DisplayMagicianShared.AMD
SharedLogger.logger.Trace($"AMDLibrary/AMDLibrary: AMD ADL2 library was initialised successfully");
SharedLogger.logger.Trace($"AMDLibrary/AMDLibrary: Running UpdateActiveConfig to ensure there is a config to use later");
_activeDisplayConfig = GetActiveConfig();
_allConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers();
}
else
{
@ -221,7 +223,7 @@ namespace DisplayMagicianShared.AMD
{
// If we get here then the AMD ADL DLL wasn't found. We can't continue to use it, so we log the error and exit
SharedLogger.logger.Info(ex, $"AMDLibrary/AMDLibrary: Exception trying to load the AMD ADL DLL {ADLImport.ATI_ADL_DLL}. This generally means you don't have the AMD ADL driver installed.");
}
}
}
@ -335,6 +337,7 @@ namespace DisplayMagicianShared.AMD
try
{
_activeDisplayConfig = GetActiveConfig();
_allConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers();
}
catch (Exception ex)
{
@ -934,7 +937,7 @@ namespace DisplayMagicianShared.AMD
}
myDisplayConfig.HdrConfigs = new Dictionary<int, AMD_HDR_CONFIG>();
// Now we need to get all the displays connected to this adapter so that we can get their HDR state
foreach (var displayTarget in displayTargetArray)
{
@ -943,7 +946,7 @@ namespace DisplayMagicianShared.AMD
ADL_DISPLAY_CONNECTION_TYPE displayConnector;
try
{
displayConnector = displayInfoArray.First(d => d.DisplayID == displayTarget.DisplayID).DisplayConnector;
displayConnector = displayInfoArray.First(d => d.DisplayID == displayTarget.DisplayID).DisplayConnector;
}
catch (Exception ex)
{
@ -1104,7 +1107,7 @@ namespace DisplayMagicianShared.AMD
SharedLogger.logger.Trace($"AMDLibrary/PrintActiveConfig: Converted ADL2_Adapter_AdapterInfoX4_Get memory buffer into a {adapterArray.Length} long array about AMD Adapter #{adapterIndex}.");
AMD_ADAPTER_CONFIG savedAdapterConfig = new AMD_ADAPTER_CONFIG();
//AMD_ADAPTER_CONFIG savedAdapterConfig = new AMD_ADAPTER_CONFIG();
ADL_ADAPTER_INFOX2 oneAdapter = adapterArray[0];
if (oneAdapter.Exist != 1)
{
@ -1604,11 +1607,8 @@ namespace DisplayMagicianShared.AMD
// We want to check the AMD profile can be used now
SharedLogger.logger.Trace($"AMDLibrary/IsPossibleConfig: Testing whether the AMD display configuration is possible to be used now");
// Check the currently available displays (include the ones not active)
List<string> currentAllIds = GetAllConnectedDisplayIdentifiers();
// Check that we have all the displayConfig DisplayIdentifiers we need available now
if (displayConfig.DisplayIdentifiers.All(value => currentAllIds.Contains(value)))
if (displayConfig.DisplayIdentifiers.All(value => _allConnectedDisplayIdentifiers.Contains(value)))
{
SharedLogger.logger.Trace($"AMDLibrary/IsPossibleConfig: Success! The AMD display configuration is possible to be used now");
return true;
@ -1631,7 +1631,9 @@ namespace DisplayMagicianShared.AMD
{
SharedLogger.logger.Trace($"AMDLibrary/GetAllConnectedDisplayIdentifiers: Getting all the display identifiers that can possibly be used");
bool allDisplays = true;
return GetSomeDisplayIdentifiers(allDisplays);
_allConnectedDisplayIdentifiers = GetSomeDisplayIdentifiers(allDisplays);
return _allConnectedDisplayIdentifiers;
}
private List<string> GetSomeDisplayIdentifiers(bool allDisplays = false)
@ -1728,7 +1730,7 @@ namespace DisplayMagicianShared.AMD
SharedLogger.logger.Trace($"AMDLibrary/GetSomeDisplayIdentifiers: Converted ADL2_Adapter_AdapterInfoX4_Get memory buffer into a {adapterArray.Length} long array about AMD Adapter #{adapterIndex}.");
AMD_ADAPTER_CONFIG savedAdapterConfig = new AMD_ADAPTER_CONFIG();
//AMD_ADAPTER_CONFIG savedAdapterConfig = new AMD_ADAPTER_CONFIG();
ADL_ADAPTER_INFOX2 oneAdapter = adapterArray[0];
if (oneAdapter.Exist != 1)
{

View File

@ -135,7 +135,7 @@
<Version>2.1.0</Version>
</PackageReference>
<PackageReference Include="NLog">
<Version>4.7.15</Version>
<Version>5.0.1</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -567,7 +567,7 @@ namespace DisplayMagicianShared
WshShell shell = new WshShell();
IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutFileName);
IWshShortcut shortcut = shell.CreateShortcut(shortcutFileName) as IWshShortcut;
shortcut.TargetPath = Application.ExecutablePath;
shortcut.Arguments = string.Join(" ", shortcutArgs);
@ -1171,7 +1171,7 @@ namespace DisplayMagicianShared
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetInfo.DisplayId}");
SharedLogger.logger.Warn(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetInfo.DisplayId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
@ -1311,7 +1311,7 @@ namespace DisplayMagicianShared
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Error(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
SharedLogger.logger.Warn(ex, $"ProfileItem/GetNVIDIAScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
@ -1473,13 +1473,16 @@ namespace DisplayMagicianShared
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 :( .
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.");
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Error(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
SharedLogger.logger.Warn(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
@ -1558,13 +1561,21 @@ namespace DisplayMagicianShared
// rather than the MMStuckRect reg keys
try
{
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tb => tb.Value.RegKeyValue.Contains("Settings")).Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on the primary display {targetId} is on the {screen.TaskBarEdge } of the screen.");
if (_windowsDisplayConfig.TaskBarLayout.Count(tbr => tbr.Value.RegKeyValue.Contains("Settings")) > 0)
{
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains("Settings")).Value.Edge;
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;
}
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Error(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on primary display {targetId}");
SharedLogger.logger.Warn(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on primary display {targetId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
@ -1573,13 +1584,56 @@ namespace DisplayMagicianShared
{
try
{
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.");
int numMatches = _windowsDisplayConfig.TaskBarLayout.Count(tbr => 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;
}
}
catch (Exception ex)
{
// Guess that it is at the bottom (90% correct)
SharedLogger.logger.Error(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
SharedLogger.logger.Warn(ex, $"ProfileItem/GetWindowsScreenPositions: Exception trying to get the position of the taskbar on display {targetId}");
screen.TaskBarEdge = TaskBarLayout.TaskBarEdge.Bottom;
}
}

View File

@ -61,13 +61,14 @@ namespace DisplayMagicianShared
private static VIDEO_MODE _currentVideoMode = VIDEO_MODE.WINDOWS;
private static FORCED_VIDEO_MODE _forcedVideoMode = FORCED_VIDEO_MODE.DETECT;
private static bool _pauseReadsUntilChangeCompleted = false;
private static bool _userChangingProfiles = false;
// Other constants that are useful
public static string AppDataPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DisplayMagician");
public static string AppIconPath = System.IO.Path.Combine(AppDataPath, $"Icons");
public static string AppDisplayMagicianIconFilename = System.IO.Path.Combine(AppIconPath, @"DisplayMagician.ico");
private static readonly string AppProfileStoragePath = System.IO.Path.Combine(AppDataPath, $"Profiles");
private static readonly string _profileStorageJsonFileName = System.IO.Path.Combine(AppProfileStoragePath, $"DisplayProfiles_2.3.json");
private static readonly string _profileStorageJsonFileName = System.IO.Path.Combine(AppProfileStoragePath, $"DisplayProfiles_2.4.json");
@ -225,6 +226,18 @@ namespace DisplayMagicianShared
}
}
public static bool UserChangingProfiles
{
get
{
return _userChangingProfiles;
}
set
{
_userChangingProfiles = value;
}
}
#endregion
#region Class Methods
@ -295,10 +308,15 @@ namespace DisplayMagicianShared
List<ProfileItem> ProfilesToRemove = _allProfiles.FindAll(item => item.UUID.Equals(profile.UUID));
foreach (ProfileItem ProfileToRemove in ProfilesToRemove)
{
// Attempt to delete the icon
try
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
File.Delete(ProfileToRemove.WallpaperBitmapFilename);
if (File.Exists(ProfileToRemove.SavedProfileIconCacheFilename))
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
}
}
catch (UnauthorizedAccessException ex)
{
@ -317,6 +335,31 @@ namespace DisplayMagicianShared
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there.");
}
// attempt to delete the wallpaper
try
{
if (File.Exists(ProfileToRemove.WallpaperBitmapFilename))
{
File.Delete(ProfileToRemove.WallpaperBitmapFilename);
}
}
catch (UnauthorizedAccessException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician doesn't have permissions to delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename}.");
}
catch (ArgumentException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} due to an invalid argument.");
}
catch (PathTooLongException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} as the path is too long.");
}
catch (DirectoryNotFoundException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} as the parent folder isn't there.");
}
}
// Remove the Profile from the list.
@ -326,6 +369,7 @@ namespace DisplayMagicianShared
{
SaveProfiles();
IsPossibleRefresh();
UpdateActiveProfile();
return true;
}
else if (numRemoved == 0)
@ -347,27 +391,58 @@ namespace DisplayMagicianShared
List<ProfileItem> ProfilesToRemove = _allProfiles.FindAll(item => item.Name.Equals(profileName));
foreach (ProfileItem ProfileToRemove in ProfilesToRemove)
{
// Attempt to delete the icon
try
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
File.Delete(ProfileToRemove.WallpaperBitmapFilename);
if (File.Exists(ProfileToRemove.SavedProfileIconCacheFilename))
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
}
}
catch (UnauthorizedAccessException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile2: DisplayMagician doesn't have permissions to delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename}.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician doesn't have permissions to delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename}.");
}
catch (ArgumentException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile2: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} due to an invalid argument.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} due to an invalid argument.");
}
catch (PathTooLongException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile2: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the path is too long.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the path is too long.");
}
catch (DirectoryNotFoundException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile2: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there.");
}
// attempt to delete the wallpaper
try
{
if (File.Exists(ProfileToRemove.WallpaperBitmapFilename))
{
File.Delete(ProfileToRemove.WallpaperBitmapFilename);
}
}
catch (UnauthorizedAccessException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician doesn't have permissions to delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename}.");
}
catch (ArgumentException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} due to an invalid argument.");
}
catch (PathTooLongException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} as the path is too long.");
}
catch (DirectoryNotFoundException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} as the parent folder isn't there.");
}
}
// Remove the Profile from the list.
@ -377,6 +452,7 @@ namespace DisplayMagicianShared
{
SaveProfiles();
IsPossibleRefresh();
UpdateActiveProfile();
return true;
}
else if (numRemoved == 0)
@ -397,27 +473,58 @@ namespace DisplayMagicianShared
List<ProfileItem> ProfilesToRemove = _allProfiles.FindAll(item => item.UUID.Equals(profileId));
foreach (ProfileItem ProfileToRemove in ProfilesToRemove)
{
// Attempt to delete the icon
try
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
File.Delete(ProfileToRemove.WallpaperBitmapFilename);
if (File.Exists(ProfileToRemove.SavedProfileIconCacheFilename))
{
File.Delete(ProfileToRemove.SavedProfileIconCacheFilename);
}
}
catch (UnauthorizedAccessException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile3: DisplayMagician doesn't have permissions to delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename}.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician doesn't have permissions to delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename}.");
}
catch (ArgumentException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile3: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} due to an invalid argument.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} due to an invalid argument.");
}
catch (PathTooLongException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile3: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the path is too long.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the path is too long.");
}
catch (DirectoryNotFoundException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile3: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there.");
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there.");
}
// attempt to delete the wallpaper
try
{
if (File.Exists(ProfileToRemove.WallpaperBitmapFilename))
{
File.Delete(ProfileToRemove.WallpaperBitmapFilename);
}
}
catch (UnauthorizedAccessException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician doesn't have permissions to delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename}.");
}
catch (ArgumentException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} due to an invalid argument.");
}
catch (PathTooLongException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} as the path is too long.");
}
catch (DirectoryNotFoundException ex)
{
SharedLogger.logger.Error(ex, $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile wallpaper {ProfileToRemove.WallpaperBitmapFilename} as the parent folder isn't there.");
}
}
// Remove the Profile from the list.
@ -427,6 +534,7 @@ namespace DisplayMagicianShared
{
SaveProfiles();
IsPossibleRefresh();
UpdateActiveProfile();
return true;
}
else if (numRemoved == 0)
@ -704,6 +812,8 @@ namespace DisplayMagicianShared
{
SharedLogger.logger.Debug($"ProfileRepository/LoadProfiles: Loading profiles from {_profileStorageJsonFileName} into the Profile Repository");
_profilesLoaded = false;
if (File.Exists(_profileStorageJsonFileName))
{
string json = "";
@ -739,7 +849,14 @@ namespace DisplayMagicianShared
args.ErrorContext.Handled = true;
},
};
_allProfiles = JsonConvert.DeserializeObject<List<ProfileItem>>(json, mySerializerSettings);
_allProfiles = JsonConvert.DeserializeObject<List<ProfileItem>>(json, mySerializerSettings);
// We have to patch the adapter IDs after we load a display config because Windows changes them after every reboot :(
foreach (ProfileItem profile in _allProfiles)
{
WINDOWS_DISPLAY_CONFIG winProfile = profile.WindowsDisplayConfig;
WinLibrary.GetLibrary().PatchWindowsDisplayConfig(ref winProfile);
}
}
catch (JsonReaderException ex)
@ -844,7 +961,32 @@ namespace DisplayMagicianShared
// We do the actual change we were trying to do
try
{
// Nothing to patch at the moment!
// Add in a default Windows DPI information we need
// This adds a 'SourceDpiScalingRel' with a default of 100% (integer 0) into each DisplaySources entry
// but only if the existing entry is a 'null'. This only occurs when the SourceDpiScalingRel is unset.
// This migration will add the default 100% scaling so that the ProfileRepository Load function works as intended.
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing Windows DPI settings.");
for (int i = 0; i < root.Count; i++)
{
JObject profile = (JObject)root[i];
//JObject WindowsTaskBarSettings = (JObject)profile.SelectToken("WindowsDisplayConfig.TaskBarSettings");
var dsList = profile["WindowsDisplayConfig"]["DisplaySources"].Children();
IList<DISPLAY_SOURCE> displaySources = new List<DISPLAY_SOURCE>();
foreach (var dsListItem in dsList)
{
var displaySourceArray = dsListItem.Values().ToArray();
for (int j=0; j<displaySourceArray.Length; j++)
{
if (displaySourceArray[j]["SourceDpiScalingRel"] == null)
{
displaySourceArray[j]["SourceDpiScalingRel"] = 0;
changedJson = true;
}
}
}
}
}
catch (JsonReaderException ex)
{
@ -1169,6 +1311,8 @@ namespace DisplayMagicianShared
// We stop the stop watch
stopWatch.Stop();
_pauseReadsUntilChangeCompleted = false;
// Pause for a bit to let things settle
Thread.Sleep(500);
// Get the elapsed time as a TimeSpan value.
TimeSpan ts = stopWatch.Elapsed;
string result = "failed";
@ -1227,7 +1371,7 @@ namespace DisplayMagicianShared
// Figure out the Video Cards and see what mode we want
// Get a list of all the PCI Vendor IDs
List<string> videoCardVendors = WinLibrary.GetLibrary().GetCurrentPCIVideoCardVendors();
List<string> videoCardVendors = WinLibrary.GetLibrary().GetAllPCIVideoCardVendors();
if (NVIDIALibrary.GetLibrary().IsInstalled && NVIDIALibrary.GetLibrary().PCIVendorIDs.All(value => videoCardVendors.Contains(value)))
{
// We detected a NVIDIA video card in the computer
@ -1245,7 +1389,7 @@ namespace DisplayMagicianShared
}
}
if (_currentVideoMode == VIDEO_MODE.NVIDIA)
if (_currentVideoMode == VIDEO_MODE.NVIDIA && !(nvidiaLibrary is NVIDIALibrary))
{
// Initialise the the NVIDIA NvAPI Library
try
@ -1259,7 +1403,7 @@ namespace DisplayMagicianShared
return false;
}
}
else if (_currentVideoMode == VIDEO_MODE.AMD)
else if (_currentVideoMode == VIDEO_MODE.AMD && !(amdLibrary is AMDLibrary))
{
// Initialise the the AMD ADL Library
try

View File

@ -19,8 +19,24 @@ namespace DisplayMagicianShared.Windows
ERROR_BAD_CONFIGURATION = 1610,
}
public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : UInt32
public enum DPI_AWARENESS_CONTEXT : Int32
{
DPI_AWARENESS_CONTEXT_UNDEFINED = 0,
DPI_AWARENESS_CONTEXT_UNAWARE = -1, //' DPI unaware. This window does not scale for DPI changes and is always assumed to have a scale factor of 100% (96 DPI). It will be automatically scaled by the system on any other DPI setting.
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2, //' System DPI aware. This window does not scale for DPI changes. It will query for the DPI once and use that value for the lifetime of the process. If the DPI changes, the process will not adjust to the new DPI value. It will be automatically scaled up or down by the system when the DPI changes from the system value.
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3, // ' Per monitor DPI aware. This window checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes. These processes are not automatically scaled by the system.
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4, //' Also known as Per Monitor v2. An advancement over the original per-monitor DPI awareness mode, which enables applications to access new DPI-related scaling behaviors on a per top-level window basis.
DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5, //' DPI unaware with improved quality of GDI-based content. This mode behaves similarly to DPI_AWARENESS_CONTEXT_UNAWARE, but also enables the system to automatically improve the rendering quality of text and other GDI-based primitives when the window is displayed on a high-DPI monitor.
};
public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : Int32
{
// MS Private API (which seems to use negative numbers)
// See https://github.com/lihas/windows-DPI-scaling-sample/blob/master/DPIHelper/DpiHelper.h from Sahil Singh
DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE = -4, // Set current dpi scaling value for a display
DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE = -3, // Returns min, max, suggested, and currently applied DPI scaling values.
// MS Public API
Zero = 0,
DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME = 1, // Specifies the source name of the display device. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns the source name in the DISPLAYCONFIG_SOURCE_DEVICE_NAME structure.
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME = 2, // Specifies information about the monitor. If the DisplayConfigGetDeviceInfo function is successful, DisplayConfigGetDeviceInfo returns info about the monitor in the DISPLAYCONFIG_TARGET_DEVICE_NAME structure.
@ -37,7 +53,7 @@ namespace DisplayMagicianShared.Windows
// Supported starting in Windows<77>10 Fall Creators Update (Version 1709).
DISPLAYCONFIG_DEVICE_INFO_GET_MONITOR_SPECIALIZATION = 12,
DISPLAYCONFIG_DEVICE_INFO_SET_MONITOR_SPECIALIZATION = 13,
DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF // Only here to
//DISPLAYCONFIG_DEVICE_INFO_FORCE_UINT32 = 0xFFFFFFFF // Only here to
}
[Flags]
@ -269,6 +285,73 @@ namespace DisplayMagicianShared.Windows
Other = 255
}
/*
* OS reports DPI scaling values in relative terms, and not absolute terms.
* eg. if current DPI value is 250%, and recommended value is 200%, then
* OS will give us integer 2 for DPI scaling value (starting from recommended
* DPI scaling move 2 steps to the right in this list).
* values observed (and extrapolated) from system settings app (immersive control panel).
*/
/*public enum DPI_VALUES: UInt32
{
DPI_100 = 100,
DPI_125 = 125,
DPI_150 = 150,
DPI_175 = 175,
DPI_200 = 200,
DPI_225 = 225,
DPI_250 = 250,
DPI_300 = 300,
DPI_350 = 350,
DPI_400 = 400,
DPI_450 = 450,
DPI_500 = 500
};*/
/*
* struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
* @brief used to fetch min, max, suggested, and currently applied DPI scaling values.
* All values are relative to the recommended DPI scaling value
* Note that DPI scaling is a property of the source, and not of target.
*/
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAYCONFIG_SOURCE_DPI_SCALE_GET
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER Header;
/*
* @brief min value of DPI scaling is always 100, minScaleRel gives no. of steps down from recommended scaling
* eg. if minScaleRel is -3 => 100 is 3 steps down from recommended scaling => recommended scaling is 175%
*/
public UInt32 MinScaleRel;
/*
* @brief currently applied DPI scaling value wrt the recommended value. eg. if recommended value is 175%,
* => if curScaleRel == 0 the current scaling is 175%, if curScaleRel == -1, then current scale is 150%
*/
public UInt32 CurrrentScaleRel;
/*
* @brief maximum supported DPI scaling wrt recommended value
*/
public UInt32 MaxScaleRel;
};
/*
* struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
* @brief set DPI scaling value of a source
* Note that DPI scaling is a property of the source, and not of target.
*/
public struct DISPLAYCONFIG_SOURCE_DPI_SCALE_SET
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER Header;
/*
* @brief The value we want to set. The value should be relative to the recommended DPI scaling value of source.
* eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source
*/
public UInt32 ScaleRel;
};
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAYCONFIG_DEVICE_INFO_HEADER : IEquatable<DISPLAYCONFIG_DEVICE_INFO_HEADER>
{
@ -1022,6 +1105,7 @@ namespace DisplayMagicianShared.Windows
// Set some useful constants
public const SDC SDC_CCD_TEST_IF_VALID = (SDC.SDC_VALIDATE | SDC.SDC_USE_SUPPLIED_DISPLAY_CONFIG);
public const uint DISPLAYCONFIG_PATH_MODE_IDX_INVALID = 0xffffffff;
//public static readonly UInt32[] DPI_VALUES = { 100, 125, 150, 175, 200, 225, 250, 300, 350, 400, 450, 500 };
// GetDisplayConfigBufferSizes
@ -1069,6 +1153,10 @@ namespace DisplayMagicianShared.Windows
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SDR_WHITE_LEVEL requestPacket);
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DPI_SCALE_GET requestPacket);
// DisplayConfigSetDeviceInfo
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_TARGET_PERSISTENCE requestPacket);
@ -1076,6 +1164,9 @@ namespace DisplayMagicianShared.Windows
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE requestPacket);
[DllImport("user32")]
public static extern WIN32STATUS DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DPI_SCALE_SET requestPacket);
// Have disabled the DisplayConfigSetDeviceInfo options except for SET_TARGET_PERSISTENCE, as per the note
// from https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-displayconfigsetdeviceinfo
@ -1087,5 +1178,10 @@ namespace DisplayMagicianShared.Windows
[DllImport("user32")]
public static extern WIN32STATUS SetDisplayConfig([In] uint numPathArrayElements, [In] DISPLAYCONFIG_PATH_INFO[] pathArray, [In] uint numModeInfoArrayElements, [In] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, [In] SDC flags);
[DllImport("user32")]
public static extern bool SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT value);
}
}

View File

@ -67,7 +67,7 @@ namespace DisplayMagicianShared.Windows
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
MMStuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect3 registry key! {address}");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Found MMStuckRect3 registry key! {address}");
}
else
{
@ -77,13 +77,13 @@ namespace DisplayMagicianShared.Windows
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
MMStuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found MMStuckRect2 registry key! {address}");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Found MMStuckRect2 registry key! {address}");
}
else
{
// It's not v2 or v3, so it must be a single display
MMStuckRectVerFound = false;
SharedLogger.logger.Warn($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an MMStuckRect2 or MMStuckRect3 registry key! Going to test if it is a single display only.");
SharedLogger.logger.Warn($"TaskBarLayout/ReadFromRegistry: Couldn't find an MMStuckRect2 or MMStuckRect3 registry key! Going to test if it is a single display only.");
}
}
@ -92,40 +92,48 @@ namespace DisplayMagicianShared.Windows
// Check if value exists
if (version >= 2 && version <= 3)
{
using (var key = Registry.CurrentUser.OpenSubKey(
try
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = false;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
// If we get here then we're done and don't need to continue with the rest of the code.
return true;
}
else
{
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen. Screen details may not be available yet in registry.");
}
}
}
catch (Exception ex)
{
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = false;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
// If we get here then we're done and don't need to continue with the rest of the code.
return true;
}
else
{
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen.");
}
SharedLogger.logger.Trace(ex, $"TaskBarLayout/ReadFromRegistry: Exception while trying to open RegKey {address}. Unable to get the TaskBarStuckRectangle binary settings. Screen details may not be available yet in registry.");
}
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A MMStuckRect entry was found, but the version of the field is wrong.");
SharedLogger.logger.Error($"TaskBarLayout/ReadFromRegistry: A MMStuckRect entry was found, but the version of the field is wrong.");
}
}
else
{
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: A MMStuckRect entry was NOT found. We will try to find the object in the StuckRect registry key instead");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: A MMStuckRect entry was NOT found. We will try to find the object in the StuckRect registry key instead");
}
bool StuckRectVerFound = false;
@ -135,7 +143,7 @@ namespace DisplayMagicianShared.Windows
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
StuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect3 single display registry key! {address}");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Found StuckRect3 single display registry key! {address}");
}
else
{
@ -145,11 +153,11 @@ namespace DisplayMagicianShared.Windows
if (Registry.CurrentUser.OpenSubKey(address) != null)
{
StuckRectVerFound = true;
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: Found StuckRect2 single display registry key! {address}");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Found StuckRect2 single display registry key! {address}");
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Couldn't find an single display StuckRect2 or StuckRect3 registry key! So we have to just return after doing nothing as there is nothing we can do.");
SharedLogger.logger.Error($"TaskBarLayout/ReadFromRegistry: Couldn't find an single display StuckRect2 or StuckRect3 registry key! So we have to just return after doing nothing as there is nothing we can do.");
return false;
}
}
@ -159,40 +167,49 @@ namespace DisplayMagicianShared.Windows
// Check if value exists
if (version >= 2 && version <= 3)
{
using (var key = Registry.CurrentUser.OpenSubKey(
try
{
using (var key = Registry.CurrentUser.OpenSubKey(
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = true;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = true;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarStuckRectangle/TaskBarStuckRectangle: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
return true;
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen.");
return false;
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
return true;
}
else
{
SharedLogger.logger.Error($"TaskBarLayout/ReadFromRegistry: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen.");
return false;
}
}
}
catch (Exception ex)
{
SharedLogger.logger.Trace(ex, $"TaskBarLayout/ReadFromRegistry: Exception2 while trying to open RegKey {address}. Unable to get the TaskBarStuckRectangle binary settings. Screen details may not be available yet in registry.");
return false;
}
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was found, but the version of the field is wrong.");
SharedLogger.logger.Error($"TaskBarLayout/ReadFromRegistry: A StuckRect entry was found, but the version of the field is wrong.");
return false;
}
}
else
{
SharedLogger.logger.Error($"TaskBarStuckRectangle/TaskBarStuckRectangle: A StuckRect entry was NOT found. This means we're unable to get the taskbar location, an unable to return a sensible TaskBarStuckRectangle object.");
SharedLogger.logger.Error($"TaskBarLayout/ReadFromRegistry: A StuckRect entry was NOT found. This means we're unable to get the taskbar location, an unable to return a sensible TaskBarStuckRectangle object.");
return false;
}
@ -332,7 +349,7 @@ namespace DisplayMagicianShared.Windows
Rows = BitConverter.ToUInt32(Binary, 44);
}
SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateFieldsFromBinary: Grabbed the following settings for {RegKeyValue} from the registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}.");
SharedLogger.logger.Trace($"TaskBarLayout/PopulateFieldsFromBinary: Grabbed the following settings for {RegKeyValue} from the registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}.");
return true;
}
@ -432,7 +449,7 @@ namespace DisplayMagicianShared.Windows
Array.Copy(bytes, 0, Binary, 44, 4);
}
SharedLogger.logger.Trace($"TaskBarStuckRectangle/PopulateBinaryFromFields: Set the following settings for {RegKeyValue} into registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}.");
SharedLogger.logger.Trace($"TaskBarLayout/PopulateBinaryFromFields: Set the following settings for {RegKeyValue} into registry: DPI = {DPI}, Edge = {Edge}, Location = ({TaskBarLocation.X},{TaskBarLocation.Y}), MinSize = {TaskBarLocation.Width}x{TaskBarLocation.Height}, Options = {Options}, Rows = {Rows}.");
return true;
}
@ -455,12 +472,12 @@ namespace DisplayMagicianShared.Windows
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(RegKeyValue, Binary);
SharedLogger.logger.Trace($"TaskBarStuckRectangle/Apply: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!");
SharedLogger.logger.Trace($"TaskBarLayout/Apply: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!");
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/GetCurrent: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!");
SharedLogger.logger.Error(ex, $"TaskBarLayout/GetCurrent: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!");
}
}
else
@ -474,22 +491,23 @@ namespace DisplayMagicianShared.Windows
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(RegKeyValue, Binary);
SharedLogger.logger.Trace($"TaskBarStuckRectangle/WriteToRegistry: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!");
SharedLogger.logger.Trace($"TaskBarLayout/WriteToRegistry: Successfully applied TaskBarStuckRectangle registry settings for the {RegKeyValue} Screen in {address}!");
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"TaskBarStuckRectangle/WriteToRegistry: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!");
SharedLogger.logger.Error(ex, $"TaskBarLayout/WriteToRegistry: Unable to set the {RegKeyValue} TaskBarStuckRectangle registry settings in {address} due to an exception!");
}
}
return true;
}
public static Dictionary<string, TaskBarLayout> GetAllCurrentTaskBarLayouts(Dictionary<string, List<DISPLAY_SOURCE>> displaySources)
public static Dictionary<string, TaskBarLayout> GetAllCurrentTaskBarLayouts(Dictionary<string, List<DISPLAY_SOURCE>> displaySources, out bool retryNeeded)
{
Dictionary<string, TaskBarLayout> taskBarStuckRectangles = new Dictionary<string, TaskBarLayout>();
int state;
bool tbsrReadWorked = false;
APPBARDATA abd = new APPBARDATA();
@ -513,6 +531,7 @@ namespace DisplayMagicianShared.Windows
MONITORINFOEX monitorInfo = new MONITORINFOEX();
monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX));
//monitorInfo.szDevice = new char[Utils.CCHDEVICENAME];
SharedLogger.logger.Trace($"TaskBarLayout/GetAllCurrentTaskBarPositions: Getting the monitor coordinates from the main monitor");
Utils.GetMonitorInfo(mainMonitorHwnd, ref monitorInfo);
abd.hWnd = mainTaskbarHwnd;
@ -531,7 +550,13 @@ namespace DisplayMagicianShared.Windows
TaskBarLayout tbsr = new TaskBarLayout();
// Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created
tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
tbsrReadWorked = tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
if (!tbsrReadWorked)
{
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar read #1 from registry didn't work.");
retryNeeded = true;
return taskBarStuckRectangles;
}
tbsr.Edge = (TaskBarEdge)abd.uEdge;
tbsr.MonitorLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight);
switch (tbsr.Edge)
@ -562,12 +587,22 @@ namespace DisplayMagicianShared.Windows
// If it's a main screen, also add a duplicate so we track the main StuckRects settings separately too
TaskBarLayout tbsrMain = new TaskBarLayout();
tbsrMain.ReadFromRegistry("Settings");
tbsrReadWorked = tbsrMain.ReadFromRegistry("Settings");
if (!tbsrReadWorked)
{
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar read #1 from registry didn't work.");
retryNeeded = true;
return taskBarStuckRectangles;
}
tbsrMain.Edge = tbsr.Edge;
tbsrMain.MonitorLocation = tbsr.MonitorLocation;
tbsrMain.TaskBarLocation = tbsr.TaskBarLocation;
tbsrMain.MainScreen = tbsr.MainScreen;
SharedLogger.logger.Trace($"TaskBarLayout/GetAllCurrentTaskBarPositions: Main monitor coordinates are {tbsrMain.MonitorLocation.X},{tbsrMain.MonitorLocation.Y} and it is {tbsrMain.MonitorLocation.Width}x{tbsrMain.MonitorLocation.Height}");
SharedLogger.logger.Trace($"TaskBarLayout/GetAllCurrentTaskBarPositions: Main taskbar coordinates are {tbsrMain.TaskBarLocation.X},{tbsrMain.TaskBarLocation.Y} and it is {tbsrMain.TaskBarLocation.Width}x{tbsrMain.TaskBarLocation.Height}");
SharedLogger.logger.Trace($"TaskBarLayout/GetAllCurrentTaskBarPositions: Main taskbar is {tbsrMain.Edge.ToString("G")}");
// Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored
tbsrMain.PopulateBinaryFromFields();
taskBarStuckRectangles.Add("Settings", tbsrMain);
@ -575,117 +610,169 @@ namespace DisplayMagicianShared.Windows
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"WinLibrary/GetAllCurrentTaskBarPositions: Exception while trying to get the maintaskbar position");
SharedLogger.logger.Error(ex, $"TaskBarLayout/GetAllCurrentTaskBarPositions: Exception while trying to get the main taskbar position");
}
// Then go through the secondary windows and get the position of them
// Tell Windows to refresh the Other Windows Taskbars if needed
IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL;
for (int i = 0; i < 100; i++)
int clonedCount = 0;
try
{
// Find the next "Shell_SecondaryTrayWnd" window
IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null);
if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL)
IntPtr lastTaskBarWindowHwnd = (IntPtr)Utils.NULL;
for (int i = 0; i < 100; i++)
{
// No more windows taskbars to notify
break;
}
IntPtr secMonitorHwnd = Utils.MonitorFromWindow(nextTaskBarWindowHwnd, Utils.MONITOR_DEFAULTTONEAREST);
// Figure out the monitor coordinates
MONITORINFOEX monitorInfo = new MONITORINFOEX();
monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX));
//monitorInfo.szDevice = new char[Utils.CCHDEVICENAME];
Utils.GetMonitorInfo(secMonitorHwnd, ref monitorInfo);
// Figure out the position of the taskbar ourselves
int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right);
int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom);
int wrkWidth = Math.Abs(monitorInfo.rcWork.left - monitorInfo.rcWork.right);
int wrkHeight = Math.Abs(monitorInfo.rcWork.top - monitorInfo.rcWork.bottom);
int tbWidth;
int tbHeight;
TaskBarLayout tbsr = new TaskBarLayout();
// Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created
tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
if (monWidth == wrkWidth)
{
// Taskbar on top or bottom
if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top)
// Find the next "Shell_SecondaryTrayWnd" window
IntPtr nextTaskBarWindowHwnd = Utils.FindWindowEx((IntPtr)Utils.NULL, lastTaskBarWindowHwnd, "Shell_SecondaryTrayWnd", null);
if (nextTaskBarWindowHwnd == (IntPtr)Utils.NULL)
{
// Taskbar on bottom
// No more windows taskbars to notify
break;
}
IntPtr secMonitorHwnd = Utils.MonitorFromWindow(nextTaskBarWindowHwnd, Utils.MONITOR_DEFAULTTONEAREST);
// Figure out the monitor coordinates
MONITORINFOEX monitorInfo = new MONITORINFOEX();
monitorInfo.cbSize = (UInt32)Marshal.SizeOf(typeof(MONITORINFOEX));
//monitorInfo.szDevice = new char[Utils.CCHDEVICENAME];
Utils.GetMonitorInfo(secMonitorHwnd, ref monitorInfo);
// Figure out the position of the taskbar ourselves
int monWidth = Math.Abs(monitorInfo.rcMonitor.left - monitorInfo.rcMonitor.right);
int monHeight = Math.Abs(monitorInfo.rcMonitor.top - monitorInfo.rcMonitor.bottom);
int wrkWidth = Math.Abs(monitorInfo.rcWork.left - monitorInfo.rcWork.right);
int wrkHeight = Math.Abs(monitorInfo.rcWork.top - monitorInfo.rcWork.bottom);
int tbWidth;
int tbHeight;
TaskBarLayout tbsr = new TaskBarLayout();
// Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created
tbsrReadWorked = tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
if (!tbsrReadWorked)
{
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar read #3 from registry didn't work.");
retryNeeded = true;
return taskBarStuckRectangles;
}
if (monWidth == wrkWidth)
{
// Taskbar on top or bottom
if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top)
{
// Taskbar on bottom
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Bottom;
}
else if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom)
{
// Taskbar on top
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Top;
}
else
{
// Invalid state
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar position was not on a horizontal edge of a monitor!");
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Forcing Taskbar position to be at the bottom");
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Bottom;
}
}
else if (monHeight == wrkHeight)
{
// Taskbar on the sides
if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom)
{
// Taskbar on left
tbWidth = monWidth - wrkWidth;
tbHeight = monHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Left;
}
else if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top)
{
// Taskbar on right
tbWidth = monWidth - wrkWidth;
tbHeight = monHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.right, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Right;
}
else
{
// Invalid state
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar position was not on a vertical edge of a monitor!");
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Forcing Taskbar position to be at the bottom");
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Bottom;
}
}
else
{
// Invalid state
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar position was not fully along one of the monitor edges!");
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Forcing Taskbar position to be at the bottom");
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcWork.bottom, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Bottom;
}
else if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom)
tbsr.MonitorLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight);
tbsr.MainScreen = false;
SharedLogger.logger.Trace($"TaskBarLayout/GetAllCurrentTaskBarPositions: Secondary monitor coordinates are {tbsr.MonitorLocation.X},{tbsr.MonitorLocation.Y} and it is {tbsr.MonitorLocation.Width}x{tbsr.MonitorLocation.Height}");
SharedLogger.logger.Trace($"TaskBarLayout/GetAllCurrentTaskBarPositions: Secondary taskbar coordinates are {tbsr.TaskBarLocation.X},{tbsr.TaskBarLocation.Y} and it is {tbsr.TaskBarLocation.Width}x{tbsr.TaskBarLocation.Height}");
SharedLogger.logger.Trace($"TaskBarLayout/GetAllCurrentTaskBarPositions: Secondary taskbar is {tbsr.Edge.ToString("G")}");
// Now as a LAST step we update the Binary field to make sure that the correct binary settings are stored
// This means the correct location should be returned even if the registry isn't updated as we're patching the registry object before we store it.
tbsr.PopulateBinaryFromFields();
if (!taskBarStuckRectangles.ContainsKey(monitorInfo.szDevice))
{
// Taskbar on top
tbWidth = monWidth;
tbHeight = monHeight - wrkHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Top;
taskBarStuckRectangles.Add(monitorInfo.szDevice, tbsr);
}
else
{
// Invalid state
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not on a horizontal edge of a monitor!");
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Skipping grabbing Taskbar position from a cloned display {monitorInfo.szDevice}");
clonedCount++;
}
// Prep the next taskbar window so we continue through them
lastTaskBarWindowHwnd = nextTaskBarWindowHwnd;
}
else if (monHeight == wrkHeight)
{
// Taskbar on the sides
if (monitorInfo.rcMonitor.right == monitorInfo.rcWork.right && monitorInfo.rcMonitor.bottom == monitorInfo.rcWork.bottom)
{
// Taskbar on left
tbWidth = monWidth - wrkWidth;
tbHeight = monHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Left;
}
else if (monitorInfo.rcMonitor.left == monitorInfo.rcWork.left && monitorInfo.rcMonitor.top == monitorInfo.rcWork.top)
{
// Taskbar on right
tbWidth = monWidth - wrkWidth;
tbHeight = monHeight;
tbsr.TaskBarLocation = new System.Drawing.Rectangle(monitorInfo.rcWork.right, monitorInfo.rcMonitor.top, tbWidth, tbHeight);
tbsr.Edge = TaskBarEdge.Right;
}
else
{
// Invalid state
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not on a vertical edge of a monitor!");
}
}
else
{
// Invalid state
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Taskbar position was not fully along one of the monitor edges!");
}
tbsr.MonitorLocation = new System.Drawing.Rectangle(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, monWidth, monHeight);
tbsr.MainScreen = false;
// Now as a LAST step we update the Binary field just before we apply it to make sure that the correct binary settings are stored
tbsr.PopulateBinaryFromFields();
if (!taskBarStuckRectangles.ContainsKey(monitorInfo.szDevice))
{
taskBarStuckRectangles.Add(monitorInfo.szDevice, tbsr);
}
else
{
SharedLogger.logger.Error($"WinLibrary/GetAllCurrentTaskBarPositions: Skipping grabbing Taskbar position from a cloned display {monitorInfo.szDevice}");
}
// Prep the next taskbar window so we continue through them
lastTaskBarWindowHwnd = nextTaskBarWindowHwnd;
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"TaskBarLayout/GetAllCurrentTaskBarPositions: Exception while trying to get a secondary taskbar position");
}
// Check if the display reg keys shown match the display sources
foreach (var tbrKey in taskBarStuckRectangles.Keys)
{
if (tbrKey.Equals("Settings"))
{
continue;
}
// If there isn't a match then we have a problem.
if (!displaySources.ContainsKey(tbrKey))
{
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: We have an error because Display Sources array doesn't include the {tbrKey} taskbar data. This means we have a mismatch somewhere.");
retryNeeded = true;
}
}
retryNeeded = false;
return taskBarStuckRectangles;
}

View File

@ -74,20 +74,22 @@ namespace DisplayMagicianShared.Windows
if (value != null && value is int intValue)
{
SharedLogger.logger.Trace($"TaskBarSettings/GetCurrent: Got taskbar setting {valueName} = {intValue}");
taskBarOptions.Add(new Tuple<string, int>(valueName, intValue));
}
}
catch (Exception)
catch (Exception ex)
{
// ignored, as this will happen
SharedLogger.logger.Error(ex,$"TaskBarSettings/GetCurrent: Exception getting taskbar setting {valueName}. It likely does not exist (which happens if it is not explicitly set).");
}
}
}
}
}
catch (Exception)
catch (Exception ex)
{
// ignored
SharedLogger.logger.Error(ex, $"TaskBarSettings/GetCurrent: Exception opening taskbar setting registry key {AdvancedSettingsAddress}.");
}
if (taskBarOptions.Count == 0)

View File

@ -46,13 +46,21 @@ namespace DisplayMagicianShared.Windows
public UInt32 SourceId;
public UInt32 TargetId;
public string DevicePath;
//The value we want to set. The value should be relative to the recommended DPI scaling value of source.
// eg. if scaleRel == 1, and recommended value is 175% => we are trying to set 200% scaling for the source
public UInt32 SourceDpiScalingRel;
public override bool Equals(object obj) => obj is DISPLAY_SOURCE other && this.Equals(other);
public bool Equals(DISPLAY_SOURCE other)
=> true;
=> //SourceId.Equals(other.SourceId) && // Source ID needs to be ignored in this case, as windows moves the source ids around :(
TargetId.Equals(other.TargetId) &&
DevicePath.Equals(other.DevicePath) &&
SourceDpiScalingRel.Equals(other.SourceDpiScalingRel);
//=> true;
public override int GetHashCode()
{
return 300;
//return (SourceId, TargetId, DevicePath, SourceDpiScalingRel).GetHashCode(); // Source ID needs to be ignored in this case, as windows moves the source ids around :(
return (TargetId, DevicePath, SourceDpiScalingRel).GetHashCode();
}
public static bool operator ==(DISPLAY_SOURCE lhs, DISPLAY_SOURCE rhs) => lhs.Equals(rhs);
@ -79,24 +87,49 @@ namespace DisplayMagicianShared.Windows
public override bool Equals(object obj) => obj is WINDOWS_DISPLAY_CONFIG other && this.Equals(other);
public bool Equals(WINDOWS_DISPLAY_CONFIG other)
=> IsCloned == other.IsCloned &&
{
if (!(IsCloned == other.IsCloned &&
DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) &&
DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) &&
DisplayHDRStates.SequenceEqual(other.DisplayHDRStates) &&
// The dictionary keys sometimes change after returning from NVIDIA Surround, so we need to only focus on comparing the values of the GDISettings.
// Additionally, we had to disable the DEviceKey from the equality testing within the GDI library itself as that waould also change after changing back from NVIDIA surround
// This still allows us to detect when refresh rates change, which will allow DisplayMagician to detect profile differences.
GdiDisplaySettings.Values.SequenceEqual(other.GdiDisplaySettings.Values) &&
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers);
DisplayIdentifiers.SequenceEqual(other.DisplayIdentifiers)))
{
return false;
}
// Now we need to go through the HDR states comparing vaues, as the order changes if there is a cloned display
if (!WinLibrary.EqualButDifferentOrder<ADVANCED_HDR_INFO_PER_PATH>(DisplayHDRStates, other.DisplayHDRStates))
{
return false;
}
// Now we need to go through the values to make sure they are the same, but ignore the keys (as they change after each reboot!)
for (int i = 0; i < DisplaySources.Count; i++)
{
if (!DisplaySources.ElementAt(i).Value.SequenceEqual(other.DisplaySources.ElementAt(i).Value))
{
return false;
}
}
return true;
}
// NOTE: I have disabled the TaskBar specific matching for now due to errors I cannot fix
// WinLibrary will still track the location of the taskbars, but won't actually set them as the setting of the taskbars doesnt work at the moment.
/*&&
TaskBarLayout.SequenceEqual(other.TaskBarLayout) &&
TaskBarLayout.Values.SequenceEqual(other.TaskBarLayout.Values) &&
TaskBarSettings.Equals(other.TaskBarSettings);*/
public override int GetHashCode()
{
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, IsCloned, DisplayIdentifiers, TaskBarLayout, TaskBarSettings).GetHashCode();
// Temporarily disabled this to make sure that the hashcode generation matched the equality tests.
//return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, IsCloned, DisplayIdentifiers, TaskBarLayout, TaskBarSettings).GetHashCode();
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, IsCloned, DisplayIdentifiers).GetHashCode();
}
public static bool operator ==(WINDOWS_DISPLAY_CONFIG lhs, WINDOWS_DISPLAY_CONFIG rhs) => lhs.Equals(rhs);
@ -114,6 +147,7 @@ namespace DisplayMagicianShared.Windows
private bool _initialised = false;
private WINDOWS_DISPLAY_CONFIG _activeDisplayConfig;
public List<DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY> SkippedColorConnectionTypes;
public List<string> _allConnectedDisplayIdentifiers;
// To detect redundant calls
private bool _disposed = false;
@ -135,7 +169,12 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace("WinLibrary/WinLibrary: Intialising Windows CCD library interface");
_initialised = true;
// Set the DPI awareness for the process this thread is running within so that the DPI calls return the right values at the right times
CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED);
_activeDisplayConfig = GetActiveConfig();
_allConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers();
}
~WinLibrary()
@ -215,47 +254,80 @@ namespace DisplayMagicianShared.Windows
return myDefaultConfig;
}
private void PatchAdapterIDs(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig)
public void PatchWindowsDisplayConfig(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig)
{
Dictionary<ulong, ulong> adapterOldToNewMap = new Dictionary<ulong, ulong>();
Dictionary<ulong, string> currentAdapterMap = GetCurrentAdapterIDs();
Dictionary<ulong, string> currentAdapterMap = GetAllAdapterIDs();
try
{
SharedLogger.logger.Trace("WinLibrary/PatchAdapterIDs: Going through the list of adapters we stored in the config to figure out the old adapterIDs");
SharedLogger.logger.Trace("WinLibrary/PatchWindowsDisplayConfig: Going through the list of adapters we stored in the config to figure out the old adapterIDs");
foreach (KeyValuePair<ulong, string> savedAdapter in savedDisplayConfig.DisplayAdapters)
{
bool adapterMatched = false;
foreach (KeyValuePair<ulong, string> currentAdapter in currentAdapterMap)
{
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Checking if saved adapter {savedAdapter.Key} (AdapterName is {savedAdapter.Value}) is equal to current adapter id {currentAdapter.Key} (AdapterName is {currentAdapter.Value})");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Checking if saved adapter {savedAdapter.Key} (AdapterName is {savedAdapter.Value}) is equal to current adapter id {currentAdapter.Key} (AdapterName is {currentAdapter.Value})");
if (currentAdapter.Value.Equals(savedAdapter.Value))
{
// we have found the new LUID Value for the same adapter
// So we want to store it
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: We found that saved adapter {savedAdapter.Key} has now been assigned adapter id {currentAdapter.Key} (AdapterName is {savedAdapter.Value})");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: We found that saved adapter {savedAdapter.Key} has now been assigned adapter id {currentAdapter.Key} (AdapterName is {savedAdapter.Value})");
adapterOldToNewMap.Add(savedAdapter.Key, currentAdapter.Key);
adapterMatched = true;
}
}
if (!adapterMatched)
{
SharedLogger.logger.Error($"WinLibrary/PatchAdapterIDs: Saved adapter {savedAdapter.Key} (AdapterName is {savedAdapter.Value}) doesn't have a current match! The adapters have changed since the configuration was last saved.");
SharedLogger.logger.Error($"WinLibrary/PatchWindowsDisplayConfig: Saved adapter {savedAdapter.Key} (AdapterName is {savedAdapter.Value}) doesn't have a current match! The adapters have changed since the configuration was last saved.");
}
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the list of adapters we stored in the config to figure out the old adapterIDs");
SharedLogger.logger.Error(ex, "WinLibrary/PatchWindowsDisplayConfig: Exception while going through the list of adapters we stored in the config to figure out the old adapterIDs");
}
ulong newAdapterValue = 0;
ulong oldAdapterValue = 0;
try
{
// Update the DisplayAdapters with the current adapter id
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Going through the display adatpers to update the adapter id");
ulong[] currentKeys = savedDisplayConfig.DisplayAdapters.Keys.ToArray();
var currentLength = savedDisplayConfig.DisplayAdapters.Count;
for (int i = 0; i < currentLength; i++)
{
oldAdapterValue = currentKeys[i];
// Change the Dictionary Key AdapterIDs
if (adapterOldToNewMap.ContainsKey(oldAdapterValue))
{
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[oldAdapterValue];
// Skip if we've already replaced something!
if (!savedDisplayConfig.DisplayAdapters.ContainsKey(newAdapterValue))
{
// Add a new dictionary key with the old value
savedDisplayConfig.DisplayAdapters.Add(newAdapterValue, savedDisplayConfig.DisplayAdapters[oldAdapterValue]);
// Remove the old dictionary key
savedDisplayConfig.DisplayAdapters.Remove(oldAdapterValue);
}
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Updated DisplayAdapter from adapter {oldAdapterValue} to adapter {newAdapterValue} instead.");
}
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchWindowsDisplayConfig: Exception while going through the display adapters update the adapter ids");
}
try
{
// Update the paths with the current adapter id
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display config paths to update the adapter id");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Going through the display config paths to update the adapter id");
for (int i = 0; i < savedDisplayConfig.DisplayConfigPaths.Length; i++)
{
// Change the Path SourceInfo and TargetInfo AdapterIDs
@ -266,14 +338,14 @@ namespace DisplayMagicianShared.Windows
savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayConfigPaths[i].TargetInfo.AdapterId.Value];
savedDisplayConfig.DisplayConfigPaths[i].TargetInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Updated DisplayConfig Path #{i} from adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} to adapter {newAdapterValue} instead.");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Updated DisplayConfig Path #{i} from adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} to adapter {newAdapterValue} instead.");
}
else
{
// if there isn't a matching adapter, then we just pick the first current one and hope that works!
// (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break)
newAdapterValue = currentAdapterMap.First().Key;
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
SharedLogger.logger.Warn($"WinLibrary/PatchWindowsDisplayConfig: Uh Oh. Adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
savedDisplayConfig.DisplayConfigPaths[i].TargetInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
}
@ -281,13 +353,13 @@ namespace DisplayMagicianShared.Windows
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display config paths to update the adapter id");
SharedLogger.logger.Error(ex, "WinLibrary/PatchWindowsDisplayConfig: Exception while going through the display config paths to update the adapter id");
}
try
{
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display config modes to update the adapter id");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Going through the display config modes to update the adapter id");
// Update the modes with the current adapter id
for (int i = 0; i < savedDisplayConfig.DisplayConfigModes.Length; i++)
{
@ -297,27 +369,27 @@ namespace DisplayMagicianShared.Windows
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayConfigModes[i].AdapterId.Value];
savedDisplayConfig.DisplayConfigModes[i].AdapterId = AdapterValueToLUID(newAdapterValue);
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Updated DisplayConfig Mode #{i} from adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} to adapter {newAdapterValue} instead.");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Updated DisplayConfig Mode #{i} from adapter {savedDisplayConfig.DisplayConfigModes[i].AdapterId.Value} to adapter {newAdapterValue} instead.");
}
else
{
// if there isn't a matching adapter, then we just pick the first current one and hope that works!
// (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break)
newAdapterValue = currentAdapterMap.First().Key;
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayConfigModes[i].AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
SharedLogger.logger.Warn($"WinLibrary/PatchWindowsDisplayConfig: Uh Oh. Adapter {savedDisplayConfig.DisplayConfigModes[i].AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
savedDisplayConfig.DisplayConfigModes[i].AdapterId = AdapterValueToLUID(newAdapterValue);
}
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display config modes to update the adapter id");
SharedLogger.logger.Error(ex, "WinLibrary/PatchWindowsDisplayConfig: Exception while going through the display config modes to update the adapter id");
}
try
{
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display config HDR info to update the adapter id");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Going through the display config HDR info to update the adapter id");
if (savedDisplayConfig.DisplayHDRStates.Count > 0)
{
// Update the HDRInfo with the current adapter id
@ -327,7 +399,7 @@ namespace DisplayMagicianShared.Windows
// Change the Mode AdapterID
if (adapterOldToNewMap.ContainsKey(savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value))
{
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: adapterOldToNewMap contains adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} so using the new adapter ID of {newAdapterValue} instead.");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: adapterOldToNewMap contains adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} so using the new adapter ID of {newAdapterValue} instead.");
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value];
hdrInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
@ -335,34 +407,35 @@ namespace DisplayMagicianShared.Windows
hdrInfo.AdvancedColorInfo.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
newAdapterValue = adapterOldToNewMap[savedDisplayConfig.DisplayHDRStates[i].SDRWhiteLevel.Header.AdapterId.Value];
hdrInfo.SDRWhiteLevel.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Updated Display HDR state #{i} from adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} to adapter {newAdapterValue} instead.");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Updated Display HDR state #{i} from adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} to adapter {newAdapterValue} instead.");
}
else
{
// if there isn't a matching adapter, then we just pick the first current one and hope that works!
// (it is highly likely to... its only if the user has multiple graphics cards with some weird config it may break)
newAdapterValue = currentAdapterMap.First().Key;
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
SharedLogger.logger.Warn($"WinLibrary/PatchWindowsDisplayConfig: Uh Oh. Adapter {savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value} didn't have a current match! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
hdrInfo.AdapterId = AdapterValueToLUID(newAdapterValue);
hdrInfo.AdvancedColorInfo.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
hdrInfo.SDRWhiteLevel.Header.AdapterId = AdapterValueToLUID(newAdapterValue);
}
savedDisplayConfig.DisplayHDRStates[i] = hdrInfo;
}
}
}
else
{
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: There are no Display HDR states to update. Skipping.");
SharedLogger.logger.Warn($"WinLibrary/PatchWindowsDisplayConfig: There are no Display HDR states to update. Skipping.");
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display config HDR info to update the adapter id");
SharedLogger.logger.Error(ex, "WinLibrary/PatchWindowsDisplayConfig: Exception while going through the display config HDR info to update the adapter id");
}
try
{
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Going through the display sources list info to update the adapter id");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Going through the display sources list info to update the adapter id");
// Update the DisplaySources with the current adapter id
for (int i = 0; i < savedDisplayConfig.DisplaySources.Count; i++)
{
@ -378,7 +451,7 @@ namespace DisplayMagicianShared.Windows
// We get here if there is a matching adapter
newAdapterValue = adapterOldToNewMap[ds.AdapterId.Value];
ds.AdapterId = AdapterValueToLUID(newAdapterValue);
SharedLogger.logger.Trace($"WinLibrary/PatchAdapterIDs: Updated DisplaySource #{i} from adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} to adapter {newAdapterValue} instead.");
SharedLogger.logger.Trace($"WinLibrary/PatchWindowsDisplayConfig: Updated DisplaySource #{i} from adapter {savedDisplayConfig.DisplayConfigPaths[i].SourceInfo.AdapterId.Value} to adapter {newAdapterValue} instead.");
}
else
{
@ -388,15 +461,16 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Warn($"WinLibrary/PatchAdapterIDs: Uh Oh. Adapter {savedDisplayConfig.DisplayHDRStates[i].AdapterId.Value} didn't have a current match in Display Sources! It's possible the adapter was swapped or disabled. Attempting to use adapter {newAdapterValue} instead.");
ds.AdapterId = AdapterValueToLUID(newAdapterValue);
}
dsList[j] = ds;
}
}
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, "WinLibrary/PatchAdapterIDs: Exception while going through the display sources list info to update the adapter id");
SharedLogger.logger.Error(ex, "WinLibrary/PatchWindowsDisplayConfig: Exception while going through the display sources list info to update the adapter id");
}
}
public bool UpdateActiveConfig()
@ -405,6 +479,7 @@ namespace DisplayMagicianShared.Windows
try
{
_activeDisplayConfig = GetActiveConfig();
_allConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers();
}
catch (Exception ex)
{
@ -418,12 +493,11 @@ namespace DisplayMagicianShared.Windows
public WINDOWS_DISPLAY_CONFIG GetActiveConfig()
{
SharedLogger.logger.Trace($"WinLibrary/GetActiveConfig: Getting the currently active config");
// We want to include head mounted devices, inform windows we're virtual mode aware
// We'll leave virtual refresh rate aware until we can reliably detect Windows 11 versions.
return GetWindowsDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS | QDC.QDC_INCLUDE_HMD);
return GetWindowsDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS);
}
private WINDOWS_DISPLAY_CONFIG GetWindowsDisplayConfig(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS | QDC.QDC_INCLUDE_HMD)
private WINDOWS_DISPLAY_CONFIG GetWindowsDisplayConfig(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS)
{
// Prepare the empty windows display config
@ -494,9 +568,13 @@ namespace DisplayMagicianShared.Windows
}
}
// Now cycle through the paths and grab the HDR state information
// Now cycle through the paths and grab the state information we need
// and map the adapter name to adapter id
// and populate the display source information
// Set the DPI awareness for the process this thread is running within so that the DPI calls return the right values
CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
List<uint> targetPathIdsToChange = new List<uint>();
List<uint> targetModeIdsToChange = new List<uint>();
List<uint> targetIdsFound = new List<uint>();
@ -504,6 +582,19 @@ namespace DisplayMagicianShared.Windows
bool isClonedProfile = false;
for (int i = 0; i < paths.Length; i++)
{
if (selector == QDC.QDC_ONLY_ACTIVE_PATHS && paths[i].TargetInfo.TargetInUse == false)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Skipping display target {paths[i].TargetInfo.Id} as we only want displays currently in use");
continue;
}
if (selector == QDC.QDC_ALL_PATHS && paths[i].TargetInfo.TargetAvailable == false)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Skipping display target {paths[i].TargetInfo.Id} as we want all available displays and this one isn't available");
continue;
}
//bool gotSourceDeviceName = false;
//bool gotAdapterName = false;
bool gotAdvancedColorInfo = false;
@ -522,6 +613,27 @@ namespace DisplayMagicianShared.Windows
// Track if this display is a cloned path
bool isClonedPath = false;
// Get the Windows Scaling DPI per display
UInt32 sourceDpiScalingRel = 0;
DISPLAYCONFIG_SOURCE_DPI_SCALE_GET displayScalingInfo = new DISPLAYCONFIG_SOURCE_DPI_SCALE_GET();
displayScalingInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_DPI_SCALE;
displayScalingInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DPI_SCALE_GET>(); ;
displayScalingInfo.Header.AdapterId = paths[i].SourceInfo.AdapterId;
displayScalingInfo.Header.Id = paths[i].SourceInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref displayScalingInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Windows DPI scasling value for source {paths[i].SourceInfo.Id} is {displayScalingInfo.CurrrentScaleRel}.");
sourceDpiScalingRel = displayScalingInfo.CurrrentScaleRel;
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get Windows DPI Scaling value for display {paths[i].TargetInfo.Id}.");
}
// get display source name
var sourceInfo = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
sourceInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
@ -531,6 +643,7 @@ namespace DisplayMagicianShared.Windows
err = CCDImport.DisplayConfigGetDeviceInfo(ref sourceInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
//gotSourceDeviceName = true;
// Store it for later
if (windowsDisplayConfig.DisplaySources.ContainsKey(sourceInfo.ViewGdiDeviceName))
@ -540,6 +653,7 @@ namespace DisplayMagicianShared.Windows
ds.AdapterId = paths[i].SourceInfo.AdapterId;
ds.SourceId = paths[i].SourceInfo.Id;
ds.TargetId = paths[i].TargetInfo.Id;
ds.SourceDpiScalingRel = sourceDpiScalingRel;
windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(ds);
isClonedPath = true;
isClonedProfile = true;
@ -553,6 +667,7 @@ namespace DisplayMagicianShared.Windows
ds.AdapterId = paths[i].SourceInfo.AdapterId;
ds.SourceId = paths[i].SourceInfo.Id;
ds.TargetId = paths[i].TargetInfo.Id;
ds.SourceDpiScalingRel = sourceDpiScalingRel;
sources.Add(ds);
windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, sources);
}
@ -575,7 +690,7 @@ namespace DisplayMagicianShared.Windows
paths[i].TargetInfo.ModeInfoIdx = CCDImport.DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
}
// Get adapter ID for later
/*// Get adapter ID for later
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get adapter name for adapter {paths[i].TargetInfo.AdapterId.Value}.");
if (!windowsDisplayConfig.DisplayAdapters.ContainsKey(paths[i].TargetInfo.AdapterId.Value))
{
@ -602,7 +717,8 @@ namespace DisplayMagicianShared.Windows
// We already have the adapter name
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: We already have the adapter name {windowsDisplayConfig.DisplayAdapters[paths[i].TargetInfo.AdapterId.Value]} for adapter {paths[i].TargetInfo.AdapterId.Value} so skipping storing it.");
//gotAdapterName = true;
}
}*/
// Get advanced color info
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get advanced color info for display {paths[i].TargetInfo.Id}.");
@ -680,6 +796,12 @@ namespace DisplayMagicianShared.Windows
}
}
// Set the DPI awareness for the process this thread is running within so that the DPI calls return the right values
CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED);
// Get all the DisplayAdapters currently in the system
// This will be used for windows to translate the adapter details beween reboots
windowsDisplayConfig.DisplayAdapters = GetAllAdapterIDs();
// Go through the list of physicalTargetIdsAvailable
// ignore the ones that were found
@ -781,10 +903,34 @@ namespace DisplayMagicianShared.Windows
Dictionary<string, TaskBarLayout> taskBarStuckRectangles = new Dictionary<string, TaskBarLayout>();
// Now attempt to get the windows taskbar location for each display
taskBarStuckRectangles = TaskBarLayout.GetAllCurrentTaskBarLayouts(windowsDisplayConfig.DisplaySources);
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get the Windows Taskbar Layouts.");
bool retryNeeded = false;
taskBarStuckRectangles = TaskBarLayout.GetAllCurrentTaskBarLayouts(windowsDisplayConfig.DisplaySources, out retryNeeded);
// Check whether Windows has actually added the registry keys that outline the taskbar position
if (retryNeeded)
{
// We wait until the reg key is populated
for (int count = 1; count <= 4; count++)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: We were unable to get all the Windows Taskbar Layouts! So we need to try again. Attempt {count} of 4.");
// Wait 5 seconds
System.Threading.Thread.Sleep(5000);
// then try again
retryNeeded = false;
taskBarStuckRectangles = TaskBarLayout.GetAllCurrentTaskBarLayouts(windowsDisplayConfig.DisplaySources, out retryNeeded);
if (!retryNeeded)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: We successfully got the Windows Taskbar Layouts on attempt {count}! So we can stop trying to get them");
break;
}
}
}
// Now we try to get the taskbar settings too
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get the Windows Taskbar settings.");
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get the Windows Taskbar Settings.");
TaskBarSettings taskBarSettings = TaskBarSettings.GetCurrent();
// Store the active paths and modes in our display config object
@ -1258,7 +1404,7 @@ namespace DisplayMagicianShared.Windows
// Get the all possible windows display configs
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Generating a list of all the current display configs");
WINDOWS_DISPLAY_CONFIG allWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD);
WINDOWS_DISPLAY_CONFIG allWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ALL_PATHS);
if (displayConfig.IsCloned)
{
@ -1271,7 +1417,7 @@ namespace DisplayMagicianShared.Windows
// Now we go through the Paths to update the LUIDs as per Soroush's suggestion
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Patching the adapter IDs to make the saved config valid");
PatchAdapterIDs(ref displayConfig);
PatchWindowsDisplayConfig(ref displayConfig);
uint myPathsCount = (uint)displayConfig.DisplayConfigPaths.Length;
uint myModesCount = (uint)displayConfig.DisplayConfigModes.Length;
@ -1385,9 +1531,34 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: SUCCESS! The display configuration has been successfully applied");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Waiting 0.1 second to let the display change take place before adjusting the Windows CCD HDR settings");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Waiting 0.1 second to let the display change take place before adjusting the Windows CCD Source DPI scaling settings");
System.Threading.Thread.Sleep(100);
SharedLogger.logger.Trace($"WinLibrary/SetWindowsDisplayConfig: Attempting to set Windows DPI Scaling setting for display sources.");
CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
foreach (var displaySourceEntry in displayConfig.DisplaySources)
{
// We only need to set the source on the first display source
// Set the Windows Scaling DPI per source
DISPLAYCONFIG_SOURCE_DPI_SCALE_SET displayScalingInfo = new DISPLAYCONFIG_SOURCE_DPI_SCALE_SET();
displayScalingInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_DPI_SCALE;
displayScalingInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DPI_SCALE_SET>(); ;
displayScalingInfo.Header.AdapterId = displaySourceEntry.Value[0].AdapterId;
displayScalingInfo.Header.Id = displaySourceEntry.Value[0].SourceId;
displayScalingInfo.ScaleRel = displaySourceEntry.Value[0].SourceDpiScalingRel;
err = CCDImport.DisplayConfigSetDeviceInfo(ref displayScalingInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetWindowsDisplayConfig: Setting DPI value for source {displaySourceEntry.Value[0].SourceId} to {displayScalingInfo.ScaleRel}.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/SetWindowsDisplayConfig: WARNING - Unable to set DPI value for source {displaySourceEntry.Value[0].SourceId} to {displayScalingInfo.ScaleRel}.");
}
}
CCDImport.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED);
// NOTE: There is currently no way within Windows CCD API to set the HDR settings to any particular setting
// This code will only turn on the HDR setting.
foreach (ADVANCED_HDR_INFO_PER_PATH myHDRstate in displayConfig.DisplayHDRStates)
@ -1444,7 +1615,8 @@ namespace DisplayMagicianShared.Windows
// Apply the previously saved display settings to the new displays (match them up)
// NOTE: This may be the only mode needed once it's completed.
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Attempting to change Display Device settings through GDI API using ");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Attempting to change Display Device settings through GDI API using ChangeDisplaySettingsEx");
bool appliedGdiDisplaySettings = false;
foreach (var gdiDisplay in displayConfig.GdiDisplaySettings)
{
@ -1463,58 +1635,162 @@ namespace DisplayMagicianShared.Windows
// Sets the greyscale and interlaced settings
currentDeviceSetting.DeviceMode.DisplayFlags = displayDeviceSettings.DeviceMode.DisplayFlags;
CHANGE_DISPLAY_RESULTS result = GDIImport.ChangeDisplaySettingsEx(currentDeviceSetting.Device.DeviceName, ref currentDeviceSetting.DeviceMode, IntPtr.Zero, CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_UPDATEREGISTRY, IntPtr.Zero);
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Testing whether the GDI Device Mode will work for display {displayDeviceKey}.");
// First of all check that setting the GDI mode will work
CHANGE_DISPLAY_RESULTS result = GDIImport.ChangeDisplaySettingsEx(currentDeviceSetting.Device.DeviceName, ref currentDeviceSetting.DeviceMode, IntPtr.Zero, CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_TEST, IntPtr.Zero);
if (result == CHANGE_DISPLAY_RESULTS.Successful)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully changed display {displayDeviceKey} to use the new mode!");
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Success. The GDI Device Mode will work for display {displayDeviceKey}.");
// Set the
if (currentDeviceSetting.IsPrimary)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Actually going to try to set the GDI Device Mode for display {displayDeviceKey} now (primary display).");
result = GDIImport.ChangeDisplaySettingsEx(currentDeviceSetting.Device.DeviceName, ref currentDeviceSetting.DeviceMode, IntPtr.Zero, (CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_SET_PRIMARY | CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_UPDATEREGISTRY | CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_NORESET), IntPtr.Zero);
}
else
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Actually going to try to set the GDI Device Mode for display {displayDeviceKey} now (secondary display).");
result = GDIImport.ChangeDisplaySettingsEx(currentDeviceSetting.Device.DeviceName, ref currentDeviceSetting.DeviceMode, IntPtr.Zero, (CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_UPDATEREGISTRY | CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_NORESET), IntPtr.Zero);
}
if (result == CHANGE_DISPLAY_RESULTS.Successful)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully changed display {displayDeviceKey} to use the new mode!");
appliedGdiDisplaySettings = true;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadDualView)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The settings change was unsuccessful because the system is DualView capable. Display {displayDeviceKey} not updated to new mode.");
//return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadFlags)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: An invalid set of flags was passed in. Display {displayDeviceKey} not updated to use the new mode.");
//return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadMode)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The graphics mode is not supported. Display {displayDeviceKey} not updated to use the new mode.");
//return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadParam)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: An invalid parameter was passed in. This can include an invalid flag or combination of flags. Display {displayDeviceKey} not updated to use the new mode.");
//return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.Failed)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The display driver failed to apply the specified graphics mode. Display {displayDeviceKey} not updated to use the new mode.");
//return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.NotUpdated)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Unable to write new settings to the registry. Display {displayDeviceKey} not updated to use the new mode.");
//return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.Restart)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The computer must be restarted for the graphics mode to work. Display {displayDeviceKey} not updated to use the new mode.");
//return false;
}
else
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Unknown error while trying to change Display {displayDeviceKey} to use the new mode.");
return false;
}
}
else if (result == CHANGE_DISPLAY_RESULTS.BadDualView)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The settings change was unsuccessful because the system is DualView capable. Display {displayDeviceKey} not updated to new mode.");
return false;
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because the system is DualView capable. Skipping setting Display {displayDeviceKey}.");
}
else if (result == CHANGE_DISPLAY_RESULTS.BadFlags)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: An invalid set of flags was passed in. Display {displayDeviceKey} not updated to use the new mode.");
return false;
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because an invalid set of flags was passed in. Display {displayDeviceKey} not updated to use the new mode.");
}
else if (result == CHANGE_DISPLAY_RESULTS.BadMode)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The graphics mode is not supported. Display {displayDeviceKey} not updated to use the new mode.");
return false;
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because the graphics mode is not supported. Display {displayDeviceKey} not updated to use the new mode.");
}
else if (result == CHANGE_DISPLAY_RESULTS.BadParam)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: An invalid parameter was passed in. This can include an invalid flag or combination of flags. Display {displayDeviceKey} not updated to use the new mode.");
return false;
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because an invalid parameter was passed in. This can include an invalid flag or combination of flags. Display {displayDeviceKey} not updated to use the new mode.");
}
else if (result == CHANGE_DISPLAY_RESULTS.Failed)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The display driver failed to apply the specified graphics mode. Display {displayDeviceKey} not updated to use the new mode.");
return false;
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because the display driver failed to apply the specified graphics mode. Display {displayDeviceKey} not updated to use the new mode.");
}
else if (result == CHANGE_DISPLAY_RESULTS.NotUpdated)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Unable to write new settings to the registry. Display {displayDeviceKey} not updated to use the new mode.");
return false;
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because we're unable to write new settings to the registry. Display {displayDeviceKey} not updated to use the new mode.");
}
else if (result == CHANGE_DISPLAY_RESULTS.Restart)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The computer must be restarted for the graphics mode to work. Display {displayDeviceKey} not updated to use the new mode.");
return false;
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because the computer must be restarted for the graphics mode to work. Display {displayDeviceKey} not updated to use the new mode.");
}
else
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Display {displayDeviceKey} not updated to use the new mode.");
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The GDI mode change would be unsuccessful because there was an unknown error testing if Display {displayDeviceKey} could use the new mode.");
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Display {displayDeviceKey} is not currently in use, so cannot set it!");
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Display {displayDeviceKey} is not currently in use, so cannot set it!");
}
}
// If we have applied GDI settings for multiple displays, then we need to run ChangeDisplaySettingsEx one more time
// see https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-changedisplaysettingsexa
if (appliedGdiDisplaySettings)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Other display settings were changed, so applying all the changes now.");
CHANGE_DISPLAY_RESULTS result = GDIImport.ChangeDisplaySettingsEx(null, IntPtr.Zero, IntPtr.Zero, CHANGE_DISPLAY_SETTINGS_FLAGS.CDS_NONE, IntPtr.Zero);
if (result == CHANGE_DISPLAY_RESULTS.Successful)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully applied the new GDI modes!");
}
else if (result == CHANGE_DISPLAY_RESULTS.BadDualView)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Couldn't apply the new GDI modes because the system is DualView capable.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadFlags)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Couldn't apply the new GDI modes because an invalid set of flags was passed in.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadMode)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Couldn't apply the new GDI modes because the graphics mode is not supported.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.BadParam)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Couldn't apply the new GDI modes because an invalid parameter was passed in. This can include an invalid flag or combination of flags.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.Failed)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Couldn't apply the new GDI modes because the display driver failed to apply the specified graphics mode.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.NotUpdated)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Couldn't apply the new GDI modes because unable to write new settings to the registry.");
return false;
}
else if (result == CHANGE_DISPLAY_RESULTS.Restart)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Couldn't apply the new GDI modes because the computer must be restarted for the graphics mode to work.");
return false;
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Unknown error while trying to apply the new GDI modes.");
return false;
}
}
// NOTE: I have disabled the TaskBar setting logic for now due to errors I cannot fix in this code.
// WinLibrary will still track the location of the taskbars, but won't actually set them as the setting of the taskbars doesnt work at the moment.
@ -1627,7 +1903,7 @@ namespace DisplayMagicianShared.Windows
public bool IsValidConfig(WINDOWS_DISPLAY_CONFIG displayConfig)
{
// Get the current windows display configs
WINDOWS_DISPLAY_CONFIG allWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD);
WINDOWS_DISPLAY_CONFIG allWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ALL_PATHS);
SharedLogger.logger.Trace("WinLibrary/PatchAdapterIDs: Going through the list of adapters we stored in the config to make sure they still exist");
// Firstly check that the Adapter Names are still currently available (i.e. the adapter hasn't been replaced).
@ -1645,7 +1921,7 @@ namespace DisplayMagicianShared.Windows
// Now we go through the Paths to update the LUIDs as per Soroush's suggestion
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Attemptong to patch the saved display configuration's adapter IDs so that it will still work (these change at each boot)");
PatchAdapterIDs(ref displayConfig);
PatchWindowsDisplayConfig(ref displayConfig);
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Testing whether the display configuration is valid ");
// Test whether a specified display configuration is supported on the computer
@ -1670,12 +1946,9 @@ namespace DisplayMagicianShared.Windows
// We want to check the Windows Display profile can be used now
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Testing whether the Windows display configuration is possible to be used now");
// check what the currently available displays are (include the ones not active)
List<string> currentAllIds = GetAllConnectedDisplayIdentifiers();
// CHeck that we have all the displayConfig DisplayIdentifiers we need available now
//if (currentAllIds.Intersect(displayConfig.DisplayIdentifiers).Count() == displayConfig.DisplayIdentifiers.Count)
if (displayConfig.DisplayIdentifiers.All(value => currentAllIds.Contains(value)))
if (displayConfig.DisplayIdentifiers.All(value => _allConnectedDisplayIdentifiers.Contains(value)))
{
SharedLogger.logger.Trace($"WinLibrary/IsPossibleConfig: Success! THe Windows display configuration is possible to be used now");
return true;
@ -1691,16 +1964,18 @@ namespace DisplayMagicianShared.Windows
public List<string> GetCurrentDisplayIdentifiers()
{
SharedLogger.logger.Trace($"WinLibrary/GetCurrentDisplayIdentifiers: Getting the current display identifiers for the displays in use now");
return GetSomeDisplayIdentifiers(QDC.QDC_ONLY_ACTIVE_PATHS | QDC.QDC_INCLUDE_HMD);
return GetSomeDisplayIdentifiers(QDC.QDC_ONLY_ACTIVE_PATHS);
}
public List<string> GetAllConnectedDisplayIdentifiers()
{
SharedLogger.logger.Trace($"WinLibrary/GetAllConnectedDisplayIdentifiers: Getting all the display identifiers that can possibly be used");
return GetSomeDisplayIdentifiers(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD);
_allConnectedDisplayIdentifiers = GetSomeDisplayIdentifiers(QDC.QDC_ALL_PATHS);
return _allConnectedDisplayIdentifiers;
}
private List<string> GetSomeDisplayIdentifiers(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS | QDC.QDC_INCLUDE_HMD)
private List<string> GetSomeDisplayIdentifiers(QDC selector = QDC.QDC_ONLY_ACTIVE_PATHS)
{
SharedLogger.logger.Debug($"WinLibrary/GetCurrentDisplayIdentifiers: Generating the unique Display Identifiers for the currently active configuration");
@ -1887,7 +2162,7 @@ namespace DisplayMagicianShared.Windows
return displayIdentifiers;
}
public List<string> GetCurrentPCIVideoCardVendors()
public List<string> GetAllPCIVideoCardVendors()
{
SharedLogger.logger.Trace($"WinLibrary/GetCurrentPCIVideoCardVendors: Getting the current PCI vendor ids for the videocards reported to Windows");
List<string> videoCardVendorIds = new List<string>();
@ -1897,7 +2172,7 @@ namespace DisplayMagicianShared.Windows
// Get the size of the largest Active Paths and Modes arrays
int pathCount = 0;
int modeCount = 0;
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD, out pathCount, out modeCount);
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ALL_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
@ -1907,7 +2182,7 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Getting the current Display Config path and mode arrays");
var paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
var modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Warn($"WinLibrary/GetCurrentPCIVideoCardVendors: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
@ -1923,7 +2198,7 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace($"WinLibrary/GetSomeDisplayIdentifiers: Getting the current Display Config path and mode arrays");
paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentPCIVideoCardVendors: ERROR - The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
@ -2038,56 +2313,56 @@ namespace DisplayMagicianShared.Windows
}
public Dictionary<ulong, string> GetCurrentAdapterIDs()
public Dictionary<ulong, string> GetAllAdapterIDs()
{
SharedLogger.logger.Trace($"WinLibrary/GetCurrentAdapterIDs: Getting the current adapter ids for the videocards Windows knows about");
SharedLogger.logger.Trace($"WinLibrary/GetAllAdapterIDs: Getting the current adapter ids for the videocards Windows knows about");
Dictionary<ulong, string> currentAdapterMap = new Dictionary<ulong, string>();
SharedLogger.logger.Trace($"WinLibrary/GetCurrentAdapterIDs: Testing whether the display configuration is valid (allowing tweaks).");
SharedLogger.logger.Trace($"WinLibrary/GetAllAdapterIDs: Testing whether the display configuration is valid (allowing tweaks).");
// Get the size of the largest All Paths and Modes arrays
int pathCount = 0;
int modeCount = 0;
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD, out pathCount, out modeCount);
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ALL_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentAdapterIDs: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
throw new WinLibraryException($"GetCurrentAdapterIDs returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
SharedLogger.logger.Error($"WinLibrary/GetAllAdapterIDs: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
throw new WinLibraryException($"GetAllAdapterIDs returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
}
SharedLogger.logger.Trace($"WinLibrary/GetCurrentAdapterIDs: Getting the current Display Config path and mode arrays");
SharedLogger.logger.Trace($"WinLibrary/GetAllAdapterIDs: Getting the current Display Config path and mode arrays");
var paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
var modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Warn($"WinLibrary/GetCurrentAdapterIDs: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
SharedLogger.logger.Trace($"WinLibrary/GetCurrentAdapterIDs: Getting the size of the largest Active Paths and Modes arrays");
SharedLogger.logger.Warn($"WinLibrary/GetAllAdapterIDs: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
SharedLogger.logger.Trace($"WinLibrary/GetAllAdapterIDs: Getting the size of the largest Active Paths and Modes arrays");
// Screen changed in between GetDisplayConfigBufferSizes and QueryDisplayConfig, so we need to get buffer sizes again
// as per https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-querydisplayconfig
err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ALL_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentAdapterIDs: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
SharedLogger.logger.Error($"WinLibrary/GetAllAdapterIDs: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes again");
}
SharedLogger.logger.Trace($"WinLibrary/GetCurrentAdapterIDs: Getting the current Display Config path and mode arrays");
SharedLogger.logger.Trace($"WinLibrary/GetAllAdapterIDs: Getting the current Display Config path and mode arrays");
paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS | QDC.QDC_INCLUDE_HMD, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
err = CCDImport.QueryDisplayConfig(QDC.QDC_ALL_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentAdapterIDs: ERROR - The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
SharedLogger.logger.Error($"WinLibrary/GetAllAdapterIDs: ERROR - The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
throw new WinLibraryException($"The displays were still modified between GetDisplayConfigBufferSizes and QueryDisplayConfig, even though we tried twice. Something is wrong.");
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentAdapterIDs: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again");
SharedLogger.logger.Error($"WinLibrary/GetAllAdapterIDs: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays again.");
}
}
else if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/GetCurrentAdapterIDs: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays");
SharedLogger.logger.Error($"WinLibrary/GetAllAdapterIDs: ERROR - QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays");
throw new WinLibraryException($"QueryDisplayConfig returned WIN32STATUS {err} when trying to query all available displays.");
}
@ -2096,7 +2371,7 @@ namespace DisplayMagicianShared.Windows
if (path.TargetInfo.TargetAvailable == false)
{
// We want to skip this one cause it's not valid
SharedLogger.logger.Trace($"WinLibrary/GetCurrentAdapterIDs: Skipping path due to TargetAvailable not existing in display #{path.TargetInfo.Id}");
SharedLogger.logger.Trace($"WinLibrary/GetAllAdapterIDs: Skipping path due to TargetAvailable not existing in display #{path.TargetInfo.Id}");
continue;
}
@ -2109,12 +2384,12 @@ namespace DisplayMagicianShared.Windows
err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/GetCurrentAdapterIDs: Successfully got the display name info from {path.TargetInfo.Id}.");
SharedLogger.logger.Trace($"WinLibrary/GetAllAdapterIDs: Successfully got the display name info from {path.TargetInfo.Id}.");
currentAdapterMap[path.TargetInfo.AdapterId.Value] = adapterInfo.AdapterDevicePath;
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetCurrentAdapterIDs: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{path.TargetInfo.Id}");
SharedLogger.logger.Warn($"WinLibrary/GetAllAdapterIDs: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the target info for display #{path.TargetInfo.Id}");
}
}
@ -2253,6 +2528,53 @@ namespace DisplayMagicianShared.Windows
Utils.SendMessage(windowHandle, Utils.WM_MOUSEMOVE, 0, (y << 16) + x);
}
public static bool EqualButDifferentOrder<T>(IList<T> list1, IList<T> list2)
{
if (list1.Count != list2.Count)
{
return false;
}
// Now we need to go through the list1, checking that all it's items are in list2
foreach (T item1 in list1)
{
bool foundIt = false;
foreach (T item2 in list2)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
// Now we need to go through the list2, checking that all it's items are in list1
foreach (T item2 in list2)
{
bool foundIt = false;
foreach (T item1 in list1)
{
if (item1.Equals(item2))
{
foundIt = true;
break;
}
}
if (!foundIt)
{
return false;
}
}
return true;
}
}
[global::System.Serializable]

View File

@ -21,7 +21,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
</ItemGroup>
<ItemGroup>

View File

@ -50,7 +50,8 @@ DisplayMagician lets you set up the following information for each game or appli
* Or maybe just create a Shortcut that permanently changes to a different Display Profile! The options are endless.
* Also comes with a Shell Extension that allows you to change to a different Display Profile by right-clicking on the desktop background!
* Supports NVIDIA Surround setups, AMD Eyefinity setups and standard Windows multi-desktop views.
* Supports USB display devices and works with SuperDisplay (Android Tablets as a display)
* Supports USB display devices, wireless display devices and works with SuperDisplay and Spacedesk (Android Tablets as a display)
* Supports Windows DPI Scaling, setting Refresh Rates and HDR
* Supports cloned displays, extended displays, NVIDIA Surround with additional displays, and nearly anything else you can throw at it!
## Planned features
@ -61,7 +62,7 @@ DisplayMagician lets you set up the following information for each game or appli
## Requirements
* DisplayMagician only supports 64-bit Windows 10/11
* DisplayMagician only supports 64-bit Windows 10 (version 1809 or later) and 64-bit Windows 11
* NVIDIA Surround support requires NVIDIA Game Ready driver to be installed
* AMD Eyefinity support requires AMD Radeon™ Software Adrenalin 2020 Edition 21.2.1 or later to be installed
@ -89,6 +90,8 @@ I am doing this work to scratch a programming itch I've had for a while. It's pr
### Initial Setup:
If you prefer to see a video on how to setup DisplayMagician, check out [this awesome 'How to Setup DisplayMagician' video from JDM PC Gaming](https://www.youtube.com/watch?v=xqguYAMNHLM). Otherwise, perform the steps shown below:
1. Install 'DisplayMagician'
2. Run 'DisplayMagician', and click on the 'Display Profiles' button
* Use 'Windows Display Settings', 'NVIDIA Control Panel' or 'AMD Radeon Setup' to configure your display(s) exactly as you would like them
@ -142,9 +145,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
## Credits
Thanks for the work and the time that all of our contributors put into making this a better project. Following is a short list, containing the names of these amazing people:
Im so very thankful for the help of the following people, who make DisplayMagician possible:
* Sean at SimCraft (Thank you so much for your generous donation!)
* Domenic (Thanks for the solid month of troubleshooting help and the monthly sponsorship!)
* RBZL (thanks for the monthly donation)
* Frcooper (thanks for the monthly donation)
Also big thanks to:
* Dogy007 (Thanks for the sponsorship)
* Ceramed (Thank you for your donation and your help troubleshooting SuperDisplay and SpaceDesk screens)
* Bjorg (Thank you for your donation)
* MichaelDWheeler (Thank you for your donation)
* Frank (Thank you for your donation)
* Dan Clark (Thank you for your donation)
* Fonzmonster (Thank you for your donation)
* Logozo (Thank you for your donation)
* 5th (Thank you for your donation)
* Patrickdiezi (Thank you for your donation)
* Fifowole (Thank you for your donation)
* Brett Horton (Thank you for your donation)
* Patrickdiezi (Thank you for your donation)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 766 KiB

After

Width:  |  Height:  |  Size: 766 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,795 +0,0 @@
using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using DisplayMagician.Resources;
using DisplayMagicianShared;
using DisplayMagicianShared.Windows;
using Manina.Windows.Forms;
using System.Drawing;
using NHotkey.WindowsForms;
using NHotkey;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace DisplayMagician.UIForms
{
internal partial class DisplayProfileForm : Form
{
private ProfileItem _selectedProfile;
//private List<ProfileItem> _savedProfiles = new List<ProfileItem>();
private string _saveOrRenameMode = "save";
//private static bool _inDialog = false;
private static ProfileItem _profileToLoad = null;
private ProfileAdaptor _profileAdaptor = new ProfileAdaptor();
//public static Dictionary<string, bool> profileValidity = new Dictionary<string, bool>();
public Task _monitorTaskBarRegKeysForChangesTask = null;
//public bool _monitorTaskBarRegKeysForChanges = false;
//private readonly object _monitorTaskBarRegKeysForChangesLock = new object();
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public DisplayProfileForm()
{
InitializeComponent();
this.AcceptButton = this.btn_save_or_rename;
ilv_saved_profiles.MultiSelect = false;
ilv_saved_profiles.ThumbnailSize = new Size(100, 100);
ilv_saved_profiles.AllowDrag = false;
ilv_saved_profiles.AllowDrop = false;
ilv_saved_profiles.SetRenderer(new ProfileILVRenderer());
}
public DisplayProfileForm(ProfileItem profileToLoad) : this()
{
_profileToLoad = profileToLoad;
}
private void Apply_Click(object sender, EventArgs e)
{
if (_selectedProfile == null)
return;
if (!_selectedProfile.IsPossible)
{
MessageBox.Show(this, Language.This_profile_is_currently_impossible_to_apply,
Language.Apply_Profile,
MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
// Apply the Profile
//if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Successful)
ApplyProfileResult result = Program.ApplyProfileTask(_selectedProfile);
if (result == ApplyProfileResult.Successful)
{
logger.Trace($"DisplayProfileForm/Apply_Click: The Profile {_selectedProfile.Name} was successfully applied. Waiting 0.5 sec for the display to settle after the change.");
System.Threading.Thread.Sleep(500);
logger.Trace($"DisplayProfileForm/Apply_Click: Changing the selected profile in the imagelistview to Profile {_selectedProfile.Name}.");
ChangeSelectedProfile(_selectedProfile);
MainForm myMainForm = Program.AppMainForm;
myMainForm.UpdateNotifyIconText($"DisplayMagician ({ProfileRepository.CurrentProfile.Name})");
}
else if (result == ApplyProfileResult.Cancelled)
{
logger.Warn($"DisplayProfileForm/Apply_Click: The user cancelled changing to Profile {_selectedProfile.Name}.");
}
else
{
logger.Error($"DisplayProfileForm/Apply_Click: Error applying the Profile {_selectedProfile.Name}. Unable to change the display layout.");
}
// Bring the window back to the front
Visible = true;
Activate();
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
}
private void Exit_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
this.Close();
}
private void Delete_Click(object sender, EventArgs e)
{
if (_selectedProfile == null)
return;
if (MessageBox.Show($"Are you sure you want to delete the '{_selectedProfile.Name}' Display Profile?", $"Delete '{_selectedProfile.Name}' Display Profile?", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.No)
return;
// remove the profile from the imagelistview
int currentIlvIndex = ilv_saved_profiles.SelectedItems[0].Index;
ilv_saved_profiles.Items.RemoveAt(currentIlvIndex);
// Remove the hotkey if it is enabled for this profile
if (_selectedProfile.Hotkey != Keys.None)
{
// Remove the Hotkey if it needs to be removed
HotkeyManager.Current.Remove(_selectedProfile.UUID);
}
// Remove the Profile
ProfileRepository.RemoveProfile(_selectedProfile);
_selectedProfile = null;
// If the imageview isn't empty
if (ilv_saved_profiles.Items.Count > 0)
{
// set the new selected profile as the next one in the imagelistview
// or the new end one if we deleted the last one before
int ilvItemToSelect = currentIlvIndex;
if (ilv_saved_profiles.Items.Count < currentIlvIndex + 1)
ilvItemToSelect = ilv_saved_profiles.Items.Count - 1;
// Set the nearest profile image as selected
ilv_saved_profiles.Items[ilvItemToSelect].Selected = true;
// select the
foreach (ProfileItem newSelectedProfile in ProfileRepository.AllProfiles)
{
if (newSelectedProfile.UUID.Equals(ilv_saved_profiles.Items[ilvItemToSelect].EquipmentModel))
{
ChangeSelectedProfile(newSelectedProfile);
}
}
}
else
{
// We now only have an unsaved current profile, and no saved ones
// So we need to change the mode
ChangeSelectedProfile(ProfileRepository.CurrentProfile);
}
// As this may impact which game shortcuts are now usable, also force a refresh of the game shortcuts validity
ShortcutRepository.IsValidRefresh();
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
}
private void Save_Click(object sender, EventArgs e)
{
//DialogResult = DialogResult.None;
// Only do something if there is a shortcut selected
if (_selectedProfile != null)
{
// if shortcut is not valid then ask if the user
// really wants to save it to desktop
if (!_selectedProfile.IsPossible)
{
// We ask the user of they still want to save the desktop shortcut
if (MessageBox.Show($"The '{_selectedProfile.Name}' Desktop Profile isn't possible to use right now so a desktop shortcut wouldn't work until your Displays are changed to match the profile. Has your hardware or screen layout changed from when the Display Profile was made? Do you still want to save the desktop shortcut?", $"Still save the '{_selectedProfile.Name}' Display Profile?", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.No)
return;
}
try
{
// Set the profile save folder to the Desktop as that's where people will want it most likely
dialog_save.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// Try to set up some sensible suggestions for the profile name
dialog_save.FileName = _selectedProfile.Name;
// Show the Save Profile window
if (dialog_save.ShowDialog(this) == DialogResult.OK)
{
if (_selectedProfile.CreateShortcut(dialog_save.FileName))
{
MessageBox.Show(
String.Format(Language.Shortcut_placed_successfully, dialog_save.FileName),
Language.Shortcut,
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
else
{
MessageBox.Show(
Language.Failed_to_create_the_shortcut_Unexpected_exception_occurred,
Language.Shortcut,
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
dialog_save.FileName = string.Empty;
//DialogResult = DialogResult.OK;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
private void RefreshDisplayProfileUI()
{
ImageListViewItem newItem = null;
// Temporarily stop updating the saved_profiles listview
// To stop the display showing all sorts of changes happening
ilv_saved_profiles.SuspendLayout();
// Figure out if anything is selected at the moment
// and if it is save it to reselect it after the refresh
// We only take the first as there is only one thing selected at a time
/*string lastSelectedItemName = "";
if (ilv_saved_profiles.SelectedItems.Count > 0)
lastSelectedItemName = ilv_saved_profiles.SelectedItems[0].Text;
*/
// Empty the imageListView
ilv_saved_profiles.Items.Clear();
//IOrderedEnumerable<ProfileItem> orderedProfiles = ProfileRepository.AllProfiles.OrderBy(p => p.Name);
// Check if the last selected profile is still in the list of profiles
//bool lastSelectedItemStillThere = (from profile in orderedProfiles select profile.Name).Contains(lastSelectedItemName);
// Fill it back up with the Profiles we have
foreach (ProfileItem profile in ProfileRepository.AllProfiles.OrderBy(p => p.Name))
{
// Create a new ImageListViewItem from the profile
newItem = new ImageListViewItem(profile, profile.Name);
// if the item was removed from the list during this
// list refresh, then we select this profile only if it
// is the currently used Profile
if (profile.Equals(_selectedProfile))
newItem.Selected = true;
// Add it to the list!
ilv_saved_profiles.Items.Add(newItem, _profileAdaptor);
}
// Restart updating the saved_profiles listview
ilv_saved_profiles.ResumeLayout();
}
/*private void DisplayProfileForm_Activated(object sender, EventArgs e)
{
// We handle the UI updating in DisplayProfileForm_Activated so that
// the app will check for changes to the current profile when the
// user clicks back to this app. This is designed to allow people to
// alter their Windows Display settings then come back to our app
// and the app will automatically recognise that things have changed.
// Reload the profiles in case we swapped to another program to change it
ChangeSelectedProfile(ProfileRepository.CurrentProfile);
// Refresh the Profile UI
RefreshDisplayProfileUI();
}*/
private void DisplayProfileForm_Load(object sender, EventArgs e)
{
// Refresh the profiles to see whats valid
ProfileRepository.IsPossibleRefresh();
// Update the Current Profile, but if another task is running then just wait.
if (Program.AppBackgroundTaskSemaphoreSlim.CurrentCount == 0)
{
logger.Error($"DisplayProfileForm/DisplayProfileForm_Load: Waiting to run the UpdateActiveProfile as there is another Task running!");
}
Program.AppBackgroundTaskSemaphoreSlim.Wait();
logger.Trace($"DisplayProfileForm/DisplayProfileForm_Load: Running the UpdateActiveProfile as there are no other Tasks running!");
ProfileRepository.UpdateActiveProfile();
Program.AppBackgroundTaskSemaphoreSlim.Release();
ChangeSelectedProfile(ProfileRepository.CurrentProfile);
// Refresh the Profile UI
RefreshDisplayProfileUI();
}
private void ChangeSelectedProfile(ProfileItem profile)
{
// And we need to update the actual selected profile too!
_selectedProfile = profile;
// We also need to load the saved profile name to show the user
lbl_profile_shown.Text = _selectedProfile.Name;
// And show the logo for the driver
if (_selectedProfile.VideoMode == VIDEO_MODE.NVIDIA)
{
pbLogo.Image = PickBitmapBasedOnBgColour(BackColor, Properties.Resources.nvidiablack, Properties.Resources.nvidiawhite);
}
else if (_selectedProfile.VideoMode == VIDEO_MODE.AMD)
{
pbLogo.Image = PickBitmapBasedOnBgColour(BackColor, Properties.Resources.amdblack, Properties.Resources.amdwhite);
}
else
{
pbLogo.Image = PickBitmapBasedOnBgColour(BackColor, Properties.Resources.winblack, Properties.Resources.winwhite);
}
// And update the save/rename textbox
txt_profile_save_name.Text = _selectedProfile.Name;
if (ProfileRepository.ContainsProfile(profile))
{
// we already have the profile stored
_saveOrRenameMode = "rename";
btn_save_or_rename.Text = "Rename To";
lbl_save_profile.Visible = false;
if (!_selectedProfile.IsPossible)
{
lbl_profile_shown_subtitle.Text = "This Display Profile can't be used as not all Displays are connected.";
btn_apply.Visible = false;
}
else
{
if (ProfileRepository.IsActiveProfile(_selectedProfile))
{
btn_apply.Visible = false;
lbl_profile_shown_subtitle.Text = "This is the Display Profile currently in use.";
cms_profiles.Items[0].Enabled = false;
}
else
{
btn_apply.Visible = true;
lbl_profile_shown_subtitle.Text = "";
cms_profiles.Items[0].Enabled = true;
}
}
}
else
{
// we don't have the profile stored yet
_saveOrRenameMode = "save";
btn_save_or_rename.Text = "Save";
lbl_profile_shown_subtitle.Text = "The current Display configuration hasn't been saved as a Display Profile yet.";
btn_apply.Visible = false;
lbl_save_profile.Visible = true;
}
// Update the Hotkey Label text
UpdateHotkeyLabel(_selectedProfile.Hotkey);
// Refresh the image list view
//RefreshImageListView(profile);
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
// And finally refresh the profile in the display view
dv_profile.Profile = profile;
dv_profile.Refresh();
}
private void btn_save_as_Click(object sender, EventArgs e)
{
// Check there is a name
if (String.IsNullOrWhiteSpace(txt_profile_save_name.Text))
{
logger.Warn($"DisplayProfileForm/btn_save_as_Click: You need to provide a name for this profile before it can be saved.");
MessageBox.Show("You need to provide a name for this profile before it can be saved.", "Your profile needs a name", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Check the name is valid
if (!Program.IsValidFilename(txt_profile_save_name.Text))
{
logger.Warn($"DisplayProfileForm/btn_save_as_Click: The profile name cannot contain the following characters: {Path.GetInvalidFileNameChars()}. Unable to save this profile.");
MessageBox.Show($"The profile name cannot contain the following characters: [{Path.GetInvalidFileNameChars()}]. Please change the profile name.", "Invalid characters in profile name", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Check we're not already using the name
foreach (ProfileItem savedProfile in ProfileRepository.AllProfiles)
{
//if (String.Equals(txt_profile_save_name.Text, savedProfile.Name, StringComparison.InvariantCultureIgnoreCase))
if (savedProfile.Name.Equals(txt_profile_save_name.Text))
{
logger.Warn($"DisplayProfileForm/btn_save_as_Click: The profile name {txt_profile_save_name.Text} already exists. Each profile name must be unique. Unable to save this profile.");
MessageBox.Show("Sorry, each saved display profile needs a unique name. Please change the profile name.", "Profile name already exists", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
// If we're saving the current profile as a new item
// then we'll be in "save" mode
if (_saveOrRenameMode == "save")
{
// We're in 'save' mode!
// Check we're not already saving this profile
string previouslySavedProfileName;
if (ProfileRepository.ContainsCurrentProfile(out previouslySavedProfileName))
{
MessageBox.Show($"Sorry, this display profile was already saved as '{previouslySavedProfileName}'.", "Profile already saved", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Check the config actual results in an image (might be a logic error that we missed)
if (_selectedProfile.ProfileBitmap.Width == 0 || _selectedProfile.ProfileBitmap.Height == 0)
{
logger.Warn($"DisplayProfileForm/btn_save_as_Click: Display Layout image rendering error (ProfileBitmap)! We won't be able to save this profile. Please log a new issue at https://github.com/terrymacdonald/DisplayMagician/issues/new/choose");
MessageBox.Show("Display Layout image rendering error (ProfileBitmap)! We won't be able to save this profile. Please log a new issue at https://github.com/terrymacdonald/DisplayMagician/issues/new/choose", "Display rendering error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Check the config actual results in an image (might be a logic error that we missed)
if (_selectedProfile.ProfileTightestBitmap.Width == 0 || _selectedProfile.ProfileTightestBitmap.Height == 0)
{
logger.Warn($"DisplayProfileForm/btn_save_as_Click: Display Layout image rendering error (ProfileTightestBitmap)! We won't be able to save this profile. Please log a new issue at https://github.com/terrymacdonald/DisplayMagician/issues/new/choose");
MessageBox.Show("Display Layout image rendering error (ProfileTightestBitmap)! We won't be able to save this profile. Please log a new issue at https://github.com/terrymacdonald/DisplayMagician/issues/new/choose", "Display rendering error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// So we've already passed the check that says this profile is unique
// Update the name just to make sure we record it if the user changed it
_selectedProfile.Name = txt_profile_save_name.Text;
// Add the current profile to the list of profiles so it gets saved
ProfileRepository.AddProfile(_selectedProfile);
// Also update the imagelistview so that we can see the new profile we just saved
// Load the currentProfile image into the imagelistview
//ImageListViewItem newItem = new ImageListViewItem(_selectedProfile.SavedProfileCacheFilename, _selectedProfile.Name);
ImageListViewItem newItem = new ImageListViewItem(_selectedProfile, _selectedProfile.Name)
{
Selected = true
};
//ilv_saved_profiles.Items.Add(newItem);
ilv_saved_profiles.Items.Add(newItem, _profileAdaptor);
}
else
{
// We're in 'rename' mode!
// Check the name is the same, and if so do nothing
if (_selectedProfile.Name.Equals(txt_profile_save_name.Text))
{
return;
}
// Lets save the old names for usage next
string oldProfileName = _selectedProfile.Name;
// Lets rename the selectedProfile to the new name
ProfileRepository.RenameProfile(_selectedProfile, txt_profile_save_name.Text);
// Lets rename the entry in the imagelistview to the new name
foreach (ImageListViewItem myItem in ilv_saved_profiles.Items)
{
if (myItem.Text == oldProfileName)
{
myItem.Text = txt_profile_save_name.Text;
}
}
// Lets update the rest of the profile screen too
lbl_profile_shown.Text = txt_profile_save_name.Text;
// And we also need to go through the any Shortcuts that use the profile and rename them too!
ShortcutRepository.RenameShortcutProfile(_selectedProfile);
}
ChangeSelectedProfile(_selectedProfile);
// now update the profiles image listview
RefreshDisplayProfileUI();
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
}
private void ilv_saved_profiles_ItemClick(object sender, ItemClickEventArgs e)
{
foreach (ProfileItem savedProfile in ProfileRepository.AllProfiles)
{
if (savedProfile.Name == e.Item.Text)
{
ChangeSelectedProfile(savedProfile);
}
}
if (e.Buttons == MouseButtons.Right)
{
cms_profiles.Show(ilv_saved_profiles, e.Location);
}
}
private void btn_view_current_Click(object sender, EventArgs e)
{
// Refresh the profiles to see whats valid
ProfileRepository.IsPossibleRefresh();
// Reload the profiles in case we swapped to another program to change it
ProfileRepository.UpdateActiveProfile();
// Change to the current selected Profile
ChangeSelectedProfile(ProfileRepository.GetActiveProfile());
// Refresh the Profile UI
RefreshDisplayProfileUI();
}
private void txt_profile_save_name_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter))
{
MessageBox.Show("Click works!", "Click works", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
protected override void WndProc(ref Message m)
{
const int WM_DISPLAYCHANGE = 0x007E;
const int WM_SETTINGCHANGE = 0x001A;
const int WM_DEVICECHANGE = 0x0219;
const int DBT_DEVICEARRIVAL = 0x8000;
const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
switch (m.Msg)
{
case WM_DEVICECHANGE:
switch ((int)m.WParam)
{
case DBT_DEVICEARRIVAL:
logger.Trace($"DisplayProfileForm/WndProc: Windows just sent a msg telling us a device has been added. We need to check if this was a USB display. Updating the current view by running btn_view_current.");
btn_view_current.PerformClick();
break;
case DBT_DEVICEREMOVECOMPLETE:
logger.Trace($"DisplayProfileForm/WndProc: Windows just sent a msg telling us a device has been removed. We need to check if this was a USB display. Updating the current view by running btn_view_current.");
btn_view_current.PerformClick();
break;
}
break;
case WM_DISPLAYCHANGE:
logger.Trace($"DisplayProfileForm/WndProc: Windows just sent a msg telling us the display has changed. Updating the current view by running btn_view_current.");
btn_view_current.PerformClick();
break;
// This auto taskbar detection logic just doesn't work at the moment
// It tries to set a 5 second timer when it detects a settings change, and tries every 1 second to see if the taskbar position has changed
// If taskbar position changed, then it attempts to get the new display layout.
// In reality, it appears that multiple tasks are firing for each message, and the multople tasks are confusing each other. So I'm going to leave this be for now.
/*case WM_SETTINGCHANGE:
switch ((int)m.WParam)
{
case 0x2f:
// This occurs when the taskbar is moved! We use it to set a timer to monitor the relevant registry keys for changes within the next 10 seconds
logger.Trace($"DisplayProfileForm/WndProc: Windows just sent a msg telling us a taskbar has been moved. We need to set a timer to check for registry key changes within the next 10 seconds, and update the current view if that happens.");
if (_monitorTaskBarRegKeysForChangesTask == null)
{
logger.Trace($"DisplayProfileForm/WndProc: We are starting to monitor the taskbar for changes for the next 10 seconds.");
//_monitorTaskBarRegKeysForChanges = true;
List<TaskBarStuckRectangle> original = TaskBarStuckRectangle.GetAllTaskBarStuckRectangles();
_monitorTaskBarRegKeysForChangesTask = new Task((Action)delegate
{
bool _itChanged = false;
for (int d = 0; d < 10; d++)
{
Task.Delay(1000);
List<TaskBarStuckRectangle> subsequent = TaskBarStuckRectangle.GetAllTaskBarStuckRectangles();
bool matched = true;
for (int x = 0; x < subsequent.Count; x++)
{
if (!original[x].Equals(subsequent[x]))
{
matched = false;
break;
}
}
if (!matched)
{
logger.Trace($"DisplayProfileForm/WndProc: The taskbar registry key has been updated within the 10 seconds of a taskbar move message, so updating the config again window.");
if (btn_view_current.InvokeRequired)
{
this.Invoke(new Action(() => btn_view_current.PerformClick()));
}
else
{
btn_view_current.PerformClick();
}
_itChanged = true;
break;
}
}
if (!_itChanged)
{
logger.Trace($"DisplayProfileForm/WndProc: The taskbar registry key did not update within 5 seconds of a taskbar move message. Returning without doing anything.");
}
_monitorTaskBarRegKeysForChangesTask = null;
});
_monitorTaskBarRegKeysForChangesTask.Start();
}
break;
}
break;*/
}
base.WndProc(ref m);
}
private void ilv_saved_profiles_ItemHover(object sender, ItemHoverEventArgs e)
{
if (e.Item != null)
{
tt_selected.SetToolTip(ilv_saved_profiles, e.Item.Text);
}
else
{
tt_selected.RemoveAll();
}
}
private Bitmap PickBitmapBasedOnBgColour(Color bgColour, Bitmap lightBitmap, Bitmap darkBitmap)
{
if ((bgColour.R * 0.299 + bgColour.G * 0.587 + bgColour.B * 0.114) > 186)
{
return darkBitmap;
}
else
{
return lightBitmap;
}
}
private void btn_hotkey_Click(object sender, EventArgs e)
{
Keys testHotkey;
if (_selectedProfile.Hotkey != Keys.None)
testHotkey = _selectedProfile.Hotkey;
else
testHotkey = Keys.None;
string hotkeyHeading = $"Choose a '{_selectedProfile.Name}' Display Profile Hotkey";
string hotkeyDescription = $"Choose a Hotkey (a keyboard shortcut) so that you can apply this" + Environment.NewLine +
"Display Profile using your keyboard. This must be a Hotkey that" + Environment.NewLine +
"is unique across all your applications otherwise DisplayMagician" + Environment.NewLine +
"might not see it.";
HotkeyForm displayHotkeyForm = new HotkeyForm(testHotkey,hotkeyHeading, hotkeyDescription);
//ilv_saved_shortcuts.SuspendLayout();
//Program.HotkeyListener.SuspendOn(displayHotkeyForm);
displayHotkeyForm.ShowDialog(this);
if (displayHotkeyForm.DialogResult == DialogResult.OK)
{
// now we save the Hotkey
_selectedProfile.Hotkey = displayHotkeyForm.Hotkey;
// And cause this has changed within a Profile we need to save all the profiles
ProfileRepository.SaveProfiles();
// And if we get back and this is a Hotkey with a value, we need to show that in the UI
UpdateHotkeyLabel(_selectedProfile.Hotkey);
if (displayHotkeyForm.Hotkey == Keys.None)
// Remove the Hotkey if it needs to be removed
HotkeyManager.Current.Remove(_selectedProfile.UUID);
else
// And then apply the Hotkey now
HotkeyManager.Current.AddOrReplace(_selectedProfile.UUID, _selectedProfile.Hotkey, OnWindowHotkeyPressed);
}
}
private void lbl_hotkey_assigned_Click(object sender, EventArgs e)
{
btn_hotkey.PerformClick();
}
private void UpdateHotkeyLabel (Keys myHotkey)
{
// And if we get back and this is a Hotkey with a value, we need to show that in the UI
if (myHotkey != Keys.None)
{
KeysConverter kc = new KeysConverter();
lbl_hotkey_assigned.Text = "Hotkey: " + kc.ConvertToString(myHotkey);
lbl_hotkey_assigned.Visible = true;
}
else
{
lbl_hotkey_assigned.Text = "Hotkey: None";
lbl_hotkey_assigned.Visible = false;
}
}
public void OnWindowHotkeyPressed(object sender, HotkeyEventArgs e)
{
if (ProfileRepository.ContainsProfile(e.Name))
{
string displayProfileUUID = e.Name;
ProfileItem chosenProfile = ProfileRepository.GetProfile(displayProfileUUID);
if (chosenProfile is ProfileItem)
//ProfileRepository.ApplyProfile(chosenProfile);
Program.ApplyProfileTask(chosenProfile);
}
}
private void btn_profile_settings_Click(object sender, EventArgs e)
{
ProfileSettingsForm profileSettingsForm = new ProfileSettingsForm();
profileSettingsForm.Profile = _selectedProfile;
profileSettingsForm.ShowDialog(this);
// If the profile was previously saved and is now changed then save all the profiles
// otherwise we'll save it only when the user wants to save this profile.
if (_saveOrRenameMode == "rename" && profileSettingsForm.ProfileSettingChanged)
{
//_selectedProfile = profileSettingsForm.Profile;
ProfileRepository.SaveProfiles();
}
}
private void btn_help_Click(object sender, EventArgs e)
{
string targetURL = @"https://github.com/terrymacdonald/DisplayMagician/wiki/Initial-DisplayMagician-Setup";
System.Diagnostics.Process.Start(targetURL);
}
private void saveProfileToDesktopToolStripMenuItem_Click(object sender, EventArgs e)
{
btn_save.PerformClick();
}
private void applyToolStripMenuItem_Click(object sender, EventArgs e)
{
btn_apply.PerformClick();
}
private void deleteProfileToolStripMenuItem_Click(object sender, EventArgs e)
{
btn_delete.PerformClick();
}
private void sendToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
{
string commandline = _selectedProfile.CreateCommand();
Clipboard.SetText(commandline);
}
private void ilv_saved_profiles_ItemDoubleClick(object sender, ItemClickEventArgs e)
{
// This is the double click to apply
_selectedProfile = ProfileRepository.GetProfile(e.Item.Text);
// Apply the selected profile
btn_apply.PerformClick();
}
private void btn_donate_Click(object sender, EventArgs e)
{
string targetURL = @"https://github.com/sponsors/terrymacdonald";
System.Diagnostics.Process.Start(targetURL);
}
}
}