diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp
index b960d4fa5e..a68d24e2b0 100644
--- a/Windows/MainWindow.cpp
+++ b/Windows/MainWindow.cpp
@@ -57,6 +57,7 @@
#include "Windows/GEDebugger/GEDebugger.h"
#endif
#include "Windows/W32Util/DarkMode.h"
+#include "Windows/W32Util/UAHMenuBar.h"
#include "Windows/Debugger/Debugger_Disasm.h"
#include "Windows/Debugger/Debugger_MemoryDlg.h"
@@ -730,6 +731,11 @@ namespace MainWindow
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ LRESULT darkResult = 0;
+ if (UAHDarkModeWndProc(hWnd, message, wParam, lParam, &darkResult)) {
+ return darkResult;
+ }
+
switch (message) {
case WM_CREATE:
if (!DoesVersionMatchWindows(6, 0, 0, 0, true)) {
diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj
index eedcf2fa90..58acec5cc9 100644
--- a/Windows/PPSSPP.vcxproj
+++ b/Windows/PPSSPP.vcxproj
@@ -248,7 +248,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/x86/lib
true
Windows
@@ -287,7 +287,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/x86_64/lib
true
$(OutDir)$(ProjectName).pdb
@@ -322,7 +322,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/aarch64/lib
true
$(OutDir)$(ProjectName).pdb
@@ -355,7 +355,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/arm/lib
true
$(OutDir)$(ProjectName).pdb
@@ -392,7 +392,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/x86/lib;%(AdditionalLibraryDirectories)
$(OutDir)$(TargetName)$(TargetExt)
true
@@ -440,7 +440,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;opengl32.lib;glu32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/x86_64/lib;%(AdditionalLibraryDirectories)
true
Windows
@@ -482,7 +482,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/aarch64/lib;%(AdditionalLibraryDirectories)
true
Windows
@@ -522,7 +522,7 @@
$(EXTERNAL_COMPILE_OPTIONS)
- mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
+ uxtheme.lib;mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;%(AdditionalDependencies)
../ffmpeg/Windows/arm/lib;%(AdditionalLibraryDirectories)
true
Windows
@@ -940,6 +940,7 @@
true
true
+
@@ -1495,6 +1496,7 @@
+
@@ -1766,4 +1768,4 @@
-
+
\ No newline at end of file
diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters
index e6f148b185..312983befe 100644
--- a/Windows/PPSSPP.vcxproj.filters
+++ b/Windows/PPSSPP.vcxproj.filters
@@ -277,6 +277,9 @@
Windows\W32Util
+
+ Windows\W32Util
+
@@ -565,6 +568,9 @@
Windows\W32Util
+
+ Windows\W32Util
+
@@ -832,4 +838,4 @@
Other Platforms\SDL
-
+
\ No newline at end of file
diff --git a/Windows/W32Util/UAHMenuBar.cpp b/Windows/W32Util/UAHMenuBar.cpp
new file mode 100644
index 0000000000..53c87b863b
--- /dev/null
+++ b/Windows/W32Util/UAHMenuBar.cpp
@@ -0,0 +1,111 @@
+#include "Common/CommonWindows.h"
+
+#include
+#include
+
+#include "Windows/W32Util/UAHMenuBar.h"
+
+static HTHEME g_menuTheme = nullptr;
+
+// processes messages related to UAH / custom menubar drawing.
+// return true if handled, false to continue with normal processing in your wndproc
+bool UAHDarkModeWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *lr)
+{
+ switch (message)
+ {
+ case WM_UAHDRAWMENU:
+ {
+ UAHMENU *pUDM = (UAHMENU *)lParam;
+ RECT rc = { 0 };
+
+ // get the menubar rect
+ {
+ MENUBARINFO mbi = { sizeof(mbi) };
+ GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
+
+ RECT rcWindow;
+ GetWindowRect(hWnd, &rcWindow);
+
+ // the rcBar is offset by the window rect
+ rc = mbi.rcBar;
+ OffsetRect(&rc, -rcWindow.left, -rcWindow.top);
+
+ rc.top -= 1;
+ }
+
+ if (!g_menuTheme) {
+ g_menuTheme = OpenThemeData(hWnd, L"Menu");
+ }
+
+ DrawThemeBackground(g_menuTheme, pUDM->hdc, MENU_POPUPITEM, MPI_NORMAL, &rc, nullptr);
+
+ return true;
+ }
+ case WM_UAHDRAWMENUITEM:
+ {
+ UAHDRAWMENUITEM *pUDMI = (UAHDRAWMENUITEM *)lParam;
+
+ // get the menu item string
+ wchar_t menuString[256] = { 0 };
+ MENUITEMINFO mii = { sizeof(mii), MIIM_STRING };
+ {
+ mii.dwTypeData = menuString;
+ mii.cch = (sizeof(menuString) / 2) - 1;
+
+ GetMenuItemInfo(pUDMI->um.hmenu, pUDMI->umi.iPosition, TRUE, &mii);
+ }
+
+ // get the item state for drawing
+
+ DWORD dwFlags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
+
+ int iTextStateID = 0;
+ int iBackgroundStateID = 0;
+ {
+ if ((pUDMI->dis.itemState & ODS_INACTIVE) | (pUDMI->dis.itemState & ODS_DEFAULT)) {
+ // normal display
+ iTextStateID = MPI_NORMAL;
+ iBackgroundStateID = MPI_NORMAL;
+ }
+ if (pUDMI->dis.itemState & ODS_HOTLIGHT) {
+ // hot tracking
+ iTextStateID = MPI_HOT;
+ iBackgroundStateID = MPI_HOT;
+ }
+ if (pUDMI->dis.itemState & ODS_SELECTED) {
+ // clicked -- MENU_POPUPITEM has no state for this, though MENU_BARITEM does
+ iTextStateID = MPI_HOT;
+ iBackgroundStateID = MPI_HOT;
+ }
+ if ((pUDMI->dis.itemState & ODS_GRAYED) || (pUDMI->dis.itemState & ODS_DISABLED)) {
+ // disabled / grey text
+ iTextStateID = MPI_DISABLED;
+ iBackgroundStateID = MPI_DISABLED;
+ }
+ if (pUDMI->dis.itemState & ODS_NOACCEL) {
+ dwFlags |= DT_HIDEPREFIX;
+ }
+ }
+
+ if (!g_menuTheme) {
+ g_menuTheme = OpenThemeData(hWnd, L"Menu");
+ }
+
+ DrawThemeBackground(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iBackgroundStateID, &pUDMI->dis.rcItem, nullptr);
+ DrawThemeText(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iTextStateID, menuString, mii.cch, dwFlags, 0, &pUDMI->dis.rcItem);
+
+ return true;
+ }
+ case WM_THEMECHANGED:
+ {
+ if (g_menuTheme) {
+ CloseThemeData(g_menuTheme);
+ g_menuTheme = nullptr;
+ }
+ // continue processing in main wndproc
+ return false;
+ }
+ default:
+ return false;
+ }
+}
diff --git a/Windows/W32Util/UAHMenuBar.h b/Windows/W32Util/UAHMenuBar.h
new file mode 100644
index 0000000000..95f9c0feb7
--- /dev/null
+++ b/Windows/W32Util/UAHMenuBar.h
@@ -0,0 +1,74 @@
+#pragma once
+
+// MIT license, see LICENSE
+// Copyright(c) 2021 adzm / Adam D. Walling
+
+// processes messages related to UAH / custom menubar drawing.
+// return true if handled, false to continue with normal processing in your wndproc
+bool UAHDarkModeWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr);
+
+// window messages related to menu bar drawing
+#define WM_UAHDESTROYWINDOW 0x0090 // handled by DefWindowProc
+#define WM_UAHDRAWMENU 0x0091 // lParam is UAHMENU
+#define WM_UAHDRAWMENUITEM 0x0092 // lParam is UAHDRAWMENUITEM
+#define WM_UAHINITMENU 0x0093 // handled by DefWindowProc
+#define WM_UAHMEASUREMENUITEM 0x0094 // lParam is UAHMEASUREMENUITEM
+#define WM_UAHNCPAINTMENUPOPUP 0x0095 // handled by DefWindowProc
+
+// describes the sizes of the menu bar or menu item
+typedef union tagUAHMENUITEMMETRICS
+{
+ // cx appears to be 14 / 0xE less than rcItem's width!
+ // cy 0x14 seems stable, i wonder if it is 4 less than rcItem's height which is always 24 atm
+ struct {
+ DWORD cx;
+ DWORD cy;
+ } rgsizeBar[2];
+ struct {
+ DWORD cx;
+ DWORD cy;
+ } rgsizePopup[4];
+} UAHMENUITEMMETRICS;
+
+// not really used in our case but part of the other structures
+typedef struct tagUAHMENUPOPUPMETRICS
+{
+ DWORD rgcx[4];
+ DWORD fUpdateMaxWidths : 2; // from kernel symbols, padded to full dword
+} UAHMENUPOPUPMETRICS;
+
+// hmenu is the main window menu; hdc is the context to draw in
+typedef struct tagUAHMENU
+{
+ HMENU hmenu;
+ HDC hdc;
+ DWORD dwFlags; // no idea what these mean, in my testing it's either 0x00000a00 or sometimes 0x00000a10
+} UAHMENU;
+
+// menu items are always referred to by iPosition here
+typedef struct tagUAHMENUITEM
+{
+ int iPosition; // 0-based position of menu item in menubar
+ UAHMENUITEMMETRICS umim;
+ UAHMENUPOPUPMETRICS umpm;
+} UAHMENUITEM;
+
+// the DRAWITEMSTRUCT contains the states of the menu items, as well as
+// the position index of the item in the menu, which is duplicated in
+// the UAHMENUITEM's iPosition as well
+typedef struct UAHDRAWMENUITEM
+{
+ DRAWITEMSTRUCT dis; // itemID looks uninitialized
+ UAHMENU um;
+ UAHMENUITEM umi;
+} UAHDRAWMENUITEM;
+
+// the MEASUREITEMSTRUCT is intended to be filled with the size of the item
+// height appears to be ignored, but width can be modified
+typedef struct tagUAHMEASUREMENUITEM
+{
+ MEASUREITEMSTRUCT mis;
+ UAHMENU um;
+ UAHMENUITEM umi;
+} UAHMEASUREMENUITEM;
+