diff --git a/winrandr/__init__.py b/winrandr/__init__.py index 0e01090..622e47b 100644 --- a/winrandr/__init__.py +++ b/winrandr/__init__.py @@ -22,6 +22,7 @@ "set_auto", "set_brightness", "set_gamma", + "set_night_mode", "set_noprimary", "set_off", "set_position", @@ -42,6 +43,7 @@ set_auto, set_brightness, set_gamma, + set_night_mode, set_noprimary, set_off, set_position, diff --git a/winrandr/api.py b/winrandr/api.py index 7c6c89f..f4cf308 100644 --- a/winrandr/api.py +++ b/winrandr/api.py @@ -4,6 +4,7 @@ "get_display_props", "list_displays", "list_providers", + "set_night_mode", "set_position_relative", ] @@ -15,6 +16,7 @@ identify_display, set_brightness, set_gamma, + set_night_mode, ) from winrandr.features.layout import ( # noqa: F401 set_noprimary, diff --git a/winrandr/cli/__init__.py b/winrandr/cli/__init__.py index c312e28..046de16 100644 --- a/winrandr/cli/__init__.py +++ b/winrandr/cli/__init__.py @@ -23,6 +23,7 @@ _handle_identify, _handle_listmodes, _handle_mode, + _handle_night_mode, _handle_off, _handle_pos, _handle_preferred, @@ -164,6 +165,8 @@ def _handle_global_ops(args: Namespace) -> None: args.output = short if args.brightness is not None: _handle_brightness(args, t.name) + if args.night_mode is not None: + _handle_night_mode(args, t.name) if args.gamma is not None: _handle_gamma(args, t.name) @@ -186,6 +189,8 @@ def _dispatch_display_ops(args: Namespace, device_name: str) -> None: # noqa: C _handle_off(args, device_name) if args.brightness is not None: _handle_brightness(args, device_name) + if args.night_mode is not None: + _handle_night_mode(args, device_name) if args.reflect: _handle_reflect(args, device_name) if args.gamma: @@ -235,7 +240,7 @@ def main() -> None: # noqa: C901 # 调度型函数,分支复杂度本质 return # 全局操作:亮度/伽马可不带 --output,应用到所有已连接显示器 - _global_only_attrs = frozenset({"brightness", "gamma"}) + _global_only_attrs = frozenset({"brightness", "gamma", "night_mode"}) if not args.output: mod_attrs = {a for a in _MOD_OP_ATTRS if getattr(args, a, None)} if mod_attrs and mod_attrs.issubset(_global_only_attrs): diff --git a/winrandr/cli/common.py b/winrandr/cli/common.py index 1353507..a19b9a2 100644 --- a/winrandr/cli/common.py +++ b/winrandr/cli/common.py @@ -70,6 +70,7 @@ def _sort_key(n: str) -> tuple[int, int | str]: "preferred", "off", "brightness", + "night_mode", "reflect", "gamma", "left_of", diff --git a/winrandr/cli/handlers.py b/winrandr/cli/handlers.py index 067fca0..5f5c708 100644 --- a/winrandr/cli/handlers.py +++ b/winrandr/cli/handlers.py @@ -9,6 +9,7 @@ set_auto, set_brightness, set_gamma, + set_night_mode, set_off, set_position, set_position_relative, @@ -123,6 +124,22 @@ def _handle_brightness(args: Namespace, dn: str) -> None: _msg(args, f"已将 {args.output} 亮度设为 {args.brightness}") +def _handle_night_mode(args: Namespace, dn: str) -> None: + val = args.night_mode + if val in ("light", "medium", "heavy"): + strength = {"light": 0.2, "medium": 0.5, "heavy": 0.8}[val] + else: + try: + strength = float(val) + except (ValueError, TypeError): + _fail("--night-mode 参数无效", ["使用 light(20%)/medium(50%)/heavy(80%) 或数值 0.0-1.0"]) + if not 0.0 <= strength <= 1.0: + _fail("夜间模式强度必须在 0.0-1.0 之间") + if not args.dry_run and not set_night_mode(dn, strength): + _fail("设置夜间模式失败", ["某些驱动或远程桌面环境不支持伽马校正", "使用 --verbose 查看详细日志"]) + _msg(args, f"已将 {args.output} 的蓝光强度减弱 {int(strength * 100)}%") + + def _handle_reflect(args: Namespace, dn: str) -> None: if args.reflect in ("x", "y"): _fail(f"单轴镜像翻转(-{args.reflect})在 Windows 上无标准 API 支持") diff --git a/winrandr/cli/parser.py b/winrandr/cli/parser.py index 8f3a249..796b2d7 100644 --- a/winrandr/cli/parser.py +++ b/winrandr/cli/parser.py @@ -63,6 +63,11 @@ def build_parser() -> argparse.ArgumentParser: g_img = p.add_argument_group("图像调节") g_img.add_argument("--brightness", type=float, help="亮度值(0.1-2.0,1.0 为正常)") + g_img.add_argument( + "--night-mode", + metavar="light|medium|heavy|0.0-1.0", + help="夜间模式:减少蓝光(预设 light/medium/heavy 或数值)", + ) g_img.add_argument("--gamma", metavar="R:G:B", help="伽马校正(如 1.0:0.9:0.8)") g_img.add_argument("--reflect", choices=["normal", "x", "y", "xy"], help="镜像翻转(仅 xy 支持)") g_img.add_argument("-x", action="store_true", help=argparse.SUPPRESS) diff --git a/winrandr/features/gamma.py b/winrandr/features/gamma.py index 2c9eefa..d51c98b 100644 --- a/winrandr/features/gamma.py +++ b/winrandr/features/gamma.py @@ -75,6 +75,25 @@ def _modify(ramp: c_uint16) -> None: return ok +def set_night_mode(device_name: str, strength: float) -> bool: + """通过衰减蓝光设置夜间模式。 + + strength: 0.0-1.0,0 为无变化,1 为完全移除蓝光。 + """ + if not 0.0 <= strength <= 1.0: + logger.error("夜间模式强度必须在 0.0-1.0 之间: %g", strength) + return False + + def _modify(ramp: c_uint16) -> None: + for i in range(512, 768): + ramp[i] = max(0, min(65535, int(ramp[i] * (1.0 - strength)))) + + ok = _apply_gamma(device_name, _modify) + if ok: + logger.debug("设置 %s 夜间模式强度为 %.2f", device_name, strength) + return ok + + def identify_display(device_name: str, duration: float = 2.0) -> bool: """通过闪烁屏幕帮助识别指定显示器(闪 3 次后恢复原状)。""" try: