[BUG][JUCE6][WIN] Displays::findDisplays returns wrong WorkArea (screen size without taskbar) on secondary monitor(s)

Hi,

I’m currently working on the UX of a cross-platform standalone plugin and I’ve faced many problems with the way workarea (monitor size without taskbar and other potential system UI elements) is calculated by JUCE on Windows specifically.

I suspect the developer in charge for this wasn’t familiar with Windows as it is unusual on most OS to have a taskbar on each monitor. On Windows however it is the default behavior.

Anyway, the problem is pretty simple, in file juce/modules/juce_gui_basics/native/juce_win32_Windowing.cpp at line ~4580, in the Displays::findDisplays method we can see this :

if (d.isMain)
{
            RECT workArea;
            SystemParametersInfo (SPI_GETWORKAREA, 0, &workArea, 0);

            d.userArea = d.userArea.getIntersection (Rectangle<int>::leftTopRightBottom (workArea.left, workArea.top, workArea.right, workArea.bottom));
}

So indeed it’s not going to work with secondary monitor(s) because of the d.isMain condition. I understand why this statement has to be there since the SystemParametersInfo WINAPI call only works for the main monitor. What is surprising however, is that this function is called on each monitors returned by the EnumDisplayMonitors WINAPI call which already gives the workarea info we’re searching for both main and secondary monitor(s).

I fixed it on my side by replacing the r parameter in the last line of the enumMonitorsProc callback by info.rcWork like this :

static BOOL CALLBACK enumMonitorsProc (HMONITOR hm, HDC, LPRECT r, LPARAM userInfo)
{
    MONITORINFO info = {};
    info.cbSize = sizeof (info);
    GetMonitorInfo (hm, &info);
 
    auto isMain = (info.dwFlags & 1 /* MONITORINFOF_PRIMARY */) != 0;
    auto dpi = 0.0;

    if (getDPIForMonitor != nullptr)
    {
        UINT dpiX = 0, dpiY = 0;

        if (SUCCEEDED (getDPIForMonitor (hm, MDT_Default, &dpiX, &dpiY)))
            dpi = (dpiX + dpiY) / 2.0;
    }

	//PATCH used info.rcWork instead of r param
    ((Array<MonitorInfo>*) userInfo)->add ({ isMain, info.rcWork, dpi });
	//PATCH END
    return TRUE;
}

I also commented the first code extract I gave to prevent unnecessary calls.

Is there any reason it was implemented this way or is it, as I guessed earlier, a mistake/an unexpected behavior?

Regards,
Gaëtan Croquefer

1 Like

We still need to fill out the totalArea so using only rcWork won’t be sufficient, but we’ve added support for getting the user area on all displays here: