mirror of
https://github.com/reactos/reactos.git
synced 2024-11-07 15:10:53 +00:00
c17a8770a3
Fix app switcher (Alt+Tab)'s behavior when there is only one app window. CORE-16176
845 lines
20 KiB
C
845 lines
20 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS system libraries
|
|
* FILE: win32ss/user/user32/controls/appswitch.c
|
|
* PURPOSE: app switching functionality
|
|
* PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
|
|
* David Quintana (gigaherz@gmail.com)
|
|
* Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
|
|
*/
|
|
|
|
//
|
|
// TODO:
|
|
// Move to Win32k.
|
|
// Add registry support.
|
|
//
|
|
//
|
|
|
|
#include <user32.h>
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(user32);
|
|
|
|
#define DIALOG_MARGIN 8 // margin of dialog contents
|
|
|
|
#define CX_ICON 32 // width of icon
|
|
#define CY_ICON 32 // height of icon
|
|
#define ICON_MARGIN 4 // margin width around an icon
|
|
|
|
#define CX_ITEM (CX_ICON + 2 * ICON_MARGIN)
|
|
#define CY_ITEM (CY_ICON + 2 * ICON_MARGIN)
|
|
#define ITEM_MARGIN 4 // margin width around an item
|
|
|
|
#define CX_ITEM_SPACE (CX_ITEM + 2 * ITEM_MARGIN)
|
|
#define CY_ITEM_SPACE (CY_ITEM + 2 * ITEM_MARGIN)
|
|
|
|
#define CY_TEXT_MARGIN 4 // margin height around text
|
|
|
|
// limit the number of windows shown in the alt-tab window
|
|
// 120 windows results in (12*40) by (10*40) pixels worth of icons.
|
|
#define MAX_WINDOWS 120
|
|
|
|
// Global variables
|
|
HWND switchdialog = NULL;
|
|
HFONT dialogFont;
|
|
int selectedWindow = 0;
|
|
BOOL isOpen = FALSE;
|
|
|
|
int fontHeight=0;
|
|
|
|
WCHAR windowText[1024];
|
|
|
|
HWND windowList[MAX_WINDOWS];
|
|
HICON iconList[MAX_WINDOWS];
|
|
int windowCount = 0;
|
|
|
|
int cxBorder, cyBorder;
|
|
int nItems, nCols, nRows;
|
|
int itemsW, itemsH;
|
|
int totalW, totalH;
|
|
int xOffset, yOffset;
|
|
POINT ptStart;
|
|
|
|
int nShift = 0;
|
|
|
|
BOOL Esc = FALSE;
|
|
|
|
BOOL CoolSwitch = TRUE;
|
|
int CoolSwitchRows = 3;
|
|
int CoolSwitchColumns = 7;
|
|
|
|
// window style
|
|
const DWORD Style = WS_POPUP | WS_BORDER | WS_DISABLED;
|
|
const DWORD ExStyle = WS_EX_TOPMOST | WS_EX_DLGMODALFRAME | WS_EX_TOOLWINDOW;
|
|
|
|
DWORD wtodw(const WCHAR *psz)
|
|
{
|
|
const WCHAR *pch = psz;
|
|
DWORD Value = 0;
|
|
while ('0' <= *pch && *pch <= '9')
|
|
{
|
|
Value *= 10;
|
|
Value += *pch - L'0';
|
|
}
|
|
return Value;
|
|
}
|
|
|
|
BOOL LoadCoolSwitchSettings(void)
|
|
{
|
|
CoolSwitch = TRUE;
|
|
CoolSwitchRows = 3;
|
|
CoolSwitchColumns = 7;
|
|
|
|
// FIXME: load the settings from registry
|
|
|
|
TRACE("CoolSwitch: %d\n", CoolSwitch);
|
|
TRACE("CoolSwitchRows: %d\n", CoolSwitchRows);
|
|
TRACE("CoolSwitchColumns: %d\n", CoolSwitchColumns);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void ResizeAndCenter(HWND hwnd, int width, int height)
|
|
{
|
|
int x, y;
|
|
RECT Rect;
|
|
|
|
int screenwidth = GetSystemMetrics(SM_CXSCREEN);
|
|
int screenheight = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
x = (screenwidth - width) / 2;
|
|
y = (screenheight - height) / 2;
|
|
|
|
SetRect(&Rect, x, y, x + width, y + height);
|
|
AdjustWindowRectEx(&Rect, Style, FALSE, ExStyle);
|
|
|
|
x = Rect.left;
|
|
y = Rect.top;
|
|
width = Rect.right - Rect.left;
|
|
height = Rect.bottom - Rect.top;
|
|
MoveWindow(hwnd, x, y, width, height, FALSE);
|
|
|
|
ptStart.x = x;
|
|
ptStart.y = y;
|
|
}
|
|
|
|
void MakeWindowActive(HWND hwnd)
|
|
{
|
|
if (IsIconic(hwnd))
|
|
PostMessageW(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
|
|
|
|
BringWindowToTop(hwnd); // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ?
|
|
SetForegroundWindow(hwnd);
|
|
}
|
|
|
|
void CompleteSwitch(BOOL doSwitch)
|
|
{
|
|
if (!isOpen)
|
|
return;
|
|
|
|
isOpen = FALSE;
|
|
|
|
TRACE("[ATbot] CompleteSwitch Hiding Window.\n");
|
|
ShowWindow(switchdialog, SW_HIDE);
|
|
|
|
if(doSwitch)
|
|
{
|
|
if(selectedWindow >= windowCount)
|
|
return;
|
|
|
|
// FIXME: workaround because reactos fails to activate the previous window correctly.
|
|
//if(selectedWindow != 0)
|
|
{
|
|
HWND hwnd = windowList[selectedWindow];
|
|
|
|
GetWindowTextW(hwnd, windowText, _countof(windowText));
|
|
|
|
TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText);
|
|
|
|
MakeWindowActive(hwnd);
|
|
}
|
|
}
|
|
|
|
windowCount = 0;
|
|
}
|
|
|
|
BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam)
|
|
{
|
|
HICON hIcon;
|
|
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
// First try to get the big icon assigned to the window
|
|
hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_BIG, 0);
|
|
if (!hIcon)
|
|
{
|
|
// If no icon is assigned, try to get the icon assigned to the windows' class
|
|
hIcon = (HICON)GetClassLongPtrW(window, GCL_HICON);
|
|
if (!hIcon)
|
|
{
|
|
// If we still don't have an icon, see if we can do with the small icon,
|
|
// or a default application icon
|
|
hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_SMALL2, 0);
|
|
if (!hIcon)
|
|
{
|
|
// using windows logo icon as default
|
|
hIcon = gpsi->hIconWindows;
|
|
if (!hIcon)
|
|
{
|
|
//if all attempts to get icon fails go to the next window
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
windowList[windowCount] = window;
|
|
iconList[windowCount] = CopyIcon(hIcon);
|
|
|
|
windowCount++;
|
|
|
|
// If we got to the max number of windows,
|
|
// we won't be able to add any more
|
|
if(windowCount >= MAX_WINDOWS)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static HWND GetNiceRootOwner(HWND hwnd)
|
|
{
|
|
HWND hwndOwner;
|
|
DWORD ExStyle, OwnerExStyle;
|
|
|
|
for (;;)
|
|
{
|
|
// A window with WS_EX_APPWINDOW is treated as if it has no owner
|
|
ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
|
if (ExStyle & WS_EX_APPWINDOW)
|
|
break;
|
|
|
|
// Is the owner visible?
|
|
// An window with WS_EX_TOOLWINDOW is treated as if it weren't visible
|
|
hwndOwner = GetWindow(hwnd, GW_OWNER);
|
|
OwnerExStyle = GetWindowLong(hwndOwner, GWL_EXSTYLE);
|
|
if (!IsWindowVisible(hwndOwner) || (OwnerExStyle & WS_EX_TOOLWINDOW))
|
|
break;
|
|
|
|
hwnd = hwndOwner;
|
|
}
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
// c.f. http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx
|
|
BOOL IsAltTabWindow(HWND hwnd)
|
|
{
|
|
DWORD ExStyle;
|
|
RECT rc;
|
|
HWND hwndTry, hwndWalk;
|
|
WCHAR szClass[64];
|
|
|
|
// must be visible
|
|
if (!IsWindowVisible(hwnd))
|
|
return FALSE;
|
|
|
|
// must not be WS_EX_TOOLWINDOW
|
|
ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
|
if (ExStyle & WS_EX_TOOLWINDOW)
|
|
return FALSE;
|
|
|
|
// must be not empty rect
|
|
GetWindowRect(hwnd, &rc);
|
|
if (IsRectEmpty(&rc))
|
|
return FALSE;
|
|
|
|
// check special windows
|
|
if (!GetClassNameW(hwnd, szClass, _countof(szClass)) ||
|
|
wcscmp(szClass, L"Shell_TrayWnd") == 0 ||
|
|
wcscmp(szClass, L"Progman") == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// get 'nice' root owner
|
|
hwndWalk = GetNiceRootOwner(hwnd);
|
|
|
|
// walk back from hwndWalk toward hwnd
|
|
for (;;)
|
|
{
|
|
hwndTry = GetLastActivePopup(hwndWalk);
|
|
if (hwndTry == hwndWalk)
|
|
break;
|
|
|
|
ExStyle = GetWindowLong(hwndTry, GWL_EXSTYLE);
|
|
if (IsWindowVisible(hwndTry) && !(ExStyle & WS_EX_TOOLWINDOW))
|
|
break;
|
|
|
|
hwndWalk = hwndTry;
|
|
}
|
|
|
|
return hwnd == hwndTry; // Reached?
|
|
}
|
|
|
|
static BOOL CALLBACK
|
|
EnumWindowsProc(HWND hwnd, LPARAM lParam)
|
|
{
|
|
if (IsAltTabWindow(hwnd))
|
|
{
|
|
if (!EnumerateCallback(hwnd, lParam))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void ProcessMouseMessage(UINT message, LPARAM lParam)
|
|
{
|
|
int xPos = LOWORD(lParam);
|
|
int yPos = HIWORD(lParam);
|
|
|
|
int xIndex = (xPos - DIALOG_MARGIN) / CX_ITEM_SPACE;
|
|
int yIndex = (yPos - DIALOG_MARGIN) / CY_ITEM_SPACE;
|
|
|
|
if (xIndex < 0 || nCols <= xIndex ||
|
|
yIndex < 0 || nRows <= yIndex)
|
|
{
|
|
return;
|
|
}
|
|
|
|
selectedWindow = (yIndex*nCols) + xIndex;
|
|
if (message == WM_MOUSEMOVE)
|
|
{
|
|
InvalidateRect(switchdialog, NULL, TRUE);
|
|
//RedrawWindow(switchdialog, NULL, NULL, 0);
|
|
}
|
|
else
|
|
{
|
|
selectedWindow = (yIndex*nCols) + xIndex;
|
|
CompleteSwitch(TRUE);
|
|
}
|
|
}
|
|
|
|
void OnPaint(HWND hWnd)
|
|
{
|
|
HDC dialogDC;
|
|
PAINTSTRUCT paint;
|
|
RECT cRC, textRC;
|
|
int i, xPos, yPos, CharCount;
|
|
HFONT dcFont;
|
|
HICON hIcon;
|
|
HPEN hPen;
|
|
COLORREF Color;
|
|
|
|
// check
|
|
if (nCols == 0 || nItems == 0)
|
|
return;
|
|
|
|
// begin painting
|
|
dialogDC = BeginPaint(hWnd, &paint);
|
|
if (dialogDC == NULL)
|
|
return;
|
|
|
|
// fill the client area
|
|
GetClientRect(hWnd, &cRC);
|
|
FillRect(dialogDC, &cRC, (HBRUSH)(COLOR_3DFACE + 1));
|
|
|
|
// if the selection index exceeded the display items, then
|
|
// do display item shifting
|
|
if (selectedWindow >= nItems)
|
|
nShift = selectedWindow - nItems + 1;
|
|
else
|
|
nShift = 0;
|
|
|
|
for (i = 0; i < nItems; ++i)
|
|
{
|
|
// get the icon to display
|
|
hIcon = iconList[i + nShift];
|
|
|
|
// calculate the position where we start drawing
|
|
xPos = DIALOG_MARGIN + CX_ITEM_SPACE * (i % nCols) + ITEM_MARGIN;
|
|
yPos = DIALOG_MARGIN + CY_ITEM_SPACE * (i / nCols) + ITEM_MARGIN;
|
|
|
|
// centering
|
|
if (nItems < CoolSwitchColumns)
|
|
{
|
|
xPos += (itemsW - nItems * CX_ITEM_SPACE) / 2;
|
|
}
|
|
|
|
// if this position is selected,
|
|
if (selectedWindow == i + nShift)
|
|
{
|
|
// create a solid pen
|
|
Color = GetSysColor(COLOR_HIGHLIGHT);
|
|
hPen = CreatePen(PS_SOLID, 1, Color);
|
|
|
|
// draw a rectangle with using the pen
|
|
SelectObject(dialogDC, hPen);
|
|
SelectObject(dialogDC, GetStockObject(NULL_BRUSH));
|
|
Rectangle(dialogDC, xPos, yPos, xPos + CX_ITEM, yPos + CY_ITEM);
|
|
Rectangle(dialogDC, xPos + 1, yPos + 1,
|
|
xPos + CX_ITEM - 1, yPos + CY_ITEM - 1);
|
|
|
|
// delete the pen
|
|
DeleteObject(hPen);
|
|
}
|
|
|
|
// draw icon
|
|
DrawIconEx(dialogDC, xPos + ICON_MARGIN, yPos + ICON_MARGIN,
|
|
hIcon, CX_ICON, CY_ICON, 0, NULL, DI_NORMAL);
|
|
}
|
|
|
|
// set the text rectangle
|
|
SetRect(&textRC, DIALOG_MARGIN, DIALOG_MARGIN + itemsH,
|
|
totalW - DIALOG_MARGIN, totalH - DIALOG_MARGIN);
|
|
|
|
// draw the sunken button around text
|
|
DrawFrameControl(dialogDC, &textRC, DFC_BUTTON,
|
|
DFCS_BUTTONPUSH | DFCS_PUSHED);
|
|
|
|
// get text
|
|
CharCount = GetWindowTextW(windowList[selectedWindow], windowText,
|
|
_countof(windowText));
|
|
|
|
// draw text
|
|
dcFont = SelectObject(dialogDC, dialogFont);
|
|
SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT));
|
|
SetBkMode(dialogDC, TRANSPARENT);
|
|
DrawTextW(dialogDC, windowText, CharCount, &textRC,
|
|
DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE);
|
|
SelectObject(dialogDC, dcFont);
|
|
|
|
// end painting
|
|
EndPaint(hWnd, &paint);
|
|
}
|
|
|
|
DWORD CreateSwitcherWindow(HINSTANCE hInstance)
|
|
{
|
|
switchdialog = CreateWindowExW( WS_EX_TOPMOST|WS_EX_DLGMODALFRAME|WS_EX_TOOLWINDOW,
|
|
WC_SWITCH,
|
|
L"",
|
|
WS_POPUP|WS_BORDER|WS_DISABLED,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
400, 150,
|
|
NULL, NULL,
|
|
hInstance, NULL);
|
|
if (!switchdialog)
|
|
{
|
|
TRACE("[ATbot] Task Switcher Window failed to create.\n");
|
|
return 0;
|
|
}
|
|
|
|
isOpen = FALSE;
|
|
return 1;
|
|
}
|
|
|
|
DWORD GetDialogFont(VOID)
|
|
{
|
|
HDC tDC;
|
|
TEXTMETRIC tm;
|
|
|
|
dialogFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
|
|
|
tDC = GetDC(0);
|
|
GetTextMetrics(tDC, &tm);
|
|
fontHeight = tm.tmHeight;
|
|
ReleaseDC(0, tDC);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void PrepareWindow(VOID)
|
|
{
|
|
nItems = windowCount;
|
|
|
|
nCols = CoolSwitchColumns;
|
|
nRows = (nItems + CoolSwitchColumns - 1) / CoolSwitchColumns;
|
|
if (nRows > CoolSwitchRows)
|
|
{
|
|
nRows = CoolSwitchRows;
|
|
nItems = nRows * nCols;
|
|
}
|
|
|
|
itemsW = nCols * CX_ITEM_SPACE;
|
|
itemsH = nRows * CY_ITEM_SPACE;
|
|
|
|
totalW = itemsW + 2 * DIALOG_MARGIN;
|
|
totalH = itemsH + 2 * DIALOG_MARGIN;
|
|
totalH += fontHeight + 2 * CY_TEXT_MARGIN;
|
|
|
|
ResizeAndCenter(switchdialog, totalW, totalH);
|
|
}
|
|
|
|
BOOL ProcessHotKey(VOID)
|
|
{
|
|
if (!isOpen)
|
|
{
|
|
windowCount = 0;
|
|
EnumWindows(EnumWindowsProc, 0);
|
|
|
|
if (windowCount == 0)
|
|
return FALSE;
|
|
|
|
if (windowCount == 1)
|
|
{
|
|
MakeWindowActive(windowList[0]);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!CreateSwitcherWindow(User32Instance))
|
|
return FALSE;
|
|
|
|
selectedWindow = 1;
|
|
|
|
TRACE("[ATbot] HotKey Received. Opening window.\n");
|
|
ShowWindow(switchdialog, SW_SHOWNORMAL);
|
|
MakeWindowActive(switchdialog);
|
|
isOpen = TRUE;
|
|
}
|
|
else
|
|
{
|
|
TRACE("[ATbot] HotKey Received Rotating.\n");
|
|
selectedWindow = (selectedWindow + 1)%windowCount;
|
|
InvalidateRect(switchdialog, NULL, TRUE);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void RotateTasks(BOOL bShift)
|
|
{
|
|
HWND hwndFirst, hwndLast;
|
|
DWORD Size;
|
|
|
|
if (windowCount < 2 || !Esc)
|
|
return;
|
|
|
|
hwndFirst = windowList[0];
|
|
hwndLast = windowList[windowCount - 1];
|
|
|
|
if (bShift)
|
|
{
|
|
SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
|
|
SWP_NOOWNERZORDER | SWP_NOREPOSITION);
|
|
|
|
MakeWindowActive(hwndLast);
|
|
|
|
Size = (windowCount - 1) * sizeof(HWND);
|
|
MoveMemory(&windowList[1], &windowList[0], Size);
|
|
windowList[0] = hwndLast;
|
|
}
|
|
else
|
|
{
|
|
SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
|
|
SWP_NOOWNERZORDER | SWP_NOREPOSITION);
|
|
|
|
MakeWindowActive(windowList[1]);
|
|
|
|
Size = (windowCount - 1) * sizeof(HWND);
|
|
MoveMemory(&windowList[0], &windowList[1], Size);
|
|
windowList[windowCount - 1] = hwndFirst;
|
|
}
|
|
}
|
|
|
|
static void MoveLeft(void)
|
|
{
|
|
selectedWindow = selectedWindow - 1;
|
|
if (selectedWindow < 0)
|
|
selectedWindow = windowCount - 1;
|
|
InvalidateRect(switchdialog, NULL, TRUE);
|
|
}
|
|
|
|
static void MoveRight(void)
|
|
{
|
|
selectedWindow = (selectedWindow + 1) % windowCount;
|
|
InvalidateRect(switchdialog, NULL, TRUE);
|
|
}
|
|
|
|
static void MoveUp(void)
|
|
{
|
|
INT iRow = selectedWindow / nCols;
|
|
INT iCol = selectedWindow % nCols;
|
|
|
|
--iRow;
|
|
if (iRow < 0)
|
|
iRow = nRows - 1;
|
|
|
|
selectedWindow = iRow * nCols + iCol;
|
|
if (selectedWindow >= windowCount)
|
|
selectedWindow = windowCount - 1;
|
|
InvalidateRect(switchdialog, NULL, TRUE);
|
|
}
|
|
|
|
static void MoveDown(void)
|
|
{
|
|
INT iRow = selectedWindow / nCols;
|
|
INT iCol = selectedWindow % nCols;
|
|
|
|
++iRow;
|
|
if (iRow >= nRows)
|
|
iRow = 0;
|
|
|
|
selectedWindow = iRow * nCols + iCol;
|
|
if (selectedWindow >= windowCount)
|
|
selectedWindow = windowCount - 1;
|
|
InvalidateRect(switchdialog, NULL, TRUE);
|
|
}
|
|
|
|
VOID
|
|
DestroyAppWindows(VOID)
|
|
{
|
|
// for every item of the icon list:
|
|
INT i;
|
|
for (i = 0; i < windowCount; ++i)
|
|
{
|
|
// destroy the icon
|
|
DestroyIcon(iconList[i]);
|
|
iconList[i] = NULL;
|
|
}
|
|
}
|
|
|
|
LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
|
|
{
|
|
HWND hwndActive;
|
|
MSG msg;
|
|
|
|
// FIXME: Is loading timing OK?
|
|
LoadCoolSwitchSettings();
|
|
|
|
if (!CoolSwitch)
|
|
return 0;
|
|
|
|
// Already in the loop.
|
|
if (switchdialog || Esc) return 0;
|
|
|
|
if (lParam == VK_ESCAPE)
|
|
{
|
|
Esc = TRUE;
|
|
|
|
windowCount = 0;
|
|
EnumWindows(EnumWindowsProc, 0);
|
|
|
|
if (windowCount < 2)
|
|
return 0;
|
|
|
|
RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0);
|
|
|
|
hwndActive = GetActiveWindow();
|
|
|
|
if (hwndActive == NULL)
|
|
{
|
|
Esc = FALSE;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Capture current active window.
|
|
hwndActive = GetActiveWindow();
|
|
if (hwndActive)
|
|
SetCapture(hwndActive);
|
|
|
|
switch (lParam)
|
|
{
|
|
case VK_TAB:
|
|
if (!GetDialogFont() || !ProcessHotKey())
|
|
goto Exit;
|
|
break;
|
|
|
|
case VK_ESCAPE:
|
|
break;
|
|
|
|
default:
|
|
goto Exit;
|
|
}
|
|
|
|
if (!hwndActive)
|
|
goto Exit;
|
|
|
|
// Main message loop:
|
|
while (1)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
|
|
{
|
|
if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break;
|
|
/* remove the message from the queue */
|
|
PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
|
|
}
|
|
else
|
|
WaitMessage();
|
|
}
|
|
|
|
switch (msg.message)
|
|
{
|
|
case WM_KEYUP:
|
|
{
|
|
PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
|
|
if (msg.wParam == VK_MENU)
|
|
{
|
|
CompleteSwitch(TRUE);
|
|
}
|
|
else if (msg.wParam == VK_RETURN)
|
|
{
|
|
CompleteSwitch(TRUE);
|
|
}
|
|
else if (msg.wParam == VK_ESCAPE)
|
|
{
|
|
TRACE("DoAppSwitch VK_ESCAPE 2\n");
|
|
CompleteSwitch(FALSE);
|
|
}
|
|
goto Exit; //break;
|
|
}
|
|
|
|
case WM_SYSKEYDOWN:
|
|
{
|
|
PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
|
|
if (HIWORD(msg.lParam) & KF_ALTDOWN)
|
|
{
|
|
if ( msg.wParam == VK_TAB )
|
|
{
|
|
if (Esc) break;
|
|
if (GetKeyState(VK_SHIFT) < 0)
|
|
{
|
|
MoveLeft();
|
|
}
|
|
else
|
|
{
|
|
MoveRight();
|
|
}
|
|
}
|
|
else if ( msg.wParam == VK_ESCAPE )
|
|
{
|
|
if (!Esc) break;
|
|
RotateTasks(GetKeyState(VK_SHIFT) < 0);
|
|
}
|
|
else if ( msg.wParam == VK_LEFT )
|
|
{
|
|
MoveLeft();
|
|
}
|
|
else if ( msg.wParam == VK_RIGHT )
|
|
{
|
|
MoveRight();
|
|
}
|
|
else if ( msg.wParam == VK_UP )
|
|
{
|
|
MoveUp();
|
|
}
|
|
else if ( msg.wParam == VK_DOWN )
|
|
{
|
|
MoveDown();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_LBUTTONUP:
|
|
PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
|
|
ProcessMouseMessage(msg.message, msg.lParam);
|
|
goto Exit;
|
|
|
|
default:
|
|
if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Exit:
|
|
ReleaseCapture();
|
|
if (switchdialog) DestroyWindow(switchdialog);
|
|
if (Esc) DestroyAppWindows();
|
|
switchdialog = NULL;
|
|
selectedWindow = 0;
|
|
windowCount = 0;
|
|
Esc = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Switch System Class Window Proc.
|
|
//
|
|
LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode )
|
|
{
|
|
PWND pWnd;
|
|
PALTTABINFO ati;
|
|
pWnd = ValidateHwnd(hWnd);
|
|
if (pWnd)
|
|
{
|
|
if (!pWnd->fnid)
|
|
{
|
|
NtUserSetWindowFNID(hWnd, FNID_SWITCH);
|
|
}
|
|
}
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_NCCREATE:
|
|
if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati))))
|
|
return 0;
|
|
SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati );
|
|
return TRUE;
|
|
|
|
case WM_SHOWWINDOW:
|
|
if (wParam)
|
|
{
|
|
PrepareWindow();
|
|
ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
|
|
ati->cbSize = sizeof(ALTTABINFO);
|
|
ati->cItems = nItems;
|
|
ati->cColumns = nCols;
|
|
ati->cRows = nRows;
|
|
if (nCols)
|
|
{
|
|
ati->iColFocus = (selectedWindow - nShift) % nCols;
|
|
ati->iRowFocus = (selectedWindow - nShift) / nCols;
|
|
}
|
|
else
|
|
{
|
|
ati->iColFocus = 0;
|
|
ati->iRowFocus = 0;
|
|
}
|
|
ati->cxItem = CX_ITEM_SPACE;
|
|
ati->cyItem = CY_ITEM_SPACE;
|
|
ati->ptStart = ptStart;
|
|
}
|
|
return 0;
|
|
|
|
case WM_MOUSEMOVE:
|
|
ProcessMouseMessage(uMsg, lParam);
|
|
return 0;
|
|
|
|
case WM_ACTIVATE:
|
|
if (wParam == WA_INACTIVE)
|
|
{
|
|
CompleteSwitch(FALSE);
|
|
}
|
|
return 0;
|
|
|
|
case WM_PAINT:
|
|
OnPaint(hWnd);
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
isOpen = FALSE;
|
|
ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
|
|
HeapFree( GetProcessHeap(), 0, ati );
|
|
SetWindowLongPtrW( hWnd, 0, 0 );
|
|
DestroyAppWindows();
|
|
NtUserSetWindowFNID(hWnd, FNID_DESTROY);
|
|
return 0;
|
|
}
|
|
return DefWindowProcW(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
|
|
}
|
|
|
|
LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
|
|
}
|