控制笔记本电脑显示屏背光亮度(Windows、英特尔显卡)

控制笔记本电脑显示屏背光亮度(Windows、英特尔显卡)

我正在尝试在 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 :(IoctlSetBrightnessIoctlQueryBrightnessCapsCallNextLowerDriver

(此外,如果没有英特尔图形驱动程序,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 步骤进行大的跳跃并使用我的代码进行微调或将亮度调得很低,从而使它们保持接近。

相关内容