From c5bc1e3d2688ee5819ddaf3d82c8f0dfc1350492 Mon Sep 17 00:00:00 2001 From: s_falahati Date: Sat, 20 Oct 2018 03:57:25 +0330 Subject: [PATCH] Cleanup + Enforcing styling --- HeliosDisplayManagement.Reporting/App.config | 9 +- .../FodyWeavers.xml | 1 + HeliosDisplayManagement.Reporting/Program.cs | 22 ++- .../Properties/AssemblyInfo.cs | 3 +- .../packages.config | 1 + HeliosDisplayManagement.Shared/Helios.cs | 48 ++++++- .../NVIDIA/SurroundHelper.cs | 95 ++++++++----- .../NVIDIA/SurroundTopology.cs | 122 ++++++++++------ .../NVIDIA/SurroundTopologyDisplay.cs | 45 ++++-- HeliosDisplayManagement.Shared/PixelShift.cs | 4 +- HeliosDisplayManagement.Shared/Profile.cs | 76 ++++++++-- HeliosDisplayManagement.Shared/ProfileIcon.cs | 132 +++++++++++++----- HeliosDisplayManagement.Shared/Scaling.cs | 2 +- .../ScanLineOrdering.cs | 4 +- .../Topology/Path.cs | 49 +++++-- .../Topology/PathHelper.cs | 4 +- .../Topology/PathTarget.cs | 67 ++++++--- .../UserControls/DisplayView.cs | 96 +++++++++---- .../packages.config | 1 + .../HeliosDesktopMenuExtension.cs | 17 ++- .../HeliosExecutableMenuExtension.cs | 17 ++- .../HeliosSteamUrlMenuExtension.cs | 39 +++++- .../Shield.cs | 5 + .../packages.config | 1 + HeliosDisplayManagement/CommandLineOptions.cs | 34 +++-- .../DisplayRepresentation.cs | 34 ++++- .../InterProcess/IPCClient.cs | 27 +++- .../InterProcess/IPCService.cs | 10 +- HeliosDisplayManagement/Program.cs | 124 +++++++++++++++- HeliosDisplayManagement/Steam/SteamGame.cs | 109 +++++++++++++-- HeliosDisplayManagement/UIForms/EditForm.cs | 119 +++++++++++++--- HeliosDisplayManagement/UIForms/MainForm.cs | 86 +++++++++--- .../UIForms/ShortcutForm.cs | 87 ++++++++++-- HeliosDisplayManagement/UIForms/SplashForm.cs | 22 ++- .../UIForms/SteamGamesForm.cs | 16 +++ HeliosDisplayManagement/packages.config | 1 + 36 files changed, 1217 insertions(+), 312 deletions(-) diff --git a/HeliosDisplayManagement.Reporting/App.config b/HeliosDisplayManagement.Reporting/App.config index b50c74f..8609e96 100644 --- a/HeliosDisplayManagement.Reporting/App.config +++ b/HeliosDisplayManagement.Reporting/App.config @@ -1,6 +1,7 @@ - + + - - - + + + \ No newline at end of file diff --git a/HeliosDisplayManagement.Reporting/FodyWeavers.xml b/HeliosDisplayManagement.Reporting/FodyWeavers.xml index c6e1b7c..100917a 100644 --- a/HeliosDisplayManagement.Reporting/FodyWeavers.xml +++ b/HeliosDisplayManagement.Reporting/FodyWeavers.xml @@ -1,4 +1,5 @@  + \ No newline at end of file diff --git a/HeliosDisplayManagement.Reporting/Program.cs b/HeliosDisplayManagement.Reporting/Program.cs index 0102395..449483e 100644 --- a/HeliosDisplayManagement.Reporting/Program.cs +++ b/HeliosDisplayManagement.Reporting/Program.cs @@ -88,7 +88,8 @@ namespace HeliosDisplayManagement.Reporting try { - Dump(PathDisplayAdapter.GetAdapters(), "WindowsDisplayAPI.DisplayConfig.PathDisplayAdapter.GetAdapters()", + Dump(PathDisplayAdapter.GetAdapters(), + "WindowsDisplayAPI.DisplayConfig.PathDisplayAdapter.GetAdapters()", new[] { new Tuple, string>(adapter => adapter.ToDisplayAdapter(), @@ -132,7 +133,8 @@ namespace HeliosDisplayManagement.Reporting { if (PathInfo.IsSupported) { - Dump(PathInfo.GetActivePaths(), "WindowsDisplayAPI.DisplayConfig.PathInfo.GetActivePaths()", null, 2); + Dump(PathInfo.GetActivePaths(), "WindowsDisplayAPI.DisplayConfig.PathInfo.GetActivePaths()", null, + 2); } } catch (Exception e) @@ -162,7 +164,8 @@ namespace HeliosDisplayManagement.Reporting { Dump(NvAPIWrapper.Display.Display.GetDisplays(), "NvAPIWrapper.Display.Display.GetDisplays()", new[] { - new Tuple, string>(display => display.GetSupportedViews(), + new Tuple, string>( + display => display.GetSupportedViews(), "GetSupportedViews()") }); } @@ -183,7 +186,8 @@ namespace HeliosDisplayManagement.Reporting try { - Dump(NvAPIWrapper.Display.PathInfo.GetDisplaysConfig(), "NvAPIWrapper.Display.PathInfo.GetDisplaysConfig()", + Dump(NvAPIWrapper.Display.PathInfo.GetDisplaysConfig(), + "NvAPIWrapper.Display.PathInfo.GetDisplaysConfig()", null, 3); } catch (Exception e) @@ -244,6 +248,7 @@ namespace HeliosDisplayManagement.Reporting { _writer.WriteLine(new string('_', Console.BufferWidth)); } + _writer.WriteLine("({0}) {{", obj.GetType().Name); } @@ -257,7 +262,8 @@ namespace HeliosDisplayManagement.Reporting foreach (var arrayItem in (IEnumerable) obj) { - _writer.WriteLine(new string(' ', padding * 3 + 2) + "[{0}]: ({1}) {{", i, arrayItem?.GetType().Name); + _writer.WriteLine(new string(' ', padding * 3 + 2) + "[{0}]: ({1}) {{", i, + arrayItem?.GetType().Name); WriteObject(arrayItem, padding + 1, default(TimeSpan), null, deepIn - 1); _writer.WriteLine(new string(' ', padding * 3 + 2) + "},"); i++; @@ -289,7 +295,8 @@ namespace HeliosDisplayManagement.Reporting !value.GetType().IsValueType && value.GetType() != typeof(string)) { - _writer.WriteLine(new string(' ', padding * 3 + 2) + "{0}: ({1}) {{", propertyInfo.Name, propertyInfo.PropertyType.Name); + _writer.WriteLine(new string(' ', padding * 3 + 2) + "{0}: ({1}) {{", propertyInfo.Name, + propertyInfo.PropertyType.Name); WriteObject(value, padding + 1, default(TimeSpan), null, deepIn - 1); _writer.WriteLine(new string(' ', padding * 3 + 2) + "},"); } @@ -306,7 +313,8 @@ namespace HeliosDisplayManagement.Reporting { foreach (var extraProperty in extraProperties) { - _writer.WriteLine(new string(' ', padding * 3 + 2) + "{0}: ({1}) {{", extraProperty.Item1, extraProperty.Item2?.GetType().Name); + _writer.WriteLine(new string(' ', padding * 3 + 2) + "{0}: ({1}) {{", extraProperty.Item1, + extraProperty.Item2?.GetType().Name); WriteObject(extraProperty.Item2, padding + 1, default(TimeSpan), null, deepIn); _writer.WriteLine(new string(' ', padding * 3 + 2) + "},"); } diff --git a/HeliosDisplayManagement.Reporting/Properties/AssemblyInfo.cs b/HeliosDisplayManagement.Reporting/Properties/AssemblyInfo.cs index 37177d2..94e63f9 100644 --- a/HeliosDisplayManagement.Reporting/Properties/AssemblyInfo.cs +++ b/HeliosDisplayManagement.Reporting/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -33,4 +32,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/HeliosDisplayManagement.Reporting/packages.config b/HeliosDisplayManagement.Reporting/packages.config index 09e9ce8..565df38 100644 --- a/HeliosDisplayManagement.Reporting/packages.config +++ b/HeliosDisplayManagement.Reporting/packages.config @@ -1,4 +1,5 @@  + diff --git a/HeliosDisplayManagement.Shared/Helios.cs b/HeliosDisplayManagement.Shared/Helios.cs index 1d642de..f5c5fc5 100644 --- a/HeliosDisplayManagement.Shared/Helios.cs +++ b/HeliosDisplayManagement.Shared/Helios.cs @@ -20,65 +20,102 @@ namespace HeliosDisplayManagement.Shared Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Helios Display Management", false)) { var executableAddress = key?.GetValue(@"ExecutableAddress", null) as string; + if (!string.IsNullOrWhiteSpace(executableAddress) && File.Exists(executableAddress)) + { return executableAddress; + } } } catch { // ignored } + return null; } } - public static bool IsInstalled => !string.IsNullOrWhiteSpace(Address); + public static bool IsInstalled + { + get => !string.IsNullOrWhiteSpace(Address); + } // ReSharper disable once MethodTooLong // ReSharper disable once TooManyArguments - public static void Open(HeliosStartupAction action = HeliosStartupAction.None, + public static void Open( + HeliosStartupAction action = HeliosStartupAction.None, Profile profile = null, - string programAddress = null, bool asAdmin = false) + string programAddress = null, + bool asAdmin = false) { try { if (!IsInstalled) + { return; + } + var args = new List {$"-a {action}"}; + if (profile != null) + { args.Add($"-p \"{profile.Name}\""); + } + if (!string.IsNullOrWhiteSpace(programAddress)) + { args.Add($"-e \"{programAddress}\""); + } + var processInfo = new ProcessStartInfo(Address, string.Join(" ", args)) { UseShellExecute = true }; + if (asAdmin) + { processInfo.Verb = @"runas"; + } + Process.Start(processInfo); } catch (Exception e) { // Check if operation canceled by user if ((e as Win32Exception)?.NativeErrorCode == 1223) + { return; + } + throw; } } public static void OpenSteamGame( - HeliosStartupAction action = HeliosStartupAction.None, Profile profile = null, + HeliosStartupAction action = HeliosStartupAction.None, + Profile profile = null, uint steamAppId = 0) { try { if (!IsInstalled) + { return; + } + var args = new List {$@"-a {action}"}; + if (profile != null) + { args.Add($"-p \"{profile.Name}\""); + } + if (steamAppId > 0) + { args.Add($"-s \"{steamAppId}\""); + } + var processInfo = new ProcessStartInfo(Address, string.Join(" ", args)) { UseShellExecute = true @@ -89,7 +126,10 @@ namespace HeliosDisplayManagement.Shared { // Check if operation canceled by user if ((e as Win32Exception)?.NativeErrorCode == 1223) + { return; + } + throw; } } diff --git a/HeliosDisplayManagement.Shared/NVIDIA/SurroundHelper.cs b/HeliosDisplayManagement.Shared/NVIDIA/SurroundHelper.cs index af72b53..517f378 100644 --- a/HeliosDisplayManagement.Shared/NVIDIA/SurroundHelper.cs +++ b/HeliosDisplayManagement.Shared/NVIDIA/SurroundHelper.cs @@ -5,64 +5,91 @@ namespace HeliosDisplayManagement.Shared.NVIDIA { internal static class SurroundHelper { + public static PixelShift ToPixelShift(this PixelShiftType pixelShift) + { + switch (pixelShift) + { + case PixelShiftType.TopLeft2X2Pixels: + + return PixelShift.TopLeft2X2Pixels; + + case PixelShiftType.BottomRight2X2Pixels: + + return PixelShift.BottomRight2X2Pixels; + + default: + + return PixelShift.NoPixelShift; + } + } + + public static PixelShiftType ToPixelShiftType(this PixelShift pixelShift) + { + switch (pixelShift) + { + case PixelShift.TopLeft2X2Pixels: + + return PixelShiftType.TopLeft2X2Pixels; + + case PixelShift.BottomRight2X2Pixels: + + return PixelShiftType.BottomRight2X2Pixels; + + default: + + return PixelShiftType.NoPixelShift; + } + } + public static Rotate ToRotate(this Rotation rotation) { switch (rotation) { case Rotation.Identity: + return Rotate.Degree0; + case Rotation.Rotate90: + return Rotate.Degree90; + case Rotation.Rotate180: + return Rotate.Degree180; + case Rotation.Rotate270: + return Rotate.Degree270; + default: + return Rotate.Ignored; } } - public static Rotation ToRotation(this Rotate rotation) { switch (rotation) { case Rotate.Degree0: - return Rotation.Identity; - case Rotate.Degree90: - return Rotation.Rotate90; - case Rotate.Degree180: - return Rotation.Rotate180; - case Rotate.Degree270: - return Rotation.Rotate270; - default: - return Rotation.Unknown; - } - } - - public static PixelShiftType ToPixelShiftType(this PixelShift pixelShift) - { - switch (pixelShift) - { - case PixelShift.TopLeft2X2Pixels: - return PixelShiftType.TopLeft2X2Pixels; - case PixelShift.BottomRight2X2Pixels: - return PixelShiftType.BottomRight2X2Pixels; - default: - return PixelShiftType.NoPixelShift; - } - } - public static PixelShift ToPixelShift(this PixelShiftType pixelShift) - { - switch (pixelShift) - { - case PixelShiftType.TopLeft2X2Pixels: - return PixelShift.TopLeft2X2Pixels; - case PixelShiftType.BottomRight2X2Pixels: - return PixelShift.BottomRight2X2Pixels; + return Rotation.Identity; + + case Rotate.Degree90: + + return Rotation.Rotate90; + + case Rotate.Degree180: + + return Rotation.Rotate180; + + case Rotate.Degree270: + + return Rotation.Rotate270; + default: - return PixelShift.NoPixelShift; + + return Rotation.Unknown; } } } diff --git a/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopology.cs b/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopology.cs index 7fca10e..3fab14f 100644 --- a/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopology.cs +++ b/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopology.cs @@ -19,8 +19,8 @@ namespace HeliosDisplayManagement.Shared.NVIDIA Displays = topology.Displays.Where( display => - (Resolution.Width > display.Overlap.HorizontalOverlap) && - (Resolution.Height > display.Overlap.VerticalOverlap)) + Resolution.Width > display.Overlap.HorizontalOverlap && + Resolution.Height > display.Overlap.VerticalOverlap) .Select(display => new SurroundTopologyDisplay(display)) .ToArray(); ApplyWithBezelCorrectedResolution = topology.ApplyWithBezelCorrectedResolution; @@ -49,16 +49,28 @@ namespace HeliosDisplayManagement.Shared.NVIDIA /// public bool Equals(SurroundTopology other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return (AcceleratePrimaryDisplay == other.AcceleratePrimaryDisplay) && - (ApplyWithBezelCorrectedResolution == other.ApplyWithBezelCorrectedResolution) && - (BaseMosaicPanoramic == other.BaseMosaicPanoramic) && (ColorDepth == other.ColorDepth) && - (Columns == other.Columns) && (Displays.Length == other.Displays.Length) && + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return AcceleratePrimaryDisplay == other.AcceleratePrimaryDisplay && + ApplyWithBezelCorrectedResolution == other.ApplyWithBezelCorrectedResolution && + BaseMosaicPanoramic == other.BaseMosaicPanoramic && + ColorDepth == other.ColorDepth && + Columns == other.Columns && + Displays.Length == other.Displays.Length && Displays.All(display => other.Displays.Contains(display)) && - (DriverReloadAllowed == other.DriverReloadAllowed) && (Frequency == other.Frequency) && - (ImmersiveGaming == other.ImmersiveGaming) && Resolution.Equals(other.Resolution) && - (Rows == other.Rows); + DriverReloadAllowed == other.DriverReloadAllowed && + Frequency == other.Frequency && + ImmersiveGaming == other.ImmersiveGaming && + Resolution.Equals(other.Resolution) && + Rows == other.Rows; } // ReSharper disable once ExcessiveIndentation @@ -79,7 +91,9 @@ namespace HeliosDisplayManagement.Shared.NVIDIA .FirstOrDefault( info => info.TargetsInfo.Any( - targetInfo => targetInfo.DisplayTarget?.Equals(pathTargetInfo.DisplayTarget) == true)); + targetInfo => + targetInfo.DisplayTarget?.Equals(pathTargetInfo.DisplayTarget) == true)); + if (correspondingWindowsPathInfo != null) { // If position is same, then the two paths are equal, after all position is whats important in path sources @@ -87,14 +101,16 @@ namespace HeliosDisplayManagement.Shared.NVIDIA NvAPIWrapper.Display.PathInfo.GetDisplaysConfig() .FirstOrDefault( info => - (info.Position.X == correspondingWindowsPathInfo.Position.X) && - (info.Position.Y == correspondingWindowsPathInfo.Position.Y) && - (info.Resolution.Width == correspondingWindowsPathInfo.Resolution.Width) && - (info.Resolution.Height == correspondingWindowsPathInfo.Resolution.Height)); + info.Position.X == correspondingWindowsPathInfo.Position.X && + info.Position.Y == correspondingWindowsPathInfo.Position.Y && + info.Resolution.Width == correspondingWindowsPathInfo.Resolution.Width && + info.Resolution.Height == correspondingWindowsPathInfo.Resolution.Height); + if (correspondingNvidiaPathInfo != null) { // We now assume that there is only one target for a NVS path, in an other word, for now, it is not possible to have a cloned surround display var correspondingNvidiaTargetInfo = correspondingNvidiaPathInfo.TargetsInfo.FirstOrDefault(); + if (correspondingNvidiaTargetInfo != null) { // We also assume that the NVS monitor uses a similar display id to one of real physical monitors @@ -104,8 +120,11 @@ namespace HeliosDisplayManagement.Shared.NVIDIA topology => topology.Displays.Any(display => display.DisplayDevice.DisplayId == correspondingNvidiaTargetInfo.DisplayDevice.DisplayId)); + if (correspondingNvidiaTopology != null) + { return new SurroundTopology(correspondingNvidiaTopology); + } } } } @@ -114,14 +133,15 @@ namespace HeliosDisplayManagement.Shared.NVIDIA { // ignored } + return null; } - + public static bool operator ==(SurroundTopology left, SurroundTopology right) { return Equals(left, right) || left?.Equals(right) == true; } - + public static bool operator !=(SurroundTopology left, SurroundTopology right) { return !(left == right); @@ -130,9 +150,21 @@ namespace HeliosDisplayManagement.Shared.NVIDIA /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((SurroundTopology) obj); } @@ -142,16 +174,17 @@ namespace HeliosDisplayManagement.Shared.NVIDIA unchecked { var hashCode = AcceleratePrimaryDisplay.GetHashCode(); - hashCode = (hashCode*397) ^ ApplyWithBezelCorrectedResolution.GetHashCode(); - hashCode = (hashCode*397) ^ BaseMosaicPanoramic.GetHashCode(); - hashCode = (hashCode*397) ^ ColorDepth; - hashCode = (hashCode*397) ^ Columns; - hashCode = (hashCode*397) ^ (Displays?.GetHashCode() ?? 0); - hashCode = (hashCode*397) ^ DriverReloadAllowed.GetHashCode(); - hashCode = (hashCode*397) ^ Frequency; - hashCode = (hashCode*397) ^ ImmersiveGaming.GetHashCode(); - hashCode = (hashCode*397) ^ Resolution.GetHashCode(); - hashCode = (hashCode*397) ^ Rows; + hashCode = (hashCode * 397) ^ ApplyWithBezelCorrectedResolution.GetHashCode(); + hashCode = (hashCode * 397) ^ BaseMosaicPanoramic.GetHashCode(); + hashCode = (hashCode * 397) ^ ColorDepth; + hashCode = (hashCode * 397) ^ Columns; + hashCode = (hashCode * 397) ^ (Displays?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ DriverReloadAllowed.GetHashCode(); + hashCode = (hashCode * 397) ^ Frequency; + hashCode = (hashCode * 397) ^ ImmersiveGaming.GetHashCode(); + hashCode = (hashCode * 397) ^ Resolution.GetHashCode(); + hashCode = (hashCode * 397) ^ Rows; + return hashCode; } } @@ -175,34 +208,45 @@ namespace HeliosDisplayManagement.Shared.NVIDIA AcceleratePrimaryDisplay = AcceleratePrimaryDisplay }; IDisplaySettings bestDisplaySettings = null; + foreach (var displaySetting in gridTopology.GetPossibleDisplaySettings()) - if ((displaySetting.Width == Resolution.Width) && - (displaySetting.Height == Resolution.Height)) + { + if (displaySetting.Width == Resolution.Width && + displaySetting.Height == Resolution.Height) { if (displaySetting.BitsPerPixel == ColorDepth) { if (displaySetting.Frequency == Frequency) { bestDisplaySettings = displaySetting; + break; } - if ((bestDisplaySettings == null) || (displaySetting.Frequency > bestDisplaySettings.Frequency)) + + if (bestDisplaySettings == null || displaySetting.Frequency > bestDisplaySettings.Frequency) + { bestDisplaySettings = displaySetting; + } } - else if ((bestDisplaySettings == null) || - (displaySetting.BitsPerPixel > bestDisplaySettings.BitsPerPixel)) + else if (bestDisplaySettings == null || + displaySetting.BitsPerPixel > bestDisplaySettings.BitsPerPixel) { bestDisplaySettings = displaySetting; } } - else if ((bestDisplaySettings == null) || - (displaySetting.Width*displaySetting.Height > - bestDisplaySettings.Width*bestDisplaySettings.Height)) + else if (bestDisplaySettings == null || + displaySetting.Width * displaySetting.Height > + bestDisplaySettings.Width * bestDisplaySettings.Height) { bestDisplaySettings = displaySetting; } + } + if (bestDisplaySettings != null) + { gridTopology.SetDisplaySettings(bestDisplaySettings); + } + return gridTopology; } } diff --git a/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopologyDisplay.cs b/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopologyDisplay.cs index a99f3a5..1b92522 100644 --- a/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopologyDisplay.cs +++ b/HeliosDisplayManagement.Shared/NVIDIA/SurroundTopologyDisplay.cs @@ -7,7 +7,6 @@ using EDIDParser.Enums; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using NvAPIWrapper.Mosaic; -using NvAPIWrapper.Native.Mosaic; namespace HeliosDisplayManagement.Shared.NVIDIA { @@ -19,6 +18,7 @@ namespace HeliosDisplayManagement.Shared.NVIDIA Rotation = display.Rotation.ToRotation(); Overlap = new Point(display.Overlap.HorizontalOverlap, display.Overlap.VerticalOverlap); PixelShift = display.PixelShiftType.ToPixelShift(); + try { var bytes = display.DisplayDevice.PhysicalGPU.ReadEDIDData(display.DisplayDevice.Output); @@ -52,10 +52,20 @@ namespace HeliosDisplayManagement.Shared.NVIDIA /// public bool Equals(SurroundTopologyDisplay other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return (DisplayId == other.DisplayId) && Overlap.Equals(other.Overlap) && - (PixelShift == other.PixelShift) && (Rotation == other.Rotation); + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return DisplayId == other.DisplayId && + Overlap.Equals(other.Overlap) && + PixelShift == other.PixelShift && + Rotation == other.Rotation; } public static bool operator ==(SurroundTopologyDisplay left, SurroundTopologyDisplay right) @@ -71,9 +81,21 @@ namespace HeliosDisplayManagement.Shared.NVIDIA /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((SurroundTopologyDisplay) obj); } @@ -83,9 +105,10 @@ namespace HeliosDisplayManagement.Shared.NVIDIA unchecked { var hashCode = (int) DisplayId; - hashCode = (hashCode*397) ^ Overlap.GetHashCode(); - hashCode = (hashCode*397) ^ (int) PixelShift; - hashCode = (hashCode*397) ^ (int) Rotation; + hashCode = (hashCode * 397) ^ Overlap.GetHashCode(); + hashCode = (hashCode * 397) ^ (int) PixelShift; + hashCode = (hashCode * 397) ^ (int) Rotation; + return hashCode; } } diff --git a/HeliosDisplayManagement.Shared/PixelShift.cs b/HeliosDisplayManagement.Shared/PixelShift.cs index 2124a16..be748dc 100644 --- a/HeliosDisplayManagement.Shared/PixelShift.cs +++ b/HeliosDisplayManagement.Shared/PixelShift.cs @@ -4,6 +4,6 @@ { NoPixelShift, TopLeft2X2Pixels, - BottomRight2X2Pixels, + BottomRight2X2Pixels } -} +} \ No newline at end of file diff --git a/HeliosDisplayManagement.Shared/Profile.cs b/HeliosDisplayManagement.Shared/Profile.cs index 4ec7a6e..473eaa7 100644 --- a/HeliosDisplayManagement.Shared/Profile.cs +++ b/HeliosDisplayManagement.Shared/Profile.cs @@ -6,16 +6,12 @@ using System.Reflection; using System.Text; using System.Threading; using System.Windows.Forms; -using System.Xml; -using System.Xml.Linq; -using System.Xml.Serialization; using WindowsDisplayAPI.DisplayConfig; using HeliosDisplayManagement.Shared.Resources; using Newtonsoft.Json; using NvAPIWrapper.GPU; using NvAPIWrapper.Mosaic; using NvAPIWrapper.Native.Mosaic; -using Formatting = Newtonsoft.Json.Formatting; using Path = HeliosDisplayManagement.Shared.Topology.Path; namespace HeliosDisplayManagement.Shared @@ -44,7 +40,10 @@ namespace HeliosDisplayManagement.Shared get { if (_currentProfile == null) + { _currentProfile = GetCurrent(string.Empty); + } + return _currentProfile.Equals(this); } } @@ -58,6 +57,7 @@ namespace HeliosDisplayManagement.Shared Paths.SelectMany(path => path.Targets) .Select(target => target.SurroundTopology) .Where(topology => topology != null).ToArray(); + if (surroundTopologies.Length > 0) { try @@ -74,14 +74,20 @@ namespace HeliosDisplayManagement.Shared PhysicalGPU.GetPhysicalGPUs() .SelectMany(gpu => gpu.GetDisplayDevices()) .Select(device => device.DisplayId); + if (! surroundTopologies.All( - topology => topology.Displays.All(display => displayDevices.Contains(display.DisplayId)))) + topology => + topology.Displays.All(display => displayDevices.Contains(display.DisplayId)))) + { return false; + } // And to see if one path have two surround targets if (Paths.Any(path => path.Targets.Count(target => target.SurroundTopology != null) > 1)) + { return false; + } return true; } @@ -89,8 +95,10 @@ namespace HeliosDisplayManagement.Shared { // ignore } + return false; } + return true; //return PathInfo.ValidatePathInfos(Paths.Select(path => path.ToPathInfo())); } @@ -109,8 +117,16 @@ namespace HeliosDisplayManagement.Shared /// public bool Equals(Profile other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return Paths.All(path => other.Paths.Contains(path)); } @@ -121,6 +137,7 @@ namespace HeliosDisplayManagement.Shared if (File.Exists(ProfilesPath)) { var json = File.ReadAllText(ProfilesPath, Encoding.Unicode); + if (!string.IsNullOrWhiteSpace(json)) { return JsonConvert.DeserializeObject(json); @@ -131,6 +148,7 @@ namespace HeliosDisplayManagement.Shared { // ignored } + return new Profile[0]; } @@ -141,6 +159,7 @@ namespace HeliosDisplayManagement.Shared Name = name, Paths = PathInfo.GetActivePaths().Select(info => new Path(info)).ToArray() }; + return _currentProfile; } @@ -164,13 +183,16 @@ namespace HeliosDisplayManagement.Shared try { var json = JsonConvert.SerializeObject(array.ToArray(), Formatting.Indented); + if (!string.IsNullOrWhiteSpace(json)) { var dir = System.IO.Path.GetDirectoryName(ProfilesPath); + if (dir != null) { Directory.CreateDirectory(dir); File.WriteAllText(ProfilesPath, json, Encoding.Unicode); + return true; } } @@ -179,15 +201,28 @@ namespace HeliosDisplayManagement.Shared { // ignored } + return false; } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Profile) obj); } @@ -196,7 +231,7 @@ namespace HeliosDisplayManagement.Shared { unchecked { - return (Paths?.GetHashCode() ?? 0)*397; + return (Paths?.GetHashCode() ?? 0) * 397; } } @@ -211,6 +246,7 @@ namespace HeliosDisplayManagement.Shared try { Thread.Sleep(2000); + try { var surroundTopologies = @@ -219,38 +255,51 @@ namespace HeliosDisplayManagement.Shared .Where(topology => topology != null) .Select(topology => topology.ToGridTopology()) .ToArray(); + if (surroundTopologies.Length == 0) { var currentTopologies = GridTopology.GetGridTopologies(); - if (currentTopologies.Any(topology => topology.Rows*topology.Columns > 1)) + + if (currentTopologies.Any(topology => topology.Rows * topology.Columns > 1)) + { surroundTopologies = GridTopology.GetGridTopologies() .SelectMany(topology => topology.Displays) .Select(displays => new GridTopology(1, 1, new[] {displays})) .ToArray(); + } } + if (surroundTopologies.Length > 0) + { GridTopology.SetGridTopologies(surroundTopologies, SetDisplayTopologyFlag.MaximizePerformance); + } } catch { // ignored } + Thread.Sleep(18000); var pathInfos = Paths.Select(path => path.ToPathInfo()).Where(info => info != null).ToArray(); + if (!pathInfos.Any()) { - throw new InvalidOperationException(@"Display configuration changed since this profile is created. Please re-create this profile."); + throw new InvalidOperationException( + @"Display configuration changed since this profile is created. Please re-create this profile."); } + PathInfo.ApplyPathInfos(pathInfos, true, true, true); Thread.Sleep(10000); RefreshActiveStatus(); + return true; } catch (Exception ex) { RefreshActiveStatus(); MessageBox.Show(ex.Message, @"Profile", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return false; } } @@ -260,6 +309,7 @@ namespace HeliosDisplayManagement.Shared try { var serialized = JsonConvert.SerializeObject(this); + return JsonConvert.DeserializeObject(serialized); } catch diff --git a/HeliosDisplayManagement.Shared/ProfileIcon.cs b/HeliosDisplayManagement.Shared/ProfileIcon.cs index db1abb9..2ea16d8 100644 --- a/HeliosDisplayManagement.Shared/ProfileIcon.cs +++ b/HeliosDisplayManagement.Shared/ProfileIcon.cs @@ -33,6 +33,7 @@ namespace HeliosDisplayManagement.Shared var maxX = 0; var minY = 0; var maxY = 0; + foreach (var path in paths) { var res = NormalizeResolution(path); @@ -41,6 +42,7 @@ namespace HeliosDisplayManagement.Shared minY = Math.Min(minY, path.Position.Y); maxY = Math.Max(maxY, res.Height + path.Position.Y); } + if (withPadding) { minX -= paddingX; @@ -48,27 +50,37 @@ namespace HeliosDisplayManagement.Shared minY -= paddingY; maxY += paddingY; } + var size = new SizeF(Math.Abs(minX) + maxX, Math.Abs(minY) + maxY); var rect = new RectangleF(new PointF(minX, minY), size); + return rect; } public static Size NormalizeResolution(Size resolution, Rotation rotation) { - if ((rotation == Rotation.Rotate90) || (rotation == Rotation.Rotate270)) + if (rotation == Rotation.Rotate90 || rotation == Rotation.Rotate270) + { return new Size(resolution.Height, resolution.Width); + } + return resolution; } public static Size NormalizeResolution(Path path) { var bigest = Size.Empty; + foreach (var target in path.Targets) { var res = NormalizeResolution(path.Resolution, target.Rotation); - if ((ulong) res.Width*(ulong) res.Height > (ulong) bigest.Width*(ulong) bigest.Height) + + if ((ulong) res.Width * (ulong) res.Height > (ulong) bigest.Width * (ulong) bigest.Height) + { bigest = res; + } } + return bigest.IsEmpty ? path.Resolution : bigest; } @@ -80,7 +92,7 @@ namespace HeliosDisplayManagement.Shared /// public static GraphicsPath RoundedRect(RectangleF bounds, float radius) { - var diameter = radius*2; + var diameter = radius * 2; var size = new SizeF(diameter, diameter); var arc = new RectangleF(bounds.Location, size); var path = new GraphicsPath(); @@ -88,6 +100,7 @@ namespace HeliosDisplayManagement.Shared if (radius < 0.01) { path.AddRectangle(bounds); + return path; } @@ -107,6 +120,7 @@ namespace HeliosDisplayManagement.Shared path.AddArc(arc, 90, 90); path.CloseFigure(); + return path; } @@ -114,26 +128,29 @@ namespace HeliosDisplayManagement.Shared { var bitmap = new Bitmap(width, height, format); bitmap.MakeTransparent(); + using (var g = Graphics.FromImage(bitmap)) { g.SmoothingMode = SmoothingMode.HighQuality; DrawView(g, width, height); } + return bitmap; } public Bitmap ToBitmapOverly(Bitmap bitmap) { var viewSize = CalculateViewSize(Profile.Paths, true, PaddingX, PaddingY); - var width = bitmap.Width*0.7f; - var height = width/viewSize.Width*viewSize.Height; + var width = bitmap.Width * 0.7f; + var height = width / viewSize.Width * viewSize.Height; using (var g = Graphics.FromImage(bitmap)) { g.SmoothingMode = SmoothingMode.HighQuality; - g.TranslateTransform(bitmap.Width - width, bitmap.Height - height*1.1f); + g.TranslateTransform(bitmap.Width - width, bitmap.Height - height * 1.1f); DrawView(g, width, height); } + return bitmap; } @@ -150,13 +167,19 @@ namespace HeliosDisplayManagement.Shared }; var multiIcon = new MultiIcon(); var icon = multiIcon.Add("Icon1"); + foreach (var size in iconSizes) { icon.Add(ToBitmap(size.Width, size.Height)); - if ((size.Width >= 256) && (size.Height >= 256)) + + if (size.Width >= 256 && size.Height >= 256) + { icon[icon.Count - 1].IconImageFormat = IconImageFormat.PNG; + } } + multiIcon.SelectedIndex = 0; + return multiIcon; } @@ -166,38 +189,57 @@ namespace HeliosDisplayManagement.Shared var icon = multiIcon.Add("Icon1"); var mainIcon = new MultiIcon(); mainIcon.Load(iconAddress); + foreach (var singleIcon in mainIcon[0].Where(image => - (image.PixelFormat == PixelFormat.Format16bppRgb565) || - (image.PixelFormat == PixelFormat.Format24bppRgb) || - (image.PixelFormat == PixelFormat.Format32bppArgb)) + image.PixelFormat == PixelFormat.Format16bppRgb565 || + image.PixelFormat == PixelFormat.Format24bppRgb || + image.PixelFormat == PixelFormat.Format32bppArgb) .OrderByDescending( image => image.PixelFormat == PixelFormat.Format16bppRgb565 ? 1 - : image.PixelFormat == PixelFormat.Format24bppRgb ? 2 : 3) - .ThenByDescending(image => image.Size.Width*image.Size.Height)) + : image.PixelFormat == PixelFormat.Format24bppRgb + ? 2 + : 3) + .ThenByDescending(image => image.Size.Width * image.Size.Height)) { - if (!icon.All(i => (singleIcon.Size != i.Size) || (singleIcon.PixelFormat != i.PixelFormat))) + if (!icon.All(i => singleIcon.Size != i.Size || singleIcon.PixelFormat != i.PixelFormat)) + { continue; + } + var bitmap = singleIcon.Icon.ToBitmap(); + if (bitmap.PixelFormat != singleIcon.PixelFormat) { var clone = new Bitmap(bitmap.Width, bitmap.Height, singleIcon.PixelFormat); + using (var gr = Graphics.FromImage(clone)) { gr.DrawImage(bitmap, new Rectangle(0, 0, clone.Width, clone.Height)); } + bitmap.Dispose(); bitmap = clone; } - icon.Add(singleIcon.Size.Height*singleIcon.Size.Width < 24*24 ? bitmap : ToBitmapOverly(bitmap)); - if ((singleIcon.Size.Width >= 256) && (singleIcon.Size.Height >= 256)) + + icon.Add(singleIcon.Size.Height * singleIcon.Size.Width < 24 * 24 ? bitmap : ToBitmapOverly(bitmap)); + + if (singleIcon.Size.Width >= 256 && singleIcon.Size.Height >= 256) + { icon[icon.Count - 1].IconImageFormat = IconImageFormat.PNG; + } + bitmap.Dispose(); } + if (icon.Count == 0) + { throw new ArgumentException(); + } + multiIcon.SelectedIndex = 0; + return multiIcon; } @@ -207,75 +249,99 @@ namespace HeliosDisplayManagement.Shared var rect = new Rectangle(path.Position, res); var rows = rect.Width < rect.Height ? path.Targets.Length : 1; var cols = rect.Width >= rect.Height ? path.Targets.Length : 1; + for (var i = 0; i < path.Targets.Length; i++) + { DrawTarget(g, path, path.Targets[i], new Rectangle( rect.X + PaddingX, rect.Y + PaddingY, - rect.Width - 2*PaddingX, - rect.Height - 2*PaddingY), + rect.Width - 2 * PaddingX, + rect.Height - 2 * PaddingY), rows > 1 ? i : 0, cols > 1 ? i : 0, rows, cols); + } } // ReSharper disable once TooManyArguments - private void DrawTarget(Graphics g, Path path, PathTarget target, Rectangle rect, int row, int col, int rows, + private void DrawTarget( + Graphics g, + Path path, + PathTarget target, + Rectangle rect, + int row, + int col, + int rows, int cols) { - var targetSize = new Size(rect.Width/cols, rect.Height/rows); - var targetPosition = new Point(targetSize.Width*col + rect.X, targetSize.Height*row + rect.Y); + var targetSize = new Size(rect.Width / cols, rect.Height / rows); + var targetPosition = new Point(targetSize.Width * col + rect.X, targetSize.Height * row + rect.Y); var targetRect = new Rectangle(targetPosition, targetSize); if (target.SurroundTopology != null) + { g.FillRectangle(new SolidBrush(Color.FromArgb(255, 106, 185, 0)), targetRect); + } //else if (target.EyefinityTopology != null) // g.FillRectangle(new SolidBrush(Color.FromArgb(255, 99, 0, 0)), targetRect); else if (path.Targets.Length > 1) + { g.FillRectangle(new SolidBrush(Color.FromArgb(255, 255, 97, 27)), targetRect); + } else if (path.Position == Point.Empty) + { g.FillRectangle(new SolidBrush(Color.FromArgb(255, 0, 174, 241)), targetRect); + } else + { g.FillRectangle(new SolidBrush(Color.FromArgb(255, 155, 155, 155)), targetRect); + } + g.DrawRectangle(new Pen(Color.FromArgb(125, 50, 50, 50), 2f), targetRect); } private void DrawView(Graphics g, float width, float height) { var viewSize = CalculateViewSize(Profile.Paths, true, PaddingX, PaddingY); - var standPadding = height*0.005f; - height -= standPadding*8; - var factor = Math.Min((width - 2*standPadding - 1)/viewSize.Width, - (height - 2*standPadding - 1)/viewSize.Height); + var standPadding = height * 0.005f; + height -= standPadding * 8; + var factor = Math.Min((width - 2 * standPadding - 1) / viewSize.Width, + (height - 2 * standPadding - 1) / viewSize.Height); g.ScaleTransform(factor, factor); - var xOffset = ((width - 1)/factor - viewSize.Width)/2f; - var yOffset = ((height - 1)/factor - viewSize.Height)/2f; + var xOffset = ((width - 1) / factor - viewSize.Width) / 2f; + var yOffset = ((height - 1) / factor - viewSize.Height) / 2f; g.TranslateTransform(-viewSize.X + xOffset, -viewSize.Y + yOffset); - if (standPadding*6 >= 1) + + if (standPadding * 6 >= 1) { - using (var boundRect = RoundedRect(viewSize, 2*standPadding/factor)) + using (var boundRect = RoundedRect(viewSize, 2 * standPadding / factor)) { g.FillPath(new SolidBrush(Color.FromArgb(200, 255, 255, 255)), boundRect); - g.DrawPath(new Pen(Color.FromArgb(170, 50, 50, 50), standPadding/factor), boundRect); + g.DrawPath(new Pen(Color.FromArgb(170, 50, 50, 50), standPadding / factor), boundRect); } + using ( var boundRect = RoundedRect( - new RectangleF(viewSize.Width*0.375f + viewSize.X, viewSize.Height + standPadding/factor, - viewSize.Width/4, standPadding*7/factor), 2*standPadding/factor)) + new RectangleF(viewSize.Width * 0.375f + viewSize.X, + viewSize.Height + standPadding / factor, + viewSize.Width / 4, standPadding * 7 / factor), 2 * standPadding / factor)) { g.FillPath(new SolidBrush(Color.FromArgb(250, 50, 50, 50)), boundRect); - g.DrawPath(new Pen(Color.FromArgb(50, 255, 255, 255), 2/factor), boundRect); + g.DrawPath(new Pen(Color.FromArgb(50, 255, 255, 255), 2 / factor), boundRect); } } else { g.FillRectangle(new SolidBrush(Color.FromArgb(200, 255, 255, 255)), viewSize); - g.DrawRectangle(new Pen(Color.FromArgb(170, 50, 50, 50), standPadding/factor), viewSize.X, viewSize.Y, + g.DrawRectangle(new Pen(Color.FromArgb(170, 50, 50, 50), standPadding / factor), viewSize.X, viewSize.Y, viewSize.Width, viewSize.Height); } foreach (var path in Profile.Paths) + { DrawPath(g, path); + } } } } \ No newline at end of file diff --git a/HeliosDisplayManagement.Shared/Scaling.cs b/HeliosDisplayManagement.Shared/Scaling.cs index 910a17c..f316b61 100644 --- a/HeliosDisplayManagement.Shared/Scaling.cs +++ b/HeliosDisplayManagement.Shared/Scaling.cs @@ -8,6 +8,6 @@ Stretched, AspectRatioCenteredMax, Custom, - Preferred, + Preferred } } \ No newline at end of file diff --git a/HeliosDisplayManagement.Shared/ScanLineOrdering.cs b/HeliosDisplayManagement.Shared/ScanLineOrdering.cs index e8915a8..2ba2b98 100644 --- a/HeliosDisplayManagement.Shared/ScanLineOrdering.cs +++ b/HeliosDisplayManagement.Shared/ScanLineOrdering.cs @@ -5,6 +5,6 @@ NotSpecified, Progressive, InterlacedWithUpperFieldFirst, - InterlacedWithLowerFieldFirst, + InterlacedWithLowerFieldFirst } -} +} \ No newline at end of file diff --git a/HeliosDisplayManagement.Shared/Topology/Path.cs b/HeliosDisplayManagement.Shared/Topology/Path.cs index 9af10c4..485d738 100644 --- a/HeliosDisplayManagement.Shared/Topology/Path.cs +++ b/HeliosDisplayManagement.Shared/Topology/Path.cs @@ -37,11 +37,21 @@ namespace HeliosDisplayManagement.Shared.Topology /// public bool Equals(Path other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return (PixelFormat == other.PixelFormat) && - Position.Equals(other.Position) && Resolution.Equals(other.Resolution) && - (Targets.Length == other.Targets.Length) && Targets.All(target => other.Targets.Contains(target)); + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return PixelFormat == other.PixelFormat && + Position.Equals(other.Position) && + Resolution.Equals(other.Resolution) && + Targets.Length == other.Targets.Length && + Targets.All(target => other.Targets.Contains(target)); } public static bool operator ==(Path left, Path right) @@ -57,9 +67,21 @@ namespace HeliosDisplayManagement.Shared.Topology /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Path) obj); } @@ -69,9 +91,10 @@ namespace HeliosDisplayManagement.Shared.Topology unchecked { var hashCode = (int) PixelFormat; - hashCode = (hashCode*397) ^ Position.GetHashCode(); - hashCode = (hashCode*397) ^ Resolution.GetHashCode(); - hashCode = (hashCode*397) ^ (Targets?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ Position.GetHashCode(); + hashCode = (hashCode * 397) ^ Resolution.GetHashCode(); + hashCode = (hashCode * 397) ^ (Targets?.GetHashCode() ?? 0); + return hashCode; } } @@ -85,9 +108,13 @@ namespace HeliosDisplayManagement.Shared.Topology public PathInfo ToPathInfo() { var targets = Targets.Select(target => target.ToPathTargetInfo()).Where(info => info != null).ToArray(); + if (targets.Any()) + { return new PathInfo(new PathDisplaySource(targets.First().DisplayTarget.Adapter, SourceId), Position, Resolution, PixelFormat, targets); + } + return null; } } diff --git a/HeliosDisplayManagement.Shared/Topology/PathHelper.cs b/HeliosDisplayManagement.Shared/Topology/PathHelper.cs index 432191e..7e36a2d 100644 --- a/HeliosDisplayManagement.Shared/Topology/PathHelper.cs +++ b/HeliosDisplayManagement.Shared/Topology/PathHelper.cs @@ -1,6 +1,4 @@ -using System; -using WindowsDisplayAPI.Native.DisplayConfig; -using NvAPIWrapper.Native.Mosaic; +using WindowsDisplayAPI.Native.DisplayConfig; namespace HeliosDisplayManagement.Shared.Topology { diff --git a/HeliosDisplayManagement.Shared/Topology/PathTarget.cs b/HeliosDisplayManagement.Shared/Topology/PathTarget.cs index 8e97e3c..7bbd6de 100644 --- a/HeliosDisplayManagement.Shared/Topology/PathTarget.cs +++ b/HeliosDisplayManagement.Shared/Topology/PathTarget.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using WindowsDisplayAPI.DisplayConfig; -using WindowsDisplayAPI.Native.DisplayConfig; using HeliosDisplayManagement.Shared.NVIDIA; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -14,13 +13,17 @@ namespace HeliosDisplayManagement.Shared.Topology { DevicePath = targetInfo.DisplayTarget.DevicePath; var index = DevicePath.IndexOf("{", StringComparison.InvariantCultureIgnoreCase); + if (index > 0) + { DevicePath = DevicePath.Substring(0, index).TrimEnd('#'); + } FrequencyInMillihertz = targetInfo.FrequencyInMillihertz; Rotation = targetInfo.Rotation.ToRotation(); Scaling = targetInfo.Scaling.ToScaling(); ScanLineOrdering = targetInfo.ScanLineOrdering.ToScanLineOrdering(); + try { DisplayName = targetInfo.DisplayTarget.FriendlyName; @@ -29,6 +32,7 @@ namespace HeliosDisplayManagement.Shared.Topology { DisplayName = null; } + SurroundTopology = surround ?? SurroundTopology.FromPathTargetInfo(targetInfo); } @@ -57,12 +61,22 @@ namespace HeliosDisplayManagement.Shared.Topology /// public bool Equals(PathTarget other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return (FrequencyInMillihertz == other.FrequencyInMillihertz) && - (Rotation == other.Rotation) && (Scaling == other.Scaling) && - (ScanLineOrdering == other.ScanLineOrdering) && (DevicePath == other.DevicePath) && - (SurroundTopology == other.SurroundTopology); + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return FrequencyInMillihertz == other.FrequencyInMillihertz && + Rotation == other.Rotation && + Scaling == other.Scaling && + ScanLineOrdering == other.ScanLineOrdering && + DevicePath == other.DevicePath && + SurroundTopology == other.SurroundTopology; } public static bool operator ==(PathTarget left, PathTarget right) @@ -78,9 +92,21 @@ namespace HeliosDisplayManagement.Shared.Topology /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((PathTarget) obj); } @@ -90,11 +116,12 @@ namespace HeliosDisplayManagement.Shared.Topology unchecked { var hashCode = FrequencyInMillihertz.GetHashCode(); - hashCode = (hashCode*397) ^ (int) Rotation; - hashCode = (hashCode*397) ^ (int) Scaling; - hashCode = (hashCode*397) ^ (int) ScanLineOrdering; - hashCode = (hashCode*397) ^ DevicePath.GetHashCode(); - hashCode = (hashCode*397) ^ SurroundTopology?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (int) Rotation; + hashCode = (hashCode * 397) ^ (int) Scaling; + hashCode = (hashCode * 397) ^ (int) ScanLineOrdering; + hashCode = (hashCode * 397) ^ DevicePath.GetHashCode(); + hashCode = (hashCode * 397) ^ SurroundTopology?.GetHashCode() ?? 0; + return hashCode; } } @@ -111,11 +138,17 @@ namespace HeliosDisplayManagement.Shared.Topology var targetDevice = PathDisplayTarget.GetDisplayTargets() .FirstOrDefault( - target => target.DevicePath.StartsWith(DevicePath, StringComparison.InvariantCultureIgnoreCase)); + target => target.DevicePath.StartsWith(DevicePath, + StringComparison.InvariantCultureIgnoreCase)); + if (targetDevice == null) + { return null; + } + return new PathTargetInfo(new PathDisplayTarget(targetDevice.Adapter, targetDevice.TargetId), - FrequencyInMillihertz, ScanLineOrdering.ToDisplayConfigScanLineOrdering(), Rotation.ToDisplayConfigRotation(), Scaling.ToDisplayConfigScaling()); + FrequencyInMillihertz, ScanLineOrdering.ToDisplayConfigScanLineOrdering(), + Rotation.ToDisplayConfigRotation(), Scaling.ToDisplayConfigScaling()); } } } \ No newline at end of file diff --git a/HeliosDisplayManagement.Shared/UserControls/DisplayView.cs b/HeliosDisplayManagement.Shared/UserControls/DisplayView.cs index 2e80ceb..ec9da8c 100644 --- a/HeliosDisplayManagement.Shared/UserControls/DisplayView.cs +++ b/HeliosDisplayManagement.Shared/UserControls/DisplayView.cs @@ -20,7 +20,7 @@ namespace HeliosDisplayManagement.Shared.UserControls public Profile Profile { - get { return _profile; } + get => _profile; set { _profile = value; @@ -31,8 +31,11 @@ namespace HeliosDisplayManagement.Shared.UserControls protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); + if (_profile != null) + { DrawView(e.Graphics); + } } private void DrawPath(Graphics g, Path path) @@ -43,23 +46,30 @@ namespace HeliosDisplayManagement.Shared.UserControls g.DrawRectangle(Pens.Black, rect); DrawString(g, path.Position.IsEmpty ? "[Primary]" : $"[{path.Position.X}, {path.Position.Y}]", rect.Size, - new PointF(rect.X + PaddingY/2, rect.Y + PaddingY/2), StringAlignment.Near, StringAlignment.Near); + new PointF(rect.X + PaddingY / 2, rect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Near); var str = $"DISPLAY #{path.SourceId + 1}{Environment.NewLine}{rect.Width}×{rect.Height}"; - var strSize = DrawString(g, str, rect.Size, new PointF(rect.X - PaddingX/2, rect.Y + PaddingY/2), + var strSize = DrawString(g, str, rect.Size, new PointF(rect.X - PaddingX / 2, rect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Far); var rows = rect.Width < rect.Height ? path.Targets.Length : 1; var cols = rect.Width >= rect.Height ? path.Targets.Length : 1; + for (var i = 0; i < path.Targets.Length; i++) + { DrawTarget(g, path, path.Targets[i], - new Rectangle(rect.X + PaddingX, rect.Y + strSize.Height + PaddingY, rect.Width - 2*PaddingX, - rect.Height - strSize.Height - 2*PaddingY), + new Rectangle(rect.X + PaddingX, rect.Y + strSize.Height + PaddingY, rect.Width - 2 * PaddingX, + rect.Height - strSize.Height - 2 * PaddingY), rows > 1 ? i : 0, cols > 1 ? i : 0, rows, cols); + } } - private Size DrawString(Graphics g, string str, SizeF drawingSize = default(SizeF), - PointF? drawingPoint = null, StringAlignment vertical = StringAlignment.Center, + private Size DrawString( + Graphics g, + string str, + SizeF drawingSize = default(SizeF), + PointF? drawingPoint = null, + StringAlignment vertical = StringAlignment.Center, StringAlignment horizontal = StringAlignment.Center) { var format = new StringFormat(StringFormat.GenericTypographic) @@ -69,9 +79,13 @@ namespace HeliosDisplayManagement.Shared.UserControls FormatFlags = StringFormatFlags.NoClip }; var stringSize = g.MeasureString(str, Font, drawingSize, format); + if (drawingPoint != null) + { g.DrawString(str, Font, new SolidBrush(ForeColor), new RectangleF(drawingPoint.Value, drawingSize), format); + } + return new Size((int) stringSize.Width, (int) stringSize.Height); } @@ -79,14 +93,15 @@ namespace HeliosDisplayManagement.Shared.UserControls { g.DrawRectangle(Pens.Black, rect); - var targetSize = new Size(rect.Width/target.SurroundTopology.Columns, - rect.Height/target.SurroundTopology.Rows); + var targetSize = new Size(rect.Width / target.SurroundTopology.Columns, + rect.Height / target.SurroundTopology.Rows); + for (var i = 0; i < target.SurroundTopology.Displays.Length; i++) { var display = target.SurroundTopology.Displays[i]; - var row = i/target.SurroundTopology.Columns; - var col = i%target.SurroundTopology.Columns; - var targetPosition = new Point(targetSize.Width*col + rect.X, targetSize.Height*row + rect.Y); + var row = i / target.SurroundTopology.Columns; + var col = i % target.SurroundTopology.Columns; + var targetPosition = new Point(targetSize.Width * col + rect.X, targetSize.Height * row + rect.Y); var targetRect = new Rectangle(targetPosition, targetSize); g.DrawRectangle(Pens.Black, targetRect); @@ -95,25 +110,30 @@ namespace HeliosDisplayManagement.Shared.UserControls { case Rotation.Rotate90: DrawString(g, "90°", targetRect.Size, - new PointF(targetRect.X - PaddingX/2, targetRect.Y + PaddingY/2), StringAlignment.Near, + new PointF(targetRect.X - PaddingX / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Far); + break; case Rotation.Rotate180: DrawString(g, "180°", targetRect.Size, - new PointF(targetRect.X - PaddingX/2, targetRect.Y + PaddingY/2), StringAlignment.Near, + new PointF(targetRect.X - PaddingX / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Far); + break; case Rotation.Rotate270: DrawString(g, "270°", targetRect.Size, - new PointF(targetRect.X - PaddingX/2, targetRect.Y + PaddingY/2), StringAlignment.Near, + new PointF(targetRect.X - PaddingX / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Far); + break; } if (!display.Overlap.IsEmpty) + { DrawString(g, $"[{-display.Overlap.X}, {-display.Overlap.Y}]", targetRect.Size, - new PointF(targetRect.X + PaddingY/2, targetRect.Y + PaddingY/2), StringAlignment.Near, + new PointF(targetRect.X + PaddingY / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Near); + } // Invert to real monitor resolution var res = ProfileIcon.NormalizeResolution(target.SurroundTopology.Resolution, display.Rotation); @@ -122,23 +142,38 @@ namespace HeliosDisplayManagement.Shared.UserControls } } - private void DrawTarget(Graphics g, Path path, PathTarget target, Rectangle rect, int row, int col, int rows, + private void DrawTarget( + Graphics g, + Path path, + PathTarget target, + Rectangle rect, + int row, + int col, + int rows, int cols) { - var targetSize = new Size(rect.Width/cols, rect.Height/rows); - var targetPosition = new Point(targetSize.Width*col + rect.X, targetSize.Height*row + rect.Y); + var targetSize = new Size(rect.Width / cols, rect.Height / rows); + var targetPosition = new Point(targetSize.Width * col + rect.X, targetSize.Height * row + rect.Y); var targetRect = new Rectangle(targetPosition, targetSize); if (target.SurroundTopology != null) + { g.FillRectangle(new SolidBrush(Color.FromArgb(150, 106, 185, 0)), targetRect); + } //else if (target.EyefinityTopology != null) // g.FillRectangle(new SolidBrush(Color.FromArgb(150, 99, 0, 0)), targetRect); else if (path.Targets.Length > 1) + { g.FillRectangle(new SolidBrush(Color.FromArgb(150, 255, 97, 27)), targetRect); + } else if (path.Position == Point.Empty) + { g.FillRectangle(new SolidBrush(Color.FromArgb(150, 0, 174, 241)), targetRect); + } else + { g.FillRectangle(new SolidBrush(Color.FromArgb(255, 155, 155, 155)), targetRect); + } g.DrawRectangle(Pens.Black, targetRect); var str = $"{target.DisplayName}{Environment.NewLine}{path.Resolution.Width}×{path.Resolution.Height}"; @@ -147,32 +182,35 @@ namespace HeliosDisplayManagement.Shared.UserControls { case Rotation.Rotate90: DrawString(g, "90°", targetRect.Size, - new PointF(targetRect.X - PaddingX/2, targetRect.Y + PaddingY/2), StringAlignment.Near, + new PointF(targetRect.X - PaddingX / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Far); + break; case Rotation.Rotate180: DrawString(g, "180°", targetRect.Size, - new PointF(targetRect.X - PaddingX/2, targetRect.Y + PaddingY/2), StringAlignment.Near, + new PointF(targetRect.X - PaddingX / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Far); + break; case Rotation.Rotate270: DrawString(g, "270°", targetRect.Size, - new PointF(targetRect.X - PaddingX/2, targetRect.Y + PaddingY/2), StringAlignment.Near, + new PointF(targetRect.X - PaddingX / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Far); + break; } if (target.SurroundTopology != null) { var strSize = DrawString(g, str, targetRect.Size, - new PointF(targetRect.X + PaddingX/2, targetRect.Y + PaddingY/2), + new PointF(targetRect.X + PaddingX / 2, targetRect.Y + PaddingY / 2), StringAlignment.Near, StringAlignment.Near); DrawSurroundTopology(g, target, new Rectangle( targetRect.X + PaddingX, targetRect.Y + strSize.Height + PaddingY, - targetRect.Width - 2*PaddingX, - targetRect.Height - strSize.Height - 2*PaddingY)); + targetRect.Width - 2 * PaddingX, + targetRect.Height - strSize.Height - 2 * PaddingY)); } else { @@ -183,15 +221,17 @@ namespace HeliosDisplayManagement.Shared.UserControls private void DrawView(Graphics g) { var viewSize = ProfileIcon.CalculateViewSize(_profile.Paths, true, 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); - var xOffset = (Width/factor - viewSize.Width)/2f; - var yOffset = (Height/factor - viewSize.Height)/2f; + var xOffset = (Width / factor - viewSize.Width) / 2f; + var yOffset = (Height / factor - viewSize.Height) / 2f; g.TranslateTransform(-viewSize.X + xOffset, -viewSize.Y + yOffset); foreach (var path in _profile.Paths) + { DrawPath(g, path); + } } } } \ No newline at end of file diff --git a/HeliosDisplayManagement.Shared/packages.config b/HeliosDisplayManagement.Shared/packages.config index 52f0fb3..210f66c 100644 --- a/HeliosDisplayManagement.Shared/packages.config +++ b/HeliosDisplayManagement.Shared/packages.config @@ -1,4 +1,5 @@  + diff --git a/HeliosDisplayManagement.ShellExtension/HeliosDesktopMenuExtension.cs b/HeliosDisplayManagement.ShellExtension/HeliosDesktopMenuExtension.cs index aa6b149..50b4c50 100644 --- a/HeliosDisplayManagement.ShellExtension/HeliosDesktopMenuExtension.cs +++ b/HeliosDisplayManagement.ShellExtension/HeliosDesktopMenuExtension.cs @@ -26,6 +26,7 @@ namespace HeliosDisplayManagement.ShellExtension profileMenu.DropDownItems.Add(new ToolStripMenuItem(Language.Create_Shortcut, Properties.Resources.Shortcut_x16, (sender, args) => HeliosDisplayManagement.Open(HeliosStartupAction.CreateShortcut, profile))); + return profileMenu; } @@ -37,17 +38,25 @@ namespace HeliosDisplayManagement.ShellExtension protected override ContextMenuStrip CreateMenu() { var explorerMenu = new ContextMenuStrip(); + if (Profile.GetAllProfiles().Any()) { Profile.RefreshActiveStatus(); var extensionMenu = new ToolStripMenuItem(Language.Display_Profiles, Properties.Resources.Icon_x16); + foreach (var profile in Profile.GetAllProfiles()) + { extensionMenu.DropDownItems.Add(CreateProfileMenu(profile)); + } + extensionMenu.DropDownItems.Add(new ToolStripSeparator()); extensionMenu.DropDownItems.Add(new ToolStripMenuItem(Language.Manage_Profiles, Properties.Resources.Icon_x16, - (sender, args) => { HeliosDisplayManagement.Open(); })); + (sender, args) => + { + HeliosDisplayManagement.Open(); + })); explorerMenu.Items.Add(extensionMenu); explorerMenu.Items.Add(new ToolStripSeparator()); } @@ -55,10 +64,14 @@ namespace HeliosDisplayManagement.ShellExtension { var extensionMenu = new ToolStripMenuItem(Language.Manage_Profiles, Properties.Resources.Icon_x16, - (sender, args) => { HeliosDisplayManagement.Open(); }); + (sender, args) => + { + HeliosDisplayManagement.Open(); + }); explorerMenu.Items.Add(extensionMenu); explorerMenu.Items.Add(new ToolStripSeparator()); } + return explorerMenu; } } diff --git a/HeliosDisplayManagement.ShellExtension/HeliosExecutableMenuExtension.cs b/HeliosDisplayManagement.ShellExtension/HeliosExecutableMenuExtension.cs index aa0d5fc..3d288ae 100644 --- a/HeliosDisplayManagement.ShellExtension/HeliosExecutableMenuExtension.cs +++ b/HeliosDisplayManagement.ShellExtension/HeliosExecutableMenuExtension.cs @@ -16,9 +16,9 @@ namespace HeliosDisplayManagement.ShellExtension protected override bool CanShowMenu() { return Helios.IsInstalled && - (SelectedItemPaths.Count() == 1) && + SelectedItemPaths.Count() == 1 && Profile.GetAllProfiles().Any() && - (Path.GetExtension(SelectedItemPaths.First())?.ToLower() == @".exe"); + Path.GetExtension(SelectedItemPaths.First())?.ToLower() == @".exe"; } protected override ContextMenuStrip CreateMenu() @@ -26,18 +26,28 @@ namespace HeliosDisplayManagement.ShellExtension var explorerMenu = new ContextMenuStrip(); var extensionMenu = new ToolStripMenuItem(Language.Open_under_Display_Profile, Properties.Resources.Icon_x16); + if (Profile.GetAllProfiles().Any()) { Profile.RefreshActiveStatus(); + foreach (var profile in Profile.GetAllProfiles()) + { extensionMenu.DropDownItems.Add(CreateProfileMenu(profile)); + } + extensionMenu.DropDownItems.Add(new ToolStripSeparator()); } + extensionMenu.DropDownItems.Add(new ToolStripMenuItem(Language.Manage_Profiles, Properties.Resources.Icon_x16, - (sender, args) => { HeliosDisplayManagement.Open(); })); + (sender, args) => + { + HeliosDisplayManagement.Open(); + })); explorerMenu.Items.Add(extensionMenu); explorerMenu.Items.Add(new ToolStripSeparator()); + return explorerMenu; } @@ -58,6 +68,7 @@ namespace HeliosDisplayManagement.ShellExtension (sender, args) => HeliosDisplayManagement.Open(HeliosStartupAction.CreateShortcut, profile, SelectedItemPaths.FirstOrDefault()))); + return profileMenu; } } diff --git a/HeliosDisplayManagement.ShellExtension/HeliosSteamUrlMenuExtension.cs b/HeliosDisplayManagement.ShellExtension/HeliosSteamUrlMenuExtension.cs index b86abb9..0cc5ed4 100644 --- a/HeliosDisplayManagement.ShellExtension/HeliosSteamUrlMenuExtension.cs +++ b/HeliosDisplayManagement.ShellExtension/HeliosSteamUrlMenuExtension.cs @@ -17,9 +17,9 @@ namespace HeliosDisplayManagement.ShellExtension protected override bool CanShowMenu() { return Helios.IsInstalled && - (SelectedItemPaths.Count() == 1) && + SelectedItemPaths.Count() == 1 && Profile.GetAllProfiles().Any() && - (ParseSteamAppId() > 0); + ParseSteamAppId() > 0; } protected override ContextMenuStrip CreateMenu() @@ -27,18 +27,28 @@ namespace HeliosDisplayManagement.ShellExtension var explorerMenu = new ContextMenuStrip(); var extensionMenu = new ToolStripMenuItem(Language.Open_under_Display_Profile, Properties.Resources.Icon_x16); + if (Profile.GetAllProfiles().Any()) { Profile.RefreshActiveStatus(); + foreach (var profile in Profile.GetAllProfiles()) + { extensionMenu.DropDownItems.Add(CreateProfileMenu(profile)); + } + extensionMenu.DropDownItems.Add(new ToolStripSeparator()); } + extensionMenu.DropDownItems.Add(new ToolStripMenuItem(Language.Manage_Profiles, Properties.Resources.Icon_x16, - (sender, args) => { HeliosDisplayManagement.Open(); })); + (sender, args) => + { + HeliosDisplayManagement.Open(); + })); explorerMenu.Items.Add(extensionMenu); explorerMenu.Items.Add(new ToolStripSeparator()); + return explorerMenu; } @@ -56,6 +66,7 @@ namespace HeliosDisplayManagement.ShellExtension (sender, args) => HeliosDisplayManagement.OpenSteamGame(HeliosStartupAction.CreateShortcut, profile, appId))); + return profileMenu; } @@ -64,31 +75,49 @@ namespace HeliosDisplayManagement.ShellExtension try { var fileAddress = SelectedItemPaths.FirstOrDefault(); - if (!string.IsNullOrWhiteSpace(fileAddress) && File.Exists(fileAddress) && - (new FileInfo(fileAddress).Length < 1024)) + + if (!string.IsNullOrWhiteSpace(fileAddress) && + File.Exists(fileAddress) && + new FileInfo(fileAddress).Length < 1024) { var fileContent = File.ReadAllText(fileAddress); + if (!fileContent.Contains(@"[InternetShortcut]")) + { return 0; + } + var steamUrlPattern = @"steam://rungameid/"; var urlIndex = fileContent.IndexOf(steamUrlPattern, StringComparison.InvariantCultureIgnoreCase); + if (urlIndex < 0) + { return 0; + } + var nextLine = fileContent.IndexOf(@"\r", urlIndex + steamUrlPattern.Length, StringComparison.InvariantCultureIgnoreCase); + if (nextLine < 0) + { nextLine = fileContent.Length - 1; + } + var appIdString = fileContent.Substring(urlIndex + steamUrlPattern.Length, nextLine - urlIndex - steamUrlPattern.Length); uint appId; + if (uint.TryParse(appIdString, out appId)) + { return appId; + } } } catch { // ignored } + return 0; } } diff --git a/HeliosDisplayManagement.ShellExtension/Shield.cs b/HeliosDisplayManagement.ShellExtension/Shield.cs index b635350..1094dff 100644 --- a/HeliosDisplayManagement.ShellExtension/Shield.cs +++ b/HeliosDisplayManagement.ShellExtension/Shield.cs @@ -12,13 +12,18 @@ namespace HeliosDisplayManagement.ShellExtension get { if (_smallIcon != null) + { return _smallIcon; + } + var iconSize = SystemInformation.SmallIconSize; _smallIcon = new Bitmap(iconSize.Width, iconSize.Height); + using (var g = Graphics.FromImage(_smallIcon)) { g.DrawIcon(SystemIcons.Shield, new Rectangle(Point.Empty, iconSize)); } + return _smallIcon; } } diff --git a/HeliosDisplayManagement.ShellExtension/packages.config b/HeliosDisplayManagement.ShellExtension/packages.config index 7c0bc6b..b3fece9 100644 --- a/HeliosDisplayManagement.ShellExtension/packages.config +++ b/HeliosDisplayManagement.ShellExtension/packages.config @@ -1,4 +1,5 @@  + diff --git a/HeliosDisplayManagement/CommandLineOptions.cs b/HeliosDisplayManagement/CommandLineOptions.cs index f4e4c9a..7e1c0c2 100644 --- a/HeliosDisplayManagement/CommandLineOptions.cs +++ b/HeliosDisplayManagement/CommandLineOptions.cs @@ -17,7 +17,7 @@ namespace HeliosDisplayManagement } [Option('a', @"action", HelpText = @"The action to perform", - DefaultValue = HeliosStartupAction.None)] + DefaultValue = HeliosStartupAction.None)] public HeliosStartupAction Action { get; set; } public static CommandLineOptions Default @@ -29,37 +29,42 @@ namespace HeliosDisplayManagement _defaultObject = new CommandLineOptions(); Parser.Default.ParseArguments(Environment.GetCommandLineArgs().Skip(1).ToArray(), _defaultObject); Console.WriteLine(string.Join(" ", Environment.GetCommandLineArgs().Skip(1))); - if ((_defaultObject.LastParserState != null) && (_defaultObject.LastParserState.Errors.Count > 0)) + + if (_defaultObject.LastParserState != null && _defaultObject.LastParserState.Errors.Count > 0) + { throw new Exception(_defaultObject.LastParserState.Errors[0].ToString()); + } } + return _defaultObject; } } - [Option(@"arguments", HelpText = @"Program's argument to execute, for temporarily switch or to create shortcut.", - DefaultValue = null)] + [Option(@"arguments", + HelpText = @"Program's argument to execute, for temporarily switch or to create shortcut.", + DefaultValue = null)] public string ExecuteArguments { get; set; } [Option('e', @"execute", - HelpText = @"Program's address to execute, for temporarily switch or to create shortcut.", - DefaultValue = null)] + HelpText = @"Program's address to execute, for temporarily switch or to create shortcut.", + DefaultValue = null)] public string ExecuteFilename { get; set; } [Option('w', @"waitfor", - HelpText = - @"Program's process name to wait for end of execution before rolling back the settings, for temporarily switch or to create shortcut; Useful when there is a launcher involved.", - DefaultValue = null)] + HelpText = + @"Program's process name to wait for end of execution before rolling back the settings, for temporarily switch or to create shortcut; Useful when there is a launcher involved.", + DefaultValue = null)] public string ExecuteProcessName { get; set; } [Option('t', @"timeout", - HelpText = - @"The maximum time in seconds to wait for the process since the execution of the program, for temporarily switch or to create shortcut.", - DefaultValue = 30u)] + HelpText = + @"The maximum time in seconds to wait for the process since the execution of the program, for temporarily switch or to create shortcut.", + DefaultValue = 30u)] public uint ExecuteProcessTimeout { get; set; } [Option('s', @"steam", - HelpText = @"AppId of the Steam game, for temporarily switch or to create shortcut.", - DefaultValue = 0u)] + HelpText = @"AppId of the Steam game, for temporarily switch or to create shortcut.", + DefaultValue = 0u)] public uint ExecuteSteamApp { get; set; } [ParserState] @@ -76,6 +81,7 @@ namespace HeliosDisplayManagement MessageBox.Show(help, Language.Help, MessageBoxButtons.OK, MessageBoxIcon.Information); Environment.Exit(0); + return help; } } diff --git a/HeliosDisplayManagement/DisplayRepresentation.cs b/HeliosDisplayManagement/DisplayRepresentation.cs index 52a00e7..8b307a5 100644 --- a/HeliosDisplayManagement/DisplayRepresentation.cs +++ b/HeliosDisplayManagement/DisplayRepresentation.cs @@ -16,11 +16,18 @@ namespace HeliosDisplayManagement Name = display.DeviceName; Path = display.DevicePath; var index = Path.IndexOf("{", StringComparison.InvariantCultureIgnoreCase); + if (index > 0) + { Path = Path.Substring(0, index).TrimEnd('#'); + } + IsAvailable = display.IsAvailable; + if (IsAvailable) + { PossibleSettings = GetDisplay()?.GetPossibleSettings()?.ToArray() ?? new DisplayPossibleSetting[0]; + } } public DisplayRepresentation(PathTarget display) @@ -28,15 +35,18 @@ namespace HeliosDisplayManagement Name = display.DisplayName; Path = display.DevicePath; IsAvailable = GetDisplay()?.IsAvailable ?? false; + if (IsAvailable) + { PossibleSettings = GetDisplay()?.GetPossibleSettings()?.ToArray() ?? new DisplayPossibleSetting[0]; + } } public bool IsAvailable { get; } - public string Name { get; private set; } + public string Name { get; } public string Path { get; } - public DisplayPossibleSetting[] PossibleSettings { get; private set; } + public DisplayPossibleSetting[] PossibleSettings { get; } public static IEnumerable GetDisplays(Profile profile = null) { @@ -47,10 +57,18 @@ namespace HeliosDisplayManagement // .GroupBy(representation => representation.Path) // .Select(grouping => grouping.First()).ToList(); var displays = new List(); + if (profile != null) + { foreach (var target in profile.Paths.SelectMany(path => path.Targets)) + { if (displays.All(display => display.Path != target.DevicePath)) + { displays.Add(new DisplayRepresentation(target)); + } + } + } + return displays; } @@ -82,16 +100,21 @@ namespace HeliosDisplayManagement { var targetInfo = GetTargetInfo(); var resolution = Size.Empty; - if ((targetInfo != null) && targetInfo.IsAvailable) + + if (targetInfo != null && targetInfo.IsAvailable) { resolution = targetInfo.PreferredResolution; } else if (profile != null) { var targetPath = GetPathSource(profile); + if (targetPath != null) + { resolution = targetPath.Resolution; + } } + var p = new Profile {Paths = new Path[1]}; p.Paths[0] = new Path { @@ -100,12 +123,17 @@ namespace HeliosDisplayManagement Targets = new PathTarget[1] }; p.Paths[0].Targets[0] = new PathTarget {DevicePath = Path}; + if (profile != null) { var targetPath = GetPathTarget(profile); + if (targetPath != null) + { p.Paths[0].Targets[0].SurroundTopology = targetPath.SurroundTopology; + } } + return new ProfileIcon(p).ToBitmap(size.Width, size.Height); } } diff --git a/HeliosDisplayManagement/InterProcess/IPCClient.cs b/HeliosDisplayManagement/InterProcess/IPCClient.cs index 244a292..7bf03b3 100644 --- a/HeliosDisplayManagement/InterProcess/IPCClient.cs +++ b/HeliosDisplayManagement/InterProcess/IPCClient.cs @@ -17,9 +17,15 @@ namespace HeliosDisplayManagement.InterProcess { } - public int HoldProcessId => Channel.HoldProcessId; + public int HoldProcessId + { + get => Channel.HoldProcessId; + } - public InstanceStatus Status => Channel.Status; + public InstanceStatus Status + { + get => Channel.Status; + } public void StopHold() { @@ -29,11 +35,16 @@ namespace HeliosDisplayManagement.InterProcess public static IEnumerable QueryAll() { var thisProcess = Process.GetCurrentProcess(); + foreach (var process in Process.GetProcessesByName(thisProcess.ProcessName)) { if (process.Id == thisProcess.Id) + { continue; + } + IPCClient processChannel = null; + try { processChannel = new IPCClient(process); @@ -42,26 +53,38 @@ namespace HeliosDisplayManagement.InterProcess { // ignored } + if (processChannel != null) + { yield return processChannel; + } } } public static IPCClient QueryByStatus(InstanceStatus status) { var thisProcess = Process.GetCurrentProcess(); + foreach (var process in Process.GetProcessesByName(thisProcess.ProcessName)) + { if (process.Id != thisProcess.Id) + { try { var processChannel = new IPCClient(process); + if (processChannel.Status == status) + { return processChannel; + } } catch { // ignored } + } + } + return null; } } diff --git a/HeliosDisplayManagement/InterProcess/IPCService.cs b/HeliosDisplayManagement/InterProcess/IPCService.cs index 04e5149..74cb3dc 100644 --- a/HeliosDisplayManagement/InterProcess/IPCService.cs +++ b/HeliosDisplayManagement/InterProcess/IPCService.cs @@ -26,14 +26,18 @@ namespace HeliosDisplayManagement.InterProcess public static IPCService GetInstance() { - if ((_serviceHost != null) || StartService()) + if (_serviceHost != null || StartService()) + { return _serviceHost?.SingletonInstance as IPCService; + } + return null; } public static bool StartService() { if (_serviceHost == null) + { try { var process = Process.GetCurrentProcess(); @@ -44,6 +48,7 @@ namespace HeliosDisplayManagement.InterProcess _serviceHost.AddServiceEndpoint(typeof(IService), new NetNamedPipeBinding(), "Service"); _serviceHost.Open(); + return true; } catch (Exception) @@ -56,8 +61,11 @@ namespace HeliosDisplayManagement.InterProcess { // ignored } + _serviceHost = null; } + } + return false; } } diff --git a/HeliosDisplayManagement/Program.cs b/HeliosDisplayManagement/Program.cs index f690d8b..f751d40 100644 --- a/HeliosDisplayManagement/Program.cs +++ b/HeliosDisplayManagement/Program.cs @@ -20,25 +20,37 @@ namespace HeliosDisplayManagement internal static bool GoProfile(Profile profile) { if (profile.IsActive) + { return true; + } + var instanceStatus = IPCService.GetInstance().Status; + try { IPCService.GetInstance().Status = InstanceStatus.Busy; var failed = false; + if (new SplashForm(() => { Task.Factory.StartNew(() => { if (!profile.Apply()) + { failed = true; + } }, TaskCreationOptions.LongRunning); - }, 3, 30).ShowDialog() != DialogResult.Cancel) + }, 3, 30).ShowDialog() != + DialogResult.Cancel) { if (failed) + { throw new Exception(Language.Profile_is_invalid_or_not_possible_to_apply); + } + return true; } + return false; } finally @@ -50,7 +62,10 @@ namespace HeliosDisplayManagement private static void CreateShortcut(IReadOnlyList profiles, int profileIndex) { if (profileIndex < 0) + { throw new Exception(Language.Selected_profile_is_invalid_or_not_found); + } + IPCService.GetInstance().Status = InstanceStatus.User; new ShortcutForm(profiles[profileIndex]) { @@ -65,13 +80,22 @@ namespace HeliosDisplayManagement private static void EditProfile(IList profiles, int profileIndex) { if (profileIndex < 0) + { throw new Exception(Language.Selected_profile_is_invalid_or_not_found); + } + IPCService.GetInstance().Status = InstanceStatus.User; var editForm = new EditForm(profiles[profileIndex]); + if (editForm.ShowDialog() == DialogResult.OK) + { profiles[profileIndex] = editForm.Profile; + } + if (!Profile.SetAllProfiles(profiles)) + { throw new Exception(Language.Failed_to_save_profile); + } } /// @@ -83,14 +107,17 @@ namespace HeliosDisplayManagement Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; + try { if (!IPCService.StartService()) + { throw new Exception(Language.Can_not_open_a_named_pipe_for_Inter_process_communication); + } var profiles = Profile.GetAllProfiles().ToArray(); var profileIndex = !string.IsNullOrWhiteSpace(CommandLineOptions.Default.ProfileName) && - (profiles.Length > 0) + profiles.Length > 0 ? Array.FindIndex(profiles, p => p.Name.Equals(CommandLineOptions.Default.ProfileName, @@ -101,16 +128,20 @@ namespace HeliosDisplayManagement { case HeliosStartupAction.SwitchProfile: SwitchProfile(profiles, profileIndex); + break; case HeliosStartupAction.EditProfile: EditProfile(profiles, profileIndex); + break; case HeliosStartupAction.CreateShortcut: CreateShortcut(profiles, profileIndex); + break; default: IPCService.GetInstance().Status = InstanceStatus.User; Application.Run(new MainForm()); + break; } } @@ -128,45 +159,72 @@ namespace HeliosDisplayManagement private static void SwitchProfile(IReadOnlyList profiles, int profileIndex) { var rollbackProfile = Profile.GetCurrent(string.Empty); + if (profileIndex < 0) + { throw new Exception(Language.Selected_profile_is_invalid_or_not_found); + } + if (!profiles[profileIndex].IsPossible) + { throw new Exception(Language.Selected_profile_is_not_possible); + } + if ( IPCClient.QueryAll() .Any( client => - (client.Status == InstanceStatus.Busy) || - (client.Status == InstanceStatus.OnHold))) + client.Status == InstanceStatus.Busy || + client.Status == InstanceStatus.OnHold)) + { throw new Exception( Language .Another_instance_of_this_program_is_in_working_state_Please_close_other_instances_before_trying_to_switch_profile); + } + if (!string.IsNullOrWhiteSpace(CommandLineOptions.Default.ExecuteFilename)) { if (!File.Exists(CommandLineOptions.Default.ExecuteFilename)) + { throw new Exception(Language.Executable_file_not_found); + } + if (!GoProfile(profiles[profileIndex])) + { throw new Exception(Language.Can_not_change_active_profile); + } + var process = Process.Start(CommandLineOptions.Default.ExecuteFilename, CommandLineOptions.Default.ExecuteArguments); var processes = new Process[0]; + if (!string.IsNullOrWhiteSpace(CommandLineOptions.Default.ExecuteProcessName)) { var ticks = 0; - while (ticks < CommandLineOptions.Default.ExecuteProcessTimeout*1000) + + while (ticks < CommandLineOptions.Default.ExecuteProcessTimeout * 1000) { processes = Process.GetProcessesByName(CommandLineOptions.Default.ExecuteProcessName); + if (processes.Length > 0) + { break; + } + Thread.Sleep(300); ticks += 300; } } + if (processes.Length == 0) + { processes = new[] {process}; + } + IPCService.GetInstance().HoldProcessId = processes.FirstOrDefault()?.Id ?? 0; IPCService.GetInstance().Status = InstanceStatus.OnHold; NotifyIcon notify = null; + try { notify = new NotifyIcon @@ -183,7 +241,9 @@ namespace HeliosDisplayManagement { // ignored } + foreach (var p in processes) + { try { p.WaitForExit(); @@ -192,47 +252,84 @@ namespace HeliosDisplayManagement { // ignored } + } + if (notify != null) { notify.Visible = false; notify.Dispose(); Application.DoEvents(); } + IPCService.GetInstance().Status = InstanceStatus.Busy; + if (!rollbackProfile.IsActive) + { if (!GoProfile(rollbackProfile)) + { throw new Exception(Language.Can_not_change_active_profile); + } + } } else if (CommandLineOptions.Default.ExecuteSteamApp > 0) { var steamGame = new SteamGame(CommandLineOptions.Default.ExecuteSteamApp); + if (!SteamGame.SteamInstalled) + { throw new Exception(Language.Steam_is_not_installed); + } + if (!File.Exists(SteamGame.SteamAddress)) + { throw new Exception(Language.Steam_executable_file_not_found); + } + if (!steamGame.IsInstalled) + { throw new Exception(Language.Steam_game_is_not_installed); + } + if (!steamGame.IsOwned) + { throw new Exception(Language.Steam_game_is_not_owned); + } + if (!GoProfile(profiles[profileIndex])) + { throw new Exception(Language.Can_not_change_active_profile); + } + var address = $"steam://rungameid/{steamGame.AppId}"; + if (!string.IsNullOrWhiteSpace(CommandLineOptions.Default.ExecuteArguments)) + { address += "/" + CommandLineOptions.Default.ExecuteArguments; + } + var steamProcess = Process.Start(address); // Wait for steam game to update and then run var ticks = 0; - while (ticks < CommandLineOptions.Default.ExecuteProcessTimeout*1000) + + while (ticks < CommandLineOptions.Default.ExecuteProcessTimeout * 1000) { if (steamGame.IsRunning) + { break; + } + Thread.Sleep(300); + if (!steamGame.IsUpdating) + { ticks += 300; + } } + IPCService.GetInstance().HoldProcessId = steamProcess?.Id ?? 0; IPCService.GetInstance().Status = InstanceStatus.OnHold; NotifyIcon notify = null; + try { notify = new NotifyIcon @@ -249,29 +346,44 @@ namespace HeliosDisplayManagement { // ignored } + // Wait for the game to exit if (steamGame.IsRunning) + { while (true) { if (!steamGame.IsRunning) + { break; + } + Thread.Sleep(300); } + } + if (notify != null) { notify.Visible = false; notify.Dispose(); Application.DoEvents(); } + IPCService.GetInstance().Status = InstanceStatus.Busy; + if (!rollbackProfile.IsActive) + { if (!GoProfile(rollbackProfile)) + { throw new Exception(Language.Can_not_change_active_profile); + } + } } else { if (!GoProfile(profiles[profileIndex])) + { throw new Exception(Language.Can_not_change_active_profile); + } } } } diff --git a/HeliosDisplayManagement/Steam/SteamGame.cs b/HeliosDisplayManagement/Steam/SteamGame.cs index bcf8d3e..c86804d 100644 --- a/HeliosDisplayManagement/Steam/SteamGame.cs +++ b/HeliosDisplayManagement/Steam/SteamGame.cs @@ -7,9 +7,6 @@ using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; -using System.Xml.Linq; -using System.Xml.Serialization; using HeliosDisplayManagement.Resources; using HtmlAgilityPack; using Microsoft.Win32; @@ -38,14 +35,16 @@ namespace HeliosDisplayManagement.Steam public uint AppId { get; } public static string GameIdsPath - => - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + { + get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Assembly.GetExecutingAssembly().GetName().Name, @"SteamGames.json"); + } public static string IconCache - => - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + { + get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Assembly.GetExecutingAssembly().GetName().Name, @"SteamIconCache"); + } public bool IsInstalled { @@ -123,11 +122,25 @@ namespace HeliosDisplayManagement.Steam } } - public string Name => _name ?? (_name = GetAppName(AppId)); + public string Name + { + get => _name ?? (_name = GetAppName(AppId)); + } - private string RegistryApp => $@"{RegistryApps}\\{AppId}"; - private static string RegistryApps => $@"{RegistrySteam}\\Apps"; - private static string RegistrySteam => @"SOFTWARE\\Valve\\Steam"; + private string RegistryApp + { + get => $@"{RegistryApps}\\{AppId}"; + } + + private static string RegistryApps + { + get => $@"{RegistrySteam}\\Apps"; + } + + private static string RegistrySteam + { + get => @"SOFTWARE\\Valve\\Steam"; + } public static string SteamAddress { @@ -141,45 +154,65 @@ namespace HeliosDisplayManagement.Steam } } - public static bool SteamInstalled => !string.IsNullOrWhiteSpace(SteamAddress); + public static bool SteamInstalled + { + get => !string.IsNullOrWhiteSpace(SteamAddress); + } public static List GetAllGames() { lock (AllGamesLock) { if (_allGames == null) + { _allGames = GetCachedGameIds()?.ToList(); + } } + // Update only once if (!_allGamesUpdated) + { if (_allGames?.Count > 0) + { UpdateGamesFromWeb(); + } else + { UpdateGamesFromWeb()?.Join(); + } + } + return _allGames; } public static SteamGame[] GetAllOwnedGames() { var list = new List(); + try { using ( var subKey = Registry.CurrentUser.OpenSubKey(RegistryApps, RegistryKeyPermissionCheck.ReadSubTree)) { if (subKey != null) + { foreach (var keyName in subKey.GetSubKeyNames()) { uint gameId; + if (uint.TryParse(keyName, out gameId)) + { list.Add(new SteamGame(gameId)); + } } + } } } catch (Exception) { // ignored } + return list.ToArray(); } @@ -193,9 +226,11 @@ namespace HeliosDisplayManagement.Steam try { var json = JsonConvert.SerializeObject(gameIds); + if (!string.IsNullOrWhiteSpace(json)) { var dir = Path.GetDirectoryName(GameIdsPath); + if (dir != null) { Directory.CreateDirectory(dir); @@ -216,6 +251,7 @@ namespace HeliosDisplayManagement.Steam if (File.Exists(GameIdsPath)) { var json = File.ReadAllText(GameIdsPath, Encoding.Unicode); + if (!string.IsNullOrWhiteSpace(json)) { return JsonConvert.DeserializeObject(json); @@ -226,36 +262,47 @@ namespace HeliosDisplayManagement.Steam { // ignored } + return null; } private static Thread UpdateGamesFromWeb() { if (_allGamesUpdated) + { return null; + } + _allGamesUpdated = true; var thread = new Thread(() => { try { var newGames = new List(); + using (var webClient = new WebClient()) { webClient.Headers.Add(@"User-Agent", @"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0"); webClient.Headers.Add(@"Accept", @"text/html,application/xhtml+xml,application/xml;"); var response = webClient.OpenRead(@"https://steamdb.info/api/GetAppList/"); + if (response != null) + { using (response) { using (var reader = new StreamReader(response)) { var content = reader.ReadToEnd(); + if (!string.IsNullOrWhiteSpace(content)) { dynamic appids = JsonConvert.DeserializeObject(content); - if ((appids != null) && (appids.success == true)) + + if (appids != null && appids.success == true) + { foreach (var app in appids.data) + { try { newGames.Add(new SteamAppIdNamePair(uint.Parse(app.Name), @@ -265,18 +312,26 @@ namespace HeliosDisplayManagement.Steam { // ignored } + } + } } + reader.Close(); } + response.Close(); } + } } + if (newGames.Count > 0) + { lock (AllGamesLock) { _allGames = newGames; CacheGameIds(_allGames); } + } } catch { @@ -284,22 +339,39 @@ namespace HeliosDisplayManagement.Steam } }); thread.Start(); + return thread; } public override string ToString() { var name = Name; + if (string.IsNullOrWhiteSpace(name)) + { name = Language.Unknown; + } + if (IsRunning) + { return name + " " + Language.Running; + } + if (IsUpdating) + { return name + " " + Language.Updating; + } + if (IsInstalled) + { return name + " " + Language.Installed; + } + if (IsOwned) + { return name + " " + Language.Not_Installed; + } + return name + " " + Language.Not_Owned; } @@ -308,6 +380,7 @@ namespace HeliosDisplayManagement.Steam return Task.Run(() => { if (!Directory.Exists(IconCache)) + { try { Directory.CreateDirectory(IconCache); @@ -316,14 +389,22 @@ namespace HeliosDisplayManagement.Steam { return null; } + } + var localPath = Path.Combine(IconCache, AppId + ".ico"); + if (File.Exists(localPath)) + { return localPath; + } + var iconUrl = new HtmlWeb().Load("https://steamdb.info/app/" + AppId) .DocumentNode.SelectNodes("//a[@href]") .Select(node => node.Attributes["href"].Value) .FirstOrDefault(attribute => attribute.EndsWith(".ico") && attribute.Contains("/" + AppId + "/")); + if (!string.IsNullOrWhiteSpace(iconUrl)) + { try { using (var client = new WebClient()) @@ -335,6 +416,8 @@ namespace HeliosDisplayManagement.Steam { return null; } + } + return File.Exists(localPath) ? localPath : null; }); } diff --git a/HeliosDisplayManagement/UIForms/EditForm.cs b/HeliosDisplayManagement/UIForms/EditForm.cs index 69bb294..3315cec 100644 --- a/HeliosDisplayManagement/UIForms/EditForm.cs +++ b/HeliosDisplayManagement/UIForms/EditForm.cs @@ -32,8 +32,8 @@ namespace HeliosDisplayManagement.UIForms public sealed override string Text { - get { return base.Text; } - set { base.Text = value; } + get => base.Text; + set => base.Text = value; } private void btn_apply_Click(object sender, EventArgs e) @@ -42,30 +42,44 @@ namespace HeliosDisplayManagement.UIForms { if (!dv_profile.Profile.IsPossible) { - MessageBox.Show(this, Language.This_profile_is_currently_impossible_to_apply, Language.Apply_Profile, + MessageBox.Show(this, Language.This_profile_is_currently_impossible_to_apply, + Language.Apply_Profile, MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; } + var currentProfile = Profile.GetCurrent(string.Empty); Enabled = false; var failed = false; + if (new SplashForm(() => { Task.Factory.StartNew(() => { if (!dv_profile.Profile.Apply()) + { failed = true; + } }, TaskCreationOptions.LongRunning); - }, 3, 30).ShowDialog(this) != DialogResult.Cancel) + }, 3, 30).ShowDialog(this) != + DialogResult.Cancel) + { if (failed) + { MessageBox.Show(this, Language.Profile_is_invalid_or_not_possible_to_apply, Language.Error, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } else + { new SplashForm( () => { Task.Factory.StartNew(() => currentProfile.Apply(), TaskCreationOptions.LongRunning); }, 60, 30) {CancellationMessage = Language.Reverting_in}.ShowDialog(this); + } + } + Enabled = true; Activate(); } @@ -74,14 +88,17 @@ namespace HeliosDisplayManagement.UIForms private void btn_save_Click(object sender, EventArgs e) { if (SaveProfile()) + { DialogResult = DialogResult.OK; + } } private void cb_clone_SelectionChangeCommitted(object sender, EventArgs e) { - if ((cb_clone.SelectedItem != null) && (SelectedDisplay != null)) + if (cb_clone.SelectedItem != null && SelectedDisplay != null) { Path newSource; + if (cb_clone.SelectedItem is string) { newSource = new Path(); @@ -91,63 +108,85 @@ namespace HeliosDisplayManagement.UIForms { newSource = cb_clone.SelectedItem as Path; } + if (newSource != null) { var target = SelectedDisplay.GetPathTarget(Profile); var source = SelectedDisplay.GetPathSource(Profile); source.Targets = source.Targets.Where(pathTarget => pathTarget != target).ToArray(); newSource.Targets = newSource.Targets.Concat(new[] {target}).ToArray(); + if (source.Targets.Length == 0) + { Profile.Paths = Profile.Paths.Where(path => path != source).ToArray(); + } } + cb_resolution_SelectionChangeCommitted(null, null); cb_colordepth_SelectionChangeCommitted(null, null); cb_frequency_SelectionChangeCommitted(null, null); cb_rotation_SelectionChangeCommitted(null, null); } + RefreshArrangementSettings(); } private void cb_colordepth_SelectionChangeCommitted(object sender, EventArgs e) { - if ((cb_colordepth.SelectedItem != null) && (SelectedDisplay != null)) + if (cb_colordepth.SelectedItem != null && SelectedDisplay != null) + { SelectedDisplay.GetPathSource(Profile).PixelFormat = ColorDepthToPixelFormat((ColorDepth) cb_colordepth.SelectedItem); + } + RefreshArrangementSettings(); } private void cb_frequency_SelectionChangeCommitted(object sender, EventArgs e) { - if ((cb_frequency.SelectedItem != null) && (SelectedDisplay != null)) - SelectedDisplay.GetPathTarget(Profile).FrequencyInMillihertz = (uint) cb_frequency.SelectedItem*1000; + if (cb_frequency.SelectedItem != null && SelectedDisplay != null) + { + SelectedDisplay.GetPathTarget(Profile).FrequencyInMillihertz = (uint) cb_frequency.SelectedItem * 1000; + } + RefreshArrangementSettings(); } private void cb_resolution_SelectionChangeCommitted(object sender, EventArgs e) { - if ((cb_resolution.SelectedItem != null) && (SelectedDisplay != null)) + if (cb_resolution.SelectedItem != null && SelectedDisplay != null) + { SelectedDisplay.GetPathSource(Profile).Resolution = (Size) cb_resolution.SelectedItem; + } + RefreshArrangementSettings(); } private void cb_rotation_SelectionChangeCommitted(object sender, EventArgs e) { - if ((cb_rotation.SelectedItem != null) && (SelectedDisplay != null)) + if (cb_rotation.SelectedItem != null && SelectedDisplay != null) + { switch (cb_rotation.SelectedIndex) { case 1: SelectedDisplay.GetPathTarget(Profile).Rotation = Rotation.Rotate90; + break; case 2: SelectedDisplay.GetPathTarget(Profile).Rotation = Rotation.Rotate180; + break; case 3: SelectedDisplay.GetPathTarget(Profile).Rotation = Rotation.Rotate270; + break; default: SelectedDisplay.GetPathTarget(Profile).Rotation = Rotation.Identity; + break; } + } + RefreshArrangementSettings(); } @@ -160,14 +199,19 @@ namespace HeliosDisplayManagement.UIForms switch (depth) { case ColorDepth.Depth8Bit: + return DisplayConfigPixelFormat.PixelFormat8Bpp; case ColorDepth.Depth16Bit: + return DisplayConfigPixelFormat.PixelFormat16Bpp; case ColorDepth.Depth24Bit: + return DisplayConfigPixelFormat.PixelFormat24Bpp; case ColorDepth.Depth32Bit: + return DisplayConfigPixelFormat.PixelFormat32Bpp; default: + return DisplayConfigPixelFormat.NotSpecified; } } @@ -179,8 +223,12 @@ namespace HeliosDisplayManagement.UIForms private void lv_monitors_SelectionChangeCommitted(object sender, EventArgs e) { SelectedDisplay = null; + if (lv_monitors.SelectedItems.Count > 0) + { SelectedDisplay = lv_monitors.SelectedItems[0].Tag as DisplayRepresentation; + } + RefreshArrangementSettings(); } @@ -198,14 +246,19 @@ namespace HeliosDisplayManagement.UIForms switch (format) { case DisplayConfigPixelFormat.PixelFormat8Bpp: + return WindowsDisplayAPI.ColorDepth.Depth8Bit; case DisplayConfigPixelFormat.PixelFormat16Bpp: + return WindowsDisplayAPI.ColorDepth.Depth16Bit; case DisplayConfigPixelFormat.PixelFormat24Bpp: + return WindowsDisplayAPI.ColorDepth.Depth24Bit; case DisplayConfigPixelFormat.PixelFormat32Bpp: + return WindowsDisplayAPI.ColorDepth.Depth32Bit; default: + return WindowsDisplayAPI.ColorDepth.Depth4Bit; } } @@ -224,22 +277,29 @@ namespace HeliosDisplayManagement.UIForms dv_profile.Invalidate(); nud_x.Value = 0; nud_y.Value = 0; + try { if (SelectedDisplay != null) { var pathSource = SelectedDisplay.GetPathSource(Profile); var pathTarget = SelectedDisplay.GetPathTarget(Profile); + if (SelectedDisplay.IsAvailable) { //gb_arrangement.Enabled = true; var possibleSettings = SelectedDisplay.PossibleSettings; + foreach (var resolution in possibleSettings.Select(setting => setting.Resolution).Distinct()) { cb_resolution.Items.Add(resolution); + if ((Size) cb_resolution.Items[cb_resolution.Items.Count - 1] == pathSource.Resolution) + { cb_resolution.SelectedIndex = cb_resolution.Items.Count - 1; + } } + foreach ( var colorDepth in possibleSettings.Where(setting => setting.Resolution == pathSource.Resolution) @@ -247,23 +307,30 @@ namespace HeliosDisplayManagement.UIForms .Distinct()) { cb_colordepth.Items.Add(colorDepth); + if ((WindowsDisplayAPI.ColorDepth) cb_colordepth.Items[cb_colordepth.Items.Count - 1] == PixelFormatToColorDepth(pathSource.PixelFormat)) + { cb_colordepth.SelectedIndex = cb_colordepth.Items.Count - 1; + } } + foreach ( var frequency in possibleSettings.Where( setting => - (setting.Resolution == pathSource.Resolution) && - (setting.ColorDepth == PixelFormatToColorDepth(pathSource.PixelFormat))) + setting.Resolution == pathSource.Resolution && + setting.ColorDepth == PixelFormatToColorDepth(pathSource.PixelFormat)) .Select(setting => setting.Frequency) .Distinct()) { cb_frequency.Items.Add(frequency); + if ((int) cb_frequency.Items[cb_frequency.Items.Count - 1] == - (int) (pathTarget.FrequencyInMillihertz/1000)) + (int) (pathTarget.FrequencyInMillihertz / 1000)) + { cb_frequency.SelectedIndex = cb_frequency.Items.Count - 1; + } } } else @@ -272,52 +339,69 @@ namespace HeliosDisplayManagement.UIForms cb_resolution.SelectedIndex = 0; cb_colordepth.Items.Add(PixelFormatToColorDepth(pathSource.PixelFormat)); cb_colordepth.SelectedIndex = 0; - cb_frequency.Items.Add((int) (pathTarget.FrequencyInMillihertz/1000)); + cb_frequency.Items.Add((int) (pathTarget.FrequencyInMillihertz / 1000)); cb_frequency.SelectedIndex = 0; } + nud_x.Value = pathSource.Position.X; nud_y.Value = pathSource.Position.Y; cb_clone.Items.Clear(); cb_clone.Items.Add("None"); cb_clone.SelectedIndex = 0; + foreach ( var potentialClone in Profile.Paths.Where( path => - (path.Resolution == pathSource.Resolution) && - (path.Targets.First().DevicePath != SelectedDisplay.Path))) + path.Resolution == pathSource.Resolution && + path.Targets.First().DevicePath != SelectedDisplay.Path)) { cb_clone.Items.Add(potentialClone); + if (potentialClone.Targets.Contains(pathTarget)) + { cb_clone.SelectedIndex = cb_clone.Items.Count - 1; + } } + cb_rotation.Items.Clear(); cb_rotation.Items.Add("Identity"); cb_rotation.Items.Add("90 degree"); cb_rotation.Items.Add("180 degree"); cb_rotation.Items.Add("270 degree"); + switch (pathTarget.Rotation) { case Rotation.Identity: cb_rotation.SelectedIndex = 0; + break; case Rotation.Rotate90: cb_rotation.SelectedIndex = 1; + break; case Rotation.Rotate180: cb_rotation.SelectedIndex = 2; + break; case Rotation.Rotate270: cb_rotation.SelectedIndex = 3; + break; default: cb_rotation.SelectedIndex = 0; + break; } + if (pathTarget.SurroundTopology != null) + { cb_surround_applybezel.Checked = pathTarget.SurroundTopology.ApplyWithBezelCorrectedResolution; + } else + { cb_surround_applybezel.Checked = false; + } } } catch (Exception) @@ -330,6 +414,7 @@ namespace HeliosDisplayManagement.UIForms { imageList1.Images.Clear(); lv_monitors.Items.Clear(); + foreach (var display in DisplayRepresentation.GetDisplays(Profile)) { imageList1.Images.Add(display.ToBitmap(imageList1.ImageSize, Profile)); @@ -355,10 +440,12 @@ namespace HeliosDisplayManagement.UIForms MessageBoxButtons.OK, MessageBoxIcon.Warning); txt_name.Focus(); + return false; } Profile.Name = txt_name.Text.Trim(); + return true; } } diff --git a/HeliosDisplayManagement/UIForms/MainForm.cs b/HeliosDisplayManagement/UIForms/MainForm.cs index 668b232..291283c 100644 --- a/HeliosDisplayManagement/UIForms/MainForm.cs +++ b/HeliosDisplayManagement/UIForms/MainForm.cs @@ -27,6 +27,7 @@ namespace HeliosDisplayManagement.UIForms new ProfileIcon(profile ?? Profile.GetCurrent()).ToBitmap( il_profiles.ImageSize.Width, il_profiles.ImageSize.Height)); + return lv_profiles.Items.Add(new ListViewItem { Text = profile?.Name ?? Language.Current, @@ -40,25 +41,33 @@ namespace HeliosDisplayManagement.UIForms private void Apply_Click(object sender, EventArgs e) { - if ((dv_profile.Profile != null) && (lv_profiles.SelectedIndices.Count > 0) && - (lv_profiles.SelectedItems[0].Tag != null)) + if (dv_profile.Profile != null && + lv_profiles.SelectedIndices.Count > 0 && + lv_profiles.SelectedItems[0].Tag != null) { if (!dv_profile.Profile.IsPossible) { - MessageBox.Show(this, Language.This_profile_is_currently_impossible_to_apply, Language.Apply_Profile, + MessageBox.Show(this, Language.This_profile_is_currently_impossible_to_apply, + Language.Apply_Profile, MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; } Enabled = false; Visible = false; + if ( new SplashForm( () => { Task.Factory.StartNew(() => dv_profile.Profile.Apply(), TaskCreationOptions.LongRunning); - }, 3, 30).ShowDialog(this) != DialogResult.Cancel) + }, 3, 30).ShowDialog(this) != + DialogResult.Cancel) + { ReloadProfiles(); + } + Visible = true; Enabled = true; Activate(); @@ -77,14 +86,20 @@ namespace HeliosDisplayManagement.UIForms var clone = dv_profile.Profile.Clone(); var i = 0; string name; + while (true) { i++; name = $"{clone.Name} ({i})"; + if (lv_profiles.Items.OfType().Any(profile => profile.Name == name)) + { continue; + } + break; } + clone.Name = name; AddProfile(clone).Selected = true; SaveProfiles(); @@ -94,8 +109,9 @@ namespace HeliosDisplayManagement.UIForms private void CreateShortcut_Click(object sender, EventArgs e) { - if ((dv_profile.Profile != null) && (lv_profiles.SelectedIndices.Count > 0) && - (lv_profiles.SelectedItems[0].Tag != null)) + if (dv_profile.Profile != null && + lv_profiles.SelectedIndices.Count > 0 && + lv_profiles.SelectedItems[0].Tag != null) { var shortcutForm = new ShortcutForm(dv_profile.Profile); shortcutForm.ShowDialog(this); @@ -105,46 +121,56 @@ namespace HeliosDisplayManagement.UIForms private void Delete_Click(object sender, EventArgs e) { - if ((dv_profile.Profile != null) && (lv_profiles.SelectedIndices.Count > 0) && - (lv_profiles.SelectedItems[0].Tag != null)) + if (dv_profile.Profile != null && + lv_profiles.SelectedIndices.Count > 0 && + lv_profiles.SelectedItems[0].Tag != null) { var selectedIndex = lv_profiles.SelectedIndices[0]; + if ( MessageBox.Show(this, Language.Are_you_sure, Language.Deletion, MessageBoxButtons.YesNo, - MessageBoxIcon.Warning) == DialogResult.Yes) + MessageBoxIcon.Warning) == + DialogResult.Yes) { il_profiles.Images.RemoveAt(lv_profiles.Items[selectedIndex].ImageIndex); lv_profiles.Items.RemoveAt(selectedIndex); SaveProfiles(); } } + ReloadProfiles(); } private void Edit_Click(object sender, EventArgs e) { - if ((dv_profile.Profile != null) && (lv_profiles.SelectedIndices.Count > 0) && - (lv_profiles.SelectedItems[0].Tag != null)) + if (dv_profile.Profile != null && + lv_profiles.SelectedIndices.Count > 0 && + lv_profiles.SelectedItems[0].Tag != null) { var selectedIndex = lv_profiles.SelectedIndices[0]; var editForm = new EditForm(dv_profile.Profile); + if (editForm.ShowDialog(this) == DialogResult.OK) { lv_profiles.Items[selectedIndex].Tag = editForm.Profile; SaveProfiles(); } } + ReloadProfiles(); } private void lv_profiles_AfterLabelEdit(object sender, LabelEditEventArgs e) { var selectedProfile = (Profile) lv_profiles.Items[e.Item].Tag; - if ((selectedProfile == null) || (e.Label == null) || (selectedProfile.Name == e.Label)) + + if (selectedProfile == null || e.Label == null || selectedProfile.Name == e.Label) { e.CancelEdit = true; + return; } + if (string.IsNullOrWhiteSpace(e.Label) || lv_profiles.Items.Cast() .Select(item => item.Tag as Profile) @@ -158,6 +184,7 @@ namespace HeliosDisplayManagement.UIForms MessageBoxButtons.OK, MessageBoxIcon.Warning); e.CancelEdit = true; + return; } @@ -177,32 +204,44 @@ namespace HeliosDisplayManagement.UIForms private void lv_profiles_MouseUp(object sender, MouseEventArgs e) { - if ((e.Button == MouseButtons.Right) && (lv_profiles.SelectedItems.Count > 0)) + if (e.Button == MouseButtons.Right && lv_profiles.SelectedItems.Count > 0) { var itemRect = lv_profiles.GetItemRect(lv_profiles.SelectedIndices[0]); - if ((e.Location.X > itemRect.X) && (e.Location.X <= itemRect.Right) && (e.Location.Y > itemRect.Y) && - (e.Location.Y <= itemRect.Bottom)) + + if (e.Location.X > itemRect.X && + e.Location.X <= itemRect.Right && + e.Location.Y > itemRect.Y && + e.Location.Y <= itemRect.Bottom) + { menu_profiles.Show(lv_profiles, e.Location); + } } } private void lv_profiles_SelectedIndexChanged(object sender, EventArgs e) { if (lv_profiles.SelectedItems.Count > 0) - dv_profile.Profile = lv_profiles.SelectedItems[0].Tag as Profile ?? Profile.GetCurrent(Language.Current); + { + dv_profile.Profile = + lv_profiles.SelectedItems[0].Tag as Profile ?? Profile.GetCurrent(Language.Current); + } else + { dv_profile.Profile = null; + } + lbl_profile.Text = dv_profile.Profile?.Name ?? Language.None; applyToolStripMenuItem.Enabled = - btn_apply.Enabled = (dv_profile.Profile != null) && (lv_profiles.SelectedItems[0].Tag != null) && + btn_apply.Enabled = dv_profile.Profile != null && + lv_profiles.SelectedItems[0].Tag != null && !dv_profile.Profile.IsActive; editToolStripMenuItem.Enabled = - btn_edit.Enabled = (dv_profile.Profile != null) && (lv_profiles.SelectedItems[0].Tag != null); + btn_edit.Enabled = dv_profile.Profile != null && lv_profiles.SelectedItems[0].Tag != null; deleteToolStripMenuItem.Enabled = - btn_delete.Enabled = (dv_profile.Profile != null) && (lv_profiles.SelectedItems[0].Tag != null); + btn_delete.Enabled = dv_profile.Profile != null && lv_profiles.SelectedItems[0].Tag != null; cloneToolStripMenuItem.Enabled = btn_clone.Enabled = dv_profile.Profile != null; createShortcutToolStripMenuItem.Enabled = - btn_shortcut.Enabled = (dv_profile.Profile != null) && (lv_profiles.SelectedItems[0].Tag != null); + btn_shortcut.Enabled = dv_profile.Profile != null && lv_profiles.SelectedItems[0].Tag != null; RefreshProfilesStatus(); } @@ -228,10 +267,17 @@ namespace HeliosDisplayManagement.UIForms var profiles = Profile.GetAllProfiles().ToArray(); lv_profiles.Items.Clear(); il_profiles.Images.Clear(); + if (!profiles.Any(profile => profile.IsActive)) + { AddProfile().Selected = true; + } + foreach (var profile in profiles) + { AddProfile(profile); + } + lv_profiles.SelectedIndices.Clear(); lv_profiles.Invalidate(); } diff --git a/HeliosDisplayManagement/UIForms/ShortcutForm.cs b/HeliosDisplayManagement/UIForms/ShortcutForm.cs index 3d5f106..734cc3e 100644 --- a/HeliosDisplayManagement/UIForms/ShortcutForm.cs +++ b/HeliosDisplayManagement/UIForms/ShortcutForm.cs @@ -25,7 +25,7 @@ namespace HeliosDisplayManagement.UIForms public string Arguments { - get { return cb_args.Checked ? txt_args.Text : string.Empty; } + get => cb_args.Checked ? txt_args.Text : string.Empty; set { txt_args.Text = value; @@ -35,7 +35,7 @@ namespace HeliosDisplayManagement.UIForms public string FileName { - get { return cb_temp.Checked && rb_standalone.Checked ? txt_executable.Text : string.Empty; } + get => cb_temp.Checked && rb_standalone.Checked ? txt_executable.Text : string.Empty; set { if (!string.IsNullOrWhiteSpace(value)) @@ -48,16 +48,14 @@ namespace HeliosDisplayManagement.UIForms } public static string IconCache - => - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + { + get => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Assembly.GetExecutingAssembly().GetName().Name, @"IconCache"); + } public string ProcessName { - get - { - return cb_temp.Checked && rb_standalone.Checked && cb_process.Checked ? txt_process.Text : string.Empty; - } + get => cb_temp.Checked && rb_standalone.Checked && cb_process.Checked ? txt_process.Text : string.Empty; set { txt_process.Text = value; @@ -67,13 +65,13 @@ namespace HeliosDisplayManagement.UIForms public Profile Profile { - get { return dv_profile.Profile; } - set { dv_profile.Profile = value; } + get => dv_profile.Profile; + set => dv_profile.Profile = value; } public uint SteamAppId { - get { return cb_temp.Checked && rb_steam.Checked ? (uint) nud_steamappid.Value : 0; } + get => cb_temp.Checked && rb_steam.Checked ? (uint) nud_steamappid.Value : 0; set { if (value > 0) @@ -90,11 +88,20 @@ namespace HeliosDisplayManagement.UIForms get { if (!cb_temp.Checked) + { return 0; + } + if (!rb_standalone.Checked) + { return (uint) nud_steamtimeout.Value; + } + if (cb_process.Checked) + { return (uint) nud_timeout.Value; + } + return 0; } set @@ -110,7 +117,8 @@ namespace HeliosDisplayManagement.UIForms private void btn_app_executable_Click(object sender, EventArgs e) { if (dialog_open.ShowDialog(this) == DialogResult.OK) - if (File.Exists(dialog_open.FileName) && (Path.GetExtension(dialog_open.FileName) == @".exe")) + { + if (File.Exists(dialog_open.FileName) && Path.GetExtension(dialog_open.FileName) == @".exe") { txt_executable.Text = dialog_open.FileName; dialog_open.FileName = string.Empty; @@ -123,27 +131,34 @@ namespace HeliosDisplayManagement.UIForms MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } + } } private void btn_save_Click(object sender, EventArgs e) { DialogResult = DialogResult.None; + try { if (dialog_save.ShowDialog(this) == DialogResult.OK) { if (CreateShortcut(dialog_save.FileName)) + { MessageBox.Show( Language.Shortcut_place_successfully, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Information); + } else + { MessageBox.Show( Language.Failed_to_create_the_shortcut_Unexpected_exception_occurred, Language.Shortcut, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + } + dialog_save.FileName = string.Empty; DialogResult = DialogResult.OK; } @@ -167,7 +182,9 @@ namespace HeliosDisplayManagement.UIForms txt_args.Enabled = cb_args.Checked; if (rb_steam.Checked) + { nud_steamappid_ValueChanged(rb_steam, e); + } } // ReSharper disable once FunctionComplexityOverflow @@ -182,7 +199,9 @@ namespace HeliosDisplayManagement.UIForms $"-a {HeliosStartupAction.SwitchProfile}", $"-p \"{dv_profile.Profile.Name}\"" }; + if (!Directory.Exists(IconCache)) + { try { Directory.CreateDirectory(IconCache); @@ -191,21 +210,32 @@ namespace HeliosDisplayManagement.UIForms { // ignored } + } + if (cb_temp.Checked) { if (rb_standalone.Checked) { if (string.IsNullOrWhiteSpace(txt_executable.Text)) + { throw new Exception(Language.Executable_address_can_not_be_empty); + } + if (!File.Exists(txt_executable.Text)) + { throw new Exception(Language.Executable_file_not_found); + } + args.Add($"-e \"{txt_executable.Text.Trim()}\""); + if (!string.IsNullOrWhiteSpace(txt_process.Text)) { args.Add($"-w \"{txt_process.Text.Trim()}\""); args.Add($"-t {(int) nud_timeout.Value}"); } + description = string.Format(Language.Executing_application_with_profile, programName, Profile.Name); + try { icon = Path.Combine(IconCache, Guid.NewGuid() + ".ico"); @@ -220,14 +250,19 @@ namespace HeliosDisplayManagement.UIForms else if (rb_steam.Checked) { if (!SteamGame.SteamInstalled) + { throw new Exception(Language.Steam_is_not_installed); + } + var steamGame = new SteamGame((uint) nud_steamappid.Value); args.Add($"-s {(int) nud_steamappid.Value}"); args.Add($"-t {(int) nud_steamtimeout.Value}"); description = string.Format(Language.Executing_application_with_profile, steamGame.Name, Profile.Name); var steamIcon = steamGame.GetIcon().Result; + if (!string.IsNullOrWhiteSpace(steamIcon)) + { try { icon = Path.Combine(IconCache, Guid.NewGuid() + ".ico"); @@ -238,15 +273,22 @@ namespace HeliosDisplayManagement.UIForms { icon = steamIcon; } + } else + { icon = $"{SteamGame.SteamAddress},0"; + } } + if (cb_args.Checked && !string.IsNullOrWhiteSpace(txt_args.Text)) + { args.Add($"--arguments \"{txt_args.Text.Trim()}\""); + } } else { description = string.Format(Language.Switching_display_profile_to_profile, Profile.Name); + try { icon = Path.Combine(IconCache, Guid.NewGuid() + ".ico"); @@ -259,18 +301,24 @@ namespace HeliosDisplayManagement.UIForms } fileName = Path.ChangeExtension(fileName, @"lnk"); + if (fileName != null) + { try { // Remove the old file to replace it if (File.Exists(fileName)) + { File.Delete(fileName); + } var wshShellType = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8")); dynamic wshShell = Activator.CreateInstance(wshShellType); + try { var shortcut = wshShell.CreateShortcut(fileName); + try { shortcut.TargetPath = Application.ExecutablePath; @@ -278,8 +326,12 @@ namespace HeliosDisplayManagement.UIForms shortcut.Description = description; shortcut.WorkingDirectory = Path.GetDirectoryName(Application.ExecutablePath) ?? string.Empty; + if (!string.IsNullOrWhiteSpace(icon)) + { shortcut.IconLocation = icon; + } + shortcut.Save(); } finally @@ -296,9 +348,13 @@ namespace HeliosDisplayManagement.UIForms { // Clean up a failed attempt if (File.Exists(fileName)) + { File.Delete(fileName); + } } - return (fileName != null) && File.Exists(fileName); + } + + return fileName != null && File.Exists(fileName); } private void nud_steamappid_ValueChanged(object sender, EventArgs e) @@ -309,8 +365,11 @@ namespace HeliosDisplayManagement.UIForms private void nud_steamapps_Click(object sender, EventArgs e) { var steamGamesForm = new SteamGamesForm(); - if ((steamGamesForm.ShowDialog(this) == DialogResult.OK) && (steamGamesForm.SteamGame != null)) + + if (steamGamesForm.ShowDialog(this) == DialogResult.OK && steamGamesForm.SteamGame != null) + { nud_steamappid.Value = steamGamesForm.SteamGame.AppId; + } } private void txt_executable_TextChanged(object sender, EventArgs e) diff --git a/HeliosDisplayManagement/UIForms/SplashForm.cs b/HeliosDisplayManagement/UIForms/SplashForm.cs index e82aad6..b21dfae 100644 --- a/HeliosDisplayManagement/UIForms/SplashForm.cs +++ b/HeliosDisplayManagement/UIForms/SplashForm.cs @@ -47,24 +47,33 @@ namespace HeliosDisplayManagement.UIForms lock (_progressPositions) { progressPanel.DrawToBitmap(_progressImage, new Rectangle(Point.Empty, progressPanel.Size)); + foreach (var position in _progressPositions) + { e.Graphics.DrawImage(_progressImage, new Rectangle(position, progressPanel.Size)); + } } + base.OnPaint(e); } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData != Keys.Escape) + { return base.ProcessCmdKey(ref msg, keyData); + } + if (t_start.Enabled) { t_start.Stop(); t_countdown.Stop(); DialogResult = DialogResult.Cancel; Close(); + return true; } + return true; } @@ -72,6 +81,7 @@ namespace HeliosDisplayManagement.UIForms { lbl_message.Text = CountdownMessage; progressBar.ProgressColor = Color.OrangeRed; + if (_countdownCounter > 0) { progressBar.Text = (progressBar.Value = progressBar.Maximum = _countdownCounter).ToString(); @@ -95,6 +105,7 @@ namespace HeliosDisplayManagement.UIForms { lbl_message.Text = CancellationMessage; progressBar.ProgressColor = Color.DodgerBlue; + if (_startCounter > 0) { progressBar.Text = (progressBar.Value = progressBar.Maximum = _startCounter).ToString(); @@ -128,8 +139,8 @@ namespace HeliosDisplayManagement.UIForms _progressPositions.AddRange( screens.Select( screen => - new Point(screen.Bounds.X - minX + (screen.Bounds.Width - progressPanel.Width)/2, - screen.Bounds.Y - minY + (screen.Bounds.Height - progressPanel.Height)/2))); + new Point(screen.Bounds.X - minX + (screen.Bounds.Width - progressPanel.Width) / 2, + screen.Bounds.Y - minY + (screen.Bounds.Height - progressPanel.Height) / 2))); } #if !DEBUG TopMost = true; @@ -141,7 +152,10 @@ namespace HeliosDisplayManagement.UIForms private void SplashForm_FormClosing(object sender, FormClosingEventArgs e) { if (_isClosing) + { return; + } + _isClosing = true; e.Cancel = true; var dialogResult = DialogResult; @@ -191,8 +205,10 @@ namespace HeliosDisplayManagement.UIForms t_countdown.Stop(); DialogResult = DialogResult.OK; Close(); + return; } + progressBar.Value = _countdownCounter; progressBar.Text = progressBar.Value.ToString(); _countdownCounter--; @@ -205,8 +221,10 @@ namespace HeliosDisplayManagement.UIForms { t_start.Stop(); DoJob(); + return; } + progressBar.Value = _startCounter; progressBar.Text = progressBar.Value.ToString(); _startCounter--; diff --git a/HeliosDisplayManagement/UIForms/SteamGamesForm.cs b/HeliosDisplayManagement/UIForms/SteamGamesForm.cs index 1f7e934..e088aea 100644 --- a/HeliosDisplayManagement/UIForms/SteamGamesForm.cs +++ b/HeliosDisplayManagement/UIForms/SteamGamesForm.cs @@ -19,15 +19,22 @@ namespace HeliosDisplayManagement.UIForms private void lv_games_DoubleClick(object sender, EventArgs e) { if (btn_ok.Enabled) + { btn_ok.PerformClick(); + } } private void lv_games_SelectedIndexChanged(object sender, EventArgs e) { if (lv_games.SelectedItems.Count > 0) + { SteamGame = lv_games.SelectedItems[0].Tag as SteamGame; + } else + { SteamGame = null; + } + btn_ok.Enabled = SteamGame != null; } @@ -38,7 +45,9 @@ namespace HeliosDisplayManagement.UIForms SteamGame.GetAllOwnedGames().OrderByDescending(game => game.IsInstalled).ThenBy(game => game.Name)) { var iconAddress = await game.GetIcon(); + if (!string.IsNullOrWhiteSpace(iconAddress)) + { try { using (var fileReader = File.OpenRead(iconAddress)) @@ -51,10 +60,17 @@ namespace HeliosDisplayManagement.UIForms { il_games.Images.Add(Properties.Resources.SteamIcon); } + } else + { il_games.Images.Add(Properties.Resources.SteamIcon); + } + if (!Visible) + { return; + } + lv_games.Items.Add(new ListViewItem { Text = game.Name, diff --git a/HeliosDisplayManagement/packages.config b/HeliosDisplayManagement/packages.config index 4a265bd..bc461e1 100644 --- a/HeliosDisplayManagement/packages.config +++ b/HeliosDisplayManagement/packages.config @@ -1,4 +1,5 @@  +