c++windowswinapiaccessibilitysystem-tray

Detect white or black high contrast mode in Windows C++


The MS App Assure team reported to me an issue where my app's notification area/system tray icon is nearly invisible on white high contrast themes (or near-white like Windows 11's "Desert" theme).

I already have a dark icon I use when the (normal, non high-contrast) Windows light theme is on so I would like to use it in these scenarios as well.

The only issue is, while I can detect if high contrast mode is on with SystemParametersInfo, I haven't found anything to detect if it's a white or a black high contrast theme. How would I proceed to detect that?

I know MSIX packages support having different icons for white and black high contrast themes, so how do they detect it?


Solution

  • First, check if High Contrast mode is on:

    HIGHCONTRAST info = { .cbSize = sizeof(info) };
    if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &info, 0) && info.dwFlags & HCF_HIGHCONTRASTON)
    {
        // it's on
    }
    

    Then, you check the relative luminance of GetSysColor(COLOR_WINDOWTEXT). If it's lower than or equal to 0.5, then use a dark icon for your tray:

    double Luminance(COLORREF color)
    {
        const uint8_t R = GetRValue(color);
        const uint8_t G = GetGValue(color);
        const uint8_t B = GetBValue(color);
    
        const double rg = R <= 10 ? R / 3294.0 : std::pow((R / 269.0) + 0.0513, 2.4);
        const double gg = G <= 10 ? G / 3294.0 : std::pow((G / 269.0) + 0.0513, 2.4);
        const double bg = B <= 10 ? B / 3294.0 : std::pow((B / 269.0) + 0.0513, 2.4);
    
        return (0.2126 * rg) + (0.7152 * gg) + (0.0722 * bg);
    }
    
    if (Luminance(GetSysColor(COLOR_WINDOWTEXT)) <= 0.5)
    {
        // use dark icon
    }
    else
    {
        // use light icon
    }