Multi-monitor support: black overlay covers all displays, brightness dim/restore per monitor
- 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
This commit is contained in:
1 parent
1e86958277
commit
87bdbd7226
3 files changed
+108
-30
No files matched your search
@@ -5,7 +5,7 @@ a = Analysis(
|
||||
['black_screen_dim.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
datas=[('icon.ico', '.')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
|
||||
@@ -25,3 +25,7 @@ pyinstaller "Black Screen Dim.spec"
|
||||
|
||||
> Управление яркостью работает только на Windows. На других ОС приложение
|
||||
> просто покажет чёрный экран без изменения яркости.
|
||||
|
||||
> При нескольких мониторах чёрный экран накрывает **весь виртуальный рабочий стол**
|
||||
> (все мониторы сразу, включая те, что слева/сверху от основного), а яркость
|
||||
> гасится и восстанавливается **на каждом** мониторе по отдельности.
|
||||
+103
-29
@@ -1,24 +1,41 @@
|
||||
import pygame
|
||||
import sys
|
||||
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 get_current_brightness():
|
||||
|
||||
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:
|
||||
current = sbc.get_brightness()
|
||||
vals = sbc.get_brightness()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if isinstance(current, list):
|
||||
if not current:
|
||||
return None
|
||||
return current[0]
|
||||
return current
|
||||
if isinstance(vals, list):
|
||||
return list(vals) if vals else None
|
||||
return [vals]
|
||||
|
||||
|
||||
def set_brightness_safe(value):
|
||||
def set_all_brightness(value):
|
||||
"""Set brightness on ALL monitors at once."""
|
||||
try:
|
||||
sbc.set_brightness(int(value))
|
||||
return True
|
||||
@@ -26,35 +43,95 @@ def set_brightness_safe(value):
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
# --- яркость: читаем текущую и ставим минимум ---
|
||||
original_brightness = get_current_brightness()
|
||||
if original_brightness is not None:
|
||||
set_brightness_safe(0)
|
||||
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()
|
||||
|
||||
info = pygame.display.Info()
|
||||
width, height = info.current_w, info.current_h
|
||||
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
|
||||
|
||||
os.environ["SDL_VIDEO_WINDOW_POS"] = "0,0"
|
||||
screen = pygame.display.set_mode((width, height), pygame.NOFRAME)
|
||||
screen = pygame.display.set_mode((w, h), pygame.NOFRAME)
|
||||
pygame.display.set_caption("Black Screen Dim")
|
||||
|
||||
try:
|
||||
icon = pygame.image.load("icon.ico")
|
||||
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=(width // 2, height // 2))
|
||||
show_hint_until = time.time() + 1.0 # ~1 секунда
|
||||
text_rect = text_surf.get_rect(center=(w // 2, h // 2))
|
||||
show_hint_until = time.time() + 1.0 # ~1 second
|
||||
|
||||
running = True
|
||||
while running:
|
||||
@@ -70,12 +147,9 @@ def main():
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
# --- выходим: пытаемся вернуть яркость и курсор, без вылета ---
|
||||
# --- exit: restore every monitor's brightness and the cursor ---
|
||||
if original_brightness is not None:
|
||||
try:
|
||||
set_brightness_safe(original_brightness)
|
||||
except Exception:
|
||||
pass
|
||||
restore_all_brightness(original_brightness)
|
||||
|
||||
try:
|
||||
pygame.mouse.set_visible(True)
|
||||
|
||||
Reference in new issue
Block a user