MacOS grayscale except some apps

accessibilitycolormacos

Is there anyway by which one can have grayscale system wide except some selected (by user) apps, on macOS?

Best Answer

Yes, this is possible, though my solution is a bit hacky. Note that this method uses some undocumented Objective C APIs, and doesn't go through ordinary accessibility settings, so be careful.

First you need to have developer tools installed so that you can compile Objective C code. Then compile two commands enable-grayscale and disable-grayscale from source (based on this StackOverflow answer which provides some additional ways of doing this).

$  cat enable-grayscale.c 
// https://stackoverflow.com/questions/14163788/how-does-on-screen-color-inversion-work-in-os-x
#include <stdio.h>
#include <ApplicationServices/ApplicationServices.h>

CG_EXTERN bool CGDisplayUsesForceToGray(void);
CG_EXTERN void CGDisplayForceToGray(bool forceToGray);

int
main(int argc, char** argv)
{
    bool isGrayscale = CGDisplayUsesForceToGray();
        if (!isGrayscale) {
                CGDisplayForceToGray(TRUE);
        }

    return 0;
}

$ cat disable-grayscale.c 
// https://stackoverflow.com/questions/14163788/how-does-on-screen-color-inversion-work-in-os-x
#include <stdio.h>
#include <ApplicationServices/ApplicationServices.h>

CG_EXTERN bool CGDisplayUsesForceToGray(void);
CG_EXTERN void CGDisplayForceToGray(bool forceToGray);

int
main(int argc, char** argv)
{
    bool isGrayscale = CGDisplayUsesForceToGray();
        if (isGrayscale) {
        CGDisplayForceToGray(0);
        }

    return 0;
}

Then compile them into commands:

clang -g -O2 -std=c11 -Wall -framework ApplicationServices enable-grayscale.c -o enable-grayscale
clang -g -O2 -std=c11 -Wall -framework ApplicationServices disable-grayscale.c -o disable-grayscale

Put the resulting files somewhere you want to run them, e.g. your home directory.

Now install Hammerspoon. You'll need to give it accessibility access in System Preferences. Within Hammerspoon, select "Open Config", and modify the Lua code to do what you want. My configuration is below, based on this example from their docs:

function applicationWatcher(appName, eventType, appObject)
    if (eventType == hs.application.watcher.activated) then
        if (appName == "Firefox") then
            os.execute('/path/to/scripts/enable-grayscale')
        end
    end
    if (eventType == hs.application.watcher.deactivated) then
        if (appName == "Firefox") then
            os.execute('/path/to/scripts/disable-grayscale')
        end
    end
end
appWatcher = hs.application.watcher.new(applicationWatcher)
appWatcher:start()

That's the configuration I chose (turn on grayscale for Firefox, but leave it off for all other applications). Whenever Firefox is focused, the whole screen becomes grayscale. When other applications are focused, it's color. It sounds like you'd want the opposite: grayscale turned on in System Preferences, with it being disabled when you focus an app from the list you're interested in. Once you've configured it as you want, click "Reload Config" to activate the changes.