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)
11 WINE_DEFAULT_DEBUG_CHANNEL(ghost
);
13 #define GHOST_TIMER_ID 0xFACEDEAD
14 #define GHOST_INTERVAL 1000 // one second
15 #define GHOST_PROP L"GhostProp"
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 /****************************************************************************/
108 typedef struct GHOST_DATA
116 Ghost_GetData(HWND hwnd
)
118 return (GHOST_DATA
*)GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
122 Ghost_GetTarget(HWND hwnd
)
124 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
127 return pData
->hwndTarget
;
131 Ghost_GetText(HWND hwndTarget
, INT
*pcchTextW
, INT cchExtra
)
133 LPWSTR pszTextW
= NULL
, pszTextNewW
;
134 INT cchNonExtra
, cchTextW
= *pcchTextW
;
136 pszTextNewW
= HeapAlloc(GetProcessHeap(), 0, cchTextW
* sizeof(WCHAR
));
141 ERR("HeapAlloc failed\n");
143 HeapFree(GetProcessHeap(), 0, pszTextW
);
146 pszTextW
= pszTextNewW
;
148 cchNonExtra
= cchTextW
- cchExtra
;
149 if (InternalGetWindowText(hwndTarget
, pszTextW
,
150 cchNonExtra
) < cchNonExtra
- 1)
156 pszTextNewW
= HeapReAlloc(GetProcessHeap(), 0, pszTextW
,
157 cchTextW
* sizeof(WCHAR
));
160 *pcchTextW
= cchTextW
;
165 Ghost_OnCreate(HWND hwnd
, CREATESTRUCTW
*lpcs
)
168 HWND hwndTarget
, hwndPrev
;
171 DWORD style
, exstyle
;
172 WCHAR szTextW
[320], szNotRespondingW
[32];
174 INT cchTextW
, cchExtraW
, cchNonExtraW
;
175 PWND pWnd
= ValidateHwnd(hwnd
);
180 NtUserSetWindowFNID(hwnd
, FNID_GHOST
);
182 else if (pWnd
->fnid
!= FNID_GHOST
)
184 ERR("Wrong window class for Ghost! fnId 0x%x\n", pWnd
->fnid
);
190 hwndTarget
= (HWND
)lpcs
->lpCreateParams
;
191 if (!IsWindowVisible(hwndTarget
) || // invisible?
192 GetParent(hwndTarget
) || // child?
193 !IsHungAppWindow(hwndTarget
)) // not hung?
199 if (GetPropW(hwndTarget
, GHOST_PROP
))
203 SetPropW(hwndTarget
, GHOST_PROP
, hwnd
);
206 pData
= HeapAlloc(GetProcessHeap(), 0, sizeof(GHOST_DATA
));
209 ERR("HeapAlloc failed\n");
214 GetWindowRect(hwndTarget
, &rc
);
215 hbm32bpp
= IntGetWindowBitmap(hwndTarget
,
220 ERR("hbm32bpp was NULL\n");
221 HeapFree(GetProcessHeap(), 0, pData
);
224 // make a ghost image
225 IntMakeGhostImage(hbm32bpp
);
228 pData
->hwndTarget
= hwndTarget
;
229 pData
->hbm32bpp
= hbm32bpp
;
230 pData
->bDestroyTarget
= FALSE
;
231 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, (LONG_PTR
)pData
);
234 style
= GetWindowLongPtrW(hwndTarget
, GWL_STYLE
);
235 exstyle
= GetWindowLongPtrW(hwndTarget
, GWL_EXSTYLE
);
238 cchTextW
= ARRAYSIZE(szTextW
);
239 cchExtraW
= ARRAYSIZE(szNotRespondingW
);
240 cchNonExtraW
= cchTextW
- cchExtraW
;
241 if (InternalGetWindowText(hwndTarget
, szTextW
,
242 cchNonExtraW
) < cchNonExtraW
- 1)
249 pszTextW
= Ghost_GetText(hwndTarget
, &cchTextW
, cchExtraW
);
252 ERR("Ghost_GetText failed\n");
253 DeleteObject(hbm32bpp
);
254 HeapFree(GetProcessHeap(), 0, pData
);
259 // don't use scrollbars.
260 style
&= ~(WS_HSCROLL
| WS_VSCROLL
| WS_VISIBLE
);
263 SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
);
264 SetWindowLongPtrW(hwnd
, GWL_EXSTYLE
, exstyle
);
266 // set text with " (Not Responding)"
267 LoadStringW(User32Instance
, IDS_NOT_RESPONDING
,
268 szNotRespondingW
, ARRAYSIZE(szNotRespondingW
));
269 StringCchCatW(pszTextW
, cchTextW
, szNotRespondingW
);
270 SetWindowTextW(hwnd
, pszTextW
);
272 // free the text buffer
273 if (szTextW
!= pszTextW
)
274 HeapFree(GetProcessHeap(), 0, pszTextW
);
276 // get previous window of target
277 hwndPrev
= GetWindow(hwndTarget
, GW_HWNDPREV
);
280 ShowWindowAsync(hwndTarget
, SW_HIDE
);
282 // shrink the ghost to zero size and insert.
283 // this will avoid effects.
284 SetWindowPos(hwnd
, hwndPrev
, 0, 0, 0, 0,
285 SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOOWNERZORDER
|
288 // resume the position and size of ghost
289 MoveWindow(hwnd
, rc
.left
, rc
.top
,
290 rc
.right
- rc
.left
, rc
.bottom
- rc
.top
, TRUE
);
292 // make ghost visible
293 SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
| WS_VISIBLE
);
296 InvalidateRect(hwnd
, NULL
, TRUE
);
299 SetTimer(hwnd
, GHOST_TIMER_ID
, GHOST_INTERVAL
, NULL
);
305 Ghost_Unenchant(HWND hwnd
, BOOL bDestroyTarget
)
307 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
311 pData
->bDestroyTarget
|= bDestroyTarget
;
316 Ghost_OnDraw(HWND hwnd
, HDC hdc
)
320 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
322 if (!pData
|| !GetObject(pData
->hbm32bpp
, sizeof(bm
), &bm
))
325 hdcMem
= CreateCompatibleDC(hdc
);
328 HGDIOBJ hbmOld
= SelectObject(hdcMem
, pData
->hbm32bpp
);
329 BitBlt(hdc
, 0, 0, bm
.bmWidth
, bm
.bmHeight
,
330 hdcMem
, 0, 0, SRCCOPY
| CAPTUREBLT
);
331 SelectObject(hdcMem
, hbmOld
);
337 Ghost_OnNCPaint(HWND hwnd
, HRGN hrgn
, BOOL bUnicode
)
341 // do the default behaivour
343 DefWindowProcW(hwnd
, WM_NCPAINT
, (WPARAM
)hrgn
, 0);
345 DefWindowProcA(hwnd
, WM_NCPAINT
, (WPARAM
)hrgn
, 0);
347 // draw the ghost image
348 hdc
= GetWindowDC(hwnd
);
351 Ghost_OnDraw(hwnd
, hdc
);
352 ReleaseDC(hwnd
, hdc
);
357 Ghost_OnPaint(HWND hwnd
)
360 HDC hdc
= BeginPaint(hwnd
, &ps
);
363 // don't draw at here
369 Ghost_OnMove(HWND hwnd
, int x
, int y
)
372 HWND hwndTarget
= Ghost_GetTarget(hwnd
);
374 GetWindowRect(hwnd
, &rc
);
377 SetWindowPos(hwndTarget
, NULL
, rc
.left
, rc
.top
, 0, 0,
378 SWP_NOSIZE
| SWP_NOOWNERZORDER
| SWP_NOACTIVATE
|
383 Ghost_OnDestroy(HWND hwnd
)
385 KillTimer(hwnd
, GHOST_TIMER_ID
);
389 Ghost_DestroyTarget(GHOST_DATA
*pData
)
391 HWND hwndTarget
= pData
->hwndTarget
;
395 pData
->hwndTarget
= NULL
;
396 GetWindowThreadProcessId(hwndTarget
, &pid
);
398 hProcess
= OpenProcess(PROCESS_TERMINATE
, FALSE
, pid
);
401 TerminateProcess(hProcess
, -1);
402 CloseHandle(hProcess
);
405 DestroyWindow(hwndTarget
);
409 Ghost_OnNCDestroy(HWND hwnd
)
411 // delete the user data
412 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
415 SetWindowLongPtrW(hwnd
, GWLP_USERDATA
, 0);
418 DeleteObject(pData
->hbm32bpp
);
419 pData
->hbm32bpp
= NULL
;
422 RemovePropW(pData
->hwndTarget
, GHOST_PROP
);
425 ShowWindowAsync(pData
->hwndTarget
, SW_SHOWNOACTIVATE
);
427 // destroy target if necessary
428 if (pData
->bDestroyTarget
)
430 Ghost_DestroyTarget(pData
);
433 HeapFree(GetProcessHeap(), 0, pData
);
436 NtUserSetWindowFNID(hwnd
, FNID_DESTROY
);
442 Ghost_OnClose(HWND hwnd
)
445 WCHAR szAskTerminate
[128];
446 WCHAR szHungUpTitle
[128];
449 KillTimer(hwnd
, GHOST_TIMER_ID
);
451 LoadStringW(User32Instance
, IDS_ASK_TERMINATE
,
452 szAskTerminate
, ARRAYSIZE(szAskTerminate
));
453 LoadStringW(User32Instance
, IDS_HUNG_UP_TITLE
,
454 szHungUpTitle
, ARRAYSIZE(szHungUpTitle
));
456 id
= MessageBoxW(hwnd
, szAskTerminate
, szHungUpTitle
,
457 MB_ICONINFORMATION
| MB_YESNO
);
460 // destroy the target
461 Ghost_Unenchant(hwnd
, TRUE
);
466 SetTimer(hwnd
, GHOST_TIMER_ID
, GHOST_INTERVAL
, NULL
);
470 Ghost_OnTimer(HWND hwnd
, UINT id
)
473 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
475 if (id
!= GHOST_TIMER_ID
|| !pData
)
481 hwndTarget
= pData
->hwndTarget
;
482 if (!IsWindow(hwndTarget
) || !IsHungAppWindow(hwndTarget
))
484 // resume if window is destroyed or responding
485 Ghost_Unenchant(hwnd
, FALSE
);
490 SetTimer(hwnd
, GHOST_TIMER_ID
, GHOST_INTERVAL
, NULL
);
494 Ghost_GetIcon(HWND hwnd
, INT fType
)
496 GHOST_DATA
*pData
= Ghost_GetData(hwnd
);
502 // same as the original icon
507 hIcon
= (HICON
)GetClassLongPtrW(pData
->hwndTarget
, GCLP_HICON
);
513 hIcon
= (HICON
)GetClassLongPtrW(pData
->hwndTarget
, GCLP_HICONSM
);
522 GhostWndProc_common(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
,
528 if (!Ghost_OnCreate(hwnd
, (CREATESTRUCTW
*)lParam
))
533 Ghost_OnNCPaint(hwnd
, (HRGN
)wParam
, unicode
);
537 Ghost_OnDraw(hwnd
, (HDC
)wParam
);
545 Ghost_OnMove(hwnd
, (SHORT
)LOWORD(lParam
), (SHORT
)HIWORD(lParam
));
555 switch ((UINT
)wParam
)
563 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
565 return DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
572 Ghost_OnTimer(hwnd
, (UINT
)wParam
);
577 DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
579 DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
580 SetCursor(LoadCursor(NULL
, IDC_WAIT
));
584 return (LRESULT
)Ghost_GetIcon(hwnd
, (INT
)wParam
);
587 if (LOWORD(wParam
) == 3333)
588 Ghost_Unenchant(hwnd
, FALSE
);
592 Ghost_OnDestroy(hwnd
);
596 Ghost_OnNCDestroy(hwnd
);
602 return DefWindowProcW(hwnd
, uMsg
, wParam
, lParam
);
604 return DefWindowProcA(hwnd
, uMsg
, wParam
, lParam
);
611 GhostWndProcA(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
613 return GhostWndProc_common(hwnd
, uMsg
, wParam
, lParam
, FALSE
);
617 GhostWndProcW(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
619 return GhostWndProc_common(hwnd
, uMsg
, wParam
, lParam
, TRUE
);