-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
125 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import os | ||
import sys | ||
|
||
from .color.conversion import get_converter | ||
from .color.theme import current_theme | ||
|
||
|
||
def is_dark_theme() -> bool: | ||
""" | ||
Determine whether the current terminal theme is a dark theme. This function | ||
converts the default foreground and background colors of the current theme | ||
to the XYZ color space and then compares their Y, that is, luminance, | ||
components. If foreground has has higher luminance than the background, the | ||
terminal is using a dark therme. | ||
""" | ||
theme = current_theme() | ||
|
||
# Convert to XYZ | ||
foreground_xyz = get_converter(theme.text.tag, 'xyz')(*theme.text.coordinates) | ||
background_xyz = get_converter(theme.background.tag, 'xyz')( | ||
*theme.background.coordinates | ||
) | ||
|
||
# Compare luminance components | ||
return foreground_xyz[1] > background_xyz[1] | ||
|
||
|
||
def is_dark_mode() -> None | bool: | ||
""" | ||
Determine whether the operating system is in dark mode. | ||
Returns: | ||
``True`` for dark mode, ``False`` for light mode, and ``None`` if the | ||
mode could not be determined. | ||
The implementation builds on answers to [this StackOverflow | ||
question](https://stackoverflow.com/questions/65294987/detect-os-dark-mode-in-python) | ||
and [the darkdetect package](https://github.com/albertosottile/darkdetect). | ||
The latter seems both over- and under-engineered. In contrast, this module | ||
provides the one interesting bit, whether the system is in dark mode, if | ||
available and nothing else. | ||
""" | ||
try: | ||
if sys.platform == "darwin": | ||
return _is_darkmode_macos() | ||
elif sys.platform == "linux": | ||
return _is_darkmode_linux() | ||
elif os.name == 'nt': | ||
return _is_darkmode_windows() | ||
else: | ||
return None | ||
except: | ||
return None | ||
|
||
|
||
def _is_darkmode_windows() -> bool: | ||
import winreg | ||
|
||
with winreg.OpenKey( # type: ignore[attr-defined] | ||
winreg.HKEY_CURRENT_USER, # type: ignore[attr-defined] | ||
"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", | ||
) as key: # type: ignore | ||
return not winreg.QueryValueEx( # type: ignore[attr-defined] | ||
key, "AppsUseLightTheme" | ||
)[0] | ||
|
||
|
||
def _is_darkmode_macos() -> bool: | ||
import subprocess | ||
|
||
# Use DEVNULL so that output of command isn't shown | ||
return not subprocess.run( | ||
["defaults", "read", "-g", "AppleInterfaceStyle"], | ||
stdout=subprocess.DEVNULL, | ||
stderr=subprocess.DEVNULL, | ||
).returncode | ||
|
||
|
||
def _is_darkmode_linux() -> None | bool: | ||
import subprocess | ||
|
||
try: | ||
result = subprocess.run( [ | ||
"dbus-send", | ||
"--session", | ||
"--print-reply=literal", | ||
"--dest=org.freedesktop.portal.Desktop", | ||
"/org/freedesktop/portal/desktop", | ||
"org.freedesktop.portal.Settings.Read", | ||
"string:org.freedesktop.appearance", | ||
"string:color-scheme" | ||
], | ||
capture_output=True, | ||
encoding='utf8', | ||
check=True, | ||
) | ||
|
||
stdout = result.stdout.replace("variant", "").replace("uint32", "").strip() | ||
if stdout in ("0", "1", "2"): | ||
# 0 stands for default, 1 for prefers-dark, and 2 for prefers-light. | ||
# Ubuntu returns 0 for light mode and 1 for dark mode. | ||
return stdout == "1" | ||
except: | ||
pass | ||
|
||
try: | ||
result = subprocess.run( | ||
["gsettings", "get", "org.gnome.desktop.interface", "color-scheme"], | ||
capture_output=True, | ||
encoding="utf8", | ||
check=True, | ||
) | ||
stdout = result.stdout.strip() | ||
except: | ||
stdout = "" | ||
|
||
if not stdout: | ||
result = subprocess.run( | ||
["gsettings", "get", "org.gnome.desktop.interface", "gtk-theme"], | ||
capture_output=True, | ||
encoding="utf8", | ||
check=True, | ||
) | ||
stdout = result.stdout.strip() | ||
return stdout.strip("'").endswith("-dark") |