-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathNativeMethods.cs
More file actions
356 lines (298 loc) · 13 KB
/
Copy pathNativeMethods.cs
File metadata and controls
356 lines (298 loc) · 13 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
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
using System.Runtime.InteropServices;
namespace AmpUp;
internal static class NativeMethods
{
// user32.dll — GetLastInputInfo for system-wide keyboard/mouse idle detection.
[StructLayout(LayoutKind.Sequential)]
internal struct LASTINPUTINFO
{
public uint cbSize;
public uint dwTime;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
/// <summary>Returns milliseconds since the last keyboard/mouse input at the system level.</summary>
internal static uint GetIdleMilliseconds()
{
var info = new LASTINPUTINFO { cbSize = (uint)Marshal.SizeOf<LASTINPUTINFO>() };
if (!GetLastInputInfo(ref info)) return 0;
return (uint)Environment.TickCount - info.dwTime;
}
// dwmapi.dll — DWM window attributes (border color, corner preference)
[DllImport("dwmapi.dll", PreserveSig = true)]
internal static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
internal const int DWMWA_BORDER_COLOR = 34;
internal const int DWMWA_CAPTION_COLOR = 35;
internal const int DWMWA_COLOR_NONE = unchecked((int)0xFFFFFFFE);
/// <summary>Remove the DWM border on Win11 windows by setting it to match the dark background.</summary>
internal static void RemoveDwmBorder(IntPtr hwnd)
{
// Set border color to our dark background (#0F0F0F) so it's invisible
// COLORREF format: 0x00BBGGRR
int darkBg = 0x000F0F0F;
DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, ref darkBg, sizeof(int));
// Also set caption/title bar color to match
DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref darkBg, sizeof(int));
}
// user32.dll — shared across AudioMixer, ButtonHandler, TrayApp
[DllImport("user32.dll")]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
internal static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
internal struct RECT { public int Left, Top, Right, Bottom; }
/// <summary>
/// Returns true if the foreground window covers the entire screen (fullscreen game/app).
/// Checks both screen coverage AND window style — a maximized window with a title bar
/// (browser, editor) is NOT fullscreen. Only borderless/exclusive fullscreen counts.
/// </summary>
internal static bool IsForegroundFullscreen()
{
var hwnd = GetForegroundWindow();
if (hwnd == IntPtr.Zero) return false;
if (!GetWindowRect(hwnd, out var rect)) return false;
var screen = System.Windows.Forms.Screen.FromHandle(hwnd);
bool coversScreen = rect.Left <= screen.Bounds.Left
&& rect.Top <= screen.Bounds.Top
&& rect.Right >= screen.Bounds.Right
&& rect.Bottom >= screen.Bounds.Bottom;
if (!coversScreen) return false;
// A real fullscreen app (game) has no title bar (WS_CAPTION).
// Maximized windows with taskbar auto-hide still have WS_CAPTION.
const int GWL_STYLE = -16;
const uint WS_CAPTION = 0x00C00000;
uint style = (uint)GetWindowLongPtr(hwnd, GWL_STYLE);
bool hasCaption = (style & WS_CAPTION) == WS_CAPTION;
return !hasCaption;
}
[DllImport("user32.dll")]
private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
internal static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
[DllImport("user32.dll")]
internal static extern bool LockWorkStation();
[DllImport("user32.dll")]
internal static extern bool ExitWindowsEx(uint uFlags, uint dwReason);
[DllImport("user32.dll")]
internal static extern short VkKeyScan(char ch);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool DestroyIcon(IntPtr hIcon);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern uint RegisterWindowMessage(string lpString);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool RegisterRawInputDevices(
[In] RAWINPUTDEVICE[] pRawInputDevices,
uint uiNumDevices,
uint cbSize);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetRawInputData(
IntPtr hRawInput,
uint uiCommand,
IntPtr pData,
ref uint pcbSize,
uint cbSizeHeader);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern uint GetRawInputDeviceInfo(
IntPtr hDevice,
uint uiCommand,
IntPtr pData,
ref uint pcbSize);
internal const int WM_INPUT = 0x00FF;
internal const uint RID_INPUT = 0x10000003;
internal const uint RIDI_DEVICENAME = 0x20000007;
internal const uint RIM_TYPEKEYBOARD = 1;
internal const uint RIDEV_INPUTSINK = 0x00000100;
internal const uint RIDEV_DEVNOTIFY = 0x00002000;
internal const int WM_KEYDOWN = 0x0100;
internal const int WM_KEYUP = 0x0101;
internal const int WM_SYSKEYDOWN = 0x0104;
internal const int WM_SYSKEYUP = 0x0105;
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTDEVICE
{
public ushort usUsagePage;
public ushort usUsage;
public uint dwFlags;
public IntPtr hwndTarget;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUTHEADER
{
public uint dwType;
public uint dwSize;
public IntPtr hDevice;
public IntPtr wParam;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWKEYBOARD
{
public ushort MakeCode;
public ushort Flags;
public ushort Reserved;
public ushort VKey;
public uint Message;
public uint ExtraInformation;
}
[StructLayout(LayoutKind.Explicit)]
internal struct RAWINPUTUNION
{
[FieldOffset(0)]
public RAWKEYBOARD keyboard;
}
[StructLayout(LayoutKind.Sequential)]
internal struct RAWINPUT
{
public RAWINPUTHEADER header;
public RAWINPUTUNION data;
}
// PowrProf.dll — replaces WinForms Application.SetSuspendState
[DllImport("PowrProf.dll", SetLastError = true)]
internal static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent);
// DisplayConfig API — get friendly monitor names (e.g. "DELL U2723QE")
[DllImport("user32.dll")]
internal static extern int GetDisplayConfigBufferSizes(uint flags, out uint numPathArrayElements, out uint numModeInfoArrayElements);
[DllImport("user32.dll")]
internal static extern int QueryDisplayConfig(uint flags,
ref uint numPathArrayElements, [Out] DISPLAYCONFIG_PATH_INFO[] pathInfoArray,
ref uint numModeInfoArrayElements, [Out] DISPLAYCONFIG_MODE_INFO[] modeInfoArray,
IntPtr currentTopologyId);
[DllImport("user32.dll")]
internal static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_TARGET_DEVICE_NAME requestPacket);
[DllImport("user32.dll")]
internal static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SOURCE_DEVICE_NAME requestPacket);
internal const uint QDC_ONLY_ACTIVE_PATHS = 2;
[StructLayout(LayoutKind.Sequential)]
internal struct LUID { public uint LowPart; public int HighPart; }
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAYCONFIG_PATH_SOURCE_INFO
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public uint statusFlags;
}
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAYCONFIG_PATH_TARGET_INFO
{
public LUID adapterId;
public uint id;
public uint modeInfoIdx;
public uint outputTechnology;
public uint rotation;
public uint scaling;
public DISPLAYCONFIG_RATIONAL refreshRate;
public uint scanLineOrdering;
public bool targetAvailable;
public uint statusFlags;
}
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAYCONFIG_RATIONAL { public uint Numerator; public uint Denominator; }
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAYCONFIG_PATH_INFO
{
public DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo;
public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo;
public uint flags;
}
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAYCONFIG_MODE_INFO
{
public uint infoType;
public uint id;
public LUID adapterId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public byte[] modeInfo;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct DISPLAYCONFIG_TARGET_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
public uint flags;
public uint outputTechnology;
public ushort edidManufactureId;
public ushort edidProductCodeId;
public uint connectorInstance;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string monitorFriendlyDeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string monitorDevicePath;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct DISPLAYCONFIG_SOURCE_DEVICE_NAME
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string viewGdiDeviceName;
}
[StructLayout(LayoutKind.Sequential)]
internal struct DISPLAYCONFIG_DEVICE_INFO_HEADER
{
public uint type;
public uint size;
public LUID adapterId;
public uint id;
}
/// <summary>
/// Get friendly monitor names mapped by GDI device name (e.g. \\.\DISPLAY1 → "DELL U2723QE").
/// </summary>
internal sealed class MonitorDisplayConfigInfo
{
public string GdiDeviceName { get; init; } = "";
public string FriendlyName { get; init; } = "";
public string DevicePath { get; init; } = "";
}
internal static Dictionary<string, MonitorDisplayConfigInfo> GetMonitorDisplayConfigInfo()
{
var result = new Dictionary<string, MonitorDisplayConfigInfo>(StringComparer.OrdinalIgnoreCase);
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, out uint pathCount, out uint modeCount) != 0)
return result;
var paths = new DISPLAYCONFIG_PATH_INFO[pathCount];
var modes = new DISPLAYCONFIG_MODE_INFO[modeCount];
if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero) != 0)
return result;
for (int i = 0; i < pathCount; i++)
{
// Get friendly name from target
var targetName = new DISPLAYCONFIG_TARGET_DEVICE_NAME();
targetName.header.type = 2; // DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME
targetName.header.size = (uint)Marshal.SizeOf<DISPLAYCONFIG_TARGET_DEVICE_NAME>();
targetName.header.adapterId = paths[i].targetInfo.adapterId;
targetName.header.id = paths[i].targetInfo.id;
if (DisplayConfigGetDeviceInfo(ref targetName) != 0)
continue;
// Get GDI device name from source
var sourceName = new DISPLAYCONFIG_SOURCE_DEVICE_NAME();
sourceName.header.type = 1; // DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME
sourceName.header.size = (uint)Marshal.SizeOf<DISPLAYCONFIG_SOURCE_DEVICE_NAME>();
sourceName.header.adapterId = paths[i].sourceInfo.adapterId;
sourceName.header.id = paths[i].sourceInfo.id;
if (DisplayConfigGetDeviceInfo(ref sourceName) != 0)
continue;
var friendly = targetName.monitorFriendlyDeviceName?.Trim('\0');
var devicePath = targetName.monitorDevicePath?.Trim('\0');
var gdiName = sourceName.viewGdiDeviceName?.Trim('\0');
if (!string.IsNullOrEmpty(gdiName))
{
result[gdiName] = new MonitorDisplayConfigInfo
{
GdiDeviceName = gdiName,
FriendlyName = friendly ?? "",
DevicePath = devicePath ?? "",
};
}
}
return result;
}
/// <summary>
/// Get friendly monitor names mapped by GDI device name (e.g. \\.\DISPLAY1 -> "DELL U2723QE").
/// </summary>
internal static Dictionary<string, string> GetMonitorFriendlyNames()
=> GetMonitorDisplayConfigInfo()
.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value.FriendlyName))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.FriendlyName, StringComparer.OrdinalIgnoreCase);
}