MacPro GPU Switch:用 JavaFX 做一个 macOS 显卡切换工具
MacBook Pro 15 寸是台双显卡的机器:一块 Intel 核显,一块 Radeon 独显。
用久了会撞上一个很具体的烦恼 —— 你没法决定现在用哪块。
macOS 的系统设置里,关于显卡只有「自动切换图形卡模式」这一个勾选框,它管的是“要不要自动切”,管不了“现在锁在哪一块”。真正能决定这件事的,是一条电源管理命令:
sudo pmset -a GPUSwitch 0 # 整合(只用核显)
sudo pmset -a GPUSwitch 1 # 高性能(只用独显)
sudo pmset -a GPUSwitch 2 # 自动
能用,但每次都得开终端、敲一遍、输一次密码。为了两秒钟的切换走完这一套,太长了。
MacPro GPU Switch 就是把这条命令包进一个窗口:选核显就一直用核显,选独显就一直用独显,选自动就交回给系统。
它长这样

界面很直白:左边大字写着当前处于哪个模式,颜色也跟着变(整合是青色,高性能是橙色,自动是蓝色);右边填 Root 密码,下面三个单选框对应三种模式;底部是「关于」「应用」「关闭」。
勾上「记住密码」,下次打开就不用再输。
它做的事就四步:读当前状态 → 你选一个新模式 → 点「应用」→ 它替你跑那条 sudo 命令,再把新状态读回来显示。
三种模式
对应 GPUSwitch 的三个值:
- 整合(0)—— 只用核显,最省电最凉快,重活干不了;
- 高性能(1)—— 只用独显,性能拉满,代价是发热和续航;
- 自动(2)—— 交给系统判断,也就是 macOS 默认的那套逻辑。
实现上其实只有三件事
代码量很小,核心就是三次 Shell 调用。
一、读懂当前状态
pmset -g 会打印一大串电源管理配置,显卡那一行长这样:
gpuswitch 0
所以读状态的逻辑就是:跑一遍 pmset -g,逐行找以 gpuswitch 开头的那行,把数字抠出来。
String[] command = {"/bin/sh", "-c", "pmset -g"};
for (String process : processList) {
if (process.startsWith("gpuswitch")) {
i = Integer.parseInt(process.replaceAll("gpuswitch| ", ""));
}
}
拿到 0 / 1 / 2,再去点亮界面上对应的那个单选框。
二、切换
String[] command = {"/bin/bash", "-c",
"echo \"" + password + "\" | sudo -S pmset -a GPUSwitch " + status};
关键在 sudo -S 这个参数。它让 sudo 从标准输入读密码 —— 不加它,sudo 会去找终端要密码,而一个图形界面程序没有终端,命令会直接卡在那里。把密码 echo 进去、管道喂给 sudo,就能在没有终端的环境里把这条命令跑完。
三、验证密码
切显卡之前得先知道密码对不对,否则用户点「应用」只会撞上一个莫名其妙的失败。
String[] command = {"/bin/bash", "-c",
"echo \"" + password + "\" | sudo -S whoami"};
跑一次 whoami,输出的第一行是 root,就说明密码正确。
用 whoami 而不是 sudo -v,好处是它验证权限的方式和后面那条 pmset 完全一致,同时又没有任何副作用 —— 密码错了也不会误伤配置。
写正式代码之前,我先在 src/test 下扔了两个带 main 的小类,把命令单独跑通再搬进工具类。目录里留下的 CheckPassword 和 RunPmset 就是当时的验证代码。
界面部分
JavaFX + FXML,没什么花活。三个 RadioButton 的 userData 里分别挂着 0 / 1 / 2,用 ToggleGroup 保证互斥;窗口初始化时先读一次当前状态,用 fire() 把对应的那个点亮。为了避免用户还没选模式就点「应用」,applyApp() 里也做了一次空判断。
「记住密码」是怎么存的
勾上「记住密码」之后,密码不会明文落盘,而是先加密再写进程序目录下的 conf.properties:
- 算法:
AES/ECB/PKCS5Padding,密文再转成 URL-safe 的 Base64 保存; - 密钥不是写死的常量,而是每台机器独有的一个标识 —— 跑一遍
system_profiler SPHardwareDataType,抓Hardware UUID:那一行,去掉空格和连字符,正好 32 位十六进制,直接拿来当 AES-256 的密钥。
配置文件里只存两样东西:一个「是否记住密码」的开关,和密文本身。
取 Hardware UUID 而不是写死一个字符串,好处是凭据跟机器绑在一起 —— 把 conf.properties 拷到别的机器上,密文就解不开了。反过来,换机器或者重装系统之后,之前记住的密码会失效,重新输一次就行。
支持范围,和一句要紧的警告
- 实际验证过的只有一台:MacBook Pro(15-inch,2018)+ macOS Mojave 10.14.3;
- 理论上所有能用
sudo pmset -a GPUSwitch切换显卡的型号都能用,欢迎大家补充自己的型号; - ⚠️ 只有核显的机器切勿尝试。 这是 README 里唯一用感叹号写的一句,我原样搬过来 —— 没有独显的机器上执行这条命令没有意义,也没人验证过会发生什么;
- 它只在 macOS 上有意义,依赖
pmset和system_profiler这两个系统命令。
跑起来
开发环境是当年的那一套:Apache Maven 3.6、JDK 8、IntelliJ IDEA 2018.3。
当年 Oracle JDK 8 自带 JavaFX,不用另外下 SDK —— 这也是我选 JavaFX 而不是拉一堆第三方 UI 库的原因:对一个这么小的工具来说,零额外依赖比什么都重要。
跑起来很简单:clone 下来用 IDEA 打开,运行 cn.jxh.switcher.Main 就行。想打包成 macOS 的原生应用,pom 里已经配好了 JavaFX 的 Maven 插件(zenjava 8.8.3,应用名 MacProGPUSwitch)。
版本号那处设计顺带提一句:app.properties 里放的不是写死的数字,而是 ${project.version},打包时由 Maven 的 resource filtering 替换成真实版本。改 pom 里的 <version> 一处,界面「关于」里显示的版本号就跟着走。
版本
- 1.0.0(2019-02-10):第一个版本,能读状态、能切换;
- 1.0.1(2019-02-12):加上 Root 密码验证,以及「记住密码」。
写完这篇的时候,正好是 1.0.1。
最后
仓库在这里,Apache License 2.0:
这是个很小的工具,解决的问题也很窄。但如果你也是 2018 款 MacBook Pro 的用户,被那块随时会醒过来的独显烦过,它可能正好是你要的那个开关。