"""Utility functions for retrieving friendly monitor/display names.""" import sys from typing import Dict, Optional from wled_controller.utils import get_logger logger = get_logger(__name__) def get_monitor_names() -> Dict[int, str]: """Get friendly names for connected monitors. On Windows, attempts to retrieve monitor names from WMI. On other platforms, returns empty dict (will fall back to generic names). Returns: Dictionary mapping display indices to friendly names """ if sys.platform != "win32": logger.debug("Monitor name detection only supported on Windows") return {} try: import wmi w = wmi.WMI(namespace="wmi") monitors = w.WmiMonitorID() monitor_names = {} for idx, monitor in enumerate(monitors): try: # Extract manufacturer name manufacturer = "" if monitor.ManufacturerName: manufacturer = "".join(chr(c) for c in monitor.ManufacturerName if c != 0) # Extract user-friendly name user_name = "" if monitor.UserFriendlyName: user_name = "".join(chr(c) for c in monitor.UserFriendlyName if c != 0) # Build friendly name if user_name: friendly_name = user_name.strip() elif manufacturer: friendly_name = f"{manufacturer.strip()} Monitor" else: friendly_name = f"Display {idx}" monitor_names[idx] = friendly_name logger.debug(f"Monitor {idx}: {friendly_name}") except Exception as e: logger.debug(f"Failed to parse monitor {idx} name: {e}") monitor_names[idx] = f"Display {idx}" return monitor_names except ImportError: logger.debug("WMI library not available - install with: pip install wmi") return {} except Exception as e: logger.debug(f"Failed to retrieve monitor names via WMI: {e}") return {} def get_monitor_name(index: int) -> str: """Get friendly name for a specific monitor. Args: index: Monitor index (0-based) Returns: Friendly monitor name or generic fallback """ monitor_names = get_monitor_names() return monitor_names.get(index, f"Display {index}") def get_monitor_refresh_rates() -> Dict[int, int]: """Get refresh rates (in Hz) for connected monitors. On Windows, attempts to retrieve refresh rates from display settings. On other platforms, returns empty dict (will use default 60Hz). Returns: Dictionary mapping display indices to refresh rates in Hz """ if sys.platform != "win32": logger.debug("Refresh rate detection only supported on Windows") return {} try: import ctypes from ctypes import wintypes # Define Windows structures class DEVMODE(ctypes.Structure): _fields_ = [ ('dmDeviceName', ctypes.c_wchar * 32), ('dmSpecVersion', wintypes.WORD), ('dmDriverVersion', wintypes.WORD), ('dmSize', wintypes.WORD), ('dmDriverExtra', wintypes.WORD), ('dmFields', wintypes.DWORD), ('dmPositionX', wintypes.LONG), ('dmPositionY', wintypes.LONG), ('dmDisplayOrientation', wintypes.DWORD), ('dmDisplayFixedOutput', wintypes.DWORD), ('dmColor', wintypes.SHORT), ('dmDuplex', wintypes.SHORT), ('dmYResolution', wintypes.SHORT), ('dmTTOption', wintypes.SHORT), ('dmCollate', wintypes.SHORT), ('dmFormName', ctypes.c_wchar * 32), ('dmLogPixels', wintypes.WORD), ('dmBitsPerPel', wintypes.DWORD), ('dmPelsWidth', wintypes.DWORD), ('dmPelsHeight', wintypes.DWORD), ('dmDisplayFlags', wintypes.DWORD), ('dmDisplayFrequency', wintypes.DWORD), ] user32 = ctypes.windll.user32 refresh_rates = {} # Enumerate all display devices idx = 0 while True: device_name = ctypes.create_unicode_buffer(32) if not user32.EnumDisplayDevicesW(None, idx, None, 0): # Try getting display settings by index devmode = DEVMODE() devmode.dmSize = ctypes.sizeof(DEVMODE) if user32.EnumDisplaySettingsW(None, -1, ctypes.byref(devmode)): # ENUM_CURRENT_SETTINGS = -1 refresh_rate = devmode.dmDisplayFrequency if refresh_rate > 0: refresh_rates[idx] = refresh_rate logger.debug(f"Display {idx}: {refresh_rate}Hz") idx += 1 if idx > 10: # Safety limit break else: break # If no refresh rates found, try alternative method if not refresh_rates: devmode = DEVMODE() devmode.dmSize = ctypes.sizeof(DEVMODE) for monitor_idx in range(4): # Check up to 4 monitors if user32.EnumDisplaySettingsW(None, -1, ctypes.byref(devmode)): refresh_rate = devmode.dmDisplayFrequency if refresh_rate > 0: refresh_rates[monitor_idx] = refresh_rate logger.debug(f"Monitor {monitor_idx}: {refresh_rate}Hz") return refresh_rates except Exception as e: logger.debug(f"Failed to retrieve monitor refresh rates: {e}") return {}