-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMonitorManager.cs
222 lines (174 loc) · 8.24 KB
/
MonitorManager.cs
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
namespace MonitorControl
{
public class PhysicalMonitor
{
public IntPtr hPhysicalMonitor;
public string DeviceName;
public bool IsEnabled;
public bool IsPoweredOn;
public uint BrightnessLevel;
}
public class Monitor
{
public IntPtr hMonitor;
public Rect rect;
public List<PhysicalMonitor> physicalMonitors;
}
internal class MonitorManager : IDisposable
{
#region [Windows API]
[DllImport("user32.dll", EntryPoint = "MonitorFromWindow")]
private static extern IntPtr MonitorFromWindow([In] IntPtr hwnd, uint dwFlags);
[DllImport("dxva2.dll", EntryPoint = "DestroyPhysicalMonitors")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DestroyPhysicalMonitors(uint dwPhysicalMonitorArraySize, ref PHYSICAL_MONITOR[] pPhysicalMonitorArray);
[DllImport("dxva2.dll", EntryPoint = "GetNumberOfPhysicalMonitorsFromHMONITOR")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetNumberOfPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, ref uint pdwNumberOfPhysicalMonitors);
[DllImport("dxva2.dll", EntryPoint = "GetPhysicalMonitorsFromHMONITOR")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetPhysicalMonitorsFromHMONITOR(IntPtr hMonitor, uint dwPhysicalMonitorArraySize,
[Out] PHYSICAL_MONITOR[] pPhysicalMonitorArray);
[DllImport("dxva2.dll", EntryPoint = "GetVCPFeatureAndVCPFeatureReply", SetLastError = true)]
private static extern Boolean GetVCPFeatureAndVCPFeatureReply([In] IntPtr hPhisicalMonitor, [In] byte bVCPCode,
IntPtr pvct, ref uint pdwCurrentValue, ref uint pdwMaximumValue);
private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData);
[DllImport("user32")]
private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData);
[DllImport("dxva2.dll", EntryPoint = "SetVCPFeature", SetLastError = true)]
private static extern bool SetVCPFeature([In] IntPtr hPhisicalMonitor, byte bVCPCode, uint dwNewValue);
#endregion
const byte SVC_FEATURE__POWER_MODE = 0xD6; // values use PowerModeEnum
const byte SVC_FEATURE__BRIGHTNESS = 0x10; // value range is [0-100]
public enum PowerModeEnum : uint
{
PowerOn = 0x01,
PowerStandby = 0x02,
PowerSuspend = 0x03,
PowerOff = 0x04,
PowerOffButton = 0x05 // Readonly
}
List<Monitor> monitors;
List<PHYSICAL_MONITOR> physicalMonitors;
public IReadOnlyList<Monitor> Monitors
{
get
{
return monitors.AsReadOnly();
}
}
public void Initialize()
{
monitors = new List<Monitor>();
physicalMonitors = new List<PHYSICAL_MONITOR>();
List<(IntPtr monitor, Rect rect)> hMonitors = new List<(IntPtr, Rect)>();
MonitorEnumProc callback = (IntPtr hMonitor, IntPtr hdc, ref Rect prect, int d) =>
{
monitors.Add(new Monitor
{
hMonitor = hMonitor,
rect = prect,
});
return true;
};
if (EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0))
{
foreach (var m in monitors)
{
uint mcount = 0;
if (!GetNumberOfPhysicalMonitorsFromHMONITOR(m.hMonitor, ref mcount))
{
throw new Exception("Cannot get monitor count!");
}
PHYSICAL_MONITOR[] physicalMonitors = new PHYSICAL_MONITOR[mcount];
if (!GetPhysicalMonitorsFromHMONITOR(m.hMonitor, mcount, physicalMonitors))
{
throw new Exception("Cannot get phisical monitor handle!");
}
Debug.WriteLine($"PM:{physicalMonitors.Length}) RECT: T:{m.rect.Top}/L:{m.rect.Left}/R:{m.rect.Right}/B:{m.rect.Bottom}");
this.physicalMonitors.AddRange(physicalMonitors);
m.physicalMonitors = physicalMonitors.Select(a => new PhysicalMonitor
{
DeviceName = a.szPhysicalMonitorDescription,
hPhysicalMonitor = a.hPhysicalMonitor
}).ToList();
}
foreach (var p in monitors.SelectMany(a => a.physicalMonitors))
{
uint cv = 0;
// power
if (GetFeatureValue(p.hPhysicalMonitor, SVC_FEATURE__POWER_MODE, ref cv))
{
p.IsPoweredOn = (cv == (uint)PowerModeEnum.PowerOn);
Debug.WriteLine($"{p.hPhysicalMonitor} + {p.DeviceName} + POWER={cv}");
p.IsEnabled = true;
}
else
{
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Debug.WriteLine($"ERROR for {p.DeviceName}: `{errorMessage}`");
}
// BRIG
if (GetFeatureValue(p.hPhysicalMonitor, SVC_FEATURE__BRIGHTNESS, ref cv))
{
p.BrightnessLevel = cv;
Debug.WriteLine($"{p.hPhysicalMonitor} + {p.DeviceName} + BRIGHTNESS={cv}");
}
else
{
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Debug.WriteLine($"ERROR for {p.DeviceName}: `{errorMessage}`");
}
}
}
}
private bool GetFeatureValue(IntPtr hPhysicalMonitor, byte svc_feature, ref uint currentValue)
{
uint mv = 0;
return GetVCPFeatureAndVCPFeatureReply(hPhysicalMonitor, svc_feature, IntPtr.Zero, ref currentValue, ref mv);
//string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
}
private bool SetFeatureValue(IntPtr hPhysicalMonitor, byte svc_feature, uint newVurrent)
{
return SetVCPFeature(hPhysicalMonitor, svc_feature, newVurrent);
//string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
}
public void ChangeBrightness(IntPtr hPhysicalMonitor, uint brightness)
{
SetFeatureValue(hPhysicalMonitor, SVC_FEATURE__BRIGHTNESS, brightness);
// TODO: use HighLevel API to set brightness on non VESA monitors
// https://docs.microsoft.com/en-us/windows/win32/api/highlevelmonitorconfigurationapi/nf-highlevelmonitorconfigurationapi-setmonitorbrightness
}
public void ChangePower(IntPtr hPhysicalMonitor, bool PowerOn)
{
SetFeatureValue(hPhysicalMonitor, SVC_FEATURE__POWER_MODE, PowerOn ? (uint)PowerModeEnum.PowerOn : (uint)PowerModeEnum.PowerOff);
}
public void Dispose()
{
GC.SuppressFinalize(this);
PHYSICAL_MONITOR[] toDestroy = physicalMonitors.ToArray();
DestroyPhysicalMonitors((uint)toDestroy.Length, ref toDestroy);
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PHYSICAL_MONITOR
{
public IntPtr hPhysicalMonitor;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szPhysicalMonitorDescription;
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}