2017-02-26 19:23:31 +00:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2018-10-20 00:23:43 +00:00
using System.Windows.Forms ;
2020-12-20 07:42:04 +00:00
using DisplayMagicianShared.Resources ;
2018-10-20 00:13:40 +00:00
using Newtonsoft.Json ;
2020-05-09 13:02:07 +00:00
using System.Drawing ;
using System.Drawing.Imaging ;
2020-06-14 04:20:52 +00:00
using System.Text.RegularExpressions ;
2021-03-07 01:50:52 +00:00
using IWshRuntimeLibrary ;
2021-06-26 09:54:11 +00:00
using DisplayMagicianShared.AMD ;
2021-04-28 10:14:54 +00:00
//using WK.Libraries.HotkeyListenerNS;
2017-02-26 19:23:31 +00:00
2020-12-20 07:42:04 +00:00
namespace DisplayMagicianShared
2017-02-26 19:23:31 +00:00
{
2021-06-24 10:14:39 +00:00
public struct ScreenPosition
2021-06-25 09:52:02 +00:00
{
public int ScreenX ;
public int ScreenY ;
public int ScreenWidth ;
public int ScreenHeight ;
public string Name ;
2021-06-26 09:54:11 +00:00
public string Library ;
2021-06-25 09:52:02 +00:00
public bool IsPrimary ;
public Color Colour ;
2021-06-27 01:53:00 +00:00
public string DisplayConnector ;
internal bool HDRSupported ;
internal bool HDREnabled ;
2021-06-25 09:52:02 +00:00
public List < string > Features ;
// If the screen is AMD Eyefinity or NVIDIA Surround or similar, it has screens that are part of it
2021-06-27 01:53:00 +00:00
// These fields indicate this. THe spanned screens are added to the SpannedScreens field
2021-06-25 09:52:02 +00:00
public bool IsSpanned ;
2021-06-27 01:53:00 +00:00
public string SpannedName ;
2021-06-25 09:52:02 +00:00
public List < SpannedScreenPosition > SpannedScreens ;
public int SpannedColumns ;
public int SpannedRows ;
}
public struct SpannedScreenPosition
2021-06-24 10:14:39 +00:00
{
public int ScreenX ;
public int ScreenY ;
public int ScreenWidth ;
public int ScreenHeight ;
public string Name ;
public bool IsPrimary ;
2021-06-25 09:52:02 +00:00
public Color Colour ;
public List < string > Features ;
public int Column ;
public int Row ;
2021-06-24 10:14:39 +00:00
}
2020-10-26 01:30:00 +00:00
public class ProfileItem : IComparable
2017-02-26 19:23:31 +00:00
{
2020-06-07 08:48:45 +00:00
private static List < ProfileItem > _allSavedProfiles = new List < ProfileItem > ( ) ;
2020-05-09 13:02:07 +00:00
private ProfileIcon _profileIcon ;
2020-06-07 08:48:45 +00:00
private Bitmap _profileBitmap , _profileShortcutBitmap ;
2020-08-19 06:55:50 +00:00
private List < string > _profileDisplayIdentifiers = new List < string > ( ) ;
2021-08-22 09:57:29 +00:00
private List < ScreenPosition > _screens = new List < ScreenPosition > ( ) ;
2017-02-26 19:23:31 +00:00
2020-12-02 08:11:23 +00:00
internal static string AppDataPath = System . IO . Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) , "DisplayMagician" ) ;
2021-08-27 09:15:53 +00:00
private static string AppWallpaperPath = Path . Combine ( AppDataPath , $"Wallpaper" ) ;
2021-01-28 09:20:00 +00:00
private static readonly string uuidV4Regex = @"(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$" ;
2020-05-09 13:02:07 +00:00
2020-06-15 09:57:46 +00:00
private string _uuid = "" ;
2021-02-28 08:24:12 +00:00
private bool _isPossible = false ;
2021-04-28 10:14:54 +00:00
private Keys _hotkey = Keys . None ;
2021-08-27 09:15:53 +00:00
private string _wallpaperBitmapFilename = "" ;
2020-06-14 04:20:52 +00:00
2020-05-09 13:02:07 +00:00
#region JsonConverterBitmap
internal class CustomBitmapConverter : JsonConverter
{
public override bool CanConvert ( Type objectType )
{
return true ;
}
//convert from byte to bitmap (deserialize)
public override object ReadJson ( JsonReader reader , Type objectType , object existingValue , JsonSerializer serializer )
{
string image = ( string ) reader . Value ;
byte [ ] byteBuffer = Convert . FromBase64String ( image ) ;
2021-01-28 09:03:02 +00:00
MemoryStream memoryStream = new MemoryStream ( byteBuffer )
{
Position = 0
} ;
2020-05-09 13:02:07 +00:00
return ( Bitmap ) Bitmap . FromStream ( memoryStream ) ;
}
//convert bitmap to byte (serialize)
public override void WriteJson ( JsonWriter writer , object value , JsonSerializer serializer )
{
Bitmap bitmap = ( Bitmap ) value ;
ImageConverter converter = new ImageConverter ( ) ;
writer . WriteValue ( ( byte [ ] ) converter . ConvertTo ( bitmap , typeof ( byte [ ] ) ) ) ;
}
public static System . Drawing . Imaging . ImageFormat GetImageFormat ( Bitmap bitmap )
{
ImageFormat img = bitmap . RawFormat ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Jpeg ) )
return System . Drawing . Imaging . ImageFormat . Jpeg ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Bmp ) )
return System . Drawing . Imaging . ImageFormat . Bmp ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Png ) )
return System . Drawing . Imaging . ImageFormat . Png ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Emf ) )
return System . Drawing . Imaging . ImageFormat . Emf ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Exif ) )
return System . Drawing . Imaging . ImageFormat . Exif ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Gif ) )
return System . Drawing . Imaging . ImageFormat . Gif ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Icon ) )
return System . Drawing . Imaging . ImageFormat . Icon ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . MemoryBmp ) )
return System . Drawing . Imaging . ImageFormat . MemoryBmp ;
if ( img . Equals ( System . Drawing . Imaging . ImageFormat . Tiff ) )
return System . Drawing . Imaging . ImageFormat . Tiff ;
else
return System . Drawing . Imaging . ImageFormat . Wmf ;
}
}
#endregion
2020-06-15 09:57:46 +00:00
public ProfileItem ( )
2017-02-26 19:23:31 +00:00
{
}
2020-05-09 13:02:07 +00:00
public static Version Version = new Version ( 2 , 1 ) ;
2020-06-14 04:20:52 +00:00
#region Instance Properties
2018-10-23 23:34:49 +00:00
2020-06-14 04:20:52 +00:00
public string UUID
2017-02-26 19:23:31 +00:00
{
get
{
2020-06-14 04:20:52 +00:00
if ( String . IsNullOrWhiteSpace ( _uuid ) )
2020-08-18 22:16:04 +00:00
_uuid = Guid . NewGuid ( ) . ToString ( "D" ) ;
2020-06-14 04:20:52 +00:00
return _uuid ;
}
set
{
Match match = Regex . Match ( value , uuidV4Regex , RegexOptions . IgnoreCase ) ;
if ( match . Success )
_uuid = value ;
2017-02-26 19:23:31 +00:00
}
}
2018-10-20 00:13:40 +00:00
[JsonIgnore]
2021-06-22 09:05:24 +00:00
public virtual bool IsPossible
2017-02-26 19:23:31 +00:00
{
get
{
2021-02-28 08:24:12 +00:00
// Return the cached answer
return _isPossible ;
}
set
{
_isPossible = value ;
2020-10-22 09:05:15 +00:00
}
}
[JsonIgnore]
2021-06-22 09:05:24 +00:00
public virtual bool IsActive
2020-10-22 09:05:15 +00:00
{
get
{
if ( this . Equals ( ProfileRepository . CurrentProfile ) )
return true ;
else
return false ;
2020-07-24 09:46:53 +00:00
2017-02-26 19:23:31 +00:00
}
}
2021-08-22 06:58:08 +00:00
public virtual VIDEO_MODE VideoMode { get ; } = VIDEO_MODE . WINDOWS ;
2021-06-22 09:05:24 +00:00
2021-04-28 10:14:54 +00:00
public Keys Hotkey {
2021-04-25 09:13:25 +00:00
get
{
return _hotkey ;
}
set
{
2021-04-28 10:14:54 +00:00
_hotkey = value ;
2021-04-25 09:13:25 +00:00
}
}
2021-06-22 09:05:24 +00:00
public virtual string Name { get ; set ; }
2017-02-26 19:23:31 +00:00
2020-10-07 07:08:36 +00:00
2020-06-15 09:57:46 +00:00
[JsonIgnore]
2021-06-22 09:05:24 +00:00
public virtual ProfileIcon ProfileIcon
2020-05-09 13:02:07 +00:00
{
2020-05-12 10:46:23 +00:00
get
{
if ( _profileIcon ! = null )
return _profileIcon ;
else
{
_profileIcon = new ProfileIcon ( this ) ;
return _profileIcon ;
}
}
2020-05-09 13:02:07 +00:00
set
{
_profileIcon = value ;
}
}
2021-08-22 09:57:29 +00:00
[JsonIgnore]
2021-06-24 10:14:39 +00:00
public virtual List < ScreenPosition > Screens
{
get
{
if ( _screens . Count = = 0 )
{
_screens = GetScreenPositions ( ) ;
}
return _screens ;
}
2021-06-27 01:53:00 +00:00
set
{
_screens = value ;
}
2021-06-24 10:14:39 +00:00
}
2020-06-14 04:20:52 +00:00
public string SavedProfileIconCacheFilename { get ; set ; }
2020-05-10 10:47:18 +00:00
2021-08-27 09:15:53 +00:00
public bool SetWallpaper { get ; set ; }
2021-08-27 10:26:12 +00:00
public Wallpaper . Style WallpaperStyle { get ; set ; }
2021-08-27 09:15:53 +00:00
public string WallpaperBitmapFilename {
get
{
return _wallpaperBitmapFilename ;
}
set
{
_wallpaperBitmapFilename = value ;
}
}
2021-06-22 09:05:24 +00:00
public virtual List < string > ProfileDisplayIdentifiers
2020-08-19 06:55:50 +00:00
{
get
{
if ( _profileDisplayIdentifiers . Count = = 0 )
{
2021-07-24 04:05:38 +00:00
_profileDisplayIdentifiers = ProfileRepository . GetCurrentDisplayIdentifiers ( ) ;
2020-08-19 06:55:50 +00:00
}
return _profileDisplayIdentifiers ;
}
set
{
if ( value is List < string > )
_profileDisplayIdentifiers = value ;
}
}
2021-08-22 09:45:51 +00:00
[JsonConverter(typeof(CustomBitmapConverter))]
2021-06-22 09:05:24 +00:00
public virtual Bitmap ProfileBitmap
2020-05-09 13:02:07 +00:00
{
2020-05-12 10:46:23 +00:00
get
{
if ( _profileBitmap ! = null )
return _profileBitmap ;
else
{
2020-06-07 08:48:45 +00:00
_profileBitmap = this . ProfileIcon . ToBitmap ( 256 , 256 ) ;
2020-05-12 10:46:23 +00:00
return _profileBitmap ;
}
}
2020-05-09 13:02:07 +00:00
set
{
_profileBitmap = value ;
}
2018-10-20 00:13:40 +00:00
}
2017-02-26 19:23:31 +00:00
2021-08-22 09:45:51 +00:00
[JsonConverter(typeof(CustomBitmapConverter))]
//[JsonIgnore]
2021-06-27 01:53:00 +00:00
public virtual Bitmap ProfileTightestBitmap
2020-06-07 08:48:45 +00:00
{
get
{
if ( _profileShortcutBitmap ! = null )
return _profileShortcutBitmap ;
else
{
_profileShortcutBitmap = this . ProfileIcon . ToTightestBitmap ( ) ;
return _profileShortcutBitmap ;
}
}
set
{
_profileShortcutBitmap = value ;
}
}
2020-06-14 04:20:52 +00:00
#endregion
2017-02-26 19:23:31 +00:00
2020-05-09 13:02:07 +00:00
public static bool IsValidName ( string testName )
{
2020-06-07 08:48:45 +00:00
foreach ( ProfileItem loadedProfile in _allSavedProfiles )
2020-05-09 13:02:07 +00:00
{
if ( loadedProfile . Name = = testName )
{
return false ;
}
}
return true ;
}
2020-06-15 09:57:46 +00:00
public static bool IsValidUUID ( string testId )
2020-05-09 13:02:07 +00:00
{
2020-06-14 04:20:52 +00:00
Match match = Regex . Match ( testId , uuidV4Regex , RegexOptions . IgnoreCase ) ;
if ( match . Success )
2020-05-10 10:47:18 +00:00
return true ;
2020-06-14 04:20:52 +00:00
else
return false ;
2020-05-10 10:47:18 +00:00
}
2021-06-22 09:05:24 +00:00
public virtual bool IsValid ( )
2020-08-19 06:55:50 +00:00
{
2021-06-22 09:05:24 +00:00
return false ;
2020-08-19 06:55:50 +00:00
}
2018-10-20 00:27:25 +00:00
2020-10-06 07:59:59 +00:00
2020-09-18 10:52:18 +00:00
2021-06-22 09:05:24 +00:00
public virtual bool CopyTo ( ProfileItem profile , bool overwriteId = true )
2020-06-14 04:20:52 +00:00
{
if ( ! ( profile is ProfileItem ) )
return false ;
2020-05-09 13:02:07 +00:00
2020-06-15 09:57:46 +00:00
if ( overwriteId = = true )
2020-06-14 04:20:52 +00:00
profile . UUID = UUID ;
// Copy all our profile data over to the other profile
profile . Name = Name ;
2021-06-22 09:05:24 +00:00
//profile.Paths = Paths;
2020-06-14 04:20:52 +00:00
profile . ProfileIcon = ProfileIcon ;
profile . SavedProfileIconCacheFilename = SavedProfileIconCacheFilename ;
profile . ProfileBitmap = ProfileBitmap ;
profile . ProfileTightestBitmap = ProfileTightestBitmap ;
2020-08-19 06:55:50 +00:00
profile . ProfileDisplayIdentifiers = ProfileDisplayIdentifiers ;
2021-08-27 09:15:53 +00:00
profile . SetWallpaper = SetWallpaper ;
profile . WallpaperBitmapFilename = WallpaperBitmapFilename ;
2021-08-27 10:26:12 +00:00
profile . WallpaperStyle = WallpaperStyle ;
2020-06-14 04:20:52 +00:00
return true ;
2017-02-26 19:23:31 +00:00
}
2021-06-22 09:05:24 +00:00
public virtual bool PreSave ( )
2020-08-19 06:55:50 +00:00
{
// Prepare our profile data for saving
if ( _profileDisplayIdentifiers . Count = = 0 )
{
2021-07-24 04:05:38 +00:00
_profileDisplayIdentifiers = ProfileRepository . GetCurrentDisplayIdentifiers ( ) ;
2020-08-19 06:55:50 +00:00
}
// Return if it is valid and we should continue
return IsValid ( ) ;
}
2020-05-13 11:04:18 +00:00
2021-06-26 09:54:11 +00:00
public virtual bool CreateProfileFromCurrentDisplaySettings ( )
{
return false ;
}
2021-06-27 01:53:00 +00:00
public virtual bool PerformPostLoadingTasks ( )
{
return false ;
}
2021-03-26 08:54:45 +00:00
public override int GetHashCode ( )
{
2017-02-26 19:23:31 +00:00
2021-03-26 08:54:45 +00:00
// Get hash code for the ProfileDisplayIdentifiers field if it is not null.
2021-03-26 10:55:35 +00:00
int hashIds = ProfileDisplayIdentifiers = = null ? 0 : ProfileDisplayIdentifiers . GetHashCode ( ) ;
2021-03-26 08:54:45 +00:00
2021-03-26 10:55:35 +00:00
// Get Paths too
2021-06-22 09:05:24 +00:00
//int hashPaths = Paths == null ? 0 : Paths.GetHashCode();
int hashPaths = 0 ;
2021-03-26 10:55:35 +00:00
// Calculate the hash code for the product.
return ( hashIds , hashPaths ) . GetHashCode ( ) ;
2021-03-26 08:54:45 +00:00
}
2020-05-13 11:04:18 +00:00
2017-02-26 19:23:31 +00:00
public override string ToString ( )
{
2020-05-16 03:58:59 +00:00
return ( Name ? ? Language . UN_TITLED_PROFILE ) ;
2017-02-26 19:23:31 +00:00
}
2020-10-26 01:30:00 +00:00
public int CompareTo ( object obj )
{
2021-01-28 09:03:02 +00:00
if ( ! ( obj is ProfileItem ) ) throw new ArgumentException ( "Object to CompareTo is not a Shortcut" ) ; ;
2020-10-26 01:30:00 +00:00
2021-01-28 09:03:02 +00:00
ProfileItem otherProfile = ( ProfileItem ) obj ;
return this . Name . CompareTo ( otherProfile . Name ) ;
2020-10-26 01:30:00 +00:00
}
2021-03-07 01:50:52 +00:00
// ReSharper disable once FunctionComplexityOverflow
// ReSharper disable once CyclomaticComplexity
public bool CreateShortcut ( string shortcutFileName )
{
string shortcutDescription = string . Empty ;
string shortcutIconFileName ;
var shortcutArgs = new List < string >
{
// Add the SwitchProfile command as the first argument to start to switch to another profile
$"{DisplayMagicianStartupAction.ChangeProfile}" ,
$"\" { UUID } \ ""
} ;
// Prepare text for the shortcut description field
shortcutDescription = $"Change to the '{Name}' DisplayMagician Display Profile." ;
// Now we are ready to create a shortcut based on the filename the user gave us
shortcutFileName = System . IO . Path . ChangeExtension ( shortcutFileName , @"lnk" ) ;
// And we use the Icon from the shortcutIconCache
shortcutIconFileName = SavedProfileIconCacheFilename ;
// If the user supplied a file
if ( shortcutFileName ! = null )
{
try
{
// Remove the old file if it exists to replace it
if ( System . IO . File . Exists ( shortcutFileName ) )
{
System . IO . File . Delete ( shortcutFileName ) ;
}
// Actually create the shortcut!
//var wshShellType = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8"));
//dynamic wshShell = Activator.CreateInstance(wshShellType);
WshShell shell = new WshShell ( ) ;
IWshShortcut shortcut = ( IWshShortcut ) shell . CreateShortcut ( shortcutFileName ) ;
shortcut . TargetPath = Application . ExecutablePath ;
shortcut . Arguments = string . Join ( " " , shortcutArgs ) ;
shortcut . Description = shortcutDescription ;
shortcut . WorkingDirectory = System . IO . Path . GetDirectoryName ( Application . ExecutablePath ) ? ?
string . Empty ;
shortcut . IconLocation = shortcutIconFileName ;
shortcut . Save ( ) ;
}
catch ( Exception ex )
{
SharedLogger . logger . Warn ( ex , $"ShortcutItem/CreateShortcut: Execption while creating desktop shortcut!" ) ;
// Clean up a failed attempt
if ( System . IO . File . Exists ( shortcutFileName ) )
{
System . IO . File . Delete ( shortcutFileName ) ;
}
}
}
// Return a status on how it went
// true if it was a success or false if it was not
return shortcutFileName ! = null & & System . IO . File . Exists ( shortcutFileName ) ;
}
2021-06-27 02:44:10 +00:00
public virtual void RefreshPossbility ( )
2021-03-07 04:11:46 +00:00
{
// Check each display in this profile and make sure it's currently available
int validDisplayCount = 0 ;
2021-04-04 06:59:38 +00:00
//validDisplayCount = (from connectedDisplay in ProfileRepository.ConnectedDisplayIdentifiers select connectedDisplay == profileDisplayIdentifier).Count();
2021-05-19 09:32:49 +00:00
2021-03-07 04:11:46 +00:00
foreach ( string profileDisplayIdentifier in ProfileDisplayIdentifiers )
{
// If this profile has a display that isn't currently available then we need to say it's a no!
2021-04-04 06:59:38 +00:00
if ( ProfileRepository . ConnectedDisplayIdentifiers . Any ( s = > profileDisplayIdentifier . Equals ( s ) ) )
2021-05-19 09:32:49 +00:00
{
SharedLogger . logger . Trace ( $"ProfileItem/RefreshPossbility: We found the display in the profile {Name} with profileDisplayIdentifier {profileDisplayIdentifier} is connected now." ) ;
2021-03-07 04:11:46 +00:00
validDisplayCount + + ;
2021-05-19 09:32:49 +00:00
}
else
{
SharedLogger . logger . Warn ( $"ProfileItem/RefreshPossbility: We found the display in the profile {Name} with profileDisplayIdentifier {profileDisplayIdentifier} is NOT currently connected, so this profile cannot be used." ) ;
}
2021-03-07 04:11:46 +00:00
}
if ( validDisplayCount = = ProfileDisplayIdentifiers . Count )
{
2021-03-27 03:15:17 +00:00
2021-03-07 04:11:46 +00:00
SharedLogger . logger . Debug ( $"ProfileRepository/IsPossibleRefresh: The profile {Name} is possible!" ) ;
_isPossible = true ;
2021-03-27 03:15:17 +00:00
2021-03-07 04:11:46 +00:00
}
else
{
SharedLogger . logger . Debug ( $"ProfileRepository/IsPossibleRefresh: The profile {Name} is NOT possible!" ) ;
_isPossible = false ;
}
2021-03-07 01:50:52 +00:00
2021-03-07 04:11:46 +00:00
}
2021-06-24 10:14:39 +00:00
2021-08-24 10:46:32 +00:00
// Actually set this profile active
public virtual bool SetActive ( )
{
return false ;
}
2021-06-24 10:14:39 +00:00
public virtual List < ScreenPosition > GetScreenPositions ( )
{
return new List < ScreenPosition > ( ) ;
}
2020-05-13 11:04:18 +00:00
}
2017-02-26 19:23:31 +00:00
}