2021-01-28 08:34:45 +00:00
using Newtonsoft.Json ;
2020-06-14 04:20:52 +00:00
using System ;
using System.Collections.Generic ;
using System.Drawing.IconLib ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Diagnostics ;
using System.Text.RegularExpressions ;
2021-06-12 21:44:36 +00:00
using DisplayMagicianShared.AMD ;
2021-06-22 09:05:24 +00:00
using DisplayMagicianShared.NVIDIA ;
2021-07-24 04:05:38 +00:00
using DisplayMagicianShared.Windows ;
2021-08-27 05:53:32 +00:00
using System.Runtime.Serialization ;
2021-10-09 05:42:50 +00:00
using Newtonsoft.Json.Linq ;
2022-01-05 20:05:55 +00:00
using System.Windows.Forms ;
using System.Threading ;
2022-01-29 02:25:52 +00:00
using System.Threading.Tasks ;
2020-10-04 11:18:22 +00:00
2020-12-20 07:42:04 +00:00
namespace DisplayMagicianShared
2020-06-14 04:20:52 +00:00
{
2021-08-22 03:42:12 +00:00
// This enum sets the video card mode used within DisplayMagician
// It effectively controls what video card library is used to store profiles on the computer
// We look up the PCI vendor ID for the video cards, and then we look for them in the order from most commonly
// sold video card to the least, followed by the generic 'catch-all' windows mode.
public enum VIDEO_MODE : Int32
{
WINDOWS = 0 ,
NVIDIA = 1 ,
AMD = 2 ,
}
2021-03-28 07:25:01 +00:00
2021-09-02 01:47:28 +00:00
public enum FORCED_VIDEO_MODE : Int32
{
WINDOWS = 0 ,
NVIDIA = 1 ,
AMD = 2 ,
DETECT = 99 ,
}
2021-08-27 05:53:32 +00:00
public enum ApplyProfileResult
{
Successful ,
Cancelled ,
Error
}
2020-07-21 07:50:41 +00:00
public static class ProfileRepository
2020-06-14 04:20:52 +00:00
{
#region Class Variables
// Common items to the class
2020-08-07 03:59:23 +00:00
private static List < ProfileItem > _allProfiles = new List < ProfileItem > ( ) ;
2021-03-06 04:43:07 +00:00
public static Dictionary < string , bool > _profileWarningLookup = new Dictionary < string , bool > ( ) ;
2020-08-07 03:59:23 +00:00
private static bool _profilesLoaded = false ;
2020-10-09 03:27:59 +00:00
private static ProfileItem _currentProfile ;
2021-03-07 04:11:46 +00:00
private static List < string > _connectedDisplayIdentifiers = new List < string > ( ) ;
2021-05-21 02:41:49 +00:00
private static bool notifiedEDIDErrorToUser = false ;
2021-08-22 03:42:12 +00:00
private static AMDLibrary amdLibrary ;
private static NVIDIALibrary nvidiaLibrary ;
private static WinLibrary winLibrary ;
2021-09-02 01:47:28 +00:00
// Make the default video mode Windows
private static VIDEO_MODE _currentVideoMode = VIDEO_MODE . WINDOWS ;
private static FORCED_VIDEO_MODE _forcedVideoMode = FORCED_VIDEO_MODE . DETECT ;
2022-01-29 02:25:52 +00:00
private static bool _pauseReadsUntilChangeCompleted = false ;
2020-10-09 03:27:59 +00:00
2020-06-14 04:20:52 +00:00
// Other constants that are useful
2020-12-02 08:11:23 +00:00
public static string AppDataPath = System . IO . Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , "DisplayMagician" ) ;
2020-10-06 10:49:10 +00:00
public static string AppIconPath = System . IO . Path . Combine ( AppDataPath , $"Icons" ) ;
2020-12-02 08:11:23 +00:00
public static string AppDisplayMagicianIconFilename = System . IO . Path . Combine ( AppIconPath , @"DisplayMagician.ico" ) ;
2021-01-28 09:20:00 +00:00
private static readonly string AppProfileStoragePath = System . IO . Path . Combine ( AppDataPath , $"Profiles" ) ;
2022-03-25 08:51:38 +00:00
private static readonly string _profileStorageJsonFileName = System . IO . Path . Combine ( AppProfileStoragePath , $"DisplayProfiles_2.3.json" ) ;
2021-09-02 01:47:28 +00:00
2020-10-09 03:27:59 +00:00
2020-06-14 04:20:52 +00:00
#endregion
#region Class Constructors
2020-07-21 07:50:41 +00:00
static ProfileRepository ( )
2020-06-14 04:20:52 +00:00
{
2021-09-02 01:47:28 +00:00
2021-06-14 09:42:16 +00:00
try
{
2020-08-18 22:16:04 +00:00
// Create the Profile Storage Path if it doesn't exist so that it's avilable for all the program
if ( ! Directory . Exists ( AppProfileStoragePath ) )
{
2021-03-26 08:54:45 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ProfileRepository: Creating the Profiles storage folder {AppProfileStoragePath}." ) ;
2020-08-18 22:16:04 +00:00
Directory . CreateDirectory ( AppProfileStoragePath ) ;
}
2020-07-24 01:11:42 +00:00
}
2021-02-10 09:40:22 +00:00
catch ( UnauthorizedAccessException ex )
{
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/ProfileRepository: DisplayMagician doesn't have permissions to create the Profiles storage folder {AppProfileStoragePath}." ) ;
}
catch ( ArgumentException ex )
{
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/ProfileRepository: DisplayMagician can't create the Profiles storage folder {AppProfileStoragePath} due to an invalid argument." ) ;
}
catch ( PathTooLongException ex )
{
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/ProfileRepository: DisplayMagician can't create the Profiles storage folder {AppProfileStoragePath} as the path is too long." ) ;
}
catch ( DirectoryNotFoundException ex )
{
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/ProfileRepository: DisplayMagician can't create the Profiles storage folder {AppProfileStoragePath} as the parent folder isn't there." ) ;
}
2020-07-24 01:11:42 +00:00
catch ( Exception ex )
{
2021-06-14 09:42:16 +00:00
SharedLogger . logger . Warn ( ex , $"ProfileRepository/ProfileRepository: Exception creating the Profiles storage folder." ) ;
2021-10-24 08:39:49 +00:00
}
2020-06-14 04:20:52 +00:00
}
#endregion
#region Class Properties
public static List < ProfileItem > AllProfiles
{
get
{
2020-08-07 03:59:23 +00:00
if ( ! _profilesLoaded )
2020-06-14 04:20:52 +00:00
// Load the Profiles from storage if they need to be
LoadProfiles ( ) ;
return _allProfiles ;
}
}
2021-03-06 04:43:07 +00:00
public static Dictionary < string , bool > ProfileWarningLookup
2021-03-05 01:44:53 +00:00
{
get
{
if ( ! _profilesLoaded )
// Load the Profiles from storage if they need to be
LoadProfiles ( ) ;
2021-03-06 04:43:07 +00:00
return _profileWarningLookup ;
2021-03-05 01:44:53 +00:00
}
}
2020-06-14 04:20:52 +00:00
public static ProfileItem CurrentProfile
{
2021-08-24 08:37:32 +00:00
get
2020-06-14 04:20:52 +00:00
{
2020-10-09 03:27:59 +00:00
if ( _currentProfile = = null )
UpdateActiveProfile ( ) ;
2020-06-14 04:20:52 +00:00
return _currentProfile ;
}
set
{
if ( value is ProfileItem )
{
_currentProfile = value ;
// And if we have the _originalBitmap we can also save the Bitmap overlay, but only if the ProfileToUse is set
//if (_originalBitmap is Bitmap)
// _shortcutBitmap = ToBitmapOverlay(_originalBitmap, ProfileToUse.ProfileTightestBitmap, 256, 256);
}
}
}
public static int ProfileCount
{
get
{
2020-08-07 03:59:23 +00:00
if ( ! _profilesLoaded )
2020-06-15 09:57:46 +00:00
// Load the Profiles from storage if they need to be
LoadProfiles ( ) ;
2020-08-07 03:59:23 +00:00
2020-06-14 04:20:52 +00:00
return _allProfiles . Count ;
}
}
2021-10-02 03:22:36 +00:00
public static string ProfileStorageFileName
{
get = > _profileStorageJsonFileName ;
}
2021-08-22 06:58:08 +00:00
public static VIDEO_MODE CurrentVideoMode
2021-08-22 03:42:12 +00:00
{
get
2021-08-24 08:37:32 +00:00
{
2021-08-22 06:58:08 +00:00
return _currentVideoMode ;
2021-08-22 03:42:12 +00:00
}
set
{
2021-08-22 06:58:08 +00:00
_currentVideoMode = value ;
2021-08-22 03:42:12 +00:00
}
}
2021-09-02 01:47:28 +00:00
public static FORCED_VIDEO_MODE ForcedVideoMode
{
get
{
return _forcedVideoMode ;
}
set
{
_forcedVideoMode = value ;
SetVideoCardMode ( value ) ;
}
}
2021-08-22 03:42:12 +00:00
2021-08-24 08:37:32 +00:00
2021-03-07 04:11:46 +00:00
public static List < string > ConnectedDisplayIdentifiers
{
get
{
if ( _connectedDisplayIdentifiers . Count = = 0 )
// Load the Profiles from storage if they need to be
2021-07-24 04:05:38 +00:00
_connectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers ( ) ;
2021-03-07 04:11:46 +00:00
return _connectedDisplayIdentifiers ;
}
set
{
_connectedDisplayIdentifiers = value ;
}
}
2021-08-24 08:37:32 +00:00
public static bool ProfilesLoaded {
get
2021-06-27 01:53:00 +00:00
{
return _profilesLoaded ;
2021-08-24 08:37:32 +00:00
}
set
2021-06-27 01:53:00 +00:00
{
_profilesLoaded = value ;
2021-08-24 08:37:32 +00:00
}
2021-06-27 01:53:00 +00:00
}
2020-06-14 04:20:52 +00:00
#endregion
2021-03-07 04:11:46 +00:00
2020-06-14 04:20:52 +00:00
#region Class Methods
2021-09-04 08:58:08 +00:00
public static bool InitialiseRepository ( FORCED_VIDEO_MODE forcedVideoMode = FORCED_VIDEO_MODE . DETECT )
{
if ( ! SetVideoCardMode ( forcedVideoMode ) )
{
return false ;
}
2021-09-19 08:56:58 +00:00
if ( ! _profilesLoaded )
2021-09-04 08:58:08 +00:00
{
2021-09-19 08:56:58 +00:00
if ( ! LoadProfiles ( ) )
{
return false ;
}
}
2021-09-04 08:58:08 +00:00
return true ;
}
2020-08-19 06:55:50 +00:00
public static bool AddProfile ( ProfileItem profile )
2020-06-14 04:20:52 +00:00
{
2020-08-19 06:55:50 +00:00
if ( ! ( profile is ProfileItem ) )
2020-06-14 04:20:52 +00:00
return false ;
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/AddProfile: Adding profile {profile.Name} to our profile repository" ) ;
2020-06-14 04:20:52 +00:00
// Doublecheck if it already exists
// Because then we just update the one that already exists
2020-08-19 06:55:50 +00:00
if ( ! ContainsProfile ( profile ) )
2020-06-14 04:20:52 +00:00
{
// Add the Profile to the list of Profiles
2020-08-19 06:55:50 +00:00
_allProfiles . Add ( profile ) ;
2020-06-15 09:57:46 +00:00
2020-06-14 04:20:52 +00:00
// Generate the Profile Icon ready to be used
2020-08-19 06:55:50 +00:00
SaveProfileIconToCache ( profile ) ;
profile . PreSave ( ) ;
2020-06-14 04:20:52 +00:00
// Save the Profiles JSON as it's different
SaveProfiles ( ) ;
2020-08-19 06:55:50 +00:00
}
2020-06-14 04:20:52 +00:00
2021-03-05 01:44:53 +00:00
// Refresh the profiles to see whats valid
IsPossibleRefresh ( ) ;
2020-08-19 06:55:50 +00:00
//Doublecheck it's been added
if ( ContainsProfile ( profile ) )
{
2020-06-14 04:20:52 +00:00
return true ;
}
else
return false ;
}
2020-07-26 08:52:46 +00:00
2021-02-14 09:42:48 +00:00
public static bool RemoveProfile ( ProfileItem profile )
2020-06-14 04:20:52 +00:00
{
2021-02-14 09:42:48 +00:00
if ( ! ( profile is ProfileItem ) )
2020-06-14 04:20:52 +00:00
return false ;
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/RemoveProfile: Removing profile {profile.Name} if it exists in our profile repository" ) ;
2020-06-14 04:20:52 +00:00
// Remove the Profile Icons from the Cache
2021-02-14 09:42:48 +00:00
List < ProfileItem > ProfilesToRemove = _allProfiles . FindAll ( item = > item . UUID . Equals ( profile . UUID ) ) ;
2020-06-14 04:20:52 +00:00
foreach ( ProfileItem ProfileToRemove in ProfilesToRemove )
{
try
{
File . Delete ( ProfileToRemove . SavedProfileIconCacheFilename ) ;
2021-08-27 09:15:53 +00:00
File . Delete ( ProfileToRemove . WallpaperBitmapFilename ) ;
2020-06-14 04:20:52 +00:00
}
2021-02-10 09:40:22 +00:00
catch ( UnauthorizedAccessException ex )
2020-06-14 04:20:52 +00:00
{
2021-02-10 09:40:22 +00:00
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile: DisplayMagician doesn't have permissions to delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename}." ) ;
2020-06-14 04:20:52 +00:00
}
2021-02-10 09:40:22 +00:00
catch ( ArgumentException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} due to an invalid argument." ) ;
}
catch ( PathTooLongException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the path is too long." ) ;
}
catch ( DirectoryNotFoundException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there." ) ;
}
2020-06-14 04:20:52 +00:00
}
// Remove the Profile from the list.
2021-02-14 09:42:48 +00:00
int numRemoved = _allProfiles . RemoveAll ( item = > item . UUID . Equals ( profile . UUID ) ) ;
2020-06-14 04:20:52 +00:00
if ( numRemoved = = 1 )
{
SaveProfiles ( ) ;
2021-03-04 22:12:27 +00:00
IsPossibleRefresh ( ) ;
2020-06-14 04:20:52 +00:00
return true ;
}
else if ( numRemoved = = 0 )
return false ;
else
throw new ProfileRepositoryException ( ) ;
}
2021-02-14 09:42:48 +00:00
public static bool RemoveProfile ( string profileName )
2020-06-14 04:20:52 +00:00
{
2021-08-24 08:37:32 +00:00
2021-02-14 09:42:48 +00:00
if ( String . IsNullOrWhiteSpace ( profileName ) )
2020-06-14 04:20:52 +00:00
return false ;
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/RemoveProfile2: Removing profile {profileName} if it exists in our profile repository" ) ;
2020-06-14 04:20:52 +00:00
// Remove the Profile Icons from the Cache
2021-02-14 09:42:48 +00:00
List < ProfileItem > ProfilesToRemove = _allProfiles . FindAll ( item = > item . Name . Equals ( profileName ) ) ;
2020-06-14 04:20:52 +00:00
foreach ( ProfileItem ProfileToRemove in ProfilesToRemove )
{
try
{
File . Delete ( ProfileToRemove . SavedProfileIconCacheFilename ) ;
2021-08-27 09:15:53 +00:00
File . Delete ( ProfileToRemove . WallpaperBitmapFilename ) ;
2020-06-14 04:20:52 +00:00
}
2021-02-10 09:40:22 +00:00
catch ( UnauthorizedAccessException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile2: DisplayMagician doesn't have permissions to delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename}." ) ;
}
catch ( ArgumentException ex )
2020-06-14 04:20:52 +00:00
{
2021-02-10 09:40:22 +00:00
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile2: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} due to an invalid argument." ) ;
}
catch ( PathTooLongException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile2: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the path is too long." ) ;
}
catch ( DirectoryNotFoundException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile2: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there." ) ;
2020-06-14 04:20:52 +00:00
}
}
// Remove the Profile from the list.
2021-02-14 09:42:48 +00:00
int numRemoved = _allProfiles . RemoveAll ( item = > item . Name . Equals ( profileName ) ) ;
2020-06-14 04:20:52 +00:00
if ( numRemoved = = 1 )
{
SaveProfiles ( ) ;
2021-03-04 22:12:27 +00:00
IsPossibleRefresh ( ) ;
2020-06-14 04:20:52 +00:00
return true ;
}
else if ( numRemoved = = 0 )
return false ;
else
throw new ProfileRepositoryException ( ) ;
}
2021-02-14 09:42:48 +00:00
public static bool RemoveProfile ( uint profileId )
2020-06-14 04:20:52 +00:00
{
2021-02-14 09:42:48 +00:00
if ( profileId = = 0 )
2020-06-14 04:20:52 +00:00
return false ;
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/RemoveProfile3: Removing profile wih profileId {profileId} if it exists in our profile repository" ) ;
2020-06-14 04:20:52 +00:00
// Remove the Profile Icons from the Cache
2021-02-14 09:42:48 +00:00
List < ProfileItem > ProfilesToRemove = _allProfiles . FindAll ( item = > item . UUID . Equals ( profileId ) ) ;
2020-06-14 04:20:52 +00:00
foreach ( ProfileItem ProfileToRemove in ProfilesToRemove )
{
try
{
File . Delete ( ProfileToRemove . SavedProfileIconCacheFilename ) ;
2021-08-27 09:15:53 +00:00
File . Delete ( ProfileToRemove . WallpaperBitmapFilename ) ;
2020-06-14 04:20:52 +00:00
}
2021-02-10 09:40:22 +00:00
catch ( UnauthorizedAccessException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile3: DisplayMagician doesn't have permissions to delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename}." ) ;
}
catch ( ArgumentException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile3: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} due to an invalid argument." ) ;
}
catch ( PathTooLongException ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile3: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the path is too long." ) ;
}
catch ( DirectoryNotFoundException ex )
2020-06-14 04:20:52 +00:00
{
2021-02-10 09:40:22 +00:00
SharedLogger . logger . Error ( ex , $"ProfileRepository/RemoveProfile3: DisplayMagician can't delete the cached Profile Icon {ProfileToRemove.SavedProfileIconCacheFilename} as the parent folder isn't there." ) ;
2020-06-14 04:20:52 +00:00
}
}
// Remove the Profile from the list.
2021-02-14 09:42:48 +00:00
int numRemoved = _allProfiles . RemoveAll ( item = > item . UUID . Equals ( profileId ) ) ;
2020-06-14 04:20:52 +00:00
if ( numRemoved = = 1 )
{
SaveProfiles ( ) ;
2021-03-04 22:12:27 +00:00
IsPossibleRefresh ( ) ;
2020-06-14 04:20:52 +00:00
return true ;
2021-08-24 08:37:32 +00:00
}
2020-06-14 04:20:52 +00:00
else if ( numRemoved = = 0 )
return false ;
else
throw new ProfileRepositoryException ( ) ;
}
2021-09-07 09:26:42 +00:00
public static bool ContainsProfile ( ProfileItem profile )
2020-06-14 04:20:52 +00:00
{
2021-09-07 09:26:42 +00:00
if ( ! ( profile is ProfileItem ) )
2020-06-14 04:20:52 +00:00
return false ;
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile: Checking if our profile repository contains a profile called {profile.Name}" ) ;
2021-02-14 09:42:48 +00:00
2020-06-14 04:20:52 +00:00
foreach ( ProfileItem testProfile in _allProfiles )
{
2021-09-07 09:26:42 +00:00
if ( testProfile . Equals ( profile ) )
2021-09-06 10:08:22 +00:00
{
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile: Our profile repository does contain a profile called {profile.Name}" ) ;
2021-09-06 10:08:22 +00:00
return true ;
2021-09-07 09:26:42 +00:00
}
2020-06-14 04:20:52 +00:00
}
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile: Our profile repository doesn't contain a profile called {profile.Name}" ) ;
2020-06-14 04:20:52 +00:00
return false ;
}
public static bool ContainsProfile ( string ProfileNameOrId )
{
if ( String . IsNullOrWhiteSpace ( ProfileNameOrId ) )
return false ;
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile2: Checking if our profile repository contains a profile with UUID or Name {ProfileNameOrId}" ) ;
2020-06-15 09:57:46 +00:00
if ( ProfileItem . IsValidUUID ( ProfileNameOrId ) )
2020-06-14 04:20:52 +00:00
foreach ( ProfileItem testProfile in _allProfiles )
{
if ( testProfile . UUID . Equals ( ProfileNameOrId ) )
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile2: Our profile repository does contain a profile with UUID {ProfileNameOrId}" ) ;
2020-06-14 04:20:52 +00:00
return true ;
2021-02-14 09:42:48 +00:00
}
2021-08-24 08:37:32 +00:00
2020-06-14 04:20:52 +00:00
}
else
foreach ( ProfileItem testProfile in _allProfiles )
{
if ( testProfile . Name . Equals ( ProfileNameOrId ) )
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile2: Our profile repository does contain a profile with Name {ProfileNameOrId}" ) ;
2020-06-14 04:20:52 +00:00
return true ;
2021-02-14 09:42:48 +00:00
}
2021-08-24 08:37:32 +00:00
2020-06-14 04:20:52 +00:00
}
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile2: Our profile repository doesn't contain a profile with a UUID or Name {ProfileNameOrId}" ) ;
2020-06-14 04:20:52 +00:00
return false ;
}
2021-09-06 10:08:22 +00:00
public static bool ContainsCurrentProfile ( out string savedProfileName )
2020-10-09 03:27:59 +00:00
{
2021-09-06 10:08:22 +00:00
savedProfileName = "" ;
2020-10-09 03:27:59 +00:00
if ( ! ( _currentProfile is ProfileItem ) )
2021-09-06 10:08:22 +00:00
{
2020-10-09 03:27:59 +00:00
return false ;
2021-09-06 10:08:22 +00:00
}
2020-10-09 03:27:59 +00:00
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsCurrentProfile: Checking if our profile repository contains the display profile currently in use" ) ;
2020-10-09 03:27:59 +00:00
foreach ( ProfileItem testProfile in _allProfiles )
{
2021-09-07 09:26:42 +00:00
if ( testProfile . Equals ( _currentProfile ) )
2021-09-06 10:08:22 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsProfile: Our profile repository does contain a profile called {testProfile.Name}" ) ;
savedProfileName = testProfile . Name ;
return true ;
}
2020-10-09 03:27:59 +00:00
}
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ContainsCurrentProfile: Our profile repository doesn't contain the display profile currently in use" ) ;
2020-10-09 03:27:59 +00:00
return false ;
}
2020-06-14 04:20:52 +00:00
public static ProfileItem GetProfile ( string ProfileNameOrId )
{
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/GetProfile: Finding and returning {ProfileNameOrId} if it exists in our profile repository" ) ;
2020-06-14 04:20:52 +00:00
if ( String . IsNullOrWhiteSpace ( ProfileNameOrId ) )
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Error ( $"ProfileRepository/GetProfile: Profile to get was empty or only whitespace" ) ;
2020-06-14 04:20:52 +00:00
return null ;
2021-02-14 09:42:48 +00:00
}
2021-08-24 08:37:32 +00:00
2020-06-14 04:20:52 +00:00
2020-06-15 09:57:46 +00:00
if ( ProfileItem . IsValidUUID ( ProfileNameOrId ) )
2020-06-14 04:20:52 +00:00
foreach ( ProfileItem testProfile in _allProfiles )
{
if ( testProfile . UUID . Equals ( ProfileNameOrId ) )
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/GetProfile: Returning profile with UUID {ProfileNameOrId}" ) ;
2020-06-14 04:20:52 +00:00
return testProfile ;
2021-02-14 09:42:48 +00:00
}
2021-08-24 08:37:32 +00:00
2020-06-14 04:20:52 +00:00
}
else
foreach ( ProfileItem testProfile in _allProfiles )
{
if ( testProfile . Name . Equals ( ProfileNameOrId ) )
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/GetProfile: Returning profile with Name {ProfileNameOrId}" ) ;
2020-06-14 04:20:52 +00:00
return testProfile ;
2021-02-14 09:42:48 +00:00
}
2021-08-24 08:37:32 +00:00
2020-06-14 04:20:52 +00:00
}
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/GetProfile: Didn't match any profiles with UUD or Name {ProfileNameOrId}" ) ;
2020-06-14 04:20:52 +00:00
return null ;
}
public static bool RenameProfile ( ProfileItem profile , string renamedName )
{
if ( ! ( profile is ProfileItem ) )
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Error ( $"ProfileRepository/RenameProfile: Profile to rename was empty or only whitespace" ) ;
2020-06-14 04:20:52 +00:00
return false ;
2021-02-14 09:42:48 +00:00
}
2021-08-24 08:37:32 +00:00
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/RenameProfile: Attempting to rename profile {profile.Name} to {renamedName}" ) ;
2020-06-14 04:20:52 +00:00
if ( ! IsValidFilename ( renamedName ) )
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Error ( $"ProfileRepository/RenameProfile: The name the user wanted to renamed to profile to is not a valid filename" ) ;
2020-06-14 04:20:52 +00:00
return false ;
2021-02-14 09:42:48 +00:00
}
2021-08-24 08:37:32 +00:00
2020-06-14 04:20:52 +00:00
profile . Name = GetValidFilename ( renamedName ) ;
2021-03-05 01:44:53 +00:00
IsPossibleRefresh ( ) ;
2020-06-14 04:20:52 +00:00
// If it's been added to the list of AllProfiles
// then we also need to reproduce the Icons
if ( ContainsProfile ( profile ) )
{
// Save the Profiles JSON as it's different now
SaveProfiles ( ) ;
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/RenameProfile: The profile was successfully renamed from {profile.Name} to {renamedName}" ) ;
2020-06-14 04:20:52 +00:00
return true ;
}
else
2021-02-14 09:42:48 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/RenameProfile: The profile was not renamed from {profile.Name} to {renamedName}" ) ;
2020-06-14 04:20:52 +00:00
return false ;
2021-02-14 09:42:48 +00:00
}
2021-08-30 00:51:44 +00:00
}
2021-03-04 22:12:27 +00:00
2021-03-26 08:54:45 +00:00
public static void UpdateActiveProfile ( )
{
2021-06-22 09:05:24 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/UpdateActiveProfile: Updating the profile currently active (in use now)." ) ;
2021-09-07 09:26:42 +00:00
ProfileItem profile ;
2021-07-24 04:05:38 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/UpdateActiveProfile: Attempting to access configuration through NVIDIA, then AMD, then Windows CCD interfaces, in that order." ) ;
2021-08-22 06:58:08 +00:00
if ( _currentVideoMode = = VIDEO_MODE . NVIDIA )
2021-07-24 04:05:38 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/UpdateActiveProfile: NVIDIA NVAPI Driver is installed, so using that for this display profile." ) ;
2021-09-07 09:26:42 +00:00
profile = new ProfileItem
2021-07-24 04:05:38 +00:00
{
Name = "Current NVIDIA Display Profile" ,
2021-09-07 09:26:42 +00:00
VideoMode = VIDEO_MODE . NVIDIA
} ;
2021-07-24 04:05:38 +00:00
}
2021-08-22 06:58:08 +00:00
else if ( _currentVideoMode = = VIDEO_MODE . AMD )
2021-07-24 04:05:38 +00:00
{
SharedLogger . logger . Debug ( $"ProfileRepository/UpdateActiveProfile: NVIDIA is not installed but the AMD ADL Driver IS installed, so using that for this display profile." ) ;
2021-09-07 09:26:42 +00:00
profile = new ProfileItem
2021-06-20 05:13:21 +00:00
{
2021-08-24 08:37:32 +00:00
Name = "Current AMD Display Profile" ,
2021-09-07 09:26:42 +00:00
VideoMode = VIDEO_MODE . AMD
2021-06-25 09:52:02 +00:00
} ;
2021-06-19 22:45:45 +00:00
}
2021-09-06 10:08:22 +00:00
else
2021-08-30 00:51:44 +00:00
{
2021-09-07 09:26:42 +00:00
profile = new ProfileItem
2021-06-25 09:52:02 +00:00
{
2021-07-24 04:05:38 +00:00
Name = "Current Windows Display Profile" ,
2021-09-07 09:26:42 +00:00
VideoMode = VIDEO_MODE . WINDOWS
2021-06-25 09:52:02 +00:00
} ;
2021-09-07 09:26:42 +00:00
}
2022-01-29 02:25:52 +00:00
int totalDelay = 0 ;
if ( _pauseReadsUntilChangeCompleted )
{
SharedLogger . logger . Warn ( $"ProfileRepository/UpdateActiveProfile: Pausing updating display settings as a display change is currently taking place." ) ;
while ( ! _pauseReadsUntilChangeCompleted )
{
Task . Delay ( 200 ) ;
totalDelay + = 200 ;
if ( totalDelay > 10000 )
{
SharedLogger . logger . Warn ( $"ProfileRepository/UpdateActiveProfile: Timeout while pausing updating display settingss as it took longer than 10 seconds." ) ;
break ;
}
}
SharedLogger . logger . Trace ( $"ProfileRepository/UpdateActiveProfile: Paused updating display settings for {totalDelay} milliseconds." ) ;
}
2022-02-11 20:18:03 +00:00
// Force explorer to update the TaskBar settings just in case they were moved
//ShellHelper.TellShellToWriteSettings();
//WinLibrary.RefreshTaskBars();
2021-09-07 09:26:42 +00:00
profile . CreateProfileFromCurrentDisplaySettings ( ) ;
if ( _profilesLoaded & & _allProfiles . Count > 0 )
{
foreach ( ProfileItem loadedProfile in ProfileRepository . AllProfiles )
2021-03-26 08:54:45 +00:00
{
2021-09-07 09:26:42 +00:00
if ( loadedProfile . Equals ( profile ) )
2021-03-26 08:54:45 +00:00
{
2021-09-07 09:26:42 +00:00
_currentProfile = loadedProfile ;
SharedLogger . logger . Debug ( $"ProfileRepository/UpdateActiveProfile: The {loadedProfile.VideoMode.ToString(" G ")} profile '{loadedProfile.Name}' is currently active (in use now)." ) ;
return ;
2021-03-26 08:54:45 +00:00
}
}
}
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/UpdateActiveProfile: The current profile is a new profile that doesn't already exist in the Profile Repository." ) ;
_currentProfile = profile ;
2021-03-26 08:54:45 +00:00
}
2020-06-14 04:20:52 +00:00
public static ProfileItem GetActiveProfile ( )
{
if ( ! ( _currentProfile is ProfileItem ) )
return null ;
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/GetActiveProfile: Retrieving the currently active profile." ) ;
2020-06-14 04:20:52 +00:00
return _currentProfile ;
}
public static bool IsActiveProfile ( ProfileItem profile )
{
2021-08-25 09:45:40 +00:00
SharedLogger . logger . Trace ( $"ProfileRepository/IsActiveProfile: Checking whether the profile {profile.Name} is the currently active profile." ) ;
2021-09-06 10:08:22 +00:00
if ( _currentProfile = = null )
{
SharedLogger . logger . Error ( $"ProfileRepository/IsActiveProfile: The current profile {profile.Name} is null, so can't test it against anything." ) ;
return false ;
}
if ( profile = = null )
{
2021-08-25 09:45:40 +00:00
SharedLogger . logger . Error ( $"ProfileRepository/IsActiveProfile: The requested profile {profile.Name} is null. Not changing anything, and reporting an error" ) ;
2020-06-14 04:20:52 +00:00
return false ;
2021-09-04 04:32:42 +00:00
}
2021-08-25 09:45:40 +00:00
2021-09-07 09:26:42 +00:00
if ( profile . Equals ( _currentProfile ) )
2021-08-24 08:37:32 +00:00
{
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/IsActiveProfile: The profile {profile.Name} is the currently active profile." ) ;
return true ;
2021-08-24 08:37:32 +00:00
}
else
{
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/IsActiveProfile: The profile {profile.Name} is the not currently active profile." ) ;
2021-08-24 08:37:32 +00:00
return false ;
}
2020-06-14 04:20:52 +00:00
}
2020-10-10 04:50:27 +00:00
2020-06-14 04:20:52 +00:00
private static bool LoadProfiles ( )
{
2021-02-14 09:42:48 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/LoadProfiles: Loading profiles from {_profileStorageJsonFileName} into the Profile Repository" ) ;
2020-06-14 04:20:52 +00:00
if ( File . Exists ( _profileStorageJsonFileName ) )
{
2021-02-20 09:03:19 +00:00
string json = "" ;
try
{
json = File . ReadAllText ( _profileStorageJsonFileName , Encoding . Unicode ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/LoadProfiles: Tried to read the JSON file {_profileStorageJsonFileName} to memory but File.ReadAllTextthrew an exception." ) ;
}
2020-06-14 04:20:52 +00:00
2021-10-09 05:42:50 +00:00
// Migrate any previous entries to the latest version of the file format to the latest one
json = MigrateJsonToLatestVersion ( json ) ;
2020-06-14 04:20:52 +00:00
if ( ! string . IsNullOrWhiteSpace ( json ) )
{
2021-10-09 05:42:50 +00:00
List < string > jsonErrors = new List < string > ( ) ;
2021-02-14 09:42:48 +00:00
try
2020-06-14 04:20:52 +00:00
{
2021-10-09 05:42:50 +00:00
JsonSerializerSettings mySerializerSettings = new JsonSerializerSettings
2020-06-14 04:20:52 +00:00
{
2022-01-21 07:37:56 +00:00
MissingMemberHandling = MissingMemberHandling . Ignore ,
2021-09-19 08:56:58 +00:00
NullValueHandling = NullValueHandling . Include ,
2020-06-14 04:20:52 +00:00
DefaultValueHandling = DefaultValueHandling . Include ,
2020-06-15 09:57:46 +00:00
TypeNameHandling = TypeNameHandling . Auto ,
2021-09-19 09:00:47 +00:00
ObjectCreationHandling = ObjectCreationHandling . Replace ,
2021-10-09 05:42:50 +00:00
Error = delegate ( object sender , Newtonsoft . Json . Serialization . ErrorEventArgs args )
{
jsonErrors . Add ( $"JSON.net Error: {args.ErrorContext.Error.Source}:{args.ErrorContext.Error.StackTrace} - {args.ErrorContext.Error.Message} | InnerException:{args.ErrorContext.Error.InnerException.Source}:{args.ErrorContext.Error.InnerException.StackTrace} - {args.ErrorContext.Error.InnerException.Message}" ) ;
args . ErrorContext . Handled = true ;
} ,
} ;
_allProfiles = JsonConvert . DeserializeObject < List < ProfileItem > > ( json , mySerializerSettings ) ;
2022-01-21 07:37:56 +00:00
}
catch ( JsonReaderException ex )
{
// If there is a error in the JSON format
if ( ex . HResult = = - 2146233088 )
{
MessageBox . Show ( $"The Display Profiles file {_profileStorageJsonFileName} contains a syntax error. Please check the file for correctness with a JSON validator." , "Error loading the Display Profiles" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
SharedLogger . logger . Error ( ex , $"ProfileRepository/LoadProfiles: JSONReaderException - The Display Profiles file {_profileStorageJsonFileName} contains a syntax error. Please check the file for correctness with a JSON validator." ) ;
}
else
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/LoadProfiles: JSONReaderException while trying to process the Profiles json data file {_profileStorageJsonFileName} but JsonConvert threw an exception." ) ;
}
2020-06-14 04:20:52 +00:00
}
2021-02-14 09:42:48 +00:00
catch ( Exception ex )
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/LoadProfiles: Tried to parse the JSON in the {_profileStorageJsonFileName} but the JsonConvert threw an exception." ) ;
2020-06-14 04:20:52 +00:00
}
2021-10-09 05:42:50 +00:00
// If we have any JSON.net errors, then we need to records them in the logs
if ( jsonErrors . Count > 0 )
{
foreach ( string jsonError in jsonErrors )
{
SharedLogger . logger . Error ( $"ProfileRepository/LoadProfiles: {jsonErrors}" ) ;
}
}
// We need to try and
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/LoadProfiles: Finding the current profile in the Profile Repository" ) ;
2021-09-07 10:08:54 +00:00
// Go through all the profiles and set up the needed structures (such as the Screens list)
2021-06-27 01:53:00 +00:00
// and check if the current profile is used
2021-09-07 10:08:54 +00:00
/ * foreach ( ProfileItem loadedProfile in _allProfiles )
2020-06-14 04:20:52 +00:00
{
2021-09-07 10:08:54 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/LoadProfiles: Profile {loadedProfile.Name} is a NVIDIA Profile" ) ;
loadedProfile . PerformPostLoadingTasks ( ) ;
* //*if (ProfileRepository.IsActiveProfile(loadedProfile))
_currentProfile = loadedProfile ; * //*
2021-09-07 09:26:42 +00:00
} * /
2020-10-26 01:30:00 +00:00
// Sort the profiles alphabetically
_allProfiles . Sort ( ) ;
2020-06-14 04:20:52 +00:00
}
2021-02-15 07:33:46 +00:00
else
{
SharedLogger . logger . Debug ( $"ProfileRepository/LoadProfiles: The {_profileStorageJsonFileName} profile JSON file exists but is empty! So we're going to treat it as if it didn't exist." ) ;
2021-09-07 09:26:42 +00:00
//UpdateActiveProfile();
2021-02-15 07:33:46 +00:00
}
2021-02-10 09:40:22 +00:00
}
else
2020-06-14 04:20:52 +00:00
{
// If we get here, then we don't have any profiles saved!
// So we gotta start from scratch
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/LoadProfiles: Couldn't find the {_profileStorageJsonFileName} profile JSON file that contains the Profiles" ) ;
2021-09-07 09:26:42 +00:00
//UpdateActiveProfile();
2020-06-14 04:20:52 +00:00
}
2020-08-07 03:59:23 +00:00
_profilesLoaded = true ;
2021-03-07 04:11:46 +00:00
2021-09-07 09:26:42 +00:00
// Update the current active profile
2021-10-30 23:50:57 +00:00
//UpdateActiveProfile();
2021-03-07 04:11:46 +00:00
IsPossibleRefresh ( ) ;
2020-06-14 04:20:52 +00:00
return true ;
2021-10-09 05:42:50 +00:00
}
public static string MigrateJsonToLatestVersion ( string json )
{
bool changedJson = false ;
JArray root = new JArray ( ) ;
try
{
SharedLogger . logger . Trace ( $"ProfileRepository/MigrateJsonToLatestVersion: Processing the Profiles json data to migrate any older feature to the latest version." ) ;
root = JArray . Parse ( json ) ;
}
catch ( JsonReaderException ex )
{
2022-01-21 07:37:56 +00:00
// If there is a error in the JSON format
if ( ex . HResult = = - 2146233088 )
{
MessageBox . Show ( "The Display Profiles file contains a syntax error. Please check the file for correctness with a JSON validator." , "Error loading the Display Profiles" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
SharedLogger . logger . Error ( ex , $"ProfileRepository/MigrateJsonToLatestVersion: JSONReaderException - The Display Profiles file contains a syntax error. Please check the file for correctness with a JSON validator." ) ;
}
else
{
SharedLogger . logger . Error ( ex , $"ProfileRepository/MigrateJsonToLatestVersion: JSONReaderException while trying to process the Profiles json data to migrate any older feature to the latest version." ) ;
}
2021-10-09 05:42:50 +00:00
}
catch ( Exception ex )
{
2022-01-21 07:37:56 +00:00
SharedLogger . logger . Error ( ex , $"ProfileRepository/MigrateJsonToLatestVersion: Exception while trying to process the Profiles json data to migrate any older feature to the latest version." ) ;
2021-10-09 05:42:50 +00:00
}
2022-01-21 07:37:56 +00:00
// We do the actual change we were trying to do
2021-10-09 05:42:50 +00:00
try
{
2022-03-24 09:36:42 +00:00
// Nothing to patch at the moment!
2021-10-15 20:37:52 +00:00
}
catch ( JsonReaderException ex )
{
2022-01-30 07:24:36 +00:00
SharedLogger . logger . Error ( ex , $"ProfileRepository/MigrateJsonToLatestVersion: JSONReaderException while trying to process the Profiles json data to migrate any older feature to the latest version." ) ;
2021-10-15 20:37:52 +00:00
}
catch ( Exception ex )
{
2022-01-30 07:24:36 +00:00
SharedLogger . logger . Error ( ex , $"ProfileRepository/MigrateJsonToLatestVersion: Exception while trying to process the Profiles json data to migrate any older feature to the latest version." ) ;
2021-10-15 20:37:52 +00:00
}
2021-10-09 05:42:50 +00:00
// Now write the changed json to the json string but only if we've changed something
if ( changedJson )
{
json = root . ToString ( Formatting . Indented ) ;
2022-01-22 21:33:14 +00:00
if ( ! string . IsNullOrWhiteSpace ( json ) )
{
SharedLogger . logger . Debug ( $"ProfileRepository/SaveProfiles: Saving the profile repository to the {_profileStorageJsonFileName}." ) ;
File . WriteAllText ( _profileStorageJsonFileName , json , Encoding . Unicode ) ;
}
2021-10-09 05:42:50 +00:00
}
return json ;
2020-06-14 04:20:52 +00:00
}
public static bool SaveProfiles ( )
{
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/SaveProfiles: Attempting to save the profiles repository to the {AppProfileStoragePath}." ) ;
2020-06-14 04:20:52 +00:00
2020-08-18 22:16:04 +00:00
if ( ! Directory . Exists ( AppProfileStoragePath ) )
2020-06-14 04:20:52 +00:00
{
try
{
2020-08-18 22:16:04 +00:00
Directory . CreateDirectory ( AppProfileStoragePath ) ;
2020-06-14 04:20:52 +00:00
}
2021-02-10 09:40:22 +00:00
catch ( UnauthorizedAccessException ex )
2020-06-14 04:20:52 +00:00
{
2021-02-10 09:40:22 +00:00
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/SaveProfiles: DisplayMagician doesn't have permissions to create the Profiles storage folder {AppProfileStoragePath}." ) ;
}
catch ( ArgumentException ex )
{
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/SaveProfiles: DisplayMagician can't create the Profiles storage folder {AppProfileStoragePath} due to an invalid argument." ) ;
}
catch ( PathTooLongException ex )
{
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/SaveProfiles: DisplayMagician can't create the Profiles storage folder {AppProfileStoragePath} as the path is too long." ) ;
}
catch ( DirectoryNotFoundException ex )
{
SharedLogger . logger . Fatal ( ex , $"ProfileRepository/SaveProfiles: DisplayMagician can't create the Profiles storage folder {AppProfileStoragePath} as the parent folder isn't there." ) ;
2020-06-14 04:20:52 +00:00
}
}
2021-02-15 07:33:46 +00:00
else
{
SharedLogger . logger . Debug ( $"ProfileRepository/SaveProfiles: Profiles folder {AppProfileStoragePath} exists." ) ;
}
2021-10-09 05:42:50 +00:00
List < string > jsonErrors = new List < string > ( ) ;
2020-06-14 04:20:52 +00:00
try
{
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/SaveProfiles: Converting the objects to JSON format." ) ;
2021-10-09 05:42:50 +00:00
JsonSerializerSettings mySerializerSettings = new JsonSerializerSettings
2020-06-14 04:20:52 +00:00
{
NullValueHandling = NullValueHandling . Include ,
2021-09-19 08:56:58 +00:00
//NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling . Include ,
//DefaultValueHandling = DefaultValueHandling.Ignore,
TypeNameHandling = TypeNameHandling . Auto ,
MissingMemberHandling = MissingMemberHandling . Error ,
ObjectCreationHandling = ObjectCreationHandling . Replace ,
2021-10-09 05:42:50 +00:00
Error = delegate ( object sender , Newtonsoft . Json . Serialization . ErrorEventArgs args )
{
jsonErrors . Add ( $"JSON.net Error: {args.ErrorContext.Error.Source}:{args.ErrorContext.Error.StackTrace} - {args.ErrorContext.Error.Message} | InnerException:{args.ErrorContext.Error.InnerException.Source}:{args.ErrorContext.Error.InnerException.StackTrace} - {args.ErrorContext.Error.InnerException.Message}" ) ;
args . ErrorContext . Handled = true ;
} ,
} ;
var json = JsonConvert . SerializeObject ( _allProfiles , Formatting . Indented , mySerializerSettings ) ;
2020-06-14 04:20:52 +00:00
if ( ! string . IsNullOrWhiteSpace ( json ) )
{
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/SaveProfiles: Saving the profile repository to the {_profileStorageJsonFileName}." ) ;
2020-06-14 04:20:52 +00:00
File . WriteAllText ( _profileStorageJsonFileName , json , Encoding . Unicode ) ;
return true ;
}
}
catch ( Exception ex )
{
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Error ( ex , $"ProfileRepository/SaveProfiles: Unable to save the profile repository to the {_profileStorageJsonFileName}." ) ;
2020-06-14 04:20:52 +00:00
}
2021-10-09 05:42:50 +00:00
// If we have any JSON.net errors, then we need to records them in the logs
if ( jsonErrors . Count > 0 )
{
foreach ( string jsonError in jsonErrors )
{
SharedLogger . logger . Error ( $"ProfileRepository/SaveProfiles: {jsonErrors}" ) ;
}
}
2020-06-14 04:20:52 +00:00
return false ;
}
private static void SaveProfileIconToCache ( ProfileItem profile )
{
// Work out the name of the Profile we'll save.
2020-10-06 10:49:10 +00:00
profile . SavedProfileIconCacheFilename = System . IO . Path . Combine ( AppProfileStoragePath , string . Concat ( @"profile-" , profile . UUID , @".ico" ) ) ;
2020-06-14 04:20:52 +00:00
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/SaveProfileIconToCache: Attempting to save the profile icon {profile.SavedProfileIconCacheFilename} to the {AppProfileStoragePath} folder" ) ;
2020-06-14 04:20:52 +00:00
MultiIcon ProfileIcon ;
try
{
ProfileIcon = profile . ProfileIcon . ToIcon ( ) ;
ProfileIcon . Save ( profile . SavedProfileIconCacheFilename , MultiIconFormat . ICO ) ;
}
catch ( Exception ex )
{
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Warn ( ex , $"ProfileRepository/SaveProfileIconToCache: Exception saving the profile icon {profile.SavedProfileIconCacheFilename} to the {AppProfileStoragePath} folder. Using the default DisplayMagician icon instead" ) ;
2020-12-02 08:11:23 +00:00
// If we fail to create an icon based on the Profile, then we use the standard DisplayMagician profile one.
2020-06-14 04:20:52 +00:00
// Which is created on program startup.
2020-12-02 08:11:23 +00:00
File . Copy ( AppDisplayMagicianIconFilename , profile . SavedProfileIconCacheFilename ) ;
2020-06-14 04:20:52 +00:00
}
}
2021-03-04 21:34:51 +00:00
public static void IsPossibleRefresh ( )
2021-02-28 08:24:12 +00:00
{
// We need to refresh the cached answer
// Get the list of connected devices
2021-10-30 23:50:57 +00:00
//ConnectedDisplayIdentifiers = GetAllConnectedDisplayIdentifiers();
2021-03-07 04:11:46 +00:00
2021-02-28 08:24:12 +00:00
if ( _profilesLoaded & & _allProfiles . Count > 0 )
{
2022-01-29 02:25:52 +00:00
int totalDelay = 0 ;
if ( _pauseReadsUntilChangeCompleted )
{
SharedLogger . logger . Warn ( $"ProfileRepository/IsPossibleRefresh: Pausing refreshing display profile possibility as a display change is currently taking place." ) ;
while ( ! _pauseReadsUntilChangeCompleted )
{
Task . Delay ( 200 ) ;
totalDelay + = 200 ;
if ( totalDelay > 10000 )
{
SharedLogger . logger . Warn ( $"ProfileRepository/IsPossibleRefresh: Timeout while refreshing display profile possibility as it took longer than 10 seconds." ) ;
break ;
}
}
SharedLogger . logger . Trace ( $"ProfileRepository/IsPossibleRefresh: Paused refreshing display profile possibility for {totalDelay} milliseconds." ) ;
}
2021-03-05 01:44:53 +00:00
2021-02-28 08:24:12 +00:00
foreach ( ProfileItem loadedProfile in AllProfiles )
2021-03-07 04:11:46 +00:00
loadedProfile . RefreshPossbility ( ) ;
2021-02-28 08:24:12 +00:00
}
}
2020-10-06 10:49:10 +00:00
2021-07-24 04:05:38 +00:00
public static List < string > GetAllConnectedDisplayIdentifiers ( )
2020-06-14 04:20:52 +00:00
{
2022-01-29 02:25:52 +00:00
int totalDelay = 0 ;
if ( _pauseReadsUntilChangeCompleted )
{
SharedLogger . logger . Warn ( $"ProfileRepository/GetAllConnectedDisplayIdentifiers: Pausing checking for all connected display identifiers as a display change is currently taking place." ) ;
while ( ! _pauseReadsUntilChangeCompleted )
{
Task . Delay ( 200 ) ;
totalDelay + = 200 ;
if ( totalDelay > 10000 )
{
SharedLogger . logger . Warn ( $"ProfileRepository/GetAllConnectedDisplayIdentifiers: Timeout while pausing checking for all connected display identifiers as it took longer than 10 seconds." ) ;
break ;
}
}
SharedLogger . logger . Trace ( $"ProfileRepository/GetAllConnectedDisplayIdentifiers: Paused checking for all connected display identifiers for {totalDelay} milliseconds." ) ;
}
2021-09-02 01:47:28 +00:00
if ( _currentVideoMode = = VIDEO_MODE . NVIDIA & & NVIDIALibrary . GetLibrary ( ) . IsInstalled )
2021-06-07 09:41:48 +00:00
{
2021-07-24 04:05:38 +00:00
return NVIDIALibrary . GetLibrary ( ) . GetAllConnectedDisplayIdentifiers ( ) ;
2020-10-10 04:50:27 +00:00
}
2021-09-02 01:47:28 +00:00
else if ( _currentVideoMode = = VIDEO_MODE . AMD & & AMDLibrary . GetLibrary ( ) . IsInstalled )
2021-02-07 01:42:17 +00:00
{
2021-07-24 04:05:38 +00:00
return AMDLibrary . GetLibrary ( ) . GetAllConnectedDisplayIdentifiers ( ) ;
2021-02-07 01:42:17 +00:00
}
2021-07-24 04:05:38 +00:00
else
2020-10-10 04:50:27 +00:00
{
2021-07-24 04:05:38 +00:00
return WinLibrary . GetLibrary ( ) . GetAllConnectedDisplayIdentifiers ( ) ;
2020-10-06 07:59:59 +00:00
}
}
2020-10-02 05:10:34 +00:00
2021-07-24 04:05:38 +00:00
public static List < string > GetCurrentDisplayIdentifiers ( )
2020-10-06 07:59:59 +00:00
{
2022-01-29 02:25:52 +00:00
int totalDelay = 0 ;
if ( _pauseReadsUntilChangeCompleted )
{
SharedLogger . logger . Warn ( $"ProfileRepository/GetCurrentDisplayIdentifiers: Pausing checking for currently connected display identifiers as a display change is currently taking place." ) ;
while ( ! _pauseReadsUntilChangeCompleted )
{
Task . Delay ( 200 ) ;
totalDelay + = 200 ;
if ( totalDelay > 10000 )
{
SharedLogger . logger . Warn ( $"ProfileRepository/GetCurrentDisplayIdentifiers: Timeout while pausing checking for currently connected display identifiers as it took longer than 10 seconds." ) ;
break ;
}
}
SharedLogger . logger . Trace ( $"ProfileRepository/GetCurrentDisplayIdentifiers: Paused checking for currently connected display identifiers for {totalDelay} milliseconds." ) ;
}
2021-09-02 01:47:28 +00:00
if ( _currentVideoMode = = VIDEO_MODE . NVIDIA & & NVIDIALibrary . GetLibrary ( ) . IsInstalled )
2020-10-06 07:59:59 +00:00
{
2021-10-30 23:50:57 +00:00
return NVIDIALibrary . GetLibrary ( ) . CurrentDisplayIdentifiers ;
2020-10-10 02:18:30 +00:00
}
2021-09-02 01:47:28 +00:00
else if ( _currentVideoMode = = VIDEO_MODE . AMD & & AMDLibrary . GetLibrary ( ) . IsInstalled )
2021-02-07 01:42:17 +00:00
{
2021-10-30 23:50:57 +00:00
return AMDLibrary . GetLibrary ( ) . CurrentDisplayIdentifiers ;
2021-02-07 01:42:17 +00:00
}
2020-10-06 07:59:59 +00:00
else
2020-10-02 05:10:34 +00:00
{
2021-10-30 23:50:57 +00:00
return WinLibrary . GetLibrary ( ) . CurrentDisplayIdentifiers ;
2020-10-02 05:10:34 +00:00
}
2021-07-24 04:05:38 +00:00
}
2020-10-06 07:59:59 +00:00
2020-06-14 04:20:52 +00:00
public static bool IsValidFilename ( string testName )
{
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Trace ( $"ProfileRepository/IsValidFilename: Checking whether {testName} is a valid filename" ) ;
2020-10-06 10:49:10 +00:00
string strTheseAreInvalidFileNameChars = new string ( System . IO . Path . GetInvalidFileNameChars ( ) ) ;
2020-06-14 04:20:52 +00:00
Regex regInvalidFileName = new Regex ( "[" + Regex . Escape ( strTheseAreInvalidFileNameChars ) + "]" ) ;
2021-02-15 07:33:46 +00:00
if ( regInvalidFileName . IsMatch ( testName ) ) {
SharedLogger . logger . Trace ( $"ProfileRepository/IsValidFilename: {testName} is a valid filename" ) ;
return false ;
}
else
{
SharedLogger . logger . Debug ( $"ProfileRepository/IsValidFilename: {testName} isn't a valid filename as it contains one of these characters [" + Regex . Escape ( strTheseAreInvalidFileNameChars ) + "]" ) ;
return true ;
}
2020-06-14 04:20:52 +00:00
}
public static string GetValidFilename ( string uncheckedFilename )
{
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Trace ( $"ProfileRepository/GetValidFilename: Modifying filename {uncheckedFilename} to be a valid filename for this filesystem" ) ;
2020-10-06 10:49:10 +00:00
string invalid = new string ( System . IO . Path . GetInvalidFileNameChars ( ) ) + new string ( System . IO . Path . GetInvalidPathChars ( ) ) ;
2020-06-14 04:20:52 +00:00
foreach ( char c in invalid )
{
uncheckedFilename = uncheckedFilename . Replace ( c . ToString ( ) , "" ) ;
}
2021-02-15 07:33:46 +00:00
SharedLogger . logger . Trace ( $"ProfileRepository/GetValidFilename: Modified filename {uncheckedFilename} so it is a valid filename for this filesystem" ) ;
2020-06-14 04:20:52 +00:00
return uncheckedFilename ;
}
2021-08-27 05:53:32 +00:00
// ApplyProfile lives here so that the UI works.
public static ApplyProfileResult ApplyProfile ( ProfileItem profile )
{
SharedLogger . logger . Trace ( $"Program/ApplyProfile: Starting" ) ;
2021-08-27 08:57:02 +00:00
// We try to time the profile display swap
Stopwatch stopWatch = new Stopwatch ( ) ;
bool wasDisplayChangeSuccessful = true ;
2021-08-27 05:53:32 +00:00
if ( profile = = null )
{
2021-08-27 08:57:02 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ApplyProfile: The supplied profile is null! Can't be used." ) ;
2021-08-27 05:53:32 +00:00
return ApplyProfileResult . Error ;
}
try
{
2021-08-27 08:57:02 +00:00
// We start the timer just before we attempt the display change
stopWatch . Start ( ) ;
2022-01-29 02:25:52 +00:00
// We also set the lock to pause reads until the profile change has happened
_pauseReadsUntilChangeCompleted = true ;
2021-08-27 05:53:32 +00:00
// We try to swap profiles. The profiles have checking logic in them
2021-09-07 09:26:42 +00:00
if ( ! ( profile . SetActive ( ) ) )
2021-08-27 05:53:32 +00:00
{
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Error ( $"ProfileRepository/ApplyProfile: Error applying the {profile.VideoMode.ToString(" G ")} Profile!" ) ;
return ApplyProfileResult . Error ;
2021-08-27 05:53:32 +00:00
}
else
{
2021-09-07 09:26:42 +00:00
SharedLogger . logger . Trace ( $"ProfileRepository/ApplyProfile: Successfully applied the {profile.VideoMode.ToString(" G ")} Profile!" ) ;
2021-09-21 20:22:57 +00:00
return ApplyProfileResult . Successful ;
2022-01-29 02:25:52 +00:00
}
2021-08-27 05:53:32 +00:00
}
catch ( Exception ex )
{
2021-08-27 08:57:02 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/ApplyProfile: Failed to complete changing the Windows Display layout" ) ;
wasDisplayChangeSuccessful = false ;
return ApplyProfileResult . Error ;
}
finally
{
2021-09-01 22:42:26 +00:00
// If the applying path info worked, then we attempt to set the desktop background if needed
if ( profile . WallpaperMode . Equals ( Wallpaper . Mode . Apply ) & & ! String . IsNullOrWhiteSpace ( profile . WallpaperBitmapFilename ) )
2020-10-02 05:10:34 +00:00
{
2021-09-01 22:42:26 +00:00
if ( Wallpaper . Set ( profile . WallpaperBitmapFilename , profile . WallpaperStyle ) )
2020-10-02 05:10:34 +00:00
{
2021-08-30 00:51:44 +00:00
SharedLogger . logger . Trace ( $"Program/ApplyProfile: We attempted to set the desktop wallpaper to {profile.SavedProfileIconCacheFilename} using {profile.WallpaperStyle} style for profile {profile.Name}, and it worked!" ) ;
2021-08-30 00:35:52 +00:00
}
else
{
2021-08-30 00:51:44 +00:00
SharedLogger . logger . Warn ( $"Program/ApplyProfile: We attempted to set the desktop wallpaper to {profile.SavedProfileIconCacheFilename} using {profile.WallpaperStyle} style for profile {profile.Name}, and it failed :(" ) ;
2020-10-02 05:10:34 +00:00
}
2021-08-30 00:35:52 +00:00
}
2021-09-01 22:42:26 +00:00
else if ( profile . WallpaperMode . Equals ( Wallpaper . Mode . Clear ) )
{
if ( Wallpaper . Clear ( ) )
{
SharedLogger . logger . Trace ( $"Program/ApplyProfile: We attempted to clear the desktop wallpaper and it worked!" ) ;
}
else
{
SharedLogger . logger . Warn ( $"Program/ApplyProfile: We attempted to clear the desktop wallpaper and it failed :(" ) ;
}
}
2021-08-27 08:57:02 +00:00
// We stop the stop watch
stopWatch . Stop ( ) ;
2022-01-29 02:25:52 +00:00
_pauseReadsUntilChangeCompleted = false ;
2021-08-27 08:57:02 +00:00
// Get the elapsed time as a TimeSpan value.
TimeSpan ts = stopWatch . Elapsed ;
string result = "failed" ;
if ( wasDisplayChangeSuccessful )
2021-08-27 05:53:32 +00:00
{
2021-08-27 08:57:02 +00:00
result = "was successful" ;
2021-10-30 23:50:57 +00:00
ProfileRepository . UpdateActiveProfile ( ) ;
2021-08-27 05:53:32 +00:00
}
2021-08-27 08:57:02 +00:00
// Display the TimeSpan time and result.
SharedLogger . logger . Debug ( $"ProfileRepository/ApplyProfile: Display change attempt took {ts.Minutes}:{ts.Seconds}.{ts.Milliseconds} and {result}." ) ;
2021-08-27 05:53:32 +00:00
}
}
2021-09-02 01:47:28 +00:00
public static bool SetVideoCardMode ( FORCED_VIDEO_MODE forcedVideoMode = FORCED_VIDEO_MODE . DETECT )
{
_forcedVideoMode = forcedVideoMode ;
// This sets the order in which the different modes have been chosen.
// NVIDIA Video cards are the most common, so go first
if ( _forcedVideoMode = = FORCED_VIDEO_MODE . NVIDIA )
{
// We force the video mode to be NVIDIA
_currentVideoMode = VIDEO_MODE . NVIDIA ;
}
else if ( forcedVideoMode = = FORCED_VIDEO_MODE . AMD )
{
// We force the video mode to be AMD
_currentVideoMode = VIDEO_MODE . AMD ;
}
else if ( forcedVideoMode = = FORCED_VIDEO_MODE . WINDOWS )
{
// We force the video mode to be WINDOWS
_currentVideoMode = VIDEO_MODE . WINDOWS ;
}
else
{
// We do normal video library detection based on the video card!
2022-01-29 02:25:52 +00:00
// First we check if we need to pause
int totalDelay = 0 ;
if ( _pauseReadsUntilChangeCompleted )
{
SharedLogger . logger . Warn ( $"ProfileRepository/SetVideoCardMode: Pausing detecting video card PCI vendors as a display change is currently taking place." ) ;
while ( ! _pauseReadsUntilChangeCompleted )
{
Task . Delay ( 200 ) ;
totalDelay + = 200 ;
if ( totalDelay > 10000 )
{
SharedLogger . logger . Warn ( $"ProfileRepository/SetVideoCardMode: Timeout while detecting video card PCI vendors as it took longer than 10 seconds." ) ;
break ;
}
}
SharedLogger . logger . Trace ( $"ProfileRepository/SetVideoCardMode: Paused detecting video card PCI vendors for {totalDelay} milliseconds." ) ;
}
2021-09-02 01:47:28 +00:00
// Figure out the Video Cards and see what mode we want
// Get a list of all the PCI Vendor IDs
List < string > videoCardVendors = WinLibrary . GetLibrary ( ) . GetCurrentPCIVideoCardVendors ( ) ;
2021-09-22 08:37:10 +00:00
if ( NVIDIALibrary . GetLibrary ( ) . IsInstalled & & NVIDIALibrary . GetLibrary ( ) . PCIVendorIDs . All ( value = > videoCardVendors . Contains ( value ) ) )
2021-09-02 01:47:28 +00:00
{
// We detected a NVIDIA video card in the computer
_currentVideoMode = VIDEO_MODE . NVIDIA ;
}
2021-09-22 08:37:10 +00:00
else if ( AMDLibrary . GetLibrary ( ) . IsInstalled & & AMDLibrary . GetLibrary ( ) . PCIVendorIDs . All ( value = > videoCardVendors . Contains ( value ) ) )
2021-09-02 01:47:28 +00:00
{
// We detected an AMD video card in the computer
_currentVideoMode = VIDEO_MODE . AMD ;
}
else
{
// We fallback to the built-in Windows CCD drivers
_currentVideoMode = VIDEO_MODE . WINDOWS ;
}
}
if ( _currentVideoMode = = VIDEO_MODE . NVIDIA )
{
// Initialise the the NVIDIA NvAPI Library
try
{
SharedLogger . logger . Debug ( $"ProfileRepository/ProfileRepository: Initialising the NVIDIA NVAPI library." ) ;
nvidiaLibrary = new NVIDIALibrary ( ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ProfileRepository/ProfileRepository: Initialising NVIDIA NVAPI caused an exception." ) ;
return false ;
}
}
else if ( _currentVideoMode = = VIDEO_MODE . AMD )
{
// Initialise the the AMD ADL Library
try
{
SharedLogger . logger . Debug ( $"ProfileRepository/ProfileRepository: Initialising the AMD ADL library." ) ;
amdLibrary = new AMDLibrary ( ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ProfileRepository/ProfileRepository: Initialising AMD ADL caused an exception." ) ;
return false ;
}
}
return true ;
}
2020-06-14 04:20:52 +00:00
#endregion
}
[global::System.Serializable]
public class ProfileRepositoryException : Exception
{
public ProfileRepositoryException ( ) { }
public ProfileRepositoryException ( string message ) : base ( message ) { }
public ProfileRepositoryException ( string message , Exception inner ) : base ( message , inner ) { }
protected ProfileRepositoryException (
System . Runtime . Serialization . SerializationInfo info ,
System . Runtime . Serialization . StreamingContext context ) : base ( info , context ) { }
}
2021-08-27 05:53:32 +00:00
public class ApplyTopologyException : Exception
{
public ApplyTopologyException ( )
{ }
public ApplyTopologyException ( string message ) : base ( message )
{ }
public ApplyTopologyException ( string message , Exception innerException ) : base ( message , innerException )
{ }
public ApplyTopologyException ( SerializationInfo info , StreamingContext context ) : base ( info , context )
{ }
}
2020-06-14 04:20:52 +00:00
}
2020-10-06 07:59:59 +00:00