Macbook Pro 用外接显示器时,如何关闭笔记本屏幕,同时开盖使用?

macos 提供了一些 私有 API,可以获得当前插入的显示器中,是否同时包含内建显示器和外部显示器,并关闭/启动 内建显示器

其中,可能用到的 API:(仅限 apple silicon,intel 未知)

CGGetActiveDisplayList
CGDisplayIsBuiltin
CGBeginDisplayConfiguration
CGSConfigureDisplayEnabled
CGSCompleteDisplayConfiguration


我用 vibe coding 写了一个 python 脚本(deepseek v4 pro 就够了)大家也可以 vibe coding 生成一下:

提示词:

完成一个 python 脚本,功能:
1. 如果系统不是 macos,报错退出
2. 如果处理器不是 apple silicon,报错退出
3. 获取显示器列表,如果仅包含内建显示器,无连接的外部显示器,则强制开启内建显示器
4. 如果同时包含内建显示器和外部显示器,则 toggle 内建显示器的开关状态,进行 toggle 前要通过交互提示用户是否确认;



以下为可能用到的公有和私有 API:
CGGetActiveDisplayList
CGDisplayIsBuiltin
CGBeginDisplayConfiguration
CGSConfigureDisplayEnabled
CGSCompleteDisplayConfiguration


脚本内容(不保证效果,仅支持 apple silicon 处理器):

#!/usr/bin/env python3
"""Toggle built-in display on/off via CoreGraphics private API.
Minimal script for Karabiner-Elements keybinding. Apple Silicon only."""

import sys
import os
import ctypes

if sys.platform != "darwin":
    print(f"ERROR: macOS only. Current: {sys.platform}", file=sys.stderr)
    sys.exit(1)

if os.uname().machine != "arm64":
    print(f"ERROR: Apple Silicon only. Current: {os.uname().machine}", file=sys.stderr)
    sys.exit(1)

libc = ctypes.CDLL('/usr/lib/libSystem.dylib')
libc.dlopen.argtypes = [ctypes.c_char_p, ctypes.c_int]
libc.dlopen.restype = ctypes.c_void_p
libc.dlsym.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
libc.dlsym.restype = ctypes.c_void_p

_cgs_handle = libc.dlopen(
    b'/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics', 1)
if not _cgs_handle:
    print("ERROR: Cannot load CoreGraphics framework.", file=sys.stderr)
    sys.exit(1)


def _lookup(name: str):
    name_bytes = name.encode()
    ptr = libc.dlsym(_cgs_handle, name_bytes)
    if not ptr:
        ptr = libc.dlsym(ctypes.c_void_p(-2), name_bytes)
    if not ptr:
        print(f"ERROR: symbol `{name}` not found.", file=sys.stderr)
        sys.exit(1)
    return ptr


MAX_DISPLAYS = 64

CGSGetDisplayList = ctypes.CFUNCTYPE(
    ctypes.c_int32, ctypes.c_uint32,
    ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32),
)(_lookup('CGSGetDisplayList'))

CGGetActiveDisplayList = ctypes.CFUNCTYPE(
    ctypes.c_int32, ctypes.c_uint32,
    ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32),
)(_lookup('CGGetActiveDisplayList'))

CGDisplayIsBuiltin = ctypes.CFUNCTYPE(ctypes.c_int32, ctypes.c_uint32)(
    _lookup('CGDisplayIsBuiltin'))

CGBeginDisplayConfiguration = ctypes.CFUNCTYPE(
    ctypes.c_int32, ctypes.POINTER(ctypes.c_void_p),
)(_lookup('CGBeginDisplayConfiguration'))

CGSConfigureDisplayEnabled = ctypes.CFUNCTYPE(
    ctypes.c_int32, ctypes.c_void_p, ctypes.c_uint32, ctypes.c_bool,
)(_lookup('CGSConfigureDisplayEnabled'))

CGSCompleteDisplayConfiguration = ctypes.CFUNCTYPE(
    ctypes.c_int32, ctypes.c_void_p,
)(_lookup('CGSCompleteDisplayConfiguration'))


def _get_all_displays():
    ids = (ctypes.c_uint32 * MAX_DISPLAYS)()
    count = ctypes.c_uint32(0)
    CGSGetDisplayList(MAX_DISPLAYS, ids, ctypes.byref(count))
    return [ids[i] for i in range(count.value)]


def _get_active_displays():
    ids = (ctypes.c_uint32 * MAX_DISPLAYS)()
    count = ctypes.c_uint32(0)
    CGGetActiveDisplayList(MAX_DISPLAYS, ids, ctypes.byref(count))
    return {ids[i] for i in range(count.value)}


def _find_builtin(displays):
    for did in displays:
        if CGDisplayIsBuiltin(did):
            return did
    return None


def main():
    displays = _get_all_displays()
    builtin_id = _find_builtin(displays)
    if builtin_id is None:
        print("No built-in display found.", file=sys.stderr)
        sys.exit(1)

    active_set = _get_active_displays()
    builtin_active = builtin_id in active_set

    external_active_count = sum(
        1 for did in displays if did != builtin_id and did in active_set)

    if external_active_count == 0 and builtin_active:
        print("Built-in is the only active display. Refusing to disable.",
              file=sys.stderr)
        sys.exit(1)

    target = not builtin_active

    config = ctypes.c_void_p(0)
    if CGBeginDisplayConfiguration(ctypes.byref(config)) != 0:
        print("CGBeginDisplayConfiguration failed.", file=sys.stderr)
        sys.exit(1)

    if CGSConfigureDisplayEnabled(config, builtin_id, target) != 0:
        print("CGSConfigureDisplayEnabled failed.", file=sys.stderr)
        sys.exit(1)

    if CGSCompleteDisplayConfiguration(config) != 0:
        print("CGSCompleteDisplayConfiguration failed.", file=sys.stderr)
        sys.exit(1)

    action = "enabled" if target else "disabled"
    print(f"Built-in display {action}.")


if __name__ == "__main__":
    main()

编辑于 2026-06-04 · 著作权归作者所有
相关文章
RGB-MiniLED 是比 OLED 显示效果更好的技术吗?在该领域海信为什么能持续领跑?真机实测!TCL Q9M Pro实现了真正的画质技术普及液晶电视之王!海信UX2026款测评:4芯RGB-MiniLED统治旗舰圈,十年不落后我入手了2026年旗舰电视的版本答案。实测分析海信电视 E7S Pro是如何做到“极致画质+绿色能效+健康舒适”先进显示技术铁三角的2026年开年电视选购避坑指南:为什么懂行的人都选这三款Mini LED电视?2026 CES 有哪些颠覆性显示技术,海信 RGB-MiniLED 的玲珑真彩背光实力有多强?雷军称小米自研第四代 pTSF 显示技术,将如何影响手机显示行业?为什么很多电视色彩失真、有光晕?卷疯高端电视的RGB Mini LED好用吗?海信的底层真技术到底有多能打?值得闭眼入的好电视——海信小墨E5Q Pro体验分享26年618最佳爆品已无悬念?海信E5S Pro体验评测:RGB-Mini LED 电视普及化先锋如何评价在CES 2026上亮相的ROG幻16 双屏,为什么称它是2026性能最强双屏本?准备换大房子,有什么热门大屏电视推荐?AWE2026 探展实测|海信 UX2026款到底强在哪?一台电视看懂 2026 高端显示换代逻辑为什么买了4k显示器却感觉不到提升?全面升级SQD-Mini LED,2026画质新旗舰TCL Q10M Pro电视用着有多爽?2026年618电视怎么选?如何用一半预算,买到万元旗舰的音画体验?Vidda 发现X 2027深度实测参数党硬核盘点,海信小墨E5S对比TCL的T7MPro谁是画质王牌?“画境合一”加持,长虹追光 Q70S 凭 AI 氛围光 + 超薄壁画,能否成为2026年旗舰电视新标杆?开年炸场!配置无短板的标杆级RGB-Mini LED电视,海信E8S实测效果有多好?AWE 2026,海信哪些RGB-Mini LED旗舰电视值得关注?还展出了哪些重磅产品?