running this code leads to the title question:
if you resize the window you will not see any flicker (repaint sended by the system)
if you move mouse inside the window, severe flicker will occurr (repaint sended by me)
how to reproduce the system-driven WM_PAINT?
#include <windows.h>
#include <wingdi.h>
LRESULT CALLBACK proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_ERASEBKGND: return true;break;
case WM_MOUSEMOVE: InvalidateRect(hwnd, 0, 0); break;
case WM_PAINT:
{
InvalidateRect(hwnd,0,0);
HBRUSH b= CreateSolidBrush(0x000000ff);
HBRUSH c= CreateSolidBrush(0x0000ff00);
HBRUSH d= CreateSolidBrush(0x00ff0000);
RECT r;
GetClientRect(hwnd,&r);
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hwnd,&ps);
FillRect(hdc,&r, b);
Sleep(10);
FillRect(hdc,&r, c);
` Sleep(10);
FillRect(hdc,&r,d);
EndPaint(hwnd,&ps);
DeleteObject(b);
DeleteObject(c);
DeleteObject(d);
}
break;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
return 0;
}
int main()
{
HWND hwnd=CreateWindow(WC_DIALOG,0,WS_OVERLAPPEDWINDOW|WS_VISIBLE,0,0,500,500,0,0,0,0);
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)proc);
MSG msg;
while (true)
{
if (GetMessage(&msg, 0, 0, 0) != WM_CLOSE)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 1;
}
You should not invalidate the window if the mouse only moves over it because that will lead to a WM_PAINT message ultimately. That causes the flicker (in combination with the sleep).
Related
I have a timer that is responsible for displaying the GIF image frame by frame. I noticed that when I right-clicked and hold the titilebar the timer pause for I think 1 second and when I left-clicked and hold the titlebar`, the timer pause until I released the mouse.
LRESULT CALLBACK GDIHelper::StaticControlProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
switch(uMsg) {
case WM_TIMER:
{
OnTimer(); // Do something on timer.
InvalidateRect(staticControl, NULL, FALSE);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
Graphics g(hdc);
g.DrawImage(m_pImage, 0, 0, width, height);
EndPaint(hwnd, &ps);
return TRUE;
}
case WM_DESTROY:
{
return 0;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_CREATE: {
staticControl = CreateWindowEx(0, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, xPosition, yPosition, width, height, hWnd, NULL, NULL, NULL); //create the static control.
SetWindowSubclass(staticControl, &StaticControlProc, unique_id, 0);
gdiHelper.AnimateGIF();
break;
}
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
//Paint other images and text here...
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
{
gdiHelper.Destroy();
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
and here are the functions responsible for creating the timer.
void GDIHelper::OnTimer() {
if(isPlayable) {
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
}
}
void GDIHelper::AnimateGIF() {
if(m_bIsPlaying == TRUE) {
return;
}
m_iCurrentFrame = 0;
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
SetTimer(staticControl, 120, ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10, NULL);
++m_iCurrentFrame;
m_bIsPlaying = TRUE;
}
How to possibly prevent that?
The InvalidateRect() will not immediately redraw your control. It will simply schedule a future redraw for a specific rectangular area of the window. Making the animation freeze when you clicked or hold on the titlebar. Use InvalidateRect() followed by UpdateWindow(), this will forcefully perform immediate redrawing of the specified area of the window.
But it won't just solve the issue. Forget about your timer and use a non-blocking thread instead together with InvalidateRect() and UpdateWindow().
int animation_duration = 0;
void GDIHelper::TheAnimation() { //This function should be static.
if(m_bIsPlaying == TRUE) {
return;
}
m_iCurrentFrame = 0;
GUID Guid = FrameDimensionTime;
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
++m_iCurrentFrame;
m_bIsPlaying = TRUE;
animation_duration = ((UINT*)m_pItem[0].value)[m_iCurrentFrame] * 10;
while(isPlayable) { //Make sure to set isPlayable to false on destroy to stop this thread.
std::this_thread::sleep_for(std::chrono::milliseconds(animation_duration)); //instead of timer, use sleep.
m_pImage->SelectActiveFrame(&Guid, m_iCurrentFrame);
m_iCurrentFrame = (++m_iCurrentFrame) % m_FrameCount;
InvalidateRect(staticControl, NULL, FALSE);
UpdateWindow(staticControl); //perform immediate redrawing
}
}
LRESULT CALLBACK GDIHelper::StaticControlProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
switch(uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
Graphics g(hdc);
g.DrawImage(m_pImage, 0, 0, width, height); //draw your image.
EndPaint(hwnd, &ps);
return TRUE;
}
....
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
Then just start the thread and use detach() for non-blocking UI.
std::thread t(TheAnimation);
t.detach(); // this will be non-blocking thread.
Read the comments in the code for some clarification and don't forget to set isPlayable to false on destroy to stop the thread.
LRESULT handleDoubleClicks(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, POINT ptLastClickPos, DWORD dwLastClickTime)
{
DWORD dwClickTime = GetMessageTime();
POINT ptClickPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
switch(message)
{
case WM_LBUTTONDOWN:
if (dwLastClickTime + GetDoubleClickTime() > dwClickTime
&& abs(ptLastClickPos.x - ptClickPos.x) < GetSystemMetrics(SM_CXDOUBLECLK)
&& abs(ptLastClickPos.y - ptClickPos.y) < GetSystemMetrics(SM_CYDOUBLECLK))
{
MessageBox(hWnd, TEXT("Double click"), TEXT("I appear when double clicked"), MB_OKCANCEL);
}
else
{
dwLastClickTime = dwClickTime;
ptLastClickPos = ptClickPos;
wchar_t waCoord[20];
wsprintf(waCoord, _T("(%i,%i)"), ptLastClickPos.x, ptLastClickPos.y);
MessageBox(hWnd, waCoord, _T("Left mouse button click"), MB_OK);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
This is function i have made to handle double click:
This function is called when windows recieve WM_LBUTTONDOWN it will take the time of the message and coordinate of the click and will transfer it to the function here i want the fuction to recieve another message WM_LBUTTONDOWN and determine the time of the message and compare with the previous click time and the co ordinates to identify if it is a double click.
But this is not working .May be I am Wrong with the approach I am newbie Pls help me to solve this problem.
case WM_LBUTTONDOWN:
{
dwLastClickTime= GetMessageTime();
// SetTimer(hWnd,0,GetDoubleClickTime(),0);
ptLastClickPos.x = LOWORD(lParam);
ptLastClickPos.y = HIWORD(lParam);
handleDoubleClicks(hWnd, message, wParam, lParam, ptLastClickPos, dwLastClickTime);
}
You could use SetTimer, after one click, not immediately judge it as a click, but start the timer, check whether there is another click within the timer range, if there is, it is judged as double-click, if not, and the previous time is determined as a single click.
#define TIMER_ID 10
static int click_count = 0;
static POINT point = { 0 };
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_TIMER:
{
KillTimer(hWnd, TIMER_ID);
if (click_count == 1)
{
wchar_t waCoord[20];
wsprintf(waCoord, _T("(%i,%i)"), point.x, point.y);
MessageBox(hWnd, waCoord, _T("Left mouse button click"), MB_OK);
}
else if(click_count == 2)
{
MessageBox(hWnd, TEXT("Double click"), TEXT("I appear when double clicked"), MB_OKCANCEL);
}
else if (click_count == 3)
{
MessageBox(hWnd, TEXT("Triple click"), TEXT("I appear when triple clicked"), MB_OKCANCEL);
}
click_count = 0;
return 0;
}
break;
case WM_LBUTTONDOWN:
{
if (click_count == 0)
{
SetTimer(hWnd, TIMER_ID, GetDoubleClickTime(), NULL);
}
click_count++;
return 0;
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
And make sure your window doesn't have CS_DBLCLKS style. Otherwise, the second WM_LBUTTONDOWN message that would normally be generated becomes a WM_LBUTTONDBLCLK message, according to the document: Double Clicks
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{ HDC hdc;
int count=1; int xs,ys,xe,ye;
switch (message)
{
case WM_LBUTTONDOWN
hdc=GetDC(hwnd);
if(count%2!=1){
xs=GET_X_LPARAM(lParam);
ys=GET_Y_LPARAM(lParam);}
else{
xe=GET_X_LPARAM(lParam);
ye=GET_Y_LPARAM(lParam);
drawline(hdc,xs,ys,xe,ye);
}
ReleaseDC(hwnd,hdc);
count++;
break;
case WM_DESTROY:
PostQuitMessage (0);
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
I've tried , but there's something wrong with this code.
Use
if(count%2 == 1)
insted of
if(count%2 != 1)
Might work then.
I try to change pen color with this code :
redPen = CreatePen(PS_SOLID,1,0xFF0000);
SelectObject(hdc, redPen);
but it change from black into blue. And no matter what color is, because pen always will be blue if i try to set new color or black if i do nothing.
Here is whole code:
#include "windows.h"
int N=50;
int M=30;
int X=25;
int width=X * N;
int height=X * M;
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
int WINAPI WinMain(HINSTANCE h,HINSTANCE hprevinstance,LPSTR lpcmdline,int ncmdshow){
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
ZeroMemory(&msg,sizeof(MSG));
wc.cbSize=sizeof(wc);
wc.style=CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc=WndProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=h;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wc.hCursor=LoadCursor(h,IDC_ARROW);
wc.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);
wc.lpszMenuName=NULL;
wc.lpszClassName="main";
wc.hIconSm=LoadIcon(NULL,IDI_APPLICATION);
if(!RegisterClassEx(&wc)) return 0;
if(!(hwnd=CreateWindowEx(NULL,"main","test",WS_OVERLAPPEDWINDOW|WS_VISIBLE,0,0,width,height,NULL,NULL,h,NULL))) return 0;
ShowWindow(hwnd,SW_SHOWDEFAULT);
UpdateWindow(hwnd);
while(msg.message != WM_QUIT){
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
HDC hdc;
PAINTSTRUCT ps;
HPEN redPen;
switch (message){
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
redPen = CreatePen(PS_SOLID,1,0xFF0000);
SelectObject(hdc, redPen);
for(int i=0;i<width;i+=X){
MoveToEx(hdc,i,0,NULL);
LineTo(hdc,i,height);
}
for(int j=0;j<height;j+=X){
MoveToEx(hdc,0,j,NULL);
LineTo(hdc,width,j);
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
redPen = CreatePen(PS_SOLID,1,0xFF0000);
No, that's definitely the wrong identifier name, it should be "bluePen". COLORREF is encoded as 0x00bbggrr in hex. So 0x00ff0000 is blue, not red.
Fall in the pit of success by using the RGB macro instead:
redPen = CreatePen(PS_SOLID, 1, RGB(0xFF, 0, 0));
I'm making an auto clicker for a game in WinAPI and I have 4 simple buttons on the main window. When the user presses the 'start' button I want another window to open asking them for settings such as number of times to click and time between clicks. When I try to create a new window, nothing is happening, but everything else works perfectly.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
case WM_COMMAND:
{
switch (wParam)
{
case ID_START:
{
HINSTANCE hInstance = GetModuleHandle(CLASS_NAME);
HWND settings = CreateWindowEx(
0,
L"Settings",
L"Settings",
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CHILD,
100, 100, 600, 200,
NULL,
(HMENU) ID_SETTINGS,
hInstance,
NULL
);
MSG msg = { };
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
case ID_QUIT:
{
PostQuitMessage(0);
return 0;
}
case ID_CALIB:
{
if (MessageBox(hwnd, L"You pressed Calibrate", L"Calibrate", MB_OK))
{
return 0;
}
}
case ID_INFO:
{
if (MessageBox(hwnd, L"You pressed about", L"About", MB_OK))
{
return 0;
}
}
}
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW+1));
EndPaint(hwnd, &ps);
return 0;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
I just started WinAPI today, so I am extremely new. Thanks for any help in advance!
The second parameter to CreateWindowEx must be a class name that you registered earlier by calling RegisterClass.
You are specifying WS_CHILD. But a child must have a parent. Pass the parent HWND into the hwndParent parameter.