Skip to content

Commit

Permalink
Add dark mode title bar support for Windows 10
Browse files Browse the repository at this point in the history
  • Loading branch information
mogemimi committed Oct 3, 2020
1 parent 28aa3b5 commit b778796
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 1 deletion.
8 changes: 8 additions & 0 deletions build/pomdog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ target_link_libraries(pomdog_static INTERFACE
winmm.lib
shell32.lib
ws2_32.lib

# NOTE: Dark mode theme
UxTheme.lib
Dwmapi.lib

# NOTE: Windows version
Version.lib # NOTE: for GetFileVersionInfoSizeW()

$<$<BOOL:${POMDOG_USE_GL4}>:
opengl32.lib # for OpenGL on Windows
>
Expand Down
2 changes: 2 additions & 0 deletions build/pomdog/PomdogPlatform.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ target_sources(pomdog_static PRIVATE
${POMDOG_SRC_DIR}/Platform.Win32/Bootstrap.cpp
${POMDOG_INC_DIR}/Platform/Win32/Bootstrap.hpp
${POMDOG_INC_DIR}/Platform/Win32/BootstrapSettingsWin32.hpp
${POMDOG_SRC_DIR}/Platform.Win32/DarkMode.cpp
${POMDOG_SRC_DIR}/Platform.Win32/DarkMode.hpp
${POMDOG_SRC_DIR}/Platform.Win32/GameHostWin32.cpp
${POMDOG_SRC_DIR}/Platform.Win32/GameHostWin32.hpp
${POMDOG_SRC_DIR}/Platform.Win32/GameWindowWin32.cpp
Expand Down
147 changes: 147 additions & 0 deletions src/Platform.Win32/DarkMode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) 2013-2020 mogemimi. Distributed under the MIT license.

#include "DarkMode.hpp"
#include <dwmapi.h>
#include <uxtheme.h>
#include <vector>

namespace Pomdog::Detail::Win32 {
namespace {

bool IsHighContrast() noexcept
{
HIGHCONTRASTW hc;
hc.cbSize = sizeof hc;

bool highContrast = false;
if (::SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(hc), &hc, 0)) {
highContrast = ((hc.dwFlags & HCF_HIGHCONTRASTON) != 0);
}
return highContrast;
}

bool ShouldAppsUseDarkMode() noexcept
{
if (!IsWindowsVersionOrGreaterForWindows10(10, 0, 0)) {
return false;
}

constexpr auto UXTHEME_DLL_NAME = L"uxtheme.dll";

auto uxthemeModule = ::LoadLibraryExW(UXTHEME_DLL_NAME, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (uxthemeModule == nullptr) {
return false;
}

// NOTE: undocumented Windows API
using ShouldAppsUseDarkModeFuncType = BOOLEAN(WINAPI*)(void);
constexpr WORD uxthemeShouldAppsUseDarkModeOrdinal = 132;
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4191)
#endif
auto shouldAppsUseDarkModeFunc = reinterpret_cast<ShouldAppsUseDarkModeFuncType>(
::GetProcAddress(uxthemeModule, MAKEINTRESOURCEA(uxthemeShouldAppsUseDarkModeOrdinal)));
#if defined(_MSC_VER)
#pragma warning(pop)
#endif

bool darkmode = false;
if (shouldAppsUseDarkModeFunc != nullptr) {
darkmode = shouldAppsUseDarkModeFunc();
}
::FreeLibrary(uxthemeModule);
return darkmode;
}

} // namespace

bool IsWindowsVersionOrGreaterForWindows10(WORD majorVersion, WORD minorVersion, WORD buildVersion) noexcept
{
#if 0
return ::IsWindowsVersionOrGreater(majorVersion, minorVersion, buildVersion);
#elif 0
OSVERSIONINFOEXA versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
versionInfo.dwMajorVersion = majorVersion;
versionInfo.dwMinorVersion = minorVersion;
versionInfo.dwBuildNumber = buildVersion;
ULONGLONG conditionMask = 0;
VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(conditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
return ::VerifyVersionInfoA(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, conditionMask) == 0;
#else
constexpr const char path[128] = "C:\\Windows\\System32\\kernel32.dll";
[[maybe_unused]] DWORD dummyHandle = 0;
DWORD fileVersionSize = ::GetFileVersionInfoSizeA(path, &dummyHandle);
std::vector<LPBYTE> versionInfo(fileVersionSize);

if (::GetFileVersionInfoA(path, 0, fileVersionSize, versionInfo.data()) == 0) {
return false;
}

UINT bufferSize = 0;
VS_FIXEDFILEINFO* fileInfo = nullptr;
if (::VerQueryValueA(versionInfo.data(), "\\", reinterpret_cast<LPVOID*>(&fileInfo), &bufferSize) == 0) {
return false;
}
if (bufferSize == 0) {
return false;
}

const auto windowsMajor = HIWORD(fileInfo->dwProductVersionMS);
const auto windowsMinor = LOWORD(fileInfo->dwProductVersionMS);
const auto windowsBuild = HIWORD(fileInfo->dwProductVersionLS);

if (majorVersion < windowsMajor) {
return true;
}
if (majorVersion == windowsMajor) {
if (minorVersion < windowsMinor) {
return true;
}
if (minorVersion == windowsMinor) {
return buildVersion <= windowsBuild;
}
}
return false;
#endif
}

bool IsDarkMode() noexcept
{
return ShouldAppsUseDarkMode() && !IsHighContrast();
}

std::shared_ptr<Error> UseImmersiveDarkMode(HWND windowHandle, bool enabled) noexcept
{
if (!IsWindowsVersionOrGreaterForWindows10(10, 0, 17763)) {
// NOTE: not support for dark mode
return nullptr;
}

if (auto hr = ::SetWindowTheme(windowHandle, L"DarkMode_Explorer", nullptr); FAILED(hr)) {
return Errors::New("SetWindowTheme() failed.");
}

constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
constexpr DWORD DWMWA_USE_IMMERSIVE_DARK_MODE = 20;

DWORD attribute = 0;
if (IsWindowsVersionOrGreaterForWindows10(10, 0, 18985)) {
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE;
}
else {
attribute = DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1;
}

const BOOL useDarkMode = enabled ? TRUE : FALSE;
if (auto hr = ::DwmSetWindowAttribute(windowHandle, attribute, &useDarkMode, sizeof(useDarkMode)); FAILED(hr)) {
return Errors::New("DwmSetWindowAttribute(..., DWMWA_USE_IMMERSIVE_DARK_MODE, ...) failed.");
}

return nullptr;
}

} // namespace Pomdog::Detail::Win32
17 changes: 17 additions & 0 deletions src/Platform.Win32/DarkMode.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2013-2020 mogemimi. Distributed under the MIT license.

#pragma once

#include "Pomdog/Platform/Win32/PrerequisitesWin32.hpp"
#include "Pomdog/Utility/Errors.hpp"
#include <memory>

namespace Pomdog::Detail::Win32 {

bool IsWindowsVersionOrGreaterForWindows10(WORD majorVersion, WORD minorVersion, WORD buildVersion) noexcept;

bool IsDarkMode() noexcept;

std::shared_ptr<Error> UseImmersiveDarkMode(HWND windowHandle, bool enabled) noexcept;

} // namespace Pomdog::Detail::Win32
12 changes: 11 additions & 1 deletion src/Platform.Win32/GameWindowWin32.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2013-2020 mogemimi. Distributed under the MIT license.

#include "GameWindowWin32.hpp"
#include "DarkMode.hpp"
#include "../Application/SystemEvents.hpp"
#include "Pomdog/Application/MouseCursor.hpp"
#include "Pomdog/Graphics/PresentationParameters.hpp"
Expand Down Expand Up @@ -76,7 +77,7 @@ void RegisterInputDevices(HWND windowHandle)
}
}

} // unnamed namespace
} // namespace

class GameWindowWin32::Impl final {
public:
Expand Down Expand Up @@ -210,6 +211,15 @@ GameWindowWin32::Impl::Impl(
// "Win32GameWindow::Impl::Initialize");
}

if (IsDarkMode()) {
if (auto err = UseImmersiveDarkMode(windowHandle, true); err != nullptr) {
///@todo Not implemented
//POMDOG_THROW_EXCEPTION(ExceptionCode::RuntimeAssertionFailed,
// "failed to use dark mode.",
// "Win32GameWindow::Impl::Initialize");
}
}

///@note See http://msdn.microsoft.com/ja-jp/library/ff485844(v=vs.85).aspx
//if (FAILED(::CoInitialize(0)))
if (FAILED(::CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))) {
Expand Down

0 comments on commit b778796

Please sign in to comment.