Skip to content
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
2 changes: 1 addition & 1 deletion napari/_vendor/darkdetect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Distributed under the terms of the 3-clause BSD License.
#-----------------------------------------------------------------------------

__version__ = '0.5.2'
__version__ = '0.6.0'

import sys
import platform
Expand Down
5 changes: 5 additions & 0 deletions napari/_vendor/darkdetect/_dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
# Distributed under the terms of the 3-clause BSD License.
#-----------------------------------------------------------------------------

import typing

def theme():
return None

Expand All @@ -12,3 +14,6 @@ def isDark():

def isLight():
return None

def listener(callback: typing.Callable[[str], None]) -> None:
raise NotImplementedError()
11 changes: 10 additions & 1 deletion napari/_vendor/darkdetect/_linux_detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#-----------------------------------------------------------------------------

import subprocess

import typing

def theme():
# Here we just triage to GTK settings for now
Expand All @@ -28,3 +28,12 @@ def isDark():

def isLight():
return theme() == 'Light'

def listener(callback: typing.Callable[[str], None]) -> None:
with subprocess.Popen(
('gsettings', 'monitor', 'org.gnome.desktop.interface', 'gtk-theme'),
stdout=subprocess.PIPE,
universal_newlines=True,
) as p:
for line in p.stdout:
callback('Dark' if line.strip().removeprefix("gtk-theme: '").removesuffix("'").endswith('-dark') else 'Light')
4 changes: 4 additions & 0 deletions napari/_vendor/darkdetect/_mac_detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import ctypes
import ctypes.util
import typing

try:
# macOS Big Sur+ use "a built-in dynamic linker cache of all system-provided libraries"
Expand Down Expand Up @@ -68,3 +69,6 @@ def isDark():

def isLight():
return theme() == 'Light'

def listener(callback: typing.Callable[[str], None]) -> None:
raise NotImplementedError()
100 changes: 99 additions & 1 deletion napari/_vendor/darkdetect/_windows_detect.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,61 @@
from winreg import HKEY_CURRENT_USER as hkey, QueryValueEx as getSubkeyValue, OpenKey as getKey

import ctypes
import ctypes.wintypes
import typing

advapi32 = ctypes.windll.advapi32

# LSTATUS RegOpenKeyExA(
# HKEY hKey,
# LPCSTR lpSubKey,
# DWORD ulOptions,
# REGSAM samDesired,
# PHKEY phkResult
# );
advapi32.RegOpenKeyExA.argtypes = (
ctypes.wintypes.HKEY,
ctypes.wintypes.LPCSTR,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD,
ctypes.POINTER(ctypes.wintypes.HKEY),
)
advapi32.RegOpenKeyExA.restype = ctypes.wintypes.LONG

# LSTATUS RegQueryValueExA(
# HKEY hKey,
# LPCSTR lpValueName,
# LPDWORD lpReserved,
# LPDWORD lpType,
# LPBYTE lpData,
# LPDWORD lpcbData
# );
advapi32.RegQueryValueExA.argtypes = (
ctypes.wintypes.HKEY,
ctypes.wintypes.LPCSTR,
ctypes.wintypes.LPDWORD,
ctypes.wintypes.LPDWORD,
ctypes.wintypes.LPBYTE,
ctypes.wintypes.LPDWORD,
)
advapi32.RegQueryValueExA.restype = ctypes.wintypes.LONG

# LSTATUS RegNotifyChangeKeyValue(
# HKEY hKey,
# WINBOOL bWatchSubtree,
# DWORD dwNotifyFilter,
# HANDLE hEvent,
# WINBOOL fAsynchronous
# );
advapi32.RegNotifyChangeKeyValue.argtypes = (
ctypes.wintypes.HKEY,
ctypes.wintypes.BOOL,
ctypes.wintypes.DWORD,
ctypes.wintypes.HANDLE,
ctypes.wintypes.BOOL,
)
advapi32.RegNotifyChangeKeyValue.restype = ctypes.wintypes.LONG

def theme():
""" Uses the Windows Registry to detect if the user is using Dark Mode """
# Registry will return 0 if Windows is in Dark Mode and 1 if Windows is in Light Mode. This dictionary converts that output into the text that the program is expecting.
Expand All @@ -21,4 +77,46 @@ def isDark():

def isLight():
if theme() is not None:
return theme() == 'Light'
return theme() == 'Light'

def listener(callback: typing.Callable[[str], None]) -> None:
hKey = ctypes.wintypes.HKEY()
advapi32.RegOpenKeyExA(
ctypes.wintypes.HKEY(0x80000001), # HKEY_CURRENT_USER
ctypes.wintypes.LPCSTR(b'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize'),
ctypes.wintypes.DWORD(),
ctypes.wintypes.DWORD(0x00020019), # KEY_READ
ctypes.byref(hKey),
)

dwSize = ctypes.wintypes.DWORD(ctypes.sizeof(ctypes.wintypes.DWORD))
queryValueLast = ctypes.wintypes.DWORD()
queryValue = ctypes.wintypes.DWORD()
advapi32.RegQueryValueExA(
hKey,
ctypes.wintypes.LPCSTR(b'AppsUseLightTheme'),
ctypes.wintypes.LPDWORD(),
ctypes.wintypes.LPDWORD(),
ctypes.cast(ctypes.byref(queryValueLast), ctypes.wintypes.LPBYTE),
ctypes.byref(dwSize),
)

while True:
advapi32.RegNotifyChangeKeyValue(
hKey,
ctypes.wintypes.BOOL(True),
ctypes.wintypes.DWORD(0x00000004), # REG_NOTIFY_CHANGE_LAST_SET
ctypes.wintypes.HANDLE(None),
ctypes.wintypes.BOOL(False),
)
advapi32.RegQueryValueExA(
hKey,
ctypes.wintypes.LPCSTR(b'AppsUseLightTheme'),
ctypes.wintypes.LPDWORD(),
ctypes.wintypes.LPDWORD(),
ctypes.cast(ctypes.byref(queryValue), ctypes.wintypes.LPBYTE),
ctypes.byref(dwSize),
)
if queryValueLast.value != queryValue.value:
queryValueLast.value = queryValue.value
callback('Light' if queryValue.value else 'Dark')