Search code examples
windowswinapiwindow

Is there a notification when a window is moved to another monitor?


I have a window that is refreshed by IDXGISwapChain1 with V-Sync off. I would like to manually control the present rate of the window to be as close as the current monitor refresh rate if possible.

In a scenario where two monitors with different refresh rate are connected to a PC, I want to get a notification when the window is moved from one monitor to another so I could change my present frame rate accordingly.

WM_DISPLAYCHANGE and WM_DPICHANGED may not cover this situation according to my knowledge. Please correct me if I'm wrong.

Update: Here is my code based on Strive's answer,

LRESULT OnPositionChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    // Get the monitor handle for the window. 
    HMONITOR currentMonitor = MonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST);
    // error handling

    // no need to bother if the window stays on the same monitor
    if (_currentMonitor != currentMonitor)
    {
        // Get more information about the monitor
        MONITORINFOEX monitorInfo;
        monitorInfo.cbSize = sizeof(MONITORINFOEX);
        BOOL succeed = GetMonitorInfo(currentMonitor, (LPMONITORINFO)&monitorInfo);
        // error handling

        // Get the current display settings for that monitor, which includes the refresh rate
        DEVMODE devMode;
        devMode.dmSize = sizeof(DEVMODE);
        succeed = EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
        // error handling

        // use devMode.dmDisplayFrequency to update my present frame rate
        // ...

        _currentMonitor = currentMonitor;
    }

    return 0L;
}

LRESULT OnDisplayChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    // WM_DISPLAYCHANGE message is sent to all windows when the display resolution has changed
    // When a WM_DISPLAYCHANGE message is sent, any monitor may be removed from the desktop and thus its HMONITOR becomes invalid or has its settings changed.

    _currentMonitor = NULL;

    return 0L;
}

I want to change the frame rate after the window changed its position, so I switched to WM_WINDOWPOSCHANGED.


Solution

  • After the window position has changed, you can check the WM_WINDOWPOSCHANGING message.

    Sent to a window whose size, position, or place in the Z order is about to change as a result of a call to the SetWindowPos function or another window-management function.

    Then get the refresh hertz of the current monitor by calling the EnumDisplaySettings function. If the refresh Hz changes, change the refresh Hz of the application.

    dmDisplayFrequency :Specifies the frequency, in hertz (cycles per second), of the display device in a particular mode. This value is also known as the display device's vertical refresh rate. Display drivers use this member. It is used, for example, in the ChangeDisplaySettings function. Printer drivers do not use this member.

    Note: You can set a variable to save the refresh Hz of the previous display, and then compare the variable with the Hz of the current display. If it is different, it means that the Hz has changed.

    If you need to determine which monitor the window is on, you can use the MonitorFromWindow function.

    Here is a code sample: How to determine which monitor an application is using and how to get a handle to it?