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