Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2685 lines
88 KiB

/****************************** Module Header ******************************\
* Module Name: tmswitch.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* History:
* 29-May-1991 DavidPe Created.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*
* COOLSWITCHTRACE is used to trace problems
* in the CoolSwitch window
*/
#undef COOLSWITCHTRACE
#define ALT_F6 2
#define ALT_ESCAPE 1
#define FDIR_FORWARD 0
#define FDIR_BACKWARD 1
/*
* Win95 hard codes the size of the icon matrix, the size of
* the icons, the highlight border and the icon spacing
*/
#define CXICONSLOT 43
#define CYICONSLOT 43
#define CXICONSIZE 32
#define CYICONSIZE 32
/*
* Pointer to the start of the SwitchInfo list.
*/
PSWINFO gpswiFirst;
VOID xxxPaintIconsInSwitchWindow(PWND, PSWINFO, HDC, INT, INT, INT, BOOL, BOOL, PICON);
/***************************************************************************\
* Getpswi
*
* 04-29-96 GerardoB Created
\***************************************************************************/
__inline PSWINFO Getpswi(
PWND pwnd)
{
UserAssert(GETFNID(pwnd) == FNID_SWITCH);
return TestWF(pwnd, WFDESTROYED) ? NULL : ((PSWITCHWND)pwnd)->pswi;
}
/***************************************************************************\
* Setpswi
*
* 04-29-96 GerardoB Created
\***************************************************************************/
__inline VOID Setpswi(
PWND pwnd,
PSWINFO pswi)
{
UserAssert(GETFNID(pwnd) == FNID_SWITCH);
((PSWITCHWND)pwnd)->pswi = pswi;
}
/***************************************************************************\
* DSW_GetTopLevelCreatorWindow
\***************************************************************************/
PWND DSW_GetTopLevelCreatorWindow(
PWND pwnd)
{
UserAssert(pwnd != NULL);
if (pwnd != NULL) {
while (pwnd->spwndOwner) {
pwnd = pwnd->spwndOwner;
}
}
return pwnd;
}
/***************************************************************************\
* GetNextQueueWindow
*
* This routine is used to implement the Alt+Esc feature. This feature lets
* the user switch between windows for different applications (a.k.a. "Tasks")
* currently running. We keep track of the most recently active window in
* each task. This routine starts with the window passed and searches for the
* next window, in the "top-level" window list, that is from a different task
* than the one passed. We then return the most recenly active window from
* that task (or the window we found if the most recently active has been
* destroyed or is currently disabled or hidden). This routine returns NULL
* if no other enabled, visible window for another task can be found.
*
* History:
* 30-May-1991 DavidPe Ported from Win 3.1 sources.
\***************************************************************************/
PWND _GetNextQueueWindow(
PWND pwnd,
BOOL fPrev, /* 1 backward 0 forward */
BOOL fAltEsc)
{
PWND pwndAltTab;
PWND pwndNext;
PWND pwndT;
PWND pwndDesktop;
BOOL bBeenHereAlready = FALSE;
/*
* If the window we receive is Null then use the last topmost window
*/
if (!pwnd) {
pwnd = GetLastTopMostWindow();
if (!pwnd) {
return NULL;
}
}
pwndAltTab = gspwndAltTab;
pwnd = pwndNext = GetTopLevelWindow(pwnd);
if (!pwndNext) {
return NULL;
}
/*
* Get the window's desktop
*/
if ((pwndDesktop = pwndNext->spwndParent) == NULL) {
pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
pwnd = pwndNext = pwndDesktop->spwndChild;
}
while (TRUE) {
if (pwndNext == NULL)
return NULL;
/*
* Get the next window
*/
pwndNext = _GetWindow(pwndNext, fPrev ? GW_HWNDPREV : GW_HWNDNEXT);
if (!pwndNext) {
pwndNext = fPrev ? _GetWindow(pwndDesktop->spwndChild, GW_HWNDLAST)
: pwndDesktop->spwndChild;
/*
* To avoid searching the child chain forever, bale out if we get
* to the end (beginning) of the chain twice.
* This happens if pwnd is a partially destroyed window that has
* been unlinked from its siblings but not yet unlinked from the
* parent. (Happens while sending WM_NCDESTROY in xxxFreeWindow)
*/
if (bBeenHereAlready) {
RIPMSG1(RIP_WARNING, "pwnd %#p is no longer a sibling", pwnd);
return NULL;
}
bBeenHereAlready = TRUE;
}
/*
* If we have gone all the way around with no success, return NULL.
*/
if (!pwndNext || (pwndNext == pwnd))
return NULL;
/*
* Ignore the following windows:
* Switch window
* Tool Windows
* NoActivate Windows
* Hidden windows
* Disabled windows
* Topmost windows if via Alt+Esc
* Bottommost windows if via Alt+Esc
*
* If we're doing Alt-Esc processing, we have to skip topmost windows.
*
* Because topmost windows don't really go to the back when we
* send them there, alt-esc would never enumerate non-topmost windows.
* So, although we're allowed to start enumeration at a topmost window,
* we only allow enumeration of non-topmost windows, so the user can
* enumerate his presumably more important applications.
*/
if ((pwndNext != pwndAltTab) &&
// BradG - Win95 is missing the check for Tool Windows
(!TestWF(pwndNext, WEFTOOLWINDOW)) &&
(!TestWF(pwndNext, WEFNOACTIVATE)) &&
(TestWF(pwndNext, WFVISIBLE)) &&
((pwndNext->spwndLastActive == NULL) || (!TestWF(pwndNext->spwndLastActive, WFDISABLED)) &&
(!fAltEsc || (!TestWF(pwndNext, WEFTOPMOST) && !TestWF(pwndNext, WFBOTTOMMOST))))) {
/*
* If this window is owned, don't return it unless it is the most
* recently active window in its owner/ownee group.
*/
/*
* Hard loop to find top level owner
*/
for (pwndT = pwndNext; pwndT->spwndOwner; pwndT = pwndT->spwndOwner)
;
/*
* Don't return it unless it is the most recently active
* window in its owner/ownee group.
*/
if (pwndNext == pwndT->spwndLastActive)
return pwndNext;
}
}
}
/***************************************************************************\
*
* SwitchToThisWindow()
*
* This function was added specifically for Win386. It is called to tell
* USER that a particular window has been switched to via Alt+Tab or
* Alt+Esc in the Win386 environment. They call this function to maintain
* Z-ordering and consistent operation of these two functions. This function
* must be exported, but need not be documented.
*
* The parameter fTab is TRUE if this window is to be switched to via an
* Alt/Ctl+Tab key sequence otherwise it must be FALSE.
*
* History:
* 04-Feb-1991 DarrinM Created.
\***************************************************************************/
VOID xxxSwitchToThisWindow(
PWND pwnd,
BOOL fAltTab)
{
CheckLock(pwnd);
/*
* If we need to, push old window to the bottom.
*/
if (gpqForeground && !fAltTab) {
BOOL fPush;
PWND pwndActive;
TL tlpwndActive;
/*
* if ALT-ESC, and the window brought forward is the next one in the
* list, we must be rotating the zorder forward, so push the current
* window to the back
*/
pwndActive = gpqForeground->spwndActive;
fPush = pwndActive && _GetNextQueueWindow(pwndActive, FDIR_FORWARD, !fAltTab);
if (fPush && !TestWF(pwndActive, WEFTOPMOST) && !TestWF(pwndActive, WFBOTTOMMOST)) {
ThreadLock(pwndActive, &tlpwndActive);
xxxSetWindowPos(pwndActive, PWND_BOTTOM, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);
ThreadUnlock(&tlpwndActive);
}
}
/*
* Switch this new window to the foreground
* This window can go away during the SetForeground call if it isn't
* on the thread calling SwitchToThisWindow()!
*/
xxxSetForegroundWindow(pwnd, TRUE);
/*
* Restore minimized windows if the Alt+Tab case
*/
if (fAltTab && TestWF(pwnd,WFMINIMIZED)) {
/*
* We need to package up a special 'posted' queue message here. This
* ensures that this message gets processed after the asynchronous
* activation event occurs (via SetForegroundWindow).
*/
PostEventMessage(GETPTI(pwnd), GETPTI(pwnd)->pq,
QEVENT_POSTMESSAGE, pwnd, WM_SYSCOMMAND,
SC_RESTORE, 0 );
}
}
/***************************************************************************\
* NextPrevTaskIndex
*
* History:
* 01-Jun-1995 BradG Ported from Win95
\***************************************************************************/
INT NextPrevTaskIndex(
PSWINFO pswInfo,
INT iIndex,
INT iCount,
BOOL fNext)
{
UserAssert(iCount <= pswInfo->iTotalTasks);
if (fNext) {
iIndex += iCount;
if (iIndex >= pswInfo->iTotalTasks) {
iIndex -= pswInfo->iTotalTasks;
}
} else {
iIndex -= iCount;
if (iIndex < 0) {
iIndex += pswInfo->iTotalTasks;
}
}
UserAssert((iIndex >= 0) && (iIndex < pswInfo->iTotalTasks));
return iIndex;
}
/***************************************************************************\
* NextPrevPhwnd
*
* Given a pointer to one entry in the window list, this can return
* the pointer to the next/prev entry in a circular list fashion.
*
* History:
* 01-Jun-1995 BradG Ported from Win95
\***************************************************************************/
PHWND NextPrevPhwnd(
PSWINFO pswInfo,
PHWND phwnd,
BOOL fNext)
{
PBWL pbwl;
PHWND phwndStart;
PHWND phwndLast;
pbwl = pswInfo->pbwl;
phwndStart = &(pbwl->rghwnd[0]);
phwndLast = pswInfo->phwndLast;
UserAssert(*phwndLast == (HWND)1); // Last entry must have a 1.
UserAssert(phwndStart < phwndLast); // There must be atleast one entry.
UserAssert(phwnd != phwndLast); // Can't be passing in an invalid entry.
if (fNext) {
phwnd++;
if (phwnd == phwndLast) {
phwnd = phwndStart;
}
} else {
if (phwnd == phwndStart) {
phwnd = phwndLast - 1; // we have atleast one valid entry.
} else {
phwnd--;
}
}
return phwnd;
}
/***************************************************************************\
* _IsTaskWindow
*
* History:
* 01-Jun-95 BradG Ported from Win95
\***************************************************************************/
BOOL _IsTaskWindow(
PWND pwnd,
PWND pwndActive)
{
/*
* Following windows do not qualify to be shown in task list:
* Switch Window, Hidden windows (unless they are the active
* window), Disabled windows, Kanji Conv windows.
*
* Also, check for a combobox popup list which has the top-most
* style (it's spwndLastActive will be NULL).
*/
UserAssert(pwnd != NULL);
return( (TestWF(pwnd, WEFAPPWINDOW)
|| (!TestWF(pwnd, WEFTOOLWINDOW) && !TestWF(pwnd, WEFNOACTIVATE))) &&
(TestWF(pwnd, WFVISIBLE) || (pwnd == pwndActive)) &&
(!(pwnd->spwndLastActive && TestWF(pwnd->spwndLastActive, WFDISABLED))));
}
/***************************************************************************\
* RemoveNonTaskWindows()
*
* Given a list of Windows, this walks down the list and removes the
* windows that do not qualify to be shown in the Task-Switch screen.
* This also shrinks the list when it removes some entries.
* Returns the total number of "tasks" (windows) qualified and remained
* in the list. The last entry will have a 1 as usual.
* It also returns a pointer to this last entry via params. It also
* returns the index of the Currently active task via params.
*
* History:
* 01-Jun-1995 BradG Ported from Win95
\***************************************************************************/
INT _RemoveNonTaskWindows(
PBWL pbwl,
PWND pwndActive,
LPINT lpiActiveTask,
PHWND *pphwndLast)
{
INT iTaskCount = 0;
PHWND phwnd;
PWND pwnd;
PWND pwndUse;
PWND pwndOwnee;
PHWND phwndHole;
*lpiActiveTask = -1;
/*
* Walk down the window list and do the following:
* 1. Remove all entries that do not qualify to be shown in the task list.
* 2. Count the total number of windows that qualify.
* 3. Get the pointer to the entry that contains current active window.
* 4. Get the pointer to the last dummy entry (that has 1 in it)
*/
for (phwndHole = phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
pwnd = RevalidateHwnd(*phwnd);
if (!pwnd) {
continue;
}
if (_IsTaskWindow(pwnd, pwndActive)) {
pwndUse = pwnd;
/*
* First let's find the task-list owner of this window.
*/
while (!TestWF(pwndUse, WEFAPPWINDOW) && pwndUse->spwndOwner) {
pwndOwnee = pwndUse;
pwndUse = pwndUse->spwndOwner;
if (TestWF(pwndUse, WEFTOOLWINDOW)) {
/*
* If this is the owner of a top level property sheet,
* show the property sheet.
*/
if (TestWF(pwndOwnee, WEFCONTROLPARENT) && (pwndUse->spwndOwner == NULL)) {
pwndUse = pwnd;
} else {
pwndUse = NULL;
}
break;
}
}
if (!pwndUse || !pwndUse->spwndLastActive) {
continue;
}
/*
* walk up from the last active window 'til we find a valid task
* list window or until we run out of windows in the ownership
* chain
*/
for (pwndUse = pwndUse->spwndLastActive; pwndUse; pwndUse = pwndUse->spwndOwner)
if (_IsTaskWindow(pwndUse, pwndActive))
break;
/*
* if we ran out of windows in the ownership chain then use the
* owned window itself -- or if we didn't run out of the ownership
* chain, then only include this window if it's the window in the
* ownership chain that we just found (VB will love us for it !)
* -- jeffbog -- 4/20/95 -- Win95C B#2821
*/
if (!pwndUse || (pwndUse == pwnd)) {
/*
* Do we have any holes above this? If so, move this handle to
* that hole.
*/
if (phwndHole < phwnd) {
/*
* Yes! There is a hole. Let us move the valid
* handle there.
*/
*phwndHole = *phwnd;
}
if (pwndActive == pwnd)
*lpiActiveTask = iTaskCount;
iTaskCount++;
phwndHole++; // Move to the next entry.
}
/*
* Else, leave it as a hole for later filling.
*/
}
}
*phwndHole = (HWND)1;
*pphwndLast = phwndHole;
return iTaskCount;
}
/***************************************************************************\
* DrawSwitchWndHilite()
*
* This draws or erases the Hilite we draw around the icon to show which
* task we are going to switch to.
* This also updates the name on the Task title window.
*
* History:
* 01-Jun-1995 BradG Ported from Win95
\***************************************************************************/
VOID DrawSwitchWndHilite(
PSWINFO pswInfo,
HDC hdcSwitch,
int iCol,
int iRow,
BOOL fShow)
{
BOOL fGetAndReleaseIt;
RECT rcTemp;
/*
* Draw or erase the hilite depending on "fShow".
*/
if (fGetAndReleaseIt = (hdcSwitch == NULL))
hdcSwitch = _GetDCEx(gspwndAltTab, NULL, DCX_USESTYLE);
rcTemp.left = pswInfo->ptFirstRowStart.x + iCol * CXICONSLOT;
rcTemp.top = pswInfo->ptFirstRowStart.y + iRow * CYICONSLOT;
rcTemp.right = rcTemp.left + CXICONSLOT;
rcTemp.bottom = rcTemp.top + CYICONSLOT;
DrawFrame(hdcSwitch,
&rcTemp,
2,
DF_PATCOPY | ((fShow ? COLOR_HIGHLIGHT : COLOR_3DFACE) << 3));
/*
* Update the Task title window.
*/
if (fShow) {
WCHAR szText[CCHTITLEMAX];
INT cch;
COLORREF clrOldText, clrOldBk;
PWND pwnd;
RECT rcRect;
HFONT hOldFont;
INT iLeft;
ULONG_PTR dwResult = 0;
clrOldText = GreSetTextColor(hdcSwitch, SYSRGB(BTNTEXT));
clrOldBk = GreSetBkColor(hdcSwitch, SYSRGB(3DFACE));
hOldFont = GreSelectFont(hdcSwitch, gpsi->hCaptionFont);
/*
* Validate this window handle; This could be some app that terminated
* in the background and the following line will GP fault in that case;
* BOGUS: We should handle it some other better way.
*/
pwnd = RevalidateHwnd( *(pswInfo->phwndCurrent) );
if (pwnd) {
/*
* Get the window's title.
*/
if (pwnd->strName.Length) {
cch = TextCopy(&pwnd->strName, szText, CCHTITLEMAX);
} else {
*szText = TEXT('\0');
cch = 0;
}
/*
* Draw the text
*/
CopyRect(&rcRect, &pswInfo->rcTaskName);
iLeft = rcRect.left;
FillRect(hdcSwitch, &rcRect, SYSHBR(3DFACE));
/*
* If an lpk is installed let it draw the text.
*/
if (CALL_LPK(PtiCurrentShared())) {
TL tlpwnd;
LPKDRAWSWITCHWND LpkDrawSwitchWnd;
RtlInitLargeUnicodeString(&LpkDrawSwitchWnd.strName, szText, (UINT)-1);
LpkDrawSwitchWnd.rcRect = rcRect;
ThreadLock(pwnd, &tlpwnd);
xxxSendMessageTimeout(pwnd, WM_LPKDRAWSWITCHWND, (WPARAM)hdcSwitch,
(LPARAM)&LpkDrawSwitchWnd, SMTO_ABORTIFHUNG, 100, &dwResult);
ThreadUnlock(&tlpwnd);
} else {
DRAWTEXTPARAMS dtp;
dtp.cbSize = sizeof(dtp);
dtp.iLeftMargin = 0;
dtp.iRightMargin = 0;
DrawTextEx(hdcSwitch, szText, cch, &rcRect, DT_NOPREFIX | DT_END_ELLIPSIS | DT_SINGLELINE, &dtp );
}
}
GreSelectFont(hdcSwitch, hOldFont);
GreSetBkColor(hdcSwitch, clrOldBk);
GreSetTextColor(hdcSwitch, clrOldText);
}
if (fGetAndReleaseIt)
_ReleaseDC(hdcSwitch);
}
/***************************************************************************\
* DrawIconCallBack
*
* This function is called by a Windows app returning his icon.
*
* History:
* 17-Jun-1993 mikesch Created.
\***************************************************************************/
VOID CALLBACK DrawIconCallBack(
HWND hwnd,
UINT uMsg,
ULONG_PTR dwData,
LRESULT lResult)
{
PWND pwndAltTab;
/*
* dwData is the pointer to the switch window handle.
* If this Alt+Tab instance is still active, we need to derive this
* window's index in the bwl array, otherwise, we are receiving an icon
* for an old Alt+Tab window.
*/
pwndAltTab = RevalidateHwnd((HWND)dwData);
if (pwndAltTab && TestWF(pwndAltTab, WFVISIBLE)) {
PSWINFO pswCurrent;
PICON pIcon;
PHWND phwnd;
PWND pwnd;
PWND pwndT;
INT iStartTaskIndex;
TL tlpwndAltTab;
/*
* Derive this window's index in the BWL array
*/
if ((pwnd = RevalidateHwnd(hwnd)) == NULL)
return;
/*
* Get the switch window info
*/
pswCurrent = Getpswi(pwndAltTab);
if (!pswCurrent)
return;
for (iStartTaskIndex = 0, phwnd=&(pswCurrent->pbwl->rghwnd[0]); *phwnd != (HWND)1; phwnd++, iStartTaskIndex++) {
/*
* Because we list the active window in the Switch Window, the
* hwnd here might not be the same, so we also need to walk back
* to the top-level window to see if this is the right entry
* in the list.
*/
for(pwndT = RevalidateHwnd(*phwnd); pwndT; pwndT = pwndT->spwndOwner) {
if (pwnd == pwndT)
goto DrawIcon;
}
}
return;
/*
* Convert the App's HICON into a PICON, or if the App did not return
* an icon, use the Windows default icon.
*/
DrawIcon:
pIcon = NULL;
if (lResult)
pIcon = HMValidateHandleNoRip((HCURSOR)lResult, TYPE_CURSOR);
if (!pIcon)
pIcon = SYSICO(WINLOGO);
/*
* Paint this icon in the Alt+Tab window.
*/
ThreadLockAlways(pwndAltTab, &tlpwndAltTab);
xxxPaintIconsInSwitchWindow(pwndAltTab,
pswCurrent,
NULL,
iStartTaskIndex,
0,
1,
FALSE,
FALSE,
pIcon);
ThreadUnlock(&tlpwndAltTab);
}
UNREFERENCED_PARAMETER(uMsg);
}
/***************************************************************************\
* TSW_CalcRowAndCol
*
* History:
* 01-Jun-1995 BradG Ported from Win95
\***************************************************************************/
BOOL TSW_CalcRowAndCol(
PSWINFO pswInfo,
INT iTaskIndex,
LPINT lpiRow,
LPINT lpiCol)
{
INT iDiff;
INT iRow;
/*
* Calculate how far is the given task from the first task shown
* on the switch window.
*/
if ((iDiff = (iTaskIndex - pswInfo->iFirstTaskIndex)) < 0)
iDiff += pswInfo->iTotalTasks;
/*
* Calculate the Row and if this lies outside the switch window, return FALSE
*/
if ((iRow = iDiff / pswInfo->iNoOfColumns) >= pswInfo->iNoOfRows)
return FALSE;
/*
* Return the Row and column where this task lies.
*/
*lpiRow = iRow;
*lpiCol = iDiff - (iRow * pswInfo->iNoOfColumns);
return TRUE; // This task lies within the switch window.
}
/***************************************************************************\
* xxxPaintIconsInSwitchWindow()
*
* This can simply paint the icons in the switch window or Scroll the
* whole window UP/DOWN and then paint the remaining area;
* * If fScroll is TRUE, then the second, third and fourth params are ignored.
* * If hIcon is passed in, then we're being called by DrawIconCallBack and
* iStartRow parameter is ignored in this case.
*
* History:
* 02-Jun-1995 BradG Ported from Win95
\***************************************************************************/
VOID xxxPaintIconsInSwitchWindow(
PWND pwndAltTab,
PSWINFO pswInfo,
HDC hdc,
INT iStartTaskIndex,
INT iStartRow,
INT iNoOfIcons,
BOOL fScroll,
BOOL fUp,
PICON pIcon)
{
INT cx, cy, xStart;
PHWND phwnd;
BOOL fGetAndReleaseIt;
INT iColumnIndex = 0;
RECT rcScroll;
PWND pwnd;
TL tlpwnd;
HICON hIcon;
RECT rcIcon;
CheckLock(pwndAltTab);
/*
* If we were not supplied a DC, get ghwndSwitch's and set a flag
* so we remember to release it.
*/
if (fGetAndReleaseIt = (hdc == NULL))
hdc = _GetDCEx(pwndAltTab, NULL, DCX_USESTYLE);
cx = pswInfo->ptFirstRowStart.x;
cy = pswInfo->ptFirstRowStart.y;
if (fScroll) {
rcScroll.left = cx;
rcScroll.top = cy;
rcScroll.right = cx + CXICONSLOT * pswInfo->iNoOfColumns;
rcScroll.bottom = cy + CYICONSLOT * pswInfo->iNoOfRows;
_ScrollDC(hdc,
0,
(fUp ? -CYICONSLOT : CYICONSLOT),
&rcScroll,
&rcScroll,
NULL,
NULL);
iStartRow = (fUp ? pswInfo->iNoOfRows - 1 : 0);
iNoOfIcons = pswInfo->iNoOfColumns;
iStartTaskIndex = (fUp ? NextPrevTaskIndex(pswInfo, pswInfo->iFirstTaskIndex,
(pswInfo->iNoOfRows - 1) * pswInfo->iNoOfColumns, TRUE) :
pswInfo->iFirstTaskIndex);
}
if (pIcon) {
/*
* If pIcon is given, this is to paint just one icon during callback.
*/
// BradG - Win95 Assert
UserAssert(iNoOfIcons == 1);
/*
* Due to earlier scrolling, the row number would have changed. So,
* recalc the row and column from the iStartTaskIndex given.
*/
if (!TSW_CalcRowAndCol(pswInfo, iStartTaskIndex, &iStartRow, &iColumnIndex))
goto Cleanup;
}
xStart = cx += (CXICONSLOT - CXICONSIZE) / 2;
cx += iColumnIndex * CXICONSLOT;
cy += ((CYICONSLOT - CYICONSIZE) / 2) + iStartRow * CYICONSLOT;
phwnd = &(pswInfo->pbwl->rghwnd[iStartTaskIndex]);
/*
* Draw all the icons one by one.
*/
while (iNoOfIcons--) {
/*
* If the Alt+Key is no longer down, abort painting icons.
*/
if ((pswInfo->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
(!pswInfo->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
goto Cleanup;
/*
* Check if this window is still alive. (Some task could have
* terminated in the background)
*/
if (pwnd = RevalidateHwnd(*phwnd)) {
/*
* Find the window's top-level owner
*/
pwnd = DSW_GetTopLevelCreatorWindow(pwnd);
/*
* If we don't have an icon, find one
*/
if (!pIcon) {
/*
* Try window icon
*/
hIcon = (HICON)_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconProp), PROPF_INTERNAL);
if (hIcon) {
pIcon = (PICON)HMValidateHandleNoRip(hIcon, TYPE_CURSOR);
}
/*
* If we don't have an icon yet, try the class icon
*/
if (!pIcon) {
pIcon = pwnd->pcls->spicn;
}
/*
* If we don't have an icon yet, use WM_QUERYDRAGICON to ask
* 3,x apps for their icon.
*/
if (!pIcon && !TestWF(pwnd, WFWIN40COMPAT)) {
/*
* The callback routine will paint the icon for
* us, so just leave pIcon set to NULL
*/
ThreadLock(pwnd, &tlpwnd);
xxxSendMessageCallback(pwnd, WM_QUERYDRAGICON, 0, 0,
(SENDASYNCPROC)DrawIconCallBack,
HandleToUlong(PtoH(pwndAltTab)), FALSE);
ThreadUnlock(&tlpwnd);
} else {
/*
* If we can't find an icon, so use the Windows icon
*/
if (!pIcon) {
pIcon = SYSICO(WINLOGO);
}
}
}
}
if (pIcon) {
_DrawIconEx(hdc, cx, cy, pIcon, SYSMET(CXICON), SYSMET(CYICON),
0, SYSHBR(3DFACE), DI_NORMAL);
} else if (fScroll) {
/*
* NOT IN WIN95
*
* No icon was available, do while we are waiting for the
* application to paint it's icon, we need to "erase" the
* background in case we have scrolled the window.
*/
rcIcon.left = cx;
rcIcon.top = cy;
rcIcon.right = cx + SYSMET(CXICON);
rcIcon.bottom = cy + SYSMET(CYICON);
FillRect(hdc, &rcIcon, SYSHBR(3DFACE));
}
/*
* Check if we are done.
*/
if (iNoOfIcons <= 0)
break;
/*
* Reset hIcon for the next run through the loop
*/
pIcon = NULL;
/*
* Move all pointers to the next task/icon.
*/
phwnd = NextPrevPhwnd(pswInfo, phwnd, TRUE); // Get next.
/*
* Is it going to be in the same row; then adjust cx and cy.
*/
if (++iColumnIndex >= pswInfo->iNoOfColumns) {
iColumnIndex = 0;
cx = xStart; // Move to first column
cy += CYICONSLOT; // Move to next row.
iStartRow++;
} else {
/*
* else, adjust cx;
*/
cx += CXICONSLOT;
}
iStartTaskIndex = NextPrevTaskIndex(pswInfo, iStartTaskIndex, 1, TRUE);
}
Cleanup:
if (fGetAndReleaseIt)
_ReleaseDC(hdc);
}
/***************************************************************************\
* PaintSwitchWindow
*
* History:
* 02-Jun-1995 BradG Ported from Win95
\***************************************************************************/
VOID xxxPaintSwitchWindow(
PWND pwndSwitch)
{
LPRECT lprcRect;
RECT rcRgn;
HDC hdcSwitch;
PSWINFO pswCurrent;
CheckLock(pwndSwitch);
/*
* If our window isn't visible, return
*/
if (!TestWF(pwndSwitch, WFVISIBLE))
return;
/*
* Get the switch window information
*/
pswCurrent = Getpswi(pwndSwitch);
if (!pswCurrent)
return;
/*
* Get the Switch windows DC so we can paint with it
*/
hdcSwitch = _GetDCEx(pwndSwitch, NULL, DCX_USESTYLE );
/*
* Paint the background of the Switch Screen.
*/
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
goto PSWExit;
lprcRect = &(pswCurrent->rcTaskName);
_GetClientRect(pwndSwitch, lprcRect);
FillRect(hdcSwitch, lprcRect, SYSHBR(3DFACE));
/*
* Store this "caption" area back into the current switch
* window data structure.
*/
InflateRect(lprcRect, -(gcxCaptionFontChar << 1), -(gcyCaptionFontChar));
lprcRect->top = lprcRect->bottom - gcyCaptionFontChar;
/*
* Draw the sunken edge for showing the task names.
*/
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
goto PSWExit;
CopyInflateRect(&rcRgn, lprcRect, gcxCaptionFontChar >> 1, gcyCaptionFontChar >> 1);
DrawEdge(hdcSwitch, &rcRgn, EDGE_SUNKEN, BF_RECT);
/*
* Paint the icons
*/
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
goto PSWExit;
xxxPaintIconsInSwitchWindow(pwndSwitch,
pswCurrent,
hdcSwitch,
pswCurrent->iFirstTaskIndex,
0,
pswCurrent->iTasksShown,
FALSE,
FALSE,
NULL);
/*
* So, just draw the hilite.
*/
if ((pswCurrent->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
(!pswCurrent->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0))
goto PSWExit;
DrawSwitchWndHilite(pswCurrent,
hdcSwitch,
pswCurrent->iCurCol,
pswCurrent->iCurRow,
TRUE);
/*
* Release the switch windows DC
*/
PSWExit:
_ReleaseDC(hdcSwitch);
}
/***************************************************************************\
* SwitchWndCleanup()
*
* Clean up all the mem allocated etc.,
*
* History:
* 07-Jun-1995 BradG Ported from Win95
\***************************************************************************/
VOID SwitchWndCleanup(
PSWINFO *ppswInfo)
{
UserAssert(ppswInfo != NULL);
UserAssert(*ppswInfo != NULL);
/*
* First of all free the Window list.
*/
if ((*ppswInfo)->pbwl)
FreeHwndList((*ppswInfo)->pbwl);
UserFreePool(*ppswInfo);
*ppswInfo = NULL;
}
/***************************************************************************\
* AddSwitchWindowInfo
*
* 09-12-01 MSadek Created
\***************************************************************************/
VOID AddSwitchWindowInfo(
PSWINFO pswInfo)
{
CheckCritIn();
pswInfo->pswiNext = gpswiFirst;
gpswiFirst = pswInfo;
}
/***************************************************************************\
* RemoveSwitchWindowInfo
*
* 09-12-01 MSadek Created
\***************************************************************************/
VOID RemoveSwitchWindowInfo(
PSWINFO *ppswInfo)
{
PSWINFO* ppswi;
PSWINFO pswiT;
CheckCritIn();
for (ppswi = &gpswiFirst; *ppswi != NULL; ppswi = &(*ppswi)->pswiNext) {
pswiT = *ppswi;
if (pswiT == *ppswInfo) {
*ppswi = pswiT->pswiNext;
SwitchWndCleanup(ppswInfo);
}
if (*ppswi == NULL) {
break;
}
}
}
/***************************************************************************\
* RemoveThreadSwitchWindowInfo
*
* 09-12-01 MSadek Created
\***************************************************************************/
VOID RemoveThreadSwitchWindowInfo(
PTHREADINFO pti)
{
PSWINFO* ppswi;
PSWINFO pswiT;
CheckCritIn();
for (ppswi = &gpswiFirst; *ppswi != NULL; ppswi = &(*ppswi)->pswiNext) {
pswiT = *ppswi;
if (pswiT->pti == pti) {
*ppswi = pswiT->pswiNext;
SwitchWndCleanup(&pswiT);
}
if (*ppswi == NULL) {
break;
}
}
}
/***************************************************************************\
* InitSwitchWndInfo
*
* This function allocs and Initializes all the data structures
* required the build and show the tasks in the system.
* If there is insufficient mem, then this find the next window to switch
* to and returns it. In this case, we will behave as if the end user hit
* ALT+ESC. The SWitchScreen will not comeup in this case.
* If there is only one task in the whole system, then this function
* fails and returns a NULL. (No ALT+TAB processing is required).
* Otherwise, it allocs one SwitchWndInfo struc, fills it up and returns
* the window we gonna switch to.
*
* History:
* 02-Jun-95 BradG Ported from Win95
\***************************************************************************/
PWND InitSwitchWndInfo(
PSWINFO * lppswInfo,
PWND pwndCurActive,
BOOL fPrev)
{
PBWL pbwl;
INT iTotalTasks;
INT iCols, iRows, iIconsInLastRow;
INT iDiff;
PHWND phwndLast;
PSWINFO pswInfo;
INT iIconIndex;
INT iCurRow, iCurCol;
INT cxSwitch, cySwitch;
INT iFirstRowIcons;
INT iActiveTask;
PWND pwnd = NULL;
PTHREADINFO ptiCurrent = PtiCurrent();
PDESKTOPINFO pdeskinfo = GETDESKINFO(ptiCurrent);
PMONITOR pMonitor = GetPrimaryMonitor();
/*
* Initialize the list
*/
*lppswInfo = (PSWINFO)NULL;
/*
* Build the Window list of all the top level windows.
*/
#if 0
if (!(pbwl = BuildHwndList(NULL, BWL_ENUMLIST | BWL_ALLDESKTOPS, NULL)))
goto ReturnNextWnd;
#else
// BradG - HACK, enumerate on current desktop!
// For the long run, we will need to enumerate all desktops
// This will be tricky because we need to check the security of
// each desktop, thus needing the user's security "token".
if (!(pbwl = BuildHwndList(pdeskinfo->spwnd->spwndChild, BWL_ENUMLIST, NULL))) {
#ifdef COOLSWITCHTRACE
DbgPrint("CoolSwitch: BuildHwndList failed (contact bradg).\n");
UserAssert(pbwl != NULL);
#endif
goto ReturnNextWnd;
}
#endif
/*
* Walk down the list and remove all non-task windows from the list.
* Replace those hwnds with 0.
*/
if ((iTotalTasks = _RemoveNonTaskWindows(pbwl, pwndCurActive, &iActiveTask, &phwndLast)) < 2) {
if (iTotalTasks == 1) {
/*
* If we have only one window and it's in full screen mode, we will
* return the shell window so the can switch back to GDI mode.
*/
pwnd = RevalidateHwnd(pbwl->rghwnd[0]);
if (pwnd && GetFullScreen(pwnd) == FULLSCREEN && pwndCurActive == pwnd)
pwnd = pdeskinfo->spwndShell;
} else {
pwnd = pdeskinfo->spwndShell;
}
#ifdef COOLSWITCHTRACE
DbgPrint("CoolSwitch: Not enough windows to switch.\n");
#endif
goto FreeAndReturnNextWnd; // If there isn't even two tasks, no switch wnd processing.
}
/*
* Allocate the Switch Info structure. If we don't have enough
* memory, act as if we are doing Alt+Esc.
*/
if (!(pswInfo = (PSWINFO)UserAllocPoolWithQuota(sizeof(SWITCHWNDINFO), TAG_ALTTAB))) {
#ifdef COOLSWITCHTRACE
DbgPrint("CoolSwitch: UserAllocPool failed on 0x%X bytes (contact bradg).\n", sizeof(SWITCHWNDINFO));
UserAssert(pswInfo != NULL);
#endif
goto FreeAndReturnNextWnd; // Unable to alloc SwitchWndInfo struct.
}
pswInfo->pti = ptiCurrent;
pswInfo->pbwl = pbwl;
pswInfo->phwndLast = phwndLast;
pswInfo->iTasksShown = pswInfo->iTotalTasks = iTotalTasks;
/*
* Get the next/prev window that must become active.
*/
iIconIndex = NextPrevTaskIndex(pswInfo, iActiveTask, 1, !fPrev);
pswInfo->phwndCurrent = &(pbwl->rghwnd[iIconIndex]);
iCols = min(gnFastAltTabColumns, iTotalTasks);
iRows = iTotalTasks / iCols; // Truncation might occur.
iIconsInLastRow = iTotalTasks - iRows * iCols;
iRows += (iIconsInLastRow ? 1 : 0); // Take care of earlier truncation.
/*
* Restrict the number of rows to just MAXROWSALLOWED (3)
*/
if (iRows > gnFastAltTabRows) {
iRows = gnFastAltTabRows;
pswInfo->fScroll = TRUE; // We need to scroll.
iIconsInLastRow = iCols;
pswInfo->iTasksShown = iCols * iRows;
} else {
pswInfo->fScroll = FALSE;
}
pswInfo->iNoOfColumns = iCols;
pswInfo->iNoOfRows = iRows;
if (iIconsInLastRow == 0)
iIconsInLastRow = pswInfo->iNoOfColumns; // Last Row is full.
pswInfo->iIconsInLastRow = iIconsInLastRow;
/*
* Find out Row and Col where the next/prev icon will lie.
*/
if (iIconIndex >= (iRows * iCols)) {
/*
* Next Icon lies outside. Bring it to the center.
*/
iCurRow = (iRows >> 2) + 1;
iCurCol = (iCols >> 2) + 1;
iDiff = (iIconIndex - ((iCurRow * iCols) + iCurCol));
} else {
iDiff = 0;
iCurRow = iIconIndex / iCols;
iCurCol = iIconIndex - (iCurRow * iCols);
}
pswInfo->iFirstTaskIndex = iDiff;
pswInfo->iCurRow = iCurRow;
pswInfo->iCurCol = iCurCol;
/*
* Calculate the Switch Window Dimensions.
*/
cxSwitch = min(
pMonitor->rcMonitor.right - pMonitor->rcMonitor.left,
gnFastAltTabColumns * CXICONSLOT +
CXICONSIZE / 2 +
6 * gpsi->gclBorder * SYSMET(CXBORDER) +
gcxCaptionFontChar);
cySwitch = min(
pMonitor->rcMonitor.bottom - pMonitor->rcMonitor.top,
iRows * CYICONSLOT +
CYICONSIZE +
gcyCaptionFontChar * 2 +
gcyCaptionFontChar / 2);
/*
* Find the number of icons in first row
*/
if (iRows == 1) {
iFirstRowIcons = iIconsInLastRow;
} else {
iFirstRowIcons = iCols;
}
/*
* Center the icons based on the number of icons in first row.
*/
pswInfo->ptFirstRowStart.x = (cxSwitch - 4*gpsi->gclBorder*SYSMET(CXBORDER) - iFirstRowIcons * CXICONSLOT) >> 1;
pswInfo->ptFirstRowStart.y = (CYICONSIZE >> 1);
pswInfo->cxSwitch = cxSwitch;
pswInfo->cySwitch = cySwitch;
AddSwitchWindowInfo(pswInfo);
*lppswInfo = pswInfo;
return RevalidateHwnd(*(pswInfo->phwndCurrent)); // Success!
/*
* When there is insufficient mem to create the reqd structures, we simply
* return the next window. We make the phwndInfo as NULL. So, we won't
* attempt to draw the switch window.
*/
FreeAndReturnNextWnd:
FreeHwndList(pbwl);
ReturnNextWnd:
if (pwnd)
return(pwnd);
return(_GetNextQueueWindow(pwndCurActive, _GetKeyState(VK_SHIFT) < 0, FALSE));
}
/***************************************************************************\
* xxxMoveSwitchWndHilite()
*
* This moves the Hilite to the next/prev icon.
* Checks if this move results in a scrolling. If it does, then
* make sure scroll occurs.
* Else, erase hilite from the current icon;
* Then draw hilite on the new Icon.
* fPrev indicates whether you want the prev task or next.
*
* History:
* 02-Jun-1995 BradG Ported from Win95
\***************************************************************************/
HWND xxxMoveSwitchWndHilite(
PWND pwndSwitch,
PSWINFO pswInfo,
BOOL fPrev)
{
INT iCurCol, iCurRow;
INT iMaxColumns;
BOOL fLastRow;
BOOL fNeedToScroll = FALSE;
HDC hdc;
HWND hwnd;
CheckLock(pwndSwitch);
UserAssert(IsWinEventNotifyDeferredOK());
iCurCol = pswInfo->iCurCol;
iCurRow = pswInfo->iCurRow;
/*
* Find out the new postion (row and column) of hilite.
*/
if (fPrev) {
if (iCurCol > 0) {
/*
* Move cursor to prev column on the same row.
*/
iCurCol--;
} else {
/*
* Try to move to the previous row.
*/
if (iCurRow > 0) {
/*
* Move to the last column on the previous row.
*/
iCurRow--;
iCurCol = pswInfo->iNoOfColumns - 1;
} else {
/*
* We are already at (0,0); See if we need to scroll.
*/
if (pswInfo->fScroll) {
/*
* Time to scroll; Scroll by one Row;
* Repaint the whole window.
*/
fNeedToScroll = TRUE;
pswInfo->iFirstTaskIndex = NextPrevTaskIndex(pswInfo, pswInfo->iFirstTaskIndex,
pswInfo->iNoOfColumns, FALSE);
iCurCol = pswInfo->iNoOfColumns - 1;
} else {
/*
* Move the hilite to the last icon shown.
*/
iCurRow = pswInfo->iNoOfRows - 1;
iCurCol = pswInfo->iIconsInLastRow - 1;
}
}
}
} else {
/*
* !fPrev
* Get the number of columns in the current row.
*/
if (fLastRow = (iCurRow == (pswInfo->iNoOfRows - 1))) // Are we at the last row?
iMaxColumns = pswInfo->iIconsInLastRow;
else
iMaxColumns = pswInfo->iNoOfColumns;
/*
* Are we at the last column yet?
*/
if (iCurCol < (iMaxColumns - 1)) {
/*
* No! Move to the right.
*/
iCurCol++;
} else {
/*
* We are at the last column.
* If we are not at last row, then move to next row.
*/
if (!fLastRow) {
iCurCol = 0;
iCurRow++;
} else {
/*
* We are at the last row, last col;
* See if we need to scroll.
*/
if (pswInfo->fScroll) {
fNeedToScroll = TRUE;
pswInfo->iFirstTaskIndex = NextPrevTaskIndex(pswInfo, pswInfo->iFirstTaskIndex,
pswInfo->iNoOfColumns, TRUE);
iCurCol = 0;
} else {
/*
* Move to the top left corner (0, 0).
*/
iCurRow = iCurCol = 0;
}
}
}
}
/*
* Move the phwnd to the next/prev
*/
pswInfo->phwndCurrent = NextPrevPhwnd(pswInfo, pswInfo->phwndCurrent, !fPrev);
/*
* Remove Hilite from the current location.
*/
hdc = _GetDCEx(pwndSwitch, NULL, DCX_USESTYLE);
DrawSwitchWndHilite(pswInfo, hdc, pswInfo->iCurCol, pswInfo->iCurRow, FALSE);
pswInfo->iCurRow = iCurRow;
pswInfo->iCurCol = iCurCol;
hwnd = (*(pswInfo->phwndCurrent));
/*
* Repaint if needed.
*/
if (fNeedToScroll)
xxxPaintIconsInSwitchWindow(pwndSwitch, pswInfo, hdc, pswInfo->iFirstTaskIndex, 0, 0, TRUE, !fPrev, NULL);
/*
* Draw Hilite at the new location.
*/
DrawSwitchWndHilite(pswInfo, hdc, iCurCol, iCurRow, TRUE);
_ReleaseDC(hdc);
xxxWindowEvent(EVENT_OBJECT_FOCUS, pwndSwitch, OBJID_CLIENT,
iCurRow * pswInfo->iNoOfColumns + iCurCol + 1, WEF_USEPWNDTHREAD);
return hwnd;
}
/***************************************************************************\
* xxxShowSwitchWindow()
*
* Show the switch Window.
* Returns: TRUE if succeeded. FALSE, if the window was not shown because
* of the pre-mature release of ALT key. The selection has been made already.
*
* History:
* 07-Jun-1995 BradG Ported from Win95
\***************************************************************************/
BOOL xxxShowSwitchWindow(
PWND pwndAltTab)
{
PSWINFO pswInfo;
PMONITOR pMonitorSwitch = GetPrimaryMonitor();
CheckLock(pwndAltTab);
UserAssert(IsWinEventNotifyDeferredOK());
/*
* Get the switch window information
*/
pswInfo = Getpswi(pwndAltTab);
if (pswInfo == NULL) {
return FALSE;
}
/*
* If the key is not down, don't bother to display Switch Window.
*/
if ((pswInfo->fJournaling && _GetKeyState(VK_MENU) >= 0) ||
(!pswInfo->fJournaling && _GetAsyncKeyState(VK_MENU) >= 0)) {
#ifdef COOLSWITCHTRACE
DbgPrint("CoolSwitch: Not displaying window because VM_MENU is up (contact bradg).\n");
#endif
return FALSE;
}
/*
* Bring and position the window on top.
*/
xxxSetWindowPos(pwndAltTab, PWND_TOPMOST, 0,0,0,0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW );
if (!TestWF(pwndAltTab, WFVISIBLE)) {
xxxSetWindowPos(
pwndAltTab,
PWND_TOPMOST,
(pMonitorSwitch->rcWork.left + pMonitorSwitch->rcWork.right - pswInfo->cxSwitch) / 2,
(pMonitorSwitch->rcWork.top + pMonitorSwitch->rcWork.bottom - pswInfo->cySwitch) / 2,
pswInfo->cxSwitch,
pswInfo->cySwitch,
SWP_SHOWWINDOW | SWP_NOACTIVATE);
}
#ifdef COOLSWITCHTRACE
UserAssert(TestWF(pwndAltTab, WFVISIBLE));
#endif
xxxUpdateWindow(pwndAltTab);
xxxWindowEvent(EVENT_SYSTEM_SWITCHSTART, pwndAltTab, OBJID_CLIENT,
0, WEF_USEPWNDTHREAD);
xxxWindowEvent(EVENT_OBJECT_FOCUS, pwndAltTab, OBJID_CLIENT,
pswInfo->iCurRow * pswInfo->iNoOfColumns + pswInfo->iCurCol + 1,
WEF_USEPWNDTHREAD);
return TRUE;
}
/***************************************************************************\
*
* xxxSwitchWndProc()
*
\***************************************************************************/
LRESULT xxxSwitchWndProc(
PWND pwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
TL tlpwndActivate;
PTHREADINFO ptiCurrent = PtiCurrent();
CheckLock(pwnd);
UserAssert(IsWinEventNotifyDeferredOK());
VALIDATECLASSANDSIZE(pwnd, message, wParam, lParam, FNID_SWITCH, WM_CREATE);
switch (message) {
case WM_CREATE:
/*
* When the queue was created, the cursor was set to the wait cursor.
* We want to use the normal one.
*/
zzzSetCursor(pwnd->pcls->spcur);
break;
case WM_CLOSE:
/*
* Hide this window without activating anyone else.
*/
xxxSetWindowPos(pwnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW |
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
/*
* Get us out of Alt+Tab mode. Since the alttab information
* is stored in the gptiRit->pq, we will reference that insteatd
* of the current-thread.
*/
xxxCancelCoolSwitch();
break;
case WM_ERASEBKGND:
case WM_FULLSCREEN:
ThreadLockWithPti(ptiCurrent, pwnd, &tlpwndActivate);
xxxPaintSwitchWindow(pwnd);
ThreadUnlock(&tlpwndActivate);
return 0;
case WM_DESTROY:
{
/*
* Get the switch window info for this window
*/
PSWINFO pswCurrent = Getpswi(pwnd);
if (pswCurrent) {
RemoveSwitchWindowInfo(&pswCurrent);
Setpswi(pwnd, NULL);
}
}
break;
}
return xxxDefWindowProc(pwnd, message, wParam, lParam);
}
/***************************************************************************\
* xxxCancelCoolSwitch
*
* This functions destroys the cool switch window and removed the INALTTAB
* mode flag from the specified queue.
*
* History:
* 18-Sep-1995 BradG Created
\***************************************************************************/
VOID xxxCancelCoolSwitch(
void)
{
CheckCritIn();
UserAssert(IsWinEventNotifyDeferredOK());
/*
* Destroy the Cool Switch window
*/
if (gspwndAltTab != NULL) {
PWND pwnd = gspwndAltTab;
/*
* Make sure that the thread calling this is the same thread which
* created the alttab window. Otherwise, we could end up with this
* window floating around until the calling process dies. Remember,
* we can't destroy windows across different threads.
*/
if (gspwndAltTab->head.pti != PtiCurrent()) {
return;
}
xxxWindowEvent(EVENT_SYSTEM_SWITCHEND, gspwndAltTab, OBJID_CLIENT,
0, WEF_USEPWNDTHREAD);
if (Unlock(&gspwndAltTab)) {
xxxDestroyWindow(pwnd);
}
}
}
/***************************************************************************\
* xxxNextWindow
*
* This function does the processing for the alt-tab/esc/F6 UI.
*
* History:
* 30-May-1991 DavidPe Created.
\***************************************************************************/
VOID xxxNextWindow(
PQ pq,
DWORD wParam)
{
PWND pwndActivateNext;
PWND pwndCurrentActivate, pwndCurrentTopFocus;
int fDir;
TL tlpwndCurrentTopFocus;
TL tlpwndActivateNext;
TL tlpwndCurrentActivate;
TL tlpwndT;
PSWINFO pswCurrent;
ULONG_PTR dwResult;
BOOL fNonRit = FALSE;
PTHREADINFO ptiCurrent = PtiCurrent();
UserAssert(!IsWinEventNotifyDeferred());
if (pq == NULL)
return;
fDir = (_GetAsyncKeyState(VK_SHIFT) < 0) ? FDIR_BACKWARD : FDIR_FORWARD;
pwndCurrentTopFocus = GetTopLevelWindow(pq->spwndFocus);
/*
* NOTE: As of NT 4.0 the slow Alt+Tab functionality now officially acts
* like Alt+Esc with the exception that Alt+Tab will activate the window
* where Alt+Esc will not.
*/
switch (wParam) {
case VK_TAB:
if (gspwndAltTab == NULL) {
PWND pwndSwitch;
TL tlpSwitchInfo;
/*
* We are entering Alt+Tab for the first time, we need to
* initialize the Switch Window structure and if needed
* create and display the Alt+Tab window. We have two special
* cases: (1) The user does not want to use the Switch window,
* (2) The initialize switch window fails thus we will act
* just like slow Alt+Tab
*/
/*
* Since Alt+Shift is the default hotkey for keyboard layout switching,
* Alt+Shift+Tab may cause a KL switching while AltTab window is up.
* To prevent it, we'd better reset the global toggle key state here,
* so that xxxScanSysQueue will not confuse when it handles keyup messages.
*/
gLangToggleKeyState = KLT_NONE;
/*
* Mouse buttons sometimes get stuck down due to hardware glitches,
* usually due to input concentrator switchboxes or faulty serial
* mouse COM ports, so clear the global button state here just in case,
* otherwise we may not be able to change focus with the mouse.
* Also do this in zzzCancelJournalling (Ctr-Esc, Ctrl-Alt-Del, etc.)
*/
#if DBG
if (gwMouseOwnerButton)
RIPMSG1(RIP_WARNING,
"gwMouseOwnerButton=%x, being forcibly cleared\n",
gwMouseOwnerButton);
#endif
gwMouseOwnerButton = 0;
/*
* Determine the current active window.
*/
Lock(&gspwndActivate, pq->spwndActive);
if (gspwndActivate == NULL) {
Lock(&gspwndActivate, grpdeskRitInput->pDeskInfo->spwnd->spwndChild);
}
if (!gspwndActivate) {
return;
}
ThreadLockWithPti(ptiCurrent, pwndCurrentTopFocus, &tlpwndCurrentTopFocus);
/*
* Make a local copy of gspwndActivate and lock it because xxxFreeWindow will
* unlock if it is the window being freed.
*/
pwndCurrentActivate = gspwndActivate;
ThreadLockAlwaysWithPti(ptiCurrent, pwndCurrentActivate, &tlpwndCurrentActivate);
/*
* Cancel the active window's mode
*/
xxxSendMessageTimeout(pwndCurrentActivate, WM_CANCELMODE, 0, 0, SMTO_ABORTIFHUNG, 100, &dwResult);
/*
* Initialize the Switch Window data structure, if we
* succeed create and display the window, otherwise act
* like slow Alt+Tab.
*/
pwndActivateNext = InitSwitchWndInfo(&pswCurrent, pwndCurrentActivate, fDir);
ThreadLockWithPti(ptiCurrent, pwndActivateNext, &tlpwndActivateNext);
if (pswCurrent == NULL) {
/*
* Couldn't initialize our switch window data structure, so we
* will act like Alt+Esc.
*/
goto DoSlowAltTab;
}
if (pwndActivateNext == NULL) {
RemoveSwitchWindowInfo(&pswCurrent);
ThreadUnlock(&tlpwndActivateNext);
ThreadUnlock(&tlpwndCurrentActivate);
ThreadUnlock(&tlpwndCurrentTopFocus);
Unlock(&gspwndActivate);
return;
}
ThreadLockPoolCleanup(ptiCurrent, &pswCurrent, &tlpSwitchInfo, RemoveSwitchWindowInfo);
/*
* Since we are in the RIT, test the physical state of the keyboard
*/
pswCurrent->fJournaling = FALSE;
/*
* Create the Alt+Tab window
*/
pwndSwitch =
xxxNVCreateWindowEx( WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME,
(PLARGE_STRING)SWITCHWNDCLASS, NULL,
WS_POPUP | WS_BORDER | WS_DISABLED,
0, 0, 10, 10, NULL, NULL, NULL, NULL, VER40);
if (gspwndAltTab != NULL) {
RIPMSG0(RIP_WARNING, "xxxNextWindow: Creating a new switch window while one already exists.");
_PostMessage(gspwndAltTab, WM_CLOSE, 0, 0);
}
Lock(&gspwndAltTab, pwndSwitch);
ThreadUnlockPool(ptiCurrent, &tlpSwitchInfo);
if (gspwndAltTab == NULL) {
/*
* Could not create the cool switch window, do the Alt+Esc thing
*/
#ifdef COOLSWITCHTRACE
DbgPrint("CoolSwitch: Could not create window (contact bradg).\n");
UserAssert(gspwndAltTab != NULL);
#endif
RemoveSwitchWindowInfo(&pswCurrent);
goto DoSlowAltTab;
}
/*
* Save the pointer to the switch window info structure
*/
Setpswi(gspwndAltTab, pswCurrent);
/*
* Set gspwndActivate so the RIT knows what window we would like
* it to activate.
*/
Lock(&gspwndActivate, pwndActivateNext);
/*
* Make sure that our rit queue has the correct pdesk
*/
if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) {
xxxSetThreadDesktop(NULL, grpdeskRitInput); // DeferWinEventNotify() ?? IANJA ??
}
/*
* If we're currently full screen tell console to switch to
* the desktop to GDI mode; we can't do this on the RIT because
* it can be slow.
*/
if (gspwndFullScreen != grpdeskRitInput->pDeskInfo->spwnd) {
ThreadLockWithPti(ptiCurrent, grpdeskRitInput->pDeskInfo->spwnd, &tlpwndT);
xxxSendNotifyMessage(grpdeskRitInput->pDeskInfo->spwnd, WM_FULLSCREEN, GDIFULLSCREEN, (LPARAM)HW(grpdeskRitInput->pDeskInfo->spwnd));
ThreadUnlock(&tlpwndT);
}
/*
* Show the Alt+Tab window. If it returns FALSE this means
* the ALT key has been released, so there is no need to
* paint the icons.
*/
ThreadLockAlwaysWithPti(ptiCurrent, gspwndAltTab, &tlpwndT);
xxxShowSwitchWindow(gspwndAltTab);
ThreadUnlock(&tlpwndT);
/*
* Exit now because the Switch window will have been
* already updated.
*/
ThreadUnlock(&tlpwndActivateNext);
ThreadUnlock(&tlpwndCurrentActivate);
ThreadUnlock(&tlpwndCurrentTopFocus);
} else {
/*
* We come here to do the actual switching and/or updating of
* the switch window when in Alt+Tab mode.
*/
PWND pwndSwitch;
TL tlpwndSwitch;
HWND hwndActivateNext;
HWND hwndStop;
if (!(pwndSwitch = gspwndAltTab)) {
goto DoAltEsc;
} else {
/*
* Move the hilight rect to the next/prev task. It is possible
* that some tasks were destoryed, so we need to skip those.
*/
ThreadLockAlwaysWithPti(ptiCurrent, pwndSwitch, &tlpwndSwitch);
hwndStop = NULL;
do {
pswCurrent = Getpswi(pwndSwitch);
if (pswCurrent == NULL) {
ThreadUnlock(&tlpwndSwitch);
goto DoAltEsc;
}
hwndActivateNext = xxxMoveSwitchWndHilite(pwndSwitch, pswCurrent, fDir);
if (!hwndStop) {
hwndStop = hwndActivateNext;
} else {
if (hwndStop == hwndActivateNext) {
pwndActivateNext = NULL;
break;
}
}
pwndActivateNext = RevalidateHwnd(hwndActivateNext);
} while (!pwndActivateNext);
ThreadUnlock(&tlpwndSwitch);
Lock(&gspwndActivate, pwndActivateNext);
if (!gspwndActivate) {
/*
* No Window to activate, bail out of Alt+Tab mode
*/
xxxCancelCoolSwitch();
}
}
}
break;
DoAltEsc:
case VK_ESCAPE:
/*
* NOTE: The RIT doesn't use gspwndActivate to activate the window when
* processing Alt+Esc, we just use it here as a convenient
* variable. The actual activation takes place below.
*/
pwndCurrentActivate = pq->spwndActive;
if (pwndCurrentActivate == NULL) {
pwndCurrentActivate = pq->ptiKeyboard->rpdesk->pDeskInfo->spwnd->spwndChild;
if (pwndCurrentActivate == NULL) {
return;
}
}
ThreadLockWithPti(ptiCurrent, pwndCurrentTopFocus, &tlpwndCurrentTopFocus);
ThreadLockAlwaysWithPti(ptiCurrent, pwndCurrentActivate, &tlpwndCurrentActivate);
/*
* Cancel the active window's mode
*/
xxxSendMessageTimeout(pwndCurrentActivate, WM_CANCELMODE, 0, 0, SMTO_ABORTIFHUNG, 100, &dwResult);
/*
* Determine the next window to activate
*/
pwndActivateNext = _GetNextQueueWindow(pwndCurrentActivate, fDir, TRUE);
ThreadLockWithPti(ptiCurrent, pwndActivateNext, &tlpwndActivateNext);
/*
* If we're going forward through the windows, move the currently
* active window to the bottom so we'll do the right thing when
* we go backwards.
*/
if (pwndActivateNext != pwndCurrentActivate) {
DoSlowAltTab:
if (pwndActivateNext) {
/*
* We're about to activate another window while the ALT key is down,
* so let the current focus window know that it doesn't need the
* menu underlines anymore
*/
if ((pwndCurrentTopFocus != NULL) && (pwndCurrentTopFocus->spmenu != NULL)) {
ClearMF(pwndCurrentTopFocus->spmenu, MFUNDERLINE);
}
if (fDir == FDIR_FORWARD) {
/*
* For Alt+ESC only move the window to the bottom if it's
* not a top most window
*/
if (!TestWF(pwndCurrentActivate, WEFTOPMOST)) {
xxxSetWindowPos(pwndCurrentActivate, PWND_BOTTOM, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
SWP_DEFERDRAWING | SWP_NOSENDCHANGING |
SWP_ASYNCWINDOWPOS);
}
}
/*
* The ALT key is down, so this window needs menu underlines
*/
if (pwndActivateNext->spmenu != NULL) {
SetMF(pwndActivateNext->spmenu, MFUNDERLINE);
}
/*
* This little ugly hack will cause xxxSetForegroundWindow2()
* to send out an activation messages to a queue that is
* already the active queue allowing us to change the active
* window on that queue.
*/
if (gpqForeground == GETPTI(pwndActivateNext)->pq)
gpqForeground = NULL;
/*
* Make the selected window thread the owner of the last input;
* since he's next, he owns the ALT-ESC.
*/
glinp.ptiLastWoken = GETPTI(pwndActivateNext);
xxxSetForegroundWindow2(pwndActivateNext, NULL,
(wParam == VK_TAB) ? SFW_SWITCH | SFW_ACTIVATERESTORE : SFW_SWITCH);
/*
* Win3.1 calls SetWindowPos() with activate, which z-orders
* first regardless, then activates. Our code relies on
* xxxActivateThisWindow() to z-order, and it'll only do
* it if the window does not have the child bit set (regardless
* that the window is a child of the desktop).
*
* To be compatible, we'll just force z-order here if the
* window has the child bit set. This z-order is asynchronous,
* so this'll z-order after the activate event is processed.
* That'll allow it to come on top because it'll be foreground
* then. (Grammatik has a top level window with the child
* bit set that wants to be come the active window).
*/
if (wParam == VK_TAB && TestWF(pwndActivateNext, WFCHILD)) {
xxxSetWindowPos(pwndActivateNext, (PWND)HWND_TOP, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_ASYNCWINDOWPOS);
}
}
}
ThreadUnlock(&tlpwndActivateNext);
ThreadUnlock(&tlpwndCurrentActivate);
ThreadUnlock(&tlpwndCurrentTopFocus);
break;
case VK_F6:
if ((pwndCurrentActivate = pq->spwndActive) == NULL)
pwndCurrentActivate = pq->ptiKeyboard->rpdesk->pDeskInfo->spwnd->spwndChild;
pwndActivateNext = pwndCurrentActivate;
/*
* HACK! console sessions are all one thread but we want them
* to act like different threads so if its a console thread (csrss.exe)
* then ALT-F6 does nothing just like in Win 3.1
* Note: we never get called with wParam == VK_F6 anyway. Win NT 3.51
* doesn't seem to either, but Windows '95 does. BUG?? (IanJa)
*/
if (!(GETPTI(pwndActivateNext)->TIF_flags & TIF_CSRSSTHREAD)) {
/*
* on a alt-f6, we want to keep the switch within the thread.
* We may want to rethink this because this will look strange
* when you alt-f6 on a multi-threaded app we will not rotate
* through the windows on the different threads. This works
* fine on Win 3.x because it is single threaded.
*/
do {
pwndActivateNext = NextTopWindow(pq->ptiKeyboard, pwndActivateNext, NULL,
fDir ? NTW_PREVIOUS : 0);
} while( (pwndActivateNext != NULL) &&
(GETPTI(pwndActivateNext) != pq->ptiKeyboard));
if (pwndActivateNext != NULL) {
if (pwndActivateNext != pwndCurrentActivate) {
/*
* We're about to activate another window while the ALT key is down,
* so let the current focus window know that it doesn't need the
* menu underlines anymore
*/
pwndCurrentTopFocus = GetTopLevelWindow(pq->spwndFocus);
if ((pwndCurrentTopFocus != NULL) && (pwndCurrentTopFocus->spmenu != NULL)) {
ClearMF(pwndCurrentTopFocus->spmenu, MFUNDERLINE);
}
/*
* The ALT key is down, so this window needs menu underlines
*/
if (pwndActivateNext->spmenu != NULL) {
SetMF(pwndActivateNext->spmenu, MFUNDERLINE);
}
}
ThreadLockAlwaysWithPti(ptiCurrent, pwndActivateNext, &tlpwndActivateNext);
xxxSetWindowPos(pwndActivateNext, PWND_BOTTOM, 0, 0, 0, 0,
SWP_DEFERDRAWING | SWP_NOSENDCHANGING | SWP_NOCHANGE |
SWP_ASYNCWINDOWPOS);
xxxSetForegroundWindow2(pwndActivateNext, NULL, SFW_SWITCH);
ThreadUnlock(&tlpwndActivateNext);
}
}
break;
}
}
/***************************************************************************\
* xxxOldNextWindow
*
* This function does the processing for the alt-tab/esc/F6 UI.
*
* History:
* 03-17-92 DavidPe Ported from Win 3.1 sources
\***************************************************************************/
VOID xxxOldNextWindow(
UINT flags)
{
MSG msg;
HWND hwndSel;
PWND pwndNewSel;
PWND pwndSel;
BOOL fType = 0;
BOOL fDrawIcon;
WORD vk;
TL tlpwndT;
TL tlpwndSel;
TL tlpwndSwitch;
PSWINFO pswCurrent;
PWND pwndSwitch;
HWND hwndStop;
HWND hwndNewSel;
PTHREADINFO ptiCurrent = PtiCurrent();
/*
* Don't allow entering this routine when we're already in the AltTab
* mode. The AltTab window may have been created via xxxNextWindow.
*/
if (gspwndAltTab != NULL) {
return;
}
if ((pwndSel = ptiCurrent->pq->spwndActive) == NULL)
return;
ThreadLockWithPti(ptiCurrent, pwndSel, &tlpwndSel);
xxxCapture(ptiCurrent, pwndSel, SCREEN_CAPTURE);
vk = (WORD)flags;
msg.wParam = (UINT)flags;
pwndNewSel = NULL;
if (vk == VK_TAB) {
TL tlpSwitchInfo;
/*
* Initialize the Switch window data structures
*/
pwndNewSel = InitSwitchWndInfo(&pswCurrent,
pwndSel,
_GetKeyState(VK_SHIFT) < 0);
if (pswCurrent == NULL) {
/*
* We were unable to initialize the data structure used by
* the Switch window, so we will act like Alt+Esc.
*/
} else {
PWND pwndSwitch;
/*
* We are doing a journal playback do use _GetKeyState to
* test the keyboard.
*/
pswCurrent->fJournaling = TRUE;
ThreadLockPoolCleanup(ptiCurrent, &pswCurrent, &tlpSwitchInfo, RemoveSwitchWindowInfo);
pwndSwitch =
xxxNVCreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME,
(PLARGE_STRING)SWITCHWNDCLASS,
NULL,
WS_POPUP | WS_BORDER | WS_DISABLED,
0,
0,
10,
10,
NULL,
NULL,
NULL,
NULL,
VER40);
if (gspwndAltTab != NULL) {
RIPMSGF0(RIP_WARNING,
"Creating a new switch window while one already exists.");
_PostMessage(gspwndAltTab, WM_CLOSE, 0, 0);
}
ThreadUnlockPool(ptiCurrent, &tlpSwitchInfo);
Lock(&gspwndAltTab, pwndSwitch);
if (!(pwndSwitch = gspwndAltTab)) {
RemoveSwitchWindowInfo(&pswCurrent);
} else {
/*
* Lock the switch window.
*/
ThreadLockAlwaysWithPti(ptiCurrent, pwndSwitch, &tlpwndSwitch);
/*
* Save the switch window info
*/
Setpswi(pwndSwitch, pswCurrent);
// Don't we need to switch from full screen mode if needed?
#if 0
/*
* If we're currently full screen tell console to switch to
* the desktop to GDI mode; we can't do this on the RIT because
* it can be slow.
*/
if (gspwndFullScreen != grpdeskRitInput->pDeskInfo->spwnd) {
ThreadLockWithPti(pti, grpdeskRitInput->pDeskInfo->spwnd, &tlpwndT);
xxxSendNotifyMessage(grpdeskRitInput->pDeskInfo->spwnd, WM_FULLSCREEN, GDIFULLSCREEN, (LONG)HW(grpdeskRitInput->pDeskInfo->spwnd));
ThreadUnlock(&tlpwndT);
}
#endif
/*
* Show the switch window, this also will paint the window
*/
xxxShowSwitchWindow(gspwndAltTab);
ThreadUnlock(&tlpwndSwitch);
}
}
}
if (!pwndNewSel)
goto StartTab;
pwndSel = pwndNewSel;
while (TRUE) {
hwndSel = PtoH(pwndSel);
/*
* Wait for a message without getting it out of the queue.
*/
while (!xxxPeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD))
xxxWaitMessage();
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
pwndSel = ptiCurrent->pq->spwndActive;
if (_CallMsgFilter(&msg, MSGF_NEXTWINDOW)) {
/*
* Swallow the message if the hook processed it
*/
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
continue;
}
/*
* If we are doing Alt+Tab and some other key comes in (other than
* tab, escape or shift), then bomb out of this loop and leave that
* key in the queue.
*/
if ((msg.message == WM_SYSKEYDOWN) && gspwndAltTab != NULL) {
vk = (WORD)msg.wParam;
if ((vk != VK_TAB) && (vk != VK_ESCAPE) && (vk != VK_SHIFT)) {
pwndSel = ptiCurrent->pq->spwndActive;
fType = 0;
goto Exit;
}
}
switch (msg.message) {
case WM_CANCELJOURNAL:
/*
* If journalling was canceled we need to exit our loop and
* remove the Alt+Tab window. We don't want to remove this
* meesage because we want the app to know that journalling
* was canceled.
*/
/* > > > F A L L T H R O U G H < < < */
case WM_HOTKEY:
/*
* When pressing ALT-CTL-ESC-DEL on the logon desktop
* We eat WM_KEYUP and the queue for the wiinlogon thread will be empty so will be stuck
* in xxxWaitMessage() forever till the user do a mouse click where we will exit the loop in the below case statement.
* consider WM_HOTKEY as a valid exit case.
* [msadek -- 03/17/2001, bug# 337206]
*/
/* > > > F A L L T H R O U G H < < < */
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
/*
* If mouse message, cancel and get out of loop.
*/
pwndSel = ptiCurrent->pq->spwndActive;
fType = 0;
goto Exit;
case WM_KEYUP:
case WM_KEYDOWN:
case WM_SYSCHAR:
case WM_SYSKEYUP:
case WM_MOUSEMOVE:
/*
* Swallow the message
*/
hwndSel = PtoH(pwndSel);
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
pwndSel = ptiCurrent->pq->spwndActive;
if (msg.message == WM_KEYUP || msg.message == WM_SYSKEYUP) {
vk = (WORD)msg.wParam;
/*
* If alt-tab up, then exit.
*/
if (vk == VK_MENU) {
/*
* If doing Alt+Esc, wait for up of ESC to get out.
*/
if (gspwndAltTab == NULL)
break;
fType = 0;
goto Exit;
} else if (vk == VK_ESCAPE || vk == VK_F6) {
/*
* Get out on up transition of ESC or F6 keys.
*/
if (gspwndAltTab != NULL) {
pwndSel = ptiCurrent->pq->spwndActive;
fType = 0;
} else {
fType = ((vk == VK_ESCAPE) ? ALT_ESCAPE : ALT_F6);
}
goto Exit;
}
} else if (msg.message == WM_KEYDOWN) {
/*
* Exit out loop is a stray key stroke comes through. In
* particular look for VK_CONTROL.
*/
pwndSel = ptiCurrent->pq->spwndActive;
fType = 0;
goto Exit;
}
break;
case WM_SYSKEYDOWN:
vk = (WORD)msg.wParam;
switch (vk) {
case VK_SHIFT:
case VK_TAB:
case VK_ESCAPE:
case VK_F6:
hwndSel = PtoH(pwndSel);
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
pwndSel = ptiCurrent->pq->spwndActive;
if (!(vk == VK_TAB))
break;
StartTab:
if (vk == VK_ESCAPE) {
pwndNewSel = _GetNextQueueWindow(
pwndSel,
_GetKeyState(VK_SHIFT) < 0,
TRUE);
if (pwndNewSel == NULL)
break;
fType = ALT_ESCAPE;
pwndSel = pwndNewSel;
/*
* Wait until ESC goes up to activate new window.
*/
break;
}
if (vk == VK_F6) {
PWND pwndFirst;
PWND pwndSaveSel = pwndSel;
/*
* Save the first returned window to act as a limit
* to the search because NextTopWindow will return NULL
* only if pwndSel is the only window that meets its
* selection criteria.
*
* This prevents a hang that can occur in winword or
* excel when then Alt-F4-F6 key combination is hit
* and unsaved changes exist.
*/
pwndFirst = pwndNewSel = (PWND)NextTopWindow(ptiCurrent, pwndSel, NULL,
_GetKeyState(VK_SHIFT) < 0 ? NTW_PREVIOUS : 0);
while (TRUE) {
/*
* If pwndNewSel is NULL, pwndSel is the only candidate.
*/
if (pwndNewSel == NULL)
break;
pwndSel = pwndNewSel;
/*
* If the window is on the same thread, wait until
* F6 goes up to activate new window.
*/
if (GETPTI(pwndSel) == ptiCurrent)
break;
pwndNewSel = (PWND)NextTopWindow(ptiCurrent, pwndSel, NULL,
_GetKeyState(VK_SHIFT) < 0 ? NTW_PREVIOUS : 0);
/*
* If we've looped around, use the original window.
* Wait until F6 goes up to activate new window.
*/
if (pwndNewSel == pwndFirst) {
pwndSel = pwndSaveSel;
break;
}
}
break;
}
/*
* Here for the Alt+Tab case
*/
if ((pwndSwitch = gspwndAltTab) != NULL) {
ThreadLockWithPti(ptiCurrent, pwndSwitch, &tlpwndSwitch);
hwndStop = NULL;
do {
pswCurrent = Getpswi(pwndSwitch);
if (pswCurrent == NULL) {
break;
}
hwndNewSel = xxxMoveSwitchWndHilite(
pwndSwitch,
pswCurrent,
_GetKeyState(VK_SHIFT) < 0);
if (!hwndStop) {
hwndStop = hwndNewSel;
} else {
if (hwndStop == hwndNewSel) {
pwndNewSel = NULL;
break;
}
}
pwndNewSel = RevalidateHwnd(hwndNewSel);
} while (!pwndNewSel);
ThreadUnlock(&tlpwndSwitch);
pwndSel = pwndNewSel;
} else {
pwndNewSel = _GetNextQueueWindow(
pwndSel,
_GetKeyState(VK_SHIFT) < 0,
FALSE);
if (pwndNewSel && pwndNewSel != pwndSel) {
if (!TestWF(pwndSel, WEFTOPMOST)) {
/*
* Force the old window to the bottom
*/
ThreadLockWithPti(ptiCurrent, pwndSel, &tlpwndT);
xxxSetWindowPos(pwndSel,
PWND_BOTTOM,
0,
0,
0,
0,
SWP_NOMOVE |
SWP_NOSIZE |
SWP_NOACTIVATE |
SWP_DEFERDRAWING |
SWP_NOSENDCHANGING |
SWP_ASYNCWINDOWPOS);
ThreadUnlock(&tlpwndT);
}
pwndSel = pwndNewSel; // Will be revalidated at top of loop
}
}
break;
default:
goto Exit;
}
break;
default:
hwndSel = PtoH(pwndSel);
xxxPeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
xxxTranslateMessage(&msg, 0);
xxxDispatchMessage(&msg);
if ((pwndSel = RevalidateHwnd(hwndSel)) == NULL)
pwndSel = ptiCurrent->pq->spwndActive;
break;
}
}
Exit:
xxxReleaseCapture();
fDrawIcon = (gspwndAltTab != NULL);
/*
* If this is an Alt-Escape we also have to send the current window
* to the bottom.
*/
if (fType == ALT_ESCAPE) {
PWND pwndActive;
if (gpqForeground) {
pwndActive = gpqForeground->spwndActive;
if (pwndActive && (pwndActive != pwndSel)) {
ThreadLockWithPti(ptiCurrent, pwndActive, &tlpwndT);
xxxSetWindowPos(pwndActive,
PWND_BOTTOM,
0,
0,
0,
0,
SWP_NOMOVE |
SWP_NOSIZE |
SWP_NOACTIVATE |
SWP_DEFERDRAWING |
SWP_NOSENDCHANGING |
SWP_ASYNCWINDOWPOS);
ThreadUnlock(&tlpwndT);
}
}
}
if (pwndSel) {
ThreadLockWithPti(ptiCurrent, pwndSel, &tlpwndT);
xxxSetForegroundWindow(pwndSel, FALSE);
if (TestWF(pwndSel, WFMINIMIZED)) {
if ((fType == 0) && fDrawIcon)
_PostMessage(pwndSel, WM_SYSCOMMAND, (UINT)SC_RESTORE, 0);
}
ThreadUnlock(&tlpwndT);
}
/*
* destroy the alt-tab window
*/
xxxCancelCoolSwitch();
ThreadUnlock(&tlpwndSel);
}
/*****************************************************************************\
*
* GetAltTabInfo() - Active Accessibility API for OLEACC
*
* This succeeds if we are currently in alt-tab mode.
*
\*****************************************************************************/
BOOL WINAPI
_GetAltTabInfo(
int iItem,
PALTTABINFO pati,
LPWSTR ccxpwszItemText,
UINT cchItemText OPTIONAL,
BOOL bAnsi)
{
PSWINFO pswCurrent;
if (!gspwndAltTab || ((pswCurrent = Getpswi(gspwndAltTab)) == NULL)) {
RIPERR0(ERROR_NOT_FOUND, RIP_WARNING, "no Alt-Tab window");
return FALSE;
}
/*
* Fill in general information
*/
pati->cItems = pswCurrent->iTotalTasks;
pati->cColumns = pswCurrent->iNoOfColumns;
pati->cRows = pswCurrent->iNoOfRows;
pati->iColFocus = pswCurrent->iCurCol;
pati->iRowFocus = pswCurrent->iCurRow;
pati->cxItem = CXICONSLOT;
pati->cyItem = CYICONSLOT;
pati->ptStart = pswCurrent->ptFirstRowStart;
/*
* Fill in specific information if asked.
*/
if (cchItemText && (iItem >= 0)) {
PWND pwndCur;
pwndCur = NULL;
try {
if ((iItem < pswCurrent->iTotalTasks) &&
(pwndCur = RevalidateHwnd(pswCurrent->pbwl->rghwnd[iItem]))) {
if (bAnsi) {
LPSTR ccxpszItemText = (LPSTR)ccxpwszItemText;
ULONG cch;
RtlUnicodeToMultiByteN(ccxpszItemText, cchItemText - 1,
&cch, pwndCur->strName.Buffer, pwndCur->strName.Length);
ccxpszItemText[cch] = '\0';
} else {
TextCopy(&pwndCur->strName, ccxpwszItemText, cchItemText);
}
} else {
// no such item
NullTerminateString(ccxpwszItemText, bAnsi);
}
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
return FALSE;
}
}
return TRUE;
}