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;
}
}
}