using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using WindowsDisplayAPI.Exceptions; using WindowsDisplayAPI.Native; using WindowsDisplayAPI.Native.DeviceContext; using WindowsDisplayAPI.Native.DeviceContext.Structures; using WindowsDisplayAPI.Native.Structures; namespace WindowsDisplayAPI { /// /// Holds configurations of a windows display /// public class DisplaySetting : DisplayPossibleSetting { /// /// Creates a new instance. /// /// The basic configuration information object /// Display position on desktop public DisplaySetting(DisplayPossibleSetting validSetting, Point position = default) : this(validSetting, position, DisplayOrientation.Identity, DisplayFixedOutput.Default) { } /// /// Creates a new instance. /// /// The basic configuration information object /// Display position on desktop /// Display orientation and rotation /// /// Display output behavior in case of presenting a low-resolution mode on a /// higher-resolution display /// public DisplaySetting( DisplayPossibleSetting validSetting, Point position, DisplayOrientation orientation, DisplayFixedOutput outputScalingMode) : this( validSetting.Resolution, position, validSetting.ColorDepth, validSetting.Frequency, validSetting.IsInterlaced, orientation, outputScalingMode ) { } /// /// Creates a new instance. /// /// Display resolution /// Display position on desktop /// Display frequency public DisplaySetting(Size resolution, Point position, int frequency) : this(resolution, position, ColorDepth.Depth32Bit, frequency) { } /// /// Creates a new instance. /// /// Display resolution /// Display frequency public DisplaySetting(Size resolution, int frequency) : this(resolution, new Point(0, 0), ColorDepth.Depth32Bit, frequency) { } /// /// Creates a new instance. /// /// Display resolution /// Display position on desktop /// Display frequency /// Display color depth /// Indicating if display is using interlaces scan out /// Display orientation and rotation /// /// Display output behavior in case of presenting a low-resolution mode on a /// higher-resolution display /// public DisplaySetting( Size resolution, Point position, ColorDepth colorDepth, int frequency, bool isInterlaced = false, DisplayOrientation orientation = DisplayOrientation.Identity, DisplayFixedOutput outputScalingMode = DisplayFixedOutput.Default ) : base(resolution, frequency, colorDepth, isInterlaced) { Position = position; Orientation = orientation; OutputScalingMode = outputScalingMode; } internal DisplaySetting() : base(default) { IsEnable = false; } private DisplaySetting(DeviceMode deviceMode) : base(deviceMode) { Position = new Point(deviceMode.Position.X, deviceMode.Position.Y); Orientation = deviceMode.DisplayOrientation; OutputScalingMode = deviceMode.DisplayFixedOutput; if (Resolution.IsEmpty && Position.IsEmpty) { IsEnable = false; } } /// /// Gets a boolean value indicating if this instance is currently enable /// public bool IsEnable { get; } = true; /// /// Gets or sets the orientation of the display monitor /// public DisplayOrientation Orientation { get; } /// /// Gets output behavior in case of presenting a low-resolution mode on a higher-resolution display /// public DisplayFixedOutput OutputScalingMode { get; } /// /// Gets or sets the position of the display monitor /// public Point Position { get; } /// /// Applies settings that are saved using SaveDisplaySettings() or other similar methods but not yet applied /// public static void ApplySavedSettings() { var result = DeviceContextApi.ChangeDisplaySettingsEx( null, IntPtr.Zero, IntPtr.Zero, ChangeDisplaySettingsFlags.Reset, IntPtr.Zero ); if (result != ChangeDisplaySettingsExResults.Successful) { throw new ModeChangeException($"[{result}]: Applying saved settings failed.", null, result); } } /// /// Returns the current display settings of a screen /// /// The name of the screen. /// An instance of public static DisplaySetting GetCurrentFromScreenName(string screenName) { return new DisplaySetting(GetDeviceMode(screenName, DisplaySettingsMode.CurrentSettings)); } /// /// Returns the saved display settings of a screen /// /// The name of the screen. /// An instance of public static DisplaySetting GetSavedFromScreenName(string screenName) { return new DisplaySetting(GetDeviceMode(screenName, DisplaySettingsMode.RegistrySettings)); } /// /// Sets and possibility applies a list of display settings /// /// /// A key value dictionary of and /// instances. /// /// Indicating if the changes should be applied immediately, recommended value is false public static void SaveDisplaySettings( Dictionary newSettingPairs, bool applyNow) { SaveDisplaySettings( newSettingPairs.ToDictionary(pair => pair.Key.ScreenName, pair => pair.Value), applyNow, true ); } /// /// Sets and possibility applies a list of display settings /// /// A key value dictionary of source ids and instance /// Indicating if the changes should be applied immediately, recommended value is false public static void SaveDisplaySettings( Dictionary newSettingPairs, bool applyNow) { SaveDisplaySettings( newSettingPairs.ToDictionary(pair => $"\\\\.\\DISPLAY{pair.Key:D}", pair => pair.Value), applyNow, true ); } private static DeviceMode GetDeviceMode(string screenName, DisplaySettingsMode flags) { var deviceMode = new DeviceMode(DeviceModeFields.None); return !string.IsNullOrWhiteSpace(screenName) && DeviceContextApi.EnumDisplaySettings( screenName, flags, ref deviceMode ) ? deviceMode : default; } private static void SaveDisplaySettings( Dictionary newSettings, bool applyNow, bool retry ) { var screens = DisplayScreen.GetScreens() .Where(screen => screen.IsValid) .ToList(); var rollbackSettings = screens .ToDictionary(screen => screen.ScreenName, screen => screen.CurrentSetting); try { foreach (var newSetting in newSettings) { screens.Remove( screens.FirstOrDefault( screen => screen.ScreenName.Equals(newSetting.Key) ) ); newSetting.Value.Save(newSetting.Key, false); } // Disable missing monitors foreach (var screen in screens.Where(screen => screen.IsValid)) { screen.Disable(false); } if (applyNow) { ApplySavedSettings(); } } catch (ModeChangeException) { if (retry) { SaveDisplaySettings(rollbackSettings, false, false); } throw; } } /// public override string ToString() { return IsEnable ? $"{Resolution} {(IsInterlaced ? "Interlaced" : "Progressive")} {Frequency}hz @ {ColorDepth} @ {Position}" : "Disabled"; } internal void Save(string screenName, bool reset) { var deviceMode = GetDeviceMode(screenName); var flags = ChangeDisplaySettingsFlags.UpdateRegistry | ChangeDisplaySettingsFlags.Global; flags |= reset ? ChangeDisplaySettingsFlags.Reset : ChangeDisplaySettingsFlags.NoReset; if (IsEnable && Position.X == 0 && Position.Y == 0) { flags |= ChangeDisplaySettingsFlags.SetPrimary; } var result = DeviceContextApi.ChangeDisplaySettingsEx( screenName, ref deviceMode, IntPtr.Zero, flags, IntPtr.Zero ); if (result != ChangeDisplaySettingsExResults.Successful) { throw new ModeChangeException($"[{result}]: Applying saved settings failed.", null, result); } } private DeviceMode GetDeviceMode(string screenName) { DeviceMode deviceMode; if (IsEnable) { var flags = DisplayFlags.None; if (IsInterlaced) { flags |= DisplayFlags.Interlaced; } deviceMode = new DeviceMode( screenName, new PointL(Position), Orientation, OutputScalingMode, (uint) ColorDepth, (uint) Resolution.Width, (uint) Resolution.Height, flags, (uint) Frequency ); } else { deviceMode = new DeviceMode( screenName, DeviceModeFields.PelsWidth | DeviceModeFields.PelsHeight | DeviceModeFields.Position ); } if (string.IsNullOrWhiteSpace(deviceMode.DeviceName)) { throw new MissingDisplayException("Display screen is missing or invalid.", null); } return deviceMode; } } }