Update settin.py
This commit is contained in:
恍兮惚兮 2024-04-14 20:55:06 +08:00
parent 7404c1911c
commit 56594c099e
18 changed files with 367 additions and 258 deletions

View File

@ -23,7 +23,7 @@ from myutils.utils import (
)
from myutils.wrapper import threader
from gui.showword import searchwordW
from myutils.hwnd import pid_running, getpidexe, testprivilege, ListProcess
from myutils.hwnd import getpidexe, testprivilege, ListProcess
from textsource.copyboard import copyboard
from textsource.texthook import texthook
from textsource.ocrtext import ocrtext
@ -42,6 +42,7 @@ import hmac, pytz, uuid
import windows
import re, gobject
import winsharedutils
from winsharedutils import pid_running
from myutils.post import POSTSOLVE
from gui.usefulwidget import Prompt, getQMessageBox

View File

@ -10,6 +10,7 @@ from PyQt5.QtWidgets import (
QApplication,
QPushButton,
)
from winsharedutils import getpidhwndfirst
from PyQt5.QtGui import QStandardItemModel, QStandardItem
import functools
from myutils.config import globalconfig, _TR
@ -21,7 +22,6 @@ from myutils.hwnd import (
ListProcess,
mouseselectwindow,
getExeIcon,
getpidhwndfirst,
)
import qtawesome

View File

@ -6,7 +6,7 @@ from PyQt5.QtCore import Qt
from myutils.config import _TR, globalconfig
from gui.usefulwidget import saveposwindow
from myutils.wrapper import Singleton_close
from myutils.hwnd import showintab
from winsharedutils import showintab
@Singleton_close

View File

@ -50,7 +50,7 @@ from gui.usefulwidget import (
)
from PyQt5.QtCore import QRect, QSize, Qt, pyqtSignal
import os
from myutils.hwnd import showintab
from winsharedutils import showintab
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtCore import Qt, QSize
from myutils.config import savehook_new_list, savehook_new_data, vndbtagdata

View File

@ -31,7 +31,6 @@ from gui.setting_proxy import setTab_proxy
from gui.settingpage7 import setTab7, settab7direct
from gui.settingpage_about import setTab_about, setTab_about_dicrect
from gui.usefulwidget import closeashidewindow
from myutils.hwnd import darkchange
class gridwidget(QWidget):
@ -257,17 +256,25 @@ class Settin(closeashidewindow):
darklight = ["light", "dark"][dark]
class WindowEventFilter(QObject):
def eventFilter(self, obj, event):
def eventFilter(_, obj, event):
if event.type() == QEvent.Type.WinIdChange:
if obj == self.parent():
return False
hwnd = obj.winId()
if hwnd: # window create/destroy,when destroy winId is None
darkchange(int(obj.winId()), dark)
winsharedutils.SetTheme(
int(obj.winId()), dark, globalconfig["WindowBackdrop"]
)
return False
self.__filter = WindowEventFilter() # keep ref
QApplication.instance().installEventFilter(self.__filter)
for widget in QApplication.topLevelWidgets():
darkchange(int(widget.winId()), dark)
if widget == self.parent():
continue
winsharedutils.SetTheme(
int(widget.winId()), dark, globalconfig["WindowBackdrop"]
)
try:
idx = globalconfig[darklight + "theme"] - int(not dark)

View File

@ -12,7 +12,7 @@ from gui.inputdialog import multicolorset
from myutils.config import globalconfig, _TR, _TRL, magpie_config, static_data
from myutils.wrapper import Singleton
import qtawesome, gobject, json
from myutils.hwnd import showintab
from winsharedutils import showintab
from gui.inputdialog import getsomepath1
from gui.usefulwidget import (
getsimplecombobox,
@ -527,6 +527,18 @@ def setTabThree_lazy(self):
5,
),
],
[
("WindowBackdrop", 6),
(
getsimplecombobox(
["Solid", "Acrylic", "Mica", "MicaAlt"],
globalconfig,
"WindowBackdrop",
callback=lambda _: self.setstylesheet(),
),
5,
),
],
]
innermagpie = [

View File

@ -2,7 +2,6 @@ import time
import functools
import threading
import os, sys
import winsharedutils
from PyQt5.QtCore import QT_VERSION_STR
import windows
from traceback import print_exc
@ -17,7 +16,7 @@ import winsharedutils
from myutils.config import globalconfig, saveallconfig, _TR, static_data
from myutils.subproc import endsubprocs
from myutils.ocrutil import ocr_run, imageCut
from myutils.hwnd import mouseselectwindow, showintab, grabwindow, getExeIcon
from myutils.hwnd import mouseselectwindow, grabwindow, getExeIcon
from gui.dialog_savedgame import dialog_savedgame_new
from gui.dialog_memory import dialog_memory
from gui.textbrowser import Textbrowser
@ -25,7 +24,7 @@ from myutils.fullscreen import fullscreen
from gui.rangeselect import moveresizegame, rangeselct_function
from gui.usefulwidget import resizableframeless
from gui.dialog_savedgame import browserdialog
from winsharedutils import showintab
class QUnFrameWindow(resizableframeless):
displayres = pyqtSignal(dict)

View File

@ -27,7 +27,7 @@ from PyQt5.QtWidgets import (
from traceback import print_exc
import qtawesome, functools, gobject
from myutils.wrapper import Singleton
from myutils.hwnd import showintab
from winsharedutils import showintab
@Singleton

View File

@ -1,7 +1,8 @@
import os, json
import windows, winsharedutils
from winsharedutils import letfullscreen, recoverwindow
from myutils.config import globalconfig, magpie_config
from myutils.hwnd import letfullscreen, recoverwindow, ListProcess, injectdll
from myutils.hwnd import ListProcess, injectdll
from traceback import print_exc
from myutils.subproc import subproc_w
import time

View File

@ -8,20 +8,6 @@ import time, winrtutils, winsharedutils, hashlib
from myutils.wrapper import threader
def pid_running(pid):
try:
process = windows.AutoHandle(
windows.OpenProcess(windows.SYNCHRONIZE, False, pid)
)
if process == 0:
return False
ret = windows.WaitForSingleObject(process, 0)
return ret == windows.WAIT_TIMEOUT
except:
return False
@threader
def grabwindow():
@ -124,18 +110,6 @@ def getpidhwndfirst(pid):
return 0
def getwindowlist():
windows_list = []
pidlist = []
windows.EnumWindows(lambda hWnd, param: windows_list.append(hWnd), 0)
for hwnd in windows_list:
try:
pid = windows.GetWindowThreadProcessId(hwnd)
pidlist.append(pid)
except:
pass
return list(set(pidlist))
def getprocesslist():
@ -286,81 +260,3 @@ def mouseselectwindow(callback):
pass
threading.Thread(target=_loop).start()
def letfullscreen(hwnd):
wpc = windows.GetWindowPlacement(hwnd, False)
HWNDStyle = windows.GetWindowLong(hwnd, windows.GWL_STYLE)
HWNDStyleEx = windows.GetWindowLong(hwnd, windows.GWL_EXSTYLE)
NewHWNDStyle = HWNDStyle
NewHWNDStyle &= ~windows.WS_BORDER
NewHWNDStyle &= ~windows.WS_DLGFRAME
NewHWNDStyle &= ~windows.WS_THICKFRAME
NewHWNDStyleEx = HWNDStyleEx
NewHWNDStyleEx &= ~windows.WS_EX_WINDOWEDGE
windows.SetWindowLong(hwnd, windows.GWL_STYLE, NewHWNDStyle | windows.WS_POPUP)
windows.SetWindowLong(
hwnd, windows.GWL_EXSTYLE, NewHWNDStyleEx | windows.WS_EX_TOPMOST
)
windows.ShowWindow(hwnd, windows.SW_SHOWMAXIMIZED)
return (wpc, HWNDStyle, HWNDStyleEx)
def recoverwindow(hwnd, status):
wpc, HWNDStyle, HWNDStyleEx = status
windows.SetWindowLong(hwnd, windows.GWL_STYLE, HWNDStyle)
windows.SetWindowLong(hwnd, windows.GWL_EXSTYLE, HWNDStyleEx)
windows.ShowWindow(hwnd, windows.SW_SHOWNORMAL)
windows.SetWindowPlacement(hwnd, wpc)
def showintab(hwnd, show):
style_ex = windows.GetWindowLong(hwnd, windows.GWL_EXSTYLE)
if show:
style_ex |= windows.WS_EX_APPWINDOW
style_ex &= ~windows.WS_EX_TOOLWINDOW
else:
style_ex &= ~windows.WS_EX_APPWINDOW
style_ex |= windows.WS_EX_TOOLWINDOW
windows.SetWindowLong(hwnd, windows.GWL_EXSTYLE, style_ex)
def darkchange(hwnd, dark):
def ChangeDWMAttrib(hWnd: int, attrib: int, color) -> None:
try:
ctypes.windll.dwmapi.DwmSetWindowAttribute(
hWnd, attrib, ctypes.byref(color), ctypes.sizeof(ctypes.c_int)
)
except:
pass
def systemmenu(mode):
try:
# https://stackoverflow.com/questions/53501268/win10-dark-theme-how-to-use-in-winapi
# https://gist.github.com/rounk-ctrl/b04e5622e30e0d62956870d5c22b7017
LoadLibrary = ctypes.windll.Kernel32.LoadLibraryW
LoadLibrary.argtypes = (ctypes.c_wchar_p,)
LoadLibrary.restype = ctypes.c_void_p
GetProcAddress = ctypes.windll.Kernel32.GetProcAddress
GetProcAddress.argtypes = ctypes.c_void_p, ctypes.c_void_p
GetProcAddress.restype = ctypes.c_void_p
uxtheme = LoadLibrary("uxtheme.dll")
SetPreferredAppMode = GetProcAddress(uxtheme, 135)
FlushMenuThemes = GetProcAddress(uxtheme, 136)
SetPreferredAppMode = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)(
SetPreferredAppMode
)
FlushMenuThemes = ctypes.CFUNCTYPE(None)(FlushMenuThemes)
SetPreferredAppMode(mode)
FlushMenuThemes()
except:
pass
if dark:
ChangeDWMAttrib(hwnd, 19, ctypes.c_int(1))
ChangeDWMAttrib(hwnd, 20, ctypes.c_int(1))
systemmenu(2)
else:
ChangeDWMAttrib(hwnd, 19, ctypes.c_int(0))
ChangeDWMAttrib(hwnd, 20, ctypes.c_int(0))
systemmenu(3)

View File

@ -1,10 +1,10 @@
import threading
import threading, windows
from queue import Queue
import re, os
import time, gobject
from collections import OrderedDict
import codecs, functools
import windows
from winsharedutils import Is64bit
from myutils.config import globalconfig, savehook_new_data, static_data
from textsource.textsourcebase import basetext
from myutils.utils import checkchaos
@ -99,7 +99,7 @@ class texthook(basetext):
self.newline = Queue()
self.newline_delaywait = Queue()
self.is64bit = windows.Is64bit(pids[0])
self.is64bit = Is64bit(pids[0])
self.lock = threading.Lock()
self.hookdatacollecter = OrderedDict()
self.hooktypecollecter = OrderedDict()

View File

@ -345,7 +345,6 @@ def GetProcessFileName(hHandle):
return v
_IsWow64Process = _kernel32.IsWow64Process
_CreateProcessW = _kernel32.CreateProcessW
_CreateProcessW.argtypes = (
c_wchar_p,
@ -391,12 +390,6 @@ def CreateProcess(
return _pinfo
def IsWow64Process(phandle):
b = c_bool()
_IsWow64Process(phandle, byref(b))
return b.value
def GetClipboardOwner():
return _GetClipboardOwner()
@ -870,133 +863,6 @@ def ConnectNamedPipe(pipe, lpoverlap):
return _ConnectNamedPipe(pipe, lpoverlap)
FILE_MAP_READ = 0x4
_OpenFileMappingW = _kernel32.OpenFileMappingW
_OpenFileMappingW.argtypes = c_uint, c_bool, c_wchar_p
def OpenFileMapping(access, inherit, name):
map_handle = _OpenFileMappingW(access, inherit, name)
return map_handle
_MapViewOfFile = _kernel32.MapViewOfFile
_MapViewOfFile.argtypes = c_void_p, c_uint, c_uint, c_uint, c_uint
_MapViewOfFile.restype = c_void_p
def MapViewOfFile(fhandel, access, size):
return _MapViewOfFile(fhandel, access, 0, 0, size)
_CreateFileMappingW = _kernel32.CreateFileMappingW
_CreateFileMappingW.argtypes = c_void_p, c_void_p, c_uint, c_uint, c_uint, c_wchar_p
_CreateFileMappingW.restype = c_void_p
def CreateFileMapping(name, acc, size):
return _CreateFileMappingW(
-1, pointer(get_SECURITY_ATTRIBUTES()), acc, 0, size, name
)
_MultiByteToWideChar = _kernel32.MultiByteToWideChar
_MultiByteToWideChar.argtypes = c_uint, c_uint, c_void_p, c_int, c_wchar_p, c_int
def MultiByteToWideChar(buff, length, codepage):
_w = create_unicode_buffer(length + 1)
l = _MultiByteToWideChar(codepage, 0, buff, length, _w, length)
if l == 0:
return None
return _w.value
class MEMORY_BASIC_INFORMATION32(Structure):
_fields_ = [
("BaseAddress", c_void_p),
("AllocationBase", c_void_p),
("AllocationProtect", c_uint),
("RegionSize", c_void_p),
("State", c_uint),
("Protect", c_uint),
("Type", c_uint),
]
class MEMORY_BASIC_INFORMATION64(Structure):
_fields_ = [
("BaseAddress", c_void_p),
("AllocationBase", c_void_p),
("AllocationProtect", c_uint),
("PartitionId", c_short),
("RegionSize", c_void_p),
("State", c_uint),
("Protect", c_uint),
("Type", c_uint),
]
_VirtualQueryEx = _kernel32.VirtualQueryEx
_VirtualQueryEx.argtypes = c_void_p, c_void_p, c_void_p, c_int
def VirtualQueryEx(hprocess, address):
if sizeof(c_void_p) == 4:
MEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION32
else:
MEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION64
info = MEMORY_BASIC_INFORMATION()
_VirtualQueryEx(hprocess, address, pointer(info), sizeof(info))
return info
_IsDBCSLeadByteEx = _kernel32.IsDBCSLeadByteEx
_IsDBCSLeadByteEx.argtypes = c_uint, c_byte
def IsDBCSLeadByteEx(codepage, char):
return _IsDBCSLeadByteEx(codepage, char)
_GetNativeSystemInfo = _kernel32.GetNativeSystemInfo
_GetNativeSystemInfo.argtypes = (c_void_p,)
class SYSTEM_INFO(Structure):
_fields_ = [
("wProcessorArchitecture", c_ushort),
("wReserved", c_ushort),
("dwPageSize", c_uint),
("lpMinimumApplicationAddress", c_void_p),
("lpMaximumApplicationAddress", c_void_p),
("dwActiveProcessorMask", c_void_p),
("dwNumberOfProcessors", c_uint),
("dwProcessorType", c_uint),
("dwAllocationGranularity", c_uint),
("wProcessorLevel", c_ushort),
("wProcessorRevision", c_ushort),
]
def GetNativeSystemInfo():
_SYSTEM_INFO = SYSTEM_INFO()
_GetNativeSystemInfo(pointer(_SYSTEM_INFO))
return _SYSTEM_INFO
def Is64bit(pid):
sysinfo = GetNativeSystemInfo()
if sysinfo.wProcessorArchitecture == 9 or sysinfo.wProcessorArchitecture == 6:
hprocess = AutoHandle(OpenProcess(PROCESS_QUERY_INFORMATION, False, pid))
if hprocess == 0:
return False
res = not IsWow64Process(hprocess)
return res
else:
return False
_MessageBoxW = _user32.MessageBoxW
_MessageBoxW.argtypes = c_void_p, c_wchar_p, c_wchar_p, c_uint

View File

@ -18,7 +18,8 @@ from ctypes import (
windll,
c_char,
)
from ctypes.wintypes import WORD, HANDLE
from ctypes.wintypes import WORD, HANDLE, HWND, LONG, DWORD
from windows import WINDOWPLACEMENT
import gobject
utilsdll = CDLL(gobject.GetDllpath(("winsharedutils32.dll", "winsharedutils64.dll")))
@ -313,3 +314,33 @@ def queryversion(exe):
startdarklistener = utilsdll.startdarklistener
startdarklistener.restype = HANDLE
SetTheme = utilsdll._SetTheme
SetTheme.argtypes = HWND, c_bool, c_int
showintab = utilsdll.showintab
showintab.argtypes = HWND, c_bool
class windowstatus(Structure):
_fields_ = [("wpc", WINDOWPLACEMENT), ("HWNDStyle", LONG), ("HWNDStyleEx", LONG)]
letfullscreen = utilsdll.letfullscreen
letfullscreen.argtypes = (HWND,)
letfullscreen.restype = windowstatus
recoverwindow = utilsdll.recoverwindow
recoverwindow.argtypes = HWND, windowstatus
pid_running = utilsdll.pid_running
pid_running.argtypes = (DWORD,)
pid_running.restype = c_bool
getpidhwndfirst = utilsdll.getpidhwndfirst
getpidhwndfirst.argtypes = (DWORD,)
getpidhwndfirst.restype = HWND
Is64bit = utilsdll.Is64bit
Is64bit.argtypes = (DWORD,)
Is64bit.restype = c_bool

View File

@ -7,6 +7,7 @@
"only_from_exe": [],
"showrangeafterrangeselect": true,
"autodisappear": false,
"WindowBackdrop":0,
"read_raw": true,
"read_trans": false,
"read_translator": 0,

View File

@ -2,8 +2,8 @@
project(winsharedutils)
add_library(winsharedutils MODULE darklistener.cpp version.cpp otsu.cpp cinterface.cpp clipboard.cpp lnk.cpp dllmain.cpp levenshtein.cpp muteprocess.cpp sapi_dll.cpp simplemecab.cpp SimpleBrowser.cpp MWebBrowser.cpp icon.cpp)
add_library(winsharedutils MODULE hwnd.cpp darklistener.cpp theme.cpp version.cpp otsu.cpp cinterface.cpp clipboard.cpp lnk.cpp dllmain.cpp levenshtein.cpp muteprocess.cpp sapi_dll.cpp simplemecab.cpp SimpleBrowser.cpp MWebBrowser.cpp icon.cpp)
target_link_libraries(winsharedutils dwmapi)
if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
set_target_properties(winsharedutils PROPERTIES OUTPUT_NAME "winsharedutils64")
else()

View File

@ -1,7 +1,10 @@
#pragma once
#include <Windows.h>
#define DECLARE extern "C" __declspec(dllexport)
extern "C"
{
__declspec(dllexport) bool _SetTheme(HWND _hWnd, bool dark, int backdrop);
__declspec(dllexport) HANDLE startdarklistener();
__declspec(dllexport) bool queryversion(const wchar_t *exe, WORD *_1, WORD *_2, WORD *_3, WORD *_4);

View File

@ -0,0 +1,119 @@
#include <Windows.h>
#include "define.h"
DECLARE void showintab(HWND hwnd, bool show)
{
auto style_ex = GetWindowLong(hwnd, GWL_EXSTYLE);
if (show)
{
style_ex |= WS_EX_APPWINDOW;
style_ex &= ~WS_EX_TOOLWINDOW;
}
else
{
style_ex &= ~WS_EX_APPWINDOW;
style_ex |= WS_EX_TOOLWINDOW;
}
SetWindowLong(hwnd, GWL_EXSTYLE, style_ex);
}
struct windowstatus
{
WINDOWPLACEMENT wpc;
LONG HWNDStyle, HWNDStyleEx;
};
DECLARE windowstatus letfullscreen(HWND hwnd)
{
WINDOWPLACEMENT wpc;
GetWindowPlacement(hwnd, &wpc);
auto HWNDStyle = GetWindowLong(hwnd, GWL_STYLE);
auto HWNDStyleEx = GetWindowLong(hwnd, GWL_EXSTYLE);
auto NewHWNDStyle = HWNDStyle;
NewHWNDStyle &= ~WS_BORDER;
NewHWNDStyle &= ~WS_DLGFRAME;
NewHWNDStyle &= ~WS_THICKFRAME;
auto NewHWNDStyleEx = HWNDStyleEx;
NewHWNDStyleEx &= ~WS_EX_WINDOWEDGE;
SetWindowLong(hwnd, GWL_STYLE, NewHWNDStyle | WS_POPUP);
SetWindowLong(
hwnd, GWL_EXSTYLE, NewHWNDStyleEx | WS_EX_TOPMOST);
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
return {wpc, HWNDStyle, HWNDStyleEx};
}
DECLARE void recoverwindow(HWND hwnd, windowstatus status)
{
SetWindowLong(hwnd, GWL_STYLE, status.HWNDStyle);
SetWindowLong(hwnd, GWL_EXSTYLE, status.HWNDStyleEx);
ShowWindow(hwnd, SW_SHOWNORMAL);
SetWindowPlacement(hwnd, &status.wpc);
}
struct AutoHandle
{
HANDLE _handle;
AutoHandle(HANDLE handle) : _handle(handle){};
~AutoHandle()
{
CloseHandle(_handle);
}
operator HANDLE()
{
return _handle;
}
operator bool()
{
return _handle == INVALID_HANDLE_VALUE;
}
};
DECLARE bool pid_running(DWORD pid)
{
DWORD code;
GetExitCodeProcess(AutoHandle(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)), &code);
return code == STILL_ACTIVE;
// auto process = AutoHandle(OpenProcess(SYNCHRONIZE, FALSE, pid));
// DWORD ret = WaitForSingleObject(process, 0);
// return ret == WAIT_TIMEOUT;
}
struct __EnumWindowsProc
{
DWORD pid;
HWND hwnd;
};
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
if (IsWindow(hwnd) && IsWindowEnabled(hwnd) & IsWindowVisible(hwnd))
{
auto info = (__EnumWindowsProc *)lParam;
DWORD processId;
GetWindowThreadProcessId(hwnd, &processId);
if (info->pid == processId && info->hwnd == 0)
{
info->hwnd = hwnd;
}
}
return TRUE;
}
DECLARE HWND getpidhwndfirst(DWORD pid)
{
__EnumWindowsProc info = {pid, 0};
EnumWindows(EnumWindowsProc, (LPARAM)&info);
return info.hwnd;
}
DECLARE bool Is64bit(DWORD pid)
{
SYSTEM_INFO sysinfo;
GetNativeSystemInfo(&sysinfo);
if (sysinfo.wProcessorArchitecture == 9 || sysinfo.wProcessorArchitecture == 6)
{
auto hprocess = AutoHandle(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
BOOL b;
IsWow64Process(hprocess, &b);
return !b;
}
else
return false;
}

View File

@ -0,0 +1,173 @@
#include <Windows.h>
#include <stdint.h>
#include <dwmapi.h>
#include "define.h"
// https://github.com/Blinue/Xaml-Islands-Cpp/blob/main/src/XamlIslandsCpp/XamlWindow.h
enum WindowBackdrop : int32_t
{
SolidColor = 0,
Acrylic = 1,
Mica = 2,
MicaAlt = 3,
};
static uint32_t GetOSBuild() noexcept
{
HMODULE hNtDll = GetModuleHandle(L"ntdll.dll");
if (!hNtDll)
{
return 0;
}
auto rtlGetVersion = (LONG(WINAPI *)(PRTL_OSVERSIONINFOW))GetProcAddress(hNtDll, "RtlGetVersion");
if (rtlGetVersion == nullptr)
{
// assert(false);
return 0;
}
RTL_OSVERSIONINFOW version{};
version.dwOSVersionInfoSize = sizeof(version);
rtlGetVersion(&version);
return version.dwBuildNumber;
}
struct Win32Helper
{
struct OSVersion
{
constexpr OSVersion(uint32_t build) : _build(build) {}
bool Is20H1OrNewer() const noexcept
{
return _build >= 19041;
}
// 下面为 Win11
// 不考虑代号相同的 Win10
bool IsWin11() const noexcept
{
return Is21H2OrNewer();
}
bool Is21H2OrNewer() const noexcept
{
return _build >= 22000;
}
bool Is22H2OrNewer() const noexcept
{
return _build >= 22621;
}
private:
uint32_t _build = 0;
};
static OSVersion GetOSVersion() noexcept;
};
Win32Helper::OSVersion Win32Helper::GetOSVersion() noexcept
{
static OSVersion version = GetOSBuild();
return version;
}
enum class PreferredAppMode
{
Default,
AllowDark,
ForceDark,
ForceLight,
Max
};
using fnSetPreferredAppMode = PreferredAppMode(WINAPI *)(PreferredAppMode appMode);
using fnAllowDarkModeForWindow = bool(WINAPI *)(HWND hWnd, bool allow);
using fnRefreshImmersiveColorPolicyState = void(WINAPI *)();
using fnFlushMenuThemes = void(WINAPI *)();
// using fnDwmSetWindowAttribute=HRESULT (WINAPI*)(HWND,DWORD,LPCVOID,DWORD);
static fnSetPreferredAppMode SetPreferredAppMode = nullptr;
static fnAllowDarkModeForWindow AllowDarkModeForWindow = nullptr;
static fnRefreshImmersiveColorPolicyState RefreshImmersiveColorPolicyState = nullptr;
static fnFlushMenuThemes FlushMenuThemes = nullptr;
// static fnDwmSetWindowAttribute DwmSetWindowAttribute=nullptr;
static bool initok()
{
return SetPreferredAppMode && AllowDarkModeForWindow && RefreshImmersiveColorPolicyState && FlushMenuThemes; //&&DwmSetWindowAttribute;
}
static bool InitApis() noexcept
{
if (initok())
return true;
HMODULE hUxtheme = LoadLibrary(L"uxtheme.dll");
// assert(hUxtheme);
SetPreferredAppMode = (fnSetPreferredAppMode)GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135));
AllowDarkModeForWindow = (fnAllowDarkModeForWindow)GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133));
RefreshImmersiveColorPolicyState = (fnRefreshImmersiveColorPolicyState)GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104));
FlushMenuThemes = (fnFlushMenuThemes)GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136));
// HMODULE hdwmapi = LoadLibrary(L"dwmapi.dll");
// DwmSetWindowAttribute = (fnDwmSetWindowAttribute)GetProcAddress(hdwmapi, "DwmSetWindowAttribute");
return initok();
}
static void SetWindowTheme(HWND hWnd, bool darkBorder, bool darkMenu) noexcept
{
if (!InitApis())
return;
SetPreferredAppMode(darkMenu ? PreferredAppMode::ForceDark : PreferredAppMode::ForceLight);
AllowDarkModeForWindow(hWnd, darkMenu);
// 使标题栏适应黑暗模式
// build 18985 之前 DWMWA_USE_IMMERSIVE_DARK_MODE 的值不同
// https://github.com/MicrosoftDocs/sdk-api/pull/966/files
constexpr const DWORD DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19;
BOOL value = darkBorder;
DwmSetWindowAttribute(
hWnd,
Win32Helper::GetOSVersion().Is20H1OrNewer() ? DWMWA_USE_IMMERSIVE_DARK_MODE : DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1,
&value,
sizeof(value));
RefreshImmersiveColorPolicyState();
FlushMenuThemes();
}
bool _SetTheme(
HWND _hWnd,
bool dark,
int backdrop)
{
auto _isBackgroundSolidColor = backdrop == WindowBackdrop::SolidColor;
if (Win32Helper::GetOSVersion().Is22H2OrNewer() &&
_isBackgroundSolidColor != (backdrop == WindowBackdrop::SolidColor))
{
return true;
}
// Win10 中即使在亮色主题下我们也使用暗色边框,这也是 UWP 窗口的行为
SetWindowTheme(
_hWnd,
Win32Helper::GetOSVersion().IsWin11() ? dark : true,
dark);
if (!Win32Helper::GetOSVersion().Is22H2OrNewer())
{
return false;
}
// 设置背景
static const DWM_SYSTEMBACKDROP_TYPE BACKDROP_MAP[] = {
DWMSBT_AUTO, DWMSBT_TRANSIENTWINDOW, DWMSBT_MAINWINDOW, DWMSBT_TABBEDWINDOW};
DWM_SYSTEMBACKDROP_TYPE value = BACKDROP_MAP[(int)backdrop];
DwmSetWindowAttribute(_hWnd, DWMWA_SYSTEMBACKDROP_TYPE, &value, sizeof(value));
return false;
}