keystroke handling

closed account (zwA4jE8b)
I have the following code. I want to keystrokes listed in case WM_KEYDOWN to change the value of a variable in the main function.

I do not yet know too much windows programming. So my question is.. How do I update the value of the variable in main when one of the four keys is hit?

I want up and down to change _amp and left and right to change _sTime.
Also, I do not want to make the variables global unless it is necessary.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
	HWND hWnd;
	WNDCLASSEX wc;
	HDC hDC;
	double _amp = 50; 
	int _sTime = 5;

	ZeroMemory(&wc, sizeof(WNDCLASSEX));

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = (HICON)LoadImage(NULL, "C:\\Visual Studio 2010\\Icons\\Smiley.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
	wc.lpszClassName = "WindowClass";

	RegisterClassEx(&wc);

	hWnd = CreateWindowEx(NULL,
                          "WindowClass",
                          "Wave",
                          WS_OVERLAPPEDWINDOW,
                          0, 45,
                          screen_width, screen_height,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

	ShowWindow(hWnd, nCmdShow);
	hDC = GetDC(hWnd);

	MSG msg;

	while(TRUE)
	{
		while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		if(msg.message == WM_QUIT)
			break;
		
		wavefunc(hDC, _amp);
		ScrollWindow(hWnd, -1, 0, NULL, NULL);
		Sleep(_sTime);
	}

	return msg.wParam;
}



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		} break;
		case WM_KEYDOWN:
		{
			switch (wParam)
			{
			case VK_UP:
				
				break;
			case VK_DOWN:

				break;

			case VK_LEFT:

				break;
			case VK_RIGHT:

				break;
			}
			return 0;
		} break;
     }
     return DefWindowProc(hWnd, message, wParam, lParam);
 }


Thank you,
Mike
closed account (zwA4jE8b)
I am thinking of using PostMessage() to return a message and then handle it in main.
Well, you need something global unless WinMain() and WindowProc() were in the same class. Since it is not the case, you simply need global variables.

If the design of the application dictates that these variables can logically belong to an instance of a class, then you can encapsulate them inside a class, but you will still need a global variable of that class so it is accessible to both functions.

As an alternative (albeit not a great one), you could use the following if both WinMain() and WindowProc() are in the same CPP file:

1
2
static double _amp;
static int _sTime;


They are basically "global" variables but only available to the functions in the CPP file where they are declared.
closed account (zwA4jE8b)
Thank you, that seems to work well. Another question, Is Sleep() a good function to use in this case? Basically I do not want it to draw too fast and Sleep() is the only way I know so far to slow it down.

Here is my entire code...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <Windows.h>
#include <math.h>

#define screen_width 800
#define screen_height 600
#define pi 3.14159

COLORREF blue = RGB(0, 0, 255);

double _amp = 50; 
int _sTime = 5;

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void wavefunc(HDC, double);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
	HWND hWnd;
	WNDCLASSEX wc;
	HDC hDC;

	ZeroMemory(&wc, sizeof(WNDCLASSEX));

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = (HICON)LoadImage(NULL, "C:\\Visual Studio 2010\\Icons\\Smiley.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
	wc.lpszClassName = "SineWave";

	RegisterClassEx(&wc);

	hWnd = CreateWindowEx(NULL,
                          "SineWave",
                          "Wave",
                          WS_OVERLAPPEDWINDOW,
                          0, 45,
                          screen_width, screen_height,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

	ShowWindow(hWnd, nCmdShow);
	hDC = GetDC(hWnd);

	MSG msg;

	while(TRUE)
	{
		while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		if(msg.message == WM_QUIT)
			break;
		
		wavefunc(hDC, _amp);
		ScrollWindow(hWnd, -1, 0, NULL, NULL);
		Sleep(_sTime);
	}

	return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		} break;
		case WM_KEYDOWN:
		{
			switch (wParam)
			{
			case VK_UP:
				_amp--;
				break;
			case VK_DOWN:
				_amp++;
				break;
			case VK_LEFT:
				_sTime++;
				if(_sTime > 50)
					_sTime = 50;
				break;
			case VK_RIGHT:
				_sTime--;
				if(_sTime < 1)
					_sTime = 1;
				break;
			}
			return 0;
		} break;
     }
     return DefWindowProc(hWnd, message, wParam, lParam);
 }

void wavefunc(HDC hDC, double _amp)
{
	static int _x = 0;
	int _y;
	_y = sin(_x/_amp)*100 + 300;
	SetPixel(hDC, 600, _y, blue);
	_x += 1;
}
Last edited on
Sleep() is alright. You must be aware, though, that Sleep() takes the number of milliseconds. 5 is way small. Besides, you should know that Sleep() will make your thread relinquish any processor slice time remaining and may actually not wait for the specified amount of time if such time is too small a time. Practically, Sleep() has a minimum of some 10 milliseconds, if I recall correctly, so Sleep(10) is the same as Sleep(1).

Furthermore, I think I would have encapsulated the code that animates the wave inside a function, then I would have provided menu items/buttons to start/stop the animation. I would have then used a standard message pump (one that uses GetMessage() and not PeekMessage()), and I would have had the "infinite" loop inside the animation function. The animation function would have had a call to PeekMessage() and would have processed the keystrokes in a separate "window stored procedure" specific for processing keystrokes in animation mode.

The above should then allow the main window procedure to take care of other, more standard stuff, and should then make your application closer to standards.
closed account (zwA4jE8b)
I only partially understand what you are talking about.

Can you give me a general template of what that would look like?
Would my wave drawing be done in the callback function?
closed account (zwA4jE8b)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <Windows.h>
#include <math.h>

#define screen_width 800
#define screen_height 600
#define pi 3.14159

COLORREF blue = RGB(0, 0, 255);

double _amp = 50; 
int _sTime = 5;

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void wavefunc(HWND, double);

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
	HWND hWnd;
	WNDCLASSEX wc;

	ZeroMemory(&wc, sizeof(WNDCLASSEX));

	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WindowProc;
	wc.hInstance = hInstance;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hIcon = (HICON)LoadImage(NULL, "C:\\Visual Studio 2010\\Icons\\Smiley.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
	wc.lpszClassName = "SineWave";

	RegisterClassEx(&wc);

	hWnd = CreateWindowEx(NULL,
                          "SineWave", "Wave",
                          WS_SYSMENU,
                          0, 45,
                          screen_width, screen_height,
                          NULL, NULL,
                          hInstance,
                          NULL);

	ShowWindow(hWnd, nCmdShow);

	wavefunc(hWnd, _amp);

	return 0;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		} break;
		case WM_KEYDOWN:
		{
			switch (wParam)
			{
			case VK_UP:
				_amp--;
				break;
			case VK_DOWN:
				_amp++;
				break;
			case VK_LEFT:
				_sTime++;
				if(_sTime > 50)
					_sTime = 50;
				break;
			case VK_RIGHT:
				_sTime--;
				if(_sTime < 1)
					_sTime = 1;
				break;
			}
			return 0;
		} break;
     }
     return DefWindowProc(hWnd, message, wParam, lParam);
 }

void wavefunc(HWND hWnd, double _amp)
{
	HDC hDC = GetDC(hWnd);
	MSG msg;

	while(TRUE)
	{
		while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		if(msg.message == WM_QUIT)
			break;

		static double _x = 0;
		int _y;
		_y = sin(_x/_amp)*100 + 300;
		SetPixel(hDC, 600, _y, blue);
		ScrollWindow(hWnd, -1, 0, NULL, NULL);
		Sleep(_sTime);
		_x += 1;
	}
}


The left and right keys works to change the speed, but now the up and down keys do nothing.
In line 53 I would have:

1
2
3
4
5
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}


And that's it.

I would also add a menu or button to start or stop the animation. the main window procedure would control only the start one.

1
2
3
case WM_COMMAND:
    if (LOWORD(wParam) == IDM_START_ANIMATION) StartAnimation();
    break;


StartAnimation() would then enter the "infinite" loop you currently have in line 53 but modified of course to only remove from the message queue those messages it actually cares about, namely the keystrokes and the new stop animation command, which would be a WM_COMMAND with LOWORD(wParam) == IDM_STOP_ANIMATION.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void StartAnimation()
{
    while (true)
    {
        while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            TranslateMessage(&msg);
            //Don't dispatch it.  Process it in the mini window procedure.
            if (AnimationWindowProc(&msg))
            {
                //Message processed.  Remove message with another call to PeekMessage().
            }
        }
        waveFunc();
        ScrollWindow();
        Sleep();
    }
}


The AnimationWindowProc() would look very similar to your main window procedure. You just need to first ensure the hwnd in the message is for the animation window. You would also return true if you process the message here so StartAnimation() can remove it from the queue.

With this method you can port your code into frameworks that use the standard message pump, for example.

Side effect: Now that I wrote this, there is a potentially undesired side effect. StartAnimation() will only process messages pertaining the animation window, and only those that affect the animation (arrow keystrokes + stop command). This will freeze any other windows that belong to the application. So maybe StartAnimation() should continue using DispatchMessage(), but that puts us back into square 1, unless you create a child window that draws the animation.

Also note that Peek/GetMessage() accept message filters. It could be useful to you.
closed account (zwA4jE8b)
Yeah, I have a lot to learn about windows programming.
Thank you.

I need to learn more about message handling. Do you know of any good sites?
Last edited on
closed account (zwA4jE8b)
So my current callback function handles messages regarding the windowclass 'wc'.
but I could create and register another one?

btw, the main structure winmain and the callback function I got from a directX tutorial. I did not write it. I was just trying to add the keyboard handling and the drawing of the sine wave.
Last edited on
Yes, the most modular approach would be to create and register a new window class with a new window procedure. Then, inside your main window you create a child window of this new class. That way you will be able to partition your animation requirements away from the main window procedure and the main message pump. This will give you more flexibility, I would say.

BTW, I DON'T program GUI most of the time. I just happen to know some stuff. I don't have much experience in the field.
closed account (zwA4jE8b)
besides for some VB stuff I have not done much gui either. I have only been programming for about 6 months. I am in school for it but still new.
Topic archived. No new replies allowed.