0a3abad6f0fef65b3b93d54893e973f2fbf0e181
[reactos.git] / reactos / dll / win32 / msgina / dimmedwindow.cpp
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 (!IsWindowVisible(m_hwnd) && IsWindow(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 }