Device context woes

This is a discussion on Device context woes within the Windows Programming forums, part of the Platform Specific Boards category; My window procedure receives a WM_TIMER event, causing the window to animate (as some of you may remember from yesterday). ...

  1. #1
    Budding Synth Programmer samGwilliam's Avatar
    Join Date
    Feb 2002
    Location
    Trefforest
    Posts
    368

    Device context woes

    My window procedure receives a WM_TIMER event, causing the window to animate (as some of you may remember from yesterday). But now I want to add the feature of drawing onto the game board while the animation is going on.

    Although I know how to draw onto a window (thanks to a tutorial), I can't get it to happen here. I can write single pixels onto the window but I can't draw by clicking and dragging. To do multiple pixels you have to release the button and click again.

    I know it's something to do with contention for the device context but I can't figure out a way around it. I even tried setting a flag on WM_LBUTTONDOWN so that the WM_TIMER code can read this and break out instead of attempting to take the DC but it still won't let me draw.

    I know the draw code is fine because if I comment out the WM_TIMER clause in the switch block it works fine.

    What is the problem?
    Last edited by samGwilliam; 04-13-2005 at 01:21 PM.
    MSVC++ 6.0

  2. #2
    Registered User
    Join Date
    Jan 2005
    Posts
    847
    Wouldn't it be better to do the drawing on WM_MOUSEMOVE?

  3. #3
    Budding Synth Programmer samGwilliam's Avatar
    Join Date
    Feb 2002
    Location
    Trefforest
    Posts
    368
    I should clarify:

    The drawing is done on both WM_MOUSEMOVE and WM_LBUTTONDOWN but it only has an effect in the latter.

    Code:
    case WM_LBUTTONDOWN:
    			hdc = GetDC (hwnd);
    			
    			if (hdc)
    			{
    				xp = LOWORD(lParam);
    				yp = HIWORD(lParam);
    							
    				SetPixel (hdc, xp, yp, RGB (255, 0, 0));
    				grid [currentGrid] [yp - 20] [xp - 20] = 1;
    			}
    		break;
    
    		case WM_LBUTTONUP:
    			if(hdc)
    				ReleaseDC (hwnd, hdc);
    				
    			hdc = NULL;
    		break;
    
    		case WM_MOUSEMOVE:
    			if(hdc)
    			{	
    				short xp = LOWORD (lParam);
    				short yp = HIWORD (lParam);
    
    				SetPixel (hdc, xp, yp, RGB (255, 0, 0));
    				grid [currentGrid] [yp - 20] [xp - 20] = 1;
    			}
    		break;
    MSVC++ 6.0

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    847
    Is hdc a static variable?

  5. #5
    Budding Synth Programmer samGwilliam's Avatar
    Join Date
    Feb 2002
    Location
    Trefforest
    Posts
    368
    It's global.

    I'm really tearing my hair out over this by the way if anyone can help...
    MSVC++ 6.0

  6. #6
    Budding Synth Programmer samGwilliam's Avatar
    Join Date
    Feb 2002
    Location
    Trefforest
    Posts
    368
    38 views and nobody has any advice? Nobody has experienced similar problems in the past? I doubt that somehow...
    MSVC++ 6.0

  7. #7
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,659
    Zip up and post a small one window app that demonstrates your woe's.....

    With compilable code available to be debugged, plenty of folks with try to fix you up.

    gg

  8. #8
    Budding Synth Programmer samGwilliam's Avatar
    Join Date
    Feb 2002
    Location
    Trefforest
    Posts
    368
    OK, here's the code. It's severely abridged but it conveys the essence of my problem:

    Code:
    #include <time.h>
    #include <stdlib.h>
    #include <windows.h>
    
    #define MAX 261
    
    LRESULT CALLBACK MainWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
    	WNDCLASS myWin;
    	MSG msg;
    
    	myWin.style = CS_VREDRAW | CS_HREDRAW;
    	myWin.lpfnWndProc = MainWindowProc;
    	myWin.cbClsExtra = 0;
    	myWin.cbWndExtra = 0;
    	myWin.hInstance = hInstance;
    	myWin.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    	myWin.hCursor = LoadCursor (NULL, IDC_ARROW);
    	myWin.hbrBackground = (HBRUSH) (COLOR_SCROLLBAR + 1);
    	myWin.lpszMenuName = 0;
    	myWin.lpszClassName = "My First App.";
    
    	
    	if (!RegisterClass (&myWin))
    		return 0;
    
    	HWND hwnd;
    
    	hwnd = CreateWindow (	"My First App.",
    							"SamLife, v1.0",
    							WS_OVERLAPPEDWINDOW,
    							20,
    							20,
    							MAX + 40,
    							MAX + 60,
    							NULL,
    							NULL,
    							hInstance,
    							NULL);
    
    	if (!hwnd)
    		return 0;
    
    	SetTimer(hwnd, 1, 1, NULL);
    	ShowWindow (hwnd, nCmdShow);
    
    	while (GetMessage (&msg, NULL, 0, 0))
    	{
    		TranslateMessage (&msg);
    		DispatchMessage (&msg);
    	}
    
        return 0;
    }
    
    HDC hdc = NULL;
    
    LRESULT CALLBACK MainWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    	static int gen1 = 0;
    	static int gen2 = 0;
    	static int gen3 = 0;
    
    	static short xp;
    	static short yp;
    
        static int timerId = 3000;
    	static int currentGrid = 0;
    
    	int col = 0;
    
    	int sum = 0;
    	int i, j;
    	int temp_i, temp_j;
    
    	static unsigned char bias [8] = { 20, 50, 75, 15, 50, 10, 15, 20 };
    	static unsigned char grid [2] [MAX] [MAX];
    	register unsigned char temp;
    
    	switch (msg)
    	{
    		case WM_CREATE:
    			srand ((unsigned) time (NULL));
    			
    			for (i = 0; i < MAX; i++)
    				for (j = 0; j < MAX; j++)
    				{
    					grid [0] [i] [j] = 0;
    					grid [1] [i] [j] = 0;
    				}
    
    		
    	
    			SetTimer (hwnd, timerId, 1, NULL);
    		break;
    
    		case WM_LBUTTONDOWN:
    			if (!hdc)
    				hdc = GetDC (hwnd);
    
    			if (hdc)
    			{
    				xp = LOWORD(lParam);
    				yp = HIWORD(lParam);
    							
    				SetPixel (hdc, xp, yp, RGB (255, 0, 0));
    				grid [currentGrid] [yp - 20] [xp - 20] = 1;
    			}
    		break;
    
    		case WM_LBUTTONUP:
    			if(hdc)
    			{
    				ReleaseDC (hwnd, hdc);
    				hdc = NULL;
    			}
    		break;
    
    		case WM_MOUSEMOVE:
    			if(hdc)
    			{	
    				short xp = LOWORD (lParam);
    				short yp = HIWORD (lParam);
    
    				SetPixel (hdc, xp, yp, RGB (255, 0, 0));
    				grid [currentGrid] [yp - 20] [xp - 20] = 1;
    			}
    		break;
    
    		case WM_MOVE:
    		case WM_SIZE:
    		case WM_PAINT:
    			hdc = GetDC (hwnd);
    
    			if (!hdc)
    				break;
    
    			for (i = 0; i < MAX; i++)
    				for (j = 0; j < MAX; j++)
    				{
    					if (grid [currentGrid] [i] [j])
    						SetPixel (hdc, j + 20, i + 20, RGB (255, 255, 255));
    
    					else
    						SetPixel (hdc, j + 20, i + 20, RGB (0, 0, 0));
    				}
    
    			ReleaseDC (hwnd, hdc);
    			hdc = NULL;
    		break;
    
    		case WM_CLOSE:
    			DestroyWindow (hwnd);
    		break;
    
    		case WM_DESTROY:
    			KillTimer (hwnd, timerId);
    			PostQuitMessage (0);;
    		break;
    
            case WM_TIMER:	
    			if (!hdc)
    			hdc = GetDC (hwnd);
    
    			if (hdc)
    			{	
    				SetPixel (hdc, rand () % MAX, rand () % MAX, RGB (255, 0, 0));
    				ReleaseDC (hwnd, hdc);
    			hdc = NULL;
    			}
    			 
    		break;
    	}
    	
    	return DefWindowProc (hwnd, msg, wParam, lParam);	
    }
    I've even put in code so that it only takes the HDC if it is free. But I still can't drag and draw unless I comment out the entire WM_TIMER clause.
    MSVC++ 6.0

  9. #9
    Budding Synth Programmer samGwilliam's Avatar
    Join Date
    Feb 2002
    Location
    Trefforest
    Posts
    368
    OK, so now there are over 70 views and I've posted code that illustrates the problem completely and still nobody will help me. This seems like a pretty elementary problem to me - I'm sure that most of you know what the solution is but just have bigger fish to fry, so to speak...
    MSVC++ 6.0

  10. #10
    Registered User
    Join Date
    Jan 2005
    Posts
    847
    Well it's not that no ones cares...
    Maybe your problem has to do with WM_PAINT setting hdc to NULL which would stop WM_MOUSEMOVE from drawing untill WM_LBUTTONDOWN set it to the dc again?

  11. #11
    Yes, my avatar is stolen anonytmouse's Avatar
    Join Date
    Dec 2002
    Posts
    2,544
    It is recommended that you only actually paint when you receive a WM_PAINT message. If you want to paint in other messages, you should record where you want painted (in an array, a memory dc, etc) and then call InvalidateRect. This causes windows to send a WM_PAINT message. This is your program altered to use that approach:
    Code:
    #include <time.h>
    #include <stdlib.h>
    #include <windows.h>
    #pragma comment(lib, "gdi32.lib")
    
    #define MAX 261
    
    LRESULT CALLBACK MainWindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {
    	WNDCLASS myWin;
    	MSG msg;
    
    	myWin.style = CS_VREDRAW | CS_HREDRAW;
    	myWin.lpfnWndProc = MainWindowProc;
    	myWin.cbClsExtra = 0;
    	myWin.cbWndExtra = 0;
    	myWin.hInstance = hInstance;
    	myWin.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    	myWin.hCursor = LoadCursor (NULL, IDC_ARROW);
    	myWin.hbrBackground = (HBRUSH) (COLOR_SCROLLBAR + 1);
    	myWin.lpszMenuName = 0;
    	myWin.lpszClassName = "My First App.";
    
    	if (!RegisterClass (&myWin))
    		return 0;
    
    	HWND hwnd;
    
    	hwnd = CreateWindow (	"My First App.",
    							"SamLife, v1.0",
    							WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
    							20,
    							20,
    							MAX + 40,
    							MAX + 60,
    							NULL,
    							NULL,
    							hInstance,
    							NULL);
    
    	if (!hwnd)
    		return 0;
    
    	// SetTimer(hwnd, 1, 1, NULL);
    	ShowWindow (hwnd, nCmdShow);
    
    	while (GetMessage (&msg, NULL, 0, 0))
    	{
    		TranslateMessage (&msg);
    		DispatchMessage (&msg);
    	}
    
        return 0;
    }
    
    
    LRESULT CALLBACK MainWindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    	static int gen1 = 0;
    	static int gen2 = 0;
    	static int gen3 = 0;
    
    	static short xp;
    	static short yp;
    
    	static int timerId = 3000;
    	static int currentGrid = 0;
    
    	int col = 0;
    
    	int sum = 0;
    	int i, j;
    
    	static unsigned char bias [8] = { 20, 50, 75, 15, 50, 10, 15, 20 };
    	static unsigned char grid [2] [MAX] [MAX];
    
    	static bool bDrawing;
    
    	switch (msg)
    	{
    		case WM_CREATE:
    		{
    			srand ((unsigned) time (NULL));
    			
    			for (i = 0; i < MAX; i++)
    				for (j = 0; j < MAX; j++)
    				{
    					grid [0] [i] [j] = 0;
    					grid [1] [i] [j] = 0;
    				}
    
    			SetTimer (hwnd, timerId, 10, NULL);
    		}
    		break;
    
    		case WM_LBUTTONDOWN:
    		{
    			bDrawing = TRUE;
    		}
    		break;
    
    		case WM_LBUTTONUP:
    		{
    			bDrawing = FALSE;
    		}
    		break;
    
    		case WM_MOUSEMOVE:
    		{
    			if (bDrawing)
    			{
    				xp = LOWORD(lParam) - 20;
    				yp = HIWORD(lParam) - 20;
    
    				if (xp > 0   && yp > 0 && 
    				    xp < MAX && yp < MAX)
    				{
    					grid [currentGrid] [yp] [xp] = 1;
    					InvalidateRect(hwnd, NULL, FALSE);
    				}
    			}
    		}
    		break;
    
    		case WM_MOVE:
    		case WM_SIZE:
    		{
    			// InvalidateRect(hwnd, NULL, FALSE);
    		}
    		break;
    
    		case WM_PAINT:
    		{
    			PAINTSTRUCT ps;
    			HDC hdc = BeginPaint(hwnd, &ps);
    
    			for (i = 0; i < MAX; i++)
    				for (j = 0; j < MAX; j++)
    				{
    					if (grid [currentGrid] [i] [j] == 1)
    						SetPixel (hdc, j + 20, i + 20, RGB (255, 255, 255));
    					else if (grid [currentGrid] [i] [j] == 2)
    						SetPixel (hdc, j + 20, i + 20, RGB (255, 0, 0));
    					else
    						SetPixel (hdc, j + 20, i + 20, RGB (0, 0, 0));
    				}
    
    			EndPaint(hwnd, &ps);
    			return 0;
    		}
    		break;
    
    		case WM_DESTROY:
    		{
    			KillTimer (hwnd, timerId);
    			PostQuitMessage (0);;
    		}
    		break;
    
    	        case WM_TIMER:	
    		{
    			grid[currentGrid][rand() % MAX][rand() % MAX] = 2;
    			InvalidateRect(hwnd, NULL, FALSE);
    		}
    		break;
    	}
    	
    	return DefWindowProc (hwnd, msg, wParam, lParam);	
    }
    Things to note:
    • I've used NULL with InvalidateRect to repaint the entire window. This is slow and causes flicker. If we are professional we should provide a rect to InvalidateRect that specifies only the area that needs repainting. Then in WM_PAINT, the contents of PAINTSTRUCT will tell us which area needs redrawing.
    • You may notice that using SetPixel to draw does not provide particularly satisfactory results. You will want to look into the gdi drawing functions such as LineTo.
    • When a thread yields, it will rarely be re-shcheduled in less than 10ms. Therefore, using a 1ms timeout with SetTimer will typically result in a timer that is triggered every 10ms or less.
    Last edited by anonytmouse; 04-15-2005 at 08:02 PM.

  12. #12
    Budding Synth Programmer samGwilliam's Avatar
    Join Date
    Feb 2002
    Location
    Trefforest
    Posts
    368
    That kind of works, but as you say it's slow. There must be a simpler way to be able to draw while animation is going on...
    MSVC++ 6.0

  13. #13
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,856
    Try something like this....
    Attached Files Attached Files
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

  14. #14
    train spotter
    Join Date
    Aug 2001
    Location
    near a computer
    Posts
    3,856
    Oppps.........


    Forgot that you must 'clear' the lne DC on each mouse move. (note to self, don't post untested code)

    BitBlt() the perm hdc to the line DC for the whole client rect, then draw the new line to the line DC.
    "Man alone suffers so excruciatingly in the world that he was compelled to invent laughter."
    Friedrich Nietzsche

    "I spent a lot of my money on booze, birds and fast cars......the rest I squandered."
    George Best

    "If you are going through hell....keep going."
    Winston Churchill

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Device & Rendering Context
    By hannibar in forum Windows Programming
    Replies: 1
    Last Post: 12-16-2005, 04:28 AM
  2. Clearing the Windows Device Context
    By loopshot in forum Game Programming
    Replies: 8
    Last Post: 11-17-2005, 09:51 AM
  3. Replies: 4
    Last Post: 06-30-2004, 03:11 PM
  4. Device Context
    By gvector1 in forum Windows Programming
    Replies: 2
    Last Post: 03-25-2003, 07:38 AM
  5. Replies: 1
    Last Post: 05-09-2002, 07:14 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21