Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cycle through taskbar windows on click v1.0 #950

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions mods/taskbar-left-click-cycle.wh.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// ==WindhawkMod==
// @id taskbar-left-click-cycle
// @name Cycle through taskbar windows on click
// @description Makes clicking on combined taskbar items cycle through windows instead of opening thumbnail previews
// @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==
/*
# Cycle through taskbar windows on click

Makes clicking on combined taskbar items cycle through windows instead of
opening thumbnail previews. It's still possible to open thumbnail previews by
holding the Ctrl key while clicking.

Only Windows 10 64-bit and Windows 11 are supported. For other Windows versions
check out [7+ Taskbar Tweaker](https://tweaker.ramensoftware.com/).

**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.

![Demonstration](https://i.imgur.com/ecYYtGU.gif)
*/
// ==/WindhawkModReadme==

// ==WindhawkModSettings==
/*
- oldTaskbarOnWin11: false
$name: Customize the old taskbar on Windows 11
$description: >-
Enable this option to customize the old taskbar on Windows 11 (if using
ExplorerPatcher or a similar tool).
*/
// ==/WindhawkModSettings==

#include <windhawk_utils.h>

struct {
bool oldTaskbarOnWin11;
} g_settings;

enum class WinVersion {
Unsupported,
Win10,
Win11,
};

WinVersion g_winVersion;

using CTaskBtnGroup_GetGroupType_t = int(WINAPI*)(PVOID pThis);
CTaskBtnGroup_GetGroupType_t CTaskBtnGroup_GetGroupType_Original;

using CTaskListWnd__HandleClick_t = void(WINAPI*)(PVOID pThis,
PVOID taskBtnGroup,
int taskItemIndex,
int clickAction,
int param4,
int param5);
CTaskListWnd__HandleClick_t CTaskListWnd__HandleClick_Original;
void WINAPI CTaskListWnd__HandleClick_Hook(PVOID pThis,
PVOID taskBtnGroup,
int taskItemIndex,
int clickAction,
int param4,
int param5) {
Wh_Log(L"> %d", clickAction);

auto original = [&]() {
CTaskListWnd__HandleClick_Original(pThis, taskBtnGroup, taskItemIndex,
clickAction, param4, param5);
};

constexpr int kClick = 0;
constexpr int kShiftClick = 4;

if (clickAction != kClick && clickAction != kShiftClick) {
return original();
}

// Group types:
// 1 - Single item or multiple uncombined items
// 2 - Pinned item
// 3 - Multiple combined items
int groupType = CTaskBtnGroup_GetGroupType_Original(taskBtnGroup);
if (groupType != 3) {
return original();
}

CTaskListWnd__HandleClick_Original(
pThis, taskBtnGroup, taskItemIndex,
clickAction == kClick ? kShiftClick : kClick, param4, param5);
}

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 {
return WinVersion::Win11;
}
break;
}

return WinVersion::Unsupported;
}

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_winVersion >= WinVersion::Win11 && g_settings.oldTaskbarOnWin11) {
g_winVersion = WinVersion::Win10;
}

// Taskbar.dll, explorer.exe
WindhawkUtils::SYMBOL_HOOK symbolHooks[] = {
{
{
LR"(public: virtual enum eTBGROUPTYPE __cdecl CTaskBtnGroup::GetGroupType(void))",
LR"(public: virtual enum eTBGROUPTYPE __cdecl CTaskBtnGroup::GetGroupType(void) __ptr64)",
},
(void**)&CTaskBtnGroup_GetGroupType_Original,
},
{
{
LR"(protected: void __cdecl CTaskListWnd::_HandleClick(struct ITaskBtnGroup *,int,enum CTaskListWnd::eCLICKACTION,int,int))",
LR"(protected: void __cdecl CTaskListWnd::_HandleClick(struct ITaskBtnGroup * __ptr64,int,enum CTaskListWnd::eCLICKACTION,int,int) __ptr64)",
},
(void**)&CTaskListWnd__HandleClick_Original,
(void*)CTaskListWnd__HandleClick_Hook,
},
};

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;
}
}

if (!HookSymbols(module, symbolHooks, ARRAYSIZE(symbolHooks))) {
return FALSE;
}

return TRUE;
}

BOOL Wh_ModSettingsChanged(BOOL* bReload) {
Wh_Log(L">");

bool prevOldTaskbarOnWin11 = g_settings.oldTaskbarOnWin11;

LoadSettings();

*bReload = g_settings.oldTaskbarOnWin11 != prevOldTaskbarOnWin11;

return TRUE;
}