WUIsBack/nsisplugin/DialogInit.c

378 lines
11 KiB
C

#include "stdafx.h"
#undef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_VISTA
#include <windows.h>
#include <nsis/pluginapi.h>
#include <shlwapi.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <windowsx.h>
#include "main.h"
#include "LoadImage.h"
#include "Registry.h"
#include "VersionInfo.h"
#ifndef WM_DWMCOMPOSITIONCHANGED
#define WM_DWMCOMPOSITIONCHANGED 0x031e
#endif
#define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
#define DWMWA_CAPTION_COLOR 35
#define DWMWA_SYSTEMBACKDROP_TYPE 38
#define DWMWA_COLOR_NONE 0xFFFFFFFE
#define DWMSBT_MAINWINDOW 2
#define IDI_BANNER_WORDMARK_LIGHT 1337
#define IDI_BANNER_WORDMARK_DARK 1338
#define IDI_BANNER_WORDMARK_GLOW 1339
typedef HRESULT (WINAPI *_DwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS *pMarInset);
typedef HRESULT (WINAPI *_DwmIsCompositionEnabled)(BOOL *pfEnabled);
typedef HRESULT (WINAPI *_DwmDefWindowProc)(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *plResult);
typedef HRESULT (WINAPI *_DwmSetWindowAttribute)(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
typedef HRESULT (WINAPI *_SetWindowThemeAttribute)(HWND hwnd, enum WINDOWTHEMEATTRIBUTETYPE eAttribute, PVOID pvAttribute, DWORD cbAttribute);
typedef BOOL (WINAPI *_IsThemeActive)();
typedef HTHEME (WINAPI *_OpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
typedef HRESULT (WINAPI *_CloseThemeData)(HTHEME hTheme);
typedef HRESULT (WINAPI *_DrawThemeBackground)(HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect);
static _DwmExtendFrameIntoClientArea $DwmExtendFrameIntoClientArea;
static _DwmIsCompositionEnabled $DwmIsCompositionEnabled;
static _DwmDefWindowProc $DwmDefWindowProc;
static _DwmSetWindowAttribute $DwmSetWindowAttribute;
static _SetWindowThemeAttribute $SetWindowThemeAttribute;
static _IsThemeActive $IsThemeActive;
static _OpenThemeData $OpenThemeData;
static _CloseThemeData $CloseThemeData;
static _DrawThemeBackground $DrawThemeBackground;
typedef enum Theme {
ThemeUnknown = -1,
ThemeClassic,
ThemeBasic,
ThemeAeroLight,
ThemeAeroDark
} Theme;
static HBITMAP g_bannerWordmark;
static HBITMAP g_bannerWordmarkGlow;
static HTHEME g_aeroTheme;
static Theme g_theme = ThemeUnknown;
static WNDPROC g_dialogOrigWndProc;
static WNDPROC g_bannerOrigWndProc;
static WNDPROC g_bottomOrigWndProc;
static Theme GetTheme() {
BOOL enabled;
if (!$DwmIsCompositionEnabled || !$IsThemeActive || !SUCCEEDED($DwmIsCompositionEnabled(&enabled))) {
return ThemeClassic;
}
if (enabled) {
DWORD light = 1;
if (AtLeastWin10_1809()) {
GetRegistryDword(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", 0, &light);
}
return light ? ThemeAeroLight : ThemeAeroDark;
}
return $IsThemeActive() ? ThemeBasic : ThemeClassic;
}
static void ConfigureWindow() {
HWND bannerWindow = GetDlgItem(g_hwndParent, 1046);
HWND bannerDivider = GetDlgItem(g_hwndParent, 1047);
HWND bottomDivider = GetDlgItem(g_hwndParent, 6900);
if (!bannerWindow || !bannerDivider || !bottomDivider) {
return;
}
Theme theme = GetTheme();
if (g_theme != theme) {
g_theme = theme;
MARGINS margins = {0};
if (theme >= ThemeAeroLight) {
// Set glass area
RECT rect;
GetWindowRect(bannerWindow, &rect);
margins.cyTopHeight = rect.bottom - rect.top;
}
if ($DwmExtendFrameIntoClientArea) {
$DwmExtendFrameIntoClientArea(g_hwndParent, &margins);
}
ShowWindow(bannerDivider, theme >= ThemeBasic ? SW_HIDE : SW_SHOW);
ShowWindow(bottomDivider, theme >= ThemeBasic ? SW_HIDE : SW_SHOW);
if (g_theme >= ThemeBasic) {
DWORD wordmark = g_theme == ThemeAeroDark ? IDI_BANNER_WORDMARK_DARK : IDI_BANNER_WORDMARK_LIGHT;
g_bannerWordmark = LoadPNGResource(NULL, MAKEINTRESOURCE(wordmark), L"PNG");
if (g_theme >= ThemeAeroLight && AtMostWin7()) {
g_bannerWordmarkGlow = LoadPNGResource(NULL, MAKEINTRESOURCE(IDI_BANNER_WORDMARK_GLOW), L"PNG");
}
} else {
DeleteObject(g_bannerWordmark);
DeleteObject(g_bannerWordmarkGlow);
g_bannerWordmark = NULL;
g_bannerWordmarkGlow = NULL;
}
// Set dark mode state
if (AtLeastWin10_1809() && $DwmSetWindowAttribute) {
DWORD attr = AtLeastWin10_2004() ? DWMWA_USE_IMMERSIVE_DARK_MODE : DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
DWORD value = g_theme == ThemeAeroDark;
$DwmSetWindowAttribute(g_hwndParent, attr, &value, sizeof(value));
}
}
}
static LRESULT CALLBACK BannerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (g_theme < ThemeBasic) {
return CallWindowProc(g_bannerOrigWndProc, hwnd, uMsg, wParam, lParam);
}
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
// Draw base color for glass area
FillRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
// Draw Aero Basic titlebar
if (g_theme == ThemeBasic && g_aeroTheme && $DrawThemeBackground) {
int state = GetActiveWindow() == g_hwndParent ? AW_S_TITLEBAR_ACTIVE : AW_S_TITLEBAR_INACTIVE;
$DrawThemeBackground(g_aeroTheme, hdc, AW_TITLEBAR, state, &rect, NULL);
}
float scale = (float)GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f;
BLENDFUNCTION blendFunc;
blendFunc.BlendOp = AC_SRC_OVER;
blendFunc.BlendFlags = 0;
blendFunc.SourceConstantAlpha = 0xFF;
blendFunc.AlphaFormat = AC_SRC_ALPHA;
// Draw wordmark with alpha blending
if (g_bannerWordmarkGlow) {
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, g_bannerWordmarkGlow);
BITMAP bitmap;
GetObject(g_bannerWordmarkGlow, sizeof(bitmap), &bitmap);
LONG width = bitmap.bmWidth * scale;
LONG height = bitmap.bmHeight * scale;
LONG x = (rect.right - rect.left - width) / 2;
LONG y = (rect.bottom - rect.top - height) / 2;
SetStretchBltMode(hdc, HALFTONE);
AlphaBlend(hdc,
x, y, width, height, hdcMem,
0, 0, bitmap.bmWidth, bitmap.bmHeight, blendFunc);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
}
if (g_bannerWordmark) {
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, g_bannerWordmark);
BITMAP bitmap;
GetObject(g_bannerWordmark, sizeof(bitmap), &bitmap);
LONG width = bitmap.bmWidth * scale;
LONG height = bitmap.bmHeight * scale;
LONG x = (rect.right - rect.left - width) / 2;
LONG y = ((rect.bottom - rect.top - height) / 2) - (1 * scale);
SetStretchBltMode(hdc, HALFTONE);
AlphaBlend(hdc,
x, y, width, height, hdcMem,
0, 0, bitmap.bmWidth, bitmap.bmHeight, blendFunc);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
}
EndPaint(hwnd, &ps);
return 0;
}
case WM_ERASEBKGND:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
case WM_NCHITTEST:
// Pass through to parent
return HTTRANSPARENT;
}
if (!g_bannerOrigWndProc) {
return 0;
}
return CallWindowProc(g_bannerOrigWndProc, hwnd, uMsg, wParam, lParam);
}
static LRESULT CALLBACK BottomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
// Draw command area background (grey with divider line)
if (g_theme < ThemeBasic || !g_aeroTheme || !$DrawThemeBackground) {
break;
}
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
$DrawThemeBackground(g_aeroTheme, hdc, AW_COMMANDAREA, 0, &rect, NULL);
EndPaint(hwnd, &ps);
return 0;
}
}
if (!g_bottomOrigWndProc) {
return 0;
}
return CallWindowProc(g_bottomOrigWndProc, hwnd, uMsg, wParam, lParam);
}
static LRESULT CALLBACK MainWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (g_theme >= ThemeAeroLight && $DwmDefWindowProc) {
LRESULT lRet = 0;
if ($DwmDefWindowProc(hwnd, uMsg, wParam, lParam, &lRet)) {
return lRet;
}
}
switch (uMsg) {
case WM_THEMECHANGED:
case WM_DWMCOMPOSITIONCHANGED:
ConfigureWindow();
break;
case WM_ACTIVATE:
case WM_NCACTIVATE:
// Redraw banner on activation
if (g_theme == ThemeBasic) {
InvalidateRect(GetDlgItem(hwnd, 1046), NULL, FALSE);
}
break;
case WM_NCHITTEST: {
if (g_theme < ThemeBasic) {
break;
}
// Allow drag in the header area
HWND bannerWindow = GetDlgItem(hwnd, 1046);
if (!bannerWindow) {
break;
}
POINT hit = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
ScreenToClient(hwnd, &hit);
RECT rect;
GetWindowRect(bannerWindow, &rect);
rect.right -= rect.left;
rect.bottom -= rect.top;
rect.left = 0;
rect.top = 0;
if (PtInRect(&rect, hit)) {
return HTCAPTION;
}
break;
}
}
if (!g_dialogOrigWndProc) {
return 0;
}
return CallWindowProc(g_dialogOrigWndProc, hwnd, uMsg, wParam, lParam);
}
static UINT_PTR NSISPluginCallback(enum NSPIM event) {
// Does nothing, but keeping a callback registered prevents NSIS from unloading the plugin
return 0;
}
PLUGIN_METHOD(DialogInit) {
PLUGIN_INIT();
if (g_dialogOrigWndProc) {
return;
}
extra->RegisterPluginCallback(g_hInstance, NSISPluginCallback);
// Get symbols
HMODULE dwmapi = LoadLibrary(L"dwmapi.dll");
if (dwmapi) {
$DwmExtendFrameIntoClientArea = (_DwmExtendFrameIntoClientArea)GetProcAddress(dwmapi, "DwmExtendFrameIntoClientArea");
$DwmIsCompositionEnabled = (_DwmIsCompositionEnabled)GetProcAddress(dwmapi, "DwmIsCompositionEnabled");
$DwmDefWindowProc = (_DwmDefWindowProc)GetProcAddress(dwmapi, "DwmDefWindowProc");
$DwmSetWindowAttribute = (_DwmSetWindowAttribute)GetProcAddress(dwmapi, "DwmSetWindowAttribute");
}
HMODULE uxtheme = LoadLibrary(L"uxtheme.dll");
if (uxtheme) {
$SetWindowThemeAttribute = (_SetWindowThemeAttribute)GetProcAddress(uxtheme, "SetWindowThemeAttribute");
$IsThemeActive = (_IsThemeActive)GetProcAddress(uxtheme, "IsThemeActive");
$OpenThemeData = (_OpenThemeData)GetProcAddress(uxtheme, "OpenThemeData");
$CloseThemeData = (_CloseThemeData)GetProcAddress(uxtheme, "CloseThemeData");
$DrawThemeBackground = (_DrawThemeBackground)GetProcAddress(uxtheme, "DrawThemeBackground");
}
// Get AeroWizard theme
if ($OpenThemeData) {
g_aeroTheme = $OpenThemeData(g_hwndParent, L"AeroWizard");
}
// Hide title caption/icon
if ($SetWindowThemeAttribute) {
WTA_OPTIONS options;
options.dwFlags = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON;
options.dwMask = WTNCA_NODRAWCAPTION | WTNCA_NODRAWICON;
$SetWindowThemeAttribute(g_hwndParent, WTA_NONCLIENT, &options, sizeof(options));
}
// Enable Acrylic blur
if (AtLeastWin11_21H1() && $DwmSetWindowAttribute) {
// I stole this undocumented 1029 attr from Microsoft Store's StoreInstaller.exe
BOOL modern = AtLeastWin11_22H2();
DWORD attr = modern ? DWMWA_SYSTEMBACKDROP_TYPE : 1029;
DWORD value = modern ? DWMSBT_MAINWINDOW : 1;
$DwmSetWindowAttribute(g_hwndParent, attr, &value, sizeof(value));
// Hide caption background
value = DWMWA_COLOR_NONE;
$DwmSetWindowAttribute(g_hwndParent, DWMWA_CAPTION_COLOR, &value, sizeof(value));
}
// Set up extended client frame
ConfigureWindow();
// Set up window procedures
HWND bannerWindow = GetDlgItem(g_hwndParent, 1046);
HWND bottomWindow = GetDlgItem(g_hwndParent, 6901);
g_dialogOrigWndProc = (WNDPROC)SetWindowLongPtr(g_hwndParent, GWLP_WNDPROC, (LONG_PTR)MainWndProc);
g_bannerOrigWndProc = (WNDPROC)SetWindowLongPtr(bannerWindow, GWLP_WNDPROC, (LONG_PTR)BannerWndProc);
g_bottomOrigWndProc = (WNDPROC)SetWindowLongPtr(bottomWindow, GWLP_WNDPROC, (LONG_PTR)BottomWndProc);
}