Introduction
Sometimes it's difficult to tell an audience to look at a particular area of the screen. Whether it's for a product demo, presentation, screencast or a lecture. MouseLight tries to eliminate this problem.
This utility puts a "spotlight" and dims the surrounding area which enables your audience to effortlessly follow your path of movement on the screen.
Background
Our goal solves the problem that we need to know the 2 concepts given below:
LayeredWindow
- Windows APIs support a layered window made transparent for a dialog. We will create a dialog with size equal to desktop and use this technique to blur the surrounding area.
Region
- We can change the shape of the dialog based on Region
. With this technique, we can make a spotlight with different shapes such as Round, etc.
How To Make SpotLight
First, we will create a dialog with a size equal to desktop window. We must go to the resource mode of that dialog, set properties of dialog - border = none, so this dialog will be removed in the title bar. And we will blur this dialog by making transparent with specified percent rate. So we still see different windows although our dialog is topmost, as you see the code below (also see my 2 WndShadow h/cpp files):
HMODULE hSysDll = LoadLibrary(_T("USER32.DLL"));
s_UpdateLayeredWindow =
(pfnUpdateLayeredWindow)GetProcAddress(hSysDll,
"UpdateLayeredWindow");
if (NULL == s_UpdateLayeredWindow)
return false;
s_SetLayeredWindowAttributes =
(pfnSetLayeredWindowAttributes)GetProcAddress(hSysDll,
"SetLayeredWindowAttributes");
SetLastError(0);
SetWindowLong(hWnd,
GWL_EXSTYLE ,
GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
if (GetLastError())
return FALSE;
return s_SetLayeredWindowAttributes (hWnd,
RGB(255, 0, 255),
factor,
LWA_COLORKEY|LWA_ALPHA);
To make spotlight, we use the Region
technique. Create a memory DC/Bitmap with a size equal to desktop window and draw Round, etc. at some position and then convert BitmapToRegion (you can see my 2 Region cpp/hpp files) and set Region
to our dialog as below:
m_cVideoMemDC.FillSolidRect(m_rcVideoWindow, COLOR_WHITE);
m_cRoundVideoMemDC.SelectObject(&m_cBrushFocusWhite);
m_cRoundVideoMemDC.SelectObject(&m_cPenFocusWhite);
CRect cRectRound(0, 0, m_cCurrentFocusSize.cx, m_cCurrentFocusSize.cy);
m_cRoundVideoMemDC.FillSolidRect(cRectRound, COLOR_WHITE);
m_cRoundVideoMemDC.SelectObject(&m_cBrushFocusBlack);
cRectRound.DeflateRect(m_cCurrentFocusSize.cx/4, m_cCurrentFocusSize.cy/4);
m_cRoundVideoMemDC.RoundRect(cRectRound, cRectRound.CenterPoint());
m_cVideoMemDC.BitBlt(m_cCurrentFocus.left, m_cCurrentFocus.top,
m_cCurrentFocusSize.cx, m_cCurrentFocusSize.cy,
&m_cRoundVideoMemDC, 0, 0, SRCCOPY);
m_cBgMemDC.SelectObject(&m_cBrushBgWhite);
m_cBgMemDC.SelectObject(&m_cPenBgWhite);
m_cBgMemDC.Rectangle(m_rcVideoWindow);
m_cRoundBgMemDC.SelectObject(&m_cBrushBgWhite);
m_cRoundBgMemDC.SelectObject(&m_cPenBgWhite);
CRect cRectRound2(0, 0, m_cCurrentFocusSize.cx+SIZE_BORDER,
m_cCurrentFocusSize.cy+SIZE_BORDER);
m_cRoundBgMemDC.Rectangle(cRectRound2);
m_cRoundBgMemDC.SelectObject(&m_cBrushBgBlack);
cRectRound2.DeflateRect(cRectRound2.Width()/4, cRectRound2.Height()/4);
m_cRoundBgMemDC.RoundRect(cRectRound2, cRectRound2.CenterPoint());
m_cBgMemDC.BitBlt(m_cCurrentFocus.left-SIZE_BORDER/2,
m_cCurrentFocus.top-SIZE_BORDER/2,
m_cCurrentFocusSize.cx+SIZE_BORDER,
m_cCurrentFocusSize.cy+SIZE_BORDER,
&m_cRoundBgMemDC, 0, 0, SRCCOPY);
HRGN hRgn = BitmapToRegion(m_cVideoMemDC.m_hDC, 0, 0,
m_rcVideoWindow.Width(), m_rcVideoWindow.Height(),
RGB(0,0,0), RGB(20,20,20));
int iRes = this->SetWindowRgn(hRgn, FALSE);
DeleteObject(hRgn);
UpdateWindow();
Invalidate();
SpotLight will move to follow the mouse position changes. In my old version, I used Mouse event as left clickup/down and mousemove on Dialog to move SpotLight. Now, I wrote a DLL to monitor MouseMove
action (also see SetWindowHook
in MSDN).
InstallHook(this->m_hWnd, WM_MOUSEMOVE);
LRESULT CMouseLightDlg::OnMouseHook(WPARAM wParam, LPARAM lParam)
{
if (wParam == WM_MOUSEMOVE)
{
if (g_bSaved == FALSE)
{
GetCursorPos(&g_cSavedPoint);
g_bSaved = TRUE;
}
CPoint Point;
GetCursorPos(&Point);
if (Point != g_cSavedPoint)
{
if (this->IsWindowVisible())
{
_tprintf(_T("MouseProc\n"));
g_pMouseLight->UpdateSpotLight(Point);
}
g_cSavedPoint = Point;
}
}
return S_OK;
}
We can also draw an image background on that dialog and an image around SpotLight. So it is easy for an audience to look at a particular area of the screen. (Also see another article here.)
Points of Interest
Windows APIs support SetLayeredWindowAttributes
function to make a dialog transparent. So we can use this function to make our application "beautifully".
History
- Version 1.0.0.2
- Use the
MouseHook
technique to monitor MouseMove
action
- Version 1.0.0.3
- Allow to zoom in/out by Ctrl +
MouseMove
- Look better