WINAPI your own keyboard onPress and onReleased - c++

Currently, I am creating my own function onPress and onRelease. However, I am having problem with my onRelease function. Apparently, my onRelease kept on triggering even if I have not release my keyboard.
I suspected it has to do with the number of cycle inside the CPU but I wasn't sure of this theory. Somehow, maybe the cycle is slower than my frame, that why the PeerMessage return false? As no event was triggered.
Solution to it:
**Under the WinProc function create a new case called WM_KEYUP. This event will trigger once the user leave the button. It help to solve the number of cycle inside the CPU issued.
**
*Note:
I have update my detail of my code.
Description. Window Programming
I have two std::array that store my keyboard data
1) InputCurr
2) InputPrev
std::array<char, 256> inputPrev;
std::array<char, 256> inputCurr;
While(TRUE) {
std::copy(InputCurr.begin(), InputCurr.end(), InputPrev.begin());
inputCurr.fill(0);
while(PeekMessage (&uMsg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage (&uMsg);
DispatchMessage (&uMsg);
}
if(onReleased(x030)) //Button 0
//do something
}
char onReleased(char key)
{
return (inputCurr[key] && !inputPrev[key])? 1 : 0;
}
void VEInputMessage(WPARAM key)
{
inputCurr[key]= 1; //Set to true of the keyboard key
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC dc; /* device context */
PAINTSTRUCT ps; /* the paint struct */
RECT rect;
UNREFERENCED_PARAMETER(rect);
switch (msg)
{
/* when the window is created */
case WM_CREATE:
break;
/* when the rectangle is drawn */
case WM_LBUTTONDOWN:
break;
case WM_MOUSEMOVE:
break;
case WM_PAINT:
dc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
/* When it's time for the window to be closed and removed */
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
VEINPUT::VEInputMessage(wParam); //Updated the input key
if(wParam == AEVK_ESCAPE)
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}

Related

How to change textout() when it's already drawn?

I'm trying to move static TCHAR greeting[] = _T("123"); by using arrows, but it doesn't move at all. MessageBox is used as a confirmation of getting keyboard input.
void move(HWND hWnd, WPARAM wParam, LPARAM lParam, unsigned int *x, unsigned int * y) {
RECT rt;
if (wParam == VK_LEFT) {
GetClientRect(hWnd, &rt);
x = x - 5;
InvalidateRect(hWnd, &rt, FALSE);
MessageBox(hWnd, LPCSTR("123"), LPCSTR("123"), MB_OK);
}
if (wParam == VK_DOWN) {
GetClientRect(hWnd, &rt);
y = y - 5;
InvalidateRect(hWnd, &rt, FALSE);
}
if (wParam == VK_UP) {
GetClientRect(hWnd, &rt);
y = y + 5;
InvalidateRect(hWnd, &rt, FALSE);
}
if (wParam == VK_RIGHT) {
GetClientRect(hWnd, &rt);
x = x + 5;
InvalidateRect(hWnd, &rt, FALSE);
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
static TCHAR greeting[] = _T("123");
unsigned int x = 50;
unsigned int y = 50;
switch (message)
{
case WM_PAINT: {
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc,
x, y,
greeting, 3);
EndPaint(hWnd, &ps);
break;
}
case WM_KEYDOWN:
move(hWnd, wParam, lParam, &x, &y);
break;
case WM_DESTROY: {
PostQuitMessage(0);
break;
default: {
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
}
return 0;
}
}
Can somebody explain, what is wrong here? I'm still trying to learn about <Windows.h> so pls don't try to make the code much more complicated.
The x and y variables you are using to draw the text with are local to WndProc and are always reset to the initial values whenever a new message is received. You need to move their declarations outside of WndProc() (or at least make them static) so that their values can persist between messages. You can then update their values in your WM_KEYDOWN handler, and use their current values to draw the text in your WM_PAINT handler.
Also, your move() function is updating the pointers that are pointing at the x and y variables, it is not updating the values of the x and y variables themselves.
Try this instead:
void move(HWND hWnd, WPARAM wParam, LPARAM lParam, unsigned int *x, unsigned int * y) {
switch (wParam) {
case VK_LEFT:
*x -= 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_DOWN:
*y -= 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_UP:
*y += 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
case VK_RIGHT:
*x += 5;
InvalidateRect(hWnd, NULL, FALSE);
break;
}
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
static TCHAR greeting[] = _T("123");
static unsigned int x = 50;
static unsigned int y = 50;
switch (message) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
TextOut(hdc,
x, y,
greeting, 3);
EndPaint(hWnd, &ps);
break;
}
case WM_KEYDOWN:
move(hWnd, wParam, lParam, &x, &y);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
While #Remy Lebeau's answer is right, I don't think I'd write the move function quite the way he has. I'd pass x and y by reference instead of using pointers, and I'd consolidate more of the code together. Oh, and since it doesn't use lParam, we might as well not pass it at all:
void move(HWND hWnd, WPARAM wParam, unsigned int &x, unsigned int & y) {
switch (wParam) {
case VK_LEFT:
x -= 5;
break;
case VK_DOWN:
y -= 5;
break;
case VK_UP:
y += 5;
break;
case VK_RIGHT:
x += 5;
break;
default: return;
}
InvalidateRect(hWnd, NULL, FALSE);
}
Also note, however that this has a little bit of a problem. Passing FALSE as the last parameter to InvalidateRect means it doesn't erase the background. If you move your string around much, you're probably going to end up with artifacts of the movement--fragments of the string showing up in both the previous and the current location.
One easy way to fix that is change the final parameter to TRUE. To make that a bit more efficient, you can calculate the rectangle covering the old and new locations of the string, and invalidate only that much. Alternatively, invalidate the old rectangle with TRUE and the new rectangle with FALSE, and Windows will figure things out from there.
Another possibility (slightly more work, but under the right circumstances it can pay off) is to create an off-screen bitmap containing both your message and a border in the background color that's large enough to cover up the message in its previous location. Then when you move it around, you just blit that bitmap to the screen.
That obviously only works if your background is a solid, uniform color though.

GetWindowLong loses data of fields

I do a wrapper for Window and want to call window methods from WndProc.
For this I pass 'this' pointer to CreateWindwEx function.
In WndProc I assign hWnd field of Window class and store it by SetWindowLongPtr
But when I try to read it by GetWindowLong I get broken instance of the window (all the fields have undefined values), however I can call w->foo()
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_NCCREATE:
{
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
Window* self = static_cast<Window*>(lpcs->lpCreateParams);
// self->m_hInst and self->m_szWindowClass are set properly
// lets set hWnd
self->m_hWnd = hWnd;
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(self));
return true;
}
case WM_COMMAND:
{
Window* w = reinterpret_cast<Window*>(GetWindowLong(hWnd, GWLP_USERDATA));
// got all the windows fields broken, though 'w' pointer itself is valid
w->foo();
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
//DialogBox(m_hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
Window* w = reinterpret_cast<Window*>(GetWindowLong(hWnd, GWLP_USERDATA));
w->foo();
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;
}
void foo()
{
//auto d = m_hInst;
auto s = m_hWnd;
}
My fault!
GetWindowLongPtr should be used instead of GetWindowLong

In win32 api using c++ i want to track double click and triple click and so on

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

Why doesn't drawing onto the hdc immediately update the window?

According to this website, any drawing operation performed on the HDC returned by BeginPaint will immediately display on the screen. However, the number printed by the following code only updates when the window is resized:
int counter = 0;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
std::string s = std::to_string(counter).c_str();
TextOutA(hdc, 0, 0, s.c_str(), s.length());
EndPaint(hWnd, &ps);
}
break;
case WM_KEYDOWN:
counter++;
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Even when continuously sending WM_PAINT to the window with
RedrawWindow(hwndMain, 0, 0, RDW_INTERNALPAINT);
after EndPaint, the number only updates when the window is resized. How can I get the number to update without manually resizing the window?
Calling RedrawWindow with different flags solved the problem.
Specifically RedrawWindow(hWnd, 0, 0, RDW_FRAME | RDW_INVALIDATE); after counter++;

PeekMessage not getting the message?

I've created a custom message type for use in resizing my Window, called WM_NEED_RESIZE. I've defined it in my .h file, and initialized in my .cpp file. I have also registered my WindowProc function to accept messages. Here is the code for these items:
const uint32 WindowsGLWindow::WM_NEED_RESIZE = WM_USER + 100;
LONG WINAPI WindowsGLWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;// do I need this?
static sint32 newWidth = 0;
static sint32 newHeight = 0;
bool res = false;
switch (uMsg) {
case WM_PAINT:
//display();
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
//glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
res = PostMessage(hWnd, WindowsGLWindow::WM_NEED_RESIZE, wParam, lParam);
std::cout << "WM_SIZE: " << res << std::endl;
return 0;
case WindowsGLWindow::WM_NEED_RESIZE:
std::cout << "WindowsGLWindow::WM_NEED_RESIZE" << std::endl;
break;
case WM_CHAR:
switch (wParam) {
case 27: /* ESC key */
PostQuitMessage(0);
break;
}
return 0;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
In another function I am running through PeekMessage(..) to collect all messages. Here is the snippet of the message pump:
MSG msg;
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE) == TRUE) // maybe use GetInputState(?) as well?
{
if (msg.message == WM_QUIT)
retVal = -1;
if (msg.message == WindowsGLWindow::WM_NEED_RESIZE) {
uint32 newWidth = LOWORD(msg.lParam);
uint32 newHeight = HIWORD(msg.lParam);
std::cout << "PeekMessage: WindowsGLWindow::WM_NEED_RESIZE" << std::endl;
// call resize only if our window-size changed
if ((newWidth != width_) || (newHeight != height_)) {
resize(newWidth, newHeight);
}
PostMessage(msg.hwnd, WM_PAINT, 0, 0);
}
switch (msg.message) {
case WM_MOUSEMOVE:
// Retrieve mouse screen position
//int x = (short) LOWORD(lParam);
//int y = (short) HIWORD(lParam);
// Check to see if the left button is held down:
//bool leftButtonDown = wParam & MK_LBUTTON;
// Check if right button down:
//bool rightButtonDown = wParam & MK_RBUTTON;
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_KEYUP:
case WM_KEYDOWN:
/*
switch (msg.wParam) {
case 'W':
// w key pressed
break;
case VK_RIGHT:
// Right arrow pressed
break;
default:
break;
}
*/
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
My problem is that the WM_NEED_RESIZE message is only found once in the message queue when the window first opens, after which it is never found in the message queue by my PeekMessage(..). I'm really not sure why this is happening. It is, however, being received by the WindowProc(..) method (which doesn't really help me). I would appreciate any help you guys could provide.
Thanks
Jarrett
Dont use std::cout expecting to see that output in your debugger, insted use OutputDebugString(); .
You need to pass your class pointer to the last parameter of your call to CreateWindowEx, then retrieve that pointer from the LPCREATESTRUCT passed to you in the LPARAM of WM_CREATE, your class pointer will be in the lpCreateParmas feild of the struct. Set your clas pointer to the GWLP_USERDATA of your window, and on any other message calls , call GetWindowsLong , retrieve your class pointer, then pass the message, wparam, and lparam all off to your internal class message handler.
http://msdn.microsoft.com/en-us/library/ff381400%28v=VS.85%29.aspx
The message pump loop that you are showing will exit as soon as the queue is empty. I can't tell from what you've posted if it ever gets entered again.
If this is your main message pump, you should use GetMessage() instead, as it will wait until something is available before returning. Take a look at this MSDN article for more info.