forked from vrcx-team/VRCX
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWinformThemer.cs
192 lines (163 loc) · 6.31 KB
/
WinformThemer.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
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace VRCX
{
//Based off DWMWA_USE_IMMERSIVE_DARK_MODE, documentation: https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
//dwAttribute was 19 before Windows 20H1, 20 after Windows 20H1
internal static class WinformThemer
{
/// <summary>
/// Flash both the window caption and taskbar button.
/// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
/// </summary>
public const uint FLASHW_ALL = 3;
/// <summary>
/// Flash continuously until the window comes to the foreground.
/// </summary>
public const uint FLASHW_TIMERNOFG = 12;
/// <summary>
/// Private holder of current theme
/// </summary>
private static int currentTheme;
/// <summary>
/// Sets the global theme of the app
/// Light = 0
/// Dark = 1
/// </summary>
public static void SetGlobalTheme(int theme)
{
currentTheme = theme;
//Make a seperate list for all current forms (causes issues otherwise)
var forms = new List<Form>();
foreach (Form form in Application.OpenForms)
{
forms.Add(form);
}
SetThemeToGlobal(forms);
}
/// <summary>
/// Gets the global theme of the app
/// Light = 0
/// Dark = 1
/// </summary>
public static int GetGlobalTheme()
{
return currentTheme;
}
/// <summary>
/// Set given form to the current global theme
/// </summary>
/// <param name="form"></param>
public static void SetThemeToGlobal(Form form)
{
SetThemeToGlobal(new List<Form> { form });
}
/// <summary>
/// Set a list of given forms to the current global theme
/// </summary>
/// <param name="forms"></param>
public static void SetThemeToGlobal(List<Form> forms)
{
MainForm.Instance.Invoke(new Action(() =>
{
//For each form, set the theme, then move focus onto it to force refresh
foreach (var form in forms)
{
//Set the theme of the window
SetThemeToGlobal(form.Handle);
//Change opacity to foce full redraw
form.Opacity = 0.99999;
form.Opacity = 1;
}
}));
}
private static void SetThemeToGlobal(IntPtr handle)
{
if (GetTheme(handle) != currentTheme)
{
if (PInvoke.DwmSetWindowAttribute(handle, 19, new[] { currentTheme }, 4) != 0)
PInvoke.DwmSetWindowAttribute(handle, 20, new[] { currentTheme }, 4);
}
}
private static int GetTheme(IntPtr handle)
{
//Allocate needed memory
var curThemePtr = Marshal.AllocHGlobal(4);
//See what window state it currently is
if (PInvoke.DwmGetWindowAttribute(handle, 19, curThemePtr, 4) != 0)
PInvoke.DwmGetWindowAttribute(handle, 20, curThemePtr, 4);
//Read current theme (light = 0, dark = 1)
var theme = Marshal.ReadInt32(curThemePtr);
//Free previously allocated
Marshal.FreeHGlobal(curThemePtr);
return theme;
}
public static void DoFunny()
{
foreach (Form form in Application.OpenForms)
{
PInvoke.SetWindowLong(form.Handle, -20, 0x00C00000);
// PInvoke.SetWindowLong(form.Handle, -20, 0x00050100);
}
}
private static FLASHWINFO Create_FLASHWINFO(IntPtr handle, uint flags, uint count, uint timeout)
{
var fi = new FLASHWINFO();
fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
fi.hwnd = handle;
fi.dwFlags = flags;
fi.uCount = count;
fi.dwTimeout = timeout;
return fi;
}
/// <summary>
/// Flash the spacified Window (Form) until it receives focus.
/// </summary>
/// <param name="form">The Form (Window) to Flash.</param>
/// <returns></returns>
public static bool Flash(Form form)
{
var fi = Create_FLASHWINFO(form.Handle, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
return PInvoke.FlashWindowEx(ref fi);
}
internal static class PInvoke
{
[DllImport("DwmApi")]
internal static extern int DwmSetWindowAttribute(IntPtr hwnd, int dwAttribute, int[] pvAttribute, int cbAttribute);
[DllImport("DwmApi")]
internal static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, IntPtr pvAttribute, int cbAttribute);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
}
[StructLayout(LayoutKind.Sequential)]
internal struct FLASHWINFO
{
/// <summary>
/// The size of the structure in bytes.
/// </summary>
public uint cbSize;
/// <summary>
/// A Handle to the Window to be Flashed. The window can be either opened or minimized.
/// </summary>
public IntPtr hwnd;
/// <summary>
/// The Flash Status.
/// </summary>
public uint dwFlags;
/// <summary>
/// The number of times to Flash the window.
/// </summary>
public uint uCount;
/// <summary>
/// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink
/// rate.
/// </summary>
public uint dwTimeout;
}
}
}