1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            win32ss/user/user32/controls/appswitch.c
5  * PURPOSE:         app switching functionality
6  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
7  *                  David Quintana (gigaherz@gmail.com)
8  *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
9  */
10 
11 //
12 // TODO:
13 //       Move to Win32k.
14 //       Add registry support.
15 //
16 //
17 
18 #include <user32.h>
19 
20 WINE_DEFAULT_DEBUG_CHANNEL(user32);
21 
22 #define DIALOG_MARGIN   8       // margin of dialog contents
23 
24 #define CX_ICON         32      // width of icon
25 #define CY_ICON         32      // height of icon
26 #define ICON_MARGIN     4       // margin width around an icon
27 
28 #define CX_ITEM         (CX_ICON + 2 * ICON_MARGIN)
29 #define CY_ITEM         (CY_ICON + 2 * ICON_MARGIN)
30 #define ITEM_MARGIN     4       // margin width around an item
31 
32 #define CX_ITEM_SPACE   (CX_ITEM + 2 * ITEM_MARGIN)
33 #define CY_ITEM_SPACE   (CY_ITEM + 2 * ITEM_MARGIN)
34 
35 #define CY_TEXT_MARGIN  4       // margin height around text
36 
37 // limit the number of windows shown in the alt-tab window
38 // 120 windows results in (12*40) by (10*40) pixels worth of icons.
39 #define MAX_WINDOWS 120
40 
41 // Global variables
42 HWND switchdialog = NULL;
43 HFONT dialogFont;
44 int selectedWindow = 0;
45 BOOL isOpen = FALSE;
46 
47 int fontHeight=0;
48 
49 WCHAR windowText[1024];
50 
51 HWND windowList[MAX_WINDOWS];
52 HICON iconList[MAX_WINDOWS];
53 int windowCount = 0;
54 
55 int cxBorder, cyBorder;
56 int nItems, nCols, nRows;
57 int itemsW, itemsH;
58 int totalW, totalH;
59 int xOffset, yOffset;
60 POINT ptStart;
61 
62 int nShift = 0;
63 
64 BOOL Esc = FALSE;
65 
66 BOOL CoolSwitch = TRUE;
67 int CoolSwitchRows = 3;
68 int CoolSwitchColumns = 7;
69 
70 // window style
71 const DWORD Style = WS_POPUP | WS_BORDER | WS_DISABLED;
72 const DWORD ExStyle = WS_EX_TOPMOST | WS_EX_DLGMODALFRAME | WS_EX_TOOLWINDOW;
73 
LoadCoolSwitchSettings(void)74 BOOL LoadCoolSwitchSettings(void)
75 {
76    CoolSwitch = TRUE;
77    CoolSwitchRows = 3;
78    CoolSwitchColumns = 7;
79 
80    // FIXME: load the settings from registry
81 
82    TRACE("CoolSwitch: %d\n", CoolSwitch);
83    TRACE("CoolSwitchRows: %d\n", CoolSwitchRows);
84    TRACE("CoolSwitchColumns: %d\n", CoolSwitchColumns);
85 
86    return TRUE;
87 }
88 
ResizeAndCenter(HWND hwnd,int width,int height)89 void ResizeAndCenter(HWND hwnd, int width, int height)
90 {
91    int x, y;
92    RECT Rect;
93 
94    int screenwidth = GetSystemMetrics(SM_CXSCREEN);
95    int screenheight = GetSystemMetrics(SM_CYSCREEN);
96 
97    x = (screenwidth - width) / 2;
98    y = (screenheight - height) / 2;
99 
100    SetRect(&Rect, x, y, x + width, y + height);
101    AdjustWindowRectEx(&Rect, Style, FALSE, ExStyle);
102 
103    x = Rect.left;
104    y = Rect.top;
105    width = Rect.right - Rect.left;
106    height = Rect.bottom - Rect.top;
107    MoveWindow(hwnd, x, y, width, height, FALSE);
108 
109    ptStart.x = x;
110    ptStart.y = y;
111 }
112 
CompleteSwitch(BOOL doSwitch)113 void CompleteSwitch(BOOL doSwitch)
114 {
115    if (!isOpen)
116       return;
117 
118    isOpen = FALSE;
119 
120    TRACE("[ATbot] CompleteSwitch Hiding Window.\n");
121    ShowWindowAsync(switchdialog, SW_HIDE);
122 
123    if(doSwitch)
124    {
125       if(selectedWindow >= windowCount)
126          return;
127 
128       // FIXME: workaround because reactos fails to activate the previous window correctly.
129       //if(selectedWindow != 0)
130       {
131          HWND hwnd = windowList[selectedWindow];
132 
133          GetWindowTextW(hwnd, windowText, _countof(windowText));
134 
135          TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText);
136 
137          SwitchToThisWindow(hwnd, TRUE);
138       }
139    }
140 
141    windowCount = 0;
142 }
143 
EnumerateCallback(HWND window,LPARAM lParam)144 BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam)
145 {
146     HICON hIcon = NULL;
147     LRESULT bAlive;
148 
149     UNREFERENCED_PARAMETER(lParam);
150 
151     // First try to get the big icon assigned to the window
152 #define ICON_TIMEOUT 100 // in milliseconds
153     bAlive = SendMessageTimeoutW(window, WM_GETICON, ICON_BIG, 0, SMTO_ABORTIFHUNG | SMTO_BLOCK,
154                                  ICON_TIMEOUT, (PDWORD_PTR)&hIcon);
155     if (!hIcon)
156     {
157         // If no icon is assigned, try to get the icon assigned to the windows' class
158         hIcon = (HICON)GetClassLongPtrW(window, GCL_HICON);
159         if (!hIcon)
160         {
161             // If we still don't have an icon, see if we can do with the small icon,
162             // or a default application icon
163             if (bAlive)
164             {
165                 SendMessageTimeoutW(window, WM_GETICON, ICON_SMALL2, 0,
166                                     SMTO_ABORTIFHUNG | SMTO_BLOCK, ICON_TIMEOUT,
167                                     (PDWORD_PTR)&hIcon);
168             }
169 #undef ICON_TIMEOUT
170             if (!hIcon)
171             {
172                 // using windows logo icon as default
173                 hIcon = gpsi->hIconWindows;
174                 if (!hIcon)
175                 {
176                     //if all attempts to get icon fails go to the next window
177                     return TRUE;
178                 }
179             }
180         }
181     }
182 
183     windowList[windowCount] = window;
184     iconList[windowCount] = CopyIcon(hIcon);
185     windowCount++;
186 
187     // If we got to the max number of windows, we won't be able to add any more
188     return (windowCount < MAX_WINDOWS);
189 }
190 
GetNiceRootOwner(HWND hwnd)191 static HWND GetNiceRootOwner(HWND hwnd)
192 {
193     HWND hwndOwner;
194     DWORD ExStyle, OwnerExStyle;
195 
196     for (;;)
197     {
198         // A window with WS_EX_APPWINDOW is treated as if it has no owner
199         ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
200         if (ExStyle & WS_EX_APPWINDOW)
201             break;
202 
203         // Is the owner visible?
204         // An window with WS_EX_TOOLWINDOW is treated as if it weren't visible
205         hwndOwner = GetWindow(hwnd, GW_OWNER);
206         OwnerExStyle = GetWindowLong(hwndOwner, GWL_EXSTYLE);
207         if (!IsWindowVisible(hwndOwner) || (OwnerExStyle & WS_EX_TOOLWINDOW))
208             break;
209 
210         hwnd = hwndOwner;
211     }
212 
213     return hwnd;
214 }
215 
216 // c.f. http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx
IsAltTabWindow(HWND hwnd)217 BOOL IsAltTabWindow(HWND hwnd)
218 {
219     DWORD ExStyle;
220     RECT rc;
221     HWND hwndTry, hwndWalk;
222     WCHAR szClass[64];
223 
224     // must be visible
225     if (!IsWindowVisible(hwnd))
226         return FALSE;
227 
228     // must not be WS_EX_TOOLWINDOW nor WS_EX_NOACTIVATE
229     ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
230     if (ExStyle & (WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE))
231         return FALSE;
232 
233     // must be not empty rect
234     GetWindowRect(hwnd, &rc);
235     if (IsRectEmpty(&rc))
236         return FALSE;
237 
238     // check special windows
239     if (!GetClassNameW(hwnd, szClass, _countof(szClass)) ||
240         wcscmp(szClass, L"Shell_TrayWnd") == 0 ||
241         wcscmp(szClass, L"Progman") == 0)
242     {
243         return TRUE;
244     }
245 
246     // get 'nice' root owner
247     hwndWalk = GetNiceRootOwner(hwnd);
248 
249     // walk back from hwndWalk toward hwnd
250     for (;;)
251     {
252         hwndTry = GetLastActivePopup(hwndWalk);
253         if (hwndTry == hwndWalk)
254             break;
255 
256         ExStyle = GetWindowLong(hwndTry, GWL_EXSTYLE);
257         if (IsWindowVisible(hwndTry) && !(ExStyle & WS_EX_TOOLWINDOW))
258             break;
259 
260         hwndWalk = hwndTry;
261     }
262 
263     return hwnd == hwndTry;     // Reached?
264 }
265 
266 static BOOL CALLBACK
EnumWindowsProc(HWND hwnd,LPARAM lParam)267 EnumWindowsProc(HWND hwnd, LPARAM lParam)
268 {
269     if (IsAltTabWindow(hwnd))
270     {
271         if (!EnumerateCallback(hwnd, lParam))
272             return FALSE;
273     }
274     return TRUE;
275 }
276 
ProcessMouseMessage(UINT message,LPARAM lParam)277 void ProcessMouseMessage(UINT message, LPARAM lParam)
278 {
279    int xPos = LOWORD(lParam);
280    int yPos = HIWORD(lParam);
281 
282    int xIndex = (xPos - DIALOG_MARGIN) / CX_ITEM_SPACE;
283    int yIndex = (yPos - DIALOG_MARGIN) / CY_ITEM_SPACE;
284 
285    if (xIndex < 0 || nCols <= xIndex ||
286        yIndex < 0 || nRows <= yIndex)
287    {
288         return;
289    }
290 
291    selectedWindow = (yIndex*nCols) + xIndex;
292    if (message == WM_MOUSEMOVE)
293    {
294       InvalidateRect(switchdialog, NULL, TRUE);
295       //RedrawWindow(switchdialog, NULL, NULL, 0);
296    }
297    else
298    {
299       selectedWindow = (yIndex*nCols) + xIndex;
300       CompleteSwitch(TRUE);
301    }
302 }
303 
OnPaint(HWND hWnd)304 void OnPaint(HWND hWnd)
305 {
306     HDC dialogDC;
307     PAINTSTRUCT paint;
308     RECT cRC, textRC;
309     int i, xPos, yPos, CharCount;
310     HFONT dcFont;
311     HICON hIcon;
312     HPEN hPen;
313     COLORREF Color;
314 
315     // check
316     if (nCols == 0 || nItems == 0)
317         return;
318 
319     // begin painting
320     dialogDC = BeginPaint(hWnd, &paint);
321     if (dialogDC == NULL)
322         return;
323 
324     // fill the client area
325     GetClientRect(hWnd, &cRC);
326     FillRect(dialogDC, &cRC, (HBRUSH)(COLOR_3DFACE + 1));
327 
328     // if the selection index exceeded the display items, then
329     // do display item shifting
330     if (selectedWindow >= nItems)
331         nShift = selectedWindow - nItems + 1;
332     else
333         nShift = 0;
334 
335     for (i = 0; i < nItems; ++i)
336     {
337         // get the icon to display
338         hIcon = iconList[i + nShift];
339 
340         // calculate the position where we start drawing
341         xPos = DIALOG_MARGIN + CX_ITEM_SPACE * (i % nCols) + ITEM_MARGIN;
342         yPos = DIALOG_MARGIN + CY_ITEM_SPACE * (i / nCols) + ITEM_MARGIN;
343 
344         // centering
345         if (nItems < CoolSwitchColumns)
346         {
347             xPos += (itemsW - nItems * CX_ITEM_SPACE) / 2;
348         }
349 
350         // if this position is selected,
351         if (selectedWindow == i + nShift)
352         {
353             // create a solid pen
354             Color = GetSysColor(COLOR_HIGHLIGHT);
355             hPen = CreatePen(PS_SOLID, 1, Color);
356 
357             // draw a rectangle with using the pen
358             SelectObject(dialogDC, hPen);
359             SelectObject(dialogDC, GetStockObject(NULL_BRUSH));
360             Rectangle(dialogDC, xPos, yPos, xPos + CX_ITEM, yPos + CY_ITEM);
361             Rectangle(dialogDC, xPos + 1, yPos + 1,
362                                 xPos + CX_ITEM - 1, yPos + CY_ITEM - 1);
363 
364             // delete the pen
365             DeleteObject(hPen);
366         }
367 
368         // draw icon
369         DrawIconEx(dialogDC, xPos + ICON_MARGIN, yPos + ICON_MARGIN,
370                    hIcon, CX_ICON, CY_ICON, 0, NULL, DI_NORMAL);
371     }
372 
373     // set the text rectangle
374     SetRect(&textRC, DIALOG_MARGIN, DIALOG_MARGIN + itemsH,
375             totalW - DIALOG_MARGIN, totalH - DIALOG_MARGIN);
376 
377     // draw the sunken button around text
378     DrawFrameControl(dialogDC, &textRC, DFC_BUTTON,
379                      DFCS_BUTTONPUSH | DFCS_PUSHED);
380 
381     // get text
382     CharCount = GetWindowTextW(windowList[selectedWindow], windowText,
383                                _countof(windowText));
384 
385     // draw text
386     dcFont = SelectObject(dialogDC, dialogFont);
387     SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT));
388     SetBkMode(dialogDC, TRANSPARENT);
389     DrawTextW(dialogDC, windowText, CharCount, &textRC,
390               DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE);
391     SelectObject(dialogDC, dcFont);
392 
393     // end painting
394     EndPaint(hWnd, &paint);
395 }
396 
CreateSwitcherWindow(HINSTANCE hInstance)397 DWORD CreateSwitcherWindow(HINSTANCE hInstance)
398 {
399     switchdialog = CreateWindowExW( WS_EX_TOPMOST|WS_EX_DLGMODALFRAME|WS_EX_TOOLWINDOW,
400                                     WC_SWITCH,
401                                     L"",
402                                     WS_POPUP|WS_BORDER|WS_DISABLED,
403                                     CW_USEDEFAULT,
404                                     CW_USEDEFAULT,
405                                     400, 150,
406                                     NULL, NULL,
407                                     hInstance, NULL);
408     if (!switchdialog)
409     {
410        TRACE("[ATbot] Task Switcher Window failed to create.\n");
411        return 0;
412     }
413 
414     isOpen = FALSE;
415     return 1;
416 }
417 
GetDialogFont(VOID)418 DWORD GetDialogFont(VOID)
419 {
420    HDC tDC;
421    TEXTMETRIC tm;
422 
423    dialogFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
424 
425    tDC = GetDC(0);
426    GetTextMetrics(tDC, &tm);
427    fontHeight = tm.tmHeight;
428    ReleaseDC(0, tDC);
429 
430    return 1;
431 }
432 
PrepareWindow(VOID)433 void PrepareWindow(VOID)
434 {
435    nItems = windowCount;
436 
437    nCols = CoolSwitchColumns;
438    nRows = (nItems + CoolSwitchColumns - 1) / CoolSwitchColumns;
439    if (nRows > CoolSwitchRows)
440    {
441       nRows = CoolSwitchRows;
442       nItems = nRows * nCols;
443    }
444 
445    itemsW = nCols * CX_ITEM_SPACE;
446    itemsH = nRows * CY_ITEM_SPACE;
447 
448    totalW = itemsW + 2 * DIALOG_MARGIN;
449    totalH = itemsH + 2 * DIALOG_MARGIN;
450    totalH += fontHeight + 2 * CY_TEXT_MARGIN;
451 
452    ResizeAndCenter(switchdialog, totalW, totalH);
453 }
454 
ProcessHotKey(VOID)455 BOOL ProcessHotKey(VOID)
456 {
457    if (!isOpen)
458    {
459       windowCount = 0;
460       EnumWindows(EnumWindowsProc, 0);
461 
462       if (windowCount == 0)
463          return FALSE;
464 
465       if (windowCount == 1)
466       {
467          SwitchToThisWindow(windowList[0], TRUE);
468          return FALSE;
469       }
470 
471       if (!CreateSwitcherWindow(User32Instance))
472          return FALSE;
473 
474       selectedWindow = 1;
475 
476       TRACE("[ATbot] HotKey Received. Opening window.\n");
477       ShowWindowAsync(switchdialog, SW_SHOWNORMAL);
478       SwitchToThisWindow(switchdialog, TRUE);
479       isOpen = TRUE;
480    }
481    else
482    {
483       TRACE("[ATbot] HotKey Received  Rotating.\n");
484       selectedWindow = (selectedWindow + 1)%windowCount;
485       InvalidateRect(switchdialog, NULL, TRUE);
486    }
487    return TRUE;
488 }
489 
RotateTasks(BOOL bShift)490 void RotateTasks(BOOL bShift)
491 {
492     HWND hwndFirst, hwndLast;
493     DWORD Size;
494 
495     if (windowCount < 2 || !Esc)
496         return;
497 
498     hwndFirst = windowList[0];
499     hwndLast = windowList[windowCount - 1];
500 
501     if (bShift)
502     {
503         SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0,
504                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
505                      SWP_NOOWNERZORDER | SWP_NOREPOSITION | SWP_ASYNCWINDOWPOS);
506 
507         SwitchToThisWindow(hwndLast, TRUE);
508 
509         Size = (windowCount - 1) * sizeof(HWND);
510         MoveMemory(&windowList[1], &windowList[0], Size);
511         windowList[0] = hwndLast;
512     }
513     else
514     {
515         SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0,
516                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
517                      SWP_NOOWNERZORDER | SWP_NOREPOSITION | SWP_ASYNCWINDOWPOS);
518 
519         SwitchToThisWindow(windowList[1], TRUE);
520 
521         Size = (windowCount - 1) * sizeof(HWND);
522         MoveMemory(&windowList[0], &windowList[1], Size);
523         windowList[windowCount - 1] = hwndFirst;
524     }
525 }
526 
MoveLeft(void)527 static void MoveLeft(void)
528 {
529     selectedWindow = selectedWindow - 1;
530     if (selectedWindow < 0)
531         selectedWindow = windowCount - 1;
532     InvalidateRect(switchdialog, NULL, TRUE);
533 }
534 
MoveRight(void)535 static void MoveRight(void)
536 {
537     selectedWindow = (selectedWindow + 1) % windowCount;
538     InvalidateRect(switchdialog, NULL, TRUE);
539 }
540 
MoveUp(void)541 static void MoveUp(void)
542 {
543     INT iRow = selectedWindow / nCols;
544     INT iCol = selectedWindow % nCols;
545 
546     --iRow;
547     if (iRow < 0)
548         iRow = nRows - 1;
549 
550     selectedWindow = iRow * nCols + iCol;
551     if (selectedWindow >= windowCount)
552         selectedWindow = windowCount - 1;
553     InvalidateRect(switchdialog, NULL, TRUE);
554 }
555 
MoveDown(void)556 static void MoveDown(void)
557 {
558     INT iRow = selectedWindow / nCols;
559     INT iCol = selectedWindow % nCols;
560 
561     ++iRow;
562     if (iRow >= nRows)
563         iRow = 0;
564 
565     selectedWindow = iRow * nCols + iCol;
566     if (selectedWindow >= windowCount)
567         selectedWindow = windowCount - 1;
568     InvalidateRect(switchdialog, NULL, TRUE);
569 }
570 
571 VOID
DestroyAppWindows(VOID)572 DestroyAppWindows(VOID)
573 {
574     // for every item of the icon list:
575     INT i;
576     for (i = 0; i < windowCount; ++i)
577     {
578         // destroy the icon
579         DestroyIcon(iconList[i]);
580         iconList[i] = NULL;
581     }
582 }
583 
DoAppSwitch(WPARAM wParam,LPARAM lParam)584 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
585 {
586    HWND hwndActive;
587    MSG msg;
588 
589    // FIXME: Is loading timing OK?
590    LoadCoolSwitchSettings();
591 
592    if (!CoolSwitch)
593       return 0;
594 
595    // Already in the loop.
596    if (switchdialog || Esc) return 0;
597 
598    if (lParam == VK_ESCAPE)
599    {
600       Esc = TRUE;
601 
602       windowCount = 0;
603       EnumWindows(EnumWindowsProc, 0);
604 
605       if (windowCount < 2)
606           return 0;
607 
608       RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0);
609 
610       hwndActive = GetActiveWindow();
611 
612       if (hwndActive == NULL)
613       {
614           Esc = FALSE;
615           return 0;
616       }
617    }
618 
619    // Capture current active window.
620    hwndActive = GetActiveWindow();
621    if (hwndActive)
622        SetCapture(hwndActive);
623 
624    switch (lParam)
625    {
626       case VK_TAB:
627          if (!GetDialogFont() || !ProcessHotKey())
628              goto Exit;
629          break;
630 
631       case VK_ESCAPE:
632          break;
633 
634       default:
635          goto Exit;
636    }
637 
638    if (!hwndActive)
639        goto Exit;
640 
641    // Main message loop:
642    while (1)
643    {
644       for (;;)
645       {
646          if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
647          {
648              if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break;
649              /* remove the message from the queue */
650              PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
651          }
652          else
653              WaitMessage();
654       }
655 
656       switch (msg.message)
657       {
658         case WM_KEYUP:
659         {
660           PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
661           if (msg.wParam == VK_MENU)
662           {
663              CompleteSwitch(TRUE);
664           }
665           else if (msg.wParam == VK_RETURN)
666           {
667              CompleteSwitch(TRUE);
668           }
669           else if (msg.wParam == VK_ESCAPE)
670           {
671              TRACE("DoAppSwitch VK_ESCAPE 2\n");
672              CompleteSwitch(FALSE);
673           }
674           goto Exit; //break;
675         }
676 
677         case WM_SYSKEYDOWN:
678         {
679           PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
680           if (HIWORD(msg.lParam) & KF_ALTDOWN)
681           {
682              if ( msg.wParam == VK_TAB )
683              {
684                 if (Esc) break;
685                 if (GetKeyState(VK_SHIFT) < 0)
686                 {
687                     MoveLeft();
688                 }
689                 else
690                 {
691                     MoveRight();
692                 }
693              }
694              else if ( msg.wParam == VK_ESCAPE )
695              {
696                 if (!Esc) break;
697                 RotateTasks(GetKeyState(VK_SHIFT) < 0);
698              }
699              else if ( msg.wParam == VK_LEFT )
700              {
701                 MoveLeft();
702              }
703              else if ( msg.wParam == VK_RIGHT )
704              {
705                 MoveRight();
706              }
707              else if ( msg.wParam == VK_UP )
708              {
709                 MoveUp();
710              }
711              else if ( msg.wParam == VK_DOWN )
712              {
713                 MoveDown();
714              }
715           }
716           break;
717         }
718 
719         case WM_LBUTTONUP:
720           PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
721           ProcessMouseMessage(msg.message, msg.lParam);
722           goto Exit;
723 
724         default:
725           if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ))
726           {
727              TranslateMessage(&msg);
728              DispatchMessageW(&msg);
729           }
730           break;
731       }
732    }
733 Exit:
734    ReleaseCapture();
735    if (switchdialog) DestroyWindow(switchdialog);
736    if (Esc) DestroyAppWindows();
737    switchdialog = NULL;
738    selectedWindow = 0;
739    windowCount = 0;
740    Esc = FALSE;
741    return 0;
742 }
743 
744 //
745 // Switch System Class Window Proc.
746 //
SwitchWndProc_common(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam,BOOL unicode)747 LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode )
748 {
749    PWND pWnd;
750    PALTTABINFO ati;
751    pWnd = ValidateHwnd(hWnd);
752    if (pWnd)
753    {
754       if (!pWnd->fnid)
755       {
756          NtUserSetWindowFNID(hWnd, FNID_SWITCH);
757       }
758    }
759 
760    switch (uMsg)
761    {
762       case WM_NCCREATE:
763          if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati))))
764             return 0;
765          SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati );
766          return TRUE;
767 
768       case WM_SHOWWINDOW:
769          if (wParam)
770          {
771             PrepareWindow();
772             ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
773             ati->cbSize = sizeof(ALTTABINFO);
774             ati->cItems = nItems;
775             ati->cColumns = nCols;
776             ati->cRows = nRows;
777             if (nCols)
778             {
779                ati->iColFocus = (selectedWindow - nShift) % nCols;
780                ati->iRowFocus = (selectedWindow - nShift) / nCols;
781             }
782             else
783             {
784                ati->iColFocus = 0;
785                ati->iRowFocus = 0;
786             }
787             ati->cxItem = CX_ITEM_SPACE;
788             ati->cyItem = CY_ITEM_SPACE;
789             ati->ptStart = ptStart;
790          }
791          return 0;
792 
793       case WM_MOUSEMOVE:
794          ProcessMouseMessage(uMsg, lParam);
795          return 0;
796 
797       case WM_ACTIVATE:
798          if (wParam == WA_INACTIVE)
799          {
800             CompleteSwitch(FALSE);
801          }
802          return 0;
803 
804       case WM_PAINT:
805          OnPaint(hWnd);
806          return 0;
807 
808       case WM_DESTROY:
809          isOpen = FALSE;
810          ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
811          HeapFree( GetProcessHeap(), 0, ati );
812          SetWindowLongPtrW( hWnd, 0, 0 );
813          DestroyAppWindows();
814          NtUserSetWindowFNID(hWnd, FNID_DESTROY);
815          return 0;
816    }
817    return DefWindowProcW(hWnd, uMsg, wParam, lParam);
818 }
819 
SwitchWndProcA(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)820 LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
821 {
822    return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
823 }
824 
SwitchWndProcW(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)825 LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
826 {
827    return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
828 }
829