-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDisplayManager.cs
More file actions
164 lines (140 loc) · 8.85 KB
/
DisplayManager.cs
File metadata and controls
164 lines (140 loc) · 8.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// These 'using' statements are necessary for file operations, P/Invoke, and JSON serialization.
// Add them at the very top of your file.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
// This is the namespace for your project.
namespace MonPro
{
/// <summary>
/// Handles saving, loading, and applying monitor display configurations.
/// All P/Invoke calls and display logic are contained within this class.
/// </summary>
public static class DisplayManager // Making this 'static' as it only contains utility methods.
{
#region Public Methods (What the UI will use)
/// <summary>
/// Queries the current display configuration and saves it to a JSON file.
/// </summary>
/// <param name="filePath">The full path for the profile file to be saved.</param>
public static void SaveCurrentProfile(string filePath)
{
// 1. Get the required buffer sizes for the configuration arrays.
DisplayApi.GetDisplayConfigBufferSizes(
DisplayApi.QdcFlags.QDC_ONLY_ACTIVE_PATHS,
out uint pathCount,
out uint modeCount);
// 2. Create the arrays to hold the configuration data.
var pathArray = new DisplayApi.DISPLAYCONFIG_PATH_INFO[pathCount];
var modeArray = new DisplayApi.DISPLAYCONFIG_MODE_INFO[modeCount];
// 3. Query the actual display configuration from Windows.
DisplayApi.QueryDisplayConfig(
DisplayApi.QdcFlags.QDC_ONLY_ACTIVE_PATHS,
ref pathCount, pathArray,
ref modeCount, modeArray,
IntPtr.Zero);
// 4. Create a profile object, serialize it to JSON, and write it to a file.
var profile = new MonitorProfile { PathArray = pathArray, ModeArray = modeArray };
string json = JsonSerializer.Serialize(profile, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(filePath, json);
}
/// <summary>
/// Loads a display configuration from a JSON file and applies it.
/// </summary>
/// <param name="filePath">The full path of the profile file to load.</param>
public static void LoadProfile(string filePath)
{
if (!File.Exists(filePath)) return;
string json = File.ReadAllText(filePath);
var profile = JsonSerializer.Deserialize<MonitorProfile>(json);
if (profile == null) return;
// Apply the loaded configuration.
DisplayApi.SetDisplayConfig(
(uint)profile.PathArray.Length,
profile.PathArray,
(uint)profile.ModeArray.Length,
profile.ModeArray,
DisplayApi.SdcFlags.SDC_APPLY | DisplayApi.SdcFlags.SDC_TOPOLOGY_CLONE | DisplayApi.SdcFlags.SDC_TOPOLOGY_EXTEND);
}
#endregion
#region Nested Classes (Data Structures)
/// <summary>
/// A helper class that defines the structure of our saved JSON profile file.
/// </summary>
public class MonitorProfile
{
public DisplayApi.DISPLAYCONFIG_PATH_INFO[] PathArray { get; set; }
public DisplayApi.DISPLAYCONFIG_MODE_INFO[] ModeArray { get; set; }
}
#endregion
#region P/Invoke Definitions (The Core Windows API Logic)
/// <summary>
/// This static class holds all the low-level Windows API structures, enums, and function imports.
/// It's nested here to keep everything neatly organized.
/// </summary>
public static class DisplayApi
{
[Flags]
public enum SdcFlags : uint { SDC_TOPOLOGY_INTERNAL = 0x00000001, SDC_TOPOLOGY_CLONE = 0x00000002, SDC_TOPOLOGY_EXTEND = 0x00000004, SDC_TOPOLOGY_EXTERNAL = 0x00000008, SDC_APPLY = 0x00000080 }
[Flags]
public enum QdcFlags : uint { QDC_ALL_PATHS = 0x00000001, QDC_ONLY_ACTIVE_PATHS = 0x00000002, QDC_DATABASE_CURRENT = 0x00000004 }
public enum DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY : uint { OTHER = 0xFFFFFFFF, HD15 = 0, SVIDEO = 1, COMPOSITE_VIDEO = 2, COMPONENT_VIDEO = 3, DVI = 4, HDMI = 5, LVDS = 6, D_JPN = 8, SDI = 9, DISPLAYPORT_EXTERNAL = 10, DISPLAYPORT_EMBEDDED = 11, UDI_EXTERNAL = 12, UDI_EMBEDDED = 13, SDTVDONGLE = 14, MIRACAST = 15, INTERNAL = 0x80000000, FORCED_UINT32 = 0xFFFFFFFF }
public enum DISPLAYCONFIG_ROTATION : uint { IDENTITY = 1, ROTATE90 = 2, ROTATE180 = 3, ROTATE270 = 4, FORCED_UINT32 = 0xFFFFFFFF }
public enum DISPLAYCONFIG_SCALING : uint { IDENTITY = 1, CENTERED = 2, STRETCHED = 3, ASPECTRATIOCENTEREDMAX = 4, CUSTOM = 5, PREFERRED = 128, FORCED_UINT32 = 0xFFFFFFFF }
public enum DISPLAYCONFIG_SCANLINE_ORDERING : uint { UNSPECIFIED = 0, PROGRESSIVE = 1, INTERLACED = 2, INTERLACED_UPPERFIELDFIRST = INTERLACED, INTERLACED_LOWERFIELDFIRST = 3, FORCED_UINT32 = 0xFFFFFFFF }
public enum DISPLAYCONFIG_MODE_INFO_TYPE : uint { SOURCE = 1, TARGET = 2, FORCE_UINT32 = 0xFFFFFFFF }
[StructLayout(LayoutKind.Sequential)] public struct LUID { public long LowPart; public long HighPart; }
[StructLayout(LayoutKind.Sequential)] public struct POINTL { public long x; public long y; }
[StructLayout(LayoutKind.Sequential)] public struct RECTL { public int left; public int top; public int right; public int bottom; }
[StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_RATIONAL { public uint Numerator; public uint Denominator; }
[StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_SOURCE_INFO { public LUID adapterId; public uint id; public uint modeInfoIdx; public ushort statusFlags; }
[StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_SOURCE_MODE { public uint width; public uint height; public DISPLAYCONFIG_PIXEL_FORMAT pixelFormat; public POINTL position; }
[StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_TARGET_MODE { public DISPLAYCONFIG_VIDEO_SIGNAL_INFO videoSignalInfo; }
[StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO { public ulong totalSize; public ulong activeSize; public DISPLAYCONFIG_RATIONAL VSyncFreq; public DISPLAYCONFIG_RATIONAL HSyncFreq; public DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; }
public enum DISPLAYCONFIG_PIXEL_FORMAT : uint { PIXELFORMAT_8BPP = 1, PIXELFORMAT_16BPP = 2, PIXELFORMAT_24BPP = 3, PIXELFORMAT_32BPP = 4, PIXELFORMAT_NONGDI = 5, PIXELFORMAT_FORCE_UINT32 = 0xffffffff }
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAYCONFIG_PATH_INFO
{
public DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo;
public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo;
public uint flags;
}
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAYCONFIG_PATH_TARGET_INFO
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology;
public DISPLAYCONFIG_ROTATION rotation;
public DISPLAYCONFIG_SCALING scaling;
public DISPLAYCONFIG_RATIONAL refreshRate;
public DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering;
public bool targetAvailable;
public uint statusFlags;
}
[StructLayout(LayoutKind.Explicit)]
public struct DISPLAYCONFIG_MODE_INFO_UNION
{
[FieldOffset(0)] public DISPLAYCONFIG_TARGET_MODE targetMode;
[FieldOffset(0)] public DISPLAYCONFIG_SOURCE_MODE sourceMode;
}
[StructLayout(LayoutKind.Sequential)]
public struct DISPLAYCONFIG_MODE_INFO
{
public DISPLAYCONFIG_MODE_INFO_TYPE infoType;
public uint id;
public LUID adapterId;
public DISPLAYCONFIG_MODE_INFO_UNION modeInfo;
}
[DllImport("user32.dll")]
public static extern int GetDisplayConfigBufferSizes(QdcFlags flags, out uint numPathArrayElements, out uint numModeInfoArrayElements);
[DllImport("user32.dll")]
public static extern int QueryDisplayConfig(QdcFlags flags, ref uint numPathArrayElements, [Out] DISPLAYCONFIG_PATH_INFO[] pathInfoArray, ref uint numModeInfoArrayElements, [Out] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, IntPtr currentTopologyId);
[DllImport("user32.dll")]
public static extern int SetDisplayConfig(uint numPathArrayElements, [In] DISPLAYCONFIG_PATH_INFO[] pathInfoArray, uint numModeInfoArrayElements, [In] DISPLAYCONFIG_MODE_INFO[] modeInfoArray, SdcFlags flags);
}
#endregion
}
}