diff --git a/base/shell/explorer/taskswnd.cpp b/base/shell/explorer/taskswnd.cpp index dcbc5cb7be5..a1db63fc11b 100644 --- a/base/shell/explorer/taskswnd.cpp +++ b/base/shell/explorer/taskswnd.cpp @@ -1602,7 +1602,7 @@ public: if (!bIsMinimized && bIsActive) { - ::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE); + ::ShowWindow(TaskItem->hWnd, SW_FORCEMINIMIZE); TRACE("Valid button clicked. App window Minimized.\n"); } else diff --git a/base/shell/explorer/traywnd.cpp b/base/shell/explorer/traywnd.cpp index 25de8857d30..aee0f74deed 100644 --- a/base/shell/explorer/traywnd.cpp +++ b/base/shell/explorer/traywnd.cpp @@ -3282,7 +3282,7 @@ HandleTrayContextMenu: { MINWNDPOS mwp = { hwnd, { sizeof(mwp.wndpl) } }; if (::GetWindowPlacement(hwnd, &mwp.wndpl) && // Save the position and status - ::ShowWindowAsync(hwnd, SW_SHOWMINNOACTIVE)) // Minimize + ::ShowWindowAsync(hwnd, SW_FORCEMINIMIZE)) // Minimize { info->pMinimizedAll->Add(mwp); } @@ -3323,7 +3323,10 @@ HandleTrayContextMenu: { HWND hwnd = g_MinimizedAll[i].hwnd; if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd)) + { + ::PostMessageW(::GetDesktopWindow(), WM_USER + 0, TRUE, (LPARAM)hwnd); ::SetWindowPlacement(hwnd, &g_MinimizedAll[i].wndpl); + } } g_MinimizedAll.RemoveAll(); diff --git a/win32ss/include/ghostwnd.h b/win32ss/include/ghostwnd.h index 6907580c79b..6e00c855231 100644 --- a/win32ss/include/ghostwnd.h +++ b/win32ss/include/ghostwnd.h @@ -4,17 +4,25 @@ * PURPOSE: Ghost window * COPYRIGHT: Copyright 2018 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ -#ifndef REACTOS_GHOST_WND_INCLUDED -#define REACTOS_GHOST_WND_INCLUDED +#pragma once #define GHOSTCLASSNAME L"Ghost" -#define GHOST_PROP L"GhostProp" -typedef struct GHOST_DATA +/// Desktop message WM_DESK_GHOSTING: +/// @param wParam BOOL bRestore. +/// @param lParam HWND hwndTarget. +#define WM_DESK_GHOSTING (WM_USER + 0) + +typedef struct tagGHOST { + LIST_ENTRY ListEntry; HWND hwndTarget; - HBITMAP hbm32bpp; + HWND hwndGhost; + HBITMAP hbmGhostImage; + RECT rc; + RECT rcWnd; + DWORD style; + DWORD exstyle; + LPWSTR pszText; BOOL bDestroyTarget; -} GHOST_DATA; - -#endif +} GHOST, *PGHOST; diff --git a/win32ss/user/ntuser/csr.c b/win32ss/user/ntuser/csr.c index 92b23c16dc3..5bdcfdf758b 100644 --- a/win32ss/user/ntuser/csr.c +++ b/win32ss/user/ntuser/csr.c @@ -219,8 +219,6 @@ DWORD UserSystemThreadProc(BOOL bRemoteProcess) Type = ST_RIT; else if (gdwPendingSystemThreads & ST_DESKTOP_THREAD) Type = ST_DESKTOP_THREAD; - else - Type = ST_GHOST_THREAD; ASSERT(Type); @@ -235,7 +233,6 @@ DWORD UserSystemThreadProc(BOOL bRemoteProcess) { case ST_RIT: RawInputThreadMain(); break; case ST_DESKTOP_THREAD: DesktopThreadMain(); break; - case ST_GHOST_THREAD: UserGhostThreadEntry(); break; default: ERR("Wrong type: %x\n", Type); } diff --git a/win32ss/user/ntuser/csr.h b/win32ss/user/ntuser/csr.h index 9629d6eb898..aa269449215 100644 --- a/win32ss/user/ntuser/csr.h +++ b/win32ss/user/ntuser/csr.h @@ -34,7 +34,6 @@ CsrClientCallServer(IN OUT PCSR_API_MESSAGE ApiMessage, #define ST_RIT (1<<0) #define ST_DESKTOP_THREAD (1<<1) -#define ST_GHOST_THREAD (1<<2) DWORD UserSystemThreadProc(BOOL bRemoteProcess); BOOL UserCreateSystemThread(DWORD Type); diff --git a/win32ss/user/ntuser/ghost.c b/win32ss/user/ntuser/ghost.c index d206df25fc6..e7686a691f3 100644 --- a/win32ss/user/ntuser/ghost.c +++ b/win32ss/user/ntuser/ghost.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS user32.dll * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Window ghosting feature - * COPYRIGHT: Copyright 2018 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + * COPYRIGHT: Copyright 2018-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ #include @@ -13,183 +13,61 @@ DBG_DEFAULT_CHANNEL(UserInput); -static UNICODE_STRING GhostClass = RTL_CONSTANT_STRING(GHOSTCLASSNAME); -static UNICODE_STRING GhostProp = RTL_CONSTANT_STRING(GHOST_PROP); - -PTHREADINFO gptiGhostThread = NULL; - -/* - * GhostThreadMain - * - * Creates ghost windows and exits when no non-responsive window remains. - */ -VOID NTAPI -UserGhostThreadEntry(VOID) +BOOL UserIsGhostWindow(PWND Window) { - TRACE("Ghost thread started\n"); - - UserEnterExclusive(); - - gptiGhostThread = PsGetCurrentThreadWin32Thread(); - - //TODO: Implement. This thread should handle all ghost windows and exit when no ghost window is needed. - - gptiGhostThread = NULL; - - UserLeave(); + return Window && (Window->fnid == FNID_GHOST); } -BOOL FASTCALL IntIsGhostWindow(PWND Window) +BOOL UserGhostFromGhostWindow(PGHOST pGhost, PWND pwndGhost) { - BOOLEAN Ret = FALSE; - UNICODE_STRING ClassName; - INT iCls, Len; - RTL_ATOM Atom = 0; + const GHOST *UserData; + BOOL ret = FALSE; - if (!Window) + if (!UserIsGhostWindow(pwndGhost)) return FALSE; - if (Window->fnid && !(Window->fnid & FNID_DESTROY)) + UserData = (const GHOST *)pwndGhost->dwUserData; + if (!UserData) { - if (LookupFnIdToiCls(Window->fnid, &iCls)) - { - Atom = gpsi->atomSysClass[iCls]; - } + ERR("!UserData\n"); + return FALSE; } - // check class name - RtlInitUnicodeString(&ClassName, NULL); - Len = UserGetClassName(Window->pcls, &ClassName, Atom, FALSE); - if (Len > 0) + _SEH2_TRY { - Ret = RtlEqualUnicodeString(&ClassName, &GhostClass, TRUE); + ProbeForRead(UserData, sizeof(GHOST), 1); + *pGhost = *UserData; + ret = TRUE; } - else + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { - DPRINT1("Unable to get class name\n"); + ERR("Exception!\n"); + RtlZeroMemory(pGhost, sizeof(*pGhost)); } - RtlFreeUnicodeString(&ClassName); + _SEH2_END; - return Ret; + return ret; } -HWND FASTCALL IntGhostWindowFromHungWindow(PWND pHungWnd) +PWND UserTargetWndFromGhostWindow(PWND pwndGhost) { - RTL_ATOM Atom; - HWND hwndGhost; - - if (!IntGetAtomFromStringOrAtom(&GhostProp, &Atom)) + GHOST Ghost; + if (!UserGhostFromGhostWindow(&Ghost, pwndGhost)) return NULL; - hwndGhost = UserGetProp(pHungWnd, Atom, TRUE); - if (hwndGhost) - { - if (ValidateHwndNoErr(hwndGhost)) - return hwndGhost; - - DPRINT("Not a window\n"); - } - - return NULL; -} - -HWND FASTCALL UserGhostWindowFromHungWindow(HWND hwndHung) -{ - PWND pHungWnd = ValidateHwndNoErr(hwndHung); - if (!pHungWnd) - { - DPRINT("Not a window\n"); - return NULL; - } - return IntGhostWindowFromHungWindow(pHungWnd); + return ValidateHwndNoErr(Ghost.hwndTarget); } -HWND FASTCALL IntHungWindowFromGhostWindow(PWND pGhostWnd) +BOOL UserPostGhostingOnDesktop(PWND pwndTarget, BOOL bRestore) { - const GHOST_DATA *UserData; - HWND hwndTarget; - - if (!IntIsGhostWindow(pGhostWnd)) - { - DPRINT("Not a ghost window\n"); - return NULL; - } - - UserData = (const GHOST_DATA *)pGhostWnd->dwUserData; - if (UserData) - { - _SEH2_TRY - { - ProbeForRead(UserData, sizeof(GHOST_DATA), 1); - hwndTarget = UserData->hwndTarget; - } - _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) - { - DPRINT1("Exception!\n"); - hwndTarget = NULL; - } - _SEH2_END; - } - else + if (!(pwndTarget->style & WS_VISIBLE) || + (pwndTarget->style & WS_CHILD) || + UserIsGhostWindow(pwndTarget) || + !MsqIsHung(pwndTarget->head.pti, 100)) { - DPRINT("No user data\n"); - hwndTarget = NULL; - } - - if (hwndTarget) - { - if (ValidateHwndNoErr(hwndTarget)) - return hwndTarget; - - DPRINT1("Not a window\n"); - } - - return NULL; -} - -HWND FASTCALL UserHungWindowFromGhostWindow(HWND hwndGhost) -{ - PWND pGhostWnd = ValidateHwndNoErr(hwndGhost); - return IntHungWindowFromGhostWindow(pGhostWnd); -} - -BOOL FASTCALL IntMakeHungWindowGhosted(HWND hwndHung) -{ - PWND pHungWnd = ValidateHwndNoErr(hwndHung); - if (!pHungWnd) - { - DPRINT1("Not a window\n"); - return FALSE; // not a window - } - - if (!MsqIsHung(pHungWnd->head.pti, MSQ_HUNG)) - { - DPRINT1("Not hung window\n"); - return FALSE; // not hung window - } - - if (!(pHungWnd->style & WS_VISIBLE)) - return FALSE; // invisible - - if (pHungWnd->style & WS_CHILD) - return FALSE; // child - - if (IntIsGhostWindow(pHungWnd)) - { - DPRINT1("IntIsGhostWindow\n"); - return FALSE; // ghost window cannot be ghosted - } - - if (IntGhostWindowFromHungWindow(pHungWnd)) - { - DPRINT("Already ghosting\n"); - return FALSE; // already ghosting + return FALSE; } - // TODO: Find a way to pass the hwnd of pHungWnd to the ghost thread as we can't pass parameters directly - - if (!gptiGhostThread) - UserCreateSystemThread(ST_GHOST_THREAD); - - return TRUE; + return UserPostMessage(IntGetDesktopWindow(), WM_DESK_GHOSTING, bRestore, + (LPARAM)UserHMGetHandle(pwndTarget)); } diff --git a/win32ss/user/ntuser/ghost.h b/win32ss/user/ntuser/ghost.h index 97466583351..f9317bd4dd2 100644 --- a/win32ss/user/ntuser/ghost.h +++ b/win32ss/user/ntuser/ghost.h @@ -1,3 +1,8 @@ -BOOL FASTCALL IntMakeHungWindowGhosted(HWND hwndHung); -VOID NTAPI UserGhostThreadEntry(VOID); +#pragma once +#include + +BOOL UserIsGhostWindow(PWND Window); +BOOL UserGhostFromGhostWindow(PGHOST pGhost, PWND pwndGhost); +PWND UserTargetWndFromGhostWindow(PWND pwndGhost); +BOOL UserPostGhostingOnDesktop(PWND pwndTarget, BOOL bRestore); diff --git a/win32ss/user/ntuser/keyboard.c b/win32ss/user/ntuser/keyboard.c index 3d8bed1ad25..e37def23e8b 100644 --- a/win32ss/user/ntuser/keyboard.c +++ b/win32ss/user/ntuser/keyboard.c @@ -1055,6 +1055,9 @@ ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD d } if (Wnd) pti = Wnd->head.pti; + if (Wnd && !UserIsGhostWindow(Wnd) && MsqIsHung(pti, 500)) + UserPostGhostingOnDesktop(Wnd, FALSE); + /* Init message */ Msg.hwnd = Wnd ? UserHMGetHandle(Wnd) : NULL; Msg.wParam = wFixedVk & 0xFF; /* Note: It's simplified by msg queue */ diff --git a/win32ss/user/ntuser/message.c b/win32ss/user/ntuser/message.c index ce9da97c1a5..430960b2f4b 100644 --- a/win32ss/user/ntuser/message.c +++ b/win32ss/user/ntuser/message.c @@ -1624,11 +1624,6 @@ co_IntSendMessageTimeoutSingle( HWND hWnd, if (Status == STATUS_TIMEOUT) { - if (0 && MsqIsHung(ptiSendTo, MSQ_HUNG)) - { - TRACE("Let's go Ghost!\n"); - IntMakeHungWindowGhosted(hWnd); - } /* * MSDN says: * Microsoft Windows 2000: If GetLastError returns zero, then the function diff --git a/win32ss/user/ntuser/msgqueue.c b/win32ss/user/ntuser/msgqueue.c index e26a3b65952..162c58738c0 100644 --- a/win32ss/user/ntuser/msgqueue.c +++ b/win32ss/user/ntuser/msgqueue.c @@ -637,6 +637,9 @@ co_MsqInsertMouseMessage(MSG* Msg, DWORD flags, ULONG_PTR dwExtraInfo, BOOL Hook if (pwnd) Msg->hwnd = UserHMGetHandle(pwnd); } + if (pwnd && !UserIsGhostWindow(pwnd) && MsqIsHung(pwnd->head.pti, 1000)) + UserPostGhostingOnDesktop(pwnd, FALSE); + hdcScreen = IntGetScreenDC(); CurInfo = IntGetSysCursorInfo(); diff --git a/win32ss/user/ntuser/simplecall.c b/win32ss/user/ntuser/simplecall.c index 05ba8d018ec..819e8273fd6 100644 --- a/win32ss/user/ntuser/simplecall.c +++ b/win32ss/user/ntuser/simplecall.c @@ -522,6 +522,11 @@ NtUserCallTwoParam( if (!Window) break; + if (!UserIsGhostWindow(Window) && MsqIsHung(Window->head.pti, 500)) + { + UserPostGhostingOnDesktop(Window, TRUE); + } + if (gpqForeground && !fAltTab) { PWND pwndActive = gpqForeground->spwndActive; diff --git a/win32ss/user/user32/controls/appswitch.c b/win32ss/user/user32/controls/appswitch.c index 6cff11c465f..3c5c83a2dd4 100644 --- a/win32ss/user/user32/controls/appswitch.c +++ b/win32ss/user/user32/controls/appswitch.c @@ -130,7 +130,7 @@ void CompleteSwitch(BOOL doSwitch) { HWND hwnd = windowList[selectedWindow]; - GetWindowTextW(hwnd, windowText, _countof(windowText)); + InternalGetWindowText(hwnd, windowText, _countof(windowText)); TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText); @@ -379,8 +379,8 @@ void OnPaint(HWND hWnd) DFCS_BUTTONPUSH | DFCS_PUSHED); // get text - CharCount = GetWindowTextW(windowList[selectedWindow], windowText, - _countof(windowText)); + CharCount = InternalGetWindowText(windowList[selectedWindow], windowText, + _countof(windowText)); // draw text dcFont = SelectObject(dialogDC, dialogFont); diff --git a/win32ss/user/user32/controls/ghost.c b/win32ss/user/user32/controls/ghost.c index dac759f3843..6c2d2816829 100644 --- a/win32ss/user/user32/controls/ghost.c +++ b/win32ss/user/user32/controls/ghost.c @@ -2,7 +2,7 @@ * PROJECT: ReactOS user32.dll * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Ghost window class - * COPYRIGHT: Copyright 2018 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + * COPYRIGHT: Copyright 2018-2024 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */ #include @@ -14,6 +14,120 @@ WINE_DEFAULT_DEBUG_CHANNEL(ghost); #define GHOST_TIMER_ID 0xFACEDEAD #define GHOST_INTERVAL 1000 // one second +/// "guGhostUnenchantMsg" message: +/// @param wParam BOOL bRestore. +/// @param lParam Zero. +UINT guGhostUnenchantMsg = 0; + +/// List of ghosts +LIST_ENTRY gGhostList = { &gGhostList, &gGhostList }; + +BOOL +IntIsGhostWindow(HWND hwnd) +{ + PWND pWnd = ValidateHwnd(hwnd); + return pWnd && (pWnd->fnid == FNID_GHOST); +} + +static VOID +IntAddGhost(PGHOST pGhost) +{ + InsertHeadList(&gGhostList, &pGhost->ListEntry); +} + +static VOID +IntRemoveGhost(PGHOST pGhost) +{ + RemoveEntryList(&pGhost->ListEntry); +} + +static void +Ghost_DestroyTarget(PGHOST pGhost) +{ + HWND hwndTarget = pGhost->hwndTarget; + DWORD pid; + HANDLE hProcess; + + pGhost->hwndTarget = NULL; + GetWindowThreadProcessId(hwndTarget, &pid); + + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (hProcess) + { + TerminateProcess(hProcess, -1); + CloseHandle(hProcess); + } + + DestroyWindow(hwndTarget); +} + +static VOID +IntDeleteGhost(PGHOST pGhost, BOOL bAbortGhost) +{ + if (IsWindow(pGhost->hwndTarget)) + { + DWORD exstyle = (DWORD)GetWindowLongPtrW(pGhost->hwndTarget, GWL_EXSTYLE); + SetWindowLongPtrW(pGhost->hwndTarget, GWL_EXSTYLE, (exstyle & ~0x800)); + } + + if (pGhost->hbmGhostImage) + { + DeleteObject(pGhost->hbmGhostImage); + pGhost->hbmGhostImage = NULL; + } + + if (IsWindow(pGhost->hwndTarget)) + { + if (pGhost->bDestroyTarget) + { + Ghost_DestroyTarget(pGhost); + } + else if (!bAbortGhost) + { + SetWindowPos(pGhost->hwndTarget, NULL, + pGhost->rcWnd.left, pGhost->rcWnd.top, + pGhost->rcWnd.right - pGhost->rcWnd.left, + pGhost->rcWnd.bottom - pGhost->rcWnd.top, + SWP_NOCOPYBITS | SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOZORDER); + } + } + + HeapFree(GetProcessHeap(), 0, pGhost->pszText); + HeapFree(GetProcessHeap(), 0, pGhost); +} + +PGHOST +IntGhostFromTargetWnd(HWND hwndTarget) +{ + PLIST_ENTRY pNode, pHead = &gGhostList; + PGHOST pGhost; + + for (pNode = pHead->Flink; pNode != pHead; pNode = pNode->Flink) + { + pGhost = CONTAINING_RECORD(pNode, GHOST, ListEntry); + if (pGhost->hwndTarget == hwndTarget) + return pGhost; + } + + return NULL; +} + +PGHOST +IntGhostFromGhostWnd(HWND hwndGhost) +{ + PLIST_ENTRY pNode, pHead = &gGhostList; + PGHOST pGhost; + + for (pNode = pHead->Flink; pNode != pHead; pNode = pNode->Flink) + { + pGhost = CONTAINING_RECORD(pNode, GHOST, ListEntry); + if (pGhost->hwndGhost == hwndGhost) + return pGhost; + } + + return NULL; +} + const struct builtin_class_descr GHOST_builtin_class = { L"Ghost", /* name */ @@ -41,22 +155,19 @@ IntCreate32BppBitmap(INT cx, INT cy) bi.bmiHeader.biBitCount = 32; hdc = CreateCompatibleDC(NULL); - if (hdc) - { - hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0); - DeleteDC(hdc); - } + hbm = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0); + DeleteDC(hdc); return hbm; } static HBITMAP -IntGetWindowBitmap(HWND hwnd, INT cx, INT cy) +IntGetClientImage(HWND hwnd, INT cx, INT cy) { HBITMAP hbm = NULL; HDC hdc, hdcMem; HGDIOBJ hbmOld; - hdc = GetWindowDC(hwnd); + hdc = GetDC(hwnd); if (!hdc) return NULL; @@ -69,10 +180,17 @@ IntGetWindowBitmap(HWND hwnd, INT cx, INT cy) { hbmOld = SelectObject(hdcMem, hbm); BitBlt(hdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY | CAPTUREBLT); + + // Draw 'X' + MoveToEx(hdcMem, 0, 0, NULL); + LineTo(hdcMem, cx, cy); + MoveToEx(hdcMem, cx, 0, NULL); + LineTo(hdcMem, 0, cy); + SelectObject(hdcMem, hbmOld); } - DeleteDC(hdcMem); + ReleaseDC(hwnd, hdcMem); earth: ReleaseDC(hwnd, hdc); @@ -84,351 +202,265 @@ static VOID IntMakeGhostImage(HBITMAP hbm) { BITMAP bm; - DWORD i, *pdw; - - GetObject(hbm, sizeof(bm), &bm); + LONG *pdw, x, y; - if (bm.bmBitsPixel != 32 || !bm.bmBits) - { - ERR("bm.bmBitsPixel == %d, bm.bmBits == %p\n", - bm.bmBitsPixel, bm.bmBits); + if (!GetObject(hbm, sizeof(bm), &bm)) return; - } + + ASSERT(bm.bmBitsPixel == 32); + ASSERT(bm.bmBits); pdw = bm.bmBits; - for (i = 0; i < bm.bmWidth * bm.bmHeight; ++i) + for (y = 0; y < bm.bmHeight; ++y) { - *pdw = *pdw | 0x00C0C0C0; // bitwise-OR with ARGB #C0C0C0 - ++pdw; + for (x = 0; x < bm.bmWidth; ++x, ++pdw) + { + if ((x + y) & 1) + *pdw |= 0xFFC0C0C0; // bitwise-OR with ARGB #FFC0C0C0 + } } } /****************************************************************************/ -static GHOST_DATA * +static PGHOST Ghost_GetData(HWND hwnd) { - return (GHOST_DATA *)GetWindowLongPtrW(hwnd, GWLP_USERDATA); + if (!IntIsGhostWindow(hwnd)) + return NULL; + return (PGHOST)GetWindowLongPtrW(hwnd, GWLP_USERDATA); } static HWND Ghost_GetTarget(HWND hwnd) { - GHOST_DATA *pData = Ghost_GetData(hwnd); - if (!pData) + PGHOST pGhost = Ghost_GetData(hwnd); + if (!pGhost) return NULL; - return pData->hwndTarget; + return pGhost->hwndTarget; } -static LPWSTR -Ghost_GetText(HWND hwndTarget, INT *pcchTextW, INT cchExtra) +static BOOL +Ghost_GetText(HWND hwndTarget, LPWSTR pszText, INT cchText) { - LPWSTR pszTextW = NULL, pszTextNewW; - INT cchNonExtra, cchTextW = *pcchTextW; - - pszTextNewW = HeapAlloc(GetProcessHeap(), 0, cchTextW * sizeof(WCHAR)); - for (;;) - { - if (!pszTextNewW) - { - ERR("HeapAlloc failed\n"); - if (pszTextW) - HeapFree(GetProcessHeap(), 0, pszTextW); - return NULL; - } - pszTextW = pszTextNewW; - - cchNonExtra = cchTextW - cchExtra; - if (InternalGetWindowText(hwndTarget, pszTextW, - cchNonExtra) < cchNonExtra - 1) - { - break; - } - - cchTextW *= 2; - pszTextNewW = HeapReAlloc(GetProcessHeap(), 0, pszTextW, - cchTextW * sizeof(WCHAR)); - } - - *pcchTextW = cchTextW; - return pszTextW; + // Normal GetWindowText won't work on hung windows + return InternalGetWindowText(hwndTarget, pszText, cchText); } -static BOOL -Ghost_OnCreate(HWND hwnd, CREATESTRUCTW *lpcs) +PGHOST +IntCreateGhostWindow(HWND hwndTarget, BOOL bRestore) { - HBITMAP hbm32bpp; - HWND hwndTarget, hwndPrev; - GHOST_DATA *pData; - RECT rc; + PGHOST pGhost; + PWND pwndTarget; + RECT rc, rcWnd; + HBITMAP hbmGhostImage; + WCHAR szText[128], szNotRespondingW[128]; + INT cchText, cbText; + LPWSTR pszText; DWORD style, exstyle; - WCHAR szTextW[320], szNotRespondingW[32]; - LPWSTR pszTextW; - INT cchTextW, cchExtraW, cchNonExtraW; - PWND pWnd = ValidateHwnd(hwnd); - if (pWnd) - { - if (!pWnd->fnid) - { - NtUserSetWindowFNID(hwnd, FNID_GHOST); - } - else if (pWnd->fnid != FNID_GHOST) - { - ERR("Wrong window class for Ghost! fnId 0x%x\n", pWnd->fnid); - return FALSE; - } - } - // get the target - hwndTarget = (HWND)lpcs->lpCreateParams; - if (!IsWindowVisible(hwndTarget) || // invisible? - (GetWindowLongPtrW(hwndTarget, GWL_STYLE) & WS_CHILD) || // child? - !IsHungAppWindow(hwndTarget)) // not hung? + style = (DWORD)GetWindowLongPtrW(hwndTarget, GWL_STYLE); + exstyle = (DWORD)GetWindowLongPtrW(hwndTarget, GWL_EXSTYLE); + + if (!IsWindowVisible(hwndTarget) || + (style & WS_CHILD) || + (!bRestore && IsIconic(hwndTarget)) || + IntIsGhostWindow(hwndTarget) || + (exstyle & 0x800) || + !IsHungAppWindow(hwndTarget)) { - return FALSE; + return NULL; } - // check prop - if (GetPropW(hwndTarget, GHOST_PROP)) + SetWindowLongPtrW(hwndTarget, GWL_EXSTYLE, (exstyle | 0x800)); + + if (!guGhostUnenchantMsg) + guGhostUnenchantMsg = RegisterWindowMessageW(L"guGhostUnenchantMsg"); + + // FIXME: Be aware of W32PF_NOWINDOWGHOSTING + pwndTarget = ValidateHwnd(hwndTarget); + if (!pwndTarget) return FALSE; - // set prop - SetPropW(hwndTarget, GHOST_PROP, hwnd); + pGhost = HeapAlloc(GetProcessHeap(), 0, sizeof(GHOST)); + if (!pGhost) + { + ERR("Out of memory\n"); + return NULL; + } - // create user data - pData = HeapAlloc(GetProcessHeap(), 0, sizeof(GHOST_DATA)); - if (!pData) + // Get rectangles + GetClientRect(hwndTarget, &rc); + if (bRestore) { - ERR("HeapAlloc failed\n"); - return FALSE; + WINDOWPLACEMENT wndpl = { sizeof(wndpl) }; + GetWindowPlacement(hwndTarget, &wndpl); + rcWnd = wndpl.rcNormalPosition; + } + else + { + GetWindowRect(hwndTarget, &rcWnd); } - // get window image - GetWindowRect(hwndTarget, &rc); - hbm32bpp = IntGetWindowBitmap(hwndTarget, - rc.right - rc.left, - rc.bottom - rc.top); - if (!hbm32bpp) + // Get client image + hbmGhostImage = IntGetClientImage(hwndTarget, rc.right - rc.left, rc.bottom - rc.top); + if (!hbmGhostImage) { ERR("hbm32bpp was NULL\n"); - HeapFree(GetProcessHeap(), 0, pData); + HeapFree(GetProcessHeap(), 0, pGhost); return FALSE; } - // make a ghost image - IntMakeGhostImage(hbm32bpp); - - // set user data - pData->hwndTarget = hwndTarget; - pData->hbm32bpp = hbm32bpp; - pData->bDestroyTarget = FALSE; - SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pData); - - // get style - style = GetWindowLongPtrW(hwndTarget, GWL_STYLE); - exstyle = GetWindowLongPtrW(hwndTarget, GWL_EXSTYLE); - - // get text - cchTextW = ARRAYSIZE(szTextW); - cchExtraW = ARRAYSIZE(szNotRespondingW); - cchNonExtraW = cchTextW - cchExtraW; - if (InternalGetWindowText(hwndTarget, szTextW, - cchNonExtraW) < cchNonExtraW - 1) + IntMakeGhostImage(hbmGhostImage); + + // Set text with " (Not Responding)" + Ghost_GetText(hwndTarget, szText, _countof(szText)); +#define CUTOFF_POS 20 + cchText = lstrlenW(szText); + if (cchText > CUTOFF_POS) + szText[CUTOFF_POS] = UNICODE_NULL; + LoadStringW(User32Instance, IDS_NOT_RESPONDING, szNotRespondingW, _countof(szNotRespondingW)); + StringCchCatW(szText, _countof(szText), L"..."); + StringCchCatW(szText, _countof(szText), szNotRespondingW); + + // Allocate pszText and store it + cchText = lstrlenW(szText) + 1; + cbText = cchText * sizeof(WCHAR); + pszText = HeapAlloc(GetProcessHeap(), 0, cbText); + if (!pszText) { - pszTextW = szTextW; + ERR("hbm32bpp was NULL\n"); + HeapFree(GetProcessHeap(), 0, pGhost); + DeleteObject(hbmGhostImage); + return FALSE; } - else + CopyMemory(pszText, szText, cbText); + + // Populate pGhost + pGhost->hwndTarget = hwndTarget; + pGhost->hbmGhostImage = hbmGhostImage; + pGhost->rc = rc; + pGhost->rcWnd = rcWnd; + pGhost->style = GetWindowLongPtrW(hwndTarget, GWL_STYLE); + pGhost->style &= ~(WS_HSCROLL | WS_VSCROLL | WS_VISIBLE); // Don't use scrollbars. + pGhost->exstyle = GetWindowLongPtrW(hwndTarget, GWL_EXSTYLE) | WS_EX_TOOLWINDOW; + pGhost->pszText = pszText; + + // Hide target + ShowWindow(hwndTarget, SW_FORCEMINIMIZE); + + // Create a ghost window + pGhost->hwndGhost = CreateWindowExW(pGhost->exstyle, GHOSTCLASSNAME, + pGhost->pszText, pGhost->style, + pGhost->rcWnd.left, pGhost->rcWnd.top, + pGhost->rcWnd.right - pGhost->rcWnd.left, + pGhost->rcWnd.bottom - pGhost->rcWnd.top, + NULL, NULL, GetModuleHandleW(NULL), pGhost); + if (!pGhost->hwndGhost) { - cchTextW *= 2; - pszTextW = Ghost_GetText(hwndTarget, &cchTextW, cchExtraW); - if (!pszTextW) - { - ERR("Ghost_GetText failed\n"); - DeleteObject(hbm32bpp); - HeapFree(GetProcessHeap(), 0, pData); - return FALSE; - } + IntDeleteGhost(pGhost, TRUE); + return NULL; } - // don't use scrollbars. - style &= ~(WS_HSCROLL | WS_VSCROLL | WS_VISIBLE); + // Show window + ShowWindow(pGhost->hwndGhost, (bRestore ? SW_RESTORE : SW_SHOWNOACTIVATE)); + SwitchToThisWindow(pGhost->hwndGhost, TRUE); - // set style - SetWindowLongPtrW(hwnd, GWL_STYLE, style); - SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exstyle); - - // set text with " (Not Responding)" - LoadStringW(User32Instance, IDS_NOT_RESPONDING, - szNotRespondingW, ARRAYSIZE(szNotRespondingW)); - StringCchCatW(pszTextW, cchTextW, szNotRespondingW); - SetWindowTextW(hwnd, pszTextW); - - // free the text buffer - if (szTextW != pszTextW) - HeapFree(GetProcessHeap(), 0, pszTextW); - - // get previous window of target - hwndPrev = GetWindow(hwndTarget, GW_HWNDPREV); - - // hide target - ShowWindowAsync(hwndTarget, SW_HIDE); - - // shrink the ghost to zero size and insert. - // this will avoid effects. - SetWindowPos(hwnd, hwndPrev, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | - SWP_DRAWFRAME); + return pGhost; +} - // resume the position and size of ghost - MoveWindow(hwnd, rc.left, rc.top, - rc.right - rc.left, rc.bottom - rc.top, TRUE); +static BOOL +Ghost_OnNCCreate(HWND hwnd, LPCREATESTRUCTW lpcs) +{ + PGHOST pGhost; + HWND hwndTarget; + PWND pWnd = ValidateHwnd(hwnd); - // make ghost visible - SetWindowLongPtrW(hwnd, GWL_STYLE, style | WS_VISIBLE); + if (pWnd) + { + if (!pWnd->fnid) + { + NtUserSetWindowFNID(hwnd, FNID_GHOST); + } + else if (pWnd->fnid != FNID_GHOST) + { + ERR("Wrong window class for Ghost! fnId 0x%x\n", pWnd->fnid); + return FALSE; + } + } - // redraw - InvalidateRect(hwnd, NULL, TRUE); + pGhost = (PGHOST)lpcs->lpCreateParams; + if (!pGhost) + return FALSE; - // start timer - SetTimer(hwnd, GHOST_TIMER_ID, GHOST_INTERVAL, NULL); + hwndTarget = pGhost->hwndTarget; + if (!IsWindowVisible(hwndTarget) || // invisible? + (GetWindowLongPtrW(hwndTarget, GWL_STYLE) & WS_CHILD) || // child? + !IsHungAppWindow(hwndTarget) || // not hung? + IntGhostFromTargetWnd(hwndTarget)) // already exist? + { + return FALSE; + } + SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)pGhost); + IntAddGhost(pGhost); return TRUE; } static void Ghost_Unenchant(HWND hwnd, BOOL bDestroyTarget) { - GHOST_DATA *pData = Ghost_GetData(hwnd); - if (!pData) + PGHOST pGhost = Ghost_GetData(hwnd); + if (!pGhost) return; - pData->bDestroyTarget |= bDestroyTarget; + pGhost->bDestroyTarget |= bDestroyTarget; DestroyWindow(hwnd); } static void Ghost_OnDraw(HWND hwnd, HDC hdc) { - BITMAP bm; + PGHOST pGhost = Ghost_GetData(hwnd); + HGDIOBJ hbmOld; HDC hdcMem; - GHOST_DATA *pData = Ghost_GetData(hwnd); - - if (!pData || !GetObject(pData->hbm32bpp, sizeof(bm), &bm)) - return; hdcMem = CreateCompatibleDC(hdc); - if (hdcMem) - { - HGDIOBJ hbmOld = SelectObject(hdcMem, pData->hbm32bpp); - BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, - hdcMem, 0, 0, SRCCOPY | CAPTUREBLT); - SelectObject(hdcMem, hbmOld); - DeleteDC(hdcMem); - } -} - -static void -Ghost_OnNCPaint(HWND hwnd, HRGN hrgn, BOOL bUnicode) -{ - HDC hdc; - - // do the default behaviour - if (bUnicode) - DefWindowProcW(hwnd, WM_NCPAINT, (WPARAM)hrgn, 0); - else - DefWindowProcA(hwnd, WM_NCPAINT, (WPARAM)hrgn, 0); - - // draw the ghost image - hdc = GetWindowDC(hwnd); - if (hdc) - { - Ghost_OnDraw(hwnd, hdc); - ReleaseDC(hwnd, hdc); - } + hbmOld = SelectObject(hdcMem, pGhost->hbmGhostImage); + BitBlt(hdc, 0, 0, pGhost->rc.right, pGhost->rc.bottom, hdcMem, 0, 0, SRCCOPY); + SelectObject(hdcMem, hbmOld); + DeleteDC(hdcMem); } static void -Ghost_OnPaint(HWND hwnd) +Ghost_OnMove(HWND hwnd) { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - if (hdc) - { - // don't draw at here - EndPaint(hwnd, &ps); - } + PGHOST pGhost = Ghost_GetData(hwnd); + GetWindowRect(hwnd, &pGhost->rcWnd); } static void -Ghost_OnMove(HWND hwnd, int x, int y) +Ghost_OnSize(HWND hwnd) { - RECT rc; - HWND hwndTarget = Ghost_GetTarget(hwnd); - - GetWindowRect(hwnd, &rc); - - // move the target - SetWindowPos(hwndTarget, NULL, rc.left, rc.top, 0, 0, - SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE | - SWP_NOSENDCHANGING); + PGHOST pGhost = Ghost_GetData(hwnd); + GetClientRect(hwnd, &pGhost->rc); + GetWindowRect(hwnd, &pGhost->rcWnd); } static void Ghost_OnDestroy(HWND hwnd) { - KillTimer(hwnd, GHOST_TIMER_ID); -} - -static void -Ghost_DestroyTarget(GHOST_DATA *pData) -{ - HWND hwndTarget = pData->hwndTarget; - DWORD pid; - HANDLE hProcess; - - pData->hwndTarget = NULL; - GetWindowThreadProcessId(hwndTarget, &pid); - - hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); - if (hProcess) - { - TerminateProcess(hProcess, -1); - CloseHandle(hProcess); - } - - DestroyWindow(hwndTarget); -} - -static void -Ghost_OnNCDestroy(HWND hwnd) -{ - // delete the user data - GHOST_DATA *pData = Ghost_GetData(hwnd); - if (pData) - { - SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); - - // delete image - DeleteObject(pData->hbm32bpp); - pData->hbm32bpp = NULL; - - // remove prop - RemovePropW(pData->hwndTarget, GHOST_PROP); + PGHOST pGhost = Ghost_GetData(hwnd); + if (!pGhost) + return; - // show target - ShowWindowAsync(pData->hwndTarget, SW_SHOWNOACTIVATE); + KillTimer(hwnd, GHOST_TIMER_ID); - // destroy target if necessary - if (pData->bDestroyTarget) - { - Ghost_DestroyTarget(pData); - } + SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); - HeapFree(GetProcessHeap(), 0, pData); - } + IntRemoveGhost(pGhost); + IntDeleteGhost(pGhost, FALSE); NtUserSetWindowFNID(hwnd, FNID_DESTROY); - - PostQuitMessage(0); } static void @@ -438,24 +470,20 @@ Ghost_OnClose(HWND hwnd) WCHAR szAskTerminate[128]; WCHAR szHungUpTitle[128]; - // stop timer + // Stop timer KillTimer(hwnd, GHOST_TIMER_ID); - LoadStringW(User32Instance, IDS_ASK_TERMINATE, - szAskTerminate, ARRAYSIZE(szAskTerminate)); - LoadStringW(User32Instance, IDS_HUNG_UP_TITLE, - szHungUpTitle, ARRAYSIZE(szHungUpTitle)); - - id = MessageBoxW(hwnd, szAskTerminate, szHungUpTitle, - MB_ICONINFORMATION | MB_YESNO); + LoadStringW(User32Instance, IDS_ASK_TERMINATE, szAskTerminate, _countof(szAskTerminate)); + LoadStringW(User32Instance, IDS_HUNG_UP_TITLE, szHungUpTitle, _countof(szHungUpTitle)); + id = MessageBoxW(hwnd, szAskTerminate, szHungUpTitle, MB_ICONINFORMATION | MB_YESNO); if (id == IDYES) { - // destroy the target + // Destroy the target Ghost_Unenchant(hwnd, TRUE); return; } - // restart timer + // Restart timer SetTimer(hwnd, GHOST_TIMER_ID, GHOST_INTERVAL, NULL); } @@ -463,15 +491,15 @@ static void Ghost_OnTimer(HWND hwnd, UINT id) { HWND hwndTarget; - GHOST_DATA *pData = Ghost_GetData(hwnd); + PGHOST pGhost = Ghost_GetData(hwnd); - if (id != GHOST_TIMER_ID || !pData) + if (id != GHOST_TIMER_ID || !pGhost) return; - // stop the timer + // Stop the timer KillTimer(hwnd, id); - hwndTarget = pData->hwndTarget; + hwndTarget = pGhost->hwndTarget; if (!IsWindow(hwndTarget) || !IsHungAppWindow(hwndTarget)) { // resume if window is destroyed or responding @@ -486,29 +514,13 @@ Ghost_OnTimer(HWND hwnd, UINT id) static HICON Ghost_GetIcon(HWND hwnd, INT fType) { - GHOST_DATA *pData = Ghost_GetData(hwnd); - HICON hIcon = NULL; - - if (!pData) - return NULL; - - // same as the original icon + HWND hwndTarget = Ghost_GetTarget(hwnd); switch (fType) { - case ICON_BIG: - { - hIcon = (HICON)GetClassLongPtrW(pData->hwndTarget, GCLP_HICON); - break; - } - - case ICON_SMALL: - { - hIcon = (HICON)GetClassLongPtrW(pData->hwndTarget, GCLP_HICONSM); - break; - } + case ICON_BIG: return (HICON)GetClassLongPtrW(hwndTarget, GCLP_HICON); + case ICON_SMALL: return (HICON)GetClassLongPtrW(hwndTarget, GCLP_HICONSM); + default: return NULL; } - - return hIcon; } LRESULT WINAPI @@ -517,46 +529,21 @@ GhostWndProc_common(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, { switch (uMsg) { - case WM_CREATE: - if (!Ghost_OnCreate(hwnd, (CREATESTRUCTW *)lParam)) - return -1; - break; - - case WM_NCPAINT: - Ghost_OnNCPaint(hwnd, (HRGN)wParam, unicode); - return 0; + case WM_NCCREATE: + return Ghost_OnNCCreate(hwnd, (LPCREATESTRUCTW)lParam); case WM_ERASEBKGND: Ghost_OnDraw(hwnd, (HDC)wParam); return TRUE; - case WM_PAINT: - Ghost_OnPaint(hwnd); - break; - case WM_MOVE: - Ghost_OnMove(hwnd, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); + Ghost_OnMove(hwnd); break; case WM_SIZE: + Ghost_OnSize(hwnd); break; - case WM_SIZING: - return TRUE; - - case WM_SYSCOMMAND: - switch ((UINT)wParam) - { - case SC_MAXIMIZE: - case SC_SIZE: - // sizing-related - return 0; - } - if (unicode) - return DefWindowProcW(hwnd, uMsg, wParam, lParam); - else - return DefWindowProcA(hwnd, uMsg, wParam, lParam); - case WM_CLOSE: Ghost_OnClose(hwnd); break; @@ -565,32 +552,21 @@ GhostWndProc_common(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, Ghost_OnTimer(hwnd, (UINT)wParam); break; - case WM_NCMOUSEMOVE: - if (unicode) - DefWindowProcW(hwnd, uMsg, wParam, lParam); - else - DefWindowProcA(hwnd, uMsg, wParam, lParam); - SetCursor(LoadCursor(NULL, IDC_WAIT)); - return 0; - case WM_GETICON: return (LRESULT)Ghost_GetIcon(hwnd, (INT)wParam); - case WM_COMMAND: - if (LOWORD(wParam) == 3333) - Ghost_Unenchant(hwnd, FALSE); - break; - case WM_DESTROY: Ghost_OnDestroy(hwnd); break; - case WM_NCDESTROY: - Ghost_OnNCDestroy(hwnd); - break; - default: { + if (uMsg == guGhostUnenchantMsg) + { + Ghost_Unenchant(hwnd, (BOOL)wParam); + break; + } + if (unicode) return DefWindowProcW(hwnd, uMsg, wParam, lParam); else diff --git a/win32ss/user/user32/include/user32p.h b/win32ss/user/user32/include/user32p.h index 06e02f8c158..97522cfa4b1 100644 --- a/win32ss/user/user32/include/user32p.h +++ b/win32ss/user/user32/include/user32p.h @@ -16,6 +16,7 @@ #include "regcontrol.h" #include "resource.h" #include "ntwrapper.h" +#include "ghostwnd.h" #define IMM_RETURN_VOID(retval) /* empty */ #define IMM_RETURN_NONVOID(retval) return (retval) @@ -132,5 +133,6 @@ HRESULT User32GetImmFileName(_Out_ LPWSTR lpBuffer, _In_ size_t cchBuffer); BOOL WINAPI UpdatePerUserImmEnabling(VOID); VOID APIENTRY CliImmInitializeHotKeys(DWORD dwAction, HKL hKL); VOID IntLoadPreloadKeyboardLayouts(VOID); +PGHOST IntCreateGhostWindow(HWND hwndTarget, BOOL bRestore); /* EOF */ diff --git a/win32ss/user/user32/misc/desktop.c b/win32ss/user/user32/misc/desktop.c index 862ff165034..aa93150dc12 100644 --- a/win32ss/user/user32/misc/desktop.c +++ b/win32ss/user/user32/misc/desktop.c @@ -9,6 +9,7 @@ */ #include +#include "ghostwnd.h" WINE_DEFAULT_DEBUG_CHANNEL(user32); @@ -66,6 +67,10 @@ DesktopWndProcW(HWND Wnd, case WM_SETCURSOR: return (LRESULT)SetCursor(LoadCursorW(0, (LPCWSTR)IDC_ARROW)); + case WM_DESK_GHOSTING: + IntCreateGhostWindow((HWND)lParam, (BOOL)wParam); + break; + default: return DefWindowProcW(Wnd, Msg, wParam, lParam); } diff --git a/win32ss/user/user32/user32.spec b/win32ss/user/user32/user32.spec index a5b4fd5b4d2..e88b3e66eb3 100644 --- a/win32ss/user/user32/user32.spec +++ b/win32ss/user/user32/user32.spec @@ -410,7 +410,7 @@ 403 stdcall InsertMenuItemA(long long long ptr) 404 stdcall InsertMenuItemW(long long long ptr) 405 stdcall InsertMenuW(long long long long ptr) -406 stdcall InternalGetWindowText(long long long) +406 stdcall InternalGetWindowText(ptr ptr long) 407 stdcall IntersectRect(ptr ptr ptr) 408 stdcall InvalidateRect(long ptr long) NtUserInvalidateRect 409 stdcall InvalidateRgn(long long long) NtUserInvalidateRgn