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 · 著作权归作者所有
相关文章
2026 CES 有哪些颠覆性显示技术,海信 RGB-MiniLED 的玲珑真彩背光实力有多强?准备换大房子,有什么热门大屏电视推荐?有什么不反光的百吋电视值得推荐?买百吋电视需要注意什么?创维、海信、东芝3款热门百吋电视推荐!为什么很多电视色彩失真、有光晕?卷疯高端电视的RGB Mini LED好用吗?海信的底层真技术到底有多能打?2026电视好画质二选一:RGB 和 SQD 到底谁更值得买?场景化全面实测对比来了2026年影音游戏神装怎么选?海信U6S Pro、索尼XR50、创维 Q7H、TCL C11L深度横测,这才是爽看电视的“版本答案”AWE2026 探展实测|海信 UX2026款到底强在哪?一台电视看懂 2026 高端显示换代逻辑液晶旗舰“新标杆”: 满血RGB-Mini LED海信电视E8S实测体验报告液晶显示技术的终局之战?深度分析海信E7S Pro:这才是高端旗舰电视的“版本答案”值得闭眼入的好电视——海信小墨E5Q Pro体验分享为什么买了4k显示器却感觉不到提升?2026年海信电视画质排行天梯图与618选购指南2026年618电视怎么选?如何用一半预算,买到万元旗舰的音画体验?Vidda 发现X 2027深度实测4k分辨率用多大显示器才会推荐100%缩放?我入手了2026年旗舰电视的版本答案。实测分析海信电视 E7S Pro是如何做到“极致画质+绿色能效+健康舒适”先进显示技术铁三角的2026 CES 有哪些颠覆性显示技术,海信 RGB-MiniLED 的玲珑真彩背光实力有多强?2026年开年电视选购避坑指南:为什么懂行的人都选这三款Mini LED电视?玲珑4芯RGB Mini LED光源+H7 Pro画质芯片 — 海信UX2026定义高端电视新视界真正护眼显示器推荐(七月更)游戏办公两手抓的TCL C3A Pro显示器值不值得买?