我正在尝试在 Windows 10 上控制 HP 笔记本电脑集成 LCD 的背光亮度,无论是否带有英特尔显卡驱动程序。
我想将亮度降低到比 Windows/Intel 允许的最低值低得多,就像我通过 ACPI 在 Linux Mint(双启动设置)上所做的那样/sys/class/backlight/intel_backlight/brightness
。
以下是我已经尝试过的方法:
WmiSetBrightness
IOCTL_VIDEO_SET_DISPLAY_BRIGHTNESS
- 笔记本电脑屏幕不支持 DDC/CI
- 我在网上找到的每个工具(f.lux、nircmd、screenbright 等)
- 尝试了 Windows/System32 中未记录的 igfx dll 库中的每一种方法,并将返回的数据与屏幕的不同亮度值进行比较,但
igfxDHLib.IDataHandler::SetBackLightBrightness(_CUI_CALIBRATION_COLOR_INFO)
没有起作用,返回了在线上找不到的错误代码。 - 尝试从 Linux 代码中查找 ACPI 驱动程序代码,但没有找到太多
- 尝试对 monitor.sys、wmiprov.dll 和 igdkmd64.sys 进行静态分析(IDA),发现了一些有希望的函数,如和
BrightnessTargetToPossible
,但找不到任何硬编码的大写字母,而只是,所以我的猜测是它被传递给设备堆栈中的下一个驱动程序,而我显然没有 igdkmd64 的 pdb :(IoctlSetBrightness
IoctlQueryBrightnessCaps
CallNextLowerDriver
(此外,如果没有英特尔图形驱动程序,Windows 根本无法改变亮度。)
我还没尝试过的事情:
- 内核模式调试,主要是因为我怀疑它是否可以在虚拟机上运行,因为它应该能够访问真实的显示界面?(因为我以前从未尝试过内核调试)
- 编写监视器过滤驱动程序?
Filter DO
如所述这里
我知道这个问题以前有人问过,但这些问题已经 5 年多了,而且没有正确答案。我浪费了这么多时间,控制一个数字(PWM 占空比)这么简单的事情竟然如此困难,这让我抓狂。英特尔和 Windows 从未想到他们的最低亮度在晚上会太刺眼,这真是令人难以置信。
我真的很想找到任何办法来解决这个问题。非常感谢您的帮助。
答案1
经过多年未找到解决方案,我终于找到了!现在我可以直接控制背光,甚至可以将其设置为零,并且精度很高。所以我会与任何感兴趣的人分享解决方案 :)
首先,你需要RWEverything(或任何可以读取 PCIe 和系统内存的类似程序/代码)。这个想法很简单,有一个地址供英特尔显卡驱动程序使用,Windows 会将您使用默认亮度键时要使用的值写入该地址。
我发现代码GitHub:
#!/usr/bin/env python3
# Intel Backlight Hack
# CC0 2018 Taeyeon Mori aka /u/Haruha
# Must be absolute!
rw_path = "D:\\Development\\Win32-Intel-Backlight\\Rw-Everything\\Rw.exe"
# Actuall Rw batch code follows
rw_code = """\
Read Intel GFX BAR1
> Local0 = rPCIe32(0, 2, 0, 0x10)
Null out flags to get address
> Local1 = and(Local0, 0xFFFFFFFFFFFFFFF0)
LVX offset = 0xC8254
See https://github.com/RehabMan/OS-X-Intel-Backlight/blob/master/IntelBacklight/IntelBacklightHandler.cpp#L39
> Local2 = or(Local1, 0xC8254)
Get LVX
> Local3 = r32(Local2)
Get max brightness
> Local4 = shr(Local3, 16)
Get relative brightness
> Local5 = mul(Local4, {level})
> Local5 = div(Local5, 100)
Construct new LVX
> Local6 = shl(Local4, 16)
> Local6 = or(Local6, Local5)
Write to hardware
> w32 Local2 Local6
"""
rw_exit_code = "> RwExit"
import sys
import ctypes
def is_admin():
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
if not is_admin():
# Re-run the program with admin rights
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join([__file__] + sys.argv[1:]), None, 1)
sys.exit()
import os
import argparse
import subprocess
import tempfile
parser = argparse.ArgumentParser()
parser.add_argument("level", help="New Backlight level", choices=list(range(0,101)), type=int, metavar="0-100", nargs="?")
parser.add_argument("--noexit", action="store_true", help="Don't exit Rw after running the script")
parser.add_argument("--pause", action="store_true", help="Pause after execution")
args = parser.parse_args()
if args.level is None:
print("Enter new brightness level in %, or \"x\" to abort")
while True:
print("Brightness: ", end="")
v = input()
if v.lower() == "x":
print("Aborted")
sys.exit(12)
try:
args.level = int(v)
except ValueError:
continue
else:
if args.level <= 100 and args.level >= 0:
break
with tempfile.NamedTemporaryFile("w", delete=False) as f:
f.write(rw_code.format(level=args.level))
if not args.noexit:
f.write(rw_exit_code)
path = os.path.dirname(f.name)
name = os.path.basename(f.name)
subprocess.run([rw_path, "/Command=%s" % name], cwd=path)
os.unlink(os.path.join(path, name))
if args.pause:
print("Press enter to continue")
input()
(有人编辑了上面的代码,我猜是为了在要点消失的情况下保留它,但无论如何。重要的部分只有“rw_code = ...”这些是你需要在 RWEverything 中执行的指令,如果你正在编写自己的代码,你可以忽略其余部分。)
您可以使用上面链接中的 Python 脚本,也可以用您喜欢的语言编写一些内容(我使用了 AHK)。但基本上,您从英特尔显卡的 PCIe 读取一个值,然后获取从系统内存读取的一些地址以及当前值和最大值,然后修改当前亮度值。
所有这些都使用 RWEverything 的命令行 API,因此您可以从某些键盘快捷键调用它,并确保它在 Windows 启动时以管理员身份运行,然后亮度控制就会变为零。
在我的代码中,我创建了两个快捷方式,一个用于控制 Windows 亮度,另一个用于直接亮度值。当您使用内存写入值时,Windows 仍认为它是旧值,因此我仅使用写入来写入低于 Windows 最小值的值,并使用窗口来写入高于 Windows 最小值的值。
这没有什么大不了的,因为很少见,比如在唤醒后,Windows 可能会再次将亮度设置为旧值,因此我使用 Windows 步骤进行大的跳跃并使用我的代码进行微调或将亮度调得很低,从而使它们保持接近。