Merge branch 'release/1.0.2' into main

This commit is contained in:
Terry MacDonald 2021-03-28 21:54:03 +13:00
commit 6cf3bd15a1
35 changed files with 445 additions and 74 deletions

View File

@ -91,8 +91,21 @@ namespace DisplayMagician.GameLibraries
catch (Exception ex) catch (Exception ex)
{ {
logger.Debug(ex, $"SteamGame/IsRunning: Accessing Process.MainModule caused exception. Trying GameUtils.GetMainModuleFilepath instead"); logger.Debug(ex, $"SteamGame/IsRunning: Accessing Process.MainModule caused exception. Trying GameUtils.GetMainModuleFilepath instead");
if (GameUtils.GetMainModuleFilepath(gameProcess.Id).StartsWith(_steamGameExePath))
numGameProcesses++; // If there is a race condition where MainModule isn't available, then we
// instead try the much slower GetMainModuleFilepath (which does the same thing)
string filePath = GameUtils.GetMainModuleFilepath(gameProcess.Id);
if (filePath == null)
{
// if we hit this bit then GameUtils.GetMainModuleFilepath failed,
// so we just skip that process
continue;
}
else
{
if (filePath.StartsWith(_steamGameExePath))
numGameProcesses++;
}
} }
} }
if (numGameProcesses > 0) if (numGameProcesses > 0)

View File

@ -94,8 +94,19 @@ namespace DisplayMagician.GameLibraries
logger.Debug(ex, $"UplayGame/IsRunning: Accessing Process.MainModule caused exception. Trying GameUtils.GetMainModuleFilepath instead"); logger.Debug(ex, $"UplayGame/IsRunning: Accessing Process.MainModule caused exception. Trying GameUtils.GetMainModuleFilepath instead");
// If there is a race condition where MainModule isn't available, then we // If there is a race condition where MainModule isn't available, then we
// instead try the much slower GetMainModuleFilepath (which does the same thing) // instead try the much slower GetMainModuleFilepath (which does the same thing)
if (GameUtils.GetMainModuleFilepath(gameProcess.Id).StartsWith(_uplayGameExePath)) string filePath = GameUtils.GetMainModuleFilepath(gameProcess.Id);
numGameProcesses++; if (filePath == null)
{
// if we hit this bit then GameUtils.GetMainModuleFilepath failed,
// so we just skip that process
continue;
}
else
{
if (filePath.StartsWith(_uplayGameExePath))
numGameProcesses++;
}
} }
} }
if (numGameProcesses > 0) if (numGameProcesses > 0)

View File

@ -52,6 +52,7 @@ namespace DisplayMagician
[System.Runtime.InteropServices.DllImport("Kernel32.dll")] [System.Runtime.InteropServices.DllImport("Kernel32.dll")]
public static extern Boolean CloseHandle(IntPtr handle); public static extern Boolean CloseHandle(IntPtr handle);
#pragma warning disable 0649
private struct IMAGELISTDRAWPARAMS private struct IMAGELISTDRAWPARAMS
{ {
public int cbSize; public int cbSize;
@ -72,6 +73,7 @@ namespace DisplayMagician
public int Frame; public int Frame;
public int crEffect; public int crEffect;
} }
#pragma warning restore 0649
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
private struct IMAGEINFO private struct IMAGEINFO
@ -125,7 +127,7 @@ namespace DisplayMagician
[PreserveSig] [PreserveSig]
int Remove( int Remove(
int i); int i);
[PreserveSig] [PreserveSig]
int GetIcon( int GetIcon(

View File

@ -25,6 +25,13 @@ namespace DisplayMagician {
Uplay Uplay
} }
public enum ApplyProfileResult
{
Successful,
Cancelled,
Error
}
internal static class Program internal static class Program
{ {
internal static string AppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DisplayMagician"); internal static string AppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DisplayMagician");
@ -501,7 +508,7 @@ namespace DisplayMagician {
} }
// ApplyProfile lives here so that the UI works. // ApplyProfile lives here so that the UI works.
public static bool ApplyProfile(ProfileItem profile) public static ApplyProfileResult ApplyProfile(ProfileItem profile)
{ {
logger.Debug($"Program/ApplyProfile: Starting"); logger.Debug($"Program/ApplyProfile: Starting");
@ -511,7 +518,7 @@ namespace DisplayMagician {
if (!profile.IsPossible) if (!profile.IsPossible)
{ {
logger.Debug($"Program/ApplyProfile: The supplied profile {profile.Name} isn't currently possible to use, so we can't apply it. This means a display that existed before has been removed, or moved."); logger.Debug($"Program/ApplyProfile: The supplied profile {profile.Name} isn't currently possible to use, so we can't apply it. This means a display that existed before has been removed, or moved.");
return false; return ApplyProfileResult.Error;
} }
@ -519,7 +526,7 @@ namespace DisplayMagician {
if (profile.UUID == ProfileRepository.GetActiveProfile().UUID) if (profile.UUID == ProfileRepository.GetActiveProfile().UUID)
{ {
logger.Debug($"Program/ApplyProfile: The supplied profile {profile.Name} is currently in use, so we don't need to apply it."); logger.Debug($"Program/ApplyProfile: The supplied profile {profile.Name} is currently in use, so we don't need to apply it.");
return false; return ApplyProfileResult.Successful;
} }
try try
@ -552,7 +559,7 @@ namespace DisplayMagician {
if (timeoutForm.ShowDialog() == DialogResult.Cancel) if (timeoutForm.ShowDialog() == DialogResult.Cancel)
{ {
return false; return ApplyProfileResult.Cancelled;
} }
// We only want to do the topology change if the profile we're on now // We only want to do the topology change if the profile we're on now
@ -612,7 +619,7 @@ namespace DisplayMagician {
if (!applyTopologyTask.IsCompleted) if (!applyTopologyTask.IsCompleted)
{ {
logger.Debug($"Program/ApplyProfile: Failed to complete applying or removing the NVIDIA Surround profile"); logger.Debug($"Program/ApplyProfile: Failed to complete applying or removing the NVIDIA Surround profile");
return false; return ApplyProfileResult.Error;
} }
} }
@ -645,7 +652,7 @@ namespace DisplayMagician {
logger.Debug($"Program/ApplyProfile: Applying Profile PathInfo stage failed to complete"); logger.Debug($"Program/ApplyProfile: Applying Profile PathInfo stage failed to complete");
if (!applyPathInfoTask.IsCompleted) if (!applyPathInfoTask.IsCompleted)
return false; return ApplyProfileResult.Error;
} }
catch (Exception ex) catch (Exception ex)
@ -653,13 +660,13 @@ namespace DisplayMagician {
Console.WriteLine($"ProfileRepository/ApplyTopology exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}"); Console.WriteLine($"ProfileRepository/ApplyTopology exception: {ex.Message}: {ex.StackTrace} - {ex.InnerException}");
{ {
logger.Debug($"Program/ApplyProfile: Failed to complete changing the Windows Display layout"); logger.Debug($"Program/ApplyProfile: Failed to complete changing the Windows Display layout");
return false; return ApplyProfileResult.Error;
} }
} }
ProfileRepository.UpdateActiveProfile(); ProfileRepository.UpdateActiveProfile();
return true; return ApplyProfileResult.Successful;
} }
public static bool LoadGamesInBackground() public static bool LoadGamesInBackground()

View File

@ -37,8 +37,8 @@ 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.0.1.*")] [assembly: AssemblyVersion("1.0.2.*")]
[assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: NeutralResourcesLanguage("en")] [assembly: NeutralResourcesLanguage("en")]
[assembly: CLSCompliant(true)] [assembly: CLSCompliant(true)]

View File

@ -441,20 +441,26 @@ namespace DisplayMagician
logger.Debug($"ShortcutRepository/LoadShortcuts: Connecting Shortcut profile names to the real profile objects"); logger.Debug($"ShortcutRepository/LoadShortcuts: Connecting Shortcut profile names to the real profile objects");
foreach (ShortcutItem updatedShortcut in _allShortcuts) foreach (ShortcutItem updatedShortcut in _allShortcuts)
{ {
bool foundProfile = false;
foreach (ProfileItem profile in ProfileRepository.AllProfiles) foreach (ProfileItem profile in ProfileRepository.AllProfiles)
{ {
if (profile.Equals(updatedShortcut.ProfileToUse)) if (profile.UUID.Equals(updatedShortcut.ProfileUUID))
{ {
// And assign the matching Profile if we find it. // And assign the matching Profile if we find it.
updatedShortcut.ProfileToUse = profile; updatedShortcut.ProfileToUse = profile;
foundProfile = true;
logger.Debug($"ShortcutRepository/LoadShortcuts: Found the profile with UUID {updatedShortcut.ProfileUUID} and linked it to a profile!");
break; break;
} }
} }
// We should only get here if there isn't a profile to match to. if (!foundProfile)
logger.Debug($"ShortcutRepository/LoadShortcuts: Couldn't find the profile with UUID {updatedShortcut.ProfileUUID} so couldn't link it to a profile! We can't use this shortcut."); {
updatedShortcut.ProfileToUse = null; // We should only get here if there isn't a profile to match to.
logger.Debug($"ShortcutRepository/LoadShortcuts: Couldn't find the profile with UUID {updatedShortcut.ProfileUUID} so couldn't link it to a profile! We can't use this shortcut.");
updatedShortcut.ProfileToUse = null;
}
} }
// Sort the shortcuts alphabetically // Sort the shortcuts alphabetically
@ -598,11 +604,20 @@ namespace DisplayMagician
{ {
logger.Info($"ShortcutRepository/RunShortcut: Changing to the {rollbackProfile.Name} profile."); logger.Info($"ShortcutRepository/RunShortcut: Changing to the {rollbackProfile.Name} profile.");
// Apply the Profile! // Apply the Profile!
if (!Program.ApplyProfile(shortcutToUse.ProfileToUse)) ApplyProfileResult result = Program.ApplyProfile(shortcutToUse.ProfileToUse);
if (result == ApplyProfileResult.Error)
{ {
Console.WriteLine($"ERROR - Cannot apply '{shortcutToUse.ProfileToUse.Name}' Display Profile"); 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;
} }
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");
return;
}
} }
// record the old audio device // record the old audio device
@ -946,6 +961,11 @@ namespace DisplayMagician
logger.Debug($"ShortcutRepository/RunShortcut: No more '{processNameToLookFor}' processes are still running"); logger.Debug($"ShortcutRepository/RunShortcut: No more '{processNameToLookFor}' processes are still running");
break; break;
} }
// Send a message to windows so that it doesn't think
// we're locked and try to kill us
System.Threading.Thread.CurrentThread.Join(0);
Thread.Sleep(1000);
} }
} }
Console.WriteLine($"{processNameToLookFor} has exited."); Console.WriteLine($"{processNameToLookFor} has exited.");
@ -1063,7 +1083,10 @@ namespace DisplayMagician
break; break;
} }
Thread.Sleep(300); // Send a message to windows so that it doesn't think
// we're locked and try to kill us
System.Threading.Thread.CurrentThread.Join(0);
Thread.Sleep(1000);
} }
Console.WriteLine($"{steamGameToRun.Name} has exited."); Console.WriteLine($"{steamGameToRun.Name} has exited.");
logger.Debug($"ShortcutRepository/RunShortcut: Steam Game {steamGameToRun.Name} has exited."); logger.Debug($"ShortcutRepository/RunShortcut: Steam Game {steamGameToRun.Name} has exited.");
@ -1222,7 +1245,10 @@ namespace DisplayMagician
break; break;
} }
Thread.Sleep(300); // Send a message to windows so that it doesn't think
// we're locked and try to kill us
System.Threading.Thread.CurrentThread.Join(0);
Thread.Sleep(1000);
} }
Console.WriteLine($"{uplayGameToRun.Name} has exited."); Console.WriteLine($"{uplayGameToRun.Name} has exited.");
logger.Debug($"ShortcutRepository/RunShortcut: Uplay Game {uplayGameToRun.Name} has exited."); logger.Debug($"ShortcutRepository/RunShortcut: Uplay Game {uplayGameToRun.Name} has exited.");
@ -1387,12 +1413,21 @@ namespace DisplayMagician
{ {
logger.Debug($"ShortcutRepository/RunShortcut: Rolling back display profile to {rollbackProfile.Name}"); logger.Debug($"ShortcutRepository/RunShortcut: Rolling back display profile to {rollbackProfile.Name}");
//if (!ProfileRepository.ApplyProfile(rollbackProfile)) ApplyProfileResult result = Program.ApplyProfile(rollbackProfile);
if (!Program.ApplyProfile(rollbackProfile))
if (result == ApplyProfileResult.Error)
{ {
Console.WriteLine($"ERROR - Cannot revert back to '{rollbackProfile.Name}' Display Profile"); Console.WriteLine($"ERROR - Cannot revert back to '{rollbackProfile.Name}' Display Profile");
logger.Error($"ShortcutRepository/RunShortcut: Rolling back display profile to {rollbackProfile.Name}"); logger.Error($"ShortcutRepository/RunShortcut: Error rolling back display profile to {rollbackProfile.Name}");
return;
} }
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}");
return;
}
} }
} }

View File

@ -35,6 +35,7 @@
this.progressBar = new CircularProgressBar.CircularProgressBar(); this.progressBar = new CircularProgressBar.CircularProgressBar();
this.lbl_message = new System.Windows.Forms.Label(); this.lbl_message = new System.Windows.Forms.Label();
this.t_countdown = new System.Windows.Forms.Timer(this.components); this.t_countdown = new System.Windows.Forms.Timer(this.components);
this.btn_close = new System.Windows.Forms.Button();
this.progressPanel.SuspendLayout(); this.progressPanel.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
@ -106,12 +107,28 @@
this.t_countdown.Interval = 1000; this.t_countdown.Interval = 1000;
this.t_countdown.Tick += new System.EventHandler(this.t_countdown_Tick); this.t_countdown.Tick += new System.EventHandler(this.t_countdown_Tick);
// //
// btn_close
//
this.btn_close.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btn_close.FlatAppearance.BorderSize = 0;
this.btn_close.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.btn_close.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.btn_close.ForeColor = System.Drawing.Color.White;
this.btn_close.Location = new System.Drawing.Point(759, 12);
this.btn_close.Name = "btn_close";
this.btn_close.Size = new System.Drawing.Size(29, 32);
this.btn_close.TabIndex = 2;
this.btn_close.Text = "X";
this.btn_close.UseVisualStyleBackColor = true;
this.btn_close.Click += new System.EventHandler(this.btn_close_Click);
//
// ApplyingProfileForm // ApplyingProfileForm
// //
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.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(50)))), ((int)(((byte)(50))))); this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(50)))), ((int)(((byte)(50)))), ((int)(((byte)(50)))));
this.ClientSize = new System.Drawing.Size(800, 450); this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.btn_close);
this.Controls.Add(this.progressPanel); this.Controls.Add(this.progressPanel);
this.DoubleBuffered = true; this.DoubleBuffered = true;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
@ -142,5 +159,6 @@
private System.Windows.Forms.Label lbl_sub_message; private System.Windows.Forms.Label lbl_sub_message;
private System.Windows.Forms.Label lbl_message; private System.Windows.Forms.Label lbl_message;
private System.Windows.Forms.Timer t_countdown; private System.Windows.Forms.Timer t_countdown;
private System.Windows.Forms.Button btn_close;
} }
} }

View File

@ -274,6 +274,10 @@ namespace DisplayMagician.UIForms
base.WndProc(ref m); base.WndProc(ref m);
} }
private void btn_close_Click(object sender, EventArgs e)
{
this.Close();
}
} }
} }

View File

@ -51,8 +51,10 @@ namespace DisplayMagician.UIForms
} }
// Apply the Profile // Apply the Profile
Program.ApplyProfile(_selectedProfile); if (Program.ApplyProfile(_selectedProfile) == ApplyProfileResult.Successful)
{
ChangeSelectedProfile(_selectedProfile);
}
} }
@ -94,7 +96,7 @@ namespace DisplayMagician.UIForms
// select the // select the
foreach (ProfileItem newSelectedProfile in ProfileRepository.AllProfiles) foreach (ProfileItem newSelectedProfile in ProfileRepository.AllProfiles)
{ {
if (newSelectedProfile.Name.Equals(ilv_saved_profiles.Items[ilvItemToSelect].Text)) if (newSelectedProfile.UUID.Equals(ilv_saved_profiles.Items[ilvItemToSelect].EquipmentModel))
{ {
ChangeSelectedProfile(newSelectedProfile); ChangeSelectedProfile(newSelectedProfile);
} }

View File

@ -96,7 +96,7 @@ namespace DisplayMagician.UIForms
{ {
Rectangle pos = Utility.GetSizedImageBounds(img, new Rectangle(bounds.Location + itemPadding, ImageListView.ThumbnailSize)); Rectangle pos = Utility.GetSizedImageBounds(img, new Rectangle(bounds.Location + itemPadding, ImageListView.ThumbnailSize));
ShortcutItem shortcutToRender = ShortcutRepository.GetShortcut(item.Text); ShortcutItem shortcutToRender = ShortcutRepository.GetShortcut(item.EquipmentModel);
if (shortcutToRender.IsValid == ShortcutValidity.Error) if (shortcutToRender.IsValid == ShortcutValidity.Error)
{ {
// The shortcut is permanently invalid (game removed or profile deleted) // The shortcut is permanently invalid (game removed or profile deleted)
@ -267,7 +267,7 @@ namespace DisplayMagician.UIForms
{ {
Rectangle pos = Utility.GetSizedImageBounds(img, new Rectangle(bounds.Location + itemPadding, ImageListView.ThumbnailSize)); Rectangle pos = Utility.GetSizedImageBounds(img, new Rectangle(bounds.Location + itemPadding, ImageListView.ThumbnailSize));
ProfileItem profileToRender = ProfileRepository.GetProfile(item.Text); ProfileItem profileToRender = ProfileRepository.GetProfile(item.EquipmentModel);
if (profileToRender.IsPossible) if (profileToRender.IsPossible)
{ {
// Draw the full color image as the shortcuts is not invalid // Draw the full color image as the shortcuts is not invalid

View File

@ -98,7 +98,7 @@ namespace DisplayMagician.UIForms
profileToUse = ProfileRepository.CurrentProfile; profileToUse = ProfileRepository.CurrentProfile;
} }
return profileToUse.Name; return profileToUse.UUID;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -205,7 +205,7 @@ namespace DisplayMagician.UIForms
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Dimensions, string.Empty, mySize)); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Dimensions, string.Empty, mySize));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Resolution, string.Empty, mySizeF)); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Resolution, string.Empty, mySizeF));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.ImageDescription, string.Empty, name ?? "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.ImageDescription, string.Empty, name ?? ""));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.EquipmentModel, string.Empty, "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.EquipmentModel, string.Empty, profileToUse.UUID));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.DateTaken, string.Empty, now)); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.DateTaken, string.Empty, now));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Artist, string.Empty, "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Artist, string.Empty, ""));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Copyright, string.Empty, "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Copyright, string.Empty, ""));

View File

@ -77,7 +77,7 @@ namespace DisplayMagician.UIForms
ShortcutItem shortcut = (ShortcutItem) key; ShortcutItem shortcut = (ShortcutItem) key;
//return shortcut.Name; //return shortcut.Name;
return shortcut.Name; return shortcut.UUID;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -181,7 +181,7 @@ namespace DisplayMagician.UIForms
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Dimensions, string.Empty, mySize)); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Dimensions, string.Empty, mySize));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Resolution, string.Empty, mySizeF)); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Resolution, string.Empty, mySizeF));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.ImageDescription, string.Empty, name ?? "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.ImageDescription, string.Empty, name ?? ""));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.EquipmentModel, string.Empty, "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.EquipmentModel, string.Empty, shortcut.UUID));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.DateTaken, string.Empty, now)); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.DateTaken, string.Empty, now));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Artist, string.Empty, "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Artist, string.Empty, ""));
details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Copyright, string.Empty, "")); details.Add(new Utility.Tuple<ColumnType, string, object>(ColumnType.Copyright, string.Empty, ""));

View File

@ -100,6 +100,11 @@ namespace DisplayMagician.UIForms
return (from item in ShortcutRepository.AllShortcuts where item.Name == shortcutName select item).First(); return (from item in ShortcutRepository.AllShortcuts where item.Name == shortcutName select item).First();
} }
private ShortcutItem GetShortcutFromUUID(string shortcutUUID)
{
return (from item in ShortcutRepository.AllShortcuts where item.UUID == shortcutUUID select item).First();
}
private void btn_save_Click(object sender, EventArgs e) private void btn_save_Click(object sender, EventArgs e)
{ {
//DialogResult = DialogResult.None; //DialogResult = DialogResult.None;
@ -252,8 +257,8 @@ namespace DisplayMagician.UIForms
private void btn_edit_Click(object sender, EventArgs e) private void btn_edit_Click(object sender, EventArgs e)
{ {
int currentIlvIndex = ilv_saved_shortcuts.SelectedItems[0].Index; int currentIlvIndex = ilv_saved_shortcuts.SelectedItems[0].Index;
string shortcutName = ilv_saved_shortcuts.Items[currentIlvIndex].Text; string shortcutUUID = ilv_saved_shortcuts.Items[currentIlvIndex].EquipmentModel;
_selectedShortcut = GetShortcutFromName(shortcutName); _selectedShortcut = GetShortcutFromUUID(shortcutUUID);
if (_selectedShortcut == null) if (_selectedShortcut == null)
return; return;
@ -262,9 +267,10 @@ namespace DisplayMagician.UIForms
// We need to stop ImageListView redrawing things before we're ready // We need to stop ImageListView redrawing things before we're ready
// This stops an exception when ILV is just too keen! // This stops an exception when ILV is just too keen!
ilv_saved_shortcuts.SuspendLayout();
var shortcutForm = new ShortcutForm(_selectedShortcut); var shortcutForm = new ShortcutForm(_selectedShortcut);
//ilv_saved_shortcuts.SuspendLayout();
shortcutForm.ShowDialog(this); shortcutForm.ShowDialog(this);
if (shortcutForm.DialogResult == DialogResult.OK) if (shortcutForm.DialogResult == DialogResult.OK)
{ {

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.1.0")] [assembly: AssemblyVersion("1.0.2.0")]
[assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyFileVersion("1.0.2.0")]

View File

@ -6,7 +6,7 @@
--> -->
<?define MajorVersion="1" ?> <?define MajorVersion="1" ?>
<?define MinorVersion="0" ?> <?define MinorVersion="0" ?>
<?define BuildVersion="1" ?> <?define BuildVersion="2" ?>
<!-- Revision is NOT used by WiX in the upgrade procedure --> <!-- Revision is NOT used by WiX in the upgrade procedure -->
<!-- Full version number to display --> <!-- Full version number to display -->
<?define VersionNumber="$(var.MajorVersion).$(var.MinorVersion).$(var.BuildVersion)" ?> <?define VersionNumber="$(var.MajorVersion).$(var.MinorVersion).$(var.BuildVersion)" ?>

View File

@ -326,12 +326,69 @@ namespace DisplayMagicianShared
// If run-time types are not exactly the same, return false. // If run-time types are not exactly the same, return false.
if (this.GetType() != other.GetType()) if (this.GetType() != other.GetType())
return false; return false;
if (Paths.Length != other.Paths.Length)
return false;
// Check if the profile identifiers are not the same, then return false
int foundDICount = 0;
foreach (string profileDI in ProfileDisplayIdentifiers)
{
if (other.ProfileDisplayIdentifiers.Contains(profileDI))
{
foundDICount++;
continue;
}
}
if (foundDICount != other.ProfileDisplayIdentifiers.Count)
return false;
foundDICount = 0;
foreach (string profileDI in other.ProfileDisplayIdentifiers)
{
if (ProfileDisplayIdentifiers.Contains(profileDI))
{
foundDICount++;
continue;
}
}
if (foundDICount != ProfileDisplayIdentifiers.Count)
return false;
// Check whether the profiles' properties are equal // Check whether the profiles' properties are equal
// We need to exclude the name as the name is solely for saving to disk // We need to exclude the name as the name is solely for saving to disk
// and displaying to the user. // and displaying to the user.
// Two profiles are equal only when they have the same viewport data // Two profiles are equal only when they have the same viewport data
if (Paths.SequenceEqual(other.Paths)) // The data may be in different orders each run, so we need to compare them one by one
int foundPathsCount = 0;
int foundOtherPathsCount = 0;
foreach (Topology.Path profilePath in Paths)
{
if (other.Paths.Contains(profilePath))
{
foundPathsCount++;
continue;
}
}
foreach (Topology.Path otherPath in other.Paths)
{
if (Paths.Contains(otherPath))
{
foundOtherPathsCount++;
continue;
}
}
if (foundPathsCount == foundOtherPathsCount)
return true; return true;
else else
return false; return false;
@ -339,7 +396,7 @@ namespace DisplayMagicianShared
// If Equals() returns true for this object compared to another // If Equals() returns true for this object compared to another
// then GetHashCode() must return the same value for these objects. // then GetHashCode() must return the same value for these objects.
public override int GetHashCode() /*public override int GetHashCode()
{ {
// Get hash code for the Viewports field if it is not null. // Get hash code for the Viewports field if it is not null.
@ -348,8 +405,21 @@ namespace DisplayMagicianShared
//Calculate the hash code for the product. //Calculate the hash code for the product.
return hashPaths; return hashPaths;
} }*/
public override int GetHashCode()
{
// Get hash code for the ProfileDisplayIdentifiers field if it is not null.
int hashIds = ProfileDisplayIdentifiers == null ? 0 : ProfileDisplayIdentifiers.GetHashCode();
// Get Paths too
int hashPaths = Paths == null ? 0 : Paths.GetHashCode();
// Calculate the hash code for the product.
return (hashIds, hashPaths).GetHashCode();
}
public override string ToString() public override string ToString()
{ {
@ -445,8 +515,10 @@ namespace DisplayMagicianShared
} }
if (validDisplayCount == ProfileDisplayIdentifiers.Count) if (validDisplayCount == ProfileDisplayIdentifiers.Count)
{ {
SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The profile {Name} is possible!"); SharedLogger.logger.Debug($"ProfileRepository/IsPossibleRefresh: The profile {Name} is possible!");
_isPossible = true; _isPossible = true;
} }
else else
{ {
@ -462,7 +534,7 @@ namespace DisplayMagicianShared
class ProfileComparer : IEqualityComparer<ProfileItem> class ProfileComparer : IEqualityComparer<ProfileItem>
{ {
// Products are equal if their names and product numbers are equal. // Products are equal if their names and product numbers are equal.
public bool Equals(ProfileItem x, ProfileItem y) /*public bool Equals(ProfileItem x, ProfileItem y)
{ {
//Check whether the compared objects reference the same data. //Check whether the compared objects reference the same data.
@ -480,11 +552,83 @@ namespace DisplayMagicianShared
return true; return true;
else else
return false; return false;
}*/
public bool Equals(ProfileItem x, ProfileItem y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (x is null || y is null)
return false;
if (x.Paths.Length != y.Paths.Length)
return false;
// Check if the profile identifiers are not the same, then return false
int foundDICount = 0;
foreach (string profileDI in x.ProfileDisplayIdentifiers)
{
if (y.ProfileDisplayIdentifiers.Contains(profileDI))
{
foundDICount++;
continue;
}
}
if (foundDICount != x.ProfileDisplayIdentifiers.Count)
return false;
foundDICount = 0;
foreach (string profileDI in y.ProfileDisplayIdentifiers)
{
if (x.ProfileDisplayIdentifiers.Contains(profileDI))
{
foundDICount++;
continue;
}
}
if (foundDICount != y.ProfileDisplayIdentifiers.Count)
return false;
// Check whether the profiles' properties are equal
// We need to exclude the name as the name is solely for saving to disk
// and displaying to the user.
// Two profiles are equal only when they have the same viewport data
int foundPathsCount = 0;
int foundOtherPathsCount = 0;
foreach (Topology.Path profilePath in x.Paths)
{
if (y.Paths.Contains(profilePath))
{
foundPathsCount++;
continue;
}
}
foreach (Topology.Path otherPath in y.Paths)
{
if (x.Paths.Contains(otherPath))
{
foundOtherPathsCount++;
continue;
}
}
if (foundPathsCount == foundOtherPathsCount)
return true;
else
return false;
} }
// If Equals() returns true for a pair of objects // If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects. // then GetHashCode() must return the same value for these objects.
public int GetHashCode(ProfileItem profile) /*public int GetHashCode(ProfileItem profile)
{ {
// Check whether the object is null // Check whether the object is null
@ -496,7 +640,23 @@ namespace DisplayMagicianShared
//Calculate the hash code for the product. //Calculate the hash code for the product.
return hashPaths; return hashPaths;
} }*/
// Modified the GetHashCode to compare the displayidentifier
public int GetHashCode(ProfileItem profile)
{
// Check whether the object is null
if (profile is null) return 0;
// Get hash code for the ProfileDisplayIdentifiers field if it is not null.
int hashIds = profile.ProfileDisplayIdentifiers == null ? 0 : profile.ProfileDisplayIdentifiers.GetHashCode();
// Get hash code for the Paths
int hashPaths = profile.Paths == null ? 0 : profile.Paths.GetHashCode();
//Calculate the hash code for the product.
return (hashIds,hashPaths).GetHashCode();
}
} }
} }

View File

@ -16,6 +16,7 @@ using NvAPIWrapper.Native.GPU;
namespace DisplayMagicianShared namespace DisplayMagicianShared
{ {
public static class ProfileRepository public static class ProfileRepository
{ {
#region Class Variables #region Class Variables
@ -47,10 +48,10 @@ namespace DisplayMagicianShared
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the NvAPIWrapper.NVIDIA library."); SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Initialising the NvAPIWrapper.NVIDIA library.");
NvAPIWrapper.NVIDIA.Initialize(); NvAPIWrapper.NVIDIA.Initialize();
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Creating the Profiles storage folder {AppProfileStoragePath}.");
// Create the Profile Storage Path if it doesn't exist so that it's avilable for all the program // Create the Profile Storage Path if it doesn't exist so that it's avilable for all the program
if (!Directory.Exists(AppProfileStoragePath)) if (!Directory.Exists(AppProfileStoragePath))
{ {
SharedLogger.logger.Debug($"ProfileRepository/ProfileRepository: Creating the Profiles storage folder {AppProfileStoragePath}.");
Directory.CreateDirectory(AppProfileStoragePath); Directory.CreateDirectory(AppProfileStoragePath);
} }
} }
@ -406,7 +407,8 @@ namespace DisplayMagicianShared
foreach (ProfileItem testProfile in _allProfiles) foreach (ProfileItem testProfile in _allProfiles)
{ {
if (testProfile.Paths.SequenceEqual(_currentProfile.Paths)) // TODO - change for Equals
if (testProfile.Equals(_currentProfile))
{ {
SharedLogger.logger.Debug($"ProfileRepository/ContainsCurrentProfile: Our profile repository does contain the display profile currently in use"); SharedLogger.logger.Debug($"ProfileRepository/ContainsCurrentProfile: Our profile repository does contain the display profile currently in use");
return true; return true;
@ -495,7 +497,7 @@ namespace DisplayMagicianShared
} }
public static void UpdateActiveProfile() /*public static void UpdateActiveProfile()
{ {
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).");
@ -526,9 +528,43 @@ namespace DisplayMagicianShared
//IsPossibleRefresh(); //IsPossibleRefresh();
} }*/
public static void UpdateActiveProfile()
{
SharedLogger.logger.Debug($"ProfileRepository/UpdateActiveProfile: Updating the profile currently active (in use now).");
ProfileItem activeProfile = new ProfileItem
{
Name = "Current Display Profile",
Paths = PathInfo.GetActivePaths().Select(info => new DisplayMagicianShared.Topology.Path(info)).ToArray(),
//ProfileDisplayIdentifiers = ProfileRepository.GenerateProfileDisplayIdentifiers()
};
activeProfile.ProfileIcon = new ProfileIcon(activeProfile);
activeProfile.ProfileBitmap = activeProfile.ProfileIcon.ToBitmap(256, 256);
if (_profilesLoaded && _allProfiles.Count > 0)
{
foreach (ProfileItem loadedProfile in ProfileRepository.AllProfiles)
{
if (activeProfile.Equals(loadedProfile))
{
_currentProfile = loadedProfile;
SharedLogger.logger.Debug($"ProfileRepository/UpdateActiveProfile: The profile {loadedProfile.Name} is currently active (in use now).");
return;
}
}
}
SharedLogger.logger.Debug($"ProfileRepository/UpdateActiveProfile: The current profile is a new profile that doesn't already exist in the Profile Repository.");
_currentProfile = activeProfile;
//IsPossibleRefresh();
}
public static ProfileItem GetActiveProfile() public static ProfileItem GetActiveProfile()
{ {
if (!(_currentProfile is ProfileItem)) if (!(_currentProfile is ProfileItem))
@ -549,7 +585,8 @@ namespace DisplayMagicianShared
SharedLogger.logger.Debug($"ProfileRepository/IsActiveProfile: Checking whether the profile {profile.Name} is the currently active profile."); SharedLogger.logger.Debug($"ProfileRepository/IsActiveProfile: Checking whether the profile {profile.Name} is the currently active profile.");
if (profile.Paths.SequenceEqual(_currentProfile.Paths)) //if (profile.Paths.SequenceEqual(_currentProfile.Paths))
if (profile.Equals(_currentProfile))
{ {
SharedLogger.logger.Debug($"ProfileRepository/IsActiveProfile: The profile {profile.Name} is the currently active profile."); SharedLogger.logger.Debug($"ProfileRepository/IsActiveProfile: The profile {profile.Name} is the currently active profile.");
return true; return true;
@ -877,6 +914,9 @@ namespace DisplayMagicianShared
} }
// Sort the display identifiers
displayIdentifiers.Sort();
return displayIdentifiers; return displayIdentifiers;
} }
@ -1082,6 +1122,9 @@ namespace DisplayMagicianShared
} }
// Sort the display identifiers
displayIdentifiers.Sort();
return displayIdentifiers; return displayIdentifiers;
} }

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.0.1.*")] [assembly: AssemblyVersion("1.0.2.*")]
[assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyFileVersion("1.0.2.0")]

View File

@ -77,16 +77,33 @@ namespace DisplayMagicianShared.Topology
// Check whether the Profile Viewport properties are equal // Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly // Two profiles are equal only when they have the same viewport data exactly
/*if (PixelFormat == other.PixelFormat &&
Position.Equals(other.Position) &&
Resolution.Equals(other.Resolution) &&
SourceId == other.SourceId)*/
// Note: Removed the source ID as it changes on boot sometimes!
// It can change and the profiles can still be the same
if (PixelFormat == other.PixelFormat && if (PixelFormat == other.PixelFormat &&
Position.Equals(other.Position) && Position.Equals(other.Position) &&
Resolution.Equals(other.Resolution) && Resolution.Equals(other.Resolution) &&
SourceId == other.SourceId) SourceId == other.SourceId)
{ {
// If the above all match, then we need to check the DisplayTargets /*// If the above all match, then we need to check the DisplayTargets
if (other.TargetDisplays.SequenceEqual(TargetDisplays)) if (other.TargetDisplays.SequenceEqual(TargetDisplays))
return true; return true;
else else
return false; return false;*/
foreach (PathTarget myTargetDisplay in TargetDisplays)
{
if (!other.TargetDisplays.Contains(myTargetDisplay))
return false;
}
/*foreach (PathTarget theirTargetDisplay in other.TargetDisplays)
{
if (!TargetDisplays.Contains(theirTargetDisplay))
return false;
}*/
return true;
} }
else else
return false; return false;
@ -114,10 +131,52 @@ namespace DisplayMagicianShared.Topology
//Calculate the hash code for the product. //Calculate the hash code for the product.
return hashPixelFormat ^ hashPosition ^ hashResolution ^ hashSourceId ^ hashTargetDisplays; return hashPixelFormat ^ hashPosition ^ hashResolution ^ hashSourceId ^ hashTargetDisplays;
} }
public bool IsPossible(Path other)
{
// If parameter is null, return false.
if (Object.ReferenceEquals(other, null))
return false;
// Optimization for a common success case.
if (Object.ReferenceEquals(this, other))
return true;
// If run-time types are not exactly the same, return false.
if (this.GetType() != other.GetType())
return false;
// Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly
/*if (PixelFormat == other.PixelFormat &&
Position.Equals(other.Position) &&
Resolution.Equals(other.Resolution) &&
SourceId == other.SourceId)*/
// Note: Removed the source ID as it changes on boot sometimes!
// It can change and the profiles can still be the same
if (PixelFormat == other.PixelFormat &&
Position.Equals(other.Position) &&
Resolution.Equals(other.Resolution))
return true;
else
return false;
}
public bool ContainsSurround()
{
foreach (PathTarget pathTarget in TargetDisplays)
{
if (pathTarget.SurroundTopology == null)
return false;
}
return true;
}
} }
// Custom comparer for the ProfileViewport class
class PathComparer : IEqualityComparer<Path> // Custom comparer for the ProfileViewport class
class PathComparer : IEqualityComparer<Path>
{ {
// Products are equal if their names and product numbers are equal. // Products are equal if their names and product numbers are equal.
public bool Equals(Path x, Path y) public bool Equals(Path x, Path y)
@ -132,6 +191,12 @@ namespace DisplayMagicianShared.Topology
// Check whether the Profile Viewport properties are equal // Check whether the Profile Viewport properties are equal
// Two profiles are equal only when they have the same viewport data exactly // Two profiles are equal only when they have the same viewport data exactly
/*if (x.PixelFormat == y.PixelFormat &&
x.Position.Equals(y.Position) &&
x.Resolution.Equals(y.Resolution) &&
x.SourceId == y.SourceId)*/
// Note: Removed the source ID as it changes on boot sometimes!
// It can change and the profiles can still be the same
if (x.PixelFormat == y.PixelFormat && if (x.PixelFormat == y.PixelFormat &&
x.Position.Equals(y.Position) && x.Position.Equals(y.Position) &&
x.Resolution.Equals(y.Resolution) && x.Resolution.Equals(y.Resolution) &&
@ -139,16 +204,21 @@ namespace DisplayMagicianShared.Topology
{ {
// If the above all match, then we need to check the DisplayTargets // If the above all match, then we need to check the DisplayTargets
// If they aren't equal then we need to return false; // If they aren't equal then we need to return false;
if (!x.TargetDisplays.SequenceEqual(y.TargetDisplays)) /*if (!x.TargetDisplays.SequenceEqual(y.TargetDisplays))
return false; return false;
else else
return true; return true;*/
/* foreach (ProfileViewportTargetDisplay xTargetDisplay in x.TargetDisplays) foreach (PathTarget xTargetDisplay in x.TargetDisplays)
{ {
if (!y.TargetDisplays.Contains(xTargetDisplay)) if (!y.TargetDisplays.Contains(xTargetDisplay))
return false; return false;
}*/ }
//return true; /*foreach (PathTarget yTargetDisplay in y.TargetDisplays)
{
if (!x.TargetDisplays.Contains(yTargetDisplay))
return false;
}*/
return true;
} }
else else
return false; return false;

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.0.1.*")] [assembly: AssemblyVersion("1.0.2.*")]
[assembly: AssemblyFileVersion("1.0.1.0")] [assembly: AssemblyFileVersion("1.0.2.0")]

View File

@ -68,7 +68,7 @@ I am doing this work to scratch a programming itch I've had for a while. It's pr
<div style="text-align:center"><img src="READMEAssets/DisplayMagicianConfigureShortcut3.png"/></div> <div style="text-align:center"><img src="READMEAssets/DisplayMagicianConfigureShortcut3.png"/></div>
<div style="text-align:center"><img src="READMEAssets/DisplayMagicianConfigureShortcut4.png"/></div> <div style="text-align:center"><img src="READMEAssets/DisplayMagicianConfigureShortcut4.png"/></div>
<div style="text-align:center"><img src="READMEAssets/DisplayMagicianConfigureShortcut5.png"/></div> <div style="text-align:center"><img src="READMEAssets/DisplayMagicianConfigureShortcut5.png"/></div>
<div style="text-align:center"><img src="READMEAssets/HeliosPlusShellExtension.png"/></div> <div style="text-align:center"><img src="READMEAssets/DisplayMagicianShellExtension.png"/></div>
### Initial Setup: ### Initial Setup:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 KiB

After

Width:  |  Height:  |  Size: 766 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 447 KiB

After

Width:  |  Height:  |  Size: 594 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -65,7 +65,7 @@ I am doing this work to scratch a programming itch I've had for a while. It's pr
<div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/DisplayMagicianConfigureShortcut3.png"/></div> <div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/DisplayMagicianConfigureShortcut3.png"/></div>
<div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/DisplayMagicianConfigureShortcut4.png"/></div> <div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/DisplayMagicianConfigureShortcut4.png"/></div>
<div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/DisplayMagicianConfigureShortcut5.png"/></div> <div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/DisplayMagicianConfigureShortcut5.png"/></div>
<div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/HeliosPlusShellExtension.png"/></div> <div style="text-align:center"><img src="https://github.com/terrymacdonald/DisplayMagician/raw/main/READMEAssets/DisplayMagicianShellExtension.png"/></div>
### Initial Setup: ### Initial Setup:

View File

@ -1,6 +1,6 @@
{ {
"version": "1.0.1.0", "version": "1.0.2.0",
"url": "https://github.com/terrymacdonald/DisplayMagician/releases/download/v1.0.1/DisplayMagicianSetup-v1.0.1.msi", "url": "https://github.com/terrymacdonald/DisplayMagician/releases/download/v1.0.2/DisplayMagicianSetup-v1.0.2.msi",
"changelog": "https://github.com/terrymacdonald/DisplayMagician/releases", "changelog": "https://github.com/terrymacdonald/DisplayMagician/releases",
"mandatory": { "mandatory": {
"value": false, "value": false,
@ -8,7 +8,7 @@
"mode": 0 "mode": 0
}, },
"checksum": { "checksum": {
"value": "78067AE8F7F28101CB530110CFBF2AD9C47B809DC5D7365D4BD413D63D314FAC", "value": "49363731427276601051E6D70F1DA3BB389760FC73CEF5F7854EC88E5011BF40",
"hashingAlgorithm": "SHA256" "hashingAlgorithm": "SHA256"
} }
} }