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