- get_virtual_screen_rect(): bounds of the whole virtual desktop (negative x/y OK) - window pinned topmost across every monitor via SetWindowPos + SetForegroundWindow - brightness saved/restored as a per-monitor list (was only first monitor) - resource_path() so icon.ico is found in the frozen exe; spec now bundles it
165 lines
5.0 KiB
Python
165 lines
5.0 KiB
Python
import os
|
|
import sys
|
|
import time
|
|
|
|
import pygame
|
|
import screen_brightness_control as sbc
|
|
|
|
# Windows-only calls are guarded so the module still imports on other OSes.
|
|
try:
|
|
import ctypes
|
|
from ctypes import wintypes
|
|
except Exception:
|
|
ctypes = None
|
|
|
|
|
|
def resource_path(rel):
|
|
"""Absolute path to a bundled resource, works both from source and from a
|
|
PyInstaller-frozen exe (sys._MEIPASS)."""
|
|
base = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
|
|
return os.path.join(base, rel)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Brightness — handle EVERY monitor, not just the first one
|
|
# ---------------------------------------------------------------------------
|
|
def get_all_brightness():
|
|
"""Return a list with one brightness value per monitor, or None on failure."""
|
|
try:
|
|
vals = sbc.get_brightness()
|
|
except Exception:
|
|
return None
|
|
if isinstance(vals, list):
|
|
return list(vals) if vals else None
|
|
return [vals]
|
|
|
|
|
|
def set_all_brightness(value):
|
|
"""Set brightness on ALL monitors at once."""
|
|
try:
|
|
sbc.set_brightness(int(value))
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def restore_all_brightness(original):
|
|
"""Restore each monitor to its previously saved brightness."""
|
|
if not original:
|
|
return
|
|
for i, b in enumerate(original):
|
|
try:
|
|
sbc.set_brightness(int(b), display=i)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Window geometry — cover the whole virtual desktop (all monitors)
|
|
# ---------------------------------------------------------------------------
|
|
def get_virtual_screen_rect():
|
|
"""Bounding rectangle of all monitors combined (may start at negative x/y)."""
|
|
user32 = ctypes.windll.user32
|
|
try:
|
|
user32.SetProcessDPIAware()
|
|
except Exception:
|
|
pass
|
|
SM_XVIRTUALSCREEN = 76
|
|
SM_YVIRTUALSCREEN = 77
|
|
SM_CXVIRTUALSCREEN = 78
|
|
SM_CYVIRTUALSCREEN = 79
|
|
x = user32.GetSystemMetrics(SM_XVIRTUALSCREEN)
|
|
y = user32.GetSystemMetrics(SM_YVIRTUALSCREEN)
|
|
w = user32.GetSystemMetrics(SM_CXVIRTUALSCREEN)
|
|
h = user32.GetSystemMetrics(SM_CYVIRTUALSCREEN)
|
|
if w <= 0 or h <= 0: # fallback to primary monitor
|
|
w = user32.GetSystemMetrics(0) # SM_CXSCREEN
|
|
h = user32.GetSystemMetrics(1) # SM_CYSCREEN
|
|
x = y = 0
|
|
return x, y, w, h
|
|
|
|
|
|
def make_window_cover_all_monitors(hwnd, rect):
|
|
"""Move/resize the window to the virtual-screen rect and pin it topmost so it
|
|
sits above the taskbar and every monitor."""
|
|
user32 = ctypes.windll.user32
|
|
HWND_TOPMOST = -1
|
|
x, y, w, h = rect
|
|
user32.SetWindowPos(int(hwnd), HWND_TOPMOST, x, y, w, h, 0)
|
|
try:
|
|
user32.SetForegroundWindow(int(hwnd))
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def main():
|
|
# --- brightness: save every monitor, then dim them all to 0 ---
|
|
original_brightness = get_all_brightness()
|
|
if original_brightness is not None:
|
|
set_all_brightness(0)
|
|
|
|
# --- black screen over the whole virtual desktop ---
|
|
pygame.init()
|
|
|
|
if sys.platform == "win32" and ctypes is not None:
|
|
x, y, w, h = get_virtual_screen_rect()
|
|
else:
|
|
info = pygame.display.Info()
|
|
x, y, w, h = 0, 0, info.current_w, info.current_h
|
|
|
|
screen = pygame.display.set_mode((w, h), pygame.NOFRAME)
|
|
pygame.display.set_caption("Black Screen Dim")
|
|
|
|
try:
|
|
icon = pygame.image.load(resource_path("icon.ico"))
|
|
pygame.display.set_icon(icon)
|
|
except Exception:
|
|
pass
|
|
|
|
pygame.mouse.set_visible(False)
|
|
|
|
# Pin the borderless window across all monitors and bring it to front.
|
|
if sys.platform == "win32" and ctypes is not None:
|
|
try:
|
|
hwnd = pygame.display.get_wm_info().get("window")
|
|
if hwnd:
|
|
make_window_cover_all_monitors(hwnd, (x, y, w, h))
|
|
except Exception:
|
|
pass
|
|
|
|
font = pygame.font.SysFont(None, 48)
|
|
hint_text = "ESC - exit"
|
|
text_surf = font.render(hint_text, True, (255, 255, 255))
|
|
text_rect = text_surf.get_rect(center=(w // 2, h // 2))
|
|
show_hint_until = time.time() + 1.0 # ~1 second
|
|
|
|
running = True
|
|
while running:
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
running = False
|
|
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
|
|
running = False
|
|
|
|
screen.fill((0, 0, 0))
|
|
if time.time() < show_hint_until:
|
|
screen.blit(text_surf, text_rect)
|
|
|
|
pygame.display.flip()
|
|
|
|
# --- exit: restore every monitor's brightness and the cursor ---
|
|
if original_brightness is not None:
|
|
restore_all_brightness(original_brightness)
|
|
|
|
try:
|
|
pygame.mouse.set_visible(True)
|
|
except Exception:
|
|
pass
|
|
|
|
pygame.quit()
|
|
sys.exit()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|