/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS msgina.dll * FILE: dll/win32/msgina/dimmedwindow.cpp * PURPOSE: Implementation of ShellDimScreen * PROGRAMMER: Mark Jansen */ #define COM_NO_WINDOWS_H #include "msgina.h" #include #include #include #include CComModule gModule; // Please note: The INIT_TIMER is a workaround because ReactOS does not redraw the desktop in time, // so the start menu is still visible on the dimmed screen. #define INIT_TIMER_ID 0x112233 #define FADE_TIMER_ID 0x12345 class CDimmedWindow : public CComObjectRootEx, IUnknown { private: HWND m_hwnd; HDC m_hdc; HBITMAP m_hbitmap; HGDIOBJ m_oldbitmap; LONG m_width; LONG m_height; BITMAPINFO m_bi; UCHAR* m_bytes; int m_step; static LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); public: CDimmedWindow() : m_hwnd(NULL) , m_hdc(NULL) , m_hbitmap(NULL) , m_oldbitmap(NULL) , m_width(0) , m_height(0) , m_bytes(NULL) , m_step(0) { WNDCLASSEXW wndclass = {sizeof(wndclass)}; wndclass.lpfnWndProc = WndProc; wndclass.hInstance = hDllInstance; wndclass.hCursor = LoadCursor(0, IDC_ARROW); wndclass.lpszClassName = L"DimmedWindowClass"; if (!RegisterClassExW(&wndclass)) return; m_width = GetSystemMetrics(SM_CXVIRTUALSCREEN); m_height = GetSystemMetrics(SM_CYVIRTUALSCREEN); memset(&m_bi, 0, sizeof(m_bi)); m_bi.bmiHeader.biSize = sizeof(m_bi); m_bi.bmiHeader.biWidth = m_width; m_bi.bmiHeader.biHeight = m_height; m_bi.bmiHeader.biPlanes = 1; m_bi.bmiHeader.biBitCount = 32; m_bi.bmiHeader.biCompression = BI_RGB; m_bi.bmiHeader.biSizeImage = m_width * 4 * m_height; m_bytes = new UCHAR[m_width * 4 * m_height]; LONG x = GetSystemMetrics(SM_XVIRTUALSCREEN); LONG y = GetSystemMetrics(SM_YVIRTUALSCREEN); m_hwnd = CreateWindowExW(WS_EX_TOPMOST, L"DimmedWindowClass", NULL, WS_POPUP, x, y, m_width, m_height, NULL, NULL, hDllInstance, (LPVOID)this); } ~CDimmedWindow() { if (m_hwnd) DestroyWindow(m_hwnd); UnregisterClassW(L"DimmedWindowClass", hDllInstance); if (m_oldbitmap) SelectObject(m_hdc, m_oldbitmap); if (m_hbitmap) DeleteObject(m_hbitmap); if (m_hdc) DeleteObject(m_hdc); if (m_bytes) delete[] m_bytes; } // This is needed so that we do not capture the start menu while it's closing. void WaitForInit() { MSG msg; while (IsWindow(m_hwnd) && !IsWindowVisible(m_hwnd)) { while (::PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE)) { ::TranslateMessage(&msg); ::DispatchMessage(&msg); if (IsWindowVisible(m_hwnd)) break; } } } void Init() { Capture(); ShowWindow(m_hwnd, SW_SHOW); SetForegroundWindow(m_hwnd); EnableWindow(m_hwnd, FALSE); SetTimer(m_hwnd, FADE_TIMER_ID, 200, NULL); } void Capture() { HWND desktopWnd = GetDesktopWindow(); HDC desktopDC = GetDC(desktopWnd); m_hdc = CreateCompatibleDC(desktopDC); m_hbitmap = CreateCompatibleBitmap(desktopDC, m_width, m_height); m_oldbitmap = SelectObject(m_hdc, m_hbitmap); BitBlt(m_hdc, 0, 0, m_width, m_height, desktopDC, 0, 0, SRCCOPY); ReleaseDC(desktopWnd, desktopDC); } bool Step() { // Stop after 10 steps if (m_step++ > 10 || !m_bytes) return false; int lines = GetDIBits(m_hdc, m_hbitmap, 0, m_height, m_bytes, &m_bi, DIB_RGB_COLORS); if (lines) { for (int xh = 0; xh < m_height; ++xh) { int h = m_width * 4 * xh; for (int w = 0; w < m_width; ++w) { UCHAR b = m_bytes[(h + w * 4) + 0]; UCHAR g = m_bytes[(h + w * 4) + 1]; UCHAR r = m_bytes[(h + w * 4) + 2]; // Standard formula to convert a color. int gray = (r * 30 + g * 59 + b * 11) / 100; if (gray < 0) gray = 0; // Do not fade too fast. r = (r*2 + gray) / 3; g = (g*2 + gray) / 3; b = (b*2 + gray) / 3; m_bytes[(h + w * 4) + 0] = b; m_bytes[(h + w * 4) + 1] = g; m_bytes[(h + w * 4) + 2] = r; } } SetDIBits(m_hdc, m_hbitmap, 0, lines, m_bytes, &m_bi, DIB_RGB_COLORS); } return true; } void Blt(HDC hdc) { BitBlt(hdc, 0, 0, m_width, m_height, m_hdc, 0, 0, SRCCOPY); } HWND Wnd() { return m_hwnd; } BEGIN_COM_MAP(CDimmedWindow) COM_INTERFACE_ENTRY_IID(IID_IUnknown, IUnknown) END_COM_MAP() }; LRESULT WINAPI CDimmedWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_NCCREATE: { LPCREATESTRUCT lpcs = reinterpret_cast(lParam); CDimmedWindow* info = static_cast(lpcs->lpCreateParams); SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)info); SetTimer(hWnd, INIT_TIMER_ID, 50, NULL); break; } case WM_PAINT: { CDimmedWindow* info = reinterpret_cast(GetWindowLongPtrW(hWnd, GWLP_USERDATA)); if (info) { PAINTSTRUCT ps; BeginPaint(hWnd, &ps); info->Blt(ps.hdc); EndPaint(hWnd, &ps); } return 0; } case WM_TIMER: { if (wParam == INIT_TIMER_ID) { CDimmedWindow* info = reinterpret_cast(GetWindowLongPtrW(hWnd, GWLP_USERDATA)); KillTimer(hWnd, INIT_TIMER_ID); info->Init(); } else if (wParam == FADE_TIMER_ID) { CDimmedWindow* info = reinterpret_cast(GetWindowLongPtrW(hWnd, GWLP_USERDATA)); if (info && info->Step()) InvalidateRect(hWnd, NULL, TRUE); else KillTimer(hWnd, FADE_TIMER_ID); } return 0; } default: break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } extern "C" HRESULT WINAPI ShellDimScreen(void** pUnknown, HWND* hWindow) { CComObject *pWindow; HRESULT hr = CComObject::CreateInstance(&pWindow); ULONG refcount; pWindow->WaitForInit(); if (!IsWindow(pWindow->Wnd())) { refcount = pWindow->AddRef(); while (refcount) refcount = pWindow->Release(); return E_FAIL; } _SEH2_TRY { hr = pWindow->QueryInterface(IID_IUnknown, pUnknown); *hWindow = pWindow->Wnd(); hr = S_OK; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { hr = E_INVALIDARG; refcount = pWindow->AddRef(); while (refcount) refcount = pWindow->Release(); } _SEH2_END return hr; }