0

Maybe this is just a code organization issue, maybe there is some extremely fundamental C++ knowledge that I am lacking -- I have tried for hours to arrive at a reasonable solution, and nothing appears to work, so I am turning to the internet.

I am writing a Direct2D application for game development, and I am trying to get mouse coordinates from the WndProc lParam when there is a WM_MOUSEMOVE message, as is outlined and recommended in the MSDN article here. Here is how the relevant portions of the project are configured.

window.h

struct Window {
    HWND _hwnd;

    bool Initialize(int width, int height);
    void RunMessageLoop();
};

window.cpp

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_MOUSEMOVE:
        int xPosAbsolute = GET_X_PARAM(lParam); // as is suggested by MSDN
        int yPosAbsolute = GET_Y_PARAM(lParam); // as is suggested by MSDN
        ...
        break;
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

bool Window::Initialize(int width, int height) {
    WNDCLASS wc;
    wc.lpfnWndProc = WndProc;
    // etc...
}

void Window::RunMessageLoop() {
    MSG Msg;
    while (true)
    {
        GetMessage(&Msg, NULL, 0, 0);
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    };
}

main.cpp

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
    // ...
    while (true) {
        int absolute_mouse_pos_x = ???;
        int absolute_mouse_pos_y = ???;
        // etc...
    }
    return 0;
}

My question: How can I reasonably assign (and update) absolute_mouse_pos_x and absolute_mouse_pos_y in main.cpp with the xPosAbsolute/yPosAbsolute values from the WndProc function in window.cpp?

The first thing that came to mind was instancing the WndProc function so that it has access to the members of the Window struct, but this is impossible/impractical due to the signature of a member function having a hidden "this" parameter, as other answers on Stack Overflow such as this one have detailed.

After that, I tried creating global variables in window.h, assigning them in the WndProc function in window.cpp, and referencing them in main.cpp. main.cpp could use the initial values of both global variables, but updating those global variables with new values later seemed to be completely invisible to main.cpp (I am wondering if these globals were implicitly read-only, but this may just be lack of understanding/user error on my part). Aside from this behavior, common wisdom is that globals shouldn't be used unless absolutely necessary, and it seems weird to have to use globals for something so seemingly simple. Is there no simpler/better way?

Thanks!

Asesh
  • 3,186
  • 2
  • 21
  • 31
rtmath
  • 1
  • 4
  • See the example at [Managing Application State](https://learn.microsoft.com/en-us/windows/win32/learnwin32/managing-application-state-), and the top answer to [Use object method as WinApi WndProc callback](https://stackoverflow.com/questions/14661865/use-object-method-as-winapi-wndproc-callback) and . – dxiv Aug 19 '20 at 02:58
  • 1
    You can of course use global variables, although you have to do it properly - presumably you didn't (although you didn't show what you did). Another way would be to ignore the `WM_MOUSEMOVE` message and just call `GetCursorPos()` whenever you want the current cursor position. – Jonathan Potter Aug 19 '20 at 05:00
  • I think you can use `POINT cursor;` `GetCursorPos(&cursor); `to get the current mouse position information. Could you explain why you need to pass it through WndProc? – Zeus Aug 19 '20 at 07:35
  • @ZhuSong It doesn't necessarily need to be passed through WndProc for my own purposes, that just seems like what the recommended usage is based on the MSDN (at least for UI-style cursor coordinates). Thank you all for your help. This morning I tried both GetCursorPos() and the Get/SetWindowLongPtr methods. I'll try to post an answer with my results to help anyone else who is struggling with a similar thing. – rtmath Aug 19 '20 at 14:53

1 Answers1

0

Here are some possible ways to accomplish this.

  1. GetCursorPos()

This is very simple, and returns the screen coordinates of the cursor, not the coordinates within a window. This could be (un)desirable based on the application you're making. You can use ScreenToClient() to convert these coordinates to window coordinates.

Here is an example using main.cpp for simplicity, but could be done in any other translation unit.

main.cpp

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
    // ...
    POINT p;
    while (true) {
        GetCursorPos(&p);
        int absolute_mouse_pos_x = p.x;
        int absolute_mouse_pos_y = p.y;
        // etc...
    }
    return 0;
}
  1. Get/SetWindowLongPtr

This is more complex, but gives you window coordinates of the cursor (through the lParam of the WM_MOUSEMOVE message), and allows you to get/set state of a custom structure of your choice inside of WndProc. Here are the changes to window.h & window.cpp that are needed to implement this, and an example of accessing these variables from a separate translation unit (main.cpp).

window.h

struct Window {
    HWND _hwnd;
    int mouse_x;
    int mouse_y;

    bool Initialize(int width, int height);
    // etc..
};

window.cpp

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    Window* window;
    if (msg == WM_CREATE)
    {
        CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
        window = reinterpret_cast<Window*>(pCreate->lpCreateParams);
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window);
    }
    else
    {
        LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
        window = reinterpret_cast<Window*>(ptr);
    }


    switch (msg)
    {
    case WM_MOUSEMOVE:
        window->mouse_x = GET_X_PARAM(lParam);
        window->mouse_y = GET_Y_PARAM(lParam);
        break;
    // etc...
    }
    return 0;
}

bool Window::Initialize(int width, int height) {
    WNDCLASS wc;
    wc.lpfnWndProc = WndProc;
    // etc...

    CreateWindowEx(.., this); // add the "this" pointer as the last argument to CreateWindowEx()
}

main.cpp

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
    // ...
    Window w;
    w.Initialize(width, height);
    while (true) {
        int absolute_mouse_pos_x = w.mouse_x;
        int absolute_mouse_pos_y = w.mouse_y;
        // etc...
    }
    return 0;
}
rtmath
  • 1
  • 4
  • Note you can use `ScreenToClient()` to convert screen coordinates to client coordinates for a particular window. – Jonathan Potter Aug 19 '20 at 20:12
  • I am glad you have got your solution and thanks for your sharing, I would appreciate it if you mark them as answer and this will be beneficial to other community. – Zeus Aug 20 '20 at 00:50