Merge branch 'develop'

This commit is contained in:
Terry MacDonald 2021-11-20 08:47:55 +13:00
commit 7feef1c50c
52 changed files with 49870 additions and 2550 deletions

View File

@ -31,7 +31,7 @@
<BootstrapperEnabled>true</BootstrapperEnabled> <BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
@ -44,10 +44,10 @@
<AllowUnsafeBlocks>false</AllowUnsafeBlocks> <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
@ -84,6 +84,7 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Management" /> <Reference Include="System.Management" />
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.Web" /> <Reference Include="System.Web" />
@ -108,7 +109,7 @@
<Compile Include="GameLibraries\SteamAppInfoParser\Package.cs" /> <Compile Include="GameLibraries\SteamAppInfoParser\Package.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\PackageInfo.cs" /> <Compile Include="GameLibraries\SteamAppInfoParser\PackageInfo.cs" />
<Compile Include="GameLibraries\SteamAppInfoParser\App.cs" /> <Compile Include="GameLibraries\SteamAppInfoParser\App.cs" />
<Compile Include="GameLibraries\UplayConfigurationParser\UplayConfigurationParser.cs" /> <Compile Include="GameLibraries\UplayFileStructure.cs" />
<Compile Include="GameLibraries\OriginGame.cs" /> <Compile Include="GameLibraries\OriginGame.cs" />
<Compile Include="GameLibraries\OriginLibrary.cs" /> <Compile Include="GameLibraries\OriginLibrary.cs" />
<Compile Include="GameLibraries\UplayLibrary.cs" /> <Compile Include="GameLibraries\UplayLibrary.cs" />
@ -144,6 +145,18 @@
<DependentUpon>HotkeyForm.cs</DependentUpon> <DependentUpon>HotkeyForm.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="UIForms\ImageListViewRenderers.cs" /> <Compile Include="UIForms\ImageListViewRenderers.cs" />
<Compile Include="UIForms\LoadingForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UIForms\LoadingForm.Designer.cs">
<DependentUpon>LoadingForm.cs</DependentUpon>
</Compile>
<Compile Include="UIForms\ChooseImageForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UIForms\ChooseImageForm.Designer.cs">
<DependentUpon>ChooseImageForm.cs</DependentUpon>
</Compile>
<Compile Include="UIForms\ProfileSettingsForm.cs"> <Compile Include="UIForms\ProfileSettingsForm.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@ -182,6 +195,7 @@
<Compile Include="UIForms\StartProgramControl.Designer.cs"> <Compile Include="UIForms\StartProgramControl.Designer.cs">
<DependentUpon>StartProgramControl.cs</DependentUpon> <DependentUpon>StartProgramControl.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Utils.cs" />
<Compile Include="Validators.cs" /> <Compile Include="Validators.cs" />
<Compile Include="InterProcess\IPCClient.cs" /> <Compile Include="InterProcess\IPCClient.cs" />
<Compile Include="InterProcess\InstanceStatus.cs" /> <Compile Include="InterProcess\InstanceStatus.cs" />
@ -215,10 +229,16 @@
<EmbeddedResource Include="UIForms\HotkeyForm.resx"> <EmbeddedResource Include="UIForms\HotkeyForm.resx">
<DependentUpon>HotkeyForm.cs</DependentUpon> <DependentUpon>HotkeyForm.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="UIForms\LoadingForm.resx">
<DependentUpon>LoadingForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UIForms\MainForm.resx"> <EmbeddedResource Include="UIForms\MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon> <DependentUpon>MainForm.cs</DependentUpon>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="UIForms\ChooseImageForm.resx">
<DependentUpon>ChooseImageForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="UIForms\ProfileSettingsForm.resx"> <EmbeddedResource Include="UIForms\ProfileSettingsForm.resx">
<DependentUpon>ProfileSettingsForm.cs</DependentUpon> <DependentUpon>ProfileSettingsForm.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
@ -279,9 +299,6 @@
<PackageReference Include="CircularProgressBar"> <PackageReference Include="CircularProgressBar">
<Version>2.8.0.16</Version> <Version>2.8.0.16</Version>
</PackageReference> </PackageReference>
<PackageReference Include="HtmlAgilityPack">
<Version>1.11.36</Version>
</PackageReference>
<PackageReference Include="IconLib.Unofficial"> <PackageReference Include="IconLib.Unofficial">
<Version>0.73.0</Version> <Version>0.73.0</Version>
</PackageReference> </PackageReference>
@ -295,7 +312,7 @@
<Version>4.0.0-beta.74</Version> <Version>4.0.0-beta.74</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications"> <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications">
<Version>7.1.0-preview1</Version> <Version>7.1.1</Version>
</PackageReference> </PackageReference>
<PackageReference Include="MintPlayer.IconUtils"> <PackageReference Include="MintPlayer.IconUtils">
<Version>1.0.4</Version> <Version>1.0.4</Version>
@ -310,11 +327,17 @@
<Version>2.1.0</Version> <Version>2.1.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="NLog"> <PackageReference Include="NLog">
<Version>4.7.11</Version> <Version>4.7.12</Version>
</PackageReference>
<PackageReference Include="protobuf-net">
<Version>3.0.101</Version>
</PackageReference> </PackageReference>
<PackageReference Include="QueryString.NET"> <PackageReference Include="QueryString.NET">
<Version>1.0.0</Version> <Version>1.0.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="System.IO.Compression">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="ValveKeyValue"> <PackageReference Include="ValveKeyValue">
<Version>0.3.1.152</Version> <Version>0.3.1.152</Version>
</PackageReference> </PackageReference>
@ -324,6 +347,9 @@
<PackageReference Include="WinFormAnimation"> <PackageReference Include="WinFormAnimation">
<Version>1.6.0.4</Version> <Version>1.6.0.4</Version>
</PackageReference> </PackageReference>
<PackageReference Include="YamlDotNet">
<Version>11.2.1</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
@ -332,6 +358,7 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Resources\x.png" />
<None Include="Resources\winwhite.png" /> <None Include="Resources\winwhite.png" />
<None Include="Resources\winblack.png" /> <None Include="Resources\winblack.png" />
<None Include="Resources\Steam.png" /> <None Include="Resources\Steam.png" />

View File

@ -556,7 +556,7 @@ namespace DisplayMagician.GameLibraries
return true; return true;
} }
public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal) /*public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal)
{ {
string address = $@"com.epicgames.launcher://apps/{game.Id}?action=launch&silent=true"; string address = $@"com.epicgames.launcher://apps/{game.Id}?action=launch&silent=true";
if (String.IsNullOrWhiteSpace(gameArguments)) if (String.IsNullOrWhiteSpace(gameArguments))
@ -566,7 +566,18 @@ namespace DisplayMagician.GameLibraries
Process gameProcess = Process.Start(address); Process gameProcess = Process.Start(address);
gameProcess.PriorityClass = processPriority; gameProcess.PriorityClass = processPriority;
return gameProcess; return gameProcess;
}*/
public override List<Process> StartGame(Game game, string gameArguments = "", ProcessPriority processPriority = ProcessPriority.Normal)
{
string address = $@"com.epicgames.launcher://apps/{game.Id}?action=launch&silent=true";
if (!String.IsNullOrWhiteSpace(gameArguments))
{
address += @"/" + gameArguments;
}
//Process gameProcess = Process.Start(address);
List<Process> gameProcesses = ProcessUtils.StartProcess(address, null, processPriority);
return gameProcesses;
} }
#endregion #endregion

View File

@ -552,21 +552,18 @@ namespace DisplayMagician.GameLibraries
} }
public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal) public override List<Process> StartGame(Game game, string gameArguments = "", ProcessPriority processPriority = ProcessPriority.Normal)
{ {
string args = $@"/command=runGame /gameId={game.Id} /path=""{game.Directory}"""; string args = $@"/command=runGame /gameId={game.Id} /path=""{game.Directory}""";
if (String.IsNullOrWhiteSpace(gameArguments)) if (String.IsNullOrWhiteSpace(gameArguments))
{ {
args += gameArguments; args += gameArguments;
} }
Process gameProcess = null; List<Process> gameProcesses = ProcessUtils.StartProcess(_gogExe, args, processPriority);
uint processID = 0; return gameProcesses;
if (ProcessUtils.LaunchProcessWithPriority(_gogExe, args, processPriority, out processID))
{
gameProcess = Process.GetProcessById((int)processID);
}
return gameProcess;
} }
#endregion #endregion
} }

View File

@ -33,7 +33,9 @@ namespace DisplayMagician.GameLibraries
public virtual List<Process> Processes { get; set; } public virtual List<Process> Processes { get; set; }
public Bitmap GameBitmap { get; set; } public ShortcutBitmap GameBitmap { get; set; }
public List<ShortcutBitmap> AvailableGameBitmaps { get; set; }
#endregion #endregion

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks;
using DisplayMagician; using DisplayMagician;
namespace DisplayMagician.GameLibraries namespace DisplayMagician.GameLibraries
@ -18,6 +19,8 @@ namespace DisplayMagician.GameLibraries
public class GameLibrary public class GameLibrary
{ {
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public struct GameAppInfo public struct GameAppInfo
{ {
public string GameID; public string GameID;
@ -30,6 +33,10 @@ namespace DisplayMagician.GameLibraries
#region Class Properties #region Class Properties
public static List<Game> AllInstalledGamesInAllLibraries { get; set; } public static List<Game> AllInstalledGamesInAllLibraries { get; set; }
public static bool GamesLoaded { get; set; } = false;
public static bool GamesImagesLoaded { get; set; } = false;
public virtual List<Game> AllInstalledGames { get; set; } public virtual List<Game> AllInstalledGames { get; set; }
public virtual int InstalledGameCount { get; set; } public virtual int InstalledGameCount { get; set; }
@ -103,11 +110,253 @@ namespace DisplayMagician.GameLibraries
return false; return false;
} }
public virtual Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal) public virtual List<Process> StartGame(Game game, string gameArguments = "", ProcessPriority processPriority = ProcessPriority.Normal)
{ {
return null; return null;
} }
public static bool LoadGamesInBackground()
{
logger.Trace($"Program/LoadGamesInBackground: Attempting to load games from detected game libraries.");
// Now lets prepare loading all the Steam games we have installed
Action loadSteamGamesAction = new Action(() =>
{
// Check if Steam is installed
GameLibrary steamLibrary = SteamLibrary.GetLibrary();
if (steamLibrary.IsGameLibraryInstalled)
{
// Load Steam library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Steam Games");
if (!steamLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Steam Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Steam Games (found {steamLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Steam not installed.");
Console.WriteLine("Steam not installed.");
}
});
// Now lets prepare loading all the Uplay games we have installed
Action loadUplayGamesAction = new Action(() =>
{
// Check if Uplay is installed
GameLibrary uplayLibrary = UplayLibrary.GetLibrary();
if (uplayLibrary.IsGameLibraryInstalled)
{
// Load Uplay library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Uplay Games");
if (!uplayLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Uplay Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Uplay Games (found {uplayLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Uplay not installed.");
Console.WriteLine("Uplay not installed.");
}
});
// Now lets prepare loading all the Origin games we have installed
Action loadOriginGamesAction = new Action(() =>
{
// Check if Origin is installed
GameLibrary originLibrary = OriginLibrary.GetLibrary();
if (originLibrary.IsGameLibraryInstalled)
{
// Load Origin library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Origin Games");
if (!originLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Origin Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Origin Games (found {originLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Origin not installed.");
Console.WriteLine("Origin not installed.");
}
});
// Now lets prepare loading all the Epic games we have installed
Action loadEpicGamesAction = new Action(() =>
{
// Check if Epic is installed
GameLibrary epicLibrary = EpicLibrary.GetLibrary();
if (epicLibrary.IsGameLibraryInstalled)
{
// Load Origin library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Epic Games");
if (!epicLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Epic Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Epic Games (found {epicLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Epic not installed.");
Console.WriteLine("Epic not installed.");
}
});
// Now lets prepare loading all the GOG games we have installed
Action loadGogGamesAction = new Action(() =>
{
// Check if GOG is installed
GameLibrary gogLibrary = GogLibrary.GetLibrary();
if (gogLibrary.IsGameLibraryInstalled)
{
// Load Origin library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed GOG Games");
if (!gogLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed GOG Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed GOG Games (found {gogLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: GOG not installed.");
Console.WriteLine("GOG not installed.");
}
});
// Store all the actions in a array so we can wait on them later
List<Action> loadGamesActions = new List<Action>();
loadGamesActions.Add(loadSteamGamesAction);
loadGamesActions.Add(loadUplayGamesAction);
loadGamesActions.Add(loadOriginGamesAction);
loadGamesActions.Add(loadEpicGamesAction);
loadGamesActions.Add(loadGogGamesAction);
try
{
logger.Debug($"Program/LoadGamesInBackground: Running game loading actions.");
// Go through and start all the actions, making sure we only have one threat per action to avoid thread issues
int threads = loadGamesActions.Count;
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = threads };
Parallel.Invoke(options, loadGamesActions.ToArray());
// Once we get here , we know that all the parallel actions have returned
logger.Debug($"Program/LoadGamesInBackground: All game loading tasks finished");
}
catch (AggregateException ae)
{
logger.Error(ae, $"Program/LoadGamesInBackground: One or more exception during execution of loadGamesActions");
foreach (Exception ex in ae.InnerExceptions)
{
logger.Error(ex, $"Program/LoadGamesInBackground: LoadGamesActions exception:");
}
}
// Clear the game libraries in case this is a refresh
SteamLibrary.GetLibrary().AllInstalledGames.Clear();
UplayLibrary.GetLibrary().AllInstalledGames.Clear();
OriginLibrary.GetLibrary().AllInstalledGames.Clear();
EpicLibrary.GetLibrary().AllInstalledGames.Clear();
GogLibrary.GetLibrary().AllInstalledGames.Clear();
// Produce a single array of Games we can reference later
GameLibrary.AllInstalledGamesInAllLibraries = SteamLibrary.GetLibrary().AllInstalledGames;
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(UplayLibrary.GetLibrary().AllInstalledGames);
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(OriginLibrary.GetLibrary().AllInstalledGames);
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(EpicLibrary.GetLibrary().AllInstalledGames);
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(GogLibrary.GetLibrary().AllInstalledGames);
// Stop creating Game Bitmaps from the Games so the rest of the program is faster later
//RefreshGameBitmaps();
GamesLoaded = true;
return true;
}
public static void RefreshGameBitmaps()
{
// Create Game Bitmaps from the Games so the rest of the program is faster later
// Get the bitmap out of the IconPath
// IconPath can be an ICO, or an EXE
foreach (var game in GameLibrary.AllInstalledGamesInAllLibraries)
{
List<ShortcutBitmap> bmList = new List<ShortcutBitmap>();
try
{
/*ArrayList filesToSearchForIcon = new ArrayList();
filesToSearchForIcon.Add(game.ExePath);
if (game.IconPath != game.ExePath)
filesToSearchForIcon.Add(game.IconPath);
bm = ImageUtils.GetMeABitmapFromFile(filesToSearchForIcon);*/
// We only want the icon location that the GameLibrary told us to use
// Note: This may be an icon file, or an exe file.
// This function tries to get a 256x256 Vista sized bitmap from the file
logger.Trace($"Program/LoadGamesInBackground: Attempting to get game bitmaps from {game.Name}.");
bmList.AddRange(ImageUtils.GetMeAllBitmapsFromFile(game.IconPath));
if (game.ExePath != game.IconPath)
{
bmList.AddRange(ImageUtils.GetMeAllBitmapsFromFile(game.ExePath));
}
logger.Trace($"Program/LoadGamesInBackground: Got game bitmaps from {game.Name}.");
}
catch (Exception ex)
{
logger.Error(ex, $"Program/LoadGamesInBackground: Exception building game bitmaps for {game.Name} during load");
}
if (bmList.Count == 0)
{
ShortcutBitmap bm = new ShortcutBitmap();
if (game.GameLibrary.Equals(SupportedGameLibraryType.Steam))
{
bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Steam, "Steam Icon", game.ExePath, bmList.Count);
}
else if (game.GameLibrary.Equals(SupportedGameLibraryType.Uplay))
{
bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Uplay, "Uplay Icon", game.ExePath, bmList.Count);
}
else if (game.GameLibrary.Equals(SupportedGameLibraryType.Origin))
{
bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Origin, "Origin Icon", game.ExePath, bmList.Count);
}
else if (game.GameLibrary.Equals(SupportedGameLibraryType.Epic))
{
bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Epic, "Epic Icon", game.ExePath, bmList.Count);
}
else if (game.GameLibrary.Equals(SupportedGameLibraryType.GOG))
{
bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.GOG, "GOG Icon", game.ExePath, bmList.Count);
}
else
{
bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.DisplayMagician.ToBitmap(), "DisplayMagician Icon", game.ExePath, bmList.Count);
}
// Add the shortcutbitmap to the list
bmList.Add(bm);
}
game.AvailableGameBitmaps = bmList;
game.GameBitmap = ImageUtils.GetMeLargestAvailableBitmap(bmList);
}
GamesImagesLoaded = true;
}
#endregion #endregion
} }

View File

@ -10,6 +10,7 @@ using System.Xml.Linq;
using System.Xml.XPath; using System.Xml.XPath;
using System.Web; using System.Web;
using System.Diagnostics; using System.Diagnostics;
using System.Text;
namespace DisplayMagician.GameLibraries namespace DisplayMagician.GameLibraries
{ {
@ -507,168 +508,153 @@ namespace DisplayMagician.GameLibraries
logger.Trace($"OriginLibrary/LoadInstalledGames: Attempting to parse XML Game Installer Data file at {gameInstallerData}"); logger.Trace($"OriginLibrary/LoadInstalledGames: Attempting to parse XML Game Installer Data file at {gameInstallerData}");
// Now we parse the XML // Now we parse the XML
XDocument xdoc = XDocument.Load(gameInstallerData); XDocument xdoc = XDocument.Load(gameInstallerData);
float manifestVersion;
// Try to figure out which version of the client created this game (as they changed their format a lot)
if (xdoc.XPathSelectElement("/DiPManifest").Attribute("version").Value != null)
{
if (Single.TryParse(xdoc.XPathSelectElement("/DiPManifest").Attribute("version").Value, out manifestVersion))
{
// This is an Origin manifest Version 4.0 client installed game
logger.Trace($"OriginLibrary/LoadInstalledGames: v4 - Detected the {gameInstallerData} manifest version was v{manifestVersion}");
}
else
{
logger.Error($"OriginLibrary/LoadInstalledGames: v4 - Couldn't determine the Detected the installer.xml manifest version for {gameInstallerData}. Skipping processing file.");
continue;
}
}
else if (xdoc.XPathSelectElement("/game").Attribute("manifestVersion").Value != null)
{
if (Single.TryParse(xdoc.XPathSelectElement("/game").Attribute("manifestVersion").Value,out manifestVersion))
{
// This is an Origin manifest Version 2.x or 3.0 client installed game
logger.Trace($"OriginLibrary/LoadInstalledGames: v3 - Detected the {gameInstallerData} manifest version was v{manifestVersion}");
}
else
{
logger.Error($"OriginLibrary/LoadInstalledGames: v3 - Couldn't determine the Detected the installer.xml manifest version for {gameInstallerData}. Skipping processing file.");
continue;
}
}
else
{
// This is an unrecognised manifest file
logger.Error($"OriginLibrary/LoadInstalledGames: Unrecognised installer.xml manifest version for {gameInstallerData}. Skipping processing file.");
continue;
}
// now we go through and attempt to process the various manifest versions
if (manifestVersion >= 4.0)
{
originGame.GameName = xdoc.XPathSelectElement("/DiPManifest/gameTitles/gameTitle[@locale='en_US']").Value; originGame.GameName = xdoc.XPathSelectElement("/DiPManifest/gameTitles/gameTitle[@locale='en_US']").Value;
logger.Trace($"OriginLibrary/LoadInstalledGames: Game Name {originGame.GameName} found in Game Installer Data file {gameInstallerData}"); logger.Trace($"OriginLibrary/LoadInstalledGames: Game Name {originGame.GameName} found in Game Installer Data file {gameInstallerData}");
string gameFilePath = xdoc.XPathSelectElement("/DiPManifest/runtime/launcher/filePath").Value; // Look for the 64-bit version of the filepath
logger.Trace($"OriginLibrary/LoadInstalledGames: Game File Path is {gameFilePath } found in Game Installer Data file {gameInstallerData}"); originGame.GameExePath = GetActualFilePath(xdoc.XPathSelectElement("/DiPManifest/runtime/launcher[requires64BitOS/text() = '1']/filePath").Value);
if (originGame.GameExePath == null)
string originGameInstallLocation = "";
// Check whether gameFilePath contains a registry key! Cause if it does we need to lookup the path there instead
if (gameFilePath.StartsWith("[HKEY_LOCAL_MACHINE"))
{ {
logger.Trace($"OriginLibrary/LoadInstalledGames: Game File Path starts with a registery key so needs to be translated"); // if not found, then look for the 32-bit version of the filepath
// The filePath contains a registry key lookup that we need to execute and replace logger.Trace($"OriginLibrary/LoadInstalledGames: Couldn't find 64-bit game exe in Game Installer Data file {gameInstallerData}, so looking for 32-bit.");
string originGameInstallKeyNameAndValue = ""; originGame.GameExePath = GetActualFilePath(xdoc.XPathSelectElement("/DiPManifest/runtime/launcher[requires64BitOS/text() = '0']/filePath").Value);
string originGameRestOfFile = ""; if (originGame.GameExePath == null)
MatchCollection mc = Regex.Matches(gameFilePath, @"\[HKEY_LOCAL_MACHINE\\(.*)\](.*)");
if (mc.Count > 0)
{ {
// Split the Reg key bit from the File Path bit logger.Error($"OriginLibrary/LoadInstalledGames: Couldn't find 64-bit or 32-bit game exe in Game Installer Data file {gameInstallerData}, so skipping file.");
originGameInstallKeyNameAndValue = mc[0].Groups[1].ToString();
logger.Trace($"OriginLibrary/LoadInstalledGames: originGameInstallKeyNameAndValue = {originGameInstallKeyNameAndValue}");
originGameRestOfFile = mc[0].Groups[2].ToString();
logger.Trace($"OriginLibrary/LoadInstalledGames: originGameRestOfFile = {originGameRestOfFile}");
if (originGameInstallKeyNameAndValue == null || originGameInstallKeyNameAndValue == "")
{
// then we have a problem and we need to continue and ignore this game
logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has registry key but we can't extract it! gameFilePath is {gameFilePath}.");
continue; continue;
} }
// Split the reg key from the value name
string originGameInstallKeyName = "";
string originGameInstallKeyValue = "";
mc = Regex.Matches(originGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)");
if (mc.Count > 0)
{
originGameInstallKeyName = mc[0].Groups[1].ToString();
logger.Trace($"OriginLibrary/LoadInstalledGames: originGameInstallKeyName = {originGameInstallKeyName }");
originGameInstallKeyValue = mc[0].Groups[2].ToString();
logger.Trace($"OriginLibrary/LoadInstalledGames: originGameInstallKeyValue = {originGameInstallKeyValue }");
} }
logger.Trace($"OriginLibrary/LoadInstalledGames: Game File Path is {originGame.GameExePath} found in Game Installer Data file {gameInstallerData}");
// Lookup the reg key to figure out where the game is installed }
else if (manifestVersion >= 3.0 && manifestVersion < 4.0)
{
originGame.GameName = xdoc.XPathSelectElement("/game/metadata/localeInfo[@locale='en_US']/title").Value;
logger.Trace($"OriginLibrary/LoadInstalledGames: Game Name {originGame.GameName} found in Game Installer Data file {gameInstallerData}");
// Look for the 64-bit version of the filepath
originGame.GameExePath = GetActualFilePath(xdoc.XPathSelectElement("/game/runtime/launcher[requires64BitOS/text() = '1']/filePath").Value);
if (originGame.GameExePath == null)
{
// if not found, then look for the 32-bit version of the filepath
logger.Trace($"OriginLibrary/LoadInstalledGames: Couldn't find 64-bit game exe in Game Installer Data file {gameInstallerData}, so looking for 32-bit.");
originGame.GameExePath = GetActualFilePath(xdoc.XPathSelectElement("/game/runtime/launcher[requires64BitOS/text() = '0']/filePath").Value);
if (originGame.GameExePath == null)
{
logger.Error($"OriginLibrary/LoadInstalledGames: Couldn't find 64-bit or 32-bit game exe in Game Installer Data file {gameInstallerData}, so skipping file.");
continue;
}
}
logger.Trace($"OriginLibrary/LoadInstalledGames: Game File Path is {originGame.GameExePath} found in Game Installer Data file {gameInstallerData}");
}
else if (manifestVersion >= 2.0 && manifestVersion < 3.0)
{
originGame.GameName = xdoc.XPathSelectElement("/game/metadata/localeInfo[@locale='en_US']/title").Value;
logger.Trace($"OriginLibrary/LoadInstalledGames: Game Name {originGame.GameName} found in Game Installer Data file {gameInstallerData}");
// This logger format requires more work and help from someone with the right game installed
string mnsftRelFileName = xdoc.XPathSelectElement("/game/installManifest/filePath").Value;
string mnsftFullFileName = Path.Combine(originGame.GameInstallDir, mnsftRelFileName);
logger.Trace($"OriginLibrary/LoadInstalledGames: Game uses a v{manifestVersion} manifest version, so needing to parse mnfst file {mnsftFullFileName} found in Game Installer Data file {gameInstallerData}");
// read in the mnsft.txt file
string mnsftData;
try try
{ {
RegistryKey originGameInstallKey = Registry.LocalMachine.OpenSubKey(originGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree); mnsftData = File.ReadAllText(mnsftFullFileName, Encoding.Unicode);
if (originGameInstallKey == null) }
catch (Exception ex)
{ {
// then we have a problem as we cannot find the game exe location! logger.Error(ex, $"OriginLibrary/LoadInstalledGames: Tried to read the mnfst file {mnsftFullFileName} to memory but File.ReadAllTextthrew an exception. Skipping this game");
logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has a install reg key we cannot find! originGameInstallKey is {gameFilePath} and originGameInstallKeyValue is {originGameInstallKeyValue}.");
continue; continue;
} }
originGameInstallLocation = Path.Combine(originGameInstallKey.GetValue(originGameInstallKeyValue).ToString(), originGameRestOfFile); // look for a .par file as that will indicate the main exe
if (!File.Exists(originGameInstallLocation)) string[] parFiles;
{
// then we have a problem as we cannot locate the game exe file to start!
logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has gameexe we cannot find! originGameInstallLocation is {originGameInstallLocation}.");
continue;
}
originGame.GameExePath = originGameInstallLocation;
}
catch (SecurityException ex)
{
logger.Warn(ex, $"OriginLibrary/LoadInstalledGames: The user does not have the permissions required to read the Origin Game location registry key {originGameInstallKeyName}, so skipping game");
continue;
}
catch (ObjectDisposedException ex)
{
logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin ClientPath registry key (closed keys cannot be accessed), so skipping game");
continue;
}
catch (IOException ex)
{
logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Origin ClientPath registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check, so skipping game");
continue;
}
catch (UnauthorizedAccessException ex)
{
logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The user does not have the necessary registry rights to check whether Origin is installed, so skipping game");
continue;
}
}
else
{
logger.Warn($"OriginLibrary/LoadInstalledGames: Game File Path {gameFilePath} starts with '[HEKY_LOCAL_MACHINE' but didn't match the regex when it should have");
continue;
}
}
else if (gameFilePath.StartsWith("[HKEY_CURRENT_USER"))
{
// The filePath contains a registry key lookup that we need to execute and replace
MatchCollection mc = Regex.Matches(gameFilePath, @"\[HKEY_CURRENT_USER\\(.*)\](.*)");
if (mc.Count > 0)
{
string originGameInstallKeyNameAndValue = mc[0].Groups[1].ToString();
string originGameRestOfFile = mc[0].Groups[2].ToString();
if (originGameInstallKeyNameAndValue == null)
{
// then we have a problem and we need to continue and ignore this game
logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has registry but we can't match it! gameFilePath is {gameFilePath}.");
continue;
}
mc = Regex.Matches(originGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)");
string originGameInstallKeyName = mc[0].Groups[1].ToString();
string originGameInstallKeyValue = mc[0].Groups[2].ToString();
try try
{ {
RegistryKey originGameInstallKey = Registry.LocalMachine.OpenSubKey(originGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree); parFiles = Directory.GetFiles(originGame.GameInstallDir, "*.par", SearchOption.AllDirectories);
if (originGameInstallKey == null) logger.Trace($"OriginLibrary/LoadInstalledGames: Found {parFiles.Length} .par files in the {originGame.GameInstallDir} directory.");
}
catch (Exception ex)
{ {
// then we have a problem as we cannot find the game exe location! logger.Error(ex, $"OriginLibrary/LoadInstalledGames: Tried to find any *.par files in the game directory {originGame.GameInstallDir} . Skipping this game");
logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has a install reg key we cannot find! originGameInstallKey is {gameFilePath} and originGameInstallKeyValue is {originGameInstallKeyValue}.");
continue; continue;
} }
originGameInstallLocation = Path.Combine(originGameInstallKey.GetValue(originGameInstallKeyValue).ToString(), originGameRestOfFile);
if (!File.Exists(originGameInstallLocation))
{
// then we have a problem as we cannot locate the game exe file to start!
logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} has gameexe we cannot find! originGameInstallLocation is {originGameInstallLocation}.");
continue;
}
originGame.GameExePath = originGameInstallLocation;
} if (parFiles.Length == 0)
catch (SecurityException ex)
{ {
logger.Warn(ex, $"OriginLibrary/LoadInstalledGames: The user does not have the permissions required to read the Origin Game location registry key {originGameInstallKeyName}, so skipping game"); // No par files found :( So lets just try and pick the first exe in the mnfst.txt instead.
continue; logger.Trace($"OriginLibrary/LoadInstalledGames: No .par files in the {originGame.GameInstallDir} directory, so attempting to get the first exe in the mnsft.txt file.");
} MatchCollection mc = Regex.Matches(mnsftData, @"""([^/]*).exe""");
catch (ObjectDisposedException ex) if (mc.Count > 0)
{ {
logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin ClientPath registry key (closed keys cannot be accessed), so skipping game"); originGame.GameExePath = mc[0].Groups[1].ToString();
continue; logger.Trace($"OriginLibrary/LoadInstalledGames: originGame.GameExePath = {originGame.GameExePath }");
} }
catch (IOException ex) logger.Error($"OriginLibrary/LoadInstalledGames: Couldn't find any *.par files in the game directory {originGame.GameInstallDir} . Skipping this game");
}
else if (parFiles.Length > 0)
{ {
logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The Origin ClientPath registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check, so skipping game"); // Par files found! So lets just try and pick the exe that has the same basename as the par file in the mnfst.txt.
continue; string parFileBaseName = Path.GetFileNameWithoutExtension(parFiles[0]);
} logger.Trace($"OriginLibrary/LoadInstalledGames: Looking for {parFileBaseName}.exe in the mnsft.txt file as it matches {parFiles[0]}.");
catch (UnauthorizedAccessException ex) MatchCollection mc = Regex.Matches(mnsftData, $@"""{parFiles[0]}.exe""");
if (mc.Count > 0)
{ {
logger.Warn(ex, "OriginLibrary/LoadInstalledGames: The user does not have the necessary registry rights to check whether Origin is installed, so skipping game"); originGame.GameExePath = mc[0].Groups[1].ToString();
continue; logger.Trace($"OriginLibrary/LoadInstalledGames: originGame.GameExePath = {originGame.GameExePath }");
} }
logger.Error($"OriginLibrary/LoadInstalledGames: Couldn't find any *.par files in the game directory {originGame.GameInstallDir} . Skipping this game");
} }
else else
{ {
logger.Warn($"OriginLibrary/LoadInstalledGames: Game File Path {gameFilePath} starts with '[HKEY_CURRENT_USER' but didn't match the regex when it should have, so skipping game"); logger.Error($"OriginLibrary/LoadInstalledGames: Count of par files was less than zero. Skipping this game");
continue; continue;
} }
} }
else else
{ {
// If we get here, then the gameFilepath is the actual filepath! So we just copy it. // This is a manifest file we cannot process as we've never seen it before
logger.Trace($"OriginLibrary/LoadInstalledGames: Game File Path {gameFilePath} doesn't start with '[HKEY_LOCAL_MACHINE' or '[HKEY_CURRENT_USER' so it must be aplain file path"); logger.Error($"OriginLibrary/LoadInstalledGames: Unrecognised installer.xml manifest version for {gameInstallerData}. Skipping processing file.");
originGame.GameExePath = gameFilePath; continue;
} }
if (!File.Exists(originGame.GameExePath)) if (!File.Exists(originGame.GameExePath))
{ {
logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} found but no game exe found at originGame.GameExePath {originGame.GameExePath} so skipping game"); logger.Warn($"OriginLibrary/LoadInstalledGames: Origin game with ID {originGame.GameID} found but no game exe found at originGame.GameExePath {originGame.GameExePath} so skipping game");
@ -739,7 +725,7 @@ namespace DisplayMagician.GameLibraries
return true; return true;
} }
public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal) /*public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal)
{ {
string address = $"origin2://game/launch?offerIds={game.Id}"; string address = $"origin2://game/launch?offerIds={game.Id}";
if (String.IsNullOrWhiteSpace(gameArguments)) if (String.IsNullOrWhiteSpace(gameArguments))
@ -749,6 +735,177 @@ namespace DisplayMagician.GameLibraries
Process gameProcess = Process.Start(address); Process gameProcess = Process.Start(address);
gameProcess.PriorityClass = processPriority; gameProcess.PriorityClass = processPriority;
return gameProcess; return gameProcess;
}*/
public override List<Process> StartGame(Game game, string gameArguments = "", ProcessPriority processPriority = ProcessPriority.Normal)
{
string address = $"origin2://game/launch?offerIds={game.Id}";
if (!String.IsNullOrWhiteSpace(gameArguments))
{
address += @"/" + gameArguments;
}
//Process gameProcess = Process.Start(address);
List<Process> gameProcesses = ProcessUtils.StartProcess(address, null, processPriority);
return gameProcesses;
}
private string GetActualFilePath(string gameFilePath)
{
string originGameInstallLocation = "";
// Check whether gameFilePath contains a registry key! Cause if it does we need to lookup the path there instead
if (gameFilePath.StartsWith("[HKEY_LOCAL_MACHINE"))
{
logger.Trace($"OriginLibrary/GetActualFilePath: Game File Path starts with a registery key so needs to be translated");
// The filePath contains a registry key lookup that we need to execute and replace
string originGameInstallKeyNameAndValue = "";
string originGameRestOfFile = "";
MatchCollection mc = Regex.Matches(gameFilePath, @"\[HKEY_LOCAL_MACHINE\\(.*)\](.*)");
if (mc.Count > 0)
{
// Split the Reg key bit from the File Path bit
originGameInstallKeyNameAndValue = mc[0].Groups[1].ToString();
logger.Trace($"OriginLibrary/GetActualFilePath: originGameInstallKeyNameAndValue = {originGameInstallKeyNameAndValue}");
originGameRestOfFile = mc[0].Groups[2].ToString();
logger.Trace($"OriginLibrary/GetActualFilePath: originGameRestOfFile = {originGameRestOfFile}");
if (originGameInstallKeyNameAndValue == null || originGameInstallKeyNameAndValue == "")
{
// then we have a problem and we need to continue and ignore this game
logger.Warn($"OriginLibrary/GetActualFilePath: Origin game path {gameFilePath} has registry key but we can't extract it!");
return null;
}
// Split the reg key from the value name
string originGameInstallKeyName = "";
string originGameInstallKeyValue = "";
mc = Regex.Matches(originGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)");
if (mc.Count > 0)
{
originGameInstallKeyName = mc[0].Groups[1].ToString();
logger.Trace($"OriginLibrary/GetActualFilePath: originGameInstallKeyName = {originGameInstallKeyName }");
originGameInstallKeyValue = mc[0].Groups[2].ToString();
logger.Trace($"OriginLibrary/GetActualFilePath: originGameInstallKeyValue = {originGameInstallKeyValue }");
}
// Lookup the reg key to figure out where the game is installed
try
{
RegistryKey originGameInstallKey = Registry.LocalMachine.OpenSubKey(originGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree);
if (originGameInstallKey == null)
{
// then we have a problem as we cannot find the game exe location!
logger.Warn($"OriginLibrary/GetActualFilePath: Origin game path {gameFilePath} has a install reg key we cannot find! originGameInstallKey is {gameFilePath} and originGameInstallKeyValue is {originGameInstallKeyValue}.");
return null;
}
originGameInstallLocation = Path.Combine(originGameInstallKey.GetValue(originGameInstallKeyValue).ToString(), originGameRestOfFile);
if (!File.Exists(originGameInstallLocation))
{
// then we have a problem as we cannot locate the game exe file to start!
logger.Warn($"OriginLibrary/GetActualFilePath: Origin game path {gameFilePath} has gameexe we cannot find! originGameInstallLocation is {originGameInstallLocation}.");
return null;
}
return originGameInstallLocation;
}
catch (SecurityException ex)
{
logger.Warn(ex, $"OriginLibrary/GetActualFilePath: The user does not have the permissions required to read the Origin Game location registry key {originGameInstallKeyName}, so skipping game");
return null;
}
catch (ObjectDisposedException ex)
{
logger.Warn(ex, "OriginLibrary/GetActualFilePath: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin ClientPath registry key (closed keys cannot be accessed), so skipping game");
return null;
}
catch (IOException ex)
{
logger.Warn(ex, "OriginLibrary/GetActualFilePath: The Origin ClientPath registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check, so skipping game");
return null;
}
catch (UnauthorizedAccessException ex)
{
logger.Warn(ex, "OriginLibrary/GetActualFilePath: The user does not have the necessary registry rights to check whether Origin is installed, so skipping game");
return null;
}
}
else
{
logger.Warn($"OriginLibrary/GetActualFilePath: Game File Path {gameFilePath} starts with '[HEKY_LOCAL_MACHINE' but didn't match the regex when it should have");
return null;
}
}
else if (gameFilePath.StartsWith("[HKEY_CURRENT_USER"))
{
// The filePath contains a registry key lookup that we need to execute and replace
MatchCollection mc = Regex.Matches(gameFilePath, @"\[HKEY_CURRENT_USER\\(.*)\](.*)");
if (mc.Count > 0)
{
string originGameInstallKeyNameAndValue = mc[0].Groups[1].ToString();
string originGameRestOfFile = mc[0].Groups[2].ToString();
if (originGameInstallKeyNameAndValue == null)
{
// then we have a problem and we need to continue and ignore this game
logger.Warn($"OriginLibrary/GetActualFilePath: Origin game path {gameFilePath} has registry but we can't match it! gameFilePath is {gameFilePath}.");
return null;
}
mc = Regex.Matches(originGameInstallKeyNameAndValue, @"(.*)\\([^\\]*)");
string originGameInstallKeyName = mc[0].Groups[1].ToString();
string originGameInstallKeyValue = mc[0].Groups[2].ToString();
try
{
RegistryKey originGameInstallKey = Registry.LocalMachine.OpenSubKey(originGameInstallKeyName, RegistryKeyPermissionCheck.ReadSubTree);
if (originGameInstallKey == null)
{
// then we have a problem as we cannot find the game exe location!
logger.Warn($"OriginLibrary/GetActualFilePath: Origin game path {gameFilePath} has a install reg key we cannot find! originGameInstallKey is {gameFilePath} and originGameInstallKeyValue is {originGameInstallKeyValue}.");
return null;
}
originGameInstallLocation = Path.Combine(originGameInstallKey.GetValue(originGameInstallKeyValue).ToString(), originGameRestOfFile);
if (!File.Exists(originGameInstallLocation))
{
// then we have a problem as we cannot locate the game exe file to start!
logger.Warn($"OriginLibrary/GetActualFilePath: Origin game path {gameFilePath} has gameexe we cannot find! originGameInstallLocation is {originGameInstallLocation}.");
return null;
}
return originGameInstallLocation;
}
catch (SecurityException ex)
{
logger.Warn(ex, $"OriginLibrary/GetActualFilePath: The user does not have the permissions required to read the Origin Game location registry key {originGameInstallKeyName}, so skipping game");
return null;
}
catch (ObjectDisposedException ex)
{
logger.Warn(ex, "OriginLibrary/GetActualFilePath: The Microsoft.Win32.RegistryKey is closed when trying to access the Origin ClientPath registry key (closed keys cannot be accessed), so skipping game");
return null;
}
catch (IOException ex)
{
logger.Warn(ex, "OriginLibrary/GetActualFilePath: The Origin ClientPath registry key has been marked for deletion so we cannot access the value dueing the OriginLibrary check, so skipping game");
return null;
}
catch (UnauthorizedAccessException ex)
{
logger.Warn(ex, "OriginLibrary/GetActualFilePath: The user does not have the necessary registry rights to check whether Origin is installed, so skipping game");
return null;
}
}
else
{
logger.Warn($"OriginLibrary/GetActualFilePath: Game File Path {gameFilePath} starts with '[HKEY_CURRENT_USER' but didn't match the regex when it should have, so skipping game");
return null;
}
}
else
{
// If we get here, then the gameFilepath is the actual filepath! So we just copy it.
logger.Trace($"OriginLibrary/GetActualFilePath: Game File Path {gameFilePath} doesn't start with '[HKEY_LOCAL_MACHINE' or '[HKEY_CURRENT_USER' so it must be aplain file path");
return gameFilePath;
}
} }
#endregion #endregion

View File

@ -633,14 +633,17 @@ namespace DisplayMagician.GameLibraries
{ {
// Check the entry is actually a directory // Check the entry is actually a directory
string steamLibraryPath = Regex.Unescape(steamLibraryMatch.Groups[1].Value); string steamLibraryPath = Regex.Unescape(steamLibraryMatch.Groups[1].Value);
if (Directory.Exists(steamLibraryPath)) if (!steamLibraryPath.Equals(_steamPath) && Directory.Exists(steamLibraryPath))
{ {
logger.Info($"SteamLibrary/LoadInstalledGames: Found additional steam library {steamLibraryPath}"); if (!steamLibrariesPaths.Contains(steamLibraryPath))
{
logger.Info($"SteamLibrary/LoadInstalledGames: Found additional steam library {steamLibraryPath} in the {_steamLibraryFoldersVdfFile} VDF file ");
steamLibrariesPaths.Add(steamLibraryPath); steamLibrariesPaths.Add(steamLibraryPath);
} }
}
else else
{ {
logger.Trace($"SteamLibrary/LoadInstalledGames: Found what it thought was an additional steam library {steamLibraryPath} in {_steamLibraryFoldersVdfFile}, but it didn't exist on the file system"); logger.Trace($"SteamLibrary/LoadInstalledGames: Found what it thought was an additional steam library {steamLibraryPath} in {_steamLibraryFoldersVdfFile}, but it didn't exist on the file system, or was already added");
} }
} }
} }
@ -669,18 +672,19 @@ namespace DisplayMagician.GameLibraries
if (steamLibraryMatch.Success) if (steamLibraryMatch.Success)
{ {
string steamLibraryPath = Regex.Unescape(steamLibraryMatch.Groups[1].Value); string steamLibraryPath = Regex.Unescape(steamLibraryMatch.Groups[1].Value);
if (Directory.Exists(steamLibraryPath)) if (!steamLibraryPath.Equals(_steamPath) && Directory.Exists(steamLibraryPath))
{ {
logger.Info($"SteamLibrary/LoadInstalledGames: Found additional steam library {steamLibraryPath}"); logger.Info($"SteamLibrary/LoadInstalledGames: Found additional steam library {steamLibraryPath}");
// Check if the steam library is already in the list! // Check if the steam library is already in the list!
if (!steamLibrariesPaths.Contains(steamLibraryPath)) if (!steamLibrariesPaths.Contains(steamLibraryPath))
{ {
logger.Info($"SteamLibrary/LoadInstalledGames: Aadditional steam library {steamLibraryPath}");
steamLibrariesPaths.Add(steamLibraryPath); steamLibrariesPaths.Add(steamLibraryPath);
} }
} }
else else
{ {
logger.Trace($"SteamLibrary/LoadInstalledGames: Found what it thought was an additional steam library {steamLibraryPath} in {_steamConfigVdfFile}, but it didn't exist on the file system"); logger.Trace($"SteamLibrary/LoadInstalledGames: Found what it thought was an additional steam library {steamLibraryPath} in {_steamConfigVdfFile}, but it didn't exist on the file system, or was already added");
} }
} }
} }
@ -766,7 +770,8 @@ namespace DisplayMagician.GameLibraries
} }
// And we add the Game to the list of games we have! // And we add the Game to the list of games we have!
_allSteamGames.Add(new SteamGame(steamGameId, steamGameName, steamGameExe, steamGameIconPath)); 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}"); logger.Debug($"SteamLibrary/LoadInstalledGames: Adding Steam Game with game id {steamGameId}, name {steamGameName}, game exe {steamGameExe} and icon path {steamGameIconPath}");
} }
} }
@ -808,16 +813,16 @@ namespace DisplayMagician.GameLibraries
} }
public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal) public override List<Process> StartGame(Game game, string gameArguments = "", ProcessPriority processPriority = ProcessPriority.Normal)
{ {
string address = $@"steam://rungameid/{game.Id}"; string address = $@"steam://rungameid/{game.Id}";
if (!String.IsNullOrWhiteSpace(gameArguments)) if (!String.IsNullOrWhiteSpace(gameArguments))
{ {
address += @"//" + gameArguments; address += @"//" + gameArguments;
} }
Process gameProcess = Process.Start(address); //Process gameProcess = Process.Start(address);
gameProcess.PriorityClass = processPriority; List<Process> gameProcesses = ProcessUtils.StartProcess(address,null,processPriority);
return gameProcess; return gameProcesses;
} }
#endregion #endregion

View File

@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// This configuration parses logic is kept here for possible future use
// It was really difficult to find this logic in some obscure webpage
// so I'm keeping it in case I need it later.
namespace DisplayMagician.GameLibraries.UplayConfigurationParser
{
class UplayConfigurationParser
{
/*def _convert_data(self, data):
# calculate object size (konrad's formula)
if data > 256 * 256:
data = data - (128 * 256 * math.ceil(data / (256 * 256)))
data = data - (128 * math.ceil(data / 256))
else:
if data > 256:
data = data - (128 * math.ceil(data / 256))
return data*/
internal static decimal ConvertData (decimal data)
{
if (data > 65536)
{
data = data - (128 * 256 * Math.Ceiling(data / 65536));
}
else if (data > 256)
{
data = data - (128 * Math.Ceiling(data / 256));
}
return data;
}
/*def _parse_configuration_header(self, header, second_eight= False):
try:
offset = 1
multiplier = 1
record_size = 0
tmp_size = 0
if second_eight:
while header[offset] != 0x08 or(header[offset] == 0x08 and header[offset + 1] == 0x08) :
record_size += header[offset] * multiplier
multiplier *= 256
offset += 1
tmp_size += 1
else:
while header[offset] != 0x08 or record_size == 0:
record_size += header[offset] * multiplier
multiplier *= 256
offset += 1
tmp_size += 1
record_size = self._convert_data(record_size)
offset += 1 # skip 0x08
# look for launch_id
multiplier = 1
launch_id = 0
while header[offset] != 0x10 or header[offset + 1] == 0x10:
launch_id += header[offset] * multiplier
multiplier *= 256
offset += 1
launch_id = self._convert_data(launch_id)
offset += 1 # skip 0x10
multiplier = 1
launch_id_2 = 0
while header[offset] != 0x1A or(header[offset] == 0x1A and header[offset + 1] == 0x1A) :
launch_id_2 += header[offset] * multiplier
multiplier *= 256
offset += 1
launch_id_2 = self._convert_data(launch_id_2)
#if object size is smaller than 128b, there might be a chance that secondary size will not occupy 2b
if record_size - offset < 128 <= record_size:
tmp_size -= 1
record_size += 1
# we end up in the middle of header, return values normalized
# to end of record as well real yaml size and game launch_id
return record_size - offset, launch_id, launch_id_2, offset + tmp_size + 1
except:
# something went horribly wrong, do not crash it,
# just return 0s, this way it will be handled later in the code
# 10 is to step a little in configuration file in order to find next game
return 0, 0, 0, 10*/
//internal static decimal ParseConfigurationHeader(decimal data);
}
}

View File

@ -0,0 +1,144 @@
using ProtoBuf;
using System.Collections.Generic;
namespace DisplayMagician.GameLibraries
{
// #####################################################################################################
// # This set of classes are used for deserialising Uplay protobuf files
// #####################################################################################################
[ProtoContract]
public class UplayCachedGame
{
[ProtoMember(1)]
public uint UplayId { get; set; }
[ProtoMember(2)]
public uint InstallId { get; set; }
[ProtoMember(3)]
public string GameInfo { get; set; }
}
[ProtoContract]
public class UplayCachedGameCollection
{
[ProtoMember(1)]
public List<UplayCachedGame> Games { get; set; }
}
// #####################################################################################################
// # This set of classes are used for deserialising Uplay YAML enbedded within the protobuf file format
// #####################################################################################################
public class ProductInformation
{
public class Executable
{
public class Path
{
public string relative;
}
public class WorkingDirectory
{
public string register;
public string append;
}
public Path path;
public WorkingDirectory working_directory;
public string internal_name;
public string description;
public string shortcut_name;
public string icon_image;
}
public class StartGameItem
{
public bool after_game_report_enabled;
public bool overlay_supported;
public bool overlay_product_activation_enabled;
public bool overlay_required;
public bool overlay_shop_enabled;
public bool legacy_ticket_enabled;
public List<Executable> executables;
}
public class StartGame
{
public StartGameItem online;
public StartGameItem offline;
}
public class DigitalDistribution
{
public int version;
}
public class Localization
{
public string l1;
}
public class Club
{
public bool enabled;
}
public class Addon
{
public uint id;
public bool is_visible;
public string name;
public string description;
public string thumb_image;
}
public class Uplay
{
public string game_code;
public string achievements;
public string achievements_sync_id;
}
public class ThirdPartyPlatform
{
public string name;
}
public class Product
{
public string name;
public string background_image;
public string thumb_image;
public string logo_image;
public string dialog_image;
public string icon_image;
public ThirdPartyPlatform third_party_platform;
public string sort_string;
public bool cloud_saves;
public string forum_url;
public string homepage_url;
public string facebook_url;
public string help_url;
public bool after_game_report_ad;
public bool force_safe_mode;
public bool uplay_pipe_required;
public bool show_properties;
public bool game_streaming_enabled;
public Uplay uplay;
public List<Addon> addons;
public Club club;
public DigitalDistribution digital_distribution;
public bool is_ulc;
public bool is_visible;
public StartGame start_game;
}
public string version;
public Product root;
public Dictionary<string,Localization> localizations;
public uint uplay_id;
public uint install_id;
}
}

View File

@ -6,6 +6,10 @@ using Microsoft.Win32;
using System.IO; using System.IO;
using System.Security; using System.Security;
using System.Diagnostics; using System.Diagnostics;
using ProtoBuf;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using System.Globalization;
namespace DisplayMagician.GameLibraries namespace DisplayMagician.GameLibraries
{ {
@ -404,6 +408,52 @@ namespace DisplayMagician.GameLibraries
} }
public bool GetInstallDirFromRegKey(string regKeyPath, out string filePath)
{
filePath = "";
RegistryKey uplayGameInstallKey;
if (regKeyPath.StartsWith("HKEY_LOCAL_MACHINE"))
{
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKLM reg key {regKeyPath}");
string regKeyText = regKeyPath.Replace(@"HKEY_LOCAL_MACHINE\", "");
uplayGameInstallKey = Registry.LocalMachine.OpenSubKey(regKeyText, RegistryKeyPermissionCheck.ReadSubTree);
}
else if (regKeyPath.StartsWith("HKEY_CURRENT_USER"))
{
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Accessing HKCU reg key {regKeyPath}");
string regKeyText = regKeyPath.Replace(@"HKEY_CURRENT_USER\", "");
uplayGameInstallKey = Registry.LocalMachine.OpenSubKey(regKeyText, RegistryKeyPermissionCheck.ReadSubTree);
}
else
{
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Skipping processing as regkey supplied was odd: {regKeyPath}");
return false;
}
// If the key doesn't exist we skip it as the game isn't installed any longer!
if (uplayGameInstallKey == null)
{
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: Skipping Uplay Game as it isn't installed at the moment (it was uninstalled at some point)");
return false;
}
// From that we lookup the actual game path
string gameInstallDir = uplayGameInstallKey.GetValue("InstallDir", "").ToString();
logger.Trace($"UplayLibrary/GetInstallDirFromRegKey: gameInstallDir found = {gameInstallDir}");
if (!String.IsNullOrWhiteSpace(gameInstallDir))
{
filePath = Path.GetFullPath(gameInstallDir).TrimEnd('\\');
return true;
}
else
{
logger.Warn($"UplayLibrary/GetInstallDirFromRegKey: gameInstallDir is null or all whitespace!");
return false;
}
}
public override bool LoadInstalledGames() public override bool LoadInstalledGames()
{ {
try try
@ -475,210 +525,330 @@ namespace DisplayMagician.GameLibraries
// Access {installdir}\\cache\\configuration\\configurations file // Access {installdir}\\cache\\configuration\\configurations file
string uplayConfigFilePath = _uplayPath + @"cache\configuration\configurations"; string uplayConfigFilePath = _uplayPath + @"cache\configuration\configurations";
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Config File Path = {uplayConfigFilePath }"); logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Config File Path = {uplayConfigFilePath }");
string uplayConfigFileString = File.ReadAllText(uplayConfigFilePath);
uplayConfigFileString = uplayConfigFileString.Remove(0, 12);
string[] dividingText = { "version: 2.0" };
List<string> uplayConfigFile = uplayConfigFileString.Split(dividingText,StringSplitOptions.RemoveEmptyEntries).ToList();
// Split the file into records at the SOH unicode character
//List<string> uplayConfigFile = uplayConfigFileString.Split((Char)1).ToList();
// Go through every record and attempt to parse it var deserializer = new DeserializerBuilder()
foreach (string uplayEntry in uplayConfigFile) { .IgnoreUnmatchedProperties()
// Skip any Uplay entry records that don't start with 'version:' .Build();
//if (!uplayEntry.StartsWith("version:",StringComparison.OrdinalIgnoreCase))
// continue;
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Entry that starts with 'version: 2.0') = {uplayEntry}"); using (var file = File.OpenRead(uplayConfigFilePath))
//Split the record into entrylines
string[] delimeters = { "\r\n" };
List<string> uplayEntryLines = uplayEntry.Split(delimeters, System.StringSplitOptions.RemoveEmptyEntries).ToList();
// Skip any records NOT containing an entryline with ' start_game:' (note 2 leading spaces)
// All games contain a start_game entry
if (!uplayEntryLines.Exists(a => a.StartsWith(" start_game:")))
continue;
// Skip any records containing an entryline with ' third_party_platform:' (note 2 leading spaces)
// We only want the native uplay games....
if (uplayEntryLines.Exists(a => a.StartsWith(" third_party_platform:")))
continue;
// if we get here then we have a real game to parse!
// Yay us :).
// First we want to know the index of the start_game entry to use later
//int startGameIndex = uplayEntryLines.FindIndex(a => a.StartsWith(" start_game:"));
MatchCollection mc;
// First we check if there are any localization CONSTANTS that we will need to map later.
Dictionary<string, string> localizations = new Dictionary<string, string>();
int localizationsIndex = uplayEntryLines.FindIndex(a => a == "localizations:");
// If there are localizations, then we need to store them for later
if (localizationsIndex != -1)
{ {
// grab the localizations: -> default: entries to use as a lookup table for the info we need try
int defaultIndex = localizationsIndex + 1; {
int currentIndex = defaultIndex + 1; var gameCollection = ProtoBuf.Serializer.Deserialize<UplayCachedGameCollection>(file).Games;
foreach (var item in gameCollection)
{
if (!String.IsNullOrEmpty(item.GameInfo))
{
ProductInformation productInfo;
try
{
productInfo = deserializer.Deserialize<ProductInformation>(item.GameInfo);
var root = productInfo.root;
// Grab all EntryLines with 4 leading spaces (these are all the localizations)
while (uplayEntryLines[currentIndex].StartsWith(" ")){
string[] split = uplayEntryLines[currentIndex].Split(':');
localizations.Add(split[0].Trim(), split[1].Trim());
currentIndex++;
}
}
// for each game record grab:
GameAppInfo uplayGameAppInfo = new GameAppInfo();
// find the exe name looking at root: -> start_game: -> online: -> executables: -> path: -> relative: (get ACU.exe)
// Lookup the Game registry key from looking at root: -> start_game: -> online: -> executables: -> working_directory: -> register: (get HKEY_LOCAL_MACHINE\SOFTWARE\Ubisoft\Launcher\Installs\720\InstallDir)
// Extract the GameAppID from the number in the working directory (e.g. 720)
// Lookup the Game install path by reading the game registry key: D:/Ubisoft Game Launcher/Assassin's Creed Unity/
// join the Game install path and the exe name to get the full game exe path: D:/Ubisoft Game Launcher/Assassin's Creed Unity/ACU.exe
//if (uplayEntryLines.Find (a => a.StartsWith(" icon_image:", StringComparison.InvariantCultureIgnoreCase)))
bool gotGameIconPath = false;
bool gotGameName = false;
string gameFileName = "";
bool gotGameFileName = false;
string gameId = ""; string gameId = "";
bool gotGameId = false; string gameName = "";
string gameRegistryKey = ""; string gameExePath = "";
bool gotGameRegistryKey = false; string gameIconPath = "";
for (int i = 0; i <= 50; i++)
// Try finding the Game Name using the localisation currently in use as a first step
logger.Trace($"UplayLibrary/LoadInstalledGames: Looking for the Uplay game name.");
string currentLang = CultureInfo.CurrentCulture.Name;
foreach (var lang in productInfo.localizations)
{ {
// Stop this loop once we have both filname and gameid // If we find the same language as the user is using, then let's use that!
if (gotGameFileName && gotGameId && gotGameIconPath && gotGameName && gotGameRegistryKey) if (lang.Key.Equals(currentLang))
{ {
logger.Trace($"UplayLibrary/LoadInstalledGames: We got all the entries: gameFileName = {gameFileName } && gameId = {gameId } && gameIconPath = {uplayGameAppInfo.GameIconPath} && gameName = {uplayGameAppInfo.GameName}"); gameName = lang.Value.l1;
logger.Trace($"UplayLibrary/LoadInstalledGames: We found the Uplay game name '{gameName}' in the user's language of {currentLang}.");
break; break;
} }
// This line contains the Game Name
if (uplayEntryLines[i].StartsWith(" name:", StringComparison.OrdinalIgnoreCase) && !gotGameName)
{
mc = Regex.Matches(uplayEntryLines[i], @" name\: (.*)");
if (mc.Count > 0)
{
uplayGameAppInfo.GameName = mc[0].Groups[1].ToString();
// if the name contains a localization reference, then dereference it
if (localizations.ContainsKey(uplayGameAppInfo.GameName))
{
uplayGameAppInfo.GameName = localizations[uplayGameAppInfo.GameName];
} }
logger.Trace($"UplayLibrary/LoadInstalledGames: Found uplayGameAppInfo.GameName = {uplayGameAppInfo.GameName}"); // If the gameName isn't available in the users language, then we go for default
gotGameName = true; if (String.IsNullOrEmpty(gameName) && productInfo.localizations.ContainsKey("default"))
}
}
else if (uplayEntryLines[i].StartsWith(" icon_image:", StringComparison.OrdinalIgnoreCase) && !gotGameIconPath)
{ {
mc = Regex.Matches(uplayEntryLines[i], @"icon_image: (.*)"); gameName = productInfo.localizations["default"].l1;
if (mc.Count > 0) if (!String.IsNullOrEmpty(gameName))
{ {
string iconImageFileName = mc[0].Groups[1].ToString(); logger.Trace($"UplayLibrary/LoadInstalledGames: Looking for the Uplay game name with the en language as the local language didn't work. We found game name '{gameName}'. ");
// if the icon_image contains a localization reference, then dereference it
if (localizations.ContainsKey(iconImageFileName))
{
iconImageFileName = localizations[iconImageFileName];
logger.Trace($"UplayLibrary/LoadInstalledGames: Found iconImageFile = {iconImageFileName }");
}
//61fdd16f06ae08158d0a6d476f1c6bd5.ico
string uplayGameIconPath = _uplayPath + @"data\games\" + iconImageFileName;
if (File.Exists(uplayGameIconPath) && uplayGameIconPath.EndsWith(".ico"))
{
uplayGameAppInfo.GameIconPath = uplayGameIconPath;
logger.Trace($"UplayLibrary/LoadInstalledGames: Found uplayGameAppInfo.GameUplayIconPath = {uplayGameAppInfo.GameIconPath }");
}
gotGameIconPath = true;
}
}
// This line contains the filename
else if (uplayEntryLines[i].StartsWith(" relative:") && !gotGameFileName)
{
mc = Regex.Matches(uplayEntryLines[i], @"relative: (.*)");
if (mc.Count > 0)
{
gameFileName = mc[0].Groups[1].ToString();
gotGameFileName = true;
logger.Trace($"UplayLibrary/LoadInstalledGames: Found gameFileName = {gameFileName}");
}
}
// This line contains the registryKey
else if (uplayEntryLines[i].StartsWith(" register: HKEY_LOCAL_MACHINE") && !gotGameId)
{
// Lookup the GameId within the registry key
mc = Regex.Matches(uplayEntryLines[i], @"Installs\\(\d+)\\InstallDir");
if (mc.Count > 0)
{
gameId = mc[0].Groups[1].ToString();
gotGameId = true;
logger.Trace($"UplayLibrary/LoadInstalledGames: Found gameId = {gameId}");
}
mc = Regex.Matches(uplayEntryLines[i], @"HKEY_LOCAL_MACHINE\\(.*?)\\InstallDir");
if (mc.Count > 0)
{
gameRegistryKey = mc[0].Groups[1].ToString();
gameRegistryKey = gameRegistryKey.Replace(@"Ubisoft", @"WOW6432Node\Ubisoft");
gotGameRegistryKey = true;
logger.Trace($"UplayLibrary/LoadInstalledGames: Found gameRegistryKey = {gameRegistryKey}");
}
}
}
logger.Trace($"UplayLibrary/LoadInstalledGames: gameId = {gameId}");
logger.Trace($"UplayLibrary/LoadInstalledGames: gameFileName = {gameFileName}");
logger.Trace($"UplayLibrary/LoadInstalledGames: gameGameIconPath = {uplayGameAppInfo.GameIconPath}");
logger.Trace($"UplayLibrary/LoadInstalledGames: gameRegistryKey = {gameRegistryKey}");
if (gotGameRegistryKey)
{
// Now we need to lookup the game install path in registry using the game reg we got above
// We assume its 64-bit OS too (not 32bit)
using (RegistryKey uplayGameInstallKey = Registry.LocalMachine.OpenSubKey(gameRegistryKey, RegistryKeyPermissionCheck.ReadSubTree))
{
// If the key doesn't exist we skip it as the game isn't installed any longer!
if (uplayGameInstallKey == null)
{
logger.Trace($"UplayLibrary/LoadInstalledGames: Skipping Uplay Game {uplayGameAppInfo.GameName} as it isn't installed at the moment (it was uninstalled at some point)");
continue;
}
// If we get here, then we have a real game.
foreach (string regKeyName in uplayGameInstallKey.GetValueNames())
{
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameInstallKey[{regKeyName}] = {uplayGameInstallKey.GetValue(regKeyName)}");
}
// From that we lookup the actual game path
string gameInstallDir = uplayGameInstallKey.GetValue("InstallDir", "").ToString();
logger.Trace($"UplayLibrary/LoadInstalledGames: gameInstallDir found = {gameInstallDir}");
if (!String.IsNullOrWhiteSpace(gameInstallDir))
{
uplayGameAppInfo.GameInstallDir = Path.GetFullPath(gameInstallDir).TrimEnd('\\');
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameAppInfo.GameInstallDir = {uplayGameAppInfo.GameInstallDir }");
uplayGameAppInfo.GameExePath = Path.Combine(uplayGameAppInfo.GameInstallDir, gameFileName);
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameAppInfo.GameExe = {uplayGameAppInfo.GameExePath}");
uplayGameAppInfo.GameID = gameId;
logger.Trace($"UplayLibrary/LoadInstalledGames: uplayGameAppInfo.GameID = {uplayGameAppInfo.GameID }");
} }
else else
{ {
logger.Warn($"UplayLibrary/LoadInstalledGames: gameInstallDir is null or all whitespace!"); logger.Trace($"UplayLibrary/LoadInstalledGames: Looking for the Uplay game name with the en language as the local language didn't work. We found no en language. ");
}
} }
// 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)
{
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)
{
string exePath = "";
// Check if its a full path or a relative path
if (!String.IsNullOrEmpty(executable.path.relative))
{
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
{
// 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))
{
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 // 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! // And we add the Game to the list of games we have!
_allGames.Add(new UplayGame(uplayGameAppInfo.GameID, uplayGameAppInfo.GameName, uplayGameAppInfo.GameExePath, uplayGameAppInfo.GameIconPath)); _allGames.Add(new UplayGame(gameId, gameName, gameExePath, gameIconPath));
logger.Debug($"UplayLibrary/LoadInstalledGames: Adding Uplay Game with game id {uplayGameAppInfo.GameID}, name {uplayGameAppInfo.GameName}, game exe {uplayGameAppInfo.GameExePath} and icon path {uplayGameAppInfo.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)
{
string exePath = "";
// Check if its a full path or a relative path
if (!String.IsNullOrEmpty(executable.path.relative))
{
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
{
// 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))
{
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;
}
}
else
{
logger.Trace($"UplayLibrary/LoadInstalledGames: Uplay Entry {gameName} doesn't have any executables associated with it! We have to skip adding this game.");
continue;
}
}
catch (Exception ex)
{
// 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}");
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}");
}
}
}
}
}
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!");
return false;
}
} }
logger.Info($"UplayLibrary/LoadInstalledGames: Found {_allGames.Count} installed Uplay games"); logger.Info($"UplayLibrary/LoadInstalledGames: Found {_allGames.Count} installed Uplay games");
@ -717,7 +887,7 @@ namespace DisplayMagician.GameLibraries
return true; return true;
} }
public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal) /*public override Process StartGame(Game game, string gameArguments = "", ProcessPriorityClass processPriority = ProcessPriorityClass.Normal)
{ {
string address = $@"uplay://launch/{game.Id}"; string address = $@"uplay://launch/{game.Id}";
if (String.IsNullOrWhiteSpace(gameArguments)) if (String.IsNullOrWhiteSpace(gameArguments))
@ -732,6 +902,21 @@ namespace DisplayMagician.GameLibraries
gameProcess.PriorityClass = processPriority; gameProcess.PriorityClass = processPriority;
return gameProcess; return gameProcess;
}*/
public override List<Process> StartGame(Game game, string gameArguments = "", ProcessPriority processPriority = ProcessPriority.Normal)
{
string address = $@"uplay://launch/{game.Id}";
if (String.IsNullOrWhiteSpace(gameArguments))
{
address += @"/" + gameArguments;
}
else
{
address += "/0";
}
List<Process> gameProcesses = ProcessUtils.StartProcess(address, null, processPriority);
return gameProcesses;
} }
#endregion #endregion

View File

@ -12,6 +12,7 @@ using TsudaKageyu;
using DisplayMagicianShared; using DisplayMagicianShared;
using MintPlayer.IconUtils; using MintPlayer.IconUtils;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.IO;
namespace DisplayMagician namespace DisplayMagician
{ {
@ -142,19 +143,40 @@ namespace DisplayMagician
return newBitmap; return newBitmap;
} }
public static Bitmap GetMeABitmapFromFile(string fileNameAndPath) public static List<ShortcutBitmap> GetMeAllBitmapsFromFile(string fileNameAndPath)
{ {
if (String.IsNullOrWhiteSpace(fileNameAndPath)) if (String.IsNullOrWhiteSpace(fileNameAndPath))
{ {
logger.Warn($"ShortcutItem/GetMeABitmapFromFile: Bitmap fileNameAndPath is empty! Unable to get the icon from the file."); logger.Warn($"ShortcutItem/GetMeAllBitmapsFromFile: Bitmap fileNameAndPath is empty! Unable to get the bitmaps from the file.");
return null; return new List<ShortcutBitmap>();
} }
Icon myIcon = null; Icon myIcon = null;
Bitmap bm = null; List<ShortcutBitmap> bmList = new List<ShortcutBitmap>();
Bitmap bmToReturn = new Bitmap(1, 1); int bmCount = 0;
string fileNameOnly = Path.GetFileName(fileNameAndPath);
if (fileNameAndPath.EndsWith(".ico")) if (fileNameAndPath.EndsWith(".jpg") || fileNameAndPath.EndsWith(".gif") || fileNameAndPath.EndsWith(".tif") || fileNameAndPath.EndsWith(".png") || fileNameAndPath.EndsWith(".bmp") ||
fileNameAndPath.EndsWith(".jpeg") || fileNameAndPath.EndsWith(".tiff"))
{
try
{
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: The file we want to get the image from is an image file. Attempting to extract the image from {fileNameAndPath}.");
Bitmap bmap = new Bitmap(fileNameAndPath);
ShortcutBitmap bm = CreateShortcutBitmap(bmap, fileNameOnly, fileNameAndPath, bmCount++);
// Add the shortcutbitmap to the list
bmList.Add(bm);
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: Added new bitmap from the image file {fileNameAndPath} using standard bitmap decoder access method.");
}
catch (Exception ex)
{
logger.Warn(ex, $"ShortcutItem/GetMeABitmapFromFile: Exception while trying to extract the bitmap from an image ({fileNameAndPath})using standard bitmap decoder tools.");
}
}
else if (fileNameAndPath.EndsWith(".ico"))
{ {
try try
@ -162,24 +184,12 @@ namespace DisplayMagician
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: The file we want to get the image from is an icon file. Attempting to extract the icon file from {fileNameAndPath}."); logger.Trace($"ShortcutItem/GetMeABitmapFromFile: The file we want to get the image from is an icon file. Attempting to extract the icon file from {fileNameAndPath}.");
myIcon = new Icon(fileNameAndPath, 256, 256); myIcon = new Icon(fileNameAndPath, 256, 256);
//Icon myIcon = Icon.ExtractAssociatedIcon(fileNameAndPath); //Icon myIcon = Icon.ExtractAssociatedIcon(fileNameAndPath);
bm = myIcon.ToBitmap(); ShortcutBitmap bm = CreateShortcutBitmap(myIcon.ToBitmap(), fileNameOnly, fileNameAndPath, bmCount++);
// Add the shortcutbitmap to the list
bmList.Add(bm);
//myIcon = Icon.ExtractAssociatedIcon(fileNameAndPath); logger.Trace($"ShortcutItem/GetMeABitmapFromFile: Added new bitmap from the icon file {fileNameAndPath} using standard Icon access method.");
//bm = myIcon.ToBitmap();
if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height)
{
bmToReturn = bm;
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using standard Icon access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
else
{
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using standard Icon access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -190,22 +200,13 @@ namespace DisplayMagician
{ {
MultiIcon myMultiIcon = new MultiIcon(); MultiIcon myMultiIcon = new MultiIcon();
SingleIcon mySingleIcon = myMultiIcon.Add("Icon1"); SingleIcon mySingleIcon = myMultiIcon.Add("Icon1");
//mySingleIcon.Load(fileNameAndPath, IconOutputFormat.All);
mySingleIcon.Load(fileNameAndPath); mySingleIcon.Load(fileNameAndPath);
foreach (IconImage myIconImage in mySingleIcon) foreach (IconImage myIconImage in mySingleIcon)
{ {
bm = myIconImage.Image; ShortcutBitmap bm = CreateShortcutBitmap(myIconImage.Image, fileNameOnly, fileNameAndPath, bmCount++);
// Add the shortcutbitmap to the list
if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) bmList.Add(bm);
{ logger.Trace($"ShortcutItem/GetMeABitmapFromFile: Added new bitmap from the icon file {fileNameAndPath} using MultiIcon access method.");
bmToReturn = bm;
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using MultiIcon access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
else
{
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the icon file {fileNameAndPath} using MultiIcon access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
} }
} }
catch (Exception ex) catch (Exception ex)
@ -244,17 +245,11 @@ namespace DisplayMagician
Icon[] allIcons = ie.GetAllIcons(); Icon[] allIcons = ie.GetAllIcons();
foreach (Icon myExtractedIcon in allIcons) foreach (Icon myExtractedIcon in allIcons)
{ {
bm = myExtractedIcon.ToBitmap(); ShortcutBitmap bm = CreateShortcutBitmap(myExtractedIcon.ToBitmap(), fileNameOnly, fileNameAndPath, bmCount++);
// Add the shortcutbitmap to the list
bmList.Add(bm);
if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) logger.Trace($"ShortcutItem/GetMeABitmapFromFile: Add new bitmap from the exe file {fileNameAndPath} using TsudaKageyu.IconExtractor access method.");
{
bmToReturn = bm;
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the exe file {fileNameAndPath} using TsudaKageyu.IconExtractor access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
else
{
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the exe file {fileNameAndPath} using TsudaKageyu.IconExtractor access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
} }
} }
catch (Exception ex) catch (Exception ex)
@ -284,17 +279,11 @@ namespace DisplayMagician
if (myIcon != null) if (myIcon != null)
{ {
bm = myIcon.ToBitmap(); ShortcutBitmap bm = CreateShortcutBitmap(myIcon.ToBitmap(),fileNameOnly,fileNameAndPath,bmCount++);
// Add the shortcutbitmap to the list
bmList.Add(bm);
if (bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height) logger.Trace($"ShortcutItem/GetMeABitmapFromFile: Added new bitmap from the file {fileNameAndPath} using MintPlayer.IconUtils.IconExtractor access method.");
{
bmToReturn = bm;
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the file {fileNameAndPath} using MintPlayer.IconUtils.IconExtractor access method is larger than the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
else
{
logger.Trace($"ShortcutItem/GetMeABitmapFromFile: New bitmap from the file {fileNameAndPath} using MintPlayer.IconUtils.IconExtractor access method is smaller or the same size as the previous one at {bm.Width} x {bm.Height}, so using that instead.");
}
} }
else else
{ {
@ -309,58 +298,114 @@ namespace DisplayMagician
} }
if (bmToReturn == null) return bmList;
{
// If we couldn't get any bitmaps at all
logger.Warn( $"ShortcutItem/GetMeABitmapFromFile: Haven't managed to get a valid icon file so returning null :(.");
return null;
}
else if (bmToReturn.Width == 1 && bmToReturn.Height == 1)
{
// If we couldn't extract anything, so we return null
logger.Warn($"ShortcutItem/GetMeABitmapFromFile: Haven't managed to get a valid icon file so returning null instead of a 1x1 bitmap!.");
return null;
}
else
{
return bmToReturn;
}
} }
public static Bitmap GetMeABitmapFromFile(ArrayList fileNamesAndPaths) public static List<ShortcutBitmap> GetMeAllBitmapsFromFile(ArrayList fileNamesAndPaths)
{ {
Bitmap bmToReturn = null; List<ShortcutBitmap> bmToReturn = null;
if (fileNamesAndPaths.Count == 0) if (fileNamesAndPaths.Count == 0)
{ {
logger.Warn($"ShortcutItem/GetMeABitmapFromFile2: The fileNamesAndPaths list is empty! Can't get the bitmap from the files."); logger.Warn($"ShortcutItem/GetMeAllBitmapsFromFile: The fileNamesAndPaths list is empty! Can't get the bitmap from the files.");
return null; return null;
} }
logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: We have {fileNamesAndPaths.Count} files to try and extract a bitmap from."); logger.Trace($"ShortcutItem/GetMeAllBitmapsFromFile: We have {fileNamesAndPaths.Count} files to try and extract a bitmap from.");
foreach (string fileNameAndPath in fileNamesAndPaths) foreach (string fileNameAndPath in fileNamesAndPaths)
{ {
logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: Getting a bitmap from {fileNameAndPath} by running GetMeABitmapFromFile."); logger.Trace($"ShortcutItem/GetMeAllBitmapsFromFile: Getting a bitmap from {fileNameAndPath} by running GetMeABitmapFromFile.");
Bitmap bm = GetMeABitmapFromFile(fileNameAndPath); bmToReturn.AddRange(GetMeAllBitmapsFromFile(fileNameAndPath));
if (bmToReturn == null)
{
bmToReturn = bm;
}
if (bm != null && bm.Width > bmToReturn.Width && bm.Height > bmToReturn.Height)
{
bmToReturn = bm;
}
logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: The biggest bitmap we could get from {fileNameAndPath} was {bm.Width}x{bm.Height}.");
} }
// Now we check if the icon is still too small.
logger.Trace($"ShortcutItem/GetMeABitmapFromFile2: The biggest bitmap we could get from the {fileNamesAndPaths.Count} files was {bmToReturn.Width}x{bmToReturn.Height}.");
return bmToReturn; return bmToReturn;
} }
public static ShortcutBitmap GetMeLargestAvailableBitmap(List<ShortcutBitmap> shortcutBitmaps)
{
// Returns the first ShortcutBitmap with the largest size
ShortcutBitmap scToReturn = new ShortcutBitmap();
logger.Trace($"ShortcutItem/GetMeLargestAvailableBitmap: We have {shortcutBitmaps.Count} bitmaps to find the largest one within.");
foreach (ShortcutBitmap sc in shortcutBitmaps)
{
if (sc.Size.Width > scToReturn.Size.Width && sc.Size.Height > scToReturn.Size.Height)
{
scToReturn = sc;
}
}
return scToReturn;
}
public static ShortcutBitmap CreateShortcutBitmap(Bitmap bitmap, string name = "", string source = "", int order = 0)
{
ShortcutBitmap sc = new ShortcutBitmap();
sc.UUID = Guid.NewGuid().ToString("D");
sc.Name = name;
sc.Order = order;
sc.Source = source;
sc.Image = bitmap;
sc.Size = new Size(sc.Image.Width, sc.Image.Height);
return sc;
}
public static List<ShortcutBitmap> ShortcutBitmapClone(List<ShortcutBitmap> shortcutBitmaps)
{
// Clones the List<ShortcutBitmap>
List<ShortcutBitmap> scListToReturn = new List<ShortcutBitmap>();
foreach (ShortcutBitmap sc in shortcutBitmaps)
{
scListToReturn.Add(ImageUtils.ShortcutBitmapClone(sc));
}
return scListToReturn;
}
public static ShortcutBitmap ShortcutBitmapClone(ShortcutBitmap shortcutBitmap)
{
// Clones the ShortcutBitmap
ShortcutBitmap scToReturn = new ShortcutBitmap();
scToReturn.UUID = Guid.NewGuid().ToString("D");
scToReturn.Image = (Bitmap)shortcutBitmap.Image.Clone();
scToReturn.Name = shortcutBitmap.Name;
scToReturn.Order = shortcutBitmap.Order;
scToReturn.Size = shortcutBitmap.Size;
scToReturn.Source = shortcutBitmap.Source;
return scToReturn;
}
public static bool ImagesAreEqual(Bitmap imageA, Bitmap imageB)
{
byte[] image1Bytes;
byte[] image2Bytes;
if (imageA == null || imageB == null)
return false;
using (var mstream = new MemoryStream())
{
imageA.Save(mstream,ImageFormat.Bmp);
image1Bytes = mstream.ToArray();
}
using (var mstream = new MemoryStream())
{
imageB.Save(mstream, ImageFormat.Bmp);
image2Bytes = mstream.ToArray();
}
var image164 = Convert.ToBase64String(image1Bytes);
var image264 = Convert.ToBase64String(image2Bytes);
return string.Equals(image164, image264);
}
public static Bitmap ToBitmapOverlay(Bitmap originalBitmap, Bitmap overlayBitmap, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb) public static Bitmap ToBitmapOverlay(Bitmap originalBitmap, Bitmap overlayBitmap, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb)
{ {

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@ using NLog.Config;
using System.Collections.Generic; using System.Collections.Generic;
using AutoUpdaterDotNET; using AutoUpdaterDotNET;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Threading;
namespace DisplayMagician { namespace DisplayMagician {
@ -36,13 +37,15 @@ namespace DisplayMagician {
public static string AppSteamIconFilename = Path.Combine(AppIconPath, @"Steam.ico"); public static string AppSteamIconFilename = Path.Combine(AppIconPath, @"Steam.ico");
public static string AppUplayIconFilename = Path.Combine(AppIconPath, @"Uplay.ico"); public static string AppUplayIconFilename = Path.Combine(AppIconPath, @"Uplay.ico");
public static string AppEpicIconFilename = Path.Combine(AppIconPath, @"Epic.ico"); public static string AppEpicIconFilename = Path.Combine(AppIconPath, @"Epic.ico");
public static string AppDownloadsPath = Utils.GetDownloadsPath();
public static bool AppToastActivated = false; public static bool AppToastActivated = false;
public static bool WaitingForGameToExit = false; public static bool WaitingForGameToExit = false;
public static ProgramSettings AppProgramSettings; public static ProgramSettings AppProgramSettings;
public static MainForm AppMainForm; public static MainForm AppMainForm;
public static LoadingForm AppSplashScreen;
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private static SharedLogger sharedLogger; private static SharedLogger sharedLogger;
private static bool _gamesLoaded = false;
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
@ -104,8 +107,9 @@ namespace DisplayMagician {
// Load the program settings // Load the program settings
AppProgramSettings = ProgramSettings.LoadSettings(); AppProgramSettings = ProgramSettings.LoadSettings();
// Rules for mapping loggers to targets // Rules for mapping loggers to targets
NLog.LogLevel logLevel = null; /*NLog.LogLevel logLevel = null;
switch (AppProgramSettings.LogLevel) switch (AppProgramSettings.LogLevel)
{ {
case "Trace": case "Trace":
@ -126,7 +130,13 @@ namespace DisplayMagician {
default: default:
logLevel = NLog.LogLevel.Info; logLevel = NLog.LogLevel.Info;
break; break;
} }*/
// TODO - remove this temporary action to force Trace level logging
// I've set this as it was too onerous continuously teaching people how to turn on TRACE logging
// While there are a large number of big changes taking place with DisplayMagician, this will minimise
// the backwards and forwards it takes to get the right level of log information for me to troubleshoot.
NLog.LogLevel logLevel = NLog.LogLevel.Trace;
AppProgramSettings.LogLevel = "Trace";
// Create the log file target // Create the log file target
@ -232,14 +242,14 @@ namespace DisplayMagician {
// Check if it's an upgrade from DisplayMagician v1 to v2 // 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 // and if it is then copy the old configs to the new filenames and
// explain to the user what they need to do. // explain to the user what they need to do.
// e.g. DisplayProfiles_1.0.json exists, but DisplayProfiles_2.0.json doesn't // e.g. DisplayProfiles_1.0.json exists, but DisplayProfiles_2.1.json doesn't
if (File.Exists(Path.Combine(AppProfilePath, "DisplayProfiles_1.0.json")) && !File.Exists(Path.Combine(AppProfilePath, "DisplayProfiles_2.0.json"))) if (File.Exists(Path.Combine(AppProfilePath, "DisplayProfiles_1.0.json")) && !File.Exists(Path.Combine(AppProfilePath, "DisplayProfiles_2.1.json")))
{ {
logger.Info($"Program/Main: This is an upgrade from DisplayMagician v1 to DisplayMagician v2, so performing some upgrade steps."); logger.Info($"Program/Main: This is an upgrade from DisplayMagician v1.0 to DisplayMagician v2.1, so performing some upgrade steps.");
// Note whether we copied the old Settings file to the new v2 name earlier (before the logging was enabled) // Note whether we copied the old Settings file to the new v2 name earlier (before the logging was enabled)
if (upgradedSettingsFile) if (upgradedSettingsFile)
{ {
logger.Info($"Program/Main: Upgraded v1 settings file {oldSettingsFile} to v2 settings file {newSettingsFile} earlier in loading process (before logging service was available)."); logger.Info($"Program/Main: Upgraded v1.0 settings file {oldSettingsFile} to v2.0 settings file {newSettingsFile} earlier in loading process (before logging service was available).");
} }
// Copy the old Game Shortcuts file to the new v2 name // Copy the old Game Shortcuts file to the new v2 name
@ -255,14 +265,29 @@ namespace DisplayMagician {
} }
catch(Exception ex) catch(Exception ex)
{ {
logger.Error(ex, $"Program/Main: Exception upgrading v1 shortcut file {oldShortcutsFile} to v2 shortcut file {ShortcutRepository.ShortcutStorageFileName}."); logger.Error(ex, $"Program/Main: Exception upgrading v1.0 shortcut file {oldShortcutsFile} to v2.0 shortcut file {ShortcutRepository.ShortcutStorageFileName}.");
} }
// Warn the user about the fact we need a new DisplayProfiles_2.0.json // Warn the user about the fact we need a new DisplayProfiles_2.0.json
StartMessageForm myMessageWindow = new StartMessageForm(); StartMessageForm myMessageWindow = new StartMessageForm();
myMessageWindow.MessageMode = "rtf"; myMessageWindow.MessageMode = "rtf";
myMessageWindow.URL = "https://displaymagician.littlebitbig.com/messages/DisplayMagician1to2.rtf"; myMessageWindow.URL = "https://displaymagician.littlebitbig.com/messages/DisplayMagician1to2.rtf";
myMessageWindow.HeadingText = "DisplayMagician v2.0.0 Upgrade Warning"; myMessageWindow.HeadingText = "DisplayMagician v2.1.0 Upgrade Warning";
myMessageWindow.ButtonText = "&Close";
myMessageWindow.ShowDialog();
}
// Check if it's an upgrade from DisplayMagician v2.0 to v2.1
// 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_2.1.json exists, but DisplayProfiles_2.0.json doesn't
else if (File.Exists(Path.Combine(AppProfilePath, "DisplayProfiles_2.0.json")) && !File.Exists(Path.Combine(AppProfilePath, "DisplayProfiles_2.1.json")))
{
logger.Info($"Program/Main: This is an upgrade from DisplayMagician v2.0 to DisplayMagician v2.1, so performing some upgrade steps.");
// 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/DisplayMagician20to21.rtf";
myMessageWindow.HeadingText = "DisplayMagician v2.1.0 Upgrade Warning";
myMessageWindow.ButtonText = "&Close"; myMessageWindow.ButtonText = "&Close";
myMessageWindow.ShowDialog(); myMessageWindow.ShowDialog();
} }
@ -293,6 +318,9 @@ namespace DisplayMagician {
// This is the RunShortcut command // This is the RunShortcut command
app.Command(DisplayMagicianStartupAction.RunShortcut.ToString(), (runShortcutCmd) => app.Command(DisplayMagicianStartupAction.RunShortcut.ToString(), (runShortcutCmd) =>
{ {
// Try to load all the games in parallel to this process
//Task.Run(() => LoadGamesInBackground());
// Set the --trace or --debug options if supplied // Set the --trace or --debug options if supplied
if (trace.HasValue()) if (trace.HasValue())
{ {
@ -351,7 +379,9 @@ namespace DisplayMagician {
{ {
logger.Debug($"RunShortcut commandline command was invoked!"); logger.Debug($"RunShortcut commandline command was invoked!");
// // Load the games in background onexecute
GameLibrary.LoadGamesInBackground();
RunShortcut(argumentShortcut.Value); RunShortcut(argumentShortcut.Value);
return 0; return 0;
}); });
@ -565,14 +595,26 @@ namespace DisplayMagician {
} }
} }
logger.Info("Starting Normally..."); logger.Info("Starting Normally...");
// Try to load all the games in parallel to this process
//Task.Run(() => LoadGamesInBackground());
logger.Debug($"Try to load all the Games in the background to avoid locking the UI");
GameLibrary.LoadGamesInBackground();
StartUpApplication(); StartUpApplication();
return 0; return 0;
}); });
logger.Debug($"Try to load all the Games in the background to avoid locking the UI");
// Try to load all the games in parallel to this process if (AppProgramSettings.ShowSplashScreen)
Task.Run(() => LoadGamesInBackground()); {
//Show Splash Form
AppSplashScreen = new LoadingForm();
var splashThread = new Thread(new ThreadStart(
() => Application.Run(AppSplashScreen)));
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.Start();
}
try try
{ {
@ -624,6 +666,10 @@ namespace DisplayMagician {
IPCService.GetInstance().Status = InstanceStatus.User; IPCService.GetInstance().Status = InstanceStatus.User;
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
// Run the program with directly showing CreateProfile form // Run the program with directly showing CreateProfile form
Application.Run(new DisplayProfileForm()); Application.Run(new DisplayProfileForm());
@ -662,8 +708,7 @@ namespace DisplayMagician {
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Program/StartUpNormally exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); logger.Error(ex, $"Program/StartUpApplication exception while trying to create directory {AppIconPath}");
logger.Error(ex, $"Program/StartUpNormally exception while trying to create directory {AppIconPath}");
} }
} }
@ -680,8 +725,7 @@ namespace DisplayMagician {
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Program/StartUpNormally exception 2: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); logger.Error(ex, $"Program/StartUpApplication exception create Icon files for future use in {AppIconPath}");
logger.Error(ex, $"Program/StartUpNormally exception create Icon files for future use in {AppIconPath}");
} }
IPCService.GetInstance().Status = InstanceStatus.User; IPCService.GetInstance().Status = InstanceStatus.User;
@ -694,13 +738,13 @@ namespace DisplayMagician {
// Run the program with normal startup // Run the program with normal startup
AppMainForm = new MainForm(); AppMainForm = new MainForm();
AppMainForm.Load += MainForm_LoadCompleted;
Application.Run(AppMainForm); Application.Run(AppMainForm);
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Program/StartUpNormally exception 3: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); logger.Error(ex, $"Program/StartUpApplication top level exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
logger.Error(ex, $"Program/StartUpNormally top level exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
MessageBox.Show( MessageBox.Show(
ex.Message, ex.Message,
Language.Fatal_Error, Language.Fatal_Error,
@ -710,6 +754,14 @@ namespace DisplayMagician {
} }
private static void MainForm_LoadCompleted(object sender, EventArgs e)
{
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
AppMainForm.TopMost = true;
AppMainForm.Activate();
AppMainForm.TopMost = false;
}
// ReSharper disable once CyclomaticComplexity // ReSharper disable once CyclomaticComplexity
private static void RunShortcut(string shortcutUUID) private static void RunShortcut(string shortcutUUID)
@ -718,6 +770,10 @@ namespace DisplayMagician {
ShortcutItem shortcutToRun = null; ShortcutItem shortcutToRun = null;
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
// Check there is only one version of this application so we won't // Check there is only one version of this application so we won't
// mess with another monitoring session // mess with another monitoring session
if ( if (
@ -768,6 +824,10 @@ namespace DisplayMagician {
{ {
logger.Trace($"Program/RunProfile: Starting"); logger.Trace($"Program/RunProfile: Starting");
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && AppSplashScreen != null && !AppSplashScreen.Disposing && !AppSplashScreen.IsDisposed)
AppSplashScreen.Invoke(new Action(() => AppSplashScreen.Close()));
// Lookup the profile // Lookup the profile
ProfileItem profileToUse = ProfileRepository.AllProfiles.Where(p => p.UUID.Equals(profileName)).First(); ProfileItem profileToUse = ProfileRepository.AllProfiles.Where(p => p.UUID.Equals(profileName)).First();
logger.Trace($"Program/RunProfile: Found profile called {profileName} and now starting to apply the profile"); logger.Trace($"Program/RunProfile: Found profile called {profileName} and now starting to apply the profile");
@ -776,211 +836,6 @@ namespace DisplayMagician {
} }
public static bool LoadGamesInBackground()
{
logger.Debug($"Program/LoadGamesInBackground: Starting");
// Now lets prepare loading all the Steam games we have installed
Action loadSteamGamesAction = new Action(() =>
{
// Check if Steam is installed
GameLibrary steamLibrary = SteamLibrary.GetLibrary();
if (steamLibrary.IsGameLibraryInstalled)
{
// Load Steam library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Steam Games");
if (!steamLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Steam Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Steam Games (found {steamLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Steam not installed.");
Console.WriteLine("Steam not installed.");
}
});
// Now lets prepare loading all the Uplay games we have installed
Action loadUplayGamesAction = new Action(() =>
{
// Check if Uplay is installed
GameLibrary uplayLibrary = UplayLibrary.GetLibrary();
if (uplayLibrary.IsGameLibraryInstalled)
{
// Load Uplay library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Uplay Games");
if (!uplayLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Uplay Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Uplay Games (found {uplayLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Uplay not installed.");
Console.WriteLine("Uplay not installed.");
}
});
// Now lets prepare loading all the Origin games we have installed
Action loadOriginGamesAction = new Action(() =>
{
// Check if Origin is installed
GameLibrary originLibrary = OriginLibrary.GetLibrary();
if (originLibrary.IsGameLibraryInstalled)
{
// Load Origin library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Origin Games");
if (!originLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Origin Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Origin Games (found {originLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Origin not installed.");
Console.WriteLine("Origin not installed.");
}
});
// Now lets prepare loading all the Epic games we have installed
Action loadEpicGamesAction = new Action(() =>
{
// Check if Epic is installed
GameLibrary epicLibrary = EpicLibrary.GetLibrary();
if (epicLibrary.IsGameLibraryInstalled)
{
// Load Origin library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed Epic Games");
if (!epicLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed Epic Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed Epic Games (found {epicLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: Epic not installed.");
Console.WriteLine("Epic not installed.");
}
});
// Now lets prepare loading all the GOG games we have installed
Action loadGogGamesAction = new Action(() =>
{
// Check if GOG is installed
GameLibrary gogLibrary = GogLibrary.GetLibrary();
if (gogLibrary.IsGameLibraryInstalled)
{
// Load Origin library games
logger.Info($"Program/LoadGamesInBackground: Loading Installed GOG Games");
if (!gogLibrary.LoadInstalledGames())
{
logger.Info($"Program/LoadGamesInBackground: Cannot load installed GOG Games!");
}
logger.Info($"Program/LoadGamesInBackground: Loaded all Installed GOG Games (found {gogLibrary.InstalledGameCount})");
}
else
{
logger.Info($"Program/LoadGamesInBackground: GOG not installed.");
Console.WriteLine("GOG not installed.");
}
});
// Store all the actions in a array so we can wait on them later
List<Action> loadGamesActions = new List<Action>();
loadGamesActions.Add(loadSteamGamesAction);
loadGamesActions.Add(loadUplayGamesAction);
loadGamesActions.Add(loadOriginGamesAction);
loadGamesActions.Add(loadEpicGamesAction);
loadGamesActions.Add(loadGogGamesAction);
try
{
logger.Debug($"Program/LoadGamesInBackground: Running game loading actions.");
// Go through and start all the actions, making sure we only have one threat per action to avoid thread issues
int threads = loadGamesActions.Count;
ParallelOptions options = new ParallelOptions { MaxDegreeOfParallelism = threads };
Parallel.Invoke(options, loadGamesActions.ToArray());
// Once we get here , we know that all the parallel actions have returned
logger.Debug($"Program/LoadGamesInBackground: All game loading tasks finished");
}
catch (AggregateException ae)
{
logger.Error(ae, $"Program/LoadGamesInBackground exception during loadGamesActions");
}
// Produce a single array of Games we can reference later
GameLibrary.AllInstalledGamesInAllLibraries = SteamLibrary.GetLibrary().AllInstalledGames;
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(UplayLibrary.GetLibrary().AllInstalledGames);
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(OriginLibrary.GetLibrary().AllInstalledGames);
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(EpicLibrary.GetLibrary().AllInstalledGames);
GameLibrary.AllInstalledGamesInAllLibraries.AddRange(GogLibrary.GetLibrary().AllInstalledGames);
// Create Game Bitmaps from the Games so the rest of the program is faster later
// Get the bitmap out of the IconPath
// IconPath can be an ICO, or an EXE
foreach (var game in GameLibrary.AllInstalledGamesInAllLibraries)
{
Bitmap bm = null;
try
{
/*ArrayList filesToSearchForIcon = new ArrayList();
filesToSearchForIcon.Add(game.ExePath);
if (game.IconPath != game.ExePath)
filesToSearchForIcon.Add(game.IconPath);
bm = ImageUtils.GetMeABitmapFromFile(filesToSearchForIcon);*/
// We only want the icon location that the GameLibrary told us to use
// Note: This may be an icon file, or an exe file.
// This function tries to get a 256x256 Vista sized bitmap from the file
logger.Trace($"Program/LoadGamesInBackground: Attempting to get game bitmaps from {game.Name}.");
bm = ImageUtils.GetMeABitmapFromFile(game.IconPath);
if (bm != null && bm.GetType() == typeof(Bitmap))
{
logger.Trace($"Program/LoadGamesInBackground: Got game bitmaps from {game.Name}.");
}
else
{
logger.Trace($"Program/LoadGamesInBackground: Couldn't get game bitmaps from {game.Name} for some reason.");
}
}
catch (Exception ex)
{
logger.Error(ex, $"Program/LoadGamesInBackground: Exception building game bitmaps for {game.Name} during load");
}
if (bm == null)
{
if (game.GameLibrary.Equals(SupportedGameLibraryType.Steam))
bm = Properties.Resources.Steam;
else if (game.GameLibrary.Equals(SupportedGameLibraryType.Uplay))
bm = Properties.Resources.Uplay;
else if (game.GameLibrary.Equals(SupportedGameLibraryType.Origin))
bm = Properties.Resources.Origin;
else if (game.GameLibrary.Equals(SupportedGameLibraryType.Epic))
bm = Properties.Resources.Epic;
else if (game.GameLibrary.Equals(SupportedGameLibraryType.GOG))
bm = Properties.Resources.GOG;
else
bm = Properties.Resources.DisplayMagician.ToBitmap();
}
game.GameBitmap = bm;
}
return true;
}
public static string HotkeyToString(Keys hotkey) public static string HotkeyToString(Keys hotkey)
{ {

View File

@ -22,6 +22,7 @@ namespace DisplayMagician
#region Instance Variables #region Instance Variables
private bool _startOnBootUp = false; private bool _startOnBootUp = false;
private bool _minimiseOnStart = false; private bool _minimiseOnStart = false;
private bool _showSplashScreen = true;
private bool _upgradeToPrereleases = false; private bool _upgradeToPrereleases = false;
private int _lastMessageIdRead = 0; private int _lastMessageIdRead = 0;
private List<int> _messagesToMonitor = new List<int>(); private List<int> _messagesToMonitor = new List<int>();
@ -49,6 +50,23 @@ namespace DisplayMagician
} }
} }
public bool ShowSplashScreen
{
get
{
return _showSplashScreen;
}
set
{
_showSplashScreen = value;
// Because a value has changed, we need to save the setting
// to remember it for later.
if (_programSettingsLoaded)
SaveSettings();
}
}
public bool UpgradeToPreReleases public bool UpgradeToPreReleases
{ {
get get

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

@ -61,6 +61,17 @@ namespace DisplayMagician
public bool DontStartIfAlreadyRunning; public bool DontStartIfAlreadyRunning;
} }
public struct StopProgram
{
public int Priority;
public bool Disabled;
public ProcessPriority ProcessPriority;
public string Executable;
public string Arguments;
public bool ExecutableArgumentsRequired;
public bool DontStartIfAlreadyRunning;
}
public struct Executable public struct Executable
{ {
public string DifferentExecutableToMonitor; public string DifferentExecutableToMonitor;
@ -90,6 +101,17 @@ namespace DisplayMagician
public string Message; public string Message;
} }
public struct ShortcutBitmap
{
public string UUID;
public string Name;
public int Order;
public string Source;
[JsonConverter(typeof(CustomBitmapConverter))]
public Bitmap Image;
public Size Size;
}
public class ShortcutItem : IComparable public class ShortcutItem : IComparable
{ {
@ -128,10 +150,15 @@ namespace DisplayMagician
private ShortcutValidity _isValid; private ShortcutValidity _isValid;
private List<ShortcutError> _shortcutErrors = new List<ShortcutError>(); private List<ShortcutError> _shortcutErrors = new List<ShortcutError>();
private List<StartProgram> _startPrograms; private List<StartProgram> _startPrograms;
private List<StopProgram> _stopPrograms;
private Bitmap _shortcutBitmap, _originalBitmap; private Bitmap _shortcutBitmap, _originalBitmap;
[JsonIgnore] [JsonIgnore]
#pragma warning disable CS3008 // Identifier is not CLS-compliant #pragma warning disable CS3008 // Identifier is not CLS-compliant
public string _originalIconPath; private string _originalIconPath;
private bool _userChoseOwnIcon = false;
private ShortcutBitmap _selectedImage = new ShortcutBitmap();
private List<ShortcutBitmap> _availableImages = new List<ShortcutBitmap>();
[JsonIgnore] [JsonIgnore]
public string _savedShortcutIconCacheFilename; public string _savedShortcutIconCacheFilename;
#pragma warning restore CS3008 // Identifier is not CLS-compliant #pragma warning restore CS3008 // Identifier is not CLS-compliant
@ -626,6 +653,20 @@ namespace DisplayMagician
} }
} }
public List<StopProgram> StopPrograms
{
get
{
return _stopPrograms;
}
set
{
_stopPrograms = value;
}
}
public string OriginalIconPath { public string OriginalIconPath {
get get
{ {
@ -713,6 +754,35 @@ namespace DisplayMagician
} }
} }
public ShortcutBitmap SelectedImage
{
get
{
return _selectedImage;
}
set
{
_selectedImage = value;
}
}
public List<ShortcutBitmap> AvailableImages
{
get
{
return _availableImages;
}
set
{
_availableImages = value;
}
}
public void UpdateNoGameShortcut( public void UpdateNoGameShortcut(
string name, string name,
#pragma warning disable CS3001 // Argument type is not CLS-compliant #pragma warning disable CS3001 // Argument type is not CLS-compliant
@ -731,6 +801,7 @@ namespace DisplayMagician
bool setCaptureVolume = false, bool setCaptureVolume = false,
decimal captureVolume = -1, decimal captureVolume = -1,
List<StartProgram> startPrograms = null, List<StartProgram> startPrograms = null,
List<StopProgram> stopPrograms = null,
bool autoName = true, bool autoName = true,
Keys hotkey = Keys.None, Keys hotkey = Keys.None,
string uuid = "" string uuid = ""
@ -776,6 +847,8 @@ namespace DisplayMagician
ShortcutPermanence audioPermanence, ShortcutPermanence audioPermanence,
ShortcutPermanence capturePermanence, ShortcutPermanence capturePermanence,
string originalIconPath, string originalIconPath,
ShortcutBitmap selectedImage,
List<ShortcutBitmap> availableImages,
bool changeAudioDevice = false, bool changeAudioDevice = false,
string audioDevice = "", string audioDevice = "",
bool setAudioVolume = false, bool setAudioVolume = false,
@ -785,6 +858,7 @@ namespace DisplayMagician
bool setCaptureVolume = false, bool setCaptureVolume = false,
decimal captureVolume = -1, decimal captureVolume = -1,
List<StartProgram> startPrograms = null, List<StartProgram> startPrograms = null,
List<StopProgram> stopPrograms = null,
bool autoName = true, bool autoName = true,
string uuid = "", string uuid = "",
Keys hotkey = Keys.None Keys hotkey = Keys.None
@ -818,26 +892,19 @@ namespace DisplayMagician
_capturePermanence = capturePermanence; _capturePermanence = capturePermanence;
_autoName = autoName; _autoName = autoName;
_startPrograms = startPrograms; _startPrograms = startPrograms;
_stopPrograms = stopPrograms;
_originalIconPath = originalIconPath; _originalIconPath = originalIconPath;
_selectedImage = selectedImage;
_availableImages = availableImages;
_hotkey = hotkey; _hotkey = hotkey;
// Now we need to find and populate the profileUuid // Now we need to find and populate the profileUuid
_profileUuid = profile.UUID; _profileUuid = profile.UUID;
// We create the OriginalBitmap // We create the Bitmaps for the game
// Find the game bitmap that matches the game name we just got _originalBitmap = selectedImage.Image;
foreach (var aGame in DisplayMagician.GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries) // Now we use the originalBitmap or userBitmap, and create the shortcutBitmap from it
{ _shortcutBitmap = ImageUtils.ToBitmapOverlay(_originalBitmap, _profileToUse.ProfileTightestBitmap, 256, 256);
if (aGame.Name.Equals(_gameName))
{
_originalBitmap = aGame.GameBitmap;
}
}
// We create the ShortcutBitmap from the OriginalBitmap
// (We only do it if there is a valid profile)
if (_profileToUse is ProfileItem)
_shortcutBitmap = ToBitmapOverlay(_originalBitmap, _profileToUse.ProfileTightestBitmap, 256, 256);
ReplaceShortcutIconInCache(); ReplaceShortcutIconInCache();
RefreshValidity(); RefreshValidity();
@ -853,6 +920,8 @@ namespace DisplayMagician
ShortcutPermanence audioPermanence, ShortcutPermanence audioPermanence,
ShortcutPermanence capturePermanence, ShortcutPermanence capturePermanence,
string originalIconPath, string originalIconPath,
ShortcutBitmap selectedImage,
List<ShortcutBitmap> availableImages,
bool changeAudioDevice = false, bool changeAudioDevice = false,
string audioDevice = "", string audioDevice = "",
bool setAudioVolume = false, bool setAudioVolume = false,
@ -862,6 +931,7 @@ namespace DisplayMagician
bool setCaptureVolume = false, bool setCaptureVolume = false,
decimal captureVolume = -1, decimal captureVolume = -1,
List<StartProgram> startPrograms = null, List<StartProgram> startPrograms = null,
List<StopProgram> stopPrograms = null,
bool autoName = true, bool autoName = true,
Keys hotkey = Keys.None, Keys hotkey = Keys.None,
string uuid = "" string uuid = ""
@ -892,22 +962,19 @@ namespace DisplayMagician
_capturePermanence = capturePermanence; _capturePermanence = capturePermanence;
_autoName = autoName; _autoName = autoName;
_startPrograms = startPrograms; _startPrograms = startPrograms;
_stopPrograms = stopPrograms;
_originalIconPath = originalIconPath; _originalIconPath = originalIconPath;
_selectedImage = selectedImage;
_availableImages = availableImages;
_hotkey = hotkey; _hotkey = hotkey;
// Now we need to find and populate the profileUuid // Now we need to find and populate the profileUuid
_profileUuid = profile.UUID; _profileUuid = profile.UUID;
// We create the OriginalBitmap from the IconPath // We create the Bitmaps for the executable
//_originalLargeBitmap = ToLargeBitmap(_originalIconPath); _originalBitmap = selectedImage.Image;
// We create the OriginalBitmap // Now we use the originalBitmap or userBitmap, and create the shortcutBitmap from it
_originalBitmap = ImageUtils.GetMeABitmapFromFile(_originalIconPath); _shortcutBitmap = ImageUtils.ToBitmapOverlay(_originalBitmap, _profileToUse.ProfileTightestBitmap, 256, 256);
// We create the ShortcutBitmap from the OriginalBitmap
// (We only do it if there is a valid profile)
//if (_profileToUse is ProfileItem)
// _shortcutBitmap = ToBitmapOverlay(_originalLargeBitmap, _profileToUse.ProfileTightestBitmap, 256, 256);
_shortcutBitmap = ToBitmapOverlay(_originalBitmap, _profileToUse.ProfileTightestBitmap, 256, 256);
ReplaceShortcutIconInCache(); ReplaceShortcutIconInCache();
RefreshValidity(); RefreshValidity();
@ -923,8 +990,7 @@ namespace DisplayMagician
// Copy all the shortcut data over to the other Shortcut // Copy all the shortcut data over to the other Shortcut
shortcut.Name = Name; shortcut.Name = Name;
shortcut.ProfileToUse = ProfileToUse; shortcut.AutoName = false; // Force the autoname to be off, as it's a copy.
shortcut.ProfileUUID = ProfileUUID;
shortcut.DisplayPermanence = DisplayPermanence; shortcut.DisplayPermanence = DisplayPermanence;
shortcut.AudioPermanence = AudioPermanence; shortcut.AudioPermanence = AudioPermanence;
shortcut.CapturePermanence = CapturePermanence; shortcut.CapturePermanence = CapturePermanence;
@ -942,12 +1008,8 @@ namespace DisplayMagician
shortcut.GameArguments = GameArguments; shortcut.GameArguments = GameArguments;
shortcut.GameArgumentsRequired = GameArgumentsRequired; shortcut.GameArgumentsRequired = GameArgumentsRequired;
shortcut.OriginalIconPath = OriginalIconPath; shortcut.OriginalIconPath = OriginalIconPath;
shortcut.OriginalLargeBitmap = OriginalLargeBitmap;
shortcut.ShortcutBitmap = ShortcutBitmap;
shortcut.SavedShortcutIconCacheFilename = SavedShortcutIconCacheFilename;
shortcut.IsValid = IsValid; shortcut.IsValid = IsValid;
shortcut.Errors.AddRange(Errors); shortcut.Errors.AddRange(Errors);
shortcut.StartPrograms = StartPrograms;
shortcut.ChangeAudioDevice = ChangeAudioDevice; shortcut.ChangeAudioDevice = ChangeAudioDevice;
shortcut.AudioDevice = AudioDevice; shortcut.AudioDevice = AudioDevice;
shortcut.SetAudioVolume = SetAudioVolume; shortcut.SetAudioVolume = SetAudioVolume;
@ -956,10 +1018,53 @@ namespace DisplayMagician
shortcut.CaptureDevice = CaptureDevice; shortcut.CaptureDevice = CaptureDevice;
shortcut.SetCaptureVolume = SetCaptureVolume; shortcut.SetCaptureVolume = SetCaptureVolume;
shortcut.CaptureVolume = CaptureVolume; shortcut.CaptureVolume = CaptureVolume;
shortcut.Hotkey = Hotkey; // shortcut.Hotkey = Hotkey; // We cannot duplicate the Hotkey as it breaks things
// Duplicate the Images
shortcut.OriginalLargeBitmap = (Bitmap)OriginalLargeBitmap.Clone();
shortcut.ShortcutBitmap = (Bitmap)ShortcutBitmap.Clone();
//shortcut.SavedShortcutIconCacheFilename = SavedShortcutIconCacheFilename; // We want a new shortcut icon!
shortcut.SelectedImage = ImageUtils.ShortcutBitmapClone(SelectedImage);
shortcut.AvailableImages = ImageUtils.ShortcutBitmapClone(AvailableImages);
// Duplicate the start programs
shortcut.StartPrograms = new List<StartProgram>();
foreach (StartProgram sp in StartPrograms)
{
StartProgram copiedStartProgram = new StartProgram();
copiedStartProgram.Arguments = sp.Arguments;
copiedStartProgram.CloseOnFinish = sp.CloseOnFinish;
copiedStartProgram.Disabled = sp.Disabled;
copiedStartProgram.DontStartIfAlreadyRunning = sp.DontStartIfAlreadyRunning;
copiedStartProgram.Executable = sp.Executable;
copiedStartProgram.ExecutableArgumentsRequired = sp.ExecutableArgumentsRequired;
copiedStartProgram.Priority = sp.Priority;
copiedStartProgram.ProcessPriority = sp.ProcessPriority;
shortcut.StartPrograms.Add(copiedStartProgram);
}
// Duplicate the stop programs
shortcut.StopPrograms = new List<StopProgram>();
foreach (StopProgram sp in StopPrograms)
{
StopProgram copiedStopProgram = new StopProgram();
copiedStopProgram.Arguments = sp.Arguments;
copiedStopProgram.Disabled = sp.Disabled;
copiedStopProgram.DontStartIfAlreadyRunning = sp.DontStartIfAlreadyRunning;
copiedStopProgram.Executable = sp.Executable;
copiedStopProgram.ExecutableArgumentsRequired = sp.ExecutableArgumentsRequired;
copiedStopProgram.Priority = sp.Priority;
copiedStopProgram.ProcessPriority = sp.ProcessPriority;
shortcut.StopPrograms.Add(copiedStopProgram);
}
// Do the profiles last as AutoName will error if done earlier
shortcut.ProfileToUse = ProfileToUse;
shortcut.ProfileUUID = ProfileUUID;
// Save the shortcut incon to the icon cache // Save the shortcut incon to the icon cache
shortcut.ReplaceShortcutIconInCache(); shortcut.SaveShortcutIconToCache();
shortcut.RefreshValidity(); shortcut.RefreshValidity();
return true; return true;
@ -967,31 +1072,22 @@ namespace DisplayMagician
public void ReplaceShortcutIconInCache() public void ReplaceShortcutIconInCache()
{ {
string newShortcutIconFilename; // Figure out if we need to remove the old file
if (_savedShortcutIconCacheFilename != null)
{
// Work out the name of the shortcut we'll save. // Work out the name of the shortcut we'll save.
newShortcutIconFilename = Path.Combine(Program.AppShortcutPath, $"{UUID}.ico"); string oldShortcutIconFilename = _savedShortcutIconCacheFilename;
logger.Trace($"ShortcutItem/ReplaceShortcutIconInCache: New shortcut Icon filename is {newShortcutIconFilename}."); logger.Trace($"ShortcutItem/ReplaceShortcutIconInCache: Old shortcut Icon filename is {oldShortcutIconFilename}.");
if (System.IO.File.Exists(oldShortcutIconFilename))
// If the new shortcut icon should be named differently
// then change the name of it
if (!newShortcutIconFilename.Equals(_savedShortcutIconCacheFilename))
{ {
logger.Trace($"ShortcutItem/ReplaceShortcutIconInCache: New shortcut Icon filename {newShortcutIconFilename} is different to the old shortcut Icon filename {_savedShortcutIconCacheFilename}."); logger.Trace($"ShortcutItem/ReplaceShortcutIconInCache: Deleting old shortcut Icon filename {oldShortcutIconFilename}.");
if (System.IO.File.Exists(_savedShortcutIconCacheFilename)) System.IO.File.Delete(oldShortcutIconFilename);
{
logger.Trace($"ShortcutItem/ReplaceShortcutIconInCache: Deleting old shortcut Icon filename {_savedShortcutIconCacheFilename}.");
System.IO.File.Delete(_savedShortcutIconCacheFilename);
} }
logger.Trace($"ShortcutItem/ReplaceShortcutIconInCache: Creating the new shortcut Icon filename {newShortcutIconFilename} (calling SaveShortcutIconToCache)."); }
// Now we save the new file
SaveShortcutIconToCache(); SaveShortcutIconToCache();
} }
else
{
logger.Trace($"ShortcutItem/ReplaceShortcutIconInCache: New shortcut Icon filename {newShortcutIconFilename} matches old shortcut Icon filename {_savedShortcutIconCacheFilename} so skipping the rename.");
}
}
public void SaveShortcutIconToCache() public void SaveShortcutIconToCache()
@ -1000,152 +1096,33 @@ namespace DisplayMagician
// Work out the name of the shortcut we'll save. // Work out the name of the shortcut we'll save.
_savedShortcutIconCacheFilename = Path.Combine(Program.AppShortcutPath, $"{UUID}.ico"); _savedShortcutIconCacheFilename = Path.Combine(Program.AppShortcutPath, $"{UUID}.ico");
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Planning on saving shortcut icon to cache as {_savedShortcutIconCacheFilename}."); logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Planning on saving shortcut icon to cache as {_savedShortcutIconCacheFilename}.");
MultiIcon shortcutIcon = new MultiIcon();
MultiIcon shortcutIcon;
try try
{ {
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Creating IconOverlay."); logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Creating Icon from Shortcut bitmap.");
shortcutIcon = ToIconOverlay(); // Create a new
if (shortcutIcon != null) SingleIcon si = shortcutIcon.Add("icon");
{ si.Add(_shortcutBitmap);
shortcutIcon.SelectedIndex = 0;
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Saving shortcut icon to cache with {_savedShortcutIconCacheFilename} as the name."); logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Saving shortcut icon to cache with {_savedShortcutIconCacheFilename} as the name.");
shortcutIcon.Save(_savedShortcutIconCacheFilename, MultiIconFormat.ICO); shortcutIcon.Save(_savedShortcutIconCacheFilename, MultiIconFormat.ICO);
}
else
{
// If we fail to create an icon based on the original executable or game
// Then we use the one appropriate for the game library
SingleIcon si = shortcutIcon.Add("icon");
Bitmap bm = null;
if (_gameLibrary == SupportedGameLibraryType.Steam)
{
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Using the Steam icon as the icon instead.");
bm = ToBitmapOverlay(Properties.Resources.Steam, _profileToUse.ProfileIcon.ToBitmap(),256,256);
}
else if (_gameLibrary == SupportedGameLibraryType.Uplay)
{
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Using the Uplay icon as the icon instead.");
bm = ToBitmapOverlay(Properties.Resources.Uplay, _profileToUse.ProfileIcon.ToBitmap(), 256, 256);
}
else if (_gameLibrary == SupportedGameLibraryType.Origin)
{
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Using the Origin icon as the icon instead.");
bm = ToBitmapOverlay(Properties.Resources.Origin, _profileToUse.ProfileIcon.ToBitmap(), 256, 256);
}
else if (_gameLibrary == SupportedGameLibraryType.Epic)
{
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Using the Epic icon as the icon instead.");
bm = ToBitmapOverlay(Properties.Resources.Epic, _profileToUse.ProfileIcon.ToBitmap(), 256, 256);
}
else if (_gameLibrary == SupportedGameLibraryType.GOG)
{
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Using the GOG icon as the icon instead.");
bm = ToBitmapOverlay(Properties.Resources.GOG, _profileToUse.ProfileIcon.ToBitmap(), 256, 256);
}
else
{
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Unknown Game Library, so using the DisplayMagician icon as the icon instead.");
bm = ToBitmapOverlay(Properties.Resources.DisplayMagician.ToBitmap(), _profileToUse.ProfileIcon.ToBitmap(), 256, 256);
}
si.Add(bm);
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Saving the replacement icon for Shortcut '{Name}' to {_savedShortcutIconCacheFilename}.");
shortcutIcon.Save(_savedShortcutIconCacheFilename, MultiIconFormat.ICO);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Warn(ex, $"ShortcutItem/SaveShortcutIconToCache: Exception while trying to save the Shortcut icon."); logger.Warn(ex, $"ShortcutItem/SaveShortcutIconToCache: Exception while trying to save the Shortcut icon.");
shortcutIcon.Clear();
// If we fail to create an icon any other way, then we use the default profile icon // If we fail to create an icon any other way, then we use the default profile icon
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Using the Display Profile icon for {_profileToUse.Name} as the icon instead."); logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Using the Display Profile icon for {_profileToUse.Name} as the icon instead.");
shortcutIcon = _profileToUse.ProfileIcon.ToIcon(); SingleIcon si = shortcutIcon.Add("icon2");
si.Add(Properties.Resources.DisplayMagician);
shortcutIcon.SelectedIndex = 0;
logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Saving the Display Profile icon for {_profileToUse.Name} to {_savedShortcutIconCacheFilename}."); logger.Trace($"ShortcutItem/SaveShortcutIconToCache: Saving the Display Profile icon for {_profileToUse.Name} to {_savedShortcutIconCacheFilename}.");
shortcutIcon.Save(_savedShortcutIconCacheFilename, MultiIconFormat.ICO); shortcutIcon.Save(_savedShortcutIconCacheFilename, MultiIconFormat.ICO);
} }
} }
public Bitmap ToBitmapOverlay(Bitmap originalBitmap, Bitmap overlayBitmap, int width, int height, PixelFormat format = PixelFormat.Format32bppArgb)
{
if (originalBitmap == null)
{
if (_category == ShortcutCategory.Application)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Using the executable icon as the icon instead.");
originalBitmap = ImageUtils.GetMeABitmapFromFile(_executableNameAndPath);
}
else if (_category == ShortcutCategory.Game)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: OriginalBitmap is null, so we'll try to make the BitmapOverlay using GameLibrary Icon.");
if (_gameLibrary == SupportedGameLibraryType.Steam)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Using the Steam icon as the icon instead.");
originalBitmap = Properties.Resources.Steam;
}
else if (_gameLibrary == SupportedGameLibraryType.Uplay)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Using the Uplay icon as the icon instead.");
originalBitmap = Properties.Resources.Uplay;
}
else if (_gameLibrary == SupportedGameLibraryType.Origin)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Using the Origin icon as the icon instead.");
originalBitmap = Properties.Resources.Origin;
}
else if (_gameLibrary == SupportedGameLibraryType.Epic)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Using the Epic icon as the icon instead.");
originalBitmap = Properties.Resources.Epic;
}
else if (_gameLibrary == SupportedGameLibraryType.GOG)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Using the GOG icon as the icon instead.");
originalBitmap = Properties.Resources.GOG;
}
else
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Unknown Game Library, so using the DisplayMagician icon as the icon instead.");
originalBitmap = Properties.Resources.DisplayMagician.ToBitmap();
}
}
else
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Using the profile icon as the icon instead.");
originalBitmap = _profileToUse.ProfileBitmap;
}
}
if (overlayBitmap == null)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: overlayBitmap is null, so we'll just return the original bitmap without a profile overlay.");
return originalBitmap;
}
if (width <= 0 || width > 256)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Width is out of range so setting to 256.");
width = 256;
}
if (height <= 0 || height > 256)
{
logger.Trace($"ShortcutItem/ToBitmapOverlay: Height is out of range so setting to 256.");
height = 256;
}
return ImageUtils.ToBitmapOverlay(originalBitmap, overlayBitmap, width, height, format);
}
#pragma warning disable CS3002 // Return type is not CLS-compliant
public MultiIcon ToIconOverlay()
#pragma warning restore CS3002 // Return type is not CLS-compliant
{
return ImageUtils.ToIconOverlay(_originalBitmap, ProfileToUse.ProfileTightestBitmap);
}
public void RefreshValidity() public void RefreshValidity()
{ {
// Do some validation checks to make sure the shortcut is sensible // Do some validation checks to make sure the shortcut is sensible
@ -1326,7 +1303,9 @@ namespace DisplayMagician
if (worstError != ShortcutValidity.Error) if (worstError != ShortcutValidity.Error)
worstError = ShortcutValidity.Error; worstError = ShortcutValidity.Error;
} }
if (audioDevice.State == DeviceState.Unplugged) // As per Issue #39, this causes issues on HDMI audio devices and others that *could* work if the screen was enabled.
// Disabling this code as it is too much error checking for audio devices. The user can plug these in after the chagne and they will work.
/*if (audioDevice.State == DeviceState.Unplugged)
{ {
logger.Warn($"ShortcutRepository/RefreshValidity: Detected audio playback device {audioDevice.FullName} is the one we want, but it is unplugged!"); logger.Warn($"ShortcutRepository/RefreshValidity: Detected audio playback device {audioDevice.FullName} is the one we want, but it is unplugged!");
ShortcutError error = new ShortcutError(); ShortcutError error = new ShortcutError();
@ -1336,7 +1315,7 @@ namespace DisplayMagician
_shortcutErrors.Add(error); _shortcutErrors.Add(error);
if (worstError != ShortcutValidity.Error) if (worstError != ShortcutValidity.Error)
worstError = ShortcutValidity.Warning; worstError = ShortcutValidity.Warning;
} }*/
break; break;
} }
} }
@ -1399,7 +1378,9 @@ namespace DisplayMagician
if (worstError != ShortcutValidity.Error) if (worstError != ShortcutValidity.Error)
worstError = ShortcutValidity.Error; worstError = ShortcutValidity.Error;
} }
if (captureDevice.State == DeviceState.Unplugged) // As per Issue #39, this causes issues on HDMI audiodevices and others that *could* work if the screen was enabled.
// Disabling this code as it is too much error checking for capture devices. The user can plug these in after the chagne and they will work.
/*if (captureDevice.State == DeviceState.Unplugged)
{ {
logger.Warn($"ShortcutRepository/RefreshValidity: Detected capture device {captureDevice.FullName} is the one we want, but it is unplugged!"); logger.Warn($"ShortcutRepository/RefreshValidity: Detected capture device {captureDevice.FullName} is the one we want, but it is unplugged!");
ShortcutError error = new ShortcutError(); ShortcutError error = new ShortcutError();
@ -1409,7 +1390,7 @@ namespace DisplayMagician
_shortcutErrors.Add(error); _shortcutErrors.Add(error);
if (worstError != ShortcutValidity.Error) if (worstError != ShortcutValidity.Error)
worstError = ShortcutValidity.Warning; worstError = ShortcutValidity.Warning;
} }*/
break; break;
} }
} }
@ -1481,7 +1462,7 @@ namespace DisplayMagician
shortcutFileName = Path.ChangeExtension(shortcutFileName, @"lnk"); shortcutFileName = Path.ChangeExtension(shortcutFileName, @"lnk");
// And we use the Icon from the shortcutIconCache // And we use the Icon from the shortcutIconCache
SaveShortcutIconToCache(); //SaveShortcutIconToCache();
shortcutIconFileName = SavedShortcutIconCacheFilename; shortcutIconFileName = SavedShortcutIconCacheFilename;
// If the user supplied a file // If the user supplied a file

View File

@ -356,6 +356,45 @@ namespace DisplayMagician
return true; return true;
} }
public static bool CopyShortcut(ShortcutItem shortcut, out ShortcutItem copiedShortcut)
{
logger.Trace($"ShortcutRepository/CopyShortcut: Checking whether {shortcut.Name} exists in our shortcut repository");
copiedShortcut = new ShortcutItem();
if (!(shortcut is ShortcutItem))
return false;
if (shortcut.CopyTo(copiedShortcut,false))
{
// Copy worked!
// We add (Copy) to the end of the shortcut name
copiedShortcut.Name = copiedShortcut.Name + " (Copy)";
// Add the shortcut to the list of shortcuts
_allShortcuts.Add(copiedShortcut);
//Doublecheck it's been added
if (ContainsShortcut(copiedShortcut))
{
// Select the copied shortcut
// Save the shortcuts JSON as it's different
SaveShortcuts();
IsValidRefresh();
return true;
}
else
return false;
}
else
{
// Copy failed
return false;
}
}
private static bool LoadShortcuts() private static bool LoadShortcuts()
{ {
@ -402,15 +441,27 @@ namespace DisplayMagician
#pragma warning disable IDE0059 // Unnecessary assignment of a value #pragma warning disable IDE0059 // Unnecessary assignment of a value
List<ShortcutItem> shortcuts = new List<ShortcutItem>(); List<ShortcutItem> shortcuts = new List<ShortcutItem>();
#pragma warning restore IDE0059 // Unnecessary assignment of a value #pragma warning restore IDE0059 // Unnecessary assignment of a value
List<string> jsonErrors = new List<string>();
try try
{ {
_allShortcuts = JsonConvert.DeserializeObject<List<ShortcutItem>>(json, new JsonSerializerSettings
JsonSerializerSettings mySerializerSettings = new JsonSerializerSettings
{ {
MissingMemberHandling = MissingMemberHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include, DefaultValueHandling = DefaultValueHandling.Include,
TypeNameHandling = TypeNameHandling.Auto TypeNameHandling = TypeNameHandling.Auto,
}); Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
jsonErrors.Add($"JSON.net Error: {args.ErrorContext.Error.Source}:{args.ErrorContext.Error.StackTrace} - {args.ErrorContext.Error.Message} | InnerException:{args.ErrorContext.Error.InnerException.Source}:{args.ErrorContext.Error.InnerException.StackTrace} - {args.ErrorContext.Error.InnerException.Message}");
args.ErrorContext.Handled = true;
},
};
_allShortcuts = JsonConvert.DeserializeObject<List<ShortcutItem>>(json, mySerializerSettings);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -418,6 +469,15 @@ namespace DisplayMagician
throw new Exception("ShortcutRepository/LoadShortcuts: Tried to parse the JSON in the {_shortcutStorageJsonFileName} but the JsonConvert threw an exception. There is an error in the SHortcut JSON file!"); throw new Exception("ShortcutRepository/LoadShortcuts: Tried to parse the JSON in the {_shortcutStorageJsonFileName} but the JsonConvert threw an exception. There is an error in the SHortcut JSON file!");
} }
// If we have any JSON.net errors, then we need to records them in the logs
if (jsonErrors.Count > 0)
{
foreach (string jsonError in jsonErrors)
{
logger.Error($"ShortcutRepository/LoadShortcuts: {jsonErrors}");
}
}
// Lookup all the Profile Names in the Saved Profiles // Lookup all the Profile Names in the Saved Profiles
// and link the profiles to the Shortcuts as we only // and link the profiles to the Shortcuts as we only
// store the profile names to allow users to uodate profiles // store the profile names to allow users to uodate profiles
@ -499,17 +559,24 @@ namespace DisplayMagician
} }
List<string> jsonErrors = new List<string>();
try try
{ {
logger.Debug($"ShortcutRepository/SaveShortcuts: Converting the objects to JSON format."); logger.Debug($"ShortcutRepository/SaveShortcuts: Converting the objects to JSON format.");
var json = JsonConvert.SerializeObject(_allShortcuts, Formatting.Indented, new JsonSerializerSettings JsonSerializerSettings mySerializerSettings = new JsonSerializerSettings
{ {
NullValueHandling = NullValueHandling.Include, NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Populate, DefaultValueHandling = DefaultValueHandling.Populate,
TypeNameHandling = TypeNameHandling.Auto TypeNameHandling = TypeNameHandling.Auto,
Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
}); {
jsonErrors.Add($"JSON.net Error: {args.ErrorContext.Error.Source}:{args.ErrorContext.Error.StackTrace} - {args.ErrorContext.Error.Message} | InnerException:{args.ErrorContext.Error.InnerException.Source}:{args.ErrorContext.Error.InnerException.StackTrace} - {args.ErrorContext.Error.InnerException.Message}");
args.ErrorContext.Handled = true;
},
};
var json = JsonConvert.SerializeObject(_allShortcuts, Formatting.Indented, mySerializerSettings);
if (!string.IsNullOrWhiteSpace(json)) if (!string.IsNullOrWhiteSpace(json))
@ -525,6 +592,15 @@ namespace DisplayMagician
logger.Error(ex, $"ShortcutRepository/SaveShortcuts: Unable to save the shortcut repository to the {_shortcutStorageJsonFileName}."); logger.Error(ex, $"ShortcutRepository/SaveShortcuts: Unable to save the shortcut repository to the {_shortcutStorageJsonFileName}.");
} }
// If we have any JSON.net errors, then we need to records them in the logs
if (jsonErrors.Count > 0)
{
foreach (string jsonError in jsonErrors)
{
logger.Error($"ProfileRepository/SaveProfiles: {jsonErrors}");
}
}
return false; return false;
} }
@ -592,6 +668,7 @@ namespace DisplayMagician
@"Cannot run the Shortcut", @"Cannot run the Shortcut",
MessageBoxButtons.OK, MessageBoxButtons.OK,
MessageBoxIcon.Exclamation); MessageBoxIcon.Exclamation);
return; return;
} }
@ -619,20 +696,21 @@ namespace DisplayMagician
ApplyProfileResult result = ProfileRepository.ApplyProfile(shortcutToUse.ProfileToUse); ApplyProfileResult result = ProfileRepository.ApplyProfile(shortcutToUse.ProfileToUse);
if (result == ApplyProfileResult.Error) if (result == ApplyProfileResult.Error)
{ {
Console.WriteLine($"ERROR - Cannot apply '{shortcutToUse.ProfileToUse.Name}' Display Profile");
logger.Error($"ShortcutRepository/RunShortcut: Cannot apply '{shortcutToUse.ProfileToUse.Name}' Display Profile"); logger.Error($"ShortcutRepository/RunShortcut: Cannot apply '{shortcutToUse.ProfileToUse.Name}' Display Profile");
return; return;
} }
else if (result == ApplyProfileResult.Cancelled) else if (result == ApplyProfileResult.Cancelled)
{ {
Console.WriteLine($"ERROR - User cancelled applying '{shortcutToUse.ProfileToUse.Name}' Display Profile");
logger.Error($"ShortcutRepository/RunShortcut: User cancelled applying '{shortcutToUse.ProfileToUse.Name}' Display Profile"); logger.Error($"ShortcutRepository/RunShortcut: User cancelled applying '{shortcutToUse.ProfileToUse.Name}' Display Profile");
return; return;
} }
else if (result == ApplyProfileResult.Successful)
{
logger.Trace($"ShortcutRepository/RunShortcut: Applied '{shortcutToUse.ProfileToUse.Name}' Display Profile successfully!");
}
} }
// Get the list of Audio Devices currently connected and active // Get the list of Audio Devices currently connected or unplugged (they can be plugged back in)
bool needToChangeAudioDevice = false; bool needToChangeAudioDevice = false;
CoreAudioDevice rollbackAudioDevice = null; CoreAudioDevice rollbackAudioDevice = null;
double rollbackAudioVolume = 50; double rollbackAudioVolume = 50;
@ -645,7 +723,8 @@ namespace DisplayMagician
if (_audioController != null) if (_audioController != null)
{ {
try { try {
activeAudioDevices = _audioController.GetPlaybackDevices(DeviceState.Active).ToList(); activeAudioDevices = _audioController.GetPlaybackDevices(DeviceState.Active | DeviceState.Unplugged).ToList();
bool foundAudioDevice = false;
if (activeAudioDevices.Count > 0) if (activeAudioDevices.Count > 0)
{ {
// Change Audio Device (if one specified) // Change Audio Device (if one specified)
@ -669,21 +748,30 @@ namespace DisplayMagician
{ {
logger.Info($"ShortcutRepository/RunShortcut: Changing to the {shortcutToUse.AudioDevice} audio device."); logger.Info($"ShortcutRepository/RunShortcut: Changing to the {shortcutToUse.AudioDevice} audio device.");
foreach (CoreAudioDevice audioDevice in activeAudioDevices) foreach (CoreAudioDevice audioDevice in activeAudioDevices)
{ {
if (audioDevice.FullName.Equals(shortcutToUse.AudioDevice)) if (audioDevice.FullName.Equals(shortcutToUse.AudioDevice))
{ {
// use the Audio Device // use the Audio Device
audioDevice.SetAsDefault(); audioDevice.SetAsDefault();
foundAudioDevice = true;
break; break;
} }
} }
if (!foundAudioDevice)
{
logger.Error($"ShortcutRepository/RunShortcut: We wanted to use {shortcutToUse.AudioDevice} audio device but it wasn't plugged in or unplugged. Unable to use so skipping setting the audio device.");
}
} }
else else
{ {
logger.Info($"ShortcutRepository/RunShortcut: We're already using the {shortcutToUse.AudioDevice} audio device so no need to change audio devices."); logger.Info($"ShortcutRepository/RunShortcut: We're already using the {shortcutToUse.AudioDevice} audio device so no need to change audio devices.");
} }
if (foundAudioDevice)
{
if (shortcutToUse.SetAudioVolume) if (shortcutToUse.SetAudioVolume)
{ {
logger.Info($"ShortcutRepository/RunShortcut: Setting {shortcutToUse.AudioDevice} volume level to {shortcutToUse.AudioVolume}%."); logger.Info($"ShortcutRepository/RunShortcut: Setting {shortcutToUse.AudioDevice} volume level to {shortcutToUse.AudioVolume}%.");
@ -699,6 +787,7 @@ namespace DisplayMagician
logger.Info($"ShortcutRepository/RunShortcut: We don't need to set the {shortcutToUse.AudioDevice} volume level."); logger.Info($"ShortcutRepository/RunShortcut: We don't need to set the {shortcutToUse.AudioDevice} volume level.");
} }
} }
}
else else
{ {
logger.Info($"ShortcutRepository/RunShortcut: Shortcut does not require changing Audio Device."); logger.Info($"ShortcutRepository/RunShortcut: Shortcut does not require changing Audio Device.");
@ -717,8 +806,9 @@ namespace DisplayMagician
try try
{ {
// Get the list of Audio Devices currently connected // Get the list of Capture Devices currently connected or currently unplugged (they can be plugged back in)
activeCaptureDevices = _audioController.GetCaptureDevices(DeviceState.Active).ToList(); activeCaptureDevices = _audioController.GetCaptureDevices(DeviceState.Active | DeviceState.Unplugged).ToList();
bool foundCaptureDevice = false;
if (activeCaptureDevices.Count > 0) if (activeCaptureDevices.Count > 0)
{ {
@ -747,15 +837,23 @@ namespace DisplayMagician
{ {
// use the Audio Device // use the Audio Device
captureDevice.SetAsDefault(); captureDevice.SetAsDefault();
foundCaptureDevice = true;
break; break;
} }
} }
if (!foundCaptureDevice)
{
logger.Error($"ShortcutRepository/RunShortcut: We wanted to use {shortcutToUse.CaptureDevice} capture (microphone) device but it wasn't plugged in or unplugged. Unable to use so skipping setting the capture device.");
}
} }
else else
{ {
logger.Info($"ShortcutRepository/RunShortcut: We're already using the {shortcutToUse.CaptureDevice} capture (microphone) device so no need to change capture devices."); logger.Info($"ShortcutRepository/RunShortcut: We're already using the {shortcutToUse.CaptureDevice} capture (microphone) device so no need to change capture devices.");
} }
if (foundCaptureDevice)
{
if (shortcutToUse.SetCaptureVolume) if (shortcutToUse.SetCaptureVolume)
{ {
logger.Info($"ShortcutRepository/RunShortcut: Setting {shortcutToUse.CaptureDevice} capture (microphone) level to {shortcutToUse.CaptureVolume}%."); logger.Info($"ShortcutRepository/RunShortcut: Setting {shortcutToUse.CaptureDevice} capture (microphone) level to {shortcutToUse.CaptureVolume}%.");
@ -770,6 +868,7 @@ namespace DisplayMagician
{ {
logger.Info($"ShortcutRepository/RunShortcut: We don't need to set the {shortcutToUse.CaptureDevice} capture (microphone) volume level."); logger.Info($"ShortcutRepository/RunShortcut: We don't need to set the {shortcutToUse.CaptureDevice} capture (microphone) volume level.");
} }
}
} }
else else
@ -831,47 +930,28 @@ namespace DisplayMagician
} }
// Start the executable // Start the executable
logger.Info($"ShortcutRepository/RunShortcut: Starting process {processToStart.Executable}"); logger.Info($"ShortcutRepository/RunShortcut: Starting Start Program process {processToStart.Executable}");
Process process = null; //Process process = null;
List<Process> processesCreated = new List<Process>();
try try
{ {
uint processID = 0; processesCreated = ProcessUtils.StartProcess(processToStart.Executable, processToStart.Arguments, processToStart.ProcessPriority);
if (ProcessUtils.LaunchProcessWithPriority(processToStart.Executable, processToStart.Arguments, ProcessUtils.TranslatePriorityToClass(processToStart.ProcessPriority), out processID))
{
process = Process.GetProcessById((int)processID);
}
/*if (processToStart.ExecutableArgumentsRequired)
{
process = System.Diagnostics.Process.Start(processToStart.Executable, processToStart.Arguments);
}
else
{
process = System.Diagnostics.Process.Start(processToStart.Executable);
}*/
/*try
{
// Attempt to set the process priority to whatever the user wanted
logger.Trace($"ShortcutRepository/RunShortcut: Setting the start program process priority of start program we started to {shortcutToUse.ProcessPriority.ToString("G")}");
process.PriorityClass = TranslatePriorityClass(processToStart.ProcessPriority);
}
catch (Exception ex)
{
logger.Warn(ex, $"ShortcutRepository/RunShortcut: Exception setting the start program process priority of start program we started to {shortcutToUse.ProcessPriority.ToString("G")}");
}*/
// Record the program we started so we can close it later // Record the program we started so we can close it later
if (processToStart.CloseOnFinish) if (processToStart.CloseOnFinish)
{ {
logger.Debug($"ShortcutRepository/RunShortcut: We need to stop {processToStart.Executable} after the main game or executable is closed."); foreach (Process p in processesCreated)
startProgramsToStop.Add(process); {
logger.Debug($"ShortcutRepository/RunShortcut: We need to stop {p.StartInfo.FileName} after the main game or executable is closed.");
}
startProgramsToStop.AddRange(processesCreated);
} }
else else
{ {
logger.Debug($"ShortcutRepository/RunShortcut: No need to stop {processToStart.Executable} after the main game or executable is closed, so we'll just leave it running"); foreach (Process p in processesCreated)
{
logger.Debug($"ShortcutRepository/RunShortcut: No need to stop {p.StartInfo.FileName} after the main game or executable is closed, so we'll just leave it running");
}
} }
} }
catch (Win32Exception ex) catch (Win32Exception ex)
@ -957,27 +1037,65 @@ namespace DisplayMagician
} }
// Now start the main game, and wait if we have to // Now start the main game/exe, and wait if we have to
if (shortcutToUse.Category.Equals(ShortcutCategory.Application)) if (shortcutToUse.Category.Equals(ShortcutCategory.Application))
{ {
logger.Info($"ShortcutRepository/RunShortcut: Starting the main executable that we wanted to run, and that we're going to monitor and watch"); // Store the process to monitor for later
// Start the executable //IPCService.GetInstance().HoldProcessId = processesToMonitor.FirstOrDefault()?.Id ?? 0;
//IPCService.GetInstance().Status = InstanceStatus.OnHold;
try // Add a status notification icon in the status area
string notificationText = $"DisplayMagician: Running {shortcutToUse.ExecutableNameAndPath}...";
if (notificationText.Length >= 64)
{ {
Process process = null; string thingToRun = shortcutToUse.ExecutableNameAndPath.Substring(0, 34);
/*if (shortcutToUse.ExecutableArgumentsRequired) notifyIcon.Text = $"DisplayMagician: Running {thingToRun}...";
}
Application.DoEvents();
string processToMonitorName;
if (shortcutToUse.ProcessNameToMonitorUsesExecutable)
{ {
process = System.Diagnostics.Process.Start(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments); processToMonitorName = shortcutToUse.ExecutableNameAndPath;
} }
else else
{ {
process = System.Diagnostics.Process.Start(shortcutToUse.ExecutableNameAndPath); processToMonitorName = shortcutToUse.DifferentExecutableToMonitor;
}*/ }
uint processID = 0;
if (ProcessUtils.LaunchProcessWithPriority(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments, ProcessUtils.TranslatePriorityToClass(shortcutToUse.ProcessPriority), out processID)) logger.Debug($"ShortcutRepository/RunShortcut: Creating the Windows Toast to notify the user we're going to wait for the executable {shortcutToUse.ExecutableNameAndPath} to close.");
// Now we want to tell the user we're running an application!
// Construct the Windows toast content
ToastContentBuilder tcBuilder = new ToastContentBuilder()
.AddToastActivationInfo("notify=runningApplication", ToastActivationType.Foreground)
.AddText($"Running {shortcutToUse.ExecutableNameAndPath}", hintMaxLines: 1)
.AddText($"Waiting for all {processToMonitorName } processes to exit...")
.AddAudio(new Uri("ms-winsoundevent:Notification.Default"), false, true);
//.AddButton("Stop", ToastActivationType.Background, "notify=runningGame&action=stop");
ToastContent toastContent = tcBuilder.Content;
// Make sure to use Windows.Data.Xml.Dom
var doc = new XmlDocument();
doc.LoadXml(toastContent.GetContent());
// And create the toast notification
var toast = new ToastNotification(doc);
toast.SuppressPopup = false;
// Remove any other Notifications from us
DesktopNotifications.DesktopNotificationManagerCompat.History.Clear();
// And then show this notification
DesktopNotifications.DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
logger.Info($"ShortcutRepository/RunShortcut: Starting the main executable that we wanted to run, and that we're going to monitor and watch");
// Start the main executable
List<Process> processesCreated = new List<Process>();
try
{ {
process = Process.GetProcessById((int)processID); processesCreated = ProcessUtils.StartProcess(shortcutToUse.ExecutableNameAndPath, shortcutToUse.ExecutableArguments, shortcutToUse.ProcessPriority);
// Record the program we started so we can close it later
foreach (Process p in processesCreated)
{
logger.Debug($"ShortcutRepository/RunShortcut: {p.StartInfo.FileName} was launched when we started the main application {shortcutToUse.ExecutableNameAndPath}.");
} }
} }
@ -998,110 +1116,44 @@ namespace DisplayMagician
logger.Error(ex, $"ShortcutRepository/RunShortcut: Exception starting main executable process {shortcutToUse.ExecutableNameAndPath}. Method call is invalid for the current state."); logger.Error(ex, $"ShortcutRepository/RunShortcut: Exception starting main executable process {shortcutToUse.ExecutableNameAndPath}. Method call is invalid for the current state.");
} }
// Figure out what we want to look for // Wait an extra few seconds to give the application time to settle down
string processNameToLookFor; //Thread.Sleep(2000);
// Now we need to decide what we are monitoring. If the user has supplied an alternative process to monitor, then we monitor that instead!
bool foundSomethingToMonitor = false;
List<Process> processesToMonitor = new List<Process>();
if (shortcutToUse.ProcessNameToMonitorUsesExecutable) if (shortcutToUse.ProcessNameToMonitorUsesExecutable)
{ {
// If we are monitoring the same executable we started, then lets do get that name ready processesToMonitor = processesCreated;
processNameToLookFor = System.IO.Path.GetFileNameWithoutExtension(shortcutToUse.ExecutableNameAndPath); logger.Debug($"ShortcutRepository/RunShortcut: {processesToMonitor.Count} '{processToMonitorName}' created processes to monitor are running");
foundSomethingToMonitor = true;
} }
else else
{ {
// If we are monitoring a different executable, then lets do get that name ready instead // We use the a user supplied executable as the thing we're monitoring instead!
processNameToLookFor = System.IO.Path.GetFileNameWithoutExtension(shortcutToUse.DifferentExecutableToMonitor);
}
logger.Debug($"ShortcutRepository/RunShortcut: Looking for processes with the name {processNameToLookFor} so that we can monitor them and know when they are closed.");
// Now look for the thing we're supposed to monitor
// and wait until it starts up
List<Process> processesToMonitor = new List<Process>();
for (int secs = 0; secs <= (shortcutToUse.StartTimeout * 1000); secs += 500)
{
// Look for the processes with the ProcessName we sorted out earlier
processesToMonitor = Process.GetProcessesByName(processNameToLookFor).ToList();
// If we have found one or more processes then we should be good to go
// so let's break
if (processesToMonitor.Count > 0)
{
logger.Debug($"ShortcutRepository/RunShortcut: Found {processesToMonitor.Count} '{processNameToLookFor}' processes to monitor");
try try
{ {
foreach (Process monitoredProcess in processesToMonitor) processesToMonitor.AddRange(Process.GetProcessesByName(shortcutToUse.DifferentExecutableToMonitor));
logger.Trace($"ShortcutRepository/RunShortcut: {processesToMonitor.Count} '{shortcutToUse.DifferentExecutableToMonitor}' user specified processes to monitor are running");
foundSomethingToMonitor = true;
}
catch (Exception ex)
{ {
logger.Trace($"ShortcutRepository/RunShortcut: Setting priority of monitored executable process {processNameToLookFor} to {shortcutToUse.ProcessPriority.ToString("G")}"); logger.Error($"ShortcutRepository/RunShortcut: Exception while trying to find the user supplied executable to monitor: {shortcutToUse.DifferentExecutableToMonitor}.");
monitoredProcess.PriorityClass = TranslatePriorityClassToClass(shortcutToUse.ProcessPriority); foundSomethingToMonitor = false;
} }
} }
catch(Exception ex)
{
logger.Warn(ex, $"ShortcutRepository/RunShortcut: Exception Setting priority of monitored executable process {processNameToLookFor} to {shortcutToUse.ProcessPriority.ToString("G")}");
}
break;
}
// Let's wait a little while if we couldn't find
// any processes yet
Thread.Sleep(500);
}
// make sure we have things to monitor and alert if not
if (processesToMonitor.Count == 0)
{
logger.Error($"ShortcutRepository/RunShortcut: No '{processNameToLookFor}' processes found before waiting timeout. DisplayMagician was unable to find any processes before the {shortcutToUse.StartTimeout} second timeout");
}
// Store the process to monitor for later
//IPCService.GetInstance().HoldProcessId = processesToMonitor.FirstOrDefault()?.Id ?? 0;
//IPCService.GetInstance().Status = InstanceStatus.OnHold;
// Add a status notification icon in the status area
string notificationText = $"DisplayMagician: Running {shortcutToUse.ExecutableNameAndPath}...";
if (notificationText.Length >= 64)
{
string thingToRun = shortcutToUse.ExecutableNameAndPath.Substring(0, 35);
notifyIcon.Text = $"DisplayMagician: Running {thingToRun}...";
}
Application.DoEvents();
logger.Debug($"ShortcutRepository/RunShortcut: Creating the Windows Toast to notify the user we're going to wait for the executable {shortcutToUse.ExecutableNameAndPath} to close.");
// Now we want to tell the user we're running an application!
// Construct the Windows toast content
ToastContentBuilder tcBuilder = new ToastContentBuilder()
.AddToastActivationInfo("notify=runningApplication", ToastActivationType.Foreground)
.AddText($"Running {processNameToLookFor}", hintMaxLines: 1)
.AddText($"Waiting for all {processNameToLookFor} windows to exit...")
.AddAudio(new Uri("ms-winsoundevent:Notification.Default"),false,true);
//.AddButton("Stop", ToastActivationType.Background, "notify=runningGame&action=stop");
ToastContent toastContent = tcBuilder.Content;
// Make sure to use Windows.Data.Xml.Dom
var doc = new XmlDocument();
doc.LoadXml(toastContent.GetContent());
// And create the toast notification
var toast = new ToastNotification(doc);
toast.SuppressPopup = false;
// Remove any other Notifications from us
DesktopNotifications.DesktopNotificationManagerCompat.History.Clear();
// And then show this notification
DesktopNotifications.DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
// Wait an extra few seconds to give the application time to settle down
Thread.Sleep(2000);
// if we have things to monitor, then we should start to wait for them // if we have things to monitor, then we should start to wait for them
logger.Debug($"ShortcutRepository/RunShortcut: Waiting for application {processNameToLookFor} to exit."); logger.Debug($"ShortcutRepository/RunShortcut: Waiting for application {shortcutToUse.ExecutableNameAndPath} to exit.");
if (processesToMonitor.Count > 0) if (foundSomethingToMonitor && processesToMonitor.Count > 0)
{ {
logger.Debug($"ShortcutRepository/RunShortcut: {processesToMonitor.Count} '{processNameToLookFor}' processes are still running");
while (true) while (true)
{ {
processesToMonitor = Process.GetProcessesByName(processNameToLookFor).ToList();
// If we have no more processes left then we're done! // If we have no more processes left then we're done!
if (processesToMonitor.Count == 0) if (ProcessUtils.ProcessExited(processesToMonitor))
{ {
logger.Debug($"ShortcutRepository/RunShortcut: No more '{processNameToLookFor}' processes are still running"); logger.Debug($"ShortcutRepository/RunShortcut: No more processes to monitor are still running. It, and all it's child processes have exited!");
break; break;
} }
@ -1111,15 +1163,14 @@ namespace DisplayMagician
Thread.Sleep(1000); Thread.Sleep(1000);
} }
} }
logger.Info($"ShortcutRepository/RunShortcut: Executable {processNameToLookFor} has exited.");
logger.Debug($"ShortcutRepository/RunShortcut: Creating a Windows Toast to notify the user that the executable {shortcutToUse.ExecutableNameAndPath} has closed."); logger.Debug($"ShortcutRepository/RunShortcut: Creating a Windows Toast to notify the user that the executable {shortcutToUse.ExecutableNameAndPath} has closed.");
// Tell the user that the application has closed // Tell the user that the application has closed
// Construct the toast content // Construct the toast content
tcBuilder = new ToastContentBuilder() tcBuilder = new ToastContentBuilder()
.AddToastActivationInfo("notify=stopDetected", ToastActivationType.Foreground) .AddToastActivationInfo("notify=stopDetected", ToastActivationType.Foreground)
.AddText($"{processNameToLookFor} was closed", hintMaxLines: 1) .AddText($"{shortcutToUse.ExecutableNameAndPath} was closed", hintMaxLines: 1)
.AddText($"All {processNameToLookFor} processes were shutdown and changes were reverted.") .AddText($"All {processToMonitorName} processes were shutdown and changes were reverted.")
.AddAudio(new Uri("ms-winsoundevent:Notification.Default"), false, true); .AddAudio(new Uri("ms-winsoundevent:Notification.Default"), false, true);
toastContent = tcBuilder.Content; toastContent = tcBuilder.Content;
// Make sure to use Windows.Data.Xml.Dom // Make sure to use Windows.Data.Xml.Dom
@ -1176,6 +1227,25 @@ namespace DisplayMagician
if (gameToRun != null) if (gameToRun != null)
{ {
string processToMonitorName;
if (shortcutToUse.MonitorDifferentGameExe)
{
processToMonitorName = shortcutToUse.DifferentGameExeToMonitor;
}
else
{
processToMonitorName = gameToRun.ExePath;
}
// Add a status notification icon in the status area
string notificationText = $"DisplayMagician: Running {gameLibraryToUse.GameLibraryName}...";
if (notificationText.Length >= 64)
{
string thingToRun = gameLibraryToUse.GameLibraryName.Substring(0, 34);
notifyIcon.Text = $"DisplayMagician: Running {thingToRun}...";
}
Application.DoEvents();
// Now we want to tell the user we're start a game // Now we want to tell the user we're start a game
// Construct the Windows toast content // Construct the Windows toast content
ToastContentBuilder tcBuilder = new ToastContentBuilder() ToastContentBuilder tcBuilder = new ToastContentBuilder()
@ -1195,10 +1265,8 @@ namespace DisplayMagician
// And then show this notification // And then show this notification
DesktopNotifications.DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast); DesktopNotifications.DesktopNotificationManagerCompat.CreateToastNotifier().Show(toast);
Process gameProcess; List<Process> gameProcesses;
//string gameRunCmd = gameLibraryToUse.GetRunCmd(gameToRun, shortcutToUse.GameArguments); gameProcesses = gameLibraryToUse.StartGame(gameToRun, shortcutToUse.GameArguments, shortcutToUse.ProcessPriority);
//gameProcess = Process.Start(gameRunCmd);
gameProcess = gameLibraryToUse.StartGame(gameToRun, shortcutToUse.GameArguments, ProcessUtils.TranslatePriorityToClass(shortcutToUse.ProcessPriority));
// Delay 500ms // Delay 500ms
Thread.Sleep(500); Thread.Sleep(500);
@ -1313,6 +1381,16 @@ namespace DisplayMagician
} }
// Now we actually start looking for and monitoring the game!
notificationText = $"DisplayMagician: Running {gameToRun.Name}...";
if (notificationText.Length >= 64)
{
string thingToRun = gameToRun.Name.Substring(0, 34);
notifyIcon.Text = $"DisplayMagician: Running {thingToRun}...";
}
Application.DoEvents();
// At this point, if the user wants to actually monitor a different process, // At this point, if the user wants to actually monitor a different process,
// then we actually need to monitor that instead // then we actually need to monitor that instead
if (shortcutToUse.MonitorDifferentGameExe) if (shortcutToUse.MonitorDifferentGameExe)
@ -1320,13 +1398,6 @@ namespace DisplayMagician
// If we are monitoring a different executable rather than the game itself, then lets get that name ready instead // If we are monitoring a different executable rather than the game itself, then lets get that name ready instead
string altGameProcessToMonitor = System.IO.Path.GetFileNameWithoutExtension(shortcutToUse.DifferentGameExeToMonitor); string altGameProcessToMonitor = System.IO.Path.GetFileNameWithoutExtension(shortcutToUse.DifferentGameExeToMonitor);
// Add a status notification icon in the status area
if (gameToRun.Name.Length <= 41)
notifyIcon.Text = $"DisplayMagician: Running {gameToRun.Name}...";
else
notifyIcon.Text = $"DisplayMagician: Running {gameToRun.Name.Substring(0, 41)}...";
Application.DoEvents();
// Now look for the thing we're supposed to monitor // Now look for the thing we're supposed to monitor
// and wait until it starts up // and wait until it starts up
List<Process> processesToMonitor = new List<Process>(); List<Process> processesToMonitor = new List<Process>();
@ -1353,7 +1424,6 @@ namespace DisplayMagician
{ {
logger.Warn(ex, $"ShortcutRepository/RunShortcut: Setting priority of alternative game monitored process {altGameProcessToMonitor} to {shortcutToUse.ProcessPriority.ToString("G")}"); logger.Warn(ex, $"ShortcutRepository/RunShortcut: Setting priority of alternative game monitored process {altGameProcessToMonitor} to {shortcutToUse.ProcessPriority.ToString("G")}");
} }
break; break;
} }
@ -1562,13 +1632,6 @@ namespace DisplayMagician
{ {
// we are monitoring the game thats actually running (the most common scenario) // we are monitoring the game thats actually running (the most common scenario)
// Add a status notification icon in the status area
if (gameToRun.Name.Length <= 41)
notifyIcon.Text = $"DisplayMagician: Running {gameToRun.Name}...";
else
notifyIcon.Text = $"DisplayMagician: Running {gameToRun.Name.Substring(0, 41)}...";
Application.DoEvents();
// Now we want to tell the user we're running a game! // Now we want to tell the user we're running a game!
// Construct the Windows toast content // Construct the Windows toast content
tcBuilder = new ToastContentBuilder() tcBuilder = new ToastContentBuilder()
@ -1755,76 +1818,8 @@ namespace DisplayMagician
{ {
logger.Debug($"ShortcutRepository/RunShortcut: We started {startProgramsToStart.Count} programs before the main executable or game, and now we want to stop {startProgramsToStop.Count } of them"); logger.Debug($"ShortcutRepository/RunShortcut: We started {startProgramsToStart.Count} programs before the main executable or game, and now we want to stop {startProgramsToStop.Count } of them");
// Prepare the processInfos we need for finding child processes. // Shutdown the processes
ProcessUtils.Initialise(); ProcessUtils.StopProcess(startProgramsToStop);
// Stop the programs in the reverse order we started them
foreach (Process processToStop in startProgramsToStop.Reverse<Process>())
{
bool stoppedMainProcess = false;
// Stop the process if it hasn't stopped already
if (!processToStop.HasExited)
{
logger.Debug($"ShortcutRepository/RunShortcut: Stopping process {processToStop.StartInfo.FileName}");
if (ProcessUtils.StopProcess(processToStop))
{
logger.Debug($"ShortcutRepository/RunShortcut: Successfully stopped process {processToStop.StartInfo.FileName}");
stoppedMainProcess = true;
}
}
// Next, check whether it had any other processes it started itself
// (copes with loader processes that perform the initial start, then run the main exe)
// If so, we need to go through and find and close all subprocesses
List<Process> childProcesses = ProcessUtils.FindChildProcesses(processToStop);
if (childProcesses.Count > 0)
{
foreach (Process childProcessToStop in childProcesses)
{
if (processToStop.HasExited)
{
// if there were no child processes, and the only process has already exited (e.g. the user exited it themselves)
// then stop trying to stop the process, and instead log the fact it already stopped.
Console.WriteLine($"Stopping child process {childProcessToStop.StartInfo.FileName} but was already stopped by user or another process.");
logger.Warn($"ShortcutRepository/RunShortcut: Stopping child process {childProcessToStop.StartInfo.FileName} but was already stopped by user or another process.");
continue;
}
Console.WriteLine($"Stopping child process {childProcessToStop.StartInfo.FileName} of parent process {processToStop.StartInfo.FileName}");
logger.Debug($"ShortcutRepository/RunShortcut: Stopping child process {childProcessToStop.StartInfo.FileName} of parent process {processToStop.StartInfo.FileName}");
ProcessUtils.StopProcess(childProcessToStop);
}
}
// if the only main process has already exited (e.g. the user exited it themselves)
// then we try to stop any processes with the same name as the application we started
// Look for the processes with the ProcessName we sorted out earlier
// Basically, if we haven't stopped all the children processes, then this is the last gasp
if (!stoppedMainProcess)
{
string processName = Path.GetFileNameWithoutExtension(processToStop.StartInfo.FileName);
List<Process> namedProcessesToStop = Process.GetProcessesByName(processName).ToList();
// If we have found one or more processes then we should be good to go
if (namedProcessesToStop.Count > 0)
{
logger.Warn($"ShortcutRepository/RunShortcut: We couldn't find any children processes so we've looked for named processes with the name '{processToStop.StartInfo.FileName}' and we found {namedProcessesToStop.Count}. Closing them.");
foreach (Process namedProcessToStop in namedProcessesToStop)
{
ProcessUtils.StopProcess(namedProcessToStop);
}
}
else
{
// then give up trying to stop the process, and instead log the fact it already stopped.
Console.WriteLine($"Stopping only process {processToStop.StartInfo.FileName} but was already stopped by user or another process.");
logger.Debug($"ShortcutRepository/RunShortcut: Stopping only process {processToStop.StartInfo.FileName} but was already stopped by user or another process.");
}
continue;
}
}
} }
// Change Audio Device back (if one specified) // Change Audio Device back (if one specified)
@ -1899,16 +1894,18 @@ namespace DisplayMagician
if (result == ApplyProfileResult.Error) if (result == ApplyProfileResult.Error)
{ {
Console.WriteLine($"ERROR - Cannot revert back to '{rollbackProfile.Name}' Display Profile");
logger.Error($"ShortcutRepository/RunShortcut: Error rolling back display profile to {rollbackProfile.Name}"); logger.Error($"ShortcutRepository/RunShortcut: Error rolling back display profile to {rollbackProfile.Name}");
return; return;
} }
else if (result == ApplyProfileResult.Cancelled) else if (result == ApplyProfileResult.Cancelled)
{ {
Console.WriteLine($"ERROR - User cancelled revert back to '{rollbackProfile.Name}' Display Profile");
logger.Error($"ShortcutRepository/RunShortcut: User cancelled rolling back display profile to {rollbackProfile.Name}"); logger.Error($"ShortcutRepository/RunShortcut: User cancelled rolling back display profile to {rollbackProfile.Name}");
return; return;
} }
else if (result == ApplyProfileResult.Successful)
{
logger.Trace($"ShortcutRepository/RunShortcut: Successfully rolled back display profile to {rollbackProfile.Name}");
}
} }
else else
@ -1916,6 +1913,35 @@ namespace DisplayMagician
logger.Debug($"ShortcutRepository/RunShortcut: Shortcut did not require changing Display Profile, so no need to change it back."); logger.Debug($"ShortcutRepository/RunShortcut: Shortcut did not require changing Display Profile, so no need to change it back.");
} }
// And finally run the stop program we have
if (shortcutToUse.StopPrograms.Count > 0)
{
// At the moment we only allow one stop program
StopProgram stopProg = shortcutToUse.StopPrograms[0];
uint processID = 0;
try
{
ProcessUtils.PROCESS_INFORMATION processInfo;
if (ProcessUtils.CreateProcessWithPriority(stopProg.Executable, stopProg.Arguments, ProcessUtils.TranslatePriorityToClass(stopProg.ProcessPriority), out processInfo))
{
logger.Trace($"ShortcutRepository/RunShortcut: Successfully started Stop Program {stopProg.Executable} {stopProg.Arguments}");
}
else
{
logger.Warn($"ShortcutRepository/RunShortcut: Unable to start Stop Program {stopProg.Executable} {stopProg.Arguments}");
}
}
catch (Exception ex)
{
logger.Warn(ex, $"ShortcutRepository/RunShortcut: Exception while starting Stop Program {stopProg.Executable} {stopProg.Arguments}");
}
}
// Reset the popup over the system tray icon to what's normal for it.
notifyIcon.Text = $"DisplayMagician";
Application.DoEvents();
} }
#endregion #endregion

View File

@ -0,0 +1,211 @@

namespace DisplayMagician.UIForms
{
partial class ChooseImageForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ChooseImageForm));
this.lv_icons = new System.Windows.Forms.ListView();
this.columnHeaderName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.columnHeaderSize = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.pb_selected_icon = new System.Windows.Forms.PictureBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.btn_add = new System.Windows.Forms.Button();
this.btn_select = new System.Windows.Forms.Button();
this.btn_back = new System.Windows.Forms.Button();
this.dialog_open = new System.Windows.Forms.OpenFileDialog();
this.btn_remove = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.pb_selected_icon)).BeginInit();
this.SuspendLayout();
//
// lv_icons
//
this.lv_icons.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeaderName,
this.columnHeaderSize});
this.lv_icons.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
this.lv_icons.HideSelection = false;
this.lv_icons.Location = new System.Drawing.Point(26, 40);
this.lv_icons.MultiSelect = false;
this.lv_icons.Name = "lv_icons";
this.lv_icons.ShowGroups = false;
this.lv_icons.Size = new System.Drawing.Size(370, 200);
this.lv_icons.TabIndex = 0;
this.lv_icons.UseCompatibleStateImageBehavior = false;
this.lv_icons.View = System.Windows.Forms.View.Details;
this.lv_icons.SelectedIndexChanged += new System.EventHandler(this.lv_icons_SelectedIndexChanged);
//
// columnHeaderName
//
this.columnHeaderName.Text = "Name";
this.columnHeaderName.Width = 270;
//
// columnHeaderSize
//
this.columnHeaderSize.Text = "Size";
this.columnHeaderSize.Width = 70;
//
// pb_selected_icon
//
this.pb_selected_icon.BackColor = System.Drawing.Color.DimGray;
this.pb_selected_icon.Location = new System.Drawing.Point(402, 40);
this.pb_selected_icon.Name = "pb_selected_icon";
this.pb_selected_icon.Size = new System.Drawing.Size(200, 200);
this.pb_selected_icon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pb_selected_icon.TabIndex = 1;
this.pb_selected_icon.TabStop = false;
//
// label1
//
this.label1.AutoSize = true;
this.label1.BackColor = System.Drawing.Color.Black;
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(139, 21);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(138, 16);
this.label1.TabIndex = 2;
this.label1.Text = "Choose image to use:";
this.label1.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// label2
//
this.label2.AutoSize = true;
this.label2.BackColor = System.Drawing.Color.Black;
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.ForeColor = System.Drawing.Color.White;
this.label2.Location = new System.Drawing.Point(457, 21);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(106, 16);
this.label2.TabIndex = 3;
this.label2.Text = "Selected image:";
this.label2.TextAlign = System.Drawing.ContentAlignment.TopCenter;
//
// btn_add
//
this.btn_add.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_add.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_add.ForeColor = System.Drawing.Color.White;
this.btn_add.Location = new System.Drawing.Point(26, 261);
this.btn_add.Name = "btn_add";
this.btn_add.Size = new System.Drawing.Size(86, 30);
this.btn_add.TabIndex = 39;
this.btn_add.Text = "Add images";
this.btn_add.UseVisualStyleBackColor = true;
this.btn_add.Click += new System.EventHandler(this.btn_add_Click);
//
// btn_select
//
this.btn_select.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_select.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_select.ForeColor = System.Drawing.Color.White;
this.btn_select.Location = new System.Drawing.Point(305, 261);
this.btn_select.Name = "btn_select";
this.btn_select.Size = new System.Drawing.Size(178, 30);
this.btn_select.TabIndex = 40;
this.btn_select.Text = "Save and use selected image";
this.btn_select.UseVisualStyleBackColor = true;
this.btn_select.Click += new System.EventHandler(this.btn_select_Click);
//
// btn_back
//
this.btn_back.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_back.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_back.ForeColor = System.Drawing.Color.White;
this.btn_back.Location = new System.Drawing.Point(525, 261);
this.btn_back.Name = "btn_back";
this.btn_back.Size = new System.Drawing.Size(79, 30);
this.btn_back.TabIndex = 41;
this.btn_back.Text = "Back";
this.btn_back.UseVisualStyleBackColor = true;
this.btn_back.Click += new System.EventHandler(this.btn_back_Click);
//
// dialog_open
//
this.dialog_open.FileName = "openFileDialog1";
//
// btn_remove
//
this.btn_remove.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_remove.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_remove.ForeColor = System.Drawing.Color.White;
this.btn_remove.Location = new System.Drawing.Point(153, 261);
this.btn_remove.Name = "btn_remove";
this.btn_remove.Size = new System.Drawing.Size(113, 30);
this.btn_remove.TabIndex = 42;
this.btn_remove.Text = "Remove image";
this.btn_remove.UseVisualStyleBackColor = true;
this.btn_remove.Click += new System.EventHandler(this.btn_remove_Click);
//
// ChooseImageForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Black;
this.ClientSize = new System.Drawing.Size(629, 319);
this.Controls.Add(this.btn_remove);
this.Controls.Add(this.btn_back);
this.Controls.Add(this.btn_select);
this.Controls.Add(this.btn_add);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.pb_selected_icon);
this.Controls.Add(this.lv_icons);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ChooseImageForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Choose shortcut image";
this.TopMost = true;
this.Load += new System.EventHandler(this.ChooseIconForm_Load);
((System.ComponentModel.ISupportInitialize)(this.pb_selected_icon)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ListView lv_icons;
private System.Windows.Forms.PictureBox pb_selected_icon;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button btn_add;
private System.Windows.Forms.Button btn_select;
private System.Windows.Forms.Button btn_back;
private System.Windows.Forms.ColumnHeader columnHeaderName;
private System.Windows.Forms.ColumnHeader columnHeaderSize;
private System.Windows.Forms.OpenFileDialog dialog_open;
private System.Windows.Forms.Button btn_remove;
}
}

View File

@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DisplayMagician.UIForms
{
public partial class ChooseImageForm : Form
{
private ShortcutBitmap _selectedImage = new ShortcutBitmap();
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public ChooseImageForm()
{
InitializeComponent();
}
public List<ShortcutBitmap> AvailableImages
{
get;
set;
}
public ShortcutBitmap SelectedImage
{
get;
set;
}
private void btn_back_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
this.Close();
}
private void ChooseIconForm_Load(object sender, EventArgs e)
{
UpdateImageListBox();
}
private void UpdateImageListBox()
{
lv_icons.Items.Clear();
if (AvailableImages.Count > 0)
{
foreach (ShortcutBitmap sc in AvailableImages)
{
string[] stringsToAdd = new string[] {
$"Image {sc.Order} from {sc.Name}",
$"{sc.Size.Width} x {sc.Size.Height}"
};
ListViewItem lvi = new ListViewItem(stringsToAdd);
lvi.Name = sc.UUID;
if (ImageUtils.ImagesAreEqual(sc.Image,SelectedImage.Image))
{
lvi.Selected = true;
lvi.Focused = true;
lvi.EnsureVisible();
pb_selected_icon.Image = SelectedImage.Image;
_selectedImage = sc;
}
lv_icons.Items.Add(lvi);
}
// Select the first largest image listed if there isn't one already
if (lv_icons.SelectedItems.Count == 0)
{
lv_icons.Items[0].Selected = true;
pb_selected_icon.Image = AvailableImages[0].Image;
_selectedImage = AvailableImages[0];
}
}
}
private void btn_select_Click(object sender, EventArgs e)
{
SelectedImage = _selectedImage;
DialogResult = DialogResult.OK;
this.Close();
}
private void lv_icons_SelectedIndexChanged(object sender, EventArgs e)
{
if (lv_icons.SelectedItems.Count > 0)
{
string uuidToFind = lv_icons.SelectedItems[0].Name;
foreach (ShortcutBitmap sc in AvailableImages)
{
if (sc.UUID.Equals(uuidToFind))
{
pb_selected_icon.Image = sc.Image;
_selectedImage = sc;
break;
}
}
}
}
private void btn_add_Click(object sender, EventArgs e)
{
dialog_open.InitialDirectory = Program.AppDownloadsPath;
dialog_open.DefaultExt = "*.exe; *.com; *.ico; *.bmp; *.jpg; *.png; *.tif; *.gif";
dialog_open.Filter = "All exe and image files (*.exe; *.com; *.ico; *.bmp; *.jpg; *.png; *.tif; *.gif) | *.exe; *.com; *.ico; *.bmp; *.jpg; *.png; *.tif; *.gif | All files(*.*) | *.*";
if (dialog_open.ShowDialog(this) == DialogResult.OK)
{
if (File.Exists(dialog_open.FileName))
{
try
{
List<ShortcutBitmap> newImages = ImageUtils.GetMeAllBitmapsFromFile(dialog_open.FileName);
if (newImages.Count == 0)
{
logger.Trace($"No new images found when parsing {dialog_open.FileName} for images. Are you sure it's a valid image format?");
MessageBox.Show(
$"No new images found when parsing {dialog_open.FileName} for images. Are you sure it's a valid image format?",
"Add images to icon",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
else
{
AvailableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(dialog_open.FileName));
UpdateImageListBox();
logger.Trace($"ChooseImageForm/btn_add_Click: Added {newImages.Count} image(s) from {dialog_open.FileName} to the end of this image list.");
MessageBox.Show(
$"Added {newImages.Count} image(s) from {dialog_open.FileName} to the end of this image list.",
"Add images to icon",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
dialog_open.FileName = string.Empty;
}
catch(Exception ex)
{
logger.Warn(ex, $"ChooseImageForm/btn_add_Click: Exception - unable to parse {dialog_open.FileName} for images. Are you sure it's a valid image format?");
MessageBox.Show(
$"Unable to parse {dialog_open.FileName} for images. Are you sure it's a valid image format?",
"Add images to icon",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
}
else
{
logger.Warn($"ChooseImageForm/btn_add_Click: Unable to open {dialog_open.FileName} to parse it for images. Are you sure you have the right file permissions?");
MessageBox.Show(
$"Unable to open {dialog_open.FileName} to parse it for images. Are you sure you have the right file permissions?",
"Add images to icon",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
}
}
private void btn_remove_Click(object sender, EventArgs e)
{
if (lv_icons.SelectedItems.Count > 0)
{
string uuidToFind = lv_icons.SelectedItems[0].Name;
foreach (ShortcutBitmap sc in AvailableImages)
{
if (sc.UUID.Equals(uuidToFind))
{
AvailableImages.Remove(sc);
pb_selected_icon.Image = null;
_selectedImage = default(ShortcutBitmap);
UpdateImageListBox();
return;
}
}
// If we get here we didn't find anything to remove!
MessageBox.Show(
$"Unable to remove selected item from the list of images!",
"Remove image from icon",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -56,10 +56,25 @@ namespace DisplayMagician.UIForms
// Apply the Profile // Apply the Profile
if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Successful) if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Successful)
{ {
logger.Error($"DisplayProfileForm/Apply_Click: Waiting 0.5 sec for the display to apply"); 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); System.Threading.Thread.Sleep(500);
logger.Trace($"DisplayProfileForm/Apply_Click: Changing the selected profile in the imagelistview to Profile {_selectedProfile.Name}.");
ChangeSelectedProfile(_selectedProfile); ChangeSelectedProfile(_selectedProfile);
} }
else if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Cancelled)
{
logger.Warn($"DisplayProfileForm/Apply_Click: The user cancelled changing to Profile {_selectedProfile.Name}.");
}
else
{
logger.Warn($"DisplayProfileForm/Apply_Click: Error applying the Profile {_selectedProfile.Name}. Unable to change the display layout.");
}
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
} }
@ -115,6 +130,15 @@ namespace DisplayMagician.UIForms
} }
// 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) private void Save_Click(object sender, EventArgs e)
@ -169,6 +193,7 @@ namespace DisplayMagician.UIForms
{ {
MessageBox.Show(ex.Message, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Warning); MessageBox.Show(ex.Message, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Warning);
} }
} }
} }
@ -316,6 +341,12 @@ namespace DisplayMagician.UIForms
// Refresh the image list view // Refresh the image list view
//RefreshImageListView(profile); //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 // And finally refresh the profile in the display view
dv_profile.Profile = profile; dv_profile.Profile = profile;
dv_profile.Refresh(); dv_profile.Refresh();
@ -416,6 +447,12 @@ namespace DisplayMagician.UIForms
// now update the profiles image listview // now update the profiles image listview
RefreshDisplayProfileUI(); 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) private void ilv_saved_profiles_ItemClick(object sender, ItemClickEventArgs e)

View File

@ -46,7 +46,7 @@ namespace DisplayMagician.UIForms
Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(() => { return false; }); Image.GetThumbnailImageAbort myCallback = new Image.GetThumbnailImageAbort(() => { return false; });
return game.GameBitmap.GetThumbnailImage(256, 256, myCallback, IntPtr.Zero); return game.GameBitmap.Image.GetThumbnailImage(256, 256, myCallback, IntPtr.Zero);
} }
catch (Exception ex) catch (Exception ex)
@ -159,7 +159,7 @@ namespace DisplayMagician.UIForms
{ {
try try
{ {
mySizeF = game.GameBitmap.PhysicalDimension; mySizeF = game.GameBitmap.Image.PhysicalDimension;
gotSizeF = true; gotSizeF = true;
} }
catch (Exception ex) catch (Exception ex)

View File

@ -0,0 +1,94 @@

namespace DisplayMagician.UIForms
{
partial class LoadingForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LoadingForm));
this.lbl_title = new System.Windows.Forms.Label();
this.lbl_description = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// lbl_title
//
this.lbl_title.BackColor = System.Drawing.Color.Black;
this.lbl_title.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbl_title.ForeColor = System.Drawing.Color.White;
this.lbl_title.Location = new System.Drawing.Point(36, 26);
this.lbl_title.Name = "lbl_title";
this.lbl_title.Size = new System.Drawing.Size(347, 30);
this.lbl_title.TabIndex = 2;
this.lbl_title.Text = "DisplayMagician is loading...";
this.lbl_title.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_title.UseWaitCursor = true;
//
// lbl_description
//
this.lbl_description.BackColor = System.Drawing.Color.WhiteSmoke;
this.lbl_description.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbl_description.Location = new System.Drawing.Point(36, 56);
this.lbl_description.Name = "lbl_description";
this.lbl_description.Size = new System.Drawing.Size(347, 48);
this.lbl_description.TabIndex = 3;
this.lbl_description.Text = "If you have installed a lot of games over time or have a lot of games installed n" +
"ow, this may take a while!";
this.lbl_description.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_description.UseWaitCursor = true;
//
// LoadingForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
this.ClientSize = new System.Drawing.Size(418, 129);
this.ControlBox = false;
this.Controls.Add(this.lbl_description);
this.Controls.Add(this.lbl_title);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(434, 168);
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(434, 168);
this.Name = "LoadingForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "DisplayMagician is loading....";
this.UseWaitCursor = true;
this.Load += new System.EventHandler(this.LoadingForm_Load);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Label lbl_title;
private System.Windows.Forms.Label lbl_description;
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DisplayMagician.UIForms
{
public partial class LoadingForm : Form
{
private string _title = "DisplayMagician is loading...";
private string _description = "If you have installed a lot of games over time or have a lot of games installed now, this may take a while!";
public LoadingForm()
{
InitializeComponent();
this.TopMost = false;
}
public string Title
{
get
{
return _title;
}
set
{
_title = value;
lbl_title.Text = _title;
this.Text = _title;
}
}
public string Description
{
get
{
return _description;
}
set
{
_description = value;
lbl_description.Text = _description;
}
}
private void LoadingForm_Load(object sender, EventArgs e)
{
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -125,6 +125,10 @@ namespace DisplayMagician.UIForms
} }
} }
// Shut down the splash screen
if (Program.AppProgramSettings.ShowSplashScreen && Program.AppSplashScreen != null && !Program.AppSplashScreen.Disposing && !Program.AppSplashScreen.IsDisposed)
Program.AppSplashScreen.Invoke(new Action(() => Program.AppSplashScreen.Close()));
if (Program.AppProgramSettings.MinimiseOnStart) if (Program.AppProgramSettings.MinimiseOnStart)
{ {
// Make the form minimised on start // Make the form minimised on start
@ -140,7 +144,8 @@ namespace DisplayMagician.UIForms
ToastContentBuilder tcBuilder = new ToastContentBuilder() ToastContentBuilder tcBuilder = new ToastContentBuilder()
.AddToastActivationInfo("notify=minimiseStart&action=open", ToastActivationType.Foreground) .AddToastActivationInfo("notify=minimiseStart&action=open", ToastActivationType.Foreground)
.AddText("DisplayMagician is minimised", hintMaxLines: 1) .AddText("DisplayMagician is minimised", hintMaxLines: 1)
.AddButton("Open", ToastActivationType.Background, "notify=minimiseStart&action=open"); .AddButton("Open", ToastActivationType.Background, "notify=minimiseStart&action=open")
.SetToastDuration(ToastDuration.Short);
ToastContent toastContent = tcBuilder.Content; ToastContent toastContent = tcBuilder.Content;
// Make sure to use Windows.Data.Xml.Dom // Make sure to use Windows.Data.Xml.Dom
var doc = new XmlDocument(); var doc = new XmlDocument();
@ -184,8 +189,16 @@ namespace DisplayMagician.UIForms
var shortcutLibraryForm = new ShortcutLibraryForm(); var shortcutLibraryForm = new ShortcutLibraryForm();
shortcutLibraryForm.ShowDialog(this); shortcutLibraryForm.ShowDialog(this);
} }
else
{
// Make this window top most if we're not minimised
if (!Program.AppProgramSettings.MinimiseOnStart)
{
this.TopMost = true;
this.Activate();
this.TopMost = false;
}
}
} }
@ -317,7 +330,7 @@ namespace DisplayMagician.UIForms
} }
private void RefreshNotifyIconMenus() public void RefreshNotifyIconMenus()
{ {
// Clear all the profiles // Clear all the profiles
profileToolStripMenuItem.DropDownItems.Clear(); profileToolStripMenuItem.DropDownItems.Clear();
@ -338,10 +351,19 @@ namespace DisplayMagician.UIForms
foreach (ProfileItem profile in ProfileRepository.AllProfiles) foreach (ProfileItem profile in ProfileRepository.AllProfiles)
{ {
ToolStripMenuItem profileMenuItem = new ToolStripMenuItem(profile.Name, profile.ProfileBitmap, runProfileToolStripMenuItem_Click); ToolStripMenuItem profileMenuItem = new ToolStripMenuItem(profile.Name, profile.ProfileBitmap, runProfileToolStripMenuItem_Click);
if (profile.IsActive || !profile.IsPossible) if (!profile.IsPossible)
{
profileMenuItem.Enabled = false; profileMenuItem.Enabled = false;
else }
else if (profile.IsActive)
{
profileMenuItem.Enabled = true; profileMenuItem.Enabled = true;
profileMenuItem.Font = new Font(profileMenuItem.Font, FontStyle.Bold);
}
else
{
profileMenuItem.Enabled = true;
}
profileToolStripMenuItem.DropDownItems.Add(profileMenuItem); profileToolStripMenuItem.DropDownItems.Add(profileMenuItem);
} }
@ -371,6 +393,8 @@ namespace DisplayMagician.UIForms
} }
} }
// Apply it by running the Application.DoEvents();
Application.DoEvents();
} }
@ -392,6 +416,12 @@ namespace DisplayMagician.UIForms
// Run the shortcut if it's still there // Run the shortcut if it's still there
if (profileToRun != null) if (profileToRun != null)
ProfileRepository.ApplyProfile(profileToRun); ProfileRepository.ApplyProfile(profileToRun);
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
} }
} }
@ -413,6 +443,12 @@ namespace DisplayMagician.UIForms
// Run the shortcut if it's still there // Run the shortcut if it's still there
if (shortcutToRun != null) if (shortcutToRun != null)
ShortcutRepository.RunShortcut(shortcutToRun, notifyIcon); ShortcutRepository.RunShortcut(shortcutToRun, notifyIcon);
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
} }
} }
@ -422,6 +458,9 @@ namespace DisplayMagician.UIForms
Restore(); Restore();
Show(); Show();
BringToFront(); BringToFront();
this.TopMost = true;
this.Activate();
this.TopMost = false;
} }
public void exitApplication() public void exitApplication()
@ -450,7 +489,7 @@ namespace DisplayMagician.UIForms
allowClose = false; allowClose = false;
// Enable the MinimiseOnStart setting // Enable the MinimiseOnStart setting
Program.AppProgramSettings.MinimiseOnStart = true; Program.AppProgramSettings.MinimiseOnStart = true;
Program.AppProgramSettings.StartOnBootUp = true; SettingsForm.SetBootMeUp(true);
// Change the exit_button text to say 'Close' // Change the exit_button text to say 'Close'
btn_exit.Text = "&Close"; btn_exit.Text = "&Close";
} }
@ -462,7 +501,7 @@ namespace DisplayMagician.UIForms
allowClose = true; allowClose = true;
// Disable the MinimiseOnStart setting // Disable the MinimiseOnStart setting
Program.AppProgramSettings.MinimiseOnStart = false; Program.AppProgramSettings.MinimiseOnStart = false;
Program.AppProgramSettings.StartOnBootUp = false; SettingsForm.SetBootMeUp(false);
// Change the exit_button text to say 'Exit' // Change the exit_button text to say 'Exit'
btn_exit.Text = "&Exit"; btn_exit.Text = "&Exit";

View File

@ -50,9 +50,14 @@ namespace DisplayMagician.UIForms
this.gb_upgrades = new System.Windows.Forms.GroupBox(); this.gb_upgrades = new System.Windows.Forms.GroupBox();
this.label2 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label();
this.cb_upgrade_prerelease = new System.Windows.Forms.CheckBox(); this.cb_upgrade_prerelease = new System.Windows.Forms.CheckBox();
this.gb_support = new System.Windows.Forms.GroupBox();
this.btn_create_support_package = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.cb_show_splashscreen = new System.Windows.Forms.CheckBox();
this.gb_general.SuspendLayout(); this.gb_general.SuspendLayout();
this.gb_hotkeys.SuspendLayout(); this.gb_hotkeys.SuspendLayout();
this.gb_upgrades.SuspendLayout(); this.gb_upgrades.SuspendLayout();
this.gb_support.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// btn_back // btn_back
@ -63,7 +68,7 @@ namespace DisplayMagician.UIForms
this.btn_back.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown; this.btn_back.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
this.btn_back.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_back.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_back.ForeColor = System.Drawing.Color.White; this.btn_back.ForeColor = System.Drawing.Color.White;
this.btn_back.Location = new System.Drawing.Point(1034, 434); this.btn_back.Location = new System.Drawing.Point(1034, 517);
this.btn_back.Name = "btn_back"; this.btn_back.Name = "btn_back";
this.btn_back.Size = new System.Drawing.Size(75, 23); this.btn_back.Size = new System.Drawing.Size(75, 23);
this.btn_back.TabIndex = 9; this.btn_back.TabIndex = 9;
@ -73,6 +78,7 @@ namespace DisplayMagician.UIForms
// //
// gb_general // gb_general
// //
this.gb_general.Controls.Add(this.cb_show_splashscreen);
this.gb_general.Controls.Add(this.cb_start_on_boot); this.gb_general.Controls.Add(this.cb_start_on_boot);
this.gb_general.Controls.Add(this.label1); this.gb_general.Controls.Add(this.label1);
this.gb_general.Controls.Add(this.cmb_loglevel); this.gb_general.Controls.Add(this.cmb_loglevel);
@ -81,7 +87,7 @@ namespace DisplayMagician.UIForms
this.gb_general.ForeColor = System.Drawing.Color.White; this.gb_general.ForeColor = System.Drawing.Color.White;
this.gb_general.Location = new System.Drawing.Point(27, 21); this.gb_general.Location = new System.Drawing.Point(27, 21);
this.gb_general.Name = "gb_general"; this.gb_general.Name = "gb_general";
this.gb_general.Size = new System.Drawing.Size(525, 183); this.gb_general.Size = new System.Drawing.Size(525, 239);
this.gb_general.TabIndex = 11; this.gb_general.TabIndex = 11;
this.gb_general.TabStop = false; this.gb_general.TabStop = false;
this.gb_general.Text = "General Settings"; this.gb_general.Text = "General Settings";
@ -105,7 +111,7 @@ namespace DisplayMagician.UIForms
this.label1.AutoSize = true; this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.ForeColor = System.Drawing.Color.Transparent; this.label1.ForeColor = System.Drawing.Color.Transparent;
this.label1.Location = new System.Drawing.Point(26, 120); this.label1.Location = new System.Drawing.Point(26, 157);
this.label1.Name = "label1"; this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(137, 16); this.label1.Size = new System.Drawing.Size(137, 16);
this.label1.TabIndex = 13; this.label1.TabIndex = 13;
@ -115,7 +121,7 @@ namespace DisplayMagician.UIForms
// //
this.cmb_loglevel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.cmb_loglevel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.cmb_loglevel.FormattingEnabled = true; this.cmb_loglevel.FormattingEnabled = true;
this.cmb_loglevel.Location = new System.Drawing.Point(169, 117); this.cmb_loglevel.Location = new System.Drawing.Point(169, 154);
this.cmb_loglevel.Name = "cmb_loglevel"; this.cmb_loglevel.Name = "cmb_loglevel";
this.cmb_loglevel.Size = new System.Drawing.Size(333, 24); this.cmb_loglevel.Size = new System.Drawing.Size(333, 24);
this.cmb_loglevel.TabIndex = 12; this.cmb_loglevel.TabIndex = 12;
@ -292,9 +298,9 @@ namespace DisplayMagician.UIForms
this.gb_upgrades.Controls.Add(this.cb_upgrade_prerelease); this.gb_upgrades.Controls.Add(this.cb_upgrade_prerelease);
this.gb_upgrades.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.gb_upgrades.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.gb_upgrades.ForeColor = System.Drawing.Color.White; this.gb_upgrades.ForeColor = System.Drawing.Color.White;
this.gb_upgrades.Location = new System.Drawing.Point(27, 235); this.gb_upgrades.Location = new System.Drawing.Point(27, 292);
this.gb_upgrades.Name = "gb_upgrades"; this.gb_upgrades.Name = "gb_upgrades";
this.gb_upgrades.Size = new System.Drawing.Size(525, 177); this.gb_upgrades.Size = new System.Drawing.Size(525, 120);
this.gb_upgrades.TabIndex = 13; this.gb_upgrades.TabIndex = 13;
this.gb_upgrades.TabStop = false; this.gb_upgrades.TabStop = false;
this.gb_upgrades.Text = "Upgrade Settings"; this.gb_upgrades.Text = "Upgrade Settings";
@ -324,12 +330,67 @@ namespace DisplayMagician.UIForms
this.cb_upgrade_prerelease.Text = "Upgrade DisplayMagician to latest beta versions when available"; this.cb_upgrade_prerelease.Text = "Upgrade DisplayMagician to latest beta versions when available";
this.cb_upgrade_prerelease.UseVisualStyleBackColor = true; this.cb_upgrade_prerelease.UseVisualStyleBackColor = true;
// //
// gb_support
//
this.gb_support.Controls.Add(this.btn_create_support_package);
this.gb_support.Controls.Add(this.label3);
this.gb_support.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.gb_support.ForeColor = System.Drawing.Color.White;
this.gb_support.Location = new System.Drawing.Point(27, 442);
this.gb_support.Name = "gb_support";
this.gb_support.Size = new System.Drawing.Size(525, 98);
this.gb_support.TabIndex = 16;
this.gb_support.TabStop = false;
this.gb_support.Text = "Support Settings";
//
// btn_create_support_package
//
this.btn_create_support_package.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
this.btn_create_support_package.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
this.btn_create_support_package.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
this.btn_create_support_package.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_create_support_package.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_create_support_package.ForeColor = System.Drawing.Color.White;
this.btn_create_support_package.Location = new System.Drawing.Point(169, 21);
this.btn_create_support_package.Name = "btn_create_support_package";
this.btn_create_support_package.Size = new System.Drawing.Size(183, 33);
this.btn_create_support_package.TabIndex = 48;
this.btn_create_support_package.Text = "Create a Support Zip File";
this.btn_create_support_package.UseVisualStyleBackColor = true;
this.btn_create_support_package.Click += new System.EventHandler(this.btn_create_support_package_Click);
//
// label3
//
this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label3.Location = new System.Drawing.Point(74, 57);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(378, 32);
this.label3.TabIndex = 15;
this.label3.Text = "Use this button to save a support zip file to your computer. You can then upload " +
"this file to GitHub when you have a problem you need me to fix";
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// cb_show_splashscreen
//
this.cb_show_splashscreen.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.cb_show_splashscreen.AutoSize = true;
this.cb_show_splashscreen.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F);
this.cb_show_splashscreen.ForeColor = System.Drawing.Color.White;
this.cb_show_splashscreen.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.cb_show_splashscreen.Location = new System.Drawing.Point(28, 113);
this.cb_show_splashscreen.Name = "cb_show_splashscreen";
this.cb_show_splashscreen.Size = new System.Drawing.Size(312, 20);
this.cb_show_splashscreen.TabIndex = 15;
this.cb_show_splashscreen.Text = "Show DisplayMagician splash screen on startup";
this.cb_show_splashscreen.UseVisualStyleBackColor = true;
//
// SettingsForm // SettingsForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Black; this.BackColor = System.Drawing.Color.Black;
this.ClientSize = new System.Drawing.Size(1142, 481); this.ClientSize = new System.Drawing.Size(1142, 564);
this.Controls.Add(this.gb_support);
this.Controls.Add(this.gb_upgrades); this.Controls.Add(this.gb_upgrades);
this.Controls.Add(this.gb_hotkeys); this.Controls.Add(this.gb_hotkeys);
this.Controls.Add(this.gb_general); this.Controls.Add(this.gb_general);
@ -351,6 +412,7 @@ namespace DisplayMagician.UIForms
this.gb_hotkeys.PerformLayout(); this.gb_hotkeys.PerformLayout();
this.gb_upgrades.ResumeLayout(false); this.gb_upgrades.ResumeLayout(false);
this.gb_upgrades.PerformLayout(); this.gb_upgrades.PerformLayout();
this.gb_support.ResumeLayout(false);
this.ResumeLayout(false); this.ResumeLayout(false);
} }
@ -377,5 +439,9 @@ namespace DisplayMagician.UIForms
private System.Windows.Forms.GroupBox gb_upgrades; private System.Windows.Forms.GroupBox gb_upgrades;
private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label2;
private System.Windows.Forms.CheckBox cb_upgrade_prerelease; private System.Windows.Forms.CheckBox cb_upgrade_prerelease;
private System.Windows.Forms.GroupBox gb_support;
private System.Windows.Forms.Button btn_create_support_package;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.CheckBox cb_show_splashscreen;
} }
} }

View File

@ -3,6 +3,8 @@ using NHotkey;
using NHotkey.WindowsForms; using NHotkey.WindowsForms;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Forms;
using WK.Libraries.BootMeUpNS; using WK.Libraries.BootMeUpNS;
@ -62,6 +64,18 @@ namespace DisplayMagician.UIForms
logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings MinimiseOnStart set to false"); logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings MinimiseOnStart set to false");
} }
// show splashscreen on startup
if (Program.AppProgramSettings.ShowSplashScreen == true)
{
cb_show_splashscreen.Checked = true;
logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings ShowSplashScreen set to true");
}
else
{
cb_show_splashscreen.Checked = false;
logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings ShowSplashScreen set to false");
}
// start upgrade settings // start upgrade settings
if (Program.AppProgramSettings.UpgradeToPreReleases == true) if (Program.AppProgramSettings.UpgradeToPreReleases == true)
{ {
@ -102,8 +116,8 @@ namespace DisplayMagician.UIForms
logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings LogLevel set to Fatal"); logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings LogLevel set to Fatal");
break; break;
default: default:
cmb_loglevel.SelectedIndex = cmb_loglevel.FindStringExact(logLevelText["Info"]); cmb_loglevel.SelectedIndex = cmb_loglevel.FindStringExact(logLevelText["Trace"]);
logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings LogLevel set to Info"); logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings LogLevel set to Trace");
break; break;
} }
@ -158,10 +172,8 @@ namespace DisplayMagician.UIForms
} }
private void SettingsForm_FormClosing(object sender, FormClosingEventArgs e) public static bool SetBootMeUp(bool enabled)
{ {
logger.Info($"SettingsForm/SettingsForm_Load: AppProgramSettings LogLevel set to Trace");
var bootMeUp = new BootMeUp var bootMeUp = new BootMeUp
{ {
UseAlternativeOnFail = true, UseAlternativeOnFail = true,
@ -170,7 +182,7 @@ namespace DisplayMagician.UIForms
}; };
// save start on Boot up // save start on Boot up
if (cb_start_on_boot.Checked) if (enabled)
{ {
Program.AppProgramSettings.StartOnBootUp = true; Program.AppProgramSettings.StartOnBootUp = true;
bootMeUp.Enabled = true; bootMeUp.Enabled = true;
@ -178,11 +190,15 @@ namespace DisplayMagician.UIForms
{ {
logger.Error($"SettingsForm/SettingsForm_FormClosing: Failed to set up DisplayMagician to start when Windows starts"); logger.Error($"SettingsForm/SettingsForm_FormClosing: Failed to set up DisplayMagician to start when Windows starts");
MessageBox.Show("There was an issue setting DisplayMagician to run when the computer starts. Please try launching DisplayMagician again as Admin to see if that helps."); MessageBox.Show("There was an issue setting DisplayMagician to run when the computer starts. Please try launching DisplayMagician again as Admin to see if that helps.");
return false;
} }
else else
{
logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully set DisplayMagician to start when Windows starts"); logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully set DisplayMagician to start when Windows starts");
return true;
} }
}
else else
{ {
Program.AppProgramSettings.StartOnBootUp = false; Program.AppProgramSettings.StartOnBootUp = false;
@ -191,11 +207,23 @@ namespace DisplayMagician.UIForms
{ {
logger.Error($"SettingsForm/SettingsForm_FormClosing: Failed to stop DisplayMagician from starting when Windows starts"); logger.Error($"SettingsForm/SettingsForm_FormClosing: Failed to stop DisplayMagician from starting when Windows starts");
MessageBox.Show("There was an issue stopping DisplayMagician from running when the computer starts. Please try launching DisplayMagician again as Admin to see if that helps."); MessageBox.Show("There was an issue stopping DisplayMagician from running when the computer starts. Please try launching DisplayMagician again as Admin to see if that helps.");
return false;
} }
else else
{
logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully stopped DisplayMagician from starting when Windows starts"); logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully stopped DisplayMagician from starting when Windows starts");
return true;
} }
}
}
private void SettingsForm_FormClosing(object sender, FormClosingEventArgs e)
{
logger.Info($"SettingsForm/SettingsForm_Load: Setting BootMeUp to {cb_start_on_boot.Checked}");
SetBootMeUp(cb_start_on_boot.Checked);
// save minimise on close // save minimise on close
if (cb_minimise_notification_area.Checked) if (cb_minimise_notification_area.Checked)
Program.AppProgramSettings.MinimiseOnStart = true; Program.AppProgramSettings.MinimiseOnStart = true;
@ -203,6 +231,13 @@ namespace DisplayMagician.UIForms
Program.AppProgramSettings.MinimiseOnStart = false; Program.AppProgramSettings.MinimiseOnStart = false;
logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully saved MinimiseOnStart as {Program.AppProgramSettings.MinimiseOnStart}"); logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully saved MinimiseOnStart as {Program.AppProgramSettings.MinimiseOnStart}");
// save show splashscreen on startup
if (cb_show_splashscreen.Checked)
Program.AppProgramSettings.ShowSplashScreen = true;
else
Program.AppProgramSettings.ShowSplashScreen = false;
logger.Info($"SettingsForm/SettingsForm_FormClosing: Successfully saved ShowSplashScreen as {Program.AppProgramSettings.ShowSplashScreen}");
// save loglevel on close // save loglevel on close
// and make that log level live in NLog straight away // and make that log level live in NLog straight away
var config = NLog.LogManager.Configuration; var config = NLog.LogManager.Configuration;
@ -471,5 +506,95 @@ namespace DisplayMagician.UIForms
} }
} }
private void btn_create_support_package_Click(object sender, EventArgs e)
{
try
{
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
DateTime now = DateTime.Now;
saveFileDialog.InitialDirectory = Environment.SpecialFolder.MyDocuments.ToString();
saveFileDialog.Filter = "Zip Files(*.zip)| *.zip | All files(*.*) | *.*";
saveFileDialog.FilterIndex = 2;
saveFileDialog.RestoreDirectory = true;
saveFileDialog.FileName = $"DisplayMagician-Support-{now.ToString("yyyyMMdd-HHmm")}.zip";
saveFileDialog.Title = "Save a DisplayMagician Support ZIP file";
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
//Get the path of specified file
string zipFilePath = saveFileDialog.FileName;
SharedLogger.logger.Trace($"SettingsForm/btn_create_support_package_Click: Creating support zip file at {zipFilePath}.");
if (File.Exists(zipFilePath))
{
File.Delete(zipFilePath);
}
ZipArchive archive = ZipFile.Open(zipFilePath, ZipArchiveMode.Create);
// Get the list of files to zip
List<string> listOfFiles = new List<string> {
// Add the DisplayMagician.log file
Path.Combine(Program.AppLogPath,"DisplayMagician.log"),
// Add the DisplayMagician.log file
Path.Combine(Program.AppProfilePath,"DisplayProfiles_2.1.json"),
// Add the DisplayMagician.log file
Path.Combine(Program.AppShortcutPath,"Shortcuts_2.0.json"),
// Add the DisplayMagician.log file
Path.Combine(Program.AppDataPath,"Settings_2.0.json")
};
foreach (string filename in listOfFiles)
{
try
{
if (File.Exists(filename))
{
archive.CreateEntryFromFile(filename, Path.GetFileName(filename), CompressionLevel.Optimal);
}
else
{
SharedLogger.logger.Warn($"SettingsForm/btn_create_support_package_Click: Couldn't add {filename} to the support ZIP file {zipFilePath} as it doesn't exist.");
}
}
catch (ArgumentNullException ex)
{
SharedLogger.logger.Warn(ex, $"SettingsForm/btn_create_support_package_Click: Argument Null Exception while adding files to the support zip file.");
}
catch (System.Runtime.InteropServices.ExternalException ex)
{
SharedLogger.logger.Warn(ex, $"SettingsForm/btn_create_support_package_Click: External InteropServices Exception while adding files to the support zip file.");
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"SettingsForm/btn_create_support_package_Click: Exception while while adding files to the support zip file.");
}
}
archive.Dispose();
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.");
}
}
}
catch (ArgumentNullException ex)
{
SharedLogger.logger.Warn(ex, $"SettingsForm/btn_create_support_package_Click: Argument Null Exception while creating support zip file.");
}
catch (System.Runtime.InteropServices.ExternalException ex)
{
SharedLogger.logger.Warn(ex, $"SettingsForm/btn_create_support_package_Click: External InteropServices Exception while creating support zip file.");
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"SettingsForm/btn_create_support_package_Click: Exception while while creating support zip file.");
}
}
} }
} }

View File

@ -74,8 +74,9 @@ namespace DisplayMagician.UIForms
this.flp_start_programs = new System.Windows.Forms.FlowLayoutPanel(); this.flp_start_programs = new System.Windows.Forms.FlowLayoutPanel();
this.tabp_game = new System.Windows.Forms.TabPage(); this.tabp_game = new System.Windows.Forms.TabPage();
this.btn_find_examples_game = new System.Windows.Forms.Button(); this.btn_find_examples_game = new System.Windows.Forms.Button();
this.lbl_no_game_libraries = new System.Windows.Forms.Label();
this.p_standalone = new System.Windows.Forms.Panel(); this.p_standalone = new System.Windows.Forms.Panel();
this.btn_choose_exe_icon = new System.Windows.Forms.Button();
this.pb_exe_icon = new System.Windows.Forms.PictureBox();
this.cbx_exe_priority = new System.Windows.Forms.ComboBox(); this.cbx_exe_priority = new System.Windows.Forms.ComboBox();
this.lbl_exe_priority = new System.Windows.Forms.Label(); this.lbl_exe_priority = new System.Windows.Forms.Label();
this.btn_exe_to_start = new System.Windows.Forms.Button(); this.btn_exe_to_start = new System.Windows.Forms.Button();
@ -92,7 +93,10 @@ namespace DisplayMagician.UIForms
this.rb_standalone = new System.Windows.Forms.RadioButton(); this.rb_standalone = new System.Windows.Forms.RadioButton();
this.rb_no_game = new System.Windows.Forms.RadioButton(); this.rb_no_game = new System.Windows.Forms.RadioButton();
this.p_game = new System.Windows.Forms.Panel(); this.p_game = new System.Windows.Forms.Panel();
this.lbl_game_library = new System.Windows.Forms.Label(); this.btn_refresh_games_list = new System.Windows.Forms.Button();
this.btn_choose_game_icon = new System.Windows.Forms.Button();
this.pb_game_icon = new System.Windows.Forms.PictureBox();
this.lbl_no_game_libraries = new System.Windows.Forms.Label();
this.cbx_game_priority = new System.Windows.Forms.ComboBox(); this.cbx_game_priority = new System.Windows.Forms.ComboBox();
this.ilv_games = new Manina.Windows.Forms.ImageListView(); this.ilv_games = new Manina.Windows.Forms.ImageListView();
this.cb_wait_alternative_game = new System.Windows.Forms.CheckBox(); this.cb_wait_alternative_game = new System.Windows.Forms.CheckBox();
@ -105,8 +109,15 @@ namespace DisplayMagician.UIForms
this.cb_args_game = new System.Windows.Forms.CheckBox(); this.cb_args_game = new System.Windows.Forms.CheckBox();
this.lbl_game_timeout = new System.Windows.Forms.Label(); this.lbl_game_timeout = new System.Windows.Forms.Label();
this.nud_timeout_game = new System.Windows.Forms.NumericUpDown(); this.nud_timeout_game = new System.Windows.Forms.NumericUpDown();
this.lbl_game_library = new System.Windows.Forms.Label();
this.rb_launcher = new System.Windows.Forms.RadioButton(); this.rb_launcher = new System.Windows.Forms.RadioButton();
this.tabp_after = new System.Windows.Forms.TabPage(); this.tabp_after = new System.Windows.Forms.TabPage();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.txt_run_cmd_afterwards_args = new System.Windows.Forms.TextBox();
this.cb_run_cmd_afterwards_args = new System.Windows.Forms.CheckBox();
this.btn_run_cmd_afterwards = new System.Windows.Forms.Button();
this.txt_run_cmd_afterwards = new System.Windows.Forms.TextBox();
this.cb_run_cmd_afterwards = new System.Windows.Forms.CheckBox();
this.groupBox2 = new System.Windows.Forms.GroupBox(); this.groupBox2 = new System.Windows.Forms.GroupBox();
this.rb_switch_capture_permanent = new System.Windows.Forms.RadioButton(); this.rb_switch_capture_permanent = new System.Windows.Forms.RadioButton();
this.rb_switch_capture_temp = new System.Windows.Forms.RadioButton(); this.rb_switch_capture_temp = new System.Windows.Forms.RadioButton();
@ -135,10 +146,13 @@ namespace DisplayMagician.UIForms
this.tabp_before.SuspendLayout(); this.tabp_before.SuspendLayout();
this.tabp_game.SuspendLayout(); this.tabp_game.SuspendLayout();
this.p_standalone.SuspendLayout(); this.p_standalone.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pb_exe_icon)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.nud_timeout_executable)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.nud_timeout_executable)).BeginInit();
this.p_game.SuspendLayout(); this.p_game.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pb_game_icon)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.nud_timeout_game)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.nud_timeout_game)).BeginInit();
this.tabp_after.SuspendLayout(); this.tabp_after.SuspendLayout();
this.groupBox3.SuspendLayout();
this.groupBox2.SuspendLayout(); this.groupBox2.SuspendLayout();
this.groupBox1.SuspendLayout(); this.groupBox1.SuspendLayout();
this.gb_display_after.SuspendLayout(); this.gb_display_after.SuspendLayout();
@ -154,7 +168,7 @@ namespace DisplayMagician.UIForms
this.btn_save.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_save.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_save.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_save.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_save.ForeColor = System.Drawing.Color.White; this.btn_save.ForeColor = System.Drawing.Color.White;
this.btn_save.Location = new System.Drawing.Point(560, 806); this.btn_save.Location = new System.Drawing.Point(560, 891);
this.btn_save.Name = "btn_save"; this.btn_save.Name = "btn_save";
this.btn_save.Size = new System.Drawing.Size(120, 40); this.btn_save.Size = new System.Drawing.Size(120, 40);
this.btn_save.TabIndex = 6; this.btn_save.TabIndex = 6;
@ -170,7 +184,7 @@ namespace DisplayMagician.UIForms
this.btn_cancel.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown; this.btn_cancel.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
this.btn_cancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_cancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_cancel.ForeColor = System.Drawing.Color.White; this.btn_cancel.ForeColor = System.Drawing.Color.White;
this.btn_cancel.Location = new System.Drawing.Point(1008, 821); this.btn_cancel.Location = new System.Drawing.Point(1008, 906);
this.btn_cancel.Name = "btn_cancel"; this.btn_cancel.Name = "btn_cancel";
this.btn_cancel.Size = new System.Drawing.Size(94, 25); this.btn_cancel.Size = new System.Drawing.Size(94, 25);
this.btn_cancel.TabIndex = 5; this.btn_cancel.TabIndex = 5;
@ -209,9 +223,8 @@ namespace DisplayMagician.UIForms
this.tabc_shortcut.Name = "tabc_shortcut"; this.tabc_shortcut.Name = "tabc_shortcut";
this.tabc_shortcut.SelectedIndex = 0; this.tabc_shortcut.SelectedIndex = 0;
this.tabc_shortcut.ShowToolTips = true; this.tabc_shortcut.ShowToolTips = true;
this.tabc_shortcut.Size = new System.Drawing.Size(1090, 682); this.tabc_shortcut.Size = new System.Drawing.Size(1090, 767);
this.tabc_shortcut.TabIndex = 28; this.tabc_shortcut.TabIndex = 28;
this.tabc_shortcut.Click += new System.EventHandler(this.tabc_shortcut_VisibleChanged);
// //
// tabp_display // tabp_display
// //
@ -226,7 +239,7 @@ namespace DisplayMagician.UIForms
this.tabp_display.Location = new System.Drawing.Point(4, 32); this.tabp_display.Location = new System.Drawing.Point(4, 32);
this.tabp_display.Name = "tabp_display"; this.tabp_display.Name = "tabp_display";
this.tabp_display.Padding = new System.Windows.Forms.Padding(3); this.tabp_display.Padding = new System.Windows.Forms.Padding(3);
this.tabp_display.Size = new System.Drawing.Size(1082, 646); this.tabp_display.Size = new System.Drawing.Size(1082, 731);
this.tabp_display.TabIndex = 0; this.tabp_display.TabIndex = 0;
this.tabp_display.Text = "1. Choose Display Profile"; this.tabp_display.Text = "1. Choose Display Profile";
this.tabp_display.ToolTipText = "Choose which previously saved Display Profile you will use with this shortcut."; this.tabp_display.ToolTipText = "Choose which previously saved Display Profile you will use with this shortcut.";
@ -275,12 +288,12 @@ namespace DisplayMagician.UIForms
this.ilv_saved_profiles.AllowPaneResize = false; this.ilv_saved_profiles.AllowPaneResize = false;
this.ilv_saved_profiles.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) this.ilv_saved_profiles.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.ilv_saved_profiles.Location = new System.Drawing.Point(0, 466); this.ilv_saved_profiles.Location = new System.Drawing.Point(0, 477);
this.ilv_saved_profiles.MultiSelect = false; this.ilv_saved_profiles.MultiSelect = false;
this.ilv_saved_profiles.Name = "ilv_saved_profiles"; this.ilv_saved_profiles.Name = "ilv_saved_profiles";
this.ilv_saved_profiles.PersistentCacheDirectory = ""; this.ilv_saved_profiles.PersistentCacheDirectory = "";
this.ilv_saved_profiles.PersistentCacheSize = ((long)(100)); this.ilv_saved_profiles.PersistentCacheSize = ((long)(100));
this.ilv_saved_profiles.Size = new System.Drawing.Size(1086, 184); this.ilv_saved_profiles.Size = new System.Drawing.Size(1086, 258);
this.ilv_saved_profiles.TabIndex = 24; this.ilv_saved_profiles.TabIndex = 24;
this.ilv_saved_profiles.UseWIC = true; this.ilv_saved_profiles.UseWIC = true;
this.ilv_saved_profiles.View = Manina.Windows.Forms.View.HorizontalStrip; this.ilv_saved_profiles.View = Manina.Windows.Forms.View.HorizontalStrip;
@ -288,19 +301,18 @@ namespace DisplayMagician.UIForms
// //
// dv_profile // dv_profile
// //
this.dv_profile.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.dv_profile.BackColor = System.Drawing.Color.DimGray; this.dv_profile.BackColor = System.Drawing.Color.DimGray;
this.dv_profile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.dv_profile.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.dv_profile.Dock = System.Windows.Forms.DockStyle.Top;
this.dv_profile.Font = new System.Drawing.Font("Consolas", 50F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.dv_profile.Font = new System.Drawing.Font("Consolas", 50F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.dv_profile.ForeColor = System.Drawing.Color.MidnightBlue; this.dv_profile.ForeColor = System.Drawing.Color.MidnightBlue;
this.dv_profile.Location = new System.Drawing.Point(0, 0); this.dv_profile.Location = new System.Drawing.Point(3, 3);
this.dv_profile.Margin = new System.Windows.Forms.Padding(18); this.dv_profile.Margin = new System.Windows.Forms.Padding(18);
this.dv_profile.Name = "dv_profile"; this.dv_profile.Name = "dv_profile";
this.dv_profile.PaddingX = 100; this.dv_profile.PaddingX = 100;
this.dv_profile.PaddingY = 100; this.dv_profile.PaddingY = 100;
this.dv_profile.Profile = null; this.dv_profile.Profile = null;
this.dv_profile.Size = new System.Drawing.Size(1082, 467); this.dv_profile.Size = new System.Drawing.Size(1076, 472);
this.dv_profile.TabIndex = 23; this.dv_profile.TabIndex = 23;
// //
// tabp_audio // tabp_audio
@ -314,7 +326,7 @@ namespace DisplayMagician.UIForms
this.tabp_audio.Location = new System.Drawing.Point(4, 32); this.tabp_audio.Location = new System.Drawing.Point(4, 32);
this.tabp_audio.Name = "tabp_audio"; this.tabp_audio.Name = "tabp_audio";
this.tabp_audio.Padding = new System.Windows.Forms.Padding(3); this.tabp_audio.Padding = new System.Windows.Forms.Padding(3);
this.tabp_audio.Size = new System.Drawing.Size(1082, 646); this.tabp_audio.Size = new System.Drawing.Size(1082, 731);
this.tabp_audio.TabIndex = 4; this.tabp_audio.TabIndex = 4;
this.tabp_audio.Text = "2. Choose Audio"; this.tabp_audio.Text = "2. Choose Audio";
// //
@ -327,7 +339,7 @@ namespace DisplayMagician.UIForms
this.lbl_no_active_capture_devices.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F); this.lbl_no_active_capture_devices.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F);
this.lbl_no_active_capture_devices.ForeColor = System.Drawing.Color.White; this.lbl_no_active_capture_devices.ForeColor = System.Drawing.Color.White;
this.lbl_no_active_capture_devices.ImeMode = System.Windows.Forms.ImeMode.NoControl; this.lbl_no_active_capture_devices.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.lbl_no_active_capture_devices.Location = new System.Drawing.Point(126, 433); this.lbl_no_active_capture_devices.Location = new System.Drawing.Point(126, 438);
this.lbl_no_active_capture_devices.Name = "lbl_no_active_capture_devices"; this.lbl_no_active_capture_devices.Name = "lbl_no_active_capture_devices";
this.lbl_no_active_capture_devices.Size = new System.Drawing.Size(831, 22); this.lbl_no_active_capture_devices.Size = new System.Drawing.Size(831, 22);
this.lbl_no_active_capture_devices.TabIndex = 36; this.lbl_no_active_capture_devices.TabIndex = 36;
@ -344,7 +356,7 @@ namespace DisplayMagician.UIForms
this.lbl_no_active_audio_devices.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F); this.lbl_no_active_audio_devices.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F);
this.lbl_no_active_audio_devices.ForeColor = System.Drawing.Color.White; this.lbl_no_active_audio_devices.ForeColor = System.Drawing.Color.White;
this.lbl_no_active_audio_devices.ImeMode = System.Windows.Forms.ImeMode.NoControl; this.lbl_no_active_audio_devices.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.lbl_no_active_audio_devices.Location = new System.Drawing.Point(131, 151); this.lbl_no_active_audio_devices.Location = new System.Drawing.Point(131, 153);
this.lbl_no_active_audio_devices.Name = "lbl_no_active_audio_devices"; this.lbl_no_active_audio_devices.Name = "lbl_no_active_audio_devices";
this.lbl_no_active_audio_devices.Size = new System.Drawing.Size(804, 22); this.lbl_no_active_audio_devices.Size = new System.Drawing.Size(804, 22);
this.lbl_no_active_audio_devices.TabIndex = 35; this.lbl_no_active_audio_devices.TabIndex = 35;
@ -361,7 +373,7 @@ namespace DisplayMagician.UIForms
this.lbl_disabled_shortcut_audio_chipset.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F); this.lbl_disabled_shortcut_audio_chipset.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F);
this.lbl_disabled_shortcut_audio_chipset.ForeColor = System.Drawing.Color.White; this.lbl_disabled_shortcut_audio_chipset.ForeColor = System.Drawing.Color.White;
this.lbl_disabled_shortcut_audio_chipset.ImeMode = System.Windows.Forms.ImeMode.NoControl; this.lbl_disabled_shortcut_audio_chipset.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.lbl_disabled_shortcut_audio_chipset.Location = new System.Drawing.Point(263, 298); this.lbl_disabled_shortcut_audio_chipset.Location = new System.Drawing.Point(263, 303);
this.lbl_disabled_shortcut_audio_chipset.Name = "lbl_disabled_shortcut_audio_chipset"; this.lbl_disabled_shortcut_audio_chipset.Name = "lbl_disabled_shortcut_audio_chipset";
this.lbl_disabled_shortcut_audio_chipset.Size = new System.Drawing.Size(557, 22); this.lbl_disabled_shortcut_audio_chipset.Size = new System.Drawing.Size(557, 22);
this.lbl_disabled_shortcut_audio_chipset.TabIndex = 34; this.lbl_disabled_shortcut_audio_chipset.TabIndex = 34;
@ -642,7 +654,7 @@ namespace DisplayMagician.UIForms
this.tabp_before.Location = new System.Drawing.Point(4, 32); this.tabp_before.Location = new System.Drawing.Point(4, 32);
this.tabp_before.Name = "tabp_before"; this.tabp_before.Name = "tabp_before";
this.tabp_before.Padding = new System.Windows.Forms.Padding(3); this.tabp_before.Padding = new System.Windows.Forms.Padding(3);
this.tabp_before.Size = new System.Drawing.Size(1082, 646); this.tabp_before.Size = new System.Drawing.Size(1082, 731);
this.tabp_before.TabIndex = 1; this.tabp_before.TabIndex = 1;
this.tabp_before.Text = "3. Choose what happens before"; this.tabp_before.Text = "3. Choose what happens before";
// //
@ -654,7 +666,7 @@ namespace DisplayMagician.UIForms
this.btn_find_examples_startprograms.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_find_examples_startprograms.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_find_examples_startprograms.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_find_examples_startprograms.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_find_examples_startprograms.ForeColor = System.Drawing.Color.White; this.btn_find_examples_startprograms.ForeColor = System.Drawing.Color.White;
this.btn_find_examples_startprograms.Location = new System.Drawing.Point(953, 72); this.btn_find_examples_startprograms.Location = new System.Drawing.Point(953, 76);
this.btn_find_examples_startprograms.Name = "btn_find_examples_startprograms"; this.btn_find_examples_startprograms.Name = "btn_find_examples_startprograms";
this.btn_find_examples_startprograms.Size = new System.Drawing.Size(117, 25); this.btn_find_examples_startprograms.Size = new System.Drawing.Size(117, 25);
this.btn_find_examples_startprograms.TabIndex = 40; this.btn_find_examples_startprograms.TabIndex = 40;
@ -681,7 +693,7 @@ namespace DisplayMagician.UIForms
this.btn_add_new_start_program.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_add_new_start_program.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_add_new_start_program.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_add_new_start_program.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_add_new_start_program.ForeColor = System.Drawing.Color.White; this.btn_add_new_start_program.ForeColor = System.Drawing.Color.White;
this.btn_add_new_start_program.Location = new System.Drawing.Point(424, 61); this.btn_add_new_start_program.Location = new System.Drawing.Point(424, 65);
this.btn_add_new_start_program.Name = "btn_add_new_start_program"; this.btn_add_new_start_program.Name = "btn_add_new_start_program";
this.btn_add_new_start_program.Size = new System.Drawing.Size(235, 40); this.btn_add_new_start_program.Size = new System.Drawing.Size(235, 40);
this.btn_add_new_start_program.TabIndex = 38; this.btn_add_new_start_program.TabIndex = 38;
@ -697,16 +709,15 @@ namespace DisplayMagician.UIForms
this.flp_start_programs.AutoScrollMinSize = new System.Drawing.Size(5, 0); this.flp_start_programs.AutoScrollMinSize = new System.Drawing.Size(5, 0);
this.flp_start_programs.BackColor = System.Drawing.Color.White; this.flp_start_programs.BackColor = System.Drawing.Color.White;
this.flp_start_programs.Dock = System.Windows.Forms.DockStyle.Bottom; this.flp_start_programs.Dock = System.Windows.Forms.DockStyle.Bottom;
this.flp_start_programs.Location = new System.Drawing.Point(3, 135); this.flp_start_programs.Location = new System.Drawing.Point(3, 130);
this.flp_start_programs.Name = "flp_start_programs"; this.flp_start_programs.Name = "flp_start_programs";
this.flp_start_programs.Size = new System.Drawing.Size(1076, 508); this.flp_start_programs.Size = new System.Drawing.Size(1076, 598);
this.flp_start_programs.TabIndex = 0; this.flp_start_programs.TabIndex = 0;
// //
// tabp_game // tabp_game
// //
this.tabp_game.BackColor = System.Drawing.Color.Black; this.tabp_game.BackColor = System.Drawing.Color.Black;
this.tabp_game.Controls.Add(this.btn_find_examples_game); this.tabp_game.Controls.Add(this.btn_find_examples_game);
this.tabp_game.Controls.Add(this.lbl_no_game_libraries);
this.tabp_game.Controls.Add(this.p_standalone); this.tabp_game.Controls.Add(this.p_standalone);
this.tabp_game.Controls.Add(this.rb_standalone); this.tabp_game.Controls.Add(this.rb_standalone);
this.tabp_game.Controls.Add(this.rb_no_game); this.tabp_game.Controls.Add(this.rb_no_game);
@ -717,19 +728,19 @@ namespace DisplayMagician.UIForms
this.tabp_game.Location = new System.Drawing.Point(4, 32); this.tabp_game.Location = new System.Drawing.Point(4, 32);
this.tabp_game.Name = "tabp_game"; this.tabp_game.Name = "tabp_game";
this.tabp_game.Padding = new System.Windows.Forms.Padding(3); this.tabp_game.Padding = new System.Windows.Forms.Padding(3);
this.tabp_game.Size = new System.Drawing.Size(1082, 646); this.tabp_game.Size = new System.Drawing.Size(1082, 731);
this.tabp_game.TabIndex = 2; this.tabp_game.TabIndex = 2;
this.tabp_game.Text = "4. Choose Game to start"; this.tabp_game.Text = "4. Choose Game to start";
// //
// btn_find_examples_game // btn_find_examples_game
// //
this.btn_find_examples_game.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btn_find_examples_game.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btn_find_examples_game.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed; this.btn_find_examples_game.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
this.btn_find_examples_game.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown; this.btn_find_examples_game.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
this.btn_find_examples_game.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_find_examples_game.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_find_examples_game.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_find_examples_game.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_find_examples_game.ForeColor = System.Drawing.Color.White; this.btn_find_examples_game.ForeColor = System.Drawing.Color.White;
this.btn_find_examples_game.Location = new System.Drawing.Point(953, 45); this.btn_find_examples_game.Location = new System.Drawing.Point(924, 19);
this.btn_find_examples_game.Name = "btn_find_examples_game"; this.btn_find_examples_game.Name = "btn_find_examples_game";
this.btn_find_examples_game.Size = new System.Drawing.Size(117, 25); this.btn_find_examples_game.Size = new System.Drawing.Size(117, 25);
this.btn_find_examples_game.TabIndex = 41; this.btn_find_examples_game.TabIndex = 41;
@ -737,26 +748,10 @@ namespace DisplayMagician.UIForms
this.btn_find_examples_game.UseVisualStyleBackColor = true; this.btn_find_examples_game.UseVisualStyleBackColor = true;
this.btn_find_examples_game.Click += new System.EventHandler(this.btn_find_examples_game_Click); this.btn_find_examples_game.Click += new System.EventHandler(this.btn_find_examples_game_Click);
// //
// lbl_no_game_libraries
//
this.lbl_no_game_libraries.Anchor = System.Windows.Forms.AnchorStyles.None;
this.lbl_no_game_libraries.AutoSize = true;
this.lbl_no_game_libraries.BackColor = System.Drawing.Color.Brown;
this.lbl_no_game_libraries.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.lbl_no_game_libraries.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F);
this.lbl_no_game_libraries.ForeColor = System.Drawing.Color.White;
this.lbl_no_game_libraries.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.lbl_no_game_libraries.Location = new System.Drawing.Point(255, 281);
this.lbl_no_game_libraries.Name = "lbl_no_game_libraries";
this.lbl_no_game_libraries.Size = new System.Drawing.Size(613, 22);
this.lbl_no_game_libraries.TabIndex = 34;
this.lbl_no_game_libraries.Text = "No supported game libraries detected! (Steam, Origin, Uplay, Epic or GOG supporte" +
"d)";
this.lbl_no_game_libraries.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_no_game_libraries.Visible = false;
//
// p_standalone // p_standalone
// //
this.p_standalone.Controls.Add(this.btn_choose_exe_icon);
this.p_standalone.Controls.Add(this.pb_exe_icon);
this.p_standalone.Controls.Add(this.cbx_exe_priority); this.p_standalone.Controls.Add(this.cbx_exe_priority);
this.p_standalone.Controls.Add(this.lbl_exe_priority); this.p_standalone.Controls.Add(this.lbl_exe_priority);
this.p_standalone.Controls.Add(this.btn_exe_to_start); this.p_standalone.Controls.Add(this.btn_exe_to_start);
@ -771,17 +766,42 @@ namespace DisplayMagician.UIForms
this.p_standalone.Controls.Add(this.label2); this.p_standalone.Controls.Add(this.label2);
this.p_standalone.Controls.Add(this.nud_timeout_executable); this.p_standalone.Controls.Add(this.nud_timeout_executable);
this.p_standalone.Enabled = false; this.p_standalone.Enabled = false;
this.p_standalone.Location = new System.Drawing.Point(35, 86); this.p_standalone.Location = new System.Drawing.Point(3, 86);
this.p_standalone.Name = "p_standalone"; this.p_standalone.Name = "p_standalone";
this.p_standalone.Size = new System.Drawing.Size(1006, 160); this.p_standalone.Size = new System.Drawing.Size(1076, 201);
this.p_standalone.TabIndex = 10; this.p_standalone.TabIndex = 10;
// //
// btn_choose_exe_icon
//
this.btn_choose_exe_icon.Enabled = false;
this.btn_choose_exe_icon.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_choose_exe_icon.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_choose_exe_icon.ForeColor = System.Drawing.Color.White;
this.btn_choose_exe_icon.Location = new System.Drawing.Point(36, 158);
this.btn_choose_exe_icon.Name = "btn_choose_exe_icon";
this.btn_choose_exe_icon.Size = new System.Drawing.Size(100, 26);
this.btn_choose_exe_icon.TabIndex = 38;
this.btn_choose_exe_icon.Text = "Swap";
this.btn_choose_exe_icon.UseVisualStyleBackColor = true;
this.btn_choose_exe_icon.Click += new System.EventHandler(this.btn_choose_exe_icon_Click);
//
// pb_exe_icon
//
this.pb_exe_icon.BackColor = System.Drawing.Color.DarkGray;
this.pb_exe_icon.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.pb_exe_icon.Location = new System.Drawing.Point(36, 59);
this.pb_exe_icon.Name = "pb_exe_icon";
this.pb_exe_icon.Size = new System.Drawing.Size(100, 100);
this.pb_exe_icon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pb_exe_icon.TabIndex = 37;
this.pb_exe_icon.TabStop = false;
//
// cbx_exe_priority // cbx_exe_priority
// //
this.cbx_exe_priority.AllowDrop = true; this.cbx_exe_priority.AllowDrop = true;
this.cbx_exe_priority.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cbx_exe_priority.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbx_exe_priority.FormattingEnabled = true; this.cbx_exe_priority.FormattingEnabled = true;
this.cbx_exe_priority.Location = new System.Drawing.Point(815, 83); this.cbx_exe_priority.Location = new System.Drawing.Point(917, 82);
this.cbx_exe_priority.Name = "cbx_exe_priority"; this.cbx_exe_priority.Name = "cbx_exe_priority";
this.cbx_exe_priority.Size = new System.Drawing.Size(150, 28); this.cbx_exe_priority.Size = new System.Drawing.Size(150, 28);
this.cbx_exe_priority.TabIndex = 31; this.cbx_exe_priority.TabIndex = 31;
@ -790,7 +810,7 @@ namespace DisplayMagician.UIForms
// //
this.lbl_exe_priority.AutoSize = true; this.lbl_exe_priority.AutoSize = true;
this.lbl_exe_priority.ForeColor = System.Drawing.Color.White; this.lbl_exe_priority.ForeColor = System.Drawing.Color.White;
this.lbl_exe_priority.Location = new System.Drawing.Point(668, 86); this.lbl_exe_priority.Location = new System.Drawing.Point(770, 85);
this.lbl_exe_priority.Name = "lbl_exe_priority"; this.lbl_exe_priority.Name = "lbl_exe_priority";
this.lbl_exe_priority.Size = new System.Drawing.Size(143, 20); this.lbl_exe_priority.Size = new System.Drawing.Size(143, 20);
this.lbl_exe_priority.TabIndex = 30; this.lbl_exe_priority.TabIndex = 30;
@ -813,7 +833,7 @@ namespace DisplayMagician.UIForms
this.txt_args_executable.Enabled = false; this.txt_args_executable.Enabled = false;
this.txt_args_executable.Location = new System.Drawing.Point(425, 46); this.txt_args_executable.Location = new System.Drawing.Point(425, 46);
this.txt_args_executable.Name = "txt_args_executable"; this.txt_args_executable.Name = "txt_args_executable";
this.txt_args_executable.Size = new System.Drawing.Size(540, 26); this.txt_args_executable.Size = new System.Drawing.Size(642, 26);
this.txt_args_executable.TabIndex = 11; this.txt_args_executable.TabIndex = 11;
// //
// cb_args_executable // cb_args_executable
@ -834,7 +854,7 @@ namespace DisplayMagician.UIForms
// //
this.btn_choose_alternative_executable.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_choose_alternative_executable.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_choose_alternative_executable.ForeColor = System.Drawing.Color.White; this.btn_choose_alternative_executable.ForeColor = System.Drawing.Color.White;
this.btn_choose_alternative_executable.Location = new System.Drawing.Point(880, 121); this.btn_choose_alternative_executable.Location = new System.Drawing.Point(981, 155);
this.btn_choose_alternative_executable.Name = "btn_choose_alternative_executable"; this.btn_choose_alternative_executable.Name = "btn_choose_alternative_executable";
this.btn_choose_alternative_executable.Size = new System.Drawing.Size(85, 27); this.btn_choose_alternative_executable.Size = new System.Drawing.Size(85, 27);
this.btn_choose_alternative_executable.TabIndex = 9; this.btn_choose_alternative_executable.TabIndex = 9;
@ -845,9 +865,9 @@ namespace DisplayMagician.UIForms
// txt_alternative_executable // txt_alternative_executable
// //
this.txt_alternative_executable.Enabled = false; this.txt_alternative_executable.Enabled = false;
this.txt_alternative_executable.Location = new System.Drawing.Point(496, 122); this.txt_alternative_executable.Location = new System.Drawing.Point(633, 156);
this.txt_alternative_executable.Name = "txt_alternative_executable"; this.txt_alternative_executable.Name = "txt_alternative_executable";
this.txt_alternative_executable.Size = new System.Drawing.Size(378, 26); this.txt_alternative_executable.Size = new System.Drawing.Size(342, 26);
this.txt_alternative_executable.TabIndex = 4; this.txt_alternative_executable.TabIndex = 4;
this.txt_alternative_executable.TextChanged += new System.EventHandler(this.txt_alternative_executable_TextChanged); this.txt_alternative_executable.TextChanged += new System.EventHandler(this.txt_alternative_executable_TextChanged);
// //
@ -855,7 +875,7 @@ namespace DisplayMagician.UIForms
// //
this.rb_wait_alternative_executable.AutoSize = true; this.rb_wait_alternative_executable.AutoSize = true;
this.rb_wait_alternative_executable.ForeColor = System.Drawing.Color.White; this.rb_wait_alternative_executable.ForeColor = System.Drawing.Color.White;
this.rb_wait_alternative_executable.Location = new System.Drawing.Point(23, 122); this.rb_wait_alternative_executable.Location = new System.Drawing.Point(169, 156);
this.rb_wait_alternative_executable.Name = "rb_wait_alternative_executable"; this.rb_wait_alternative_executable.Name = "rb_wait_alternative_executable";
this.rb_wait_alternative_executable.Size = new System.Drawing.Size(468, 24); this.rb_wait_alternative_executable.Size = new System.Drawing.Size(468, 24);
this.rb_wait_alternative_executable.TabIndex = 8; this.rb_wait_alternative_executable.TabIndex = 8;
@ -869,7 +889,7 @@ namespace DisplayMagician.UIForms
this.rb_wait_executable.AutoSize = true; this.rb_wait_executable.AutoSize = true;
this.rb_wait_executable.Checked = true; this.rb_wait_executable.Checked = true;
this.rb_wait_executable.ForeColor = System.Drawing.Color.White; this.rb_wait_executable.ForeColor = System.Drawing.Color.White;
this.rb_wait_executable.Location = new System.Drawing.Point(23, 83); this.rb_wait_executable.Location = new System.Drawing.Point(169, 117);
this.rb_wait_executable.Name = "rb_wait_executable"; this.rb_wait_executable.Name = "rb_wait_executable";
this.rb_wait_executable.Size = new System.Drawing.Size(439, 24); this.rb_wait_executable.Size = new System.Drawing.Size(439, 24);
this.rb_wait_executable.TabIndex = 7; this.rb_wait_executable.TabIndex = 7;
@ -903,7 +923,7 @@ namespace DisplayMagician.UIForms
// //
this.label2.AutoSize = true; this.label2.AutoSize = true;
this.label2.ForeColor = System.Drawing.Color.Transparent; this.label2.ForeColor = System.Drawing.Color.Transparent;
this.label2.Location = new System.Drawing.Point(783, 12); this.label2.Location = new System.Drawing.Point(881, 13);
this.label2.Name = "label2"; this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(125, 20); this.label2.Size = new System.Drawing.Size(125, 20);
this.label2.TabIndex = 5; this.label2.TabIndex = 5;
@ -912,7 +932,7 @@ namespace DisplayMagician.UIForms
// //
// nud_timeout_executable // nud_timeout_executable
// //
this.nud_timeout_executable.Location = new System.Drawing.Point(910, 10); this.nud_timeout_executable.Location = new System.Drawing.Point(1012, 10);
this.nud_timeout_executable.Maximum = new decimal(new int[] { this.nud_timeout_executable.Maximum = new decimal(new int[] {
240, 240,
0, 0,
@ -953,7 +973,10 @@ namespace DisplayMagician.UIForms
// //
// p_game // p_game
// //
this.p_game.Controls.Add(this.lbl_game_library); this.p_game.Controls.Add(this.btn_refresh_games_list);
this.p_game.Controls.Add(this.btn_choose_game_icon);
this.p_game.Controls.Add(this.pb_game_icon);
this.p_game.Controls.Add(this.lbl_no_game_libraries);
this.p_game.Controls.Add(this.cbx_game_priority); this.p_game.Controls.Add(this.cbx_game_priority);
this.p_game.Controls.Add(this.ilv_games); this.p_game.Controls.Add(this.ilv_games);
this.p_game.Controls.Add(this.cb_wait_alternative_game); this.p_game.Controls.Add(this.cb_wait_alternative_game);
@ -966,45 +989,91 @@ namespace DisplayMagician.UIForms
this.p_game.Controls.Add(this.cb_args_game); this.p_game.Controls.Add(this.cb_args_game);
this.p_game.Controls.Add(this.lbl_game_timeout); this.p_game.Controls.Add(this.lbl_game_timeout);
this.p_game.Controls.Add(this.nud_timeout_game); this.p_game.Controls.Add(this.nud_timeout_game);
this.p_game.Dock = System.Windows.Forms.DockStyle.Bottom; this.p_game.Controls.Add(this.lbl_game_library);
this.p_game.Location = new System.Drawing.Point(3, 292); this.p_game.Location = new System.Drawing.Point(3, 338);
this.p_game.Name = "p_game"; this.p_game.Name = "p_game";
this.p_game.Size = new System.Drawing.Size(1076, 351); this.p_game.Size = new System.Drawing.Size(1076, 389);
this.p_game.TabIndex = 7; this.p_game.TabIndex = 7;
// //
// lbl_game_library // btn_refresh_games_list
// //
this.lbl_game_library.Anchor = System.Windows.Forms.AnchorStyles.None; this.btn_refresh_games_list.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.lbl_game_library.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_refresh_games_list.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
this.lbl_game_library.ForeColor = System.Drawing.Color.White; this.btn_refresh_games_list.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
this.lbl_game_library.Location = new System.Drawing.Point(389, 40); this.btn_refresh_games_list.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.lbl_game_library.Name = "lbl_game_library"; this.btn_refresh_games_list.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbl_game_library.Size = new System.Drawing.Size(145, 16); this.btn_refresh_games_list.ForeColor = System.Drawing.Color.White;
this.lbl_game_library.TabIndex = 30; this.btn_refresh_games_list.Location = new System.Drawing.Point(950, 162);
this.lbl_game_library.Text = "Game Library:"; this.btn_refresh_games_list.Name = "btn_refresh_games_list";
this.lbl_game_library.TextAlign = System.Drawing.ContentAlignment.TopRight; this.btn_refresh_games_list.Size = new System.Drawing.Size(117, 25);
this.btn_refresh_games_list.TabIndex = 42;
this.btn_refresh_games_list.Text = "Refresh Games List";
this.btn_refresh_games_list.UseVisualStyleBackColor = true;
this.btn_refresh_games_list.Click += new System.EventHandler(this.btn_refresh_games_list_Click);
//
// btn_choose_game_icon
//
this.btn_choose_game_icon.Enabled = false;
this.btn_choose_game_icon.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_choose_game_icon.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_choose_game_icon.ForeColor = System.Drawing.Color.White;
this.btn_choose_game_icon.Location = new System.Drawing.Point(36, 145);
this.btn_choose_game_icon.Name = "btn_choose_game_icon";
this.btn_choose_game_icon.Size = new System.Drawing.Size(100, 26);
this.btn_choose_game_icon.TabIndex = 37;
this.btn_choose_game_icon.Text = "Swap";
this.btn_choose_game_icon.UseVisualStyleBackColor = true;
this.btn_choose_game_icon.Click += new System.EventHandler(this.btn_choose_game_icon_Click);
//
// pb_game_icon
//
this.pb_game_icon.BackColor = System.Drawing.Color.DarkGray;
this.pb_game_icon.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.pb_game_icon.Location = new System.Drawing.Point(36, 48);
this.pb_game_icon.Name = "pb_game_icon";
this.pb_game_icon.Size = new System.Drawing.Size(100, 100);
this.pb_game_icon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pb_game_icon.TabIndex = 35;
this.pb_game_icon.TabStop = false;
//
// lbl_no_game_libraries
//
this.lbl_no_game_libraries.Anchor = System.Windows.Forms.AnchorStyles.None;
this.lbl_no_game_libraries.AutoSize = true;
this.lbl_no_game_libraries.BackColor = System.Drawing.Color.Brown;
this.lbl_no_game_libraries.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.lbl_no_game_libraries.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F);
this.lbl_no_game_libraries.ForeColor = System.Drawing.Color.White;
this.lbl_no_game_libraries.ImeMode = System.Windows.Forms.ImeMode.NoControl;
this.lbl_no_game_libraries.Location = new System.Drawing.Point(267, 162);
this.lbl_no_game_libraries.Name = "lbl_no_game_libraries";
this.lbl_no_game_libraries.Size = new System.Drawing.Size(613, 22);
this.lbl_no_game_libraries.TabIndex = 34;
this.lbl_no_game_libraries.Text = "No supported game libraries detected! (Steam, Origin, Uplay, Epic or GOG supporte" +
"d)";
this.lbl_no_game_libraries.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
this.lbl_no_game_libraries.Visible = false;
// //
// cbx_game_priority // cbx_game_priority
// //
this.cbx_game_priority.AllowDrop = true; this.cbx_game_priority.AllowDrop = true;
this.cbx_game_priority.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cbx_game_priority.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbx_game_priority.FormattingEnabled = true; this.cbx_game_priority.FormattingEnabled = true;
this.cbx_game_priority.Location = new System.Drawing.Point(150, 47); this.cbx_game_priority.Location = new System.Drawing.Point(679, 11);
this.cbx_game_priority.Name = "cbx_game_priority"; this.cbx_game_priority.Name = "cbx_game_priority";
this.cbx_game_priority.Size = new System.Drawing.Size(164, 28); this.cbx_game_priority.Size = new System.Drawing.Size(164, 28);
this.cbx_game_priority.TabIndex = 29; this.cbx_game_priority.TabIndex = 29;
// //
// ilv_games // ilv_games
// //
this.ilv_games.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) this.ilv_games.Dock = System.Windows.Forms.DockStyle.Bottom;
| System.Windows.Forms.AnchorStyles.Right)));
this.ilv_games.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.ilv_games.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.ilv_games.IntegralScroll = true; this.ilv_games.IntegralScroll = true;
this.ilv_games.Location = new System.Drawing.Point(0, 125); this.ilv_games.Location = new System.Drawing.Point(0, 200);
this.ilv_games.Name = "ilv_games"; this.ilv_games.Name = "ilv_games";
this.ilv_games.PersistentCacheDirectory = ""; this.ilv_games.PersistentCacheDirectory = "";
this.ilv_games.PersistentCacheSize = ((long)(100)); this.ilv_games.PersistentCacheSize = ((long)(100));
this.ilv_games.Size = new System.Drawing.Size(1076, 226); this.ilv_games.Size = new System.Drawing.Size(1076, 189);
this.ilv_games.SortOrder = Manina.Windows.Forms.SortOrder.Ascending; this.ilv_games.SortOrder = Manina.Windows.Forms.SortOrder.Ascending;
this.ilv_games.TabIndex = 28; this.ilv_games.TabIndex = 28;
this.ilv_games.UseWIC = true; this.ilv_games.UseWIC = true;
@ -1013,7 +1082,7 @@ namespace DisplayMagician.UIForms
// cb_wait_alternative_game // cb_wait_alternative_game
// //
this.cb_wait_alternative_game.AutoSize = true; this.cb_wait_alternative_game.AutoSize = true;
this.cb_wait_alternative_game.Location = new System.Drawing.Point(25, 85); this.cb_wait_alternative_game.Location = new System.Drawing.Point(165, 111);
this.cb_wait_alternative_game.Name = "cb_wait_alternative_game"; this.cb_wait_alternative_game.Name = "cb_wait_alternative_game";
this.cb_wait_alternative_game.Size = new System.Drawing.Size(229, 24); this.cb_wait_alternative_game.Size = new System.Drawing.Size(229, 24);
this.cb_wait_alternative_game.TabIndex = 27; this.cb_wait_alternative_game.TabIndex = 27;
@ -1026,7 +1095,7 @@ namespace DisplayMagician.UIForms
this.btn_choose_alternative_game.Enabled = false; this.btn_choose_alternative_game.Enabled = false;
this.btn_choose_alternative_game.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_choose_alternative_game.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_choose_alternative_game.ForeColor = System.Drawing.Color.White; this.btn_choose_alternative_game.ForeColor = System.Drawing.Color.White;
this.btn_choose_alternative_game.Location = new System.Drawing.Point(453, 85); this.btn_choose_alternative_game.Location = new System.Drawing.Point(981, 109);
this.btn_choose_alternative_game.Name = "btn_choose_alternative_game"; this.btn_choose_alternative_game.Name = "btn_choose_alternative_game";
this.btn_choose_alternative_game.Size = new System.Drawing.Size(85, 27); this.btn_choose_alternative_game.Size = new System.Drawing.Size(85, 27);
this.btn_choose_alternative_game.TabIndex = 26; this.btn_choose_alternative_game.TabIndex = 26;
@ -1037,9 +1106,9 @@ namespace DisplayMagician.UIForms
// txt_alternative_game // txt_alternative_game
// //
this.txt_alternative_game.Enabled = false; this.txt_alternative_game.Enabled = false;
this.txt_alternative_game.Location = new System.Drawing.Point(258, 86); this.txt_alternative_game.Location = new System.Drawing.Point(399, 109);
this.txt_alternative_game.Name = "txt_alternative_game"; this.txt_alternative_game.Name = "txt_alternative_game";
this.txt_alternative_game.Size = new System.Drawing.Size(193, 26); this.txt_alternative_game.Size = new System.Drawing.Size(576, 26);
this.txt_alternative_game.TabIndex = 24; this.txt_alternative_game.TabIndex = 24;
// //
// txt_game_name // txt_game_name
@ -1049,12 +1118,13 @@ namespace DisplayMagician.UIForms
this.txt_game_name.ReadOnly = true; this.txt_game_name.ReadOnly = true;
this.txt_game_name.Size = new System.Drawing.Size(385, 26); this.txt_game_name.Size = new System.Drawing.Size(385, 26);
this.txt_game_name.TabIndex = 21; this.txt_game_name.TabIndex = 21;
this.txt_game_name.Text = "Please select a game from the list below...";
// //
// lbl_game_priority // lbl_game_priority
// //
this.lbl_game_priority.AutoSize = true; this.lbl_game_priority.AutoSize = true;
this.lbl_game_priority.ForeColor = System.Drawing.Color.White; this.lbl_game_priority.ForeColor = System.Drawing.Color.White;
this.lbl_game_priority.Location = new System.Drawing.Point(41, 50); this.lbl_game_priority.Location = new System.Drawing.Point(570, 14);
this.lbl_game_priority.Name = "lbl_game_priority"; this.lbl_game_priority.Name = "lbl_game_priority";
this.lbl_game_priority.Size = new System.Drawing.Size(108, 20); this.lbl_game_priority.Size = new System.Drawing.Size(108, 20);
this.lbl_game_priority.TabIndex = 18; this.lbl_game_priority.TabIndex = 18;
@ -1076,16 +1146,16 @@ namespace DisplayMagician.UIForms
// txt_args_game // txt_args_game
// //
this.txt_args_game.Enabled = false; this.txt_args_game.Enabled = false;
this.txt_args_game.Location = new System.Drawing.Point(788, 11); this.txt_args_game.Location = new System.Drawing.Point(399, 72);
this.txt_args_game.Name = "txt_args_game"; this.txt_args_game.Name = "txt_args_game";
this.txt_args_game.Size = new System.Drawing.Size(279, 26); this.txt_args_game.Size = new System.Drawing.Size(667, 26);
this.txt_args_game.TabIndex = 13; this.txt_args_game.TabIndex = 13;
// //
// cb_args_game // cb_args_game
// //
this.cb_args_game.AutoSize = true; this.cb_args_game.AutoSize = true;
this.cb_args_game.ForeColor = System.Drawing.Color.White; this.cb_args_game.ForeColor = System.Drawing.Color.White;
this.cb_args_game.Location = new System.Drawing.Point(555, 13); this.cb_args_game.Location = new System.Drawing.Point(166, 74);
this.cb_args_game.Name = "cb_args_game"; this.cb_args_game.Name = "cb_args_game";
this.cb_args_game.Size = new System.Drawing.Size(213, 24); this.cb_args_game.Size = new System.Drawing.Size(213, 24);
this.cb_args_game.TabIndex = 12; this.cb_args_game.TabIndex = 12;
@ -1099,7 +1169,7 @@ namespace DisplayMagician.UIForms
// //
this.lbl_game_timeout.AutoSize = true; this.lbl_game_timeout.AutoSize = true;
this.lbl_game_timeout.ForeColor = System.Drawing.Color.White; this.lbl_game_timeout.ForeColor = System.Drawing.Color.White;
this.lbl_game_timeout.Location = new System.Drawing.Point(656, 51); this.lbl_game_timeout.Location = new System.Drawing.Point(881, 14);
this.lbl_game_timeout.Name = "lbl_game_timeout"; this.lbl_game_timeout.Name = "lbl_game_timeout";
this.lbl_game_timeout.Size = new System.Drawing.Size(125, 20); this.lbl_game_timeout.Size = new System.Drawing.Size(125, 20);
this.lbl_game_timeout.TabIndex = 4; this.lbl_game_timeout.TabIndex = 4;
@ -1108,7 +1178,7 @@ namespace DisplayMagician.UIForms
// //
// nud_timeout_game // nud_timeout_game
// //
this.nud_timeout_game.Location = new System.Drawing.Point(787, 50); this.nud_timeout_game.Location = new System.Drawing.Point(1012, 13);
this.nud_timeout_game.Maximum = new decimal(new int[] { this.nud_timeout_game.Maximum = new decimal(new int[] {
240, 240,
0, 0,
@ -1123,12 +1193,24 @@ namespace DisplayMagician.UIForms
0, 0,
0}); 0});
// //
// lbl_game_library
//
this.lbl_game_library.Anchor = System.Windows.Forms.AnchorStyles.None;
this.lbl_game_library.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbl_game_library.ForeColor = System.Drawing.Color.White;
this.lbl_game_library.Location = new System.Drawing.Point(408, 40);
this.lbl_game_library.Name = "lbl_game_library";
this.lbl_game_library.Size = new System.Drawing.Size(127, 22);
this.lbl_game_library.TabIndex = 30;
this.lbl_game_library.Text = "Game Library:";
this.lbl_game_library.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// rb_launcher // rb_launcher
// //
this.rb_launcher.AutoSize = true; this.rb_launcher.AutoSize = true;
this.rb_launcher.Checked = true; this.rb_launcher.Checked = true;
this.rb_launcher.ForeColor = System.Drawing.Color.White; this.rb_launcher.ForeColor = System.Drawing.Color.White;
this.rb_launcher.Location = new System.Drawing.Point(15, 262); this.rb_launcher.Location = new System.Drawing.Point(15, 309);
this.rb_launcher.Name = "rb_launcher"; this.rb_launcher.Name = "rb_launcher";
this.rb_launcher.Size = new System.Drawing.Size(466, 24); this.rb_launcher.Size = new System.Drawing.Size(466, 24);
this.rb_launcher.TabIndex = 6; this.rb_launcher.TabIndex = 6;
@ -1140,6 +1222,7 @@ namespace DisplayMagician.UIForms
// tabp_after // tabp_after
// //
this.tabp_after.BackColor = System.Drawing.Color.Black; this.tabp_after.BackColor = System.Drawing.Color.Black;
this.tabp_after.Controls.Add(this.groupBox3);
this.tabp_after.Controls.Add(this.groupBox2); this.tabp_after.Controls.Add(this.groupBox2);
this.tabp_after.Controls.Add(this.groupBox1); this.tabp_after.Controls.Add(this.groupBox1);
this.tabp_after.Controls.Add(this.gb_display_after); this.tabp_after.Controls.Add(this.gb_display_after);
@ -1148,17 +1231,86 @@ namespace DisplayMagician.UIForms
this.tabp_after.Location = new System.Drawing.Point(4, 32); this.tabp_after.Location = new System.Drawing.Point(4, 32);
this.tabp_after.Name = "tabp_after"; this.tabp_after.Name = "tabp_after";
this.tabp_after.Padding = new System.Windows.Forms.Padding(3); this.tabp_after.Padding = new System.Windows.Forms.Padding(3);
this.tabp_after.Size = new System.Drawing.Size(1082, 646); this.tabp_after.Size = new System.Drawing.Size(1082, 731);
this.tabp_after.TabIndex = 3; this.tabp_after.TabIndex = 3;
this.tabp_after.Text = "5. Choose what happens afterwards"; this.tabp_after.Text = "5. Choose what happens afterwards";
// //
// groupBox3
//
this.groupBox3.Controls.Add(this.txt_run_cmd_afterwards_args);
this.groupBox3.Controls.Add(this.cb_run_cmd_afterwards_args);
this.groupBox3.Controls.Add(this.btn_run_cmd_afterwards);
this.groupBox3.Controls.Add(this.txt_run_cmd_afterwards);
this.groupBox3.Controls.Add(this.cb_run_cmd_afterwards);
this.groupBox3.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.groupBox3.ForeColor = System.Drawing.Color.White;
this.groupBox3.Location = new System.Drawing.Point(175, 582);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(765, 122);
this.groupBox3.TabIndex = 14;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "Run a program or command afterwards?";
//
// txt_run_cmd_afterwards_args
//
this.txt_run_cmd_afterwards_args.Enabled = false;
this.txt_run_cmd_afterwards_args.Location = new System.Drawing.Point(251, 75);
this.txt_run_cmd_afterwards_args.Name = "txt_run_cmd_afterwards_args";
this.txt_run_cmd_afterwards_args.Size = new System.Drawing.Size(479, 26);
this.txt_run_cmd_afterwards_args.TabIndex = 13;
//
// cb_run_cmd_afterwards_args
//
this.cb_run_cmd_afterwards_args.AutoSize = true;
this.cb_run_cmd_afterwards_args.ForeColor = System.Drawing.Color.White;
this.cb_run_cmd_afterwards_args.Location = new System.Drawing.Point(98, 77);
this.cb_run_cmd_afterwards_args.Name = "cb_run_cmd_afterwards_args";
this.cb_run_cmd_afterwards_args.Size = new System.Drawing.Size(147, 24);
this.cb_run_cmd_afterwards_args.TabIndex = 12;
this.cb_run_cmd_afterwards_args.Text = "Pass arguments:";
this.cb_run_cmd_afterwards_args.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
this.cb_run_cmd_afterwards_args.UseVisualStyleBackColor = true;
this.cb_run_cmd_afterwards_args.CheckedChanged += new System.EventHandler(this.cb_run_cmd_afterwards_args_CheckedChanged);
//
// btn_run_cmd_afterwards
//
this.btn_run_cmd_afterwards.Enabled = false;
this.btn_run_cmd_afterwards.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_run_cmd_afterwards.ForeColor = System.Drawing.Color.White;
this.btn_run_cmd_afterwards.Location = new System.Drawing.Point(645, 35);
this.btn_run_cmd_afterwards.Name = "btn_run_cmd_afterwards";
this.btn_run_cmd_afterwards.Size = new System.Drawing.Size(85, 27);
this.btn_run_cmd_afterwards.TabIndex = 11;
this.btn_run_cmd_afterwards.Text = "Choose";
this.btn_run_cmd_afterwards.UseVisualStyleBackColor = true;
this.btn_run_cmd_afterwards.Click += new System.EventHandler(this.btn_run_cmd_afterwards_Click);
//
// txt_run_cmd_afterwards
//
this.txt_run_cmd_afterwards.Enabled = false;
this.txt_run_cmd_afterwards.Location = new System.Drawing.Point(250, 36);
this.txt_run_cmd_afterwards.Name = "txt_run_cmd_afterwards";
this.txt_run_cmd_afterwards.Size = new System.Drawing.Size(389, 26);
this.txt_run_cmd_afterwards.TabIndex = 10;
//
// cb_run_cmd_afterwards
//
this.cb_run_cmd_afterwards.AutoSize = true;
this.cb_run_cmd_afterwards.Location = new System.Drawing.Point(98, 38);
this.cb_run_cmd_afterwards.Name = "cb_run_cmd_afterwards";
this.cb_run_cmd_afterwards.Size = new System.Drawing.Size(154, 24);
this.cb_run_cmd_afterwards.TabIndex = 0;
this.cb_run_cmd_afterwards.Text = "Run this program:";
this.cb_run_cmd_afterwards.UseVisualStyleBackColor = true;
this.cb_run_cmd_afterwards.CheckedChanged += new System.EventHandler(this.cb_run_cmd_afterwards_CheckedChanged);
//
// groupBox2 // groupBox2
// //
this.groupBox2.Controls.Add(this.rb_switch_capture_permanent); this.groupBox2.Controls.Add(this.rb_switch_capture_permanent);
this.groupBox2.Controls.Add(this.rb_switch_capture_temp); this.groupBox2.Controls.Add(this.rb_switch_capture_temp);
this.groupBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.groupBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.groupBox2.ForeColor = System.Drawing.Color.White; this.groupBox2.ForeColor = System.Drawing.Color.White;
this.groupBox2.Location = new System.Drawing.Point(175, 404); this.groupBox2.Location = new System.Drawing.Point(175, 395);
this.groupBox2.Name = "groupBox2"; this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(765, 161); this.groupBox2.Size = new System.Drawing.Size(765, 161);
this.groupBox2.TabIndex = 13; this.groupBox2.TabIndex = 13;
@ -1197,7 +1349,7 @@ namespace DisplayMagician.UIForms
this.groupBox1.Controls.Add(this.rb_switch_audio_temp); this.groupBox1.Controls.Add(this.rb_switch_audio_temp);
this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.groupBox1.ForeColor = System.Drawing.Color.White; this.groupBox1.ForeColor = System.Drawing.Color.White;
this.groupBox1.Location = new System.Drawing.Point(175, 219); this.groupBox1.Location = new System.Drawing.Point(175, 210);
this.groupBox1.Name = "groupBox1"; this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(765, 161); this.groupBox1.Size = new System.Drawing.Size(765, 161);
this.groupBox1.TabIndex = 12; this.groupBox1.TabIndex = 12;
@ -1236,7 +1388,7 @@ namespace DisplayMagician.UIForms
this.gb_display_after.Controls.Add(this.rb_switch_display_temp); this.gb_display_after.Controls.Add(this.rb_switch_display_temp);
this.gb_display_after.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.gb_display_after.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.gb_display_after.ForeColor = System.Drawing.Color.White; this.gb_display_after.ForeColor = System.Drawing.Color.White;
this.gb_display_after.Location = new System.Drawing.Point(175, 31); this.gb_display_after.Location = new System.Drawing.Point(175, 22);
this.gb_display_after.Name = "gb_display_after"; this.gb_display_after.Name = "gb_display_after";
this.gb_display_after.Size = new System.Drawing.Size(765, 162); this.gb_display_after.Size = new System.Drawing.Size(765, 162);
this.gb_display_after.TabIndex = 11; this.gb_display_after.TabIndex = 11;
@ -1274,7 +1426,7 @@ namespace DisplayMagician.UIForms
this.txt_shortcut_save_name.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) this.txt_shortcut_save_name.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.txt_shortcut_save_name.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.txt_shortcut_save_name.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.txt_shortcut_save_name.Location = new System.Drawing.Point(207, 759); this.txt_shortcut_save_name.Location = new System.Drawing.Point(207, 844);
this.txt_shortcut_save_name.MaxLength = 200; this.txt_shortcut_save_name.MaxLength = 200;
this.txt_shortcut_save_name.Name = "txt_shortcut_save_name"; this.txt_shortcut_save_name.Name = "txt_shortcut_save_name";
this.txt_shortcut_save_name.Size = new System.Drawing.Size(744, 35); this.txt_shortcut_save_name.Size = new System.Drawing.Size(744, 35);
@ -1300,7 +1452,7 @@ namespace DisplayMagician.UIForms
this.lbl_shortcut_name.AutoSize = true; this.lbl_shortcut_name.AutoSize = true;
this.lbl_shortcut_name.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lbl_shortcut_name.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbl_shortcut_name.ForeColor = System.Drawing.Color.Transparent; this.lbl_shortcut_name.ForeColor = System.Drawing.Color.Transparent;
this.lbl_shortcut_name.Location = new System.Drawing.Point(23, 762); this.lbl_shortcut_name.Location = new System.Drawing.Point(23, 847);
this.lbl_shortcut_name.Name = "lbl_shortcut_name"; this.lbl_shortcut_name.Name = "lbl_shortcut_name";
this.lbl_shortcut_name.Size = new System.Drawing.Size(178, 29); this.lbl_shortcut_name.Size = new System.Drawing.Size(178, 29);
this.lbl_shortcut_name.TabIndex = 31; this.lbl_shortcut_name.TabIndex = 31;
@ -1314,7 +1466,7 @@ namespace DisplayMagician.UIForms
this.cb_autosuggest.Checked = true; this.cb_autosuggest.Checked = true;
this.cb_autosuggest.CheckState = System.Windows.Forms.CheckState.Checked; this.cb_autosuggest.CheckState = System.Windows.Forms.CheckState.Checked;
this.cb_autosuggest.ForeColor = System.Drawing.Color.White; this.cb_autosuggest.ForeColor = System.Drawing.Color.White;
this.cb_autosuggest.Location = new System.Drawing.Point(969, 768); this.cb_autosuggest.Location = new System.Drawing.Point(969, 853);
this.cb_autosuggest.Name = "cb_autosuggest"; this.cb_autosuggest.Name = "cb_autosuggest";
this.cb_autosuggest.Size = new System.Drawing.Size(117, 17); this.cb_autosuggest.Size = new System.Drawing.Size(117, 17);
this.cb_autosuggest.TabIndex = 32; this.cb_autosuggest.TabIndex = 32;
@ -1331,7 +1483,7 @@ namespace DisplayMagician.UIForms
this.btn_hotkey.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_hotkey.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_hotkey.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_hotkey.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_hotkey.ForeColor = System.Drawing.Color.White; this.btn_hotkey.ForeColor = System.Drawing.Color.White;
this.btn_hotkey.Location = new System.Drawing.Point(434, 806); this.btn_hotkey.Location = new System.Drawing.Point(434, 891);
this.btn_hotkey.Name = "btn_hotkey"; this.btn_hotkey.Name = "btn_hotkey";
this.btn_hotkey.Size = new System.Drawing.Size(120, 40); this.btn_hotkey.Size = new System.Drawing.Size(120, 40);
this.btn_hotkey.TabIndex = 36; this.btn_hotkey.TabIndex = 36;
@ -1341,11 +1493,12 @@ namespace DisplayMagician.UIForms
// //
// lbl_hotkey_assigned // lbl_hotkey_assigned
// //
this.lbl_hotkey_assigned.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lbl_hotkey_assigned.AutoSize = true; this.lbl_hotkey_assigned.AutoSize = true;
this.lbl_hotkey_assigned.BackColor = System.Drawing.Color.Transparent; this.lbl_hotkey_assigned.BackColor = System.Drawing.Color.Transparent;
this.lbl_hotkey_assigned.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lbl_hotkey_assigned.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.lbl_hotkey_assigned.ForeColor = System.Drawing.Color.White; this.lbl_hotkey_assigned.ForeColor = System.Drawing.Color.White;
this.lbl_hotkey_assigned.Location = new System.Drawing.Point(26, 793); this.lbl_hotkey_assigned.Location = new System.Drawing.Point(26, 907);
this.lbl_hotkey_assigned.Name = "lbl_hotkey_assigned"; this.lbl_hotkey_assigned.Name = "lbl_hotkey_assigned";
this.lbl_hotkey_assigned.Size = new System.Drawing.Size(57, 16); this.lbl_hotkey_assigned.Size = new System.Drawing.Size(57, 16);
this.lbl_hotkey_assigned.TabIndex = 37; this.lbl_hotkey_assigned.TabIndex = 37;
@ -1360,7 +1513,7 @@ namespace DisplayMagician.UIForms
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Black; this.BackColor = System.Drawing.Color.Black;
this.CancelButton = this.btn_cancel; this.CancelButton = this.btn_cancel;
this.ClientSize = new System.Drawing.Size(1114, 858); this.ClientSize = new System.Drawing.Size(1114, 943);
this.Controls.Add(this.lbl_hotkey_assigned); this.Controls.Add(this.lbl_hotkey_assigned);
this.Controls.Add(this.btn_hotkey); this.Controls.Add(this.btn_hotkey);
this.Controls.Add(this.cb_autosuggest); this.Controls.Add(this.cb_autosuggest);
@ -1401,11 +1554,15 @@ namespace DisplayMagician.UIForms
this.tabp_game.PerformLayout(); this.tabp_game.PerformLayout();
this.p_standalone.ResumeLayout(false); this.p_standalone.ResumeLayout(false);
this.p_standalone.PerformLayout(); this.p_standalone.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.pb_exe_icon)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.nud_timeout_executable)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.nud_timeout_executable)).EndInit();
this.p_game.ResumeLayout(false); this.p_game.ResumeLayout(false);
this.p_game.PerformLayout(); this.p_game.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.pb_game_icon)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.nud_timeout_game)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.nud_timeout_game)).EndInit();
this.tabp_after.ResumeLayout(false); this.tabp_after.ResumeLayout(false);
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.groupBox2.ResumeLayout(false); this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout(); this.groupBox2.PerformLayout();
this.groupBox1.ResumeLayout(false); this.groupBox1.ResumeLayout(false);
@ -1508,5 +1665,16 @@ namespace DisplayMagician.UIForms
private System.Windows.Forms.ComboBox cbx_game_priority; private System.Windows.Forms.ComboBox cbx_game_priority;
private System.Windows.Forms.Label lbl_game_priority; private System.Windows.Forms.Label lbl_game_priority;
private System.Windows.Forms.PictureBox pbLogo; private System.Windows.Forms.PictureBox pbLogo;
private System.Windows.Forms.Button btn_choose_exe_icon;
private System.Windows.Forms.PictureBox pb_exe_icon;
private System.Windows.Forms.Button btn_choose_game_icon;
private System.Windows.Forms.PictureBox pb_game_icon;
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.Button btn_run_cmd_afterwards;
private System.Windows.Forms.TextBox txt_run_cmd_afterwards;
private System.Windows.Forms.CheckBox cb_run_cmd_afterwards;
private System.Windows.Forms.TextBox txt_run_cmd_afterwards_args;
private System.Windows.Forms.CheckBox cb_run_cmd_afterwards_args;
private System.Windows.Forms.Button btn_refresh_games_list;
} }
} }

View File

@ -1,23 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing.IconLib;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
using DisplayMagician.Resources; using DisplayMagician.Resources;
using DisplayMagicianShared; using DisplayMagicianShared;
using DisplayMagician.GameLibraries; using DisplayMagician.GameLibraries;
using System.Globalization;
using Manina.Windows.Forms; using Manina.Windows.Forms;
using System.Windows.Forms.VisualStyles; using System.Windows.Forms.VisualStyles;
using AudioSwitcher.AudioApi.CoreAudio; using AudioSwitcher.AudioApi.CoreAudio;
using AudioSwitcher.AudioApi; using AudioSwitcher.AudioApi;
using NHotkey.WindowsForms; using NHotkey.WindowsForms;
using NHotkey; using NHotkey;
//using WK.Libraries.HotkeyListenerNS; using System.Threading;
namespace DisplayMagician.UIForms namespace DisplayMagician.UIForms
{ {
@ -27,6 +23,7 @@ namespace DisplayMagician.UIForms
private ProfileAdaptor _profileAdaptor; private ProfileAdaptor _profileAdaptor;
private GameAdaptor _gameAdaptor; private GameAdaptor _gameAdaptor;
private bool _editingExistingShortcut = false;
//private List<ProfileItem> _loadedProfiles = new List<ProfileItem>(); //private List<ProfileItem> _loadedProfiles = new List<ProfileItem>();
private ProfileItem _profileToUse = null; private ProfileItem _profileToUse = null;
private string _gameLauncher = ""; private string _gameLauncher = "";
@ -36,6 +33,7 @@ namespace DisplayMagician.UIForms
private ShortcutPermanence _audioPermanence = ShortcutPermanence.Temporary; private ShortcutPermanence _audioPermanence = ShortcutPermanence.Temporary;
private ShortcutPermanence _capturePermanence = ShortcutPermanence.Temporary; private ShortcutPermanence _capturePermanence = ShortcutPermanence.Temporary;
List<StartProgram> _startPrograms = new List<StartProgram>(); List<StartProgram> _startPrograms = new List<StartProgram>();
List<StopProgram> _stopPrograms = new List<StopProgram>();
private string _audioDevice = ""; private string _audioDevice = "";
private bool _changeAudioDevice = false; private bool _changeAudioDevice = false;
private bool _setAudioVolume = false; private bool _setAudioVolume = false;
@ -57,11 +55,26 @@ namespace DisplayMagician.UIForms
private List<CoreAudioDevice> captureDevices = null; private List<CoreAudioDevice> captureDevices = null;
private CoreAudioDevice selectedCaptureDevice = null; private CoreAudioDevice selectedCaptureDevice = null;
private Keys _hotkey = Keys.None; private Keys _hotkey = Keys.None;
//private bool _userChoseOwnGameIcon = false;
//private string _userGameIconPath = "";
//private bool _userChoseOwnExeIcon = false;
//private string _userExeIconPath = "";
private List<ShortcutBitmap> _availableImages = new List<ShortcutBitmap>();
private ShortcutBitmap _selectedImage = new ShortcutBitmap();
private bool _firstShow = true;
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public ShortcutForm(ShortcutItem shortcutToEdit) public ShortcutForm()
{ {
InitializeComponent(); InitializeComponent();
Program.AppSplashScreen = new LoadingForm();
Program.AppSplashScreen.Title = "Preparing images...";
Program.AppSplashScreen.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.";
var splashThread = new Thread(new ThreadStart(
() => Application.Run(Program.AppSplashScreen)));
splashThread.SetApartmentState(ApartmentState.STA);
splashThread.Start();
// Set the profileAdaptor we need to load images from Profiles // Set the profileAdaptor we need to load images from Profiles
// into the Profiles ImageListView // into the Profiles ImageListView
@ -70,8 +83,6 @@ namespace DisplayMagician.UIForms
_profileAdaptor = new ProfileAdaptor(); _profileAdaptor = new ProfileAdaptor();
_gameAdaptor = new GameAdaptor(); _gameAdaptor = new GameAdaptor();
_shortcutToEdit = shortcutToEdit;
// Style the Saved Profiles list // Style the Saved Profiles list
ilv_saved_profiles.MultiSelect = false; ilv_saved_profiles.MultiSelect = false;
ilv_saved_profiles.ThumbnailSize = new Size(100, 100); ilv_saved_profiles.ThumbnailSize = new Size(100, 100);
@ -96,9 +107,13 @@ namespace DisplayMagician.UIForms
lbl_profile_shown_subtitle.Text = "Please go back to the main window, click on 'Display Profiles', and save a new Display Profile. Then come back here."; lbl_profile_shown_subtitle.Text = "Please go back to the main window, click on 'Display Profiles', and save a new Display Profile. Then come back here.";
try try
{
if (audioController == null)
{ {
audioController = new CoreAudioController(); audioController = new CoreAudioController();
} }
}
catch (Exception ex) catch (Exception ex)
{ {
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."); 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.");
@ -109,8 +124,14 @@ namespace DisplayMagician.UIForms
public ShortcutItem Shortcut public ShortcutItem Shortcut
{ {
get => _shortcutToEdit; get => _shortcutToEdit;
set => _shortcutToEdit = value;
} }
public bool EditingExistingShortcut
{
get => _editingExistingShortcut;
set => _editingExistingShortcut = value;
}
public SupportedGameLibraryType GameLibrary public SupportedGameLibraryType GameLibrary
{ {
@ -194,13 +215,6 @@ namespace DisplayMagician.UIForms
} }
} }
/*private static bool IsLowQuality(IconImage iconImage)
{
return iconImage.PixelFormat == System.Drawing.Imaging.PixelFormat.Format1bppIndexed ||
iconImage.PixelFormat == System.Drawing.Imaging.PixelFormat.Format4bppIndexed ||
iconImage.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed;
}*/
private void btn_app_executable_Click(object sender, EventArgs e) private void btn_app_executable_Click(object sender, EventArgs e)
{ {
if (dialog_open.ShowDialog(this) == DialogResult.OK) if (dialog_open.ShowDialog(this) == DialogResult.OK)
@ -490,6 +504,29 @@ namespace DisplayMagician.UIForms
// Replace the old start programs with the ones we've created now // Replace the old start programs with the ones we've created now
_startPrograms = newStartPrograms; _startPrograms = newStartPrograms;
// Store the single stop program if it's set (but wth lots of defaults)
if (!String.IsNullOrWhiteSpace(txt_run_cmd_afterwards.Text) && File.Exists(txt_run_cmd_afterwards.Text))
{
_stopPrograms = new List<StopProgram>();
StopProgram stopProgram = new StopProgram();
stopProgram.Executable = txt_run_cmd_afterwards.Text;
stopProgram.Priority = 0;
stopProgram.DontStartIfAlreadyRunning = false;
stopProgram.Disabled = false;
if (cb_run_cmd_afterwards_args.Checked)
{
stopProgram.ExecutableArgumentsRequired = true;
stopProgram.Arguments = txt_run_cmd_afterwards_args.Text;
}
else
{
stopProgram.ExecutableArgumentsRequired = false;
stopProgram.Arguments = "";
}
stopProgram.ProcessPriority = ProcessPriority.Normal;
_stopPrograms.Add(stopProgram);
}
// Now we create the Shortcut Object ready to save // Now we create the Shortcut Object ready to save
// If we're launching a game // If we're launching a game
if (rb_launcher.Checked) if (rb_launcher.Checked)
@ -550,6 +587,8 @@ namespace DisplayMagician.UIForms
_audioPermanence, _audioPermanence,
_capturePermanence, _capturePermanence,
_gameToUse.GameToPlay.IconPath, _gameToUse.GameToPlay.IconPath,
_selectedImage,
_availableImages,
_changeAudioDevice, _changeAudioDevice,
_audioDevice, _audioDevice,
_setAudioVolume, _setAudioVolume,
@ -559,6 +598,7 @@ namespace DisplayMagician.UIForms
_setCaptureVolume, _setCaptureVolume,
_captureVolume, _captureVolume,
_startPrograms, _startPrograms,
_stopPrograms,
_autoName, _autoName,
_uuid, _uuid,
_hotkey _hotkey
@ -574,6 +614,8 @@ namespace DisplayMagician.UIForms
_audioPermanence, _audioPermanence,
_capturePermanence, _capturePermanence,
_gameToUse.GameToPlay.IconPath, _gameToUse.GameToPlay.IconPath,
_selectedImage,
_availableImages,
_changeAudioDevice, _changeAudioDevice,
_audioDevice, _audioDevice,
_setAudioVolume, _setAudioVolume,
@ -583,6 +625,7 @@ namespace DisplayMagician.UIForms
_setCaptureVolume, _setCaptureVolume,
_captureVolume, _captureVolume,
_startPrograms, _startPrograms,
_stopPrograms,
_autoName, _autoName,
_uuid, _uuid,
_hotkey _hotkey
@ -612,6 +655,7 @@ namespace DisplayMagician.UIForms
_executableToUse.ProcessNameToMonitorUsesExecutable = true; _executableToUse.ProcessNameToMonitorUsesExecutable = true;
} }
try try
{ {
_shortcutToEdit.UpdateExecutableShortcut( _shortcutToEdit.UpdateExecutableShortcut(
@ -622,6 +666,8 @@ namespace DisplayMagician.UIForms
_audioPermanence, _audioPermanence,
_capturePermanence, _capturePermanence,
_executableToUse.ExecutableNameAndPath, _executableToUse.ExecutableNameAndPath,
_selectedImage,
_availableImages,
_changeAudioDevice, _changeAudioDevice,
_audioDevice, _audioDevice,
_setAudioVolume, _setAudioVolume,
@ -631,6 +677,7 @@ namespace DisplayMagician.UIForms
_setCaptureVolume, _setCaptureVolume,
_captureVolume, _captureVolume,
_startPrograms, _startPrograms,
_stopPrograms,
_autoName, _autoName,
_hotkey _hotkey
); );
@ -645,6 +692,8 @@ namespace DisplayMagician.UIForms
_audioPermanence, _audioPermanence,
_capturePermanence, _capturePermanence,
_executableToUse.ExecutableNameAndPath, _executableToUse.ExecutableNameAndPath,
_selectedImage,
_availableImages,
_changeAudioDevice, _changeAudioDevice,
_audioDevice, _audioDevice,
_setAudioVolume, _setAudioVolume,
@ -654,6 +703,7 @@ namespace DisplayMagician.UIForms
_setCaptureVolume, _setCaptureVolume,
_captureVolume, _captureVolume,
_startPrograms, _startPrograms,
_stopPrograms,
_autoName, _autoName,
_hotkey _hotkey
); );
@ -681,6 +731,7 @@ namespace DisplayMagician.UIForms
_setCaptureVolume, _setCaptureVolume,
_captureVolume, _captureVolume,
_startPrograms, _startPrograms,
_stopPrograms,
_autoName, _autoName,
_hotkey _hotkey
); );
@ -703,6 +754,7 @@ namespace DisplayMagician.UIForms
_setCaptureVolume, _setCaptureVolume,
_captureVolume, _captureVolume,
_startPrograms, _startPrograms,
_stopPrograms,
_autoName, _autoName,
_hotkey _hotkey
); );
@ -726,7 +778,7 @@ namespace DisplayMagician.UIForms
// Save everything is golden and close the form. // Save everything is golden and close the form.
DialogResult = DialogResult.OK; DialogResult = DialogResult.OK;
this.Close(); this.Hide();
} }
private void txt_alternative_executable_TextChanged(object sender, EventArgs e) private void txt_alternative_executable_TextChanged(object sender, EventArgs e)
@ -845,7 +897,7 @@ namespace DisplayMagician.UIForms
else if (rb_switch_display_temp.Checked) else if (rb_switch_display_temp.Checked)
txt_shortcut_save_name.Text = $"{_profileToUse.Name} (Temporary)"; txt_shortcut_save_name.Text = $"{_profileToUse.Name} (Temporary)";
} }
else if (rb_launcher.Checked && txt_game_name.Text.Length > 0) else if (rb_launcher.Checked && ilv_games.SelectedItems.Count > 0)
{ {
txt_shortcut_save_name.Text = $"{txt_game_name.Text} ({_profileToUse.Name})"; txt_shortcut_save_name.Text = $"{txt_game_name.Text} ({_profileToUse.Name})";
} }
@ -872,28 +924,63 @@ namespace DisplayMagician.UIForms
} }
} }
private void SelectGameInImageListView() private void ClearForm()
{ {
ilv_games.ClearSelection(); // Clear the textboxes
IEnumerable<ImageListViewItem> matchingImageListViewItems = (from item in ilv_games.Items where item.Text == _shortcutToEdit.GameName select item); txt_alternative_executable.Text = "";
if (matchingImageListViewItems.Any()) txt_alternative_game.Text = "";
txt_args_executable.Text = "";
txt_args_game.Text = "";
txt_executable.Text = "";
txt_game_name.Text = "Please select a game from the list below...";
txt_run_cmd_afterwards.Text = "";
txt_run_cmd_afterwards_args.Text = "";
txt_shortcut_save_name.Text = "";
// Set the radio buttons to default
rb_no_change_audio.Checked = true;
rb_change_audio.Checked = false;
rb_keep_audio_volume.Checked = true;
rb_set_audio_volume.Checked = false;
rb_change_capture.Checked = false;
rb_keep_capture_volume.Checked = false;
rb_no_change_capture.Checked = true;
rb_set_capture_volume.Checked = false;
// Set the game mode on load
rb_launcher.Checked = true;
rb_no_game.Checked = false;
rb_standalone.Checked = false;
// Set the checkboxes
cb_args_executable.Checked = false;
cb_args_game.Checked = false;
cb_autosuggest.Checked = true;
cb_run_cmd_afterwards.Checked = false;
cb_run_cmd_afterwards_args.Checked = false;
cb_wait_alternative_game.Checked = false;
// Wipe the pictureboxes if they're in use
if (pb_exe_icon.Image != null)
{ {
ImageListViewItem itemToSelect = matchingImageListViewItems.First(); pb_exe_icon.Image = null;
itemToSelect.Selected = true; }
itemToSelect.Focused = true; if (pb_game_icon.Image != null)
itemToSelect.Enabled = true; {
ilv_games.EnsureVisible(itemToSelect.Index); pb_game_icon.Image = null;
//ilv_games.Refresh();
} }
} }
private void ShortcutForm_Load(object sender, EventArgs e) private void LoadShortcut()
{ {
Game shortcutGame = null;
// Load all the profiles to prepare things // Load all the profiles to prepare things
bool foundChosenProfileInLoadedProfiles = false; bool foundChosenProfileInLoadedProfiles = false;
ProfileItem chosenProfile = null; ProfileItem chosenProfile = null;
ClearForm();
// Prepare the Game process priority combo box // Prepare the Game process priority combo box
cbx_game_priority.DataSource = new ComboItem[] { cbx_game_priority.DataSource = new ComboItem[] {
new ComboItem{ Value = ProcessPriority.High, Text = "High" }, new ComboItem{ Value = ProcessPriority.High, Text = "High" },
@ -920,6 +1007,9 @@ namespace DisplayMagician.UIForms
cbx_exe_priority.SelectedIndex = 2; //Normal cbx_exe_priority.SelectedIndex = 2; //Normal
cbx_exe_priority.Enabled = true; cbx_exe_priority.Enabled = true;
// Empty the selected game in case this is a reload
txt_alternative_executable.Text = "";
// Populate all the Audio devices in the audio devices list. // Populate all the Audio devices in the audio devices list.
// Set the Audio device to the shortcut audio device only if // Set the Audio device to the shortcut audio device only if
// the Change Audio radiobutton is set // the Change Audio radiobutton is set
@ -1137,11 +1227,15 @@ namespace DisplayMagician.UIForms
ImageListViewItem newItem = new ImageListViewItem(game, game.Name); ImageListViewItem newItem = new ImageListViewItem(game, game.Name);
//ilv_saved_profiles.Items.Add(newItem); //ilv_saved_profiles.Items.Add(newItem);
ilv_games.Items.Add(newItem, _gameAdaptor); ilv_games.Items.Add(newItem, _gameAdaptor);
if (_editingExistingShortcut && game.Name.Equals(_shortcutToEdit.GameName))
{
shortcutGame = game;
} }
if (_shortcutToEdit != null) }
{
if (_shortcutToEdit.Category == ShortcutCategory.Game && _shortcutToEdit.GameAppId != null)
{
bool gameStillInstalled = false; bool gameStillInstalled = false;
foreach (ImageListViewItem gameItem in ilv_games.Items) foreach (ImageListViewItem gameItem in ilv_games.Items)
{ {
@ -1154,12 +1248,16 @@ namespace DisplayMagician.UIForms
} }
if (!gameStillInstalled) if (!gameStillInstalled)
{ {
// Close the splash screen
CloseTheSplashScreen();
DialogResult result = MessageBox.Show( DialogResult result = MessageBox.Show(
$"This shortcut refers to the '{_shortcutToEdit.GameName}' game that was installed in your {_shortcutToEdit.GameLibrary.ToString("G")} library. This game is no longer installed, so the shortcut won't work. You either need to change the game used in the Shortcut to another installed game, or you need to install the game files on your computer again.", $"This shortcut refers to the '{_shortcutToEdit.GameName}' game that was installed in your {_shortcutToEdit.GameLibrary.ToString("G")} library. This game is no longer installed, so the shortcut won't work. You either need to change the game used in the Shortcut to another installed game, or you need to install the game files on your computer again.",
@"Game no longer exists", @"Game no longer exists",
MessageBoxButtons.OK, MessageBoxButtons.OK,
MessageBoxIcon.Exclamation); MessageBoxIcon.Exclamation);
} }
}
if (ProfileRepository.ContainsProfile(_shortcutToEdit.ProfileUUID)) if (ProfileRepository.ContainsProfile(_shortcutToEdit.ProfileUUID))
{ {
@ -1172,7 +1270,8 @@ namespace DisplayMagician.UIForms
// since the shortcut was last created, then we need to tell the user // since the shortcut was last created, then we need to tell the user
if (!chosenProfile.IsPossible) if (!chosenProfile.IsPossible)
{ {
// Close the splash screen
CloseTheSplashScreen();
MessageBox.Show( MessageBox.Show(
$"The '{chosenProfile.Name}' Display Profile used by this Shortcut still exists, but it isn't possible to use it right now. You can either change the Display Profile this Shortcut uses, or you can change your Displays to make the Display Profile valid again.", $"The '{chosenProfile.Name}' Display Profile used by this Shortcut still exists, but it isn't possible to use it right now. You can either change the Display Profile this Shortcut uses, or you can change your Displays to make the Display Profile valid again.",
@"Display Profile isn't possible now", @"Display Profile isn't possible now",
@ -1183,11 +1282,10 @@ namespace DisplayMagician.UIForms
} }
}
if (!foundChosenProfileInLoadedProfiles && !String.IsNullOrWhiteSpace(_shortcutToEdit.ProfileUUID)) if (!foundChosenProfileInLoadedProfiles && !String.IsNullOrWhiteSpace(_shortcutToEdit.ProfileUUID))
{ {
// Close the splash screen
CloseTheSplashScreen();
MessageBox.Show( MessageBox.Show(
@"The Display Profile used by this Shortcut no longer exists and cannot be used. You need to choose a new Display Profile for this Shortcut.", @"The Display Profile used by this Shortcut no longer exists and cannot be used. You need to choose a new Display Profile for this Shortcut.",
@"Display Profile no longer exists", @"Display Profile no longer exists",
@ -1196,10 +1294,30 @@ namespace DisplayMagician.UIForms
} }
// If we get to the end of the loaded profiles and haven't // If we get to the end of the loaded profiles and haven't
// found a matching profile, then we need to show the current profile // found a matching profile, then we need to show the current profile
// that we're running now // that we're running now (only if that's been saved)
else if (!foundChosenProfileInLoadedProfiles && ProfileRepository.ProfileCount > 0) else if (!foundChosenProfileInLoadedProfiles && ProfileRepository.ProfileCount > 0)
{ {
chosenProfile = ProfileRepository.GetActiveProfile(); ; ProfileItem currentProfile = ProfileRepository.GetActiveProfile();
bool foundCurrentProfile = false;
foreach(ProfileItem profileToCheck in ProfileRepository.AllProfiles)
{
if (profileToCheck.Equals(currentProfile))
{
chosenProfile = currentProfile;
foundCurrentProfile = true;
}
}
// If we get here, and we still haven't matched the profile, then just pick the first one
if (!foundCurrentProfile)
{
if (ProfileRepository.ProfileCount > 0)
{
chosenProfile = ProfileRepository.AllProfiles[0];
}
}
} }
@ -1264,13 +1382,12 @@ namespace DisplayMagician.UIForms
cb_wait_alternative_game.Checked = false; cb_wait_alternative_game.Checked = false;
} }
// Set the launcher items if we have them // Set the launcher items if we have them
if (_shortcutToEdit.GameLibrary.Equals(SupportedGameLibraryType.Unknown)) if (_shortcutToEdit.GameLibrary.Equals(SupportedGameLibraryType.Unknown))
{ {
if (DisplayMagician.GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries.Count <= 0) if (DisplayMagician.GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries.Count <= 0)
{ {
// Fill in the game library information to highliught there isn't one detected. // Fill in the game library information to highlight there isn't one detected.
_gameLauncher = "None detected"; _gameLauncher = "None detected";
txt_game_name.Text = "No supported game libraries detected"; txt_game_name.Text = "No supported game libraries detected";
txt_args_game.Text = ""; txt_args_game.Text = "";
@ -1300,6 +1417,7 @@ namespace DisplayMagician.UIForms
} }
} }
// Set the autoname checkbox
cb_autosuggest.Checked = _shortcutToEdit.AutoName; cb_autosuggest.Checked = _shortcutToEdit.AutoName;
// Set the executable items if we have them // Set the executable items if we have them
@ -1318,17 +1436,192 @@ namespace DisplayMagician.UIForms
if (_shortcutToEdit.ProcessNameToMonitorUsesExecutable) if (_shortcutToEdit.ProcessNameToMonitorUsesExecutable)
{ {
rb_wait_executable.Checked = true; rb_wait_executable.Checked = true;
//rb_wait_alternative_executable.Checked = false;
} }
else else
{ {
//rb_wait_executable.Checked = false;
rb_wait_alternative_executable.Checked = true; rb_wait_alternative_executable.Checked = true;
} }
txt_alternative_executable.Text = _shortcutToEdit.DifferentExecutableToMonitor; txt_alternative_executable.Text = _shortcutToEdit.DifferentExecutableToMonitor;
// Set the shortcut name // Set the shortcut name
if (_editingExistingShortcut)
{
txt_shortcut_save_name.Text = _shortcutToEdit.Name; txt_shortcut_save_name.Text = _shortcutToEdit.Name;
}
// Set the selected image and available images (originalBitmap is set during shortcut update)
if (_editingExistingShortcut)
{
//ShortcutBitmap defaultBitmap = new ShortcutBitmap();
// Check if AvailableImages have been set, because if not, then we need to 'upgrade' the image structure
// To use this new way of working
if (_shortcutToEdit.AvailableImages.Count > 0)
{
_selectedImage = _shortcutToEdit.SelectedImage;
_availableImages = _shortcutToEdit.AvailableImages;
if (_shortcutToEdit.Category == ShortcutCategory.Game)
{
pb_game_icon.Image = _shortcutToEdit.SelectedImage.Image;
btn_choose_game_icon.Enabled = true;
}
else if (_shortcutToEdit.Category == ShortcutCategory.Application)
{
pb_exe_icon.Image = _shortcutToEdit.SelectedImage.Image;
btn_choose_exe_icon.Enabled = true;
}
}
else
{
// if there aren't any available images, then we need to find some!
if (_shortcutToEdit.Category == ShortcutCategory.Game)
{
// If this is a shortcut we're editing
_availableImages = new List<ShortcutBitmap>();
// If the game is selected, then grab images from the game
if (shortcutGame != null)
{
_availableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(shortcutGame.IconPath));
if (shortcutGame.ExePath != shortcutGame.IconPath)
{
_availableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(shortcutGame.ExePath));
}
}
// If the different exe to monitor is set, then grab the icons from there too!
if (!String.IsNullOrWhiteSpace(_shortcutToEdit.DifferentGameExeToMonitor) && File.Exists(_shortcutToEdit.DifferentGameExeToMonitor))
{
_availableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(_shortcutToEdit.DifferentGameExeToMonitor));
}
// If we still don't have any availableImages, then we need to add some emergency replacements!
if (_availableImages.Count == 0)
{
if (_shortcutToEdit.GameLibrary == SupportedGameLibraryType.Steam)
{
logger.Trace($"ShortcutForm/ShortcutForm_Load: Using the Steam icon as the icon instead.");
ShortcutBitmap bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Steam, "Steam Icon", "", 0);
_availableImages.Add(bm);
}
else if (_shortcutToEdit.GameLibrary == SupportedGameLibraryType.Uplay)
{
logger.Trace($"ShortcutForm/ShortcutForm_Load: Using the Uplay icon as the icon instead.");
ShortcutBitmap bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Uplay, "Uplay Icon", "", 0);
_availableImages.Add(bm);
}
else if (_shortcutToEdit.GameLibrary == SupportedGameLibraryType.Origin)
{
logger.Trace($"ShortcutForm/ShortcutForm_Load: Using the Origin icon as the icon instead.");
ShortcutBitmap bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Origin, "Origin Icon", "", 0);
_availableImages.Add(bm);
}
else if (_shortcutToEdit.GameLibrary == SupportedGameLibraryType.Epic)
{
logger.Trace($"ShortcutForm/ShortcutForm_Load: Using the Epic icon as the icon instead.");
ShortcutBitmap bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.Epic, "Epic Icon", "", 0);
_availableImages.Add(bm);
}
else if (_shortcutToEdit.GameLibrary == SupportedGameLibraryType.GOG)
{
logger.Trace($"ShortcutForm/ShortcutForm_Load: Using the GOG icon as the icon instead.");
ShortcutBitmap bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.GOG, "GOG Icon", "", 0);
_availableImages.Add(bm);
}
else
{
logger.Trace($"ShortcutForm/ShortcutForm_Load: Unknown Game Library, so using the DisplayMagician icon as the icon instead.");
ShortcutBitmap bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.DisplayMagician.ToBitmap(), "DisplayMagician Icon", "", 0);
_availableImages.Add(bm);
}
}
bool matchedImage = false;
if (_shortcutToEdit.OriginalLargeBitmap != null)
{
// go through available images and match the one we had
foreach (ShortcutBitmap sc in _availableImages)
{
if (ImageUtils.ImagesAreEqual(sc.Image, _shortcutToEdit.OriginalLargeBitmap))
{
// We've found the original image!
_selectedImage = sc;
pb_game_icon.Image = _selectedImage.Image;
matchedImage = true;
break;
}
}
}
if (!matchedImage)
{
_selectedImage = ImageUtils.GetMeLargestAvailableBitmap(_availableImages);
pb_game_icon.Image = _selectedImage.Image;
}
if (_shortcutToEdit.OriginalLargeBitmap != null)
{
btn_choose_game_icon.Enabled = true;
}
}
else if (_shortcutToEdit.Category == ShortcutCategory.Application)
{
// If this is a shortcut we're editing
_availableImages = new List<ShortcutBitmap>();
// If the exe is selected, then grab images from the exe
_availableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(_shortcutToEdit.ExecutableNameAndPath));
// If the different exe to monitor is set, then grab the icons from there too!
if (!String.IsNullOrWhiteSpace(_shortcutToEdit.DifferentExecutableToMonitor) && File.Exists(_shortcutToEdit.DifferentExecutableToMonitor))
{
_availableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(_shortcutToEdit.DifferentExecutableToMonitor));
}
if (_availableImages.Count == 0)
{
logger.Trace($"ShortcutForm/ShortcutForm_Load: Unknown Game Library, so using the DisplayMagician icon as the icon instead.");
ShortcutBitmap bm = ImageUtils.CreateShortcutBitmap(Properties.Resources.DisplayMagician.ToBitmap(), "DisplayMagician Icon", "", 0);
_availableImages.Add(bm);
}
bool matchedImage = false;
if (_shortcutToEdit.OriginalLargeBitmap != null)
{
// go through available images and match the one we had
foreach (ShortcutBitmap sc in _availableImages)
{
if (ImageUtils.ImagesAreEqual(sc.Image, _shortcutToEdit.OriginalLargeBitmap))
{
// We've found the original image!
_selectedImage = sc;
pb_game_icon.Image = _selectedImage.Image;
matchedImage = true;
}
}
}
if (!matchedImage)
{
_selectedImage = ImageUtils.GetMeLargestAvailableBitmap(_availableImages);
pb_game_icon.Image = _selectedImage.Image;
}
if (_shortcutToEdit.OriginalLargeBitmap != null)
{
btn_choose_exe_icon.Enabled = true;
}
}
}
}
else
{
// We're editing a new shortcut, so no game or anything selected
ilv_games.ClearSelection();
}
// Set up the start programs // Set up the start programs
if (_shortcutToEdit.StartPrograms is List<StartProgram> && _shortcutToEdit.StartPrograms.Count > 0) if (_shortcutToEdit.StartPrograms is List<StartProgram> && _shortcutToEdit.StartPrograms.Count > 0)
@ -1348,7 +1641,7 @@ namespace DisplayMagician.UIForms
continue; continue;
} }
StartProgramControl startProgramControl = new StartProgramControl(myStartProgram,spOrder); StartProgramControl startProgramControl = new StartProgramControl(myStartProgram, spOrder);
startProgramControl.Dock = DockStyle.None; startProgramControl.Dock = DockStyle.None;
if (spOrder == 1) if (spOrder == 1)
{ {
@ -1368,6 +1661,22 @@ namespace DisplayMagician.UIForms
} }
} }
// Setup the single stop program we're beginning with
if (_shortcutToEdit.StopPrograms is List<StopProgram> && _shortcutToEdit.StopPrograms.Count > 0)
{
cb_run_cmd_afterwards.Checked = true;
txt_run_cmd_afterwards.Text = _shortcutToEdit.StopPrograms[0].Executable;
if (_shortcutToEdit.StopPrograms[0].ExecutableArgumentsRequired)
{
cb_run_cmd_afterwards_args.Checked = true;
txt_run_cmd_afterwards_args.Text = _shortcutToEdit.StopPrograms[0].Arguments;
}
}
else
{
cb_run_cmd_afterwards.Checked = false;
}
// Refresh the Shortcut UI // Refresh the Shortcut UI
RefreshShortcutUI(); RefreshShortcutUI();
ChangeSelectedProfile(chosenProfile); ChangeSelectedProfile(chosenProfile);
@ -1381,6 +1690,37 @@ namespace DisplayMagician.UIForms
} }
private void ShortcutForm_Load(object sender, EventArgs e)
{
if (_firstShow)
{
// Parse the game bitmaps now the first time as we need them
// We need to add a refresh button to the shortcut page now!
if (!GameLibraries.GameLibrary.GamesImagesLoaded)
{
GameLibraries.GameLibrary.RefreshGameBitmaps();
}
// Close the splash screen
CloseTheSplashScreen();
_firstShow = false;
}
// Load the shortcut info
LoadShortcut();
}
private void CloseTheSplashScreen()
{
// Close the splash screen
if (ProgramSettings.LoadSettings().ShowSplashScreen && Program.AppSplashScreen != null && !Program.AppSplashScreen.Disposing && !Program.AppSplashScreen.IsDisposed)
Program.AppSplashScreen.Invoke(new Action(() => Program.AppSplashScreen.Close()));
this.TopMost = true;
this.Focus();
this.TopMost = false;
}
private void rb_standalone_CheckedChanged(object sender, EventArgs e) private void rb_standalone_CheckedChanged(object sender, EventArgs e)
{ {
if (rb_standalone.Checked) if (rb_standalone.Checked)
@ -1395,6 +1735,14 @@ namespace DisplayMagician.UIForms
// Disable the Game Panel // Disable the Game Panel
p_game.Enabled = false; p_game.Enabled = false;
// Empty the bitmaps
EmptyTheImages();
if (!String.IsNullOrWhiteSpace(txt_executable.Text) && File.Exists(txt_executable.Text))
{
UpdateExeImagesUI();
}
SuggestShortcutName(); SuggestShortcutName();
EnableSaveButtonIfValid(); EnableSaveButtonIfValid();
} }
@ -1415,12 +1763,23 @@ namespace DisplayMagician.UIForms
// Disable the Standalone Panel // Disable the Standalone Panel
p_standalone.Enabled = false; p_standalone.Enabled = false;
// Empty the bitmaps
EmptyTheImages();
SuggestShortcutName(); SuggestShortcutName();
EnableSaveButtonIfValid(); EnableSaveButtonIfValid();
} }
} }
private void EmptyTheImages()
{
_availableImages.Clear();
_selectedImage = new ShortcutBitmap();
pb_exe_icon.Image = null;
pb_game_icon.Image = null;
}
private void rb_no_game_CheckedChanged(object sender, EventArgs e) private void rb_no_game_CheckedChanged(object sender, EventArgs e)
{ {
if (rb_no_game.Checked) if (rb_no_game.Checked)
@ -1488,6 +1847,9 @@ namespace DisplayMagician.UIForms
private void btn_choose_alternative_executable_Click(object sender, EventArgs e) private void btn_choose_alternative_executable_Click(object sender, EventArgs e)
{ {
dialog_open.InitialDirectory = Path.GetDirectoryName(_executableToUse.ExecutableNameAndPath);
dialog_open.DefaultExt = "*.exe";
dialog_open.Filter = "exe files (*.exe;*.com) | *.exe;*.com | All files(*.*) | *.*";
if (dialog_open.ShowDialog(this) == DialogResult.OK) if (dialog_open.ShowDialog(this) == DialogResult.OK)
{ {
if (_loadedShortcut) if (_loadedShortcut)
@ -1615,7 +1977,7 @@ namespace DisplayMagician.UIForms
private void btn_back_Click(object sender, EventArgs e) private void btn_back_Click(object sender, EventArgs e)
{ {
this.Close(); this.Hide();
} }
private void radiobutton_Paint(object sender, PaintEventArgs e) private void radiobutton_Paint(object sender, PaintEventArgs e)
@ -1731,6 +2093,21 @@ namespace DisplayMagician.UIForms
private void btn_exe_to_start_Click(object sender, EventArgs e) private void btn_exe_to_start_Click(object sender, EventArgs e)
{ {
txt_executable.Text = getExeFile(); txt_executable.Text = getExeFile();
UpdateExeImagesUI();
}
private void UpdateExeImagesUI()
{
_availableImages = new List<ShortcutBitmap>();
_availableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(txt_executable.Text));
if (rb_wait_alternative_executable.Checked && File.Exists(txt_alternative_executable.Text))
{
_availableImages.AddRange(ImageUtils.GetMeAllBitmapsFromFile(txt_alternative_executable.Text));
}
_selectedImage = ImageUtils.GetMeLargestAvailableBitmap(_availableImages);
_shortcutToEdit.SelectedImage = _selectedImage;
pb_exe_icon.Image = _selectedImage.Image;
btn_choose_exe_icon.Enabled = true;
} }
private void txt_shortcut_save_name_Click(object sender, EventArgs e) private void txt_shortcut_save_name_Click(object sender, EventArgs e)
@ -1756,6 +2133,9 @@ namespace DisplayMagician.UIForms
private string getExeFile() private string getExeFile()
{ {
dialog_open.InitialDirectory = Environment.SpecialFolder.ProgramFiles.ToString();
dialog_open.DefaultExt = "*.exe";
dialog_open.Filter = "exe files (*.exe;*.com) | *.exe;*.com | All files(*.*) | *.*";
string textToReturn = ""; string textToReturn = "";
if (dialog_open.ShowDialog(this) == DialogResult.OK) if (dialog_open.ShowDialog(this) == DialogResult.OK)
{ {
@ -2166,6 +2546,19 @@ namespace DisplayMagician.UIForms
private void btn_choose_alternative_game_Click(object sender, EventArgs e) private void btn_choose_alternative_game_Click(object sender, EventArgs e)
{ {
string gamePath = "";
foreach (Game game in DisplayMagician.GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries)
{
if (game.Name == txt_game_name.Text)
{
gamePath = game.Directory;
break;
}
}
dialog_open.InitialDirectory = gamePath;
dialog_open.DefaultExt = "*.exe";
dialog_open.Filter = "exe files (*.exe;*.com) | *.exe;*.com | All files(*.*) | *.*";
if (dialog_open.ShowDialog(this) == DialogResult.OK) if (dialog_open.ShowDialog(this) == DialogResult.OK)
{ {
if (_loadedShortcut) if (_loadedShortcut)
@ -2383,8 +2776,6 @@ namespace DisplayMagician.UIForms
} }
private void ilv_games_ItemClick(object sender, ItemClickEventArgs e) private void ilv_games_ItemClick(object sender, ItemClickEventArgs e)
{
if (ilv_games.SelectedItems.Count > 0)
{ {
txt_game_name.Text = e.Item.Text; txt_game_name.Text = e.Item.Text;
foreach (Game game in DisplayMagician.GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries) foreach (Game game in DisplayMagician.GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries)
@ -2396,8 +2787,13 @@ namespace DisplayMagician.UIForms
_gameLauncher = game.GameLibrary.ToString("G"); _gameLauncher = game.GameLibrary.ToString("G");
lbl_game_library.Text = $"Game Library: {_gameLauncher}"; lbl_game_library.Text = $"Game Library: {_gameLauncher}";
_gameId = game.Id; _gameId = game.Id;
_availableImages = game.AvailableGameBitmaps;
} _shortcutToEdit.AvailableImages = game.AvailableGameBitmaps;
_selectedImage = ImageUtils.GetMeLargestAvailableBitmap(_availableImages);
_shortcutToEdit.SelectedImage = _selectedImage;
pb_game_icon.Image = _selectedImage.Image;
btn_choose_game_icon.Enabled = true;
break;
} }
} }
@ -2405,11 +2801,6 @@ namespace DisplayMagician.UIForms
EnableSaveButtonIfValid(); EnableSaveButtonIfValid();
} }
private void tabc_shortcut_VisibleChanged(object sender, EventArgs e)
{
if (tabc_shortcut.Visible == true)
SelectGameInImageListView();
}
private void btn_find_examples_startprograms_Click(object sender, EventArgs e) private void btn_find_examples_startprograms_Click(object sender, EventArgs e)
{ {
@ -2434,6 +2825,111 @@ namespace DisplayMagician.UIForms
return lightBitmap; return lightBitmap;
} }
} }
private void btn_choose_exe_icon_Click(object sender, EventArgs e)
{
if (rb_standalone.Checked && _availableImages.Count > 0)
{
ChooseImageForm exeIconForm = new ChooseImageForm();
exeIconForm.AvailableImages = _availableImages;
exeIconForm.SelectedImage = _selectedImage;
if (exeIconForm.ShowDialog() == DialogResult.OK)
{
_availableImages = exeIconForm.AvailableImages;
_selectedImage = exeIconForm.SelectedImage;
pb_exe_icon.Image = exeIconForm.SelectedImage.Image;
}
}
}
private void btn_choose_game_icon_Click(object sender, EventArgs e)
{
if (rb_launcher.Checked && _shortcutToEdit.AvailableImages.Count > 0)
{
ChooseImageForm gameIconForm = new ChooseImageForm();
gameIconForm.AvailableImages = _availableImages;
gameIconForm.SelectedImage = _selectedImage;
if (gameIconForm.ShowDialog() == DialogResult.OK)
{
_availableImages = gameIconForm.AvailableImages;
_selectedImage = gameIconForm.SelectedImage;
pb_game_icon.Image = gameIconForm.SelectedImage.Image;
}
}
}
private void cb_run_cmd_afterwards_CheckedChanged(object sender, EventArgs e)
{
if (cb_run_cmd_afterwards.Checked)
{
txt_run_cmd_afterwards.Enabled = true;
btn_run_cmd_afterwards.Enabled = true;
}
else
{
txt_run_cmd_afterwards.Enabled = false;
btn_run_cmd_afterwards.Enabled = false;
}
}
private void cb_run_cmd_afterwards_args_CheckedChanged(object sender, EventArgs e)
{
if (cb_run_cmd_afterwards_args.Checked)
{
txt_run_cmd_afterwards_args.Enabled = true;
}
else
{
txt_run_cmd_afterwards_args.Enabled = false;
}
}
private void btn_run_cmd_afterwards_Click(object sender, EventArgs e)
{
txt_run_cmd_afterwards.Text = getExeFile();
}
private void btn_refresh_games_list_Click(object sender, EventArgs e)
{
// Change the mouse crusor so the user knows something is happening
this.Cursor = Cursors.WaitCursor;
// Empty the games list
GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries.Clear();
// Load all the new games
GameLibraries.GameLibrary.LoadGamesInBackground();
// Parse the libraries
GameLibraries.GameLibrary.RefreshGameBitmaps();
// Load all the Games into the Games ListView
ilv_games.Items.Clear();
foreach (var game in DisplayMagician.GameLibraries.GameLibrary.AllInstalledGamesInAllLibraries.OrderBy(game => game.Name))
{
// Add the game to the game array
ImageListViewItem newItem = new ImageListViewItem(game, game.Name);
if (_editingExistingShortcut && game.Name.Equals(_shortcutToEdit.GameName))
{
newItem.Selected = true;
}
ilv_games.Items.Add(newItem, _gameAdaptor);
}
// Make sure that if the item is selected that it's visible
if (ilv_games.SelectedItems.Count > 0)
{
int selectedIndex = ilv_games.SelectedItems[0].Index;
ilv_games.EnsureVisible(selectedIndex);
}
// Change the user cursor back
this.Cursor = Cursors.Default;
// Show we're done
MessageBox.Show(
@"The list of available games has been updated.",
@"Games List Updated",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
} }
// Class used to populate combo boxes // Class used to populate combo boxes

View File

@ -45,10 +45,12 @@
this.tsmi_edit = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi_edit = new System.Windows.Forms.ToolStripMenuItem();
this.tsmi_run = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi_run = new System.Windows.Forms.ToolStripMenuItem();
this.tsmi_save_to_desktop = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi_save_to_desktop = new System.Windows.Forms.ToolStripMenuItem();
this.tsmi_copy = new System.Windows.Forms.ToolStripMenuItem();
this.tsmi_delete = new System.Windows.Forms.ToolStripMenuItem(); this.tsmi_delete = new System.Windows.Forms.ToolStripMenuItem();
this.lbl_mask = new System.Windows.Forms.Label(); this.lbl_mask = new System.Windows.Forms.Label();
this.btn_help = new System.Windows.Forms.Button(); this.btn_help = new System.Windows.Forms.Button();
this.btn_donate = new System.Windows.Forms.Button(); this.btn_donate = new System.Windows.Forms.Button();
this.btn_copy = new System.Windows.Forms.Button();
this.cms_shortcuts.SuspendLayout(); this.cms_shortcuts.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
@ -82,7 +84,7 @@
this.btn_delete.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_delete.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_delete.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_delete.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_delete.ForeColor = System.Drawing.Color.White; this.btn_delete.ForeColor = System.Drawing.Color.White;
this.btn_delete.Location = new System.Drawing.Point(462, 643); this.btn_delete.Location = new System.Drawing.Point(377, 643);
this.btn_delete.Name = "btn_delete"; this.btn_delete.Name = "btn_delete";
this.btn_delete.Size = new System.Drawing.Size(120, 40); this.btn_delete.Size = new System.Drawing.Size(120, 40);
this.btn_delete.TabIndex = 26; this.btn_delete.TabIndex = 26;
@ -115,7 +117,7 @@
this.btn_run.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_run.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_run.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_run.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_run.ForeColor = System.Drawing.Color.White; this.btn_run.ForeColor = System.Drawing.Color.White;
this.btn_run.Location = new System.Drawing.Point(588, 643); this.btn_run.Location = new System.Drawing.Point(629, 643);
this.btn_run.Name = "btn_run"; this.btn_run.Name = "btn_run";
this.btn_run.Size = new System.Drawing.Size(120, 40); this.btn_run.Size = new System.Drawing.Size(120, 40);
this.btn_run.TabIndex = 25; this.btn_run.TabIndex = 25;
@ -132,7 +134,7 @@
this.btn_edit.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_edit.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_edit.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_edit.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_edit.ForeColor = System.Drawing.Color.White; this.btn_edit.ForeColor = System.Drawing.Color.White;
this.btn_edit.Location = new System.Drawing.Point(336, 643); this.btn_edit.Location = new System.Drawing.Point(251, 643);
this.btn_edit.Name = "btn_edit"; this.btn_edit.Name = "btn_edit";
this.btn_edit.Size = new System.Drawing.Size(120, 40); this.btn_edit.Size = new System.Drawing.Size(120, 40);
this.btn_edit.TabIndex = 28; this.btn_edit.TabIndex = 28;
@ -149,7 +151,7 @@
this.btn_new.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_new.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_new.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_new.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_new.ForeColor = System.Drawing.Color.White; this.btn_new.ForeColor = System.Drawing.Color.White;
this.btn_new.Location = new System.Drawing.Point(210, 643); this.btn_new.Location = new System.Drawing.Point(125, 643);
this.btn_new.Name = "btn_new"; this.btn_new.Name = "btn_new";
this.btn_new.Size = new System.Drawing.Size(120, 40); this.btn_new.Size = new System.Drawing.Size(120, 40);
this.btn_new.TabIndex = 29; this.btn_new.TabIndex = 29;
@ -166,7 +168,7 @@
this.btn_save.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.btn_save.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_save.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.btn_save.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_save.ForeColor = System.Drawing.Color.White; this.btn_save.ForeColor = System.Drawing.Color.White;
this.btn_save.Location = new System.Drawing.Point(714, 643); this.btn_save.Location = new System.Drawing.Point(755, 643);
this.btn_save.Name = "btn_save"; this.btn_save.Name = "btn_save";
this.btn_save.Size = new System.Drawing.Size(211, 40); this.btn_save.Size = new System.Drawing.Size(211, 40);
this.btn_save.TabIndex = 30; this.btn_save.TabIndex = 30;
@ -214,9 +216,10 @@
this.tsmi_edit, this.tsmi_edit,
this.tsmi_run, this.tsmi_run,
this.tsmi_save_to_desktop, this.tsmi_save_to_desktop,
this.tsmi_copy,
this.tsmi_delete}); this.tsmi_delete});
this.cms_shortcuts.Name = "cms_shortcuts"; this.cms_shortcuts.Name = "cms_shortcuts";
this.cms_shortcuts.Size = new System.Drawing.Size(216, 92); this.cms_shortcuts.Size = new System.Drawing.Size(216, 114);
// //
// tsmi_edit // tsmi_edit
// //
@ -241,6 +244,13 @@
this.tsmi_save_to_desktop.Text = "Save Shortcut to Desktop..."; this.tsmi_save_to_desktop.Text = "Save Shortcut to Desktop...";
this.tsmi_save_to_desktop.Click += new System.EventHandler(this.tsmi_save_to_desktop_Click); this.tsmi_save_to_desktop.Click += new System.EventHandler(this.tsmi_save_to_desktop_Click);
// //
// tsmi_copy
//
this.tsmi_copy.Name = "tsmi_copy";
this.tsmi_copy.Size = new System.Drawing.Size(215, 22);
this.tsmi_copy.Text = "Copy Shortcut...";
this.tsmi_copy.Click += new System.EventHandler(this.tsmi_copy_Click);
//
// tsmi_delete // tsmi_delete
// //
this.tsmi_delete.Name = "tsmi_delete"; this.tsmi_delete.Name = "tsmi_delete";
@ -296,6 +306,23 @@
this.btn_donate.UseVisualStyleBackColor = true; this.btn_donate.UseVisualStyleBackColor = true;
this.btn_donate.Click += new System.EventHandler(this.btn_donate_Click); this.btn_donate.Click += new System.EventHandler(this.btn_donate_Click);
// //
// btn_copy
//
this.btn_copy.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
this.btn_copy.BackColor = System.Drawing.Color.Black;
this.btn_copy.FlatAppearance.MouseDownBackColor = System.Drawing.Color.IndianRed;
this.btn_copy.FlatAppearance.MouseOverBackColor = System.Drawing.Color.Brown;
this.btn_copy.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_copy.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_copy.ForeColor = System.Drawing.Color.White;
this.btn_copy.Location = new System.Drawing.Point(503, 643);
this.btn_copy.Name = "btn_copy";
this.btn_copy.Size = new System.Drawing.Size(120, 40);
this.btn_copy.TabIndex = 36;
this.btn_copy.Text = "&Copy";
this.btn_copy.UseVisualStyleBackColor = false;
this.btn_copy.Click += new System.EventHandler(this.btn_copy_Click);
//
// ShortcutLibraryForm // ShortcutLibraryForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -303,6 +330,7 @@
this.BackColor = System.Drawing.Color.Black; this.BackColor = System.Drawing.Color.Black;
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage"))); this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.ClientSize = new System.Drawing.Size(1134, 716); this.ClientSize = new System.Drawing.Size(1134, 716);
this.Controls.Add(this.btn_copy);
this.Controls.Add(this.btn_donate); this.Controls.Add(this.btn_donate);
this.Controls.Add(this.btn_help); this.Controls.Add(this.btn_help);
this.Controls.Add(this.lbl_mask); this.Controls.Add(this.lbl_mask);
@ -353,5 +381,7 @@
private System.Windows.Forms.Label lbl_mask; private System.Windows.Forms.Label lbl_mask;
private System.Windows.Forms.Button btn_help; private System.Windows.Forms.Button btn_help;
private System.Windows.Forms.Button btn_donate; private System.Windows.Forms.Button btn_donate;
private System.Windows.Forms.Button btn_copy;
private System.Windows.Forms.ToolStripMenuItem tsmi_copy;
} }
} }

View File

@ -19,6 +19,7 @@ namespace DisplayMagician.UIForms
private ShortcutAdaptor _shortcutAdaptor = new ShortcutAdaptor(); private ShortcutAdaptor _shortcutAdaptor = new ShortcutAdaptor();
private ShortcutItem _selectedShortcut = null; private ShortcutItem _selectedShortcut = null;
private ShortcutForm _shortcutForm = null;
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public ShortcutLibraryForm() public ShortcutLibraryForm()
@ -47,6 +48,7 @@ namespace DisplayMagician.UIForms
// Refresh the Shortcut Library UI // Refresh the Shortcut Library UI
RefreshShortcutLibraryUI(); RefreshShortcutLibraryUI();
RemoveWarningIfShortcuts(); RemoveWarningIfShortcuts();
} }
@ -200,6 +202,7 @@ namespace DisplayMagician.UIForms
} }
catch (Exception ex) catch (Exception ex)
{ {
logger.Warn(ex, $"ShortcutLibraryForm/btn_save_Click: Exception saving shortcut to {dialog_save.FileName}.");
MessageBox.Show(ex.Message, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Warning); MessageBox.Show(ex.Message, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Warning);
} }
} }
@ -262,19 +265,31 @@ namespace DisplayMagician.UIForms
private void btn_new_Click(object sender, EventArgs e) private void btn_new_Click(object sender, EventArgs e)
{ {
this.Cursor = Cursors.WaitCursor; this.Cursor = Cursors.WaitCursor;
var shortcutForm = new ShortcutForm(new ShortcutItem()); ShortcutItem si = new ShortcutItem();
//ShortcutRepository.IsValidRefresh(); if (_shortcutForm == null)
shortcutForm.ShowDialog(this);
if (shortcutForm.DialogResult == DialogResult.OK)
{ {
ShortcutRepository.AddShortcut(shortcutForm.Shortcut); _shortcutForm = new ShortcutForm();
_selectedShortcut = shortcutForm.Shortcut; }
//ShortcutRepository.IsValidRefresh()
// Set the Shortcut to as a new shortcut
_shortcutForm.Shortcut = si;
_shortcutForm.EditingExistingShortcut = false;
_shortcutForm.ShowDialog(this);
if (_shortcutForm.DialogResult == DialogResult.OK)
{
ShortcutRepository.AddShortcut(_shortcutForm.Shortcut);
_selectedShortcut = _shortcutForm.Shortcut;
//ShortcutRepository.IsValidRefresh(); //ShortcutRepository.IsValidRefresh();
RefreshShortcutLibraryUI(); RefreshShortcutLibraryUI();
} }
this.Cursor = Cursors.Default; this.Cursor = Cursors.Default;
RemoveWarningIfShortcuts(); RemoveWarningIfShortcuts();
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
} }
private void btn_edit_Click(object sender, EventArgs e) private void btn_edit_Click(object sender, EventArgs e)
@ -306,14 +321,15 @@ namespace DisplayMagician.UIForms
this.Cursor = Cursors.WaitCursor; this.Cursor = Cursors.WaitCursor;
// We need to stop ImageListView redrawing things before we're ready if (_shortcutForm == null)
// This stops an exception when ILV is just too keen! {
_shortcutForm = new ShortcutForm();
}
var shortcutForm = new ShortcutForm(_selectedShortcut); _shortcutForm.Shortcut = _selectedShortcut;
_shortcutForm.EditingExistingShortcut = true;
//ilv_saved_shortcuts.SuspendLayout(); //ilv_saved_shortcuts.SuspendLayout();
shortcutForm.ShowDialog(this); _shortcutForm.ShowDialog(this);
if (shortcutForm.DialogResult == DialogResult.OK) if (_shortcutForm.DialogResult == DialogResult.OK)
{ {
RefreshShortcutLibraryUI(); RefreshShortcutLibraryUI();
// As this is an edit, we need to manually force saving the shortcut library // As this is an edit, we need to manually force saving the shortcut library
@ -321,6 +337,12 @@ namespace DisplayMagician.UIForms
} }
this.Cursor = Cursors.Default; this.Cursor = Cursors.Default;
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
} }
} }
@ -358,6 +380,12 @@ namespace DisplayMagician.UIForms
ShortcutRepository.IsValidRefresh(); ShortcutRepository.IsValidRefresh();
RefreshShortcutLibraryUI(); RefreshShortcutLibraryUI();
RemoveWarningIfShortcuts(); RemoveWarningIfShortcuts();
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
} }
private void btn_run_Click(object sender, EventArgs e) private void btn_run_Click(object sender, EventArgs e)
@ -430,6 +458,12 @@ namespace DisplayMagician.UIForms
lbl_mask.Visible = false; lbl_mask.Visible = false;
lbl_mask.SendToBack(); lbl_mask.SendToBack();
// 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_shortcuts_ItemHover(object sender, ItemHoverEventArgs e) private void ilv_saved_shortcuts_ItemHover(object sender, ItemHoverEventArgs e)
@ -472,6 +506,11 @@ namespace DisplayMagician.UIForms
btn_delete.PerformClick(); btn_delete.PerformClick();
} }
private void tsmi_copy_Click(object sender, EventArgs e)
{
btn_copy.PerformClick();
}
private void ShortcutLibraryForm_KeyPress(object sender, KeyPressEventArgs e) private void ShortcutLibraryForm_KeyPress(object sender, KeyPressEventArgs e)
{ {
if (lbl_mask.Visible == true) if (lbl_mask.Visible == true)
@ -495,5 +534,39 @@ namespace DisplayMagician.UIForms
string targetURL = @"https://github.com/sponsors/terrymacdonald"; string targetURL = @"https://github.com/sponsors/terrymacdonald";
System.Diagnostics.Process.Start(targetURL); System.Diagnostics.Process.Start(targetURL);
} }
private void btn_copy_Click(object sender, EventArgs e)
{
if (_selectedShortcut == null)
{
if (ShortcutRepository.ShortcutCount > 0)
{
MessageBox.Show(
@"You need to select a Game Shortcut in order to copy it. Please select a Game Shortcut then try again, or right-click on the Game Shortcut and select 'Copy Shortcut'.",
@"Select Game Shortcut", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
else
{
MessageBox.Show(
@"You need to create a Game Shortcut in order to copy it. Please create a Game Shortcut by clicking the New button.",
@"Create Game Shortcut", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
}
else
{
ShortcutItem copiedShortcut;
// Copy the shortcut
ShortcutRepository.CopyShortcut(_selectedShortcut, out copiedShortcut);
// Select the new copied shortcut
_selectedShortcut = copiedShortcut;
// Invalidate the list of shortcuts so it gets redrawn again with the copy included!
ilv_saved_shortcuts.Invalidate();
// Refresh the UI
RefreshShortcutLibraryUI();
}
}
} }
} }

59
DisplayMagician/Utils.cs Normal file
View File

@ -0,0 +1,59 @@
using System;
using System.Runtime.InteropServices;
namespace DisplayMagician
{
class Utils
{
// 1. Import InteropServices
/// 2. Declare DownloadsFolder KNOWNFOLDERID
private static Guid FolderDownloads = new Guid("374DE290-123F-4565-9164-39C4925E467B");
/// 3. Import SHGetKnownFolderPath method
/// <summary>
/// Retrieves the full path of a known folder identified by the folder's KnownFolderID.
/// </summary>
/// <param name="id">A KnownFolderID that identifies the folder.</param>
/// <param name="flags">Flags that specify special retrieval options. This value can be
/// 0; otherwise, one or more of the KnownFolderFlag values.</param>
/// <param name="token">An access token that represents a particular user. If this
/// parameter is NULL, which is the most common usage, the function requests the known
/// folder for the current user. Assigning a value of -1 indicates the Default User.
/// The default user profile is duplicated when any new user account is created.
/// Note that access to the Default User folders requires administrator privileges.
/// </param>
/// <param name="path">When this method returns, contains the address of a string that
/// specifies the path of the known folder. The returned path does not include a
/// trailing backslash.</param>
/// <returns>Returns S_OK if successful, or an error value otherwise.</returns>
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern int SHGetKnownFolderPath(ref Guid id, int flags, IntPtr token, out IntPtr path);
/// 4. Declare method that returns the Downloads Path as string
/// <summary>
/// Returns the absolute downloads directory specified on the system.
/// </summary>
/// <returns></returns>
public static string GetDownloadsPath()
{
if (Environment.OSVersion.Version.Major < 6) throw new NotSupportedException();
IntPtr pathPtr = IntPtr.Zero;
try
{
SHGetKnownFolderPath(ref FolderDownloads, 0, IntPtr.Zero, out pathPtr);
return Marshal.PtrToStringUni(pathPtr);
}
finally
{
Marshal.FreeCoTaskMem(pathPtr);
}
}
}
}

View File

@ -38,7 +38,7 @@
<IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath> <IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
<SuppressAllWarnings>False</SuppressAllWarnings> <SuppressAllWarnings>False</SuppressAllWarnings>
<Pedantic>True</Pedantic> <Pedantic>True</Pedantic>
<DefineConstants>DisplayMagicianFilesDir=H:\vscode-projects\DisplayMagician\DisplayMagician\bin\Debug</DefineConstants> <DefineConstants>DisplayMagicianFilesDir=H:\vscode-projects\DisplayMagician\DisplayMagician\bin\Release</DefineConstants>
<SuppressPdbOutput>True</SuppressPdbOutput> <SuppressPdbOutput>True</SuppressPdbOutput>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -37,9 +37,6 @@
</Component>--> </Component>-->
<Component Id="cmpE8833A8E220525D7707A018B20CEDD87" Guid="{EBC27E33-6C4D-4612-BC5F-AC0A09DDD636}"> <Component Id="cmpE8833A8E220525D7707A018B20CEDD87" Guid="{EBC27E33-6C4D-4612-BC5F-AC0A09DDD636}">
<File Id="filD4FD53D2FBF78674DBC3A1B446B44FC5" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\EDIDParser.dll" /> <File Id="filD4FD53D2FBF78674DBC3A1B446B44FC5" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\EDIDParser.dll" />
</Component>
<Component Id="cmp8C52E948AAB2D43DF696BB7D003A8E4B" Guid="{3C8F90C4-CFD5-4B8C-9929-44239BAECE94}">
<File Id="fil1965347683EC48A269FE672473CD04C2" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\HtmlAgilityPack.dll" />
</Component> </Component>
<Component Id="cmpC13B4DB64E30D03117ECF099B1A9CD9C" Guid="{36C70104-58C9-497D-9775-38E65F1809CD}"> <Component Id="cmpC13B4DB64E30D03117ECF099B1A9CD9C" Guid="{36C70104-58C9-497D-9775-38E65F1809CD}">
<File Id="fil44FFB513E29A8346713440B4BCB7F1E6" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\IconExtractor.dll" /> <File Id="fil44FFB513E29A8346713440B4BCB7F1E6" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\IconExtractor.dll" />
@ -77,11 +74,14 @@
<Component Id="cmpC64ECA245317593760E42F3996F3A079" Guid="{AF290F63-703E-441F-86C8-2F00DADDEF9B}"> <Component Id="cmpC64ECA245317593760E42F3996F3A079" Guid="{AF290F63-703E-441F-86C8-2F00DADDEF9B}">
<File Id="fil5299F0448E950048C778AA65A04FB498" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\NLog.dll" /> <File Id="fil5299F0448E950048C778AA65A04FB498" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\NLog.dll" />
</Component> </Component>
<Component Id="cmp8C52E948AAB2D43DF696BB7D003A8E45" Guid="{3C8F90C4-CFD5-4B8C-9929-44239BAECE95}">
<File Id="fil1965347683EC48A269FE672473CD04C5" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\protobuf-net.Core.dll" />
</Component>
<Component Id="cmp8C52E948AAB2D4F322455234322A8E45" Guid="{3D8F90C4-FFD5-4B8C-9929-42339BAECE91}">
<File Id="fil19658443928748A269FE672473CD04C5" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\protobuf-net.dll" />
</Component>
<Component Id="cmpBEC07D0236076B07E4E7D7632CCA69CB" Guid="{9845F3EB-281F-4BB1-9B88-5B5D454F0977}"> <Component Id="cmpBEC07D0236076B07E4E7D7632CCA69CB" Guid="{9845F3EB-281F-4BB1-9B88-5B5D454F0977}">
<File Id="fil8C59ED1965E9E2D1C82459BE5C1E903E" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\QueryString.NETCore.dll" /> <File Id="fil8C59ED1965E9E2D1C82459BE5C1E903E" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\QueryString.NETCore.dll" />
</Component>
<Component Id="cmpEA08258761850D4592B74F91B39654F1" Guid="{103CB41F-0C23-4760-AAF4-AA69AA31F1EF}">
<File Id="fil1176ED2D70EEF4480775A10F7BF79850" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.Drawing.Common.dll" />
</Component> </Component>
<Component Id="cmp7662BF27A2D0CB01FFDEAC0A17A0850E" Guid="{62327FF7-0212-4CAB-BCB1-DE54E5965A03}"> <Component Id="cmp7662BF27A2D0CB01FFDEAC0A17A0850E" Guid="{62327FF7-0212-4CAB-BCB1-DE54E5965A03}">
<File Id="fil657D20B233D2855D5699D59E41AC4E61" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.ValueTuple.dll" /> <File Id="fil657D20B233D2855D5699D59E41AC4E61" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.ValueTuple.dll" />
@ -100,6 +100,27 @@
</Component> </Component>
<Component Id="cmp27AC6F10258FA1C97C54F3C1D153D122" Guid="{06BD97C2-07D8-4032-842C-AD16D1560803}"> <Component Id="cmp27AC6F10258FA1C97C54F3C1D153D122" Guid="{06BD97C2-07D8-4032-842C-AD16D1560803}">
<File Id="fil01CF08752F4284E3144706F10481AEA0" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\WinFormAnimation.dll" /> <File Id="fil01CF08752F4284E3144706F10481AEA0" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\WinFormAnimation.dll" />
</Component>
<Component Id="cmp44AC6F10258FA1C97C54F3C1D153D144" Guid="{44BD9992-0448-4099-842C-AD16D1560899}">
<File Id="fil44CF08752F4284E3144706F10481AE45" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\YamlDotNet.dll" />
</Component>
<Component Id="cmp43CBAB0FA1E5BC0D644986539E5BD28B" Guid="{FD7CC9FD-92EF-4F11-B5DA-679406DBA52D}">
<File Id="fil43CBAB0FA1E5BC0D644986539E5BD285" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.Buffers.dll" />
</Component>
<Component Id="cmpD6046E9FF7D2F0974D390F769EBB3CCC" Guid="{833996F7-259F-4B67-B7FF-45688DBE2B0C}">
<File Id="filD6046E9FF7D2F0974D390F769EBB3CC5" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.Collections.Immutable.dll" />
</Component>
<Component Id="cmpA809EC8EFE6C9D623A9A9F81E98A4EB5" Guid="{29372F1B-0887-4249-A615-63A6521DB415}">
<File Id="filA809EC8EFE6C9D623A9A9F81E98A4EB6" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.Drawing.Common.dll" />
</Component>
<Component Id="cmpA16DB2114AB68F120A2C838F831673F5" Guid="{5BA436DA-DD32-48A7-BF9C-97F141B14BB8}">
<File Id="filA16DB2114AB68F120A2C838F831673F6" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.Memory.dll" />
</Component>
<Component Id="cmp1AA1063749A646E4F0503C0B23C95909" Guid="{455074F1-5079-428B-AAD7-9FCFD81F2715}">
<File Id="fil1AA1063749A646E4F0503C0B23C95905" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.Numerics.Vectors.dll" />
</Component>
<Component Id="cmpCDDE936F29A669E69BD66B25670BE7F3" Guid="{00378BE6-D93E-4751-AF47-4E82E22C6E70}">
<File Id="filCDDE936F29A669E69BD66B25670BE7F5" KeyPath="yes" Source="$(var.DisplayMagician.TargetDir)\System.Runtime.CompilerServices.Unsafe.dll" />
</Component> </Component>
</DirectoryRef> </DirectoryRef>
</Fragment> </Fragment>
@ -113,7 +134,6 @@
<ComponentRef Id="cmpCF520827D65E51E3ECB59344A6F528D0" /> <ComponentRef Id="cmpCF520827D65E51E3ECB59344A6F528D0" />
<ComponentRef Id="cmp936B3459EEFF10D8EB978B7DD0F9FDDA" /> <ComponentRef Id="cmp936B3459EEFF10D8EB978B7DD0F9FDDA" />
<ComponentRef Id="cmpE8833A8E220525D7707A018B20CEDD87" /> <ComponentRef Id="cmpE8833A8E220525D7707A018B20CEDD87" />
<ComponentRef Id="cmp8C52E948AAB2D43DF696BB7D003A8E4B" />
<ComponentRef Id="cmpC13B4DB64E30D03117ECF099B1A9CD9C" /> <ComponentRef Id="cmpC13B4DB64E30D03117ECF099B1A9CD9C" />
<ComponentRef Id="cmp6AB92D1CF70395670A490E1B9BD347DE" /> <ComponentRef Id="cmp6AB92D1CF70395670A490E1B9BD347DE" />
<ComponentRef Id="cmp23C6CFB3962E9B4E2A7E8EF56F20A99F" /> <ComponentRef Id="cmp23C6CFB3962E9B4E2A7E8EF56F20A99F" />
@ -124,7 +144,6 @@
<ComponentRef Id="cmpA7927FF61A499CF81EE0A562CDFEDEC7" /> <ComponentRef Id="cmpA7927FF61A499CF81EE0A562CDFEDEC7" />
<ComponentRef Id="cmpC64ECA245317593760E42F3996F3A079" /> <ComponentRef Id="cmpC64ECA245317593760E42F3996F3A079" />
<ComponentRef Id="cmpBEC07D0236076B07E4E7D7632CCA69CB" /> <ComponentRef Id="cmpBEC07D0236076B07E4E7D7632CCA69CB" />
<ComponentRef Id="cmpEA08258761850D4592B74F91B39654F1" />
<ComponentRef Id="cmp7662BF27A2D0CB01FFDEAC0A17A0850E" /> <ComponentRef Id="cmp7662BF27A2D0CB01FFDEAC0A17A0850E" />
<ComponentRef Id="cmpAE5FC77E7BB7E9CB720EF7CEE118A4CE" /> <ComponentRef Id="cmpAE5FC77E7BB7E9CB720EF7CEE118A4CE" />
<ComponentRef Id="cmpB76F273FDED5EA841158E966B403CFC8" /> <ComponentRef Id="cmpB76F273FDED5EA841158E966B403CFC8" />
@ -135,6 +154,15 @@
<ComponentRef Id="cmp5069692C1912B298A026EA30A3823321" /> <ComponentRef Id="cmp5069692C1912B298A026EA30A3823321" />
<ComponentRef Id="cmp27AC6F10258FA1C97C54F3C1D153D122" /> <ComponentRef Id="cmp27AC6F10258FA1C97C54F3C1D153D122" />
<ComponentRef Id="cmp357CADF3E913AADB2DD6BD3938986798" /> <ComponentRef Id="cmp357CADF3E913AADB2DD6BD3938986798" />
<ComponentRef Id="cmp8C52E948AAB2D43DF696BB7D003A8E45" />
<ComponentRef Id="cmp8C52E948AAB2D4F322455234322A8E45" />
<ComponentRef Id="cmp44AC6F10258FA1C97C54F3C1D153D144" />
<ComponentRef Id="cmp43CBAB0FA1E5BC0D644986539E5BD28B" />
<ComponentRef Id="cmpD6046E9FF7D2F0974D390F769EBB3CCC" />
<ComponentRef Id="cmpA809EC8EFE6C9D623A9A9F81E98A4EB5" />
<ComponentRef Id="cmpA16DB2114AB68F120A2C838F831673F5" />
<ComponentRef Id="cmp1AA1063749A646E4F0503C0B23C95909" />
<ComponentRef Id="cmpCDDE936F29A669E69BD66B25670BE7F3" />
</ComponentGroup> </ComponentGroup>
</Fragment> </Fragment>
</Wix> </Wix>

View File

@ -157,8 +157,6 @@ namespace DisplayMagicianShared.AMD
// .NET guarantees thread safety for static initialization // .NET guarantees thread safety for static initialization
private static AMDLibrary _instance = new AMDLibrary(); private static AMDLibrary _instance = new AMDLibrary();
private static WinLibrary _winLibrary = new WinLibrary();
private bool _initialised = false; private bool _initialised = false;
// To detect redundant calls // To detect redundant calls
@ -167,11 +165,12 @@ namespace DisplayMagicianShared.AMD
// Instantiate a SafeHandle instance. // Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true); private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
private IntPtr _adlContextHandle = IntPtr.Zero; private IntPtr _adlContextHandle = IntPtr.Zero;
private AMD_DISPLAY_CONFIG _activeDisplayConfig;
static AMDLibrary() { } static AMDLibrary() { }
public AMDLibrary() public AMDLibrary()
{ {
_activeDisplayConfig = CreateDefaultConfig();
try try
{ {
SharedLogger.logger.Trace($"AMDLibrary/AMDLibrary: Attempting to load the AMD ADL DLL {ADLImport.ATI_ADL_DLL}"); SharedLogger.logger.Trace($"AMDLibrary/AMDLibrary: Attempting to load the AMD ADL DLL {ADLImport.ATI_ADL_DLL}");
@ -193,6 +192,8 @@ namespace DisplayMagicianShared.AMD
{ {
_initialised = true; _initialised = true;
SharedLogger.logger.Trace($"AMDLibrary/AMDLibrary: AMD ADL2 library was initialised successfully"); 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();
} }
else else
{ {
@ -204,7 +205,6 @@ namespace DisplayMagicianShared.AMD
SharedLogger.logger.Trace(ex, $"AMDLibrary/AMDLibrary: Exception intialising AMD ADL2 library. ADL2_Main_Control_Create() caused an exception."); SharedLogger.logger.Trace(ex, $"AMDLibrary/AMDLibrary: Exception intialising AMD ADL2 library. ADL2_Main_Control_Create() caused an exception.");
} }
_winLibrary = WinLibrary.GetLibrary();
} }
catch (DllNotFoundException ex) catch (DllNotFoundException ex)
{ {
@ -274,6 +274,26 @@ namespace DisplayMagicianShared.AMD
} }
} }
public AMD_DISPLAY_CONFIG ActiveDisplayConfig
{
get
{
return _activeDisplayConfig;
}
set
{
_activeDisplayConfig = value;
}
}
public List<string> CurrentDisplayIdentifiers
{
get
{
return _activeDisplayConfig.DisplayIdentifiers;
}
}
public static AMDLibrary GetLibrary() public static AMDLibrary GetLibrary()
{ {
return _instance; return _instance;
@ -297,6 +317,22 @@ namespace DisplayMagicianShared.AMD
return myDefaultConfig; return myDefaultConfig;
} }
public bool UpdateActiveConfig()
{
SharedLogger.logger.Trace($"AMDLibrary/UpdateActiveConfig: Updating the currently active config");
try
{
_activeDisplayConfig = GetActiveConfig();
}
catch (Exception ex)
{
SharedLogger.logger.Trace(ex, $"AMDLibrary/UpdateActiveConfig: Exception updating the currently active config");
return false;
}
return true;
}
public AMD_DISPLAY_CONFIG GetActiveConfig() public AMD_DISPLAY_CONFIG GetActiveConfig()
{ {
SharedLogger.logger.Trace($"AMDLibrary/GetActiveConfig: Getting the currently active config"); SharedLogger.logger.Trace($"AMDLibrary/GetActiveConfig: Getting the currently active config");
@ -915,7 +951,7 @@ namespace DisplayMagicianShared.AMD
string stringToReturn = ""; string stringToReturn = "";
// Get the current config // Get the current config
AMD_DISPLAY_CONFIG displayConfig = GetActiveConfig(); AMD_DISPLAY_CONFIG displayConfig = ActiveDisplayConfig;
stringToReturn += $"****** AMD VIDEO CARDS *******\n"; stringToReturn += $"****** AMD VIDEO CARDS *******\n";
@ -1294,9 +1330,6 @@ namespace DisplayMagicianShared.AMD
// Set the initial state of the ADL_STATUS // Set the initial state of the ADL_STATUS
ADL_STATUS ADLRet = 0; ADL_STATUS ADLRet = 0;
// We want to get the current config
AMD_DISPLAY_CONFIG currentDisplayConfig = GetAMDDisplayConfig();
// set the display locations // set the display locations
if (displayConfig.SlsConfig.IsSlsEnabled) if (displayConfig.SlsConfig.IsSlsEnabled)
{ {
@ -1367,12 +1400,12 @@ namespace DisplayMagicianShared.AMD
// We need to change to a plain, non-Eyefinity (SLS) profile, so we need to disable any SLS Topologies if they are being used // We need to change to a plain, non-Eyefinity (SLS) profile, so we need to disable any SLS Topologies if they are being used
SharedLogger.logger.Trace($"AMDLibrary/SetActiveConfig: SLS is not used in the new display configuration, so we need to set it to disabled if it's configured currently"); SharedLogger.logger.Trace($"AMDLibrary/SetActiveConfig: SLS is not used in the new display configuration, so we need to set it to disabled if it's configured currently");
if (currentDisplayConfig.SlsConfig.IsSlsEnabled) if (ActiveDisplayConfig.SlsConfig.IsSlsEnabled)
{ {
// We need to disable the current Eyefinity (SLS) profile to turn it off // We need to disable the current Eyefinity (SLS) profile to turn it off
SharedLogger.logger.Trace($"AMDLibrary/SetActiveConfig: SLS is enabled in the current display configuration, so we need to turn it off"); SharedLogger.logger.Trace($"AMDLibrary/SetActiveConfig: SLS is enabled in the current display configuration, so we need to turn it off");
foreach (AMD_SLSMAP_CONFIG slsMapConfig in currentDisplayConfig.SlsConfig.SLSMapConfigs) foreach (AMD_SLSMAP_CONFIG slsMapConfig in ActiveDisplayConfig.SlsConfig.SLSMapConfigs)
{ {
// Turn off this SLS Map Config // Turn off this SLS Map Config
ADLRet = ADLImport.ADL2_Display_SLSMapConfig_SetState(_adlContextHandle, slsMapConfig.SLSMap.AdapterIndex, slsMapConfig.SLSMap.SLSMapIndex, ADLImport.ADL_FALSE); ADLRet = ADLImport.ADL2_Display_SLSMapConfig_SetState(_adlContextHandle, slsMapConfig.SLSMap.AdapterIndex, slsMapConfig.SLSMap.SLSMapIndex, ADLImport.ADL_FALSE);
@ -1391,48 +1424,6 @@ namespace DisplayMagicianShared.AMD
} }
// We want to set the AMD HDR settings now
// We got through each of the attached displays and set the HDR
// Go through each of the HDR configs we have
foreach (var hdrConfig in displayConfig.HdrConfigs)
{
// Try and find the HDR config displays in the list of currently connected displays
foreach (var displayInfoItem in currentDisplayConfig.DisplayTargets)
{
// If we find the HDR config display in the list of currently connected displays then try to set the HDR setting we recorded earlier
if (hdrConfig.Key == displayInfoItem.DisplayID.DisplayLogicalIndex)
{
if (hdrConfig.Value.HDREnabled)
{
ADLRet = ADLImport.ADL2_Display_HDRState_Set(_adlContextHandle, hdrConfig.Value.AdapterIndex, displayInfoItem.DisplayID, 1);
if (ADLRet == ADL_STATUS.ADL_OK)
{
SharedLogger.logger.Trace($"AMDLibrary/GetAMDDisplayConfig: ADL2_Display_HDRState_Set was able to turn on HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
else
{
SharedLogger.logger.Error($"AMDLibrary/GetAMDDisplayConfig: ADL2_Display_HDRState_Set was NOT able to turn on HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
}
else
{
ADLRet = ADLImport.ADL2_Display_HDRState_Set(_adlContextHandle, hdrConfig.Value.AdapterIndex, displayInfoItem.DisplayID, 0);
if (ADLRet == ADL_STATUS.ADL_OK)
{
SharedLogger.logger.Trace($"AMDLibrary/GetAMDDisplayConfig: ADL2_Display_HDRState_Set was able to turn off HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
else
{
SharedLogger.logger.Error($"AMDLibrary/GetAMDDisplayConfig: ADL2_Display_HDRState_Set was NOT able to turn off HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
}
break;
}
}
}
} }
else else
{ {
@ -1443,15 +1434,80 @@ namespace DisplayMagicianShared.AMD
return true; return true;
} }
public bool SetActiveConfigOverride(AMD_DISPLAY_CONFIG displayConfig)
{
if (_initialised)
{
// Set the initial state of the ADL_STATUS
ADL_STATUS ADLRet = 0;
// We want to set the AMD HDR settings now
// We got through each of the attached displays and set the HDR
// Go through each of the HDR configs we have
foreach (var hdrConfig in displayConfig.HdrConfigs)
{
// Try and find the HDR config displays in the list of currently connected displays
foreach (var displayInfoItem in ActiveDisplayConfig.DisplayTargets)
{
try
{
// If we find the HDR config display in the list of currently connected displays then try to set the HDR setting we recorded earlier
if (hdrConfig.Key == displayInfoItem.DisplayID.DisplayLogicalIndex)
{
if (hdrConfig.Value.HDREnabled)
{
ADLRet = ADLImport.ADL2_Display_HDRState_Set(_adlContextHandle, hdrConfig.Value.AdapterIndex, displayInfoItem.DisplayID, 1);
if (ADLRet == ADL_STATUS.ADL_OK)
{
SharedLogger.logger.Trace($"AMDLibrary/SetActiveConfigOverride: ADL2_Display_HDRState_Set was able to turn on HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
else
{
SharedLogger.logger.Error($"AMDLibrary/SetActiveConfigOverride: ADL2_Display_HDRState_Set was NOT able to turn on HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
}
else
{
ADLRet = ADLImport.ADL2_Display_HDRState_Set(_adlContextHandle, hdrConfig.Value.AdapterIndex, displayInfoItem.DisplayID, 0);
if (ADLRet == ADL_STATUS.ADL_OK)
{
SharedLogger.logger.Trace($"AMDLibrary/SetActiveConfigOverride: ADL2_Display_HDRState_Set was able to turn off HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
else
{
SharedLogger.logger.Error($"AMDLibrary/SetActiveConfigOverride: ADL2_Display_HDRState_Set was NOT able to turn off HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
}
}
break;
}
}
catch (Exception ex)
{
SharedLogger.logger.Error(ex, $"AMDLibrary/GetAMDDisplayConfig: Exception! ADL2_Display_HDRState_Set was NOT able to change HDR for display {displayInfoItem.DisplayID.DisplayLogicalIndex}.");
continue;
}
}
}
}
else
{
SharedLogger.logger.Error($"AMDLibrary/SetActiveConfig: ERROR - Tried to run SetActiveConfigOverride but the AMD ADL library isn't initialised!");
throw new AMDLibraryException($"Tried to run SetActiveConfigOverride but the AMD ADL library isn't initialised!");
}
return true;
}
public bool IsActiveConfig(AMD_DISPLAY_CONFIG displayConfig) public bool IsActiveConfig(AMD_DISPLAY_CONFIG displayConfig)
{ {
// Get the current windows display configs to compare to the one we loaded
bool allDisplays = false;
AMD_DISPLAY_CONFIG currentWindowsDisplayConfig = GetAMDDisplayConfig(allDisplays);
// Check whether the display config is in use now // Check whether the display config is in use now
SharedLogger.logger.Trace($"AMDLibrary/IsActiveConfig: Checking whether the display configuration is already being used."); SharedLogger.logger.Trace($"AMDLibrary/IsActiveConfig: Checking whether the display configuration is already being used.");
if (displayConfig.Equals(currentWindowsDisplayConfig)) if (displayConfig.Equals(_activeDisplayConfig))
{ {
SharedLogger.logger.Trace($"AMDLibrary/IsActiveConfig: The display configuration is already being used (supplied displayConfig Equals currentWindowsDisplayConfig)"); SharedLogger.logger.Trace($"AMDLibrary/IsActiveConfig: The display configuration is already being used (supplied displayConfig Equals currentWindowsDisplayConfig)");
return true; return true;

View File

@ -23,14 +23,16 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies> <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<UseVSHostingProcess>true</UseVSHostingProcess> <UseVSHostingProcess>true</UseVSHostingProcess>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>Resources\DisplayMagician.ico</ApplicationIcon> <ApplicationIcon>Resources\DisplayMagician.ico</ApplicationIcon>
@ -85,6 +87,7 @@
<DependentUpon>DisplayView.cs</DependentUpon> <DependentUpon>DisplayView.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Windows\CCD.cs" /> <Compile Include="Windows\CCD.cs" />
<Compile Include="Windows\GDI.cs" />
<Compile Include="Windows\WinLibrary.cs" /> <Compile Include="Windows\WinLibrary.cs" />
<Compile Include="Wallpaper.cs" /> <Compile Include="Wallpaper.cs" />
</ItemGroup> </ItemGroup>
@ -103,9 +106,6 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AdvancedDLSupport">
<Version>3.2.0</Version>
</PackageReference>
<PackageReference Include="EDIDParser"> <PackageReference Include="EDIDParser">
<Version>1.2.5.4</Version> <Version>1.2.5.4</Version>
</PackageReference> </PackageReference>
@ -130,7 +130,7 @@
<Version>2.1.0</Version> <Version>2.1.0</Version>
</PackageReference> </PackageReference>
<PackageReference Include="NLog"> <PackageReference Include="NLog">
<Version>4.7.11</Version> <Version>4.7.12</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -364,6 +364,72 @@ namespace DisplayMagicianShared.NVIDIA
UNKNOWN = 0xFFFFFFFF, UNKNOWN = 0xFFFFFFFF,
} }
public enum NV_DISPLAYCONFIG_SPANNING_ORIENTATION : UInt32
{
NV_DISPLAYCONFIG_SPAN_NONE = 0,
NV_DISPLAYCONFIG_SPAN_HORIZONTAL = 1,
NV_DISPLAYCONFIG_SPAN_VERTICAL = 2,
}
public enum TIMING_SCAN_MODE : ushort
{
/// <summary>
/// Progressive scan mode
/// </summary>
Progressive = 0,
/// <summary>
/// Interlaced scan mode
/// </summary>
Interlaced = 1,
/// <summary>
/// Interlaced scan mode with extra vertical blank
/// </summary>
InterlacedWithExtraVerticalBlank = 1,
/// <summary>
/// Interlaced scan mode without extra vertical blank
/// </summary>
InterlacedWithNoExtraVerticalBlank = 2
}
public enum TIMING_VERTICAL_SYNC_POLARITY : byte
{
/// <summary>
/// Positive vertical synchronized polarity
/// </summary>
Positive = 0,
/// <summary>
/// Negative vertical synchronized polarity
/// </summary>
Negative = 1,
/// <summary>
/// Default vertical synchronized polarity
/// </summary>
Default = Positive
}
public enum TIMING_HORIZONTAL_SYNC_POLARITY : byte
{
/// <summary>
/// Positive horizontal synchronized polarity
/// </summary>
Positive = 0,
/// <summary>
/// Negative horizontal synchronized polarity
/// </summary>
Negative = 1,
/// <summary>
/// Default horizontal synchronized polarity
/// </summary>
Default = Negative
}
public enum NV_TIMING_OVERRIDE : UInt32 public enum NV_TIMING_OVERRIDE : UInt32
{ {
CURRENT = 0, //!< get the current timing CURRENT = 0, //!< get the current timing
@ -525,7 +591,7 @@ namespace DisplayMagicianShared.NVIDIA
} }
public enum NV_COLOR_FORMAT : UInt32 public enum NV_COLOR_FORMAT : byte
{ {
RGB = 0, RGB = 0,
YUV422, YUV422,
@ -536,8 +602,36 @@ namespace DisplayMagicianShared.NVIDIA
AUTO = 0xFF AUTO = 0xFF
} }
public enum NV_DYNAMIC_RANGE : byte
{
VESA = 0x0,
CEA = 0x1,
public enum NV_DYNAMIC_RANGE : UInt32 AUTO = 0xFF
}
public enum NV_BPC : byte
{
BPC_DEFAULT = 0,
BPC_6 = 1,
BPC_8 = 2,
BPC_10 = 3,
BPC_12 = 4,
BPC_16 = 5,
}
public enum NV_HDR_COLOR_FORMAT : UInt32
{
RGB = 0,
YUV422,
YUV444,
YUV420,
DEFAULT = 0xFE,
AUTO = 0xFF
}
public enum NV_HDR_DYNAMIC_RANGE : UInt32
{ {
VESA = 0x0, VESA = 0x0,
CEA = 0x1, CEA = 0x1,
@ -546,14 +640,49 @@ namespace DisplayMagicianShared.NVIDIA
} }
public enum NV_BPC : UInt32 public enum NV_COLOR_CMD : byte
{ {
BPC_DEFAULT = 0, NV_COLOR_CMD_GET = 1,
BPC_6 = 1, NV_COLOR_CMD_SET,
BPC_8 = 2, NV_COLOR_CMD_IS_SUPPORTED_COLOR,
BPC_10 = 3, NV_COLOR_CMD_GET_DEFAULT
BPC_12 = 4, }
BPC_16 = 5,
public enum NV_COLOR_COLORIMETRY : UInt32
{
NV_COLOR_COLORIMETRY_RGB = 0,
NV_COLOR_COLORIMETRY_YCC601,
NV_COLOR_COLORIMETRY_YCC709,
NV_COLOR_COLORIMETRY_XVYCC601,
NV_COLOR_COLORIMETRY_XVYCC709,
NV_COLOR_COLORIMETRY_SYCC601,
NV_COLOR_COLORIMETRY_ADOBEYCC601,
NV_COLOR_COLORIMETRY_ADOBERGB,
NV_COLOR_COLORIMETRY_BT2020RGB,
NV_COLOR_COLORIMETRY_BT2020YCC,
NV_COLOR_COLORIMETRY_BT2020cYCC,
NV_COLOR_COLORIMETRY_DEFAULT = 0xFE,
NV_COLOR_COLORIMETRY_AUTO = 0xFF
}
public enum NV_COLOR_SELECTION_POLICY : UInt32
{
NV_COLOR_SELECTION_POLICY_USER = 0, //!< app/nvcpl make decision to select the desire color format
NV_COLOR_SELECTION_POLICY_BEST_QUALITY = 1, //!< driver/ OS make decision to select the best color format
NV_COLOR_SELECTION_POLICY_DEFAULT = NV_COLOR_SELECTION_POLICY_BEST_QUALITY,
NV_COLOR_SELECTION_POLICY_UNKNOWN = 0xFF,
}
public enum NV_DESKTOP_COLOR_DEPTH
{
NV_DESKTOP_COLOR_DEPTH_DEFAULT = 0x0, // set if the current setting should be kept
NV_DESKTOP_COLOR_DEPTH_8BPC = 0x1, //8 bit int per color component (8 bit int alpha)
NV_DESKTOP_COLOR_DEPTH_10BPC = 0x2, //10 bit int per color component (2 bit int alpha)
NV_DESKTOP_COLOR_DEPTH_16BPC_FLOAT = 0x3, //16 bit float per color component (16 bit float alpha)
NV_DESKTOP_COLOR_DEPTH_16BPC_FLOAT_WCG = 0x4, //16 bit float per color component (16 bit float alpha) wide color gamut
NV_DESKTOP_COLOR_DEPTH_16BPC_FLOAT_HDR = 0x5, //16 bit float per color component (16 bit float alpha) HDR
NV_DESKTOP_COLOR_DEPTH_MAX_VALUE = NV_DESKTOP_COLOR_DEPTH_16BPC_FLOAT_HDR, // must be set to highest enum value
} }
[Flags] [Flags]
@ -759,36 +888,39 @@ namespace DisplayMagicianShared.NVIDIA
} }
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Ansi)]
public struct NV_TIMINGEXT : IEquatable<NV_TIMINGEXT> public struct NV_TIMING_EXTRA : IEquatable<NV_TIMING_EXTRA>
{ {
public UInt32 Flag; //!< Reserved for NVIDIA hardware-based enhancement, such as double-scan. public UInt32 Flags; //!< Reserved for NVIDIA hardware-based enhancement, such as double-scan.
public ushort Rr; //!< Logical refresh rate to present public ushort RefreshRate; //!< Logical refresh rate to present
public UInt32 Rrx1k; //!< Physical vertical refresh rate in 0.001Hz public UInt32 FrequencyInMillihertz; //!< Physical vertical refresh rate in 0.001Hz
public UInt32 Aspect; //!< Display aspect ratio Hi(aspect):horizontal-aspect, Low(aspect):vertical-aspect public ushort VerticalAspect; //!< Display aspect ratio Hi(aspect):horizontal-aspect, Low(aspect):vertical-aspect
public ushort Rep; //!< Bit-wise pixel repetition factor: 0x1:no pixel repetition; 0x2:each pixel repeats twice horizontally,.. public ushort HorizontalAspect; //!< Display aspect ratio Hi(aspect):horizontal-aspect, Low(aspect):vertical-aspect
public UInt32 Status; //!< Timing standard public ushort HorizontalPixelRepetition; //!< Bit-wise pixel repetition factor: 0x1:no pixel repetition; 0x2:each pixel repeats twice horizontally,..
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (Int32)NVImport.NVAPI_UNICODE_STRING_MAX)] public UInt32 TimingStandard; //!< Timing standard
//[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)]
public string Name; //!< Timing name public string Name; //!< Timing name
public override bool Equals(object obj) => obj is NV_TIMINGEXT other && this.Equals(other); public override bool Equals(object obj) => obj is NV_TIMING_EXTRA other && this.Equals(other);
public bool Equals(NV_TIMINGEXT other) public bool Equals(NV_TIMING_EXTRA other)
=> Flag == other.Flag && => Flags == other.Flags &&
Rr == other.Rr && RefreshRate == other.RefreshRate &&
Rrx1k == other.Rrx1k && FrequencyInMillihertz == other.FrequencyInMillihertz &&
Aspect == other.Aspect && VerticalAspect == other.VerticalAspect &&
Rep == other.Rep && HorizontalAspect == other.HorizontalAspect &&
Status == other.Status && HorizontalPixelRepetition == other.HorizontalPixelRepetition &&
TimingStandard == other.TimingStandard &&
Name == other.Name; Name == other.Name;
public override Int32 GetHashCode() public override Int32 GetHashCode()
{ {
return (Flag, Rr, Rrx1k, Aspect, Rep, Status, Name).GetHashCode(); return (Flags, RefreshRate, FrequencyInMillihertz, HorizontalAspect, HorizontalPixelRepetition, TimingStandard, Name).GetHashCode();
} }
public static bool operator ==(NV_TIMINGEXT lhs, NV_TIMINGEXT rhs) => lhs.Equals(rhs); public static bool operator ==(NV_TIMING_EXTRA lhs, NV_TIMING_EXTRA rhs) => lhs.Equals(rhs);
public static bool operator !=(NV_TIMINGEXT lhs, NV_TIMINGEXT rhs) => !(lhs == rhs); public static bool operator !=(NV_TIMING_EXTRA lhs, NV_TIMING_EXTRA rhs) => !(lhs == rhs);
} }
[StructLayout(LayoutKind.Sequential, Pack = 8)] [StructLayout(LayoutKind.Sequential, Pack = 8)]
@ -800,20 +932,20 @@ namespace DisplayMagicianShared.NVIDIA
public ushort HFrontPorch; //!< horizontal front porch public ushort HFrontPorch; //!< horizontal front porch
public ushort HSyncWidth; //!< horizontal sync width public ushort HSyncWidth; //!< horizontal sync width
public ushort HTotal; //!< horizontal total public ushort HTotal; //!< horizontal total
public byte HSyncPol; //!< horizontal sync polarity: 1-negative, 0-positive public TIMING_HORIZONTAL_SYNC_POLARITY HSyncPol; //!< horizontal sync polarity: 1-negative, 0-positive
public ushort VVisible; //!< vertical visible public ushort VVisible; //!< vertical visible
public ushort VBorder; //!< vertical border public ushort VBorder; //!< vertical border
public ushort VFrontPorch; //!< vertical front porch public ushort VFrontPorch; //!< vertical front porch
public ushort VSyncWidth; //!< vertical sync width public ushort VSyncWidth; //!< vertical sync width
public ushort VTotal; //!< vertical total public ushort VTotal; //!< vertical total
public byte VSyncPol; //!< vertical sync polarity: 1-negative, 0-positive public TIMING_VERTICAL_SYNC_POLARITY VSyncPol; //!< vertical sync polarity: 1-negative, 0-positive
public ushort Interlaced; //!< 1-Int32erlaced, 0-progressive public TIMING_SCAN_MODE ScanMode; //!< 1-Int32erlaced, 0-progressive
public UInt32 Pclk; //!< pixel clock in 10 kHz public UInt32 Pclk; //!< pixel clock in 10 kHz
//other timing related extras //other timing related extras
NV_TIMINGEXT Etc; public NV_TIMING_EXTRA Extra;
public override bool Equals(object obj) => obj is NV_TIMING other && this.Equals(other); public override bool Equals(object obj) => obj is NV_TIMING other && this.Equals(other);
@ -830,13 +962,13 @@ namespace DisplayMagicianShared.NVIDIA
VSyncWidth == other.VSyncWidth && VSyncWidth == other.VSyncWidth &&
VTotal == other.VTotal && VTotal == other.VTotal &&
VSyncPol == other.VSyncPol && VSyncPol == other.VSyncPol &&
Interlaced == other.Interlaced && ScanMode == other.ScanMode &&
Pclk == other.Pclk && Pclk == other.Pclk &&
Etc.Equals(other.Etc); Extra.Equals(other.Extra);
public override Int32 GetHashCode() public override Int32 GetHashCode()
{ {
return (HVisible, HBorder, HFrontPorch, HSyncWidth, HTotal, HSyncPol, VVisible, VBorder, VFrontPorch, VSyncWidth, VTotal, VSyncPol, Interlaced, Pclk, Etc).GetHashCode(); return (HVisible, HBorder, HFrontPorch, HSyncWidth, HTotal, HSyncPol, VVisible, VBorder, VFrontPorch, VSyncWidth, VTotal, VSyncPol, ScanMode, Pclk, Extra).GetHashCode();
} }
public static bool operator ==(NV_TIMING lhs, NV_TIMING rhs) => lhs.Equals(rhs); public static bool operator ==(NV_TIMING lhs, NV_TIMING rhs) => lhs.Equals(rhs);
@ -868,6 +1000,27 @@ namespace DisplayMagicianShared.NVIDIA
public static bool operator !=(NV_RECT lhs, NV_RECT rhs) => !(lhs == rhs); public static bool operator !=(NV_RECT lhs, NV_RECT rhs) => !(lhs == rhs);
} }
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct NV_LUID : IEquatable<NV_LUID>
{
public UInt32 LowPart;
public UInt32 HighPart;
public override bool Equals(object obj) => obj is NV_LUID other && this.Equals(other);
public bool Equals(NV_LUID other)
=> LowPart == other.LowPart &&
HighPart == other.HighPart;
public override Int32 GetHashCode()
{
return (LowPart, HighPart).GetHashCode();
}
public static bool operator ==(NV_LUID lhs, NV_LUID rhs) => lhs.Equals(rhs);
public static bool operator !=(NV_LUID lhs, NV_LUID rhs) => !(lhs == rhs);
}
[StructLayout(LayoutKind.Sequential, Pack = 8)] [StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct NV_POSITION : IEquatable<NV_POSITION> public struct NV_POSITION : IEquatable<NV_POSITION>
@ -922,6 +1075,14 @@ namespace DisplayMagicianShared.NVIDIA
public float W; //!< Width of the viewport public float W; //!< Width of the viewport
public float H; //!< Height of the viewport public float H; //!< Height of the viewport
public NV_VIEWPORTF(float myX, float myY, float myW, float myH) : this()
{
X = myX;
Y = myY;
W = myW;
H = myH;
}
public override bool Equals(object obj) => obj is NV_VIEWPORTF other && this.Equals(other); public override bool Equals(object obj) => obj is NV_VIEWPORTF other && this.Equals(other);
// NOTE: Using Math.Round for equality testing between floats. // NOTE: Using Math.Round for equality testing between floats.
@ -955,7 +1116,7 @@ namespace DisplayMagicianShared.NVIDIA
public NV_SCALING Scaling; //!< (IN) scaling setting. public NV_SCALING Scaling; //!< (IN) scaling setting.
// Refresh Rate // Refresh Rate
public UInt32 RefreshRate1K; //!< (IN) Non-Int32erlaced Refresh Rate of the mode, multiplied by 1000, 0 = ignored public UInt32 RefreshRateInMillihertz; //!< (IN) Non-Int32erlaced Refresh Rate of the mode, multiplied by 1000, 0 = ignored
//!< This is the value which driver reports to the OS. //!< This is the value which driver reports to the OS.
// Flags // Flags
//public UInt32 Int32erlaced:1; //!< (IN) Interlaced mode flag, ignored if refreshRate == 0 //public UInt32 Int32erlaced:1; //!< (IN) Interlaced mode flag, ignored if refreshRate == 0
@ -967,8 +1128,9 @@ namespace DisplayMagicianShared.NVIDIA
//public UInt32 disableVirtualModeSupport:1; //public UInt32 disableVirtualModeSupport:1;
//public UInt32 isPreferredUnscaledTarget:1; //public UInt32 isPreferredUnscaledTarget:1;
//public UInt32 reserved:27; //public UInt32 reserved:27;
public UInt32 Flags;
// TV format information // TV format information
public NV_GPU_CONNECTOR_TYPE Connector; //!< Specify connector type. For TV only, ignored if tvFormat == NV_DISPLAY_TV_FORMAT_NONE public NV_GPU_CONNECTOR_TYPE ConnectorType; //!< Specify connector type. For TV only, ignored if tvFormat == NV_DISPLAY_TV_FORMAT_NONE
public NV_DISPLAY_TV_FORMAT TvFormat; //!< (IN) to choose the last TV format set this value to NV_DISPLAY_TV_FORMAT_NONE public NV_DISPLAY_TV_FORMAT TvFormat; //!< (IN) to choose the last TV format set this value to NV_DISPLAY_TV_FORMAT_NONE
//!< In case of NvAPI_DISP_GetDisplayConfig(), this field will indicate the currently applied TV format; //!< In case of NvAPI_DISP_GetDisplayConfig(), this field will indicate the currently applied TV format;
//!< if no TV format is applied, this field will have NV_DISPLAY_TV_FORMAT_NONE value. //!< if no TV format is applied, this field will have NV_DISPLAY_TV_FORMAT_NONE value.
@ -987,15 +1149,16 @@ namespace DisplayMagicianShared.NVIDIA
=> Version == other.Version && => Version == other.Version &&
Rotation == other.Rotation && Rotation == other.Rotation &&
Scaling == other.Scaling && Scaling == other.Scaling &&
RefreshRate1K == other.RefreshRate1K && RefreshRateInMillihertz == other.RefreshRateInMillihertz &&
Connector == other.Connector && Flags == other.Flags &&
ConnectorType == other.ConnectorType &&
TvFormat == other.TvFormat && TvFormat == other.TvFormat &&
TimingOverride == other.TimingOverride && TimingOverride == other.TimingOverride &&
Timing.Equals(other.Timing); Timing.Equals(other.Timing);
public override Int32 GetHashCode() public override Int32 GetHashCode()
{ {
return (Version, Rotation, Scaling, RefreshRate1K, Connector, TvFormat, TimingOverride, Timing).GetHashCode(); return (Version, Rotation, Scaling, RefreshRateInMillihertz, Flags, ConnectorType, TvFormat, TimingOverride, Timing).GetHashCode();
} }
public static bool operator ==(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO lhs, NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO rhs) => lhs.Equals(rhs); public static bool operator ==(NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO lhs, NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO rhs) => lhs.Equals(rhs);
@ -1006,19 +1169,19 @@ namespace DisplayMagicianShared.NVIDIA
public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 : IEquatable<NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2> public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 : IEquatable<NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2>
{ {
public UInt32 DisplayId; //!< Display ID public UInt32 DisplayId; //!< Display ID
NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO Details; //!< May be NULL if no advanced settings are required public NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO Details; //!< May be NULL if no advanced settings are required
public UInt32 TargetId; //!< Windows CCD target ID. Must be present only for non-NVIDIA adapter, for NVIDIA adapter this parameter is ignored. public UInt32 WindowsCCDTargetId; //!< Windows CCD target ID. Must be present only for non-NVIDIA adapter, for NVIDIA adapter this parameter is ignored.
public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 other && this.Equals(other); public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 other && this.Equals(other);
public bool Equals(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 other) public bool Equals(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 other)
=> DisplayId == other.DisplayId && => DisplayId == other.DisplayId &&
Details.Equals(other.Details) && Details.Equals(other.Details) &&
TargetId == other.TargetId; WindowsCCDTargetId == other.WindowsCCDTargetId;
public override Int32 GetHashCode() public override Int32 GetHashCode()
{ {
return (DisplayId, Details, TargetId).GetHashCode(); return (DisplayId, Details, WindowsCCDTargetId).GetHashCode();
} }
public static bool operator ==(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 rhs) => lhs.Equals(rhs); public static bool operator ==(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2 rhs) => lhs.Equals(rhs);
@ -1029,7 +1192,7 @@ namespace DisplayMagicianShared.NVIDIA
public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 : IEquatable<NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1> public struct NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 : IEquatable<NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1>
{ {
public UInt32 DisplayId; //!< Display ID public UInt32 DisplayId; //!< Display ID
NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO Details; //!< May be NULL if no advanced settings are required public NV_DISPLAYCONFIG_PATH_ADVANCED_TARGET_INFO Details; //!< May be NULL if no advanced settings are required
public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 other && this.Equals(other); public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 other && this.Equals(other);
@ -1046,23 +1209,26 @@ namespace DisplayMagicianShared.NVIDIA
public static bool operator !=(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 rhs) => !(lhs == rhs); public static bool operator !=(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 lhs, NV_DISPLAYCONFIG_PATH_TARGET_INFO_V1 rhs) => !(lhs == rhs);
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct NV_DISPLAYCONFIG_PATH_INFO_V2 : IEquatable<NV_DISPLAYCONFIG_PATH_INFO_V2> // Version is 2 public struct NV_DISPLAYCONFIG_PATH_INFO_V2 : IEquatable<NV_DISPLAYCONFIG_PATH_INFO_V2> // Version is 2
{ {
public UInt32 Version; public UInt32 Version;
public UInt32 SourceId; //!< Identifies sourceId used by Windows CCD. This can be optionally set. public UInt32 SourceId; //!< Identifies sourceId used by Windows CCD. This can be optionally set.
public UInt32 TargetInfoCount; //!< Number of elements in targetInfo array public UInt32 TargetInfoCount; //!< Number of elements in targetInfo array
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] //[MarshalAs(UnmanagedType.ByValArray)]
public NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2[] TargetInfo; public IntPtr TargetInfo;
public NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 SourceModeInfo; //!< May be NULL if mode info is not important public IntPtr SourceModeInfo; //!< May be NULL if mode info is not important
//public IntPtr SourceModeInfo; //!< May be NULL if mode info is not important
//public UInt32 IsNonNVIDIAAdapter : 1; //!< True for non-NVIDIA adapter. //public UInt32 IsNonNVIDIAAdapter : 1; //!< True for non-NVIDIA adapter.
//public UInt32 reserved : 31; //!< Must be 0 //public UInt32 reserved : 31; //!< Must be 0
//public LUID pOSAdapterID; //!< Used by Non-NVIDIA adapter for poInt32er to OS Adapter of LUID public UInt32 Flags;
//!< Used by Non-NVIDIA adapter for pointer to OS Adapter of LUID
//!< type, type casted to void *. //!< type, type casted to void *.
public UInt32 Reserved;
public IntPtr OSAdapterID; public IntPtr OSAdapterID;
public bool IsNonNVIDIAAdapter => Flags.GetBit(0); //!< if bit is set then this path uses a non-nvidia adapter
public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_INFO_V2 other && this.Equals(other); public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_PATH_INFO_V2 other && this.Equals(other);
public bool Equals(NV_DISPLAYCONFIG_PATH_INFO_V2 other) public bool Equals(NV_DISPLAYCONFIG_PATH_INFO_V2 other)
@ -1071,12 +1237,11 @@ namespace DisplayMagicianShared.NVIDIA
TargetInfoCount == other.TargetInfoCount && TargetInfoCount == other.TargetInfoCount &&
TargetInfo.Equals(other.TargetInfo) && TargetInfo.Equals(other.TargetInfo) &&
SourceModeInfo.Equals(other.SourceModeInfo) && SourceModeInfo.Equals(other.SourceModeInfo) &&
Reserved == other.Reserved && Flags == other.Flags;
OSAdapterID == other.OSAdapterID;
public override Int32 GetHashCode() public override Int32 GetHashCode()
{ {
return (Version, SourceId, TargetInfoCount, TargetInfo, SourceModeInfo).GetHashCode(); return (Version, SourceId, TargetInfoCount, TargetInfo, SourceModeInfo, Flags).GetHashCode();
} }
public static bool operator ==(NV_DISPLAYCONFIG_PATH_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_INFO_V2 rhs) => lhs.Equals(rhs); public static bool operator ==(NV_DISPLAYCONFIG_PATH_INFO_V2 lhs, NV_DISPLAYCONFIG_PATH_INFO_V2 rhs) => lhs.Equals(rhs);
@ -1125,10 +1290,11 @@ namespace DisplayMagicianShared.NVIDIA
public NV_POSITION Position; //!< Is all positions are 0 or invalid, displays will be automatically public NV_POSITION Position; //!< Is all positions are 0 or invalid, displays will be automatically
//!< positioned from left to right with GDI Primary at 0,0, and all //!< positioned from left to right with GDI Primary at 0,0, and all
//!< other displays in the order of the path array. //!< other displays in the order of the path array.
//public NV_DISPLAYCONFIG_SPANNING_ORIENTATION spanningOrientation; //!< Spanning is only supported on XP public NV_DISPLAYCONFIG_SPANNING_ORIENTATION SpanningOrientation; //!< Spanning is only supported on XP
//public UInt32 bGDIPrimary : 1; public UInt32 Flags;
//public UInt32 bSLIFocus : 1;
//public UInt32 reserved : 30; //!< Must be 0 public bool IsGDIPrimary => (Flags & 0x1) == 0x1; //!< if bit is set then this source is the primary GDI source
public bool IsSLIFocus => (Flags & 0x2) == 0x2; //!< if bit is set then this source has SLI focus
public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 other && this.Equals(other); public override bool Equals(object obj) => obj is NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 other && this.Equals(other);
@ -1799,8 +1965,8 @@ namespace DisplayMagicianShared.NVIDIA
public NV_HDR_MODE HdrMode; //!< HDR mode public NV_HDR_MODE HdrMode; //!< HDR mode
public NV_STATIC_METADATA_DESCRIPTOR_ID StaticMetadataDescriptorId; //!< Static Metadata Descriptor Id (0 for static metadata type 1) public NV_STATIC_METADATA_DESCRIPTOR_ID StaticMetadataDescriptorId; //!< Static Metadata Descriptor Id (0 for static metadata type 1)
public NV_HDR_COLOR_DISPLAY_DATA MasteringDisplayData; //!< Static Metadata Descriptor Type 1, CEA-861.3, SMPTE ST2086 public NV_HDR_COLOR_DISPLAY_DATA MasteringDisplayData; //!< Static Metadata Descriptor Type 1, CEA-861.3, SMPTE ST2086
public NV_COLOR_FORMAT HdrColorFormat; //!< Optional, One of NV_COLOR_FORMAT enum values, if set it will apply requested color format for HDR session public NV_HDR_COLOR_FORMAT HdrColorFormat; //!< Optional, One of NV_COLOR_FORMAT enum values, if set it will apply requested color format for HDR session
public NV_DYNAMIC_RANGE HdrDynamicRange; //!< Optional, One of NV_DYNAMIC_RANGE enum values, if set it will apply requested dynamic range for HDR session public NV_HDR_DYNAMIC_RANGE HdrDynamicRange; //!< Optional, One of NV_DYNAMIC_RANGE enum values, if set it will apply requested dynamic range for HDR session
public NV_BPC HdrBpc; //!< Optional, One of NV_BPC enum values, if set it will apply requested color depth public NV_BPC HdrBpc; //!< Optional, One of NV_BPC enum values, if set it will apply requested color depth
//!< Dolby Vision mode: DV supports specific combinations of colorformat, dynamic range and bpc. Please refer Dolby Vision specification. //!< Dolby Vision mode: DV supports specific combinations of colorformat, dynamic range and bpc. Please refer Dolby Vision specification.
//!< If invalid or no combination is passed driver will force default combination of RGB format + full range + 8bpc. //!< If invalid or no combination is passed driver will force default combination of RGB format + full range + 8bpc.
@ -1867,6 +2033,79 @@ namespace DisplayMagicianShared.NVIDIA
public static bool operator !=(NV_HDR_COLOR_DISPLAY_DATA lhs, NV_HDR_COLOR_DISPLAY_DATA rhs) => !(lhs == rhs); public static bool operator !=(NV_HDR_COLOR_DISPLAY_DATA lhs, NV_HDR_COLOR_DISPLAY_DATA rhs) => !(lhs == rhs);
} }
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct NV_COLOR_DATA_V5 : IEquatable<NV_COLOR_DATA_V5>
{
public UInt32 Version; //!< Version of this structure
public UInt16 Size; //!< Size of this structure
public NV_COLOR_CMD Cmd;
public NV_COLOR_FORMAT ColorFormat; //!< One of NV_COLOR_FORMAT enum values.
public NV_COLOR_COLORIMETRY Colorimetry; //!< One of NV_COLOR_COLORIMETRY enum values.
public NV_DYNAMIC_RANGE DynamicRange; //!< One of NV_DYNAMIC_RANGE enum values.
public NV_BPC Bpc; //!< One of NV_BPC enum values.
public NV_COLOR_SELECTION_POLICY ColorSelectionPolicy; //!< One of the color selection policy
public NV_DESKTOP_COLOR_DEPTH Depth; //!< One of NV_DESKTOP_COLOR_DEPTH enum values.
public override bool Equals(object obj) => obj is NV_COLOR_DATA_V5 other && this.Equals(other);
public bool Equals(NV_COLOR_DATA_V5 other)
=> Version == other.Version &&
Size == other.Size &&
Cmd == other.Cmd &&
ColorFormat == other.ColorFormat &&
Colorimetry == other.Colorimetry &&
DynamicRange == other.DynamicRange &&
Bpc == other.Bpc &&
ColorSelectionPolicy == other.ColorSelectionPolicy &&
Depth == other.Depth;
public override Int32 GetHashCode()
{
return (Version, Size, Cmd, ColorFormat, Colorimetry, DynamicRange, Bpc, ColorSelectionPolicy, Depth).GetHashCode();
}
public static bool operator ==(NV_COLOR_DATA_V5 lhs, NV_COLOR_DATA_V5 rhs) => lhs.Equals(rhs);
public static bool operator !=(NV_COLOR_DATA_V5 lhs, NV_COLOR_DATA_V5 rhs) => !(lhs == rhs);
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct NV_CUSTOM_DISPLAY_V1 : IEquatable<NV_CUSTOM_DISPLAY_V1>
{
public UInt32 Version; //!< Version of this structure
public UInt32 Width; //!< Source surface(source mode) width
public UInt32 Height; //!< Source surface(source mode) height
public UInt32 Depth; //!< Source surface color depth."0" means all 8/16/32bpp
public NV_FORMAT ColorFormat; //!< Color format (optional)
public NV_VIEWPORTF SourcePartition; //!< For multimon support, should be set to (0,0,1.0,1.0) for now.
public float XRatio; //!< Horizontal scaling ratio
public float YRatio; //!< Vertical scaling ratio
public NV_TIMING Timing; //!< Timing used to program TMDS/DAC/LVDS/HDMI/TVEncoder, etc.
public UInt32 Flags; //!< If set to 1, it means a hardware modeset without OS update
// Gets a boolean value indicating that a hardware mode-set without OS update should be performed.
public bool IsHardwareModeSetOnly => Flags.GetBit(0);
public override bool Equals(object obj) => obj is NV_CUSTOM_DISPLAY_V1 other && this.Equals(other);
public bool Equals(NV_CUSTOM_DISPLAY_V1 other)
=> Version == other.Version &&
Width == other.Width &&
Height == other.Height &&
Depth == other.Depth &&
ColorFormat == other.ColorFormat &&
SourcePartition == other.SourcePartition &&
XRatio == other.XRatio &&
YRatio == other.YRatio &&
Timing == other.Timing &&
Flags == other.Flags;
public override Int32 GetHashCode()
{
return (Version, Width, Height, Depth, ColorFormat, SourcePartition, XRatio, YRatio, Timing, Flags).GetHashCode();
}
public static bool operator ==(NV_CUSTOM_DISPLAY_V1 lhs, NV_CUSTOM_DISPLAY_V1 rhs) => lhs.Equals(rhs);
public static bool operator !=(NV_CUSTOM_DISPLAY_V1 lhs, NV_CUSTOM_DISPLAY_V1 rhs) => !(lhs == rhs);
}
// ================================== // ==================================
// NVImport Class // NVImport Class
// ================================== // ==================================
@ -1911,6 +2150,7 @@ namespace DisplayMagicianShared.NVIDIA
public const UInt32 NVAPI_LONG_STRING_MAX = 256; public const UInt32 NVAPI_LONG_STRING_MAX = 256;
public const UInt32 NVAPI_SHORT_STRING_MAX = 64; public const UInt32 NVAPI_SHORT_STRING_MAX = 64;
public const UInt32 NVAPI_MAX_PHYSICAL_GPUS = 64; public const UInt32 NVAPI_MAX_PHYSICAL_GPUS = 64;
public const UInt32 NVAPI_MAX_PHYSICAL_GPUS_QUERIED = 32;
public const UInt32 NVAPI_UNICODE_STRING_MAX = 2048; public const UInt32 NVAPI_UNICODE_STRING_MAX = 2048;
public const UInt32 NVAPI_BINARY_DATA_MAX = 4096; public const UInt32 NVAPI_BINARY_DATA_MAX = 4096;
public const UInt32 NVAPI_SETTING_MAX_VALUES = 100; public const UInt32 NVAPI_SETTING_MAX_VALUES = 100;
@ -1938,6 +2178,7 @@ namespace DisplayMagicianShared.NVIDIA
public static UInt32 NV_MOSAIC_SUPPORTED_TOPO_INFO_V1_VER = MAKE_NVAPI_VERSION<NV_MOSAIC_SUPPORTED_TOPO_INFO_V1>(1); public static UInt32 NV_MOSAIC_SUPPORTED_TOPO_INFO_V1_VER = MAKE_NVAPI_VERSION<NV_MOSAIC_SUPPORTED_TOPO_INFO_V1>(1);
public static UInt32 NV_MOSAIC_SUPPORTED_TOPO_INFO_V2_VER = MAKE_NVAPI_VERSION<NV_MOSAIC_SUPPORTED_TOPO_INFO_V2>(2); public static UInt32 NV_MOSAIC_SUPPORTED_TOPO_INFO_V2_VER = MAKE_NVAPI_VERSION<NV_MOSAIC_SUPPORTED_TOPO_INFO_V2>(2);
public static UInt32 NV_HDR_COLOR_DATA_V2_VER = MAKE_NVAPI_VERSION<NV_HDR_COLOR_DATA_V2>(2); public static UInt32 NV_HDR_COLOR_DATA_V2_VER = MAKE_NVAPI_VERSION<NV_HDR_COLOR_DATA_V2>(2);
public static UInt32 NV_COLOR_DATA_V5_VER = MAKE_NVAPI_VERSION<NV_COLOR_DATA_V5>(5);
public static UInt32 NV_HDR_CAPABILITIES_V2_VER = MAKE_NVAPI_VERSION<NV_HDR_CAPABILITIES_V2>(2); public static UInt32 NV_HDR_CAPABILITIES_V2_VER = MAKE_NVAPI_VERSION<NV_HDR_CAPABILITIES_V2>(2);
public static UInt32 NV_MOSAIC_DISPLAY_TOPO_STATUS_V1_VER = MAKE_NVAPI_VERSION<NV_MOSAIC_DISPLAY_TOPO_STATUS_V1>(1); public static UInt32 NV_MOSAIC_DISPLAY_TOPO_STATUS_V1_VER = MAKE_NVAPI_VERSION<NV_MOSAIC_DISPLAY_TOPO_STATUS_V1>(1);
public static UInt32 NV_GPU_DISPLAYIDS_V2_VER = MAKE_NVAPI_VERSION<NV_GPU_DISPLAYIDS_V2>(3); // NOTE: There is a bug in R470 that sets the NV_GPU_DISPLAYIDS_V2 version to 3! public static UInt32 NV_GPU_DISPLAYIDS_V2_VER = MAKE_NVAPI_VERSION<NV_GPU_DISPLAYIDS_V2>(3); // NOTE: There is a bug in R470 that sets the NV_GPU_DISPLAYIDS_V2 version to 3!
@ -1945,6 +2186,7 @@ namespace DisplayMagicianShared.NVIDIA
public static UInt32 NV_EDID_V3_VER = MAKE_NVAPI_VERSION<NV_EDID_V3>(3); public static UInt32 NV_EDID_V3_VER = MAKE_NVAPI_VERSION<NV_EDID_V3>(3);
public static UInt32 NV_DISPLAYCONFIG_PATH_INFO_V1_VER = MAKE_NVAPI_VERSION<NV_DISPLAYCONFIG_PATH_INFO_V1>(1); public static UInt32 NV_DISPLAYCONFIG_PATH_INFO_V1_VER = MAKE_NVAPI_VERSION<NV_DISPLAYCONFIG_PATH_INFO_V1>(1);
public static UInt32 NV_DISPLAYCONFIG_PATH_INFO_V2_VER = MAKE_NVAPI_VERSION<NV_DISPLAYCONFIG_PATH_INFO_V2>(2); public static UInt32 NV_DISPLAYCONFIG_PATH_INFO_V2_VER = MAKE_NVAPI_VERSION<NV_DISPLAYCONFIG_PATH_INFO_V2>(2);
public static UInt32 NV_CUSTOM_DISPLAY_V1_VER = MAKE_NVAPI_VERSION<NV_CUSTOM_DISPLAY_V1>(1);
#region Internal Constant #region Internal Constant
@ -2078,9 +2320,11 @@ namespace DisplayMagicianShared.NVIDIA
GetDelegate(NvId_DISP_GetGDIPrimaryDisplayId, out DISP_GetGDIPrimaryDisplayIdInternal); GetDelegate(NvId_DISP_GetGDIPrimaryDisplayId, out DISP_GetGDIPrimaryDisplayIdInternal);
GetDelegate(NvId_Disp_GetHdrCapabilities, out Disp_GetHdrCapabilitiesInternal); GetDelegate(NvId_Disp_GetHdrCapabilities, out Disp_GetHdrCapabilitiesInternal);
GetDelegate(NvId_Disp_HdrColorControl, out Disp_HdrColorControlInternal); GetDelegate(NvId_Disp_HdrColorControl, out Disp_HdrColorControlInternal);
/*GetDelegate(NvId_DISP_GetDisplayConfig, out DISP_GetDisplayConfigInternal); GetDelegate(NvId_Disp_ColorControl, out Disp_ColorControlInternal);
GetDelegate(NvId_DISP_GetDisplayConfig, out DISP_GetDisplayConfigInternalNull); // null version of the submission*/ GetDelegate(NvId_DISP_GetDisplayConfig, out DISP_GetDisplayConfigInternal);
GetDelegate(NvId_DISP_GetDisplayConfig, out DISP_GetDisplayConfigInternalNull); // null version of the submission
GetDelegate(NvId_DISP_GetDisplayIdByDisplayName, out DISP_GetDisplayIdByDisplayNameInternal); GetDelegate(NvId_DISP_GetDisplayIdByDisplayName, out DISP_GetDisplayIdByDisplayNameInternal);
GetDelegate(NvId_DISP_EnumCustomDisplay, out Disp_EnumCustomDisplayInternal);
// GPUs // GPUs
GetDelegate(NvId_EnumPhysicalGPUs, out EnumPhysicalGPUsInternal); GetDelegate(NvId_EnumPhysicalGPUs, out EnumPhysicalGPUsInternal);
@ -2118,13 +2362,13 @@ namespace DisplayMagicianShared.NVIDIA
private static string GetDllName() private static string GetDllName()
{ {
if (IntPtr.Size == 4) if (IntPtr.Size > 4)
{ {
return "nvapi.dll"; return "nvapi64.dll";
} }
else else
{ {
return "nvapi64.dll"; return "nvapi.dll";
} }
} }
@ -2219,9 +2463,6 @@ namespace DisplayMagicianShared.NVIDIA
} }
} }
#endregion #endregion
@ -3135,7 +3376,7 @@ namespace DisplayMagicianShared.NVIDIA
// ******** IMPORTANT! This code has an error when attempting to perform the third pass as required by NVAPI documentation ********* // ******** IMPORTANT! This code has an error when attempting to perform the third pass as required by NVAPI documentation *********
// ******** FOr this reason I have disabled the code as I don't actually need to get it going. ******** // ******** FOr this reason I have disabled the code as I don't actually need to get it going. ********
/* // NVAPI_INTERFACE NvAPI_DISP_GetDisplayConfig(__inout NvU32 *pathInfoCount, __out_ecount_full_opt(*pathInfoCount) NV_DISPLAYCONFIG_PATH_INFO *pathInfo); // NVAPI_INTERFACE NvAPI_DISP_GetDisplayConfig(__inout NvU32 *pathInfoCount, __out_ecount_full_opt(*pathInfoCount) NV_DISPLAYCONFIG_PATH_INFO *pathInfo);
private delegate NVAPI_STATUS DISP_GetDisplayConfigDelegate( private delegate NVAPI_STATUS DISP_GetDisplayConfigDelegate(
[In][Out] ref UInt32 pathInfoCount, [In][Out] ref UInt32 pathInfoCount,
[In][Out] IntPtr pathInfoBuffer); [In][Out] IntPtr pathInfoBuffer);
@ -3154,30 +3395,33 @@ namespace DisplayMagicianShared.NVIDIA
/// <param name="PathInfoCount"></param> /// <param name="PathInfoCount"></param>
/// <param name="PathInfo"></param> /// <param name="PathInfo"></param>
/// <returns></returns> /// <returns></returns>
public static NVAPI_STATUS NvAPI_DISP_GetDisplayConfig(ref UInt32 PathInfoCount, ref NV_DISPLAYCONFIG_PATH_INFO_V1[] PathInfos, bool partFilledIn = false) public static NVAPI_STATUS NvAPI_DISP_GetDisplayConfig(ref UInt32 PathInfoCount, ref NV_DISPLAYCONFIG_PATH_INFO_V2[] PathInfos, bool thirdPass = false)
{ {
NVAPI_STATUS status; NVAPI_STATUS status;
IntPtr pathInfoBuffer = IntPtr.Zero; IntPtr pathInfoBuffer = IntPtr.Zero;
IntPtr currentPathInfoBuffer = IntPtr.Zero; IntPtr currentPathInfoBuffer = IntPtr.Zero;
if (partFilledIn) if (thirdPass)
{ {
// Copy the supplied object for the third pass (when we have the pathInfoCount and the targetInfoCount for each pathInfo, but we want the details) // Copy the supplied object for the third pass (when we have the pathInfoCount and the targetInfoCount for each pathInfo, but we want the details)
//NV_DISPLAYCONFIG_PATH_INFO_V1[] passedPathInfo = PathInfos; // Third Pass(Optional, only required if target information is required): Allocate memory for targetInfo with respect
//PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V1[PathInfoCount]; //! to number of targetInfoCount(from Second Pass).
NV_DISPLAYCONFIG_PATH_INFO_V2[] passedPathInfo = PathInfos;
PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount];
// Go through the array and create the structure // Go through the array and create the structure
int overallTargetCount = 0; int overallTargetCount = 0;
for (Int32 x = 0; x < (Int32)PathInfoCount; x++) for (Int32 x = 0; x < (Int32)PathInfoCount; x++)
{ {
// Copy the information passed in, into the buffer we want to pass // Copy the information passed in, into the buffer we want to pass
//PathInfos[x].Version = MAKE_NVAPI_VERSION(Marshal.SizeOf(passedPathInfo[x]),1);
*//*PathInfos[x].SourceId = passedPathInfo[x].SourceId; PathInfos[x].SourceId = passedPathInfo[x].SourceId;
PathInfos[x].TargetInfoCount = passedPathInfo[x].TargetInfoCount; PathInfos[x].TargetInfoCount = passedPathInfo[x].TargetInfoCount;
PathInfos[x].TargetInfo = passedPathInfo[x].TargetInfo; PathInfos[x].TargetInfo = passedPathInfo[x].TargetInfo;
PathInfos[x].SourceModeInfo = = passedPathInfo[x].SourceModeInfo;*//* PathInfos[x].SourceModeInfo = passedPathInfo[x].SourceModeInfo;
overallTargetCount += (int)PathInfos[x].TargetInfoCount; overallTargetCount += (int)PathInfos[x].TargetInfoCount;
PathInfos[x].Version = MAKE_NVAPI_VERSION(Marshal.SizeOf(passedPathInfo[x]), 1);
} }
// Initialize unmanged memory to hold the unmanaged array of structs // Initialize unmanged memory to hold the unmanaged array of structs
int memorySizeRequired = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V1)) * (int)PathInfoCount; int memorySizeRequired = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V2)) * (int)PathInfoCount;
pathInfoBuffer = Marshal.AllocCoTaskMem(memorySizeRequired); pathInfoBuffer = Marshal.AllocCoTaskMem(memorySizeRequired);
// Also set another memory pointer to the same place so that we can do the memory copying item by item // Also set another memory pointer to the same place so that we can do the memory copying item by item
// as we have to do it ourselves (there isn't an easy to use Marshal equivalent) // as we have to do it ourselves (there isn't an easy to use Marshal equivalent)
@ -3194,23 +3438,48 @@ namespace DisplayMagicianShared.NVIDIA
} }
else else
{ {
// This is the second pass
// Second Pass: Allocate memory for pathInfo with respect to the number of pathInfoCount(from First Pass) to fetch
// targetInfoCount. If sourceModeInfo is needed allocate memory or it can be initialized to NULL.
// Build a new blank object for the second pass (when we have the pathInfoCount, but want the targetInfoCount for each pathInfo) // Build a new blank object for the second pass (when we have the pathInfoCount, but want the targetInfoCount for each pathInfo)
// Build a managed structure for us to use as a data source for another object that the unmanaged NVAPI C library can use // Build a managed structure for us to use as a data source for another object that the unmanaged NVAPI C library can use
PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V1[PathInfoCount]; PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount];
// Prepare the struct for second pass duties
for (Int32 x = 0; x < (Int32)PathInfoCount; x++)
{
NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1 sourceMode = new NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1();
IntPtr sourceModeBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1)));
Marshal.StructureToPtr(sourceMode, sourceModeBuffer, true);
PathInfos[x].Version = NVImport.NV_DISPLAYCONFIG_PATH_INFO_V2_VER;
PathInfos[x].SourceModeInfo = sourceModeBuffer;
/*PathInfos[x].SourceModeInfo.Resolution = new NV_RESOLUTION();
PathInfos[x].SourceModeInfo.Position = new NV_POSITION();
//PathInfos[x].SourceModeInfo = null;
PathInfos[x].TargetInfoCount = 0;
PathInfos[x].TargetInfo = IntPtr.Zero;
//!< This field is reserved. There is ongoing debate if we need this field.
//!< Identifies sourceIds used by Windows. If all sourceIds are 0,
//!< these will be computed automatically.
PathInfos[x].SourceId = 0;
PathInfos[x].Flags = 0;
PathInfos[x].OSAdapterID = new NV_LUID();
//PathInfos[x].OSAdapterID = IntPtr.Zero;*/
}
// Initialize unmanged memory to hold the unmanaged array of structs // Initialize unmanged memory to hold the unmanaged array of structs
pathInfoBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V1)) * (int)PathInfoCount); int sizeOfOneStruct = Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_INFO_V2));
int sizeOfAllStructs = sizeOfOneStruct * (int)PathInfoCount;
//int sizeOfOneStruct = Marshal.SizeOf(PathInfos);
pathInfoBuffer = Marshal.AllocCoTaskMem(sizeOfAllStructs);
// Also set another memory pointer to the same place so that we can do the memory copying item by item // Also set another memory pointer to the same place so that we can do the memory copying item by item
// as we have to do it ourselves (there isn't an easy to use Marshal equivalent) // as we have to do it ourselves (there isn't an easy to use Marshal equivalent)
currentPathInfoBuffer = pathInfoBuffer; currentPathInfoBuffer = pathInfoBuffer;
// Go through the array and copy things from managed code to unmanaged code // Go through the array and copy things from managed code to unmanaged code
for (Int32 x = 0; x < (Int32)PathInfoCount; x++) for (Int32 x = 0; x < (Int32)PathInfoCount; x++)
{ {
PathInfos[x].Version = MAKE_NVAPI_VERSION(Marshal.SizeOf(PathInfos[x]), 1);
PathInfos[x].SourceModeInfo = new NV_DISPLAYCONFIG_SOURCE_MODE_INFO_V1();
// Marshal a single gridtopology into unmanaged code ready for sending to the unmanaged NVAPI function // Marshal a single gridtopology into unmanaged code ready for sending to the unmanaged NVAPI function
Marshal.StructureToPtr(PathInfos[x], currentPathInfoBuffer, false); Marshal.StructureToPtr(PathInfos[x], currentPathInfoBuffer, true);
// advance the buffer forwards to the next object // advance the buffer forwards to the next object
currentPathInfoBuffer = (IntPtr)((long)currentPathInfoBuffer + Marshal.SizeOf(PathInfos[x])); currentPathInfoBuffer = (IntPtr)((long)currentPathInfoBuffer.ToInt64() + Marshal.SizeOf(typeof(NV_DISPLAYCONFIG_PATH_TARGET_INFO_V2)));
} }
} }
@ -3227,16 +3496,16 @@ namespace DisplayMagicianShared.NVIDIA
// Reset the memory pointer we're using for tracking where we are back to the start of the unmanaged memory buffer // Reset the memory pointer we're using for tracking where we are back to the start of the unmanaged memory buffer
currentPathInfoBuffer = pathInfoBuffer; currentPathInfoBuffer = pathInfoBuffer;
// Create a managed array to store the received information within // Create a managed array to store the received information within
PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V1[PathInfoCount]; PathInfos = new NV_DISPLAYCONFIG_PATH_INFO_V2[PathInfoCount];
// Go through the memory buffer item by item and copy the items into the managed array // Go through the memory buffer item by item and copy the items into the managed array
for (int i = 0; i < PathInfoCount; i++) for (int i = 0; i < PathInfoCount; i++)
{ {
// build a structure in the array slot // build a structure in the array slot
PathInfos[i] = new NV_DISPLAYCONFIG_PATH_INFO_V1(); PathInfos[i] = new NV_DISPLAYCONFIG_PATH_INFO_V2();
// fill the array slot structure with the data from the buffer // fill the array slot structure with the data from the buffer
PathInfos[i] = (NV_DISPLAYCONFIG_PATH_INFO_V1)Marshal.PtrToStructure(currentPathInfoBuffer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V1)); PathInfos[i] = (NV_DISPLAYCONFIG_PATH_INFO_V2)Marshal.PtrToStructure(currentPathInfoBuffer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V2));
// destroy the bit of memory we no longer need // destroy the bit of memory we no longer need
Marshal.DestroyStructure(currentPathInfoBuffer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V1)); Marshal.DestroyStructure(currentPathInfoBuffer, typeof(NV_DISPLAYCONFIG_PATH_INFO_V2));
// advance the buffer forwards to the next object // advance the buffer forwards to the next object
currentPathInfoBuffer = (IntPtr)((long)currentPathInfoBuffer + Marshal.SizeOf(PathInfos[i])); currentPathInfoBuffer = (IntPtr)((long)currentPathInfoBuffer + Marshal.SizeOf(PathInfos[i]));
} }
@ -3280,7 +3549,7 @@ namespace DisplayMagicianShared.NVIDIA
else { status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; } else { status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; }
return status; return status;
}*/ }
// NVAPI_INTERFACE NvAPI_DISP_GetDisplayIdByDisplayName(const char *displayName, NvU32* displayId); // NVAPI_INTERFACE NvAPI_DISP_GetDisplayIdByDisplayName(const char *displayName, NvU32* displayId);
@ -4017,6 +4286,48 @@ namespace DisplayMagicianShared.NVIDIA
return status; return status;
} }
//NVAPI_INTERFACE NvAPI_Disp_ColorControl(__in NvU32 displayId, __inout NV_HDR_COLOR_DATA *pHdrColorData);
private delegate NVAPI_STATUS Disp_ColorControlDelegate(
[In] UInt32 displayId,
[In][Out] ref NV_COLOR_DATA_V5 colorData);
private static readonly Disp_ColorControlDelegate Disp_ColorControlInternal;
/// <summary>
//! This API gets and sets the color capabilities of the display.
/// <param name="displayId"></param>
/// <param name="colorData"></param>
/// <returns></returns>
public static NVAPI_STATUS NvAPI_Disp_ColorControl(UInt32 displayId, ref NV_COLOR_DATA_V5 colorData)
{
NVAPI_STATUS status;
colorData.Version = NVImport.NV_COLOR_DATA_V5_VER;
if (Disp_ColorControlInternal != null) { status = Disp_ColorControlInternal(displayId, ref colorData); }
else { status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; }
return status;
}
//NVAPI_INTERFACE NvAPI_DISP_EnumCustomDisplay(__in NvU32 displayId, __inout NV_HDR_CAPABILITIES *pHdrCapabilities);
private delegate NVAPI_STATUS Disp_EnumCustomDisplayDelegate(
[In] UInt32 displayId,
[In] UInt32 index,
[In][Out] ref NV_CUSTOM_DISPLAY_V1 pCustDisp);
private static readonly Disp_EnumCustomDisplayDelegate Disp_EnumCustomDisplayInternal;
/// <summary>
//! This API gets High Dynamic Range (HDR) capabilities of the display.
/// <param name="displayId"></param>
/// <param name="pHdrCapabilities"></param>
/// <returns></returns>
public static NVAPI_STATUS NvAPI_DISP_EnumCustomDisplay(UInt32 displayId, UInt32 index, ref NV_CUSTOM_DISPLAY_V1 pCustDisp)
{
NVAPI_STATUS status;
pCustDisp.Version = NVImport.NV_CUSTOM_DISPLAY_V1_VER;
pCustDisp.SourcePartition = new NV_VIEWPORTF(0, 0, 1, 1);
if (Disp_EnumCustomDisplayInternal != null) { status = Disp_EnumCustomDisplayInternal(displayId, index, ref pCustDisp); }
else { status = NVAPI_STATUS.NVAPI_FUNCTION_NOT_FOUND; }
return status;
}
//NVAPI_INTERFACE NvAPI_GPU_GetFullName(NvPhysicalGpuHandle hPhysicalGpu, NvAPI_ShortString szName); //NVAPI_INTERFACE NvAPI_GPU_GetFullName(NvPhysicalGpuHandle hPhysicalGpu, NvAPI_ShortString szName);
private delegate NVAPI_STATUS GPU_GetFullNameDelegate( private delegate NVAPI_STATUS GPU_GetFullNameDelegate(
[In] PhysicalGpuHandle gpuHandle, [In] PhysicalGpuHandle gpuHandle,

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,8 @@ namespace DisplayMagicianShared
public string Name; public string Name;
public string Library; public string Library;
public bool IsPrimary; public bool IsPrimary;
public bool IsClone;
public int ClonedCopies;
public Color Colour; public Color Colour;
public string DisplayConnector; public string DisplayConnector;
internal bool HDRSupported; internal bool HDRSupported;
@ -452,10 +454,11 @@ namespace DisplayMagicianShared
public virtual bool PreSave() public virtual bool PreSave()
{ {
// Prepare our profile data for saving // Prepare our profile data for saving
if (_profileDisplayIdentifiers.Count == 0) // Disabling as this should never happen now
/*if (_profileDisplayIdentifiers.Count == 0)
{ {
_profileDisplayIdentifiers = ProfileRepository.GetCurrentDisplayIdentifiers(); _profileDisplayIdentifiers = ProfileRepository.GetCurrentDisplayIdentifiers();
} }*/
// Return if it is valid and we should continue // Return if it is valid and we should continue
return IsValid(); return IsValid();
@ -464,82 +467,50 @@ namespace DisplayMagicianShared
public bool CreateProfileFromCurrentDisplaySettings() public bool CreateProfileFromCurrentDisplaySettings()
{ {
// Create defaults for NVIDIA and AMD so that the JSON file can save properly // Calling the 3 different libraries automatically gets the different configs from each of the 3 video libraries.
// If the video library isn't in use then it also fills in the defaults so that the JSON file can save properly
// (C# Structs populate with default values which mean that arrays start with null) // (C# Structs populate with default values which mean that arrays start with null)
if (VideoMode == VIDEO_MODE.NVIDIA && NVIDIALibrary.GetLibrary().IsInstalled) try
{ {
NVIDIALibrary nvidiaLibrary = NVIDIALibrary.GetLibrary(); NVIDIALibrary nvidiaLibrary = NVIDIALibrary.GetLibrary();
if (nvidiaLibrary.IsInstalled)
{
// Create the profile data from the current config
_nvidiaDisplayConfig = nvidiaLibrary.GetActiveConfig();
_windowsDisplayConfig = WinLibrary.GetLibrary().GetActiveConfig();
_profileDisplayIdentifiers = nvidiaLibrary.GetCurrentDisplayIdentifiers();
// Now, since the ActiveProfile has changed, we need to regenerate screen positions
_screens = GetScreenPositions();
return true;
}
else
{
return false;
}
}
else if(VideoMode == VIDEO_MODE.AMD && AMDLibrary.GetLibrary().IsInstalled)
{
AMDLibrary amdLibrary = AMDLibrary.GetLibrary(); AMDLibrary amdLibrary = AMDLibrary.GetLibrary();
if (amdLibrary.IsInstalled)
{
// Create the profile data from the current config
_amdDisplayConfig = amdLibrary.GetActiveConfig();
_windowsDisplayConfig = WinLibrary.GetLibrary().GetActiveConfig();
_profileDisplayIdentifiers = amdLibrary.GetCurrentDisplayIdentifiers();
// Now, since the ActiveProfile has changed, we need to regenerate screen positions
_screens = GetScreenPositions();
return true;
}
else
{
return false;
}
}
else if (VideoMode == VIDEO_MODE.WINDOWS)
{
WinLibrary winLibrary = WinLibrary.GetLibrary(); WinLibrary winLibrary = WinLibrary.GetLibrary();
if (winLibrary.IsInstalled)
// For a library update to the latest version so that we pick up any new changes since the last update
if (VideoMode == VIDEO_MODE.NVIDIA && nvidiaLibrary.IsInstalled)
{ {
// Create the profile data from the current config nvidiaLibrary.UpdateActiveConfig();
_windowsDisplayConfig = winLibrary.GetActiveConfig(); winLibrary.UpdateActiveConfig();
_profileDisplayIdentifiers = winLibrary.GetCurrentDisplayIdentifiers(); }
else if (VideoMode == VIDEO_MODE.AMD && amdLibrary.IsInstalled)
{
amdLibrary.UpdateActiveConfig();
winLibrary.UpdateActiveConfig();
}
else
{
winLibrary.UpdateActiveConfig();
}
// Grab the profile data from the current stored config (that we just updated)
_nvidiaDisplayConfig = nvidiaLibrary.ActiveDisplayConfig;
_amdDisplayConfig = amdLibrary.ActiveDisplayConfig;
_windowsDisplayConfig = winLibrary.ActiveDisplayConfig;
_profileDisplayIdentifiers = nvidiaLibrary.CurrentDisplayIdentifiers;
// Now, since the ActiveProfile has changed, we need to regenerate screen positions // Now, since the ActiveProfile has changed, we need to regenerate screen positions
_screens = GetScreenPositions(); _screens = GetScreenPositions();
return true; return true;
} }
else catch (Exception ex)
{ {
SharedLogger.logger.Error(ex, $"ProfileRepository/CreateProfileFromCurrentDisplaySettings: Exception getting the config settings!");
return false; return false;
} }
} }
else
{
SharedLogger.logger.Error($"ProfileRepository/CreateProfileFromCurrentDisplaySettings: Tried to use an unknown video mode!");
return false;
}
}
/*public bool PerformPostLoadingTasks()
{
// First thing we do is to set up the Screens
//_screens = GetScreenPositions();
return true;
}*/
// ReSharper disable once FunctionComplexityOverflow // ReSharper disable once FunctionComplexityOverflow
@ -668,6 +639,7 @@ namespace DisplayMagicianShared
} }
else else
{ {
SharedLogger.logger.Warn($"ProfileRepository/IsPossibleRefresh: We have a current video mode we don't understand, or it's not installed! The current video mode is {ProfileRepository.CurrentVideoMode}. The profile {Name} has a {VideoMode.ToString("G")} video mode and NVIDIALibrary IsInstalled is {NVIDIALibrary.GetLibrary().IsInstalled}, AMDLibrary IsInstalled is {AMDLibrary.GetLibrary().IsInstalled} and WinLibrary IsInstalled is {WinLibrary.GetLibrary().IsInstalled} ");
_isPossible = false; _isPossible = false;
} }
} }
@ -681,7 +653,7 @@ namespace DisplayMagicianShared
WinLibrary winLibrary = WinLibrary.GetLibrary(); WinLibrary winLibrary = WinLibrary.GetLibrary();
if (nvidiaLibrary.IsInstalled) if (nvidiaLibrary.IsInstalled)
{ {
if (!nvidiaLibrary.IsActiveConfig(_nvidiaDisplayConfig) && !winLibrary.IsActiveConfig(_windowsDisplayConfig)) if (!winLibrary.IsActiveConfig(_windowsDisplayConfig) || !nvidiaLibrary.IsActiveConfig(_nvidiaDisplayConfig))
{ {
if (nvidiaLibrary.IsPossibleConfig(_nvidiaDisplayConfig)) if (nvidiaLibrary.IsPossibleConfig(_nvidiaDisplayConfig))
{ {
@ -701,10 +673,19 @@ namespace DisplayMagicianShared
bool itWorkedforWindows = winLibrary.SetActiveConfig(_windowsDisplayConfig); bool itWorkedforWindows = winLibrary.SetActiveConfig(_windowsDisplayConfig);
if (itWorkedforWindows) if (itWorkedforWindows)
{ {
SharedLogger.logger.Trace($"ProfileRepository/SetActive: The Windows CCD display settings within profile {Name} were successfully applied."); bool itWorkedforNVIDIAColor = nvidiaLibrary.SetActiveConfigOverride(_nvidiaDisplayConfig);
if (itWorkedforNVIDIAColor)
{
SharedLogger.logger.Trace($"NVIDIAInfo/loadFromFile: The NVIDIA display settings that override windows within the profile {Name} were successfully applied.");
return true; return true;
} }
else else
{
SharedLogger.logger.Trace($"NVIDIAInfo/loadFromFile: The NVIDIA display settings that override windows within the profile {Name} were NOT applied correctly.");
}
}
else
{ {
SharedLogger.logger.Trace($"ProfileRepository/SetActive: The Windows CCD display settings within profile {Name} were NOT applied correctly."); SharedLogger.logger.Trace($"ProfileRepository/SetActive: The Windows CCD display settings within profile {Name} were NOT applied correctly.");
} }
@ -733,7 +714,7 @@ namespace DisplayMagicianShared
WinLibrary winLibrary = WinLibrary.GetLibrary(); WinLibrary winLibrary = WinLibrary.GetLibrary();
if (amdLibrary.IsInstalled) if (amdLibrary.IsInstalled)
{ {
if (!amdLibrary.IsActiveConfig(_amdDisplayConfig) && !winLibrary.IsActiveConfig(_windowsDisplayConfig)) if (!winLibrary.IsActiveConfig(_windowsDisplayConfig) || !amdLibrary.IsActiveConfig(_amdDisplayConfig))
{ {
if (amdLibrary.IsPossibleConfig(_amdDisplayConfig)) if (amdLibrary.IsPossibleConfig(_amdDisplayConfig))
{ {
@ -753,10 +734,19 @@ namespace DisplayMagicianShared
bool itWorkedforWindows = winLibrary.SetActiveConfig(_windowsDisplayConfig); bool itWorkedforWindows = winLibrary.SetActiveConfig(_windowsDisplayConfig);
if (itWorkedforWindows) if (itWorkedforWindows)
{ {
SharedLogger.logger.Trace($"ProfileRepository/SetActive: The Windows CCD display settings within profile {Name} were successfully applied."); bool itWorkedforAMDColor = amdLibrary.SetActiveConfigOverride(_amdDisplayConfig);
if (itWorkedforAMDColor)
{
SharedLogger.logger.Trace($"AMDInfo/loadFromFile: The AMD display settings that override windows within the profile {Name} were successfully applied.");
return true; return true;
} }
else else
{
SharedLogger.logger.Trace($"AMDInfo/loadFromFile: The AMD display settings that override windows within the profile {Name} were NOT applied correctly.");
}
}
else
{ {
SharedLogger.logger.Trace($"ProfileRepository/SetActive: The Windows CCD display settings within profile {Name} were NOT applied correctly."); SharedLogger.logger.Trace($"ProfileRepository/SetActive: The Windows CCD display settings within profile {Name} were NOT applied correctly.");
} }
@ -845,9 +835,6 @@ namespace DisplayMagicianShared
// Now we need to check for Spanned screens // Now we need to check for Spanned screens
if (_nvidiaDisplayConfig.MosaicConfig.IsMosaicEnabled) if (_nvidiaDisplayConfig.MosaicConfig.IsMosaicEnabled)
{ {
// TODO: Make the NVIDIA displays show the individual screens and overlap!
// Create a dictionary of all the screen sizes we want // Create a dictionary of all the screen sizes we want
//Dictionary<string,SpannedScreenPosition> MosaicScreens = new Dictionary<string,SpannedScreenPosition>(); //Dictionary<string,SpannedScreenPosition> MosaicScreens = new Dictionary<string,SpannedScreenPosition>();
@ -927,12 +914,54 @@ namespace DisplayMagicianShared
screen.SpannedScreens.Add(spannedScreen); screen.SpannedScreens.Add(spannedScreen);
} }
// Need to look for the Windows layout details now we know the size of this display
// Set some basics about the screen
try
{
string displayId = _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Displays[0].DisplayId.ToString();
string windowsDisplayName = _nvidiaDisplayConfig.DisplayNames[displayId];
List<uint> sourceIndexes = _windowsDisplayConfig.DisplaySources[windowsDisplayName];
for (int x = 0; x < _windowsDisplayConfig.DisplayConfigModes.Length; x++)
{
// Skip this if its not a source info config type
if (_windowsDisplayConfig.DisplayConfigModes[x].InfoType != DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE)
{
continue;
}
// If the source index matches the index of the source info object we're looking at, then process it!
if (sourceIndexes.Contains(_windowsDisplayConfig.DisplayConfigModes[x].Id))
{
screen.Name = displayId.ToString();
screen.ScreenX = (int)_windowsDisplayConfig.DisplayConfigModes[x].SourceMode.Position.X;
screen.ScreenY = (int)_windowsDisplayConfig.DisplayConfigModes[x].SourceMode.Position.Y;
screen.ScreenWidth = (int)_windowsDisplayConfig.DisplayConfigModes[x].SourceMode.Width;
screen.ScreenHeight = (int)_windowsDisplayConfig.DisplayConfigModes[x].SourceMode.Height;
break;
}
}
}
catch (KeyNotFoundException ex)
{
// Thrown if the Windows display doesn't match the NVIDIA display.
// Typically happens during configuration of a new Mosaic mode.
// If we hit this issue, then we just want to skip over it, as we can update it later when the user pushes the button.
// This only happens due to the auto detection stuff functionality we have built in to try and update as quickly as we can.
// So its something that we can safely ignore if we hit this exception as it is part of the expect behaviour
continue;
}
catch (Exception ex)
{
// Some other exception has occurred and we need to report it.
//screen.Name = targetId.ToString(); //screen.Name = targetId.ToString();
//screen.DisplayConnector = displayMode.DisplayConnector; //screen.DisplayConnector = displayMode.DisplayConnector;
screen.ScreenX = (int)overallX; screen.ScreenX = (int)overallX;
screen.ScreenY = (int)overallY; screen.ScreenY = (int)overallY;
screen.ScreenWidth = (int)overallWidth; screen.ScreenWidth = (int)overallWidth;
screen.ScreenHeight = (int)overallHeight; screen.ScreenHeight = (int)overallHeight;
}
// If we're at the 0,0 coordinate then we're the primary monitor // If we're at the 0,0 coordinate then we're the primary monitor
if (screen.ScreenX == 0 && screen.ScreenY == 0) if (screen.ScreenX == 0 && screen.ScreenY == 0)
@ -953,9 +982,9 @@ namespace DisplayMagicianShared
// Set some basics about the screen // Set some basics about the screen
try try
{ {
uint displayId = _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Displays[0].DisplayId; string displayId = _nvidiaDisplayConfig.MosaicConfig.MosaicGridTopos[i].Displays[0].DisplayId.ToString();
string windowsDisplayName = _nvidiaDisplayConfig.DisplayNames[displayId]; string windowsDisplayName = _nvidiaDisplayConfig.DisplayNames[displayId];
uint sourceIndex = _windowsDisplayConfig.DisplaySources[windowsDisplayName]; List<uint> sourceIndexes = _windowsDisplayConfig.DisplaySources[windowsDisplayName];
for (int x = 0; x < _windowsDisplayConfig.DisplayConfigModes.Length; x++) for (int x = 0; x < _windowsDisplayConfig.DisplayConfigModes.Length; x++)
{ {
// Skip this if its not a source info config type // Skip this if its not a source info config type
@ -965,7 +994,7 @@ namespace DisplayMagicianShared
} }
// If the source index matches the index of the source info object we're looking at, then process it! // If the source index matches the index of the source info object we're looking at, then process it!
if (_windowsDisplayConfig.DisplayConfigModes[x].Id == sourceIndex) if (sourceIndexes.Contains(_windowsDisplayConfig.DisplayConfigModes[x].Id))
{ {
screen.Name = displayId.ToString(); screen.Name = displayId.ToString();
@ -1021,6 +1050,22 @@ namespace DisplayMagicianShared
UInt32 sourceId = path.SourceInfo.Id; UInt32 sourceId = path.SourceInfo.Id;
UInt32 targetId = path.TargetInfo.Id; UInt32 targetId = path.TargetInfo.Id;
screen.IsClone = false;
screen.ClonedCopies = 0;
foreach (var displaySource in _windowsDisplayConfig.DisplaySources)
{
if (displaySource.Value.Contains(sourceId))
{
if (displaySource.Value.Count > 1)
{
// We have a cloned display
screen.IsClone = true;
screen.ClonedCopies = displaySource.Value.Count;
}
break;
}
}
// Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies // Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies
foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes) foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes)
@ -1251,6 +1296,21 @@ namespace DisplayMagicianShared
UInt32 sourceId = path.SourceInfo.Id; UInt32 sourceId = path.SourceInfo.Id;
UInt32 targetId = path.TargetInfo.Id; UInt32 targetId = path.TargetInfo.Id;
screen.IsClone = false;
screen.ClonedCopies = 0;
foreach (var displaySource in _windowsDisplayConfig.DisplaySources)
{
if (displaySource.Value.Contains(sourceId))
{
if (displaySource.Value.Count > 1)
{
// We have a cloned display
screen.IsClone = true;
screen.ClonedCopies = displaySource.Value.Count;
}
break;
}
}
// Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies // Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies
foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes) foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes)
@ -1342,6 +1402,21 @@ namespace DisplayMagicianShared
UInt32 sourceId = path.SourceInfo.Id; UInt32 sourceId = path.SourceInfo.Id;
UInt32 targetId = path.TargetInfo.Id; UInt32 targetId = path.TargetInfo.Id;
screen.IsClone = false;
screen.ClonedCopies = 0;
foreach (var displaySource in _windowsDisplayConfig.DisplaySources)
{
if (displaySource.Value.Contains(sourceId))
{
if (displaySource.Value.Count > 1)
{
// We have a cloned display
screen.IsClone = true;
screen.ClonedCopies = displaySource.Value.Count;
}
break;
}
}
// Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies // Go through the screens as Windows knows them, and then enhance the info with Mosaic data if it applies
foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes) foreach (DISPLAYCONFIG_MODE_INFO displayMode in _windowsDisplayConfig.DisplayConfigModes)
@ -1524,6 +1599,7 @@ namespace DisplayMagicianShared
{ {
return (Name ?? Language.UN_TITLED_PROFILE); return (Name ?? Language.UN_TITLED_PROFILE);
} }
} }
} }

View File

@ -11,6 +11,7 @@ using DisplayMagicianShared.AMD;
using DisplayMagicianShared.NVIDIA; using DisplayMagicianShared.NVIDIA;
using DisplayMagicianShared.Windows; using DisplayMagicianShared.Windows;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using Newtonsoft.Json.Linq;
namespace DisplayMagicianShared namespace DisplayMagicianShared
{ {
@ -62,7 +63,7 @@ namespace DisplayMagicianShared
public static string AppIconPath = System.IO.Path.Combine(AppDataPath, $"Icons"); public static string AppIconPath = System.IO.Path.Combine(AppDataPath, $"Icons");
public static string AppDisplayMagicianIconFilename = System.IO.Path.Combine(AppIconPath, @"DisplayMagician.ico"); 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 AppProfileStoragePath = System.IO.Path.Combine(AppDataPath, $"Profiles");
private static readonly string _profileStorageJsonFileName = System.IO.Path.Combine(AppProfileStoragePath, $"DisplayProfiles_2.0.json"); private static readonly string _profileStorageJsonFileName = System.IO.Path.Combine(AppProfileStoragePath, $"DisplayProfiles_2.1.json");
@ -586,9 +587,6 @@ namespace DisplayMagicianShared
public static void UpdateActiveProfile() public static void UpdateActiveProfile()
{ {
//ProfileItem activeProfile;
SharedLogger.logger.Debug($"ProfileRepository/UpdateActiveProfile: Updating the profile currently active (in use now)."); SharedLogger.logger.Debug($"ProfileRepository/UpdateActiveProfile: Updating the profile currently active (in use now).");
ProfileItem profile; ProfileItem profile;
@ -693,11 +691,17 @@ namespace DisplayMagicianShared
SharedLogger.logger.Error(ex, $"ProfileRepository/LoadProfiles: Tried to read the JSON file {_profileStorageJsonFileName} to memory but File.ReadAllTextthrew an exception."); SharedLogger.logger.Error(ex, $"ProfileRepository/LoadProfiles: Tried to read the JSON file {_profileStorageJsonFileName} to memory but File.ReadAllTextthrew an exception.");
} }
// Migrate any previous entries to the latest version of the file format to the latest one
json = MigrateJsonToLatestVersion(json);
if (!string.IsNullOrWhiteSpace(json)) if (!string.IsNullOrWhiteSpace(json))
{ {
List<string> jsonErrors = new List<string>();
try try
{ {
_allProfiles = JsonConvert.DeserializeObject<List<ProfileItem>>(json, new JsonSerializerSettings JsonSerializerSettings mySerializerSettings = new JsonSerializerSettings
{ {
MissingMemberHandling = MissingMemberHandling.Error, MissingMemberHandling = MissingMemberHandling.Error,
NullValueHandling = NullValueHandling.Include, NullValueHandling = NullValueHandling.Include,
@ -706,13 +710,33 @@ namespace DisplayMagicianShared
//DefaultValueHandling = DefaultValueHandling.Ignore, //DefaultValueHandling = DefaultValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto, TypeNameHandling = TypeNameHandling.Auto,
ObjectCreationHandling = ObjectCreationHandling.Replace, ObjectCreationHandling = ObjectCreationHandling.Replace,
});
Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
jsonErrors.Add($"JSON.net Error: {args.ErrorContext.Error.Source}:{args.ErrorContext.Error.StackTrace} - {args.ErrorContext.Error.Message} | InnerException:{args.ErrorContext.Error.InnerException.Source}:{args.ErrorContext.Error.InnerException.StackTrace} - {args.ErrorContext.Error.InnerException.Message}");
args.ErrorContext.Handled = true;
},
};
_allProfiles = JsonConvert.DeserializeObject<List<ProfileItem>>(json, mySerializerSettings);
} }
catch (Exception ex) catch (Exception ex)
{ {
SharedLogger.logger.Error(ex, $"ProfileRepository/LoadProfiles: Tried to parse the JSON in the {_profileStorageJsonFileName} but the JsonConvert threw an exception."); SharedLogger.logger.Error(ex, $"ProfileRepository/LoadProfiles: Tried to parse the JSON in the {_profileStorageJsonFileName} but the JsonConvert threw an exception.");
} }
// If we have any JSON.net errors, then we need to records them in the logs
if (jsonErrors.Count > 0)
{
foreach (string jsonError in jsonErrors)
{
SharedLogger.logger.Error($"ProfileRepository/LoadProfiles: {jsonErrors}");
}
}
// We need to try and
SharedLogger.logger.Debug($"ProfileRepository/LoadProfiles: Finding the current profile in the Profile Repository"); SharedLogger.logger.Debug($"ProfileRepository/LoadProfiles: Finding the current profile in the Profile Repository");
// Go through all the profiles and set up the needed structures (such as the Screens list) // Go through all the profiles and set up the needed structures (such as the Screens list)
@ -745,12 +769,128 @@ namespace DisplayMagicianShared
_profilesLoaded = true; _profilesLoaded = true;
// Update the current active profile // Update the current active profile
UpdateActiveProfile(); //UpdateActiveProfile();
IsPossibleRefresh(); IsPossibleRefresh();
return true; return true;
} }
public static string MigrateJsonToLatestVersion(string json)
{
bool changedJson = false;
JArray root = new JArray();
try
{
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Processing the Profiles json data to migrate any older feature to the latest version.");
root = JArray.Parse(json);
}
catch(JsonReaderException ex)
{
SharedLogger.logger.Error($"ProfileRepository/MigrateJsonToLatestVersion: JSONReaderException while trying to process the Profiles json data to migrate any older feature to the latest version.");
}
catch(Exception ex)
{
SharedLogger.logger.Error($"ProfileRepository/MigrateJsonToLatestVersion: Exception while trying to process the Profiles json data to migrate any older feature to the latest version.");
}
// We do the change we wre trying to do
try
{
/*// Now we try and add a default NVIDIA Color Settings if there isn't one
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing NVIDIA Color Config.");
// Create a default object
NVIDIA_DISPLAY_CONFIG myDefaultConfig = new NVIDIA_DISPLAY_CONFIG();
myDefaultConfig.ColorConfig.ColorData = new Dictionary<uint, NV_COLOR_DATA_V5>();
JObject defaultColorConfig = (JObject)JToken.FromObject(myDefaultConfig.ColorConfig);
for (int i=0; i < root.Count; i++)
{
JObject profile = (JObject)root[i];
JObject result = (JObject)profile.SelectToken("NVIDIADisplayConfig.ColorConfig.ColorData");
if (result == null)
{
JObject NVIDIADisplayConfig = (JObject)profile.SelectToken("NVIDIADisplayConfig");
NVIDIADisplayConfig.Add("ColorConfig",defaultColorConfig);
changedJson = true;
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Patched missing NVIDIA Color Config in profile {profile.SelectToken("Name")} (index {i}).");
}
}
// Now we try to patch in a Windows GDI device context into the json if there isnt one
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing Windows GDI Device Context.");
// Create a default object
Dictionary<string, GDI_DISPLAY_SETTING> GdiDisplaySettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
JObject defaultGdiDisplaySettings = (JObject)JToken.FromObject(GdiDisplaySettings);
for (int i = 0; i < root.Count; i++)
{
JObject profile = (JObject)root[i];
JObject result = (JObject)profile.SelectToken("WindowsDisplayConfig.GdiDisplaySettings");
if (result == null)
{
JObject WindowsDisplayConfig = (JObject)profile.SelectToken("WindowsDisplayConfig");
WindowsDisplayConfig.Add("GdiDisplaySettings", defaultGdiDisplaySettings);
changedJson = true;
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Patched missing Windows GDI Device Context in profile {profile.SelectToken("Name")} (index {i}).");
}
}
// Now we try to convert the individual sourceids into a list of source ids to cope with cloned devices
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Looking for missing Windows GDI Device Context.");
// Create a default object
List<uint> WinDisplaySourcesList = new List<uint>();
//JObject WinDisplaySources = (JObject)JToken.FromObject(WinDisplaySourcesList);
for (int i = 0; i < root.Count; i++)
{
JObject profile = (JObject)root[i];
try
{
JObject WindowsDisplaySources = (JObject)profile.SelectToken("WindowsDisplayConfig.DisplaySources");
Dictionary<string, List<uint>> existingDisplaySources = WindowsDisplaySources.ToObject<Dictionary<string, List<uint>>>();
}
catch (Exception ex)
{
JObject WindowsDisplaySources = (JObject)profile.SelectToken("WindowsDisplayConfig.DisplaySources");
//foreach (var displaySource in WindowsDisplaySources.ToObject<Dictionary<string,uint>>())
Dictionary<string, uint> existingDisplaySources = WindowsDisplaySources.ToObject<Dictionary<string, uint>>();
Dictionary<string, List<uint>> newDisplaySources = new Dictionary<string, List<uint>>();
foreach (var sourceName in existingDisplaySources.Keys)
{
List<uint> newList = new List<uint>();
newList.Add((uint)existingDisplaySources[sourceName]);
newDisplaySources[sourceName] = newList;
}
JObject newSourcesDict = JObject.FromObject(newDisplaySources);
JToken WindowsDisplayConfig = (JToken)profile.SelectToken("WindowsDisplayConfig.DisplaySources");
WindowsDisplayConfig.Replace(newSourcesDict);
changedJson = true;
SharedLogger.logger.Trace($"ProfileRepository/MigrateJsonToLatestVersion: Patched missing Windows GDI Device Context in profile {profile.SelectToken("Name")} (index {i}).");
}
} */
}
catch (JsonReaderException ex)
{
SharedLogger.logger.Error($"ProfileRepository/MigrateJsonToLatestVersion: JSONReaderException while trying to process the Profiles json data to migrate any older feature to the latest version.");
}
catch (Exception ex)
{
SharedLogger.logger.Error($"ProfileRepository/MigrateJsonToLatestVersion: Exception while trying to process the Profiles json data to migrate any older feature to the latest version.");
}
// Now write the changed json to the json string but only if we've changed something
if (changedJson)
{
json = root.ToString(Formatting.Indented);
}
return json;
}
public static bool SaveProfiles() public static bool SaveProfiles()
{ {
SharedLogger.logger.Debug($"ProfileRepository/SaveProfiles: Attempting to save the profiles repository to the {AppProfileStoragePath}."); SharedLogger.logger.Debug($"ProfileRepository/SaveProfiles: Attempting to save the profiles repository to the {AppProfileStoragePath}.");
@ -782,11 +922,14 @@ namespace DisplayMagicianShared
{ {
SharedLogger.logger.Debug($"ProfileRepository/SaveProfiles: Profiles folder {AppProfileStoragePath} exists."); SharedLogger.logger.Debug($"ProfileRepository/SaveProfiles: Profiles folder {AppProfileStoragePath} exists.");
} }
List<string> jsonErrors = new List<string>();
try try
{ {
SharedLogger.logger.Debug($"ProfileRepository/SaveProfiles: Converting the objects to JSON format."); SharedLogger.logger.Debug($"ProfileRepository/SaveProfiles: Converting the objects to JSON format.");
var json = JsonConvert.SerializeObject(_allProfiles, Formatting.Indented, new JsonSerializerSettings JsonSerializerSettings mySerializerSettings = new JsonSerializerSettings
{ {
NullValueHandling = NullValueHandling.Include, NullValueHandling = NullValueHandling.Include,
//NullValueHandling = NullValueHandling.Ignore, //NullValueHandling = NullValueHandling.Ignore,
@ -795,7 +938,13 @@ namespace DisplayMagicianShared
TypeNameHandling = TypeNameHandling.Auto, TypeNameHandling = TypeNameHandling.Auto,
MissingMemberHandling = MissingMemberHandling.Error, MissingMemberHandling = MissingMemberHandling.Error,
ObjectCreationHandling = ObjectCreationHandling.Replace, ObjectCreationHandling = ObjectCreationHandling.Replace,
}); Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
jsonErrors.Add($"JSON.net Error: {args.ErrorContext.Error.Source}:{args.ErrorContext.Error.StackTrace} - {args.ErrorContext.Error.Message} | InnerException:{args.ErrorContext.Error.InnerException.Source}:{args.ErrorContext.Error.InnerException.StackTrace} - {args.ErrorContext.Error.InnerException.Message}");
args.ErrorContext.Handled = true;
},
};
var json = JsonConvert.SerializeObject(_allProfiles, Formatting.Indented, mySerializerSettings);
if (!string.IsNullOrWhiteSpace(json)) if (!string.IsNullOrWhiteSpace(json))
@ -811,6 +960,15 @@ namespace DisplayMagicianShared
SharedLogger.logger.Error(ex, $"ProfileRepository/SaveProfiles: Unable to save the profile repository to the {_profileStorageJsonFileName}."); SharedLogger.logger.Error(ex, $"ProfileRepository/SaveProfiles: Unable to save the profile repository to the {_profileStorageJsonFileName}.");
} }
// If we have any JSON.net errors, then we need to records them in the logs
if (jsonErrors.Count > 0)
{
foreach (string jsonError in jsonErrors)
{
SharedLogger.logger.Error($"ProfileRepository/SaveProfiles: {jsonErrors}");
}
}
return false; return false;
} }
@ -840,7 +998,7 @@ namespace DisplayMagicianShared
{ {
// We need to refresh the cached answer // We need to refresh the cached answer
// Get the list of connected devices // Get the list of connected devices
ConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers(); //ConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers();
if (_profilesLoaded && _allProfiles.Count > 0) if (_profilesLoaded && _allProfiles.Count > 0)
{ {
@ -871,15 +1029,15 @@ namespace DisplayMagicianShared
{ {
if (_currentVideoMode == VIDEO_MODE.NVIDIA && NVIDIALibrary.GetLibrary().IsInstalled) if (_currentVideoMode == VIDEO_MODE.NVIDIA && NVIDIALibrary.GetLibrary().IsInstalled)
{ {
return NVIDIALibrary.GetLibrary().GetCurrentDisplayIdentifiers(); return NVIDIALibrary.GetLibrary().CurrentDisplayIdentifiers;
} }
else if (_currentVideoMode == VIDEO_MODE.AMD && AMDLibrary.GetLibrary().IsInstalled) else if (_currentVideoMode == VIDEO_MODE.AMD && AMDLibrary.GetLibrary().IsInstalled)
{ {
return AMDLibrary.GetLibrary().GetCurrentDisplayIdentifiers(); return AMDLibrary.GetLibrary().CurrentDisplayIdentifiers;
} }
else else
{ {
return WinLibrary.GetLibrary().GetCurrentDisplayIdentifiers(); return WinLibrary.GetLibrary().CurrentDisplayIdentifiers;
} }
} }
@ -940,6 +1098,8 @@ namespace DisplayMagicianShared
else else
{ {
SharedLogger.logger.Trace($"ProfileRepository/ApplyProfile: Successfully applied the {profile.VideoMode.ToString("G")} Profile!"); SharedLogger.logger.Trace($"ProfileRepository/ApplyProfile: Successfully applied the {profile.VideoMode.ToString("G")} Profile!");
// We also need to update the ActiveProfile so that DisplayMagician knows things have changed
ProfileRepository.UpdateActiveProfile();
return ApplyProfileResult.Successful; return ApplyProfileResult.Successful;
} }
@ -983,14 +1143,13 @@ namespace DisplayMagicianShared
if (wasDisplayChangeSuccessful) if (wasDisplayChangeSuccessful)
{ {
result = "was successful"; result = "was successful";
ProfileRepository.UpdateActiveProfile();
} }
// Display the TimeSpan time and result. // Display the TimeSpan time and result.
SharedLogger.logger.Debug($"ProfileRepository/ApplyProfile: Display change attempt took {ts.Minutes}:{ts.Seconds}.{ts.Milliseconds} and {result}."); SharedLogger.logger.Debug($"ProfileRepository/ApplyProfile: Display change attempt took {ts.Minutes}:{ts.Seconds}.{ts.Milliseconds} and {result}.");
} }
ProfileRepository.UpdateActiveProfile();
return ApplyProfileResult.Successful; return ApplyProfileResult.Successful;
} }
@ -1045,7 +1204,6 @@ namespace DisplayMagicianShared
{ {
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the NVIDIA NVAPI library."); SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the NVIDIA NVAPI library.");
nvidiaLibrary = new NVIDIALibrary(); nvidiaLibrary = new NVIDIALibrary();
_currentVideoMode = VIDEO_MODE.NVIDIA;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1060,7 +1218,6 @@ namespace DisplayMagicianShared
{ {
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the AMD ADL library."); SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the AMD ADL library.");
amdLibrary = new AMDLibrary(); amdLibrary = new AMDLibrary();
_currentVideoMode = VIDEO_MODE.AMD;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -35,5 +35,5 @@ using System.Runtime.InteropServices;
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.0.0")] [assembly: AssemblyVersion("2.1.0.0")]
[assembly: AssemblyFileVersion("2.0.0.0")] [assembly: AssemblyFileVersion("2.1.0.0")]

View File

@ -123,6 +123,7 @@ namespace DisplayMagicianShared.UserControls
private void DrawView(Graphics g) private void DrawView(Graphics g)
{ {
var viewSize = ProfileIcon.CalculateViewSize(_profile.Screens, PaddingX, PaddingY); var viewSize = ProfileIcon.CalculateViewSize(_profile.Screens, PaddingX, PaddingY);
var factor = Math.Min(Width / viewSize.Width, Height / viewSize.Height); var factor = Math.Min(Width / viewSize.Width, Height / viewSize.Height);
g.ScaleTransform(factor, factor); g.ScaleTransform(factor, factor);
@ -207,6 +208,10 @@ namespace DisplayMagicianShared.UserControls
{ {
str = $"Primary Display{Environment.NewLine}" + str; str = $"Primary Display{Environment.NewLine}" + str;
} }
if (screen.IsClone)
{
str = str + $"(+{screen.ClonedCopies-1} Clone)";
}
DrawString(g, str, wordTextColour, selectedWordFont, wordRect.Size, wordRect.Location); DrawString(g, str, wordTextColour, selectedWordFont, wordRect.Size, wordRect.Location);

View File

@ -98,7 +98,7 @@ namespace DisplayMagicianShared.Windows
public enum DISPLAYCONFIG_TOPOLOGY_ID : uint public enum DISPLAYCONFIG_TOPOLOGY_ID : uint
{ {
Zero = 0x0, Zero = 0x0,
DISPLAYCONFIG_TOPOLOGY_public = 0x00000001, DISPLAYCONFIG_TOPOLOGY_INTERNAL = 0x00000001,
DISPLAYCONFIG_TOPOLOGY_CLONE = 0x00000002, DISPLAYCONFIG_TOPOLOGY_CLONE = 0x00000002,
DISPLAYCONFIG_TOPOLOGY_EXTEND = 0x00000004, DISPLAYCONFIG_TOPOLOGY_EXTEND = 0x00000004,
DISPLAYCONFIG_TOPOLOGY_EXTERNAL = 0x00000008, DISPLAYCONFIG_TOPOLOGY_EXTERNAL = 0x00000008,
@ -106,7 +106,7 @@ namespace DisplayMagicianShared.Windows
} }
[Flags] [Flags]
public enum DISPLAYCONFIG_PATH : uint public enum DISPLAYCONFIG_PATH_FLAGS : uint
{ {
Zero = 0x0, Zero = 0x0,
DISPLAYCONFIG_PATH_ACTIVE = 0x00000001, DISPLAYCONFIG_PATH_ACTIVE = 0x00000001,
@ -164,6 +164,7 @@ namespace DisplayMagicianShared.Windows
SDC_FORCE_MODE_ENUMERATION = 0x00001000, SDC_FORCE_MODE_ENUMERATION = 0x00001000,
SDC_ALLOW_PATH_ORDER_CHANGES = 0x00002000, SDC_ALLOW_PATH_ORDER_CHANGES = 0x00002000,
SDC_VIRTUAL_MODE_AWARE = 0x00008000, SDC_VIRTUAL_MODE_AWARE = 0x00008000,
SDC_VIRTUAL_REFRESH_RATE_AWARE = 0x00020000,
// Special common combinations (only set in this library) // Special common combinations (only set in this library)
TEST_IF_VALID_DISPLAYCONFIG = (SDC_VALIDATE | SDC_USE_SUPPLIED_DISPLAY_CONFIG), TEST_IF_VALID_DISPLAYCONFIG = (SDC_VALIDATE | SDC_USE_SUPPLIED_DISPLAY_CONFIG),
@ -566,7 +567,7 @@ namespace DisplayMagicianShared.Windows
public bool Equals(DISPLAYCONFIG_PATH_TARGET_INFO other) public bool Equals(DISPLAYCONFIG_PATH_TARGET_INFO other)
=> // AdapterId.Equals(other.AdapterId) && // Removed the AdapterId from the Equals, as it changes after reboot. => // AdapterId.Equals(other.AdapterId) && // Removed the AdapterId from the Equals, as it changes after reboot.
Id == other.Id && // Id == other.Id && // Removed as ID changes after reboot when the display is a cloned copy :(
ModeInfoIdx == other.ModeInfoIdx && ModeInfoIdx == other.ModeInfoIdx &&
OutputTechnology.Equals(other.OutputTechnology) && OutputTechnology.Equals(other.OutputTechnology) &&
Rotation.Equals(other.Rotation) && Rotation.Equals(other.Rotation) &&
@ -591,7 +592,7 @@ namespace DisplayMagicianShared.Windows
{ {
public DISPLAYCONFIG_PATH_SOURCE_INFO SourceInfo; public DISPLAYCONFIG_PATH_SOURCE_INFO SourceInfo;
public DISPLAYCONFIG_PATH_TARGET_INFO TargetInfo; public DISPLAYCONFIG_PATH_TARGET_INFO TargetInfo;
public uint Flags; public DISPLAYCONFIG_PATH_FLAGS Flags;
public override bool Equals(object obj) => obj is DISPLAYCONFIG_PATH_INFO other && this.Equals(other); public override bool Equals(object obj) => obj is DISPLAYCONFIG_PATH_INFO other && this.Equals(other);
public bool Equals(DISPLAYCONFIG_PATH_INFO other) public bool Equals(DISPLAYCONFIG_PATH_INFO other)
@ -637,22 +638,33 @@ namespace DisplayMagicianShared.Windows
if (InfoType != other.InfoType) if (InfoType != other.InfoType)
return false; return false;
// This happens when it is a target mode info block
if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET && if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET &&
Id == other.Id && Id == other.Id && // Disabling this check as as the Display ID it maps to will change after a switch from clone to non-clone profile, ruining the equality match
TargetMode.Equals(other.TargetMode)) TargetMode.Equals(other.TargetMode))
return true; return true;
// This happens when it is a source mode info block
if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE && if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE &&
//Id == other.Id && // Disabling this check as as the Display ID it maps to will change after a switch from surround to non-surround profile, ruining the equality match //Id == other.Id && // Disabling this check as as the Display ID it maps to will change after a switch from surround to non-surround profile, ruining the equality match
// Only seems to be a problem with the DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE options weirdly enough! // Only seems to be a problem with the DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE options weirdly enough!
SourceMode.Equals(other.SourceMode)) SourceMode.Equals(other.SourceMode))
return true; return true;
// This happens when it is a desktop image mode info block
if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE && if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE &&
Id == other.Id && Id == other.Id && // Disabling this check as as the Display ID it maps to will change after a switch from clone to non-clone profile, ruining the equality match
DesktopImageInfo.Equals(other.DesktopImageInfo)) DesktopImageInfo.Equals(other.DesktopImageInfo))
return true; return true;
// This happens when it is a clone - there is an extra entry with all zeros in it!
if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.Zero &&
//Id == other.Id && // Disabling this check as as the Display ID it maps to will change after a switch from clone to non-clone profile, ruining the equality match
DesktopImageInfo.Equals(other.DesktopImageInfo) &&
TargetMode.Equals(other.TargetMode) &&
SourceMode.Equals(other.SourceMode))
return true;
return false; return false;
} }
@ -661,15 +673,20 @@ namespace DisplayMagicianShared.Windows
{ {
if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET) if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET)
return (InfoType, Id, TargetMode).GetHashCode(); return (InfoType, Id, TargetMode).GetHashCode();
//return (InfoType, TargetMode).GetHashCode();
if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE) if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE)
return (InfoType, Id, SourceMode).GetHashCode(); //return (InfoType, Id, SourceMode).GetHashCode();
return (InfoType, SourceMode).GetHashCode();
if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE) if (InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE)
return (InfoType, Id, DesktopImageInfo).GetHashCode(); return (InfoType, Id, DesktopImageInfo).GetHashCode();
//return (InfoType, DesktopImageInfo).GetHashCode();
// otherwise we return everything // otherwise we return everything
return (InfoType, Id, TargetMode, SourceMode, DesktopImageInfo).GetHashCode(); return (InfoType, Id, TargetMode, SourceMode, DesktopImageInfo).GetHashCode();
//return (InfoType, TargetMode, SourceMode, DesktopImageInfo).GetHashCode();
} }
public static bool operator ==(DISPLAYCONFIG_MODE_INFO lhs, DISPLAYCONFIG_MODE_INFO rhs) => lhs.Equals(rhs); public static bool operator ==(DISPLAYCONFIG_MODE_INFO lhs, DISPLAYCONFIG_MODE_INFO rhs) => lhs.Equals(rhs);
@ -955,6 +972,19 @@ namespace DisplayMagicianShared.Windows
public int Right; public int Right;
public int Bottom; public int Bottom;
public RECTL(int left, int top, int right, int bottom)
{
this.Left = left;
this.Top = top;
this.Right = right;
this.Bottom = bottom;
}
public static RECTL FromXYWH(int x, int y, int width, int height)
{
return new RECTL(x, y, x + width, y + height);
}
public override bool Equals(object obj) => obj is RECTL other && this.Equals(other); public override bool Equals(object obj) => obj is RECTL other && this.Equals(other);
public bool Equals(RECTL other) public bool Equals(RECTL other)
=> Left == other.Left && => Left == other.Left &&
@ -977,6 +1007,7 @@ namespace DisplayMagicianShared.Windows
{ {
// Set some useful constants // Set some useful constants
public const SDC SDC_CCD_TEST_IF_VALID = (SDC.SDC_VALIDATE | SDC.SDC_USE_SUPPLIED_DISPLAY_CONFIG); 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;
// GetDisplayConfigBufferSizes // GetDisplayConfigBufferSizes

View File

@ -0,0 +1,823 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace DisplayMagicianShared.Windows
{
// 90% of this file is cribbed from WindowsDisplayAPI by Soroush Falahati
// The other 10% is from MikedouglasDev's ChangeScreenResolution
// https://github.com/mikedouglasdev/changescreenresolution/blob/master/ChangeScreenResolutionSolution/ChangeScreenResolution/SafeNativeMethods.cs
// and GemingLeader here: https://www.c-sharpcorner.com/uploadfile/GemingLeader/changing-display-settings-programmatically/
public enum CHANGE_DISPLAY_RESULTS
{
/// <summary>
/// Completed successfully
/// </summary>
Successful = 0,
/// <summary>
/// Changes needs restart
/// </summary>
Restart = 1,
/// <summary>
/// Failed to change and save setings
/// </summary>
Failed = -1,
/// <summary>
/// Invalid data provided
/// </summary>
BadMode = -2,
/// <summary>
/// Changes not updated
/// </summary>
NotUpdated = -3,
/// <summary>
/// Invalid flags provided
/// </summary>
BadFlags = -4,
/// <summary>
/// Bad parameters provided
/// </summary>
BadParam = -5,
/// <summary>
/// Bad Dual View mode used with mode
/// </summary>
BadDualView = -6
}
[Flags]
public enum CHANGE_DISPLAY_SETTINGS_FLAGS : UInt32
{
CDS_NONE = 0,
CDS_UPDATEREGISTRY = 0x00000001,
CDS_TEST = 0x00000002,
CDS_FULLSCREEN = 0x00000004,
CDS_GLOBAL = 0x00000008,
CDS_SET_PRIMARY = 0x00000010,
CDS_VIDEOPARAMETERS = 0x00000020,
CDS_ENABLE_UNSAFE_MODES = 0x00000100,
CDS_DISABLE_UNSAFE_MODES = 0x00000200,
CDS_RESET = 0x40000000,
CDS_RESET_EX = 0x20000000,
CDS_NORESET = 0x10000000
}
public enum DEVICE_CAPABILITY : Int32
{
DriverVersion = 0,
Technology = 2,
HorizontalSizeInMM = 4,
VerticalSizeInMM = 6,
HorizontalResolution = 8,
VerticalResolution = 10,
BitsPerPixel = 12,
Planes = 14,
NumberOfBrushes = 16,
NumberOfPens = 18,
NumberOfMarkers = 20,
NumberOfFonts = 22,
NumberOfColors = 24,
DeviceDescriptorSize = 26,
CurveCapabilities = 28,
LineCapabilities = 30,
PolygonalCapabilities = 32,
TextCapabilities = 34,
ClipCapabilities = 36,
RasterCapabilities = 38,
HorizontalAspect = 40,
VerticalAspect = 42,
HypotenuseAspect = 44,
//ShadeBlendingCapabilities = 45,
HorizontalLogicalPixels = 88,
VerticalLogicalPixels = 90,
PaletteSize = 104,
ReservedPaletteSize = 106,
ColorResolution = 108,
// Printer Only
PhysicalWidth = 110,
PhysicalHeight = 111,
PhysicalHorizontalMargin = 112,
PhysicalVerticalMargin = 113,
HorizontalScalingFactor = 114,
VerticalScalingFactor = 115,
// Display Only
VerticalRefreshRateInHz = 116,
DesktopVerticalResolution = 117,
DesktopHorizontalResolution = 118,
PreferredBLTAlignment = 119,
ShadeBlendingCapabilities = 120,
ColorManagementCapabilities = 121,
}
[Flags]
public enum DEVICE_MODE_FIELDS : UInt32
{
None = 0,
Position = 0x20,
DisplayOrientation = 0x80,
Color = 0x800,
Duplex = 0x1000,
YResolution = 0x2000,
TtOption = 0x4000,
Collate = 0x8000,
FormName = 0x10000,
LogPixels = 0x20000,
BitsPerPixel = 0x40000,
PelsWidth = 0x80000,
PelsHeight = 0x100000,
DisplayFlags = 0x200000,
DisplayFrequency = 0x400000,
DisplayFixedOutput = 0x20000000,
AllDisplay = Position |
DisplayOrientation |
YResolution |
BitsPerPixel |
PelsWidth |
PelsHeight |
DisplayFlags |
DisplayFrequency |
DisplayFixedOutput,
}
[Flags]
public enum DISPLAY_DEVICE_STATE_FLAGS : UInt32
{
/// <summary>
/// The device is part of the desktop.
/// </summary>
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
/// <summary>
/// The device is part of the desktop.
/// </summary>
PrimaryDevice = 0x4,
/// <summary>
/// Represents a pseudo device used to mirror application drawing for remoting or other purposes.
/// </summary>
MirroringDriver = 0x8,
/// <summary>
/// The device is VGA compatible.
/// </summary>
VGACompatible = 0x10,
/// <summary>
/// The device is removable; it cannot be the primary display.
/// </summary>
Removable = 0x20,
/// <summary>
/// The device has more display modes than its output devices support.
/// </summary>
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000
}
public enum DISPLAY_FIXED_OUTPUT : UInt32
{
/// <summary>
/// Default behavior
/// </summary>
Default = 0,
/// <summary>
/// Stretches the output to fit to the display
/// </summary>
Stretch = 1,
/// <summary>
/// Centers the output in the middle of the display
/// </summary>
Center = 2
}
[Flags]
public enum DISPLAY_FLAGS : UInt32
{
None = 0,
Grayscale = 1,
Interlaced = 2
}
public enum DISPLAY_ORIENTATION : UInt32
{
/// <summary>
/// No rotation
/// </summary>
Rotate0Degree = 0,
/// <summary>
/// 90 degree rotation
/// </summary>
Rotate90Degree = 1,
/// <summary>
/// 180 degree rotation
/// </summary>
Rotate180Degree = 2,
/// <summary>
/// 270 degree rotation
/// </summary>
Rotate270Degree = 3
}
public enum DISPLAY_SETTINGS_MODE : Int32
{
CurrentSettings = -1, // Retrieves current display mode
RegistrySettings = -2 // Retrieves current display mode stored within the registry.
}
public enum DISPLAY_TECHNOLOGY : Int32
{
Plotter = 0,
RasterDisplay = 1,
RasterPrinter = 2,
RasterCamera = 3,
CharacterStream = 4,
MetaFile = 5,
DisplayFile = 6,
}
public enum MONITOR_FROM_FLAG : UInt32
{
DefaultToNull = 0,
DefaultToPrimary = 1,
DefaultToNearest = 2,
}
[Flags]
public enum MONITOR_INFO_FLAGS : UInt32
{
None = 0,
Primary = 1
}
[StructLayout(LayoutKind.Sequential)]
public struct APP_BAR_DATA
{
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public ABE_EDGE uEdge;
public RECTL rc;
public ABS_SETTING lParam;
}
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd183565(v=vs.85).aspx
// https://www.c-sharpcorner.com/uploadfile/GemingLeader/changing-display-settings-programmatically/
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct DEVICE_MODE : IEquatable<DEVICE_MODE>
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
[FieldOffset(0)]
public string DeviceName;
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(32)]
public UInt16 SpecificationVersion;
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(34)]
public UInt16 DriverVersion;
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(36)]
public UInt16 Size;
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(38)]
public UInt16 DriverExtra;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(40)]
public DEVICE_MODE_FIELDS Fields;
[MarshalAs(UnmanagedType.Struct)]
[FieldOffset(44)]
public POINTL Position;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(52)]
public DISPLAY_ORIENTATION DisplayOrientation;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(56)]
public DISPLAY_FIXED_OUTPUT DisplayFixedOutput;
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(60)]
public Int16 Color;
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(62)]
public Int16 Duplex;
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(64)]
public Int16 YResolution;
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(66)]
public Int16 TrueTypeOption;
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(68)]
public Int16 Collate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
[FieldOffset(72)]
public string FormName;
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(102)]
public UInt16 LogicalInchPixels;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(104)]
public UInt32 BitsPerPixel;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(108)]
public UInt32 PixelsWidth;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(112)]
public UInt32 PixelsHeight;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(116)]
public DISPLAY_FLAGS DisplayFlags;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(120)]
public UInt32 DisplayFrequency;
public override bool Equals(object obj) => obj is DEVICE_MODE other && this.Equals(other);
public bool Equals(DEVICE_MODE other)
=> //DeviceName.Equals(other.DeviceName) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//SpecificationVersion == other.SpecificationVersion &&
//DriverVersion.Equals(other.DriverVersion) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//Size.Equals(other.Size) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//DriverExtra.Equals(other.DriverExtra) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//Fields.Equals(other.Fields) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//Position.Equals(other.Position) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
DisplayOrientation.Equals(other.DisplayOrientation) &&
//DisplayFixedOutput.Equals(other.DisplayFixedOutput) &&
//Color.Equals(other.Color) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//Duplex.Equals(other.Duplex) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//YResolution.Equals(other.YResolution) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//TrueTypeOption.Equals(other.TrueTypeOption) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//Collate.Equals(other.Collate) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//FormName.Equals(other.FormName) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//LogicalInchPixels.Equals(other.LogicalInchPixels) && // Removed specifically for DisplayMagician matching. Remove if you need true equality matching
BitsPerPixel.Equals(other.BitsPerPixel) &&
//PixelsWidth.Equals(other.PixelsWidth) &&
//PixelsHeight.Equals(other.PixelsHeight) &&
DisplayFlags.Equals(other.DisplayFlags) &&
DisplayFrequency == other.DisplayFrequency;
public override int GetHashCode()
{
// Removed specifically for DisplayMagician matching. Remove if you need true equality matching
//return (DeviceName, SpecificationVersion, DriverVersion, Size, DriverExtra, Fields, Position, DisplayOrientation, DisplayFixedOutput, Color, Duplex,
// YResolution, TrueTypeOption, Collate, FormName, LogicalInchPixels, BitsPerPixel, PixelsWidth, PixelsHeight, DisplayFlags, DisplayFrequency).GetHashCode();
return (DisplayOrientation, BitsPerPixel, DisplayFlags, DisplayFrequency).GetHashCode();
}
public static bool operator ==(DEVICE_MODE lhs, DEVICE_MODE rhs) => lhs.Equals(rhs);
public static bool operator !=(DEVICE_MODE lhs, DEVICE_MODE rhs) => !(lhs == rhs);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DISPLAY_DEVICE : IEquatable<DISPLAY_DEVICE>
{
[MarshalAs(UnmanagedType.U4)]
public UInt32 Size;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
[MarshalAs(UnmanagedType.U4)]
public DISPLAY_DEVICE_STATE_FLAGS StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
public override bool Equals(object obj) => obj is DISPLAY_DEVICE other && this.Equals(other);
public bool Equals(DISPLAY_DEVICE other)
=> Size == other.Size &&
// DeviceName == other.DeviceName && // Had to remove this as the device name often changes after a reboot!
DeviceString == other.DeviceString &&
//StateFlags == other.StateFlags &&
DeviceId == other.DeviceId;
//DeviceKey == other.DeviceKey;
public override int GetHashCode()
{
//return (Size, DeviceName, DeviceString, StateFlags, DeviceId, DeviceKey).GetHashCode();
return (Size, DeviceString, DeviceId).GetHashCode();
}
public static bool operator ==(DISPLAY_DEVICE lhs, DISPLAY_DEVICE rhs) => lhs.Equals(rhs);
public static bool operator !=(DISPLAY_DEVICE lhs, DISPLAY_DEVICE rhs) => !(lhs == rhs);
}
[StructLayout(LayoutKind.Sequential)]
public struct GAMMA_RAMP : IEquatable<GAMMA_RAMP>
{
public const int DataPoints = 256;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)]
public UInt16[] Red;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)]
public UInt16[] Green;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = DataPoints)]
public UInt16[] Blue;
public override bool Equals(object obj) => obj is GAMMA_RAMP other && this.Equals(other);
public bool Equals(GAMMA_RAMP other)
=> Red.SequenceEqual(other.Red) &&
Green.SequenceEqual(other.Green) &&
Blue.SequenceEqual(other.Blue);
public override int GetHashCode()
{
return (Red, Green, Blue).GetHashCode();
}
public static bool operator ==(GAMMA_RAMP lhs, GAMMA_RAMP rhs) => lhs.Equals(rhs);
public static bool operator !=(GAMMA_RAMP lhs, GAMMA_RAMP rhs) => !(lhs == rhs);
}
[StructLayout(LayoutKind.Sequential)]
public struct MONITOR_INFO : IEquatable<MONITOR_INFO>
{
internal UInt32 Size;
public RECTL Bounds;
public RECTL WorkingArea;
public MONITOR_INFO_FLAGS Flags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DisplayName;
public override bool Equals(object obj) => obj is MONITOR_INFO other && this.Equals(other);
public bool Equals(MONITOR_INFO other)
=> Size == other.Size &&
Bounds.Equals(other.Bounds) &&
WorkingArea.Equals(other.WorkingArea) &&
Flags == other.Flags &&
DisplayName == other.DisplayName;
public override int GetHashCode()
{
return (Size, Bounds, WorkingArea, Flags, DisplayName).GetHashCode();
}
public static bool operator ==(MONITOR_INFO lhs, MONITOR_INFO rhs) => lhs.Equals(rhs);
public static bool operator !=(MONITOR_INFO lhs, MONITOR_INFO rhs) => !(lhs == rhs);
}
public struct GDI_DISPLAY_SETTING : IEquatable<GDI_DISPLAY_SETTING>
{
public bool IsEnabled;
public bool IsPrimary;
public DISPLAY_DEVICE Device;
public DEVICE_MODE DeviceMode;
public override bool Equals(object obj) => obj is GDI_DISPLAY_SETTING other && this.Equals(other);
public bool Equals(GDI_DISPLAY_SETTING other)
=> IsEnabled == other.IsEnabled &&
//IsPrimary == other.IsPrimary &&
Device.Equals(other.Device) &&
DeviceMode.Equals(other.DeviceMode);
public override int GetHashCode()
{
//return (IsEnabled, IsPrimary, Device, DeviceMode).GetHashCode();
return (IsEnabled, Device, DeviceMode).GetHashCode();
}
public static bool operator ==(GDI_DISPLAY_SETTING lhs, GDI_DISPLAY_SETTING rhs) => lhs.Equals(rhs);
public static bool operator !=(GDI_DISPLAY_SETTING lhs, GDI_DISPLAY_SETTING rhs) => !(lhs == rhs);
}
internal class DCHandle : SafeHandle
{
private readonly bool _created;
private DCHandle(IntPtr handle, bool created) : base(handle, true)
{
_created = created;
}
public override bool IsInvalid
{
get => handle == IntPtr.Zero;
}
public static DCHandle CreateFromDevice(string screenName, string devicePath)
{
return new DCHandle(
GDIImport.CreateDC(screenName, devicePath, null, IntPtr.Zero),
true
);
}
public static DCHandle CreateFromScreen(string screenName)
{
return CreateFromDevice(screenName, screenName);
}
public static DCHandle CreateFromWindow(IntPtr windowHandle)
{
return new DCHandle(
GDIImport.GetDC(windowHandle),
true
);
}
public static DCHandle CreateGlobal()
{
return new DCHandle(
GDIImport.CreateDC("DISPLAY", null, null, IntPtr.Zero),
true
);
}
protected override bool ReleaseHandle()
{
return _created
? GDIImport.DeleteDC(this)
: GDIImport.ReleaseDC(IntPtr.Zero, this);
}
}
public enum ABM_MESSAGE : UInt32
{
ABM_NEW = 0x00000000, // Registers a new appbar and specifies the message identifier that the system should use to send notification messages to the appbar.
ABM_REMOVE = 0x00000001, // Unregisters an appbar, removing the bar from the system's internal list.
ABM_QUERYPOS = 0x00000002, // Requests a size and screen position for an appbar.
ABM_SETPOS = 0x00000003, // Sets the size and screen position of an appbar.
ABM_GETSTATE = 0x00000004, // Retrieves the autohide and always-on-top states of the Windows taskbar.
ABM_GETTASKBARPOS = 0x00000005, // Retrieves the bounding rectangle of the Windows taskbar. Note that this applies only to the system taskbar. Other objects, particularly toolbars supplied with third-party software, also can be present. As a result, some of the screen area not covered by the Windows taskbar might not be visible to the user. To retrieve the area of the screen not covered by both the taskbar and other app bars—the working area available to your application—, use the GetMonitorInfo function.
ABM_ACTIVATE = 0x00000006, // Notifies the system to activate or deactivate an appbar. The lParam member of the APPBARDATA pointed to by pData is set to TRUE to activate or FALSE to deactivate.
ABM_GETAUTOHIDEBAR = 0x00000007, // Retrieves the handle to the autohide appbar associated with a particular edge of the screen.
ABM_SETAUTOHIDEBAR = 0x00000008, // Registers or unregisters an autohide appbar for an edge of the screen.
ABM_WINDOWPOSCHANGED = 0x00000009, // Notifies the system when an appbar's position has changed.
ABM_SETSTATE = 0x0000000A, // Windows XP and later: Sets the state of the appbar's autohide and always-on-top attributes.
ABM_GETAUTOHIDEBAREX = 0x0000000B, // Windows XP and later: Retrieves the handle to the autohide appbar associated with a particular edge of a particular monitor.
ABM_SETAUTOHIDEBAREX = 0x0000000C, // Windows XP and later: Registers or unregisters an autohide appbar for an edge of a particular monitor.
}
public enum ABE_EDGE : UInt32
{
ABE_LEFT = 0,
ABE_TOP = 1,
ABE_RIGHT = 2,
ABE_BOTTOM = 3,
}
[Flags]
public enum ABS_SETTING : UInt32
{
ABS_AUTOHIDE = 0x1,
ABS_ALWAYSONTOP = 0x2,
}
class GDIImport
{
private const int ABS_NO_AUTOHIDE = 0x00;
private const int ABS_AUTOHIDE = 0x01;
[DllImport("user32", CharSet = CharSet.Ansi)]
public static extern CHANGE_DISPLAY_RESULTS ChangeDisplaySettingsEx(
string deviceName,
ref DEVICE_MODE devMode,
IntPtr handler,
CHANGE_DISPLAY_SETTINGS_FLAGS flags,
IntPtr param
);
[DllImport("user32", CharSet = CharSet.Ansi)]
public static extern CHANGE_DISPLAY_RESULTS ChangeDisplaySettingsEx(
string deviceName,
IntPtr devModePointer,
IntPtr handler,
CHANGE_DISPLAY_SETTINGS_FLAGS flags,
IntPtr param
);
[DllImport("user32", CharSet = CharSet.Ansi)]
public static extern bool EnumDisplaySettings(
string deviceName,
DISPLAY_SETTINGS_MODE mode,
ref DEVICE_MODE devMode
);
[DllImport("gdi32", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateDC(string driver, string device, string port, IntPtr deviceMode);
[DllImport("gdi32")]
internal static extern bool DeleteDC(DCHandle dcHandle);
[DllImport("user32", CharSet = CharSet.Unicode)]
internal static extern bool EnumDisplayDevices(
string deviceName,
UInt32 deviceNumber,
ref DISPLAY_DEVICE displayDevice,
UInt32 flags
);
[DllImport("user32")]
internal static extern bool EnumDisplayMonitors(
[In] IntPtr dcHandle,
[In] IntPtr clip,
MonitorEnumProcedure callback,
IntPtr callbackObject
);
[DllImport("user32")]
internal static extern IntPtr GetDC(IntPtr windowHandle);
[DllImport("gdi32")]
internal static extern int GetDeviceCaps(DCHandle dcHandle, DEVICE_CAPABILITY index);
[DllImport("gdi32")]
internal static extern bool GetDeviceGammaRamp(DCHandle dcHandle, ref GAMMA_RAMP ramp);
[DllImport("user32")]
internal static extern bool GetMonitorInfo(
IntPtr monitorHandle,
ref MONITOR_INFO monitorInfo
);
[DllImport("user32")]
internal static extern IntPtr MonitorFromPoint(
[In] POINTL point,
MONITOR_FROM_FLAG flag
);
[DllImport("user32")]
internal static extern IntPtr MonitorFromRect(
[In] RECTL rectangle,
MONITOR_FROM_FLAG flag
);
[DllImport("user32")]
internal static extern IntPtr MonitorFromWindow(
[In] IntPtr windowHandle,
MONITOR_FROM_FLAG flag
);
[DllImport("user32")]
internal static extern bool ReleaseDC([In] IntPtr windowHandle, [In] DCHandle dcHandle);
[DllImport("gdi32")]
internal static extern bool SetDeviceGammaRamp(DCHandle dcHandle, ref GAMMA_RAMP ramp);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern bool MoveWindow(IntPtr hWnd, int x, int y, int cx, int cy, bool repaint);
// This code was part of development to add recording taskbar location and state so that we could apply it later
// Windows 11 doesn't support moving
[DllImport("Shell32.dll", CharSet = CharSet.Auto)]
private static extern int SHAppBarMessage(ABM_MESSAGE dwMessage, ref APP_BAR_DATA abd);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate int MonitorEnumProcedure(
IntPtr monitorHandle,
IntPtr dcHandle,
ref RECTL rect,
IntPtr callbackObject
);
public static APP_BAR_DATA GetTaskbarPosition()
{
APP_BAR_DATA abd = new APP_BAR_DATA();
abd.cbSize = Marshal.SizeOf(abd);
// Query the system for an approved size and position.
SHAppBarMessage(ABM_MESSAGE.ABM_GETTASKBARPOS, ref abd);
return abd;
}
public static bool GetTaskbarAutoHide(APP_BAR_DATA abd)
{
// Query the system for an approved size and position.
ABS_SETTING state = (ABS_SETTING)SHAppBarMessage(ABM_MESSAGE.ABM_GETSTATE, ref abd);
return state.HasFlag(ABS_SETTING.ABS_AUTOHIDE);
}
//public static void MoveTaskbar(APP_BAR_DATA abd, ABE_EDGE edge, Size idealSize)
public static void SetTaskbarPosition(APP_BAR_DATA abd, ABE_EDGE edge)
{
abd.uEdge = edge;
SHAppBarMessage(ABM_MESSAGE.ABM_SETPOS, ref abd);
/*// Get current size
int idealSize = 100;
if (edge == ABE_EDGE.ABE_LEFT || edge == ABE_EDGE.ABE_RIGHT)
{
abd.rc.Top = 0;
abd.rc.Bottom = SystemInformation.PrimaryMonitorSize.Height;
if (edge == ABE_EDGE.ABE_LEFT)
{
abd.rc.Right = idealSize;
}
else
{
abd.rc.Right = SystemInformation.PrimaryMonitorSize.Width;
abd.rc.Left = abd.rc.Right - idealSize;
}
}
else
{
abd.rc.Left = 0;
abd.rc.Right = SystemInformation.PrimaryMonitorSize.Width;
if (edge == ABE_EDGE.ABE_TOP)
{
abd.rc.Bottom = idealSize;
}
else
{
abd.rc.Bottom = SystemInformation.PrimaryMonitorSize.Height;
abd.rc.Top = abd.rc.Bottom - idealSize;
}
}
ABS_SETTING state = (ABS_SETTING)SHAppBarMessage(ABM_MESSAGE.ABM_GETSTATE, ref abd);*/
}
// THE FOLLOWING CODE WAS AN ATTEMPT TO SET THE TASKBAR POSITION USING CODE
// TURNS OUT WE CAN'T ACTUALLY SET THE POSITION PROGRAMMATICALLY iIN WIN10 or WIN11
// Next we want to remember where the windows toolbar is for each screen
// Query the system for an approved size and position.
// APP_BAR_DATA taskbarPosition = GDIImport.GetTaskbarPosition();
// bool taskbarAutoHide = GDIImport.GetTaskbarAutoHide(taskbarPosition);
// try to move the taskbar
// GDIImport.SetTaskbarPosition(taskbarPosition, ABE_EDGE.ABE_TOP);
// GDIImport.SetTaskbarAutoHide(taskbarPosition, true);
public static void SetTaskbarAutoHide(APP_BAR_DATA abd, bool hide)
{
if (hide)
{
// Set the autohide flag
abd.lParam |= ABS_SETTING.ABS_AUTOHIDE;
}
else
{
// Clear the autohide flag
abd.lParam &= ~ABS_SETTING.ABS_AUTOHIDE;
}
SHAppBarMessage(ABM_MESSAGE.ABM_GETSTATE, ref abd);
}
}
}

View File

@ -2,10 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
using DisplayMagicianShared;
using System.ComponentModel;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace DisplayMagicianShared.Windows namespace DisplayMagicianShared.Windows
@ -21,7 +18,7 @@ namespace DisplayMagicianShared.Windows
public override bool Equals(object obj) => obj is ADVANCED_HDR_INFO_PER_PATH other && this.Equals(other); public override bool Equals(object obj) => obj is ADVANCED_HDR_INFO_PER_PATH other && this.Equals(other);
public bool Equals(ADVANCED_HDR_INFO_PER_PATH other) public bool Equals(ADVANCED_HDR_INFO_PER_PATH other)
=> // AdapterId.Equals(other.AdapterId) && // Removed the AdapterId from the Equals, as it changes after reboot. => // AdapterId.Equals(other.AdapterId) && // Removed the AdapterId from the Equals, as it changes after reboot.
Id == other.Id && //Id == other.Id && // Removed the ID too, as that changes if the user has a Clone!
AdvancedColorInfo.Equals(other.AdvancedColorInfo) && AdvancedColorInfo.Equals(other.AdvancedColorInfo) &&
SDRWhiteLevel.Equals(other.SDRWhiteLevel); SDRWhiteLevel.Equals(other.SDRWhiteLevel);
public override int GetHashCode() public override int GetHashCode()
@ -41,22 +38,30 @@ namespace DisplayMagicianShared.Windows
public DISPLAYCONFIG_PATH_INFO[] DisplayConfigPaths; public DISPLAYCONFIG_PATH_INFO[] DisplayConfigPaths;
public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes; public DISPLAYCONFIG_MODE_INFO[] DisplayConfigModes;
public ADVANCED_HDR_INFO_PER_PATH[] DisplayHDRStates; public ADVANCED_HDR_INFO_PER_PATH[] DisplayHDRStates;
public Dictionary<string, GDI_DISPLAY_SETTING> GdiDisplaySettings;
public bool IsCloned;
// Note: We purposely have left out the DisplaySources from the Equals as it's order keeps changing after each reboot and after each profile swap // Note: We purposely have left out the DisplaySources from the Equals as it's order keeps changing after each reboot and after each profile swap
// and it is informational only and doesn't contribute to the configuration (it's used for generating the Screens structure, and therefore for // and it is informational only and doesn't contribute to the configuration (it's used for generating the Screens structure, and therefore for
// generating the profile icon. // generating the profile icon.
public Dictionary<string, uint> DisplaySources; public Dictionary<string, List<uint>> DisplaySources;
public List<string> DisplayIdentifiers; public List<string> DisplayIdentifiers;
public override bool Equals(object obj) => obj is WINDOWS_DISPLAY_CONFIG other && this.Equals(other); public override bool Equals(object obj) => obj is WINDOWS_DISPLAY_CONFIG other && this.Equals(other);
public bool Equals(WINDOWS_DISPLAY_CONFIG other) public bool Equals(WINDOWS_DISPLAY_CONFIG other)
=> DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) && => IsCloned == other.IsCloned &&
DisplayConfigPaths.SequenceEqual(other.DisplayConfigPaths) &&
DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) && DisplayConfigModes.SequenceEqual(other.DisplayConfigModes) &&
DisplayHDRStates.SequenceEqual(other.DisplayHDRStates) && 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);
public override int GetHashCode() public override int GetHashCode()
{ {
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates).GetHashCode(); //return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, GdiDisplaySettings.Values, IsCloned, DisplayIdentifiers).GetHashCode();
return (DisplayConfigPaths, DisplayConfigModes, DisplayHDRStates, IsCloned, DisplayIdentifiers).GetHashCode();
} }
public static bool operator ==(WINDOWS_DISPLAY_CONFIG lhs, WINDOWS_DISPLAY_CONFIG rhs) => lhs.Equals(rhs); public static bool operator ==(WINDOWS_DISPLAY_CONFIG lhs, WINDOWS_DISPLAY_CONFIG rhs) => lhs.Equals(rhs);
@ -72,20 +77,20 @@ namespace DisplayMagicianShared.Windows
private static WinLibrary _instance = new WinLibrary(); private static WinLibrary _instance = new WinLibrary();
private bool _initialised = false; private bool _initialised = false;
private WINDOWS_DISPLAY_CONFIG _activeDisplayConfig;
// To detect redundant calls // To detect redundant calls
private bool _disposed = false; private bool _disposed = false;
// Instantiate a SafeHandle instance. // Instantiate a SafeHandle instance.
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true); private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
private IntPtr _adlContextHandle = IntPtr.Zero;
static WinLibrary() { } static WinLibrary() { }
public WinLibrary() public WinLibrary()
{ {
SharedLogger.logger.Trace("WinLibrary/WinLibrary: Intialising Windows CCD library interface"); SharedLogger.logger.Trace("WinLibrary/WinLibrary: Intialising Windows CCD library interface");
_initialised = true; _initialised = true;
_activeDisplayConfig = GetActiveConfig();
} }
~WinLibrary() ~WinLibrary()
@ -123,11 +128,46 @@ namespace DisplayMagicianShared.Windows
} }
} }
public WINDOWS_DISPLAY_CONFIG ActiveDisplayConfig
{
get
{
return _activeDisplayConfig;
}
}
public List<string> CurrentDisplayIdentifiers
{
get
{
return _activeDisplayConfig.DisplayIdentifiers;
}
}
public static WinLibrary GetLibrary() public static WinLibrary GetLibrary()
{ {
return _instance; return _instance;
} }
public WINDOWS_DISPLAY_CONFIG CreateDefaultConfig()
{
WINDOWS_DISPLAY_CONFIG myDefaultConfig = new WINDOWS_DISPLAY_CONFIG();
// Fill in the minimal amount we need to avoid null references
// so that we won't break json.net when we save a default config
myDefaultConfig.DisplayAdapters = new Dictionary<ulong, string>();
myDefaultConfig.DisplayConfigModes = new DISPLAYCONFIG_MODE_INFO[0];
myDefaultConfig.DisplayConfigPaths = new DISPLAYCONFIG_PATH_INFO[0];
myDefaultConfig.DisplayHDRStates = new ADVANCED_HDR_INFO_PER_PATH[0];
myDefaultConfig.DisplayIdentifiers = new List<string>();
myDefaultConfig.DisplaySources = new Dictionary<string, List<uint>>();
myDefaultConfig.IsCloned = false;
myDefaultConfig.GdiDisplaySettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
return myDefaultConfig;
}
private void PatchAdapterIDs(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig, Dictionary<ulong, string> currentAdapterMap) private void PatchAdapterIDs(ref WINDOWS_DISPLAY_CONFIG savedDisplayConfig, Dictionary<ulong, string> currentAdapterMap)
{ {
@ -223,6 +263,22 @@ namespace DisplayMagicianShared.Windows
} }
public bool UpdateActiveConfig()
{
SharedLogger.logger.Trace($"WinLibrary/UpdateActiveConfig: Updating the currently active config");
try
{
_activeDisplayConfig = GetActiveConfig();
}
catch (Exception ex)
{
SharedLogger.logger.Trace(ex, $"WinLibrary/UpdateActiveConfig: Exception updating the currently active config");
return false;
}
return true;
}
public WINDOWS_DISPLAY_CONFIG GetActiveConfig() public WINDOWS_DISPLAY_CONFIG GetActiveConfig()
{ {
SharedLogger.logger.Trace($"WinLibrary/GetActiveConfig: Getting the currently active config"); SharedLogger.logger.Trace($"WinLibrary/GetActiveConfig: Getting the currently active config");
@ -283,122 +339,253 @@ namespace DisplayMagicianShared.Windows
WINDOWS_DISPLAY_CONFIG windowsDisplayConfig = new WINDOWS_DISPLAY_CONFIG(); WINDOWS_DISPLAY_CONFIG windowsDisplayConfig = new WINDOWS_DISPLAY_CONFIG();
windowsDisplayConfig.DisplayAdapters = new Dictionary<ulong, string>(); windowsDisplayConfig.DisplayAdapters = new Dictionary<ulong, string>();
windowsDisplayConfig.DisplayHDRStates = new ADVANCED_HDR_INFO_PER_PATH[pathCount]; windowsDisplayConfig.DisplayHDRStates = new ADVANCED_HDR_INFO_PER_PATH[pathCount];
windowsDisplayConfig.DisplaySources = new Dictionary<string, uint>(); windowsDisplayConfig.DisplaySources = new Dictionary<string, List<uint>>();
windowsDisplayConfig.IsCloned = false;
// First of all generate the current displayIdentifiers
windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers();
// Next, extract the UID entries for the displays as that's what the Path IDs are normally supposed to be
// This is how we know the actual target id's ofd the monitors currently connected
Regex rx = new Regex(@"UID(?<uid>\d+)#", RegexOptions.Compiled | RegexOptions.IgnoreCase);
HashSet<uint> physicalTargetIdsToFind = new HashSet<uint>();
foreach (string displayIdentifier in windowsDisplayConfig.DisplayIdentifiers)
{
MatchCollection mc = rx.Matches(displayIdentifier);
if (mc.Count > 0)
{
physicalTargetIdsToFind.Add(UInt32.Parse(mc[0].Groups["uid"].Value));
}
}
// Now cycle through the paths and grab the HDR state information // Now cycle through the paths and grab the HDR state information
// and map the adapter name to adapter id // and map the adapter name to adapter id
HashSet<uint> targetIdsToChange = new HashSet<uint>();
var hdrInfos = new ADVANCED_HDR_INFO_PER_PATH[pathCount]; var hdrInfos = new ADVANCED_HDR_INFO_PER_PATH[pathCount];
int hdrInfoCount = 0; int hdrInfoCount = 0;
foreach (var path in paths) for (int i = 0; i < paths.Length; i++)
{ {
// Figure out if this path has a physical targetId, and if it doesn't store it
if (!physicalTargetIdsToFind.Contains(paths[i].TargetInfo.Id))
{
// Add to the list of physical path target ids we need to patch later
targetIdsToChange.Add(paths[i].TargetInfo.Id);
}
// Track if this display is a cloned path
bool isClonedPath = false;
// get display source name // get display source name
var sourceInfo = new DISPLAYCONFIG_SOURCE_DEVICE_NAME(); var sourceInfo = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
sourceInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; sourceInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
sourceInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>(); sourceInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
sourceInfo.Header.AdapterId = path.SourceInfo.AdapterId; sourceInfo.Header.AdapterId = paths[i].SourceInfo.AdapterId;
sourceInfo.Header.Id = path.SourceInfo.Id; sourceInfo.Header.Id = paths[i].SourceInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref sourceInfo); err = CCDImport.DisplayConfigGetDeviceInfo(ref sourceInfo);
if (err == WIN32STATUS.ERROR_SUCCESS) if (err == WIN32STATUS.ERROR_SUCCESS)
{ {
// Store it for later // Store it for later
windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, path.SourceInfo.Id); if (windowsDisplayConfig.DisplaySources.ContainsKey(sourceInfo.ViewGdiDeviceName))
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {path.SourceInfo.Id}."); {
// We already have at least one display using this source, so we need to add the other cloned display to the existing list
windowsDisplayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Add(paths[i].SourceInfo.Id);
isClonedPath = true;
windowsDisplayConfig.IsCloned = true;
} }
else else
{ {
SharedLogger.logger.Warn($"WinLibrary/PrintActiveConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the source info for source adapter #{path.SourceInfo.AdapterId}"); // This is the first display to use this source
List<uint> sourceIds = new List<uint>();
sourceIds.Add(paths[i].SourceInfo.Id);
windowsDisplayConfig.DisplaySources.Add(sourceInfo.ViewGdiDeviceName, sourceIds);
}
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {paths[i].SourceInfo.Id}.");
}
else
{
SharedLogger.logger.Warn($"WinLibrary/PrintActiveConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the source info for source adapter #{paths[i].SourceInfo.AdapterId}");
}
// Check if this path is a cloned display path, and if so make some changes
// so that the cloned display will be applied properly
if (isClonedPath)
{
// We need to make some modifications to this path so that we store as ready for being applied
// https://docs.microsoft.com/en-us/windows-hardware/drivers/display/ccd-example-code
paths[i].Flags |= DISPLAYCONFIG_PATH_FLAGS.DISPLAYCONFIG_PATH_ACTIVE;
paths[i].SourceInfo.ModeInfoIdx = CCDImport.DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
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 {path.TargetInfo.AdapterId.Value}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get adapter name for adapter {paths[i].TargetInfo.AdapterId.Value}.");
if (!windowsDisplayConfig.DisplayAdapters.ContainsKey(path.TargetInfo.AdapterId.Value)) if (!windowsDisplayConfig.DisplayAdapters.ContainsKey(paths[i].TargetInfo.AdapterId.Value))
{ {
var adapterInfo = new DISPLAYCONFIG_ADAPTER_NAME(); var adapterInfo = new DISPLAYCONFIG_ADAPTER_NAME();
adapterInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME; adapterInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADAPTER_NAME;
adapterInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_ADAPTER_NAME>(); adapterInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_ADAPTER_NAME>();
adapterInfo.Header.AdapterId = path.TargetInfo.AdapterId; adapterInfo.Header.AdapterId = paths[i].TargetInfo.AdapterId;
adapterInfo.Header.Id = path.TargetInfo.Id; adapterInfo.Header.Id = paths[i].TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo); err = CCDImport.DisplayConfigGetDeviceInfo(ref adapterInfo);
if (err == WIN32STATUS.ERROR_SUCCESS) if (err == WIN32STATUS.ERROR_SUCCESS)
{ {
// Store it for later // Store it for later
windowsDisplayConfig.DisplayAdapters.Add(path.TargetInfo.AdapterId.Value, adapterInfo.AdapterDevicePath); windowsDisplayConfig.DisplayAdapters.Add(paths[i].TargetInfo.AdapterId.Value, adapterInfo.AdapterDevicePath);
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found adapter name {adapterInfo.AdapterDevicePath} for adapter {path.TargetInfo.AdapterId.Value}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found adapter name {adapterInfo.AdapterDevicePath} for adapter {paths[i].TargetInfo.AdapterId.Value}.");
} }
else else
{ {
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to query the adapter name for adapter {path.TargetInfo.AdapterId.Value}."); SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: ERROR - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to query the adapter name for adapter {paths[i].TargetInfo.AdapterId.Value}.");
} }
} }
// Get advanced color info // Get advanced color info
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get advanced color info for display {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get advanced color info for display {paths[i].TargetInfo.Id}.");
var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO(); var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO; colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
colorInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>(); colorInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
colorInfo.Header.AdapterId = path.TargetInfo.AdapterId; colorInfo.Header.AdapterId = paths[i].TargetInfo.AdapterId;
colorInfo.Header.Id = path.TargetInfo.Id; colorInfo.Header.Id = paths[i].TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref colorInfo); err = CCDImport.DisplayConfigGetDeviceInfo(ref colorInfo);
if (err == WIN32STATUS.ERROR_SUCCESS) if (err == WIN32STATUS.ERROR_SUCCESS)
{ {
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found color info for display {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found color info for display {paths[i].TargetInfo.Id}.");
if (colorInfo.AdvancedColorSupported) if (colorInfo.AdvancedColorSupported)
{ {
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is supported for display {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is supported for display {paths[i].TargetInfo.Id}.");
} }
else else
{ {
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is NOT supported for display {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is NOT supported for display {paths[i].TargetInfo.Id}.");
} }
if (colorInfo.AdvancedColorEnabled) if (colorInfo.AdvancedColorEnabled)
{ {
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is enabled for display {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is enabled for display {paths[i].TargetInfo.Id}.");
} }
else else
{ {
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is NOT enabled for display {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: HDR is NOT enabled for display {paths[i].TargetInfo.Id}.");
} }
} }
else else
{ {
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get advanced color settings for display {path.TargetInfo.Id}."); SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get advanced color settings for display {paths[i].TargetInfo.Id}.");
} }
// get SDR white levels // get SDR white levels
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get SDR white levels for adapter {path.TargetInfo.AdapterId.Value}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Attempting to get SDR white levels for adapter {paths[i].TargetInfo.AdapterId.Value}.");
var whiteLevelInfo = new DISPLAYCONFIG_SDR_WHITE_LEVEL(); var whiteLevelInfo = new DISPLAYCONFIG_SDR_WHITE_LEVEL();
whiteLevelInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL; whiteLevelInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
whiteLevelInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SDR_WHITE_LEVEL>(); whiteLevelInfo.Header.Size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SDR_WHITE_LEVEL>();
whiteLevelInfo.Header.AdapterId = path.TargetInfo.AdapterId; whiteLevelInfo.Header.AdapterId = paths[i].TargetInfo.AdapterId;
whiteLevelInfo.Header.Id = path.TargetInfo.Id; whiteLevelInfo.Header.Id = paths[i].TargetInfo.Id;
err = CCDImport.DisplayConfigGetDeviceInfo(ref whiteLevelInfo); err = CCDImport.DisplayConfigGetDeviceInfo(ref whiteLevelInfo);
if (err == WIN32STATUS.ERROR_SUCCESS) if (err == WIN32STATUS.ERROR_SUCCESS)
{ {
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found SDR White levels for display {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found SDR White levels for display {paths[i].TargetInfo.Id}.");
} }
else else
{ {
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get SDR White levels for display {path.TargetInfo.Id}."); SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get SDR White levels for display {paths[i].TargetInfo.Id}.");
} }
hdrInfos[hdrInfoCount] = new ADVANCED_HDR_INFO_PER_PATH(); hdrInfos[hdrInfoCount] = new ADVANCED_HDR_INFO_PER_PATH();
hdrInfos[hdrInfoCount].AdapterId = path.TargetInfo.AdapterId; hdrInfos[hdrInfoCount].AdapterId = paths[i].TargetInfo.AdapterId;
hdrInfos[hdrInfoCount].Id = path.TargetInfo.Id; hdrInfos[hdrInfoCount].Id = paths[i].TargetInfo.Id;
hdrInfos[hdrInfoCount].AdvancedColorInfo = colorInfo; hdrInfos[hdrInfoCount].AdvancedColorInfo = colorInfo;
hdrInfos[hdrInfoCount].SDRWhiteLevel = whiteLevelInfo; hdrInfos[hdrInfoCount].SDRWhiteLevel = whiteLevelInfo;
hdrInfoCount++; hdrInfoCount++;
} }
// Now we need to figure out the maps between the cloned ID and the real physical target id
// the Advanced color info structure actually holds this information! So lets mine it.
Dictionary<uint, uint> targetIdMap = new Dictionary<uint, uint>();
foreach (var hdrInfo in hdrInfos)
{
targetIdMap[hdrInfo.Id] = hdrInfo.AdvancedColorInfo.Header.Id;
}
// Now we need to go through the list of paths again and patch the 'cloned' displays with a real display ID so the config works
for (int i = 0; i < paths.Length; i++)
{
if (targetIdsToChange.Contains(paths[i].TargetInfo.Id))
{
// Patch the cloned ids with a real working one!
paths[i].TargetInfo.Id = targetIdMap[paths[i].TargetInfo.Id];
}
}
// And then we need to go through the list of modes again and patch the 'cloned' displays with a real display ID so the display layout is right in cloned displays
for (int i = 0; i < modes.Length; i++)
{
// We only change the ids that match in InfoType for target displays
if (modes[i].InfoType == DISPLAYCONFIG_MODE_INFO_TYPE.DISPLAYCONFIG_MODE_INFO_TYPE_TARGET && targetIdsToChange.Contains(modes[i].Id))
{
// Patch the cloned ids with a real working one!
modes[i].Id = targetIdMap[modes[i].Id];
}
}
// Store the active paths and modes in our display config object // Store the active paths and modes in our display config object
windowsDisplayConfig.DisplayConfigPaths = paths; windowsDisplayConfig.DisplayConfigPaths = paths;
windowsDisplayConfig.DisplayConfigModes = modes; windowsDisplayConfig.DisplayConfigModes = modes;
windowsDisplayConfig.DisplayHDRStates = hdrInfos; windowsDisplayConfig.DisplayHDRStates = hdrInfos;
windowsDisplayConfig.DisplayIdentifiers = GetCurrentDisplayIdentifiers(); windowsDisplayConfig.GdiDisplaySettings = GetGdiDisplaySettings();
return windowsDisplayConfig; return windowsDisplayConfig;
} }
public static Dictionary<string, uint> GetDisplaySourceNames() public Dictionary<string, GDI_DISPLAY_SETTING> GetGdiDisplaySettings()
{
// Get the list of all display adapters in this machine through GDI
Dictionary<string, GDI_DISPLAY_SETTING> gdiDeviceSettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
Dictionary<string, string> gdiDeviceSources = new Dictionary<string, string>();
UInt32 displayDeviceNum = 0;
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.Size = (UInt32)Marshal.SizeOf<DISPLAY_DEVICE>();
while (GDIImport.EnumDisplayDevices(null, displayDeviceNum, ref displayDevice, 0))
{
// Now we try and grab the GDI Device Settings for each display device
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
if (displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.AttachedToDesktop) || displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MultiDriver))
{
// If the display device is attached to the Desktop, or a type of display that is represented by a psudeo mirroring application, then skip this display
// e.g. some sort of software interfaced display that doesn't have a physical plug, or maybe uses USB for communication
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
DEVICE_MODE currentMode = new DEVICE_MODE();
currentMode.Size = (UInt16)Marshal.SizeOf<DEVICE_MODE>();
bool gdiWorked = GDIImport.EnumDisplaySettings(displayDevice.DeviceName, DISPLAY_SETTINGS_MODE.CurrentSettings, ref currentMode);
if (gdiWorked)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Got the current Display Settings from display {displayDevice.DeviceName}.");
GDI_DISPLAY_SETTING myDisplaySetting = new GDI_DISPLAY_SETTING();
myDisplaySetting.IsEnabled = true; // Always true if we get here
myDisplaySetting.Device = displayDevice;
myDisplaySetting.DeviceMode = currentMode;
if (displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.PrimaryDevice))
{
// This is a primary device, so we'll set that too.
myDisplaySetting.IsPrimary = true;
}
gdiDeviceSettings[displayDevice.DeviceKey] = myDisplaySetting;
gdiDeviceSources[displayDevice.DeviceName] = displayDevice.DeviceKey;
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get current display mode settings from display {displayDevice.DeviceName}.");
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: The display {displayDevice.DeviceName} is either not attached to the desktop, or is not a mirroring driver. Skipping this display device as we cannot use it.");
}
displayDeviceNum++;
}
return gdiDeviceSettings;
}
public static Dictionary<string, List<uint>> GetDisplaySourceNames()
{ {
// Get the size of the largest Active Paths and Modes arrays // Get the size of the largest Active Paths and Modes arrays
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the size of the largest Active Paths and Modes arrays"); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the size of the largest Active Paths and Modes arrays");
@ -449,12 +636,12 @@ namespace DisplayMagicianShared.Windows
} }
// Prepare the empty DisplaySources dictionary // Prepare the empty DisplaySources dictionary
Dictionary<string, uint> DisplaySources = new Dictionary<string, uint>(); Dictionary<string, List<uint>> DisplaySources = new Dictionary<string, List<uint>>();
// Now cycle through the paths and grab the HDR state information // Now cycle through the paths and grab the HDR state information
// and map the adapter name to adapter id // and map the adapter name to adapter id
var hdrInfos = new ADVANCED_HDR_INFO_PER_PATH[pathCount]; //var hdrInfos = new ADVANCED_HDR_INFO_PER_PATH[pathCount];
int hdrInfoCount = 0; //int hdrInfoCount = 0;
foreach (var path in paths) foreach (var path in paths)
{ {
// get display source name // get display source name
@ -467,7 +654,18 @@ namespace DisplayMagicianShared.Windows
if (err == WIN32STATUS.ERROR_SUCCESS) if (err == WIN32STATUS.ERROR_SUCCESS)
{ {
// Store it for later // Store it for later
DisplaySources.Add(sourceInfo.ViewGdiDeviceName, path.SourceInfo.Id); //DisplaySources.Add(sourceInfo.ViewGdiDeviceName, path.SourceInfo.Id);
if (DisplaySources.ContainsKey(sourceInfo.ViewGdiDeviceName))
{
// We want to add another cloned display
DisplaySources[sourceInfo.ViewGdiDeviceName].Add(path.SourceInfo.Id);
}
else
{
// We want to create a new list entry if there isn't one already there.
DisplaySources.Add(sourceInfo.ViewGdiDeviceName, new List<uint> { path.SourceInfo.Id });
}
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {path.SourceInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {path.SourceInfo.Id}.");
} }
else else
@ -492,55 +690,17 @@ namespace DisplayMagicianShared.Windows
public string PrintActiveConfig() public string PrintActiveConfig()
{ {
string stringToReturn = ""; string stringToReturn = "";
// Get the current config
WINDOWS_DISPLAY_CONFIG displayConfig = ActiveDisplayConfig;
WIN32STATUS err = WIN32STATUS.ERROR_GEN_FAILURE;
stringToReturn += $"****** WINDOWS CCD CONFIGURATION *******\n"; stringToReturn += $"****** WINDOWS CCD CONFIGURATION *******\n";
stringToReturn += $"Display profile contains cloned screens: {displayConfig.IsCloned}\n";
stringToReturn += $"\n";
// Get the size of the largest Active Paths and Modes arrays // Get the size of the largest Active Paths and Modes arrays
int pathCount = 0; foreach (var path in displayConfig.DisplayConfigPaths)
int modeCount = 0;
WIN32STATUS err = CCDImport.GetDisplayConfigBufferSizes(QDC.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: ERROR - GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
throw new WinLibraryException($"GetDisplayConfigBufferSizes returned WIN32STATUS {err} when trying to get the maximum path and mode sizes");
}
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: 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_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Warn($"WinLibrary/PrintActiveConfig: The displays were modified between GetDisplayConfigBufferSizes and QueryDisplayConfig so we need to get the buffer sizes again.");
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: 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_ONLY_ACTIVE_PATHS, out pathCount, out modeCount);
if (err != WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: 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/PrintActiveConfig: 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_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero);
if (err == WIN32STATUS.ERROR_INSUFFICIENT_BUFFER)
{
SharedLogger.logger.Error($"WinLibrary/PrintActiveConfig: 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/PrintActiveConfig: 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/PrintActiveConfig: 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.");
}
foreach (var path in paths)
{ {
stringToReturn += $"----++++==== Path ====++++----\n"; stringToReturn += $"----++++==== Path ====++++----\n";
@ -556,6 +716,17 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {path.SourceInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/PrintActiveConfig: Found Display Source {sourceInfo.ViewGdiDeviceName} for source {path.SourceInfo.Id}.");
stringToReturn += $"****** Interrogating Display Source {path.SourceInfo.Id} *******\n"; stringToReturn += $"****** Interrogating Display Source {path.SourceInfo.Id} *******\n";
stringToReturn += $"Found Display Source {sourceInfo.ViewGdiDeviceName}\n"; stringToReturn += $"Found Display Source {sourceInfo.ViewGdiDeviceName}\n";
if (displayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Count > 1)
{
stringToReturn += $"Display Source is Cloned: true\n";
stringToReturn += $"Number of Display Source clones: {displayConfig.DisplaySources[sourceInfo.ViewGdiDeviceName].Count - 1}\n";
}
else
{
stringToReturn += $"Display Source is Cloned: false\n";
stringToReturn += $"Number of Display Source clones: 0\n";
}
stringToReturn += $"\n"; stringToReturn += $"\n";
} }
else else
@ -620,6 +791,8 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the adapter device path for target #{path.TargetInfo.AdapterId}"); SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to get the adapter device path for target #{path.TargetInfo.AdapterId}");
} }
// show the
// get display target preferred mode // get display target preferred mode
var targetPreferredInfo = new DISPLAYCONFIG_TARGET_PREFERRED_MODE(); var targetPreferredInfo = new DISPLAYCONFIG_TARGET_PREFERRED_MODE();
targetPreferredInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE; targetPreferredInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE;
@ -736,7 +909,7 @@ namespace DisplayMagicianShared.Windows
{ {
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found SDR White Level: {whiteLevelInfo.SDRWhiteLevel} for target {path.TargetInfo.Id}."); SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Found SDR White Level: {whiteLevelInfo.SDRWhiteLevel} for target {path.TargetInfo.Id}.");
stringToReturn += $"****** Interrogating SDR Whilte Level for Display {path.TargetInfo.Id} *******\n"; stringToReturn += $"****** Interrogating SDR White Level for Display {path.TargetInfo.Id} *******\n";
stringToReturn += $" SDR White Level: {whiteLevelInfo.SDRWhiteLevel}\n"; stringToReturn += $" SDR White Level: {whiteLevelInfo.SDRWhiteLevel}\n";
stringToReturn += $"\n"; stringToReturn += $"\n";
} }
@ -745,6 +918,76 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out the SDL white level for display #{path.TargetInfo.Id}"); SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - DisplayConfigGetDeviceInfo returned WIN32STATUS {err} when trying to find out the SDL white level for display #{path.TargetInfo.Id}");
} }
} }
// Get the list of all display adapters in this machine through GDI
Dictionary<string, GDI_DISPLAY_SETTING> gdiDeviceSettings = new Dictionary<string, GDI_DISPLAY_SETTING>();
UInt32 displayDeviceNum = 0;
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.Size = (UInt32)Marshal.SizeOf<DISPLAY_DEVICE>();
stringToReturn += $"----++++==== GDI Device Information ====++++----\n";
while (GDIImport.EnumDisplayDevices(null, displayDeviceNum, ref displayDevice, 0))
{
// Now we try and grab the GDI Device Info for each display device
stringToReturn += $"****** Display Device Info for Display {displayDevice.DeviceName} *******\n";
stringToReturn += $" Display Device ID: {displayDevice.DeviceId}\n";
stringToReturn += $" Display Device Key: {displayDevice.DeviceKey}\n";
stringToReturn += $" Display Device Name: {displayDevice.DeviceName}\n";
stringToReturn += $" Display Device String: {displayDevice.DeviceString}\n";
stringToReturn += $" Is Attached to Desktop: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.AttachedToDesktop)}\n";
stringToReturn += $" Is Disconnected: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.Disconnect)}\n";
stringToReturn += $" Is a Mirroing Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MirroringDriver)}\n";
stringToReturn += $" Has Modes Pruned: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.ModesPruned)}\n";
stringToReturn += $" Is Multi-driver: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MultiDriver)}\n";
stringToReturn += $" Is Primary Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.PrimaryDevice)}\n";
stringToReturn += $" Is Remote Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.Remote)}\n";
stringToReturn += $" Is Removable Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.Removable)}\n";
stringToReturn += $" Is VGA Compatible Display Device: {displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.VGACompatible)}\n";
stringToReturn += $"\n";
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
if (displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.AttachedToDesktop) || displayDevice.StateFlags.HasFlag(DISPLAY_DEVICE_STATE_FLAGS.MultiDriver))
{
// If the display device is attached to the Desktop, or a type of display that is represented by a psudeo mirroring application, then skip this display
// e.g. some sort of software interfaced display that doesn't have a physical plug, or maybe uses USB for communication
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Getting the current Display Settings for {displayDevice.DeviceName}");
stringToReturn += $" Display Device Settings for attached Display {displayDevice.DeviceName} :\n";
DEVICE_MODE currentMode = new DEVICE_MODE();
currentMode.Size = (UInt16)Marshal.SizeOf<DEVICE_MODE>();
bool gdiWorked = GDIImport.EnumDisplaySettings(displayDevice.DeviceName, DISPLAY_SETTINGS_MODE.CurrentSettings, ref currentMode);
if (gdiWorked)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Got the current Display Settings from display {displayDevice.DeviceName}.");
// Now we try and grab the GDI Device Settings for each display device
stringToReturn += $" Bits Per Pixel: {currentMode.BitsPerPixel}\n";
stringToReturn += $" Device Name: {currentMode.DeviceName}\n";
stringToReturn += $" Display Fixed Output: {currentMode.DisplayFixedOutput}\n";
stringToReturn += $" Grayscale Display: {currentMode.DisplayFlags.HasFlag(DISPLAY_FLAGS.Grayscale)}\n";
stringToReturn += $" Interlaced Display: {currentMode.DisplayFlags.HasFlag(DISPLAY_FLAGS.Interlaced)}\n";
stringToReturn += $" Refresh Rate: {currentMode.DisplayFrequency}Hz\n";
stringToReturn += $" Display Rotation: {currentMode.DisplayOrientation.ToString("G")}\n";
stringToReturn += $" Driver Extra: {currentMode.DriverExtra}\n";
stringToReturn += $" Driver Version: {currentMode.DriverVersion}\n";
stringToReturn += $" All Display Fields populated by driver: {currentMode.Fields.HasFlag(DEVICE_MODE_FIELDS.AllDisplay)}\n";
stringToReturn += $" Display Width and Height in Pixels: {currentMode.PixelsWidth} x {currentMode.PixelsHeight}\n";
stringToReturn += $" Display Position: X:{currentMode.Position.X}, Y:{currentMode.Position.Y}\n";
stringToReturn += $" Specification Version: {currentMode.SpecificationVersion}\n";
stringToReturn += $"\n";
}
else
{
SharedLogger.logger.Warn($"WinLibrary/GetWindowsDisplayConfig: WARNING - Unabled to get current display mode settings from display {displayDevice.DeviceName}.");
stringToReturn += $" No display settings found.\n\n";
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: The display {displayDevice.DeviceName} is either not attached to the desktop, or is not a mirroring driver. Skipping this display device as we cannot use it.");
}
displayDeviceNum++;
}
return stringToReturn; return stringToReturn;
} }
@ -754,36 +997,127 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Generating a list of all the current 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); WINDOWS_DISPLAY_CONFIG allWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ALL_PATHS);
if (displayConfig.IsCloned)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: We have a cloned display in this display profile");
}
else
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: We have no cloned displays in thus display profile");
}
// Now we go through the Paths to update the LUIDs as per Soroush's suggestion // 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"); SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Patching the adapter IDs to make the saved config valid");
PatchAdapterIDs(ref displayConfig, allWindowsDisplayConfig.DisplayAdapters); PatchAdapterIDs(ref displayConfig, allWindowsDisplayConfig.DisplayAdapters);
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Testing whether the display configuration is valid");
// Test whether a specified display configuration is supported on the computer
uint myPathsCount = (uint)displayConfig.DisplayConfigPaths.Length; uint myPathsCount = (uint)displayConfig.DisplayConfigPaths.Length;
uint myModesCount = (uint)displayConfig.DisplayConfigModes.Length; uint myModesCount = (uint)displayConfig.DisplayConfigModes.Length;
WIN32STATUS err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.DISPLAYMAGICIAN_VALIDATE);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully validated that the display configuration supplied would work!");
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: ERROR - SetDisplayConfig couldn't validate the display configuration supplied. This display configuration wouldn't work.");
return false;
}
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Yay! The display configuration is valid! Attempting to set the Display Config now");
// Now set the specified display configuration for this computer // Now set the specified display configuration for this computer
err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.DISPLAYMAGICIAN_SET); WIN32STATUS err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.DISPLAYMAGICIAN_SET | SDC.SDC_FORCE_MODE_ENUMERATION);
if (err == WIN32STATUS.ERROR_SUCCESS) if (err == WIN32STATUS.ERROR_SUCCESS)
{ {
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully set the display configuration to the settings supplied!"); SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Successfully set the display configuration to the settings supplied!");
} }
else if (err == WIN32STATUS.ERROR_INVALID_PARAMETER)
{
SharedLogger.logger.Warn($"WinLibrary/SetActiveConfig: The combination of parameters and flags specified is invalid. Display configuration not applied. So trying again without SDC_FORCE_MODE_ENUMERATION as that works on some computers.");
// Try it again, because in some systems it doesn't work at the first try
err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.DISPLAYMAGICIAN_SET);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Retry. Successfully set the display configuration to the settings supplied!");
}
else if (err == WIN32STATUS.ERROR_INVALID_PARAMETER)
{
SharedLogger.logger.Warn($"WinLibrary/SetActiveConfig: Retry. The combination of parameters and flags specified is invalid. Display configuration not applied. So trying again without any specific data other than the topology as that works on some computers.");
// Try it again, because in some systems it doesn't work at the 2nd try! This is a fallback mode just to get something on the screen!
err = CCDImport.SetDisplayConfig(myPathsCount, displayConfig.DisplayConfigPaths, myModesCount, displayConfig.DisplayConfigModes, SDC.SDC_APPLY | SDC.SDC_TOPOLOGY_SUPPLIED | SDC.SDC_ALLOW_CHANGES | SDC.SDC_ALLOW_PATH_ORDER_CHANGES);
if (err == WIN32STATUS.ERROR_SUCCESS)
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Retry 2. Successfully set the display configuration to the settings supplied!");
}
else if (err == WIN32STATUS.ERROR_INVALID_PARAMETER)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry 2. The combination of parameters and flags specified is invalid. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_NOT_SUPPORTED)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry 2. The system is not running a graphics driver that was written according to the Windows Display Driver Model (WDDM). The function is only supported on a system with a WDDM driver running. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_ACCESS_DENIED)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry 2. The caller does not have access to the console session. This error occurs if the calling process does not have access to the current desktop or is running on a remote session. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_GEN_FAILURE)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry 2. An unspecified error occurred. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_BAD_CONFIGURATION)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry 2. The function could not find a workable solution for the source and target modes that the caller did not specify. Display configuration not applied.");
return false;
}
else else
{ {
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: ERROR - SetDisplayConfig couldn't set the display configuration using the settings supplied. Something is wrong."); SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry 2. SetDisplayConfig couldn't set the display configuration using the settings supplied. Display configuration not applied.");
throw new WinLibraryException($"SetDisplayConfig couldn't set the display configuration using the settings supplied. Something is wrong."); return false;
}
}
else if (err == WIN32STATUS.ERROR_NOT_SUPPORTED)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry. The system is not running a graphics driver that was written according to the Windows Display Driver Model (WDDM). The function is only supported on a system with a WDDM driver running. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_ACCESS_DENIED)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry. The caller does not have access to the console session. This error occurs if the calling process does not have access to the current desktop or is running on a remote session. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_GEN_FAILURE)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry. An unspecified error occurred. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_BAD_CONFIGURATION)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry. The function could not find a workable solution for the source and target modes that the caller did not specify. Display configuration not applied.");
return false;
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: Retry. SetDisplayConfig couldn't set the display configuration using the settings supplied. Display configuration not applied.");
return false;
}
}
else if (err == WIN32STATUS.ERROR_NOT_SUPPORTED)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The system is not running a graphics driver that was written according to the Windows Display Driver Model (WDDM). The function is only supported on a system with a WDDM driver running. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_ACCESS_DENIED)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The caller does not have access to the console session. This error occurs if the calling process does not have access to the current desktop or is running on a remote session. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_GEN_FAILURE)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: An unspecified error occurred. Display configuration not applied.");
return false;
}
else if (err == WIN32STATUS.ERROR_BAD_CONFIGURATION)
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: The function could not find a workable solution for the source and target modes that the caller did not specify. Display configuration not applied.");
return false;
}
else
{
SharedLogger.logger.Error($"WinLibrary/SetActiveConfig: SetDisplayConfig couldn't set the display configuration using the settings supplied. Display configuration not applied.");
return false;
} }
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: SUCCESS! The display configuration has been successfully applied"); SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: SUCCESS! The display configuration has been successfully applied");
@ -791,9 +1125,11 @@ namespace DisplayMagicianShared.Windows
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Waiting 0.5 seconds to let the display change take place before adjusting the Windows CCD HDR settings"); SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Waiting 0.5 seconds to let the display change take place before adjusting the Windows CCD HDR settings");
System.Threading.Thread.Sleep(500); System.Threading.Thread.Sleep(500);
// 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) foreach (ADVANCED_HDR_INFO_PER_PATH myHDRstate in displayConfig.DisplayHDRStates)
{ {
SharedLogger.logger.Trace($"Trying to get information whether HDR color is in use now on Display {myHDRstate.Id}."); SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Trying to get information whether HDR color is in use now on Display {myHDRstate.Id}.");
// Get advanced HDR info // Get advanced HDR info
var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO(); var colorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO; colorInfo.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
@ -805,9 +1141,10 @@ namespace DisplayMagicianShared.Windows
{ {
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Advanced Color Info gathered from Display {myHDRstate.Id}"); SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Advanced Color Info gathered from Display {myHDRstate.Id}");
if (myHDRstate.AdvancedColorInfo.AdvancedColorSupported && colorInfo.AdvancedColorEnabled != myHDRstate.AdvancedColorInfo.AdvancedColorEnabled) if (myHDRstate.AdvancedColorInfo.AdvancedColorEnabled != colorInfo.AdvancedColorEnabled)
{ {
SharedLogger.logger.Trace($"HDR is available for use on Display {myHDRstate.Id}, and we want it set to {myHDRstate.AdvancedColorInfo.AdvancedColorEnabled} but is currently {colorInfo.AdvancedColorEnabled}."); SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: HDR is available for use on Display {myHDRstate.Id}, and we want it set to {myHDRstate.AdvancedColorInfo.BitsPerColorChannel} but is currently {colorInfo.AdvancedColorEnabled}.");
var setColorState = new DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE(); var setColorState = new DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE();
setColorState.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE; setColorState.Header.Type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
@ -837,17 +1174,92 @@ namespace DisplayMagicianShared.Windows
} }
} }
// Get the existing displays config
Dictionary<string, GDI_DISPLAY_SETTING> currentGdiDisplaySettings = GetGdiDisplaySettings();
// 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 ");
foreach (var gdiDisplay in displayConfig.GdiDisplaySettings)
{
string displayDeviceKey = gdiDisplay.Key;
GDI_DISPLAY_SETTING displayDeviceSettings = displayConfig.GdiDisplaySettings[displayDeviceKey];
if (currentGdiDisplaySettings.ContainsKey(displayDeviceKey))
{
SharedLogger.logger.Trace($"WinLibrary/SetActiveConfig: Trying to change Device Mode for Display {displayDeviceKey}.");
GDI_DISPLAY_SETTING currentDeviceSetting = currentGdiDisplaySettings[displayDeviceKey];
// Use the current device as a base, but set some of the various settings we stored as part of the profile
currentDeviceSetting.DeviceMode.BitsPerPixel = displayDeviceSettings.DeviceMode.BitsPerPixel;
currentDeviceSetting.DeviceMode.DisplayOrientation = displayDeviceSettings.DeviceMode.DisplayOrientation;
currentDeviceSetting.DeviceMode.DisplayFrequency = displayDeviceSettings.DeviceMode.DisplayFrequency;
// 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);
if (result == CHANGE_DISPLAY_RESULTS.Successful)
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Successfully changed display {displayDeviceKey} to use the new mode!");
}
else if (result == CHANGE_DISPLAY_RESULTS.BadDualView)
{
SharedLogger.logger.Error($"WinLibrary/GetWindowsDisplayConfig: 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/GetWindowsDisplayConfig: 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/GetWindowsDisplayConfig: 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/GetWindowsDisplayConfig: 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/GetWindowsDisplayConfig: 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/GetWindowsDisplayConfig: 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/GetWindowsDisplayConfig: 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/GetWindowsDisplayConfig: Display {displayDeviceKey} not updated to use the new mode.");
}
}
else
{
SharedLogger.logger.Trace($"WinLibrary/GetWindowsDisplayConfig: Display {displayDeviceKey} is not currently in use, so cannot set it!");
}
}
return true; return true;
} }
public bool IsActiveConfig(WINDOWS_DISPLAY_CONFIG displayConfig) public bool IsActiveConfig(WINDOWS_DISPLAY_CONFIG displayConfig)
{ {
// Get the current windows display configs to compare to the one we loaded
WINDOWS_DISPLAY_CONFIG currentWindowsDisplayConfig = GetWindowsDisplayConfig(QDC.QDC_ONLY_ACTIVE_PATHS);
// Check whether the display config is in use now // Check whether the display config is in use now
SharedLogger.logger.Trace($"WinLibrary/IsActiveConfig: Checking whether the display configuration is already being used."); SharedLogger.logger.Trace($"WinLibrary/IsActiveConfig: Checking whether the display configuration is already being used.");
if (displayConfig.Equals(currentWindowsDisplayConfig)) if (displayConfig.Equals(ActiveDisplayConfig))
{ {
SharedLogger.logger.Trace($"WinLibrary/IsActiveConfig: The display configuration is already being used (supplied displayConfig Equals currentWindowsDisplayConfig"); SharedLogger.logger.Trace($"WinLibrary/IsActiveConfig: The display configuration is already being used (supplied displayConfig Equals currentWindowsDisplayConfig");
return true; return true;
@ -1087,6 +1499,15 @@ namespace DisplayMagicianShared.Windows
displayInfo.Add("#"); displayInfo.Add("#");
} }
try try
{
displayInfo.Add(targetInfo.MonitorDevicePath.ToString());
}
catch (Exception ex)
{
SharedLogger.logger.Warn(ex, $"WinLibrary/GetSomeDisplayIdentifiers: Exception getting Windows Path Target Info Id from video card. Substituting with a # instead");
displayInfo.Add("#");
}
try
{ {
displayInfo.Add(targetInfo.MonitorFriendlyDeviceName.ToString()); displayInfo.Add(targetInfo.MonitorFriendlyDeviceName.ToString());
} }
@ -1228,6 +1649,25 @@ namespace DisplayMagicianShared.Windows
} }
public static bool GDISettingsEqual(Dictionary<string, GDI_DISPLAY_SETTING> gdi1, Dictionary<string, GDI_DISPLAY_SETTING> gdi2)
{
if (gdi1.Count == gdi2.Count)
{
for (int i = 0; i < gdi1.Count; i++)
{
if (gdi1.Values.ToList()[i] != gdi2.Values.ToList()[i])
{
return false;
}
}
return true;
}
else
{
return false;
}
}
} }
[global::System.Serializable] [global::System.Serializable]

View File

@ -24,16 +24,18 @@
<UseVSHostingProcess>true</UseVSHostingProcess> <UseVSHostingProcess>true</UseVSHostingProcess>
<RegisterForComInterop>false</RegisterForComInterop> <RegisterForComInterop>false</RegisterForComInterop>
<CodeAnalysisRuleSet>DisplayMagicianShellExtension.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>DisplayMagicianShellExtension.ruleset</CodeAnalysisRuleSet>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>DisplayMagicianShellExtension.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>DisplayMagicianShellExtension.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisIgnoreGeneratedCode>true</CodeAnalysisIgnoreGeneratedCode> <CodeAnalysisIgnoreGeneratedCode>true</CodeAnalysisIgnoreGeneratedCode>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>Resources\DisplayMagician.ico</ApplicationIcon> <ApplicationIcon>Resources\DisplayMagician.ico</ApplicationIcon>

View File

@ -35,5 +35,5 @@ using System.Runtime.InteropServices;
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.0.0")] [assembly: AssemblyVersion("2.1.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyFileVersion("2.1.0.0")]

View File

@ -10,6 +10,11 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -15,13 +15,15 @@ The latest version of this application is available for download via the [releas
<p align="center"><a href="https://github.com/terrymacdonald/DisplayMagician/releases/latest"><img src="READMEAssets/gh-download.png"/></a></p> <p align="center"><a href="https://github.com/terrymacdonald/DisplayMagician/releases/latest"><img src="READMEAssets/gh-download.png"/></a></p>
** IMPORTANT! ** - If you tried DisplayMagician in the past and it didn't work for you, please try it again. THere has been a complete rewrite of the NVIDIA, AMD and Windows video manipulation libraries, and it is able to track and manipulate nearly all display configuration changes!
## What it does ## What it does
Different games require your displays configured in different ways. If you're a simracer like me, you also require a lot of additional 'helper' applications the give you the additional functionality to game the way you want. Making all those changes each time I wanted to play each game REALLY started annoying me, and I thought there must be a better way. Different games require your displays configured in different ways. If you're a simracer like me, you also require a lot of additional 'helper' applications the give you the additional functionality to game the way you want. Making all those changes each time I wanted to play each game REALLY started annoying me, and I thought there must be a better way.
There is now. DisplayMagician allows you to configure multiple different display profiles, and then use those different display profiles to create Game Shortcuts. These Game Shortcuts allow you to have your game or application start exactly the way you like it. There is now. DisplayMagician allows you to configure multiple different display profiles, and then use those different display profiles to create Game Shortcuts. These Game Shortcuts allow you to have your game or application start exactly the way you like it.
Do you like running Dirt Rally 2.0 on a single NVidia Surround window across triple screens, and yet you like to run Assetto Corsa across four individual screens (a triple and one above)? Do you like running SimHub when you play iRacing, yet you want to start Twitch when you play Call of Duty? Well with DisplayMagician you can do all that with a single Desktop Shortcut (you can even start games with a Hotkey)! Do you like running Dirt Rally 2.0 on a single NVIDIA Surround window across triple screens, and yet you like to run Assetto Corsa across four individual screens (a triple and one above)? Do you like running SimHub when you play iRacing, yet you want to start Twitch when you play Call of Duty? Well with DisplayMagician you can do all that with a single Desktop Shortcut (you can even start games with a Hotkey)!
DisplayMagician also allows you to automatically change to a different audio device just for one game, and will revert that change when you close the game. Great if you have some special audio devices you use only for certain games. No more fiddling with audio settings - just play the game! DisplayMagician also allows you to automatically change to a different audio device just for one game, and will revert that change when you close the game. Great if you have some special audio devices you use only for certain games. No more fiddling with audio settings - just play the game!
@ -48,16 +50,18 @@ 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. * 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! * 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 NVIDIA Surround setups, AMD Eyefinity setups and standard Windows multi-desktop views.
* Supports cloned displays, extended displays, NVIDIA Surround with additional displays, and nearly anything else you can throw at it!
## Planned features ## Planned features
* Change UI from Winforms to .NET6 and MAUI * Change UI from Winforms to .NET6 and MAUI
* Add Unit Tests! * Add Unit Tests!
* Maybe add Intel Alchemist GPU spanned screen support when they are eventually released. * Maybe add Intel Alchemist GPU spanned screen support when those cards are eventually released.
## Requirements ## Requirements
* DisplayMagician only support 64-bit Windows 10/11 * DisplayMagician only supports 64-bit Windows 10/11
* Your displays must use HDMI or DisplayPort connections and support EDID
* NVIDIA Surround support requires NVIDIA Game Ready driver to be installed * 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 * AMD Eyefinity support requires AMD Radeon™ Software Adrenalin 2020 Edition 21.2.1 or later to be installed
@ -141,6 +145,15 @@ Thanks for the work and the time that all of our contributors put into making th
* Original HelioDisplayManagement project created by the amazing Soroush Falahati * Original HelioDisplayManagement project created by the amazing Soroush Falahati
* Various icons made by Freepik from www.flaticon.com * Various icons made by Freepik from www.flaticon.com
* Erbkaiser (for the amazing help bug squashing)
* SneakingJ (Major props for the help getting cloned display profiles to work)
* Scowling (Again, without you, AMD support wouldnt exist for v2.1)
* Slippery-silvia (Again, thanks for the testing help for Mosaic cards with v2.1)
* Domenic (Thanks for the testing help as always)
* Mobeeuz (Your thorough testing logs really helped!)
* S4b0tage (Thanks for the testing help and the coffees)
* Gpo123 (thanks for the bug report)
* Whitestar127 (thanks for the bug report)
* Scowling (Without you, AMD support wouldnt exist) * Scowling (Without you, AMD support wouldnt exist)
* Frcooper (thanks for the sponsoring) * Frcooper (thanks for the sponsoring)
* Stringfieldmark (thanks for sponsoring and the testing help) * Stringfieldmark (thanks for sponsoring and the testing help)
@ -148,8 +161,5 @@ Thanks for the work and the time that all of our contributors put into making th
* Sk666 (thanks for the donation) * Sk666 (thanks for the donation)
* FormelLMS (thanks for the donation and the testing help) * FormelLMS (thanks for the donation and the testing help)
* RBZL (thanks for the donation) * RBZL (thanks for the donation)
* Domenic (thanks for the testing help)
* Mobeeuz (thanks for the testing help)
* Slippery-silvia (thanks for the testing help for Mosaic cards)
* Neilperson (thanks for the cool idea) * Neilperson (thanks for the cool idea)
* DragRedSim (thanks for the bug report) * DragRedSim (thanks for the bug report)

View File

@ -56,10 +56,25 @@ namespace DisplayMagician.UIForms
// Apply the Profile // Apply the Profile
if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Successful) if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Successful)
{ {
logger.Error($"DisplayProfileForm/Apply_Click: Waiting 0.5 sec for the display to apply"); 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); System.Threading.Thread.Sleep(500);
logger.Trace($"DisplayProfileForm/Apply_Click: Changing the selected profile in the imagelistview to Profile {_selectedProfile.Name}.");
ChangeSelectedProfile(_selectedProfile); ChangeSelectedProfile(_selectedProfile);
} }
else if (ProfileRepository.ApplyProfile(_selectedProfile) == ApplyProfileResult.Cancelled)
{
logger.Warn($"DisplayProfileForm/Apply_Click: The user cancelled changing to Profile {_selectedProfile.Name}.");
}
else
{
logger.Warn($"DisplayProfileForm/Apply_Click: Error applying the Profile {_selectedProfile.Name}. Unable to change the display layout.");
}
// Also refresh the right-click menu (if we have a main form loaded)
if (Program.AppMainForm is Form)
{
Program.AppMainForm.RefreshNotifyIconMenus();
}
} }
@ -115,6 +130,15 @@ namespace DisplayMagician.UIForms
} }
// 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) private void Save_Click(object sender, EventArgs e)
@ -169,6 +193,7 @@ namespace DisplayMagician.UIForms
{ {
MessageBox.Show(ex.Message, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Warning); MessageBox.Show(ex.Message, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Warning);
} }
} }
} }
@ -316,6 +341,12 @@ namespace DisplayMagician.UIForms
// Refresh the image list view // Refresh the image list view
//RefreshImageListView(profile); //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 // And finally refresh the profile in the display view
dv_profile.Profile = profile; dv_profile.Profile = profile;
dv_profile.Refresh(); dv_profile.Refresh();
@ -416,6 +447,12 @@ namespace DisplayMagician.UIForms
// now update the profiles image listview // now update the profiles image listview
RefreshDisplayProfileUI(); 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) private void ilv_saved_profiles_ItemClick(object sender, ItemClickEventArgs e)