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 == 0) 428 return FALSE; 429 430 if (windowCount == 1) 431 { 432 selectedWindow = 0; 433 CompleteSwitch(TRUE); 434 return TRUE; 435 } 436 437 selectedWindow = 1; 438 439 TRACE("[ATbot] HotKey Received. Opening window.\n"); 440 ShowWindow(switchdialog, SW_SHOWNORMAL); 441 MakeWindowActive(switchdialog); 442 isOpen = TRUE; 443 } 444 else 445 { 446 TRACE("[ATbot] HotKey Received Rotating.\n"); 447 selectedWindow = (selectedWindow + 1)%windowCount; 448 InvalidateRect(switchdialog, NULL, TRUE); 449 } 450 return TRUE; 451 } 452 453 void RotateTasks(BOOL bShift) 454 { 455 HWND hwndFirst, hwndLast; 456 DWORD Size; 457 458 if (windowCount < 2 || !Esc) 459 return; 460 461 hwndFirst = windowList[0]; 462 hwndLast = windowList[windowCount - 1]; 463 464 if (bShift) 465 { 466 SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0, 467 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | 468 SWP_NOOWNERZORDER | SWP_NOREPOSITION); 469 470 MakeWindowActive(hwndLast); 471 472 Size = (windowCount - 1) * sizeof(HWND); 473 MoveMemory(&windowList[1], &windowList[0], Size); 474 windowList[0] = hwndLast; 475 } 476 else 477 { 478 SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0, 479 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | 480 SWP_NOOWNERZORDER | SWP_NOREPOSITION); 481 482 MakeWindowActive(windowList[1]); 483 484 Size = (windowCount - 1) * sizeof(HWND); 485 MoveMemory(&windowList[0], &windowList[1], Size); 486 windowList[windowCount - 1] = hwndFirst; 487 } 488 } 489 490 static void MoveLeft(void) 491 { 492 selectedWindow = selectedWindow - 1; 493 if (selectedWindow < 0) 494 selectedWindow = windowCount - 1; 495 InvalidateRect(switchdialog, NULL, TRUE); 496 } 497 498 static void MoveRight(void) 499 { 500 selectedWindow = (selectedWindow + 1) % windowCount; 501 InvalidateRect(switchdialog, NULL, TRUE); 502 } 503 504 static void MoveUp(void) 505 { 506 INT iRow = selectedWindow / nCols; 507 INT iCol = selectedWindow % nCols; 508 509 --iRow; 510 if (iRow < 0) 511 iRow = nRows - 1; 512 513 selectedWindow = iRow * nCols + iCol; 514 if (selectedWindow >= windowCount) 515 selectedWindow = windowCount - 1; 516 InvalidateRect(switchdialog, NULL, TRUE); 517 } 518 519 static void MoveDown(void) 520 { 521 INT iRow = selectedWindow / nCols; 522 INT iCol = selectedWindow % nCols; 523 524 ++iRow; 525 if (iRow >= nRows) 526 iRow = 0; 527 528 selectedWindow = iRow * nCols + iCol; 529 if (selectedWindow >= windowCount) 530 selectedWindow = windowCount - 1; 531 InvalidateRect(switchdialog, NULL, TRUE); 532 } 533 534 VOID 535 DestroyAppWindows(VOID) 536 { 537 // for every item of the icon list: 538 INT i; 539 for (i = 0; i < windowCount; ++i) 540 { 541 // destroy the icon 542 DestroyIcon(iconList[i]); 543 iconList[i] = NULL; 544 } 545 } 546 547 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam ) 548 { 549 HWND hwndActive; 550 MSG msg; 551 552 // FIXME: Is loading timing OK? 553 LoadCoolSwitchSettings(); 554 555 if (!CoolSwitch) 556 return 0; 557 558 // Already in the loop. 559 if (switchdialog || Esc) return 0; 560 561 hwndActive = GetActiveWindow(); 562 // Nothing is active so exit. 563 if (!hwndActive) return 0; 564 565 if (lParam == VK_ESCAPE) 566 { 567 Esc = TRUE; 568 569 windowCount = 0; 570 EnumChildWindowsZOrder(NULL, EnumerateCallback, 0); 571 572 if (windowCount < 2) 573 return 0; 574 575 RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0); 576 577 hwndActive = GetActiveWindow(); 578 579 if (hwndActive == NULL) 580 { 581 Esc = FALSE; 582 return 0; 583 } 584 } 585 586 // Capture current active window. 587 SetCapture( hwndActive ); 588 589 switch (lParam) 590 { 591 case VK_TAB: 592 if( !CreateSwitcherWindow(User32Instance) ) goto Exit; 593 if( !GetDialogFont() ) goto Exit; 594 if( !ProcessHotKey() ) goto Exit; 595 break; 596 597 case VK_ESCAPE: 598 break; 599 600 default: 601 goto Exit; 602 } 603 // Main message loop: 604 while (1) 605 { 606 for (;;) 607 { 608 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) 609 { 610 if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break; 611 /* remove the message from the queue */ 612 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 613 } 614 else 615 WaitMessage(); 616 } 617 618 switch (msg.message) 619 { 620 case WM_KEYUP: 621 { 622 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 623 if (msg.wParam == VK_MENU) 624 { 625 CompleteSwitch(TRUE); 626 } 627 else if (msg.wParam == VK_RETURN) 628 { 629 CompleteSwitch(TRUE); 630 } 631 else if (msg.wParam == VK_ESCAPE) 632 { 633 TRACE("DoAppSwitch VK_ESCAPE 2\n"); 634 CompleteSwitch(FALSE); 635 } 636 goto Exit; //break; 637 } 638 639 case WM_SYSKEYDOWN: 640 { 641 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 642 if (HIWORD(msg.lParam) & KF_ALTDOWN) 643 { 644 if ( msg.wParam == VK_TAB ) 645 { 646 if (Esc) break; 647 if (GetKeyState(VK_SHIFT) < 0) 648 { 649 MoveLeft(); 650 } 651 else 652 { 653 MoveRight(); 654 } 655 } 656 else if ( msg.wParam == VK_ESCAPE ) 657 { 658 if (!Esc) break; 659 RotateTasks(GetKeyState(VK_SHIFT) < 0); 660 } 661 else if ( msg.wParam == VK_LEFT ) 662 { 663 MoveLeft(); 664 } 665 else if ( msg.wParam == VK_RIGHT ) 666 { 667 MoveRight(); 668 } 669 else if ( msg.wParam == VK_UP ) 670 { 671 MoveUp(); 672 } 673 else if ( msg.wParam == VK_DOWN ) 674 { 675 MoveDown(); 676 } 677 } 678 break; 679 } 680 681 case WM_LBUTTONUP: 682 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 683 ProcessMouseMessage(msg.message, msg.lParam); 684 goto Exit; 685 686 default: 687 if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE )) 688 { 689 TranslateMessage(&msg); 690 DispatchMessageW(&msg); 691 } 692 break; 693 } 694 } 695 Exit: 696 ReleaseCapture(); 697 if (switchdialog) DestroyWindow(switchdialog); 698 if (Esc) DestroyAppWindows(); 699 switchdialog = NULL; 700 selectedWindow = 0; 701 windowCount = 0; 702 Esc = FALSE; 703 return 0; 704 } 705 706 // 707 // Switch System Class Window Proc. 708 // 709 LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode ) 710 { 711 PWND pWnd; 712 PALTTABINFO ati; 713 pWnd = ValidateHwnd(hWnd); 714 if (pWnd) 715 { 716 if (!pWnd->fnid) 717 { 718 NtUserSetWindowFNID(hWnd, FNID_SWITCH); 719 } 720 } 721 722 switch (uMsg) 723 { 724 case WM_NCCREATE: 725 if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati)))) 726 return 0; 727 SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati ); 728 return TRUE; 729 730 case WM_SHOWWINDOW: 731 if (wParam) 732 { 733 PrepareWindow(); 734 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); 735 ati->cbSize = sizeof(ALTTABINFO); 736 ati->cItems = nItems; 737 ati->cColumns = nCols; 738 ati->cRows = nRows; 739 if (nCols) 740 { 741 ati->iColFocus = (selectedWindow - nShift) % nCols; 742 ati->iRowFocus = (selectedWindow - nShift) / nCols; 743 } 744 else 745 { 746 ati->iColFocus = 0; 747 ati->iRowFocus = 0; 748 } 749 ati->cxItem = CX_ITEM_SPACE; 750 ati->cyItem = CY_ITEM_SPACE; 751 ati->ptStart = ptStart; 752 } 753 return 0; 754 755 case WM_MOUSEMOVE: 756 ProcessMouseMessage(uMsg, lParam); 757 return 0; 758 759 case WM_ACTIVATE: 760 if (wParam == WA_INACTIVE) 761 { 762 CompleteSwitch(FALSE); 763 } 764 return 0; 765 766 case WM_PAINT: 767 OnPaint(hWnd); 768 return 0; 769 770 case WM_DESTROY: 771 isOpen = FALSE; 772 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); 773 HeapFree( GetProcessHeap(), 0, ati ); 774 SetWindowLongPtrW( hWnd, 0, 0 ); 775 DestroyAppWindows(); 776 NtUserSetWindowFNID(hWnd, FNID_DESTROY); 777 return 0; 778 } 779 return DefWindowProcW(hWnd, uMsg, wParam, lParam); 780 } 781 782 LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 783 { 784 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE); 785 } 786 787 LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 788 { 789 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE); 790 } 791