-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Taskbar keyboard-only auto-hide v1.0
- Loading branch information
Showing
1 changed file
with
341 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,341 @@ | ||
// ==WindhawkMod== | ||
// @id taskbar-auto-hide-keyboard-only | ||
// @name Taskbar keyboard-only auto-hide | ||
// @description When taskbar auto-hide is enabled, the taskbar will only be unhidden with the keyboard, hovering the mouse over the taskbar will not unhide it | ||
// @version 1.0 | ||
// @author m417z | ||
// @github https://github.com/m417z | ||
// @twitter https://twitter.com/m417z | ||
// @homepage https://m417z.com/ | ||
// @include explorer.exe | ||
// @architecture x86-64 | ||
// @compilerOptions -lversion | ||
// ==/WindhawkMod== | ||
|
||
// Source code is published under The GNU General Public License v3.0. | ||
// | ||
// For bug reports and feature requests, please open an issue here: | ||
// https://github.com/ramensoftware/windhawk-mods/issues | ||
// | ||
// For pull requests, development takes place here: | ||
// https://github.com/m417z/my-windhawk-mods | ||
|
||
// ==WindhawkModReadme== | ||
/* | ||
# Taskbar keyboard-only auto-hide | ||
When taskbar auto-hide is enabled, the taskbar will only be unhidden with the | ||
keyboard, hovering the mouse over the taskbar will not unhide it. For example, | ||
the Win key or Ctrl+Esc will unhide the taskbar. | ||
**Note:** To customize the old taskbar on Windows 11 (if using ExplorerPatcher | ||
or a similar tool), enable the relevant option in the mod's settings. | ||
*/ | ||
// ==/WindhawkModReadme== | ||
|
||
#include <windhawk_utils.h> | ||
|
||
#include <psapi.h> | ||
|
||
#include <atomic> | ||
|
||
struct { | ||
int showSpeedup; | ||
int hideSpeedup; | ||
int frameRate; | ||
bool oldTaskbarOnWin11; | ||
} g_settings; | ||
|
||
enum class WinVersion { | ||
Unsupported, | ||
Win10, | ||
Win11, | ||
Win11_24H2, | ||
}; | ||
|
||
WinVersion g_winVersion; | ||
|
||
std::atomic<bool> g_initialized; | ||
std::atomic<bool> g_explorerPatcherInitialized; | ||
|
||
using TrayUI_SetUnhideTimer_t = void(WINAPI*)(void* pThis, | ||
long param1, | ||
long param2); | ||
TrayUI_SetUnhideTimer_t TrayUI_SetUnhideTimer_Original; | ||
void WINAPI TrayUI_SetUnhideTimer_Hook(void* pThis, long param1, long param2) { | ||
Wh_Log(L">"); | ||
|
||
// TrayUI_SetUnhideTimer_Original(pThis, param1, param2); | ||
} | ||
|
||
bool HookTaskbarSymbols() { | ||
HMODULE module; | ||
if (g_winVersion <= WinVersion::Win10) { | ||
module = GetModuleHandle(nullptr); | ||
} else { | ||
module = LoadLibrary(L"taskbar.dll"); | ||
if (!module) { | ||
Wh_Log(L"Couldn't load taskbar.dll"); | ||
return false; | ||
} | ||
} | ||
|
||
// Taskbar.dll, explorer.exe | ||
WindhawkUtils::SYMBOL_HOOK symbolHooks[] = { | ||
{ | ||
{LR"(public: virtual void __cdecl TrayUI::SetUnhideTimer(long,long))"}, | ||
&TrayUI_SetUnhideTimer_Original, | ||
TrayUI_SetUnhideTimer_Hook, | ||
}, | ||
}; | ||
|
||
if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) { | ||
Wh_Log(L"HookSymbols failed"); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
VS_FIXEDFILEINFO* GetModuleVersionInfo(HMODULE hModule, UINT* puPtrLen) { | ||
void* pFixedFileInfo = nullptr; | ||
UINT uPtrLen = 0; | ||
|
||
HRSRC hResource = | ||
FindResource(hModule, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); | ||
if (hResource) { | ||
HGLOBAL hGlobal = LoadResource(hModule, hResource); | ||
if (hGlobal) { | ||
void* pData = LockResource(hGlobal); | ||
if (pData) { | ||
if (!VerQueryValue(pData, L"\\", &pFixedFileInfo, &uPtrLen) || | ||
uPtrLen == 0) { | ||
pFixedFileInfo = nullptr; | ||
uPtrLen = 0; | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (puPtrLen) { | ||
*puPtrLen = uPtrLen; | ||
} | ||
|
||
return (VS_FIXEDFILEINFO*)pFixedFileInfo; | ||
} | ||
|
||
WinVersion GetExplorerVersion() { | ||
VS_FIXEDFILEINFO* fixedFileInfo = GetModuleVersionInfo(nullptr, nullptr); | ||
if (!fixedFileInfo) { | ||
return WinVersion::Unsupported; | ||
} | ||
|
||
WORD major = HIWORD(fixedFileInfo->dwFileVersionMS); | ||
WORD minor = LOWORD(fixedFileInfo->dwFileVersionMS); | ||
WORD build = HIWORD(fixedFileInfo->dwFileVersionLS); | ||
WORD qfe = LOWORD(fixedFileInfo->dwFileVersionLS); | ||
|
||
Wh_Log(L"Version: %u.%u.%u.%u", major, minor, build, qfe); | ||
|
||
switch (major) { | ||
case 10: | ||
if (build < 22000) { | ||
return WinVersion::Win10; | ||
} else if (build < 26100) { | ||
return WinVersion::Win11; | ||
} else { | ||
return WinVersion::Win11_24H2; | ||
} | ||
break; | ||
} | ||
|
||
return WinVersion::Unsupported; | ||
} | ||
|
||
struct EXPLORER_PATCHER_HOOK { | ||
PCSTR symbol; | ||
void** pOriginalFunction; | ||
void* hookFunction = nullptr; | ||
bool optional = false; | ||
|
||
template <typename Prototype> | ||
EXPLORER_PATCHER_HOOK( | ||
PCSTR symbol, | ||
Prototype** originalFunction, | ||
std::type_identity_t<Prototype*> hookFunction = nullptr, | ||
bool optional = false) | ||
: symbol(symbol), | ||
pOriginalFunction(reinterpret_cast<void**>(originalFunction)), | ||
hookFunction(reinterpret_cast<void*>(hookFunction)), | ||
optional(optional) {} | ||
}; | ||
|
||
bool HookExplorerPatcherSymbols(HMODULE explorerPatcherModule) { | ||
if (g_explorerPatcherInitialized.exchange(true)) { | ||
return true; | ||
} | ||
|
||
if (g_winVersion >= WinVersion::Win11) { | ||
g_winVersion = WinVersion::Win10; | ||
} | ||
|
||
EXPLORER_PATCHER_HOOK hooks[] = { | ||
{R"(?SetUnhideTimer@TrayUI@@UEAAXJJ@Z)", | ||
&TrayUI_SetUnhideTimer_Original, TrayUI_SetUnhideTimer_Hook}, | ||
}; | ||
|
||
bool succeeded = true; | ||
|
||
for (const auto& hook : hooks) { | ||
void* ptr = (void*)GetProcAddress(explorerPatcherModule, hook.symbol); | ||
if (!ptr) { | ||
Wh_Log(L"ExplorerPatcher symbol%s doesn't exist: %S", | ||
hook.optional ? L" (optional)" : L"", hook.symbol); | ||
if (!hook.optional) { | ||
succeeded = false; | ||
} | ||
continue; | ||
} | ||
|
||
if (hook.hookFunction) { | ||
Wh_SetFunctionHook(ptr, hook.hookFunction, hook.pOriginalFunction); | ||
} else { | ||
*hook.pOriginalFunction = ptr; | ||
} | ||
} | ||
|
||
if (!succeeded) { | ||
Wh_Log(L"HookExplorerPatcherSymbols failed"); | ||
} else if (g_initialized) { | ||
Wh_ApplyHookOperations(); | ||
} | ||
|
||
return succeeded; | ||
} | ||
|
||
bool IsExplorerPatcherModule(HMODULE module) { | ||
WCHAR moduleFilePath[MAX_PATH]; | ||
switch ( | ||
GetModuleFileName(module, moduleFilePath, ARRAYSIZE(moduleFilePath))) { | ||
case 0: | ||
case ARRAYSIZE(moduleFilePath): | ||
return false; | ||
} | ||
|
||
PCWSTR moduleFileName = wcsrchr(moduleFilePath, L'\\'); | ||
if (!moduleFileName) { | ||
return false; | ||
} | ||
|
||
moduleFileName++; | ||
|
||
if (_wcsnicmp(L"ep_taskbar.", moduleFileName, sizeof("ep_taskbar.") - 1) == | ||
0) { | ||
Wh_Log(L"ExplorerPatcher taskbar module: %s", moduleFileName); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
bool HandleLoadedExplorerPatcher() { | ||
HMODULE hMods[1024]; | ||
DWORD cbNeeded; | ||
if (EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), | ||
&cbNeeded)) { | ||
for (size_t i = 0; i < cbNeeded / sizeof(HMODULE); i++) { | ||
if (IsExplorerPatcherModule(hMods[i])) { | ||
return HookExplorerPatcherSymbols(hMods[i]); | ||
} | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
using LoadLibraryExW_t = decltype(&LoadLibraryExW); | ||
LoadLibraryExW_t LoadLibraryExW_Original; | ||
HMODULE WINAPI LoadLibraryExW_Hook(LPCWSTR lpLibFileName, | ||
HANDLE hFile, | ||
DWORD dwFlags) { | ||
HMODULE module = LoadLibraryExW_Original(lpLibFileName, hFile, dwFlags); | ||
if (module && !((ULONG_PTR)module & 3) && !g_explorerPatcherInitialized) { | ||
if (IsExplorerPatcherModule(module)) { | ||
HookExplorerPatcherSymbols(module); | ||
} | ||
} | ||
|
||
return module; | ||
} | ||
|
||
void LoadSettings() { | ||
g_settings.oldTaskbarOnWin11 = Wh_GetIntSetting(L"oldTaskbarOnWin11"); | ||
} | ||
|
||
BOOL Wh_ModInit() { | ||
Wh_Log(L">"); | ||
|
||
LoadSettings(); | ||
|
||
g_winVersion = GetExplorerVersion(); | ||
if (g_winVersion == WinVersion::Unsupported) { | ||
Wh_Log(L"Unsupported Windows version"); | ||
return FALSE; | ||
} | ||
|
||
if (g_settings.oldTaskbarOnWin11) { | ||
bool hasWin10Taskbar = g_winVersion < WinVersion::Win11_24H2; | ||
|
||
if (g_winVersion >= WinVersion::Win11) { | ||
g_winVersion = WinVersion::Win10; | ||
} | ||
|
||
if (hasWin10Taskbar && !HookTaskbarSymbols()) { | ||
return FALSE; | ||
} | ||
} else if (!HookTaskbarSymbols()) { | ||
return FALSE; | ||
} | ||
|
||
if (!HandleLoadedExplorerPatcher()) { | ||
Wh_Log(L"HandleLoadedExplorerPatcher failed"); | ||
return FALSE; | ||
} | ||
|
||
HMODULE kernelBaseModule = GetModuleHandle(L"kernelbase.dll"); | ||
auto pKernelBaseLoadLibraryExW = (decltype(&LoadLibraryExW))GetProcAddress( | ||
kernelBaseModule, "LoadLibraryExW"); | ||
WindhawkUtils::Wh_SetFunctionHookT(pKernelBaseLoadLibraryExW, | ||
LoadLibraryExW_Hook, | ||
&LoadLibraryExW_Original); | ||
|
||
g_initialized = true; | ||
|
||
return TRUE; | ||
} | ||
|
||
void Wh_ModAfterInit() { | ||
Wh_Log(L">"); | ||
|
||
// Try again in case there's a race between the previous attempt and the | ||
// LoadLibraryExW hook. | ||
if (!g_explorerPatcherInitialized) { | ||
HandleLoadedExplorerPatcher(); | ||
} | ||
} | ||
|
||
void Wh_ModUninit() { | ||
Wh_Log(L">"); | ||
} | ||
|
||
BOOL Wh_ModSettingsChanged(BOOL* bReload) { | ||
Wh_Log(L">"); | ||
|
||
bool prevOldTaskbarOnWin11 = g_settings.oldTaskbarOnWin11; | ||
|
||
LoadSettings(); | ||
|
||
*bReload = g_settings.oldTaskbarOnWin11 != prevOldTaskbarOnWin11; | ||
|
||
return TRUE; | ||
} |