2 * PROJECT: ReactOS user32.dll
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Ghost window class
5 * COPYRIGHT: Copyright 2018 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
12 WINE_DEFAULT_DEBUG_CHANNEL(ghost
);
14 #define GHOST_TIMER_ID 0xFACEDEAD
15 #define GHOST_INTERVAL 1000 // one second
17 const struct builtin_class_descr GHOST_builtin_class
=
21 GhostWndProcA
, /* procA */
22 GhostWndProcW
, /* procW */
24 IDC_WAIT
, /* cursor */
29 IntCreate32BppBitmap(INT cx
, INT cy
)
36 ZeroMemory(&bi
, sizeof(bi
));
37 bi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
38 bi
.bmiHeader
.biWidth
= cx
;
39 bi
.bmiHeader
.biHeight
= cy
;
40 bi
.bmiHeader
.biPlanes
= 1;
41 bi
.bmiHeader
.biBitCount
= 32;
43 hdc
= CreateCompatibleDC(NULL
);
46 hbm
= CreateDIBSection(hdc
, &bi
, DIB_RGB_COLORS
, &pvBits
, NULL
, 0);
53 IntGetWindowBitmap(HWND hwnd
, INT cx
, INT cy
)
59 hdc
= GetWindowDC(hwnd
);
63 hdcMem
= CreateCompatibleDC(hdc
);
67 hbm
= IntCreate32BppBitmap(cx
, cy
);
70 hbmOld
= SelectObject(hdcMem
, hbm
);
71 BitBlt(hdcMem
, 0, 0, cx
, cy
, hdc
, 0, 0, SRCCOPY
| CAPTUREBLT
);
72 SelectObject(hdcMem
, hbmOld
);
84 IntMakeGhostImage(HBITMAP hbm
)
89 GetObject(hbm
, sizeof(bm
), &bm
);
91 if (bm
.bmBitsPixel
!= 32 || !bm
.bmBits
)
93 ERR("bm.bmBitsPixel == %d, bm.bmBits == %p\n",
94 bm
.bmBitsPixel
, bm
.bmBits
);
99 for (i
= 0; i
< bm
.bmWidth
* bm
.bmHeight
; ++i
)
101 *pdw
= *pdw
| 0x00C0C0C0; // bitwise-OR with ARGB #C0C0C0
106 /****************************************************************************/
109 Ghost_GetData(HWND hwnd
)
111 return (GHOST_DATA
*)GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
115 Ghost_GetTarget(HWND hwnd
)
117 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
120 return pData
->hwndTarget
;
124 Ghost_GetText(HWND hwndTarget
, INT
*pcchTextW
, INT cchExtra
)
126 LPWSTR pszTextW
= NULL
, pszTextNewW
;
127 INT cchNonExtra
, cchTextW
= *pcchTextW
;
129 pszTextNewW
= HeapAlloc(GetProcessHeap(), 0, cchTextW
* sizeof(WCHAR
));
134 ERR("HeapAlloc failed\n");
136 HeapFree(GetProcessHeap(), 0, pszTextW
);
139 pszTextW
= pszTextNewW
;
141 cchNonExtra
= cchTextW
- cchExtra
;
142 if (InternalGetWindowText(hwndTarget
, pszTextW
,
143 cchNonExtra
) < cchNonExtra
- 1)
149 pszTextNewW
= HeapReAlloc(GetProcessHeap(), 0, pszTextW
,
150 cchTextW
* sizeof(WCHAR
));
153 *pcchTextW
= cchTextW
;
158 Ghost_OnCreate(HWND hwnd
, CREATESTRUCTW
*lpcs
)
161 HWND hwndTarget
, hwndPrev
;
164 DWORD style
, exstyle
;
165 WCHAR szTextW
[320], szNotRespondingW
[32];
167 INT cchTextW
, cchExtraW
, cchNonExtraW
;
168 PWND pWnd
= ValidateHwnd(hwnd
);
173 NtUserSetWindowFNID(hwnd
, FNID_GHOST
);
175 else if (pWnd
->fnid
!= FNID_GHOST
)
177 ERR("Wrong window class for Ghost! fnId 0x%x\n", pWnd
->fnid
);
183 hwndTarget
= (HWND
)lpcs
->lpCreateParams
;
184 if (!IsWindowVisible(hwndTarget
) || // invisible?
185 (GetWindowLongPtrW(hwndTarget
, GWL_STYLE
) & WS_CHILD
) || // child?
186 !IsHungAppWindow(hwndTarget
)) // not hung?
192 if (GetPropW(hwndTarget
, GHOST_PROP
))
196 SetPropW(hwndTarget
, GHOST_PROP
, hwnd
);
199 pData
= HeapAlloc(GetProcessHeap(), 0, sizeof(GHOST_DATA
));
202 ERR("HeapAlloc failed\n");
207 GetWindowRect(hwndTarget
, &rc
);
208 hbm32bpp
= IntGetWindowBitmap(hwndTarget
,
213 ERR("hbm32bpp was NULL\n");
214 HeapFree(GetProcessHeap(), 0, pData
);
217 // make a ghost image
218 IntMakeGhostImage(hbm32bpp
);
221 pData
->hwndTarget
= hwndTarget
;
222 pData
->hbm32bpp
= hbm32bpp
;
223 pData
->bDestroyTarget
= FALSE
;
224 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LONG_PTR
)pData
);
227 style
= GetWindowLongPtrW(hwndTarget
, GWL_STYLE
);
228 exstyle
= GetWindowLongPtrW(hwndTarget
, GWL_EXSTYLE
);
231 cchTextW
= ARRAYSIZE(szTextW
);
232 cchExtraW
= ARRAYSIZE(szNotRespondingW
);
233 cchNonExtraW
= cchTextW
- cchExtraW
;
234 if (InternalGetWindowText(hwndTarget
, szTextW
,
235 cchNonExtraW
) < cchNonExtraW
- 1)
242 pszTextW
= Ghost_GetText(hwndTarget
, &cchTextW
, cchExtraW
);
245 ERR("Ghost_GetText failed\n");
246 DeleteObject(hbm32bpp
);
247 HeapFree(GetProcessHeap(), 0, pData
);
252 // don't use scrollbars.
253 style
&= ~(WS_HSCROLL
| WS_VSCROLL
| WS_VISIBLE
);
256 SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
);
257 SetWindowLongPtrW(hwnd
, GWL_EXSTYLE
, exstyle
);
259 // set text with " (Not Responding)"
260 LoadStringW(User32Instance
, IDS_NOT_RESPONDING
,
261 szNotRespondingW
, ARRAYSIZE(szNotRespondingW
));
262 StringCchCatW(pszTextW
, cchTextW
, szNotRespondingW
);
263 SetWindowTextW(hwnd
, pszTextW
);
265 // free the text buffer
266 if (szTextW
!= pszTextW
)
267 HeapFree(GetProcessHeap(), 0, pszTextW
);
269 // get previous window of target
270 hwndPrev
= GetWindow(hwndTarget
, GW_HWNDPREV
);
273 ShowWindowAsync(hwndTarget
, SW_HIDE
);
275 // shrink the ghost to zero size and insert.
276 // this will avoid effects.
277 SetWindowPos(hwnd
, hwndPrev
, 0, 0, 0, 0,
278 SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOOWNERZORDER
|
281 // resume the position and size of ghost
282 MoveWindow(hwnd
, rc
.left
, rc
.top
,
283 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
, TRUE
);
285 // make ghost visible
286 SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
| WS_VISIBLE
);
289 InvalidateRect(hwnd
, NULL
, TRUE
);
292 SetTimer(hwnd
, GHOST_TIMER_ID
, GHOST_INTERVAL
, NULL
);
298 Ghost_Unenchant(HWND hwnd
, BOOL bDestroyTarget
)
300 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
304 pData
->bDestroyTarget
|= bDestroyTarget
;
309 Ghost_OnDraw(HWND hwnd
, HDC hdc
)
313 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
315 if (!pData
|| !GetObject(pData
->hbm32bpp
, sizeof(bm
), &bm
))
318 hdcMem
= CreateCompatibleDC(hdc
);
321 HGDIOBJ hbmOld
= SelectObject(hdcMem
, pData
->hbm32bpp
);
322 BitBlt(hdc
, 0, 0, bm
.bmWidth
, bm
.bmHeight
,
323 hdcMem
, 0, 0, SRCCOPY
| CAPTUREBLT
);
324 SelectObject(hdcMem
, hbmOld
);
330 Ghost_OnNCPaint(HWND hwnd
, HRGN hrgn
, BOOL bUnicode
)
334 // do the default behaivour
336 DefWindowProcW(hwnd
, WM_NCPAINT
, (WPARAM
)hrgn
, 0);
338 DefWindowProcA(hwnd
, WM_NCPAINT
, (WPARAM
)hrgn
, 0);
340 // draw the ghost image
341 hdc
= GetWindowDC(hwnd
);
344 Ghost_OnDraw(hwnd
, hdc
);
345 ReleaseDC(hwnd
, hdc
);
350 Ghost_OnPaint(HWND hwnd
)
353 HDC hdc
= BeginPaint(hwnd
, &ps
);
356 // don't draw at here
362 Ghost_OnMove(HWND hwnd
, int x
, int y
)
365 HWND hwndTarget
= Ghost_GetTarget(hwnd
);
367 GetWindowRect(hwnd
, &rc
);
370 SetWindowPos(hwndTarget
, NULL
, rc
.left
, rc
.top
, 0, 0,
371 SWP_NOSIZE
| SWP_NOOWNERZORDER
| SWP_NOACTIVATE
|
376 Ghost_OnDestroy(HWND hwnd
)
378 KillTimer(hwnd
, GHOST_TIMER_ID
);
382 Ghost_DestroyTarget(GHOST_DATA
*pData
)
384 HWND hwndTarget
= pData
->hwndTarget
;
388 pData
->hwndTarget
= NULL
;
389 GetWindowThreadProcessId(hwndTarget
, &pid
);
391 hProcess
= OpenProcess(PROCESS_TERMINATE
, FALSE
, pid
);
394 TerminateProcess(hProcess
, -1);
395 CloseHandle(hProcess
);
398 DestroyWindow(hwndTarget
);
402 Ghost_OnNCDestroy(HWND hwnd
)
404 // delete the user data
405 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
408 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, 0);
411 DeleteObject(pData
->hbm32bpp
);
412 pData
->hbm32bpp
= NULL
;
415 RemovePropW(pData
->hwndTarget
, GHOST_PROP
);
418 ShowWindowAsync(pData
->hwndTarget
, SW_SHOWNOACTIVATE
);
420 // destroy target if necessary
421 if (pData
->bDestroyTarget
)
423 Ghost_DestroyTarget(pData
);
426 HeapFree(GetProcessHeap(), 0, pData
);
429 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
435 Ghost_OnClose(HWND hwnd
)
438 WCHAR szAskTerminate
[128];
439 WCHAR szHungUpTitle
[128];
442 KillTimer(hwnd
, GHOST_TIMER_ID
);
444 LoadStringW(User32Instance
, IDS_ASK_TERMINATE
,
445 szAskTerminate
, ARRAYSIZE(szAskTerminate
));
446 LoadStringW(User32Instance
, IDS_HUNG_UP_TITLE
,
447 szHungUpTitle
, ARRAYSIZE(szHungUpTitle
));
449 id
= MessageBoxW(hwnd
, szAskTerminate
, szHungUpTitle
,
450 MB_ICONINFORMATION
| MB_YESNO
);
453 // destroy the target
454 Ghost_Unenchant(hwnd
, TRUE
);
459 SetTimer(hwnd
, GHOST_TIMER_ID
, GHOST_INTERVAL
, NULL
);
463 Ghost_OnTimer(HWND hwnd
, UINT id
)
466 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
468 if (id
!= GHOST_TIMER_ID
|| !pData
)
474 hwndTarget
= pData
->hwndTarget
;
475 if (!IsWindow(hwndTarget
) || !IsHungAppWindow(hwndTarget
))
477 // resume if window is destroyed or responding
478 Ghost_Unenchant(hwnd
, FALSE
);
483 SetTimer(hwnd
, GHOST_TIMER_ID
, GHOST_INTERVAL
, NULL
);
487 Ghost_GetIcon(HWND hwnd
, INT fType
)
489 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
495 // same as the original icon
500 hIcon
= (HICON
)GetClassLongPtrW(pData
->hwndTarget
, GCLP_HICON
);
506 hIcon
= (HICON
)GetClassLongPtrW(pData
->hwndTarget
, GCLP_HICONSM
);
515 GhostWndProc_common(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
,
521 if (!Ghost_OnCreate(hwnd
, (CREATESTRUCTW
*)lParam
))
526 Ghost_OnNCPaint(hwnd
, (HRGN
)wParam
, unicode
);
530 Ghost_OnDraw(hwnd
, (HDC
)wParam
);
538 Ghost_OnMove(hwnd
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
548 switch ((UINT
)wParam
)
556 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
558 return DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
565 Ghost_OnTimer(hwnd
, (UINT
)wParam
);
570 DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
572 DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
573 SetCursor(LoadCursor(NULL
, IDC_WAIT
));
577 return (LRESULT
)Ghost_GetIcon(hwnd
, (INT
)wParam
);
580 if (LOWORD(wParam
) == 3333)
581 Ghost_Unenchant(hwnd
, FALSE
);
585 Ghost_OnDestroy(hwnd
);
589 Ghost_OnNCDestroy(hwnd
);
595 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
597 return DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
604 GhostWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
606 return GhostWndProc_common(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
610 GhostWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
612 return GhostWndProc_common(hwnd
, uMsg
, wParam
, lParam
, TRUE
);