-
Selecting an area
I'm making a capture screenshot program and I'm down to allowing the user to select any part of the screen to screenshot it.
The problem I have is how to efficiently display the selection tool
What I'm aiming for is a lookalike of when you left-click mouse and drag it on the desktop, or as when you are browsing a folder and you are selecting multiple files with only your mouse by leftclicking with mouse and dragging.
I've tried to do as much as I can but now in stuck, the rectangle which shows what you are about to select flickers and remains partcially on the screen sometimes...
I'm using global hooks for this to intercept mouseclicks and keyboard messages.
I've basically intercepted the starting position of the mouse and stored it, then evertime the mouse moves an end position is gotten and then a rectangle of the bounds are drawn.
All the drawings are made on an transpartent window and then evertime the mouse pos updates the old rectangle is removed...
Any hints are highly appreciated since I'm all out of ideas and really don't know what to test anymore, been trying to fix this for a say 10 hours now
Some of the code that I've used:
Code:
/* */
/* Area Section */
/* */
#define AREA_ABORT 1
#define AREA_CLICK 2
#define AREA_POSMOVED 3
HHOOK AreaMouseHook;
HHOOK AreaKBHook;
long __stdcall AreaCaptureProc(unsigned int _message, long _lParam)
{
static HWND hWnd = FindWindow("XimaSSS", "");
static Win::Window ghostWindow;
static clock_t endWait;
static const int borderSize = 5;
static bool sizing(false);
static POINT startPos;
static POINT endPos;
switch(_message)
{
case AREA_ABORT:
{
sizing = false;
RECT rect;
{
rect.left = startPos.x;
rect.top = startPos.y;
rect.right = endPos.x;
rect.bottom = endPos.y;
RedrawWindow(ghostWindow, &rect, 0, RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_INVALIDATE);
}
startPos.x = startPos.y = 0;
endPos.x = endPos.y = 0;
if(ghostWindow.isWindow())
ghostWindow.destroy();
PostMessage(hWnd, Area_Abort, 0, 0);
break;
}
case AREA_CLICK:
{
if(sizing)
{
sizing = false;
RECT rect;
{
rect.left = startPos.x;
rect.top = startPos.y;
rect.right = endPos.x;
rect.bottom = endPos.y;
RedrawWindow(ghostWindow, &rect, 0, RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_INVALIDATE);
}
startPos.x = startPos.y = 0;
endPos.x = endPos.y = 0;
if(ghostWindow.isWindow())
ghostWindow.destroy();
}
else
{
if(!GetCursorPos(&startPos))
return AreaCaptureProc(AREA_ABORT, 0);
sizing = true;
{
Win::Class _class(0, GetModuleHandle(0), 0, 0, "Dummy", 0, 0);
if(!_class.reg())
return false;
}
ghostWindow.create(0, WS_VISIBLE | WS_CHILD, "Dummy", "", 0, 0, 1280, 1024, 0, GetDesktopWindow());
}
break;
}
case AREA_POSMOVED:
{
if(!sizing)
return 1;
RECT rect;
{
rect.left = startPos.x;
rect.top = startPos.y;
rect.right = endPos.x;
rect.bottom = endPos.y;
RedrawWindow(ghostWindow, &rect, 0, RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_INVALIDATE);
}
GetCursorPos(&endPos);
{
HDC hMarkerDC = GetDC(ghostWindow);
HBRUSH hBorder = CreateSolidBrush(RGB(255, 0, 0));
// tl->tr
rect.left = startPos.x;
rect.top = startPos.y;
rect.right = endPos.x;
rect.bottom = startPos.y > endPos.y ? startPos.y - borderSize : startPos.y + borderSize;
FillRect(hMarkerDC, &rect, hBorder);
// tl->bl
rect.left = startPos.x;
rect.top = startPos.y;
rect.right = startPos.x > endPos.x ? startPos.x - borderSize : startPos.x + borderSize;
rect.bottom = endPos.y;
FillRect(hMarkerDC, &rect, hBorder);
// br->bl
rect.left = startPos.x;
rect.top = endPos.y > startPos.y ? endPos.y - borderSize : endPos.y + borderSize;
rect.right = endPos.x;
rect.bottom = endPos.y;
FillRect(hMarkerDC, &rect, hBorder);
// br->tr
rect.left = endPos.x > startPos.x ? endPos.x - borderSize : endPos.x + borderSize;
rect.top = startPos.y;
rect.right = endPos.x;
rect.bottom = endPos.y;
FillRect(hMarkerDC, &rect, hBorder);
DeleteObject(hBorder);
ReleaseDC(ghostWindow, hMarkerDC);
}
break;
}
}
return 1;
}
LRESULT __stdcall AreaMouseHookProc(int nCode, unsigned int _wParam, long _lParam)
{
if(nCode != HC_ACTION)
return CallNextHookEx(AreaMouseHook, nCode, _wParam, _lParam);
switch(_wParam)
{
case WM_RBUTTONUP:
return AreaCaptureProc(AREA_ABORT, 0);
break;
case WM_LBUTTONUP:
return AreaCaptureProc(AREA_CLICK, 0);
break;
case WM_LBUTTONDOWN:
return AreaCaptureProc(AREA_CLICK, 0);
break;
case WM_MOUSEMOVE:
AreaCaptureProc(AREA_POSMOVED, 0);
return 0;
break;
default:
return CallNextHookEx(AreaMouseHook, nCode, _wParam, _lParam);
}
return 1;
}
LRESULT __stdcall AreaKBHookProc(int nCode, unsigned int _wParam, long _lParam)
{
if(nCode != HC_ACTION)
return CallNextHookEx(AreaKBHook, nCode, _wParam, _lParam);
switch(_wParam)
{
case WM_KEYDOWN:
switch(reinterpret_cast<kbstruct*>(_lParam)->vkCode)
{
case VK_SPACE:
//PostMessage(hWnd, CW_WindowSelected, 0, 0);
break;
case VK_ESCAPE:
return AreaCaptureProc(AREA_ABORT, 0);
break;
default:
return CallNextHookEx(AreaKBHook, nCode, _wParam, _lParam);
}
break;
default:
return CallNextHookEx(AreaKBHook, nCode, _wParam, _lParam);
}
return 1;
}
-
Are you processing WM_ERASEBKGND mgs and returning non zero?
All drawing should be done in the paint handler.
You should redraw (erase) your rectangle on the transparent DC in response to a mouse move (but not to the screen yet.
Call for a paint, bypassing the OS que and posting direcdtly to the app callback (InvalidateRect() + UpdateWindow() ).
Draw the smallest possible area (from the transparent DC to the Paint DC) with a single BitBlt() in the paint handler.
I use 'double buffering' to get flicker free drawing, doing exactly this. Try a search here.