view src/sys/win32/dark_theme.cc @ 379:5eaafed6c10b default tip

*: clang-format
author Paper <paper@tflc.us>
date Wed, 05 Nov 2025 12:59:46 -0500
parents 862d0d8619f6
children
line wrap: on
line source

#include "sys/win32/dark_theme.h"

#include <QApplication>
#include <QOperatingSystemVersion>
#include <QSettings>
#include <QWidget>

#include <iostream>
#include <memory>

#include <dwmapi.h>

struct LibraryDeconstructor {
	using pointer = HINSTANCE;
	void operator()(pointer t) const { ::FreeLibrary(t); };
};

using Library = std::unique_ptr<HINSTANCE, LibraryDeconstructor>;

class Dwmapi {
public:
	Dwmapi()
	{
		/* load functions */
		library.reset(::LoadLibraryW(L"dwmapi.dll"));
		set_wind_attrib = reinterpret_cast<decltype(::DwmSetWindowAttribute) *>(
		    GetProcAddress(library.get(), "DwmSetWindowAttribute"));
	}

	HRESULT SetWindowAttribute(HWND hWnd, DWORD key, LPCVOID data, DWORD sz_data)
	{
		if (!library.get())
			return E_POINTER;

		/* GCC throws a fit here because C++ lacks a "generic" function pointer type.
		 * Ignore. */
		if (!set_wind_attrib)
			return E_POINTER;

		return set_wind_attrib(hWnd, key, data, sz_data);
	}

protected:
	Library library = nullptr;
	decltype(::DwmSetWindowAttribute) *set_wind_attrib;
};

Dwmapi dwmapi;

namespace win32 {

bool SetTitleBarToBlack(QWidget *win, bool enabled)
{
	/* 19 and 20 are *both* DWMWA_USE_IMMERSIVE_DARK_MODE.
	 *
	 * It's 20 on newer versions of windows (i.e. win11 and late win10),
	 * but it's 19 on very old versions of win10 nobody ought to be using anymore.
	 */
	static constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_OLD = 19;
	static constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE = 20;

	BOOL b = enabled;

	{
		HRESULT result = dwmapi.SetWindowAttribute(reinterpret_cast<HWND>(win->winId()), DWMWA_USE_IMMERSIVE_DARK_MODE,
		                                           &b, sizeof(b));
		if (result == S_OK)
			return b;
	}

	{
		HRESULT result = dwmapi.SetWindowAttribute(reinterpret_cast<HWND>(win->winId()),
		                                           DWMWA_USE_IMMERSIVE_DARK_MODE_OLD, &b, sizeof(b));
		if (result == S_OK)
			return b;
	}

	return b;
}

void SetTitleBarsToBlack(bool enabled)
{
	for (QWidget *widget : qApp->topLevelWidgets()) {
		SetTitleBarToBlack(widget, enabled);
	}
}

bool DarkThemeAvailable()
{
	const auto &ver = QOperatingSystemVersion::current();
	return (ver.majorVersion() > 10) ? true : (ver.majorVersion() == 10 && ver.microVersion() >= 17763);
}

bool IsInDarkTheme()
{
	QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
	                   QSettings::NativeFormat);
	return settings.value("AppsUseLightTheme", 1).toInt() == 0;
}

} // namespace win32