Thread: SetTimer() doesn't behave.

  1. #1
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555

    SetTimer() doesn't behave.

    I wanted to try using SetTimer() as an FPS limiter but things aren't going so well.
    Code:
    #define FPS 60
    #define IDT_TIMER 1001
    
    const char oglclass[] = "OpenGL Renderer";
    
    HWND window;
    int w;
    int h;
    
    int WINAPI WinMain (HINSTANCE inst, HINSTANCE pinst, char* args, int show)
    {
    
    ...
    
    	window = CreateWindowEx(WS_EX_CLIENTEDGE, oglclass, "OpenGL", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
    	                        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, inst, NULL);
    	if (window == NULL || SetTimer(window, IDT_TIMER, (2000 + FPS)/(2*FPS), NULL) == 0) {
    		ErrorMsg();
    		return 1;
    	}
    	ShowWindow(window, show);
    	UpdateWindow(window);
    
    	while (GetMessage(&msg, NULL, 0, 0) > 0) {
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    
    	return msg.wParam;
    }
    WM_TIMER should be sent every 17 milliseconds to oglproc but it seems like it doesn't.
    oglproc looks kinda like
    Code:
    LRESULT CALLBACK oglproc (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
    {
    	static HDC   hdc;
    	static HGLRC hrc;
    	static RECT  re = {0, 0, 0, 0};
    
    	switch (msg) {
    
    ...
    
    		case WM_SIZE:
    			re.right  = w = LOWORD(lparam);
    			re.bottom = h = HIWORD(lparam);
    			if (!h)
    				h = 1;
    			glViewport(0, 0, w, h);
    		case WM_PAINT:
    			render();
    			SwapBuffers(hdc);
    			break;
    		case WM_TIMER:
    			next_frame();
    			render();
    			SwapBuffers(hdc);
    			break;
    		default:
    			return DefWindowProc(hwnd, msg, wparam, lparam);
    			break;
    	}
    
    	return 1;
    WM_TIMER is never recieved, not even manually with PostMessage (I have yet to try SendMessage()). MSDN mentions that WM_TIMER is low priority but how low can it be?
    It did get sent at one time, when case WM_TIMER: was below case WM_SIZE: and I didn't notice that it 'spilled' over to WM_TIMER, however debug messages indicated that wparam was the same as IDT_TIMER so WM_TIMER was called as well but strangely enough it was only sent when the window was minimized. I can't seem to reproduce it though but so far it seems to only want to get sent when it's tickled in the right spot.

    I think it might've happened when I had a TimerProc in use while also catching WM_TIMER messages but I don't see why, as I understood the documentation WM_TIMER will be sent to a TimerProc if there's one attatched, otherwise it's sent to your window procedure?
    Quote Originally Posted by MSDN
    If lpTimerFunc is NULL, the system posts a WM_TIMER message to the application queue.
    Quote Originally Posted by MSDN
    When you specify a TimerProc callback function, the default window procedure calls the callback function when it processes WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

  2. #2
    Registered User Joelito's Avatar
    Join Date
    Mar 2005
    Location
    Tijuana, BC, México
    Posts
    310
    Code:
    case WM_SIZE:
    			re.right  = w = LOWORD(lparam);
    			re.bottom = h = HIWORD(lparam);
    			if (!h)
    				h = 1;
    			glViewport(0, 0, w, h); // No break or return when WM_SIZE is trapped
    		case WM_PAINT:
    * PC: Intel Core 2 DUO E6550 @ 2.33 GHz with 2 GB RAM: Archlinux-i686 with xfce4.
    * Laptop: Intel Core 2 DUO T6600 @ 2.20 GHz with 4 GB RAM: Archlinux-x86-64 with xfce4.

  3. #3
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    It's supposed to be like that because after resizing the screen needs to be redrawn again.

  4. #4
    Registered Abuser
    Join Date
    Jun 2006
    Location
    Toronto
    Posts
    591
    Try placing the SetTimer call under WM_CREATE in your olgproc.

  5. #5
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    I did that (and changing window to hwnd) but that didn't make any difference.
    I have found that by calling my debug() function WM_TIMER will be triggered, and if you were to put a call to debug() inside the WM_TIMER case there will be a mass creation of message boxes that you usually freezes the computer until the program eventually crashes. This happens only as longs as there is a message box open, whether it be the one from WM_CREATE or WM_TIMER and each new message box is created within the interval.
    So why does the timer seem to be bound to message boxes?
    Here's the debug() function but I am also attaching the whole source if needed.
    Code:
    void debug (const char* format, ...)
    {
    	va_list args;
    	TCHAR buf[1024];
    
    	va_start(args, format);
    	#ifdef C99
    		vsnprintf(buf, sizeof(buf)/sizeof(*buf), format, args);
    	#else
    		vsprintf(buf, format, args);
    	#endif
    	MessageBox(NULL, buf, NULL, MB_OK | MB_ICONERROR);
    	va_end(args);
    }

  6. #6
    Registered User Queatrix's Avatar
    Join Date
    Apr 2005
    Posts
    1,342
    >> So why does the timer seem to be bound to message boxes?

    It isn't, but when you call MessageBox(), the function will pause, but the window won't, so WM_TIMER will continue to be called, hence the continues messages.
    But if you give MessageBox() the handle to your window, it will cause the program to wait untill the message is gone before resuming WM_TIMER callings, so no more than one box should be up at a time.

    When doing these kind of tests, I suggest you make you make your app set it's own proccess priority to low before even creating the window. (So you can still end the app with the taskmanager before its too late, in cases of these message box hordes!)

    EDIT:

    For an FPS limiter, you might want to use somthing like GetTick() or GetClock() or somthing...
    Last edited by Queatrix; 04-08-2007 at 08:57 PM.

  7. #7
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    Quote Originally Posted by Queatrix View Post
    It isn't, but when you call MessageBox(), the function will pause, but the window won't, so WM_TIMER will continue to be called, hence the continues messages.
    But the WM_TIMER notification was never handled in the first place, it just magically started to get handled when there was a message box present.
    Quote Originally Posted by Queatrix View Post
    But if you give MessageBox() the handle to your window, it will cause the program to wait untill the message is gone before resuming WM_TIMER callings, so no more than one box should be up at a time.
    I've tried that but that makes no difference either, there will still be a flood.
    Last edited by OnionKnight; 04-09-2007 at 06:54 AM. Reason: place not case

  8. #8
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    I think what is happening is that the window is never validated so there is always a WM_PAINT outstanding. WM_PAINT has a higher priority than WM_TIMER so you never receive a timer event.

    The typical Windows drawing model is to do all drawing in WM_PAINT (when another message needs to cause a redraw, it calls InvalidateRect). WM_PAINT calls BeginPaint/EndPaint which validates the window.

    However, if not using that model, you can manually validate a window using ValidateRect at any time. I got your program working by adding a ValidateRect(hwnd, NULL) after the completion of drawing (at the end of WM_PAINT and WM_TIMER) (it could also go at the end of the render function).

    By the way, cool animation!

    P.S When you called debug from WM_CREATE, it was before the window was visible. Hence, no WM_PAINT calls, and the WM_TIMER could be processed.
    Last edited by anonytmouse; 04-08-2007 at 11:11 PM.

  9. #9
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    Not sure I understand what's going on in this case. Under normal circumstances the WM_PAINT starts out with an invalid window and when it's done the area gets valid? Is something inside the case (render() or SwapBuffers()) causing it to go invalid again and thus causing the WM_PAINT message to loop?
    Quote Originally Posted by anonytmouse View Post
    By the way, cool animation!
    It's really trippy.

  10. #10
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    Under normal circumstances the WM_PAINT starts out with an invalid window and when it's done the area gets valid?
    Sort of. For each window, the system maintains an update region, which specifies the area that needs painting. For example, when part of a window is obscured by another window and then brought to the top, the previously obscured area will be added to the update region. The API can also be used to manually add to the update region with functions such as InvalidateRect. When the update region is not empty and there are no other higher priority messages waiting, the system will dispatch a WM_PAINT message. Calling BeginPaint, ValidateRect or ValidateRgn will empty (or reduce the size of) the update region.

    MSDN has a section on the WM_PAINT message that is well worth a read.
    Quote Originally Posted by MSDN
    BeginPaint sets the update region of a window to NULL. This clears the region, preventing it from generating subsequent WM_PAINT messages. If an application processes a WM_PAINT message but does not call BeginPaint or otherwise clear the update region, the application continues to receive WM_PAINT messages as long as the region is not empty. In all cases, an application must clear the update region before returning from the WM_PAINT message.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. SetTimer Function problems
    By Swarvy in forum Windows Programming
    Replies: 2
    Last Post: 10-01-2008, 08:02 AM
  2. Why does it behave so
    By mudigonda_ms in forum C Programming
    Replies: 6
    Last Post: 12-21-2003, 01:54 AM
  3. Settimer or Alarm?????
    By gvector1 in forum C Programming
    Replies: 1
    Last Post: 08-29-2003, 09:56 AM
  4. Two Struct & Class Programs that behave the same
    By Unregistered in forum C++ Programming
    Replies: 1
    Last Post: 07-05-2002, 09:59 AM