xref: /reactos/dll/win32/msgina/dimmedwindow.cpp (revision 4561998a)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS msgina.dll
4  * FILE:            dll/win32/msgina/dimmedwindow.cpp
5  * PURPOSE:         Implementation of ShellDimScreen
6  * PROGRAMMER:      Mark Jansen
7  */
8 
9 #define COM_NO_WINDOWS_H
10 #include "msgina.h"
11 #include <wingdi.h>
12 #include <atlbase.h>
13 #include <atlcom.h>
14 #include <pseh/pseh2.h>
15 
16 CComModule gModule;
17 
18 // Please note: The INIT_TIMER is a workaround because ReactOS does not redraw the desktop in time,
19 //              so the start menu is still visible on the dimmed screen.
20 #define INIT_TIMER_ID   0x112233
21 #define FADE_TIMER_ID   0x12345
22 
23 class CDimmedWindow :
24     public CComObjectRootEx<CComMultiThreadModelNoCS>,
25     IUnknown
26 {
27 private:
28     HWND m_hwnd;
29     HDC m_hdc;
30     HBITMAP m_hbitmap;
31     HGDIOBJ m_oldbitmap;
32     LONG m_width;
33     LONG m_height;
34     BITMAPINFO m_bi;
35     UCHAR* m_bytes;
36     int m_step;
37 
38     static LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
39 
40 public:
41     CDimmedWindow()
42         : m_hwnd(NULL)
43         , m_hdc(NULL)
44         , m_hbitmap(NULL)
45         , m_oldbitmap(NULL)
46         , m_width(0)
47         , m_height(0)
48         , m_bytes(NULL)
49         , m_step(0)
50     {
51         WNDCLASSEXW wndclass = {sizeof(wndclass)};
52         wndclass.lpfnWndProc = WndProc;
53         wndclass.hInstance = hDllInstance;
54         wndclass.hCursor = LoadCursor(0, IDC_ARROW);
55         wndclass.lpszClassName = L"DimmedWindowClass";
56 
57         if (!RegisterClassExW(&wndclass))
58             return;
59 
60         m_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
61         m_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
62 
63         memset(&m_bi, 0, sizeof(m_bi));
64         m_bi.bmiHeader.biSize = sizeof(m_bi);
65         m_bi.bmiHeader.biWidth = m_width;
66         m_bi.bmiHeader.biHeight = m_height;
67         m_bi.bmiHeader.biPlanes = 1;
68         m_bi.bmiHeader.biBitCount = 32;
69         m_bi.bmiHeader.biCompression = BI_RGB;
70         m_bi.bmiHeader.biSizeImage = m_width * 4 * m_height;
71         m_bytes = new UCHAR[m_width * 4 * m_height];
72 
73         LONG x = GetSystemMetrics(SM_XVIRTUALSCREEN);
74         LONG y = GetSystemMetrics(SM_YVIRTUALSCREEN);
75 
76         m_hwnd = CreateWindowExW(WS_EX_TOPMOST,
77                                  L"DimmedWindowClass",
78                                  NULL,
79                                  WS_POPUP,
80                                  x, y,
81                                  m_width, m_height,
82                                  NULL, NULL,
83                                  hDllInstance,
84                                  (LPVOID)this);
85     }
86 
87     ~CDimmedWindow()
88     {
89         if (m_hwnd)
90             DestroyWindow(m_hwnd);
91         UnregisterClassW(L"DimmedWindowClass", hDllInstance);
92         if (m_oldbitmap)
93              SelectObject(m_hdc, m_oldbitmap);
94         if (m_hbitmap)
95             DeleteObject(m_hbitmap);
96         if (m_hdc)
97             DeleteObject(m_hdc);
98         if (m_bytes)
99             delete[] m_bytes;
100     }
101 
102     // This is needed so that we do not capture the start menu while it's closing.
103     void WaitForInit()
104     {
105         MSG msg;
106 
107         while (IsWindow(m_hwnd) && !IsWindowVisible(m_hwnd))
108         {
109             while (::PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE))
110             {
111                 ::TranslateMessage(&msg);
112                 ::DispatchMessage(&msg);
113 
114                 if (IsWindowVisible(m_hwnd))
115                     break;
116             }
117         }
118     }
119 
120     void Init()
121     {
122         Capture();
123 
124         ShowWindow(m_hwnd, SW_SHOW);
125         SetForegroundWindow(m_hwnd);
126         EnableWindow(m_hwnd, FALSE);
127 
128         SetTimer(m_hwnd, FADE_TIMER_ID, 200, NULL);
129     }
130 
131     void Capture()
132     {
133         HWND desktopWnd = GetDesktopWindow();
134         HDC desktopDC = GetDC(desktopWnd);
135 
136         m_hdc = CreateCompatibleDC(desktopDC);
137 
138         m_hbitmap = CreateCompatibleBitmap(desktopDC, m_width, m_height);
139         m_oldbitmap = SelectObject(m_hdc, m_hbitmap);
140         BitBlt(m_hdc, 0, 0, m_width, m_height, desktopDC, 0, 0, SRCCOPY);
141 
142         ReleaseDC(desktopWnd, desktopDC);
143     }
144 
145     bool Step()
146     {
147         // Stop after 10 steps
148         if (m_step++ > 10 || !m_bytes)
149             return false;
150 
151         int lines = GetDIBits(m_hdc, m_hbitmap, 0, m_height, m_bytes, &m_bi, DIB_RGB_COLORS);
152         if (lines)
153         {
154             for (int xh = 0; xh < m_height; ++xh)
155             {
156                 int h = m_width * 4 * xh;
157                 for (int w = 0; w < m_width; ++w)
158                 {
159                     UCHAR b = m_bytes[(h + w * 4) + 0];
160                     UCHAR g = m_bytes[(h + w * 4) + 1];
161                     UCHAR r = m_bytes[(h + w * 4) + 2];
162 
163                     // Standard formula to convert a color.
164                     int gray = (r * 30 + g * 59 + b * 11) / 100;
165                     if (gray < 0)
166                         gray = 0;
167 
168                     // Do not fade too fast.
169                     r = (r*2 + gray) / 3;
170                     g = (g*2 + gray) / 3;
171                     b = (b*2 + gray) / 3;
172 
173                     m_bytes[(h + w * 4) + 0] = b;
174                     m_bytes[(h + w * 4) + 1] = g;
175                     m_bytes[(h + w * 4) + 2] = r;
176                 }
177             }
178             SetDIBits(m_hdc, m_hbitmap, 0, lines, m_bytes, &m_bi, DIB_RGB_COLORS);
179         }
180         return true;
181     }
182 
183     void Blt(HDC hdc)
184     {
185         BitBlt(hdc, 0, 0, m_width, m_height, m_hdc, 0, 0, SRCCOPY);
186     }
187 
188     HWND Wnd()
189     {
190         return m_hwnd;
191     }
192 
193 
194     BEGIN_COM_MAP(CDimmedWindow)
195         COM_INTERFACE_ENTRY_IID(IID_IUnknown, IUnknown)
196     END_COM_MAP()
197 
198 };
199 
200 
201 LRESULT WINAPI CDimmedWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
202 {
203     switch (uMsg)
204     {
205     case WM_NCCREATE:
206     {
207         LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
208         CDimmedWindow* info = static_cast<CDimmedWindow*>(lpcs->lpCreateParams);
209         SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)info);
210         SetTimer(hWnd, INIT_TIMER_ID, 50, NULL);
211         break;
212     }
213 
214     case WM_PAINT:
215     {
216         CDimmedWindow* info = reinterpret_cast<CDimmedWindow*>(GetWindowLongPtrW(hWnd, GWLP_USERDATA));
217         if (info)
218         {
219             PAINTSTRUCT ps;
220             BeginPaint(hWnd, &ps);
221             info->Blt(ps.hdc);
222             EndPaint(hWnd, &ps);
223         }
224         return 0;
225     }
226 
227     case WM_TIMER:
228     {
229         if (wParam == INIT_TIMER_ID)
230         {
231             CDimmedWindow* info = reinterpret_cast<CDimmedWindow*>(GetWindowLongPtrW(hWnd, GWLP_USERDATA));
232             KillTimer(hWnd, INIT_TIMER_ID);
233             info->Init();
234         }
235         else if (wParam == FADE_TIMER_ID)
236         {
237             CDimmedWindow* info = reinterpret_cast<CDimmedWindow*>(GetWindowLongPtrW(hWnd, GWLP_USERDATA));
238             if (info && info->Step())
239                 InvalidateRect(hWnd, NULL, TRUE);
240             else
241                 KillTimer(hWnd, FADE_TIMER_ID);
242         }
243         return 0;
244     }
245 
246     default:
247         break;
248     }
249 
250     return DefWindowProc(hWnd, uMsg, wParam, lParam);
251 }
252 
253 
254 extern "C"
255 HRESULT WINAPI
256 ShellDimScreen(void** pUnknown, HWND* hWindow)
257 {
258     CComObject<CDimmedWindow> *pWindow;
259     HRESULT hr = CComObject<CDimmedWindow>::CreateInstance(&pWindow);
260     ULONG refcount;
261 
262     pWindow->WaitForInit();
263 
264     if (!IsWindow(pWindow->Wnd()))
265     {
266         refcount = pWindow->AddRef();
267         while (refcount)
268             refcount = pWindow->Release();
269 
270         return E_FAIL;
271     }
272 
273     _SEH2_TRY
274     {
275         hr = pWindow->QueryInterface(IID_IUnknown, pUnknown);
276         *hWindow = pWindow->Wnd();
277         hr = S_OK;
278     }
279     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
280     {
281         hr = E_INVALIDARG;
282         refcount = pWindow->AddRef();
283         while (refcount)
284             refcount = pWindow->Release();
285     }
286     _SEH2_END
287 
288     return hr;
289 }
290