c++ gdi animation not working - c++

im trying to create ball animation using gdi but i can't get it working.
i created a ball using this
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen, sf , 0, 10, 10);
i have while loop that loops and adds 1 to sf value basicly like this sf++;
than i try to repaint the window(it doesn't work) so ill end up with more than one circle ;/
here is the loop( the loop is int WM_PAINT)
while(sd==1)//sd equals 1
{
sf++;
onPaint(hdc);
InvalidateRect (hWnd, NULL, TRUE);// this should repaint the window but it doesn't
UpdateWindow(hWnd);
}
thanks in advance
Rami

In order to achieve animation I would suggest you use a timer. For example:
int OnCreate(HWND window, WPARAM wParam, LPARAM lParam)
{
SetTimer(window, TIMER_ID, 1000, 0);
return 0;
}
now window will receive WM_TIMER messages every second (1000ms). You should handle them:
int OnTimer(HWND window, WPARAM wParam, LPARAM lParam)
{
if(TIMER_ID == wParam)
{
/*do stuff*/
InvalidateRect(window, NULL, TRUE);//force window to repaint
}
return 0;
}
then you need to handle WM_PAINT message to do the drawing
int OnPaint(HWND window, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(&ps);
Graphics graphics(hdc);
graphics.Draw...
EndPaint(&ps);
return 0;
}

You do realize that you are incrementing sf in a loop with a conditional of (sd == 1), right? That will of course just loop infinitely or never be entered because the value of sd is not being changed in any way. Have you used the debugger at all here? Why would you need such a loop anyway? You should not be calling OnPaint in a loop.
If you want more than one circle, just draw them all before returning from the function. Maintain a collection of data that will be used to draw the circles in the OnPaint handler.

InvalidateRect sends a WM_ERASEBKGND message, and if you don't have a hbrBackground (brush to repaint the background) member of the WNDCLASS structure defined when you create the window it won't redraw the background unless you handle the WM_ERASEBKGND message yourself.
If that isn't the problem, then maybe because you are calling UpdateWindow directly instead of polling and handling messages, the WM_ERASEBKGND message never gets handled. Try overwriting the previous circle with the background colour before drawing the new one.
Or call SendMessage with WM_ERASEBKGRND as the message.

I found an example on msdn which shows how to draw stuff in pure win32.
You should not call Invalidate or Updatewindow in WM_PAINT, as UpdateWindow sends a new WM_PAINT-event, and invalidates get accumulated until the next wm_paint event.
You should divide your Code into two functions, one to perform the movement and the other to draw your circle at the current location.
Your Mover-function can be called from anywhere (perhaps in a timer handler function?) and should end with
InvalidateRect (hWnd, NULL, TRUE);
UpdateWindow(hWnd);
In order to mark your client area for redrawal and notify your window to redraw itself.
Your Draw()-function should read the position set with your mover function, and just a draw a circle around this location.
(Sidenote: If you want to minimize flicker and get smooth animation, have a look at double buffering once you get your basic animation up and running)
UPDATE
You were missing the UpdateWindow command in your Update-function
Your OnPaint-Function is only called when a WM_PAINT-message is received by your application, so you need to send those.
UpdateWindow serves this purpose
VOID update(HDC hdc,HWND hWnd)
{
sf++;
FillRect(hdc,rect,(HBRUSH)(COLOR_WINDOW+1));
InvalidateRect (hWnd, NULL, TRUE);
UpdateWindow(hWND);//<- This Line sends a wm_paint-message to your window in order to make it redraw itself
}
//i didn't do any changes to the onPaint functon but here is the code for it
VOID onPaint(HDC hdc)
{
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen, sf , 0, 10, 10);
}
//here is the while loop
while(sd==1)
{ onPaint(hdc);
update(hdc,hWnd);
}

Related

How to restrict the mouse cursor inside the window [duplicate]

I'm writing a very simple program to clip the mouse to a specified window. It runs from the system tray without a visible window. Because there will be multiple instances of the same window, it uses EnumWindows() to iterate through every top-level window and compare their hwnd with GetForegroundWindow(). When true, it runs the standard ClipCursor() code. ClipCursor() returns TRUE, and, I've asserted that the RECT set by GetClipCursor() is the exact same as the RECT passed to ClipCursor(). Yet, the cursor is free to move anywhere on the screen.
I've checked that the values in the RECT are the exact values of the window, I've compiled the program in release mode and ran it with admin rights, still nothing. The code below is exactly what runs after we've found the HWND of the GetForegroundWindow():
// Get the window client area.
GetClientRect(hwnd, &rc);
// Convert the client area to screen coordinates.
POINT pt = { rc.left, rc.top };
POINT pt2 = { rc.right, rc.bottom };
ClientToScreen(hwnd, &pt);
ClientToScreen(hwnd, &pt2);
SetRect(&rc, pt.x, pt.y, pt2.x, pt2.y);
clipped = true;
ClipCursor(&rc);
RECT rect;
GetClipCursor(&rect);
assert(rect.bottom == rc.bottom);
assert(rect.left == rc.left);
assert(rect.right == rc.right);
assert(rect.top == rc.top);
I've removed a lot of the checks because they were getting annoying (I was using MessageBox()'s), but this code is certainly running when it's supposed to. The cursor just isn't getting clipped, and I can't fathom why.
Since the cursor is a shared resource, your attempt to clip it is overwritten by anybody else who calls ClipCursor to unclip the cursor. And a lot of operations automatically unclip the cursor (such as any focus change). It's considered poor form for a background window to change the cursor clip.
Well, it only took me a few days to find out that despite the shared nature of the Cursor resource, if a process is clipping a cursor in certain circumstances that I don't fully know of yet (maybe the process has to be the foreground application? or something like that... I'm not so sure.) then the OS automatically releases the cursor back to full clipping mode (or whatever you'd call it).
Anyway, the fix is to do a low level mouse hook and call the clipcursor from there. Here's some quick proof of concept code (tested and works, although I removed the irrelevant stuff like creating a window or setting up the systray etc):
// Some variables we'll use
bool clipped = false; // Do we need to clip the mouse?
RECT rc; // The clip rect
HHOOK hMouseHook; // Low level mouse hook
// Low level mouse hook callback function
__declspec(dllexport) LRESULT CALLBACK MouseEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
// This part should be rewritten to make it not be a CPU-hog
// But works as a proof of concept
if ( clipped )
ClipCursor(&rc);
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
// .... Blah blah blah ....
// Low level mouse hook
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)MouseEvent, hInstance, 0);
// Only included to show that you set the hook before this,
// And unhook after this.
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Unhook the mouse
UnhookWindowsHookEx(hMouseHook);
return msg.wParam;
}

How to draw on standart button WinApi

I want to draw a simple square on a button.
I created a regular window and a regular button in it. Now, in the window procedure of my window, in the WM_PAINT message, I get the HDC of my button and draw a square:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_PAINT)
{
PAINTSTRUCT ps;
HDC my_hdc = BeginPaint(hWnd, &ps);
//---------------------------------------------
HDC my_button_HDC = GetDC(GetDlgItem(hWnd, 11)); //Get HDC my button
Rectangle(my_button_HDC, 5, 5, 30, 30);
//---------------------------------------------
EndPaint(hWnd, &ps);
}
WinMain()
{
//standart code create window and button...
}
When creating a window, a square does not appear on the button. It appears ONLY when I move my window down outside of the screen and lift it up.
But, as soon as I resize the window or click on the button, the square disappears again.
I don't understand why this is happening.
You are drawing on the button only when its parent window is being painted (also, you are leaking the button's HDC). Resizing the window does not always trigger a repaint.
But even when it does, when the button itself paints itself, it will draw over anything you have already drawn.
The correct way to draw on a standard Win32 button is to either:
give the button the BS_OWNERDRAW style, and then have its parent window handle the WM_DRAWITEM message.
if ComCtl32.dll v6 is being used (see Enabling Visual Styles), the parent window can instead handle the NM_CUSTOMDRAW message, BS_OWNERDRAW is not needed. See Custom Draw for more details.

How to repaint a child window only once every frame using WinAPI?

I'm using Direct3D and WinAPI to create windows and render 3D objects in the client area. I use the standard Windows message loop to invalidate the rectangle of the render window, and in the message handler of the render window, I perform a render call in Direct3D when processing the WM_PAINT message:
BOOL bRet;
HWND mainWnd; // Main window
HWND renderWnd; // Child of mainWnd, takes up a portion of the client area
// to be used as a Direct3D render target
MSG msg = {};
while ((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if(bRet != -1)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
InvalidateRect(renderWnd, nullptr, false);
}
}
// ...
// Window procedure used by renderWnd
LRESULT renderWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// ...
case WM_PAINT:
{
// Perform Direct3D rendering
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
This setup seems to work correctly as long as my app only has one window, as InvalidateRect is called each frame and ensures that the client area needs to be redrawn, which in turn will result in a Direct3D draw call. It doesn't feel very elegant though, especially once I try to create multiple windows, as I'd have to invalidate their rectangles in that same piece of code, even if the features that the windows serve otherwise have nothing to do with each other (some of the windows might not even exist at any one time).
That said, my question is this: is it possible to have a window invalidate part of its client area exactly once each frame (assuming it's currently not minimized, etc.)? Maybe through the use of the message queue? Referring back to the above code segment: I'd want some way for mainWnd (perhaps in its own window procedure) to call InvalidateRect on renderWnd exactly once each frame.
Thanks in advance!
EDIT: small error in the code sample
Note that the 'standard' way to handle this is something more like this:
// Main message loop
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Update your simulation/animation/etc. based on elapsed time
// -or-
// multiple fixed time-steps.
//
// Then render one frame.
}
}
…
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
This model is a 'flat-out' rendering model where you will render as fast as the system can do it, limited by Present details like refresh rate and automatic throttling if you have 3 or more frames ready.
You should take a look at GitHub for some more details like implementing WM_ENTERSIZEMOVE / WM_EXITSIZEMOVE.
The only time you'd implement WM_PAINT / InvalidateRect to render is if you are doing an editor or some 'mixed' UI and Direct3D application.
So with the help of the comments on the original post, I figured out that the best option was to use the Timers provided by WinAPI to schedule a repaint of the render window at the required framerate. Going to mark this as solved.

How do I properly move a window with a region?

I've just started looking into window regions, and I'm trying to create an elliptical window that I can move by dragging the client area. Unfortunately, when I drag the window, the window flashes back and forth from the ellipse to the normal window (as if I never called SetWindowRgn), and back, repeatedly and rapidly.
I read on MSDN that I have to call SetWindowRgn(nullptr);, then move the window, then reset the region, which I have already done in my code. I move the window by calling SetWindowPos with SWP_NOZORDER, SWP_NOSIZE, and SWP_NOREDRAW, and I've tried adding on all of SWP_NOSENDCHANGING, SWP_DEFERERASE, and SWP_NOCOPYBITS as well, to no avail.
Here's my window procedure, with emphasis on WM_MOUSEMOVE. I know it won't work if I release the button outside of the window; I was planning to deal with that after this worked. I also left out error checking. It's pretty obvious that the calls do work, though, because the window does move as I drag it.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static bool moving{};
switch (msg) {
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_LBUTTONDOWN: {
moving = true;
return 0;
}
case WM_LBUTTONUP: {
moving = false;
return 0;
}
case WM_MOUSEMOVE: {
static POINT old{0, 0};
if (moving) {
RECT r;
GetWindowRect(hwnd, &r);
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
RECT newPos;
newPos.left = r.left + x - old.x;
newPos.top = r.top + y - old.y;
SetWindowRgn(hwnd, nullptr, FALSE);
SetWindowPos(hwnd, nullptr, newPos.left, newPos.top, 0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOREDRAW
);
SetWindowRgn(hwnd, CreateEllipticRgn(200, 200, 600, 400), FALSE);
}
old.x = GET_X_LPARAM(lParam);
old.y = GET_Y_LPARAM(lParam);
return 0;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
I've also tried calling ValidateRgn(hwnd, nullptr); at the end of WM_MOUSEMOVE, which doesn't change anything. As well, I've tried wrapping the DefWindowProc call in a condition that just returns 0 if moving is set in order to see whether it was just some other message being sent that was messing with it, but that resulted in the window doing nothing when dragged. I then applied that condition to WM_PAINT and WM_ERASEBKGND handlers, but that resulted in the same flickering problem when dragged.
In order to test it more easily, here's full code (just rudimentary window creation etc). Am I going about moving the window the right way? Is there some message or something that I should handle, but don't? This is happening on Window 7 Ultimate N.
A much easier way to handle the move logic is to handle the WM_NCHITTEST message, returning HTCAPTION. This makes Windows handle all the move logic for you.

Why is my child window unresponsive to mouse events?

I have created a customized static window which displays a bitmap image, this window is the child window of some other window. Now I want to capture mouse events for this window, so that I can provide functionality to crop the image.
But the problem is Mouse events are not passed to this child window....
following is a code snippet of the WndProc of child window ..
WNDPROC origStatProc;
// Variable which stores the handle of BITMAP image
HBITMAP hBitmap=NULL;
LRESULT CALLBACK dispWndProc(HWND hwnd,UINT msg, WPARAM wParam, LPARAM lParam)
{
static HDC hdc;
static PAINTSTRUCT paintSt;
static RECT aRect;
switch(msg)
{
case WM_PAINT:
{
hdc = BeginPaint(hwnd,&paintSt);
GetClientRect(hwnd,&aRect);
if(hBitmap!=NULL)
{
HDC memDC = CreateCompatibleDC(hdc);
if(memDC!=NULL)
{
BITMAP bmp;
GetObject(hBitmap,sizeof(bmp),&bmp);
SelectObject(memDC,hBitmap);
SetStretchBltMode(hdc,HALFTONE);
StretchBlt(hdc,0,0,aRect.right,aRect.bottom,
memDC,0,0,bmp.bmWidth,bmp.bmHeight,
SRCCOPY);
DeleteObject(&bmp);
ReleaseDC(hwnd,memDC);
}
}
// the code for painting
EndPaint(hwnd,&paintSt);
}
break;
case STM_SETIMAGE:
{
InvalidateRect(hwnd,&aRect,true);
}
break;
case WM_LBUTTONDOWN:
{
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
char xstr[10];
_itoa(xPos,xstr,10);
MessageBox(NULL,xstr,"X Value ",MB_OK);
}
break;
default:
return origStatProc(hwnd,msg,wParam,lParam);
}
return 0;
}
Can anyone tell me what else I need to capture mouse events inside this Child window...
The window class that you use for the window will determine certain default behaviors for the window. The Static window class is particularly difficult to work with, because Windows makes assumptions that the window will never change its contents, and won't interact with the user in any way. You'll probably find that the WM_LBUTTONDOWN is being passed to the parent window.
If I remember correctly: static windows declare themselves to be 'invisible' to mouse clicks by returning HTTRANSPARENT in response to WM_NCHITTEST. Because of this, windows passes the mouse click on to the parent. If you want to process the mouse clicks in the statics, you'll need to also override this behavior to return HTCLIENT instead.
I have called DefWndProc() instead of origStatProc(hwnd,msg,wParam,lParam) and the problem is solved....
anyWays thanks to everyone....