-
Notifications
You must be signed in to change notification settings - Fork 5
/
RudeWindowFixer.c
136 lines (105 loc) · 6.06 KB
/
RudeWindowFixer.c
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
#include <Windows.h>
#include <TraceLoggingProvider.h>
TRACELOGGING_DEFINE_PROVIDER(RudeWindowFixer_traceloggingProvider, "RudeWindowFixer", (0xf7a4605a, 0x5eba, 0x46a2, 0x8a, 0x75, 0xd7, 0xe2, 0xfe, 0xcc, 0x8d, 0x62));
static UINT RudeWindowFixer_shellhookMessage;
#define HSHELL_UNDOCUMENTED_FULLSCREEN_ENTER 0x35
#define HSHELL_UNDOCUMENTED_FULLSCREEN_EXIT 0x36
static __declspec(noreturn) void RudeWindowFixer_Error(LPCWSTR text) {
TraceLoggingWrite(RudeWindowFixer_traceloggingProvider, "Error", TraceLoggingWideString(text, "ErrorMessage"));
MessageBoxW(NULL, text, L"RudeWindowFixer error", MB_ICONERROR);
exit(EXIT_FAILURE);
}
// TODO: this is a "shotgun" approach that broadcasts to all applications for simplicity. This presumably wakes up a ton of processes, which is inefficient. A cleaner approach could be to locate the specific window CGlobalRudeWindowManager is listening on.
static void RudeWindowFixer_BroadcastShellHookMessage(WPARAM wParam, LPARAM lParam) {
DWORD recipients = BSM_APPLICATIONS;
if (BroadcastSystemMessage(BSF_POSTMESSAGE | BSF_IGNORECURRENTTASK, &recipients, RudeWindowFixer_shellhookMessage, wParam, lParam) < 0)
RudeWindowFixer_Error(L"BroadcastSystemMessage() failed");
}
static BOOL CALLBACK RudeWindowFixer_AdjustWindows_EnumWindowsProc(_In_ HWND window, _In_ LPARAM lParam) {
UNREFERENCED_PARAMETER(lParam);
if (!IsWindowVisible(window)) return TRUE;
const wchar_t* const nonRudeProp = L"NonRudeHWND";
const LONG_PTR extendedWindowStyles = GetWindowLongPtrW(window, GWL_EXSTYLE);
if (!(
// Some sneaky full screen windows (e.g. NVIDIA GeForce Overlay) have WS_EX_LAYERED | WS_EX_NOACTIVATE.
// Others (e.g. NVIDIA GeForce Overlay DT) have WS_EX_LAYERED | WS_EX_TRANSPARENT.
// These criteria are probably not foolproof, but it's a start.
(extendedWindowStyles & WS_EX_LAYERED) && ((extendedWindowStyles & WS_EX_TRANSPARENT) || (extendedWindowStyles & WS_EX_NOACTIVATE)) &&
GetPropW(window, nonRudeProp) == NULL
)) return TRUE;
TraceLoggingWrite(RudeWindowFixer_traceloggingProvider, "SettingNonRudeHWND", TraceLoggingPointer(window, "HWND"), TraceLoggingIntPtr(extendedWindowStyles, "ExtendedWindowStyles"));
const wchar_t* const propComment = L"NonRudeHWND was set by https://github.com/dechamps/RudeWindowFixer";
SetPropW(window, propComment, INVALID_HANDLE_VALUE);
SetPropW(window, nonRudeProp, INVALID_HANDLE_VALUE);
// Just in case we set the property too late, also tell the Rude Window Manager to remove the window from its set of full screen windows.
RudeWindowFixer_BroadcastShellHookMessage(HSHELL_UNDOCUMENTED_FULLSCREEN_EXIT, (LPARAM)window);
return TRUE;
}
static void RudeWindowFixer_AdjustWindows(void) {
if (EnumWindows(RudeWindowFixer_AdjustWindows_EnumWindowsProc, 0) == 0)
RudeWindowFixer_Error(L"EnumWindows() failed");
}
static LRESULT CALLBACK RudeWindowFixer_WindowProcedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
TraceLoggingWrite(RudeWindowFixer_traceloggingProvider, "ReceivedMessage", TraceLoggingHexUInt32(uMsg, "uMsg"), TraceLoggingHexUInt64(wParam, "wParam"), TraceLoggingHexUInt64(lParam, "lParam"));
const UINT_PTR timerId = 1;
if (uMsg == WM_TIMER) KillTimer(hWnd, timerId);
if (uMsg == RudeWindowFixer_shellhookMessage) {
// This will reset the timer if it's already running, which probably makes the most sense - we only want to trigger recalculation after things have settled.
if (SetTimer(hWnd, /*nIDEvent=*/timerId, /*uElapse=*/50, /*lpTimerFunc=*/NULL) == 0)
RudeWindowFixer_Error(L"SetTimer() failed");
}
if (uMsg == WM_CREATE || uMsg == WM_TIMER) {
// Note: we always go through and check *all* visible windows every time.
// An alternative would be to listen for HSHELL_UNDOCUMENTED_FULLSCREEN_ENTER and only check the window that just entered full screen,
// but that won't work with windows that become transparent *after* they become full screen (e.g. NVIDIA GeForce Overlay DT)
RudeWindowFixer_AdjustWindows();
// Broadcast a dummy HSHELL_MONITORCHANGED message. This message will trigger CGlobalRudeWindowManager to recalculate its state.
// According to reverse engineering, HSHELL_MONITORCHANGED is the message that is processed in the most direct, straightforward, side-effect-free manner by CGlobalRudeWindowManager.
// TODO: if AdjustWindows() already sent a full screen exit message, then this is redundant.
RudeWindowFixer_BroadcastShellHookMessage(HSHELL_MONITORCHANGED, 0);
}
TraceLoggingWrite(RudeWindowFixer_traceloggingProvider, "Done");
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
int APIENTRY WinMain(_In_ HINSTANCE hInst, _In_opt_ HINSTANCE hInstPrev, _In_ PSTR cmdline, _In_ int cmdshow) {
UNREFERENCED_PARAMETER(hInstPrev);
UNREFERENCED_PARAMETER(cmdline);
UNREFERENCED_PARAMETER(cmdshow);
TraceLoggingRegister(RudeWindowFixer_traceloggingProvider);
RudeWindowFixer_shellhookMessage = RegisterWindowMessageW(L"SHELLHOOK");
if (RudeWindowFixer_shellhookMessage == 0)
RudeWindowFixer_Error(L"RegisterWindowMessageW() failed");
TraceLoggingWrite(RudeWindowFixer_traceloggingProvider, "Starting", TraceLoggingHexUInt32(RudeWindowFixer_shellhookMessage, "ShellhookMessage"));
WNDCLASSEXW windowClass = { 0 };
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.lpfnWndProc = RudeWindowFixer_WindowProcedure;
windowClass.lpszClassName = L"RudeWindowFixer";
if (RegisterClassExW(&windowClass) == 0)
RudeWindowFixer_Error(L"RegisterClassExW() failed");
const HWND window = CreateWindowW(
/*lpClassName=*/L"RudeWindowFixer",
/*lpWindowName=*/L"RudeWindowFixer",
/*dwStyle=*/0,
/*X=*/CW_USEDEFAULT,
/*Y=*/CW_USEDEFAULT,
/*nWidth=*/CW_USEDEFAULT,
/*nHeight=*/CW_USEDEFAULT,
/*hWndParent=*/HWND_MESSAGE,
/*hMenu=*/NULL,
/*hInstance=*/hInst,
/*lpParam=*/NULL
);
if (window == NULL)
RudeWindowFixer_Error(L"CreateWindowW() failed");
if (!RegisterShellHookWindow(window))
RudeWindowFixer_Error(L"RegisterShellHookWindow() failed");
for (;;)
{
MSG message;
BOOL result = GetMessage(&message, NULL, 0, 0);
if (result == -1)
RudeWindowFixer_Error(L"GetMessage() failed");
if (result == 0) return 0;
DispatchMessage(&message);
}
}