Monitor callback never fired

Hi, the monitor callback set with glfwSetMonitorCallback() is never fired on my system.

I have a nVidia GTX970 with 353.06 drivers on Win 7 x64.

I already tried this at the time with GLFW 3.1.2 but then I saw this was supposed to be fixed in 3.2, so I tried again but still doesn’t work.

[Win32] Bugfix: Monitor connection and disconnection events were not reported when no windows existed
[Win32] Bugfix: Activating or deactivating displays in software did not trigger monitor callback

I tried from my own program but then I noticed the events.c test program is supposed to test the monitor attach/detach events too and report them, so I tried with that one too.

I tried to enable/disable a second monitor from the nVidia control panel, the monitor is seen/removed as expect from the OS but no event is fired in the events test program.

I also tried to physically connect/disconnect the monitor from the graphic card, no changes.

I also tried to manually call glfwGetMonitors() in my code (by pressing a function key) after I attached/detached the second monitor, but the list of reported monitors still doesn’t change and it’s always the one detected at the moment the program was launched.

If at least this manual refresh were working would be enough for me, even if not optimal like the callback.

I have to exit the program and relaunch it to make glfwGetMonitors() aware of the changed configuration.

Can anyone else confirm this ?

I did some experiment, I wrote a small program which creates a window with win32 API.
In winproc I’m testing for

WM_DEVICECHANGE
and
WM_DISPLAYCHANGE

NO RegisterDeviceNotification()

I get a WM_DEVICECHANGE when I connect/disconnect a USB joystick
I get a WM_DISPLAYCHANGE when I add/remove a monitor in the nVidia control panel, but no WM_DEVICECHANGE.

EDIT:

The only way which seem to work on my system is to change windowProc() in win32_windows.c adding WM_DISPLAYCHANGE to the switch() dedicated to the MAIN window (not the helper one).

// Window callback function (handles window messages)
//
static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
                                   WPARAM wParam, LPARAM lParam)
{
    _GLFWwindow* window = GetPropW(hWnd, L"GLFW");
    if (!window)
    {
        // This is the message handling for the hidden helper window

        switch (uMsg)
        {
        ...
        }

        switch (uMsg)
        {
            case WM_DISPLAYCHANGE:
            {        
                _glfwInputMonitorChange();
                return 0;
            }

            case WM_SETFOCUS:
           {
           ...
          etc
          ...

This way the callback work and the monitors list is refreshed.

But this way I imagine the callback will be called even at a simple screen resolution change, maybe it could be possible to do something in _glfwInputMonitorChange() and verify if the monitors list is really changed.
If it is call the callback else skip it ?

Anyway, is this happening to someone else ?.

I’ve just tested this and on my Windows 8.1 system with Radeon drivers and latest GLFW 3.2 from Github I get the same as you.

Investigating further it appears the WM_DEVICECHANGE message is being sent with a valid window on my system, so copying the code for device change (testing DBT_DEVNODES_CHANGED only) outside the if(!window) and in the second switch works for me:

static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    _GLFWwindow* window = GetPropW(hWnd, L"GLFW");
    if (!window)
    {
        // This is the message handling for the hidden helper window

        switch (uMsg)
        {
            case WM_DEVICECHANGE:
            {
                if (wParam == DBT_DEVNODES_CHANGED)
                {
                    _glfwInputMonitorChange();
                    return TRUE;
                }
                else if (wParam == DBT_DEVICEARRIVAL)
                {
                    DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
                    if (dbh)
                    {
                        if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
                            _glfwDetectJoystickConnectionWin32();
                    }
                }
                else if (wParam == DBT_DEVICEREMOVECOMPLETE)
                {
                    DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam;
                    if (dbh)
                    {
                        if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
                            _glfwDetectJoystickDisconnectionWin32();
                    }
                }

                break;
            }
        }

        return DefWindowProcW(hWnd, uMsg, wParam, lParam);
    }

    switch (uMsg)
    {
        // following case added here
        case WM_DEVICECHANGE:
        {
            if (wParam == DBT_DEVNODES_CHANGED)
            {
                _glfwInputMonitorChange();
                return TRUE;
            }
        }
        case WM_SETFOCUS:
        \\ rest of func removed for clarity

If you have an account you can file this on the GLFW Github issue list or I can do that for you.

@dougbinks

Thanks for checking !

I tried your modification but it doesn’t work on my system, as I wrote the WM_DEVICECHANGE message it’s not sent at all to my window when attaching/detaching a display, so we have two different behaviors here.
It’s really strange, but I get WM_DISPLAYCHANGE only.

Looks tricky to make this thing work everywhere.

I created an account (as promised the last time :slight_smile: ) so I’ll post a report myself after lunch and link it here.

Thanks again for your time.

EDIT: reported here -> https://github.com/glfw/glfw/issues/784

This should be fixed now.
https://github.com/glfw/glfw/commit/97dbd8b63bbd15bb781e54a670b183dbd59f1bf0