Skip to content

Commit 722be51

Browse files
committed
ALL: Added saveSetup() and restoreSetup(). Fixed / Improved watchdog (especially in Linux). Fixed / improved setPosition() method
LINUX: Added ewmhlib as separate module. Fixed watchdog (freezing randomly invoking screen_resources and get_output_info), fixed workarea crash (some apps/environments do not set it), improved to work almost fine in Manjaro/KDE, avoid crashing in Wayland for "fake" :1 display (though module won't likely work) WIN32: Fixed dev.StateFlags returning weird values for multi-monitor. Fixed GetAwarenessFromDpiAwarenessContext not supported on Windows Server MACOS: Replaced display-manager-lib by other alternatives which seem to work in several macOS versions. Added setScale() method (using a workaround). Added wakeup feature to turnOn() method
1 parent b57795e commit 722be51

File tree

1 file changed

+64
-51
lines changed

1 file changed

+64
-51
lines changed

src/pymonctl/_pymonctl_macos.py

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -436,10 +436,10 @@ def brightness(self) -> Optional[int]:
436436
if self._iokit is None:
437437
self._iokit, self._cf, self._ioservice = _loadIOKit(self.handle)
438438
if self._iokit is not None and self._cf is not None and self._ioservice is not None:
439-
var_name = CF.CFStringCreateWithCString(None, b"brightness", 0)
439+
kDisplayBrightnessKey = CF.CFStringCreateWithCString(None, b"brightness", 0)
440440
value = ctypes.c_float()
441441
try:
442-
ret = self._iokit.IODisplayGetFloatParameter(self._ioservice, 0, var_name, ctypes.byref(value))
442+
ret = self._iokit.IODisplayGetFloatParameter(self._ioservice, 0, kDisplayBrightnessKey, ctypes.byref(value))
443443
except:
444444
ret = 1
445445
if ret == 0:
@@ -488,10 +488,10 @@ def setBrightness(self, brightness: Optional[int]):
488488
if self._iokit is None:
489489
self._iokit, self._cf, self._ioservice = _loadIOKit(self.handle)
490490
if self._iokit is not None and self._cf is not None and self._ioservice is not None:
491-
var_name = CF.CFStringCreateWithCString(None, b"brightness", 0)
491+
kDisplayBrightnessKey = CF.CFStringCreateWithCString(None, b"brightness", 0)
492492
value = ctypes.c_float(brightness / 100)
493493
try:
494-
ret = self._iokit.IODisplaySetFloatParameter(self._ioservice, 0, var_name, value)
494+
ret = self._iokit.IODisplaySetFloatParameter(self._ioservice, 0, kDisplayBrightnessKey, value)
495495
except:
496496
ret = 1
497497
if ret != 0:
@@ -706,7 +706,6 @@ def _NSgetAllMonitorsDict():
706706
# https://stackoverflow.com/questions/30816183/iokit-ioservicegetmatchingservices-broken-under-python3
707707
# https://stackoverflow.com/questions/65150131/iodisplayconnect-is-gone-in-big-sur-of-apple-silicon-what-is-the-replacement
708708
# https://alexdelorenzo.dev/programming/2018/08/16/reverse_engineering_private_apple_apis
709-
# https://github.com/nriley/brightness/blob/master/brightness.c
710709

711710
def _loadDisplayServices():
712711
# Display Services Framework can be used in modern systems. It takes A LOT to load
@@ -733,57 +732,71 @@ def _loadCoreDisplay():
733732

734733
def _loadIOKit(displayID = Quartz.CGMainDisplayID()):
735734
# In other systems, we can try to use IOKit
735+
# https://github.com/nriley/brightness/blob/master/brightness.c
736736
# https://stackoverflow.com/questions/22841741/calling-functions-with-arguments-from-corefoundation-using-ctypes
737737

738-
try:
739-
class _CFString(ctypes.Structure):
740-
pass
741-
742-
CFStringRef = ctypes.POINTER(_CFString)
743-
744-
lib = ctypes.util.find_library("CoreFoundation")
745-
if not lib:
746-
return None, None, None
747-
CF: ctypes.CDLL = ctypes.cdll.LoadLibrary(lib)
748-
CF.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32]
749-
CF.CFStringCreateWithCString.restype = CFStringRef
750-
751-
lib = ctypes.util.find_library('IOKit')
752-
if not lib:
753-
return None, None, None
754-
iokit: ctypes.CDLL = ctypes.cdll.LoadLibrary(lib)
755-
iokit.IODisplayGetFloatParameter.argtypes = [ctypes.c_void_p, ctypes.c_uint, CFStringRef, ctypes.POINTER(ctypes.c_float)]
756-
iokit.IODisplayGetFloatParameter.restype = ctypes.c_int
757-
iokit.IODisplaySetFloatParameter.argtypes = [ctypes.c_void_p, ctypes.c_uint, CFStringRef, ctypes.c_float]
758-
iokit.IODisplaySetFloatParameter.restype = ctypes.c_int
759-
iokit.IOServiceRequestProbe.argtypes = [ctypes.c_void_p, ctypes.c_uint]
760-
iokit.IOServiceRequestProbe.restype = ctypes.c_int
761-
762-
try:
763-
service: int = Quartz.CGDisplayIOServicePort(displayID)
764-
except:
765-
service = 0
766-
767-
if not service:
768-
769-
iokit.IOServiceMatching.restype = ctypes.c_void_p
770-
iokit.IOServiceGetMatchingService.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
771-
iokit.IOServiceGetMatchingService.restype = ctypes.c_void_p
772-
773-
try:
774-
kIOMasterPortDefault = ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
738+
class _CFString(ctypes.Structure):
739+
pass
775740

776-
service = iokit.IOServiceGetMatchingService(
777-
kIOMasterPortDefault,
778-
iokit.IOServiceMatching(b'IODisplayConnect')
779-
)
780-
except:
781-
service = 0
741+
CFStringRef = ctypes.POINTER(_CFString)
742+
743+
lib = ctypes.util.find_library("CoreFoundation")
744+
if not lib:
745+
return None, None, None
746+
CF: ctypes.CDLL = ctypes.cdll.LoadLibrary(lib)
747+
CF.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32]
748+
CF.CFStringCreateWithCString.restype = CFStringRef
749+
CF.CFDictionaryGetValue.argtypes = [ctypes.c_void_p, CFStringRef]
750+
CF.CFDictionaryGetValue.restype = ctypes.c_void_p
751+
752+
lib = ctypes.util.find_library('IOKit')
753+
if not lib:
754+
return None, None, None
755+
iokit: ctypes.CDLL = ctypes.cdll.LoadLibrary(lib)
756+
iokit.IODisplayGetFloatParameter.argtypes = [ctypes.c_void_p, ctypes.c_uint, CFStringRef, ctypes.POINTER(ctypes.c_float)]
757+
iokit.IODisplayGetFloatParameter.restype = ctypes.c_int
758+
iokit.IODisplaySetFloatParameter.argtypes = [ctypes.c_void_p, ctypes.c_uint, CFStringRef, ctypes.c_float]
759+
iokit.IODisplaySetFloatParameter.restype = ctypes.c_int
760+
iokit.IOServiceRequestProbe.argtypes = [ctypes.c_void_p, ctypes.c_uint]
761+
iokit.IOServiceRequestProbe.restype = ctypes.c_int
762+
# iokit.IODisplayCreateInfoDictionary.argtypes = [ctypes.c_void_p, ctypes.c_uint]
763+
# iokit.IODisplayCreateInfoDictionary.restype = ctypes.Structure
782764

783-
if service:
784-
return iokit, CF, service
765+
try:
766+
# CGDisplayIOServicePort is deprecated as of 10.9
767+
service: int = Quartz.CGDisplayIOServicePort(displayID)
785768
except:
786-
pass
769+
service = 0
770+
771+
# Check if this works in an actual macOS (preferably in several versions)
772+
if not service:
773+
774+
iokit.IOServiceMatching.restype = ctypes.c_void_p
775+
iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
776+
iokit.IOServiceGetMatchingServices.restype = ctypes.c_void_p
777+
iokit.IODisplayCreateInfoDictionary.restype = ctypes.c_void_p
778+
779+
kIOMasterPortDefault = ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
780+
iterator = ctypes.c_void_p()
781+
ret = iokit.IOServiceGetMatchingServices(
782+
kIOMasterPortDefault,
783+
iokit.IOServiceMatching(b'IODisplayConnect'),
784+
ctypes.byref(iterator)
785+
)
786+
787+
kIODisplayNoProductName = 0x00000400
788+
kDisplaySerialNumber = CF.CFStringCreateWithCString(None, b"DisplaySerialNumber", 0)
789+
while True:
790+
service = iokit.IOIteratorNext(iterator)
791+
if not service:
792+
break
793+
info = iokit.IODisplayCreateInfoDictionary(service, kIODisplayNoProductName)
794+
serialNumber = CF.CFDictionaryGetValue(info, kDisplaySerialNumber)
795+
if serialNumber == Quartz.CGDisplaySerialNumber(displayID):
796+
break
797+
798+
if service:
799+
return iokit, CF, service
787800

788801
return None, None, None
789802

0 commit comments

Comments
 (0)