From 2914b84cba2528701e51fcb904fe379e9bfd982d Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sun, 8 Sep 2024 10:08:16 +0300 Subject: [PATCH 1/9] Customize Windows notifications placement v1.0.1: (#938) * The mod was tested on Windows 10 and was found to be working correctly. The mod name and description were updated accordingly. * Made the mod work on non-English Windows versions. --- mods/notifications-placement.wh.cpp | 61 ++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/mods/notifications-placement.wh.cpp b/mods/notifications-placement.wh.cpp index 4cdc4708d..7acfd666d 100644 --- a/mods/notifications-placement.wh.cpp +++ b/mods/notifications-placement.wh.cpp @@ -1,8 +1,8 @@ // ==WindhawkMod== // @id notifications-placement -// @name Customize Windows 11 notifications placement +// @name Customize Windows notifications placement // @description Move notifications to another monitor or another corner of the screen -// @version 1.0 +// @version 1.0.1 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -23,11 +23,11 @@ // ==WindhawkModReadme== /* -# Customize Windows 11 notifications placement +# Customize Windows notifications placement Move notifications to another monitor or another corner of the screen. -Only Windows 11 is supported. +Only Windows 10 64-bit and Windows 11 are supported. ![Screenshot](https://i.imgur.com/4PxMvLg.png) */ @@ -60,6 +60,7 @@ Only Windows 11 is supported. #include #include +#include #include std::atomic g_unloading; @@ -173,9 +174,57 @@ bool IsTargetCoreWindow(HWND hWnd) { return false; } - WCHAR szWindowText[32]; + // The window title is locale-dependent, and unfortunately I didn't find a + // simpler way to identify the target window. + // String source: Windows.UI.ShellCommon..pri + // String resource: \ActionCenter\AC_ToastCenter_Title + // The strings were collected from here: + // https://github.com/m417z/windows-language-files + static const std::unordered_set newNotificationStrings = { + L"Jakinarazpen berria", + L"Jauns paziņojums", + L"Naujas pranešimas", + L"Neue Benachrichtigung", + L"New notification", + L"Nieuwe melding", + L"Notificació nova", + L"Notificación nueva", + L"Notificare nouă", + L"Nouvelle notification", + L"Nova notificação", + L"Nova notificación", + L"Nova obavijesti", + L"Nové oznámení", + L"Nové oznámenie", + L"Novo obaveštenje", + L"Novo obvestilo", + L"Nowe powiadomienie", + L"Nueva notificación", + L"Nuova notifica", + L"Ny meddelelse", + L"Ny varsling", + L"Nytt meddelande", + L"Pemberitahuan baru", + L"Thông báo mới", + L"Új értesítés", + L"Uus teatis", + L"Uusi ilmoitus", + L"Yeni bildirim", + L"Νέα ειδοποίηση", + L"Нове сповіщення", + L"Ново известие", + L"Новое уведомление", + L"הודעה חדשה", + L"\u200f\u200fإعلام جديد", + L"การแจ้งให้ทราบใหม่", + L"새 알림", + L"新しい通知", + L"新通知", + }; + + WCHAR szWindowText[256]; if (GetWindowText(hWnd, szWindowText, ARRAYSIZE(szWindowText)) == 0 || - wcscmp(szWindowText, L"New notification") != 0) { + !newNotificationStrings.contains(szWindowText)) { return false; } From 40c94993b0e85ef8ed053f5b5b0e06b16380e42d Mon Sep 17 00:00:00 2001 From: Toru the Red Fox Date: Sun, 8 Sep 2024 11:42:30 +0100 Subject: [PATCH 2/9] Logon, Logoff & Shutdown Sounds Restored - v1.0.0 (#937) * Create logon-logoff-shutdown-sounds.wh.cpp * Hopefully make it now work on older versions * x86 fix and follow hook naming convention --- mods/logon-logoff-shutdown-sounds.wh.cpp | 586 +++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 mods/logon-logoff-shutdown-sounds.wh.cpp diff --git a/mods/logon-logoff-shutdown-sounds.wh.cpp b/mods/logon-logoff-shutdown-sounds.wh.cpp new file mode 100644 index 000000000..e3140b9d8 --- /dev/null +++ b/mods/logon-logoff-shutdown-sounds.wh.cpp @@ -0,0 +1,586 @@ +// ==WindhawkMod== +// @id logon-logoff-shutdown-sounds +// @name Logon, Logoff & Shutdown Sounds Restored +// @description Restores the logon, logoff and shutdown sounds from earlier versions of Windows +// @version 1.0.0 +// @author Toru the Red Fox +// @github https://github.com/TorutheRedFox +// @twitter https://twitter.com/TorutheRedFox +// @include explorer.exe +// @compilerOptions -lcomdlg32 -lshlwapi -lwinmm -luser32 +// ==/WindhawkMod== + +// ==WindhawkModReadme== +/* +# Logon Sounds Restored +Restores the logon, logoff and shutdown sounds from earlier versions of Windows, simple as. + +It is recommended to use [these reg files](https://www.howtogeek.com/wp-content/uploads/2016/09/Shutdown-Logoff-Logon-Sound-Hacks.zip) to restore the sound events to the Sound control panel applet. +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== +/* + +- xpmode: false + $name: XP Startup behavior + $description: Plays the startup sound on logon. Resuming will still play the logon sound. It is recommended to untick the box to play the startup sound in the control panel to prevent it from playing before you log on. + +*/ +// ==/WindhawkModSettings== + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// settings +struct { + bool bXpMode; +} settings; + +// the various global handles used + +HMODULE g_hShlwapi; +HWND g_hwSound; +HANDLE g_hSoundThread; +HMODULE g_hExplorer; + +// NOTE: sligtly different versions of this exist in... +// SHPlaySound() -> shell32 +// IEPlaySound() -> shdocvw/browseui + +STDAPI_(void) ExplorerPlaySound(LPCTSTR pszSound) +{ + // note, we have access only to global system sounds here as we use "Apps\.Default" + TCHAR szKey[256]; + wsprintf(szKey, TEXT("AppEvents\\Schemes\\Apps\\.Default\\%s\\.current"), pszSound); + + TCHAR szFileName[MAX_PATH]; + szFileName[0] = 0; + LONG cbSize = sizeof(szFileName); + + // test for an empty string, PlaySound will play the Default Sound if we + // give it a sound it cannot find... + + if ((RegQueryValue(HKEY_CURRENT_USER, szKey, szFileName, &cbSize) == ERROR_SUCCESS) + && szFileName[0]) + { + // flags are relevant, we try to not stomp currently playing sounds + PlaySound(szFileName, NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP); + } +} + +typedef LRESULT WINAPI (* _OnSessionChange_t)(void* _this, WPARAM wParam, LPARAM lParam); +_OnSessionChange_t _OnSessionChange_orig; +LRESULT WINAPI _OnSessionChange_hook(void* _this, WPARAM wParam, LPARAM lParam) +{ + if ((WTS_SESSION_LOCK == wParam) || (WTS_SESSION_UNLOCK == wParam)) + { + if (wParam == WTS_SESSION_LOCK) + { + ExplorerPlaySound(TEXT("WindowsLogoff")); + } + else if (wParam == WTS_SESSION_UNLOCK) + { + ExplorerPlaySound(TEXT("WindowsLogon")); + } + } + + return _OnSessionChange_orig(_this, wParam, lParam); +} + +#ifndef SND_SYSTEM // because this is sometimes missing??? +#define SND_SYSTEM 0x00200000 +#endif + +DWORD WINAPI PlaySoundFileThreadProc( LPVOID lpParam ) +{ + PlaySoundW((WCHAR*)lpParam, 0, SND_NODEFAULT | SND_MEMORY | SND_SYSTEM); + LocalFree(lpParam); + return 0; +} + +DWORD WINAPI GetLastErrorError() +{ + DWORD hr = GetLastError(); + if ( hr == S_OK ) + return 1; + return hr; +} + +HRESULT WINAPI HRESULTFromLastErrorError() +{ + HRESULT hr = GetLastErrorError(); + if ( FAILED(hr) ) + return HRESULT_FROM_WIN32((USHORT)hr); + return hr; +} + +HRESULT WINAPI PlaySoundFile(HANDLE* hCurrentThread, LPCWSTR lpszFileName) { + DWORD szSoundFile; // eax + signed int hr = 0; // esi + DWORD NumberOfBytesRead; // [esp+8h] [ebp-Ch] BYREF + HANDLE hFile; // [esp+Ch] [ebp-8h] + LPVOID lpSndBuf = 0; // [esp+10h] [ebp-4h] + + hFile = CreateFile( + lpszFileName, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL + ); + + if (hFile != INVALID_HANDLE_VALUE) { + szSoundFile = GetFileSize(hFile, 0); + hr = E_OUTOFMEMORY; + if (szSoundFile != INVALID_FILE_SIZE && szSoundFile >= 0 && szSoundFile < 0x400000) { + lpSndBuf = LocalAlloc(0, szSoundFile); + if (lpSndBuf && ReadFile(hFile, lpSndBuf, szSoundFile, &NumberOfBytesRead, 0)) { + hr = szSoundFile != NumberOfBytesRead ? HRESULT_FROM_WIN32(ERROR_IO_PENDING) : 0; + } else { + hr = HRESULTFromLastErrorError(); + } + } + CloseHandle(hFile); + } else { + hr = HRESULTFromLastErrorError(); + } + + if (SUCCEEDED(hr)) { + HANDLE hPlaySndThread = CreateThread(0, 0, PlaySoundFileThreadProc, lpSndBuf, 0, 0); + if (hPlaySndThread) { + if (hCurrentThread) + *hCurrentThread = hPlaySndThread; + else + CloseHandle(hPlaySndThread); + return hr; + } + hr = HRESULTFromLastErrorError(); + } + + if (lpSndBuf) + LocalFree(lpSndBuf); + + return hr; +} + +typedef enum _ELOGONLOGOFFSOUNDTYPE +{ + ST_LOGON, + ST_LOGOFF, + ST_EXIT, + ST_START, +} ELOGONLOGOFFSOUNDTYPE; + +HRESULT PlayLogonLogoffSound(HANDLE* hThread, ELOGONLOGOFFSOUNDTYPE enSoundType) +{ + const wchar_t *szSoundType; + HRESULT hr; + DWORD pcbData[4]; + WCHAR szSubKey[264]; + WCHAR pvData[264]; + + switch (enSoundType) { + case ST_LOGON: + szSoundType = TEXT("WindowsLogon"); + break; + case ST_LOGOFF: + szSoundType = TEXT("WindowsLogoff"); + break; + case ST_EXIT: + szSoundType = TEXT("SystemExit"); + break; + case ST_START: + szSoundType = TEXT("SystemStart"); + break; + } + + pcbData[0] = 520; + + hr = wsprintf(szSubKey, TEXT("AppEvents\\Schemes\\Apps\\.Default\\%ws\\.Current"), szSoundType); + + if (SUCCEEDED(hr)) + { + hr = RegGetValue(HKEY_CURRENT_USER, szSubKey, 0, 2u, 0, pvData, pcbData); + if (hr == S_OK) + return PlaySoundFile(hThread, pvData); + else + return HRESULT_FROM_WIN32((USHORT)hr); + } + return hr; +} + +LRESULT SHDefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (IsWindowUnicode(hwnd)) + { + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + else + { + return DefWindowProcA(hwnd, uMsg, wParam, lParam); + } +} + +STDAPI_(BOOL) SHRegisterClassW(const WNDCLASSW* pwc) +{ + WNDCLASSW wc; + if (!GetClassInfo(pwc->hInstance, pwc->lpszClassName, &wc)) + { + return RegisterClass(pwc); + } + return TRUE; +} + +HWND WINAPI SHCreateWorkerWindowW(WNDPROC pfnWndProc, HWND hwndParent, DWORD dwExStyle, DWORD dwFlags, HMENU hmenu, void * p) +{ + WNDCLASSW wc = {0}; + + wc.lpfnWndProc = DefWindowProcW; + wc.cbWndExtra = sizeof(void *); + wc.hInstance = g_hShlwapi; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + wc.lpszClassName = L"WorkerW"; + //dwExStyle |= IsBiDiLocalizedSystem() ? /*dwExStyleRTLMirrorWnd*/0L : 0L; + + SHRegisterClassW(&wc); + + HWND hwnd = CreateWindowExW(dwExStyle, L"WorkerW", NULL, dwFlags, + 0, 0, 0, 0, hwndParent, + (HMENU)hmenu, g_hShlwapi, NULL); + if (hwnd) + { + SetWindowLongPtr(hwnd, 0, (LPARAM)(p)); + + // Note: Must explicitly use W version to avoid charset thunks + if (pfnWndProc) + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc); + + } + + return hwnd; +} + +class CSoundWnd { + public: + WINAPI CSoundWnd(); + LONG m_refCount; + HWND m_hwndSound; + HANDLE m_thread; + BOOL WINAPI Init(); + static DWORD CALLBACK s_ThreadProc(void* lpParam); + LONG WINAPI Release(); + static DWORD CALLBACK s_CreateWindow(void* lpParam); + static LRESULT CALLBACK s_WndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam); + LRESULT WINAPI v_WndProc(HWND hWnd, UINT mst, WPARAM wParam, LPARAM lParam); +}; + +WINAPI CSoundWnd::CSoundWnd() + : m_refCount(1) + , m_hwndSound(nullptr) + , m_thread(nullptr) +{ +} + +BOOL WINAPI CSoundWnd::Init() +{ + if (!SHCreateThread(s_ThreadProc, this, CTF_COINIT_STA | CTF_PROCESS_REF, s_CreateWindow)) { + DWORD dwLastError = GetLastError(); + LPWSTR pszMessageBuffer = nullptr; + + //Ask Win32 to give us the string version of that message ID. + //The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be). + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&pszMessageBuffer, 0, NULL); + + Wh_Log(TEXT("CSoundWnd::Init Failed to create thread %s"), pszMessageBuffer); + } + g_hwSound = this->m_hwndSound; + SetProcessShutdownParameters(0x4FF, NULL); + return this->m_hwndSound != NULL; +} + +DWORD CALLBACK CSoundWnd::s_CreateWindow(void* lpParam) +{ + InterlockedIncrement(&((CSoundWnd*)lpParam)->m_refCount); + + ((CSoundWnd*)lpParam)->m_hwndSound = SHCreateWorkerWindowW(s_WndProc, 0, 0, 0, 0, lpParam); + return 0; +} + +__inline void * GetWindowPtr0(HWND hWnd) { + return (void *)GetWindowLongPtrA(hWnd, 0); +} + +DWORD CALLBACK CSoundWnd::s_ThreadProc(void* lpParam) +{ + struct tagMSG Msg; // [esp+8h] [ebp-1Ch] BYREF + CSoundWnd* _this = static_cast(lpParam); + + if ( _this->m_hwndSound ) + { + while ( GetMessageW(&Msg, 0, 0, 0) ) + { + TranslateMessage(&Msg); + DispatchMessageW(&Msg); + } + } + _this->Release(); + return 0; +} + +LRESULT CALLBACK CSoundWnd::s_WndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) { + CSoundWnd* soundWnd = (CSoundWnd*)GetWindowPtr0(hWnd); + if (soundWnd) + return soundWnd->v_WndProc(hWnd, msg, wParam, lParam); + else + return SHDefWindowProc(hWnd, msg, wParam, lParam); +} + +LRESULT WINAPI CSoundWnd::v_WndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) { + WCHAR szShutdownReason[256]; + + switch (msg) { + case WM_QUERYENDSESSION: + if ((lParam & ENDSESSION_CRITICAL) == FALSE) { + LoadString(GetModuleHandle(NULL), 0x2DBu, szShutdownReason, ARRAYSIZE(szShutdownReason)); + ShutdownBlockReasonCreate(this->m_hwndSound, szShutdownReason); + PlayLogonLogoffSound(&this->m_thread, (lParam & ENDSESSION_LOGOFF) != 0 ? ST_LOGOFF : ST_EXIT); + if (this->m_thread) { + g_hSoundThread = this->m_thread; + WaitForSingleObject(this->m_thread, INFINITE); + CloseHandle(this->m_thread); + } + } + return 1; + case WM_ENDSESSION: + if (wParam && (lParam & ENDSESSION_CRITICAL) == FALSE && this->m_thread) { + WaitForSingleObject(this->m_thread, INFINITE); + CloseHandle(this->m_thread); + } + DestroyWindow(this->m_hwndSound); + return SHDefWindowProc(hWnd, msg, wParam, lParam); + case WM_NCDESTROY: + SetWindowLong(hWnd, 0, 0); + g_hwSound = NULL; + this->m_hwndSound = NULL; + PostQuitMessage(0); + return 0; + default: + return SHDefWindowProc(hWnd, msg, wParam, lParam); + } +} + +LONG WINAPI CSoundWnd::Release() +{ + LONG lRefCount = InterlockedDecrement(&this->m_refCount); + if ( !lRefCount ) + operator delete((void *)this); + return lRefCount; +} + +BOOL WINAPI InitSoundWindow() +{ + BOOL bSuccess = FALSE; + CSoundWnd* soundWnd = new CSoundWnd(); + if (soundWnd) + { + bSuccess = soundWnd->Init(); + soundWnd->Release(); + } + return bSuccess; +} + +DWORD WINAPI GetCurrentSessionId() +{ + DWORD pSessionId; + DWORD CurrentProcessId = GetCurrentProcessId(); + ProcessIdToSessionId(CurrentProcessId, &pSessionId); + return pSessionId; +} + +HRESULT WINAPI SHCreateSessionKey(REGSAM samDesired, PHKEY phKey) +{ + HRESULT hr = S_OK; + static WCHAR wszSessionKey[256]; + LONG Error; + + if (DWORD dwSessionId = GetCurrentSessionId()) + { + swprintf(wszSessionKey, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SessionInfo\\%d", + dwSessionId); + } + else + hr = HRESULT_FROM_WIN32(GetLastError()); + + if(SUCCEEDED(hr)) + { + Error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wszSessionKey, 0, NULL, + REG_OPTION_VOLATILE, samDesired, NULL, phKey, NULL); + if (Error != ERROR_SUCCESS) + hr = HRESULT_FROM_WIN32(Error); + } + + return hr; +} + +// a three state boolean for bools that need initialization +typedef enum +{ + TRIBIT_UNDEFINED = 0, + TRIBIT_TRUE, + TRIBIT_FALSE, +} TRIBIT; + +TRIBIT g_tbPlayedStartupSound; +DWORD g_dwCurProcId; + +BOOL WINAPI HasPerLogonActionBeenDone(LPCWSTR lpSubKey, TRIBIT* out_tbDone) +{ + DWORD dwDisposition; + HKEY hKey; + HKEY phkResult; + + *out_tbDone = TRIBIT_TRUE; + if ( (int)SHCreateSessionKey(131078, &hKey) >= 0 ) + { + if ( !RegCreateKeyExW(hKey, lpSubKey, 0, 0, 1u, 0x20006u, 0, &phkResult, &dwDisposition) ) + { + RegCloseKey(phkResult); + if ( dwDisposition == 1 ) + *out_tbDone = TRIBIT_FALSE; + } + RegCloseKey(hKey); + } + return *out_tbDone == TRIBIT_TRUE; +} + +BOOL WINAPI HasLogonSoundBeenPlayed() +{ + return HasPerLogonActionBeenDone(L"LogonSoundHasBeenPlayed", &g_tbPlayedStartupSound); +} + +BOOL WINAPI IsDesktopProcess() { + DWORD dwShellProcId = NULL; + DWORD dwCurProcId = GetCurrentProcessId(); + HWND hShellWindow = FindWindow(TEXT("Shell_TrayWnd"), NULL); + if (!hShellWindow) // no desktop present yet, assume it's main explorer starting up + { + g_dwCurProcId = dwCurProcId; + return TRUE; + } + GetWindowThreadProcessId(hShellWindow, &dwShellProcId); + if (dwCurProcId == dwShellProcId || !hShellWindow) { + if (dwCurProcId != g_dwCurProcId) // because the dll handle is shared, the boolean needs to be reset when the explorer process changes + { + g_tbPlayedStartupSound = TRIBIT_UNDEFINED; + g_dwCurProcId = dwCurProcId; + } + return TRUE; + } else { + g_dwCurProcId = dwCurProcId; + return FALSE; + } +} + +// Load the settings for the mod +void LoadSettings(void) +{ + settings.bXpMode = (BOOL)Wh_GetIntSetting(L"xpmode"); +} + +// The mod is being initialized, load settings, hook functions, and do other +// initialization stuff if required. +BOOL Wh_ModInit() { + Wh_Log(L"Init"); + + LoadSettings(); + + g_hExplorer = GetModuleHandleW(NULL); + if (!g_hExplorer) + Wh_Log(L"Failed to get Explorer's handle"); + + g_hShlwapi = LoadLibrary(TEXT("shlwapi.dll")); + + WindhawkUtils::SYMBOL_HOOK explorerExeHooks[] = { + { + { + #ifdef _WIN64 + L"protected: __int64 __cdecl CTray::_OnSessionChange(unsigned __int64,__int64)" + #else + L"protected: long __thiscall CTray::_OnSessionChange(unsigned int,long)" + #endif + }, + (void **)&_OnSessionChange_orig, + (void *)_OnSessionChange_hook, + false + } + }; + + if (!WindhawkUtils::HookSymbols( + g_hExplorer, + explorerExeHooks, + ARRAYSIZE(explorerExeHooks) + )) + { + Wh_Log(L"Failed to hook _OnSessionChange"); + return FALSE; + } + + if (IsDesktopProcess()) { + InitSoundWindow(); + SetProcessShutdownParameters(0x4FF, NULL); + if (!HasLogonSoundBeenPlayed()) + PlayLogonLogoffSound(NULL, settings.bXpMode ? ST_START : ST_LOGON); + else + Wh_Log(TEXT("Logon sound already played in this session.")); + } + + return TRUE; +} + +void Wh_ModSettingsChanged(void) +{ + LoadSettings(); +} + +VOID Wh_ModUninit() +{ + Wh_Log(TEXT("Exiting")); + + if (g_hSoundThread) // make sure DLL stays alive until we finish playing + WaitForSingleObject(g_hSoundThread, INFINITE); + + if (g_hwSound) { + PostMessage(g_hwSound, WM_CLOSE, NULL, NULL); + g_hwSound = NULL; + } + + if (g_hExplorer) { + CloseHandle(g_hExplorer); + } + + if (g_hShlwapi) { + CloseHandle(g_hShlwapi); + } +} From c17ce99483d75133beaed805c705e10872660059 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:51:15 +0300 Subject: [PATCH 3/9] Update VERIFIED_TWITTER_ACCOUNTS in pr_validation.py --- .github/pr_validation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/pr_validation.py b/.github/pr_validation.py index a33c449d4..92c4c94fb 100644 --- a/.github/pr_validation.py +++ b/.github/pr_validation.py @@ -25,6 +25,8 @@ 'https://twitter.com/learn_more': 'https://github.com/learn-more', 'https://twitter.com/realgam3': 'https://github.com/realgam3', 'https://twitter.com/teknixstuff': 'https://github.com/teknixstuff', + 'https://twitter.com/kawaipure': 'https://github.com/kawapure', + 'https://twitter.com/TorutheRedFox': 'https://github.com/TorutheRedFox', } MOD_METADATA_PARAMS = { From f00161757c50921c354688dd50fd0ef968fe36ac Mon Sep 17 00:00:00 2001 From: aubrey <44238627+aubymori@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:09:57 -0500 Subject: [PATCH 4/9] Old This PC Commands 1.0.1: Codebase cleanup (#941) Codebase cleanup - Remove unnecessary `#include` and `DEFINE_GUID` statements - Rely on public `IOpenControlPanel` interface instead of finding `COpenControlPanel::Open` from symbols - Move symbol hook definitions and update the name to match modern convention - Properly free memory in `OpenCplPage` --- mods/old-this-pc-commands.wh.cpp | 119 ++++++++++++++----------------- 1 file changed, 52 insertions(+), 67 deletions(-) diff --git a/mods/old-this-pc-commands.wh.cpp b/mods/old-this-pc-commands.wh.cpp index 3a74b0778..49680132e 100644 --- a/mods/old-this-pc-commands.wh.cpp +++ b/mods/old-this-pc-commands.wh.cpp @@ -2,7 +2,7 @@ // @id old-this-pc-commands // @name Old This PC Commands // @description Makes "Open Settings", "System properties", etc. in This PC use Control Panel instead of Settings -// @version 1.0.0 +// @version 1.0.1 // @author aubymori // @github https://github.com/aubymori // @include explorer.exe @@ -28,40 +28,33 @@ to `ExplorerFrame.dll` takes place to remove the redirect. #include #include -#include #include -#include -DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46); -DEFINE_GUID(IID_OpenControlPanel, 0xD11AD862, 0x66DE, 0x4DF4, 0xBF,0x6C, 0x1F,0x56,0x21,0x99,0x6A,0xF1); -DEFINE_GUID(IID_IServiceProvider, 0x6D5140C1, 0x7436, 0x11CE, 0x80,0x34, 0x00,0xAA,0x00,0x60,0x09,0xFA); -DEFINE_GUID(SID_STopLevelBrowser, 0x4c96be40, 0x915C, 0x11CF, 0x99,0xD3, 0x00,0xAA,0x00,0x4A,0xE8,0x37); - -HRESULT (*COpenControlPanel_Open)(void *pThis, LPCWSTR lpPage, LPCWSTR lpSubPage, IUnknown *pu); - -HRESULT OpenCplPage(IUnknown *pu, LPCWSTR lpPage, LPCWSTR lpSubPage) +HRESULT OpenCplPage(IUnknown *punk, LPCWSTR lpPage, LPCWSTR lpSubPage) { IObjectWithSite *pows; - HRESULT hr = pu->QueryInterface(IID_IObjectWithSite, (void **)&pows); + HRESULT hr = punk->QueryInterface(IID_IObjectWithSite, (void **)&pows); if (SUCCEEDED(hr)) { - IUnknown *pu2; - hr = pows->GetSite(IID_IUnknown, (void **)&pu2); + IUnknown *punk2; + hr = pows->GetSite(IID_PPV_ARGS(&punk2)); if (SUCCEEDED(hr)) { - void *pocp; + IOpenControlPanel *pocp = nullptr; hr = CoCreateInstance( CLSID_OpenControlPanel, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, - IID_OpenControlPanel, - &pocp + IID_PPV_ARGS(&pocp) ); if (SUCCEEDED(hr)) { - hr = COpenControlPanel_Open(pocp, lpPage, lpSubPage, pu2); + hr = pocp->Open(lpPage, lpSubPage, punk2); + pocp->Release(); } + punk2->Release(); } + pows->Release(); } return hr; } @@ -71,52 +64,79 @@ typedef HRESULT (*CDrivesViewCallback__OnEventPunkSite)(IUnknown *, IUnknown *, CDrivesViewCallback__OnEventPunkSite CDrivesViewCallback__OnOpenSystemSettingsPunkSite_orig; HRESULT CDrivesViewCallback__OnOpenSystemSettingsPunkSite_hook( - IUnknown *pu, - IUnknown *pu2, + IUnknown *punk, + IUnknown *punkRibbon, IShellItemArray *psia, IBindCtx *pbctx ) { - HRESULT hr = OpenCplPage(pu, NULL, NULL); + HRESULT hr = OpenCplPage(punk, NULL, NULL); if (!SUCCEEDED(hr)) { - hr = OpenCplPage(pu2, NULL, NULL); + hr = OpenCplPage(punkRibbon, NULL, NULL); } return hr; } CDrivesViewCallback__OnEventPunkSite CDrivesViewCallback__OnSystemPropertiesPunkSite_orig; HRESULT CDrivesViewCallback__OnSystemPropertiesPunkSite_hook( - IUnknown *pu, - IUnknown *pu2, + IUnknown *punk, + IUnknown *punkRibbon, IShellItemArray *psia, IBindCtx *pbctx ) { - HRESULT hr = OpenCplPage(pu, L"Microsoft.System", NULL); + HRESULT hr = OpenCplPage(punk, L"Microsoft.System", NULL); if (!SUCCEEDED(hr)) { - hr = OpenCplPage(pu2, L"Microsoft.System", NULL); + hr = OpenCplPage(punkRibbon, L"Microsoft.System", NULL); } return hr; } CDrivesViewCallback__OnEventPunkSite CDrivesViewCallback__OnAddRemoveProgramsPunkSite_orig; HRESULT CDrivesViewCallback__OnAddRemoveProgramsPunkSite_hook( - IUnknown *pu, - IUnknown *pu2, + IUnknown *punk, + IUnknown *punkRibbon, IShellItemArray *psia, IBindCtx *pbctx ) { - HRESULT hr = OpenCplPage(pu, L"Microsoft.ProgramsAndFeatures", NULL); + HRESULT hr = OpenCplPage(punk, L"Microsoft.ProgramsAndFeatures", NULL); if (!SUCCEEDED(hr)) { - hr = OpenCplPage(pu2, L"Microsoft.ProgramsAndFeatures", NULL); + hr = OpenCplPage(punkRibbon, L"Microsoft.ProgramsAndFeatures", NULL); } return hr; } +const WindhawkUtils::SYMBOL_HOOK shell32DllHooks[] = { + { + { + L"public: static long __cdecl CDrivesViewCallback::_OnOpenSystemSettingsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" + }, + &CDrivesViewCallback__OnOpenSystemSettingsPunkSite_orig, + CDrivesViewCallback__OnOpenSystemSettingsPunkSite_hook, + false + }, + { + { + L"public: static long __cdecl CDrivesViewCallback::_OnSystemPropertiesPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" + }, + &CDrivesViewCallback__OnSystemPropertiesPunkSite_orig, + CDrivesViewCallback__OnSystemPropertiesPunkSite_hook, + false + }, + { + { + L"public: static long __cdecl CDrivesViewCallback::_OnAddRemoveProgramsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" + }, + &CDrivesViewCallback__OnAddRemoveProgramsPunkSite_orig, + CDrivesViewCallback__OnAddRemoveProgramsPunkSite_hook, + false + } +}; + BOOL Wh_ModInit(void) { HMODULE hShell32 = LoadLibraryW(L"shell32.dll"); @@ -126,45 +146,10 @@ BOOL Wh_ModInit(void) return FALSE; } - const WindhawkUtils::SYMBOL_HOOK hooks[] = { - { - { - L"public: virtual long __cdecl COpenControlPanel::Open(unsigned short const *,unsigned short const *,struct IUnknown *)" - }, - &COpenControlPanel_Open, - nullptr, - false - }, - { - { - L"public: static long __cdecl CDrivesViewCallback::_OnOpenSystemSettingsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" - }, - &CDrivesViewCallback__OnOpenSystemSettingsPunkSite_orig, - CDrivesViewCallback__OnOpenSystemSettingsPunkSite_hook, - false - }, - { - { - L"public: static long __cdecl CDrivesViewCallback::_OnSystemPropertiesPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" - }, - &CDrivesViewCallback__OnSystemPropertiesPunkSite_orig, - CDrivesViewCallback__OnSystemPropertiesPunkSite_hook, - false - }, - { - { - L"public: static long __cdecl CDrivesViewCallback::_OnAddRemoveProgramsPunkSite(struct IUnknown *,struct IUnknown *,struct IShellItemArray *,struct IBindCtx *)" - }, - &CDrivesViewCallback__OnAddRemoveProgramsPunkSite_orig, - CDrivesViewCallback__OnAddRemoveProgramsPunkSite_hook, - false - } - }; - if (!WindhawkUtils::HookSymbols( hShell32, - hooks, - ARRAYSIZE(hooks) + shell32DllHooks, + ARRAYSIZE(shell32DllHooks) )) { Wh_Log(L"Failed to hook one or more symbol functions in shell32.dll"); From d2ebc37c2e5b3b8c2cda9d391238b7ac3879fd63 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:32:03 +0300 Subject: [PATCH 5/9] Cycle taskbar buttons with mouse wheel v1.1.5 (#945) * Added support for Windows 11 version 24H2. --- mods/taskbar-wheel-cycle.wh.cpp | 182 +++++++++++++++++--------------- 1 file changed, 95 insertions(+), 87 deletions(-) diff --git a/mods/taskbar-wheel-cycle.wh.cpp b/mods/taskbar-wheel-cycle.wh.cpp index b2a099f8b..a2e25a510 100644 --- a/mods/taskbar-wheel-cycle.wh.cpp +++ b/mods/taskbar-wheel-cycle.wh.cpp @@ -2,7 +2,7 @@ // @id taskbar-wheel-cycle // @name Cycle taskbar buttons with mouse wheel // @description Use the mouse wheel while hovering over the taskbar to cycle between taskbar buttons (Windows 11 only) -// @version 1.1.4 +// @version 1.1.5 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -108,6 +108,15 @@ enum { kHotkeyIdRight, }; +using CTaskBtnGroup_GetGroupType_t = int(WINAPI*)(void* pThis); +CTaskBtnGroup_GetGroupType_t CTaskBtnGroup_GetGroupType; + +using CTaskBtnGroup_GetNumItems_t = int(WINAPI*)(void* pThis); +CTaskBtnGroup_GetNumItems_t CTaskBtnGroup_GetNumItems; + +using CTaskBtnGroup_GetTaskItem_t = void*(WINAPI*)(void* pThis, int index); +CTaskBtnGroup_GetTaskItem_t CTaskBtnGroup_GetTaskItem; + using CWindowTaskItem_GetWindow_t = HWND(WINAPI*)(PVOID pThis); CWindowTaskItem_GetWindow_t CWindowTaskItem_GetWindow_Original; @@ -225,36 +234,31 @@ BOOL IsMinimizedTaskItem(LONG_PTR* task_item) { BOOL TaskbarScrollRight(int button_groups_count, LONG_PTR** button_groups, int* p_button_group_index, - int* p_button_index, - int* p_buttons_count, - LONG_PTR*** p_buttons) { + int* p_button_index) { int button_group_index = *p_button_group_index; int button_index = *p_button_index; - int buttons_count = *p_buttons_count; - LONG_PTR** buttons = *p_buttons; - LONG_PTR* plp; int button_group_type; - if (button_group_index == -1 || ++button_index >= buttons_count) { + int buttons_count = + button_group_index == -1 + ? 0 + : CTaskBtnGroup_GetNumItems(button_groups[button_group_index]); + if (++button_index >= buttons_count) { do { button_group_index++; - if (button_group_index >= button_groups_count) + if (button_group_index >= button_groups_count) { return FALSE; + } - button_group_type = (int)button_groups[button_group_index][8]; + button_group_type = + CTaskBtnGroup_GetGroupType(button_groups[button_group_index]); } while (button_group_type != 1 && button_group_type != 3); - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - button_index = 0; } *p_button_group_index = button_group_index; *p_button_index = button_index; - *p_buttons_count = buttons_count; - *p_buttons = buttons; return TRUE; } @@ -262,38 +266,32 @@ BOOL TaskbarScrollRight(int button_groups_count, BOOL TaskbarScrollLeft(int button_groups_count, LONG_PTR** button_groups, int* p_button_group_index, - int* p_button_index, - int* p_buttons_count, - LONG_PTR*** p_buttons) { + int* p_button_index) { int button_group_index = *p_button_group_index; int button_index = *p_button_index; - int buttons_count = *p_buttons_count; - LONG_PTR** buttons = *p_buttons; - LONG_PTR* plp; int button_group_type; if (button_group_index == -1 || --button_index < 0) { - if (button_group_index == -1) + if (button_group_index == -1) { button_group_index = button_groups_count; + } do { button_group_index--; - if (button_group_index < 0) + if (button_group_index < 0) { return FALSE; + } - button_group_type = (int)button_groups[button_group_index][8]; + button_group_type = + CTaskBtnGroup_GetGroupType(button_groups[button_group_index]); } while (button_group_type != 1 && button_group_type != 3); - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - + int buttons_count = + CTaskBtnGroup_GetNumItems(button_groups[button_group_index]); button_index = buttons_count - 1; } *p_button_group_index = button_group_index; - *p_buttons_count = buttons_count; - *p_buttons = buttons; *p_button_index = button_index; return TRUE; @@ -309,20 +307,11 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, int button_group_index, button_index; BOOL bRotateRight; int prev_button_group_index, prev_button_index; - LONG_PTR* plp; - int buttons_count; - LONG_PTR** buttons; BOOL bScrollSucceeded; button_group_index = button_group_index_active; button_index = button_index_active; - if (button_group_index != -1) { - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - } - bRotateRight = TRUE; if (nRotates < 0) { bRotateRight = FALSE; @@ -334,31 +323,33 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, while (nRotates--) { if (bRotateRight) { - bScrollSucceeded = TaskbarScrollRight( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + bScrollSucceeded = + TaskbarScrollRight(button_groups_count, button_groups, + &button_group_index, &button_index); while (bScrollSucceeded && bSkipMinimized && - IsMinimizedTaskItem((LONG_PTR*)buttons[button_index][4])) { - bScrollSucceeded = TaskbarScrollRight( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + IsMinimizedTaskItem((LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[button_group_index], button_index))) { + bScrollSucceeded = + TaskbarScrollRight(button_groups_count, button_groups, + &button_group_index, &button_index); } } else { - bScrollSucceeded = TaskbarScrollLeft( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + bScrollSucceeded = + TaskbarScrollLeft(button_groups_count, button_groups, + &button_group_index, &button_index); while (bScrollSucceeded && bSkipMinimized && - IsMinimizedTaskItem((LONG_PTR*)buttons[button_index][4])) { - bScrollSucceeded = TaskbarScrollLeft( - button_groups_count, button_groups, &button_group_index, - &button_index, &buttons_count, &buttons); + IsMinimizedTaskItem((LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[button_group_index], button_index))) { + bScrollSucceeded = + TaskbarScrollLeft(button_groups_count, button_groups, + &button_group_index, &button_index); } } if (!bScrollSucceeded) { // If no results were found in the whole taskbar if (prev_button_group_index == -1) { - return NULL; + return nullptr; } if (bWarpAround) { @@ -371,10 +362,6 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, button_group_index = prev_button_group_index; button_index = prev_button_index; - plp = (LONG_PTR*)button_groups[button_group_index][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; - break; } } @@ -384,10 +371,12 @@ LONG_PTR* TaskbarScrollHelper(int button_groups_count, } if (button_group_index == button_group_index_active && - button_index == button_index_active) - return NULL; + button_index == button_index_active) { + return nullptr; + } - return (LONG_PTR*)buttons[button_index][4]; + return (LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[button_group_index], button_index); } LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, @@ -395,44 +384,42 @@ LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, BOOL bSkipMinimized, BOOL bWarpAround, LONG_PTR* src_task_item) { - LONG_PTR* button_group_active; - int button_group_index_active, button_index_active; - LONG_PTR* plp; - int button_groups_count; - LONG_PTR** button_groups; - int button_group_type; - int buttons_count; - LONG_PTR** buttons; - int i, j; + if (nRotates == 0) { + return nullptr; + } - if (nRotates == 0) - return NULL; + LONG_PTR* plp = + (LONG_PTR*)*EV_MM_TASKLIST_BUTTON_GROUPS_HDPA(lpMMTaskListLongPtr); + if (!plp) { + return nullptr; + } - plp = (LONG_PTR*)*EV_MM_TASKLIST_BUTTON_GROUPS_HDPA(lpMMTaskListLongPtr); - if (!plp) - return NULL; + int button_groups_count = (int)plp[0]; + LONG_PTR** button_groups = (LONG_PTR**)plp[1]; - button_groups_count = (int)plp[0]; - button_groups = (LONG_PTR**)plp[1]; + int button_group_index_active, button_index_active; if (src_task_item) { + int i; for (i = 0; i < button_groups_count; i++) { - button_group_type = (int)button_groups[i][8]; + int button_group_type = + CTaskBtnGroup_GetGroupType(button_groups[i]); if (button_group_type == 1 || button_group_type == 3) { - plp = (LONG_PTR*)button_groups[i][7]; - buttons_count = (int)plp[0]; - buttons = (LONG_PTR**)plp[1]; + int buttons_count = CTaskBtnGroup_GetNumItems(button_groups[i]); + int j; for (j = 0; j < buttons_count; j++) { - if ((LONG_PTR*)buttons[j][4] == src_task_item) { + if ((LONG_PTR*)CTaskBtnGroup_GetTaskItem( + button_groups[i], j) == src_task_item) { button_group_index_active = i; button_index_active = j; break; } } - if (j < buttons_count) + if (j < buttons_count) { break; + } } } @@ -441,12 +428,13 @@ LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, button_index_active = -1; } } else { - button_group_active = + LONG_PTR* button_group_active = *EV_MM_TASKLIST_ACTIVE_BUTTON_GROUP(lpMMTaskListLongPtr); button_index_active = *EV_MM_TASKLIST_ACTIVE_BUTTON_INDEX(lpMMTaskListLongPtr); if (button_group_active && button_index_active >= 0) { + int i; for (i = 0; i < button_groups_count; i++) { if (button_groups[i] == button_group_active) { button_group_index_active = i; @@ -454,8 +442,9 @@ LONG_PTR* TaskbarScroll(LONG_PTR lpMMTaskListLongPtr, } } - if (i == button_groups_count) - return NULL; + if (i == button_groups_count) { + return nullptr; + } } else { button_group_index_active = -1; button_index_active = -1; @@ -1236,7 +1225,14 @@ bool HookSymbols(HMODULE module, if (noAddressMatchCount == symbolHooks[i].symbols.size()) { Wh_Log(L"Optional symbol %d doesn't exist (from cache)", i); + symbolResolved[i] = true; + + for (auto hookSymbol : symbolHooks[i].symbols) { + newSystemCacheStr += cacheSep; + newSystemCacheStr += hookSymbol; + newSystemCacheStr += cacheSep; + } } } @@ -1513,6 +1509,18 @@ BOOL HookTaskbarDllSymbols() { } SYMBOL_HOOK taskbarDllHooks[] = { + { + {LR"(public: virtual enum eTBGROUPTYPE __cdecl CTaskBtnGroup::GetGroupType(void))"}, + (void**)&CTaskBtnGroup_GetGroupType, + }, + { + {LR"(public: virtual int __cdecl CTaskBtnGroup::GetNumItems(void))"}, + (void**)&CTaskBtnGroup_GetNumItems, + }, + { + {LR"(public: virtual struct ITaskItem * __cdecl CTaskBtnGroup::GetTaskItem(int))"}, + (void**)&CTaskBtnGroup_GetTaskItem, + }, { {LR"(public: virtual struct HWND__ * __cdecl CWindowTaskItem::GetWindow(void))"}, (void**)&CWindowTaskItem_GetWindow_Original, From 59083d3667a6d5170f10f51bc2c112e5f32d5e1d Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:37:50 +0300 Subject: [PATCH 6/9] Try to fix GitHub Actions deployment --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4f4b9f02a..98c98bf79 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,5 @@ { + "type": "module", "devDependencies": { "@types/node": "^17.0.13", "@types/showdown": "^2.0.0" From 769a313ba347d2af37cb4e80255546d58d94c041 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 21:41:47 +0300 Subject: [PATCH 7/9] Try to fix GitHub Actions deployment (2) --- .github/workflows/deploy.yml | 4 +- package-lock.json | 713 ++++++++++++++++++++++++++++++++++- package.json | 4 +- 3 files changed, 715 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ea434ce78..8c06344e7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,10 +22,8 @@ jobs: fetch-depth: 0 - name: Install dependencies run: npm install - - name: Install global dependencies - run: npm install -g ts-node - name: Prepare static content - run: ts-node deploy.ts + run: npx tsx deploy.ts - name: Deploy uses: peaceiris/actions-gh-pages@v4 with: diff --git a/package-lock.json b/package-lock.json index 3d58260d7..41ff07605 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,392 @@ }, "devDependencies": { "@types/node": "^17.0.13", - "@types/showdown": "^2.0.0" + "@types/showdown": "^2.0.0", + "tsx": "^4.19.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@types/node": { @@ -33,6 +418,45 @@ "node": "^12.20.0 || >=14" } }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -44,6 +468,41 @@ "node": ">=0.4.0" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -64,6 +523,25 @@ "url": "https://www.paypal.me/tiviesantos" } }, + "node_modules/tsx": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", + "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/xml-js": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", @@ -77,6 +555,174 @@ } }, "dependencies": { + "@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "dev": true, + "optional": true + }, "@types/node": { "version": "17.0.13", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.13.tgz", @@ -94,6 +740,38 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" }, + "esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "feed": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", @@ -102,6 +780,28 @@ "xml-js": "^1.6.11" } }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "requires": { + "resolve-pkg-maps": "^1.0.0" + } + }, + "resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -115,6 +815,17 @@ "commander": "^9.0.0" } }, + "tsx": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.0.tgz", + "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", + "dev": true, + "requires": { + "esbuild": "~0.23.0", + "fsevents": "~2.3.3", + "get-tsconfig": "^4.7.5" + } + }, "xml-js": { "version": "1.6.11", "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", diff --git a/package.json b/package.json index 98c98bf79..31ee6fab5 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "type": "module", "devDependencies": { "@types/node": "^17.0.13", - "@types/showdown": "^2.0.0" + "@types/showdown": "^2.0.0", + "tsx": "^4.19.0" }, "dependencies": { "feed": "^4.2.2", From 89b7248389967e412f6de6213e320adbfee9ad89 Mon Sep 17 00:00:00 2001 From: Michael Maltsev <4129781+m417z@users.noreply.github.com> Date: Mon, 9 Sep 2024 22:23:13 +0300 Subject: [PATCH 8/9] Virtual Desktop Preserve Taskbar Order v1.0.4: (#946) * Added support for Windows 11 version 24H2. --- mods/virtual-desktop-taskbar-order.wh.cpp | 61 +++++++++++++++-------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/mods/virtual-desktop-taskbar-order.wh.cpp b/mods/virtual-desktop-taskbar-order.wh.cpp index 7fbbd025b..babbd7a02 100644 --- a/mods/virtual-desktop-taskbar-order.wh.cpp +++ b/mods/virtual-desktop-taskbar-order.wh.cpp @@ -2,7 +2,7 @@ // @id virtual-desktop-taskbar-order // @name Virtual Desktop Preserve Taskbar Order // @description The order on the taskbar isn't preserved between virtual desktop switches, this mod fixes it -// @version 1.0.3 +// @version 1.0.4 // @author m417z // @github https://github.com/m417z // @twitter https://twitter.com/m417z @@ -127,6 +127,15 @@ size_t* EV_APP_VIEW_MGR_APP_ARRAY_SIZE(LONG_PTR lp) { #pragma endregion // offsets +using CTaskBtnGroup_GetGroup_t = void*(WINAPI*)(void* pThis); +CTaskBtnGroup_GetGroup_t CTaskBtnGroup_GetGroup; + +using CTaskBtnGroup_GetNumItems_t = int(WINAPI*)(void* pThis); +CTaskBtnGroup_GetNumItems_t CTaskBtnGroup_GetNumItems; + +using CTaskBtnGroup_GetTaskItem_t = void*(WINAPI*)(void* pThis, int index); +CTaskBtnGroup_GetTaskItem_t CTaskBtnGroup_GetTaskItem; + using CTaskGroup_DoesWindowMatch_t = HRESULT(WINAPI*)(LPVOID pThis, HWND hCompareWnd, @@ -283,32 +292,28 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, LONG_PTR** button_groups = (LONG_PTR**)plp[1]; LONG_PTR* button_group = button_groups[nButtonGroupIndex]; - plp = (LONG_PTR*)button_group[7]; - if (!plp) - return; - - int buttons_count = (int)plp[0]; - LONG_PTR** buttons = (LONG_PTR**)plp[1]; - if (buttons_count == 0) + int buttons_count = CTaskBtnGroup_GetNumItems(button_group); + if (buttons_count == 0) { return; + } - LONG_PTR* task_group = (LONG_PTR*)button_group[4]; - plp = (LONG_PTR*)task_group[4]; - if (!plp) + LONG_PTR* task_group = (LONG_PTR*)CTaskBtnGroup_GetGroup(button_group); + if (!task_group) { return; + } - int task_items_count = (int)plp[0]; - if (task_items_count == 0) + LONG_PTR* first_task_item = + (LONG_PTR*)CTaskBtnGroup_GetTaskItem(button_group, 0); + if (!first_task_item) { return; - - LONG_PTR** task_items = (LONG_PTR**)plp[1]; + } plp = *(LONG_PTR**)task_group; void** ppTaskGroupRelease = (void**)&plp[2]; PointerRedirectionAdd(ppTaskGroupRelease, (void*)TaskGroupReleaseHook, &prTaskGroupRelease); - plp = *(LONG_PTR**)task_items[0]; + plp = *(LONG_PTR**)first_task_item; void** ppTaskItemRelease = (void**)&plp[2]; PointerRedirectionAdd(ppTaskItemRelease, (void*)TaskItemReleaseHook, &prTaskItemRelease); @@ -352,8 +357,9 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, break; } - if (!g_taskGroupVirtualDesktopReleased) + if (!g_taskGroupVirtualDesktopReleased) { continue; + } if (g_taskGroupVirtualDesktopReleased != task_group) { if (nRightNeighbourItemIndex == nArraySize) { @@ -361,7 +367,7 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, j++) { LONG_PTR* check_button_group = button_groups[j]; LONG_PTR* check_task_group = - (LONG_PTR*)check_button_group[4]; + (LONG_PTR*)CTaskBtnGroup_GetGroup(check_button_group); if (g_taskGroupVirtualDesktopReleased == check_task_group) { // The current item in lpArray is from the same group // of at least one of the items in button_groups to the @@ -375,12 +381,13 @@ void OnButtonGroupInserted(LONG_PTR lpTaskSwLongPtr, continue; } - if (!g_taskItemVirtualDesktopReleased) + if (!g_taskItemVirtualDesktopReleased) { continue; + } for (int j = 0; j < buttons_count; j++) { - LONG_PTR* button = buttons[j]; - LONG_PTR* task_item = (LONG_PTR*)button[4]; + LONG_PTR* task_item = + (LONG_PTR*)CTaskBtnGroup_GetTaskItem(button_group, j); if (g_taskItemVirtualDesktopReleased == task_item) { // The current item in lpArray matches one of the @@ -943,6 +950,18 @@ BOOL Wh_ModInit() { } SYMBOL_HOOK taskbarDllHooks[] = { + { + {LR"(public: virtual struct ITaskGroup * __cdecl CTaskBtnGroup::GetGroup(void))"}, + (void**)&CTaskBtnGroup_GetGroup, + }, + { + {LR"(public: virtual int __cdecl CTaskBtnGroup::GetNumItems(void))"}, + (void**)&CTaskBtnGroup_GetNumItems, + }, + { + {LR"(public: virtual struct ITaskItem * __cdecl CTaskBtnGroup::GetTaskItem(int))"}, + (void**)&CTaskBtnGroup_GetTaskItem, + }, { {LR"(public: virtual long __cdecl CTaskGroup::DoesWindowMatch(struct HWND__ *,struct _ITEMIDLIST_ABSOLUTE const *,unsigned short const *,enum WINDOWMATCHCONFIDENCE *,struct ITaskItem * *))"}, (void**)&CTaskGroup_DoesWindowMatch_Original, From 93793152290b764989cd5f1f1ae4f0dd156bee36 Mon Sep 17 00:00:00 2001 From: Olivia <149018134+OliviaIsTyping@users.noreply.github.com> Date: Tue, 10 Sep 2024 23:38:25 -0600 Subject: [PATCH 9/9] FixStrips WH Port - v1.0 (#948) * Add files via upload Add fixstrips.wh.cpp * Update and rename fixstrips.wh.cpp to mods/fix-strips.wh.cpp * Move credits mention to description * update README for more clarity on mod functionality --------- Co-authored-by: Tech Stuff <103606018+teknixstuff@users.noreply.github.com> --- mods/fix-strips.wh.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 mods/fix-strips.wh.cpp diff --git a/mods/fix-strips.wh.cpp b/mods/fix-strips.wh.cpp new file mode 100644 index 000000000..e9d0240f9 --- /dev/null +++ b/mods/fix-strips.wh.cpp @@ -0,0 +1,71 @@ +// ==WindhawkMod== +// @id fix-strips +// @name FixStrips WH Port +// @description Port of the fixstrips AHK script that fixes some classic theme issues with Explorer7 +// @version 1.0 +// @author OliveIsTyping +// @github https://github.com/OliviaIsTyping +// @include explorer.exe +// @compilerOptions -lcomdlg32 +// ==/WindhawkMod== + +// ==WindhawkModReadme== +/* +## NOTE: + +This mod fixes the presence of the DWM window frame around the taskbar when using classic theme with [Explorer7](https://winclassic.net/thread/2588/explorer7-windows-explorer-10-11). You may need to restart explorer.exe when enabling the mod to see the changes take effect. + + +## Before +![Before](https://raw.githubusercontent.com/OliviaIsTyping/images/main/fixstrips-before.png) + +## After +![After](https://raw.githubusercontent.com/OliviaIsTyping/images/main/fixstrips-after.png) + +Credits to [@Anixx](https://github.com/Anixx) for the original AHK Script +*/ +// ==/WindhawkModReadme== + +// ==WindhawkModSettings== + +// ==/WindhawkModSettings== +#include + +using CreateWindowExW_t = decltype(&CreateWindowExW); +CreateWindowExW_t CreateWindowExW_Orig; +HWND WINAPI CreateWindowExW_Hook(DWORD dwExStyle,LPCWSTR lpClassName,LPCWSTR lpWindowName,DWORD dwStyle,int X,int Y,int nWidth,int nHeight,HWND hWndParent, + HMENU hMenu,HINSTANCE hInstance,LPVOID lpParam) { + + wchar_t wszClassName[200]; + ZeroMemory(wszClassName, 200); + //Add WS_DLGFRAME to Shell_TrayWnd + if ((((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0) && !wcscmp(lpClassName, L"Shell_TrayWnd")) + { + dwStyle |= WS_DLGFRAME; + } + HWND hWnd = CreateWindowExW_Orig(dwExStyle,lpClassName,lpWindowName,dwStyle,X,Y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam); + + //Remove WS_DLGFRAME from Shell_TrayWnd (dont ask why it works but it does :D) + if ((((ULONG_PTR)lpClassName & ~(ULONG_PTR)0xffff) != 0) && !wcscmp(lpClassName, L"Shell_TrayWnd")) + { + SetWindowLongPtrW(hWnd,GWL_STYLE,GetWindowLongPtrW(hWnd, GWL_STYLE) & ~WS_DLGFRAME); + } + + return hWnd; +} + + +// The mod is being initialized, load settings, hook functions, and do other initialization stuff if required. +BOOL Wh_ModInit() { + Wh_Log(L"Init"); + + Wh_SetFunctionHook((void*)CreateWindowExW, + (void*)CreateWindowExW_Hook, + (void**)&CreateWindowExW_Orig); + return TRUE; +} + +// The mod is being unloaded, free all allocated resources. +void Wh_ModUninit() { + Wh_Log(L"Uninit"); +}