[EXPLORER] Implement Win+<number> hotkeys (#9555)

This commit implements Win+<number>, Win+Shift+<number>,
Win+Shift+Ctrl+<number> and Win+Alt+<number> hotkeys.
These hotkeys activate Nth task on the taskbar.

CORE-20781
This commit is contained in:
Mohammad Amin Mollazadeh
2026-09-27 13:51:46 +03:30
committed by GitHub
parent 1fd8b503be
commit 00a09fd24a
4 changed files with 151 additions and 0 deletions

View File

@@ -36,5 +36,6 @@ add_executable(explorer ${SOURCE} explorer.rc)
target_link_libraries(explorer uuid wine cpprt atl_classes)
set_module_type(explorer win32gui UNICODE)
add_importlibs(explorer setupapi advapi32 gdi32 user32 comctl32 ole32 oleaut32 shell32 browseui shlwapi shdocvw version uxtheme msvcrt kernel32 ntdll)
add_delay_importlibs(explorer psapi)
add_pch(explorer precomp.h SOURCE)
add_cd_file(TARGET explorer DESTINATION reactos FOR all)

View File

@@ -395,6 +395,7 @@ HRESULT CSysPagerWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv);
*/
#define TSWM_UPDATETASKBARPOS (WM_USER + 2)
#define TSWM_ACTIVATETASKINDEX (WM_USER + 88)
HRESULT CTaskSwitchWnd_CreateInstance(IN HWND hWndParent, IN OUT ITrayWindow *Tray, REFIID riid, void **ppv);

View File

@@ -11,6 +11,7 @@
#include <commoncontrols.h>
#include <regstr.h>
#include <shlwapi_undoc.h>
#include <psapi.h>
/* Set DUMP_TASKS to 1 to enable a dump of the tasks and task groups every
5 seconds */
@@ -101,6 +102,23 @@ SHELL_IsRudeWindow(_In_opt_ HMONITOR hMonitor, _In_ HWND hWnd, _In_ BOOL bDontCh
return ::EqualRect(&rcUnion, &rcWnd) && (bDontCheckActive || SHELL_IsRudeWindowActive(hWnd));
}
static BOOL
SHELL_GetImageFileNameByWindow(_In_ HWND hWnd, _Out_cap_(cchPath) LPWSTR pszPath, _In_ DWORD cchPath)
{
DWORD dwProcessId;
HANDLE hProcess;
BOOL bSuccess;
if (GetWindowThreadProcessId(hWnd, &dwProcessId) == 0)
return FALSE;
// TODO: Switch to PROCESS_QUERY_LIMITED_INFORMATION once NT6 kernel32 support is implemented.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId);
if (hProcess == NULL)
return FALSE;
bSuccess = GetModuleFileNameExW(hProcess, NULL, pszPath, cchPath) != 0;
CloseHandle(hProcess);
return bSuccess;
}
////////////////////////////////////////////////////////////////
const WCHAR szTaskSwitchWndClass[] = L"MSTaskSwWClass";
@@ -2096,6 +2114,72 @@ public:
return TRUE;
}
LRESULT OnActivateTaskIndex(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
DWORD dwAccelerators = HIWORD(wParam);
WCHAR szExecutablePath[MAX_PATH];
PTASK_ITEM pTaskItem = FindTaskItemByIndex(LOWORD(wParam));
if (pTaskItem && ::IsWindow(pTaskItem->hWnd))
{
// Win+Shift: New instance
if ((dwAccelerators & MOD_SHIFT) == MOD_SHIFT)
{
if (!SHELL_GetImageFileNameByWindow(pTaskItem->hWnd,
szExecutablePath,
_countof(szExecutablePath)))
{
return FALSE;
}
// Win+Shift+Ctrl is used for creating a new instance of a task as Administrator
LPCWSTR szVerb = L"open";
if ((dwAccelerators & MOD_CONTROL) == MOD_CONTROL)
szVerb = L"runas";
ShellExecuteW(NULL, szVerb, szExecutablePath, NULL, NULL, SW_SHOWNORMAL);
return TRUE;
}
// Win+Alt: Jump list
else if ((dwAccelerators & MOD_ALT) == MOD_ALT)
{
// TODO: Use JumpList once implemented
// Find TaskItem's position on the screen
RECT rcItem, rcToolbar;
m_TaskBar.GetItemRect(pTaskItem->Index, &rcItem);
m_TaskBar.GetWindowRect(&rcToolbar);
OffsetRect(&rcItem, rcToolbar.left, rcToolbar.top);
::SendMessageCallbackW(pTaskItem->hWnd, WM_POPUPSYSTEMMENU, 0, MAKELPARAM(rcItem.left, rcItem.top),
SendAsyncProc, (ULONG_PTR)pTaskItem);
}
// Win+Ctrl: Switch to last window of a task
else if ((dwAccelerators & MOD_CONTROL) == MOD_CONTROL)
{
// TODO: Implement once Superbar is implemented.
return FALSE;
}
// No modifier: just switch to the task
else
{
// TODO: This should launch a pinned task once Superbar is implemented
BOOL bIsMinimized = ::IsIconic(pTaskItem->hWnd);
BOOL bIsActive = (pTaskItem == m_ActiveTaskItem);
if (!bIsMinimized && bIsActive)
{
if (!::IsHungAppWindow(pTaskItem->hWnd))
::ShowWindowAsync(pTaskItem->hWnd, SW_MINIMIZE);
}
else
{
::SwitchToThisWindow(pTaskItem->hWnd, TRUE);
}
}
}
return TRUE;
}
LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
BOOL bSettingsChanged = FALSE;
@@ -2277,6 +2361,7 @@ public:
MESSAGE_HANDLER(WM_COMMAND, OnCommand)
MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
MESSAGE_HANDLER(TSWM_UPDATETASKBARPOS, OnUpdateTaskbarPos)
MESSAGE_HANDLER(TSWM_ACTIVATETASKINDEX, OnActivateTaskIndex)
MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged)
MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
MESSAGE_HANDLER(WM_TIMER, OnTimer)

View File

@@ -44,6 +44,8 @@ HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, ICont
#define IDHK_DESKTOP 0x1fe
#define IDHK_PAGER 0x1ff
#define IDHK_ACTIVATE_TASK 0x209
enum { NONE, TILED, CASCADED } g_Arrangement = NONE;
struct WINDOWPOSBACKUPDATA
@@ -2437,6 +2439,61 @@ ChangePos:
MAKELONG(IDHK_SYS_PROPERTIES, MAKEWORD(VK_PAUSE, MOD_WIN)),
MAKELONG(IDHK_DESKTOP, MAKEWORD('D', MOD_WIN)),
MAKELONG(IDHK_PAGER, MAKEWORD('B', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_ALT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_CONTROL)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('1', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('2', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('3', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('4', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('5', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('6', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('7', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('8', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('9', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
MAKELONG(IDHK_ACTIVATE_TASK, MAKEWORD('0', MOD_WIN | MOD_CONTROL | MOD_SHIFT)),
};
if (!SHRestricted(REST_NOWINKEYS))
{
@@ -3107,6 +3164,13 @@ HandleTrayContextMenu:
LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (wParam == IDHK_ACTIVATE_TASK)
{
// Map the ('1'-'9','0') hotkey to a zero-based task index
DWORD iTask = (HIWORD(lParam) - '0' + 9) % 10;
// Request the task switcher to perform the switch
::SendMessageW(m_TaskSwitch, TSWM_ACTIVATETASKINDEX, MAKEWPARAM(iTask, LOWORD(lParam)), 0);
}
return HandleHotKey(wParam);
}