Skip to content

Add Monterey (macOS 12.0+) support #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: 1.x-macos12-compat
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


# Stillcolor for macOS
<img src="https://github.com/aiaf/Stillcolor/assets/119462/26f4fe39-44bb-436d-9348-fc5ba9e8dfde" align=left width=256>
Save your eyesight and disable temporal dithering on your Mac with Stillcolor, a lightweight menu bar app for macOS running on Apple M1/M2/M3.
Expand All @@ -15,14 +13,14 @@ Save your eyesight and disable temporal dithering on your Mac with Stillcolor, a

These sensitivities manifest as eyestrain and fatigue, dry eyes, headache, nausea, inability to focus, and other physical symptoms.

There's even a [petition](https://www.change.org/p/apple-add-accessibility-options-to-reduce-eye-strain-and-support-vision-disability-sufferers) urging Apple to make use of these technologies known and to implement accessbility options.
There's even a [petition](https://www.change.org/p/apple-add-accessibility-options-to-reduce-eye-strain-and-support-vision-disability-sufferers) urging Apple to make use of these technologies known and to implement accessibility options.

While there are apps and accessories to help dim blue light, and plenty of flicker-free monitors, temporal dithering can happen at the GPU level with no visible option to disable it (such as the case in Apple silicon Macs).
While there are apps and accessories to help dim blue light, and plenty of flicker-free monitors, temporal dithering can happen at the GPU level with no visible option to disable it (such as the case in Apple Silicon Macs).

Stillcolor allows you to disable GPU/DCP-generated temporal dithering from user space, helping massively reduce eyestrain with little to no degradation in image quality.

## Caveats
Note that while Stillcolor is 100% confirmed to remove GPU/DCP-generated temporal dithering, which is applied directly to the pixel framebuffer right before it's sent to the external/embedded display, the display panel's timing contoller (TCON) may still apply its own dithering/FRC to achieve advertised color bit depth. Whether or not Apple displays actively use TCON dithering in addition to DCP/GPU dithering is under investigation.
Note that while Stillcolor is 100% confirmed to remove GPU/DCP-generated temporal dithering, which is applied directly to the pixel framebuffer right before it's sent to the external/embedded display, the display panel's timing controller (TCON) may still apply its own dithering/FRC to achieve advertised color bit depth. Whether Apple displays actively use TCON dithering in addition to DCP/GPU dithering is under investigation.


## Story and write-up
Expand All @@ -34,8 +32,8 @@ See this timeblend video of how your screen looks like with temporal dithering v
[https://www.youtube.com/watch?v=D9AZqJH-U-U](https://www.youtube.com/watch?v=D9AZqJH-U-U)

## Requirements
- Apple silicon Mac e.g. M1/M2/M3
- macOS >= 13
- Apple Silicon Mac e.g. M1/M2/M3
- macOS >= 12.0

## Installation
Head over to [Releases](https://github.com/aiaf/Stillcolor/releases) and download the latest zip.
Expand All @@ -46,7 +44,7 @@ Select “Launch a login” to make this app run automatically and disable dithe

## Verifying status of temporal dithering

To check wether the app did the job, run the following in Terminal:
To check whether the app did the job, run the following in Terminal:

`ioreg -lw0 | grep -i enableDither`

Expand All @@ -58,16 +56,15 @@ To re-enable dithering simply uncheck “Disable Dithering.”

To verify that your GPU is not applying dithering you can try a visual test by visiting [Lagom LCD Gradient (banding) test](http://www.lagom.nl/lcd-test/gradient.php)

Set your built-in display’s color profile to sRGB at full brightness and look carefully at the gray parts, you should be able to see subtle banding when you disable dithering which happens in realtime.
Set your built-in display’s color profile to sRGB at full brightness and look carefully at the gray parts, you should be able to see subtle banding when you disable dithering which happens in real-time.

And if you're sensitive to temporal dithering you should notice a lot less eyestrain while dithering is disabled.

A more complicated approach is to use a [video capture card](https://www.blackmagicdesign.com/products/ultrastudio/techspecs/W-DLUS-12) and record your display's uncompressed output then run the recording through ffmpeg to visualize dithering with something like the following command:
A more complicated approach is to use a [video capture card](https://www.blackmagicdesign.com/products/ultrastudio/techspecs/W-DLUS-12) and record your display's uncompressed output. You can then process the recorded footage with FFmpeg to visualize dithering using a command like the following:

`ffmpeg -i input.mov -sws_flags full_chroma_int+bitexact+accurate_rnd -vf "format=gbrp,tblend=all_mode=grainextract,eq=contrast=-60" -c:v v210 -pix_fmt yuv422p10le diff.mov`

## Roadmap
- Make this app compatible macOS 11+
- Create a foolproof and easy dithering test
- Intel Macs?
- iOS?
Expand Down
57 changes: 41 additions & 16 deletions Stillcolor.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objectVersion = 60;
objects = {

/* Begin PBXBuildFile section */
2DB2FB672C92383600158943 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 2DB2FB662C92383600158943 /* LaunchAtLogin */; };
384543E92BB5F0EB00E9C709 /* IORegistryPropertyHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 384543E82BB5F0EB00E9C709 /* IORegistryPropertyHelper.swift */; };
3865859A2B8B55CC00812B02 /* StillcolorApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 386585992B8B55CC00812B02 /* StillcolorApp.swift */; };
3865859E2B8B55CD00812B02 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3865859D2B8B55CD00812B02 /* Assets.xcassets */; };
386585C62B8B8A1F00812B02 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 386585C52B8B8A1F00812B02 /* LaunchAtLogin */; };
386585C82B8BF53300812B02 /* Stillcolor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 386585C72B8BF53300812B02 /* Stillcolor.swift */; };
386585CA2B8BF5AC00812B02 /* ScreenDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 386585C92B8BF5AC00812B02 /* ScreenDetector.swift */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -43,7 +43,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
386585C62B8B8A1F00812B02 /* LaunchAtLogin in Frameworks */,
2DB2FB672C92383600158943 /* LaunchAtLogin in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -98,6 +98,7 @@
386585922B8B55CC00812B02 /* Sources */,
386585932B8B55CC00812B02 /* Frameworks */,
386585942B8B55CC00812B02 /* Resources */,
2D1CAE382C922F0700B84AA6 /* Launch at Login Helper */,
386585BF2B8B880D00812B02 /* CopyFiles */,
);
buildRules = (
Expand All @@ -106,7 +107,7 @@
);
name = Stillcolor;
packageProductDependencies = (
386585C52B8B8A1F00812B02 /* LaunchAtLogin */,
2DB2FB662C92383600158943 /* LaunchAtLogin */,
);
productName = HonestColor;
productReference = 386585962B8B55CC00812B02 /* Stillcolor.app */;
Expand All @@ -129,7 +130,7 @@
};
};
buildConfigurationList = 386585912B8B55CC00812B02 /* Build configuration list for PBXProject "Stillcolor" */;
compatibilityVersion = "Xcode 14.0";
compatibilityVersion = "Xcode 15.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Expand All @@ -138,7 +139,7 @@
);
mainGroup = 3865858D2B8B55CC00812B02;
packageReferences = (
386585C42B8B8A1F00812B02 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */,
2DB2FB652C92383600158943 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */,
);
productRefGroup = 386585972B8B55CC00812B02 /* Products */;
projectDirPath = "";
Expand All @@ -160,6 +161,28 @@
};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
2D1CAE382C922F0700B84AA6 /* Launch at Login Helper */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Launch at Login Helper";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\"\n";
};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
386585922B8B55CC00812B02 /* Sources */ = {
isa = PBXSourcesBuildPhase;
Expand Down Expand Up @@ -212,7 +235,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
Expand All @@ -228,7 +251,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down Expand Up @@ -275,7 +298,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
Expand All @@ -285,7 +308,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
Expand All @@ -309,6 +332,7 @@
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Stillcolor;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
Expand All @@ -318,7 +342,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.makkuk.Stillcolor;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -346,6 +370,7 @@
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_CFBundleDisplayName = Stillcolor;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
Expand All @@ -355,7 +380,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.makkuk.Stillcolor;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -390,9 +415,9 @@
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
386585C42B8B8A1F00812B02 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */ = {
2DB2FB652C92383600158943 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-Modern";
repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-Legacy";
requirement = {
branch = main;
kind = branch;
Expand All @@ -401,9 +426,9 @@
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
386585C52B8B8A1F00812B02 /* LaunchAtLogin */ = {
2DB2FB662C92383600158943 /* LaunchAtLogin */ = {
isa = XCSwiftPackageProductDependency;
package = 386585C42B8B8A1F00812B02 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */;
package = 2DB2FB652C92383600158943 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */;
productName = LaunchAtLogin;
};
/* End XCSwiftPackageProductDependency section */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Stillcolor/ScreenDetector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ScreenDetector {
}


func addObervers() {
func addObservers() {
let userData = Unmanaged<ScreenDetector>.passUnretained(self).toOpaque()
CGDisplayRegisterReconfigurationCallback(ScreenDetector.callback, userData)
}
Expand Down
Loading