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